pax_global_header00006660000000000000000000000064135115611600014511gustar00rootroot0000000000000052 comment=984644a72ede80dd057fcd6232b33448e3a097ce libtorrent-rasterbar-1.1.13/000077500000000000000000000000001351156116000157435ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/AUTHORS000066400000000000000000000010301351156116000170050ustar00rootroot00000000000000Written by Arvid Norberg. Copyright (c) 2003-2018 Contributions by: Andrei Kurushin Steven Siloti Thomas Fischer Massaroddel Tianhao Qiu. Shyam Magnus Jonsson Daniel Wallin Cory Nelson Stas Khirman Ryan Norton Andrew Resch Thanks to (github user) nervoir for bug reports Building and maintainance of the autotools scripts: Michael Wojciechowski Peter Koeleman Thanks to Reimond Retz for bugfixes, suggestions and testing Thanks to University of UmeŚ for providing development and test hardware. Project is hosted by sourceforge. libtorrent-rasterbar-1.1.13/CMakeLists.txt000066400000000000000000000263331351156116000205120ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10.0 FATAL_ERROR) project(libtorrent DESCRIPTION "Bittorrent library" VERSION 1.1.13 ) set (SOVERSION "9") list(APPEND CMAKE_MODULE_PATH ${libtorrent_SOURCE_DIR}/cmake/Modules) include(GNUInstallDirs) include(GeneratePkgConfig) include(LibtorrentMacros) set(sources web_connection_base alert alert_manager announce_entry assert bandwidth_limit bandwidth_manager bandwidth_queue_entry bdecode bitfield block_cache bloom_filter chained_buffer choker close_reason cpuid crc32c create_torrent disk_buffer_holder entry error_code file_storage file_progress lazy_bdecode escape_string string_util file fingerprint gzip hasher hex http_connection http_stream http_parser i2p_stream identify_client ip_filter ip_voter performance_counters peer_class peer_class_set peer_connection bt_peer_connection web_peer_connection http_seed_connection peer_connection_handle instantiate_connection merkle natpmp part_file packet_buffer piece_picker platform_util proxy_base peer_list puff random receive_buffer request_blocks resolve_links resolver rss session session_call session_handle session_impl session_settings proxy_settings session_stats settings_pack socket_io socket_type socks5_stream stat stat_cache storage time timestamp_history torrent torrent_handle torrent_info torrent_peer torrent_peer_allocator torrent_status tracker_manager http_tracker_connection utf8 udp_tracker_connection udp_socket upnp utp_socket_manager utp_stream file_pool lsd disk_io_job disk_job_pool disk_buffer_pool disk_io_thread enum_net broadcast_socket magnet_uri parse_url ConvertUTF thread xml_parse version # -- extensions -- metadata_transfer ut_pex ut_metadata smart_ban lt_trackers ) # -- kademlia -- set(kademlia_sources dht_storage dos_blocker dht_tracker msg node node_entry refresh rpc_manager find_data put_data node_id routing_table traversal_algorithm item get_peers get_item ) # -- ed25519 -- set(ed25519_sources add_scalar fe ge key_exchange keypair sc seed sha512 sign verify ) set(includes include ed25519/src) list_prepend(sources "src/") list_prepend(kademlia_sources "src/kademlia/") list_prepend(ed25519_sources "ed25519/src/") # these options control target creation and thus have to be declared before the add_library() call feature_option(BUILD_SHARED_LIBS "build libtorrent as a shared library" ON) feature_option(static_runtime "build libtorrent with static runtime" OFF) find_public_dependency(Threads REQUIRED) if(static_runtime) include(ucm_flags) ucm_set_runtime(STATIC) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME ON) set(OPENSSL_USE_STATIC_LIBS TRUE) set(OPENSSL_MSVC_STATIC_RT TRUE) endif() if (NOT BUILD_SHARED_LIBS) set(Boost_USE_STATIC_LIBS ON) endif() add_library(torrent-rasterbar ${sources}) if (BUILD_SHARED_LIBS) target_compile_definitions(torrent-rasterbar PRIVATE TORRENT_BUILDING_SHARED INTERFACE TORRENT_LINKING_SHARED ) endif() set_target_properties(torrent-rasterbar PROPERTIES CXX_VISIBILITY_PRESET "hidden" VISIBILITY_INLINES_HIDDEN "true" VERSION ${PROJECT_VERSION} SOVERSION ${SOVERSION} ) target_compile_definitions(torrent-rasterbar PUBLIC $<$:TORRENT_DEBUG> BOOST_ASIO_ENABLE_CANCELIO PRIVATE TORRENT_BUILDING_LIBRARY _FILE_OFFSET_BITS=64 BOOST_EXCEPTION_DISABLE ) target_include_directories(torrent-rasterbar PUBLIC $ $ ) target_link_libraries(torrent-rasterbar PUBLIC Threads::Threads ) feature_option(build_tests "build tests" OFF) if(NOT build_tests) # tests require deprecated symbols target_optional_compile_definitions(torrent-rasterbar PUBLIC FEATURE NAME deprecated-functions DEFAULT ON DESCRIPTION "enable deprecated functions for backwards compatibility" DISABLED TORRENT_NO_DEPRECATE) endif() feature_option(build_examples "build examples" OFF) feature_option(build_tools "build tools" OFF) feature_option(python-bindings "build python bindings" OFF) target_optional_compile_definitions(torrent-rasterbar PUBLIC FEATURE NAME logging DEFAULT ON DESCRIPTION "build with logging" DISABLED TORRENT_DISABLE_LOGGING) target_optional_compile_definitions(torrent-rasterbar PUBLIC FEATURE NAME pool-allocators DEFAULT ON DESCRIPTION "Uses a pool allocator for disk and piece buffers" DISABLED TORRENT_DISABLE_POOL_ALLOCATOR) target_optional_compile_definitions(torrent-rasterbar PUBLIC FEATURE NAME resolve-countries DEFAULT ON DESCRIPTION "enable support for resolving countries from peer IPs" DISABLED TORRENT_DISABLE_RESOLVE_COUNTRIES) target_optional_compile_definitions(torrent-rasterbar PUBLIC FEATURE NAME unicode DEFAULT ON DESCRIPTION "enable unicode support" ENABLED UNICODE _UNICODE) feature_option(dht "enable support for Mainline DHT" ON) feature_option(encryption "link against openssl and enable encryption" ON) feature_option(exceptions "build with exception support" ON) if (dht) target_sources(torrent-rasterbar PRIVATE ${kademlia_sources} ${ed25519_sources}) else() target_compile_definitions(torrent-rasterbar PUBLIC TORRENT_DISABLE_DHT) endif() if (encryption) find_public_dependency(OpenSSL REQUIRED) set_package_properties(OpenSSL PROPERTIES URL "https://www.openssl.org/" DESCRIPTION "Full-strength general purpose cryptography library" TYPE RECOMMENDED PURPOSE "Provides encryption support to libtorrent" ) target_sources(torrent-rasterbar PRIVATE src/mpi src/pe_crypto) target_link_libraries(torrent-rasterbar PUBLIC OpenSSL::SSL) target_compile_definitions(torrent-rasterbar PUBLIC TORRENT_USE_OPENSSL) else() target_sources(torrent-rasterbar PRIVATE src/sha1) target_compile_definitions(torrent-rasterbar PUBLIC TORRENT_DISABLE_ENCRYPTION) endif() option(tcmalloc "link against google performance tools tcmalloc" OFF) # C++ standard and Boost requirements are connected: # 1. With C++11 onward, we require Boost system component, with C++03 we need chrono and random components too # 2. When building against boost 1.66 and newer, C++11 is required. set(minimal_required_cxx_standard 98) # For the first requirement we do: set(required_boost_components system) if (NOT cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES) list(APPEND required_boost_components chrono random) endif() # Boost find_public_dependency(Boost REQUIRED COMPONENTS ${required_boost_components}) target_include_directories(torrent-rasterbar PUBLIC ${Boost_INCLUDE_DIRS}) target_link_libraries(torrent-rasterbar PUBLIC ${Boost_SYSTEM_LIBRARY}) # now test the second requirement: if (Boost_VERSION VERSION_GREATER_EQUAL 106600) set(minimal_required_cxx_standard 11) if (NOT cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES) message(FATAL_ERROR "When building against boost 1.66 and newer, C++11 is required,\n" "and your compiler does not support that") endif() endif() select_cxx_standard(torrent-rasterbar ${minimal_required_cxx_standard}) target_link_libraries(torrent-rasterbar PUBLIC ${Boost_LIBRARIES}) if (WIN32) target_link_libraries(torrent-rasterbar PRIVATE wsock32 ws2_32 Iphlpapi debug dbghelp ) add_definitions(-D_WIN32_WINNT=0x0600) # prevent winsock1 to be included add_definitions(-DWIN32_LEAN_AND_MEAN) if (MSVC) target_compile_definitions(torrent-rasterbar PUBLIC BOOST_ALL_NO_LIB _SCL_SECURE_NO_DEPRECATE _CRT_SECURE_NO_DEPRECATE # disable bogus deprecation warnings on msvc8 ) target_compile_options(torrent-rasterbar PRIVATE /Zc:wchar_t /Zc:forScope # these compiler settings just make the compiler standard conforming /MP # for multi-core compilation /bigobj # increase the number of sections for obj files ) endif() endif() if (exceptions) if (MSVC) target_compile_options(torrent-rasterbar PUBLIC /EHsc) else (MSVC) target_compile_options(torrent-rasterbar PUBLIC -fexceptions) endif (MSVC) else() if (MSVC) target_compile_definitions(torrent-rasterbar PUBLIC _HAS_EXCEPTIONS=0) else (MSVC) target_compile_options(torrent-rasterbar PUBLIC -fno-exceptions) endif (MSVC) endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_definitions(-Wno-c++11-extensions) add_definitions(-fcolor-diagnostics) endif() if (tcmalloc) target_link_libraries(torrent-rasterbar PRIVATE tcmalloc) endif() set_target_properties(torrent-rasterbar PROPERTIES SOVERSION ${SOVERSION}) get_property (COMPILETIME_OPTIONS_LIST DIRECTORY ${CMAKE_CURRENT_SOURCE_DIRECTORY} PROPERTY COMPILE_DEFINITIONS ) set(COMPILETIME_OPTIONS ${CXX_MODE_COMPILE_OPTION}) foreach (s ${COMPILETIME_OPTIONS_LIST}) set (COMPILETIME_OPTIONS "${COMPILETIME_OPTIONS} -D${s}") endforeach (s) # There is little to none support for using pkg-config with MSVC and most users won't bother with it. # However, msys is a linux-like platform on Windows that do support/prefer using pkg-config. if (NOT MSVC) generate_and_install_pkg_config_file(torrent-rasterbar libtorrent-rasterbar) endif() install(TARGETS torrent-rasterbar EXPORT LibtorrentRasterbarTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) install(DIRECTORY include/libtorrent DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h*") # === generate a CMake Config File === include(CMakePackageConfigHelpers) set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/LibtorrentRasterbar) string(REGEX REPLACE "([^;]+)" "find_dependency(\\1)" _find_dependency_calls "${_package_dependencies}") string(REPLACE ";" "\n" _find_dependency_calls "${_find_dependency_calls}") if(CMAKE_VERSION VERSION_LESS "3.11.0") set(_compatibility ExactVersion) else() set(_compatibility SameMinorVersion) endif() write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/LibtorrentRasterbar/LibtorrentRasterbarConfigVersion.cmake" VERSION ${libtorrent_VERSION} COMPATIBILITY ${_compatibility} ) export(EXPORT LibtorrentRasterbarTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/LibtorrentRasterbar/LibtorrentRasterbarTargets.cmake" NAMESPACE LibtorrentRasterbar:: ) configure_package_config_file(LibtorrentRasterbarConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/LibtorrentRasterbar/LibtorrentRasterbarConfig.cmake" INSTALL_DESTINATION "${ConfigPackageLocation}" NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO ) install(EXPORT LibtorrentRasterbarTargets NAMESPACE LibtorrentRasterbar:: DESTINATION ${ConfigPackageLocation} ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/LibtorrentRasterbar/LibtorrentRasterbarConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/LibtorrentRasterbar/LibtorrentRasterbarConfigVersion.cmake" DESTINATION ${ConfigPackageLocation} ) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/examples/cmake/FindLibtorrentRasterbar.cmake DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/Modules ) include(CheckCXXCompilerFlag) add_subdirectory(bindings) # === build tools === if (build_tools) add_subdirectory(tools) endif() # === build examples === if (build_examples) add_subdirectory(examples) endif() # === build tests === if(build_tests) enable_testing() # this will make some internal functions available in the DLL interface target_compile_definitions(torrent-rasterbar PUBLIC TORRENT_EXPORT_EXTRA) add_subdirectory(test) endif() feature_summary(DEFAULT_DESCRIPTION WHAT ALL) libtorrent-rasterbar-1.1.13/COPYING000066400000000000000000000027641351156116000170070ustar00rootroot00000000000000Copyright (c) 2003-2018, 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 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. libtorrent-rasterbar-1.1.13/ChangeLog000066400000000000000000002330031351156116000175160ustar00rootroot000000000000001.1.13 release * fix sock_type_t python binding * tighten up various input validation checks * replace use of boost-endian with boost-predef 1.1.12 release * uTP performance fixes 1.1.11 release * fix move_storage with save_path with a trailing slash * fix tracker announce issue, advertising port 0 in secondary IPv6 announce * fix missing boost/noncopyable.hpp includes * fix python binding for torrent_info::creation_date() 1.1.10 release * fix issue in udp_socket with unusual socket failure * split progress_notification alert category into file-, piece- and block progress * utp close-reason fix * exposed default add_torrent_params flags to python bindings * fix redundant flushes of partfile metadata * add option to ignore min-interval from trackers on force-reannounce * raise default setting for active_limit * fall back to copy+remove if rename_file fails * improve handling of filesystems not supporting fallocate() * force-proxy no longer disables DHT * improve connect-boost feature, to make new torrents quickly connect peers 1.1.9 release * save both file and piece priorities in resume file * added missing stats_metric python binding * uTP connections are no longer exempt from rate limits by default * fix exporting files from partfile while seeding * fix potential deadlock on Windows, caused by performing restricted tasks from within DllMain * fix issue when subsequent file priority updates cause torrent to stop 1.1.8 release * coalesce reads and writes by default on windows * fixed disk I/O performance of checking hashes and creating torrents * fix race condition in part_file * fix part_file open mode compatibility test * fixed race condition in random number generator * fix race condition in stat_cache (disk storage) * improve error handling of failing to change file priority The API for custom storage implementations was altered * set the hidden attribute when creating the part file * fix tracker announces reporting more data downloaded than the size of the torrent * fix recent regression with force_proxy setting 1.1.7 release * don't perform DNS lookups for the DHT bootstrap unless DHT is enabled * fix issue where setting file/piece priority would stop checking * expose post_dht_stats() to python binding * fix backwards compatibility to downloads without partfiles * improve part-file related error messages * fix reporting &redundant= in tracker announces * fix tie-break in duplicate peer connection disconnect logic * fix issue with SSL tracker connections left in CLOSE_WAIT state * defer truncating existing files until the first time we write to them * fix issue when receiving a torrent with 0-sized padfiles as magnet link * fix issue resuming 1.0.x downloads with a file priority 0 * fix torrent_status::next_announce * fix pad-file scalability issue * made coalesce_reads/coalesce_writes settings take effect on linux and windows * use unique peer_ids per connection * fix iOS build on recent SDK * fix tracker connection bind issue for IPv6 trackers * fix error handling of some merkle torrents * fix error handling of unsupported hard-links 1.1.6 release * deprecate save_encryption_settings (they are part of the normal settings) * add getters for peer_class_filter and peer_class_type_filter * make torrent_handler::set_priority() to use peer_classes * fix support for boost-1.66 (requires C++11) * fix i2p support * fix loading resume data when in seed mode * fix part-file creation race condition * fix issue with initializing settings on session construction * fix issue with receiving interested before metadata * fix IPv6 tracker announce issue * restore path sanitization behavior of ":" * fix listen socket issue when disabling "force_proxy" mode * fix full allocation failure on APFS 1.1.5 release * fix infinite loop when parsing certain invalid magnet links * fix parsing of torrents with certain invalid filenames * fix leak of torrent_peer objecs (entries in peer_list) * fix leak of peer_class objects (when setting per-torrent rate limits) * expose peer_class API to python binding * fix integer overflow in whole_pieces_threshold logic * fix uTP path MTU discovery issue on windows (DF bit was not set correctly) * fix python binding for torrent_handle, to be hashable * fix IPv6 tracker support by performing the second announce in more cases * fix utf-8 encoding check in torrent parser * fix infinite loop when parsing maliciously crafted torrents * fix invalid read in parse_int in bdecoder * fix issue with very long tracker- and web seed URLs * don't attempt to create empty files on startup, if they already exist * fix force-recheck issue (new files would not be picked up) * fix inconsistency in file_priorities and override_resume_data behavior * fix paused torrents not generating a state update when their ul/dl rate transitions to zero 1.1.4 release * corrected missing const qualifiers on bdecode_node * fix changing queue position of paused torrents (1.1.3 regression) * fix re-check issue after move_storage * handle invalid arguments to set_piece_deadline() * move_storage did not work for torrents without metadata * improve shutdown time by only announcing to trackers whose IP we know * fix python3 portability issue in python binding * delay 5 seconds before reconnecting socks5 proxy for UDP ASSOCIATE * fix NAT-PMP crash when removing a mapping at the wrong time * improve path sanitization (filter unicode text direction characters) * deprecate partial_piece_info::piece_state * bind upnp requests to correct local address * save resume data when removing web seeds * fix proxying of https connections * fix race condition in disk I/O storage class * fix http connection timeout on multi-homed hosts * removed depdendency on boost::uintptr_t for better compatibility * fix memory leak in the disk cache * fix double free in disk cache * forward declaring libtorrent types is discouraged. a new fwd.hpp header is provided 1.1.3 release * removed (broken) support for incoming connections over socks5 * restore announce_entry's timestamp fields to posix time in python binding * deprecate torrent_added_alert (in favor of add_torrent_alert) * fix python binding for parse_magnet_uri * fix minor robustness issue in DHT bootstrap logic * fix issue where torrent_status::num_seeds could be negative * document deprecation of dynamic loading/unloading of torrents * include user-agent in tracker announces in anonymous_mode for private torrents * add support for IPv6 peers from udp trackers * correctly URL encode the IPv6 argument to trackers * fix default file pool size on windows * fix bug where settings_pack::file_pool_size setting was not being honored * add feature to periodically close files (to make windows clear disk cache) * fix bug in torrent_handle::file_status * fix issue with peers not updated on metadata from magnet links 1.1.2 release * default TOS marking to 0x20 * fix invalid access when leaving seed-mode with outstanding hash jobs * fix ABI compatibility issue introduced with preformatted entry type * add web_seed_name_lookup_retry to session_settings * slightly improve proxy settings backwards compatibility * add function to get default settings * updating super seeding would include the torrent in state_update_alert * fix issue where num_seeds could be greater than num_peers in torrent_status * finished non-seed torrents can also be in super-seeding mode * fix issue related to unloading torrents * fixed finished-time calculation * add missing min_memory_usage() and high_performance_seed() settings presets to python * fix stat cache issue that sometimes would produce incorrect resume data * storage optimization to peer classes * fix torrent name in alerts of builds with deprecated functions * make torrent_info::is_valid() return false if torrent failed to load * fix per-torrent rate limits for >256 peer classes * don't load user_agent and peer_fingerprint from session_state * fix file rename issue with name prefix matching torrent name * fix division by zero when setting tick_interval > 1000 * fix move_storage() to its own directory (would delete the files) * fix socks5 support for UDP * add setting urlseed_max_request_bytes to handle large web seed requests * fix python build with CC/CXX environment * add trackers from add_torrent_params/magnet links to separate tiers * fix resumedata check issue with files with priority 0 * deprecated mmap_cache feature * add utility function for generating peer ID fingerprint * fix bug in last-seen-complete * remove file size limit in torrent_info filename constructor * fix tail-padding for last file in create_torrent * don't send user-agent in metadata http downloads or UPnP requests when in anonymous mode * fix internal resolve links lookup for mutable torrents * hint DHT bootstrap nodes of actual bootstrap request 1.1.1 release * update puff.c for gzip inflation * add dht_bootstrap_node a setting in settings_pack (and add default) * make pad-file and symlink support conform to BEP47 * fix piece picker bug that could result in division by zero * fix value of current_tracker when all tracker failed * deprecate lt_trackers extension * remove load_asnum_db and load_country_db from python bindings * fix crash in session::get_ip_filter when not having set one * fix filename escaping when repairing torrents with broken web seeds * fix bug where file_completed_alert would not be posted unless file_progress had been queries by the client * move files one-by-one when moving storage for a torrent * fix bug in enum_net() for BSD and Mac * fix bug in python binding of announce_entry * fixed bug related to flag_merge_resume_http_seeds flag in add_torrent_params * fixed inverted priority of incoming piece suggestions * optimize allow-fast logic * fix issue where FAST extension messages were not used during handshake * fixed crash on invalid input in http_parser * upgraded to libtommath 1.0 * fixed parsing of IPv6 endpoint with invalid port character separator * added limited support for new x.pe parameter from BEP 9 * fixed dht stats counters that weren't being updated * make sure add_torrent_alert is always posted before other alerts for the torrent * fixed peer-class leak when settings per-torrent rate limits * added a new "preformatted" type to bencode entry variant type * improved Socks5 support and test coverage * fix set_settings in python binding * Added missing alert categories in python binding * Added dht_get_peers_reply_alert alert in python binding * fixed updating the node id reported to peers after changing IPs 1.1.0 release * improve robustness and performance of uTP PMTU discovery * fix duplicate ACK issue in uTP * support filtering which parts of session state are loaded by load_state() * deprecate support for adding torrents by HTTP URL * allow specifying which tracker to scrape in scrape_tracker * tracker response alerts from user initiated announces/scrapes are now posted regardless of alert mask * improve DHT performance when changing external IP (primarily affects bootstrapping). * add feature to stop torrents immediately after checking files is done * make all non-auto managed torrents exempt from queuing logic, including checking torrents. * add option to not proxy tracker connections through proxy * removed sparse-regions feature * support using 0 disk threads (to perform disk I/O in network thread) * removed deprecated handle_alert template * enable logging build config by default (but alert mask disabled by default) * deprecated RSS API * experimental support for BEP 38, "mutable torrents" * replaced lazy_bdecode with a new bdecoder that's a lot more efficient * deprecate time functions, expose typedefs of boost::chrono in the libtorrent namespace instead * deprecate file_base feature in file_storage/torrent_info * changed default piece and file priority to 4 (previously 1) * improve piece picker support for reverse picking (used for snubbed peers) to not cause priority inversion for regular peers * improve piece picker to better support torrents with very large pieces and web seeds. (request large contiguous ranges, but not necessarily a whole piece). * deprecated session_status and session::status() in favor of performance counters. * improve support for HTTP where one direction of the socket is shut down. * remove internal fields from web_seed_entry * separate crypto library configuration and whether to support bittorrent protocol encryption * simplify bittorrent protocol encryption by just using internal RC4 implementation. * optimize copying torrent_info and file_storage objects * cancel non-critical DNS lookups when shutting down, to cut down on shutdown delay. * greatly simplify the debug logging infrastructure. logs are now delivered as alerts, and log level is controlled by the alert mask. * removed auto_expand_choker. use rate_based_choker instead * optimize UDP tracker packet handling * support SSL over uTP connections * support web seeds that resolve to multiple IPs * added auto-sequential feature. download well-seeded torrents in-order * removed built-in GeoIP support (this functionality is orthogonal to libtorrent) * deprecate proxy settings in favor of regular settings * deprecate separate settings for peer protocol encryption * support specifying listen interfaces and outgoing interfaces as device names (eth0, en2, tun0 etc.) * support for using purgrable memory as disk cache on Mac OS. * be more aggressive in corking sockets, to coalesce messages into larger packets. * pre-emptively unchoke peers to save one round-trip at connection start-up. * add session constructor overload that takes a settings_pack * torrent_info is no longer an intrusive_ptr type. It is held by shared_ptr. This is a non-backwards compatible change * move listen interface and port to the settings * move use_interfaces() to be a setting * extend storage interface to allow deferred flushing and flush the part-file metadata periodically * make statistics propagate instantly rather than on the second tick * support for partfiles, where partial pieces belonging to skipped files are put * support using multiple threads for socket operations (especially useful for high performance SSL connections) * allow setting rate limits for arbitrary peer groups. Generalizes per-torrent rate limits, and local peer limits * improved disk cache complexity O(1) instead of O(log(n)) * add feature to allow storing disk cache blocks in an mmapped file (presumably on an SSD) * optimize peer connection distribution logic across torrents to scale better with many torrents * replaced std::map with boost::unordered_map for torrent list, to scale better with many torrents * optimized piece picker * optimized disk cache * optimized .torrent file parsing * optimized initialization of storage when adding a torrent * added support for adding torrents asynchronously (for improved startup performance) * added support for asynchronous disk I/O * almost completely changed the storage interface (for custom storage) * added support for hashing pieces in multiple threads * fix padfile issue * fix PMTUd bug * update puff to fix gzip crash 1.0.10 release * fixed inverted priority of incoming piece suggestions * fixed crash on invalid input in http_parser * added a new "preformatted" type to bencode entry variant type * fix division by zero in super-seeding logic 1.0.9 release * fix issue in checking outgoing interfaces (when that option is enabled) * python binding fix for boost-1.60.0 * optimize enumeration of network interfaces on windows * improve reliability of binding listen sockets * support SNI in https web seeds and trackers * fix unhandled exception in DHT when receiving a DHT packet over IPv6 1.0.8 release * fix bug where web seeds were not used for torrents added by URL * fix support for symlinks on windows * fix long filename issue (on unixes) * fixed performance bug in DHT torrent eviction * fixed win64 build (GetFileAttributesEx) * fixed bug when deleting files for magnet links before they had metadata 1.0.7 release * fix bug where loading settings via load_state() would not trigger all appropriate actions * fix bug where 32 bit builds could use more disk cache than the virtual address space (when set to automatic) * fix support for torrents with > 500'000 pieces * fix ip filter bug when banning peers * fix IPv6 IP address resolution in URLs * introduce run-time check for torrent info-sections beeing too large * fix web seed bug when using proxy and proxy-peer-connections=false * fix bug in magnet link parser * introduce add_torrent_params flags to merge web seeds with resume data (similar to trackers) * fix bug where dont_count_slow_torrents could not be disabled * fix fallocate hack on linux (fixes corruption on some architectures) * fix auto-manage bug with announce to tracker/lsd/dht limits * improve DHT routing table to not create an unbalanced tree * fix bug in uTP that would cause any connection taking more than one second to connect be timed out (introduced in the vulnerability path) * fixed falling back to sending UDP packets direct when socks proxy fails * fixed total_wanted bug (when setting file priorities in add_torrent_params) * fix python3 compatibility with sha1_hash 1.0.6 release * fixed uTP vulnerability * make utf8 conversions more lenient * fix loading of piece priorities from resume data * improved seed-mode handling (seed-mode will now automatically be left when performing operations implying it's not a seed) * fixed issue with file priorities and override resume data * fix request queue size performance issue * slightly improve UDP tracker performance * fix http scrape * add missing port mapping functions to python binding * fix bound-checking issue in bdecoder * expose missing dht_settings fields to python * add function to query the DHT settings * fix bug in 'dont_count_slow_torrents' feature, which would start too many torrents 1.0.5 release * improve ip_voter to avoid flapping * fixed bug when max_peerlist_size was set to 0 * fix issues with missing exported symbols when building dll * fix division by zero bug in edge case while connecting peers 1.0.4 release * fix bug in python binding for file_progress on torrents with no metadata * fix assert when removing a connected web seed * fix bug in tracker timeout logic * switch UPnP post back to HTTP 1.1 * support conditional DHT get * OpenSSL build fixes * fix DHT scrape bug 1.0.3 release * python binding build fix for boost-1.57.0 * add --enable-export-all option to configure script, to export all symbols from libtorrent * fix if_nametoindex build error on windows * handle overlong utf-8 sequences * fix link order bug in makefile for python binding * fix bug in interest calculation, causing premature disconnects * tweak flag_override_resume_data semantics to make more sense (breaks backwards compatibility of edge-cases) * improve DHT bootstrapping and periodic refresh * improve DHT maintanence performance (by pinging instead of full lookups) * fix bug in DHT routing table node-id prefix optimization * fix incorrect behavior of flag_use_resume_save_path * fix protocol race-condition in super seeding mode * support read-only DHT nodes * remove unused partial hash DHT lookups * remove potentially privacy leaking extension (non-anonymous mode) * peer-id connection ordering fix in anonymous mode * mingw fixes 1.0.2 release * added missing force_proxy to python binding * anonymous_mode defaults to false * make DHT DOS detection more forgiving to bursts * support IPv6 multicast in local service discovery * simplify CAS function in DHT put * support IPv6 traffic class (via the TOS setting) * made uTP re-enter slow-start after time-out * fixed uTP upload performance issue * fix missing support for DHT put salt 1.0.1 release * fix alignment issue in bitfield * improved error handling of gzip * fixed crash when web seeds redirect * fix compiler warnings 1.0 release * fix bugs in convert_to/from_native() on windows * fix support for web servers not supporting keepalive * support storing save_path in resume data * don't use full allocation on network drives (on windows) * added clear_piece_deadlines() to remove all piece deadlines * improve queuing logic of inactive torrents (dont_count_slow_torrents) * expose optimistic unchoke logic to plugins * fix issue with large UDP packets on windows * remove set_ratio() feature * improve piece_deadline/streaming * honor pieces with priority 7 in sequential download mode * simplified building python bindings * make ignore_non_routers more forgiving in the case there are no UPnP devices at a known router. Should improve UPnP compatibility. * include reason in peer_blocked_alert * support magnet links wrapped in .torrent files * rate limiter optimization * rate limiter overflow fix (for very high limits) * non-auto-managed torrents no longer count against the torrent limits * handle DHT error responses correctly * allow force_announce to only affect a single tracker * add moving_storage field to torrent_status * expose UPnP and NAT-PMP mapping in session object * DHT refactoring and support for storing arbitrary data with put and get * support building on android * improved support for web seeds that don't support keep-alive * improve DHT routing table to return better nodes (lower RTT and closer to target) * don't use pointers to resume_data and file_priorities in add_torrent_params * allow moving files to absolute paths, out of the download directory * make move_storage more generic to allow both overwriting files as well as taking existing ones * fix choking issue at high upload rates * optimized rate limiter * make disk cache pool allocator configurable * fix library ABI to not depend on logging being enabled * use hex encoding instead of base32 in create_magnet_uri * include name, save_path and torrent_file in torrent_status, for improved performance * separate anonymous mode and force-proxy mode, and tighten it up a bit * add per-tracker scrape information to announce_entry * report errors in read_piece_alert * DHT memory optimization * improve DHT lookup speed * improve support for windows XP and earlier * introduce global connection priority for improved swarm performance * make files deleted alert non-discardable * make built-in sha functions not conflict with libcrypto * improve web seed hash failure case * improve DHT lookup times * uTP path MTU discovery improvements * optimized the torrent creator optimizer to scale significantly better with more files * fix uTP edge case where udp socket buffer fills up * fix nagle implementation in uTP * fix bug in error handling in protocol encryption 0.16.18 release * fix uninitialized values in DHT DOS mitigation * fix error handling in file::phys_offset * fix bug in HTTP scrape response parsing * enable TCP keepalive for socks5 connection for UDP associate * fix python3 support * fix bug in lt_donthave extension * expose i2p_alert to python. cleaning up of i2p connection code * fixed overflow and download performance issue when downloading at high rates * fixed bug in add_torrent_alert::message for magnet links * disable optimistic disconnects when connection limit is low * improved error handling of session::listen_on * suppress initial 'completed' announce to trackers added with replace_trackers after becoming a seed * SOCKS4 fix for trying to connect over IPv6 * fix saving resume data when removing all trackers * fix bug in udp_socket when changing socks5 proxy quickly 0.16.17 release * don't fall back on wildcard port in UPnP * fix local service discovery for magnet links * fix bitfield issue in file_storage * added work-around for MingW issue in file I/O * fixed sparse file detection on windows * fixed bug in gunzip * fix to use proxy settings when adding .torrent file from URL * fix resume file issue related to daylight savings time on windows * improve error checking in lazy_bdecode 0.16.16 release * add missing add_files overload to the python bindings * improve error handling in http gunzip * fix debug logging for banning web seeds * improve support for de-selected files in full allocation mode * fix dht_bootstrap_alert being posted * SetFileValidData fix on windows (prevents zero-fill) * fix minor lock_files issue on unix 0.16.15 release * fix mingw time_t 64 bit issue * fix use of SetFileValidData on windows * fix crash when using full allocation storage mode * improve error_code and error_category support in python bindings * fix python binding for external_ip_alert 0.16.14 release * make lt_tex more robust against bugs and malicious behavior * HTTP chunked encoding fix * expose file_granularity flag to python bindings * fix DHT memory error * change semantics of storage allocation to allocate on first write rather than on startup (behaves better with changing file priorities) * fix resend logic in response to uTP SACK messages * only act on uTP RST packets with correct ack_nr * make uTP errors log in normal log mode (not require verbose) * deduplicate web seed entries from torrent files * improve error reporting from lazy_decode() 0.16.13 release * fix auto-manage issue when pausing session * fix bug in non-sparse mode on windows, causing incorrect file errors to be generated * fix set_name() on file_storage actually affecting save paths * fix large file support issue on mingw * add some error handling to set_piece_hashes() * fix completed-on timestamp to not be clobbered on each startup * fix deadlock caused by some UDP tracker failures * fix potential integer overflow issue in timers on windows * minor fix to peer_proportional mixed_mode algorithm (TCP limit could go too low) * graceful pause fix * i2p fixes * fix issue when loading certain malformed .torrent files * pass along host header with http proxy requests and possible http_connection shutdown hang 0.16.12 release * fix building with C++11 * fix IPv6 support in UDP socket (uTP) * fix mingw build issues * increase max allowed outstanding piece requests from peers * uTP performance improvement. only fast retransmit one packet at a time * improve error message for 'file too short' * fix piece-picker stat bug when only selecting some files for download * fix bug in async_add_torrent when settings file_priorities * fix boost-1.42 support for python bindings * fix memory allocation issue (virtual addres space waste) on windows 0.16.11 release * fix web seed URL double escape issue * fix string encoding issue in alert messages * fix SSL authentication issue * deprecate std::wstring overloads. long live utf-8 * improve time-critical pieces feature (streaming) * introduce bandwidth exhaustion attack-mitigation in allowed-fast pieces * python binding fix issue where torrent_info objects where destructing when their torrents were deleted * added missing field to scrape_failed_alert in python bindings * GCC 4.8 fix * fix proxy failure semantics with regards to anonymous mode * fix round-robin seed-unchoke algorithm * add bootstrap.sh to generage configure script and run configure * fix bug in SOCK5 UDP support * fix issue where torrents added by URL would not be started immediately 0.16.10 release * fix encryption level handle invalid values * add a number of missing functions to the python binding * fix typo in Jamfile for building shared libraries * prevent tracker exchange for magnet links before metadata is received * fix crash in make_magnet_uri when generating links longer than 1024 characters * fix hanging issue when closing files on windows (completing a download) * fix piece picking edge case that could cause torrents to get stuck at hash failure * try unencrypted connections first, and fall back to encryption if it fails (performance improvement) * add missing functions to python binding (flush_cache(), remap_files() and orig_files()) * improve handling of filenames that are invalid on windows * support 'implied_port' in DHT announce_peer * don't use pool allocator for disk blocks (cache may now return pages to the kernel) 0.16.9 release * fix long filename truncation on windows * distinguish file open mode when checking files and downloading/seeding with bittorrent. updates storage interface * improve file_storage::map_file when dealing with invalid input * improve handling of invalid utf-8 sequences in strings in torrent files * handle more cases of broken .torrent files * fix bug filename collision resolver * fix bug in filename utf-8 verification * make need_save_resume() a bit more robust * fixed sparse flag manipulation on windows * fixed streaming piece picking issue 0.16.8 release * make rename_file create missing directories for new filename * added missing python function: parse_magnet_uri * fix alerts.all_categories in python binding * fix torrent-abort issue which would cancel name lookups of other torrents * make torrent file parser reject invalid path elements earlier * fixed piece picker bug when using pad-files * fix read-piece response for cancelled deadline-pieces * fixed file priority vector-overrun * fix potential packet allocation alignment issue in utp * make 'close_redudnant_connections' cover more cases * set_piece_deadline() also unfilters the piece (if its priority is 0) * add work-around for bug in windows vista and earlier in GetOverlappedResult * fix traversal algorithm leak in DHT * fix string encoding conversions on windows * take torrent_handle::query_pieces into account in torrent_handle::statue() * honor trackers responding with 410 * fixed merkle tree torrent creation bug * fixed crash with empty url-lists in torrent files * added missing max_connections() function to python bindings 0.16.7 release * fix string encoding in error messages * handle error in read_piece and set_piece_deadline when torrent is removed * DHT performance improvement * attempt to handle ERROR_CANT_WAIT disk error on windows * improve peers exchanged over PEX * fixed rare crash in ut_metadata extension * fixed files checking issue * added missing pop_alerts() to python bindings * fixed typos in configure script, inversing some feature-enable/disable flags * added missing flag_update_subscribe to python bindings * active_dht_limit, active_tracker_limit and active_lsd_limit now interpret -1 as infinite 0.16.6 release * fixed verbose log error for NAT holepunching * fix a bunch of typos in python bindings * make get_settings available in the python binding regardless of deprecated functions * fix typo in python settings binding * fix possible dangling pointer use in peer list * fix support for storing arbitrary data in the DHT * fixed bug in uTP packet circle buffer * fix potential crash when using torrent_handle::add_piece * added missing add_torrent_alert to python binding 0.16.5 release * udp socket refcounter fix * added missing async_add_torrent to python bindings * raised the limit for bottled http downloads to 2 MiB * add support for magnet links and URLs in python example client * fixed typo in python bindings' add_torrent_params * introduce a way to add built-in plugins from python * consistently disconnect the same peer when two peers simultaneously connect * fix local endpoint queries for uTP connections * small optimization to local peer discovery to ignore our own broadcasts * try harder to bind the udp socket (uTP, DHT, UDP-trackers, LSD) to the same port as TCP * relax file timestamp requirements for accepting resume data * fix performance issue in web seed downloader (coalescing of blocks sometimes wouldn't work) * web seed fixes (better support for torrents without trailing / in web seeds) * fix some issues with SSL over uTP connections * fix UDP trackers trying all endpoints behind the hostname 0.16.4 release * raise the default number of torrents allowed to announce to trackers to 1600 * improve uTP slow start behavior * fixed UDP socket error causing it to fail on Win7 * update use of boost.system to not use deprecated functions * fix GIL issue in python bindings. Deprecated extension support in python * fixed bug where setting upload slots to -1 would not mean infinite * extend the UDP tracker protocol to include the request string from the tracker URL * fix mingw build for linux crosscompiler 0.16.3 release * fix python binding backwards compatibility in replace_trackers * fix possible starvation in metadata extension * fix crash when creating torrents and optimizing file order with pad files * disable support for large MTUs in uTP until it is more reliable * expose post_torrent_updates and state_update_alert to python bindings * fix incorrect SSL error messages * fix windows build of shared library with openssl * fix race condition causing shutdown hang 0.16.2 release * fix permissions issue on linux with noatime enabled for non-owned files * use random peer IDs in anonymous mode * fix move_storage bugs * fix unnecessary dependency on boost.date_time when building boost.asio as separate compilation * always use SO_REUSEADDR and deprecate the flag to turn it on * add python bindings for SSL support * minor uTP tweaks * fix end-game mode issue when some files are selected to not be downloaded * improve uTP slow start * make uTP less aggressive resetting cwnd when idle 0.16.1 release * fixed crash when providing corrupt resume data * fixed support for boost-1.44 * fixed reversed semantics of queue_up() and queue_down() * added missing functions to python bindings (file_priority(), set_dht_settings()) * fixed low_prio_disk support on linux * fixed time critical piece accounting in the request queue * fixed semantics of rate_limit_utp to also ignore per-torrent limits * fixed piece sorting bug of deadline pieces * fixed python binding build on Mac OS and BSD * fixed UNC path normalization (on windows, unless UNC paths are disabled) * fixed possible crash when enabling multiple connections per IP * fixed typo in win vista specific code, breaking the build * change default of rate_limit_utp to true * fixed DLL export issue on windows (when building a shared library linking statically against boost) * fixed FreeBSD build * fixed web seed performance issue with pieces > 1 MiB * fixed unchoke logic when using web seeds * fixed compatibility with older versions of boost (down to boost 1.40) 0.16 release * support torrents with more than 262000 pieces * make tracker back-off configurable * don't restart the swarm after downloading metadata from magnet links * lower the default tracker retry intervals * support banning web seeds sending corrupt data * don't let hung outgoing connection attempts block incoming connections * improve SSL torrent support by using SNI and a single SSL listen socket * improved peer exchange performance by sharing incoming connections which advertize listen port * deprecate set_ratio(), and per-peer rate limits * add web seed support for torrents with pad files * introduced a more scalable API for torrent status updates (post_torrent_updates()) and updated client_test to use it * updated the API to add_torrent_params turning all bools into flags of a flags field * added async_add_torrent() function to significantly improve performance when adding many torrents * change peer_states to be a bitmask (bw_limit, bw_network, bw_disk) * changed semantics of send_buffer_watermark_factor to be specified as a percentage * add incoming_connection_alert for logging all successful incoming connections * feature to encrypt peer connections with a secret AES-256 key stored in .torrent file * deprecated compact storage allocation * close files in separate thread on systems where close() may block (Mac OS X for instance) * don't create all directories up front when adding torrents * support DHT scrape * added support for fadvise/F_RDADVISE for improved disk read performance * introduced pop_alerts() which pops the entire alert queue in a single call * support saving metadata in resume file, enable it by default for magnet links * support for receiving multi announce messages for local peer discovery * added session::listen_no_system_port flag to prevent libtorrent from ever binding the listen socket to port 0 * added option to not recheck on missing or incomplete resume data * extended stats logging with statistics=on builds * added new session functions to more efficiently query torrent status * added alerts for added and removed torrents * expanded plugin interface to support session wide states * made the metadata block requesting algorithm more robust against hash check failures * support a separate option to use proxies for peers or not * pausing the session now also pauses checking torrents * moved alert queue size limit into session_settings * added support for DHT rss feeds (storing only) * added support for RSS feeds * fixed up some edge cases in DHT routing table and improved unit test of it * added error category and error codes for HTTP errors * made the DHT implementation slightly more robust against routing table poisoning and node ID spoofing * support chunked encoding in http downloads (http_connection) * support adding torrents by url to the .torrent file * support CDATA tags in xml parser * use a python python dictionary for settings instead of session_settings object (in python bindings) * optimized metadata transfer (magnet link) startup time (shaved off about 1 second) * optimized swarm startup time (shaved off about 1 second) * support DHT name lookup * optimized memory usage of torrent_info and file_storage, forcing some API changes around file_storage and file_entry * support trackerid tracker extension * graceful peer disconnect mode which finishes transactions before disconnecting peers * support chunked encoding for web seeds * uTP protocol support * resistance towards certain flood attacks * support chunked encoding for web seeds (only for BEP 19, web seeds) * optimized session startup time * support SSL for web seeds, through all proxies * support extending web seeds with custom authorization and extra headers * settings that are not changed from the default values are not saved in the session state * made seeding choking algorithm configurable * deprecated setters for max connections, max half-open, upload and download rates and unchoke slots. These are now set through session_settings * added functions to query an individual peer's upload and download limit * full support for BEP 21 (event=paused) * added share-mode feature for improving share ratios * merged all proxy settings into a single one * improved SOCKS5 support by proxying hostname lookups * improved support for multi-homed clients * added feature to not count downloaded bytes from web seeds in stats * added alert for incoming local service discovery messages * added option to set file priorities when adding torrents * removed the session mutex for improved performance * added upload and download activity timer stats for torrents * made the reuse-address flag configurable on the listen socket * moved UDP trackers over to use a single socket * added feature to make asserts log to a file instead of breaking the process (production asserts) * optimized disk I/O cache clearing * added feature to ask a torrent if it needs to save its resume data or not * added setting to ignore file modification time when loading resume files * support more fine-grained torrent states between which peer sources it announces to * supports calculating sha1 file-hashes when creating torrents * made the send_buffer_watermark performance warning more meaningful * supports complete_ago extension * dropped zlib as a dependency and builds using puff.c instead * made the default cache size depend on available physical RAM * added flags to torrent::status() that can filter which values are calculated * support 'explicit read cache' which keeps a specific set of pieces in the read cache, without implicitly caching other pieces * support sending suggest messages based on what's in the read cache * clear sparse flag on files that complete on windows * support retry-after header for web seeds * replaced boost.filesystem with custom functions * replaced dependency on boost.thread by asio's internal thread primitives * added support for i2p torrents * cleaned up usage of MAX_PATH and related macros * made it possible to build libtorrent without RTTI support * added support to build with libgcrypt and a shipped version of libtommath * optimized DHT routing table memory usage * optimized disk cache to work with large caches * support variable number of optimistic unchoke slots and to dynamically adjust based on the total number of unchoke slots * support for BitTyrant choker algorithm * support for automatically start torrents when they receive an incoming connection * added more detailed instrumentation of the disk I/O thread 0.15.11 release * fixed web seed bug, sometimes causing infinite loops * fixed race condition when setting session_settings immediately after creating session * give up immediately when failing to open a listen socket (report the actual error) * restored ABI compatibility with 0.15.9 * added missing python bindings for create_torrent and torrent_info 0.15.10 release * fix 'parameter incorrect' issue when using unbuffered IO on windows * fixed UDP socket error handling on windows * fixed peer_tos (type of service) setting * fixed crash when loading resume file with more files than the torrent in it * fix invalid-parameter error on windows when disabling filesystem disk cache * fix connection queue issue causing shutdown delays * fixed mingw build * fix overflow bug in progress_ppm field * don't filter local peers received from a non-local tracker * fix python deadlock when using python extensions * fixed small memory leak in DHT 0.15.9 release * added some functions missing from the python binding * fixed rare piece picker bug * fixed invalid torrent_status::finished_time * fixed bugs in dont-have and upload-only extension messages * don't open files in random-access mode (speeds up hashing) 0.15.8 release * allow NULL to be passed to create_torrent::set_comment and create_torrent::set_creator * fix UPnP issue for routers with multiple PPPoE connections * fix issue where event=stopped announces wouldn't be sent when closing session * fix possible hang in file::readv() on windows * fix CPU busy loop issue in tracker announce logic * honor IOV_MAX when using writev and readv * don't post 'operation aborted' UDP errors when changing listen port * fix tracker retry logic, where in some configurations the next tier would not be tried * fixed bug in http seeding logic (introduced in 0.15.7) * add support for dont-have extension message * fix for set_piece_deadline * add reset_piece_deadline function * fix merkle tree torrent assert 0.15.7 release * exposed set_peer_id to python binding * improve support for merkle tree torrent creation * exposed comparison operators on torrent_handle to python * exposed alert error_codes to python * fixed bug in announce_entry::next_announce_in and min_announce_in * fixed sign issue in set_alert_mask signature * fixed unaligned disk access for unbuffered I/O in windows * support torrents whose name is empty * fixed connection limit to take web seeds into account as well * fixed bug when receiving a have message before having the metadata * fixed python bindings build with disabled DHT support * fixed BSD file allocation issue * fixed bug in session::delete_files option to remove_torrent 0.15.6 release * fixed crash in udp trackers when using SOCKS5 proxy * fixed reconnect delay when leaving upload only mode * fixed default values being set incorrectly in add_torrent_params through add_magnet_uri in python bindings * implemented unaligned write (for unbuffered I/O) * fixed broadcast_lsd option * fixed udp-socket race condition when using a proxy * end-game mode optimizations * fixed bug in udp_socket causing it to issue two simultaneous async. read operations * fixed mingw build * fixed minor bug in metadata block requester (for magnet links) * fixed race condition in iconv string converter * fixed error handling in torrent_info constructor * fixed bug in torrent_info::remap_files * fix python binding for wait_for_alert * only apply privileged port filter to DHT-only peers 0.15.5 release * support DHT extension to report external IPs * fixed rare crash in http_connection's error handling * avoid connecting to peers listening on ports < 1024 * optimized piece picking to not cause busy loops in some end-game modes * fixed python bindings for tcp::endpoint * fixed edge case of pad file support * limit number of torrents tracked by DHT * fixed bug when allow_multiple_connections_per_ip was enabled * potential WOW64 fix for unbuffered I/O (windows) * expose set_alert_queue_size_limit to python binding * support dht nodes in magnet links * support 100 Continue HTTP responses * changed default choker behavior to use 8 unchoke slots (instead of being rate based) * fixed error reporting issue in disk I/O thread * fixed file allocation issues on linux * fixed filename encoding and decoding issue on platforms using iconv * reports redundant downloads to tracker, fixed downloaded calculation to be more stable when not including redundant. Improved redundant data accounting to be more accurate * fixed bugs in http seed connection and added unit test for it * fixed error reporting when fallocate fails * deprecate support for separate proxies for separate kinds of connections 0.15.4 release * fixed piece picker issue triggered by hash failure and timed out requests to the piece * fixed optimistic unchoke issue when setting per torrent unchoke limits * fixed UPnP shutdown issue * fixed UPnP DeletePortmapping issue * fixed NAT-PMP issue when adding the same mapping multiple times * no peers from tracker when stopping is no longer an error * improved web seed retry behavior * fixed announce issue 0.15.3 release * fixed announce bug where event=completed would not be sent if it violated the min-announce of the tracker * fixed limitation in rate limiter * fixed build error with boost 1.44 0.15.2 release * updated compiler to msvc 2008 for python binding * restored default fail_limit to unlimited on all trackers * fixed rate limit bug for DHT * fixed SOCKS5 bug for routing UDP packets * fixed bug on windows when verifying resume data for a torrent where one of its directories had been removed * fixed race condition in peer-list with DHT * fix force-reannounce and tracker retry issue 0.15.1 release * fixed rare crash when purging the peer list * fixed race condition around m_abort in session_impl * fixed bug in web_peer_connection which could cause a hang when downloading from web servers * fixed bug in metadata extensions combined with encryption * refactored socket reading code to not use async. operations unnecessarily * some timer optimizations * removed the reuse-address flag on the listen socket * fixed bug where local peer discovery and DHT wouldn't be announced to without trackers * fixed bug in bdecoder when decoding invalid messages * added build warning when building with UNICODE but the standard library doesn't provide std::wstring * fixed add_node python binding * fixed issue where trackers wouldn't tried immediately when the previous one failed * fixed synchronization issue between download queue and piece picker * fixed bug in udp tracker scrape response parsing * fixed bug in the disk thread that could get triggered under heavy load * fixed bug in add_piece() that would trigger asserts * fixed vs 2010 build * recognizes more clients in identify_client() * fixed bug where trackers wouldn't be retried if they failed * slight performance fix in disk elevator algorithm * fixed potential issue where a piece could be checked twice * fixed build issue on windows related to GetCompressedSize() * fixed deadlock when starting torrents with certain invalid tracker URLs * fixed iterator bug in disk I/O thread * fixed FIEMAP support on linux * fixed strict aliasing warning on gcc * fixed inconsistency when creating torrents with symlinks * properly detect windows version to initialize half-open connection limit * fixed bug in url encoder where $ would not be encoded 0.15 release * introduced a session state save mechanism. load_state() and save_state(). this saves all session settings and state (except torrents) * deprecated dht_state functions and merged it with the session state * added support for multiple trackers in magnet links * added support for explicitly flushing the disk cache * added torrent priority to affect bandwidth allocation for its peers * reduced the number of floating point operations (to better support systems without FPU) * added new alert when individual files complete * added support for storing symbolic links in .torrent files * added support for uTorrent interpretation of multi-tracker torrents * handle torrents with duplicate filenames * piece timeouts are adjusted to download rate limits * encodes urls in torrent files that needs to be encoded * fixed not passing &supportcrypto=1 when encryption is disabled * introduced an upload mode, which torrents are switched into when it hits a disk write error, instead of stopping the torrent. this lets libtorrent keep uploading the parts it has when it encounters a disk-full error for instance * improved disk error handling and expanded use of error_code in error reporting. added a bandwidth state, bw_disk, when waiting for the disk io thread to catch up writing buffers * improved read cache memory efficiency * added another cache flush algorithm to write the largest contiguous blocks instead of the least recently used * introduced a mechanism to be lighter on the disk when checking torrents * applied temporary memory storage optimization to when checking a torrent as well * removed hash_for_slot() from storage_interface. It is now implemented by using the readv() function from the storage implementation * improved IPv6 support by announcing twice when necessary * added feature to set a separate global rate limit for local peers * added preset settings for low memory environments and seed machines min_memory_usage() and high_performance_seeder() * optimized overall memory usage for DHT nodes and requests, peer entries and disk buffers * change in API for block_info in partial_piece_info, instead of accessing 'peer', call 'peer()' * added support for fully automatic unchoker (no need to specify number of upload slots). This is on by default * added support for changing socket buffer sizes through session_settings * added support for merkle hash tree torrents (.merkle.torrent) * added 'seed mode', which assumes that all files are complete and checks hashes lazily, as blocks are requested * added new extension for file attributes (executable and hidden) * added support for unbuffered I/O for aligned files * added workaround for sparse file issue on Windows Vista * added new lt_trackers extension to exchange trackers between peers * added support for BEP 17 http seeds * added read_piece() to read pieces from torrent storage * added option for udp tracker preference * added super seeding * added add_piece() function to inject data from external sources * add_tracker() function added to torrent_handle * if there is no working tracker, current_tracker is the tracker that is currently being tried * torrents that are checking can now be paused, which will pause the checking * introduced another torrent state, checking_resume_data, which the torrent is in when it's first added, and is comparing the files on disk with the resume data * DHT bandwidth usage optimizations * rate limited DHT send socket * tracker connections are now also subject to IP filtering * improved optimistic unchoke logic * added monitoring of the DHT lookups * added bandwidth reports for estimated TCP/IP overhead and DHT * includes DHT traffic in the rate limiter * added support for bitcomet padding files * improved support for sparse files on windows * added ability to give seeding torrents preference to active slots * added torrent_status::finished_time * automatically caps files and connections by default to rlimit * added session::is_dht_running() function * added torrent_handle::force_dht_announce() * added torrent_info::remap_files() * support min_interval tracker extension * added session saving and loading functions * added support for min-interval in tracker responses * only keeps one outstanding duplicate request per peer reduces waste download, specifically when streaming * added support for storing per-peer rate limits across reconnects * improved fallocate support * fixed magnet link issue when using resume data * support disk I/O priority settings * added info_hash to torrent_deleted_alert * improved LSD performance and made the interval configurable * improved UDP tracker support by caching connect tokens * fast piece optimization release 0.14.10 * fixed udp tracker race condition * added support for torrents with odd piece sizes * fixed issue with disk read cache not being cleared when removing torrents * made the DHT socket bind to the same interface as the session * fixed issue where an http proxy would not be used on redirects * Solaris build fixes * disabled buggy disconnect_peers feature release 0.14.9 * disabled feature to drop requests after having been skipped too many times * fixed range request bug for files larger than 2 GB in web seeds * don't crash when trying to create torrents with 0 files * fixed big_number __init__ in python bindings * fixed optimistic unchoke timer * fixed bug where torrents with incorrectly formatted web seed URLs would be connected multiple times * fixed MinGW support * fixed DHT bootstrapping issue * fixed UDP over SOCKS5 issue * added support for "corrupt" tracker announce * made end-game mode less aggressive release 0.14.8 * ignore unkown metadata messages * fixed typo that would sometimes prevent queued torrents to be checked * fixed bug in auto-manager where active_downloads and active_seeds would sometimes be used incorrectly * force_recheck() no longer crashes on torrents with no metadata * fixed broadcast socket regression from 0.14.7 * fixed hang in NATPMP when shut down while waiting for a response * fixed some more error handling in bdecode release 0.14.7 * fixed deadlock in natpmp * resume data alerts are always posted, regardless of alert mask * added wait_for_alert to python binding * improved invalid filename character replacement * improved forward compatibility in DHT * added set_piece_hashes that takes a callback to the python binding * fixed division by zero in get_peer_info() * fixed bug where pieces may have been requested before the metadata was received * fixed incorrect error when deleting files from a torrent where not all files have been created * announces torrents immediately to the DHT when it's started * fixed bug in add_files that would fail to recurse if the path ended with a / * fixed bug in error handling when parsing torrent files * fixed file checking bug when renaming a file before checking the torrent * fixed race conditon when receiving metadata from swarm * fixed assert in ut_metadata plugin * back-ported some fixes for building with no exceptions * fixed create_torrent when passing in a path ending with / * fixed move_storage when source doesn't exist * fixed DHT state save bug for node-id * fixed typo in python binding session_status struct * broadcast sockets now join every network interface (used for UPnP and local peer discovery) release 0.14.6 * various missing include fixes to be buildable with boost 1.40 * added missing functions to python binding related to torrent creation * fixed to add filename on web seed urls that lack it * fixed BOOST_ASIO_HASH_MAP_BUCKETS define for boost 1.39 * fixed checking of fast and suggest messages when used with magnet links * fixed bug where web seeds would not disconnect if being resolved when the torrent was paused * fixed download piece performance bug in piece picker * fixed bug in connect candidate counter * replaces invalid filename characters with . * added --with-libgeoip option to configure script to allow building and linking against system wide library * fixed potential pure virtual function call in extensions on shutdown * fixed disk buffer leak in smart_ban extension release 0.14.5 * fixed bug when handling malformed webseed urls and an http proxy * fixed bug when setting unlimited upload or download rates for torrents * fix to make torrent_status::list_peers more accurate. * fixed memory leak in disk io thread when not using the cache * fixed bug in connect candidate counter * allow 0 upload slots * fixed bug in rename_file(). The new name would not always be saved in the resume data * fixed resume data compatibility with 0.13 * fixed rare piece-picker bug * fixed bug where one allowed-fast message would be sent even when disabled * fixed race condition in UPnP which could lead to crash * fixed inversed seed_time ratio logic * added get_ip_filter() to session release 0.14.4 * connect candidate calculation fix * tightened up disk cache memory usage * fixed magnet link parser to accept hex-encoded info-hashes * fixed inverted logic when picking which peers to connect to (should mean a slight performance improvement) * fixed a bug where a failed rename_file() would leave the storage in an error state which would pause the torrent * fixed case when move_storage() would fail. Added a new alert to be posted when it does * fixed crash bug when shutting down while checking a torrent * fixed handling of web seed urls that didn't end with a slash for multi-file torrents * lowered the default connection speed to 10 connection attempts per second * optimized memory usage when checking files fails * fixed bug when checking a torrent twice * improved handling of out-of-memory conditions in disk I/O thread * fixed bug when force-checking a torrent with partial pieces * fixed memory leak in disk cache * fixed torrent file path vulnerability * fixed upnp * fixed bug when dealing with clients that drop requests (i.e. BitComet) fixes assert as well release 0.14.3 * added python binding for create_torrent * fixed boost-1.38 build * fixed bug where web seeds would be connected before the files were checked * fixed filename bug when using wide characters * fixed rare crash in peer banning code * fixed potential HTTP compatibility issue * fixed UPnP crash * fixed UPnP issue where the control url contained the base url * fixed a replace_trackers bug * fixed bug where the DHT port mapping would not be removed when changing DHT port * fixed move_storage bug when files were renamed to be moved out of the root directory * added error handling for set_piece_hashes * fixed missing include in enum_if.cpp * fixed dual IP stack issue * fixed issue where renamed files were sometimes not saved in resume data * accepts tracker responses with no 'peers' field, as long as 'peers6' is present * fixed CIDR-distance calculation in the precense of IPv6 peers * save partial resume data for torrents that are queued for checking or checking, to maintain stats and renamed files * Don't try IPv6 on windows if it's not installed * move_storage fix * fixed potential crash on shutdown * fixed leaking exception from bdecode on malformed input * fixed bug where connection would hang when receiving a keepalive * fixed bug where an asio exception could be thrown when resolving peer countries * fixed crash when shutting down while checking a torrent * fixed potential crash in connection_queue when a peer_connection fail to open its socket release 0.14.2 * added missing functions to the python bindings torrent_info::map_file, torrent_info::map_block and torrent_info::file_at_offset. * removed support for boost-1.33 and earlier (probably didn't work) * fixed potential freezes issues at shutdown * improved error message for python setup script * fixed bug when torrent file included announce-list, but no valid tracker urls * fixed bug where the files requested from web seeds would be the renamed file names instead of the original file names in the torrent. * documentation fix of queing section * fixed potential issue in udp_socket (affected udp tracker support) * made name, comment and created by also be subject to utf-8 error correction (filenames already were) * fixed dead-lock when settings DHT proxy * added missing export directives to lazy_entry * fixed disk cache expiry settings bug (if changed, it would be set to the cache size) * fixed bug in http_connection when binding to a particular IP * fixed typo in python binding (torrent_handle::piece_prioritize should be torrent_handle::piece_priorities) * fixed race condition when saving DHT state * fixed bugs related to lexical_cast being locale dependent * added support for SunPro C++ compiler * fixed bug where messeges sometimes could be encrypted in the wrong order, for encrypted connections. * fixed race condition where torrents could get stuck waiting to get checked * fixed mapped files bug where it wouldn't be properly restored from resume data properly * removed locale dependency in xml parser (caused asserts on windows) * fixed bug when talking to https 1.0 servers * fixed UPnP bug that could cause stack overflow release 0.14.1 * added converter for python unicode strings to utf-8 paths * fixed bug in http downloader where the host field did not include the port number * fixed headers to not depend on NDEBUG, which would prohibit linking a release build of libtorrent against a debug application * fixed bug in disk I/O thread that would make the thread sometimes quit when an error occurred * fixed DHT bug * fixed potential shutdown crash in disk_io_thread * fixed usage of deprecated boost.filsystem functions * fixed http_connection unit test * fixed bug in DHT when a DHT state was loaded * made rate limiter change in 0.14 optional (to take estimated TCP/IP overhead into account) * made the python plugin buildable through the makefile * fixed UPnP bug when url base ended with a slash and path started with a slash * fixed various potentially leaking exceptions * fixed problem with removing torrents that are checking * fixed documentation bug regarding save_resume_data() * added missing documentation on torrent creation * fixed bugs in python client examples * fixed missing dependency in package-config file * fixed shared geoip linking in Jamfile * fixed python bindings build on windows and made it possible to generate a windows installer * fixed bug in NAT-PMP implementation release 0.14 * deprecated add_torrent() in favor of a new add_torrent() that takes a struct with parameters instead. Torrents are paused and auto managed by default. * removed 'connecting_to_tracker' torrent state. This changes the enum values for the other states. * Improved seeding and choking behavior. * Fixed rare buffer overrun bug when calling get_download_queue * Fixed rare bug where torrent could be put back into downloading state even though it was finished, after checking files. * Fixed rename_file to work before the file on disk has been created. * Fixed bug in tracker connections in case of errors caused in the connection constructor. * Updated alert system to be filtered by category instead of severity level. Alerts can generate a message through alert::message(). * Session constructor will now start dht, upnp, natpmp, lsd by default. Flags can be passed in to the constructor to not do this, if these features are to be enabled and disabled at a later point. * Removed 'connecting_to_tracker' torrent state * Fix bug where FAST pieces were cancelled on choke * Fixed problems with restoring piece states when hash failed. * Minimum peer reconnect time fix. Peers with no failures would reconnect immediately. * Improved web seed error handling * DHT announce fixes and off-by-one loop fix * Fixed UPnP xml parse bug where it would ignore the port number for the control url. * Fixed bug in torrent writer where the private flag was added outside of the info dictionary * Made the torrent file parser less strict of what goes in the announce-list entry * Fixed type overflow bug where some statistics was incorrectly reported for file larger than 2 GB * boost-1.35 support * Fixed bug in statistics from web server peers where it sometimes could report too many bytes downloaded. * Fixed bug where statistics from the last second was lost when disconnecting a peer. * receive buffer optimizations (memcpy savings and memory savings) * Support for specifying the TOS byte for peer traffic. * Basic support for queueing of torrents. * Better bias to give connections to downloading torrents with fewer peers. * Optimized resource usage (removed the checking thread) * Support to bind outgoing connections to specific ports * Disk cache support. * New, more memory efficient, piece picker with sequential download support (instead of the more complicated sequential download threshold). * Auto Upload slots. Automtically opens up more slots if upload limit is not met. * Improved NAT-PMP support by querying the default gateway * Improved UPnP support by ignoring routers not on the clients subnet. release 0.13 * Added scrape support * Added add_extension() to torrent_handle. Can instantiate extensions for torrents while downloading * Added support for remove_torrent to delete the files as well * Fixed issue with failing async_accept on windows * DHT improvements, proper error messages are now returned when nodes sends bad packets * Optimized the country table used to resolve country of peers * Copying optimization for sending data. Data is no longer copied from the disk I/O buffer to the send buffer. * Buffer optimization to use a raw buffer instead of std::vector * Improved file storage to use sparse files * Updated python bindings * Added more clients to the identifiable clients list. * Torrents can now be started in paused state (to better support queuing) * Improved IPv6 support (support for IPv6 extension to trackers and listens on both IPv6 and IPv4 interfaces). * Improved asserts used. Generates a stacktrace on linux * Piece picker optimizations and improvements * Improved unchoker, connection limit and rate limiter * Support for FAST extension * Fixed invalid calculation in DHT node distance * Fixed bug in URL parser that failed to parse IPv6 addresses * added peer download rate approximation * added port filter for outgoing connection (to prevent triggering firewalls) * made most parameters configurable via session_settings * added encryption support * added parole mode for peers whose data fails the hash check. * optimized heap usage in piece-picker and web seed downloader. * fixed bug in DHT where older write tokens weren't accepted. * added support for sparse files. * introduced speed categories for peers and pieces, to separate slow and fast peers. * added a half-open tcp connection limit that takes all connections in to account, not just peer connections. * added alerts for filtered IPs. * added support for SOCKS4 and 5 proxies and HTTP CONNECT proxies. * fixed proper distributed copies calculation. * added option to use openssl for sha-1 calculations. * optimized the piece picker in the case where a peer is a seed. * added support for local peer discovery * removed the dependency on the compiled boost.date_time library * deprecated torrent_info::print() * added UPnP support * fixed problem where peer interested flags were not updated correctly when pieces were filtered * improvements to ut_pex messages, including support for seed flag * prioritizes upload bandwidth to peers that might send back data * the following functions have been deprecated: void torrent_handle::filter_piece(int index, bool filter) const; void torrent_handle::filter_pieces(std::vector const& pieces) const; bool torrent_handle::is_piece_filtered(int index) const; std::vector torrent_handle::filtered_pieces() const; void torrent_handle::filter_files(std::vector const& files) const; instead, use the piece_priority functions. * added support for NAT-PMP * added support for piece priorities. Piece filtering is now set as a priority * Fixed crash when last piece was smaller than one block and reading fastresume data for that piece * Makefiles should do a better job detecting boost * Fixed crash when all tracker urls are removed * Log files can now be created at user supplied path * Log files failing to create is no longer fatal * Fixed dead-lock in torrent_handle * Made it build with boost 1.34 on windows * Fixed bug in URL parser that failed to parse IPv6 addresses * Fixed bug in DHT, related to IPv6 nodes * DHT accepts transaction IDs that have garbage appended to them * DHT logs messages that it fails to decode release 0.12 * fixes to make the DHT more compatible * http seed improvements including error reporting and url encoding issues. * fixed bug where directories would be left behind when moving storage in some cases. * fixed crashing bug when restarting or stopping the DHT. * added python binding, using boost.python * improved character conversion on windows when strings are not utf-8. * metadata extension now respects the private flag in the torrent. * made the DHT to only be used as a fallback to trackers by default. * added support for HTTP redirection support for web seeds. * fixed race condition when accessing a torrent that was checking its fast resume data. * fixed a bug in the DHT which could be triggered if the network was dropped or extremely rare cases. * if the download rate is limited, web seeds will now only use left-over bandwidth after all bt peers have used up as much bandwidth as they can. * added the possibility to have libtorrent resolve the countries of the peers in torrents. * improved the bandwidth limiter (it now implements a leaky bucket/node bucket). * improved the HTTP seed downloader to report accurate progress. * added more client peer-id signatures to be recognized. * added support for HTTP servers that skip the CR before the NL at line breaks. * fixed bug in the HTTP code that only accepted headers case sensitive. * fixed bug where one of the session constructors didn't initialize boost.filesystem. * fixed bug when the initial checking of a torrent fails with an exception. * fixed bug in DHT code which would send incorrect announce messages. * fixed bug where the http header parser was case sensitive to the header names. * Implemented an optmization which frees the piece_picker once a torrent turns into a seed. * Added support for uT peer exchange extension, implemented by Massaroddel. * Modified the quota management to offer better bandwidth balancing between peers. * logging now supports multiple sessions (different sessions now log to different directories). * fixed random number generator seed problem, generating the same peer-id for sessions constructed the same second. * added an option to accept multiple connections from the same IP. * improved tracker logging. * moved the file_pool into session. The number of open files is now limited per session. * fixed uninitialized private flag in torrent_info * fixed long standing issue with file.cpp on windows. Replaced the low level io functions used on windows. * made it possible to associate a name with torrents without metadata. * improved http-downloading performance by requesting entire pieces via http. * added plugin interface for extensions. And changed the interface for enabling extensions. release 0.11 * added support for incorrectly encoded paths in torrent files (assumes Latin-1 encoding and converts to UTF-8). * added support for destructing session objects asynchronously. * fixed bug with file_progress() with files = 0 bytes * fixed a race condition bug in udp_tracker_connection that could cause a crash. * fixed bug occuring when increasing the sequenced download threshold with max availability lower than previous threshold. * fixed an integer overflow bug occuring when built with gcc 4.1.x * fixed crasing bug when closing while checking a torrent * fixed bug causing a crash with a torrent with piece length 0 * added an extension to the DHT network protocol to support the exchange of nodes with IPv6 addresses. * modified the ip_filter api slightly to support IPv6 * modified the api slightly to make sequenced download threshold a per torrent-setting. * changed the address type to support IPv6 * fixed bug in piece picker which would not behave as expected with regard to sequenced download threshold. * fixed bug with file_progress() with files > 2 GB. * added --enable-examples option to configure script. * fixed problem with the resource distribution algorithm (controlling e.g upload/download rates). * fixed incorrect asserts in storage related to torrents with zero-sized files. * added support for trackerless torrents (with kademlia DHT). * support for torrents with the private flag set. * support for torrents containing bootstrap nodes for the DHT network. * fixed problem with the configure script on FreeBSD. * limits the pipelining used on url-seeds. * fixed problem where the shutdown always would delay for session_settings::stop_tracker_timeout seconds. * session::listen_on() won't reopen the socket in case the port and interface is the same as the one currently in use. * added http proxy support for web seeds. * fixed problem where upload and download stats could become incorrect in case of high cpu load. * added more clients to the identifiable list. * fixed fingerprint parser to cope with latest Mainline versions. release 0.10 * fixed a bug where the requested number of peers in a tracker request could be too big. * fixed a bug where empty files were not created in full allocation mode. * fixed a bug in storage that would, in rare cases, fail to do a complete check. * exposed more settings for tweaking parameters in the piece-picker, downloader and uploader (http_settings replaced by session_settings). * tweaked default settings to improve high bandwidth transfers. * improved the piece picker performance and made it possible to download popular pieces in sequence to improve disk performance. * added the possibility to control upload and download limits per peer. * fixed problem with re-requesting skipped pieces when peer was sending pieces out of fifo-order. * added support for http seeding (the GetRight protocol) * renamed identifiers called 'id' in the public interface to support linking with Objective.C++ * changed the extensions protocol to use the new one, which is also implemented by uTorrent. * factorized the peer_connection and added web_peer_connection which is able to download from http-sources. * converted the network code to use asio (resulted in slight api changes dealing with network addresses). * made libtorrent build in vc7 (patches from Allen Zhao) * fixed bug caused when binding outgoing connections to a non-local interface. * add_torrent() will now throw if called while the session object is being closed. * added the ability to limit the number of simultaneous half-open TCP connections. Flags in peer_info has been added. release 0.9.1 * made the session disable file name checks within the boost.filsystem library * fixed race condition in the sockets * strings that are invalid utf-8 strings are now decoded with the local codepage on windows * added the ability to build libtorrent both as a shared library * client_test can now monitor a directory for torrent files and automatically start and stop downloads while running * fixed problem with file_size() when building on windows with unicode support * added a new torrent state, allocating * added a new alert, metadata_failed_alert * changed the interface to session::add_torrent for some speed optimizations. * greatly improved the command line control of the example client_test. * fixed bug where upload rate limit was not being applied. * files that are being checked will no longer stall files that don't need checking. * changed the way libtorrent identifies support for its excentions to look for 'ext' at the end of the peer-id. * improved performance by adding a circle buffer for the send buffer. * fixed bugs in the http tracker connection when using an http proxy. * fixed problem with storage's file pool when creating torrents and then starting to seed them. * hard limit on remote request queue and timeout on requests (a timeout triggers rerequests). This makes libtorrent work much better with "broken" clients like BitComet which may ignore requests. Initial release 0.9 * multitracker support * serves multiple torrents on a single port and a single thread * supports http proxies and proxy authentication * gzipped tracker-responses * block level piece picker * queues torrents for file check, instead of checking all of them in parallel * uses separate threads for checking files and for main downloader * upload and download rate limits * piece-wise, unordered, incremental file allocation * fast resume support * supports files > 2 gigabytes * supports the no_peer_id=1 extension * support for udp-tracker protocol * number of connections limit * delays sending have messages * can resume pieces downloaded in any order * adjusts the length of the request queue depending on download rate * supports compact=1 * selective downloading * ip filter libtorrent-rasterbar-1.1.13/Jamfile000066400000000000000000000606131351156116000172430ustar00rootroot00000000000000# This Jamfile requires boost-build v2 to build. # The version shipped with boost 1.34.0 import modules ; import path ; import os ; import errors ; import feature : feature ; import package ; import virtual-target ; BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; CXXFLAGS = [ modules.peek : CXXFLAGS ] ; LDFLAGS = [ modules.peek : LDFLAGS ] ; ECHO "CXXFLAGS =" $(CXXFLAGS) ; ECHO "LDFLAGS =" $(LDFLAGS) ; ECHO "OS =" [ os.name ] ; if $(BOOST_ROOT) { ECHO "building boost from source directory: " $(BOOST_ROOT) ; use-project /boost : $(BOOST_ROOT) ; alias boost_chrono : /boost/chrono//boost_chrono : : : $(BOOST_ROOT) ; alias boost_system : /boost/system//boost_system : : : $(BOOST_ROOT) ; alias boost_random : /boost/random//boost_random : : : $(BOOST_ROOT) ; } else { local boost-lib-search-path = /opt/local/lib /usr/local/lib /sw/lib /usr/g++/lib ; local boost-include-path = /opt/local/include /usr/local/include /usr/sfw/include ; # the names are decorated in MacPorts lib boost_chrono : : darwin boost_chrono-mt $(boost-lib-search-path) : : $(boost-include-path) ; lib boost_system : : darwin boost_system-mt $(boost-lib-search-path) : : $(boost-include-path) ; lib boost_random : : darwin boost_random-mt $(boost-lib-search-path) : : $(boost-include-path) ; lib boost_chrono : : boost_chrono $(boost-lib-search-path) : : $(boost-include-path) ; lib boost_system : : boost_system $(boost-lib-search-path) : : $(boost-include-path) ; lib boost_random : : boost_random $(boost-lib-search-path) : : $(boost-include-path) ; } VERSION = 1.1.13 ; rule linking ( properties * ) { local result ; if on in $(properties) { result += /libsimulator//simulator ; } if windows in $(properties) && ( debug in $(properties) || on in $(properties) || production in $(properties) || debug in $(properties) || debug in $(properties) || on in $(properties) ) { result += dbghelp ; } # gcrypt libraries, if enabled if gcrypt in $(properties) { # on mac os x, adding the /opt/local/include path # would include openssl headers incompatible with # the system library. Only add this include path # if we're not using openssl (which we're most # likely not if we're using libgcrypt) result += gcrypt /opt/local/include ; } # socket functions on windows require winsock libraries if windows in $(properties) || cygwin in $(properties) { result += ws2_32 wsock32 iphlpapi WIN32_LEAN_AND_MEAN __USE_W32_SOCKETS WIN32 _WIN32 ; # when DHT is enabled, we need ed25519 which in turn # needs entropy if ! off in $(properties) { result += advapi32 ; } } if beos in $(properties) { result += netkit gcc ; } if solaris in $(properties) { result += libsocket libnsl ; } if on in $(properties) { result += libiconv ; } if ( gcc in $(properties) || clang in $(properties) ) && linux in $(properties) && ( debug in $(properties) || on in $(properties) || production in $(properties) || debug in $(properties) || debug in $(properties) || on in $(properties) ) { # for backtraces in assertion failures # which only works on ELF targets with gcc result += -export-dynamic -rdynamic ; } if static in $(properties) { if shared in $(properties) { # if libtorrent is being built as a shared library # but we're linking against boost statically, we still # need to make boost think it's being built as a shared # library, so that it properly exports its symbols result += BOOST_ALL_DYN_LINK ; result += boost_system/static/BOOST_ALL_DYN_LINK ; result += boost_chrono/static/BOOST_ALL_DYN_LINK ; result += boost_random/static/BOOST_ALL_DYN_LINK ; } else { result += boost_system/static ; result += boost_chrono/static ; result += boost_random/static ; } if gcc in $(properties) && ! windows in $(properties) && shared in $(properties) { result += on ; } } else if shared in $(properties) { result += boost_system/shared ; result += boost_chrono/shared ; result += boost_random/shared ; } else { result += boost_system ; result += boost_chrono ; result += boost_random ; } result += BOOST_ALL_NO_LIB BOOST_MULTI_INDEX_DISABLE_SERIALIZATION ; return $(result) ; } rule warnings ( properties * ) { local result ; if off in $(properties) { return $(result) ; } if clang in $(properties) || darwin in $(properties) { result += -Weverything ; result += -Wno-documentation ; result += -Wno-c++98-compat-pedantic ; result += -Wno-padded ; result += -Wno-global-constructors ; result += -Wno-c++98-compat ; # this warns on any global static object, which are used for error_category # objects result += -Wno-exit-time-destructors ; result += -Wno-unused-command-line-argument ; result += -Wno-implicit-fallthrough ; result += -Wno-c++11-long-long ; result += -Wno-variadic-macros ; # in C++98 mode there's no way to silence this warning # in the code (without final) result += -Wno-non-virtual-dtor ; # enable these warnings again, once the other ones are dealt with result += -Wno-weak-vtables ; result += -Wno-sign-compare ; result += -Wno-sign-conversion ; result += -Wno-conversion ; } if gcc in $(properties) { result += -Wall ; result += -Wextra ; result += -Wpedantic ; # result += -Wmisleading-indentation ; result += -Wparentheses ; result += -Wvla ; result += -Wc++11-compat ; result += -Wno-format-zero-length ; result += -Wno-long-long ; # enable these warnings again, once the other ones are dealt with result += -Wno-sign-compare ; result += -Wno-unused-variable ; } if msvc in $(properties) { # disable warning C4503: decorated name length exceeded, name was truncated result += /wd4503 ; # enable these warnings again, once the other ones are dealt with # disable warning C4389: signed/unsigned mismatch result += /wd4389 ; result += /wd4245 ; result += /wd4018 ; # disable warning C4244: 'argument' : conversion from 'int' to 'unsigned short', possible loss of data result += /wd4244 ; # disable warning C4512: assignment operator could not be generated result += /wd4512 ; } return $(result) ; } # rule for adding the right source files # depending on target-os and features rule building ( properties * ) { local result ; if shared in $(properties) && on in $(properties) { # export some internal libtorrent functions # in order to me able to unit test them. # this is off by default to keep the export # symbol table reasonably small result += TORRENT_EXPORT_EXTRA ; } if msvc in $(properties) { # allow larger .obj files (with more sections) result += /bigobj ; } if ( debug in $(properties) && ( clang in $(properties) || gcc in $(properties) || darwin in $(properties) ) ) { result += -ftrapv ; } if ( debug in $(properties) || on in $(properties) ) { result += src/assert.cpp ; } if on in $(properties) { result += src/mpi.cpp ; result += src/pe_crypto.cpp ; } if built-in in $(properties) { result += src/sha1.cpp ; } if ( darwin in $(properties) || gcc in $(properties) || clang in $(propertoes) || clang-darwin in $(propertoes) ) && shared in $(properties) # on GCC, enabling debugging in libstdc++ # breaks the ABI and its ability to appear # in shared object interfaces, so when it's # enabled, just export everything (since we're) # probably not a production build anyway && ! on in $(properties) { # hide non-external symbols result += -fvisibility=hidden ; result += -fvisibility-inlines-hidden ; if ( gcc in $(properties) ) { result += -Wl,-Bsymbolic ; } } return $(result) ; } rule tag ( name : type ? : property-set ) { name = [ virtual-target.add-prefix-and-suffix $(name) : $(type) : $(property-set) ] ; if $(type) = SHARED_LIB && ( ! ( [ $(property-set).get ] in windows cygwin ) ) { name = $(name).$(VERSION) ; } return $(name) ; } # the search path to pick up the openssl libraries from. This is the # property of those libraries rule openssl-lib-path ( properties * ) { local OPENSSL_LIB = [ feature.get-values : $(properties) ] ; if darwin in $(properties) && $(OPENSSL_LIB) = "" { # on macOS, default to pick up openssl from the homebrew installation # brew install openssl OPENSSL_LIB = /usr/local/opt/openssl/lib ; } else if windows in $(properties) && gcc in $(properties) && $(OPENSSL_LIB) = "" { # on mingw, assume openssl is installed in c:\OpenSSL-Win32 by default OPENSSL_LIB = c:\\OpenSSL-Win32\\lib ; } else if windows in $(properties) && $(OPENSSL_LIB) = "" { # on windows, just assume openssl is installed to c:\openssl if 64 in $(properties) { OPENSSL_LIB = c:\\openssl\\lib64 ; } else { OPENSSL_LIB = c:\\openssl\\lib ; } } local result ; result += $(OPENSSL_LIB) ; return $(result) ; } # the include path to pick up openssl headers from. This is the # usage-requirement for the openssl-related libraries rule openssl-include-path ( properties * ) { local OPENSSL_INCLUDE = [ feature.get-values : $(properties) ] ; if darwin in $(properties) && $(OPENSSL_INCLUDE) = "" { # on macOS, default to pick up openssl from the homebrew installation # brew install openssl OPENSSL_INCLUDE = /usr/local/opt/openssl/include ; } else if windows in $(properties) && gcc in $(properties) && $(OPENSSL_INCLUDE) = "" { # on mingw, assume openssl is installed in c:\OpenSSL-Win32 by default OPENSSL_INCLUDE = c:\\OpenSSL-Win32\\include ; } else if windows in $(properties) && $(OPENSSL_INCLUDE) = "" { # on windows, just assume openssl is installed to c:\openssl # not sure if there's a better way to find out where it may be if 64 in $(properties) { OPENSSL_INCLUDE = c:\\openssl\\include64 ; } else { OPENSSL_INCLUDE = c:\\openssl\\include ; } } local result ; result += $(OPENSSL_INCLUDE) ; return $(result) ; } path-constant blacklist-file : tools/sanitizer-blacklist.txt ; rule sanitizer-options ( properties * ) { local result ; if on in $(properties) { local sanitizers ; local blacklist ; # sanitize is a clang and GCC feature # TODO: carve out specific sanitizers based on compiler version # if clang in $(properties) { sanitizers += address,undefined,implicit-conversion ; blacklist = -fsanitize-blacklist=$(blacklist-file) ; } else if gcc in $(properties) { sanitizers = address,undefined,leak ; } else if darwin in $(properties) { sanitizers = address,undefined ; } local flags = -fsanitize=$(sanitizers) -fno-sanitize-recover=all ; result = $(flags) $(flags) $(blacklist) ; } return $(result) ; } feature openssl-lib : : free path ; feature openssl-include : : free path ; feature sanitize : off rtc on : composite propagated link-incompatible ; feature ipv6 : on off : composite propagated link-incompatible ; feature.compose off : TORRENT_USE_IPV6=0 ; feature fiemap : off on : composite propagated ; feature.compose on : HAVE_LINUX_FIEMAP_H ; feature file-leak-logging : off on : composite propagated ; feature.compose on : TORRENT_DEBUG_FILE_LEAKS=1 ; feature i2p : on off : composite propagated ; feature.compose on : TORRENT_USE_I2P=1 ; feature.compose off : TORRENT_USE_I2P=0 ; feature iconv : auto on off : composite propagated ; feature.compose on : TORRENT_USE_ICONV=1 ; feature.compose off : TORRENT_USE_ICONV=0 ; feature use-valgrind : off on : composite propagated link-incompatible ; feature.compose on : TORRENT_USE_VALGRIND=1 ; feature memory-optimization : off on : composite propagated link-incompatible ; feature.compose on : TORRENT_OPTIMIZE_MEMORY_USAGE ; feature asserts : auto on off production system : composite propagated ; feature.compose on : TORRENT_RELEASE_ASSERTS=1 ; feature.compose production : TORRENT_PRODUCTION_ASSERTS=1 TORRENT_RELEASE_ASSERTS=1 ; feature.compose off : TORRENT_USE_ASSERTS=0 ; feature.compose system : TORRENT_USE_SYSTEM_ASSERTS=1 ; feature windows-version : vista win7 xp : composite propagated link-incompatible ; feature.compose vista : _WIN32_WINNT=0x0600 ; feature.compose win7 : _WIN32_WINNT=0x0601 ; feature.compose xp : _WIN32_WINNT=0x0501 ; feature extensions : on off : composite propagated link-incompatible ; feature.compose off : TORRENT_DISABLE_EXTENSIONS ; feature asio-debugging : off on : composite propagated link-incompatible ; feature.compose on : TORRENT_ASIO_DEBUGGING ; feature picker-debugging : off on : composite propagated link-incompatible ; feature.compose on : TORRENT_DEBUG_REFCOUNTS ; feature simulator : off on : composite propagated link-incompatible ; feature.compose on : TORRENT_BUILD_SIMULATOR ; # deprecated use allocator=pool instead feature pool-allocators : on off debug : composite propagated link-incompatible ; feature.compose off : TORRENT_DISABLE_POOL_ALLOCATOR ; feature.compose debug : TORRENT_DISABLE_POOL_ALLOCATOR TORRENT_DEBUG_BUFFERS ; feature allocator : pool system debug : composite propagated ; feature.compose system : TORRENT_DISABLE_POOL_ALLOCATOR ; feature.compose debug : TORRENT_DISABLE_POOL_ALLOCATOR TORRENT_DEBUG_BUFFERS ; feature piece-allocator : valloc memalign posix_memalign : composite propagated ; feature.compose memalign : TORRENT_USE_MEMALIGN=1 ; feature.compose posix_memalign : TORRENT_USE_POSIX_MEMALIGN=1 ; feature invariant-checks : on off full : composite propagated link-incompatible ; feature.compose off : TORRENT_DISABLE_INVARIANT_CHECKS ; feature.compose full : TORRENT_EXPENSIVE_INVARIANT_CHECKS ; feature disk-stats : off on : composite propagated link-incompatible ; feature.compose on : TORRENT_DISK_STATS ; feature utp-log : off on : composite propagated link-incompatible ; feature.compose on : TORRENT_UTP_LOG_ENABLE ; feature simulate-slow-read : off on : composite propagated ; feature.compose on : TORRENT_SIMULATE_SLOW_READ ; feature logging : on off : composite propagated link-incompatible ; feature.compose off : TORRENT_DISABLE_LOGGING ; feature dht : on off : composite propagated link-incompatible ; feature.compose off : TORRENT_DISABLE_DHT ; feature encryption : on off : composite propagated link-incompatible ; feature.compose off : TORRENT_DISABLE_ENCRYPTION ; feature mutable-torrents : on off : composite propagated link-incompatible ; feature.compose off : TORRENT_DISABLE_MUTABLE_TORRENTS ; feature crypto : built-in openssl gcrypt : composite propagated ; feature.compose openssl : TORRENT_USE_OPENSSL ; feature.compose gcrypt : TORRENT_USE_GCRYPT ; feature openssl-version : 1.1 pre1.1 : composite propagated ; feature resolve-countries : on off : composite propagated link-incompatible ; feature.compose off : TORRENT_DISABLE_RESOLVE_COUNTRIES ; feature character-set : unicode ansi : composite propagated link-incompatible ; feature.compose unicode : _UNICODE UNICODE ; feature deprecated-functions : on off : composite propagated link-incompatible ; feature.compose off : TORRENT_NO_DEPRECATE ; feature boost-link : default static shared : propagated composite ; feature debug-iterators : off on : composite propagated link-incompatible ; feature.compose on : _SCL_SECURE=1 _GLIBCXX_DEBUG ; feature fpic : off on : composite propagated link-incompatible ; feature.compose on : -fPIC ; feature profile-calls : off on : composite propagated link-incompatible ; feature.compose on : TORRENT_PROFILE_CALLS=1 ; # controls whether or not to export some internal # libtorrent functions. Used for unit testing feature export-extra : off on : composite propagated ; # this is a trick to get filename paths to targets to become shorter # making it possible to build on windows, especially mingw seems particular # for release builds, disable optimizations as they bump GCC over the edge of # allowed memory usage on travis-ci variant test_release : release : production on full shared off on on multi on ; variant test_debug : debug : openssl on on debug on full shared on on multi on ; variant test_barebones : debug : off off off off shared off off on on multi on ; # windows variants for libssl and libcrypto (they have different names and some # additional dependencies) lib advapi32 : : advapi32 ; lib user32 : : user32 ; lib shell32 : : shell32 ; lib gdi32 : : gdi32 ; lib z : : shared z ; # openssl libraries on windows alias ssl-deps : advapi32 user32 shell32 gdi32 ; # pre OpenSSL 1.1 windows lib crypto : ssl-deps : windows pre1.1 libeay32 @openssl-lib-path : : @openssl-include-path ; lib ssl : ssl-deps : windows pre1.1 ssleay32 crypto @openssl-lib-path : : @openssl-include-path ; # OpenSSL 1.1+ windows lib crypto : ssl-deps : windows libcrypto @openssl-lib-path : : @openssl-include-path ; lib ssl : ssl-deps : windows libssl crypto @openssl-lib-path : : @openssl-include-path ; # generic OpenSSL lib crypto : : crypto z @openssl-lib-path : : @openssl-include-path ; lib ssl : : ssl crypto @openssl-lib-path : : @openssl-include-path ; lib dbghelp : : dbghelp ; # required for networking on beos lib netkit : : net /boot/system/lib shared ; lib gcc : : gcc static ; # when using iconv lib libiconv : : iconv shared /usr/local/lib ; # openssl on linux/bsd etc. lib gcrypt : : gcrypt shared /opt/local/lib ; lib dl : : shared dl ; # time functions used on linux require librt lib librt : : rt shared ; lib libsocket : : libnsl socket shared /usr/sfw/lib shared ; lib libnsl : : nsl shared /usr/sfw/lib shared ; # socket libraries on windows lib wsock32 : : wsock32 shared ; lib ws2_32 : : ws2_32 shared ; lib iphlpapi : : iphlpapi shared ; SOURCES = alert alert_manager announce_entry assert bandwidth_limit bandwidth_manager bandwidth_queue_entry bdecode bitfield block_cache bloom_filter chained_buffer choker close_reason cpuid crc32c create_torrent disk_buffer_holder disk_buffer_pool disk_io_job disk_job_pool entry error_code file_storage lazy_bdecode escape_string string_util file fingerprint gzip hasher hex http_connection http_stream http_parser identify_client ip_filter ip_voter merkle peer_connection platform_util bt_peer_connection web_connection_base web_peer_connection http_seed_connection peer_connection_handle i2p_stream instantiate_connection natpmp packet_buffer piece_picker peer_list proxy_base puff random receive_buffer resolve_links rss session session_handle session_impl session_call settings_pack socket_io socket_type socks5_stream stat storage torrent torrent_handle torrent_info torrent_peer torrent_peer_allocator torrent_status time tracker_manager http_tracker_connection udp_tracker_connection sha1 timestamp_history udp_socket upnp utf8 utp_socket_manager utp_stream file_pool lsd disk_buffer_pool disk_io_thread enum_net broadcast_socket magnet_uri parse_url ConvertUTF thread xml_parse version peer_class peer_class_set part_file stat_cache request_blocks session_stats performance_counters resolver session_settings proxy_settings file_progress # -- extensions -- metadata_transfer ut_pex ut_metadata lt_trackers smart_ban ; KADEMLIA_SOURCES = dht_storage dht_tracker msg node node_entry refresh rpc_manager find_data node_id routing_table traversal_algorithm dos_blocker get_peers item get_item put_data ; ED25519_SOURCES = add_scalar fe ge key_exchange keypair sc seed sha512 sign verify ; local usage-requirements = ./include ./include/libtorrent /usr/sfw/include # freebsd doesn't seem to include this automatically # and iconv.h is installed there /usr/local/include release:NDEBUG debug:TORRENT_DEBUG _FILE_OFFSET_BITS=64 BOOST_EXCEPTION_DISABLE # enable cancel support in asio BOOST_ASIO_ENABLE_CANCELIO @linking @sanitizer-options # these compiler settings just makes the compiler standard conforming msvc:"/Zc:wchar_t" msvc:"/Zc:forScope" # msvc optimizations msvc,release:"/OPT:ICF=5" msvc,release:"/OPT:REF" $(CXXFLAGS) $(LDFLAGS) # this works around a bug in asio in boost-1.39 BOOST_ASIO_HASH_MAP_BUCKETS=1021 ; project torrent ; lib torrent : # sources src/$(SOURCES).cpp : # requirements ./ed25519/src multi TORRENT_BUILDING_LIBRARY shared:TORRENT_BUILDING_SHARED BOOST_NO_DEPRECATED shared:BOOST_SYSTEM_SOURCE openssl:ssl openssl:crypto openssl,linux:dl on:src/kademlia/$(KADEMLIA_SOURCES).cpp on:ed25519/src/$(ED25519_SOURCES).cpp @building @warnings $(CXXFLAGS) # disable bogus deprecation warnings on msvc8 msvc:_SCL_SECURE_NO_DEPRECATE msvc:_CRT_SECURE_NO_DEPRECATE @tag $(usage-requirements) : # default build multi : # usage requirements $(usage-requirements) shared:TORRENT_LINKING_SHARED ; headers = [ path.glob-tree include/libtorrent : *.hpp ] ; package.install install : libtorrent : : torrent : $(headers) ; libtorrent-rasterbar-1.1.13/Jamroot.jam000066400000000000000000000000001351156116000200350ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/LICENSE000066400000000000000000000104351351156116000167530ustar00rootroot00000000000000Copyright (c) 2003-2018, 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. ------------------------------------------------------------------------------ puff.c Copyright (C) 2002, 2003 Mark Adler For conditions of distribution and use, see copyright notice in puff.h version 1.7, 3 Mar 2003 puff.c is a simple inflate written to be an unambiguous way to specify the deflate format. It is not written for speed but rather simplicity. As a side benefit, this code might actually be useful when small code is more important than speed, such as bootstrap applications. For typical deflate data, zlib's inflate() is about four times as fast as puff(). zlib's inflate compiles to around 20K on my machine, whereas puff.c compiles to around 4K on my machine (a PowerPC using GNU cc). If the faster decode() function here is used, then puff() is only twice as slow as zlib's inflate(). All dynamically allocated memory comes from the stack. The stack required is less than 2K bytes. This code is compatible with 16-bit int's and assumes that long's are at least 32 bits. puff.c uses the short data type, assumed to be 16 bits, for arrays in order to to conserve memory. The code works whether integers are stored big endian or little endian. In the comments below are "Format notes" that describe the inflate process and document some of the less obvious aspects of the format. This source code is meant to supplement RFC 1951, which formally describes the deflate format: http://www.zlib.org/rfc-deflate.html ------------------------------------------------------------------------------ Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libtorrent-rasterbar-1.1.13/LibtorrentRasterbarConfig.cmake.in000066400000000000000000000014101351156116000244660ustar00rootroot00000000000000# - Config file for the @PROJECT_NAME@ package # It defines the following variables # @PROJECT_NAME@_INCLUDE_DIRS - include directories for @PROJECT_NAME@ # @PROJECT_NAME@_LIBRARIES - libraries to link against @PACKAGE_INIT@ include(CMakeFindDependencyMacro) @_find_dependency_calls@ include("${CMAKE_CURRENT_LIST_DIR}/LibtorrentRasterbarTargets.cmake") get_target_property(@PROJECT_NAME@_INCLUDE_DIRS LibtorrentRasterbar::torrent-rasterbar INTERFACE_INCLUDE_DIRECTORIES) get_target_property(_lt_iface_link_libraries LibtorrentRasterbar::torrent-rasterbar INTERFACE_LINK_LIBRARIES) get_target_property(_lt_imported_location LibtorrentRasterbar::torrent-rasterbar IMPORTED_LOCATION) set(@PROJECT_NAME@_LIBRARIES "${_lt_imported_location};${_lt_iface_link_libraries}") libtorrent-rasterbar-1.1.13/Makefile.am000066400000000000000000000104501351156116000177770ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 AM_DISTCHECK_CONFIGURE_FLAGS = --enable-tests=yes SUBDIRS = include/libtorrent src examples test bindings tools DOCS_IMAGES = \ docs/client_test.png \ docs/cwnd.png \ docs/cwnd_thumb.png \ docs/delays.png \ docs/delays_thumb.png \ docs/hacking.html \ docs/merkle_tree.png \ docs/our_delay_base.png \ docs/our_delay_base_thumb.png \ docs/read_disk_buffers.png \ docs/read_disk_buffers.diagram \ docs/storage.png \ docs/todo.html \ docs/write_disk_buffers.png \ docs/write_disk_buffers.diagram \ docs/ip_id_v4.png \ docs/ip_id_v6.png \ docs/hash_distribution.png \ docs/complete_bit_prefixes.png \ docs/troubleshooting.dot \ docs/troubleshooting.png \ docs/troubleshooting_thumb.png \ docs/hacking.diagram \ docs/hacking.png \ docs/disk_cache.diagram \ docs/disk_cache.png \ docs/utp_stack.diagram \ docs/utp_stack.png \ docs/bitcoin.png \ docs/style.css \ docs/rst.css \ docs/img/bg.png \ docs/img/blue_bottom.png \ docs/img/blue_top.png \ docs/img/dotline.gif \ docs/img/minus.gif \ docs/img/orange.png DOCS_PAGES = \ docs/building.html \ docs/client_test.html \ docs/contributing.html \ docs/dht_extensions.html \ docs/dht_rss.html \ docs/dht_sec.html \ docs/dht_store.html \ docs/examples.html \ docs/extension_protocol.html \ docs/features.html \ docs/index.html \ docs/manual-ref.html \ docs/projects.html \ docs/python_binding.html \ docs/tuning.html \ docs/settings.rst \ docs/stats_counters.rst \ docs/troubleshooting.html \ docs/udp_tracker_protocol.html \ docs/utp.html \ docs/streaming.html \ docs/building.rst \ docs/client_test.rst \ docs/contributing.rst \ docs/dht_extensions.rst \ docs/dht_rss.rst \ docs/dht_sec.rst \ docs/dht_store.rst \ docs/examples.rst \ docs/extension_protocol.rst \ docs/features.rst \ docs/index.rst \ docs/manual-ref.rst \ docs/projects.rst \ docs/python_binding.rst \ docs/tuning.rst \ docs/troubleshooting.rst \ docs/udp_tracker_protocol.rst \ docs/utp.rst \ docs/streaming.rst \ docs/tutorial.rst \ docs/tutorial.html \ docs/reference-Alerts.html \ docs/reference-Bdecoding.html \ docs/reference-Bencoding.html \ docs/reference-Core.html \ docs/reference-Create_Torrents.html \ docs/reference-Custom_Storage.html \ docs/reference-ed25519.html \ docs/reference-Error_Codes.html \ docs/reference-Filter.html \ docs/reference-Plugins.html \ docs/reference-Settings.html \ docs/reference-Storage.html \ docs/reference-Utility.html \ docs/reference.html \ docs/single-page-ref.html ED25519_SOURCE = \ ed25519/readme.md \ ed25519/test.c \ ed25519/src/fe.h \ ed25519/src/fixedint.h \ ed25519/src/ge.h \ ed25519/src/precomp_data.h \ ed25519/src/sc.h \ ed25519/src/sha512.h EXTRA_DIST = \ Jamfile \ Jamroot.jam \ CMakeLists.txt \ test/CMakeLists.txt \ setup.py \ LibtorrentRasterbarConfig.cmake.in \ LICENSE \ README.rst \ cmake/Modules/FindLibGcrypt.cmake \ cmake/Modules/GeneratePkgConfig.cmake \ cmake/Modules/LibtorrentMacros.cmake \ cmake/Modules/ucm_flags.cmake \ cmake/Modules/GeneratePkgConfig/generate-pkg-config.cmake.in \ cmake/Modules/GeneratePkgConfig/pkg-config.cmake.in \ cmake/Modules/GeneratePkgConfig/target-compile-settings.cmake.in \ $(DOCS_PAGES) \ $(DOCS_IMAGES) \ $(ED25519_SOURCE) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libtorrent-rasterbar.pc libtorrent-rasterbar-1.1.13/Makefile.in000066400000000000000000001021431351156116000200110ustar00rootroot00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 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@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd 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@ target_triplet = @target@ subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ $(top_srcdir)/m4/ax_boost_chrono.m4 \ $(top_srcdir)/m4/ax_boost_python.m4 \ $(top_srcdir)/m4/ax_boost_random.m4 \ $(top_srcdir)/m4/ax_boost_system.m4 \ $(top_srcdir)/m4/ax_check_openssl.m4 \ $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = libtorrent-rasterbar.pc CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac 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 = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkgconfigdir)" DATA = $(pkgconfig_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in \ $(srcdir)/libtorrent-rasterbar.pc.in \ $(top_srcdir)/build-aux/compile \ $(top_srcdir)/build-aux/config.guess \ $(top_srcdir)/build-aux/config.rpath \ $(top_srcdir)/build-aux/config.sub \ $(top_srcdir)/build-aux/install-sh \ $(top_srcdir)/build-aux/ltmain.sh \ $(top_srcdir)/build-aux/missing AUTHORS COPYING ChangeLog NEWS \ build-aux/compile build-aux/config.guess \ build-aux/config.rpath build-aux/config.sub \ build-aux/install-sh build-aux/ltmain.sh build-aux/missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BOOST_CHRONO_LIB = @BOOST_CHRONO_LIB@ BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ BOOST_LDFLAGS = @BOOST_LDFLAGS@ BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ BOOST_RANDOM_LIB = @BOOST_RANDOM_LIB@ BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUGFLAGS = @DEBUGFLAGS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONV_LIBS = @ICONV_LIBS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ PYTHON_CXXFLAGS = @PYTHON_CXXFLAGS@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I m4 AM_DISTCHECK_CONFIGURE_FLAGS = --enable-tests=yes SUBDIRS = include/libtorrent src examples test bindings tools DOCS_IMAGES = \ docs/client_test.png \ docs/cwnd.png \ docs/cwnd_thumb.png \ docs/delays.png \ docs/delays_thumb.png \ docs/hacking.html \ docs/merkle_tree.png \ docs/our_delay_base.png \ docs/our_delay_base_thumb.png \ docs/read_disk_buffers.png \ docs/read_disk_buffers.diagram \ docs/storage.png \ docs/todo.html \ docs/write_disk_buffers.png \ docs/write_disk_buffers.diagram \ docs/ip_id_v4.png \ docs/ip_id_v6.png \ docs/hash_distribution.png \ docs/complete_bit_prefixes.png \ docs/troubleshooting.dot \ docs/troubleshooting.png \ docs/troubleshooting_thumb.png \ docs/hacking.diagram \ docs/hacking.png \ docs/disk_cache.diagram \ docs/disk_cache.png \ docs/utp_stack.diagram \ docs/utp_stack.png \ docs/bitcoin.png \ docs/style.css \ docs/rst.css \ docs/img/bg.png \ docs/img/blue_bottom.png \ docs/img/blue_top.png \ docs/img/dotline.gif \ docs/img/minus.gif \ docs/img/orange.png DOCS_PAGES = \ docs/building.html \ docs/client_test.html \ docs/contributing.html \ docs/dht_extensions.html \ docs/dht_rss.html \ docs/dht_sec.html \ docs/dht_store.html \ docs/examples.html \ docs/extension_protocol.html \ docs/features.html \ docs/index.html \ docs/manual-ref.html \ docs/projects.html \ docs/python_binding.html \ docs/tuning.html \ docs/settings.rst \ docs/stats_counters.rst \ docs/troubleshooting.html \ docs/udp_tracker_protocol.html \ docs/utp.html \ docs/streaming.html \ docs/building.rst \ docs/client_test.rst \ docs/contributing.rst \ docs/dht_extensions.rst \ docs/dht_rss.rst \ docs/dht_sec.rst \ docs/dht_store.rst \ docs/examples.rst \ docs/extension_protocol.rst \ docs/features.rst \ docs/index.rst \ docs/manual-ref.rst \ docs/projects.rst \ docs/python_binding.rst \ docs/tuning.rst \ docs/troubleshooting.rst \ docs/udp_tracker_protocol.rst \ docs/utp.rst \ docs/streaming.rst \ docs/tutorial.rst \ docs/tutorial.html \ docs/reference-Alerts.html \ docs/reference-Bdecoding.html \ docs/reference-Bencoding.html \ docs/reference-Core.html \ docs/reference-Create_Torrents.html \ docs/reference-Custom_Storage.html \ docs/reference-ed25519.html \ docs/reference-Error_Codes.html \ docs/reference-Filter.html \ docs/reference-Plugins.html \ docs/reference-Settings.html \ docs/reference-Storage.html \ docs/reference-Utility.html \ docs/reference.html \ docs/single-page-ref.html ED25519_SOURCE = \ ed25519/readme.md \ ed25519/test.c \ ed25519/src/fe.h \ ed25519/src/fixedint.h \ ed25519/src/ge.h \ ed25519/src/precomp_data.h \ ed25519/src/sc.h \ ed25519/src/sha512.h EXTRA_DIST = \ Jamfile \ Jamroot.jam \ CMakeLists.txt \ test/CMakeLists.txt \ setup.py \ LibtorrentRasterbarConfig.cmake.in \ LICENSE \ README.rst \ cmake/Modules/FindLibGcrypt.cmake \ cmake/Modules/GeneratePkgConfig.cmake \ cmake/Modules/LibtorrentMacros.cmake \ cmake/Modules/ucm_flags.cmake \ cmake/Modules/GeneratePkgConfig/generate-pkg-config.cmake.in \ cmake/Modules/GeneratePkgConfig/pkg-config.cmake.in \ cmake/Modules/GeneratePkgConfig/target-compile-settings.cmake.in \ $(DOCS_PAGES) \ $(DOCS_IMAGES) \ $(ED25519_SOURCE) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libtorrent-rasterbar.pc all: all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): libtorrent-rasterbar.pc: $(top_builddir)/config.status $(srcdir)/libtorrent-rasterbar.pc.in cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(pkgconfigdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-libtool \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-pkgconfigDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-pkgconfigDATA .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am clean clean-cscope clean-generic \ clean-libtool cscope cscopelist-am ctags ctags-am dist \ dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ dist-xz dist-zip distcheck distclean distclean-generic \ distclean-libtool distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkgconfigDATA install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-pkgconfigDATA .PRECIOUS: Makefile # 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: libtorrent-rasterbar-1.1.13/NEWS000066400000000000000000000000161351156116000164370ustar00rootroot00000000000000See ChangeLog libtorrent-rasterbar-1.1.13/README.rst000066400000000000000000000050341351156116000174340ustar00rootroot00000000000000libtorrent ---------- .. image:: https://travis-ci.org/arvidn/libtorrent.svg?branch=master :target: https://travis-ci.org/arvidn/libtorrent .. image:: https://ci.appveyor.com/api/projects/status/w7teauvub5813mew/branch/master?svg=true :target: https://ci.appveyor.com/project/arvidn/libtorrent/branch/master .. image:: https://codecov.io/github/arvidn/libtorrent/coverage.svg?branch=master :target: https://codecov.io/github/arvidn/libtorrent?branch=master&view=all#sort=missing&dir=desc .. image:: https://www.openhub.net/p/rasterbar-libtorrent/widgets/project_thin_badge.gif :target: https://www.openhub.net/p/rasterbar-libtorrent?ref=sample libtorrent is an open source C++ library implementing the BitTorrent protocol, along with most popular extensions, making it suitable for real world deployment. It is configurable to be able to fit both servers and embedded devices. The main goals of libtorrent are to be efficient and easy to use. See `libtorrent.org`__ for more detailed build and usage instructions. .. __: http://libtorrent.org To build with boost-build, make sure boost and boost-build is installed and run: b2 In the libtorrent root. To build the examples, run ``b2`` in the ``examples`` directory. See `building.html`__ for more details on how to build and which configuration options are available. For python bindings, see `the python docs`__. .. __: docs/building.rst .. __: docs/python_binding.rst pull request checklist ...................... When creating a pull request, please consider the following checklist: * make sure both travis-CI and appveyor builds are green. Note that on gcc and clang warnings are treated as errors. Some tests may be flapping, if so, please issue a rebuild of the specific build configuration. (I'm working on making all tests deterministic) * If adding a user-facing feature, please add brief entry to ``ChangeLog`` * Add a unit test to confirm the new behavior or feature. Don't forget negative tests (i.e. failure cases) and please pay as much care to tests as you would production code. * rebase on top of master periodically * if your patch is against the current stable release branch, please also forward-port the patch to master (at the time of this writing, automatic merge in git does not work, possibly because the branch was created in svn) * if your patch adds a new .cpp file, please make sure it's added to the appropriate ``Jamfile``, ``Makefile.am`` and ``CMakeList.txt``. If it's adding a header file, make sure it's added to ``include/libtorrent/Makefile.am``. libtorrent-rasterbar-1.1.13/aclocal.m4000066400000000000000000001501321351156116000176050ustar00rootroot00000000000000# generated automatically by aclocal 1.15.1 -*- Autoconf -*- # Copyright (C) 1996-2017 Free Software Foundation, Inc. # This file 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. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # Copyright (C) 2002-2017 Free Software Foundation, Inc. # # This file 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. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.15' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.15.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.15.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file 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. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2017 Free Software Foundation, Inc. # # This file 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. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file 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. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file 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. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each '.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2017 Free Software Foundation, Inc. # # This file 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 macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file 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. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2017 Free Software Foundation, Inc. # # This file 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. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering # Copyright (C) 1996-2017 Free Software Foundation, Inc. # # This file 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. # AM_MAINTAINER_MODE([DEFAULT-MODE]) # ---------------------------------- # Control maintainer-specific portions of Makefiles. # Default is to disable them, unless 'enable' is passed literally. # For symmetry, 'disable' may be passed as well. Anyway, the user # can override the default with the --enable/--disable switch. AC_DEFUN([AM_MAINTAINER_MODE], [m4_case(m4_default([$1], [disable]), [enable], [m4_define([am_maintainer_other], [disable])], [disable], [m4_define([am_maintainer_other], [enable])], [m4_define([am_maintainer_other], [enable]) m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) dnl maintainer-mode's default is 'disable' unless 'enable' is passed AC_ARG_ENABLE([maintainer-mode], [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], am_maintainer_other[ make rules and dependencies not useful (and sometimes confusing) to the casual installer])], [USE_MAINTAINER_MODE=$enableval], [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) AC_MSG_RESULT([$USE_MAINTAINER_MODE]) AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) MAINT=$MAINTAINER_MODE_TRUE AC_SUBST([MAINT])dnl ] ) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file 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. # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2017 Free Software Foundation, Inc. # # This file 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. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file 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. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file 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. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file 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. # AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------------- # Adds support for distributing Python modules and packages. To # install modules, copy them to $(pythondir), using the python_PYTHON # automake variable. To install a package with the same name as the # automake package, install to $(pkgpythondir), or use the # pkgpython_PYTHON automake variable. # # The variables $(pyexecdir) and $(pkgpyexecdir) are provided as # locations to install python extension modules (shared libraries). # Another macro is required to find the appropriate flags to compile # extension modules. # # If your package is configured with a different prefix to python, # users will have to add the install directory to the PYTHONPATH # environment variable, or create a .pth file (see the python # documentation for details). # # If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will # cause an error if the version of python installed on the system # doesn't meet the requirement. MINIMUM-VERSION should consist of # numbers and dots only. AC_DEFUN([AM_PATH_PYTHON], [ dnl Find a Python interpreter. Python versions prior to 2.0 are not dnl supported. (2.0 was released on October 16, 2000). dnl FIXME: Remove the need to hard-code Python versions here. m4_define_default([_AM_PYTHON_INTERPRETER_LIST], [python python2 python3 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 dnl python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0]) AC_ARG_VAR([PYTHON], [the Python interpreter]) m4_if([$1],[],[ dnl No version check is needed. # Find any Python interpreter. if test -z "$PYTHON"; then AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) fi am_display_PYTHON=python ], [ dnl A version check is needed. if test -n "$PYTHON"; then # If the user set $PYTHON, use it and don't search something else. AC_MSG_CHECKING([whether $PYTHON version is >= $1]) AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_ERROR([Python interpreter is too old])]) am_display_PYTHON=$PYTHON else # Otherwise, try each interpreter until we find one that satisfies # VERSION. AC_CACHE_CHECK([for a Python interpreter with version >= $1], [am_cv_pathless_PYTHON],[ for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do test "$am_cv_pathless_PYTHON" = none && break AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) done]) # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. if test "$am_cv_pathless_PYTHON" = none; then PYTHON=: else AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) fi am_display_PYTHON=$am_cv_pathless_PYTHON fi ]) if test "$PYTHON" = :; then dnl Run any user-specified action, or abort. m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) else dnl Query Python for its version number. Getting [:3] seems to be dnl the best way to do this; it's what "site.py" does in the standard dnl library. AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`]) AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) dnl Use the values of $prefix and $exec_prefix for the corresponding dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made dnl distinct variables so they can be overridden if need be. However, dnl general consensus is that you shouldn't need this ability. AC_SUBST([PYTHON_PREFIX], ['${prefix}']) AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) dnl At times (like when building shared libraries) you may want dnl to know which OS platform Python thinks this is. AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`]) AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) # Just factor out some code duplication. am_python_setup_sysconfig="\ import sys # Prefer sysconfig over distutils.sysconfig, for better compatibility # with python 3.x. See automake bug#10227. try: import sysconfig except ImportError: can_use_sysconfig = 0 else: can_use_sysconfig = 1 # Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: # try: from platform import python_implementation if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7': can_use_sysconfig = 0 except ImportError: pass" dnl Set up 4 directories: dnl pythondir -- where to install python scripts. This is the dnl site-packages directory, not the python standard library dnl directory like in previous automake betas. This behavior dnl is more consistent with lispdir.m4 for example. dnl Query distutils for this directory. AC_CACHE_CHECK([for $am_display_PYTHON script directory], [am_cv_python_pythondir], [if test "x$prefix" = xNONE then am_py_prefix=$ac_default_prefix else am_py_prefix=$prefix fi am_cv_python_pythondir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') sys.stdout.write(sitedir)"` case $am_cv_python_pythondir in $am_py_prefix*) am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` ;; *) case $am_py_prefix in /usr|/System*) ;; *) am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages ;; esac ;; esac ]) AC_SUBST([pythondir], [$am_cv_python_pythondir]) dnl pkgpythondir -- $PACKAGE directory under pythondir. Was dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is dnl more consistent with the rest of automake. AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) dnl pyexecdir -- directory for installing python extension modules dnl (shared libraries) dnl Query distutils for this directory. AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], [am_cv_python_pyexecdir], [if test "x$exec_prefix" = xNONE then am_py_exec_prefix=$am_py_prefix else am_py_exec_prefix=$exec_prefix fi am_cv_python_pyexecdir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') sys.stdout.write(sitedir)"` case $am_cv_python_pyexecdir in $am_py_exec_prefix*) am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` ;; *) case $am_py_exec_prefix in /usr|/System*) ;; *) am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages ;; esac ;; esac ]) AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) dnl Run any user-specified action. $2 fi ]) # AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # --------------------------------------------------------------------------- # Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. # Run ACTION-IF-FALSE otherwise. # This test uses sys.hexversion instead of the string equivalent (first # word of sys.version), in order to cope with versions such as 2.2c1. # This supports Python 2.0 or higher. (2.0 was released on October 16, 2000). AC_DEFUN([AM_PYTHON_CHECK_VERSION], [prog="import sys # split strings by '.' and convert to numeric. Append some zeros # because we need at least 4 digits for the hex conversion. # map returns an iterator in Python 3.0 and a list in 2.x minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]] minverhex = 0 # xrange is not present in Python 3.0 and range returns an iterator for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]] sys.exit(sys.hexversion < minverhex)" AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file 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. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2017 Free Software Foundation, Inc. # # This file 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. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2017 Free Software Foundation, Inc. # # This file 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. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file 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. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2017 Free Software Foundation, Inc. # # This file 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. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2017 Free Software Foundation, Inc. # # This file 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. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/ax_boost_base.m4]) m4_include([m4/ax_boost_chrono.m4]) m4_include([m4/ax_boost_python.m4]) m4_include([m4/ax_boost_random.m4]) m4_include([m4/ax_boost_system.m4]) m4_include([m4/ax_check_openssl.m4]) m4_include([m4/ax_pthread.m4]) m4_include([m4/ax_python_devel.m4]) m4_include([m4/gettext-lib.m4]) m4_include([m4/iconv.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) m4_include([m4/pkgconfig.m4]) libtorrent-rasterbar-1.1.13/bindings/000077500000000000000000000000001351156116000175405ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/bindings/CMakeLists.txt000066400000000000000000000000671351156116000223030ustar00rootroot00000000000000if (python-bindings) add_subdirectory(python) endif() libtorrent-rasterbar-1.1.13/bindings/Makefile.am000066400000000000000000000001151351156116000215710ustar00rootroot00000000000000 SUBDIRS = python EXTRA_DIST = \ CMakeLists.txt \ README.txt libtorrent-rasterbar-1.1.13/bindings/Makefile.in000066400000000000000000000474461351156116000216240ustar00rootroot00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 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@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd 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@ target_triplet = @target@ subdir = bindings ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ $(top_srcdir)/m4/ax_boost_chrono.m4 \ $(top_srcdir)/m4/ax_boost_python.m4 \ $(top_srcdir)/m4/ax_boost_random.m4 \ $(top_srcdir)/m4/ax_boost_system.m4 \ $(top_srcdir)/m4/ax_check_openssl.m4 \ $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BOOST_CHRONO_LIB = @BOOST_CHRONO_LIB@ BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ BOOST_LDFLAGS = @BOOST_LDFLAGS@ BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ BOOST_RANDOM_LIB = @BOOST_RANDOM_LIB@ BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUGFLAGS = @DEBUGFLAGS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONV_LIBS = @ICONV_LIBS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ PYTHON_CXXFLAGS = @PYTHON_CXXFLAGS@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = python EXTRA_DIST = \ CMakeLists.txt \ README.txt all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bindings/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign bindings/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: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am distclean 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-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # 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: libtorrent-rasterbar-1.1.13/bindings/README.txt000066400000000000000000000002201351156116000212300ustar00rootroot00000000000000Documentation covering building and using the python binding for libtorrent is located in the main doc directory. See docs/python_binding.html libtorrent-rasterbar-1.1.13/bindings/python/000077500000000000000000000000001351156116000210615ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/bindings/python/CMakeLists.txt000066400000000000000000000140661351156116000236300ustar00rootroot00000000000000# To build python bindings we need a python executable and boost python module. Unfortunately, # their names might not be interlinked and we can not implement a general solution. # The code below assumes default boost installation, when the module for python 2 is named # 'python' and the module for python 3 is named 'python3'. # To customize that one can provide a name for the Boost::python module via # 'boost-python-module-name' variable when invoking cmake. # E.g. on Gentoo with python 3.6 and Boost::python library name 'libboost_python-3.6.so' # the parameter would be -Dboost-python-module-name="python-3.6". # The extension module and the cpython executable have to use the same C runtime library. On Windows # Python is compiled with MSVC and we will test MSVC version to make sure that it is the same for # the given Python version and our extension module. See https://wiki.python.org/moin/WindowsCompilers # for details. We provide a flag to skip this test for whatever reason (pass -Dskip-python-runtime-test=True) # Sets _ret to a list of python versions (major.minor) that use the same MSVC runtime as this build does # assumes MSVC was detected already # See https://en.wikipedia.org/wiki/Microsoft_Visual_C++#Internal_version_numbering function(_get_compatible_python_versions _ret) if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16) list(APPEND _tmp 2.6 2.7 3.0 3.1 3.2) elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 17) list(APPEND _tmp 3.3 3.4) elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 20) list(APPEND _tmp 3.5 3.6) endif() set(${_ret} ${_tmp} PARENT_SCOPE) endfunction() if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" AND NOT skip-python-runtime-test) _get_compatible_python_versions(Python_ADDITIONAL_VERSIONS) endif() find_package(PythonInterp REQUIRED) if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" AND NOT skip-python-runtime-test) message(STATUS "Testing found python version. Requested: ${Python_ADDITIONAL_VERSIONS}, found: ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") if (NOT "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" IN_LIST Python_ADDITIONAL_VERSIONS) message(FATAL_ERROR "Incompatible Python and C runtime: MSVC ${CMAKE_CXX_COMPILER_VERSION} and Python ${PYTHON_VERSION_STRING}") endif() endif() set(Python_ADDITIONAL_VERSIONS "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") find_package(PythonLibs REQUIRED) if (NOT boost-python-module-name) # use active python if (PYTHON_VERSION_STRING VERSION_GREATER_EQUAL "3") set(_boost-python-module-name "python${PYTHON_VERSION_MAJOR}") else() set(_boost-python-module-name "python") # to overwrite possible value from a previous run endif() endif() set(boost-python-module-name ${_boost-python-module-name} CACHE STRING "Boost:python module name, e.g. 'pythom-3.6'") find_package(Boost REQUIRED COMPONENTS ${boost-python-module-name}) python_add_module(python-libtorrent src/alert.cpp src/big_number.cpp src/converters.cpp src/create_torrent.cpp src/datetime.cpp src/entry.cpp src/error_code.cpp src/fingerprint.cpp src/ip_filter.cpp src/magnet_uri.cpp src/module.cpp src/peer_info.cpp src/session.cpp src/session_settings.cpp src/string.cpp src/torrent_handle.cpp src/torrent_info.cpp src/torrent_status.cpp src/utility.cpp src/version.cpp ) set_target_properties(python-libtorrent PROPERTIES OUTPUT_NAME libtorrent ) target_include_directories(python-libtorrent PRIVATE ${PYTHON_INCLUDE_DIRS} ) string(TOUPPER "${boost-python-module-name}" boost_python_module_name_uppercase) target_link_libraries(python-libtorrent PRIVATE torrent-rasterbar ${Boost_${boost_python_module_name_uppercase}_LIBRARY} # Boost::python adds that but without a path to the library. Therefore we have to either # provide the path (but, unfortunately, FindPythonLibs.cmake does not return the library dir), # or give the full file name here (this FindPythonLibs.cmake provides to us). ${PYTHON_LIBRARIES} ) # Bindings module uses deprecated libtorrent features, thus we disable these warnings if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") check_cxx_compiler_flag("-Wno-deprecated-declarations" _WNO_DEPRECATED_DECLARATIONS) if (_WNO_DEPRECATED_DECLARATIONS) target_compile_options(python-libtorrent PRIVATE -Wno-deprecated-declarations) endif() endif() execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import distutils.sysconfig; print(';'.join(map(str, [ distutils.sysconfig.get_python_lib(plat_specific=True, prefix=''), distutils.sysconfig.get_config_var('EXT_SUFFIX') ])))" OUTPUT_VARIABLE _python_sysconfig_vars OUTPUT_STRIP_TRAILING_WHITESPACE) list(GET _python_sysconfig_vars 0 PYTHON_SITE_PACKAGES) list(GET _python_sysconfig_vars 1 PYTHON_EXT_SUFFIX) message(STATUS "Python site packages: ${PYTHON_SITE_PACKAGES}") # python 2 does not provide the 'EXT_SUFFIX' sysconfig variable, so we use cmake default then if (NOT "${PYTHON_EXT_SUFFIX}" STREQUAL "None") message(STATUS "Python extension suffix: ${PYTHON_EXT_SUFFIX}") # we mimic the name, created by setuptools # example: libtorrent.cpython-36m-x86_64-linux-gnu.so set_target_properties(python-libtorrent PROPERTIES SUFFIX ${PYTHON_EXT_SUFFIX}) endif() set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.cmake.in") set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py") set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/timestamp") set(DEPS python-libtorrent "${SETUP_PY}") configure_file(${SETUP_PY_IN} ${SETUP_PY} @ONLY) add_custom_command(OUTPUT ${OUTPUT} COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} build -b "${CMAKE_CURRENT_SOURCE_DIR}" COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} egg_info -b "${CMAKE_CURRENT_SOURCE_DIR}" COMMAND ${CMAKE_COMMAND} -E touch ${OUTPUT} DEPENDS ${DEPS}) add_custom_target(python_bindings ALL DEPENDS ${OUTPUT}) install(TARGETS python-libtorrent DESTINATION "${PYTHON_SITE_PACKAGES}") install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/libtorrent.egg-info" DESTINATION "${PYTHON_SITE_PACKAGES}") libtorrent-rasterbar-1.1.13/bindings/python/Jamfile000066400000000000000000000142551351156116000223620ustar00rootroot00000000000000import python ; import feature ; import feature : feature ; import project ; import targets ; import "class" : new ; import modules ; use-project /torrent : ../.. ; BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; # this is used to make bjam use the same version of python which is executing setup.py LIBTORRENT_PYTHON_INTERPRETER = [ modules.peek : LIBTORRENT_PYTHON_INTERPRETER ] ; feature lt-visibility : default hidden : composite ; feature.compose hidden : -fvisibility=hidden -fvisibility-inlines-hidden ; feature libtorrent-link : shared static : composite propagated ; feature libtorrent-python-pic : off on : composite propagated link-incompatible ; feature.compose on : -fPIC ; # this is just to force boost build to pick the desired python target when using LIBTORRENT_PYTHON_INTERPRETER feature libtorrent-python : on ; if $(LIBTORRENT_PYTHON_INTERPRETER) { echo "using python interpreter at: " $(LIBTORRENT_PYTHON_INTERPRETER) ; using python : : "$(LIBTORRENT_PYTHON_INTERPRETER)" : : : on ; } # copied from boost 1.63's boost python jamfile rule find-py3-version { local versions = [ feature.values python ] ; local py3ver ; for local v in $(versions) { if $(v) >= 3.0 { py3ver = $(v) ; } } return $(py3ver) ; } if $(BOOST_ROOT) { use-project /boost : $(BOOST_ROOT) ; alias boost_python : /boost/python//boost_python : : : $(BOOST_ROOT) ; if [ find-py3-version ] { alias boost_python3 : /boost/python//boost_python3 : : : $(BOOST_ROOT) ; } else { alias boost_python3 ; } } else { local boost-lib-search-path = /opt/local/lib /usr/lib /usr/local/lib /sw/lib /usr/g++/lib ; local boost-include-path = /opt/local/include /usr/local/include /usr/sfw/include ; # the names are decorated in MacPorts lib boost_python : : darwin boost_python27-mt : : $(boost-include-path) ; lib boost_python3 : : darwin boost_python3-mt : : $(boost-include-path) ; lib boost_python : : boost_python : : $(boost-include-path) ; lib boost_python3 : : boost_python3 : : $(boost-include-path) ; } rule libtorrent_linking ( properties * ) { local result ; if ! windows in $(properties) && gcc in $(properties) { result += on ; } if gcc in $(properties) || darwin in $(properties) || clang in $(properties) || clang-darwin in $(properties) { result += hidden ; if ( gcc in $(properties) ) { result += -Wl,-Bsymbolic ; } } if static in $(properties) { ECHO "WARNING: you probably want to specify libtorrent-link=static rather than link=static" ; } if static in $(properties) && linux in $(properties) { ECHO "WARNING: you cannot link statically against boost-python on linux, because it links against pthread statically in that case, which is not allowed" ; } local boost_python_lib ; for local prop in $(properties) { switch $(prop) { case 2.* : boost_python_lib = boost_python ; case 3.* : boost_python_lib = boost_python3 ; } } if ! $(boost_python_lib) { ECHO "WARNING: unknown python version" ; boost_python_lib = boost_python ; } # linux must link dynamically against boost python because it pulls # in libpthread, which must be linked dynamically since we're building a .so # (the static build of libpthread is not position independent) if shared in $(properties) || linux in $(properties) { result += $(boost_python_lib)/shared ; } else { result += $(boost_python_lib)/static ; } if shared in $(properties) { result += /torrent//torrent/shared ; } else { result += /torrent//torrent/static ; } return $(result) ; } # this is a copy of the rule from boost-build's python-extension, but without # specifying no as a mandatory property. That property # would otherwise cause build failures because it suppresses linking against the # runtime library and kernel32 on windows rule my-python-extension ( name : sources * : requirements * : default-build * : usage-requirements * ) { requirements += /python//python_for_extensions ; local project = [ project.current ] ; targets.main-target-alternative [ new typed-target $(name) : $(project) : PYTHON_EXTENSION : [ targets.main-target-sources $(sources) : $(name) ] : [ targets.main-target-requirements $(requirements) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; } my-python-extension libtorrent : # sources src/module.cpp src/big_number.cpp src/converters.cpp src/create_torrent.cpp src/fingerprint.cpp src/utility.cpp src/session.cpp src/entry.cpp src/torrent_info.cpp src/string.cpp src/torrent_handle.cpp src/torrent_status.cpp src/session_settings.cpp src/version.cpp src/alert.cpp src/datetime.cpp src/peer_info.cpp src/ip_filter.cpp src/magnet_uri.cpp src/error_code.cpp : # requirements src gcc:-Wno-deprecated-declarations darwin:-Wno-deprecated-declarations darwin:-Wno-unused-command-line-argument @libtorrent_linking openssl:/torrent//ssl openssl:/torrent//crypto : # usage-requirements false ; install stage_module : libtorrent : . LIB ; install stage_dependencies : /torrent//torrent boost_python : dependencies on SHARED_LIB ; explicit stage_module ; explicit stage_dependencies ; libtorrent-rasterbar-1.1.13/bindings/python/Makefile.am000066400000000000000000000023201351156116000231120ustar00rootroot00000000000000 EXTRA_DIST = \ CMakeLists.txt \ Jamfile \ setup.py \ setup.py.cmake.in \ client.py \ simple_client.py \ make_torrent.py \ src/alert.cpp \ src/bytes.hpp \ src/big_number.cpp \ src/converters.cpp \ src/create_torrent.cpp \ src/datetime.cpp \ src/entry.cpp \ src/error_code.cpp \ src/fingerprint.cpp \ src/gil.hpp \ src/ip_filter.cpp \ src/magnet_uri.cpp \ src/module.cpp \ src/optional.hpp \ src/peer_info.cpp \ src/boost_python.hpp \ src/session.cpp \ src/session_settings.cpp \ src/string.cpp \ src/torrent_handle.cpp \ src/torrent_info.cpp \ src/torrent_status.cpp \ src/utility.cpp \ src/version.cpp if ENABLE_PYTHON_BINDING all-local: $(PYTHON) $(srcdir)/setup.py build install-exec-local: $(PYTHON) $(srcdir)/setup.py install @PYTHON_INSTALL_PARAMS@ uninstall-local: rm -rf $(DESTDIR)$(libdir)/python*/*-packages/*libtorrent* clean-local: $(PYTHON) $(srcdir)/setup.py clean --all endif libtorrent-rasterbar-1.1.13/bindings/python/Makefile.in000066400000000000000000000370121351156116000231310ustar00rootroot00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 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@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd 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@ target_triplet = @target@ subdir = bindings/python ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ $(top_srcdir)/m4/ax_boost_chrono.m4 \ $(top_srcdir)/m4/ax_boost_python.m4 \ $(top_srcdir)/m4/ax_boost_random.m4 \ $(top_srcdir)/m4/ax_boost_system.m4 \ $(top_srcdir)/m4/ax_check_openssl.m4 \ $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = link_flags compile_flags CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/compile_flags.in \ $(srcdir)/link_flags.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BOOST_CHRONO_LIB = @BOOST_CHRONO_LIB@ BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ BOOST_LDFLAGS = @BOOST_LDFLAGS@ BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ BOOST_RANDOM_LIB = @BOOST_RANDOM_LIB@ BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUGFLAGS = @DEBUGFLAGS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONV_LIBS = @ICONV_LIBS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ PYTHON_CXXFLAGS = @PYTHON_CXXFLAGS@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = \ CMakeLists.txt \ Jamfile \ setup.py \ setup.py.cmake.in \ client.py \ simple_client.py \ make_torrent.py \ src/alert.cpp \ src/bytes.hpp \ src/big_number.cpp \ src/converters.cpp \ src/create_torrent.cpp \ src/datetime.cpp \ src/entry.cpp \ src/error_code.cpp \ src/fingerprint.cpp \ src/gil.hpp \ src/ip_filter.cpp \ src/magnet_uri.cpp \ src/module.cpp \ src/optional.hpp \ src/peer_info.cpp \ src/boost_python.hpp \ src/session.cpp \ src/session_settings.cpp \ src/string.cpp \ src/torrent_handle.cpp \ src/torrent_info.cpp \ src/torrent_status.cpp \ src/utility.cpp \ src/version.cpp all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bindings/python/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign bindings/python/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: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): link_flags: $(top_builddir)/config.status $(srcdir)/link_flags.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ compile_flags: $(top_builddir)/config.status $(srcdir)/compile_flags.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$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 @ENABLE_PYTHON_BINDING_FALSE@all-local: all-am: Makefile all-local installdirs: 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: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." @ENABLE_PYTHON_BINDING_FALSE@uninstall-local: @ENABLE_PYTHON_BINDING_FALSE@install-exec-local: @ENABLE_PYTHON_BINDING_FALSE@clean-local: clean: clean-am clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-exec-local install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-local .MAKE: install-am install-strip .PHONY: all all-am all-local check check-am clean clean-generic \ clean-libtool clean-local cscopelist-am ctags-am distclean \ distclean-generic distclean-libtool distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-exec-local install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am uninstall-local .PRECIOUS: Makefile @ENABLE_PYTHON_BINDING_TRUE@all-local: @ENABLE_PYTHON_BINDING_TRUE@ $(PYTHON) $(srcdir)/setup.py build @ENABLE_PYTHON_BINDING_TRUE@install-exec-local: @ENABLE_PYTHON_BINDING_TRUE@ $(PYTHON) $(srcdir)/setup.py install @PYTHON_INSTALL_PARAMS@ @ENABLE_PYTHON_BINDING_TRUE@uninstall-local: @ENABLE_PYTHON_BINDING_TRUE@ rm -rf $(DESTDIR)$(libdir)/python*/*-packages/*libtorrent* @ENABLE_PYTHON_BINDING_TRUE@clean-local: @ENABLE_PYTHON_BINDING_TRUE@ $(PYTHON) $(srcdir)/setup.py clean --all # 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: libtorrent-rasterbar-1.1.13/bindings/python/client.py000077500000000000000000000247661351156116000227330ustar00rootroot00000000000000#!/usr/bin/env python # Copyright Daniel Wallin 2006. 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) import sys import atexit import libtorrent as lt import time import os.path import sys class WindowsConsole: def __init__(self): self.console = Console.getconsole() def clear(self): self.console.page() def write(self, str): self.console.write(str) def sleep_and_input(self, seconds): time.sleep(seconds) if msvcrt.kbhit(): return msvcrt.getch() return None class UnixConsole: def __init__(self): self.fd = sys.stdin self.old = termios.tcgetattr(self.fd.fileno()) new = termios.tcgetattr(self.fd.fileno()) new[3] = new[3] & ~termios.ICANON new[6][termios.VTIME] = 0 new[6][termios.VMIN] = 1 termios.tcsetattr(self.fd.fileno(), termios.TCSADRAIN, new) atexit.register(self._onexit) def _onexit(self): termios.tcsetattr(self.fd.fileno(), termios.TCSADRAIN, self.old) def clear(self): sys.stdout.write('\033[2J\033[0;0H') sys.stdout.flush() def write(self, str): sys.stdout.write(str) sys.stdout.flush() def sleep_and_input(self, seconds): read,_,_ = select.select([self.fd.fileno()], [], [], seconds) if len(read) > 0: return self.fd.read(1) return None if os.name == 'nt': import Console import msvcrt else: import termios import select def write_line(console, line): console.write(line) def add_suffix(val): prefix = ['B', 'kB', 'MB', 'GB', 'TB'] for i in range(len(prefix)): if abs(val) < 1000: if i == 0: return '%5.3g%s' % (val, prefix[i]) else: return '%4.3g%s' % (val, prefix[i]) val /= 1000 return '%6.3gPB' % val def progress_bar(progress, width): assert(progress <= 1) progress_chars = int(progress * width + 0.5) return progress_chars * '#' + (width - progress_chars) * '-' def print_peer_info(console, peers): out = ' down (total ) up (total ) q r flags block progress client\n' for p in peers: out += '%s/s ' % add_suffix(p.down_speed) out += '(%s) ' % add_suffix(p.total_download) out += '%s/s ' % add_suffix(p.up_speed) out += '(%s) ' % add_suffix(p.total_upload) out += '%2d ' % p.download_queue_length out += '%2d ' % p.upload_queue_length out += 'I' if p.flags & lt.peer_info.interesting else '.' out += 'C' if p.flags & lt.peer_info.choked else '.' out += 'i' if p.flags & lt.peer_info.remote_interested else '.' out += 'c' if p.flags & lt.peer_info.remote_choked else '.' out += 'e' if p.flags & lt.peer_info.supports_extensions else '.' out += 'l' if p.flags & lt.peer_info.local_connection else 'r' out += ' ' if p.downloading_piece_index >= 0: assert(p.downloading_progress <= p.downloading_total) out += progress_bar(float(p.downloading_progress) / p.downloading_total, 15) else: out += progress_bar(0, 15) out += ' ' if p.flags & lt.peer_info.handshake: id = 'waiting for handshake' elif p.flags & lt.peer_info.connecting: id = 'connecting to peer' else: id = p.client out += '%s\n' % id[:10] write_line(console, out) def print_download_queue(console, download_queue): out = "" for e in download_queue: out += '%4d: [' % e['piece_index']; for b in e['blocks']: s = b['state'] if s == 3: out += '#' elif s == 2: out += '=' elif s == 1: out += '-' else: out += ' ' out += ']\n' write_line(console, out) def add_torrent(ses, filename, options): atp = {} if filename.startswith('magnet:'): atp = lt.parse_magnet_uri_dict(filename) else: atp['ti'] = lt.torrent_info(filename) try: atp["resume_data"] = open(os.path.join(options.save_path, info.name() + '.fastresume'), 'rb').read() except: pass atp["save_path"] = options.save_path atp["storage_mode"] = lt.storage_mode_t.storage_mode_sparse atp["paused"] = False atp["auto_managed"] = True atp["duplicate_is_error"] = True ses.async_add_torrent(atp) def main(): from optparse import OptionParser parser = OptionParser() parser.add_option('-p', '--port', type='int', help='set listening port') parser.add_option('-d', '--max-download-rate', type='float', help='the maximum download rate given in kB/s. 0 means infinite.') parser.add_option('-u', '--max-upload-rate', type='float', help='the maximum upload rate given in kB/s. 0 means infinite.') parser.add_option('-s', '--save-path', type='string', help='the path where the downloaded file/folder should be placed.') parser.add_option('-r', '--proxy-host', type='string', help='sets HTTP proxy host and port (separated by \':\')') parser.set_defaults( port=6881 , max_download_rate=0 , max_upload_rate=0 , save_path='.' , proxy_host='' ) (options, args) = parser.parse_args() if options.port < 0 or options.port > 65525: options.port = 6881 options.max_upload_rate *= 1000 options.max_download_rate *= 1000 if options.max_upload_rate <= 0: options.max_upload_rate = -1 if options.max_download_rate <= 0: options.max_download_rate = -1 settings = { 'user_agent': 'python_client/' + lt.__version__, 'download_rate_limit': int(options.max_download_rate), 'upload_rate_limit': int(options.max_upload_rate), 'listen_interfaces': '0.0.0.0:%d' % options.port, 'alert_mask': lt.alert.category_t.all_categories } if options.proxy_host != '': settings['proxy_hostname'] = options.proxy_host.split(':')[0] settings['proxy_type'] = lt.proxy_type_t.http settings['proxy_port'] = options.proxy_host.split(':')[1] ses = lt.session(settings) # map torrent_handle to torrent_status torrents = {} alerts_log = [] for f in args: add_torrent(ses, f, options) if os.name == 'nt': console = WindowsConsole() else: console = UnixConsole() alive = True while alive: console.clear() out = '' for h,t in torrents.items(): out += 'name: %-40s\n' % t.name[:40] if t.state != lt.torrent_status.seeding: state_str = ['queued', 'checking', 'downloading metadata', \ 'downloading', 'finished', 'seeding', \ 'allocating', 'checking fastresume'] out += state_str[t.state] + ' ' out += '%5.4f%% ' % (t.progress*100) out += progress_bar(t.progress, 49) out += '\n' out += 'total downloaded: %d Bytes\n' % t.total_done out += 'peers: %d seeds: %d distributed copies: %d\n' % \ (t.num_peers, t.num_seeds, t.distributed_copies) out += '\n' out += 'download: %s/s (%s) ' \ % (add_suffix(t.download_rate), add_suffix(t.total_download)) out += 'upload: %s/s (%s) ' \ % (add_suffix(t.upload_rate), add_suffix(t.total_upload)) if t.state != lt.torrent_status.seeding: out += 'info-hash: %s\n' % t.info_hash out += 'next announce: %s\n' % t.next_announce out += 'tracker: %s\n' % t.current_tracker write_line(console, out) print_peer_info(console, t.handle.get_peer_info()) print_download_queue(console, t.handle.get_download_queue()) if t.state != lt.torrent_status.seeding: try: out = '\n' fp = t.handle.file_progress() ti = t.torrent_file for f,p in zip(ti.files(), fp): out += progress_bar(p / float(f.size), 20) out += ' ' + f.path + '\n' write_line(console, out) except: pass write_line(console, 76 * '-' + '\n') write_line(console, '(q)uit), (p)ause), (u)npause), (r)eannounce\n') write_line(console, 76 * '-' + '\n') alerts = ses.pop_alerts() for a in alerts: alerts_log.append(a.message()) # add new torrents to our list of torrent_status if type(a) == lt.add_torrent_alert: h = a.handle h.set_max_connections(60) h.set_max_uploads(-1) torrents[h] = h.status() # update our torrent_status array for torrents that have # changed some of their state if type(a) == lt.state_update_alert: for s in a.status: torrents[s.handle] = s if len(alerts_log) > 8: del alerts_log[:len(alerts_log) - 8] for a in alerts_log: write_line(console, a + '\n') c = console.sleep_and_input(0.5) ses.post_torrent_updates() if not c: continue if c == 'r': for h in torrents.keys(): h.force_reannounce() elif c == 'q': alive = False elif c == 'p': for h in torrents.keys(): h.pause() elif c == 'u': for h in torrents.keys(): h.resume() ses.pause() for h,t in torrents.items(): if not h.is_valid() or not t.has_metadata: continue h.save_resume_data() while len(torrents) > 0: alerts = ses.pop_alerts() for a in alerts: if type(a) == lt.save_resume_data_alert: data = lt.bencode(a.resume_data) h = a.handle if h in torrents: open(os.path.join(options.save_path, torrents[h].name + '.fastresume'), 'wb').write(data) del torrents[h] if type(a) == lt.save_resume_data_failed_alert: h = a.handle if h in torrents: print('failed to save resume data for ', torrents[h].name) del torrents[h] time.sleep(0.5) main() libtorrent-rasterbar-1.1.13/bindings/python/compile_flags.in000066400000000000000000000001561351156116000242170ustar00rootroot00000000000000-I@top_srcdir@/include @COMPILETIME_OPTIONS@ @CPPFLAGS@ @BOOST_CPPFLAGS@ @PYTHON_CXXFLAGS@ @OPENSSL_INCLUDES@ libtorrent-rasterbar-1.1.13/bindings/python/link_flags.in000066400000000000000000000002221351156116000235160ustar00rootroot00000000000000-L@top_builddir@/src/.libs @LDFLAGS@ @LIBS@ @BOOST_LDFLAGS@ @BOOST_SYSTEM_LIB@ @BOOST_PYTHON_LIB@ @PTHREAD_LIBS@ @OPENSSL_LIBS@ @OPENSSL_LDFLAGS@ libtorrent-rasterbar-1.1.13/bindings/python/make_torrent.py000077500000000000000000000022441351156116000241320ustar00rootroot00000000000000#!/usr/bin/env python import sys import os import libtorrent if len(sys.argv) < 3: print('usage make_torrent.py file tracker-url') sys.exit(1) input = os.path.abspath(sys.argv[1]) fs = libtorrent.file_storage() #def predicate(f): # print f # return True #libtorrent.add_files(fs, input, predicate) parent_input = os.path.split(input)[0] for root, dirs, files in os.walk(input): # skip directories starting with . if os.path.split(root)[1][0] == '.': continue for f in files: # skip files starting with . if f[0] == '.': continue # skip thumbs.db on windows if f == 'Thumbs.db': continue fname = os.path.join(root[len(parent_input)+1:], f) size = os.path.getsize(os.path.join(parent_input, fname)) print('%10d kiB %s' % (size / 1024, fname)) fs.add_file(fname, size); if fs.num_files() == 0: print('no files added') sys.exit(1) t = libtorrent.create_torrent(fs, 0, 4 * 1024 * 1024) t.add_tracker(sys.argv[2]) t.set_creator('libtorrent %s' % libtorrent.__version__) libtorrent.set_piece_hashes(t, parent_input, lambda x: sys.stderr.write('.')) sys.stderr.write('\n') f = open('out.torrent', 'wb+') f.write(libtorrent.bencode(t.generate())) f.close() libtorrent-rasterbar-1.1.13/bindings/python/setup.py000066400000000000000000000120071351156116000225730ustar00rootroot00000000000000#!/usr/bin/env python from distutils.core import setup, Extension from distutils.sysconfig import get_config_vars import os import platform import sys import shutil import multiprocessing import subprocess class flags_parser: def __init__(self): self.include_dirs = [] self.library_dirs = [] self.libraries = [] def parse(self, args): """Parse out the -I -L -l directives and return a list of all other arguments""" ret = [] for token in args.split(): prefix = token[:2] if prefix == '-I': self.include_dirs.append(token[2:]) elif prefix == '-L': self.library_dirs.append(token[2:]) elif prefix == '-l': self.libraries.append(token[2:]) else: ret.append(token) return ret def arch(): if platform.system() != 'Darwin': return [] a = os.uname()[4] if a == 'Power Macintosh': a = 'ppc' return ['-arch', a] def target_specific(): if platform.system() != 'Darwin': return [] # on mavericks, clang will fail when unknown arguments are # passed in. python distutils will pass in arguments it doesn't # know about return ['-Wno-error=unused-command-line-argument-hard-error-in-future'] try: with open('compile_flags') as _file: extra_cmd = _file.read() except: extra_cmd = None try: with open('link_flags') as _file: ldflags = _file.read() except: ldflags = None ext = None packages = None if '--bjam' in sys.argv: if '--bjam' in sys.argv: del sys.argv[sys.argv.index('--bjam')] if not '--help' in sys.argv \ and not '--help-commands' in sys.argv: toolset = '' file_ext = '.so' if platform.system() == 'Windows': file_ext = '.pyd' # See https://wiki.python.org/moin/WindowsCompilers for a table of msvc versions # used for each python version # Specify the full version number for 9.0 and 10.0 because apparently # older versions of boost don't support only specifying the major number and # there was only one version of msvc with those majors. # Only specify the major for msvc-14 so that 14.1, 14.11, etc can be used. # Hopefully people building with msvc-14 are using a new enough version of boost # for this to work. if sys.version_info[0:2] in ((2, 6), (2, 7), (3, 0), (3, 1), (3, 2)): toolset = ' toolset=msvc-9.0' elif sys.version_info[0:2] in ((3, 3), (3, 4)): toolset = ' toolset=msvc-10.0' elif sys.version_info[0:2] in ((3, 5), (3, 6)): toolset = ' toolset=msvc-14' else: # unknown python version, lets hope the user has the right version of msvc configured toolset = ' toolset=msvc' parallel_builds = ' -j%d' % multiprocessing.cpu_count() if sys.maxsize > 2**32: address_model = ' address-model=64' else: address_model = ' address-model=32' # add extra quoting around the path to prevent bjam from parsing it as a list # if the path has spaces os.environ['LIBTORRENT_PYTHON_INTERPRETER'] = '"' + sys.executable + '"' # build libtorrent using bjam and build the installer with distutils cmdline = 'b2 libtorrent-link=static boost-link=static release optimization=space stage_module --abbreviate-paths' + address_model + toolset + parallel_builds print(cmdline) if os.system(cmdline) != 0: print('build failed') sys.exit(1) try: os.mkdir('build') except: pass try: shutil.rmtree('build/lib') except: pass try: os.mkdir('build/lib') except: pass try: os.mkdir('libtorrent') except: pass shutil.copyfile('libtorrent' + file_ext, 'build/lib/libtorrent' + file_ext) packages = ['libtorrent'] else: # Remove the '-Wstrict-prototypes' compiler option, which isn't valid for C++. cfg_vars = get_config_vars() for key, value in cfg_vars.items(): if isinstance(value, str): cfg_vars[key] = value.replace('-Wstrict-prototypes', '') source_list = os.listdir(os.path.join(os.path.dirname(__file__), "src")) source_list = [os.path.abspath(os.path.join(os.path.dirname(__file__), "src", s)) for s in source_list if s.endswith(".cpp")] if extra_cmd: flags = flags_parser() # ldflags must be parsed first to ensure the correct library search path order extra_link = flags.parse(ldflags) extra_compile = flags.parse(extra_cmd) # for some reason distutils uses the CC environment variable to determine # the compiler to use for C++ if 'CXX' in os.environ: os.environ['CC'] = os.environ['CXX'] if 'CXXFLAGS' in os.environ: os.environ['CFLAGS'] = os.environ['CXXFLAGS'] ext = [Extension('libtorrent', sources = sorted(source_list), language='c++', include_dirs = flags.include_dirs, library_dirs = flags.library_dirs, extra_link_args = extra_link + arch(), extra_compile_args = extra_compile + arch() + target_specific(), libraries = ['torrent-rasterbar'] + flags.libraries)] setup(name = 'python-libtorrent', version = '1.1.13', author = 'Arvid Norberg', author_email = 'arvid@libtorrent.org', description = 'Python bindings for libtorrent-rasterbar', long_description = 'Python bindings for libtorrent-rasterbar', url = 'http://libtorrent.org', platforms = [platform.system() + '-' + platform.machine()], license = 'BSD', packages = packages, ext_modules = ext ) libtorrent-rasterbar-1.1.13/bindings/python/setup.py.cmake.in000066400000000000000000000007331351156116000242620ustar00rootroot00000000000000from setuptools import setup import platform setup( name='libtorrent', version='@libtorrent_VERSION@', author='Arvid Norberg', author_email='arvid@libtorrent.org', description='Python bindings for libtorrent-rasterbar', long_description='Python bindings for libtorrent-rasterbar', url='http://libtorrent.org', platforms=[platform.system() + '-' + platform.machine()], license='BSD', package_dir = {'': '@CMAKE_CURRENT_BINARY_DIR@'} ) libtorrent-rasterbar-1.1.13/bindings/python/simple_client.py000077500000000000000000000016061351156116000242700ustar00rootroot00000000000000#!/usr/bin/env python # Copyright Arvid Norberg 2008. 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) from __future__ import print_function import libtorrent as lt import time import sys ses = lt.session({'listen_interfaces': '0.0.0.0:6881'}) info = lt.torrent_info(sys.argv[1]) h = ses.add_torrent({'ti': info, 'save_path': '.'}) print('starting', h.name()) while (not h.is_seed()): s = h.status() print('\r%.2f%% complete (down: %.1f kB/s up: %.1f kB/s peers: %d) %s' % \ (s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \ s.num_peers, s.state), end=' ') alerts = ses.pop_alerts() for a in alerts: if a.category() & lt.alert.category_t.error_notification: print(a) sys.stdout.flush() time.sleep(1) print(h.name(), 'complete') libtorrent-rasterbar-1.1.13/bindings/python/src/000077500000000000000000000000001351156116000216505ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/bindings/python/src/alert.cpp000066400000000000000000000717031351156116000234730ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include "boost_python.hpp" #include #include #include // for piece_block #include #include #include "bytes.hpp" #include using namespace boost::python; using namespace libtorrent; bytes get_buffer(read_piece_alert const& rpa) { return rpa.buffer ? bytes(rpa.buffer.get(), rpa.size) : bytes(); } list stats_alert_transferred(stats_alert const& alert) { list result; for (int i = 0; i < alert.num_channels; ++i) { result.append(alert.transferred[i]); } return result; } list get_status_from_update_alert(state_update_alert const& alert) { list result; for (std::vector::const_iterator i = alert.status.begin(); i != alert.status.end(); ++i) { result.append(*i); } return result; } dict get_params(add_torrent_alert const& alert) { add_torrent_params const& p = alert.params; dict ret; ret["ti"] = p.ti; ret["info_hash"] = p.info_hash; ret["name"] = p.name; ret["save_path"] = p.save_path; ret["storage_mode"] = p.storage_mode; list trackers; for (std::vector::const_iterator i = p.trackers.begin(); i != p.trackers.end(); ++i) { trackers.append(*i); } ret["trackers"] = trackers; // TODO: dht_nodes ret["flags"] = p.flags; ret["trackerid"] = p.trackerid; ret["url"] = p.url; ret["source_feed_url"] = p.source_feed_url; ret["uuid"] = p.uuid; return ret; } list dht_stats_active_requests(dht_stats_alert const& a) { list result; for (std::vector::const_iterator i = a.active_requests.begin(); i != a.active_requests.end(); ++i) { dict d; d["type"] = i->type; d["outstanding_requests"] = i->outstanding_requests; d["timeouts"] = i->timeouts; d["responses"] = i->responses; d["branch_factor"] = i->branch_factor; d["nodes_left"] = i->nodes_left; d["last_sent"] = i->last_sent; d["first_timeout"] = i->first_timeout; result.append(d); } return result; } list dht_stats_routing_table(dht_stats_alert const& a) { list result; for (std::vector::const_iterator i = a.routing_table.begin(); i != a.routing_table.end(); ++i) { dict d; d["num_nodes"] = i->num_nodes; d["num_replacements"] = i->num_replacements; result.append(d); } return result; } dict dht_immutable_item(dht_immutable_item_alert const& alert) { dict d; d["key"] = alert.target; d["value"] = bytes(alert.item.to_string()); return d; } dict dht_mutable_item(dht_mutable_item_alert const& alert) { dict d; d["key"] = bytes(alert.key.data(), alert.key.size()); d["value"] = bytes(alert.item.to_string()); d["signature"] = bytes(alert.signature.data(), alert.signature.size()); d["seq"] = alert.seq; d["salt"] = bytes(alert.salt); d["authoritative"] = alert.authoritative; return d; } dict dht_put_item(dht_put_alert const& alert) { dict d; if (alert.target.is_all_zeros()) { d["public_key"] = bytes(alert.public_key.data(), alert.public_key.size()); d["signature"] = bytes(alert.signature.data(), alert.signature.size()); d["seq"] = alert.seq; d["salt"] = bytes(alert.salt); } else { d["target"] = alert.target; } return d; } dict session_stats_values(session_stats_alert const& alert) { std::vector map = session_stats_metrics(); dict d; for (std::vector::const_iterator i = map.begin(); i != map.end(); ++i) { d[i->name] = alert.values[i->value_index]; } return d; } list dht_get_peers_reply_alert_peers(dht_get_peers_reply_alert const& a) { list result; std::vector v(a.peers()); for (std::vector::const_iterator i = v.begin(); i != v.end(); ++i) { result.append(*i); } return result; } void bind_alert() { using boost::noncopyable; #ifndef TORRENT_NO_DEPRECATE typedef boost::shared_ptr alert_holder; if (boost::python::converter::registry::query( boost::python::type_id >()) == NULL) { register_ptr_to_python >(); } #else typedef alert alert_holder; #endif typedef return_value_policy by_value; { scope alert_scope = class_("alert", no_init) .def("message", &alert::message) .def("what", &alert::what) .def("category", &alert::category) #ifndef TORRENT_NO_DEPRECATE .def("severity", &alert::severity) #endif .def("__str__", &alert::message) ; #ifndef TORRENT_NO_DEPRECATE enum_("severity_levels") .value("debug", alert::debug) .value("info", alert::info) .value("warning", alert::warning) .value("critical", alert::critical) .value("fatal", alert::fatal) .value("none", alert::none) ; #endif enum_("category_t") .value("error_notification", alert::error_notification) .value("peer_notification", alert::peer_notification) .value("port_mapping_notification", alert::port_mapping_notification) .value("storage_notification", alert::storage_notification) .value("tracker_notification", alert::tracker_notification) .value("debug_notification", alert::debug_notification) .value("status_notification", alert::status_notification) .value("progress_notification", alert::progress_notification) .value("ip_block_notification", alert::ip_block_notification) .value("performance_warning", alert::performance_warning) .value("dht_notification", alert::dht_notification) .value("stats_notification", alert::stats_notification) .value("session_log_notification", alert::session_log_notification) .value("torrent_log_notification", alert::torrent_log_notification) .value("peer_log_notification", alert::peer_log_notification) .value("incoming_request_notification", alert::incoming_request_notification) .value("dht_log_notification", alert::dht_log_notification) .value("dht_operation_notification", alert::dht_operation_notification) .value("port_mapping_log_notification", alert::port_mapping_log_notification) .value("picker_log_notification", alert::picker_log_notification) .value("file_progress_notification", alert::file_progress_notification) .value("piece_progress_notification", alert::piece_progress_notification) .value("block_progress_notification", alert::block_progress_notification) // deliberately not INT_MAX. Arch linux crash while throwing an exception .value("all_categories", (alert::category_t)0xfffffff) ; } class_, noncopyable>( "torrent_alert", no_init) .add_property("handle", make_getter(&torrent_alert::handle, by_value())) .add_property("torrent_name", &torrent_alert::torrent_name) ; class_, noncopyable>( "tracker_alert", no_init) #ifndef TORRENT_NO_DEPRECATE .def_readonly("url", &tracker_alert::url) #endif .def("tracker_url", &tracker_alert::tracker_url) ; #ifndef TORRENT_NO_DEPRECATE class_, noncopyable>( "torrent_added_alert", no_init) ; #endif class_, noncopyable>( "torrent_removed_alert", no_init) .def_readonly("info_hash", &torrent_removed_alert::info_hash) ; class_, noncopyable>( "read_piece_alert", 0, no_init) .def_readonly("ec", &read_piece_alert::ec) .add_property("buffer", get_buffer) .def_readonly("piece", &read_piece_alert::piece) .def_readonly("size", &read_piece_alert::size) ; class_, noncopyable>( "peer_alert", no_init) .add_property("ip", make_getter(&peer_alert::ip, by_value())) .def_readonly("pid", &peer_alert::pid) ; class_, noncopyable>( "tracker_error_alert", no_init) #ifndef TORRENT_NO_DEPRECATE .def_readonly("msg", &tracker_error_alert::msg) #endif .def("error_message", &tracker_error_alert::error_message) .def_readonly("times_in_row", &tracker_error_alert::times_in_row) .def_readonly("status_code", &tracker_error_alert::status_code) .def_readonly("error", &tracker_error_alert::error) ; class_, noncopyable>( "tracker_warning_alert", no_init); class_, noncopyable>( "tracker_reply_alert", no_init) .def_readonly("num_peers", &tracker_reply_alert::num_peers) ; class_, noncopyable>( "tracker_announce_alert", no_init) .def_readonly("event", &tracker_announce_alert::event) ; class_, noncopyable>( "hash_failed_alert", no_init) .def_readonly("piece_index", &hash_failed_alert::piece_index) ; class_, noncopyable>( "peer_ban_alert", no_init); class_, noncopyable>( "peer_error_alert", no_init) .def_readonly("error", &peer_error_alert::error) ; class_, noncopyable>( "invalid_request_alert", no_init) .def_readonly("request", &invalid_request_alert::request) ; class_("peer_request") .def_readonly("piece", &peer_request::piece) .def_readonly("start", &peer_request::start) .def_readonly("length", &peer_request::length) .def(self == self) ; class_, noncopyable>( "torrent_error_alert", no_init) .def_readonly("error", &torrent_error_alert::error) ; class_, noncopyable>( "torrent_finished_alert", no_init); class_, noncopyable>( "piece_finished_alert", no_init) .def_readonly("piece_index", &piece_finished_alert::piece_index) ; class_, noncopyable>( "block_finished_alert", no_init) .def_readonly("block_index", &block_finished_alert::block_index) .def_readonly("piece_index", &block_finished_alert::piece_index) ; class_, noncopyable>( "block_downloading_alert", no_init) #ifndef TORRENT_NO_DEPRECATE .def_readonly("peer_speedmsg", &block_downloading_alert::peer_speedmsg) #endif .def_readonly("block_index", &block_downloading_alert::block_index) .def_readonly("piece_index", &block_downloading_alert::piece_index) ; class_, noncopyable>( "storage_moved_alert", no_init) #ifndef TORRENT_NO_DEPRECATE .def_readonly("path", &storage_moved_alert::path) #endif .def("storage_path", &storage_moved_alert::storage_path) ; class_, noncopyable>( "storage_moved_failed_alert", no_init) .def_readonly("error", &storage_moved_failed_alert::error) .def("file_path", &storage_moved_failed_alert::file_path) .def_readonly("operation", &storage_moved_failed_alert::operation) ; class_, noncopyable>( "torrent_deleted_alert", no_init) .def_readonly("info_hash", &torrent_deleted_alert::info_hash) ; class_, noncopyable>( "torrent_paused_alert", no_init); class_, noncopyable>( "torrent_checked_alert", no_init); class_, noncopyable>( "url_seed_alert", no_init) #ifndef TORRENT_NO_DEPRECATE .def_readonly("url", &url_seed_alert::url) .def_readonly("msg", &url_seed_alert::msg) #endif .def_readonly("error", &url_seed_alert::error) .def("server_url", &url_seed_alert::server_url) .def("error_message", &url_seed_alert::error_message) ; class_, noncopyable>( "file_error_alert", no_init) .def_readonly("error", &file_error_alert::error) .def("filename", &file_error_alert::filename) #ifndef TORRENT_NO_DEPRECATE .def_readonly("file", &file_error_alert::file) .def_readonly("msg", &file_error_alert::msg) #endif ; class_, noncopyable>( "metadata_failed_alert", no_init) .def_readonly("error", &metadata_failed_alert::error) ; class_, noncopyable>( "metadata_received_alert", no_init); class_, noncopyable>( "listen_failed_alert", no_init) .add_property("endpoint", make_getter(&listen_failed_alert::endpoint, by_value())) .def("listen_interface", &listen_failed_alert::listen_interface) .def_readonly("error", &listen_failed_alert::error) .def_readonly("operation", &listen_failed_alert::operation) .def_readonly("sock_type", &listen_failed_alert::sock_type) ; class_, noncopyable>( "listen_succeeded_alert", no_init) .add_property("endpoint", make_getter(&listen_succeeded_alert::endpoint, by_value())) .def_readonly("sock_type", &listen_succeeded_alert::sock_type) ; enum_("listen_succeded_alert_socket_type_t") .value("tcp", listen_succeeded_alert::tcp) .value("tcp_ssl", listen_succeeded_alert::tcp_ssl) .value("udp", listen_succeeded_alert::udp) .value("i2p", listen_succeeded_alert::i2p) .value("socks5", listen_succeeded_alert::socks5) .value("utp_ssl", listen_succeeded_alert::utp_ssl) ; enum_("listen_failed_alert_socket_type_t") .value("tcp", listen_failed_alert::tcp) .value("tcp_ssl", listen_failed_alert::tcp_ssl) .value("udp", listen_failed_alert::udp) .value("i2p", listen_failed_alert::i2p) .value("socks5", listen_failed_alert::socks5) .value("utp_ssl", listen_failed_alert::utp_ssl) ; class_, noncopyable>( "portmap_error_alert", no_init) .def_readonly("mapping", &portmap_error_alert::mapping) .def_readonly("map_type", &portmap_error_alert::map_type) .def_readonly("error", &portmap_error_alert::error) #ifndef TORRENT_NO_DEPRECATE .def_readonly("type", &portmap_error_alert::map_type) .def_readonly("msg", &portmap_error_alert::msg) #endif ; class_, noncopyable>( "portmap_alert", no_init) .def_readonly("mapping", &portmap_alert::mapping) .def_readonly("external_port", &portmap_alert::external_port) #ifndef TORRENT_NO_DEPRECATE .def_readonly("type", &portmap_alert::map_type) #endif .def_readonly("map_type", &portmap_alert::map_type) ; #ifndef TORRENT_DISABLE_LOGGING class_, noncopyable>( "portmap_log_alert", no_init) .def_readonly("map_type", &portmap_log_alert::map_type) #ifndef TORRENT_NO_DEPRECATE .def_readonly("type", &portmap_log_alert::map_type) .def_readonly("msg", &portmap_log_alert::msg) #endif ; #endif // TORRENT_DISABLE_LOGGING class_, noncopyable>( "fastresume_rejected_alert", no_init) .def_readonly("error", &fastresume_rejected_alert::error) #ifndef TORRENT_NO_DEPRECATE .def_readonly("msg", &fastresume_rejected_alert::msg) #endif .def("file_path", &fastresume_rejected_alert::file_path) .def_readonly("operation", &fastresume_rejected_alert::operation) ; class_, noncopyable>( "peer_blocked_alert", no_init) .add_property("ip", make_getter(&peer_blocked_alert::ip, by_value())) ; class_, noncopyable>( "scrape_reply_alert", no_init) .def_readonly("incomplete", &scrape_reply_alert::incomplete) .def_readonly("complete", &scrape_reply_alert::complete) ; class_, noncopyable>( "scrape_failed_alert", no_init) #ifndef TORRENT_NO_DEPRECATE .def_readonly("msg", &scrape_failed_alert::msg) #endif .def("error_message", &scrape_failed_alert::error_message) .def_readonly("error", &scrape_failed_alert::error) ; class_, noncopyable>( "udp_error_alert", no_init) .add_property("endpoint", make_getter(&udp_error_alert::endpoint, by_value())) .def_readonly("error", &udp_error_alert::error) ; class_, noncopyable>( "external_ip_alert", no_init) .add_property("external_address", make_getter(&external_ip_alert::external_address, by_value())) ; class_, noncopyable>( "save_resume_data_alert", no_init) .def_readonly("resume_data", &save_resume_data_alert::resume_data) ; class_, noncopyable>( "file_completed_alert", no_init) .def_readonly("index", &file_completed_alert::index) ; class_, noncopyable>( "file_renamed_alert", no_init) .def_readonly("index", &file_renamed_alert::index) #ifndef TORRENT_NO_DEPRECATE .def_readonly("name", &file_renamed_alert::name) #endif .def("new_name", &file_renamed_alert::new_name) ; class_, noncopyable>( "file_rename_failed_alert", no_init) .def_readonly("index", &file_rename_failed_alert::index) .def_readonly("error", &file_rename_failed_alert::error) ; class_, noncopyable>( "torrent_resumed_alert", no_init ); class_, noncopyable>( "state_changed_alert", no_init) .def_readonly("state", &state_changed_alert::state) .def_readonly("prev_state", &state_changed_alert::prev_state) ; class_, noncopyable>( "state_update_alert", no_init) .add_property("status", &get_status_from_update_alert) ; class_, noncopyable>( "i2p_alert", no_init) .add_property("error", &i2p_alert::error) ; class_, noncopyable>( "dht_reply_alert", no_init) .def_readonly("num_peers", &dht_reply_alert::num_peers) ; class_, noncopyable>( "dht_announce_alert", no_init) .add_property("ip", make_getter(&dht_announce_alert::ip, by_value())) .def_readonly("port", &dht_announce_alert::port) .def_readonly("info_hash", &dht_announce_alert::info_hash) ; class_, noncopyable>( "dht_get_peers_alert", no_init ) .def_readonly("info_hash", &dht_get_peers_alert::info_hash) ; class_, noncopyable>( "peer_unsnubbed_alert", no_init ); class_, noncopyable>( "peer_snubbed_alert", no_init ); class_, noncopyable>( "peer_connect_alert", no_init ); class_, noncopyable>( "peer_disconnected_alert", no_init) .def_readonly("error", &peer_disconnected_alert::error) #ifndef TORRENT_NO_DEPRECATE .def_readonly("msg", &peer_disconnected_alert::msg) #endif ; class_, noncopyable>( "request_dropped_alert", no_init) .def_readonly("block_index", &request_dropped_alert::block_index) .def_readonly("piece_index", &request_dropped_alert::piece_index) ; class_, noncopyable>( "block_timeout_alert", no_init) .def_readonly("block_index", &block_timeout_alert::block_index) .def_readonly("piece_index", &block_timeout_alert::piece_index) ; class_, noncopyable>( "unwanted_block_alert", no_init) .def_readonly("block_index", &unwanted_block_alert::block_index) .def_readonly("piece_index", &unwanted_block_alert::piece_index) ; class_, noncopyable>( "torrent_delete_failed_alert", no_init) #ifndef TORRENT_NO_DEPRECATE .def_readonly("msg", &torrent_delete_failed_alert::msg) #endif .def_readonly("error", &torrent_delete_failed_alert::error) .def_readonly("info_hash", &torrent_delete_failed_alert::info_hash) ; class_, noncopyable>( "save_resume_data_failed_alert", no_init) #ifndef TORRENT_NO_DEPRECATE .def_readonly("msg", &save_resume_data_failed_alert::msg) #endif .def_readonly("error", &save_resume_data_failed_alert::error) ; class_, noncopyable>( "performance_alert", no_init) .def_readonly("warning_code", &performance_alert::warning_code) ; enum_("performance_warning_t") .value("outstanding_disk_buffer_limit_reached", performance_alert::outstanding_disk_buffer_limit_reached) .value("outstanding_request_limit_reached", performance_alert::outstanding_request_limit_reached) .value("upload_limit_too_low", performance_alert::upload_limit_too_low) .value("download_limit_too_low", performance_alert::download_limit_too_low) .value("send_buffer_watermark_too_low", performance_alert::send_buffer_watermark_too_low) .value("too_many_optimistic_unchoke_slots", performance_alert::too_many_optimistic_unchoke_slots) .value("bittyrant_with_no_uplimit", performance_alert::bittyrant_with_no_uplimit) .value("too_high_disk_queue_limit", performance_alert::too_high_disk_queue_limit) .value("too_few_outgoing_ports", performance_alert::too_few_outgoing_ports) .value("too_few_file_descriptors", performance_alert::too_few_file_descriptors) ; class_, noncopyable>( "stats_alert", no_init) .add_property("transferred", &stats_alert_transferred) .def_readonly("interval", &stats_alert::interval) ; enum_("stats_channel") .value("upload_payload", stats_alert::upload_payload) .value("upload_protocol", stats_alert::upload_protocol) .value("upload_ip_protocol", stats_alert::upload_ip_protocol) #ifndef TORRENT_NO_DEPRECATE .value("upload_dht_protocol", stats_alert::upload_dht_protocol) .value("upload_tracker_protocol", stats_alert::upload_tracker_protocol) #endif .value("download_payload", stats_alert::download_payload) .value("download_protocol", stats_alert::download_protocol) .value("download_ip_protocol", stats_alert::download_ip_protocol) #ifndef TORRENT_NO_DEPRECATE .value("download_dht_protocol", stats_alert::download_dht_protocol) .value("download_tracker_protocol", stats_alert::download_tracker_protocol) #endif ; class_, noncopyable>( "cache_flushed_alert", no_init) ; class_, noncopyable>( "anonymous_mode_alert", no_init) .def_readonly("kind", &anonymous_mode_alert::kind) .def_readonly("str", &anonymous_mode_alert::str) ; enum_("kind") .value("tracker_no_anonymous", anonymous_mode_alert::tracker_not_anonymous) ; class_, noncopyable>( "incoming_connection_alert", no_init) .def_readonly("socket_type", &incoming_connection_alert::socket_type) .add_property("ip", make_getter(&incoming_connection_alert::ip, by_value())) ; class_, noncopyable>( "torrent_need_cert_alert", no_init) .def_readonly("error", &torrent_need_cert_alert::error) ; class_, noncopyable>( "add_torrent_alert", no_init) .def_readonly("error", &add_torrent_alert::error) .add_property("params", &get_params) ; class_, noncopyable>( "torrent_update_alert", no_init) .def_readonly("old_ih", &torrent_update_alert::old_ih) .def_readonly("new_ih", &torrent_update_alert::new_ih) ; class_, noncopyable>( "dht_outgoing_get_peers_alert", no_init) .def_readonly("info_hash", &dht_outgoing_get_peers_alert::info_hash) .def_readonly("obfuscated_info_hash", &dht_outgoing_get_peers_alert::obfuscated_info_hash) .add_property("ip", make_getter(&dht_outgoing_get_peers_alert::ip, by_value())) ; #ifndef TORRENT_DISABLE_LOGGING class_, noncopyable>( "log_alert", no_init) .def("msg", &log_alert::msg) ; class_, noncopyable>( "torrent_log_alert", no_init) .def("msg", &torrent_log_alert::msg) ; class_, noncopyable>( "peer_log_alert", no_init) .def("msg", &peer_log_alert::msg) ; class_, noncopyable>( "picker_log_alert", no_init) .add_property("picker_flags", &picker_log_alert::picker_flags) .def("blocks", &picker_log_alert::blocks) ; #endif // TORRENT_DISABLE_LOGGING class_, noncopyable>( "lsd_error_alert", no_init) .def_readonly("error", &lsd_error_alert::error) ; class_, noncopyable>( "dht_stats_alert", no_init) .add_property("active_requests", &dht_stats_active_requests) .add_property("routing_table", &dht_stats_routing_table) ; class_, noncopyable>( "dht_immutable_item_alert", no_init) .def_readonly("target", &dht_immutable_item_alert::target) .add_property("item", &dht_immutable_item) ; class_, noncopyable>( "dht_mutable_item_alert", no_init) .def_readonly("key", &dht_mutable_item_alert::key) .def_readonly("signature", &dht_mutable_item_alert::signature) .def_readonly("seq", &dht_mutable_item_alert::seq) .def_readonly("salt", &dht_mutable_item_alert::salt) .add_property("item", &dht_mutable_item) .def_readonly("authoritative", &dht_mutable_item_alert::authoritative) ; class_, noncopyable>( "dht_put_alert", no_init) .def_readonly("target", &dht_put_alert::target) .def_readonly("public_key", &dht_put_alert::public_key) .def_readonly("signature", &dht_put_alert::signature) .def_readonly("salt", &dht_put_alert::salt) .def_readonly("seq", &dht_put_alert::seq) .def_readonly("num_success", &dht_put_alert::num_success) ; class_, noncopyable>( "session_stats_alert", no_init) .add_property("values", &session_stats_values) ; class_, noncopyable>( "dht_get_peers_reply_alert", no_init) .def_readonly("info_hash", &dht_get_peers_reply_alert::info_hash) .def("num_peers", &dht_get_peers_reply_alert::num_peers) .def("peers", &dht_get_peers_reply_alert_peers) ; } libtorrent-rasterbar-1.1.13/bindings/python/src/big_number.cpp000066400000000000000000000022131351156116000244630ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include #include "boost_python.hpp" #include "bytes.hpp" long get_hash(boost::python::object o) { using namespace boost::python; return PyObject_Hash(str(o).ptr()); } using namespace libtorrent; bytes sha1_hash_bytes(const sha1_hash& bn) { return bytes(bn.to_string()); } void bind_sha1_hash() { using namespace boost::python; using namespace libtorrent; class_("sha1_hash") .def(self == self) .def(self != self) .def(self < self) .def(self_ns::str(self)) .def(init()) .def("clear", &sha1_hash::clear) .def("is_all_zeros", &sha1_hash::is_all_zeros) .def("to_string", &sha1_hash::to_string) .def("__hash__", get_hash) .def("to_bytes", sha1_hash_bytes) ; scope().attr("big_number") = scope().attr("sha1_hash"); scope().attr("peer_id") = scope().attr("sha1_hash"); } libtorrent-rasterbar-1.1.13/bindings/python/src/boost_python.hpp000066400000000000000000000007041351156116000251110ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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 BOOST_PYTHON_HPP #define BOOST_PYTHON_HPP #include #include #include #include #endif libtorrent-rasterbar-1.1.13/bindings/python/src/bytes.hpp000066400000000000000000000011301351156116000235020ustar00rootroot00000000000000// Copyright Arvid Norberg 2006-2013. 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 BYTES_HPP #define BYTES_HPP #include struct bytes { bytes(char const* s, int len): arr(s, len) {} bytes(std::string const& s): arr(s) {} #if __cplusplus >= 201103L bytes(std::string&& s): arr(std::move(s)) {} bytes(bytes const&) = default; bytes(bytes&&) = default; bytes& operator=(bytes&&) = default; #endif bytes() {} std::string arr; }; #endif libtorrent-rasterbar-1.1.13/bindings/python/src/converters.cpp000066400000000000000000000055071351156116000245550ustar00rootroot00000000000000// Copyright Andrew Resch 2009. 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) #include "boost_python.hpp" #include "libtorrent/address.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/session_stats.hpp" // for stats_metric #include "libtorrent/file_pool.hpp" // for file_pool_status using namespace boost::python; namespace bp = boost::python; template struct pair_to_tuple { static PyObject* convert(const std::pair& p) { return incref(bp::make_tuple(p.first, p.second).ptr()); } }; template struct endpoint_to_tuple { static PyObject* convert(Endpoint const& ep) { return incref(bp::object(bp::make_tuple(ep.address().to_string(), ep.port())).ptr()); } }; struct address_to_tuple { static PyObject* convert(libtorrent::address const& addr) { libtorrent::error_code ec; return incref(bp::object(addr.to_string(ec)).ptr()); } }; template struct tuple_to_pair { tuple_to_pair() { converter::registry::push_back( &convertible, &construct, type_id >() ); } static void* convertible(PyObject* x) { return PyTuple_Check(x) ? x: 0; } static void construct(PyObject* x, converter::rvalue_from_python_stage1_data* data) { void* storage = ((converter::rvalue_from_python_storage< std::pair >*)data)->storage.bytes; object o(borrowed(x)); std::pair p; p.first = extract(o[0]); p.second = extract(o[1]); new (storage) std::pair(p); data->convertible = storage; } }; template struct vector_to_list { static PyObject* convert(const std::vector& v) { list l; for (int i = 0; i < int(v.size()); ++i) { l.append(v[i]); } return incref(l.ptr()); } }; void bind_converters() { namespace lt = libtorrent; to_python_converter, pair_to_tuple >(); to_python_converter >(); to_python_converter >(); to_python_converter(); tuple_to_pair(); to_python_converter, vector_to_list >(); to_python_converter, vector_to_list >(); to_python_converter, vector_to_list >(); to_python_converter, vector_to_list >(); } libtorrent-rasterbar-1.1.13/bindings/python/src/create_torrent.cpp000066400000000000000000000222061351156116000253760ustar00rootroot00000000000000// Copyright Daniel Wallin & Arvid Norberg 2009. 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) #include "boost_python.hpp" #include #include #include "libtorrent/torrent_info.hpp" #include #include "bytes.hpp" using namespace boost::python; using namespace libtorrent; namespace { void set_hash(create_torrent& c, int p, bytes const& b) { c.set_hash(p, sha1_hash(b.arr)); } void set_file_hash(create_torrent& c, int f, bytes const& b) { c.set_file_hash(f, sha1_hash(b.arr)); } void call_python_object(boost::python::object const& obj, int i) { obj(i); } #ifndef BOOST_NO_EXCEPTIONS void set_piece_hashes_callback(create_torrent& c, std::string const& p , boost::python::object cb) { set_piece_hashes(c, p, boost::bind(call_python_object, cb, _1)); } #else void set_piece_hashes_callback(create_torrent& c, std::string const& p , boost::python::object cb) { error_code ec; set_piece_hashes(c, p, boost::bind(call_python_object, cb, _1), ec); } void set_piece_hashes0(create_torrent& c, std::string const & s) { error_code ec; set_piece_hashes(c, s, ec); } #endif void add_node(create_torrent& ct, std::string const& addr, int port) { ct.add_node(std::make_pair(addr, port)); } #ifndef TORRENT_NO_DEPRECATE void add_file(file_storage& ct, file_entry const& fe) { ct.add_file(fe); } struct FileIter { typedef libtorrent::file_entry value_type; typedef libtorrent::file_entry reference; typedef libtorrent::file_entry* pointer; typedef int difference_type; typedef std::forward_iterator_tag iterator_category; FileIter(file_storage const& fs, int i) : m_fs(&fs), m_i(i) {} FileIter(FileIter const& fi) : m_fs(fi.m_fs), m_i(fi.m_i) {} FileIter() : m_fs(NULL), m_i(0) {} libtorrent::file_entry operator*() const { return m_fs->at(m_i); } FileIter operator++() { m_i++; return *this; } FileIter operator++(int) { return FileIter(*m_fs, m_i++); } bool operator==(FileIter const& rhs) const { return m_fs == rhs.m_fs && m_i == rhs.m_i; } int operator-(FileIter const& rhs) const { assert(rhs.m_fs == m_fs); return m_i - rhs.m_i; } FileIter& operator=(FileIter const& rhs) { m_fs = rhs.m_fs; m_i = rhs.m_i; return *this; } file_storage const* m_fs; int m_i; }; FileIter begin_files(file_storage const& self) { return FileIter(self, 0); } FileIter end_files(file_storage const& self) { return FileIter(self, self.num_files()); } #endif bool call_python_object2(boost::python::object& obj, std::string const& i) { return obj(i); } void add_files_callback(file_storage& fs, std::string const& file , boost::python::object cb, boost::uint32_t flags) { add_files(fs, file, boost::bind(&call_python_object2, cb, _1), flags); } } void bind_create_torrent() { void (file_storage::*add_file0)(std::string const&, boost::int64_t , int, std::time_t, std::string const&) = &file_storage::add_file; #ifndef TORRENT_NO_DEPRECATE #if TORRENT_USE_WSTRING void (file_storage::*add_file1)(std::wstring const&, boost::int64_t , int, std::time_t, std::string const&) = &file_storage::add_file; #endif // TORRENT_USE_WSTRING #endif // TORRENT_NO_DEPRECATE void (file_storage::*set_name0)(std::string const&) = &file_storage::set_name; void (file_storage::*rename_file0)(int, std::string const&) = &file_storage::rename_file; #if TORRENT_USE_WSTRING && !defined TORRENT_NO_DEPRECATE void (file_storage::*set_name1)(std::wstring const&) = &file_storage::set_name; void (file_storage::*rename_file1)(int, std::wstring const&) = &file_storage::rename_file; #endif #ifndef BOOST_NO_EXCEPTIONS void (*set_piece_hashes0)(create_torrent&, std::string const&) = &set_piece_hashes; #endif void (*add_files0)(file_storage&, std::string const&, boost::uint32_t) = add_files; std::string const& (file_storage::*file_storage_symlink)(int) const = &file_storage::symlink; sha1_hash (file_storage::*file_storage_hash)(int) const = &file_storage::hash; std::string (file_storage::*file_storage_file_path)(int, std::string const&) const = &file_storage::file_path; std::string (file_storage::*file_storage_file_name)(int) const = &file_storage::file_name; boost::int64_t (file_storage::*file_storage_file_size)(int) const = &file_storage::file_size; boost::int64_t (file_storage::*file_storage_file_offset)(int) const = &file_storage::file_offset; int (file_storage::*file_storage_file_flags)(int) const = &file_storage::file_flags; #ifndef TORRENT_NO_DEPRECATE file_entry (file_storage::*at)(int) const = &file_storage::at; #endif // TODO: 3 move this to its own file class_("file_storage") .def("is_valid", &file_storage::is_valid) .def("add_file", add_file0, (arg("path"), arg("size"), arg("flags") = 0, arg("mtime") = 0, arg("linkpath") = "")) #if TORRENT_USE_WSTRING && !defined TORRENT_NO_DEPRECATE .def("add_file", add_file1, (arg("path"), arg("size"), arg("flags") = 0, arg("mtime") = 0, arg("linkpath") = "")) #endif .def("num_files", &file_storage::num_files) #ifndef TORRENT_NO_DEPRECATE .def("at", at) .def("add_file", add_file, arg("entry")) .def("__iter__", boost::python::range(&begin_files, &end_files)) .def("__len__", &file_storage::num_files) #endif .def("hash", file_storage_hash) .def("symlink", file_storage_symlink, return_value_policy()) .def("file_path", file_storage_file_path, (arg("idx"), arg("save_path") = "")) .def("file_name", file_storage_file_name) .def("file_size", file_storage_file_size) .def("file_offset", file_storage_file_offset) .def("file_flags", file_storage_file_flags) .def("total_size", &file_storage::total_size) .def("set_num_pieces", &file_storage::set_num_pieces) .def("num_pieces", &file_storage::num_pieces) .def("set_piece_length", &file_storage::set_piece_length) .def("piece_length", &file_storage::piece_length) .def("piece_size", &file_storage::piece_size) .def("set_name", set_name0) .def("rename_file", rename_file0) #if TORRENT_USE_WSTRING && !defined TORRENT_NO_DEPRECATE .def("set_name", set_name1) .def("rename_file", rename_file1) #endif .def("name", &file_storage::name, return_value_policy()) ; enum_("file_flags_t") .value("flag_pad_file", file_storage::flag_pad_file) .value("flag_hidden", file_storage::flag_hidden) .value("flag_executable", file_storage::flag_executable) .value("flag_symlink", file_storage::flag_symlink) ; class_("create_torrent", no_init) .def(init()) .def(init((arg("ti"), arg("use_preformatted") = false))) .def(init((arg("storage"), arg("piece_size") = 0 , arg("pad_file_limit") = -1, arg("flags") = int(libtorrent::create_torrent::optimize_alignment)))) .def("generate", &create_torrent::generate) .def("files", &create_torrent::files, return_internal_reference<>()) .def("set_comment", &create_torrent::set_comment) .def("set_creator", &create_torrent::set_creator) .def("set_hash", &set_hash) .def("set_file_hash", &set_file_hash) .def("add_url_seed", &create_torrent::add_url_seed) .def("add_http_seed", &create_torrent::add_http_seed) .def("add_node", &add_node) .def("add_tracker", &create_torrent::add_tracker, (arg("announce_url"), arg("tier") = 0)) .def("set_priv", &create_torrent::set_priv) .def("num_pieces", &create_torrent::num_pieces) .def("piece_length", &create_torrent::piece_length) .def("piece_size", &create_torrent::piece_size) .def("priv", &create_torrent::priv) .def("set_root_cert", &create_torrent::set_root_cert, (arg("pem"))) ; enum_("create_torrent_flags_t") #ifndef TORRENT_NO_DEPRECATE .value("optimize", create_torrent::optimize) #endif .value("optimize_alignment", create_torrent::optimize_alignment) .value("merkle", create_torrent::merkle) .value("modification_time", create_torrent::modification_time) .value("symlinks", create_torrent::symlinks) ; def("add_files", add_files0, (arg("fs"), arg("path"), arg("flags") = 0)); def("add_files", add_files_callback, (arg("fs"), arg("path") , arg("predicate"), arg("flags") = 0)); def("set_piece_hashes", set_piece_hashes0); def("set_piece_hashes", set_piece_hashes_callback); } libtorrent-rasterbar-1.1.13/bindings/python/src/datetime.cpp000066400000000000000000000065631351156116000241620ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include "boost_python.hpp" #include #include "optional.hpp" #include #include #include "libtorrent/time.hpp" using namespace boost::python; namespace lt = libtorrent; #if BOOST_VERSION < 103400 // From Boost 1.34 object import(str name) { // should be 'char const *' but older python versions don't use 'const' yet. char *n = extract(name); handle<> module(borrowed(PyImport_ImportModule(n))); return object(module); } #endif object datetime_timedelta; object datetime_datetime; struct chrono_time_duration_to_python { static PyObject* convert(lt::time_duration const& d) { object result = datetime_timedelta( 0 // days , 0 // seconds , lt::total_microseconds(d) ); return incref(result.ptr()); } }; struct time_duration_to_python { static PyObject* convert(boost::posix_time::time_duration const& d) { object result = datetime_timedelta( 0 // days , 0 // seconds , d.total_microseconds() ); return incref(result.ptr()); } }; #if defined BOOST_ASIO_HAS_STD_CHRONO using std::chrono::system_clock; #else using boost::chrono::system_clock; #endif struct time_point_to_python { static PyObject* convert(lt::time_point tpt) { object result; if (tpt > lt::min_time()) { time_t const tm = system_clock::to_time_t(system_clock::now() + lt::duration_cast(tpt - lt::clock_type::now())); std::tm* date = std::localtime(&tm); result = datetime_datetime( (int)1900 + date->tm_year // tm use 0-11 and we need 1-12 , (int)date->tm_mon + 1 , (int)date->tm_mday , date->tm_hour , date->tm_min , date->tm_sec ); } else { result = object(); } return incref(result.ptr()); } }; struct ptime_to_python { static PyObject* convert(boost::posix_time::ptime const& pt) { boost::gregorian::date date = pt.date(); boost::posix_time::time_duration td = pt.time_of_day(); object result = datetime_datetime( (int)date.year() , (int)date.month() , (int)date.day() , td.hours() , td.minutes() , td.seconds() ); return incref(result.ptr()); } }; void bind_datetime() { object datetime = import("datetime").attr("__dict__"); datetime_timedelta = datetime["timedelta"]; datetime_datetime = datetime["datetime"]; to_python_converter< boost::posix_time::time_duration , time_duration_to_python >(); to_python_converter< lt::time_point , time_point_to_python >(); to_python_converter< boost::posix_time::ptime , ptime_to_python >(); to_python_converter< lt::time_duration , chrono_time_duration_to_python >(); optional_to_python(); optional_to_python(); } libtorrent-rasterbar-1.1.13/bindings/python/src/entry.cpp000066400000000000000000000114301351156116000235140ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include "boost_python.hpp" #include #include "bytes.hpp" using namespace boost::python; using namespace libtorrent; struct entry_to_python { static object convert(entry::list_type const& l) { list result; for (entry::list_type::const_iterator i(l.begin()), e(l.end()); i != e; ++i) { result.append(*i); } return result; } static object convert(entry::dictionary_type const& d) { dict result; for (entry::dictionary_type::const_iterator i(d.begin()), e(d.end()); i != e; ++i) result[bytes(i->first)] = i->second; return result; } static object convert0(entry const& e) { switch (e.type()) { case entry::int_t: return object(e.integer()); case entry::string_t: return object(bytes(e.string())); case entry::list_t: return convert(e.list()); case entry::dictionary_t: return convert(e.dict()); case entry::preformatted_t: { std::vector const& pre = e.preformatted(); list l; for (std::vector::const_iterator i = pre.begin() , end(pre.end()); i != end; ++i) l.append(int(*i)); return tuple(l); } default: return object(); } } static PyObject* convert(boost::shared_ptr const& e) { if (!e) return incref(Py_None); return convert(*e); } static PyObject* convert(entry const& e) { return incref(convert0(e).ptr()); } }; struct entry_from_python { entry_from_python() { converter::registry::push_back( &convertible, &construct, type_id() ); } static void* convertible(PyObject* e) { return e; } static entry construct0(object e) { if (extract(e).check()) { dict d = extract(e); list items(d.items()); std::size_t length = extract(items.attr("__len__")()); entry result(entry::dictionary_t); for (std::size_t i = 0; i < length; ++i) { if (extract(items[i][0]).check()) { result.dict().insert( std::make_pair( extract(items[i][0])().arr, construct0(items[i][1]) ) ); } else { result.dict().insert( std::make_pair( extract(items[i][0])(), construct0(items[i][1]) ) ); } } return result; } else if (extract(e).check()) { list l = extract(e); std::size_t length = extract(l.attr("__len__")()); entry result(entry::list_t); for (std::size_t i = 0; i < length; ++i) { result.list().push_back(construct0(l[i])); } return result; } else if (extract(e).check()) { return entry(extract(e)().arr); } else if (extract(e).check()) { return entry(extract(e)()); } else if (extract(e).check()) { return entry(extract(e)()); } else if (extract(e).check()) { tuple t = extract(e); std::size_t const length = extract(t.attr("__len__")()); std::vector preformatted(length); for (std::size_t i = 0; i < length; ++i) { preformatted[i] = char(extract(t[i])); } return entry(preformatted); } return entry(); } static void construct(PyObject* e, converter::rvalue_from_python_stage1_data* data) { void* storage = ((converter::rvalue_from_python_storage*)data)->storage.bytes; new (storage) entry(construct0(object(borrowed(e)))); data->convertible = storage; } }; void bind_entry() { to_python_converter, entry_to_python>(); to_python_converter(); entry_from_python(); } libtorrent-rasterbar-1.1.13/bindings/python/src/error_code.cpp000066400000000000000000000152451351156116000245060ustar00rootroot00000000000000/* Copyright (c) 2011, 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 #include #include #include #include #if defined TORRENT_USE_OPENSSL #include #endif #if TORRENT_USE_I2P #include #endif #include "boost_python.hpp" using namespace boost::python; using namespace libtorrent; using boost::system::error_category; namespace { struct ec_pickle_suite : boost::python::pickle_suite { static boost::python::tuple getinitargs(error_code const& ec) { return boost::python::tuple(); } static boost::python::tuple getstate(error_code const& ec) { return boost::python::make_tuple(ec.value(), ec.category().name()); } static void setstate(error_code& ec, boost::python::tuple state) { using namespace boost::python; if (len(state) != 2) { PyErr_SetObject(PyExc_ValueError, ("expected 2-item tuple in call to __setstate__; got %s" % state).ptr()); throw_error_already_set(); } int const value = extract(state[0]); std::string const category = extract(state[1]); if (category == "system") ec.assign(value, libtorrent::system_category()); else if (category == "generic") ec.assign(value, libtorrent::generic_category()); else if (category == "libtorrent") ec.assign(value, libtorrent::libtorrent_category()); else if (category == "http error") ec.assign(value, libtorrent::http_category()); else if (category == "UPnP error") ec.assign(value, libtorrent::upnp_category()); else if (category == "bdecode error") ec.assign(value, libtorrent::bdecode_category()); else if (category == "asio.netdb") ec.assign(value, boost::asio::error::get_netdb_category()); else if (category == "asio.addinfo") ec.assign(value, boost::asio::error::get_addrinfo_category()); else if (category == "asio.misc") ec.assign(value, boost::asio::error::get_misc_category()); else if (category == "asio.misc") ec.assign(value, boost::asio::error::get_misc_category()); #if defined TORRENT_USE_OPENSSL else if (category == "asio.ssl") ec.assign(value, boost::asio::error::get_ssl_category()); #endif else { PyErr_SetObject(PyExc_ValueError, ("unexpected error_category passed to __setstate__; got '%s'" % object(category)).ptr()); throw_error_already_set(); } } }; } struct category_holder { category_holder(boost::system::error_category const& cat) : m_cat(&cat) {} char const* name() const { return m_cat->name(); } std::string message(int const v) const { return m_cat->message(v); } friend bool operator==(category_holder const lhs, category_holder const rhs) { return *lhs.m_cat == *rhs.m_cat; } friend bool operator!=(category_holder const lhs, category_holder const rhs) { return *lhs.m_cat != *rhs.m_cat; } friend bool operator<(category_holder const lhs, category_holder const rhs) { return *lhs.m_cat < *rhs.m_cat; } boost::system::error_category const& ref() const { return *m_cat; } operator boost::system::error_category const&() const { return *m_cat; } private: boost::system::error_category const* m_cat; }; void error_code_assign(boost::system::error_code& self, int const v, category_holder const cat) { self.assign(v, cat.ref()); } category_holder error_code_category(boost::system::error_code const& self) { return category_holder(self.category()); } #define WRAP_CAT(name) \ category_holder wrap_ ##name## _category() { return category_holder(name## _category()); } WRAP_CAT(libtorrent) WRAP_CAT(upnp) WRAP_CAT(http) WRAP_CAT(socks) WRAP_CAT(bdecode) #if TORRENT_USE_I2P WRAP_CAT(i2p) #endif WRAP_CAT(generic) WRAP_CAT(system) #undef WRAP_CAT void bind_error_code() { class_("error_category", no_init) .def("name", &category_holder::name) .def("message", &category_holder::message) .def(self == self) .def(self < self) .def(self != self) ; class_("error_code") .def(init<>()) .def(init()) .def("message", static_cast(&error_code::message)) .def("value", &error_code::value) .def("clear", &error_code::clear) .def("category", &error_code_category) .def("assign", &error_code_assign) .def_pickle(ec_pickle_suite()) ; def("libtorrent_category", &wrap_libtorrent_category); def("upnp_category", &wrap_upnp_category); def("http_category", &wrap_http_category); def("socks_category", &wrap_socks_category); def("bdecode_category", &wrap_bdecode_category); #if TORRENT_USE_I2P def("i2p_category", &wrap_i2p_category); #endif #ifndef TORRENT_NO_DEPRECATE def("get_libtorrent_category", &wrap_libtorrent_category); def("get_upnp_category", &wrap_upnp_category); def("get_http_category", &wrap_http_category); def("get_socks_category", &wrap_socks_category); def("get_bdecode_category", &wrap_bdecode_category); #if TORRENT_USE_I2P def("get_i2p_category", &wrap_i2p_category); #endif #endif // TORRENT_NO_DEPRECATE def("generic_category", &wrap_generic_category); def("system_category", &wrap_system_category); } libtorrent-rasterbar-1.1.13/bindings/python/src/fingerprint.cpp000066400000000000000000000020401351156116000246770ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include #include "boost_python.hpp" void bind_fingerprint() { using namespace boost::python; using namespace libtorrent; def("generate_fingerprint", &generate_fingerprint); #ifndef TORRENT_NO_DEPRECATE class_("fingerprint", no_init) .def( init( (arg("id"), "major", "minor", "revision", "tag") ) ) .def("__str__", &fingerprint::to_string) .def_readonly("name", &fingerprint::name) .def_readonly("major_version", &fingerprint::major_version) .def_readonly("minor_version", &fingerprint::minor_version) .def_readonly("revision_version", &fingerprint::revision_version) .def_readonly("tag_version", &fingerprint::tag_version) ; #endif } libtorrent-rasterbar-1.1.13/bindings/python/src/gil.hpp000066400000000000000000000063651351156116000231460ustar00rootroot00000000000000// Copyright Daniel Wallin 2007. 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 GIL_070107_HPP # define GIL_070107_HPP #include # include # include # include # include #include //namespace libtorrent { namespace python { // RAII helper to release GIL. struct allow_threading_guard { allow_threading_guard() : save(PyEval_SaveThread()) {} ~allow_threading_guard() { PyEval_RestoreThread(save); } PyThreadState* save; }; struct lock_gil { lock_gil() : state(PyGILState_Ensure()) {} ~lock_gil() { PyGILState_Release(state); } PyGILState_STATE state; }; template struct allow_threading { allow_threading(F fn) : fn(fn) {} template R operator()(A0& a0) { allow_threading_guard guard; return (a0.*fn)(); } template R operator()(A0& a0, A1& a1) { allow_threading_guard guard; return (a0.*fn)(a1); } template R operator()(A0& a0, A1& a1, A2& a2) { allow_threading_guard guard; return (a0.*fn)(a1,a2); } template R operator()(A0& a0, A1& a1, A2& a2, A3& a3) { allow_threading_guard guard; return (a0.*fn)(a1,a2,a3); } template R operator()(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) { allow_threading_guard guard; return (a0.*fn)(a1,a2,a3,a4); } template R operator()(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4, A5& a5) { allow_threading_guard guard; return (a0.*fn)(a1,a2,a3,a4,a5); } F fn; }; template struct visitor : boost::python::def_visitor > { visitor(F fn) : fn(fn) {} template void visit_aux( Class& cl, char const* name , Options const& options, Signature const& signature) const { typedef typename boost::mpl::at_c::type return_type; cl.def( name , boost::python::make_function( allow_threading(fn) , options.policies() , options.keywords() , signature ) ); } template void visit(Class& cl, char const* name, Options const& options) const { this->visit_aux( cl, name, options , boost::python::detail::get_signature(fn, (typename Class::wrapped_type*)0) ); } F fn; }; // Member function adaptor that releases and aqcuires the GIL // around the function call. template visitor allow_threads(F fn) { return visitor(fn); } //}} // namespace libtorrent::python #endif // GIL_070107_HPP libtorrent-rasterbar-1.1.13/bindings/python/src/ip_filter.cpp000066400000000000000000000016011351156116000243270ustar00rootroot00000000000000// Copyright Andrew Resch 2008. 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) #include #include "boost_python.hpp" #include "gil.hpp" using namespace boost::python; using namespace libtorrent; namespace { void add_rule(ip_filter& filter, std::string start, std::string end, int flags) { return filter.add_rule(address::from_string(start), address::from_string(end), flags); } int access0(ip_filter& filter, std::string addr) { return filter.access(address::from_string(addr)); } } void bind_ip_filter() { class_("ip_filter") .def("add_rule", add_rule) .def("access", access0) .def("export_filter", allow_threads(&ip_filter::export_filter)) ; } libtorrent-rasterbar-1.1.13/bindings/python/src/magnet_uri.cpp000066400000000000000000000044071351156116000245130ustar00rootroot00000000000000// Copyright Andrew Resch 2008. 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 #include "boost_python.hpp" #include #include #include #include "gil.hpp" #include "bytes.hpp" using namespace boost::python; using namespace libtorrent; namespace lt = libtorrent; extern void dict_to_add_torrent_params(dict params, add_torrent_params& p); namespace { #ifndef TORRENT_NO_DEPRECATE torrent_handle _add_magnet_uri(lt::session& s, std::string uri, dict params) { add_torrent_params p; dict_to_add_torrent_params(params, p); allow_threading_guard guard; p.url = uri; #ifndef BOOST_NO_EXCEPTIONS return s.add_torrent(p); #else error_code ec; return s.add_torrent(p, ec); #endif } #endif dict parse_magnet_uri_dict(std::string const& uri) { add_torrent_params p; error_code ec; parse_magnet_uri(uri, p, ec); if (ec) throw libtorrent_exception(ec); dict ret; ret["ti"] = p.ti; list tracker_list; for (std::vector::const_iterator i = p.trackers.begin() , end(p.trackers.end()); i != end; ++i) tracker_list.append(*i); ret["trackers"] = tracker_list; list nodes_list; for (std::vector >::const_iterator i = p.dht_nodes.begin() , end(p.dht_nodes.end()); i != end; ++i) nodes_list.append(boost::python::make_tuple(i->first, i->second)); ret["dht_nodes"] = nodes_list; ret["info_hash"] = bytes(p.info_hash.to_string()); ret["name"] = p.name; ret["save_path"] = p.save_path; ret["storage_mode"] = p.storage_mode; ret["url"] = p.url; ret["uuid"] = p.uuid; ret["source_feed_url"] = p.source_feed_url; ret["flags"] = p.flags; return ret; } std::string (*make_magnet_uri0)(torrent_handle const&) = make_magnet_uri; std::string (*make_magnet_uri1)(torrent_info const&) = make_magnet_uri; } void bind_magnet_uri() { #ifndef TORRENT_NO_DEPRECATE def("add_magnet_uri", &_add_magnet_uri); #endif def("make_magnet_uri", make_magnet_uri0); def("make_magnet_uri", make_magnet_uri1); def("parse_magnet_uri", parse_magnet_uri_dict); def("parse_magnet_uri_dict", parse_magnet_uri_dict); } libtorrent-rasterbar-1.1.13/bindings/python/src/module.cpp000066400000000000000000000025221351156116000236420ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #ifdef __GNUC__ #define BOOST_PYTHON_USE_GCC_SYMBOL_VISIBILITY 1 #endif #include "libtorrent/config.hpp" #include void bind_utility(); void bind_fingerprint(); void bind_sha1_hash(); void bind_session(); void bind_entry(); void bind_torrent_info(); void bind_unicode_string_conversion(); void bind_torrent_handle(); void bind_torrent_status(); void bind_session_settings(); void bind_version(); void bind_alert(); void bind_datetime(); void bind_peer_info(); void bind_ip_filter(); void bind_magnet_uri(); void bind_converters(); void bind_create_torrent(); void bind_error_code(); BOOST_PYTHON_MODULE(libtorrent) { Py_Initialize(); PyEval_InitThreads(); bind_error_code(); bind_utility(); bind_fingerprint(); bind_sha1_hash(); bind_entry(); bind_torrent_handle(); bind_session(); bind_torrent_info(); bind_unicode_string_conversion(); bind_torrent_status(); bind_session_settings(); bind_version(); bind_alert(); bind_datetime(); bind_peer_info(); bind_ip_filter(); bind_magnet_uri(); bind_converters(); bind_create_torrent(); } libtorrent-rasterbar-1.1.13/bindings/python/src/optional.hpp000066400000000000000000000014111351156116000242030ustar00rootroot00000000000000// Copyright Daniel Wallin 2007. 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 OPTIONAL_070108_HPP # define OPTIONAL_070108_HPP # include "boost_python.hpp" # include template struct optional_to_python { optional_to_python() { boost::python::to_python_converter< boost::optional, optional_to_python >(); } static PyObject* convert(boost::optional const& x) { if (!x) return boost::python::incref(Py_None); return boost::python::incref(boost::python::object(*x).ptr()); } }; #endif // OPTIONAL_070108_HPP libtorrent-rasterbar-1.1.13/bindings/python/src/peer_info.cpp000066400000000000000000000150601351156116000243240ustar00rootroot00000000000000// Copyright Daniel Wallin 2007. 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) #include #include #include "boost_python.hpp" #include using namespace boost::python; using namespace libtorrent; boost::int64_t get_last_active(peer_info const& pi) { return total_seconds(pi.last_active); } boost::int64_t get_last_request(peer_info const& pi) { return total_seconds(pi.last_request); } boost::int64_t get_download_queue_time(peer_info const& pi) { return total_seconds(pi.download_queue_time); } #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES str get_country(peer_info const& pi) { return str(pi.country, 2); } #endif #endif // TORRENT_NO_DEPRECATE tuple get_local_endpoint(peer_info const& pi) { return boost::python::make_tuple(pi.local_endpoint.address().to_string(), pi.local_endpoint.port()); } tuple get_ip(peer_info const& pi) { return boost::python::make_tuple(pi.ip.address().to_string(), pi.ip.port()); } list get_pieces(peer_info const& pi) { list ret; for (bitfield::const_iterator i = pi.pieces.begin() , end(pi.pieces.end()); i != end; ++i) { ret.append(*i); } return ret; } void bind_peer_info() { scope pi = class_("peer_info") .def_readonly("flags", &peer_info::flags) .def_readonly("source", &peer_info::source) .def_readonly("read_state", &peer_info::read_state) .def_readonly("write_state", &peer_info::write_state) .add_property("ip", get_ip) .def_readonly("up_speed", &peer_info::up_speed) .def_readonly("down_speed", &peer_info::down_speed) .def_readonly("payload_up_speed", &peer_info::payload_up_speed) .def_readonly("payload_down_speed", &peer_info::payload_down_speed) .def_readonly("total_download", &peer_info::total_download) .def_readonly("total_upload", &peer_info::total_upload) .def_readonly("pid", &peer_info::pid) .add_property("pieces", get_pieces) #ifndef TORRENT_NO_DEPRECATE .def_readonly("upload_limit", &peer_info::upload_limit) .def_readonly("download_limit", &peer_info::download_limit) .def_readonly("load_balancing", &peer_info::load_balancing) #endif .add_property("last_request", get_last_request) .add_property("last_active", get_last_active) .add_property("download_queue_time", get_download_queue_time) .def_readonly("queue_bytes", &peer_info::queue_bytes) .def_readonly("request_timeout", &peer_info::request_timeout) .def_readonly("send_buffer_size", &peer_info::send_buffer_size) .def_readonly("used_send_buffer", &peer_info::used_send_buffer) .def_readonly("receive_buffer_size", &peer_info::receive_buffer_size) .def_readonly("used_receive_buffer", &peer_info::used_receive_buffer) .def_readonly("num_hashfails", &peer_info::num_hashfails) #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES .add_property("country", get_country) #endif #endif // TORRENT_NO_DEPRECATE .def_readonly("download_queue_length", &peer_info::download_queue_length) .def_readonly("upload_queue_length", &peer_info::upload_queue_length) .def_readonly("failcount", &peer_info::failcount) .def_readonly("downloading_piece_index", &peer_info::downloading_piece_index) .def_readonly("downloading_block_index", &peer_info::downloading_block_index) .def_readonly("downloading_progress", &peer_info::downloading_progress) .def_readonly("downloading_total", &peer_info::downloading_total) .def_readonly("client", &peer_info::client) .def_readonly("connection_type", &peer_info::connection_type) .def_readonly("remote_dl_rate", &peer_info::remote_dl_rate) .def_readonly("pending_disk_bytes", &peer_info::pending_disk_bytes) .def_readonly("send_quota", &peer_info::send_quota) .def_readonly("receive_quota", &peer_info::receive_quota) .def_readonly("rtt", &peer_info::rtt) .def_readonly("num_pieces", &peer_info::num_pieces) .def_readonly("download_rate_peak", &peer_info::download_rate_peak) .def_readonly("upload_rate_peak", &peer_info::upload_rate_peak) .def_readonly("progress", &peer_info::progress) .def_readonly("progress_ppm", &peer_info::progress_ppm) .def_readonly("estimated_reciprocation_rate", &peer_info::estimated_reciprocation_rate) .add_property("local_endpoint", get_local_endpoint) ; // flags pi.attr("interesting") = (int)peer_info::interesting; pi.attr("choked") = (int)peer_info::choked; pi.attr("remote_interested") = (int)peer_info::remote_interested; pi.attr("remote_choked") = (int)peer_info::remote_choked; pi.attr("supports_extensions") = (int)peer_info::supports_extensions; pi.attr("local_connection") = (int)peer_info::local_connection; pi.attr("handshake") = (int)peer_info::handshake; pi.attr("connecting") = (int)peer_info::connecting; #ifndef TORRENT_NO_DEPRECATE pi.attr("queued") = (int)peer_info::queued; #endif pi.attr("on_parole") = (int)peer_info::on_parole; pi.attr("seed") = (int)peer_info::seed; pi.attr("optimistic_unchoke") = (int)peer_info::optimistic_unchoke; pi.attr("snubbed") = (int)peer_info::snubbed; pi.attr("upload_only") = (int)peer_info::upload_only; pi.attr("endgame_mode") = (int)peer_info::endgame_mode; pi.attr("holepunched") = (int)peer_info::holepunched; #ifndef TORRENT_DISABLE_ENCRYPTION pi.attr("rc4_encrypted") = (int)peer_info::rc4_encrypted; pi.attr("plaintext_encrypted") = (int)peer_info::plaintext_encrypted; #endif // connection_type pi.attr("standard_bittorrent") = (int)peer_info::standard_bittorrent; pi.attr("web_seed") = (int)peer_info::web_seed; // source pi.attr("tracker") = (int)peer_info::tracker; pi.attr("dht") = (int)peer_info::dht; pi.attr("pex") = (int)peer_info::pex; pi.attr("lsd") = (int)peer_info::lsd; pi.attr("resume_data") = (int)peer_info::resume_data; // read/write state pi.attr("bw_idle") = (int)peer_info::bw_idle; #ifndef TORRENT_NO_DEPRECATE pi.attr("bw_torrent") = (int)peer_info::bw_torrent; pi.attr("bw_global") = (int)peer_info::bw_global; #endif pi.attr("bw_limit") = (int)peer_info::bw_limit; pi.attr("bw_network") = (int)peer_info::bw_network; pi.attr("bw_disk") = (int)peer_info::bw_disk; } libtorrent-rasterbar-1.1.13/bindings/python/src/session.cpp000066400000000000000000001226731351156116000240520ustar00rootroot00000000000000// Copyright Daniel Wallin, Arvid Norberg 2006. 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) #include #include #include #include #include #include #include #include #include #include #include #include // for sign_mutable_item #include #include #include #include #include #ifndef TORRENT_NO_DEPRECATE #include #include #endif #include #include #include #include "gil.hpp" #include "bytes.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include "boost_python.hpp" #include "libtorrent/aux_/disable_warnings_pop.hpp" using namespace boost::python; using namespace libtorrent; namespace lt = libtorrent; namespace { #ifndef TORRENT_NO_DEPRECATE void listen_on(lt::session& s, int min_, int max_, char const* interface, int flags) { allow_threading_guard guard; error_code ec; s.listen_on(std::make_pair(min_, max_), ec, interface, flags); #ifndef BOOST_NO_EXCEPTIONS if (ec) throw libtorrent_exception(ec); #endif } void outgoing_ports(lt::session& s, int _min, int _max) { allow_threading_guard guard; settings_pack p; p.set_int(settings_pack::outgoing_port, _min); p.set_int(settings_pack::num_outgoing_ports, _max - _min); s.apply_settings(p); return; } #endif // TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_DHT void add_dht_node(lt::session& s, tuple n) { std::string ip = extract(n[0]); int port = extract(n[1]); allow_threading_guard guard; s.add_dht_node(std::make_pair(ip, port)); } #ifndef TORRENT_NO_DEPRECATE void add_dht_router(lt::session& s, std::string router_, int port_) { allow_threading_guard guard; return s.add_dht_router(std::make_pair(router_, port_)); } #endif #endif // TORRENT_DISABLE_DHT void add_extension(lt::session& s, object const& e) { #ifndef TORRENT_DISABLE_EXTENSIONS if (!extract(e).check()) return; std::string name = extract(e); if (name == "ut_metadata") s.add_extension(create_ut_metadata_plugin); else if (name == "ut_pex") s.add_extension(create_ut_pex_plugin); else if (name == "smart_ban") s.add_extension(create_smart_ban_plugin); #ifndef TORRENT_NO_DEPRECATE else if (name == "lt_trackers") s.add_extension(create_lt_trackers_plugin); else if (name == "metadata_transfer") s.add_extension(create_metadata_plugin); #endif // TORRENT_NO_DEPRECATE #endif // TORRENT_DISABLE_EXTENSIONS } void make_settings_pack(lt::settings_pack& p, dict const& sett_dict) { stl_input_iterator i(sett_dict.keys()), end; for (; i != end; ++i) { std::string const key = *i; int sett = setting_by_name(key); if (sett < 0) { PyErr_SetString(PyExc_KeyError, ("unknown name in settings_pack: " + key).c_str()); throw_error_already_set(); } TORRENT_TRY { object const value = sett_dict[key]; switch (sett & settings_pack::type_mask) { case settings_pack::string_type_base: p.set_str(sett, extract(value)); break; case settings_pack::int_type_base: p.set_int(sett, extract(value)); break; case settings_pack::bool_type_base: p.set_bool(sett, extract(value)); break; } } TORRENT_CATCH(...) {} } } dict make_dict(lt::settings_pack const& sett) { dict ret; for (int i = settings_pack::string_type_base; i < settings_pack::max_string_setting_internal; ++i) { ret[name_for_setting(i)] = sett.get_str(i); } for (int i = settings_pack::int_type_base; i < settings_pack::max_int_setting_internal; ++i) { ret[name_for_setting(i)] = sett.get_int(i); } for (int i = settings_pack::bool_type_base; i < settings_pack::max_bool_setting_internal; ++i) { ret[name_for_setting(i)] = sett.get_bool(i); } return ret; } boost::shared_ptr make_session(boost::python::dict sett, int flags) { settings_pack p; make_settings_pack(p, sett); return boost::make_shared(p, flags); } #ifndef TORRENT_NO_DEPRECATE void session_set_settings(lt::session& ses, object const& sett) { extract old_settings(sett); if (old_settings.check()) { allow_threading_guard guard; ses.set_settings(old_settings); } else { settings_pack p; make_settings_pack(p, extract(sett)); allow_threading_guard guard; ses.apply_settings(p); } } #endif void session_apply_settings(lt::session& ses, dict const& sett_dict) { settings_pack p; make_settings_pack(p, sett_dict); allow_threading_guard guard; ses.apply_settings(p); } dict session_get_settings(lt::session const& ses) { settings_pack sett; { allow_threading_guard guard; sett = ses.get_settings(); } return make_dict(sett); } dict min_memory_usage_wrapper() { settings_pack ret; min_memory_usage(ret); return make_dict(ret); } dict default_settings_wrapper() { return make_dict(default_settings()); } dict high_performance_seed_wrapper() { settings_pack ret; high_performance_seed(ret); return make_dict(ret); } #ifndef BOOST_NO_EXCEPTIONS #ifndef TORRENT_NO_DEPRECATE torrent_handle add_torrent_depr(lt::session& s, torrent_info const& ti , std::string const& save, entry const& resume , storage_mode_t storage_mode, bool paused) { allow_threading_guard guard; return s.add_torrent(ti, save, resume, storage_mode, paused, default_storage_constructor); } #endif #endif } void dict_to_add_torrent_params(dict params, add_torrent_params& p) { // torrent_info objects are always held by a shared_ptr in the python binding if (params.has_key("ti") && params.get("ti") != boost::python::object()) { // make a copy here. We don't want to end up holding a python-owned // object inside libtorrent. If the last reference goes out of scope // on the C++ side, it will end up freeing the python object // without holding the GIL and likely crash. // https://mail.python.org/pipermail/cplusplus-sig/2007-June/012130.html p.ti = boost::make_shared( extract(params["ti"])); } if (params.has_key("info_hash")) p.info_hash = sha1_hash(bytes(extract(params["info_hash"])).arr); if (params.has_key("name")) p.name = extract(params["name"]); p.save_path = extract(params["save_path"]); if (params.has_key("resume_data")) { std::string resume = extract(params["resume_data"]); p.resume_data.assign(resume.begin(), resume.end()); } if (params.has_key("storage_mode")) p.storage_mode = extract(params["storage_mode"]); if (params.has_key("trackers")) { list l = extract(params["trackers"]); int n = boost::python::len(l); for(int i = 0; i < n; i++) p.trackers.push_back(extract(l[i])); } if (params.has_key("dht_nodes")) { list l = extract(params["dht_nodes"]); int n = boost::python::len(l); for(int i = 0; i < n; i++) p.dht_nodes.push_back(extract >(l[i])); } #ifndef TORRENT_NO_DEPRECATE std::string url; if (params.has_key("tracker_url")) p.trackers.push_back(extract(params["tracker_url"])); if (params.has_key("seed_mode")) p.seed_mode = params["seed_mode"]; if (params.has_key("upload_mode")) p.upload_mode = params["upload_mode"]; if (params.has_key("share_mode")) p.upload_mode = params["share_mode"]; if (params.has_key("override_resume_data")) p.override_resume_data = params["override_resume_data"]; if (params.has_key("apply_ip_filter")) p.apply_ip_filter = params["apply_ip_filter"]; if (params.has_key("paused")) p.paused = params["paused"]; if (params.has_key("auto_managed")) p.auto_managed = params["auto_managed"]; if (params.has_key("duplicate_is_error")) p.duplicate_is_error = params["duplicate_is_error"]; if (params.has_key("merge_resume_trackers")) p.merge_resume_trackers = params["merge_resume_trackers"]; #endif if (params.has_key("flags")) p.flags = extract(params["flags"]); if (params.has_key("trackerid")) p.trackerid = extract(params["trackerid"]); if (params.has_key("url")) p.url = extract(params["url"]); if (params.has_key("source_feed_url")) p.source_feed_url = extract(params["source_feed_url"]); if (params.has_key("uuid")) p.uuid = extract(params["uuid"]); if (params.has_key("file_priorities")) { list l = extract(params["file_priorities"]); int n = boost::python::len(l); for(int i = 0; i < n; i++) p.file_priorities.push_back(extract(l[i])); p.file_priorities.clear(); } } namespace { torrent_handle add_torrent(lt::session& s, dict params) { add_torrent_params p; dict_to_add_torrent_params(params, p); allow_threading_guard guard; #ifndef BOOST_NO_EXCEPTIONS return s.add_torrent(p); #else error_code ec; return s.add_torrent(p, ec); #endif } void async_add_torrent(lt::session& s, dict params) { add_torrent_params p; dict_to_add_torrent_params(params, p); allow_threading_guard guard; s.async_add_torrent(p); } #ifndef TORRENT_NO_DEPRECATE void dict_to_feed_settings(dict params, feed_settings& feed) { if (params.has_key("auto_download")) feed.auto_download = extract(params["auto_download"]); if (params.has_key("default_ttl")) feed.default_ttl = extract(params["default_ttl"]); if (params.has_key("url")) feed.url = extract(params["url"]); if (params.has_key("add_args")) dict_to_add_torrent_params(dict(params["add_args"]), feed.add_args); } feed_handle add_feed(lt::session& s, dict params) { feed_settings feed; // this static here is a bit of a hack. It will // probably work for the most part dict_to_feed_settings(params, feed); allow_threading_guard guard; return s.add_feed(feed); } dict get_feed_status(feed_handle const& h) { feed_status s; { allow_threading_guard guard; s = h.get_feed_status(); } dict ret; ret["url"] = s.url; ret["title"] = s.title; ret["description"] = s.description; ret["last_update"] = s.last_update; ret["next_update"] = s.next_update; ret["updating"] = s.updating; ret["error"] = s.error ? s.error.message() : ""; ret["ttl"] = s.ttl; list items; for (std::vector::iterator i = s.items.begin() , end(s.items.end()); i != end; ++i) { dict item; item["url"] = i->url; item["uuid"] = i->uuid; item["title"] = i->title; item["description"] = i->description; item["comment"] = i->comment; item["category"] = i->category; item["size"] = i->size; item["handle"] = i->handle; item["info_hash"] = i->info_hash.to_string(); items.append(item); } ret["items"] = items; return ret; } void set_feed_settings(feed_handle& h, dict sett) { feed_settings feed; dict_to_feed_settings(sett, feed); h.set_settings(feed); } dict get_feed_settings(feed_handle& h) { feed_settings s; { allow_threading_guard guard; s = h.settings(); } dict ret; ret["url"] = s.url; ret["auto_download"] = s.auto_download; ret["default_ttl"] = s.default_ttl; return ret; } void start_natpmp(lt::session& s) { allow_threading_guard guard; s.start_natpmp(); } void start_upnp(lt::session& s) { allow_threading_guard guard; s.start_upnp(); } #endif // TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE boost::shared_ptr #else alert const* #endif wait_for_alert(lt::session& s, int ms) { allow_threading_guard guard; alert const* a = s.wait_for_alert(milliseconds(ms)); #ifndef TORRENT_NO_DEPRECATE if (a == NULL) return boost::shared_ptr(); return boost::shared_ptr(a->clone().release()); #else return a; #endif } list get_torrents(lt::session& s) { std::vector torrents; { allow_threading_guard guard; torrents = s.get_torrents(); } list ret; for (std::vector::iterator i = torrents.begin(); i != torrents.end(); ++i) ret.append(*i); return ret; } bool wrap_pred(object pred, torrent_status const& st) { return pred(st); } list get_torrent_status(lt::session& s, object pred, int const flags) { std::vector torrents; s.get_torrent_status(&torrents, boost::bind(&wrap_pred, pred, _1), flags); list ret; for (std::vector::iterator i = torrents.begin(); i != torrents.end(); ++i) ret.append(*i); return ret; } list refresh_torrent_status(lt::session& s, list in_torrents, int const flags) { std::vector torrents; int const n = boost::python::len(in_torrents); for (int i = 0; i < n; ++i) torrents.push_back(extract(in_torrents[i])); { allow_threading_guard guard; s.refresh_torrent_status(&torrents, flags); } list ret; for (std::vector::iterator i = torrents.begin(); i != torrents.end(); ++i) ret.append(*i); return ret; } cache_status get_cache_info1(lt::session& s, torrent_handle h, int flags) { cache_status ret; s.get_cache_info(&ret, h, flags); return ret; } list cached_piece_info_list(std::vector const& v) { list pieces; lt::time_point now = lt::clock_type::now(); for (std::vector::const_iterator i = v.begin() , end(v.end()); i != end; ++i) { dict d; d["piece"] = i->piece; d["last_use"] = total_milliseconds(now - i->last_use) / 1000.f; d["next_to_hash"] = i->next_to_hash; d["kind"] = static_cast(i->kind); pieces.append(d); } return pieces; } list cache_status_pieces(cache_status const& cs) { return cached_piece_info_list(cs.pieces); } #ifndef TORRENT_NO_DEPRECATE cache_status get_cache_status(lt::session& s) { cache_status ret; s.get_cache_info(&ret); return ret; } dict get_utp_stats(session_status const& st) { dict ret; ret["num_idle"] = st.utp_stats.num_idle; ret["num_syn_sent"] = st.utp_stats.num_syn_sent; ret["num_connected"] = st.utp_stats.num_connected; ret["num_fin_sent"] = st.utp_stats.num_fin_sent; ret["num_close_wait"] = st.utp_stats.num_close_wait; return ret; } list get_cache_info2(lt::session& ses, sha1_hash ih) { std::vector ret; { allow_threading_guard guard; ses.get_cache_info(ih, ret); } return cached_piece_info_list(ret); } #endif entry save_state(lt::session const& s, boost::uint32_t flags) { allow_threading_guard guard; entry e; s.save_state(e, flags); return e; } #ifndef TORRENT_NO_DEPRECATE object pop_alert(lt::session& ses) { std::auto_ptr a; { allow_threading_guard guard; a = ses.pop_alert(); } return object(boost::shared_ptr(a.release())); } list pop_alerts(lt::session& ses) { std::vector alerts; { allow_threading_guard guard; ses.pop_alerts(&alerts); } list ret; for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { ret.append(boost::shared_ptr((*i)->clone().release())); } return ret; } #else list pop_alerts(lt::session& ses) { std::vector alerts; { allow_threading_guard guard; ses.pop_alerts(&alerts); } list ret; for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { ret.append(boost::python::ptr(*i)); } return ret; } #endif void load_state(lt::session& ses, entry const& st, boost::uint32_t flags) { allow_threading_guard guard; std::vector buf; bencode(std::back_inserter(buf), st); bdecode_node e; error_code ec; bdecode(&buf[0], &buf[0] + buf.size(), e, ec); TORRENT_ASSERT(!ec); ses.load_state(e, flags); } dict get_peer_class(lt::session& ses, int const pc) { lt::peer_class_info pci; { allow_threading_guard guard; pci = ses.get_peer_class(pc); } dict ret; ret["ignore_unchoke_slots"] = pci.ignore_unchoke_slots; ret["connection_limit_factor"] = pci.connection_limit_factor; ret["label"] = pci.label; ret["upload_limit"] = pci.upload_limit; ret["download_limit"] = pci.download_limit; ret["upload_priority"] = pci.upload_priority; ret["download_priority"] = pci.download_priority; return ret; } void set_peer_class(lt::session& ses, int const pc, dict info) { lt::peer_class_info pci; stl_input_iterator i(info.keys()), end; for (; i != end; ++i) { std::string const key = *i; object const value = info[key]; if (key == "ignore_unchoke_slots") { pci.ignore_unchoke_slots = extract(value); } else if (key == "connection_limit_factor") { pci.connection_limit_factor = extract(value); } else if (key == "label") { pci.label = extract(value); } else if (key == "upload_limit") { pci.upload_limit = extract(value); } else if (key == "download_limit") { pci.download_limit = extract(value); } else if (key == "upload_priority") { pci.upload_priority = extract(value); } else if (key == "download_priority") { pci.download_priority = extract(value); } else { PyErr_SetString(PyExc_KeyError, ("unknown name in peer_class_info: " + key).c_str()); throw_error_already_set(); } } allow_threading_guard guard; ses.set_peer_class(pc, pci); } #ifndef TORRENT_DISABLE_DHT void dht_get_mutable_item(lt::session& ses, std::string key, std::string salt) { TORRENT_ASSERT(key.size() == 32); boost::array public_key; std::copy(key.begin(), key.end(), public_key.begin()); ses.dht_get_item(public_key, salt); } void put_string(entry& e, boost::array& sig, boost::uint64_t& seq, std::string const& salt, std::string public_key, std::string private_key, std::string data) { using libtorrent::dht::sign_mutable_item; e = data; std::vector buf; bencode(std::back_inserter(buf), e); ++seq; sign_mutable_item(std::pair(&buf[0], buf.size()) , std::pair(&salt[0], salt.size()) , seq, public_key.c_str(), private_key.c_str(), sig.data()); } void dht_put_mutable_item(lt::session& ses, std::string private_key, std::string public_key, std::string data, std::string salt) { TORRENT_ASSERT(private_key.size() == 64); TORRENT_ASSERT(public_key.size() == 32); boost::array key; std::copy(public_key.begin(), public_key.end(), key.begin()); ses.dht_put_item(key, boost::bind(&put_string, _1, _2, _3, _4 , public_key, private_key, data) , salt); } #endif } // anonymous namespace void bind_session() { #ifndef TORRENT_DISABLE_DHT void (lt::session::*dht_get_immutable_item)(sha1_hash const&) = <::session::dht_get_item; sha1_hash (lt::session::*dht_put_immutable_item)(entry data) = <::session::dht_put_item; #endif // TORRENT_DISABLE_DHT #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_DHT void (lt::session::*start_dht0)() = <::session::start_dht; void (lt::session::*start_dht1)(entry const&) = <::session::start_dht; #endif class_("session_status") .def_readonly("has_incoming_connections", &session_status::has_incoming_connections) .def_readonly("upload_rate", &session_status::upload_rate) .def_readonly("download_rate", &session_status::download_rate) .def_readonly("total_download", &session_status::total_download) .def_readonly("total_upload", &session_status::total_upload) .def_readonly("payload_upload_rate", &session_status::payload_upload_rate) .def_readonly("payload_download_rate", &session_status::payload_download_rate) .def_readonly("total_payload_download", &session_status::total_payload_download) .def_readonly("total_payload_upload", &session_status::total_payload_upload) .def_readonly("ip_overhead_upload_rate", &session_status::ip_overhead_upload_rate) .def_readonly("ip_overhead_download_rate", &session_status::ip_overhead_download_rate) .def_readonly("total_ip_overhead_download", &session_status::total_ip_overhead_download) .def_readonly("total_ip_overhead_upload", &session_status::total_ip_overhead_upload) .def_readonly("dht_upload_rate", &session_status::dht_upload_rate) .def_readonly("dht_download_rate", &session_status::dht_download_rate) .def_readonly("total_dht_download", &session_status::total_dht_download) .def_readonly("total_dht_upload", &session_status::total_dht_upload) .def_readonly("tracker_upload_rate", &session_status::tracker_upload_rate) .def_readonly("tracker_download_rate", &session_status::tracker_download_rate) .def_readonly("total_tracker_download", &session_status::total_tracker_download) .def_readonly("total_tracker_upload", &session_status::total_tracker_upload) .def_readonly("total_redundant_bytes", &session_status::total_redundant_bytes) .def_readonly("total_failed_bytes", &session_status::total_failed_bytes) .def_readonly("num_peers", &session_status::num_peers) .def_readonly("num_unchoked", &session_status::num_unchoked) .def_readonly("allowed_upload_slots", &session_status::allowed_upload_slots) .def_readonly("up_bandwidth_queue", &session_status::up_bandwidth_queue) .def_readonly("down_bandwidth_queue", &session_status::down_bandwidth_queue) .def_readonly("up_bandwidth_bytes_queue", &session_status::up_bandwidth_bytes_queue) .def_readonly("down_bandwidth_bytes_queue", &session_status::down_bandwidth_bytes_queue) .def_readonly("optimistic_unchoke_counter", &session_status::optimistic_unchoke_counter) .def_readonly("unchoke_counter", &session_status::unchoke_counter) #ifndef TORRENT_DISABLE_DHT .def_readonly("dht_nodes", &session_status::dht_nodes) .def_readonly("dht_node_cache", &session_status::dht_node_cache) .def_readonly("dht_torrents", &session_status::dht_torrents) .def_readonly("dht_global_nodes", &session_status::dht_global_nodes) .def_readonly("active_requests", &session_status::active_requests) .def_readonly("dht_total_allocations", &session_status::dht_total_allocations) #endif // TORRENT_DISABLE_DHT .add_property("utp_stats", &get_utp_stats) ; #ifndef TORRENT_DISABLE_DHT class_("dht_lookup") .def_readonly("type", &dht_lookup::type) .def_readonly("outstanding_requests", &dht_lookup::outstanding_requests) .def_readonly("timeouts", &dht_lookup::timeouts) .def_readonly("response", &dht_lookup::responses) .def_readonly("branch_factor", &dht_lookup::branch_factor) ; #endif // TORRENT_DISABLE_DHT #endif // TORRENT_NO_DEPRECATE enum_("storage_mode_t") .value("storage_mode_allocate", storage_mode_allocate) .value("storage_mode_sparse", storage_mode_sparse) ; enum_("options_t") .value("delete_files", lt::session::delete_files) ; enum_("session_flags_t") .value("add_default_plugins", lt::session::add_default_plugins) .value("start_default_features", lt::session::start_default_features) ; enum_("add_torrent_params_flags_t") .value("flag_seed_mode", add_torrent_params::flag_seed_mode) .value("flag_override_resume_data", add_torrent_params::flag_override_resume_data) .value("flag_upload_mode", add_torrent_params::flag_upload_mode) .value("flag_share_mode", add_torrent_params::flag_share_mode) .value("flag_apply_ip_filter", add_torrent_params::flag_apply_ip_filter) .value("flag_paused", add_torrent_params::flag_paused) .value("flag_auto_managed", add_torrent_params::flag_auto_managed) .value("flag_duplicate_is_error", add_torrent_params::flag_duplicate_is_error) .value("flag_merge_resume_trackers", add_torrent_params::flag_merge_resume_trackers) .value("flag_update_subscribe", add_torrent_params::flag_update_subscribe) .value("flag_super_seeding", add_torrent_params::flag_super_seeding) .value("flag_sequential_download", add_torrent_params::flag_sequential_download) .value("flag_use_resume_save_path", add_torrent_params::flag_use_resume_save_path) .value("flag_merge_resume_http_seeds", add_torrent_params::flag_merge_resume_http_seeds) .value("flag_stop_when_ready", add_torrent_params::flag_stop_when_ready) .value("default_flags", add_torrent_params::default_flags) ; class_("cache_status") .add_property("pieces", cache_status_pieces) #ifndef TORRENT_NO_DEPRECATE .def_readonly("blocks_written", &cache_status::blocks_written) .def_readonly("writes", &cache_status::writes) .def_readonly("blocks_read", &cache_status::blocks_read) .def_readonly("blocks_read_hit", &cache_status::blocks_read_hit) .def_readonly("reads", &cache_status::reads) .def_readonly("queued_bytes", &cache_status::queued_bytes) .def_readonly("cache_size", &cache_status::cache_size) .def_readonly("write_cache_size", &cache_status::write_cache_size) .def_readonly("read_cache_size", &cache_status::read_cache_size) .def_readonly("pinned_blocks", &cache_status::pinned_blocks) .def_readonly("total_used_buffers", &cache_status::total_used_buffers) .def_readonly("average_read_time", &cache_status::average_read_time) .def_readonly("average_write_time", &cache_status::average_write_time) .def_readonly("average_hash_time", &cache_status::average_hash_time) .def_readonly("average_job_time", &cache_status::average_job_time) .def_readonly("cumulative_job_time", &cache_status::cumulative_job_time) .def_readonly("cumulative_read_time", &cache_status::cumulative_read_time) .def_readonly("cumulative_write_time", &cache_status::cumulative_write_time) .def_readonly("cumulative_hash_time", &cache_status::cumulative_hash_time) .def_readonly("total_read_back", &cache_status::total_read_back) .def_readonly("read_queue_size", &cache_status::read_queue_size) .def_readonly("blocked_jobs", &cache_status::blocked_jobs) .def_readonly("queued_jobs", &cache_status::queued_jobs) .def_readonly("peak_queued", &cache_status::peak_queued) .def_readonly("pending_jobs", &cache_status::pending_jobs) .def_readonly("num_jobs", &cache_status::num_jobs) .def_readonly("num_read_jobs", &cache_status::num_read_jobs) .def_readonly("num_write_jobs", &cache_status::num_write_jobs) .def_readonly("arc_mru_size", &cache_status::arc_mru_size) .def_readonly("arc_mru_ghost_size", &cache_status::arc_mru_ghost_size) .def_readonly("arc_mfu_size", &cache_status::arc_mfu_size) .def_readonly("arc_mfu_ghost_size", &cache_status::arc_mfu_ghost_size) #endif ; class_("peer_class_type_filter") .def(init<>()) .def("add", <::peer_class_type_filter::add) .def("remove", <::peer_class_type_filter::remove) .def("disallow", <::peer_class_type_filter::disallow) .def("allow", <::peer_class_type_filter::allow) .def("apply", <::peer_class_type_filter::apply) ; enum_("socket_type_t") .value("tcp_socket", peer_class_type_filter::tcp_socket) .value("utp_socket", peer_class_type_filter::utp_socket) .value("ssl_tcp_socket", peer_class_type_filter::ssl_tcp_socket) .value("ssl_utp_socket", peer_class_type_filter::ssl_utp_socket) .value("i2p_socket", peer_class_type_filter::i2p_socket) ; { scope ses = class_("session", no_init) .def("__init__", boost::python::make_constructor(&make_session , default_call_policies() , (arg("settings") , arg("flags")=lt::session::start_default_features | lt::session::add_default_plugins)) ) #ifndef TORRENT_NO_DEPRECATE .def( init(( arg("fingerprint")=fingerprint("LT",0,1,0,0) , arg("flags")=lt::session::start_default_features | lt::session::add_default_plugins , arg("alert_mask")=int(alert::error_notification))) ) .def("outgoing_ports", &outgoing_ports) #endif .def("post_torrent_updates", allow_threads(<::session::post_torrent_updates), arg("flags") = 0xffffffff) .def("post_dht_stats", allow_threads(<::session::post_dht_stats)) .def("post_session_stats", allow_threads(<::session::post_session_stats)) .def("is_listening", allow_threads(<::session::is_listening)) .def("listen_port", allow_threads(<::session::listen_port)) #ifndef TORRENT_DISABLE_DHT .def("add_dht_node", &add_dht_node) #ifndef TORRENT_NO_DEPRECATE .def( "add_dht_router", &add_dht_router , (arg("router"), "port") ) #endif // TORRENT_NO_DEPRECATE .def("is_dht_running", allow_threads(<::session::is_dht_running)) .def("set_dht_settings", allow_threads(<::session::set_dht_settings)) .def("get_dht_settings", allow_threads(<::session::get_dht_settings)) .def("dht_get_immutable_item", allow_threads(dht_get_immutable_item)) .def("dht_get_mutable_item", &dht_get_mutable_item) .def("dht_put_immutable_item", allow_threads(dht_put_immutable_item)) .def("dht_put_mutable_item", &dht_put_mutable_item) .def("dht_get_peers", allow_threads(<::session::dht_get_peers)) .def("dht_announce", allow_threads(<::session::dht_announce)) #endif // TORRENT_DISABLE_DHT .def("add_torrent", &add_torrent) .def("async_add_torrent", &async_add_torrent) #ifndef BOOST_NO_EXCEPTIONS #ifndef TORRENT_NO_DEPRECATE .def( "add_torrent", &add_torrent_depr , ( arg("resume_data") = entry(), arg("storage_mode") = storage_mode_sparse, arg("paused") = false ) ) #endif // TORRENT_NO_DEPRECATE #endif // BOOST_NO_EXCEPTIONS .def("remove_torrent", allow_threads(<::session::remove_torrent), arg("option") = 0) #ifndef TORRENT_NO_DEPRECATE .def("add_feed", &add_feed) .def("status", allow_threads(<::session::status)) .def("settings", <::session::settings) .def("set_settings", &session_set_settings) #endif .def("get_settings", &session_get_settings) .def("apply_settings", &session_apply_settings) #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_ENCRYPTION .def("set_pe_settings", allow_threads(<::session::set_pe_settings)) .def("get_pe_settings", allow_threads(<::session::get_pe_settings)) #endif #endif .def("load_state", &load_state, (arg("entry"), arg("flags") = 0xffffffff)) .def("save_state", &save_state, (arg("entry"), arg("flags") = 0xffffffff)) .def("pop_alerts", &pop_alerts) .def("wait_for_alert", &wait_for_alert #ifdef TORRENT_NO_DEPRECATE , return_internal_reference<>() #endif ) .def("add_extension", &add_extension) #ifndef TORRENT_NO_DEPRECATE .def("pop_alert", &pop_alert) #if TORRENT_USE_I2P .def("set_i2p_proxy", allow_threads(<::session::set_i2p_proxy)) .def("i2p_proxy", allow_threads(<::session::i2p_proxy)) #endif #endif .def("set_ip_filter", allow_threads(<::session::set_ip_filter)) .def("get_ip_filter", allow_threads(<::session::get_ip_filter)) .def("find_torrent", allow_threads(<::session::find_torrent)) .def("get_torrents", &get_torrents) .def("get_torrent_status", &get_torrent_status, (arg("session"), arg("pred"), arg("flags") = 0)) .def("refresh_torrent_status", &refresh_torrent_status, (arg("session"), arg("torrents"), arg("flags") = 0)) .def("pause", allow_threads(<::session::pause)) .def("resume", allow_threads(<::session::resume)) .def("is_paused", allow_threads(<::session::is_paused)) .def("get_cache_info", &get_cache_info1, (arg("handle") = torrent_handle(), arg("flags") = 0)) .def("add_port_mapping", allow_threads(<::session::add_port_mapping)) .def("delete_port_mapping", allow_threads(<::session::delete_port_mapping)) .def("set_peer_class_filter", <::session::set_peer_class_filter) .def("set_peer_class_type_filter", <::session::set_peer_class_type_filter) .def("create_peer_class", <::session::create_peer_class) .def("delete_peer_class", <::session::delete_peer_class) .def("get_peer_class", &get_peer_class) .def("set_peer_class", &set_peer_class) #ifndef TORRENT_NO_DEPRECATE .def("id", allow_threads(<::session::id)) .def( "listen_on", &listen_on , (arg("min"), "max", arg("interface") = (char const*)0, arg("flags") = 0) ) #ifndef TORRENT_DISABLE_DHT .def("start_dht", allow_threads(start_dht0)) .def("stop_dht", allow_threads(<::session::stop_dht)) .def("start_dht", allow_threads(start_dht1)) .def("dht_state", allow_threads(<::session::dht_state)) .def("set_dht_proxy", allow_threads(<::session::set_dht_proxy)) .def("dht_proxy", allow_threads(<::session::dht_proxy)) #endif .def("set_local_download_rate_limit", allow_threads(<::session::set_local_download_rate_limit)) .def("local_download_rate_limit", allow_threads(<::session::local_download_rate_limit)) .def("set_local_upload_rate_limit", allow_threads(<::session::set_local_upload_rate_limit)) .def("local_upload_rate_limit", allow_threads(<::session::local_upload_rate_limit)) .def("set_download_rate_limit", allow_threads(<::session::set_download_rate_limit)) .def("download_rate_limit", allow_threads(<::session::download_rate_limit)) .def("set_upload_rate_limit", allow_threads(<::session::set_upload_rate_limit)) .def("upload_rate_limit", allow_threads(<::session::upload_rate_limit)) .def("set_max_uploads", allow_threads(<::session::set_max_uploads)) .def("set_max_connections", allow_threads(<::session::set_max_connections)) .def("max_connections", allow_threads(<::session::max_connections)) .def("num_connections", allow_threads(<::session::num_connections)) .def("set_max_half_open_connections", allow_threads(<::session::set_max_half_open_connections)) .def("set_severity_level", allow_threads(<::session::set_severity_level)) .def("set_alert_queue_size_limit", allow_threads(<::session::set_alert_queue_size_limit)) .def("set_alert_mask", allow_threads(<::session::set_alert_mask)) .def("set_peer_proxy", allow_threads(<::session::set_peer_proxy)) .def("set_tracker_proxy", allow_threads(<::session::set_tracker_proxy)) .def("set_web_seed_proxy", allow_threads(<::session::set_web_seed_proxy)) .def("peer_proxy", allow_threads(<::session::peer_proxy)) .def("tracker_proxy", allow_threads(<::session::tracker_proxy)) .def("web_seed_proxy", allow_threads(<::session::web_seed_proxy)) .def("set_proxy", allow_threads(<::session::set_proxy)) .def("proxy", allow_threads(<::session::proxy)) .def("start_upnp", &start_upnp) .def("stop_upnp", allow_threads(<::session::stop_upnp)) .def("start_lsd", allow_threads(<::session::start_lsd)) .def("stop_lsd", allow_threads(<::session::stop_lsd)) .def("start_natpmp", &start_natpmp) .def("stop_natpmp", allow_threads(<::session::stop_natpmp)) .def("get_cache_status", &get_cache_status) .def("get_cache_info", &get_cache_info2) .def("set_peer_id", allow_threads(<::session::set_peer_id)) #endif // TORRENT_NO_DEPRECATE ; ses.attr("global_peer_class_id") = int(session::global_peer_class_id); ses.attr("tcp_peer_class_id") = int(session::tcp_peer_class_id); ses.attr("local_peer_class_id") = int(session::local_peer_class_id); } enum_("protocol_type") .value("udp", lt::session::udp) .value("tcp", lt::session::tcp) ; enum_("save_state_flags_t") .value("save_settings", lt::session::save_settings) .value("save_dht_settings", lt::session::save_dht_settings) .value("save_dht_state", lt::session::save_dht_state) #ifndef TORRENT_NO_DEPRECATE .value("save_encryption_settings", lt::session:: save_encryption_settings) .value("save_as_map", lt::session::save_as_map) .value("save_i2p_proxy", lt::session::save_i2p_proxy) .value("save_proxy", lt::session::save_proxy) .value("save_dht_proxy", lt::session::save_dht_proxy) .value("save_peer_proxy", lt::session::save_peer_proxy) .value("save_web_proxy", lt::session::save_web_proxy) .value("save_tracker_proxy", lt::session::save_tracker_proxy) #endif ; #ifndef TORRENT_NO_DEPRECATE enum_("listen_on_flags_t") .value("listen_reuse_address", lt::session::listen_reuse_address) .value("listen_no_system_port", lt::session::listen_no_system_port) ; class_("feed_handle") .def("update_feed", &feed_handle::update_feed) .def("get_feed_status", &get_feed_status) .def("set_settings", &set_feed_settings) .def("settings", &get_feed_settings) ; typedef session_settings (*mem_preset1)(); typedef session_settings (*perf_preset1)(); def("high_performance_seed", (perf_preset1)high_performance_seed); def("min_memory_usage", (mem_preset1)min_memory_usage); scope().attr("create_metadata_plugin") = "metadata_transfer"; #endif def("high_performance_seed", high_performance_seed_wrapper); def("min_memory_usage", min_memory_usage_wrapper); def("default_settings", default_settings_wrapper); class_("stats_metric") .def_readonly("name", &stats_metric::name) .def_readonly("value_index", &stats_metric::value_index) .def_readonly("type", &stats_metric::type) ; enum_("metric_type_t") .value("counter", stats_metric::type_counter) .value("gauge", stats_metric::type_gauge) ; def("session_stats_metrics", session_stats_metrics); def("find_metric_idx", find_metric_idx); scope().attr("create_ut_metadata_plugin") = "ut_metadata"; scope().attr("create_ut_pex_plugin") = "ut_pex"; scope().attr("create_smart_ban_plugin") = "smart_ban"; } libtorrent-rasterbar-1.1.13/bindings/python/src/session_settings.cpp000066400000000000000000000507111351156116000257630ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include "boost_python.hpp" #include using namespace boost::python; using namespace libtorrent; void bind_session_settings() { #ifndef TORRENT_NO_DEPRECATE class_("session_settings") .def_readwrite("user_agent", &session_settings::user_agent) .def_readwrite("tracker_completion_timeout", &session_settings::tracker_completion_timeout) .def_readwrite("tracker_receive_timeout", &session_settings::tracker_receive_timeout) .def_readwrite("stop_tracker_timeout", &session_settings::stop_tracker_timeout) .def_readwrite("tracker_maximum_response_length", &session_settings::tracker_maximum_response_length) .def_readwrite("piece_timeout", &session_settings::piece_timeout) .def_readwrite("request_timeout", &session_settings::request_timeout) .def_readwrite("request_queue_time", &session_settings::request_queue_time) .def_readwrite("max_allowed_in_request_queue", &session_settings::max_allowed_in_request_queue) .def_readwrite("max_out_request_queue", &session_settings::max_out_request_queue) .def_readwrite("whole_pieces_threshold", &session_settings::whole_pieces_threshold) .def_readwrite("peer_timeout", &session_settings::peer_timeout) .def_readwrite("urlseed_timeout", &session_settings::urlseed_timeout) .def_readwrite("urlseed_pipeline_size", &session_settings::urlseed_pipeline_size) .def_readwrite("urlseed_wait_retry", &session_settings::urlseed_wait_retry) .def_readwrite("file_pool_size", &session_settings::file_pool_size) .def_readwrite("allow_multiple_connections_per_ip", &session_settings::allow_multiple_connections_per_ip) .def_readwrite("max_failcount", &session_settings::max_failcount) .def_readwrite("min_reconnect_time", &session_settings::min_reconnect_time) .def_readwrite("peer_connect_timeout", &session_settings::peer_connect_timeout) .def_readwrite("ignore_limits_on_local_network", &session_settings::ignore_limits_on_local_network) .def_readwrite("connection_speed", &session_settings::connection_speed) .def_readwrite("send_redundant_have", &session_settings::send_redundant_have) .def_readwrite("lazy_bitfields", &session_settings::lazy_bitfields) .def_readwrite("inactivity_timeout", &session_settings::inactivity_timeout) .def_readwrite("unchoke_interval", &session_settings::unchoke_interval) .def_readwrite("optimistic_unchoke_interval", &session_settings::optimistic_unchoke_interval) .def_readwrite("announce_ip", &session_settings::announce_ip) .def_readwrite("num_want", &session_settings::num_want) .def_readwrite("initial_picker_threshold", &session_settings::initial_picker_threshold) .def_readwrite("allowed_fast_set_size", &session_settings::allowed_fast_set_size) // this is no longer used .def_readwrite("max_queued_disk_bytes", &session_settings::max_queued_disk_bytes) .def_readwrite("max_queued_disk_bytes_low_watermark", &session_settings::max_queued_disk_bytes_low_watermark) .def_readwrite("handshake_timeout", &session_settings::handshake_timeout) #ifndef TORRENT_DISABLE_DHT .def_readwrite("use_dht_as_fallback", &session_settings::use_dht_as_fallback) #endif .def_readwrite("free_torrent_hashes", &session_settings::free_torrent_hashes) .def_readwrite("upnp_ignore_nonrouters", &session_settings::upnp_ignore_nonrouters) .def_readwrite("send_buffer_low_watermark", &session_settings::send_buffer_low_watermark) .def_readwrite("send_buffer_watermark", &session_settings::send_buffer_watermark) .def_readwrite("send_buffer_watermark_factor", &session_settings::send_buffer_watermark_factor) .def_readwrite("choking_algorithm", &session_settings::choking_algorithm) .def_readwrite("seed_choking_algorithm", &session_settings::seed_choking_algorithm) .def_readwrite("use_parole_mode", &session_settings::use_parole_mode) .def_readwrite("cache_size", &session_settings::cache_size) .def_readwrite("cache_buffer_chunk_size", &session_settings::cache_buffer_chunk_size) .def_readwrite("cache_expiry", &session_settings::cache_expiry) .def_readwrite("use_read_cache", &session_settings::use_read_cache) .def_readwrite("explicit_read_cache", &session_settings::explicit_read_cache) .def_readwrite("explicit_cache_interval", &session_settings::explicit_cache_interval) .def_readwrite("disk_io_write_mode", &session_settings::disk_io_write_mode) .def_readwrite("disk_io_read_mode", &session_settings::disk_io_read_mode) .def_readwrite("coalesce_reads", &session_settings::coalesce_reads) .def_readwrite("coalesce_writes", &session_settings::coalesce_writes) .def_readwrite("peer_tos", &session_settings::peer_tos) .def_readwrite("active_downloads", &session_settings::active_downloads) .def_readwrite("active_seeds", &session_settings::active_seeds) .def_readwrite("active_dht_limit", &session_settings::active_dht_limit) .def_readwrite("active_tracker_limit", &session_settings::active_tracker_limit) .def_readwrite("active_lsd_limit", &session_settings::active_lsd_limit) .def_readwrite("active_limit", &session_settings::active_limit) .def_readwrite("auto_manage_prefer_seeds", &session_settings::auto_manage_prefer_seeds) .def_readwrite("dont_count_slow_torrents", &session_settings::dont_count_slow_torrents) .def_readwrite("auto_manage_interval", &session_settings::auto_manage_interval) .def_readwrite("share_ratio_limit", &session_settings::share_ratio_limit) .def_readwrite("seed_time_ratio_limit", &session_settings::seed_time_ratio_limit) .def_readwrite("seed_time_limit", &session_settings::seed_time_limit) .def_readwrite("peer_turnover_interval", &session_settings::peer_turnover_interval) .def_readwrite("peer_turnover", &session_settings::peer_turnover) .def_readwrite("peer_turnover_cutoff", &session_settings::peer_turnover_cutoff) .def_readwrite("close_redundant_connections", &session_settings::close_redundant_connections) .def_readwrite("auto_scrape_interval", &session_settings::auto_scrape_interval) .def_readwrite("auto_scrape_min_interval", &session_settings::auto_scrape_min_interval) .def_readwrite("max_peerlist_size", &session_settings::max_peerlist_size) .def_readwrite("max_paused_peerlist_size", &session_settings::max_paused_peerlist_size) .def_readwrite("min_announce_interval", &session_settings::min_announce_interval) .def_readwrite("prioritize_partial_pieces", &session_settings::prioritize_partial_pieces) .def_readwrite("auto_manage_startup", &session_settings::auto_manage_startup) .def_readwrite("rate_limit_ip_overhead", &session_settings::rate_limit_ip_overhead) .def_readwrite("announce_to_all_trackers", &session_settings::announce_to_all_trackers) .def_readwrite("announce_to_all_tiers", &session_settings::announce_to_all_tiers) .def_readwrite("prefer_udp_trackers", &session_settings::prefer_udp_trackers) .def_readwrite("strict_super_seeding", &session_settings::strict_super_seeding) .def_readwrite("seeding_piece_quota", &session_settings::seeding_piece_quota) .def_readwrite("max_sparse_regions", &session_settings::max_sparse_regions) .def_readwrite("lock_disk_cache", &session_settings::lock_disk_cache) .def_readwrite("max_rejects", &session_settings::max_rejects) .def_readwrite("recv_socket_buffer_size", &session_settings::recv_socket_buffer_size) .def_readwrite("send_socket_buffer_size", &session_settings::send_socket_buffer_size) .def_readwrite("optimize_hashing_for_speed", &session_settings::optimize_hashing_for_speed) .def_readwrite("file_checks_delay_per_block", &session_settings::file_checks_delay_per_block) .def_readwrite("disk_cache_algorithm", &session_settings::disk_cache_algorithm) .def_readwrite("read_cache_line_size", &session_settings::read_cache_line_size) .def_readwrite("write_cache_line_size", &session_settings::write_cache_line_size) .def_readwrite("optimistic_disk_retry", &session_settings::optimistic_disk_retry) .def_readwrite("disable_hash_checks", &session_settings::disable_hash_checks) .def_readwrite("allow_reordered_disk_operations", &session_settings::allow_reordered_disk_operations) .def_readwrite("allow_i2p_mixed", &session_settings::allow_i2p_mixed) .def_readwrite("max_suggest_pieces", &session_settings::max_suggest_pieces) .def_readwrite("drop_skipped_requests", &session_settings::drop_skipped_requests) .def_readwrite("low_prio_disk", &session_settings::low_prio_disk) .def_readwrite("local_service_announce_interval", &session_settings::local_service_announce_interval) .def_readwrite("dht_announce_interval", &session_settings::dht_announce_interval) .def_readwrite("udp_tracker_token_expiry", &session_settings::udp_tracker_token_expiry) .def_readwrite("volatile_read_cache", &session_settings::volatile_read_cache) .def_readwrite("guided_read_cache", &session_settings::guided_read_cache) .def_readwrite("default_cache_min_age", &session_settings::default_cache_min_age) .def_readwrite("num_optimistic_unchoke_slots", &session_settings::num_optimistic_unchoke_slots) .def_readwrite("no_atime_storage", &session_settings::no_atime_storage) .def_readwrite("default_est_reciprocation_rate", &session_settings::default_est_reciprocation_rate) .def_readwrite("increase_est_reciprocation_rate", &session_settings::increase_est_reciprocation_rate) .def_readwrite("decrease_est_reciprocation_rate", &session_settings::decrease_est_reciprocation_rate) .def_readwrite("incoming_starts_queued_torrents", &session_settings::incoming_starts_queued_torrents) .def_readwrite("report_true_downloaded", &session_settings::report_true_downloaded) .def_readwrite("strict_end_game_mode", &session_settings::strict_end_game_mode) .def_readwrite("broadcast_lsd", &session_settings::broadcast_lsd) .def_readwrite("ignore_resume_timestamps", &session_settings::ignore_resume_timestamps) .def_readwrite("no_recheck_incomplete_resume", &session_settings::no_recheck_incomplete_resume) .def_readwrite("anonymous_mode", &session_settings::anonymous_mode) .def_readwrite("force_proxy", &session_settings::force_proxy) .def_readwrite("tick_interval", &session_settings::tick_interval) .def_readwrite("report_web_seed_downloads", &session_settings::report_web_seed_downloads) .def_readwrite("share_mode_target", &session_settings::share_mode_target) .def_readwrite("rate_limit_utp", &session_settings::rate_limit_utp) .def_readwrite("upload_rate_limit", &session_settings::upload_rate_limit) .def_readwrite("download_rate_limit", &session_settings::download_rate_limit) .def_readwrite("local_upload_rate_limit", &session_settings::local_upload_rate_limit) .def_readwrite("local_download_rate_limit", &session_settings::local_download_rate_limit) .def_readwrite("dht_upload_rate_limit", &session_settings::dht_upload_rate_limit) .def_readwrite("unchoke_slots_limit", &session_settings::unchoke_slots_limit) .def_readwrite("connections_limit", &session_settings::connections_limit) .def_readwrite("utp_target_delay", &session_settings::utp_target_delay) .def_readwrite("utp_gain_factor", &session_settings::utp_gain_factor) .def_readwrite("utp_min_timeout", &session_settings::utp_min_timeout) .def_readwrite("utp_syn_resends", &session_settings::utp_syn_resends) .def_readwrite("utp_fin_resends", &session_settings::utp_fin_resends) .def_readwrite("utp_num_resends", &session_settings::utp_num_resends) .def_readwrite("utp_connect_timeout", &session_settings::utp_connect_timeout) .def_readwrite("half_open_limit", &session_settings::half_open_limit) .def_readwrite("utp_delayed_ack", &session_settings::utp_delayed_ack) .def_readwrite("utp_dynamic_sock_buf", &session_settings::utp_dynamic_sock_buf) .def_readwrite("utp_loss_multiplier", &session_settings::utp_loss_multiplier) .def_readwrite("mixed_mode_algorithm", &session_settings::mixed_mode_algorithm) .def_readwrite("listen_queue_size", &session_settings::listen_queue_size) .def_readwrite("announce_double_nat", &session_settings::announce_double_nat) .def_readwrite("torrent_connect_boost", &session_settings::torrent_connect_boost) .def_readwrite("seeding_outgoing_connections", &session_settings::seeding_outgoing_connections) .def_readwrite("no_connect_privileged_ports", &session_settings::no_connect_privileged_ports) .def_readwrite("alert_queue_size", &session_settings::alert_queue_size) .def_readwrite("max_metadata_size", &session_settings::max_metadata_size) .def_readwrite("smooth_connects", &session_settings::smooth_connects) .def_readwrite("always_send_user_agent", &session_settings::always_send_user_agent) .def_readwrite("apply_ip_filter_to_trackers", &session_settings::apply_ip_filter_to_trackers) .def_readwrite("read_job_every", &session_settings::read_job_every) .def_readwrite("use_disk_read_ahead", &session_settings::use_disk_read_ahead) .def_readwrite("lock_files", &session_settings::lock_files) .def_readwrite("enable_outgoing_tcp", &session_settings::enable_outgoing_tcp) .def_readwrite("enable_incoming_tcp", &session_settings::enable_incoming_tcp) .def_readwrite("enable_outgoing_utp", &session_settings::enable_outgoing_utp) .def_readwrite("enable_incoming_utp", &session_settings::enable_incoming_utp) .def_readwrite("max_pex_peers", &session_settings::max_pex_peers) .def_readwrite("ssl_listen", &session_settings::ssl_listen) .def_readwrite("tracker_backoff", &session_settings::tracker_backoff) .def_readwrite("ban_web_seeds", &session_settings::ban_web_seeds) .def_readwrite("max_http_recv_buffer_size", &session_settings::max_http_recv_buffer_size) .def_readwrite("support_share_mode", &session_settings::support_share_mode) .def_readwrite("support_merkle_torrents", &session_settings::support_merkle_torrents) .def_readwrite("report_redundant_bytes", &session_settings::report_redundant_bytes) .def_readwrite("handshake_client_version", &session_settings::handshake_client_version) .def_readwrite("use_disk_cache_pool", &session_settings::use_disk_cache_pool) ; enum_("disk_cache_algo_t") .value("lru", session_settings::lru) .value("largest_contiguous", session_settings::largest_contiguous) .value("avoid_readback", session_settings::avoid_readback) ; #endif // TORRENT_NO_DEPRECATE enum_("choking_algorithm_t") .value("fixed_slots_choker", settings_pack::fixed_slots_choker) #ifndef TORRENT_NO_DEPRECATE .value("auto_expand_choker", settings_pack::rate_based_choker) #endif .value("rate_based_choker", settings_pack::rate_based_choker) .value("bittyrant_choker", settings_pack::bittyrant_choker) ; enum_("seed_choking_algorithm_t") .value("round_robin", settings_pack::round_robin) .value("fastest_upload", settings_pack::fastest_upload) .value("anti_leech", settings_pack::anti_leech) ; enum_("suggest_mode_t") .value("no_piece_suggestions", settings_pack::no_piece_suggestions) .value("suggest_read_cache", settings_pack::suggest_read_cache) ; enum_("io_buffer_mode_t") .value("enable_os_cache", settings_pack::enable_os_cache) #ifndef TORRENT_NO_DEPRECATE .value("disable_os_cache_for_aligned_files", settings_pack::disable_os_cache_for_aligned_files) #endif .value("disable_os_cache", settings_pack::disable_os_cache) ; enum_("bandwidth_mixed_algo_t") .value("prefer_tcp", settings_pack::prefer_tcp) .value("peer_proportional", settings_pack::peer_proportional) ; enum_("enc_policy") .value("pe_forced", settings_pack::pe_forced) .value("pe_enabled", settings_pack::pe_enabled) .value("pe_disabled", settings_pack::pe_disabled) #ifndef TORRENT_NO_DEPRECATE .value("forced", settings_pack::pe_forced) .value("enabled", settings_pack::pe_enabled) .value("disabled", settings_pack::pe_disabled) #endif ; enum_("enc_level") .value("pe_rc4", settings_pack::pe_rc4) .value("pe_plaintext", settings_pack::pe_plaintext) .value("pe_both", settings_pack::pe_both) #ifndef TORRENT_NO_DEPRECATE .value("rc4", settings_pack::pe_rc4) .value("plaintext", settings_pack::pe_plaintext) .value("both", settings_pack::pe_both) #endif ; enum_("proxy_type_t") .value("none", settings_pack::none) .value("socks4", settings_pack::socks4) .value("socks5", settings_pack::socks5) .value("socks5_pw", settings_pack::socks5_pw) .value("http", settings_pack::http) .value("http_pw", settings_pack::http_pw) .value("i2p_proxy", settings_pack::i2p_proxy) ; #ifndef TORRENT_NO_DEPRECATE enum_("proxy_type") .value("none", proxy_settings::none) .value("socks4", proxy_settings::socks4) .value("socks5", proxy_settings::socks5) .value("socks5_pw", proxy_settings::socks5_pw) .value("http", proxy_settings::http) .value("http_pw", proxy_settings::http_pw) .value("i2p_proxy", proxy_settings::i2p_proxy) ; class_("proxy_settings") .def_readwrite("hostname", &proxy_settings::hostname) .def_readwrite("port", &proxy_settings::port) .def_readwrite("password", &proxy_settings::password) .def_readwrite("username", &proxy_settings::username) .def_readwrite("type", &proxy_settings::type) .def_readwrite("proxy_peer_connections", &proxy_settings::proxy_peer_connections) .def_readwrite("proxy_hostnames", &proxy_settings::proxy_hostnames) ; #endif #ifndef TORRENT_DISABLE_DHT class_("dht_settings") .def_readwrite("max_peers_reply", &dht_settings::max_peers_reply) .def_readwrite("search_branching", &dht_settings::search_branching) #ifndef TORRENT_NO_DEPRECATE .def_readwrite("service_port", &dht_settings::service_port) #endif .def_readwrite("max_fail_count", &dht_settings::max_fail_count) .def_readwrite("max_torrents", &dht_settings::max_torrents) .def_readwrite("max_dht_items", &dht_settings::max_dht_items) .def_readwrite("restrict_routing_ips", &dht_settings::restrict_routing_ips) .def_readwrite("restrict_search_ips", &dht_settings::restrict_search_ips) .def_readwrite("max_torrent_search_reply", &dht_settings::max_torrent_search_reply) .def_readwrite("extended_routing_table", &dht_settings::extended_routing_table) .def_readwrite("aggressive_lookups", &dht_settings::aggressive_lookups) .def_readwrite("privacy_lookups", &dht_settings::privacy_lookups) .def_readwrite("enforce_node_id", &dht_settings::enforce_node_id) .def_readwrite("ignore_dark_internet", &dht_settings::ignore_dark_internet) .def_readwrite("block_timeout", &dht_settings::block_timeout) .def_readwrite("block_ratelimit", &dht_settings::block_ratelimit) .def_readwrite("read_only", &dht_settings::read_only) .def_readwrite("item_lifetime", &dht_settings::item_lifetime) ; #endif #ifndef TORRENT_NO_DEPRECATE class_("pe_settings") .def_readwrite("out_enc_policy", &pe_settings::out_enc_policy) .def_readwrite("in_enc_policy", &pe_settings::in_enc_policy) .def_readwrite("allowed_enc_level", &pe_settings::allowed_enc_level) .def_readwrite("prefer_rc4", &pe_settings::prefer_rc4) ; #endif } libtorrent-rasterbar-1.1.13/bindings/python/src/string.cpp000066400000000000000000000034531351156116000236670ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include "boost_python.hpp" #include using namespace boost::python; struct unicode_from_python { unicode_from_python() { converter::registry::push_back( &convertible, &construct, type_id() ); } static void* convertible(PyObject* x) { #if PY_VERSION_HEX >= 0x03020000 return PyBytes_Check(x) ? x : PyUnicode_Check(x) ? x : 0; #else return PyString_Check(x) ? x : PyUnicode_Check(x) ? x : 0; #endif } static void construct(PyObject* x, converter::rvalue_from_python_stage1_data* data) { void* storage = ((converter::rvalue_from_python_storage< std::string>*)data)->storage.bytes; if (PyUnicode_Check(x)) { PyObject* utf8 = PyUnicode_AsUTF8String(x); if (utf8 == NULL) { new (storage) std::string(); } else { #if PY_VERSION_HEX >= 0x03000000 new (storage) std::string(PyBytes_AsString(utf8) , PyBytes_Size(utf8)); #else new (storage) std::string(PyString_AsString(utf8) , PyString_Size(utf8)); #endif Py_DECREF(utf8); } } else { #if PY_VERSION_HEX >= 0x03000000 new (storage) std::string(PyBytes_AsString(x), PyBytes_Size(x)); #else new (storage) std::string(PyString_AsString(x) , PyString_Size(x)); #endif } data->convertible = storage; } }; void bind_unicode_string_conversion() { unicode_from_python(); } libtorrent-rasterbar-1.1.13/bindings/python/src/torrent_handle.cpp000066400000000000000000000464771351156116000254060ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include "boost_python.hpp" #include #include #include #include #include #include #include #include "libtorrent/announce_entry.hpp" #include #include #include "gil.hpp" using namespace boost::python; using namespace libtorrent; namespace { list url_seeds(torrent_handle& handle) { list ret; std::set urls; { allow_threading_guard guard; urls = handle.url_seeds(); } for (std::set::iterator i(urls.begin()) , end(urls.end()); i != end; ++i) ret.append(*i); return ret; } list http_seeds(torrent_handle& handle) { list ret; std::set urls; { allow_threading_guard guard; urls = handle.http_seeds(); } for (std::set::iterator i(urls.begin()) , end(urls.end()); i != end; ++i) ret.append(*i); return ret; } list piece_availability(torrent_handle& handle) { list ret; std::vector avail; { allow_threading_guard guard; handle.piece_availability(avail); } for (std::vector::iterator i(avail.begin()) , end(avail.end()); i != end; ++i) ret.append(*i); return ret; } list piece_priorities(torrent_handle& handle) { list ret; std::vector prio; { allow_threading_guard guard; prio = handle.piece_priorities(); } for (std::vector::iterator i(prio.begin()) , end(prio.end()); i != end; ++i) ret.append(*i); return ret; } } // namespace unnamed list file_progress(torrent_handle& handle, int flags) { std::vector p; { allow_threading_guard guard; boost::shared_ptr ti = handle.torrent_file(); if (ti) { p.reserve(ti->num_files()); handle.file_progress(p, flags); } } list result; for (std::vector::iterator i(p.begin()), e(p.end()); i != e; ++i) result.append(*i); return result; } list get_peer_info(torrent_handle const& handle) { std::vector pi; { allow_threading_guard guard; handle.get_peer_info(pi); } list result; for (std::vector::iterator i = pi.begin(); i != pi.end(); ++i) result.append(*i); return result; } namespace { template T extract_fn(object o) { return boost::python::extract(o); } } void prioritize_pieces(torrent_handle& info, object o) { stl_input_iterator begin(o), end; if (begin == end) return; // determine which overload should be selected. the one taking a list of // priorities or the one taking a list of piece -> priority mappings bool const is_piece_list = extract >(*begin).check(); if (is_piece_list) { std::vector > piece_list; std::transform(begin, end, std::back_inserter(piece_list) , &extract_fn >); info.prioritize_pieces(piece_list); } else { std::vector priority_vector; std::transform(begin, end, std::back_inserter(priority_vector) , &extract_fn); info.prioritize_pieces(priority_vector); } } void prioritize_files(torrent_handle& info, object o) { stl_input_iterator begin(o), end; info.prioritize_files(std::vector (begin, end)); } list file_priorities(torrent_handle& handle) { list ret; std::vector priorities = handle.file_priorities(); for (std::vector::iterator i = priorities.begin(); i != priorities.end(); ++i) ret.append(*i); return ret; } int file_prioritity0(torrent_handle& h, int index) { return h.file_priority(index); } void file_prioritity1(torrent_handle& h, int index, int prio) { return h.file_priority(index, prio); } void dict_to_announce_entry(dict d, announce_entry& ae) { ae.url = extract(d["url"]); if (d.has_key("tier")) ae.tier = extract(d["tier"]); if (d.has_key("fail_limit")) ae.fail_limit = extract(d["fail_limit"]); } void replace_trackers(torrent_handle& h, object trackers) { object iter(trackers.attr("__iter__")()); std::vector result; for (;;) { handle<> entry(allow_null(PyIter_Next(iter.ptr()))); if (entry == handle<>()) break; if (extract(object(entry)).check()) { result.push_back(extract(object(entry))); } else { dict d; d = extract(object(entry)); announce_entry ae; dict_to_announce_entry(d, ae); result.push_back(ae); } } allow_threading_guard guard; h.replace_trackers(result); } void add_tracker(torrent_handle& h, dict d) { announce_entry ae; dict_to_announce_entry(d, ae); h.add_tracker(ae); } namespace { #if defined BOOST_ASIO_HAS_STD_CHRONO using std::chrono::system_clock; #else using boost::chrono::system_clock; #endif time_t to_ptime(time_point tpt) { return system_clock::to_time_t(system_clock::now() + duration_cast(tpt - clock_type::now())); } } list trackers(torrent_handle& h) { list ret; std::vector const trackers = h.trackers(); for (std::vector::const_iterator i = trackers.begin(), end(trackers.end()); i != end; ++i) { dict d; d["url"] = i->url; d["trackerid"] = i->trackerid; d["message"] = i->message; dict last_error; last_error["value"] = i->last_error.value(); last_error["category"] = i->last_error.category().name(); d["last_error"] = last_error; if (i->next_announce > min_time()) { d["next_announce"] = to_ptime(i->next_announce); } else { d["next_announce"] = object(); } if (i->min_announce > min_time()) { d["min_announce"] = to_ptime(i->min_announce); } else { d["min_announce"] = object(); } d["scrape_incomplete"] = i->scrape_incomplete; d["scrape_complete"] = i->scrape_complete; d["scrape_downloaded"] = i->scrape_downloaded; d["tier"] = i->tier; d["fail_limit"] = i->fail_limit; d["fails"] = i->fails; d["source"] = i->source; d["verified"] = i->verified; d["updating"] = i->updating; d["start_sent"] = i->start_sent; d["complete_sent"] = i->complete_sent; d["send_stats"] = i->send_stats; ret.append(d); } return ret; } list get_download_queue(torrent_handle& handle) { list ret; std::vector downloading; { allow_threading_guard guard; handle.get_download_queue(downloading); } for (std::vector::iterator i = downloading.begin() , end(downloading.end()); i != end; ++i) { dict partial_piece; partial_piece["piece_index"] = i->piece_index; partial_piece["blocks_in_piece"] = i->blocks_in_piece; list block_list; for (int k = 0; k < i->blocks_in_piece; ++k) { dict block_info; block_info["state"] = i->blocks[k].state; block_info["num_peers"] = i->blocks[k].num_peers; block_info["bytes_progress"] = i->blocks[k].bytes_progress; block_info["block_size"] = i->blocks[k].block_size; block_info["peer"] = boost::python::make_tuple( i->blocks[k].peer().address().to_string() , i->blocks[k].peer().port()); block_list.append(block_info); } partial_piece["blocks"] = block_list; ret.append(partial_piece); } return ret; } void set_metadata(torrent_handle& handle, std::string const& buf) { handle.set_metadata(buf.c_str(), buf.size()); } namespace { tcp::endpoint tuple_to_endpoint(tuple const& t) { return tcp::endpoint(address::from_string(extract(t[0])), extract(t[1])); } } void connect_peer(torrent_handle& th, tuple ip, int source, int flags) { th.connect_peer(tuple_to_endpoint(ip), source, flags); } std::vector file_status(torrent_handle const& h) { std::vector ret; h.file_status(ret); return ret; } #ifndef TORRENT_NO_DEPRECATE #if BOOST_VERSION > 104200 boost::shared_ptr get_torrent_info(torrent_handle const& h) { allow_threading_guard guard; return h.torrent_file(); } #else boost::shared_ptr get_torrent_info(torrent_handle const& h) { // I can't figure out how to expose shared_ptr // as well as supporting mutable instances. So, this hack is better // than compilation errors. It seems to work on newer versions of boost though allow_threading_guard guard; return boost::const_pointer_cast(h.torrent_file()); } #endif void set_peer_upload_limit(torrent_handle& th, tuple const& ip, int limit) { th.set_peer_upload_limit(tuple_to_endpoint(ip), limit); } void set_peer_download_limit(torrent_handle& th, tuple const& ip, int limit) { th.set_peer_download_limit(tuple_to_endpoint(ip), limit); } #endif // TORRENT_NO_DEPRECAE void add_piece(torrent_handle& th, int piece, char const *data, int flags) { th.add_piece(piece, data, flags); } void bind_torrent_handle() { // arguments are: number of seconds and tracker index void (torrent_handle::*force_reannounce0)(int, int, int) const = &torrent_handle::force_reannounce; #ifndef TORRENT_NO_DEPRECATE bool (torrent_handle::*super_seeding0)() const = &torrent_handle::super_seeding; #endif void (torrent_handle::*super_seeding1)(bool) const = &torrent_handle::super_seeding; int (torrent_handle::*piece_priority0)(int) const = &torrent_handle::piece_priority; void (torrent_handle::*piece_priority1)(int, int) const = &torrent_handle::piece_priority; void (torrent_handle::*move_storage0)(std::string const&, int flags) const = &torrent_handle::move_storage; void (torrent_handle::*rename_file0)(int, std::string const&) const = &torrent_handle::rename_file; #if TORRENT_USE_WSTRING && !defined TORRENT_NO_DEPRECATE void (torrent_handle::*move_storage1)(std::wstring const&, int flags) const = &torrent_handle::move_storage; void (torrent_handle::*rename_file1)(int, std::wstring const&) const = &torrent_handle::rename_file; #endif #ifndef TORRENT_NO_DEPRECATE // deprecated in 1.1 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES bool (torrent_handle::*resolve_countries0)() const = &torrent_handle::resolve_countries; void (torrent_handle::*resolve_countries1)(bool) = &torrent_handle::resolve_countries; #endif #endif // TORRENT_NO_DEPRECATE #define _ allow_threads class_("torrent_handle") .def(self == self) .def(self != self) .def(self < self) .def("__hash__", (std::size_t (*)(torrent_handle const&))&libtorrent::hash_value) .def("get_peer_info", get_peer_info) .def("status", _(&torrent_handle::status), arg("flags") = 0xffffffff) .def("get_download_queue", get_download_queue) .def("file_progress", file_progress, arg("flags") = 0) .def("trackers", trackers) .def("replace_trackers", replace_trackers) .def("add_tracker", add_tracker) .def("add_url_seed", _(&torrent_handle::add_url_seed)) .def("remove_url_seed", _(&torrent_handle::remove_url_seed)) .def("url_seeds", url_seeds) .def("add_http_seed", _(&torrent_handle::add_http_seed)) .def("remove_http_seed", _(&torrent_handle::remove_http_seed)) .def("http_seeds", http_seeds) .def("torrent_file", _(&torrent_handle::torrent_file)) .def("set_metadata", set_metadata) .def("is_valid", _(&torrent_handle::is_valid)) .def("pause", _(&torrent_handle::pause), arg("flags") = 0) .def("resume", _(&torrent_handle::resume)) .def("stop_when_ready", _(&torrent_handle::stop_when_ready)) .def("clear_error", _(&torrent_handle::clear_error)) .def("set_priority", _(&torrent_handle::set_priority)) .def("super_seeding", super_seeding1) .def("auto_managed", _(&torrent_handle::auto_managed)) .def("queue_position", _(&torrent_handle::queue_position)) .def("queue_position_up", _(&torrent_handle::queue_position_up)) .def("queue_position_down", _(&torrent_handle::queue_position_down)) .def("queue_position_top", _(&torrent_handle::queue_position_top)) .def("queue_position_bottom", _(&torrent_handle::queue_position_bottom)) // deprecated #ifndef TORRENT_NO_DEPRECATE // resolve countries deprecated in 1.1 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES .def("resolve_countries", _(resolve_countries0)) .def("resolve_countries", _(resolve_countries1)) #endif .def("get_torrent_info", &get_torrent_info) .def("super_seeding", super_seeding0) .def("filter_piece", _(&torrent_handle::filter_piece)) .def("is_piece_filtered", _(&torrent_handle::is_piece_filtered)) .def("write_resume_data", _(&torrent_handle::write_resume_data)) .def("is_seed", _(&torrent_handle::is_seed)) .def("is_finished", _(&torrent_handle::is_finished)) .def("is_paused", _(&torrent_handle::is_paused)) .def("is_auto_managed", _(&torrent_handle::is_auto_managed)) .def("has_metadata", _(&torrent_handle::has_metadata)) .def("use_interface", &torrent_handle::use_interface) .def("name", _(&torrent_handle::name)) #endif .def("add_piece", add_piece) .def("read_piece", _(&torrent_handle::read_piece)) .def("have_piece", _(&torrent_handle::have_piece)) .def("set_piece_deadline", _(&torrent_handle::set_piece_deadline) , (arg("index"), arg("deadline"), arg("flags") = 0)) .def("reset_piece_deadline", _(&torrent_handle::reset_piece_deadline), (arg("index"))) .def("clear_piece_deadlines", _(&torrent_handle::clear_piece_deadlines), (arg("index"))) .def("piece_availability", &piece_availability) .def("piece_priority", _(piece_priority0)) .def("piece_priority", _(piece_priority1)) .def("prioritize_pieces", &prioritize_pieces) .def("piece_priorities", &piece_priorities) .def("prioritize_files", &prioritize_files) .def("file_priorities", &file_priorities) .def("file_priority", &file_prioritity0) .def("file_priority", &file_prioritity1) .def("file_status", &::file_status) .def("save_resume_data", _(&torrent_handle::save_resume_data), arg("flags") = 0) .def("need_save_resume_data", _(&torrent_handle::need_save_resume_data)) .def("force_reannounce", _(force_reannounce0) , (arg("seconds") = 0, arg("tracker_idx") = -1, arg("flags") = 0)) #ifndef TORRENT_DISABLE_DHT .def("force_dht_announce", _(&torrent_handle::force_dht_announce)) #endif .def("scrape_tracker", _(&torrent_handle::scrape_tracker), arg("index") = -1) .def("set_upload_mode", _(&torrent_handle::set_upload_mode)) .def("set_share_mode", _(&torrent_handle::set_share_mode)) .def("flush_cache", &torrent_handle::flush_cache) .def("apply_ip_filter", &torrent_handle::apply_ip_filter) .def("set_upload_limit", _(&torrent_handle::set_upload_limit)) .def("upload_limit", _(&torrent_handle::upload_limit)) .def("set_download_limit", _(&torrent_handle::set_download_limit)) .def("download_limit", _(&torrent_handle::download_limit)) .def("set_sequential_download", _(&torrent_handle::set_sequential_download)) #ifndef TORRENT_NO_DEPRECATE .def("set_peer_upload_limit", &set_peer_upload_limit) .def("set_peer_download_limit", &set_peer_download_limit) .def("set_ratio", _(&torrent_handle::set_ratio)) .def("save_path", _(&torrent_handle::save_path)) #endif .def("connect_peer", &connect_peer, (arg("ip"), arg("source") = 0, arg("flags") = 0xd)) .def("set_max_uploads", _(&torrent_handle::set_max_uploads)) .def("max_uploads", _(&torrent_handle::max_uploads)) .def("set_max_connections", _(&torrent_handle::set_max_connections)) .def("max_connections", _(&torrent_handle::max_connections)) #ifndef TORRENT_NO_DEPRECATE .def("set_tracker_login", _(&torrent_handle::set_tracker_login)) #endif .def("move_storage", _(move_storage0), (arg("path"), arg("flags") = 0)) .def("info_hash", _(&torrent_handle::info_hash)) .def("force_recheck", _(&torrent_handle::force_recheck)) .def("rename_file", _(rename_file0)) .def("set_ssl_certificate", &torrent_handle::set_ssl_certificate, (arg("cert"), arg("private_key"), arg("dh_params"), arg("passphrase")="")) #if TORRENT_USE_WSTRING && !defined TORRENT_NO_DEPRECATE .def("move_storage", _(move_storage1), (arg("path"), arg("flags") = 0)) .def("rename_file", _(rename_file1)) #endif ; class_("pool_file_status") .def_readonly("file_index", &pool_file_status::file_index) .def_readonly("last_use", &pool_file_status::last_use) .def_readonly("open_mode", &pool_file_status::open_mode) ; enum_("file_progress_flags") .value("piece_granularity", torrent_handle::piece_granularity) ; enum_("add_piece_flags_t") .value("overwrite_existing", torrent_handle::overwrite_existing) ; enum_("pause_flags_t") .value("graceful_pause", torrent_handle::graceful_pause) ; enum_("save_resume_flags_t") .value("flush_disk_cache", torrent_handle::flush_disk_cache) .value("save_info_dict", torrent_handle::save_info_dict) .value("only_if_modified", torrent_handle::only_if_modified) ; enum_("reannounce_flags_t") .value("ignore_min_interval", torrent_handle::ignore_min_interval) ; enum_("deadline_flags") .value("alert_when_available", torrent_handle::alert_when_available) ; enum_("status_flags_t") .value("query_distributed_copies", torrent_handle::query_distributed_copies) .value("query_accurate_download_counters", torrent_handle::query_accurate_download_counters) .value("query_last_seen_complete", torrent_handle::query_last_seen_complete) .value("query_pieces", torrent_handle::query_pieces) .value("query_verified_pieces", torrent_handle::query_verified_pieces) ; enum_("move_flags_t") .value("always_replace_files", always_replace_files) .value("fail_if_exist", fail_if_exist) .value("dont_replace", dont_replace) ; } libtorrent-rasterbar-1.1.13/bindings/python/src/torrent_info.cpp000066400000000000000000000314351351156116000250720ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include "libtorrent/aux_/disable_warnings_push.hpp" #include "boost_python.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/time.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/announce_entry.hpp" #include "bytes.hpp" using namespace boost::python; using namespace libtorrent; namespace lt = libtorrent; namespace { std::vector::const_iterator begin_trackers(torrent_info& i) { return i.trackers().begin(); } std::vector::const_iterator end_trackers(torrent_info& i) { return i.trackers().end(); } void add_node(torrent_info& ti, char const* hostname, int port) { ti.add_node(std::make_pair(hostname, port)); } list nodes(torrent_info const& ti) { list result; typedef std::vector > list_type; for (list_type::const_iterator i = ti.nodes().begin(); i != ti.nodes().end(); ++i) { result.append(boost::python::make_tuple(i->first, i->second)); } return result; } list get_web_seeds(torrent_info const& ti) { std::vector const& ws = ti.web_seeds(); list ret; for (std::vector::const_iterator i = ws.begin() , end(ws.end()); i != end; ++i) { dict d; d["url"] = i->url; d["type"] = i->type; d["auth"] = i->auth; ret.append(d); } return ret; } void set_web_seeds(torrent_info& ti, list ws) { std::vector web_seeds; int const len = boost::python::len(ws); for (int i = 0; i < len; i++) { dict e = extract(ws[i]); int const type = extract(e["type"]); web_seeds.push_back(web_seed_entry( extract(e["url"]) , static_cast(type) , extract(e["auth"]))); } ti.set_web_seeds(web_seeds); } list get_merkle_tree(torrent_info const& ti) { std::vector const& mt = ti.merkle_tree(); list ret; for (std::vector::const_iterator i = mt.begin() , end(mt.end()); i != end; ++i) { ret.append(bytes(i->to_string())); } return ret; } void set_merkle_tree(torrent_info& ti, list hashes) { std::vector h; for (int i = 0, e = len(hashes); i < e; ++i) h.push_back(sha1_hash(bytes(extract(hashes[i])).arr)); ti.set_merkle_tree(h); } bytes hash_for_piece(torrent_info const& ti, int i) { return bytes(ti.hash_for_piece(i).to_string()); } bytes metadata(torrent_info const& ti) { return bytes(ti.metadata().get(), ti.metadata_size()); } list map_block(torrent_info& ti, int piece, boost::int64_t offset, int size) { std::vector p = ti.map_block(piece, offset, size); list result; for (std::vector::iterator i(p.begin()), e(p.end()); i != e; ++i) result.append(*i); return result; } // Create getters for announce_entry data members with non-trivial types which need converting. lt::time_point get_next_announce(announce_entry const& ae) { return ae.next_announce; } lt::time_point get_min_announce(announce_entry const& ae) { return ae.min_announce; } // announce_entry data member bit-fields. int get_fails(announce_entry const& ae) { return ae.fails; } int get_source(announce_entry const& ae) { return ae.source; } bool get_verified(announce_entry const& ae) { return ae.verified; } bool get_updating(announce_entry const& ae) { return ae.updating; } bool get_start_sent(announce_entry const& ae) { return ae.start_sent; } bool get_complete_sent(announce_entry const& ae) { return ae.complete_sent; } bool get_send_stats(announce_entry const& ae) { return ae.send_stats; } // announce_entry method requires lt::time_point. bool can_announce(announce_entry const& ae, bool is_seed) { lt::time_point now = lt::clock_type::now(); return ae.can_announce(now, is_seed); } #ifndef TORRENT_NO_DEPRECATE boost::int64_t get_size(file_entry const& fe) { return fe.size; } boost::int64_t get_offset(file_entry const& fe) { return fe.offset; } boost::int64_t get_file_base(file_entry const& fe) { return fe.file_base; } void set_file_base(file_entry& fe, int b) { fe.file_base = b; } bool get_pad_file(file_entry const& fe) { return fe.pad_file; } bool get_executable_attribute(file_entry const& fe) { return fe.executable_attribute; } bool get_hidden_attribute(file_entry const& fe) { return fe.hidden_attribute; } bool get_symlink_attribute(file_entry const& fe) { return fe.symlink_attribute; } #endif } // namespace unnamed boost::shared_ptr buffer_constructor0(char const* buf, int len, int flags) { error_code ec; boost::shared_ptr ret(boost::make_shared(buf , len, boost::ref(ec), flags)); #ifndef BOOST_NO_EXCEPTIONS if (ec) throw libtorrent_exception(ec); #endif return ret; } boost::shared_ptr buffer_constructor1(char const* buf, int len) { return buffer_constructor0(buf, len, 0); } boost::shared_ptr file_constructor0(std::string const& filename, int flags) { error_code ec; boost::shared_ptr ret(boost::make_shared(filename , boost::ref(ec), flags)); #ifndef BOOST_NO_EXCEPTIONS if (ec) throw libtorrent_exception(ec); #endif return ret; } boost::shared_ptr file_constructor1(std::string const& filename) { return file_constructor0(filename, 0); } boost::shared_ptr bencoded_constructor0(entry const& ent, int flags) { std::vector buf; bencode(std::back_inserter(buf), ent); bdecode_node e; error_code ec; if (buf.size() == 0 || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) { #ifndef BOOST_NO_EXCEPTIONS throw invalid_torrent_file(ec); #endif } boost::shared_ptr ret(boost::make_shared(e , boost::ref(ec), flags)); #ifndef BOOST_NO_EXCEPTIONS if (ec) throw libtorrent_exception(ec); #endif return ret; } boost::shared_ptr bencoded_constructor1(entry const& ent) { return bencoded_constructor0(ent, 0); } void bind_torrent_info() { return_value_policy copy; void (torrent_info::*rename_file0)(int, std::string const&) = &torrent_info::rename_file; #if TORRENT_USE_WSTRING && !defined TORRENT_NO_DEPRECATE void (torrent_info::*rename_file1)(int, std::wstring const&) = &torrent_info::rename_file; #endif class_("file_slice") .def_readwrite("file_index", &file_slice::file_index) .def_readwrite("offset", &file_slice::offset) .def_readwrite("size", &file_slice::size) ; class_ >("torrent_info", no_init) .def(init((arg("info_hash"), arg("flags") = 0))) .def("__init__", make_constructor(&bencoded_constructor0)) .def("__init__", make_constructor(&bencoded_constructor1)) .def("__init__", make_constructor(&buffer_constructor0)) .def("__init__", make_constructor(&buffer_constructor1)) .def("__init__", make_constructor(&file_constructor0)) .def("__init__", make_constructor(&file_constructor1)) .def(init((arg("ti")))) #if TORRENT_USE_WSTRING && !defined TORRENT_NO_DEPRECATE .def(init((arg("file"), arg("flags") = 0))) #endif .def("add_tracker", &torrent_info::add_tracker, arg("url")) .def("add_url_seed", &torrent_info::add_url_seed) .def("add_http_seed", &torrent_info::add_http_seed) .def("web_seeds", get_web_seeds) .def("set_web_seeds", set_web_seeds) .def("name", &torrent_info::name, copy) .def("comment", &torrent_info::comment, copy) .def("creator", &torrent_info::creator, copy) .def("total_size", &torrent_info::total_size) .def("piece_length", &torrent_info::piece_length) .def("num_pieces", &torrent_info::num_pieces) .def("info_hash", &torrent_info::info_hash, copy) .def("hash_for_piece", &hash_for_piece) .def("merkle_tree", get_merkle_tree) .def("set_merkle_tree", set_merkle_tree) .def("piece_size", &torrent_info::piece_size) .def("similar_torrents", &torrent_info::similar_torrents) .def("collections", &torrent_info::collections) .def("ssl_cert", &torrent_info::ssl_cert) .def("num_files", &torrent_info::num_files) .def("rename_file", rename_file0) .def("remap_files", &torrent_info::remap_files) .def("files", &torrent_info::files, return_internal_reference<>()) .def("orig_files", &torrent_info::orig_files, return_internal_reference<>()) #ifndef TORRENT_NO_DEPRECATE .def("file_at", &torrent_info::file_at) .def("file_at_offset", &torrent_info::file_at_offset) #if TORRENT_USE_WSTRING .def("rename_file", rename_file1) #endif // TORRENT_USE_WSTRING #endif // TORRENT_NO_DEPRECATE .def("is_valid", &torrent_info::is_valid) .def("priv", &torrent_info::priv) .def("is_i2p", &torrent_info::is_i2p) .def("is_merkle_torrent", &torrent_info::is_merkle_torrent) .def("trackers", range(begin_trackers, end_trackers)) .def("creation_date", &torrent_info::creation_date) .def("add_node", &add_node) .def("nodes", &nodes) .def("metadata", &metadata) .def("metadata_size", &torrent_info::metadata_size) .def("map_block", map_block) .def("map_file", &torrent_info::map_file) ; #ifndef TORRENT_NO_DEPRECATE class_("file_entry") .def_readwrite("path", &file_entry::path) .def_readwrite("symlink_path", &file_entry::symlink_path) .def_readwrite("filehash", &file_entry::filehash) .def_readwrite("mtime", &file_entry::mtime) .add_property("pad_file", &get_pad_file) .add_property("executable_attribute", &get_executable_attribute) .add_property("hidden_attribute", &get_hidden_attribute) .add_property("symlink_attribute", &get_symlink_attribute) .add_property("offset", &get_offset) .add_property("size", &get_size) .add_property("file_base", &get_file_base, &set_file_base) ; #endif class_("announce_entry", init()) .def_readwrite("url", &announce_entry::url) .def_readonly("trackerid", &announce_entry::trackerid) .def_readonly("message", &announce_entry::message) .def_readonly("last_error", &announce_entry::last_error) .add_property("next_announce", &get_next_announce) .add_property("min_announce", &get_min_announce) .def_readonly("scrape_incomplete", &announce_entry::scrape_incomplete) .def_readonly("scrape_complete", &announce_entry::scrape_complete) .def_readonly("scrape_downloaded", &announce_entry::scrape_downloaded) .def_readwrite("tier", &announce_entry::tier) .def_readwrite("fail_limit", &announce_entry::fail_limit) .add_property("fails", &get_fails) .add_property("source", &get_source) .add_property("verified", &get_verified) .add_property("updating", &get_updating) .add_property("start_sent", &get_start_sent) .add_property("complete_sent", &get_complete_sent) .add_property("send_stats", &get_send_stats) .def("next_announce_in", &announce_entry::next_announce_in) .def("min_announce_in", &announce_entry::min_announce_in) .def("reset", &announce_entry::reset) .def("can_announce", can_announce) .def("is_working", &announce_entry::is_working) .def("trim", &announce_entry::trim) ; enum_("tracker_source") .value("source_torrent", announce_entry::source_torrent) .value("source_client", announce_entry::source_client) .value("source_magnet_link", announce_entry::source_magnet_link) .value("source_tex", announce_entry::source_tex) ; #if BOOST_VERSION > 104200 implicitly_convertible, boost::shared_ptr >(); boost::python::register_ptr_to_python >(); #endif } libtorrent-rasterbar-1.1.13/bindings/python/src/torrent_status.cpp000066400000000000000000000171011351156116000254540ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include "boost_python.hpp" #include #include #include using namespace boost::python; using namespace libtorrent; object bitfield_to_list(bitfield const& bf) { list ret; for (bitfield::const_iterator i(bf.begin()), e(bf.end()); i != e; ++i) ret.append(*i); return ret; } object pieces(torrent_status const& s) { return bitfield_to_list(s.pieces); } object verified_pieces(torrent_status const& s) { return bitfield_to_list(s.verified_pieces); } boost::shared_ptr get_torrent_file(torrent_status const& st) { return st.torrent_file.lock(); } void bind_torrent_status() { scope status = class_("torrent_status") .def(self == self) .def_readonly("handle", &torrent_status::handle) .def_readonly("info_hash", &torrent_status::info_hash) .add_property("torrent_file", &get_torrent_file) .def_readonly("state", &torrent_status::state) .def_readonly("paused", &torrent_status::paused) .def_readonly("stop_when_ready", &torrent_status::stop_when_ready) .def_readonly("auto_managed", &torrent_status::auto_managed) .def_readonly("sequential_download", &torrent_status::sequential_download) .def_readonly("is_seeding", &torrent_status::is_seeding) .def_readonly("is_finished", &torrent_status::is_finished) .def_readonly("has_metadata", &torrent_status::has_metadata) .def_readonly("progress", &torrent_status::progress) .def_readonly("progress_ppm", &torrent_status::progress_ppm) .add_property( "next_announce" , make_getter( &torrent_status::next_announce, return_value_policy() ) ) #ifndef TORRENT_NO_DEPRECATE .add_property( "announce_interval" , make_getter( &torrent_status::announce_interval, return_value_policy() ) ) #endif .def_readonly("current_tracker", &torrent_status::current_tracker) .def_readonly("total_download", &torrent_status::total_download) .def_readonly("total_upload", &torrent_status::total_upload) .def_readonly("total_payload_download", &torrent_status::total_payload_download) .def_readonly("total_payload_upload", &torrent_status::total_payload_upload) .def_readonly("total_failed_bytes", &torrent_status::total_failed_bytes) .def_readonly("total_redundant_bytes", &torrent_status::total_redundant_bytes) .def_readonly("download_rate", &torrent_status::download_rate) .def_readonly("upload_rate", &torrent_status::upload_rate) .def_readonly("download_payload_rate", &torrent_status::download_payload_rate) .def_readonly("upload_payload_rate", &torrent_status::upload_payload_rate) .def_readonly("num_seeds", &torrent_status::num_seeds) .def_readonly("num_peers", &torrent_status::num_peers) .def_readonly("num_complete", &torrent_status::num_complete) .def_readonly("num_incomplete", &torrent_status::num_incomplete) .def_readonly("list_seeds", &torrent_status::list_seeds) .def_readonly("list_peers", &torrent_status::list_peers) .def_readonly("connect_candidates", &torrent_status::connect_candidates) .add_property("pieces", &pieces) .add_property("verified_pieces", &verified_pieces) .def_readonly("num_pieces", &torrent_status::num_pieces) .def_readonly("total_done", &torrent_status::total_done) .def_readonly("total_wanted_done", &torrent_status::total_wanted_done) .def_readonly("total_wanted", &torrent_status::total_wanted) .def_readonly("distributed_full_copies", &torrent_status::distributed_full_copies) .def_readonly("distributed_fraction", &torrent_status::distributed_fraction) .def_readonly("distributed_copies", &torrent_status::distributed_copies) .def_readonly("block_size", &torrent_status::block_size) .def_readonly("num_uploads", &torrent_status::num_uploads) .def_readonly("num_connections", &torrent_status::num_connections) .def_readonly("uploads_limit", &torrent_status::uploads_limit) .def_readonly("connections_limit", &torrent_status::connections_limit) .def_readonly("storage_mode", &torrent_status::storage_mode) .def_readonly("up_bandwidth_queue", &torrent_status::up_bandwidth_queue) .def_readonly("down_bandwidth_queue", &torrent_status::down_bandwidth_queue) .def_readonly("all_time_upload", &torrent_status::all_time_upload) .def_readonly("all_time_download", &torrent_status::all_time_download) .def_readonly("active_time", &torrent_status::active_time) .def_readonly("finished_time", &torrent_status::finished_time) .def_readonly("seeding_time", &torrent_status::seeding_time) .def_readonly("seed_rank", &torrent_status::seed_rank) .def_readonly("last_scrape", &torrent_status::last_scrape) .def_readonly("has_incoming", &torrent_status::has_incoming) .def_readonly("seed_mode", &torrent_status::seed_mode) .def_readonly("upload_mode", &torrent_status::upload_mode) .def_readonly("share_mode", &torrent_status::share_mode) .def_readonly("super_seeding", &torrent_status::super_seeding) #ifndef TORRENT_NO_DEPRECATE .def_readonly("error", &torrent_status::error) #endif .def_readonly("errc", &torrent_status::errc) .def_readonly("error_file", &torrent_status::error_file) .def_readonly("name", &torrent_status::name) .def_readonly("save_path", &torrent_status::save_path) .def_readonly("priority", &torrent_status::priority) .def_readonly("added_time", &torrent_status::added_time) .def_readonly("completed_time", &torrent_status::completed_time) .def_readonly("last_seen_complete", &torrent_status::last_seen_complete) .def_readonly("time_since_upload", &torrent_status::time_since_upload) .def_readonly("time_since_download", &torrent_status::time_since_download) .def_readonly("queue_position", &torrent_status::queue_position) .def_readonly("need_save_resume", &torrent_status::need_save_resume) .def_readonly("ip_filter_applies", &torrent_status::ip_filter_applies) .def_readonly("moving_storage", &torrent_status::moving_storage) .def_readonly("is_loaded", &torrent_status::is_loaded) .def_readonly("announcing_to_trackers", &torrent_status::announcing_to_trackers) .def_readonly("announcing_to_lsd", &torrent_status::announcing_to_lsd) .def_readonly("announcing_to_dht", &torrent_status::announcing_to_dht) .def_readonly("info_hash", &torrent_status::info_hash) ; enum_("states") #ifndef TORRENT_NO_DEPRECATE .value("queued_for_checking", torrent_status::queued_for_checking) #endif .value("checking_files", torrent_status::checking_files) .value("downloading_metadata", torrent_status::downloading_metadata) .value("downloading", torrent_status::downloading) .value("finished", torrent_status::finished) .value("seeding", torrent_status::seeding) .value("allocating", torrent_status::allocating) .value("checking_resume_data", torrent_status::checking_resume_data) .export_values() ; } libtorrent-rasterbar-1.1.13/bindings/python/src/utility.cpp000066400000000000000000000050111351156116000240540ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include #include #include "boost_python.hpp" #include "bytes.hpp" using namespace boost::python; using namespace libtorrent; struct bytes_to_python { static PyObject* convert(bytes const& p) { #if PY_MAJOR_VERSION >= 3 PyObject *ret = PyBytes_FromStringAndSize(p.arr.c_str(), p.arr.size()); #else PyObject *ret = PyString_FromStringAndSize(p.arr.c_str(), p.arr.size()); #endif return ret; } }; struct bytes_from_python { bytes_from_python() { converter::registry::push_back( &convertible, &construct, type_id()); } static void* convertible(PyObject* x) { #if PY_MAJOR_VERSION >= 3 return PyBytes_Check(x) ? x : NULL; #else return PyString_Check(x) ? x : NULL; #endif } static void construct(PyObject* x, converter::rvalue_from_python_stage1_data* data) { #if PY_MAJOR_VERSION >= 3 void* storage = ((converter::rvalue_from_python_storage*)data)->storage.bytes; bytes* ret = new (storage) bytes(); ret->arr.resize(PyBytes_Size(x)); memcpy(&ret->arr[0], PyBytes_AsString(x), ret->arr.size()); data->convertible = storage; #else void* storage = ((converter::rvalue_from_python_storage*)data)->storage.bytes; bytes* ret = new (storage) bytes(); ret->arr.resize(PyString_Size(x)); memcpy(&ret->arr[0], PyString_AsString(x), ret->arr.size()); data->convertible = storage; #endif } }; #ifndef TORRENT_NO_DEPRECATE object client_fingerprint_(peer_id const& id) { boost::optional result = client_fingerprint(id); return result ? object(*result) : object(); } #endif entry bdecode_(bytes const& data) { return bdecode(data.arr.begin(), data.arr.end()); } bytes bencode_(entry const& e) { bytes result; bencode(std::back_inserter(result.arr), e); return result; } void bind_utility() { // TODO: it would be nice to install converters for sha1_hash as well to_python_converter(); bytes_from_python(); #ifndef TORRENT_NO_DEPRECATE def("identify_client", &libtorrent::identify_client); def("client_fingerprint", &client_fingerprint_); #endif def("bdecode", &bdecode_); def("bencode", &bencode_); } libtorrent-rasterbar-1.1.13/bindings/python/src/version.cpp000066400000000000000000000011551351156116000240430ustar00rootroot00000000000000// Copyright Daniel Wallin 2006. 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) #include #include "boost_python.hpp" using namespace boost::python; using libtorrent::version; void bind_version() { scope().attr("__version__") = version(); #ifndef TORRENT_NO_DEPRECATE scope().attr("version") = LIBTORRENT_VERSION; scope().attr("version_major") = LIBTORRENT_VERSION_MAJOR; scope().attr("version_minor") = LIBTORRENT_VERSION_MINOR; #endif } libtorrent-rasterbar-1.1.13/build-aux/000077500000000000000000000000001351156116000176355ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/build-aux/compile000077500000000000000000000162451351156116000212230ustar00rootroot00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2012-10-14.11; # UTC # Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey . # # 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, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: libtorrent-rasterbar-1.1.13/build-aux/config.guess000077500000000000000000001263731351156116000221710ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-02-24' # This file 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 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > "$dummy.c" ; for c in cc gcc c89 c99 ; do if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "$UNAME_SYSTEM" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval "$set_cc_for_build" cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" # If ldd exists, use it to detect musl libc. if command -v ldd >/dev/null && \ ldd --version 2>&1 | grep -q ^musl then LIBC=musl fi ;; esac # Note: order is significant - the case branches are not exclusive. case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ "/sbin/$sysctl" 2>/dev/null || \ "/usr/sbin/$sysctl" 2>/dev/null || \ echo unknown)` case "$UNAME_MACHINE_ARCH" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine="${arch}${endian}"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case "$UNAME_MACHINE_ARCH" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval "$set_cc_for_build" if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "$UNAME_MACHINE_ARCH" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "$UNAME_VERSION" in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "$machine-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" exit ;; *:MidnightBSD:*:*) echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:Sortix:*:*) echo "$UNAME_MACHINE"-unknown-sortix exit ;; *:Redox:*:*) echo "$UNAME_MACHINE"-unknown-redox exit ;; mips:OSF1:*.*) echo mips-dec-osf1 exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix"$UNAME_RELEASE" exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval "$set_cc_for_build" SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint"$UNAME_RELEASE" exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint"$UNAME_RELEASE" exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint"$UNAME_RELEASE" exit ;; m68k:machten:*:*) echo m68k-apple-machten"$UNAME_RELEASE" exit ;; powerpc:machten:*:*) echo powerpc-apple-machten"$UNAME_RELEASE" exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] then if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ [ "$TARGET_BINARY_INTERFACE"x = x ] then echo m88k-dg-dgux"$UNAME_RELEASE" else echo m88k-dg-dguxbcs"$UNAME_RELEASE" fi else echo i586-dg-dgux"$UNAME_RELEASE" fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` case "$UNAME_MACHINE" in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "$sc_cpu_version" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "$sc_kernel_bits" in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if [ "$HP_ARCH" = "" ]; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ "$HP_ARCH" = hppa2.0w ] then eval "$set_cc_for_build" # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo "$UNAME_MACHINE"-unknown-osf1mk else echo "$UNAME_MACHINE"-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi"$UNAME_RELEASE" exit ;; *:BSD/OS:*:*) echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case "$UNAME_PROCESSOR" in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; i*:CYGWIN*:*) echo "$UNAME_MACHINE"-pc-cygwin exit ;; *:MINGW64*:*) echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) echo "$UNAME_MACHINE"-pc-mingw32 exit ;; *:MSYS*:*) echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) case "$UNAME_MACHINE" in x86) echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; i*:UWIN*:*) echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; *:GNU:*:*) # the GNU system echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" exit ;; i*86:Minix:*:*) echo "$UNAME_MACHINE"-pc-minix exit ;; aarch64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) eval "$set_cc_for_build" if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi else echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; e2k:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; k1om:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } ;; mips64el:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-"$LIBC" exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; padre:Linux:*:*) echo sparc-unknown-linux-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-"$LIBC" exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-"$LIBC" exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-"$LIBC" exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-"$LIBC" exit ;; riscv32:Linux:*:* | riscv64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" exit ;; sh64*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) if objdump -f /bin/sh | grep -q elf32-x86-64; then echo "$UNAME_MACHINE"-pc-linux-"$LIBC"x32 else echo "$UNAME_MACHINE"-pc-linux-"$LIBC" fi exit ;; xtensa*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo "$UNAME_MACHINE"-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux"$UNAME_RELEASE" exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv"$UNAME_RELEASE" else echo mips-unknown-sysv"$UNAME_RELEASE" fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux"$UNAME_RELEASE" exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux"$UNAME_RELEASE" exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Rhapsody:*:*) echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval "$set_cc_for_build" if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-*:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk"$UNAME_RELEASE" exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk"$UNAME_RELEASE" exit ;; NSR-*:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk"$UNAME_RELEASE" exit ;; NSV-*:NONSTOP_KERNEL:*:*) echo nsv-tandem-nsk"$UNAME_RELEASE" exit ;; NSX-*:NONSTOP_KERNEL:*:*) echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" exit ;; i*86:rdos:*:*) echo "$UNAME_MACHINE"-pc-rdos exit ;; i*86:AROS:*:*) echo "$UNAME_MACHINE"-pc-aros exit ;; x86_64:VMkernel:*:*) echo "$UNAME_MACHINE"-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; esac echo "$0: unable to guess system type" >&2 case "$UNAME_MACHINE:$UNAME_SYSTEM" in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF exit 1 # Local variables: # eval: (add-hook 'write-file-functions 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: libtorrent-rasterbar-1.1.13/build-aux/config.rpath000066400000000000000000000351041351156116000221450ustar00rootroot00000000000000#! /bin/sh # Output a system dependent set of variables, describing how to set the # run time search path of shared libraries in an executable. # # Copyright 1996-2003 Free Software Foundation, Inc. # Taken from GNU libtool, 2001 # Originally by Gordon Matzigkeit , 1996 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # # The first argument passed to this file is the canonical host specification, # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld # should be set by the caller. # # The set of defined variables is at the end of this script. # Known limitations: # - On IRIX 6.5 with CC="cc", the run time search patch must not be longer # than 256 bytes, otherwise the compiler driver will dump core. The only # known workaround is to choose shorter directory names for the build # directory and/or the installation directory. # All known linkers require a `.a' archive for static linking (except M$VC, # which needs '.lib'). libext=a shrext=.so host="$1" host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` # Code taken from libtool.m4's AC_LIBTOOL_PROG_COMPILER_PIC. wl= if test "$GCC" = yes; then wl='-Wl,' else case "$host_os" in aix*) wl='-Wl,' ;; mingw* | pw32* | os2*) ;; hpux9* | hpux10* | hpux11*) wl='-Wl,' ;; irix5* | irix6* | nonstopux*) wl='-Wl,' ;; newsos6) ;; linux*) case $CC in icc|ecc) wl='-Wl,' ;; ccc) wl='-Wl,' ;; esac ;; osf3* | osf4* | osf5*) wl='-Wl,' ;; sco3.2v5*) ;; solaris*) wl='-Wl,' ;; sunos4*) wl='-Qoption ld ' ;; sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) wl='-Wl,' ;; sysv4*MP*) ;; uts4*) ;; esac fi # Code taken from libtool.m4's AC_LIBTOOL_PROG_LD_SHLIBS. hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_direct=no hardcode_minus_L=no case "$host_os" in cygwin* | mingw* | pw32*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; openbsd*) with_gnu_ld=no ;; esac ld_shlibs=yes if test "$with_gnu_ld" = yes; then case "$host_os" in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no fi ;; amigaos*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes # Samuel A. Falvo II reports # that the semantics of dynamic libraries on AmigaOS, at least up # to version 4, is to share data among multiple programs linked # with the same dynamic library. Since this doesn't match the # behavior of shared libraries on other platforms, we can use # them. ld_shlibs=no ;; beos*) if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; cygwin* | mingw* | pw32*) # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then : else ld_shlibs=no fi ;; netbsd*) ;; solaris* | sysv5*) if $LD -v 2>&1 | egrep 'BFD 2\.8' > /dev/null; then ld_shlibs=no elif $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; sunos4*) hardcode_direct=yes ;; *) if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = yes; then # Unlike libtool, we use -rpath here, not --rpath, since the documented # option of GNU ld is called -rpath, not --rpath. hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' fi else case "$host_os" in aix3*) # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done esac fi hardcode_direct=yes hardcode_libdir_separator=':' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && \ strings "$collect2name" | grep resolve_lib_name >/dev/null then # We have reworked collect2 hardcode_direct=yes else # We have old collect2 hardcode_direct=unsupported hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi esac fi # Begin _LT_AC_SYS_LIBPATH_AIX. echo 'int main () { return 0; }' > conftest.c ${CC} ${LDFLAGS} conftest.c -o conftest aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` fi if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib" fi rm -f conftest.c conftest # End _LT_AC_SYS_LIBPATH_AIX. if test "$aix_use_runtimelinking" = yes; then hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' else hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" fi fi ;; amigaos*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes # see comment about different semantics on the GNU ld section ld_shlibs=no ;; bsdi4*) ;; cygwin* | mingw* | pw32*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec=' ' libext=lib ;; darwin* | rhapsody*) if $CC -v 2>&1 | grep 'Apple' >/dev/null ; then hardcode_direct=no fi ;; dgux*) hardcode_libdir_flag_spec='-L$libdir' ;; freebsd2.2*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; freebsd2*) hardcode_direct=yes hardcode_minus_L=yes ;; freebsd*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; hpux9*) hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; hpux10* | hpux11*) if test "$with_gnu_ld" = no; then case "$host_cpu" in hppa*64*) hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=no ;; ia64*) hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=no # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; *) hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; netbsd*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; newsos6) hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; openbsd*) hardcode_direct=yes if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then hardcode_libdir_flag_spec='${wl}-rpath,$libdir' else case "$host_os" in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) hardcode_libdir_flag_spec='-R$libdir' ;; *) hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; osf3*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) if test "$GCC" = yes; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else # Both cc and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi hardcode_libdir_separator=: ;; sco3.2v5*) ;; solaris*) hardcode_libdir_flag_spec='-R$libdir' ;; sunos4*) hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes ;; sysv4) case $host_vendor in sni) hardcode_direct=yes # is this really true??? ;; siemens) hardcode_direct=no ;; motorola) hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac ;; sysv4.3*) ;; sysv4*MP*) if test -d /usr/nec; then ld_shlibs=yes fi ;; sysv4.2uw2*) hardcode_direct=yes hardcode_minus_L=no ;; sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[78]* | unixware7*) ;; sysv5*) hardcode_libdir_flag_spec= ;; uts4*) hardcode_libdir_flag_spec='-L$libdir' ;; *) ld_shlibs=no ;; esac fi # Check dynamic linker characteristics # Code taken from libtool.m4's AC_LIBTOOL_SYS_DYNAMIC_LINKER. libname_spec='lib$name' case "$host_os" in aix3*) ;; aix[4-9]*) ;; amigaos*) ;; beos*) ;; bsdi4*) ;; cygwin* | mingw* | pw32*) shrext=.dll ;; darwin* | rhapsody*) shrext=.dylib ;; dgux*) ;; freebsd*) ;; gnu*) ;; hpux9* | hpux10* | hpux11*) case "$host_cpu" in ia64*) shrext=.so ;; hppa*64*) shrext=.sl ;; *) shrext=.sl ;; esac ;; irix5* | irix6* | nonstopux*) case "$host_os" in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; *) libsuff= shlibsuff= ;; esac ;; esac ;; linux*oldld* | linux*aout* | linux*coff*) ;; linux*) ;; netbsd*) ;; newsos6) ;; nto-qnx) ;; openbsd*) ;; os2*) libname_spec='$name' shrext=.dll ;; osf3* | osf4* | osf5*) ;; sco3.2v5*) ;; solaris*) ;; sunos4*) ;; sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) ;; sysv4*MP*) ;; uts4*) ;; esac sed_quote_subst='s/\(["`$\\]\)/\\\1/g' escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` shlibext=`echo "$shrext" | sed -e 's,^\.,,'` escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo "$1" | sed 's/-[^-]*$//'` if [ "$basic_machine" != "$1" ] then os=`echo "$1" | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | wasm32 \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pru-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-pc os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2*) basic_machine=m68k-bull os=-sysv3 ;; e500v[12]) basic_machine=powerpc-unknown os=$os"spe" ;; e500v[12]-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=$os"spe" ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; nsv-tandem) basic_machine=nsv-tandem ;; nsx-tandem) basic_machine=nsx-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh5el) basic_machine=sh5le-unknown ;; simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; x64) basic_machine=x86_64-pc ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases that might get confused # with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # es1800 is here to avoid being matched by es* (a different OS) -es1800*) os=-ose ;; # Now accept the basic system types. # The portable systems comes first. # Each alternative MUST end in a * to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \ | -midnightbsd*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -xray | -os68k* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo "$os" | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4*) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -pikeos*) # Until real need of OS specific support for # particular features comes up, bare metal # configurations are quite functional. case $basic_machine in arm*) os=-eabi ;; *) os=-elf ;; esac ;; -nacl*) ;; -ios) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; pru-*) os=-elf ;; *-be) os=-beos ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` ;; esac echo "$basic_machine$os" exit # Local variables: # eval: (add-hook 'write-file-functions 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: libtorrent-rasterbar-1.1.13/build-aux/depcomp000077500000000000000000000560171351156116000212230ustar00rootroot00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2016-01-11.22; # UTC # Copyright (C) 1999-2017 Free Software Foundation, Inc. # 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, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: libtorrent-rasterbar-1.1.13/build-aux/install-sh000077500000000000000000000354631351156116000216540ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2014-09-12.12; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) # $RANDOM is not portable (e.g. dash); use it when possible to # lower collision chance tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 # As "mkdir -p" follows symlinks and we work in /tmp possibly; so # create the $tmpdir first (and fail if unsuccessful) to make sure # that nobody tries to guess the $tmpdir name. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: libtorrent-rasterbar-1.1.13/build-aux/ltmain.sh000066400000000000000000011714741351156116000214740ustar00rootroot00000000000000#! /bin/sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2014-01-03.01 # libtool (GNU libtool) 2.4.6 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool 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, see . PROGRAM=libtool PACKAGE=libtool VERSION="2.4.6 Debian-2.4.6-2" package_revision=2.4.6 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2015-01-20.17; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # Copyright (C) 2004-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 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 3 of the License, or # (at your option) any later version. # As a special exception to the GNU General Public License, if you distribute # this file as part of a program or library that is built using GNU Libtool, # you may include this file under the same distribution terms that you use # for the rest of that program. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNES 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, see . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some retarded systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # We should try to minimise forks, especially on Windows where they are # unreasonably slow, so skip the feature probes when bash or zsh are # being used: if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then : ${_G_HAVE_ARITH_OP="yes"} : ${_G_HAVE_XSI_OPS="yes"} # The += operator was introduced in bash 3.1 case $BASH_VERSION in [12].* | 3.0 | 3.0*) ;; *) : ${_G_HAVE_PLUSEQ_OP="yes"} ;; esac fi # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # useable or anything else if it does not work. test -z "$_G_HAVE_PLUSEQ_OP" \ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ && _G_HAVE_PLUSEQ_OP=yes if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1+=\\ \$func_quote_for_eval_result" }' else func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1=\$$1\\ \$func_quote_for_eval_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_for_eval ARG... # -------------------------- # Aesthetically quote ARGs to be evaled later. # This function returns two values: # i) func_quote_for_eval_result # double-quoted, suitable for a subsequent eval # ii) func_quote_for_eval_unquoted_result # has all characters that are still active within double # quotes backslashified. func_quote_for_eval () { $debug_cmd func_quote_for_eval_unquoted_result= func_quote_for_eval_result= while test 0 -lt $#; do case $1 in *[\\\`\"\$]*) _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; *) _G_unquoted_arg=$1 ;; esac if test -n "$func_quote_for_eval_unquoted_result"; then func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" else func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" fi case $_G_unquoted_arg in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_quoted_arg=\"$_G_unquoted_arg\" ;; *) _G_quoted_arg=$_G_unquoted_arg ;; esac if test -n "$func_quote_for_eval_result"; then func_append func_quote_for_eval_result " $_G_quoted_arg" else func_append func_quote_for_eval_result "$_G_quoted_arg" fi shift done } # func_quote_for_expand ARG # ------------------------- # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { $debug_cmd case $1 in *[\\\`\"]*) _G_arg=`$ECHO "$1" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; *) _G_arg=$1 ;; esac case $_G_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_arg=\"$_G_arg\" ;; esac func_quote_for_expand_result=$_G_arg } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_for_expand "$_G_cmd" eval "func_notquiet $func_quote_for_expand_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_for_expand "$_G_cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # Set a version string for this script. scriptversion=2014-01-07.03; # UTC # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # Copyright (C) 2010-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 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 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# warranty; '. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # to the main code. A hook is just a named list of of function, that can # be run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of functions called by FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It is assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook funcions.n" ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do eval $_G_hook '"$@"' # store returned options list back into positional # parameters for next 'cmd' execution. eval _G_hook_result=\$${_G_hook}_result eval set dummy "$_G_hook_result"; shift done func_quote_for_eval ${1+"$@"} func_run_hooks_result=$func_quote_for_eval_result } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list in your hook function, remove any # options that you action, and then pass back the remaining unprocessed # options in '_result', escaped suitably for # 'eval'. Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # func_quote_for_eval ${1+"$@"} # my_options_prep_result=$func_quote_for_eval_result # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # # Note that for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # ;; # *) set dummy "$_G_opt" "$*"; shift; break ;; # esac # done # # func_quote_for_eval ${1+"$@"} # my_silent_option_result=$func_quote_for_eval_result # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # # func_quote_for_eval ${1+"$@"} # my_option_validation_result=$func_quote_for_eval_result # } # func_add_hook func_validate_options my_option_validation # # You'll alse need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd func_options_prep ${1+"$@"} eval func_parse_options \ ${func_options_prep_result+"$func_options_prep_result"} eval func_validate_options \ ${func_parse_options_result+"$func_parse_options_result"} eval func_run_hooks func_options \ ${func_validate_options_result+"$func_validate_options_result"} # save modified positional parameters for caller func_options_result=$func_run_hooks_result } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propogate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before # returning. func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} # save modified positional parameters for caller func_options_prep_result=$func_run_hooks_result } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd func_parse_options_result= # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} # Adjust func_parse_options positional parameters to match eval set dummy "$func_run_hooks_result"; shift # Break out of the loop if we already parsed every option. test $# -gt 0 || break _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) test $# = 0 && func_missing_arg $_G_opt && break case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac done # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} func_parse_options_result=$func_quote_for_eval_result } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE # save modified positional parameters for caller func_validate_options_result=$func_run_hooks_result } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables after # splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} test "x$func_split_equals_lhs" = "x$1" \ && func_split_equals_rhs= }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /(C)/!b go :more /\./!{ N s|\n# | | b more } :go /^# Written by /,/# warranty; / { s|^# || s|^# *$|| s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| p } /^# Written by / { s|^# || p } /^warranty; /q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.4.6' # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { $debug_cmd $warning_func ${1+"$@"} } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information and exit --mode=MODE use operation mode MODE --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname $scriptversion Debian-2.4.6-2 automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func__fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_preserve_dup_deps=false opt_quiet=false nonopt= preserve_args= # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; esac # Pass back the list of options. func_quote_for_eval ${1+"$@"} libtool_options_prep_result=$func_quote_for_eval_result } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $_G_opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac done # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} libtool_parse_options_result=$func_quote_for_eval_result } func_add_hook func_parse_options libtool_parse_options # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" case $host in # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote_for_eval ${1+"$@"} libtool_validate_options_result=$func_quote_for_eval_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_for_eval "$libobj" test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" func_append install_prog "$func_quote_for_eval_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" func_append install_prog " $func_quote_for_eval_result" if test -n "$arg2"; then func_quote_for_eval "$arg2" fi func_append install_shared_prog " $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_for_eval "$install_override_mode" func_append install_shared_prog " -m $func_quote_for_eval_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=\"$qECHO\" fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module=$wl-multi_module continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "'-no-install' is ignored for $host" func_warning "assuming '-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -os2dllname) prev=os2dllname continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $func_quote_for_eval_result" func_append compiler_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $wl$func_quote_for_eval_result" func_append compiler_flags " $wl$func_quote_for_eval_result" func_append linker_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # -fstack-protector* stack protector flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang # -fsanitize=* Clang/GCC memory and address sanitizer -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ -specs=*|-fsanitize=*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; -Z*) if test os2 = "`expr $host : '.*\(os2\)'`"; then # OS/2 uses -Zxxx to specify OS/2-specific options compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case $arg in -Zlinker | -Zstack) prev=xcompiler ;; esac continue else # Otherwise treat like 'Some other compiler flag' below func_quote_for_eval "$arg" arg=$func_quote_for_eval_result fi ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result test none = "$pic_object" || { # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object } # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test dlfiles = "$prev"; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test dlprefiles = "$prev"; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the '$prevarg' option requires an argument" if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname=$func_basename_result libobjs_save=$libobjs if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" # Definition is injected by LT_CONFIG during libtool generation. func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" func_dirname "$output" "/" "" output_objdir=$func_dirname_result$objdir func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test lib = "$linkmode"; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=false newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test lib,link = "$linkmode,$pass"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs=$tmp_deplibs fi if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass"; then libs=$deplibs deplibs= fi if test prog = "$linkmode"; then case $pass in dlopen) libs=$dlfiles ;; dlpreopen) libs=$dlprefiles ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test lib,dlpreopen = "$linkmode,$pass"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs=$dlprefiles fi if test dlopen = "$pass"; then # Collect dlpreopened libraries save_deplibs=$deplibs deplibs= fi for deplib in $libs; do lib= found=false case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test lib != "$linkmode" && test prog != "$linkmode"; then func_warning "'-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test lib = "$linkmode"; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib=$searchdir/lib$name$search_ext if test -f "$lib"; then if test .la = "$search_ext"; then found=: else found=false fi break 2 fi done done if $found; then # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll=$l done if test "X$ll" = "X$old_library"; then # only static version available found=false func_dirname "$lib" "" "." ladir=$func_dirname_result lib=$ladir/$old_library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi else # deplib doesn't seem to be a libtool library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi ;; # -l *.ltframework) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test conv = "$pass" && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi if test scan = "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "'-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test link = "$pass"; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=false case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=: fi ;; pass_all) valid_a_lib=: ;; esac if $valid_a_lib; then echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" else echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." fi ;; esac continue ;; prog) if test link != "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test conv = "$pass"; then deplibs="$deplib $deplibs" elif test prog = "$linkmode"; then if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=: continue ;; esac # case $deplib $found || test -f "$lib" \ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "'$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir=$func_dirname_result dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass" || { test prog != "$linkmode" && test lib != "$linkmode"; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test conv = "$pass"; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for '$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done elif test prog != "$linkmode" && test lib != "$linkmode"; then func_fatal_error "'$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test yes = "$prefer_static_libs" || test built,no = "$prefer_static_libs,$installed"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib=$l done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for '$lib'" fi # This library was specified with -dlopen. if test dlopen = "$pass"; then test -z "$libdir" \ && func_fatal_error "cannot -dlopen a convenience library: '$lib'" if test -z "$dlname" || test yes != "$dlopen_support" || test no = "$build_libtool_libs" then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of '$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir=$ladir fi ;; esac func_basename "$lib" laname=$func_basename_result # Find the relevant object directory and library name. if test yes = "$installed"; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library '$lib' was moved." dir=$ladir absdir=$abs_ladir libdir=$abs_ladir else dir=$lt_sysroot$libdir absdir=$lt_sysroot$libdir fi test yes = "$hardcode_automatic" && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir=$ladir absdir=$abs_ladir # Remove this search path later func_append notinst_path " $abs_ladir" else dir=$ladir/$objdir absdir=$abs_ladir/$objdir # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test dlpreopen = "$pass"; then if test -z "$libdir" && test prog = "$linkmode"; then func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" fi case $host in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test lib = "$linkmode"; then deplibs="$dir/$old_library $deplibs" elif test prog,link = "$linkmode,$pass"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test prog = "$linkmode" && test link != "$pass"; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=false if test no != "$link_all_deplibs" || test -z "$library_names" || test no = "$build_libtool_libs"; then linkalldeplibs=: fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if $linkalldeplibs; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test prog,link = "$linkmode,$pass"; then if test -n "$library_names" && { { test no = "$prefer_static_libs" || test built,yes = "$prefer_static_libs,$installed"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then # Make sure the rpath contains only unique directories. case $temp_rpath: in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if $alldeplibs && { test pass_all = "$deplibs_check_method" || { test yes = "$build_libtool_libs" && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test built = "$use_static_libs" && test yes = "$installed"; then use_static_libs=no fi if test -n "$library_names" && { test no = "$use_static_libs" || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc* | *os2*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test no = "$installed"; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule= for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule=$dlpremoduletest break fi done if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then echo if test prog = "$linkmode"; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test lib = "$linkmode" && test yes = "$hardcode_into_libs"; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname=$1 shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname=$dlname elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc* | *os2*) func_arith $current - $age major=$func_arith_result versuffix=-$major ;; esac eval soname=\"$soname_spec\" else soname=$realname fi # Make a new name for the extract_expsyms_cmds to use soroot=$soname func_basename "$soroot" soname=$func_basename_result func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from '$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for '$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test prog = "$linkmode" || test relink != "$opt_mode"; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test no = "$hardcode_direct"; then add=$dir/$linklib case $host in *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; *-*-sysv4*uw2*) add_dir=-L$dir ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir=-L$dir ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we cannot # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library"; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add=$dir/$old_library fi elif test -n "$old_library"; then add=$dir/$old_library fi fi esac elif test no = "$hardcode_minus_L"; then case $host in *-*-sunos*) add_shlibpath=$dir ;; esac add_dir=-L$dir add=-l$name elif test no = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; relink) if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$dir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$absdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name elif test yes = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; *) lib_linked=no ;; esac if test yes != "$lib_linked"; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test prog = "$linkmode"; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test yes != "$hardcode_direct" && test yes != "$hardcode_minus_L" && test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test prog = "$linkmode" || test relink = "$opt_mode"; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$libdir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$libdir add=-l$name elif test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add=-l$name elif test yes = "$hardcode_automatic"; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib"; then add=$inst_prefix_dir$libdir/$linklib else add=$libdir/$linklib fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir=-L$libdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name fi if test prog = "$linkmode"; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test prog = "$linkmode"; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test unsupported != "$hardcode_direct"; then test -n "$old_library" && linklib=$old_library compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test yes = "$build_libtool_libs"; then # Not a shared library if test pass_all != "$deplibs_check_method"; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system cannot link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test yes = "$module"; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test lib = "$linkmode"; then if test -n "$dependency_libs" && { test yes != "$hardcode_into_libs" || test yes = "$build_old_libs" || test yes = "$link_static"; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs=$temp_deplibs fi func_append newlib_search_path " $absdir" # Link against this library test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test no != "$link_all_deplibs"; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path=$deplib ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of '$dir'" absdir=$dir fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names"; then for tmp in $deplibrary_names; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl"; then depdepl=$absdir/$objdir/$depdepl darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" path= fi fi ;; *) path=-L$absdir/$objdir ;; esac else eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "'$deplib' seems to be moved" path=-L$absdir fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test link = "$pass"; then if test prog = "$linkmode"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs=$newdependency_libs if test dlpreopen = "$pass"; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test dlopen != "$pass"; then test conv = "$pass" || { # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= } if test prog,link = "$linkmode,$pass"; then vars="compile_deplibs finalize_deplibs" else vars=deplibs fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Add Sun CC postdeps if required: test CXX = "$tagname" && { case $host_os in linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; solaris*) func_cc_basename "$CC" case $func_cc_basename_result in CC* | sunCC*) func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; esac } # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i= ;; esac if test -n "$i"; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test prog = "$linkmode"; then dlfiles=$newdlfiles fi if test prog = "$linkmode" || test lib = "$linkmode"; then dlprefiles=$newdlprefiles fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "'-R' is ignored for archives" test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "'-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "'-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs=$output func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form 'libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test no = "$module" \ && func_fatal_help "libtool library '$output' must begin with 'lib'" if test no != "$need_lib_prefix"; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test pass_all != "$deplibs_check_method"; then func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test no = "$dlself" \ || func_warning "'-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test 1 -lt "$#" \ && func_warning "ignoring multiple '-rpath's for a libtool library" install_libdir=$1 oldlibs= if test -z "$rpath"; then if test yes = "$build_libtool_libs"; then # Building a libtool convenience library. # Some compilers have problems with a '.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "'-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs=$IFS; IFS=: set dummy $vinfo 0 0 0 shift IFS=$save_ifs test -n "$7" && \ func_fatal_help "too many parameters to '-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major=$1 number_minor=$2 number_revision=$3 # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # that has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|freebsd-elf|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_revision ;; freebsd-aout|qnx|sunos) current=$number_major revision=$number_minor age=0 ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_minor lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type '$version_type'" ;; esac ;; no) current=$1 revision=$2 age=$3 ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT '$current' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION '$revision' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE '$age' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE '$age' is greater than the current interface number '$current'" func_fatal_error "'$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" # On Darwin other compilers case $CC in nagfor*) verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" ;; *) verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; esac ;; freebsd-aout) major=.$current versuffix=.$current.$revision ;; freebsd-elf) func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; irix | nonstopux) if test no = "$lt_irix_increment"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring=$verstring_prefix$major.$revision # Add in all the interfaces that we are compatible with. loop=$revision while test 0 -ne "$loop"; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring_prefix$major.$iface:$verstring done # Before this point, $major must not contain '.'. major=.$major versuffix=$major.$revision ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=.$current.$age.$revision verstring=$current.$age.$revision # Add in all the interfaces that we are compatible with. loop=$age while test 0 -ne "$loop"; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring:$iface.0 done # Make executables depend on our current version. func_append verstring ":$current.0" ;; qnx) major=.$current versuffix=.$current ;; sco) major=.$current versuffix=.$current ;; sunos) major=.$current versuffix=.$current.$revision ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 file systems. func_arith $current - $age major=$func_arith_result versuffix=-$major ;; *) func_fatal_configuration "unknown library version type '$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring=0.0 ;; esac if test no = "$need_version"; then versuffix= else versuffix=.0.0 fi fi # Remove version info from name if versioning should be avoided if test yes,no = "$avoid_version,$need_version"; then major= versuffix= verstring= fi # Check to see if the archive will have undefined symbols. if test yes = "$allow_undefined"; then if test unsupported = "$allow_undefined_flag"; then if test yes = "$build_old_libs"; then func_warning "undefined symbols not allowed in $host shared libraries; building static only" build_libtool_libs=no else func_fatal_error "can't build $host shared library unless -no-undefined is specified" fi fi else # Don't allow undefined symbols. allow_undefined_flag=$no_undefined_flag fi fi func_generate_dlsyms "$libname" "$libname" : func_append libobjs " $symfileobj" test " " = "$libobjs" && libobjs= if test relink != "$opt_mode"; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) if test -n "$precious_files_regex"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles=$dlfiles dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles=$dlprefiles dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test yes = "$build_libtool_libs"; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test yes = "$build_libtool_need_lc"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release= versuffix= major= newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib=$potent_lib while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | $SED 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib= ;; esac fi if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib=$potent_lib # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs= tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test yes = "$allow_libtool_libs_with_static_runtimes"; then for i in $predeps $postdeps; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test none = "$deplibs_check_method"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test yes = "$droppeddeps"; then if test yes = "$module"; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test no = "$allow_undefined"; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs=$new_libs # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test yes = "$build_libtool_libs"; then # Remove $wl instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test yes = "$hardcode_into_libs"; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath=$finalize_rpath test relink = "$opt_mode" || rpath=$compile_rpath$rpath for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath=$finalize_shlibpath test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname=$1 shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname=$realname fi if test -z "$dlname"; then dlname=$soname fi lib=$output_objdir/$realname linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols=$output_objdir/$libname.uexp func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile func_dll_def_p "$export_symbols" || { # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols=$export_symbols export_symbols= always_export_symbols=yes } fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs=$IFS; IFS='~' for cmd1 in $cmds; do IFS=$save_ifs # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test yes = "$try_normal_branch" \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=$output_objdir/$output_la.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS=$save_ifs if test -n "$export_symbols_regex" && test : != "$skipped_export"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test : != "$skipped_export" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs=$tmp_deplibs if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test yes = "$compiler_needs_object" && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test : != "$skipped_export" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then output=$output_objdir/$output_la.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then output=$output_objdir/$output_la.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test yes = "$compiler_needs_object"; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-$k.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test -z "$objlist" || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test 1 -eq "$k"; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-$k.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-$k.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi ${skipped_export-false} && { func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi } test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs=$IFS; IFS='~' for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi ${skipped_export-false} && { if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi } libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs=$IFS; IFS='~' for cmd in $cmds; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs # Restore the uninstalled library and exit if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test yes = "$module" || test yes = "$export_dynamic"; then # On all known operating systems, these are identical. dlname=$soname fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "'-R' is ignored for objects" test -n "$vinfo" && \ func_warning "'-version-info' is ignored for objects" test -n "$release" && \ func_warning "'-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object '$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj=$output ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # if reload_cmds runs $LD directly, get rid of -Wl from # whole_archive_flag_spec and hope we can get by with turning comma # into space. case $reload_cmds in *\$LD[\ \$]*) wl= ;; esac if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags else gentop=$output_objdir/${obj}x func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test yes = "$build_libtool_libs" || libobjs=$non_pic_objects # Create the old-style object. reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs output=$obj func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi test yes = "$build_libtool_libs" || { if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS } if test -n "$pic_flag" || test default != "$pic_mode"; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output=$libobj func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "'-version-info' is ignored for programs" test -n "$release" && \ func_warning "'-release' is ignored for programs" $preload \ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test CXX = "$tagname"; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " $wl-bind_at_load" func_append finalize_command " $wl-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs=$new_libs func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath=$rpath rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath=$rpath if test -n "$libobjs" && test yes = "$build_old_libs"; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" false # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=: case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=false ;; *cygwin* | *mingw* ) test yes = "$build_libtool_libs" || wrappers_required=false ;; *) if test no = "$need_relink" || test yes != "$build_libtool_libs"; then wrappers_required=false fi ;; esac $wrappers_required || { # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command=$compile_command$compile_rpath # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.$objext"; then func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' fi exit $exit_status } if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test yes = "$no_install"; then # We don't need to create a wrapper script. link_command=$compile_var$compile_command$compile_rpath # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi case $hardcode_action,$fast_install in relink,*) # Fast installation is not supported link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath func_warning "this platform does not like uninstalled shared libraries" func_warning "'$output' will be relinked during installation" ;; *,yes) link_command=$finalize_var$compile_command$finalize_rpath relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` ;; *,no) link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath ;; *,needless) link_command=$finalize_var$compile_command$finalize_rpath relink_command= ;; esac # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource=$output_path/$objdir/lt-$output_name.c cwrapper=$output_path/$output_name.exe $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host"; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do case $build_libtool_libs in convenience) oldobjs="$libobjs_save $symfileobj" addlibs=$convenience build_libtool_libs=no ;; module) oldobjs=$libobjs_save addlibs=$old_convenience build_libtool_libs=no ;; *) oldobjs="$old_deplibs $non_pic_objects" $preload && test -f "$symfileobj" \ && func_append oldobjs " $symfileobj" addlibs=$old_convenience ;; esac if test -n "$addlibs"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase=$func_basename_result case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj"; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test -z "$oldobjs"; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test yes = "$build_old_libs" && old_library=$libname.$libext func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` if test yes = "$hardcode_automatic"; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test yes = "$installed"; then if test -z "$install_libdir"; then break fi output=$output_objdir/${outputname}i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name=$func_basename_result func_resolve_sysroot "$deplib" eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs=$newdependency_libs newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles=$newdlprefiles else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles=$newdlprefiles fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test -n "$bindir"; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result/$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that cannot go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test no,yes = "$installed,$need_relink"; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } if test link = "$opt_mode" || test relink = "$opt_mode"; then func_mode_link ${1+"$@"} fi # func_mode_uninstall arg... func_mode_uninstall () { $debug_cmd RM=$nonopt files= rmforce=false exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic for arg do case $arg in -f) func_append RM " $arg"; rmforce=: ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir=$func_dirname_result if test . = "$dir"; then odir=$objdir else odir=$dir/$objdir fi func_basename "$file" name=$func_basename_result test uninstall = "$opt_mode" && odir=$dir # Remember odir for removal later, being careful to avoid duplicates if test clean = "$opt_mode"; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif $rmforce; then continue fi rmfiles=$file case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case $opt_mode in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test none != "$pic_object"; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test none != "$non_pic_object"; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test clean = "$opt_mode"; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.$objext" if test yes = "$fast_install" && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name"; then func_append rmfiles " $odir/lt-$noexename.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the $objdir's in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then func_mode_uninstall ${1+"$@"} fi test -z "$opt_mode" && { help=$generic_help func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode '$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # where we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: libtorrent-rasterbar-1.1.13/build-aux/missing000077500000000000000000000153301351156116000212360ustar00rootroot00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2013-10-28.13; # UTC # Copyright (C) 1996-2014 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # 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, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: libtorrent-rasterbar-1.1.13/build-aux/test-driver000077500000000000000000000110401351156116000220270ustar00rootroot00000000000000#! /bin/sh # test-driver - basic testsuite driver script. scriptversion=2013-07-13.22; # UTC # Copyright (C) 2011-2014 Free Software Foundation, Inc. # # 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, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <$log_file 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then tweaked_estatus=1 else tweaked_estatus=$estatus fi case $tweaked_estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report the test outcome and exit status in the logs, so that one can # know whether the test passed or failed simply by looking at the '.log' # file, without the need of also peaking into the corresponding '.trs' # file (automake bug#11814). echo "$res $test_name (exit status: $estatus)" >>$log_file # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: libtorrent-rasterbar-1.1.13/cmake/000077500000000000000000000000001351156116000170235ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/cmake/Modules/000077500000000000000000000000001351156116000204335ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/cmake/Modules/FindLibGcrypt.cmake000066400000000000000000000114201351156116000241330ustar00rootroot00000000000000#.rst: # FindLibGcrypt # ------------- # # Try to find libgcrypt. # # This will define the following variables: # # ``LibGcrypt_FOUND`` # True if libgcrypt is available. # # ``LibGcrypt_VERSION`` # The version of LibGcrypt # # ``LibGcrypt_INCLUDE_DIRS`` # This should be passed to target_include_directories() if # the target is not used for linking # # ``LibGcrypt_LIBRARIES`` # This can be passed to target_link_libraries() instead of # the ``LibGcrypt::LibGcrypt`` target # # If ``LibGcrypt_FOUND`` is TRUE, the following imported target # will be available: # # ``LibGcrypt::LibGcrypt`` # The libgcrypt library # # Since 1.9.50. #============================================================================= # Copyright 2007 Charles Connell (This was based upon FindKopete.cmake) # Copyright 2010 Joris Guisson # Copyright 2016 Christophe Giboudeaux # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. #============================================================================= find_path(LibGcrypt_INCLUDE_DIRS NAMES gcrypt.h PATH_SUFFIXES libgcrypt ) find_library(LibGcrypt_LIBRARIES NAMES gcrypt ) if(MSVC) find_library(LibGcrypt_LIBRARIES_DEBUG NAMES gcryptd ) if(NOT LibGcrypt_LIBRARIES_DEBUG) unset(LibGcrypt_LIBRARIES CACHE) endif() if(MSVC_IDE) if(NOT (LibGcrypt_LIBRARIES_DEBUG AND LibGcrypt_LIBRARIES)) message(STATUS "\nCould NOT find the debug AND release version of the libgcrypt library.\n You need to have both to use MSVC projects.\n Please build and install both libgcrypt libraries first.\n" ) unset(LibGcrypt_LIBRARIES CACHE) endif() else() string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER) if(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug) set(LibGcrypt_LIBRARIES ${LibGcrypt_LIBRARIES_DEBUG}) endif() endif() endif() # Get version from gcrypt.h # #define GCRYPT_VERSION "1.6.4" if(LibGcrypt_INCLUDE_DIRS AND LibGcrypt_LIBRARIES) file(STRINGS ${LibGcrypt_INCLUDE_DIRS}/gcrypt.h _GCRYPT_H REGEX "^#define GCRYPT_VERSION[ ]+.*$") string(REGEX REPLACE "^.*GCRYPT_VERSION[ ]+\"([0-9]+).([0-9]+).([0-9]+).*\".*$" "\\1" LibGcrypt_MAJOR_VERSION "${_GCRYPT_H}") string(REGEX REPLACE "^.*GCRYPT_VERSION[ ]+\"([0-9]+).([0-9]+).([0-9]+).*\".*$" "\\2" LibGcrypt_MINOR_VERSION "${_GCRYPT_H}") string(REGEX REPLACE "^.*GCRYPT_VERSION[ ]+\"([0-9]+).([0-9]+).([0-9]+).*\".*$" "\\3" LibGcrypt_PATCH_VERSION "${_GCRYPT_H}") set(LibGcrypt_VERSION "${LibGcrypt_MAJOR_VERSION}.${LibGcrypt_MINOR_VERSION}.${LibGcrypt_PATCH_VERSION}") unset(_GCRYPT_H) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LibGcrypt FOUND_VAR LibGcrypt_FOUND REQUIRED_VARS LibGcrypt_INCLUDE_DIRS LibGcrypt_LIBRARIES VERSION_VAR LibGcrypt_VERSION ) if(LibGcrypt_FOUND AND NOT TARGET LibGcrypt::LibGcrypt) add_library(LibGcrypt::LibGcrypt UNKNOWN IMPORTED) set_target_properties(LibGcrypt::LibGcrypt PROPERTIES IMPORTED_LOCATION "${LibGcrypt_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${LibGcrypt_INCLUDE_DIRS}") endif() mark_as_advanced(LibGcrypt_INCLUDE_DIRS LibGcrypt_LIBRARIES) include(FeatureSummary) set_package_properties(LibGcrypt PROPERTIES URL "http://directory.fsf.org/wiki/Libgcrypt" DESCRIPTION "General purpose crypto library based on the code used in GnuPG." ) libtorrent-rasterbar-1.1.13/cmake/Modules/GeneratePkgConfig.cmake000066400000000000000000000164121351156116000247630ustar00rootroot00000000000000# This module provides generate_and_install_pkg_config_file() function. # The function takes target name and expects a fully configured project, i.e. with set version and # description. The function extracts interface libraries, include dirs, definitions and options # from the target and generates pkg-config file with install() command # The function expands imported targets and generator expressions # save the current file dir for later use in the generate_and_install_pkg_config_file() function set(_GeneratePkGConfigDir "${CMAKE_CURRENT_LIST_DIR}/GeneratePkgConfig") include(GNUInstallDirs) function(_compile_features_to_gcc_flags _res _features) set(features ${_features}) # leave only cxx_std_nn items list(FILTER features INCLUDE REGEX cxx_std_) if (${features} STREQUAL "") set(${_res} "" PARENT_SCOPE) else() # if there are more than a single cxx_std_nn feature... list(SORT features) # take the most recent standard, i.e. the last element list(GET features -1 standard) # cmake calls it cxx_std_98, but we (obviously) want -std=c++03 string(REPLACE 98 03 standard "${standard}") string(REPLACE cxx_std_ -std=c++ standard "${standard}") set(${_res} "${standard}" PARENT_SCOPE) endif() endfunction() function(_get_target_property_merging_configs _var_name _target_name _propert_name) get_target_property(vals ${_target_name} ${_propert_name}) if (NOT vals) if (CMAKE_BUILD_TYPE) list(APPEND configs ${CMAKE_BUILD_TYPE}) elseif() list(APPEND configs ${CMAKE_CONFIGURATION_TYPES}) endif() foreach(cfg ${configs}) string(TOUPPER "${cfg}" UPPERCFG) get_target_property(mapped_configs ${_target_name} "MAP_IMPORTED_CONFIG_${UPPERCFG}") if (mapped_configs) list(GET "${mapped_configs}" 0 target_cfg) else() set(target_cfg "${UPPERCFG}") endif() get_target_property(val_for_cfg ${_target_name} "${_propert_name}_${target_cfg}") if (val_for_cfg) list(APPEND vals "$<$:${val_for_cfg}>") endif() endforeach() endif() # HACK for static libraries cmake populates link dependencies as $. # pkg-config does not support special handling for static libraries and as such we will remove # that generator expression string(REPLACE "$") configure_file("${_GeneratePkGConfigDir}/generate-pkg-config.cmake.in" "${_generate_target_dir}/${cfg}/generate-pkg-config.cmake" @ONLY) install(SCRIPT "${_generate_target_dir}/${cfg}/generate-pkg-config.cmake") endforeach() endif() endfunction() libtorrent-rasterbar-1.1.13/cmake/Modules/GeneratePkgConfig/000077500000000000000000000000001351156116000237555ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/cmake/Modules/GeneratePkgConfig/generate-pkg-config.cmake.in000066400000000000000000000045451351156116000312100ustar00rootroot00000000000000include(@_variables_file_name@) function (cmake_list_to_pkg_config _result _list _prefix) set(_tmp_list "${_list}") list(REMOVE_ITEM _tmp_list "") # remove prefix from prefixed items string(REGEX REPLACE "(^|;)(${_prefix})" "\\1" _tmp_list "${_tmp_list}") # append 'prefix' to each element string(REGEX REPLACE "([^;]+)" "${_prefix}\\1" _tmp_list "${_tmp_list}") # transform cmake list into a space delimited list string(REPLACE ";" " " _tmp_list "${_tmp_list}") set(${_result} "${_tmp_list}" PARENT_SCOPE) endfunction() # Helper function for splitting full library paths into [dir, name] and merging repetitive dir entries function(split_library_dirs _libraries _base_library_dir _library_dirs_var _library_names_var) set(libdirs "${_base_library_dir}") set(libs "") foreach (l IN LISTS _libraries) get_filename_component(lDir "${l}" DIRECTORY) if (lDir) get_filename_component(lDir "${lDir}" REALPATH) endif() get_filename_component(lFile "${l}" NAME_WE) string(REGEX REPLACE "${_SHARED_LIBRARY_PREFIX}" "" lFile "${lFile}") list(APPEND libdirs "${lDir}") list(APPEND libs "${lFile}") endforeach() list(REMOVE_DUPLICATES libdirs) list(REMOVE_AT libdirs 0) # as it is the base libdir and will be handled separately set(${_library_dirs_var} "${libdirs}" PARENT_SCOPE) set(${_library_names_var} "${libs}" PARENT_SCOPE) endfunction() split_library_dirs("${_TARGET_INTERFACE_LINK_LIBRARIES}" "${CMAKE_INSTALL_PREFIX}/${_INSTALL_LIBDIR}" _lib_dirs _library_names) cmake_list_to_pkg_config(_libs "${_library_names}" "-l") list(LENGTH _lib_dirs _additional_libdirs_count) if (_additional_libdirs_count GREATER 0) cmake_list_to_pkg_config(_additional_libdirs "${_lib_dirs}" "-L") set(_interface_link_libraries "${_additional_libdirs} ${_libs}") else() set(_interface_link_libraries "${_libs}") endif() cmake_list_to_pkg_config(_interface_definitions "${_TARGET_INTERFACE_DEFINITIONS}" "-D") cmake_list_to_pkg_config(_interface_include_dirs "${_TARGET_INTERFACE_INCLUDE_DIRS}" "-I") set(_interface_compile_options "${_TARGET_INTERFACE_COMPILE_OPTIONS}") string(REPLACE ";" " " _interface_compile_options "${_interface_compile_options}") configure_file("@_pkg_config_file_template_filename@" "@_generate_target_dir@/@_package_name@.pc" @ONLY) file(INSTALL "@_generate_target_dir@/@_package_name@.pc" DESTINATION "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/pkgconfig") libtorrent-rasterbar-1.1.13/cmake/Modules/GeneratePkgConfig/pkg-config.cmake.in000066400000000000000000000004661351156116000274160ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=${prefix}/@_INSTALL_LIBDIR@ Name: @_PROJECT_NAME@ Description: @_PROJECT_DESCRIPTION@ Version: @_PROJECT_VERSION@ Libs: -L${libdir} -l@_TARGET_OUTPUT_NAME@ @_interface_link_libraries@ Cflags: @_interface_compile_options@ @_interface_include_dirs@ @_interface_definitions@ libtorrent-rasterbar-1.1.13/cmake/Modules/GeneratePkgConfig/target-compile-settings.cmake.in000066400000000000000000000011241351156116000321340ustar00rootroot00000000000000set(_TARGET_INTERFACE_LINK_LIBRARIES "@_interface_link_libraries@") set(_TARGET_INTERFACE_COMPILE_OPTIONS "@_interface_compile_options@") set(_TARGET_INTERFACE_INCLUDE_DIRS "@_interface_include_dirs@") set(_TARGET_INTERFACE_DEFINITIONS "@_interface_definitions@") set(_TARGET_OUTPUT_NAME "@_output_name@") set(_INSTALL_LIBDIR "@CMAKE_INSTALL_LIBDIR@") set(_INSTALL_INCLUDEDIR "@CMAKE_INSTALL_INCLUDEDIR@") set(_SHARED_LIBRARY_PREFIX "@CMAKE_SHARED_LIBRARY_PREFIX@") set(_PROJECT_NAME "@PROJECT_NAME@") set(_PROJECT_DESCRIPTION "@PROJECT_DESCRIPTION@") set(_PROJECT_VERSION "@PROJECT_VERSION@") libtorrent-rasterbar-1.1.13/cmake/Modules/LibtorrentMacros.cmake000066400000000000000000000102071351156116000247260ustar00rootroot00000000000000# Various helper function and macros for building libtorrent include(FeatureSummary) # macro for issuing option() and add_feature_info() in a single call. # Synopsis: # feature_option( ) macro(feature_option _name _description _default) option(${_name} "${_description}" ${_default}) add_feature_info(${_name} ${_name} "${_description}") endmacro() # function to add a simple build option which controls compile definition(s) for a target. # Synopsis: # target_optional_compile_definitions( [FEATURE] # NAME DESCRIPTION DEFAULT # [ENABLED [enabled_compile_definitions...]] # [DISABLED [disabled_compile_defnitions...]] # ) # NAME, DESCRIPTION and DEFAULT are passed to option() call # if FEATURE is given, they are passed to add_feature_info() # ENABLED lists compile definitions that will be set on when option is enabled, # DISABLED lists definitions that will be set otherwise function(target_optional_compile_definitions _target _scope) set(options FEATURE) set(oneValueArgs NAME DESCRIPTION DEFAULT) set(multiValueArgs ENABLED DISABLED) cmake_parse_arguments(TOCD ${options} "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) option(${TOCD_NAME} "${TOCD_DESCRIPTION}" ${TOCD_DEFAULT}) if (${${TOCD_NAME}}) target_compile_definitions(${_target} ${_scope} ${TOCD_ENABLED}) else() target_compile_definitions(${_target} ${_scope} ${TOCD_DISABLED}) endif() if(${TOCD_FEATURE}) add_feature_info(${TOCD_NAME} ${TOCD_NAME} "${TOCD_DESCRIPTION}") endif() endfunction() # a helper macro that calls find_package() and appends the package (if found) to the # _package_dependencies list, which can be used later to generate package config file macro(find_public_dependency _name) find_package(${_name} ${ARGN}) string(TOUPPER "${_name}" _name_uppercased) if (${_name}_FOUND OR ${_name_uppercased}_FOUND) # Dependencies to be used below for generating Config.cmake file # We don't need the 'REQUIRED' argument there set(_args "${_name}") list(APPEND _args "${ARGN}") list(REMOVE_ITEM _args "REQUIRED") list(REMOVE_ITEM _args "") # just in case string(REPLACE ";" " " _args "${_args}") list(APPEND _package_dependencies "${_args}") endif() endmacro() function(_cxx_standard_to_year _yearVar _std) if (${_std} GREATER 97) math(EXPR _year "1900 + ${_std}") else() math(EXPR _year "2000 + ${_std}") endif() set(${_yearVar} ${_year} PARENT_SCOPE) endfunction() function(select_cxx_standard _target _minimal_supported_version) message(STATUS "Compiler default is C++${CMAKE_CXX_STANDARD_DEFAULT}") # make the CXX_STANDARD property public to ensure it makes it into the pkg-config file get_target_property(_std ${_target} CXX_STANDARD) # RC_1_1 still supports C++98, which is lower version than C++11. Thus we convert std # version to year value and compare years _cxx_standard_to_year(minimal_supported_version_year ${_minimal_supported_version}) # if it is unset, select the default if it is sufficient or the ${_minimal_supported_version} if (NOT ${_std}) _cxx_standard_to_year(std_default_year ${CMAKE_CXX_STANDARD_DEFAULT}) if (${std_default_year} GREATER_EQUAL ${minimal_supported_version_year}) set(_std ${CMAKE_CXX_STANDARD_DEFAULT}) else() set(_std ${_minimal_supported_version}) endif() else() _cxx_standard_to_year(std_year ${_std}) if (${std_year} LESS ${minimal_supported_version_year}) message(SEND_ERROR "Sorry, C++${_std} is not supported by libtorrent with the given Boost version") message(FATAL_ERROR "The minimal supported C++ standard version is C++${_minimal_supported_version}") endif() endif() if (cxx_std_${_std} IN_LIST CMAKE_CXX_COMPILE_FEATURES) # force this standard target_compile_features(${_target} PUBLIC cxx_std_${_std}) else() message(FATAL_ERROR "Requested C++ standard version (${_std}) is not supported by the compiler") endif() message(STATUS "Building in C++${_std} mode") endfunction() function(list_prepend _listName _prefix) string(REGEX REPLACE "([^;]+)" "${_prefix}\\1" _tmp_list "${${_listName}}") set (${_listName} "${_tmp_list}" PARENT_SCOPE) endfunction() libtorrent-rasterbar-1.1.13/cmake/Modules/ucm_flags.cmake000066400000000000000000000075521351156116000234060ustar00rootroot00000000000000# taken from https://github.com/onqtam/ucm/blob/master/cmake/ucm.cmake # # ucm.cmake - useful cmake macros # # Copyright (c) 2016 Viktor Kirilov # # Distributed under the MIT Software License # See accompanying file LICENSE.txt or copy at # https://opensource.org/licenses/MIT # # The documentation can be found at the library's page: # https://github.com/onqtam/ucm include(CMakeParseArguments) # ucm_gather_flags # Gathers all lists of flags for printing or manipulation macro(ucm_gather_flags with_linker result) set(${result} "") # add the main flags without a config list(APPEND ${result} CMAKE_C_FLAGS) list(APPEND ${result} CMAKE_CXX_FLAGS) if(${with_linker}) list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS) list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS) list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS) list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS) endif() if("${CMAKE_CONFIGURATION_TYPES}" STREQUAL "" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "") # handle single config generators - like makefiles/ninja - when CMAKE_BUILD_TYPE is set string(TOUPPER ${CMAKE_BUILD_TYPE} config) list(APPEND ${result} CMAKE_C_FLAGS_${config}) list(APPEND ${result} CMAKE_CXX_FLAGS_${config}) if(${with_linker}) list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config}) endif() else() # handle multi config generators (like msvc, xcode) foreach(config ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER ${config} config) list(APPEND ${result} CMAKE_C_FLAGS_${config}) list(APPEND ${result} CMAKE_CXX_FLAGS_${config}) if(${with_linker}) list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config}) endif() endforeach() endif() endmacro() # ucm_set_runtime # Sets the runtime (static/dynamic) for msvc/gcc macro(ucm_set_runtime) cmake_parse_arguments(ARG "STATIC;DYNAMIC" "" "" ${ARGN}) if(ARG_UNPARSED_ARGUMENTS) message(FATAL_ERROR "unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}") endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" STREQUAL "") message(AUTHOR_WARNING "ucm_set_runtime() does not support clang yet!") endif() ucm_gather_flags(0 flags_configs) # add/replace the flags # note that if the user has messed with the flags directly this function might fail # - for example if with MSVC and the user has removed the flags - here we just switch/replace them if("${ARG_STATIC}") foreach(flags ${flags_configs}) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") if(NOT ${flags} MATCHES "-static-libstdc\\+\\+") set(${flags} "${${flags}} -static-libstdc++") endif() if(NOT ${flags} MATCHES "-static-libgcc") set(${flags} "${${flags}} -static-libgcc") endif() elseif(MSVC) if(${flags} MATCHES "/MD") string(REGEX REPLACE "/MD" "/MT" ${flags} "${${flags}}") endif() endif() endforeach() elseif("${ARG_DYNAMIC}") foreach(flags ${flags_configs}) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") if(${flags} MATCHES "-static-libstdc\\+\\+") string(REGEX REPLACE "-static-libstdc\\+\\+" "" ${flags} "${${flags}}") endif() if(${flags} MATCHES "-static-libgcc") string(REGEX REPLACE "-static-libgcc" "" ${flags} "${${flags}}") endif() elseif(MSVC) if(${flags} MATCHES "/MT") string(REGEX REPLACE "/MT" "/MD" ${flags} "${${flags}}") endif() endif() endforeach() endif() endmacro() # ucm_print_flags # Prints all compiler flags for all configurations macro(ucm_print_flags) ucm_gather_flags(1 flags_configs) message("") foreach(flags ${flags_configs}) message("${flags}: ${${flags}}") endforeach() message("") endmacro() libtorrent-rasterbar-1.1.13/configure000077500000000000000000025732721351156116000176740ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for libtorrent-rasterbar 1.1.13. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: arvid@libtorrent.org about your system, including any $0: error possibly output before this message. Then install $0: a modern shell, or manually run the script under such a $0: shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" SHELL=${CONFIG_SHELL-/bin/sh} test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='libtorrent-rasterbar' PACKAGE_TARNAME='libtorrent-rasterbar' PACKAGE_VERSION='1.1.13' PACKAGE_STRING='libtorrent-rasterbar 1.1.13' PACKAGE_BUGREPORT='arvid@libtorrent.org' PACKAGE_URL='http://www.libtorrent.org' ac_unique_file="src/torrent.cpp" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS PYTHON_CXXFLAGS COMPILETIME_OPTIONS DEBUGFLAGS PYTHON_INSTALL_PARAMS WITH_OPENSSL_FALSE WITH_OPENSSL_TRUE ENABLE_PYTHON_BINDING_FALSE ENABLE_PYTHON_BINDING_TRUE ENABLE_TESTS_FALSE ENABLE_TESTS_TRUE ENABLE_EXAMPLES_FALSE ENABLE_EXAMPLES_TRUE ENABLE_DHT_FALSE ENABLE_DHT_TRUE ICONV_LIBS LTLIBICONV LIBICONV BOOST_PYTHON_LIB PYTHON_EXTRA_LIBS PYTHON_EXTRA_LDFLAGS PYTHON_SITE_PKG PYTHON_LIBS PYTHON_CPPFLAGS pkgpyexecdir pyexecdir pkgpythondir pythondir PYTHON_PLATFORM PYTHON_EXEC_PREFIX PYTHON_PREFIX PYTHON_VERSION PYTHON OPENSSL_LDFLAGS OPENSSL_LIBS OPENSSL_INCLUDES PKG_CONFIG BOOST_RANDOM_LIB BOOST_CHRONO_LIB BOOST_SYSTEM_LIB BOOST_LDFLAGS BOOST_CPPFLAGS PTHREAD_CFLAGS PTHREAD_LIBS PTHREAD_CC ax_pthread_config LT_SYS_LIBRARY_PATH OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL MANIFEST_TOOL RANLIB ac_ct_AR AR DLLTOOL OBJDUMP LN_S NM ac_ct_DUMPBIN DUMPBIN LD FGREP EGREP GREP SED LIBTOOL MAINT MAINTAINER_MODE_FALSE MAINTAINER_MODE_TRUE am__fastdepCXX_FALSE am__fastdepCXX_TRUE CXXDEPMODE am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__quote am__include DEPDIR am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_os target_vendor target_cpu target host_os host_vendor host_cpu host build_os build_vendor build_cpu build CXXCPP ac_ct_CXX CXXFLAGS CXX CPP OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC INTERFACE_VERSION_INFO AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_dependency_tracking enable_maintainer_mode enable_shared enable_static with_pic enable_fast_install with_aix_soname with_gnu_ld with_sysroot enable_libtool_lock with_boost with_boost_libdir with_boost_system with_boost_chrono with_boost_random enable_largefile enable_logging enable_debug enable_dht enable_encryption enable_export_all enable_pool_allocators enable_invariant_checks enable_deprecated_functions enable_disk_stats enable_examples enable_tests enable_python_binding with_libiconv with_openssl with_boost_python with_libiconv_prefix ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP CXX CXXFLAGS CCC CXXCPP LT_SYS_LIBRARY_PATH PKG_CONFIG PYTHON PYTHON_VERSION PYTHON_INSTALL_PARAMS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures libtorrent-rasterbar 1.1.13 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/libtorrent-rasterbar] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] --target=TARGET configure for building compilers for TARGET [HOST] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of libtorrent-rasterbar 1.1.13:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --disable-maintainer-mode disable make rules and dependencies not useful (and sometimes confusing) to the casual installer --enable-shared[=PKGS] build shared libraries [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-largefile omit support for large files --enable-logging enable logging alerts [default=yes] --enable-debug enable debug build [default=no] --enable-dht enable dht support [default=yes] --enable-encryption enable encryption support (requires OpenSSL to be installed on your system, you can use --with-openssl to set the path) [default=yes] --enable-export-all export all symbols from libtorrent, including non-public ones [default=no] --enable-pool-allocators enable pool allocators for send buffers [default=yes] --enable-invariant-checks enable invariant checks (use value "full" to turn on extra expensive invariant checks) [default=yes if debug is enabled, no otherwhise] --enable-deprecated-functions enable deprecated functions in the API [default=yes] --enable-disk-stats enable disk activity logging feature [default=no] --enable-examples build example files [default=no] --enable-tests build test files [default=no] --enable-python-binding build python bindings [default=no] Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use both] --with-aix-soname=aix|svr4|both shared library versioning (aka "SONAME") variant to provide on AIX, [default=aix]. --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --with-boost[=ARG] use Boost library from a standard location (ARG=yes), from the specified location (ARG=), or disable it (ARG=no) [ARG=yes] --with-boost-libdir=LIB_DIR Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located. --with-boost-system[=special-lib] use the System library from boost - it is possible to specify a certain library for the linker e.g. --with-boost-system=boost_system-gcc-mt --with-boost-chrono[=special-lib] use the Chrono library from boost - it is possible to specify a certain library for the linker e.g. --with-boost-chrono=boost_chrono-gcc-mt --with-boost-random[=special-lib] use the Random library from boost - it is possible to specify a certain library for the linker e.g. --with-boost-random=boost_random-gcc-mt --with-libiconv enable linking against system libiconv [default=no] --with-openssl=DIR root of the OpenSSL directory --with-boost-python specify yes/no or the boost python library or suffix to use --with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib --without-libiconv-prefix don't search for libiconv in includedir and libdir Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor CXX C++ compiler command CXXFLAGS C++ compiler flags CXXCPP C++ preprocessor LT_SYS_LIBRARY_PATH User-defined run-time library search path. PKG_CONFIG path to pkg-config utility PYTHON the Python interpreter PYTHON_VERSION The installed Python version to use, for example '2.3'. This string will be appended to the Python interpreter canonical name. PYTHON_INSTALL_PARAMS Set specific install parameters for python bindings. Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . libtorrent-rasterbar home page: . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF libtorrent-rasterbar configure 1.1.13 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_cxx_try_link LINENO # ------------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_link cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by libtorrent-rasterbar $as_me 1.1.13, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in build-aux "$srcdir"/build-aux; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Silencing build output (automake-1.11) # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=0;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' ############################################################################### INTERFACE_VERSION_INFO=9:0:0 ############################################################################### ############################################################################### # Start ############################################################################### $as_echo $as_echo "Building $PACKAGE_STRING" ############################################################################### # Performing some basic checks and initializing the build system ############################################################################### $as_echo $as_echo "Checking for a C/C++ compiler to use:" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test "x$CC" != xcc; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC and cc understand -c and -o together" >&5 $as_echo_n "checking whether $CC and cc understand -c and -o together... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether cc understands -c and -o together" >&5 $as_echo_n "checking whether cc understands -c and -o together... " >&6; } fi set dummy $CC; ac_cc=`$as_echo "$2" | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` if eval \${ac_cv_prog_cc_${ac_cc}_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # We do the test twice because some compilers refuse to overwrite an # existing .o file with -o, though they will create one. ac_try='$CC -c conftest.$ac_ext -o conftest2.$ac_objext >&5' rm -f conftest2.* if { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -f conftest2.$ac_objext && { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then eval ac_cv_prog_cc_${ac_cc}_c_o=yes if test "x$CC" != xcc; then # Test first that cc exists at all. if { ac_try='cc -c conftest.$ac_ext >&5' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then ac_try='cc -c conftest.$ac_ext -o conftest2.$ac_objext >&5' rm -f conftest2.* if { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -f conftest2.$ac_objext && { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # cc works too. : else # cc exists but doesn't like -o. eval ac_cv_prog_cc_${ac_cc}_c_o=no fi fi fi else eval ac_cv_prog_cc_${ac_cc}_c_o=no fi rm -f core conftest* fi if eval test \$ac_cv_prog_cc_${ac_cc}_c_o = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "#define NO_MINUS_C_MINUS_O 1" >>confdefs.h fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if ${ac_cv_prog_CXXCPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX understands -c and -o together" >&5 $as_echo_n "checking whether $CXX understands -c and -o together... " >&6; } if ${ac_cv_prog_cxx_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # We test twice because some compilers refuse to overwrite an existing # `.o' file with `-o', although they will create one. ac_try='$CXX $CXXFLAGS -c conftest.$ac_ext -o conftest2.$ac_objext >&5' rm -f conftest2.* if { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -f conftest2.$ac_objext && { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then ac_cv_prog_cxx_c_o=yes else ac_cv_prog_cxx_c_o=no fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_c_o" >&5 $as_echo "$ac_cv_prog_cxx_c_o" >&6; } if test $ac_cv_prog_cxx_c_o = no; then $as_echo "#define CXX_NO_MINUS_C_MINUS_O 1" >>confdefs.h fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu $as_echo $as_echo "Checking system type:" # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 $as_echo_n "checking target system type... " >&6; } if ${ac_cv_target+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$target_alias" = x; then ac_cv_target=$ac_cv_host else ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 $as_echo "$ac_cv_target" >&6; } case $ac_cv_target in *-*-*) ;; *) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; esac target=$ac_cv_target ac_save_IFS=$IFS; IFS='-' set x $ac_cv_target shift target_cpu=$1 target_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: target_os=$* IFS=$ac_save_IFS case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac # The aliases save the names the user supplied, while $host etc. # will get canonicalized. test -n "$target_alias" && test "$program_prefix$program_suffix$program_transform_name" = \ NONENONEs,x,x, && program_prefix=${target_alias}- $as_echo $as_echo "Initializing Automake:" am__api_version='1.15' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 $as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 $as_echo "$_am_result" >&6; } rm -f confinc confmf # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='libtorrent-rasterbar' VERSION='1.1.13' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi depcc="$CXX" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CXX_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CXX_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CXX_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CXX_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 $as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then am__fastdepCXX_TRUE= am__fastdepCXX_FALSE='#' else am__fastdepCXX_TRUE='#' am__fastdepCXX_FALSE= fi # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 $as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } # Check whether --enable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then : enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval else USE_MAINTAINER_MODE=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 $as_echo "$USE_MAINTAINER_MODE" >&6; } if test $USE_MAINTAINER_MODE = yes; then MAINTAINER_MODE_TRUE= MAINTAINER_MODE_FALSE='#' else MAINTAINER_MODE_TRUE='#' MAINTAINER_MODE_FALSE= fi MAINT=$MAINTAINER_MODE_TRUE $as_echo $as_echo "Initializing Libtool:" case `pwd` in *\ * | *\ *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 $as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.4.6' macro_revision='2.4.6' ltmain=$ac_aux_dir/ltmain.sh # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 $as_echo_n "checking how to print strings... " >&6; } # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "" } case $ECHO in printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 $as_echo "printf" >&6; } ;; print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 $as_echo "print -r" >&6; } ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 $as_echo "cat" >&6; } ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 $as_echo_n "checking for fgrep... " >&6; } if ${ac_cv_path_FGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in fgrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_FGREP" || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 $as_echo "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 $as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if ${lt_cv_path_NM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 $as_echo "$lt_cv_path_NM" >&6; } if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else if test -n "$ac_tool_prefix"; then for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 $as_echo "$DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 $as_echo "$ac_ct_DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if ${lt_cv_nm_interface+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 $as_echo "$lt_cv_nm_interface" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi # find the maximum length of command line arguments { $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 $as_echo_n "checking the maximum length of command line arguments... " >&6; } if ${lt_cv_sys_max_cmd_len+:} false; then : $as_echo_n "(cached) " >&6 else i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n "$lt_cv_sys_max_cmd_len"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 $as_echo "$lt_cv_sys_max_cmd_len" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 $as_echo_n "checking how to convert $build file names to $host format... " >&6; } if ${lt_cv_to_host_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac fi to_host_file_cmd=$lt_cv_to_host_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 $as_echo "$lt_cv_to_host_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 $as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } if ${lt_cv_to_tool_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else #assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac fi to_tool_file_cmd=$lt_cv_to_tool_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 $as_echo "$lt_cv_to_tool_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 $as_echo_n "checking for $LD option to reload object files... " >&6; } if ${lt_cv_ld_reload_flag+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_reload_flag='-r' fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 $as_echo "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in cygwin* | mingw* | pw32* | cegcc*) if test yes != "$GCC"; then reload_cmds=false fi ;; darwin*) if test yes = "$GCC"; then reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 $as_echo_n "checking how to recognize dependent libraries... " >&6; } if ${lt_cv_deplibs_check_method+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 $as_echo "$lt_cv_deplibs_check_method" >&6; } file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 $as_echo "$DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 $as_echo "$ac_ct_DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi test -z "$DLLTOOL" && DLLTOOL=dlltool { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 $as_echo_n "checking how to associate runtime and link libraries... " >&6; } if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 $as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} : ${AR_FLAGS=cru} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 $as_echo_n "checking for archiver @FILE support... " >&6; } if ${lt_cv_ar_at_file+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ar_at_file=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 $as_echo "$lt_cv_ar_at_file" >&6; } if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 $as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } if ${lt_cv_sys_global_symbol_pipe+:} false; then : $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then nm_file_list_spec='@' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 $as_echo_n "checking for sysroot... " >&6; } # Check whether --with-sysroot was given. if test "${with_sysroot+set}" = set; then : withval=$with_sysroot; else with_sysroot=no fi lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 $as_echo "$with_sysroot" >&6; } as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 $as_echo "${lt_sysroot:-no}" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 $as_echo_n "checking for a working dd... " >&6; } if ${ac_cv_path_lt_DD+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} if test -z "$lt_DD"; then ac_path_lt_DD_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in dd; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_lt_DD" || continue if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi $ac_path_lt_DD_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_lt_DD"; then : fi else ac_cv_path_lt_DD=$lt_DD fi rm -f conftest.i conftest2.i conftest.out fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 $as_echo "$ac_cv_path_lt_DD" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 $as_echo_n "checking how to truncate binary pipes... " >&6; } if ${lt_cv_truncate_bin+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 $as_echo "$lt_cv_truncate_bin" >&6; } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; fi test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 $as_echo_n "checking whether the C compiler needs -belf... " >&6; } if ${lt_cv_cc_needs_belf+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_cc_needs_belf=yes else lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 $as_echo "$lt_cv_cc_needs_belf" >&6; } if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. set dummy ${ac_tool_prefix}mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$MANIFEST_TOOL"; then ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL if test -n "$MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 $as_echo "$MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_MANIFEST_TOOL"; then ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL # Extract the first word of "mt", so it can be a program name with args. set dummy mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_MANIFEST_TOOL"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL if test -n "$ac_ct_MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 $as_echo "$ac_ct_MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_MANIFEST_TOOL" = x; then MANIFEST_TOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL fi else MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" fi test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 $as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } if ${lt_cv_path_mainfest_tool+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&5 if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 $as_echo "$lt_cv_path_mainfest_tool" >&6; } if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 $as_echo "$DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 $as_echo "$ac_ct_DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 $as_echo "$NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_NMEDIT="nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 $as_echo "$ac_ct_NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 $as_echo "$LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LIPO="lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 $as_echo "$ac_ct_LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 $as_echo "$OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL="otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 $as_echo "$ac_ct_OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 $as_echo "$OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL64="otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 $as_echo "$ac_ct_OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 $as_echo_n "checking for -single_module linker flag... " >&6; } if ${lt_cv_apple_cc_single_mod+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&5 # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 $as_echo "$lt_cv_apple_cc_single_mod" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 $as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } if ${lt_cv_ld_exported_symbols_list+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_ld_exported_symbols_list=yes else lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 $as_echo "$lt_cv_ld_exported_symbols_list" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 $as_echo_n "checking for -force_load linker flag... " >&6; } if ${lt_cv_ld_force_load+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 echo "$AR cru libconftest.a conftest.o" >&5 $AR cru libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&5 elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&5 fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 $as_echo "$lt_cv_ld_force_load" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[91]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[012][,.]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in dlfcn.h do : ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DLFCN_H 1 _ACEOF fi done func_stripname_cnf () { case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;; esac } # func_stripname_cnf # Set options enable_dlopen=no enable_win32_dll=no # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac else enable_shared=yes fi # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac else enable_static=yes fi # Check whether --with-pic was given. if test "${with_pic+set}" = set; then : withval=$with_pic; lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac else pic_mode=default fi # Check whether --enable-fast-install was given. if test "${enable_fast_install+set}" = set; then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac else enable_fast_install=yes fi shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[5-9]*,yes) { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 $as_echo_n "checking which variant of shared library versioning to provide... " >&6; } # Check whether --with-aix-soname was given. if test "${with_aix_soname+set}" = set; then : withval=$with_aix_soname; case $withval in aix|svr4|both) ;; *) as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 ;; esac lt_cv_with_aix_soname=$with_aix_soname else if ${lt_cv_with_aix_soname+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_with_aix_soname=aix fi with_aix_soname=$lt_cv_with_aix_soname fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 $as_echo "$with_aix_soname" >&6; } if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 $as_echo_n "checking for objdir... " >&6; } if ${lt_cv_objdir+:} false; then : $as_echo_n "(cached) " >&6 else rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 $as_echo "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir cat >>confdefs.h <<_ACEOF #define LT_OBJDIR "$lt_cv_objdir/" _ACEOF case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o func_cc_basename $compiler cc_basename=$func_cc_basename_result # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 $as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/${ac_tool_prefix}file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 $as_echo_n "checking for file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC=$CC ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test yes = "$GCC"; then case $cc_basename in nvcc*) lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; *) lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 $as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 $as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= if test yes = "$GCC"; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi lt_prog_compiler_pic='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 lt_prog_compiler_wl='-Xlinker ' if test -n "$lt_prog_compiler_pic"; then lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; *Sun\ F* | *Sun*Fortran*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Intel*\ [CF]*Compiler*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; *Portland\ Group*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic=$lt_prog_compiler_pic fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 $as_echo "$lt_cv_prog_compiler_pic" >&6; } lt_prog_compiler_pic=$lt_cv_prog_compiler_pic # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if ${lt_cv_prog_compiler_pic_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 $as_echo "$lt_cv_prog_compiler_pic_works" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works"; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 $as_echo "$lt_cv_prog_compiler_static_works" >&6; } if test yes = "$lt_cv_prog_compiler_static_works"; then : else lt_prog_compiler_static= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test no = "$hard_links"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs=no ;; esac ld_shlibs=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; *\ \(GNU\ Binutils\)\ [3-9]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' export_dynamic_flag_spec='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' export_dynamic_flag_spec='$wl--export-all-symbols' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; haiku*) archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs=yes ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) export_dynamic_flag_spec='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test no = "$ld_shlibs"; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct=no hardcode_direct_absolute=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' $wl-bernotok' allow_undefined_flag=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' fi archive_cmds_need_lc=yes archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes file_list_spec='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, )='true' enable_shared_with_static_runtimes=yes exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib old_postinstall_cmds='chmod 644 $oldlib' postlink_cmds='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' enable_shared_with_static_runtimes=yes ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec='' fi link_all_deplibs=yes allow_undefined_flag=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test yes = "$GCC"; then archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 $as_echo_n "checking if $CC understands -b... " >&6; } if ${lt_cv_prog_compiler__b+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler__b=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -b" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler__b=yes fi else lt_cv_prog_compiler__b=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 $as_echo "$lt_cv_prog_compiler__b" >&6; } if test yes = "$lt_cv_prog_compiler__b"; then archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi ;; esac fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 $as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } if ${lt_cv_irix_exported_symbol+:} false; then : $as_echo_n "(cached) " >&6 else save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo (void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_irix_exported_symbol=yes else lt_cv_irix_exported_symbol=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 $as_echo "$lt_cv_irix_exported_symbol" >&6; } if test yes = "$lt_cv_irix_exported_symbol"; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi link_all_deplibs=no else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler ld_shlibs=yes archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' else archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='$wl-rpath,$libdir' fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes ;; osf3*) if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test yes = "$GCC"; then wlarc='$wl' archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='$wl-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='$wl-z,text' allow_undefined_flag='$wl-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='$wl-Blargedynsym' ;; esac fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 $as_echo "$ld_shlibs" >&6; } test no = "$ld_shlibs" && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc=no else lt_cv_archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 $as_echo "$lt_cv_archive_cmds_need_lc" >&6; } archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([A-Za-z]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test yes = "$hardcode_automatic"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && test no != "$hardcode_minus_L"; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 $as_echo "$hardcode_action" >&6; } if test relink = "$hardcode_action" || test yes = "$inherit_rpath"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes; then : lt_cv_dlopen=shl_load else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if ${ac_cv_lib_dld_shl_load+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes; then : lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld else ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes; then : lt_cv_dlopen=dlopen else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 $as_echo_n "checking for dlopen in -lsvld... " >&6; } if ${ac_cv_lib_svld_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_svld_dlopen=yes else ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 $as_echo_n "checking for dld_link in -ldld... " >&6; } if ${ac_cv_lib_dld_dld_link+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dld_link (); int main () { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_dld_link=yes else ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = xyes; then : lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld fi fi fi fi fi fi ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 $as_echo_n "checking whether a program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 $as_echo "$lt_cv_dlopen_self" >&6; } if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 $as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self_static+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 $as_echo "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; esac fi # Report what library types will actually be built { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 $as_echo_n "checking if libtool supports shared libraries... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 $as_echo "$can_build_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 $as_echo_n "checking whether to build shared libraries... " >&6; } test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 $as_echo "$enable_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 $as_echo_n "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 $as_echo "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC=$lt_save_CC if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if ${ac_cv_prog_CXXCPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu else _lt_caught_CXX_error=yes fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu archive_cmds_need_lc_CXX=no allow_undefined_flag_CXX= always_export_symbols_CXX=no archive_expsym_cmds_CXX= compiler_needs_object_CXX=no export_dynamic_flag_spec_CXX= hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no hardcode_libdir_flag_spec_CXX= hardcode_libdir_separator_CXX= hardcode_minus_L_CXX=no hardcode_shlibpath_var_CXX=unsupported hardcode_automatic_CXX=no inherit_rpath_CXX=no module_cmds_CXX= module_expsym_cmds_CXX= link_all_deplibs_CXX=unknown old_archive_cmds_CXX=$old_archive_cmds reload_flag_CXX=$reload_flag reload_cmds_CXX=$reload_cmds no_undefined_flag_CXX= whole_archive_flag_spec_CXX= enable_shared_with_static_runtimes_CXX=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o objext_CXX=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC compiler_CXX=$CC func_cc_basename $compiler cc_basename=$func_cc_basename_result if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' else lt_prog_compiler_no_builtin_flag_CXX= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec_CXX= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } ld_shlibs_CXX=yes case $host_os in aix3*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds_CXX='' hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes file_list_spec_CXX='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct_CXX=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L_CXX=yes hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_libdir_separator_CXX= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec_CXX='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. always_export_symbols_CXX=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. no_undefined_flag_CXX='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath__CXX+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag_CXX="-z nodefs" archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath__CXX+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag_CXX=' $wl-bernotok' allow_undefined_flag_CXX=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec_CXX='$convenience' fi archive_cmds_need_lc_CXX=yes archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag_CXX=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs_CXX=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec_CXX=' ' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=yes file_list_spec_CXX='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' enable_shared_with_static_runtimes_CXX=yes # Don't use ranlib old_postinstall_cmds_CXX='chmod 644 $oldlib' postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec_CXX='-L$libdir' export_dynamic_flag_spec_CXX='$wl--export-all-symbols' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=no enable_shared_with_static_runtimes_CXX=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs_CXX=no fi ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc_CXX=no hardcode_direct_CXX=no hardcode_automatic_CXX=yes hardcode_shlibpath_var_CXX=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec_CXX='' fi link_all_deplibs_CXX=yes allow_undefined_flag_CXX=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds_CXX="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" if test yes != "$lt_cv_apple_cc_single_mod"; then archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi else ld_shlibs_CXX=no fi ;; os2*) hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_minus_L_CXX=yes allow_undefined_flag_CXX=unsupported shrext_cmds=.dll archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes_CXX=yes ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF ld_shlibs_CXX=no ;; freebsd-elf*) archive_cmds_need_lc_CXX=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions ld_shlibs_CXX=yes ;; haiku*) archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs_CXX=yes ;; hpux9*) hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' hardcode_libdir_separator_CXX=: export_dynamic_flag_spec_CXX='$wl-E' hardcode_direct_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' hardcode_libdir_separator_CXX=: case $host_cpu in hppa*64*|ia64*) ;; *) export_dynamic_flag_spec_CXX='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no ;; *) hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; interix[3-9]*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds_CXX='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi link_all_deplibs_CXX=yes ;; esac hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' hardcode_libdir_separator_CXX=: inherit_rpath_CXX=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac archive_cmds_need_lc_CXX=no hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [1-5].* | *pgcpp\ [1-5].*) prelink_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' old_archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec_CXX='-rpath $libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' hardcode_libdir_flag_spec_CXX='-R$libdir' whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object_CXX=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; m88k*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) ld_shlibs_CXX=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no hardcode_direct_absolute_CXX=yes archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' export_dynamic_flag_spec_CXX='$wl-E' whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else ld_shlibs_CXX=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' hardcode_libdir_separator_CXX=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; cxx*) case $host in osf3*) allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' ;; *) allow_undefined_flag_CXX=' -expect_unresolved \*' archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' hardcode_libdir_flag_spec_CXX='-rpath $libdir' ;; esac hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' case $host in osf3*) archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ archive_cmds_need_lc_CXX=yes no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_shlibpath_var_CXX=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' ;; esac link_all_deplibs_CXX=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then no_undefined_flag_CXX=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir' case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag_CXX='$wl-z,text' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag_CXX='$wl-z,text' allow_undefined_flag_CXX='$wl-z,nodefs' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='$wl-R,$libdir' hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes export_dynamic_flag_spec_CXX='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ '"$old_archive_cmds_CXX" reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ '"$reload_cmds_CXX" ;; *) archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test no = "$ld_shlibs_CXX" && can_build_shared=no GCC_CXX=$GXX LD_CXX=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... # Dependencies to place before and after the object being linked: predep_objects_CXX= postdep_objects_CXX= predeps_CXX= postdeps_CXX= compiler_lib_search_path_CXX= cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$compiler_lib_search_path_CXX"; then compiler_lib_search_path_CXX=$prev$p else compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$postdeps_CXX"; then postdeps_CXX=$prev$p else postdeps_CXX="${postdeps_CXX} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$predep_objects_CXX"; then predep_objects_CXX=$p else predep_objects_CXX="$predep_objects_CXX $p" fi else if test -z "$postdep_objects_CXX"; then postdep_objects_CXX=$p else postdep_objects_CXX="$postdep_objects_CXX $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling CXX test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken case $host_os in interix[3-9]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. predep_objects_CXX= postdep_objects_CXX= postdeps_CXX= ;; esac case " $postdeps_CXX " in *" -lc "*) archive_cmds_need_lc_CXX=no ;; esac compiler_lib_search_dirs_CXX= if test -n "${compiler_lib_search_path_CXX}"; then compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi lt_prog_compiler_wl_CXX= lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX= # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' fi lt_prog_compiler_pic_CXX='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic_CXX='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic_CXX='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static_CXX='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic_CXX='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all lt_prog_compiler_pic_CXX= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static_CXX= ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic_CXX=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac else case $host_os in aix[4-9]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' else lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic_CXX='-DDLL_EXPORT' ;; dgux*) case $cc_basename in ec++*) lt_prog_compiler_pic_CXX='-KPIC' ;; ghcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then lt_prog_compiler_pic_CXX='+Z' fi ;; aCC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic_CXX='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler lt_prog_compiler_wl_CXX='--backend -Wl,' lt_prog_compiler_pic_CXX='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fPIC' lt_prog_compiler_static_CXX='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fpic' lt_prog_compiler_static_CXX='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) # IBM XL 8.0, 9.0 on PPC and BlueGene lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-qpic' lt_prog_compiler_static_CXX='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) lt_prog_compiler_pic_CXX='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) lt_prog_compiler_wl_CXX='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 lt_prog_compiler_pic_CXX='-pic' ;; cxx*) # Digital/Compaq C++ lt_prog_compiler_wl_CXX='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x lt_prog_compiler_pic_CXX='-pic' lt_prog_compiler_static_CXX='-Bstatic' ;; lcc*) # Lucid lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 lt_prog_compiler_pic_CXX='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) lt_prog_compiler_can_build_shared_CXX=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic_CXX= ;; *) lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works_CXX=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works_CXX=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then case $lt_prog_compiler_pic_CXX in "" | " "*) ;; *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; esac else lt_prog_compiler_pic_CXX= lt_prog_compiler_can_build_shared_CXX=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works_CXX=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works_CXX=yes fi else lt_cv_prog_compiler_static_works_CXX=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then : else lt_prog_compiler_static_CXX= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test no = "$hard_links"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' case $host_os in aix[4-9]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) export_symbols_cmds_CXX=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' ;; esac ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs_CXX=no ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test no = "$ld_shlibs_CXX" && can_build_shared=no with_gnu_ld_CXX=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc_CXX" in x|xyes) # Assume -lc should be added archive_cmds_need_lc_CXX=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds_CXX in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl_CXX pic_flag=$lt_prog_compiler_pic_CXX compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag_CXX allow_undefined_flag_CXX= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc_CXX=no else lt_cv_archive_cmds_need_lc_CXX=yes fi allow_undefined_flag_CXX=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 $as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec_CXX='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action_CXX= if test -n "$hardcode_libdir_flag_spec_CXX" || test -n "$runpath_var_CXX" || test yes = "$hardcode_automatic_CXX"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct_CXX" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" && test no != "$hardcode_minus_L_CXX"; then # Linking always hardcodes the temporary library directory. hardcode_action_CXX=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action_CXX=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action_CXX=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 $as_echo "$hardcode_action_CXX" >&6; } if test relink = "$hardcode_action_CXX" || test yes = "$inherit_rpath_CXX"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_commands="$ac_config_commands libtool" # Only expand once: ############################################################################### # Checking for needed base libraries ############################################################################### $as_echo $as_echo "Checking for posix thread support:" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_LIBS$PTHREAD_CFLAGS" != "x"; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5 $as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_join (); int main () { return pthread_join (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 $as_echo "$ax_pthread_ok" >&6; } if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -kthread lthread -lpthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; esac if test "x$ax_pthread_ok" = "xno"; then for flag in $ax_pthread_flags; do case $flag in none) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 $as_echo_n "checking whether pthreads work without any flags... " >&6; } ;; -*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5 $as_echo_n "checking whether pthreads work with $flag... " >&6; } PTHREAD_CFLAGS="$flag" ;; pthread-config) # Extract the first word of "pthread-config", so it can be a program name with args. set dummy pthread-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ax_pthread_config+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ax_pthread_config"; then ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ax_pthread_config="yes" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" fi fi ax_pthread_config=$ac_cv_prog_ax_pthread_config if test -n "$ax_pthread_config"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 $as_echo "$ax_pthread_config" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ax_pthread_config" = "xno"; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5 $as_echo_n "checking for the pthreads library -l$flag... " >&6; } PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 $as_echo "$ax_pthread_ok" >&6; } if test "x$ax_pthread_ok" = "xyes"; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 $as_echo_n "checking for joinable pthread attribute... " >&6; } attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { int attr=$attr; return attr; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : attr_name=$attr; break fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5 $as_echo "$attr_name" >&6; } if test "x$attr_name" != "xPTHREAD_CREATE_JOINABLE"; then cat >>confdefs.h <<_ACEOF #define PTHREAD_CREATE_JOINABLE $attr_name _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5 $as_echo_n "checking if more special flags are required for pthreads... " >&6; } flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5 $as_echo "${flag}" >&6; } if test "x$flag" != "xno"; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test "x$GCC" != "xyes"; then for ac_prog in xlc_r cc_r do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_PTHREAD_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$PTHREAD_CC"; then ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_PTHREAD_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi PTHREAD_CC=$ac_cv_prog_PTHREAD_CC if test -n "$PTHREAD_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 $as_echo "$PTHREAD_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$PTHREAD_CC" && break done test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}" else PTHREAD_CC=$CC fi # The next part tries to detect GCC inconsistency with -shared on some # architectures and systems. The problem is that in certain # configurations, when -shared is specified, GCC "forgets" to # internally use various flags which are still necessary. # # Prepare the flags # save_LDFLAGS="$LDFLAGS" save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" save_CC="$CC" # Try with the flags determined by the earlier checks. # # -Wl,-z,defs forces link-time symbol resolution, so that the # linking checks with -shared actually have any value # # FIXME: -fPIC is required for -shared on many architectures, # so we specify it here, but the right way would probably be to # properly detect whether it is actually required. CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CC="$PTHREAD_CC" # In order not to create several levels of indentation, we test # the value of "$done" until we find the cure or run out of ideas. done="no" # First, make sure the CFLAGS we added are actually accepted by our # compiler. If not (and OS X's ld, for instance, does not accept -z), # then we can't do this test. if test x"$done" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to check for GCC pthread/shared inconsistencies" >&5 $as_echo_n "checking whether to check for GCC pthread/shared inconsistencies... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : else done=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "x$done" = xyes ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi fi if test x"$done" = xyes; then done="no" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -pthread is sufficient with -shared" >&5 $as_echo_n "checking whether -pthread is sufficient with -shared... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : done=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "x$done" = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi # # Linux gcc on some architectures such as mips/mipsel forgets # about -lpthread # if test x"$done" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lpthread fixes that" >&5 $as_echo_n "checking whether -lpthread fixes that... " >&6; } LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : done=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "x$done" = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi # # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc # if test x"$done" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc_r fixes that" >&5 $as_echo_n "checking whether -lc_r fixes that... " >&6; } LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : done=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "x$done" = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test x"$done" = xno; then # OK, we have run out of ideas { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Impossible to determine how to use pthreads with shared libraries" >&5 $as_echo "$as_me: WARNING: Impossible to determine how to use pthreads with shared libraries" >&2;} # so it's not safe to assume that we may use pthreads acx_pthread_ok=no fi CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" CC="$save_CC" else PTHREAD_CC="$CC" fi # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then $as_echo "#define HAVE_PTHREAD 1" >>confdefs.h : else ax_pthread_ok=no fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$PTHREAD_CFLAGS $CFLAGS" CC="$PTHREAD_CC" CXXFLAGS="$CXXFLAGS -ftemplate-depth=120 -Wno-format-zero-length" $as_echo "Checking for visibility support:" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((visibility(\"hidden\")))" >&5 $as_echo_n "checking for __attribute__((visibility(\"hidden\")))... " >&6; } if ${ac_cv_hidden_visibility_attribute+:} false; then : $as_echo_n "(cached) " >&6 else echo 'int __attribute__ ((visibility ("hidden"))) foo (void) { return 1; }' > visibility_conftest.c ac_cv_hidden_visibility_attribute=no if { ac_try='${CC-cc} -fvisibility=hidden -S visibility_conftest.c -o visibility_conftest.s 1>&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 (eval $ac_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then $as_echo "found" ac_cv_hidden_visibility_attribute=yes fi rm -f visibility_conftest.* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_hidden_visibility_attribute" >&5 $as_echo "$ac_cv_hidden_visibility_attribute" >&6; } $as_echo $as_echo "Checking for boost libraries:" # Check whether --with-boost was given. if test "${with_boost+set}" = set; then : withval=$with_boost; if test "$withval" = "no"; then want_boost="no" elif test "$withval" = "yes"; then want_boost="yes" ac_boost_path="" else want_boost="yes" ac_boost_path="$withval" fi else want_boost="yes" fi # Check whether --with-boost-libdir was given. if test "${with_boost_libdir+set}" = set; then : withval=$with_boost_libdir; if test -d "$withval" then ac_boost_lib_path="$withval" else as_fn_error $? "--with-boost-libdir expected directory name" "$LINENO" 5 fi else ac_boost_lib_path="" fi if test "x$want_boost" = "xyes"; then boost_lib_version_req=1.53 boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([0-9]*\.[0-9]*\)'` boost_lib_version_req_major=`expr $boost_lib_version_req : '\([0-9]*\)'` boost_lib_version_req_minor=`expr $boost_lib_version_req : '[0-9]*\.\([0-9]*\)'` boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[0-9]*\.[0-9]*\.\([0-9]*\)'` if test "x$boost_lib_version_req_sub_minor" = "x" ; then boost_lib_version_req_sub_minor="0" fi WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for boostlib >= $boost_lib_version_req" >&5 $as_echo_n "checking for boostlib >= $boost_lib_version_req... " >&6; } succeeded=no libsubdirs="lib" ax_arch=`uname -m` case $ax_arch in x86_64) libsubdirs="lib64 libx32 lib lib64" ;; ppc64|s390x|sparc64|aarch64|ppc64le) libsubdirs="lib64 lib lib64 ppc64le" ;; esac libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" case ${host_cpu} in i?86) libsubdirs="lib/i386-${host_os} $libsubdirs" ;; esac if test "$ac_boost_path" != ""; then BOOST_CPPFLAGS="-I$ac_boost_path/include" for ac_boost_path_tmp in $libsubdirs; do if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" break fi done elif test "$cross_compiling" != yes; then for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then for libsubdir in $libsubdirs ; do if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" break; fi done fi if test "$ac_boost_lib_path" != ""; then BOOST_LDFLAGS="-L$ac_boost_lib_path" fi CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if BOOST_VERSION >= $WANT_BOOST_VERSION // Everything is okay #else # error Boost version is too old #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } succeeded=yes found_system=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test "x$succeeded" != "xyes"; then CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" BOOST_CPPFLAGS= BOOST_LDFLAGS= _version=0 if test "$ac_boost_path" != ""; then if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` V_CHECK=`expr $_version_tmp \> $_version` if test "$V_CHECK" = "1" ; then _version=$_version_tmp fi VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" done if test -z "$BOOST_CPPFLAGS"; then if test -d "$ac_boost_path/boost" && test -r "$ac_boost_path/boost"; then BOOST_CPPFLAGS="-I$ac_boost_path" fi fi fi else if test "$cross_compiling" != yes; then for ac_boost_path in /usr /usr/local /opt /opt/local ; do if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` V_CHECK=`expr $_version_tmp \> $_version` if test "$V_CHECK" = "1" ; then _version=$_version_tmp best_path=$ac_boost_path fi done fi done VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" if test "$ac_boost_lib_path" = ""; then for libsubdir in $libsubdirs ; do if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done BOOST_LDFLAGS="-L$best_path/$libsubdir" fi fi if test "x$BOOST_ROOT" != "x"; then for libsubdir in $libsubdirs ; do if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` stage_version_shorten=`expr $stage_version : '\([0-9]*\.[0-9]*\)'` V_CHECK=`expr $stage_version_shorten \>\= $_version` if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: We will use a staged boost library from $BOOST_ROOT" >&5 $as_echo "$as_me: We will use a staged boost library from $BOOST_ROOT" >&6;} BOOST_CPPFLAGS="-I$BOOST_ROOT" BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" fi fi fi fi CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if BOOST_VERSION >= $WANT_BOOST_VERSION // Everything is okay #else # error Boost version is too old #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } succeeded=yes found_system=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi if test "$succeeded" != "yes" ; then if test "$_version" = "0" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation." >&5 $as_echo "$as_me: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation." >&6;} else { $as_echo "$as_me:${as_lineno-$LINENO}: Your boost libraries seems to old (version $_version)." >&5 $as_echo "$as_me: Your boost libraries seems to old (version $_version)." >&6;} fi # execute ACTION-IF-NOT-FOUND (if present): : else $as_echo "#define HAVE_BOOST /**/" >>confdefs.h # execute ACTION-IF-FOUND (if present): : fi CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" fi # Check whether --with-boost-system was given. if test "${with_boost_system+set}" = set; then : withval=$with_boost_system; if test "$withval" = "no"; then want_boost="no" elif test "$withval" = "yes"; then want_boost="yes" ax_boost_user_system_lib="" else want_boost="yes" ax_boost_user_system_lib="$withval" fi else want_boost="yes" fi if test "x$want_boost" = "xyes"; then CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::System library is available" >&5 $as_echo_n "checking whether the Boost::System library is available... " >&6; } if ${ax_cv_boost_system+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu CXXFLAGS_SAVE=$CXXFLAGS CXXFLAGS= cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { boost::system::error_category *a = 0; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ax_cv_boost_system=yes else ax_cv_boost_system=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$CXXFLAGS_SAVE ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_system" >&5 $as_echo "$ax_cv_boost_system" >&6; } if test "x$ax_cv_boost_system" = "xyes"; then $as_echo "#define HAVE_BOOST_SYSTEM /**/" >>confdefs.h BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` LDFLAGS_SAVE=$LDFLAGS if test "x$ax_boost_user_system_lib" = "x"; then for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do ax_lib=${libextension} as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 $as_echo_n "checking for exit in -l$ax_lib... " >&6; } if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-l$ax_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char exit (); int main () { return exit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$as_ac_Lib=yes" else eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break else link_system="no" fi done if test "x$link_system" != "xyes"; then for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do ax_lib=${libextension} as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 $as_echo_n "checking for exit in -l$ax_lib... " >&6; } if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-l$ax_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char exit (); int main () { return exit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$as_ac_Lib=yes" else eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break else link_system="no" fi done fi else for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 $as_echo_n "checking for exit in -l$ax_lib... " >&6; } if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-l$ax_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char exit (); int main () { return exit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$as_ac_Lib=yes" else eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break else link_system="no" fi done fi if test "x$ax_lib" = "x"; then as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 fi if test "x$link_system" = "xno"; then as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 fi fi CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" fi if test -z "$BOOST_SYSTEM_LIB"; then : as_fn_error $? "Boost.System library not found. Try using --with-boost-system=lib" "$LINENO" 5 fi # Check whether --with-boost-chrono was given. if test "${with_boost_chrono+set}" = set; then : withval=$with_boost_chrono; if test "$withval" = "no"; then want_boost="no" elif test "$withval" = "yes"; then want_boost="yes" ax_boost_user_chrono_lib="" else want_boost="yes" ax_boost_user_chrono_lib="$withval" fi else want_boost="yes" fi if test "x$want_boost" = "xyes"; then CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::Chrono library is available" >&5 $as_echo_n "checking whether the Boost::Chrono library is available... " >&6; } if ${ax_cv_boost_chrono+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu CXXFLAGS_SAVE=$CXXFLAGS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { boost::chrono::system_clock::time_point time; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ax_cv_boost_chrono=yes else ax_cv_boost_chrono=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$CXXFLAGS_SAVE ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_chrono" >&5 $as_echo "$ax_cv_boost_chrono" >&6; } if test "x$ax_cv_boost_chrono" = "xyes"; then $as_echo "#define HAVE_BOOST_CHRONO /**/" >>confdefs.h BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` LDFLAGS_SAVE=$LDFLAGS if test "x$ax_boost_user_chrono_lib" = "x"; then for libextension in `ls $BOOSTLIBDIR/libboost_chrono*.so* $BOOSTLIBDIR/libboost_chrono*.dylib* $BOOSTLIBDIR/libboost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_chrono.*\)\.so.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 $as_echo_n "checking for exit in -l$ax_lib... " >&6; } if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-l$ax_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char exit (); int main () { return exit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$as_ac_Lib=yes" else eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : BOOST_CHRONO_LIB="-l$ax_lib"; link_chrono="yes"; break else link_chrono="no" fi done if test "x$link_chrono" != "xyes"; then for libextension in `ls $BOOSTLIBDIR/boost_chrono*.dll* $BOOSTLIBDIR/boost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_chrono.*\)\.dll.*$;\1;' -e 's;^\(boost_chrono.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 $as_echo_n "checking for exit in -l$ax_lib... " >&6; } if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-l$ax_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char exit (); int main () { return exit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$as_ac_Lib=yes" else eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : BOOST_CHRONO_LIB="-l$ax_lib"; link_chrono="yes"; break else link_chrono="no" fi done fi else for ax_lib in $ax_boost_user_chrono_lib boost_chrono-$ax_boost_user_chrono_lib; do as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 $as_echo_n "checking for exit in -l$ax_lib... " >&6; } if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-l$ax_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char exit (); int main () { return exit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$as_ac_Lib=yes" else eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : BOOST_CHRONO_LIB="-l$ax_lib"; link_chrono="yes"; break else link_chrono="no" fi done fi if test "x$ax_lib" = "x"; then as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 fi if test "x$link_chrono" = "xno"; then as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 fi fi CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" fi if test -z "$BOOST_CHRONO_LIB"; then : as_fn_error $? "Boost.Chrono library not found. Try using --with-boost-chrono=lib" "$LINENO" 5 fi # Check whether --with-boost-random was given. if test "${with_boost_random+set}" = set; then : withval=$with_boost_random; if test "$withval" = "no"; then want_boost="no" elif test "$withval" = "yes"; then want_boost="yes" ax_boost_user_random_lib="" else want_boost="yes" ax_boost_user_random_lib="$withval" fi else want_boost="yes" fi if test "x$want_boost" = "xyes"; then CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::Random library is available" >&5 $as_echo_n "checking whether the Boost::Random library is available... " >&6; } if ${ax_cv_boost_random+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu CXXFLAGS_SAVE=$CXXFLAGS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { boost::random::random_device()(); ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ax_cv_boost_random=yes else ax_cv_boost_random=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$CXXFLAGS_SAVE ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_random" >&5 $as_echo "$ax_cv_boost_random" >&6; } if test "x$ax_cv_boost_random" = "xyes"; then $as_echo "#define HAVE_BOOST_RANDOM /**/" >>confdefs.h BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` if test "x$ax_boost_user_random_lib" = "x"; then for libextension in `ls $BOOSTLIBDIR/libboost_random*.so* $BOOSTLIBDIR/libboost_random*.dylib* $BOOSTLIBDIR/libboost_random*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_random.*\)\.so.*$;\1;' -e 's;^lib\(boost_random.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_random.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 $as_echo_n "checking for exit in -l$ax_lib... " >&6; } if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-l$ax_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char exit (); int main () { return exit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$as_ac_Lib=yes" else eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : BOOST_RANDOM_LIB="-l$ax_lib"; link_random="yes"; break else link_random="no" fi done if test "x$link_random" != "xyes"; then for libextension in `ls $BOOSTLIBDIR/boost_random*.dll* $BOOSTLIBDIR/boost_random*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_random.*\)\.dll.*$;\1;' -e 's;^\(boost_random.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 $as_echo_n "checking for exit in -l$ax_lib... " >&6; } if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-l$ax_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char exit (); int main () { return exit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$as_ac_Lib=yes" else eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : BOOST_RANDOM_LIB="-l$ax_lib"; link_random="yes"; break else link_random="no" fi done fi else for ax_lib in $ax_boost_user_random_lib boost_random-$ax_boost_user_random_lib; do as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 $as_echo_n "checking for exit in -l$ax_lib... " >&6; } if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-l$ax_lib $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char exit (); int main () { return exit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$as_ac_Lib=yes" else eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : BOOST_RANDOM_LIB="-l$ax_lib"; link_random="yes"; break else link_random="no" fi done fi if test "x$ax_lib" = "x"; then as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 fi if test "x$link_random" = "xno"; then as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 fi fi CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" fi if test -z "$BOOST_RANDOM_LIB"; then : as_fn_error $? "Boost.Random library not found. Try using --with-boost-random=lib" "$LINENO" 5 fi CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS" LDFLAGS="$BOOST_LDFLAGS $LDFLAGS" LIBS="$BOOST_CHRONO_LIB $BOOST_RANDOM_LIB $LIBS" ############################################################################### # Checking for functions and other stuffs ############################################################################### $as_echo $as_echo "Checking for pkg-config:" if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.20 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi $as_echo $as_echo "Checking for functions and headers:" # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } if ${ac_cv_sys_largefile_CC+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext CC="$CC -n32" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 $as_echo "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if ${ac_cv_sys_file_offset_bits+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 $as_echo "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits _ACEOF ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } if ${ac_cv_sys_large_files+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 $as_echo "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _LARGE_FILES $ac_cv_sys_large_files _ACEOF ;; esac rm -rf conftest* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi for ac_func in clock_gettime do : ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime" if test "x$ac_cv_func_clock_gettime" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_CLOCK_GETTIME 1 _ACEOF else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5 $as_echo_n "checking for clock_gettime in -lrt... " >&6; } if ${ac_cv_lib_rt_clock_gettime+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clock_gettime (); int main () { return clock_gettime (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_rt_clock_gettime=yes else ac_cv_lib_rt_clock_gettime=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5 $as_echo "$ac_cv_lib_rt_clock_gettime" >&6; } if test "x$ac_cv_lib_rt_clock_gettime" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBRT 1 _ACEOF LIBS="-lrt $LIBS" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lposix4" >&5 $as_echo_n "checking for clock_gettime in -lposix4... " >&6; } if ${ac_cv_lib_posix4_clock_gettime+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lposix4 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clock_gettime (); int main () { return clock_gettime (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_posix4_clock_gettime=yes else ac_cv_lib_posix4_clock_gettime=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_posix4_clock_gettime" >&5 $as_echo "$ac_cv_lib_posix4_clock_gettime" >&6; } if test "x$ac_cv_lib_posix4_clock_gettime" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPOSIX4 1 _ACEOF LIBS="-lposix4 $LIBS" else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: clock_gettime function not found." >&5 $as_echo "$as_me: WARNING: clock_gettime function not found." >&2;} fi fi fi done COMPILETIME_OPTIONS="" ############################################################################### # Setting configure options ############################################################################### # Check whether --enable-logging was given. if test "${enable_logging+set}" = set; then : enableval=$enable_logging; ARG_ENABLE_LOGGING=$enableval else ARG_ENABLE_LOGGING=yes fi # Check whether --enable-debug was given. if test "${enable_debug+set}" = set; then : enableval=$enable_debug; ARG_ENABLE_DEBUG=$enableval ac_arg_enable_debug=$enableval else ARG_ENABLE_DEBUG=no ac_arg_enable_debug=no fi # Check whether --enable-dht was given. if test "${enable_dht+set}" = set; then : enableval=$enable_dht; ARG_ENABLE_DHT=$enableval else ARG_ENABLE_DHT=yes fi # Check whether --enable-encryption was given. if test "${enable_encryption+set}" = set; then : enableval=$enable_encryption; ARG_ENABLE_ENCRYPTION=$enableval else ARG_ENABLE_ENCRYPTION=yes fi # Check whether --enable-export-all was given. if test "${enable_export_all+set}" = set; then : enableval=$enable_export_all; ARG_ENABLE_FULL_EXPORT=$enableval else ARG_ENABLE_FULL_EXPORT=no fi # Check whether --enable-pool-allocators was given. if test "${enable_pool_allocators+set}" = set; then : enableval=$enable_pool_allocators; ARG_ENABLE_POOL_ALLOC=$enableval else ARG_ENABLE_POOL_ALLOC=yes fi # Check whether --enable-invariant-checks was given. if test "${enable_invariant_checks+set}" = set; then : enableval=$enable_invariant_checks; ARG_ENABLE_INVARIANT=$enableval else if test "x$ac_arg_enable_debug" = "xyes"; then : ARG_ENABLE_INVARIANT=yes else ARG_ENABLE_INVARIANT=no fi fi # Check whether --enable-deprecated-functions was given. if test "${enable_deprecated_functions+set}" = set; then : enableval=$enable_deprecated_functions; ARG_ENABLE_DEPRECATED=$enableval else ARG_ENABLE_DEPRECATED=yes fi # Check whether --enable-disk-stats was given. if test "${enable_disk_stats+set}" = set; then : enableval=$enable_disk_stats; ARG_ENABLE_DISK_STATS=$enableval else ARG_ENABLE_DISK_STATS=no fi # Check whether --enable-examples was given. if test "${enable_examples+set}" = set; then : enableval=$enable_examples; ARG_ENABLE_EXAMPLES=$enableval else ARG_ENABLE_EXAMPLES=no fi # Check whether --enable-tests was given. if test "${enable_tests+set}" = set; then : enableval=$enable_tests; ARG_ENABLE_TESTS=$enableval else ARG_ENABLE_TESTS=no fi # Check whether --enable-python-binding was given. if test "${enable_python_binding+set}" = set; then : enableval=$enable_python_binding; ARG_ENABLE_PYTHON_BINDING=$enableval else ARG_ENABLE_PYTHON_BINDING=no fi # Check whether --with-libiconv was given. if test "${with_libiconv+set}" = set; then : withval=$with_libiconv; ARG_WITH_LIBICONV=$withval else ARG_WITH_LIBICONV=no fi ############################################################################### # Checking configure options ############################################################################### $as_echo $as_echo "Checking build options:" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether deprecated functions should be enabled" >&5 $as_echo_n "checking whether deprecated functions should be enabled... " >&6; } case "$ARG_ENABLE_DEPRECATED" in #( "yes"|"on") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ;; #( "no"|"off") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "#define TORRENT_NO_DEPRECATE 1" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_NO_DEPRECATE " ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_DEPRECATED" >&5 $as_echo "$ARG_ENABLE_DEPRECATED" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_DEPRECATED\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether debug build should be enabled" >&5 $as_echo_n "checking whether debug build should be enabled... " >&6; } case "$ARG_ENABLE_DEBUG" in #( "yes") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define TORRENT_DEBUG 1" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_DEBUG " DEBUGFLAGS="-g" ;; #( "no") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "#define NDEBUG 1" >>confdefs.h #COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DNDEBUG " DEBUGFLAGS="-Os" ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_DEBUG" >&5 $as_echo "$ARG_ENABLE_DEBUG" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_DEBUG\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether invariant check should be enabled" >&5 $as_echo_n "checking whether invariant check should be enabled... " >&6; } #depends: $ac_arg_enable_debug case "$ARG_ENABLE_INVARIANT" in #( "yes"|"on") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } if test "x$ac_arg_enable_debug" = "xno"; then : as_fn_error $? "invariant-checks: this setting only affects debug build. Try using --enable-debug." "$LINENO" 5 fi ;; #( "no"|"off") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if test "x$ac_arg_enable_debug" = "xyes"; then : $as_echo "#define TORRENT_DISABLE_INVARIANT_CHECKS 1" >>confdefs.h fi ;; #( "full") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: full" >&5 $as_echo "full" >&6; } if test "x$ac_arg_enable_debug" = "xyes"; then : $as_echo "#define TORRENT_EXPENSIVE_INVARIANT_CHECKS 1" >>confdefs.h else as_fn_error $? "invariant-checks: this setting only affects debug build. Try using --enable-debug." "$LINENO" 5 fi ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_INVARIANT" >&5 $as_echo "$ARG_ENABLE_INVARIANT" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_INVARIANT\". Use either \"yes\", \"no\" or \"full\"." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether logging to disk should be enabled" >&5 $as_echo_n "checking whether logging to disk should be enabled... " >&6; } case "$ARG_ENABLE_LOGGING" in #( "yes"|"default") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ;; #( "no"|"none") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "#define TORRENT_DISABLE_LOGGING 1" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_DISABLE_LOGGING " ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_LOGGING" >&5 $as_echo "$ARG_ENABLE_LOGGING" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_LOGGING\". Use either \"yes\" or \"no\"" "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether disk activity logging should be enabled" >&5 $as_echo_n "checking whether disk activity logging should be enabled... " >&6; } case "$ARG_ENABLE_DISK_STATS" in #( "yes"|"on") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define TORRENT_DISK_STATS 1" >>confdefs.h ;; #( "no"|"off") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_DISK_STATS" >&5 $as_echo "$ARG_ENABLE_DISK_STATS" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_DISK_STATS\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac $as_echo $as_echo "Checking features to be enabled:" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether encryption support should be enabled" >&5 $as_echo_n "checking whether encryption support should be enabled... " >&6; } case "$ARG_ENABLE_ENCRYPTION" in #( "yes"|"on") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: encryption support: now checking for the OpenSSL library..." >&5 $as_echo "$as_me: encryption support: now checking for the OpenSSL library..." >&6;} found=false # Check whether --with-openssl was given. if test "${with_openssl+set}" = set; then : withval=$with_openssl; case "$withval" in "" | y | ye | yes | n | no) as_fn_error $? "Invalid --with-openssl value" "$LINENO" 5 ;; *) ssldirs="$withval" ;; esac else # if pkg-config is installed and openssl has installed a .pc file, # then use that information and don't search ssldirs # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"$PKG_CONFIG" != x""; then OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null` if test $? = 0; then OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null` OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default ssldirs if ! $found; then ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" fi fi # note that we #include , so the OpenSSL headers have to be in # an 'openssl' subdirectory if ! $found; then OPENSSL_INCLUDES= for ssldir in $ssldirs; do { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h in $ssldir" >&5 $as_echo_n "checking for openssl/ssl.h in $ssldir... " >&6; } if test -f "$ssldir/include/openssl/ssl.h"; then OPENSSL_INCLUDES="-I$ssldir/include" OPENSSL_LDFLAGS="-L$ssldir/lib" OPENSSL_LIBS="-lssl -lcrypto" found=true { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } break else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi done # if the file wasn't found, well, go ahead and try the link anyway -- maybe # it will just work! fi # try the preprocessor and linker with our new flags, # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiling and linking against OpenSSL works" >&5 $as_echo_n "checking whether compiling and linking against OpenSSL works... " >&6; } echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \ "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&5 save_LIBS="$LIBS" save_LDFLAGS="$LDFLAGS" save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" LIBS="$OPENSSL_LIBS $LIBS" CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { SSL_new(NULL) ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define TORRENT_USE_OPENSSL 1" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_USE_OPENSSL " else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "OpenSSL library not found. Try using --with-openssl=DIR or disabling encryption at all." "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; #( "no"|"off") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "#define TORRENT_DISABLE_ENCRYPTION 1" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_DISABLE_ENCRYPTION " ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_ENCRYPTION" >&5 $as_echo "$ARG_ENABLE_ENCRYPTION" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_ENCRYPTION\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether dht support should be enabled" >&5 $as_echo_n "checking whether dht support should be enabled... " >&6; } case "$ARG_ENABLE_DHT" in #( "yes"|"on") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ;; #( "no"|"off") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "#define TORRENT_DISABLE_DHT 1" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_DISABLE_DHT " ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_DHT" >&5 $as_echo "$ARG_ENABLE_DHT" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_DHT\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pool allocators should be enabled" >&5 $as_echo_n "checking whether pool allocators should be enabled... " >&6; } case "$ARG_ENABLE_POOL_ALLOC" in #( "yes"|"on") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ;; #( "no"|"off") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "#define TORRENT_DISABLE_POOL_ALLOCATOR 1" >>confdefs.h ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_POOL_ALLOC" >&5 $as_echo "$ARG_ENABLE_POOL_ALLOC" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_POOL_ALLOC\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac if test "x$ac_cv_hidden_visibility_attribute" = "xyes"; then : if test "x$ARG_ENABLE_FULL_EXPORT" = "xno"; then : CXXFLAGS="$CXXFLAGS -fvisibility=hidden -fvisibility-inlines-hidden" CFLAGS="$CFLAGS -fvisibility=hidden" LDFLAGS="$LDFLAGS -fvisibility=hidden -fvisibility-inlines-hidden" fi fi $as_echo $as_echo "Checking for extra build files:" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether example files should be built" >&5 $as_echo_n "checking whether example files should be built... " >&6; } case "$ARG_ENABLE_EXAMPLES" in #( "yes") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ;; #( "no") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_EXAMPLES" >&5 $as_echo "$ARG_ENABLE_EXAMPLES" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_EXAMPLES\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether test files should be built" >&5 $as_echo_n "checking whether test files should be built... " >&6; } case "$ARG_ENABLE_TESTS" in #( "yes") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define TORRENT_EXPORT_EXTRA 1" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_EXPORT_EXTRA " ;; #( "no") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_TESTS" >&5 $as_echo "$ARG_ENABLE_TESTS" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_TESTS\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether python bindings should be built" >&5 $as_echo_n "checking whether python bindings should be built... " >&6; } case "$ARG_ENABLE_PYTHON_BINDING" in #( "yes") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } if test -n "$PYTHON"; then # If the user set $PYTHON, use it and don't search something else. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $PYTHON version is >= 2.4" >&5 $as_echo_n "checking whether $PYTHON version is >= 2.4... " >&6; } prog="import sys # split strings by '.' and convert to numeric. Append some zeros # because we need at least 4 digits for the hex conversion. # map returns an iterator in Python 3.0 and a list in 2.x minver = list(map(int, '2.4'.split('.'))) + [0, 0, 0] minverhex = 0 # xrange is not present in Python 3.0 and range returns an iterator for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] sys.exit(sys.hexversion < minverhex)" if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5 ($PYTHON -c "$prog") >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Python interpreter is too old" "$LINENO" 5 fi am_display_PYTHON=$PYTHON else # Otherwise, try each interpreter until we find one that satisfies # VERSION. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a Python interpreter with version >= 2.4" >&5 $as_echo_n "checking for a Python interpreter with version >= 2.4... " >&6; } if ${am_cv_pathless_PYTHON+:} false; then : $as_echo_n "(cached) " >&6 else for am_cv_pathless_PYTHON in python python2 python3 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do test "$am_cv_pathless_PYTHON" = none && break prog="import sys # split strings by '.' and convert to numeric. Append some zeros # because we need at least 4 digits for the hex conversion. # map returns an iterator in Python 3.0 and a list in 2.x minver = list(map(int, '2.4'.split('.'))) + [0, 0, 0] minverhex = 0 # xrange is not present in Python 3.0 and range returns an iterator for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] sys.exit(sys.hexversion < minverhex)" if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5 ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then : break fi done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_pathless_PYTHON" >&5 $as_echo "$am_cv_pathless_PYTHON" >&6; } # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. if test "$am_cv_pathless_PYTHON" = none; then PYTHON=: else # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args. set dummy $am_cv_pathless_PYTHON; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PYTHON+:} false; then : $as_echo_n "(cached) " >&6 else case $PYTHON in [\\/]* | ?:[\\/]*) ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PYTHON=$ac_cv_path_PYTHON if test -n "$PYTHON"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 $as_echo "$PYTHON" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi am_display_PYTHON=$am_cv_pathless_PYTHON fi if test "$PYTHON" = :; then as_fn_error $? "Python interpreter not found." "$LINENO" 5 else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON version" >&5 $as_echo_n "checking for $am_display_PYTHON version... " >&6; } if ${am_cv_python_version+:} false; then : $as_echo_n "(cached) " >&6 else am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[:3])"` fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_version" >&5 $as_echo "$am_cv_python_version" >&6; } PYTHON_VERSION=$am_cv_python_version PYTHON_PREFIX='${prefix}' PYTHON_EXEC_PREFIX='${exec_prefix}' { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON platform" >&5 $as_echo_n "checking for $am_display_PYTHON platform... " >&6; } if ${am_cv_python_platform+:} false; then : $as_echo_n "(cached) " >&6 else am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"` fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_platform" >&5 $as_echo "$am_cv_python_platform" >&6; } PYTHON_PLATFORM=$am_cv_python_platform # Just factor out some code duplication. am_python_setup_sysconfig="\ import sys # Prefer sysconfig over distutils.sysconfig, for better compatibility # with python 3.x. See automake bug#10227. try: import sysconfig except ImportError: can_use_sysconfig = 0 else: can_use_sysconfig = 1 # Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: # try: from platform import python_implementation if python_implementation() == 'CPython' and sys.version[:3] == '2.7': can_use_sysconfig = 0 except ImportError: pass" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON script directory" >&5 $as_echo_n "checking for $am_display_PYTHON script directory... " >&6; } if ${am_cv_python_pythondir+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$prefix" = xNONE then am_py_prefix=$ac_default_prefix else am_py_prefix=$prefix fi am_cv_python_pythondir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') sys.stdout.write(sitedir)"` case $am_cv_python_pythondir in $am_py_prefix*) am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` ;; *) case $am_py_prefix in /usr|/System*) ;; *) am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages ;; esac ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pythondir" >&5 $as_echo "$am_cv_python_pythondir" >&6; } pythondir=$am_cv_python_pythondir pkgpythondir=\${pythondir}/$PACKAGE { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON extension module directory" >&5 $as_echo_n "checking for $am_display_PYTHON extension module directory... " >&6; } if ${am_cv_python_pyexecdir+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$exec_prefix" = xNONE then am_py_exec_prefix=$am_py_prefix else am_py_exec_prefix=$exec_prefix fi am_cv_python_pyexecdir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') sys.stdout.write(sitedir)"` case $am_cv_python_pyexecdir in $am_py_exec_prefix*) am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` ;; *) case $am_py_exec_prefix in /usr|/System*) ;; *) am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages ;; esac ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pyexecdir" >&5 $as_echo "$am_cv_python_pyexecdir" >&6; } pyexecdir=$am_cv_python_pyexecdir pkgpyexecdir=\${pyexecdir}/$PACKAGE fi # # Allow the use of a (user set) custom python version # # Extract the first word of "python[$PYTHON_VERSION]", so it can be a program name with args. set dummy python$PYTHON_VERSION; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PYTHON+:} false; then : $as_echo_n "(cached) " >&6 else case $PYTHON in [\\/]* | ?:[\\/]*) ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PYTHON=$ac_cv_path_PYTHON if test -n "$PYTHON"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 $as_echo "$PYTHON" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$PYTHON"; then as_fn_error $? "Cannot find python$PYTHON_VERSION in your system path" "$LINENO" 5 PYTHON_VERSION="" fi # # Check for a version of Python >= 2.1.0 # { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a version of Python >= '2.1.0'" >&5 $as_echo_n "checking for a version of Python >= '2.1.0'... " >&6; } ac_supports_python_ver=`$PYTHON -c "import sys; \ ver = sys.version.split ()[0]; \ print (ver >= '2.1.0')"` if test "$ac_supports_python_ver" != "True"; then if test -z "$PYTHON_NOVERSIONCHECK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? " This version of the AC_PYTHON_DEVEL macro doesn't work properly with versions of Python before 2.1.0. You may need to re-run configure, setting the variables PYTHON_CPPFLAGS, PYTHON_LIBS, PYTHON_SITE_PKG, PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. Moreover, to disable this check, set PYTHON_NOVERSIONCHECK to something else than an empty string. See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: skip at user request" >&5 $as_echo "skip at user request" >&6; } fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi # # if the macro parameter ``version'' is set, honour it # if test -n ">= '2.4'"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a version of Python >= '2.4'" >&5 $as_echo_n "checking for a version of Python >= '2.4'... " >&6; } ac_supports_python_ver=`$PYTHON -c "import sys; \ ver = sys.version.split ()[0]; \ print (ver >= '2.4')"` if test "$ac_supports_python_ver" = "True"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "this package requires Python >= '2.4'. If you have it installed, but it isn't the default Python interpreter in your system path, please pass the PYTHON_VERSION variable to configure. See \`\`configure --help'' for reference. " "$LINENO" 5 PYTHON_VERSION="" fi fi # # Check if you have distutils, else fail # { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 $as_echo_n "checking for the distutils Python package... " >&6; } ac_distutils_result=`$PYTHON -c "import distutils" 2>&1 | grep -v '^\[0-9]\{1,\} refs\'` if test -z "$ac_distutils_result"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "cannot import Python module \"distutils\". Please check your Python installation. The error was: $ac_distutils_result" "$LINENO" 5 PYTHON_VERSION="" fi # # Check for Python include path # { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python include path" >&5 $as_echo_n "checking for Python include path... " >&6; } if test -z "$PYTHON_CPPFLAGS"; then python_path=`$PYTHON -c "import distutils.sysconfig; \ print (distutils.sysconfig.get_python_inc ());"` plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ print (distutils.sysconfig.get_python_inc (plat_specific=1));"` if test -n "${python_path}"; then if test "${plat_python_path}" != "${python_path}"; then python_path="-I$python_path -I$plat_python_path" else python_path="-I$python_path" fi fi PYTHON_CPPFLAGS=$python_path fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_CPPFLAGS" >&5 $as_echo "$PYTHON_CPPFLAGS" >&6; } # # Check for Python library path # { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python library path" >&5 $as_echo_n "checking for Python library path... " >&6; } if test -z "$PYTHON_LIBS"; then # (makes two attempts to ensure we've got a version number # from the interpreter) ac_python_version=`cat<>confdefs.h <<_ACEOF #define HAVE_PYTHON "$ac_python_version" _ACEOF # First, the library directory: ac_python_libdir=`cat<&5 $as_echo "$PYTHON_LIBS" >&6; } # # Check for site packages # { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python site-packages path" >&5 $as_echo_n "checking for Python site-packages path... " >&6; } if test -z "$PYTHON_SITE_PKG"; then PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ print (distutils.sysconfig.get_python_lib(0,0));"` fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_SITE_PKG" >&5 $as_echo "$PYTHON_SITE_PKG" >&6; } # # libraries which must be linked in when embedding # { $as_echo "$as_me:${as_lineno-$LINENO}: checking python extra libraries" >&5 $as_echo_n "checking python extra libraries... " >&6; } if test -z "$PYTHON_EXTRA_LDFLAGS"; then PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \ conf = distutils.sysconfig.get_config_var; \ print (conf('LIBS') + ' ' + conf('SYSLIBS'))"` fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_EXTRA_LDFLAGS" >&5 $as_echo "$PYTHON_EXTRA_LDFLAGS" >&6; } # # linking flags needed when embedding # { $as_echo "$as_me:${as_lineno-$LINENO}: checking python extra linking flags" >&5 $as_echo_n "checking python extra linking flags... " >&6; } if test -z "$PYTHON_EXTRA_LIBS"; then PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \ conf = distutils.sysconfig.get_config_var; \ print (conf('LINKFORSHARED'))"` fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_EXTRA_LIBS" >&5 $as_echo "$PYTHON_EXTRA_LIBS" >&6; } # # final check to see if everything compiles alright # { $as_echo "$as_me:${as_lineno-$LINENO}: checking consistency of all components of python development environment" >&5 $as_echo_n "checking consistency of all components of python development environment... " >&6; } # save current global flags ac_save_LIBS="$LIBS" ac_save_LDFLAGS="$LDFLAGS" ac_save_CPPFLAGS="$CPPFLAGS" LIBS="$ac_save_LIBS $PYTHON_LIBS $PYTHON_EXTRA_LIBS $PYTHON_EXTRA_LIBS" LDFLAGS="$ac_save_LDFLAGS $PYTHON_EXTRA_LDFLAGS" CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { Py_Initialize(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : pythonexists=yes else pythonexists=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # turn back to default flags CPPFLAGS="$ac_save_CPPFLAGS" LIBS="$ac_save_LIBS" LDFLAGS="$ac_save_LDFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pythonexists" >&5 $as_echo "$pythonexists" >&6; } if test ! "x$pythonexists" = "xyes"; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? " Could not link test program to Python. Maybe the main Python library has been installed in some non-standard library path. If so, pass it to configure, via the LIBS environment variable. Example: ./configure LIBS=\"-L/usr/non-standard-path/python/lib\" ============================================================================ ERROR! You probably have to install the development version of the Python package for your distribution. The exact name of this package varies among them. ============================================================================ See \`config.log' for more details" "$LINENO" 5; } PYTHON_VERSION="" fi # # all done! # ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_boost_python_save_CPPFLAGS="$CPPFLAGS" ax_boost_python_save_LDFLAGS="$LDFLAGS" ax_boost_python_save_LIBS="$LIBS" if test "x$PYTHON_CPPFLAGS" != "x"; then CPPFLAGS="$PYTHON_CPPFLAGS $CPPFLAGS" fi # Versions of AX_PYTHON_DEVEL() before serial 18 provided PYTHON_LDFLAGS # instead of PYTHON_LIBS, so this is just here for compatibility. if test "x$PYTHON_LDFLAGS" != "x"; then LDFLAGS="$PYTHON_LDFLAGS $LDFLAGS" fi # Note: Only versions of AX_PYTHON_DEVEL() since serial 18 provide PYTHON_LIBS # instead of PYTHON_LDFLAGS. if test "x$PYTHON_LIBS" != "x"; then LIBS="$PYTHON_LIBS $LIBS" fi if test "x$BOOST_CPPFLAGS" != "x"; then CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS" fi if test "x$BOOST_LDFLAGS" != "x"; then LDFLAGS="$BOOST_LDFLAGS $LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::Python library is available" >&5 $as_echo_n "checking whether the Boost::Python library is available... " >&6; } if ${ac_cv_boost_python+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include BOOST_PYTHON_MODULE(test) { throw "Boost::Python test."; } int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_boost_python=yes else ac_cv_boost_python=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_boost_python" >&5 $as_echo "$ac_cv_boost_python" >&6; } if test "$ac_cv_boost_python" = "yes"; then $as_echo "#define HAVE_BOOST_PYTHON /**/" >>confdefs.h ax_python_lib=boost_python # Check whether --with-boost-python was given. if test "${with_boost_python+set}" = set; then : withval=$with_boost_python; if test "x$with_boost_python" != "xno" -a "x$with_boost_python" != "xyes"; then ax_python_lib=$with_boost_python ax_boost_python_lib=boost_python-$with_boost_python fi fi BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` for ax_lib in $ax_python_lib $ax_boost_python_lib `ls $BOOSTLIBDIR/libboost_python*.so* $BOOSTLIBDIR/libboost_python*.dylib* $BOOSTLIBDIR/libboost_python*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_python.*\)\.so.*$;\1;' -e 's;^lib\(boost_python.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_python.*\)\.a.*$;\1;' ` boost_python boost_python3; do as_ax_Lib=`$as_echo "ax_cv_lib_$ax_lib''_main" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $ax_lib is the correct library" >&5 $as_echo_n "checking whether $ax_lib is the correct library... " >&6; } if eval \${$as_ax_Lib+:} false; then : $as_echo_n "(cached) " >&6 else LIBS="-l$ax_lib $ax_boost_python_save_LIBS $PYTHON_LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : eval "$as_ax_Lib=yes" else eval "$as_ax_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$as_ax_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ax_Lib"\" = x"yes"; then : BOOST_PYTHON_LIB=$ax_lib break fi done fi CPPFLAGS="$ax_boost_python_save_CPPFLAGS" LDFLAGS="$ax_boost_python_save_LDFLAGS" LIBS="$ax_boost_python_save_LIBS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -z "$BOOST_PYTHON_LIB"; then : as_fn_error $? "Boost.Python library not found. Try using --with-boost-python=lib." "$LINENO" 5 fi BOOST_PYTHON_LIB="-l$BOOST_PYTHON_LIB" ;; #( "no") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_ENABLE_PYTHON_BINDING" >&5 $as_echo "$ARG_ENABLE_PYTHON_BINDING" >&6; } as_fn_error $? "Unknown option \"$ARG_ENABLE_PYTHON_BINDING\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac $as_echo $as_echo "Checking for external libraries:" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to link against system libiconv" >&5 $as_echo_n "checking whether to link against system libiconv... " >&6; } if test "X$prefix" = "XNONE"; then acl_final_prefix="$ac_default_prefix" else acl_final_prefix="$prefix" fi if test "X$exec_prefix" = "XNONE"; then acl_final_exec_prefix='${prefix}' else acl_final_exec_prefix="$exec_prefix" fi acl_save_prefix="$prefix" prefix="$acl_final_prefix" eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" prefix="$acl_save_prefix" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shared library run path origin" >&5 $as_echo_n "checking for shared library run path origin... " >&6; } if ${acl_cv_rpath+:} false; then : $as_echo_n "(cached) " >&6 else CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh . ./conftest.sh rm -f ./conftest.sh acl_cv_rpath=done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_rpath" >&5 $as_echo "$acl_cv_rpath" >&6; } wl="$acl_cv_wl" libext="$acl_cv_libext" shlibext="$acl_cv_shlibext" hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" hardcode_direct="$acl_cv_hardcode_direct" hardcode_minus_L="$acl_cv_hardcode_minus_L" use_additional=yes acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" # Check whether --with-libiconv-prefix was given. if test "${with_libiconv_prefix+set}" = set; then : withval=$with_libiconv_prefix; if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" else additional_includedir="$withval/include" additional_libdir="$withval/lib" fi fi fi LIBICONV= LTLIBICONV= INCICONV= rpathdirs= ltrpathdirs= names_already_handled= names_next_round='iconv ' while test -n "$names_next_round"; do names_this_round="$names_next_round" names_next_round= for name in $names_this_round; do already_handled= for n in $names_already_handled; do if test "$n" = "$name"; then already_handled=yes break fi done if test -z "$already_handled"; then names_already_handled="$names_already_handled $name" uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` eval value=\"\$HAVE_LIB$uppername\" if test -n "$value"; then if test "$value" = yes; then eval value=\"\$LIB$uppername\" test -z "$value" || LIBICONV="${LIBICONV}${LIBICONV:+ }$value" eval value=\"\$LTLIB$uppername\" test -z "$value" || LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$value" else : fi else found_dir= found_la= found_so= found_a= if test $use_additional = yes; then if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then found_dir="$additional_libdir" found_so="$additional_libdir/lib$name.$shlibext" if test -f "$additional_libdir/lib$name.la"; then found_la="$additional_libdir/lib$name.la" fi else if test -f "$additional_libdir/lib$name.$libext"; then found_dir="$additional_libdir" found_a="$additional_libdir/lib$name.$libext" if test -f "$additional_libdir/lib$name.la"; then found_la="$additional_libdir/lib$name.la" fi fi fi fi if test "X$found_dir" = "X"; then for x in $LDFLAGS $LTLIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" case "$x" in -L*) dir=`echo "X$x" | sed -e 's/^X-L//'` if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then found_dir="$dir" found_so="$dir/lib$name.$shlibext" if test -f "$dir/lib$name.la"; then found_la="$dir/lib$name.la" fi else if test -f "$dir/lib$name.$libext"; then found_dir="$dir" found_a="$dir/lib$name.$libext" if test -f "$dir/lib$name.la"; then found_la="$dir/lib$name.la" fi fi fi ;; esac if test "X$found_dir" != "X"; then break fi done fi if test "X$found_dir" != "X"; then LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$found_dir -l$name" if test "X$found_so" != "X"; then if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" else haveit= for x in $ltrpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $found_dir" fi if test "$hardcode_direct" = yes; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" else if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" haveit= for x in $rpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $found_dir" fi else haveit= for x in $LDFLAGS $LIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-L$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir" fi if test "$hardcode_minus_L" != no; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" else LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name" fi fi fi fi else if test "X$found_a" != "X"; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_a" else LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir -l$name" fi fi additional_includedir= case "$found_dir" in */lib | */lib/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'` additional_includedir="$basedir/include" ;; esac if test "X$additional_includedir" != "X"; then if test "X$additional_includedir" != "X/usr/include"; then haveit= if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then for x in $CPPFLAGS $INCICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_includedir"; then INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir" fi fi fi fi fi if test -n "$found_la"; then save_libdir="$libdir" case "$found_la" in */* | *\\*) . "$found_la" ;; *) . "./$found_la" ;; esac libdir="$save_libdir" for dep in $dependency_libs; do case "$dep" in -L*) additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` if test "X$additional_libdir" != "X/usr/lib"; then haveit= if test "X$additional_libdir" = "X/usr/local/lib"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then haveit= for x in $LDFLAGS $LIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir" fi fi haveit= for x in $LDFLAGS $LTLIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir" fi fi fi fi ;; -R*) dir=`echo "X$dep" | sed -e 's/^X-R//'` if test "$enable_rpath" != no; then haveit= for x in $rpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $dir" fi haveit= for x in $ltrpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $dir" fi fi ;; -l*) names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` ;; *.la) names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` ;; *) LIBICONV="${LIBICONV}${LIBICONV:+ }$dep" LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$dep" ;; esac done fi else LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name" LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-l$name" fi fi fi done done if test "X$rpathdirs" != "X"; then if test -n "$hardcode_libdir_separator"; then alldirs= for found_dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir" done acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIBICONV="${LIBICONV}${LIBICONV:+ }$flag" else for found_dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$found_dir" eval flag=\"$hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIBICONV="${LIBICONV}${LIBICONV:+ }$flag" done fi fi if test "X$ltrpathdirs" != "X"; then for found_dir in $ltrpathdirs; do LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-R$found_dir" done fi case "$ARG_WITH_LIBICONV" in #( "yes") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } am_save_CPPFLAGS="$CPPFLAGS" for element in $INCICONV; do haveit= for x in $CPPFLAGS; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X$element"; then haveit=yes break fi done if test -z "$haveit"; then CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element" fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv" >&5 $as_echo_n "checking for iconv... " >&6; } if ${am_cv_func_iconv+:} false; then : $as_echo_n "(cached) " >&6 else am_cv_func_iconv="no, consider installing GNU libiconv" am_cv_lib_iconv=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : am_cv_func_iconv=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$am_cv_func_iconv" != yes; then am_save_LIBS="$LIBS" LIBS="$LIBS $LIBICONV" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : am_cv_lib_iconv=yes am_cv_func_iconv=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$am_save_LIBS" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv" >&5 $as_echo "$am_cv_func_iconv" >&6; } if test "$am_cv_func_iconv" = yes; then $as_echo "#define HAVE_ICONV 1" >>confdefs.h fi if test "$am_cv_lib_iconv" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiconv" >&5 $as_echo_n "checking how to link with libiconv... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBICONV" >&5 $as_echo "$LIBICONV" >&6; } else CPPFLAGS="$am_save_CPPFLAGS" LIBICONV= LTLIBICONV= fi if test "$am_cv_func_iconv" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv declaration" >&5 $as_echo_n "checking for iconv declaration... " >&6; } if ${am_cv_proto_iconv+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include extern #ifdef __cplusplus "C" #endif #if defined(__STDC__) || defined(__cplusplus) size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); #else size_t iconv(); #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : am_cv_proto_iconv_arg1="" else am_cv_proto_iconv_arg1="const" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);" fi am_cv_proto_iconv=`echo "$am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${ac_t:- }$am_cv_proto_iconv" >&5 $as_echo "${ac_t:- }$am_cv_proto_iconv" >&6; } cat >>confdefs.h <<_ACEOF #define ICONV_CONST $am_cv_proto_iconv_arg1 _ACEOF fi if test "x$am_cv_func_iconv" = "xyes"; then : ICONV_LIBS=$LIBICONV LIBS="$ICONV_LIBS $LIBS" else as_fn_error $? "Iconv library not found." "$LINENO" 5 fi ;; #( "no") : ARG_WITH_LIBICONV="no" ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARG_WITH_LIBICONV" >&5 $as_echo "$ARG_WITH_LIBICONV" >&6; } as_fn_error $? "Unknown option \"$ARG_WITH_LIBICONV\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac ############################################################################### # Setting conditional variables for Makefiles ############################################################################### if test "x$ARG_ENABLE_DHT" = "xyes" -o "x$ARG_ENABLE_DHT" = "xlogging" ; then ENABLE_DHT_TRUE= ENABLE_DHT_FALSE='#' else ENABLE_DHT_TRUE='#' ENABLE_DHT_FALSE= fi if test "x$ARG_ENABLE_EXAMPLES" = "xyes"; then ENABLE_EXAMPLES_TRUE= ENABLE_EXAMPLES_FALSE='#' else ENABLE_EXAMPLES_TRUE='#' ENABLE_EXAMPLES_FALSE= fi if test "x$ARG_ENABLE_TESTS" = "xyes"; then ENABLE_TESTS_TRUE= ENABLE_TESTS_FALSE='#' else ENABLE_TESTS_TRUE='#' ENABLE_TESTS_FALSE= fi if test "x$ARG_ENABLE_PYTHON_BINDING" = "xyes"; then ENABLE_PYTHON_BINDING_TRUE= ENABLE_PYTHON_BINDING_FALSE='#' else ENABLE_PYTHON_BINDING_TRUE='#' ENABLE_PYTHON_BINDING_FALSE= fi if test "x$ARG_ENABLE_ENCRYPTION" = "xyes" -o "x$ARG_ENABLE_ENCRYPTION" = "xon" ; then WITH_OPENSSL_TRUE= WITH_OPENSSL_FALSE='#' else WITH_OPENSSL_TRUE='#' WITH_OPENSSL_FALSE= fi ############################################################################### # Other useful stuff ############################################################################### # this works around a bug in asio in boost-1.39 # see: https://svn.boost.org/trac/boost/ticket/3095 $as_echo "#define BOOST_ASIO_HASH_MAP_BUCKETS 1021" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DBOOST_ASIO_HASH_MAP_BUCKETS=1021 " $as_echo "#define BOOST_EXCEPTION_DISABLE 1" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DBOOST_EXCEPTION_DISABLE " $as_echo "#define BOOST_ASIO_ENABLE_CANCELIO 1" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DBOOST_ASIO_ENABLE_CANCELIO " if test "x$PYTHON_INSTALL_PARAMS" = "x"; then : PYTHON_INSTALL_PARAMS='--prefix=$(DESTDIR)$(prefix)' fi if test "x$enable_shared" = "xyes"; then : $as_echo "#define TORRENT_BUILDING_SHARED 1" >>confdefs.h COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_LINKING_SHARED " fi # Disable deprecated warnings for the Python binding builds. PYTHON_CXXFLAGS="${PYTHON_CXXFLAGS} -Wno-deprecated-declarations" # Try to guess real git revision if any, fallback to hardcoded otherwise GIT_REVISION=`git log -1 --format=format:%h 2>/dev/null` if test -z "$GIT_REVISION"; then : GIT_REVISION=`sed -n -e "s/^#define LIBTORRENT_REVISION \"\([0-9a-z]*\)\"$/\1/p" $(dirname $0)/include/libtorrent/version.hpp` fi ############################################################################### # Generating Makefiles ############################################################################### $as_echo $as_echo "Generating Makefiles:" ac_config_files="$ac_config_files Makefile src/Makefile include/libtorrent/Makefile examples/Makefile test/Makefile tools/Makefile bindings/Makefile bindings/python/Makefile bindings/python/link_flags bindings/python/compile_flags libtorrent-rasterbar.pc" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_DHT_TRUE}" && test -z "${ENABLE_DHT_FALSE}"; then as_fn_error $? "conditional \"ENABLE_DHT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_EXAMPLES_TRUE}" && test -z "${ENABLE_EXAMPLES_FALSE}"; then as_fn_error $? "conditional \"ENABLE_EXAMPLES\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_TESTS_TRUE}" && test -z "${ENABLE_TESTS_FALSE}"; then as_fn_error $? "conditional \"ENABLE_TESTS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_PYTHON_BINDING_TRUE}" && test -z "${ENABLE_PYTHON_BINDING_FALSE}"; then as_fn_error $? "conditional \"ENABLE_PYTHON_BINDING\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_OPENSSL_TRUE}" && test -z "${WITH_OPENSSL_FALSE}"; then as_fn_error $? "conditional \"WITH_OPENSSL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by libtorrent-rasterbar $as_me 1.1.13, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Configuration commands: $config_commands Report bugs to . libtorrent-rasterbar home page: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ libtorrent-rasterbar config.status 1.1.13 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } # Quote evaled strings. for var in SHELL \ ECHO \ PATH_SEPARATOR \ SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ file_magic_glob \ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ AR_FLAGS \ archiver_list_spec \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_import \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ lt_cv_nm_interface \ nm_file_list_spec \ lt_cv_truncate_bin \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_pic \ lt_prog_compiler_wl \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ MANIFEST_TOOL \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_separator \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ install_override_mode \ finish_eval \ old_striplib \ striplib \ compiler_lib_search_dirs \ predep_objects \ postdep_objects \ predeps \ postdeps \ compiler_lib_search_path \ LD_CXX \ reload_flag_CXX \ compiler_CXX \ lt_prog_compiler_no_builtin_flag_CXX \ lt_prog_compiler_pic_CXX \ lt_prog_compiler_wl_CXX \ lt_prog_compiler_static_CXX \ lt_cv_prog_compiler_c_o_CXX \ export_dynamic_flag_spec_CXX \ whole_archive_flag_spec_CXX \ compiler_needs_object_CXX \ with_gnu_ld_CXX \ allow_undefined_flag_CXX \ no_undefined_flag_CXX \ hardcode_libdir_flag_spec_CXX \ hardcode_libdir_separator_CXX \ exclude_expsyms_CXX \ include_expsyms_CXX \ file_list_spec_CXX \ compiler_lib_search_dirs_CXX \ predep_objects_CXX \ postdep_objects_CXX \ predeps_CXX \ postdeps_CXX \ compiler_lib_search_path_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postlink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ configure_time_dlsearch_path \ configure_time_lt_sys_library_path \ reload_cmds_CXX \ old_archive_cmds_CXX \ old_archive_from_new_cmds_CXX \ old_archive_from_expsyms_cmds_CXX \ archive_cmds_CXX \ archive_expsym_cmds_CXX \ module_cmds_CXX \ module_expsym_cmds_CXX \ export_symbols_cmds_CXX \ prelink_cmds_CXX \ postlink_cmds_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done ac_aux_dir='$ac_aux_dir' # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "include/libtorrent/Makefile") CONFIG_FILES="$CONFIG_FILES include/libtorrent/Makefile" ;; "examples/Makefile") CONFIG_FILES="$CONFIG_FILES examples/Makefile" ;; "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; "tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;; "bindings/Makefile") CONFIG_FILES="$CONFIG_FILES bindings/Makefile" ;; "bindings/python/Makefile") CONFIG_FILES="$CONFIG_FILES bindings/python/Makefile" ;; "bindings/python/link_flags") CONFIG_FILES="$CONFIG_FILES bindings/python/link_flags" ;; "bindings/python/compile_flags") CONFIG_FILES="$CONFIG_FILES bindings/python/compile_flags" ;; "libtorrent-rasterbar.pc") CONFIG_FILES="$CONFIG_FILES libtorrent-rasterbar.pc" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`$as_dirname -- "$mf" || $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$mf" : 'X\(//\)[^/]' \| \ X"$mf" : 'X\(//\)$' \| \ X"$mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$file" : 'X\(//\)[^/]' \| \ X"$file" : 'X\(//\)$' \| \ X"$file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir=$dirpart/$fdir; as_fn_mkdir_p # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ;; "libtool":C) # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool 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, see . # The names of the tagged configurations supported by this script. available_tags='CXX ' # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # Whether or not to build static libraries. build_old_libs=$enable_static # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # Shared archive member basename,for filename based shared library versioning on AIX. shared_archive_member_spec=$shared_archive_member_spec # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that protects backslashes. ECHO=$lt_ECHO # The PATH separator for the build system. PATH_SEPARATOR=$lt_PATH_SEPARATOR # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # convert \$build file names to \$host format. to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method = "file_magic". file_magic_cmd=$lt_file_magic_cmd # How to find potential files when deplibs_check_method = "file_magic". file_magic_glob=$lt_file_magic_glob # Find potential files using nocaseglob when deplibs_check_method = "file_magic". want_nocaseglob=$lt_want_nocaseglob # DLL creation program. DLLTOOL=$lt_DLLTOOL # Command to associate shared and link libraries. sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR # Flags to create an archive. AR_FLAGS=$lt_AR_FLAGS # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # Whether to use a lock for old archive extraction. lock_old_archive_extraction=$lock_old_archive_extraction # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm into a list of symbols to manually relocate. global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # The name lister interface. nm_interface=$lt_lt_cv_nm_interface # Specify filename containing input files for \$NM. nm_file_list_spec=$lt_nm_file_list_spec # The root where to search for dependent libraries,and where our libraries should be installed. lt_sysroot=$lt_sysroot # Command to truncate a binary pipe. lt_truncate_bin=$lt_lt_cv_truncate_bin # The name of the directory that contains temporary libtool files. objdir=$objdir # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Manifest tool. MANIFEST_TOOL=$lt_MANIFEST_TOOL # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Permission mode override for installation of shared libraries. install_override_mode=$lt_install_override_mode # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Detected run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path # Explicit LT_SYS_LIBRARY_PATH set during ./configure time. configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects postdep_objects=$lt_postdep_objects predeps=$lt_predeps postdeps=$lt_postdeps # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain=$ac_aux_dir/ltmain.sh # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" cat <<_LT_EOF >> "$ofile" # ### BEGIN LIBTOOL TAG CONFIG: CXX # The linker used to build libraries. LD=$lt_LD_CXX # How to create reloadable object files. reload_flag=$lt_reload_flag_CXX reload_cmds=$lt_reload_cmds_CXX # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds_CXX # A language specific compiler. CC=$lt_compiler_CXX # Is the compiler the GNU compiler? with_gcc=$GCC_CXX # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic_CXX # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl_CXX # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static_CXX # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc_CXX # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object_CXX # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds_CXX archive_expsym_cmds=$lt_archive_expsym_cmds_CXX # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds_CXX module_expsym_cmds=$lt_module_expsym_cmds_CXX # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld_CXX # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag_CXX # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag_CXX # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct_CXX # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute_CXX # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L_CXX # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic_CXX # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath_CXX # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs_CXX # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols_CXX # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds_CXX # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms_CXX # Symbols that must always be exported. include_expsyms=$lt_include_expsyms_CXX # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds_CXX # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds_CXX # Specify filename containing input files. file_list_spec=$lt_file_list_spec_CXX # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action_CXX # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects_CXX postdep_objects=$lt_postdep_objects_CXX predeps=$lt_predeps_CXX postdeps=$lt_postdeps_CXX # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path_CXX # ### END LIBTOOL TAG CONFIG: CXX _LT_EOF ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi ############################################################################### # Generating config.report ############################################################################### $as_echo $as_echo "Configure script has finished system check." $as_echo cat > config.report << END Config results: -=-=-=-=-=-=-=-=- Package: name: ${PACKAGE_NAME} version: ${PACKAGE_VERSION} git revision: ${GIT_REVISION} Build environment: build system: ${build} host system: ${host} target system: ${target} Compiler and linker flags: CPPFlags: ${CPPFLAGS} CFlags: ${CFLAGS} CXXFlags: ${CXXFLAGS} LDFlags: ${LDFLAGS} Libs: ${LIBS} Defs: ${DEFS} Build options: deprecated functions: ${ARG_ENABLE_DEPRECATED:-yes} debug build: ${ARG_ENABLE_DEBUG:-no} invariant checks: ${ARG_ENABLE_INVARIANT:-no} logging support: ${ARG_ENABLE_LOGGING:-yes} disk statistics: ${ARG_ENABLE_DISK_STATS:-no} Features: encryption support: ${ARG_ENABLE_ENCRYPTION:-yes} dht support: ${ARG_ENABLE_DHT:-yes} pool allocators: ${ARG_ENABLE_POOL_ALLOC:-yes} Extra builds: examples: ${ARG_ENABLE_EXAMPLES:-no} tests: ${ARG_ENABLE_TESTS:-no} python bindings: ${ARG_ENABLE_PYTHON_BINDING:-no} Pthread library: CFlags: ${PTHREAD_CFLAGS} Libs: ${PTHREAD_LIBS} Boost libraries: version: ${BOOST_VERSION} CPPFlags: ${BOOST_CPPFLAGS} LDFlags: ${BOOST_LDFLAGS} boost.system: ${BOOST_SYSTEM_LIB} boost.chrono: ${BOOST_CHRONO_LIB} boost.random: ${BOOST_RANDOM_LIB} END if test "x$ARG_ENABLE_PYTHON_BINDING" = "xyes"; then : cat >> config.report << END boost.python: ${BOOST_PYTHON_LIB} Python environment: -automake- binary: ${PYTHON} version: ${PYTHON_VERSION} platform: ${PYTHON_PLATFORM} prefix: ${PYTHON_PREFIX} exec_prefix: ${PYTHON_EXEC_PREFIX} pythondir: ${pythondir} pkgpythondir: ${pkgpythondir} pyexecdir: ${pyexecdir} pkgpyexecdir: ${pkgpyexecdir} -m4- cppflags: ${PYTHON_CPPFLAGS} ldflags: ${PYTHON_LDFLAGS} extra libs: ${PYTHON_EXTRA_LIBS} END fi cat >> config.report << END External libraries: system libiconv: ${ARG_WITH_LIBICONV:-no} END if test "x$ARG_WITH_LIBICONV" = "xyes"; then : cat >> config.report << END Iconv library: Iconv Libs: ${ICONV_LIBS} END fi if test "x$ARG_ENABLE_ENCRYPTION" = "xyes"; then : cat >> config.report << END OpenSSL library: OpenSSL Libs: ${OPENSSL_LIBS} OpenSSL LDFlags: ${OPENSSL_LDFLAGS} OpenSSL Includes: ${OPENSSL_INCLUDES} END fi cat config.report $as_echo $as_echo "Type 'make' to compile $PACKAGE_STRING" $as_echo "or type 'make V=1' for verbose compiling" $as_echo "and then 'make install' to install it into $prefix" $as_echo libtorrent-rasterbar-1.1.13/configure.ac000066400000000000000000000536071351156116000202440ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. # $Id$ AC_PREREQ([2.63]) AC_INIT([libtorrent-rasterbar],[1.1.13],[arvid@libtorrent.org], [libtorrent-rasterbar],[http://www.libtorrent.org]) AC_CONFIG_SRCDIR([src/torrent.cpp]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) # Silencing build output (automake-1.11) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) ############################################################################### dnl --------------------------------------------------------------------------- dnl interface version info dnl --------------------------------------------------------------------------- dnl Advanced information about versioning: dnl * "Writing shared libraries" by Mike Hearn dnl http://navi.cx/~mike/writing-shared-libraries.html dnl * libtool.info chapter "Versioning" dnl * libtool.info chapter "Updating library version information" dnl --------------------------------------------------------------------------- dnl Versioning: dnl - CURRENT (Major): Increment if the interface has changes. AGE is always dnl *changed* at the same time. dnl - AGE (Micro): Increment if any interfaces have been added; set to 0 dnl if any interfaces have been removed. Removal has dnl precedence over adding, so set to 0 if both happened. dnl It denotes upward compatibility. dnl - REVISION (Minor): Increment any time the source changes; set to dnl 0 if you incremented CURRENT. dnl dnl To summarize. Any interface *change* increment CURRENT. If that interface dnl change does not break upward compatibility (ie it is an addition), dnl increment AGE, Otherwise AGE is reset to 0. If CURRENT has changed, dnl REVISION is set to 0, otherwise REVISION is incremented. dnl --------------------------------------------------------------------------- m4_define([VERSION_INFO_CURRENT],[9]) m4_define([VERSION_INFO_REVISION],[0]) m4_define([VERSION_INFO_AGE],[0]) INTERFACE_VERSION_INFO=VERSION_INFO_CURRENT:VERSION_INFO_REVISION:VERSION_INFO_AGE AC_SUBST(INTERFACE_VERSION_INFO) ############################################################################### ############################################################################### # Start ############################################################################### AS_ECHO AS_ECHO "Building $PACKAGE_STRING" ############################################################################### # Performing some basic checks and initializing the build system ############################################################################### AS_ECHO AS_ECHO "Checking for a C/C++ compiler to use:" AC_PROG_CC AC_PROG_CPP AC_PROG_CC_C_O AC_PROG_CXX AC_PROG_CXXCPP AC_PROG_CXX_C_O AS_ECHO AS_ECHO "Checking system type:" AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_CANONICAL_TARGET AS_ECHO AS_ECHO "Initializing Automake:" AM_INIT_AUTOMAKE([1.11 foreign]) AM_MAINTAINER_MODE([enable]) AS_ECHO AS_ECHO "Initializing Libtool:" LT_PREREQ([2.2.6]) LT_INIT ############################################################################### # Checking for needed base libraries ############################################################################### AS_ECHO AS_ECHO "Checking for posix thread support:" AX_PTHREAD() LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$PTHREAD_CFLAGS $CFLAGS" CC="$PTHREAD_CC" CXXFLAGS="$CXXFLAGS -ftemplate-depth=120 -Wno-format-zero-length" AS_ECHO "Checking for visibility support:" AC_CACHE_CHECK([for __attribute__((visibility("hidden")))], ac_cv_hidden_visibility_attribute, [ echo 'int __attribute__ ((visibility ("hidden"))) foo (void) { return 1; }' > visibility_conftest.c ac_cv_hidden_visibility_attribute=no if AC_TRY_COMMAND(${CC-cc} -fvisibility=hidden -S visibility_conftest.c -o visibility_conftest.s 1>&AS_MESSAGE_LOG_FD); then AS_ECHO "found" ac_cv_hidden_visibility_attribute=yes fi rm -f visibility_conftest.* ]) AS_ECHO AS_ECHO "Checking for boost libraries:" AX_BOOST_BASE([1.53]) AX_BOOST_SYSTEM() AS_IF([test -z "$BOOST_SYSTEM_LIB"], [AC_MSG_ERROR(Boost.System library not found. Try using --with-boost-system=lib)]) AX_BOOST_CHRONO() AS_IF([test -z "$BOOST_CHRONO_LIB"], [AC_MSG_ERROR(Boost.Chrono library not found. Try using --with-boost-chrono=lib)]) AX_BOOST_RANDOM() AS_IF([test -z "$BOOST_RANDOM_LIB"], [AC_MSG_ERROR(Boost.Random library not found. Try using --with-boost-random=lib)]) CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS" LDFLAGS="$BOOST_LDFLAGS $LDFLAGS" LIBS="$BOOST_CHRONO_LIB $BOOST_RANDOM_LIB $LIBS" ############################################################################### # Checking for functions and other stuffs ############################################################################### AS_ECHO AS_ECHO "Checking for pkg-config:" PKG_PROG_PKG_CONFIG([0.20]) AS_ECHO AS_ECHO "Checking for functions and headers:" AC_SYS_LARGEFILE AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_CHECK_FUNCS([clock_gettime], [], [AC_CHECK_LIB([rt], [clock_gettime], [], [AC_CHECK_LIB([posix4], [clock_gettime], [], [AC_MSG_WARN([clock_gettime function not found.])])])] ) dnl Pass some build options to .pc file COMPILETIME_OPTIONS="" ############################################################################### # Setting configure options ############################################################################### AC_ARG_ENABLE( [logging], [AS_HELP_STRING( [--enable-logging], [enable logging alerts [default=yes]])], [[ARG_ENABLE_LOGGING=$enableval]], [[ARG_ENABLE_LOGGING=yes]] ) AC_ARG_ENABLE( [debug], [AS_HELP_STRING( [--enable-debug], [enable debug build [default=no]])], [ ARG_ENABLE_DEBUG=$enableval ac_arg_enable_debug=$enableval ], [ ARG_ENABLE_DEBUG=no ac_arg_enable_debug=no ] ) AC_ARG_ENABLE( [dht], [AS_HELP_STRING( [--enable-dht], [enable dht support [default=yes]])], [[ARG_ENABLE_DHT=$enableval]], [[ARG_ENABLE_DHT=yes]] ) AC_ARG_ENABLE( [encryption], [AS_HELP_STRING( [--enable-encryption], [enable encryption support (requires OpenSSL to be installed on your system, you can use --with-openssl to set the path) [default=yes]])], [[ARG_ENABLE_ENCRYPTION=$enableval]], [[ARG_ENABLE_ENCRYPTION=yes]] ) AC_ARG_ENABLE( [export-all], [AS_HELP_STRING( [--enable-export-all], [export all symbols from libtorrent, including non-public ones [default=no]])], [[ARG_ENABLE_FULL_EXPORT=$enableval]], [[ARG_ENABLE_FULL_EXPORT=no]] ) AC_ARG_ENABLE( [pool-allocators], [AS_HELP_STRING( [--enable-pool-allocators], [enable pool allocators for send buffers [default=yes]])], [[ARG_ENABLE_POOL_ALLOC=$enableval]], [[ARG_ENABLE_POOL_ALLOC=yes]] ) AC_ARG_ENABLE( [invariant-checks], [AS_HELP_STRING( [--enable-invariant-checks], [enable invariant checks (use value "full" to turn on extra expensive invariant checks) @<:@default=yes if debug is enabled, no otherwhise@:>@])], [[ARG_ENABLE_INVARIANT=$enableval]], [ AS_IF([test "x$ac_arg_enable_debug" = "xyes"], [ARG_ENABLE_INVARIANT=yes], [ARG_ENABLE_INVARIANT=no]) ] ) AC_ARG_ENABLE( [deprecated-functions], [AS_HELP_STRING( [--enable-deprecated-functions], [enable deprecated functions in the API [default=yes]])], [[ARG_ENABLE_DEPRECATED=$enableval]], [[ARG_ENABLE_DEPRECATED=yes]] ) AC_ARG_ENABLE( [disk-stats], [AS_HELP_STRING( [--enable-disk-stats], [enable disk activity logging feature [default=no]])], [[ARG_ENABLE_DISK_STATS=$enableval]], [[ARG_ENABLE_DISK_STATS=no]] ) AC_ARG_ENABLE( [examples], [AS_HELP_STRING( [--enable-examples], [build example files [default=no]])], [[ARG_ENABLE_EXAMPLES=$enableval]], [[ARG_ENABLE_EXAMPLES=no]] ) AC_ARG_ENABLE( [tests], [AS_HELP_STRING( [--enable-tests], [build test files [default=no]])], [[ARG_ENABLE_TESTS=$enableval]], [[ARG_ENABLE_TESTS=no]] ) AC_ARG_ENABLE( [python-binding], [AS_HELP_STRING( [--enable-python-binding], [build python bindings [default=no]])], [[ARG_ENABLE_PYTHON_BINDING=$enableval]], [[ARG_ENABLE_PYTHON_BINDING=no]] ) AC_ARG_WITH( [libiconv], [AS_HELP_STRING( [--with-libiconv], [enable linking against system libiconv [default=no]])], [[ARG_WITH_LIBICONV=$withval]], [[ARG_WITH_LIBICONV=no]] ) ############################################################################### # Checking configure options ############################################################################### AS_ECHO AS_ECHO "Checking build options:" AC_MSG_CHECKING([whether deprecated functions should be enabled]) AS_CASE(["$ARG_ENABLE_DEPRECATED"], ["yes"|"on"], [ AC_MSG_RESULT([yes]) ], ["no"|"off"], [ AC_MSG_RESULT([no]) AC_DEFINE([TORRENT_NO_DEPRECATE],[1],[Define to exclude all deprecated functions from the API.]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_NO_DEPRECATE " ], [AC_MSG_RESULT([$ARG_ENABLE_DEPRECATED]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_DEPRECATED". Use either "yes" or "no".])] ) AC_MSG_CHECKING([whether debug build should be enabled]) AS_CASE(["$ARG_ENABLE_DEBUG"], ["yes"], [ AC_MSG_RESULT([yes]) AC_DEFINE([TORRENT_DEBUG],[1],[Define to enable debug code.]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_DEBUG " DEBUGFLAGS="-g" ], ["no"], [ AC_MSG_RESULT([no]) AC_DEFINE([NDEBUG],[1],[Define to disable debug code.]) #COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DNDEBUG " DEBUGFLAGS="-Os" ], [AC_MSG_RESULT([$ARG_ENABLE_DEBUG]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_DEBUG". Use either "yes" or "no".])] ) AC_MSG_CHECKING([whether invariant check should be enabled]) #depends: $ac_arg_enable_debug AS_CASE(["$ARG_ENABLE_INVARIANT"], ["yes"|"on"], [ AC_MSG_RESULT([yes]) AS_IF([test "x$ac_arg_enable_debug" = "xno"], [AC_MSG_ERROR([invariant-checks: this setting only affects debug build. Try using --enable-debug.])]) ], ["no"|"off"], [ AC_MSG_RESULT([no]) AS_IF([test "x$ac_arg_enable_debug" = "xyes"], [AC_DEFINE([TORRENT_DISABLE_INVARIANT_CHECKS],[1],[Define to disable internal invariant checks. Asserts are still enabled while debug is on.])]) ], ["full"], [ AC_MSG_RESULT([full]) AS_IF([test "x$ac_arg_enable_debug" = "xyes"], [AC_DEFINE([TORRENT_EXPENSIVE_INVARIANT_CHECKS],[1],[Define to enable extra expensive invariant checks.])], [AC_MSG_ERROR([invariant-checks: this setting only affects debug build. Try using --enable-debug.])]) ], [AC_MSG_RESULT([$ARG_ENABLE_INVARIANT]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_INVARIANT". Use either "yes", "no" or "full".])] ) AC_MSG_CHECKING([whether logging to disk should be enabled]) AS_CASE(["$ARG_ENABLE_LOGGING"], ["yes"|"default"], [ AC_MSG_RESULT([yes]) ], ["no"|"none"], [ AC_MSG_RESULT([no]) AC_DEFINE([TORRENT_DISABLE_LOGGING],[1],[Define to disable support for logging alerts]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_DISABLE_LOGGING " ], [AC_MSG_RESULT([$ARG_ENABLE_LOGGING]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_LOGGING". Use either "yes" or "no"])] ) AC_MSG_CHECKING([whether disk activity logging should be enabled]) AS_CASE(["$ARG_ENABLE_DISK_STATS"], ["yes"|"on"], [ AC_MSG_RESULT([yes]) AC_DEFINE([TORRENT_DISK_STATS],[1],[Define to create a log of all disk activities.]) ], ["no"|"off"], [ AC_MSG_RESULT([no]) ], [AC_MSG_RESULT([$ARG_ENABLE_DISK_STATS]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_DISK_STATS". Use either "yes" or "no".])] ) AS_ECHO AS_ECHO "Checking features to be enabled:" AC_MSG_CHECKING([whether encryption support should be enabled]) AS_CASE(["$ARG_ENABLE_ENCRYPTION"], ["yes"|"on"], [ AC_MSG_RESULT([yes]) AC_MSG_NOTICE([encryption support: now checking for the OpenSSL library...]) AX_CHECK_OPENSSL([ AC_DEFINE([TORRENT_USE_OPENSSL],[1],[Define to use OpenSSL support.]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_USE_OPENSSL " ], [ AC_MSG_ERROR([OpenSSL library not found. Try using --with-openssl=DIR or disabling encryption at all.]) ]) ], ["no"|"off"], [ AC_MSG_RESULT([no]) AC_DEFINE([TORRENT_DISABLE_ENCRYPTION],[1],[Define to disable any encryption support and avoid linking against OpenSSL.]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_DISABLE_ENCRYPTION " ], [AC_MSG_RESULT([$ARG_ENABLE_ENCRYPTION]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_ENCRYPTION". Use either "yes" or "no".])] ) AC_MSG_CHECKING([whether dht support should be enabled]) AS_CASE(["$ARG_ENABLE_DHT"], ["yes"|"on"], [ AC_MSG_RESULT([yes]) ], ["no"|"off"], [ AC_MSG_RESULT([no]) AC_DEFINE([TORRENT_DISABLE_DHT],[1],[Define to disable the DHT support.]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_DISABLE_DHT " ], [AC_MSG_RESULT([$ARG_ENABLE_DHT]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_DHT". Use either "yes" or "no".])] ) AC_MSG_CHECKING([whether pool allocators should be enabled]) AS_CASE(["$ARG_ENABLE_POOL_ALLOC"], ["yes"|"on"], [ AC_MSG_RESULT([yes]) ], ["no"|"off"], [ AC_MSG_RESULT([no]) AC_DEFINE([TORRENT_DISABLE_POOL_ALLOCATOR],[1],[Define to disable use of boost::pool for pool allocators.]) ], [AC_MSG_RESULT([$ARG_ENABLE_POOL_ALLOC]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_POOL_ALLOC". Use either "yes" or "no".])] ) AS_IF([test "x$ac_cv_hidden_visibility_attribute" = "xyes"], [ AS_IF([test "x$ARG_ENABLE_FULL_EXPORT" = "xno"], [ CXXFLAGS="$CXXFLAGS -fvisibility=hidden -fvisibility-inlines-hidden" CFLAGS="$CFLAGS -fvisibility=hidden" LDFLAGS="$LDFLAGS -fvisibility=hidden -fvisibility-inlines-hidden" ]) ]) AS_ECHO AS_ECHO "Checking for extra build files:" AC_MSG_CHECKING([whether example files should be built]) AS_CASE(["$ARG_ENABLE_EXAMPLES"], ["yes"], [AC_MSG_RESULT([yes])], ["no"], [AC_MSG_RESULT([no])], [AC_MSG_RESULT([$ARG_ENABLE_EXAMPLES]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_EXAMPLES". Use either "yes" or "no".])] ) AC_MSG_CHECKING([whether test files should be built]) AS_CASE(["$ARG_ENABLE_TESTS"], ["yes"], [ AC_MSG_RESULT([yes]) AC_DEFINE([TORRENT_EXPORT_EXTRA],[1],[Define to export additional symbols for unit tests.]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_EXPORT_EXTRA " ], ["no"], [AC_MSG_RESULT([no])], [AC_MSG_RESULT([$ARG_ENABLE_TESTS]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_TESTS". Use either "yes" or "no".])] ) AC_MSG_CHECKING([whether python bindings should be built]) AS_CASE(["$ARG_ENABLE_PYTHON_BINDING"], ["yes"], [ AC_MSG_RESULT([yes]) AM_PATH_PYTHON([2.4], [], AC_MSG_ERROR([Python interpreter not found.])) AX_PYTHON_DEVEL([>= '2.4']) AX_BOOST_PYTHON() AS_IF([test -z "$BOOST_PYTHON_LIB"], [AC_MSG_ERROR([Boost.Python library not found. Try using --with-boost-python=lib.])]) BOOST_PYTHON_LIB="-l$BOOST_PYTHON_LIB" ], ["no"], [ AC_MSG_RESULT([no]) ], [AC_MSG_RESULT([$ARG_ENABLE_PYTHON_BINDING]) AC_MSG_ERROR([Unknown option "$ARG_ENABLE_PYTHON_BINDING". Use either "yes" or "no".])] ) AS_ECHO AS_ECHO "Checking for external libraries:" AC_MSG_CHECKING([whether to link against system libiconv]) AS_CASE(["$ARG_WITH_LIBICONV"], ["yes"], [ AC_MSG_RESULT([yes]) AM_ICONV() AS_IF([test "x$am_cv_func_iconv" = "xyes"], [ ICONV_LIBS=$LIBICONV AC_SUBST([ICONV_LIBS]) LIBS="$ICONV_LIBS $LIBS" ], [ AC_MSG_ERROR([Iconv library not found.]) ]) ], ["no"], [ ARG_WITH_LIBICONV="no" ], [AC_MSG_RESULT([$ARG_WITH_LIBICONV]) AC_MSG_ERROR([Unknown option "$ARG_WITH_LIBICONV". Use either "yes" or "no".])] ) ############################################################################### # Setting conditional variables for Makefiles ############################################################################### AM_CONDITIONAL([ENABLE_DHT], [test "x$ARG_ENABLE_DHT" = "xyes" -o "x$ARG_ENABLE_DHT" = "xlogging" ]) AM_CONDITIONAL([ENABLE_EXAMPLES], [test "x$ARG_ENABLE_EXAMPLES" = "xyes"]) AM_CONDITIONAL([ENABLE_TESTS], [test "x$ARG_ENABLE_TESTS" = "xyes"]) AM_CONDITIONAL([ENABLE_PYTHON_BINDING], [test "x$ARG_ENABLE_PYTHON_BINDING" = "xyes"]) AM_CONDITIONAL([WITH_OPENSSL], [test "x$ARG_ENABLE_ENCRYPTION" = "xyes" -o "x$ARG_ENABLE_ENCRYPTION" = "xon" ]) ############################################################################### # Other useful stuff ############################################################################### # this works around a bug in asio in boost-1.39 # see: https://svn.boost.org/trac/boost/ticket/3095 AC_DEFINE([BOOST_ASIO_HASH_MAP_BUCKETS],[1021],[Define to fix a wrong behavior in boost 1.39.]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DBOOST_ASIO_HASH_MAP_BUCKETS=1021 " AC_DEFINE([BOOST_EXCEPTION_DISABLE],[1],[Define to disable the boost.exception features.]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DBOOST_EXCEPTION_DISABLE " AC_DEFINE([BOOST_ASIO_ENABLE_CANCELIO],[1],[Define to enable cancel support in asio on windows XP and older.]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DBOOST_ASIO_ENABLE_CANCELIO " dnl Use possibly specific python install params AC_ARG_VAR([PYTHON_INSTALL_PARAMS], [Set specific install parameters for python bindings.]) AS_IF([test "x$PYTHON_INSTALL_PARAMS" = "x"], [PYTHON_INSTALL_PARAMS='--prefix=$(DESTDIR)$(prefix)']) dnl Set some defines if we are building a shared library AS_IF([test "x$enable_shared" = "xyes"], [AC_DEFINE([TORRENT_BUILDING_SHARED],[1],[Define to exports functions and classes with default visibility in GCC.]) COMPILETIME_OPTIONS="$COMPILETIME_OPTIONS -DTORRENT_LINKING_SHARED "]) AC_SUBST(DEBUGFLAGS) AC_SUBST(PYTHON_INSTALL_PARAMS) AC_SUBST(COMPILETIME_OPTIONS) # Disable deprecated warnings for the Python binding builds. PYTHON_CXXFLAGS="${PYTHON_CXXFLAGS} -Wno-deprecated-declarations" AC_SUBST(PYTHON_CXXFLAGS) # Try to guess real git revision if any, fallback to hardcoded otherwise GIT_REVISION=`git log -1 --format=format:%h 2>/dev/null` AS_IF([test -z "$GIT_REVISION"], [GIT_REVISION=`sed -n -e "s/^#define LIBTORRENT_REVISION \"\([0-9a-z]*\)\"$/\1/p" $(dirname $0)/include/libtorrent/version.hpp`]) ############################################################################### # Generating Makefiles ############################################################################### AS_ECHO AS_ECHO "Generating Makefiles:" AC_CONFIG_FILES( [Makefile] [src/Makefile] [include/libtorrent/Makefile] [examples/Makefile] [test/Makefile] [tools/Makefile] [bindings/Makefile] [bindings/python/Makefile] [bindings/python/link_flags] [bindings/python/compile_flags] [libtorrent-rasterbar.pc] ) AC_OUTPUT ############################################################################### # Generating config.report ############################################################################### AS_ECHO AS_ECHO "Configure script has finished system check." AS_ECHO cat > config.report << END Config results: -=-=-=-=-=-=-=-=- Package: name: ${PACKAGE_NAME} version: ${PACKAGE_VERSION} git revision: ${GIT_REVISION} Build environment: build system: ${build} host system: ${host} target system: ${target} Compiler and linker flags: CPPFlags: ${CPPFLAGS} CFlags: ${CFLAGS} CXXFlags: ${CXXFLAGS} LDFlags: ${LDFLAGS} Libs: ${LIBS} Defs: ${DEFS} Build options: deprecated functions: ${ARG_ENABLE_DEPRECATED:-yes} debug build: ${ARG_ENABLE_DEBUG:-no} invariant checks: ${ARG_ENABLE_INVARIANT:-no} logging support: ${ARG_ENABLE_LOGGING:-yes} disk statistics: ${ARG_ENABLE_DISK_STATS:-no} Features: encryption support: ${ARG_ENABLE_ENCRYPTION:-yes} dht support: ${ARG_ENABLE_DHT:-yes} pool allocators: ${ARG_ENABLE_POOL_ALLOC:-yes} Extra builds: examples: ${ARG_ENABLE_EXAMPLES:-no} tests: ${ARG_ENABLE_TESTS:-no} python bindings: ${ARG_ENABLE_PYTHON_BINDING:-no} Pthread library: CFlags: ${PTHREAD_CFLAGS} Libs: ${PTHREAD_LIBS} Boost libraries: version: ${BOOST_VERSION} CPPFlags: ${BOOST_CPPFLAGS} LDFlags: ${BOOST_LDFLAGS} boost.system: ${BOOST_SYSTEM_LIB} boost.chrono: ${BOOST_CHRONO_LIB} boost.random: ${BOOST_RANDOM_LIB} END AS_IF([test "x$ARG_ENABLE_PYTHON_BINDING" = "xyes"], [ cat >> config.report << END boost.python: ${BOOST_PYTHON_LIB} Python environment: -automake- binary: ${PYTHON} version: ${PYTHON_VERSION} platform: ${PYTHON_PLATFORM} prefix: ${PYTHON_PREFIX} exec_prefix: ${PYTHON_EXEC_PREFIX} pythondir: ${pythondir} pkgpythondir: ${pkgpythondir} pyexecdir: ${pyexecdir} pkgpyexecdir: ${pkgpyexecdir} -m4- cppflags: ${PYTHON_CPPFLAGS} ldflags: ${PYTHON_LDFLAGS} extra libs: ${PYTHON_EXTRA_LIBS} END ]) cat >> config.report << END External libraries: system libiconv: ${ARG_WITH_LIBICONV:-no} END AS_IF([test "x$ARG_WITH_LIBICONV" = "xyes"], [ cat >> config.report << END Iconv library: Iconv Libs: ${ICONV_LIBS} END ]) AS_IF([test "x$ARG_ENABLE_ENCRYPTION" = "xyes"], [ cat >> config.report << END OpenSSL library: OpenSSL Libs: ${OPENSSL_LIBS} OpenSSL LDFlags: ${OPENSSL_LDFLAGS} OpenSSL Includes: ${OPENSSL_INCLUDES} END ]) cat config.report AS_ECHO AS_ECHO "Type 'make' to compile $PACKAGE_STRING" AS_ECHO "or type 'make V=1' for verbose compiling" AS_ECHO "and then 'make install' to install it into $prefix" AS_ECHO libtorrent-rasterbar-1.1.13/docs/000077500000000000000000000000001351156116000166735ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/docs/bitcoin.png000066400000000000000000000065231351156116000210360ustar00rootroot00000000000000‰PNG  IHDR––<qâ IDATx^í]á•5 t***TTč**H¨¨$PA  ‚@đt ·űéFë‘eďww™}ďţ|g[öxV–lKű¤µöo»_Ď­µ_\—ľk­};ą›OČöěď[köűčSÁÉţĽµör´3+긕A®č“uŚŞ5Č:KĤÎq5Kı¨z)ü¦µöŰ h™jČŕ\ˇ±¬M˙ gŤűŁÖšýíź?[köÇże3_rܢµaS%ő™óv!KÄş„ţ±‘ZÄÚ!đŘ&·…-Ť5ńŐ±ÉRČFęNäÎaSvüó·+Ţ\;ÚřĂ•łßP@ĎijŐXEaŁŕŽ‚#$aŽ^(6…’'Ť·-™Wök‰đDŁlRL42K,ÔM¶?‰!.9|ĎČźZVÄęď"ÖĺD,k€6ý*"–ŐgÉ@ # 4°`źcE Ł}0d@Gʨ˨Mô Údű “˘0Ö&{¶Óc{ga‘‰GăFł)”Đ<½ĐĚíöľ-›xŘü Q“¬ffÇčúô˘+îc±ŃŐ ;±.!±6<2±:úCın>’Ä~¸IKáńuŐĄĐ <źH?ę®7€­žŹdŽŚj¶MD–ČI@†:2lŮ1"Çaş14RE/NťäʞßřS%6 -<Á˛â0–Őbgm˛ř˘r™€`Tź ąŁç6cĽł§…‹X,¤Ýr"–ŐŇXÇܱD¬®ö)p/‰…®uXÔňč׿˘TB0äŐˇĺů+ň+XUŮ– ß_ىvŞ‘3Â’ĺËG˛ÍńXacŮ—Ç^ą†ŮëWŘĄ,[Ž9>‰ÚdòX/ÓʱFlvśľ<{e‡ÝR©j,4vʱ.!±6ëcEě•Qśm™‘S±c«űwě‡ *¶ĺ’Ü "Vźb"Ö#–Á"–%Ťĺ8 ĄđŘqčż2oJX"Ö čî»B!˛ťF¬Ş;ĽÂífß>ÖAÉ´ÇÚXěe»ŚlV+łÓÁŐk3+6đŘAfś™ _–c$CÄšdĽg&śÝę`'—~ŁLce‹X;¤±ú ±6ŚčWı Öü(Ëî˝vŰťG ĄŮASewśĐý.MĚFĺfä ˛ţ[ËV†Ťë1±g’Őd0ňe«˛YĚéx†h)±6D¬ ‹xýĄ±ň«„%bÝ ŔÚw§-…(Ť‘ĺöô†d”JĺEsS06Q&ťjŽv)Ś˘˝YÍĄoBQËţ·ŹA°‰ŤE€łi˘¬>+çľ+¶h—4‘`¶z/‰%űć ń¶K¬L›łË˛Ű ´\ë*kĂŁ´1,b‰X†€4ÖŽlxR¤ľŮk»¬ŤÉyg—B6'2č«7HYŮÓÓěJcdË#Šgí 4F6ď++#S9(QţÓL»e#w”˝ V%ęxĹΡ÷Y®ř0¸Ů[+dłY KšZÄZ1uý6E¬F¬—ŮnĆÚ¸ćY,ši¬*Z űÚňŞKá đ5)ŰM÷ îí-óW_lwŢ'ń‡©nZk(ťO$ŰďćŁ4;Ő4FhűÉéOßVŤ9¬ĆB©Ť"|ŮĄP2Ľ™»ęóÔ bĎ’¬kT#­qVxfÂŮm€Ň’ś. ~˛ÄŞ:L¬cĆbFé° X,R—ĺX‹X›”ŇX}RŠX"Vź%»ŇX¸´¦řt[XÄĂíjµŞF,ŰqöĘŰ^TŽÝČČať3V6˝GÉ Î ć¬˛"Viv~E¬–"–ŐG` „ŐM«ŹŃť"V´««rŇÎ^_Íěc±ű9}X·ŃçŐ|¬çËŽŰÚG“ËbÎĘ©âËŽ›Ć|ĹŐd$Ľ:pz@AAk˝¸"Ö ĂD,kÉ÷lD,KÄęl©¬05–,…(•Ąb>݆ŚK»ŰcźĄŰ?™4FH¶H$›â'ĘĎ™Q,Ţ.±µÝď,›ËDďš\DJD,$‡w4>äL ‡)ň>+i•X9H6r`ţÓęÎ{†•˛¬vĘě@łw˘*ýÎÔťí0E˛Y9™ľß)+b]B˛â’!;Aě„W=RVŰoXNıJŠ*‹X"Ö2bˇ4;•dölÝh@(Ą˛±Pz!»răŻěd6)í gŻ\…iŚĚôJß„RˇĄ¦j­!9h)D#ÜB)ó‘¦%ĚŤŢ7—żjÓ°·Wľlvćë_•4FtŠ*«˙şXF"Vź/đV)Ş&b‰Xť¶"ěA°%b‰Xöëěţ]y)dĄffwżĎÚyŻŚ'łëĎĘaŤw¶=+Çn–4u5:3 TVÄ:FPÄd%b R縚%b‰XdcíĘŘXväŕż@Ŕn°f"”‘ă`Gö×{˘ë— ˘§ŁŠĚ¶Ł µocdžč(ËG(łó`2?m­˝GG‘Đôq]†X¬7úś ć\á‘VŽUĐxX/5š?öđż*‡ŕĎš""Ö®Ő ±v¸Kcm`XťRKk GB¬iŚĐrÄ:(™4Fh P0’ͦ1Šś#$ɱ{hv—mä Ó±{I×\ ŮĄ'ă$°çh,ŘŐí†řF¤ö—;+S -bmđź5ąě„Ż uĺ˘ęŹĺP‘ĆÚ‘ĆęĽÂZ ű:Nk‡kcEé|Ř$ţ¨J—”IcĦ@©„Dť(…*Eą{“í Hówb)Ěětł¶eőę {Ső˝t'ŞŻĽnK°¶e*Ű 0+Ť'łD±Q: ܦv$3á"Ön¦ŘĎb°“+b±H]–ËxL›Z¬ŇĆ"<@VSk)ÜŔ\B,©Ëľ%Q„2;ąfpÚßčé#Ů(ÚŚjź?ŐR%ůtI0"ř­6đýFŃç+l,$E=łskN‚Ďuj§wć&sV8:©GőXbť%»"'łóÎÚ]ŐĄp6ľŹ.J§2áQ]vrYŮ"Ö)i,–6ýr"–u€4V˙eń%ĘK!2bŮűÎlą˙Ý\ßůčλOpŹŇ Eé|„čę ›Îµ‡ Űhęآö52o,ŻŔ×®ľř{đß2±ň\žWÝ E†mőCÓcçÁ2­%vŹ’Ĺš™čŽi#ë4$b­EZÄÚáËnRJcőI)b‰X}– ”±D¬Úô«śB¬˙MŰôźÂBÖŹIEND®B`‚libtorrent-rasterbar-1.1.13/docs/building.html000066400000000000000000001163151351156116000213650ustar00rootroot00000000000000 libtorrent manual

libtorrent manual

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

downloading and building

To download the latest version of libtorrent, clone the github repo.

The build systems supported "out of the box" in libtorrent are boost-build v2 (BBv2) and autotools (for unix-like systems). If you still can't build after following these instructions, you can usually get help in the #libtorrent IRC channel on irc.freenode.net.

Warning

A common mistake when building and linking against libtorrent is to build with one set of configuration options (#defines) and link against it using a different set of configuration options. Since libtorrent has some code in header files, that code will not be compatible with the built library if they see different configurations.

Always make sure that the same TORRENT_* macros are defined when you link against libtorrent as when you build it.

Boost-build supports propagating configuration options to dependencies. When building using the makefiles, this is handled by setting the configuration options in the pkg-config file. Always use pkg-config when linking against libtorrent.

building from git

To build libtorrent from git you need to clone the libtorrent repo from github. If you downloaded a release tarball, you can skip this section.

git clone https://github.com/arvidn/libtorrent.git

building with BBv2

The primary reason to use boost-build is that it will automatically build the dependent boost libraries with the correct compiler settings, in order to ensure that the build targets are link compatible (see boost guidelines for some details on this issue).

Since BBv2 will build the boost libraries for you, you need the full boost source package. Having boost installed via some package system is usually not enough (and even if it is enough, the necessary environment variables are usually not set by the package installer).

If you want to build against an installed copy of boost, you can skip directly to step 3 (assuming you also have boost build installed).

Step 1: Download boost

You'll find boost here.

Extract the archive to some directory where you want it. For the sake of this guide, let's assume you extract the package to c:\boost_1_64_0 (I'm using a windows path in this example since if you're on linux/unix you're more likely to use the autotools). You'll need at least version 1.49 of the boost library in order to build libtorrent.

Step 2: Setup BBv2

First you need to build bjam. You do this by opening a terminal (In windows, run cmd). Change directory to c:\boost_1_64_0\tools\jam\src. Then run the script called build.bat or build.sh on a unix system. This will build bjam and place it in a directory starting with bin. and then have the name of your platform. Copy the bjam.exe (or bjam on a unix system) to a place that's in you shell's PATH. On linux systems a place commonly used may be /usr/local/bin or on windows c:\windows (you can also add directories to the search paths by modifying the environment variable called PATH).

Now you have bjam installed. bjam can be considered an interpreter that the boost-build system is implemented on. So boost-build uses bjam. So, to complete the installation you need to make two more things. You need to set the environment variable BOOST_BUILD_PATH. This is the path that tells bjam where it can find boost-build, your configuration file and all the toolsets (descriptions used by boost-build to know how to use different compilers on different platforms). Assuming the boost install path above, set it to c:\boost_1_64_0\tools\build\v2.

To set an environment variable in windows, type for example:

set BOOST_BUILD_PATH=c:\boost_1_64_0\tools\build\v2

In a terminal window.

The last thing to do to complete the setup of BBv2 is to modify your user-config.jam file. It is located in c:\boost_1_64_0\tools\build\v2. Depending on your platform and which compiler you're using, you should add a line for each compiler and compiler version you have installed on your system that you want to be able to use with BBv2. For example, if you're using Microsoft Visual Studio 12 (2013), just add a line:

using msvc : 12.0 ;

If you use GCC, add the line:

using gcc ;

If you have more than one version of GCC installed, you can add the commandline used to invoke g++ after the version number, like this:

using gcc : 3.3 : g++-3.3 ;
using gcc : 4.0 : g++-4.0 ;

Another toolset worth mentioning is the darwin toolset (For MacOS X). From Tiger (10.4) MacOS X comes with both GCC 3.3 and GCC 4.0. Then you can use the following toolsets:

using darwin : 3.3 : g++-3.3 ;
using darwin : 4.0 : g++-4.0 ;

Note that the spaces around the semi-colons and colons are important!

Also see the official installation instructions.

Step 3: Building libtorrent

When building libtorrent, the Jamfile expects the environment variable BOOST_ROOT to be set to the boost installation directory. It uses this to find the boost libraries it depends on, so they can be built and their headers files found. So, set this to c:\boost_1_64_0. You only need this if you're building against a source distribution of boost.

Then the only thing left is simply to invoke bjam. If you want to specify a specific toolset to use (compiler) you can just add that to the commandline. For example:

bjam msvc-7.1
bjam gcc-3.3
bjam darwin-4.0

Note

If the environment variable BOOST_ROOT is not set, the jamfile will attempt to link against "installed" boost libraries. i.e. assume the headers and libraries are available in default search paths.

To build different versions you can also just add the name of the build variant. Some default build variants in BBv2 are release, debug, profile.

You can build libtorrent as a dll too, by typing link=shared, or link=static to build a static library.

If you want to explicitly say how to link against the runtime library, you can set the runtime-link feature on the commandline, either to shared or static. Most operating systems will only allow linking shared against the runtime, but on windows you can do both. Example:

bjam msvc-7.1 link=static runtime-link=static

Note

When building on windows, the path boost-build puts targets in may be too long. If you get an error message like: "The input line is long", try to pass --abbreviate-paths on the bjam command line.

Warning

If you link statically to the runtime library, you cannot build libtorrent as a shared library (DLL), since you will get separate heaps in the library and in the client application. It will result in crashes and possibly link errors.

Note

With boost-build V2 (Milestone 11), the darwin toolset uses the -s linker option to strip debug symbols. This option is buggy in Apple's GCC, and will make the executable crash on startup. On Mac OS X, instead build your release executables with the debug-symbols=on option, and later strip your executable with strip.

Note

Some linux systems requires linking against librt in order to access the POSIX clock functions. If you get an error complaining about a missing symbol clock_gettime, you have to give need-librt=yes on the bjam command line. This will make libtorrent link against librt.

Note

When building on Solaris, you might have to specify stdlib=sun-stlport on the bjam command line.

The build targets are put in a directory called bin, and under it they are sorted in directories depending on the toolset and build variant used.

To build the examples, just change directory to the examples directory and invoke bjam from there. To build and run the tests, go to the test directory and run bjam.

Note that if you're building on windows using the msvc toolset, you cannot run it from a cygwin terminal, you'll have to run it from a cmd terminal. The same goes for cygwin, if you're building with gcc in cygwin you'll have to run it from a cygwin terminal. Also, make sure the paths are correct in the different environments. In cygwin, the paths (BOOST_BUILD_PATH and BOOST_ROOT) should be in the typical unix-format (e.g. /cygdrive/c/boost_1_64_0). In the windows environment, they should have the typical windows format (c:/boost_1_64_0).

Note

In Jamfiles, spaces are separators. It's typically easiest to avoid spaces in path names. If you want spaces in your paths, make sure to quote them with double quotes (").

The Jamfile will define NDEBUG when it's building a release build. For more build configuration flags see Build configurations.

When enabling linking against openssl (by setting the crypto feature to openssl) the Jamfile will look in some default directory for the openssl headers and libraries. On macOS, it will look for the homebrew openssl package. On windows it will look in c:\openssl and mingw in c:\OpenSSL-Win32.

To customize the library path and include path for openssl, set the features openssl-lib and openssl-include respectively.

Build features:

boost build feature values
boost-link
  • static - links statically against the boost libraries.
  • shared - links dynamically against the boost libraries.
openssl-lib can be used to specify the directory where libssl and libcrypto are installed (or the windows counterparts).
openssl-include can be used to specify the include directory where the openssl headers are installed.
logging
  • off - logging alerts disabled. The reason to disable logging is to keep the binary size low where that matters.
  • on - default. logging alerts available, still need to be enabled by the alert mask.
dht
  • on - build with DHT support
  • off - build without DHT support.
asserts
  • auto - asserts are on if in debug mode
  • on - asserts are on, even in release mode
  • off - asserts are disabled
  • production - assertion failures are logged to asserts.log in the current working directory, but won't abort the process. The file they are logged to can be customized by setting the global pointer extern char const* libtorrent_assert_log to a different filename.
  • system use the libc assert macro
encryption
  • on - encrypted bittorrent connections enabled. (Message Stream encryption).
  • off - turns off support for encrypted connections. The shipped public domain SHA-1 implementation is used.
mutable-torrents
  • on - mutable torrents are supported (BEP 38) (default).
  • off - mutable torrents are not supported.
crypto
  • built-in - (default) uses built-in SHA-1 implementation.
  • openssl - links against openssl and libcrypto to use for SHA-1 hashing. This also enables HTTPS-tracker support and support for bittorrent over SSL.
  • gcrypt - links against libgcrypt to use for SHA-1 hashing.
openssl-version

This can be used on windows to link against the special OpenSSL library names used on windows prior to OpenSSL 1.1.

  • 1.1 - link against the normal openssl library name. (default)
  • pre1.1 - link against the old windows names (i.e. ssleay32 and libeay32.
allocator
  • pool - default, uses pool allocators for send buffers.
  • system - uses malloc() and free() instead. Might be useful to debug buffer issues with tools like electric fence or libgmalloc.
  • debug - instruments buffer usage to catch bugs in libtorrent.
link
  • static - builds libtorrent as a static library (.a / .lib)
  • shared - builds libtorrent as a shared library (.so / .dll).
runtime-link
  • static - links statically against the run-time library (if available on your platform).
  • shared - link dynamically against the run-time library (default).
variant
  • debug - builds libtorrent with debug information and invariant checks.
  • release - builds libtorrent in release mode without invariant checks and with optimization.
  • profile - builds libtorrent with profile information.
character-set

This setting will only have an affect on windows. Other platforms are expected to support UTF-8.

  • unicode - The unicode version of the win32 API is used. This is default.
  • ansi - The ansi version of the win32 API is used.
invariant-checks

This setting only affects debug builds (where NDEBUG is not defined). It defaults to on.

  • on - internal invariant checks are enabled.
  • off - internal invariant checks are disabled. The resulting executable will run faster than a regular debug build.
  • full - turns on extra expensive invariant checks.
debug-symbols
  • on - default for debug builds. This setting is useful for building release builds with symbols.
  • off - default for release builds.
deprecated-functions
  • on - default. Includes deprecated functions of the API (might produce warnings during build when deprecated functions are used).
  • off - excludes deprecated functions from the API. Generates build errors when deprecated functions are used.
iconv
  • auto - use iconv for string conversions for linux and mingw and other posix platforms.
  • on - force use of iconv
  • off - force not using iconv (disables locale awareness except on windows).
i2p
  • on - build with I2P support
  • off - build without I2P support
profile-calls
  • off - default. No additional call profiling.
  • on - Enable logging of stack traces of calls into libtorrent that are blocking. On session shutdown, a file blocking_calls.txt is written with stack traces of blocking calls ordered by the number of them.

The variant feature is implicit, which means you don't need to specify the name of the feature, just the value.

The logs created when building vlog or log mode are put in a directory called libtorrent_logs in the current working directory.

When building the example client on windows, you need to build with link=static otherwise you may get unresolved external symbols for some boost.program-options symbols.

For more information, see the Boost build v2 documentation, or more specifically the section on builtin features.

building with autotools

First of all, you need to install automake and autoconf. Many unix/linux systems comes with these preinstalled.

The prerequisites for building libtorrent are boost.system, boost.chrono and boost.random. Those are the compiled boost libraries needed. The headers-only libraries needed include (but is not necessarily limited to) boost.bind, boost.ref, boost.multi_index, boost.optional, boost.integer, boost.iterator, boost.tuple, boost.array, boost.function, boost.smart_ptr, boost.preprocessor, boost.static_assert.

If you want to build the client_test example, you'll also need boost.regex and boost.program_options.

Step 1: Generating the build system

No build system is present if libtorrent is checked out from CVS - it needs to be generated first. If you're building from a released tarball, you may skip directly to Step 2: Running configure.

Execute the following command to generate the build system:

./autotool.sh

Step 2: Running configure

In your shell, change directory to the libtorrent directory and run ./configure. This will look for libraries and C++ features that libtorrent is dependent on. If something is missing or can't be found it will print an error telling you what failed.

The most likely problem you may encounter is that the configure script won't find the boost libraries. Make sure you have boost installed on your system. The easiest way to install boost is usually to use the preferred package system on your platform. Usually libraries and headers are installed in standard directories where the compiler will find them, but sometimes that may not be the case. For example when installing boost on darwin using darwinports (the package system based on BSD ports) all libraries are installed to /opt/local/lib and headers are installed to /opt/local/include. By default the compiler will not look in these directories. You have to set the enviornment variables LDFLAGS and CXXFLAGS in order to make the compiler find those libs. In this example you'd set them like this:

export LDFLAGS=-L/opt/local/lib
export CXXFLAGS=-I/opt/local/include

It was observed on FreeBSD (release 6.0) that one needs to add '-lpthread' to LDFLAGS, as Boost::Thread detection will fail without it, even if Boost::Thread is installed.

If you need to set these variables, it may be a good idea to add those lines to your ~/.profile or ~/.tcshrc depending on your shell.

If the boost libraries are named with a suffix on your platform, you may use the --with-boost-thread= option to specify the suffix used for the thread library in this case. For more information about these options, run:

./configure --help

On gentoo the boost libraries that are built with multi-threading support have the suffix mt.

You know that the boost libraries were found if you see the following output from the configure script:

Checking for boost libraries:
checking for boostlib >= 1.53... yes
checking whether the Boost::System library is available... yes
checking for exit in -lboost_system... yes
checking whether the Boost::Chrono library is available... yes
checking for exit in -lboost_chrono-mt... yes
checking whether the Boost::Random library is available... yes
checking for exit in -lboost_random-mt... yes

Another possible source of problems may be if the path to your libtorrent directory contains spaces. Make sure you either rename the directories with spaces in their names to remove the spaces or move the libtorrent directory.

Creating a debug build

To tell configure to build a debug version (with debug info, asserts and invariant checks enabled), you have to run the configure script with the following option:

./configure --enable-debug=yes

Creating a release build

To tell the configure to build a release version (without debug info, asserts and invariant checks), you have to run the configure script with the following option:

./configure --enable-debug=no

The above option make use of -DNDEBUG, which is used throughout libtorrent.

Step 3: Building libtorrent

Once the configure script is run successfully, you just type make and libtorrent, the examples and the tests will be built.

When libtorrent is built it may be a good idea to run the tests, you do this by running make check.

If you want to build a release version (without debug info, asserts and invariant checks), you have to rerun the configure script and rebuild, like this:

./configure --disable-debug
make clean
make

building with other build systems

If you're building in MS Visual Studio, you may have to set the compiler options "force conformance in for loop scope", "treat wchar_t as built-in type" and "Enable Run-Time Type Info" to Yes.

build configurations

By default libtorrent is built In debug mode, and will have pretty expensive invariant checks and asserts built into it. If you want to disable such checks (you want to do that in a release build) you can see the table below for which defines you can use to control the build.

macro description
NDEBUG If you define this macro, all asserts, invariant checks and general debug code will be removed. Since there is quite a lot of code in in header files in libtorrent, it may be important to define the symbol consistently across compilation units, including the clients files. Potential problems is different compilation units having different views of structs and class layouts and sizes.
TORRENT_DISABLE_LOGGING This macro will disable support for logging alerts, like log_alert, torrent_log_alert and peer_log_alert. With this build flag, you cannot enable those alerts.
TORRENT_DISK_STATS This will create a log of all disk activity which later can parsed and graphed using parse_disk_log.py.
UNICODE If building on windows this will make sure the UTF-8 strings in pathnames are converted into UTF-16 before they are passed to the file operations.
TORRENT_DISABLE_POOL_ALLOCATOR Disables use of boost::pool<>.
TORRENT_DISABLE_MUTABLE_TORRENTS Disables mutable torrent support (BEP 38)
TORRENT_LINKING_SHARED If this is defined when including the libtorrent headers, the classes and functions will be tagged with __declspec(dllimport) on msvc and default visibility on GCC 4 and later. Set this in your project if you're linking against libtorrent as a shared library. (This is set by the Jamfile when link=shared is set).
TORRENT_BUILDING_SHARED If this is defined, the functions and classes in libtorrent are marked with __declspec(dllexport) on msvc, or with default visibility on GCC 4 and later. This should be defined when building libtorrent as a shared library. (This is set by the Jamfile when link=shared is set).
TORRENT_DISABLE_DHT If this is defined, the support for trackerless torrents will be disabled.
TORRENT_DISABLE_ENCRYPTION This will disable any encryption support and the dependencies of a crypto library. Encryption support is the peer connection encrypted supported by clients such as uTorrent, Azureus and KTorrent. If this is not defined, either TORRENT_USE_OPENSSL or TORRENT_USE_GCRYPT must be defined.
TORRENT_DISABLE_EXTENSIONS When defined, libtorrent plugin support is disabled along with support for the extension handskake (BEP 10).
_UNICODE On windows, this will cause the file IO use wide character API, to properly support non-ansi characters.
TORRENT_DISABLE_RESOLVE_COUNTRIES Defining this will disable the ability to resolve countries of origin for peer IPs.
TORRENT_DISABLE_INVARIANT_CHECKS This will disable internal invariant checks in libtorrent. The invariant checks can sometime be quite expensive, they typically don't scale very well. This option can be used to still build in debug mode, with asserts enabled, but make the resulting executable faster.
TORRENT_EXPENSIVE_INVARIANT_CHECKS This will enable extra expensive invariant checks. Useful for finding particular bugs or for running before releases.
TORRENT_NO_DEPRECATE This will exclude all deprecated functions from the header files and cpp files.
TORRENT_PRODUCTION_ASSERTS Define to either 0 or 1. Enables assert logging in release builds.
TORRENT_USE_ASSERTS Define as 0 to disable asserts unconditionally.
TORRENT_USE_SYSTEM_ASSERTS Uses the libc assert macro rather then the custom one.

If you experience that libtorrent uses unreasonable amounts of cpu, it will definitely help to define NDEBUG, since it will remove the invariant checks within the library.

building openssl for windows

To build openssl for windows with Visual Studio 7.1 (2003) execute the following commands in a command shell:

perl Configure VC-WIN32 --prefix="c:/openssl
call ms\do_nasm
call "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\bin\vcvars32.bat"
nmake -f ms\nt.mak
copy inc32\openssl "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\include\"
copy out32\libeay32.lib "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib"
copy out32\ssleay32.lib "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib"

This will also install the headers and library files in the visual studio directories to be picked up by libtorrent.

libtorrent-rasterbar-1.1.13/docs/building.rst000066400000000000000000001073731351156116000212350ustar00rootroot00000000000000================= libtorrent manual ================= :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none downloading and building ======================== To download the latest version of libtorrent, clone the `github repo`__. __ https://github.com/arvidn/libtorrent The build systems supported "out of the box" in libtorrent are boost-build v2 (BBv2) and autotools (for unix-like systems). If you still can't build after following these instructions, you can usually get help in the ``#libtorrent`` IRC channel on ``irc.freenode.net``. .. warning:: A common mistake when building and linking against libtorrent is to build with one set of configuration options (#defines) and link against it using a different set of configuration options. Since libtorrent has some code in header files, that code will not be compatible with the built library if they see different configurations. Always make sure that the same TORRENT_* macros are defined when you link against libtorrent as when you build it. Boost-build supports propagating configuration options to dependencies. When building using the makefiles, this is handled by setting the configuration options in the pkg-config file. Always use pkg-config when linking against libtorrent. building from git ----------------- To build libtorrent from git you need to clone the libtorrent repo from github. If you downloaded a release `tarball`__, you can skip this section. __ https://github.com/arvidn/libtorrent/releases/latest :: git clone https://github.com/arvidn/libtorrent.git building with BBv2 ------------------ The primary reason to use boost-build is that it will automatically build the dependent boost libraries with the correct compiler settings, in order to ensure that the build targets are link compatible (see `boost guidelines`__ for some details on this issue). __ https://boost.org/more/separate_compilation.html Since BBv2 will build the boost libraries for you, you need the full boost source package. Having boost installed via some package system is usually not enough (and even if it is enough, the necessary environment variables are usually not set by the package installer). If you want to build against an installed copy of boost, you can skip directly to step 3 (assuming you also have boost build installed). Step 1: Download boost ~~~~~~~~~~~~~~~~~~~~~~ You'll find boost here__. __ https://sourceforge.net/project/showfiles.php?group_id=7586&package_id=8041&release_id=619445 Extract the archive to some directory where you want it. For the sake of this guide, let's assume you extract the package to ``c:\boost_1_64_0`` (I'm using a windows path in this example since if you're on linux/unix you're more likely to use the autotools). You'll need at least version 1.49 of the boost library in order to build libtorrent. Step 2: Setup BBv2 ~~~~~~~~~~~~~~~~~~ First you need to build ``bjam``. You do this by opening a terminal (In windows, run ``cmd``). Change directory to ``c:\boost_1_64_0\tools\jam\src``. Then run the script called ``build.bat`` or ``build.sh`` on a unix system. This will build ``bjam`` and place it in a directory starting with ``bin.`` and then have the name of your platform. Copy the ``bjam.exe`` (or ``bjam`` on a unix system) to a place that's in you shell's ``PATH``. On linux systems a place commonly used may be ``/usr/local/bin`` or on windows ``c:\windows`` (you can also add directories to the search paths by modifying the environment variable called ``PATH``). Now you have ``bjam`` installed. ``bjam`` can be considered an interpreter that the boost-build system is implemented on. So boost-build uses ``bjam``. So, to complete the installation you need to make two more things. You need to set the environment variable ``BOOST_BUILD_PATH``. This is the path that tells ``bjam`` where it can find boost-build, your configuration file and all the toolsets (descriptions used by boost-build to know how to use different compilers on different platforms). Assuming the boost install path above, set it to ``c:\boost_1_64_0\tools\build\v2``. To set an environment variable in windows, type for example:: set BOOST_BUILD_PATH=c:\boost_1_64_0\tools\build\v2 In a terminal window. The last thing to do to complete the setup of BBv2 is to modify your ``user-config.jam`` file. It is located in ``c:\boost_1_64_0\tools\build\v2``. Depending on your platform and which compiler you're using, you should add a line for each compiler and compiler version you have installed on your system that you want to be able to use with BBv2. For example, if you're using Microsoft Visual Studio 12 (2013), just add a line:: using msvc : 12.0 ; If you use GCC, add the line:: using gcc ; If you have more than one version of GCC installed, you can add the commandline used to invoke g++ after the version number, like this:: using gcc : 3.3 : g++-3.3 ; using gcc : 4.0 : g++-4.0 ; Another toolset worth mentioning is the ``darwin`` toolset (For MacOS X). From Tiger (10.4) MacOS X comes with both GCC 3.3 and GCC 4.0. Then you can use the following toolsets:: using darwin : 3.3 : g++-3.3 ; using darwin : 4.0 : g++-4.0 ; Note that the spaces around the semi-colons and colons are important! Also see the `official installation instructions`_. .. _`official installation instructions`: https://www.boost.org/doc/html/bbv2/installation.html Step 3: Building libtorrent ~~~~~~~~~~~~~~~~~~~~~~~~~~~ When building libtorrent, the ``Jamfile`` expects the environment variable ``BOOST_ROOT`` to be set to the boost installation directory. It uses this to find the boost libraries it depends on, so they can be built and their headers files found. So, set this to ``c:\boost_1_64_0``. You only need this if you're building against a source distribution of boost. Then the only thing left is simply to invoke ``bjam``. If you want to specify a specific toolset to use (compiler) you can just add that to the commandline. For example:: bjam msvc-7.1 bjam gcc-3.3 bjam darwin-4.0 .. note:: If the environment variable ``BOOST_ROOT`` is not set, the jamfile will attempt to link against "installed" boost libraries. i.e. assume the headers and libraries are available in default search paths. To build different versions you can also just add the name of the build variant. Some default build variants in BBv2 are ``release``, ``debug``, ``profile``. You can build libtorrent as a dll too, by typing ``link=shared``, or ``link=static`` to build a static library. If you want to explicitly say how to link against the runtime library, you can set the ``runtime-link`` feature on the commandline, either to ``shared`` or ``static``. Most operating systems will only allow linking shared against the runtime, but on windows you can do both. Example:: bjam msvc-7.1 link=static runtime-link=static .. note:: When building on windows, the path boost-build puts targets in may be too long. If you get an error message like: "The input line is long", try to pass --abbreviate-paths on the bjam command line. .. warning:: If you link statically to the runtime library, you cannot build libtorrent as a shared library (DLL), since you will get separate heaps in the library and in the client application. It will result in crashes and possibly link errors. .. note:: With boost-build V2 (Milestone 11), the darwin toolset uses the ``-s`` linker option to strip debug symbols. This option is buggy in Apple's GCC, and will make the executable crash on startup. On Mac OS X, instead build your release executables with the ``debug-symbols=on`` option, and later strip your executable with ``strip``. .. note:: Some linux systems requires linking against ``librt`` in order to access the POSIX clock functions. If you get an error complaining about a missing symbol ``clock_gettime``, you have to give ``need-librt=yes`` on the bjam command line. This will make libtorrent link against ``librt``. .. note:: When building on Solaris, you might have to specify ``stdlib=sun-stlport`` on the bjam command line. The build targets are put in a directory called bin, and under it they are sorted in directories depending on the toolset and build variant used. To build the examples, just change directory to the examples directory and invoke ``bjam`` from there. To build and run the tests, go to the test directory and run ``bjam``. Note that if you're building on windows using the ``msvc`` toolset, you cannot run it from a cygwin terminal, you'll have to run it from a ``cmd`` terminal. The same goes for cygwin, if you're building with gcc in cygwin you'll have to run it from a cygwin terminal. Also, make sure the paths are correct in the different environments. In cygwin, the paths (``BOOST_BUILD_PATH`` and ``BOOST_ROOT``) should be in the typical unix-format (e.g. ``/cygdrive/c/boost_1_64_0``). In the windows environment, they should have the typical windows format (``c:/boost_1_64_0``). .. note:: In Jamfiles, spaces are separators. It's typically easiest to avoid spaces in path names. If you want spaces in your paths, make sure to quote them with double quotes ("). The ``Jamfile`` will define ``NDEBUG`` when it's building a release build. For more build configuration flags see `Build configurations`_. When enabling linking against openssl (by setting the ``crypto`` feature to ``openssl``) the Jamfile will look in some default directory for the openssl headers and libraries. On macOS, it will look for the homebrew openssl package. On windows it will look in ``c:\openssl`` and mingw in ``c:\OpenSSL-Win32``. To customize the library path and include path for openssl, set the features ``openssl-lib`` and ``openssl-include`` respectively. Build features: +--------------------------+----------------------------------------------------+ | boost build feature | values | +==========================+====================================================+ | ``boost-link`` | * ``static`` - links statically against the boost | | | libraries. | | | * ``shared`` - links dynamically against the boost | | | libraries. | +--------------------------+----------------------------------------------------+ | ``openssl-lib`` | can be used to specify the directory where libssl | | | and libcrypto are installed (or the windows | | | counterparts). | +--------------------------+----------------------------------------------------+ | ``openssl-include`` | can be used to specify the include directory where | | | the openssl headers are installed. | +--------------------------+----------------------------------------------------+ | ``logging`` | * ``off`` - logging alerts disabled. The | | | reason to disable logging is to keep the binary | | | size low where that matters. | | | * ``on`` - default. logging alerts available, | | | still need to be enabled by the alert mask. | +--------------------------+----------------------------------------------------+ | ``dht`` | * ``on`` - build with DHT support | | | * ``off`` - build without DHT support. | +--------------------------+----------------------------------------------------+ | ``asserts`` | * ``auto`` - asserts are on if in debug mode | | | * ``on`` - asserts are on, even in release mode | | | * ``off`` - asserts are disabled | | | * ``production`` - assertion failures are logged | | | to ``asserts.log`` in the current working | | | directory, but won't abort the process. | | | The file they are logged to can be customized | | | by setting the global pointer ``extern char | | | const* libtorrent_assert_log`` to a different | | | filename. | | | * ``system`` use the libc assert macro | +--------------------------+----------------------------------------------------+ | ``encryption`` | * ``on`` - encrypted bittorrent connections | | | enabled. (Message Stream encryption). | | | * ``off`` - turns off support for encrypted | | | connections. The shipped public domain SHA-1 | | | implementation is used. | +--------------------------+----------------------------------------------------+ | ``mutable-torrents`` | * ``on`` - mutable torrents are supported | | | (`BEP 38`_) (default). | | | * ``off`` - mutable torrents are not supported. | +--------------------------+----------------------------------------------------+ | ``crypto`` | * ``built-in`` - (default) uses built-in SHA-1 | | | implementation. | | | * ``openssl`` - links against openssl and | | | libcrypto to use for SHA-1 hashing. | | | This also enables HTTPS-tracker support and | | | support for bittorrent over SSL. | | | * ``gcrypt`` - links against libgcrypt to use for | | | SHA-1 hashing. | +--------------------------+----------------------------------------------------+ | ``openssl-version`` | This can be used on windows to link against the | | | special OpenSSL library names used on windows | | | prior to OpenSSL 1.1. | | | | | | * ``1.1`` - link against the normal openssl | | | library name. (default) | | | * ``pre1.1`` - link against the old windows names | | | (i.e. ``ssleay32`` and ``libeay32``. | +--------------------------+----------------------------------------------------+ | ``allocator`` | * ``pool`` - default, uses pool allocators for | | | send buffers. | | | * ``system`` - uses ``malloc()`` and ``free()`` | | | instead. Might be useful to debug buffer issues | | | with tools like electric fence or libgmalloc. | | | * ``debug`` - instruments buffer usage to catch | | | bugs in libtorrent. | +--------------------------+----------------------------------------------------+ | ``link`` | * ``static`` - builds libtorrent as a static | | | library (.a / .lib) | | | * ``shared`` - builds libtorrent as a shared | | | library (.so / .dll). | +--------------------------+----------------------------------------------------+ | ``runtime-link`` | * ``static`` - links statically against the | | | run-time library (if available on your | | | platform). | | | * ``shared`` - link dynamically against the | | | run-time library (default). | +--------------------------+----------------------------------------------------+ | ``variant`` | * ``debug`` - builds libtorrent with debug | | | information and invariant checks. | | | * ``release`` - builds libtorrent in release mode | | | without invariant checks and with optimization. | | | * ``profile`` - builds libtorrent with profile | | | information. | +--------------------------+----------------------------------------------------+ | ``character-set`` | This setting will only have an affect on windows. | | | Other platforms are expected to support UTF-8. | | | | | | * ``unicode`` - The unicode version of the win32 | | | API is used. This is default. | | | * ``ansi`` - The ansi version of the win32 API is | | | used. | +--------------------------+----------------------------------------------------+ | ``invariant-checks`` | This setting only affects debug builds (where | | | ``NDEBUG`` is not defined). It defaults to ``on``. | | | | | | * ``on`` - internal invariant checks are enabled. | | | * ``off`` - internal invariant checks are | | | disabled. The resulting executable will run | | | faster than a regular debug build. | | | * ``full`` - turns on extra expensive invariant | | | checks. | +--------------------------+----------------------------------------------------+ | ``debug-symbols`` | * ``on`` - default for debug builds. This setting | | | is useful for building release builds with | | | symbols. | | | * ``off`` - default for release builds. | +--------------------------+----------------------------------------------------+ | ``deprecated-functions`` | * ``on`` - default. Includes deprecated functions | | | of the API (might produce warnings during build | | | when deprecated functions are used). | | | * ``off`` - excludes deprecated functions from the | | | API. Generates build errors when deprecated | | | functions are used. | +--------------------------+----------------------------------------------------+ | ``iconv`` | * ``auto`` - use iconv for string conversions for | | | linux and mingw and other posix platforms. | | | * ``on`` - force use of iconv | | | * ``off`` - force not using iconv (disables locale | | | awareness except on windows). | +--------------------------+----------------------------------------------------+ | ``i2p`` | * ``on`` - build with I2P support | | | * ``off`` - build without I2P support | +--------------------------+----------------------------------------------------+ | ``profile-calls`` | * ``off`` - default. No additional call profiling. | | | * ``on`` - Enable logging of stack traces of | | | calls into libtorrent that are blocking. On | | | session shutdown, a file ``blocking_calls.txt`` | | | is written with stack traces of blocking calls | | | ordered by the number of them. | +--------------------------+----------------------------------------------------+ The ``variant`` feature is *implicit*, which means you don't need to specify the name of the feature, just the value. The logs created when building vlog or log mode are put in a directory called ``libtorrent_logs`` in the current working directory. When building the example client on windows, you need to build with ``link=static`` otherwise you may get unresolved external symbols for some boost.program-options symbols. For more information, see the `Boost build v2 documentation`__, or more specifically `the section on builtin features`__. __ https://www.boost.org/tools/build/v2/index.html __ https://www.boost.org/doc/html/bbv2/reference.html#bbv2.advanced.builtins.features building with autotools ----------------------- First of all, you need to install ``automake`` and ``autoconf``. Many unix/linux systems comes with these preinstalled. The prerequisites for building libtorrent are boost.system, boost.chrono and boost.random. Those are the *compiled* boost libraries needed. The headers-only libraries needed include (but is not necessarily limited to) boost.bind, boost.ref, boost.multi_index, boost.optional, boost.integer, boost.iterator, boost.tuple, boost.array, boost.function, boost.smart_ptr, boost.preprocessor, boost.static_assert. If you want to build the ``client_test`` example, you'll also need boost.regex and boost.program_options. Step 1: Generating the build system ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ No build system is present if libtorrent is checked out from CVS - it needs to be generated first. If you're building from a released tarball, you may skip directly to `Step 2: Running configure`_. Execute the following command to generate the build system:: ./autotool.sh Step 2: Running configure ~~~~~~~~~~~~~~~~~~~~~~~~~ In your shell, change directory to the libtorrent directory and run ``./configure``. This will look for libraries and C++ features that libtorrent is dependent on. If something is missing or can't be found it will print an error telling you what failed. The most likely problem you may encounter is that the configure script won't find the boost libraries. Make sure you have boost installed on your system. The easiest way to install boost is usually to use the preferred package system on your platform. Usually libraries and headers are installed in standard directories where the compiler will find them, but sometimes that may not be the case. For example when installing boost on darwin using darwinports (the package system based on BSD ports) all libraries are installed to ``/opt/local/lib`` and headers are installed to ``/opt/local/include``. By default the compiler will not look in these directories. You have to set the enviornment variables ``LDFLAGS`` and ``CXXFLAGS`` in order to make the compiler find those libs. In this example you'd set them like this:: export LDFLAGS=-L/opt/local/lib export CXXFLAGS=-I/opt/local/include It was observed on FreeBSD (release 6.0) that one needs to add '-lpthread' to LDFLAGS, as Boost::Thread detection will fail without it, even if Boost::Thread is installed. If you need to set these variables, it may be a good idea to add those lines to your ``~/.profile`` or ``~/.tcshrc`` depending on your shell. If the boost libraries are named with a suffix on your platform, you may use the ``--with-boost-thread=`` option to specify the suffix used for the thread library in this case. For more information about these options, run:: ./configure --help On gentoo the boost libraries that are built with multi-threading support have the suffix ``mt``. You know that the boost libraries were found if you see the following output from the configure script:: Checking for boost libraries: checking for boostlib >= 1.53... yes checking whether the Boost::System library is available... yes checking for exit in -lboost_system... yes checking whether the Boost::Chrono library is available... yes checking for exit in -lboost_chrono-mt... yes checking whether the Boost::Random library is available... yes checking for exit in -lboost_random-mt... yes Another possible source of problems may be if the path to your libtorrent directory contains spaces. Make sure you either rename the directories with spaces in their names to remove the spaces or move the libtorrent directory. Creating a debug build ~~~~~~~~~~~~~~~~~~~~~~ To tell configure to build a debug version (with debug info, asserts and invariant checks enabled), you have to run the configure script with the following option:: ./configure --enable-debug=yes Creating a release build ~~~~~~~~~~~~~~~~~~~~~~~~ To tell the configure to build a release version (without debug info, asserts and invariant checks), you have to run the configure script with the following option:: ./configure --enable-debug=no The above option make use of -DNDEBUG, which is used throughout libtorrent. Step 3: Building libtorrent ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once the configure script is run successfully, you just type ``make`` and libtorrent, the examples and the tests will be built. When libtorrent is built it may be a good idea to run the tests, you do this by running ``make check``. If you want to build a release version (without debug info, asserts and invariant checks), you have to rerun the configure script and rebuild, like this:: ./configure --disable-debug make clean make building with other build systems --------------------------------- If you're building in MS Visual Studio, you may have to set the compiler options "force conformance in for loop scope", "treat wchar_t as built-in type" and "Enable Run-Time Type Info" to Yes. build configurations -------------------- By default libtorrent is built In debug mode, and will have pretty expensive invariant checks and asserts built into it. If you want to disable such checks (you want to do that in a release build) you can see the table below for which defines you can use to control the build. +----------------------------------------+-------------------------------------------------+ | macro | description | +========================================+=================================================+ | ``NDEBUG`` | If you define this macro, all asserts, | | | invariant checks and general debug code will be | | | removed. Since there is quite a lot of code in | | | in header files in libtorrent, it may be | | | important to define the symbol consistently | | | across compilation units, including the clients | | | files. Potential problems is different | | | compilation units having different views of | | | structs and class layouts and sizes. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_DISABLE_LOGGING`` | This macro will disable support for logging | | | alerts, like log_alert, torrent_log_alert and | | | peer_log_alert. With this build flag, you | | | cannot enable those alerts. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_DISK_STATS`` | This will create a log of all disk activity | | | which later can parsed and graphed using | | | ``parse_disk_log.py``. | +----------------------------------------+-------------------------------------------------+ | ``UNICODE`` | If building on windows this will make sure the | | | UTF-8 strings in pathnames are converted into | | | UTF-16 before they are passed to the file | | | operations. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_DISABLE_POOL_ALLOCATOR`` | Disables use of ``boost::pool<>``. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_DISABLE_MUTABLE_TORRENTS`` | Disables mutable torrent support (`BEP 38`_) | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_LINKING_SHARED`` | If this is defined when including the | | | libtorrent headers, the classes and functions | | | will be tagged with ``__declspec(dllimport)`` | | | on msvc and default visibility on GCC 4 and | | | later. Set this in your project if you're | | | linking against libtorrent as a shared library. | | | (This is set by the Jamfile when | | | ``link=shared`` is set). | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_BUILDING_SHARED`` | If this is defined, the functions and classes | | | in libtorrent are marked with | | | ``__declspec(dllexport)`` on msvc, or with | | | default visibility on GCC 4 and later. This | | | should be defined when building libtorrent as | | | a shared library. (This is set by the Jamfile | | | when ``link=shared`` is set). | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_DISABLE_DHT`` | If this is defined, the support for trackerless | | | torrents will be disabled. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_DISABLE_ENCRYPTION`` | This will disable any encryption support and | | | the dependencies of a crypto library. | | | Encryption support is the peer connection | | | encrypted supported by clients such as | | | uTorrent, Azureus and KTorrent. | | | If this is not defined, either | | | ``TORRENT_USE_OPENSSL`` or | | | ``TORRENT_USE_GCRYPT`` must be defined. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_DISABLE_EXTENSIONS`` | When defined, libtorrent plugin support is | | | disabled along with support for the extension | | | handskake (BEP 10). | +----------------------------------------+-------------------------------------------------+ | ``_UNICODE`` | On windows, this will cause the file IO | | | use wide character API, to properly support | | | non-ansi characters. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_DISABLE_RESOLVE_COUNTRIES`` | Defining this will disable the ability to | | | resolve countries of origin for peer IPs. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_DISABLE_INVARIANT_CHECKS`` | This will disable internal invariant checks in | | | libtorrent. The invariant checks can sometime | | | be quite expensive, they typically don't scale | | | very well. This option can be used to still | | | build in debug mode, with asserts enabled, but | | | make the resulting executable faster. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_EXPENSIVE_INVARIANT_CHECKS`` | This will enable extra expensive invariant | | | checks. Useful for finding particular bugs | | | or for running before releases. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_NO_DEPRECATE`` | This will exclude all deprecated functions from | | | the header files and cpp files. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_PRODUCTION_ASSERTS`` | Define to either 0 or 1. Enables assert logging | | | in release builds. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_USE_ASSERTS`` | Define as 0 to disable asserts unconditionally. | +----------------------------------------+-------------------------------------------------+ | ``TORRENT_USE_SYSTEM_ASSERTS`` | Uses the libc assert macro rather then the | | | custom one. | +----------------------------------------+-------------------------------------------------+ .. _`BEP 38`: https://www.bittorrent.org/beps/bep_0038.html If you experience that libtorrent uses unreasonable amounts of cpu, it will definitely help to define ``NDEBUG``, since it will remove the invariant checks within the library. building openssl for windows ---------------------------- To build openssl for windows with Visual Studio 7.1 (2003) execute the following commands in a command shell:: perl Configure VC-WIN32 --prefix="c:/openssl call ms\do_nasm call "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\bin\vcvars32.bat" nmake -f ms\nt.mak copy inc32\openssl "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\include\" copy out32\libeay32.lib "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib" copy out32\ssleay32.lib "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib" This will also install the headers and library files in the visual studio directories to be picked up by libtorrent. libtorrent-rasterbar-1.1.13/docs/client_test.html000066400000000000000000000121601351156116000220760ustar00rootroot00000000000000 client_test example program

client_test example program

Client test is a, more or less, complete bittorrent client. It lacks most settings and you can't start or stop torrents once you've started it. All the settings are hardcoded. The commandline arguments are:

client_test <filename1.torrent> <filename2.torrent> ...

You can start any number of torrent downloads/seeds via the commandline. If one argument starts with http:// it is interpreted as a tracker announce url, and it expects an info-hash as the next argument. The info-hash has to be hex-encoded. For example: 2410d4554d5ed856d69f426c38791673c59f4418. If you pass an announce url and info-hash, a torrent-less download is started. It relies on that at least one peer on the tracker is running a libtorrent based client and has the metadata (.torrent file). The metadata extension in libtorrent will then download it from that peer (or from those peers if more than one).

While running, the client_test sample will look something like this:

client_test.png

The commands available in the client are:

  • q quits the client (there will be a delay while the client waits for tracker responses)
  • l toggle log. Will display the log at the bottom, informing about tracker and peer events.
  • i toggles torrent info. Will show the peer list for each torrent.
  • d toggle download info. Will show the block list for each torrent, showing downloaded and requested blocks.
  • p pause all torrents.
  • u unpause all torrents.
  • r force tracker reannounce for all torrents.
  • f toggle show file progress. Displays a list of all files and the download progress for each file.

The list at the bottom (shown if you press d) shows which blocks has been requested from which peer. The green background means that it has been downloaded. It shows that fast peers will prefer to request whole pieces instead of dowloading parts of pieces. It may make it easier to determine which peer that sent the corrupt data if a piece fails the hash test.

libtorrent-rasterbar-1.1.13/docs/client_test.png000066400000000000000000024222221351156116000217240ustar00rootroot00000000000000‰PNG  IHDRG¬'`OgAMA±Ź üasRGB®Îé cHRMz&€„ú€ču0ę`:pśşQ<bKGD˙˙˙ ˝§“ pHYs  šś€IDATxÚě˝wxUĹö?Ľff—ÓOzHhˇ÷Ţ«4AlŘPDT°÷ŢŻĺZĐ«bďŠ H•"Eé%H ˝÷ÓĎ.3óţ±“@@ĽWďőű{ĎçáŃd2{fÖšµÖôµB!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B¸8 fż śóóäE.ô÷B!„B!„B!„ż ‚  żM9HŚţŚ’ţ<üyÔ…B!„B!„B˙>ÎĚɉ Č’ÄS‚AÖRVQ6IŃ5EQő˙ułC!„B!„B!„š€F]~cfnńÁm?¶ŔśÔmá-óâeÁâăď-/*,X±ô)·xFa_€°v=Ěo(ăú<¸>FBšśÁśő+ë.$DÝxË‚ž)Q ŠÁ¸i]cŔÔ·´ůĎMŞ&„4ţ`BĘA€"„Ôg¨ŻčÜ Qí{/Ľőćh±>Ą1Á¸iuFşq}ťi@ Í !„B!„B!„ţMA€)·fpč1óWU~«sxßuéÚC'¶})4>˛iŽ3IĐsć®Ę3ĺ@-Ľäiáű30KŻĽJ÷ťł€`¬šŮř:O9-ăś|¨…tn Ć:Śż!+ă@/ ©§®yÉF˧ľB!„B!„Bá?†pćG>wŐľé€TĄ¤´ŞÇcşĄݸfăˇă‚µĄ'sŞFt§-: @qÎcSgÍśęYfÚţUwpU))kVŹJJť5}Š“ř×,˙&łÖvů¤{6¬,tSŔbÂĚKŢąętąŽśqKŔőęęš ŞŔ%łçšę2ظ_ŠNąćŠËÚ8ŃşUß8–×kÄŚ$1wő–4čÔcD·vŠU[ŻŇaŚCç~#őę$sµ°¤.µKâšo>Í*ńŚşdFżžťî[żćÇŁY…á­;ďŰ™‹¶ž[ý´zĹÁc§#ÚtÖ§>eýŞĺ‡2r8g]zŹÚNzóőW ur|Ęč~ťUMęß§ăő«6ÎäŔ’şôš=cJĹ©c5>=;mwVQM\R—ËfLqČ,ăĐî5?˙€BľB!„B!„Bá?FýYÍü˛Žíptźx}qŤ?+ăȡyuůG‡¶u9ď{oë‰mź€łÎ0ęĎ%ěíÖí=žťöëć_ö¤ď[/twmImC9CZIŐiGF~É©#‡łJňŇ6÷6=§¤jÁänF9‰Łn¨Ş,™”bRĚ8«é‘–ťwý¸Î×?˛„+îŰÇw‡đ¶köffřuíć_KsŇű¶‰¸ň‘ĽE‡ ËöäěýňUL0ŔKöŐť<]T|úDqŤë›'Ż3µśvşhóŞď·ěLË?y StwKĺ§3Óö9U]Ů7AN{ť[«O©Ě;6*5n¸kÉÉü˘‚Ě}˝č>ůćš ?uüȡy®ÂŚń˘)±Gz^yŢŃ‡ŹźâŞűćIÝâ68iđ'm÷jŐčoć!„B!„B!„ţO˘ÉŞć› ĐuâŤ5µŐW ŽsďÓĄU‹ŻdäĽ÷Ý--Żj0K§i>ßË‹' čH't™pcmC9y•µ×Źď<ňš§üuą]cÂ:gUÔľxĎŤ_mŰűěí×]zó?^]xůóÍ:¶Ĺ\_ń_ ˘­çÎĂ'vnß\—uŐĐ0gń+ŠRóä-WO»ěęĚĘŔÚW"şŐúo™ÚMn3®˘¶úşńˇńşZê摏·~ńĢ{^^ůÓ{wŢpÇóéż~mF¤ű 13fθćƇĘëÜ‹ç Śé?Ëĺ®˝nT@ĚÂÚ·ďŮşĎô:Oť‘˛/Żňهo4¨îš[ăÖ[µűä¬YsxŹ`Áá5[˛€˛fîÜWWą]ŞËoÂ,ZPm“:đ˝÷?nm dç–  R%É[]’¶żҲ«R’#śGkë*JŇŤ”SU’#ŤŇĚډ (X˝µĺi‡J9E"ŰD'%•śŘW€3NVÔşÍv;”üşôŁÍóžřöÚ‡Ü>{率_ňAł{v!„B!„B!„Âżó0¦ëÂŚRÖ°68˙ü›s„ę_<#±uâ/ĺÝŹ\ÖĎáö87Ę!”rĆuŠ›#Ęłm«8î#¶$Źľ"Žć:ŻžÖé睇€ŕfu1ެDąď–kWÚWů&pŐú5OŢUF 2dôČa÷˝ľľúřýÎn~pÁ´ĺ}\Ë ¤aĹP˙?JčZ°®ŽĆX««Ő.ąl~kśÝµ}ę„ëvë„`D)µŘÂlwĹľ={ňOĄmÝ{lĐ1­mú#÷ÝúăľîńÔ¸Ş~X˝ň@Ć±ŠśŚ5k~ń1@ˇ&׳¸ßSyčĐÁüś“ż9í°Šż®Yľâç}I íŰíÄÁ_ľ_ľ®˘ÚMUďŮóLą??öŢŹ€4_€qM d=™_X—qřŘ©˘˘“_}¶¬;&O•µ}ůW+ÖďúeDw™9<ő—ťÇ;uH|ű±{?۔޶Çŕ)ŰďŘe¤ÜýńúCD #̧wetčŰëĐĎËěËu´J2j´Áźµűráó­j~׋ŔŮP=Ç ˇuB8Ź›qío‰˙PÎJţ_Só˙>]˙¦ô˙"íV C!„Bř[Ŕx‘ߦS·›o[të WĹa%›f fßđŕ÷Íš4 ×§!ŇŃň<ĺž©Zř±ůďgƉ‹.šŽ*cNîţÔű?×M—6 ks%5ţdÄŘé=󎚲śîV‚ ž•‚‚óhaAŔFŰĚήź|óÝÂys˙ůţŠŠÓ‡»Ć5íiˇć :—^|¶wĐŔú·jŚë !b˙„:Gaęş^0Óß͸ńżĄë/žţ-(ýwĺ­ĺĆ˙ťĄ×°„cŚ›+ő%7QŔłSţk- !„Bá8gPxň؇'Ź©aB0ŐuHc”3şé‡O7ťůŐőf…!$`LŔĄĚ(Gש(›%üţçX‰®i€L)‹ŐÂu]Q5A$ş¦sQ6IŃ5EQŤ*!c$"AXU‚Ş®cLpʱ>#â»D OÜ2ď“5űN[¸)‡‰€8ăőÁuĆ ëg|‚…˛ĚŁź|ým 3€f)`¦S$â@uť!€0Áęş®E®kOiFVÁ¬ą7]9sg?Q¦#L0Ţ„?çí’lBś*ކĆ(cMOu$YBś+ŞÖŇŔ1UQ5I’,X`2™(Ą! Á˙­×;Čbµ¨żFyK ް P©(`ÎR*"Á¨ľ70ŤŠf×5ÖŔ3ÎÂaŚ‚ÁŕŇĄKUU%‚(K"gL Ůh^‹ŕzuŕśéú\$ś% PJa„8pÎĎ/ Ťş@A–¤?‰Ň?ĚŤ˙%]课äů· ôß•·–˙·–^Î;a śq@ŔąQr=Đ9)q C!„Bř»€FĎą)»°üČ/«:[DŚA0ްsÇ˝Ź=ýÔă×]6Ţ8š!RĚ»züчݙ>¶áôćlÔűenÓsŰsFwăqĽ9&®S—ŽŞ?9{ákŰJńń÷–¬Xú”ńRĹđ==ęň3s‹l]ŃÉ"!D H4ĹÜ8˙–ž)QĐp"ĆČX5=Ń!„`ŚŤ­¸Ć#&"ĆÜv÷ĂŹ?öČYT`AEˇév Q8“‚1! ň™Ú Ş}ď…·Ţ-hÎb…ńDQ!¤Ĺ-äóqĚשK‡ßĺŘĘ÷źF6»ł˛˘śsžs`Ă”KFνů…*7Őµ ?~ż? *Š˘(J0ôűý~ż?h¤sňśŤ–ňfĺ(Á@ `ü)4”]_×™’ĎŞ]Q‚Á€ßP|®m6äU¸™®ĎjF0¨hL«Ë_tĂ-{˛+9×A…sđąËĘ«‚Š ř9ç§÷Ż»qŢcµA®«ź?Č9ýöý—† üÔ[źqÎ+ËËĂÂÂ`ôśyÍ$ cˇţř7vGS-h1ĺ‚’€ˇ~Ź AęÎ|… !ĽxI`Q>nҤ‰cLFř?ĐLšŐŽPăţtăööŮ”^Lž–¸qÖfů_LWłD,H’$gřŚpc›ëF5öŽAĹą)CJ1BF Q€°v=Ěź/ @pţ6×›MAuă- ŚĆ‹˘@06ěÔßSzn[Ů^¦Čř~ýô0 Sb+Ň`XE9|Ü%—^>sZRëhŁgĄ –ÎŁţ”†ŽqB!„ţ^¨ŹWsËEyéŁtΛ¦VĎýëý“Ĺ®¬_ľ2\ćłRZ8ę7®Ýôâ‡6X1bď~zIvYUţŃ],WŮ,ĺWż2˙RD€Âă».]{čĶ/dÜkl!AjĐŮ=ŻŇµxVrVłĎüzŐçˇ áćă´±×üCăfőٵ@‡ń7dečei¸«ÖĽő±J/˙c'·…,6GƱŁŐ.˙Ďź>—ľKÔ›ÔV”ćć5kCnNn­Çűń7w1=·˘® /ݏŕÔëĎ<ăě]&Í-/.ČË+(®ČKŮ˝őŁďŻőÔUćääćçĺĺd/X­iL$Ľ¶4ădNŐî”C“˘ZH9ŕ‰]zĎž6¶Ş$PWQÔ8D&vž>u’“ř×,˙&łÖvů¤{6¬,tSŔR«é—ô9Yź‚ÁÔ·w‡ëWm>ś gŃ…0Ł4*)uÖô)Ně[·jeFn`qä„)ý›p€'5©]Ńôč7a`;éÍ×_)Ô9Čń)ŁűĄjşÔݎ.,©KŻŮ3¦Tś:VăÓłŽěĘ.®3.ĂýYŮŔ1Á8k"BtT|×Ô¶ČWô»NćÖ˛`ŐţĂŮýĆMîÚ&‚q¬Ş~\¶ĽŽZ'\21Îańş*OćŐ˛@}žnIQJ ȆnÎ9D=Pw2Ż&P[rřdѸÉÓ“cíAUÍ?qxߣÔ6v„„p›Ď]™yަ]ÇҲŹ·'$µŠtř<•+żůˇĘŹű Ú5%!čîőË?ÍŻa§LO޵jŚŞ >ůvYb»X9L”AX“3Ž™řKżűiëĄóߎ·ň*fŐĘ2~ŮphČ„q;ŇUŔ‚$‰TĄÖřÔëfŚ_şâ‹ĹWŽ3IAťB\›ÖÝZĹť,A”ŚŤjÎdđÄÔY3§:d–qh÷ÚźăśKŃí®ąbv'Zűă÷3r@ŠjwÍ•ÍR.N:«šÔżOÇS8@ÇK®Ľ{ÁÄŰw­ęzíꏟłvهëł?¨;ő”&wť3sZ]áńĘĎMűí¤Ď6sTŻm?®Şcâ´™ÓrŽěÎČ)•c’Żž3«Ť­]őĂÁc9¦„— LÝľzuËyZâF@=űÓ_NWCfŮłjÓ®©ý&\>'µsęä™s[Uřü˛ńDAULŰÔ™—NqbßşŐ+3rĘ"ZwÖ·3m=;¶úiőŠÇNG´é8¬O}ĘúU?ş~˙ďRj\°GN»lHŹö5ŧľůl™”–W÷3cL·”Ł×l{jâ÷_·şě’>ö>©ťO®š;;ł„Nżqţ¸–~Ý:ź( \~ĺĚb/‡ćŢ&˙ĆB!„đ÷Ał(ś D@ sGç‰KŢzŁ"s?‰ěxëM×ĚśrÉ‘Ü: &™śąĚ€xłÔ<˘ BŔÁšÚ7΢<ü+ŚüY† úqě w9p>ă`väŕNëU‹zŘÄ^ÍĂbIhÜQFÍZ óŽCW­]éô¸$gj‚3č÷ŁÝ÷ë~JkĘYĚm7ĚĽüö·_ză˝—n>±tý1h3|Ň’7ď~ăźď,zţMZšQÎoąfĘȡ»ňÜŤt!ŕThŐőűuë:™ęĘô¨E oś8b`¶mě[ożYv|‰ěxŰü릌’'öYł~­Ý•ď’śťăíĆžľĂ§Ýw×Lłżä·ďÖT«ţö˝F}ůÝ59inöĐ­×NĐ+ML]ł~ŁĂSXKně•uËě1ŮŇFzăřřźsěěŤC®kjŔ´‹˛Żâä„Ń3’zN¶ž~ç“-[N5WOť|i IJ¶,YňŻő[·‘ę¬ń٦·5ňĽűŮ–ÍëSŁmH\ŃqB¤Ľü˝FÍHě1¸C¸öćŇŹVnÜ–¨äÜżč„Cµ‚Ăď,}˙‡ő›ĹâWÜţĹĆőËÚ •Ź.=ú©UONo˝zÎ)©mĎh}óáôw_}=\Ëżőňëłht϶ü/ľýäŰ=ĚS.ťÁ[őwçěJ«şÉ,ž5qŕś ’X[\P’Źőëô«QŢöÓMJűxŘßr@€1ňűŐŢCç~űuI•§µ•č”ęşTĆÍÍů0Đ9r$üýňަęĽ:ńŞYŁ·mźęŤHýaőí…šÓµ0˙Ćk®›6ak©}ĹşŐɤút-Ü2oîŐ“Çě÷·[»~˝íŻ—ŢcHmöo« ÉGŻ_ÍX°g‡ž‡ŕčĐąĐuŮšőĄş’ ©o·”ŰŻá.Můř˝çF­]UÇRţőŃďŢ?3٬\·ľ-Ş<] ·Ütíś1K:Žüč˝GǬ_Ýrž–¸ˇťű6㯡ëś”ć^>zŐ¦©ťzŹżĺĘńş¦˝ě†ŃDyćäćîË×oH‘jËô¨;o›7jPč<ä‹ď?©:ť^ŁXď^8oÂŔ^u}ůĂg•§Ňkëť n1z论čëV] ß˙{”bCŕž|ôěmÓöí=СçbQ)^~Ş&"±ë#˙xĚKÂ’^7ypďß‚mW¬űŃhóü®ľqöĄ›Ó îůă§nťşoďöÝď° 5˙ü8GI0ŕżţ‘%ź>qýÝłÇ@˙Óî˝űo)˝íŔu])®öćmŮ^ă§Öđp±a,vîŘą°xßwË–Q ĆZÂŐ§|˙í2ŁĆ˙’Ţ !„BáďˇůŻśęŮ»®żţ†¶qa¶°ÎŻľúŕÄ1Ź|´Î®rÖŘ7Ěőż#ÄÚµ—1sŐŐ6V˛$wÚŠW]€¦VmÉÁcš15?ĎßúÄKf^×F(ęÖłx屟ß÷{٧ĎëOt액vÎĘÚ}Ů€Ý'O·n×çŇ›çŽä 5ĺ™ŰN×ÝŻÖŢ=oŇׇcOçlî;¨Ë®Ľ=Ťu‹łé3nčŻN핡t:yz˙ÍWÎąçÍe7Ě›×&Ęn ëüĆé߯Mě¬$©¤sĎ~dŕ•G6Ľk¸ţäőE»O¦ŻăÎ)ĄÜWőřM“ľ9—W°ĄSבÉ7¶ËR»÷ăfţi)ŐĎ~Ěúgpě\—¨€0B3¦asüKď}=­]`HŻYĺAž˙UšË˛}ĎşîRö€ľ#>YřîA—cëóô|Ľ¨ĆV•őËá“&ł Q5ŕlŰŁł°)úů·żÝß2jŔĐ÷~ŘńŃ]ă˙őů'µżZ=`ńÂŰ·ĄΑ4ŕ€ ŽÝ$WűôÜ›>{{ńÔµ5U’…ě˙îóŐ{KŢúć}“ôĚą7ţĺŽy]ŇkqŇîźÖłŁ«‡ŚľJaźÓů nOE‘nwĆ9Ç‚@™Z^YĂÎŹ€QŃá¤ŐUno€Ř%´©—˝ł 5͜Э·¤Oľű%›c˘ě^f_~Ă„Ţń/,zţPĽúÁG·^1Ív*b|ßÖĎßţܡJôę.Ľrf\iŰVRi×˙Š$ ŞZעǟníOßr0`’ę•÷č6ó†Ţqjż=k"§ž<ôe:¦šĎă3Î`ýţ@UŤoôě&ôŽć¶gUÁ+ď|˙ü9­«öú‚µ§en`˙şÎéÁM‘aVkż~q뾣‡7ľţÂ-Öfřŕ˛Űďď«Úťť{hŢe“Ţ:TÇuO/Ľôóí…'oĽlň?~s± ËH9TtbÖř16W§ ýZ?·đą˙=Ą`Nw×­ł—Ü9űáOÖG¤t‘=…¶®Ó%ć{ňfCƶvčšlŠ˝â’ľ­žąíąCUđňŇŹîš;iKîŢĹ Żxkńڇ>YŢ®ł“W]çöĎě­Ä¶IWŚňÝŢ "ź˝±hoÖßQz ď*TSÂşoذ)¦S×ęŁ[~ŢľŁH®Ú|hÜ·k¶ř=ukľýčŢ;ź\±ü­Y3ú)«żůŕÁ{ź.§Đ$NŔ_ÔÂB!„ţÇ8kUĆ^`bj˙Ąďżg žĘ+A4d ĎO Îśy$Ů,ĄůČn\¨ő(:c˘Üä<€ŽËŚ!Âđ- ŞiŢÚfűi-5¸>LNTR»ň¬üŘŃÂ*!&‡)˘ş$;ż€ćĘ.6Đ®í9׌ҧÍČN¸Ż9C.<đCĐŻÜUiK€Ú•íQM¨Ś8L‘U ĺ8YěDbl|ßwß˙4Nđfç•"Íç¬1Mj/Şr‹ 7šĚډ (X˝µĺi‡J9E"ŰD'%•śŘW€3NVÔzżú‹9VĎ6Şííű¦$DůŐ\[¸PUH¨©LéŮ)ܤérT÷a+Ęk€a{r#Ź5ŚÔş•üüĂß~µĘáä4o]ěpÖn|lXűÁâĂtŤĄ$'WťĚ««ĘąţŞËjM­Çöhă-„R„ESť.DWUST»koľä™k.y96îŠîyń©ŰrFDŠ;Ö±ÉďŤNé5¬otÚöĽn];YĹ1íwqý·«Ŕ9“d‹QŤóúíRΩNÂs]×™ÄëłRŽlvY¸!źđźřuéG›oyâŰkr˙üĹ;×>řRl»ŽžŞŞˇWŢ2É,Őd§«ô&u檨zE}JF•«UçŽe'T°ż^Hč<â¶ŘÂëć\qÓ;«L Ő/^ŚÔ0ŮY]’SX Š?§ÚÂ9ŕ@€!N™ěKęŕ®®zĺ-“LRő©ŚĚ’RÉlgŚŕçä97Ä˙]çöŕ†ĎŢYôđK^0GaŔĚ!G4j÷ţĚÂ(Ůf໦4}¤eWĄ$G8ŹÖÖU”Ô§d•·íܶ˝+ą¶˘ęď@©ˇ)˝’Eůö[ŔW]Łj=ú›]U%é‡J:E"ÚŶIněÁšśăéą©C:oٶ[Ŕ[]«SÉËDKĎŢ˝ĄÇNV–FţmĄ·Ţá Qµ?mÚ0˝us]QI%őĂŃ™c >Ş÷IO=ř`ɱ#°röÄ!ď=bŇS>\z,íáÄ„4.Ŕţ*9 !„BáŠsV5ŕ’Ëć·ĆŮť:L$ 8q`Mă»îSâđf)gq¨ĚÎŐµUűnpdjÜ©éLŐTŤ1@Ŕ SŰÖť¬R aCň<»aőűÍţŇ ë$ 1ńVY¤TĄX±9"ĽČٶU\%¸ŹŘňřâWJNî>®F]=­íÓ·v«ńS@őďgC1rźĘPýo+6G”…µoąÓ«MĽl~śŐˇĂŘ@ÂŔSGÖ šâ+­°Ću4j·ČbŁ{hź˘ĹŻ«Śé:EăAż»¤$2ąWMHڰY)ŁÇÎbBś3Ę8@sNu "‚¸$ŮJ NJV«,z łŽ%÷·0Ôç!«ľŕ¸97÷›r˝ńĐ–s&K–üôµž‚ —†‹Ł¦Ľ"¦w‡?Ľq=2¶®2WŘg˝Ć8ĆDw{l‘VĺŞf’]\üâ—w=«lýqÉĚůĎľvn«v’-µÇ;ź~j®ýT4Ż8öiéŃ*ł…pđçU¨˛H84›? @:ŐĂÂâÍe9Ą5Ú´ň)”sp†Űb"‚Ů›mW*«\Á$•śH3G‡G„ŮuÝgśńóÖśŞ{|ńŚç°O˝ő©o_ylŮWVŐP_ţUĆWë ›e% ÜüŘTŐuęŞ S<6ÍÓżX ÔUé íŃąŹ©K’Ö¸«ŚĽ­;ő D·Ú"ě‚f«$ ś†Ae› ŘE‘PżWqűUwŢUĆUë šĚZ0ĐyĘ͢ ś/Ď…ąńWÓuN>ů勏lűůűĎ·äČ’duX|giw»VQGŐ)XmámlG˛= ŃćĘ*%d6GD}JŚ5ż¸®. ŢÜ«&Śý›PZ^T%­;´…Ó' cL§†e#Śsô5ëA٬)ČŢ3%łµá+‘ Ş3°ĺľ[®íyóókľx˝×Đkk(˙űJoý~öTĺżúÚkßü|*}ßňŰgóĘŞ}a2ޱeĂŽ-[F_:9):NbR¦MJŽo ¬©)ů+ő+„B!„˙ÎÜńáśS]ă  ¨čTx«Ź>xß§_-í#Š˘‘çءí­űO_łę»;ohÜUkšÂˇaUÔP F –îÚV<{Â4ЧH|ęőŹ^ş÷¶đč¤/6nĽcÎ$M§âřYSŹnZS¬`b,5xPĄĽaŐѤ…ĆÚcó/«ĺ”1ź,}ów_lëp?Ż[tôzďă·ßřř˝QtĂ–-{3s#"²·¬Y·éxXDôń}ż‰Ž¨H€DÇÄZÍRSşV­YľđĘa›–tö­/'š­Ű¸±°®4"ľŰŁŢ÷éW﵋Žr:ČĆí?šjďĐ&cq÷O<˛đĘÖmÚ=öâ?çĎ#ÉbLL´áý32*6&Zúiĺ·Zřŕő«ľűęÓg٬TŁç<‚ůĎ9vÎĺq.™áv‚€s]*Ć•»Ýžă.3—ţ:wţ˘E·Ěß‘ŻśÔŰăń0Ą>2C0¨«”Q]ÁřU &_ÝéĹ·ÝsďmóVﯚqégLBvÚţ‡îvîWě+pÓ#c“Ű\ő•×ß´p_±[”LZmî Óg>öĚk?oßkŠę(Ţę Rî¦ ŻyáŮG§N›úÍćcŁgĚ­Ůłî¦[ďZĽŕ¶Cµ"ÁĆBS‚¨Ş8Z·ŘżëŞk°ÍÁłHüĂ—ľĺŃW˝Y{ćÎľúëźOZm3V­ŮŮoČĐÖaDŁśs°ŠŮbbś5=˙1$ĘH±w›ňëŢýßzuż.m”ÚJWŤgÓš/˝á#Ö÷ŢŰďÚ˛sß«G~őŃŰJĚh#eë®} ®ľlŐW–Nă˙ ’›7.?RP5hÄU_|ůul0˙‹ŐFř(rńşcÜáٶá'=˘ç;Ľńî’çŰFɓʣŮZXâŁď}¸ôÝ—ÚĹD†‡‰ë~řÔ1Ň tۮݷĎubÇ!Ůî|yZäĆąńy˙"şÎéÁDÍUQśďΡRŻůń׾yóůgú';Ö¬\ˇ:{Ú=0š®Z˙37‰Č÷ä{ożöî'Rm+·lR±D±FĘŘNöM»7¬ZóC |ŘßR#­ňŕćowçżöÉşGßýÝęm·^9Ěí§­âc…‹‹–Ö~ß´w~»óĆKŞŻü~oQýW«¶Ţqăx$$´!îĚ{ç]oę3÷ă=Ś™}ŃýŹ?ú·”^#ĹbuĆÄD;LP”ľúóŐÇîyäţŰŢv #ý•'XúĹŹÓ{µŮüŰöaW/ĚŃłNj¸N.H&WIĆšĂŐ3GvÍĘ+żó‰\Ň+!,±[+;Ú˝w÷ŃłćĚžÚľ]ŰÄvíş&µ9vřHŰľĂ/›>ľ{—n QöâÂܧ˛}ĚúŘłĎőJ öةăF?šSRžŇcâ¨A­ÚvÚ'yßo»ăzŚś1ut÷®˝ś‰žu…ť3Š-âä×ŢzŔ„é­ě"śpčpÎÉ#M(ýőÇ5[J‹2ŹçyFŤ:_žsą±|ďţýĹ•nAšőźŇl  ŔtÖyŕ°7ÝÔ?µŐ/k×VzGLô°1căÍţgżkíŢ,¬Ýł/C±dÂ'ާź:‘¶ý`Ć Ńc[ŮőÇĽ}ŐŢś¤ž'Jůu÷‰ÎťßzâžĎ6¤·í=xňŔú”%ŹŢő٦tµ®ŕçí 1௥Ô,Un{ę_OŢ:S);ţÁg+)"˘@(Ą] »uŢÍ:·úuýć:ÍżmĂ] ÉHOÎˇŻżY^^çw—ĺíŢ»ßËhŔSy`ďŢśěô-Ű$öď?Ľ_÷“YľrcY•kKĂWŢÜC_˝Ľ´ĆŻú«wÉ˝—=´pÁ‰r·‰€JĄŰžţד ÎG{ëM߯©ŃiѩٻŐ);u:çjGl/ó[ú&x^yú±Ź×ěŇT‰öކ†‰žWž~üĂ[ b˙irب_Á!„đ˙+pÄ`ţ˛0 Ś"üET ÎěŐ_ţŹ˙o ńÚÔź‚ú śóč¤ö#•šuk~ň~R˙´JČ„©SKŽď>vş#ÄšzŘÄ3–ÜŁO·xËšŤ;9 Ś8ă¨ďč©=ŰFäß˙ËŢăBŔ9ŹmŰaä°!b°fíŹë\:ű3[řĂŕ°lKyéźůecűţÓfk3eüŘÓuü,†4rlü”)Ą™{.žcýĆLíŮ62÷řţ­{2lvǑÇ""ŁjKó< QŁG ˛Č‚ĂjŞ«sQŔgđřM¶ŘVçô{Ľ~EMN›ěr»)ǧSó{üŞŢ°‚3*YÂJŇ–Źľíë;×%:tźĎďőa»Ý.K˘®ëŔąôűšŮn·šd¦Sô{‚Ź 7a|n@á˘lq:-śĆ஫ ŞşŮć°™MŚQÎÁçq©”ť{gŚsDś¶ű7gr÷¶±EŁÎ°łL(ĺ„`ź»& Łęü“§jůŕŢ©şŞp@˛„ŹÜ}2ŻŞ}·ž=RŰ×TUöîÓÇăvǶë8rčŕ˙¦D5HB»_yđđöMLť5,qęřńąîóJÂŘK&•ťÜ“‘SŐÔynŁ$´íÖ«Kśé§ź÷4JBď‘“{$…ť>¶oסlŽhA»Ť˘yăÁĂŰßą÷Ĺ÷× iáŐ_Č“®śăw8Á!„8gmÚu»dŘQ©úaĹ·°1Ő{O^´íí{‡wO9ꣂ ęşVźŇ-娟¶Ü)ĄŔŕ~S.ďŮ.2'cßöm8 „g,¶c×QŁG‰Ş5ß|ďŇţ7ÖŻąônl?ŕҡ'N7îüŇ+L3§4}ÇŃ̢–íXßÝ“ÂVŻŘŘH{ß)—÷JŽĚËŘżuëţ3´wč:jĚ_Cűż#‡“z´ŤČÍ8đëţŤB!„B˙ďɲ|óÍ7[­Ö€ßçóů"Î0§1%I HUTĘXŁą'‚€bŚRZżş"DŔQŞ×ÔÖsPÎ9B8Ł™dQ řkë\˘Éf5‹ŚqA˝Q1J9@Ŕç j4,ĚŚq„BüW@Ą’Éě°YS]§€± x˝>Ž3ÜilŠ2ĆŚĄĄ:c@áśrŽF”RŽ(ŚR¬k`ât†!¦‚AťRÎeĚXŘA˛9Ł:e˘dbŚéšJ7Bî ˘H0Ö4•1΄0‚@8çFí#"ĄĚxĚ"¬ë:$ "ŐHJXtĎmmŁ­:Ó·|÷ĺŞ_™Ě2ÂDgTQT#. 0É’»®–!Ńb–9A â „¸×UÔl2[,fłIşĺ–«M%łIÖÔ€×ççśk5[,"ÁJ0@9¸ľŤäŔĄś‚§:E˘Ŕe¬ţá‚,›k‹Ź~¶ć輮tZ ¨"BƆó5Ju]§‚(bJ™(Ëń` @04NšƆ7pĆXă‚b”6N}Ś”zYj@ŔłĹJŐ ¦S„Ą:cŤ_  Ę’~qn6[e‰M§Á ˛té{ŞŞęŞâńú8"Ž0§1!QĘ"ѱŕŚqŚ îp†‰˘śSĆ8§”qBgaÁf·˝nEcgŢŽ#ŚQĆ ÁT×9 A¨DBÜ»nmmĄ\˙yŮWkv±Ř,6»C"đűý`c°H Ľ®Î%š­V“D9'pÎŔxŐM)"$čóTf1›Ô€_gĚë®S) sÜřŕ©Á€·Q»B„0]Ń˙˙µEGX ¨TęUă,Ú9ç‚d2KŘëőBgó0Ć@5*šM\×ŘEÓĎ9śaMmřą)çý¶ů0gđßb–ü˙)˙ĎpC aĂ»?NUC˙Ѳp)_|©p•H€ĘąA‹"€¶¬€s@" sNÖ0HŔH˝ ¦QËsçš°Ěš¸˘D7ú3D8Ó4ýŹ‘WŐŇuëŞ"Śţ: 7d KbüÔicQ–¸®_ĽDýŃÚ,V‹đk”˙Iˇ‘ĹjUľ?Xŕď[Î96™M8Ąş×ç_şt©ŞŞ#GVVVLL´Şj„N)’D”—ťígRJ‡LUva·/1];´żOŃ@Ś .‘lě;śQŘ«_o‹€o~Î9gśĆŘŘĂë"qîqŐiAÎ4UE‚)ĚiÎ!„Ć(Łś7 c aŚÎçČó)p¦.„1B0eÇĎŠ\z§n˝űtďŔ4í,ożśsBß]S\V!Y“۶¦šB9’ERś—ăgR»äDĐťa§Óî««®őއç€ަ)g÷"ĆDő×ĺk”#@Ö°čČH›EňşjJJ+DkxJ“şŠňrTln›ÔšŞ ăĽ~žŔů9ŁŤĺ#L‚9cş®3ŹŰëŞ.)-'fgJŰ6@5ŤŐl*/Ę+©rÇ´já°Ü!‚ úęTpŘ,gy,EŔ=7‘ěć€X0ŕŞá‘±±áâÁ}ű}Ěܱs{éżßçói”…EF‡ŮM^ŹQXÎÉ“~0wě”BtUgđ'iZ=cĆjłeĹâÜ`WUcŚsdČ BáááĆç„ëŇß9¦0ŁcÇŽMËlřc¦üöëoíú MŚ0+şÎ¨—pCDŃ™†Z]]Łé”s°……9m6¬ű×®X–[«÷ę7dŘ€îTUÂśsĆ„Ć=d @7Ą!Đżýş»]ź!mÂM ‹Ş«xçÁÜACZNçś7ĐŤ8c€ŤFĽ®¶V¶Říý]Oq-ł…1ŽWvďŘݶĎÄł˘Sh˘ÝçĐÎwÄĎâdóĺcfy†˙’Śý%÷ÜůĚ܇_ěš„»o䪫!5[Eˇ¦AťcŚÓBťsEŔÎíUŔ űŁ@lD­ňşŐ`X\„€uSl&şćŻ-ó9ťá’€i8w Űá‰G«o{ęť__ç9;Vt֢ܫ_Ü@ n`öĹQŞëHtŹgßµsÝ®ş:Ć1ÂmL2çŚrŕś‹‚ sd`Ag<łÔ@Âśťňú8:Ř,”3Îرں8GX¬HTĆ0Bś3Ź®Sá(YÎôĆAMW#2”Jv{˙/ľěv®ë Ś8Sk]~Ý•wď]Ď/xőÝ1ÉŽmłćXŐ`€±:ťÚ0BM-U“dN2I:Ą:€™ŕŞ@ LŐ˘Lćppม—Ťv`Î2ÜŢgX A]ol3,B!H._]xüçgßŢůÚ…ú˘Ţ4J~ r10Ć8o¦_gôŚ7ćÇQĘ «‹`L‹˙ăsĆxë*ó Š%{tÇ”$ Ąz]]ť¦SÎ`˛F„9 Ż0 ;hőĚ—$1č÷¸ĽjXpF›}SHäťĘö3ů˘FsΑ k‹vČ2b°Śśź‡-NY €F-h®őy ÁB¶¤;çćs¸ŃBÝ@$ě+Ľç®çŻyôůíĂ 5‰¨휉˛©4+}Žoę%i00iʤEŮ”{`ýsďî~íťg/$c†56ÎŘđţÝ©¦ \inŐŐs 2ÄH]Ő8Â,k‹vÎ:|H 7”5Ęa‹ü9/7yn`‡(ŻËĺńzuĘD“=6&śŞ*m°ŐgŮĄůĚ Hp©®A;‡ą”:(äŚToi‘ se~ŔVtľ#LdB´T§uŚÄŘŽ¸‡„„€y’1Âśy9gĚĂą“8ö Ű唜:׍µ⼮¦* 3Bęç™X0G†;řERś–v˙µs5Ź Â_ç¨!ÄtťX-ĂW¬É?ţósaµŚ>E 3Xc µY/ź“b|D¸jĚ#ĚŞNĎťŮ6N#ŤÂ›J/j?CĂÓŔÎm;Űő–a ŞázNţŽúkĂ9""ćJvV–WEíRÚl×¶­Çă9ë´ü߆Ŕ9/++Ó4 "A4PGN›iéł‹žţbłh#®zŕ­'n×ý>Ŕ-ďńp΋‚VőüÝWm>î1aŞq,pĹŮmÚłó=ţŇĘ>x#Ń ^ŻOŐD,V«Áëö©:Ĺ7R‘ ÓşĽuËIż éÇ"h–öŁŢűçă1&ęW4@H’$¦©:G’( LdITxů!R–>}äÔLбßŐ}Ú‚ďŢ}APÝ &“Ě©®¨ , ő»O–ľřękÖv­ű(ÚŠěËFM9®cěwu›:˙‡_‡’3FM3RşO»ĺŰ˝&Ţ}î®g.n4‡ú›ĆÎŇ´-×ÝőÍ/[–§„óšj·ŞĹÜ\—M˛¦ióí*Î"˘Ăngš˘ę”ꚡŤzÁ4UŐ)‚Ýn ¸<EÓ %‰ś?ŹűÂşs¶•xíŮí9•÷8Ą‚ĽB‹ŐôZó™Śću[ÂcŹţňĂŇí…ýôĐ]6›ąŮlçńŰ´`µ=şuÎţO_ý|Đye @>cŤ=C‚ŢĆ/}]qUhbŐßPk+€ÍNd­Á_řč=KĽü|ç(©ÎíŐtj‹©ÉŮżđ‘OW,ű0^VĎ’CŤ˛–řs^nÜe'Őuî_?}ňŞ»—Xbbü5 óîůçă l&ű Čf ćzPŃÎ'áśs nę¦ĺjí3eĚCÎXZ‰ľwyWxx”)ęéL8çĐ$R-ç° ů—»]_yIáv9ęa§ëńR=ÂőtMó¶×O«Ż3F GÝDËť–ŠĘŠ ę\Ž“^˝řęI?¦{ÍHS9‘jë2móŠ⤠۱T`BtźŻFQtUEż·ˇů‚SJD±  ŔńǬ–?¨¨8 b·;{!„›§ÔĎhĂ`vŻVu~•ŕć$7X6‹Iú˝E“MłYF}^ŻŞSBéĚ<Á Ř,{áľë缾~fĎ I„kósµAśÁf‘.°vŕś­öţąsÖťrµŤŠüń_.űHÄđ'B`ÂĂ#[·NŘđů'…JÂÜĆ.žÚłěĺ÷~ţpëţVŮ˙;ď©YłfMëçjΩf@ŁđŰź~ďn«uĺ+Oět9^xć~¤A ě¶™ OĄíÉ>Őiŕđńa”!B}żnţ±ÔŇŚÝ&ĘTšpâĽýé÷îu8VľüřN—ăĹgÔ}> weť®mťÔFB´ŕô)[lbČÍ«ăJݱůÝ ďĐ*2ŕ®Î+vł`ݱůÝŹhç<·.Ż]˙ŔSRmĹşRE”$.g9m#K΄{źycPź^%Ç6/ľ÷…ě«.ďÂŽ´ě·§?Y[đíMŹ˝3aŇ”Áa5{=Ţ{ČŕŮ: "™ÍľŇěý;3RA ĽŮr\`j°óČ9ő™,ZťĄiď|á«Á˝“ÁÇîöÍÁ}{źŻ®ń.–(fĺ•jžĘĚÜňA#Ć´‰´Ő3ăś ˘XY|Ş6hę”’ś:ˇYcťa­?ţň >˝KŽm^|ß ÇgĎč-,ů׏wżňɵcSOeĺÉ<¨Rä°đ÷?ü¸Ě É\Ąqvć ŁTÁ‘şu|ó—ŁŐ‡köfď0|Q݉­_nŻ|íă/S…Ľ+®żcő‘+Ű3x‡ď-yuÇK^zęá”.+˘kw~ľĄäź~‘*ä]5ďÎU‡Ć]7°]­O!s÷áż‚sťV]ĆöŞ ĆŮNC"-6eî'ß.Kl+‡‰"Ć&łŐë/ţqŮŠ:Ý2qň”()v8«}—Î2ćŚ# ꉌSQń1µŐnÍWyŕHvżq“»%Fúý°ř©×M•%ß%ŮÄ!&«îŻüqŮň:jťpÉÄX›¨ęzţ‰ĂűĄ¦°±'ÄŰeŠĹ@U~“Ú‰Ĺj9–q87ź˝ýÜ}Q&ŽůkOćÖ˛`ŐţĂŮýĆMîÚ&‚#!P•˙ů×?G·ďŢ6ÎáŚin•Ř<˙iWUqíÄŃ#ÄťÜ&~ÎV2Ç‚LüĄßý´uúÍo%Řŕ”"`»`ż+ÜÚÍŞgůj^¬Ó§Ű‰GQ xÄ’XQ˘UwWłt[‘ů›:zÄ2#ďgŞ€@8`ŕSŽřt·MKW «şI˘^%Ş^ţPą˙€.LfVgęu3Ć˝żâ‹ĹWŽ3‰Á¦v"z°6óTu»Ž)&¤eĎp´n)řNäUkKź,7yzr¬- śźRÎ!”«dyľgď6v{µ·zÁ‘̬Öqý,ÁđŐé‚j )¤ÉIăÜ,Šé%ů9~µ_?“R{÷‘ŚCq±ălDŔ€r¡šA¶ Ŕ±)ěŤ>=˘©˙ÎCi?V{ďLŻRu§(¬+,¦&Çh§Mçq}GYĄ‹ˇ6[›MA9j– w~˝vÓԛߎ1ł2"ëšr¤ÚÓ-2ü‡#@$véŢ×é,wWÚD—µĚôôř”NN“ŔÎŘC.Í[ľěóďśm:·ŤqXŁZGĘĘńă•ÉőzqÜžÔ*ŇÉ‚uë—/ĎŻa¦\Ú!>ĚU[‘yަÝyňLś2=%Îîjo%.ť˙vĽ…ÖBXʼn /7§}J÷hcQ¶šdI°d+ĎÜxVžÉ]Ł{çëfŚ?źŚFpĆO&y›Úp“‰y1Ĺö&V]ć‚Ŕf —Í8c‚Éä*Ě;°wĂú­[R·Ź-Š´´ďÚ+>Üš›u8ǰá2$©˘¬4űÄńF9;erśĂ‚®¦üńŐćŰç͸‘`?2銻M˙1+ľ_V±ű«Ů×>¸őňâYU¸O·ŘéŚtźŃ­}k5čk‘ϸ‚(K´PŃ yÔ’XAŇ«î®Pr¨ĄWhRo“šÇ# Đě^GHD¬Lń~ëw>k,jy: S¨›5Ú^Đ9ÄĘQ/D¶×·_“¦‰BPTżFâ$ňé~z%"üă»oÚPöů‡Żó€?P’~(`ęŮĄýĹPaśŐ×ú9B7\Ôţ+€gLŔsÝß墭î3hh×”x„˘»×/˙ÔčĺäX[PG"ő4I±jŚŞ Ga‰ADˇÉ*—s,ŢŞâzËv(ł÷ĐѽڷĘ9~h˙ACzÇ'„ŰĽ®Ę“yµ,`Ě&um›€‘-€±îܶmµ%ô씤˝çÓAÎÉę(ĎX÷ňů­ Łş5>ĄŰ )»3,™dŮbżá¦[ű·9rş(=-ˇ}ʆ<í3vC&ů/´˙ pα`ŇjNM›>ýýµ›˙ůü3k÷T:¬fV^3yúóoŻţéł—¦]:«¬şňéŰŻÚť[étÚĂ"ë ްੜĽ“SÇ ^đĐ [×~1mÖś\—f'čuyýF„!Ŕ"QΔ3uĘĄeŞDkNßżč]'r~]ýѬY—ř1rĺL˝ôŇúÚ÷VŮLDHfćŃ÷ßxvţŻą4"K˛§üä„ŃC<ôâÖµ_L»ěň|/"žĽ©Ó/}wő¶·žżwа±™ĺ“HţĐŞćO¤=§N1™LÔ[rÍŘ ľţduđs6ĎŚĄ{miAI>ÜżSŔD p‚dę%$NDpĆ‘… łJ«(Č8?ëđ›˛cu‡G©@ö đp €ĂąŹXáŐŞAh/q/C"€źi§Uő¸ÂE"1âŕ÷«˝‡ Î=]^RĺEŇT–9g‚l©ÍßĺíĎÖj&+«{ä–YkŇ*/oܡ‹ž|íןľştĆôĚĘ ů”"Ä‹‚ŘĆ,{5µČď7cŃ‚‘YÓĘ‹öńµ ±Š¦5Ý5 ¨ń+ÂśIf9Ńćh'ăCî „ĺ%‰› Nß›‘]Í@DéZ®ŰsÄU«bś K:gcMńmި›Ň:ÉŽ9bú»Ç–—×亪ż.*rŚ7'öL_ éß1ŕ"ıhz¤wŻ­bp@Hg4Ňbî”0Š7[MDŚOŐVG:lŞżćÁôŚ,•Ú iÉë gçisZ]­Šp‚,1żÚ{čűÂ؉•őşűç^ąäŰźŹí^˙ňűľ'oťł·A]ŻżőéüüÓŹÝýŕ®Ěś_W4űň9yuŠĹj+?±cÂČ×ÝńÄ–5ß/y˙›ďŢ[rĎ˝Ź˝ńňc×Ü~˙Ż˝úú{klÔUtYý] „4%ߡďĚqdYLNîbCĺµđ…Çî~č™U°Y"‹ÜWZ_—ÍÉ? kűé¦ /ŢrV廓AĄń+b–BcJ……9íÇ[oď5üŇ‹ÍÁ8C0Ű«­}üé—¶ŻűjÚĚŮ…ĹĎ-şşQ—]…‡Ż˝éáŠI|pX JĺŐÓ§żňůšŹ˙ńŔ !Ă9ĺÖĘŹĚYř´ˇŹŢ2ëÇCĄaÄuë¬Ë_yĂÉg_yŐŃ*®”šsŰ3çÍsŐŐ‡‹<łůâ­Ä~ť|Ţ€Éj=±ç'{ŻaMi—dѸ—ÎçŚIfsć9ydłčő)ç×wŽE9PuęĹÖxöĺsň\şLÎŘpcr€{•Ć`$ł%<"Âi ,,2"¬23}ĺŹjtĺ—u«ż_ą¦ÜŁZĚbŁ ŻUHX„mÝ;/ÝuďoľbČá+o|°Ńf .Ľlöţű,"i6ŹjÎ ż/1ጚť퓢† ‘AÜ>„\ŮsÇNXuĽÖ•µvâŘ«2ËułYţ>3ެL6XZl°aĎG9&›[žťr@2Ň2‚¸Č 5ĎŐh5I€ÂÜ÷wl/§g—ä ŹIŹłš-fsl\\ll Ôś¸věÄ?DĹ#˙E[­ŚÝë_z÷]ŘE­â¶ŮW4ôňUGJÔ,ś}FwŇĘuÁ›;ĺŇiŤs‡Y¤gyŤĺL”­'5,ŰöuË_|ý󪪢Gq.Q juÖřQŤó„9ąuŞDĄůĺŹNĽöî˛ H’ČÎĎUΙl6ź«qgY„¦ř«,*.‚}üĄ“b’F˙LhĺČa–<ńP@)Łä×Ý«ő±ó n˝crôŇWőµ1sFűÚ:/!żDDu]S5ĘcTÓTN9c‘Łďyâĺ‘­‚sg]™[ŁĐ˘u[Ó*{őî. 4÷ѧżűî—皨rŐ0#d”ŁjĄ”1Şi§”bB€81 Ë‘÷<ůĘČVÁąłŻ:YX;Č GŐ§´\×ögFg݉6~3®cęJ ČIt8ţ…'„Năz§$ü¶Ii×&â»Oß Äö;&Ńĺö‰˘TŐęWCTމŔx“€†Ŕ÷ő%c:"ruQćšGg?x›¤˝Ş˘hÂyëÝÚí `$vçŁ˙ÖÍtĺ•sŘvô©«UŐůŚ›Pc%ŕm×ďŇ…·fřÜť_"e‚[gm_[SÍĎżđ„ĐilĎä¸ô˝ú‰ĂűÍŽ¨ÉóČâ;_~ď“Ń)•ď}¶zţËźhk—ěŻĂť¶Ł;vg– ’®ĘQ‰ŁzvII`űN–…Kűő”žIŃ«*\QNBLgL0;¨_ĺ1Ć45čÇÜnqĘË+U5Ňa2ňłú5Ă}Ä_e3ţহmŽ_Z'¶ŰůÓzvtő —‹ăđćwÓÝ–í{Öu—˛úőłę`E·Ţ]+µlˡ‰=YZdű¤Ä‡.ÄĽĽô›iíĂú 9rşĽSźVŚÎ3ŰVĽ™î¶lÝ˝®»”=°ß¨ŹVýöňĽa˙úü“ZŹ_­°čÖŰö为ťúşFh»ű§őěčę!ŁŻŇůĽ)—Ý885jčüO ?ŚiŘ˙Ň{_OkŇsŔÉŠ 7í‹j±ío×ăŚuCFĎQĂtżůĎŁ=ítE·Řż`1fDdçL×Z¸ Ç`Áí©(ŇíA¦Ŕ!Đ8SąĆëŢ«AÉ1,Éťqíc¸ĐÇ"wy€ťąáŤ.ÍűŁßv4ßęâ^@fÄUnşÄâyĄFšl—¬ŔAťřÉËKUP‘O€p*:ś´şĘí »Äžł¸ŠÁ°QD 2 Ć4$G˝đöłú[F úÁę}Ţ5ÉSs!J9p ¨&ŕy$3»8 ôŹmŐĹjö=_”]Ű­·V–sDCBÜZ3˛4ĆăíÖ´ě’.Ź%Xąß§ĎŔ}:+Űx¬gŹ‘SF%PÍ˙SIˇßď.eB'ł¤Pn„=…ĹÁ>.Ňá§”R-×ÔĄă¬(§W PHhŞşÍű‚Ś0^«j /x‘ƨ(RDode önVSF-?UWk¤XLźO;úDŻ^ě’żáj##ŚZnsŔSĆ„NfIă3*Ú-ö ‚诬<–žsĂ'KOé_ăr…É,ˇ}‡´ăĺć‚m94±¤‡%Ƥö´ä“Ë\~­zŔť·/Ú–VĐ»]“Á¸és;ÔbB€žWßBLSĚŃŢűňÓĘ:ŻÚ ? FĄĆ† o,żÁçśK˛p*m_úÉ<Ń$GLWĚŃIc¦]ůţ ŽS†,~đĄ·úÄKµu­wÚ¬uŽ:˙ĆP=ě˛OurďřĹŰó†>ąďüş˙·źęÖî+~kŮ+ üYűé7úĘj@ çáCŚšĚ¶ă{V]7ď¦Ě ëäv—^Ň+:>vÎSOîáö™oBđňGżaBך:‘mçĺ3PąĐV–:áşÇ*ĐÇ"wy€" ¨‹ť=Čó3? Z†‚¬„¬îŮŞđG"i´ĹóęmŻÎĎ2´ş®ŞšŞSĆ(ŐuÝWWÓiÄuŹ?qôPńßB@/ÚjMP[S%YČţď>_˝·ä­oţŃ7IĎś{óç_î° Î˙qwŃ;Ë^jL™×%˝'5ÎĆ[Đm7ĹľňéW“»ĆćWXśŽwľü˘˘Î«VXĽđöíi…łZ#cŤyÂĐ>Źä”÷î) ’řŇ㋎ËůrĺŞiÝăj|ÁßájËÚt¦E“€§®ĎĄ·MZ·çň±ýám®şí®ŰŻźŽ˙Ôɡŕ čłçŢ&ţ±đůŤ€aέOÝwÓeë˙ůóńź?ÉNďµbĹ7K¸B7‚˛˙^˝MžŔ"„ Ä4ĹÚ¦Këč° Vd¶·Ç[šUá#]ó[Ŕ™Řą{‡`PÇ›L&J©®kČ( 7. ŚCáŚ2 „ L5ՒصutXP+¶8Hu],šĺwęŠTÝd‘ ŻaŚęŚq‚2ÉrCíŔ‰vZ>xń®ź‹Đ;˙şSÖüŘlĎŮł˛ «óoľüŐ?—ËŤ÷”R&€á>ěLJĂđޤdÄ·8,{Vo ’Îc{wô|QŽóÖőĺ«÷Pލ¦Ř“z%FŰ}:Ol“XťťK…á ‘cŁ—wöőównüz›oř»×Íńą*HŃscÉf¦Agl×g_xĄk¸Ďu͵‡NIG¶d­P•»#ătQťt(ăDEÁéźÖď0Űmś zmť†Ś5°krÇ’´]ż˘ÉťŁĂdÂqPĄ”2N(c¨~ŁŽ1.Š!*„ŰĚ(¨ŐçA”2ÄţßZŃ#IbYiĹ]R;YĹ1íwq(:÷VU´ďŐ)ܤéRt÷!}Ëݬ_J»ťG¶n¨ŘĽ!ŘçÚDÜ)ˇ§CVLmű¤$DůŐ\[)Żň‚9çŚ# ëÁÄUŮPŽŐm耪*o 2ëÚ«/wÉ­Çöhí%3ÖN¦wIíŘX{@Ń Fľ€ĎĺńçTÓ(—´·ďkÔe ęÜn%˝¸w—ÎDYT›~)Ž J˙¨Í˙iݍöc `ŽűlË/ş×[ărˇs.{Ι$[ęś$q ď{ŐÁ2ń´ËHÝéU hř 1ˇµŻÖúUl#$ćçHF@9WŮQŕʦ"¨Ń•lťú‘š©r8R0 ’Ĺ&ý —#:xٱ(B´@őłeľýp% @9˛ŮeIඣÖ‹„SťNĚÖŐ€łĂ aşĆÚ§´/ĎČRńdlŽąĄăĚ"ŰţŃ»O­ŻîĹĚě=ŢDRU”ͨţÝn_™ŽŐą;šeÜ„1ĺ\P(m?»ĘýČ˝íí¶(łL9EqŞ®¬tÚÂÚÉR1Ψd[ÔąK˘Ŕţ™–ţCAéc©m™ę_]R=˘M§Ěk5nĺ‰ ‘ź;ňŤ,ʉm}}'ÇŮvżI_hĽ^űˇá-3Іź#ňeÖŃ_čĺžÉ&ÎJí˛ýˇ®Ý:‰ú˘‡öÔyG…Eů(5T4¸4şP›ţˇ ě*A`ŔZî „€ŞŞ9Şőu7OzćšK^ŽŤ»â†{_~zaźÄÖ§Ó¶n(ÝhČaj›~’ďÄUWĚŞ5µŰŁŤO°`J ß×uđŘť“ŞęĘbZGłD"ĂĂ5Ń® A-1]C–¸O·ě ç“Ő‹U !>ĘúÂâ˖碟ֿ`şÎÖO.yˡ1lôĎó_Ň9`Šm&-ŚRť"„ŤQ†jŞčLx˘ÉW›öf/ľ$ő±7ߊT 4|óľÓ]"ŁŹmzűTzź?0š#¬+g‡a[GčA–Ü6©ş¦zXrĘľ#[7TţĽ!Řçş$ˇS|÷pŞő©żGČ1ÁuUŐ)=Ú;%•X˘SŁmŠÎ0BX ˝Ŕ‹ËŮU)îX˙Ĺ&ż7¦}ŻÝc  ] OĎ=b}^?¶^¬•Đ8G€8c˘Ĺy|óGŮiMf2g=gLj1:ŻľsÎhö•¤_{íÍŐBĽ!?„Rèń5ŐuĆ8 lśm MUeä?ôíw­Nç űÝá}&1R÷z1`ŻÇSg!:BF®ëTÓÁlä0 ?}¸°şÇřÍg™gs#@:Ó#lŃ˝{ö*ŘşǶIv–UÖŢ|ďĂx{µ{Ňş{nńÖ•#N%öĽ|f€,XÝçSóiÄ‹±ëg,măňrhř¨ÁörŔŔUŽŁ¤°{#E«~°R9ˇń{¶÷* Ţć·QĂLBH$¸˛Ć}ó=ořţ˘©řźý!«uĎ‹OÝ–sčL/G§ôÚ7ęŘî_˘š¤ ëť¶=Ż[×NMç-¬50®·lť’*kËśQŃŢÂĂsŻť_/˝˘…0Nµ =Ąď™9IĄW˘©Ď˝}ŐŞ¨nĂş´Žő‚Ŕ.ČUÎh g¬ BQUv&~ří†űłł÷mZůô«šcۆŮ-ŻďĎâ4ŽpĎŢyÝwGOŢqĂüEĎFoźrÝăqťş”VŻzđź};ŹíĎnÝ.†jô"÷s1Ć„4,˝Ć3ă ŠŃ ¶äÎŹżôć[oĽńÖ’N×GW©ę.zúŃľXý3–,`ÄlćÍĘASŻĎâŚpš%O##ľĄ8jLią®KÇ÷Sü„Ń,€ DEÇ9¬‚R[ĐX;ŁĚf5/űŕŐĺ‡|źý}źö1şFŁâ*ë”)7?4¸ťůÔ±üŘVTgö°Č0‘ĺČčh«I`ŚŰśMR$‚°Ř´d`ŚcA¦îŐ[·t;2Ţ)(*ł˙n] á$PzĘŁˇ¨0«»¶Öâ Ô>e”,[¨NŁÂť+żüč$mßŢś÷Á—Ëma‘Vł¸ěĂ3%Sť9˘"D“ #Ć—DAWö6]z·ŤÝ˛ćÇăĺ55Ĺ»÷ĄžrĺëoľţÂóĎżřÂó/żöćă7]¦ű•ácfźřmŰńÜ>c†3_0±k뢼" [m-ĚĎ‹mźu]°XăâËó3źĚoŰ.)±CLa^q“"ÜýĆŠźzL)(AŚ5|ł2$Ö•Á˛EQŠsÚť6“(XJ *%«U6CÁ‰tGlÜž>ı=2ýúÁ?ŹEJ@Ă­ÚŮK*ÎÔ.‹rŮ:>Z´Z"ÂŻŻ‹§Bb‡°ăąyÄDDÝu´Ü' äŹ.9˙DÚÄPj^ĽëŽ÷–ŻĚV8'j;¤S=,ßyóü¸ěşËF(~k®‹´9Ą5˛(hšźÔş´:xíg¶J˘:ŤK<;Ź®hâůőťSfq8ö,˙Çvo&?mx´Íć03FHU·ŠŤ™=oůŹ+?űôÓĎ?űôëďWţëy&,ô*ÁęŚMH”˘EEG4ÚpIr"Ä€cCrD+Řţćg›Ź$¶‰˙řő¤ë]»Zł^x󣰨XżgŁ`Ĺ:Šĺ^ –6G° ; á‚%ڏ»ŃöbЇaÜŁCWóß±˝p‡}‚ č”ÇEG|úćËŚŠ˙.8c‚|äl«őŢK<ínŐN˛Ąö0zůÇż˝fć8‹ÝÖ­OcĘŐ3F‡G‘ŇęŞ&sbÜ}l^ýËśËëîĺ7—^n<0:3'ÁĄ‚őłŤż^ëš}Ăc8,F$ÎĎU„¨N[Ô¸¦íÁXđ{k}`î?xÄOľ6yä€ě¬"AŕĎöúő+ďz㧕ꦶŠrD§Ľłbý+÷]Őˇç¸!]×~őÚ¬ńăŠí®ÓËă ü;ÇÖĆЧ2ŞÖ»äFŔ) jĹšÎ=oĎ1— Ĺ»nžwűżŢyýÖ…·nM+˛YĚAÍĆm;Ď‚ś„¸Ęęýëşݦ•’ńČw?řÄłÇ*}Bśë4hř·FŞB5ƧŤ)çŻKňűµ®zfÜ:˙¦[×n=f˛Y_µQ;`l˛ÚŽnýćKż·›ź˝ńäüĹî:YśÚg\Źäřík>ž7o^…5qŇŔTŞW~üŇ“o~ęĎOčî{ÖîÉ‹tHßđŹĆ”u{rmVsĐw†.ΨÉdÉ:Ľ}Wf`Ć%ăŻÇě;¶íÂu%MŘ) 2Ĺ[ôâs/?üčăŰŽÖŽźĐS hşR·©ľÍÄd±¦o˙ňĺ7żżĺ‘7ßzrÁŞ˝¸ě·Sy{üÇ{MJÎ,ě>äŇ0Č}ŕţÇźzú©­GĘútJę1rŇË˙xá•×^ťŃŻK׾#nşz¦ću)ަ4@ĄL Úöd*8tô´6ŞO‡şšşÎC&FÂÉűyřˇ‡î9°NŮ%ä§Ňv?|÷ÂkŢÝëňKşµŠď92gŐçń['Źęć÷ńßĆUŔź„pŔçë?qŽkßú›n˝kń‚ŰŽąő` űŘË,e;çÎ_´č–ů;r„ËƤĆ$uQ+Ý­;_2˝oĽ§ŞbDďdŔß « ę*eCŔŻ÷3řđŻ?Nś~őÇ_o8c®©xG}9ąxön¶ŘÖ§Ň÷?t˙łsݏâ@‘G úŽźăÚ__űˇ‚ZHřŢzęŽ[xÉurß‚«çĽőĹJÁdŇ|Ł.Ue.—»˙´kpá//™}Ý ·çÔŞ˛€˙¨ý?‘vEg 4Xűî›˙únăŻX2Áąa†˘ŞâhÝ~`˙®«V¬Á2qmŹ×÷mËŘ÷YMőłŐJŽ.v•čń@í?«k_© ×Ĺľ"¨ŔUÜTOi "ŕŇkř}‘áGš»Kb7łmŞ•{ę·Q€s T Ľ@q˝YSýPeŕMîkfYqpŐš]ý† mFTý,/ŘHÓÔ¨¸ä¶×_}ĺő7-Ü_â!‘ý®ÜĹ·ÝsďmóV﫚5k°âSć:ĄŚs™§*K:rxIÖ‰;<Ä}íćÔ¨Ř'şu}˛{·IaöÎaQ×´‰çŚiLßVYťćń"„8Biďgű0÷ôĂié^ěfőé”qNyAjŞî+yýt(~_í™÷:´¬¬®d¬‰——V IH@ĺ#LőŔ iéä¨s‰˘ŮN0;ËĺwÓľXľ[íśQ†µůŮ/ź*đű]O§ÝéVókŠ—ä۰°ěô‰»Ó2öy]"ăśŕúXć+™'~©Sú‡ŮTĆu¦5Ra\×ŐĎ×懗UÖőŹŚFś[Hpőę–ű‚sŽYŻË˝qĆĚÇžyíçí{ĺČ6%u꥔×rč«©ÜĄŤłUŇ©´}†6íÍŻ âLTÍ`ŚüŞ®¨ĐUMŐu¦¨:"*”ŐߣÔâŰżęí;ź{?Ěě|ýń…“¦ĎŮW˘doţlęÔiw޵xüřK ¤äń}“ü ăZ]}]˘‰1nů‡/?|ËŁŻzłö\}Ů+÷–ťÚöŐÔ©SŤŻrIŇ´Q=v~˙ÎĚé—Ö—#·žÝŞű%R×\ühÎÇ»ŞOÝuű]÷ŢvóŞ˝ĺ3¦ NHě¤TŐ벻˛rDďvEm´ęă Ď×mĚĄb ď\ôŕ=‡*ý˘á±gôâ@™Oőů^:OĚÝ8aâ5/<űčÔ©Sľßq˛mrj˘ďŔůňL™2yĺîÓ»Eó×\Ľ• ö0ĹçîÔ˙Ň!]’šŇîöšµŮďí4ŕě<.źjÔUkv¶¬ďéš—Ň!ëČů1\Z5ÚđKfĚýzŐ~«ĂâóŞgRVL’Ďç Ö#Ô¨®ë¶Č„¤íŽŻĽőöŰOUmBpÉÓ‹mř§+VhDÖ}Ş!‡”BmuÝŔéóÄĽ Ťüůqw®Í"Ѧvü,›is NuŤyÜľ‚R·­c˙[&yóĺ×7ŻYúĐăď?˛d媥Ź}ůä=˙ü~gÓˇů«/Ŕg®é(ńLµŢŇfčb_ tî˙ˇ®î3/V랯Ń‘ń@í2ˇ»• ˝öĄšş·jTN™§>¶Ől/´48!ŕ ŐJe»cďš7îyôýGŢúTü—Á€XIpŐE[-STGôţSor7˝]ßďS§Mýfó±Ń3ćÖěY×8 ćĽŮI¬ń‚éŠaŮBş¦7•Ţ}.Ćt=P?.+A]cś3ĐTMp$ĽřŃÇÚ±÷ďxüu“3‚ŞÎÇUڰđť«qM­ gT6Ű ­;~Üâ{7sŇ7[҆î üiÁj€˘|×]waNMá­;'ÇsŔv‹ ˘měÁ§ţćł$=˙ň?:ĹXüAŐđĎ~>PJ«««cTŐcZ'µKlĄk*cśX©’p$šSÚ·Ź‹o3¸_ź˛Ó' Ë«˘ÚÔÇ$e Ň·wߤV±TÓŚK¨őĺ$µŇ‚ŃޱUÜÉŚĚřÔŢcG nźŇ!Ě"ŮŃĄľdSűNťť±IJ u ÔÇ,"%hÝĄs+{nNaBŰ.ÉmŁT• ‚Đ·wߤ„XFiĐçµ9ÂŁ#ÍÁ .™LťR»ÄFÇôëÝ­(óHĐ”°čž»ŰFt]ĎÉ:î˘Î}»hşŢ¦C·”xljcénf¤$vě–kWµ†’[ĹꚊ©)ÍŹLî1Ľ_]U ŕ˝P]‹ďą+9ĆQYtr[fíľÉyEUW/¸kd·Vľ@cń±héÇ2:˝dę.–đÄÖ±6ŹB“âbLkt¤%¨Ô—;¨OŹĽiĺ>4wá]cz&z<>MÓTMeŠ™Đ*±UŐ4Ţ”RA˛ŘÂl{ę×5Y úEKÔ~Ýr3Ň=~űÝ÷w‹wtÍ$ŞĆÇÍĽvń-ł@ń`sÔĐ3yîëšŕUă…ć˙E4Ľ-ĺaaaŤTpÎ5%`‹MÖ§ÝîťżĹtqéäQ©SăÚŽÚ÷Ř=5Šĺ‰ç^Ü)ZŐQlLäŔŃă»¶‹¶¶í3´_¬+ŘŃ·[8HÖ®=zEŰM~ż7&ąOď¶Î'r[§tď7¨ß˝ŽÜSŁXž|îĺí»ÄYá·={»Žś1ű˛Éɉ­S:wŘ3iĎ.Łö]»ô 3±»¶UŞá#÷xę"Sztî€M¶ľÝ; Ŕ‘dKíÚĄMR§K'O(Ď+č=dś˙tzęřYť˘m~U‹î ?“öž˝"m’®sł ô2´sR+MkAT8Ó5dJ‰•^{si˙q—FÚŘ'ÇżP-šN¸Ę‘ŚĹ¶˘"Kíý´ĆTd˝Ňiî&ň„#ą‹,Ä  s®s®r®qPÇB„„$ KC¤•HÂ1 ť#»`ľĆćčc˝®ÍMé;ž^˛ö‘—_Ś5EcM[Č9×5E˛Guo×&ýŕá6˝‡Î2¶gĎľ‚çôň}ĺÓG¤žĚ)»ăŃç§hçň`!çRjDşŐ‚ÁŞ ?UxÜš. ¦yí;t±J>ťjś©ŚsĆĂÍć〠Ô3,ĽŤYÖ'•ü'˝ţhGäíŰŮ×8GÇ›-‘&sŞŐT¦Ňd‡Ý„1ŽEůŠääKbÂ÷—ä­¬QnoßNäÔSŞóü~?'s۶íě´†M‚@)=#ůŤ}±ä˝ľă§ÇÉĽxÍÚÜšj–ű†Ů)ç­íŽX‹D’‰Â¸Dp{»#V6ő szÝ•:ĚINć´ř)@ŤTPÎ9p8_›%Ó•IíFÇ„EOźU’ńËłçé ĆĄŚ¸¤0÷řÉ^ÝüčÓĎôlˇ3ÜDű čޱM‡n1öŰž}†6uHI‰qZ‚ŞćŠëÚ1™ë*ă@5—śÜ&ĆjŤj×¶]xXd›É)\ŐLDč;ôĽ˛ú»źËíŹŠŹµšd˛ôî70.LĚ8~˘Ľ˘*2yČ+˙|9%Úě *7HKŰVަ Ž9XĂ"ƌ裩Zűžý;¶˛;šY^i|őRűh‹?čĎÎĘ.«¨4ĘIŽl?ňŹŤćśqUUÂS;E‘´ěŇĹŹ?7ĄŰ qŤ¶čöÉm5>µG—NLQÍ-J8eřżQ`ít–Ł3Y K«r=Oă KÝ$NąĐN"1hÉ]d!Nŕ*G–{Čúi…ú‘íj§)Uŕ:m/d{ť}íWF]!bQ§úŮ&WŃZ%wěŘľ-aęŢýzNĽâę }/† Z˙ąRş~UUޱ řKţˇdˇÍe—ŰţG¬Öł=íÜ=yěČ‚zMé>aÔŔČŘÖFŤ0ô"ľm·q#Ć·é0¸Wۆ9ŔČ®]zÚe˘ź- L§Ô×µc2§şl&˝łfNęŘ>%ÚnâÖ°ľÝŤqŮҵGŻ(‡¨cŇ.95ľuň€ÔÄür_·ÝdÎ$|ëÇą®©` kŞMçX®ëšŐ®ÖUť>ťŔ¶űźyqrßä7–Ľ­iÚó'äp8Nž<cěÉéz}”VĂŤ-0ŞkçÄôia1ĘXVV–®ë&«MDÜëó LD»Eňx} °ŐfÓľ Ş‹˛Ůf3sĘ1ŻŰ­Q†‰ŕpŘu%čőűÂŔ—­VqŻ×‡0ćśKf«Ő,3]g”€WcÄa­/Ůfł©ÍS.P—{ÄlµŰ¬¦€Ďíń‰ 6ÔQ6[-fŚŚĐTÜëŞ ¨ş Év›÷{˝AUGŮNłD4ĘDř=.Ź_s„…™eA§LŔŘďqy*E§Óˇ)Ż7€✉f«EÂnŹŚ[ż˛Ąľ.‚pO]]@ŐEI¶;¬Ŕër1b®ĚÜ8ç©Őëř¤µUóřü~źŠ "Ô—ě p‹Ő!"ęňx9€ÝîÄL čĚfµ6ĄÂŻę’d˛YÍ +Ź?صJ¶Ú$Ő„4x‡äŤN±çV›CÝĺő!„9c‚l±[d ř˝ţ *š¬v›…`¤ý^ŻŹŕ‚d˛YL`äQôű !dD4bçßn9C¦!’ÔĹ|ő‡pľ(śśsA’HvP]Ó(EÉđcČU5!$I’®k(Ş˘$hŞĘ$I˘T×uf”!Š"BSŞę:%ˇY9X’ÄĆŞ)Őuť ’LXKu•2Ô4pŞQ˘××%RÜSąäŐWť˝GVďýňó_ÔM?˙'«ýĂá„ţ<Ú)ÂDŕTŐÎë*—s”¶ç·Č”Ţáá¬ĎŽ!Á-bÁxÉ=Śk™1’¸Ęy`„m¨ţ×Ć'öâÜĎ‘ ó 3#3Ęą؆€ ÄAóivf?0á€Z\r˘Zܧ U•–cp‚dÜáś!„ ʬpËçé;—GKŔŐ"óH’ŘĄś#AĐ<žC×]g !ŔąBő㍞ţeA|T7¶Ü‚ 1ę§Ě Ë*ĆÍ.Ż®WÜ-‚ Q]a\ B^J­őaçą_×)Ŕ©Úę.őŹpčŚ66Ć.Š€#TÝjđĹWÍŁpžé‹ôÝż9;öjcA;ŻĽĘTĚ’ 1."ä×5Íx‰ř4-Ŕ‰U @cÔG™ńn˛‘ Łäó·üŠ˘ZĚC–ýP[U”]Ą:_`©áJgTÓt8KU$I:K› «ŞfĐ(˘qË0‚‘ŞiD’~GV_qˇ©sy]×ĐđÔ»^)Z’Q’«4®‹†ŻŐUME±±dŁřăŁ9,H %sÎTUC-ńđ\Ň(aŚ0č7]6şËµoÜ;łŻ˘3YŔŤMŇ4*’Đ`¶(Őtť ˘Ôxę@řś<\d™ŔďÉŘŮňÓ¤ŹęmřůRZ’-Úđú>h.‡śŃsůs!nPfŚĘgćf I˝5¦ ŔĎ'áp)_~©č |ĆâĆł~Ôn°ĆĐĚö"ăďŤÇ/ĘöĘsŰĚ5a™ÁŮ»“ŤóCăg \4€WŐŇuëZoi˙:pƱ$ýŰVë\-hAwÎ…[°¦–­%é…3ó„†q™Răú*A âj3aľ µáśÂÇ9!‚¦©K—.mq—äßĂ˙SOţ{°„Aý;6PAčFƵc xV  @¨jR`8p‘ŤtĂv”5@‰HL@ÚIĆk*5Xjţ,’Dz#±UŚ(`Őç*(.˙ßú-3ô6,:¶Oď^ľĘÂ}‡Ź·Ř„Ą6‰­d"j Ş°¸†qřÝŻţڱ!VŚýŹů‡B!„B!ü)@ţL˙MŃxŤÂD Ťw÷Ď´@ÓŤűNaAŔÓőFÇ;cŚńŮ_‰"§´ń ˇdÚxoŠ‚fôL9„‹<-h˛qrÎ't…&LOî¸8šÖꌂ+Ľz*÷'_ZĂLÄ·˝ňčc§ykG÷v´E˘Ć–Ő˙Ýö§ -ĎupAŐשÜ!¬Ď‡©4­ňŕâlgďţjGG+Â9˘eľcd»ŠÔ6‹Úµż6Nwé8 ąú'($ÝÖşŐeQ¨6víqŻŹaňź®N1T%8ţęy_żőE‰9<˛(ý—뮿1=Ż7l0¦sŽ9grĚČ ß=őÜÍ“·ś #LŐiCž§/ţX ŤöLĽvŃ÷üł®¸Đ—¶ţĂ«Ż˝«±1zÂTSí©6núÜÉ˝Řě¬ËŢÖeĐŚq×,ZůńkµEÎřŁ?}8}ÎâN7<đţ ‹¶,_řŔy^A2N§ÎvńI)Ô%Ý;J…>1^qęŰJśş¤[ó’úV×ŕŽ2ˇO O+?őmĄhąţléD)5ô_üŢíß ”S޸č˙«€8DĐżá°ĆÝŻ‹Ç˙<`VŚY8€IDATÜŮd\Dű˙›mľö„đ?2vÍqŰ»żâŚ2Ćí˙ukţďáoo{/ !«ő÷AăË—?Âź^˘úŤöVFôK墭gÇVëWýp(#’:wďÚ.¶R·Nčźšľwëš-ű˘’RgMźâÄľµ«VĎ-€¤.˝fĎRqęXŤOĎNŰťUTÓ6µG—¶1ő_íŮşfë>9:ůú+fµq˘µ?~0#bSgÍśęYơÝkţŤŤMJť5Ł>eÍĎż]„"ŽĹčé3'°šÓë¶îˇg}˘”­ÉŻŰWĘ\š}dtę] ›«Ă'%ÚâôýłŇXdř€ĎŰ;˙?öž;>Şbë33÷în˛é˝÷@Bčzď˝ "˘ Š"(vQź˝÷ŢATzď=$!„ôŢ“ÝÔÝÍ–{gľ?îîfS ďůŢ—ó‹ţČÉť™Ó§ĎŮQ*†ą:—ć%Sw×Řo#Ü÷•–fš¨Ń¶9­:_ä3¤$°(p•ŻőL4¸Ä::Gŕ¤Ůńu‚ýý=ťűÉ52»{´ěÇgźđčËîKxř­_޶ 0° Q:őV:…Z(wę!k(%νäÁöNa­0ˇ„Č»óÚJNćLŚő&DőÂć§˙™lÎPĎ€Šô@ĎMbE·Ż- &Ţ´śEkrěN–B˙q©6ÇĐNđţ魯3ôtaţ lľ×÷Oˇ§H…=˙e|ěíŠZ˙=ó©ĺżYHłšŰ& }F}·íËęÜ+ŐĺŁ+ďť?}ęÉÔâ>Ł&˙ňńżĘ 35znŃŚţ§sźűuçţe]™Éý‘ŐË&Ť‘/‹Ţµď sCQ YŢ7Ücőť“2‹ăúŚšüóGŻ[KťąűŮŻú=Z^“],_ĽtÖäŁY仆˪ňků»ćŤ=qjFňÍö?"ĺŞüZţ®ąŁOžť]ßČ:ľ„0fTTD ţň·ëNýpčŘ´Q‚6Ť1đěf¸V]_Mý=äŤY ş ĘŞµŤu˘c°R§ŕôą †:ŰCšŮs+]łő;+ őxĚt @eęťý T&cťhďg'0m0銌®ýĽĹĘFęŇë-ߊĄZTT—°¤Ću–oŹ^^·öv*ŠLć8|ÎějE¤©A}5-mč´Ą_>>¬GPżBĽôĺ·.~ďHéĘ»'‹&ăđyËĆĂ‹‡Ók#věنŐ9mę˘s’oŚRH‹;ťO@Yy®Vg˝č!ëÂŚŘ»=˛éĺq)?~űe±Zť~éLzűę±e3OĽ•: wč7O<úäű‡<ÜśŚ`Ňk`LĐ·›Ö=ůţ!wĄÖ Ýľëp`Í5ęĸT]f®9“Eë1ʸ'ďAöeo€IÝhăíŢWÉ]íđÄÓN^A\MzIaÜŐß~úŢ)Sz~—ě3*Ü鉧Ř}0ýĵ˘ř}om]9iOŞćŻy|Bź­k_NTµˇ‹ÎJľ-Ŕ„çÉH˝Ţ|ëőĆěc»]Ńüp™y,ęŻ&Ĺ•çV ^´ęîĹsĆŚ“ś_Łŕ¨žz˝ůÖk¦‚łżďÜsĽőýűý˝HŞĂSÓyńŰOޫ՛łRTůGçĘ{ŞrO_tůńŕ·"¬]L?”řXăP÷ŔĹ.ÔŔâ°Imhlç095R€.L¦ Ó…éÂtaş0].ĚíÄ,ÖĘV3z›g5śŚoP—&_*€älUT¸;ήŞ4í`r`T_[Łŕܫ˲ Ôb~|jľ«“ŹżwHqF\)Nͨ¬Ńđ<r™˘˛$í`rh¨­vóŹÔ¨UĂď\9U!Sg]Í®®+.|ňšUĎţşä©úß}˛vÓkšĚ3ź}ř3ćă5›^ׂÍ"[ -ąĘĎOÚł ×ę3é}÷IîÄ Ż8^•ű*ś:Fżm(Ń›Ô&Ε«ţ©˘tŞ«„Ô&dϱÚę”uĚ@ŃwęˇŰŁťú9*Ľě]ö±sí!'Ľ{?{ŕę€ÓËh¤|Ď×ĂüĆ9덌Öč36fkMČ.¦źkw'iV Ěo†Ŕ-;ߊ8­+Z:zĐĺz×ďN\xçéŐCî~îĎ ŮwܱőöŇ^Ţ}$D 1ëyWßŕČz•jř˘ötŃ)É·IMF°ă›m3Ł„ńŁ® €1˘ 8ž* ˘Ú¬Ł fť˙ĆoIű¦N–üů˝ÉńŤo¶ÍŠÇŽ\Y.Ď­[ˇl(üëJyěJ±1C„0cÔü»]ř ç>ÎÄĐč"“;ŃŔů>|€Âe3ilă"łsźŃN¨›Sŕ\S˝‚h3ëkŠ!`‚Ł Ą].L¦ Ó…éÂtaş0]ŰŠÁ2b*®Ż8\}kŹ/5L8™˛˘ŞĽxf¤3Ţ›”˙Çë`úOĄ%SĘa÷nř¸˛ŕ’€Ľ.Tľ˝ňÎE«Ţ­Č;ë ŕ9˝¤\őŕ¬0sĺ“×ĚĄ0<°ĺÇ’´îÇˤ™ČŽw¬{ł±±nÉŘ` ·Ŕp?lj0đîá/żóÉłkqć)C‹oĚ3€>ż čł)@z#l‡ŃŽCŽ šî oČÝqč‰X˙™®î ‚ýÜ«˙w˝˙ŐoıQűŢ>lWŚô¬kźżÄ,ór›ě7ňH_%¨Ďö~Ń+Ľ%*\gůŰÓŰ^mŇxŁ@8&/__ś{%ĆŔs?śÍ;ű ľăá‚Ҳ삒wą[úŘ>zv^qţ˘ážŇŻ<Ó)]p7řŞtbrÓŰß”ć'÷ŹËł!C¦˝üă»Oń‚1Î{HNUݦ§Ŕ–÷ľ/ÍOXć¸xŮ}!®dĘĘMçţútČŕŮ…ů™ťd ˝o+M ]Ak"‡íďŰăŮ!»űĹ<ä;Ő+xeذýÚĆěę×óŮ`źŃN„°ůGšgZíÂtaş0].L¦ Ó…éÂÜvĚmó¬fâ’*Ť?úĂŰź|g2¨î™Ü¬{QU’¤ä@şŁîŰcjaŤi×÷ľűÍTS1­›«KÄ„âjáüÎm»Ď&*î›ŰĽ˙3+/ţńĂڇםIH~č®Q0ú||Âóë|őóm •ąŁýyÔmĘąKń¦ľţ9€—{Ťř­8Ťó}yhĚs!["Ç&ăJűA‡ řWxÄć± ±>Cí lcxěöľŁĎĹü*:`’ Ŕß{MšŐĚ\µ‘2v`űŻűŹ_0 ú×]ś2äĎ 9†Ę´‘ˇ®’ô‘"č\zEęĹ#ď˝ňblłkřś*C{şč¤ä[ŃC`ţĘWcé—Ďýôă¶s'/š6ÎYÍË;ý“’X¶îý”KÇź{âą“)…ue©ˇ®˛éË^°-µpr¬Tçč;ĘJOţü»mW/”€ĺĘ#F`7ŇgČď=Ý'ů ÝÓÇÎľ…d ·Řqş  ş  ş  ş  şŕ&ŕv˝7‡0fŚúvŹť14âôŮô¨nA<˝ţËÝq ´$7;.>Iz÷±ľ2óŘĹÔˇŁÇ8›_µ3.W_ť·˙póÉČ1ÁÔXsńBşČ™śKżv5+#ůč‰Ë±ÚÔEg%oNĘeˇ€1PČ ňR/_ÍnĐÔ•––]Nş\P\ÁDZQ^yđĐË©Ů"ĹžáÝô‹©L>ąá‘Ç“ *ť\ósS._ÍnĐÔ—––^ľ|ą ¤JĆËrS’J5Řß‘m}ů•kĄ*Bx)CF1°óéłtŤĺ&Cnc]şžQP¸ăĆLŤ®Ě`ČŐÖ¦é;ąő›Ú4"A+.Ć˙Ś9ŐŰ„ąrÍ]Ҹ-ôXýŞ} ˙­ňé=˙4űůĎrúOł±˙F o_·Š÷š oŽž˙,ťňÁ˙?ńçź&ů[Ú§ ü;Ö—Ą˝š~s×T—çöR0źöiŁe[BěśbľýmÇş‡î˙𛝕9‰1>´Ţ1é‚Ű ’.ÜBş=÷é7 őyK§ő‚?EÖ]Đ]Đ]Đ]Đ]đo€ŰőZ€tíľ<íę7?ýZ‡y™ť\†™^/GŢĂËěQa*r3~DHĆĆHÁĐXyéRRHpDCyĘâůO¦– =VpśB*ĹĚ÷ÎÁ€‰”q„P&RŠ8‚) ‚‘(Š€0ÁÍ0Ě<‡jăÖ:@fz~ ÔÄń7óGĘŚŔ¤Ű #Yhä=Ż`L© ‘1@X¤€°hŇç¦˙@©©…Ýď!DÎíô\1`B°Y<5őýĆö \řůǧ/ ŽŽÍ˛b‚1˘@#t¬‹ëJ^ y?Rj´á‚Ź^&ăíĚĽ# ˘(ŠaĚq„Q*Š"Ŕ``"ĄÄ@ELšqAEQ¤’!adÁX··†°(C^öŹ” @ŔD†1µ„p Fi“"nÍ3 ·Ú—ęMŘĆż©ć.iÜ>N[-…đo—OÇôüÓěç?Ëé?ÍĆţ)Ľ}|Á-âýź&Ă›Łç?ËEç}°5ü°Ő‚äoUźBECNĆŹ„9Ç &ÁŘ”uó¶Íj(€Ę¬łŹ?~rĚ‚ĺ‡ŢxĄ!çě˛y+ýz­s¶w© âLĆşüěm‚É(%3‘r#RSŐGoľŇA$™Ě!ş÷z^ćĚĐúj5Rî ¦M’­ôPjLB[…ĂDÖŁďcíŃc­§ g5!Ć`NÝ{=/wf´ŁRůŮR) €ůNµ•źµŤR“冉¬GźJuÂ&˝ćťwŻĘ~ÝúoąîţoJľ {ĄF.äQ˝í$7ńÂÚőéÉŮF©ŃZ+&ňčŢ·@Ş˙.hOŞß6n_Í]Ҹś¶Çš(„Ł|:CĎ?Í~ţłśţÓlěż‘ÂŰÇ×­ňťš oŽž˙,ťńÁÖđżŕ&ů[%g†1/Şł3˙|ęťĎVÍ·ď«;|Ţe·;_ ÂDppőtőO=“Óhň3U76 ” €1g2ÖKĚ7’"Ž#€ĄĚ WQj¤T¸}§ç0[z:ţ¶zZÖĂţŤĄ:GáŤČĆ `‚Q_y›¶%n7˙íôüÓ¸řo”Ď˙iÜ\ćŃřß"źŽčů§ŮĎ–ÓšŤý7Rxűř‚[ä;˙4Ţ=˙Y.nÄŰ(ý?o«˙ Éßš>E0Ő0}řâłÁżŚîa7#v›g5RâEĆ@[ŻŠKČ0€’`!c„8„Ú$€ ‚´ť„#ó—Ăí›k"`íÓÓęăöéé žH©‘ €(2„w›d»ąřo§çźĆĹŁ|ţ˙Hăfăüűĺó‘Ř­‚ŰÇé?ÍĆţ)Ľť|Ýßů§Éđź9&ąiš;UüßV˙’ż%} ŽçXmyzF®jTOŃśť1^Iţ7&ß®çÖş  ş  ş  ş  ş  şŕ˙`žłěů·‰ţMłƀ޾”ź]Đ]Đ]Đ]Đ]Đ]đ˙X«Ő˙¦YÍß~^tAtAtAtAtAü—ŔżkŻć?ÍgtAtAtAtAtAüŻÂżcVĂŁ‚©kfÓ]Đ]Đ]Đ]Đ]ĐŢ$˛f÷[nďh !˝Ćüţă‡k|Z/8Ńň"HĎ˝_źn&0&Üţ—éšŃ#ĄĐąQzšę‘e°ÉŁŇ©R7×ÖBb· ţi\üÓčů§qńß(ź˙?Ň耞(„öŁńí“Ď?Db· n§˙4űo¤đöń·Čwţi2üo“ÜţG(Ľ}đ_$ů[ѧ DMz{ŕŮ7.­şüb·ýegJ)$ź:đč:hĘŚ"łłó+\©% 'ƲN\şAr…ÇÍeíAc„€1ʨ4»hÂPÚ|ĘÂléA1›ůźą€(Š=r…+0Qâ±)Q#Ć €`,„$ X&6sÁÚ‘ĆuxçyąˇQ~ŁĄ:’Bă¶äÓFiŽ#TĄ’aB°(”jg˘Ř\ňŚA@ ;/…ť«`Ňłëgálw ÚÍíŔ ë˘3ĽßÜ´všŮ! „0«őţýĽŠ7a«·:-ůżK3Ć!Ŕ¨h–ĽYŔĂ7.çÎŘaG|sAŚ1A;Çiłâ3ڰLá!3熎ä#wfL@€:&aĚĆ0F ÁX†MÍ’y2@–łRŠv[‰! RţâŰ ťŽťÎHŢFŞ7źń†¬WŠV[mÓZ-b‹ŐÂ)m×NĄĹmm0Řô•íÄĚż/ůżéDTŚ1ÁLXËQuŤRâË…1á¦c ÂłĂD†‰Ľť¶n"®"Ě&Š7+ç›ä‹đrň7Ć$í°rý4ŰČűŻ—łsR. Ö‡nsľJ 0ýęgéWc™âĘ;„ČĄ BX T4Jß¶YPŃx-ům©T'e`2j0Ś&Âś˝Ňž  ”[`C€¤, z R 2ą1Ń`4IŚXÇŚŻ%˝% Ě`4Zë‘ňňč´Z HéŕÔ z^ĆaŔFŁńş\°v¤Ń^)€1&ÔŐid 9Łť*u}ů0f2 Cků@[­×Ö6Ř)íä$ë´ŤN..B XÔc”!„PóZđΨ(PIΦ+q/kőĚŃŮ©uťť©ącš)k˙ËNëÂüqűĽßܬv†FmíP˛gQš¬÷˝éoÚęm‚’üMÓl~Ń`ĐëőđJ%AŔQÁ¨Ńj"NNÎ@ ”˛Čµ+çÎŰa»ŚbL¨®®)ČvNŽR‡o}۲q•R ‚`HłPíĘÇ–ü6áÔ 0 H†@s9KV‡€1QG±'ćÁĂ)9J ˘`hŞŮ˛^dY8b@m%†ŐS óÝ|nLŚť7VçíéŃ:m˝-#€˝R‰-ş°bZÄ «…ŰĆŚ1ѤŐ5R@ööJž ÖĄlbQ*mĽ@G¶á·Dň6NTo§tşQ'ş®¨©±QŻŐËťť °í›XKÉSQ@#|c±…B„ĆF <'ăĚĂkÄD˝QQęÍ[·uCăI>úşNéČ˸›p˘›“čëµX.»Ń1IGŚĘ mk1Ç`ÔjAäöcŇş9•˘1ĆŔXS„&Š PËřc«ť˙ŠŤ›˙lż|ű˘_ma¦?ľó—ăÍëżí€"„p<ŻP(d<ŹÚý sÇqĆćµ;„1!Âqś´đsĂM€WpŔ„ #†Ś ČŚkŽi›ŕűŹŹ_8.áşőš6}ćčeĽ™6ęA¨olźaCzpć`ôüŐń§ö„Ȥ?v^h-ĄŃsX|ď=€s©NŠ« ů n^îă'ŤŇ? _Ż´mëŕÜýľĺw¸qČZy{Ť¶_+Ź~«WÝď%#ŕąěŢ9×Ů©šŰ˘wB%[uŃöÇŇňOgxż!ř[Ú±±C‰‡&ëĺnń=şżď§7ßôÍJŢę_ťő„ŔÓ×kĆěIc†EY# qp™8mŇÔ±ý9 ’z0/9aęÔÉă˛đÜÎŘa‹âwş˙ˇ•7¬Z0u¶ [¤[ëĹ‚8ą×ňVö ÷¸Öóďxűěű‚C›3Í1vávv<AĘH{™AöśC´cŚC¸ćZVÍçbĺřvÁmŞ»eÜ@sGAąAŤ·¬›ë™n‹Đ&¦Ůç­c‹´Ôm7dřŕ #ű8“6dŐf‹0'4běŘŃĂĺ|‡Qęď §\ĽtţM9QG˘Y@D˙;gŮŰŘ$ÂcBd2ĚLjł9iăĚq_7¶ ‡p'w{Y ‡pGw{Ŕ„»N[ťpĘ>‹ćz*Á˘â› &63Ňţ°eź;çĹs渿Óz§ąמ113ĆąűşJHÎÉŻÇŚ='Śĺdě˝üÄôéĐ·`ß~.xyŘčÉQăĆp-mµµvşŕź˙‘±G3@aĚ#뱄1á[8\›€1o‹GCłÖ0×â‡prsgÜŐĚóg/¤\ËÜńůV7€ĐŃw^şš%a~˙ô%_âHó˛Á°âŐ 8p‘Łď¸|íÜ™3WRŻýţŮ‹®-ëyŮŹ§MŻ}-őZÂĺËßľúŚŁD¶}dbúŤfáHk:1ćmO"DÂ-¤ŃZbđ Ę>…jíşůC€k®×•Ď_Ľę 0tÎňkYyçNťMą–ąíĂ˝ąVňA\›­@ä¤ű˛Ó’ú;Č0Gd¶|a"ą×č#GŹŹ ·‚I3ľ, ‘—e¦Ć÷w”ŔŔ;Ö׫Kú)9Ŕcb.‚0‡0‘ţŃŞfډ¬3ăyY3ľ0ŹqÓÖ%BZ‡Ps Ć„7K˛ď-Z˙ŰÖŰí`ĚcŚźzŐl‡ßýë9 tôü‹Éi’őnűř9­mľĄÚgĺÔÜĆ6o+NIł‚f ¶ö®•%´eóÍ<NŃ–äQ…¤…!ŚąÖ}TÇşŕxL\Ľ¶VgĚĎĘ©®ÓťüíCoepß#‰ąĹĹĺŐµGľ~Ď Ŕaë'»jŞU%e%»>yÍ0Ź;á;<Ç51%Y5â0ć0ć°™0É#‚döÁŻ|üuf™6űôor@$p‚‹4krÁV je«KöÜŇęa"#„“$ mD¤Ö57ĄŽnۢZb0Ć\‹¸ŃVŹ†Ú¤ąEüiÇÚĐNs/nľ}u‹#@‹ľÉA˛LKÍ/€‘s—§ZbËöŹ^ňŔŢQ_ď<‘ś—pĺÄ®źŁü]°5—vëeéíźÚúuĘŐ+—.}˝u“Hn%˛s}Aëh#y„í_y‡…jíşĂŔÜýµ!±VćqÝžQţ3×nÍÉ °ç¬ŠÎ}ř싱KWĹyŚ\ýסČP;0úmyġ6ÚÂDöžÉ¬1k8p2»&Ěě‘ óim !Ô­¬ÎV>’ĺ€-Ů5Łg,ÉwN>ÍZ[]ł8Ź0ĆM=,¶ď˝%»fô¬Á–F›Kă†<0ćä­äÜĚZ™k4ň™Ź¶–4lI)|áŇ_"~=<–úüŐĚç˛KWôč}˙ë/T<ťpí©¸”W‹Ş¦>pLwçË9ĄĎĄĺ­xó9`s‡n«.0ľ~°rZ—j· |s  vB_×-r  p»Ű"ť!â&@RŞ‹ä¤q#şő°pÎMuYYe cTî˛ôľfMˇQ—”V¨@îjĆT—–VÖ@pTŻá±˝]B{-_4×EfĚČ-ňî±tůňńن¸ÚˇĚÜĆ(á=fÍ›ŰÍG™•[ČF›ý0Átz÷ł/Ľ´÷rÝĆ'Ö&î={%/+ţŕ3Ď˝hĆ$ľ’W1P*JĄt8CüéÇŻí˙hëž‹©ŔDSň™˝Ď˝đĘľ¤†§ž|´E=›žZ—qć÷rßaźm}pË‚I»ŇLO=ýpN¡”Ľ*fŞĆ~ýď›7č“/(  Ě†BŚ1Ń;(zé˛ĺăGuµC™ą… Üł™4ZKŚ1ÜŁßý÷Ýŕ×·oŹs‡¶'d”ú„ôX˛t™TOVnŃ Ëçɵůq»óTĺé7mxňČ5ĂŁëď/H>’’_ŮL>1*Ú¶~ţж„ڞŢ'Ťv»tîČ™Ä+Ť"PľÜ•8=;?2zŕ‚ĄwĎť4\_[ăm¨-©ŞŐ„D÷ŰKҲ«ĚžSÜŁ˙ÄŃ1—Î;—tM'PĎĐžS†Ĺ”VhĆĚ„ë+ Ę«ä~3'Ś(ĎLÓ3nöüąBm•WÄÝ+–Ě™h©ą¦´Ş¶AázďňűŰ“ŘŮŰ3Ë0bVľ(e@FĎ\p÷‚9Ń!^ĹůYZ˝č˝dé˛ńCűÔVWÖhÜ"'ŽnµçŇ 5Ł´Gż ŁbÜmyo­ŻćZľqëí„vF”Ұ1sßzvůs 'ďJ3mzúáś {.\ÉĎL8Ľĺů—ÍÖ{ĺH+›of‡nö8#§ŔĹ?rŇřáÝz6q 5iÖ‚ůsFéW§*.W×uÂO ŁrĎfŇ@5÷`L ŽęŮToĚČ-’{†.˝Ż…űŢ˙ŔŠ_·ŔĐUŞj51ý'ŽěŃLňMú*ި®·÷‹8vXpXĎ;ďśm§Qç”U€­.®ë) (0&·“g\<ĽzĹ{®žń±„?ůŹ\ůä˘čáýŁ>˙#ăĺ÷^Í>÷K©ďźl\;¶ď¶«âó/mν´31ł”tč;’çz…D/^˛tü°ľŐ%Y•˘ű‚9“ę‹Ňęô"cËüćNíQźţ#"{/\8[Ń Ę)W!Vd÷N0˛ŹGíGß˙ĹbTh⽢¨˘ş0oŐW˝Ş¤BÝćÖýűőëyţŕŽ„ĚRNć9cÎś&Ţ›q aNá÷¸5fie^¤b{ąQdbůAŚQj?Ě=úqďĚ’+łhřă~šs•F/§g|3W$UfѰG}Óµň(G—HEÝĄ‡1>ޱvŐTzĆyQ^™›\x¤>pu W§W'kÇČV;;2Ţ(¦Ŕx…Ď,/·ŽĽśéŠőÍÝ„Ł ßđIăšiŮ6fjkJJĘŐýM=(0őZ6elÂÜ;#ś’{,\±lî¤aRÜĐW«ę´š' 2j˛ĹćŐ%ĺŞZ;˙I‹˛×Öd—–ŰůGLn˛1UNY%óh7~‘3&Ś(ĎĘĐ?óŽąXS®ć˝fL$•ŇŞsJ+QĆšĹMŁIázŻŤ Ě{J¶šťWĚŮJŁuŘüě#©'¶WůűÔ¦oĘM8ś’[ÎÉÝ'L™ęeźWPĚä YÎĺÓ›źŘxäšaÝă«®ťţ GŚ~fůäĺżôÝ™G7>V_śt19›—{ŚźÜT ăeKڞô#>{uőł §ěN7=ł6'áPJnS´‘Bndô€ŽűgNź™W2jňÜ ćŚlŽ6QÚ$ď‚ěkÁmÉŇ9öý”Y6má’Ţ|jV‘Ü3Ě6nٶ±Ĺ™7fćaŢsÖsÚŽ˝cÁ1Q˘TUÝfNW4ÖV••{vĐgѢ^ăGuµNáŃŤĺ… ďnýďYÜsěPˇ®Ö1,ÚX•k˛÷ď1v¤Gt˙>Ó&kËëU5ńî=gÎöôRVIm!Dáß u­řă©×L€ ć^ąmă«N‘ýű.ş«×řáB]­cX”P[¦­Ó¸uëŇŻ—]pĎAsg+xCUAiظŮ}fÍ ŘWŻ.i¨®“ě'|ĘýfÍôö®ÉM3®Í*8ükqNyôśĹľ~|ynç:đîĺ1c†jJ몪Q×ČĐţÖšŤUĹ`‘‰4bń6#ŔO¦*©źA“C"+ňJ•Ń-‰ŚíÝXU¤©­gŚşvď3řž»=ü|ý{EĺűŁ8»Ě!(jŔťŇ7ĹšZMłČ ĺ|ÂşŹćÓwÎL^«V•W2F9÷‹ď‹3ĚX[VW©ňě> Ď˘;{Žf‘F‰¦Ne13f{z)UEE”R×!óż¶ńŘú©ß­y<óRREIiď%[FNŹřp\ż‹{ł§Ľü˘6ĺ`ÂďŰÓď;˙ĺű5b̨ÁG6>$öY´äőGţš5(éš0㥍Uq»KrJ JŮđŤo†şVţńÔk&ŕÂĆÍ0Ëąş´A]Ëű†w9ÔLł®FUVŃ cáBŘ}ŕ˘{#c{ëUĹ 5őśOxŹ1òÓMŚ‹™94ĺ:ΫÇŘ®ÁMĄši0Č«¶0Ű 7q!ďľĎŞŻ˝gŰ?­áşEnáOgŕßŮÖ-L8č5mEŤĺ¤%_JĘ®*Hćn‘{/^K‹?˝çđéâěäqŃŢŕaĹe]žÜ/f­|B+ĐśÜôäÔě” Ű=Cvťż–}ĺôá“’ĎďrRr ě1łJdŮÇż—´µŤ€đ®Ý"٦­ÜwńĚÔţľ “»7aúůÁ¶«Ô”}ć”Ý5ÔĆדvXÓôĄ[OmwńűâŻS™/®X U8ôÎ5eEř$š§ŕގ{/^ËJ>}řä…ä‹{8ŻčÝRŰ“OIΕŃÝ=ěcŻćWĺ%Ç'Ąeéë*–NŠäs0>Ă\ĎM˧źp=§$^IąkLX®~‚t»Ŕ3rxRnĄąőÚňÓűŔňőd¦ĹőuPv łđu1ĺŇq÷¦äkéůąąçΞŤ?wdŢč0ýŕł–S˛ŇvŔÝkŢË,,.L‹ëçd˝'/-­iĚNż—”]W’14@:éuE^9U\§]{×äáÓפddškľplÖ /pŠŘ׾ÄjË—MíÍw·$xěµďµő5Ç.Ş({|ů° <“^Tšť”^\’y)Ň "',«3Zě9?eL´,[÷ľ™wGČüzě:ߤÁń=|€÷?p)ýöi!€‡`ŃŞ7.ťŘÖd‡÷/^áٞͷ¶Ă«qűx€SpŞ*Ľ6$XIÂ&]Í.<´sűŃ3Éy×. đ€Y+7´ë§­ěą(ëň„ŢAVź’€p̸˙qs=×˛ŻžŰćőçŮ”tI†9WĆt÷’ôĽ’_‘5ţňµlf¨[5»3«sTďs<%ßV_á–ŐYöµ¤ÄôüÚÂÔa K»A]4EďţsUµµób»-ŢřSćą Č®TTżqß‚E÷˝U–sÚÍŻűźç˛DQ÷Áşe€›vBZúގ®ňŢ Ńŕz:­°4;érfi~ňáŘłsKUNë)• q^ę†5Ô ąéɉéůµW‡‡8I}ü“céÇż—ŕlx/ĘëáŤřđ)W˛ $}¤_ęéÇ;ľbă˧§žł:ćń\gů?ĐG)GŰ4˙_ÚĚđ]>üŻś§˘×wýĆ%ťîî='xřžŢSŃëű~c.Ĺv_ŇűžC˙ěőlXďĎc†>ÇŐy´ç“¦ş{Ďńz ŕdgs‹>ŹĹ†Ítä¤čócß!Űbú~ŐsĐ÷QDŢÔ´µ—‰™¶˘ZoŁe¸włĆĚŇÜ+ĂBÜŁÇ>"0ÓŞ1Ý"‡Ż01áá‰}'Í^w%=Ó‘flć€cô´¤Ě|ł 3âŁ< lÂR«EŐ§ p‡Đ ËęŚÍ[÷č~*µŔÖ#¦<¨.7G­˘ÚĆÇîé5dA`)U22Ô žř×6ńg"(ö]JłúÎÄ>aŕÓ®ľZG€â’ěÇçM›łhKü™ÖđŇ‹Ŕ>jrRAő™mďZĎGK,{ôś’”’6/ÖĎ{ŔÔřřKw Šé;ďBÜĹ…“ű€C÷I6ĄšőŚô#¶ÎY˛9)5­˝ľŕʵśäÓ?ůőťšžx×ďGĎ$ç_»Ř?Ě6XĺS^öř}ăBSs‹–NZşů}f¨xb/p Ů}1­Ý±DJfjÂ^;Y÷*Ú¶ áŔgě=ĎĺÔöjeăćËW7śO|ď·N˝&ŻżůŕOż®Úwééó§ýť`Ěó_żZP±úŹ˝Ď¦Ś^< äÉÄśŘŃÝ>ööŰeŞácz‚KđŠĂÉŹ[ńDZgăăş÷ł­ůńs©OŮ!—K!L aŰ‹—Ű!pZy®ä­+AéýĐ…Üç.=v1÷ٸ3ŢŢD8hCRÉÓ§Î?~áÚëEGGcčš ŮŇ7[.śň t„‘´V"ÉŮ{²—Ę ›Î_zěbÖ+W’B|eŕy˙á¤'Ź[ńDZç.Çxw›˝Ţ*ůu÷÷l+U´ćݧăÎMÚü²ď~é7q ŚxzűĆ}_cŕCK)]¸ö^ <€é_źzrçÇ0híĎŶ÷é¶ü`ę›Şšą«–ŁŠ¤ť»¦€]ŹéŹ_Ę|đ§_Ví»ôLÜy/7pµä•rŁ™ć”ä7p˝ä•r W“B|eŕŢíáóYfů\:çáăď1=ĂOŔuß’W7záp‡wl­4IĄ^ľ’ěh«Á-écîň€Ž^yҢŻČAĐŢař.°Âí}-@4‰T«~aő¬ďO4&§ß1qśC]÷I^^ýr˘ Ţüâ«UwÎtČv›8 ŕ•‡_N¬‚×?ů|Ăó>ôzŁ ő•Ěř3!×ÇĎ•x Ü+ř»Íëź|˙»‹R§jT‰q©şĚ\ény‹›BcFĹ!c>Úş%2Ä3ĺÄ_'’Ę tĐ( ćĎă—ËíkR!ŕ*Ç´®¶FŞ(í ž´3;Ďĺ×/ś"SŐÖ/ôqßĆô3Wôr™Y°uµŤ„Wř‡„˛˝uŹ0°÷ëŰ3řŰMëź|˙°—‡ŁF€ů –Męç»uí+mÉ˝ůĹ—«Íő) ń—•ĹôH/J>ô9Á·glĎ /ź|äďČçřĺr <€ű–g7ňĎ9™–‡ěc$R2÷Ţ@®¸gźd𢤟H3ćoŢY{>ăĘľw×0 ¶|rwQr„Űóó«Çâ®^>řÎÖ•÷¤ęd<:˝ŃdÖrv@X¨BÎýüᣉą)űŢ]ä÷ĺŰcýć5łż=R!/űĹ3·žŞÖjE¸®VŁp”ťýĺĂa9E—öľąuĺ¤=©Zżćń }|¶®}9QՆĒ|B€6ˇTTŤ_·jţűŹÎßôÍ>÷hP—ĚĽç©>^Ć!Q}S Ýłňď›7őÄZ¦Ż•ě9.?uÉś©'ŇľůöÝG.d^Ý÷î„ĚącŮäţf ţ볯[6óÄ[©z‡~óÄŁ·I;Ö§r$»Ş©o˛C…\áCFřŇć¶mľĄňtw2IĎxiĚž{)˙ÚýwÝqáµď—._äá¨t‰zóÍŤ“Ć NČŢkSŰ~Ú†=żţÉçOÜżŕČ#oQ„ˇą &“©ľrĂ‚™&ä8ş¸ĚZöÔ”~Ż=şU˛ůU‹f{—†đ•˝{ ¤ć_Ţ˙™Ń(ÚZť$ůŮs–đ†F7éëĂ˵ĐX»eĹÔ_’|róŹőívî˘Ď >!_>±®óşLx™ŚÔëÍ·^oĚ9±˙Rć„!ś6ĽŻ>;ÂËu—^ŔöHUS·vË ş+G˛fˇµµď¤ů\×Đ8vö}ý}ĹAÝú¦Ł23ĎĎäv>#' ´˙¬űďĹ'$0żęŠ´ă9µOkÖß7őçËŢ9ą‡ éq6˙XîKÄŮs–YyĎČąt˙˘…Ź˝÷۲űî ôptp‰z÷ÝMĂbzß,+Ť˛x bŤşcŢ2Ŕjů §í—L bŕĘ@ą^[“F± 1¦z1`e \Ż«Iă1ŹG«Î7xÎđ3&–×`"Ç@’s~Ký‰›Ś'BCˇôśxßáÍ×ęJ÷×vłsŚ•˝—źóK-ďH¨©UÓ L«’´ś_x,2&Lá}ç”ţ/>ô˛du/lX>qÍ›ŹĽÖű©Ďv>ňď_ßđŃá$€¤!©ŮÖ$Íg¬/„IďÜ4fť]ştYŹ‹KÔŰďlž4nđoyÔX#µ•WpĽ˙î»U:ĐŐ4µŢ#Ś÷Z4ĐźĆv3ŰáňąS>M©Ö56ŠXŁŃ‰”jj¬–Ů»oŕéj§ő«Ľ˙ČňƤľŢ[×Z$Ör,‘nŔµę„‹Ů<Ě#ýîGg&^őŮ|y_Ä„§wĽůÁŐôőż˝~tý´ké:L8šöÎ{)Ź˙ţ†„ď1XÔTÚpGü™ĆuII±Ó'çf~Cj‹.›rň,ĎĘčwďýöęÔóżD)X0ç~=@®üĽ5í|’µfÂó`2¨¦j÷ň9W“rśőĺőż>|ż»Ź‹Ě©ű¬—źŚÜŁD-Ś^1˙ĚĆ…{>`ÔÝŽ©AćŮŘ Ľá×  挽|1ĄĎĘW»÷ô>ňÔ«ĹjńÖÇc–ĚËxâM“ 6Ő"ŇŰŠŇűG@ĽňÇďů´{_·4ă@ôă7żDÎÚčm|/6¶ÜŘmsň…>Ł'ËFşČĘţ5j(°đ‰??6itÓ–ů{‹Ň7O%ś4}ÚÎOC3ÁćÍFQ­ęŔ#3/§řáŮ_Ňζ`Ě<óËC÷»y»ČśşĎ~ec÷Q±— 4 Ż–h~:ů`ŔŔn©j4VŰrA<úĐwccËŤÝ6%]4cŇą´Sc#Ŕ­VD)Ó©ĄR›“řő Ě«q}ßü3O-ÜűóehÔ”ö\ôxdŚ÷‘Ť[%iŚ˝w^ÖSďRDnňéę˙'p{g5śŚoP—&_*€äĚŠ¨ş°šJŐđE+§ÚÉŞł®¤Ti‚ŁFÔUV żsĺT…Lť“š^R2^QYšv09U–©iééĎż>ĽňŮ_—U%™j1ďRZ‘‡ÜÁŽgőŐeW${ÎVE†ąŰňÎÔ¬u…Lťu5»ş^¬¸đɇVÝ&í0ŕ8“ć Ô¨÷ |Ż{©d‡2sqĎś©Çú¶eó­íđĐź/ßř –É5ŐfĎ˝’[äŻ ë÷ѧßúÉ5YyĄHlÔS 2ąťE-ý´Ą=+dęśÔ´Ň2Ôüń_ ,–…j«Ýü#5j•­ÍôčVzíB)Jͨ¬Ńđ–Ő_łµđśnŞŇ,[})8V_Sq%± D§FÉÁ+ĘöŐ‘ňé1(#ŘżńͶ™QÂř15ěŮö姇}żëHq~VMu–ŮQÎ/jÔCîy÷.ĽsĹÇ;¤ŮáŢÖľS¤Ş#Dá¤pS[hNĚ*±kDgOä.;¬ŕčîx€]ŞĽ(ţw˝ÎĐXŻJN(ŃQoĺŽć: PpR¸[yŹĎ(qFĽ·ď€O>˙Ö‡Ódĺ—!“VÇ)˝Zx.A`(ďw›? ËÄ™CćÖÍAÍDe!Nţn†kOfům‰ÂA#•‡:ů;ëŻ=™ĺ»1 ;Ň\ßé¤9Ů ŁDc¦1¦­¸Ú ĺ˘żě~Żg¦"p±śĺVůu®^D@Ěo(ýłÎďÁHźűÄęÝĺ™ď—Š­hĂ<Ż©©HN,ŃÉ`0rnˇŢaőę*«ŐĄ—c€Ď^yíž…—zńŮ ^ů€±‰HÍÖścL©Ô#öłĎżđQčłóK‘¨×‹ŔÉdšjs[z“ wpÇ5ŘÚşŢd’9ř8É]líĐSáČK“$BŔ-Í‚dÎŢ}‚¬ńGWŢhdA‘1uUM\¤•” }ÉąIęË&<ĽřőŻe„cşFďđÁ÷:™c0T%Ü3{BS)QÁNŠ-‹m©?oI˙ ن–çSŹßuń“Ţúů QťÔTŠ ¶1Ş~°Ť·üĄÝv_€Kň ĂzOřü łä1Ó74 >ýĂymů‰SÇ@S”U#2Ą7ĺíűôŞ)Kͬ,?ŰŘŰĆXŠssT\Çć1Çë«ËK’Ë:™L"çŕMýi8dÁpćyCuYib ”ć©<‚]ŔTvńóÉý-Q¨HÁµű¨»g&żűpŤçE“©9†P“hSłąŹ#2ECYzFJ®+(rď1nÁ»ź8)ôŞÂ2D úFѱw(ŃUdź;ĆŇěFA$öŢ”·÷ď5ÄP‘ZUU ÎújUčĽQ ™.ďZey9N®1× uŔtmĄ"E É óµÝÇĎâ }MĄÉY 'řjJ˛ŞkÄ‚˘ô;G/gĎ †śD KK©S×bb'—»éĘł«kÄüÂĚbĄÜ„ÌQi …9™ˇ¶˘4ą¨“`0b×Ç€P}µş… #°HC˛&ˇÂ,UĚaJdö¦ĘŚßď_Qnä=ú_ë5bâ/ďx~öř»>P[­S©/mňBôśyĽ>/ńŕ1`zŤSäôažĹżÜ·xđ›;¤‘ ňڱę\ş ˙ŽYÎ 6šDDxŢJłÉ(rJ7TC u•ډ(}ä2­•÷Ś"ĄÜ‘HŁ(lyĂsMőE2'‡ Ň(i‹3L&ć­WW5I٬ŔĽĚÔŔí}:@©ŇŃ-ČĎ€ůy)U%µµUŤ˘&ď®Ic† 6fě¨?úµQĎë˛ďš4fȰacGŹ|âímŔŁI”ěÂŐnydNP@Đšm_´nóăBÂ3ňĺw>yví" Í‡*á@µšę3‡÷ŞŚ$,8€R3Ć@ÂBš?OFTeĺ™Ň?˘§ô׎ë©2rÝB*ŞËÜ<Ľţřüµ“BŹ oUCŤT_÷H$Öf¨ ĐbuÚü–´•Żß=öĚŚŢî•ĹŐ˘¶ mů 6f쨗>Ú¦­Ö;x âĺk/ăLžYű7ĺĂ…‡†Ŕ#/Ľ2góÂ;_Í­´ťŇ` Ťĺ•Jź`,µ.çËŇ‹Ö`Ň :“ps}Í„#r™Léd–efF©@­±7«G0€h2ńöNa.ÔÇC^­2i$“Ë8ąŁŚ é Q„±\&S:ÚK5čëtĆúĽv%fCłE *ŠÔśťS@d´T"Ň;8y:!äęď! Q©ŇÁ5P˛g»*•Á–f­(6k}ذ1ŁGlxk}vÍĚŔ[­Âq”˛Ŕ~cܱ÷ť-«8Ć ˛AĺćîiµĂŞ5 QÔ´kó-ěpű˘ő›ç tŞ­Ó9:ąKžŕĄČËÓŹ™±2dĹDDOZş©^0żčdŁÁ~ÚĘž%ď~k=5SµŚ@_ݵŃŕč—>ÚV«jpéć  ô rsPŠ–ýC‹Ő™@Ä'«ľLH¤”‰˘ ݎ˘ R ި§ ÄŔ¦·?Z<ÖuܸI ĄcVťűč‚ń>AÓxĚČřBUfŽFí¦tüáÍMSŚ}Âü*ęU€¬#şVľŁ”ó˘h±ÁÁÉÝ  çőIńGĂĆŢé#ć]Ó;ß=3ęđ™ËőFŚf.DëŢ)Ó)2˙fË{D€{­Ć4yŢ8łGdô¤Ą›ę)Ď™ ş˛ćž+RŕÝĂ;¶Cs["Ł&jv[E/׾ßôŠ~ĚO*`Ôx7ľüűÂęlć 3éŚz­Ŕ»ňĺßVç0‡™Qc˘€ŔhJyŕjţ_ŐÄžPcL Td&}…Ić+ĎÉŢ d(> ó@Č}=ăܤÄĚoŐŢ‹ý=cĺ-nă0&˘t “2FőZC˝ÎXźoµşŤďüFxňqZU˘÷\·yµ´Ř‰0j‘lB€)óŔY1‘›ÇŔ(š,ŠRF1 †P‘Q&¨™ H4bÄó2›¨e[ ‹‚0Ş,VsöÎRüaDŤuZk$;zä“oogĽ[H»úb*Ş,Ŕä[Ł­(ŃV»»y4őMšZą?ńÜÖuËfq3}řŮ—¤Ř’śU>á&Mńţ3 IgŹĺÔ{q ‘JIŹ˘Ůƨ6űÁކjŔ­× ¤ÝV_ -N5“Ľ ËxRQ¤˛Čó@AI V.ůłČq׏ď!€şť©!żý±`L®góŇĄÔ¬&Š–§Ď'—ńŽJ+ÍaN.ăí%sb”ňJW_ćäf§©1ç6ő•&>°[ގöľăAwTpöŹ?m‘Öłlj¶ě%QJEČ,źî3W¸ŕě ěóŮC[ôćx¬)Qc;gçđ`I>Ą '†]ë—§”8­řě-h¬Ő‰š‚ćM|ňčŹgŽÝýŃď˘IÍ á¤#ťn&ŻÜwî®—#Ŕ€pƤm;CçŻrçÄký H&ČÝ9ąůy2Ag¬¨’{!ěé#“ËDŃȰÁňŤł»Ź;ăXłšĄ.€QJ›<—éµB˝NlČo˘đăß%)™YćłŮn©2 :}5Ąé)  îĎĺSžďöĺş'(ju!ď1`ŢĚÂ};*ŐÔŐU*=Ľ?|&#Íčě«ŐŞ ĎĽ•nf] r6ĆɢŮ6¨ů¦´ †QjbČ sň0óîëA‘(Dxąś;pQ±y)AŚ´ĄV 2ŕä`ŞÓŠ -¤Ńş÷ě‚p›DŤŕä÷ܧľýÉ7ă»;:`çîß]GěŰöĂÚ‡×=·úîŃ?}őˇÁk¬„9~îÂę%S@éäěĺá0ĄÔˇçôÓ/mZu÷ŔA¦şĘŇbđľ˝ÖŻ[őŕÝsĄŰú„ńĽĺĎ=Ľű‰uŹ}űëĎ˝ÝMźşxËŃ;%LŚ«ńÂĹó´işÁĂŚeg&—Ěź4D‘ŢŃa=ŃN†+W.ÇťHÍ-ë7xÎ{ďżďa(ţăđI‰‰w̸zhw‰ŰŽS:ÄlĂW ˇ¦Ş®şáĐî5®ŁÚ“ϱłqŢ9ň·ť?ŮwźřÍgď}ńÉ«‘ľĚ¤ŕqgâţ®|8<ęŽő/=ů€¦N÷Ŕ#Ďţµýçi#{Í-Ę:ąS>ÎÚ:ĆŔmýĆg7Ż^úĚkď<|Ď—ž“N]Śß´j±DOIA=črË4v[ŢţĺÝ—žĺvŽ-#J)ć|ÖnxĆ\Ď«o­\4AÎ1Oß çßýŕ­ŹżžÜĂ鯓GóRKD瀧?ýňłO^ŹđóäŤy—Ąšß{ĺĹŘ0çßůRď1¦‰‘ćëčŇ»éŞÄCżž/xű›˝›Y˙ëźÇ×Ü;nŰŹ? ®ý?ýúĂwżţt°§¸sß¦ŕ‘ŹdĎ“˘wÚŕ±~ă‰ćÍ/żµzÉÔ;ľĐ¸ŤŢżí‡µŻ;qáŇó‡AŔčóń ·\;ÓGőO˙Ńc' ě%MšOźÜźš_.١·Xúí¶3ď}ţČÁżÚłůVvdŞ«(-ŇpX´zîÄ(ÇÝ'öçU–ąűÇl޸áŰź>‹ôőâe<(ěÚńÓ6ěůřą «—LŽkůH‰ÜRŹtpߎď´nV/¸¸ęîQż~÷ő±oç¶źľ}ÉSIŤ&ŔÝju›_~{ő’)Ç÷îhtě÷é7’ľč_{bwww@ĽĽ}ĺČ~ŁÎާĆ`ţĘW¶®_V_Ł}üé·Ďź::olŔQ{~~Ăšź~ÜKâ÷ŽżtîPR‘zȨ»~řńgo}Á÷»‚ĺ S›ľęëíäÄŮ»KďÔW˛±AâŁG/¦ĺąąądÝ˝÷Đ57Ďkqçx'7/w€xzy+ídRť)‰'bgďÜ˝cő˘‡vü¬w`®Ç“î=x°¨¶ĚÍ·çÓ7|űÓ§ˇžÎNäŕ‰ż6žËq2ůönŹwłw úDxÄoŢMŢăăč€)®Ŕ»Ë܆»şô°—¬®ú\µ¦ĐčÔߣÇÖYŁľęb}}RmCľÁ©żGŹ­‘2ˇâdť"@yłżq.g‡‡dĘ—"ű|Ń3xОň €.rWí,ÓŐâ0 ®ýčĽŔĂ)LN5‚ˇ\ 5ŁČíĽ˝<¤{ îŢ>ž˛=ŰżŐąŤŢ·í‡µŻ?y1îŢŮ#b¦¬˙dËŞ7WĎš}˙«ËźzďĺĄă@_Ü""YϬKŁĽâ˘lW˙ŢOoÜđíOź…{ąqGdvŢ^ž’.ĽĽ<í<â öđđňňÚłKďlŃ©»°ďČɢśÁ1PŠZaŢ®iVʧ‡§›»Ë=Üţ:ľćŢń?~őAłžńŢ©`ŘŻ=}I+P§l"€{cŃłçăOĄ4ë›NçŮ}Őú§–.ÎƦ߳ń卫¤Ř˛ëŹ_Ç÷Ź8—tÎ5lÄ;Ż?żĺµ·ÇFzśżz ä^Q«×?µtÁtfŰ3¶Ý:eë橪5b·î ¤ĐLňŢîJgW[ůlŰylÍň‰´±ŃĎ/Ô§=~ßRYßĹŰßy|˙ĎiÝͱ·­±„Y­Ú<,·wđđŔŔ‡—LÁ€©0ą^c7éĄďçl~:(„" óĂÜgž Pu:ěě?ů­wg˝ůE·n)'Źç3văš‹ćb& €ýGŢ7?ĺç+Ş^4‰€ýl0D’•± IŞyÎć§#}€W:8¸»3˧®$ÇηׄG[ôŮGˇ>2GWVp4éRÁě˙˙ŕ#÷ţtpÄÝ㙾ŃÉ;7¤ď\ł‚ôYştëşôß?3şŽzŕ›oFÜżöˇ§‡ß9dŽNRÍ`q-…{@Ô„ˇ}{"KF©ôC{8ߡá!v)€¬»Ç>ó?xgÎş±ĚS‡Ó“ŽqÁŁ˝ýÖÂ7_óS8r™‡öZż ň )‡Ź€ť«owKÍÍ匕nŢŽ˛k»ľ7şŤ|ŕ›Ż% ‡-bÉ•&itóη·$UŇüY Aw~öŮĚ7ľęî‘™xp÷żěžĽfő=źţ†+._٧.îŃÇ;ńŻ_$5gűł$Ż,hŘťwöťŁ±(n×nźËćIş 2ľĄś#|0G°ĚÎŃĆ6xŹř&ŚŇÍËŃCžqpwďnBúÉSµů%ÔŃÂ[źÎsk`wLšŐŁt÷°wsłŐŕ=?~çŘÄ?<ą€QQáčĐ˝GŁúÚŻ<ŕb´ĘpŚÇÍŻ1=ýüS—÷ýSˇ1ŤvŽí×óܡř0Ôž=ź3b¤ ®|yÓ–řś ±řťÇć>őĐŞŚŠzŇVn\[ľžzíÄ}MţˇcíĘ'-ţř_{Ž\K߾Ķź»p±ĽZ¶Ł#„ŚGöď3Č\&LU—˙ëöÝĹ©Ç.¦ 5.ŔAxú‰‡w^Ę î9túŕÉž?|fý×ű‰Ěuč¨aÍF;¦©Ţ»ϡc—cc‡č™–pâ÷?öUVÔŢZíhµőĺeWS®””W3ŃT^sň䱫€0Ő¨Î]¸ŇsÄH\ůҦ-W‹ŐT0)ĚÖŰžÍŰÚá‹Ď®?pą$8f•Ó÷6ŻűńčŐ‚´Ôj¦ś:ulć‰?lßuéb\Q™JlŇŕőí9=áäö{+ŐőĐâŘcT0ĺdĆĹ'SĆ0Fő•9‡ŽĹ[˝ŕŻ]GŠň®<š€ÝěrΦFěđ§řĚÚQĆšŞr,’WďŰżëŘĹÔacĆů+…ÍVďŽ/ UeźŹ‹×RA§Qť;}ş¸¨Jéí3|ĚŘNę02™]~ÎŐËW3ëjKJŠ“ KŠ˝"˘ÇŚ_|qĎę‡ϮֶęřÉř~&şă˛ «>—^Š0¶žµ“–KšűÎö¸„ËEy©Ç.¦J6¶yĂŞżârYCCuťjű®?RS«ňRöě» ±ş$çÜĹx-´ŐąÓ§ŠËk0‚ěÔ¸ĚBĎËÔy'Ďź>—:lôXĄ°ůń‡ö&ćć_»¦fvS§ŽÉ<ńÇŰw%]NÎNŽ;“R6e˘Ő .”©5LlTU¶o‡€Ŕ.Ha,Ń©ĎÖD]şVWf‘Kôęsµš= ZSm˘Öq°3ŹMŮŻäŐš@/Ô&h;óČ”ýJ^]¶‘™„ş¤c@Ô jŻite&ŁĘh¬ŚĺúňíeE;k=…Mg<›kĐ’üÓŔ çë,—ŃüóŐ©†Ö9APçJZÖ4TĹ]¸ź}Ő3{¦'˙íŻ#!aˇ ‡ľ˙ŕ÷SނĔ‡i|Ň5jŞ˝p>U°F$U’6ć,ąŚääPJ2üń÷=—.^,,Sוś»xIKmúÜé“…ĺµuĺůFÓPwáBAv˛5nlްúŻ‹9 +NĘ®–˘ÖŻŰwž;w®¨ĽĆZJ«©:wćLQyő‘ýűőĽó„ÉŁę˛ăý}wq~Zóžqo…şŽ ŤŞĘŠvôĹFLŁnŠO=“§†ĆęłĚË›¶Äg—ôęŇÜ“'ަdP2N‘“qYŠüĺeĄéYéWăN]Í)íÓ ·’|üö«?í9I¨©Q%•Ę( z›Ő^?0jµĚT×~_ĐRň?lßu).ľ¨LÝ$źÜÄź~ţ˝˘F§ÓŞâ r3N]ÎqVň§wďřăH\đ AĂôlo,ŔĐŘQěFEAW’›ź`d‚Q«Î;ş®˛†™j .]Ł"2®ěęĄzµ† u†SđĹ—N1Źž˝‡vËąćtć• Ć55jJĘ2Žě/ÍĘe zß˙ŇŮ!;^]×Ŕ‡e˝l0C@¨·i+ľNŐŔD±6/łđňI–U)W‘2jÂhŐŮ?/lßY”X[ˇÎťĽ{CusÍMŃŘĐPXš~tIz®´SdŞ/­«ŞČ=¶'ůäEĆ@ŻĘÎN¸<|¬ł˝°˙Ů5©—óM噹×ʢƎ©żv:î·í…Iɵ…©Ů ׂ‡Ťq¶ö?»öĘĹ4 &C}SÍĐLΚŞÂř¸šÜ”ĚSńÎ})ܵOSSĎLu¶’ŻS7€¨×—eŮ_śž łú’ěs‰ľc&¸»r‡_Ţx(S‡‡Ź[—°˙÷'7V©tŕěęQ•r:q× Â5TfźM 3Ţ—ďyr}^fyď•[GĎ –t!%îl.ç]E —j*Şő•ůń F&4Şüógj+k¬3yWłR-ĽŻąšş’ŇÜšđaęłĎĆmű+˙â…ćĄTůÎŐVÔ4i0/!i׾ڢôfŇŘ˝_SSß*vÁż ¤­Ě~s×T—çöR0?5‚Z/ŢL†¦›{˘Ť÷}ÚŻs —,Ň' Z? Ôf=͑һa= »÷ΤÉkÍWgäÓVţ6~íH®¸-Š:(Đěő¶N±ŇyÍKÚŇ€;)!ÔÎg-tž3ŰŞqK=ۦÜá ßśćöÜV˝čşŤnđűÎóĐę3Ôˇ–‘%;’íWĎ][[‘×Űś)Ý”Ö۶çŽc,e·!€ťsĚ7żl[}ß=o}ţGeÎĺ a0ký˝En€ć¶C´Ęu=ˇŰŇÜÎK5׏@Î1F–Ú±x„HsU¶’ĹÍxA› µĆ4ĺĆÁ!$iÍw0Ám§lě¨çQž~SťŻű ¶´cű’n;P „›ŰLçČęśś¬ä4# µK Ř8 ĺ×–ílۨÝZ¤^·%¦3uŢ„ ZŠöۺɷWjzŃń¶>Őnż+˝čĺ?}őKéľöÄBI˘ťĽdČüńŇiŻVżk™í•iňÖ›–jŞ«Ť–Ú¬·˝hÜq;mv˝˛¨ckEmG Űř‰ÚÖĹMv|-Űż9§»ÍŮK˙Wá¶mf!ĆŘŮËôÇŽoᣔIy”bŚÂ!Äc­0€0汞 „8B0„0!ČĽŠÇńXzţ˝MZ™&„lľ¸Epű—cMMN*®¨0g~b¬©ud‹±¬EI9wĄ>^˘ĽŞ¤(95“5ż‡ÚĽĚńҦ3"ÄĽ˛Ű†|‘H%‡€1„'@Äqśô` F´čę1`sŰ-¦$ż¶řâÁ°9mŽdr'ť·Ě- Žă“Č  sL!ýnngçÁ"Hi/s"ČžsvpŚqp·Ă\Ë5@E¤˝ÂiĆŮf@n%G)‹s`źăńń ÇĹ&X®ôščá6ر)»k Űy¨˝ŇGváöÍĐÍRžÝŃóWÇźÚbͪЉÄ|w!ŇĆ‚d>ü—ťß;±lç\ťqtč„…ZŁňĺ÷Y}ç0ťÁđ׋W?ը҉$J0Ź-/˙ëmUbrŽ HKăŇÍČfÔÚpŚŠC‡V>pe€˙1ůžkZ=kk:ŽĂžŚôźâHŤ¬áxUę+…"ć đźçŽk“îNŐ1yŹ·cřşú¸{3G{÷}7´č©4ußë_ab•8ňú¬ú´gs´ĺ"‰Śrę÷idá+é%Ç4€€ M˙ A„{ÚŽU{O¦€Ý`÷>Ż„2Ť‰sá5ç+ŻmĘ78|c¤ßd [W„P&węűy´X~ůéňč·{ČęâîÍhŹćôçr5epz˙üÖË/ťőÄ»0!˘ĐŞnkińé7ËŠłj°i2öëŘÁÔhpPČÓKrVąXëčő٤‘Ž`BDV[W:mďÉFŠF”Rf©–·wţbÂŽ2ŕFMőňĂG˛ôvOĆüäŚ:µ^/e'úɰţŤúF…L^V]ţȉłéZ#AHdĚĹŮgÇ´Ńźś9úm~ÚŮ·iC'R€2ůĘ˝—G‡ĘkďÚy(S -Á d÷@˙fßHMűąúü6}bCyúÇÓŢ›8ĹÓP1fçń^AÝvLňé±ĂGMvßO®Ń4Č%šOžO×g:ÔBQĘ{ßýół[śu<·#Ä!˘hZz7’V¤Ý"Ç;ňŰ[«g|ü‡\C{Ż_»ęü÷ßTšjžWBň“v#€doöC<ún5ŰŞöbŐ• ySĽ{="T8W™ćlEęÓFÚQ)ÍůĘÔ'óŤś|JşN`˘ŘáP«ČÖúŃRŰą4M«ĄnÓĽc¶X5X™şé¶hÇbaÜSCG/÷Ô‰ôJAÎCg4­¶FlýëžÝ‡ÓŚâŘĐWb#´z‰ŹŇî“ §ŢH«y26öc}gµó{çbB;ŚŇQ’ZsXú&k/&Mé°,ŕąWŢxG}99GĹ()„€QAhÖS[k¶˝ţ/ ]Ů3Ŕ|4“uĂmip­1­áöŤĐşŔ:Ł‹.řçĐ|ĚaÖćßLK`T~ôĚĂzG¨ ÓwíÚUQgň Žž7{ş3ÖîÝőgjną˝_äÝŤ&Yl˙n űwJLµóŹ7 »Á$‹íß-~ßÎĂ—ÓwPôsg8Éijâů=GÎ1Â{Î=‰Vçě=v¶5`ŚăLµëďťv(ŁÁ Z#DNY´îÁÉŤQĹ,ŮőŐ–;w˙úĺľ$B° 6‹ÝÁ=úÍź9^UÚX[Yˇ71đŽš=cŞ3ŃíŢńKZŤĂ‚©/řł¨^,óź=ĄFQ–Żo8Ĺ€~‘š;¤!`Ś„:z sĐ^npě.oc1Nc óśď’ą4ŮîŮ÷µ€ŞĂ•eE ďáŽÚË ŽQrimʉ`ćŕÚ[a?ČE¦€x„´Ć”…Éo‡?÷đźěšů]"ŔDđ˝Ë«´•Ç5Ŕ{)<Çşr2Ц5¨.6´h!,dÁ+LÚööZ­°1_“ţäµęK»XźÁź…¸ýZ¦şfÜ“d ÷ěűŞżę”şô´V:cÖTc PđCvPŹ`İ Wv@łß$×Ěď*©6ë‹ß?~˙ŠÍ_ě2 ´Ů!`Lî1uhŹ»vŐR~ÖĽYqÇ őΓFô¦D٧›˙ľťż'¦ć!Ä{ÎlGR…ę:ŐúهO•V…řEťš6h’ßµźj©ś™¶?|˛ÎäŔD#fxIăěX˙€qîö‹¶m‹ŁnWMť䛕^Wg4DăBĂőue1dŇÝ·ăŻ2Ą×ˇą“–…=u%›`$Šě®ŠĆęÝUŕ¦tščHX†ŞęPIĺutÁ(ť=gxf”UöňP¶^b’ŚYéŇň ŻŘ7D €Âe!®î#˝ĂýÜ9Dq1CĂ};ţ,•h xęJ¶Řľ.¤Łt‘Ń&-X=mî=ţ•şK'd©’Ťž1wXďuQćŽß·ś#Ćގ\“v0ĆQ`ůÚuvĺ‰Ű˙ş1ý' •˝÷ÎE"g˙‘˘ďا›˙ľťŰŻ«S‰2SŤ­~â[¦ÎĐäľ–SľSe7ÂoŔ[CŞJĎéšlµu©OB<†U”ž…z¸Mô¤u€tËżZ[Ýéi¦űĄU«¶ž=»-3/,0f×Äľł˛*®Á‰ćĽx­luę É?›ę †[nuŔÉĘŻŽµ`Ńú·ßxë»ó‡»×kMÁ˘ŔL(ŚúěűĽjçęöĐË_Ď(™|×ÄžčÜvZG¶ÖqŁu„d†˘Ćś­Ů»Őv#üĽŕ>¤Ľě­׎4{ }0ÚoÓ®żÔ®áŰĆő9X÷SQµU­ý QűłŻ¤”eë Ť}‚Ăߍíy©´ŔľFŻżµÚiÓăâOL/P…D÷îâU%('ĹF'ź?˛çxü¨ÉsőëNLÚ}»ţ¸šU‚1PĆŤž9OňĘ?vlW BuuµŢ(Ŕ”ů÷(jÓ~?xIćľřÎyÎhĎ_ŰRó 8ŞWL¨·ąć Çö‹ĂĽ×Ě9©şÝ^ř˙0Ć®{Śó¦ŕ6ŽĐş  ţç€ŕîX±fhwťŢ„ăeŠŇěËUĚŁ_¤żŢhBĐ cśÜN•}é˝/¶ 6q #  {í«—šw1>˘÷'®î­_3ţtß!±´Çđź˙F•ť\Ź]6­^:¶Otuôđź˙¦*;ą»l|pÉ´áÎUz}÷űárU~-×ÜŃ'ĎĎ«× ŠČ!_ţöcÝ©»`ÔúČ>ÂŚŘ»=˛éĺq)żüřu±J5şĎđš¬s;‹ČWďÜM©ľO·ľ°/ÉšéaĚ(őŚľsϟΠ…u2çh?g˝N NˇŰ÷îç«+¨×CËć.xřĂ×ßýôőűÓ?Ű—#§ľ˙Ţúwßúxí+ď‰e©µŕĽqĺâcGťÉ­±ëÖ>…Ś-¨MXRí:Ë·G//Ű9€ůŔ+b Ŕ9ŇQ(l¨*‡č :D:B\UüâZ×Yľ=zyŕÓ Ťe˘ç?YŃä± 1-cöƄ˧%‘šJk*9e˝}”\PôU‚ë0'S…ÂFćd ÜtoCOxĚt @eëť<1"iýuh6S“”pFy×ÚŢ|‚Ćd»fŹ1ˇ˘9hŇo°oW- ˙č«ßY3é—lźź~ßV‘yĄÚ |tĺ˝ó¦N9ťV˘ě6´=]HV7ÔťŐĘ»»şFş;Tëtj°#âd÷÷í7˘ş~GffąÁh^ć´–P76V„PGçjć¤3č˵:Bô˘°°ďŕŹD®Ů˝łX¤@řÁ!AŐÄÉhĐçk´`)qđ\îýŰĹ“jDîřÎř±aśˇČ€ç†xź+?¦›ŹZé‚ ÔXW2cgáČn=>ňęÖěÝpł‰1hýŤŔ`rtżŃŽř·śŠľöBČd2j„iQ==ů´Z­Ś jbsšÓ íéa”Ĺ śüŔť“qřĽecqă+y§2ŠÔOĽöŐó«fÄ]ŚďÖ癩ôŹ|˙źvüfŐÎÜÉĎd”#.hČwŽüfâ* xŔżĂNWznŰnµAÔgôwŰľ¨Î•J-ą®NÍÉ!mlŐTeôÔ§/-Ň1ŁJ/6¶¸7ŐV)µÁTkŕh >+Bz¬öJ[‘¬ČČßńŻÖV§ŇjˇŞ¶ţÇú ˘±A+Đ–:mm«ŤZ•A  č6XťdF˘PUVńćSO,ş{ҤáăÎięxgźĄKk@éă¤ČL5uŐ5uŐŐµFć§/.+1öŰÓN‘­uÜh!EÚ”zm*«Í'ćo±v¤»CÜĽjë«ökĐ;CÂt&±‡łU3Ňü޵ISFŁ!Çh Ëý‚KU…'ëL#čVÇ„6<5n-<ź^ ę3jŇĎý«Ľ0S«çN핸öÇwŢ{§:3»E®^qĎĽ“së6üëëVÍ»ŃkŤ#WýÖ׹ržčuK7ż˙ísËÖĚ®!żďŮAT95p˙˛»î_8˙ŕĺś>٦üňńëĺ…™ľűŽ‘GLĂ}őŰŹµ'Űí…»ŕa¸ŕµ#ĽtáÖŽĐş  ţ'ĄÂÝĎĎ·AgŔ2;űĆŠ,ťŃŐĎßO§3 `ťŔP^áČÔ×lëEP**‚ĆŻ[5˙ýGçoúfź{D4¨KfŢóT/㨾©†îYy‰÷Í›úáĺZh¬Ý˛bę/I>yÇűéľ[Ą]Ť„ÉÍ?Ö·Řą‹>ú„|ůÄş'ß?äî˘Ôé„Ub\Ş.3×ü}+WŢţjR\ynĹŕE«–,ť×»ű@ťA¨©«_»ĺ…Ý•Ł Ť Yłó#‘”ą÷rĹ=ű $Ąů\×Đ8vö}ý}ĹAÝú¦Ł23ĎĎäv>#' ´˙¬űďĹ'$0żęŠ´ă9µOkÖß7őçËŢ9ą‡ Ž>“{ŽŐUwLˇ”ôs­ŢČh~ÖĆÔ ® ”ëu5i”X.®`®éiÂŃŞóu^süŤ‰ĺµČ1P†äśßRâ&ă‰ĐPhÎé}‡7_«+Ý_ ŘÍÎ1BVö^~Î/µĽ#ˇ¦–M#„@h€«ÓşZéá ö\bž VĘ4U5E˘Ż2Ő V Ą#ňŚY»_`"eŘRŹÜç3ąţ.ŢŚšáhĺů:ŻŮÓŚX]m#áţ!$!ĹÔúĺ,´­€5­AEŠÚÚVĎúţDc\~겹SN§}Eëk:Đ…ÔGúú:"¶›“]JqŢQµÉή©T•őúţáÝçEݱű@JAš«eL“QZš˘é˝uĘd-p µĺ'Šks­7 ÷ôŕ¨\ągßź• ă\‘-îÝŰAˇtÆËő©ŃyQÝ\ô5ßg—€\áŇ×Íá׸K/Ą–şĘH#EĐú`d ]€ůZ–nú·m>ÍżAĚŃÁ}SďĐť:áčŰ{5Pć€Ĺ=ĹĄ3»őŞ*O?_‹íˇŚb"·ŇśTŻéX˘@&ý°őĐů+—ľłuĺÄ=©:PŤ_»ň)¸…÷79ô66T[µł|ŢÔ3Ż~ĂîZąŇU•ňŮO€üýűŹĆe§ě{w c"&jŐ7¤Só†€ĹVÎŞÔWŤ ]˝>âń ZT§:Żkf«­JiΩÔ×d'´ÔoeÂOžzJE˛Á-ŚÜ˙˛±ş«ĹąG+u€0  xnČC]ůˇ’ Ěv‡˛•­Vݬ7‰·Üę¬M€jÔŻ·RVb;=ňŘF‘‘`7űSćŤnéžvÓµ±F}ťč×<˛µŚíDH„%"% Ö«.ęÁNy;´#AµÁx˙~~BÍi• °0׾IŇÂ"ŁÎŽ>wř»ţx>™(0Ň·X;mzśŚç@§7šę+7,ńgB¶hPmYýňeËB}]\˘ŢzkóŘa=«LŹ=´đýµs7}łĎ54Ę™©AćY[Ż{ŕ™‚B‚ď7â—łW>ňƤľŢ[׾”¨‚×?ů|Ăó>ôzŁ –šĂĂM¸Vťp±C-wÁßiM)÷đó÷mĐoá­ şŕ8zţÂm©«Á$Ďóĺ%9Ő˘SuÉc§1„“Ő–gŮnwJ°đ~aĽ®âÄ©cH[”©7îN>U%™j1ďRZ‘‡ÜAÁ±úšŠ+‰e :éM‚ÜÁ×`MMErbNŤ“W”ţě«#÷=űë’§ę|÷ńšMŻ €Śeç'íii°y *€&ëč‚Y'EÁÄżń[NĘ)“†•Ô¨ý{Üůgţ˝ ď\ńńNE‹{{TŹŕĐŠĚ„J–rµHUGÂIá¦.Í’hNĚ*±kDgOä.;¬ŕčîx€]ŞĽ(ţw˝ÎĐXŻJN(ŃQoe®`(żĐ…¶Ŕ¤Ç|¤‘C€tVZ:¦˛'7õ'łü6wÇÄv«P†í‰>ąľ!ÚYs˛A6F‰ĆLcL[qµAËEŮ3ü^Ď„MEŕb8Ë­ňë\˝€ßPúgťß‘>÷‰Ő»Ë3ß/m±a-őQ5 R^ŽĚç©u‰ę„EµN={<ëď«jy¸[K ‘9ŹžôĐ“ďÝöNűŘąF˱ńăHě‘>©ˇ!JÓ.ÍK<71Â!ŃdŇÔŘ<üÔśH P@ădĽF]š|©’łU‘ˇ® +9?ehOf#ňć*`U–?㏒ţţáďŤčą8ÔëŰĽŇWE‘ż’wvŢ„±ţž)éĹŇ‹»Ň÷"căĂ»t"ëÍcŠĎĆ [ÚÍ˙ŤŚZ |wOmcm¶ľ0ĆÔ¨yd÷ž«&ůű3¦oé1ăŘeP¸¬ě´;é\1‚AWWń}Fé’ţŁô5ĎĚŘt骮ů¶YşË-´V–ĹaĚ-ĎĹXż‘n3ßÓ/L.Ę\†{( nN°—‡ **’Ý}R *˝CÜ @µVšźé1ăře¸ž.€ÄCpBx_kmQVµŃÔ;VY_UҤťpOĎ^ë—ÍůýŐ•…"`ŽH„ŰL0&`ŕd˛†Ô©4 ¶Újôf?ß N%Gę)#=BŮĺyF&í™m•‰¬E©OűűŽU–ś(ýE•NWcéÍ®żď_­¬nQ¨÷·yFĆ=7zĚg6oĎ…*fŢô¶ę´U©^‹BÜżĎ7"ÂGßj«łšc"•ń QěëK‡ Ő@ޤ¤Ú™×†(łĆ,* †ňó׉~mF6dŁ‹¶"$PĆ6kđţ<FŚqءű-ÖP*z»x/łÓ®9zňîáăäćW‡Q›ţ%FLd0!<ÄNlřł° ¤\ő‹ą=Ú±ń8óÇ2™˘˛$í`r6.É+ ë=áó/ľđQčłóK1Ó74 >ýĂymąä•š˘¬A”)˝)oß§ßPMYjfe řGÖ«TĂ­śŞ©sRÓKĘ@Ć+*KĄšˇ87‡@Ĺő´Ü¤~ćě…Ă %Γ·n„Ö]đż ·lÝćűÇEU7č10{§¤S;2ič]cz×kQ§0Táŕš}ć§©‹7Z.ßKQľ˘HÍŮ9D†@N:`@‘ŢÁÉÓ ą„ú{\F"ĄH$ ¤DJĆ„&Ś R >łvÎóŹŮĎXőÜŹŻn>~ř·ďŹĺ+<#źŢü1÷äÖ~ms_["d&‘R ÄXV[á˘tzkă¦)Ć÷Ăüv׫Ŕ|ÍŔ\(m,ŻTŽĆŕĺ«”ó˘h±ÁÁÉÝ 9‡řűTA}RüŃ-ŹĽQšqţšŃăî™!/<|ąŢč‹fšÍ—/x·đç7­ď€B ȨɼË˝\Łź4\Q§ż]ÂŚďĆ}UťÍşČTiQ¨Č¨`yX•3™R¸ GOóŚ1R‘ô&9xNöV Cę5W…Ü×3ňß!î bńŻ9Ż.żdhv†2¨ĘĘ3!ĄDOHŠC! 2Q+ÔśRŤĘ ąŞĘŔ»É ßłP¨Ŕ®—KÔc†+ęô7K€PoĐr~ }xg‚Äs”Pvš}äŔ {H$k3ThuŚx^îŔpŽ2‚D‘QJí\ü’łü<ěŞâM óvÓştAĆ4&Ă©Âü*±w¸“@Ąy Fłd ôňcpżŠĘĽÇ/^;8›Lš˝%•ŔYz±—Ł@ŤÓÎśŽéŢ˙‡1±±;O(”Đh‡::Ŕ´°îAHóPN¤ Ć×Ď{÷"?1şďGzť)ÉÝ^޵žŕoOŇĹT1#ĄŇŁ Č+ŕíˇý+*óÖťżj˛J‰1#Ą&Ć ÎĐ^'»'ş»«‚çlz'bTőěÝ ĽŮ«žRĆŔdCó`ĄBT‡ş„±\&S:Ůh ˘¸E0‰˘¨ttłj§ú˛Ü˝:ĺ/ţőw‘& 5ôN0@‹RťÔ)"Čj«¦'üíő!O†zä.?ÖP`’n+z›=.íŤÖĽ”ń‰ű@;€zBXöKŮs{żrnIŘßőŻćV§{G:9T¬6tžźlŢŢĂWę Ć"Ą-t*Íp¬Ą"śś*•·ÚęŮîDŃdÔSę5©Ď®ÂÜFďޢI0™IL˘ĺz4µ˙G1‘wî™ íh0C‹ČÖ÷ż˛ëÇ÷ßSWŁ35äß5i’ZŽ—q&M"“&wQěXË]đ7AMń÷­fůčČÍ­ˇuAüŻ`|~ĹĚ—0f 0@R‘zcóŇ[ç0ŚŠFóŕ €1ŠT‰‡~=_đö7{˝_ýpĐ´…G~~ú‹zýŮUź~ýa®č7Ř“>·ç ŽşÇÇÝť//O{Źx…·—''aĽ}ĺČ~ŁÎî~÷ŔźÉ{™ę*K‹uŔűöZżnUí·×?řUh~˘—pXč’µď<±¸÷öm'Ć-]á¤+8}ţň]Urń3CFÝőøHo}Á÷»€őŃ)Ăýˇ“;źßüË7ź˝' őővrâöěÝĄcťDó ńĄŁG/ ýÝÜ\NÝ}Ü8xćŘkqçřČĽ<Ü%š=˝Ľě ÷ëÝ…0„BźóäČ»ń=>Ž.ß^^| †w—ą wmĄacőąjMˇŹSŹ2َüx- {2Ě=Ö‘w“őř¸{Ĺ ‘b‡%ąs.ŘaÄ!Y 2âĄHp‘»öW˝Y@ą«včęq ŚD¸öy. ć@%“SŤ`(Zܸf±ěěÁä’ů“f~ü{ś 2ßĹ!Áó*Ő8 vwp¤yi őEDS`4Sh0”ź¬ÎÉÂ(˙:żň; &ňJo˙`mę e{9tDóŰe2ńŽWm+1`Âl^÷‘¤™w%Óäôô§_–˛°0oWž—1“9ű>÷é‡c3é¤h‡ĄO;ßžíZ F"eócb‰ňÜ•[ÜË?¤Ź}!żdZÔŔÍŃĺ” ďé,jŹ•¨ŔQn?600—ÖJgëŻÔT9; ~Ô 2¦ďj÷\Ľ€óRŘcÝs'ĎĚľkö»±•ÔQo'·Ćڦr‡Qľnźśż ŔŻŠ =źeiŘęŕđóČľ§r˛ä®J“Q_¦ ůyŽ6tÁzbČđI~Ţ. ĹÓ&öeN‘ł˝R˘PZX=1ÔüÍ»S'nOMý:éâžÜ(ĐÇĆĚ\ćRóŔ©”s‹g+Ą}ĆśärŕŹ‘ŹłëócF3ąĂH_·O/\3Pŕ&´Ł óP±1ďr™ĆnËŰż 9tfű/źžMÜ·íBˇOżóŔw˙ĚPsnţí8Ţ»ţ/Źő«ć˙őÍij „EÇů<Ľ~Ů€áŁCźŢúć©s»âTZćä˙ü§ŤÍ;ŁSi<糨ÉVÝXö±j·ÝÖúÖź­ Ľ?Ř·+ţ¦¸âLďf¶UL@Áç®ŕą w¦y u<ďÂ#mcös9ŢűűDŻm¨(`˛@űżă_­­®·}!żptdß­{$­ę¤”}™|ůŻ˘*W‹Ny&óc­íî!•ę)§o••ČÝoµŐI[# ™]hŹ!ýąkĐŘq…'~ůbďéEϬőts•¶Ü<˝ëěĄď•Îîžn„‰2ßvŁź´ScŮZĹ óE'ŚB3GČϢ‹>)Üśş­óo8[x°ť/_ôi~e“yČn­v¤.ádqAJ}tß°ýťĽDÍď…Ąŕd‰­ýëiw\»öeNqźĐ‰v«ÎçZÄÜíěoyLhĺq§wl˙öô•;G/7 ćZqQ¶«˙Ч7n˛8ÜŰ]éěĘr÷ZűĺŘ© NlsýüI}Úă÷-]·ýťËk>řhóŁÇöoűᇣ —ÝűÇ/ľóů_J'g©f©×€´Ü$ź]>í…[=Bë‚.ř_Ň› ˘ôź ŠTÂ7iY7BĆ#ű÷d.&ŹŞË‰˙uűîâ‚ÔcS†Śŕ lްzw|A¨Ş(ű|\Ľ– ÚőąÓ' ËkëĘóĎ]Ľ¤Ą‚NŁ:wútqQ•ŇŰgř±~rÝ Ď<şçb&ťşŞňđÁ}‰©9-U:€lgXDż~Ń•—Ź?ţČúKٕШ>v2ľß„‰î¸lĂŞ‡ĎĄ—"Ü”J8V\;“R6uŇز«'ým{\Â墼ÔcS-4Żú+.—54Tש¶ďú#!5µ*/eĎľ :«KrÎ]Ś×RA«Qť;}˛¨Ľš ŤŞĘŠ6)´j¤0–čÔgëŚ ˘.]«+3‚ČŚ%zőąZMŽ­©6Që8ؙǦěWňęňMŔaE`S©Ćtm}–¶ţšĆX+ jµ×4ş2“Qe4V Ćr}ůö˛˘ťµž‹B‚&Č3žÍ5h-IŔ ڏp.±ÎrÍ˙0_ťjh˝Ö†ÇÍŻ1=ýüĆÄ˝?ćTh °#QvSŇ]Ţ;ůŞ$…š sŤŔf,µpMW±™@u™Ú†L˝Ř(Ô'7ëh›4—ýZVşŻžQ3bń;ŹÍ}ęˇUő¤Ĺ*c@¨+¸’S3jÔ˛ŚÓżíŘ}âŘaěŐsîȨ“g®uŹ úŕ™ő_íąÔ±µH E{ą,ÚÝÍŘXýúŸŁU €›ŁSOwç*uů ç.%Öj@ b…¦ţxqÉ•šPZ«ľR«éçííÍŁ~Č+Śu¦Ć+*uqCýU˝=Ś*uV¦Jo¨ĐÔoKKý4łxjĚ ŐˇOž-3™E. ÔN!ěç덅·ăăWiŔ|'ą]ěů1§˘cŕä¨n¨9RTVe4ćŞŐ™Ą´˘ˇîxqÉŐšéL ł“ő›<•:SÓ(ÝÇ6Q1§ZťTÝĐ(**+ čE!MU•©iTétz}…¦ţ—ÔÔosJEĆbFÜÝ®.c`¦şóçSE‚drr59ˇ¤ŞćčÁzŢyÂäQőy—żůú{yP˙9#$íüÜă_îIś·ćŤőóCV/»ż¤žI§űq6z¸¨ÎÝ{ਠtÖ•eť§ í~úl§tjľpĆ€(qâ$[Í}7_ťbŕě.SŁÉÖ‹ZŃPelH×č+«Ç5äč€(qäšY8Tkj¸¦Ń—čkÓô„mŞVWb4Ö Ć˛żĺ_-¬îH•Ƒ糫«®T×kLĆrťîŠJ]¬ÓSJË%ťÖ6P˝(XK˝v1nEĆHŰŽŐUj~K»öifń´A«:guÖ{ŚÉ¨/*ĘŻ¬¬8´íۧž~µZ  s2._K¦Ŕtş†„sg2ŠĘ@0Ň“.^ş’a.*4väqZF¶6ă†m„Ôş4ť^%4h˛ô˘V4Tҵ•@őBCę-ÖFšôgËU˝üý\Qă g.ĆŐęl#@ŰţĄVgjtľ »\uůyeFKmŤ&ýí -=.éRIU=„Â쌸řd©gËHN®ĺÔ©c2OüńĂö]—ââ‹ĘÔGöď—Ľ˛.7ń§źݍŃé´ę„„ř‚ÜŚS—sś•üéÝ;ţ8pđŻúôS©yU 04bü¤‰ÖÖ%f ˘zŰËTuő `ÄÜ•ż}´ĺĐO?׊ć÷˝,ްŤśc 2ŻĄ÷-¦u5:đč·tጂäË: Hš`ó–>c qć(ZQŁłúrëř0& °±y„™H‚9,0L0Á5‡Q‚̉ &’ ĚßX" `„ &ć5oKDjW›U=# 󛬮ąSPJ#ú ›7a@brµpł­géS ňŤe­ü #D0––ś12g‚’ú<Ś1›{€’4EŃ’”+ő”aËłlš–x%y+ÇqLE› Ć(D¦T´ĽÇŔ,«y ˛©kęć$ăŮX¦$źÖŽ16ż¸Ň> Ś­ě[dŽHíőď­cxg۲ô ć†`ëMŇQ†"D ˝„dyë®eżÓ1…Ň,0$¦˙ř1#ü\ЋЅć™ëĄˇ­«w@Ď&ÇF}˝®G 9iÔ¤«Ż¨©ŹěăF ZŁĂś,şgdÔé9§>˝bü=]kkj„¶N—µŽ‡ăϵ'L8Ćh‹HŇ®:ÂÁ˝šzŘ6>sŚŞ¬«ouâA+«ł-dĎWÖ5DĆĸq7,C0o”™c«+¨¨ŃY·Č¤u$v=K€vĆ]pÎĽÇqŇę!\˘ÓŽ[iűtjůi•Bá9ňđáĂcĂě@:>ŃÜŇ;1Ó•>QľňÉ®šjUIYÉ®O^s´6ŘÎ7»?yÝ Ŕa«óéż” ěWPU˙č@.“Ŕ„%k+j5ůY™ĺUęřcô t›ESłT1Ŕ#oţY^ů×o?Íž ¶[7Â"'.ËLŤď§ä@fđňGźg”ÔežüI­kýďËÖŽô›t¦¨ĎÜ5uUý›Î5SŽdÄľoľEňÁÍj±¨YV¶}ŻüŞşGî×5vĆ6,źbnÝĆçT¨š7¤‰ …Ľ_Ô_gŻV––T¤ťßč6:“8í;wMmUA_ §-W4Ű'ł‚í„_´ńÍҤ¬ťÁ ”pxőÓ=µŐę’˛’ÝíčKRÍŠW(L< 4˙­™–ťŰG&¨ßx`¶ě„pܱîĺśř}ťóAxŻáý¬rUÁŐS=”Ň;ÖÖ÷~ţ&{÷C5ÚĆÜŚ uMýń_Ţő° ęu(>łĽ(żL­:öíŰ`ţĂo©ękňr˛332µşúçWÎÇ #—s¤oő– p>Ź˝řą-{Y›$ÚJĂ‘p÷ęjŤąőc?˝íK1cRËăţřHÖ9ů´éq`‰c}íĄ“ź¶®_nOŐ×5 vëŕ›µůÎF‰ëYKçŁÍuVpQ§Łč ö•­ęĚ7ř˙Řűęđ(’çďꞙݍ»% ¸K€ ‚»űˇÜçî.śqĆqîîBڏ{˛É&Ůdł6Óďł»Ů$áŽăxżżý<ÇsĐŰÓ]]U]]=Ó]…ŔŇ3čb´F{Żţú90ROĂořÔ`ŔÂmÓ3Ď|÷§úŰG/Ű‘ÎŮď> .îH_Í 1Ő¸ kµî°Ă\ĺĄăş(ż˘6'+«˛şöĆÁĎ\©F9$dÎsWÔTńó˝®®úíŤs`ËűßW×Éň Š “®÷±şú¨4ëţ1!Ŕŕđő „Ľł`ÂI‹*ęäm{ -ěáÓűBŔOyÔž%6WXý :ĹhOÔP Ł[·lŹĘ‘‡?1zÝN«Čx|´uíúşÍ™ŃAKň·üđ§$Q#xyѶŢ3fÍp·Ôđťb\gÎîă,2uÇÎ]/nŰäçĆ˙ääŃmýć;_Ř65třuëűÜ«_~´mŐÂE ü=ě@äčł|ĂŽ×^zˇOwo@q:sAXČŔ–ýy›Ňeü §X6Ŕ7lů„p+'ö˝9c¨Y8ĚËqđ<%QéJ–Źóčź‘ż&¬7L]°<´ź×ř%Ď•¦ő6Ą» -¨•»c)h—7ÝđÍ\}ĂŻľšyn˙÷łĂ§u÷s€‘ăĂ·ż¸SěÁcV­^ýÜĘ9¶ °ĺłóÉWĄřC×mŚôY´ĐŮwZřk 1a3fř»Ű"{ŻÉSĂĆLľëĄťc{wţ$bF„ÍŮőŇKk–αDĐ}üŠÔ¤¸Ąa3·ďzq\ďľ={Ďnk6maËşî>.@›ŦćňśźOźÎł¨q^ú€ČŐŻQ-ëh9¦›qac# ϨŽéÉKنĽ4g6ž‘™eŻźÍ6—ň¤`ľ|݇eFžd9ÝTč4 “׼yéOA™żîZN‹¸}âđ‰üڏž"đźĐÔ¶ŚęâŇ­ĎşĺóĚzMÜ.WÉfö÷š¸ćSEmN »‰oźYµ «Ăzöěéáâ0}ÍiMů@G“)ľ”×dóu¤rĺĘq]Ŕ#4*:–難)cÜč?lŐ’Ůf˝&n«k¨[9ľ}}즢ˇáÎńď„Smó‡˙·ţŚ[3ĄôčŞgÇľŽM;°iPśŢ\žÚ—ç]ł–źťď¸•h[[:ŢBŤrő♎–5b\“őwĚŠ í˝;şV¶şRĎôq@öÓf-4ČŢ#_°}O˝$K«™ŠĄˇÎţ ž´CZW±zć‚+QŹv.śs8:ă›móŔwË몧ĐÄ cúŤťúŇîť“Cúf§ÎętVĽElőěĹW#í\8·ÍľšhfŘÁĽ zkVa;ĆÔuîâ«W­\şdɢł}Ým@äđß‘ťg·±! ó¨őrĄrí¤¦rGBk[˙Ŕ ‡ék¶×H*Fą mű„WŐ×îś?‚ş»Řőą–TĚŐćOč»őëł„·L9}Ą¸$ŁUÄ č§ęóGżŰ¶´Đć Ë˙‹ĎŔ¤oŁô›âw×ÝZ·lĘĹ­#źYúÝH.{|6·u«§ôWż ŢFŤ›<ą›Ź+ĐÇ'âońD@Đ3VlÔŐY&W!D¨8óA±ös“+U:PB-4gFńă!>b=L€ĄĚĽżÚóÓk‹R<ź ť†Mřćó6®CßýP™÷lüÖ­\45tD|ąĂŻGŽů ĹąŐ‚…łFťĽ4ąkđŘŐsÇŞUĘ!Ó—Ž˘o¦]N­ö=z攮̒ŔĘĄó–Ďśq9>ŰĽË źţQskߥk÷McJbÄqĂz –dDś, ö~6źăä=»ô‚s9¤ůĽß˛€˘‹W_NĐ·2ű>_B˘§_O¸xÓXŢ [˛űË_ßXľlxŹ<•„CçV ýŐĺ™ůů wÎ !Lké°zőj7ĚőßŐµÇGݬMnđűú›/‹“îSv]Ö®X8+,4&«¦˙)Ű·N7‘ß9pĽJ©D€%jîó` 8Nä; µ‘ţ·ŔâXµ_˙ĐżÝ>ćÜ©j®ó·{˙řlChU¦óهĘŇăk±ő‹«†Ť|;§vLJ?żľvrTdŚ_ĎMŚ˘čhf•­G÷Ýďż\GYű¬[ů쓺ÔďÎ&€ŰĐďľ{wŃŔłYej>BŽr 8xú\Au±\Ô'°óss†×–tţů»·Gž9YÍuţfďľ=;“‹¨ăgĎyˇŠ, ¬^±hvČ â.#ö~÷RČąSë¬Z¶`É”qW3¨ßŽď,¨Č­fć…Ź¸yozm˝Zä7đŐ±FyÝmK^łn}śM•±nóeÍĄě×ŕ<Ś˝c6ocs&¶N…ĚŽ0Ąw «­#š|„ë†ô ž˝tëV°0,h^ď zţăúŻ?d%±%ĄYuőJŽl¬ťň’î¦4°e±Y•őýý{ţp:.íQ’JĹ®=˝ öŘýň†őΞ9‰wř:™•µ˙tń×ţý.ń}qÄPn˝Ü 0zu;%†"%ĄŮő2ďO„Ż{k¬óë[<͇0ŰĆ„ăü†ś?>ÚET™[ÍĚ›>ęÚŤÉuĐ”v.żşxăô12µÇ{ž)ťď¸•h[[:Řźhőů÷÷ľµ>,*2Ć·ÇF3¨řě÷Íž/‹ďSv]Ö­X8}rh\vÍö~Łm+*÷:vö„ŐˇµŇŕJí6$ôű>t$Ű»ßχöUÝ0Ŕ>É›Ť•s^˛F{3Ĺҡ=üzé—˛“X€â’¬:GT5 rą‹Ź'­Ş•)BÂŻž7lÇŠµ ¬Nô¬š zđ»ŹJóÓëdxţŚaWűLb}úď=řGőÍöĄóóçóXµ˛‡Ź7'­qňun­/^ÇŔ»Q3ĂGŢą7EROú›˛ýůé&˛â»OV*匕גĺë¬p5±rÜĂgő¸ŔĚ*ëcgĎzcq=ţ]e^ʵb»îAÝz{ŠK **ĹŔźjÖdnPTK2jŞXëGĎ(zpüzbÉě±5EiÂnŹ۶ďŰ×OgɸşÚěĽúđ•Űť˝­r K"š•Ş‰Ŕ|č´©•"a{ŘŢÉá' (uŰ–¶íř¤Ďj2eMĄyiÎ:@JÔíÔÖ,Ű_·úşšaŠRÖJr‡‡-m]7W+ą¬,Ľť>çCUËE}=w¬™“ňĂIÓ®ÓŐ @ĎZµq€ŻŁLˇîŢÂë~šÂ}ĆA€™ČÎŐŐE*S`‰iCY†Liăęć*“)”pŚČ‚T>Ň5J€CŠÂĹen=ĂV.ÁÄF«ť««óÎ_>˙¨˘Ň×ÍÚĚĘ˙“OwŹÖ+ţŠp@OďźvlyáËKvÖf4Eźůó˝kQ‰.~öîęĐ3Éő0sö1=ťßÝřvś>ŘóĂö•3.oüHU#y•\źž­‰žoČ3Kj6ľňF'YÂŐŘ‘&văˇŘfu®?›¸Z÷”B$d@Z'_őňW^žóG 9p/{Ľż ­VnÉŇŃĂŽGgäŇXćXTqjÂĐł?^ŤŁŻ~»ěÝ…hdą`á"/gksk˙Ź?~qôČ1Yý|ÓýôÄsźo ď¤ÉĺMnaIe\T˛¬­‘ţ—ŔjU}]= €ëęęj±W'yeĹ„ťłsŻőčĺ~»ĘrëşY_nšľë—s¶ť„ŇóîS\ýk+'xčś›Í/Ŕ‡qśŰ׍ëץW˛˘kzVě˛đÉŰľŠ«kPi82ôŔÝÄŮ›> íĺôîĆ·âÄčăZ77ÜąÄËMPŇ˝g_jŔ܇ö`ŕ:Ć1Ăşˇ š…y«WŰ“ľß¸¦·t ůÚĹů/}}öŞLk]żt! ů1}°)–ďŢ0ő×+ŇŚ¤ŮĂíddązOYą`8­v«*Ką‘+ţ<8¦«†°iË‚ť•}ýzVŮMN‹;ÄrjĚŞęĄ>Ëd âŞúQ3—†»ĽąţÍ81|ôĂĎ;VÍŢy¶˛®^Î"Ău>ü~ďóKĂn|’ܧ‡÷/;6ó3N&PKÄĎ˛Žµ+/„đîd#Ä\Mµ¤í§jŞ(FäćEĹ&©ĐZ«ˇ€ÖŹýÔ˛w«Ypüř“˛nśŹNÓ'ϫϚ‰Cz©;Oőuµą+W€JĹÚřŚZ8:đçío@EYŞOż•š:.6 5߼¶/šs‚)†F*%çřń'Čłn=/tďýÖ¶ŮŻ.źi9|÷H•.§dküÁ±Ś_ěNjgźTă—Ď6ŢKK8÷ůşö ôüu×ÖľĽdocQŻFkëřą›éŢął WWĆFvH:nőń7⻀,wĹĘţ¸řg°-î ! M4ÓÎÚ¬¶!Ś˙bSTFâąĎ7p¬ äe‘Gö%VŢ^ľdrôű/Ą…ořpLOçw6ľŐqcĚql·ă˙úţ“.^Nń—ţPR–<Ó:Ö®Ľř•D"U¨9Žâ¶ź˘hÄŞTuţrô"‘Ź)ŔqşśRćę?µ±wšąÂôŁ_…ů«GŹ\ßpćĐOߍüű©+…ąUXŔ_ȉłgš*˛÷ť¸ ‡˙řnŘČş:ŃÔáű2x>®%7«Rß;;nôz1Ŕ¦­oűYËEîÝGőö4q´ź6řŔٶuţ Ž{Oď˛ôŘrÂĎ8í™ ŢŽń·ęRoż÷ňęW˙Z´łöâoß­Űőž’p 0ÓXŃŠ2ŔĐř™éŮÔůvµî‰h/ď€vöŃYHYQVµJíÓcĚwßkÖ LäҵsďÎL}i«VTˇ¤m˝ťÜ}ÇŢîZihĄ&ĐůŃ7” Ž7°;iůË8vŕÇďÇ4j/%é4|J7vÔ°Ő°~mË 3iţ‰„Ň~‡š±U(¤şĎ&zDĺE)ă3 0;‹@YÇĄ3wĹ×G¬¬ÝWma.-8Wh°/Âq@–vG§™Ľ&ÔÓ p¬ţ*Ś«ôń7ŽsŞľ <ĽýjÄć*OÇ@ę­Cz]0vÁŻßî\6cč·GďP Çň91"¬vľ˙yć*0´P^–˛fî’dĄĐ»×ĂCLi*ëŢÝčQń§ďşLéCa‚(†«ÉoĂ1hź.řx•­ZÚöWXDÔK7˝ŮŦ‰ŤÚ:‚ ĚĐá85Ë Ňşe éíaâh6węS3:+ântpGyhČÖŐ`Zh%´Ô٨Ş:ő$ü ţÝőÝű—ĄEV  Ďë6B €^şe÷Ę˙*©15·|xëh:ç=odŹÚşÔˇNdn“yg˙„ąŰ”Ú3QlBěŐ­ĽX’őHé°pŞű Ë߶΋ÉđőÝŕÚ?5ć4Ć4€úĺŤáŻ?o:yíkĽ·űú•Ăż_ͦhJ(YÔ€ĽF¦¬Í™:®R 4#ŔĆÖëµ—·+łoľűŐ_ÍľÁńa‹JjĘě¬l>}iáĹ$ĺ—>®gę%ŕ3`Â÷żUr˙Ż•ŰßS7­óŤO§řşâĽ:[3‹O_Üu1IůUg×µ”9ĄŘľzQĎ•ďśúý3ďóäjN­V*Ő,@ENAé0OO@Í‘_‘šUë˘áŚźľŞÎčꪻƩW¨ä …LĄâsRŻä&ŕ`ě:·6ŇgjŚFhNĐ ±,„¨Y5BłjaT^XI›Zuňó‚¬TŔ4€ŠăM@ËލY¤0·´·DP‡¬}ÜěS5rţŹ/: XX#‘©¤ąóBC+Ő 4*k^3wě„(GS!Ă/Ś]ç×w=ßqÝ8]+­kĹyyĚ‘s,€küÁ8®ˇ´Ül„'~Ći­SFŐŻlšöö “׾¶˙ýÝ—ĎďÝKa)PňsůôľĎz YTĹ5ž@{ÖtľŁZ÷$´—˙2_VPI›Xň’bhP©Ç…7®éqç UV nĂŠr„pňúfco­lľRŰĎëôńËź€‰Cç—vn5lř=IuŁf^?š×ĚW>˙zÁ(›Qˇqb,°ľzňóMżůĎzˇłIŐŇ÷Î:řŃťg#k•!ýo„ăÔá/`L±,ŰĆŞg€«ť;ťŞÍż˙0ĺ×÷×ú…okĄ/BžfţńŢîk—ţÚw=š®Âj˝ôů˘Á˘ńŁÇ'q “Ô?–¢˘fĄJń•SGĘ>{Ůż«?ŔŻż˙ř­’ű–my_c9Ŕ¶ü|O­P@…¬JÍňóލX€c ć”ełCý¶`gŤJMQŞÚô@ZXô¤ľJw hÓҶ»Âň§řŞ«r“2mt6jć”Đ?NGxřËçďóŰćě3$ŁBulßĆç¶ÜŤ‹_3guĐx±Šüńýż^&„¬ť:¬zÎx,ÝčánzI-5/۱ۭäŇ_Ţ^Ú;úŕĹ=pú¦zŽü鋏żßĎů¶™#€˘šF ´Š%äÁ•??ůöµŞföpWä˘hśŞiÁ@5Ćł§ S`XQŤňđŹźďŮw‰ůúąCÁ˛wT©ăóÖ%!Bç9•*~^܉}řÜ‘`Ńvťřőó†C§÷bbu3nLG°ě1ýŐ±ČKĂ6đÝ…×~x‹/˘š>ŐÓÝ‚/ţţZňĺ^ă%Ô˛GţÂhغݱW ôÔˇ “×?Ű›Q'­Žş}őąi#ÁÜóaQß{]YŠ—LZö&!$őAÄţ?Ý»uućč@Ŕţg/]{ďµí·ç=Ľčc… ďĚç Q-ˇą”,˛ >uţЮޗ˝ůą¶Ż›—7ĚÍěŹ!nĚ\óŽ®÷›—gŹëf& Ľň{LYěAíó­ň‡ďÂgř¬ŇµnĆ­žÜŔv닯jěŘ—_>·pĽmи[‘1ŻoYűއ¤eY#˝L€ŇÍĺ˛rrĎ«zd>s:ßa­kG[:ÔŽć óŻÎĹŐ”díŢôü©K÷VĚ5túN…´\·^Ľ¸f´kEWŤ85Ž˝CkĄ•ZŢǬÚ㏾f&]±˛Ň€ŽŔ9ë3RăříPâýËîMŮÄ30|ăë⢇f4Żf4†?ľtP}i˘zMľÍk‚´43ÄÇŔyË ŻđÚűÇW_­›ę5xZ !™QŢýđŁ?~ý©Og+źÇó@řDyK·|ť–ýÖ«Żźąő•Ěę}¦®ĺ=!P ôž±U7ß1íŢ?ľ öţˇ˝źěů“m(^>}QR®xŰśA 8™PúŰóóG„Ż"m{ --~šŃx˙§UKŇ‘V§ź"ZĹ{™Áa/ΔŐşe{u_\IÔŻo®X¶uÖŔŽóĐ ­[6¶‹©Çč˛:µĆFq˛MË&Áăű˘-ÉßđĂ˙“4DĎ>(ŕXV­Vł,˙GͲ_˘~Ě’& ą¤ŞŞşňČ©c±ÉÉĺŮÉ—.E$'&Tůř #ÓoŰwätttta^ą™“ó‘Ł\…˛7^Ţ|&*cĚ)«ďÝKf)$ŕ”Gń™i‰—ŻÇ¸őé;´oPjěÍĂGĎ–UÖu¸ĽěňĹsqÉY-ö©cÄŐ–_ż)1×Ośąš÷(îNbÉřĐQ%‰7˙:x8âţý’Ę:Â6ËË;®©ĹcÝa]ЦG¦?÷޶™Ţë–®,Ş%-ß…B@aJL\jn˙AC]-¨^ßůýˇË ¦‡+G¸üĚ”b™ _€÷ç»·ýrţ!Ô×VŐkçĹéÓ7ëőňĄ!AMyúť¤˘I“B+ âÁĚ1óÁ­{7“˛«‡P’vűŔˇăQŃ1ą™‰—®Eóó"%ćƱ—ËJŇ2%Æ·R'öĆ‘cçĘËŞÍ‡Ś Ń̸Č,`U˛ĘŠňgQÇ: /˘ ár%Ş—^1öĚŮĺuDZˇ˙ÔÝÔbč>tÁgχď\ż6­¬–2ôŽ cL8®kߡ#śöţ~„í`DT„ĽüüÄىg/\-­–d%&<ĘxtíVlđč1|ď1ąbsS󜬄‰éµŇꢢ¸‡qůE…ŽľÝFŽ]yf݆íéĺőŕjç•pk˙‘k @­R8tî2jäčÂČ3ë6nĎËMyúújúŞ©Îx›’WÜě…ť7v>8÷GVYť‰Č,;SÓ{qqaÜŘüb1!Ŕ˘RÉÓbbâÓH›ü!„`UyŹî$•LĐ̸#w#"J%ÔP­S›X¨kJďDĉ윆Ťĺ"”˝ůňćł1ÂőRÍ\ľý0ËŚ!É‘Ń2Ž gTç;ŞuíiKÇÚA@yĺü9…Ŕz̸áŐY±‡ŹŹľ{_‚-ÇŹ‘~ăؾ碣b J*Űł˘‘úcďĐZihĄ>}úF=G8uV ™fnŘ–_#3™ćd%6Ó1šÂ9‰qEuČŐś}÷í·Wáf‰SáÔŞ‚¬ô¨řƬHę†Ç•Í0@¸Ü¤môĹÉ•¦vöCG…¸ eoľĽůLt%°<|0[ɯŠŞâÄ”Ěňü‚řĚEÉęŞ3R2ŇŻ<ž„@CC©µUPŻ^Ęň‡ŻĽ°ĺřÍP«äĽň 9 !nöŽŤó#¶¦čú­ŘŔ1c˝m©·^ÚľďÔ]V]qçvIe44Ô'ĆÜKÉ+*«(/®¨hĂinˤFOç•>ĆGH–¤#+lŁnp„đ6**>Ť°jŤ÷ŕQG@(0 uĽeSŞĺé 1Ń é˛úŞ{w:ÎCC¶îptěüěÄ;‰E'†VÄącZĚŐŘ4ÄÉ+ĘĂ5hIţžţ4Äů˙%Psü­’6Ú×Ýhí@ŢÔä:ŰÂиZ˙—®źuZ޵Ô­¤LhšŕąMJ;>¦VŔOÎÖ,¬>ĆOřL-?®Ą\i“˙íUošľ¦ŮW”ÇiöoĐĆ÷6tňŇUóǢ#ä9­I¶ âDłľ˙ŹZ}®qRQ¦÷˛‹_ß4ôbF·ĆŤ–-µď’7͡w×éŇŤéĘ4%‹ĺ˙×%ĚĚ^´¤_wĐĆ)ÖĎĐđâ9“Ű`Íż<žúÜ+ń7Źvě[Í?¦YWĄ}mzLbšpŁM5ÓdInź?‡°˘|ËOSç;@vűě¶tL Ąl·Î㏽m¶´n‘ÚgÖ㙿Śpµsíqn•” á?j¸= đo ]KŇö Űbtčođ˙qźjĹ‹3PŇ®Ťj·!Ôši‚^·˙MÓ#„1Ă»ÂϤ˘řźŻaŞIo¦ůďyS|°cŠ˘5%ÂíşěŇäđ¤´5nţĆŻ ş:-J­#ĎëŽ1­Ë†ÔćR>±v@-ÇÎw‡>ý’^SM3 CiSOv<ăŇx ŤĂy ŔFaL1 Ť±ľ«Źt#E놏1‚ĆÝQc>•5BH« Í8OÔLQ¦wm˛:~“×Í-äŢ ?›üˇć•)ŠćŰiM@aša(Śu  ?/šo›> @ ßüě‹i#€ahͧ{ÇZpO%Ń( uMQµśq|źi5ůŠÎŰÓĆ3r˙Aý;ňjă©Öéç3»Ť]´ń¡=LŰU›=¨5 /î˝cL1ÚDkZé ~—ȧV×ŃÜT+ ÔiŮW»Ü0Ô»ö'Šnľ0·9č3饆chŠÔRÇšĎehöĚŚÉ[ź1m„NçQ›ú ÚTߏ©ó°«íj]µĄ#ÚŰÜB‚ő˘CV´:0RC©OéifkŢČ(†ˇ[[˛ů~›±ăoK§­ľZŃL}íE¨q}gZ÷@ÚĄ˙…¦(˝ôÍš9˘W­ŮtŔÂi׋ƉMSké€ňß;ľížßí…ú6Ş۱l˙ڇ-˝‹–6Š˘¨nIŚř˙ĽµŇ3ĘҨ€Ćč Ś)L „†ˇ5/†{ßŕukW:0˘Ő=JŁť-ę ígš¦y Ň‘–y5§(šŃ3c@ş÷ÓŐhnxbôu”/ˇ¬˝{¬YµÜEHóVPŹ ­;mżlőšžťí€a4MéFˇg”EŃT+9›x˙ŇÍ«óôŮsĆŤ$ ±®PäčÜ5ŔŻUŻůżĂ0mďE›É˘™ţčŃŚšÓ¬ë—»@@3ŤźZrUűu‹Ň)L3>cŠjf§xźÁŔđůóf÷đńŕéi)]łŤ}iĆŢĽ¤%=˘†Á(ĆqÝÖ]ŻĽĽ{ÁÔŃš­-Â`ď×»ĄÎ3Lsť§u:ĎŹ‹¦[ŰăńGνş÷™1kö¸1C„چĐ!tŇô™aăEľf„ĂĆL0.D$x¦í2Ą·€/ĆyA€iZ$ü×Ců׹ŃäŹúřźŃ#ţ÷đ¸ŕIˇ]KŇr…5Â'ţņîe­q»Ä@ÓÍż»µřÉ_7wvůňĺQ>&€âďiůŤ]šžÓË”‚–——ŞÓ,U3î`ËOŕ­sÓ3<ü˝·žáj*ňz›Ńú›žmăÉŁL{ĺVÔnžŃt‰fQóoíĘFĎ_WUם–V)©˝ţçgÎ;=˙ćWĄâĽÄ[¦<|ÇŐBíéŹ& hÔcsŐpË`ăŰßT×Éró ‹Sîő43Ö˝ĺ ôŢ6=|Âo··żů!­¨&ăć~! Šç˙ăę|ŰâŰô]”_Q›“•UY]{ăđ.ŕâňnRyI~~QYĘ˝óf˘÷ľ;-©•ťÚóľEKńü«–Ä˙c0°0Â#žemÔł řëXú äď•4kšO kďŮmŃ’ĄŁő¬./,—ÔŮvę2vÔ.A}fO› “”•Uu č;sÉ‚đqĂ5'ßeuQ…¤>°÷ŘaÝícî]»÷ IĆ‚ż“'Žääě5eęŘŞĽ4qM®ÎÝI ,"@ś<ş-YşlôAvf85+߯[ßY­·|÷AR €€3"lÖüYÓşy9ćfÔ+ôó"Bgß°±CK3ŇäŔ„ÍÇue•§Éc‡yřΙ3Ő¤ľ2»DŚpc;ŢÎńÉžA‡fdWŚ™f^_•URúܨÔQ‡‹Â#/M/™8{QŹN‚G™EB‡ÎK–Żś2vh]Uqqą`ĆaĘôđ.Îf™9…šođ€‘‰IzÔ•ő+םITľúĆ é·˙xČ}ńŇŇě„$×N}÷c™Šăł"?"k7żĐˇ]‚úĚž6ľ®Ş¤¤\B{ď%ËWé÷޲¤‘ž9Ó¬EFfęŐł§Ź‘=jżůýA@8. xĚđÇĐůnrIqeŤŚ8†…—&ë«Z}őđ¦­/Ť¬ůđý7.˙ ţă?Ů4=,ĐkŰžkŻđniüĹQźcßíÜ8Ş×ˇDöő·vgGźŚË(Ą)Ü$<ëżcIţ+W,#:ٶa„Füç č+6 ęę,“«"Ś@Tśů ‚ŘűąÉ•*(!„š3Łżřń.V7źúŠr 8|ö\WQu©Ú~óúĺ#ö˙Áű˙"ÎJ¨R=żnɨ~CťG­ť7V­R™ľtnx/ďfJ^eżaŰ·L7‘•D:.V*§®ző•™] %`u`ĺ8ĉ›ţÂ7˝GLŰľuŞI}ńÝżNV)ĺŘŇççĂG»*s« gŤ:yir×౫ç¶ÖrńÝżNT)„Ŕóďď}k}XTdŚoŕzkĹG?śˇ(ŠO€1űjżţˇ?~»}ĚąSŐ\ço÷ţńنЪLçG•ĄÇ×bëW/ 9řvNíŽ~}íä¨Čżž›EŃŃĚ*[Źî»ßąŽ˛öY·xâ ŕ…÷±s|…’RµýƵK'ŚšTĘ h$o-Ůý寯-Ý0yŘx9sĆ—gI`ĺŇyËgθźmŢeĐO˙¨ąµďҵű @5;á“s§DÝNŤˇ@Iiv]ťśZBŢ…ţý.Ś^şőŹ«8Ň®e3¦^Iȧ0fuÉĹpú˛č±Á’®ů䯴ă/ůĐ•:ý!=BöîŮŇ Í»×-ÔŰ9(¤QŁ(Ĺ›i—S«}Źž9ĺ+ůŢWΞyńAVĎáă|űAi~zť Ďź1ějźI¬O˙˝˙¨ľi€ĎÇÁ¨ĂĄĹéÂnŹÝúç&?T€^%„)ÂŞÝ{˙íĐOUŮ U łÍk–N5änµĂ±ł'}¨Ę, ¬Z¶`qX赋cgOń%é™>řî)P"öü}†…mß6ĂDÖ1ťG oçŢMË›ř lU@•y)׊íşuëíYQ’_U#‘Pâ˛J©ŻwW1×E^]š™]Ň{NUVÄÉjďgó9NŢłK/8÷CĎC€ĐłVmŕë(S¨ź %ůAž™ý•Ď>ŚÚb„Fńź3‘ť««‹T¦ŔÓ†˛ ™ŇĆŐÍU&S (á‘©|¤ß.źbijř˛>.ęAÝz%+şfäÄ-ź>᫸j"Ż~cÝ”ßo4Dĺ&oZ>cĹ» JČ=űŃ»«ÇžI–QaŠúĺłM÷ŇĎ}±‘÷Čiűüć!c-oLrŘ6wľůýłç˘Ňžű|L]űzţşkë _^˛ł6Ł)úĚźď]‹J|pń3-ľOľkę9fËÚ™_nžąë—sÖ^]l©h/«Uőuő,®««W¨YÄr\ťä•=[“¤±e,ZЎ‘JÉ9~üÉ ™×N_NL§ hÜx{¨ĹS˙ݸv¬śueÓ'Ť>1¦8ŽyŚÖÉÂηT…-ÜŮÓQ9Đ_Ł?ËÂÇ—T%khhŤć¬ěËŁĆ÷ůâŔC¦DźűäÝŐˇg’ë`ć†mcz:ż»ńm WWÍĽ¸ţ5«®-ß>kňńŘL÷ÎťU¸ş262YfĎT ÎŐ7xl÷GIµěás—O˛ďB2Ea}…aU,W_Ékx\aꌱ!ć5]Cűvz{ÝŰqbřřÇ˝kç„™gÚŽíÓéťçŢŽ«0LO'o…@ü‘pSÔľ/7Gg&u\çůÓ\ĘĘŠ¸¨ÖĆcŽc»Ť˙×÷źtńrŠż|ŕzr]s#6GúőŮ«R0­Oży)"mć`ÓŠĘŞŤŻĽŃI–p5¶A$hrµ—ßt™ í]Ý\¤2ĺ´$Fa„Fa„ ŔÝ»ĄľŘFˇRĂ0ĄEYU¬eUQ‚˛Ă%-¨.ÍĐ˙"Ŕ§Mµى‹3ň*Řśč”{ˇą Cj«J˘  >SÜĄł¨&čb/ń‡dL(L!DŃ4KSP)®­QÖČDp" šë¦¦0ćoËÔĄŢţ~ďĺŐŻţµhgí…߾ݸë: [icZ PvîĺĂČĘnÜşőŐ,oÝ„OíĂź«Ł¦NRW¬ĄBÍ"•SOývŞ”ŞýLjÄĹ |•J`îl)´Öq#*%ßAd f¦=‚Ő•$§—€«§_­XCęęh–WT]ÝĐŔÖĺĚ ]©ˇ‰PŃ XůňdeMfô „ őJŽĎśFĎĄÇŇyžÂ6ĆޢfĄJń•SGĘżzŮÇ«ËtVÖç»rON”*U??·)…6f–źľ¸ëb’ňK×ÓµbĐľĽĐ~Şa–o}yŮ?IÝ“´$Fa„Fa„#D"ˇ˙#ľDô8%BŁß&ŠĘ%`BľDuę÷Ż?˙ĺW_6ŢÇŇcčś*9‰ąşďÓ=ż©âůcşmć‘Z–yĺó·^äďŕ´ů…W~=~©ľ¶ęŔWźÍť5ýÓ‘{·®\µý·+żľĽ~űž‚řÓ&”ősŰ_ůíÄĄúÚĘ}_~ůÜÂń¶AănEĆĽľeí{?Ş-Íík”ik-Wîűâ‹-+§ľ<ű ¶4k÷¦­ÇĎßŮşz*čĺTŇÔň‘#i8ýçOßďżFŮ8wűĄňÚŇž"&°¬ž{mCěą_S’µ{ÓÖ“—;ÔCŻNyٵr4í4¤XĘńÜ`ĄĄSěw/©–Ż›ŕ/rďWˇ G>Űćě3$ŁBulßĆç¶ÜŽŽŰĽr XőšYOHŃ˝B€fq­(š€™«ß!„¤>Ř˙ǡ[Wf‡ö€·żü%61ŁNZuóň†Yă4ăž…qmZ>IźĎÚ›¶ć_ť‹ăű:qáކĹ!Bç!ŵŤ-‡ůŰĂ üEk4—ÖłŻm ĆÜ›—űďĽŮĎÇʦóȬ E¤¶÷­«§Ŕ¬­o‰‹šŃÚP`fá­ńa t§ţńµ÷íýřŰ}DU˝d| hs;ęé>vQ…Śă5\­’Ěâjî1<[¬éýNlüşů#L\fW*Ű !ÄK6táÎÚ:Éą“‡7-Cő¦^ýíxÇuţµ!`Ńczkăâ7TK·|ť–ýÖ«ŻźąőČŠÇuw·FNČÁźľřřűý„(źź:ĚĽUÝ9¸oßŃűŐEI=Ü͡EZ·Ă’a„Fa„Ftp,«V«Y–˙ŁfYŽ/Q?fI“† Á¤™×"“Ťéd®~iÇs'Łł=Mŕ{ë~_˝Ľu1«¬ľw/™Ą@H'<¸_\I†Ť®gź˝pUej®’”Ü‹ŠJM|–_ź“ü09++31.©pĐȬ8űě…«j uMéť8‘ťÓ°QŁ\„˛7^Ţ|&*c̵ÝrMydĚĂ‹Ϋë1ă†×dĹí˙óhi•tÇtAꚼ„,ÉđáKŇn˙uřdDDDA©¤¦47"2şžS××UDÜąSPZuĺüy9c5fÜđšěŘýűŹ–T×U—hęÔI+˘îßĎËŚż™4pxH'sőKŰ×żź…Şľ¶,666/;íÖ,+3ćöéŁÇ®Dyöď?¤O`jěÍĂGĎ–UÖu¸ĽěňĹsqÉYÍŢ^#B@ 0ÉÍJ|^+­..*Ś‹‹É/®ôęę_‘•pöÂŐҚꌱ)yĹŤŻżźq•WÖ‚ţq(„”WÎźSxYÄüuřta^˛®ĺÝŰםĚYáĂĚŞÖiß˝uł°TŇ(wNyź™–xůzŚ[źľCűĄĆŢ<|ôL™¸†S©ň3Ó˘bâuWWşˇ˛˘Ü źŚ[StýVlŕ±>6ô[»·˙~ú>€~´Lçҵ_Ř ?^Ăż|ióŻ”5yŻFwęŰwhß ”ëÇO_)Ě}téZtŰôđÇó˛’ŁŇóŰ$ýÁĂ´ŠÁ!!¬8«Ă:O%>Ś*ŞŰ .oe\„@CC©µUPŻ^Ęň‡/ďŘ|&2§"+!&5§˙ ˇ®ÔűŻżřăń뜲úÚŤčŕ1cípÉöµĎE¤#Śugyü+–Ä#Ś0Â#Ś0˘Ł@Íń·J 6ÜřwţMvđ´ UĄŮAfÚ,4aŚů¬5z´üťA4K͵y;0ú^C?ńzSŞ(ý¤Â­4˧nš¦iËş¤ăX“SR“A˙Ł`˘­<¬OsŁoĚ4ßb\şQčŐ14.Śu#2Ř”ŽutŔŹťč¦c­6'¨)ßhoŞá€ Ž˝ă]>yŹ˙H+Ă40ÎćąZyđß±$Fa„Fa„mă_Ox‡ůLđQs‡ŔÄT żvőşTM(šŇä«0t~óy×) ńţoă˙0EaŽţw~S„°M_ëÚÔú˙!€š>…8Bř !˘Ú=ËOÓ4!€išâďâkJôĆ€ůc=14ĹŞYBĹ0ÇĂhŻ»Sp Že9B0ESć8Ž'ŚBQ4BMZn‘—BBÁMÓš»44Ă „J;TŠ˘P+Gů˝ĆMS|„á8BE3€‚1Ö<«q:±¦2GÂ!zR¦4ďď5Ěä`„ßĂđD€˘hŚ€Ó'„0h’ę`mC-ëh¦‚Zʢ…Ü iÁUMćcN˙ŇB4ÍđZŃŞ>c „(Š"¤éWÄk˘á„ă@K™v hšÖh&iÔ^ŠÂq4Eŕuéľś4ŐůöÇ…1Ĺ aŠÂšv(Š !š˘8ťřÓq?;YeŚ0Â#Ś0Â#ŕ ĽÖýŰk\9şk@wKS†?vBŃŚ˘¶¬Dm>ŞĎ™ß÷« MšÓÜ˙ć? đ™úZ4פLQ¬˘6)9ťLX–PE8ŽĐf˙@„ăXŽă·[ŔÇ·eY¶y_„ăXŠq\˝a…˝ĘNŚ"„¸yuĐż_˝8˙ú­űJ5‡#2"ÄśR\ż~C®äřj&ŽÎö–Y)éj˘ąe-rtö´·ČJÉP,˛qęÔŮÓŤ¨U€0F¤4/» BŇÉŰ·ż>őů×oG¶ŇĆÚČo†9Ć;č8Ž%(Š ,ËąyuĐżŻ®eŚ€#ŘŔĐŢ>.I‘÷˛ó5ŮU tň˛‘Ió ¶iţ‚}ş©* J+ŔÎŮÍÝŃ:>! ¬GŹc Ň«—®I*{'WOwÔ9YÉĺUr>Á-°7ƤW.]­V¨˙ű”%Fa„Fa„Fü˙Š˘†Ö‚ˇ)Š˘čĆ‚'UÂ04EˇFwmę96ł¤0/7ż  //·°<ńćÁ9 ¶–•ćö5§AsJ­ů‘¦g– ´“ťWśő੿©¶…V`ŕ—¦}ią˝ýÍiE5é7÷  y“EŔčůëŞę˛ÓŇ*%µ×ţüĚ•ó÷öś’T‰‹JŠNďůŔhç­o~•Q*ÎKĽ`&ŠvŢúĆ—šSAł¦)Ŕśç>®¨©ĘÉĘLOKo×˝¸hlżńókĘÖú:µç}‹Cm1cMífŁřÔ•ŘôηŐu˛ÜüÂâ”{=Í~'Ü©űČä҆¨cß0µÇ`î¦/kę$™™9ĄiŻ<7 »|}đNeeiI…řţ±o:QJşJ34ő7?c6j¸Fa„Fa„˙W€čNť;;wň~ëŹ[©7xąyş8šöž´üQbÜҰ™Űw˝8®w_Qhď˝|ĂŽ×^zˇOwohéí6mÇÓŐÝËĂŢÔĹgjřk 1a3fř»Ű"{ŻÉSĂĆLľëĄťˇ˝»€‰›ß¤)“ů’±ÁÝ öĹж|v>ůęŻ@ó ĆĐ­˙°UKf›ôš¸M¦lX8ÄÝaČ|%Q-ŕ¶ü BČŇÎŕ1.*:öäáůq=ĚE`q?ú_bĘ@óŔ[Bk[˙Ŕ ‡ék¶×HĘGzš»÷°vŮÜ6úZ–ťÂ§O ›9˙ĺÝ;z·ŕĆ˝úNś>šżÎ3fúܱ}»ř÷ę·jicË‹†yXö“Ȥ;ç€î˝úŘ™@đő±›Š††;ÇżnÜi„XóúoĺYw1˛ľ–YńóîąýÇ/P«ĺS{ş»NqÜúńĂW}Q”}·“‹Óö÷Uݤ6˝&ŻćÔ˛©=Ý]Ç×*Ő/- úqŇŚ0Â#Ś0Â#Ś0˘ĐôŚuu–ÉUF *Î|PAěýÜäJ‚'TB-4gFńăˇ&3ş47KÍ‚¤ZI\ĺEEy*{5kĺćżűý—ë(kźu‹' Ž{;{‡ŞĚ’ŔĘĄó–Íz%!_s¸źż9Đ´ťââ@Đ”Ů?}»}ĚąSŐ\ço÷ţńنЪLçG•ĄÇ×bë]ë–ŚęŮ­ŞŰ?ŹüR‘_‹­_\łČP_Ó®$ä%ňgŐ@Kżć(Ç@JÔíÔŠ””fË”r3¬ßબ“ÔŢĎćs¤ˇOĐĐ_ŻýÖżßĹŃK·ţ1`ÇŞ !ýüŕÇ5%¤1÷ ¦e ¨–dÔT±ÖŹž‘}ěv^ Ń?$Çp­ô  p÷ó­Ą=|¤2=ˇRa¶yőâđqc洛j9ĆďË8Ú!ŕ÷cďÚŚ ĽŞvćčwoĚzůHdFr<«iYŐ ÇŁ—§ »M_§˘˛ˇł ĐäżÔ^A3ˇ˝«›‹T¦ü/tŐ#Ś0Â#Ś0Âg4wďţ•úb…J Ă”eU±–UE Ę'ZBŃ‚ęŇ ¶ÚŔM´oĹ)š‘V–$Ä•k©P(i[o'wźÚĘŠ!sWO *ł’SŠK€Ä0 áX5Ël‡ @ř”<üçŠaę$eńq%ŔZĘUjˇą–`]I+}3* źć0ŽŹ+EÇ©YVĄÓŹ~9ćŻóś€aś»N]ďX°xöśßžiďiđŃş4§ŘP‹ Ú–5ă°gĎ4Ud˙yú°JKZíkŐ÷g…Ó #­,N.€řL±źŹpŽqj–ĺÂ@~x˙ýóçô`2g˝ó …Ô Ą^Ër S3YiĘšąK’•Ś˙ŔĚľţŁ„ýGúŮ*MÜ»Źęăaćd7iô ×á7/Q)‘b ŚČ\’}ű×ç/\H5q¤@śy©€RpŚ™Â0BŚÔzFÇ1BT/V)TJˇ…ơ…©q=!ŔŢ˝YZdĄP±˙‰®a„Fa„Fńl‚ —nŮ˝2ÄżJ*Ç@LÍ-Ţ:šÎyĎŮŁ¶®=±Ndn“yg˙„ąŰ” ?Yó–eŐ¬ÖŤ$śZÍ@G'ŻWÔĘ”µąóBÇTŞf|°_ďţˇ?}ţnÉý˶ĽŻ„iÖŽŠ†šÓ´…€B,Ë!jVŤÂËqDżÄp_8p„Ô+9MhbL€ő0qŻ®w„!»>ýfÁ(›Đ¸”b(’”ÚYŮ|úŇ‹IĘ/}\O׊yŞÔjN©RŞ8´9(UŤ%#Äk ^Ë€9–l»pîÔűǿ˨f1E±,ŰF__ű¸ţ%ˉ­ą™Ť»«ůĂ ©«˝‰8V >&üřń[|Ëj`ő‹/vâ*Šä[vŻ[µëK5f-WÔWŞY਩TjZ$’Tĺ&fv]ą~­­­X:®5fîÚ?˝Aó”ůřŰpýαٯ­s!řŕGß@U~˘őĚńn4dqν:;\üą˛^ĺCX…‚Ź ĐŢžn§2ň]lÝć»ŇĹąwv9+ŚA—fůÖ——Ťđ“Ôý'şj„Fa„FaÄ3 F ‰DB!˙G$0|‰čI—L‹Îüĺ}±eqÇřźĂ7ŐUő0ĺ dת1§9•ŞČcű6>·ĺvtÜ–US ď´u„śŰű…Ńk‡‘ÇIĂé?ú~˙5BČĆąCÜG,•×–ň-WČÉöĹĂ܆/Ń•ěkÓňI<ˇˇ wÖÖIÎť<´uEOŐônJĚZó!$őAÄţ?Ý»}uVHXúÄVŢ9¸oßŃű5ĹÉ=ÝLo~ńslbFť´:ęÖĺ 3CŕÍŻ~ŤăKn^Ţ4o2ôÖĹ@ď[ Q-áÍS2su[}Ő–¦v±źó+HĚŐ}źîůMĄ/ €ľÓÖň-›`€áó^%„Ý8Ćżëđ5„aý&Í™’¦myć¨î`”T\˙ĐŢŹżÝGTŐKĆ€ą ŻüS{hăaş J«"¤6k„·8wťY®¸{ŕçźößŕŞóúŰYĚţgB¤'Źź*©®‹żř‡ €Uç¬r%_GY‘3Îßš%ýOuŐ#Ś0Â#Ś0Âgp,«V«Y–˙ŁfYŽ/Q˙ %‰ j•"őadtBP«UâÂě{Q1őśşNZu?273ńҵh·>}‡ö JŤ˝yřčٲĘVĄ(--˝xáÜä,Ң¤®ÉMČ’ >°$íö_‡OFDD”JjJs#"Łë9u˝´2âöÍüŇj]Ióľújú*ݬĹ2“ŁŇóĹ 4=!.%ŹU+ËřŢeq„Óś¬Ä‰éµŇꢢ‚‡ yŮ7nĹö=Ć—l[·ánZ 0´gg_qvâŮ WK«k2âbRňKĽşúWd%ś˝pµ´¦:#.öQ^QcËÉY|bG7{Ǭ„[űŹ\S€@`’k Ż¸ŕ1clqÉŽő#łĘ=Lŕ{ë~_˝Ľő§ÓQ RĘËĘĘ.^8˙ )“Ü;čţŮ_ľ>zKś÷ © ’B$-+7;7Ą±ĺÄÄĽ¬äë·b»ŹăcCżµ{űď§ď 5Ë•JžžźMsĘó2eëKË*ÄwŻž9v=ŽTW™}ýVBpč({Qőε뮤çc˘.ČĎ.//»tčם»ß-e9…$çú­řŕĐQ&5;×®;ó0 aĚ5M®úźëęŽ6‚ta„Fa„Fü߆žKJ3Ś.{cWˇ'Qň÷ ü[Źu´"¦išÂm÷őŘ44­ßZ«Of<úYn(‚Ă7T•f™Q ÉůÓNľšŽ‚§ˇC˛DMţŇ"wó"]5Í‚ŁűÇăkŻŘ ĂĐ4…Ú¦(†¦˙‘˛âMÓř‰5řomkžć~éź÷…¦iú™Űá!DŃ4MÓ#^î4MS7÷‘˙sŕMŔł©˝ĎžŽEzF-É„Žsăß["ŰŁáÉ®§íŹë˙Ż•Ń`«”ˇ•čďiţł˙^µůrËqÜrŤS4…ß7B¦)ţ=MÓ„BQ ĚD"!­Vł€0˘0…1n‹¬±Ŕ4&Á˘i!-[Âq!şľ0¦0B„Š˘ŇohšćŁ.#„i†ĆHÓßĆcLQX× „4!•t•(ŠBÚXgcš¦1…ů’f-óŤSO6ę !"Ma–ĺ0…9ŽC`b*_»z]Ş&”fČЬeŠ˘) qG(š¦â}˛–Ł@!Š˘áš°šBÍľĄ4Ń-š¦pc}nđÍjG6Ô¦(H  JĄR@‡B+w §A;ő!÷äżŇ´ßqÇ  h «˙ŤďHO3„ő“éë_‘Ô?áőŚhµŽofˇ…Ö<“ÚűLáéY$Ł,ôŃ1nqiçá mBzŢ3âąČiÔ}ŕÝDŰ/[ą“ä‹ë( kűmŢr3W›˙§S„pC¦­:řÍ«—öďŻRqTśwéâU©š€žhŇ2‚´—á1B|¨Ĺ(´‰sš‹vl iş‹Đ=®ĺ-i¶Ó@Žcb-˙‡ëi3mY>oş•aS§YK‡Ź¦¨–¸t ¨)Éłrńź·bqxh“•±…âfDجůzöǡ ;6#וU ś&ŹćŃú*¬¨.×Č4¶Î'hĹśi˘4wŘĹ´Ť­9=ž…ąŇúškçôü„ă‚Ç ďnßČgB=™ÇńžŃÉâŕ78>§"'>ćaJ†˘¦|ńn`é};%ż8óáôâÜřËý†NÍ.Ż™Č×÷˝˛0'yű†çňjÔŮ©ńq©ąŐy‰CĽ,ŕ0¤łžÓóŠ ć rá §-Úý09%7;;âîÝűצôwkżŰŹňŠ3ĆĄgDzY ĆĎy1!%•Ż{˙úä>Öŕ9613˙ŇÉĂWďÄç<şßËŰšomŰžk©×§x•hĺ-~غݱW ZÔáw˝AWH$+%>úafE^ňk°ő;ů(%ćö™Ë· łFv±AŢŤ˝ç¦DőpÝ=13oÉ˙%»ż$ňę űę1ÁÓ0hΆ’‚´>ć ´·ŤćëNY_Q’ÝSŔřT7<żpËŔYu¬vą‰C»­ü/Á(ĺ(eÍa ďł‘Ź2âo_ľy?>ňŚ9 ŕŘíôýdžÂ˘ěÄ!Ţ–žŁŐ(I棇q©ąŇÂä6ÂĐ9/&jyqeę÷frç™3yĺ¶z5—•ť˙(31⍗˙ń»I©üŘ3ăCş9­ßí”üâŚřÄ4BäŰVNZűM‰˘q3™>ŚĽ<´W§µźť—WĄeeĄUV|s»ÜH)äĄ\r݇§čŮŤö†..–4d¦&D=̬.HŐŐť.D§fÄßľ|32)ú ,}ýWiA¬ ÔÁűŮ‘|Ě?ŹZWrĆŁS ‚Íďţ&“V_ż|ą¸˘dů¬ą»“dݲA`u!©ěü7[Q´ľęş žrţNLjFvr¸軋‡ň?ňZÇWµ ô0=—׍ĽÔ¨@W©GŹřܲÜÄŹ2‰˛vĺ„@ç‹1i Ţ;ei‚ coűţ?B«şęp#9——{Qz´ź-řŽ_SYšĂۨĽJé«§Ŕ´µ/´&Ż‚˛’mËĆ‚¨Óąč^ç 2ŚéáT›3Π†[yw»šW’™`Pź[Ž"!ęĐuÜr‰B3żj  pˇÍ<$äjVXĄ´bÎPďNg•Ő*xí•$‡¸ÂÓ˛´OYĘ”kŔŤä<­MPŻź=ŘeŘ"IE/Ó|Iý®•ˇ rż“ZđVÝ šúc._J©<ţÁRß“JjT˙÷dŃ\ź[ÚUiqj°‹ĹÜ—~Ş+ÔXŃ#19Wľy',üą‡)éy9z벥ďŮ(í:?:Ŕ7­56`ÇřĺĎÁoČĂěr~^Č«KWL €e[żJË+ĚO‰ęe.léÓh%îź1gDA ˙t=m¦-I©™÷.^´rWLbJ^NNÄÝ»˘®†:Žž±=1µ­•‘˙ëóď˙^_+ŃÚź1`ҨĎ-íďk9ś%U·µ OáS×lç)LNÍĽ}i˙–~©ĘŤńd µ­9=%E;V‡ńżčü„,Úô…†Ď"¸śş÷Oý„g v|[UđŔ Ó€ąŐŇÚ™ĽF-złľ:§»#€µząä˝ç—íżůÖs‹§¬|˙ăułć­}5áţ‘>”UTĚě &ÁY%âMsŔý ب7üd›´˘´4ob7Đ ¦ľ“Ó˛2&w7ă«MîŁÚĘĚîŽV]Óĵź?ż,ü§¦eeLînĘË›¦­z95|Ú‚e;K*%/®Ç?űü·Wµţ®uÄß »´˛JĽx¤'€c\aŐ§k¦Ź›»KĹÖľ¶z~ŘôůŹĘęϱz 3-|Ú‚e;K«jv,ŕs?1ăÎŤËůąéłt‡żĺě¦(·˘Ľ4oJ ÚS5žćWĺf$ €”Bńss;öťQS+áG•[±w×2Ŕ4ýÄej”ň—2ď[›v +ŻŻ˙pÓ8ěho3źű@ˇ¨â)LËOĽłÂ·XeuőĽA.üĚÝ5đvÚúŹj*5<Kekföw<·0[#ÓÔ"ńŞé‡ĎUV“ó7¬şľmůsY\S99ČÜmČü˙s˛č2Y\#]»hśgß1/lßĘŰůM[žź6˘çřĄUŐ’ec|x»şď•´uŹ"I˝ÎŠ.çM׋™W™”Іł®˘śWh­qK;ĆÓłč…ď+óăřy!–HVLěÉ˙ę?qUvz|OsűOŃÍ/{KŔ÷Ľó ő®§-´Ü}})QçÉ:­kęB ·Js(Ëct~uű¶ę]iUVkvŚ÷µśúͨ©®je6Ă4Ą9»9Řw™_ŃęŠÖ’kŻ.>ť=ř_őý~ ĺůl)€Ů?|?Á7žĐŕŕá]–S@’ Ä5%˛ŮVgäU°9qE& čîŤěŁ÷vŃ÷1IÄ‘Ë µâřŘb`-ä*VhiđŻ\Żáo_I¤ 5Ç1Bţş!ˇAaŔśĄĐV¬Ą9:%ß^hŤ "#Š#¬W·~ß˙đŁłHž™[ŚŘ9§Ő !0‚ż …0ŇĘâřčBO/óň÷ň­ń‘”‹‡Ě]=A$ä¤$ćäřtí·ç»ź]…őŤ˝s„1ë<¨®$)­˘ 0öďôŹ€˘«RŐIř;b,§,âżOÓ¦N7ŠL±źŹpÜÓ{a”ň?”2á8 K»óýŢË«_ýkŃÎÚ+űľ]ôâNŢ]¤b …•YÉ)ĹE¦–VŇĘ’„¸`-ĺ*Vha„çˇvăĐ{Ä´Í WJę0Fd.É˝˙ÓďGFTQüčb|VWŮşůŐUЇĚY=A$¨ĘJzTTádă/.Ę*€B–]U'Ł ßüŃĘTű#M‘JqmŤ˛F&˘€pśČRäPYś‘[  Î‰NÉ·Yđ:ŞßĹ0µ… ÷ň )«Âß×*ţúöÇËk_ýkŃÎÚ żíŮĽëýÚś[§#3fĚMz8Ęóśľšl‹¨/¨Ľ(ĺb|& ޤ¨˘űToACŮŤ[× .?­ZÍ‘’+Q™ŇqăÂMÁ …ń§ď€Á`‘BŚšź « GŁf– U}=mîäíSśY €“ÓĘ%µ&P|űű˝——kFńí†]Ô6¦Ç˙ čęţďíxĎÂÄľ˛8S;» D ˝QcŁ4oÂÖä…dů© Jâá×˝¦˘BOçKé@Ř$­&dƵŐSÚRR’Ýš>·ĹĄ}?,{ńÄę%eńq%ŔY**ÚŢÝŢÝŁ,=Ž_a ŵ Ť)`jË5Ú›ś]ٵ“<-KűÔ€€€µČŞ˛XĂĂJiCS|D­L €ŔJh+.zL«Îö4o.ÄźI¬ h.)Í˙ż& I˝LÍQÁĂĂ6ošP&®Ĺ„fÖy1Ç^;RŁŃL’”Uŕ뢮Ţüžž˝’ ,}îâéW«[e23«jٲű{~Ľ´¶;†8ě=˝ËŇcőć…f'i·Ą(¨K˝­›_űnÝ®÷”„ÁYOő´ (;‡ĐH«u€0…KPĄ@€ÎÁ>ŚLcę Ňĺ ÖÎŇą˘(˝u;†Šaęx+ŃrÖőޔŠ’ –»q:ŞŐ­=P_QÍšÓó´×Ąx>Ój.ÁvýCÜxv€@VZnćě‰(G3!òJ+Ě-í,˛ňrs¦ öaĚUźQsśŮśGr«ůaţ—ď<¨UbDX@s,ű/ĆFäTd䨙›o –›€0 f–¦°Xanio‰µ·«˝ł€® ăg¬ę„3şűu ]˛«VݸŐ$őJ˘íëo€e93 [Wsâęh&.Ş®®h`ërć…Ž8xpȨ!/|~rÜÜíî(5@Ű;˙ŐĐŚVl_˝čxĺé}źŮ°ÜßŮ"D «—b«ÓÄ €9íjŚFhN-bYÂqś©ąŤfö&b üT4×(ĺ*e>ÔJő+›¦ytňŘđᑹϿ<ą‡]ya[źÇS8jÄĐ]ß\ŕsN­ÔÍ\–pŤ<´ŕßÁ˝Ż.óöőďŰ·OďŢ}zt÷¶0p*– €1Čk땵š±Ź1ěőO©MLĚM­,3Ă´ćŠkdjaŞá*ÇZ%Ż®faYµ‚E sK{+ĐJ±Đâr «R1¦–î>Öśłť ˛’Ŕ«›ů±ž»eWřrŕ·_üCWľ¸&ěčŢź%`šjIá85GtłË *iËN~^ŠĘś¶`ŐŞĹăNúSNS”ÁhjjVÍę´ 5j_0núęFÍäFŐPS\dçÓÍŔĚŐĂĆĚTÍ"ĹËuŁŘ=}”'ˇţ…oŕ˙)Zčꖗ‚l$őµVÖšŮífŻF¬#†čŮ(¶MyB  ˇ¦Q3GŤ¶ă“C #žŹ®e8JefnŰş>7Ĺáą[wOďkY+m „C(ŽŇĐ +­Đ­°¦BZ­ćXµJ`¦Ó^aUĄ 4â˙wŔýă°>i ݸî`–U±”ň±­z+ď‰s.ď 8=KňEĹX›Ŕ‰O7řřvÓÚů®ăçm“ĘÔ"sËN>V»*řó·˝:+ZĹO#„ôm¸ĽF¦· ÝţÉQîŐ aî­Ů1Ś AĎó42|ƨW¨ä …LĄÜl~ wdë–ĺ?_OA_[´|ôü ]TFÔ¬¤ńqB ©ýĚ‹äć–mŰ1QłjdpÖë« …”ö˙Ľ·µ­=ˇZĂVç˙hCňň|®gŮflßO0ÄŤg.ß:-ě<ú—ďżřqĎ{Ţ.N––ô•ł§ä–˝ľűůëĎţ®ż={áęŐČ”[[댫§Ď^zdmëđ(*‚±´u´·Ł(G'3áżD"!#P–Ü˝_434 T€†ś%u&Ż|zŕ‹wŢěçcyúř1ĄU0OóîÄ™K ĎŠáë|őŢ›.™©vî=_zqűŻűż÷sqd†ď")îF§~SOź<´yé8>4ďcSÉ*ÁŇőµďľţtĎ/Ł»Z\şwáäé# 6CĎÚ·ńą­×#˘–†KLzŕŕŃ‹ďÝ×ŮžfhĘŃŃŤŞMٶ|‰°÷Â_öĽ @<î‡=DÔ,5vĆäÄK§‹j[ář_sŇUV/}÷Ó÷{>đq˛aQ)• ?ŠĐnćÇ®\LžF,Ł”˙ˇ”ůŻĎć“nGFďZ;żo€»BRQS%˝túŹ:›áçíŰřÜ–ë‘«f¬m`]]śt3×\Ä@C®†ĎźżőÚWB8ĐFŢfY–ďHhjîho đqîčoő¶š–ŻŢ‰\;řчÁ±÷·?~ľçËwĽD U Í!śľLż~çŐήδ©ĄŤ…©©ąŤ­Ą©‰ČŇÓÓöĘŮ“ –˝öü˘‘ňÉł—5}oB3ŚŁ‹Çëź}őÉ·?O˛=pâ xŹąyçÚů}̬âmÔCÇ#îF–V˛ju~fšayeĆüuätanĘĄkŃn}úí”{óđѳ啵ĐîÚÔLĂŞ)O»“T4iRhú¬?Š7^Ţ|1ľTdŢ‹Š©çÔuŇŠčČČĚ„č;I%ăÇŽ*IĽyđĐ‘ű‘eŐ ĹąéĹő‚~Ţźí޶÷LÔcYÚ˙0zÄctMFPSžŢČC3ÇĚ·"îÝLĘ®:l/Ó¨čŘĽĚřk‘IĂŞ7—écĎĺű,,Çĺg>â-É˙!Yhő™Â!„·ň„Žpy™)%ŤÜ¤ ąEú6\€SĹg¦%^ľĂϸ”ŘGŽť+/«6wt22Ä ă_bVĺ=ş“¤ó<ŹÜŤ(•PCC†«ĹŮg/\U›X¨kJďDĉ윆Ťĺ"”˝ůňćł1ÂőҲŘŘزžĐBš®MTâ¨"±´IÉĂč"±!Ô¨u(őt,ćŻĂ§ ó’őôąąűëđÉ‚RIMi[«pâĂ袊Z˘gë Ŕ©ëÚZŃšÓ·˙ĎŁĄUR=˙GX’ţŕaZĹŕVśuöÂUĄ‰9©«:{ţĚĄkŃîýú éŘA?Á7žČZůwcrm n’ŤQ¨i@\@XóźŕÇŚYŠ™Y –ô đ~Ř"ľž!š›—¶*_¦ďŔ6§™żX6mă+ wŽ6ąGŽćîrpř†Ş˛śÚ(ď­„Éo=9F­őŢ.Çř/§ÝúZ4knłžt¬/M]Ă ˝Ă7V덢1&/B”ŢFÍöç!ţ?d¨ÇÇ37™ąpqźîZ)Ă“r{ü­”Ă7Ľš|űXkŃ‚Ă7T•f™ńqô˙”Ľ”ýű Z0»})ëÓŇű1EéťkBX/ŽJ󚸉`uďf „´×uG™ŢË.~}ÓL0üŽĽÝ0»m˙»•ˇ¶`—Oŕ?QŞëVOŹËÄIluÔz}j’ ęĹţG¸ÉLnłÎÄŞű/­[ľđ“Ž•g=čî¬3 úO Цiš¦¨Ç˙şřŹŃ,=čôášĹiÄ6 ÷Şy[éj:::ÝtĆX#Ŕëł~‹í“×úcš|‘Ź9=ž2Ż{„‘އ÷[áaKą#„póoéŹŃ­ţűă–äâĆŰŐ†ÓŹoů´ŠŹÉڦĹĂçClŻ%>]%˘iş©@ş űm{=†Ô›żzú™SÂËB(`ô7lE_˙áB%]5uPn)ťög2LĎcCŻŞ__› Ç Lůü~Í;hŮe«Ć-˘č.ôXÁ ššD­Dř¸ŰŕŕôúG䊚SÚJ×Ó cŰń ;B_~‚>ŮÚ”¤÷…6ś‹ĆµGřŚĽ Ë„Š4Ż´MÓf(ŠpÂćsŚ řô±-7mDű_3®>ţE#$9áaqE 轡A€išâżmb]‚X ë޵`ŠÂŻ˝‘ęďĆEŃho?5Ą!L×É;¨›ŤÉ_'ĎqşäQşA‚ŔÄę/_˝ZǦ) ! ŰxQÍç,¦(ŠÂËĽÉą˘) Ž#ŹË1B@\\đ(C7üŁâšľőĺí;ĆMÓ,˲śQuWŻ^«góµkrß˝›0bBDČŁ„ř=)kçŇş”őë@«|¦hšnĚ9Ý´_„ áś}<EGŹťo~RW+eSüÚŐëR5ˇhŠpŃK6ßş”MÓüaVť”“+ŤRNL΀6¤ŚMQB˘4o[R"Gˇh#ž´Ć·O!˝WQşěĹšQ4Ţ’çE ÉŇMcG4v¦˝<]ܸšWŔ@ţcťĽxFa¤I‹¦0ć´<>ň›ˇ3ašO,­7×dě4ĹqśŤođ˘ĐţßżţÂwÇo"Üę>´ţÍ@„0?4¬Ń–ĆůŽp«ľ¶ţ›HmĂş šçtşASNĄdý‚zL ›dM•ĽřÜćű™b„) ŁfäţW_lŢÁí'٬ˇ^ZÎn=EéĎ·'/ŇRç›! c®‰ć7RŇh1ÚŐç3ŽOŃM8N·hňV €đ––OÓÝR{^Ęmčߥ@(˘ůÓůOˇ€¦°ú±úĺť@ŘÝ«SüÍ ©ąŤ<¤hpF{ˇZ+÷ćrA¨ŐŐSŻRK…uź´ďéôžlîŕbJű śh ˙°mYđŢmŤB_V iYłÁ5I0Çq\Ë١±ŇHcźŰ—…! ×9ą:&Üą”–[DŁušĺľI±ö ^6aĐŻďľüőák:î4éŤ3A”PŔđá42mŤÍš„1ÂďčtYŇ҉RßjxÂLQ Mł\Oőe DçČţ~żçÓÚyšaÎ(5ő. µŰ¸4¦1C!˘łlŤľ–Ć´°Íúâš,(±îÜkQč€ýżőíá+×ÚŠÖÜjĘő;8…€IDATýźF>c #Äq¦0_W+AęĆČĎ\üĐÓŐ˙fchʬł˘âäĘúl%OŞiĄe'¦.QZW¨lîź"ŘB`ŰĎęŞČZNM€¦]¦ÚI#+ë ŐFDM9cć.Dś•ĺÉ9u“Ĺ@äkĄ\¬nÜÚbř €śÍú|ŃĄđÓ´ŇH]E–f¬DY'ĺÚĽ*ô6eäęş%m'0qËßťe‰[1PŻn¨P…Ě|LTe •™yŠôiFŽÚ8vć+%şáŰ0"Ş!G®±™vŘş·5®QIb5ô]E–ć\µB[DZ†ôŹ˘]¦ýSŽB„%6ˇ.]Ř>\‘,W·µ™™¸ ™ÜęZ 3§®& #Ň# ĺRi±\ €|{X¤VT<Ş­GX4§‹gbqnr­‚Ćĺ8Z ębiÎ PŞ•™5RĄÖîđBëdk rYˇL5‡™!Ŕ\‘"„Đć¶GÇ˙ĺŢÍE !öiyr|ď~N®ĂLIĘ|ÖNUóö}pçnoą/=u©ŕď†čá‡éćŐy@˙ľőů×oG)Ő,f,|E b”˛’¤GůAüě⢼’J9:{Ú[dĄd¨ ćڱ ×®Ý+źĎžlË˝úq¶ÝĽ~˝áď¶ŁŁÇśR\żŢ’@ˇM@w_Ô`¸Ěä$‰RĽşęß˝8-úNLKÚżńkBŔ˛„ăXŠq\˝aą˝)ĘNŠ:pň*/yľÂuÜ»ĺwŽ#ÔŘ8şy:™'$¦q€˝şqUůů%dxŁđX<Ą»t·4ex0E±ŠÚ¤¤4O ń€€}}nmüë”đä´Łc<ÉŽqďyéřŢ=;–şžLaÜQ—îźS4ÇŞGĚ\÷ɦI3ÇLÎUv(0źÎÚ ĐOZž{íć}5€(Ż€^}|Ą’Âë7ď)U_ÍÄŃŮĂŢ2+5]ÍŤs§ÎnD­„1"ĄąŮâh]K™"ŕö0´·ŹKRÔ˝„¬|Ŕ”W·^}:×IŠnÜĽ§Pq€(Ď€^ý|ë$…×ů= ©TwLfY®•Ţť“"ď'dçäŻdťülŞó cżtŮŚ¸«GăłÄ ð*mĺŘÍ×S€A)«MMĎT±Ŕqlű˛hÁ m_ČÍ?Т¶"µ¸ÔĆŮÝ×Ă•Óňą$'ŁPÂőđŃŁĐ›”ĄŤŹ«}nz¦Š#’B ‘©¶wô :ůŮČ$‰ů…MG‚˘í—.o‹ UűQ@Z¬z™ „#Ç łÄ˛[—Ż•Ë•™-›upńp±Ĺ‰Éą°w`wUyaŤJĐŮÝ&!!őńl&żëcYÂďňó0h!MSÇň›ß–V]w\#ÄiKřő‚żCĹ÷ĄDZ‡0Ć€)ÂqiůŤwĽ‚›Ěť–šÜ^ť§ĘËş˙ůľĎąÚŻ˙Ď~Đi÷đţ/ôr,Đ\“AIŹvÚĹ´×ţ^C/ôrŁoŻ×˝h01|˝żűsŔ ›ÉÎCo÷tŞ×ĐëýúţĐŐĚ™ţ”ăi5čj_·s]I[âĆ.›|˙ŐŤĎ:`3ÁiŘ˝Növ«đ{žĂçµ;[¸: ßŢŕ8Çsčť~N<<üNźÎK:őü=xĐţ®Ŕ*ÄyD ź‰ÖVÆŢĐŚf°źß©˙ľ.š~(Ć}ť÷Ŕ+}énf†Ŕv’óČAOöv«_đ^€őř6)äß^™ úçă˙*ő;ß×wşMküDČduź÷ÎŤť1ľ ­‰”ܨŔ´îýR—-ž3=bvxö˛ů;»{ŔęţŇ—-Ś™7+~Ö$ €˛‰[´`•·=( Ăüş%-Y=gzâ˘y—&Ťň7…X[ąD.ś»ÔËčö¶íü#óű ‹›>ÖTodOü$źÔ˝WÄ”Í 4<Ô?\ôTˇůĆH[^ž7÷Uw»oŚ ŁçŻ«ŞkČNK«”Ô^?đ…#€ ë„äüŠüÜě²ʄ›MţŚm—ŃŮe•ëÂűo盌Rq^â­3>4ů;{NIŞÄE%E§öĽoŃ”iÚ 9Ź=P„Ŕ¬Ť–§ó÷ľ;S[S]\úŰi‹>i—sŇ Ę ňssó * 2Śî37˝U,®ÉHM—V‹7/ťÎ/!ţÄBĄ=Ŕ¦ÇC ´Číío~H+ŞIżą_Ŕ}|Ć>6ř„;K·ý—zM„@xńQîŹ;–Á?Ë桔©˙ĺŘĚ’ÂĽÜü‚‚ĽĽěĽâ¬WüM[ŔűWŔźęsÚúĆ—Ľ>w77s˙ IyÍgAăqÝż©ĎcIÇtž?p˛â˝}ůqĚ4Ń•›eü-;Sż¸ĽĘŹVM˝üQ­ˇĄµąyđ+[ß±‹ ĵ9YY•Őµ7}áJ`§çßüŠ—EĄćmř¤˘¦*'+3=-˝®®ú­ sÚ뱹LĚüßţ¦şN–›_X’ŐŔkô‚Bmď·Ž|e×”žŰGż˛>o]u˝†ćkű?uˇ ى,0¦Űę˝8ĺ^O3 ˛ůťşŹL.m>ľ‡@¦˝óĵ›gôˇ@cm,«®ËÍH/­¨Śąv,ĐĂ(ł.­Ë˘7L:‰đ}=8ýĚZ˙‰¸VÂóą^&}}ĺDpśRTĄ™·™ŇŘyŰŰßdWHň;*ť–˛E;·AOÔ±oře§™őŁL{ĺV´É wkhÓhZő>s˙“w“ĘKňó‹ĘRîťô0˝÷Ý鎯;Ľ…\¶ýÇĽÔ+f4ev5#ď“ĺsg-ý  ó¦Éßµ™­YČĚ—»™ ôyÔŃůß´žŁă|˘‡a—/_ĺcŽÓŕş(żB;s~ćÚt^t¤ÎÓö´x:ء§˛b„Ö zŮ Ťčç9ÁĚşš1¦óBŕ´ŘgÄ­`3'l`72p—OşŹŚĐű3 €,h3_S‘ă0Óuص`űnV=ô‰čm×CäľÓlú ď‰ÖÖ!ŽĂ®7ŁěfşőÝăÇ÷@űÚöŮôQ×Á§z›b0 ŇŁ'şżs‘ŔÇÜmŞ}«ň»‘iżăO€c|łn[üţŃŐ /ć6g¦NţutHěś°n-v5<ˇź­Ť›©hb·îKć1§lě=R—-ŘčëţvöÖ€˛ľůęŻ ś<ş­ßĽcç ŰÂĆ î.đ+Ą§ĐÄ cúŤťúŇîť“GőkűAÍš˝ăçüôü®ćZZŢĎ/¬„'’ŁŃť:wvîäýÖ·RođtéäÓٵý#ŰOün8-â~4ŻĎ=ĚE`ć?%53ŁŮ,x č Ži.° <#3Ë>^?:°Żx˛ŕóZ¬űđŻĚČ“ ٶ.°6őKGwÁ¶^ăÇ €ÎŁÖ+ÔěĘđ«‘…&6vţA.Ó×l—T–Ž÷w„6łţµ”)oŁĽ‡Î©Öěś?űYQ`íŢulČ@Mď*ŐʱÝôéQŞŐKGuqîÖgÝňy<Íu u+ǵ+ °lł÷î˝ú؉„Đ|ľi˛’|}즢ˇáΉď0é—š»&¬7L]°<´ź×ř%Ď•¦ő6Ą» -¨•»c)˙pk˛0 á¦ Đéőőť€±˛ď¦ĺsuUů/đź‘­Y ÜěěŔ"0üŢýč“–NkóË*(Ľ-zŽ'l:Í®Ć$0¶Ü Z·H-ő°A%ź;еçôçÔŠš‘ťLh§•rvűÂa6ýg)‰şăëNŁ…L»"B ş–‘˙éŠył—}R}ű1l&oöť}§‡Oł¦3eĆônľťÜ=\=|t˛ło'°÷ě¶fÓö¶¬ đvâźÖ·ęaŁ€‡ʉÇjJBú€ĐÁGł¦ú€©›ßÄ)“ÇLľëĄťăú@—€~Ď˝úUaAáGŰV-\´ «»ťľ0řżŘyvÓÍąRąvRţt¤ÎÓÖ°Ô„î´Â˝óg; 6=-Ů 9ö¶ú¤‹™R)ö6#˙Ć‚‘gIee\CF}C gái§Öâśó Żoz i jĘj”­M°%‘ŞJ•Ŕ) r6w µ(ůŁDEYŠş}ÔŐ}šŤÍ0[źçÜ(ˇ~GŤ"§ĽÍ"NšZ C}RmńŮ*@)–ł úß=µ˙§€°źénŰ •\¨ĄM1©Z–%“W¨,ŘÉIŞ˛XĹ5«¦ąÚřPuŮJ,@„%„ÁÍhć[ÖíIŘĽęŘE‰…·k‘–4Ą§ž`!Vfן­lBťě?ćź[Iš"ĄěMĚM›~XÓž}l¨)š|ňĚŢ‚R¦YD`=Š”JEV•¤H&ďíęU"Îż[ÇŽuu•Ö׬;ý"úpiůÎ_fa „v 8|úĚŽĄSúŤ ;qâŘž*5J$ÔÜ8&,˛ôţůđŃ­Ë§Śž4ëí·vZšđgŇPk-öTwĐĐĐç/ýőýłç/˙ĂW,„ yĐŔSÚ2ŇxăŻÔµŃW%Ť ęŇܬŇÂIµ’pňÂ’Âě¬Ň§m‘đ!\Ó/ ŘŹ×gľ!B5ťOçSHGuŚ˙ Ó­Źł©2öÁmmĽŢ§(x{ÇĚŐż‡9ÚKŞ­óą…µ©S2Ś«Ę˝~'Ł{@PpoĎŠâÜŞzä_î߯/ >Ŕ®˘ş*#91ż¤˘˙č±'.§–`®uýh)SŢFŤ8\Zś.ě6ńŘŃĂţŽPĂBuAÚőŰÚŢKň%R)W•{M[R^ś'm•¦ÄţřűžćzŢB6C YPÉÚě˝›®”+ř¤cşyAŃ#0eÍ[c}_ܲ°4G!šylÉî/OüúĄ+Ť*5-††O;t¨˘ş<3?ź§˘5Y´äG8PsD×—ą…PŐTĄëřwňJnČT&6ü,đ°7yć…Aű}ŮaéčťU@Cúů¶č±4kňY´‘Ő¸#ÜhăpWK=”Ö«hŠ®(-)Ż”úzwő÷í"Ż.ÍĚ.é=˘*ë±×N­;.^˛`ÁŇĹN–"ĄšĺŻ–h~Őł™­?á×?ôűo?ó¤čÎ_ýřŰôĐŢůĹůŮ: ™•YH»u?|öük›Î]űŇĹs'‚;Ű@ĎáătVýĂŹ^€€ÁcŹśşř×÷Ě^¸ňÝ··Űxů˙uâÔ K§ôvęÔÉ!Ţ–]8rú»O^ťµhí‘“‡{Ř˝‚BÖΫV)‡L_şeÝ2SĐó…ř‹=•y)ş™".-¨¨ç^@ŻŐ:O×—ĂĽŘëSëd…*ËńN=ľó™auzŮř •ő$đ«nÎCL€@ăýx@ ¨h0énc×ËĚnĽ˝‰Ĺ)Řâ˛ÚËg¦uňʤ’x`P®KÜ|Ö»0 'ÍWđĎ;ÍpbŞeĹç«ŰšXř *Ž=\‘ôŕą Ž·!- r˘1µ”ĺEźG•ńÝćÁÔďÉ€żËŞ˝±NX śÍ|–Řg+-ä0źŞ#B€îdé<Ŕ¤âl%§ZHĘ#jlĆ812Yuž’bŕŇ-inŞČ0ݸö#Ü‚>ż4iv)Ś&BŔӀɓ4Y Tň†ż!ť–Ü ,Ű6=*ŢĐéŰ Ś‘´®}n´ű´‰¦]ľ|7ż8ęvLŽôëłWŻź˙AUx)"ÍĚÜ´˘˛ęq× ŽU™Úzl~ţĹ·nö´5U±j 6÷k4V«ęë¤,\'•)”Út§F˘1ŔÔiKű¸¨ÇîŐ{Ŕh™C÷eá“@ɲŤV}îfP©•ĘÚňíłÂztŶü`©\ 6WZ|ĽĆeŤßË˝}—9áV^?©ë9Ž#?­Š€°„°Ř÷M?{o’řrŽ’h´‡§˙»Ë|wSK;›Ř±­Či¸ź/Ř.Ô+e·¤Ŕ`,ÂňřZiZ]ĺM)+'`Lôi^¤9ÖňĂ—Bóׂ śşU 5íćIpLăQa [K4-7Cóđ/Đënhđn×Î^&¬ôx~ ˙«ĽˇfÇŐ;/ÜĽ}«Z1ĚŢ @ĹRt€ťĹ*3ĺ Ľ*sĘşM§ĎôűóÄŮjüJ_‘őę.§“r@aŐ”ýžV<§÷đ„3^ďŮMŘ"Ü OVµ’UÂ<ť“(˙‚ÂŔr\˝R«ďń«RŞM?úĺPżzúµbuöµSÂÖoŢ4hX¸ÄÔs¸A@;vßşdÚ‘o>ĎgÓ”&?1Çé^asJ™«˙đőă»˝ĽëĄj$=ą¸Ăś˛á‰´ü4ŰŃďĐ-°j®A ˛´Äš™©ńŠ˘¦ÉyM …8\<»ÔŠĹCć®~iű†Ę¬ä”âbMşA ńßdYÚťď÷ţ?öŢ;>Ş*ýźsď”Lz#˝R!„@čUŠ(Uş "Ü\ËZvŐ]ëş»®k[W?bAEPé EzB !=¤÷I&™Lą÷žßw&™$“DÜßëő}^Ż]3‡{Îóś§ťţ<űW?·µşŞäŤ?¬WK–¬Ď˝´ÜY˘TŞë*sö^,PyµÄ$°>č±üd„‰ĄĺD‰Q9-!ŠţpŮ)é ÖpFVMľ%Ů~» LÖgy ×VppńwtXÁĚéŁa§oô«c2‘M:Ł I Ő-˝ŃaË2Ž'˘ŮÜÚÔË(ÓŁŠ­·Y¸j}µJHΑŻÇ$%Ě^ü_ň”5 ÇA4Ă* yś˘„}×"Ť±řË]‡¬˙:»ř(ĄĘÁP›óŔŇŐ®Yu0W;eŘX9ÇľéŔľzÁ8ůÇ·w”Üłhjb¬ŘÓË@&żšwbFfţ'Ť>+ĽSUčöB†€ąĚȧ Ňt?äń0źţüý9şR3ácP'şŰR(´[Ë˙»|ťÂ8â¬öžŕ ĽďLŹ–ĂŤím Â$0łůňýY%;®qNc‚ÔIłŹ˝Dv#"“Ě–U(c]éˇ`R˙‚ăŘ pLd4ţj‚^kľ+ŻgĆL’%˛š Ü:÷öŽNďćJڍ†e–”$Ť‚Id–4Ëff±N ‘ţtüŘîVĹ–I© Ŕ(A$c€ˇ´­ÝÇÉ ŔěđˇÁ¤őżEĄ2ͦWOůĹ7O_,ť—8Çßö^ř]ki5÷sq“­üVZÂ˙>ČěprˇĚTdpCve[ęoď¬ě>eĘŚŚüJ “‡%f™ÄäCEËŁ%˙Úú Xe™Éd6ɱaŞšk=]Ý·ĽńÔŢ˦ááţµ- ČÍĹŚ’oŮUßtË·ľů‡d2ô×yˇŕ˘»OĚô´H0‘rĽB©–6{ď±sźĽů„ĺ„v”P0´´™Z®.›1itzúä‰ă7żůµ$‡:k3I„I“(!Úg7Ě  ~äµmK{zŃÔpé˝·lMćÍ$Iě]ĺ›čˇ˝ÓĂC`›ĽŠ’¤â©@ ,mÖžŢqŮ)±·•,˙GEA´ěJZßä>ô&_YźÍ–e!±µ‚[ć~ŞcňfbÁU3q LŔ­Ś ‹‡0CCٍÍk0Â2Ńę§JwoCáx&:]ĂťßÔůČ(ůcÁF˘ ‚z¬\zç©ď¶hEPnŕŰ˝¶>ޱ˝Ie:™ŮęÄ؉=:" $©=üçżm¦Ś{ęť=’ÂIL ˇ’(ŠĚ˛/¦R*]4čĺÎÇ+şXîĐˇŁ»řyľë7Ceëľą{é7ľËCÓľ[Ľ}Äř}Ăť|(<Gí5ňµČ§#'źKőďŔy˛ĎÔâ‰é[^ĚS'ÇaďÄE<ŕ?âŰ‘é_ÇŞU€ŇqěŃ´€qÔÇi|ćظG}=ďđť|iĚđף†0)#-b™;ŔŤř.%v˝7Â\¤ű-‰a+|"žŤžp0Ů=GŹ1Pţ5ôťaÉĎÉ%CO/?zkBÜËQ#?Šőç ŔyR A‰X,ôĄÄq_FÉŤ8Oó›’5Ú7E €::¦˙l AÄm#cWyąM29;˝“ćĺ–ř^‹Rß·¶ĚӰͩے&žHMý$Î7ÍŮkVŔôň Ł·&Ä˝ťňIśGĽÇěŔĺýPHÓŹ Ç2ôáIĎĘ·#fĘm3v˙ÂĹk–ďž;cmD€ÄСŤŹ¬?;w‚ŇŞyĂÂâę×Ý˝ĐĎIf;ďčuhůЧŽ}~ěřšµ+8®—VŻZäŞrôĘY{ß;©1SŁc«×­Ů:mâsćT¬]őL| Řąxé?R"aŤ-ćä¸óÎŰ7% }fü¤Â• Ćą¨z’)óäµYs·ŹOp÷«näh3b÷ÍůŤ {­×çĚűz|ŇŤ(?/^´îeĆXîůźöő™_ÎH‰źżö•śĚ#Ďo~ţČĺ˛kĺ—‡‡:>ÇŻÔ~üŇjXcČ@áđÂ[ed´ę´gŽ\?ośB.T^;ţŐ–-ŰOi+/ rÂMÇxo&P˙řĚŇú›iůV¶c‰b”8ăë{óJjjJňö}żmfb ŕňÁţ,muáł›ź8x*㉇H™˙cěę±ĎU8%€¤ąëäqÓókŰO»ĺŃőŹ;›ąáŢ92–+źlimÚ˝kűú%cť˘'?›ńÂcüőŻ[j ¦`äťöÖ˛ śŔüG_h¨ĽŕČËĘ čöMwz(„Ź[\§—¶üĎż}řĄŘްbZ €”y}á˛SbgČoÝ˙¸%Ł6ó[Îţ7ż ‹>gürpĺÔ Kî}ůJćQ«\J ÷Ľ^mąAjŞ«–¤‘ďí9č€ŢĘh–µđű‡˛÷đü@°÷âm¬}˝07ăĹç^řáčIW1tŔ˝řĎŹ-ľĺČţ Ënr×&ĆĚ+'†ŕúmÜ]¦gŽ|x޸ĆeUµťúúŁ7ţýc-w¦FĚ_űFvÖ^99ÚwÁşżćt”TMę5cĺó4ź8˛˙®ŰR{ĐĐC„ô‰} 3k„n¶B9N­ä<űéąşĚm š‘•MíÍŠQĄ6ŮgY?{͉±=۶î>|Ę$^yLŽO ě]=¸qd˙#‹gĐh”2®ÚŚŻäCŕ‘ ×Ág«űÇ峇­V•ć(ţüŹ˙^Źtz`?ş˙‘E3üůíO2űŁ'yî2ÔŢ1ąâšľ/nl\ k Ęęáéă‡n7c­±ŻţóŹ7Ţ˙ś1ÓďďLcčuŤhr´€‡źűşµ>ÓÄ!ŁA˙ŮćU^ŃS̬Ăg.›Ů·ËËÝřéw7čͶ~ôá'{c‡ľzÇ—ŔłźeÔf~+Oňüăf•5™w~úŻżü­ĐRµpD€E˙ÔáŐy…]ü<`3¦ü’yéţEŁ'­1´Ô WŠ„š6ńůGçP8…ťČ­Í>}ŕď/>ź`ŰwK<ĚÇţ•—}V¶QWľp\lĆ™€{~×ĺ›ůcÂ`ťŢ2ŕH"SŞťc4ćÜćÂWKZĘ´››Îµ9ŤvU»’’·KkÖ`ffŞ24žĐ¶0)Őî)®ĆËMy-ko–…ŘfÖ]i5T´9N¶ě6}ĄÉÔ,Ş 5ŰŞËż×z/ ž¦Ę{®ŘŘËÓ #ăÜx·TW•R*ůWIc¶±g&9i’AÇB Đoho©’ę Zu…±M4Ö›tą­†:"3UvRŘqvǦ/hÓĺ·Pą*ÚóZj4K `ôBóEť©YdŰ®´ę«Í¦“éš`Ş1To­®řˇ ”€Á!ÎĹ-BQýĂ5ůî”C°ÚT©oüĄŮÔ*ęsô†ˇ˝´MW`ŰDcťI—ÓfnŰŻZKzŁôÁ1Ŕ1Ů;ęnŹ˘—‹ő×Ä^’4’ W—F]Óňęz“©¸±1żU/IR­®ůpEĺĺ&ťě‹üÔęâĆÚoŻV›JhŇŻnŚ đVÓżť=·­¬‘PŞ7·_jh¬ĐµśjhŃpČ«o,еÖڵ­-_çdż—_1+~ÔCaN8ňKµŮ"LQÔŞ4?*üíÜ™ýő­@÷›¸ňˤrűÝČĬ˛˘’vłm:Č_(! ,ÜŰ'ÝÍá‹‚RélUC ‘€ˇľ/ ~éřɢv3wýĚ!cP*JОÎgĺ·č´UU™™ç Ëëý‡Ć&%ĹÔť?üÄď?ťWłđŃ׿0ôˇ5k+[%Î>!ˇQQ ĹY?î9XŁm*ĘştĄŕʡŁÉS§yŇęM®?‘[E(e7űâ›QJ¤–şĂG3’§Mż‰–o];r&w·`_·Ě_?—ĄmjĚĽYŐа÷§źŚ*Źé3ĆU_:őŮ—ßÖ46‹fcMMÍŢ=?ťĎ.˛śř ĆÚÚÚ˝{~ĘĚ.!-őEűť LI—’›qdŰöë[(Aaö™ü˛…RU[u.3_ă0~Ňd?•ţOܸëtÁlŰéŮňůě"‰ŚI‚ąĽ(˙Ěą‹ÖË_]ľéNĎĺ"ŇTvĺtVAJú?'ńĺgźüň§ł 0›úÂŐK‰=ć‚Ů{áôŮKy T©Tň\'đ<Ďó¶7VÂó<Çzć łŐçfmafĆĄ«>CcG$ĹÔž?üř†ßźÉŻşE™s¬«„ă“®6™ź}áÉ̶Őę8z‹‚ŞPĘIŚĹŹ[ů÷Çç?ůĐyµ-\ůjě{›óyĹĺîŢžńÇ›ę.<űÄcßËŻ ‰č”ĹůsWJŞĽ†^<ňů7‡ŚlŔ§Ň]}TqVÖ•Ľ‹‡Žf&NťćÎżôô¦Żśc˘Yăę’”dŞ»đěćÇ~:[$Ů”<÷ÄĆĎ\uqtąZ|ɖ沪†nĘĐE?n)ŞmĄŮöźŢôéÎ=l‡ČéA`6ň/ť;s1`íşşŚŚŚŇâĽc®:*XöŮĚś’˛şĆ†ę˛‚˙ľýĆ»źý H,~üň~dŃUĂ Îgä”VÉŹ d\g/ć1 ĐkHᥣźsČŔ$0ďđČääŘ:‹TCˇ‰Ľnét‘EsAćąś˛ęС1őE—zŁçĚĹ<ŘúCŮ“´µÖgöÎŤw>Ý)˛ľŢŁ÷ÔĂŠŠň¬+—2ŽíĎĚŻH=Öß™{ĺ…?|řÝaɤ=ôóŮë/`6ç]8uć˘ÖÖŞ»xîlć…“§łŠRĆŚ÷•}ćî †ľ< ˇ”1Éwč;ŇŻ^mähŃ×;ń‚đó-D“1çÂéł—ň!şú‚C§łÇLśč$<˝éáíÇłć.^ťuńóŚRŇRg3¦dŮöÍŹuM:muɉÓgŰ$ˇ­µá—ŁG*jš$“öäÉl‘#J—uále®óŔÚŰŰ5n®‰˛í<ńŘwGr`3îdfI=ż9š‡[äE»HĽ—˛›!Ă ®ĽMU»›€v}«ŢG](2Ó«=·YWlęë3ŰĆ(‘át˙Ž@h GČĽ–FÜćxvMŢŻrűűf8F $¦‰srŕëökoRnčÔ‚Ůů¤s2g'Á0 iÁ‘Q ýçEUL^^—Rz{DXYCÍ%m%¸5a‘p„ŠLš?ü©Ďi; ,ËÜ­™Ű‘^ŢĂ]TßWüzO8 QÂŘŰ×ÄąTţç‹ý TN©lOşph0ů5X-˙¦íůNÓub´gVDÎr#Bo~˝\‘¶é˝eÇŔéŚ|ŮšI(Ç$Ń)nćÁoŢő íff·ŤA˛ą˛ý%Ś…tí“÷ĺę9JÄËŁkÇ}¸ŽQ墥K®^ř9ăJů-#’PĘ$)&eôČ0Ź/·ýtŁI€űďxw´ÇDáĆ1uoĽçŽiě×ĺ&ú–EżŘ-_YŢév7{jß}Č©WoLÝpuŻ"”#öŽôŞ`rŘEŽBş©D٤—çľ=™Ő7nŘ«ë:–ˇN&Ś Ü]ôER$Ę)n“ç?rđ˝ßMŹľŘ&Ęl'”€Iëđ–Lş®–;<°)ŘuőŔńööŤ|7Xú9Z_q ¬Ë„#`ť‹B ł{ŁÓÎr‚Ňa2¶ 'Ň©ŻýĚlé´iđc’ ;«Zt¶`w…Đ (!Ś&1·éCĆ8\ůs©|VCyÂĆ;łGŹ v°ţ(Žő%J˘¤D$ ŕ)eډŚB8BäżűŕlI!ÝňÍR«ĺ3›’ŽgŔ]WÇ„#äÄé€Ř×Î é7âŻr7Ç„G­p}äŘ9áם+ŢÜ$a”ăŠă8Q´UřNâ)ĺ$ŽŁ‚  ÇD‘PŽç©Ůd¶ ±˝MKşČbŘŃ»}Ůí ĺxJ ťë:J9بw_˛°CŹ„ţÄg÷_mĄĂ‘.áŕzŰ·$„bŮ\ O! "S*=é±v“ ‚HíŕuOą÷ËŤ> ű¨'uń«”ă!‰5€B aˇĘĘBĘ$É/vâS÷Î|őŮg* Śç¨(‡ÔďŃĺ8XÖ9۱ăŐ{”tS$Qá9* áy®“„đGQ´ĂŤÎβĄHc¬›>Űý¦'8BÄ_ÍÁŕ9Ç5ĺBÁÜZdĎ9†«)/—ÚŠM}[¦á B[µ ĽÂďNÝéĆÖ đ„ Ś8)T„‚D}©AXçÜťu¤šL†PbyT!őâ® Ŕ@|5É˙¬xł°îŚ€Ę_í’ŕ(6™´™:I´_Ąç7ÔYé‘ę Łpítł$M„k6·7ÁÇps­Ń,ǵ-Í–ĂxŹhEĺwŤĚÚ8uW¨Ýąö«™96¸Z%ąáçpµŘbŇfč$»Ű¬ď7Ďó&9BČÜfřF-wĎZ›cčs>®vpP)J´-˘nÁŐA欑ő›ÔétUĄ\ŞźŻ‘~©Ş1HŚPŐ’čĐ¬Ş’ě#O©(IĽRí⤠0 ¦ÂfťIbrňs‰1čáľBo¤˛źz[DÉţ”wňŘ>cÜǧŹí¨lâą©Ť ë˛ů0ÄŰg‚‡Ă'y%żĺ¬ÁČ‹®ôđ?'ŢóĂň›Ů,%Ś©‡ř†x9ĺĚRâ0Ä7ŘZ"3˘ 2BCĐPYZÝŔ¶B5vâgŢxčĐĎÓ ńl°ZľUí€)Tîqń‘< ”‡T}ąÉdChtňQ UůgŽźÍŮ€„Fĺ8J$‘‰’Č)†¬{ä^/ )ľ|ćËďĘ’—Ç-ŚI˘8Đ~Jĺ[ť”~+Ęá>$ ÄÇéRVžź(]++«n„«Y„ď˘Q˘0ŽSŰł® ¸+żô°‚[Š|ş*ϡAĂîüżoZąőŕeŽŁ—ţÍ€Ľť<é®őomĽ}ţäY%¦ťeÉz‘6*Ą­ľěđ±Ó&A’ »ńY6ŚŔDw}SVYĄ[ŔŚ)ăąöş=~6šÖÇžŤHŚĆ¤ŤîwůôÉKĹe \hlRJ\dkSĹá#'Ťf©'çmjů^>}ęRqYĎ-çŢdŃv{[ם}o×f•–…ך{fÜ~±¨AˇPf3ď:$62DIaŇ·äćšEH’Řż,zúů¸Ü|˘‚ý%Á B)aŐW *šÄÄa1j ĽQ_}ůJ™Ä,ć.KçrYŁ\hlŇČ¸Ö¦ĘźŹś´'0ĄĘ{Ň´ń.Tt˙ˇ:InĄ== ¶‘űŁYě—Fó€ŢâŰőhh\˛EŽž2šDwßŔŕ& „‚Ő”—7ôĺëě{Ȧň˛ŞkJµű„Iܤ3gO•VÔ_źĂ$„Ł„ŹŠ‰µzHPŽŤ-—/ç™ĺyŽ0&Z—"=˝şťë"Š’$I D>äŕ˘$J)(ÇS‰‰L‚Ôc5bµĺČŃi)­őĄ‡Źž6 ’]†ôýMoš9ČŔ…şŤÚť’ţSňŘ©Ł>ަ ±)ů(Šă­Šj ”PE¸¤LKů[ î||/?ÓwżÝwܱQcv&Ť;śšňÁPG_ň¦= qs0%`ŠSGI_R¦ŕ·!2}k¬LŚű,źń'ÓĆ|ź4ţč¨äż†(»>F’ěňÍ+ˇJ€óŐ$}‘4nϱGR‡˙1HˇPŹř>eô§C)ŕ:ĹwâĄ1áłÝ\Ç{Ź;™ÖŤfŢËFcÁĂ)‚ } %ý›xGG Ŕc¶ ®WCyŔa´wúĎŁĆěJlTňkˇ*{ĎůăĂ7Í1ůOµ:uwJä÷ŢřIĂş‘i'W.ÍX83š§°ľÎ·twĆĄ\Y˝ěě’'îZP|Ďň'ăCüqü´ü»—^Xq×–q)ĽGĆŞ÷‡yPrŔř¨ŘË«Wś]˛ kŐ˛}s&Ç8*ČѢÝ\ýNŻ\ş&ÔßßCąĘňÔń™ ¦kěiÜŻ ˛‘ĎIH>9oŞRfŘ-ÄŢ/Čą<ď˛ŮŇçb‚:ŘuÝÍ„óůÝźţYPÓPšu4ÎQ€ă};K4J”ůĽGôÔâÚƇć§N›^~Ƕŕôň»;›®5TVWî|÷çÁaš|*ŕxÓ-ßşv䇪Ń—ä—ו—•””–×—® `ц«š róuÚ†ŤkfŕűÍoŮJ~GΫ^zçĽĘćü#ź+aÎͲy`y!úřĄą‡ä<Ů{Ż”|¸ůôť'»†RĽ&fFauEiIYyyiiqiUŃ…ń.j€*şľ“čŹa=ěÂj·€Ď×U9|ę}ÝR–ąÇŃ~üŐ_ŹD €h˘2K_ż˙Xa÷r”‘©ËşÖÚ^ś—×ŘÔrř‹·|)@}~˙ç·mů,?)»¦ýěŽw)ŕ2lßąüšň’ęƆ}˙yĂÍ7şîžMÎG_zGŰŞ/)«¨Î;3ťş˘˘ˇĺjQQٶĺçŻ˙Ŕpü«•ó»Ţ}Ő°áĄ˵ŞrNwT ‡2t‘Ę÷…˝·v,±Fäľ÷.Í҆–Ť GP)•¦­z´VŰZR_SßxîĐ· Á8ÇčŢea_źmůśąó]‹×żŮĐŇtµ¨0?/żMŻ{aíl IĎ©ĽVVR\QŰxéČWä××r­sßż zĆę˛~ő–?×E<˛Ş(ücvü’UW]VVY›sjw| đzüĹ·{ł/Bĺ 6‰{ĎZäľ˙ŁżąpL.­ď“AnčÇT»s#ÁŮ@Ümw—Ő[zqlű?ś€żŃÁŤÖV틏,é[ĎíxČś˛[Ąßw'˛µőŐąWË/ß­uŠ5p[łă!ĎďŹuě·Ú×ďzÖ’•Pí=~˙ţý“Ăzňł§-úüo~]ĺ>onť§Ą!îŁw%{Ws® •+€†ş§íJ˛”¸đvĘ·S˘ßŚźt:mä[á€J“şcDŔG^ł‡xÄ«<ćúŽŰ›ä¬&Ęçô_ҢW{ J řɱ_Ĺ*ä°î~ËýCÖř{Ą9ŰĂP¨RvĄDŢe‰Hćŕâ?דśĆůO<=Ę?]Ř„ŹŁ ±ůfŇą´!ń*ĎEˇ“~IvôˇqžN¤ÎňNü4yĘÉ‘žĂÔAOĆLĎ6ŰÍmĘń’»Ń ŔsQ@Ę»–Ře|¤ÇČ-‰‰ŻMß9ĚIC8&ÚĐsf”ß(5qUyŽvŕę;élZŔxÇ.Ę«µ&ő»AŕÜlŔcQŁ?ÚSX˛;ą˙pçíźLť’±dnlŹUŤ JĄ*ĘÝ=@Łž_°ziş#őń‹(»˙îeCśg MŇ>´fY +ŕ|č®Ew{5ÉŰiJtLöňů‰<ńrőÉ\łę•a‘°.xJźzqá OCś\×$$<:<~FŔ{B&@ťv/Yň|\(nIô!['>ÓcwĎžđżM^«®I›xjŢ9\ĚőR(;/ç„y'NťÝ±mGYAć0'5×Äůť%ËĐËqŔăo諭tĐ pźňô9ů›áÎj1łîÓKĆ{Ň"çŢű'Ƥµł“`ěoäh0Ń3o¶ĺ[ߎR­ró ţëçÇůú•P?OŽŔ#jFU‹ů˙^ß uěěôÔ8ô«Ő„Py…ÝűČćçźybd|O<öÖîěźpŔ'8ö፛ź|âńąÓҢ ňH“8{Ö´Ôéw>óô“·ONí»˘±7·Ş±ÄFDř†˝řŮŃÜźż ń Źđ˙­ňTٱ Ťť čŻÔ1Y‚P†ś.¬}ăá»0€uĹŕĎ˝¶µđô÷Ů·.Ë)ůFŤżő]Ž@ŇěÇŰŚík&… d晳¶|ć)”˙úö±˝ýä®,{ü}˝¶(!Č!räâ}űw¦ OËí)AŮG…Ť[RŻk~ry:€„äTWnAC§O bňĂ&Q\3!Ô+}™‰™­śg«&†ą›{MŻ“kĹ'ŤôT«ĐŐNzĘ€KźŘí¶K,5Kߏďx_ŔaXfnÉsG¸sĹ˝3RCg®^_W‘7BÉWŢbř÷ć5rĺŢdŃ›>ŰňůřŽ÷”€ÂŐ+6!1ŘĎ{Á›´×ꦅ: dfAqŃÂô(gO_wwŽ …ŕßÁCÉôŕś.ş*‡Śź´z“Aß4)Đ÷IkhžĽgü§ž=—Ů›}Éi&Wlz·­É*÷văý·Çá Ęúĺ×»G˛Ă G%ďиÎ^MëfuH>\ćFScÍĚ!čs5ŇÓC)ŞxëľĹs–?ŰÚVä (/]’ŕ/Żú0@ĺą`Á<7 Š;.Ť ô öď𑼂cذé‰ÇŠ ó‘kŰzőąSÓÇ ›={şĄdĘ(*ďpË’@5űŽŰ§ÍZđÔ3OŢ62@t\ęúçŢ®(ŻxýńűW®Z14ČÓV«zÚrk{ëÚ™‰¶rŘ7·ÂÓR KřŔű‚"VűŞ=8„‚¨–wŽuŘbÇrścđZäHŞ÷´pŽÖĚ=˘Qň˝/tŘźCU<Ŕ ä\'{¸'»0ťą˝Ć@21âëä7Ăąúłj3qQÇľ>4hž»űxŹđőśŞ ˘‘saŽJµ¤Ëm‘Ék»ÜRőă505Äö®K` Ţ®ďř¦Ń ¶K „wV uúZ©˝ ­˝Yts„ÖÜ^cöžçď囨’0‘1íFłÜrÇšD,Őf¬ĘŞ8ÖB”D~ Ó…=#„E ś"-4x‚ď“ŃPŇÚŔ$Jś“÷ŠźŻ˛˛8•ó[S'?04h|Pč“) N\NиyáĄK µ@˙Ů‚üż:ą äĎŞŻŐh\c׿Şa’ =OúčÔ·<˘P©%Ń  =wg “oÇRQd|đčű—Ś˙řŤ×µ€xuß´ů@0>qlSÁ‰ďËą{×.—$Ăđč$ŇÍE¦§D0~XúM¶|ëŰ‘LFmuYS«`ŇkKŞE†É·MtĐ_ý×_ţ ăÄîgŻ$Ö3Š\BcĽ_ܶ]?l^sGꤹ;v|;mXY`8µŠ“—DL$.a˙ݶýw÷Ţ1uÎâ—^|ŇĹA~şKzkňz qÜŚm»öm}˙Ő»–ßűĘkĎ:«`©h§–µŚu^E¤Î‹Ţ}ŕ꣤PSRTSqµIkb’ˇ˘ş˘¸¨ć–…éNKO»`»Ö20P“tbGújLçŹŔ­}ů#p!㸣Ě0'ŔHźHN{’sćضloŞkŠ[[ ĽĘĄ{FĄŽ´ňY HěŽ^ś®řäËŁÎ.în>Ą—É)o/ĚĘ(şÖ–ť>-·§e5yô]Uľ*vö·Ű·Ĺ AłmyŢácńq‰É#BŻŐ–•Ui''O¸Vhá<`Ś1sdZKu\+Ö‡6Śčp˛‡,8Jô}b·m§Ă.8žH 6}w"!„ç`hׯ~úź;>ů§?OŤf*çqóďś>nśQ[WXV&SŃ›,ěésw>;9;RŔÜ|-?;«¬ş~ÔÔ…ĺ™ß(iWkTppßđÔKŻlz8ÄŰYd°ĄĐÉYŁšJł+ŚŹKLŇPS^ßŘ›˙ňő55uŤşČ°ˇ1‘ŃmŤŐ5 :TLMŃÓľ¬Ž‹pwő-Ͷ‘{T" PžöËŤ>îwŮá†$h(ąrȢ ! ŐĺŤÚkÚó/_´p#cÇţÜ:€ö˙<ĆÖC2IdśFéBbhtŚ#5oŰúUvU;ĐWÜ#J9QŁfĽ˙Î[!<ŔGĽýá˙-1˘ľĽ¬Ş¬¸ĂCVđńŰ~Úýü†•K|fďO;’#< źp[‡WíőçÄĄO˙fçŢ­ďżz×ʵyi“{hĚÖ;źXsGꤹ;w~?6ĚĹ;zô—ßězďÍçŻzđ›ď· sW…&NypŮtÁl»`ÍcݬÍJ¬§-·éÍô[‚K č›[ái©Lr[n«ľÂě2Ógř{1N^T2mů–’aďÇ8ůó`čŚLŔDpľŽá«˝Š_+ŃUH”c@(„vÉ]hř"·ěµ—«/‰’BÉůŻŘOˇte–ĽB> }Z}Őn-ęá੬˙®ňÂ}—ĎŻ/d>ôеO)ťśu„“ă+"–Ę›Nę!´]ŕt|óű`±ĽĄ1Ű ´ŐqîžIŽž3˝ĽxÉČxĄT˛Ů}šŹBŻ×–š8…ÄŠďIs_žđťc?ˇ]é9­ŕ0Â5ţ± Źű‹tŤY&Č94»<·‡  Žtá©Ęߢ‡íPRJI—µĺ!D\ť}¸ýPP€§¸f4­‘ě/4«oQq@«Y\™śöd¬˙ş~úŞN§â8Â)W öÔČxoN8ßŇ*·¶ &ÚÍĐôia%•Ú-ÉĂé§ÜKwýđÓÂÝGŰ%ŇMČ2EÁNJ%a-&ţô:“H)ççÔ}í:p`˘@Ąä;}–(Ú”X’waŮşuî —ßű|ÉděV „445?úěźő—fŞ•7˝ß Őň-lG6;ŽRj™|ą;ř·h˲uBąŽý=źÖĘ%rňÓy Wß6Âoëďľ˙î»:—¨Ík[Z·©äŕ—šňÝG/Mź8fʬĺmFËsµŢZîŁŮln©Ű´řöá Ń3—l4ФwzşlY6µHŻ-°Ä–_ňÔŽp”ČWf ąĄWN{@W»¸ő¤ôŁc2»ÂÝUTjÖ6ý&Ьmçę€Pó?”S(¨ yăÍWŰ íÚź%'i•ů,z¸„Ś|yóŇç6nJýŮ‚RąýľeaKOO>;pfʉ îá“WNMřfËv*b¸|ţtvqmÚŇwú)Ńß÷ö×?,ë¨%?Ž™tŰW»öńÚŐ٧~:Ut>ě‘#dÚ{ľ¬í_?<ĽűsEÖľ˝¶r·µ/«ă˘ŞŞr"lĺn˘kíźýîö°n€؉3»ô‚ň&±“"ŔqKź`u]L‚ł ÷ýö·_ÔnÝu°Ľşü“ż??¤ű-¬^,E0·µęD€¶ęôF“%7±ě!y wÎ[3ŇOžž4"mŞŢ;ţžůsD±Ó«/ÝŔ,L-u›Ďť>űÁŮóîť9Ň˙Ë;ÇE›VĐ7?{߬Éóë8źÉ3GîűęŐ±óž02ń/릧¤OűádĐ=\w[Ţ—@”s_Ď7ż¶§ĄÄ˛ćËsó^+ÉX“gV)='ş°Ş–ěßĺÉ%‚Zé9Ć 8BxB8Ë>ßň Ť‹D}ÜăTÔÝÁgś#‘$đśÓPgbôMfČ´—í'ˇM’$F”b"c"Ťüs”WËúăUł(ŤLˇ•w6ß<}U÷ŐVi‹ýGlÄrws“ŽP®ýb‹.ݵńN40BJ™-Í«Ľ­zŰE‰!/˘:VRWzD€@źŃ±ôâ…ß—(‡şúMs–bK!  Ŕ1ËŚ LdbK×Poť4ۡČSÚq:!»íiˇ˘î»˛j˘ ř¸ů¬ týëŮĚfp*9ŔÇÇyzs˘©ĐĐ9^©uĂ®RżŘńŁ–>;,Ônë˘we_©ŔQč›k?Í«Z2bÂĄ _«bťçś 5‰cŠßę&Ę˙€Ł%©ÍÄpăŃËÎ ±šM÷Aáó»Őóľyçďe"(ĎY‚üIRÇą´dŇűÇLxxfěźzFKTjnĐîĂH¦öAiůV¶cŐXç+HíJµ‹ µX¦%ä !ĽBŃĺ.ŤĄ„#’Ŕ/$şĄˇaěŇuĎlz¤±(;§ŞJľmŽČg˛„BźwüýŹöŻ~nkuUÉXݶ„6˘˝´ÜY˘TŞë*sö^,PyµÄ$°>č±üd„‰ĄĺD‰Q9Ç5!ŠţpŮ)é ÖXGVMţMăzu°‹]ÜBčWÇäuf“Î(H’BuËsu[ÄńD4›[›zezTa˘Ů$h^˙řëą1ÂÂUëk%(xkDoŽcÖlřs´»A?yDĘ3rĺě”ďżxç˝.şëŔkg^«Ż'JŐ€ ě⣔*CmÎKW?¸fŐÁ\í”acäűfLRÂěĹżóKž˛lňČ–úúŔ¸IVΫ•”*•šöúÜŽZÓ’Ç`6'EöeŃvK;”S(ˇäÍËĐŽ †–6SËŐe3&ŤNOźŞ±˝Ie:™ŮęÄ(ęt vn«5*âŁC*ĘÝť\?{ă©˝—MĂÂtU5F­$2›Z=řÜ»,úŔ.?  µÚ/1€67•tôťw [8ga˘†3m^w÷wĺ.;?}KIL&Aꯖ×ř…¨Ä @˝ń™w [xűtőZµěÎSßm+hA99(–…bQâÔÚkW/Xką…-3UMÁQ×Ňp`ç7µF~hdĐ9i“•6<(Ƥ+Ů~ŕä™?ć^3Ť“˙U°e$fă%(Ń–l\<Ő7Ř*÷ş@áLŤ›Ö­ęĽ,‘X­usĽ‚ ˛&Č˝ Ę!+—Ţyę»mZ”ë;ĘźŚŇdă!Ő<×.(á]xrôŕž<żéČ•˛pż@RôILR*\QÍÍ–{:˛‡”w“Djtrńr!q ó÷¨|wIěđęňš¤ÓĎSŠîcʸ§ŢŮ#)ś$Á*‰˘¬ě„P•Ré袽íďž¶ĚóĽ$1«7+`ń˝c zĘbđÁwyhÚńa«“·Źp`¸Ú‰ą+¤ŁdüžáÎÁ Γ|¦OL˙Řňb”Ča»B_J÷e4¨ÇM ç@}śĆgŽŤ{Ô×óßÉ—Ć =jř‡ “2Ň"–ąÜďRb×{ <ŔEşŹŘ’¶Â'âŮč “Ýxôĺ_Cß–ül\2dQđôŇńŁ·&Ä˝5ňŁXźqζĘëĆ.ßü7nČ( ¨Ăđ÷â"đń]Ę/†:x8Ž>4*hş“Ś$qŰČŘU^nS†LÎNď¤yą%>×â€Ô÷­}çiŘćÔmIO¤¦~ç›ćě5+`zů„Ń[â^ŽNů$Î#Nă˝ dôw‰áűďđÉ'Fx'«{ň88¦ŽdčĂ“ž ”EcG̔ۧ`ĹĽ użx÷Ę5A®*GŻśµ÷˝“35:¶zÝš­Ó&~1gNĹÚUĎÄŠť‹—ţ#%ÖŘbN;ďĽ}SÂĐgĆO*\ą`ś‹Ş'™2O^›5wűřd·x»”#ŔŚŘÄ}s&ţoF ˝Öësć}=>éF $ pxá­Ź2˛ ZuÚ3G÷?˛h€?żýI¦ĄäŔ#‹¦®Çsë?~i5¬±MşÔ:vpýĽIp ąPyířW[¶l?Ą­Ľ<,Č 7ăAľ)Aýă3Këo¦ĺ[ŮŽ%ŠQ⌯wěÍ+©©)ÉŰ÷ý¶™‰€Ëűł´Ő…Ďn~âੌ'Z eţCڱ«Ç>Wŕ”’ć®“KĆMĎŻm?ýí–G×?vělć†{çČXf¬|˛Ąµi÷®íë—ŚuŠžtülĆ Ź=đ׾n©)>t€‘w>Ř[Ë*€pJó}ˇˇň‚#ŻčöMwz(„Ź[\§—¶üĎż}řĄŘްbZ €”y}á˛SbgČoÝ˙¸%Ł6ó[Îţ7· zŘĹ‘ýŹ,ž‰ě°Ţ<î꪿éŘ•ŞŇ {Cť€Äąę棞7®qYUm§ľţčŤĆXËť©ó׾Q›ńâs/üpôŠÔZ9o¤\Ł.V4Ęśo®ľ’čCá“’Uˇ;őőGoü{ 3kWĎLŔu9uě! BúÄľ…™µ÷ĚIB7ۡ§Vr¶}'š‘•MíÍŠQĄ6ŮgY?{͉±=۶î>|Ę$^yLŽO ě]˝ęsźk3ľ’G.ücć•-«ŽŐŹýăňŮĂĎo~ţČĺ˛kĺ—’Â=hÔ ąVăĹmî~ěßůWÎÉ<uĺóÇ„Á:ĐĂj2Łlh“ŘW˙ůÇďÎńń…ăĽŘ1ĘŮżaŮíFĚłřC5ŔÁ5yçn«Ü/îVęä~¸±q1ú±ÓîÜČřĺŕĘ©—ŢűJAn¦µ ÇEµřńnpýĹ•‘ßĂ„Ťíô̬ť“ä6qĺcuŐW_{~óű[~݆ăp˝S(/wă§ßÝ 7ŘúчźěeŚúę_Ď~–Q›ů­<ÉóŹ›UÖdŢůéżţţń·BKŐÂmüS‡Wç tńó@€ÍňKćĄűŤž´ĆĐR3\ (jÚÄçť@áv"·6űôżżř|z\lüŹ[>˛éě1R¬Ôp°řľľ±ą*]OËD¦ T;ÇhĚąÍ…Ż•¶Ö˘č()i)1`ffŞ24žĐ¶€Î“1&°ö‚¶–üvR›YwĄŐPiĐć8Ú˛Űô•&Sł`Ş6Ôl«.˙^ë˝44xš*ďąbc,O+ŚŚsăÝR]UJ©ä_%ŤŮF;ů)AÇB Đoho©’ę Zu…±M4Ö›tą­†:"3UOhuE”§úBë7u&]^›±Î¤ rpéjĚjĘ˙k©ľAd&ˇů‚ÎÔ,D2ŠmWZőŐfSÉtM0ŐŞ·VWüĐJŔŕç⡨ţáš|wĘ!XmŞÔ7ţŇljő9zCĐ^Ú¦+°ŕjÍkÓWîĽc´ŁÔ¤żúVI}F»’~088&{GÝíQôr±ţšHÝ»$ČŐĄQ×t ĽşŢd*nlĚoŐK’T«k>\QyąI'ŰľźZ]ÜXűíŐj“üĚĆlřĄ¦!1Ŕßť´˙éřésÍJ‰Ţl¸ÔĐXˇk9ŐТáWßX k­7k[[ľÎÉ~/żbVü¨‡Âśţpä—jłE˘ 9¨Uiţ~>TřŰą3űë[ĺ.ô€rűÝČĬ˛˘’v3%»Ő:@ a`áŢ>én_”J˙c«J őŤxqXđKÇOµ›ąf!ˇQQ ĹY?î9XŁm.Č<—SV:4¦ľčŇŹ{ÖëZÎ<špűc›—G?´úľĘf µßĄVSQÖĄ+WÍHž:Í“Vozpý‰Ü*B)»ŮߌR"µÔ>š‘ô`^m ×cŔ”J‡’˘¬óYů-:mUeEf湲ŞĆoÓÁgůâ˝ŮlČżtîěĹ\ŃlôŹš'sţńÖź(¨G[Őˇ# Ó¦‡»ó/>˝éÓ]§€îůFşČâÇ-Eµ­”ăB"#{ĂţâÓ›>Ýy˘‡íĆ$9áŞÜ÷3sÖ®«ËČČ(-Î;v᪣‚eźÍĚ))«kl¨.+řďŰoĽűŮ‚ÄâÇ/ďGöôą+źóč5¤đŇŃĎż9d”O晜[wţđă~&żŠP* b'…rŰŰőŽn®V>öÝŃ<ŘäcaŚ "ç\fnɨ1ăüťąWţôäű_8eHDD}=™WJ+EÁT[S#{?ŮxGDwČ˝°QĎQ´ęę2{çĆ;źîY_ŃěrŁđ|Ć…ĽBç!^ Ç[4áHü=˝-Ü`ýß…{ÚTžÓé!˙ř‡o_1¶éDN3,eŚ+§}ĺů?ţgű!Ög<B)c’ďĐw¤'^˝ÚČѢŻwâáçZ$&cÎ…Óg/ĺBtő‡NgŹ™8%ĐIxzÓĂŰŹgĚ]Ľ:ëâ祤ĄÎfLÉ<˛í›ëštÚ꒧϶IB[kĂ/GŹTÔ4I&íÉ“Ů"G”*.ëÂŮĘ]Ç5.;¶\U‘™™QZY/ &ËvĄHbP)5WűřĆ2Đ_ŐÓűe¬żk9ˇ– `ýO× ŢŽŽ¦ŞÝÍ@»ľUďJ†ĚôjĎmÖ›neâwÂ&2Że·9ž]“'őMdŻ­ČY¬;#.t,Ă““„Ć99đuűµ7ÉbM¶mͧ܅2ŰvKQ@Ň‚#ŁúĎ‹ŞXo™‡ű˘€ŢVÖPsIŰF nYX$ŽP‘Iłâ‡?á9mç!Ó-™Î\Wä$ˇ^ŢĂ]TßWüz÷cdžŹ»}MśkŐGźďÇK’`ź]94ü¬–Óvśâě:1Ú3+"gą±ÍÝ ÓŻç »Î& !d°ŰËń™$rLťâfüć]Únf6qŰĺń1n°„1˘p®=°`ňľ\=G‰ř[ĹYë®cTąhé’«~θR> ąţ PG)“¤”Ń#Ă<ľÜöÓÍ$¶«Ż„RŽPI´$íłíţőfjď«vP“Ž7˛65Đ_­č[ýb·|eˇˇ»ŮSűîŁTĽ1YB)Ą 2ź»Ść”Ú}C"»ŁŢ(쥛rW ëeć0P·‹:Ô n Ú¸cÓĺx0q€*×CČ=¶veEOSÜ&Ďäŕ{ż›}±M”u‰P*ÇcŢ’I}ăꍑ›‚ÝÂYőŻ Ö·Â=ĎF(m6Z„#`°,c¬Ď˝ ĎűĄ®Qs(‘ßôwQB:¦·6u-K#yÝ÷,Ŕ†NB á‰d–Ŕ@x‰1É…ßs¬ëplKCo@ a „0‰ąM0ĆáĘźKí/Ŕˇ6ô0‘É7 Čl—}IbP8Ö—Z%%‚$IO)cLdŚÂ"˙Ý ËI‡Ë·%§@J Ú•Ă÷uW‹$Ćäűbűýą€_ d?2&Si`®vpP)J´-víŚRE´»‹Š„…ö\m›Ä Tj&úűp˘ápeŤQb„Ş–D‡fU•d·yJEIâ•ęh'I06ëL““źKŚ1@7ôz#%„Ză°Ů]ŐČľ€wňŘ>cÜǧŹí¨lâoÉNdöHoź źä•üÖńşĽčJŹůsBŕ=?(żąÍŇžťcę!ľ!^ÎE9@B"˘FŽLn«/;|ě´I°Žm#4$! UĄŐ *÷€é“Çóíu»ül4Ď@ޱ§8óĆC‡~6n°ĺ[ŐBĺÉC@yH…Ů—›Lf0„F'Ź•P•ćřŮ<‘ Hh„PŽŁ„@™(‰śbČşGîőŇâËgľüţ d嬻w˘8Đ~Jĺ[ť”~+Ęá>$ ÄÇéRVžź(]++«nş®ym/­óCăâ]4 Q”Ćq s{cÖ•˘weAćih\ňČ¸Ö¦ĘźŹśD} ýęŞ<‡V ?¸ó˙ţ˝iĺÖ—9kđ¨_äíäIw­kăíó'Ď*1a ÇD=| U¸ÄĹEŞQôŐŮ9e˘Ą[ŔŚ)ăąöş˝ŹL˘»o`dH€$ˇDŞ)ąZŢ0­ë‰‹@b4&mÜpżË§O^*.ĺBc“Râ"[›*9i4KrłC|˝\ŠrňF&ÓÓ‡gëMý`·¬Ć$ş·kłJˉÂkÍ= 3nżXÔ P(Dł™w˘¤0é[ró Í"$Iě_=˝z —űŢő 7:Ć őMYeéW—oÎśĽTTF•®ń±*«Ü/_)“şřC0Şp‰ŹŹTó„7ꫳ˛ËďuĎ˝}qĂhî9$w3 4"mTJÇÖł”B’äß˧Oő&Żn˘łx~"2ޱ‚Ë—´"[–—Ód4ßŘ!G kő '[._Î33ĘóaL´.Ezzu;%Ö1E%I’@<|ČÁ!DI"”RPާ™Ięľ’·ËĂđy v:řŔ…şŤÚť’ţSňŘ©ŁţÍńQ=6ú@Jú7ńŽę^ňꀆ?=tüŃ”±FűcŔÁ1ýđ¨ iNä@î·űŽ;6jĚΤq‡SS>ęčËCŢ´!®c¦Lqę(éKĘü6D¦oŤµ|Ë)‚ęťBJhĆx§5fWňřcŁFĽĆn3}ĆźLó}ŇřcŁ’^ ViÔÉ;RĆ|:”®S|'^>ŰÍuĽ÷¸“iÝhŕ˝ů­łˇĄéjQa~^~k«öĹG–ô‡±;®8G9 }ém«ľ¤¬˘:ďĚP tꊊ«”żţ‡?Pźß˙ům+…jnˇ‰{Ďć÷íٺȂĺűÂ^•sr¸Łť4ę–X#ń“˛kÚĎ~÷.Í҆–Ť GP)•¦­z´VŰZR_SßxîĐ· Á8ÇčŢeŃĂ«;;»íîN ďŃ÷8GŽ÷íě…F k´ …ßľ#?řŢđňżűč—üggßsĎÄs ‘·])kĺ~éČWš®1yd>hluăč65’úáF[O®Ú‚L˙ÔĺuŽh_üM¶nŰ^$9)lěł_ÝŔŽçŻ,ľ+=ľËkęС'7`kv<äůý±ŽJ[’j˙]żëYKfľÚ{üţýű'‡;ôěµZfťČ7věTŁ~…@14Ä}ô®dďájÎUˇrĺp‘#·$&ľ>4}ç0'‡^Ö éC&f¦ů%Ş˝ćMËă—î¨Gí0Á€×ě!ń*Źąľăö&9«‰2Ä9ý—´čŐި’~2fěW± 9¬»źCŔr˙5ţ^iÎöDP¨RvĄDŢe‰HÖ…rĐ ŹT'©ľ“2FűĄŞ•aŽwzq€óx˙ 'R¦z%nIžrr„ç0uĐ“1ÓóÇ„Ívs›2düän4đ\ň®%v uOŰ•t#¤{-ÔšÔŔ1yŐđXÔčĎ†Ú 3N'÷ŕîĽý“©S2–ĚŤíeUŁv :ľdÁ'µC€Z`Á°1WW/qäĂĽB ď]µ2Ř p>tע»=ĚŚŚšäí4%:&{ůüDžxąúd®YőʰHX<ĄO˝¸p†'€!N®k?#`=! N»—,y>.7MëzAžřLŹMÜ={Â˙f 4y­ş&mâ©ySäđ57OˇěÎśćť8uvǶe™ĂťTâÓĆßżú.G iöă­í­kg&P)8Źż˝«6űÜőř{zmQBCäČĹ-úöîLÁ 9q[ç—Ń3ďÓKĆ{Ň"çŢű'Ƥµł“®·ĺ[ߎR­ró ţëçÇůú•P?OŽŔ#jFU‹ů˙^ß uěěôÔ8ô«Ő„Py…ÝűČćçźybd|O<öÖîěźpŔ'8ö፛ź|âńąÓŇĺšýšP!1‰łgMKť~ç3O?yűäÔľ+Ę«š{6˙·,˙gyUs(ŻôżO¬ĹÍ­j¬ńľa/~v4÷ç/CüĂ#ü«e4€ÉL¦ç$á¦őy 0@“%eČéÂÚ7ľ ZW &đđĐk[ OŻčd[ď\íá[†9©8ĆÜ‘[X°0=ĘŮÓ7ČŰŔňMď¶5Y=I{űşą#@Ő Ă‡űy/x`SScÍĚ!¸ŢmÇŽsV·¤^×üäňt É©®Ü‚†vHŮ(k§„#ř¶3g3vlŰQVp>ŮE`Őć÷:é±çŮzĘ€KźŘă“FzŞUčnor,5ĺżľ=blo?ľă}‡a™ą%ĚŕÎ÷ÎH ťąz}]EŢ ?$z\y‹áß›×Č•{“…Î;*x‡ĆőŢw‹t\çwÖŇ(đ”tPřË÷ hěŇúÖ^ű%o~…Ś˝«“ó#FąЍŰó eąű»»w_‡Q€ĂĐąşŕáIę8np˝{$B)€ŘQť#šŢÔľj|°Ë°ąMzťLaâČ4' pĚâţäeşy~ž@9»´¶qÝíÉ ‘Ýľoä‚óÜ8€(îX¸ 62Đ;(Ř?8ĽĂCFDđ Ž}`æ'{(.ĚG®mëŐçNM3löěé–’)ٍĽĂ-cJB8M@Ôě;nź6kÁSĎ€Su"˛9ć¨TKşÜą@*í‹BKŠ›Şö¦óÇpŤSŚĘ\o ’éj[ŐŹŤ"`l0 Ö&¶W ŢóüÝĂąÖbU&2¦ Ýh–[îȢCZ1pŽŮ©ĺ!ď~B!Ţ4Ç#t9:ÎËÁICĐuJ.ź$¶7WŢţý•×((g{¶Hlh$`ŻZ›”ṵ̈@€»ĘˇâZ}A›pőZc©QHru$J‰AîJJŰ21͇Ł(S¤…Ođb2JZŰD‰sň^áóUVv#§r~kę䆍 }2%Á‰ë.dů‡ĆÍs/]j¨úĎ<Ř@ä˙ ŇČŕ?«ľVŁqŤUP ĆŞ†I"€öü=éŁSßţńBĄE3€ś3Ç˙łe{P]Sܦ7˨Ěf‘}˙’ źĽů†řyů•^ţ%§Ľ˝0+ŁčZ[jt"‰ÜÜŞ†HĆKo*8ń}9wďÚĺ’dťt˝-ßúv$“Q[]ÖÔ*ôÚ’ęF‘aňmôW˙ő—Č8±űÄŮ+$‰őĚź"—BÁď·m×›×Ü‘:iîŽßNbN­âä%‰KŘ·m˙Ý˝wLťłřĄźtq_ö‘ŢZ†ü†H7cŰ®}[ßő®ĺ÷ľňÚłÎ*X*Ú©e-cťW$©ó˘w¸ú(é&Ô”ŐT\mŇšd¨¨®(.Şů­^ňË1ţKs+ŹKLŇPS^ßŘ ¶ŇMĂ@uL>Љé«1eś?·0© € Çýc†9)Fúô@=}‹\Bă4žzé•MëC˝xşű•f[=Icۨá yW.—U׏šş°-¬ R»%y8ý”{é®~Z¸űh»Dzh;)•„µLřĐ čL"ĄśźS÷µëÍ*%O-+;PŽWPňÛݶÚµď(•€eëÖą7d˝÷ů^ĺWÂSçĚ›4kéŞHwŁÁ|sTŘihj~ôŮ?ę/Ě(T+ořdŕ¶#›G)µLÜü[´eŮ:ˇ\G>‡žAäĘó®ľm„ßÖŢ}˙Ýwu.Q›×.¶´nSÉÁ/!5!仏^š>qĚ”YËŰŚ–çj˝µÜFłŮÜR·ińíâg.ŮhIďôt‰A"+Z·˝’ľqŮ-±ĺ—eNĂQ"_™%ä–^9í rˇŘ‰3żÚµ˙‹×ž¨Î>őÓ© ŕE“Ň·ŽÉě tWQ©YŰô[0‰hÖ¶s u@(‡ůź®ľ¨dČşp&»¸6mé?ŘćÂ_Šu{‡'1ĚĚfŃ=|ňĘ© ßlŮ.×˙ü˛'.łQňŹLžT§·ý´őśaâ§ĚîňîӕฎZŚp*«r#úôlveŃ/öU3ăÁÇQŮ.Ą˘ 9ŹxyóŇç6nđă° Ť`2·éŤýrŁß=PĘ):F4CáÁΖ¨*ßđáÓăętŇ—?ě»cTxCC[PôČŢűŐ Řxţn…Ýţę‡BÁÜÖŞ ÚŞÓM–ÜIJ‡ä)Ü9oÍH?azzŇ´©zďř{ćĎ`ĹNŻľt#ł`2µÔmZ퇓şEŚ´ĺa{áˇ]ű˛R—0Aů¦ç|cpËš/oĚÍ{­$cMž VzŽq˛ŕfť;ů„„'ťo6Ě’2Ô% ݡřźĺd‰2ĆS§ˇÎÄ(č›Ěx´šrîË:9ăbýUq·7¸i‚îđ¨űŞĆ €pKtUß5ű=5v˙Č{|h/ŰOB›$IŚ(»öEˇÄč33–^ĽđűĺPżi.$r‘Žň c—˙xŐŘÎx'®ýB‹.ݵńN40BJ™-Í«Ľ{ę¦XÖ|ů±ëácÝj™Ő*Ź1Î0‹PrŔ1‹]‰Ll‘–÷ŕ őĄ~·bžRÎú ł­ąfíľC8yúö]‡®QÇ;Âüĺ]ů¬\űöm3žMжĺ#Śăă<˝9ŃThh‡)ĹÔşaש_ěřQKź j·uŃÁ»˛ŻTHŕ(ô͵źćU-1áŇŠ…/ ŹU1ÖŤĄ2YZ“(0¦ř­n˘ü˙8 Q’ÚL -°ěµ‹žf“ yýăŻçĆ W­Ż•@$ ž1ż[=ď›wţ~ŐČ8‚í_|đţYźî<đÚĆ™×ęë‰R5XäH¦v˙ ĎŚýăSĎh‰J}Łůąne;–CbÖ9Ŕ R»RíâB-–i óBŻPtą–`)á$đ ‰nih»tÝ3›i,ĘΩޒo‚#ň™,ˇĐç˙Łý«źŰZ]UňĆÖ«%K ´^Zî,Q*Őu•9{/¨ĽZbXôX~2ÂD‰R€r˘Ä(łś×+úĂe§¤+XŁLY5ů7Ťë%I"€ÜŁ_ŹIJ˝ř1żä)÷,0JőhúÓ1yťŮ¤3 ’¤PÝ"ŞşŔńD4›[›zeěW˛řą mßqÇĂ7Ś?żAĽdń”ÝýăĂź˛;< µz’Ůw-Ň‹żÜuČÚÂuŕ’gůJ•ˇ6祫\łę`®vʰ±rŽ}Ó!ĺ5 ÇA4wÔ’Źľý˛Ďf_ýaź–<ŁśBˇŕ9Ë2m͆?G»ÔAń“G„8 ‰Z6;™—Ú%…Ó°ä1ĽáZ^C-ŽWJÍĺ«'Ž Ýž«xőńŐ2Â~dŃÝ«3ÚUĂ»őݲ0˛©ĹqÝ( vđŽš3&ÉAÉëkr»÷«ëٵRĄéě{NămŁgˇęřÂyódą7iBfÎ €rśě%ä]|[Ýhr™9}4L­DĺÜ/7Ä~–ŘÄvD[p÷zŕ¬qÔ×Č®<’ß4%iśC{}^ßýęEé,žżK9±îŐ řő FĘ@:r€Z·ť%pQ{6T”6ÍWĎć”y©śđJ‡NŻ^\@­’K $ťöšG@TkcĂŘ%cJĄĆEŁk¬ľ”Y łŢ`UÎ^OaňťH{4“žłJ íxŃ÷7v,ĺ׸éo ‹C´đN&2Él9Gc€:Ń=éăÄŘßËg0µšŠšOË®2§@…©Í ž“ _,¬Żá†˝JIdL ™ µfĄź €÷m>jb¬ŘÓË@&żšwbFfţ'Ť>+ĽSUş(’|ť¬Ěȧ ŇŘRß7…„#™Ř&4m4›‰&X ô‰0źţüý9ş ‘(‰$‚™Í—ďĎ*ŮqŤÓp’ŔŔ¤Nš}ě „ŢÇşđ™YDË ÇD@㯆(čµöç»2Uc&I¬f<$pëÜŰ˙9:Až9qÖřđLľjB)`üă˝ĂľŘ¶âŘY¤´˝ NDüÓńc»[[&Ą*ŁA’LŚ†Ň¶v''łĂ‡“Ö˙•Ę4^=qhäß<}±t^bâGŘ»Ly­ĄŐ ŢĎĹ­S÷˙XAfG„“ e¦"„ţł„]Éd6 ’@dxęoď¬ě>eĘŚŚü…’gŔÂĄ‡Ń’mýňu8íŐŤ‹§úÎą˙÷fĆ]­-@oŽ&ů–]us­§«ű–7žÚ{Ů4<Üż¶ĄąžEÜ­oGţ!™ &˝e¶~ˇŕ˘»OĚô´H0‘rĽB©–6{ď±sźĽů„ĺ„v”P0´´™Z®.›1itzúä‰ă7żůµ$‡:k3I„I“(!Úg7Ě  ~äµmK{zŃÔpé˝· HŚ`’$H–aZľ‰Ú;=<$f±IÁ;¨ I*ž ÂŇfíé—ť{[ÉňDQ´u•oßü*0YsĽ‚ ˘N×p`ç7µF~hdĐm(úU` :&1őWÍÄ1 2·2V€, Ť"˘6ŻÁ.C_? űłeůMä% 3‹LÁ)ĆŤwŮx’ş2PxŻ\zç©ď¶hEPnŕ‡f2.9†\c{“ Ęt2ł5X;Ą#KäZrtZmI?ž­wYô]~@jµ^bmn*ą\Řp˙Ă&Ĺ:ó®a çL#LÔp¦Íëîţ®Üeç§o) ‚É$@ýŐňꠀȖóÝ4Ľ[ßĺoä^šÉd6›E€öÚŐ 9×đ»îĐÜ^'Ć«]ôPÖçĆök]9oń?ąKL~j–6ËÖKPkÚf“["NĽqÓşU}p—%Ň»5Č3ŻŽíLv€ú¶Ć M"xbnÔ_$ôÖŻŢŔÖów”™M&Ád€ő} íHLR*\QÍÍ–{:˛‡”w“Djtrńr!q ó÷¨(+t‡W—×$ť~žRtSĆ=őÎIá$ &€PIEfŮS)•Ž.ŘŰţîĆĂŚüžç%‰Yýüf,>˘÷o,Rî©™ľËCÓľ[Ľ}Äř˝Ăťx€„?‘ş-iâ‰Ô”Źbgşpžě3µxbúÇQň9ńÖ¤îNńjdÜ› öwôxÍŘci㨏Óṟ̌qŹúzŢá;ůŇáŻG ˙0aRFZÄ2w€ń]Jězo„'¸H÷[ĂVřD<=á`˛{Źc ükč;Ă’ź ˛üćiŘć^)”׍~+ĂFoO  p؇O9=Ň=”÷¸=pFůřŃ[âţ=âˇ>S|FíN ľMQ@·ŤŚ]ĺĺ6eČäěôNš—[âx-H}ßŇwß•ˇŁ>Ź8Çdzş×ňŁŕŤcúÁđ¤g›ë¶@ąÍcĆî_¸ xÍňÝsg¬Ť:´ń‘őgçNĐX’vdŢĚMÆ·hQŢňy±N<8÷-łn{fDü÷‹ďĘX0#€8Ź‹wŻ\äŞrôĘY{ß;©1SŁc«×­Ů:mâsćT¬]őL| Řąxé?R"aŤ-ćä¸óÎŰ7% }fü¤Â• Ćą¨z’)O^›5wűřdżĆ"ľŕ0#6qßś‰˙›ŃdŻőúśy_ŹO<ĺŔ/ĽőQFVA«N›ńˡUÓĆŢľô)ĆXŢůźöőÉŁîš>đř%§îă—VŕxµKňÎÝţúü¦cWŞJ/ě unúŮź|çúÇg–Ö˙jË–í§´•—‡9á:ŁGÜĘv,QŚg|˝co^IMMIŢľď·ÍL \>ŘźĄ­.|vóOe<ńĐB)óbŚ]=öą §4wť\" Ś›ž_Ű~úŰ-Ź®ěŘŮĚ ÷Α±ĚXůdKkÓî]Ű×/ë=éřŮŚ{ŕŻ|ÝRS0}č#ď|°·–Uá”ć?úBCĺGB´ý¦;=ÂÇ-®ÓKŰ?ţçß>üRloX1-@ĘĽľpŮ)±ŁD~ëţÇ-µ™ßr ;7–hoŹý+/űě‹Ď˝đĂŃ+˘®|ţ0XÝׯ ÖUyĆ‚÷öś?ôÁ‹@oe´ËZřýCŮű?x~`Ř{ú–+§NXrďËW2Ź>żůů#—Ë®U\N Ö@ťřĂŢž$Ü•HYô{ĆĚ+'†č˙P\gŽ|x޸ĆeUµťúúŁ7ţýc-w¦FĚ_űFan†,eIW1tŔ˝řĎŹ-µŽ|xÁD j×ţC}z¶˛ ¤Oě[Y{Ďś$tłĘqj%ŕŮOĎŐen#ŃŚ¬ljhVŚ:(µÁĚ>űËúŮk6HŚíٶu÷áS&ÁđĘcr|eﲰËů‰Kď}Ą 7ł÷ľďdŃ ~ű“LąäČţGĎ Ń(-žßF8ćRą®Ł_«g&ŘĘH~5ÎŽşXŢbĂůđy÷˝š“yÄ"÷ňK#˘|ڰz-Ů)­y쟗϶ęFö°`đ‰UZC_ÜظÖpdvAޱhÝËŚ±\yD;vpŃäx¸'^îŽŘ˛jJ8܆eWµöÖ/{&ŃÝóďßą}Z´Âf7´łË'n˙ćŰ}Ž|řĘF…¬A˝´#/wă§ßÝ 7ŘúчźěeŚúę_Ď~–Q›ů­<ÉóŹ›UÖdŢůéżţţń·BKŐÂmüS‡Wç tńó@€ÍňKćĄűŤž´ĆĐR3\ (jÚÄçť@áv"·6űôżżř|z\lüOOž8˛éě1RćYF4 ‹čëˡ§ŇĆR¬:Foô&…‰D¦ T;ÇhĚąÍ…Ż–¶”šŔSuŕ™ž8€IDATÚT©oüĄŮ¤őąmújDfŞ44žĐ¶@ÚĚÚĚ6ç4W5ľ|µĄT ÄVłîJ«ˇŇ Í1p ´e·é+M¦fÁTm¨ŮV]ţ˝Ö{ihđ4UŢsĹĆ6XžVçĆ»ĄşŞ”RÉżJłŤöł‚2t,ôÁݱúöF ”8÷Jˇ®Č@2Jś3ďí(5é‹˙^ŇxŮČ;púüV]ˇAÔ‹¦:“.·­íŞ^wąŐ¨"Ŷ+­újł©Ádş&j Ő[«+~h%`psq‹PTďşĆ&Bp“éaşđąĚLřAâŕěu·GŃËĹúk"!ök@‚\]uMĘ«ëM¦âĆĆüV˝$IµşćĂ•YZť<\\=]ëk^8qöB‹€y8;Źő÷Ż®«řÉsWŤGĐf6\jh¬ĐµśjhŃpČ«o,еÖڵ­-_çdż—_1+~ÔCaN8ňKµŮ"LQÔŞ4?*üíÜ™ýő­ÝĎkĺ=< ÜÄ~721«¬¨¤ÝLđVo°€ÂŔ½}ŇÝľ((•ţÇV5” ęńâ°ŕ—Žź,j7sČBBŁ˘Šł~Üs°¦ą)?ă|imSye^楼ť¶¦¦ęĐÁ#Éó˙đÔ˛čWßWŮÂä<[‚Ůč=yŇÔŠÓ?<ôȦ‚ý`d:g”©ĄîđŃŚäiÓ=iő¦ןȭ"”ö›Çö·jGNíŕîěë–ůËÁă粴MŤ™3«öţô“Qĺ1}ƸęK§>űňŰšĆfŃl¬©©Ů»ç§óŮEňĹQ0ÖÖÖîÝóSfvi©/Úwč\`Jʸ”ÄÜŚ#۶˙X×ŘB łĎä—5(”ŞÚ¬s™ůź€ń“&ű©ôúăĆ]§‹fÜNĎ–ĎgI`LĚĺEůgÎ]´^ţęňMwz.1¦˛+§ł RŇ'ř9‰/?űä—?ťe€ŮÔ®^Jě1ĚĆÜ §Ď^ʡJĄŇ6¨3Ďó|×(Ď7TÂó<'÷%f†öövŤ›kbR’©îÂłO<öÝŃ<Ü|ZžAŐUÂńŚIW›ĚĎľđdć[Šju˝EAU(ĺ$ĆâÇ­üűăóź|čÁĽÚn€)M»řmafĆĄ«>CcG$ĹÔž?üř†ßź-¬#Lô ‹ěđ$ůumü=˝‹.ýü›CF6ŕSi[\ڦ⬬+yÍLś:-ĚťééM_8ÇDłĆŐ%ˇCĘÇňÁ+C""¬µ´…2sĘŠ†DĆMšŘ—gë"‹·Ő¶RŽ ‰Śě ű‹Ooút牶C“ä„«fł!˙Ňą3sÖ®«ËČČ(-Î;v᪣‚eźÍĚ))«kl¨.+řďŰoĽűŮ‚ÄâÇ/ďGÝ8>ăB^ˇóŻ„áĂ{é{sAćąś˛ęС1őE—äZç3rJ«ägfł!˙âŮŚ‹yRKšŁ Ó¦‡ËýÚu °±Ć(%bsĺaë7/=˝é«„ă=C#’“cëÎ~|ĂďOçV «×’Ś‚äŮńÍŮÂ:ĘÓÖ–ÚĚŢąńΧ;EÖW´0ĄŇˇ¤(ë|V~‹N[YY~!+«´(űđŃŚř©ÓÂÝůźzüóź2ˇöŕĎçzí—÷đüÚk™çĎV×iŰškŠJËëęj«Ş«ŻäťąÓWÖJ“|‡Ž¸#=ńęŐFŽ}˝ă/?8Đ"A4s.ś>{)Ź˘«/8t:{ĚÄ)NÂÓ›Ţ~<€`îâŐY?Ď(%-u6cJć‘mßüXפÓV—ś8}¶MÚZ~9z¤˘¦I2iOžĚ9˘TqYÎV6č:®őäaUUEffFie˝(,#Ú•"‰AĄÔ\íăËX@m5SÖ±ţR]űeĽ/Ëzůi·0¸Nđvt4UínÚő­zżŘ)2Ó«=·YWlę‡ČŢ °§R닟rĆOŻe·9ž]“g/#× =q,ŽÉIBăśśřşýÚAăHźÂ±›ŤĄĐ^TĹäéuM ˝="¬¬ˇć’¶Ťܲ°Hˇ"“fĹ*ÂsÚÎC¦A4˛ÁK’P/ďá.ŞďŠ+nĺY ăn_ëRůź/ö3PJ{¬BbFŰĆlyă’řMŰ!ň]›ëÄhŹŹDÎrÓ{˘îůb}YG ¸IvťĎÉ™ŕGűä'+L’@9&‰Nq3~ó®m73›¸m R—„č×[ÂQ8H×X0y_®žŁD¸=[߲č»`çҡöÝG©8(˛°›Ô^o ˇ%rÖ^9Ţb˙ýęď›~U—BIçő޸ћ)ĄÖĐ\˝ŕéI!é¶ąAĆ^ŻUĘ)n“ç?rđ˝ßMŹľŘ&Ę-ůµş$±oÉşŰĘ@|Ô€ÇćA3ę߬ďĂzę$G,ĺX÷hŬk-JĐ-j% `¶d¶c©MüeN}ígĐ•NÂP03“_1©‡ŢSBd!öM‚CGd9ůY—žĘ1Óas›>$`ŚĂ•?—2Ëń3c¬=˝q¬'O~%Žő©–:G)#„ȡDƬ dă_ĺ;é˛%É;¶ÍS«ß±QŇń Ř–d™=cň} ±ŻťÂ~ Ó’ýČđ¨Ő®Ź;'ü›řŻAÇóň’™"‰‘ĘŇćyc˘śáX~igŘł.t©‡„R"Yr*S&ÝčĚí7n‡PŽ2©‹˛B9ž$Y“Ř)ˇ”#`˘$qĎşNéxžgL%ĆS*J˘<ŘYEÓË=ĺ5ŔZÝ&^©ŐöžěcĽSŕüąSÔ\çşPŽ@˛íţŤ”Ę1Ăáż+× }Ď(ÇSÂA@9®›ŕ~u°ŽÉYío!e= ¸ľ [ß"ĘWäQ¦sÂJ , QB)Äá8*‰âőI€PĄ‚Ěf‰1žçĺ¤é˙H9މ"áx©#_;“DyÔ’±ó $QĎs˝n"ô& ;ŘÇq˘(t«b׾첔r<%…ÎôërŔÉć ¸YtĺĽp¶Ł˛‰#ä–Í,,‡!Ţ><>É+ů-g ö@^tĄ‡Çü9!đž”âfi/¬ŤH•ŇV_vřŘi“ És>Qˇ! h¨*­n ‹L•ŇZW*3hDˇ;qŠ3oi{o˛°Ĺ>v`scÝ۵YĄĺDáµćž…™·_,jP(˘ŮĚ»‰Ť QRô-ąů…f’$Ţ,äSC|-€)UŢ“¦Ťwˇúcű׌îľÁL0Ë3ýš’âň†&ĄzȤ©ă\¨ţčţCu“lţC|˝\Šrň{“ fŹóˇ‘ŁÓěŹoă ‚|6S]ś_ŃÄß»nIĆoză†Ń,ÚĹ—<2.˘µ©ňçą[{*÷B.™<}‚3‘KŚśÂ%6.ŇAA@xŁľúň•2©ŹMĆ+]ăc#”JN0ęssóÚMšďâ čX9‹Ć–Ë—óĚ7SB8Jř¨X«‡´i‡Qžçc˘u)B(•ďęSBäáŔN‰uLEI’$"rčQ’Ą”ă©ÄD&Aę‘oÇáýcßN¸P·Q»SŇJ{0uÔGQo•—ü_Žpźĺ3ţdÚď“Ć•üJ¨ŕ|5I_$ŤŰ3běĎ)I/„ňÓŹ šć€*÷Ű}Ç5fgҸé) uôĺ;T„¸Ž90Ĺ©Ł¤/)SđŰ™ľ5VţV~¬§ŠpI;–ň·0y†řHŘ„ŁŇ÷Śűm‚“C—„„€!KBĆOł+yô÷IOŽ [ě¨ŇŹöO3ďĺAŁ?‹ˇčýͰď`€x;$~2|ÜţéűRŇ>ŤqôĄŤřăĐńGSĆ9ěŹ!Ü`qLţS­NÝťąŔÝ.?ĺĹëSc§ćß˝ôŠ»>7Ň©‹;•€ż»ďŃ•+÷NMG'÷Żć/ČZľčŇÝKżžę €÷ČXµâţ0/JŽ{yőŠłKd­Z¶oÎäG%¬ą,Ý\ýNŻ\ş&ÔßßCąĘňÔń™ ¦kě‘÷«‚läs’OΛބ­îüO€śËđ.ű—-}.&żZ¶PŽăL]ţеÖö⼼Ʀ–Cź˙ÍOÎK9ŃSK´wOš<çî±ë77Ď5Y_~wgÓµ†ĘęĘťďľâ|# ßşvägŽŃ—ä—ו—•””–×—® `ц«š róuÚ†ŤkfŕűÍo›“ŽPĽ:ŕĄw>Č«lÎ?ňą°©^ČŻáWnř‡¶U[“WßT˙ă{ŻúuĆqí«ÖšÇ?(Í=¤&T{Ż”|¸ů”żáĚ?ÖNibögVW”–”•——–—V]8˘ë;™Aţ6XÚrŁ,v94ę}ÝR–ąÇ±·ř«ż‰ŃDe–6ľ~˙°> ľ‘¦ä7Öń“˛kÚĎîx—N!ĂöťËŻ)/©nlŘ÷Ń›€ /˙[ŰŞ/)«¨Ę99ܱßäá|~÷§Ô4”fMpvwŰÝeő-W‹Šµ-G¶ýĂpNěŔµ˙żsCo{YP |'ö8GKŠG_z§ďvşpă»w9€hF”6´l\8 €J©0mŐŁµÚÖ’‚üšúĆs‡ľMöŔ9F_Ź,şó'ÎI @ĺ»ă—¬şę˛˛ĘÚüł{‚ůy÷żÚ¨kşZTź—ßÚÖň§ćAí·ëtŽüMΩÝńŔëńß¶´ŁQvë”üö=fĆŞÎŰţ¶+0aŮCÚ¶öŢĆ 9!Ë’őoÔ7_“±·µµĽđŕ\ ¬˘©­/nąŮĐűĎŰţáŔ/ćű_.[{±'ÎW ݍť'¬%§÷Ćx.|Ć•˛†˛’âŠÚĆKGľŇô5ČňĘÚŞňÚć˘ÂüŠęúĽŚŇg˙ËĺÚęňNĎvţ@چďO»Űšy~¬cnÂş¦ę¤W^Îőźt*Ő͇O§ńgFĎqć<']í—¨öš4-wŚ_şĂ`qL^ő<5úłˇ=»-kLx`TÉÚUˆ8Ďš¤}hőŠ ôS€ţeú̲{Wţx[:€ůĂĆ\]˝0Ć‘ó -ĽwŐĘ`7ŔůĐ]‹îö032j’·Ó”čěĺóyâĺꓹfŐ+Ă"a]đ<”>ő➆8ą®IHxtxüŚ€!ö„L€:í^˛äů¸P\g¬Ş›™ÓcwĎžđżM^«®I›xjŢ9÷ŻAˇÁ&vÔřűWßĺ$Í~Ľµ˝uíĚD*ŕń·wŐdp˘RÇŻ[˝¤Ű7|˙ŃŠúyśžyź^2Ţ“9÷Ţ?1&­ťťt˝-ßúv”j•›_đ_??ţËׯ„úyrQ3ŞZĚ˙÷ú©cg§§Ćˇ_­&€Ę+ěŢG6?˙Ě#ăĂ(xŕ±·vgü„e‡Ć'8ö፛ź|âńąÓŇĺš}´*ŻOîŮüQeáń0O§i«žÖ›Ú7,™€ď=ňڵÖËň–W5‡ňJ˙űÄZÜÜŞĆÚS>0"Â70ěĹĎŽćţüe_`x„˙o•§j°´ĺWĹ.Ďu  9]XűĆĂwá&Ö7°ěń÷őÚ˘„ ‡Č‘‹uFÓ=Ó˘\oż¦×=ą<@|ŇHOµ }޸<-sNwâÔŮŰv”dsTđŤ›>e4€É ’°ttĬu3¶\•qµ™ŚKĆť˝îÍŽ’bď) .6؇;«„Ť[RŻkîł]¸q|Çű Ă2sK;Ŕť+îť‘:sőúşŠĽ~Hô¸ňĂż7Ż‘+\=ů“äâ`ĘęM}Ó¤@Ţ'­AozzÝ€OLJ öó^đŔ¦mĂ8_nä]ĚíZË7í“÷L‚˙Ôłç2-|Öt_ŞÉ¤x†ÄvpŢ$k&GűĆŽ|čŢe˝ŽTn1 ‰2vmcíŚ(ˇňKűĺgőH=±›%iezаů Ćfą׌ŇĆeéńs׉¦–.ýňžXTRĽ0=ĘŮÓ×ßÝ˝_ë’-7aÖ}őőUK§D ›xş¸ńä7 ôň Š|éłcől˛Ű÷Ť\°`žĹ ÄFzű‡wxČČ@^Á±lŘôÄcĹ…ůȵCbgĎš–:ýÎgž~rîÔ4Á1ĂfĎžn)™2 €Ę;Ü2¦$„ĐDÍľăöił<őĚ“·ŤŚ—şţą·+Ę+^üţ•«V ň´ŐŞ>f|×Řwý}ÓĂN5ýnX\7P KřŔű‚"VűŞÝ9Öayň˙K żÜRőă505Äv‰đÎ CˇN_+µ´µ7KÎ!É(ůŢ:ěϡ*L`Pr®“=Ü“]ÎÜ^c ™ńuň›á\ýYµ™¸¨c_4ĎÝ}ĽGřúNeÚFä\ŁR-ér[ Ćŕµ(Č#Tďiáť)÷Dg±Ţ@ĂÜߌvô$f#ëŇ ą+:ˇ˝Ho¨7»Śň0ä\»V&BE ŘÍr;–Ô”tÁîČu|`ű”ÄÔh25‹Ç µ¤3ë+÷s‰®ľ~ó=ĹvÉ)X3XcŚĐĺč8/'M—CŞŽżG{ ѶÔďn%ˆ†ëÍbś«F:©ć‘n‹MžčLż*ŞuRp\••MumÂŐkŤĄF!ÉŐ(%Q¸+)mËÄ4BŚ˘N‘<ÁwÉh(im`%ÎÉ{E„ĎWYŮŤ śĘů­©“4>(ôÉ”'®»ĺ7Ď!Ľt©ˇč?[đ`‘˙÷+ťÜ<Č ťU_«Ń¸Ć*(~ťUŤ|:ç̱˙lŮŢT×·éÍ2*łYäGßżdüGŻżŃ ź?ńá–olľ D0~XzSÁ‰ďËą{×.—$Ăđč$ŇődąőíH&ٶş¬©U0éµ%ŐŤ"ĂäŰ&:čŻţë/˙qb÷‰łWHë™?E.!„‚1Ţ/nŰ®6Żą#uŇÜ;ľť6,Ä,0śZĹÉK"&—°˙nŰţ»{ď:gńK/>éâ żŕ%˝´l-ccecëÁ?_ÓKNî.ä}Ö꼊 Iť˝{ëEß%ťŔ„š’˘šŠ«MZ“ ŐĹE5·,4Č Ků·Ä.čÄŽôŐ2΀_)ŐC/ p!㸣Ě0'…uô¸>ŕx"1ÜńŔ‹ÓĂź|yÔŮEŔÝͧôň/9ĺí…YWµ­ńÁĂgŽÝR] Šťýíöm±>´Ń`Dź!ĺŽíů{ŇG§ľýă…J-I€†’+‡ŽÄÇ%&Źm¨)­ilöö čŔU|­udôŤűŐ+'Ż{Yp”čm°"€ÉŁ'čŞň{¶Óa=¸áDBĎÁĐ®_ýô?w|ňOžÍTÎăćß9}Ü8ٶ®°¬L¦bರĂQP_[Sר‹ Ýv­¶Ş¦ň._,«®5uaYĆwÇkÄöfmMÇ7ŤŐ5 :TLMai‡użĆHciŽ•ó!uUĄşv}MNƇźÚ/,Ü``Ô6dgÉŘË3wě/h!”°~ąŃáŁzbŻŻ.»ÖÜÔÔĐPŰŮÓšÚš:mCCMĄDßTWYY%O<6<őŇ+›â,páÎ šÍÍĺĺĺ—Ž8_äă=äZeCmyaSó@=›| "jÔŚ÷ßy+„ř·?üż3FÔ——U•wxȢ > ~ŰO»źß°réĎěýiGr„€án۶kßÖ÷_˝ků˝Ż˝ţ<€¸ôéßěÜ»őýWďZąö//mrŤŮşcçkîHť4wçÎďdžąxGŹţň›]ď˝ůÜâU~óý¶aîŞĐÄ).›.Mc¬yěˇ{b‚5°‰EÝˬ ‹öM=dýż§ş^ŕe’Űr[Mf—™>>ł=Îß›ÝZŐýa%áäŘnŠČß‹ĺ-ŤŮ÷8Ł:ÎÇ3É‘ą;xsZíĚo]¨Ú_•˝öríEŁG(…’ó_Ŕy(ś +łäňYčŁĐę«vkPçHeő?JŠľÔ*ś9ÉlQ”n öá)… HfĆů:†Żö*~!źŕÂ03TAŽî‘ím­,áíŘ+.×üŇŢńüĆŞ>„IŚtńIÓTż^€* č%˙ĐÜ ăü:±»tŘ“­UŁíLskµýN¬Ŕ8±´Ąéb»w57 Aë‚T}Ó&źŔ ÇŔ č$ÂS•?Ń2»Ż.®MkG$ű Mǵu÷Ŕúü—Ś9:y>5,쵣?;ů&ŽwejôÍ!ŢŃS}}]Říʢ+v0%˙ČäéńWě¶Ó““#Ěf€RŞk3Ü˙Ç·CC–OűĺÉâ™1*˘r]»é—!Áž\ĂŮ‚Rąýë•…-…“dÚwľě™ýxPMó•Ăűöd€p&AtźĽrj‡ż{@ÎŃ}™ĄOu~ł7”‡$Řđą;PJ%IěŕüĹ}_ütŞś‚Y@÷ń˘Ë<“RQęÄÎmiíź¶{ Ý±ď˙ňpvk»â猫:ą-W~>zĽ°JjθÚb)É9˛o_ľ*2řRĆ©ŞâÚ´Ą._1oň„ÉJ)%ý„ŕcS8­xęµĺnq+ć§ü퉷ÚĺĐK”\—gŁ‚ą­U'´U§7š,ą‰eÉSîś·f¤ź0&6)Ű84Żčě=óçśc‹IÍ-u›ßţ]FaHtł`2µÔmZ<÷»Ś"g7·;Ö<9s¤˙+˙’YŹ×?řď测7}_'蛟˝oÖ—|‹Š÷Ož9ň_ľ:öBÎŮźŢüËşé?dë9Jt‹ŮcV`G‚ů¦‡ť2PbYó卹yŻ•d¬ÉÔJĎ1NŔ“ŽLdL¤‘Žň cYO_•€Ć}µ•GÚb˙±ĚMh‚ŚńÔi¨31 ú&3äµP«)羬“3.Ö_%w{€›&菺Żj ±DWő]łßQc÷ŹĽÇ‡öⵄ6I’±ÜV…ßň Ť‹D}ÜăTÔÝÁs#U±±=ď…yĎ^+݇şíŇ Y»ďŇď źŁ˙T7±É¬Šp Hw(ţgą*GíŽYfT`"[]’$ŃÇÍgM ë_Ďf6SYv O)G`i|R¸JT9ąŤórRŞ\yî)ĚýżŇ¦·o›ńlB€¶Ý(‡açé͉¦BC;d'bjݰë‡Ô/vü¨ĄĎ‹µŰşčŕ]ŮW*$púćÚOóŞ–ŚpiņǪX牠EÁZ“(0¦ř­n˘ü˙8 Q’ÚL żâ 0Ńl4ŻüőÜaáŞőµ$Á3ćw«ç}óÎßËDpř!K. vű˛°Á.Ď4”*CmN÷v(÷˙±÷ŢńQVŮ˙řűŢç™’dŇŇ{! BŻR•ŢDD]Áµ¬mŐ]ű®«®«»ëú ‚X芀ôšPBHB é˝Î$™ú<÷÷Ç3“L2“vŻ×÷Ľv}e.Ď˝çÜsÎ=·žsd2Ďq¶ÜpµtF2/jE™jPňH^×[W €ăĺbséŞqĂCbwćČŢ|j•™ź7+ + Ąä’3—=’ˇ\3ÖĚąKIČő«ćH1ag,Zŕ¨/üvß3Żłţć‘Us š:óą+˘+Îű%O|`ÁF˝IŐuľčl(aíŘż6»™Ü©WnVttĆţdŕĐÉ & J›µrt´ŁÔ .|ŘŠ9GĎZ>z “T‚ठݧĎ˙uŃĽ9ŹnÚ8rĚÜFÇiSFŔRąG`"‡›—ˇľđŃŐ ź˙Ë6ŽŮÍZ6ó1@H{öOK¤]\”žuyĹő@óŤóŮ%^ gĽÜˇ¦<űçËůĘ ‹(RI¨ijđj©Żµ¸}N)wtqÔÔW^ɨ„±MgÎ^OaŇňŐŢ%•ťU•ą>|cg¤Ü‰—ţ–0ŇŃ|í¤LtKúOběďüŰq†<ć3ŚżřP¶¦Č@(ЬĎ{:űä´Ś+Ż3Ftőz€đTĚ5ż¶ŠôZ(D1“”ͨ«6ĘýĽďńQ}Ůz‹¸M…oćžššqýózźĺŢ) XE,:Ć=#ś"ŘěFnjÖ·”üůŞÂ8â¬0Î٤3¶+™´“éÜ ÂÂűŢăˇ>ŇЦ–bׂăXO4űXM Äl:LjkěJďń®ťq1J_ÖŞŻ=ŁQźklmf.ÁrSł÷W}QŇĎT2C« <×G%S[“Íz—@µAëˇtřîJúoŤbĽ‹cťQ x@Ŕ¶Ůłţ6"ҬoÉiÖŻŕ&ăäNłBe†Ö?úeĐ×;–?o)Ö¶TE„WNßß"Ű:>EčEDŃŔ +nŐú¨Tf„ &-źK4ŢqâÔôëU29Ď€ůK ŁEnűDˇË7”Ň[O+aé•]esµ§«űÖwžýůŞap¸µşď´é‡hĐÚĚ\¸”wŮÝ'fJj$@9^&—Kťńóń źżű´ ĺ„¶—PĐ©[ ęK§Ž‘–6aÜ-ďnĄPg­‘0`"%¤é…Ťs‚{kÇ’'ž[0)@DZ÷-C:ć4uĹďĽ÷Ţ’e›BÇŻŮ´l ŔÂÓfvW‹‡Ŕ(4ĘxDQÁS€°ÔéşĹeŻÄÎÜećž – ®Ňë›;wG[n úŠ]djón‰S@dîf¬I<„E„¦Ü:=Ě ­›é)cij(şš_÷ĐŁŹ$Ĺ:s.!óî›!k.Ý´p’_Pŕ̇~g`˛âÚĽ2F¤ůśń¦ćŁI4 FQŔń2f4šş_w}W­ŁŁ ÖlZ<Ů/8pćCż3äF]ĐôĉľÁ7˝{YHŘĄvőÚF“€öv$7ŚPËŘáEĐćĆnđ®aógN&Lpä [ÖÝ˙C©Ë®/ţ"t&Ńd2LP{Ł´Ę'(DňŽą%YHš  Š5hŠvţzúܡ˝9 †!á °bÉ}g~Ř‘SgÓů›0©“źmˇçő|Ld€?üíďíóĎó˘Č:[ * "¨‡„=ŻŃŔ$‰Óo^·˛nđ’D}ě5ęah-ţţ×ÓçíËmÔ'GĆřGh5RÉŢśĂđp&Qzř#°Î ö¤Ő Lßô×g|đˇ‡˙ąő{`^ľZY¶>í9E&Ęĺ®N€ ä ŚFó;©é4I z•‹— [ż—‰ Ŕ“hŢtK{&ŠćJŃuNýěGD™J4¤Ŕ¸˘ HĘNUČĺN.Ž–ˇÚ lWť%¸EłŤčţó\`;Nű|—…¦~¶*0yç1;Ë8Ź÷™T8.í?QŇlŔ‚ŕ)ĹcFlK{=jčgq†;‚: ţG\ÄĂţCľš¶=V©äNŁŽĄŚv >Ş1Łâ÷őĽ×w•‘ߎüĎ„ńé©KÝnČĂb7x <ŔEşŮš¶Ü'â…豇’ÝxŘĚŇŻ J~!(%RаĐ×Go‹çá?úVDäs‘.¤řŤqŕ<ÁÜ ónÍy’ßÄĚľC斜ҎöHó2OéKŻ…)źDµ)t`˙&J"O5΂‹€ë$ßqGĆżůBÔ„K©AăUpP¤ü’2äÍȸwĆěä𮣮÷Ç2đÓÁI/VĎ!ÍęHPG·K—ěš4ćĂ)3ó–ß—ŕ,0(d`ýcÎĎ+7Ź˘ Ŕ“ăg]ť7ž2ąçÓ§>?$ţ§…‹ŇçM ŕÎăňý+Vą*śĽ˛×>řQJ̤čŘĘu«·M÷őĚ™ekW>Čv-\ňţ°HXb‹©<wÝ7ksÂŔçÇŚĎ_1o´‹Â†Ls¨ę·¦ĎŢ9&ŔťŘÄ÷ˇ¦Ć&ţ2sÜ˙f´Éj˝=sÎö1IwŽ@ÉyÁş×c9O}őĺöÓÇ~]4e0ŕq2»ć?Ż­ SČ»|sęčÁE÷¤í'L·’.őŹĎ(®=ńíÖ­;Ď4•_¤ÂMFʏ›íŁ%NÝţăĎąEUUEążü´cZb ŕňéÁ̦Ęü¶<}čLúÓëç6w=cěĆńŻ89€¤Ůë¤qS®WkĎ~żőń O?ź±qÍL ËÔϨ[÷ďŢąań(UôřçÓ_~âá?ş]]•7eŕCď{¤»–ťxxôĹí-µ]•đî×çőĄĽ¸™ëşĄ‡@řč…5mâÎ˙üí˝~#hë–OŽ0lN·¸ě—Ř9Á ’Żű¶¦Wg|ĎŮ˙ć.AiËĆ.­XđŹú*Đ»y›$­š¸OgüôĄŰÁN(§”s^řâBMĆČ]†î:đëź_Ú|üZEńĺÁrŔxf™ćĚöżó÷­ĚŘ´jZz±-d/˙ĺßé™y-š¦ô“‡VL·dÍy9Żľřňžc×Ä–ňűR|ÁÇíýĺP.%¨SŇî›Ćn# B¬±ź;~čŃ9cá—YŃÚŢÎ3“Đeě€P®+7ăĐňFíúé1Ę ”:#űňOf¬Ţ(2v`ǶýGÎLş7žâČoF]ůsáäá5÷ŚNžĽFËŘ·˙z˙ťOľ™nËâIRmf̸bśy÷2bŢĆVŃü cú§ćŹđęźgH==zđ±…Ó` ‰ö؉O|›u^â<ÓVLč5uĹKÖóĹ’# ›cme†Ě˛;8§ä˛†¶ž¸±ia/ŘŰ*î‰÷Ž»çakď…qýÔˇ “ÖX•ś2˙?eg{iËKGŻ–4”^I ÷DŹ@Ú¦š˝A0µ,Hv ó„ž7cŮ̱ԦÜ_×füuŰż˙ůůĎŚ±Ăß~äKŕ…/Ó«3ľ—yţqÓKŤ»ľřđŻ˙ůޤ®?$Ŕ‚MŻÔ•_’Śin_ç‹˙c»J ôŤxuPđk'NhŤÜťaŽäđ$—;d^ĚĽ®Ö4UUU>t4yîďź]ýČŞËŐLĘÄfýMEEYĆĹô’ŠşŰÎdÂ(%˘şćȱôäÉSărFE]ÝĎűöéS¦Ž®ĽrćËoľŻŞoŚúŞŞŞźě»U ™Á¤Ż®®ţůŔľŚ¬˘®-řĺđ…ŔaĂFKĚI?şcçŢšz5%ČĎ:w˝¤N&WTçg^ȸîč0fü?EŰ+Ř´űl“Q'µcŰňŬ‘Áh4ä\ôÁ°UWą÷ŔˇŞć¦ü‹é—róťx% l¨ąôÂÓOüx˘˝Â"͸Űś_ßFD×Ícď$‹˝[ Ş[(Ç…DFš±75ff^Ë˝|řXFâ¤ÉaR;»NŮŚÂhÍŤs—s¦ŐÔ¤§§ćżtĂIƲÎgd•ÔÔ×U–ä}öÁ;ąÇ$˛ř1ËnZVü©ll,ľvĺđ‘}×ËRFŚňwćŢ|ů™Owü,0xxĺ_9öŐw‡ĄKeŮ2rІŹíďĚ˝ńĘ3źl˙Uää!µ>ç]LĎ.®°NĹĂ´Z­Ł›kbR’ˇćŇ‹OoÚ{“ËŤÂ+óEFzqy­`2´ŰCiî đP`Ćn~ŐŰÖR›Ń=7>úb—Ŕ:Ý-tÁţ‡-›öś˝Q[pĺBÎ s/^ţýg?­ş‘yÁŇŻ·ţřĚżvaĽĚ+4"99¶ćâ‘§6ţîÜőŠ>LgĚ$ëĘ Žť8Sß*Š’N–­W±PĘč;pČ˝i‰7nÔs´`űʇy“é·_U‹ úěKgĎ_É%„hjóźÍ9nb ĘôÜćGwžČ`2K ®ź»pY˛Ś1ŃÔ^Â(%ę«9%ăčŽďöÖ4jš*‹Nť=ß*šZ[ęN;ZVŐ(šNźÎ8"Wp™—Η×iÚźqŮ® ěHđZČ ;Ţčáó\@­Ç©­ţÜ&űe·Ů¸ÝŔŕ:ÖŰÉÉP±żhÇö OŘ)0ÍK›Ó¬)4JXűluSds]¦ąî«Ž0y- ڏÇéüę\ŃÂ6Bn*YÖ?o‡cR’Đ8•s_s°©{z:áč®»í ÁzkÂü¤S#Łdm_T0iCzSë\BgE„•ÔU]ijĄw-,G¨ŔÄéńźŤđśĽë°ˇY€9I¨—÷`Ĺ…ewÓA’ÂčY«c]Ę˙őőAjI }çüz:ńţÖ%ń_m‡H)ÎnŁ˝aE¤,7Ý&;ży‹ÝwVtNđG!ý•Ř^z°ÎD”c˘ Š›v軏=¨ÖČĚ ”Ł`ćÄç¸ĹĆĚAlxxŢ„_rÚ8J„äŃ_ÚrkĐwěTľ`Éâ—~KżVz;©0oŽ:J™(Ć 14Ěă›űn3 °ů˝–} BëÚ˙~Š‹bÓ¬Íh»Iě=ˢoítÇ B훎RˇźdA@ş¤Ż—BU1B9óÔÍúC77śĺ8&tJĎŘ7úÔ°Ťś ěÇ+˛†>.©mKŽqąGEÖ7vI)n“ç>~äÓߍŤĽÔ*آ–Ú±Ő_J4·ű˙6oşú|ÔxǢÝ*Hď‡Ĺ›|ĄFşí á¤mˇ„đD4Š` <`ŁéčÔˇĚzç`fżÍ÷¤C_{Yt· °‡ťI—ť{Ń©–͡;šA™Ű”#®ý±ŮĂhË1ëoGČ$Ö1“ÄĎ®.dýñŐ˛}¨´7LáaŚ ÝN¨DF‰ŃěŽÜŐ|PB(`5ţ¨ľŻ+$@dLzo!t?Ćlíě݉9#ĂŁV¸>vü‚ép[ŕ.Fm«Ĺó2ĆsÜ‚öcËîľéô”ćśĘ”‰·şrű/·C(G™ŘIŮ ˇĎAŰź\Ű–PĘ0A9ŽgL°öVâyž1QO© ŇÖŔ"š^Z¦”Ł”H[#B)ĎQ“ÉôNl^}é…Ývşc/ăUsgOTrűB9Ńşű·RB(ÇtGöţPÚdęeÔ_ÚrkĐgěRVű»H™-·ykÓˇrž3Ťvç8މ}ÇóÔĽţ#˘`žš¤Ą0„ ”çÉ$ ”‚‰ ¶Ř‰ŚçM&#8ŽşWŕN˛ T.ăMFŁČĎóL‘q'¦.UěŽ/»,ĄO SÇú^ Ř#Z]ßš,8^F`ĎQ‰Ď‹‚`§5BxŽët¤BŚçMFŁy°·î¤O ZL“zž—Ë8A/2đc=>Ď-ú/Çc˛iÓ•óÇ„ŔöüZz›‡Ą}`E@hDęđa­µ%GŽź5DĘń”0Á$0BCPWQ\Y9Z4ĎŕyŹ +—=ć@ÎQc˘bŻ®Z~~ńĽĚ•K™9!ĆIK.K7Wżł+–¬őŔ÷ć¨"UY–2&cŢG{äÝQđĚ„äÓs&I±ţ§üj¤”!„w9¸tÉ‹1A¸cŮB9Ž0iŮú†mann}ŁúđWďůq€xDO*ŞkşŇŔä™÷«uúÎßÜ>×$tzýă]Ť uĺ•ĺ»>~ĂůVľ{íHnŽŃă_/­)-)**.­-Í_>)Ŕ‚ŤŻVÔ5çĺ\×4ŐmZ=ßk>x«ČV’9Ż xíŁOsË›ŻýJôîż2űÝ>őiqÎa% řůZŃ?·<€ň·žËĹÜ)Çéů•eĹE%ĄĄĹĹ…Ĺ—Ĺ»(*ëě'Ó/ĐĂúK[n•%}Ă.­TüóÖ’ŚNÝĹ_˝S$RÄ1*ُţí‡î…ĹřVš’˘kÄŹĎŞŇž˙ţď2śď“ü ŻŞ®8óX‚‹#€)˵±?7' ;věëżřsT¶đy÷Ço:ŕ;°Ç9)pĽď“ŻüÍ\â(‡=wďN˛Ö$ĽOG-'sŠÇ_ű¨©Ą­¨¤¬"űô`';iÔ;q㇏9€8)®Soš?€B.0yĺăŐM-Ey׫jë/ţ>!ŘçÝwYHţč1SW–ÔŞoÔ7©Űţ×€S;7öýó- 7Ľ[×ÜxŁ ˙zîő––¦×_ ŕ‰7>iďE’J`Óëď®_’ŞČüc~<™YSYRR^ť}f| #ŕőÔ«tÇU)3˝KHâĎçŻW•UÖ×ü÷{.ś’‹k{äF›]ő¤ ’FŢšó<ŔůÇýtňŞ™ćÓűâś{hŮ®+)ŻJ?´ëµgţ^ÝÜX“[ŰX»÷oúY^DuK!Ď|˙ĆZu[—~š˛úzÖ…$Gł•Ţc<81€Â)¶«˝x0Ö©oűj#:g[KRT ű„p[Îp<°xĂ;µÍ íúóęc‹­őłűŐ…őč&çÓëěQăCÜGěNö¬ä\e ަ7RÜ-ʎ[ß¶kĘ‘pJtńźíÉŞŃţăÎ ÷ˇ”Ăw ëŔkĆŹx…ÇlßŃ?'9+‰<Ä9ídjô*oRöÉŕgbF}+“Âşű9,óYíď•ęlŹÝ™bŘîa‘‹ĚÉÜgűŹ?“âćC‰§jĚ™!s]á$÷á Ŕ!ĹwüůÔ€1N@G„hiˇďsř¸cÉN>Ô!ÎsěŮT˙1Ž}¤€ç‚€a›c )“=GźJ ™î Ŕi “LŃŮNŮâ:—꟦„ŻŰ°Ż™y(m„”Ž)?ôǤn<5â˶’—´!<0ŞhíĘĄś§LjZżjylVĆ” š2­dÍŠÝSÓ8ŔŃ=dď}ł>ź41}ńě8žŕy÷ Üě `ZdÔxoŐÄč¬esyâĺꓱzĺ"aŮđ¬O›tyţTOT®«?5`€=! Şý‹żŠ»}Č$VL‰MÜ?cě˙f 4iŻş:uÜ™9Ą&w‚Biv‰>ćˇU‹ś€¤Oµh[ÖNK qžú`wUćŻ* *eĚşU‹»|Ăß^ 4iöŤžö`›¨ 5röšW×ÎHşŮ–ď~;rĄÂÍ/řĎ_ť8ąýŤP?OŽŔ#jj…Úřoo2jFZJzŐjB(ĽÂÖ<¶ĺĄçź@ĆOüeÖˇĎ9„ŕűč¦-Ď<ýÔěÉiRÍŰ’ş=v5lů¬äúoŇ®ćpnńgOŻĹííj,=ĺ#"|Ă^ýňXÎoß„ř†Gř˙·ňTő—¶ÜQěŇ* ňłůŐď<ş·±Ż¸5ŕ9`ý[ŰňĎţd¶·"/)züĂďŹęµÚ?ý“śâçť>sţÇ?–äe V)ħڱ?7% [;ÖfĐ®ä=j™-|f«'F řžsçÓÚńcI^Ć •€kâÜSz9ÚŰŠŘČ€KÂśöZť•ÂF/®Ő4?ł, @|ŇPOĄ]jgnüř‰ €Ă Śś˘‡gpßň5SSB§­ÚPS–;Ä‘=şT­űű–ŐRĺľËBúgĎŘ)Gđ¨AVŹ őJ[jÍŤ5Ó:$ ěç=ďáÍÍŤµ‚Cç6´ŞĄ^$MU#öĐ/iŤ>~Őf][ăř@Ţ'µNkzćńđźtţBFw\•ÖÄË7ÜÚXä9tˇZ«hV<~9ݤWnpÝ[¤nf´A@”çOíţ€±÷oŃ·ÓÜjx~ýlôzŐŮŽ…{¸ş®}ú_ĺů'Ă×fĐn\<ß}Ś/‰řék7×–ć´÷ëă§WH>íˇuë6¬]쥠˘c‡nxń˛Ň˛·źzhĹĘĺQÁ‡ř…·[ŃČ@^Á±oÜüôëăÂ|$!1‰3¦ON™rßóĎ=3{R*€ŕA3fL1—L@ánžwÂ8D͸wÖäéóž}ţ™{†ĆŽK±Ć>0Čł“ćP¸yÄ$$JúÓX_5-f®#^h·«‹öŃ-mźś­F“Ý1ŘHL€ř`PÄ*_Ą;×á7bvŚ`Äâ¦ô•™eÇŐDnvĐo˝Ş®ŘŰ †:ť 5»B A/ú>:菡 ĚÄ ç\'x¸'»0ŤQ[e ńUůMu®ü˛ŇČ@\”±o šăî>Ć#|C§°Bm‘¸0'ąRÔ䨥2C˝ÁĐ,8ú;8)…fˇA@«ˇń˘Î)ÜQŁ0Ö MFk^K‰2g™®@ÓV-jóZµÍ˘sŔŔAěŤf©BÍ­ą'şµ:ć–řn´“'1ęY'ŽŮâj\˘śQŐtaů3Ű=d¸~ŕc€&[Ăy9¨;_Yţá1 I]»ż…,Ţfâ\=0bˇš#Dd¸'6yś3ý¶ ÚQĆń€¶ąlćO{ţ]Z%ŁśŘN2N0-JJÝ:.Ő‡˝ ‚“Ą†Źő`ĐëŠZZ‘Sy/Źđů63«žS8˙eŇ„‡Ť }fX‚Šë*d釣›ç^ĽRW ŕ.÷éLącw ·’’eÖV;:şĆĘ(îĚ®†‰"€ěsÇ˙µug+PYUŘÚf”PŤ<âˇĹcţýö;-@áĹS˙ÜúťŐ7ý”Ć JkĚ;őS)·fí2QÔ ŽN öšďżÚŽhĐ7U–4¶ mME•őĂ„{Ć9´ÝřđO~j˙©ó×"łÍź"•BÁď·c÷ž-«ďM?űÇżź<(Ähb8Ą‚“¶DL .aźíŘůäš{'Í\řګϸ8HĎÖIw-ßR‰ĄŚu<3EÖîóvk-w3UT•Ýhl20QWVYVXPu×Bܲ”˙›ŘĄ ťŘˇľŽ†ô‹ÇŕĄzčDp.Ąźpňʤ’YfŹ›Ž'"Ă˝ż:%\öů7ÇT*%hóöŹ‘ňÁŢŁ2…RŚ˛ĎťčlnlíX›Ö ÓËĆ Jk(°đ™i‡&ŽFÉĎĂS†ţmďQ™B) FÚëűÓ,ôĚŢ"Yp”´]?Đ^ L1VSq];ăűť;b}h˝N/%ôh]¸áě˘"!„ç Ó¶­zîo?~ţ7žęŤ&(śGĎ˝oĘčŃú¦šü’‰ŠľËBrŞ/Î>|.1yHhCuIIEÓ„ä± ůíZ§MŽQ›{íjIeíđIóË/ţp¤T?{ü”ćň\©ńľ¤ysѲ5o˝ý€¸´)ßíúyŰ'o.Z±öOŻmvŤŮöă®§Wß›2~ö®]?Ť sńŽńÍw»˙ńî‹ W>ňÝO;ą+B'>˛tJ;ö`GXçç` ojĚËĘ”ô§4ýÇ95™yÖëfuaĄ Z«Ńd vĽ$öÖśC™ŃešŹĎ Ź‹k˛Z*l+%_"ľc¶#T "!‹|*X,UםŐÁÁÉÔ*úŻ Uú+˛Ö^­ľ¬÷Ąsţ«8ąŚ3iJĚy…|ćűČšÚ*ö7 ΑňĘ÷‹ ľi’9s˘±AÖ ôá)…I#HęŃz®ąĄRŚţ(ÖÄ8ˇ°ąáL ‡d×řçCś‚äš“µő™tJęJ¦ŻŐ:Äűx&9± wo®É‚–ůőćNúg`Š '÷Hmk Kř öÚĆ«U'µVlpyńÍs—:ńS[pŚ &ŤHxŞđ'š|f×ë˘AoX;$ŮßÔxĽÎ¤ä¬v`!D`ĚIĺůě °·Žý¦ňMLsŤ0ďiĺ´ă™´„É©ÎNëöěűˇF3Ń-pňĺ©”NTQÝ"}9/&ÚM×řE~9…Ň-ÉCµíÜůWł*ÜĺśV´$X%—¦6đ˙ ĐJ9?ąŇBî”o ĺd<1ÄďĽű¦6˙đî_®€RQ—Ż[ç^wő“Ż  2jýM&€[sďłR×Řüř ݶ]9”®UĘoůfŕ.¶#=3ŕ(ĄDŠčîŕŻn*ÉŇHŢůÇD‰9¶Ó¤TÂqD0aÎüU÷ ńűÓăŻgÔáÍŹ?ݲváŻß:™cż„”„Ďź}ňéżýâéćÔŞ'íaě¶|«%‡pćCr»-[óËśJ›ŁDşÝ6$¸Uőô—¶ÜěRŕű°@w››˙üaš›´śLĘĄ_5ެý!” &Q<äő-K^|pËŘçƇŤ 9OÍ'K OѶĹÚŽéňďľP¸x‚Sm}Ł™Ď´J…tíÝŽ°GO_dÁS—vŚzŃ?2yJüµ«jaÇľ÷ϵő@V»+”-7&DŤĄTÓŞ{臆,7ę›Ó…ÓbDáşvóó.‚=ąşóyĹRű7% J©( ±ă¦műäÝčP꫇·ÎoZ=OYÓĐŘEëŚFÁ=|ŠI źm~_„Ô ń›=ż´Ą&ŐŐµEí®_R䀬Ă?_,yţĂ˝‡4plľvä—źÓAy&[®JV‚„ŠŠě”‡gŚJ2EÜéď~Bo¦ĄwnôzÚI ~Űą÷˛"hČ«O-zqŤÄy=dţĺbńs4HĐ[jik;ĆS Ś:7˙Ä~00ľ!ëĐ»{őęŕţĐsżNçřü˝ÇÎä\Ţ÷×ÇLF#@ľ˙âő§._üů/Z7eOVG‰Ś'F“ŮŠň‚űć¬ęg›”Ą[pţą3/ľłŐ FuÍć…ł~HωŽ`4 ęšÍ g˙^ŕěćvďęg¦ őcÓź2jńö§źmY»póO5¦¶ćśţÍ%߂¦ }˙›7G]Ę>żďÝvěşD•$” ˘Y>}âEAš×txĐeuaÝÖ2ě<šnîô„JšŻnĘÉ}«(}u®I)÷©ŽžtńĐ`R¤:©LdL ‘Śň c™¸a(aڧŞÎDojk4Bz‹ŐbČ~0óôÔ˵7HÄýŢŕćtŻGÍ·U:ˇHSńCłßĂQى|Ŕ‡v#zS«(ŠŚČ!±ÇsşŹKÉţ]î•'óŕçä?Ó€öbCú’Ë—~W$čę7ŮY"@ę…WŁvuů±ÖŘ÷c#–ş™MDF€Ţh^i~f}CĺśPŻÍý}~î‹ů E‚ű@ =áo9ułć!cőÇĚ+*0 jKĽô.CF|Ü|Vşţů|F38…ůđ”r„p`–Ä'…+…Ęm´—ĘÁÁenđë°Ž¸…”ŹóôćCľN É\Z6îŢ“ňőŹ{›č "@é¶.:xwÖµ2E[sőą‹‡Ś˝˛|ţËcÖ ˘Ţ&`bLößz‰ň˙ŕ(Ql5H3ÁÂŁÁäřö¶ĎŽ1Í_ąˇZExĆ<ąjÎwýµDÇó‚©ë7´ź˛g­ĚŘG§ĹţáŮç›By«ůąîf;ŰD­\éâBÍ#ÓĽ&#„—É:˝Ą1—pDř…D«ëęF-Y÷üćÇę ˛˛+*ĚË.ŽHw˛„˘-÷Ä'˙>¸ęĹm•Eďü~ҢvÓň­•XúČDJĘ "ŁR Bd·Đrg°Ä:˛hň5®WĄü_Ä.Ý’5jô&Q”)îř ’= 8žFcKŁýY¦gŕ°zăŁÝuĘ ř CBD-ť=’ćs\"mnmíĎ-Ů–NvlŢýô€LĐú´ĂgkěŇ`îTŇYXŐ’*Éşę쇗¬zdőĘC9M““Ç`”“Éd<ÇŮçĆŚd^ÔŠ2ՠ䑼®!·®ÇËĹćŇUㆇÄîĚ‘˝ůÔ*3?oF˘(Č9¶}dRÂŚ…Oú%O\:a¨ş¶60nĽ-7f,Zŕ¨/üzĎ!2^aéĹŠŁ×'&Ťv`ĐÖćví—ĺ^Qň8źľl}Z„rÍüY3ç.%!CY5˘É†«¬ÝJH;˘ďżůç'{2żŘőë[›¦5ÖÖ™ܱWn˝Ľ·ÖaŃęG뀇ź|-ĘÍĚy'żłF'Ě\úŕ‡vš×ŻšKúŔĘŽ™]QĘ´Ťű9ĐÔj0Ş+JË[чlK¤);p”Ł”“ɤ·ÇŚ#Ś0bvý§ÖVTJáč˘ô¬«Č+®šośĎ.ńR8ŕĺ5ĺŮ?_ÎP^X@©JňDMSG@TK}ݨĹíóNąŁ‹Ł¦ľňJF%Śm:Ł pöŔxj…ÝË)aíúóÍîĂî^:Í)ÄţčFçů˘·1ŘXBHśŃ|Ą¤LtKúOběďü;-=&-i†Bž óĆ_|([Sdľá8–˙j~m7čµPc&)šQWm”ű)xßăŁ$ú˛ő0Od¦Â7sOM͸ţy˝Ďňď¬üaÚĺe,Ń3Â)‚$7r(}X«ľöŚF}®±µ™9*$őZMŤÇęŤFââ@9ČŇ ©}ŢÓŮ'§e\yµ1˘«×„§bO4ű(lhQg‚e f"“ö~łÁ%şó­‹(ńP4ó°8&0ŽţJ¦¶&›ő.€jÖCéđÝ•ôßĹxÇ:Ł@đ€€młgýmD¤Yß’Ó¬_;0ÁMĆÉUł‚%?.ŚDŃ&é^9q|‹lëř aEc€®¸UëŁR>0´|VP,ŃL`xóÔáˇ_÷Üĺâ9‰‰3ýť`ďˇdşĹŢĎĹ­CŻ˙X@bG„Ę…2CNÚmŢD„1ŕŮ÷>Z>Á}âÄ©é׫držó—<F‹>ÜöB—o(Ą˝kőŇ+»ĘćjOW÷­ď<űóUĂŕp˙jur3k¨»ßŽôC4č mf.\Ę»ěî3%5L /“Ë„ĄÎřůř…Ďß}Z€ňBŰK(čÔ­őŤĄSÇŹHK›0nĚ–w·‹R¨łVH0‘ŇôÂĆ9ÁÁŹ˝µcÉĎ- "­ű–o©„‡Ŕ(4ĘxDQÁS€°ÔénŞe;ó’™{‚ XÂz’ö×(ýwG[n úŠ]:,Ë»a$N‘ ¸›±$ń``hšrëônv*˝jj(şš_÷ĐŁŹ$Ĺ:ó®aófMć`DŃ`EëŰŇĹŽťËŞPŢXeËg&“h0Śb‡]5Z•t…îe!Ő’"ŘŐkM‚9[¸Q%ŞPËŘáEĐćĆNÜ?s2a‚#gزîţJ]v}ń 3‰&“Á`€ÚĄU>A!’'oĚMĘ‚ăeĚ$h4użîÚQ­—ĹG‡”וş«\żl熦äŢ+–Üwć‡9µ&µm í˝0ŕ‰±ľ­Á$vôËć-9cĐíüőôąC{s C"ÂlřLDfe%(i*Ú´p’opŕ̇~gdÜŤš"@ćLő›×­ěĽ$‘îGCM8{­  u…íś—ą…ΞvOTP”^Sô˝…ćä0b/٬“ł<ĎĄšşâwŢ{oɲMˇă×<ľx"Ŕho§$LŤĆNý’"N´éŤ:˝ľÍh3‡#WČĺN.ްě®;°¨^ĺâĺBâćďe˘0Á$š7žŇž„‰˘ą„RtťwF?űŃQ¦M)x®(3źťu`·\DPIňš*“[Iy‹ f;b=şyžEÖeľ°MĆ[xâ»,4őëř°UÉ;‡Ś90Ř9XŔyĽĎ¤Âqi˙1űÇCFöD¤ěHw*%ĺó8ßTgŻéSJÇŽŘ–÷zÔĐĎâ wFťH í@}Tc2FĹ=îëyŻď„+#ż5řź ăÓS#–şÜ†Ĺnđ@x)ű­‰aË}"^{(Ů=‡Í(ýřѠ䂤׉ľă.ŽŚ)4ň…¨‰™©~)*ď9Á#~L 8pĐg'ś❬´íU9 ú(.âa˙!ßMŰ«Tr§QÇz¤y™9>×€”OĚí_ŐđÇľů\ä„ )~cś8Oč×·±J€vđpŘgq]eq ?śôB `ýÔQŇ4€:ş\şdפ1N™™·üľg9€A!ëŰp~öXąYۉ‚OŽźuuîxĺ·ŚupţĽÂŐËĚľgu¨ŕryŐĘŐA® 'Żěµ~”3):¶rÝęm“Ç}=sfŮÚ•ĎDz] —Ľ?,–Řb*ŹŔ]÷ÍÚś0đů1ăóWĚí˘°!ÓŞú­éłwŽIpłôŰŽPSc™9î3Z€d‘Ţž9gű¤;G ä‚Ľ`Ý댱ś‹§ľúrűécż.š2đ8™]óź×V)ä]ľ9uôŕ˘{R`9¨»eÜp©|Fqí‰o·nÝy¦©üę  n2zÄÝlÇĹ(qęöÎ-ŞŞ*Ęýĺ§Ó—Of6Ućż°ĺéCgŇź^?Ŕ°ąëc7ŽĄŔÉ$Í^'•8Ś›r˝Z{öű­ŹoxâřůŚŤkfJX¦®xFÝҸ÷Î ‹G©˘Çź8źţň˙ůÓíꪼ)zß#ݵ|k%ÂG/¬iwţçoďýóA[·|r €asn—ť "yáţakzuĆ÷śýoîô—¶ÜaěŇj˙8pńđ§Ż˝›·IŇŠűäpÖÁO_şě„rJ9ŕ…/.TglçČś^ůëżÓ3óZ4Mé'ŻśĎJŽ|lá4XBv+ B sxů/ć^ś;~čŃ9cá—YŃzfűżßůűVflz`fşŚĘupŁ&căĐňFíúé1Ę ”:#űňOf¬Ţ(2v`ǶýGÎLş7žâČű. s<Ă'>ĚÍ:˙ę‹/ď9vMl)ź3Ô®QíÜh*żšę`Ř‚ß1f\1. ř á—KŐć^ę•Ăá6(«˘Ą˝_«¦%XKGbÔyöíżŢ瓯Ó?5 €W­¸şqé,Cćíˇŕ๥k˙Ż~ióńkĹ—+er/ÜŘ´ĐF:˝hB»F99Č$Î×]ţŔđąO´13Í"Ó=µ`®—ݵűAň ôĹí-µ]•đî×çµEg‚-Á+»ˇ0ű‘ß·÷ËhŇ˙ůń9€Ë¦ßżô?üŇŞ®ßú׿nZ·  9•Sťuö׿ľúRZ\€Ô‚dEĄ… Üô’Fă®/>üëľ7©+扰`Ó+uĺ—śxŕe2sŮRBXÍ;'3®<´`DđřŐ:uŐ`% K¨j^z|.™*¬ vk%íۇ̲]äňNsČmáĂťd±dĆHĂćXÍŚäVŁÉţě~ě&T:Ç8sšóß*RK/Ff¨ĐŐźjj)ĐC°ŇPŢV˛ŮĐ"´e·éęLÚâVMžNhô5MN«ľÎ$ęLš¬]ą®)[ÇÉĐšŐÚVn04› •şŞ•Ą?5y/ ž¬Č}±Pß €zĆąńn)® ąXôaQ}–ŢÖ'„Pť†…> 9^«mőE­š:—A. GRňqIĺQ5Dp*Î)ÚIll»ń—˘Út­m/‰É•îĂ\őWs˙\˘m …ĐjÔ\łOsĺ¶Ę˛=Ť  q.n˛Ę= Ś´Ó[U#\•®¤čâęß4˝ŕzŁDŰ$‚łâˇFĐć¶¶Uú‡c€S˛wÔýݶ5¶Ol)!˘Qw˛Ş.1Ŕßťh_9qö\S“(T·¨Ź”•_mÔ0ó*`… őš»şÔk-­¬1nÔ×çµ´ęĂĺşş2ŤúLťÚ‘Cnm}ž¦ĄV§ŻnQoĎÎúÇő˛éńĂׇ©~ôdĄŃ,LÁ$:(©ţ~>ÔôŢ…sk[Ňe§/=V.5°'‡&f–iŤ”;~RjĹ"îí“ććđu^±ř?¶«ˇ„Ŕ@ßWżvâtÖČÝćHOrąCQAćĹĚëjMSUUĹáCG“çţţŮĄŃŹ¬z°\Í(a˘Č¬ż©¨(˸^RQwŰ™LĄDT×9–ž˝ÂcĘÔŃ•WÎ|ůÍ÷UőÍ‚Q_UUőó}ł ¤;oÁ¤Ż®®ţůŔľŚ¬˘®-řĺđ…ŔaĂFKĚI?şcçŢšz5%ČĎ:w˝¤N&WTçg^ȸîč0fü?EŰ+Ř´űl“Q'µcŰň­•d\-` Ť%×Îfć Kë§^á™oöťg€Ńp ¸ě10ő9—Ξż’ BĺrąuPgžçůÎQžo©„çy)ýâ]Đ–;«ó„ăo4_xů™Ś=[ Ş5\?˝ůě(ĺDĆâGŻřëSsźY˙Hnµš»©dńťşkN1j4ę®_ąpîr ŤŠŞ+ĚÜ{ŕPUsăőô‹ĹŐŤĄĺąWroٶŘÚ±ňňŇKW®ćýv,=iŇdOZůÔúÇNćVBƇDDš±75çe\Č.© S[peďCUÍMyÓł‹+şf§¶–ĹŢ­Ő-”ăB"ŰŰi,ĚĚĽ–{ůđ±ŚÄI“ĂÜůWźŰüĹ®S6c‡0K†Ńn€i55éééĹ…ąÇ/Ýp’±¬óŮE%5őu•%yź}đÎÇ_î1‰,~̲›’cĐjµŽn®‰II†šK/<˝éÇ“Đ7ţí‚Eë;‘]ŔßÓ»ŕĘ±Żľ;¬g ”ÍĺGŽĄ'LžîÎżúěS_íKgşęCż]0—<·ů‹Ýg¬_X1ĆP–}!#§hřČŃţÎÜŻ<óÉö_ENQk–rS^FúµârÁd¨®Ş’¬źd Ľ#˘'ŚźTvvĎúÇ6ç×·q-ššŚîąńѻ֓ ź­&´kc3uą—Χ_Î)ËąnˇůÍ—źůdűA¤/o-věĚą+ą 0 9Oť»,2äĺ]nlŃćž«mI7żŚ™ †ň˛ŇĘşşĘ’ĽĎ>|÷Ă˙ۙ稱i¦ú½UĆĆŠłWESó™ÓY&ŽČ\ćĄóĺuXYQB¦6ďđ٬‘ă&ŞLĎm~tç‰,&ٱ´ŕúą —EóK+&šÚKĄD]c5ďdÝńÝŢšFMSeŃ©łç[ESkKÝÉcGËŞECÓéÓY‚öާ^ŇťC€×€výaŚ1f5\+rÇÖ˛ČH/.ŻL†Î3#µ˛ öÇ`ďľĚÖŚ±›|Ak×qŤ ®c˝ťś ű›@JĚúPWJLóŇć4k ˝÷îf©»˙žp„ Ěki`Ä=NçW犷ĐxŻ$ÝǤ$ˇq*çľć`S÷tuÂŃ-V 4{덽9Fň“N ŽŚ’µ}UPÁ¤ éM­s ťVRWuĄ©•ܵ°Hˇ§Ç~6Âsň®Ă†> »ć$ˇ^Ţ]?–Ý é­Žť,Ń"ĂčY«c]Ę˙őőAjI—|çüz:/ nY˙Őv”ľě&1ÚVDŠ.Ú)˝wgLwîťTçDJâÖ?-KYDE”c˘ Š›v軏=¨ÖČĚ ”Ł`°VČ›/aŚČƇçMř%§ŤŁDčAť§ş»=ţű®cTľ`Éâ—~KżVz;©0oŽ:J™(Ć 14Ěă›űn6 p'ŰbÉî'ĄNďwĺíÎŽµ÷¤?ĄÜł,ě妴K0`çAˇöÍG©ĐgYôÄ ^»ôJ·ß ű±~:· Âú°Ţč ¤ă˘zěsăv¬şµÚŮwš)ĄL™ô‡ef4Ű7·f/Ą—łö^öB—Ť¸ú¶čŰ‘Ą”±ľB8 Ařݬ ¤$!íé‘Â3? 0KÎűއzVJL;ţŐÂBXçŕ:–żŰőµ—U€µ4­ZôĄ pL´iżs]3.©Ëťµ­;šA™Ű”#®ý±µ˙“u Ö´Yö=ŕ˛Óą~áXŹ ĎS*ešm·ż„ŽĆĐ=ĺ(%Ś™¬ľ¤„PÁJÉ©šŹŮ p"cŇ{ ˇ§“•NÖđ®Ä“‘áQ«\;~Átg׊·ýO!!Ö!†ˇíaµx^Ć`ŽŰCĐ~,G9žB4Y˛S0SĄ9'„R"š[¦LĽŐ•ŰąB9ĘÄNĘNĺx˘hq&±SB)GŔQä8ž1Ázbăyž1QO© ŇÔiMď-ßZ l^·ÜNwěcĽ*pîě‰J®cßA(G Šť¦•›/!”cş#{(m2ő}d˝:ąKĐg“2Žß=Âěp3÷±6¶ĹnŹ1oDy™Ś‰c„ăŃhj/úb[lpQŽç9b4Íłé´J±•2ÇË뤱¶%](ďˇro2EĆxžg˘(Śă8A0u©bw|Ůe)ĺxJ :R«SĘÁJ˝{’…«Ţń1ĄccDĆó&“‘™¤‰‘ńĽ Dűôp;©¦mI;2žăş?1ÓvV˘ÜčQ5ěĎhw(Ďó„MĆ^G ˇ”ă8XÜÖ{`ĄĄ„‚`^|Qާč¨" …”’ö€{ĄÖâ°-ˇ•xÉq<„ç¨`21žçD±ÓLÄń2BŻăZây)SŽ—ńśŃ xž—Z¶ŐŽçĄĄ4!D„ľ‡@´J Ă s”éL-•ŢSîŕ'—|ĐA`¨Đë›Lv†Éeî©Î<›Î¨ <ďwź§ćl}K™‰đ„™QÉś‚„‚é„¶bťhbkwB”‘h4čęL„łS…ŘŤą&ńuL~?˛ěÝüšsm¨łÜc¸3Ń™ęĎ6‹&+z¨ŘtşŮ``ţJ—'ˇŃĐ”ˇí* ÇűÍé…fóeHĽ‹G´¬ü‡zk(Ây­©µĘ`¦'ĹzS…ž.ô[s•čËuz5ń›ď©9s[!L`nS}Ł–ąg®ÍÖő¸W:8*e7Őv™A©,ÚÝEABŚ&mNc«äî1ŘÍąŐĐv˛˛Ö 2B‹ŁC3+ОÔzi›ÄË•Ń.*ÁdČoÖDfuáC=Ü k+kÓSiîşŰDI6—Wyěś:ú?gŹ˙XŢČŇ—íVż€ů2ÄŰg¬‡ĂçąE˙ĺxL6 mşŇÂcţřŔž_Koö°´·Î1ĺß/ç‚ě<# ŤH>¬µ¶äČńł“H9ž&FhHBę*Š+ëĘă&ŤvˇmÇ®Ńú‹$™bÔ¸‰ÎĽţđáßt†[”ĆÝj‡L¦pŹ‹ŹäaAyůYW F0„F'ŹžPqý܉óąë‡ˇG (0A8Ů€uŹ­ńr$…WĎ}óÓ!IňŇś€1±ďÓ3ˇTzŐI 鵢4"Ü„ř¨®d抠ˇń‰bCIIeămż3?0.ŢĹQ&"Ŕ8NfÔÖg^+¸++ {äŚĐ¸ä”¸Č–Ʋ#GOëŤw•”ľčŞ´†– >´ë˙ţľyŶCWŰĂéŢi / ¦ń‹6üeÓ¬ą¦Đ×k˘®¶Tć©”Dfh«ĚĘ.DČݦNĂiküú›Ţ(¶sCĹéŹéóȵĹE 2“:zH¸ďŐłg®–€rˇ±IìĄL¸Đ¸¤ˇq-ŤĺżYäŢĹúő]Ý`÷»zöô•»ç’QŚIt×6e—™×ęćgÚyą N&“ F#ď: 62DNahSç\Ď7 EˇwYŘp„†Ć%[÷]Č|˝\ ˛Ż›ýZ]ÇáBâ’z)ťú~îô•‚*wŤŹŤPXä~őZ‰ČşT3*s‰ŹŹTň„×·Uff•0Ţë5=qCoě};$uÓJ¦ç &ˇ‹^Í*aĽkBBD;v »iśň1qń*ĄLE™\VVT 6ŇčđPJ ®*Ď/)ďk˛B8JĄ‹#ŽŁ \TLśĹB‚rś W_˝škdT ־ͰµęR‰Ąă˘ Ňś@EQŰď©!LQ$”RPŽ§Ň“HQěú^dž‡vĆE_ľ±Ż™ý”PD¸¤JMy? Ŕ€Ĺ!ŁO¤ŚÜť<⧤q§R–xB:loW?€x;$~>xôÁ!iż KÝëäEŔ;¦ý6rWŇč#)Ă>čäË··# qyhXŔDU§–»ŐđŰ™¶-VňIăý“ľJ}`Ȩ߆%˝Ę°˘gÄW±J'¸Nń{&uäOIcŽ OţsĽ‹«ˇÔ §‘Gz§€÷˛ _ĆŁXqlŘ_ÂŔů9&}mˇçĄ.ą‡®\==˝/>Čýŕń-i˘Óëďjl¨+Ż,ßőńÎRů˙j;’ cô¸Ĺ×KkJKŠŠŠKkKó—Oа`ă«uÍy9×5Mu›VOGŻ™ŞŰš˙¤xeŔk}š[Ţ|ýčW§Ó»ˇ›fă§>-Î9¬$?_+úç–PţÖsą;ĺs0=ż˛¬¸¨¤´´¸¸°¸˘ŕҡx%@eťýdúzfää3ueI­úFAA}“ú·o˙âĎuó„ľęŞ´šyđĎ[K28u[ő‘HǨŚâú·ş'ěž+ˇłm‰W9PĹLżZ\[RTXV]źy|‡p JüĺÂőŞŇ˘Ęúş˙~×Tşą‘ŰWś“A ŹżöQSK[QIYeîą@č¤ĺeuf)űîO rĘýĄ–’ă;?pĆ.]ßÔÚĹúuEßI(ßöŠěÓťě¤H7ljźUĄ=˙ĂÇ@‡ש7Í@!—Ľňńꦖ˘ĽëUµőźě€sŠî^6VÝŮ@Ü=÷whřö÷ý9€úüîŹXhVŕxßjŮrĚîH‘ţîč{Îąx4ňžk%u’ÜŻýÖ±sL‰ŽÖşql‡’I˝p#Č =Î;Ç´l}űŚvä›÷ňÓłJÚőđ; ž~ŐRbKawBîüjIeE鍢"u›öÓç×,[űrS[K^vnmcíŢĽé×-ü&Ćš yń`¬“Üşł}˙ťżł­%1_é=ćŕÁÂlůiËCŰqŃ—oěŚSG9úaŮ`§ËHô»ńăϦůK83ďé¨ô–y/đóŰPĎ0ĐmYZ4»Ďö&Ĺ͇OŐ3#B滊ệŚuŕ5c€GĽÂc¶ďčź“ś•Dâśv25z•7*'‚ź‰őm¬L ëîç°Ě?dµżWŞł=ú2ŰÝĂ"™#’ůÜ>îX˛“uów~„ĎP…j‚Ď„sĂÍôś|Ż«,Ř1`ލFűŹ;;Ü?ÍŃşć]ŤŇ1ĺ‡Ţiŕą `ŘÇ–hVú~¸™žăfzĆžI Żę„ @ťyG WÇćĚ8¤üÔ“<5â˶Ú!)±Ę=xĎ}ł>ź41}ńěŘnv5J· ‹çÍôqQ)| îήăü˝„úÇ”d ŇÂgJlâţc˙7c I{ŐŐ©ăÎĚ™(ĺ0ą} %sćś0çÔ™ó?îř±$/c°J >uĚC«9I3žjѶ¬ť–@!ă<őÁî꫇•Ŕ¨•› mŤăxźÔşVĂóëgŁ/«öAZ_FO{°MÔ?9{Í+ډkg$Á˛ŚřźmG®T¸ů˙ů«'·żęçÉxDM­P˙ďíŤRFÍHK‰CŻZM…WؚǶĽôüÓCăĂČxŕ‰żěĎ:ô9ó‰Op죛¶<óôSł'§I5{4ˇBbgLźś2ĺľçź{fÖ„”ž+J»š¶|Vrý7iWs8·řł§×âöv5‚řŔßŔ°Wż<–óŰ7!~áţ˙­ŚrÎn׍OP‡ÄľpëŢ"JÄďŃZőÚƇÂrţŤ3.O/J+ě¶Úi™Pśă Ë׋×/JĺśC‚˛zó?ËóO†yŞ&Ż|®Í Ý¸xľ‡ř]’Ů÷Ťś7wŽŮ˝óçĹFzű‡·[ČČ@^Á±oÜüôëăÂ|¤ÚÖV}ö¤TÁ1fĚb.™8€Â;Ü<§$„p šqď¬ÉÓç=űü3÷ Ť—˛áĹĘJËŢ~ęˇ+— ň´Ö*[ÚŽ‹ľ}cŁ™Žv¶ú· ”p` ^ ‚=Iĺ5ďH0Ť©­ MWktîˇĎnh¸aÉ2oĺ*a¨7šG§ Ą 6ę« ˝čű`č ?†*x0śsťŕážěÂ4Fm•€h`ÄWĺ7ŐąňËJ#qQĆľ=0hŽ»űŹđ ś×­óÖć$WŠšŤT&s–é 4mŐ˘6ŻUŰ,¸†;™Ş zµ…ž&±Ůd,i«ŘS/†:ť íě|bÝ<×;ÍR-ó.…Ćŕµ ČĚ1' €w–éň-ô¨çéKk\˘Ć¤µpU—ÝĐP"@Aű…cŚšl çĺ r$čĽ$—nµÍĺł~ÚóďŇ*ĺ¬ď‰?Ç+Ö&%?ź¨ä4jšOT©ş»'xŞ´­uz@9ť`Z””şu\Ş!zA'K ë;Ŕ ×µ´0"§ň^áómfV=§pţˤ  ṵ́g#ŽnžxńJ]5Đ{¶ŕţ"ýżźî@ú¤€ü™µŐŽŽ®±2ŠţŘŐ0Q ˝~ mDĘ{ŹĘJA0Č>wâ_[w¶•U…­mF •Ń(đÁ#Z<ćłwßŐšúšŞzMdŘŔČčÖĆęňň ô’Nľw D0fPZcŢ©źJą5k—‰˘npt‘ÜĚnä®·#ôM•%Ť-&C[SQe˝Ŕ0ážqm7>üÓߤźÚęü5˘Čló§H%„P0ĆűĹíŘ˝gËę{SĆĎţńÇď' 1šN©ŕ¤-KŘg;v>ąćŢI3ľöę3.`v3ł´—PŽH=uÇî_¶}ňć˘ekŢxëgĚíÔ˛”±Ž§˘ČX‡›u·¸z(éfŞ**¨*»ŃŘd`˘®¬˛¬° ę®…éJ c©/Î>|.1yHH]Uim}n[źű}Ő1éB'v¨ŻŁ!ýâq¸…4·"8—ŇO8ůÇ RÉFz´@¶¶E*!„qŽź}íŤÍB˝ťxşűgťĚ.Őćg¦6µ$„&OŠI©/<Ý÷‘k‹ L1VSq];ăűť;b Y@Siî‹”k+K5±ˇ¨]î5Ĺm[Uvú?żřÎĘúŮ€Ť,8JÚzÄëCëuz)YGű¸ŕx"2Üűđ«SÂeźsĚŮEEBĎA§m[őÜß~üüoţ<ŐMP8Źž{ߔѣőM5ů%%ÝÉ–˘hPWt­˝§µE ­z”ž2ÔüŤ` ˝ľż§Z6ł)RÖĘ.}od<Ąfą?2ŔY°,š­­ˇíşńh°·Š”ă¸>pهq*Ĺ<Č>wĽ}Fk‘dJY=ŢÎ8Žpö(ě^`C‡Ť™5}Ć´{& p”ŚLtĺő-‡ýÖĐ&ŞÜ]¤ďşi5|ę'˙Kđüó˙ćMR[ZRQRŘn! ňËř€řűöż´qĹ’Gž˙yߏÉŹ˝§ÝŞżőöKâҦ|·ëçmźĽąhĹÚ?˝¶Ů=4fŰŹ»ž^}oĘřŮ»vý4*ĚĹ;zÄ7ßíţÇ»/.\ůČw?íä®MśřČŇ)&ŁaÔĽŐO¬ &ŘVą7lyh=.$ öíÍd}} ×wŕ™Î×)|µgáK×ů!î’óa"ă]|R+Ţ.e0Ç3÷1­çš[*ĹčŹbMŚ Ő §´P:šZE˙uˇJEÖÚ«Ő—őˇrÎUç!—q&M‰9ĄĎ|YS[Ĺţ&ÔĂÁ9R^ů~QÁ7M2gN”ř`Ł˘JžR4f‡}­Ö!ŢÇ3ɉą+=9pTw­ľĄJ°ĐÓÜpŞŐC˛Č§‚ĹŇćşÓm€M<7 S[źhî‘q~N᫼ _ľÎ đ¶Đ“`¦ÇÁ‹o–RżwĹŐÁŐĘ·ËPY_±÷Â104"á©Âźhň™Ż &SJI§fŚ€2!§ľ¶F­1p^Tđ‚=?gŞuýţ>*%ÚĹájŮŤCµzNć¤6 +’SťťÖíŮ÷CŤf˘[ áäË R)ť<¨ţ˘şEjy^L´›®ń‹ür Ą[’‡jŰąóŻfU¸Ë9­h?ŠJ°J.'Lm0ŕ˙A7 1”r~*rĄ¶!Ľo ` óÔĽł“ĽŤqŔ;ďľ©Í?Ľű—+ TĹĺëÖą×]ýxë^W~=p±äŮ÷ŇŔ±ůÚ‘_¤¸ýDśR×Řüř ݶ]9”®UĘoůfŕ.¶#Ýýr”R"EtwđW7•diLĺ8& ć]Y$•pL3Ő=CüţôřëuxóăO·¬]řëĆw€NĎńüRB>öɧ˙ö‹§›S«ž´;ÂÚmąôFŁQ]łyá¬ŇóĂĂô‚ýŠťC”´_¤Ú9+ézŮH F8J¤Űs@‚[ŃíĄT…ŘqÓ¶}ňnt¨Ďĺ_ľŢw¦ ¸KĆ$†ô¬cRŕű°@w››˙Lbš›´śLĘĄ_5öĹţt¶-@E]ćĄsU…Ő©K^ąjŢŕAů…W˘¶<6cT’)âľČî»t&ęx+#×—Q/úG&O‰żvU-ěŘwpő˝c˙oď•ř‰3ľúűŰ’”ś+0pěÔoţa%wNĆÁhB»őËĐŐíŢF˝bż樭˛Ú]ˇĄ‚ITy}Ë’\ŕ2öą F#@)Ő´ęúĂÁˇ!ËĆŤúćtá´Q¸®ÝüĽË€`O®î|^±Ô~ϲ°±ę„YkřţłĺŕdDkš™ ô\Ë>Ç:ŤÖĄďŰ÷\>)áë܆ě+çË «S—<˛lůś c'\*ާ”ťC$ÝÚuă‘ĺ+ćŤM~ĄÚŘڦžRNĆóڦË;Ľű·"ETÔ•ôł®•óƤ ˝Ö¦ľvů|y7v„Ŕ Ň)óÖ ťĆ|ÝhyĆ©–ŤGŕŕ~00ľ!ëĐ»{óŁ&ck‹F h‹¦Mo0ç&–,$O!¸oÎęˇ~¦‘±IYúąç;óâ;[ ‚ĐnŐC˘ŁMşfóÂŮ?¤8»ąÝ»ú™iCýßŘô§ŚZĽýég[Ö.ÜüSŤ©­ů…§sÉ· đŕ„iCß˙ćÍQ—˛Ďď{÷Oë¦ěÉjă(Đ%b¤5­Ç…5ďűňŤízŁđ]čč,R_÷8őPúŚu–Âj{NőäôşęcjHa­OG¤KSĎé>.A$űwąWžĚźSŔ˝nĐ PpŞÎDojk4Bz«ÖbČ~0óôÔ˵7HÄýŢŕćtŻGÍ·U:ˇHSńCłßĂQى|Ŕ‡v#zS«(ŠŚH@íţęňc­±ďÇF,ušM‚ŽąŤőq ¤íôřĎöc˘‰‹üc”WËüĂ 3Ď›R/: ŚöDóJó 4bő_żĄAŽ.fŽ7ĄďhUÝľŞňcm=¦FxÉ«. »…«Í`b˝`ď#ÇĚ+*0 jfî—íŕł·Îŕ)ĺ,ľż­ÍUk9üűÓggí>ÜĚ9Ť÷÷PPU<ëű—<çíé»$ÔS0 '‹óôćCľN iA`hٸ{OĘ×?îm˘/ ŠĄŰşčŕÝY×ĘDpmÍŐ_äV,2öĘňů/ŽU°CÖ.MÁÄěżőĺ˙ŔQ˘Ř*m›ű«Qi=)Š1ë‰`4ßţĎöŮ1¦ů+7T‹ ˘Ď'WÍů–`úŇGŇ"”kćĎš9w) ˛~ŐXÂYŢ&­ĚŘG§ĹţáŮç›BŮ·Ü[˙ÝvĚ×جc‚5‰ZąŇĹ…šG¦` “ĘËdťŢŇK8" üB˘ŐuuŁ–¬{~ócőYŮŇkCpDş“%mą'>ů÷ÁU/n«¬(zç÷”˘9Z7-w”ČĺĘšňěź/ç(żQd0±č1˙d„ "Ąĺ‘Q)5!˛ŢpŮ)é –hTMţŻĆőE@αí#“f,|Â/yâóG¬_ôąOô¦cŇ-YŁFoE™â.QŐŽ'‚ŃŘŇŘÍ,cż’ٶH]hÍ;´đŢ{Ý´qäąuŠŕĹ 'î˙÷ű˙Ü—őĹ®_ßÚ4­±ˇ–ĘD}Ű­Ś\+\Ňl+W8誳^˛ę‘Ő+ĺ4M4 @öńďÚĄĽjŢh×Oěěű‚1Śz“ĘÚúŃÎyěˢ7ě““Ç`”“Éd<Çq„X˝ńŹŃî:ePü„!!˘–ÎHćE­(S JÉërëŞpĽ\l.]5nxh@ěÎŮ›O­’ö"‹®VťŃÎľzţhĆÎßô^ËÇ썹±ŁďŮő÷ŚŽŠóçĚ‘äŢč2mę”ă$+!ťâ[ëFŁCČ´)#`h!OŁČé€IDAT ç^ą!ôćve=ŁÍ[ńH`*<<˙ŢŮ®eĐĚY“„˘ß͵˘pĘôaFQÉŘO-K—Ľ'˝ŔÝĹIŰRż˙—M­Łş˘´ĽčCxó1@H{VSËѸ.JĎşŠĽâz ůĆůě/…3^îĐaŐ ‹(RI¨ijđj©Żµ¸}N)wtqÔÔW^ɨ„±MgÎ^OaŇňŢŢ%±]PJ¬ç‹žż±3RîÄK Ŕ¤Ö·–šüůŞÂ8â¬ôë  Ľď4ő‘­š'`P&ş'ý'1öwţŇžGéëŔZőµg4ęsŤmjŃ9P0ʱüWók«¸AŻ…@3I ĐŚşjŁÜOŔű%Ń—¨‡y"3ľ™{jjĆőĎë}–x§(ĐĹ#…€±DϧrŽ@ŁĎ{:űä´Ś+Ż‹ m5zążc;=­ÍĚ%H äé0źaüҲ5ĹFÂfÝ Ë”ĘőLłŹŐbI˘dRë[J,S)ĽÇ»‘CŢ–kz k2P&¸Y8&É›đľ÷x¨Ź4´©Í—9\żpL`ý•LmMö׻Ҡ21fE“eÜ6{ÖßF$H+'ÎľSDr‘µőÇJŠęéâNT|ĺÄńý-˛­ăSd€^„I ŚşâV­ŹJ`FřŔ`ŇňYA±D3áÍS‡‡~ýÝs—‹ç$&Îôw‚˝Ç” ę#x?·Ý˙`‰*Ę :éÔż?Ű7šDŃ`EĂłď}´|‚űĉSÓŻWÉä<ć/y4Ś}¸í;‰–đ ¦h篧ĎÚ›Ó`HŽ Ţž‘’^ŮU6W{şşo}çŮźŻ‡űW«ëĐ1řţGŰ‘~ťˇÍĽZż”wŮÝ'fJj$@9^&—Kťńóń źżű´ ĺ„¶—PĐ©[ ęK§Ž‘–6aÜ-ďnĄPg­‘0`"%¤é…Ťs‚{kÇ’'ž[0)@DZ÷-"#(šDfÎĎwý¦K-"ŁĐ(ă EOMÂR§č—ť;s—™{‚ XByJŻoîôEÖ/c&AŁ©űu×wŐz~`d`ăy Ż:&2µy7ŚÄ) 2w3V€$ Ť"BSnťć…Vź@˛-Fóö›HŹUQ`‚NÔoZ4É78pćCż30YIÝő‚–z'ç[ą.)jZ˝¶Ń$Ht2Łĺ°ś RŽŽQl/‰‰Śđ‡żý˝ÝúQJ»žÖw/‹°KTˇ–±Ă‹  ÍŤEWóëzô‘¤XgŢ5lţĚÉ„ ŽśaËşű(uŮőĹ_d€Î$šLIjo”Vů…(ÄôAÖśď˘áRO¬ľ‘ć”jŮr¬ËH‘ôą^ŰĐ™ófűc–»Č¤ +a©Ó­­µ$ÉaF1©%˘âő›×­ěĽ$‘žÜúC§ŤRÂŔ¬é1o*:•ôQ»EAb‘pšşâwŢ{oɲMˇă×<ľx"`îoO­0Q.wpu%O`4šßéHR:M¨^ĺâĺBâćďe˘‚¤ĐíVÝ’dĹbç)E×9eôłe*ŃdA`ćs1…\îäâ{Çß¶<äy^™ĹÎo‘Ál#ş˙Ć,e[ÍĽ@‰€+ôµÄŃßDI8ť'űMĚá;T€ČŐXźI…ăŇţ%í]]'ůŽ»82ţĄĐȢ&\J š ”ŁN¤Śv >Ş1Łâ÷őĽ×w•‘ߎüĎ„ńé©KÝnČĂb7x <ŔEşŮš¶Ü'â…豇’ÝxŘĚŇŻ J~!ČL˛ĘiĐGqűů~hÚwqrŔyĽď¸Kfz&fŽđîä1-`jéŘŰâ^ŹúďXźŃÎśÇ[z!µěŕ”v´Gš—™ăx- Hů$ŠÚáX4âä4čďzľŤup&śÇYᢀó¤®ö {ź9źNz!°zŮIĘÜ–‘ŁÎźW¸zŮţŮS×FH X˙؆ółÇ:XśztδÍ˙°`aŢňű‚äü}qĂO/śýű!I_ΞSązŃ4oŔíĘŞ•«\N^Ůkü(%fRtlĺşŐŰ&Źűzć̲µ+źŹd».yX$,±ĹT»î›µ9aŕócĆçŻ7ÚEaK¦ÄŰ·¦ĎŢ9&ŔťŘÄ÷ˇ¦Ć&ţ2sÜ˙f´Éj˝=sÎö1IýG ™ĂËůwzf^‹¦)ýäá•“GÍZň,c,÷⩯ľÜ~úŘŻ‹¦ ţr]ů%'tţ¦+=ÂG/¬iwţçoďýóA[·|r €aszÂe§ÄÎ ‘|Ý˙°5˝:ă{Îţ7w ĚŃŢžř07ëü«/ľĽçŘ5AS:wd,ćëŽBźuUZ±ŕ.ţôU w3Z€y/üÉᬟľÔ7ě¶¶ĺĐŠIcŻyýZƱ—¶ĽtôjICŮŐÄ`G(÷ü|čĎ/m>~­˘řňÁ`౤îfFnW\çŽztÎX¸ĆeV´žŮţďwţţ%cęűR"ć®}'?']’2Ó–Oöť·î˝üěö’ŠI˝¦®x‰1–sńÔW_n?uôŕ˘{R€öłóndAHŹŘ·2cÓ3“Đeě€PŽSĘ9/|qˇ&căĐňFíúé1Ę ”:#űňOf¬Ţ(2v`ǶýGÎLş7žâČ»—…]ÎŹ[˛ćŤĽś ©§˘¦lî€{őo˙1Ó|ěŕc ¦řăźgtSË–c¶#Eňç‡_.U[q>|Îofg5Ë˝ôĘ(C,VK2J«źřŰŐóG,ş‘5(Ř|bE“®'nlZK J» ŐX°îőv™ž;yhę°řąkßh§§ąňj¨«Ă˘őď^K˙­ťÂ¤pĎžµÎ- ą¤ľő‰…©”2ë_ÜŢR{ŃU ď~}^[t&ŘĽŇn;Ňv7~ĘýumĆ_·ýűźź˙Ě;üíGľ^ř2˝:ă{i‘ç7˝¤Ń¸ë‹˙úźďMęŠůC",ŘôJ»Uçe2t˛ó@€Őśr2ăĘC FŹ_­SW V˛„ŞVáĄÇç©ÂNĺTgťýőŻŻľ”`Ýw[ž:zpÉŚ‘†Í1ĎhŽ,|¸§oĚ _ąŐH9zđ±…Óz–ŕÍtÜq1kËkŐ\×P¸Ę´ąęę_Ő˘Őż*tő§šZňuô7Z5E:—A. GRüŹ’Š_Őŕ‰ŘbÔ\kѕ뚲uś ­Y­mĺCłÉP©«ÚQYúS“÷’ĐŕÉŠÜ ő­ŇćĐ3ÎŤwKqUČŢ‹ęłôvăPť†…> 9^§m 0Y€Ň}«ţjcu-˘ˇ¸UsĂLOÉÇ%GŐś#ߖעÉ× ­‚ľÖ ÉiŃŐ 0Cą®ţTSKNj\lë–ćĘm•e{A â\Ü"d•{]Ž™,ĐBĎĹm b'ŽčĚܬ¸Úö›ŕŕ”ěużGÁë…m B7ĎťI«K˝¦ń×ŇĘZˇ°ľţzK›(ŠŐšć#eĺ™M00x¸¸$zşÖÖW˝|ú|V‹NEą,ÖÓĂ mxăěąýŐJI«Q{Ą®ľLŁ>S§vä[[ź§i©Őé«[ÔŰłłţq˝lzüđőaŞß=Yi4 S0‰JEŞżź5˝wáÜÁÚ€tˇQz¬\j`OMĚ,)(ŇĄ(ěw(! ,ÜŰ'ÍÍáëĽbńlWC ľŻ ~íÄé­‘ëGćUWą÷ŔˇŞćĆëé‹«KËs3®äŞ5MUU‡Mžűűg—F?˛ęÁr5ÁP–}!#§hřČŃţÎÜ›/?óÉö"n?ń<Ł”ęš#ÇŇ“'Oń¤•›Ůp*§‚PĘnÎcçîµ#ąď9¸»űşeśČ@)c]g’ţ‘ťyë’řݶC¤·67‰Ńް"RĽěîußA'űÎ ţ”Ö­Z–˛Š"(ÇDA7íĐw{P­‘YĹmcć”ó¸ĹĆĚAlxxŢ„_rÚ8JşřżJďÓD»Ż î˛軎Qů‚%‹o\ú-ýZi_SaŢ>u”2QŚ6bhÇ7;öőcŕ®]0ÇřˇÖÁvúkäÚ*¶M߲žeŃ+ööĂÎŁB훎Rˇ_daż§˝µÖGŽőÖwbń—úM)e¬ëJÁšĄÝqŁż†©­řnjpI–Äş R‡űBž”â6yîăG>ýÝřĐČK­‚-jű­B-HŰýl­Yź“&ßɉ䖠'űÜc7:˙ć,Şc«„#`v–ňćZÖ¦Év›dfżZËßíúÚË* ;ž[ʉä"Za¤ŔLĚŔXě±ÝĐ BČܦ épíŹĹ]MŹÎzşoß{tÁeMżÍţ¤8Ö“Z9%&QžRĆŔ!D  Cß´˙mö±±GN «ńGĄđ}¬+É’pDƤ[[ˇ§“róŰč~ÉŽŚ ŹZŕúŘń ¦˙Á!nţ'ŚPąŚ7Ť"c<ĎKɆíHĐ'ÇÇŰĄ‡PJDsNeĘÄ[]ąý—Ű!”ŁL:ŻŮ(ÇsE‹3‰ťJ9&"ÇńŚ ÖoúyžgLDĆS*‚´50‡TęCË„RŽRëÝQ_jÁfáŐ'\öÚ鎽`ŚWÎť=QÉuě;ĺDëîßJ ˇÓŮűCi“©Óš€Ž.­Ú{H9ŽŮd׾łĐg“˛Úß=Âěp3ŔÚ¶ČxQ0+­É$ŔĽ–íe4Ů]ďöŽËlÇÇq‚`­đÄSĘ1Q O Z‘äK9ŽÁ` Ąb·öĐJ}ŔŽîÇ—ÝRާ‚©#ý:Ą¬Ô»gYpǶĚ©~szˇŮ|ďâ-+˙ˇžuĂ1ę,÷îť±álłh˛ßę,÷Hq†ŢÔpVÝ_!L`nS}Ł–ąg®ÍÖő¸W:8(dEMj» HBąá~ľNDü›Îp‹Ň¸[í€Éîqń‘WčeQ% LN6`ÝckĽIáŐsßütH’Ľ4o`Lz‹ÚŃ2ĄŇ«NJHŻĄá> ÄGu%3W ŤOJJ*oúáĆĹ»8ĘAÇÉŚÚúĚk}îĘmµÎgç™X@häÔa-µĹGŽť5îŞ č‹®J«YĐŕC»ţďď›Wl;tµ=Lđťé8yü˘ Ů4kî„éE†>źdwĺs—žŐ*s‰ŹŹTňD˛$YŮ%‚€Đ©)7' \”@d4&uôpż«gO_),ĺBc“†ĹE¶4–9zZoČަŚu&­Ç~9\Ł7´źÇĆ$ş·5f–”Ůąw'‹^°Ű;ş'í¸´M™ĹĄDćµúů‡v^.¨“Éd‚ŃČ»Ť ‘SÚÔ9×óŤDQ¸Yt˘çÜé+%TîˇDfh«ĽšUÂd® ń ŢRr­DdŇ!* ŚIpokĘ,)Ąr×ř¸vyŮZ~ÉB…Ä%§tćły|Ő˙vâśŢ(t™eŞŠ Kë¬m á˝VŻé‰zcď O¤6B#R‡k­-9rü¬Á$ÚçF\äÍĚh`ĽÜ5>6B.çLú¶śÜëZ˝‘Ę ±Ń%ąŮMzSßfD0ÎŃ#2ÄßIĺ hŐ9ąůzŁ éWdňÂÉCżű÷gµF‘RJ@9e˘(Š˘Č¸čŘ8‹ĺ8AŻľz5×Ȩp¬}»B”ÓĂĽAŃv.ćRŰ Dšb¤đ‚(J)(ÇS‘ L„ŘůÜÇ<_řF„ô Sű˛čËXľ9pźć3ćtęČź’ĆžüF¨€—CâçG’ö˰Ô/bś|;%M”B»O·ŞőçP9ĄSÚoĂ&«HÎôîł|G>rWŇč#)Ă>čäË·W—…¸Ž<4,`˘Ş˝¤'™SđŰ™¶-Vň)rJóN;2|äîä1LJ'żŞŕĚÇČŠ—ÔC©ĂŢ #0{Łw*é˘Hqpy¤wšx/ ńeŚ´•čÔ÷7BĺçëôuŇčCFý6,éĄŢ.ŢĎ1é+Ë7/‡ňśŇú€˝ŽI*•)ű‡EÎs„8¬šzzĹ’ôůÓ˘y ‹w~ź5éúýK.-_ôĺa2 <0"cĺňó‹çgŻZöäáđé+—?ć@ÎQc˘bŻ®Z~~ńĽĚ•K™9!ĆIK.K7Wżł+–¬őŔ÷ć¨"UY–2&cŢG«žÝđĚ„äÓs&IńĂ˙§üj¤”!„w9¸tÉ‹1Ač·lˇá|ž|ĺoyUuĹ™Ç]”–>önmsĂŤ‚üëą×[Zš^}l1^.ŕ=©°¶ńá đ›]V_RTXV]ĺč·ŽýcA:-uzýă]Ť uĺ•ĺ»>~ĂůVľ{íHnŽŃă_/­)-)**.­-Í_>)Ŕ‚ŤŻVÔ5çĺ\×4ŐmZ=ßk>xëśt„ŕ•Ż}ôinyóőŁ_YśNď†nš}čźú´8ç°’Pü|­čź[@ů[Îücé”cĚÁôüʲ⢒ŇŇââÂ⊂K‡â]”•uö“éč̰®:źŕâ`ʲGZ´…ąąőŤęĂ_˝çÇuó„ľęŞ´RyđĎ[K28uőN‘HǨŚâú·şâž+uásś“ä-¬jďéîĽí(cfd×J–$ëäNă—¬ol˝)YŘâ’K˙đřk5µ´•”Ućž„NZ^V§ľQPPߤţmÇű~üb~:yµ¦˛¤¤Ľ:űôľř g©b`üř¬*íąď?’¶x:É‚ĺ{Â^‘}z°“ť4ęćX#ń㳪´çřăâ:ő¦ůĂ(är“W>^ÝÔR”w˝Ş¶ţÂáď‚=pNŃ7# 3÷:čÉ9ĎFŢs­¤Nâ|ćńď€"zúŐ’ÚÎVÝŠÂ˙AĺŔW‹k»łü’ď{ĚÔ•%µf>ßů+0véú&‹LŹ}űWG`ţŁďÔ©»Ě2í}±řâ'ŐöČŤ 7[®ZÇń&-[ß1şż~Ďź€ŤŻ˙˝;nôeF3{ůO^YZÝ\˝¬˘&˙Ę©ÔX_N(©¬ypZB»žô R8ĘÉ÷o¬U·u逨)«Żg]Hr4łEé=ćŕÁ#(śb»ZŃ‹cťäÖ é«Ťčüťm-IQ%ěÂlyÎńŔâ ďŘ®ÚejGvF·ÍXv”7LĆ1Áٶ'¨FűŹ»ę“(WMôťp.Ĺ͇OŐč3©ˇ‹:Ż•©TËĄŁÖąá~#”€rř®!cťxÍŕŻđí;úç$g%‘‡8§ťLŤ^ĺ €Ę €ŕgbF}+“Âşű9,óYíď•ęlŹÝ™bŘîa‘‹ĚÉdţ)*)ľăϧŚq’(‹~7~üŮÔ!ď…YRŻ“N%6ÍĄcĘ˝Ó ŔsAŔ°ŹÍ1Đś;ú>ţBę€x…ç‚Đń'“ť|¨CśçŘ3©ăU@GPP‰u>÷‡Ź;fůćlŞ˙Çţâ„(ŕ‰¨_´fś*÷ŕ=÷Íú|ŇÄôĹłcmv5’Ć„F­]ąt€óÔIMëW/pĺމ„úÇ”H ź)±‰űgŚýߌ&ŤżŐ©ăÎĚ™(Eâľ} %Sĺś0çÔ™ó?îř±$/c“ €»gLBb°ź÷Ľ‡77ÖWM‹@ÎSO}°»ňĘ!7€Źžť›ź??-ĘŮÓ×ßÝ˝_"HóDô´ŰDý©‘ł×ĽÂ¸vFú6=üŰ‘+n~ÁţęÄÉío„úyrQS+ÔĆ˙{{#€”Q3ŇRâĐ«VKG3^akŰňŇóOŤ ă €'ţ˛?ëĐçÂđ Ž}tÓ–gž~jöä4©f/‚&@HLâŚé“S¦Ü÷üsĎĚšŇsEiWóŔ–ĎJ®˙&íjçöôZÜŢ®ĆBáöę—Çr~ű&Ä/0<Â˙î䩲ŐůÁ*€řÔ1­Zä$ÍxŞE۲vZ"nR[n ú¨cćçÔňłůŐď<ş}[Ëö#đ°ţ­můg2[ČžuŮ–ĎÎJ1Ó­{ĘVOŠ€˙äü…’% ôô;üćdŃ®°Ń‹k5ÍĎ,KśâĘÁ-hŕ”‰#DLxÔ(Š+Ň‚Í}Ô¤očŔű¤Öµţ°ţ^ ~TŻŐžřá ›ÎÚĘ€KŹŘ㓆z*č:ޤXjr3®?‘p”‘Sôđě!î[ľfjJč´UjĘr‡8ň˘G—Şu߲ZŞÜwYHŮëCF-ęŕĆán€,jV» đô 8;'?ŻĂŞt˘đ§Oy@1đľ,żDŠgHl;ź &Óę Ńľ±CׯY*É´U×ňŕ”xp w™e¸öŘ’Ň®Ć!!˝Üŕş·HRL6kŤj3hWŽ v4»±MÓ7ú2ŁI#7aúµµK&F {¶¨ůäÖäÁcŠjž•Ś›ŮŐL_»ą¶4§˝_?˝@âđi­[·aíb/;tĂ‹”•–˝ýÔC+V.Ź 0 8Ä?(ĽÝŠFDđ Ž}xăć§źXć#ˇ°¶üł'ĄŽ4cĆsÉÄáŢáćy'!€c@ÔŚ{gMž>ďŮçźągh€č¸kě<;i…›‡íʡC¦6˛°ÝvÖ$ŽvŽz’ €¶«šŠ˝ `¨Ó ZF8b¬7šEG§ ĄŘlÔW¬é–‚·]U[×2{¶PzŃ÷ÁĐA Uđ`&9ç:ÁĂ=Ů…iŚÚ*ŃŔŻĘoŞsĺ—•F⢌}{`Đw÷1á8…"‹vŕÂśäJQ“Ł–°+´ŤuNᎪ…±Ţ`Ôx/ ň$•ÔĽG8€ÁkAGIGşŘ®wšĄŠí»”ÖLKßëu‚Vd ĽłL—Żi«µy­ZµŕâÔ’” €ĚY¦+°|Ó,:‡8 ÄŰćc€&[Ăy9¨ :/ÉĄ»?msů¬źöü»´JFąN÷†Ň˙ŚđФ®ÝßB– o3š»{ ú–ăŐšîî žŞmkť^PŽ@'%Ąn—ęC^ÁÉRCÇú0čuE-­ ‚Č©Ľ—Gř|›™UĎŔ)ś˙2iÂĂĆ…>3,AŐéňŻCâŽnžxńJ]5Đ{¶ŕţł÷b?Ýô?Hńó3k«]ce–műm¶) ´×¤ŤHů`ďQ™B)Ą#Ô75äee–TÖź4ż4ýÇ95 ĽÁ$ňÁ#Z<ć?ď˝×8Č)utßřěkol~4d€łĐGÇ”Ć JkĚ;őS)·fí2QÔ ŽN ŢLć»ßŽhĐ7U–4¶ mME•őĂ„{Ć9´ÝřđO~j˙©ó×"łÍź"•BÁď·c÷ž-«ďM?űÇżź<(Ähb8Ą‚“¶DL .aźíŘůäš{'Í\řګϸ8€ŮÍĚŇ^B9 qôÔ»ŮöÉ›‹–­yă­ś0W´SËRĆ:ž"cÜÝâꡤ©Ş¨ ŞěFc“‰ş˛Ę˛Â‚Ş»ÄVçÁ ű܉mÝŮ TV¶¶ď)ú®cŇ…NěP_GCúĹăp§R=Ř€Ké'śüc©d#=Z [>€1‰ŁÚ{Ę`HŠJ"&qň”,I—€śó7'‹îpM1VSq];ăűť;b Y@Siî‘ăyńq‰ÉCBj+KšëęŞë5‘ac"Ł[«+«*Ě}äŹSÂeźsLĺâd5‡w+ Ž’¶±ÇúĐzť^JčŃ>.8ž ÷>üŞ„ËŮEEBĎA§m[őÜß~üüoţ<ŐMP8Źž{ߔѣőM5ů%%}—…äIŢ…ž&@ĆSęča¶áŢÎ8ŽpV%= uňčÎň3ĆR_ś}ŘÂ皊bŤ¶­*;ýź_|'É´ĄŐH8 A{=ëJ§YTdť-ˇ}áFb%—žěsÇŰ5ŞMkÔęh/ÜčűŚĆ ŤÍĄĄĄWŽş?`€żBĆ ‚płotEÁ$ĘUŁćűu٬ŔĐŃł6?˙âďźÚ¬”HH™öĐâÉ&ŁaÔĽŐO¬ .HYSR\QZŘnE ňËř€řűöż´qĹ’Gž˙yߏÉŹ˝§ÝňżőöKâҦ|·ëçmźĽąhĹÚ?˝¶Ů=4fŰŹ»ž^}oĘřŮ»vý4*ĚĹ;zÄ7ßíţÇ»/.\ůČw?íä®MśřČŇ)íŘc‚a}‰É@ßÔŘuĺ`%S[YXŹnË76kÖű#ĂNÚ€pRL]YäSÁbqsĂ%˝ţŞFS!D›ôQ«ik8Ő ÉÜÚíŐşV©şî¬ś©Uô_ľŔ-kíŐĘËz"§sţ«Âő“ÉDM‰9ŻĎ|YS[Ĺţ&ÔĂÁ9R^űCůĄŻ^Ü'm,@éĂS “Fh·)ŽCÝâߏMxĘßpŁĄö’óu [éYřV‘¦L¤<pľŞđU^ć΢b]bQÚúDł5tôýwÁB©ş>KgjŃ+ăÜ=“ś<§y9xń˘”úÝ* }­Ö!ŢňŤ7'@Đ2żŰç“F$9µŐyĘń2*Ţy÷MmţáÝżd¸5wŐ[‚^tLbWX »‚ŠÍMŤwŹSV<ĐܤĺdĘ€P}ł?ťůlî‰UOó” 9ťµ%ęÁśxł˛°ĹeÔ‹ţ‘ÉSâj4ÂŽ}WÍ ~âŚowüú­§Ë3OÉj)=ó[ú ͇{Ů˙©©ň¶óyĹRű7# Ö…ží{.›ŇÖÜ}弙óżîNôSčÚÔ×.›KöŢ—čďŔűúóď—v˘PÔ]˝Ř“ĺ—âqĹŽ›&ńą2ëĚľ3eŕdŚťdJx‰YĎ2G$W{«xDÓŇ;7z=Ąś¬}tëňí9_¤)|ĂwËŤľĎhLd2ŐňgßÚúýÁgç ů~ĎM›IÖqEÓgŁ& ÔÁýˇ-ć~ťÎ)đů{ŹÝ·ţ'7Ťůţ‹×ÓîۢgÂźÖM–6yĎéRé&_˛˘<€űć¬ęgš’–4$uR›wüsg0B‡ĺ_˛ €Ńd0¨k6/ś=8.:mĆ#3欙6Ô˙›vĚ;˛Ö&S[ó N’<·†ó™0mč/ßľ9jÎÓVŘËtŤ*Ii—•™ZÉÂzt[ckźűh䣼ÂpőĹŔs–ŹK Éţ]î•'óŕçä?ŰŇRž'íďЬj±Ě?Ü0”0ĆSŐ@g˘7µ5Ą*h1d?yzęĺÚ$â~ops ş×ŁćŰ*ť „P¤©řˇŮďá¨Q‡D>ŕC»1\¦VQ‘^«2 -Ł>}ÉĺKż+’Ǹx§ŞĽď prcÔ×Á=NÁy:xqň[ŕč"rľîq ęˇôk~«lÝ 0@Ö#Í+˝mt“0ŃŞďĎÝú_ŞËŹ¶Ćľ±ÔÍÔh/yYAÂ%«Ý_]~¬ă"#@?qĚĽ˘ ¶ ag¦ş#Š˘5đ”¶ßN˘ŕăćł:ĐőĎç3šĄ\z@~eŃĚď\rđś·§ď’POÁh$ś,ÎÓ› ů:-J©hhٸ{OĘ×?îm˘/ ŠĄŰşčŕÝY×ĘDpmÍŐ_äV,2öĘňů/ŽU0ÖEU%˛š ‚‰1ŮÝy‰ň˙Oŕ(Ql5t Ow» ¸Ą’K0cŃG}á7{0ŚđŚyrŐśď>úk±”çZ®˙şđŢ{Ý´q乍Ž!Ӧڀe>»M Z˙±ŹN‹ýĂłĎ7…ňVósÝÍvĚƬc‚5‰ZąŇĹ…šG¦yMF/“uz–`.á(đ ‰V×ŐŤZ˛îůÍŹŐdeWTHŻ ÁéN–P´ĺžřäßW˝¸­˛˘čťßoPšS¶ÓnZî(‘Ë•5ĺŮ?_ÎP~ŁČ`b=ĐcţÉDJĘ "ŁRŽkBd˝á˛SŇ,Á-š|—ăzŮč<Ś“ăŰ˙Ů>;Ć4ĺ†jôneAtLÚg6jô&Q”)îř»¸îXĆńD0[»™eěW2óYšgDC[{O™ĚÉŃ…Ő_°¶$Ó§¦LgTÝ´,lpÉşę쇗¬zdőĘC9MŤ}ü»‘I 3>8tň‚IŇf­í¸fţ¬™s—ňaÉKM_˛zó@/Ł2(~‡QKg§‘Îąëíˢ7ě““Ç`”“Éd<Çq„X˝ńŹŃîş\3’yQ+ĘT’Gňş†Üşj/›KWŤ»3GöćS«$„7+ ąÂ±žěú{FLGʼnůsćHśoPÍś5I(úmŃ\©džÚ)xäčQ |6ĆÓ Q¨ô _1k¤îú/ z´üŇ…αí#“f,|Â/yâ Ć@0ęMťd*ăöYf÷aHçô]ěÜ©Wn˝lëőčžw˙ŕěčÔVŐ-7nfFc"‡›—ˇľđŃ?˙Á^ą“Ë˙ÇŢ{‡Gu\ďăďĚ˝[´ZőŢ»PčŐbÓ;7l\ŔŽÁq‰Kb;±ă·8¶?.1îŕ6˝éEˇŠz/+­´Úrďťßw%­¤Udśßó|Ďó$fGwćĚĽçĚ™~“¬ďéďŇ”8ĘQĘ)ňÝcĆF±>ý§¶VTľEĺ¬ö¨-Ď-ŞŻžÉ*öT9ŕ•–ż €Z%§äHz]˝{@Ts]í%íăN™ĆYŁŻ«¸^‹ÁhUNžOm¸ŰiW§™CW™rÝeŃŃ»ŃYîÝěs˙‰Ę™C óÁź»÷rS€Ú۵jNę›N7´42ç`5U˘ëĐOc˙äßΡ-W–ľĐ €Iŕ8–÷B^M%7řĹPH"c‚Íb¬˛(ýTĽnńQSé®:X2ˇŕ•ěăÓÓs>«óYŕ•¬‚Í‹”vyYŠMŚpŞ ŤµÉČġáHťŮD#5B]ks©ŕżŘWĆ­Ę{Ş›Řhl.üä'µ×DęöV´)Cuöé>€0°Nm'h4ĺ>žőŰŚô‹/I€Qg NčĚKßń cÄXgOĄ@Ld4ţj‚Agľ+ŻgĆĚ’$´u`ďŔoćĚ~{TĎ€js«»ÚaëĹ´C RĽł¦Úl@$Öl1).¬I¤ł3 9RéŻÇŽîlVlž¬LI23‹ZZ}´Z3Ă“ćOň‹ä:_9~`řW[źşP471q–ż#ě]”¬oj¶€÷svíĐë˙Gm$áu¦Ěśo”€§a$łĹl‘$˘ ‚şŻ\zűÉ·ä6śRÉ€KŁ…ď~łEB‰| Ě,"ë4č_?ÉĄT4Vy¸¸m~íÉÝ—ĚCÂý«šjkYÄÝürä’Ůh6XÇ™óąÜ|b¦ĄD‚‰”ăJ%€°”™»ŹžýěőÇ( ´=…€±©ĹÜtuŮô‰ŁRS'M·éőď$ŮŐY‹Y"LD Ń=ł~np`đC˙ܲô‘§N ‘ÚsÉ€Ä&I‚ĬQâů®ßtÉĹC`ĽŠ’¤â©@ ,ĺÖ]=ó˛“bg\˛˘'Šb›[OůöÍďB˝ëĽ ë<Ă“oĽ·b’ŰäÉÓÓr*)Ąýń{ăÔ_“·ĆrŻZc@dn¦ŻY<„ED]v­ ÖITżHĆYöëVŢąĄ•Mµ ‰- ł2zć­ë”…-ŻşÖA”ëÉ,mÎâ(ęőµű¶m­6ÓđĐŘPźsKŃűNśŢż#«Ök¨É˝[·öű‡Ć:ń.aógOáeú!‹^¸ËĎ0BŰú/1€66^Ę«˝·Ť×‚YS 5śyÓÚ;~,qŢöů› Ŕ(H‚`6 "Psµ¤Ň'(DE ¦ß˛u¬®µľ3V›€Ţ)E%ÎA­«żz)·­†®aógOUë}/–źăL°â\eâc"Łüĺí·Ë”ă8‹Ečet"U(%fk7$#ŽśiăÚU˝ ÁËéą7ČÁ$Ű{÷éĚr5-u}ˇŃ/}c ̤ű×wß}ď}nţA(afłI0Ńv„ŢŻr$ÉbéÔ.y[Ů`˛M&Ĺ& ”Ş”JGg ÚV×hł˘ňŽ“HMZgOg×0OŠ˛Ň·[~yMŇ1PŠ®ăÎŘ'ßŰ%)´’`–ăJ˘(2ëŢYw{AŠDQęY¦›°Ú[KËóĽ$±.ă…mo˛\ǹׂ iEăF}“÷RôđĎâ<;8Žňžxatüsˇ‘ĎDM:—|‹ §I>S &¤~%ݏĽ·ĺŠţIś÷H ŕ0ćXJŔX꣗>&îa_ŹŰ|']=äŐ¨!%LLK‰XćpĂ~ű Â\¤Ű°Í‰a+|"ž‰ż?É-G·1Pţ5č˝ÁIĎÉ)~+ÂF}ź~_ŕŕO†L:>Ěk° §"B_Lűu”u%«ěśh'X[!ŻUcęá^ëĽÜęźŔsQ@ňQňi¤ť¶S‡!˙‰‹¸ĎŘĂSżŤup"ś:ó˘ZÇÁďµ}ó]¬Z(ÇÄ2čĂ!Cź şűk‘Ůs›FŹŮ»`~Ášĺ;çLż'"@bč ş‡<3gĽüš‰Ó¸î]¶tŰ”qďN›U°rnOnŹybŃś?úĹśąkĎđr\/®^µ&ČEĺč™uĎÝď%ÇL‰Ž­X»ć›©ľš5«ôžUOÇŠm‹–ľ5"mľĹ´îŰnź˝1aĐÓă&ć­ś?ÖYŐ˝š2J˙ĽuÎ÷ă’ ŘýŞţG(€é±‰{fMřßô [¤WgÍýnÜĐ« …Ăóo~ś–‘۬ם>Ľwý˛ŮF,ŢČeĺ„0ČŽ hŔ‘ĚĘO_\ @©RXýČ[—Î|nÓs‡/×—\îöń żĄţńéE5Ǿݼůű“ş˛K´×ZňÍ,ÇęĹ(qúw?íÎ.¬¬,ĚŢó󖉀ó‡{3tyĎlz|˙É´Ç×-0bŢ:ĆŘŐŁ_Şů4tčśµrŠ€Ŕ¸i9U­§~ŘüđŹ=“ľţ®Y2—é+źhjnŘąýű—ŚŃFOÂÇ.Ş6Hßúö}-¶Ö®`ÄÜŢxŮI±łAä·îŮśV•ţg˙›ß‰şę|ÚoVM3{铌±ěsÇżüâ»ă‡÷.ľ%÷ű{ 跮ʳüg׹ľôfz °®…?8ą÷ĂçúÇ˝›m9ş˙Áąˇ 9_V/·´ąęr°†.~ŕŤËi‡dKŇX‘榜µćŻŚ±+× ;Ľ;.qĺ-'żűřµÁXÓíÉóîy-ďJÚ Ď>˙Ë‘ËĚP~KĽWÜ-÷űö˙Ţzí/%f|láŽ%€g>?[•ö­=ĺě& Bzĺľ™YtwΊ.}„rśZÉÉĽŞÓ·€h†—5´®»5F”\ka_üýÁ™kÖKŚíÚňÍÎ'Í‚ńĺGd˙ĘţËB~ĄÍŽĽPŇdFřÜ»_ÉJ?܆üĄP‡Ĺë^o—E»Uרr ë.l°ę‘w2Ďöhů­ľy7;óŚçÖň)<§Ż|ÎV¦KgŽ0rŃcíŁŚRŮÉn¨ĺŇ“Jë ˝ˇ±aÚQÚ%٩Ƶ/µs?qt˙ÂIńpKĽÔI7:ˇŃźM^¦žó (4/LŇP«T4±s« ŇĄűżßúĂž}‡?zy]7z65äĚą˙Ďíí˛¦<<pŢđççţűăž–¦şÍ˙ú׆µKxęăWŞ2Oíű× ĎĄĆČ%ČVTžúÇÝZÜ`Ůöů»˙úôˇ©|Á° 7üµÝňó :Ť@€Í¸ó[úĹ{Ž ž¸ĆŘT9D (*[Äçž@ˇ ë½ÓÍCŽ0lÁŁ=ÉTvä¶čľ—şkÂą6##e§9ÉC‹fô.ĺNx múŘŠ‹'żřú‡ĘşFŃbެ¬Ü˝kÇąĚ|ůâ(ŞŞŞvďÚ‘ž™Bšjň÷88bÄ؉WŇoůţ×ęş&J—y:§¸VˇTUĺeśMĎŃřŚ›8ÉOeřë_6l?•@°ĺrş—|.3_"c’`)ÉĎ9}öB۵…Nßt­ĎĄ|ŇP|ůTFîÔń~ZńĄgžřzÇXĚ˝ńę!Ÿ€`1]9ęĚĹlŞT*mť:ó<Ďwöň|])<ĎsňńwĎ:ß“v®¨Şˇ¤,;ýbv“^W^^š~.­¸Ľv TzŔt•pľá×ÓWťťŻŘĘôlQYm —w®<Ę0뱆­=”-IKsMzĎhĽ÷ů6‘őć-€ŚA©t(ĚĎą—••śĎČ(ĘĎ}>«ŹľÂ`6—•–TÔÖVç~ňîëďţ÷(<ĆŚOę ~ݵߢŃZĘOĄ_’„Ć“'2Ž(U\Ćů3eµzŘXQBľ&÷Ŕ©ĚŃ&j…§6>đý±L‚Ą“ĺgťĆF)i޶wŇoŮúku^WQxüÔ™Ihi®ýíČáŇĘɬ;q"S´áNH‡ ’Ń đôÎżxÄ®LĎ]ΗTJÍŐ6Y”——¦§§•Ő‚ąóČHmç$ąçҲŠĘí†{˛+ńßěÎŞ Ŕŕ2ŢËŃŃ\ľłh··ę=啉ďž­WőÝâiĘ?‰ťŇ%Lě‡őí™/á™ç˛Ŕ[ϬÉţ]nßbrĐ8­S_˝Ww#cś”§H*µs‚ŁŘ`ÖĄë%Ń:o˘î µ+×zŐŘmë`ŕ=”~Jůť:!0•MMÄo‡ţd]s©@xÂF´ Ç ˇ`FŃPd”Ö1w'D逳±V ”XUH=k0_MŇ[‘eoäUť2(ĽTjDë‹.sąÉ¤¬m Óđ­B‹g†§®Ăť9Ž5śi’,ö–ăýćzčOőVgëaHĽł{´˘ěÇ:ąÁępŤS¸Zl2ëŇô’ÔIé>ŇFKý©FI°ß•żÚ9A+6tçš%çýnď{ź&2×éľQËÝ2îÉ2ö0'ˇ\˛źŻ–HÇĘ+M]î6 pqĐ„9idÝĄŐz}ąŃ¬Tj&řűp˘ń`YĄIb„Ş–D‡f”f6™xJEIâ•ęhg­‚Ŕ,óőf‰ÉÁĎ%ĆH »+چR‰BŰv‹í®jd{ĘkÝżź>öÓSG*kŕąi‹}ëa—Ďxw‡Ď˛ ˙ČY=’]©á1KĽó—}%7´Yj§ń`Lííâ锟•+0;)”ă)a˘ 2BCPSVTQ–0bxlDsCéÁĂ'L–ĂŚDˇ3a˛o:pŕŃ|ť%߬rŔ*·¸řH‚ĘCĘËĽÔ`¶€!4:iôČ„ňśÓÇÎd‹¬_B#„r%’ČDIäŢkşËSC .ťţúçý˛äĺ1 c’Ř×ä[Ömo˙ަƞĽâ˛>‡Y9—›w@ŹöbF¶ź(ŐW4 Ŕ˝,Š‹wÖ(DQÇ),­u—óoć:˘K…¦TyMś:ΙŽě=Pm4ßĚ}ŤţčŞ<‡V Ůżíż˙޸ň›ý—8Žö)ý!Ęń’(L\üŕ›fĎ›tkˇąß'*Ý,I÷–R‰Ń”±ĂÂý.ť:q± Xs;Y¤ë‚”—”ŮÜPzđđI“E¤ çřřH5O@x“ˇ"3«X” t >yßZ˝sß!“…LNáZ«wí;dײő$‹N6ł{»:-#Ŕ¨Â9..R­Âl¨ČČ,fĽçťw-Hß˙ý…üZ…B!Z,Ľ‹wld’Âlhş’“g!IâőȢm›čfĐe•PĄK|\¤š‡ŚĆĄĚbĆ»$$ؤ\.f r+|/ť:y± @@h䨔ÍŐEŹž2 Ýń±ö¦IÓĆ;‘–#{T›Ě˛ŃčM¦„€Ů"Řd‰ÂsÍť˝ˇa˛ôcÁ0BCă’†ÇE47”j­lä~¸M7˘lŰ.ői±9~PÔ ć†ę˛Ş€FęË+ëZ'Ćg_Ö™„k°!„p”Ę‹jŽŁ \TL\›…ĺ8ŃÔtéR¶…QŮQXű2P*ßŐ§„ČĂś« —DQ’Ç’$I’Ô~BHa’$Jˇ”‚r<•ŻDJRו|O^ë77‰ÜfřŚ;‘2úçˇăŽŚLúG¨’Tô@ب}#R·Ć;Ş»Ć?!˛·€%!cŹ%ŹŢž4ęçˇNŚ [ä¨RŹŚ šŞ@€Űlß±GGŽŢ6těÁärôĺ!oÚŠ—ŃűGLÖ¶§ô&n ~ë#Sż‰•cČúŘr?ž¶Ôú¦_ᜲ?eÄaňÓĆđ§Ť;2bĚľáźîúÎHn–ăč}×€×ň Q_ÄČK ÇTŻÔ#GoOwtdŇ«ˇJ€új†~5tě®acŤú\H—XrÝníŚ3µcęˇFLţ§ZťĽsDä|7»xŕź3%玥çW,ţ|ěp­MV´˝=nÄĺŐËÎ,™|ńü‚;WlŚ †ÂqËüË^Ľcéw㓝đîi«VÜć @ÉQă˘b/­^qfÉüŚUËöĚšă¨D[,KWżS+—® őŔ÷őPEβ€rĐ­™Ĺ5Ĺ…ĄUuG·¨Ç Ä=gs*K +ęj÷~ň†3ŕŇ‘˛ç˙^síS(oŰRŮ~ń˝¶vťâŘ)DşlN51·^*j«Ď‘-j 8 -ŞmÚ°`$•R `ꪇ«tÍ…ą9•5ugüě€sŚľYtp ŚźYŮzć§˙P@=hf÷Ł[U€*úÖKmř\<ň­ÜŽő/YĄS™s&łä>]KkAvv]CÓ/ßđëlůíô¦“;ă5€çc/ô(SŮźsHâî3m˛řř gŽIE5˝˘äÚ»zŘÓ„·|‡`[ążé¨bnÍlCăâáo5˝z ’Átr©´ńňŃ8Čňß-‚kJqUÍÝ3Úő䚨G yno¬c'ÜúŰ˙;×=—ܵ׸˝{÷N wčŽgw »Źřýůćć‘&ÁÉŽhÇúO8ťâ7B?×_N|uPę¶ÁZűŁuâ5‘µ—Âkˇ˙řĂ#Ü9‡äź‡Śwŕ9ÓŰ=^ĺ>ÇwěîˇNj˘ qJý-%zµŮ/Yđ1cľŤUČnÝý–ű‡¬ń÷Lq˛'€B5bűČĹrŠ-÷q‡†{„ÉAHôëńOĄ +€CŞ÷„s)~‰jĎąASłFůŹsl|FËÍRk’ě»Î<ŚxßęÍLáďŕž¬ŕě;éÜ(źÁjŹ!KrôˇqăO¦LÔvâEeśťmpé7J ¨GnÄdFŹDŤúbPwý‘4<0ŞđžUËĽť¦Ş[·zE;şÍŚ•JU”›[€F=36!ő’áÜ-q)E«Ä8ňažˇyw­Zě 8XĽđŽ`3"Ł&zi'GÇd.ź—ČOźô5«^‰¶ĎşÔ)L÷Çi]Ö$$<<$~z€·=! ÚťK–<Šö¦u­$C1-6qçĚń˙›>Đäµęš” 'çN¶Şű —)›3§„ąÇOžůiËOĹąéµj.‰ó:R4ÖÁă€ÇŢŮ^qqŻ+ŕ;}ň(“0šÍ÷ĎŠë2â¶$[Ćčw$Óť)‘sîú+cŇ=3Żąä›_ŽR­rő ţÇ—Ç~űîĺP?ŽŔ=jzy“忯®<> €gí‘7wfî˙ŚuWÉ'8ö ›žxü±9SSĺś˝”*ŻOîÜôqYޱ0íÔUOĚ­ë—LŔ÷ěU¦-×'Ĺ9‡äUÍě˘Oż7¶Şik)áöÂG®ú:Ä/0<Â˙ŹŠS%ű š¸zŁŃĐ01Đ÷I©m1?˝núł ˝aę§ŽÉs(CNĺU˝öŔbô{.;PÄsŔş~“węg«…ě]—»Ů–!Nj1·Ú¶”­šć2xN˝A˙ÄňT ĂFşš¸ŰOtł?×T[ąr!±ÓÚl”Iď™˙©yW ¤F9yřyyXľńý–†ü„ ‡Čá‹Z̦%ăÍ\űş©éŞśŇdh˝ďö}Ę€s·–†Ť]RŁo”Ű?t¸‡Z›Žjťšs%/W®O€»¨Cbú•Âűć pűŠ»¦'‡ÎXý`uiö0 ď=¶¤ÉřďMkäú/ +C@ůî‡M­­Ç~ţTnĎÎËłr÷đ ¶©O ‡'€1­H–¬˘GŚY»z‰#0tćcÍ­Í÷ĚH´ĹÇNojž¸s"ü§ś9›Ţ“Lĺ’+ldŃÔjşwv<~!·¸O4¸ž-RwM$aé¨[׾Ń.e˝Ń|Ď-ŕ7Ą]7üÝÜzď]˛řÍĽPTÇŚş§—Lp¸ âŁ KŕžZR§[;; ýndłď9Ţ\W ŠŰĚŹŤ ô öo·‘1"AËu˛üCăęáÍKk«€ľŁ4ůt2đ$߶̨©Ňh\b±Şa’ 5gWę¨äw~=¬P©%Ń 5ggGŠě&źPQd|đ¨{—Śűäµ×u€ľ,k˙ŃÜř¸Ä¤a!µ•%5uµ¸î‹ÉmD‰`ÜŕÔ†Üă?—pwÝł\’ŚC˘‡ȵ¬Fnz9’٤«(nhĚ]aEťČ0é– †«ďţýßŇŽď<~ć2IbÝă§Č)„P0ĆűĹmŮţ˦5·%OśóÓO?LbN­âä%‰sŘ'[ľô®Ű¦ĚZôâ O8;Č×ÖI%·Ą1‚©¬®y˙ţCőIëć @öiĐk®Ž«’ÄÚ߼őÔŠŢS: •…ů•ĄWtf&K+J ň+˙¨—ürk*+«ëô‘ab"Ł[ŞĘĘĘqĂúÜꯎÉ:±Ă}5ć´sGŕ:Â8ÜIŕśO;ćč3X«h=zFµ›m‘˝”ŽKÓŢRŔ·«—OČ)ůő-Éщ}Ę‚ŁÄĐ­Ą“FŤ×—çČíŠőˇuF“¬Ă¶_Ę8Ťűú'_|yăÁ^ZPŽă8[ «źzű§ĎŢöç©É"@ĺ4vŢíÓĆŽ5éŞóŠ‹ĺĽý—Ž'Ăm÷˝0-\ńŮ×Gśśµ „QG+÷/'GÚëę«09µŁĽĐ ä¤˙żÍ[[€ŠĘ‚Ĺňť{S]Ee­ĺű“G ë.Ó6ĂŸąř¶Ë"żľ%9*(OűDŁ—~ÚMBk+‹*ë˝<Š.ýÖIĘ‚‰8zXŃđvű±Xd ÄŇ’]V»hí˝ Ţ`Ć$őßtPĘ9ýżÂ|Ä;ýwţôa5%ĹĺĹí2?Ż”߲cçsëW.˝˙éÝ;~JŠp0dü-[¶ďůćW/żëźŻ> .uÚÖm»żůŕ•Ĺ+ďůű‹ÝBcľůiŰăknKž8g۶źÇ„9{EŹúzëö˙Ľţě˘U÷oýyË`7Uhâäű—M,ć1ó×<˛îÎ` l|Q÷Đ›jŃöî…ôţÍ͝˵_pb ŠČÇ‚Ą’ĆÚë…Pžtí*ťźŠ3‰ńÎ>)šŠWKPäż6TíŻĘĽçRŐ“{(…’ó_Ŕą+ś /6ÉY}ř(t†ňť:ÔÝÁ)RYńVaţ×:…'Yş1¨}xJ!čEë_mążVĘ>@ľÚłŕů~X€{Ľb ˝´6He44dIň/ÖŇíoť;0¦î˙—Ç eó©Ú†bÁłÉ¤ŽóńęČ‚Ü<ůF9ô{g^ťqnŞ=e„ŁĐ2€AĐK„§*˘Ďcv_]Ô›Ě÷ KňŽÖ ę¶}ŃÎŢ2Ä‹“ď‚ׯOžPŰÚě5ĹÇ]t Ő(OŠ@š-âʤ”@'ǵżěř±Z?Ů5pĘkŐŽîÔt®©Y.m~L´«±áóĽ2*µëPwí7§ĎĽYî¦äZeGł¶¬k•JšĚfü?ęôf‘RÎOK.6 Ţ×LL¨”|‡E›€R–­]ëV{é/wp’(&LšńÍŻG‡ú\ŘóŐŽ“ĄŔ@9/"µ Ť?ó×@ĂĹýi­jĺuź ÜÄräł_ŽRJdO€nţMşâL˝€PŽc’üޱ»‰·ľXĺ(`î‚Ő· óűűĂ/Ą×â•÷?ÜtϢ}ë_:˝ęuđKHNůěÉG{ʇ«c‹ISě oëlD°]ýwíÚă=(ľ>s˙ŹŰ°űşł‹’öÔN{%=çę-Ĺ/ů‘á(‘ŻĚZ\Żn€d‡i™vź+~úÝ_÷ëˇiĽ|pĎ®47'§ Hď:&;ľ tSQ©Q×đŔ Q×Ę)Ôˇ\Ú%KěOwŰŇŃŇçŚ*µ©EôOšźq©IܲcďłĆlŢ•‰Îöç:R*Ib쫍ş¸ď۝盢م´SU)Kď[µzţÁyŁ6=4sĚP!âöwÁŘ\Ń|%<ů)9%Ňßí¸±ëÄÝ®,ş·Ôb’ü#“¦Ĺ_¶mWűS(«Ł?ŃqţteAUĘŇűW¬ś?>uäĹ*K‹Átď_Ţ  Y>aĚ×' fĨĘ垍O;{{pµgr‹äňű/ B©(HÚŕa/mZúěÝ ťÇ?5)Ňb’ńŇąÓ2÷UóÇ%żlhş|áLYAUĘŇűďX=?1fdcŁ)0zM+R7ﺬRJ­&ď×^Ą5ďŔö=ÚsŰďM»Ó@yHBw™ĘV‚Äňň¬äűÚ‘?f˘o6ö‰Fď{ ]4áŇoĺÖŢ®+ľÖĘËĎő°Iä1ăüéň‚Ş”Ą÷/_1wŇřIç ë(%˝Q‚ł#˙ŮGźÜ¶tÍŇĹÓšô͵^şę°žýTWÁŇҬ Ú¬7ĚÖŘIJ…ä)D ·Ď]3ÜO;4Ó4(;˙Ěťófť{młY-MŐÍţ1-/$: €E0››Ş7.šócZľ“«ëmkž1Ü˙ĺ OŻÁ«~˛éžE® ŤĎÜ}ë×ç}ó öNš1ü­Ż_s>ëĚŽ×˙ľvÚ/™ůI‚­ÇČ.vŚř6~Mzüćć®j(&2&ŇČżEy†±Śż\53ëË&;”GkŮŁOÚßlČBóîÁ™ŚUG@`PPí 'b ČóřfsÖÝ'¦_¨ąJ"îđWMĐmîŐßVbˇľüÇFżű˘Ćěy§íaűIh‘$‰ĄUO:¸›ŤUGř. Ô8KÔ×Á-NEÝ<†8Â,(CťR Ţ.dÇBčÚ 0ôQçU^]u“1†ôş´ĄÎ˙©Pé0I[»µ˘ü!ö­Ře®B^~‰Ő‰Wś-%ŚńuF&2±‰YŰŐ™$IôqőYčňŹ3éŤŕTÖť'ÂSÚ~:!'MŤuő[‹*lĎËţĽX÷Î-ÓźIеšd—#Śăă<Ľ8ŃśglµŞ˛ąyýö_’żúéW}fp$¨]×FoĎĽ\*Ł04V}ž]ľdŘř‹+F ßýf+I9^ÁQŻŻÝ·mk•‰téş×Lň€PŃXĺáâ¶ůµ'w_2 ÷ŻjŞ@®ewóË‘HfŁŮ`ť­źĎ˝ŕć3-%L¤ŻP*„ĄĚÜ}ôěgŻ?®@yˇí)ŚM-榫˦O•š:i¸MŻ'É®ÎZĚaŔ$Jî™ősúç–ĄŹ<µpJ8€ÔžK†ěÜ“Ó×˝öĆK—oťx׆ĺÓž:«§\<$±AÁ;¨ I*ž ÂRnÝŐ#/{)vćVôDQlsĺIÚoŁ 8ő.eY”áA1f}á÷űNśŢ˙ë•zsRDé÷żŹÚ_“€šÜ«â™€›é+@F‰0BŁ¨Ë®5Á:MčÉ–DöëVnÓŇÁáµúňJ“NĺE-łR»ĺl,Ňő‘]e Dh™(‚SuOń śuďźĚąZ[čY4YN±0îjU!Ęú% Ű–Öµ6"ÚŰ%?  µí; ”R&›\‹Č¬ DË›6®]őc‰ó¶ĎßTFAłYš«%•>A!*1ý–…|;HW_x)ŻöŢîëÄ»†Íź=UA¬űńĚ"2‰Y'Ömő‘$pÔ\ghm[Aŕé·Ţ]9Émňäéi9•”ŇN®Őíő¦aaÝdJ$fc%(ŃnX4ĄůęB@áDű@—%Ňkoč¤ F:(:Rő†%SýÚxT·–ÚĐ臆I’Ů"©iëÇC™śâÓŇj!‹Y0eäűy˙Jb’RéŕâjžŔb±ŢÓ‘-¤Ľ›$R“ÖŮÓ™Ä5ĚßS ňÝ%Q<đĘk&IÖJŃuLűä{»$…V̲c\IĺÎGU)•ŽÎš6…éŁ7Ĺ Š:Ş“ťç‡YÁµׂ čőţb“tOČđŹc˝F:2‘…˙9"r•ŹÂ]÷ďŘŔnJ÷1n®q뮾Ód/Ź8EŐžš6u& 7%iiÍ{.źĆ{Ç>ěKSi"_ňQBČ ÇęÝu´ÔŁöç C#OŔŔEş Űś˛ČÓ9\%5 ¦Ę¶;fíĤZ}}¶Ů{´`5zVî»kĘ*?-L»+3}uFŐ%Őú¬·+kOčšKĚÎĂ<ăţ©4™*ëđÎ6­čOť÷7t˛|—…¦lI ż/pđ‡±ZÖpşśĂŕ·bĂîđŤ{)šÖ¶ÔjŔ»XyÉg’Ţ ;pţ$Î{¤†™Ňs€qźâÖr˘ÁdhçŃp¸´čR“a_ř»“Ć{‹Í[‹Ë8«4“‚‚FzşÉËs Ťćéđ}AÂRéţŮŚiÇü{Ň$ŢP·˝°śŇ]ĺŔ™ź;|LĺőŻä%$g÷żNśđŐ¬YŹFxü”_(îŹŰ•ť•kG©8şn»}öñáCÜ-fcEł€¶Ż6!3 Ő‡ę ł _LţdCňČ4!,(«´´Bl›eÜ("Q<˙ćÇŻ<ö€›WČć=»Z8],{çłZSö<´pŕňčĂË~úô­¬:(U ưęˇ7/gśzáŮç9°w¨»ńĐ‘CŔŤŢő—|íŮýýůâÚQă—mţâ+cŃçŰvŁm¬ý,G~6ę7ý»źvß;kpâ-wďůyËŚÄŔóŰľţüXŃ;ßízfÓ㻎ž|ôî9<ü‚'§ 3<‘Âjë]˝&§ 3»5ÎűŕŮtt™Ëţ>ÔO]eŚQsĹo»/”-ś>€Ŕn檆0A¸i fgěŮ^f(×°]mËg;w=8wâÎďţ{±ĽAn©—ˇđÇŹÜ]*}úĹÇŻýűÓqŇäĹ·?µZ¤Ý»Z4CbŚ^ËY«ěëÂÖF ó6˙üăŽyk_˝xrĎs›žŰż˙;7s鯻BťřăöťĎo|čË/ľWTť˙vë>ę8ôÇmm)•é[~ÚtŠôjGëŇŇćŽ˙é‹O+¸pk»bÝś;Ŕ˝­ď(ylŐĂo^<µO®Ź«©d×Ţŕť<<ý¸¦¬ÇîZ­ľôÓż?¨b‚_hâë˙ýzçÁ“O/ľĺ‡_M €rjże!JŘ—/o3nüŘÔQ_źnTŐś˙ ‹7Ľ“yÚŠ†§XúÍ·Ż{ýü kŠłˇřŘÉôă~-–‚?ýâă×ţýŮŚDŻď÷ď¶ü™^ŐŘĐňŘÓo?Ľwá´á@űŮ‚ťŢ43Îçŕ™ł^xçłv™®_6 €»ŻŐr ©]†ţüënyľ2}ËŹżAíęáéŰß˙"”ëńZwMHň08°|ÜĎŰw>·ńˇ/żř^]{ţ˙ľŘżěá·ÎoÓ Sń®}'»ČÝŽŠ«T>ÎîîîĆ’#[~9ëęě®U) U8{x˙éŻď~żő‡»~ôň`óJĹ>1‹Yéú·w?ţč˙^Žôž}ËíľňD×ŐĂËÝE^6îűu›Ńyč>y÷_źü'ĹKúů×}Ô*Ť·§»uÝÉ•F+§Č[;:Ť)§î]8Ş©Uô÷óá€óňöŃŞZ ĎU4;<óĆ×˙záą”Řř¶éŽáPwăá#‡¸xtŐçź\+q(O[óZôyF±E4ŐőWšM5‚:ÔÁ\j¨ű­Ń¬ WZ fĚ\f¬;®kÎ7ZŁŻ¸*Zł›Şö5µ/Ô%EąŮXfÔe9Z2[ efsŁ`®0Vn©(ůYçµ44xŞ*űŮS çĘ»&»¨”Rá»…u™&;ń)QĎBďĐ­i­ÎÜYÇI!§Ą9§‹.­Ĺ)ĹEA-y/]m,0Ë5—·µ˘Ż:W|SQúKĽHpsvŤPTlŻg€d’8'Ţ1ÚQj0\}ł°öĽ еŰÓĄ†ě—‹ ő’-/}ľQĆŮ×lĹąÚ¬żŇbŞ$Ł ĎĽaÄÇ$ݍ;Üó_*0Ô‹ÝŻŘRB$‹ń·ĘÚÄ7Ňú×c§Në I¬jn:XZv©A/O2üÔꂺŞ®VČO[DIôtrăď_Q]úçăgŻšŽ ĹbĽX[WŞo:Yۤá]S—«o®1šŞš›ľËĘüONé­ń#×…i˙|ř· ‹U˘ 9¨U)ţ~>Txăěé˝5Íňö')€3{txbFq~a«…ĚÄ˝_D a`á^>©®_ĺI˙c>Đ(!0Č7â…ÁÁ/;‘ßjáBBŁ˘j 2~ݵżR×›~6«¸"tPLMţĹ_wíŻŃ7ťŮ$aö#›–GŻ[}wY#€ÄXkk«ĆŐ%qčPsőůgäÇ#ٸń0&`”©©úŕ‘´¤©Ó¶ââÉ/ľţˇ˛®Q´*++wďÚq.3_6W˘`ŞŞŞÚ˝kGzf>iŞÉßsŕlŕcG$^I;Ľĺű_«ëš(A^ćéśâZ…RU•—q6=Gă0nâ$?•áŻٰýT>Áb”Ëé^ňąĚ|‰Áb1ç_9wüô‰!7÷BCsk~汊úÖ†Úš]»~µSźKů ¤ˇřň©ŚÜ©ăý´âKĎ<ńőŽ3 °{ăŐCŠ=pÁbşrţÔ™‹Ů T©TÚ:ućyžďěĺůşRxžď#6cŚĄYgÓŻŽ=Ö߉{ĺů'>řnŻÔ9řŕďFýŐUÂńŚIW,Ď<˙Dú/›ó«ô˝INU(ĺ$ĆâÇ®ü×cóžXwvU×ĎîťlKC~ĆĹËą—IKš2ŐV¶ţOgňŞ =Ă"'MśRzę—umĚ«3ÉěŐž’[k覓,~Ýś_ŐL9.$2˛˝Ą—ł/8’ž8ej˙ÂS?ßvĽKß‘L‚äi[ĘÓ榪ô´´˘‚ěŁçŻ:*Xć™ô¬ÂâęşÚŠâÜOŢyíý/~$?nů5Ë‚Y»Z,Ćś‹i§Ď_1 Ě+¬ű…âjF8ŹĐ¶”GĎä× ĄâŔᳲt^|ę±oö¦;kť‹ 2Ň3ršôşňňŇôsiĹĺµí1¬ş÷¦—˙úÄßí“8eHDD‡LÓÓ.•‰‚ąŞ˛R¶~˛5đŠ¶•GѬŻîŤ÷>ß&˛>ŢŁwׄźŽĺ’­Ü‹ ŚÁ#¬˝í:ťSŢw`.fi®Żđý±L‚ĹR’źsúěë!$c’ĐžÂ(%MŐ6cJúá-[­nĐë* Źź:Ó" -͵ż9\ZŮ ™u'NdŠQ޸ŚógĘjő„t—îţx8 6ăNzfľÔý›™\ ÄĚŤ#č2dŘ}¸F—ń^ŽŽćňťŤ@;–"}䕉ďž­WőćŽĎ(AGé.Źß{ýŮ%‘öxK8ÂDćą,0âÇ3k˛íEäęĆ×řčáF“„Ćiťřę˝:Ö?=1Dg§˝Çn‡—QL ŽŚRľĚ/gíŃeŻ :;"¬¸¶ň˘®ĹV¶ż7q„ŠLş5~Č“S·0_ëú;“5H¨§×gŐŹĄ7>Ő’oćHýŘD‘Ą0vöšX粏żÚË('ßď^â€Öą¨ë/ř-‡Č!ή‘Ł˝nEd˙¶q »pşÖ'Vý‡˘ó|ŽČaÝ® Än%ËQD% ”c’¨Ť›±ëűî´Ő¬ ”Ł`ťöJŻ=…1˘pęď›?iĎG‰Řł<ëËBţŽÔŁĘ…K—\=(írI˙Ă/Ţhí(e’3bÔđ0÷Ż·ě¸‘ Ŕ¶1ăĺëCgĄż!ě{łlÝ`í`÷ţ×ë̶wYte×w»l !Ôľůŕ(ű-‹.hXoĘŮk!„té „€áš[ţö&9 <ëáNČ÷„ĆuZőn˘é.ĺk‰mJ(µĆŻě/@¶Ś8^…¤yí˙ĎŁ“ÂŁ/´2kB)$±vkÉşjkßď É×Xńţpďů›ţĎ@®•Ú^÷Ş´„‚ÉOĽ8&Żal?ë6Ű&”€´}iý†öé­íĽźtčkł€Î#y×Z˝kß!“ĺdŃw{ kJ 1“ŕÖŞË(*! Ď5w.Hß˙ý…üZ…B!Z,Ľ‹wld’Âlhş’“g!Ib߲č†ÚúŔD™—«oPTż$ňŞ˘»,2/‹ŕBă:#Ö¦«˝IGľ—ëíěéśź•Ó ßK§N¶Ëbx\DsC١Ă'şáLFxĎ5wő††ÉŇ÷"Dq@h䨔Í5EŹś2 kJuŃÁŁ§Ě‚ÔOyu)ÖĹŰ?"$€#¤©˛,ż¤L`¸Q›IG Űf!A9N45]ş”ma”ç9ÂضénŐí¤´Ť)˘(I’Bäá"o4J)(ÇS‰‰L‚Ôm5҆×úMOš9ŔÄ…şŽÜ9"uGŇýÉ#?Ť¦ ±Iů8ŠăŰt ˙ŕý4Cż:v×°1‡F }>”ŕŕzpdĐT-98ŚŰlß±GGŽŢ6těÁärômŹEËčý#&kŰSz“2żő‘©ßÄZżĺAëÂF푺5ŢQM:UŻŁa6ß8PÔË!ńł!c÷KÝ;"ĺÓhm6iëđŃ›QŔe˛ď„‹ŁĂgşşŚó{"ĄKťx-őE ˝NÄH×\źDS¨SÝ0bň?Őęäť#"ç»ő„'!k‡§śXą4mÁŚhž˘ÍŹł*JÜ7âňęeg–Ě?ľx~ÁťË˙oő[âďć{dĺĘÝÓFqá=ŇV-ż7Ě€’ŁĆEĹ^Z˝âĚ’ů«–í™5)ĆQ‰¶X–®.~§V.]ę€ď롊śeyň¸ôůÓ4ťń» $wňY I'ćNQ¶Ëě†ä·Ő„wŢ»léł1A¸Îhˇá|ýëŰą•µEGâ´j*˙Řź~˨®(..«Ę:±#>HÇĆpŹžRPU·n^2 ÝřŇ{Ö\ޞ«SíKďok¨Ż-«(ŰöţËNš|*ŕxĂ%߼räGĚŃ–ä”T—•Ô”ä­ `áúĘksŻäčuµÖÜŠţÄŞ·ŤĐG(^đâ{f—5ćţR ôć8ż’_®yěâ+Ô€j÷ĺÂŹ6Ý €ň×ů§­Qš˝iyĄE…Ĺ%%EEEĺůç÷Ç;«ŞčüNf@¨wŔä*)ücş÷‚k iťôOWe÷©w˙csqú.GűţWż*RD•^T÷ę˝·ŕúÔänÖ&ŃY `ŮCŻ×4Ö_ÍĎËÉÎi1čźżg&ĽSłĘę‹ J«ę.ůÎQ Ŕńď×Ös»Y6G9 ~ń=]łˇ°¸´"űô tĘŠŇÚ¦«ůůuş¦Cß˝ĺĎÔçO{GÎŻUp MÜ}&§˛¤°˘®vĎ˙˝ćÚ§,(Pľ7îĺY'†8*ĐMŁd_#ń3+[Ďüř>Ͱ˘Ú¦ FP)•¦®z¸J×\›SYSwöŔ Áî8ÇčžeŃ Ťm&]敾í}‹|˝¶©ˇ'Y\:¶•˘¦­*î@ě_<Çô&ťë’Ĺ·oúsť ’UŽÓ -¬éŤ WôÚOĺVOYľ®ľąµ ;»®ˇéđ·o;–­ÓµXS|ő†Ě}ýK˙î]^ťt€§V®K׬ËÍĘ®i¨ÝýŻ:+ű°8űúmfŹňÜŢXGĄm•úď3šôř«C Ő^ăöîÝ;)ܡ{«»cxŕË7ü:Ë«?ßô¤™ofiۨíI^CÔś‹Bĺ ˇn)ۇZSśů. ĺIłĎáŽ$9úP‡8Źń§RüÇiőČmĂĆ;đśéíŻrźă;v÷P'5Q†8Ąţ–˝Ú UÁOÄŚů6V!»u÷sXî˛Ćß3ĹÉžH Őí#"{XŚtľ91ńŐA©Űkş­jäU„í7Ž€ŰmţO&»úPâé4ö·ä°;>:ůÄpŹÁę 'b¦ĺŚ›éę:Ů{Üľ¤.uŕ±0`ÄűQňJ >ý@L®O—o@­Iţi“W}ŹDŤúbPwí•XëüËíł?›29mÉśŘn«™”JU”›[€F=36>çŽĹ“\d°˙>mFń]+·M­Ŕąí[´đŽ`3"Ł&zi'GÇd.ź—ČOźô5«^‰¶ĎşÔ)L÷ ŕ­uY“đđřéŢö„L€jw.Yň\\(lüoÜ’'>ÓbwÎß/Ú?äµęš” 'çN–c\k eăĺ”0÷řÉ3?mů©87}¨ł€É«7 xź”ÚóÓëćă>öÎöŠ‹ű]‡řy'Nť•s qRąőndş3%rÎ]eLşgćP´ ö×M˛óÓč7ZňÍ/G©Vąú˙ăËcż}÷r¨źGŕ5˝ĽÉňßW×H3359}j5!Tžaw=´éą§@ÁŹĽą3s˙g Gó Ž}`æ'lÎÔÔţč‚❏ţ;ú°š’âňâ‚v ™źWĘÄoٱóąő+—Ţ˙ôî?%E¸2ţ–v«ţĎWź—:më¶Ýß|đĘâ•÷üýĹŤnˇ1ßü´íń5·%OśłmŰĎcÂś˝˘G}˝uű^vŃŞű·ţĽe°›*4qňý˦ óůkYwgL°6ľ¨»cŘb°Řôů9uľé®™ÂĘUnąŇl(µ8Ďđňź­'•ŚbKŽ5eđ1Zr€¦¶ €©¦Ő!ŢÍc¨ŁÇ O/N2€ŘĘüÖ††/tÍĽçRĹQR(9˙Őář)’ľŘWČgŹBg(ß©@Ýś"•5?–ťżűŇąs%‹UQşÚ‡§‚^´ň—ß~đ¤«aµÍhóŤ¬óÍgtÍRô{±Iď…ł*CýńĄ3©ůMç6ŐGa0čŠĚśŠBbDĹwŻs'’®1{ą|(ł0Ń(ůß8b˝DxŞňďá>“()Ą¤Ó_m˝<B$ŔĹÉwA€ŰŻyĹŔŮÉóÉÁa˙ǵĹWîţôXNˇ0wÁę[†ů}óáűĽ˙ľŢ9jÓ=‹¬ĄŰdrđKHNůńă§M=ůÖĺ-&ësµžJn'“ĹbiŞŢ¸hö„čK6DŇs}:ů ±nŃKîgŠ-^ňÔ…p”ȧ˙„ÜÔ+§6$?öÍ<°»k/úóy€¨“á tSQ©Q×đGŕÄ4ęZ9…: ”C˙쏭µ±îQNdp ź´rJÂÖÍßPăĄs§2 ŞR–Ţżcď/ˇNjIhhlşÖžŰ™XL’dŇ´ř j˝¸eÇŢŐłź<óŰí{żúçă™'wž*ǵçb„PV~%"yÖĚ1Co]ş*ŇßÍd´tadW}r_5#Śq•űˇT$mđ°—6-}vÆłW śĹPJő-Ć{˙ňÎ kg,ź0ćł*•Ѝ\îŮřôK/lôQÎäÉĺ÷. ŰúČçÎ!Ămy™ô%‹¤po0ÄOşőŰí{żúç¦ňĚ“ňtŽŽęęú†ŢĄs˛Řq˛ ň‹DßÜ7}îRNˇ "ŕýÚ믴ćŘľ;TA™EN1ćí˙ĺLˇJˇň Ň]^˝—,XŚ®ţ‰»víÉřmKĂĺý?n;čěä(IRw›ŮG KKł^$hłŢ`2[ăĘ’§pűÜ5Ăý„i©C‡ĄL1xĹß9oł(vXőĄXłą©ză˘9Câ˘SgŢ?sî]3†űýQǢhŃ †ĆgîľuXŇĽjÎgŇŚá{ľ}eĚÜÇMLüűÚi#R§ţr˘čęŻ+†{2]äŐŹoşŰ„% @,nĽ´áJö? ÓÖd[TJŹ Î¬Ľ)óŃl9EP+=Fk€#„'„ł:ÝŞŮYUv¤%ö­Ře®B@`ڧÚANÄ$,ďŞ5›łîÎ81ýBÍUq‡¸j‚nsŻţ¶Ň(€p őĺ?6úÝ5fď°Č;}hŰOB‹$IŚ(;%2ÖqÖ@dof<éňŞDţ†pŕq«ŹsÉúSö…Gsáçč?Ó Śz}vsÝa˝hd„”2Ű:ݞŢ@ł-T,nĽôČ5 &ĎH:ĺR«ÜG;Á"BÉ bÖČÄ&Ůiy7Ű^ęwIć)m?ť»ďÔPQ˙sI9€ĹńCÂU˘Jë:ÖS«vpžäÎ1Q˘|ś‡'šóŚ­(Ą’ąyýö_’żúéW}fp$¨]×FoĎĽ\*Ł04V}ž]ľdŘř‹+µiňç×Kî·._ש¬ž‹6‡Ş7úÔ1yťŮ 7 ’¤PÝÔXÝmDp<-–ć†Fű™¬vĂz›03/Ô ľÚ¶€ţĘŢ…·ÝöŔ†őŁÇÍÓiBfLOŐ7ÔÄMĽćžkĂK¶QJ•±*뾥«ď_łj˙ÝäÁcdÝ:zhÂĚEŹř%M^ł`,DK{.yűĂ×~đKĆçŰöýsĂŚúš˘TőK}qźš4ŁśBˇŕ9Ž# Ŕšő‹v3Şâ' qđŽZ63‰—Z%…vpŇhŢXź][€ă•RcÉę #Cbżż˘xĺ±Ő2Ă>dŃÉÎŰáµ|NŞ‚Xz‘E&ä–©Ér¬=ę—4y٤áM55}JçÚeq炱ă8ľ“Ý`"S:ö‰†Ř׳+&ZĚ‚ćŐOż›#,Xő`‹©-eţ'ŤŁˇ˛›Ľú:ۧ”3·6ěÜłK×b67–W ŕyI»ÚĚľµ—`  ¤=Şi۶łÎjŹÚňܢ: ńꙬbO•^éĐaŐ ¨UrJ.¤×Ő»D5×ŐŽYŇ>¦”iś5úşŠ‹é°ŚQĺä €ń”&߉´wĘŢ C ”Űń˘÷oěô”ß㦛ąK61[ä$9…ęDסź&ĆţÉßz&§7ĺ>žőŰŚô‹/1FŚu&€đTĘ{!ݦ’üb($‘1A€f1VY”~*^·ř¨‰©tW¬™PđJöńéé9źŐů¬đJVč¤H ,Ĺ&F8UƶöLd’ĹzA‚ęD7k í|>¬ĹTsRßtJghdN*"0f±\ş7Łđ§zNĂIcL:ęěcg!ôł*­M.f-Ç b" ńWC :űó]%1ł$ mÝ$Ř;đ›9łß• ßÍ•Q-J/,Ę52€čŚÍWM+c%¸*xĄvV°?aĚH=vtgłbóÄd`’ H’™1ŔXÔŇęŁŐ>(4’_$×™ŔüĘńĂżÚúÔ…˘ą‰‰łüaď2e}SłĽźłk‡î˙?j#Ž­3eć|Ł\—źţ6˛’Ůb  Š5ë żßwâôţ_ŻÔ›“ŁÂĚ[ö@)|÷›­€56u{.ŮçMyc•‡‹ŰćמÜ}É<$ÜżŞ©ą1ŻQň-»Š.ůć—#˙ĚFłÁ:[?ź{ÁÍ'fZJ$H9^ˇTK™ąűčŮĎ^\€ňBŰS(›ZĚMW—Mź8*5uŇ„q›^˙N’]ťµ%Â$€I”Ý3ëç?ôĎ-Kyjá”p©=—Üđ›I’ Y‡iů&zhĎőá!° ŢAĹIRńT –r뮞yŮI±·•,˙GĹ6×Ďňí›ß…z—˛,Ęđ Ű^@úýďŁöWÇäÍÄܫ♀›é+@F‰0BŁ¨Ë®5ˇÇovH¶I "¨űĘĄ·źüqKnĘÉŽ0‹(Iŕ8s…®ĘŐŃůúz®­ŤŞkmDkÔuK›c˘¨××î۶µĘÄÇDFɉ‚śKöN«+ܰhŠopŕ¬{˙daÜŐŞB”őK˝p—P…¶ő^bml(Ľ”W{ď÷Ťuâ]ÂĚšJ¨áĚ›ÖŢńc‰ó¶ĎßTFAłYš«%•>A!*1ý…EĚ‹EčęŻvâ5{D€z®Zf_Φ­m©2)âŁCĘjKÜ´._ôC:×&‹A1BGµŰ €Ä#gÚ¸vU/hđ˛Dzî ňĚëÉ7Ţ[1Émňäéi9•GE©#ĺtf9€š–şÎ5죵]]âôµEŻ˝ńĆŇĺÂ&ŢýŔÔ¤†úb•JŰn3Íč×%+‰IJĄ‹# Şy‹ĹÔ†˝(˘Ľ2©IëěéLâćď)PQ±ÝŞËk’;O)şŽ)cź|o—¤ĐJ‚ TE‘Y÷ĹTJĄŁłö¶ż»cČóĽ$±6;żI~źĐë7Ö± »Mxň]šňU|ŘęŔ¤ď‡Ťß7D­ĄŢ‹CÚSĆíâ¬ŕ4ŃgJÁ„ÔOŰ^Ěkżqź˙°†§~«VJÇ1GRĆ:Pí¸ô1qűzÜć;éâč!ŻF ů(abZJÄ27€öăŘ˝^~Óď6lsbŘ źg˘ÇďOr âŃm ” zopŇ3AÖß< Ű‘Ľeč„ăÉ#>Ž śáŔiR§BaóÍgq~©NN)Ţ.ŽŽ.4ň™¨Ig“çřĄü2"řŮEIÜ2ş˙Áąˇ 9_VěŰÍ›ż?©+»48H‹öń ß” ţńéE57RňÍ,ÇęĹ(qúw?íÎ.¬¬,ĚŢó󖉀ó‡{3tyĎlz|˙É´Ç×-0bŢ:ĆŘŐŁ_ŞpJC笕Sd§'qÓrŞZOý°ůá9z&}ý]łd.ÓW>ŃÔܰsű÷.ŁŤžxěLÚóŹÜ÷ŹżkŞĚť6ČŔđŰďď©d@8%€y?_[vŢ‘ŽWtů¦k}(„Ź]Tmľ˙ôí7>úZl­]15Ŕą˝ń˛“bgČoÝ˙˛9­*ýÎţ77‰d/vŁćŻo‘¬˝@bĆÇNŔ Ü dOÔo]•g,řĎ®s>|A®÷MÉşţŕ@ćŢźë÷®Öćôá˝ë—Í0bńFĆ,+'„Éß­~ä­Kg>·éąĂ—ŠëK/ r„Ç ó%µ×Ňs»ń:ş˙ąăá—QŢrň»Ź_ű÷Ś5Ýž1ďž×ň®¤˝đěóżą,éKçŤ ¸ŢţÔšëČţćO˘¶ď=đŹç6˝\^t~w¨誝ÝdAHŻÜ73‹îÎYCŃĄď€PŽS+9Ď|~¶:} fxYCëş[cÔAɵöĹßśąf˝ÄŘ®-ßěőî·űç=ôúꄬ[sOYłúŮ·ÉU©kČϸx9÷ň#iIS¦zĐŠŤ÷?xüJ9ˇ”Ýč;F)‘šŞIKš:íJľyĺČqýÜ\}]ÓŰěl†®ˇ.ýBzymíî;L*÷iÓÇV\<ůĹ×?TÖ5ŠSeeĺî];ÎećËGDÁTUUµ{׎ôĚ|ŇT“żçŔŮŔ#ĆŽHĽ’vxË÷żV×5Q‚ĽĚÓ9ŵ ĄŞ*/ălzŽĆ'`ÜÄI~*Ă_˙˛aű©|‚Ĺ(—Ó˝äs™ů“KI~ÎéłÚ.uú¦k}.ĺ3†â˧2rG¤Ž÷ÓŠ/=óÄ×;Î0ŔbîŤW)öŔ‹éĘůSg.fPĄRiëÔ™çyľł—çëJáyž“Źż{”1c(Í:›~Ąpäč±ţNÜ+Ď?ńÁw{%›ﲿşJ8ž1éjĺ™çźH˙es~•žŁ7É© ĄśÄXüŘ•˙zlŢëîĎ®jâúÓÝÖn4ęňÎť˝\Xŕéťwáđ—[@ć™”[}îŕcë=“WŤÖş‡Ď^sĎílŁ 22.g_8p$=qĘÔ07ţŧ6~»ď,-無CÍŐçźyü‘Źć€W†DD´ĺŇĺťOĎ*Î÷ŽŚ›8aJé©_Ö=´1·ÖĐ= L'Yüş9żŞ™r\HddOÜ_xjăçŰŽwë;DHĎ‹Ĺsńěé WÖŞŻNKK+*Č>zţŞŁ‚ežIĎ*,®®«­(Îýäť×Ţ˙âAbńă–÷!‹ÎČçžKË**—8ČĽÎ\Čf@ §wŢĹ#=ČâOŠ«EÁ¬qnGlĂOżĺĂTŕP_ŇąY΂ŤKoł$-Í5é=ŁńŢçŰDÖ›·0ĄŇˇ0?ă\FN“^W^^z6-˝ŞÎPQš•~1§IŻ+++9ź‘Q”źyđHZü”©á˛Ľ¶źú2Ă,sţ•sÇO_rrÎ7›Ą“»7o?vyô¸ ľŽâKĎ<ńőÎ4Ök9„RĆ$ßAĂnKMĽzµŽŁůßýt€„Cűö5Iͦ¬ó§Î\Ě&„čkrśĘ=ar VxjăßË X:YuÖÉÎ3JISµÍ’~xËÖ_«ôşŠÂă§Î´HBKsíoG—V6Hf݉™"G”*.ăü™˛Z}űő"»¦§§•Ő‚Ů:˘]ΗTJÍŐ^ľ±Ę”v×ĚŢC]űi¬×ű“ćľ> Ëx/GGsůÎF ťßŞ÷Y8%Ţ3<[Ż4ę Ě×Đ^CXŻ1§9ÂDćą,0âÇ3k˛Ą>‹íD]ľąÄä ˇqZ§ľzŻn@ô‚BÚî˝SB¨˝ř¨vŁJQ@R‚#ن/óË™Ľ ˝&GB„ÎŽ+®­Ľ¨kˇVßu78BE&Ý?äÉŹ©Ű°“ Y„zz qVýXP:°S-Ňţ:\Ć|ěě5q.ĺą/I‚} ëPÇÄ«sY×_ňZ‘Cś]#G{ÝŠČţ…mcEwáôűE‹ě<ź#„ ŘD_~˛Â$ ”c’¨Ť›±ëűî´ŐÂlü¶1HťBĹ_k cDá Őß7Ňž+ŽQęeöCl}$üž ÚeßoŁĘ…K—\=(írICaŢxí(e’3bÔđ0÷Ż·ě¸‘ Ŕ6er„IöBa€lKלÝwLű,ş×đ‰˝Ë˘Oîm ;—~µo>8JĹë“!”R "‰]‚#ÂQ]Wţ„Jş>G[mŻÂ~ˢíu"@)µu€Ň8îJŃÁńÚm]›ĹżV›)‡¸Mš÷Đţ˙<:)<úB‹(—@(•}‹±vkÉ:«X˙ßď 7ŰćýN$ű$i‹^ʶIM» (!h“á Ú,ż­°şÍ¶;•)%¤Ca´}O:ôµŹY@Ž€ˇSƶ/ G¬˙&ö&ćÖwĽ¶ĺtţ†Âasťć0Úáňߊ¬{ú@̶ŤäCk›ňÉď€XŻjŮ>¨s”‚1‘1BGcLl·„đ„Ř˝ěh[ťöŮę1›”ögŔ¶U–1““ď[˝í¬>=!ţ$Ű‘ŃáQ«\:zVřîâ7^1Žçi›MIEIâ8^ö»ĘQÚöΡŤˇ˛;+Žçĺ§‚`é"Ŕ.#Đ ·PJ$kLejoćó˙‹rĺ(“:);!”ă9HR;ČÝS(ĺ,ĆD[7\<Ď3&‰ă)%QěäŕÓý)™PĘQj»:ęO.t›xő‹—˝rz‚ŚńÚŔys&«ąŽIˇdŰüëI!”cĆżţX˘zźB(GEAŔńĽ$Š7Á­s'Í蟎ÉQío^ĹěuMČt·6”rO-‹u&¤Cµ:Í,k´-vyÉ“yJQ”ťăH˘ĘQÂDQˇJ/X,c<ĎˡÖmŃîĹ™_'YŘ)‡q'ŠB—,vű—]”ă)(t„_§”Ťz÷.‹îẖؔÎńă{j)ĺxŽŔŇyK…Rk úţ EAX§ör'I"Ƕ˛č•úDٞmĎ+$J *g6›$žç%I‡‡íęÚÓbŐNÝ(e’ä;áÉ»fĽňĚÓeFĆsT”]ęwËL9Öu±ZţîV˝[J§1E%žŁ˘ 0žç:ÚNĎqEŃŢvCg ™(ŠR}îĎ7˝÷¸§¶łžşwć8Öp¦I˛ô&U†oZ*ͨ“Ň}¤3Ś–úSM’ŔŔó~·{čOŐ5— ňú‡hŽA*BÁŚ˘ˇČ( ¬cîN:Ň fc­@(±>Şz0×ňˇŠŻ&é­ČŇ7ňŞOŔu¨Ć)\-6™uizI•żÚ9ÁQl0ëŇő’h§ŢCéŕ§„Čd_Ą¦2Ł©‰ř-đĐźě­ÎÖĂxg÷hEŮŹu¬˙uű†(nŁśx"éN4™ÍŇ€ BČ\§űF-w˸'ËŘĂ|ś„rÉ~ľZ"+Ż4őĐĺ¬~®n.‚9»ą…~NNĂĽ<[ŚÍÇ+kÍ#Tµ$:4ŁĽ0łÉÄS*JŻTG;kfÁś×¨7KL~.1Ć@Ý]a4”L”Úć‡ÍîŞF6ĐĽÖýűéc?=uô§˛ŽńfM,¬‡!^>ăÝ>Ë.ü#g öH^tĄ†Çü-!đÎ_ö• ČfiĎX€±čáĂ“ZjŠ=enŰÚ##4$!µĺEµa‘)#G4×<ŇńÍđB5fÂd'ŢtŕŔ!Łů:KľYĺ€)Tnqń‘< ”‡”—y©ÁlChtŇč‘ ĺ9§ŹťÉYż„Fĺ8J$‘‰’Č)Ľ×>t—§†\:ýőĎű%«(×¶{×ĎąB©|«“ŇgFąG¸y„řh/fdK ˇń‰R}qqEõÎkí•Ί‹wÖ(DQÇ),­u—óűÝ”§Ň–ߏ»<‡V Ůżíż˙޸ň›ý—8Žö_ú7BňvňÄĹľąaöĽI·šq#ÇDr7ŚIt34\*.e@@h䨔ÍŐEíÖ& 4bTJň€Ű{µ!`Lííâ锟•+0€Đи¤q‘Í Ąź0Y®A¶ĺP‰Ń”±ĂÂý.ť:q± Řîćz­şŚ˘˘đ\sç‚ôýß_ČŻU(˘Ĺ»xÇF†()̆¦+9y’$^«,d«—4<.˘ąˇěP[»B#RFŽh©)>tě´É"şůF†H‚J¤ŠÂ«Ąµ “2ƶň2406Ń­Ą!ُ´ëIcĎĽz’©ŐÚřF„0ÁB)a9Ą ü]k—¤íŰÚ&KßońĺÂŰ[*ëśh•ו\A‚›o`Dp9vXeaAImo¶Nţ“—_°ź;ÍČ,d ˇń BMiŁEävńâ•ë´™„p”0đQ1±m”ăDSÓĄKŮFyž#ډmK‘îVÝNJŰ"Š’$I D>dç˘$J)(ÇS‰‰L‚ÔyĹۆýÁą_=îw ţô qGFŚŮ7|đÓÁößěP@ᜲ?eÄ›aŕü4Cż:v×°1‡F }>”ŕŕzpdĐT-Ş ÜfűŽ=:rô¶ˇc&ŹřpŁ/ʶEËčý#&kŰSz“2żő‘©ßÄĘoŠS˝RŽ˝=iÜŃ‘Ă^ ă×>ăN¤Śţyč¸##“ţ˘ěü4Köěě˝$dě±äŃŰ“Fýňíż4Ŕ‚^«mj¸šź—“ťÓܬ{ńá%yůέ íu>ýĂ{ ŔÖőOxu—©eÉŻŐ4ÖËÜ[ZšžżVÚĐŇA®ÝQµĄî#ÚÁŻŢôĄőůÓßŢ‘ĺ•謰ěˇ×Űą77ë^xhIďŘĘňÎŤ]ŮçČśăţܢ×ďZşhÍ+%y‡®×föh!ĎíŤuTÚ6¶żýżówÝsÉJ¨ö·wďŢIáÝńěyV`#Á~|Ó˝ÇĹi”Ŕďŕ(F“ę5áÜ(żDµçÜ ©YŁüÇ9čâ¤[ýzüÄS)Ăß ŕsGř„ŁIŽ>Ô!Îcü©˙q@=r۰€ńŽ/‘'ž.>ékV˝<8m žu©S.,îAŔ[ë˛&!áá!ńÓĽí ™Őî\˛äą¸Pܰ7­k%Ši±‰;gŽ˙ßô&ŻUפL89w˛ěľć÷¨!ˇ@ěČq÷®^ě ťůXskó=3¨€ÇŢŮ^™±O D%Ź[»zI—ořľb–őNň=ăndş3%rÎ]eLşgćĐk-ůć—ŁT«\ý‚˙ńĺ±ßľ{9ÔĎ#pŹš^Ţdůď«ë$Ź™™š‡>µš*ϰ»ÚôÜÓŹŹ ŕ €GŢÜ™ą˙3ÖźŕŘ6lzâńÇćLM•sö!VB„Ä$ÎĽujň´Űź~ę‰Ů“’{Ďhł7}RśsH^ŐČ.úäń{pc«š¶ ńľa/|qäʡŻCüĂ#ü˙¨8UĄ-ż+wY‚P†śĘ«zíŸuĹőĎëţůMŢ©ź­ňzä%{SľűĂaSkëo?`ĐČńí–Ä`2¬šá;bÝ]KÖ¶Ř« Ŕ)aîń“g~ÚňSqnú`G%ŻĐ¸i“Gô€ŃlľV߲ŕlSÎ'5€°±KjôŤO,O?t¸‡Z…®ý­Ç~ú@Ŕapú•Âűć pűŠ»¦'‡ÎXý`uiö0 ď=¶¤ÉřďMkäĚý—…ügŹŘöv™aͤhßŘáëîZ&ăÜblľ{Z<8‡„!C‚ýĽćß·±±ˇfRĘ}řĽú–¦ŽV8ČŽ¬ÚęüăTť+Đ^vdJ@ĺę“(s×ŐUMŹrt>§¨O4¸ž-R÷­ĹÔşfbBfś>“Ö&w7Źvî u•3bĽp´2{źšPČ-~ăîe‹ď|˝´ŕč5ŘLŮěűFÎź?וâ¶óc#˝‚‚ýĂŰ-dDd ĎŕŘűÖo|ü‘uqa>rn[«>gJ €ŕÁ3gNł¦L @ĺnSÂh˘fŢ6{ę­óź|ú‰[†ÇŽK~đŮwJKJ_}ěŢ•«V ň°j/ł‚v öď›n=Ncg©Äp‰tŠő5•Ýč!¶2m°h%h»Ő'?>ń\äH*v5ąPĽ“§7TI¬ľĄµQr qŔŃfpL’ďݡq뼳îľ`”śË$w‹JÍô–ÖJ3ÉĚŻÖoşSĹ›ą⬎}uŇbÔźÉΠ篦Îw¨c\ŁR-éŻ4 –ňÖ†jŢ1\ىQYjL‚Q2_5–—`®5Š­öźšHzˇµY` #ÝŤYőőĹ"Ô”}ŐY.‡PČîĹ\ŁzCLöábűŤA҆k›rL¦&Iăď 0µŘd1U™eµé“{ß™ ŔôYzn†ŻVCôf{ ,×y”»·®©fg3ysT¸Á"Ćą¸Ł¤ž«'2Č×˝n‰MšŕDżÍݦĺU@^MUA-‘€ŞV}‹ő0‘đ„EańĐ”÷†G=´ýçRQ§H ®çśÍ&cas ł(qZŻ>ßž:\ÇŔ©śŢś2)ś7•čĽPźă•šĹÎw¨Ŕ qőđ楋µU@ßŃ‚šëw:ąqb ˨©ŇDÄĆ*čE‹ô{\B“dť>zĺ,Ç€ŠĘ‚Es,‘uď’qo\Ú ś;ž—›o€(‘$`ÜŕÔ†Üă?—pżą\’ŚC˘‡bÇy‰ĐţGȸůĺHf“®˘¸ˇY0«t…u&Ý2ÁÁpőÝż˙@ÚńťňIb¶Żd’S@(cď·ĺ‡-QŠúüÜłfŮť ćî»X€S«8yIÄDâöÉ–ďŁŐu…:Ųy˙v{S«ýkÜíĽ(GEAJ;ýŰ˙ĽZYśÓl ËćĄ{›ŢÔS}:˘ýŹ’ÔqŃ»§VôžbŁgBeaľ ˘AgfţĆŇŠR7wăwĐ–ß—;!`pŚî«1§ť;*Ëăf˘$„óiÇ—=’â ˛OÍMŁPQY o6©4•Yg˙/ď‚e@mKwb’ 5gWꨟ¦¬yô‹”{%IP[xů@™G|\bě°ÚĘ’šşZtéXÝdÁQb°)L5^_žŁŠťůĂ÷Ź~őá?3Ď›@őĹc¬ŁBµňĂEž±Ő°ú©·?űë]wŽ\d r;ďö:U”IWťW\,ע˙˛˝XÖe(·¶«şĽHßj¨ĚĘů(÷˘TT4·XG!¶ćd^4 ě) ĘÎýx°Ä´zń´Ć˛ěöVdť7¸íţv :Z»-»^¶2µ˘ÁŔ¤kČm¬˛ $ý§˝ąMDN ëŤ^.wuŃš›ŤĽĘE»F&ďj“»Ŕ¤«ĎŐŐYą§ý´÷J5@Ąľz™$X”Zď;VŻhŁŹłÚ, Ě®Íě‰(ĺ$Q9ý÷6Mýő'ťńÎG˙ýđ/+^ú÷6ŔÖB‚߲ă×Aj]Ąŕůđ}wÜ6{Öąüú!ăoůúß˙”­úĘ…ă·›—:m뇯Vç4ůĄł‡[ţě'_nŤU5ä5`íÝ«OS:ęë­ź×ä]h˘®O­[5&qoâäű—M,ć1ó×L"­/É.©ŁD…ŘÓ¬ łűőM·ÇúĚçš—Zš„ µA*َ!K’ŻBu8 !`"8_ÇđŐžĎçđĂÜŔTÓęŕă1Ô‘ą9xq:3­Ěom¨Ú_•yĎĄŞ &÷P %çż:€sW*8A_lŤ+äłŔGˇ3”ďÔ îN‘ĘŠ· óżÖ)ś8ÉŇ©‡´“Ú‡§‚^~lD¦î˙—Ç Ąţ·Úş łő1;‘ŹK%Ťµ' ťZaUÂ$Ć:ű¤h*^-@ ’?ęlŰs`Ń‹="fŁÍ7™Lá̵fÖ6WŠŃďĹ Ś šęŹ·B­ZúĹ˝ÄŔ č%ÂS•?Ńç1»Ż.ęMć{†%ů Gk5×ÉBDƵOűç‘CZßÄT'É€PžHf¦~nÔpcSĹž˛&»7Y„•I)NŽkŮńcµ~˛k á”+ÖŞÝ©é\Sł\ňühWcĂçyeTjסîÚoNźy!łÜMɵʎf;ˇk•JšĚfü?ęôf‘RÎOK.6€üËĺ<±%ď×^Ą5ďŔö=A©$I+Ö®u«˝ôÁ—»Pµý&Ŕ=ű#µ Ť?ó×@ĂĹýi­jĺuź ÜÄrŽRjťŠş9ř7éŠ3őB9ŽYßDv€ĺŽ#˘€ą Vß2Ěďďż”^‹WŢ˙pÓ=‹ö­ čtmÝÁ/!9!äł'}üí=®Ž-&ŇÓć.‰&‹ĹŇT˝qŃěÓňĂĂL˘ýŚť]”´¤v:ľě9Wo)¶xYCis”ČWf­«˛ëŃĐ@iËďÂ]v|覢RŁ®áŹŔ‡hÔµr u@(—vÉr­ö‡P* ’6xŘK›–>{÷BçńOM ·pJžĚhł$»3@”Y€ßötk•(P)y9¨:@Xě„ß|đzt¨Ď…=_í8Y tz¸bWťË‹IňŹLšůR“¸eÇŢ;fŤŮĽ+łý)Tw4&EX,ĄTßbĽ÷/,ź0ćë3bTDĺrĎƧť˝=¸Ú3ąErů×$ J©$‰]ŰĹ)8X[ś o·đI+§$|˛ńyLd~m­ŘąĹÔ!_ťwxůĎËź]3_– čň–·żĽ±łW B©(‰2÷Ź}ž ÚÔÜ7}îvŃöf€ňąPN­m˙đ‘gEŮ&÷5ë–D‹Ć=xĂźţ,2.Ä]c%‘öcŽţď‘RÁŇҬ Ú¬7ĚÖŘIJ…ä)D ·Ď]3ÜO;4Ó4(;˙Ěťófť{młYŰ­zHt‹`67Uo\4çÇ´|'W×ŰÖ<1c¸˙Ëţž^W?üdÓ=‹6ţ\-źąűÖŻĎűćěť4cř[_ż2ć|Ö™Ż˙}í´_2 ň U÷wÝfv$Řźoşő¸& €YDes@ŞCÁŰ%‚ě,=žÎ*żĺAg‰ú:¸Ĺ©«Úw¬¶vGeŮCě[±Ë\…(ŔOµśI04X ?đh6gÝťqbú…š«$â/pŐÝć^ýmĄQá ęËlô»/jĚŢa‘wúĐ —Đ"I#ňmUĆŇëŇ–^8˙§Bĺ gż©Î$r‘‹ň cąjfV}’[ óîÁ™ŚUG@`PôZçUÖhÄć˙a‘”ˇ="†îßpTje®ă|\iÖź˛/>š ?Ç€Ű\aˇâ1ëŚ LdbSŰáK—ľ'‰>®>k]ţq&˝śĘzaŽđ”r„p`–Ć W‰*­ëXO­óüoĘ$łÄ?7aâ vסSu€’0‘ňq^śhÎ3¶B6aććőŰIţę§_uô™Á‘ v]Ľ=ór©ŽÂĐXőyvů’aă/®XđüXc]ÔY®ŻÎ, Ś)ţ¨›(˙ ŽB”¤3Ăď8$L´Í«ź~7'FX°ęÁ* D’ŕóčęą[ßűW±ŽçEˇë7t€˘gHćV˙ńĚýË“OëJ}˝ńąnf9m>I;XAjUŞťť©µgZçd„đ E§»4ÖŽH"żč¦ÚÚ1K×>˝ńˇşü̬ňrů¶!8Ć@( ŮÇ>řxďęgż©(/|íĎŞ%«´JîHQ*ŐŐeY»/ä(»ZhX/ő±ţd„‰ĄĺD‰Q9Ć5!ŠľxŮIéLmÎŰ4ůőëŐO)˙Üĺß˝I$…ęwżgŹä㋥ąÁţ(Ó;q„XłţoŃnFuPü¤a!ŢQË猡˘É,8ľúY›%aŕ`1ý>¶Ą‡fÉ»ŃDŢ(˘Ŕ•#ߍš0sŃ#~I“ď\0`˛kÝŢdaSŽ<ÓPŞŚUY÷-]}˙šUűŻč¦&ŤŔ(§P(xŽł‹Ć˛™IĽÔ*)´“FóĆúěÚ*Ż”KVOűýĹ+Ź­¶ây-˛Ź#:µká8“ µĹYÁŔĚĹ 5¦‚Ż~Ů@Á«Ú[q8§aD̤;×>ĺnvhŻóśTŮěµň˘”0t˛”°îŰD S:ö‰†ŘÇňÁΦŕŰ<Ë[ĺމű×Ű´ýµâ•ş’ôQ#>ęlQFĹ3‘1Qěj3űÖCy›  ¤=jgŰfąÎjŹÚňܢ: ńꙬbO•“\«^P@­’Sr $˝®Ţ= Şą®vĚ’ö1ĄLă¬Ń×U\LŻ€Ĺ`´*'OڧŚ0ůN¤˝Ťv0´‘ ׏oěô”ßă¦?`Ö[nŠĘĎ‹ëó6Pi6]‡~šű'ů†’Đdj.üűjĂ8˘UyMt%ÍćÜM—›‘~ń…"ƱΞJy/äŐTr_ %€$2&ČĐ,Ć*‹ŇOŔë51•îŞu  ^É>>==çł:ź^É*tyĂŔRlb„Si¬°p"[„†#u Ń«„>ć3‚?wo–ľČB8ÂÔ‰nÖVXg Ľď-îMë M cŕ8Ö[ť}TÝęsłEáŢ3bÝľq Tt‚ĘO#µjNę›N7š$§@ŔhďÜű‰ČhüŐ®Ű|—@•ąŐ]í°őbÚˇ)ŢYSk1öřfÎě·G%ČgvŤ¦ć+Ť¦•±\ĽŇiv°ź<4jÔĺü_÷^Ô ŽDü뱣;››'&+“A’ĚŚƢ–V­ŔĚđAÁ¤ů“ü"ąÎćWŽţŐÖ§.ÍMLśĺď{—)뛚-ŕýś];t˙˙cď˝ă«,˛˙ń÷ĚóÜ’›Ţ{/¤SB„^EŠŇ;‚Š˝kYuwQ×®kYŰZVTT@‘Ţ»ô„B)¤×RnĘ-ĎóĚďŹçŢä¦'pżŻßçĽ^îr'3sÎśsćś™yfÎů?°€ĚŽP;ĘŚ9zůÔ˙Ö "ŚĎż÷Éâ±ÎăĆMJąZ¦Pň ˝ŕá`š÷ńO@ŰÔˇ”Ţ|dLŮ5–Ö–»::Ż{çůÝ—ŚýC|Ęë´HoÖP·żů‡dÔÍ\8źuÁŮ3rbRH9^ˇTNš˛űčŮoß}V€ň‚šK(čëŚu×N349yěč‘kŢýE’Cť5%Â$€I”š—źŕđč[<ůÂśń!B“;ďŔ$I9K<ß¶N›V<$&±ZÁۨ I*ž ‚“îÜŐ9®J:đ]fh ů*ßľą%p{´ĺĆ §Ř% 2뚉Řú†ĹâvĆ ĹC€~AáD¬ÉÔĐű›yňݤšëy—˛µ<Ľj@”˝Â)dδńđ—÷>Y2ĆlI8ŽŠRßŰ–®Á$HF“QNiŔń &:ťvß–Ťĺľ_X8ĐzQŇą,ä~ävUMŐ‚(s‰™DI~@d™;ĽÄZ[Ý Ţ1xöÔ „‰θfĺ=ż:lůî} $A0¨ĽVXćé¨"ŮKY´WdX8€ż~řďf>sg2  .KÜ}ň· W*•Ť×-Ł€É$đjuőőĽ´lí Íł¦Ť7ßůé .žç%‰µ¶TĄfěYŐ&‚[ΰzĺŇ.¸ÁËé|6´÷h”šŻ–YË]Äě5"(×ud9łÍ—D“I4™Á(‚$ŚRŤ‚×4ŰL#ztÉJb’Riăh jžŔd2ßÓ‘-¤Ľ3©ÁÎÁÍÄ)ŘÇM "0±ŮŞË{’;O)Úú”ϲKRŘI‚QŚ+‰˘ČĚçb*ĄŇÖAŽ>ł·çak ®QŔl#:Żcöíg\ßq·IÜ5xĐ›aŃďĆŽÚÓß·`?Ús|îčäo›7Ńň=« WăF¬Ź@lmă˙ú Ď _’‰R«Ąíđ#Iľ#l¨§ÝČÔáŃŹyąŢĺ5öâ°ţo‡÷˙2vLJRčBg€ôŰŕ¨GÜžŕÂś­‹ ^ěúRĨýťýy´óňŻ~źÄ|É_.ń^°_ŐŁŹś™>JižDE €§ĆLË=Ŕ”¨„†ÇücÖ”Źł}úÓ|ťÇ‹Ë–.÷wTŮşe¬¸˙“ÄČńQĄ+—˙4aôŹS§­XúbŚ? Ř2wÁż‡Á[ĚÎĹoËÝÓVÇö{qäě%łF8¨Ú‘iUýÖťÓ7ŤŕVl⻎P“˘âöLýż-@¶ZoOťńËČ·Ž@ů ňś•Ż1Ć®ś;ţĂ÷żś8˛oŢÄţ€Ë߼ş €BĄlSçřá˝óîHšOnäg¸Ô'&5żňŘĎëÖm:YS|)Ţß˝Śq;ű1G1Š›ôËćÝ™yeey™{~ß09ÎpřboZMiöKkžÝ2ĺهf<ó!ĆصŁ?¨pJ¦Ż”KlřEOĽZŢtę×uŹ=ňäŃ3©Źß7UĆ2iÉsuőŐ;·nzdţp»1ÇΤüíÉ˙ůĹ/ueYűyH¸{Ug=«Â)Ě|ěoÚâó¶Ľ,hE›:méˇ2bnEŁ´é›ßűr˝Ř¤]ŰuîŔk€ŢÎŻIć˝đçŇ÷~ńĘÍ`'”S+9/}wV{a#€Y+˙ŮlINýq`rbä¤ĹísŰŇ!- °ůŰű_Ą¤eŐëjRţŘżdüč÷˝‘u%uíËŰv䲨+ś9,GÖ26˛ ÄşźÓG÷?/2 ŔąĎ0fZ2:x˙! ëšG±lr,;…LsyĘĎm&oŻp žamÍ~Ş;8ŰE×»âĆsa DŮ!´÷hÇŹě›7)Ŕ«~c–×὏/ś`đĽŐÍŘ»Ő7y¤żüK}eŞ Mжńű5KÝ"ĆiM¬Ůf.Öuoňv7fâ=ÚFÓľźľúňŰÝŚ±?âEŕĄďSĘS•y>ŃwT›¶|÷ńßü*Ô•Ě `Îo¶ęĽBVvžđµň)¤^|`ÎĐ€1ËőueýŐ€"¶¬A|ĺ±™vÁÇŻ”§źÚ÷ÁÚW’Ł}ae:ŕa; j8űŕkÝH€Ňj¦ŢűčÜÉ]K°·Ŕ BMJ}’Ł‚š˛_»V›kŔf,ŃWŻ©ĎŃ-ßÁŔłtW› 0…źÚy°ŁáRuć? šj%B!6t—ëőĹúš =§@CzCc±ŃX+KőeJ Żq_0A•ůr®ˇDîÖŔ8'Ţ)ŃQĄ”ň>ΫJ7ts–0ču,h•Żî¶©Jd&ĆŮs¶¶RucîyU— Ľ ×xµ^—­DCĄQwĄ^_!@dĆbË(Ŕ rR4eÖ•ď«k>ú‘;Ąąô§Ň˘mŐ  6ŃNˇŠŇm×!h0Ő¤vɱ6u^˝¦+Ś… şkz‡x•†äVP˛Ż<‘ęű‚c€í@÷đ{\r^ËmĽ.¶żbK ‘Lú?Ę´qľ>ΤéďÇNť–?ĽHby}ÝÁ˘âKŐ:YÂňÓ6“$ć^Ż:WU«¤|NuĺĹëuő&cYCăEmUqŁ^//hµEşş“Ú: ‡ĚĘŞ,]}ĄŢP^_÷KFúgW‹îŚňP°Ý_˙Qj2 S$µ*ÉÇŰ“ ďť=˝·˛ć8VR&„…FöTB\ZAN^“IŽą~{€ÂŔBÜ=“ťl~ĚĘ—ţÇv5” čçş6>ŕŐc'ršLÜ­aŽüŕI©´ÉËI;—vµNWSVVr`˙á3˙ňüÂUËî/®c”0IbÖuJJŠRĎĄ”ho:“ Ł”Huʤ ś0Ń•–®^őČń+%„RÖ»ĂÚŰ׏üŇĆŮ)ŔË)őŹýÇΦŐTWĄ^H-ŃjwďŘaPąLś4˘ôâÉď×˙ZVU+š eee»wí8—ž#TQ0”——ď޵#5=„ÔUćě9pÖođŕ㮤ްi{EU%ČN?}µ@«PŞĘłÓΦ^ŐxúŽ3Ö[Őř÷ż>±őTÁ¤—űißóąôů!›$ s®ž>{Árů«Uť¶ô\Ęa Ő—OĄe Nĺm'ľöŇsëwśa€ÉŘ®NJ:b. WÎź:s1„*•Jë Î<Ďó­Ł<ßP ĎóśüůűÖkË­ŐyÂńŚIתM/ýíąÔmërĘuÜ­»—Ő(ĺ$ĆbF,ůŕ™™Ď=´*łĽŽ»áśîĚśbÔdŇg]Ŕ ¡}űę$FCĆůSg.fBt•YNĄ=ÎĎNxaőĂ›ŽĄL­¬:ke祤®Âʧ¤ްq{Eµ®¦4ďř©3 ’ĐPŻýăČᢲjÉXsâDşČĄŠK;¦X«#Äl^:ŕa{ ^ΑTJ͵.ę}µžqYçR2ňK:LĺtÚBr-ččgĎźvX“ ŽŁÜmmŤ%;k€¶l–şi+%“Ýš®ÔęrŤÝĐÜ“Ţz\“p„‰Ěmˇ_č¶g–gJ=äX)ĽyŽÉIBŁíě}ůŠ˝5¬g8:FhŽX)'ĐěŽäŽ|ŚüN:) ,\ŃřCN “7¤˝ňE„N .Đ–]¬i ·cQŕ™tgL˙çC]'l9`ěĂIÖ`NęćŢßAő[nŃ­řj+ßŐiňE–ÂiËŁŠ˙óă^jI˙}ëŢő´ÎévĂ’řSű!rŠł^běhZ9ËŤĐ:˝·5¦[wOŞu‚?Bé«ÄöňC&I “D»čÉű7~ęB›LĚ| @9 ©UBôŢ–0F6ŇőgŤÝsĄ‘٤Íű×V:O¨uČ—Ű=˙{®cT9gÁükçĄ\.Ľ™T˝ŁŽR&I‘‡&»¬ß°ăf’Bĺ3`In*›çm‚.„ѵ,Ú6ěiť%ˇ'´cóÁQ*ö‰,şTqY❍‚ĘQ"çńíúŽqXµ?ĘnSĐ7nbžvşj$…(ްRöÖfĘ)nÎ|t˙gOŤ ‰¸Đ Ę=J Ŕ$‰5[KÖZĹz6řÜJGrÁr‰‚'L` »ŮÂw˘CV©î[„ŐnµÝR­äeÖÁu,˙nĆŐÍ*ŔŠç„ÂA2YŃL µ”´ężM[JHÂşŁ„0‰9Môđfsůůň$\8FÍ:GHۧ°„#Lb}ϱ.Ő˛y˛5wLáaډť(=%„#D°\jn3‰(!”@´šh”ćgŔ­…@8‰1ůľ…ŘŐÉJŻŁ…ö ČĚľĚ×ńŃŁg…˙á)Ţ÷„Â"¶ZňţfbmmdM#„ň÷¤NG3ĺÖ4ę˘Đ„¨ÍqĂhb<'»ÚůÉo.ÚŚ¨˝ŇmĽ›ŰpGĘŕyďŮžv~<,/@ťÂ.ĘÎ>ĆÎ.ÔF®C,aj÷In.‰ö}Č1ůŻN“ĽżŤRó赥ŚR.É×wĽź·ŞÝeqů·ŁŤf€‡[Ľ›kĽ›ëwWµrŽ„yxÎ Šv“ŤŞDö‹qPŕ)%€B©Žqsŕîíě ”y¶Ď#~.Î~ů á)•C®u,dBđv.żĎşk†Ż3noę{»ç}ý‚ţ”C]|m99$rß]üĺ6}:xjŻ~ŃáV*O#“F-Z8/>$ĺy^ÎĂGh`|| ·+€ŕŘ„ŮsçÝ1a¸JŃ—<#U¨FN¸sĘäqjĺŤ÷|»ú!*çţ 8(!qĐ@g•B–PPÄŔ…K–ŽŇŹëqKB(ĎË1c8śÂ㡧ž{éĹçß=ľYň„RËU«^Ś‹PĘq•oeu×PžÎľâúÉÉÓbúx;7˙馀đýbú'&4hĐ A‡ ˝ő/[ ­u„Ć$Ě™;r_ësŹČé®ĘRáß˙Čąó ĆÇâ–<5éä«˙cć=rAJŕ†^A™5ĘÓođ¤„A'Nđssp­¬ Eűs ow¤ AÝiBg˛čÄŠÎďbr‘_d\\ ?˘p»wĺýCÝ( (=ú'$&&&öŹé§Rp”r7, yĎčŕ'—(ť|§ÍZp÷ťăä‘’öܰPíeŐJĺ>ię¬9Ó'{¨•čĐ®Ŕ¦KźĘĹtáAÂwĂŤžŤšđ ť5oţc‡)yU8Äö4xđ Á‰Iń1ňšHžwŢŃ#!wëî$Żv‚bâü<śíť=ÄG޸Í$„ă(ĺ”VrĐŕÄÄńý ”W(’˘ŘŰqL óťž#O$ ű}ŔČ#Cţ3H @m›|č¦9&˙S­NÜ98l–s‡ü”%öüđńWďYp~ńĽďF$ŘY5…%îÝŃ//[xfţ¬ăófĺŢ»čą`+‡ »zď’ł ç^;5’8çÔĄ‹v ä(€‘áQ—–->3VÚŇ…{¦ŽŤ´U²!qrô>µdÁň w|włZn˛(qdꬉšÖäÝçöÔŘ'fŚ—wĄ˙SďjŮÄ;ě]¸ŕĺHôŮ–Ź śçS˙0«L›źv$ÚVŢĘâ±W?©©oĚ+(*É8ŃßV@^d»DŚż¦­ąoR”ÇĐŮ%Őő×rrŞjęýüľ×'\“ Łíkźn©ľ®-.-Ţňéö7ŇńíëG~ć1zţŐŠ‚ĽĽüÂĘÂěĹăŁĚy|m‰¶6ëĘU]Ťö‰ĺwŕ»ÍoĺO䕯ö}ő“/2‹kŻţÁ˛†ąşi~őűĚůWČy˛w_ÎűrÍ˝č:Ov÷ ĄxMäŢ”ěҢüĽ‚ÂÂüüÜü’śóűcÔU´~'Ó'Đšamu>ÖŢ@ô÷TÖőµ>÷Ś%=ÓU94ęý˙\WşË– W+Ą›&‘ šđÔüŞ·¸ –Ä˝ާć?ňNeíők9ŮW3ŻÖ×׼úŘ|Oľńyłµ`§đÄk˙ncnA4‰ׄV˛°|ʬh›QcŤÄŚI/k:óۧ@4ňµuOĚ@ĄT°ô±ňšúĽ¬«e•Ugüŕ€łŤč­,ZáÚü)ěă÷ś˝ZVWZĄÝűŐ»Nxݧ˙ń‘y%Žă­)äÎ'ú÷?.U”—gśŘăoßz\Ýű”ŇĚÓý€ ń‹‹´ťňYV9N3 ݞKnř;u­2ýă=t˝ľ)73łŞşîŕúyĘ~w¦Täĺ•W]<ň‹­€íë˝ń˛…Ľwő—ůWöŮňg»?+˙ÝűĚ]ţfaöa›µ™ťZČs{Łl•Öíéüo]Ż ĺ¨ÝGîÝ»wlM{~¶çáŢón-ŻžÔé@74JÜŠ©Í…ş$¬‹‹{»_ň–x;[ Ŕf ëă‰wÚ°íg«Pµž‡ŕyOČč#m=©M´ë¨SI>#5€zČ–AľŁl¸Mńp‰QąL÷±{€˝š(í“˙HŠXć€* €€ç"‡˙Ą  ô¶ń]ä¸ÜÇ-Éľ#‘ŞÁ[‡Í3G$ăÂş¤9ŇŽ§ŕ˝l\†Ř°Iôs&Éw¤ü©ˇU·D­Iü­{š¸Îńü©9Ö;ŽÉá«4±>Ó]9Ŕn„ĎčÓCĽ‡ŞűŠcň |ź ú}żöÚ!kL_xŢŠĄ =ě'őPóвĹţ.h·2V*UáÎÎľő”¨¬e †ŰqÎnWî]üX€HW7' pN{çÎą'ŔŔä°đ1îvă""Ó͌㉛ŁgęňĄoćÁ˛áy(yü…Ů“ä/\vŽËccë3Éף#!ËG%v;çĎ%:·%úP«Y@€‰Qq;§Śúߌ&ďU—'Ť>9cśśĂäć)”Í™}ěŚă'Ďlްą +µż˝@đů•şÚç%ŕŞV?Ő<óŃÖŇ‹{ťÇ€¨Iă†ű°Ţh\5u,Îţ†A>Ž|Łd¸7)lú}gLZ1Ą×=ßţ~”j•“wŔ?8öÇ/oy»r.á“JęL˙}űq‰Ă§$'FŁ[­&€Ę-řľG׼ňâł 1Á<đäű;Ó÷Ë Ď€¨‡źXóÜłĎLź,·ěFĐ„ŚŚ›rç„ĉwżřÂsÓĆ&vÝĐěł×|]pőĽ«9™˙őł+ps» AĽ_h¨—_đÚďŹ\9´>ĐŰ/$Ôçöä©jŻóń¶JîAŃűZź{=Ô1óMeŕ©ěňwž‡ÚWÜ ČÓ˙ˇ·~Ę>ő»ŮBöV^ň…'—Čظo÷Y®®­®ëŻrIy˝ˇN¶6q Iv€ß°ąííOßďinTÚË€CϬ(iC üřׯ¦¦c›?W°‰O˝’÷ŕôAî^|ߤĠÉË©(ʤá="FÖé˙˝fąÜ¸—˛h…ëÄÖ/,|ćóĆšśX›°„ą:˝qĹtúLŠ™ž’ćVÇ·ţŔ¨{Ö«ÇřŮđžIÚă‹M‡ŐyMO|JěŔDGNţýşŕłyWc›Ňnpť[$B)€¨!#X6Ď0ĺ™CÓ˝c‚ŕ3!űZÎěäp{W/?77á˝ô-2sźšPČ*xďţ…óî}·(÷h/l¦lö˝ÂfÍšáÄDq×ěYQa~îţ>!Í24Ě€[@ÔŹŻ~öɇ˘=ĺÖÖV}úř$‘ńS¦L4—Ś@ĺbö)±!4ľáSîš6áÎYĎżřÜ Ń"˘yůŁ˘Â˘·źy`ÉŇĹýü]­µŞ=ë›ęWLŽk%ŻŐi7ă4}`AH5)KÓŠŽÖĄůޱSśX©§ÁNqďFŘş“Á ÜŚšPŘ+ô9şĆr©)«ˇ©V˛´8HÉëţ ř©x0AÉ9ŽuqčŔt¦¦2#ÉČ—ť÷$űŇďKM ÄAőv?˙ÎÎ#]BńĺTÖZDÎŰ*Ő’îJť\ ĺwIł  BYSuŞŢ6Dc©2UŤ5&K‡­5ŠŘÍr«ć‘ßKŽIĐx©®dűu0jőb“ůĆ$ˇÝcď–cŚş çfc§iýaÍňďˇ.5u•;ëÉÂ~!Ť&1ÚŃ# Vü0 9ŐŐĹŤúA>AĄÚ‚?ęʼn>>ş†ZĄ“ß×Ç„Ű ˇ<^ć HZ7:É“(S$Śňň0ôyő Ś˘ÄŮą/őü9-˝ŠSŮż?~ěýüGú=78ÖŽk'ů ź“«/]Ô–Ýg îk0ßşť×Ţz˛JĄU–k4ŽQňm›ďS4]Ý•<4ńŁí‡*5 ;t”®äŞ*jĘŻ›6DyŇ*˝PN0ôů#ż~çÝ@Wś±˙hVLtÜŔAÚ˛ÂĘ*-zt‹¸+ D02>ą:ëřď…Ü}+I’ľÄéÍnä¶÷# 5ĄŐő‚±±&Ż´Jd{Çh›Ćkżţo)Çw?s€$±öůSäB(㽣7lݶfů]‰c¦oŢüë„ř@“Ŕpj'o‰H‚żŢ°é©űî?uî«kźs°‘ßď’Îz†ü†1iĂÖ=?}ţćĽE÷˝ńÖKö*vĐĘRfő¨@’Z.zw«‹’`BY^NYѵę#“ôEĄEą9e·'4H{ť—$€6ďňľÖçž@OuLţ •ŕĄ1¦ś; ·č2z' p>ĺ­OdĽťÂâ=z 5ŐYéiĄ•CĆĎ.>÷ŰÁBĂô1k‹3ekăEę ĂÇ´±?¸!o\Úɂ٤±VTEóĽŕx"1ÜőŕÚ‰!Šo×±w°#!„ç oj\ö‡›żýЇ§“•ý™wO1ÂPS‘]P SŃ+Y´ĂĄŕěä™鏌¦촔쪺ˇqÉ(Ř3$1ÁĚ 9;žÄš[ŮŮkT–—U\Ż îŃP]^\\bÍźžř”HÔŠ¨)Ě<ŘžĎměˇ=áFóTŽyqúčÖmjJËrëM@Ő¸<ţü«o¬~8ČÓŔčţĂoŔ_H‚Iiçqϲŋ—ßăé 6 "¬$YŰĚÎ@ľR>dŇ矼Č|čG_ţwÖ¤A•…%ąÍ2'»÷ŤŮ°cç+Ź/Y°ęĹÝ;6 uĐÔÍVý­·_ťvăşMČĽCek#­ß¶ç®!!ZmD‚µýc·čŃ hB‡˛č‰•GaŐC©(Hv^[łŕĺ'ž8{­Ń†3™J©®A˙Ŕ_?Z»rň˘ŃĂż=‘«R©ĘqĹę_[»ÚSŮxFvÍ˝‘E{\jNP\r%$qę”áî\°4ĚŰŮ`0ż/—G!-qLhiE q`ĎąüúŹ·ď?¸ó SQÚž])Ú<Ůď–˦Ć7Ą-ź%ÉÚnPJtőÝsŁŰ3PĘ)T<Ţy÷M}Ö­‡ňT6ôbĘ©ôÜň¤«věÝdŻn4Őµu˝ő’hҸ<ńô_ţňÔ.“(Bš?sô|ŽPÁÔPŻ ZŻk4Íą‰e )?Qą{Ćňoabň€AIăÝcîť9€Q[¬ú‚'ٱ®bőÜéýŁ#’§¬š2ăľÉ >ëżlń)Іˇ±öĄűď4pfç9vržźß>ăY__9qpň„m'Š´‰iÍæě[÷¤ŰČ«uÚ͸>†3aĺ+3RAĹŞ¦Ěżdgľś}=OtîçČĎ˝‰9xPąłĽřHCÔż˘B: ŐQ€1žÚőł'ˇ±Úů®Z˝1ăţ´“.T^#ˇ÷¸€“Ć˙.—ŠźËô1OWň[­÷áĂ÷ »×“vrü$4H’IJUaW4GŘhH­JYpáüÓyĘ~ŽŢěe’äQXş]ŇĽÔ|­ý‡…žsĚ"KĆDöŹp·`–ö×k&€’>âyE&2±Ž™ÇŐ$Iôtň\îçřĎ3©µŕT楱~ľ/M ˛uż”ŕ)Ő7Ő®ŮěŮĂGŹÔFşy&‘ăŁ]Ý9Ń­o@)•ŚőŹoÝ–řăćí5ôĄř0P;­ŚŘš~ąHGŃX[ţ]fÉüAŁ..žý·ţQ*K0ÇQjŚ˘ŔâöÜDů8 Q’Ś } MÖgIjţć¬TŮčË3\°lŐňĄűŻÔL4őŚ}jŮŚŤź|P BN™uĺČ/ĂÄN™ű¤÷Ŕq÷Î0Jű`ĺ!›|"G=<9ęŻĎżXCTęÍĎu;ű‘U–±+HMJµ5ĎLŃ&UŽ`ŐR.á$đڍÓj‡/XůâęG«rŇ3JJäۆŕüM–P4fűü«˝Ë^ţ©´$ďťż<˘–Ě1Đ:鹥D©TWgěľ  řZžQ`]ĐcţÉ%JʉŁr BÝáę ¤5X" Y4ů6Çő˛Ňy0zËôą'Đ­ŽÉűĚjťA$…ęO hBp<M¦úꎽLO€`ĘĽ9CîŹŰöPđ*‹µYrřjő¸#lš*3[ěĎŔ‘Xoľ˛örX˝Ó„ŽeŃ­•GA9…BÁsG€ĺŹ˙#ÂYŻöŹ;(ĐĆ#|ᔼÔ$)ěâăő×3µĺ8^)Ő.=$Č7jÓĹ›Ď,“ö\íq©\Ă–LüűŹź|¶íŇw[÷˝őÄäë••DaN“(Ź‚ăÚ¶˛őî7mDěÔ…÷ µąoö´©3’ŔA-›KčáąÚ!7ĆĹqtc>sßĘn0‘)m»ĺ†ŘÝ¶Ž‰&Ł yű›_¦G ł–¬ŇBîŮwMř‰Ç‡ŤśYŁ ś<)YW]ĺ=¦·ţ‚WÚÔ¦ź0ôl~µFĹ3‘1Qlk3»×C€€2ć@ –cg Ô®Ú’¬ü* öÚ™Ś7•˝L@‹UĎÍ VÉ%Y’®ćş‹ox}•vřüfźR¬qĐčŞJ/¦–ÂÔ¨7‰*{7ڧŚ0ůžbG_Ů[ńpöŇGĘ%PJ¬ýE×u:)·â¦żŐD™d2źG ,.†IL^—«ăś|ő´ŹůK°ÎőlĆ“S/®ÍgŚč« á©”˝6»˛Ś‹5’Č '@3éËMJo÷;<ŐÄP´« fG&äľ™y|RęŐo«<űş'Ş`ýúf™š Śp*Ť5ő]Đlžä"„ę#U&ŃÚPÇ·DZ®höěȰ^rĚŇ.đ™`ĎÁüą2tyćGÝ`ď!ÇD@㣆(4Ö´[ď2(76ą¨m6^L9T-Ĺ8h´&€ßź¦Oűph¬|7Wb D5;Ä?5/?K/¨Ś"3ŹĚÄĚłSC¤ż;şł^±nL˘0H$ÉČ Ďohň´ł0%¤_©˙:'_¦™Ŕřćń ?n|áBţڏ¸©>¶čč2ĺőşzxo§Ý˙?°€ĚŽP;ĘŚ9z =ĘÖ 0 ’Ńd4JPŐT-˛Ü™I”ä+őł<Ló>ţi#I9^ÁQ§Óî۲±ÜŔ÷ ÚLÝ^|Ë®´¶ÜŐŃyÝ;ĎďľděâS^§@zł†şýýČ?$ŁŢŘh^­źĎşŕě91) L¤ŻP*'MŮ}ôě·ď>«@yAÍ%ôu Ćşk 'Ťšś1wĽ·żßÔž61îZEžŐ(L&QPsýZs+…SĐôÉw„ű‡tyżî;qz˙ö+׍CHicÜ0&¶đ9˛_$€ ˇÍvC 1bËVŻ\Ú7xY"ťëśLňů÷>Y<ÖyܸI)WË(% Lľ4ĹL˘$㌥5ĺN¶=÷f›/‰&“h2‚QI2ĄŻi¶™Fôč’•Ä$ĄŇĆŃŐ<Édľ§#[Hyí%Rť›S°Ź›@E™‰ÍV]Ţ“´ŘyJŃÖ§Śxţ“]’ÂNŚ ˇ’(ŠĚ|.¦R*m4…醇<ĎKłŘů5 mDçuĚľ ýŚë[ !ÁkBĂ–z*\TQGůŽuĐnŻ0PUäkˇa/„şóŐWtx'ĄËpg§hŤů˘¶ťmüGQÁ÷xEżAŞę+ę äNJŇĐ”ýJŤńzĚ‹2¦ôׄ­ ď˙elŕdŰŠÝUçżŔUű{ic-OŔŔ…9Z8×Í!D%Ő †2±E_,3’’Vw=Óč1ĚcÝĐ즨3pč†Řý⿲s”jRëđŽ­FBÎ]ŇĽżş=×{Ë1ʀǜ€Ç}Ä:ÉE`Â×ŃC4LĎ”n}Ä1—ńÎ 'Ş F9OŽŐl.ĘżT×8Č;ä㱣<ÄúŤ%Tš±ţţCÜś‰E#ăB'şŮlĘÍ‘µo~^)ě>;ü•áĂÇ:«ŹjË…»Ú†3ÖľrřĘ=üÄH%$O—żŹýăÔ©O…şnÎÉ«b‚wefdŔQ*¶.~[îžöXTHg[“Q_Z/Ŕrâe2Ł€±±âĐőĆi~ţ@Ď,Á˙ź@öLŁý3ŠŠJEôŃEs@$Šż˝˙Ő›Ď<ěěříÎ]ϵůűoJąoľ˙ęť39ÚuĎ©˝€ë“«ćlţć_UPŞŚaéŁď_N;µöĺżm;°w€‹ţĐ‘CŔÍŢő—SŔďŮ˝é|v訅ëľ˙ŃSź˙ݖݰ\ŰýěG~é=é—Í»wÇý{~ß09Îďü–őßË˙č—]/­yv×Ń“OÝ?€«wŔ¸¤řá qr4MNîľă’â‡'ÄĘżsÓ\FířeÝcŹúŘ™”çW-`Ş­()Şŕčć×IĎqÍń U;7óĘ‚ˇmťÖô Žź=ĄđúĺWľ÷ĺ7#ümĄŕěćÓ®±·ŹËHŔÎŃŐÝĹŃRČnt­ó?îß»düčŮ÷¬˝růlßęsę*cŚK˙Ř}ˇxΤ鄞÷ &7qö´´=[‹ ĺnŕŃ‘ü„`ĐĚe“ű{ţřË(°cëĆbĘÖfb?çSY§ěÝQ*ů›íO”ËsGľ˙šwšĐ,$ÖŤŤr9pîó܉e űáŤ'†Ź5"yčúÓµ\ŮsyŰ@Ľ<˝iÝĺgî[¦NXđÍ돨ŕ÷î×ďGđÍÎ=ŹÎťüÝkŹŽ;VnEKŹŻřë»Ç/_p ýß/˙őÎçßŢíqđl*j• Ýř”o'ÇąýşoĎĚďd\8ŃĚçĂGptµŘK®.ŁSnlÚ&”ëôÇsŚaÎĘ×^jy]uĂ3/ľwúŹýEß}ďëégĽ˛ć•ýűqhĚ?zâÜŮc;/UőÜ_ČÓĎÖÎÉÓĂ• ÔŮÍÓŰÍîة㠿Ä›y¶GÚËLFĄ{Đ?>ţęË˙Ľáç1펻˝Ř;ąş»8ĘGeű¶oŃ; řěëŹ?řúł$wé÷íű¨U+«Î`eçeś;Zů”SĚZ×$úx{ňŔą{xÚ©šňΕÖŰĽôŢúÖľ’é«Ř6íyxüđŢ9“¸y™=š‚4÷Á®ęP`"mŃŤu»w?:w˛Ä˝Ń›H\ŽM€ÚXÜXőG­Q'ęłň«Sě’ŐŽ$ďŁüňC:ĚÄŚ%úŞă5ő9zL` ?µó`GĂĄęĚ4ŐJ„Bl0é.×ë‹ő5zN†ô†ĆbٱV0–ęË6”ţ^ăľ (`‚*óĺ\CĚO+ ŚsâťUJ)ď㼪tCif)^Ç‚VůęŽV6UI ¤ šó?ĘŻ8ŐÎŽłŤ°•ŞŻ˝źW™ŇÔ~¤ĆNi.ý©´h[µĽI°‰vp U”n»ÎzĎ1]Žĺicv˝.[/6† ŁîJA+HzA—~Ólş‡ßă’óZnău‘´c %D2é˙(ÓĆůú8“¦ż;uş¦€ ‰ĺőu‹Š/Uëä9ę­VçV•˙z­ÔPBDcă±ŇŞ(_ź5}ďĚŮ U„ŇFSÓEmU‘®î¤¶NĂ!ł˛*KW_©7”××ý’‘ţŮŐ˘;c†<l÷—Ă”šĚÂÉF­Jňńö¤Â{gOď­¬—Ż/·’2! (4˛§âŇ rňšLôĽí (! ,ÄÝ3ŮÉćǬ|é,%Dúy…®Ťxő؉ś&ׇĚ!$(<\››¶}×ţ˛šęÜ´´Ë™IŤ?!ŘYńúËkľÝrzÖŁď<='čˇĺ+Šë$Ćššš4NŽq+Îżô쓿ÉD¤çc”©®âŕ‘”&şŇŇŐ«9~Ą„PÚË4|·Ż9©ŤłS€—SęűŹťM«©®J˝Z˘Őîޱàr™8iDéœ߯˙µ¬ŞV4ĘĘĘvďÚq.=ÇüĹW0”——ď޵#5=„ÔUćě9pÖođŕ㮤ްi{EU%ČN?}µ@«PŞĘłÓΦ^ŐxúŽ3Ö[Őř÷ż>±őTÁ¤—űißóąô‰ŚI‚©0çęéł,—żZŐiKĎĄR]půTZÖŕäQŢvâk/=·~ÇŚ]áꤤ#ć‚Épĺü©33A¨R©´ęlIČs“%<ĎsňçďNuľ¶&ű\ĘůĚl{·ŘţýűTźűRW Ç3&]«6˝ô·çR·­Ë)×qô6Uˇ”“‹±ägf>÷ĐŞĚň:î†rşËőuóČąx䇍 „±¶řŕ‘”Ř CśůµĎ?óĂŽ¦/ß謹ä…Őßm= Ü2YÜ&´’Ĺöu9ĺő”ăĂÂ:±˘üÚV·ĺx»ąCäś 0™ôW/ž=}á Ŕšt)))ůą™GĎ_łU°ô3©yUÚŇ‚¬Ż?zçÓď· ‹ą¨×˛`Ě×™ WD“Á=$|ěńE§¶=ôŘę,m#á­FQ[“u.%#żD~a2é3ĎźIąpĄčĘŮ”+yC†Ťđ±çŢüŰsź˙˛Wi›¶+źÂżúÂęź÷ťe˘IăčŰěAgŔĘŽĄZ,IC}ejçÜřä»-b—é2 Ŕ”J›Ľś´siWët5%%E©©gł +}úE Yqîŕ3Ź?u&»MUźí•ż` ‚É›yţäé XC˝îÂŮ3©çOśJËÔu–Ć>śd}2·ĂÜÜű;¨~Ë-şĺČVŁ—Ą0bÚň(‡â˙ü¸—RŠŽŔűa­»şńŽ˙Ô~!˝N‰Ţy˛đ.,T>±j ”Xż“&„ľJl/_Äg’Ę1I´‹žĽă§.´ÉĬâ¶1HVgĄ˝/aŚ(l¤ëλçJ#G‰ŘCyBéŐg˝›…žëUÎY0˙ÚůC)— [KçVRG)“¤ČÁC‚]ÖoŘ!őRç,ąŰ›“ÓŚŽBkŻômKzŚ®-.9±:Ľ@)µmęóşFײh۰ăQt–„žĐŽÍG©xٞ „r”H˘ůV_[r;±=Ö­Ú٧č»ĺF—†µ3nô•Ű! }ă/zk3)ÇK˘0pćŁű?{jlHÄ…QîP*«)k¶–¬µŠőŚDŇÓÓ™[éHn#´»Ŕ0t슉ĺŻíŽ˝:ţşBZפ„4/„ĚOk-ÝZôµ›U@Ď·%–đ„‚ m(ëtťŃ BÄś&zřłąüŹ|ÖĚ( fbň{§Ž•¶ .+ GŔZ©zßp¬Kń”Šň ±Ě:BGcL´(=O©Ä¤ş~J%­&•˝…e¸h=b‰19®ŠŘŐÉ é6â­™'ĂB—ů:>zô¬đ?<Ĺűž0B• ^0™$Ćxžg’$¶Z&r°ř9l•¬””ă9“ Ü‚J‰$J(GY˙sýĘQ&µRvB(Çs$Ëc’J(ĺ(IÇ3&6ĎFŽç)!’(ŠŚń”Š’(;;y±Ń“ž ĄĄ‚•ČzŇ í^=ÂŐQ?ť±Śńv~3§ŹSs-űB9ÉÚÝH ˇÓÜţ[aŤĐf Ŕń<µD’•DQĽ˝’Ű3ˇ‡:Fí«ŤĺŤRÚ›Ď&„p„tĘŰŢźző!ťŻ)Űaçxafëg±r“¤Î(l%‹¬(ă8N…6M:ś_˘ O DˇeC)+őî­,škJ “$P%ĎM¦ć?Ę+iyOČ+DAdÍ󦥕‚7Ť ŕ8NĹ®)ą*"Ą!̲9ęxMŢ…Ýč–]Ąś}NĆ+ ˘…Q%Ęq„1Q’úĚ_X ô(6Ľ|dŕ5úůű&żůŇ‹ĹzĆsT”Cę·k,»cPJĚ–ż˝UoWŇʧH˘ÂsTÂóś$YĽ !<Ç@Ĺö·>ăćx(ŠRyő¤n±í5´Q‡hěCÔbť±&Ą^á9Mšň!LolČ5v ™v­ŔóŢw»ęNUŐ „'L`ÄNaëŻ"L/6ćë%µ¬Ý Q‡Ů Ú¨× „s؉u¶=ńŇ üWXń{Ůĺ§î*µ§–Ý‚±Ô`¨¨Rá”dĎS©ćD­ŃČä9ىs°÷S4¤éꋌ¬9Ţ{F74›?†Ä8¸D(Š«˛î@¬áőBC©fÂHÚxdď ˝QVoŻ3Í`J'ßIăFrM»ö’ąŃy+‡śŚ«B;Âó M2¸ˇ˛ŕŕQ3ĺŃűEĹ97Ö¤ĺRĄcLtR&#ĽŰňűşâ†ÁÔg/p{ë/äaş{x»Đ´ô<+TŐš”ˇţÎ/^ąA›IG e± 'ę.]Ę41Ęóň6ĚĽéĐ;´-iăS‘݇üEZ”$B)ĺx*1‘Iň1fë+—k5sÍ3Ą·unh†ş%2lëŔ‘G‡ |;H @§¤ť“w ľ?qČWáo!YJh†ą'´´z+H @m›|h˙;TA8OóqtȰ-FLüE?[/ňˇ= t¶°ď8»ć’®¤LŔűń°äź˘ä ­žóGK¶uŕĐߌ>9$xŽ ”ĘřuF씼gpŇw‘¶^żÇ‚G’Ľ+ařݱv6­S|Č:Űa»§€ű"˙ˇßGšĂ!S@ę´?iđűÁ ANCţ,ŽÉ˙T«w›ĺÜ? ±Y™tbÉ‚”Ů“#x ëřwŐ‰RrfF'^^¶đĚüYÇçÍĘ˝wńš(ŔîÜ=Kv ä(€‘áQ—–->3VÚŇ…{¦ŽŤ´UÂ’ËŇÉŃűÔ’ËÜđÝ=T‘›,J™:k˘¦5˙nČ“|jěŔ3Ć+ŃJSţ@~[Mx‡˝ ĽéŹ>Ë*ç ń|ęďf•ióÓŽÄÚ۾㞂ʺk99U5u‡~~߇“YÄp‰ź[®}dö0OýółšúĆĽ‚˘’Śýmű$I°|ÂeűÚ§[ŞŻk‹K‹·|ú†˝\ţżÚŹüĚ1bôü«……yyů…•…Ů‹ÇGóřÚmmÖ•«şíËď„Uînvü«•Ôn5Č™ł—?óEţ•ržěÝ—óľ\s/şÎ“Ý=C)^ą7%»´(?Ż °0??7ż$çüţ5@­ßÉô t-u9ÄüGŢ©¬˝~-'űjćŐúúšµŹÎŔu+Ż>€žęŞőţ®+HÝeŰYüŐ[E"@4á©ůUo?pWĎ8Ó˝m9ĽáCO@ŮďÎô‚Ę‚ĽÜ˘ňŞ´ŁU€*âÎK–’‹‡ÖtGŻ \Ѷć,Ź˝ú‰lŁJ3O÷‚Ć/.Ňš±Ůř‘+6ńžBKÉŃM9Ł>TÓĐ”›™YU]wŕ‡÷Ľą°·’Ęw…˝3 )Çń‹“^Ötć·O9€hĺkëž=€J©0aécĺ5őyYWË*«Îř56ŔgŃsYČâSřDnţ#­˘´  ¸<ăäÎ? ŕöĚÚŹdšcěÔś‚âvźąZVWZĄÝűő{Žő‰úýŹKťµŠÖ(Ű Šăxă=t˝ľ5 ß2ŇÍźQ@ÝoĘĄüNĄ,ÓĚiäUvÉ 'üI~G¶÷®ţ2˙Ę>[ŕl÷gĺż{ß‚ąËß,Ě>lsŁ6łS yno”m+n÷tţwçSd%T»ŹÜ»wďŘ›öü”Ă÷ENZÚ~UĐ\±'un(Ľm\†Ř°Iô“2ÔgîŽC·tďŻć*ľ}ô —DK«łC}’mő-|GŮp›âáŁr™î5b÷{5QÚ'˙‘±ĚUĎE˙9J!2÷¶ń]ä¸ÜÇ-Éľ#‘ČTŞo6ĎU.ˇöĽ&LŁvW¸Ďńut°Ł±źä5ćT˘“'%®v#O îŔ…8Ť<5$đN{¶ýl*Ňľ[˘Ö$ţÖ=Í\çřţ4ÜĽ«!HÄ»1cN%%|Bä9cśśĂäć)”Í™}ěŚă'Ďlްą +5ŢV Ŕ=(z⸡BÇ>¬7WM@Ąä<óŃÖ˛´ýö€Oň|m}Ýs‹’Ä HpU«nž$Ů2FLľżQ2Ü›6ýľż3&­2–eÄ˙l?JµĘÉ;ŕź?űă—7‚Ľ]9—đI%u¦˙ľý8€ÄáS’ŁŃ­V@ĺ|ߣk^yńŮ„`ą802nĘť'Ţýâ ĎM?€g`ôĂO>űÜłĎLź,·ěFĐ„¶égÚŘÄ®š}öšŻ ®’w52óż~vnnWc!÷ őň ^űý‘+‡Özű…„úüiyިś\"căĽÝg=¸şşŞlr¤îÖ[ˇę,A(Oe—żóđ<ܦW ČáÝzë§ěSż›-d׺ÜŰbÄăCá3!űZîěäp{W/_W7ę~ÓŻdgÉ%>ÎÎÝďźÚáęoŻwGB4€čÄG^ţ¨¨°čígX˛tq?Wk­’˙áŐ~UĐĚźžÔą=@J›ŞSő¶!»H•Ik4ŐšŔ¨yżűýC—y©ť99ęš˙—€©¤©úśĄU•ÁTc(DäuPü?‚T<Ŕ äÇş8t`:SS™€ddÄËÎ{’}é÷Ą&â Žz»ź˙ gün2d€IDATç‘.!Źřr*+DV"ç‚m•jIwĄN.tBSNŁľŇä0ÄEůz­–Iő˘±FÔřŘŘú«%ť©±Đč<ĐQ¬h˘ÁNqďFŘş“µEs÷\÷4Ë­Ěë)a nsü]üHé®:ÎÖG6ÇäD2ş çfc§iýIĘr±ł©¶xÚďŰľ*,S´ŽIäď†íęČ_´ŤFCNuuqŁ~O`‰¶ŕp­‰đô˘0o@ŇşŃIž„D ś")(`”—‡Ń Ď«o`%ÎÎ}q¨çĎiéU śĘţýńcěç?Ň?čąÁ±v\;9Đ8ązđŇEm9Đ}¶ŕľ"˙×Gß@údőM«,×hŁ}±«a’ éę®äˇ‰m?¬P©%I Í»|ŕhVLtÜŔAÚ˛ÂĘ*-ŁQä†>0äßWÜ9rL]q¦*jĘŻ›6DyŇ*˝áćMS"ź\ťuü÷Bîľ‹$Iß?b©7™Cn?’ŃPSZP]/kňJ«D†±wڶiĽöńë˙r|çń3—Hkź?E.!„‚1Ţ;zĂÖmk–ß•8fúćÍżŢ10@˙Q“7lÝóÓçoÎ[ĽâźŻ­±wúň§ŤOß7}üÔąŻ®}ÎÁF~|M:ëňz nÄ$s?‹î{ă­—ěU07ě •ĄŚµ\E$f,¤3\]”´ĘňrĘŠ®U×™¤/*-ĘÍ)»mˇAÚ†šę¬ô´‚ŇĘ!ăg¦lŢ{Ą =}bxĐS““ŁĽ4Ć”sGŰuÚ$pΧłő‰Ś·SŚtizl[*ÁSŞq~üůWßXýp »=Ž#śĆĹ\âa/v·loŹKN 0vč(]ÉUŮFEz VDMaćA öĘŇ‚jťNşž×LOEIľ®©±,#ĺËď66Ąeą Ť¦đµ“GIc—Ř­-dóĽŕx"1ÜőŕÚ‰!Šo×±w°#!„ç oj\ö‡›żýЇ§“•ý™wO1ÂPS‘]P SŃ Y0 ˛¬¬˘JÜ/2,˘ˇŞ´L«CÉţÄÁ>Ú~XˇR1Iŕě號ţGFaSvZJNUý°ču…eŐ ť´RK¬í09ĆFĆéŁ˙Y·©™‡GĚ|¨ÍHµí@Ę­ěˇ=áĆM^˝! &ĄťÇ=Ë/^~ʧÚ(°z¬em3;ĹN9áC&}ţÉű<Ŕ‡~ôĺgMTYXPRŰl!s˛‹xß ;vľňř’«^Ü˝cóŔPýGÝŃlŐßzűŃÉ7nŮýÓçoÎ[˛âőWW;Eţ´yËłËďJ3}˖߇;¸G ]żqëgďľwUçö®ĺxóŁ‘ÁN1/Úú+ëOhµ™" D}F˝±Čä0ŮÓsŠËąűŇëKZ?µ”[%8Ĺü5ĐÖ_Y\[uY 6*ˇAňY¤öQĄŻ¸T~ÁŕDˇä|–ůr.J'č Ěy…çЦíTţÖ>3ďĺűć8ŚzalÉ($ýĄs§K;‘˛ů8†]}÷ÜčŁ3Đń’hҸ<ńô_DĆşhL˘D8ŇĽ ďů)L ő:‘ őşFŃś‡P¶<…(áîËĽ…aQŇ ý2sÎÜ;sęąwÖE±ŮŞF„0 Fc]ĹęąÓKɱwrşkůs“|ŢxâőÔJĽýĹ×kVĚ]ý{…ĐXűŇýw®?ď•“»wěä„­sřůŚ3;Ţ}}ĺÄméŤň7jë‘”RI۬ bH°Ó:·wWCĺĐąŤ)U) .ś:Oîŕ3Ţ^ĘŻM:3󭼔噂Zé:Ě8Bxł2·J5·RösôkËš˘âěúŮĐXm‚üŔŁŢqÚ‰I*Ż‘Đ{ÜŔIă—KĹĎez„§+ů­ÖűÁđá{…ÝëI;Y ’$1"ßV%f]qťäĘôĺÇj¸ŢáéŕO2žÎĽřTń¶őçČŚLŞÖgţ%;óĺěëy˘s? VŁŔíŠćĄćhÖź^Ľúk$ęeă­˘NjŻQöR~ÍĄ'Żüi3ݍŔD&Ö1ó¸Ú€ĺĄ~›b^{ŮIJ€ ˇA6˘î·‚RČ!ď)íęΉĆl}“Y•ŤőŹoÝ–řăćí5ôĄř0P;­ŚŘš~ąHGŃX[ţ]fÉüAŁ..žý·ţQ*Öb¶šĺ Ć( Ś)ţ´›(˙G!JR‘ˇ/Łć36yj1 \9ň˰±Sć>é=pÜ˝sG1IäM©˛±¶Qăâ‡Č8ş±Ů˛-›5ŔŐc›ZlÝś‘MÁîío~™)Ě^úHąÚ:/PDzč»ŮBRNˇPđÇ`ůă˙pÖ«ýcĆ ´ń_8e /5I »řĂxýőLm9ŽWJµ…ËF ňŤÚtEńć3Ëd„=—ÇQw.z(9T}ßěiSg.$V-›Ih¦YŢKüşţ‹Ď·Ą}·eß[OL®˝^Uß$Ž™¶Ş‹Vť,Ű MFAóö7żLŹç-X <řÔ«áN摪ÝB–L¦żşgÎ]wµHyŇP”ăZŮ &2Ąm·ÜűâĆřŤů ^iSS:tp||ÂĐłůŐĎDĆD±­Íě~¦ČÇ<e f‘µ¤ňŔAíŞ-ÉĘŻjŻťÉ(pSŮË´XőÜ<j•\’E éj®»ř†×Wi‡Ď7ű”Ś’bŤFWUz1µ¦F˝ITŮ»`낳gäĤ00‘rĽB©ś4e÷Ńłßľű¬ĺ5—PĐ×5ë®-ś4fhrňŘŃ#׼÷ &M˘Ľad ”š—źŕđč[<ůÂśń!B“;ďŔ$IĚOů&zPçôđÄjoŁb€$©x*PNşsWç¸:(é`Íaćž(Š–pźňí›[ÝKšQ”@]–,¸űäo˛jDXnáŢj詎I @eÖ5±ő ‹ĹíŚ`ćĐ/(ś5™ZĚ ­A¶Ą_X¸ep`&‘IĚĽĽ“?lšKzA§ŚKŽ_UŐTÝÚF[°G„†€$5—D†…řë‡˙^<ÖyܸI)WË(mw±sYt]~Ľd™;ĽÄZ[ťw)[űŔĂ«DŮóŽÁł§N LÔpĆ5+ďů­ĐaËwď+˝ ‚Ń(@ĺµÂ2O˙@ůŤpdŹe!+R¤Q—·i߉Óű·_ąnj~¶'Č4Ë‘ykňž;Ţ+ŔoęO „^-)ň Š3é®ýÚI+“Ôa€8Ć€çßűDćá©ËĄ˝®Ím©Sđ¬iÄü5@–˛v%8éN‹ÝHŚŘr†Ő+—vÁ ^–ČM̆óf›/‰&“h2‚QI2ĄŻi¶™FôčŻÄ$ĄŇĆŃŐ<Édľ§#[Hyg$Rť›S°Ź›@ĺ»KbłU—÷$-vžR´ő)#ž˙d—¤°“#@¨$Š"3ź‹©”J[ :ůĚŢfćFö‹4´•ťç;šÝ苳Ξŕ9?0ic\Č~ń_DŮ»0íj÷ŮCľ‹^ć7ŕ‹pŢhĽ~Şď¨tîě­ˇx- JÚ`neç(U§ÔĽÂIAš˛_ɡ1QŹyQĆ”ţš°µáýżŚ śl[±» ŕü¸j/m¬á ¸0çAëâçş9„¨¤zÁPfącÖ˘;Ś’Vw=Óč1Ě0/źěÇą»F+Ę÷TʵęstĽżcÔ+Aa/…ąóuyŤ×ŹŐ2ňµĐ°B]‚ůę+:Ľ“yÍĎ ÎĘ®hŢ_Ý2k-T•}ť—z_z겴ňK­ÖĄ˝Zä±(8éű¨?“c .ăťNTŚí`&J„®6üĄ¸H'µÝGS&®ő਱ëď?ÄÍYV:¤u? č6ŃÍfSn®…ńÄŐFĂk_9|LĺţAb¤’§ËßÇŚţqęÔ§B]7çäŠU1Á»23˛ ŕ(•[ż-wO{,*¤żł­É¨/­`9ń˛™QŔŘXqčză4? g–ŕ˙O ß[ěźQTT*âć±E*ĹßŢ˙ęÍgvvüq˙Ţ%ăGĎľgí•Ëg׾ü·möt5îß·p|ŐśÍßü+ŁĘĽ ޱec1żůţ«wţýÍä(—çŽ 7wâ.§€ßł{ÓůíĐQ ×}˙٧>˙»-»ŃËóŰŮŹüRŮ+zŇ/›w?05>îŽű÷üľarśßů-ëż;–˙Ń/»^Zó쮣'źş:Wď€qIńĂâ(̶ŢÉÝw\Rüđ„XYăwnúo˨ż¬{ě‘'?ůĐŇÉl=Ü\  ’$ŮĹN=zęĚó« Ž0ŐV”Őptóë¤ç8j´JcgéÇĽ%nU§5=D€ăgO)|‡~ůŐ‡ď}ůÍŰC)Ç8»ůt…«Cěí#¨°stuwq´˛[Ý ZľÚ>hć˛Éý=üĺ\źLŻľÓUĆ%0–ţ±űBńśIÓ=<î Lnâěii{¶€Öď3;l‚îlË ăďżíąňí‹'÷Ľ˛ć•ýűq‹~úůŔĽ‡Ţ=Â\âl(صď$Đ*×j·¸ľÝąëáŁ6˙M)ňÍ÷_˝óďo'ÇąýşoĎĚďd\8a…}笕寧o.1ýţŰ–IK^YűŘŇşę†g^|ďřá˝s&&ÍgçťČBb]b—-ä1.ćąËöĂO 9jDňĐő§ką˛?ć<ň¶8xyzÓşËĎÜ·Lť°ŕ›×Q1Á;(îÝ˙®ßyđä‹ 6üşÝŔ('ôXňˇFĘĺ“Nˇcľűň_ď|ţí”hĎgÎXűŃ·2Íëöě}xÖh‰…mŮ˝÷o«ýáűM\Éą­»N\-Ls őßÎZíŢýčÜÉ3ďIp<Çć¬|íő§–×U7<óâ»ÇďťwGÂ÷Ż?:jĚy¤ŞĘłV­ť÷ÄGé§­Ą| €˝«ĹnX¨xxřpuťrcÓ6 ÜMÝA»1!O?[;'OW€Pg7Oo7»c§Ž+ü[lćŮyFf2*ÝţńńW_ţçŤ?ŹiwÜí%/tť\Ý]ĺmőľí[ô>űúăľţ,É]ú}ű>j•ĆĘŞ3XŮyçŽV>ĺÔs†Ö5‰>Ţž<pîžvj€¦ĽsĄő6/˝·ţµŻ$EúŔ*¶ŤaéŁď_N;%Ď”.úĂGpt5ű%€-}¬UťCG·ű @2Hś=oa+U7ć~W•n ”(|Ôö‘Ó•Úě·ňęňډKôUÇkt9ú6­®˝ź§=o©Á¤»\Ż/Ö×dč9Ň‹ŤĆZÁXŞ/ŰPZř{Ťű‚ € ŞĚ—s ``śď”č¨RJyçUĄ:ÎéÉ ×± UľşŁ•MZ€ĘIŃ”YWľŻN–·áZ.Oďď ŇüO JÔÁhŞ>Ű`7ÔQíHň>Ę/?¤łE}ŽŢ¬ĐŤťŇ\úSiѶjy“`íŕŞ(ÝvÝü)D>[XăŐ†şĚ&I„Ň÷Ďă`;Đ=ü—ś×rŻ‹ť\±%ţŽUşę}…Ą•FcnUŐŐúFI’Ęuµ‹ŠÓŞu¬]ťkUUWë˝Ő6ąUeż^+m~ěŇdŇ_ÔVéęNjë42+«˛tő•zCy}Ý/éź]-ş3fČCÁv9üG©É,LQlÔŞ$oO*ĽwöôŢĘz íŞAľ¬\hdO%ÄĄää5™hß,Ü{”âî™ědócVľô?Ť"ýĽB×ĆĽzěDN“‰ëCć®ÍMŰľkYmMöą”ó™Ůön±ýű+Î˙íů§7ÉšőŘ;OĎ|hůŠâ:FD(k‹I‰ť01Ä™_űÂęď¶žn>ąŁ”Huʤ ś0Ń•–®^őČń+%„RÖ»wŰ·Ż9S€—SęűŹťM«©®J˝Z˘Őîޱàr™8iDéœ߯˙µ¬ŞV4ĘĘĘvďÚq.=G6!˘`(//ß˝kGjz©«ĚŮsŕ¬ßŕÁ#Ç]I9ĽaÓ¶rm­d2dgž>{AĆ)éŤW·cÇů¨˙ţ×'¶žĘ ôr?í{>—ž#?d“SaÎŐć~ŔZŐiKĎĄR]půTZÖŕäQŢvâk/=·~ÇŚ]áꤤ#ć‚Épĺü©33A¨R©´ęĚó<ß:Ęó •đ<ĎÉźż»śŚ1řşyä\<ňĂĆÖ×_BűBW Ç3&]«6˝ô·çR·­Ë)×qô6Uˇ”“‹±ägf>÷ĐŞĚň:®‡ą©ş´-/=űäŽ3ą„ň®A!FUś;řĚăO_(¨`„s m.9}µ¤Gé>¬qŐT禥]ÎĽpŕHjÜř ÁÎü«/¬ţyßY&š4ޱ+Îż´ćÉgr$«’—ź}bűék¶×r/žK»Z§«)))J=—RP˘mC@+Yl_—S^O9.0,¬3ěk_XýÝ–ăíćaĚü0ÁdŇ_˝xöô…+kŇU¤¤¤äçf=ÍVÁŇϤfäTTiK ˛ľţčťOżß&H,f䢞˂1F€˘Śł©Wň† ácĎ˝ń÷ç>˙eźÄ)CC+Í4×dźOÍ(Čń‹3z|Ń©m«~:Ż®±ôjjJFç­jk˛ÎĄdä—4§â‘źĚ*•6y9imxČ$ĆL&ýŐ‹)§Ď_1 Ě=8¬Yʧ®ÁĘŽĄZ,IC}ejçÜřä»-"»ů÷č7č/`2ćfž?yú‚ÖPŻ»pöLęů§Ňré%ŰĚť)¬KĎH(eLňę7č®ä¸kת8šóËćĽ Ú·ŻN‚h4dś?ućb&!DW™uŕTú°Ńăüě„V?ĽéX:ÁÔĘŞłVvžQJę*¬|Jęá ·WTëjJóŽź:Ó őÚ?Ž.*«–Ś5'N¤‹Q޸´ógе:BZĚ chjjŇ89ĆÉsçŮ';śŃF^Rű:G2q»R~µđłă2ÖQIsy‡‹fëÂÎ+8Žr·µ5–ě¬Ú˛=č {3Pâ1Ů­éJ­.×Řňčźt¦v˝zKÝyeÂ&2·…~ˇwŘžYž)YŘF(ab—¨»& Ż8&Ç!¶ł÷ĺ+öÖÜ"ő±NŰ\Ň^YĺwŇIaáŠĆrJŘ äŽ&tZhp¶ěbMC˛ísŕ™tgL˙çC]'l9`쫌’}ć$ˇnîýTżĺÝÎŁJ!I1my”Cń~ÜË@)µČ´}3Zëžo\j?DNqÖKŚL+Bä\iťFbčă‰ĺŐ%¤çS˛u‚?Bé«Äöňek&I “D»čÉű7~ęB›LĚ*nkuBßűĆÂFşţ଱{®4r”ÝËŽ˘»té}=×1Şśł`ţµó‡R.ö4ćÍSG)“¤ČÁC‚]ÖoŘŃ—I€Ű µý n|m;obÚŽŃ˝2]ˢ[ěćZň‹ś¶X íŘ|p”Š7$ b}?˘ÍZ‚[ő¦U×ÝuşéZĘťqŁwâ˘!w’±ŠłĎ$ůęc_ř‹ŢÚL9ĹíŔ™Źî˙쩱!Dą™>&I¬ŮZ2©Í"¬'$ö8h/eŰěĆ2JţnfÉ^oYÁ·‚Ę:ĐYS3%¬uü:ë>Í`íK­Ú‚´čk7«€Îxn)'śĄźvűŤć?ˇó ÚÍ „IĚi˘‡ď0›Ë˙ČoyçŢŚ´[Žő`“Ó7ëEłSç(c"c„ŽĆŘNó8J ccÍS´ýƆB D«‰F i~lM˛üŞHbLŽB/vaĽZ[ŢŰň0‡…„/óu|ôčYˇ/é} ·‚0Žç©%JŁ$šE*K›rłd–o¶7‘Ç1ë`(7;BB)‘Ě9•)»áŽ˙ä~ĺ(“Z);!”ă9H’ĺ1I%”rL”$Žă;űDO©(‰˛ł3‡TęAĎV”ÉSÖĂVm^=iŐööĂcĽťßĚéăÔ\ËľPŽ@˛ţŤ”Ę1ýÁíżÖÝxXB(!ňvHľp[Ź{¬crVűŰGXôîđµŰ"Š„ă(a‚`»$JÔş„Ҷ‹ąă˛\Ćq\Ë^—Á ;O 5gp7ß8˛Śă`bgq[É‚PĄ‚L&‰1žç™$‰ă8N…6M:ś_˛”r<%ˇĹĂRVęÝ[YXqg’(E%žŁ˘ 0™˝’E:„đGQŰO9keŁ”cLjďA:¬sóĚěçÍ˙ĎS§ŽcŐgę$SGbžÓ„¨)ÂôƆkF&AĺŁvµ« 5çę%ç˝ďvŐťŞŞ/OŔťÂÖ_E(^lĚ×KkY»˘łAµQŻ%ć”Rç;(âĄřݰ˘wł+N72v[±ÚX“Ş“ÄŽ›´Ph©C” ç${žH5'ëŚF©'4›?†Ä8¸D(Š«b=ăQ‡hěCÔbť±&Ą^Xkšęs ŕŢ3\n’crě§I^ዜÓVdč;YŹ€P.ŃŰËŽHÇJĘ í–Jr#/{űw·}ýń2­QbÖ­ţ()ÓKŚPŐü ´’Ľô:O©(IĽRá`§ 0 ĆěZťQb”HŚ1?'č‹ ”j9ŞépW#ĎŢÎeӤߜ:şą¸š#DĽ]SÂü1ÄÝs”‹Í·™yr<¦v oş’C"˙ëwď¶}…}yXÚ1+|†& ®ŻĚ?xä”QdŹ" "#406Ú’üR­_pXŇ–:}F@ŞáŁÇŮó†éŤ7Řóíę‡LˇrŽŽ ă!H <¤ěôKŐF‚"[rőô±3™"ë‘СG $2Q9…ÇĘGďsÓÜK§×˙ľ_–Ľě·0&uëe:zř„úr„Ô•gw»[9{řzÚ]LË”@bâ¤ëĄŐ7Ľ–˛ęťďă Q˘0ŽSšŞŇ.çôĹrâF)˛HŮŽ3N3ď‘÷ź6sěťyĆü~ҡmi?ö>™ą\ˇC“­mTě”Nľ“ĆŤäš*ví;d0I \`ô€ÁѡőŐŇź0z! µ‡W ›}NF–eá@#“F ńľtęÄĹ܂ʮeŁŕçÜT“–_HnËďťťşÓ…­BˇM&ŢŃ#*,PIal¬»r5Ű$B’ÄŢĘB¶AŃG‡ŐW<|Â`’:ÔyBŔń‹Śun¬N+( TąŹť8Ęž4Ů»żBol>Żô‹Ś3×ůßşŮĐXÉÂëŇé“s ¨Ň1&*TĄÂŘXzérÄĚÇĹ~QqÎ ÝŹKV0wďoš–žÇ@‚bb…ʢZ“2ÔßůâĹ+7h3 á(aŕĂ#Ł,”ăDCÝĄK™&Fy^ŢŠÍ[Mů®>%Dv”X|Š(J’$Ů}Č·DI"”RPާ™Ij»éPz˘cšÄC^ě7ňČŕáűâ_ hÍNN‚ä4dçŕä‡ďOňm§;µdÁň w|wUä&‹G¦Îš¨iMŢmy’OŤxbĆx%€˙±w5ňŰjÂ;ě]¸ŕĺHܲlˇÇżčˇëőMą™™UŐu~xĎ›|Ŕ%b|ž¶ćžńýN˝§Noh]çćą&źpŮľöé–ęëÚâŇâ-źľa#ßľ~äg˛Łç_-¬(,ČËË/¬,Ě^<> ŔśÇ×–hkł®\ŐŐhźX~'ľŰ|đV‘­äěÚĽÚ÷ŐOľČ,®˝zř%ĐŢż’ßz.yü_5ő5Y™•Ő•Ű?{Ó[ľFŰ]«ĺĎ|‘倜'{÷ĺĽ/×Ü‹®ódwĎP €×DîMÉ.-ĘĎ+(,ĚĎĎÍ/É9ż?ĆA PEëw2}Ý1¬Ż´ĺFYŇ3ěrřÔű˙ą® u—mÇńWo‰Ń„§ćW˝ýŔ]¸n5ąt`[~|߇`÷OËŘ·~ú¦Ř˝~s˛hŹëđĎÚŁ>TÓ`.9¸ţŔ& nĎŮ«e…yĄUÚ}ß|`„ßqOˇ¶îZNNUMݡźe ۢo% ”÷|ęďf•ióÓŽDŰĘ9(đŘ«źÔÔ7ć•dśčo«@»Čźr¬ż1éeMg~ű”fPľ¶î‰ŮC¨”J–>V^Sź—uµ¬˛ęě_c\p¶=—…~-rŇŇ‚J˸~ůŔ—`űĎ6|ćxkzx€ó‰ţýŹKĄĹĺ'vÄřŰË}ĘuN˙ú‰°JŘřż2Ë[dqĺt vÇĺmA^nQyŐĹĂ?kß™÷|\˛…Ľwő—ůWöŮňg»?+˙ÝűĚ]ţfaöa›µ™ťZČs{Łl•°RˇžÎ˙ÖőÚ·’•Pí>rďŢ˝cClĐNK;Đźvó˘'unh’ÝGźę§v›á?!c¨ĎH[Ŕ*!hsŇÖîýŐśŁBĺČĐÄ:řLwĺ»>ŁOńŞÔC¶ ňe ŔmЇKŚĘeş×ÝěŐDhźüGRÄ2wTI<9üç(…ÖÝŰĆw‘Oŕr·$űŽDP¨o6ĎU.łµĆ~jO˛¦Ím)<›ä§´ç5öt˘“'%®v#O śí¨†lížf®s|.cĐ ÷ťšÔ9Ç@ácă’hŔ&ŃkLĘPź!j¸;Ý>ĐĚCÔšÄÍ}Ŕ1µď“áCżď×^d ń Ď[±tˇ‡ý¤~jZ¶ŘßV+cYŃ#<<—„i€X˙Ľű—Ěń´uő )\yŹĄŐň…~Ž€ýysî p09,|Ś»Ý¸ČôE3ăxâć虺|éńa°lxJaö$WvŽËccë3Éף#! v;çĎ%:Vń7nȬ·sʨNbŃţÉ ďU—'Ť>9cś‰űVPH(5däËć٦!YnŮEŻfď»ć«âěcÁ®v–ľĐhlz|ţh|ç9,­ľ.¸zHŢŐČĚ˙úٸą]Ťe¤Ľ_h¨—_đÚďŹ\9´>ĐŰ/$ÔçĎĘSŐWÚrK±Ëk(Oe—żóđ<ÜĐľâf€ç€‡Ţú)űÔďf ŮŰ‚v¶ĄŃŘ´d¸żűđEFf˛Śť-䑼ЪäFdŃ.CăŇѡ^Qşo-0`Ęj˝©qΠ)ľg¨»ëo–0·NŻżJuś<1@č؇őF㪩ÝË€CěŚă'Ďlްą +µż˝@đů•şÚç%ŕŞVˇíD•#ń*?ţő°ˇ©éŘćĎlâSŻä=8}€»ß7)1hň˛G*Š2ixŹ…uúŻY.7îą,ä?»FM7T—Q—Ź rkĂç©€¨ezŽoý€Q÷¬14VŹńłá=“´ Ćż>tP3Íż}¦ş!e¸m Ćź×,‹ŘACśEř´ĚěěŮÉáö®^>ÎÎA+Yô`\-2sźšPČ*xďţ…óî}·(÷h/l¦lö˝ÂfÍšáÄDq×ěYQa~îţ>!Í24Ě€[@ÔŹŻ~öɇ˘=ĺÖ‘qSîś8ńî_xnúř$‘ńS¦L4—Ś@ĺbö)±!4ľáSîš6áÎYĎżřÜ Ń"˘yůŁ˘Â˘·źy`ÉŇĹýü]­ß^ÚĎ‹žÔąMŕf/č*Ëŕ=ÓUlbv ­bq˘VřÝďşĚKíÄhLŻ+Ů~]ŚZ˝ŘÄ,l $Żűâ˙¤âÁ%ç8ÖĹy Ó™šĘŚ$##^vŢ“ěKż/51uÔŰýüg8;Źt yÄ—SÉřÚŠś ¶UŞ%Ý•:KĂĄÖŘ[*0ool]‡pÄTe4ÖJ[µXg2”eYtKłÜ3ˇćĄ®Sx—c`*iŞ>§· ŃŘEŞLZŁ©ÖDÍ›yč"ź~“>ác€.CÇąŮŘiZ/Éĺuń¨©«ÜYOö i4‰ŃŽ.‘ż2ŕjEůŮůŤ@y“®IôMrqŻ©­[5 bŚŁ3 QJô˘0o@ŇşŃIž„D ś")(`”—‡Ń Ď«o`%ÎÎ}q¨çĎiéU śĘţýńcěç?Ň?čąÁ±v\[!Ë?4N®ĽtQ[ôU¶ŕž‘˙»Eß@näúi•ĺŤc”‚âÖějäkĺ§Źţgݦ ´,·ˇŃ$Ł2™D>`čóG~őö;ő@îąă_®ŰhU§€ ŔČřäę¬ăżr÷­X$IúţH˝‰yűű‘چšŇ‚ęzÁŘX“WZ%2Ś˝c´M㵏_˙7€”ă;Źźą @’Xűü)r !ŚńŢѶn[łü®Ä1Ó7oţuB| I`8µŠ“·DL$Á_oŘôÔ}wŤź:÷ŐµĎ9Ř€u™…ŇbÇÁP\Uż˙ˇëŤ’ťł9¦A—­Z®"HRËEďÎFŃuI 0ˇ,/§¬čZuŤ‘Iú˘Ň˘Üś˛Ű䆥ügb—?čD%xiŚ)玷;^ŞŔů”c¶>‘ńv ‹÷č´·-ŤMF˝A12>ůzŽyě€!"hđ„¨ÁUą'oFíqéę *MYĆYKIN}QbJg'ĎüKd6e§Ąähë‡Ĺ$JŐůűŹĺÄDÇ ¨-+¬¬Ň˘Íűv˛ŕ(iĽş+yhâGŰ+Tj91ÁءŁt%WUQS~Ý´!Ę“Vé r8ţćyÁńDb¸ëÁµCß®?bď`GBĎAßÔ¸ě…7űˇO &*ű3ďž8b„ˇ¦"» @¦˘ç˛łTĺg8š7pPĐőň‚‚’š±G]ĎnŃşAQĚxä5™;{ €Ęň˛ŠëőaÁý"Ă"ŞËKËJĚ\ős[3â˙Ő hrČ6˛¨<Ą—ÇźőŤŐşŰ‹ Ö˛čů¸$Á¤´ó¸gŮâĹËďńtPV’¬mfçr‡Lúü“÷y€ýčË˙Κ4¨˛° ¤ ·ŮBćdńľ1vě|ĺń% V˝¸{Çćˇ.úŹşcĂÖ=?}ţćĽE÷˝őö+˘“'nܲű§Ďßś·dĹ믮vŠüió–g—ß•8fú–-żvpŹş~ăÖĎŢ}yîŇUßď¬ Š·jáDÁd>ků“Ý Ő‡Şvúc5/,úÜUťŰ»–ă€SťŕżŇ_Ąo¬Îä«P¬M¬-Ij¸Ro,29LöôśârîľôúrL’aĎH…uÚSzŘŘ ’ĎĘ µŹ*}ĹĄň — %çłĚ—sQ*8AW`Î+ä9ŰSQÓX˛łu±±S–ţ+/g}ŤÂž“Lk’Ú“§‚Μ͆prü@{­öDc{š[ŐÉŻ˝~Ţ`ä$]‰ńI”Ŕ81·îúń&¨5=¤ŮZĂ`҉]qL~Š“ŕó×@[eý ­6S¤¨Ď0óĐkŠK겴úëLÔ÷ÇŔ č$ÂS•Ńeł_]\7W č#TŐ j®9K ĺ ™ ~eč`}]éžJÝ oľJoiU)Ş8  Ţ$.ägo»rŰŽß*tăśü§\o§¶uˇ†suőroł"#śôŐßeP©ť¸ŘýtúĚÚôg%×$šµB-Ó`§TVg4â˙ ĐEJ9o;r±ť„đî ś‚'&Łäńλo6eŘşç"(•$ińĘ•ÎÚKź˙° UPë:i@ĎFo¶şö±—ţî×xqJ“ZyĂ_nc?ň….ŽRJäH€Î6>u5é:ˇÇ$™9íMĽ\ÂqD0cö˛;yżţŘk©ZĽůékVĚÝ÷ř;@«ëÝ6ޱ‰±ß>˙Ôłîqu˛m0k?Úľg™8Á¤wň‰ŰµkŹGżëéűŰz€$JťŃÓ˛k¶¬¬÷(ť·ęŞÄš_ćTÚ%ň•YóăÚQ@_iË-Á.ľösVQ©¶¦úĎŕP[ÓÄ)ÔľA\Ę%ÓŤŮkۢĎ>°őlîü±¶•UŐňŘžÓŰ(¶¦dŃÖŽíNUPf<Ţy÷ͦśC;Ď\ť”đŕ”á„Đ»Ă|śOE‘Łďřé“·Â3ďĺűć8ŚzaLÉüÇÖLµ,~ŮľwńřŘ3Żg\•o[ß»ś ˇâş˘y©ů±ú_$ePç9÷pcjUĘ‚ çźÎS„;řŚ·—ňkӟΔyhR«\†ŮĂ$BÉőÇĚ+*0‘‰uíB˝ÉsO=ť<—ű9ţóLj-8•ůÜ‹đr°C™cL21ţ•Ńc&;J÷:ex&ş·jEĆ8>ÚŐťŤŮú&ł*ëßş-ńÇÍŰkčKńa vZ°5ýr‘Ž˘±¶ü»Ě’ůF]\<űoýŁT–ŔöÍ Ó[cĆÖM”˙€Ł%©ÁČp Ýa˘É(hŢţć—é‘Â쥏”K ’×ȧ–ÍŘřÉ"8ž…¶uheĎŚM>‘Łžő×ç_¬!*5w7mng?ćĎ´¬ĺ#Ł 5)ŐÔ<3Íű=Bx…˘Őys G$€w`DťV;|ÁĘW?Z•“žQR"ß6Gäo˛„˘1óŘç_í]öňOĄ%yďüĺµ%vPg=Ëż(ĺŚMŐ;÷ěŞi0šęJ ‹Â@şnF(Q PN”•3?˘č¸U—%­ÁĽŃ˘Éj\ŻJůOÄ.źřVë ‚$)T·őF‡Ž'˘ÉT_ݱ—éI'Ö¶eÖ=Ź…ŘäŐŻyějŻ`†ź~7/‹vvŚÉ`)™±čá&`Ű/˙ůl[Úw[ö˝őÄäë••ŕy™G6 ;eî“ŢÇÝ;{ŔäĐş]ÉBŢ•KRóÍĄĘF_žńŕ‚e«–/ÝĄfÂŔ‘ĺ Ďqrš×ĺŹ˙#ÂYŻöŹ;(ĐĆ#|ᔼÔ$)ěâăő×3µĺ8^)Ő.=$Č7jÓĹ›Ď,“öJ’$¸rä—ab§Ě}Ę{ฅcę*+ý˘ÇČ|ÖQĄŢ¨Ľ÷±D8lücĆ ´őî7mDěÔ…÷ µąoö´©3ňÁλsÁňŐýÜL-4OO&Řm}ŰkPŞ4-˛Č¨şcčť(96{ĆŚ‡źx|ŘČYu¶ĂF źs˙ó‘®ĆŢŽ‹WÚÔ¦ź0ôl~µFĹ3‘1Qlk3»ó1@HsÖWËaąjWmIV~P{íLF›Ę^& ˘8c÷…lĹąyÔ*ą$‹@ŇŐ\wń ŻŻŇźßěSŠ5]UéĹÔRő&Qeď€ń”&߉l˙•˝µţ´Ě Žă›í|gu¬çÎm Ŕ¨3)śeß\Ďfv~JcŁŔ&ÎiŔ7qQOűXvś_-Ęßś€ g=óçČĐÉI'%pË^›]YĆĹżDIdL ™ôĺ&Ą· €űžjb(ÚUł#rßĚ<>)őę·Už‹}ÝU°~ŁË…®#śĘ_Ó,ţŔg,ŘóM„#ŚAçlMłUťËuąFjÖ`¨<©«;]ÝX'Ůű©F»¦ŮSŐŽëM —î8ĆLlŞŹT™LD`ćĂKŃčŕ8©8&25Dˇ±¦Ýz—@ą±ÉEmłńbʡj)ĆAŁ5xřţ4}Ú‡Cc廹 x|hŇ,ĺ¬í{ĎU7(5´jUi4Ä–?vtg˝bÝD` H’‘1@źßĐäig`JHżR˙uNľL3ńÍă~ÜřÂ…üqqS}lŃîE€ëuő&đŢN2 oçLřß™ˇv”sôň©˙­ADžď“ĹcťÇŤ›”rµLˇä0{ÁĂÁ4ďăź6ćlĚÖu(ĄŇM_!’]Himą«Łóşwžß}ÉŘ?ħĽN €ôf uűű‘HF˝±ŃĚ…óYś=#'&…‰”ăJ%€ŕ¤)»ŹžýöÝg( ¨ą„€ľ®ÁXwmá¤1C““ÇŽąćÝ_$9ÔYQ"LD ©yéń~ŹľµaÁ“/Ě 4ąóž!ź s:mţ;ď˝·`ŃAcî{bŃD€…$Oí¬ €I¬Vđ6*H’Ч€ŕ¤;wuЫْŽ,ÍÜE±9„«|—áVŔíŃ–žb—€Ę¬k&bë‹Űţ”AÎúŰ/(ś5™ZĚ ­^wbm7N§—(®.k{|_U}IľNëbk˙ýÍɢ˝ă8*JV%™%”Rv=÷‰ąă˝ü¦>đ´‰qąĺy čtÚ}[6–ř~aá@ëEIç˛0 ’Ńd”c¶U5U ˘9ç»I”äTA–ąĂK  µŐy—˛µ<Ľj@”=ď<{ęÂD g\łňžß ¶|÷ľĐ ’ Ť‚T^+,óôTěĄ,8^ÁQ§Óî۲ˇÜ ‰,Ö:Ű9~˙Îó»/™ú{Wu5—łµ+^5 Ę^á4}ňáţá]ŢŻűNśŢż#C«OŚŽj¬ĚşUµŇBó¬iăÍw~nŹ"öäůUŐtÝZ˛;•Ż)2“(‰gŁ®ą~íRV‹,ş—ŮćK˘É$šL€`A’ F©FÁkšm¦=Š=-1I©´q´D5O`2™ďéČRމÔ`çŕć@âěă&Půî’(HLVOyOÂ$É\B)Úú”ϲKRŘI‚QŚ+‰˘ČĚçb*ĄŇÖAN>ł[éĎĆrŮ/@ĐĐVvžo]§ąsë¨>y˝ľĐč0Č-úő0ĄÁPv¸ď tîě­ˇx. ˛.*x™ß€/¢±bw­ëÝţŹz‹u’˙ŠŔ„ŻŁ=†hž)Ý”¤ˇ)ű•ăőeLéŻ [Ţ˙ËŘŔɶ»«Î«ö÷ŇĆZž€ s´..p®›CJŞ eb‹ľu‡@Ňę®g=†9`"óń¸ŹűWQĂě(\ZŃlU'(áŰh·x›š´ZE cÔ+Aa/…ą„(ŞÓŞtVtEóţęVö‚Ŕőă×ë şá× ¤ q!úĹeď´ŞÝgů.Zć!o4\?©Ç)śúc .ăťNTŚmí ‡‹ň/Ő5ňůxě(±~cA •f¬ż˙7gž`ÓŁ˝ž[oV LÜ>ýŽé>ÎŻĺfčšäVîbý†übPĄ«Ę†3ÖľrřĘ=üÄH%$O—żŹýăÔ©O…şnÎÉ«b‚wefdŔQ*¶.~[îžöXTHg[“Q_Z/Ŕrâe2Ł€±±âĐőĆi~ţ@Ď,Á˙ź@ľ·::Ř?ٍ¨T´¬2ú8žc sVľöúSË몞yń˝Gö͸<ýč‚Íßü+Ł •R”uťă‡÷Ι4ź0Ý Č)ŕ÷ěŢtľ@;tÔÂuß˙č©Ď˙nËnX<Đ˙`?rŻčIżlŢýŔÔř¸;îßóű†Éq~ç·¬˙îXţGżěziÍł»Žž|ęţé\˝Ć%ĹOŁ0Űz'wßqIńĂbeŤßąéż .Łvü˛î±Gž{1yřp™Zz|Ĺ_ß=~ů‚kŘč˙~ůŻw>˙fR”g꥔ źż2jÔČášç>¸Ö$—ľŤęŘsĎÚvlŮXŚŔoľ˙ęť;9Îí×}»gÜ˙fÚÉ=ݬye˙ţźš N=±ëÓ†Ť9˘Çă’§ź­ť“§‡+@¨ł›§·›Ý±SÇ~‰-6óěQ¤»oŃĚdTşýă㯾üĎ~Óî¸Ű‹€˝“«»‹ŁLÄľí[ô>űúăľţ,É]ú}ű>j•ĆĂÍVi–T;ąDĆąŁ•O9őŔśˇuM˘Ź·'ś»‡§ťZ )ď\i˝ÍKď­˙`í+I‘>°ŠmÓ^¸č9 ŔŃŐě ”<¶ô±VuÚĎťŰ(Đ Ô¤4Ř'9*¨)űµkµąFL`Ć}Őń]Ž ôSŰGjLWjłßĘŻ/x5×UŻËÖ‹ ˘ˇÂ¨»Ň`Đ ’^ĐĄ×ë‹ő5zN†ô†ĆbٱV0–ęË6”ţ^ăľ (`‚*óĺ\CĽY ŚsâťUJ)ď㼪tCů)^Ç‚VůęŽi›ŞDޤ-Ř+Ťş+őú "3·ĐLyÚm©SnlČmÔť«ŐĺíăíU’˙YAÉľ:đDŞ7é.wLséOĄEŰŞĺM‚M´S¨˘tŰuFL5©ÝpL2Hś=oa+U7ć~W•n ”(|,<|3ż®ŔDxťcďÇŰîá÷¸äĽ–Űx]lĹ–"™ô”iă|}śIÓߏť:]Ó@ÄňúşEĹi5: PR.»Z›v˝®Ţd,khĽT}˝HW÷GYU¬Ź·Üęl­žRŇhŇ_ÔVéęNj˙?öţ;ľę"űÇź3Ż×-éťôB*$PBŻŇĄ ‚Š(¨¨+¸–ŲčîÚÖu]ײkY± ĹBW¤#=ˇ„Ţ{żÉMnny˝^óűău“Üä¦ýţ>ďóxěš;ĚkΙsÎś™3ĺśF[™ŐµŮÚ¦j˝ˇ˛©q{zÚ‡Y%3˘‡Ż ¶˙ăń_ËMfaŠ‚dŁVŤđńö¤Âß/ž?TÝd>ďł”2! (6˛§†Ĺ¦ĺ´číY¸w ”âá™älóuvˇô;Űy˘„HŔ ŻĐ-^=u&·ĹÄÝćČž”J›‚ÜÔK©YŤZMEEŮ‘ĂÇăç˙ńąeᏬz°´‘É©Đ,딕•¤\J.*«ąĺL&ŚR"5V=‘?eŞ-ßřČŁ§3ĘĄěĆ&Í;׎śťÖĆĹ9ŔË9ĺ×ç.¦jękS®¤”ŐÔü´żAĺ:uÚň«gżü滊ÚŃd¨¨¨řéŕţKiąňĹQ0TVVţtpJZ.i¬ÎýůČEż„„1 ±ÉÇwěÚWUŰH rŇÎgŐ(”ŞĘśÔ‹)Y¶žľc'LôVé^ůÓ{ÎĺLząë–/ĄĺJ &“17ăŇéóW$†ěě+őM-ąi§ĘëZękŞÜ×=×rH}Ńős©Ů IăĽíĹ×6?űÍţ 0{ÂŐMIWĚ“!ăňą W3A¨R©´ ęĚó<ß1ĘóM•đ<ĎÉÇß·_[nŻÎŽgLĘŻ7m~ůŮ”˝[s+µ\?Ýůě(ĺ$ƢǬřÇÓóź]÷Hfe#wăJ¬mKiińĺ«W 󲏝HŽ›<ĹŤ–?ýđúÓŮUĐŐ9~ńVdaŤ«¬¬äbrJe­®Ľ$=媌˝$ĺrJQiÉ€°Č ă'—śŰ»î±ŤE :Á¤W;9ĆĆĹ«.o~ćÉďOdÂʬuĹľ­ą•M”ăĂÂjňR÷<\ˇ©ĎKM˝žyĺȉ”ŘÉS‚]ř-Ďoüb÷i«±CXköb“Iźuőâů+kŃV%''ćežĽśo§`iRŇ ŠŞjkĘ‹˛?}ď­ľÜ+H,zěň’chii±uvjí×?üš CÝ‘c|Î,#”Š‚(Ó“yůBň•Ś’Ś‹ÉĂGŤńqŕ^ůŮŹ¶’@Úęd]˝xţJćťPÁ›Ć(%bCéŃÉ1S¦†¸đŻ>żńŰ_’ Ç»…ĆÇGV]:úô†?śĎęĐ÷>ö‹1&c^ćĺłçŻH`ÍMÚ+/¤\>s.57aÔX/ŮfHfčiZ$”2&y :7)6?ż–ŁąŰ8 ±_~i”  é—Ď]¸šIŃVg9—6jü$?{áůŤëwťJ LĹąYç/^1ç™gLÚJĄ¤±ĘbNI9ľc羪z­¦Ľŕôą Í’ĐÜTóë‰ă%ő’QsćLšČĄŠK˝|ˇ´FŰ~E« ýyňűăé°wRŇr%ë:]ŤťŰ ­9 ¬n÷ú»Á%Ó8;;cŮ í—űÔ2%¦»·d4hóŚ]Tł?F¨U´ľp˘»\Îa"s_ćz—Ý…Ő™Rq̲äV8&G&˛wđĺ«iş'¤Ž>?î놪É])«üNzD@Ř@…î«Ü2&;¤7¤Ô„Î .Ş©¸Şi¦wl˙‡#TdŇŚč!Ď…şMŮ}Äř;Ë+fNęî1ÄQő}^ÉíŘúďęHÖY˘ $†1łWG:–ţ÷ëC ´51ýí{×Ó÷7/‰ß´"§8»Ń•_ĂŠČYn,sEwÂtŁ÷¤úÎŠŽ ţ!˝ççî#Č—­™$rLíŁ¦Ţů+m11ó†ĺ(,ŇŞ„PJ,Y&ILŢ_lĎ"O6RÝĂ &ţśˇă(;ů –:ßqÚ¸Óăżď:F•‹–Ţ“ůXňőâ›K…y3ÔQĘ$)"aä°`×ověżŃ$ŔÝŮ–¶Ö‰EuBŔpó#·\˝m=ţşGßł,:Ř5ÓşKBoN iĄâ-Ȣyć%§ĄÎůĽ]Ŷ!ťúBĄ¤µNwč)•3k±ŽĘ9B űrr{ťĐYSÉóśd™ęľ5ť7Ćú¬ţ=ĘBľü*1Fĺ(‘ß[ek&IR¨zkINq?˙±Ă>51$üJł(ë’Ěd&I¬ÍZvęoßFź·oP‘ú‚˝ű:·6N{éF×DŽ0É⊕U±ý9€<Áž@ę0ŮJä7ýíE”¶……Ň>z{YtăXRҡ:%„db–Íý’MFÇqÔÍ „IĚyęßQ6×˙\Ř™ľ6±µ‘$/í)!rk„&Éϰ,čě¨BýñŐ’§TÎFŰf !!Ś1‘1”P%&Q”žR©7›A ˇöF~*c¦Č’dYJcr¨±‡} ^#!Ţy2*dŕ*_§ÇN^~ÇQ]úź0B8BúľŚ uŰÉŇVBqëďjÚĐPJ$sNeʤ›]ąýĆíĘQ&uPvB(Çs¤ÖÇ$]”PĘ0Q’8ŽgL´ä*ĎóŚI˘ÄxJEI”';sHĄŢZ¦”ٔȮˇ”ç¨ @ďôŔjáŐ—^tŮNwěcĽ˝ßü9“Ô\»ßA(G Yvßş„rśh2čőFPŽW«Ő%k&€PŽéŹîűľX#tܡéIç)Ą·×oďŠ }Ô19«ýť#¬ ndóŐŠĎ”ăÜ”4TWTÖ7öů(ÍBçÓłFć:rDbSuáŃçŚÂµ}ŃUy ­đrx÷˙ţ˝qŶĂ×ÚÂßn·“',yôť'fĎź8ŁŔľućs%TáfŁ ĽÁlI@obäZ·L 11bĚĐŻkçÎ^Í+ĺ‚"ă†E…6Ő—;~Ć`’(U§Žs Í'~>Re0Ę™Ë#~1.şúÔ˘’.¶ß»‘E7Ř˝Żť;s5ݍK唍‚_D¬K‹&µ°(ÜWßż0ĺđ®+ą5 …B4™x§‘aJ Ł®1#+Ç$B’Ä›EzÎźąš[D•NŃ‘ˇę6Χ1…SLt¨Š‡ĄU—ťżČXť&µ°XţJĄ@w–_¶PAQń–|¶–E›ÎŰs†ŁG»¶«„w_ý@OÜ0úöWľ'<Ŕ+ŔÝ17=«kéJ•Ç„)c©îġ#UzcďöZ6A.žŃˇXKCyZv §tŽŽ Q*8Á ËČĚj1Ü`27B8Jř‘­”ăDCăµk™&Fyž#ډ­®µUuNEI’$"O„&I˘$J)(ÇS‰‰L‚dĺŤČ6ß7(lä„î,d_ęt9NűlGş'>jOüŘ“ĂăßRQ€*ü׏ü%!ig´ťşő–šĄ˛Ľ·mÜWqc},!îĺ €Ť]ŇŃáţSěČ)\\f{Ť99|Ôî¸1G>dçĹCŢ´NŁ'řN˛o+éIĘĽ7„%m‹”c<Ú%y$5Ó<ôí đX8öTâ¨=ń#Ś&1d©˙'BĆťžtpŘčďběm:¦§”»ec7Ş4đXî?ňËŮ•°Ä˙FŠ@C_4öDÂč_† ~! sJ—‡MěçCĆšôs­‘vîĽmұ[ćü§Zťx !lKwü$Äfí°gV,M^8=ś§hĎu;gßcË–¦,[xiŲŁs&¨Ŕą&ŻĽ÷ˇ`wJŽ;0ňÚŞ{/Üł u岟gMڰS˘5—Ął“÷ąKWyŕ{{¨"˛9Żşţá™1đ—^R[TWRY{őř·¶ÝÄşşQzäwŻ}°»ľ®¦´Ľt÷s¸™†ď\;”ㄏż'«¸Ş¸¨  °¸ş8çŢÉ‘mŘRVÓť‘ĄŐÔ<±zľ×|đ‘­äćĽÚ÷Ő÷?Î,mČ:ţ•0źß~_®~úăÂŚ#ržěź®ügÓýč9Ovď ĄxŰCÉ9ĺ%…EĹĹ……y…eą—G;ŞŞčřN¦{ŕU*€UxżIŻÍËĘ©o¬?řź·üy ŽďÔHG†uÖůG[S—ŻŻkjÉË̬­o<ňŐß˝ąŽbľŤĐW]•ç>ř×­E)íşŽżzűH¤íŔ”ÂÚ7š €ëU“­ře§Ŕń^m%Ńö6ě#f\+¬6[’Űí”ěţrc#×—śŹżúľ¦IWPTRžy~4ůŢ’šĆüÜÜZMă±ďzđŽřń×kUĺEEĄ•égöGű;ČúEOH«h9˙Ýű Ŕ"!aW˛ @ůž°—Ąźb§€UäO9ÖŚëÂ÷p±ZXÓřÄÂáTJ%€)+ŻÔ4dgUT×^<ň]L€+Î.üFdaf_;=çŁ9а»®ŐČśO=ąS¨Âg\+ŞîhŐ-(üáC ¨Íl——•ĺ—C˝EL[YTÝĘçí˙đă`Ăk˙nĺĆŮ!v`ó×îĄ,«gWPÝ#7üť­ązŁşQ–q.V x†ď¶Ň„[Çń&.]_TY[TVyýě0hŇĘ⪆ܜ¬’˛ŞěË'“â˘ő Y_ [ yéP¤ťŇ’¤ľŽ˙Žő¬ż’•Pí1öСCCl¬{-wsňňu=XČľÔéB¶JôĆä›…·Ťëp{6‰^’Gú WĂŰ9á«Á±oJÚ=Xv,ň˘Ůóľń'âí<©M”۸s#|ĆÚęệúŽłŕ>s€k´ĘuŽ×źâÔDčôëđU¨’x6bô·‘ 9¬»·ŤďrźŔŐ>î#ş‰LĄ*aOBŘ73Í>6®‰­4_á›d JíÂmŐ ŹE>ăN$¸xQE”ëŘ3Ăg8°d§Pëf‰Ú6ńűŢiŕ¶Č7áÔ{ĘHďˇ*Ĺ÷ WFzÇŞÝçůOIé3Öhg'sĚeŽĎ„ł‰Îž”¸ŮŹ=;2pˇ ľ§8&#ň}rŕČ/Yk‡¬Äö.{ďžýůäIÉ÷̉´ňj¬ëD)8¶Îţ§îY0ËÓŃ^măĄR€ň.‡/ş/Ŕ Ŕô°<ě'…G¤-źËw'Ď”Ő+˙68 ­Ďş¤ÉWNs#0ŔŢiuLĚăC˘§ůčJȨý{îy)*ń7î Č ź©‘±fŽëźőy˙S«GŚ?;o’śĂäÖ)”Í™CĚĽÓg/ü°ă‡˘ě”Áv 6.n1±Ţ ŢX_[1=b%O<ýŢžň«‡ť>|NfNΤn^>..ý’ÝCžç?¨“ ÷Ź›óŔ+ŚIkfơuń»mG©V9{üő«Sżn˙[·Gŕ:pZYŁéon8zfRbzŐjB¨ÜxlÓK/<3,:€‚'ž|ç@ÚáĎ9„ŕąţ‰MĎ>óôś)Iň—˝šP±3gLIśz÷ Ď?;{bbĎĘ^Íý›>-Ę:&{5G2 ?}f nÍ«i%÷ őň Ţň剌cßzű…„úÜhžŞV ?)Í9ěf?eĺó:cˆ{Ćŕ»Ďpb­óCěU˘GŚ}hŐ; nćÓM-Mk¦Çâµĺć Ź:&KĘŔs9•o­_‚ľ­eűxŽX÷ƶśs?š-dĎşlm[ěŐśbçw*±‹›‘“-[?wwopäv!S5€ŕ1÷Tkž]ž &>щł˙ ©“Fť¸Ţ$I+’üĎ_/&řŮđž#jšŤZ7¨Íżľ;nhi9őý‡*«ÎZË€cŹŘŁă†ą©Uč<ŢäHĽJ3®>R°ś’Qđđśˇîľ÷i‰AÓW=ZU’9Ô–>¦¸Q˙ďM«ĺŹű. 9{}ŕč%íÜ:ÜP śÝfĂ}ÝܨÍi“…Ź‹ GĐÂ?ćŐ »{°ü2)n‘m|6Šâý‚Ď©×ieě±ĂFŘ!Óîo‘ŚÝIŮěŐŘÄ$÷\÷©/ş18a¤ »â]}›&Ľ°nz߇"lí\=<|ű˶â¬ă"ďz°¦¦lé¤0˙ÁăÎćŐťŰńĐăć˝lö˝Â,çĚD1wá‚Č0?˙ź€6 ćŔ= ňá źyr]T°§üµĄUź3y€€Á3gN5—L@ĺbžSbBŘúś9wö” ž{áŮ»†EŹJ|ôĹ÷JŠKŢ|úˇ+ďäďf©U„R‘Ă{˛}«cel»pőo(SyK}ŠŢ.ÄÖ>BeŞ6Z$”k.Ţ{µäd#QvŚG×úF €ÂAˇĎŐę*Ą–ěć–É!Đ`ŕ $Ż˙9HĹ JÎi˘«KĽ#ÓšZ*Ś$##^öŢÓĘż,71Guä›ü繸Śu yÔ—SY"j9l§TKÚŚF„ÂTÖR©•ćŁQ+@’Zruúj“ăpWCF]}…ä–ŕ$TµĐ`çŘ·ĂíÜÉĐšO©“]á öFłüU›—b‰]¨5č«—GSˇ¶şŢóÝÄf` ”X>.1ÖŤ ˘­ŹŤťżZl4*Ť˛ÚôŠ˝WŽ1FhÓµś»Ť˝mÇ#©ÖKŽ- ĄłÜűIq…˘cH"ôZŐ‘ZerĽjM\ü ±~j…|¦ÉčEaIÜ­ăGxb%pŠA㼠ú‚¦fFQâě=î őü65­–S9Ľ3yâĂüÇú=›cĎYÉ€­łŰ^şZS ŕ÷éHé·3ţY}S«+mmť"ýáŐ0IĐ’u0idâ{űŽ+Tj9‘–AS—ť–ZT^=|ňÂâäeTňFAâF>tĎŘĎţţw `٤ÔÖeĂsŻţmăúŔblS";8©>űôŹĹÜk–K’~HxéFbŢßův$ŁAS^Tß$uš‚ňZ‘aâ]ămtů˙úËż$ź>púÂu’Ĭó§Č%„P0Ć{GíŘłwÓ깉ćüđĂwSš€S«8Ů%b"q ţtÇ®§;yÖâW·<ëhCÖef–¶ĘQ±c¦íŘóó¶Ź^_˛üż˝±ŮAó‡]|ŐZĆÚŻ"H’ĺĂânqőPŇL¨(Č­(ɯי¤/)/ÉË­¸ÉgY ‚`(­m:|řXťN˛wqzÖ:/Š&éçOýwë®f Ľ"ŻYw—FnúŞcňNä0/[cňĄ“˛<î‘$p.'ź˛ó‰lŻéŃua[D€–¬m%rBgë*[’ O{㇌ľˇ‘kŤKN 0qä8mY–*rćw»vD @MqćŃ“ŮŃQ±ńC«Ë‹ęęëkj*kµaÁ"›ë+Ë+ĘĚäĎSCźsÂŢŃÎbŐÓ­,8Jt=bŹô¤µzŽżm\p<‘ć>ĽEĆĺŕh/?[ç9č[t«ž˙çź˙Ó‡§“•ĂůwO3Ć ©Ę)*’©č»,äs‚NôhOi+ç=pá,JD†®(dÔε;Ë/g ¨-L?ŇĘçşĘ˘ÂŇş©Ł'6–fĘŘŁ˝Đ LŚ[—ý«µ”;Ř BűÂŤ.ÄöQ7 @]Mue]S›&”––ˇ÷G; €Ń ©®.mŞkdć;öMBCqqńŐ‡/ĺx đQČĄÝHö9źöŃűďňúŢţ·`ÚĐę⢲˘Ľ6 ™›SÂűFďŘॠ+–>ňÂOűu0dÜ]mVýŤ7_•4uçîź¶}ôú’kţňęF— m?ě~főÜÄ svďţqt°ŁGřČovîůđíŻ|dçŹ;»¨‚b'=˛lŞ`2Ž^°úÉu÷GŘÂâ€RŞž~ţd—’´…čC+›Ŕú?…• ·MpŽ~72ćiC®¶6Ő(Ržt*moCu‹M´‹[śťŰtwN2€Ř¼ׅ,rN[s­üŠ()”śĎ*ßőŢ …¤-2çň\č©ĐčĘhPW‡0eő÷Ą—ĽvéŃlÉd‰¨Ôž<Ą´˘Ě!¶ĂÚhn’i–Dđ~Žž#l«öŐ€•żťKĘŘĚbŢ‹ômB;6N!č$ź>ĐÜť°ç5Ő‹ś 15ţkýUz]}z‹|ť¬- Ž<0šĎ74•KáďGĆ˝Ę*uu§[ ¦Bsź°÷Â1‚V"ź–W9bé#űíŤ rcŚőÓ ©©ox|ó+~ş«‡“sÔĘ›>¸íČĂŽŁ”š_.6>Ťš˘4­ü:źăZo X?B—K(GĚ[¸ę®ˇŢŰ>ţŕŁ>Đ:Ü´f±ąu‹ŹlĽccż˙äŐ©ăGMš±ĽŮ`~®Ö]Ëm`0™LŤUĎ>ýž' "éžž1HĚŰA¤Ű–űXbÉ/yéB8yź äćÜtÁ¤wö‰=xđçÔ_wÔĄţ~Ď˝ľl¶ÖyĘń *Ţzűő–ś#{~Npűž´Z3¤g“Ůě碢R¦ţNQŐg4-śBíġoö§#źĺ±S •ô––$ČA­3ő Ť7:r­q™ ’OXüÔh˙*­¸c˙ˇUłž4óŰ=‡ľ~ă™ŇÔÓGÓšŠĎKÎ×ţkßáŁ>JÓö~Aĺ7tËÓK^|≋ů:Îd:oÁu%‹^±Żś Ć8ŽĘă‚P* ’}ŔĐ×6-µÄE)Ő6ëúÓ{[ÖN_>~ôçgňT*Q9­ŮřÂk[6z*u˛ ĺöoD¬=Ű÷Z>1P×P—~ő‚™óżě‰őVéuŤ×ŻKŮëcĂ{ ţë—u PŇ_»Ô“ĺ—o[EŽź.óą<íě‘ěÇ{…ĆMŤöŻŇJßě=4'1¨ˇIŻiÔZKŮŇnPJ´M˝sŁ×=ĐžĄóížźWŢžvđ‡«%:YL%©?LzC̡Ţ@)/' $Ld ű{ź{cëw‡ž[đÝŢ˝&€r˝ĐHSs“V$h“Vg0šóĘ’§p÷ĽŐĂĽ…©IqCGLÖyDß?Ł(¶[őĄO0 FccŐĆĹs†D…'Í|d漦óůć?ísŠ˘Y#č6?8chüü*Îsâôa?űúčyĎř—µS’¦ě=S SÄHĘ)ş´äŐ‡:Ö¶·ʡsuɵÉKŻ\ţCr“÷ą±Öť|€0€€đ¤-†XőĘŇÍ‘ďF†.sę˘ c<µä@ ‚®ŢůĚ­Éţ`ę™iWŞóIč}ŕlë?×µęŰ ˝ÂA,Đ–}ßŕýđŔч††ÝďI»™D„fI’Qšµ€.ĄŤfÇ6šÝ¦ąq}剄ŁbmKćs2_Ě©+]9%r/Z (z¤yĄůšĺ^DGěNž‰vB˝  uôM˛Éűg± ś@;Çäcf·žŽţ$ý™WźĘ†·ťď\gčE¨¸~ŕyE&2±‘™űŐyđ™W0ťŠy9Ôbu]cůšźŹüńĚąŮ{Žh8ű‰~Â)˘Ü<8ŃŁo@)•ŚMöěMüú‡}şyp¨ť×†ěI»^"ŁĐ5T~‘YvĎĐqWď]řňHUëAP;SŁ(0¦¸Ń›(˙/G!JRł‘ˇ?Łć˝"ë% ŔĚ%‹l yßě=Ŕh4Á-â©Uóvľ˙ŹB(Ď5eý˛xîÜőOl5v~˝mŕô©#q#·‡{ÉŘâ1nýôČ?=÷‚†¨ÔÜMŢ´ą“íeYű+H-JµŁ#5ŹĚ¶ 9ĽBŃá.Ťą„#’Ŕ;0Ľ±¦fôҵ/l|¬67-˝¬LľmŽČg˛„B—yęŁO­zq[yYÁ[|Tm'J»ią˝D©TW•¦˙t%@i~Q`=ĐcţÉ%JʉŁĚ`FŃ®.J:BkđĆVMľŮ¸^”rĆ–ú?Ô4MŤeĹĄÍ@‚Xé<MFÁöÍ϶ω®|´R˝SŮ`Đ“ýĚz­A$…ęŽćę¶dÇŃdjŞďf–éúŁ|¶,‘;Őś}¸Í’hl§OKŇÖ×úFM¸á‘kѲŚJ©˛ŃW¦?ĽtŐ#«WÎĐL<@úÉťŁâbf.~ĘoŘ”E“'Í^9&Üö…łgÍ_ĆÇ/[2céęŤÜMj˙č‰Cm \6'‰@˛ÜiďZ˝aź?ŁśBˇŕ9Ž# Ŕę wŃ·ăšĎK-’Â~pü(^_—YS €ă•RCńŞńĂ|#we(^z•ŚđFeˇTٶӓ^{×Č(;µpŢ<™óuj˙Ył'‹Ç–Ě—K4ÚŚ3zŃĎE¸e Őî!+fŹŇgýĽ¨GË/řgśŘ.óŮ+~Ҳ Cőf¬ÎzxéŞGVŻ8žĄ™?ž6ÖzEZIą“ma"SÚőĘ ±W·®GéËn’8nÖŞQa6˛&ŔˇëVÍCf´¶g’$´)‰(ÁŐŮÝX›·~őâŢŮ@ęK(H€€2¶|Ö­ŰÎ8ŞÝjʲ k†ü éEî*ĽŇ¦ÝŞçP«ä’lI«©sőŘT[3úž¶9ĄÔÖŃV[[~5Ą&ťŢ$ŞÜ0ž2"‡ęíňÚéÚBÂrľčąN#ĺvÜô§WŇ"›…úµ&±ő·iS'Édľ~Ŕu¬KÜg±‘đ1'˙Ó˛źI˙uzĘŐ-…Ś}­ <•r¶äTWp_ "€$2&Č ĐLúJ“Ň[Ŕă.O51”¬…y"ň^Ď<=-%ëóZĎ{}=U°x‘b+`*20©ümÍléDs€ (^wą6­ÓŐ3&˝€Ö‰’ILödÔ±ÎzÁŔq¬'š=»@,±ŤÄ.@e¨7(\•_Őĺ0{?ĄQ'°iĂ@íeĂš ŐgµŤçëuŤ’ź `´gě}äČŘú¨! :M×ë]YŽcFIZ‡IŔżmsf˙sdŚÂ˘¦eÚúˇeüh;*ľręä&ĹÖ ‰ Ŕ A$#c€ľ°ąĹÓŢŔĚA¤éÓÜB™făë§Ź űzçóW çĹĆÎň±CW—)ë›LཝŰu˙˙ dv„Ú;RfĚŐK@?çÓ0 ’Ńd4IQA]W,˝űě÷;˛ëEN©dŔÂĄëiÁż¶íŔD‘P":3“Č$Ö/â’[)o¨tsrŮúÖs?]3 ń©l¬@nĉ»óí'6ŁŢ¨3•ËŮW\<#¦Ž)Ç+”JÁ#fţtňâço?Ł@yAm%ôŤÍĆĆüeÓ&ŚLJš8~즷·Kr¨łfŁD0‰˘ŮĽa^€_ŔcoěXúäó‹&‡Męľe@b“$Ab˛u•o˘uO €I¬Wđ6*H’Ч€ŕ3vŹ«‹’.ć.3÷DQl q+ßľą0·J9mMá[˙űŇĺOMxŕń{&Śăą5{ÔyAÖy†çţţţ˝]&Mš–śUA)í·Hĺý˘cňfbvľ‰Řů†ĹŕNĆ ĹC€AA‰¨É¬1č»ji[d«eY"bŔL˘$ăŚĺšJg;Ç›ąrËr<­Ú–zA4g]7µß1QÔjk~Ů˝łĘHC‚"é3ó‹yjo7řý¨Đ‡}†~7,i{¤Z(íFźá;ƆzÚŹMő¸—Ű\݉WG ysŕ˙ÄLHşĚŕ†~źů¨Â\ËĐ­±Á÷z†nw8ŢĹź‡Ő(˙ôţŕřÍţr‰÷˝Á#wµŃ<Ô#^ ŔqŞ÷¤Ô‘^ĂTćŻ<í‡>ěŤĐ°çĂ&^LôkgÝ bc—tĽGš—›ă¸/öMüh ||é˝Âű™aîQ 8¨‡˙”8ôő°¨·cĆý<Äއŕ0ľ§É^ă/ŤŠ~)(lóŔ‰—GřO´ÔŁOőÇ2čă!q›ýëx-˛Ŕ¸MŁFZ¸ oőňs¦­ ő4¨ö±G/̧˛Şshî] }<ď4ôÄ‚ů~ѢĚĺó˘ěyP—+«V®öwRŮą§ŻyđýÄÉá‘ĺkWo›2ţëYłJÖ¬|!ÚPě^ĽôÝ„0´ĆłwőŰ}÷ěŤ1^;!gĹ‚1Ž*k2e‰Ľ1cή±ńn‡ßp„űó¬ńżĎh˛ŐzsÖĽícăúŹ@ ›—ßů$95»I«9üІeł$,ŮČiĹř`Č ¨ď‰´ŠĎ^]@)ÇžzňÝkŽľ´éĄă׊ꊯƅ¸á–c<Č/D©OtJaő©o·nÝuVSzm°żýŤ¶|'Ű1G1Šť¶ý‡ź2 ** 2ţqÇôX?ŔńăC©šňśÍ›ž9|6ů™u $Ě_ÇË?ů• §7g­\"‡eń‹ššUŮrî»­Ź?úäÉ )%c™¶âŮƦú{v=zĎhűđ §.$żüäĂýx{cEöÔA »ű‘îZV„S˙řË5Ą—íx@NÖѱNgz(„ŚY\Ą“v}öĎż˙籥ćŢ)ćő„«‹’.v0ü ÷O[“+Sľăş®Ó ČŃÖż¸˝©ú’“ŢţúBKţYEĎÍuÖůä_ʬś2zöŇçc™—NőĺöÓÇ-ą+hŰ1˝ŤĐg]•W,řđŕĄ#oz'Ł}Ꮞ¤úřĄľa·˛-'=¶h€?ż÷yŠ™ó‡WLwĎŻ]O9a¶$%×űŰÁmĐĺâšąV¸N^?oś˘RËšĎn˙ä­ÉXă݉ˇó׼•“‘ĽĺĹ—÷ž¸ÎtewE{DÝő°ž±o˙űî[}%1ýӋưłUŘüĹĹĘäo»RN+YŇ#ö­Ě¤ąV:ŤĘqj%'ăŞJŮAb;¬´ľeÝŚµbŤ‰}ů—Gg®Ţ 1vpǶGĎýßž”ă(ű. ů%7ď7üJqŁ7Bć=řzzĘq™ó ĺׂśl–¬{űzň±NVÝV­)¬˝˛ŔĘ'ßK»Ř­ĺ7ÇN|ň_™id>KMewó„kܵŇ&37„Ć•“ĂŕzĄ¤¶MĘńAΆÎ}XćŹZnÍ.ľ¤N×7žXŚÖ@”7Ż’vqRPäÔ‡¬5ë¶e"ß {áłă9)?=˝éŐ˘:ă/˝ `đÝŹ‹BÓ˘x[*%ß«U“ÝÝč©÷ŐčLżlűä?ź˙Ä;ňíű^6™\™ňťĽČó‰šQToÚýĹżţńŮwBcŮ¡ˇ=ńJ›Uç t°ó€ŻĹśňkĘŐ‡Ť °ZßX1D (b*šĹ—ź@a|:Ł2íÜ/˙ŘňRR”ŻĄLĺŔ$‹ÖľĆËhµKgŽ0Ď<ŁŮr°řážę×™ĘëŤÇOďM‚7É qĽ]¸ťTŻË§ ú’Uű«ŤĄşÚ_ŚZQ—Ѭ+7BdĆR}íiMS®ÂOí’ŕd¸Vźůע–‰PÍ&íő&}©^“®çhNkÖ•Ť ‚±\_±ŁĽřGŤÇŇ €)ŞĚó Í0?­00ΙwNtR)Ą‚Ô¦¬3qČšôZôŻöTMK­ČLŚsŕÚiNn tR´d6VţŇ(1 Öd¬On¶é¤v"ďVÓ`&f,kďI×-ÍĺŰĘKöÖ0ŘD9:‡*Ę÷Ô1+ŽŐ\6Ŕ(Ô'7;ŚpRPSÎků yF™Ef\9z†üfmŢq°ŁĘ–~XTöK#x"5őÇ»xŹ÷ąćľ–§« éň<šř;9Öjë).Ż6ójkłšt’$UjŽ–”¦ÖkYÇ:UFcAmmzcł‡łSŚ›SumĹ˧/\jÔSBt¦–«5µ%ÚĆł5Ť¶2«kłµMŐzCeSăöô´łJfD_l˙Çăż–›ĚÂÉF­áăíI…ż_<¨şIľŐŘ>BPldO ‹M-Ę-h1É1×ď PBX‡g’łÍ×Ů…ŇďĚ«ˇ„HŔ ŻĐ-^=u&·ĹÄő#s 8°&/ußÁĂ šśKŻ”ůşČąrü«ťG ”2‰-xôŻO/ ^·zMi##’ÄŚó ‹ŹŹ¬ştôé 8źUÖiLŔ(%RcŐŃÉńS¦şŃňŤŹu1US_›r%Ą¬¦ć§ýű *שÓĆ”_=űĺ7ßUÔ6&CEEĹO÷_JË•/Ž‚ˇ˛˛ň§űSŇrAHcuîĎG.ú%$ŚIÍH>ľc׾ŞÚFJ“v>«¨FˇTUć¤^Lɲőô;a˘·J÷ĘźžŘs.€`ŇËíX·|)-W"c’`*ÎÍ:ńJëĺŻu:Ós-—Ô]?—šť4ÎŰ^|mółßěżŔ“±'\Ý”tĹ\@02.ź»p5„*•JËxĚ<Ďó4w,á9ŽAŚ9é—Nźż"1dg_©×Šó/Wj%•RAÍqňńw÷:_ź•|©°˛ľ¸43ĺjfŁVSVV’r)ą¨¬¦?Tşßt•pk˛S.¦• Š¨Î˝j¶6)ÉWóK<EŤ‹¨Ľtôé O]Č©BKí‘ăoxävŔUź—šz=óĘ‘)±“§»đŻ>żńŰ_.2Ńdëäg¬şü§MOě=—_ť{őbFţđQc|¸×_~öŁí‡$Q`2éł®^<%ł T–˛Ř·5·˛‰r\`XXwŘ·<żń‹Ý§­ĆŽ9c¬® €µh«’““ ó2O^ηS°´ )éEUµ5ĺEŮźľ÷Ö_î$=vů Č‚1J‰ŘPzôDrĚ”©!fn$Žw młáWŠŞá,KÎg•J-¸‘|ţr†Q`Á=Y~ĆĐŇŇbëěg¬şĽů™ ?ţš‡–ŠĂÇ/ĘŘ·<˙ôWű/2cý‘cíR>•^@0µŰCŮ’47U§tĎŤ÷żŘ-ö%}gOşÁmyîém?§Tç]˝QĐIzĘxKc¨)¬töťyjĎçzýÝz­AŤ5%ą'Nť­m–zĎä J“Ľ ť››ź_ËŃÜí?ááŘ/ż4JŤ†ôËç.\Í$„h«łŹśK5~’ź˝đüĆő»NĄL¬:ë`祤±ĘbNI9ľc羪z­¦Ľŕôą Í’ĐÜTóë‰ă%ő’QsćLšČĄŠK˝|ˇ´FKiĎ0Ď TÚä¦^JÍ2[Č”äÂŇjQ0šg´ëąJi›ßCłL©ĺz#űRrzaY_óŚőĹät]v‹ŤwŮśĆyŘŮË4ňő&«š=`§dŔt÷–ŚmžU>Íî1ö•TÚí!.á™ű2żĐ»ě.¬Î”şl} ç6qLNeďŕËWŇÜÖ9Î’Ě.ç{™‹#Â*t_ĺ–1Ů!˝ˇuˇłC‹j*®jš)Áąˇ"“fDy.ÔmĘî#Ć~dýć$ˇîCUßç•Üî'Ěm‚ ĺxĘL‚4zöę(ÇŇ˙~}R ë§é}ÍĘ×7ô–ĽżyIü¦íůfÍŤ'˶.DŽ.*t—ĽoÖ†´ľšŁ„ô}Hv+!„ôWb{ůÂ:“$PŽI˘}ÔôĂ;?pĄ-&f·Ťµ%}·.aŚ(l¤ş‡Lü9C§ŕ9‰QÔcíţÜŰĆ`”éX‡ŁDĽcĺFˇď:F•‹–Ţ“ůXňőâţt=SG)“¤„‘Ă‚]żŮ±˙Ö’÷ň-!` Ä"dEŤÜ.P[Ť7ó«J ‰"ë™ÜžeŃ+öV€..ý“_ZG©xs˛čŤžN]h‹Ł!ďŮËܰîfź”°µŹ°›™ß')wÇŤţÓŤÎËŤ›PqB)Ŕd:ĺË­NkO §¸Ťź˙Řáźš~ĄY”Y*+ˇün‡¶ć\îČÂ>uľĎ[3·ľô˙]€ď’‰ L2˝1{i<“sŰp­÷Ć B HÇš–s©Ą@Úőµ—U€–.h†9=];RBĚ˙drĐĄeŃŮΡ„0BÄś§đesýĎ… }–~÷ł¦§8Ö#am“:G)#„p„0ĆÄVĄç)[wĚg[‰’÷¤;±‡ŤĘáűZý{XHŚ#“ď[ÝŹ1Ňú„ôlGF… \ĺëôŘÉ‹Âďx÷?a„*Ľ`2IŚńëśŐţÎÖ7r~ŐŁm‘_=1ĆúË’p;wň‡ŇzŽńŽěD¶aóđçjóyfÁoąjč d§+)$âĎ1~÷ďýĄřÖ6K­;ĆÔĽÝr3˛ .^~ˇľL0PJXyA^IŤ†ç9Qˇ±1¨.-,Ż ŽIÚT_rôř©ßxF˘PŤ?É79rLoĽÉ–ďT;` •KTtAĺ!ĺ¤]«7šŔ?jxLYÖůS2EÖ'ˇB9ŽId˘$rŠk{ŔÝ–ä];˙ÍŹ‡eÉËóƤ^߲n; đ ôĺi¬(Í)*íő «ü•Ëß@Oű«©™hPt¬TWTT^ß÷˛?(*ÚŃV!ŠŔ8Naj©M˝ž{C~ÄÍő«íăvťOĎoPčá ÍŐEGOž3 wÔôEWĺ5´ÂČáÝ˙ű÷ĆŰ_ă8zg\/y;yÂ’Gßyböü‰3 Ś}>›íĚgP…cTTZ…QWž–^$Jđ 9"ˇ©ŞđčÉóFA”ëŘ(oĐ•_»^$őměôF aŚ™qU=Ń®´"¦pЉUńf »ÄŢť,:ô”@b4bġ!Ţ×ÎťąšWÔĺćşÜ´_D¬K‹&µ°(ÜWßż0ĺđ®+ą5 …B4™x§‘aJ Ł®1#+Ç$B’Ä—Rĺ1qę8˘;qčp•Ţ(3Äf€W@W4§ť?s%·ČĹË?,ŔGjť * ňŠkę)!_DŚ‹®>µ¨ÄꤱźÍŁI®ĺë˘Ó¤SĄStd¨ş[)€Ţ}ő=qĂ`ęŰ·ŇCźÖ6YpĂ17=K荣Ň!őZ†ČhPt¬T_\TV§T»Ś›0Öņťżp¶°¤úFťOŽ~`Dd«…ĺ8ŃĐxíZ¦‰Qžçcb«+B(•ďęSBäé ‹’Ö9E%I’@<}Č:DI"”RPާ™ÉĘŰo•iO˛/uş–E˙§đ_<ň—„¤ťŃv¶ďc÷UÜCGK{)°s¬x9ĹÍ(ʤŁĂGí‰{rxüAJj»¤cĂý§Ř¶¸Ěösrř¨ÝqcŽ&&|<Č΋Ě)VNŁ'řN˛o+éIĘĽ7„%m‹4×ĺţëZiVw“ˇ4äůAcO$ŚţeŘŕ?rń°‰ý|ČCC“%Śř,Ü>Đ>~ç°Q[QŔi’×ř«ŁBf:;ŤősfD'šx,÷ůe„ěJpAÎĂ$$íŹ}8qřgá %ź äĚř[éç—žcĎŚőcÜŘĂă˙Ú“˙T«$„-pé’źřçFOÎşoéĺ{—|1f}Ű˙~0adÖý+..[|uń¬ …ťóöů R—/şzßŇíăđ®É+ď}(Ř€’ŁĆŚĽ¶ęŢ ÷,H]ąěçY#ě”hÍeéěä}nĹŇŐAřŢŞČź,O›˛`Ş­y·äA>+&ţĚĽÉJŕ÷-@ÎĺAxÇCË–ľáŹŮ:K9€IDAT~Ë*ç ń|ę•fWÔ¦žuTXöŘŰŐ uůą9Y™YMMš-ŹÝ€W*¸†OÎŻŃ<0-rŔČ…eőMůąąµšĆcßľăĂő ×äS»×>Ř]_WSZ^şűż9ÜLĂw®ů™cřř{˛Š«Š‹ ‹«‹sîť `ц-e5 ŮYZMÍ«g ÷LŐmÇ"ňźŻö}őýŹ3K˛ŽĄĚGŇ7ňűÝŢŐ4i˛Ó3«ë«÷}řşwwVłăW«źţ¸0ăšPýt˝ŕ?›î@ů›ÎüÓÚ)ŰCÉ9ĺ%…EĹĹ……y…eą—G;ŞŞčř–¦{ŕU*€•Oüł˝_ĽîMŽďÔH§§Ţč¨ó1޶¦.__×Ô’—™Y[ßx䫿{÷Ź>÷Ť%}ÓU94ęÝZ”rĐ®»ř«·‹D €ŘL)¬}óˇąh}@ÜóGťřmoŔ>bƵÂꢂĽ’ĘÚ´_w°t]}ł™óGżyw 4#­Č\çęńomű#~ Çń&/_×&ek\©'wŞUřŚk˝aď ”oďi”ťśŹżúľ¦IWPTR–~f]iÔͱF˘'¤U´\řţ ¶C kźX8€J©0eĺ㕚¦‚쬊ęÚ‹Gľ‹ pŔŮ…÷]˛ř>?üšZU^TTZ™~ö@´ź-ŕţô–÷şŁą2ű\p÷#oÔ6Öwš Úh>˙ÝűćŘÝóŮ<šßŢÓ>¤€zĐĚ6M°ćłL3gWPÝ#7üť­ąÚłĆ8غ뾢ęFóüµý] žřs+7l•˝5Ű•…L/úřÉĺPz:MS]ž‘ť_[|í®p[\źŁÔtk!/Š´ë@U_ÇÇzÖ_ÉJ¨ö{čС‰!6ÖďV¦ňęKkYô…Ď7\¨ë°­±±oJÚ3ŘŢžđZ2ţDĽť'µ‰rwv„ď{Ŕ"Ú˛RÂÇĆ5Ń€M˘×„‹#}’lőđÝC}ÇŮpź9Ŕ5Zĺ:ÇkĚOqj˘ tHúuDř*TI<1úŰH…8ÜŰĆwąOŕj÷]‰ P%ěI[bŽHĆ…µŇĽ{°˝ŤŐü,ű]IƧŚđŽU»Ďóź’5ĘsŇ~’×Äó‰Îž”¸;Śů51ř>ßĎâ&ťć6Xí˙lÄÔ¬QÁ3ťť' űK|'š¸-ňMř 5ţ[Ë=qCÔś“BĺÄw.q´ŠzAŔ6ĆŃgŽŘŹń~¸÷HuqLŤď“G~9ČZ;dŤ ńX°fĺ˛ÓĹiÖ­ş×ß+cYŃýĽ‚ŇV/Ř=(ç•+ś‡#KÝŕ`zŘŔ ö“Â#ҖϏ剻“gĘꕆV‡g]Ňä+ §ą`ď´:&ćń!ŃÓ|t%d9H«ý{îy)*·Më†G!¦FĆ9î÷MöUWŹvŢ$9÷­S(›3‡y§Ď^řaÇEŮ)íl\Ü"bbĽ=<Ľ±ľ¶bzÄJžxú˝=ĺW9N‘Ó&Ť:q˝Ţh|dVZ'ű›9Lřôu’áţasx…1iÍĚnůηŁT«ś˝ţúŐ©_·˙-ČŰŤ#p8­¬Ńôż77H=3)1 ˝j5!TîÁ<¶éĄž @ÁOľs íđçÂđ \˙ĦgźyzΔ$ůËZ•gßű7}Ršs*ŘÍ~ĘĘçuĆ– ÷ŚŔwy¦ő«O‹˛ŽÉsö‘ĚÂOźY[ójZ{Ęű…†zůoůňDƱo˝ýBB}n4OŐÍőËZç‡Ř«DŹűĐŞ%v@Ü̧›ZšÖLŹĹ-ës_ Ź:&Żu  <—SůÖú%č“_ŃźŔsŔş7¶ĺśűŃl!{ÖekŰbŻ`17#'{aŇ@7/?77‘ĂŰ9ßlhąB|¦ääçĘu|\\úĄźr°žqůşąPšÓFa—Ř­eŔŃRŁÔ‚ÇÜS­mxvy€č¸anj:T9–šň_ß7´´śúá#›Á)Ď ŕî{–4}ŐŁU%™CmůácŠő˙Ţ´Zţ¸ď˛Ă¸OXµQŻ«źŕgĂ{ލiž˝|&_¸ŇÍ1ń ®Jçči9DzTfšż˙PŐ‘k>7µ4­™>ÚüŐŹó€jĐÝ™99ÝńŮěŐŘÄ$÷\÷©«9N Ŕ#(jjëüeÄ5“Bp×ů Éć:¶]¸ ťG„•…<ž[ň΋g-ßÜÔ\éď(/˝'ĆGözPM@ĺ¶`ÁđůFŠ4É+SKN6…9)'ď Đçju•RKvsKŁčhY,O%Le-ő—ôv!¶ö*S­Á¤1˘Aňz0hđźT<Ŕ äś&şşÄ;2­©ĄÂ@22âeď=͡üËrqTGľ9Čž‹ËX×G}99ö[G×l§TKÚŚFą@*lĄYI:\Z“Â8 tŠ´Őđžď&ę$ű{±Ň`h”l}lěüŐbŁÉPc‚^l)7yĚóq ášňŚTIČ‚v˘YnąÍŻ#D­đ{Đ?t•—Ú•@¨E‰ ÇÚş ˙żşkŤeűęDŔXŁ[Ě7&IpŚ1@›®ĺÜměm :.ÉĺżGşĐ4Vh"Ë…čLb”“+FĚyVĺ!7f€gł®Qéě÷éÔ m ®*Ű’şŞěf!ż®¶Đ Ä9ąĄD/ KâFl?“(SŚ ç5ŔhĐ450Šgďqo¨ç·©iµ śĘáťÉä?Ö?čŮ„{®łĺ¶ÎnxéjM%Đ{¶ŕţóŰČ~:éň§VWÚÚ:E*(úĂ«a’ %ë`ŇČÄ÷öW¨Ôr5¦.;-µ¨Ľzřä…ĹÉ?ʨ卂ÄŚ|čž±źľő¶Đ–¦>™?4°¦˘¸ş¶čC,š ŔŘÁIő٧,ćXł\’ôCÂăHäFĽ‘;ŢŽd4hʋꛣNSP^+2LĽkĽŤ.˙_ů7€äÓN_¸@’uţó6BÁďµcĎŢM«ç&NóĂßMhN­âd—‰Ä1řÓ»žz`îäY‹_Ýň¬Ł X—™Yä¬.­Â† Jk›>V§“ě]ůńaĎ_µ_E$˧˝]÷˘ç’v`BEAnEI~˝ĆČ$}IyI^nĹMľäď¦_ÝV·ŇyQ4H?ęż[w5ĺyÍ:ÓM‘r3ĐW“t"‡yŮ“/ť”ĺqÇ p9ů”ťOÄ`{Eëěq|–Kaś­ë†ç^ýŰĆőţîv2.´sľIć7ëLGĚ_ש§ŚÚuÁçvĐľpهy§«9NPSpýHëüU]VP×l@ѡá‰ĂĚuX_ݞv°Lg«t$D1(<ÂŽšvlű6­¬č)ç}ôţ;<Ŕ‡ľ÷ź˙-6´ş¸¨¬(ŻÍBćć”đľŃ;öxiĂŠĄŹĽđÓţâC] w׎=?oűčő%ËxăÍ—D%MÝąű§m˝ľdĹšżĽşŃ%(bŰ»źY=7qœݻěč>ň›ť{>|űĹĹ+ŮůăŽÁ.Ş ŘIŹ,›*ŚŁ¬~rÝý¶°đÄş”©ĹhźS÷ĄŽ•,úÎç>E+ż©Ĺ !CU‹M´‹[śťŰtww^’™[Fß’C†9Gżó´Ź1·©öş@l¨Đ,ů¬ Yäś¶ćZůQR(9źUľ!ë˝ I[dÎ+äąĐSˇŃ•Đ ®6aĘęďK/?xíŇŁŮ’©Ăiµ'O)­üŘȒ掹c “Vô_ëŻŇëęÓô G®%MÓT!†ż˙~«ÔŐťnV:’ę_5.S<:ť¦ĐČ©($FTĽ5Í@’š3št%&ÇéžC>ڰw§’^lÎ2— ţ(Âއ‡śŇĘňˇ0'Ç.V„= 7ÖśÓÆëŽ´á©Ę§Ű›%u㚡ń>BýÉšF5×n‹Úh4‰đttďb[cd˙˝ë®é¶Ů µ!ž~“=]'‡†Ů* ˘&“¸"~Äł‘>k÷î˙¶J«â8Â)ď<řąaŃśp©±InmAD¸łľţ‹śR*µsś«ýţŚ«Köî_xŕD‹D¬döJ%aŤF#ţş­Q¤”ó¶ďě»Ţ 0Q Rň´5ę ˇśČŕ2qĹäť[w‰€Š'–­]ëRsíŁŻ‚p’ČńÓżÝsčë7ž)O;»˙l Đ_™HM}Ăă›_ńÓ]=śśŁVŢôÉŔlGvĄÔĽu±ńiÔĄi™ź\kŽjë§Đr ĺ€y WÝ5Ô{ŰÇ|ôÁZÇ›Ö,6·nń‘ŤwLbLŕ÷źĽ:uü¨I3–7ĚĎŐşiŮ\&ôÎ>±ţśú뎺´Ăßď9@Ąžż’‘›·hHgš­{Ńs‰%żĚ+ŽůĘ,!7ç¦wׯžż˛ÖyĘń *Ţzűő–ś#{~NĐżĎX{„^tLfW°ź‹ŠJ šú;EUžhĐ´p µo‡ľŮźŽ|*éS/źOË«±ô‘ý‡ö re *N’9ŻĎ>˛çXʆ^M>×V'.ČŤ1Ö/§÷”S´Ią \żě‰őVéuŤ×Ż\č{—˛°î©É ů„ĹOŤöŻŇŠ;öZ9=Śq•ǡT$ű€ˇŻmZúâO\Ě×Ůp&@)Ő6ëúÓ{[ÖN_>~ôçgňT*Q9­ŮřÂk[6z*u˛ ĺöű. 9l`Ú‘ź.5˙kßáŁ>6•¤ţüS˛ś0·7š%©u.ŘőŐ÷FŔ)8ńµŤh¶^ptŕsî±]ű®¨ü‡nyzI‡žJúk—Î[óŮŇnPJ´M˝sŁ×=P«ńN:Ě_Ε‚ă:Öé3´mřKppä~ÜőŢŃ+šm{—ţŹ—ôí.+LÍMZ‘ MZťÁśĽ“ČRÎŤs÷ĽŐĂĽ…©IqCGLÖyDß?Ł(š«6.ž=$&|úŇ'ٱ±jăâ9C˘Â“f>2sŢÓ‡ů|óźö9EѬt ›ś14~~ç9qú°źż}}ôĽg LüËÚ© ISöž)Đ)b¤ĄL--dyőˇŽµíí_hß b¬}ę¬>XYzRůndč2gˇ^/ż+áÍěTčRj“—^ąü‡ĺ 'ď‰v¬E *Î~1şzäMĆôSĎL»RťOBďóg[˙ą®UßVčb¶ěű>44ě~OÚÍ$"4K’Ğ٦Ęaĺő„ͬŤB&Iäč›d“÷ĎbŁR sëéäGÓ˙yĺ©lxŰůĚt}ŞV›ŮT{\+ę!ĄĚ’ć•­zŰbQõ'32ß(H^ťiR)ÝĆ;˛˛Ć´§2ĺA­te±¤‡‰Ś‰4ěĎÝYęźňM%Śń´8f^Q‰Ll”–wf $‰žÎž«ýśţz!ĄśĘĽô"Ľ€  śˇĄaÓáSĎ?y´Ţ0ÓÇ÷döő˙iŢ»kÚć_M‹A9Â8>ĘÍŤ9úȱSŚMöěMüú‡}şyp¨ť×†ěI»^"ŁĐ5T~‘YvĎĐqWď]řňHcťX*Ó«1ŠcŠ˝‰ň˙p˘$5Ë ýŐ¨ŕJ’Ł”03—,˛5ä}ł÷ŁŃ·§VÍŰůţ?ŠDŮţ#ăÄöQq13?é?éţ…cFi?ÜŘ‘Ś->ăÖOŹüÓs/hJ}łůąîd;ćYŤµO°‚Ô˘T;:RóČ4ŻŹ áŠ÷šĚ%‘DŢáŤ55Ł—®}aăcµąiéeeňmCpD>“%şĚS}rhŐ‹ŰĘË ŢúăŁjs‚vÚ]Ëň/J9cKýźjšŤ¦Ć˛âŇf€0žż#L”(('JŚ2ói¸˘ëŻz,é­”Z5ůfăzuÝŻŢCÍĘ4´ë<MFÁöÍ϶ω®|´R˝SŮ`Đ“ýĚz­A$…ę¶ß‹ëŽeOD“©©ľëY¦›ŹĚ|–»Đś}xńÜąëźŘ0jěüzŰŔÓ’¦7ŮËś_°â‘@Č;˛pîś¶:Ó§ŽDkpŰ[삥”­qŐ©ýgÍž,[2^Ř»–…EOĺU©˛ŃW¦?ĽtŐ#«WÎĐL‰ €QNˇPđÇ`ő†?‡»čŐţŃ‡Ú ¸lfm$Ęqě™Ň®Wn˝z ťÇ;ŁçŻŐ Ç@4u¬Ó7°°‚ *•6FmŃüÉçÍYôʇ»î~äą'ľd™”­(iË˙Űşí,€ŁÚ­¦,»°hČż^ä®rŔ+mŞJÓş’ 4Ż€Z%—dHZMť«ďŔ¦ÚšŃ÷´Í)Ą¶Ž¶ÚÚň«)ĺ0éô&Qĺŕ€ń”&߉ěęh”tm!a9_ô\§‹‘r;nú[ T‘I&É<Ó4˛7]˙uzĘŐ-… ×¨cśă>‹ŤüŹyߎ#™Ř,Ôź¨5‰­ż Ŕ8ŽĺlÉ©®ŕżDIdL ™ô•&Ą· €Ç]žjb(9X óD&佞yzZJÖ絞÷úz$Ş`ů†­ŢŠ Śp*Űę$ÓĚĚó˘:ÖĹ’Bc“I᪨ř˘¨.‡9ř) Aĺm+5ŞĎjĎit ĚÁOEĆL¦kĄüPÇŮr’ŔŔ¤vš=»@HŰl'Zdą‘‹äJ¨c;p @ŕÓÁž üĄ‡ŇµFąűýĂ1‘°őQCt«ő.€Jc‹«ÚfçŐäcőR´ŁmŤÉ `€ď¶9ł˙92†g @˝h™ąC&ơ€éO‡üőŽ{O^@ [šjOÄWNť<ФŘ:!Q$’dd Đ6·xÚŰ2(€4}š[(ÓL`|ýô‘a_ď|ţJáĽŘŘY>včę2e]c“ Ľ·Łs»î˙´‚ĚŽP{GĘŚąz ¸Ů8ýÝ€IŚ&ŁI’‚ęşbéÝgżß‘]/rJ%.]L ţµm'I9^ÁQ«­ůe÷ÎJ?(l ĐičŢ0ČScyCĄ›“ËÖ·žűéšqHOec r#NÜťoGţ!őFťyµ~9űŠ‹gÄÔa`"ĺx…R xÄĚźN^üüíg€ĽQÔVB@ßŘllĚ_6mÂȤ¤‰ăÇnz{»$‡:k6J„I“(!šÍćř<öĆŽĄO>żhr€Đ¤î[†”ÓÖľő÷ż/]ţDĐ„žX>`!IłşűЇŔ$Ö+x$IĹS@đ»ĹŐUIs—™{˘(¶†p•oßÜ[íŘŻÇď™0Žç:ÔěQçYçžűűű÷Nt™4iZrVĄ´OcoúŞcPťťo"vľa1¸“±dń`PĐ@"j2k č6Á[`i[ä°KI”ó&Řü®%ç kŻÓiÁ|k]`č$ek\­×işĹŢ˝,äžĘěj[ęQć3‰’ü€*¨uěđhC}Áµśš‡Ö?éŔ;/ś5…0Ń–3nZ{ß÷ĹŽ»żxGčIŚFAŞó‹+<ýU"ú, Y‘Bü#ŚÚ‚]żś9x_Fťqhh°üŻB÷4·ú:äą ˝FřşÚ|KšĚžĚËÜí†Ďç®—´®&Żý+çŕł§(ů4@泼î1ĂŇjIŚŘq†ŤkWöŔ ^–H”ÄR;Í_ňüŐĘ Sie”F ©ćąÁH ďČ“‡ľűŇĆă׋BĽýH˝Ń'1I©´q˛D5O`2™ďéČRŢM©ÁŢŃÝ‘Ä9ŘÇ] "0Q<ńĘ> “$s Ąč<§Śyîý’Â^Śr`\IEfŢS)•v޶čęFźőŘáy^’X«ťß¤€ŮFt_Ç<XËâ6O7…&î:1áÓ(ßIŽ€zđ‡Qˇű ýnXŇ·‘6€ĂxĎÉyă“>({eŢ+‚GîŠ yŘođ§C&žć­lGźá;ƆzÚŹMő¸—Ű\݉WG ysŕ˙ÄLHşĚŕ†~źů¨Â\ËĐ­±Á÷z†nw8ŢĹź‡Ő(˙ôţŕřÍţćß–4é7Ý€ĂD3…ňq*ń°M<0ôő°¨·cĆŽł±‡ýXŻń—GEż¶yŕÄ‹‰sĽGěM¸KQ@bw ‹\éî·Ůčü0M6LÔÖůвĄ»'Źý×ÔYŮ÷Ţă 08pPícŹ^3N~ÍÄŰ»Y~ďľÉŁ_=¶bÍňEľ N_ÍĽë…ˇŃ?.^’Ľ`š/p®Wî[±ÚßIeçžľćÁ÷#&‡G–Ż]˝mĘřŻgÍ*Yłň…h@±{ńŇwÂĐ[ĚŢŐo÷Ýł7Ć za서 Ć8ެČ4{ŘoĚłkl<€ŰáÄ÷ˇ¦EĆţyňBʆfÉX¦­x¶±©ţŔž]ŹŢ3Ú>|© É/?ůđ_?ŢŢX‘=uĐĂî~¤»–íxX˙âö¦ęKNjxűë †â‹@Ô¬µÝŇC dĚâ*ť´ëłţý?ß-5÷N‰0Ż[\]—t±Aä·îÚš\™ň×uť^@~­Ű©_-ůgý=7×Yç“=˛rĘčŮKźcŚe^:ýŐ—ŰO?´ä®D mÇô6BźuU^±ŕĂ—Ž|Ľ蝌 Żš¸ŹŽ¤úřĄľa·ćóá“ÇÝóŔk×SNĽ´éĄă׊ĘÓ‚]”łVżÂ˸tú«/·ź˙őđ´„čůkţ–žr\®SW|5.Ä 78r­A®°hík=ŕj(żädłdÝŰדŹőÝJ„t°˘'Żź7NQ©eÍg·ňÖż·2“ćţYqč4v@(Ç©•€Í_\¬JŮAb;¬´ľeÝŚµbŤ‰}ů—Gg®Ţ 1vpǶGĎýßž”ă(ű. Ůaą`CłÄľýď»o}ôc†§Ž°ĺ˝ĎSzŁyř’§ŰçJm§ą2ůŰNךĎmŁÉÎF!U{e€•Oľ—vńhź‡ô0´ŐŽ©Ľ]|Iť®'n<±˝ś„t©‡ă—>đ·ěŚyţ’´%óGÜ–~Ö6>¶xzĎ-Ëďa‚G·[HfŇĚŠsżâÉŞňü7^ÚôŃÖ}˘ Ý°` ®‡v8@ôÔűjt¦_¶}ňźĎbŚůö}/›żL®LůN^äůDÍ(Ş7íţâ_˙řě;ˇ±láĐP‹žxĄ¦ô˛läy…ŔüÇ_n-ˇ|-ć”_S®>´hdŔ„ŐúĆŠ!j@SŃ,ľôř| űŕÓ•iç~ůÇ–—’˘|-uľK™.ť9 @Â<óŚfËŔâ‡{Ş#Ż3ˇě°Ţč•Ď7<ŇeˇŰ¨ŤĄşÚ_ŚZ±%łYWaPúŰ8s2\«Ďü[ˇ®NŔf,ÓמÖhső$Ä9đvávR˝.˙ť‚šËđšMÚëMúR˝&]Ď)ĐśÖ¬+5cąľbGyńŹŹĄAST™/ćša~Za`ś3ďśč¤RJ˙*¨M3tťÓ“AŻeAŹřjOV·ÔJ Ä’f]Fł®Ü‘Kőµ§5Mąz‚f“&ĄŮa„“‚šr^Í×– ĆâfmľŢq°ŁĘ–~XTv¨AĆˍĆ ’AlľŢ¤+7kŚĆ:ÁXˇ/ßV^˛·”€Á&ĘŃ9TQľ§ŽL„ŇWíakĘhČyٰ©ÂE{IAŁ|cęŔ1ĘS]N“6G/6‹†*Ł6ŁŮP#HzA›vËěâ=ŢçšűZž®N$V ¤„H&ýŻ5±ľ>.¤ĺ•SçÎktI¬ljĚ*™=|]°ýŹ˙Zn2 S$µj„Ź·'ţ~ńüˇę&ůÎ`)€b#{jXljQnA‹IŽą~g€ÂŔB<<“śmľÎ.”~g^ %Dy…nđę©3ą-&®™CHĐŔ5y©ű®hĐä\şx˝ Ě×}@Εă_írŃ/!aLBlFňń»öUŐ6R‚ś´óYE5 ĄŞ2'őbJ–­§ďŘ ˝UşWţôÄžsą“^nÇşĺKiąÉdĚ͸túü‰!;űJ}SKnÚ©ňş–úšę÷uAϵ\R_tý\jvBŇ8o{ńµÍĎ~ł˙LĆžpuSŇsÁdȸ|îÂŐLŞT*-ă1ó<Ďw Đܱ„ç8^Q0ć¤[ôKg(Îż\©•TJ5$§ŘëAçëł’/VÖ—f¦\ÍlÔjĘĘJR.%•ŐôCfžţÓUÂńŚIůő¦Í/?›˛wknĄ–»Swä(ĺ$ƢǬřÇÓóź]÷Hfe#×ÇśîťlKJňŐüĎA‘Că"*/}zĂ.ÖŘŰ:äç^˝”šeć|ĘĹśâjźA‘qqU—Ž>˝áçłĘn]` JĄMAnj¸®U1Âą…ĆÇGö€˝,ömÍ­l˘f>/5őzć•#'Rb'O vá·<żń‹Ý§­Ća­ÉçM&}ÖŐ‹çŻd¬E[•śś\—yňrľť‚Ą]HI/(ŞŞ­)/Ęţô˝·>řrŻ ±č±Ëű. ĆJŇ/¦d 5ĆÇűŰ+Ď~´ý‰S††VwOóÖݧŕăę‘sőÄW;ŹŔŔëHsfď|ľ”\TVĂ$ĆL&}ÖŐäó—3ŚókăóąŚXر”VKŇÜTťŇ=7Ţ˙b·ČúĄ¦ó—|93Ça€{Ě!ćůëdxe`hh[ťěKÉé…e]¦˛äj}qz»…üÓw˝nhÖŠśíŕ„QNśćo/ýéż»Ž°ăJ“Ľ ť››ź_ËŃÜí?ááŘ/ż4JŤ†ôËç.\Í$„h«łŹśK5~’ź˝đüĆő»NĄL¦âܬóŻČÖ‚1& m%ŚRŇXe1§¤ß±s_U˝VS^púÜ…fIhnŞůőÄń’Šzɨ9s&MäRĹĄ^ľPZŁ%Äl^ş;É…ĄŐ˘`4Ďh×s%•Ň6ż‡:f™RKYôĘç›éý–+i«Uu[ˇÓ8;;cŮ ߪ÷đ­ ” îŢ’Ń Í3ö)a§:}ů¤Ëf8ÂDćľĚ/ô.» «3»ĘŽv#˝č ©Ýq¬¶@b¶Qöľ|Ő! 뎾عV!w5ÍP@F„ TčľĘ-c˛CzCÓˇłC‹j*®jš)Áąˇ"“fDy.ÔmĘî#Ć~dýć$ˇîCUßç•Ü©'Ě„pe‚(aĚěŐ‘ŽĄ˙ýúmM˙Ýiďˇ˙Ö9§ŰÍ6ü›¶Cä»67±«aEä,7–ą˘;a껵‘ź0H}|ĆB é(é›5ŁÝQ"I “Dű¨é‡w~čÎëMĚś–\ŢĚ’:¤§%Ś…ŤT÷đ‚‰?gč8J$P&‰ę€±‡vîmc0J„XŐďAąQ軎Q墥÷ä_>–|˝¸Ż©0oť:J™$E$ŚěúÍŽýý¸/]¸cÝěWOŘ{–Eg)’Ö2€Rb΢H|+Źušai×ćŁTĽ)YÖ§MmÔt™ ŰŠć.v„J)EéF,iMĂÝGÎwř¶nôĎ´Ó*‚›ú´Ă:D)e=^f•SÜĆĎěđ‡OM żŇ,ʬ ”ʱĹX›µdRϸşë\ß–`ý6¨[0ÇË"”0‘Aľă$1&µę%Lž˙GÔ>(%„d’“›v~ęI(łÜ łś-"D›±·ďžVV!ĹĚ%ňÖ±üô#`x©Yę„ C/`î˛e§g^€ËŤČŤ3‘Éä9Oŕ;Ęćúź ™ůř™1 íü±ţVôPBh{Ż!¶â’Z˙Ä:aď˘ď=p¬Gµlłm˘ „p„0ĆÄVÁđ”Čk9BÄÖĘí/{­¤˘Ĺ@ł¬l)d´vTľo!ö`Ľ:ZŢ;2sF… \ĺëôŘÉ‹ÂďxßÂ8ž—u“"‰˘(1Ę9”Ľ¸d­ă¨í)61/>€rłĘF| =$”š±SŽš7ßíB9Ž“%CQ%Ć8Ž“¤ĘNĺx’Ôú¤‹J9&JÇńډ–"ĎóŚI˘ÄxJEI”';9ůt_ZnĘóˇ!žă\m>Ď,ř-W ]ět%…Dü9Ćďţ˝ż÷ăfi—ÜB5zü${Îpôč1˝Qâ8ž& "#40&5e…ĺ5~Áa#†'4U=qÎ(ôĎÚ°;đ†#GŽéŤ7ŮňoŰŽĽő?jxLYÖůS2EÖµĐ8Ž“n˘$š}ŽId˘$rŠk{ŔÝ–ä];˙ÍŹ‡oBň˛n; đ ôĺi¬(Í)*íu- ĺ6Ŕ'(Đź'BANZeťľßnd~PT´Ł­B%€qśÂÔR“–^âăç§Tr‚ˇą°¸ě«Xëüo‚˝“×Đ ˙!‡w˙ďßWl;|Ťăčťq˝äíä K}ç‰Ůó'Î(0ŢÂů‰|cs€W€»Cnz¶ĐvĹđ‹uŃŐ§•PŞ<&Nç@šOü|¤Ę`ěGsg‰ëZQ Ł\Pd\BTXS}ÉŃăg &‰HŚFŚ34ÄűÚą3WóЬ7Ĺ»“…z€W Eż:ˇĄ Ǩ¨0µ Ł®üÚő"‰€Y~Ex÷Ő,L9ĽëJnŤBˇM&Ţi@dX ’¨kĚČĘ1‰$±dqł ü GĘÍLcĚ7(tÄđ„ćę˘c§ÎL˘‹—_X Ż$r¦°ňüě’z1vp„Šgť¤Ó3Bĺ2~â8éü…ł…%Ő|˝\ȵ뫇ĎŔ°€«.´ôÁIn#—Ł„Ůj!A9N44^»–ib”çegÉěŠJĺ»ú”Ć$Q”ş(iťSDQ’$ „Č/`ĺ*Q’Ą”ă©ÄD&A˛ÚŁěÄĂŁ'»ńűRG®Ćş)ý4ä…AcO$ŚţeŘŕş~łĂ)ü׏ü%!ig´ť @¬KlěFî?ĹU.ł˝Ćś>jwÜŁ‰ ˛óâóˇ„"ĐiÔáßIöm%=I™€÷†°¤m‘ćşwđŃLžÇr˙‘_Fü&5{Ł V'H[ŕ‚®řI€nôä¬ű–^ľwÉc†Ů[|Ú^ŤŘ¬6âĚŠĄÉ §‡ó­))}\ĽN¬XńÓÔ‘@x·ä•Ë v ä(€±#Ż­ş÷Â= RW.űyÖÄ;eۇÎNŢçV,]ä€ďíˇŠüÉňı) ¦ÚvEŢmyĎŠ‰?3o˛RfĆÄŢ+Čń ďxhŮŇ#üqł…Ę×ě^ű`w}]MiyéîţćČ@\Ă'Ôhî›<(~Ö}ŤzC^ffm}㑯ţîÝ·¨ü7ŤýţŤŰ‘řůŹţଦ!;#K«©ybő |ç|đVş,~ËďČyµď«ďśYÚuü+e{Ű7ň«úŢŐ4i˛Ó3«ë«÷}řş·ůśľ—ŻVnx·^[ź““_Qśů§őso†…ť;L𶇒sĘK ŠŠ‹ Š+ÓNíZ2˙ˇĽŞĆ˘ÂĽňŞÚĽk§Ç  T(¸Á:÷gďuúIĘý}Ĺ.‡F}đŻ[‹RÚuőv‘HŰ)…µo>4­o´„ó|ę•fWÔ¦ž˛Uŕ8€_ô„´Š– ßŔśOÔŹż^«*/**­L?ł?Úßý”€ÜŰ#zBZEËĹ?>mUQMc~nn­¦ńŘöřń°áµkštE%eég†Řu‘ţĽ,(Pľsż,?‘ńÚF̸VX]TWRY{őÄv[@<źúó{ćŻěÔvńŐŤO,@ĄT˛ňńJMSAvVEuíĹ#ߟŕěÂoM7«żĺHąµkňňuuM-ňluâŰŘ ׿UÓXźź›“•™Ő¬ÓľĽf&$Ą—Ö™Ąsü[ŰޢÉ‚ó‰Üuüަş<#;_S~}¸?ţaIö%ŕ8řlveňž÷]ŰY×wa!ó Ër/Š´ë W}o‘tű hUNµÇŘC‡M ±•¶[óĐzĆďKťî,@ż í`›ä1ţŇHďXµű<˙)é#}ĆÚZ‰wËuŘÖŘŘ7%íloKđ–%6QŰ&~?Ôwś÷™\ŁU®sĽĆüç &Ę@‡¤_G„Żň@•@ŔłŁżŤTČa˝m|—ű®öqáĐ•H Už„°%nčÔż Łs[ä›đÁŔߣWÓ*,ß'Žür5kdŤ ńX°fĺ˛ÓĹiÖ­ş×ß+cYŃí]öŢ=űóÉ“’ď™ÉS<@˙2uzŃ+vOĄŔąü˛xŃ}n¦‡ śŕa?)<"můüXž¸;y¦¬^ů·ÁahuxÖ%Mľ˛pš€öN«cb=Íw@]ĹLíÜsĎKQA¸ĺ77 2+¦FĆ9î÷MÖúŐ#Ćźť7IÎar;(”㱄OP'î6çW“ÖĚŚ Rrž~oOEę/öŔŔıkWÝcÄÍ|ş©ĄiÍôX´Nö·ű µü۶#Onáw•5˙÷扣g&%FÁJ« büś{ž{á…‡V.ôtRP{„<đئ—^xfXt0O<ůδßߜç(ű'÷oú¤4çT°›ý”•ĎëŚ-î€ď>ňLëWźeźđóöÜřĆW˘^sϸ0ôKd0Âű…†zůoůňDƱo‚}ĽŘ™ů@uuŮŇIaţǝͫ;·ă ŕNűţŇ–ŰŠ]^ë@x.§ň­őKpGײŔsŔş7¶ĺśűŃl!ot‡€rbćť>{á‡?e§ ¶Uŕ)”˙úĄĺôž˙wß&®~‚ź ď9˘¦ŮřÂş9čbGŕ&@Ž]fĆuꇏ8Ŕ%0zꤑB'®7Šâý‚Ď©×iź]ž :n›Z…ŽĆÖZ­úeíŐŘ š“‘“˝0i ›—Ż›;‡ůí_Ů©(lc“3 ž3ŔÝ÷>0-1húŞG«J2‡ÚňÂÇ7ę˙˝iµÜć­Čâćŕ·)7„R‘ĂÇ>´j‰<[5뛜 Î&fČoŹoÔÔUM ˛AŕôěĽ\Y:>..˝j›<—lř{Ł&'Âß P.[±ĚW…ĺO}ZxmźkŔŔC镾 vz^¸@ĺ¶`Áó亨`OůëŔŘ™3¦$N˝ű…çźť3y€€Á3gN5—L@Ő6§Ä„°ő8sîě)3<÷Âłw ‹•řč‹ď•—ĽůôC+VŢ;Čß ZeÍCëżouş¶ýëŐPNaB‘¶şŢóÝÄf`+ Á"±¤BMňĘÔ’“ŤDi~#Y”´˝á $Ż˙9HĹ JÎi˘«KĽ#ÓšZ*Ś$##^öŢÓĘż,71Guä›ü繸Śu yÔ—S™›ę$r.ŘN©–´Ťm$Ýi ·Šă¶ŇFhÓµś»Ť˝-AÇ…Źü÷HךĆęMdŮ ťIŚrrŔ9‹©|ÚŘŇP:űÇ˝źW(('— wEĆŹw ßćVÚ+ĺä„#Ћ’¸[ÇŹđ$Ä Jŕ#‚Ćy 0ôMÍŚ˘ÄŮ{ÜęůmjZ-§rxgňćůŹőz6!ĆŢ{Ż{Ř:» ५5•@ďŮ‚űť‰ň˙nŰČ­‚?µşŇÖÖ)RAq›Ľ";8©>űôŹĹÜk–K’~HxQäF>tĎŘOŢ|« Č»tú?[w6ĺyÍ:ÓíĆ.ÝČđű­Ú‘ł¨ČŃ­ÇOgŁ+ř×_ţ ůôÓ®$Öv:#?úűĂ_?Ů˙Ő‡Ó&Lxń­­X0jżďöďŰ´znâ„9?üđÝ”Á&ŕÔŞÖ¨ŮÄ—5ö.č1˙`CimÓáĂÇęt’˝‹ŁÜXo_1& Őĺ•o=»éb…aZŇDŚĐ>cďPQ[Q’_Ż12I_RZP^ĄM¦†âââ«'_ĘńŕŁŔú-]IIů·Á.o’Gó˛5&_: ·+ŐC× p9ů”ťOÄ`{EëěqŔ$@KÖÁ¤‘‰ďí;®P©ĺ‚Äć>ĽejâóoNŘ;ب®¬¨Şk Ţ\_YZZôĂÓŽ'%.P_vôdNtTlüĐŔşĘ˘ÂŇş©Ł'6–fŞ"g~·kG¤'­ŐĐ)क,8Jtú%ZÔµ8}ĄŚłuÝđÜ«۸>ŔÝ€>Çâ+Éüę€ç oŃ­zţź?|ţOžLTcćß=u̦*§¨HníVdqsđŰŽ”›9@úů“˙ÝşKž­ššM„Ł[˛Ň®•WźĽ°8ĺÇ_ ZÔ¶*ظČŇ ŕ öć,ĘY€lŽ”*"Â#ě¨éŰoż-5@EM&•ç‡_ěôŻ;9uÖĘüZBiOWĺK‡Oűčýwy€}ď?˙[0mhuqQYQžŮB–—äć”đľŃ;öxiĂŠĄŹĽđÓţâC] w׎=?oűčő%ËxăÍ—D%MÝąű§m˝ľdĹšżĽşŃ%(bŰ»źY=7qœݻěč>ň›ť{>|űĹĹ+ŮůăŽÁ.Ş ŘIŹ,›*ŚŁ¬~rÝý¶°Č˝aÍCË_ÖđľŐ±¶7ü,ŞWs`SŁŕżÖ_Ą×Ő§·Č—ÇX§čd@y‹ŮNě\Bt’ĎÚ EÎik®•_1%…’óYĺ˛Ţ[ˇ´EćĽBž =]Ů ęj㦬ţľôň×.=š-É|°Rµ'O)­Řĺżţ?ňx´á©Ę§Ű›%u㚡ń>BýÉšFuëh93 €’RJ̆ŰÁŢíąÁÁoś9ąQPI(A“ ®ńl¤ĎÚ˝űż­ŇŞ8ŽpĘ{~nX´'\jl’[˙"§€Jíçjż?ăę’˝ű8Ń"™T·LM€˝RIXŁŃř[sô÷ ZŁH)çmßŮwío 5ő Źo~ĹOwőprŽZ%‡ËDzµk]j®}ôŐATAE`Ŕ[ożŢ’sdĎĎ©úéŮ_GěJţ˙+í0Ć<ŰąÚř4jŠŇ´Bą¶L>ć@”“Ô“ź|dŃ?ź¸wâÔ©qŁ'}ţĂą»ď{dĘŻmđŃhnZłX&Ŕz+Ç:´`7%ć2Á¤wö‰=xđçÔ_wÔĄţ~ĎňKßľä;]­ŻŐĽ=mH¬ďŘ»e©Ľb %­§1„ILaďsolýîĐs ľŰ»×PîŽíoô—¶Üě2»‚ý\TTjĐÔßYÚd`4-śBíÄáfí*%O‰ůĐĂ1pŘk›–ľřÄóujbŕ€ô#?_*lú׾ĂG|l*Iýů`2€[LŠJ(É>`h.Î$ż§Žp×·{}ýĆ3ĺigŹd7¨8Ţ+4nj´•Vܱ˙ĐĘéŃ`ŚëpnÖ…,,űe9[Ž*ęS/źOË«±ô‘ý‡öĆą™ô-í_É™ĺ(Ń6éúÓ{[ÖN_>~ôçgňT*Q9­ŮřÂk[6z*u˛ ĺÖn]7ÍËßt¤Ü PNŃy¶"ĽQ`.!WLŽŮąuŃ_»tÎR:ڱîŚČ/Ú~üî˝#W4Űö../ţâť—Ô@‹ŃŕłxLäµKW˛«ä1˝«.LÍMZ‘ MZťÁh~×,[HžŔÝóVó¦&Ĺ 1Yç}˙üYŚ˘hj¬Ú¸xöđéKź`ŚĆĆŞŤ‹ç ‰ OšůČĚyLćóÍÚçEłFĐ5l~pĆĐřůUśçÄéĂ~ţöőŃóž10ń/k§&$MŮ{¦@§‘]đĄ3H_ęt7RúMÖIT;ú&ŮäýłX% <étřo`ń˛ĄC cPPűAÄ čęMď4ÓL=3íJu> ˝ĎśmýçşV}[ˇ@8Ú˛ďĽ8úĐаű=i7‹"ˇY’$F”í¨˙:áŔD&6ʱA:˙«$‰žÎž«ýśţz!ĄśĘĽłBxJŰO',/ňQ‰*{ç1îöjÇůţ®%ĘGąyp˘1Gß9ŽŤ±iĂž˝‰_˙°OC7µóÚđ€=i×K$pş†Ę/2Ëî:îę˝ _©bćPQm Ó«1ŠcŠ;{Ăđ˙[ŔQ’Ôld¸ťŢ˝dlń‰·~zäźž{ACTj^€zĆ<µjŢÎ÷˙Q$‚ăyQ0Ű7?Ű>'BX¸ňŃJ ´ź˛gtĆ~łůąît;„đ EŰť.kQŞ©ydšý=só‘{h|BWyěÄ€čŠ2jëF7TWŹ^şö…ŤŹŐ榥—•É· Á0FĐşÖÚŽvÚ=”rĆ–ú?Ô4MŤeĹĄÍačĺ+ą„1@J4ŐĘNQô{·;¸­q-5™I ®ÎîĆÚĽő«żđÎ6ŇŤÇ8ş˝RţŤ°Ë~f˝Ö H’Bő›ě‹OD“©©ľëY¦ĎÍČ;»„ă€Őţî˘WűGOhç=höYËjóŔÂŮłć/#C×­š‡Öŕ¶7 éŚËfŔŔĄs’~|ç¨ř™‹źňŠź´lÂP˝«ł^şę‘Ő+gh¦ÄŹŔ,Î"ş–…EżZť Ö6 ä]ęćěĂ‹çÎ]˙ĆQcç×ŰNź:ŇÜ $ć b"‰Li78~ŻŻË¬©ŔńJ©ˇxŐřáAľ‘»2Ż?˝ĘÜŁ[—ĹMÁo;Rn ;ĎV f.YdkČűz÷!ÚŚC‹¬¤ÓÖɧőůWL1m΢W>Üu÷#Ď=1šQgj.O{đŮżM^űĚł EQ }ą<)Żľ@HŰ-ßÖmg Őn5eŮ…µ@Cţ…ô"w•^iSUšţÓ•ĄyÔ*ą$›@Ňję\}6ŐÖŚľ§mN)µu´ŐÖ–_M)‡I§7‰*wڧŚ0ůŽfW‡T]đҶ„ëCťFJ`Ôš.ŠŠ/Šęr˝źŇ¨ŘÄ:Ç}ůźKO‘I¦±Ě%0Žc9[rŞ+¸ÁŻ@äh&}ĄIé­ŕq—§šJÖÂ|6 佞yzZJÖ絞÷úz$Ş€Žw˝Š Śp*Ű~gÁ˙€¬¶>j‚NcµŢePilqUŰ켚|¬^Šv´­1 đÝ6gö?GĆ(,Ş Ś%ÉÄ€6ŁÁ°"rPŚł‚WÚĎ đ!ŚŮé•S'4)¶NHT ‚$ô…Í-žööf† Mźć`ŚŻź>2ěëťĎ_)ś;ËÇ]]¦¬kl2÷vt–»ő[óő÷2;Bí)3ćę%Üž›ňuźň†J7'—­o=÷Ó5ăßš¦‹—®¦˙Ú¶$‘Ďýýý{'şLš4-9«‚Rz‹›©Ý`÷©l¬Aëq˙ďłůşpĐ™?ťĽřůŰ›8&¸’}ĹĹ3bę00‘rĽB©l®ó ĎDU%µĽŤŁßŔ €1N ĄˇŮŘżlÚ„‘IIÇŹÝôövINTĐl”Ě>#eí¸žQ "ŐÔ´áâťĚš¬¦`‚¨m¬ůe÷ŽJ"j`PMC…ČĽ¨1™3˛t„îea$ŁÉh’$€H¬ó(S—Čí2©ýjĄů+y=Ägظvĺ÷ĹŽ»żxGčIŚFAŞó‹+<ýU"úI7ŔĂßt¤Ü4ČŰ2–łÇq&“ęşbéÝgżß‘]/‚rm)k:I§‡v8»{8đäÄáďľ´ńŘő˘o_Î$´ę>}ëĹżľüÂ?ţíc# bŻOG$&)•6Nv€¨ć L&óÍ&ŮB2"5Ř;ş;€8ű¸ Tľ»$ ’Ůé–}&IćJč-ç”1Ď˝PRŘK‚QŚ+‰˘ČĚ1µUJĄťŁ-şşíiÍCžç%‰µÍzňű„ëGÜ ĹHą"÷°I<0ôő°¨·cĆý<Äއŕ0ŢsrŢř¤ĎZßÇ+hđ¦ĐÄqăO'&|é7Ő@đŰJ˘|'9ęѧFřޱˇžöcSFG=îĺ6×kâŐQCŢ8ä?1’G„.s¸ˇß'D>ę€đr—ˇ[cďő Ý>îpĽ‹?«9Pţ5čýÁń›ý‹ë~wĚŃű&~ô{ŤdĐÇCâ6űťů#\jë|hŮŇÝ“Çţkę¬ě{ďŽqP8¨ö±G/Ě'żfĺ6Ť}hႼŐËĚ™ö@/žxjÂěk &€đn—ď[±ÚßIeçžľćÁ÷#&‡G–Ż]˝mĘřŻgÍ*Yłň…h@±{ńŇwÂĐ[ĚŢŐo÷Ýł7Ć za서 Ć8ެČ4÷~cĆś]căeşď$9BL‹ŚýyÖřßg´Ůj˝9kŢö±q·Ź@ůNő‰N)¬>őíÖ­»Î6”Ą öU^§®W~öę* •Ŕ˘µŻ1Ć2.ťţęËí§ŹZrW"nůą5vMéµÁţö¸Áčw¸ůmÜśµŚ±ü“_™Gq˙ŕ§«šňśÍ›ž9|6ů™u $Ě_סŢŰźŇPžűü†§~řéĚc÷MV{ŹČ«5žűnëăŹ>yňBʆfÉő¦­x¶±©~˙ŹŰźzp€ř»-Úᔝ±w,±ă`ý‹Ű›Ş/9©ŕíŻ/Š/zQłzůęáçżd¬ńÇďw—kš.ř_Š:çáľcW]í`yűňO[“+Sľ“wU†Ě}TšĹŰP)ů;6űK[n3vyĹ‚^:ňń wrŹ\^5qI;ôńK7‹ť€Âćĺw>INÍnŇjÎ?ôŘâélm•6q±ćĘNĂç?©cěŰ˙ľűÖG_IL˙ô˘ń¸ţ8 ”S+9Wí•î{ňßY×/nyńĺ˝'®KMewó„kܵҦłŰ?yëß[™Iłjz :[6+YŇ©_–Í0tŢ:ËŃ´úÉ^»pôĄM/żVTWśě(ţüî§ćŻN^ż`<0°¬Á°nF„Ú?±ĆÄľüËŁ3Wo;¸cŰŁgŤ‚ţoOĘń ”·&‹›âŢo:Rnä7ýťf«Ą3GľřiĆL+ĆË5W=ů®…t®Ć…¸őÜ/ŽçĚ}äŹĺ%ąoĽ´éŁ­űk^16nĺźµÔ§Ú6ţÓkL×öţW-3Ż›–äé#zę}5:Ó/Ű>ůĎç?1ĆŽ|űľ€Í_&W¦|'_ňó‰šQToÚýĹżţńŮwBcŮ¡ˇ=ńJMéeYÁx…ŔüÇ_n-ˇ|٦fU¶ČsĘŻ)WZ42`Âj}cĹ5 ©h_z|>…}đéŚĘ´sżücËKIQľ–}ď‡ ­nËŔâ‡{Şcžő”]XÚ'~(Đ,h’›F8)¨)çµü†<#&0c™ľö´¦)W6jc©®öףVÔĄ7é*L6A¶ĆsIKfł®Â(émZ“ľTŻI×s 4§5ëJŤĆÁX®ŻŘQ^üŁĆciPŔUć‹y†fźVçĚ;':©”RÁż jÓ Öůä<€z- zÄW{˛şĄVlOgyűAĆnĺčŞ(ß[÷»Ű‘  vńďsÍ}-OW'+RB$“ţ׊šX_ŇňĘ©sç5:‚$V65-)M­×Ęiüťkµőż—WŤů5µYM-ň{“$ć×Ö^ŞkĐbŇ_­©-Ń6ž­i´ĺY]›­mŞÖ*›·§§}U2#zřş`ű?˙µÜd¦(H6jŐoO*üýâůCŐMéÄIBŠŤě©a±©Eą-&JČă6%„…xx&9Ű|ť](ýÎĽJ ň Ý28ŕŐSgr[LÜíbŁ”HŤUGO$ÇO™ęFËźylĂ©ôŠ…ŢřĂ‚ u«×”629šRiS›z)5«Q«)++Ią”\TVsËůL:cßřČŁ§3ĘĄěĆ‚î|;L •••?Ü)-— ÖüÓľ˝FµŰÔicĘŻžýň›ď*jD“ˇ˘˘˘˝ żŘoP:Oąk\CÎĹm;÷”¤˙|ä‚ď°„1 ±ÉÇwěÚWUŰH rŇÎgŐ(•ĘŠ¬«)é…˘`¬lkÇ»U‰Ä`2s3.ť>EbČÎľRßÔ’›vŞĽ®Ąľ¦úŕÁ}Ý}%ZŠŠ ŞŞ*Ţţůs/üµÔ zÄŐMIWĚ“!ăňą W3 ¦šŇܧÎÖ6K’$1B•*%o ™çyž·,čK Ďóś|aŕökËíŐyÂńŚIůő¦Í/?›˛wknĄ–ë§;ź˝ĄśÄXô˙xzţłëɬlän:§;!AÖäĄî;x¸˘A“})9˝°Lľxo2é3/_Hľ’Q’q19Ł`ř¨1>Üë/?űŃöCH˙äeć§&“>ëęĹó—3ZZtvÎN1qqĆŞË›źŮđăŻyh©8|übĚ”©!.ü–ç7~±ç,Đ٬uĹľ­ą•M”ăĂÂÚű•’|˝°ÔrśJ Aň ‹ŹŹ¬ştôé 8źU…"0´ő+Ť&űRrzQ‰®E“|ńba^ćÉËův –v!%˝ ¨Ş¶¦Ľ(űÓ÷ŢúŕË˝‚ĢÇ.ďYÜ8ű~Ó‘r3@Ć`5[],,­ńó}ĺřW;Ź@ÖQ:e˝OgŚ5Ö×K űÁ Łś8Íë/˙éűORą×“ϧ\1h*ĎśĘU¦Ü«ç5únĎ ĄŚI^†ÎMŠÍĎŻĺhîöŽđ‚pě—_%FCúĺs®fB´ŐŮGÎĄŤ?ÉĎ^x~ăú]§Ň&SqnÖů‹W$sbR& m%ŚRŇX•űó‘‹~ cb3RŽďŘąŻŞ^«)/8}îBł$47ŐüzâxIE˝dÔś9“&rD©âR/_(­Ńb6/]ó0%ą°´ZŚćíz®Ä RÚć÷PÇ<Pk `ťęV$ŽÎ^„üł­Ü¸˙Đĺ'`pçagg,;Đ´Ó[őŢĐQ2`ş{KF6ĎŘN$ú%ɲ1»ĚćAhICëßm÷ßd+*§ąt_ćz—Ý…Ő™ĚO‰ÚCĵ~Ţą¤‹.t®C8Â$Ö‘ĽŘ;}ŢÇlŁě|ůŞCšžçZ~Ů˝şćź5›»đ€aşŻrËěŢĐ:—Đ١ÁE5W5Í”ŕŽ™JŽP‘I3˘‡<ę6e÷c?˛ţs’PwŹ!ŽŞďóJn}z—oćHíçżDŢŚ”DŃśşµ÷%˘ÄFĎ^ĺXúÉׇĺä;â·µ·–ĽżyIÜŃvÚ3 Ë÷L9ç µÝĺ°"rL[Ë\Ń·7Ç 9ű­ß3”Aľ°ÎĚy˛)€6m$”c’h1íčwąŇkŤÇQ0X(mŻ%Ś…ŤT÷đ‚‰?gčdMî#Sîôřď;vŞ\´ôžüËÇ’Żß±ô‹rúó„‘Ă‚]żŮ±˙FSÁZZ›.4Ľ5' Ůřtł$ą \f†QŽ0&I!”R "‰‚ÔeˤíĹpđ÷Y]ţkĎźtg>8JĹ>Ëš2ccťîg2Ćc/IĚťRÚćJb¬ŻşJˇťÄÚVb•äŃBXqđ<'Y¦şoMç €±~PB9bvuo@:–Ý´ě ĄTć6Ą„ôĺY śâ6~ţc‡?|jbHř•fQFM(%-dçţöÍHőy«ń&ÖúżG¸‘Ëi÷%Dsł'ÇH°„0Ë 2JHŰňÖŇÓ íŁ—WyN(zÝ»é\Dz….=„®ĘT9Oŕ;Ęćúź ď¨ŘI¬/ÔöP‡§TÎFŰ6\ !!Ś1±GĄďněPB(…˝‘/šeŘŃť$$'N7«O·üfżĹĐ’y2*dŕ*_§ÇN^~ÇCüV #„#˙?öŢ;>Ę*űßű}CĚn)Nĺ;×˝gĬuʱöCBÄ\§űD.uËX}ÉŘă|\ëŕ U]©3Hv ‚‹.ÔIgŮž!¨jl,3š•ă_W7WŃ|©©T»8*8٬ Ó`R–IĽZĺ¬WEsnCŁYfĘf‹Ě pw…±Ą¤ĹD۶Xş[D)Ö–×»3}ěÇ'o/­ă‘jfa9 ńôďîđIvÁď9k°—”EWjXôźăîüţçâkÝ,µm*Ózů{8ĺ]Ę@Tš1&ë9ÓżÍm[ć#48>žŐ”•×Đzú{öRęĆS{ÍNĽi˙ţëŻy ę!SiÜbă"x2(97óBťYCHTâč‘ńe—O9•-±> M™ÜYb’,q*Ż5Üĺˇ#ůN~ůÝ>EňĘ7P^ó÷y1I(UnuRBz-¨ô7/˙`oýůŚl4$.AľZTT^wĂ÷ ÂŽŤsÖ©$ËÜ“L† ˛…߯ăő—¶üvÔ•9´*pčľ˙ýdžĺ›÷]ŕ8Úo[ =&e;yâíëŢzčćy“fűş“mcm˛rDn>áAţL”#ň+9%uRÂh Ď@Tć–ň ‹dz˛čbŮ(ĚhtĘŘáa>N?ź_Ę…Ä ŢTWúËÁc&AV”ŮÁË'ČRŠLíę?}ň8ľµj×Ďż„kE/Ôí-¬ŁťŕÖZźQXLT«î\ľď›sy5*•JŢĹ+&"XMan1d]Î$ȲԻ,ş Ń•–«O`dźÜ˝,2/IŕBbmëUW ł`ȵVínĂP­ńś8uś3m9´w•ŃlçDŽĐŘÄ6ZÇM‚Ô+&ˇ÷MEÄţ!á)#“š«‹>ae4Zę.•0Ŕ?$bTJRSUaű7˝UmcÇ8ž75]-»ÚŕĎsÄP^š[TzÍá(aŕ#ŁcěXHFy^Y[–"]­şťś¶1E’dY–ŰOG ’,J)(ÇS™IL¶sÖ†×úMwšŮω qą+)őÇÄ1ű’G~El•óźHŽoS:tüÁűę†}>lěîác~Iö\ŔÁ1őŔČŔ©zJĐ·›}Ć9zǰ±’“>ěčĂŁíń˝*Řeôľ$˙Éúöśž¤LŔw}DęćË·ś*pm訟“R·Ć9j»‰ĐbýŤŽŕýÚx><ôO*•vřwIŁ>L—É>ÎŹ›ĺę2Îsě±”N<đ\8ęłhzť‘ÎĄ>Š˘Đ8¦ţrĂ)jµÉ»’"ć»u‡'!kF¤[ľ8mÁŚ(ž˘íuľ*JÜ›tqĺ’S‹ć˝}~ţťK˙gyEççćshůň=ÓFqᥭXzO¨5GŚ‹Śą°rŮ©Eó3V,ůiö¤hG5ÚbYşşřžXľxU'ľ·‡*J‘ĄÉăŇçOÓŮâ7IéäłăŹÍť˘n—Ů˙L˛8ýäť÷.YüLt ®3Z¨!Äű‘çßÉ©¨)Ě8먼ßÓżôţŽş«5ĄĺĄ;ŢŮ P¶{¸GMÉŻ¬];/Đoxé˝^KÝ0hĘ…Ç®yŕęQž9FMXtą¸Ş¸¨  °¸ş8wŮ”·­ˇ¬¦!'ërc}ÍC«f˘/ńŃ­cö €×úżřއ٥ —~®:0ÎŻxžXő؇…Yűµ€fĎĹ‚mĽĺŻ?B…ĄQşč˝iąĺ%…EĹĹ……ů…eyg~ŽÖń°v,@×O©7ŔúK[®’ľQW\ŁŢý×MEé»»óżú[±H]dzaík÷Ü‚¶Ä=‚­µIpÖXňŔŐ WŻäĺ^ÎľÜÜŇřÜęYđJ˝Tzµ¨ ż¤˛öüˇŻŐ˙rm˛čjŮ”(xđĹ÷ę›Z ŠJĘłOB¦,+©1\ÉË«­7üňőŰ~@˝ýóß•Rqz-ׄ=§.W”×Öüôď×]{•Ę÷D˝ěұˇŽv¨+îâ&fV´žÚö>ÝđÂĂC FШզ®x°˛ľ© çrEuíéýßƹࣺ—E4tjÇ·ÓJßń>€…ëި1Ôu'‹ G¶r@ä´EýÍźŕř×!vîŔpďGoşÔ/ć»_/T••V^:öc\ “5Ę‹ůčé+ŠŞ-´~ýއaE=ŁčÚU뤴zĘҵW›Zółłkë ű?Ó—Hiß}`ŇŇuuͶßtŻvvíXQiEÚľ/>ńŹĘ†şĽ¬ěęşęţůŠo7óÓžűš= ą7ĆQmÝŘľöŰďş–R”Pë9nďŢ˝“ÂşâŮ-†Vřôĺ›î4łWqלh°Ű¨ť‰žCµś‹Jă !n);‡Yrś;ű˘Q&ÍŢw„M8”ččMbŤ?‘â7NhGîî?Ţ€Ç,/÷8Ťűź±{†9i‰:Ř)ő×”¨•ž¨šz"zĚW1*j_˙Ą~Á«ü=íňčĐY®®“˝ĆýśŘ‰gnóOzßâÍź> f‰®eű huÉŰű1eŐç˙pä¨ĎŰq3N˝[Đ÷·ŢüÉ”Éi‹ćÄtYŐ(I­ÖDşąůë´łbâ.ßqű$WěżL›Qt×ňÓF«pn?/ĽíŽ AfDDNôÔOŽŠÎ\:/'.Ţé«VĽ<$m žµ©SÎ->€—ŢeU|üCă¦ű{Ů2ŞßµhŃł±!pź*ĘÄgZL®Yă˙7} )kŐU)ŽĎť¬řŚşVăĺ?÷čńSŰ·l/ĘIę¤=óîŮtgJÄś»žgL^=kŤšđŘßw–źßç 8ÄÍ;vâtŻĄřó¦ŚmQ3n´ćŻG­Ő¸úýőó#ż~ýrď ŽŔ=rz™Ařďkë$Ź™•š‹^µšŹĐ»ŘřěSŹŹ  â €‡ßÚ•ąďáxĹÜ˙ĐĆ'lÎÔÔľč‚2RG'Ěš95yÚ­Oýń‰›'%÷\PYŐÜąńŁ˘Ëż(«šýŮ…=ľ7¶Şic÷ }áłCYż|ěî÷{Ĺ©ę/můM©+„:řDnĺë÷ߎ>­+ú3ń°öŐÍą'ľłXČžuą‹µâ¨ŕŕ6(:>!Č×sţ˝ęŻVM q@đŚśüĽ©‘N|<<D^Ł,şłlˇcU76<±4@|b˛ ×ŔÁÓ&Ź>é~“(­ž† ›NžJŰľe{QΙDg €˙Ů\—č1bˇˇĄőŢ[“z•ç©Ç 1H«Açţ¦řRSżűíASkë‘í¨8 IĎ*¸wÎp·.»kzrČŚ•ëŞJ˛‡ëxݍ±Ĺă?6®R w' ;ČëTxJ¬hýS ¨\ľłď1ţ\W Ş[ĚŹ‰đ ň k·á<‚bî]żáń‡×Ɔz+Ą­­úś))‚˘‡Ěš5Í’3y$ŤgeL‰ óŹśuËÍSgÎň©'n *6yÝ3/).yí±{–ŻX68pµVuˇ >}űĆľfö惘Z uŕî _éŁuç ˘UYrÜ8ÖŢŰŢhP9©ŚyŤ-•rkNskěě0pM˛ĎÝ!Cţ˘áÁD5ç2ÉÝ-Ń™5 ­f˛™˝ďt§ňĎĘ⬍ympŕ\7·qîaëü9Ť5ˇ‘sˇŽj­ÜeP2äÂú´%‡ DM:żśĺąOÇ7*ŞĽ:çmx–śBQ/´Vžsýܸ¦|3U&1¦˘ťxVjn÷"@>}B¬»R„PH7ŚťŻńR#çá ×ŘNÉ•“Äֆқżűţ?Ĺ*ĘYź-+ÍfS^]]i‹q¸_piuń‘úV7Ĺ ›ŕDżĘ«Ô«Źä„#0JâíĂR6MHń&Ä$ÉŕT)!Aă}ĽĚ&cAS3ł$szĎeáŢ_edÖ2p§·¦Lşwpŕ¸Ŕ'’âő\g!+?t®Ľxů|M%€‹(nÍąÎ3HŠ˙üŚęJťÎ%FEqí«ĺ•ëĺÝ©Ł’˙ţĂA•F HĆ%Ś©Ë9ú]1w×ꥲl5 €É,ńAŁîY4îă×_«¤+?ŤNIęµ”LnlUCdㆤŢ`Í_Źl6Ő—Ő5‰ć–ú‚ňZ‰aŇMZ®Ľű—H;şëč©‹d™uŤź˘äBÁď»eç÷WÝ’üď˙úďüéĂ«‹‹ĘŠňŰ-d^n ď·ĺÇ]Ď®_ľřľ§öü¸=1ÜŔĐń7µ[őW_{@lę´­;ölţŕ•Ű—ŻţË‹ÜB˘7oßńřŞ[’'Îٱă»1ˇÎžQŁľÜşóźo<łpĹ}[żŰ2ÄM’0ůľ%ÓDÁ|ÓU3űßóUXnÎjj)śgxýg´ŢĘF©ů˛%gČŃz? Vţ‚ Su«CśŰ aŽfx8xr˛¤Vć»&$ě6×ĚŐĘĎ™šBÍů­ô»ßWĄ’‹,q…Ľx«ę[ĘvŐ îNęęmĄgďľpf]ެŕĐEEµŢ<Ą% }ĺíO:Vë‚ßX’©ĘŠg^61^-Wkp›ę­ji©/4s ™ ß•g›$_+böJyS&0É(űÝ8bÄF™đTă×Íy'“¨)ĄÄć_­=&BdŔĹÉgżŰąEŕěäńäĐWŹťÍ- Ńşs×˙uţöď\°ëP«LĐĺN-€ ˝ZMÁlĆ˙KݤFłD)ç«ďĽví{b’@Łć;Ž ©©kxđéçZÎďKËŐj,ű^KÖ¬q«ąđĎĎwͦ^J©ox˙ľ#őWÍXŹŇí8J©exwsđ3Ôe6*Żó9®ÍłP×'¨J倹 VŢ4Üwó‡ďđţűŤÎ‘W/´ÔnUČÁ7>9>xŰ^ś6aôä™K›M–çjÝŐÜžL‚ Ş6,Ľyh|ÔŚE™$Ň=?Ö/úÚ¶hH·5÷1Ç/ejG8J”ÓBôĘ©=–~3}îę \ˇn*7Ô×ýř0 ő­śJë¡oöÇÖÚ„r[ؤĺSâ·nú€†/ś9‘™_™˛řľ÷~â¤m1‰u †k•EWË&dżÄiqUŤŇ–÷®ś=@ÜäY_íÜűĹ«Ź—gßu˘×^Š@iYVxňěYc†Í\Ľ"ÂĎÍdě<Áµ+‹^©ŻĆ8Ž*ý‚P*‰˛>hřK?óĐC§Ż´8p‚PJ›Ť÷üéď/¬™±tÂOŽĺk4˘qY˝á©—^Řŕ­n9•S¨Ôßł,¬ůQ—śGXÓ2čM‰a^`›4ó«ť{żxucYćńýąőŽŽÚŞ«uÝI‡(+»ÔŽa· ˇš+i?ť)lz÷‡}v}(”dü´; €$wX-ĹÍWĚ„mŇ9ńăéZNŻmhěŤ^÷@)§RQ đzýŤWŚyż|óĂ9Mŕđ»Ý NÍË7­ąűwţ” ·‡ěÖvŚWlĽ$]ývďţ)ă×-W3÷mŰą€ÜŰű7* ÍMŤ@›[LfÉR?G QŔpëÜU#|Ĺi©Ă†§LińŚ»sŢlfIę°ę‹ fłˇjĂÂ9CcŁRgÝ7kî]3Fř}ůŻŽ1EŐ\/¶4<}÷Ěá‰óŞ8ďI3FüôŐ+cć>nbŇ_ÖLKJťúý±E:ÝahŤŹµťďË7]mB˙& @*j¸đPVö«i«˛ŤzĐgVfČ|$[ɵęAŁőŔ‹'b zWeéˇć·c—¸Šu"Q€1žę;“ŘR'@ą«ÖdľtwƱé窯đ;<ŔUx‹{ŐWF„TĐX¶­Á÷ŢČ1{‡GÜéM»˝Ř,Ë2#j›L‹#6e>Á…Ă®ß(Vď®,=ÜóvLřR7ˇNG¨×zÎĐÝT{°Q22BJ™5Ď+,7Ь+•Š.<| )ämJi5 HPsý€eF&1É 8-·Óű`oKWÜ^¶k05<ÄAjü®¸ ŔíqCĂ4’Fď:ÖCŻupžčÎ1I¦|ě ON2ç[ˇ¸247­ßů}ňۨ§O‰­ëš¨ ť™Kdp- •źf—->ţü˛Ď ŤŃ0Ö R…­zł$2¦ú˝n˘ü_H…$ËÍf†ë÷XvV,{†˛ąĹ/züý3bţôäSőDŁĺU¨wü#+çn}ďoE(Ď)´z*ŐQ´dskżÔ<őt· °˘ÜŞÖ:;SKĎ´ŚŽ„đ*•Í­KGd €op”ˇ¦fĚâ5Omx 6/óRY™rŰQÎd EKö‘ţłwĺ3›ËË ^˙Ă:­Ĺa.í¦ć޵Z[UziĎą\ĄW Ě"ëËOF$S PN’Ub\˘ęŤ–ťŰÔć!©M“Wż^}”ňďH]YgÖ5šDYVi~—pĐÇIšęşeě˛Ř ËMlÂĚşý6ť)˙‹{4fí˝í–[îhýčqóęuÁ3¦§6ÖŐúÇNĽfYt±ljŤ±ňŇ˝‹WŢ·jĹľ¬úÉCƸtxëčańł>ě›8yŐ‚±„öRĘóëożüđď3>Ýńó«͸Z]MÔšNtěˢ7ęSÇ`”S©T<Çq„XµţĎQnFm`ܤáÁ^‘Kf%ňr«¬ŇIÍŻf×TŕxµÜPĽrÂČ˙o˛TŻ<¶R!Ř‹,¬řá8;´–ÎIUˇYÔé‚ošš ಱG|'/™4ÂP]Đ˝t7Îß~ůŻv ®Ö6µJoľ/5\{ׂ›gĎ[B‚‡Ż]9ĺ¸v+ˇĺeúZ‘ŽOâä;ç&IM DŰ;RoĎ®$EÝk='Zş}Őý5Ŕ˝ŹĽéjŤĆ*™Ě˘ăkź|='Z\°b]Ą Ú‡xPVvĚâěŠRŢÜZ·ë§ÝőÍfÁPV\Ú ôÁ˝Š2C#e íQ_۶ťepÖŞ)Ë)¬®śşTäˇqŔ«:¬z~­FÉÉ!믺űG6ŐÖŚYÔ>¦”ęśuŤµĺçÓË!´Iăä€ń”¦Ü‰´wĘnŤˇ>°/zţĆNOů-nú·ąAP—,Řm>›•Jh\‡}śó¨ź%ř_Ł)çńKżÎH?˙B!cÄXkOĺÜr«+¸!/†@–•h‚±RPűjxŢä­%¦’ݵ° dbţ+ŮG§§_ţ¤Ö{™żg˛€ÍQ3ˇČħ ÔŮč“ÄdÁrA‚Ú7 ‡]żQ4Ď`ĘŮxń×éç˙\Ŕc­I"fáÂ=ŰŻr:Nc˘ÜÁł·ť„ĐkD̢´VĄE´×I €ÎO Il©·?ßUP3ËĎj2ä°yÎÍWîćĘŚh„¦ć@ęŤMY ¦ĺ1ă]UĽZ?;ČŹ0ć@äçŹŢŐ¤Ú41Yd˛lf 06·zëőf… "Mĺ*<_9şÄ[˙x®pnBÂl?GŘ»LyŐĐ$€÷uvíĐý˙—Ú’G¸Ţ™2sžQú⪾Ű$˛Y0+ŢkĘ*ą¸mzýÉ=ĚCĂükšj,\|(-xwóVhŰgęľ”_Ąˇm×®;)·ěĘo¸ćŻGů!›ŤćË(w6çś›wô´”0‰rĽJ­š2kĎáÓźĽń¸ ĺ„´çP0šÍ†+K¦O•š:i¸Ťo|-+®ÎšÍ2a2ŔdJHýÓëç=đę–Ĺ˙ń¶)aÂS»ŻŔdY”™%J<ßů›NĄxČ©NĹ;h ËžŠBSfîîž–ť{[ÉĘ˙$Ijsý¬ÜľůMŇŔhËőĄľRW6s®ÄŃ?"é+@a‡D©>»Ć«ˇŻ×¤Ř A–H˘ęľ|ń­Ç·mÉ©“@9Ĺ&H˛ Ž3—×Wş::_ź,¬mTmkť()|2ˇÍ1Ijl¬ůyÇÖJ©dŠJ)Ĺ;m}ÁC §řĚľçQqW* PÖ'Yô@]y@ŇÖwx™´ˇ®ŕBnÍ=÷ß7,Ɖw ]0{*a’Ž3o\sǶbçźľĄŚ˘,Šfł(ŐWŠ+Ľ5˘ű A”Í‚ H€ú«WlhÝ< @=V,±/‹Ž0ĶTšTqQÁĄ5Ĺnz—Ďş“Ž2µŻżŇގHč岟ˇńĘ·?;ąď‡¬«ć‘‚FͶ¶<Żb˘E:U&~pD$ čyó†5+z@W$Ň}oPf^Oľů޲In“'O?q± WkňŰŃPą†ÝvóřĂ›ď-źč6yňô´Ë”Ň>Ä沱cm—™hcMáëoľąxéC!ďzpŃd€ŃŢVć2“ŐjG@Ňň‚`˛©™€DMzgg×P?‘JŠB·[ueMŇaç)Eç1eě“ďí–UzY4„Ę’$1ËľF­vtÖÁŢöw' Ó.Wđnń™t~ôĐ×"‡ţ+~bZJř7€ľ-)fť'Â+oú݆oJ]ćţtÔř}‰n<şŚĘŻÁď I|:Đň›§ˇĂ“· ›p49é?13Ü8M˛á*«o>Šőźě h‡ü36ü^żáŰ’F1ŘÁÝqÔţ‘Óô ‘„-#bVx¸Nöš”™ÚÁóR‹Ź…ţÉD*‡Ë>ËCF~ŰwÄ}¶.5nĎP˝/§s¸Čŕ‡{:°şiť(·qô˝ ćçŻZşkÎôŐáB×>°îÔśńjX–’CBc«×ܱŔW€#„˘ˇŔ#oľ0"?čěËWşh=.­ľű˝äč)Q1ĺkVmž:á‹ŮłKVŻx*.PíX¸ří¤´ůÓ»ě¸őć ńź71wůü±Îš®l*R{ućśoĆ%ř-ń=$ŽPÓc~š=áÓ[€bµ^›=÷ëqĂ®—A*‡çŢúOZFNScýÉĂűÖÍť}đŮŇ«GľÚ´é›ă e™CüՀϑ‹•ż¸mľMz.U_zaH 7ěăAą)AýâŇ «o¤ć¬ÇâĹ(aú×Ű÷dTTd˙ôÝ– €ó‡{3ęËsźŢřřľăiŹŻ] iŢZĆؕßkpjĂć¬Qr§'±Ó.W¶žřvÓë>|*}ý]ł*Ó—?ahŞŰµó›u‹ĆčŁ&9•öÜĂ÷ţőĂŻ 9Ó{që}ÝŐ¬§0ďÁçjJĎ:ňŔńŞNßtć‡@ŘŘ…U-ň7żóćżľ”Zk–MŤ4·'Zvrěě`ĺťđź6ĄU¦ËŮ˙f€RiËoL]™±ŕź»Ďě˙đ yšdY °?sď‡ĎöŤzkspďú%7Hş}cÂň ˇĘw+~ű©Ďn|öŕ…˘«%†:bĐŕłĹ5×" ;–íţąăá›QÖ|üë˙ĽţŹĎ3Üš>oőëąYi/<óÜ÷‡.ĘŤ%óFÜ ď|l)uhßýó'‘;÷î˙ëł_,+<»'DtÖÎ.˛ ¤Gę›PçěačÔw@(ÇiŐ€§?=]•ľ…D7˘´®uíĚhm`rŤŔ>űËşY«ÖËŚí޲y×ăfŃřňĂŠu÷˛°ü gĐéÔ ­Ę´Ż”Cŕ éN ĺ‚]t‹î˙[ÎĄ6ÄšJçŽđ…Käą’Úní!ˇ´Î‰;vý¬`Xśńł0üÖ[űęßożţÁç23n\4Ŕą{¨Ą;y7;óT­˛[“}€¸ňzcOh<´mŽ(í&Ĺ©Ćmk^bŚeť9úůg_=¸÷ö›’8:¨4jÎm0Í_»~Ăőâ/ÄÚŽmSőîćë¦ę3.Zxă‹S­ÇÚśWÚ­BYîĆM»Ł¦EřyóţőÉĆŘţŻŢó!đôgi•éß*“<żŘ™EuÂŽOßýŰÇߊ†˛ĂĂÜöĐóíVťW©`cç)«1ĺ×ôó÷Ü6*hâ*َb¨PĹW4KĎ>8€Jz4«2óÄĎ{áŮÔXk™ÚĹpń¬Ń’Ú$¨ă`á˝=}c9ôTŰŃĚ^W}}OYbę­S´NČjČ}µ°©BĘż=§ŔP`Ŕf.3Ö­oĘ3`"ShÝ’\Lę˛˙ZÔÚ  ©YhĽŘd,5Ö_2r*4g6·”šÍ ˘ąÜX±ĄĽř»zĎĹ!AS5ŮĎ䛚ayZabś+ďšě˘QËďÔfšşFâP"YČ}ţŤ‡«[kePâ¤5—¶ÔţÚ`n”Z˛š[ĘÍą´CXÓšÝÜRaR:¸Žp1eÔ]ţkaKŤÄĚbĂŮFs Ů$5_lj)Ě5fóUŃ\a,ß\^ň}(C¬łk¸Ş|çU0 j˙k@¬Ń‚:p~ĄĐP$RS 8&zFŢáž÷R~ËU‰»wH ‹smcÝĎĹĺŐfs~míĺ¦Y–+””^¨kT,ˇŻV›_[ůí•rĺi ”+ö‚,]©­=sµ@«`<_S[Ňh8^cĐqČ®®ÍilŞ6š*› __Ęüçĺ’™q#׆ę˙pđ×rÁ"LI”´š?_o*ľyúäŢę&ĺΠ „0 ŘĚ‘Q”WĐ*(>×&QBX§wŞ«Ă9…ň˙ŘŞ†"}Â_ôâ‘cy­wÝŕY“źńĂî}őuyç/ć\Ü(-qĘÔA´üńÖąT±`ý«ŹÎY»ju©Yb“ôXjĂ}ëŽf•JŮŤľřf”ŮPuŕPZâÔi7PóŔŐŁÄnsps ňqM˙uß‘ÓőuµéçŇËjjöüřŁIă>múŘňóÇ?űňŰŠÚI0UTTěŮýă™Ě<ĺâ$š*++÷ěţ1=3„Şó~Ú: )ilRBVÚÁ-ßüPUk ą™'/Ő¨ÔšĘÜŚÓé—uŢţă&NňŐ´<˙§‡vžČ FĄž®5źÉĚ“ Ŕ, Ĺy—Ož>×viÂć›Îü\Čc uEOdä$ĄŽ÷ŐK/=ýÄ—?žb€`î‰V79öŔDÁ”uöÄ©óŮ T­VóVîyžçyëŚëËáyžSŽż{můmužpýřĂŰ_Żo+Uź{6ýRQžWDěÄ SJN|żö 95-]ÂŘČâ‡My•M”ă‚#"şŁţÂ7|şăh—ľC“• –‚`Ľ|ţôÉsYkm¬JKK+ĚĎ>|öŠŁŠežJżTPTU[S^”óŃß_˙łďE™ĹŤ[Ú‹,l‘Ď9“v©°Lyŕ Đ:u.›^ąçu#‹GĎUI˘YçÜŽŘCŰÍéęţ_z‘Ž(<ĂŁ&MśRrâűűî´ŔĐR~9=íRÁČŃcýś¸Wž{â-{d± ŤôĚ<™ˇµµUçę’ĐNëHUqMŤ•éÝŁńާ;$Ö“·0µÚˇ /ăLĆeCc}YYIú™´˘˛Ć 3&Ćśó§NžÍR« ó3ŇmżéKl®6;vüäůl‚9/ëĚŃ“çd†śśsuM­ůŮ'«›dŇÍi#ˇ”1Ůgđđ[R®\©ĺhŢ×Ű÷ó˘řËĎ?dHfÓĄł'NťĎ&„4Vçě?‘9zÂä˝řÇ ÷s$€(ŘXufcçĄÄPe5¦¤ܲő‡ŞşĆúň‚Ł'N5ËbsSÍŻ‡–TÔÉćúcÇ2%ލ5\ĆŮSĄ5Ťí׋ěcžVXZ-‰fËv1OfШuWzřĆ2Đ®šŮm¬÷kOÄ~ë-§×Z™ýL—ńžŽŽć˛] @mߪ÷J‹Ż­Y Ťůć^X"mWĽú%ڬ'<•źKÂor<µ*Ű^D®ëBĚú›AL «wňç«öÖ÷‹^PĹ_E[Ŕ]%ZlçĐöz»˛3%("RŐňy^ëS|ŕÎň¸9<´¨¦â|}3%0·Hˇ“gĆ }2|ĐÔűÍýŘÉú#Y‚„zxuÖlË/éçS[«¶r’ڱ7ßçZöďĎö0އmx{«R¬Cű/Űş®żćßµ˘„8»FŠöşQüźZÇŠîDé·»'e;ź#„ţ lŻ<f˛ Ę1YŇÇÎŘ·ő}wÚ*0+żm ˛M¨őkÍaڍä«÷ÎźôSV GI§÷ŻĘý4ËÇżť>÷%ő]Ǩú¶Ĺ‹®śý%íbq_CaŢ8w”2YŽN5"ÔýË-?ŢX`‹Ë+B9bĎÉ!`č'Yt.ŮuǴת{śÖö,‹©·˙P”Ér§9 ˇöÍG©t}˛ „RJAd©SpDB8 ©óĘźBIç'ă–íľkł‡„†®őŁí˛ĄÔŽ»«Ş»CăFú)!„RĘ$Iľ#Ş\qm ëVôm PBÜ&Î{đŔ‡ŹN ‰8Ű,ŮYE+SÚÎ E(! n´ÍŤÜĺNWź&˙–ÉőcKäkĽĄ¦ř$!„ÂňŘB™–Úô-byG@8&YéÝť„Ž/KS ¶–§µ–żŰőµ °]Pž0‘1ÂČŚÉoLd=ĆB‹ĆdĺoŰ?)6Öş9”Ć@“™ë4/˙Ń˙\Č”ÖQ°îłbŘ1›ú)A'?Cý‚XŹjŮ>¨s”‚1‰1BGcLjźÔŇ)¨¬ŤńíŹbőUNÇrČŠeĄő2cĘů´ÔÓÎ ażG×RěČč°Č•ţ.>-ţvń¶tăŚq-°ëX™P5Ď™ÁšIŽăäĽŮ•NßŃčYRí­ăy“ݰř‡ŕxN%Âń˛(ÉŠ™,I˝9.łá Ě®­Ł”‚±Ć E‹|˘Ć<Ľlň›/˝Xef”REdí'%6‚“$FGIwěuµó6cŠ,É ‘KÝ2V_2v3'ˇ\˛ŻŹžČGĘ*LvÇ@‰đňâäU]}ŃĐ @ĹiÇř{ë‰xĽ¤˘F’Ő.Š Î(+Č4xJ%YćŐÚ(g˝ŠŔ,šsÍ2S‚źËŚ1wW[JZL”Ú¶7cwUŁŘ>^ďţÍô±ź8Ľ˝´Ž#D¨‰…ĺ0ÄÓ{Ľ»Ă'Ůżł?¦.IYtĄ†E˙9>ŕÎď.ľ‘ÍR{Ťc^>AÎy—.‹ ĽÖ=>.\‰Q•ąĄüÂĹ"™ĹA #48>5e…ĺ5ˇ)#“šŞ :aű 3•fĚ„ÉNĽi˙ţ_Śćë¬y ę!SiÜbă"x2(97óBťYCHTâč‘ńe—O9•-±> ŤĘq”Č“d‰Sy­yŕ.ÉżpňËďö)’WĆ-ŚÉ}z ĄĘ­NJHŻ•áćĺě­?ź‘-†Ä%ČW‹ŠĘë®u^kŻv~plśłN%I2Ŕ8N%´Öf\ĚësSn8Ć´^>ÁNy—rD›ŃTWrŕŕ1“0 6 /şŞĚ~TC÷ířď?6,߼ďÇŃľK˙F’˛ť<ńöuo=tóĽI3 ̸Žc"‹Fy„ű3Qˇ”°Š‚üâš:JĚďÖRwˇ¨„Q.$fXŇď$ X´(üd•t=č* ž@ć5c&LÖs¦ş‘`›”ŰżQ0±ÖCÂ{¬şkAúľoÎĺŐ¨T*IxŻ`5…ąĹu9W ËҵĘB±!±‰#bĂ›ęJiCŐ?$ż€Ză9iÚx'Ňrhďľ*ŁŮŽ]ëSÚúWŰQC@L‚[K}Fa±«O`dźl« V¶…¬W4LBďO”:Ű[zŕđ ł(w‘ÚŐúäq\kŐîźéUë”j=}|ÝiFf ‰‹«Kux ŰůóY}¶™`śÎ="ŘĎQď µ˛˛sM‚¤čGDâ©#¶ţçŁjA¦”PNE™,˲,3.*&¶ÍŠ‚rśd2\¸-0ÚîVÎrD” ϲ$É]ÇeÜ Ô B”!F9H”d™PJA9žĘLb2äΫân{·uŰíʢ»hcźŻ-qŞŔűCGýś”ş5ÎŃ‘pLőL=0rôÎÄq‡G&ľ˘±űڇšpç”})Io†ZÇÔ_FNŐ *Ŕíf걇GŽŢ1lěä¤;úđP6íU°Ëč}Iţ“őí9=ißő©›c,ßrŞŔµmĐ;Ď<—Žú,ş-|®uŔ…şŽÜ•”úcâ}É#˙Éń$Ú˙ŕ}uĂ>6v÷đ1ż$ {.„ŕŕÚę˝ ¦ü©Ő&ďJŠďfO…ţ“c¦\ľcńŮe·:v„Ţ–ÁöżďNuůÎĺ§—,<·pv ś?˝ĺÖ Ë¦-[täÖi‘:ę–ľbŮ=ˇÔ0.2ćÂĘe§ÍĎX±ä§Ů“˘Őh‹eéęâ{bůâU!žřŢŞ(E–&ŹKź?M×…˝ß:)xv|ⱹSÔ@ŰAű˙JRbyŢyď’ĹĎD˘ß˘…*ľö˝yţťśŠšÂŚCqzúč™ «‹ ňK*kĎüJ§Ú‚…ąGM)¨©żcĘŕÄŮwڦüěěÚ:ĂţĎßôĺú5ĺTŔńĄ÷wÔ]­)-/ÝńţËN×SńŔŐŁÜĽ#ŹY^îq÷9>c÷ sŇu°SęŻ)Q+=P5ôDôŻbTŠ[w_˙Ą~Á«üdÓ 0-&a׬ń˙›>Đ”µęŞ” ÇçNVÜ×Ü8‡Š©rŠź{ôř©í[¶ĺ¤Ńk8Fß’•›ł 5ŇiŹź››b“”xĚŹý}gEĆĎz 2yÜš•‹ałkjmZ=#mýu'eôŤšqw‹lş3%bÎ]Ď3&Żž5ěZkřzÔZŤ«oĐ_??ňë×/‡řâÜ#§—„˙ľ¶@ňY©É±čU«  ń˝ëŤĎ>őř¸P*žxř­]™ű>á€wPĚým|âńÇćLMUJö"hBG'Ěš95yÚ­Oýń‰›'%÷\Đ2foü¨čň/ĘŞfváGŹŻĆŤ­jÚâÂĂ}B_řěPÖ/_ű„…ű Lś*;:ď¨ŕ;mň(á“î7šÍ÷͆Ö群>ę"A¨OäVľ~˙í¸®uĹŤ$ž#Öľş9÷Äw y­ň" quŹŽOňőśڊ1ŢÍ»ß4µ¶Ůţ¸Çý.˛P¸¤€ÚÂ϶jş4¶«,Đ›íI™­šŽ ›NžJű΢‡*]BZVÁ˝s†¸uŮ]Ó“Cf¬\WU’=\Ç{EŤ-6˙±q•Rgßeˇüó ŕvT͢¸jR”O̵w-Qlxł±éîiqŕâ‡U¤ÓPW=)Pă>bŢŐfĂKS IĄĆ-ÔÔR71Ŕ÷N©i6?µv¬ökěô/ť OIŞŰ˙©T.1֚퀳őĄĆ9Ä÷ ®{‹D”ŕž#ÇÝłňvĄĄ-ćÖĺc=Ç,53ˇ]·Ť ź˝ćM“áJ| CÄ…†–Ö{oMęYë:,döĎZ@»?§čÍ»—Ü~ç%ů‡űn3ćg®ŢP]śŐŢ®÷_ aäŚ{Ö¬Y·z‘‡†Š±î™ż——ĽöŘ=ËW,‹ ňň ö k·˘á<‚bî]żáń‡×Ɔ*ťËĆňĎ™’ (zȬYÓ,9“GĐx†YĆťř0:˙ČY·Ü÷Ó?/ČĂHĎ:¦8ľ pÓPąˇľn ¸˛Á @C}+§Ňú‡pi„ëł?„RI–ÜÂ&-ź˙ŻGź3.ˇÉ/mXüĚÝ·9Ź˙ăÄ0AyĆ=ń¦Í˙hYJ%QÖ ic?Đé­j7˛°/AŰŢdőÍéV­F9toÓCBPJ›Ś÷üéďA!ÁK'ŚůňXţŚh Ѹ¬Ţđ”łWĐ ®ćTNˇRęšdA)•e©ł†s*‚+Nxł(*ŇůhĂsÄ|çĹ]Ľ`ľÚů“fVҦÝŰĎ—üéÝö5B×pńŔO»Ótz˛oÝżAÎÁ#¬Q5 ś$Ë ­~FRl dŃ4ËvLßĐču”r*žXF+cîţť§óMr¬®­{đéçšĎď?c֫ŒҬ°ä?Î3L ż5ÂĎí¨±O[u˛$čÜzôă‚Ýu‚$Ž´a\Ă©,Q·{6ZÚu,«Ŕ'o>p<ëÜŹ{@€|űéK»Źž;łç­ż¬™ö}f G‰Š'‚h±˘<…$ăÖą«FřŠŁc†ešgçťşsŢě3Żo2KR»ĺŽŠ fłˇjĂÂ9ŰŇňś\]oYőÄŚ~/?ô—ôjĽöáGW/Üđ]•ŘŇđôÝ3ż<ë“—żwŇŚoůĘł—NýřF;u ł‹<ëŢÝU¦]ea=s°–aű| ©cj9Äh+Ţ’^›¶řÜŮG Ô]|§: !Â…dďľe?$úA+Łü!šÁ©byr’9×Ř Ĺ\š›Öďü>ů‹í?ÔÓ§‡D€ÖuMTĐÎĚ‹%28Š–†ĘOłË ~Ů‚ç†Ćhë©Âo˝YS ĚM”˙›‰ŁdąŮĚĐźŢË>ŠĹ]sÎľ…·Ür˙CëGŹ›W§ žyÓhĽWÜ#+çn}ďoE8ž—DÁ,ę^űřë9Ńâ‚ë*eĐ~Šž!›[ý˘Çß?#ćOO>UO4ÚëŤĎ5őX<ţ±ŽV”[ŐZggjé™–ő!ĽJes«Á’ĂYŕe¨©łxÍS¨ÍËĽTVf™řpD9“%-ŮG>řĎŢ•Ďl./+xýë´˛ĹZ75wä¨ŐÚŞŇK{Îĺ(˝R`YüX~2Â$™Re ¨ăšUo´ěäئ6‡Wmš<Ŕ~˝¬t^Ů @ÖˇŻG‹źµđaßÄÉw. třKý­SŻ:¦tĚşF“(Ë*ÍqŐ2Ž'’ 4ŐŮeú’(afÝ~›Î”˙őű¬|ŕą(7Ł60nŇđ`ŻČĹsR péŕÖщ- Ž0«Ö˙Ůšź%sR‰měz»˛°/AKOázú¦M-Ź'e‰©‡$ŽćŤWłk*pĽZn(^9adĚ7YŞW[iáöZdˇŮhřmă &QomĂU<ÚĄóĹ÷ű¨xŤ±ňŇ˝‹WŢ·jĹ/9 )aÉăgŻápׂ›gĎ[B‚‡Ż]9mŽÚm•ĹŇ.ŽłęŇ9©*"´Óúrç~(űôťěëR/ˤŽŃjţëL€Jjől‘E­¬ör×ďůúľĎřtÇĎŻ>4ăju5Qkú˘3ĽÚˇľ8}TŇ!#Fť.¬Óix&1&Iťmfo‰t#eŽr”r*•r÷q„F,·Ł¨µUÜ;kŐ”ĺÖ WN]*ňĐ8)LvXţüZŤ’“C 7Ö_u÷ŹlŞ­ł¨}Ü)Ő9ëkËϧ—Ch1 ’ĆÉă©u;í˛îÝ]dĘu•EÇ̶rďlźŻ!Y­j$& –Ë„#Ô,ÖŞ˘ v â:ěă„Gý”ŕ˘ÁÔT$úÝîŁĺ“Ćsś ŁśśűBnu7äĹČc˘M0V j_ Ď›ĽµÄT˛»–LĚ%űčôôËźÔz/ó÷LÖ ÓB‘‰N¨łćžIL,î můŃzŽw Ţg†»áŔŐVOŔ Mpłn8ŽőÄł·˝„uF íó9 -ÇrÚÄĐhĘyüŇŻ3ŇĎżPČ1ÖšÂÓţ@Lbt~ZHbK}—ů.€Js«»Öaëů´_ęä8g]Ť`äĺżyÎÍçP'™Ą6OÝ"d‘‘ ˝«`núˇ´ęLYIŽQJpŇLKĺçŹŢŐ¤Ú41Yd˛lf 06·zëőf… "Mĺ*<_9şÄ[˙x®pnBÂl?GŘ»(yŐĐ$€÷uvíĐë˙—Ú’G¸Ţ™2sžQŮőďĎúQ6 fÁ2E&Ęa1$&3Ę·-ą?”Ľ»y+Čž|ó˝e“Ü&Ožžvą‚RÚ›='eęPŢP9ČĹmÓëOîą`ćWi¨@®e5đő(?dłŃÜbAálÎ97ďči)`ĺx•Z 4e֞ç?yăqĘiϡ`44› W–Lź8*5uŇ„qßřZV\ť5›eÂd€É”ú§×Ď zŕŐ-‹ţămS„§v_3 3€É˛(3K”xľó7ťJńRťŠwĐ0@–5<)„¦ĚÜÝ=-;9vĆ% z’$µąő$°Ä'č˙ÔťçxĄĆĆšźwl­4ń#".ĎIÔWS¶Ćr®ÄŃ?"é+@a‡D©>»Ćຼ®I’AÝ—/ľőř¶-—jD€żZ{ĺBnÍ=÷ß7,Ɖw ť?{Š–‚‰RŁaŔeÁ@ęŻtđă:˙ć)ĽÂzűw]d.¬j®ÖÖS”ŢdWĘDE•ŃťGδaÍŠmĹÎ;>}KEYÍfQŞŻWxkD_Ł,:ixtD$€?˝óŹvÎqś íŇÉŞT·\Ą¶Y, "cj*řöçc'÷ýuŐś @¶§Ť‚(›A$őWŻXŁşŕćię±bÉ­Ç·mÉ©—¨J-3k»!ű€ŻH¤űŢ “l­Nf–(­«čE¨Oełhxdáß €Ů÷<*0îJeÚĂÝ6EdI$ADł(Оl2Ëő*^×n3ÍčÓ*&Ë‚`Ó.eżĹ$M¦AP‚J5jµŁłm«k´YQeő$Q“ŢŮĂ™Ä5ÔĎC¤0©Ýň+k’ޱ€RtwĆ>ůŢnYĄ—Ełâ$ěŢ€! ťtt¸g˘€ÓDď)ůR?Žl{ţF·]!/&Śý2 ´ŽcĄřŹu Ţúqécbôt‹Ď¤óهľ9ô_ńÓR—¸ÜđmI1ë<žŕ"܆oJ]ćţtÔř}‰n<şŚĘŻÁď I|:ĐňŰŠç¤˙ÄLsŔiŰů‰TŠ8Mőťś1Ęg„Qúń6­ Ž©{äy©Ĺ?ÇB˙äÚÚn‹ĎHgŻąA)›ăBW$~3|ÜîˇNAŞ®Q˝ă÷bĂďőţíÔŻc´@Ýodđ‡C‡= ĆVÓŞsÝ»dńŽ)ăŢť6;gŮ­ńNjC‚×>°îÔśńʦŻ÷ŘżtŮSĆ<;f\Ő=ËćxëŁüŁŠÖÜůÁř‘ĎŤ_qĎ÷†xÎçW®Xč˘qô¸´úî÷’٧DĹ”ŻYµyę„/fĎ.Y˝â©¸@@µcáâ·“"Đć[Lď°ăÖ›7Ä~jÜÄÜĺóÇ:kş°iYaż:sÎ7ă\ëýG(€é1 ?Ížđżé-@±HŻÍžűő¸aýÇ •Ăsoý'-#§©±>í×}˧Ś_t×KÓ=»ńŮŠ®_âxąXůń‹+¨4j·­y‰1–ućčçź}}ôŕŢŰoJŔÝŘ‹^ĺ…(ő‹K/¬>ňŐ¦M߯/˝0$PŹkô1őX<&%L˙zűžě‚ŠŠ‚ěźľŰ2#!pţpoF}yîÓßw<íńµ $Í[Ë»rřs N `Řś5JŽ€€Ři—+[O|»éÁu>•ľţ®Ů •éËź04ŐíÚůÍşEcôQŹśJ{îá{˙úá×†Šśi˝Ś¸őľîjÖ„S÷ŕs5ĄgyŕxU§o:óC lěÂŞů›Źßyó__J­5˦FHšŰ-;9vv0ňşřO›Ň*Óżĺěó%»:?ań]/çdĄżđĚsßş(5ĎŠ6óőŰrÓW]Uf#řçî3ű?|č@z °¬…?Řźą÷ĂgŻ›şňh~ř‚G–OP&„rZ5ŕéOOמŰŕŽ‡˙qůâé—E[S;ř©LűĘžrv‘ŔŮJ01ÄŔđ[îUz–(ßÄ·ÓP–9Ô_¨˙üöGŠž<´ďţů€Č˛ÓÚ™ŃÚŔä}ö—ułV­—Ű˝eó®Ç͢ńĺ‡˙ęľËÂâĎđáwł3O)¨˛Ö˛)=¦/ÖÚ†/ž5ŔȅʵK‡|ŕČsņă_˙çő|ĆäĆ…©!1Óî12öŐżß~ýĎef|ě¶ ¸ŽÓĹÎýëäÁ˝,ś@§S·ŁŞAʰҵÚĆnhÎK®¶ô„ĆC ŃćŇnRśjXŹVÇď[89ÎaçJjŰdq1Ţ—Ăď÷ěűëł_,+<»'DoGđ]Q˝˙™Ż›ŞÓ@ŇjZ>۸Â#jrŤŔÚmć’IčqdT6ćÜ÷‡öv ˘éŻÎśúĂł˙ÝöSłˇvÓßţöĐšE<mđѬĘĚ?˙í…gScý•+ŞLýbgŐ ;>}÷o+Ę pŰCĎ·[~^Ą‚ÍX@ř[Ť;ż¦źżç¶QAW Cµ€*ľ˘YzöÁyTúĐNÔ­mT×ŢÝI¦Š#·…÷ľÔUë’ćZŤŚÔvô‡öí¶…ĺŐ¶CÖ\ÚRűk©I2f77ywŢ1ŇQ®kąňVAuZ+&0s™±öh}SžčrĎDÖšÓl¸Ü @n/6KŤő—Śś Í™Í-Ąfsh.7Vl)/ţ®ŢsqHĐTMö3ů¦fĄă\y×dŤZ.x· 6Ód'ţ/%`06˛űüW·ÖĘ ¤ťgsŁÔ’ŐÜRnVXb"kÉinĽÜ @ă˘jÍ6Tţl­¸µi ·tËsůćň’ďë@ bť]ĂUĺß_e]kÍjj*5«Cś넬†ÜW ć®1‘©´nI.¦ uŮ-jm …Ô/މž‘w¸ç˝”ßrUęzĹ–" Ć_+jüýÜHëóGNś¬o ĘRe“á@IiF]#‘Í-GĘkcüý‚´ôŤS§ż+©­i¬;W×číí­"OKŰtĄ”¶­çkjK Çk :ŮŐµ9ŤMŐFSe“áëK™˙Ľ\23näÚPýţZ.X„)‰˛V“âçëMĹ7OźÜ[ÝÎ}a@±™=2"!Ł(Ż UPü©L˘„0°0OďTW‡/r ĺ˙±U %Dű„ż0$čĹ#ÇňZ®Á!$$2˛&?ă‡Ýű*ęsÓÓÎ_)ń3|Xt噏?ú؉슾ö肵«V—%L–™ZíP—q&㲡±ľ¬¬$ýLZQYÍ G2a”ŮPuŕPZâÔihů†űÖÍ*#”öˇů÷ŞG Içŕćäăšţëľ#§3ęëjÓĎĄ—ŐÔěůńG“Ć}Úô±ĺçŹöĺ·µ ’`ި¨ŘłűÇ3™y–_ŃTYYąg÷Źé™y ÄPť÷ÓţÓIIc“˛Ňnů懪Z%ČÍúä٬ÖÖGW—řaĂ\Š@lů9—m§!ťdQŐÄ«­%xäR Q°ęݔʆĘöo[űŔŻŮĺPńÁá=¬ŻĎ9“v©¨¤Ąµ>íôéÂüěĂgŻ8ŞXć©ôKEUµ5ĺE9ýýő÷?ű^”YܸĄ×$ ĆĐÚÚŞsuI6Ě\uö™ÇúáägGç+ůç­lřéÂŇšOŻśséJ¤†Ň‡Ňâ§N să^xň±Í?ĄWçź?ťU0rôX?'î•çžřŕë˝2HçP¶ý+çLÚĄÂ2YfŚ1ŐSç˛ŕá•ۦ J¬Uk{¨X’ć¦ęô´´îĐxďÓëé źň„×z´*-->{ţ|a~Î/‡Ň†M™:–?¶vݱËU„‰ˇ“&N)9ńýÚ6äÔ´ô1‚9?űěń“çd°ć¦Ćs§OĄź=v"#/iô8ĹfîJcčM{ÍćŇ’âňššň˘śŹŢ}ăÝ˙~Ő 1ăSĹÚüvďtzˇ®ěDúYl8~,SäZĂeś=UZÓ++Ji¬ÎŮ"sô„ÉzńŹî˙ćH&Q°±üĚf,`”C•Ő¸“~pËÖŞęëË Žž8Ő,‹ÍM5ż:XRQ'›ëŹË”¬¨Ňa‚şöîN2=s1OfШuW¬géi…ĄŐ’h¶iWýé6Ş;‰÷!u÷M r/±>}O—ńžŽŽć˛] @;–F˝Ó@‰× ŹÖ¬†Ć|łý@“í+»DŻS™îé*><–„ßäxjUvźĂ®őĺŤ ¦ ŤŐ;ůóU{ëYßhŘ'h2–GÂvŢô·l'1 )A‘Ş–Ďóʲ ˝¦áĐ›ĂC‹j*Î×7÷Ený•8B%&ĎŚúdř ©;ö›űŘ*Y‚„zxuÖlË/Č'…$cĚÍ«bťK˙ýĹ^f‰•ü[F^·Ĺţú%ń»ÖC”{|×HŃ^·"Š×TëXŃť(ývŹěmÇu˘qëźš•(˘˛ Ę1YŇÇÎŘ·ő}wÚ*0+żm ˛M@ôkÍaڍä«÷ÎźôSV G‰Ôwy °軎Qőm‹]9űKÚĹâë…yťÜQĘd9:iÔP÷/·üx#A€ŰŃĘ‘6Cb ˙'IĚ®:¸1&„r”(qj{j޵,(µÖĂN,[$u-–„PűćŁTęYŘ#oG:č4 č<,÷•0ĐYçŰňH@tĽ[K]FQÉ@ôEW•9´*pčľ˙ýdžĺ›÷]ŕ8Úo[ =&Ęń˛$NĽ}Ý[Ý:vďđÔź’R6Ĺ8zđşÔ_FNŐP ¸Ýě3öđČŃ;†Ť=śôá`Gʦ=  v˝/ɲľ=§G˝ßő©›c,ßrŞŔµm<;RŽ©ž©FŽŢ™8îđČÄWC4 ťŰeÜăč˝ó Ŕsiŕ¨Ď˘)şPď b–DĂţ8xܡ¤1?Źň§`€cj¨÷‚ň§V›Ľ+)bľ›]< đOŽ™růŽĹg—ÝţéŘz«˘h ţzklŇĹ•KN-šôöůůw.ŰŻĂË–¤/Ypfů’s&jŔą§­XvO¨5GŚ‹Śą°rŮ©Eó3V,ůiö¤hG5ÚbYşşřžXľxU'ľ·‡*J‘ĄÉăŇçOÓٲ7IéŔłăŹÍť˘ţ׼Xś~ňÎ{—,~&:ý-” ś÷#Ďż“SQSq(ÖŃâ=ýÁß«oj)(*)»tl¨Ł G9îQS jęď28qöŁ)?;»¶Î°˙ó7}ą~AM9p|éýuWkJËKwĽ˙˛ÓőTřąčŔ8ż˛Ľ0~ěìýJśě= ţµńNô'»€RĽ.zoZnyIaAQqqaa~aYŢŮ}qÎZ€ŞlßÉôKęôÔťt^§Ŕq<€€¸‰™­'ż}OĄ@=@÷YW·§w˙uSQúnÇn}«ţF,RD™^XűÚ=· íö5%ާ­{˝şáꕼÜËŮ—›[ź[= ^©—JŻä—TÖfţú €‰‹×Ö5·ö·mék3U~ŃŰͨ*/**­ĽtěǸ@'ty5n# ”ď“íâv€C›ÖťÚö>ÝđÂĂC FШզ®x°˛ľ© çrEuíéýßĆąŕŁú. ĺwôôEŐ†+yyµő†ĂßüÝżdm}·ľú›Xp˙ë5†:E:MMő/>¸ŔĂ/`iEÖ‰5ŕµă× ÝăÓąĹ;9˝éŽvęż|ý¶PďG˙üwë>h Ž" N7¬ şG4]»˘ÚIŞč<Ć)/Ňőí=îÇ˝ `áş7j:Úţ‹zĹVˇ;oÝźŰíüĂwΠv Ýs®¸<ó—®7ö,Ţě¶ @ä´U—3OÓYŘĐzŽŰ»wďäpŤcLg+zfoŚŁ ’}µ¶ßu-Ą(ŞB}RC×FuíÝ]1ěK˙Rěđ”ĄkŻ6Ý€ŕÂÝGlJHxmpęŽ!zeŽ€D˝7ńDĘđ7C;ůnP&Ínsü&Ovő¦d~ÜńQÁ \ÍČťĂýÇ;đĺĺ§qźă3vĎ0'-Q;ĄţšµŇUAODŹů*FE@íëŕżÔ/x•źGŠS7: ¨4I;“"n·x$ă":ó¬ňspOÖpHö™6Ęo¤ľ®Iź±ţĆÎŞF«KŢÖ;ĎÝćźôľĹ›qm)Ł.ŐkBzŠo‚ÖcnŕԬѾ©€väŽ~@LYőů?9ęłÁ]宨KX@dÁęKĽś¦Vżvĺ˛@wt™«ŐšH77ťvVLÜĺ;Źwâ ÷?¶xÁlog˝ÖÁGŁ!ĺÝö.ĽíŽ AfDDNôÔOŽŠÎ\:/'.Ţé«VĽ<$m žµ©SÎ->€—ŢeU|üCă¦ű{Ů2ŞßµhŃł±!¸F_U7ž(¦Ĺ$ěš5ţÓš˛V]•2ářÜÉŠ™çP1UNńsŹ?µ}Ëö˘śôˇNZˇcU76<±4@ܰ´űÔă#âB¨xŕá·veîű„”¨ŢA1÷?´ń‰Ç›35U)Ů‹  ť0kćÔäi·>őÇ'nž”ÜsAeUsçĆŹŠ.˙˘¬jög~ôřjÜŘŞ¦Ť!> <Ü' ô…Ďeýňe°o@X¸ßŔÄ©ęŞóCt*<%€úÝošZ[Źlű§Fí·g©Ź:¦Hęŕą•Żß;®k]q#Iéţk_Ýś{â;ŐőC@ăęźäë9˙Ţ őW«¦†8 xFN~Ţ‚ÔH§A>9îž•·÷ŻméKRć—Wn0¶ÔM pŕ˝SjšÍO­ťŰý®˛ŕÜ7+ÚÉ˝+%čĐşí¨8 IĎ*¸wÎp·.»kzrČŚ•ëŞJ˛‡ëxݍ±Ĺă?6®R ÷]Ę? Ž™6y€đI÷›EqŐ¤(źkďZ˘ŕÜllş{Z8‡řˇCé4ÔUO Ô¸ŹwµŮ ´bHŇ( 0nů٦îń±ÓżŐzßéNĺź• ÄYóÚŕŔąnnăÜĂÖůsš€Úµę¨ÖĘŤY%C.ěĚłPÖZwĆč¦ÓGk„j“Đ*ŁĽţô˛óÖßXŁßń‹ëťgĄT»»|©đÚSĽUąD:‰EŤŐđť7Hj•őA:€|Ă1F4^jä<ô:Ű)ąň÷(wŻzCő®&˛dpX‹ Ĺş¸`Ä:ň*ĚfS^]]i‹q¸_pYMѡFIËSpęŐĂźJĐŞ”óJŽŔ(‰·KŮ4!Ĺ›“$SĄ„Ť÷ń2›ŚMÍĚ’Ěé=—…{•‘YËŔiśŢš2éŢÁăCžHŠ×s]ä@@ç:Č‹—Ď×T˝G îďD”˙úé ¤˙“â?ŁşR§s‰QQôÇŞ†É€ÖË»SG%˙ý‡*ŤL5ľ±ě˛&fÖ·ßl‰ń¦µFˇś(1>hÔ=‹Ćýçµ×›€ü3G˙µik3P^‘ßÜҧ0Ě˝&JdㆤÖĺý®»kőRY6Ť@ľ–h_Źl6Ő—Ő5‰ć–ú‚ňZ‰aŇMZ®Ľű—H;şëč©‹d™uŤź˘ä(oyßŘ-;żß¸ę–ä‰s¶o˙vę`Ad8­†S–DL"Ρmůć‘»n™2{á‹/<áě@f72K{ĺ(€„±Ó·ěüióŻÜľô®—_}ÚIKA;ĄÚňXÇ5YfíoŢz ŐCNGbbEA^EÉ•şz3“Ť%ĺ%ůy󞥫ÎËJś™Ýrď ÓÂTź|yHďěháü·g©Ż:¦čÄŚđŃ™Ó΀ë ăpýIŕlÚGżč!zUŰčq-‰€©ľ.'3ٍĽzä”Ĺéßý\ĐŞŐiŕŕ¶ţÉ_Ţp ‡#€¬SGţ˝é›ţµ-}b1ŐUµŤˇŁ#˘šë*KKË[/[]dÁQŇŇ+ŞLÚűÇ™ˇ]ëśśő „đŚ­-+˙řÎöOŢńă©Iˇq;ďÖicÇšę«r‹Š.ú. ĺmmáĄý‡sâb‡W•6¶¶T\Jű×§ŢÔ,ŽBj˝śy^‘Né™mŠMs&Nk(ÍVZípµ¦şňjSwřŘé_˛ ¦ŕb;ő겂«Í&í™<˘­ÚĽâłZÜĐľ ŃĂ%±îƸq c¬z\kbôpČ­Ů/X43műެ* § lЧń ÓĆ;´´Űůă§2Ě<Ű˝ď»sWoť:€ŢY’ÚiĚ\K»®”1öć O=ó‡Ç6iŐâ“gÜłhŞ(ÇĚ_őđÚ;cµUE…eĹůíV4/·„÷ŹŰňă®g×/_|ßS{~Üžî`čř›Ú-˙«Ż= 6uÚÖ{6đĘíËW˙ĺĹ n!Ń›·ďx|Ő-ÉçěŘńÝPgϨQ_nÝůĎ7žY¸âľ­ßm⦠I|ß’iíÔŁt°>ÎîÚ»Ű1d–Q¦‡ţ…öodŔĄ“‡ŻŰđ€%¤ĺ ĺµďë¶Ň#˙ąËüp7˙vmµéůÍ'šĘĺ¨÷bDĆIů†«G[ˇŐ‰Í˛ßš­ź&ső…Ęs&÷ 5ç·ŇźsW«8±±Č¤Ôŕ˝Ŕ[UßR¶«uwpŠP—ż]÷e˝Ę‰“ZíIëÍS ±Qy,eËs[ŇŤpŤűS°c şń×ęÚ łĺZ«í7ť+§[úÄł­^bmýBُ&Pcl©»Č”©•ůŢ8b`eÂSŤi̵“óŞÉĽzx˘źXw¸FÔrvFmBĚ‹“Ď·ĎŹť †”U[]e0Ľ 2hîw?\l•[DiybJ€“ăšďÜVŐ8Ů5€pęeC†čµŽîÔtĆФÔ6?:ĘŐX÷in)ŤÖu»~óÉS/d–ą©ąVŮľwµ ˝ZMÁlĆ˙KݤFłD)ç«'çë@H˙Ěş$Шův %dżÄiq/¤-?î˝cöM»3,YłĆ­ćÂźď@@UT0Ë^ŻżńJkîţť?e6O]o ‘šş†ź~> ĺüľ´V­úşO°e˙‚Ł”Ĺ ›źˇľ(łQ@”ç±J`Ó.°’ĂqD1wÁĘ›†űţĺÁ—ŇkđĘűn\˝đçőŻ6×ń|ă“ă?yň‘Çßůi«cł‰ Gť2M‚ Ş6,Ľy[Zn@X¨I˛_ĐÖEIűAŞÍ^I÷ĄzʱĆKydG8e꤭ösŢkĄóŠçŕ/m\üĚÝ·9Ź˙ăÄ0A° 0`{+˝čâř>4ŔMCĺ†úşâĘ3 ő­śJëÂĄ]®ĎţJ%Yr ›´|Jüż}€†/ś9Qž_™˛řľĄËćN?ń\Ať–—Ťż‰mé))NĚ2÷ď9SôÔ»?ěk„®áâźv§°ńHaO}´˘íOˇĄ’(놷kݤpA(ĄŤÍĆ{ţô÷ ŕĄĆ|y,F´†h\VoxĘŮ+hWs*§P©˙šdA)•e)fÂŚÍĽâ}î§/~<^NĹAa…3á͢¨HçŁ Ď`ó ·´â«ť?if%mÚ˝ý|ÉźzŔ§  Ěšú®ĄŕTd«oÚĘ2Öţ›RŇŘÔ;˝îv•ť'Íüđág$Ĺ&w˙lG ’ëneç)Ç3Quź3'ů‡żÜDŚűןxox«R°uUb_ů$ęŕvĎFK»ŽeřäÍŽgťűńo‚o?}i÷ŃsgöĽő—5ÓľĎlá(QńD-V”§dÜ:wŐ_qt̰LÓŕěĽSwΛ}ćőMfIj·üÁQ‘Ńl6TmX8g[Zž“«ë-«ž1Âďĺ‡ţ’^Ť×>ühăę…ľ«[žľ{ć—g}ňň÷Nš1âí/_söŇ©ßh§ “WIëŢm‹!CŰŠĹn˙’d›‘r*ž\çě˘cÉ@,#‹Ď’@ťłL}Üb5Ô]ë=ŢrË—đ„pD944ÓŰ9\z4űü#9đuôżĹF N?؉Ė:Ę]µ&óĄ»3ŽM?W}…„ßá ®şŔ[Ü«ľŞ0Š ¤‚Ʋm ľ÷FŽŮ;<âNoÚ Űbł,ËŚ¨m5•Áú¬ˇ%˝6mńąłŹ¨»řNuRÖÖߥŤ<éxyÂí‰çžmlŁÎׄĄ” «CśýSňß)A)%c<íÄ,3*0‰IfiW§.#Kޮޫ\ţz*˝śĆ˛ HxJŰO'”¬©á!Ră7EešĘW˙´˙ÇNÜĽs=§źŕ Y$ś*v''™sŤ­PĚĄąiýÎŘţC=}zHh]×DíĚĽX"Łhi¨ü4»lŃđńç—-xnhŚĆĘlµË@˝YS ĚM”˙›‰ŁdąŮĚПދű쒩5ĆĘK÷.^yߪű˛ę§ź€zÇ?˛rîÖ÷ţV$ăyI̢żž-.X±®Rí§č˛ąŐ/züý3bţôäSőDŁí[ě­ß·Ë16ë`EąU­uv¦–ži±Č„đ*•ÍIş%‡#˛Ŕ78ĘPS3fńš§6˝B‹Q4NO­¨ŰÜşw·a(µŹ)Ęëeűý‹ă¬Ć¤ëź]XuT‰ÉS0 ¦¦"Ńďv}(Gś´ž]h\‡}ś󨟲BŐú8°fSőńFĂÉşě ĺXî ąŐÜC KډJ4ÁX)¨}5©đ˙o’G¸Ţ™2sžQŮőďĎúQ6 fĹ÷Rmkť()F’ ’¬\©_°řţPZđîć­ K xňÍ÷–Mr›&öAźA·řL:?zčk‘C˙?1-%|‰Ŕ ß–łÎá .Âmř¦„ĐeŢáOGŤß—čČŁËH˘üüŢħ-ż­xNţ$Ö{¤ł÷Ü Qۇ„Ý0䣡“ŽŽđLĐ$ěVíšáŔiR[+”šSöČóR‹Ź…ţÉXJ]3bĘţ¦§.yWŇđW"bßżw¨ŁŔëĆîÄ2řáÞ:{îQ .Őąî]˛xÇ”qďN›ťłěÖx'5€!ÁkXwjÎx5,KÉ!ˇ±ŐkîXŕ«řHĂíń)çÎŘ0dč¶ŰnË^:7VĎşť[ąbU ‹ĆŃăŇę»ßKŽžSľfŐ橾=»dőЧâŐŽ…‹ßNŠ@›o1˝{ŔŽ[oŢ?ř©qs—Ďë¬é¦e…ýęĚ9ߌK`:a“8BLŹIřiö„˙MoŠEzmöÜŻÇ ë? ¨ž{ë?i9MŤő'ď»îx¸Äf”5˙ú?Ż˙cVNŹýz©ęăWPiÔn[óc,ëĚŃĎ?űúčÁ˝·ß”ڶÍÎëç†PÔ/.˝°úČW›6}sĽľôÂ@=®Ń{Ä@Öcńb”0ýëí{˛ ** ˛únËŚ„ŔůĂ˝őĺąOo||ßń´Ç×.4o-cěĘáĎ585€asÖ(9ŠC“€Ři—+[O|»éÁu>•ľţ®Ů •éËź04ŐíÚůÍşEcôQŹśJ{îá{˙úá×†Šśi˝Ś¸őľîjÖ„S÷ŕs5ĄgyŕxU§o:óC lěÂŞů›Źßyó__J­5˦FHšŰ-;9vv0ň:ôO›Ň*ÓżĺěóĄ.:pď gĐéÔžţôteÚWĆRźuU™Ťŕź»Ďě˙đ é-Ŕ˛ţ`ćŢź˝nęĘĚ~ř‚G–OU2W>üö…SžÝřěÁ E 噡nęŮ«žďwŰŇ7ö8ŁćŻo–ŮW˙~űő>—™ń±Ű&ŕlNϺȂ^¬¨PçěačÔw@(ÇiŐ€§?=]•ľ…D7˘´®uíĚhm`rŤŔ>űËşY«ÖËŚí޲y×ăfŃřňĂŠußeańgřđ»Ů™§^xćąď]d­eS{L_ţ¬5΋gŤ0rácíҡ8ň\±ář×˙yýź1ąqajHĚ´{ڬ|:÷Ż´_÷-ź2ań]/çdĄ+ÔĺĆ’yŁ‚î…w>nďë—Ü `ř\‹…Ô*ś;&–\mé Ť‡¶ ®;ŐíÄĎÉĂűÖÍť}đŮŇ«í=.1Ä@Ňmʶ·˝w}ł¬e=Ţßsľľ<÷™Ç˙pŕřÉűW/ţó;ŢPşîóťlĚŰçhőuפl0ÍąďííDÓ_ś 8?ô‡g˙»í§fCí¦żýíˇ5‹xÚŕŁY•™'~ţŰ Ď¦Ćú+5(VT™úĹÎ,Şv|úîß>ţV4”-ඇžo·üĽJ›±€đ·w~M?ĎmŁ‚&®2*†jU|Ełôěó¨ôˇť¨[ۨ®˝[­¶S8mŰż6.š`D›Ügo ď˝ţŮ…ĺŐ¶CÖ\ÚRűkąQjÉjn)7+1"™ČZrš/·`"3—kŹÖ7ĺ®47ť‡8kt¤đźEe?ŔąIhĽŘd,5Ö_2r*4g6·”šÍ ˘ąÜX±ĄĽř»zĎĹ!AS5ŮĎ䛚A0ŔÄ8WŢ5ŮEŁ– Ţ-¨Í4u˝ĚL(±‘…Üçßx¸şµV%í<›š$cvsS‘wç#ĺş–+oTź1‚ŁÚŔ.í’ąÔX{´ľ)ϨT.·tËsůćň’ďë@ bť]ĂUĺß_e×Xž„ Y¨OovJqQQ!÷Ą+†B‘pú1Ŕ1Ń3ň÷Ľ—ň[®J]ŻŘRBdÁřkEM‚żźi}ţȉ“ő-DYŞl2()˝Pרô}_­6ż¶ňŰ+ĺĘÓł wgç„A.ŐµĎ=uĆ`¤„´­çkjK Çk :ŮŐµ9ŤMŐFSe“áëK™˙Ľ\23näÚPýţZ.X„)‰˛V“âçëMĹ7OźÜ[ݤÜěd"PlfŹŚHČ(Ę+hę“(! ,ĚÓ;ŐŐዜBůlUC ‘Á>á/ zńȱĽVëGp ‰Ś¬ÉĎřa÷ľŠúşüŚŚ‹ŮçöJO25ÔMő—g6~˛ăäü^ô¶µ«V—YN­v(ČË8“qŮĐX_VV’~&­¨¬ć†#™0J‰l¨:p(-qę´A´|Ă}ëŽf•JٵmŐ \=JÜ57× ×ô_÷9ťQ_W›~.˝¬¦fĎŹ?š4îÓ¦Ź-?üł/ż­¨mSEEĹžÝ?žÉĚłśřЦĘĘĘ=»LĎĚ!†ęĽźöźHJ›”•vpË7?TŐ(AnćÉËE5*µ¦27ătúeť·˙¸‰“|5-Ď˙顝'ň‚Q©§kÍg2ó”‡l˛(ç]>yú\۵›o:ós!ŹÔ]<‘‘“”:ŢW/˝ôô_ţxŠ‚ą'ZÝäŘSÖ٧ÎgPµZmíÔ™çyŢÖËóuĺđ<Ď)ÇßÝę|C}Ι´K…e˛Ěc‚`Ľ|ţôÉsŮ7ÜŻúYW Ç3&_©ž~î‰ôď7ĺU6rr/ ĄśĚXÜŘĺ{lŢkďË®4p×ËG1ţ^yç}ľuż €Ydža‰‰1Ug<¶ţŃł…5zťÓ•Ľóým[ú ĆPrétzVÁČŃcýś¸Wž{âŻ÷Ę ťBéŮČâ‡My•M”ă‚#"ş±˘ü Üđ鎣]úŽ%ę+Ú´. `­ŤUiii…ůهĎ^qT±ĚSé— ŠŞjkĘ‹r>úűëďö˝(ł¸qKŻIڎµµUçę’0ląęě3Ź?ôĂÉ+ÎŽÎWň­q>]XZŕé•sîŕç[÷›%RCéCińS§…ąq/<ůŘćźŇ«óĎźî źNý+÷LÚŮě\'/ŹřˇCÍUgź~üám‡/W‡‡wôÁô´‹…Ą’h®¬¨PěˇbIš›ŞÓ»Gă˝OwH ˝+†í——qţbÎĹý‡Ň§LDË7Ü÷Ŕ‘K%üyZ4“őí.!@Ëžľ7kMť6¶ôÜŃ-[6¶Vűő¶Ü’JUeW*ŞŞ/śJkQŽË»Źh6—–—×Ô”ĺ|ôîďţ÷{¨Ťź*Öć˙°{ź Ó ue'Ň/ČbĂńc™"GÔ.ăě©ŇšFXYQBHcuÎţ™Ł'LĐ‹Üp˙7G2‚Ťĺg6cŁ”Ş¬Ćťô[¶ţPU×X_^pôÄ©fYlnŞůőĐÁ’Š:Ů\ěX¦dEťÔąw30Ćë4¦té_[öČ b{ż¸'3hÔş+×;» =ýËu»e Ŕŕ2ŢÓŃŃ\¶«(”•@_éRâ5ĂŁ5«ˇ1ßÜ;{ÝńK>ˇ`ÖÔ»ŻP‰řé±$ ü&ÇS«˛ĺh?GůŁÓ¤7ę}DL «wňç«öÖwŹŠ ŤžńłČíRžXEű˛«aĘ;é” HUËçyeLY^ÓXDčÍáˇE5çë›)Á€…ůć•<3nč“á¦îŘoČ|}AE ęá9ÔYł-żä7‚`ŐzE co^ă\úď/ö2PJ-Öj¨wůő¦˘„/»FŠöşQĽ^ZÇîD©/¶š´](Ą„ô]x¶ţ!¤żŰ+ń™,rL–ô±3öm}ßť¶ ĚĘoł<ŕĆuć0FTňŐ{çOú)«…ŁDęQ„J)“$y`(´űŞcT}ŰâEWÎţ’v±ř:Ba^'w”2YŽN5"ÔýË-?öG`B8 IęZK?6ŞÓýCĹjQJŰś-Yč(r‡â‡ ¬Źceϲč,Eű5Y8ěÚí©}óÁQ*ő‹,şS2ĹĄ"G!KŚ´™űvDaËÖu’·Oť Í‘PçéR7hÜ€U·™Z×C9Lęă•§ně<ˇT‰E~ťş¬h®=zÁ»‹Ňő  >.(µ·Cd‡Áý±‹X_ű×uĄ®×䥵 lŔjŽŽ^é(؇ŐXj[[»ľö2 č¦őťxV¨3ĆŔ@8ұ˘{Ž»ă„0™ąNóňípńĎ…¬Gęv*éŁ$ú±¤Ýě¶WLáaŚIö®HöŞć”J Yu[ŞŚ¬3ËĘ»'™1‹żˇî+î¤ń–pF‡E®ôwyŕđiqŕ§5}NżcĎ+KfB,IÖÎF8Žk˙ `Ń Bh»-žW1&ő[sB(%˛%^2eňőNr~çzĺ(łµě„PŽç ËmŹIěäPĘ0I–9Žg¶+ĎóŚÉ’ĚxJ%YR–—J}¨ŮŠ3ËLĄŹĄ:MĽúRŞę]ácĽ>`ŢśÉZ®cÝA(G ŰřžşŽB9f<đöâúÎnşę|»]ĺxN–äßúd }Ô1%âřŔ1f‡~;3!„‚Ʉ㠓¤¶ `„ň… x•ŠÉýg[lČSÂdXmáJ!ËĘ^ś"Žç™$u' YŞVń˘ ČŚń<ĎdY’Çq’$v*b·Ů…”r<%D±c„Ą¬Ôűšdaĺ\”ă,[ŻĄ_€6Óčnö$ ˇ„°ö ő„Ů›ą[َâ™$1PŽÁvk¦‡q§+ő^ŃčYÖ]¤cµB9Ć$ĆÇQYşÖŮ[';Oxžc2“d G9B‰5ĎÝ×B9ŽłX(Ćz0›”rĘ’IjÓLĘńE(Ç)‚¤”´;Üă(µŢëšC9Ş`Éq<“%„ç¨$Š „ç9Y¶‰8^YęĹ)Y›{ ,´ş|C)Çq´m¦Ięë"m+chü´ÎńŽRťą>˝QîLžşŽpć8VwĘ Śđś.LKy€fšňLŕTľsÝOÔ6•„'LdDŻr Ô f”Z Ť˛Č¬–IDá€:ł±F$ÔâąŰ…Ź.ńí’7r«N¶Ö<7ÉJ…a:§0­d0×§5ʢťřAj_µň¶žJŤ&ń]0¨ńxO<[CâśÝŁTĄŰjYßłĎ9ţ&1×é>‘KÝ2V_2v3'ˇ\˛ŻŹžČGĘ*L]÷ř89Ťđôh66­¨1ËĚŐÁ1ÄI§l|R‚JˇÜD–Ä„ž/-Č4xJ%YćŐÚ(g˝ŠŔ,šsÍ2S¶ÂdĆH€»+Ś-%-&JmŰ-¶»ŞQ 4Żw˙fúŘŹOŢ^ZÇ" ÔÄÂrâé=ŢÝá“ě‚ßsÖ`/)‹®Ô°č?ÇÜůýĎĹý±YÚ3ţ!áŁR’›Ş :aeĘń”0I”ˇÁńń¨)+,Ż Ť™<˘ąşčŔáf±ß0#QiĆLěÄ›öď˙ĹhľÎšŞ0•Ć-6.‚‡(ňs3/Ô™0„D%Ž_vůä‘SŮë“СG ,1I–8•ךîňĐ‘ü 'żünź"yeLŔÜë„O¨‹—_x°?Gˇ˘4·¨´·Ą†Ą”›—°·ţ|F¶ — _-**Żë‡y-áÇĆ9ëT’$ŚăTBkmĆĹĽßbîÚWŽÚ¤¬çL\ż¶Ü őtU™C«‡îŰńßlXľyß…v7Áżu˘/KâÄŰ×˝őĐÍó&Í,0_ç‰J»mI™dm7ÔĎIÓĆ;‘ćC?íŻ2™o\íőög˘ Ěľ+ ňŠkęCâFŚ oŞ+ýĺŕ1“ P»úOź<Žk­Úýó/&LÉá[«výü‹ňMeˇőň öpĘ»”Ó6q Ń)c‡‡ů^8qě|~‘ÝŤsĹ(üěýwxUUö?ö>ç–ÜôŢ{!¤@čU¤(Uş€uÇ2¶QgĆ>ę8ŽúµŚXPD¤÷"=ˇ„RIďýŢÜÜrÎŮďç&ąéÎűľżő<<$;{Ż˝ÎZkŻ]×Z~‘qÎ- é…ĹDá¶rŐĽ´[.ćŐ( Ńlć=˘Â•&}ÓŐě\łIŻW­ś‘ś «.}Ş'yuFL “XW;ďëN.f•°~_¦sÔ˛§â8ĘŔ…GF·ZHPŽŤM—/g™•ĵm3Ąň[}J<Č%­B—DQ’ç’$I’$÷e© I˘$J)(ÇSůI¤$u>Óé"ť“]ÇEżęt·ş¸ŽÝOpžć9ćdňČ_‡Ž9š˙÷@e÷94äąAcŽ&ŚÚ?|đ_9€9%íJHŮ?ę@bŇ”*Ű”ĂIţ“íPŕ<Ókô±¤‘ۆŽ>”đé [/ňˇ= ty Áw˘][IŻzŢëÂR6Eq<8ßiEóA<`3Â=ĺpŇČíńcŽ%Ĺż¤ęčÍ*mĚ>ň­čőż’}…—­{żAד™U]_˝ă?oxË!Lújµň©O ŻTŞ=W >۰ ĺo8óOëGi"÷Ąć–—ć–ĺ]8ă ¨˘ŁźĚ€@źa_H[n”%ýë]^ÍÜ˙÷ŤEi»m{Ś­z‹H¤&<­°ö­ďŔő©É] «m9ôý{žĽ#ýýrUyQQiećÉ]±¶€úď˙Ů~3˛ŕx `á#oW7Ö]ËËÍÎĘÖ·čž_y‡OâÝĄ Í×ňňjšŽl~ß ° Ű{.»˘¸ Ľ¶fßď:¶í%{˙ďm§>eA€ňžOľňANEMaúŃh[KŠÇ^ű¨A§/(*)Ë<9ÄV.(,±FbĆgT´śýĺc ša…5MŹĎK R*L^ţXe® '»˘şöÜÁźc\p¶ý—EwśßPş3ٍş¨ ż¤˛öňńź8 |Ęň˘š&™?ǶĽoĚ]űvMS˝ĚC]sÓ+͆Ú{űéĚVyíŚń·ďř]ᮏ˛ŁyäÔĺEŐ–ŢŹüř›ˇE˝sĂß©+W;)o'zb4¦,YŰ>Ç}÷®ë^˙wďňęň_ç<ň×6;˙ÄŞé”¶Á{.—g˛áúDҙܞ,äů}Q¶Jklý˙ëum%+ˇÚ}Ěľ}ű&„Řt%¸«t˙đžOÇż?uz^] ´©ŐÄÚűĚrĺ»Ń>ăN'ů¤h«ŘʔФxŚKKöŽS»Íöź|u¤w’îŽ#vÄ»QsŽ •jMâÖaľcm¸M÷p‰QąĚň˝g¨˝š(íS~OŽXá@Î>đL䨢rXwoß%>+}Ü’í» @ˇJŘžvŹ%"™m¬C;Íg’Ľ“ÔÄQĺ:€M˘×řłÉľcl;|…ü)öĽ&LŁvW¸Ď÷{$Á%€±Iüµoš¸Î÷MřŘÍ6Ρ7ŽÉ1Ç‚ś“·µđÇ‘ď\2 “»ö}"|Ä7şę†¬ !~á,_ěa?uĐІ5+–ú»Ŕje,+z„‡ç˛đ ë“ßŇ%ľN \¤«‹ŻF==*&űŢ…ă€ăÁ{ćßŕ `ZXřxw»‰‘KćÄńÄÍŃ3mĺň Cë†gMʤ‹ó¦şđ°s\űŘ©ľÝ ™µŰµpáKŃA¸ÎXU72+¦DĹíš>ö3šĽW]™<îÔě‰r“[A!ˇ@TŇWÜc ťţ”®E÷Ŕ´8*ŕ©·W¤ď·ÂǬ^±°SţćâÉ–1bÚýzɸ*9lÖ}Ż0&=0}čőbľýx”j•“wŔßż=ţűŹ˙ňvĺ\§–5™˙űÖ:‰Ł¦§$FŁO­&€Ę-řľG7ĽôüÓĂc‚(xŕ‰÷veřŠĺ„Ć3 jíăžyú©Y“Sä–˝`•÷'«6|^š{<ŘŐnňňçô¦–u Çŕ{ŽÔÚꋢěĂň®ć`VáO?€›ŰŐ´~)ďęĺüę7GŻţ>ĐŰ/$ÔçŹĘS5PÚrK{—×:PžÎ­|{í=¸ˇ}ĹÍ€Ţ}Í››rO˙j±×{BĐŶ´ ‹Fř ™ű`lďgĂ{&×ÄőËĆ8'-01á¦dA@ĺäŕí>÷ˇő u•“CUžawL tÂZAŤ˝ső»Ć¦k±ţ6aĂ4›Ś Ç šľúť¶’&}ËCw'ô) ±łOś:»uóÖ˘ś´!öjÁŁVkźY’ fčpWµ ťŞKMůŻźŹ[ZŽoýDŔfpÚŐ‚‡f p÷Ňű¦&M[ńHUIÖ0 ď1ş¸Éđď +ĺĆý—EWÎ7Ť‚ĎäÜkyóRÂí]˝|]\¸FM™8BćŹÁlZ=m0¨Mě!2›jF{qĂďYgniĺUÓlz~Í,Xť×ČKdűëä†üÖ˝ÍÂę;ˇsŠúä׳Eę†;€ävnčM-ËÇ8 žUŻ×ö*Żn0»FÜQÖdjłóŁG 0čÎűŻUÖ7Ő?8+©oŐ•ÍľWŘÜ9łť8€(îš77*ĚĎÝ?Ŕ' ¤ÍB††ůp zhÝú§źXě)·ŚŚ›~çäÄ)w?˙Ü3ł&%<}úKÉÄ$*÷Ëś@ă>ý®™“ďśűěóĎÜ1<@Dtâ#/~XR\ňÖS.[ľtż«µVu•ŽÁdzxF‡QŮŻ:=Ż.ĐŇRúËÚ˛u"`Ş1-ť4` ďn/i«+ŕ=ÇU4HvŃđ~÷ű‡®đR»Čgą„PFÉëţ Á Rń`’sśŕâďŔ´ć– ÉÄ—ť÷TűňoĘÍ ÄAőÖ ˙ŮÎÎc\BńĺTí][‹ś ¶UŞ%íŐ&„˘ůrS;ÍzFxĘŤőiŰŤ]¤Ę\k25­żBI+´äé Őf‡$Cf]]‘%¤ošeôĂĎţ;wę°ę⢲˘ü6 ™—[ÂűĆlŢąëĄuË=üüžť[ăC] {Çćí{7}ňĆ=Kî{ó­—D§LůiŰžMźĽqϲţöÚzç ČM[·=˝ň®Äńł¶műuT°{ÄďÚţźw^\°üáź~Ý<ŘY7ńáĹSłiÔÜ•O¬Y UîŤî¤S\][Vź(ŇĎ:ÝŻ.Ú81EŘSRqcÍI=Đ1>łVô_íŻ2čëŻ0Ş %ÝUť©Äě0ÍÓkşKÚŠt] ’Ďę µŹ*ăË•Ť.AJÎg…/ç˘Tp‚¶Č(#óśç©hĐ—íj@]lěĂ”ĺďä}ß °ç$s»I˛µ'O)­śQ¨ ͧől†9Ć<hëŻÔţ^]›nB×tŐ”0‰ń~žÉšň·JP}żh¶†~qL’šŰř3Ă%mizłAlÎľ5# •OU>D›ŰýKÎ:Łéań>Bý±AÝz.Ę:°‡rĚPż4b¸±±boi=@Ls´÷šçëüÍÉ‹ PS˘ÄeńÉ~ö¶«ŰůK•v˘“á”K¶SŰşPăů&ťŚmnd„“ˇţëÜR*µÓP»MgÎľšQć¬äZ¤Ö@'íúŘ)•„5™LřĐhM"Ąś·ąTBn•o ĺ<1›$Ź·ßyŁ%÷ŕö˝—@©$IKWŻv®ąüÉ·»Pµ®“@•©©o|ě…Wüô—¤¶¨•7|3pńČg@Ą”Č‘ťm|šŠ2´B9ŽYüS»NŔr ÇQŔěy+îćý·Ç^O«Áşáű×˝ txŢmă›řŐłO>ýÁ^W'Űf#A­Ťf“OÜîÝ{=ĹÔeřeűAÝúÄw QŇv‘ÚᬤçV˝•XóKv˛#%ň“YK@‚ŃŔ@iË-é]|ě笢RcCýÁ ±ˇ…S¨}¸ÔËćł?lKÖľ}żUň-ç®i˙µă€šćě#{OdÍOŃT×ÖÝĽ,Ą˘$:‡LX6)öł'^”=0"Çݱé“w"‚°ţyŹW®ćlNˇŚ˙zeaÍyCÎÁí‡ Táá—RO—ĺW&/zxÉŇŮÇO>ź_3áÎď>~;"ČóâŢďvž*ĺM˘`áá“/Č<ş7­đYY^ŤWíÝť tŽÖu˝Ü@)•$1jÜ4Y:—ömÚy®–łóoÔöÍŤ>Ď@;ŇC`ń°oĺFîßÎÜ•¤ň 2%ćrOvV0.Vvžr<Ě .łf%îřŰÚăBĚgťçąţ˝JsçP%ÝHG07ë´"@uZ˝ŃdÉM,[HžB”p÷ě•Ă˝…‘QC3ڞňήš3ăüŰM˘hnŞZż`ć/©ąáĚ‚ÉÔTµ~Á¬_RóěťśîZůĚ´á>˙xüoiŐxëÓ/6<°`ýŻU‚ľń…űďüţ‚W^ţľ Ó†ż˙ýŁ.džÝůÎßVOů-CĎQŔ:bd'éXtD˛ŠkŇź:čfu1€+@ľ«a"c" űk¸[0K˙Ë5“%§<OÚ=4Ě’2ČÁ7Ĺ&˙bjá±ůňYYo¤®Ě2«U.#íaˇäěŮŁ Ż7C^ýëL™÷§źśz±ú ˝×ś4ţwąTýPa@8Ú˛_˝ µoXŘ*OÚç Í’$1˘lŐ¦N4‹>µ6uŃĹ *Prôžl/`ýňwąNu匆ʣŤ 0(ho4/woŐŕvefRŻc €XÔxů‰«ţ¨”®ăXYSĆ“·†c–ČÄ&9†FgJ’čéäąŇĎńďgÓÁ©,'O„§´ív‚1ÉĚř—ĆŤźćČî;|ŞšAÎw>94ČFÔţRT.ב(íęΉ¦\C‹E•MşuŰKünëŽúÂŕ0P;­ŽŘžqĄDGˇo¬ü:«lá°±—–Î{yH”Ş5tc»Ń4D1Ĺőĺ˙€Ł%©ŮÄp W€„‰f“ yëËgE ó–?R)H\#ź\1ű§ŹţY$‚ăyQč\‡Pö ÉÔâ9ví´¨ż<ű|Q©ą|is;ńX"ţ±ö VZ”jj™«MŻPt¸m·”pDxF4ŐÔŚZ´úůőŹÖćed–•ÉŻ ÁůN–P賎ňůľ/n*/+xűĎŹ¨%K ´ž0ËżQĘ™ZęwíÝÝĐl27•—6„ôŢ Ś0Q˘ ś(1Ę,7řŠî[őZŇZ7¶jň׫źRţ{—oÉęµFA’Ş[~ÔODłYWßý,Ó$Ö¶eÁŞÇ*Ë׎ Uß7oćŚ9‹Iŕ°‡çŢŃ\Sé3áćeA 0ýžůcţwŰĘßpőčŹ#‡ĆN_đ„Çŕ kç$ýúÉ{˙·+ăëműß||ZMuµ“»óî>ýô·tमşš(Uý’…Ľ+—¤¶—;J•Ťˇ2óˇE+^ąüŔŐ†Éńc0Ę) žă8¬\÷×gÚ?f°@ŹđĹÓăy©ERŘ ŽÉę˛j*pĽRj,^1.)Č7jËUĹO­;ĽNYtŕüÜe×BţÁywÍZűřş‘cćÔkďś űŘO2Ľă'®š?’ĐĆĂv0}áę+y­Y1­Ú;*K˙ąaq‡•tĽâ'®šť ꉺon}ľ·˘G®Ű÷>bě5¶úŠ.öuChfVvL|†Ś‹ötŤšo-Ç<e m'[Ë%pP»Ö”ĺÖŤ×Îfą©ěđJ›ŞŇĚ=s”ćP«ä’IŰPç⮫­µ°mN)Ő8h´µĺ—ŇĘaÖ̢ĘŢ ă)#L~Ůő–˝“tĽă'®š7`Çó=H°K®«ěĘA* ;đ©`Ďţü™ÚB3ácPÇ9ý2.ęO>ňÇ™tf…‹˘â뢺\fç§4ëĚ,ÉDÄÖĐÉÇIąŻćVWp_ "€$2&Čá­Í†JłŇ[ŔýO51–ě®…e"ňßČ:15-ű«ZĎĄľî‰*tň‡a`.22©ü5mâ·¦Yv ‘‰ÍBýŃZł™hm¨;Y}a"á˝îpi:T§obDZŢhöě:0°ľ9ÖžśH´h$Đz?1૬˘ oč˛ŢePijqQŰüt)őp˝ă ©1xřnš5ó±ňŰ\¬‘<×G9wÇľKŤ-”łÄ@TóBüÓ ňŤÁ–Ż?¶K§Ř8>Q%’db 06·xÚŮ2(€čľČ+”i&0˝qâŕđď~zîbá츸>¶čÎ…®®Igďíŕ$łpŔÔü˙'@fG¨ťe¦<|ęk:"ŚĎľűŃŇ Î'NMÍ®P(yĚ[´6ükÓO ‰ťęPJű{±'_Ů•7Vş::o|űŮ=—MCB|*›jĐz0ö?‹GţE2Lz .ä\töŚś’&RŽW(•‚“§ď9vî«wžV <€ ¶ †¦fSÓµĹSÇŹHI™0n̆w~”äPgÍ&‰0 `%¤á…ułü}só˘'ž›?)@hJĎ!ź sښ·ß}wŃ’ÇĆß÷ř’) I™ŃS+łXŻŕmT $O ÁÉwîîJşYsX¸'Šbk¸OůőÍ-ŰŁ-7ýí]bŞs®™‰­oX,ng¬Y<„NƬ#¬¦µëBbm7Î\)ä;Ȥ-ز˙ä™;2ë̉áľuu%ζßܤ,E Ôe٢»Oý˛9§Aĺ8^ÁQ«­Ůżí§J ©ęń…“˝üf<ř'“D®Ő” O,čŕ7ăÁ?™w­˛eý’…YLf“ł­¶Ą^e.1ł(ÉTA­c‡—@ë .çÖ<¸öáˇQöĽc𼓠5śiĂę{)vŘöő{ Ŕ H‚`2 "P}­¸ÂÓ?PE ňzdŃŐŞSJü(™Ĺöb+Śü °A ôyxµĆ Č/˛M^WëLńˇÁ¤î´±˙Ü­o%ť*#?(,0Űń¦ő«—÷ ^–H?FLŹ IDfĄ‡eŞ›k­)ěs"1 ŕbv»ťç<€ńŁFRcĄşx(Ä2=’GMŔ(í ›¤TÚ8Ú˘š'0›Ť­ŇQ”O“Dj´sps q öq¨üvI$ËćKŢ“0I˛”PŠÎsĘčg?Ú-)ě$Á$Ć•DQd–s1•Rië A×ěĆŽ‘Ź hD;Ď÷Vg;2ŕ+‡Îŕ>ĎJá›bŁ_ţy”çh{öă='ĺŹKů2\ľ@$îšÄ] ĂŢ‹~'vÜ!j5<%ĽÂ/~˰1{†ŘySpšQÇ’}GŰPO»1iŁ˘ór˝ËkÂĄ‘CŢ ňYěřÔäĐĹÎ7ě—„¨GÜžŕÂś‡mŚ ^ęúBÄŘńÎţ<şĚňo>˙‚ż\â1? •愯˘]˘5îsGüňßŕ/†L81Ě=^Ýá+`y$h?É{búŻá–Óbc›r¤Wš—Xâ¸-đMüÄ «Ţ»áĽ#őZ”ôm´Ěź±ű‡¨í¨Ç=·Žcôéˇ/řVĎ!ĺ?ČŃ 4Nű/Ú6iĚż¦ĚČYzw¬˝ŔŕŔAµŹ>rvÖXł˘†é{č÷ąÓ˙=aüo3§ÎöwS˝úŢyŢv˛ 8Ţĺü˝ËVú;ŞlÝ2¸˙ŁÄČIQĺ«Wnš<î»3JXţ|Ś? Ř¶`Ńű ahŤ-fçâ·íî™ëc=?f|ŁT]Č´Ľ<}óÎY[ĆÄčóđe`#ŔÔ¨¸˝3ĆýoF ­Ö[3f˙8fč­#PvAžżúuĆŘŐó'ľýćÇ“G÷ß3eŕň{fŐ—Ż­ P);Ő9qdß=w$m'L7˛.ő‰I+¬>ţĂĆŤ[N5”^ěo‡ëŚq;ńX˘ĹMýq랬‚ŠŠ‚¬˝żnžç8|ş/˝ˇ<÷… O8•úôšyć¬aŚ];ö­ §0tÖjąD~ŃS˛+[N˙Ľń±Gž8v6mÝ}3ä^¦.{¦IWżkű–Gޞ‹ülęËO<ô÷OlŞČ™2ČŔđ»î ł-k_üQW}ŢQ ď|wÖX|Ξ±şGz(„Ś^PĄ—¶|ůÁ»ź}/¶Ô,ť avŹ}u_ŇÍ ‘˝‹˙˛1µ2íg®ű:· J[nqďňŠ˙Ů}ţŕ§Ż˝ť·Iňމűä`ĆľO_ş±Ţ»Ú–ÓÇÎ=őc?üßűoň-c¦?Ýť Ű  Ąu7) yÝńŻ¬ŚłŻľřňoGŻHşŇ»˝ŔGďŘ{ŕď/­?vĄ¬đâľ5¨íĐí»÷[J.ě ˛:kgY…ÍËď}žšžŁÓ6ś9v`íě±pŚN/k>őăço˙{#37¬š1ťĆĺ8µ’đÂ×çŞŇ6€h†—Ö·¬ą3RíźXcfßüí‘é+×IŚíŢĽiסS&ÁđŹ'äřĘţˢ+çĎü~`jBĚśţ‘™väĄ /ą\ÔX~9ĐQłpí?s2SeţÚ’yŁĂ$-xĘš‡#ć®k–,ň’á©ůăpí÷iŔőrC¶Q«ž´–NŮ݉^@Lyˇ7n<ľ­({RÝNô¤ţ~půäQ3=ËË’ç¸cćOsÜe+ WL‹Eď3še/ëöńžK ĺą/>ýçC§Î¬}`ŃŹ\Úöެ.Ż|qF›wŔÖŞvOŠ3ĺŢ˝y˙¦Ď?űjcěŕyxá›ÔĘ´źĺEžOôťEőćm_˙ëź_ţ,4•Í `ţăŻÔ”^Č+ć<örk ŕk5§üžvéÁů#ĆŻ44U QŠŘŠfńĄÇćPŘź¸Z™qz˙?_})%Ú×zÄYâaZŤQ[,ëFŰĽÓU‚]ëh8XđĐŔŻ:h;ĘÓ–Üfm®AlŤŐ&íUťˇJ€ČLĄ†Ú ş<Ałą!­Ů>ŮQAÍąŻ_Ó– Př¨í#5櫍ąo6™ QgÖ^ŃJ ™NćŚf}©ÉÔ(Ę ›Ë‹mp_0Y•őbľ±ŚŚsâťUJ©ŕ_µĆ®>!„0´,ča_íńš–Z‘*©>GgˇąĘ¤ËjÖ—ÎĽm„­TŻżö^Auj ff¦˛¶ŻTNŠ–¬¦ĘýMm›CIß#Íĺ›ĘK~«%`°‰vp U”oŻcĺ©>W×Ç´yL€ŇŻ•?oę*ĚQřŢŽ¶ńîá÷şä˝žŻŻ»>±Ą„HfĂď5qľ>Τĺ•ă§Ď4č’X©k:TRšŢ •%ĺrëkŇëštfS…^ź^[SŇlôQŰäŐVü|­ĽÍŮĄĹl¸TS[˘m:UÓ¤áU]›ŁŐUŚ•ş¦33ţ“]rgLŇš`»?ů˝Ül¦(H6jU˛Ź·'Ţ=wf_µNľęd"PlbOŹK/Ę+h1Ó^}ő(! ,ÄÝ3ĹÉ滜BélWC ‘€A^ˇŻxířÉĽ3wk#;<)•6yéçÓł›´ e‰źóçgG<ĽâţŇ&F “$f]§¬¬$í|jQYÍMg2a”©©ęĐŃÔřÉS\iůú‡9qµŚPĘ®ď8çöá!„€1g§/§´ß?—ŢP_›v1­¬¦fĎÎťF•Ë”©ŁË/ťúćűź+jEł±˘˘bĎîťç3ňä‡#˘`¬¬¬Üł{gZFiŞÎŰ{đś_BÂ脸«©G6oŮQUŰD r3ÎdŐ(”ŞĘÜôsiŮOß1ă'x«ôŻüĺńí§ófڧ+ćóyŮlĘ»zţÄ™‹CNÎĹz]K^Ćńňş–úšęÝ»wtCĎĺ<R_tĺtzNBĘXo;ńőžů~çYM˝őŐCIwĚłńę…Óg/ePĄRiÔ™çyľc”ç*áyľĎ|ŘĄ-·Vç Ç3&]«7żđň3iżmĚ«ÔrúrŁ ”“‹˝ěźOÍyfÍĂY•MÜőçtďj[JJŠÓŻ\J=¶/-»$qÄ({î/˙ůł_I¦†‡Ďޤ,dSäëć‘wéč·?4‚hiiŃ89Ć jŞşđÂÓOl=žHnÁaĆO*9ýŰšG×çÖę‰dr o+É©ŃwMÓA;6ćUę(dž…Őä§ďŘ} ˘ˇ>?=ýJÖĹGÓâ&Mvć_}ný×ŰNt;„µf86› Ů—Îťąx`-ÚŞÔÔÔÂü¬c®Ů*XĆŮ´Ě‚˘ŞÚšň˘ś/>|űăo~$3fI˙eŃ•óee%iiçr‹«}E YuţĐSëţt±¨JL‡X ßz$“>®îą2P’y.íjAŇČŃ>öÜ/?óÉŹű$Ω ď?7Ň2ň$ÖI:Źo=žGśN[™Ö37>úz›ČĐ÷¤cMOc}vęůÂĘúâҬ´KYMÚ†ŇŇâ éé…y‡Ž¦ĆLš"S¸ýĐ×tF ßłă7“Úuň”ŃĄOlţiżˇĄúçÉ-©PUv­˘ŞúňŮT˝ÄzZQJ“Ľ »+%îÚµZŽćý¸ő /‡÷ďo’ šŚ™Nź˝”EŃVç<ť1rÜD?;áąők·Ď ÍĹyŮgÎ]”, <™$´•0JIS•Őś’vdóO;Şęµ ĺ'Nźm–„f]ÍďGŹ”TÔK¦†“'3DŽ(U\ú…łĄ5ÚöGë,ť'~9’ «y§; v®sţJžÄ Rj® üʡ¶÷»"ëů×N%Ý:®€Áq¬»­­©lW#ĐöG˝anJ<¦ąµ\mÔć›úÎkÔSąśt2‘=#$a"s[ěz‡íŮ•Y7žÉŻóVÍę%ůÍpLNmgďËWíkč™+ú¸^?Ob•€ą­¤«Ę~ŇÉaá ý·ye¬§ěÄ˝őDg†ŐT\jhî*Ą[ˇ"“îŚňl¨ëämMýÉżuÁ’$ÔÍ}ę—ü’Űé‚ KaôĚ•QĄ˙ę5˛‘€IDAT÷Ý>ÚšţÖůőtĚÎvĂ’řCń9ĹŮużšé:¬5U„Ú\·‡C˙YŃq=Gä´n7ÄÄ.ĺ,˘’Ę1I´‹žvŕ§Ź]h‹™Y(GÁ Y§żîĆÂFŞ{hî„˝Wő%b/ň(mą1čďT9ŃÂk§^)ľ±T7BĄL’"F vů~óÎML@X§ąiŕeŃÁťQw˝.·z—EäÝNÁň#I&I¬Ó2Švo>8JĹ[#‹ÎźĐJĺŻ&JëF^ý’@oÜčłIOÜČqÚYÔýú2J)ëlX! ¤o‡A9ĹmüśGüçÉ !›EY„R9nkł–L꤭ýůř~o®S‘úÓű±Ś’“„ĘÉ,đ"ë¬{hÝ ČN޲śhë:·[[A‰ěÓß^D aë´ţܦŻ}¬¬x.;ĺËęBx‰1™RGüD®Ů‘’.Řz§„0‰9Mńđis寅 ”.#a’Ő`§­ôP+Âz‘Ä€p¬WµlłYm !!Ś1±_Ë´ÎB D«Fĺđ}¬3ɲÄd)Q —î:YĚŰ2sF†„Żđu|ôŘ9áFÖŠ· na„ж°Z<Ż`L´Äí!h;ľę©ÎŔtO‰dÉ©L™tŁ+·?ˇe’Řqť@9ž$µ:“tSB)GŔDIâ8ž1ŃúÍ1ĎóŚI˘ÄxJEI”'»VŃô™RŽR"oŤĄ2đŽ÷ě}ÓŢxáůRă9*ĘßŢĄĺ8Xö9¤-G©ő™W×’sŠ$J )Ç1IbÝI°Ď:”ă˝lŃĘ w|_Ž*r“%‰cŇćNŃt$ď6€<ČgÄĆźś=I üŻE S†ŢaßâE/Fúă–e ĺ8Ŕ¤%kęt-ůYYµőMż}×[ÎK9.“ jîť4(~Ć˝McÇ:7Ď5ůVŔöőŹ·Ő×Ő”–—nűřö7‚řöá‘U#Ć-Ě.®*.*((,®.Î]:) ŔüuŻ–Ő4ć\ÍÖ6Ô<ľňNXeŕîŁCËŹŻö}íŁOłJłŹ|«z{‚_YJą/5·Ľ¤° ¨¸¸°0ż°,ďÂ5@ýdú ű:@ÚrŁ,é_ďrřÔű˙ľ±(m·m÷ńWo‰Ń„§Öľőŕ]hu@ż.ŕx `á#oW7Ö]ËËÍÎĘn1čţĽ|Jâ´%MF“lI}˙ž'ďČ_ż\U^TTZ™yrgŚż=nS4‰–Eź­:cŢţńöxŻ'˙úaNEMaúŃh[5…m|AuÓă󒨔J“—?VŮ +ČÉ®¨®=wđçŘśmD˙e!űŁGN]^TÝt-/ݶˇéŘ–±‹×44[ěüŃţ©ć­}»¦©^–ŽN×đÚc <ńŹOtú‚˘’˛«§ă”€gĶžĄ#çŹwŚŰs6»˘¸ Ľ¶fßď:Ô'Ş™vĄđČŹx°ZTÓ+7üťz׍®3ÚˇďŢó˘őüS+çcělŘEŢyą°ş¨ ż¤˛öŇ‘4}E ’-äŞőź^ÝoËśíśÂwî[´`ĺĹąGlnÔföh!ĎUv`u?GDÇz][ÉoÔîcöíŰ7!Ħ+?{^´ó§?uä5á<ź|ĺ‹Îk”¸C› uľ1.î­A)ŰŰi(Ű8źY®`7ÚgÜé$ź €Ná¶ĺ·(ďÄŚ?ť<ü˝@ĄIÜ:Ěw¬-·é.1*—Y^Ł÷ µWe }ĘďÉ+ÜP%đL䨢r soß%>+}Ü’í» @ˇJŘžvŹ%"™ç˝!ăŽĆŰzR›h×qgGxWŮMđśp&ÉÉ“W»Ń§’î鸾—Ă`řظ$Ú°Iôn„OŠ  NÚÖ7Í\çű&|.ó€ë̱%6ťw52P{^¦Q»+ÜçűŚ=’ŕŔŘ$ţ:“EăűDřouŐYcBü XľŘĂ~ę ˇ kV,őwA—•±R© wvöŐ¨§GĹäÜ»p”ťÂÇĹ}ED°őŹąv߲ž€ýÁ{ćßŕ `ZXřxw»‰‘KćÄńÄÍŃ3mĺň Cë†gMʤ‹ó¦şĘăěWĆĆ>6$fŞŻGwB–CÚíZ¸đĄč Ü–čCF!¦DĹíš>ö3š¬Ë+“Çťš=QÎar+(”礨¤1®¸Ç:ý)]‹îiqT ŔSnŻHßo„'ŽY˝ba§:üÍE2‘ç¶i÷ë%ăŞä°Y÷˝ÂôŔôˇ×‹ůöăQŞUNŢ˙öřď?ţ#ČŰ•#p źZÖdţď[ë$Žšž’Ť>µš*·ŕűÝđŇóOŹ  ŕ €'ŢŰ•qŕ+–3Ď€¨µŹoxćé§fMN‘[ޔԻËś˝á‹˘ěĂň®ć`VáO?€›ŰŐ´~)ďęĺüę7GŻţ>ĐŰ/$ÔçŹĘS5PÚrK{—×:PžÎ­|{í=¸ˇ}ĹÍĎkŢÜ”{úW‹…Ľ^yP9ąDĆĆx»Ď}h}c}Őř@;˙ÁÉŻZ$[’łaŃź!sŚŤăýlxĎäšfÓókfˇ?'7 7,‹>[u‡™­śŠ€;ÎśMýuóÖ˘ś´Á¶* M\ęŐ‚‡f p÷Ňű¦&M[ńHUIÖ0 ď1ş¸Éđď +eśý—…üg×Ŕ¨)Gť°Ö$+'DxE _sßb™óÍÝýSbŔŮÄŇ*ťę ţ*—ásęš›žY’`pÂ0fŮźŚúúž¤#ď]—®˙¸ą>/Öß&lřťÉĽt\đŮkz‘iW Ťfaő€Đ‹9E}rëŮ"uťŃšŤ-+Ç"pÚ™ł©[eÎŰ©ŘFŢu57g^J¸˝«—ŹłsźÚÖn!łö« őÁś˘wď_|ĎŞwJňŹ]‡Í”ÍľWŘÜąłť8€(îš77*ĚĎÝ?Ŕ' ¤ÍB††ůp zhÝú§źXě)·ŚŚ›~çäÄ)w?˙Ü3ł&%<}úKÉÄ$*÷Ëś@ă>ý®™“ďśűěóĎÜ1<@Dtâ#/~XR\ňÖS.[ľtż«µVő˛*hÓđţŐáŘÇÎ>qꬅózWĂŠR—×9ßĺç!űu4_njÎ L5±Ą· ‰p›ďďâGĘw79zSŮĂŹPFÉëţ č5™÷_4 JÎq‚‹YĄfZsK… €dbÄËÎ{Ş}ů{9fâ ŽzkŤŇlĐωő®ŠĆNNsŚqÁ¶Jµ¤˝Ş•Ëö CžV_)±şć–FŃ1Ķ1Ădl5>6SKŤfcĄ©Í Ěe-őUĽmF©2×Í f@~Đ,ăiŰ׉…ť9&ZóĐúÎÍęC$­Đ˘o’‹!ł®®H„š23€i3µÜ4/; Ńę;8[Ę!ŃF¸x44UďŇ‘÷F„čÍb´Ł Šëdż?´Ňh2óLF XĺXR]tRg–Pł±ľF*[´Í–ËDÂb…{†&4<üŃíż–8ErP@ç`2 tÍL˘ÄŮą/ őüáô‘ZNe˙ޤ !Ľ±ŘHçyž¨8¨»ńťÔ8ązđŇĄšJ ďlÁ DNu‹î@nŔŇ«+5ˇQQ zÉ,ÝŠWňŕĚ3Ç®žăP^‘߬7ËëłYäF<¸pĚçëé€üó'rSaUg€IĆ N©Ď9ńk1÷ů{K$É0$b(v^톌ۏG2Ę‹ęu‚IŐPP^ `Âălô×ţő·H=±K~""I¬ŰŔ` „2&ńŢћޮ¨Ë«Ç+Żš7{˙ĄBśZĹÉ["&‡ŕ/6o‰P×4(Ďwä÷»›ZX'×kĚ7TŇ®m”¤ö‡Ţ7†ŮJĎ„Š‚V¨qöÓŚsP^‘Żm6ó_]Q^U« T#E4×W–––á&\kn‡,znEë3k7ú«˙MJÜ3iĺ“ß$?('1ˇ<C‹~Ĺs|őĘ}«Ć.4 PŮŹžsw­*ÜŘP•[T$÷ŘYČ^´µ…™Ë\c˘ă˘†V•j[ô™Ůźĺ\€ňŠ|]ł™pbKvĆ%“ŔÖNšWzţ—CĹĆ÷Li,ÍREM˙yË“›>çPWS]Y§ëI:˛ípvô*Ěř=ł¸E¬LͫՍŚzâLQE}s§V`­ö° …5EŐµ5€;GYźÜčE7şÎh:ťW9 pwRân™óL’}B§qY÷ěk/g}óŐ˙•Ő÷ëŮ$•v÷®XÚ [OµIAY·6łG­Łś$ áIS?ůhĂä[¤Đ?űď§Yúúż·ÖĽoĚćť;©*·Çş÷®™3ÎçŐ {Ç÷˙~ł˘([§§ËćŹÝ>lztĘ”ź>}«Ľ([gŕÍv|É‹_|űS”Ş>·«ď_~ϤQ%A#ľ˙éëęÜ‹MÔéą5ËGĹ óŠ›řđâ)‚Ů4jîĘ ¤ĺŐüŁYŵ”Č©{Z´ŤŮË˝?uD-Ů»SFlµč<ëűáőXRJQž´íĚäH€Š°§¤âĆš“zČ)˙Z˝Ő™ÎË6d…[ţËŮü0_g?&„Bh‘|V©}T\®Ľht ˘Pr>+|9Ą‚´E–ĽBžó< ú˛] ¨‹Ť}˛üý‚Ľďöś$󡋨=yJ!h-ţOĆę›Oס¶ĚßYíĘŁ†+µş 1âŁ(qb~c݉fČ~ëVžěf¸SĚ_mý•ş5µWbŁšűEsGEîĚ1YřJĐĺC(aăý<“5ĺo•  úŕ­DxŞň!Ú\Ö­×EťŃôŔ°xˇţXŤ n=í´ ‘s´÷šçëüÍÉ  ŕ($Q‚úĄĂ Mĺ{K›ďŇd–Ĺ'űŮŰ®ţmç/UÚ‰N~„S.<ŘNmëBŤç›t2¶ą‘N†úŻsK¨ÔNC]ě6ť9űjF™ł’k‘ş‹‡`§TÖd2á˙A 5‰”rŢväR=Č­ŘÖ(§ŕ‰Ů$yĽýÎ-ą·ď˝J%IZşzµsÍĺOľÝ €€*¨uťtâŔ¬´HM}ăc/Ľâ§żt µE­Ľá›ŰGľ ĺ(Ą–右ŤOSCQ†VöÎç8&ÉĚé:MĘ%GDłç­¸c÷ß{=­o|üé†ě_÷¶ě;ÝVßĆ;616đ«gź|ú˝®N¶ÍFҶˇ[Ě7ZŇ~Ń-˙d˝ßż1ĚÖü˛¤Ňć(‘ŻÔ-»˛ŃŔ@iË-é]|ě笢RcCýÁ ±ˇ…S¨}¸ÔËćł?„RQťC&,›űŮ/Š€RĄ€h4ÁăíwŢhÉÚ·ď÷˘JľĺÜ5íżvĐBÓxĺĐŢÝ©>U_Ď4Ţ,şoŐq4YŐ9עVÉ—îP)yJ,C€R˘ŐüˇAKĆŤúţdţ´HQ9>°ţyŹW®ćlNˇÜęşdA)•$1jÜ´MźĽäyqďw;O•€Sp0 °˛á„7 ‚,ť/Öż €‰Ě;4~JĚ•ËMâŰ÷ަ'lÜ˝őRÉ_z’IJ˛ĚĐ榏*„ŢâéxśŞ®ĄţvľPש•(1«đ(Ľ´oÓÎsµśťٶonôyÚyFŰ—ĘC,ś—ëH†ô g*ň+“=Ľdéě c'\(¨Ą”ô®x’hÖ¸<ţ§?‹Ś tŃE‰p¤íšŁ˙g¤T07ë´"@uZ˝ŃdÉM,[HžB”p÷ě•Ă˝…‘QC3ڞňήš3ăüŰM˘hnŞZż`ć/©ąáĚ‚ÉÔTµ~Á¬_RóěťśîZůĚ´á>˙xüoiŐxëÓ/6<°`ýŻU‚ľń…űďüţ‚W^ţľ Ó†ż˙ýŁ.džÝůÎßVOů-C/»$t ěÖeU`™ń­yßź:LÚuţĽ2hżTµ¸ĽË]Hډ4ěŻánÁ,ý/×LĚ"ÂÂYÄĺ˝Ä_ă Q/çhu¶ńmK$ ­6uŃĹ *Prôž`ËZ˘ę•ćĺ–hxOşp¬K ‘#°µőŢŞß®S]9َňh# :ł¬¨ŔD&6Y]ľXŹ=Iôtň\éçř÷łiŤŕT–‹'ÂËAa¬´arhŤ¨ýą¨\nefüKăĆOsd÷>] ( )íęΉ¦\C dcdŇ­Űţ[âw[w4Ї€ÚiuDŔöŚ+%8 }cĺ×Ye ‡Ť˝´tŢËC˘TŚub©LoISüQ/Qţż8 Q’šM ·pHh6 š·ľüqV¤0oů#•$Á5ňÉłúčźE"8ž…ÎučeĎL->‘c×N‹úËłĎ7•š»Á—'·Ź%âkź`©E©vp –‘Ů!‡W(:Ľk˛”pDxF4ŐÔŚZ´úůőŹÖćed–•Y>c ú¬ăź|ľoĹ‹›ĘË Ţţó#jÉ­Ě7VŇúŤŚ0Q˘ ś(1*'] Dq;BkđĆVMţCăzőSĘ`ďňů\˝Ö(H’BuËßĹuÇŃlÖŐw?Ëô(a¦ß3_cĚ˙nűA˘ÉŘfI¬z¬±|íČPő}ófÎł[łb6ZŰ޸1YtßĘ2R¸Ţ13I,G˘D™ŇvpüHŢP—US €ă•RcńŠqIAľQ[®*Ţxj…Üęşd!_]=úăȡ±Ó<á?qŐü1ÍFÁÎÚ†+x´Kç·ĽĘP™ůТŻ\~8§19$qěŚ#Ălz’Ž,íçď?űä·ôŻ·íóńiŤuµşqü̇SşĘ”ăÚ¬D' ˝â'®šť ę‰Ú®On}lş™Ń|kdůÖk®ćś îşkíăëFŽ™SŻ ś6eD´ŽWÚ4§ŤHě™Ŕź0S[h&a ę8çˇ_ĆEýÉG~ł$4uE‚Ď=^vÁ±WąŹqc”“r_Í­®ŕżDIdL ™ •fĄ· €űžjb,Ů] ËD&äż‘ubjZöWµžK}ÝUčäĂ#?+22©ü5ŔhŤ9Ogţ>-íŇ«…ľĘ¨ôѰfcő)mÓ™úćFć  Šs˛Đ,3“#™Ř,Ô­5™Ćß`ÇzŁŮł» „YsŚuáˇežnçEŢ „÷şĂĄéPťľ‰É|îŁ÷~rLd4>j‚ľˇËz—@Ą©ĹEmóÓĄÔĂőRڦĆlŕá»iÖĚFÄĘos%Ć@TóBüÓ ®!DdX7"y®ŹrîŽ}—äÄť ¶D|ĺř±]:ĹĆń‰ Ŕ(A$c€ˇ°ąĹÓÎŔôAD÷E^ˇL3鍇÷Ós gÇĹÍđ±EwŹ)ëštfđŢNíş˙˙ dv„Ú9PfĘ3ȧţ·¦#Âđě»-ťŕn€‰‚Ää5łĽ'a’d)ˇťç”ŃĎ~´[RŘI‚IŚ+‰˘Č,çb*ĄŇÖAî®Ů»ňçyIb­v~Ńs‹”e„ÖśX !ÁBĂ–{*\T1źDy%Ű»Ýéń„ŻŘ$ů?8üó(Ź‘v.J—QÎNŃY_¤Ý—‘¶"˝ň˛@j´—˙^&Nĺ˘$Í-ą/ĺŃŹ¨ÇĽ(cJMŘ«áC>‹ śf[µ§ŕüąÖüZ®oá ¸0çaă¸9„¨$ť`¬ŰőŢ;ŚRŤ¶.Ëä1ҵłüaTđ˝^ŃŻGšćÚş– =çëőRPŘ a®!ŠúĚf ' Í”ŻĹAÉ›ăBňüi”ťŁTźÚđ 'Eo4¨ďĘukŽEĺ7ʼn ,řĎ­%˙Žň›ć @ájéťX8 ű‰î®ŃŠĘ˝Őmš˘p ޏLrn>Yo4É™…¬F ŕHIáĺ&ý0ďMë!ę~**ŕ ŇLđ÷Ors&­98(tŠ›Í–ü|YłgF ű[b¬Î(<ź¸cÖ3}ťDAňĐŘr¦Ć—ŽWą‡˙31R ÉÓÁĺ•ńăľ›1ăÉP×­y€âáŕÝY™9Fp”J€­‹ß¶»g>2ÄŮÖl2”ë´žxµ ™QŔ¤Ż:\§źéçôĎü˙Č/ŹÇűg–””‹­«ŚŽçĂüŐŻ˙íÉ•MőÍO=˙îÉŁű猋\ţô袭_ľźY …J)J̺Ή#űćO´ť0Ý Č)ŕ÷îŮrˇ¨fÄŘĹżůÎÓPřő¶=h}Úű?Gö€ôŠžúăÖ=ÎwÇý{Ý<-Îď¶ďż>^řᏻ_Řđôîc§žĽWÉG ŹŁ°Ţ8ąűNLöȇNśZłü9rďĺ´Ă~‰w˙¶}Ë# GÚ†Ź;~6őه—$D«ĘJtÝüzŔ|c%±D€çN+|G|öůď~öĺhŰĂ©Ç8»ů\f.KK‰ťŁ«»‹ck!»Ep{´ĺĆ ź˝3Ć(©ü÷=KçOť@čçđŔa‚pSćÍLß»˝ÔPî]ä8ŠĂć¬6Äó»ż ‰˘µ%9}üŕĚ‘Q‡Ît ˙őgďżýÉWwF{:—€Ţzżš–€}[}łm/oëŃ˝oĎĎmuĽŚE·î”}˙‹<µÖŮ=đë={ÖÎ'č[ĽĽýą¦Ě§î[ˇľčËż=˘b‚wPÜ;˙ý~סSĎ/ľůçF@9ąß˛ýÚ—?úŢ•ôÓŻľřňo÷ ó0˙ú˶©Ë^ző±ĺm6|ÁI’ć­j“vn˙©_~óůŰ˙ţrr¤Ű‰ŚŁgň/¸ŽůoŇ‘C)¨âݱçĺőŹ~űÍ®ěüöÝ'ł‹Óƶ·J=ŔĄŐ’(ylůcíu1>rptwóîŤ[~ĘőřL ëŚvâčţů“ăEAxí/ßxj­ł{ŕwö-›4vŢŠż^>wčĄ /8𣳱h÷ţS@‡ żť‡ŔÖÎÉÓĂ• ÔŮÍÓŰÍîřé żÄv›yîŇ×]43›”îAý×çźýß?"üűJ9‰±ŃËţůÔśgÖ<śUŮÄÝPŇpŮ<řşyä]:úíOĺăhkKRRRś~ĺRę±}iŮ%‰#FůŘsoĽüĚ'?î“@nKîŃ•E•Ži«­[Ď, ­F7ĄRSe[ť§Ö<ú{V9|`hXM~úŽÝ*rΧf•č[RĎť+ĚĎ:váš­‚eśMË,(ŞŞ­)/ĘůâĂ·?ţć7Ab1c–\—,CKK‹ĆÉ1nčPSŐ…ź~|Ç™k¶×ň/YŮđs…Ą5~î9Ź|űÓA#ˇDl,=t45vň”gîŐgźÚ´7­:˙Ňą«I#G÷"ÁltŤ0~RÉéß^ű§‚&}yvZj¦U«Í{$ˇŐ’¤eäI)|áéÇ·ĎŁ N§­LKMí‰}˝Md˝ąđuťŃĘJKŇŇÎ•Ő Š¬Î»´c÷ŠĆ†Ü´ÔK×J<E YyţĐSëţt&»¬ĎéŚ1fS~Ö…Sg.J`Í:íĹsgÓ.ś<ťž—0rŚ—l3wĄ2ô†‡PĘä5hŘ])q×®Őr4ďÇ­yA8Ľ“ŃdĚĽpúěĄ,B¶:çŕ錑ă&úŮ Ď­_»ĺxÁl.ÎË>sî˘dyiĹ$ˇ­„QJšŞ¬ć”´#›ÚQUŻm(/8qúlł$4ëj~?z¤¤˘^25ś<™!rD©âŇ/ś-­Ńb1/Ýđ°¬$--µ°´ZL–íJžÄ Rj®őRÇ2Đ đp‹Î76äśOÍ,,ą!ŇÄoŞ5ˇ„uť/şu\#ăXw[[SŮ®F í[Ł>ÚĘ@‰Ç4·–«ŤÚ|Óuç ě‰$´–÷ę†p„‰Ěm±_č¶gWf]‡eí?‘7Ă19 B´ť˝/_µŻőŻŹŢůAZ3ÄöîíŰőď2“ÂÂúoóĘĽ!˝®u.ˇ3C‹j*.54S‚Űć Ę*2éÎ!φşNŢvĐ4€l Ŕ’$ÔÍ}ę—ü’›źŢĺ—9ÖQ]KdĄ0zćĘ(‡ŇĎżŰÇ(Ǥ[|‘Ö‘÷7.‰?‘_4\gŹÝ +"Ç?µÎÝ©§[÷NŞc‚?BČ€-.ĺëL’@9&‰vŃÓüô± m13Ëĺ(X‡łŇë/aŚ(l¤ş‡ćNŘ{UĎQ"ö"ŹŇ–ţ÷N•ó-Ľvápę•âH…yÔQĘ$)2aÄđ`—ď7N$ŔrN@€I¸OG!vżŰěŃ뺼«“]Gł$ˇ§TÎřĤV'ćÎ%„ZGę·,Z5uŰĘ"©ëŃ1B»7ĄbżeŃ“Uďą{‹"”#$‰u©Ö9´a?N˛¬3Î_ŹL­şî‰·nś^×࢔‚1ąţőÚL9ĹmüśGüçÉ !›EˇTVJÖf-™ÔIýůř~5Ţʉä6éZ Çh¦$3kŻ"űľs –mŚG:D“oW:lx(!mË[ëíi××>V=ńĽ=Ţt+ž¶˝á ,´YSŐN3A{@®N]PB!LbNS<|GÚ\ůkˇ|WCyÂĆO uPZÂHčđ!mh)!ťŘ2°ëU-yJĺl´mŁŽÂÂ{PzJGĐę0ŃŐRSŃj YfŻŽŠÓŞ#Í:ÄŢNV®3ZčĚ“‘!á+|=vNřâ7K!!‚•u)ˇ”ă8Ę#„‚ąŰAI9žŁ0›ĽBÁ$qŔŇśB)‘,9•)“ntĺöă!”ŁLę ě„PŽç I­Î$Ý”PĘ0Q’8ŽgL´öVâyž1I”O©(‰ňd'ł˝?o¬m.mÖčFńôÄ^0ĆŰůÍ™5Q͵Ż|Ű×U¸‰B9f8´ă—⡯5Q𼠙%ÓíŤ^Đo“łÚßVÚ:p÷±„ĐNge]K,ĺ”’ÖÇóLű5äşłl¤—Kž®gn]J(Ą˝ßYˇvćZ®d9«|Ô±¶b˝©!M'ɢ±Q‹M¦†T­Ôí1+Ç{ĎľYŽ&2§©^áKśÓČ4ôşWŰŘř©×ę›:1LnáhŁ ¶×X´Ş´Ú2 aím®VW_ij&T˝0"0˝¬ ŁÉ(o“xĄ:ÂÁNA`LąŤZ“Ää/‰1âçâľDo¤˛ťzÚDÉš·sŮ2uô—§Źm-­ço×y©ĺ2ÄÝs¬‹ÍWYp<¦. oşRB"˙ë·ę·ýĹ×uXÚĺSÁÚĂ+ĐÍ>/3G`Ý•ČFh`l,«)-*Ż v÷ t·Ô!Q¨FŤ›hÇ:l0 ĎÚ0ŰóĆoóíÂC¦P9GÇ„ń$PRnĆĺz“ Ań#“b˲Ď?›%˛~PRŽŁ„@™(‰śÂcőŁ÷ąiHţĺ3ß˙z@˛…r­§wýßLʧćĚr`ŇGCyD8{řzÚ]JĎ’@b⤺˘˘ň~Ąqč‹~PtŚF!ŠŔ8Nan©Mż’7Pűâ"‰€1Ż7‡ĽĚláöiôGWĺ5´ÂČm˙ý÷úe›\ć8:`G ˝‚|ś<ţžGŢ{|ćś wúu’-ë‰oPhrRBsuѡc§M‚ ­äđń3FłčěĺŕËłĽ ¨(ĽV\]§rňť2q ßRµk˙aŁąßŘĹŽQ‰ŃČäŃĂBĽ.ź>u)ż” Š:<:TW_zřČIŁYႢ;–ľAˇ#’uŐ…‡ŽZhî§,¬ő§›Ţ»ŰXËFÁ/2ÎąĄ!˝°(ÜV®š—v`ËĹĽ…B!šÍĽŁGTX ’¤oşšťk!Ib߲čÎŞĆ_d¬Ü—“—x€ŹÔĘůňk9%őbÜŕHĎ@&}ůĺ+E#@븸š-H°ČK@«(Č/®édŔ”*÷ SĆÚ“ćŁ{VMr…ŢĆ!`Léä;u⮥j÷ţ#FłŘ'7ŚćľÄÝęˇ\hĹX÷Ţ·ÖÉ=|B}9Bš*JóŠK†›µ™„p”0đá‘Q­”ăDcÓĺËYfFyž#ډ­[‘®V˝›’Ö9E%I’@<}ČDI"”RPާ™©ă~­‹ čřN#Ą?Z×›0ŔŔ9%íJHŮ?ę@bŇáśü {")e÷đQ?ÇÚŮ´§°|Ŕ{k†~;tôîaŁ' }9`c›r(É˛Ş śgzŤ>–4rŰĐч>dëĹC>´Ž#$řN´k+éMʼׅĄlŠâxpľÓsĚÉ䑿s4)ţďĘn]”)  uH>śđn0.äůAcŽ&Ś:0<î?…B=ěׄ_˘€ăDŻq—F†Lwră>údr'š¸/ńńM¤ĄNáż&xÄţ„”źblm)—éVôĽÄ6#ÜS'ŤÜ?ćXRü›AŞN®PrX4Ű‘7Ď1ůGµ:qWBŘ\çžřIÍęáÉ'—-Jť7-‚§°ŽČáÉďŽN¸˛bńŮ…sOÜ37Ő’gb‚¬N™˝jٹŠ..ÉśsÚňĄ»PrŔđ¨Ë+–ž]87}ůâ˝3&DÚ*ŃšËŇÉŃűô˛E+Üđ}9ŞČM–$ŽI›;Ecőe·äA>#6ţäěIrüđ˙)żKĐOŢaßâE/Fúăł…„ó|ň•r*j ÓŹFŰŞpĽW{‰F ůN‘r\"&ĺWÖ®™“Ř­ý#ëV€Ýëo«Ż«)-/Ýöń?ě†iň —íMcľ}xd7Çq ł‹«Š‹ ‹«‹s—NŠ0Ý«e5Ť9Włµ 5ŹŻĽýÉŹnŮJö»ĺŐľŻ}ôiVicö‘o•°çfŮÜ=ŚW>őiáŐržě=W >۰ ˝çÉ݉ܗš[^RXPT\\X_X–wá@Ś ŠŽ~2}1¬Ë¸h·Ďý×U94ęýßX”¶Ű¶§ř«·ŠD €hÂÓ kßzđ.\_šĚq<€IKÖÔéZňł˛j뛎üđ=0nńš†fKÉŃţ©ć­}»¦©ţZ^nvVvłľéĺŐwĂ>`˙ůĽŠâ‚ňÚš˝˙÷¶SwÜčD :[6Ąü‡Ç^ű¨A§/(*)Ď:3š´´¤¦éZ^^mCÓŃź>t¦Ü[ÜZrlˇŽŔX+ ~ű®7×M÷dA€ň˝ő^–yrm7iÔe3ë3>Ł˘ĺě/sŃ +¬iz|^•R `ňňÇ*t9ŮŐµçţŕ€łŤčYÝëł,ąŻ´mXđČ;Vś×ľüŔtx¤d–Öä—TÖ^:úŁFĎ'˙úˇŚ'ÎA `ńŁďT7ÖÉ­tş†W]hM¬* źČ­ż§W••VfžÚă§ÜžzőÞƗś™Ţ!0nĎŮlYîű>×€m|auŻÜđwBŻCµ«úî=/ PĎ?µ~WŚť€SP{ďýŃ:ŮB.[÷~®!'3«şľfĎ˙˝e,{ęÓ˘¬·™=ZČóű˘l;đ­żăżc˝®­d%T»ŹŮ·oß„›®ü¤ ręň˘jËH9üĂ{>ťÇEßV´«,z_7 4ĐyÄöx÷!jÎQˇrRPu}"1đN{¶lŞŽă#<ď w4ŢÖ“ÚD»Ž=ťě3F¨“¶ ók Ŕmş‡KŚĘe–×č=CíŐDhźň{rÄ wTI<9ę‡(Ą·ŤďźŔ•>nÉö݉ P%lO»ÇU.łŤuđ™ĺĘvŁ}ĆťNňIŃăA[ŘD"މ:yřű!lR<ĆťOöŽS»Íöźt9ÉwŠ{ÜĆř‰'‡»Vű?9%{dđt'§‰cöÇw˘€ë|ß„ŹĂĺř0—áăâŢ”˛m°ť†°Ťł˘çL’w’š8Ş\GذIô6ŮwŚm ĺ]ŤZ“řËpLFëűDřoufśvÎżÝ=ó«ISΊ겫‘A©T…;;űjÔÓŁbrV,eÇ9»\]µô±0‘®nNŕśö-o€+€iaáăÝí&FDf,™Ç7GĎ´•Ë˙18 ­ž5)“.ΛęJŔĂÎqelěcCb¦úzt'dÔn×Â…/EÁ*ţĆíyá3%*n×ô±=ĢýAŢ«®LwjöD9‡ÉőR(/űŘŮ'NťÝşykQNÚ`;5Ǹ9í%ËÔËqŔSn/żtŔ °‰™sňô9ąÎ{5€Č;ď×KĆUÉałî{…1ééCŃ:Ůß0ČÖ3bÚÍbľýx”j•“wŔßż=ţűŹ˙ňvĺ\§–5™˙űÖ:‰Ł¦§$FŁO­&€Ę-řľG7ĽôüÓĂc‚(xŕ‰÷veřŠ@8žQkßđĚÓOÍšśŇ]gĘŔȸéwNNśr÷óĎ=3sBbď ĺ9{Ő†/ОË3ôÁ¬Â/ž~7·«i%÷ őň ~ő›ŁWčíęóGĺ©ęf\hşY€Ţ"č§ŽÉ„2đtnĺŰkďA?ö .Ô+*aÍ}‹ä’fîţ)1ŕlb‡ đvźűĐmcŐ›»ýŔĐëo6|A“ľĺˇ»ĐëČí*AŮFŹ^X­m|fI €ŘřDGNţ¦L tÂZŁŮüŔ”(ę4mrŠ\b„•"Ľ˘†Żąo±Lˇ®E÷Ŕ´¸>eŔˇ×Ţc†wU«ĐyĽÉ‘x•˙úů±ĄĺřÖOl§]-xhÖ0w/˝ojbĐ´ŹT•d Ó𣋛 ˙ްRnÜ“,zŇgž«ľţŁŽnQ±qŢîsZßPW59ČÓrňó楄ۻzůşş°Źµšllś]#[[Ő×VL‹ô@ëÁ(ŽçŚ_±Ţ ŻďgĂ{&×´ϬźIgĎĄő4ľ8žXşţăćú<‹Ü[ŚÎŚB.ćőÉ ®g‹ÔU›Ť-+Ç"pÚ™ł©[7o-Ę9ď °|ĂÚ{ď‡ÖµZČĎKsŹ»ÚM^ţśI2.ź”p÷ęĘňŽ^‡Í”ÍľWŘÜąłť8€(îš77*ĚĎÝ?Ŕ' ¤ÍB††ůp zhÝú§źXě)·¶¶ęł&%<}úKÉÄ$*÷Ëś@ă>ý®™“ďśűěóĎÜ1<@Dtâ#/~XR\ňÖS.[ľtż«µVÉ?¸FµŤÉôđڎÖüéŹí*‹nÇ×Mµ°Ô†÷»ß?t…—Ť+ŕ<ÄA¬6Đ`§¸w"l]‰ŮŘşŰB °Wň´úJ©%§ąĄQ˛´8HFÉëţ Á Rń`’sśŕâďŔ´ć– ÉÄ—ť÷TűňoĘÍ ÄAőÖ ˙ŮÎÎc\BńĺTÖµ‹ś ¶UŞ%íŐ&„˘ůrSŮŽ:0ŐÄ–ŽŽ%ň˙Ű|?Rľ»‰·ŁśÂí…BmuĽçş‰-Ě.Ř–4[*Ěîł}śC8]ľ‰* SĐN4ËŰö$baCęňô’cMDiqŕé@Źžž˛Fc}šÁ6Dc©2ךL ćN¶Ń)Ţ4Ç#´™ZÎÍĆNÓńb­őagKcéĚ_űĽ¸BŃ1 $±˘Čd2ćŐ×—ę Ă|‚ĘkŠ~׉S||´ÍŤJ'ż/¦Ś·! 8By(Ü34yă¸dOBŚ˘N‘0ÖËĂd4čšD‰łs_ęůCzF-§˛oŇ„‡ůŹńz&!ÖŽë 'W^şTS ôť-x Č˙nčäv€<Ó«+5Ç(Ĺőďjd/˙–ěÝ)#?ÜqDˇRK˘@Kö®ö&ż ¦˘Čř€.óĺŰo5âµ˝#“ä:rŘí1qŁęsNüZĚÝ÷ŔI2 ‰ @"7·«!€1SnóíÇ#™Ś ĺEő:Á¤o((Ż&Ü1ÎFí_ű7€Ô»Nś˝@’X×ü)r !ŚńŢŃ›·˙¶aĺ]‰ăgmÝúóäÁfŕÔ*NŢ1‘8±yË“÷Ý5iĆ‚×^}ĆÁFö…$=a†ěCÄŤžşyűŢMźĽqĎ’űţńć ö*XvÓŞ=űD›ą¤ö‡Ţ˝ôŐKI;0ˇ˘ Ż˘äZ}‰I†’ň’üĽŠŰ—;ľ-]Ç»}ĺű«cň…NÔp/Ť)őü1¸˝ž?8RŹŰúD¶SŚôjäPČ™gŽýßĆ-Í@yEľVgTŮh*2ϵ•čšÍ„Ł[˛3.•W'Mš[śúó©Ş?ŻŔkéÇ3‹[rÓSóęš#âĐëČí*AŮFM1V[–­Ššţó–Í‘hŃPśučXNLt\ü°Ŕęň˘z­VŞ+8ŘZRUV¨mŃWd¦~öőO2…Ízs7ýu‘G‰ľ×ŢŁŘúŐ><5š¨ěGĎą{ĘčŃƆŞÜ˘"™ŠždŃť>K‰µőegoKsc]vFzQyuҤyĹiżî/hQkT°q^÷ěk˙Xż6ŔMŔk…GęrÚZĄnÝwµ hOV&›‹ęŠŠŞZmXđ Č°ćÚňŠ-Ę$& ë:ľZ ŕěčUń{»ÜĂăň´Onôň¸««ęt^ĺ€ÂÝI‰Ă?ÜqDˇR1Iŕěč١÷ľ´®µ‚±´VwŕŔáZ˝dďb/ íîŃÖ6ł'Ăť‡'MýäŁ÷y€ýđł˙Îť:¬ş¸¨¬(żÍBćĺ–đľ1›wîziݲE?żgçÖřPCĆŢŃfŐß|ë%Ń)S~Ú¶gÓ'oÜłěż˝¶Ţ9(rÓÖmOŻĽ+qü¬mŰ~ěŕ1âűź¶˙çť,ř§_7vVĹM|xńÁl5wĺkVEh`yíÔ&SR[Ů6Rj*Š«kk`\ŞW+ÚZ§ł,ş_7rüh©ůŞN_bvć9ř?‘j[*č*ŔÖ9Lejf±FyŤ˛ˇíAA«[lbś]‡ÚşNsłqç$-Ě{uPČ|§Ś.—_4%…’óYá˛Ö[ˇ´E–ĽBžó< ú˛] ¨‹Ť}˛ú—Ň ÷_>˙HŽd¶(J'P{ň”BĐZ˛ŮNެ{*@*n¬9©¬Â–0ś—mČ ·ü7 ´%ĺ,ŻJÍZѵżĘ ŻĎlˇ ÂńRőÉFçÉž ˝ľˇĐÄ©($FT|Wš;€ěy·Ďý„v¤ç´€Í0Ç÷Łbźň1ćikÓMóSuđ„ —|nžc­DxŞ˛äüě:ö$JJ)éđWëj„ p´÷šçëô[N!Hđtp笩1±Ď§Mťçk/ b‹(-‹O~&Ęgőo;¨ŇŞ8ŽpĘĄ?;<ĆťÎ7édls##ś ő_ç–P©ť†şŘíĽzéžßvÎŰu´E"č⨠ŔN©$¬ÉdÂ˙@k)ĺĽí:ď]űL¨”|»ÍE«Kň.,^˝ÚąćňľÝ @2;µ!5őŤŹ˝đŠźţŇÔ\µň¦ĎďŰa 0ßF<ň°ă(Ą–iĐŮƧ©ˇ(C+ ”ăZsTwuA•K(GĚž·âŽaŢ›>ýř“Ź?Ö:„ox`»U#ďŘÄŘŔ_>mʸ‘ď\Ňl´¸«ő„ą Śfłą©jý‚™Cb#¦-|Ü(’žéépTd9Ô"=bîg‰5żäĄá(‘o˙ ą­ON»@ÇqqűIéCÇdvű9«¨ÔŘP˙‡p@cC §PűqčźýˇśBAEŔăíwŢhÉ=¸}O:¨‚2s{ÉŢtŢ$0ç Ë&Ĺţ´ńWŐ•WCgL5ôÎEËĂ|śŤ†~-}şZ6łQň ‹źă_Ą7ďÜ·bĆ`1§˙°}ßwo>]žqj÷™ĆNm+ŮyŞś‚CG ŃÁ‰ż[YôŮűňi1`LŽÂÂB©(HvĂ^ß°čĹÇ?wMoÙͥTŰlxđ/ľşzÚ’qŁľ:™ŻR©ĘńőĎżţęzOĄţ¬<5÷% kzäË%‡ŔáÖ}™PNdhĺü*b¸|ţtF~eň˘‡wîűmh«ŮĐ‚Žă‚tl%G¬Öń€Ś{Î5˙kÇC»>5—¤ďÝ“*'çí:ľZ PV–j-wŁ­®onôyÚY÷Ą[ÓĂ ´ějčőkť`68ůÄíŢ˝7ý÷ÍőWü˛í˝­$I]mf ćfťV$¨N«7š,ye ÉS¸{öĘáŢ””ˇĂ’'éÝcVÍ™Ŕ$ŠíV}ŃăĚ‚ÉÔTµ~Á¬!Ń)Óž>űľiĂ}ľ˙¬}NQ47úĆîżsXüś*Îs´á{xcÔě§ŤLüŰę) )“;YŔ:fˇ„-jÜ´#DöŔi«×“µ®ÓY]Ć×M 5^~üjÖ›©+łµĘu„Ŕę YÎÍz1·®@t䔞´Ĺ«ŢUYz´9ęý¨ĐĹNB˝@`ڧvě‰QĐ×›!żUÓ™2ďO?9őbő5zŻ;8iüďr©úˇÂ €p ´eż4z?>jß°°Už´‡Oš%IbD~­JŔDĆDö×p·`–ţ—k&fŃ™Bůgď%ţ‰zŮ8G«¨łŤë[eoŠMţűE( ×r±I›Ą«=˘ Ś€RfMórË ´® ŚYÝtHé}jmꢋţT äč=Ů^fLa«¨Ĺ@p̲˘™ŘÄ,;A«§~§b^ŚŇ¦ ŔäĐ QűKQąüWCKă†Çź>rěhqŚ›'`9>ÚŐťMą†YÝ%“nÝößżŰşŁľ08 ÔN«#¶g\)‘ŔQč+żÎ*[8l쥥ó^Ąb¬Ke˛L˘ŔâŹz‰ň˙ ŔQ’Ôlb¸ńh€ĺÔ„´›Î%‚ Â5ňÉłúčźE"(/ź XęČŤ$“Ţ'rěÚiQyöů˘R\-ÉÔ2 o'KÄ?Ö>Á R‹Ríŕ@-#Óbµ ኷햎H"ďŔ¦ššQ‹V?żţŃÚĽŚĚ˛2ůµ!8"ßÉ }ÖńO>ß·âĹMĺeo˙ůµ%16ís{‰R©®*ÍÜs1@鵓ŔzˇÇň+#L”(('JŚĘ™ QôŐW7%ˇ5xc«&˙ˇq˝ÚDŘa\ÜFčSÇä}f˝Ö(H’B5`Ż5®—AODłYWßĂ,ÓĄ Í&AóÖ—?Ίć-¤’Ůh]"AÁŔô{ćkŚů·î°ů›˙|ň[ú×Űöżůř´şęj˘Tő›Ŕ6J©˛1Tf>´hĹĂ+—¸Ú0qđ(™Ç~94vú‚'Ľă'®;@öń-m%«ćŹh6 vÖŇŽyş—E_˝OŽ€QNˇPđśe#°rÝ_#ś j˙ Ăm<ÂOŹçĄIa78~$o¨ËŞ©ŔńJ©±xŸ¤ ߨ-Wo<µBî°YXŃĂqÝôµdVŠ‚Ű8˙ݶ}´W÷Íżë®µŹŻ9fN˝&pÚ”–O¶”°¶Vßo?ŘúW ȉď\˛&%T}߼™3ć,&Ă^1’€Îă«ÝţČ>ń?˙Y›Üë««‰BL©é“b_nWťőP‚‚§môČM?˙é hĄś©Ą~×ŢÝ Í&ScYą ž—D±łÍě[{ FĘ@ÚňY·;Kŕ v­)Ë)¬ŻťÍ,rSŮŕ•6íV=ż€Z%—äHÚ†:ßp]mͨ…msJ©ĆAŁ­-ż”VłŢ`UönOaň›Č®·ěň5ÝŐŁ?¶Ź”yŁĆq|;ߍeç”ndA4ďVkŁĆĘĚFˇ-^0“Ľ WÇ9 ý2.ęO>–]ąÖótćďÓŇ.˝ZČ1ÔÂS)÷ŐÜę nđkADĆ9šŮPiVz«¸ßá©&ƒݵ°LdBţY'¦¦eUëąÔ×=Q…N2 ĚEFF8•ż¦MüO{&đçĚÔš G:ÎŮšBˇÉ¨+|îń˛ ć˝Ęcś˝YgR¸(*ľ.ŞËcv~J“Î,Ŕdľü`zÁÖ:NĂIcLÚiöěn‘ÍČ$ł%ôcč‘/CdbłP´Öl&š@ęÁyČŔql8&25DAßĐýzWŢĎŚ™$Ih&~›fÍü`D¬ü6Wb D5/Ä?­ 0Ç ¨L"ł$ň13ËčÔé•ăÇvéÇ'*ŁA’LچÂćO;;ÓCÝy…2ͦ7NţÝOĎ],ś7ĂÇÝ=[ŻkŇ™Á{;8µŹŹ˙­ ł#ÔÎ2SžAn*NżYLf“YjÇcUB0oŃÚ`ZđŻM?@kś%ąŽꩬ±ŇŐŃyăŰĎîąlâSŮT€Ü\Điů•]ůMcľýxä_$“Á¤·Î 9ť=#§$‡‰”ăJ%€ŕäé{Žťűęť§€|XÔVBŔĐÔljş¶xęř))ĆŤŮđÎŹ’ę¬Ů$&L˘„4Ľ°nv€_ŔŁon^ôÄsó'…Mé3 1€I’ 1ŮşĘ/Ńz¦‡‡Ŕ,Ö+x$IĹS@pňť»{î«›’ĺ˙DQl ëIÚ^Ł 8ô_âÖăâö@uL>LĚąf&¶ľa±¸ť±@~<…AAáDlČŞ1˘Çoš0ŕŮw?Z:ÁyâÄ©©ŮGE©S g6  .ËÝ}ę—Í9ő(GęŻ=ľ`’W€ßŚ˙dfܵĘXeŻď¬mTmK˝ Z˛®›[ŤE­¶f˙¶ź*Ť|Dh8HR[IdX8€ż|đď6 )Ąťc÷,‹^z—¨‚ZÇ/1€6Ö\έypíĂCŁěyÇŕy3&&j8Ó†Ő÷ţRě°íë÷€AÁdD úZq…§ ěéŮYÉd6›E@Cݵ}Íś nËËśA99(–L1“XŰ$l=.DAl—WĘIŢ´3!ţ‘&mÁ–ý'ĎŘqµÎ4,4Xţ«`5ËHĚĘJ0PŇPĐAîU€Âžׯ^Ţ 7xY"=ʆ®zŘşÝ"/9s§ŢűŇşÖ§Kś¶¦đíwß]´äńŕń÷Żť__W¤RٵŮLúőśUb’Riăh jžŔl¶ĽÓ‘-¤Ľö©ŃÎÁÍÄ)ŘÇM ňŰ%±ÍŞË{’v;O):Ď)Łźýh·¤°“@¨$Š"łś‹©”J[ zxŃÇń &´ŹťČA‘‚Ft°ó]´ĹZʰؑ.˛čÇÇ^K‚’ż‹ ^áżeŘ˝Cě<)\l“ö& 34ěą° ç˝Çذźŕ9)\Ę—Źyjg;řŁčЇ|†ý<<ĺÇ(µ PÚŽ:šě;Ú†zÚŤIý—ë]^.ŤňVřĎbǧ&‡.v¸aż$D=â€đć7pÄ/q!ů ţbČ„ĂÜăŐ])$6¶)G†côéˇ/řÉŢŤ)·aä¨}óććŻ\˛kÖÔBýÄ Ş}ô‘łłĆ*aŮJŽ®^}ďvĐócĆç.›;ÚAŐ•L™'oŢ9kËx·ů¸”#ŔÔ¨¸˝3ĆýoF ­Ö[3f˙8fčŤH@aóň{ꧦçč´ gŽî{tţTýđ«4KÉţGçOŹ_­ţňµhŤ[ҡձŹĚ»Ŕ ĄuÇظqË©†ŇËýípÓ1ä—Ô'&­°úf0ßN<–(FqSÜş'« ˘˘ kﯛ§ĹůźîKo(Ď}aĂÓNĄ>˝f€„9kc׎}«Ŕ) ťµZ.±!ŕ=%»˛ĺôĎ{ä‰cgÓÖÝ7Cîeę˛gštő»¶oydá(»ńÇϦľüÄC˙ôÇ¦Šś)< żűáž0«Â)ĚyěĺšŇ ¶< 'čX§3=BF/¨ŇK[ľüŕÝĎľ[j–NŽ0»·ľş)éćČľîŮZ™ö3×}ťŰ]ĆĹ‘}Ź.†Öw·¶ďţꪼbÁvź?řé«@og´Ë^ř“ű>}©?˝ËÁ ćŻ~ť1vőü‰ożůńôď§%FN]ú—¶’Gö-š>@Ň‚§3/, Cíżm×ţżż´ţŘ•˛Â {‚ě€>ôŁ‹ŹX;{,ŁÓËšOýřůŰ˙ţ†±¦»Cç<đvîŐÔW_|ů·ŁWXK鄯ą«ßÍÍl+)›4Čm겗¬)ĽçŽD íěĽYŇkaŐŚˇč4v@(Ç©•€ľ>W•¶™D3Ľ´ľeÍť‘j˙Ä3űćoŹL_ąNbl÷ćM»ť2 †¸ńřô:N»ęባűď™:Ŕk|Ů:X;wľ}ßÁţkť-`í‹?ęŞĎ;Şŕ˝Î7dě7qN‰NúůËe›ąxBXW.uä fĘ˝5zóţMźöŐĆŘÁ>ň"đÂ7©•i?Ë‹<źč;‹ęÍŰľţ×?żüYh*›7,ŔüÇ_iłęĽBvžđµšS~O»ôŕüăWš*†¨ElEłřŇcs(ě‚O\­Ě8˝˙źŻľ”í +űc‰‡ůÄż˛2ÎĘ#EÔĎŽNóe­ë$e  z˝ŻńuSŔD¦ôSŰGjĚWsß(h*Đb®?×l7ÂQíH >,¬<¬ŔĚĚTf¨=Ń Ë3`Sř©ťŤ—ëłţ^ÔŇ( ±Ů¬˝˘3”2 śÍÍúR“©Q0•*6—˙Úŕľ((`˛*ëĹ|c3,®FĆ9ńN‰Ž*ĄTđŻ‚Ú c×L„0´,ča_íńš–Z‘*©>G§Í5͢±Ú¤˝Ş3T ™©´ťÂ¶»;&0}vł.»zsCjł}˛Ł‚s_żÖkbfˇń‚ÖÔ(D2ŠÍWtúrł©ĆdŞL†ňMĺ%żŐ0ŘD;8…*Ę«“ßN٨MĄúÚßM:Qź©7Ô-…ÍÚŘ,«Lş¬f}‰IáĚŰFŘJőúkďT§¶tĺ!I?lăÝĂďuÉ{=__'ŇíŰâďčP«­ß_\^m2ĺ×Öfëô’$Uj•”^®×Ę–Đ[­ÎŻ­üůZąINfŇ/ŻŤňő PÓwĎžŰ\TK(Ő›[.ŐÔ–h›NŐ4i8dU×ćhuŐcĄ®éÇĚŚ˙d—Ü“´&ŘîĎG~/7[„) ’ŤZ•ěăíI…wĎťŮW­“/:ĐGŠMěÉáqéEy-f9ćúíJ q÷Lq˛ů.§PúŰŐPB$`Wč«^;~2ŻĹĚÝ0s ŻÉOß±ű@ECcNڹ̢ň A‘Őy—vě>P­m:{ŕhěĚ'6,‰XłâţŇ&f‰łßˇU}^úĄ+9WMŤź4Ů•–Żř‘WËĄěf]ĄDjŞ:t45~ň”›Ŕ|űđČąÉlśťĽśŇ~?pü\zC}mÚĹ´˛šš=;wU.S¦Ž.żtę›ď®¨mÍĆŠŠŠ=»wžĎČłÜř ĆĘĘĘ=»w¦eä¦ęĽ˝Ďů%$ŚN»šzdó–UµM” 7ăLvQŤB©ŞĚM?—–­ńô3~‚·J˙Ę_ß~:€`6Čxşb>ź‘'€1I0çeź9w±őńW‡:ťéąśÇ@ę‹®śNĎIHëm'ľţÂ3ßď<Ëł©·ľz(鎹€`6^˝púěĄ,ŞT*­:ó<ĎwŚň|C%<Ďsňő÷uڋÆśó©™…e·0uůőë*áxƤkőć^~&í·Ťy•Zn@_nô”rc1Ł—ýó©9Ϭy8«˛‰ë‹1` JĄMA^úůôě&mCYYÉąÔ´ĘZ}yIfÚ%KIÚůs…Ą5~î9Ź|űÓA92‘`6ş‡FL?©äôok]źSŁďW˘÷Ž6*?=ýJÖĹGÓâ&Mvć_{nýűĎ1Ѭqt:ÔTuá… Oě<›'Y•Ľřôă;Î\s°u¸–©Ťć´ó©Ee5ťňŤtĹŽŤy•:Ęqaa=őţęsëżŢv˘ËŘ!rNL͆ěKçÎ\Ľ °mUjjja~Ö± ×l,ălZfAQUmMyQÎľýń7ż ‹ł¤Yt§Ď˛„Ü×Ů‹Y đsóČ˝tôŰźAćUuţĐSëţt&» E`hXžÜóç®”ůşyä¶Ę«Sߌ1”džK»Z4r´Ź=÷ŹWžůäÇý§ ­nŁ'-őJa©(*+*dëÇşČ=·VĎQč´Ui=s㣯·‰¬·hÝčaiIZÚą˘˛Ú¶YŻ˘ˇ!÷BZfQžGXôřqסuŚÁl6ĺ]=âĚE‰!;ű‚Î$ťÚłqűń+#ÇŚó˛_á™ďwĄ2ô–¬†PĘä5hŘ])q×®Őr4ďÇ­yA8Ľ“ŃdĚĽpúěĄ,B¶:çŕ錑ă&úŮ Ď­_»ĺxÁÜÁŞłvžQJšŞ¬ć”´#›ÚQUŻm(/8qúlł$4ëj~?z¤¤˘^25ś<™!rD©âŇ/ś-­Ń¶?ăCKK‹ĆÉ1N;O?ńË‘Lt3ďĐZ×QĘçŻäI *Ąćš•Mčv|Ý Ën·«j08Žu·µ5•íjÚŃW˝—¶2Pâ1Í­ĺjŁ6ßÔ[µ^ľĄS«ţ!!a"s[ěz‡íŮ•Y7žÉŹ]ç_űÉ19Ih´ť˝/_µŻa@ôB>źXk‚ĆîíV ) Éaá ý·ye¬›ěÍ}2ŠÎ .Ş©¸ÔĐL n[X$ŽP‘IwĆ y6Ôuň¶¦Ű˛śą®ČIBÝ܇8¨~É/ąuďcdžŹžą2ʡô‹ďöW0Ičţ‚¸#‡’_…ůĹCäg×ŮcwĂŠČYn¬sEwęéćŢýőçuB¨Äö˛ű)“$PŽI˘]ô´?}ěB[ĚĚ*nkKOŢm ˇ”XłL’|ľŘZ‡1˘°‘ęš;aďU=G‰Řł<pZ˝迎QĺüE Ż]8śzĄ¸_kýˇŽR&I‘ #†»|żyçÍ$ÂQH]/ß„d:ó±ë‰iF_—™č]}ö´ĺC”D‘u¬Ah÷ćŁTĽ1YB)Ą ’(Hťű˘¬»W—˝*ˇÇÄîÓŐë#7¤.äŢ7nŮ<}ýZ×jń»˝ĺ·ńs=đź''„D\le¶Jĺ¸a¬ÍZ2éş0_ç—\§”ţ·–H>ŁŹo!”X"⎀AŽe BfÉXŰÝí i­)%¤myk˝Ů íúÚÇ*€tÜź´b <ÄÔ…B€P‹ŞĄ k ” Ď{”Ć@“Óß‘6WţZ(ßŐPž01ÖÚ;ëBŹ©µÓďBás¬7Q%%‚$IO)cLdŚÂ"˙,Wâ‘ú=–)!”@´hTßתh'‡pí\ŘŰÉ é3â­ŮŽŚ _áëřč±s­]+ŢÜ}ęR~‘\‡ŚĐŔŘXÔ”–רś}§LĂ·TíÚŘh0ž€(TŁĆM´çŤ6nü~ô¶ŕ!S¨śŁcÂx()7ăr˝É † ř‘I±eŮgŽźÍYż„Fĺ8J$‘‰’Č)óýŻdÉËóƤ‹ÂŢ1S*żę¤„ôŮPÎľžv—Ňł$Đ 8©®¨¨Ľ~®5?(:ĆAŁE `§0·Ô¦_Éë÷§XHc*g7%ŤŐ•őM7p‚hůROżĐ@_&A(%¬˘ ż¸f ľ´ź4ôCWĺ5´ÂČm˙ý÷úe›\ćZGÝjŹ“ÇßóČ{ŹĎś3áÎúsM$sĎ7(49)ˇąşčбÓ&AႢ‡ŹŐŐ—>rŇhî®Ä"[â묯O/*é—X»Ř1J 1™ËęX‡€1Ż€¶ŻčHaFf‘(Aéä;u⮥j÷€Zţ[]¸Ń&w;ÎxčP'ąw+ŻîŐ»ł…¬/.*«SŞťÇŽël#ť9{ް¤úúĚ!% |xdT«…ĺ8ŃŘtůr–™Qžçcbëf «U捻uNEI’¤¶B98„(I„R ĘńTb"“ Çkîú±ľAa#’tŐ…‡Žž6 R· é<Ţű!‹.Č)iWBĘÎřQ“ľŠ €f„{ĘᤑŰăÇKŠ3HŐ­#§đ_#:îĚě‰J´“Ő˙ɱ<owpń˘Â}ŃoŃBĺ!îO˝üAVYU~ĘńHŮ{:őŁşF]^AQIÚ™6 ĺ8…MĚ­¨yhf‚'g—ć•VWřĎŰýš|*`óÚÇ;kkŞŠK‹w~üşíď)ůö•#?T »0ł°˘° //ż°˛0űž‰ćŻŮXRUź•ž©­«zbĹ4|Żńŕ-<[ÉďČyµ÷«}–Q\źyě;%ĐŰăüĘüBtígůéGä8ŮűŻç}ľţ>ô'»w@)Ţ:ü`RviQ~^Aaa~~n~IÎĺĂQvj€*Úż“éžx•J`ů_>jÔks3łkj÷}ţ¶/OÁń é09ÄÂGß®¬Żą‘“ť™‘ŮŘX·ń±…hyjüS_uUvźúŔ?6$ďłéÚ˙ęÇ"@¬C“ó«ßzđÎľ #{™¸duMcsnFFumĂo›ßwB&ß[XŐp#'§ş®ář֝ۧüöă{^@8>QăRËšĎ˙ü‘čĆNčÍŽ•fśLĽ§¨µ®źţéͰůG ň»>~ðćŐw°~^Ť·“Ę÷ÉŠ¶–#{±źv-ż˛ /·¨Ľ:ĺř5¬ĺW5<1o•R `ҲÇËëó˛2Ë*«/ů9ÚĎ gÖ˝,:˘mk ňŽ{ *[ŰţľP÷żĽňa Ď*ďŃš+Jc@cÉá‰-*ŔĆ7ćŔĹĚţ¶üuÖ ůe»ćµ›—{;čl!Ó >{r ”žŰO§ÖU–¦gݨ.ĽvG5®Ď^jşµ—FŘ(Ű©PQo˙]ç\˛*Ş]GňÇ…ěÖÝÓĘ{‰—˙ /—Ű®DP¨†îr·Ů#™M´ť×,gĐŚň{nW˘uOîŠŐh(ŹĺAcŹÇٸS«Hç1§ă}¦ąĆ|7áĚçXµď3á“3GNwpŕ6úP\ž8Ď÷ú±ŮwoŮvk Ŕ&Ć‚ź q*>ĚqôŮaţÓlŘ °Q¨şpëÖ_É ÷~2tř·:k‡¬ÄGżÝwÍüzℤ…ł":­jäo|<RW,y<Ä @¸‹‹ °÷9µpî w;ŤÚĘCĄ"ĺ.Żź3€©!ˇă\5ÂÂS—̉ቋ˝{ňŠeŻÇ† eÁł:qâ•ySś ¸iěWDG?>0jŠ·[WB&@5{.|12·ěMëfIžřLŽŮ;}Ě˙¦4y­ş"aěŮŮd÷5·ÎˇlÎlŁgź>{aÇ–YÉmŐG-¬ÔÖ?ł$@Ô !Îjž#Ö~¸«<őÜ˝ö]]N´ŻUČ şć‡îŠ–Áţw“ě &lę:Ép_BȬű_fLZ9}ĐÍ–|űËQŞUž~˙řî䩟^đtćśB§”4ţűÖń#§'ĆG˘W­&€Ę%đţÇÖżřüÓC˘(xŕÉ÷ö¦ţšyÂçîńČëźyzí¬I‰rÎ^M(˙đéÓ&ĹOľëůçž™9>ľçŚň}ßú/ 2“Çě#ů_>˝·¶Şia÷ öđ Üříńôß~đ÷ô öşŮ8U-~Qś}2ĐY3iŮs:cóš…cđ7廌€ĘÁ)<:ĆĎÓuîCëj«Ë¦†»áfć"ż›ú¨c˛ˇô?—]ţö#wă6­¸ÚHîţ«ßÜś}îł…ěY—ĺ°}ĂF?¸ün`ĐôµMú¦¨SŔÔI‰‚Ç?b0™VNްLŃŤĎ}`Ş˙őó1CsóÉퟨz«®/v,:.Ţžď€É†ËuEqĹ—ÄĹFfjAž-h;«F§í`ýÚĎ ;Ę€]߬(iĎłŐ€YéŮYóCmť=Ľťś @­b’Óóš5Ŕ]÷Ü?%>`ęňG+Š2[ónaŁ ô˙^żB.ˇ;YtF#ÖF Ŕ5 ˛µíA\9!~wśżdţFŁ`3§-—F Ŕ&üÎV}]ť,Y÷qSm?[ţ?ŽşÓŤđi–=®Orď@ť-䱜˘÷X0cɆƦĽ_{@ą`ŃÂh/yťĐ‹€Ę#dîÜŮ@wΛâăęëçĺÔj!C|¸řE<´fÝÓO®Ž t—s[ZőYř…ÇNź>Ůś2a•kyL‰`í:ýΙ“¦Í}öůgî ,2ţŃ>,*,zkíK—Ý3Ŕ×ŮR«:÷ĺĆćĆ•Sc,ĺŢ·o:i¦u/KÇßAÔ ©ďó€ođrµŔTŇ\{Iod­ W™ŞŤĆş–«ć§ €”_—´,ĄčDQ’Ö·„h<}%@Ĺ JÎ~Ľ“cśÓššËŚ$##Ď)¶Ąß–šť:â­ľłG;=ęÍ©,*˛9hŁTKÚô„˘éZCÉŻ5"`¬Ň‹Íí–tćPAĺW缭BźŁŐ•KÍYMÍő˘m  ęLÍe&×Ů^ŽA\c®‘* SĐ<›[×ŇmE˶‹ hÇŹd`’1vbe3 ty'ĚĆ™ -ţÔ;¬nú19Ś6MËąXi¬Ű µ\„l®/žůËî/ Ë”kçË -]n”›{“®AéŕóĺäqˇV¤ŕ ˇĽjĺ ¸çcÂ}Ô ůL“#Đ‹ÂÝ6ŤMp'Ä Jŕ ~c<ÜŚ}^cŁ(q×{‚ÝLI­fŕT¶ďM˙ĐßŃľĎ ŤÖpť `íŕěĆKW«ĘŢŁ÷7ůż~:é’(Ą˛ÜÚÚ>BAŃ«&‰š3÷%Ź˙đ×c •Ś>F[’©Šţó¶-î´Zo ”DĆű páŻßy[<]<óŻťJ+lÎNIĘ©iŠ‹ ‘[[Ő ŔčŘÄÚ¬Óżr÷Ż\"Iúan¶äŰ_Žd4Ô•Ô6 F]]^iµČ0ţޱVş˙úűż$ťŢ{úÂu’Ä:ÇO‘Sˇ`Ś÷ŚÜ˛k÷úwĆŹ›µcÇĎ“býMŔ©UśĽ$b"± ür˶§îżs⌯n|ĆÎJ~jMş+ň;{ fÔ”-»lţôŤ»—Ü˙ú›lU0gě"WKk»Š Im˝{¨«‡”6bBY^NYŃŤÚ:#“ôEĄEą9eżór5 Š«ţ­F'i퀛ě uµY©)Ą•Ă&Î+LÚq0˝ř)t˘ľę| 1ÄĂÚté„,Ź?š7K’Ŕ¸śtŇĆ+ŕjŔŮŃ3?µź-˙GÝéĆč‘­=®Żr廬6 É$‘qÖJ;BÂÂm¨iËćSKšžüQĘ6ĺÓŹŢóç>řĂĎ˙;wĘŕĘ‚’‚ÜV ™“]Ä{GmŮł÷Ĺ5K=üüţ=;₝ sG«Uó­D&NŢşs˙ćO߸{éĘżżşÎ1 |óŽťOݏ3~ܬť;hç6ü‡­»>yç…ËŢúË–XGU@Ě„‡OLĆ‘sW<ąúľp?kX¬Ä:÷ĺ&ť©•YźűöM'Íd}zuSDe–›ŇuE&»©î±ź†Űú(ŘÄ;D˝˝ÖËŁ­N1BŽ˙h)‰ |ŰřGt’ת€ ů©+Ż•^1%…’óZîô§B!i Ěq…Üçą+ęt%{ëP'+ŰeĺöâË\»ôh–$ăĐIÔî<Ą´rÔ!Nö¬Yë'ÖWťŃA^Ët͡™ ÍVQŽÎlś§şXąđ’ńJ©ňL˝ă$w…NW—oäT#*ľ3Ďí¨sŰi?bA}UŞňDémă˘26±č#;$Ę•.54ĘĄÍ sĐ×~“] @Ąvä¤Ů“~őîÝ{ćí=Ţ,‘B–9ňÓ(•„5Ťř?ꆴF‘RÎSÓqíz+ÄD€JÉ·Z1“Aň ‰›ĺ[ˇ·ě9¸lj”lŹŻZĺX•ňÉwű^Šź1}ä i‹–…x9ô¦ßĎAG"UµőŹoxŮGwőpR¶Zů»Onc9r·ă(ĄćáÝŃĘ«ˇ® U+ ”“Ż8]¸”S(GĚž·üŽÁž›?űřÓŹ?ÖÚ…®_ąŔ\şE&+Ďčřh˙í_Ľ:yě Ó–4ĚĎŐş+ą• &“©ˇbÝ‚™ŁĂ¦.| ’îůi·UdŢ!Ý–ÜÇKĽä©áä}€ß·LLzŻ}ű¤śÚR“zxű®#¤›mB(Ć/ť˝uÓ60‡żMÔ‹ŽÉpú8ިT_W{űřj# ľ®™S¨˝8ôÍţPNˇ "ŕöö;o4gŮ}č*€°ŃS~Üuđű7ź.M=»ď|€cĚ)e×Ď˙rŞŔÚođ+kď~á‰'.ŢĐYq&Đ—ujŻvlůŚXQ¦˙¸ëŕ÷o®/I={$»ÎĆF]QS+#ôR–•JmĐ‹^Aí¬ăÚť›u!‹ľXQąË~AE}Ęĺó©ąĺ ‹Ţ{č×X_kÁhjŇüۇWM]2vä×grU*QŮŻ\÷ük׹+u˛ňĺĽ=ˢ=? ;µů˝çŠÁqxf˘Ř!…J­>ô롽v\vîŐĐa3˙Ë˙GQg逴ő¸>Ę˝[j1]L‚­÷˶Ź^©ŰĽëpaiá×˙|Ń­ă-¬®‰ ¦¦F­HĐF­Î`4»O-$Oŕ®Ů+†x “ N¨sŤşoÎ FQlłę‹ž`ŚĆ†Šu f Ś KśţđôŮ÷OâőĂçmcŠ˘©NĐŐox`Úŕ¸9śűř©CüřĆČŮOř÷U“‡&NÚ}¦@Ź‘úň®)D©ť› ľ|ÓI3ű™(± ţÚéoć%­ČÔJ§şK5I‹®\ţKžr€˝ç$[„#„'Ţl0Ö˛Ű/˙PPÍ[btµ&9 Ťi¤ś™rĄň ľ×¬}ďtŞř±L/€pó´%Űë= yppČ}î´›ÁHh’$‰ĄY‡ČHC^ u d)»adfĄéŽCŃĘ}ĺĹ'tďG/q4Ő ŕµâšŻ4h3«ŹiE=# ”YňĽĚµEoŰ)qǶK­üŕÚ†Ŕ©9±ş9ăŻŮ/d×䉎쀶ǡ3ϨŔD&60sÉ;žyÓ!™§´őtBA9CsýúĂ'ź>vâX­aś›7t8ň×3çfî:RÇiĆű¸B§tvĺDc¶ľ˛§căš]»ăżßńkÝj‡Ua~»RŻIŕ(tőĺßd”,<ćę=ó^ˇb¬¤2[uFQ`Lqł7Qţ˙D…(IMF†ţô÷ZĚ»J••ľ<íˇEË^±ěpzݤÁcP÷č§–ĎŢúŃ?oG°íűĎ>ÝťňÍÎCo>1µ¦˛’(UýĹŽdlö óČÔż=ű|Q©o$ÄŰYNŰ!qË!Ł 5+ŐvvÔÜ3ÍáŠv·5Ě)‘Džţa UU#­z~ÝcŐ9©i%%ňmCpfo „B—qňÓ/.asiIŢŰ}T-™} uSr[ŠR©®(NŰ%@ńŤ<ŁŔzŕÇü“&J””%Fĺׄ(z««‹”öÔâWŞE“Ż_/J9csíŢűꚌ¦†’Ââ&ŕ÷<ń§„~÷|kC e÷ńöPŻ:&Ż3kµA’Ş?e_śŕx"šLŤµÝŚ2ť˛0Ńd¬ßúę§YáÂĽeŹ–  „¤űiÄ čé žôŚ›°|î(™'·Ť=}ÁS>CĆĎ™?oů_#śMjߨńý­ÜBĎJ$=GŹoc°';6!v$€´[ĺş<ă&,?¤ˇ˛Ň'r\ ňj%ĄJĄusez›ő‹ €YśEt-‹^­¨\ĺäľ#ď5e^p睏<±fÄč9µVţS'‡±‘¨lcăFđúšŚŞrŻ”ę —Źŕ±-]ńĆÚĺr…˝Č‚0 ¤oC~ĹĽQMíżi—Kn¦%‡U*ż… &ěýâýĎ÷¤ţ–˙¤NŇ‘Ś:‹×'ąwMRDĄŇʨ-3qŘ”Yó_ţdŰ]?űä}łŃ—xľň Ť”´FĄlŮv–ŔNí\U’•_ Ô߸VࢲŔ+­Ú¬znµJNÉ"´u5NޡŤŐU#¶Ž)ĹÖvÖÚęҫɥ0éô&Qeë€ń”&Ç—íę”˝c_.—@)a°/zţ¦‹žňGÜôoq w ŃŚLljŹW›LÄÚß €:ÖaĐW1ńj7™d’äń1pËŢ]YĆĹľ@IdL ™ôĺ&Ą§ €ëîjb(ÚW ó@&äľ‘qzJrć×Őî÷x»Ć«`ůBf™š Śp*_ëVńűŻ tĘ_z0M›o"a ęÇî9dĐ`ČZýÔÔ䫯ä1@_m …ŃtíÁ”Ľ5ś5' Ś1AjăŮ˝«„Y”lv0ŘĘĎő†#SłĐ &“Ľ’QÇ´`Ř2 ÷b"`íĄ†(čęşžďĘĂĽŔQ’„–nâçćłyÖ̆G󌨍"3s-´޶4ˇµ(*ľ|ňÄŢFŦqń Ŕ A$#c€>ż©Ů]Ł0=h€iü2'_ć™ŔřĆé#CľßúÜ•üŮ113ĽlĐŐeĘš†FxO;‡6Ý˙?j!Ž`ŤeĆ˝ôł›Z“ MFŮ÷Rus­ ʢg&Q’ŻÔĎ[ôH Íű×ć­ŻĂŐÝxbÁD?źţĹĸĺyč­ń$O\JëËťí7˝ýěţkĆA^ĺ UČÍ,ân9ćx)F˝Qgî*—ł®8ş‡ON)Ç+”J Ó÷ź¸řő;O+P@@k }C“±áĆâ)ă†'&Ž;zý;?I˛«ł&ŁD0‰R·aÍl?żÇŢܲčÉçćO śŘ}É€Ä&I‚d¦9ľă7rńÄZoĄb€$©x*PL¶ŻűşşHéj+Yţź(Š­N]aöŰW2—J9mUţŰďľ»hÉăî|á€q<×îË^‰Q”@ť–.şëěö-Yu"(w{BÁôUÇ$ 2놉Řx‡Dăvú 0#Ä %b]F•“…˛0ŕŮw?şgĽă„ S’2Ë(!„ă™ jµU‡vn-7đaÁˇ IZmŐˇ][ËšiTtLmy浜ęyxP„-o8wćD^.Ż|ö`ÇĚ€‹rí[Ę Š¨0˙âŞBGŤý·o?»˙š16Č»J[Rf¨“Df‘«SµÝˢ‡ÚĺT–}„¶Ša&‘1Y†7¬[µl{ˇÝÎoŢSzAŁQĘ…eîľţň+Ýđ>ČBćÇ$I8^a‰|xH¨üŤ`ńŤ¬ň–ąd7YfEś¨~âî~¶ü·‡,ĄSbŃăú*÷ö$KËha!Ő<×,)áíxrüđľ÷_\wězA§©·Ň$&)•Vö6€¨ć L&ó=ŮBĘ»I"5hě\ě@˝\*ß][­şĽ&iłó”˘ă2ęŮŹöI Ť$AB%Q”MU)•6vÖčjű»s_ćy^’X‹ť_Ż€ŮFt˙ŤYç-eaúvŽ(÷Eţ ßF.÷ôY¨B2–ďop»;`ŘOŃAůÄ~ˇ±—ę’đöJ§‘Ž‘Öć]IJׇ,sW8©"?Žđž`Çô’ŇEIššł_̡QnŹ{PĆ”ľÖ!C~í?Ő¦b5Ŕů.r®úĄTWÂ0p!Ž7Ĺř/p± RIŤ‚ˇĚ|ÇĚBw¤*mM†Ńm„#&2·ů~akĽÄÉwĄ˙/"ÜFh(śzŕ0Ň{‚ ŽýwDད@Şš*Ž6¨}T”7×Â;đĽ%;ďšůxDĐ@G“Q_Ú( eÇ«EČŚF]Ĺo5ş™>ľŻľţYc c}ÓŠŠJEôéĘoďDDńŇ{_Ľ±öGW˙Ż÷î{döß~UĘ}őío˙ű«©‘ÎÎśź|xţŽŻŢO«Ç+$ĆÔvqżüş˙ĄuŹ}÷í6EYň–‡H·f¤äđöo»\P5|ĚâMß~ď®Ď˙fç~ÜäĆůí,G~é9ĺ§űśsÇ~Ů25ĆçňÎľ9™˙áOű6¬z߉łO=0 €ł§ß„„Ř‘Cb(Ě›7®ŢbG‰–5~ď¶˙69ŤŮóÓ¦Ç}ňčéł«—Ý!OĆ®%˙ć×î]Ű]8Â&těÉ IĎ>Ľdh¤ź©ľ˘¤¨€˝‹O7%ÇĐA«¬5n.NhŮ„ęřM{~§/žSx˙ü‹Ţýü«Qľ6ż%ťŕčâŐS]]ÖŢÉ˘±wvu˛oId7ErllěÝÜ\íÔ(şşóëíW^xýďľ ‚H,ŠëUĐňŐöÁs–OčţýO߸m·Ďú¨«Ś1J`,=µ˙Jńü)łěv®jD€›t`ÎĘ·Ó®ś‘kʵoľxńĚąăű®×$ŚYĽéŰď]uyŰŹ=~t‘čk¶~NG.ť@XʞX/V4ÂéČĄ“śZúŽ’Ŕ–=ţŢŐs‡^\˙âáĂ?9 ÷<ŢÖŮĹ“kH[{˙rőE_ýýQ<bŢůď{Źž}~ń-?˙j`”“ş•EG4ľ?|péıóîÝ~ý˘Üö8'ĂoÇŽÜĆľz}í#Ž®ţ›ělţA0˝ňá×o¶ĺ3oů+×.•9t4ýşď(Ô1Űwíí_Ë˙‡ŞngÝxtö¸˝?ý÷jIíđ›‘{’˙ĺô…6 9:Đî×#;GßóXZFĘ›/®˙tÓŽY±ž§R/Ł«>f2*]^ůן˙çő0·™wÜĺAŔÖÁŮŐÉ^VýCżîÔŰ úäËýóËO\Ą_~=@­˛¶°ę v^ćpO»1ĺÜó‡74‹^žî<p®nîµ@sŢĄŇF« ďţđĎŤ/&„{Á·Mçľ|úŘÁůSâ¸xG40iÁC=}#żxi›,6íß˙Ř‚©c˝źeő™8’Č”>jŰpkSz}öůŤe&&2NĂŮ„ŮHµşďĺU&5`&f,ŃWź®kĚŃËŞbĺ§6ëŞOŐµbsF“®Ě(émjŁľX_—¦çhJmŇŤő‚±T_¶Ą´đ—:×E~“T/äš`~Za`śďoŻRJy˙Ę«N5tÓ“AŻe{kOV5W‹TIuYŤÚl˝Ř$*ŤÚôF}…‘‹{ćĐ ôµrboH©ÍüGľ®JdFˇţ˛ÖX/D2M×uĄ&c•ŃX#ËôĄ›K‹v×Ę‹«H;‡`EéîÖˇäFQ—¦ÓW ÍůMÚ,˝Ř$*ŚŤY:}Qsmr“&Á^mOň>Ě/˙Mۆ€¤3iŻß2b€Mśkč˝N9ŻĺęjDBş:¬ńµ·«ÖÖ*,­4s««3u’$•k돧ÔjAdÔť,­ŽđöňSÓw/\üĄ¸šPędkíl_Y]öŇé —ô”ť©ůjUu‘¶álU5‡ŚĘę,mcĄŢPŢŘđSZę'™EÓ˘†­ÔüőŘ©R“Y˘ Y©U ^žîTx÷âů•Ťň5ßvü€B#{jHLJAN^ł‰öĎÄ˝OD a`A®î‰VßgĺK˙c>Đ(!0Ŕ#xc¬ß«'Ďä4›¸~‡€ĐĐŞÜ”_÷.««ÍMIąžqĺČń䉓aý×;ĎĎ}ěíżĚX˝beq“ăę &kpŘřq‹Îí^ýŘş¬*]D:g”©ˇâčń¤¸I“ťi麇=ť^B(e7÷nűö•CäŽ~ɧźĽRW[ť|%ą¤Şj˙ž=•Óä)ŁJŻžýö‡źËŞëE“ˇ¬¬l˙ľ=—RsĚ'ľ‚ˇĽĽ|˙ľ=É©9 ¤ˇ2çŔ‘‹>C‡Ž“žtl˶_+Ş(AvęůĚ‚*…RUžťr19ÓÚÝ{ô¸ńž*ÝË{b׹‚I/—ÓąäK©9“SaNćů‹WZ.µű¦#?×rHmÁős)YCÇxjÄ×6<óĂž 0{Ş«›”®Ŕ“!ýňą W3@¨R©´ôÇĚó<ßŢAsűžăxADÁťvéôů+CVÖ•ZťˇđĆĺr­¤R*¨9'÷ŘcđvqËązü»­G m"oőUW Ç3&ݨ5mxé™äÝ›rʵ˝MNU(ĺ$ƢF-ýçÚ9Ϭ~8ŁĽë-~ Ri•—“r)%łA[WRR”|))#·ĐŃŐ9jŕ@cĹĺ ëźÜs!GMÖövŃ+.oxú‰í'2@¨( 0™ô™W/žż’Ń'.{˛cü«Ď­űńĐEÖľ®§r`¨9ňŰEůµ=z:«M%GŽ%EOšäČo|nÝ7»ÎvľÓŘNżnĘ)o¤çŇ]íź[÷ÍÎÓúŽÄ`$× ¸¸ŠKG×®ůË…ě ĘÓƆň䤤üÜŚ—oŘ(Xę…ä´Ľ‚ŠęŞŇ‚¬/?|űăow ‹˝¤YX˘Q_—})érF¶­›K´ŚüÓOn?‘ ^éÜÂs}VňĹ´‚Ň€á•9WÍą’“®Ţ(r1xPxy ‡„‰.!ýmů˙`jŻ9)WŻg]?r<)n⤛’»%1ĆP[Öf!˙ö×­GŻš´"g;t„=W÷ú‹ű϶#¬«ÓŹ6Ö(eLň0řÎÄ7Ş9šóÓŽ#Ľ üvčPŃhH»|îÂŐ B¶2ëČąÔc'řh„çÖ=˛íd*ÁÔÎŞłvvžQJ*,Ć”äc[¶ţZQ«­+Í;}îB“$45Vť:~¬¨¬V2Öť9“*rD©âR._(®Ň¶^ăęş/''ĺWŠ‚Ń<˘]Ď‘TJë=|c ¨Ąff]JJË/ůqƺųë4Ö[JŻĄ˛®íǸÚŘKöÖm˙V˝×ş(q›ęŇś^ŻÍ5Ţ4K·‚GČ\űßasaE†ÔG@nŠĂ[AL©±őć+Öő+*ٲüÝe`) H@‚_H¨B÷]N “¤7uɝЙÁUeWëš(Áď‚ČLˇ"“¦E |6ŘyŇÎ#Ć~ědýAć ˇ.®íTŰs‹ţđm1‹ÖËR5sE„]ńľ?Č@Íá¤-ۡ„ÜD4ĺľ×Ţé×˙kĘ!ňÝŤ›¬±«nEä(7Ý˝ţ#ŤaűůŠYĽ´ŹĘQD% ”c’¨‰śzxëÇN´ŮÄ,ü¶±v{ŔíS# +©ćˇąă¤ë8J$P&‰jżŃw~íie0Jr Çvß}ˇaŇ힥ő]Ǩrţ˘…7.˙–t˝đ¶Í&ĺđçáC‡ túaËžţ Üą©˛Üä0ö’ÄnEÁ;ŢyÇ´őŤ¬Eô–«•z–Eݵw.ϢB»6ĄbżČâwĹ«Ą¤Ă#ňn#LBä>nŇQk;mąDy'‚đ<'Y†şo ç €±[PB-LÉroß”>Ű«…$-/”ş-ă%Q›óŘáOžvĄI”Ë$”ĘľĹX«µěĐŢľI°Ď1@oă¬úŹ$ů^2!ňJ’µK–JĚqGŔŔ:m{™Ó-tťP"żéoűŇ:˝µ(¤­÷ö2 °ŔśPBx"™$0ž@bLę‚CÂP0“_%1 „ÖűŤ` žA“Ăd7ďV×_ÉďÔxČ—ľĚĘÜĘŹ:C‘Éü0©k ďÄzRK˘¤D$ ŕ)eډŚB8Bäż»jz/OŇi‹Ťc)­op,Y–E!1&»ú{Ř·čÍüA$Ű‘AˇË˝í;qQřîâýϡJ/Lc<Ď3I%¦ŕyA01€ă8ŃüňAžüńŃÖ ˇ”HćĘô÷Ď0˙ärĺ(“Ú);!”ă9H’ĐiÇJ9&JÇńډ–kEžç“D‰ń”Š’(vrđéľ”L(ĺ(µ\ő%Zź ´ZŁľÔŐU9ÝÁ ĆxŤĎśYÔ\Ű”‰PŽ@’Ú +íSĺ$ýoűvÔš$ĆŔ¬\‚ďĽc”’ćŻĺţčŻŰ ë„^ç”rOM&ÓźĐóű¬crTűŰÎź%7a(Ç+xÎd4H <ĎK’Ôy&Ęq< µĆPg’Čá(% GĹ^UČĚYvŚă8Q´Tř6ć)ĺäa°ŐÖQJ%I"„¶Îf8ž“D©»ö¶“EjG§~!wv ¬Ë™ĺxJ má×)ĺ`Ńz–ÇóňF(!Dyn‡3(%ćmĘń̲·rĽ‚°.úŻĽ¬hY™–écZ"˙h•iŞŐ†<1ožő| HHŹÇ ݡ!‰˘%'”Ň>^Uí‘3+”ăŘg7Śň–gÄŘgďźúƆç‹őŚç¨(;ŚîÄĺ8×9Älů;[őN)íĆI”@xŽŠ‚ŔäEŁÔ2ĘÂsDËe¤e[0âx(ŠR}îË7í…ҵŽÝ ‘¶˙3¨­ůfˇ©Ě@奶‹Öµ†şd­ÔeŤ„¨¬mÔb±.©QxŢó.gíąęĆ"đ„ Śh6ľ*BÁô˘._/ ¬mîN:Ä µF}•@(1‡„”ş1×` Öq“]q^'źźPG…Ú‘kľˇďYŰÚŐ2ŃnM± ¶F˝©ąĘŽŘY™Ę &Řř«-y6†DŮ9…)Š·W3€đśušň!Lolşad¨­Ň)ޡć\˝$tŰŠv<÷b „‰ĚaŠGčÇ”•iúçăj++o•"Ż®Aěš;âëč4ĐѶɠ;UZi”ĄŠ0G;5@ŚBsZmŁęEaţ)%y© žRQ’xĄ:ĚNŁ 0 Ćěz­Qbrđs‰1âăä˝®Hg -[,].˘ĐbůxŤÓ¶)Łľ:wbGq-Gx»vL͇!®îcś¬ľÎČű_»#,/şÂ_‰öąo÷ˇÂţÜ,5O+Őnţ.¶9iY3§XąyřąŘĺ¤e L_™(ŚĐ€ˇ4żN˛Š ń@(‘ĘňnVŐŢÔŚ§[v˘PŤ;Á–79ň›Ţř;Ąq»Ę!S¨#ŁBx();őZ­Ń†€°¸âK2ĎźĽ!˛> ŤĘq”H"%‘S¸­zě~k’{íüż–%/Ź[“njř”ouRBzÍ(‹ŇŃÍŰß]s5%C Š‘j JűCĘ„eg­E `§05W§\Ďąy‡ĚýI˛x|Âcuµ)E·óĽ¶/ş*ϡľďüďż×-Ý|řÇŃľK˙VHŢNw÷Łď=1sÎřiyFôő¨“mˇ »¨¨5O@x®45­@” rđž%‰PJA9žJLdR»ă5KÄ"ăĚşqěLç~ѧoaŚy' ÚTYpôÄ9ŁđGX0J¨‚í'Ä˙3€ă4ŹŃgFü2hôńaq˙đWŇ.ľ·ášxtŘ]qŁO ‹{3@ @m“řŰ0ßITA8ÎôubŘťFŤúŮň¦= đ·qx¨÷MkJOR¦ŕą&$qs„ů[Ná»:přˇˇ‰[ŁlÔÝDh±h×ĐwI‡”·ü9Ąjđ/C‡3€ö<Ć^4ÝÁ~´ë¨3 xŕşÄwř·á2\€Ă°˝C÷ÄŤ<?ě«0 pÖľ4jßŕ‘ż ô˘×q1,y¶˘•ÍŁ·Śü§ZżwhČ\Çîđ$ÄjŐ„3K%Í›ĆScµBE„ú'-[raἴĺK¶OJpöŢÇ/J^<ďŇŇĹGgŤS€sJZvĎ.”0:4âÚň{.,ś›˛lńăĂm”h‰eé`ďyn颮řŢŞČY–ÄŹNž;ŮÚ˘e·‡äN>#:îĚě‰J°ŰX{Ż$źŢîŕâE/„ű˘ß˘…Ę1CÜźzů¬˛Şü”ă‘6*ďŃ–b­@ ‘źĹ;…MĘŻ®ągÜŔÉK_®m¬ż‘“ť™‘ŮŘX·ń±…č‡xçňž—Íkשּׂ©*.-Ţůńë¶rú˙j9ň3ǰ± 3 + ňňň + łď™`ţšŤ%UőYé™ÚşŞ'VLŔ÷ŠŹe„>BđjďW?ú,ُ>óŘwJEqk ÷‰äČŮ+Ö~–ź~DŽś˝˙zŢçëď@ůßů§ĄQÖᓲK‹ňó óósóKr.ޞSTŃţ-MŻÔ9ögďßtÉĺřDŤK-k>˙óG ęŰtźuUvŤúŔ?6$ďłéÎ˙ęĹ"@¬C“ó«ßzđNô©§w´-Q+šđi×ň+ ňr‹Ę«SNlQ6ľ1.f–ć•VWüâĽÇ_^ů°ýą©ş"mä(xüŐŹęuyEĄçď)Şj¸‘“S]×đŰOď{quo­+JŁŕł˙‚™ź˙yۡWYP |Oµ—¤ťhÓEuK­»°ýc Öó«ž7 €J©0iŮăĺuŤyY™e•ŐŹüí瀳 ë»,ä¸ďvţmí:řĺ»öőŠřĺÔµŠŇ‚‚âňôóB8~IQµVĆçĶ÷5ŔÜGŢ®j¨5Űů¦†—š µç®sir®´3{˘|m-Ű%«ŠÂ+|Ç©ó7g÷FůX.k7v+Ó.8üâ];6qů•=˘áëĐőhG˛3şđ)Ë *ÍšplËî€rŔ´Ôłf^=öŁu7ŢŰt ł…L+řěÉ%Pzn?ťZWYšžuŁşđÚaÖ¸>Ű’n-äĄ6ípëk˙o˙]ç\˛Ş]G7Gň­’°w˘ĆťKňĎ ÖŃv^łś9@3Ękěąa^‰Ö€…·e /+§x «xŹq‡{%Zęa;{ʱŕ2ÝÍ)Jĺ4ËcÔţA¶j˘ô·M<•¶ÜU~Ď„Źü1B!»u÷´ň^âĺżÂË%Á¶+ţ ŐĐ]CCîv6Łâ4dSLĚ[wĆj¬ş^ŐX¶kđ»-ÁŘ[RŢŕÔę¸mCÇźě«ö}&|rćŔéÜFŠëŔ3çůŢC?•1 Ž »ąTsö µ#ŔýŢ ±ÇălÜ©U¤ół Ţă4í#]ňLÔÖńŰű1ą"ď'C‡;  7ă„Đ8úíľkć×'$-śŃiU#OšťlíÇxąđ /Xyď=^¶°ö<»hŢ w;ŤÚĘCĄ"ĺ.Żź3€©!ˇă\5ÂÂS—̉ቋ˝{ňŠeŻÇ† eÁł:qâ•ySś ¸iěWDG?>0jŠ·[WB–/Cjö.\řbd,üoÜ’'>“#böNÓ›5űsH^«®H{vö9†É­s(›3Űč٧Ď^رeGAVr¬F Ŕ>fN[еy0ć8`퇻J®p¨˝SdLŚź§ë܇ÖŐV—M wĂÍŘî.I¶ŚaSĐI†űBfÝ˙2cŇĘéĐ2Ťřź-G©V9xúý㻓§~z=ŔÓ™#p ťRŇ`úď[kÄŹśž‰^µš*—Ŕű[˙âóO‰  ŕ €'ßŰ›zřká¸űE<ňÄúgž^;kR˘śłA Ŕ?fhn>ąý• ŰĎRuL– ”ţç˛Ëß~änôĂÂÍ‘ěŢ}ő››łĎý˘č8ÝŮ›đ;Ółłć%†Ú:{řş:X˛îă¦Úśh_«! ´zăĘ;Âŕ7ĺü…¤öç¦ęh«8jaĄ¶ţ™%‰˘ăâí98řŇ‚OUiau] ¤ćĚkWd;_´ýd™Ř\_WV­5çŞ-/..ĹăůŹĘ˛˛ŠÖoŞKËŞ´(9?tpg™¶®NÖ4ŇĆĺiŻhÜÔ…X9FBu~Z[KË +«+ÁSjí¸ćŮW__÷ż›­ŘÇ] K É$‘qÖJ;BÂÂm¨iËćSKšžüÉnĺC‡MůôŁ÷üy€ţđó˙Îť2¸˛° ¤ ·ŐBćdńŢQ[öě}qÍŇE?żĎޏ`'ÇÜŃjŐß|ëE‘‰“·îÜżůÓ7î^şňﯮs ߼cçÓ+îŚ7kçÎ_FÚą† ˙aë®OŢyaÁ˛‡·ţ˛%ÖQ3ááĹ““qäÜO®ľ/ÜĎ+±n«BË;%ŇÇo$ @Úů˙Ů´­ (-ËmŇ™ú.¸>ĎDp6A+śs_Ěä{;z3yfÇ!ký¤ÂúŞ3:ŔŇ]D‹ß­!Qó·ńU6ž®Şľ.+•Đ$y­ P{©RW^+żbp  Pr^Ë˝9'Ą‚´ć¸BîóÜuş’˝u¨“•m˛ôýĽśę¶ś$·±“¨ÝyJ!hEąŹĘ:Byұ3·¬ĚíZî’űR&?ŘŰч‰"8Ť9e·;“LŚăĄŠ3őnwy“Ëęň)§˘QńťynG’Ô”Ţh,2ŮMu÷ĺtaÎU]±Îj…‡ó ćëhĺÂ×ˡß;ř+ëÄ3!DĐőb`´á©Ę‹hłYŻ.@I)%í¦Ă|š…zz2*>ĚÎęZŃŤĂŐ&+{\ŻŞŞhĐ0/Ôoö/ż^o–t‚¸4.ÁÇÖfŐî=Ű+´|§Ľ'6Vٶq˘†K ŤrsĂĂôµßdP©9i6źż°1µÄQÉ5ËŽf-¸“ůőÓ(•„5Ťř?ꆴF‘RÎSC®Ö˘Ţ7ML¨”|›E‹€RŻZĺXuíÓďö#"cĐřĄŁ?{ňŕ8ŇOŻţHUmýă^öŃ]=śÔ¬Vţî“ŰXŽ|`ĚQJ‰ě ĐŃĘ«ˇ® U+ ňă\sś»Ž2“S8ŽfĎ[~Ç`Ďż?ţZrŢřřłő+Zó6Ú;e˛ňŚŽŹö˙ú٧žţŕ€łM“ÁšĄ ɶO4L¦†Šu fnOĘö 4]glő µ‹˝’ęB/S bĄÍÉű$-Ą˙ B©(HżÁŻ­_ôÂóíĆ<7.Čd2#pŰęEÇdÇ÷>Ž**Ő×Őţ 0¨Żkćjď.éš©/ö§łmˇ’>ĺňů˛Üň„E-[>w`xlvîŐĐőŹM9Hľ+ÄÓń´Áü,ŐŇţô‰żNu™ ’WHÜä¨ë×Ä-{®¸sĚ˝5aúw˙~+,ŔýĘď÷ž+§€(Éąᱸ$=8~•™/ÇÓúŽŻ.eŃkí÷Îąi_jëS¨ÎZ7>Řd(ĄÚ&ýűĐ/ŔÉŘ‘?śÉť®"*ű•ëž·sósćŞ.dĺËĺ÷] KJŇ‚ă’Űän’Şn$íľ”ßřŻ_ka]ýčÁÉÂFOţá“wd|öś-卢 ŰůĎźz @ÚńÉů϶ć:°/ @ë~–ěE őČţKĎ·}ł? ”‡$t–i‹S"®‡!^Ž' @´Ť˝Łqł{ ”RI#ĆNÝüé;aîWý¸÷rU»’t®4·˛ë@ ±˙öh(ŹE>Ö¶ő°rŚTQ'µű["2I¤!Ż„ş˛”żÝ02óCxó$OĐuÉŐI‹®\ţKžr€˝çxÖ,§`K ‚®ÖůGŁ1í”3S®TŢ Á÷ş€µďťN?–ébž¶d{˝çCˇ#ąĎťvÓ4ˇI’$F”ík9k쥍'„3/°=—řZŰ™ŰĹ9[9¶ń\ěmm'qVŽ*ędĺ6Ζł&úËZmFcő1­¨g„”2Kž—ą¶čm‰őמLĎx3/iE†Iˇr›dWł˝´ä¤.âýŕĹB­^~‰eXW<1(h? fžQ‰Llť–wîxć—ú’yJ9‹Ę*Í›ůóŽEĎ»:»ßčÖ\_ňĐÁ#=snć®#uśfĽŹ+$pŠHgWN4fë›ÍŞll\łkwü÷;~­ŁbC@í°*ĚoWęő" …®ľü›Ś’…Ç\˝gŢK#T˛k8KXuFQ`Lń§ŢDů'ŽB”¤&c/îénŽäů¤$»$]¦‚çđ§–ĎŢúŃ? Důö‹`úÝó­ ą?ě:Ňň}?dlö óČÔż=ű|Q©o|®ŰYNۡlË+HÍJµť5÷LłŐ&„W(Úť¶›S8"‰<ýĂŞŞF.ZőüşÇŞsRÓJJäۆŕ|&K(t'?ýâŕň6—–ä˝ý×GŐćŕ´›’ŰR”JuEqÚţ+ŮŠoäÖ?柌0Q˘ ś(1*G~$DŃ[]]¤´§_G-šüçůő’n®XóJŁ^í5~°ż•[čâY‰»]ŹűzŐ1ů”¬Vk$Iˇę·Ű7CÇŃdj¬íf”é:“Ů’ČMhĘ:ĽŕÎ;ybÍŃsŞT~ LŘűĹűźďIýfçˇ7źZSYI*s“--R_4ç’3)UVúň´‡-xŲĂéubGH;±uÄ čé žôŚ›°bŢ(¦Ö\ňüőç>űtwJ?JUźdŃ[í“âF`”S(<Çu­uÓăx©YRhbăFđúšŚŞrŻ”ę —Źŕ±-]ńĆÚĺr…}—…ě$ůç>omW}Mucł8nćÉÁęűçÍś1g1ńüĐŇ;džÜÖŠĎ}óGAĐbçÜsŔô…«,s­^>-®‡p0mÉjËo^>[.§˝LŰ쏼ҳä°Ö¬ "SZ÷Іx“7Ć%I~ü'ąĄÇ>0oTsćÁEsî’5łÖÚęäá–íę–,,¤ JĄ•Q[0gâ°)łćżüɶ»~öÉűfŁĺůeŹÚ+oó”al ĺ!€ťÚąŞ$+ż¨żq!­ŔEe €WZµYőÜ<j•ś’E iëjśĽC««F.lSŠ­í¬µŐĄW“KaŇéM˘ĘÖă)#LľŮůŞbžqî›7 `Ç·ÚůŢľ‘ L4ë·ľúiV¸0oŮŁĺhżĆݢ„CSˇŕu·‡&#¶j×±ö đ[ä>”żô`š6ßD8ÂÔ1ŽľŠ‰ř‹—yߎ#™Ř$ÔŻ6‰µŻŔ8ŽeoĚ®,ăb_ €$2&ČĐLúr“ŇSŔőw51í«†y rßČ8=%9óëj÷{Ľ]ăU°|‘‚– oF8•Żu;u™d’Z·qČ ·«± Ą]•Ű$G±^ßX xŢíˇ ä¨­ĘuŚc&ÓµSňvÔpÖś$00Ć©Ťg÷.Ň*8Y믟šš|ucľčëŚÔŃ–µăą%Ľw˙ &2Ö^j‚®®ëů®Ś’ŔQ’„–nâçćłyÖ̆GË3'žR0Öh2/Č«i°ťZ,¸ĄßI*ľ|ňÄŢFŦqń Ŕ A$#c€>ż©Ů]Ł0=h€iü2'_ć™ŔřĆé#CľßúÜ•üŮ113ĽlĐŐU隆FxO;ć~Só˙O G°ĆŽ2cŽ^ŢőďĎňM‚d4MR[É)„ó=Hóţµy+&1Q”@ť–.şëěö-Yu"(wëˇ3äIdi}ął˝ă¦·źÝÍ80Č«Ľˇ -—(ţgË‘HF˝Qgî*—ł®8ş‡ON)Ç+”J Ó÷ź¸řő;O+P@@k }C“±áĆâ)ă†'&Ž;zý;?I˛«ł&ŁD0‰R·aÍl?żÇŢܲčÉçćO śŘ}É€Ä&I‚Ädë*ßDčž“X«ŕ­T $O Óöu_W)]Ě9Ě艢ŘâÖ“´ŢFéwęEĘŚ¤®&ďZvŐŹ<<(–·ś;s"ß5çýL}Ő1‰¨Ěşa"6Ţ!Ѹťľdń`@@(ë2Ş @_ă# ˝m‘Ý.`&‘‰"8%PýÄÝ=ü|f<řănTäuÎŐw’sÉ>䪛kŃuÝÔâ,މ˘V[uhçÖr*' r.Ů;m]Ţ ,ř)ĎC‡ŘđÝˢ‡ÚĺT-}‡—@ëkŰiÝĽ“­9ăúU÷n/´ŰůÍ{ @/H‚`4 "PyٰĚÝ×_E Ľď˛'u7ZŰ%šYRäcŇŢřůĐ™ó‡MŻ1ĆE©ź!@é&Űůô*#€źpŁ6o[k®ŕ@Rë 0Aľíľh‰ł<¦HĚÂJ0ĐŽČWä [jX·jYhđ˛Dn˛7pĽ‚ ˘EKC[ fY ”ż0ZXH5Ď5 FJx;ž?Ľďý×»^äé@ę­8‰IJĄ•˝ Şy“É|OG¶ňn’H ;;‡@/ОB·ZuyMŇfç)EÇ1eÔłí“I0‚„J˘(2óľJ©´±łF7Çě  `x;;Ď÷ôÍzĚväŮw?şgĽă„ S’2Ë(˝Ő‹ë]%˛®€WcF˙ŔuľßäüŃĂ7GGľ:ä‹÷Q¶lÇąOĚ›řU¨Ľâň\8|[LĐC>±_zK”°y"Á{”u׌Nů¸‡óť㯎řVčŔĎŁÇ%%/v¸ÁۇF<ę €đňzÇÁ›bďqŢ6ćpśŁ/ŹN#‰ükŔG±q|Íży¸>8~Ë ±§ă‡~á3Ő€íx3‡´S»Fý*Ż@ĺ—÷ŻĹŽü6*Ô#~K𝬑+‰Ů2$b™‹Ă·ń©‰m;RWš˝aýÓ‡Ď&=˝z€ˇsV3ĆnśřN€S4k•ś";=ń‰śśYŢ|îçMŹ?úä‰ Ékîź!×2eé3 Ťµ{wm{táHMظ“’^zňˇ|öSCYÖän†Üőpw%«Â)ĚyüĄŞâË6< h˙MG~(ŤZPˇ“¶}őÁ»ź˙ 6WÝ3)ŔĐŮ=ŐŐEJ;D~]ü·MIĺÉ?s]s[‰PN­älřćbyŇŹ·ŤĄ>ëŞwM“dÎ%1ýÚůcp-Ň‘Ĺdů c†µóFŘŘ:¦;¸fńLg›íˇ`eßĆaţ•~j@× O,čM7:’ŮwŮ“˙ĘH˝ ·”5Źsźłę­´äcfÍ,Ľ:(Č=Žň{Ŕ‘m’™ęf r»ôÉŠŇoľ¸ţÓMżŠ‚vÍÜQ–řtQÇš|o•Îth󟽟1väÇŹ<lř6©<ůgy’ç9­ Ö´ó›ýó«ź…†’yĚâĺV«Î+hgç)o‹1ĺTňŐç÷·BßP6P (˘ËšÄź@ˇ <ť^žzîĐ?7ľémŮöΉÚBY7ZÇ™űžęék<ôc,ýŇéďľýéô±wߏ~š?ČÄmg\L`şĚ¦†ŚfĘÓćěFm¶^l •FmzŁľB€ČŚĹúęÓuÚ=É q¶ĽMŤT«»ń^^ŐexHM&íőF}±ľ.MĎ)Дڤ+6ëc©ľlKiá/u®‹ü&©2^Č54ČUçŔ;ÄŰ«”RŢżňŞS ]Ä˙Ą z- xŘ[{˘˛ąZ%V~jc±®úT˝Q+ęŇ›tĄĆVsôŰ•ŐÔŮ ÷4 é2›´™z±Yh¸˘5ÖK‘ bÓőF]©ÉXe4ÖĆ2}éćҢݵ  V‘vÁŠŇ]5 `"”ŢjŰpkSz}öů …&€)ĽÔŽCí ×j3^Ď×ŐHr˝Ć ~,ynÎhŇ•%˝ M˝eÄ›8×Đ{ťr^ËŐŐÝ\±%ľövŐÚÚC…Ą•FcnuufŁN’¤rmýѢâ”:­čEÁZĄŚpv26׼qîüáJ-q˛µ‹v¶Ż¬.{éô…K zJÎÔ|µŞşHŰp¶ŞÁšCFeu–¶±Ro(olř)-ő“̢iQĂVjţzěT©É,LQ¬ÔŞ/Ow*Ľ{ńüÁĘFyűż„0 ĐČž“R“×l’}®ß˘„0° W÷D«ďłňĄ˙±U %DxoŚő{ő䙜f׏ŕZ•›ňëľĂeuőYÉÓ J„Wć\ýußáJmĂ…ĂÇŁg>ą~IŘęĺ70&ďćx»¸ĺ\=ţÝÖ#Ö_'GŚR"5T=ž7i˛3-]÷đ٧ÓKĄěć¶sn_9rÜ1+G?‡äS‡O^L©«­Nľ’\RUµĎĘiň”QĄWĎ~űĂĎeŐő˘ÉPVV¶ßžK©9˛qCyyůţ}{’Ss@HCeÎ#}†54&=éŘ–mżVT7P‚ěÔó™U ĄŞ<;ĺbr¦µ»÷čqă=Uş—˙öÄ®s9“^.§sÉ—Rsä‡l’`*ĚÉ<ńJËĺŻvßtäçZ©-¸~.%khâOŤřÚ†g~Řs&cOuu“Ҹ€`2¤_>wájU*•–NťyžçŰ{yţ])<ĎË{µ9ôĄÉ¤ĎĽzńü•ŚţĐçţÔUÂńŚI7jM^z&y÷¦śr-ׯ77z J9‰±¨QK˙ąvÎ3«Î(oŕúËÇҶÔ×e'']˝Qä> bđ đňKG×®ůË…ě ÂD—Ŕńă&ťŰ˝úńuYU:Âsţ!!­ą˛.%Ąĺ—ôţ4ˇť«ÍMIąžqĺČń䉓ůWź[÷㡋L4YŰŰEd¬¸Ľáé'·źČŻônÉU—}99­ Ç-$rÜ؉EçvŻ~l]V•®s@v˛řuSNy#ĺ,xîTűĆçÖ}łót§ľC“Úk]:ŔšµIIIůą'.ß°Q°Ô ÉiyŐUĄY_~řöÇßî$5zÉÍĘB0\Ădś~ä/y şŇĚ䤴Ľa#FyŮroľňĚv$Ńź'vKc€—łkv‹ť'@QÚĹäts®7^zćÓźJ ­ˇcľyýĺg>ýéÄ)ý+[ešśt=żXŚĺee˛őcí9\ýŘşějGѨ­HľŮ)˛›ó€1477[;ŘÇ´hž ą„ňÎAqq—Ž®]ó—ó™%=Ç™‘[Z[Öf!˙ö×­GŻš´"g;t„=W÷ú‹ű϶#¬ÇG†„RĆ$ŹďLŚąqŁšŁ9?í8 Âo‡5HŤ†´Ëç.\Í „h+łŽśK1v‚ŹFxnÝ#ŰN¦Lí¬:kg祤ˇÂbLI>¶e믵ںҼÓç.4IBScŐ©ăÇŠĘj%cÝ™3©"G”*.ĺň…â*-!mćĄ3bŰŹĄÁbÜINÍ‘zűćŇő‰AĄ´ľ‘“r)%łA[WRR”|)© ¤Ş_"Ý™ńlU>·ş…B-gŇ]ÎŞ Ŕ`?ĆŐĆĆX˛·hŰÂŁër:%nS]šÓ뵹ƾ>ă$ DŽio‘B ëĂ×–#Ld.‹}‚ď°ą°"Ł«\·üÄáV“=7Djl˝ůŠuý8ĆuĐ3ů‚H+–]jˇüN:Á/$Tˇű.§„É Ň›ŇVBgT•]­k˘·Í-G¨Č¤iQź vž´óńvFŕë *rP×vŞíąE·ţAľ™#őáv‡,…Q3WDŘńýAF9Ö../!5_ÜîżÖZb˙ű%ń§–Cä»67YcWÝŠČ^S-cEw¨éŹ{ÓŢ~>G!ýŘ^ľ°Î$ ”c’¨‰śzxëÇN´ŮÄ,ü¶1XŞčͧ0FVRÍCsÇH×q”=ĘĘQ"G'˝ťŽäşűŞcT9Ń—Kş^Ř×P·ÎĄL’‡čôĂ–=ýXľháZ„P"ß—ę—vuıóŽiŻťąÇéVϲčµö– €.¦˝„vm>8JĹ>ˢ{;O: ńť›jŃB9Â$‰1ŇňDއVőĺ›Ţ¨;ݡŃw«.Ǧ$ö© >v®Ž—’ďK;@Ń™ä·qs;üÉSă®4‰rŐ„RŮokµ–¬=G}k|ź 7)ĄľÔţgLŁşŢŹ&”’‰µ}"żĹçä%ˇňîÚ}cQÚ-(!¬˝sť–ż[őµ—Y@ű…ĺ c <ÄÔ‘ĂŽY:•Đç.Âasěć=Âęú+ů ]IżËĹRg~,˙±˝¸ű±í›’A’$€§”1&2Fá‘˙ĆÍ%„Íl5Ú+Ž\;'Ź^„P ‡ęz5ÉvdDPčroűÇN\n˙´¦Ďt«ŚÂbér„Jy"H9ž‚ ˘ČńĽĽŽS×â"„¶Ś—ňijíµ€J‰dŽ©L™ô{gnr9„r”I픝Ęń$©ĺ1I)”rL”$Žă-ďó<Ď$JڧT”Dy°3»TęCÉ„RŽRËŐQ_rˇÓÄ«OuuUNwđ‚1^ă3gÖ5×6ă ”#,›˙{RĺţčŻŰ ë„^FX‹ÁźPJ:Î ţ`곎ÉQíoc=áÔ—Ż©RÁ &“ÄĎór ó?ŚłÎu1ŽăDŃRáۧ”cLd NĐëŠt*ĎťîQrŕ5NۦŚúę܉ŵ!żoąő{z‚|âę>ĆÉę댼?sÖĐÉ‹®Ä đW˘}îŰ}¨đV6K cj7Űś´,€)U®ă&Ť¶ŁşăŹTčŤÄĽŹF¨t4«*.(­ vőđw•sDˇ9v‚†3=ú›ŢŘoµ–lËŽůý%ß®rŔ*ÇȨ‚ĘCĘN˝Vk4! ,nİč’Ěó'/d¬OB#„r%’ČDIän«»ßĹšä^;˙Ă/‡%łX(ײ{×÷!AŢ­dć “^2Ę=ÂŃÍŰß]s5%C Š‘j Jkűá¶áDFŮY+DQÇ)LÍŐ)×súut»9†şë·­ú^uUžC+|Ţů߯[şůđµV7Á4ÉŰÉăî~ô˝'fÎ?-ĎŘםěvÖ&=Kŕčáâď- @8ĘŠ˛2yÍ€`I0µOń“L&y}Y–—[XŐ­ëhŮ䡛†'ŚäyíÜ™«ą \@Ä ˇ‘!ŤµEGŹť1¤Îv¬‹\ťÄÝɢ—Ú»ZXËjćăŘ\—’_H.+î›—|xŰ•ś*…B!šLĽ˝[Dż’¨kHĎĚ6‰$±wYtBŁ%Ťř„GËu9xř†úyI˘ ĎôKodŐŠ1±áj ĽAWzízÄĚŔű„Ç8ęjݱNv' +7?»ś´L„DĆ ‰ n¬-ţMFžÖ‰ĂŔŢeĹý=ˇa0őL@®Č; 8aŘЦʂŁ'Ωr»ď€á C+ň[żé˝pʇGFiÔ Q’JEQ^N‰†pőĄĹŮĹ7쀎>4<˘ĹB‚rśhh¸v-ĂÄ(Ďs„1±e)ŇŮŞw‘Ň2¦˘$I‘‡Ůu‡(I„R ĘńTb"“ IR‡ľ&Ź:‘q=Č˝Oßt#‹~&.ŔaŘޡ‰{âFŽöe(§źÇÇś–¸oČČźŁ5VmáĚĽÄŐ*ćëŁN<04aS„Ť oťřŰ0ßIň3}Ç™ŁN ±sШŁńC?`ăÁCŢ´ţö#őž iMéQoŔsMHâćó·śÂwuŕđCC·F٨Ű|Ѷđy´Ą;˘m€“U¬ĚóÁˇ _…iü5q[‡ŚŘ4€ö<Ć^4ÝÁ~´ë¨3 xŕşÄwř·áôw"F:ĺ ŁPŮôbňźjuüޡ!s»Ă“«UCÎ,]”4ojOŃň:߬”¸+rčőĺ‹/,ś{úîąů÷ßł:ÄwBđŔŚű–Č)ą÷Ýł>ÂĐ\şwé.”0:4âÚň{.,ś›˛lńăĂm”h‰eé`ďyn颮řŢŞČY–ÄŹNž;ŮÚ˘e·‡äN>#:îĚě‰ĘV™ýĎěźđv/z!Üż3Z(@8÷§^ţ «¬*?ĺx¤F @ĺ±ăTJEiAAqyÚ™=Qľ¶8Žŕ61·Ľzőśx@łîµŹĚąldW§š×>ŢY[SU\ZĽóă×mű4ůTŔć–Kľ}ĺČUĂĆ.Ě,¬(,ČËË/¬,Ěľgb€ůk6–TŐgĄgj몞X1 ¸{©Đü'Ŕ«˝_ý賌âúĚcß)€ŢçWć˘k?ËO?"GÎŢ=ďóő÷ 7É»Sű(Ţ:ü`RviQ~^Aaa~~n~IÎĺĂQvj€*Úż“éę0™%…Wxç^p[<ôUWe÷©ücSAň>›®ýŻţq,RÄ:49żú­ďdßî˝dB{kc§°ř±w*ëknädgfd6ëÖ-™5mń†š¦†îRë6>¶°·;Y6sÇ_ý¨®Q—WPTšq~0ńž˘Ş†99Őu żýôOo€Í?Zßőńv€5Żţ[ÎU’vf Ťť4ˇť,(Pľ§Ú»+Çěk$j\jYó…ís±ś_ŐđÄĽaTJ%€IË/ŻkĚËĘ,«¬ľxäçh?'śMX÷˛č„†µ-&]®+yçÇ<úNUC­Śs“NűŇĘépKL+®)ČË-*ŻľzěGkä××r®‹ż| lĘň‚V |ϋ렬kʶµyÇ˝•­ČżďĹÔý/Ż|hɡ%8˛ĘqÖň*{DĂ×=öSąŐ—¬®ilÎÍȨ®m8ňÝ»ž@ÚĐHúĺSă—;Üž= ¶k°÷.ÓÝś˘TNłćÓšĽV]‘0öěě ˛'î›ĺP6^¶ŃłOź˝°cËŽ‚¬äAvV&,_§×ŐŽó±âÝŞšŚĎŻž@ޏöĂ]ĄW;VQsÎś»(çh«>íťd¸/!dÖý/3&­ś>-ýď&Ů2†M˝Ő’o9JµĘÁÓďßť<őÓëžÎSč”’ÓßZ ~äôÄřHôŞŐ„PąŢ˙ŘúźzHT O<ůŢŢÔĂ_sŔÝ/â‘'Ö?óôÚY“ű˘ ňHé3}Ú¤řÉw=˙Ü33ÇÇ÷śQ^ŐÜ·ţË‚ĚßäUÍ‘Śü/ź^‰[[Ő´0Äű{řnüöxúo?ř{ú{ýYqŞd'×ăşę˝ŻBo™ú¨c˛ˇô?—]ţö#wŁOëŠţ$ž#Vżą9űÜ/f Ůł.w˛6±6 VŽÎáŃ1~ž®sZWW]6%ĚjۨŘŘîRj«Ë¦†»ˇeë­ŹuÉ6*pÔÂJmý3KDÇĹŰspđ0yÂpÁă1ŠâŠ1.‰‹ŤĚÔ‚<[66Đ>vVŤN+çŠ4ÄY­Bű~ŇYěz¬˝ËrĚůˇü×ĎÇ ÍÍ'w|Ş`›śž÷ЬÁîşçţ)ńS—?ZQ”1Řšw UŘ ˙÷úrćîdŃňÖ <%u}˘ö.­˛¨©`˙©Yą9óCmť=Ľ9‚r€ŁT+†zŁńáítµ+ą+¸D¶ć2âĘ Ađ»ăü…$K»XŐXE'ő ®{‹D(1lôËď¶M_ŰŘܸrj,µÜ®Sż|`Ŕ°1«–/´ř&=Ž-ë®Ř+™ů«ďNŕ¬lýý|˛bÝçĹ٧ť5“–=§36ŻY8ß—6Ůě{„Ěť;ŰâÎys#B|\}ýĽü‚Z-dpż‡Ö¬{úÉŐ‘îrnK«>kbżđŘéÓ'›S&  r 2Ź)ŃA¬˝C§ß9sŇ´ąĎ>˙ĚC"„EĆ?ú‡E…Eo­}pé˛{ř:[j•ü‡łDOrďË7]ˢśo–ĚÁ"aĹű<ŕĽÜĂĘ™ph'Vęi CĚ;a6ÎÄdhqÝmńTÂXm4Ö‹Ö^V6ľj±Ád(7Ęí ’Ç±Ż¨x0AÉŮŹwrŚłcZSs™€ddÄCă9ŶôŰR±SGĽ5Ŕw¶Łăh§ G˝9•EE"çm”jI›Ţ 'HůuIËRŠN4%iwiŤ´ć€cl[+¬ťj…v´qrâëŽŰÚiä§Ś<}łnůsěřú/žLT¶ŁćÜ5yÔ(C]EvAĚEw˛čy9ÎťÄZëŇŘÚPŔT_“Ů"‹Âä_ĺ5«­U°r\ó쫯Ż{ÄßŐVd°äPck­jóSŹžČŽŠŚ‰ě_UVXY]‹žŇ•ÜUy׏‘÷Ż,É«i2 ŕŕ°ř!-¶{ed±¸ˇ}AŁ'Oe’ íü‰˙lÚÖ”–ĺ6éLGĚYmn—ŤĆJ dś?ńźo·¶~ÓÇ~AŔ "2tôĚiÓ§Ţ1ŢÍZa41QÔW7>ü[ŤNŇ8ÚÉßuG˛‡čĐaS>ýč=ŕ?üüżs§ ®,,()Čmµ9ŮEĽwÔ–={_\łtŃĂĎďßł#.Ř ŔŔ1w´Ző7ßz@dâä­;÷oţôŤ»—®üű«ëÂ7ďŘůôŠ;ăÇÍÚąó—‘v®aĂŘşë“w^X°ěá­żl‰uTÄLxxńdÁd9wĹ“«ď ÷ł†Eě 9˘Wu~Z«ŰäŢ"Ż>}Ó•,~·EęOYěM鍺"“ÝT÷ŘOÂŐ6Thd*?Ç•±‰Eá1Ň „¶8Jf @ÓůúĆR)ěŁAłr]Íéf¨©Đ$y­ šďşňZéQR(9ŻĺŢAŹx*’¶ŔWČ}ž»˘NW˛·u˛˛ QVn/ľüŔµKŹfIr;©¨Úť§‚V4ë‡ÄPžt4¬¬í’‘©|[ZńA¸ÇuÓéšĆ )ěŁ¸Ź‚Xą®ćt“ŇŽTžŞsśä®ĐéęňŤśŠBbDĹwćąI7‡Xç\? ׸Sfb˘ľ?# h%ÂS•W7çťL ¤”’v˙jéSŤ"ö¶óĽwgçKO‰ČĚ)»˛  ˘¤Q—Ć%<áµj÷ž+´*Ž#śňžŘŘg‡DąrÂĄ†Fą´ąáaúÚo˛‹¨Ôś4{ŇŻŢ˝{ĎĽ˝Ç›%ŇAČ2G~Ą’°Ł˙GÝÖ(RĘyj:®]űNL¨”<%G»”#.4ýë×ĂG÷~f*J9°/ „JŔâU««®}ňÝ>’ŃĐšËL„TŐÖ?ľáeÝŐĂIŮňŮN?Q•|Ë‘»G)5OE­Ľę RµB9®%Fuç'¨r ĺ€Ůó–ß1ŘsógúńÇZ»Đő+K·Čdĺíżý‹W'Ź1aÚ’&ůąZw%·’Ád25T¬[0s`tŘÔ…ODŇ=?í|·hH·%÷1Ĺ/yęB8JäÓBnë•S ’ß ¦Ůß±@x¸néYÇd¸}UTŞŻ«ý3pbęëš9…Ú;€Cß쏥µ1ďQNdp żtbô¶MŰD@ˇě)eë¦m"Ŕq˝;˛o_ ’WHÜä(ß ­¸eĎÁĺ3bDMţă®ßżąľ$őě‘ě:uEM­ŚüŃKYV*µA/zµĺZ65 ŚqíÎÍşEݵ·–#÷ B©(HżÁŻ­_ôÂO\ĽˇłâL&€RŞmŇ?ř·7®šşděČŻĎäŞT*˘˛_ąîů×6®sWę.dĺËĺ÷, K~äS;˙!–uĐv8PýµKçRsË=Ľ÷Čž/+Ţ#ö]ÜšK~>îŽwüţͧKSĎî9[t|ŘÓIî„c§¶ćÚ{®×A7Ěy-ěĄDŰŘ;˝îRNˇ "ŕöö;očs~Űöë•ďŕŤkďn‡§ä‰ů›ćě#»¤čŐą!0JtňÜű_zé…—6<7,·±Qëä3pßľ)§¶Ô¤Ţľë©·÷oT055jE€6juŁ96±l!y wÍ^1ÄSś8hpÂDťkÔ}sf0Šb›U_ô“`46T¬[0k`dXâô‡§ĎľęŻ>oSMu‚®~ĂÓÇÍ©ŕÜÇOrŕÇ7FÎ~ÚŔÄżŻš<4qŇî3E,}ĹÉNŘ,%h–»$µĘ«/ßtEßqî;QbAýµ'Ň3ŢĚKZ‘!¨UÎĂ5L«Őgü5;ă…ěš<Ńq€PBxB8"š:Os·ó%iɸúTe—Z-%(ËťăřVyőö ×I7sߩŠ‚\˘ĆĘLˇŐ_0“ĽNPÇ8 ú*&â/^ňLWíaĹš •gµ çku ’­Ź `”cŮł+˸ŘW ‰Ś r4“ľÜ¤ôTp˝Ă]M EűŞaČ„Ü72NOIÎüşÚýo×x€v—; Śp*_kKî™Č$“ů‚Ô1Ž-€I/ Ĺ2‰1‘¨Ý­$ťˇň¬¶á\ť®žŮú¨ŔÉtíÁ”Ľ5ś5' Ś1AjăŮ˝‹„Đ›E p6{8NęÄDŔÚK QĐŐu=ß•Q3J’ĐŇMüÜ|6ĎšůÁđhůn®Äj^or^Ţ #„𤶔\@`°!âË'OěmTlŻ I22čó›šÝ5Óř‘Ć/sňež Śoś>2äű­Ď]Éź3ĂË]˝đ«ih4÷´së75˙˙Épkě(3ćčo5˘IŚ&ŁÜC}#ŚÚĽm‡Îś?ükzŤ1>4ŔśĹŹ’ĽmŢ -űLr.Ů?SI}ął˝ă¦·źÝÍ80Č«Ľˇ -ËŕßMň-»Ň[.ůö—#˙ŚzŁÎl.g]qtźś&RŽW(•¦ď?qńëwžV <€€Ö ú†&cĂŤĹSĆ OL?vôúw~’dWgMF‰0 `%¤nĂšŮ~>~Ź˝ąeŃ“ĎÍź 8±ű’‰L’É<=“o˘tĎ €I¬UđV*H’Ч€Ŕ„iűşŻ«‹”®¶’ĺ˙‰˘ŘââVľ}ó‡PĎR–EänŮ â‚Hü}ÔľęÄTfÝ0ďhÜN_2J„JÄşŚ*€ľ/Ce»a’$˘ ‚:-]t×Ůí[˛ęDÂń¦ŢR@ąľ{ٶ´QŐ͵‚hŽĽmj1bLµÚŞC;·”QaţĹU…Žűoß~v˙5clw•¶¤ĚP'‰Ě"W'ś»—EµË¨Zú/1€Ö×ć]Ë®zđ‘‡EŘňöófL"L´ćŚëWÝ»˝Đnç7ď)˝ ‚Ń(@ĺŤÂ2w_ůÝnxda$ŁÉdEu57ÚŐ5s2D€ş,[|×Ůí[˛jEPNvŠećX”8+u]ÍŤkY-ąçΨ¦`‚¨m¨:´ską t´u!wŽW0AF~ką—sBŰ7Db–vC 1bĂÖ­ZÖĽ,‘žžő3<űîG÷Śwś0aʹ륭©ĘmECá4ćD řë»-ç8a”¤Ě2Jiߎj%QE‘0ÉGY„ÓVĺżý–<0îţÇNímOMb’RieojžŔd2ßÓ‘-¤Ľ›$RĆÎĹŽÄ!ĐËE ˘¬Đ­V˝%ňd‹ť§Ç”QĎ~´ORh$Á*‰˘¬ě„P•RicgŤnnôu”ŕ€pĂŰŮyľ§oÖ+`¶#­˛¸śo†<–$|¸Ü'nŰŕŃjÜ)śl†6äÍŕçBĆ_Ś÷mŔvĽűÄܱ‰_…Ękrű‰c/Ťz1 dCčřË ľă5€zäÉďQVÔ]3:ydäăÎwzŚż:bŕ[ˇ?Ź—”ĽŘŕoń¨+Â\ăŕM1÷¸os8ÎŃ—G§1Pţ5ŕŁŘ¸ ľćß< \żeĐŘÓńCżđ™ęŘŽCy?ŃC3lźą. s¨˛î>îJ Ďăýfy&ěęw‡ě˘€Äl±ĚĹa‚ŰřÔÄ6ž—ý¸,đŽ˙Ô\˛ÇŇ€aßEö1YźŰĺÚ?PăIÁYŹ<Ń?dŔgmđ,®CZĺÖŹypŢÜÜKöΚ˛2Ř@LŔ€ęÇ˝0kŚf«YąęŢyž Ú1…ďtéŢĄ+|íU6.i+ř(>|bXD骛'Ťý~ĆŚ˘•ËžŹň;,zhZ|‹iś|vŢ5s]ô€çGŹË^:w”ťŞ3›˛Q|sÚ¬mŁăüÎűUż—8BL‰90cě˙¦·Ůj˝5cöOŁý^ (¬^z”¬FmÝĹSGîżcTܤű›űń?ďżýéwŚÖÎ8śJŻüęŐĺhń[b™ëü‰ĂŹÎŤ˙ĺâš“?nÚ´íl]ńµX_ nŮÇ|S‚zE%çWŢJÉ·łłŁ)?íŘź‘WV–—qŕ—-Sc|»Ď¦Ô•foX˙ôáłIOŻž`čśŐڱ'ľSŕ”ÍZ%§ČNO|"'g–7źűyÓăŹ>yâBňšűgȵLYúLCcíŢ]Ű]8R6îä…¤—ž|čźýÔP–5y€€!w=Ü]É*€pJs©Şř˛ ŻčđMG~(ŤZPˇ“¶}őÁ»ź˙ 6WÝ3)ŔĐŮ=ŐŐEJ;D~ëţ·MIĺÉ?s]s›Hžp 껦I2÷‰é×Î €ëżČî¨Ďş*ĎXđÉľKG>Ű(ó}A2Ż…?=’zđłűV{GksţŘÁ5‹gz÷:ĆLKÇP*Ď{އ®÷—Äťę:qř‘Ůc`™RŇtö§/Ţţ÷·Ś5ÜUoÔŠşô&]©"3ë«O×5ćčA€FcmRK+>Č/?Ód*Ň5Ü0ŘĹÚšy>X/ býĺc˝É 6]oÔ•šŚUFcŤ`,Ó—n.-Ú] JŔ`iç¬(ÝUĂ&Bé}isô śó L„‡Ř/6q®ˇ÷:弖«« éňnńµ·«ÖÖ*,­4s««3u’$•këŹ_«ŐʶČS­Î­.˙ůF©ü´…uJĐlŇ_­Ş.Ň6ś­j°ćQYťĄm¬ÔĘ~JKý$łhZÔ°Őšż;Uj2 S$+µ*ÁËÓť ď^<°˛Qľ3ŘŢFŮSCbR rňšM”ô~—şżÂŔ‚\ݬľĎĘ—ţÇV5” ŕĽ1ÖďŐ“gršMÜĐĐŞÜ”_÷.­­Íż~őČŃ=É™EńĂGzŮro˝úÜż~<<ç±wţ2ĎőŠ•Ĺ Ě%Â"WY]mNĘŐëY׏OŠ›8É™–®{řŃÓé%„Rv«›.ŚR"5T=ž7iň-”|űĘ‘c·Y9:řy8$ź:|ňbJ]muň•ä’ŞŞý{öTN“§Ś*˝zöŰ~.«®M†˛˛˛ýűö\JÍ‘/Ž‚ˇĽĽ|˙ľ=É©9 ¤ˇ2çŔ‘‹>C‡Ž“žtl˶_+Ş(AvęůĚ‚*…RUžťr19ÓÚÝ{ô¸ńž*ÝË{b׹‚I/—ÓąäK©9“SaNćů‹WZ.µű¦#?×rHmÁős)YCÇxjÄ×6<óĂž 0{Ş«›”®Ŕ“!ýňą W3@¨R©´tęĚó<ßŢËóďJáyž“Źż»•1c(J»śž7lÄ(/[žůô§Čm‰wŮW]%ĎtŁÖ´áĄg’woĘ)×rýzsŁ˘”“‹µôźkç<łúáŚň®ŹAĂ-íF}]öĄ‹×óJĽ]ܲŻűnë3ż˘đvqËązĽ‡”>qŮŢF妤\ϸräxrĚÄIŽü«Ď­űńĐE&š¬íí˘ 2V\Ţđô;NĺŔPsä·‹2ňkzôtV%šJŽKŠž49Č‘ßřÜşovťí|¨ť,~Ý”SŢH9Î?$¤»Ú7>·î›ť§;ő"Gd€É¤ĎĽzńü•t€5k+’’’ňs3N\ľaŁ`©’Óň *Ş«J ˛ľüđ폿Ý-H,jô’^dŃů¬KIiů%ňą® W2ŕăâ–-ă Ŕ(0× ¸¸ŠKG×®ůËůĚB©(m^NonÖŮ8Ř·`řäöăčJ¨ŁÜ“.gdŰşąDhÎu"ĽŇ?8¸ŤĂä¤ëůŢ`,/+“íˇlIš+“»GăŁovŠ=€&cP*­ňrR.Ąd6hëJJŠ’/%”T1‰1“IźuőÂůËéJĄU~nJrűoú›‹57Vž9}Ş´ş€$1ÁdĚIżtúü‰!+ëJmcsnĆůĘF‰tsO€PĘä1`đť‰17nTs4ç§GxAříС ˘ŃvůÜ…«„meÖ‘s©#ĆNđŃĎ­{dŰÉT‚©ťUgíě<Ł”4TXŚ)ÉǶlýµ˘V[WšwúÜ…&Ihj¬:uüXQY­d¬;s&UäRĹĄ\ľP\Ąm»^0†ććfkűVąKŸ“śš#őöÍĄë9Ji}Ł“,ú!š…Ä»Iëđ”ű¦Şëň{0ŘŹqµ±1–ě­Úţ­zŻuQâ6ŐĄ9˝^›kěK7ËvwĹp„‰Ěe±Ođ6Vdt­b7‹Ň­ & ŤÔŘzóën±Ĺ¤ÍaE»!Ő2ěnkJA Hđ UčľË)aň‚ô¦´•Đ™ÁUeWëš(Ám{¬Ë*2iZÔŔgť'í“$PŽI˘&ręá­;ŃfłđŰĆĚOůń;S# +©ćˇąă¤ë8JÄ>GUďÇQ¶OÔwŁĘů‹޸ü[ŇőÂľ†ÂĽuî(e’>tř@§¶ěą• Ŕ­ĐĘ‘öá)lj˛ź@ů ˇéÚĆďAµ‹®"{´pŹAzËŐĆqʞčµö8€..ýÚµůŕ(ź,ˇ”RI¤J»|ą$‹ŠB)e˘(un!­—ÖnM4˝}Ţ żŰŞ"»N‚$Š }Ž*Űmi„R ĆZ_˝Jĺ­¤^K•CÜĆÍyěđ'OŤ »Ň$ĘĚK$Öj-;”Ö·Ć÷Ů”Ýd§îKíĆ4ŠňÁc¬·Ž€ÉŰ^„ůoÂ&µ÷šC HË—2QBZM“ĺ2€´ék/ł€öËĘ&0Ć@x‰1©‡hÇŹl¸ä·ű}—)!Ś&1‡ÉnŢ#¬®ż’ĎĚÇĎíłŕ­ Ťüôs˙ Ö“Z%%‚$IO)cLdŚÂ"˙ÝrO꼰ˇ„PѢŁQŮ}ëȲěAbL>µ{ÚY!쏛ŁuOr3G….÷¶ěÄEᏝ+ŢÝ:cĎÓ–ĺ«$ ˘D”JŢd42€ă8ŮĘw™K~ú &ĆĐćÎh˙ăÖ[H(%’9¦2eŇďnţärĺ(“Ú);!”ă9HRËc’.R(ĺ(IÇ3&ZNűxžgL%ĆS*J˘<ŘÉÁ§űR2ˇ”ŁÔruÔ—\č4ńęS]]•ÓĽ`Ś×řĚ™5A͵­;:ĎzO ˇÓýu{aťđżµQŃ „>ęŐţOĺôfÖ{„*Ľ`2IŚń®¶˛ňV)ňęşśhPŞs´S€“Đś^×$1PĘĹ{zhtޤL/1BU ĂRJňR <Ą˘$ńJuťFA`ŚŮőZŁÄäŕçc ÄÇÉz]‘Î@ ˇ-~Řş\ŐČ}†×8m›2ę«s'v×r„·kb> quădőuFŢź9kčŠäEWbPř+Ń>÷í>Txk›ĄťƬÜ<ü\ěrŇ2žŁ‚HÂFň¸vîěŐÜů0BýŁŁQU’_ZĄR»Ť›4Ú–čŽ<\ˇ7öK ŐȱlyĂ‘#żéŤżS·«0…Ę12*„‡ ň˛SŻŐM`‹1,ş$óüÉ "ëB„PŽŁ„@™(‰śÂmŐc÷»X“Ükçřĺ°,yyÜŔy˘Đ§†P*ß꤄ôšQîŽnŢţîš«)h@TŚTSPPZŰç„eg­E `§05W§\ĎésSú“Ě-u÷ ö÷f˘ G*ËË-¬ęŹ–ö‘‡>čŞ<‡Vř<Ľóż˙^·tóákGű.ý[!y;yÜÝŹľ÷ÄĚ9ă§ĺűĽ·MS»yř»Řć¤e Ta˘VDaÔ•^»^Ŕhx¨ÁA©çĎ^É)pôđ ńó’“ĽŞč«,:×E 1ądĎkçÎ\Í-ĺ" Ť i¬-:zě¬Á$ĘüX)oĐ•^K+$(U®ă'ŹéÁ˛u'‹^jďja-îăŘ\—’_H.+î›—|xŰ•ś*…B!šLĽ˝[Dż’¨kHĎĚ6‰$±wYtB„DĆ ‰ n¬-ţíŘI’!m˙MëX`NiŐL gří·cÍŃŃĂ'Äß[äž"•ĺÝčB:k'kAµéř#ŁeÉGŹvmW ď˛âţžĐ0zŰ4éBM,ú{{SŞÝĆMeGuÇéă&o1„ĹŤU’qáäEłť÷‹v%uW2ŠŘÍźp”0đˇá-”ăDCõk&Fyž#ډ-K‘ÎV˝‹”–1E%yĄ*ň9›(I„R ĘńTb"“ űbî$ ×ŇwÎL]ß[ę—1÷–‰Ş`»„Ă ńďp[č?ędü]qĂ4öt|ŕ"gČ›ííł=7`ôńˇ# ‰ý›?ŔĘ&ńč0ßITA8ÎôubŘťFŤúŮľµ…żýĂC˝'hş*ąłŢ€çšÄÍćo9…ďęŔᇆ&nŤ˛QwŠĐB 뮉G‡ŤŘ7úݸ7TśůµÜҡoůsJŐŕ_†˙fě'xŚ˝:"hşýh×Qg:đ Ŕu‰ďđoĂ©™‹rŢ $çi=čűAŁö ůŰĐA/úwĂ2×»€Ú&ń·[FLţS­Žß;4d®cwxň˙°÷ßáUUŮ˙8ţÚűś[rsÓ{%˝’@„^ĄR)‚ ÔÔ±Ť:cÇ6Žú¶ŚčXA@z‘"=ˇ„RIďőŢ”[Î9ű÷ÇąInz€ó}~źý<3ćnÎŢkíµÖ~íľ–ÍšˇI§–-N™?-ڧh}ťßúŻ€ŤŁĎŻ÷,N˝gţ…e÷™5NCđĎŚž”uďâ‹KďŢ3&ţÔśIJY$·‘zźIŽĺAxű÷,~>Â-TŽâńÄKďe—W¤‹˛µxOô•ë›ó ‹K3N ±Uŕ(Ŕ9lR^eíłâaë·ëôŐʲÂÂ’ŠŚS»Łýě0/ľeK´}őŁuµŐ%e%;>zÍNÎ˙_­G~¨6nQVQeQa~~AQUQÎŇI‘¬ą´ş!űZ–ľľú±•ÓŃźXőVž­äרĽÚç•?É,iČ:úMëŁÓŰa›–˘O~Rpí°šPí»š˙éĆűPţćcYĄ‰8’SV\_XTTPWPš{ńP´˝ ŠŽďd$ő.09Ģ‡ßŞj¨˝ž›“•™ŐŘX˙ň#‹ĐúÔřwNýµUŮ}ęýß\ş×¶{˙«ż‹Ń„¦ÔĽůŔěţI¦3¶Dkmh#¦_)¨*ĚĎ+®¨ą|ě{oÖżú/m*˛Ďw=řFŤ®îFtŃ7Ž•ež &--®Ö]ĎÍ­©×Ýňž  źž^ŘĘĎń-6őŚřů·+˝#[]P |żP´S=_#ŃăÓË[Îmű&ˇ Z÷ŘüáTJ%€ÉË­¨oĚĎÎ*ŻŞ9ř§gśmXĎşč,Ť;QwÜ[XeiűŻ?ĽëÍÔăO}ż•gŽ÷´j…]Űf™»?}Ŕ‡߮nčE;]tˇUPyGn˙-­UŞ{bümőß˙˝ł'›o}‹—_Ő«4üqăN7ý˝I÷ŇÚ9P{í<“qC#šüŻsţkÎ?~ß JŰŔ}—ŠĘŇ °án”˝ňÂH[Ąumýí˙żëZJ6BµŰL˛éʰěĎ bęňvűůţo®ŁÂlĚ$C {;zü™¤„w‚(@ěxŰŤÚMá¶Ŕ{ĚŻC]€•ă>yÍě>.5É+Ví:Çoňµ‘^É6€zřŽź±¶\g¸;G«śgyŽŢg§&ĘAvÉż%…­p@•€˙¦QßG*d·î^6>KĽ­ôvM˛ëŽ?€B5lç°»-ɸ硛ccß OŢ1XkÓeU#;áđ¶qNÔ°Iô.ÉgŚ­Ě˝ĄĄopjuüÖa~Kp¬öŰ1%kdŕ GljîcĆwâ€Ëźa…Ň.únŹ{ĆŹ·ő 6Q.cO'ůŚ×vX×Rď*MâöLČçńĐ_‡w5 ŮÖ´Nţ»îşóËISÍŠěaUŁvô;±hŢL{­ÚĆK­äš˙Ŕň{Üí¦†ÇŐŻ[yŹŻ`wřî÷ú»:ŢM;1,"}ÉÜXž¸:x¤®\ţÚŕ´.xÖ%Oş4Ş w­ĂĘG‡DOőqďNÉňeHížE‹^ Ŕ-{ÓşŃ$O|¦DĆî™1öÓšĽV]™4îôś‰˛űš[çP†3»9'OźŰľe{avę;5€ŔŃ‹Şô ›–$Žę˘Vŕ9ŕÉ÷wV\9¬F-ß`j®ďkĂ{$U7™ž]7 ý™µ÷šdô ›vłdĽ/)dÖŞ—“VĎCë4â¶ĄZĺčĺ˙÷oNüöĂk^.sčÔRťů?o®8jFrbú´jyŰĹ5pŐ#_xö©ˇŃ<đř;{Ň}É ˙ȇ۸é©'gMN–KöˇhB Šť1}râ”»ž}fÓť{/(ŻjîŰřyaÖŻňŞćpfÁçO­Ć­­jZâ}=}_ţúص_żäĺěý‡Ĺ©" rtމő÷r›·vC]Mů´wÜďŹBý´1YP:“SńÖCwă6­¸Ú“Üý×˝ń]Ιź-Ů»-wÁ–ÁZ5ŰŮ×r˛ç'‡Úąxúş¸4Şmbâ‡9+ś˝SäŤč˘?8źčŔÁŃ/|ĘÄ‚'üüűĹEĹo>ůŔ˛ĺKĂý\¬­JţĂePd›ýL¦gvĐ×@Ťą·ž(áŔ\ř;ű’˛˝:^C0˝ĐśŰl¨2Űw6fÔÖ^7Ł-ä%,~ëBí„B}U9Ľćş-’Ö_0pŚ’çý˙ âÁ%ç0ÁŮ)ŢžéÍ-ĺ&’‰O­×T»˛ŻËĚ Ä^ůf¸ß'§1ÎAűpŞvłłV9h«TKúk:9C*¨OYžV|\G”¤ĂĄ5«ÇćŇ–ş Ű Ť6Be®1™őfnwű9ű’˛}:Ζް&ÁPfv›ăíÄ5晨’0‘1íÄł\łe•B cp]ŕg‘-ŔŰ) 9úć ©%»©E'Ú ˛‘żlżâg‘łĄgŰëBĽe‰1Fč3ôś«ŤVCĐqJ.ź$¶4”Üůó®ĎŠĘ”ëđ(ÖJŘŚăU«ă⟍Ťđ·áŚtqŻo¨ÚÓHî jÄh'@˘”DáÍă’<1Š8ER€˙XOw“ŃßŘŔ$JśÖmi°Ç÷ié5 śĘîťIÖ†űŤń Ř4,FËuV˛üCăčâÎK—«+€ľŁt"ň˙č dŕ“|4­ŞBŁqTP ÄŞ†I"€–¬˝É#ß˙ĺ¨BĄDFŚŐ—f©"gü´uK¤­1 ĺ‘ńţ#X4ćó·ß6úšĘň}H`xDHXS]EII)nůi %€1“ë˛Oţ\Ä­Z˝D’ CÂâHäFV#·˝Éd¬/+¬kLÍőůe5"Ă„;ĆŮ4_˙ŕo˙rrĎÉsWČ/ˇ;)NÎ!„‚1Ţ+jËÎ]WÎN?kűöź&dN­âä%‰}ŕç[¶>±jö¤™ _yy“˝ X·‘YÚr(GÄŽžşeçţď>~ýî%«^{ă9;,»)Őšgőđ@’¬U÷H«—śöÄ„ňüÜňâëuő&&ŠËŠórËo›k.Ě€±ľ.;=­°¬jř¤ůE)Ű\«~‡@ ]RmLŢŤę©1Ą\8Ť»?IŕbĘ [ďÁZŔHŻÔ[äB§q^˙ô+Żmx(ŔS `br;ÚD¸“Z“YÔ7dÝ.úcîhQ_”yäxvtTl| ęň˘Şš*đ”jśd~ü]µŞ+**{G¶.şŕ(iîŠĘ“§¶~ÁńDb˝öĺ)AŠ/ż=fgŻ•.ň -Í+žyoű—ďyóÔh ˛=÷®)ŁGë+s e.zŇEWiH’ :˙ęáÖ¶W•ć×6Qx`xâPË7˘@KÖžN­;ĘĘ2[â# µd^˝Ň“vşˇ.ŠŞ*Ęۤj¨/ĎÉ«ś0®6·›ď€„öG76îtéď…)ŰN”‹- ő74˘Éľ¬ÇMkӜ߆ó§ĎĄ6q¦.óĐĎ—jďš< €€ŢJ9ˇĂ§~üá;x€~˙Ó˙Ě›šPUTXZ׆ą9ĹĽOô–Ý{^XżlńĎîŰ˝=>ŘŔ±w´ˇúoľ *yĘŹ;ö}÷ńëw/[ý·W68D|·}ÇS+g'ŽźµcÇĎŁíÝÂF|űăÎżýüÂĺţřó–ÁNŞ€Ř‰Ţ3E0›FÍ[ůřşű"ü5°Š˝!{A­)Č8ܡďTŁÝą¨1÷Öe"8OŰ •.yoäë‹%Ę1ËÄť÷µ÷HŇTţRË`yőŢÉ1šY/ú­ńSšë®äó±…y­ ZŕľúJŮ%#QR(9ď>Ay)’ľĐWČcľ‡˘ľątO=ęlc˘¬ÚVrńţ+ΖĚífgťÔ<Ąô˘LZöŤEyŇX­Ţ­Đ uŚ~72ćIoÓőĆŞ‹&ÎÓ6pykK)“ĚŚăĄĘS N“=ÍÍő&NE!1˘â»ňÜž$ĆyŮ­pm•«Zlbś\âl]¦ąÚ¸ň’úťµżď·Čą˝“B!´HŢ·.1‚^"tăę…–Ú­ ŮxĹ$Ć ÚöŮ+SĆŤś8}I“Ńň\­§šŰ’Ńl6ë*7,ĽsHLŘ´EŹEŇ3?|ČvŃiݤwZÝćXËKžşŽůĘ,!·őĘiW†(ś‚&,›óăć­"` ˙~»č÷nc˛¸}ťTTj¨Żű#$Ä4Ô·p µO‡ţáOWlˇ’!íâŮôĽŠ¤Ĺî9°+Ŕ^ÓĐ`ô K°FŮńÓŤę˘O[1s0€č‰3ľßyŕżo; ĚMŤz‘ ŤúfŁÉ›XFHžŔ]sVő¦$Ç%$Mjv‹ľoîL&QlGőĹŹ0 &“®rĂÂYC˘Â’g<8cÎŞiC˝żý´}LQ4Ő Í ĎÝ?=!~n%ç1aÚĐýßż>jÎSF&ţmÍ”aÉ“wť*`í1RvÂfm?»OD~c5^ Ô{ó‰đ\쫱“¨§ŤS”Š:«=ĆÚÉÚr™ę ÇtÝZž´żŮ0KĘ{źd›Ľ÷ŠP‹;žjĂíQh®3C^ 5š2îO;5őRŐu|Ż8jüf;W~_n@8ůúŇm ^kCGHąĎö°ý$4I’IJC¦ĺřHžOČ>ÇÚ8”€ćÔš”Ĺ—.ţ)_aď–¤u›ícëȨ§ŤS¤Š:Ű¸Ź·ă4ÄpQŻĎl¬9Ş Ś€RfÍór·V n˙Ż{ü4ö‰Gµçhmőîň’cÍ‘ďFßă(Ô ŕĺ—X~döZŇ^Š:Ůxڶ%’ž‰YfT`"u˛Óň®]Đ2é”ÍSʵľókj(_˝˙đźOťąsçá^;Á×˝Ńhôrô\éëđ÷s© ŕT˛oŽŹrqăDSގE6wÉÔ¸~ç®Ä˙n˙Ąž>78ÔŽkÂüw¦_-–ŔQ47T|•Yş(aěĺĄó_©’Ý_X«PoĆŘM”˙$ŽB”¤&yŮŃ:×Ü [Č”¶}JCĽń->ëţţýî#f,Zs#š™Yá<xĺá9yĘä) Înˇă‡ű }žWČ34PŇ•˛uŰY{µKuivA Đpý\Fˇ«ĘŻ´iGőĽ|j•ś“M éëkť}BkŞG-jSJ4ö}MŮĺÔ2› fQeç €ń”FZouâN’Dt´źűćŹÇńÖăĹ@Ťą·’(Agl*ĽďöÔrÄNí6Ö@xĎiÎş#µ-:ž€Aë÷EläźäłÍ gEůW…µ9Lë«05™ÂsRÎË9UĺÜŕW ‰Ś r4łˇÂ¬ôRp»ĂCMŚĹ{k`Č„Ľ×3ONMÍú˛Ćc©Ź[˘ ť^¤Č×É ŤŚp*?Ť5÷Ld’ŮrA‚ˇ3‡„#™Ř$Ô«1‰mF¨ii,¶´”Ú©ÜĆjĺ LWHËß^Ëi8I``Ś R;ĎVHk%Agl,l•Vĺ6Ţ‘4š˛7^ýmZęĺ— $ŔPo Žq´đĂş”˛SąŤqct@$&2o5Dˇąľűů®,%1“$ ­ÝÄßÝ÷»Ywľ7"Fž9q­®›ĺ['+L-Î66?^NůµNж×T™Ś±%âK'ŽďiTlź¨ŚI21 šZ<´Z3‚ÂýIăçą2Ϧ×OúßźąT0'6v¦·-ş{BW«k4÷˛w´ű˙%«$‹#XkO™)× ˇß±éú™Ě‚d2›dź%5-u‚()3‹’|Ą~ţâ‡iţßý(óäaŇço=xęěˇ_®ŐšâH·v~$߲+k¨pqpÚüÖÓű®†yWčŞŃŢůţGë‘H&©Ů2[ż}ÉÉ#bJRH9^ˇTLš±ďřů/ß~J€ňÚr(tM&Ýő{¦Ž‘śłÚËD«_IĆłeům oÂ̢$٦šć:Ń mZ×î7§‹^pĚ"pQÔë«îř±Âȇ‡„¶ŠĚ,’Yä‚|#Mú‚Ţ­g]ôB]~xĐÚwx‰´ˇ.˙JNő=iÇ;Îź9™0QĂ™6®ąw[‘ýŽŻŢQA“IŞëEĺ~T"úˇ kÉsĽ‚ ímŹ •ż¬ľ‘ĹlÝŠŇN–©ŻĄ[´#×#@čŮ6^dÔšC}jk‹ťlíżn­ą˛©@P;¶H$Fl9ă†5Ë{‘/kä†zCÇţ~­Ú Ŕ÷ĆF4‰Q—˛ÚqžWđĆŹIŤęě®K›‘4j"Ö×ęHb’Riă` jžŔl¶ÜÓ‘RŢM©QkďjOâčí*PQ6č6T—×$í8O):Ź)ŁźţpݤĐJ‚  TE‘YöĹTJĄ­˝=Ü»ël?áFt/n}Ě D‰ě€+ŕ•ŘŃ߆Ę:´›ě51m„çP˘ ´c=&ĺŤKţ"T>š$nšÄ=Ă^‰z;fě!¶®Żu<Ég´ őĐŽIő¨§ËlĎ —Gy3tȧ1ăS’‚ďq¸„mĂ"v@x€ qJظÔ#řą°±‡âťüxtĺ_áŽÎĎň›§·ÄŤ;™8ěłHßiNě&X8”ׇ^ËGlŤ Zë;řó!N&¸ VŕT@Ŕ«G}Lꑿ&ůMŃĘDb· Ť\îę8Ń}Bzr;ĎK,ţ \ú$~J»‘Xbk;ř_QÁk˝~šü}¤Ť`7®Ub=”‚ÚvÔ±‘@Â?÷śŻL¨;-sGŽ:0^ŢĘ%{fM]ě 6 Ľć‘‡ĎÍ«!°(6éčśiٶ`Ać’9Q¶<Ô‡–,Ţ1iĚSff/ťˇ! N—î]¶ŇĎAeëš±úţ#&…E–­YůÝäq˙ť9łxőňgŁýĹŽ…‹ß‚VßbZgßwÝą!&üŮ1ăs–ÍmŻęʦ,Ą7¦ĎÚ:&Ŕ€ÝŻę_â052v˙Ěq˙›ŢdÔzsćśĆÄ ˛c ›ßů,%-»Q_öřˇ‡ćŚ…CTZiÓé>{ë_›™Đ°bjŕň[Fĺݬ OĐGĚ[ß$±ď˙ďÝ·>ţFb†'ŚŔÝÚŢŚüB”zG§Tťř~óć­§ëK® öÓâ˝GÜÎz,^Śb§ţ°}_f~yy~ćţź·L‹őě?9V_–óÜƧťNyjÝ|Ăć®cŚ]?ţŤ §7kŤś#;=ńŤš’UŃrć§ÍŹ>üřńs©ëWÍ”©L]¶I×X·gçÖ‡ŤŇ†Ť?q.ĺĹÇ×ţý“tĺŮSÂÝ ˝ëÁžjV„Sűč‹Ő%myŕxE§o:óC hôÂĘfiëďýăÓoŖ꥓# ›Ó­nrşŮÁ ň+ÜżlN©Hý‰ëţ›Ű—ägÂü'3/´í•ţî©ß¶*ĎXđď˝ň˛Ěôm”e-üńáôźĽĐ?ęť±%ĺ·CË&Ť]´ęŐ«©Ç^ŘřÂŃ+…µĹé}mŕžVŇhAsý}3ă żűÉŃEź8ö5cş»ç®~+çZĘËĎż¸ëŘUÖR2!Ěcîš73RŹĘüÔ—¦‡ą*ăf>ÔÂzG¶.ş ¤mmW‡ľB9N­ä<÷ŐůĘÔ- šˇ%u-ë¦G¨ý«Íěëż=ŔŔ°ęY;ť©ź˙íđŞ;FÇO^Ő&UĆLş+ ¶]kN˝V–ŹŔŰĆ×6÷&ŤÇ˘Őe˙S×ţŽ›Ń,kY׏ö]®/Ëyţ©?9}öˇŐ‹˙s,˙ňŽ·ĺ®űŇçgőą‡l­ľî‰™č)÷V7›~÷٧_îcŚţţCOĎ}ťR‘ú“<ÉóŽš^XgŢńŐ˙üâ'AW:?!Ŕ‚Ç^jCu^ˇ@ś§|¬Ć”ßR/?°`„˙ř•]ů5 )o_xt.…6đ䵊ô3˙ůň ÉQ>°Â‹?ĚÇ?ČL?'ŰŹ¨/š?:VăŽěŠóŽąĹ1÷Ö´źq15g7éłZ¨-™şŠ:Éę_MĄ†š“őŤą‚&s}j“]’‚šs^˝®+±Ń¬żÚh(1Ôg8šŇ›šKL¦ÁTf(ßRVôs˝Űâ˙ÉŞĚçóŚM°<­02ΑwLtP)ĄüňkŇŤÝÄ˙Ą = xĐGĽŞĄF%6ţjSIsÍo &˝Ř|­©ąĚ‘™J 5'ëőą’QâěxŰ0[©®ůú;ůU©ůý 5g5éł b‹ »¤75H‘ŚbÓŐĆć2ł©ÚdŞL冲ďĘŠwŐ0ŘDŮ;+ĘvŐ˛n%&0…ŻÚińJ]ćk͵Rg‰u,ՒݤËj 5 „ÄŰx·Đ{ťs_Ík® éî°ÄĎÁľF_w°¨¬ĘdĘ«©Éjl–$©Bßp¤¸$­^/&g{űX‡ŞšňOž» 3@0ž(«Žőńv"-/ť8sľÁ@)i6.W×ëu§«u™U5ŮúĆ*±˘Q÷CFúżłŠ§G_¨ýóŃßĘĚeŠ‚dŁV%y{yPáçϨj”ď v‚™ŘCcÓ só[̲ĎőŰ“(! ,ČÍ#ŮŃćżŮŇ˙ŘŞ†"ážÁ/öĺÄ©Ü37€Â!$ 4´:/í—˝‡ĘëëňŇŇ®f^:|,5vŇä@'Ĺßžßř厳óyëO Ö­\]˘c`Š3Χ^Ë>r´·÷ú‹›>ţင[<Ď(%’®ňȱ”řÉS\hن>y­”PĘněĹÎí«GŽNkăäčďéúۡçÓęëjR/Ą–VWď۽ۨrž2utŮĺÓ_űSyMh6–——ďŰ»űBz® ˘`¬¨¨Ř·wwjz.ŃUĺî?|ŢwذŃĂbŻĄݲő—Ę%ČI?›UX­PŞ*rŇΧfi<|ĆŚźŕĄj~é/Źí<“ @0äzşÖ|!=W"c’`.ĘÍ:{ţRëĺŻßtćçJ.©+Ľz&-{XňX/­řęs›ľÝ}ŽfSo´zČéN¸€`6^»xćÜĺLŞT*­ť:ó<Ďwôň|S9<ĎsňńwŻ=€1W÷ÜËÇľůń°‘ ôIč@Ř*áxƤëućç^Ü”şksn…žŁ·É© ĄśÄXôče˙|rî¦ufVč¸~†4µĆ–†úśÔ”Ë׋=Â#â"*.yrýçr«ĐTvřčůÉS‚śř—źŮ°yÇIx;»ĺܨ.zĂ1ţ•g6|đ<Íű¸8SĺĹçžz|÷ą“>rÜD_­đ̆‡¶žH ; :ë€óŚR˘«´SRŹnůń—Ę:}}YţÉ3çš$ˇ©±ú·cG‹Ëë$Sý©Sé"G”*.íâą’j=!íđÂZZZ4ޱ­}gŰŃ XŤ;©éą TŇUÜň{«‰´˙·[ş” '†:±ţŮmm`pëfkk*ÝÓ´ă[őŢ9@‰ű4×–k ú<ÓÍÄ ”‹JŘŤD1–#~şŢă|‡íą•™R«Ř!¸ˇ; IJZ·čx $& ŤŇÚůđ•ęĐ|Ŕ:† ëŔrw1Ń( Iţ!ˇŠćorK™Ľ ˝!Č!ôÎŕŔÂęňËőM˝X߀'ŽP‘IÓŁ‡<ě2yÇaS·ńŇţ¸d ęę6Ä^µ-ŻřwżďoŐzY Łď\i_ň˙=Ŕ@)m˝©híLoăv”ýÍkâ­‡Čwmnbwb$˛wQëXŃť(ý~'üüBn}áÚZłET’@9&‰Ú¨i‡~üČ™¶™•ß6©C@ôÍaŚ(l¤Úµó&ěżÖĚQ"öňö·5 <ˇňUÜĂ­[Mý·1Ş\°xŃő‹ż¦\-ęo(Ě[çŽR&IĂF tţvËî L:µ¶˝rB9¤›o#é±ć>°lRôZĆ*ő®‹>©·QD7—~äđŽ]żç(Dý2"Ă˝%<ˇ¤í9ˇIě§vHëĂŞvYt±yyj%Ó˘”Z÷Áž¤qC¨.ßG•:Ľ’[!H¬3öösD#„RÂ:‹Ś1©ĎarŰřąŹůäOăB.6‰]mÉ2{ílP„"K©íýOWzýšoĐş )ŰŁµt'0Iěm„ěN_ýjF_-jó•L,žĐşYt…J@:~iÇ:ŃşĚ,RčmĐq@yÂĆO 1&uát·djĄK8Â$Ö‡)!Ś&1Ç)î>#m®ţµ€u¬§­ćöĹRŰÚŁ‰umÜ€H¬7ł$JJI’žRĆČ!Dv ˛ÎÍę îÝŠG¶Zf•c˝jłáä5 !{®¸öݶ$ăČČ Đ>Ź?/üľsĹ[JϡJ/Íc<ĎËÁ†e/ť"—ËĎď>Ű#„R"Yb*Ó›źŐüÁőĘQ&u0vB(Çs¤ÖÇ$ÝäPĘ0Q’8ŽgL´vÂĂóĄŃ«m®‰lEĺ8 H—2}îěĘVä6ęńĄ˙ńę+•&F)%„Hb7YĘqL!%ťFg+ŠťqľĂ"‰ĎQQĎs’de(ĺäՆ؝ÍSĘ Q á(e=Ť;7”ˇÝ;Ë쪯ţ×iýC¨á[„¦r•·Ú>ĆV¬3Ő§ęĄną%D¤± R‹:S}JŁ$0đĽ×].ú35ŤĹá Ń*lýT„‚Äć$°öą;!ęÔ™ ŐˇÄň¨Bę®ĺSOMü»!ĹoçTžm–ĎO¨“Bíĵ\7tĘsš 5ĺBÁÔ”gb ŕ©ăP{Žcug$h‚mXąĄÚ ŽŘŮ+ŚfŘR[ól9 ‰¶wS”l«i[1kběí|MWôŤĹ&ŔŞćs:ÉĚşňĎ»(mĽ”ňË~B`,1uÄkľ‹ţô-I „0‘9Nő ]â”¶:ĂĐë|\mcăŁRä×뺪T–±ź“ó'»&cóoeU&‰QŞs˛WSÄ$´dÔ51Ş^6(­4?]gä)%‰WŞĂěµ “`ĘiĐ›$&?—c ľÎŽ047)!´Ő[·«dx­óÖ©Łż8s|{IGx»¦!–Ă7ʱÎ6_fć˙Áţş$yŃ•ń×ßűv,ŔÍRX Zíî9ČŐ.7#[4d#÷ŤŚujŞK+,¦O ‘+”ĺ×KšČIäYIy~^QuÝ€śŘ€(TŁĆM´ăŤ‡˙j0ݤ6nW=` •STtAĺ!ĺ¤_©3™Á?rxLiÖŮç2EÖ/ĄB9ŽId˘$r ÷5ʬrŐĽ+gżýů¬yyÜŔÔÓŘÖM͔ʷ:)!}”Uéäî3ČC{9-S Ž•j ËBË„ŹŠ¶×(DQÇ)Ě-5iWsűÝ”L––zřňa‚„R2öÜ/úa«ňěGá7äĐŽ˙ükòď]á8ÚíßJ’·“Çßýđ;ŹÝ9wÂô|ú{LÔ[¨Â>::TÍ„76—ĄgŠ”Ž>S'Žá[*÷üŐh¶Ü‡`€oD¬Ss]Zaqżöä»Ň"ŤHťäuĺĚ©Ëy… \@dܰ¨Ćşâ#GOÍ’¬bwOK)°®üôS}Pďv{»­Ą-őiEDáşňľů©‡¶^Ę­V(˘ŮĚ;¸G† RRšuײrĚ"$IĽa]fi×Rą·µ]J•ŰřÉcěió±‡+ &. *nhTpc]ÉŻÇNM˘“§o°żĐvśWŞÝÇOÝVŞ›‹;„DĹ[ę9zJ¦ĺ<")±±Şŕȱ3&ˇÓ~Y7}°,/«¸Ž_µfQĘÁ{’†ŃÜżÉtgŰ kkűńG* FKKĹ~Źh˛HÜ#)9ZtײrŤ&AˇtŠ Qđ$(xRR[\Ń;’€qçA޶Z±Ew-3Çhes ‰_8y菟}^e–(Ą”SP&I’$IŚ ‹ŚjEQPŽŤş+W2ÍŚĘNäÚ–+„ČnŠe9K˘(u äq€\7‘‡Ů˝‡(I„R”Z¨ËW';mµj°µX·]ţŰ' 8iř°¦ŞÂ#Ç;[BO}ů%TÁöI‡’˙ŔişçSI#Žslxüß)i7ßkFş%>rgüăĂăßPPŰ&˙:Üo˛€ü,ŢéNĎŃLJŹÜ7úHâ°OÂm=yČ›ö€bĂČCĂ|&jŰrz3H ^ëC’ż‹´|Ë)üÖŽ88,ůÇh[u—-rŕÎÇá{†%ďŽu(qř硜 z6|̱aŁ ŤÝä«P¨~6â«p 8LôwydĐ G‡1nŁO%uâ€Űż_G´IÂ÷áŔ±'‡'ď:j[ŚÖ†4X®ůŕĐÁĎúw~»Ç€ű˘AŁO$ŽÜ?âç¸q§†.tTÉÇnYbňźjuâža!óśz’'!6k†&ťZ¶8eţ´0žâBÉëŤPßŕ”ĺKÎ-šź±bɶÉIn€ÂÁçč=‹Sď™aŮ=GfŤW€sNYľô@WJŽyeĹŇs‹ćĄ-żg˙Ě ¶J´Ć˛ttđ:łlńĘ7|_Uä"KǤΛ˘±jŮíIržjÎ$e› ýĎ$9–áíÜłřů? X´P9fÇ/˝—]^]v,JŁŔq<ßčńéĺ-gúPTć6ą ¦véř!S–˝T×Řp=7'+3«±±ţĺGaâťË§¶Ż~´Ł®¶ş¤¬dÇGŻŮÉů˙«őČUĂĆ-Ę*Ş,*ĚĎ/(Ş*ĘY:)Ŕ‚ő/—V7d_ËŇ×W?¶r:zŤTÝ ĺO €WűĽňá'™% YGżQ˝=ÎŻ,/Dźü¤ŕÚa5 Úw5˙ÓŤ÷ 7É»sű(^q %§¬¸ ż°¨¨  Ż 4÷âˇh{5@ßÉ Hę]`r0EżUŐP;ĐöÜ?‘ôĎVĺ™Ęýß\ş×¶{˙«ż‹Ń„¦ÔĽůŔěţI¦3¶Dkmh#¦_)¨*ĚĎ+®¨I;ľEŘúĹî?źU^”_VS˝˙˙Ţr„ĹŤ^'ü!7B+ĘÖâŃW>¬olÎ/,.Ë<LZZ\­»ž›[SŻűő‡w˝9€züéŻď·r¨ŕ»ď\G~z×Ę÷F˝4ăÔ[şřİř‰ź^ŢrnŰG@4 ŐşÇć R*L^ţhE}c~vVyUÍůĂ?Ĺř;ŕlĂúŻ B)űAíí:đů?ęůóoW*Ë K*®ťÝâ„Ŕ KŠkô˛|Žo}W Ě{č­j]ťĄ_4é^Z;jŻťg2äR§vGűŮY·K~ű1uya•®µž÷€±÷¬«ojÉË̬©Óţć^\±víMMşś×5ő& ?Ç®RíĂ6´j*ďČíżĄÉ­Č:·×Ď–źóŔë5úşţ#€ěFŇšźÔ_qBđŘ%ĹUş‚‚ëůş¦ş·Y €öYĎ˝ë«tÍťÚ tĘʬôóqKqµŰL ÖPŮFvFŃ "m•Öé/Ftü®k)ŮPeę‚lşĘĽ?(*Ď+&-YWŰŘŁ%ô4'ą1g2ň­’°·ŁÇźIúĎ š{ďY. í=îĚpďd `ĺmYvqám㜨`“č9ţüďd@=|G‚ĎX[®3ÜťŁUÎłSÜb7ÇO<5Ôe°ÚoSÄ”¬‘3'şŹ9߉g. |†}dńf¦Žw}2qĐt;ÚH-ŘŚtk«yrĆď1¶$ vĽ&DŁvS¸-đ{tł?b“řóHL&äóxčŻĂ»j^6P­“˙®»îürŇÄ”Eł"»¬jäIłłťĂXo7Ţ…«ď]ęmŤ×éĹógzŘkŐ6ž*(ďt`á‚{ý]L 劣‘ľdn,O\łiÍ´Á 61C†ČýBW_=Ú“z÷zsK˝ĄT“éŮuł`µ_#łâ2(˛MÎ&AX9!Ě3rčşU÷Řq3žlli\=-¶T»ôÁúšŠ©ˇö áł ú”×3"uµŤ8{Wl04×µ¶ÝôĚš™×‰N_ýdeQ†ĚOI“ůíďňMś_[S6kźĘŢĹŰË]cÓÇÓZĎ†Ş˘kmíúč©•b‡O{`Íš‡W/rUQa‘C~ţýâ˘â7ź|`ŮňĄˇţîîţĽý‚ÚP48Ä€«äÚőžz|]T ‡LÂůgMJŕ1xĆŚ)–ś‰Ă¨Ü‚,ăNLŤOčŚŮwNž>ďég7Ý14 @XT˘5őp?—–וWבĂÇ<°âîž,ˇ§9I˙W5”p` ® üť}IŮ^gC)Đ|EWöK­Ş bKÇgh SÚRwÁ`¤ŃF¨Ě5Fs˝  Ť’çý˙ âÁ%ç0ÁŮ)ŢžéÍ-ĺ&’‰O­×T»˛ŻËĚ Ä^ůf¸ß'§1ÎAűp*+B­˝h«TKúk:9C*¨OYžV|\G”¤ĂĄ5ҡQ+|ď÷ ^áiăB8†Ű úŞrxÍs[6Đ–Ô›[ĘÍnsĽť‚¸Ć<U&2¦ ťx–k&ÔRżS¬˝Xe Ž±o‡ŮşBĂí-5Ďu[Ö_#KךI/´ä6ŞĚöĂť µµ…"T”c€>CĎąÚh5§äňŮ_KCÉť?ďú¬¨\Aą熖p Vßđ[ą.ÜÉ)ĆE[ŰÜ\c4€Â+WÇĹ?á«VČç•AîŽKÚ<.ÉŁ(S$řŹőt7 ůŤML˘ÄiÝ–{|ź–^ĂŔ©ěŢ™4am¸ßż€MĂb´\g%Ë?4Ž.îĽtąş¸™hÁ·–,oč dŕ“ě?­ŞBŁqTP ÄŞ†I"€–¬˝É#ß˙ĺ¨BĄ–ă@ ›˝öĺ)AŠ/ż=¦µ·ě "ăýG<°hĚo˙Ł M ™WŇ ËŞ†Oš_”˛ýŔµJ€öëŠsω ŔÁÉuŮ'.âV­^"I†!aq¤>ŁýˇőH&c}Ya]Ł`j®Ď/«&Ü1Φůúű€”“{Nž» @’X×ř)r!Śń^Q[vîÚ¸rvâřYŰ·˙4yđ łŔpj'/‰Hě?߲ő‰Uł'Í\řĘË›ěmä—·¤§š!żˇbGOݲs˙wż~÷’UŻ˝ńśť –‚Ý”jÍcí× $©=†t/´zÉiOL(ĎĎ-/ľ^Wob’ˇ¸¬8/·üözʱfŚőuŮélĎýIýµ1ů@'r¨§Ć”rá8ÜĆ ˇ$p.¦ś°őެUŚôŠ@]±EÎ!„qçőOżňÚ†‡Ül¸8y¤˙–QÔ’“–’[Ó4"j0€9żÚĐŰĺÍ®´do§FŚŐ—f©"gü´uK„;DÔe9žź0¨Ş4ż¶ÉÂÇľ˙ËQ…JĹ$€“G;?µM‰a±}ꂣ¤ąWę‘´Ć`”ţ´ő Ž'CŇÚŮk @á9ZšW<óŢö/ßóć©Ń,@e7zî]SFŹ6ÖWćĘ\ô_rŔ'O+97ŽŚŠÓ–×5…†G„„5Ő–WTŐB_r¸U>ŐeE5őµZ˛®\’űEaʶĺbKC}yŤŢRŞ®˘¤¤VÎc©)Čh«§˛´@ßŇ\ž‘ňéW?6eĺyMÍf+Y‚îú`ęöŮ:bcC ëS˝\ífŚEUĺ•m­¨­(-/„ĚÖ–ö$QdJ»Qs,üäńBíE™e”‘Ueś=ţ›·öd ÝÍIněĹĎDpž¶A+]ň^Čâ|ś|ä'rňőOEČ“ţRQCő©fČîű¬^…CbšˇŽŃdë§l—\š%sS[Í’|ť¬ÄPÂ$ĆűÚ{$iĘŢ,@šBb`ôá©Ę›čsX7Ż.@)ß®$ÝLľďęĺóďщaö6WŠŻŞ1Ű8ŕjuuĄÎ>?ÔÎĎż\m‘šqY|’Żťíš]»·Uę':úNątđ`­ÚÖ™/čĺ çE„9ęľĘ) R;Ć9kż;{îĺôR'%×";šµâNć×_«T¦3™đ˙RIo)弴ärzpá}ɉ•’§ÄâÍÁ~ĐĐW7.~ţţöcźd6Cv†{Ö¬qŞľňń7{€‘Á)h²I1ź<ţĽpą©]©®kxôą—|›/JiQ+oúdŕ6Ö#s”R"{t˛ńÖŐ¦ëD~śk‰s×YgrÇQŔśů+îHđúŰŁŻ¦VăőŹ>ٸzáÁőoˇŁ“(ŻÄA_>ýÄSďíwq´m2ôđµS¦Ńl6ë*7,Ľs[JŽoP Qěľ`G%m©Ýě•ôB čýő­e.C8yꤵö?î5?ˇT”ÄßÇžűEżw“ßú:©¨ÔP_÷GHh¨oájź.効?řcŤ-r˘’!íâŮňĽŠ¤Ĺk—Ż7$bpNŢĺĐŤŹĚ'ßęăr¬¶žÓ˝˛aŃó+ć¶áOś¤tĄe6JŢ!ńS˘Ż^щ[vX9{ě~ą=qĆ7˙z3,ŔăŇţ˙î9SNQ’K1ÂbIéµŕÄ52?!ŢN' ćN„şŐEźÔďť9jóŢô¶§P„RQ´ţ mH;!Řl(Ąú&Ăyß?`Đ’qŁľ=•7-BET«7ۡ„3 7Ž’HmśŘř¬˝»ż›˛ţ\vő ©fÁž)Q7ĺóOfä–RJĄŢ벪Dž«>u­Ŕ—˙xäôµK»˙ů`6ä§Ż^Ý{ňŇ…}ďümÍ”]éÍ% ž Šň˘„»ć¬ę%ŚŚŚK7†gćž»oîĚ om6‰bň `L&]内ł¶ĄäÚ9:Î^ąiÚPď×ű[jŢüäóŤ«nřąRhnxîţéß^ôĚÍ;0aÚĐwż}}ÔĹŚs»ßnŁtvqÖ3ŠZ;Ŕ R‹RmoO-=ÓâÎ…^ˇčp{Í’ĂIŕ5(LW]=jńšg7`Ƣ5ÖĄÖ­VGír’$Ŕµc?XŐ3˘Ů(hßüâ‡YÂüĺWH ”0t@ ë>řßť‡H˘™)mű”†ŘçŤńc0sÉť[ÁDÜ8üđŻýé^%ŚśĐđ䂱Ń1Á‘źm9  Oa¤-Űp”Ł”S(ä»ÇŚ#Ś0byúO­QŘ«]ŞKł j€†ëç2 ]UvxĄM;ňçĺP«äślI__ëěÚXS=jQ۸S˘±×čkĘ.§–ÁÜl0‹*;WڧVÔ»yw(*vSÍ&AÓ»%XëëF_P‚ÎŘT$xßí© äťÚmśüź ňĆ_x C_`&a ęX§¸/b#˙äm™ßs"›„şc5&ŃřŮŚăXÎË9UĺÜŕW ‰Ś r4łˇÂ¬ôRp»ĂCMŚĹ{k`Č„Ľ×3ONMÍú˛Ćc©Ź[˘ ť^¤ČŢ ŤŚp*?Ť5÷Ld’ŮrA‚ˇ ‡m!„D0  Ě¨7+ś•ĺ_Öć2­ŻŇÔh–@`2_y -{-§á$1&Hí<{¨şđłA@«µ3 „2cŁYá¤(˙ް6‡i}•¦f€M¬Ł?„‰ „÷ĽĂYw¤¶YÇ0†‘ČhĽŐ…ćúî绲”ĆL’$´vwßďfÝůŢyćÄS ĆÍĆc…ůU" ¶·G+‚[űʶĄâK'ŽďiTlź¨ŚI21 šZ<´Z3‚ÂýIăçąÁôúÉĂC˙űă3— ćÄĆÎô¶Ewg˛µşF3x/{Çv»ţ©5ÉâÖÚSfĘ5Č»ţYżYLfłYÔ×^ż’SýŔCĆEÚńAwĎž`Ţâ‡Iţßý€IL%Pçe‹ď:˝mKv˝ĘÝzč yâRÖPáâŕ´ů­§÷]1 ň®ĐUŁőĹ˙l=ňÉd05[şĘĹěKNS’BŔDĘń Ą@`ŇŚ}ÇĎůöS XžD´ĺP0čšLşë÷L?"9y¸1ßţA’]ť5™$Â$€I”úçÖĎń÷őäŤ-‹fÁ¤ ÁÉ=× Č!ŕ$ łD‰ç;Ó© €Y¬Sđ6*H’Ч€Ŕ¤é{{¦ŐMN7ă’Ez˘(¶Ž÷ňí›ß%ő­iB~{H“€ŞěëfbëŰé+Ŕ"! < ”ő™ŐFý_ó™Éd6™-Ëo"O™Yd˘N Ô(€(Jmň  Ą»Ü/®U›řFôů[ŰJ:żW0ˇłś˙ňŢż–Npš8qjJV9Ďó’Ä:˘•¬ű`ť€ Á–3nXłĽiđ˛Fúa˛väŮ[ _¤u+‚@á~ŁŔ$Élî¨%o4šŚ&3 šĚf±·X»Ö#ďá7ÍٱŮl–]„R•RikŻAëÜ ­(*ď8‰Ô¨µwµ'q ôv¨x^lC~yMŇ>PŠÎăÎč§?Ü+)´’`•DQd–˝łvęÝącTˇ”XŰX°Q Ž<ýŹ{µ„˝É|3»K”Č7¦^‰óC·ţS ĆŚř.&ęŐСźEzڶ`7ŢcR޸ä/Bĺ—ײŔ[cÖúţ|Č„“C]Ł€fÔń$źŃ6ÔC;&uTÔŁž.ł='\9äÍĐ!źĆŚOI ľÇ ŕ¶ ‹|Ř á .Ä)aslŕRŹŕçÂĆŠwňăŃ˝ä_áŽÎĎň›§·ÄŤ;™8ěłHßiNě&tŕĐsYŔđo˘WřĆoMłÖÂŃ&qß°„×C˘ţ;fO¬]€ÝĂý¦je"±[†F.wuśč>!=ąťç%˙® }?¶x žÚá{‡}#8䙉)Ă=‡«a«ľ?1áő¨·cĆî˘őćŘŤłđCaą~h7ÉkbÚϡ–˝bc›|t`$đO†Ä=ç  {o1”Ű8rÔůóňV.Ů3kęę`?±á5Ź<|nÖX €Ĺ±#~[pçźâľž5§bŐÝŁlfD ;1wÚ†ÁC¶-XądN”–uş´bůJ?•­kĆęű?LŚY¶fĺw“ÇýwćĚâŐËźŤö;.~wXZ}‹iť}wÜuç†đgÇŚĎY6o´˝Ş+›˛lß>këx źCŘ€%ŽPS#c÷Ď÷żé-@F¤7gÎůaLÜŔ1(»ţ°yńťĎRҲőőgŹxdá4ŤŔs_ťŻLÝ ~ÇÓËżxeä¸ aţŚ™—Ť ŔÝČ+üą!őŽN-¨:ńýćÍ[O×—\ě§Ĺ zʏťőXĽĹNýaűľĚüňňüĚý?o™ë Řr ­ľ,繍O:ťňÔşů†Í]Ç»~üN nÖ9Gvzâ5%«˘ĺĚO›}řńăçRׯš)S™şl“®±nĎέ/Ą â\Ę‹ŹŻýű'?čĘł§„»z×=Ő¬§0÷Ń«K.ÚňŔńŠNßtć‡@Đč…•ÍŇÖ/Ţűǧߊ-ŐK'G6§7ZÝätłAä×ĹŮśR‘ú×ý7·/ýNöÜźÔo[•g#ř÷Ţ ‡?yYfú6JȲţřpúO^čőÎŘ’ňۡe“Ć.ZőęŐÔc/l|áč•ÂÚâ+±ţ¨cwí;ô÷6żZZpq_€§Qńžűę|EĘ÷ý0Ź.8vüĐCsĆÂ!*­´éôź˝őŻŻÓÝ•ĂŔ)Đ”ŢÔ\b25¦2Ců–˛˘źëÝřOVe>źgl‚剑qŽĽc˘J)ĺ_“ně&ţ/%`0čYŔ>úăU-5(±ńW›Jšk~k0éĹćkMÍe¦N2J_µ]„Ć|­!çő|]‘PźŇd—ä  ćśWŻ7äYh¸¨75H‘ŚbÓŐĆć2ł©ÚdŞL冲ďĘŠwŐ0ŘDŮ;+ĘvŐ2BĐhŞKiŇŽpP;ü÷ *N4Á,Ôťo˛KrPPsΫ×ňL˛y­”PĘněÝöí«GŽßfăäčďéúۡçÓęëjR/Ą–VWď۽ۨrž2utŮĺÓ_űSyMh6–——ďŰ»űBz® ş˘`¬¨¨Ř·wwjz.ŃUĺî?|ŢwذŃĂbŻĄݲő—Ę%ČI?›UX­PŞ*rŇΧfi<|ĆŚźŕĄj~é/Źí<“ @0äzşÖ|!=W~Č& ć˘Ü¬łç/µ^[čđMg~®ä2şÂ«gҲ‡%ŹőŇŠŻ>·éŰÝç`6őF«‡śî„ f㵋gÎ]ΡJĄŇÚ©3Ďó|G/Ď7•ĂóĽp°÷đ;Ůó@Ú*áxƤëućç^Ü”şksn…žŁ·É© ĄśÄXôče˙|rî¦ufVč¸~†4í-9©)—Ż{„G&ÄET\8ňäú?ťË©$Lt ™0~Rń™]ëŮ]ÝL Qb€ŮlČş|ţěĄĚ~qiM«ľ./-íjćĄĂÇRc'Mtâ_yfĂ÷Ď3Ѭq°Ź‰‹3U^|î©Ç·ĎŻÜZŞ>çbjFa®{HÔřqÖüt‰őn­‹_6çV4RŽŇő—źŮđŐŽ“]úarŘŹö–^X‹ľ2%%Ą /óřĹë¶ –~.5#ż°˛¦ş¬0űó÷ßúčë]‚ĢÇ,ąQ]fŁ[p,çúSľ®ą,+5%#řČŃŢvÜÝô;Ź’h%źÇ¶Í`€·‹[Nkż @qĆůÔk–RŻż¸éăH ÖˇCKK‹ĆŃ!6.ÎTyńů§űĺěu{[űëy—/¤eéôőĄĄĹ©©)%U˘`jĂC€1«>hąŐŰÜX•Úł4>üj‡Čú@ÓĘ6Ęęę ®^>|dwjVqâQr+>ٲOdđqvÍą!`L0™JŠ‹ĘŞ«Ë ł?˙ŕ­˙»_"¨.-Î/.­,/+-ŻČżšz1ł°Ź‡aťëyű˙ě‚ÂeÔŘdˇ&ď—˝‡Ě­ą®ôLęIh8}*]ŕRĹĄ]Gä nSłET’@9&‰Ú¨i‡~üČ™¶™•ß6ë(Ý]rĄÄZd’Ää˝ĂÖo# ©víĽ űŻ5s”ôă6!µĆV¬3Ő§ęĄnĹK:Hc¤u¦ú”FI`ŕyŻ»\ôgj‹Â&0˘UŘú©3ÍI`íswBÔ!6¨3ŞB‰ĺQ…Ô\€xjâß )~;§ňlł|~Bťj'®ĺşˇŰ~MxN¬¦@3šňLŚü«Át“Ú¸]ő€)TNQŃ!< ”‡”“~ĄÎdC@XüČá1ĄYgOśËY?·5)ÇQB ‰L”DNáľć‘U®’wĺě·?’5/ŹI“D±ßpO©|«“ŇgAY•Nî><´—Ó2%Đ€čX©¶°°l ´Lřđ¨h{ŤB%€qśÂÜR“v5·ßM±°ĆTöNŢî® JŞĘ+ęt7·Y*·Č' 8iř°¦ŞÂ#Çτۊý±Uy­đrhÇţµaŮw‡®´ą ţ˝ĺxIĆßýđ;ŹÝ9wÂô|ú{LDS»{rµËÍȨÂ>::TÍ„76—ĄgŠTŽ>S,hsÔh©Â>**D­ÂÔ\vĺjˇÔźľÓ•ÄhDŇč„ Ż+gN]Î+ĺ"ă†E…4Ö9zĘh–dŐ[•"@gôëż.ú ŢťqĘ óŤuj©O+(" ו÷ÍO=´őRnµBˇÍfŢÁ=2d’ÂÔ¬»–•c!Iâ ë˘;T TąM2ÖŽ4;p¨Ň`á˘Zĺsě´Ń$:yúűű0ˇÎ+Őî&Źi+Őí!łq÷ôo•FWíth{s]ZaqGů€ŢuĺŞŢ¤a4÷Ďůzgę`ťÚÎ}y^9sş'}ui~rŤfAˇtŠ Qđ$(xRR[\ŃĚ$„Ł–5ÇQ.4"Ş!A9N4ę®\É43*; k[ftEu9X“DQ’Ç’$I’„ÖłPŮu‡(I„R ĘńTŢ’¤Î«Ůţ dQ´;KčD U°}ҡ¤Äpšî9ćTŇČźăĆ˙÷AJÚÍ÷š‘nÉG†ŹÜ?ćřđř7”Ô¶Éż÷›¬ »pşÓsôńá#wÄŤ>’8ě“p[Oň¦= ä0ňĐ0ź‰Ú¶śŢÔMŔk}Hňw‘–o9…ßşŔ‡%˙m«¶¸Qî 5€ pľgXňîřQ‡‡Ę)Đ gĂÇ6ęĐĐŘMľ …:áça#ľ §€ĂDĎq—GÍptă6úTR'ž¸-ńńuD›$|{rxňŢˇŁ¶Ĺhm@ĺšü¬ç7Mx/MÜ7qŁ÷&ŚúuXÜ‹<Űä#·,1ůOµ:qϰyN=É“›5C“N-[ś2ZOŃú:żUĄ@¨opĘň%çÍĎX±dŰä$7@áŕsôžĹ©÷Ěż°ěž#łĆ«Ŕ9§,_ú@ +%GŚ ŤĽ˛béąEóŇ–ßłć„[%ZcY::xťY¶xe€ľŻ‡*r‘%‰cRçMŃXµěö$ą“ĎŚ‰?5g’˛Í†ţg’2„đöîYü|„~·hˇrô_űA±űÎe•ĺ—ŐTď˙ż·-˙Âp›\PS»tü)Ë^Şkl¸ž›“•™ŐŘX˙ň#‹0ńÎĺSŰW?ÚQW[]RV˛ăŁ×ěäü˙Őzä'ŚaăeUćçUĺ,ť `Áú—K«˛Żeéë«[9V¸ű hů“ŕŐ>Ż|řIfICÖŃo”@oŹó+Ë«ß'?)¸vXM¨ö]Í˙tă}(Ó‘ZĄ‰8’SV\_XTTPWPš{ńP´˝ ŠŽďdzNĽJĄ°âO6ôyY9uşş˝źľĺÇSp|§JúĽÎń&-YWŰŘ’—™YS§;üÍ?Ľ¸ŽjţSmUžÍÜ˙÷Í…©{m{ô­ú;±HMhjAÍ›ĚFżz:@8Ź'^z/»Ľş íX´Ö€6bú•‚ŞÂüĽâŠš´ă[T€­_ěţó´9đů;v€*bzzë7—Ź~ŻéŰKgZQ¶–(Źľňa}cs~aqYćŮp `ŇŇâjÝőÜÜšzÝŻ?ĽëÍÔăO}ż•C5Ç€nĐŻ7]P |oÔK3N ±í&DşĹ×Hôřôň–sŰ>â˘I(¨Ö=68•R `ňňG+ęółłĘ«jÎţ)ĆßgÖ]tEőź˙Ă Ţ‘?˙vĄ˛¬°°¤âÚŮý!Nś°¤¸F/ËçřÖwµŔĽ‡ŢŞÖŐYpľI÷ŇÚ9P{í<“!—Ę8µ;ÚĎ®c»şęB€ă=Űs4J´ö;ąígúPvÖ ý8M\~UŻŇđsě*Ő>lC« ňŽÜţ[Zk+öÄúŰxěo˙ę]_ÖI~ĺoÍOęŻ?‡8!xě’â*]AaÁőü]SÝŰŹ,@ű=2öDÚ*­Yęo˙ďř]×R˛ŞÝĆ8p`BM×V÷!ű‡˘]tˇQżŁyQövôř3IC˙@cď=Ë…´Ł˝Çť¬Ľ-Ë.%Ľmśµl=Çźáťl¨‡ďHđk Ŕu†»s´Ęy–çč}qvj˘d—ü[RŘ 7TIřoŠő}¤BvëîeăłÄ{ĐJo×$»îř(TĂv ąŰ⑌ qş96öÍđäµ6ÝŻjh€SŇÎ8·!jÎAˇrTĐ$»Ź»ä«vťă7éĘpź)n±›ă'žę2Xí·)bJÖČŔŽŽÝÇŚďÄ3—>Ă>˛ř@SÇ»Ś>™8hşm¤–lFşµŐ<9c„÷[k‰ÉË Ź{Ć‹·ő 6Q.cĎ$yŹŃ ”ÄdB>ʇŽř:Ľ«uČF¬uňßuם_Nš˛hVd—UŤĐäµęʤq§çL”=q˙r<°tĂGMuą1~6!Cęš[ÖŢ5 €JÉxňýťĄ—ö;ÔÁ9*6ÖßËmŢÚ u5ĺÓ"Üp·¦5ىJŘ´ű›%ă}I!łV˝Ä´zFZ§˙łő(Ő*G/˙żsâ·^ đráśC§–ęĚ˙ys=€ÄQ3’ŁĐ§U@ĺ¸ę‘Ť/<űÔĐč@ žxüť=釾ä9j‡äCŹmÜôÔ“ł&'Ë%{oüüűĹEĹo>ůŔ˛ĺKĂý\¬•Ń„ěß7]lCÓÇŇń&%ëg_R¶WÇŮP 4_Ń•ýR+¦jŘŇńŮZČ”¶Ô]0Ři´*sŤŃ\o(DŁäyŔ࿨x0AÉ9LpvŠ·gzsKą €dbÄSë5Ő®ěë23±WGľî7ÇÉiŚsĐĂ>śĘŠ•Ęą@[ĄZŇ_ÓÉRA}Ęň´âă:˘$.­‘ĺZá{ż_đ O Ŕ1ÜN(ĐW•Ăkž«Ř´¶¤ŢÜRnv›ăíÄ5晨’0‘1íÄł\3ˇ–úťbíĹ* tŚ};ĚÖ"ŕno©y®‹Ř´ţYş­ü0 ;…!Wß\!µd7µ4HvlŇ-KŚ1@źˇç\m´‚ŽSrů$±ĄˇäÎźw}VT® śőŮ"±ś3µú†ßĘuáNN1.ÚÚć棔^ą:.ţŮŘ_µB>ĺä ˘pw\ŇćqI„E ś")Ŕ¬§»ÉhČol`%Në¶4Řăű´ôNe÷Τ kĂýĆřlŁĺ:+YţˇqtqçĄËŐ@ßŃ‚:ůżÓČ­'Ů!ZU…Fă© ř}V5r 'Ď‚ôß2ŠZrŇRrk›ĂbM"ď?âEcľxűµijČĽ’VXV5|Ňü˘”í®Uôć^X¶%J$c'×eźüą[µz‰$†„ĹČŤ¬Fn{=’ÉX_VX×(šëóËjD† wŚłiľţÁßţ ĺäž“ç®$Ö5~ŠśCcĽWÔ–ť»6®śť8~Ööí?M<Č,0śZĹÉK"&űŔĎ·l}bŐěI3ľňň&{°n#ł´ĺPŽ=uËÎýß}üúÝKV˝öĆsv*X vSŞ5Ďęb˝$µżDě…V/9í‰ ĺůąĺĹ×ëęML2—çĺ–ߤí0‚±¤¦ńС_k›%­“=pc}Cź’qöř˙mŢÚ”•ç55›o ü­ĄţÚ| 9ÔScJąpncP8SNŘzG Ö*Fz•2“D-Y{“G$ľ˙ËQ…J-çÂ8Ťóú§_ymĂĂn¶\śĽ:ŁŤ`$¶.ëź~ĺµ  r·űš¶wĄ&Ś«/ÍREÎřië–w4¨/ĘaPUi~m“…†'}˙—Ł •ŠI'Ź®č×».8Jš{ĄéAk F9XG[żŕx"1Ě^űň” Ĺ—ßłł×€Âs0´4Żxć˝í_ľçÍSŁY€ĘnôÜ»¦Śm¬ŻĚ),”ąčż.şAőšĆ‘Qqş˘Âňş¦Ŕđ°¦ÚňŠŞZčK·Ę§ş¬¨¦ľRKÖ•K2Φl;Q.¶4Ô—×č-Ąę*JJJaĺě «.$Ń  %kO{ŽwObmm×Ú۶/g¬•Mh¤ŃËĺ®îřTU”W¶¶ÂP_‘ť[>uĚD}IfW}őŃ/D‘)íFͱđ“[TÄóśYâFŤš0ăŽ)Ó¦Mäí‰^'îň%ĐáS?ţ×;x€~˙Ó˙Ě›šPUTXZ׆ą9ĹĽOô–Ý{^XżlńĎîŰ˝=>ŘŔ±w´ˇúoľ *yĘŹ;ö}÷ńëw/[ý·W68D|·}ÇS+g'ŽźµcÇĎŁíÝÂF|űăÎżýüÂĺţřó–ÁNŞ€Ř‰Ţ3E0›FÍ[ůřşű"ü5°:8ë!e}őď›.ş`ý»@x#‰g"8OŰ •.y/dń >N>ŚAžŮ1EČ“ţRQCő©fČnî¬^…CbšˇŽŃdë§lţf/0"28MX6)ć“ÇźŽ#}ľ ď_"Őu Ź>÷’oóĺC)-jĺMź ÜĆzäcŽRJdO€N6ŢşúÂt˝€PŽc’üޱë,çpĚ™żâŽŻż=újj5^˙蓍«\˙Đáz·ŤWLbĚ /ź~â©÷ö»8Ú6IOOŰ;eÍfł®rĂÂ;·ĄäřĹî vtQŇvÚÍ^I/´ĐëTŁušĘ'ď“´Ö~ăö, ŽŢ±{÷îwŹ®M?´Í’ü†gü”SđÄl’Üßzűő–śĂ;÷§č˙Ő[N}Řěř>Đ×IEĄ†úşŰĹ•ubę[8…Ú'€Kąbîţ0Q Rňm3$*Ň.ž-Ď«HZĽvůŠyC"çä]ÝřmĽŹEŽŇ.ž-Í«HZüŕ’Ąs&Śťp1ż†RŇű¦IWZfŁä?%úꝸe÷•łÇţç—ËŃg|óŻ7Ă<.í˙ďž3%ŕ%ą# –”^ N\c…~ť—¸Ýę˘Oę÷εyozŰS(B©(HZ˙„W7.~ţţöcź™l6”R}“ῼď0hɸQߞʛˇ"*‡Őžµw÷wáŞĎeČő÷_]Q=ČĂáU]OŮuˇ ń_éˇi¸zäŔTac¦|űď·eůě>] Ę›DAĆůOźx@Ʊý©O·•Úż7čě­««4(¶ĺČ>ě mkűř łH[ŔoËv %úĆľĄŃç¨5?ňš*íđţ …ĎĘ­hĘ:zŕ·¬™CTžAC¦D_éV_=&I¤6Nl|ÖŢÝßMY.»€z†‹TłŕÁ?Ď”¨›ňů' 3rK)ĄRŻŁ#ĚMŤz‘ ŤúfŁÉ›XFHžB”pל•C˝„‘‘qéĆđĚÜs÷Íťyá­Í&QlCőAaˇĚ‚ɤ«Ü°pÖ¶”\;GÇŮ+7MęýÚcK­Â›ź|ľqő ?W Í ĎÝ?ýŰ‹žąy&Lúî·ŻŹşqn÷Ű[3eWzł|碓ÇČžŇZöýů¦Łm üŢ,ŕąŘWc'QO§(uV{ڵ#"“Dň×P×@–ö—ë&fqo€äEEsjMĘâK˙”Ż wđš`ËZ˘â´ávÄ(4×™!߼j4eÜźvjꥪë$ř^7pÔřÍv®üľÜ €póőĄŰĽÖ†Ž:rźíÁ~„&I’Qv´TÖzÖŮçĚ!cĆ+Ź_Ë|#?ee¦ Vą$kEť  ˛÷I¶É{·P%±áZ.éô™Ť5Gő˘Pʬy^nąF¬{š’kZ2˙ś“ů|NíuÁ)ž4 Ş źd›Ľ÷ŠىÚů‘űiŐžŠ’cM‘ďFßă(Ô DAĆx:łĚ¨ŔD&ęd]$ŘúRżS6O)gőOŮeůwţ´}ńłn.KÝ[J×8üçSgîÜy¸žÓNđu$NĺâƉ¦C J©dj\żsWâ·˙ROźjÇ5aţ;ÓŻKŕ(š*ľĘ,]”0öňŇů/‰Tµş\kW% Ţ$ Ś)nô&Ę˙?%ŽB”¤&Sîén:É`~úöÓŹwĄ}µăŕŹM«­Ş˘*5ęóÄŠ9?~řĎBQľý"q÷Ť1ďŰť‡Ńş[sëI2µxGŚ}hZä_ž~¶ž¨Ô7r›čŹŞÇrŚÍÚXAjQŞíí©ĄgZćÇ„đ E‡{M–ŽH"ŻAaşęęQ‹×<»á‘šÜôŚŇRů¶!8"źÉŠćĚv`Ĺóß••ćżőç‡Ő–í´‡šŰs”JueIĆľK9J®ç›Ö ?–źŚ0Q˘ ś(1*‘ DŃ­nr:¦ÖŮK«%߬_/J9SKÝžý{ë›Lf]iQInĆMa˘Ů$hŢüâ‡YÂüĺWH ·+ úacň)YťŢ(H’Bu›"„vŽ'˘ŮÜX×Ă(Ó}!,Î-›˛-ś=űˇÇÖŹ3·ZĺżháÄ=ź˝űéîtmj*+Zg±ú|Ű7ušAÓ¦Ś@«+đ~Ň’g,J•Ťˇ"cíâ®\~čZýÄÁŁd˙qd\ĚŚ…Ź{ĹO\94Ds[)yjţÓ·źXŁQŞúĄ‹ľ¨OŽ€QNˇPđÇ`ĺúż†9Ô~Ńٸ‡Ţ3#ž—Z$…vpüHŢP›Y]€ă•RCŃŠqĂ|"·^SĽţä ™`˙uŃŐjk[Äńw>¬^5˙Ιsď!Ö.› ëÄÖ6ůÜ·`4$­8˙ýî#f,Zc]jÝŠ9ÝhÇJ­ÓÇöŽë®íł’ $Fhla"SÚö) ±Ďă¨S3—tlűĽ©˘Q0TeuÖW_gű¤?ükşW #'4<ą`DltL`päg[Žč—+H€€2¶ ­[ăŘ«]ŞKł j€†ëç2 ]UvxĄM;ŞçĺP«äślI__ëěÚXS=jQŰR˘±×čkĘ.§–ÁÜl0‹*;WڧŚ0ůNdwGŁ= $¬Ç‹Ţżé¦§ü7ý)Agl*ĽďöÔrÄNí6ÎţOy ă/<ˇ/0Ž0u¬Sܱ‘ň¶Ěď9‘‰MBݱ“‰hülĆq,ç圪rnđ+DĆ9šŮPaVz©¸Ýáˇ&Ćâ˝5° dBŢë™'§¦f}Yă±ÔÇ-Që73‹Lb74Č´dr5-u‚(óÉĚ­;îLőúę;~¬0ň!ˇr¦ —E¤>ß ý¸ëů(ë—.zˇ.?  hí;ĽÄÚP—%§ú‡Ś‹´ăçĎśL¨áL×Ü»­Č~ÇWď( ‚É$@Őő˘rżA*ý×ETÍ*-ö 5ëŻ˙tđÔŮCż\«5ĹE©M>á!á t—qţZµ @€o„Iźżµ­Tp ©;k´Ö»,?ł ™Ěfł(¨Ż˝nÝöł§2`P;nH$Fl9ă†5Ë{‘/k¤F"ó#ĎŢý"ŰZ‘QkN ńil¬0KÄĘZúevL’ĚćŽÚQňFŁÉh2˘ÉlîGŚ,٦$ĄŇĆÁŐ<Ůlą§##¤Ľ›$RŁÖŢŐžÄ1ĐŰU ˘lĐm¨.ŻIÚqžRtSF?ýá^Iˇ•@¨$Š"łě‹©”J[{ şŰţîŠ<ĎKkĹůŤ X0˘ço,cAWŰř%ňŤ©€WbÇüŔm˙”‚1#ľ‹‰z5tčg‘ŁíŘŤ÷”7.ů‹PyUćµ,pÄÖŘ µľ?2áäP×h u<Ég´ őĐŽIő¨§ËlĎ —Gy3tȧ1ăS’‚ďq¸„mĂ"v@x€ qJظÔ#řą°±‡âťüxtĺ_áŽÎĎň›§·ÄŤ;™8ěłHßiNě&tŕĐsYŔđo˘WřĆoMłÖÂŃ&qß°„×C˘ţ;fO¬]€ÝĂý¦je"±[†F.wuśč>!=ąťç%˙® }?¶x žÚá{‡}#8䙉)Ă=‡«a«ľ?1áő¨·cĆî˘őćŘŤłđ#—˘ZŰÁFŻőNřihň‘j ´ul`$đO†Ä=ç++´;-sGŽ:0^ŢĘ%{fM]ě 6 Ľć‘‡ĎÍ«!°8vÄo îüsBÜ׳ćT¬ş{”ÍŚa'ćNŰ0xȶ 2—̉Ňň N—V,_éç ˛uÍX}˙‡‰“Â"ËÖ¬ünň¸˙ÎśYĽzůłŃ~€bÇÂĹď A«o1­łďŽ»îÜţěń9Ë捶WueS–ŇÓgmŕ÷XÄ÷’8BLŤŚÝ?sÜ˙¦·µŢś9ç‡1qżňMµ}üŽ=˙ţ†ăWK .îĐđ9qµü‹WV@öŽĎńć?Áyٸ@ moi¨SďčÔ‚ŞßoŢĽőt}É•Á~ZÜ ÷ŰYŹĹ‹QěÔ¶ďËĚ//ĎĎÜ˙ó–i±ľ€ý'ŇęËržŰřÔˇÓ)O­›`ŘÜuڱëÇżQŕ”âf­‘sd§'ľQS˛*ZÎü´ůч?~.uýŞ™2•©Ë6éëöěÜúđ˘QÚ°ń'ÎĄĽřřÚżň®<{J¸;€ˇw=ŘSÍ*€pJs}±şä˘-Č:~Ó™ AŁV6K[żxďź~+¶T/ť`ŘśŢhu“ÓÍ‘_˙esJEęO\÷ßô‘do=˙CcŐ5Ľýßs-×Oű)n¬:ůŮý‚5Ż2Ć®]8ůÍ×?śűôË}ڱĂßčI๯S*R’'yŢQÓ ëĚ;ľúŕź_ü$čJç'XđŘKm¨Î+č€ó€ŹŐň[ęĺŚđżŇ +˘1ĺM⠏ΠОĽV‘~ćŕ?_~!9ĘÇşÇu‹‹gŚ0lŽeDÓp°pmoßX=•V¶Ńj ôfoRtŁ ýŚ‹ ¬9«I—ŮByÚ’Ó¨Ď1M˘±Ę¤żÖh¨ 2S‰ˇćd˝>×@2Jśof+Ő5_'żú˘<¤&łţjَÄPźaŕhJoj.1™S™ˇ|KYŃĎőn‹ü'«2źĎ36ȤŤŚsäTJ)˙üštc7ń)AĎôŃŻj©‘@‰ŤżÚTŇ\ó[I/6_kj.3uâ PúŞí"4ćk 9Żç늄ú”&»$1çĽz˝!ÇÄĚBĂE˝©Ad›®66—™MŐ&S­`*7”}WVĽ«”€Á&ĘŢ1XQ¶«–‚FS]J“v„Úäż_Pq˘ fˇî|“]’‚šs^˝Ţg’ĺi*5Ôś¬o´đĂľj§aĆ+u™/li…8 lăÝBďuÎ}5ŻąVěáŠ-ńs°ŻŃ×,*«2™ňjj˛›%IŞĐ7).I«×K€A4*e¤‹ł©Ąöő3gUéAłť}Ś‹CUMů‹'Ď]Đ(!Íć–ËŐ5ĹzÝéjť†CfUM¶ľ±Ę`¬hÔý‘ţď¬âéŃĂ×j˙|ô·2łE™˘ ٨UIŢ^TřÇůłŞĺ;‘—0 ČÄž›V›ßb¦ýx«7P‰ÂŔ‚Ü<’mţ›] ýŹ­j(!îüň`˙WNśĘm1sż§płŃ-8lÂřIĹgv=ňřĆĚŞ¦ŹľöÄüŔu+W—č“ws|\Ýs/űćÇĂF6P'GŚR"é*ŹK‰ź<Ĺ…–mxđá“×J ĄěĆ6Îo_=DŽĚŕäčďéúۡçÓęëjR/Ą–VWď۽ۨrž2utŮĺÓ_űSyMh6–——ďŰ»űBz® ş˘`¬¨¨Ř·wwjz.ŃUĺî?|ŢwذŃĂbŻĄݲő—Ę%ČI?›UX­PŞ*rŇΧfi<|ĆŚźŕĄj~é/Źí<“ @0äzşÖ|!=W~Č& ć˘Ü¬łç/µ^ţęđMg~®ä2şÂ«gҲ‡%ŹőŇŠŻ>·éŰÝç`6őF«‡śî„ f㵋gÎ]ΡJĄŇÚ3Ďó|GÍsxŽăASNĆ…“g/I ŮٗꚍE×/Vč%•RA-…8ůř»§$?ňS*mňsÓ.¤eéôőĄĄĹ©R K«$ţŇ@Ů*áxƤëućç^Ü”şksn…ž»]wä(ĺ$ƢG/űç“s7­{0łBÇő3,!ˇˇŐyiżě=TŢPź“šrůz±GxdB\DĹ…#O®˙ÓąśJÂD×ŔmÖ=˛ˇ°ˇ™1¸ÇÇGV^8ňäú?ťÍ*í—"¬iŐ×奥]ÍĽtřXjě¤ÉNü+Ďlřţŕy&š5ö1qq¦Ę‹Ď=őř¶ăYŕ•‚[KŐç\LÍ(Ěu‰?ÎÂOvus×€0tńËćÜŠFĘqBBz˘ţň3ľÚq˛Kß±DŚe€ŮlČş|ţěĄkkŃW¦¤¤äeżxÝVÁŇĎĄfäVÖT—fţţ[}˝KXô%7Ş kTđˇ?ĺëšË˛RS2ň‡ŹímÇ˝ń×M˙·ó ‰VňylűŃ x»¸ĺ´â<Š3Χ^ł”zýĹM˙p@éj°.˛SĎg–„GTĺ^–-!űBJFA©$1ĆXkŰ3`Ť‡2’45VĄö,ŤżÚ!2Üm”ŐŐ\˝|řČîÔ¬âÄŁĽí¸×^üó§Ű~• U‡~=3yJ¬Żť§űľËĘ`2••UW—fţÁ[ţwżDP]Zś_\ZY^VZ^‘5őbf!Đý´ ˇ”1É3|&}三ľZá™ m=‘@0w@uÖçĄDWi5¦¤Ýňă/•uúú˛ü“gÎ5IBScőoÇŽ—×I¦úS§ŇEŽ(U\ÚĹs%Őú¶ëNÝ#djJAI•(,#ÚŐ\‰AĄÔ\ďĺ‹N©5&Č–psqĆş—gŰ %ěćcŞvTY·ę#ĂX7[[Séž í Şîëé”(qźćÚr­Aźgş™„VÜú¦eýGČ\ďń ľĂöÜĘL©Ű‚äfjîăű®ëA,&JkçĂW¨˙]ǸJîn‘ßI'ů‡„*šżÉ-eň‚ô†ć„ŢXX]~ąľ‰Ü–; Ŕ*2izô§]&ď8lŔN6É$ÔŐm˝j[^ńít~$kaÔť+ŁěKţďżhk¸mË«NÂQÜB<ćn[k-ű›×ÄZ‘ďÚÜ Ĺîş‘˝¦ZÇîD©_8Öú|ŽŇ˙.Ůq>Gä°n7%Ä.5ËQD% ”c’¨ŤšvčÇŹśi‹™Yůmc–Pčč&‡1˘°‘j×Λ°˙Z3G‰Ę$Qí?ćŔŽ/˝lŚ&‰.ßôó6ČúocTą`ń˘ëMąZÔßP·ÎĄL’"†Ťčüí–ݸKşé7ßĚÎrěşcÚggîu=Ő».zĄŢöB)e˘ĺÎS{gŁÝĂG©8ş „0°ľ¦-]Âb ­O¤z,u#LýX“ô,Ť›Fő®­ëęčó÷†«ľ‰}9ÄmüÜGýű‰ Aa—šDŮ–Ą˛o1Ö†–¬s_éOăűÍŇďŕ}čŹHíŻ¸äćČ3xB¸¶>Ś1X^˝·¶šPB8Hf€ĐÎO= % č°L˛K­¤Ý^űt”9á%‡cňÓ#Ž€µŇĄ„€1ÉęÔb„€I%»[j©–‰LfĎqŠ»ĎH›«-`­ ”ÓľěXłĄiÖü´őL+~G µÄ:Qď¦í˝H¬WłlÔ9JÁČ!„#„1&2€Ę$ ­‰đOęv(!”@´ęhTvß׺ľG;;–†ZĽźôÜÇ:aÍmKr{G…®đqxäřyḋßơ%ňŇşĺÖéîŇ-Ĺ ®^ű O‰d‰©L™tł3·?¸B9ʤĆNĺx’Ôöx´kĄ%‰ăxĆDë×<Ď3&‰ă)%Qě,.•úQłgťöł”ĺŮ@űB¶ďR˝Pď*^0Ćk}çΚ¨ćÚׄr’uó;çĘI†_÷î(¬3HŚ€Ů¸Ďľc´’­®”(Ç G~ŮVT/ô> ĎQÍ^ˇ`’(޸#µ›Oý¶19Şýíc¬nlŢĆńĽĽIEAľĎb2 =Íȸ)eVš˝é#2kZ’(Š’D)'?Lć)DQvŽ#‰"(G‰Ľ”čTJá8"ťŃŻw]B8ŽĘĄx…’(ť©Ën-c·Ľá‡öŽç$±CLwĘń”@ÚĂŻSĘáćE8ŽJ˘H(0yw€ăxIp=ˇRŽRŇĆ!„rTDYt’(vËCW]tͱj&łĽ3nžç$Q”“?“$Fiëô”±ľáĹŇtŞTđ‚Ů,1Ćó<“$‘1ľU_˝´˘ź‰çyŔ˘)ĘqL°Ŕ2ˇ”ôŽ$ň–W丧WM{ýągK Śç¨(»ÔďÂĺ8XÖ9¤Í™G©őžWלcŠ$J <(vđ /Wľ!óî^tÇ„‘J~ eÖ•ú˙v=€Bĺ4$!qhB||ÂĐÄ„x'•BÖP@Xü=Ë–ŹÎőěĹŇr·ŠłŘ;!”çeź1Náľî‰MĎ=űôŇ»&µižPÚz«ďvɶíŕîť8|x¸żO®0ËĄśÜ}âbĂĺŕéŃCü˝śÚţé–áĂŁ‡$&KHHHHOLüűża镣V-Ożă–¬ĺ©÷b«ň©đrěÂ˙Ź˝˙ŻŞZţÇń×Z{ź’““žŢ{%„@čUŠH“‚(˘¨WđZ.vďµ{Őëµ˝-W,( Jé5ˇ„©¤’“vrĘŢ{ýţŘ'ÉI-ú}~źy4™ěµÖ¬™YłęĚśť7¶‡G˙·ä§˙Łć¬:sdg€¸™Äy„°éçĘkń‰Ťőó@•±ń“&%%ÇEűݦ°]xC˛ŕIĎúÓQÇÚb8DázďŠú»P(P:ôë?0)))©t¸JÁÉéMnŤ,nţr@µ•Öµí…liícúč˝Öun!˝ś(ŐNă&Ţ5{ĆT7\ŻÁ$„ă(ĺ”Vr@bRRB\¸‚„ň …‚ç­;)ÇqTžˇ9Ú9¦yN±„Ş#¤ĺMŻěůL(ĺ(ŻT*yÇq\G‚ĺY' zŕ¬Ůsî7TĄčDî˝ů¦Ď€=>ü`âĐßĆ=ë×ů„Ç)|Wţ=1eS´­ @:bll‡ěä;N @Ţ9Ýé1ěĐ !Űâ‡íOJü4ÜÖGó*\áď0do˘÷m ¦;)Sđ\’˛!’ł±Ü' “ç¶Ŕwđ7Ĺ,łB­NÚ™2Ó ťń“˙ôбY÷Ě;»pÎ×Ăj­Š¶~FlV L>¶h^ꬉal˝GS†Ţ~ĎüyĎEřâ6nůdfűęGŰj®U•”•lűč5;@¶{śĂĆćWéîž0ĺž:1/3łş¦nß·ďxr·„k]¶~ť÷]=˛›cŘČąYEW‹ óó Š*‹rŽŤp÷ę—K«jł/gŐëŞ]: V¸­ę'ť4hů‘ŕŐŢŻ|řifImÖo›×0×× Ů«~Ńę÷t şěK™•5•ż~üş§Ő=}7Ą–>ńiÁĺ}j@µűbţgkď@ůÎüÓÜ)MÄžÔś˛â‚ü¢˘‚‚Ľ‚ŇÜł{ŁíŐU´őĄé:ćţěů›ë—ňí„޶.‡F˝ď_ë ÓvŮvőv‘HMhZAő›÷ß…fâë­áÜńýěňŞ‚ôQ%ŽăřDŹĘ(o:µőc ¨Ă'_(¨,ĚĎ+®¨>ŕMßĹoąaYôXŞ}ÍŰ?zÝ´˙lÁ|ü†-Ű„‚ĘşGg  R*Ś[üH…®!?;«Ľ˛úôľźbüśp¶a7'‹ŰËźżtĐ:[Ůk]Ű*‹ŹßÔęÉףuťXČK…ź>¶JĎ-G3t•e—łŻT]¸#L€ëu”š.-ä™=‘¶JXmb{;ţŰ~×ÉŽ…rÔnĂ÷ěŮ3:ČöÉňˇFĄŅ•uWrs«uuüđ®WŰż7ßôhRÜFžě«vťî;îŇ`Żá¶°ľ !rÜ-çëccß O٧ŐPĽ5Ɔ jMŇ–Ţ#l¸Nîç­ržę1lwĽťš(ýíRŽ$‡-q@•€ßSC”o†”ž6Ţ Ľü—zą&Űu&€B•¸=1dŽ z± şµ 7çr·wâGˇĹ]Mł°Ľ üMxGÖČ äšż|ńü~vÂău+—,ôu†ŐĘXVt­“ß/ÓîüjěÔąS#y €'č?ÇO,\¶hŰř! śÓďłďľÇĎŔÄĐQnÚ1a fÄňÄŐÁ=méâ×âBĐĽáY™2öܬ ň W?­ĂŇGúGOđî×I,wÚťsç>€ëŚUuó łb|děÎÉ#ţš1Đd­_š<ňřô1r¸ŰAˇl›Â&ާ—Ś÷&‡L]ö"cŇňÉńTJŔl/O˙] „& _±d®-?ů‰†¦†ĺcŃ<Ůߎ֯«ćľŻG©V9zúýëŰĂG~|-ŔÓ…#pťPZgţß›«$ ťś’…ZM@#§Î}úŮgď_<ËÝA@í´ěáµĎ?űäŔč@ žxěÝť{żâ€»_äCŹ®}ęÉ'¦ŽK‘kę¦ňě{ďÚĎKrşhÇ-~FojZ=w$ľëČ3ÍĄľ(ĚúCžł÷e|ńärÜÜ®¦ąçĽOp°‡OŕË߼üÇ÷ţž>AÁ^VžŞ[Ą-·µuËKĄ˙‰śŠ·š>]Ë€|S»ňŤ 9'~¶XČë=! »éGŹźÚşqkavZśF€§Pţç§Ʀ¦Ă?ĘŞđi™99łRBí\<Ľśśú¬ź7,‹KuV3[4< _Ę|3·`–ÝźË.|`ęÓ.›0qÉŞ«Ĺ™4|ż°aEu†˙®]*×y3˛¸­üůKAG­ëo§1©Ť,–Ž †×¸ś+y˝×şŽň@nń»÷Íž˛`]Cc~¤Ż ś=onŚ—ĽOč†D@ĺ2sćtG Š»fÍŚ ńqóőóň j±Á!>\ý"X˝ćÉÇVFşËĄý#b'O—4~ÚłĎ<5ul2ż¸É“Ç[0cPµĚ)1A4ޡ“ďşsܤ™O?űÔŁ„E%­zîâ˘â7ź¸Ńâ…áľ.°Ň*ů˙ČńcýÁdzpJą÷曾 Ŕ!ÄN(¬Ż,‡ç ±‰iý4…`qŚ`¤]ęâôâCuDiq# ­0-2DŁäq_@ÜK*L`PrŁťťěY˝ą©Ü@21âˇőś`WöM™™Ř«#ß ÷ťîä4Ü9h•7§˛TŐNü0€IDATä\ ­R-Ő_®k!©Żôíł·ëŁŤ¨żTĎąÚh5íĎ`°s?]]ĺÎ2?/*WP®9ż îLiGČ­Đ*ĺä„#0Âśřäő#“Ý 1Š8Er€ßŹ~&Ł!żˇ€I”8­ŰÂ`÷Ň3Ş8•Ý»cG?î;Ü7ŕ©Ä­ĽoË^G—~ĽtľŞč9[đ-g˘üŻ/ź˝]ČůÓ++4‡H…ĺőÂ-J$ĂăRj˛Źţ\Ä-[ľ@’ ýĂâM"ď7řţąĂ?ó­ ďĚŃĎÖojĘĘóőć›l·ÇÖĄë~}_Źd2ęĘ k“^—_V-2Śľc¤ŤţĘţů_©Gw=u€$±–ŰŮďo˙ú|Ç·O5ęą·ţłhć(¨}~ÚńëÚĄw%ŤšşuëOăâüÍŔ©UśĽ%b"±übăćÇ—Ý5vĘěW^~Ęކ¬ÓĚ,rVË/ ‚`,©nŘ»÷ŹkzIëd@ŽiĐm©Ö‡ţ’ÔúĐ»‹R=`Z ĺůąĺĹWjt&&ŠËŠórË˙,Oţ[Ą-··uůżzŃĽźÝ˝ckB°3€ţ#îظý· źĽ>gÁ˛7Ţ|@TĘřMŰvořäő9‹–˙ó•5N¶n{ré]I٦nŰöóĐ@{·°ÁßoÚţńŰĎÍ^üট7Ć9©bÇ<8Ľ`6 ťąô±•÷Fři`µcڤşŕŇľCŮŃQ± ü«Ę‹*««ĐěCş˙¦o×r–7vć:Áw…ŻĘ ŻąÔ$?cí˘“I ĺ­f;±=†"č%ŻAw;f,żPvÎH”JÎk‰wĐCž …T_hÉ+ä>Ë]ˇÓ—îÔ Î6v!ĘĘ-%gď»pfU¶$ŻŽ:0AíÎS ˇ^ěôŻ˙˛Îőá©Ę«Ë—%׌¦ĺĽ„šCUuęćsŃ6rf%Ą”X ·ťÖĺé¸Ŕ7Žť<['¨‰$” A%$?éµâ—?\­Wqá” ăâžíĆ gęäĘfF„9jľÎ) R;Ć;kw\>?ç—łvl’:Ťŕ§U* «3™ţlŽţuˇŢ$RĘyjŰď]o5ŞšÚGÖ˝čŁ?ż75G­˛śÍĎ_±Â©ęÂ'ßî@@Tú˝őöëM9ű¶˙–ŕąýµm]yĂ7}XŹ<ě8J©ezw˛ńŞÓfÔ ĺ¸ćÜ۲ë1ˇśÄ öűŘwż˙čÂŃăÇÇóŐÖÓîyp\Ź ź~ôÉGŐۇ®]>ŰR»ŐÄ`ă“ăżĺóWĆŹ2fŇ‚FŁ%ŽPG§ffĺp* Ž^±»vý–~d㵌˝[¶ď {'wSJnÜňě—´ű¦c©0Öü’W „kv$}ł0ën•¶Ü–Öevú8©¨T««ů3řĂÔęš8…Ú;€ĂŤÚ& TJžËĄ‡˝˙ŔW×Î{îŃGO_ŃŰpf3@%Ă…3'3ň*’ç=¸cĎ/ń.ڱ>Ľ˝ż1Yt^Şíhjýf˙™lĄB©ŇY[Z€úĂý˙řŕĺŚúŐ±<•JETË×<űęËkÜ•úSŮr]7/‹ŰÄźż Xk]s'¬{‘­V)!ŇĎŢÖµ\H°łç~ŢüÁţsş Ű÷•}őďçűőîĚŤ ő"@ęőF“%X‚l!eÇŐiÓ—ôƧÄH«w‹ľwĆ&Q4×]]3űÎţ1aç= Ŕ,LuW×ĚžÚ?*,eň“§/›8ĐëűĎZçEŁNĐ×®»oŇ€„W9÷ŃţöĂëC§?idâ?WŚOL÷˱bÖ#e‡śČ‘Řľç»7ž,Ë8ľăx1@$©uéň›ľßŐ0ł¨ ´÷N±É{żH%ČrâŰű©396—•gK cPPm¸1 ú3ä·[ ¦K÷Ą›p®ň ľÇ 5ľw9_ýˇÜ €póëK·Ôz>:tĎ€{Ýi‹"ˇQ’$F”­M˙?h„™X'Çi˙WIÝÝ—ú8üëTZ-8•eéEÚ¸ď[…Y”=ęćF'©D•Öq«Vmc?Ă×™c˘Dů(7N4ĺš DZ15¬ŢţKŇw[ŐŃuq! v\ć·=ăb±ŽB_[ńuféÜ#Î/śőB˙H•PËŠ<™^ťISüY/Qţż…(IŤ&†Űą»—LM^#šůʧźŐ•šW î1Ź/™ľéĂŠŕx^Ě&Aóć—?NŤf-^U!ޢěí[żŃü\}YOëĹv3©I©¶·§–‘iŮď"ÇÇ\pBB_ńÇÁ}Ń^®®©ó Ť®­¬:oĹłk®Î͸TZ*ż6GŔ…>óđ'źďYň܆˛Ňü·ţľJmIŮNĺš­h’۲PK)gjŞŮůŰ.]ŁÉ\WZTŇŇ})0ÂD‰R€r˘Ä(ł4Pt^Ş[L[hŢجÉj\Ż^JůOl]^×ÔIR¨ţ”sq€ă‰h67Ôt>Ëôş90á8`éę—Âś jßčŃüŐ®A‹îbČúíî»îzčŃŐC†Ď¨ŃřO?Ík¦>€“EçĄ,#…ë쵊W0cŁWx3r)‘)5q Cxõ̪ Ż”j‹–Śŕąů˛âő'–ȵÝYÜţüÁJëdŰ+™ô-˝¨a [Ť=«>=ű´ÎĘB ‚¨TÚę gŚ4aęÝ/~ĽyÚO?vďtô&ˤĽú"e -@šŹť%°W»T•fTµWN]*tUŮŕ•6WK.í>— $/€Z%c˛ ¤zÝ5gďІꪡs[攍˝¦ľşě|ZĚzYTŮą`´łA±~T’0J$ÉÄ`(hlr×jL ÷# _ä`LŻÝ7đ»MĎś+;ĹËťEnąV×`ďiď(wëĎćë_ dvkí)3ĺ$Üž—ň;·˛Ú §őo=˝ű‚©wUC€Ůó ¤ů˙ٰ $‘OżóáÂŃNcĆLHÍ*§”J7ý„¨łÖ˝*ęŞë™·űľůÉd0é-\8›}ÎÉ=b|rH9^ˇTLžĽűĐéŻŢ~’g"€«ĹŐĽŤ˝OhŔ§ĐTŰhŞ»2¨Á))ŁG_űöŹ’ś¨ Ń$&L˘„čÖ­žîçă÷đç=öĚÝc§XjV <€€äŚ”«Ż*xëťwć-x4`Ô˛GŚXPĘ”®JńĹoŁb€$©x*PLž´«Ë¶:ĂtrRiáž(Š-!\ĺ śŰ}Ł-7˝m]b*łŻ‰­wH ú2V€,„„Q—Ye„eˇu`$“ŮlEşkW.äTÝ˙Đń‘vĽcŕĚ;Ç)ĺś™E&±>› nXK]m¬Ô¤žŞ“¤TÚ8آš'0›-/›d )ź&‰Ô¨µwµ'q ôr¨üvI$&Ż™ĺ= “$ †R†:ë9eŘÓî’ZI0„J˘(2ËąJ©´µ× ‹7cŻ`‚X__őű¶MF>"<@Ŕŕ6vžoűMxH¨LV_Kš¸Ů$íJđzHÔŰ1#~ëŻőâŘŤt›72ĺËf˙x \ś´1~äѤÄ/"}Ć;ü{ &Ę{Ś= z8Ů{ u×Oő‡Ë]ŁĎé˙fh˙ĎbFĄ&Ďw¸[#Wą–čĎ\Ó€ő± Ý×…ŤŘ›ŕäËŁĂ(˙ţa\Â:_ {Ç«ŰŔ9ZŔlď¤OţŞŃ€„Ú?~ťĐž?ňäG5Ž{ćĎŰ6vřĆOÉ^8-ĆN Î?ĽúáU§¦Ž˝™@ąµC†î™53o邝S', ň`ĂSŹŹşóÂĚQ ĽËŮ{-őuPŮş^Z~߇IcĂ"ËV,Ý0näwS¦/_ül´/ Ř6{Ţ{‰!hŽ-¦uöŮ6íÎ51áĎ•łhć0{U2ĺ×xcŇÔÍĂdşű’‡ˇ&DĆţ6eä_3Z€lµŢś2ýÇáń·Ź@ůMőŠN+¨<üĂúő›Ź×–fÄy+ŹĂ+ľ|e …J ŕîŻ2Ć.ź9úí7?=°gÎI¸éPł[ו\óŐâ:ŁGôe=˛7ŞOě„·îÎĚ//ĎĎüíçŤc}űO÷¤ëĘrÖ­}rďńÔ'WÎ8c%cěʡo-#věH«-Ë}főă[w{řž±jĎäĽjÓ‰źÖ?˛ę±C§ŇV/›"7aŃSu 5;·o^5w¨6lÔáS©/<öŔż>ý±®<{|x?§=ŘZ3§?u…ڱĺŕˇç~l¨<ă €·ż;e,:íDMYŃU) AĂf_ŐK›ż|˙ťĎľ›ŞŽ‹8˝Ë¶:Çtr‚Ad_÷¬O­Hű‰ëü›>‚[Ą-·ąuyĹ‚ŹwťŮ÷éË@űňŚ\^pźěËŘóéó7Ú:…Í ď~žšžÝPŻ;y`ĎĂł'Đh”Ö}}şúÜF‹ű ăôţç×>ŕBᵢóńA.ř‹ËŕÚ–Jp0ŕ®äQ &ň71­vµěb¬;׳EUÍ–öbŚ'…"®´¦i夵oR•™}óĎU“—®–۵qĂÎýÇM‚áµÇäřĘ›“ĹmĺĎ_ :hݡ˝«¦Ź‚Ö˙lÉ5ą ý4tÎCď\LýŁ÷Z'űĂmµĚ¬›ď8rŃcWË®ĽńüÚOÖ˙* ő«gŔu}W#Ç`O•Ţüű†Ď?űj7clßzX÷MjEÚOň#?ݍI…5ćm_˙çß_ţ$Ô•Î ŕîG_¬*9+y^ˇ0㑚1€wÔř¬Š&yN9’vţţ»űŤZj¨+ďŻ1ĺŤâóŹĚ Đ˝\‘qâ÷żü|J”·uß-ŃŢűOfĆ©—ź{á—Ĺú˘YĂBa5ďČÜűx›of Dó"°Ď€]jŁ]˛‚šs^˝R›gŔf*5TŐ5ädŰńS›JôŐGjMő˘ţRľÜl 1[0M™Ťúr“dę3 %Ý%§@cFŁľÄdŞLe†ňŤeE?ëÜćřŤSe>—gl„ŵÂČ8GŢ1ÉAĄ”ň˙“_ťaě$˙/%`0Ôł€˝ëU6U‹„öÝ•«Üş&ĘŢ1XQö˵żÜ‰%`°Mp ˝Ç9÷Ő<ý5‘t` %D2Ž”WĹz{9‘¦ź8©Ó$±˘ˇnqIzM˝ś„É×Áľşľć÷˘˛J“éJUuVC“ü(Ř,‰WŞ«Ď\«Đd6śŻŞ.®Ż;^U§áYYť]ßPi0V4Ôýx)ăă¬âIŃVj˙~ŕH™Ů"LQlÔŞd/Ow*ĽsúäžĘ€´Ď NŠLěń±é…ąůMfJHźq›ÂŔ‚ÜÜSmľË.ţb»J„{żç÷ĘácąMfîv1‡QJ¤ş«ű¦&ŚďBËž|xőáKĺłVżń·™+—./©cr*4ĄŇ&?7ýLzV]˝®´´8íLjaiŐ §ĎëŞő5®:zą”PĘ®ď"¨ďę‘óŽŮ89úy8¦Ů{řtş®¦:í\ZiUŐî;Ś*çń†•ť?ţÍ÷?•W׊fcyyůî];Îdä2Băď;w•ŽăîQ›szæíĹů—~ŰwĘ{`â°ÄŘË©6nţőju%ČÉ8™UXĄPŞ*rŇO§eiÜ˝‡Źí©ŇżřŹG·źČ –š€‰‚±#1ͦÜËgŽž<'1dgź«ihĘÍ8\v­©¦Şr×®_;–J»Ë@j /žHĎNLá©_]÷Ô÷;N1Ŕlę®­.0ť1ĚĆËgOś:ź B•JĄuPçć„<7‰áyž“ Ü~mą˝:O8ž1éJŤyÝ OĄý˛>·˘ž»Eo>{J9‰±ča‹ţýÄŚ§V>YQÇÝhNwZ•—ţ뮽嵺ě3©— JĺÇůfł!ë|ęÉł—Ms IHĽzf˙«˙v2«ô¦ Ëm–ĹŐV_i]ęđĄb‚ąy4eä‚R©®˘ĺ›'xčhöUč«ö8mÁ¬\u,«’ăICýŐ´ÔÔ‚ĽĚCgŻŘ*XĆ©´Ků…W««Ę łżřŕ­ŹľůEXôđ·@·Ť?9°Ö:]Mnúů‹Ů÷LM;Î…–­yđáÔ‚«ŚP—€ŕŢkcŚ5E—Z-ä?ţľi˙Ecc˝Čiâ‡8pş×ž˙Ç˙mŢÇşő'”2&y„¸+%öĘ•jŽćţ¸u/üţ{ťŃdĽtöÄ©ó™„úĘě}'2†ŚăŁžYóĐćĂłą(7ëäés’%1)“„ Ł”Ô]ÍýmßiźÄÄa‰±—ÓlÜôëŐšz]Yţѧ%ˇ±ˇęČÁĹĺ5’IwěX†ČĄŠK?{Ş¤ŞžVóššš4ޱńń¦«g×=ůŘ–—`5ď¤eäJż9‰>ąVü´ü§ťăv7żö˛VÖ9Ňa„›­­©tg-Đvľę=5GIż‰®M—këóLín´,± ĺ^˘Íł!-ßP@`¬ĄJŽ©íŕ „P+{‹ícZ€6ę#›u Ýś[ôdnČĚşÄŰááC§…ÚÎ÷ ô a„ă¨hqx°ÂI)G9JD“Y"”Ü2 EĄD%”ŁLşŃ©ëO®‡PŽ2Il»iˇĎA’šťI@Ą”˘H9NžÜ)ĺ(IÇ3&Z{+ń<Ď$JڧT”Dy˛E©«šŰaZ€ň-ö»Ąşi˝#{ÁŻő™1uŚškÝwĘHÖÝż ˇ3ě˙uK‘Nči& žłĽ†¸Óm÷L襎BoŐĆňF)˝žĂWB8BÄ6Ë}ÂqTDČf$QÇQ”ZĚŽ|˙ٲ¤ĆÍťˇXLm9S$Tţ™bÁÉď2cň2^YÝÖl-‹ŽúC(µlŐZ€RN.B›ŮŇM[”ă)(ĚŞ8¬ÔűÖ_a‘N¸aÝ_ôň”˛ÜAA×g"×ţŮ\çućxaťŘ%ŽăÁD±y»uKNî8žgÖ[˛Î»N™$yFŽ|zŮÄ××=[b`biŮDu3Ś1^ëĽy°/OÚZRĂ"ŢZ“ÝÍH/CÜÜG8Ű|•™˙'Çcęň¦+%(⥟{ů˝čzKŻ»9HŚF$äyáıóy…”ç)(ŚP˙T•”UËTůDÄ:ékŇ ‹oŐů6B5tä;޸oßÓ JŁŻę!S¨ś˘˘Cx()'ăBŤÉ †€°„!bJłN>•)˛^ ŤĘq”H"%‘Sô[ńđ2W É»pňűź÷Ę’—ç-ŚI˝X{ĆC?Ż`oŽşň’śÂ’ž¶–RNýĽýݵçÓ3%Đ€čXéZaaYÍ-XK><*Ú^ŁE `§07U§_Ěí©+·ä—„ý<ü\ís/e }{¤Ń]•×Đ ßţ{·ýďżkmŘ{ăhŹŇż% 'ŹšłęÝGďś1zRľ©×÷'„€1u?W»ÜKŮkí©–3îß˙‡Á$Éędý  JHŚ i¨)ŢŕŃ|ă}”‡\«Ť”ŽŢĆ çš®îúýŁYęHOk©ČX§ĆÎ-[W˛°Ö++ęqáÄńóy…ťZČÖ¶ôşô‚"˘p]zהּ˝›ĎĺV) ŃlćúE†ř+)LúşËY9f’$^—,,cŮÝ'Řß› fůĽĄ8;«×†űIfłĽ˙*ĎĎ+ŞŞˇ őĐ‘Ł›ąÁćś<(±±˛p˙ˇ“&A¤ ű¨¨áŤú˛  ĄŽ–­Ü;áĺ"ăF7Ô”üщ” ŔďştYwÜ0š{´d–îwß‹ŚK…˘•Ł÷x+Ýč•‚Q>"*Z«V’¤P*ŠósëĚ4,(€Ł¨-땥íH.G Ůl!A9N4Ö]¸if”ç9ÂŘĽ‘oŇĺ´ZE©Lóś"Š’$I Dž>ä RQ’Ą”ă©ÄD&Aę°ićaČŕäĆʂýO©á=Ó©nÜzpšč>üXňźă‡”đ/%µÉŞ‚í“÷&'ľŘą§đ]8ř÷Ä”MѶ6±±˛ď8-9éŤÓťĂ ˛-~Řţ¤ÄOĂm=x4;ß+ü†ěMôŁmÁt«7ŕą:$eC¤śŐÚ}®˙°ĂIC¶' ţ9~äńAłś`«Š˙!aŘ®C˙HŚŢź—)|¨™Bu‡,.2˘w4p[ŕ;ř›yóe›â–˛Đí Ă Jx#@EÚs[Ľ§&ţŰx …/đllSnžcňŹjuŇÎÄ™NťňSn˙éˇcłî™wvᜯ‡ Ô˘ ŤňY¬BăđŐ]Ó.,śťşpîáiăCm¨Răř㌙é î>ĎĽG$ŮŕťS/Ľ?Đ€’ن‡F^X˛đÔÜ™é‹ç˙6et„­Íą,ü4ł¤6ëŔ·J™É×Ů Ů×sŃę÷t şěK™•5•ż~üşg—y­Ú”Zúħ—÷É™łw_Ě˙lí˝(ă*,ťŇDěIÍ)+.Č/,**(Č+(Í=»7Ú^ PE[?™[=1LÎśăţř‹ďg—W¤ŚŇ(Ńwá z««rřÔűţµľ0m—mçńWo‰Ń„¦Tży˙]¸5ą#WmĺÚ–žn˙ču;ĽÇă/} cg ęŽ{ +ë®äćVëęţřá]ŻŢeüč„9’Gô¨Śň¦S[?˘€Ö?î·ÓYĺEůeŐU{>Ű ´˙lÇyŽGO–­Ť,(PŢş§rŠN¬h;ĄjCá–Ź8€hTŐ=:k•R `ÜâG*t ůŮYĺ•Ő§÷ýăç €ł ë˝,ät'sW˝UY{íJnNVfV“ˇnÍ‚©“毻ÖX'ck_yx6€×?űUćĆŻźľE€Ń ŞilĘË̬®©Ű˙ý{ýeř¤ŚÂĘÂüĽâŠęó~Đ´Ź®ÓQîíąQ–y2»°¸ŞK)Ë*Çiâó+»ĺ†Ż#şŞÇ»`嵆.{‘~hŁ °őŤmŃŤßţď-Çž”N¦Pi}čBaYiŃ•üü:}Ó§Ď.[°üťľˇ÷–¶Ój;±göDÚ¶±K˝˙mżëXJVBµŰđ={öڞéČĎŽ<Ü÷í;žmĺŐ›oúÎŇjb켦şp€v×ČĽ†ĘKJövô¨ÉŢ lż€Č±Ëś®ŹŤ}32vçäÍhň^uiňČăÓÇČ‘¸o…r\˙ˇs*ëkźZ :~ ‹Z€ç€'>Ř^zî7WËçĘ˙ütŔŘÔtxËÇ*™i7G“ &lâ}zÉxorČÔe/2&-źŹćeÄ_¶ĄZĺčé÷ŻoůńµOŽŔ9tBiťůo®4trJRzÔjů8É5pŮĂkźöÉŃ<đŘ»;3ö~Ĺ wżČ‡]űÔ“OL—"—ě¦VyrďÚĎKrşhÇ-~FojZ=w$ľëČ<ÍĄľ(ĚúCŢŐěË,řâÉ帹]MsOyźŕ`źŔ—ż9xůŹďý=}‚‚˝ţ¬Ńërc|mBÎn0š–Ś qK™gbB+ç§Č8ŐÝX¶Ž˛`ߡ§Ăćv´˘¤k oýDŔ&.írţS¶pŮ„¤€‰KV]-Πáű… +Ş3üwíRąpďe!·Şrtމőót›ůŔ]uů„0W¨í˘ăâdL­®r”·ŇsÔb#3·HgŃč0ĎČ+—Í·â'?ŃhlşwTĽĆĺ\Éť•jçâáĺäÔN »’»57b’88ú†w#eË®Ć&&µÜŕş¶HDN 9hřýKćtŐ _7 Ö|ÔXcŃŤ:}Óӻ׺ć}WÜą¬‚•s’9;?€,]óYIΑŢ[Z‹Ů÷™9sş#Ĺ]łfF†ř¸ůúyůµXČŕ®~‘¬^óäc+ŁÝĺŇţ±“'ŤK?íŮgžš:6€_DÜäÉă-1¨Ü‚,sJLŤwčä»î7ićÓĎ>uÇŔ(aQI«žű ¸¨řÍ'î_´xa¸Ż‹µVuäaCSĂň‰±mäŐ«oúÂŇRú őĄż^S•Albňc0·9ľÎ>¤lWoŰ, ‚ÖWO€T K]ś^|¨Ž(I‹· á %Źűâ^ Pń`’síě”`ĎęÍMĺ&’‰­ç»˛oĘĚ Ä^ůf¸ďt'§áÎA«Ľ9Uë ´9h«TKő—ëd„T/4ĺę •fűdgăĹku×$•«Ş)»^_!5e76éDűP;”ëN/yýČdwBŚ˘N‘ŕ7ÂŁźÉhČoh`%Në¶0Řý‡ôŚjNe÷îŘŃ„ű÷ x*1FËuŤŁK?^:_U Ź‚ű´Ą€ÜÎ;›9:~ze…Fă© ¸]»=xD}i–*rňO›7FşÓj‘PNď7řţąĂż|űť*Ŕ]Ľ<>HńŐ÷µö¶Vš~­ Ŕ𸔚ěŁ?qË–/$C˙°xŇőÄĽďűz$“QWVXÓ ôşü˛j‘aô#môWţóĎ˙H=şóč©‹$‰uĚź"cˇ`Ś÷ŚÚ¸ý—µKďJ5uëÖźĆĹů›€S«8yKÄDbřĹĆÍŹ/»kě”ŮŻĽü”˝ X§™Y!¤EMÁXRݰwď×ô’ÖɰřŇu[Şő)‚$Y;×vŢ‹î1­Ŕ„ňüÜňâ+5:“ ĹeĹyąĺV%&‰š˛vĄ Núŕ× •Zb×ýl䆡·:&_čDôĐRĎ€ľőü‘Ŕ8›zŘÖ+"N«hž=®«€`xěĐÖž˛¦±ĂP¸{PŇŔ÷eÎK€Şü‹űeGGĹ& đŻ*/ެ®ÂŤ¸O‰µÚ(;{ 'G÷‚ G.5大ćébĆF$Uçkáü€ČÓW˝Úťeë Ž}‡žv´˘rŇź–qŃB-!<C“~É3ďoýę}/žÍTvĂfL?lQw5§°P¦˘÷˛‰7ęj˛3Ň Ë*ŤťUşuoV úËéçeLIÚ–?JLŤ©Ę:ŇĚŤ¦äţ)e—R?ýć§F ¬<ŻAoʨĆyőÓŻĽ¶ć!˙~vbŰMUWr·ćFD?ÔŠĐeîď(ĺvvĐŢpŁ»He’ŕŇÉC˙·~s˝Xŕf ŔĹÉł âą×“ÂbŃ‹ů‚€E60qřť“&OĽct?ŤÂdf˘hčhi»ů$1tĐ„O>|ןřŕ>űßĚ *‹ K óZ,dnN1ď˝qÇÎçW/š÷ŕł»wlMvĐÄ·˙¶á“×ç,XöĆ›ĎJżiŰî źĽ>gŃňľ˛Ć) bĂÖmO.˝+iÔÔmŰ~hď6řűMŰ?~űąŮ‹ÜôóĆ8'U@ěçŹ̦ˇ3—>¶ňŢ? ¬.(;ň°Qćˇe4X>ô⛾°´-ś"ä ?©¸®ňžşŰŢă’÷|?ŔŰÉ»Y_Ú©ŤěSŷ΄A/y­P{©2–_¨8gt Pr^KĽ9gĄ‚ę -y…Ügą+túŇť:ÔŮĆ.DYö^~î÷:…'™;k P»ó”B¨-Ą„IŚ÷±w¤){§€ąşÉ&ÚÝ%Ţ–ů:ٸňµ&KÖ˘cG(zIł•0¦čý[_eý‘Ęęt“ĺŃh—m€+­(tăt&›çÍsŚ B˝DxŞň"ő9¬SŻ‹kFÓň ^B͡*A͵±Ő˛QČ,-˝Đ÷ωw4‚o¬-?\^“âÔŕë>ÖÝY´÷ Đ(Ź‹@Ě⢄d;ŰżěŘrµ~ŚŁá” ăâ´j[gjúó{S›ÔĘľčĂzä ZŽRJä`%N6^uşÂŚzˇÇ$Ů?˛ă,c8ަĎZrÇĎ>ňjZ^˙čÓµËg˙ľú-´ ”dă“ă˙ŐÓŹ?ůţo.޶ŤFŇ• ŻuxÁlpôŠÝµë·~áŃ×2önŮľ€$J]ŃÓ:75ë=JץşĂXóË’J›ŁDľË&-§+0Q Rň”ŕöśt=čř>ĐÇIEĄZ]ÍźÂ!µş&NˇöŕR/{cÚrŐғ֞žnR«,) ­8OXäȉ>y;,ŔýÜoßí8^ \_4B©(HZżV56LPRz9(éţÉCă…ŕi!ýś¶ŞiĄgß#ŚÄ6đ•5sź[2Ł+ËÖ©,:ö´S+Úâ Ő‘ÂŃÁf3@)­o4Ü˙ŹüüŚúý±Ľ‰*˘rXľćYű~~.\Ő©ěąţ땡T”D§ Ń‹ĆĆ|öŘs" Prf“óĹš(8Z]Łk§‡ *č÷ÖŰŻ˛÷m˙#_z>őDi^EňĽ,ś>zÄčłůŐ”ë°=rcé]#ţ÷ëůč1“żýď›m¤Ü'Ă‘’ú†žąŃă(ĺ<1›¤N{ńŔâ%3űGÄĺäť]ű°E7ĽśŽĚč“DÇĎ\6p"óp¤%iGęť}úw´´=P(ęE€6ÔëŤ&KnbŮBň˘„iÓ—ô†DĆgĂ3sOÝ;cĘ™·Ö›DŃ\wuÍě;·¤ćř‡…0 &SÝŐ5ł§nI͵stĽkéSz˝öč?Ó*ńć§_¬]>{ÍĎW}íşű&}Ö#7oĎč‰ßűţőˇg/ťÚńö?WŚ˙%CĎQ ]ÄHk6ĺěŰţ[:±m\“Ţ|s»--ŔDĆDňR¨k .:tĎ€{Ýi˘%IbD~źŮţŘe‚ g4TěŻpuGEÉA}ä{‘Áó…|óŰŠBŇŰ­µ =ĐĽŘňŤ´é3ôiŐ©óÎťý[ľ2ÜÁsśťĽÇc]´%G©ÜYQr°±…B˘ cü­ŕeE&2±ŽYúŐ$Itwt_ęăđŻSiµŕT–ÂSĘ"_PŚ O´çßłwáo$[çUá>{.e|S¤ű஠ëbĽuMF9 ăř(7N4ĺš Ç{15¬ŢţKŇw[ŐŃuq! v\ć·=ăb±ŽB_[ńuféÜ#Î/śőB˙H•%äš•"t&Q`Lńg˝Dů˙p˘$5šnç P©Ň*.=0oÉKャ7`$ęóř’é›>üwîyäĄ0'Ú7zô›~ˇó§¦Üš4ŕ’©É+bÄC#˙ńôł:˘Rs7řҦ/ëi˝Ćnž`©I©¶·§–‘i ůBŻP´yŐ`ÁpDxú‡ŐUU ť·âŮ5Wçf\*-µ,ú8"ßÉ }ćáO>ßłäą eĄůoý}•Z˛Ä@ëŞfů7J9SSÍÎßvéMćşŇ˘’F€0îK&J””%Fĺׄ(:/Ő-¦-4G˘jÖä?5®W‹™$µN0}=ę|KVSo$IˇúSŇAODłąˇ¦óY¦‹B®ĘL•LúN{jĹyFË3yöcž cîť5 `ôzrůq„XşşŐF©\BMNüů»?ţĺÂ×Űăщ5×*©ŇF2¶ŇSO•“ňŢG^ s2ÚtmŮ:—E‡ž*U6m¬hÂpŚr …‚縎Úô ť?9—š$…6.ao¸–YU€ă•RmŃ’‘Ľ#7_VĽţÄKŻS”0“çÜ­1ć}·}Hb+ć—˝DC§µtx€Á¨yó˧F3=XyűfÝ5őˇGW>ŁFă?qü`4‡îFîí¸1&n(€K‡6µ“2Çńmě™Ň¶Gn=ŚXÂDłIč˛U*żąłÇěüü˝Ďvd|˝í÷7ťx­˛’(UčHŚhěµ'$ÄE‡F%ü’šëdoŰÔPÝŢŇö&2Ŕ@HK>ëćPąŘ«]ŞJł ŞÚ+§.şŞěđJ›«%—vźËP’—@­’1ŮR˝îšłwhCuŐĐą-sJ‰Ć^S_]v>­ f˝Á,Şě\0ž2Âä7‘ťÝ˛·áá¬Ĺ«*$PÚ2p˝ř¦“‘r;,-•đ"Đ=‘?s˙Ĺş<Agl(Ľćxh9b§vĺ@ë˙elä߼Ú,FE&™-©DZś—s*Ëą¸W ‰Ś r4łˇÂ¬ôTp»Ă]MŚĹ»Şa™Č„Ľ×3ŹNHËúŞÚ}ˇ·[’ @›K?ćB##śĘW#‹ź‰ „÷¸Ăąn˙5}%¨3f?yńČÄ´ó/H€ášĺ–C’)d–ąSëdéEó”ÚÍîťL „#™Ř(Ô¬6›‰Ćצ 7şj«Ţýä%™BơÚžJ·€c" ńRCôşë]¦&gµÍ¦ó©ÔHŃöš*ł€_?ď Sď|pŚ‚0ţZłąá×’«gJ‹ł Rś=`|ć÷ßâľŰ¸đĐ)¤ © Z"ľxřĐÎĹúQI Ŕ(A$c€ˇ ±É]«09(ÜŹ4|‘[ ÓL`zýčľßmzć\ÁôŘŘ)^¶čě1嵺3xO{ÇVÝ˙Đ 2;‚µö”™r ň©˙­oEžĽ«›® ˘lH™Y”ä'őłć=Hó˙ła“LŽîZţ…śŞűz0>ŇŽwśyçX¸I$ąő˛Ú §őo=˝ű‚©WE]r=›¸ľŻGţE2LzËjýlö9'÷ńÉ!`"ĺx…R 0yňîC§żzűIĘhÁP0Ô5šę®Ěź0jpJĘč‘Ă׾ýŁ$‡:k4I„I“(!şu«§űůř=üĆĆyŹ=s÷Ř Á)]× ůl«Ż*xëťwć-x4`Ô˛GŚXPĘ”®JńĹoŁb€$©x*PLž´«Ë¶:Ăt˘ŘbU~}s[ ÷7 ’Él2÷áă®Ţę|4–}ĹLl˝CbĐ—±@~<…đ€P"ę2«Ś°,´z2Wĺ¸nĄťő€`ĹyŽW0A¬ŻŻú}ۦ # ´[ôÄUĆÚŮ(ÎŢć´ÉŠÚ˘GgŹőôő™r˙ßLLQX••ŰPílk·ţ­§w_0÷ô¬6Ő_Ő^Ě©ZŢŤeëZÖ=­nޱ¶˘˛U@óŘá%ĐÚš6VtÖ”q„‰δvĹ=[Šě·}ý®0’ L‚T^)*w÷őW¸.Y"Ѝó˘yÓŽoŮ­ Ç›±sąRP˘k•N|÷ŐşJ˙xďĂ…ŁťĆŚ™šUN)a`ňs#fY·YÝpĂ"&±UĘá·±Z#¶śqÍŠĹÝp—%Bşé:cŔÓďtÝ Q§Şť3ÖĂĎgĘý33îJE>Úó,+‰˘(Š€Ů,ici™;`´§35‰IJĄŤ- ŞyłŮ˛‚•-¤|š$RŁÖŢŐžÄ1ĐËU ňŰ%Q<8ä= “$ †R´źS†=ýá.Iˇ•“WE‘YÎĹTJĄ­˝¦yřôŔCžç%‰5Űůµ XlD×ßXćkݸ]–Öm–ďř‚á7ÄD˝:đóČ~C´8đJě°ďCe*´#ÝÇćŤLůŇâ1 \ś´1~äѤÄ/˘ĽÇŘꡇ“˝‡ŮPwíđ´ˇQŹx¸Üĺ1úüţo†ö˙,fTjrđ|'€°%1r•ÂËQś¬Ź \čĽ.lÄŢ'_ć@ů·đăÖů¶´ë9&}°Ç@Ë~šjmă>Ś ~ŔkŔOS~TŰÔŠÂĎ#}&:°ݦÄĆ6ĺ@·4/p‘ëwťíťôI¨|ťęą(pđćŘ |âľč?úč@·X@‚ţŢC[m(ü1R­”¶CŢŽ$üÓţńë|€öńZdK5Ž{ćĎŰ6vřĆOÉ^8-ĆN Î?ĽúáU§¦Ž$D^ąé'#˝0|Důý‹W¸ößLľăŮŃ?Ďž“:s‚7pÎçîY´Ô×Aeëziů}&EŚ ‹,[±tø‘ßM™RĽ|ńłŃľ€bŰěyď%† 9¶ÖŮgŰ´;×Ä„?;|T΢™ĂěUČ´÷~cŇÔÍĂeÍ>Ž@LŚýmĘČżf´Ůj˝9eúŹĂăo˛Ďď3č\QÝń?ëżë™P»dBŕräŇŐ/_Y‚f˙fB9µ’°îëÓ©?pŔÍ{ýÉţ—Ô+:­ ňđë×o>®+ąç«ĹuFŹčËz,QŚb'ü¸uwf~yy~ćo?ośëŘş']W–łní“{ʧ>ąr€Ä+cW}«Ŕ)ÄO]!cä1č5>«˘éÄOëYőءSi«—M‘[™°č©ş†šťŰ7Żš;T6ęđ©Ô{ŕ_źţXWž=>Ľ€ÓěŞf[zîdžĘ3jxű»SƢÓn@Ô”]ŇC hŘě«zió—ďżóŮ÷bSŐÂq§wŮVçNČľî˙XźZ‘öÓ-Qž›Ń;PŘĽđîç©éŮ őş“ö<<{"šÜÝ޶{««ňŠď:łďÓ—€öe´yŐÄ}˛/cϧĎ÷®ő\=´wŐôQĐúź-ą&÷´¶4Łż·PľôŢň7©Gö.;r޲ײ/§˝üÜ żĽ(ÖÍć©ä:‰nµQWÓ6@i?pŰ®ß˙őüšCK ÎíńSîŃg «Z9Qóčβu!ízúĐôpJ/m´XQłîŢ)ńh7v@(מB˘XRÓ´rR„Ú7©ĘĚľůçŞÉKWKŚíÚ¸açţă&ÁđÚcr|ĺőČÂb´Ězś1󢑔JE; 8+=l¨¸`‹)÷ľÄ»|ćč·ßüxňČŢ ‰Ń3–żv)íŔókź?pˇđZŃůř ´ŃŐNäŢ–ß0V7-)xĆň·r.§¶HyÖ°PXŮ15€·M(ľ¦ďŽŹÎF·ăTŞq÷ŠW»ëEń…X? Ô±żěŢkŃŤł»´ťľŤX˘$V7>6;€ŤZ`e[KŰ”ÜOŮ]U˛h˘ÇßSĄ7˙ľáóĎľÚÍŰ÷ÇÖ}“Z‘ö“ĽČóŠšTXcŢöőţýĺOB]é¬Áî~ôĹŞ’ł˛‘ç 3yˇCx[Í)GŇÎß÷`żQK uĺýŐ€"¦ĽQ|ţ‘ÚŔŁ—+2Nüţď—źO‰ň¶–iG=°gŢä!§[f4 łčîËb]y{--€ň´)§±>Ç 6ŠĆJSýĺFcĄ L`úěĆú¬&L`¦RCőQ]C®AV]?µ©D_}¤ÖT/6e6ęËM’A¨Ďh0”t— śŤŤú“©V0•Ę7–ý¬s›ŕ7N•ů\ž±ŚŚsä“TJ)˙?ůŐĆŽŹóĺlP†zđ wýˇĘ¦*€ĘQŃ”YWń{ť|4ĘĚLáŁvJt0^¨É|­°I'kCˇţrŁľĚ‘™J¬zHzsýĹÎi.ŰPVüK (M”˝c°˘lű5HF‰łămĂlĄý•wó+ĎŔQµom1ÁŠÂ6ŐJ„Blě˛őëŕ`›ŕzŹsî«yúkbÇ'¶”Él8R^ëíĺDš^<|â¤N@ÄŠ†şýĹ%çkęPŞ«>ŻkHpwwWRÓÖ_)gÎvvC˝ĽĘ®˙ýčé+F#h4ÎWU×ׯŞÓpȬ¬Î®o¨4+ę~Ľ”ńqVń¤čA+µ?p¤Ěl¦(H6jU˛—§;Ţ9}rOeüFŻ­Ť (2±ÇƦćć7™ĺë}”äćžâhó]vôŰŐPB$ Ü#řĺ8żWËm2s·‰9ŚQJÄÚ’ýScĆŤrRüóąµ_m;9óá·ţvwŔĘĄËKęĹÉYRšÍ†¬ó§OžËĽ%ÍSJ¤ş«ű¦&ŚďBËÖ<¸ęčĺRB)»>_ňľ«GÎhăäčçávdďáÓéşšę´siĄUU»wě0ŞśÇOVvţř7ß˙T^]+šŤĺĺĺ»wí8“‘+Ű-Q0VTTě޵#-#„ÔUćţ¶ď´Obâ°ÄŘË©6nţőju%ČÉ8™UXĄPŞ*rŇO§eiÜ˝‡Źí©ŇżřŹG·źČ r=k>“‘+1ͦÜËgŽž<'1dgź«ihĘÍ8\v­©¦Şr×®_;ˇçB.©)Ľx"=;1e„§V|uÝSßď8Ĺł©»¶şŔtĆ\@0/ź=qę|&U*•ÖAťyžçŰFyľ! Ďóś|ýÝZ•—ţ뮽嵺ě3©— JoU ¦[˘«„ă“®Ô×˝đTÚ/ës+ę9ÚGAU(ĺ$Ƣ‡-ú÷3žZů`fE×KĆXsUW“›~ţböĹ}SĆŽsˇeO¬|řHfĽpH çsΤžÍ̱ëçÓżżéęŮuO>¶ĺ`&n,ER[uęÜeŃlt =jlń‰_V®z"皍•űśnĺ|f)ˇTÄî-[Yüş>·˘rśHHKOóŇÓ/fžŰw0-vě¸@'ţĺgÖ|˝íh‡±C“ÚZŃËkŞżšššZ—yčě[Ë8•v)żđjuUYaöĽőŃ7ż‹ľŕşd!óĎ۵_îůßnÚgd_üz›Čşsá“]x•J›üÜônzq*ç*a˘k`E7^“]Ąď]R&ÖÔPyě葲ę’Ä„–6/ódeDşx'@(eLňpWJě•+ŐÍýqë>^ţřý÷: ˘Éxéě‰Sç3 !ő•ŮűNd 9ĆG+<łćˇÍ‡3fsQnÖÉÓç,+aĆ$ˇĂ(%uW­ć”´7ýzµ¦^W–ôÄ©FIhl¨:rđ@qyŤdŇ;–!rD©âŇĎž*©Ş'„´ř8uĆĂÔ‚’JQ0Yf´‹ąJ©ąŇÍ7™ŇŰji»Ţ†Zn˛n¨™N×Ŕŕ0ÂÍÖÖTşłhëŁÁĘĘ@Iż‰®M—kë󬲂R‚› ťÓZO——¸„#Ld®ó}‚ď°=µ4Sꦒ›'ă8&' ŤŇÚyóW÷čXďÚčşÁî˛Ćtš‰Oćb˛_H¨B˙mn)»ŚÁ„ŢXXU~^×x“˛˝.ŕ™4)ş˙ÓÁ.ă¶í3őÉrć:¸"' uuëoŻÚ’W|“·¶r–.€Ibkší6«ŢS I°;—FÚ—|ţÝF9&µäŹ'”R&gż…~>myă’řSë!rŠłëO–Ýa¸9ËŤu®čv-]/ë{ĎŠ¶ó:!„ÜŞÄöňC|&I “DmÔÄ˝›>r¦Mff9P dőDˇ†PJ¬Y&IL>_lţ†1˘°‘®=0sôo—ő%íü_ĺ÷iRßëzŻcTy÷ĽąWÎţ‘z±¨·©0ož:J™$E$čüýĆ7“PŠć¬íňó@©ÇAr=C·ťL»µQ„€1BÁZ§U ŠJ¨$ =X¶îeŃžěÎk˛Ľ^ë8ěiçćŁTěµ,:ŐpB9Â$‰±–Lđ„rŚ1‰«śö=2žBI{·ňŢ ±sitYş nÜŞyşřz›jX& sKŇ»9@Nq›0ăá˝?>:(ě\Ł(C(•c‹±kÉÚÓŘ›Î÷:iňm‰>Ô÷@:éKW]# YËŻLhţ™´ńö$”€4)%„µ ®ÓşY_{Đ€¶„ž@l#,K=”´źy-”[W޲C h ČŐ®ď”Ć@“ăř~ŢCl.ľTŔş`á(™µrŁ#Ç(a]ŢŽu«–<Ąr6Ú–Láaډ˝í,/m¶hĚ Ó˛¶&YfŹÄ|k+vw˛BŘź1´dž ]âíđđˇÓÂ_xßB¡íÂgµ`(ĺŔ$F€®ÖM„b6ÇsťFÓşq˛(‘,9•)“ntĺö'×C(G™ÔFŮ ˇĎA’šťI:ÁPĘ0Q’8ŽgL´^óń<Ď$JڧT”Dy˛ł„Tę©fJ9J‰Ľ5"”ňč™tXxő¦ťÖÓ{ÁŻő™1uŚšk] ĘHÖÝďˇ'šŤ‰”ăŐj5G-çß­ea˙Ż[ŠtB›5!!bűŽWÖ Ęo!ôZÇä¬ö}GX'\ϵ ˇJ/Íc<Ďˉ̻ůśW( ŠG‰ą«m|×”µ—)!]îĂ;Z?J)cm0ÇuĚłn]Ckĺťô”q'ŠB»"ťŽŻN› O Dˇ5ýş°G˛ş(f]+JG §”ăxj6›{?…PŽWđśŮd”xž—$±ç]hď¸aM<Ącc¬»Ń#7ş#ÇJÖ<Ż`’(±Ö+B)$‰‘«x`˝c·â9í°i_ŠI’gäȧ—M|}Ýł%ĆsT”Cęw(D9–}i ¦ÇŃ6dwÄ´™S$Qá9* áy®U¦„đGQ;Üž‡LE©ťĽzóÍőÚ„ë–đśmšň!Ě`nČ5¶ĹóLťH„ š{;Eă…††b#x…ç4çúŐ Ĺá Ń*l}U„‚D}AXëÚťu jL†*PbqŞş°?` š„÷BŠßÎązR/ßźP'…Ú‰kşbč\aQiě‚ÔbťI—Z/ @í”ÎIv0 ×NÔJŃ۰ZsS•± ˛1WͱőW[Ólą ‰¶wS”l©fá9MüQjř&ˇ±ÜÔŐM‡&ÖŢÎGŃ~Ë8&ÇNpśŕşŔ)}ů%C·ëqµŤŤŹZqĄ¦Ně„48Řhí4˛~S‚ĘúúÉÓÎ~€›‹ŢĐp¤ĽĘ$1BUsĂŇKó3ęŚň6‰WŞĂěµ “`Ę©­7ILN~.1Ć@|śaĐ더ů]iW›(ŮĆńZç͆}yâĐÖ’ŽŢl·nÍH/CÜÜG8Ű|•™˙8Ľmň¦+%(⥟{ů˝čFKĺnz'Jl¬,Üč„ItÄ@Ö BýcbXUIaY5µ›‡ż›]îĄlDˇ:rŚ–3îß˙‡ÁtËxÖRłoÜ·ďĆkî«zŔ*§¨č‚ĘCĘɸPc2! ,aȠҬ“‡OeЬWB#„r%’ČDIäýV<ĽĚUCň.śüţç˝’E,”k>˝{ŠÚZ3Ąň«NJHŹeUqęçíď®=źž)DÇJ× Ëj®o]Űyí|xT´˝F!ŠŔ8NanŞNżŰë®XHc*{'Ż~® Jj+Ë+jęz}•FŔşź‡ż«¬Ď`ťŽ‚ľŢ誼†Vřöß»í˙]łhĂŢ -a‚o7ČÇÉŁć¬z÷Ń;gŚž”oBo݉¬ů|9[ŕäáěçÍ”P°ň‚Ľ˘ĘźŔAI[8/'JH6 Čó‰cçó {%Öö2…U=N?źWĘDĆ'F…4Ôď?pĚh–@8˙¨ř$kLö°7˛č˘őîz!źX§&]zAQ¸.˝wVÚŢÍçr« …h6óý"Cü•&}Ý嬳I{–EntlËŃĂ7ÔĎKĚ ”Vv%»¸FŚŤ‹PóáŤú˛  %F€ö ń÷– ”HĺůWŠŞ:Ř„îdŃĚŤŽ˛čŔ».]Ö7ŚćžŹşžőB''6\-¸1­“«učçěďÍRW^’[T"0ܬÍ$„Ł„Ťl¶ 'ë.\Č43ĘóaLlŢŠt´ęť`šçQ”$I!ňôA‘7„R ĘńTb"“Đq'߯÷›®4óŕ8hgbĘŽ„ˇ{“}Fęo…ů<”ă›uĚZßźU#ŽJŮ5pč–X[% Ô¤ü1ČwśUNwz ;4hȶřaű“? ·őhIŹ…żĂ˝‰Ţc´-î¤LŔsuHʆHË·śÂweŕŕßS6EŰŞI{ň( â–˛Đí Ă Jx3@ PMüwńĂv z ©˙?| ő€źN‡1#Ď šěč0Ümرäv4p[ŕ;ř›Ú‘cţŞ`űä˝É‰ovŇźŰÇ1ůGµ:igbČL§®řIÍŠÉÇÍKť51ڧhöηč%¦E%^\2˙ÔÜ™GçĚĚ_¶đáPŻxß śe‹OΙqiÉ‚ÍcąŕťS/Ľ?Đ€’ن‡F^X˛đÔÜ™é‹ç˙6et„­Íą,V)3¬[ďä”!„·ß3Ţsľ¸ˇlˇÇ»`嵆¦ĽĚĚęšş?ĽoŚśżR×hÁěűöOňqç°±yŐ+g$Ú5Ż~]^U~0ĘVöúÓľúѶškU%e%Ű>zÍîÖ0Mľ°˝éšű®ŮÍ1läܬ˘«E…ůůE•E9 ÇF¸{őËĄUµŮ—łęuUŹ.ť€ď1ĽUd+Ů•W{żňá§™%µYľmv:í Ýäy `éź\ާ&T»/ć¶ö^”żáĚ?ÍťŇDěIÍ)+.Č/,**(Č+(Í=»7Ú^ PE[?™®W©–üíĂC}^VNM]Í®ĎŢňĺ)8ľ]%í\˝Îýńß—ő9Ć^`ü‚‡ZĆEó(č+Đ[]•CŁŢ÷Żő…i»l»Šżz»H¤&4­ úÍűďBłq÷…Úń9Ö^ `ţĂoWÖ^»’›“•™ŐdhřűâńIÔMÎ÷Ž«_ýŻ®Aź_X\zéXŰ·o+ĘVÎG^ůP®§,ód80vaqUÝ•ÜÜj]ÝÁM¸!ăď)jĆÚü0˘{Ř^mdA€ňݵŢU/,±F˘Ge”7ťÚňÍ€‚ŞşGg  R*Ś[üH…®!?;«Ľ˛úôľźbüśp¶a]ˢ74J4Űą­´m˝ęíŞşYŤúú–OFż”K%× óóŠ+ŞĎüQŁűă/}Đ•t/?<·- 7"‹?~x׫-ź›}ńăó+»ĺ†Żc÷şŃ«YN¶‹Vż§kĐe_ʬ¬©ÚýoÚ‹žř´0óĆmf—ňĚžH[Ą5I˝˙mżëXJVBµŰđ={öڞéŘëŽ<ě8.zóMWšyëĹP§ÁŰÜú«9…Ę@ś’·Ç[0ö|ű) Žwv4É’Ű-ĎJMŇÖŢ#l¸Nîç­ržę1lwĽťš(ýíRŽ$‡-q@•€ßSCTČĚ=mĽxů/őrM¶ëL$…*q{bČKD2.ÄyŕúŘŘ7ĂS¶Ĺim:ějä ^6ÎIZ6IŁĎ vŹS»Ěňu$ÁÖťÚDąŚ8šä3É-öë„1Ǻĩ}źŠź5$p˛Łă~ĂOhG3—»˝?jŽfÖŽ?Ä"¬°·ŁGťHđN`űX-·™cň®Ďű±ĐÁß„wfśZ'ż_¦ÝůŐŘ1©s§FvŘŐČ TŞBťśĽ5ęÉ‘ŃŮ÷ĚŞUx9»- Ô1ľŃW–-šďmŘí›s÷=~.&†„ŽrÓŽ ‹ČX0#–'®îiKż‚ć ĎĘ”±çfMp!ĐOë°4&ć‘ţŃĽűu&dT»sîÜçŁpť±Şnä…ĎřČŘť“Gü5c É{ŐĄÉ#ŹO#ç0ą^ ĺřf‘†ßżdŽ-?ů ˝Qżxd°GdâĘeódLCSĂň‰±T Ŕl/;ż×°‰žqěÄé­·f§ő·Stź^2Ţ›2uŮ‹ŚIË'ÇŁy˛żaŁÁ„MĽŮšűľĄZĺčé÷ŻoůńµOŽŔ9tBiťůo®4trJRzÔjB¨\—=Ľöůgź@ÁŹ˝»3cďWÂp÷‹|čѵO=ůÄÔq)˝Ńy¦ôŹťóÔťŁ“ş/(ĎŮ÷®ý˘0ëy†Ţ—YđĹ“Ëqs»šf‚xźŕ`źŔ—ż9xůŹďý=}‚‚˝®7OU3…ź—ätŃŽ[üŚŢÔ´zîH|×uä)Ü.fúŃă§,ú¬UNn-Łŕ&őą7ĐK“%Ą˙‰śŠ·š^í+n%ČáÝWľ±!çÄĎ Ů˝.wŕsś­€Ť“KDL¬ź§ŰĚÖÔÖ\ĺŻőŤK~đ^‹ýŃ›š÷łŹ›ZŁŻjA €čř.jşUńNdj§8lne}­\OLB’GßđńcýŃl^>>’:L—"cL‚°ttGäŔ•ËćwŁ eŔľŰÖ»č…쿬üĎOŚMM‡·~˘`—v9˙©L[¸lBRŔÄ%«®gĐđý†Őţ»v©\¸+YtÂyŤO‰U[+…kdł,t×®Ž °˙ÄěĽÜY)ˇv.Ţ.®ěbft#Ášęň‰ýĐ|0zò0LNiŁó–]ŤMLj/¸Áum‘z3ëÝÖu´?&ɸxlâ´ď—漛)›}Ź™3§;rQÜ5kfdŹ›Żź—_P‹… ńŕęůŔę5O>¶2*Đ].mmŐ§ŽMŕ7yňx fĚ *· Ëś@ă:ů®;ÇMšůôłOÝ10 @XTŇŞç>(.*~ó‰ű-^îëb­UyŘq\ôî›Î5óÖîj¨…Ą6ĽĎ}ľÁK<ÔÎň9-ZaÁ8q¬E¶¤EpŠł+ 4Đ1öí0[gG…h”<î {)@Ĺ JÎa´łS‚=«77•›H&F<´žěĘľ)33{uä›áľÓťś†;­ňćT­ Y‹ś ´UŞĄúËu2B*ĐĄ.N/>TG”¤ÍŁ5+gsiSÍmFˇ2WM:Aá¨4äÔë+¤¦ěƦZŃ.Đ:sSąŮmş—S×g˘JÂDĆ´ÍrÍ-áň iË0¸ŢíëěCĘvŐń¶\+1}Â1Ć€úKőś«ŤVCĐvI.ß$6Ő–Üůó/ź•+(×ƏŠŮ&“1·¦¦Doŕĺ_\Yx¬Á\z­j}vľ¨hŞo´\&ž(̉O^?2ŮťŁ(S$řŤđčg2ňD‰Óş- v˙!=ŁšSŮ˝;vôáľĂ}žJŚŃrí…,˙˘qtéÇKç«*€žłßj ňż¸éň§WVh4‘ ŠëßŐČáů/ť<ôë77eĺyő F•Ť¦üŇéLŁŢ,Wl6‹Ľßŕűç˙ň­7u€xĺ·!ɉüz@ˇR"€á±Ck˛Źţ\Ä-[ľ@’ ýĂâHדS˘#P"—r“5÷}=’ɨ++¬iLz]~YµČ0úŽ‘6ú+˙ůç¤ÝyôÔE’Ä:ćO‘1„P0Ć{FmÜţËÚĄw%ŤšşuëOăâüÍŔ©UśĽ%b"±übăćÇ—Ý5vĘěW^~Ęކ¬ÓĚ,-ĘQ±Ă&lÜţۆO^źł`Ůko¬łSÁR°“RÍ8ÖúA’¬Ý»l«L+0ˇ»{ÇÖ„`gýGÜŃbŐßxóyQ)ă7mŰ˝á“×ç,ZţĎWÖ8DlŘşíÉĄw%ŤšşmŰĎCíÝÂżiűÇo?7{ń›~Ţç¤ óŕüń‚Ů4tćŇÇVŢá§U<´˛Ľz÷MGÍĽőľ‹T&ąńrľŘl?Ń˝˙ÇZW*ÄĆ, &î“­«xÁ €db*_[§•©‘Ĺü'Ęc°š5I˘QňZt·cĆň eçŚDIˇäĽ–x=ä©PHő…–ĽBîłÜ:}éNęlc˘¬ÜRröľ gVeKć–Ú€Úť§B˝ěldIEyŇްZů­Đ tŚ~/2ć /c^CMˇ ÔŐQN.ń¶.]m\yÉČxĄTy¬Öiś»BŻ×8…ÄŠďHs¬8öi„ÖŤMđR׼7ňë‹%ʱV>ő Çőá©Ę«ĂÍUłbPRJI›żZÇT#„H€ťÇ,o§_r $@ÁQ P??x ˇ®ě·’:ÂóufaQBňS‘^+~ŮńĂŐzÇNą0.îéŃnśp¦®A®mfDَćëś*µcĽłvÇĺós~Ů1kçÁ&‰´˛L‘źV©$¬ÎdÂ˙. Ţ$RĘyjŰď]{”S(¨ô{ëí×›römßťŞ ĚÜŠůí<(•€ů+V8U]řřŰ]$“€JÉ[Ą% U5µŹ¬{ŃG~ojŽZyÓç÷­p«jîĂzäaÇQJ-KQ'Ż:]aF˝ěťĎµdŰčPĆPŽ>kÉ<7|úŃ'}ToşvůlKíV…lÚ÷j˝¸qǞţÁ×Ó˝Y۶Ŕl”ĽBZęY2%@ôÉ?lßóÝO–eßu˛@ř -Ç‹Á)8»Ń„NeŃcë-˝ÇˇT$­ß€W×Î{îŃGO_ŃŰpf3@)­o4Ü˙Ź^^1qÁȡ_ËS©TDĺ°|ÍłŻľĽĆ]©?•] ×ß˝,¬é‘ď=ěýZ·e`%‹Më7PĂ…3'2ň*’ç=¸cĎ/ń.fCS7Ü´~łp\űŐ˙ ČbÇńb€ČVńH}CĎÜčń ´űYďf´ÎÚţÔ\Ü»eŰ~{;[I’:ÚĚ(ĚŤ ő"@ęőF“%7±l!y Ó¦/č)ŚO‰TɉŐM™ĎÉ|.§&_pаD¦ŕ´ávÄ(čkĚ<L—îK?6á\ĺ|Ź8j|ďrľúCąAá ć×—n©ő| tčž!÷şÓ.&ˇQ’$F”mŚY݇ČŃĚZ(”}ZuęĽsg˙–Żqđ­­ÚTVzXů^dđ'sŤŽP®é\]}fCőzŃŔ(eÖ4/¶Ľ@łć˝XX{á±Ë™oä§Ţ›)بěěúÍđÖ82ęa㥢Îj÷–wŃ}Ä1ËŠ Ldbť´Ľ›=őۡyJ[n'äá;.8ŔF¬˙©° €$‰fĆ??rÔD¶ěŹŐ€’0‘ňQ.nśhĘ14 ”J¦†ŐŰIúnëŻ:ş..ÔŽ+Âü¶g\,–ŔQčk+ľÎ,ť;`Äů…ł^č©b¬Ke˛t&Q`Lq˝/Qţ˙ 8 Q’M 7 Ť0Ńl4o~ůăÔaÖâU ĚFkŚ"Ip‰x|ÉôMţ»Pĺ9ą-&I-÷Ň’Iď1⡉‘˙xúYQ©o]-ÉÔtKjîËz,—Ĭu‚¤&ĄÚŢžZF¦eUDŻP´y×dÁpDxú‡ŐUU ť·âŮ5Wçf\*-•_‚#ňť,ˇĐgţäó=KžŰPVš˙ÖßW©%K ´.jnĹ(•ę«%—vźËPr%ß$°nč±üĘ%JʉŁĚrű¬č©­N0mˇ9xcł&ßh\/J9SSÍÎßvéMćşŇ˘’F ŽąrP7IU)ßµh_eA/tLŢgÖÔIR¨nű»¸®XĆńD4›jşe:/Ԇϔ0“çÜ­1ć}·}ŃdláüĚ{V;Ť­ľüŇó–<¸tńŢËşq ðoY­Ú’m”Rec¨h­gLÜP—m3yöcž c–Ě ëđćĚ˝w‡h6 Ún4ˇsYôÔşĄ”S(ŁFă?qü`K—»ŕ÷Ű÷5˙őfeqď¬aă8ľŤÝ`"SÚöČ ±‡íCłŢÍhťµý1Ő––ˇŕyIŰŰĚžG Ŕ@šwSÍÇÎŘ«]ŞJł ŞÚ+§.şŞěđJ›V«ž—@­’1ŮR˝îšłwhCuŐĐą-sJ‰Ć^S_]v>­ f˝Á,Şě\0ž2Âä7‘ťÝ˛“Î-$¬ç‹îżéd¤ÜŽ—ţÍaä!!Zĺl‘Q2Fv ‰uŚ˙26ňoň]̡ĺăćôŕ8–órNe9÷J$‘1AN€f6T•ž*nw¸«‰±xW5,™÷zćŃ iY_U»/ôvKRhŁHňs˛B##śĘWcM=™d¶<`€:ÖÉšBÂLljV›MĐhŔĚŮżxdbÚů—ň`¨6Š„ÂdľpzţÖkś†“Ć µŇěŢÉBZlś&1˘˘B­±±Pđšăˇ äťÚm”CźrLd4^j‚^×ůzWć’ŔI’„ćaâ×ĎgĂÔ;ß#żÍ•QÍ ňMËĎżbd„‘aőŕä™^Ę™żî9/'îd°%⋇ílP¬•¤ŚI21 ›ÜµZ“ÂýHĂą2ͦ׏îřݦgÎLŹŤťâe‹ÎS^«k0÷´wlŐý˙Í ł#XkO™)× 7§_ëOżóáÂŃNcĆLHÍ*ç8*Jm0 %Ď€Yó ¤ů˙ٰ šOľÍ‚d2›äřLĄµ.Nëßzz÷S˙ ŻŠş*äć‚NËŻěĘnşćľŻÇ’/Ĺd0é-«őłŮçśÜ#Ć'‡€‰”ăJ%€ŔäÉ»ťţęí'(  CŔP×hŞ»2¨Á))ŁG_űöŹ’ę¬Ń$&L˘„čÖ­žîçă÷đç=öĚÝc§t]3 1€I’ Y¦iů%z@×ôđĹoŁb€$©x*PLž´«ë¶:Átv”,˙OĹć®ňë›ëK­”«Ż*xëťwć-x4`Ô˛GćŽÇsmľěd}$ €ČÚŚJiŻ"ŘŢ4ôVÇ$ 2űŠ™Řz‡Ä /cČâ! @x@(u™UFt™ŕ­ůl–ů, Î‹ćM;ľec¶N¤gÍů“Ą*«Q®ź™EéşćkUÝTÓ¶`˘X__őű¶MF>,8$© ŕď˙·;MčZÝ´.;P4Ź^b­­ÉżSu˙CĆGÚńł¦Ś#LÔp¦µ+îŮRdżíëw€AÁdD ňJQą»ŻżŠ@D/da$“ŮlEşkWÚ´uçxu]<Úń-łkDPNŠ%SĚ$ÖÂřn$Ęu ďşd `p‹ÝHŚŘrĆ5+wĂ ^–H×:Ňă¬wcZ×ü·ŐţŽşďˇq 5× U*m‹Í4ˇWʬ$&)•6¶€¨ć ĚfË;ŮBĘ;#‘µö®ö Ž^®e&¶Xőć<“ÍvžR´źS†=ýá.Iˇ•@¨$Š"łś‹©”J[{ :;ţîČCžç%‰5Űůµ XlD×ßX悎uëÁcA@ňwŃK|6ń{µ–ö›ăß‚ľ«żťź€Ý(÷±y#Sľ •݉‡vĐ®Aßy&dLę Źd5`3ôp˛÷0ꮞ64ę—»6pˇ{đş°{ś|ytĺßÂ?ŚKXçkůť§k“6ĆŹ<š”řy¤ĎD'vŁ-ĘűFĎE7Ç=ŕ÷E˙ŃGşF+ŔŮô˙(*řŻ[‡|năl;xß ßńZą‘ŘŤ#»:Žé7:#Ą•ć–ř®ł˝“>±ôÝcQŔ oŁZřŁőćp* ŕ•Řa߇ĘkGöÇţi˙řu>€ŐsHk ÜÚ!C÷Ěš™·tÁΩ–ű Ż~xŐ©©#”°l%ăŁ*WÜ3ËSć îŚĐđČGfNţďčQżN˝ăNoGŔáü’ĹK}T¶®—–ß÷aRÄذȲK7ŚůÝ”)ĹË?í (¶Íž÷^bšc‹iť}¶M»sMLřłĂGĺ,š9Ě^Ő‘Ly÷řƤ©›‡'¸›řn€#Ŕ„ČŘߦŚükF ­Ö›S¦˙8<ţƔ݋ď^ń*cěň™Łß~óă‰#ű&&ELXřẔ́żĎßp>réę—Ż,Asl(l^x÷óÔôě†zÝÉC{WM­˙Ů’k‡Xż~óq]É…8_-n:ĆüR‚zE§TŢLÍ}YŹ%ŠQě„·îÎĚ//ĎĎüíçŤc}űO÷¤ëĘrÖ­}rďńÔ'WÎ8c%cěʡoU8%€ř©+dŚôÄ'j|VEÓ‰źÖ?˛ę±C§ŇV/›"·2aŃSu 5;·o^5w¨6lÔáS©/<öŔż>ý±®<{|x?§=ŘUÍ*€pJ3yˇŞä¬-/+˘Ý7íéˇ4löU˝´ůË÷ßůě{±©jḉӻk«L''Döu˙ÇúÔŠ´ź¸Îżédo݇žű±ˇňŚŢţîTӕ㾊ɴęsę‘}‹Ç ˝sŢÓŚ±Ě3GżýćÇŁöĚą# @ŹďOnz­«ňŠď:łďÓ—€öe´Ë^ř“}{>}ľw­·çóÉ{VĎż@âś5Ś™Ť ”żł¶HÇí˝{t4śb/”6˙ńó·ţ»ž™uK&Ćô$‹mÚűĐôpJ·Ôó cuÓ’‚g,+çręËĎ˝đËÁ‹¬©dtÇĚďä\jÁ”Ž wť°čůzşĐ„˛ ¤ŰÖ×3łîŢ)ńh7v@(Ç©•€u_źľš¶‘D3°¤¦i夵oR•™}óĎU“—®–۵qĂÎýÇM‚áµÇäřĘ®eŃ çž=€FŁ”ŰŞHýAľ8ëqkY,yě˝ §ö?żöů ŻĄÇ:Š—Ţű˘ vËŤČB¬/š5,VvL ŕmŠŻé»ăĆŁłŃ˛SčŐ¬wCZ×ŃţĽűĂ]Ćo#ÇĚ(n~úňŮfÎŇ}=ňv7zü=Uzóď>˙ě«Ýڱ}?|čA`Ý7©i?É‹<ݍI…5ćm_˙çß_ţ$Ô•Î ŕîG_l±ęĽB6vžđ¶šSޤťż˙îÁ~Ł–ęĘű«ELyŁřü#3(´G/Wdśřýß/?źĺ +űÓ‘‡Gě™7y€Äé–MĂŔěşűFöš‡˛ͤ·î­@™ŇGmˇ1_®ÍyŁ ˇÜ …w &ż.߀™™©ÔP}T×kL5©ŤÚÁj’˙AAůˇFđDj0×_l0”t— śŤŤú“©V0•Ę7–ý¬s›ŕ7N•ů\ž±× #ăyÇ$•RĘ˙O~u†±“śł”€ÁPĎô®?TŮT-?µ©D_}¤ÖT/ę/7ęËL™©ÄP}TWźk %ÎŽ· ł•jôWŢÍŻ:kÂ[í”č`LŻÉúWľJd&ˇöl˝©Vd/6čË̦*“éš`*7”m(+ţĄ”€Á&ĘŢ1XQ¶ýĄ5 Ě ˛GŔôŮŤőYMňĎ}Ä1Ŕ6Á-ôçÜWóô×DB:}@|ě«ëk~/*«4™ňŞ«łô’$UÔ×î/.ąPS/Ű"Oµ:Żşâ§+e˛k‹’r95Ué×ę̦ňFýůŞę˝Á šÎUU×ׯŞÓpȬ¬Î®o¨4+ę~Ľ”ńqVń¤čA+µ?p¤Ěl¦(H6jU˛—§;Ţ9}rOeüf° }„0 ČÄ›^›ßd¦˝ńŐ»E@ a`Anî)Ž6ßeH±] %DÂ=‚_Žó{ĺđ±Ü&3wýĚ!cP*mňsÓϤgŐŐëJK‹O§¦UTëËŠ/ĄťĎޫו——îŰ{ aĆßźžöŕ’űJę%Î>!ˇˇUyéżîÚ[®«ÉM?1űâľ© cǹв5®:zą”PÚc垀QJ¤ş«ű¦&Ś5÷]=rvZ'G?Ç´#{źN×ŐT§ťK+­ŞÚ˝c‡Qĺ<~°˛óÇżůţ§ňęZŃl,//ß˝kÇ™Ś\ůá(+**vďÚ‘–‘ Bę*sŰwÚ'1qXběĺÔ7˙zµşŽädśĚ*¬R(U9é§Ó˛4îŢĂGŤöTé_üÇŁŰOäĚąžŽ5źÉČ•Ŕ$‹rłNž>×üř«Í7íéąË@j /žHĎNLá©_]÷Ô÷;N1Ŕlę®­.0ť1ĚĆËgOś:ź B•JĄu޶¶H%%EgÓÓ r3öLŤ;.ȉů™5_o?Ţ»W…mlT^zúĹĚsű¦ĹŽčÄżňĚš~?ÍDłĆÁ>&>ŢtőěşµŹí8•+Yaž{ňŃ_O^±·µż’wľĹBvŞ mdńëúÜŠĘqţ!!]µţň3kľŢv´ĂرdŚe€ŮlČ:úäąËkŞżšššZ—yčě[Ë8•v)żđjuUYaöĽőŃ7ż‹ľ Y´ĺ|ö™ÔKĄ˛ÜÖ©s™ đqí—sţŕ·›öAć’yőĚţ'V˙ídV ˙ŕ.%Čşx3p˝˛xň±-.ÁĘŽĄ5[’Ɔʴ®ąńá×ŰDÖ]´€ŢĚz7¬uŚÁl6ĺ^¶Řꬬł &éřîőŰ_2|¤‡­řęş§ľß™Ęş­‡PĘä>ŕ®”Ř+WŞ9šűăÖ}Ľ üńűďuD“ńŇ٧ÎgBę+ł÷ťČ2rŚŹVxfÍC›gĚm¬:kc祤îŞŐś’v`ă¦_ŻÖÔëĘňŹž8Ő( Ť UG(.Ż‘LşcÇ2DŽ(U\úŮS%Uő-ϸ:ĺaZZjAIĄ(,3ÚĹ\‰AĄÔ\éć‹LiGÍěmž±Ţś.´ [ŚükçKç.jhF:Śpłµ5•î¬ÚÖW˝›˛2PŇo˘kÓĺÚú‚ąť)Ąť…ŕşáJ‰dÉ©L™tŁ+·?ąB9ĘڮۡĎA’šťI:ÁPĘ0Q’8ŽgL´ĎóŚI˘ÄxJEI”';9ůtoj¶˘ŚĘAÓzYŞÝ«7Ąşi˝#{ÁŻő™1uŚšk]D‘;ęöB9ÉđÇ®m…5f‰10×ŕ»î¦$‚ĺ+B9fŘ˙ë–"ťĐn Ŕń· &FÁ$Âń’ĐśCť‚ 7ĆyB• ^0›%Ćxžg’$JLÁó‚`fÇq˘(Rާ„ ‚(·Ĺ$©ă~‚ăxŇLŻP0©KMh#‹ÎZç8N…vE:_ť˛”r<%…Öôë”r°ÝˢÎs'I"cDˇŕEAč΢uÖ/«f»8íPĘz|q<'‰"!G‰Yč™?˝äFwŞaE%Ç+Đůč&í´ĄÇjŃ|ÓŇşĐ!Ô:ú#ÇóLěaă'o)=#G>˝lâëëž-10žŁ˘RżC9Ęq°ěsĹňSĘQj}ćŐÓfN‘D „ç¨( „ç9Ijžeá9Žbg4wĘĂvňęÍ7ťiÔ­4h¤ő˙ Ş@ ß$4–›şş®ŃÄŘŰů(/Ô7›äBkoçŁhLoh(6‚WxNs®?QÝP,ž0­ÂÖWE(AÔ$µ®Ý Q‡Ř Ćd¨äMH]k0MÂ{!Ĺďä\=ˇGÔ» µXgŇĄÖK¨ĽÔö1¶bŤI—V/»ă«ĽÔö1Z±ĆŘ‚iď9ÝĄ{š-—!ŃöÎaŠ’-Ő¬™ę¬P;rMW ˛ ©ťŇ9ÉFáÚ‰Z©ÓCUBÔA-47HĎ{NsąIŽ&2Ç ˇ śŇ—_2t±'ˇ\’§‡–H‡KËŤ \ČĂÎn ›kَáhy•Ibśz¨·»–Nj˫D‘PőÜ0˙ôŇüŚ:#O©(IĽRfŻUSNm˝I˛ě…$ĆŹł# úb˝‘B›O‹;ÝŐČš×:ož0졶–Ôp„}rŮŇz›űg›Ż2ó˙ĚUCg oşR‚"^Šńą÷—ß‹nö°´§ć8Ž#0 ˘OD¬“ľ&˝°X^z€ęŞŇ‚˛*źŔäA‰ •űž0 ·Śg ŐĐ‘cěxăľ}L7Xs_ŐC¦P9EE‡đ$PRNĆ…“ a CĹ”fť<|*Sd˝!”ă(!D&J"§č·âáe®’wáä÷?ď•%/Ď[“z\|ËşíĐĎ+Řß›#¤®Ľ$§°¤Ç[.ĺÔĎŰß]{>=S Ž•®–ŐÜ‚wY„ŹŠ¶×(DQÇ)ĚMŐésűrѱłŢÁÉ+ ÷ş•úÜ+zˇ«ňZáŰď¶˙ýw͢ {/pí›­—|ś!ţŢ’ „©<˙JQU «~€YÉkßU©Ů´—ĚUľéęÎß˙0šĄžřCFxץ˺ă†ŃÜóŢŁ˝ÖÁ~ŢL0PJXy~žÜSkmé•JS>"*Z«V’¤P*ŠósëĚ4,(€RÔ•őĘŇvG Ůl!A9N4Ö]¸if”ç9ÂŘĽ!”Ęoő)!ňtĐ ¦yNEI’$"OrpQ’Ą”ă©ÄD&AŽŻÝ›‘{˝ßt>No=P@lźĽ79ńíŔNĄÉn¬«G”˛kŕĐ-1ZŔçáL¬­PjRţä;N €*§;=†4d[ü°ýI‰ź†Űzđ텿ý‰Ţc´-nőV üբȹ<ożgţĽç"|q›ł…Z<ŕŁGe”7ťüéCŮűZNć66żJwĎŘđ„)÷ÔŚy™™Ő5uűľ}Ç“»%\“ol_ýh[͵ޒ˛’m˝fw#÷]=˛›cŘČąYEW‹ óó Š*‹rŽŤp÷ę—K«jł/gŐëŞ]: ßc>x«ČV˛9Żö~ĺĂO3Kjł|«š/MŻdŻÖE«ßÓ5č˛/eVÖTţúń랲ě©ÔŇ'>-¸ĽOΓ˝űbţgkďE÷y˛{f(Ŕk"ö¤ć”ää”ćžÝmݍ˘­/MŹĐ1÷gĎß´9ĆŘ+Ż54Ýj}îKz§«rhÔűţµľ0m—mWńWo‰Ń„¦Tży˙]hv ľ.˝˘#&,.¬¬»’›[­«űcă{ž<#~>rájYaaIĹĄ»#Ü4ábaUa~^qEőů?hzŽßBÎýńßĎ.Ż*H?e«Ŕńí0€¶…ĎŰ?~S¨#&gTĘmĄÚ¨l}c;ťU^”_V]őŰ˙˝ĺŘŁ,(PŢşuKŠG^ůP× Ď/,.˝t¬żm'iÔ­-í©-qŃ (¨Ş{tÖ *ĄŔ¸ĹŹTčňłłĘ+«Oďű)ĆĎgÖ{YtÔđýß˝ëAęţ·—>i޵W˙đŰ•µ×®äćdef54Öľ´j ^óĎ˙ZúĄUPyEn=’n‘ױѾvműŐ^ŃZ5Ç€ŘݧšąúůŰ΀կţ·+ţČ*Çiâó+»ĺ†ŻcG®vŻu‡6ż§f>ôVU]M—=Ő(»Ż¶…BĄmôˇ …eĄEWňóëôMź>»lÁňtú†Ţ[ÚN«íÄBžŮiۆŞŢŽ˙¶ßu,%+ˇÚmřž={FŮtěx'#÷‡w˝ÚZČŢ|ÓÉ8íźoädhaoGŹ:‘<ŕťŔö‘ä°Îń.ĂŽ&ůO˛ ŤÔr€Ş+Ć6BËó€R“´u€÷[®“ű9G«ś§z Űo§&J»”#ÉaKÜP%ŕ÷TÄĐ"r sOď^ţK˝\“í:Ł P%nO ™c‰H¦đ˛qNҰIň•:Ř3I­ ´őžćĘÚa^#O ň¨Vi[1'yĄh«Ń˛Ş©5I[z¦€ËÝމ…ĘĄůçëccß O٧ŐRK‚FL°u§6Q.#Ž'{ŹŇvl« ͧ{ĄŘęAŰnÇ䆼 üMxGí5&Č'4ůâůýě&„ÇëV.Yčë «•±¬čaýÜ…h€ßčüűO柳(Y¶ Ĺ–ămÜ.Ý{Ďę/Ŕv˙ÜŮ÷řą:ĘM;&,"cÁŚXž¸:¸§-]üZ\š7<+SĆž›5Á…@?­ĂŇGúGOđî×™ĺÇÚťsç>€›Ž¦u˝ łb|děÎÉ#ţš1Đä˝ęŇä‘Ç§Ź‘Ă×Ü6 ĺ?Ę˙ütŔŘÔtxËÇr+ž#žř`{yúďZ 4iřŠ%smřÉO445,ź‹ćÉţ†A¶ŚaďÓKĆ{“C¦.{‘1iůäřë­ąďëQŞUŽž~˙úöđ‘_ đtáśC'”Ö™˙÷ćjIC'§$EˇG­&€Ę5pŮĂkźöÉŃ<đŘ»;3ö~Ĺ wżČ‡]űÔ“OL—"—ě¦VyrďÚĎKrşhÇ-~FojZ=w$ľëČ3ÍĄľ(ĚúCŢŐěË,řâÉ帹]MsOyźŕ`źŔ—ż9xůŹďý=}‚‚˝ţ¬óÔÔ±Éü"â&OoÁŚ@ĺd™Sb‚hĽC'ßuç¸I3ź~ö©;F‹JZőÜĹEĹo>q˙˘Ĺ Ă}]¬µŞšLN‰·˝ú¦ă8ퟯ(áŔ\ďösö!e»ęxŰfÖ“ću?§8{±Ň@cßłu…8÷·Â8C@8B(DŁäq_@ÜK*L`PrŁťťěY˝ą©Ü@21âˇőś`WöM™™Ř«#ß ÷ťîä4Ü9h•7§jn˝­Čą@[ĄZŞż\€PK›jÎl4Ú•ąŇ($Ó•ĆŇ_«EŔTeUQS^C+¦©­ó‰uő\Ď4ËĄZv)b.uqzńˇ:˘ ˛?;o§0äÖë+¤¦ěƦ:ŃÎßF段sIš«Ťfť ¸cڍżTĎąÚh5m—äňĎťűéę*w6ůáAzłĺŕ €‹śĺŰƬ«ßć芦úFAâ ©njŞ2 v!öözَĽQp`…9ńÉëG&»b%pŠäżýLFC~C#“(qZ·…Áî?¤gT3p*»wÇŽ~ Üw¸oŔS‰1Z®h]úńŇůŞ  çlÁ·üď¶ŢÜ ČůÓ++4‡HĹmŰŐp<‘îzŕĺńAŠŻľ?¨µ·ś "ăýß?wřçoľŐäť9úŮúMŤ@Yy^ŁŢ|Kš¦D0<.Ą&űčĎEܲĺ $ÉĐ?,€Ôcľ?µÉdÔ•Ö4&˝.ż¬Zd}ÇHý•˙üóżRŹî%9™{fćś3>sΞ5sÇv8fÇŽíC:đ˘TiIÄDdôĂ–mĎĚ;xÔ”7׿`o%=ľF-p®Ą1cAyőŃŁ'*ôÔÖÉ$ź­~u÷*Ąw/z·T‹Ö)wÁ„âěĚâü;ęJنü˘ü¬Ěâë%żôN:ĺŇé˙nÚöpíą-h«ŤI:‘]=­M WOüCˇZ×ÎŘxGt´•ŐŽ÷É—byNʱÓéŃQ:w (+Ę­¨R«UŞ’rmhP»Đp˝ş´  ä˛r^ąöÍwW/ p·ď5mgT€š´±˝ş±÷¤Lˇ¤"5iűë(’—Ň~úÔÉ™)&, dă˛rí›ď®^čf.N^9ÉgSňj2’2+tÝĂ;ÜS#}˝ÜĄĽâzőצ)"Gnß¶%Ň—ŚR@ŹşvѨ§µł·•.r 5ú9/~ľcăçŢ6ň(ěúN7´o_ceiFn®TŠ¶ë˘©…WW8…=äčŃ˝«YbTceEzrRnQYŹÁ“ň®l?–]rŹ÷ěŃ­VŞ"”•—ÖęK§.)((„zŹ“ščBÁ¨NuR˝SYíß)ľk/MQz+ň@¸-ŇhĺBlS«Sĺ•WV­I»y˝Ĺš˛¶^CŔŚ"ëÚ­ßč#ă‡Çą[ËL<ECÓž¶%H— Âz ŰđŐ'ňĹw?MÖĄ,/·07«®‡ĚĚČç|˘·ěŰżnĺ¬éK^:¸oGçgčÔř–݇ţŘđŢÔ™óß˙`DĹÝşëŕŢ›:kÁŰo®v ŚřcÇ®çćŽí>pĚ®];űŮ»…÷ú}ëîo>zuĘě%[wnéč¤ě0hÉŚˇoę3qîÓKçEř[C˝ŘÍȰ8ݬ\µŻ¶PÓ4m§m–sŰÁ1§Mđ\—¬ui\'ź:ŰĽk@MLágăZŁ«fíżŚ»aŞ¤Š€»”[Ën_ ˘‘z/ Tz+’Ü,ąntÄ 'Ţs|ł\Fm®9®Ç$YĄľp%`g+»PyŃgŮ™żWĘěĺĺn†ŇĂ­ôŘ0뮎Ń/ŘřɵgUĺI&óŰz…®ň§yUŞózłK€ú¨ď´I2(ôm*sHo©ëÝ©2–ÖXE{¸ÄŘ0?'+W®J ýŢĐĐ»e®>§*ż% +… {C -EVx#mköŐE…Ń´ KgoA}Z%(k÷Ea”ĺş^]kŞ O–čʰx]Ëż?\ś¶˛řD~%pN^Őą§ŻťÍ˘=űţ*ŐrôEDţXÇŽ¶JglĽŞ©–¸MŚw4¨Î(…Ň1ĆŮöŹK—×':ÉIŤäh¶A× ŔßV.GLc2-@k1&^¶č†Zpáý·€0jëßĺ­5Ó_}b˛}˙óĽäßB„‹9©nnřő Ŕ2Ě›¨ű‡˝W“ql÷ˇ$xHĎţJ]őä+ŻűęoM¨QĘřdŕň‘ö€ĆIDś¬Ľ5•ąÉZ&„™ßD6€Í/h ?iÎđ.^o?ůV˘ ŢűúŰ5 ¦Yů!@ëřV^í»·ظö™ç>?äâhŁ3˘úďS›r– 'đGďro]‘|ôŻÝÇ$F¬•ݤĄźęO,[ţŞ5J}y™Ciڤ+łf‡Ş˘ż Ldú‡ěą-¸‡ŤIŽď|ťVUŞ˙ 1¨Ş¬!2ĄO I¸É?@˙1¦TŚ˙džŹÂ=®ţýxruŤěDÂí—{ŹjÁZ“rňС4E¨˙Ť„ …Y%=§/™ůŘř¸ţqײ˛¸–jR>Q…ś»;ĹF@őĺlT*ä ’®]*Ě*é9}ńě9;EtĚČş¶fĹČ>1BȸPo§s†ĆKÜfuŃ4wŢH˝C;ŤľuS#nŮwřńQ}6H®{ Ő´§Ť áyڱVgXřňţ3ôůý|V|„)¬~ÉŢÝß…¨.§çHüďWŤ-üp`¨P[fQś‚ăf n˙íÓŻR9Mµő˘Ś@ұCWs_’ôUuëřˇ ĐH5őĄÁ o‡t_d–Ş»Ón#ŚzwťÔĽ|Ě´Ő÷–Fë{ Ť­îĐoű.äćL-×´í{†‰âˇçwŤgžŽ¸ ń\uµÖŮ·SÓžöÚx]µVD€«µzŁÉ›Xę!9 "…qăçvőzGĆ$ŰĄf^ž7aÔŐ7™D‘×”®ž2ú݄ڀđ0ŕ“ISşzĘż2íÇÎ}!ľ«÷»O˝ťX|űĂšSVď,ôUŻ<1â÷kž™Y‡ăâ»~öű{}®Ą\Ţ÷ŃŰ‹†îIÖŚ¸żk^†€h=ż&mIÓ¤Ą<ü˝Y žÓ}­í(ö´rŠR`gĄGóť^ġşXNÄňšÔç3R_ͨ¸#8µł# h…A˘¨ł§;‘Ém;;dôj¤Ő¦”'’λ^v…<îŕhí7ÖąôĎb€­-ü«ĘkqXźĂ]BçyŕT/č(Ą I·U}byÂôëמ͖·ł÷b”Q‘„ľćÄ’^ľcb€€Q7 H3Źző Ă­•y¶ůZÓĆîN6Ę”śÖG~2ĂQP ŔI/±ęĺE•ŮÁ+ΆŐHń0$fžQ™¨aćz5Ą˘‡ŁÇ\_‡w.'VQwž‡qÝéc”gÜşăŘ“ˆ†¶ëjŹź9|ô±C'‘­ËśpL@dQ.nD4ej̦lŞ^ą{O÷ßvě­ÄŻt P:. ÷ßť|+źÁ Ż*ů9µpZ—ţ7›ôZ§HcŤD*•·Ň$ ŚÉţ­›(˙7€`)ŐIËć‚?b0wĺáNĄ_t\—+÷°cű0Q—¨gćŽßúŐ§ą"ŽŢ$Xđăć1¤ŮËK(ŕ‡=šjĽ#ú/‹Ź|yíK•Hˇ|Đř\Ź’ŹůP–Ý`Z#WÚŰcsË4ĎŹâd˛÷šĚ‚¨^ᕪĎôE/­^Qž™śRX(«UŚt&‹0čSĎlřţđśW˙(*ĚţđůĺJjöÖgé7ډ©F˝˙ĐJť‰×ćčÔúWŔ)Ć”af>Á—5˙U«”†¨uŢXkÉ˙Ş_/ÄÄʞۂ{ÚtJ¦ÖJeŠü^\ł"Â!‘ç«ŐÍŹ2÷®&ŕö©Í˝cÚŹśňŚo×!“wŚ=»o¸őüIŁGMţ1ËŽ3f™:qü˛§Vöî7Am?´Ô:·˝WĄs T;ŘߥHjŇ×ÉYÍd6Öö¬üĘ”±cĄĽT ˙iSí˙ţłďö%˙ĽëČűOĹW”•!ą˘Mşh’—\ae(IY<}Î’ąłŹŢ®Ňą0Ld2GHó=íČέˇ2ŰŽť{s†ŠTU NN«ňć ččąí¶ě˝Us¤ ďSÍX¸ŚĂĐPb19u˛µ1ë÷=Çj˙jN#…’5sIlRŇ č˛tÎřf´SOŇ‚gűďßnŘ“$IU]Q†8™Lne(»Ý‚|ją1‘Émî) ±Őă ­îiŻÎćMî ThĄ¦mżN˛•±wWÍěÜ1:,Şóž„L'{›šęňĆ=m˘-CŞ‹ÚY»5Ně•.ŞÂôśr€Ş;—Sr]vŔÉ­J R^Ď€‚¬lP*$J:Ş­¬pö «.Wő™V7¦XŰ[kË‹n$Ż7đ˘Â·bҝȦGŁÍČpR_FW×Ď·%Móíôˇ€ 1ęň艹¶AŮ)Ý:€˛cĚŹ"źő–ňä Ô:ŚBŚ7uĂPť{1BXĆúڞbŇńÍ@@EĆ)o(áĺ^ pîˇDĆüĺ`Č„¬÷RĎ KLŰXîńŹ[wÔ‘µ×·rŤ …źµY,ČDť >UÎóČÚ_ Ďytă®.LŃć𣰪… Ć@ŮÁÉ\ŻÚ!őeöhn‘ş‘QžšÇfŤ1}Í­łń‰7ÖçPCĄ ”íȰ~™M&dígŔŽÄDÖŢJ}e“ů.(1Ő8+­¶ŢH8ˇ¦ŃöÖ*Ţţî>ŚýyŻöŇÝ\°˛WωŢň‰{_UëŔ×Ćç«÷”^-ĚO7ělÓ×ĎśŢ_-Ű4°» ŔHA ÔÄ€!GWăak #ŰůŁę2ș÷ÎëúŰÖŻçŚďĐa”· 4w™˛BSÍçeďx×ö-¨…$Ž[{ĚL™i×˙áç"ť#WVdßĚP-\¶$&ŇŽsž:v(Lśľ,eůÇV*2€µőXśÓ AĂŇŠ1Ćmń°yŹÜ@QU‰‹Ó¦×Ľięě]˘Qşź9ÔŁç#ýBM“Ţ,…kéם<"†ö &bÂÉärę9ňŕé+?zN€Ŕ: 0ht&ÍťĂöŠŤŤĐoÍG›©äęLg˘QF1B•ݬďďëżâý-Óź~qňŕ`‰m™3H{ĂD«Ęůđ㏧Ď|*pŕü§f`Á±ŁZúŠ Ľ¨–qV @©‚Ăę9â@‹y5Gifě2KOĹZ®Ňí›÷T´nîˇŰóĂ´1Ę ,ýŹl|BŰĂŁôP+"h†ÄĘT•lJ8D­Vud×ÖRŚ ô1ér¶9éčŢۦ.ÁÁ yćfĽČ(»Żˇ€¨‰7ńôn)Q$Żr… ĺ\¬Qg0Ś™(‘”?5u°§żď¨…ĎňŚÜ)ÉĚÚ¤‹úy•רQ’ăE*=  ¬m;e¸J]ż§ š4jb˘51­Yôř_yö»~ţD`¨ L‚Pv'ŻŘĂ/@"îG-X8m$1Q;Ďš>îÂ_[ŇŐ"`"młKi¤Y`_¤I›˝­V_ťC‚€6gŤfiH>+łźšb–މÉrĘŇóMZ*˛–ä#őZ”!b\˝hv+Ňŕ$Ť´î¤žŐ•ąvˇíäî-Ő”żŹCZ*Š˘(bŕy5čiźś6€á{í©QFĺr+QÉ!ŕyó=©‡”v“Dl´µwµGČ1ČŰUŔRčwQ Lš3KkF©™‚14Sú®ýę•ŮRÁ$9ĆĄ˘()!¬Ëmě­ˇ…cöF2Śh˝ôó\Ň4m§J6ć$ĚKNś“TrS@*ÍÍ7ň€s’;÷qrڞ–šKů©r#SDĽúbs§É­V_P™\˘¸sę-‘;É®&c]&ŽvŹ|Ň3&÷ł]Öé»öń6ĄËßtŐÎ"} ęÔeS‡€)®öÁ Z-‹%=Ő+#c€Ş´©&÷ŢNŔ(xN칥CđbߎßFÚ93Ő± çŃľáOz‰ę· °ŰĆ(§H+瑾ížö5ÔoA@×ď#Ý{Ű€ĚŮ\ŻşgP2'yke>Şn*u†PĐšĐŮ2gEÔ×Q>ě”˙ô¸gÔ[áXĄ+;Qś9/LŐ§_˙ľ±˝~żTĹ•ś›°ŕ5Ŕ~Ď,›ĽăÇĎRĘA¦‹”M^ôÖŰĎĚŐ¨u«^úřÜÉĂ“‡v¨Űaz@H!ŕÜv-WŐ«˙ŚMżüćaČůy×A¨ÝHűä#˝€ôڶyÇÁ…Ł:vţġť[â;ř^ŰőűĎgrľŘ|ŕ•5Ď8}á™'Ć€‹—˙ žűtí€ÁĽyăčć3¨gÇ>]ŰKżŰO:çţű6ozrůÓÇĎ]X:{¸tsáfâ ßîăöě޶|Zo›°g.'¬]2ł[”?_UZ_ ®ľ-q–TbmăŕîîfŻ„ü»6ţuýŐ÷ŢqP8zµô˘ç®\”ůôúîűĎ?ţîÇľ~6'΀“«w _µ@¦×ţÍ[7g‡Z"ű‡Đş– Gž˘­2Ć0SŃŮ× &{”«Ä€ ť4:éĐî#&÷ëÝ[ň?1{Ĺ'·’.®őµ=Çut2]¸pěÚťëŽý~úîł7lĺůס3“潝|áđş5ëŽÝědĚ=pä@x¬M‹"’˝öÉ÷ď­Zćä°éĐÁ“‡ ˙Ćß_µĚÉ-`ăţËÇÜżů§…jIÎ>BŢ?ţ5uŮÇ×Ď2çeĘß{ŕ8(;üµ{˙k«WüúË6Yqâ–GĺŢŚ.(«źűĆý–Ťďżă—‹HđŹż|˙á~ŚŹt>vő 8›ŰN{)|Kýž–źťĽü#˛÷ôđš[«ćĎQvťţăŰËLđ ěđŃOżď?~áĄ]·lßkd Ňf]4cá§ŽLŇY„7?˙Ń,±WÎMşMžßÉă·ÍżabýzýtŕĐüá}Ďž?ě:đçď>űpĂĆQîÇŻ$BU_S]^6qeˇ»–¤Š ®ě?rĺě‰y˘_ ňé€k¨¸»{MJ‹ŇضGŔ¤ĹcŐ&Vw8ĆŮxęĚ)č1nvK5ÝtđŕŠ)ń”Ý{5 ss÷°¶şűęÝÚ¶QOű¶żDQlýM<ăMr·Ŕ7ľüţ»˙ľîë>zř8O`çčâćě m°Ů»Ë`óÍ_~úĂ7=ÝčνG@©°vwu6Żž…µ­D‘–ŚűŚ)N˝˝<8âćîa«”@MöŐ˘j«W>ţýÓőëzFxC=ß6ÍÉĐpňÔIpp1ëKΛýdki0ńÉą­ wϸŔôé:mZŤôł©ĐP~®˛:Ó Ú¤NĐŮörP: ě/rJÎčŔÄ«ŻÜĄźÖ‡h5Ż˝Um(0T¦ tÉ:}ÉT%Š Ĺ[ŠňvVşMô˘H}5˨$emdÄ‘sěî Óě/łË“ŤÍĜŴ,p‰ŹöŚŞ¦\d<#vÄ&܆ŞőYźf—ß4rVDźV­Í0:ŃXjҦčř*±ćŽN›nu˘±Ě¤˝]m(@d¦‚ÚzŐ·Xć˘?Šň÷¨ĄE‚U”˝c¬hO…twĘĘ_i*Đ—ź­2iĹšTťľŘ(÷łrěę`Ľ©N}7G_AëËP›ij¤ÄŽ“Ę|ç“lŐ5#p@uCb6ťÝÂwÎ|+K_!6˝b‹˘Ľál±ŞŹ·ŞyýĚĹK•z¨XR­9ž_T©ĄrL2ÔŞ¤ M5o*Ňéo«Ő׋ oTęb<<STQŁV•8°·™ňÜĚd€Ôą·.&Ąw‹íďe+ľőĘ żď»ĚxSkyµ@iN¸oĽ}íâĺ©€°\.ŻďÔ™ă8®ˇ—ç˘pG¤ăď– =ňűgěůaÚ*"côŽšĺµ÷lĘ,Ń’GuGcB‹î;ëÓU^Xş$µDC(¦;cPSScíčĐ!&ĆTzíĺ5Oíąx§,óĆ•ŰwzôîëmGŢă…˙î>Î8™k`HçΑĄWŹŻZů쥴Â6)ˇŔ°0UVŇŢG‹+«Ňݤä¶‹(ËĽ±÷ŔŃâJufŇŤ[é·ŽťJč®î×Oţşő‘5–j‘ZťsëƱăűÓň»÷ęămGŢ{í… ›S@ŤC 6ĐEeƵĔÜL÷Шç_ÜłtůŞ µ´ÇN&´24¸9ů$Öö$şę˛Ä–ĄńŐĎ»DÖúľĆV÷ĘsOí8™ÂĽ]Ü2nśj¶¦ĹU•éWRr › 1Ô}MuŮůsg‹Ę«€R&4éiłR/•USÔÂ=)ާg».cc;ÜąSNpććÇ8A8q䆂h2¦\»xůF*BH[–~ěbrď|m…W/Űv&žĎËL»tĺşÔ[0ƨPGa#Mi˝1%ńä–­{KŐÚʢěs/먠«Vť=u2żXMM•çĎ'‹É$éÚ啡»ÝK>ý×ɨ7î$&gŇ{Ą© đÉą­¨=±0ź[5tĆęýĐô-Z+ŻÓšý`ŕĐßÍĆĆT¸ż ß]Pµ‰-Fîń®5·«´YMâ„>đłéşq‹‡¸ &2׾!Ăm.ĎMĄ'»–júw$&ů!˛µóáJW˛6ĺ×Ö´"ćf‡IŠ=ýCĂdú_3 ™´ ˝ŻyÂŁC‚rUĹ7*uÁ#s‹DÝimË]ÇL±‘= „şşu˛Wü••˙÷OmĄ›9ôîĆf'%µ1ŮkC¦b ”BźŃsŁě ľ˙í0Ă„Ńř ­ˇě\˙*$…8»Ď›kVHňš*B ßÜwwÔvQ4śĎ!„ĐĂ l/]Äg”&ŚŠ¶QńG·~íŚkxfŢPŔk°G~˙ĆĚŠV,žwč¶ž`$ţ[~ÖŰ–Ož>íε ·ň,ć”cFiD·^]ś߲ď~7ęmęýÂŤ^šŁf†¸‡WM„ë»ńi*ç& °ŐőTëşhĚ˝yˇ™oŻ5mö¸ůî`,¶Y-JľĹş™i´ŮKŤľz•ż´Mĺu±é9‡hIŘ«×ć !‚>x¸o3§&r&„ ĘîípD qŰyŠŁß<~]'J¶„0FŚRV×[2ÚČ6ŰRů6+č>’¶äţoLŁîc?ałhFLdŤ'ĺĐ@ #@ŔęoI>ĘX“ôč®˝ŢcP/G„"@y©<Ŕ(FßĺÉD€t%Ô»sy×)Yí Á]‡\ŤÔŠb b”9u÷ému덜fVXťXĚ,D0ó”Ä({řkŐ,9ŚĄh´uý/B ÄkK›®_ś: F Ökhć™1kj  ŚI>Ţ[ÉbaÉxßdŇ;8lŽŹĂŠÓW„‡´tý'đw †AHlqśĂőjŽc˘Ř씎`óMbN&cT|haÎÂQsLeĚčNiţe>ĚŽ—a ´ö1I3Ś &RJÇXhç8Ž1*RĆa,RQěĚ.îĹc‚1’–FcŽ`A$5¶^h2ńjK-šĺÓ’x1ÎÖwÂAJrwÝŃt^ő „ 3ßűW^ĄĐú[ßň9NĆŘĂłç6K[mLŠj˙č ÖLîgŰjoÓ„p”ŠÄDÉ0ĆmąF€°\Ć #…»á×1&PĎĽ[ÓEÉcL°`ŢÄS„1jýR_+RmËWHƵ(ůúE’îYŚĹ{u÷”F«Ĺą«fLŁ }?J3Şz2Á„ĂŔîÝw5JábĽ J|8Śxo­çÁQę9`íüř÷^y©ŔŔ8‚EÉĄ~“Ż0!`^ç :gzăú{^M) Ć*R@Á˘ 0@G(­eâAbs~}cĂ0FcŤěą-i€p®őřLEńáşšD± Vb!fŕ«3Ť )&]–©Y• ąĚ©§‡iĺyŤÉDăĽĆąh/–Wç CL`ČVfă§@AÔç¨ŔîÎÝR†ZÚdP #n/ÓşkŔyZwţ,4˙ŁŚŇKzéü;Ë”Ž¤ćŽˇőOdÍŐşb`;ąsw;0 «¨€¬C¬X_Łâ ›`+ľÄČ Č&@YżĚćĂh{çpYÁ_ĺ qÄşž|ŞłL’ńYw°·ó•é’´ŐůÍź&a;ąs;05T`Eb€™ă0ϰ™NI R ­ÎÇ•VVľJٵ¦Qc•ľp°˛˛ł–ě#(Ój &/;».n®zCőŮb•‰2„Ó“ ł“5Fi™ÄÉ•áö¶2&Á”QĄ5Q&?§Ś1@ľÎŽ`ĐçëŤ!\»ýßěŞFjś­ó¶a}ĽxzGš ôŕ[(÷ۤĂ7ŹţÎVSł˙]LM!-şb#Ţhď;oĎ‘ĽűÝ,mXU`Léîŕj—™’.0Ŕ|Czöč¦+Ë=~ú˘I¨=Ŕ`´oĎTąEĺ tó p“ľ€dŠ>Ůăńă' ¦‡&ł:Îvśńرçü¨ř &S8EE‡r PŔĐŚä›j Ă;÷îŃľ0íҙ˩"k“Ň„`„€ŠL¤"‘ą/Z1ßŐeÝĽôűÎŁÔ¬LjwďÚ>ů–Näy‡âJ-ÂÉÝ'ŔĂöFR*ÝVä㩽,ĵ‹Š¶·–‰"`„Čřšň¤[™ŹrŃ´˛Í¶‚GT€6ŘŞ4‡–ůu:şë§˙¬žőÇŃ›unp˙iHŰɧ.˙ä©ŃâFd›Ú|~Rż·ąť.Ppňđ ôˇ‚ Eh*ÎÉĘ+Sű…öěŃ­ş,çř)łäĺŽ>Ăő#5ĄŽś0ňm«căžMşqDĎľ]‚=o^Ľp#+0 ŚŚéZ­Î?~ňĽ‘§€H`TC €O`hŻžÝŞKsZ˛„–tŃBî^7/žż‘•ŰěÖµÔ)řFtpŞ©LĘÉC2׹ó&%Ýv=S%“ÉDžçÜ#CäLzÍí´ ^JĹ{뢉4šćĺčéćďMĆÝIĎW‹:F(8HfŇÝĽ•Kh¨AOß&Öj0;+OŐ¸Oh ůKndćbąCtT¨’@ś±çúbŔ2ű¨¨PĄ ¤Ü“’sç:o~kŇ0ň÷^xÔ¶îĐ^=ëŮ"Q1]ŁBŞŐ'jő.W¸ ŇĎëO>Vj0µŢ_KlÜ˝C|Bšâ‚ŚÜQÚŁ‘;>ŘŞŹ'»wzŮO&SvŮŮ­×Ďí0€Ă Ď7zŹttčçÖ÷|ĎFe·™~˝~‰ŔM%öC‘ř®ę®Gě®}¶··µj SZ'şĹĽČ€•Měńż-1éGĄ˛űţnˇťZ’'BV‹şöžWK9˝í €ţ3–Vęîa t0×Zî…)ç;Ů4F]ň5â=0ą¸ćň__dÝ%GĄyjRPČĺ0dö“%•ŐŮéiĹeĺWŽmoďď Ä&Ľe]4‘†µjí\Ę+q××0eůG*ŤZŇ…NŻ}mÁHpŹM)¨ČÍÎĘ/)żqjłµ y<óĆ-i°şşrýŠiŤĘ UńnÝo_Š&€C‡ßĘU™9źüÓşˇoDIÖ#nć”Ii’NmQb«{HĂϱ©TëŁië>ńÇgN ő~bËg^ŕ±óěÍҢÜÜ‚’”óű˘ýěZç,őłV~VY]™ž’Z¦.ŰűÍűŢ€ĽľúóLyyqQ™ęÂöŻ| ÜWSm±‡Ľz8ŇF^żHmeÚ0]ÓŻ$á+Ýú>|8.ŘŞi­%÷tĂfç–ŐJěĎOĽ¶‹¶¤ytŔN˝vwvë¤$2…8Đ©çî3ĹžkT$iŇě4Ć{ŕ…Řö»Đ+`’€˘Çî.>ýmŔu¤»s´ÂyŚgß1vJ$°‹=Ű3|Ž`9˙"úü)“Üş{YůĚôëíÚÓ®9•€LŃmw·Đ©.Ť„:wÝÔˇĂíbwu”–M§Ŕ ˙(zŕĹž]? ŹÇśîlă­˘\úźëî;­ĂĎťťďęŇQé÷BÄĐ´ŢA#ą÷;ŇąQ™Ŕe˛O·ŻĂ¤•@ů8Ę@ăŇ÷\÷€v`ÓÎF¦hŘsdÎýTmî{z÷łPöŘő$&­ú|žëőK»¦ö#±­“˙žqŁ7”0mLd“UŤą\ćääc­ťţř´>¶2og·9áAÖíý˘ďĚź5ĂÇŔîŘÔÉŹű»@|hŘ@7ŰAáÉ3'tŕ«GâÜŮďv …ÚĎŇŘÁ×' sAî¶sŰ·˛Sô0÷攌°íţiÓÖEB=˙ŹŇÄghd‡ý#ű·ŕ‹ö_†´VťŰsŔ…ń¤&÷[B©ó˛k?ţÜ…Ë;¶ěČMOěd«€čžýΙj3rUuMő‚ř XőĹî˘G¬˘'śżxĹü•ť"F<ˇ§Ćy=CÇĚť1ş`d Ôö ©g Ź˙»ś=ąRáčĺ˙ÎŻgÎn~7ĐË… pV¨áú`%tď32¶{ÜÓŞ…kĐükÖ˝ô\×č qžţdňŃŤ˙ČeO­yáąUc†Ä¶Ĺ¤‘2 ˘ĂČCş÷Ň‹/ŚŽëŢú‡Ň=oÍąi'¤UͱԜž[oUS[ Î7$ÄÓ7hý/§nźř=ŔË78ÄűߊS…0€ČÍ´‚żiĎmAmLŇ Č.f”|¸l*´i]ń0ÁKß˙#ăâNsŮş-7ém:ÚČŔĘÉ%˘}/·‰‹WW©KŘúuěądŢt€‘« Ľ~rŹŔ‘‹?6jî´÷ł í:EŁŻY<®[ëşh¦głS@PßieÚŞfĆ@űÎÝ8úµ:¨„Ä-3ňü‚ˇ‘Ř90~H¬D1 ÂܸpĎČ®KçĎhĹšęě[Í=:¦«‹RŤŰ›ô˘Yţĺö“Ćšš3;6ČŔŞcâíěĹcşŔ¸Çćë?gyi~jkÎ=ĽožĆđź5sĄŹ[ŇE3’·–‡Q˝Ľľ‘Č\#kuQYQ:$Đ âÓł2'ņٹxú¸¸€]ű ­hP]^ᵣ`ľ}¦Ţ•|—ޞ°Ń©go'§Ćë0LŔŞÝŰéćÜť]¶ęĐi–{¤¦­[gĐÍ^_ď<Ąłbý:NX&«úZq=U:ÓKKÇ@«űPµ=ä÷g‚\l‡Ě~Ń(ňóă;´ŹźÇ„šqťüĽŰÇkLÂKł†¶ÎÇÜí{†Nś8Ţ‘ ŮŘI#C}Ýüü˝ýëzČP_pőŹ\ĽrősO/Ť ňľ®ß«ŹÜü#:Ž9ÔLÔnÁć1Ą}0Xű„Ť;zȉk_zax×(ŹęľüŐ/ňóň?XµpÖěÇÚůąÔ·*闀Ⱥ¶c0™–ŚŠ©ß.Ú’ćŃŔ,¬8ß'üBćx*ťĄ}Z@J™™âD$Ż_P÷`*7™ŞDko+?Ą¨áŤ%&©^˘‘z>ŘńŤ@L` 'qÎNťí™–Ż)651äië5Ě®č—"ž˛WF~ĐÎoĽ“S?çŕĺ>DQ/Łz*'A6r%ŐŢÖHšS™0;)˙´ÉQKkuKŚëd?g_Tt@ĂŮ`ŕěd† ­ľ„Ö¤ëjŞD» ¨äkŠy·ńŢNÁ¤:˄剌ÉpŁ2Kśë˘č tW>V.ś:Ů‹eäŘáŁpÄY‰™ťĚY—;µ °`@€ţm‰1†@›˘%®V¶Ö ʉj/“ÖTŚŢąçűĽbYC/¨ž°M&c¦Z] 7tńČ/Ë=_ÍV¨6ĄgëJj´:óA<â2ÂÔž›ôô@Č(R ˛žţý=ÝMFCvµL"%¶nŹ…xü™”\΀(ě>·¸ť_?żŔşµ·%Ť•,ýbíčâÎŃŞxtđę•=ŕČŁ€dRIe%ÖÖ‘2 ÷żŞ‘^ůפíŐý‹˝'e Ą(ňréĚ7mÓgéôĽÄçEÎż×Âiý~üđJńΡŢ=»I_In·űučŁN?·3ŹĚ_0“RC§đ čď­j€~c˙&çGχšŚ•EąęjÁ¤ŻĚ.*Ä `ĄżóĺŰ˙€„sűĎ]ľ”˛¦ńS$ B㼢¶ěŢłfîŘîÇěر}HÇ^`@” "-‰ě~زí™ůcŹšňćúě­¤Gݍ%Î ˝ˇčĐwؖ݇ţŘđŢÔ™óß}˙;?lć«Z»{Ň»˝[É«Ę]0ˇ8;ł8˙ŽşŇĨ!ż(?+łřßzÉ/9PNątşa+xDh«ŤI:‘]=­M WOüˇZ×ÎŘxGt´•0ÔjÔ´·‘‚ô++Ň““r‹Ęz ž”syűéśęĽ—żýy‹ ¨8łZg˘Lîäč‘sólJ^MFRBf…®{xhµĺ6ÍKęŁâzőצ)"Gnß¶%ÂŞD¨ĚK=~:=:ŞCç.eEąj­–Vd«Ą”ćhkôĹ) ßýĽµ5Kh˘ ‚‘ľŐÜ#=pąÁ(ąăŻk„C”ÁŘĹë‡Ë6ţ~ĘÎŢ „8†ýś?ß±ńsoyv}'ŚÚ·Ż±˛4#7W*EKşhFňRś;Ęęň˛µłÁ|UEZ­.ňwÉ®QZ+ŔĘiĺÚ7ß]˝ĚßŐ ­i0/aÇáŰĄw;IŃ8Ő˝@Ćalí,qp·kçÁő{ „©MăďfË0!¤ ŇhĺBlÓÖ]­3ÉäJZ‘}üĚ]K¨¨R«UŞ’rmhP»Đpťş¤  °uε€  Ę«Ź=Qˇç厞Ńíc‹ł/LĘ+ĽyôŘÍĽ>1íˇU?ěŇ:0¬Ç° _}Ŕp!_|÷ÓÄa]Ęňr słęzČĚŚ|Î'zËľýëVΚľäĄűvtq€Ný‡×őęď°˘b‡nÝuđŹ ďMťµŕí7W;Fü±c×ssÇv8f×®ť}‚ěÝÂ{ýľu÷7˝:eö’­;·ttRv´dĆP7ő™8÷éĄó"ü­Á|Ű j…€ĘsRęZŠŞ8ݬ\µocPëií\NňMu·«őůĽ}ĽG§o"l]15ş43Ąă†[oÔů –Ѝ»TU]DĂżŠŚů*„•č+ÎŐ€ :ę˝(0x˛cň‚›E׍HŽANĽçř/ó’ɨ6×WČc’‡¬R_¸ż°ł•]¨ĽěŻ‚kOÜĽş<ťňfCiĄ‡1Z™Ă`a5îXďz âi<Ç5ëýlm>•"ĆËj¬Ú;ąÄظĻZąrÔČ89-;_ĺ4ÄC¦×WćeHÁ5-sĐ»ëřM„Ň ŐLáoăŞ0éXű/"=űX»Ţ ™sŹ®ÍÝŤP€XĂĽţľÄ€ ĄĂ ďîă1 rŚ1jđ×ú>ŐBŔÁÎs’ŹÓžŚ #P®ëŐŐ ):T A§á…Yť{ľé˝hĎľ?Kµ B‘?Ö±ăÚ®ŃnD¸Ş©–¸MŚw4¨Î(…Ň1ĆŮvßíS÷ě›´˙T EĐäŮřŰĘĺiL&° hM"ĆÄ˶ńÚµí`˘ 9‡Íë8Ŕ„“aŔýĂŹŢ«É8¶űĐ ŔĚX´ČIuó›_5ë}©ÔUOľňşŻţĆŃ„ ĄüoďßßĹĂâüůHÍŽ`ŚÍ“/'+oMen˛VzťOHměí¦OŹ% &ĆOš3Ľ‹×ß~˝á믵öakL1sŻ÷‘•Wűîíţúţ͡z1Sg4?Wk‰sŚ<ĎkJWOÝ©}xü´§Ś"ją< |·hP‹śŰH©//óś†`$ťe#ôHŻś6&˛†­ î3ÖVq“Ääë¤Ŕ´ŞRýoH@Ue ‘)}Ą[5mř¦Io08ÇÍÜ~ۦm"€\!ăP­ä3Oěżś¦©Ę ě:bdźÓg‡z; mZd6Ě €7RďĐÎCŁýJµâ–}‡çŚęŃFţąűđoď?W”|áŔĄ|h×Xeß…| 2|+–Ь.î™űěřh`Ś,µ „±(P[˙.o­™ţęSO]ąŁ·"<€1Öę _ţbý˘ř™úl<źĄP(ÂaÁę—ŢZżÚC®żśž#ńo]őË#.Ůt­ź— ęébë¦m @†›W/&g•ôśľdßá=1.Ľˇ¦ nÝ´M ¤~Ö¨î›÷ž ŻŞHąqą>gĆĆ \aŃtí’”f˙‘˝ý¬ŻÓď)Ť{î6jÝ{ŽÜ€đ~f˝$ť;ž\ťwáDÂí—{Źß˙-źźtč@ÜÓŔ˝;8p(éÜ–Ę›‡·8íR]-E¶,+ŻQ:I®VîŃ˝a×UkE¸Z«7šĚqĄ’ĂăĆĎíę% ŤŤéŇs°Ţ-zŢ„Q`Ĺ»˝úô§€L&Méę)c:E…ÇŽ\2rüüř®ŢżwwL‘é*}Ő+OŚčŇyB)ń‹ďzčĎ÷úŚÎČÄ· í;dĎů|¨ď1RrÂ9 ľAK$˝Ŕa­§yô«1·ęćS·SßĎN›Ę+ä.ěYˇ&ů™T‰"(ĺ.˝mBBI‡¦.#<ěýPĘł©7žI/ź±Ž`AAlŰŮ!Ł Wó ÝĽŞ6Ą<‘t~Řő˛;(äq7Gkż±ÎĄ@Älmá_U^‹Ăúî:Ď·0:J)CňDĆîž5 É›™TBŕ5ÓĎÚžbO+§(rTzöµUí+.8ĄŹü,2d¦Ż€ lEj®k´©Őĺ'µ˘!€1«_ćŮćhő-RĚ­şůôíZů(\zŮ2Úú|Fę«٢S;{l–ä¬lIÁ)]äg‘!3µ€d€1?‰™gTŔD&j¤Ąg ÖľÔoDć0®;ť¦]CB­DíöÜ" Tä·nŔŔx6˙ÄĹr9b"ć˘\ÜhĘ0ÔH¦LMŐ+wďéţŰŽ˝•ř•ގJÇEáţ»“oĺS ôU%?§NëŇ˙Ćc“^ë©`¬‘HĄbUšD1Ůżuĺ˙ "Ą:÷`ŢÁBf«y“`ýÁŹ›ÇD“f//ˇ€(—gćŚßúŐ§ą"`ŽHyI_IöBMzďţËâ#_^űR%R(^-jŞy(ś%ó16»;Ŕ ´F®´·Çć–iž!ÄÉd NäÍ‚¨^ᕪĎôE/­^Qž™śRX(Ý6‚¤3Y„AźzfĂ÷‡çĽúGQaö‡Ď/WRł´8ߥČĺĘŇ‚”×3 ŕN¶I`­”Çü+CL¤`"R†ĄG„d÷Ę«JCÔ:o¬µäŐŻjÚ đŁŠm°1iť©ÖJeŠGzŁŁND@8$ň|µş…Q¦ůŹô619u˛µ1ë·ÝÇ@4ë$?~沀=›˙űÍž¤źwy˙©řО2$W´ą€ ú(ąÂĘP’˛xúś%sg˝]9¨cH9˝µwLű‘Sžö꣬t|3P‘1A €ĆJxą—܆{(‘1˙@92!ë˝ÔsĂÓ6–{<ćăÖ] Žšźkd(ü¬ë—ž‰Śňć  @ŮÁÉ\B hŚŐą‚÷TOŰ ‚lnQµ)}Í­łń‰7ŢČf†rŁ0ř› “˛wTkBŚ1Ţ-łG3Âw%ĆfĽQ¨ó°Ě(“6ďJLú‹Öţ\ĘŮřÄësC†r#â0}X{+Aô•ÍĎw%) Ś™(j›‰ż»ďcFŢ«˝t7—2H1)Ř/1;űŽ‘!„D+{őśč-ź¸÷đ )p'$ľ~ćôţj٦ÝeF Ą&Ć 9ş[[ÜÎU˙™#•é˝sÇşţ¶őĹë9ă;tĺmͽëĐTóŔyŮ;Jb~”-á’8Blí13e(ŔßrĎ ÔÄ›J@d°ö㯋s4hXBZ±LÎ1€IÓ—áě/˙Ř PëgIúJňĎTXUââŕ´éõoš:{—hT€ţžÓii?«čos~ô|¤_¨É`Ň›;‚kéם<"†ö &bÂÉärę9ňŕé+?zN€Ŕ: 0ht&ÍťĂöŠŤŤĐoÍG›©äęLg˘QF1B•ݬďďëżâý-Óź~qňŕ`‰m™3eĄeRď*ÝDlą<PŕEµŚłR0J–č ž#´śW3”ć¶’ĄDQ¬u÷)ÝľůGpOEKc`ýV€1n‹ÇŘżŹ¶Ú´5–~‡G6>ˇíáQú ¨´ CbeŞĘ-xkRżÁK˝Ť vž5}Ü…ż¶¤WŠýOj!ĆUd=5e°§żď¨…ĎňŚÜ)ɆŃëďť—ÔG•רŃuťŻíÄ(jµŞ#»¶–ąđ0Jë(ˇađňç˙iÍZÖE+ąK¨kŰG®RgßĚP-\¶$&ŇŽsš4jb˘51­Yôř_yö»~ţD`¨ L‚Pv'ŻŘĂ/@z·Ń]đ5ńó©ŔA — érçúY˙ö>çŐ9Ä-3KßcࡌĘĺV6˘’CŔóć{:R)í&‰ŘhkďjŹc·«€EÉ ëzőÚČ“µý<ĆĐxLé»ö«TfK „©(ŠĚĽ/¦Ëmě­ˇ…;c„“ŐI¬ÄČE´‹€Ŕ^ úy®ašvˇaR±ÚŇr&ëô]ű =Cf8.u‹\îC@BťşlęôGČ+áýŹvvňă É(ýÖť_ń3˙Îá 5!Ý·Ä 8×˝Ű÷‘ľńN`W[Bé3Ś$Çbovčű{8 ›Ž˙‰ YěÝĺŻn˝kgĺlÓëXżˇ¶R&¶tŤśíę8Č=.9ön™gšý¸Nńéľ!L:\öśŘă×(ł|u˛őŔŕlÓăPŹ®ď‡„ľwĄ»W?›¦ĺÁ¶6żŠ YěÝe{×ŘÍ‘J€Ü¦Ď©‡#1ÔîŰN1ŻřJoFͬéÝçđ¤‰Ysgî3lAtlWľbůĺ1ýĺµ–×1(ŞlŃ㓼$™ŔčČ.ŐO.>;qäâî3|´Ź#€ĂŤ9łçú9(l\S<ńU÷Áá‘E‹ćţ1dŔoŁFĺ/ýR´€l×”éźu …Zßb¶Îľ»ĆŤ^ÝľÝKýfĚšŘ×^Ń´’”Ţ1f[żÎđ€÷«aŮáШ˙›Ţ¤^ëQă7÷‹yĐ"™Őkź|ź”^­­L8{lö>٧ŻeŚĄ^=÷ë/›Ďź:2uh'çł)Ą?ľ9j}ČÔ˙ęŇéŁËÇŰ€kgţÜ´iŰ…Ę‚›ýláoűxnJ`ďčÄś˛żĂůQň1{1ę0lóŽ©ŮĹĹŮ©‡vn‰ďŕ `˙íá¤Ę˘ŚWÖÎH©Łnç:lÖş{YB] Ôjî›_9oT 4j;€0!J9€W~ľRš¸ ë®ꚥ#"”~ÝU<űĺíĺ#箤ŚŘňÇţăL‚áݧ%˙ň–uŃŚäWL‰kką”WIÂźŇ!p×IĎÔ×Ĺś§?»yůřş5ëNŢĚ­ČKŠ r˝ńŮ­h°Q‘^çsľ=®çięI>xüďĄ$ž¬ĺ|ŁKt©íµ¤NiîÓźßÍ=?ąŁżp + ­Iă©)Pë˛Y4mÝ—ÎÖ­ýÄܵ}áđh·¨á‹ ŚýůßĎ>Üđ+e†U“!÷đ°ěŐÍŐeW”˙‘¨I=Ú5z@r‘ńü?ü÷צ˛;Ă#<ˇž7…¦–»ŃCWéů#|˙ÝĆŚ±c~ĺ‰^ů%ˇ$q»4ÉóŽ‘«ćwýüĺ§?n4…“ş„Ŕä§^ŻëŐ9™ ôó|ęŤ)go,śÜËŕ\¦¸“@ÖľX'®{rČlÎÝ.IľxäÓőëbŁ| ^˙cö‡ůô—©É—%‰‰ÚĽI}à޸#`Ţ3 ŇL赓ŔGTdr_Ą]„5»*ăýśębÉ|ę(Ůšl0ž™ ĺç*«3 `ĽŁÓfě;Ú+¬QÎ7ą…G4Ŕ!ZÍkoU •)"]˛N_`2U ¦"Cń–˘Ľť•nÓý‡(R_Í2ęŔü´ÂČ#çŘÝQ© Ů_e«n1Ç!Śë˙‡9ÂDjÔáŕĺ~ŐgËőe”ȉU€5_h¨8ŻĺuĚVS]`ŔđE¦Š ]–‚ĂŇ­ CFŤ6µ1,÷·vîádJ®J/O§bH¤š:Łš!BĎô·őĆ^(ç…*A(1–l.*Ř­¦«({ÇYŃî ŔD×Éç˝lMž5ĽúŠÎ¶—Ňe‘SrBŰ@b™`“ů*ťş9oŞSßÉ­©˘¨{‰qŽ2ÇrÍţ2»<Ů6ť]Ăf»dľťĄŻë^@5ęéüě˵ę#yEe&SVyyZµžRZ˘­:ž_pS­•zB/Ą2«Ľdűť"éi‹“ µ*©BSÍ›ŠuúŞň˝Á š®«TůZ͕ƚ@jYyş¶şĚ`,©ÖlNIţ&-DtŹĄA¶Ďź<[ě䋵R*zz{y`áă+——UKw”!gbOwm#7+§†—|®?`„°`7ŹXG«ßŇsč˙ŘŞ#DÚyżŃÁ˙­łç3kxňŔÂA(0,L••´÷ŔŃâ*uZÂŐśu^AjâŤTŤ¶˛¸¸đŘŃ“ť'<żvFř’9Oh9AýŻ*Ő™I7nĄß:v*ˇóŕ!.¸hő’ĺçn")ŞÝßĂQMéńS ť‡ ýś)L¶•“Łż§câŮŁg®$UŞËŻ'ŞT÷í3*ś‡ë[tăÂ/żo/.ŻycqqńÁű®&gJGDÁXRRrđŔľÄäL@HS–yčŘßnÝúvëp;áä–m{KË5AFňĄ´\•L®(ÉHş’fíáÓo`ś—B˙úËOíľ oř4ĺ|59“"ƨŔçe¦]şr˝öňW4ŤËs3“RçŢş”Ţ-¶ż—­řÖ+/üľď2ŕM­ĺŐĄ9áĽńöµ‹—o¤ÂrąĽľSgŽă¸†^žÂq‘Žż[lŚ\n•ť™t5)MŁ­,,ĚOĽš[¨z‘yž­"Â1FłŐüKŻ=ź°ç—¬-yTwä0&”±čľŹ}ňě„–-I-Ń6 oĐŰTf\˝r+»ĐÇŐ=ăúÉ_·“¶ŁëKľ  ?ńZbnAľ{häŔó/îYşbuşJߦđ8 ű¨¬¤¤[©×ŹťJě0xH÷ć‹«˙{)­d˛€Đ5ČšÜ` c$V?•Đ~ČĐ`łäá\Cę8_Ľť {-ĘŔ(Đúą_Î(Ů֔$¶,ŤŻ~Ţ%˛ÖŢŁ·ÔşSłňśÜ\˘;u2•^{yÍS{.Ţ)ËĽqĺöť˝űzŰ‘÷^{aĂćĂPëož7eŢľzîŇuĘ -íZµIĽ|ôŹ?÷%ö1ČEYąvÉŇ=×2Q«żcƨg».cc;ÜąSNpććÇ8A8q䆂h2¦\»xůF*BH[–~ěbrď|m…W/Űv&ľAŻÎôó c¤)­7¦$žÜ˛uo©Z[Y”}îâetŐŞł§Nć«©©ňüůd‘ ą‚$]»\ ŇŢ˝ĆŔÔÔÔX;:tÚÎsO˙u2Ąľ“3iÓ4§RáQtˇŤ5ެH˙bƤĹúÁvĄ ńć´ŇL»Aˇ›ŤŚ8ôwł±1îŻj­D-KŔ6–9´¦€µžě!Ë &2Ź™Áì/ÎM©łńňéäµ–"%¨UgýÂ6•F$ ŤII»¸ŰÚňĹ{«€ J› J‰PfÓŢη+9¨~Ŕŕé÷/ÎfÇ{Ś€2詨ů95ďAnĘ#<648WUtM­“¸=$ iK‰ŠÍĎoÂ"ŁŁ:t~.ŘyčÎŁ¦ű’ B„p7Fď °őÄánîťěä۲ň˙ą'’ĚűŽži_đßß·ÖŇF”Cmśĺ´­¶‡óżĘI!Îî3Ç–­˘Eüťa“Ę>üĚ&ŚŠÖĂŽoßŕŚkxVĎokţüţ)Ś!™­X<1îĐm=ÁHü·ü¬Ý[ m¶1,›úŘĚ;‰Ç/'çbŚÍ59Ś1Ą4ŞGl· ç_ţÜów‚ׯsźć<řĽ¨±›äŘTĐ÷ŐM´®‹{ć^[=hîÎŹďľiz‚±ř şh)Ż–ĘvŻĹd[G‡¦)۲L­ź¦%i<´q§‰•=ФkÂé^E”BÜvž°âč7Ďć_׉- §‰PŰTů6·źű¬q[rÓ‚6— @:ŮeŚŢý ڱÚŔĄ’{@€9LÚ´â` Ś1ó{&2@€0Ć#`fq±şůdĂi> S(L‘ĹĚx\ac Łä0aŽĂŞL"đXA ˘aŽ0* &PX&#rŽQ‘×ď~E *ŚLD#,“aڍ`02†° #D0&ډ‚Ŕ&†ńç1dr3™¨Ń nŢža2ÖCť<{qŮëË †î®JÂŤśýÜ•X=j”¦éW#ÄĚO^ZáĂ&·VĘى;¶nŮ„H˝+ć”1*™śYË0Ć0BXۤ0BőĎý)Ł”‡±ôF›^Őz°fLlaNF¤Ĺ0B@l}gĺá›9jZÓFI¤a»—Đ|O›ĺ—nň÷Ő¤˙űőüŰ2"‡k}2Š˘„ý}#˘X´¸ˇ ęEEQ¤c"mea„®·¨‡Ĺů‘ńAB$Ĺ Q)caŚ‘(Š`”R†&Jk“@S Ć)%„cěî:™p–$χ±HEé-«|ş-śĆcAű^_BĄ ÁH"¶)Ż&”ćK8Ś@ÎѨ8%ę¦.´ţLńA(f8ľ÷ŻĽJˇő6Ť1‘üSAĂVđČĐF[ý7ć «Íö#Qj]'5•<‚©("L ˇ~ /é=‡± Š’s*Š€ FLił”6ZB[zH)w‚±ŘBŰ‘€ ‡qˇ˘H“”2ŚIť Ú¶Ş‹¦ĺi’4Ő…Ôů`"°§mŃ`łhFňM87’FsiîG­¦N‘!D¤áŻVď@'=y'·ť3ĆHęTĆÁ‚ tăÂQę9`íüř÷^y©ŔŔ8‚EĆ€QĆĂÝjbB$żl#sĎß´WoBi0¦P‘âIâ­ÝŤ•ęR7~5­iŁ–ŇĽ›¤yÄhö¬crGźaú‘šŇGNy µS)ßNzőÍÜ|ŕŇłGwť*çř©‹&ˇqš¤Ü|@t1‘Ö*Ş·Ľ „Eaŕ”yß|đ–&ýĚŹ-°ű´¦•EEÄÚÉ-8 F*RCĄŞZĄ"¶N®ŚŻĄ”©°µc4mˇ¨TŘĘÉ-8°Q—ŕ9ŻŁ{ŰNV“›]­§8<ĚˇŞĽ¦˙]®>Q˝;·«Ö;|ÖČS@$0*¦[T¨Vťâäy‰Ó˝!%0*¦kTHµş >Ą[Ň4 Óµ]HueÁ‰“LĽČ@öĘ7,ŐýŇľ¦-~Ť—¶× Śx @2Eźl‰ńřńS‹öG‘®ŮTd 0!Ň2¦ţŃ„9 BĎ;wš1´ŰöŤ? ŚcL¨ëżF"Ą´…v.q–úé+Ś1ĆćÎHň!(ń‘~­ĺęŰŘŐ§č7ŘIf:päÁD۲v@#J‰Ł÷´©“#ýwoűýµt™LV[ŕşĺąĚŚQQ¤c p2‘Qs˝Ú¤˛{­ ¤â¶±[·Ŕ %DZć¦˙4Ć!K·P]X`Á˙4¤—^ö^N+ÎË.*Wú`vkă=0ą¸&a盹\­«ÉJM-WkŽýú±@wÓ\Úţ•  …wë ąâµčą‚ě¤ţ]Âlť\çmüĘÖÍŃŢ?ĐšăÇ-[y9cÉć­ťě‚ţI ˙äĘ3 gOÍxf¦ŻĚNîč@FÎéşcK\—qó˛J*ł33UU§¶~á:ôń<•ćNffyĄćQRNüů©ČŮ»ý†=‰·Ź˙—št\ @¬í—î=8Ç‘gŇýţ-±¶šĽőôęsćl=đlBú?IŮ€¬lŰqlßďńv„Ľ÷Ó¸ŤoDŔ+ÇEÄtĐŻ„Ä-3ňü‚ˇ‘Ř90~H죧L¦%Ł:Kr{öóC)Ç"°Ş‘ÖáńOč©q^ĎĐ1ó_gŚ.ŤâK €dĆL[űŇK‹źěnOČ€áV?˙Âó«žě&9ʸ›fńÜiö˘ăź¸}3qîÉ«×>?ĽK”ÄJá4Ĺšu/=×5:¨ůB#®˙đńő9@@TĚŞ_š=uܨQŁÂ|ť@éÜ€–µhcm©iý4ó[•FĂŇ"°ńťđř˘Ł72öýşaę„ńŃa>00~BŁZxřG.{jÍ Ď­7¬„Ev[ţęůyů¬Z8köcíü\D‹X`X`Ô˘Ic`ŕäŕ™“|6%ŻF,IČ,×őŠę¸açĺńËß,Űřű©žVJ€ÔK§Ó0(*ÎŇéyBLXşľ6Ť Ć”ˇ˝ţűç/U§6:vÁŘęł%Ś &T 6Î.vJťş¬";S®pQ(dŘĘŃĹOa¨ü)ęěL™ÜŮÎŃ&ă|ľM„ű 0ĺ«ćŔíLľ˛đÖíRŰöQ"ş”媵ZZ‘ě´.:ŞC䣥¨ŠóĘĘËĚâ’s‹#JúuŚU§źŰ™Gľ˙d&Ą†Ná1°ď5ż”‘’eđě»ßŻ_6ćŇĹ+ˇź´˛OŃŻľţĽ0éq _úĬ‰Ł‡%fU­~˙‡×—ŚľtńJX§•2cÁ¶Ś g˙čß}ąš8/}|dďÎç Ű÷î&ĺ™jX0wĆĽIăŽÜČ•^=Bísa»aźńiiĘeâľláěQúfq»ötĐć©ÉĽP÷c{§«í·ďÝ„Uźů“'ľžeŢ»%k˝¦1` ŇěhYŤ[ ÂŚ‰öQ‹-ň±Âb‡kŰuüŕ•%É5a_~őyáÍ Ä%|é‚Ůㆠ¸^ę¶qëöP…*»R>kJÜÎCŁŰuşhúP7ő™87Ő¬Ď:•šWŽÜĎýA ,°Ŕ ,°Ŕ Z†äŰ{âŇ·ŤĆň1}bF<ľ®š±ĎO"¶Á7łó§ čľđ•żROü€€(ä¸o:ž–sé/W…_—›™9ui$/ßJŻŢĎßükĂk-Ĺ©;«IO>ĄD€VOžJ\¶ó'75ouRÖł—n-Ůţ‹“Żű?J ź·>'çÝea±ńA«·8$8~¨wüĽX7ë?#9łŘ$˛ËűµÁ†>vóß X™+ÁŞŻŹŢ>ţóťŐHŢÇźxî‡ëv¬űúŹËG¶ştă»çćĚ ]) ‡ŇpnĄţťy#Ŕ1°]P°€uLlܸ ă›÷Bq…fŐ¬>`ף°Ú(Ąq‰ňr· ‹›UYY1Ł·XuÎ.SĎ1hö:Q¨Z·hć‰3o•Tü| R·¨FŔqťz”8—UU-×{ÄÂŹ47|Ľ{L)ŻŞśÔ3pČś×Dľň.ź/րҷߡl¬-5mKš`ŽáđÝŃë?ľ¸AőkQ¬Ö¬šŮÜV ﯀\m9€uččÔĚôŃŃÖĐŞ'{ ,°Ŕ ,°Ŕ Ú‚Ćl$··Ű˙nĂž¤źwy˙©řŞňR­`=÷É7ÂťŚV~Ńq]¬ÜĂfŽéEŁI°ů`ăć1âÔąËT‹źy3ĚŃ ¬M3cL,0]Ţ»ý„ĹŻń÷ †Ža&šîí¦oÜńř7źŞŻěĽu1M® rךň‰úĘÎä )¶.öéçîüv¸bĹ“mDŢÄ‹ keú©Í˝:·9ĺiŻÎćLě ig¶őŽů(ó&÷­}i˙w§ÂÔTăŃY|äËk_ŞD eĂhSŇR)¤s°L_râÔ1ĐdŢÉ*îű͆>{˙ťy3‡bfĐÖž]Bdşb)Ť./˝¨TkemUĄ*Ľ‘XĽŢh4qÎA~Ášň˛>Ó˝´zEyfrJa¨sÔ1˙¨îľýńł÷ß™7sâu:ÎÖ#(¸đöĹB€’äŰĄUŘÚÉ# LS®şË§  Î kŐĆšŻ)B2™Ś«­uëŇh l~ÄDVŻCŻä¶PzfĂ÷‡ćĽúGQaö‡Ď/WЇbR,7ËÝ3 ,°Ŕ ,°Ŕ‚ż‰&Ď$?Á•wžš2ŘÓßwÔÂgyŠs5E…Uy·ŇË,[i's ž9]z×ŢJQŮu 0‘*Ý\CŁB‡ wtR–Ţ)Á)\]C#˙9JŘxG«Â´bĎű2 Śy˘[l—ž¬őđQ ŕD­Fud×Ö#@©Vű/PÚ…„@Ó–÷)ÂNQU‰‹Ó¦×Ľięě]˘QAíD¨ui_’WÎYŮű†‘q0|ÂB_ś9lÎZ Źĺ2R’§â¬¤4’W *ąůCPƨAgÔčMšěĂöŠŤŤĐoÍG›°:W¶ â'ÖăLe2ľ¦Ş°Ŕ%8ŇŔĆŰßÉΎ꫍Uz“ćNźç>ŢÂdÎo~ŇĽŤ5[ÓRť‚{Ž8púĘĆŹžă€ŢS­ ˘PŁ~-ŞŚ1 Ľüä_˙ďo™ţô‹áB.·±·€GŁĘ ,°Ŕ ,°ŕ˙}4Ůw޵˙Č;ëVźľUsí` -kŻü|Eu}+L\ôcěöŐsżţ˛ůÜÉĂS‡w+™”¦$áOéîŽm‡ :Ć Î˙©hÖŰ“tmä‚5·ŻU(\='˝¸"°Wl÷ůóĂz†!™Ďă‡nÍ~o­·=žx⟥(fď8}áÄÔÎAĘĐŢţC»;ú„»˙çżĂWľüMňŤKo®{mĎ©[¬¦ .Üs⢏3RÖżú¨)˘6oBď`InĎ|r ůŘĆ»&i{G'攝ůsÓ¦m* nvôł€»N8Ě\mżŘ—XU”ůâĘgw:˙Ä”¸ľ_0jKß~~őĎÇŻ2Ćž_< ľ>t˝Ş(óĹ•ĎěO- 1“[˛±fkÚ9ĐşŚ]Ě»súW% Ţí[“F+rnűŐĽťëWJ´q kńěăqŕÓ˙BBâkO/~çŰÍšâôÁˇ®@¬ÎÝ.IľxäÓőëbŁ|îť—X`X`-Łůk6ot Ź88˙➥+V§«ô´Íó†ô—/]»-—[ĺd%%&Ąi´•……ů‰Wr UŚeŚç i7®\ľžĘ¨PS^VzřŕľÄäĚfw¤1ĆŚŇî}Ď_Đ=ĚýÜáăÁ3çŠUUq9ç÷âľ!n ®´@S˘ćś˙!Ş+ÍŻ.©9 zń5ß`WŠŐ%yUĹĺÄŢŔ ŻűŘňűusĘ˝ĄŞQÚ<»ş‹Ż-úaS’ÜÚ¶cçÎĆŇkݬyzßĺL*ňÖöícbLŹňÜ3;OÝf _üÚWŻ.™PSśüíO;DŔčľOnĆjJŹźJč{AŤíă㤝ؾiˮ˗®ä•Ůżß s2ĽUVÂŻżn+ެ®,Ę>wń˛Ž ŐÚ˛K.fg$:v٧k·ľÝ:ÜN8ąeŰŢŇr Ô^“SŻ_WÍ%ÎWŻ^ËJ˝zŕHv¶Ę<›ÖĄÓŤŰĎ^şxäřď®]ëř””W1ˇFUZŇ‚Ť5SÓ3)ů’‘—””<°/190¦š’{HŁea úë—ÎßÎ+Bn_«W‹­»/_ľśźSjăáŮg`ś·B˙úËOíą”…1¦¦Ęóç“E‚ä ’tírJ‹˛śŮX`X`<Ú˛=lv*…B1JYËÁÍÍűű÷sĄFŠ«ę×®}üAXSĽw׾Ń;Oşůů FB7Tó5F,·RÚŮ!FI§á f PâőZľĆM)Öu_ń:ŤŃ`•.J;{& ŻŃ F““«•=†2ę´L_9V˙¶5g´±U `&P˘2ąŁZW/Ö|ÓhľŹŽ"éÇŤťŢ1ČůNň•=G.Đ5ßyKAŠ›Đ›ÉîžÁ¦[s~×g„0cÔĘ!ú?߬»txWh݉łĹĹvO.0F÷ó»-5m›4Ú’Ů=ÔęĐ[1X`X`ÍŻjÂA]ŕv„c`$R €Á˘(bÂa ‚HÁ‰śI?7žż"Ä˝w\„€1ąŤí‚ťä¶ö„“!`o@c`”Jyq2„¤ÄTš"LĆ«{GŹ ĆDĘĚ_1Ě)8Ä΀h”+äQA“Ŕ6oI7L†ťÁ((¬mm­¸pţ¨ukKMŰ.ŤĆ"LĆ(ŠDćľxĹ.6(+éâď;ŹJ'ic"-ęD‘2 S*ÖZjř«X`X`Ňűg™wÄŽłIĄEąą%)öGűZ¸®Z˙Ez±*'éT”­Ţ‘wÓśßíg'qđŤ\\siűW2€{ż€6Ç«Yô\AvR˙.áÖvËęŘ5ŇŢ/Č9ŔĎ%*~卂©/ĚňŚőä•ěµW.GöíÖí™{ iG¬­ííävVöv.ť‡†w QŘ;YŮŰ)ě•6 ·A ƬžçÝgäŠËŮk.GôéÚő™/®%/yö1ą“µ‹‹bÔÜn‡O–+ĺ«ßű®˛ş:-=ŁF_ąlÖPP*8ŽĂŇRtŃ˙ ČÉ+zĂžÄŰÇ‘Ľ~Ýżg`é›·ľŢĄ®Pěúú]»:öŤS˘V5(&tš°˘Ş,§‹ Ňă}Ô¸Śm)1BŇ™!jD˝O>m©iŰĄŃ ˙F˛ŕ¬|ŢüęŰÔ‚Ş´“żĘÍĚxlTć˙-ó˛Ŕ ,°Ŕ ,řżŤ b‚Eö::ľło|¸÷ľcqöŮ1C{$$ÓGôI>}¦KŚż#č3tÔÝ4Y§'ŚŽKţz`«Öżę—ÁËÝFŹF+2÷kőj­ębbsňÔ¦ťM»]3műnweţăƧ&ć¸ÉA©ÂÖ^‰Ś@yłöć/=E|ýťU™Uăß]O««¨ÜÁţę›ĎdV9Ś{{'ł*ŘşN¤ ´łW"Ł"ápYzá·żĺŻß8Ć#'uŢşÄöáÜŕé _}zÎÇKă^űáě¸Çćó%Ą`â[(LůęGůŚŃK)tô¸)ĹW÷˝ýăYŘőŰRŮţg—4µJŔr=čF?F”ôë«N?·3Ź|˙ÉLJ ťÂc`ß5Š0@íĂ”Áłď~ż~ŮKŻ„v|ŇĘ>9Dżúúó¤ Ä%|éł&Ž–Uµúý^_2úŇĹ+aťVĘŚŰ2*śýŁ_|÷ĺjâĽôń‘˝;ź3nß»#”gŞaÁÜó&Ť;r#—`,˝EA0*ÚE űü‹OKS.—đe gŹÔ7‹‹Řµď 6OMćĹ„ş/Ű;]mż}ďŢ ¬’řĚź<éđő,ŰđŢ-ŮXë5Ek«4AňgvA?lŮ®,Ď®”Í0ŕÄŮqÚD© Ň…<&r>Ń[öîm§¬,ˇîËćN˛üË÷>ýć˝·7ě» >}†}óÍŰł{íÍ,0B–‹hX`X`€Ć«iBź|ěŕŐÜ—ľÜ{T ÖU·Ž:*(äF@€¤c‡ęŇhRNîůë˛Â·ËúUS_ť?Ůľ˙‹yéa‚¨V%^JÖ§eQÖşŰ'Ę@0čťÚő ŠŮş}ÎH2ąőŇ107ôŮ·®ţúŁkKî”ú(8‘ç)B”R&Š"Ď‹‚Čy3SLŚ2,SčďÜ”ľ*Í-mŻŕLZä«řă§k5îŰUSÄ)jÔe&3—˝>¤«×É]?ü´ó` ˙» óYÄĂxüTęŞ'_yÝWăhBŤRŢŔ&”ŠJ˙ÁO/™üůS“×ţ¸Ď1°ť®†bő¬YŹx8Ř:F|ôŃ‹q±íËřg—MýüÉ kÜçĄĐćŮFŹ“SÝş#~żć™ť{,,:Xé1-ľ«ĎËŢLTÁ{_»fÁ”#+?Ş[3HnjŇĎΙ37ĐÓŃÖ1âÓO×Ćvďęç>9HV١ë1%éđw•jÝÉs‡wözcůz‰Ďę“?ů_Ąľz)YךŤ5_ÓZ˘{JŁ9á!``íÝľ{ű€ŤkźyîóC.Ž6z#"‰ÔĚYşy6nüÜ®^BďČdSDZÚů‰=śĎ§fúu»`VYÂeÁ§˘$ĺD¶,>,°Ŕ ,°Ŕ  Ť§n„`QGĚ\˘ś?itőřý—o–Ě˙ęw;Ŕ<űDŇŰčQ3—ÔĄŮňű†SG¨Ăg·sĺ•~Ńq]¬Ü]gډýu÷9CŃ…á˝Űײż×¬ŤĚÖľđئ̴˛Iżîu!…[ž^•x Ń“c.]OřjŁ.ŰňÄL“Ţ fŇd~ú!ůž†: ÁbŤÎĄŰ _ýhŹKź;˨3Č­™çsr2Ýľř¤ď–/N 8áąíŘ qÖO;/Ŕľż´ř»/2¨©Ć;˘˙2ŹĽÇ§N{â?;•¤Ađ"„é,Ó—ś8u ô™•ĽÜqČ7ľóT2˛ 13hkĎ.!2]±”F——^aâ;v·ŞRŢH,ŃŢh4qÎA~Ášň˛>ÓŤPĘË3“S ‹@]4Ś1Ą˘T÷ ßJś‹ŻÓq¶AÁ…·/ŕäŰĄUŘÚÉ# LS®şË§  Î kŐĆšŻ)B2™ŚQ*­[—FS0J€>ő̆ď/zőŹŮ/hüôźkß«Á ` WÚ+]T…é9ĺâťÄô«töDÖcq±]ü´Ă]7¸Ľ„5"&@ďĺ'Đ ,°Ŕ ,°Ŕ‚ć€ýŽ€@°_„I›˝íČůKG÷Ţ®0u ’ţ*ÔÄ› äY›f_ŠĘĐ=*R_–~#˝|Ѳ%1‘vśCФ1C q }ëÓŻ_}rşô®˝•˘0@d2íť kϰ>ýű ńöv-KÎÁ &ŔDŠ"’<;„ Âäî<¶ŽR÷‹ 1QEŠd2F™›—uY5Śy˘[lI»Ą÷ń¤9i7=ÚÇvôă6ľłjÓák˘(üŰą'X€˘Ş§M®=xÓÔ)Ř»DŁ‚Z€ÚsŚ’ĽrÎĘŢ7,Ś€áúâôč°ČasÖjx,—‘’<gí ĄĚ•Ü%#D(cÔ 3jô&MöŚa{ĹĆĆ č·ćŁÍ yŹ1€ř‰ő8S™ŚŻ©*,p Žt°ńöw˛łŁújc•ޤąSÇ繏·0™sŕ›ź4ocÍÖ´T§€ŕž#śľ˛ńŁç8 ÷”FłRdUľ˛rĽżŻ˙Š÷·LúʼnqŇ!Ó™(2ßd±ŃÖŢŐ ‡@OškWŽÇMóďÜ28ĚÓîČ™«@°%~ŤX`X`Á˘ńކ2 ·.8† üů»Ď>ܰqd”ÇńËW`ýß[µĚÉ-েćď{öüaçP)ÍŹĂ"=o&lٰ®˙~}b{ý~©Šźť˛čuŔĘ»ýÓO-Y~îÂŇŮĂ€ăH­´äçe8ůt|éůŐý&ČÍŐŃIvŕŻ?y§Ţűvnţuăz+*·‚ýŰŇŐçóř°öëüĚÓÍŰXł5ýe×!°wöÔłcź®íA8|p{+ŇhF €műQ§/^^»df·(ľŞ´0O+ýőfâ ßîăvî޶tzßCŰ~38týć‡/?ýá›®âŁG/¦ÜqvvL?ş{ďˇ[Înn)WÎŔ˙đĄG ,°Ŕ ,°Ŕ‚˙u4ľfĂCů)Wog÷čÝ×ŰŽĽűú 6ˇDR–•´÷ŔŃ"µ:çÖŤcÇ÷%¦ĺwďŐÇŰŽĽ÷Ú 6¦€DAd]»őíÖávÂÉ-Űö––k öş´:K˝~] 6#F ”8_˝z-+őę# ŘŮ*ólrX—N7Nl?{éâ‘ăWĽ»v­ăSR^Ĺ„UiI 6ÖLMϤä€ŔKJJŘ—ś SMÉ=¤Ń¨ÁdíâÚ7n·B˙úËOíąt`™É—ŇrU2™ĽüNęÉó§Ź]JŽçc#Ľ¸zÉŽKYL«­¨Rmٵ=!9ą4+y÷î“:ú?ľ’¶Ŕ ,°Ŕ ,řźFóG'Ť\•5ëą Ş˙.[zĂŽ0&SQ ĐŠç†|0f”úµk?dÖďÝ}`ä–ýÎ^~3„5 :geÇqȨŐPŔJ;{ F 8+‚QŻ“B‚0J9k[‚Iݬ´‘qȨŐ0Ŕ2;G…P1\őšŤÜdm#GŚ™ŚbY…qĚô:ýyÔ5ż/˘bť{ĺžBĄwč8něôŽAÎw’Żě9ŇŞżě{Ô Aµ[Bz3ŮÝS€m‹mz—łäaĚĘ!ú?߬»txWh݉łĹĹvO.0Fô~=µĄ¦m“FŰkPŻ.őX!„‘ůj"P±ímĹ ,°Ŕ ,°Ŕ‚ćŃÖça„ĚŇ8™ ¨(Śă őÓÔ='ˇ"eŚBá€QAĽ×Kh„€1ąŤÍÂ]‡ĺ6¶„`â1!ŚŠŚ‚ĚÚ5ékŚcŇKFy’ΙęS}E0„‹5!™€`âöç–4“ äÖJ*ŠLc”Q`@JT0š0Đ0áĂꥑXB¨ŘB4CB8Qâ¦>ńíÇďhÓÎĚť6żűoűÝ<\T©·x¦t b‚‘1Ěɱ:+ť§ŠżC1€-''ę¬4‰‚ŃÎŢJ¦ ę¬tĘ]B‚od€9QßIăEąsh¨śŻ«ŰŽ«ÉÍ®ÖSęPĄ®‰¶]g ý ¶!ĆăÇOL ™˘Ď€A¶˙,ĺ¤Ń$2­űnëŇŃÝ/îýďäŻňę±­Žłg÷°±¶Ô´íŇhP`Ś1Ć -©˘˘H™ą„@©ą„Ś(âÜf=>>ńŘöë™*p íů´Ą¦m—Ć=sk¦„!é†$±î]VµrRwéOaCç¦%_‰±&f)Ý“łX`X`mP8:G´ďŕďĺ6qńmUi/w«±+>7Teµ÷ł í:Ek4Ífßat…^űÂĚXčÔ­öěô•™RŤľfÉřî WzŚ›üŘA˝p÷†0Żj>—št\ sp{⇏­Năţ<7űýE0iŰĺ•}/'văţ<˙h) `ĆÖcűţ·Ă佟Ćm|#^űNŻŘK«L†ů˝BÇĚť16«_ {ě ăçőü§)tÁ¨Î’ÜžýüPĘńźZ÷,פµGxüzjĽËyd HŃ{îš@˛c¦­}éĄEŹOv·'dŔđ «źáůUOvóŚď¦YgÜľwG0)ĎTĂ‚ą3ćMwäF.ÁX”.§a¨h1ěó/>-MąL\—-ś=jPß,.b׾Ú<5™ęľ`lďtµýö˝{°Jâ3ň¤Ă׳lĂ[´±ÖkŠÖVi4ÂQcľ˛nJDľlˇ={8ÔL{wĎ–=űBqY¦Íź5sä Ëú =űöŮVĺTÉ"Ľě‘@×~cV?=ÁJ_xnóîr“ľ]Źaßýgő}»*iČľ˙鍕3M‘ó7,ďu§L´…jppďĺJ§¬ý 0˙ B*Y`X`ç(…6Ď6zśśęÖ-ńű5ĎěÜcaŃÁJŹiń]}ŢXöf˘ ŢűúŰ5 ¦YůQÝšA S“~vÎśąžŽ¶Žź~ş6¶{W?÷ÉA˛’ČÝXŹ)I‡ż«Të†Lž;Ľł×Ë×K|V/třÉř*őŐKÉşÖl¬ůšÖĘÝS-¶Ţţé{Wm µ?±ő¶×ĽAÝg<î5¤Łç{O­O(żűďŇé<‹}äEŃťş‘žÓŻřZňcýă'OžO˝±ďÓډ€^W­HµVg4r´íÓ÷.+ µ?ńçM·UÓűÉŕ+^¤˙Şcq ,°Ŕ ,°Ŕ‚˙Q4?uÉ#§N¶6fmÚq¶üňMż=Ţu$?;]]Q†8™ YĘn/ž>'Ů$ ë‘1q`üś÷?űfňP)MEY’q`,:?ĽwűZĆmŹ1Š­ěËÎüUć1ő› ßšO‘ŘŮ«ÎüV™ŢqĘ׏’ňÍů·‹•ŮÉ2Ďçädş}ńIß-_ť+ĚLzďvC—ú<>uÚâoő+ő~€IDATw+8ÓéĽŰő_ć|çĄ<ńźťJ"…B˙Ą5ŐxGô_ć‘׳!Ň9X¦/9qęč 2+y!¸ăo6|ç©4ddbfĐÖž]Bdşb)Ť./˝ÂÄwěnUĄ*Ľ‘X˘˝Ńhâś<ü‚5ĺe}¦/ˇ”—g&§!¨scJE˙¨îľ•8!^§ăl=‚‚ o_,ŔÉ·K+ްµ“G@¦\u—OA0śÖŞŤ5_S„d2ŁćH1­KŁĹöñr•¦ĘTĄW9Qćč`ŁQ•ő™ľ0^)ŻHż‘¬Şň‰/ľ}Ą €ŢLĘWidśąŃYL0&ś ŔȤŐ !P®ŇTYWé•Q%0©<–ŤX`X`A3hîŮB˘H;Ďš>îÂ_[ŇŐ`‚Ôwžš2ŘÓßwÔÂgML–S–žoŇRsp{fD r€Ş•S‡xůűŽZř,ĎČťŇ\P8żůÉׯ>9]z×ŢJQ¶Cm}Ľ5ĺşKÖ[•ݸcëĺlíáˇÓňí—Ľů¨)IY6^..nʲjóD·Ř ’złÚÇ”»şąüňáÚ7M}Ë« s´*g»šŇ)Ř»DS&IëďLo (Ş*qqpÚt—ł PíĽY:Ç(É+ç¬ě}Ă$?Ú0|ÂB_ś9lÎZ Źĺ2R’§â¬¤4€9 ’»d€eŚtFŤŢ¤Éž1l`ŻŘظýÖ|´™3;bĆ@üÄzś©LĆ×T¸G:Řxű;ŮŮQ}µ±JoŇÜ©ăóÜÇ[€Ě9°%k¶¦Ą:5÷qŕô•Ť=Ç˝§4šłVQ¤ đ†ĘJuZŁFoŇfO:°WďŘqý×µYWa°u÷EÄÝËZ!s­uFŢ`4ęx 2™Â–PŘĘ EJ»Ë™‰BÝĹ3ËşĆ ,°Ŕ ,°  š9«ÁPQč2aN|'ŹŮOý Š űΛ˙ü ůÂľÓžĹWöą’ëŠóÄ5?ţňý•<>ĘőńˇGîŢýŐŤłűúN{VVś¸uçQŕ<˘źyzIĺç÷ľřChů] cŚJÓ;޵Ó0˙vŰÓv|Uľ=SkŃ%ŘYű†ú¤ířŞ|[Ć#ŁTlKݶ ëéctélulOňťUĹV¶ky®xyË’% { ąiP¨›ţÎö#'’Äüëůĺ=űĎŘ4(쟣xr~ŢyĐ,1ÉĽ¨ĽP‘ŔˇŰ®ĺ>Ů«Žó®P{ Ł@•xčŹó9˙¸×㝯zŤž˛ó»“’®ş­~öĄçWűĆ?âábăŕIJöÖ¦ů˛çčiűX˝ŻHôńňŕ°‹«‡§›ü×-?zý̾͛~9š0uîă[ľ~ĺóör‘¦řŇÚ#?/ĂÉç ‰s›«Ł“ěĎżţäß\şoçf•k¨‹•[Á®í?é^9VÇgë†×>űď.kżÎ-ŮXł5ýe×!°wöÔłc6D€Ă··"Ť&0ÇÍ´wr±·±˛&NÎö*ĄŇ!0ŔqßÚŹV>}b˙ćM›Ž&LbŢĎď­řsçŻo­Ű˙ă†Ď„€Ř0?/Ś)€ó3ĎŻčÔ«ŻŻ_Đ‹o}pńĘŤ'’M/óßčé† gçŕä`ceÍśśíUÖÖvn.Nć|ł,l,°Ŕ ,°Ŕ ˇ™k6ҤĐÇŐ=óĆ©_·32@ot Ź88˙➥ËWe¨ő -8v2ˇýˇÁNÜú—VoÚ{u 5§Y±:­LT¨)/+=|p_brfł“1Ś1Ł4˘{źĹóöw?wřHŕcËŁâúU$]­ż¸ÎŐYÁ8›ŢÝ«n]­}żřŞ›ł\äěCz÷¨Ľ• Zű~qť‹łBäě.E°öŹ{ń5wYKč×Ă%ďv…ÁÚnŐš®ţ.Ę˙~yüŘÉ«Ý⇻p%«—¬8›V zŐ‰3W» îúPNžIč6l¨W´fŮ“gnČżöŐ«K&Ô'űÓ0B÷;Ďe#Ş)=~*ˇóˇ.¸hő’ĺçn"ŚYý(1Žěßg”;Ţż23a˶ż.ź˝ ĆöńńŇNlß´e×ĺKWňŠĘŹěßo9 Ţż*+á×_·UVWeź»xYG…jmŮĄ ł3’»ěÓµ[ßnn'śÜ˛moiąjŹś¤ŐYęőëj°1b ÄůęŐkY©WIŔÎV™g“ĂştşqbűŮKŹżâݵkź’ň*&Ô¨JKZ°±fjz&%ŢXRRrđŔľÄäLŔjJî!Ť¦BŕŤ5éI )9yąŮÉW“Ósó2Ź=tčŘeżîÝűtmźrĺřŽ=Gsn%žI*ŠW”tňŹ?·ž=w®XMúę/¨˛ö8*XŮŠZŐńcűŻg¨ čUzęĎ-ť;w® °čNʵ[ŮąąŮÉ×nedfŢşr-Ųś±Ŕ ,°Ŕ ,h­żÍ@& ÍĆ?[F /Ç´ćî¬aQę×®}üAXSĽw÷‘›÷9¸yȬ”I[e2‰<¶Ć2…ÜĆđ:­Ŕór¦Çś\ag‹€ńŐZž€1,W*íl0“VĂ›xhDA!™BacŤL:ŤŔ‹RěE…­5bf 0†e ‰ł±ş›´ăôo[qF[fŞJ+LcgśĐé…ĺÝ6`€ăĆNďä|'ůĘž#hcÉ·ťW ´Tť&ôf˛»§(Úf w9#„ŁVŃ˙ůfÝĄĂ»B{Mś=8(.¶{r‰€1˘÷t´-5m›4ں놠í¬T‡X`X`@K«„pÝSn„1PĘć„ć˝ĘbB4—!ŽpŔ¨ Š÷* Ćä6¶‹÷—ŰX  @ś\Ži°á ˘&N†1€‰§Ë3ĺ±ËQ`ŚŠÂsĺyjöډLŔ(o’fŔc"“1QËä„ĂL©H€Š (“P&E©'|(€ůŞÇŢ$ü±%Ýh¤É*FaŚ@rKFÍ„c„`*đ 0Çq˘Ŕ3@„#ő(&¸…˘ŔX]D8†ĂY3™€(°šţĎ­é<˙€wĎ«#I`Fi «Ś1EŚR „`éWŚĄ"Ą¬Iš:Fă”c‚€‰”Â1&6ł2A`3g‚±(đ9Ż^÷b|ż®†Ş¬Ďßx÷ĐŐt).gc>÷´±Ć5eŚ1„0áĐÚŻÚ&Ť&–Ď!F™yŐ†0†şg3@aŚRʤ‚!‰‚Hp©+›T©`a) `Śk‰łX`X`őŃÜŞFZ]8ú ÔŹÔ”8rÂČSŚ€2Ńło—`Ż›ĎßČĘL#cşE…V«óŹź<ß|„€!€ŠbóqB8Qâ¦>ńíÇďhÓĎĚť:ŻÓ7ż9{`N„E´Óe_ÚËô9ŮŐ5”„YEĺ6Ů*ŤV‘,łóň“N§®R:8O‘3VV@¬ĄI!jÔTr¶NVvÖšś u‰ÚŢ'ÄÎŐVD…µ•&·ŽbŁW• "ĹbLü?ě˝wXUÇöř˝fö>•ŢEQQÄ^˘"ö¬1jě=1‰ĆÄôÄ›zÍMÔb‰±Ĺ†€ ±7DńŇDz9”sŕÔ˝÷ĚűÇl€ÄÜ÷ůĺ~ł?˙$,gŻ=łfÎóĚÚ3k-ą€¤ś `Ě%b¤öö2…˝]˝í¨IS)PFagŹë˘Ë‰ąF‹X‰ €ÜĆ#bÔjÔšÎh¶Ź$ Í&"·H4Z$U2k´µR†NA_Y3úĽ‡:˝€ÚűŰ×TÖö;±á›=+&öľ˝=bţ{\]]™™{$‘ő4Ć5ĹÇź7š›ô”†ĹQJA †±”´¸+Ź·Žăě}¦†őÚµ«§,ĄĽĹŐD?&âUęZ<©ą~U"„€ÔíűźŇš_c-iË­ńĚĎ3 J© ŚÄuáňąÎVřÁÝ{ŹžP„1JP`LPď5ˇ'˙ůË ŚŔ¶M—S73Jňs‹+Ô§ţĘV~ňCu­>7Ż ř~BźˇŻ¨µ˛ł+޵ç|Űš}˘MQÚµ®VŔĎ«†Î°,Ś^řfaîťÝ:8şŰui×yĆú·T9oßË6c˘gďŃWSVFÎô–:(ťśĺ#^ ş”Ľ|ѶŹeJ©ÂÉEŞP´ ›şčř1OOŹÉ‡.Ľu+mMRć˛Nv.¶¶®ÓŹ]z39mMrÖ˛¨ßÜüçĆßűćB©Lé(µm=ć§ÓŇł‡ż>O&·ňî,µk3ń§?Bř±VJ[©ŤťÂÖƩ۰Ý}ĺ¶¶V¶Vrkk»ö˝ś[O>y3uuBÚ’c{¬¬”ŽSť‹LH]ťľřŕŻ.m{ ś3ÁŁ˙če7˛ßľ™ĐˇW§ďůÁ\çáËrž” [v=ë­×ۇvěńŢţ™6H®µďݵۊo’nĎ_5ŐCb#µ·cFĚ >ur˘BĆÚşvú1&9ýÜo–¬_ľâĽĺ«O¶DUUŞ ‹ ٶ|nc‘7Ň5«č‰`†€®—kĘv·bëzşŹ-é1B`9™i¤ăBOKFÚrk4˘˙)[° ĎŹż˙ń~ˇ&ăÂiťň'2 >Őçżšź[DDDDDDDDäŮh@`ěäĄýŰ1˝‚:]ÇÜşşgüŢ­‡J;ĽżúŐĎç‡}ţűŐŔnˇ%  ŚÄצ„ź‰żŢvđŇ´¸ď†ő˙÷•Ô·éRB±ÎăLJ“Ęěřf#@(ÔÖT$ÜşŹÝ[· î|őć-ť±cÔą÷îş÷ź_űđÚÖ=ůíç–—1çÝ;CÇöS=q ťµëcć®YŻĄÜčmď Őe:ľ•ÖD”¦ň"ÎŢ]))«.Żőp–šŔĘT^Č9µ3d]=őőďý˙µŁÇŔ‡—ŁgeEň©3›÷ ţú×ÁyWbŔÚşß»ű))źĽ‘­±›đůG¤V@¤v¶·>^ť]m;á‹ĎQUfĚš×´UőÔsDÉU• έm$eÚ*#u×s`ĹkĘͶ­•vÖ„+Ŭc–đ„„Ą€¶NBfÂŚÂŢQ‰Ś c‚0«°wT"!$‘K+”ýt äăcÝŢźó^JźŢîvÖ¸¤ě^zNĹ€@áĹľďc†!ß~Ä´Ő‹†/ëÓYÝůŐ¨źßťz|ßO±) ůÇN<˘$ĆNęÔ®"˙ţŃŁGĘ´0hř¸Đŕ Ż‹Ť:śšY1ĘZÚTfíÝąź7ŠK+‚†LŘ6őÔńSÉ÷¨ÔŮ÷•i^v(účÁ$ŐF"m;0|LĎŕ€ͤM§ŕ ŁËłT:.ăöµĚ‚J™‹ßŚ©/?Ň€$.c›Xc-iË­ń–!¸yw|yâ[Q%_;wĚ…–-T›˝q L©ŕܦăËăGŰ1úă‡ö¦UYOŮëúÉ#ůZ°Äcâ¸>·.Ë.ý©%"""""""ň€§˝Ë*;÷‡Ş+iůˇ4éAumg﮺ÖÁÚâLYÇQ‡­ţýÇÝňďź+qęÜ©KÇîm*KóV ¬-ĽßĐF•bY»^?í˙Msq÷éřë¦fĆ1f śŢäůR=üeuyµŚ-(άéóYżŚ‹żYwtâ/ď«|HM«qőó"ÚĘrŐťŞędŽa\ýÚ™śĽő••×6mĚćB† upîŕ)g]ąúÚń÷˛ą!m\ÚŰ—_¸čüRȨȬ »‘ň—–ěÜ#¬űČȬ żĐ\]¦×¬[9Ër‰IníęĆł”¬ts’+äŁ\éä"Sšen>úŠĘk›Ţkâ]ÖöžÖJk%´®j˝ĺú˛@ÔI€–Hô%™ń§¬@]]\jĂ0aő%ůIUa©›•<űvˇU€ëů{_ŔÝ´ZKŚ #aĐ‹~čÇ€A}«2ŻËg~ţf!Ć®í!6… @ꛡđĆç?´tlÂŤÄvA+¬ ü›Óäű-›‹RŻ3Ní—Ě›9iLxrŽ&ň_ż|°xLÂŤD˙®+%¦ÂCY•ŽŢť×ľˇ–±÷[2kTźnWŤ>‡cŽú1ŮU0Îô×^w'ŹÁX¨‹b(lÂ7÷mYÚMĆ©ýŇŻŽŇ?‡ Š=eW“_ĹĽÜÎuţ¸>™U¶‡cb|±Ú˘gnÄËgnçX·ďÓÔk~¤Q -µĆÓż„)%`ăűËÁCíĺąŐ’éťż2ľĆ@€‘ËË…<*°žťĆÄtW—ץs&N^öźMßnÝ4?}[ě]đěľuë§ŻöŽÉ.ĺ1BâE4‘ŕi݆ŠŠŇÚ†.Ő/o;ľť«Ăqđ”xřuÖ9ő®V8{fθ;cît2jĎ_´÷qSĹď‹ĎÔLÎş· ÖYei3{ě€]±©H[•ś ŇgäXęu6źč‰P8“ŤW'Oš›V¤”]!nţmZjÍ~Ţň};S nnĂ^’kjĚ­$2ˇ"űöľźLąI˝ťG{µŇUw÷ľY•]!8—Ůkě]}dČ MIŻ“ŘŮŮŮą˛LąÜFʲTuŕçŚ+÷»˝¶Ś‘K6‹$ózfŔľ=ŰóF!„ ‚Ŕq/PJÎL ˇ„&aĄ6vžµ©÷MMĽ«ÜŢÖÎÎS˘0ˇˇöK}©ĄÚ(Ą„b©L—§şµçW9®)Í+÷•0H"m”ĺ•ĘXs ńm-Ű·3Ĺŕć:ÂÝVSĂ1¸ÎV¤®Ň¬x÷Öú;g“ ré‹a†Aî=tŐâÍŻG¬ű5Öާ®…’Ş™3gµqłł¶řę«őű&—so,ť˛yĹÄużĆ:¶í$«É·î<^JtçŹÜ›âž›ďßŮOî6uDç‡K?NVæ-?®ť?9nĺW >Ą,Ś!óĘěŮs|Üí­íľýv]ßĐ/×_IiÇ.=hĎÉ©g¶WWéÂ"ć ďÖęĂeYôDÎůĚŠ/8MŐ­•®ą5ÖřHë˝ô\k4f<”ˇmv¬[ýććÓNöVzb0HťfËÍłńć„´âűt V™22®Męéxí~vkßîăćĎ(IşÉ{V–¦ťĎ5@‹<ĂÓ[7A pxďöma}wEĹäfVU–#V"A cyú˘iłUfI»ĐĚ!AývĆÜI»ôGźŕ˝†ÍÜýăşé/u7š¨ą<َÍĐ®ývŦJo ďXŻţ9»6@yޱwwoשúÖą,ćˇk».6vR$a˛Żĺ>ČpţîŰţ{ľç(ÎŔ¶éţńwNÖ†…3Żţr¬cG˙ó;Ž8÷مѧ]Lń}eŤ'{÷üÎcÉ˝K·Ű˝‰Ëbx`QŚřě3G­ú»Če‹$çĚQěŘÉPW YzcÉ·Eëc! ČćÚÄ».Ţö™>Ša."†Ą„PJĆ( Ŕ,Ó xΩ[ŘÄďwŘŕňs¦šMˇ‹Ä—íť3Ó¤3J•˛ěkf»|÷Mż˝˙ľREęâ7ţz<1<.uËź5e꼎ə'Š!„(@Űn~}éů‹ń /Ě®ćxż °­Ű¶»ËŤYąEk Ľ{÷¶]‰ĄŤ.?łŇĚ…*4ę˘;ÉĹ ŘšLfÖŃ×ÍËO[QŢoÚ‘riE¶*­¨ACš=Ŕ"xw ÝöŁEs1ât:ÖÚÍׯ(ýFVĄ—Uj°ŇÁ­Ťż¶BýHOa!0^ ovŤ5>R„$ %Ärc­yk< %čď_Ţöó™…ďí{őmíÉť?,_·É€€ĆlĺNę˘Ě‡ÂäĚB…]9źóĘŕľÝ˝uŔ!˛;l~Ňy€ b–3‘?-°D¬~đúäˇîŢ­G/xĂL%Ë3 Ě5¤®”=ĺŽ ˇ¦Fu°Ô$éäďŁÖ”5´±IKśÚ~üÍ–÷VLłÄµ7Ó @0˘f#j?¬ąęüý»Y^aaÔ sń°.Őбózôőe3îÖ¸yĘž2rąÜÎŢÚŁµŤ«}MŮű ewĺ­‚Űx8¤):´uîä]VpWŃ*¸Ť‡SqAš˘c;ç6žĺwŇÔEÜ ·> ™¦Ż4ą´-ż}O]Č zëÓ‰Ă̵&Ś@Ay´C¦ő ‚‰:·+}x[ŃÄ»Š Ň­»´•Iä3'µw´r°§Ľ‰‘I%J[ń‘„UČ€"F‚¨ŔA@#„-A H"ˇ„ş´R–×Býصî­ĺ„'@˙‚[C@±¦ÔÉÎa÷—ëNÝ5wőó(ŐŞˇţ "Ôźc”ćW° ŰÖţ>ŔHX>qAkśŮŮżcřěuZK%LiľšUÚYÚf€XŇ%#ÄJ‰QgŇęÍÚÜéá/őîŰwđ kż:@€Ömâ1¦0bŇcš‰DÂ4E…N~í¬<ĽllľÖ¤Ń›µôĽůőA qôijŤ5:Ň2]řőyňR⎯Ţd<׍Z‘ TýîĘ Ţ­˝—˙ëŕ´Uë' ncÉĘFuf‚ę2´ Řdmël‹ťŹ§;ڔij~§ş îífŚíwů0X¬X#"""""""ň‚<íŐX\ąm·c1§ŢŹ\ľç·C¸0ńD\â•ó'óŻ_űůËvŚčâ|8îôÄů_¦ÝľöŃ{ďGÇź ˛3&&_ąw˘ ®ÍŻ#::Ƨ\ą{ŕęU‹ÍX÷yĽ‰˝8Ą”đPŚŁŢ1xű0ˇ0“ëÚˇ¶JçäŐÝO­š=ďL™µőŘžö:ĺ˛ośx{Ůo3&]ŹľćŰo¸•ťضďćŕŽ6R;×Ç%6 Ië1ݏŮköÍšrtŐňÔčóe˝ĆÎpµÓě›=ĺčŞĺwŽÇËíí‰ žđÂŁâ'ę%!*đr§V¨™wŮŮČě; B—Ž®\|dÍâiµľť˝lüű˘Ě:IŽJëŇÁTcŕĚJQO9B8Ây‹„rzŁÖääÜFV?vŰ1=u¨‘«s3˙¸Cď8(öŔîËVť»z}É«ĂŐl±řůYžAYcĎV_g{ÉÉ#ű9‡>±ÇěŮń‘“‚HpâđNÝăzfŤĄW·¦ÖXŁ#ý-ę4Ř:¶Ň+¨_H gNnĆĎ‚0ÖŁ/ݸąnńŚťĽ9MYQ~Ťĺ_ď&źo:ţŘńCK¦ő?}čwŁ]ČÖ_ţóí/[{: 'Ďž˝‘öŔŃŃ>óěńÓ÷]\ŇŻ˙F "‘$Ť_łá9“KŰö_Zp#zɲ5YUz¨)Śż6ĚĎýx}äţ¸D*pJ;ŰŔŕ`sYĘ»o®rwÔ5 púŠň˛3§b“UŮŤ~ŠĆSBBű-š» g{×kgĎ{Ośníč"ł·qď9Č»kuíŮŰBóÓ+MJ›7Ö„´qVŢCý°\Ž;zö{ăťN‚G­¬íýô˛rrcmĽÚô–Éĺ Ą]ťÄÖŰ'¤‹Tn0|0­Î*}Pâ7|JŻ©#(‘µ"Te•dű ‹ť6ń& @ÍśŤ—ź“Ź71›(Ą´A"Ť‘)•6mú‡*\ekďî]¤R©\năc‘XµöęÚIéěéÔˇ<ýŽWô^ą®M{W¤piÓ- <íŽWôYń¶o'/“ˇVŮĘÇÍĎO°ÔÁżŁť“LęäĺÖÖ' ™S۶ˇn™}‚móďUäVk"CZ[Ó­{Ě_żůý%“ô%Şw#ôgżňSŚŃ–ť»Ô-l.Ž\ĽějzÂ>Ż€9îD¬Ij6|`uvŇÁCGn^ą^…mGŚ”qţđîQ7ó‹+âNś0Jě†Ôä$íŮs¨¸ş¶ş8÷ꍛ:Â×Ö”'\ż‘›•z:ţ¦gHŹţ=ş¤']8x(¦¬B ő×Ĺ,ŢŮýŰ·«ŔjäČ—,šoÝJÉąëd\vTd_Qůwďzçüá+ 7âÎ%z„„4č)­ĐPŢ .+mbŤ52ŇËi–E^ZZzędl˛*0&ÚŇçXŁ1Ѭtrî?x‡L˙Á†×ŁÂ4[•‘§–H¤î_¸v)>AŐwĐ`O+~}ä⣠9´¦¦RŁ>u8IĄ*ËQ?~AG@<©yaZRŁ.´äÉ€…gr4[‚=žČp†Z˛SCSBĽ:Ž‚µ%1ÇOŽ>xÂĘÎŽUXł×ŐčĄ`ŻűDÁš¬mäŔPkĐšeŃö 7–E@‰ą¶Ćl0"†•ŮÚp5ZŚÜFiŇh0r»:‰ÂFaÔÖ‰ ±2©µ5BTŕtµ6VF"•XY#LĄćÚP „QZ3uzKú”aą­­IS%Ł´ł6kµ`Ĺă’­ŔSVi%‘K‚`Ňs`Ź$f˝QŘHXd¨ŃR@r;ŕô<•ĘdXŻŐ ©ŤŚŻ®Ţh%ĺJ Ę™HyĄa`xTĎÁCÚąä¨nFÇ5›/űy3űřś6•Vřy#Ż{nJâć’ß5¦Ů’aLa×ů‡­ÎDµë=éŐˇľű†ŞJyŚůł©Z2Ň–YŁĺ#xl,Ź©BŁş«iÁ€¤…ż‘¦x‘ŕ Ě0TfY™ÍbÔTYw@eX „ž ŤP*UZ-:q^nmm6č!ĚĘ$ iÇ]C‚‘ă(B€ŚYYŰ‹ă(„bXFÂPbć(B(ˇŔJĄOJLŠý —1łŚšÍ”ŕ·=w fPĘ$”Ď™9b ŔHä,¦›Ě|cJ(V*@)g2r}VÂ` Ë%xÎĚ“§%Á $±RČ€3L‚@0Ăy\ăcfaYĘó˘O#"""""""ňW@ŤI(–ŘvîÜNÎ"@¬I_¬ş—'ăÓ)¸G§vµUç.\3qÄňő]áęîíl“ť–ÉSŔŰNťÚ)$uOÝ˝—G( †aHSnð‚Ŕž2ďÇŻ?«É¸üÚôy˙ŮíäŃZÂbB1ĺMúZ‡”ŚŇJ.—‚(o6ÔÖȨU*ĺr!ň&Cm-bµyJDŕąę˛Šm¶ýĂÜZ˝€;´·­(7-Z‘iŇVçWW€ťť›˝•Á×=¬¬úHěÝím´Fĺ«K*ĘŔĘÎÍÓŮ.#űÁ{?\:¶×ŤíóßăęęĘĽČÜ#‰¬ß !6¬)>ţĽŃܤ§Ä0,ĆR"ĄŔ0Ś%xŲ/Ľ pgď45¬GÔ®]Ĺďů§oĆünĂ0›vŽßńaĽ˙ÍK.íjö~˛8|Ę;ÄT6¬Ł XűÝ)®ýýo)Ś%áA^»˝±ůtÚąťÍg–k ‹ďŃ~Ä<=1˝Ö«ÝŘąPJćŹ –y"-BH2hěÔuSpV„«-Ŕ >1ň­·ßZł˘‹ż'`ü¨Í˘9Smt1/ýnňś±‘ëŢŢ˝“E•ĚŮwîňµßy3¤łoăťFěŔá× m:ŻY˙ά)FŹíßÚä.~OčŔMݱ–Ś´ĺÖxB3č1hÎü…‹ćÍ5rš5+Ú{Ú€Üő‰@›NÝ׼µvö«ËST‹Ć…@PŹđ .›?ŐIŠ@ÖŞÝ„‰ăě$˙ň¤vŢîľ]ű˝¶`Qf»™i‘&ug5ÇdU\8ľ˙űĎ>ěÓÁ#qęÜ©KÄšMyŮ“zyZ˛Bťłş8ď^Ą¬;Ž}ú)eÇ1ĺÍ:·KĐč'éŻ&SuQÉ–ĘWݸ7wűgV­*Ějٸżńů.ť¶ęÜ™3ůĄ%«g ÷wsňO;xöňíÜ{7şűŮ@äżv[Ú”•.źŢżUčÄ2­)C•’śž[ť§ęë)§ö17îĄ%^Š>s)?óVX7XnX5¬:›ŽŁR2r-š¦'zH•ŢA·sKsSoÝˢfíĽťÁĘ'6!­Aϰ®~`Óy\Sk¬ů‘"„ Ză© ‹Ţܧ«,¸ź]Pť^XQ˝˙Ăů t?žnéaaöťAś•^ˇ©ąĺn'¦¤e«KćŤîŻ­ţîţĂ‚Ľ´„n¶Jż´Ľ8§« @PX]łhÖŘ×>;bÖ×iVWül ¦©ţüŁiüS4Ś©) ŞśŇ^ÓÇž‰îćç :ą˙ř™ß˙őf±ęú‰…Ŕ0 “˛¸~‰ÉOűşŐT&'¨R3r,Ĺ ›OőD †1×hZő‹đnEj ž!“†rµµś®¶Uż/Ź”h ž=&á4^Ŕľ­eűv¦Ü\G ó¨Ömlĺע¶¤•šěQńŽÍX,r)jKz™ůo)9 P_%óŻÔUšď~ĐZçlR–\úÄŽa†P{]µ8bóëŻ 6¬KŻAG/gAIŇĚ™łľß±ç—ź÷ĘÝ;î6=ßX:ĹŇ&¨ĎŕCgo[[[I‰năü‘Ý»M¬Vxřwö2júĎ}?nٶeKŤ­˙Úů“€ şĺg) cČĽ2{ö‹fe«}CC źí+)íץÇč9ë* ¤şJ1gx·V z"çż ś¦ęÖsÖXă#Ą”>Öş9k4+Á‡żÝ´íĐÍ;g¶Ľ÷ŃVĎŕĐéł„ąďßľuŰ–­µöţK¦M;r‚§´¸o×cćĽ[Ke–<Öż~łbü’xFF©çtµ:€©­Ń™Śf©Ş×üÎű?   OkDDDDDDDDžĺi݆j3ĎN7néë+ű XĄl3<,2.ýŃ'8pÔäU­ş ™ór8¨ŰŹ"ËÖP÷äS#ÂzWvcxźŔ‰‹ŢçŕyÉr©Ť]aüľÜd#/H‰şŔZYI¬¬‹â÷=|qIţ­¨ VĘd_ËýýLĺňAVG6čL#^ďŇJŞŹ™‹'ńz űßS23őÖúKłÁ#`ŕŇ7¬{§ÉäĚĹ‹,@m»ůIôĄç/Ć€ľ0űANˇ_Pß­Ű~ů÷ż>{mĆ0LŤ5Ţ˝{[‰®ÄŇF—źY\VŁP*4ę˘;ÉĹŔéM&3ëčëćĺ§­(ď7má;‘Ë+˛UiEĹ ˇŚ%ŕŢ»Sč¶ý÷ż>{mF8ât:ÖÚÍׯ(ýF@©*˝¬R•nmüµęGz €±đjxłk¬ń‘"$‘HŘúQ7oŤ¦`YZˇÖj*5z3+HěÝŰtĐŞËűM[đÎÚe•™wTjŤg@ű’ôÄr€˘»©j­¤ţüGÁ`c†•µxc &€1 ÔkĆ”ČZú#& i„Ć?HcŚ,źĎ)'<*D(5u\ÔĄßlhçpxž93W—䪮‡ĺ)„HśÚľ˙öjsÎ…OżŰÇ7[—P H˘` q_~ÓqţÇ]śKö~öQŔđ87EÉé/ľż©‹Sń IŠö|úIŻ ˝{ůŘ–jčÂ7{ôőe?Ľ§ëĚË}şóŐşč^?–í÷Ç÷˙Úµ/ęVŤ÷·_­ŹţayTÎßNrđ»M»Ç%gUÂ_ŰŢR@P¬)u˛sřúť™§îš7űyתŐ+¶ś•ćW° ŰÖţ>ťÎHXŕřá´Ć™üĂ =3’cĄ¦4_Í*í,mł±¤KF!”ŁÎ¤Ő›µąÓĂĂ*x`%Ňş¤mT¨_o”1©Asݬ”h gĐ:ůŰŢ66D_kŇčÍÚÓÇYô`D)€ÄŃgă†ČF×XŁ#ŤÖU€_Ż‘Űľü¨řúľů‘źńĎłFc«Őň› ŔsĆęjÁAWcŇsMî´aĂ*x)d&iц±Ö®­ăÚJ)“đ|ݨu&Îh2éy8 ‰Ěš`­Ą B(}¤™ ĽŔ?ůf‘¦aX łWýűîÍs×nĽp7OS|·ŤťręŇo3Ó’>zďýč‹÷HMÁÄŢmŹ6˙š”šY[Sťtĺě̡§Îýä^ňEËS•ůwşµs› I:J Żío>®fÔüµé·ăd 'ç^#şŘ¶¸ěĆĂ ©·Úumă;fŐ ™aÖľ}–%üI°ź÷ĐąWŻĚéî+k×Ç{XOűVmťţ}â®ÓwŞÜhoçŇŐ7ŹţüÝŢËÚ‚ÄżĄ¤ŐŮ{÷NíPŔßśRĹďx±¸KJ.ěŃ9ůaůĺý»wş^]x7ČËpĂ…Â:­ÖßĹ&kŠł×Ż|#ęôµy“÷źô¶©¦ěÓ·"wť»E)}kŃhŘrú¶¦8{ýĘŐÇN__<­ż÷ 9FmIW9€$°Ě@×-“şőzPÁÝ8Ľ{ŲU—n&Żś;X–©_ Ś›÷‰®ş¤AóŞŮžŠŞ…kÇż’ĚéÔÓ_ň·ň꟣67čy}ţ8° ŽhjŤ5:Ňn>öĐ}Ü"JéK{ä€ńlÎĎĐWłéhň/«ç-Ü·còµŰrnţáăß'˝Ô”px÷Še«®$ß^4u€}—jŽţ¶íß;Nޤ”.ŕ¸ú­÷v9­ÓVěŢĽyů¬QÖŢ}sŞŚŃż˙´mĎ9Jéě)ŁÖţr~÷Ú…ÍKÖüPzRŇÄ/HDDDDDDDDäék6€R0óÔĹŻ]·nËnť[łňŤŰyeoVÚÚ›ËRŢ}sŐ‘KŔJŰ´m«ÎIŤ9y¶DSť•śtçA[‡ŽÝJoť[łňŤ÷ €púŠň˛3§b“UŮŤ~eĆSBBű-š» ´C«k11ŠnaÝŻvsfMĽÜ3(ŔTRJXű ą \ť$FłÔ#°˝±¸˛ŽAóZ,᤭:µ3—Wőě¨ŇŰ>WĄ6Č­V­îÚÖŐáĆőÂ-ź|q=§ &éZ ±•רłřä‹ëŮCImrB*‘đ·“S¦ľµůĂ%“ô%Şw#ôg?ŕSŚŃ–ť»Ô-l.Ž\ĽějzÂ>^%!s܉X“Ô>lřŔę줇ŽÜĽr˝ ŰŽ1(ăüáÝŁn&$ćWÄť8a”Ř… ¨ÉIÚłçPqumuqîŐ7u„Ż­)O¸~#7+őtüMĎý{tIOşpđPLY…ꯋQp˙öí*°9ň%‹ć[·Rrîß:—„ŮWTţÝ»Ţ9řJÂŤ¸s‰!! zJ+4”7¨ËJ›XcŤŚôrZđś©´´ôÔÉŘdU6`L´ĄĎ±ĆłFŕL†ĚÔ¤´‡ůyąŞ[ŞĚĽü츳§OÇßô í–xîhôه÷’/§Ź\śzaßţ?®\˝ZRĹô2WçÄś<Ë+¬…őąř·łŞ ę]t˙âţG®^˝ZXTü -ĺ^n^^®*ĺ^Vvö˝Ä”4ńśFDDDDDDD¤QZôá#ôt@„ĐóÖyŞŃ`L ńę8"lÖ–Ä?9éĚuĄ„1čô!,UČmJ{'bĐ™Lf€e ™•(ô5&×2 E2[%?¬čMc¶˛‘!JÍFˇT­q¤VÇł,C,›X„ĆŇżˇ„PJ)ŕÁă¦ů:>P%FÇ]'ÍŢëkÖôčń ”'˙‚¦ĺŤĽ®©g_/-HođH3BR˘°ëüĂÖŤ g˘ÚőžôęPßÁ}CUĄ<Ćü٢Ł-iˬтW==Ň©zŃ9¦Ľšgwfa1˘–Ě0”ĎnÔ†*XvâcJ)Ąb(ááy}A@©ÔĘjÁńł {KŢ„§×]ÚţŮdfY–P"P‚0˘–zěĐ Á„  @Ě„"Ŕń.4¸Ăëb٧fb „˛–6•…Y{wîç †âŇŠ !†¶M=uüTň=*uö}eZ„—Š>z0Iő Ç±ĂÇô hĐ @Út Ž0şX<&áF˘וSᡬJGďÎë?ßPËŘű-™5ŞO·«FźĂ1GýŠě*?gúk/ŹŹ»“Ç`ląű‡0C‰`ľů»oËŇn2Ní—.xuôţ9l@Tě)»šü*ćµŕv®óÇőɬ˛=ă‹Ő=s#^>s;Çş}“k¬ů‘"D¶ÔO˙D%Ć-xwă䀂*°†Z°wďigúyôÁčŘv¸<» Îť9cÔ›zßčŘXkÍCŤÔ. •ŤĹM 06rŐD…ľčęăf}‡žáŰ ‹ŤŞ&mřyç‡+g;Îݶ¬÷rÁjÁε·3™Ľî{Ŕ 4ň)"""""""ňʤ‰*ś‚15%ˇ$§´×´ĹŻÎžÔˇ‡ŢÄWi´+Ţý µţÎŮ$\ĘÇ ~g ü%ňC(/M÷ ť?Ş_0ßv|;‡«„ęŠä•>#‡Xę§7!Ŕ Gpf,×xbÔÇXE ţJ,¶úo|łGę*ÍSsúčß0C ÷şjqÄć×#ÖýkďÓÁ×BIŐĚ™łÚ¸ŮYŰ|őŐúÁ}“Ëą7–NŮĽbâş_cŰv’Őä[w/%şŤóGîMqĎÍ‹÷ďě'w›:"ÄóĂĄ'«aÓ–×Îź·ň«źÁRňŐyeöě9>îöÖöß~»®oh—k„ݤ´c—´çäÔ3Ű««tas†wkő᲏,z"çż|fĹś¦ęV‚J×Ük|¤ő6DϵF“ż >üí¦[ÖC†Ůž˙#˝ŐkCB§ĎjäľéőŹ’ĘáËí?-™6Ń˝ŘÇSZÜąk¦×´”“[,y¬ýfŵűwbż]N©çtµ:€©­Ń™Śf™úvÓMů a¶ç÷ßuY3m€ľçňÂÉŮDDDDDDDDţóôÖÍ’y¬6óěäqž“|±?űîÉá} «*<;M]ę’;kĘÔy?“3u…nFM‰Pšrv=Ű:ŕĄ^»˘â r3+ËË‘„Sńµá}Ô7ż{¬¤%@Y¶őő‘–ŰEu1őýĂ$O[ëE!fGŔŔĄnůOÍiĂP€¶Ýü$úŇóă@_]Íń~Aa[·mw—łr‹05Öx÷îm%şK]~fĄ™ UhÔEw’‹A°5™Ě¬ŁŻ›—ź¶˘Ľß´…#ĺŇŠlUZQ1h¨1&DđîşíG‹ćbÄét¬µ›Ż_QúŤ"¬J/«Ô`Ą[m…ú‘žÂB`,ĽŢěk|¤I$jń¤źgŤ¦`YZˇÖjĚ˝ś$öîm¬´ęň~ÓŚK+3ď¨ÔĎ€ö%é‰ĺänjZ+aë~t 33¬ŔD-ŢĆÄ0PˇÖj”˝S"Ę4,‘§hâ¬#ËçsĘ „Ă‹«Kí­lżzkÝ©»ćÍ~Çk*¤.3§Ťż~dkfAU^ź>̢#J$Ž>7D6şĆi´® üzŤÜöĺGĹ×÷ÍŹüŚž51aÝĽŕ9cuµŕ «1i‰ą&wÚ°a<Č2“Á´hĂXk×Ö€qmĄ”I4ëLśŃdŇóp$™5 ŔZK,„PúH3ř†‹g˘_#"""""""ň ř©żŔ«+ľąs#năÚŤgϰŐ?ĽtíVâĺ· *zśľű·ßÝŚ÷DÇ@ŹqŻŽčęöű=€ łív,ćÔű‘Ë÷üvHR’üDZłŔşu^˝jń˘ë>Ź7qŔ@)%<PWŽĐ’w‹„ÇO$¬ěť\íĘÚîź+ˇFł@_°Ň(ś>u(%OÝ0§»˘NAýe0 ” uňé}×~ýkĚú•oě?znŢäÁ©©·\Ľßy+rÇžmmÝś¬ěhΙú6«Dť[<­żV/x¶rc°“ł›»‹4úŕ˝ă Ř»W,[uîęő%Ż–­;±řůYžAYcĎV_g{ÉÉ#ű9‡>±ÇěŮń‘“‚HpâđNÝăzfŤĄW·¦ÖXŁ#ý-ę4Ř:¶Ň+¨_H gNnĆĎP÷['[+…ŇÚÁŃV!—Űů´±ŹýăWťăŔvŻX¶*ţJ¢©öŰŁě0ě×m˙Ţľĺ3ŻVÇŐo˝·~É´Ö^ľë?ůbů¬Qy·Uf»6¶ţ´mËż|Ü]0ĂÚŘ9ŘŐiV*•6.Nu‰<ĂÓ×lĄ`ć©‹_»nÝ:–Ý:·fĺę›Ye`¨żŘ-l.Ž\ĽürZx8ądßą¸çŹxŔs&—¶íż4´ŕFô’ĺ‘ĺ: Ľˇ˘ĽěĚ©ŘdUvŁ_™1Ć”€Đ~‹ć.čŮŢőZÜą6/Ď+‚™łmÓεť§«M9¸—3™îßIHĽť,‹0&áď§Ţü;H ˇ”p©7o§#ÖR„xΔžrăćťű–ÁRř«T ’Eď˙đŢ≆ŐŹ;Ź €úłđ)ĆhËÎ]LŞźÓeWÓ‹Ćôń*1ăNÄš¤öaĂVg'u3!1ż¸"îÄ ŁÄ.lř@MNŇž=‡Š«k«‹sŻŢ¸©#|mMyÂőąY©§ăoz†ôčߣKzŇ…‡bĘ*´Đp«Üż}» ¬FŽ|ɢůÖ­”śű·NĆ%aGEö•÷®wÎľ’p#î\˘GHHžŇ ĺ ę˛Ň&ÖX##µ¬^ž3•––ž:›¬Ęډ¶ô9Öx֜əš”ö0?/WuK•™—źwöôéř›^ˇˇýBÓĎŤ>űđ^ňĺÔâá‹S/ěŰ˙Ç•«WKŞţCňęś“gy…µPŁ>âvVŐ A˝‹î_ÜđČŐ«W ‹Š¤ĄÜËÍËËUĄÜËĘÎľ—’&žÓ4J‹>ü"žR®ŻR” ňL©ř–&ˇESBĽ:Ž‚µ%1Q'G:imk‹¤2 ”7™LşÚícĂĚ:] ʇüMÁ=UBń%SB(ŕÁă¦ů:>P%FÇ5›/»yP#súĽVŤ˝îą3Ó˛ŐđH3BR˘°ëüĂÖŤ g˘ÚőžôęPßÁ}CUĄ<Ćü٢Ł-iˬтW==Ň©zŃ9¦ĽŚ”mřVŤnîĆ ¦„6˛IEXĘ2fŽÇ÷v± ”<ŇX_P*µ˛ZuFfc aP“N—Ľw—Ů`”Y۰,2ŚD€"UZ±,ţ+1LH0¶ă®#ĘÉ•rÁŔ™ĚäŔˇ,ł™<±WE˘Ź_{1ÉmňĆÔŐlÁ”&ĽŚ13 PB0 ¶ü‰"D „>ÓćQJ–e,m,‹J „aXJ…F<„\§™ÁXŕ9‚#7®1 ĨÉŮüáç§oe"„BOëyî{z¤”RŠfXHýS-łĆS`†E”ĐşőŽ0žž)fJB@ë´"–e !e1ĄĽ X:†f$đ`Ś(}Jó üź ÉłŚ€PÔ: ĐA_•šWR{Ďđ!XCى¸ &NŔŰNťÚÉ%HbÖß˝—G(  Wwog›ě´LľŢ!bćÉŞńOŔ0¬ đ§ĚűńëĎj2.Ď™:7tOŚ›g«Ęěűš ­Ť§—µ˝­ĚĆV¦Tjó˛5jŤuk/+;J¨Tˇ¨ůH¬Z·q°eÇë?±a yąµz‚ŰűŰi* C‡©Ń { čćç~÷Ćő;9y!8 W˙î/,I¸~7;Ź€tăöKĆ„Ţů)bţ{\]]™™{$‘ő4Ć5ĹÇź7š›tś†ĹQJA †±ŻXÜ•ÇŰÇqöľASĂzDíÚUĚS–RŢR”Yü BšW©kń¤ćşŢ"„ş}˙SzžłĆZ2Ň–[ă‰cŚ1J)BˇŤôc#¬Ë«ł&$Çľť­çvÝ&‡…üńó/屸…„Ô™—bńńžŇüç'YDDDDDDDä  ´îü’ŞÄpóč `Ý&čtbFI~nq…úĚ/ßŘČFŞ–çĺć”VÜąx@)@n«?ü.łDý0őb'Ą0zţ 7†e`ôÂ7 sS†tP(­^ż’<~ÝrV"uňń° »čâ˝7“UŁŢX€Öˇµ«Uç‘ ÎÝy3ůîßAâŘÚUŮyô’s‰W/M_=˝µÄFjoÇŚś|ęäD–…ĄüGŁÓçćĄ] ¶–ŔëźüP]ű—$]­%ŕä¸-:9ýÜo,@39š™a„Ŕę“-QU•ęÂâ¨-źŰX䍴DÍ*z˘a ëÄĺšň‡Ý­Řş5€žîcKzŚXNféřźĐÓ’‘¶ÜĎ}[#=D!„€QvÉ-׬|9ÔňOţĂćd¨• ´ä—"& išF3;#D Ň·?ú ť$3 ±¬;¦g—ŽF×1·®ě™şëOwĚ2Rłú•á§ď×ŘŻçŔ&°ďäđŢŞK—»{łőŐV°ÄeěřpR™ßl…ÚuBŇ}ŢÖŮř0ńüćC7í=ĽKYZ¦TĆň/ýpxŘ÷Ç{‡–ĄeJ•˛żŹ¤$-ÇF‰Ęď—˙|°äăźG»=Ľ?ç˝ŰÝş(‚GĽşaĺ+ź/ űlĎŐ.!˝Ôš[÷™l‘|ţű J:‡”đTÝ˝źŁŘEx±Ż÷aŔ·1mő˘áËútVw~5ęçw§ß÷Sl Ă`ţ±„(É ±“úµ«Čżôč‘2- >.4¸Ăëb٧fb „˛–6•…Y{wîç †âŇŠ !†¶M=uüTň=*uö}eZ„—Š>z0Iő ‘HÄ Ó38 A3iÓ)8bÂčň,U…ŽË¸}-ł Rćâ7cęËŹô ¦×XKFÚrk˛w``+°ňy\ϰ®~ŔŞ}ź¦ÖXó#E!@-´Ć“ ŚĆ-x÷ű˝÷ĆúŹ·˙çó7ÖôѢPşŚŽµô0*ęŘ ÎJŻĐčŘSËgL]˝ţőO[‹›2`lä;オfŤ·\z†o˙áŰ6,Űö‡źw})ôĄi‘ŰľzżNóş>]1·¨6¨Č?Ť§ĎjĆO¬˝»˛vÚ{ó"l®Ú^€Â˘tżĐŁúómÇ·keÁ$0Ôš’P”SÚkÚâŻLp-”TÍś9«Ť›ťµ}ŔW_­Ü70ąś{cé”Í+&®ű5Ö±m'YMľuçńR˘Ű8äŢ÷ÜĽx˙Î~r·©#B˛č‰ś˙ň™_pšŞ[ *]sk¬ń‘ÖŰ=×Mţ~$řđ·›nYf{ţŹôVŻ ť>«UXű¦×?J*‡/·˙´dÚD÷bOiqç®=^ÓRNn±ä±ţő›×î߉ýv9Ą`žÓŐꦶFg2šeRtčŰM7ĺ†Ůžß×eÍ´řžČ˙l"@‘˙yzëĆ ĘĚYůa{ŁÜ«óŕîmdN†™Łzüöű÷[Ăúě:W𠳢¬Tbí(¨ŹL7Ž3$_ěżź;bXď”í1P·CE–Ś©äÚđ>őę[áaÁ¤Wxv÷ŐÖVŽzCÄč¬[÷ĺ‚ĚĄËŘŻ¶µrĐý­$µş ™‰i˝l˛®ÜÍÍtůî›ţżżR T*WJŇM›­2Iúd îņňű‹¦ÍV™_PŇ.43¬ŰŔÝ'U–Žż8÷Älđ¸Ô-Ö”©ó~8&gž8@Q€¶Ýü$úŇóă@_]Íń~Aa[·mw—łr‹05Öx÷îm%şK]~fĄ™ UhÔEw’‹A°5™Ě¬ŁŻ›—ź¶˘Ľß´…#ĺŇŠlUZQ1‚†¤z€1&DđîşíG‹ćbÄét¬µ›Ż_QúŤ"¬J/«Ô`Ą[m…ú‘žÂB`,ĽŢěk|¤I$JęňÔ5oŤ&?,­Pk5fŤ^΀Ŕ {÷6VZuyżi FČĄ•™wTjŤg@ű’ôÄrr7µ@­•°u?:ŚV`˘oŚĂ@…Z«QjôrL‰¨Ą?˘C#"""""""ŇO{5”RT]™{7ËaÁŇĹŽŽ6ŚŤë¤ńŁöÇ~řúäˇo)X×ÝâcŁrĘr('PBöŘOĚś™#Ą§¶7Dšs.|úÝ>ľŮŞÔR´ą“#Ż@@–e/Čťťąâo(ˇ„˛,#đÄĹWY^ ×őčëË||W×z .×VIJŦfXÄUč+y–ó„“půŻDŤ[f«XSędçđő;3OÝ5oöó8®UŞź Ë9Fi~«°míďŮ錄Ž>qAkśŮÁ?ÜŕŃ3#9V*aJóŐ¬ŇÎŇ0 ŔKşd€B)1ęLZ˝Y›;=<¬‚V"­KÚFëRcL “4÷ĘJ‰–pMQˇ“_°=€ŕáí`cCôµ&ŤŢ¬}0=|EF”H}šZcŤŽ4ZW~˝FnűňŁâëűćG~Ć?ĎŤ­VA ŔsĆęjÁAWcŇsMî´aĂ*x)d&iц±Ö®­ăÚJ)“4$hÖ™8ŁÉ¤ç9ŕ0H$2k€µ–2Xˇô‘f*đ˙ä›EDDDDDDDDžÂŚ\ĘŔ»»Ë’"©mHÔɸĎ6F^şWTp÷Ś=Ŕô˙ľ—x~ăÚŤîćUć§ű:H>ü÷/I©™µ5Ő ά:lşFč(-Ľ¶_ĐhŞ'K´Qóצß>+`”v+NF·jçî1pJďéăAâ8á÷Ko߸äęăäů÷“ô™>$.{Nݸ0­›ŻĽ]ďaˇvîmÝöěw č—ô@sýŔĎ_ţ°› ÚW‡ř}ިö/I¸ęŮ#şXě¶ú›“Şřu‡ŇŐ±¤äÂť“–_Ţż{÷ˇëŐ…wĽ¬Ŕ’ťů1­ÖßĹ&kŠł×Ż|#ęôµy“÷źô¶©¦ěÓ·"wť»E)}kŃhŘrú¶¦8{ýĘŐÇN__<­ż÷ 9FmIW9€$°Ě@×-“şőzPÁÝ8Ľ{ŲU—n&Żś;X–©_ Ś›÷‰®ş¤AóŞŮžŠŞ…kÇż’ĚéÔÓ_ň·ň꟣67čy}ţ8° nrŤ5:Ňn>öĐ}Ü"JéK{ä€ńlÎĎĹ›Ž&˙˛zނȝq;6,[»-çć>ţ}ŇKM ‡wŻX¶ęJňíESŘwˇćčoŰţ˝ăäMJéÂ1AŽ«ßzoÇ‘Ó:mĹîÍ›—ĎeíÝ7§ĘýűOŰöśŁ”Ξ2jí/çwŻ]hŃĽdÍ©'%Mü‚DDDDDDDDDš¸fCëŠrś1ăNâÍŰégrńóüŇĐ‚ŃK–Gćiô”‚“oŰnÝ:–Ý:·fĺ Ĺ ‘´iŰNť“sňl‰¦:39ńŢĂ"J ęŇŇ3§b“UŮŤ~eĆSBBű-š» g{׫gâ|_Y8d úîÍZŰgŐ»‚ŰČĘż_ŻĘ´äÓb‰7AňöýzV¦%éô¸ĎŞ Á^f"o×'´B•¨Őˇ^ŻżÓ!Č‹'Šv}BŐŞÄš’hu¨çë:yz›Ż čáwOm[ŻŽ ö˛—mß|&î|RPx¸ŻóŃş5{b“¨±ôěůÄŔ°a~ě Jމüířu ’Eď˙đŢ≆ŐŹ;Ź €úłđ)ĆhËÎ]Lę6Ě G.^v5˝aLÉeCĚq'bMRű°á«ł“:róĘő*l;bÄ Śó‡wŚş™_\wâ„Qb6| &'iĎžCĹŐµŐĹąWoÜÔľ¶¦<áúŤÜ¬ÔÓń7=CzôďŃ%=éÂÁC1eZ¨ż.f)1z˙öí*°9ň%‹ć[·Rrîß:—„ŮWTţÝ»Ţ9řJÂŤ¸s‰!! zJ+4”7¨ËšZcŤŚôrZđś©´´ôÔÉŘdU6`L´ĄĎ±ĆłFŕL†ĚÔ¤´‡ůyąŞ[ŞĚĽü츳§OÇßô í–xîhôه÷’/§Ź\śzaßţ?®\˝ZRĹô2WçÄś<Ë+¬…őąř·łŞ ę]t˙âţG®^˝ZXTü -ĺ^n^^®*ĺ^Vvö˝Ä”4ńśFDDDDDDD¤Q}řµ”Őh(ŹÂSA đĚ˝1„Đ“Áę!Ś@x‘R€0¦„xu6kKbŽźuđ„ťł+cabŇ› &€%2©•µXb6#–Č$śŃdk+ee …Ť5ŕ 5Fť™BakŤč JĚ˝ «Ż˙T)1ŰÚ*PŁŢ\RnU[k~,¶z*čă$ÓŁÁă¦ů:>P%FÇ5›/ű9¦"đĽ©0ôg䍼îą!ě-KIüH3BR˘°ëüĂÖŤ g˘ÚőžôęPßÁ}CUĄ<Ćüٕ֒‘¶Ě-xŐÓ#m‘ŞťC¨‹«AAH äŃn !@ 0,CęDęxfJ)¨Ąf"Xü"V"şŕ Ä2,P BŁ=°äżĘżw{† (•ZYI08#„(o2€¨Ŕ™ĚzŠ EÎıHâÎ “™51Ç™§wetúÚZˇČĘĆF&a) Âkjj Ĺ/&QÚŘ*%4ŹK^§­Ń ŮÚŮ"BMfęý„1˘”<¶ĺ}Q‰ĺ˙Čą¨ßĎ=fž~Ě`RWłSBšlĹ A0Ă%„Ă0–?1B„„ĐgÚ_¨yí=Ń̰ĐuârMůĂîV,XÂŰŃÓ}lIʞśţˇ§¤ROKFÚrk<÷mŤô!dIZŔ(»ä–kVľjů'˙as2T‰ÁJZ’@L """""""Ň4,Xôž2ĽŹęŇĺîÁŢŚ `äÔŐ‹†/ëÓYÝůŐ¨źß›rpëŽ4łŚÔ¬~eřéű56Ô„˘ö~Şş´SWQŇcÜśť˙Z{ňčéqł7ő÷C=;]Ç$_Ú5-z˙ÖŁ7%r·‘cÂHevL|ł jkÔ7’3¨{MĆ•ŚtĂÔĂÇ]ĺżLuŻ´Ííô4ý+ÇOąĘó["ůybÄŐ\ŤłzŰžĽŹ~ëöđţk“:ůu9:,Đm\ ×%s—˛ĽkCÓÚŔ‘ÁCýÜ.sAQ2<¸udČ€î¤6ptďɰÁ=Đ_–”ä\?zđÝ-Q…wďç¨v^ěë=f"đíGL{l–ßťz|ßO±) ůÇN<˘$ĆNęÔ®"˙ţŃŁGĘ´0hř¸Đŕ Ż‹Ť:śšY1ĘZÚTfíÝąź7ŠK+‚†LŘ6őÔńSÉ÷¨ÔŮ÷•i^v(účÁ$ŐF"m;0|LĎŕ€ͤM§ŕ ŁËłT:.ăöµĚ‚J™‹ßŚ©/?Ň€$.cLJ7şĆZ2Ň–[ă " z ęÜAFÍůEŐ;yß·;ٰZćę7cĘŁ@›NÝ"ĆUŞËJ-gA=Â{ůJ˙ýÍů<‰{Ű‘˝;ťŹŠŞ&’ń“ĆŞn^ÚľÔ3PJLÍQ{weiZž$"""""""ňŹCć©>˝Ž ťłú·^ ĐĄ_UćŐcůĚĎßĚ `öF©ĄČĘi序‡Ü˝˙ŰŽźŠ*«8mu†ŞR °tč¤ü¤Ă×Ë KÝŰ?í˙Msq÷éřë¦fĆ1f X{ViUSZŞ•`…ŇÚNĺvĄuMY©BÚI1’)Ým3 ¬܆řËßű˛€R”–e4ҲҊšvľíË…öşĘ’Ň’˛jŁşD]Óηšü/HŞJ ‹a)‹^ô>F„ ęŰ0Ë„»¶†Ř‚0©o„Âź˙üŃұ 7Ű­°‚ňoN“ď·l.J˝Î8µ_2oć¤1áÉ9šČýňÁâ1 7ý»®” eU:zw^˙ů†ZĆŢoɬQ}ş]5úŽ9ęÇTdWÁü9Ó_{y|Üť<cÁr9 3”6᛿ű¶,í&ăÔ~é‚WGéźĂDĹž˛«ÉŻb^ nç:\źĚ*ŰĂ11ľXmŃ37âĺ3·s¬Ű7ąĆš)BhK­ń$#J(Ś[đîĆÉU` µ`ďŢÓÎ0őóčѱípyv,ś;sƨ!7őľŃ±±Öš‡©]@+‹›2`l䪉 }ŃŐÇ+Ěú=Ă·˙UMÚţđóÎWÎ0wś»mYďĺ‚5Ô‚ťkog2yÝ÷€h(\#""""""""R Tŕ@&eç@H]ĄYńî­őwÎ&™ä2)ĆÔ”„˘śŇ^ÓĎxeÂŕSr+żÁ3‡ţů!”—¦ű…ÎŐ/o;ľť‡ĂUBuEr‚Jź‘c©#Ů|d;ˇŔőúű˙qxö( 2»ߦ«śZůus8<{”L-‘8ő›Đ6ŘWgôó–ďŰ™bpsŃĘd&RŐ…Ó·®ţ.úl (µ÷Î_ĽśUD4I´˙‰ůßhîť;}*čă›ýăłlKź(ÉŠ0C ÷şjqÄć×#ÖýkďÓÁ×BIŐĚ™łÚ¸ŮYŰ|őŐúÁ}“Ëą7–NŮĽbâş_cŰv’Őä[w/%şŤóGîMqĎÍ‹÷ďě'w›:"ÄóĂĄ'«aÓ–×Îź·ň«źÁšoČĽ2{öw{kű€oż]×74ÄË5ÂWRÚ±KÚsrę™íŐUş°9Ă»µúpŮG=‘ó_>łâ NSu+AĄknŤ5>Ňz˘çZŁÉߏţvÓ-ë!ĂlĎ˙‘Ţęµ!ˇÓgµ rßôúGIĺđĺöź–L›č^ěă)-îܵÓkZĘÉ-–<Öż~łâÚý;±ß.§TĚsşZťŔÔÖčLFłLŠ}»é¦|Đ0Űóűﺬ™6@ßsyáäl""""""""˙‡±ÄeÔí,ëbYď0~©[ţ¬)SçýpĚJiK+'ŹÇ ’/ößO‰Ţ;ĺÇ5%BiĘŮ}ô üm뀗z튊+Čͬ,/GLĹ׆÷ ¬Ýóöc$Ö¶Eń»ł3Ę_ŢăÄý±zÍť ţŠë™éĄżĹ82…Ď•81…űVż•}s”‹]ć®›2\ľű¦ßďŻ?¨2Śť±¸o[ŮÜ—Çd·{·Ďś0äŞ9¨«˙ÉŢß¶.™5áÝíGˇaÚţÄlđŘ0Ërć‰âE! ж›źD_zţb<č ł«9Ţ/(lë¶íîrcVn¦ĆďŢ˝­DWbiŁËϬ4sAˇ ŤşčNr1¶&“™uôuóňÓV”÷›¶p¤\Z‘­J+*F Ő`0ƄޝB·ýhŃ\Ś8ťŽµvóő+JżQ€Uée•¬tpk㯭P?ŇSXŚ…WĂ›]cŤŹ!‰DB ±ÜXkŢMţ~XZˇÖjĚ˝ś$öîm¬´ęň~ÓŚK+3ď¨ÔĎ€ö%é‰ĺänjZ+aëü%ŚV`˘oŚĂ@…Z«QjôrL‰(S÷ y n§ÄńÄĚ™-™źŠ4ĄNv_ż3óÔ]óf?Ź(­€@9ZçýH\fNýČÖĚ*0ŞĽ>yčZ…Ä­C·řبey sô{gÝjsÎ…OżŰÇ7[•PF"©yĄtđď7Đ^˘voĺ\™U\ăôĐĘ-Ŕż˙;¶%’r—Ň;ąnýěËk`áŰ=úú˛ź¨4n#Z·i`®}x8îš‘JďW™˝ŰUęŰjţW$’ôJs·¶ľđ] ŠźśĺăZ5 ú ˛śc”ćW° ŰÖţ>ťÎHXŕřá´Ć™üĂ =3’cĄ¦4_Í*í,mł©Kę €B)1ęLZ˝Y›;=<¬‚V"­KÚFëS*cJŚÔ ąWVJ´„3hŠ ťü‚ío˘Ż5iôfíéáĂ,z0˘@âčłqCdŁk¬Ń‘FëŞŔŻ×Čm_~T|}ßüČĎřçYŁ±Ő xÎX]-x "čjLZb®Éť6lX2…Ěd0-Ú0ÖÚµ5`\[)e’†Í:g4™ô<‰DfͰÖR !”>ŇLľáâ™č×< –|đŐŹc†ppqŮqâäÎŻ>úţŔÎ;­ę=púî!ţž|ţwż™˛ôë÷çußżďÜŮóLy§â ÇřWGtu{őő=€ łív`˙Şë'űO}CR’üDZłŔşu^˝jqőuÇMßí㛎«ˇ”žÁdręˇÝůáˇ5Kő˝¤ěŃ«ÚÚájmĘŻ¬Yć€Ëź+qÄĺiIYc·÷n{«›Ż<>Zőਸ਼Di»l€ýÁł——/ýrçO˙ÎçťĂŰ»-MMş.đN>Ëwn˙wľđ? ŮÉuíĆ[ŁY -Ę>ü,D púÔˇ”Ľ–Yv3>Üu ę/Ą¨“Oď»öđë_cÜ>űľ÷ÉǶŻOM˝ĺůĆ;oE¶ńJ[7'+;šSßć?˝ĆL=ńKdl±ŕŮĘŤŔNÎnî.Ň=w|őÁĺŘ»;›4eά[ŢÝüK Ë2–-ľĹ÷(ČĎrđśgŃěëâlď Ůd?÷ń’ŘcÔÎíśDŞ€¨Ă;uďĆ7čůcŰű˙ţ)JéŐ­©5ÖčH‹: ¶Ž­†ô Ęĺî‚pćÔáf¬ń uľ“­“­•BÉ88ÚŞĺr;ź6ö±ëľZąęü‰»wźMš6ďµ]›–ď?¶ç“Ť'~ÝöoľM_ŻVÇŐo-ďÚ»k/ßőź|q#ńäŽó*ł]› [*¤m}Ü]0ĂÚŘ9ŘY)”ÔÁŃV­TÚ¸89Ô˝QѱyŠş«8>íýŐŮ©1'Ď–TWe§Ţą—y/ţbR·ˇaN¸8rńň¤‡ea'ź¶Ýşu,»unÍĘ7n¤€§“KÖť‹{ţ7Q@°:;éŕˇ#7Ż\ŻÂ¶#F Ę8x÷Á¨› ‰ůĹq'N%vaĂjr’öě9T\][]ś{őĆMákkĘ®ßČÍJ=Ó3¤G˙]Ň“.<SVˇ…úëbďěţíŰU`5räKÍ·nĄäÜżu2. ;*˛Ż¨ü»w˝sţđ•„qç=BBô”Vh(oP—•6±Ćéĺ´ŕ9Siié©“±ÉŞlŔhKźcŤgŤŔ™ ™©IióórU·T™yůŮqgOźŽżéÚ/$0-ńÜŃčłď%_N->¸8őÂľý\ązµ¤Šé?d ŻÎ‰9y–WX 5ęsń'ngU Ô»čţĹýŹ\˝zµ°¨řAZʽܼĽ\UĘ˝¬ěě{‰)i˘;#"""""""Ň(M\dB(m¸ěňl€ňcßĂf*đĎŠoaZ„1%Ä«Cŕ°!X[süä¨'=Ľ˘€€M&˝$J‰”Ą@ź+ ĽŮLőU# ß(XłŇJ†(5›řňJÓä©'jő\Ăp §Â0ţţ @ż4nZW_ÇŞÄč¸fóe?ÇôOĚkSačĎČyÝsCŘ[¶ĐcKSJvťŘş1áLT»Ţ“^ę;¸o¨Ş”Ç‘?[t´%#m™5ZđާGÚ"U/:‡"""""""""PWŔ°, –*ćÂco0Ć”P„Ë…1‚J)P"đcD Ě0Čr§aKH„X†JxAh´–K>ů÷ďnĎPĄR++ F&m`†aÂó–sM5/•1 n‰Dŕ âĚB-奄R@B±dŻK|6B,Ăđ<˙ż"!ä|Ôďç3á O?f0©«Ů‚›ľm… ‚€(!†±ü‰"D „>Ó¦ˇKeKŚT „aXJ…F< ®ÓĚ`,đÄPS¬ĘČ{yć<Ł&gfÄ[ŞR! €ü¤žç­±gFJ(Ba Źžj™5žRË"Jhť/0žž)fJB@IťCͲ,!!ł,¦”KÇ ^ŚĄOi~á‰ůżM]˘'Ja‘xú´ >ÜŰÍ&/ŻP <}Ú…÷vµÉËŻ“Hí=GŽÝŃÇőAn.O( Ś´˙đŔöŢůsůş‘–âôMf"f–R2xĘĽ3§Nľ6¦ĎĺcŃngŘ»8Őäç”çć!©\*—Q 2k+}QnůçKÔ‚T)W0ířkVRR§-«â\]•śIŘ0Ăl&{ ĐékJ«4 @@Żá˙$†šň* éĆí‡÷˙řő€Žv‡˘ÎŔ腀غůĘ{4_ĎB1fX–AźcĚ0 zäŇÝC„0)’¸Ěžű Ő<,­ŇBśÚĎž2&÷Vr-OÂYĽoÚpÚÓf‘Ŕ0, ť±¤˛Ös˙~E•öÂţÍ6¦/©ÖŐIÎíýÖ@áÝĺtbFI~nq…úĚĎ_9Xş%ŞŞR]X\µĺsKdĆsŢȲ0zá›…ą©C:(”VŻ_Iżn9+‘:ůxÚŤ]téŢŰwŇǬ]"‘ĘZ"qöń°żěRJµW"_ő–9Čśť$cćt?{fËÂŇţŁŃésó ŠŇ®[KŕőO~¨®ýź‘tµ–€“Gŕ¶čäôsżY2s?ßĘOcyÄę“çĎj^»ĺÚ`†€®—kĘv·b#čé>¶¤ÇBřé–ZOKFÚrk<÷mŤô!„FŮ%·\łňĺPË?ů›“ˇJ V2uVz®f‘‚0€Ž=,=Ĺ xÔ˝I˙ę ¶î{,™;Í xT¤‘ÓGôôµčk“öA —˘]ČäZ“yöv.}§™)˙ZŻvcç~@)™?şHĺnă#^;¤7htkVçŐ,xó~ę99këĽč÷í¬\‡o‰Ý“ż$&ţő÷WŰc#wľőD %‹b.Ľy#éŘ®0[–Ý´süŽĐúM}zŚyµ¨ŞjÝ+} KH/k€Ö}&—×hŢžńż!éâ$—Y¬řĆćÓiçvÖe{ř“[^‹ďŃ~Ä<=1=šŻQÁŔ2ř‰ő€dĐŘ©ëŢygá¬W[€4|bä[ożµfEOŔřQ›Es¦Ú"č݆„Vä\;–ĎĚť?cp‡î őďóÓţßľyw© ů]Ć `ĺŠŇ›g­»t˛Ďí]˛Ľ¨¤ÖÚٱřĆ™?#YVT˘łq¶Éş•oŕ:Ä_ľ˙`N¸Ą6p¶ CÖqÔáC;»ŁZ€°~/ŐýĎH:şá ىŰ”E/ú#‚úVe^mŻ®í€ üX3 ŢřěçŘ=[Ă_zéÝ/ţóĘ„ţŕŢ÷ű-›Ăű†„OśüŘáî~v„@ä¦_ęÚükó¬iý++*˝;Ż˙|ĂŚŮKö=Ř×S NíÇĬť3.ôĄ±GŹ ň¦”2¸î]3`ľů»o-šŁŹ ô*˝˘bO­>qőű_FŮ;0°Xů<®gXW? `ŐľÉ5ÖüHBP ­ń$ČâŐŚ[đî÷˙zďŤőo˙Ďço¬˙čŁE t?kéaTÔ±Aś•^ˇŃ±§–Ďşzýëž¶Ľ@ dŔŘČwŢ{kÍoą:ô ßţĂ·mX¶í?ďúRčKÓ"·}ő~ťću|şb&nQmP‘lŁRĚHXÄ™‰ë—_m2dĹ?• X‚)ŕúĺW› ŮçOÜĚ yč˛hTż`ľířv®QF+‘şJłâÝZëďśM2ČĄ,ŐÉ *}F°Dż7J0‹‹Ż_Ŕ {ąöآąň?âÖňâ›—%6Îv˛IŽ/šëth_–řxJ÷íL1¸ąŽnmV×ᨇ·°Îéw5doôi}Ż`µZçŐ>dXç{wµ‰p0ö̬ŃývźTÁŁÜ …FćëŃża†Aî=tŐâÍŻG¬ű5Öާ®…’Ş™3gµqłł¶řę«őű&—so,ť˛yĹÄużĆ:¶í$«É·î<^JtçŹÜ›âž›ďßŮOî6uDç‡K?NVæ-?®ť?9nĺWáú9'`ČĽ2{öw{kű€oż]×74ÄË5ÂWRÚ±KÚsrę™íŐUş°9Ă»µúpŮG=‘ó_>łâ NSu+AĄknŤ5>҆üϵF“ż >üí¦[ÖC†Ůž˙#˝ŐkCB§ĎjäľéőŹ’ĘáËí?-™6Ń˝ŘÇSZÜąk¦×´”“[,y¬ýfŵűwbż]N©çtµ:€©­Ń™Śf™úvÓMů a¶ç÷ßuY3m€ľçňÂÉŮDDDDDDDDţÓčÖ Q3ň‹_Ś ŕ‡ZVJAś‰Ż—Ľ´Ô}ŕ§­Ăú+ČͬŞ,ÇR1é=.uz0kĘÔy?“!Sńµá}ë5· Ă-‚IŻđě>î«­­ő†ŃY·îĘ™K—±_mkĺ {®ÄáV1!31­·MÖ•»ą™.ß}Ó˙ŕ÷WŠJĺJCIÚ˘iłU&I@ź¬!ÁýŻŔP~Ń´Ů*ó˙€¤]hfX·»OŞ,qî‰Ůŕ0p©[ľeľäĚç! ж›źD_zţb<č ł«9Ţ/(lë¶íîrcVn¦ĆďŢ˝­DWbiŁËϬ4sAˇ ŤşčNr1¶&“™uôuóňÓV”÷›¶p¤\Z‘­J+*F Ő`0ƄޝB·ýhŃ\Ś8ťŽµvóő+JżQ€Uée•¬tpk㯭P?ŇSXŚ…WĂ›]cŤŹ!‰DB ±$‘kŢMţ~XZˇÖjĚ˝ś$öîm¬´ęň~ÓŚK+3ď¨ÔĎ€ö%é‰ĺänjZ+aë~t 33¬ŔD-Ţ † µVŁÔčĺ9PKD‡FDDDDDDD¤ńj,Ö}ýý+† OÎ(a,ä‘ä~ĆTćĽ>yčZ…Ä­C· §cóÔŮ7G+›ŻŢZwę®yłźÇńš :ůmx{µ9ç§ßíă›­ĘA-E;‘;91ň aY†đ‚Üى‘+Z(ˇ„˛,#đÄĹWY^ ×őčëË||W×z .×VIJíĄfXÄUč+ybI—ü? áň_‰§€ XSędçđő;3ëćK«€†tj–sŚŇü VaŰÚß˛Ó ?|â‚Ö8ł¸ÁŁgFr¬T”ć«YĄťĄ `€#–tÉ1„RbÔ™´zł6wzxX¬DŠ€P ő)•1%FLjĐÜ++%ZÂ4E…N~Áö‚‡·Ť Ńך4złöÁôđa=Q qôٸ!˛Ń5ÖčHŁuUŕ×kä¶/?*ľľo~ägüó¬ŃŘjűđ^ňĺÔâá‹S/ěŰ˙Ç•«WKŞţCňęś“gy…µPŁ>âvVŐ A˝‹î_ÜđČŐ«W ‹Š¤ĄÜËÍËËUĄÜËĘÎľ—’&žÓ4ĘŁż–˛¤e%ŐźŐ€Ň'™[ÖŚ0¦„xu6kKbŽźu0ÖÎŮŤ•K Á¤7L€%2ą•Z&áÍFŞ«ořDÁ¬¬e¨Ů(”WšĆN;ŻÓó˙ÓńÖ€VBţńG±”eĚŹö¬H²<ĎŃşřobQÎ2,P Âóú‚€R©•Ő‚ăgööÇ!„ça„1F -‘ĽŔiOn°! @yłY8p([ Ć@)áy^@łRiÝ6ôď%ˇ@áMţüu‰B¨ŢŔí?Éq/ć|>mpKŃzŔ ¦„4áŐ`Ś‘ a€B€a°ĺOŚem<Ó¦AbYĆŇca–ŇFWbpťfcçrŚÜ¸~Ä€Ł&g󇟟ľ•‰ęŇ1?©çąkěé‘RJ)Ba őOµĚOQBë~ch›†a(%„PĚ0”1 ^ ôŃí;@ČŇKÇ–6€1˘´)Í"""""""""ŹSçŐĄrW÷6Î6Ůi™<}R’žÉppoÝÖŰ“ň Ś-ÉÍÉWW€ÂŐÝűɧ®îŢζŮi|ýgw†a 4ľgVřÁSćýřőg5—çLťúŰ19ÍF#`ÖÚµ‹Îh4Öh /PĚ´LâÎbZkDĆšÂó€Y+Ww & )1Ő>’Ô?ő·” Féć©„šQšm%¦Ľ‡µzµ÷·Ż©¬ívbĂ7{VLě}=z{Äü÷8ŔuÁ÷~î‘DÖoĐÖŢhnŇSbcD)R`Ćtó¸»kiÇŮűM ëµkW1OYJyB!‹‚@š:¬kń¤ćşŢ"„ş}˙SzžłĆZ2Ň–[ăéf ”RA‰ëÂĺsť­đ»7ö=+ c3”ˇŔ0>ú<ţ§Č ·Ő~—Y˘~z±“• Ö}ő›-’.¶rľü«rMĺ쬌űµ:͇Ë^G~úCSOuRJˇeˇÍ–lŁľY›:°»żµóÜß¶Z;Ú)\ä6VA眸˝üř…­Ráčú§$+Žď¶˛•[9:)lNAý_;‘ĽôČN™R*łu”(dvťúΊNZzd§L)űJ¤ ™mŕŔY‡âŻ]ťązzk‰­ÔŢŽ13řÔɉ këÚéÇäôsż±/TqŢňŐ'[˘Ş*Ő…Ĺ…Q[>·±Či‰šUôD0Ă@׉Ë5ĺ»[±uk=ÝÇ–ô!°śĚ4Ňń?ˇ§%#mą5Ń˙”-X…çÇß˙xżP“qaŹ´NůĄ<źęłü/"""""""ň×Á`Đ{Ęđ>ŞK—‘ÜšE¬;ö™\/A‚G÷|: -%€M€IDATß AýűĽýőŹĽÉt1&µ:9¬gSO±@€RŠ%.ă#^;¤7hn Gˇ¶Fťśi"@ÝĆ|żÁ}JµęĘ®ýHĆÁeěţśdR^pýź?ć8 W§]Ţőc%ÔjüÖC‹˙a]}˙ÚžCŚ•űßP˛čđA«Ęô¤GŞJő?í+üř»‘ß®ń?ů[Ęő,ť5Ö–ÝKĎ©  ĽŘ÷}Ě0”BűÓV/ţĆČŢ‹ßůiěâ7§Ž  –c‡˘ŘAc§®{çť…ł"\mfĐđ‰‘o˝ýÖš]ü=)ڎˇÍ‚W_¶EŔ ĹĄAC&D®{kX·Ž@(•:űÎ]ľvă;o†töĄ”6â vŕđ Źk¦”xwěşfý;ł¦L=z´kG Tćâ÷„€fÖXKFÚrk<Ý_„¨›wÇĄŻŻ}űÍ5c‡ö0nX¶pË«Ď €0ĄÄąMÇE+#ß\˝´Ł·µn=yJ„—mÝ 4,ńxůĺ¶n,´Ě‹y †ĚS}zőř.ć‚D&'†Ś}{‡ÖI¦ęĘLUj^qyϡ/ç'ŽĎ5 Ľs˝z6ý·ëőÓţßľyw© ůoë3¸nI1ŁtnŕÝo°O÷†ębB1f”NíţQ’!>ÝÚóĆҬۥÖ.ýeűŔÝ´Z‰#aĐ‹~čÇŔ€ ľU™WŹĺ3sçĎ Äص}0Ç0JáŤĎ~ŽÝł5üĄ—Ţýâ?ŻLčî}żß˛9ĽoHřÄůÇŹîîgGDnúĄ®Íż6ĎšÖż˛˘ŇŃ»óúĎ7Ě˝d˙Ń}=ĄŕÔţpLĚÚ9ăB_{ôčá° oJiĂ|#Ě€M@řćďľµhŽ>~$ĐCŞôŠŠ=µbúÄŐď}dďŔŔV`ĺó¸ža]ý(€Uű>M­±ćGŠB€ZhŤ§@SJÁĆ÷—‡VĎ7tôäŹ?zŰF,Á/Ś\ĆX.äQőě|0ćÄĆ•3§/ŮpâřĐ®Ý7}»uT˙Ž=žý·ný®˝uýą–Čź‡*𠓲 Ĺ9¨ <&A€0#Ľßŕ™C\ő"Áüś§UÉ *}FŽĄŽd󉞠a‰`Ě?űá—…˛Ô“W}Ľ’ˇľŮŞuźÍ¦„g•>aüŰ­•ÔP]Í›M” ĄOŘ›ÝÜ%˙ ««Ş5ëu®ÖĄşđÍ}}Ůîj»ô”gńč_pk( (Ö”:Ů9|ýÎĚSwÍ›ý<ŽkŐ€ę'ČrÚPš_Á*l[űű@v:#aă‡O\Đgvđ7xôĚHŽ•JŇ|5«´ł´ĚpÄ’. †PJŚ:“VoÖćN«ŕ•Hë’¶ŃşM<Ć”1©Asݬ”h gĐ:ůŰŢ66D_kŇčÍÚÓÇYô`D)€Äѧ©5ÖčHŁuUŕ×kä¶/?*ľľo~ägüó¬Ń¨)BŐﮜđń›6coüíłőń§÷í>÷¨ÎLę‹? Řdmël‹ Ůůxş—6%ńě»+ż(şížŮyĆŘÖ_ľó 0„˙Fľn‘ ,ůŕŰ_6­YęŕŇf÷éSË#Âyžűđ»˙˛HNťZ9} č1wDW·ßě† ’Ľ˙ÍĎÍ<µbę·î¶zŐâE3&Ö}ob/N)%<– )%„«­Q—Ô”–ju€0P"pµµ˙0‰Q§7›ß.­»ű)âŁUłçť)ł¶ÓÓQg€9ľŕ.Nź:”’§î=púîß~w3>Üu ęŹŕ€R‚Ôɧ÷]{řőŻ1ëWľ±˙čąy“§¦Ţrń~ç­Č{¶µus˛˛s 9gę۬>unń´ţZ˝ŕŮĘŤŔNÎnî.Ňč;ôŽbě^±lŐą«×—Ľ:ŕQÍ‹ďQźĺŕôÎ[‘;ölőuq¶wś<˛źsč{ěŔž9)T'ďÔ=®gÖHPz5ąĆéoQ§ŔÖ±Ő^AýBA8sęp3ÖxK,uŕčK7n®[<ŁG'oNSV”_ců×»Éç[‡Ž?vüĐ’iýOúÝh˛ő—˙|űËÖžÎÂÉłgo¤=pt´Ď<{<ćô=G—´Äk–ű—"""""""""/BÝ>Ч˝ż:;5ćäŮ’jMfrbZ^±O‡€ňě;1'Ď–hŞłn%ŢË-ňtvÍş}aĎń¦úSuN“Oe&'Ţ{XD‰A]ZzćTl˛*»ŃďŢcJH@hżEs„¶wąwŢ{Ň +OŹ NR)c٤"@ŚÜÉłkŕ?I‚)b0á:°ÉCňÓ+LJëČ7CÚŘ»Ě_żůý%“ô%Şw#ôgă1(ĆhËÎ]Lę6Ě G.^v5˝aLŹ×AŔw"Ö$µ>°:;éŕˇ#7Ż\ŻÂ¶#F Ę8x÷Á¨› ‰ůĹq'N%vaĂjr’öě9T\][]ś{őĆMákkĘ®ßČÍJ=Ó3¤G˙]Ň“.<SVˇ…úó=‹wv˙öí*°9ň%‹ć[·Rrîß:—„ŮWTţÝ»Ţ9řJÂŤ¸s‰!! zJ+4”7¨ËšZcŤŚôrZđś©´´ôÔÉŘdU6`L´ĄĎ±FcŁYéäÜđ™ţ ŻG'<„h¶*!#O-‘H+ÜżpíR|‚Şď ÁžVüúČĹGrhMMĄF}0ęp’JU–Ł:~ü‚Ž15""""""""/Ęź/tbą-ô_ěĆ”Ż#†`mILԉчă\Ľ<ôU•”˘şm/ĂĘm”&m ĄđO‘JKÁ8Ňđµ‚5+­dRłIPWĆM9Ý{ČÄîí\rT7Łă®“fďő5kzôxĘ“AÓňF^×ÔłŹ5@ôůAUŹ4#„)% »Î?lÝp&Ş]ďIŻőÜ7TUĘcŚČźM•Đ’‘"Ś(ĄŹ:Đň °ĆGŤŞBŁş«iÁ€^xEDDDDDDDD Á«aXP„á1ż…a% Ľ@%VŕůÇÓ41,Ë`Ä›9‚0F@(eĚó°  Ä2,P ϋ„F(•ZY/ډ—*”DFe)aÂq–ňň˙ bĄˇv¸î+–‰QJ(­Őń/O‰ŃéůÜv?ipŚ`SBšđj0ĆHĚ0@ !Ŕ0Řň'FúL›G»x–e,m0fP†a)ńLbpťfcçrŚÜ¸~Ä€Ł&g󇟟ľ•‰ęŇ1?©çąkěé‘RJ)Ba -X™ÍÚĹX Ćáńk±Ź Ě0@·ďcfaYĘó˘O#"""""""ňW¨+Ç!đ<Çó<ĎsG„}:‡Ľ”ŕK€(,6rÔ!R¦I‰R€$ F–Řú-»Ĺ9ihĂ`Lµ÷ Z´`n+ BęŰP„±Dň˙±wŢqQ]é˙Îą÷Î CďŘĄ)Š˘¨Ř•€¨Řc/‰-ö[’Ő“lbš‰ŮDŤŤ%ö†ŠŠjiŠHGzĘ3ĚĚ˝÷śß±ˇw»ůîŢ÷k˙XÎ|ć”gň:Ď˝ç<Kź{žř&úPYBHĹ7ë׌;çbBĆRd÷ŚNS>öĚHëű,𼠊RlŹ9«N]şůőčîç×ËÇ»5~ąü` śŇiöĽů]ܨ vn]-śkŹ@3 ę Ba̲,˲ ĂHsň/řąËČČČČČČČüŹ€Ľ†LĎ-«~™Y^U}ůđÍěĽjĂŹéĹšśÄ«Ő xŤ´ô~-©¸îöńź–ßRYˇ)(*ŮňĄ%ŔËTKgXFĚ· űîŔ­ĚąÖÎ`7`Úęű%ów~ HŃďă­ť}¬¬zM\ť\ú?důő¶ďú wç­~ŁĄŇVé`ĎŤśĺ~qśZÉY9uüĺl\Jä>)3÷źßńJ1˙ĽéőB/VŻĎéý°Ťä?]Ć.Ő–ĺt3XY=ÝÇ—é1BŇq-ô”őOę4=RéŢżÚkŘ˝ś˛Üě¬ü’ň»W©ą—sß§ż­‘JŻâ€QwÎ.Ó.ď'ýÉs𬴤®j^¦ţ¦ÔČČČČČČČČ<ź§K R"@YjĚśÉC.FÜtX|˙âŹĂýŘ‘á9eXߤ?®uëÚš ŠŠż}ú‰‡5İś Ŕ#xňĘ…Á‹{wÔxOŮůáä3‡vśŤçTÎĂF‘ŠĚł/ĽBˇ¶Zs+î>ĺf âŹČʲ˛˘k5Ő·ţÇ,\8%W’^´í÷ »G9ç¤Îú(ŢŻ‡ł•*.˝ź’U> “řj'—0ĂQhF„ đé[™ţh˝ş´ë ˇ aň° «ľÜůéâQŃ·b<|–™C٦0ňÓ–Í…‰7űv‹ćĽ9nä¸,횯~ýdáČč[1ž]–sĆ‚cv­˝×}ůA-că¶hĆđ>ľQ†¶ÇĎžtcĘ3+ałÇŹ ż›Ë`,ÝŕBˇD´ô˛ůÇďK“o3öíĎ›>"°ëzÁş&Ż’™ÝŐĂiîč>é•VÇĎžuĹIç­ ă/Ţɲh÷\{ńH˘ ”QŰ-_űYŕ˝Ô}»wVVľ0„@QBaôĽ×OôĘŻ ¨—žÖu“żÇ.ݱ°0WÝúąĂşůŽ­2kîéí8|jp÷ٲmË–+ĎwćN‚ęÝOĘŞW—~}ćĚY’˛şYűľ~Ýý‡ÎtĺJúuî1bÖÚŠ:RU© š0k¨ołť5sÇŻ­ŚoÂÇ)},jÁ˘!1!:)«¤×”…ˇĎtmkO)ĹM]Yb9|üűŤŰŽÝľ{qËGźnmŃŐoęŚyA>.‡¶oݶek­Ťç˘)cG {˝…˘¨o—#g}XK•Rë]›–ŤYô‰Ŕ() Ľ®V'0µ5:ŁÁ¤P c•ß˙řgŻ8*ů~ŤŚŚŚŚŚŚŚĚł4Ő"@ĘŐĂ}şv>qE3ßŔYăűČCýî1 €YË7´ł5¨Zytkmćč9˘Ż/2j]Úű/îđÁÚ÷«R‰ŚE7†öé4vÁÇMťč©!Ť3—vú¸÷ `E=`V4Ô.í:îűżf!q*.óFÎď+–.ó1yÔßöřç7·ÄT×Ük`Ăz©ćÉ…@ŕîëĆéK._Ť}Ać¬7źľ[·ýúĂW_Ěž6SCMťŕŇÍťÓKmtyéEĄ5fj3­¦đn\đzŁŃÄÚą:·r«./ë7eţűk––g&%!€†j0R ÝşŁß¶_výđŐł§ AĽNÇZ8»ş¦Ü*(IJ)­Đbµ­sĎęrÍ#ť‚`(ňBk|¤qÇ2X:u©Kż4qôčĹo/ď3`lĄşMđŕŢ {,KË5ŐÚ ­ŢĀȊśŤK›öŐš˛~Sć˝˙Î’Šô»Im ŻvĹ)1e…÷ó5Ő[S™1Áa9 R4Ć`ĂŔCeL‰ ¨´:ňń3™Fh|ÓưÄšMxČŃ#ëĺá)ŮxžE¨Şxp/C3oń®,k÷IŁ”i ě­mö}łöÂ=S·ć%5ĺ °wűlÓ–Ź–M‘îµ7Ů!€1D9hăNßŔ.F˝‰aYÉôżdéŘĹXgŔŽÍÔeµ0jNŹľ®lÚ˝j—–*" ˙DXC@‘¶ÄŢÚvoĂzUk=Ü7KŻ1JňĘY3«–žm€áX:v^KśîíŮaČ̵ŐÝĽëËŐ‹m[ď:¶tbđžĎ—čß·÷h-*ücöß6źľpînž¦×Ŕ©{÷ýîlČŮ{ú"pÎťV®X¸`ÚŘúÇăĎŮ,RJ‰ŔBA0Ф>O/ˇ‚‰Á$Š˙cJ ĽˇÚčÚąe×6Ę3I3ç\,µ°ŮÓNW'5đ"}Ĺr¨D¤váXB®¦÷ĂőÚrJ ĐÄ…Ľ‘óÝ®łë–Ż:t2rÎÄ€ÄÄxÇÖ]ßoÍîýŰÜťíÍ­miÖŇmV‰\8Ąµ^lŃĚ™ŔöÎ.ŽŠ3GvëíüCď]¶dEdÔÍEÓ‡ËÖż’büĽ Ű>ďż·f÷ţ­®Ž6¶Üů‡xŰ>ˇ§ďßý©˝QÁąăżé×™1 Ô­|źçcŤŽt_HXŮ5 ěĺÓŻ{g tú˛Mwo…ŻgýĄK‡mŤąçĂoynÁŮúď°˛µ·27S[ŘÚY™©TÖmŰ؄ݥłxîđŢeKVD\Ź^0yŔˇSűŐíďÚöĂö-_x¶j†1°[ůŢGëMiŮĘuÝç_/ť1<÷N’ÉşÍ[wlŰňU[G̰–Ö¶ÖőĘjµÚŇŃ޶ţ{ĺh22222222ĎŔ69§J)ĺyCڝ۱wRimiä•Xß Áö¸hÍÂ%ד €đúň˛Ň‹Bă’2}Ę,=#÷ňë·`Î<żvÍn\şÔfĘŚV]ú(Ĺvv-ěÝ;«ĚX ˙”š…­˝{g33(ĄŮy¸[;ŰÚşuR)"‘€Ť››m3»ż‚…ŠD$Ř®M+?çĚ>]-óî—שĚWŻéŢŇ‚nÝ˙`îşÍ/§/Núĺ·“"`„ţě|Š1"ŐĄ‘W­WTJ!Â>~_!SřąPŁÂ&hčŔŞĚŘ#ÇNÜľ~ł[ű§]>ľ÷HČí輢ňđsç śuĐĐÚ¬ŘýűŹUŐVeGÝş­#BmMYôÍ[ىa·[tďŃżGç”Ř+GŽť--݆‡ÇŤč,őÎťJ06ě5I9>>!+5ţ|x,¶3ËĽžäŮ­ËÝËÇŻGß ŹŚiŢ˝{NIą– ušŇ’çřX##˝–śo,))ąp>4>)“P0 ÄŃÍĂ×·Ci|äęĺ«˘Ó ›L8Fxc]zblrN^nvR|Rzn^fřĄ°°Ű­üüúuď”yňĚĄśűq׋‚‡%^9xččő¨¨âJ¦ŕ@A“uöü%ÁĚB¬ŃDFś»“Qéďß»0őęˇ#'˘˘˘ ‹$'ÜĎÎÍÍNJ¸ź‘™y?&!Y~O########Ó(/÷ŕ·Ń:öaŚ1 " ő´FŹClôCŤhcL iŐľSpP ®.>{úÂŘóQf2MHanDV%ÖŐ" ˙„E4qv˘ˇFŕE Hinލ`mg ĽNPPXXPÁH5ý,”ł°ćřЎšő*ÁÜ\‰€š b‰F7pčé^c»{8f%Ý>ţÂ|ŮfYź·^ĎŘůş&×úĺR?RFSJ̬˝Ţş>úbGďqÓąôőK*0FäϦJxą‘>FĽRĺgGúR?„W]Cx^TÓŘÎ 1 –®°D|ĽÖfJ}ΦeX DŦú‚€R…ąůÜ“ÔöŽ”!JD"JD†S"ŚŕŐ,„$Ý ×D*5x#Oş_]kBP"śŇŚcÁ(HsóźµQ)9ot˨ŻE@1±'N>0™ššĚ—Z|„1""Ě`JČscŚDQÄ ” Ąb„ ˇĎ´iB,ËHm0fP‘†a)‰Lbp˝2±(đŮ­Yż.x@w6kó†/ĂâÓĄşśOë4écOŹ”RJ ˩˙Ô«•‚Á ‹(ˇőźF ĎŽTú ´~Ž˲„„̲RAĄŽ!„‰‚XzFđ„ňż`ŃedddddddţAŤY(欼˝=T,ÄőE÷îç Söó´`Ś‘‘W &Ó¦c×Ýk+ ._ąiäEĚYuěčaĆ=ń)Ä0 ĹĆŻ(0 +ŠBŔ¤9ż|÷EMÚµY“çří;Ąb°É`”r+,l*TWĄ%"‘.±ż‚…PĆÂ\1 }mŽëňrjő"nçi]UY7rJ”łŤŁ™Š ° •h´¤}Ű, ýĎ[ŠKóË´l[W7–BepEi–¦J˙ńö GúÝ:»cÂÜŹřúş2ݞöSöó´dŤ— ¦çŢŇacD)E‘R`FşĽ"…+Ź·žçm\}&őŮł§H ,Ą‚T”Iq"yŢ}•úO*×÷!„€ÔďűźŇiÂÇšiĎ{ŞXxĚ{›~‚1–ň¤QJBD m¤‡3`§Ďx=.âřťL 8xřN ę~tçŻe<‘ÂBBę§—"ĹxO)˙ůE–‘‘‘‘‘‘‘ůźaÔ^Ăîĺ”ĺfg嗔߽zŘ\ćßRYˇ)(*8óË7@»ˇ3ň4Ő23Ë«ŞŻŮě  h?,)÷á§®RKąĄš:ăư,Ś˙nAvâŔnž¶oíŰjag©´˛µ°·C*›O¬;˘˛P)­_Ůb f7쎻3Őm¶j{{ĺČ9˝BN îőúĽśŇÚü‚śĽ=Ż˙rî(÷žă µ†ü‚żŠĹĂďőb­ľ  ///OoÔn^1 ě[uŮv&.%rź”őëĎ×0‘>bţůĂ5 ŮňĄeăË…^¬.ý±ˇ fXč2v©¶,§›9 !@O÷ńezŚHoféřźĐiz¤ŤřüK{ď3ßÖHBR‰'FÝ9»L»|Ľźô'ĎÁłŇ’bşŞ™úYjRYFFFFFFFFć9°ŤZ)eYľjĺŚáa©5ÖHĐ™Ŕ3xĘŠC—ôńÖxOŮůŃGvî˝}uŢÔŕóŁÜßżřă¨A?ěHć•bÍĘ7††ĄÖXŁQ@łŽ#Ç !™g#^x„BmŤ&:.]´vQO ĺ¤}»8Ţ$(,ěô"€i5iß>{ö,Žö¬ÁD-ÍqnÂއ >Ý9Â9'uÖúŰť<üĚÔkŞX4¶ß…T˝Ł•˘0ż¨CđΠykLź°´żŠ…1TĚÓ7,MooÎTUiĺywRł4;‹Żöô3 …vÁSV>ZÓ'ź>¸#4ađăg ˘8˙Qăúúx”神ŻvrOĽpúBÜ}Şpp}cĘ„VÖčĚÉ#±I9ń…ŘCFöěęŐ  @Útě:áőeIĺ:>íÎŤôü ĄŁŰ´Éăé ÎqÔs|ěĺGú¸Ď[ٱ©úJŇ 2Żţ˝»¶WRS^aU‡Ž­NÜ›VPĄtr›6éQ MGß Łi ëŞJKŚ<źCzą*~Řôuž@€sqÖ»ăĺ*ÂŤ7*év”hëţZĎN b””CěI/ÔľÚ19™˙zŹj¦ŚÚnůÚĎďĄŘ÷kľFăߥ_ezÔ©µo÷ŽÂĘJPzôÚqhźöęް›Ć^^ŔÁDJ1”Ö–ÜżĂŰr&ŁčŘĄĂp”Ô–ŢżĂ۰Żf©3€GŻÎŮ÷ŠĚŰ;zŞ>ú&(ľ—\˱”'LßľL[Ł˝•ŮĄÓ‡L&“lż~¬«éŻf±µPܸz)9ł°‚}ĺ,ż0Ŕ§ďckjčŇ®+„&„ČĂf@(¬úrç§‹GEߊńđYfe›ÂČO[6&ŢděŰ-šó渑CⲴkľúő“…#ŁoĹxvYÎ ŽeTصö^÷嵌ŤŰ˘ĂűřFÚ?{ŇŤ)Ϭ„ął¦Î?&ün.±(NĂ %˘Ą×Í?~_š|›±o·xŢôýłXŻĐ Ö5y•Ěě®NsG÷IŻ´:~ö¬+ÖH:oMńN–E»>Ďó±Ź!Ú¸<îó’÷ľ0„@QBaôĽ×OôĘŻ ¨—žÖu“ż-‹HőÓ_€T!–Jď67çôFęçÔ±Ť…B4•Ü=ĽŰ\Íľ’…©10m>lIpŰ–Ęż%Ô9; o!T× j+*2ć˝7BDÎvęŐĹń‘ĺuŔYNXřŢ‚˙jGkĹG«ó¤¨ć_ńĚi*µ kŞR<á 3„ŞÖV,ś°ůí kw…Ú´mo‹kˇ¸ňÍ7g´q¶¶°ńúöŰu};Ĺ•ń«OÚĽlěÚ]ˇvî•5yŢcD·~î° .ŮąžŢn*çÉÁÝ[lXüYś6nů坹×ŰAIEręŇŻĎś9«­‹Ť…Ť×÷߯íë×˝•ÓW®¤Cç´çÄċ۫*uAf őm¶aɧ’Κąă/.űš×VĆG'é^äcŤŹTšCôŚĎO{ăő€ ŮĺMć[c9|üűŤń­.Mi6;ĐoęŚfA>.ßţ4¶ ľŮľcŃ”±.Em[(ŠĽ»ô`zMI8żEĘc˝kÓ˛©wCż_J©Xŕuµ:€©­Ń &Ąű~ăm•˙`«Ë‡î9®ž2€źx‘ĽdjA™˙)žŽj( 6ýŇÄŃWDçľ>”yď|đľ•ĺ-:N^ě=cŇä9?źR €ä«Gzw í5řŤÝ?˙mÎřţ[Ž]ś2öŞŃhäľ>”šÔ3aűYľôÖĐ>ťä›ŢŹ!L‰‘Qµň÷źť9ŢČł*ËšĚ{¬Yű!ź˙ěäČľ’…1¨ť5%×qÖÍśět‡7ő;ôŹ›U˘ 0«*—Žď}*Ig®VęôzďŕîȨY=ľ÷©űúżšĹLÉxžĹ řçoZS]sŻ‹ťó¤5U1O/BQw_7N_růjč 2«xÁÍ'hë¶í.*CFv!¦†š:ÁĄ›;§+–ÚčňŇ+LĽŹź™VSx7®D+ŁŃÄÚą:·r«./ë7eţ0•˘<3)ą°4ÔĆÁ"¶îč·íIąń:káěęVr«'Ą”Vh±ÚÖąŤguąć‘NA0D yˇŹ5>R„8ŽŁD”r éóůÔ„ĐŕÁ˝¶ź•:ö˘ßKË5ŐZ“VŻb@dEÎĆĄŤyµ¦¬ß”yÁ*EEúÝ$Ť¶…W»â”2r/1_Sͱő?:333,`¤R4Ća \S­Ukő*L‰ ¨´:r@########ÓĎyW‘ôřśň"!Ŕ0¦˘Şs«oß[{ážił[ó­° Ôč5á!GK6}ĐŢĂŕš´MĄĽH •R*söî˙mĄ)ëĘß<(Ľ°*‡ôéň9f*k[3[g(E@)ffÖ¶f¶Ü+XÔ6,ĂFŐŽ.f%Z:˙Ý}]ŮOďŐ¸ŤsŞŐŠ‚(M&Ńd2P"‚ç˙Šž„˛řŃt˝iKě­mż{˙MiMOWk=–Ţc”䕳fV-=ŰBf ñŔ CÇÎk‰ÓŰ{©kŢ3-.TÁ1%yVm-µĚđDJ—ŚC(%ť±ZoŞÎž:$¨\–SÔ'mŁâCŁ„@đ¸ĺ^ g8ľN[X`ďÖŐ@lŢÚÖŇ’čkŤZ˝©úÁÔ!%Ś(ŕěÚ®˙`MŁ>ÖčHĎč*Ŕ­×°mß|ZtóŔě_ň€¤j° ŢŰTĚX˙˘H@ŕ UUbs@D]Ť±šj˛§ \.€ŇLi¬3.ř`”…SKŔ85S+ą†Í:#o0ő<ŽSZ°¬…‚Á˘HĄŹ”©(4<“ă™gx:ŞaX, dú˛MďĽásäđĺŔ™s¬ô9Üż«/»“˙AďS÷z¶ň~Üurň’>[Úďŕ3ľA“ş9™Öś;˙ëż/ěyđ@DŕĚ9¶ĆÜ󗢀uî´rĹÂŞ›v<(<˙^ Ą”<Ôç±ex]ę…u‹-­”u•µžŁç»w°äk’/ĽżŘÂRńJN_eô ?t€§oŰ„3INjK-¬–÷´Ý}žqháńń¶ŁóŞx{gçĐ_>8›o´oéöÉÖŁóµ9‹­ý•Ă›?ůÇQI¤¤©őm"R»p,!w™´¦Î†ś=!ŕáa0 ” M\ŘÁ9ßí:ëüĹO˝GN<µ}]bbĽăšUďż·¦eđîÎöćÖ¶4ëěĂ6˙č5rňą_ׄ‰-š9łŘŢÁŮĹQ±˙Čîo?ązxďľK±“fÍ8˛ĺĂÍżžeYFÚâK1D~^†m‹9’˛«ŁŤ-wčÄ!ţłEˇ§k<ěÍ BŽ˙¦ű0˘Aç趏آnĺű<kt¤űBÂŔĘ®Y`/źlţž‚^ Ó—mZóFgÉçmŤąçĂoŔó2P7TÍ´˛µ·27S3¶vV•Ęşm›Đµß._qůÜá˝{/ĹN™3{ĎĆĄ‡Ní˙|ýą]Ű~ÚôőlŐ c`·ň˝Ą]z÷oŮĘuÝç_ߊ9żűr’ÉşÍ[wP÷¶.Ža-­m­ÍÍÔÔÖÎJŁV[:ÚŰÖ/˘r`########óĚS˙F”‚I ŽnľľJă#W/_y;ŁęĘ#®Äř ¶ÇEk.‰Í)“ÚĘŞS×®¦Ň„ß]z; aÖľ­ŰĂO­ş•š„×——•^Ľ—”ŮčfLzFîĺ×oÁ[óüÚ9ŢżÜzÂtµĄ%ĘpB ;ŹÎŽnÍ) #ÉbďŃŮÉÍQ`1°‹kďŃŮ®Ť3)F3 këęíŕęBDŠb†Ą”uhŰľŹGA‹Ľäň:•ůŞ5ÝZZ ß¤ĺdç—•d'ÇĹßĎ(+-ËÉ+()ţëYŠKs“câSň|ňËG ^Ż+Núĺ·“"`„ţě>—bŚHuiäŐ؆5ŤJ)DÓÇŻ‘ ` ?jTŘ X•{ä؉Ű×oVb«ŕ`˙´ËÇ÷ ą“WT~îśł:P›»˙±˘ŞÚŞ˘ě¨[·uD¨­)‹ľy+;#1,âv‹î=ú÷čś{ĺȱłĄĺŐđ𸝥޹S ćÆ˝&)ÇÇ'dĄĆźŹĹvf™×“<»uą{ůřőč[á‘1Í»woĐ))×RˇNSZňkd¤×’ó@ŕŤ%%%·Ć'e F<ćó«˘Ó ›L8Fxc]zblrN^nvR|Rzn^fřĄ°°Ű­üüúuď”yňĚĄśűq׋‚‡%^9xččő¨¨âJ¦ŕ@A“uöü%ÁĚB¬ŃDFś»“Qéďß»0őęˇ#'˘˘˘ ‹$'ÜĎÎÍÍNJ¸ź‘™y?&!YgddddddddĺĄîf ž¸¤ÜČ•eôT„/QaL iŐľSpP ®.>rnĉpÇV­(!€0f°I§5éy3k„1 2éŞkë8•ŤŤTi3HĐk•,¶°·cX(Ą@MµZcť`nkËp,P*BµĹCK?4W –Vf:˝©Dc2üd­Ž˙?tPŔ٧ř¸Ú=HŠ9ţÂ|ŮMh5µ¦ŤŰůş&'đĺR?RFSJ̬˝Ţş>úbGďqÓąôőK*šĽÁ˙Ę#}ŚĐ«U˝|v¤/ĺ]Żş†222222222đÜĚÎ5äŘBĄ@)ĆőĺŇ€RŔA(%ő[Ě'7n”ŇúZ*± ”Hw˛źE:ň”—zo{ZPŞ07g©¨ŻŞB@EŢhD ‡1č«*–"ňFË@gń&-0(”Ę›D“ŽK×VÖMFŚYs –a(P^g01ĂŞ-,• IbCŤNoD kmmEŃh" }gXQ"bĂĆôŻh"bdČMá+/?f0‰ôčsO[a†A˘(b†J†a¤b„ ˇĎ´ičbYFj1€Š„0 K©ŘHd‚€ÁőĘ Ć˘@ęjŠ’ŇrÇż9Ç ÍzsÂ{I%B1řIť¦|왑RŠfXHý§žŠF^2¤Á ‹(ˇőźF ĎŽ3 %! őşeYBDBfYL© ŠRÇ DAŚĄO)żňBËČČČČČČČüwÓČ»iŐ˘­Gď^=jKs"˙¸ezr‹´ěĐŮVW™›a „˘–^ťlëŞsňl\Zy¶iNaDŠłäi*Âc"ŠŤoš†E!`Ňś_ľű˘&íÚĚÉoő>xÎś59yŚ…Ł[b2¦ęňŞÂ"ÖÜŢŢÍ•5UŽŇ}nĹrłkő"öô´®©2ffîŘ×Ď·¦,÷Z| E(µwpęé۵VóČbăŘŞ›o]YNt|śdiŃÖ˝WĎş˛Ü†‘ţu-עy^¤Ŕ­ß~tŃHż[gwLű__WćUÖqĘ~ţ–¬1"â˛ÁôÜ[: ĂbŚ(%˘(R Ă „ŕaHó¨ đăő¸ăw25ŕŕá;1¨űŃťż–ń!Ś1"¤~z !€óŚňź_d™˙I†€AÓUÔÖeĄ¦–WVGě˙®€XhéýZRq]ôńź8Ŕ\%.d L\ň­¦şňAfFZjZmmŐ§K'Ă2M|#ËŔůďd'ôő´°wžńóß­,mZwT›)Ü&˝˝ňnţĽÝß[ÚŮ´ń67S¸NYłňÖÝ×§ŻžŢVa«¶·WŽšÓűřŃAcćŻ,×s˛˛*kôűŮ `ú˛j#źť™Ů` |sIŤŃ›™®­©ą~ôG˙©‹«tő#˝rhł%€˙ÔEYKýZ˛kŢiŰ™¸”Č},€ôjíO"}Äüó-!•š‚˘‚-_ZJöFZ˘ =Ń̰ĐeěRmYN7s0B€žîăËô!@?ÝňOë4=RéŁÚkŘ˝ś˛Üě¬ü’ň»W©ąĆ§Ł©ok¤‡!„00ęÎŮeÚĺăý¤?yž•–ÓUÍÔĎR“Ę222222222/ Â:ô0oć$s€®ĂW×ÖŐÎ ö@Şżb¬«»vb«PÔ[NnUpÖ:unÝĚqÜ‚5•ĺĹÁś€S:Ť™đƨŔŢ Ń­Y}T3ďÝÔÄHgi7{Ďî×·xűVňěíߪUމ'o.=}bôŹ{WÜJ™˝ý•JýĆ©ČĐÁ– łń·1»7xŔşŻ:hÖ´‰]‡ŻÖőÓýÝ]:ôXôÖó˙"ËŁµXµ9,9ň·úÂ+rË+Ĺí‚çč‰qv/ŹQo}B)™;Ľ+° ~ÂâüGM^űţűógLp˛b˙ˇc׼÷·÷V/ëěŮ0~ÔfÁ¬ÉVĽç¤Ü‹›5jšµď íÖQ’R:¸ľµôťőďżŰÝ۵ńN#vŕĐ×W€6»®^÷ţŚIŻŹ1ÂłĄ¨ÝžĐŔśăó|ěeF*E5fíGĄd¤ŹďëiiďŇÜÖ¶‰X#ŻţłćÎ_0gÖđaŻŻ^˝¬] P9=ŃChÓ±Űę÷Ţ™9}iBRÚ‚ŃÝŔ§Çyóç/™;Ů^@ŮĚăő±ŁmÄŤ?ÎŁµ‹k—~łç-hPölný*+-#######ó?Ž´Ősî6¶¤ĽrÁ.0vńש QŰ~˝piŻ9 0záW fŔ—‡oÜ Ű*]Ů1ď8ŞL¤‘{”Ť>’njŇ“®ŞŔ*ő˛?âţ–ľ:>cĺ…Ó6–ăGţ-1óť¸äŐ ™«.ś¶4·uţĘŹëěұŮí¨ÉĂ:[ŔÄ%ýš9«¤­ÔçąÁť€ă¸˙:‹Ő 5[#R"÷ĽZTò漻ł 5Ň®yűQ風˙qĹ,Ŕ죫VŇĆ}Ő—{tŐ•‘/敯śá.îeĺ…ť:réÚťěű·şąYŔšŻöJmňKK–NíßĚoliµ1-)!.%»*7©o Ř·;{ë~rĚg.ţ‘—äÓ¤V ÎfŮaxBZ¶¤ś“Ý©ąBÝÚçNvIvbLüý jŞžě ćmCŁ“twqKďŃĎó±ŹTzU˙®¦ĂČôÂň+§ýôņ>í[75­HŞôî¶p]E~jf~AfJAyŐˇ sAír::EęaAć]˙öęV~‰ŮeîÄ$$§ŞŠçŚđ€Ů+LÍÉĎMŽöµR€ĎĹeEY]”śWAUÍ‚ŁfqÂT]Tݬ©8ňĹ@ 222222222Ď€·2‡E§oľÝhČĽ|ěěe«nź®žôŃŰoÇ<Đ«9Q'€şuŹĎß™,YĚŢ)ŘşĽ9¨ÓŃ˝Ç Tj⢓Ó˛€T?ý!bÔt&ĄŇxĺăUL‡á-=[««Śz^©4^ţx%ă5Ľ•gËşZŁ[kŐÁßęśť‚7×ÖđÇP©Ďu§/$ć0ĺ˙Ű,aw}‰”bM‚4•Úe~ŇR÷Rl†JńÄŽa†PPµ´bá„Íoż0xpç^ţ'Że@qě›oÎři÷ţ_wPątčŰ ,{®Z”š<¸wÂöłRÇ^ôűaią¦ZkŇęU ¬Čٸ´1ŻÖ”ő›2/XĄ¨Hż›¤Ń¶đjWśS@î%ćkŞą‡oĂĚĚ`̰€‘JŃ †rMµV­Ő«0%* ŇęČ ddddddddˇ‘¨FĘp»ö»źŢ° —\€+4Y÷2¬ç-^hggÉZ9M?âjqFb†óĽE‹lí,YK§ń#:ŘaúÔ17OlMŻĂ‘çěÝ×°Ć”uĺď?^X•J˙Ł ´PÇţüq|ŚnÖ±pg…¶¸DiaűóÇq·u3Ź…;:Ş]±csu‰–ηG_WvCRMçžĘť¸ză¶ú>§3 Eňh˙-ڱ”VëźŮŢR@P¤-±·¶ýîý7/Ü3mvk~şZ衰ôŁ$Żś5łjéŮ2SŽ^:v^KśŢŢsH]óžiqˇ Ž)ÉÓ°jk© `€'Rşd€B)1čŚŐzSuöÔ!Aĺ°ś˘>i­0¦„@đ¸ĺ^ g8ľN[X`ďÖŐ@lŢÚÖŇ’čkŤZ˝©úÁÔ!%Ś(ŕěÚ>ĎÇé]%¸ő¶í›O‹n˝âKT (/RB›ŠëżB ĽˇŞJlލ«1VSMö”ÁËPš)ŤuĆڞpj‰§fj%× Ygä FŁ^ŕ€ÇŔqJ €µP0X ˇô‘2Qxň›eddddddddžŹ”˛lÂüĎ)Ą)ńQű÷ŽşrqŇP?07ăŕĂ=1%±‡Ŕś™’m°pĐ}üJJů7ý]€ĺ80ď4VGiÁŤC/ľW3|î;)w.)”ö.“?{˝|'Np9ĺtěě?ÇNľÇ» y}Ęá‹7/OöuUyôi=ŘϦy;ç­;˝µj#Ą45!j˙ľĂ·®Gűy yă†QüwX˘®\ś4´§4o+7ťOŠŘýj÷j¤”\¸ąw\NٵC{÷»YUpϧ•<:sUŻjńchś¶(sÝňU!a7ćL č?îoƚҿż·fOd<Ąô˝#`KŘmQćşĺ+O…Ý\8Ąk˙Y†ęâ.*®Si];/HáÜëA9ëřŢeKVüq;nů[#€}OňşŃs>×U7(Ż`Öb@a•xăÔáÓ×ăxťfękžć­úgiL :oĎ Ö]'<ĎÇ©o[č6zĄôÁűĄKbłVlľw;rý;ëŻÜË­Č»ŰŐÍţ‰Ůxf Ą{5OĆýşrÎĽ5ż…ďţ`É;۲nmëŮ'ĄÄ}|ď˛%+®ÇÝY0y€Mç` O÷műa÷ůŰ”Ňů#}ěVľ÷Ńîaşęň˝›7/ť1ܢu߬JĂ™ßwlŰI)ť9iř;ż^ŢűÎ|IyŃęźóĎsĎůÉČČČČČČČČ<ťę P …Yvfb|bZuMUaa~\|lnˇ†R ”ňĽ!ínLôťÔ†ś’ĺöťT ĐŇÁ)ăîŐýG#ŚT: D©PW^VzńBh\RfŁO™Ągä^~ýĽ5×ĎÓńFÄŐćÁcl[´6wP‹ĽČš©›űú©9ĘYÚY9YŠĽČŞ-ZtęÜÝ>­o7›ü”JÚrő;Ý›[°'Χ¦Üą{?łZW]T\źPZYWR—Qó|KqYmQ~rÜÝú‘ĆÄĆ•”ë˙Ę–„řŰ9…ĺ‹6lűpÁŘşâ¤_~;)FčĎ>Ŕ§#R]y5Ö7h°=.ZłpITJ!Â>^%!SřąPŁÂ&hčŔŞĚŘ#ÇNÜľ~ł[ű§]>ľ÷HČí輢ňđsç śuĐĐÚ¬ŘýűŹUŐVeGÝş­#BmMYôÍ[ىa·[tďŃżGç”Ř+GŽť--݆‡ÇĹ(H˝s§̇ {MRŽŹOČJŤ?‹íĚ2Ż'yvër÷ňńëŃ·Â#cšwďŢ SR®ĄBť¦´ä9>ÖČHŻ%ç€ŔKJJ.śŤOĘ$ŚqtóđőíPązůŞč´Â§ęr62‰Ľ±.=169'/7;)>)=7/3üRXXÄíV~~ýşwJމţ€@‰eEŢD€Q(9©Ź@0Y(`Ś µ51żďĄ‚Q°GE‘bÄóÂ_ÔBžX‹nńĆ0)!ωj0ĆHEĚ0@ !Ŕ0Xú'F‘úL›!IJŚÔc a–6ô"ÄŕzecQŕ ˛[ł~]đ€îmÖć _†Ĺ§Ku9źÖiŇÇž)Ą”"„–R˙©—‹»ž3,˘„Öa ‚ >;RĚ0”„€ÖĎ1bY–‘‚YS*˘Ô1„0Ă QcDéSĘ˙šu—‘‘‘‘‘‘‘ůŻŁ‘¨FÚFµhëŢ«g]Ynä·LÄ´íص{G÷ÚĘ‚ËWny ›C0uĄçĂ/y‚Š˝zőďććrďÖÍ»Yą€P`€źďÜ{řňŘÜˬĎçşôşüvÖş[{{÷úäđ›Ţrč1xÉÍŚ÷nÝlç×±űűűgozWÉ‚ĘÖ^mgŔĽöÝé™ß¬Vr’Űş×Ú-*»Ö~8VUY^PTpzËF+‹żo ©¬Đü%-_Z`»fť¶ť‰K‰ÜÇĽRĹyé#ćź?TŮňĄĄdo¤%zˇĐ=Ŕ ]Ć.Ő–ĺt3gë=ÝÇ—é1B ˝™i¤ăB§é‘ÖWáôv/§,7;+ż¤üî•Cj®ńéhęŰé!BHJZŔ¨;g—i—Ź÷“ţä9xVZRLW5/“@N #######ó|žÎěL(€ÂŚ{ď-™ůűo‡=‡Żľ~|ĂŔźöÄ^ť7%ř|x”{ŔâÔK›_ë˝Yë3Űż×Ý»Áid|Ôţ1¶+i˙ńĘé_Î úň÷(ď®Ý‹ Ŕ#ÖaĚ!¤"ólÄ o€P¨­ŃD'd çÖ@M˘‰ @€@0šD“ ^4 Ţ‹„DŠ–6JĚŠaA5ł±Sc‘á8^WŰlňĚÇsVöŐ !‡?ŢŮşcKë©3O c-ě´ń§N|±żYűĆÁăV. ~{`—˛ŽÓCv~4ú÷-abź5 ‡-čÝAăý´|8é̡gă+ŠîĄfiv_íé=f" í‚§¬\0tIoIyňé;B Ź˝ń@RŕüGŤëëăQž—zňä‰Ňjđ:ÚŻk{FĐ…†OL/ŔeĄ6~;$ÔŐ•”űľŘÉ=ńÂé q÷¨ÂÁőŤ)ZYŁ3'ŹÄ&=häÄbŮł«W2iÓ±ë„×G”e$•ëř´;7Ňó+”ŽnÓ&ʤ€8ÇQĎń±—)Ą,ËW­ś1<,µĆ’ŤĽd|ţ" Ľzř÷îÚ^IMy…U:¶:}poZA•ŇÉmÚ¤G=€6}'ڤ)¬«*-‘ŢsúôŇËUńæŻó ś‹ű°Ţ/‡„Tn̸QI·ŁD[÷×zvR٤r`OzˇöŐŽÉÉČČČČČČČü×ótT#•ěHŽţ#%†ˇEĹYµ:§P‘Š´Čk:;tk«)Î).×¶¶o‘sďzr^ťXű ŞÖ»u]Ë®ŐEéĘĂŹ[ůű/_%%@éŃkǡ}Ú«{Ă"n_xyc×oH=î–tKŹŔˇáµĚqúâüô ć ©**±dİú⼇–Rg†A 3sG Gë{›"ś{ uiăŔWUX86SŰ[Ţű.˘™ß`ëfÍű´÷«Ę¸q2ŹŮůÝ4c»¶=˘GyÖÍSyĚÎM9 !†.ž]â0V°čUŕcDŔźľ•éQŹ”Űu…Đ‚0yŘ …U_îütń¨č[1>Ë̡lSůiËćÂÄ›Ś}»EsŢ7rH\–vÍWż~˛pdô­Ď.Ë9cÁ±Ś »ÖŢëľü –±q[4cxß(CŰăgOş1噕0wÖÔŮăÇ„ßÍe0ĄĂiˇD´ô˛ůÇďK“o3öíĎ›>"°ëzÁş&Ż’™ÝŐĂiîč>é•VÇĎžuĹIç­ ă/Ţɲh×çy>öâ‘"D„)ٶ[ľöłŔ{©űvď(¬¬|a0˘„Âčy®źč•_ P 6.=­ë&yćČ™P\–Y óßzsÚđŔŰz×3ˇˇÚ­ÂÚ«™ĄJu0jÍŠ±fú¨çËMúö=‡l˙yMPhHq˙yço–O3uxkŰ’ŢĘD ¨k§ŢdâÚź3ĐP¸FFFFFFFFFć!lŁVĚp,âMÄé›o7ÖeDś ż í 9°őŰvmťďEşś®S•Ő¦űüáýş îc<ślOA ¤ą›ď`ďÄ{Őâ‘Đ‹3G ŘšŞ+㢓ôiY@Şźţ¤\jRvi©áŁ]Ąd’ŇIŠJ]nRüţ]*\S’[ćĘ1S<˛<(mˇd©ŔSŔşÜÄÄ[qÝ>\Ăđz#!‘,ń˝>_Çňu1ĺ•Úĺnh©żo0Sp¦J»ěĂOţ‚–K±u*ĄTü+žŮ#MĺcĘŠ'śa†QŐzĐŠ…6ż=aí®P›¶ímq-WľůćŚ6ÎÖ6^ß~». o§¸2~ŐâI›—Ť]»+ÔÎ˝Ł˛&ĎÂ{Ś‚čÖĎv Á%;7ÂÓŰMĺ<9¸{‹ ‹?‹ÓŔĆ-żĽ3wbřňo"()–®Kż>s欶.66^ßż¶Ż_÷VN\ą’ť{Đž/nŻŞÔM5Էن%źJ:k掿¸ěk^[ť¤{‘Ź5>҆L€ECbBtqVIŻ) §˝ńzŔŔ€„ěň&ó­±>ţýĆx‹ŔÁV—ʦ4›č7uFł —Ťo[ßlß±hĘX—˘¶-EŢ]z0˝¦$śß"ĺ±ŢµiŮŤÔ»ˇß/ĄT,đşZťŔÔÖ茓RŽ}żń¶Ę°ŐĺC÷WOŔÁOĽHţëłüÉČČČČČČČĽŤF5ŠĽ Ô_ď:<ĘK俤HŚPň•Ă}ş^č5řŤÝ[Ö-Űóçm›v í·'$!Y:h23YŢĐĚkТćE3&MZđËi%ËQť®yű‹íĚ4ůŻf™óó)Ă<śŞö¦1Ő5÷¸Ř9ďIĺ‡kp÷uăô%—ŻF€ľ łŠÜ|‚¶nŰî˘2ddbj¨©\şąsşb©Ť./˝ÂÄűř™i5…wăŠ@´2M¬ť«s+·ęň˛~SćS)Ę3“’ ‹@CmŚ1!bëŽ~Ű~‘”‹ŻÓ±ήn…)· pRJi…«mťŰxV—ké CAÔúXă#Eă8JD)š.ýŇÄŃWDçľ>”š<¸wÂöłRÇ^ôűaią¦ZkŇęU ¬Čٸ´1ŻÖ”ő›2/XĄ¨Hż›¤Ń¶đjWśS@î%ćkŞ9¶ţGgĆ`c†ĺŚTŠĆ €1 ”k޵j­^…)Q•VGhdddddddd?kBR€µßýôF€m`ŕŘ´bŚbX*55šđŁ%Üľ]' ĄoOjÖşĺy«L”Ë)KĎ7ŐQ*¨Hy‘H7¤9{÷Ď6můhŮé^ű şBĄ-ˇśąµÁŔs6vć¶6T02J§¶Ş3đЇÖL 1˘˘@Dq,F?˛°BeEíť°s-µ˛ćD~h2ÄĘZ‰ˇH[â`ç°ďŰż]¸gňqkY^[Sٱ3·Ü÷ÍÚż Ą‹[ó’ę2i¶ţ™í-EÚ{kŰ˝Ź”5€÷%y嬙UK϶Ŕp, ;Ż%N÷öě0dćÚj+8¦$OĂŞ­Ą6€Y Rşd€B)1čŚŐzSuöÔ!ŻőîŰ7ŔŔ;ߦ@ëŚ)Ź{L™p_§-,°wë``ŢĽµ­Ą%Ń×µzSőťwż;B8»¶Ďó±FGZŞ«·^ĂÎ˙łűŰwĄĽR5XÉw)ˇMĹŚőł$ŠŢPU% ˘®ĆX­7ŐdOüZď>}_ řéO‡u §–€qj¦Vr šuFŢ`4ęx §´`” ‹"!”>R¦˘ĐpđLŽkddddddddžáéw5 Ë‚8aţç_9+5áĆę÷żótµ˙ŕí·-şÎüjMŕßOűMňµ7m¶ă©Ó?Ţ˝~®˙äU¸ ć\xL®Îßٵoç5Ť"¸Ýě„ë ré´rĹÂŞ›v<(<˙^ Ą”<ŚzűÎđÓ˛“ËŮŞJ$×vóneÉöEé?ť\ľĐFYš•TÝ«{{cxo)PB zĘ‚oę-F1ň¬Ú+Öąujc2ę-š»9{¸# ”Uٵ÷¶q4S9¶qńtG@(Ł´k×Áľ™}m~¦e{żv˝»ÁĺôEŮíü<{wĄ"˘ţńĺĺ+Ń=‚íŮ’5 —^O+˝ćʵŘC;˛Ek.˝žVzMĕߠÁö¸hő‚ĹQéĄ˙)ËšEKŁR  >ţ飅c늓~ůí¤ˇ?»ĎĄ#R]y5¶^yá’¨”B„1}ü B¦đsˇF…MĐĐU™±GŽť¸}ýf%¶ öO»||ď‘ŰŃ1yEĺáçÎ8ë ˇµY±ű÷+ŞŞ­*ĘŽşu[G„Úš˛č›·˛3Ă"n·čŢŁŹÎ)±WŽ;[Z^ _9IŃYęť;•`>lŘk’r||BVjüůđXlg–y=Éł[—»—Ź_ŹľÓĽ{÷ť’r-ę4Ą%Ďń±FFz-9ŢXRRrá|h|R&ˇ`Ł›‡Żo‡ŇřČŐËWE§6™pŚđĆşôÄŘ䜼Üě¤ř¤ôÜĽĚđKaa·[ůůőëŢ)9&ňä™K9÷ă®% (JĽrđĐŃëQQĹ•L˙Ŕ‚&ëěůK‚™…XىŚ8w'ŁŇßżwaęŐCGNDEE=HN¸źť››ť”p?#3ó~LB˛ÎČČČČČČČČ4Ę«ŢÍxúÎ2B@é3Ć—9.0¦„´jß)8(Wź=}~ř‘s6.Í#„EÎTg¤¬J­0S Ѩ7ęô¬™%Ë‚ˇ¦†VYYQ“^¤ Nę-–V ŚzcfˇPŕşęj©& P‚•ć ®ÓVc…ÂX]˝}tI§_ÁF(m‹”í?b‘RÁŠFOńqµ{s&ü…ů˛˙Ě">ož±7ňuM^aą”Äč±cJ‰™µ÷Ď[×G_ ńč=nú ×€ľ~I%B“7ř_y¤ŹzµŞ—ĎŽôĄî÷żęĘČČČČČČČČŔó˘̰,xOfYFAk·#„Pet‚a ”PŠ8–ž0 #Š"B,Ă%ŇťěöĄ sóy§Ăͬm)%@‰`â1Ă „1¨Xoˇ1,ŁPLz=`# ËÖ[ę3ÁcŔ#$Ť0zh‘çŁ)öŕ^DëS&ŢS¸É"Â)0­«ăMçů‡ÉŽ1¦”>ľmýĎZţąĹG#"Ŕ ¦„<'ŞÁ#QĄi'K˙Ä"BźiÓ „X–‘Ú`Ě  "! ĂR*6™ ÄŕzecQŕ ˛[ł~]đ€îmÖć _†Ĺ§Ku9źÖiŇÇž)Ą”"„–R˙©W+QBë?Ť0Aź)fJB@ëç±,KHÁ,‹)DQęBa(€1˘ô)ĺÍşËČČČČČČČü×ńś¨ˇŘ«W˙nn.÷nÝĽ›• ĄcŔŕ–Hw5,˘ÔhBSöó´dŤ— &"íżĚś\Z;Xe&§ »3 #˘gaV…€Is~ů´kł&ĎńŰÚ ‰U…EŚą­›+F"C•¦¶LŐ6 –ş*Ť®ŢŇÓ«XD^aaiÔ–×–iµŤť«»‚ŻĄű»%W—›ŁÓ‹¨ť§µ¶Ľnŕ°‡–űř´«©ČŹĽrĂČ@L›Ž]ý:zÔVţ›-7MĽH[żý袑~·Îî0÷#ľľ®Ě«¬ýS+řĽ– ĂbŚ(%˘(R ĂH±®®<Ţxž·qő™Ô#dĎž"˛” RQ$ĹA Šäy÷Uę[<©\ß[„RżďJ§ kj¤€bÎĘŰŰSĹ Ö¨/şw?—Ц_ `Ś1Ć@)EQ$´‘bĚ`D€uś>ăő¸ăw25ŕŕá;1¨űŃťż–ńD ©ź^Bă=ĄüçYFFFFFFFć©ć˛Ď~ŞŞŐgçć¦Üę¬pnrý^iQnnAIňŤsťZ›¨ľŘzş˛BSPTpzËFK`]Vnř1˝X““xµŁZ5}ÂŤaY1˙Ý‚ěÄÝ<-lćěÝfŐĚÁĆÍĂBĄrż|E|öâă{-mmÝ<˙ĺ–…Gv©m-­Ű¸ŞY¦ÍëË–_‹˝~uęĘi-9K…Ť53|f÷“GşŤ™ťU\•ť™Y^U}őčŹögäiŞüŰ-—}ß‚d׼Ӷ3q)‘ű¤¬_/1ÍĎ,2óĎ·„H+˛ĺKËG‹˙TKôBˇ'z€şŚ]Ş-ËéfÎÖűzşŹ/Óc„@z3ÓHÇ˙„NÓ#•N'Ş˝†ÝË)ËÍÎĘ/)ż{ĺšk|:šú¶Fz’ň2ęÎŮeÚĺăý¤?yž•–ÓUÍŔËüRţŮ<Ţ22222222˙K`Ě@›~“Ęj´›Ö|zôV xs•Q_ůZK3ÖąWąA\óćŰžMTÝËcÔ[źPJgşCëˇŃ·cO9™›çŁć`ÎqĚ„7FöĆŤnÍ꣚yď¦&FŞµŐ˘łŢ:ľ*6mţ_µŮ„٬‰ş9óčůU±é˙?-Ű™ů'#B[2ĚĆßĆěŢŕ~ŕŐµ‹˙€žŕ°ŘČóswŔvmúţű-“iá_iŢVmKŽü­ľđĘźÜňJ±G»ŕ9zb|¸‚dîđ®Ŕ2O$űFqţŁ&Ż}˙ýů3&8Y1Ś˙бkŢűŰ{«—uöl?jł`Öd+ŢÁsRîĹÍ5aÍÚ÷†vë(I)\ßZúÎú÷ßíîíÚx§;pčëŹ+@›Ž]WŻ{Ƥ×GŚáŮŇTŽnOčĽĐÇ^f¤RTcÖ~TJFúřľž–ö.ÍmmźNŁńě"ŻţłćÎ_0gÖđaŻŻ^˝¬] P9=ŃChÓ±Űę÷Ţ™9}iBRÚ‚ŃÝŔ§Çyóç/™;Ů^@ŮĚăő±ŁmÄŤ?ÎŁµ‹k—~łç-hPölný*+-#######óżÁÓ™ť1B  ÷ŔšÂ4e‡áÇŹ­<¸óŰ»š˛’ŠZ×öŇÎPUś‘UŘmrEfÔ©íŐ˝a7Ť/ĽĽ€1"0ćvö–*]eYEv¦BiŻTrŘĚĆľ•ŇPő˙ŃR™ťÉ)ě,mĚ3nä›{9zŞ>ú&R2ůŞÂű)Ąť:vöęÖ¦¬(·˛¦†TäGüˇóîعÿע)Î++/«ź.‹^ő>F„ đé[™ţp‰ˇK»®š@xx‰ˇ°ęËťź.}+ĆĂg™9”m #?mŮ\x“±o·hΛăF‰ËŇ®ůę×OŽŚľăŮe9g,8–Qa×Ú{Ý—Ô26n‹f ďăeh{üěI7¦<łćΚ:{ü𻹠Ƣt8 3”–^C6˙ř}iňmĆľÝâyÓGöĎb˝BB/X×äU2ł»z8ÍÝ'˝ŇęřŮł®X#éĽ5aüĹ;Yíú<ĎÇ^cˇŔIsßÁŢ÷ďU‹‡N‡)‡÷Ř{ţäÝüţqöR ¨uiW¢R'ôU—•W,űđ“–ú»—bęTJNúĽRÁbHÚ‹•ĺqŃIú´,©:ç‹ď)”R„]~ŇĹË* ĘÜFĚnÖÖI¨3č ’-_U™_ú˙ĎŇ&xv W}ŤŃ­•ęŕo uÎNÁ.V5z"yĎÁ‡vmňlë|'ě÷óŃůĐ~ŕ[żm÷o·„ŢĚ—"ĎEΤ©ÔÖŻ`lťJń„3 Ě"ŞZZ±pÂć·'¬ÝjÓ¶˝-®…âĘ7ßśŃĆŮÚÂĆëŰo×ôíWĆŻZs欶.66^ßż¶Ż_÷VN\ą’ť{Đž/nŻŞÔM5Էن%źJ:k掿¸ěk^[ť¤{‘Ź5>RiĄĐ‹†Ä„č⬒^SN{ăő€ ŮĺMć[c9|üűŤń­.Mi6;ĐoęŚfA>.ßţ4¶ ľŮľcŃ”±.Em[(ŠĽ»ô`zMI8żEĘc˝kÓ˛©wCż_J©Xŕuµ:€©­Ń &Ąű~ăm•˙`«Ë‡î9®ž2€źx‘ĽTF5™˙1pŁV…Rm(I^0ećÂYÓ/§k{ąů 1łŹ‡Ů[ăGŽ;µé¶pÜPť¦¤…wŔâŕ¬}ż )ULý™J’n$`„ŚĹ7†öé4vÁÇ<4µ“˛­QѤtj?e÷É[żŻŚ9u˙VšBÉ(ţ[6UĆśJş™lao•őŕ÷‹K—ů‹Ľ‰•jUúŐĂ˝}; ź¸˘™oŕĚqý íÚ±>]˙–Ůú?Ľi˙ĎF"¦şć^ź]Ázu„ŔÝ׍ӗ\ľú‚ĚYn>}·nűő‡Żľ=m0¦†š:ÁĄ›;§+–ÚčňŇ‹JkĚÔfZMáݸ"ŕőFىµsunĺV]^ÖoĘü÷×,-ĎLJ.,B µq¤ ÷­;úműe×_}1{ÚÄët¬…ł«[aĘ­B€’¤”Ň -VŰ:·ń¬.×<Ň)(@†‚¨!/ô±ĆGŠÇq,)@—~iâčŃ‹ß^ŢgŔŘJu›ŕÁ˝:öX––k޵Z˝‰‘9—6í«5eý¦Ě{˙ť%éw“4Ú^íŠSbĘ ď%ćkŞ9¶>¦2c01Ăr@ĄhŚŔDú0 ą‚čáľYzŹQ’WΚYµôl ÇŔбóZâtoĎCf®­ć±‚cJň4¬ÚZj"ĄKF!”ÎX­7UgOňZďľ}üĽóía ´>U1Ć‚Ç=¦L8ŽŻÓŘ»u°0oŢÚÖŇ’čkŤZ˝©úAλߡś]ŰçůXŁ#-ŐU€[Żaç˙Ůýí»R^©,P^¤„63ÖĎ’(xCU•€şcµŢT“=eđk˝űô}-`ŕ§?ÖU,śZ"Ć©™ZÉ5$hÖyѨxŕ1pśŇ‚PZ(,Š„PúH™ŠBĂÁ39®‘‘‘‘‘‘‘‘i„1°-{ŢÉ«ľyxç7?fbß¶Ď3PzhÇßlŰO©iŐ^`Ţ6ˇ âÚˇ˝{ŹÝÔ&uiˇPlřá×ŘÄôÚšŞč+—M –]&č(-¸qH ĐhŞ')[Ŕđąď¤Üą¤P:¸Ś_·´mďľ~o˝ĺŮËq-f„Ýźľqmë={Ιó˙עj3ýä7/OňuUyôi=ŘϦE;§źw ]ţÁÖ¤»Ńź­˙řĚŐű´®  ťË¸ůße$Ç~úŃżŰ"ÖäŤíă&ÍŰĘMç“"vżZ¶)%nî—S&­`UÁ=źV eg~LŐâÇĐ8mQćşĺ«BÂnĚ™ĐÜߌ5ĄoÍžČxJé{ FŔ–°;Ú˘ĚuËWž »ąpJ˙Öţł ŐĹ]T\§Ň:şv^Âą×rţÖń˝Ë–¬řăvÜň·FË2}€€Ńs>×U7(Ż`Öb@a•xăÔáÓ×ăxťfękžć­úgiL :oĎ Ö]źëcŤŽÔ·­ t˝€RúŕŹýć,Ŕ¬›ďÝŽ\˙Îú+÷r+ňîvułb6ž™Bl<÷ëĘ9óÖüľű%ďlËş}´­gź”côń˝Ë–¬¸wgÁä6ť5<Ý·í‡ÝçoSJçŹô°[ůŢG»O„éŞË÷nŢĽtĆp‹Ö}ł* g~ß±m$Ątć¤áďüzyď;ó%ĺE«ÎO<Ď=ç$########óLŞ'J1F˘¶ ňjl§ Án¶Ě§kW ‹+Ëş“’ÝłO˙ć–Ě—ż·ýD$1UE\ľí4Ř­^´ôzjplwMVâŮ󗊵Uéq1÷s )©Ó””\Ľ—”ŮčSféą—_żoÍőótŠşé6m–X­Ń—ł¶®ë6´tw¤tĄŐ%•¬mŰƂѕć×–Tp¶m×}ÜŇÍA bmIž¶¸ś±jí˙·OZµ±jËßĐĂ6÷ľ¦NeľjM·–č×˝‰ µ…ŹŻŻ±4áĂwV„ŢÎ$"ݶ¶ęÔµ«éßlywĺ©«) >ţ飅c늓~ůí¤ýé77cDŞK#ŻĆJ+¸fá’¨”B„1}ü B¦đsˇF…MĐĐU™±GŽť¸}ýf%¶ öO»||ď‘ŰŃ1yEĺáçÎ8ë ˇµY±ű÷+ŞŞ­*ĘŽşu[G„Úš˛č›·˛3Ă"n·čŢŁŹÎ)±WŽ;[Z^ Ź‹QzçN%öš¤ź•><Ű™e^OňěÖĺîĺăףo…GĆ4ďŢ˝A§¤\K…:Méó|¬‘‘^KÎ7–””\8ź”I(âčćáëۡ4>rőňUŃi…MÖ夼±.=169'/7;)>)=7/3üRXXÄíV~~ýşwJމ®v’b΄ß$Đt±Č—Yĺç­ŕ3öFľ®ÉŐo2BxJ!L)1łöţyëúč‹!˝ÇMäĐ×/©Dhň˙+Źôq0BŻVőňŮ‘ľÔOăU×PFFFFFFFF^ćĆ9Ă0„°‚cy“‰0 #Š" „q!„ŕɬ\1‰„B,Ă%‚(6Ő”*ĚÍç…„«¬­©Ô^ŞiOB¨~{řІˇĽ›áL ‡(Dŕ8p ©ŞŞ1ŚÔFÁaorÓdЉĂ"QÄ'Crxţ©Î?» ýwZţU‹Ź0FD$€L yNT1F˘(b†J†ÁŇ?1B„„ĐgÚ4!–e¤63¨Hð”ŠŤD&1¸^™ÁXx‚ěÖ¬_< »A›µyĂ—ańéR]Χušô±§GJ)Ąa†e€Ôęĺâ®§Á ‹(ˇőźF ĎŽ3 %! ősŚX–%D$„`–Ĺ” ˘(u !Ě0HDɇźRţ˙â22222222˙÷i$Ş‘¶Q-Úş÷îĺW[–yő–I ’ŃĚÉĄµUfrš@U(´Dú«#J F†łęĐŃCĹ Τ/şw?—P@ ĂQ$Ťö€aXQ&Íůĺ»/jҮ͚2·ÇîŁ*Ěđ‚cÎÜ‚c^[€ĄýÝźµ%”áĚUh"|oÉčsrjő"nďi]U®5+ÖŮĘNÉÔ—‘Ż©(É×3mZ¶ĹTÄQ„k4ąUĽŰyb""„(ÂŐe9ąU¦žžĚżßH_™w?łtýö‹FúÝ:»cÂÜŹřgޤ˝üÚ#NŮĎ?Đ’5FD\6ČóZ2 +ʼn˘(R ĂH—n¤pĺń6ŔóĽŤ«Ďä !{ö ”ĄTŠŇ )Q$Oľ’zD}‹'•Ü! őűţ§tšđ±¦FŠ(欼˝=U,bŤőŢŰt<‰1–ň¤QJBD m¤‡3`§Ďx=.âřťL 8xřN ę~tçŻe<‘ÂBBę§—"ĹxO)˙ůE–‘‘‘‘‘‘‘ůź„aX4mQEm]Vjjyeuäď›\0v^µáÇôbMNâŐŽ*P6ďpňzbiQnnAIň­ ^Žqr?W“›ť•_R~÷Ę!µ”[Ş©·AR¶€óß-ČNŕëaaď<ýźŮ8ŮŮÚ[5kÁxN[1˙đ.K[µ™­Ă«Zšcϱ3nŢY¶úŤV [µ˝˝rÔśŢ'Ž1wE®¦¦ ?75=˝´ĆxýČ×çÍ.¨¨-ĚĎNKOŐÔÔÝ8Ľqčä™9eŐyŮYÉÉÉĹ•ş?|ńź˛hŞăC·)¬›uţĺl\Jä>)ëןżA.}Äüó-!•š‚˘‚-_Z6ľ\čĹęŇÚ`†€.c—jËrş™ł ]oGO÷ńezŚHoféřźĐiz¤3 öv/§ěĎzď3ßÖHBRŇFÝ9»L»|Ľźô'ĎÁłŇ’bşŞx™$r’™çótNB fÜ{oÉĚß;ě9|őőź˙ňýî¦ ë›ôǵn][+0€~Gű¶ n×üďSś}ýő‘ţ_ž1*IőĘiCĂRk,‰Ń( ŠYÇ‘c†ŠĚł/ĽBˇ¶Fs;>7ł°j?pęŢŔš™ňâ~źµ°y?›v]§î aÔÖ¦ÜŘW°đą1{g-vë5¨"íČ/ >Ý5Ę9'uÖG7»wć÷€Ë)_ެçë!{~Ľ~íJčďç‚îÄjŠ›÷z=dĎŹ\˙ă¡sÁ÷î4XnÜĽţź˛śÚócxX¨ŔX”’©ĐY|µ§÷a(´ ž˛rÁĐ%}Ľ5ŢÓCv~8ůôÁˇ …ÇŢx D)pţŁĆőőń(ĎK=yňDi5ří×µ=#čBCŽ'¦` „˛R›Š‚ŚżęęŠJĘ}_ěäžxáô…¸űTáŕúĆ” ­¬Ń™“Gb“4râ ±‡ŚěŮŐ«A€´éŘuÂë#Ę2’Ęu|ÚťéůJG·i“Ç?Ň@śă¨çřŘËŹ”R–ĺ«VÎ^ď˝MŐW’^yőđďݵ˝’šň «:tluúŕŢ´‚*Ą“Ű´IŹzm:úN5HSXWUZbä řôŇËUńæŻó ś‹ű°Ţ/‡„Tn̸QI·ŁD[÷×zvR٤r`OzˇöŐŽÉÉČČČČČČČüď"=Ŕvî6¶¤ĽrîĐÎ’qЬ•Eą÷}ĚĐ9řÍÜĽÜąşvę7=·°púvĐbPVIŐ•Ó‡~úbCźö­ĄŹ¨;Ś,iFäžgv1ďÝô¤«j•ůŇČ›«cÓßONńńk×+ČűnE»fťšßŽš<¬łL\ŇŻ™łŘąž–xa»9 ˙µ-#Xł5"%rĎ«evfY sŢÝYi׼ý‰¨tQÔ˙¸b`öQ +Ą5^őĺ]ueäĹ‹y%Ĺ+gřË€{Yya§Ž\şv'űţ­nnÖ°ć«˝R›üŇ’ĄSű7ómLKJKÉ®ĘMęŰBöíÎŢşźóÇ™‹äĄÇů´ćaŤKÉŮ,; OHË–”sR˘;5W¨[űÜÉ.ÉNډżźAMŐs‚˝ÁĽmhtrÎŕ.n`é=úy>öâ‘JďˇęßŐt™^Xţ¸÷ľpZë3;ż»-\W‘źš™_™RP^uhĂ\P»śŽN‘zXy׿˝ş•_bvŮ;1 É醪â9#|`öĘSsňs“Ł}­Ôŕ3fqYQV%çUPUł`ƨŮ_ś0UŐ+k*Ž|±Ă‚ŚŚŚŚŚŚŚŚĚ34ľI Ç"ŢDśľůvc]FÄ鋉€Y ‚RÁbT˙>'1",>÷ýś˝Tęęä+aaiJŹÖwcof•ôš˛pÚŻřJxPFµqŃIú´, ŐO„ ‚Ŕ™«R÷|žśCfüľ”ÖjEY©R÷~őj–”2uď2Z«5ńȵĄňŕo uÎNÁ.VÚAÉ1đĆG_ŚńVöëýˇ@É"#˙×¶0`$đŻxfŹ4•Úe~ŇR÷RlťJń„3 Ě"ŞZZ±pÂć·'¬ÝjÓ¶˝-®…âĘ7ßśŃĆŮÚÂĆëŰo×ôíWĆŻZÖřHĄ9”b,˘‹Ľw`@Bvy“ůÖX˙~cĽEŕ`«ËGSšÍô›:ŁYŹËĆ·?Ť-o¶ďX4e¬KQŰŠ"ď.=^SÎo‘ňXďÚ´ěFęÝĐď—R*x]­N`jktFI©@Çľßx[ĺ?Řęňˇ{Ž«§ ŕŕ'^$ŻślPFFFFFFFćżFŁDEŢęŻwĺ% ň_RB€S`Ţ$í>Ă`1ma_wŐ[ăGfçű¶.™7ćŁí§&Ť»f2¸ŻĄ&„őLŘ~–/˝5´O§‡ĘMěÇ%„1ł4ćĹ…˙ôKß5ż;8qúĘ…Ą­!/>ü§m}WżŠĄĎęýŽÎśA_«V+3o¤=HsřqSż?\×Öëj]Ľ†|󷹛֎ąťRĚ)”F“±ŤOđ×U Ăr˘Č?ś­ bŞkî5p±sŢŚI“çü|JĹ~fZMáݸ"­ŚFkçęÜĘ­şĽ¬ß”ůĂTŠň̤äÂ"ĐPcLŘşŁß¶_$ĺ"Äët¬…ł«[aĘ­Bś”RZˇĹj[ç6žŐĺšG:ŔP5ä…>ÖřHâ8ŽQʦKż4qôQŕë˝wpď„ígĄŽ˝č÷ĂŇrMµÖ¤Ő«Y‘łqic^­)ë7e^°JQ‘~7IŁmáŐ®8%¦ €ÜKĚ×Ts߆™1Áa9#•˘1 @ ĺšj­Z«WaJT@ĄŐ‘™Fh$Ş‘ŽîŻýî§7l‡ÄĄcŚĄTNĽ@LĽI®­:j˛Ź…ß0R.ĄÂÔÍÍ B€ň"%T:(ĹŮ»ü·•¦¬+˙ń đÂ$Ĺ´ţoŚ‚3\ýđC›×Ţ X0ÂT^inËč.˙ýC›×Ţ{5Kŕ‚ueZ•’qjfV˘ĄóßíŃוÝTŰŮóČů‹M_eťůţ˝NbD“Ŕfýw_=ř‹ZŐO×+C@‘¶ÄŢÚö»÷߼pĎ´Ů­ůéj  ‡ÂŇ{Ś’ĽrÖĚŞĄg[ČLa8xačŘy-qz{Ď!uÍ{¦Ĺ…*8¦$OĂŞ­Ą6€YžHé’bĄÄ 3VëMŐŮS‡• ŔrŠú¤m´>`ŔÁă”{e$śář:ma˝[W±yk[KK˘Ż5jő¦ęS‡ –t0˘€łk»ţ5ŤúXŁ#=Ł«·^ö}óiŃÍłW|É’ŞÁ6xoS1cýW"7TU‰Íu5ĆjbŞÉž2xpąJ3Ą±Î¸ŕQN-ăÔL­ä4댼ÁhÔ <đ8NiÁ° ‹"!”>R¦˘ O~łŚŚŚŚŚŚŚŚĚ#žŽj–qÂüĎ˙ľrVjÂŤŐďçÚÚîűĎŢ;űŮć]ĂúŰ::ţv>ěç ]żqŃnĂÎ=ŰȆwt™»ôÚřŮ˙lY˙.ÎśckĚ=)XçN+W,¬şi·ńÇÂó‹‚PJ‰ŔS Ś™ąćö…¸«wť{·<>¦A[eŰs”wG.îjRł~m_Írlţ¬şšZűνżĺŃÝíNÄ™¤'µ+˙öX?iĆ‚ŕ. ׊Ž;feë{ćä}~ö Î × ˙‚sK›ĘśŰó–ţ­Ö`ŕEJ^b‰Â.KČ]Ö{ŕÔ˝žÎ†ś=!ŕáa0 ” M\ŘÁ9ßí:ëüĹO˝GN<µ}]bbĽăšUďż·¦eđîÎöćÖ¶4ëěĂ6˙č5rňą_ׄ‰-š9łŘŢÁŮĹQ±˙Čîo?ązxďľK±“fÍ8˛ĺĂÍżžeYFÚâK1D~^†m‹9’˛«ŁŤ-wčÄ!ţłEˇ§k<ěÍ BŽ˙¦ű0˘Aç趏آnĺű<kt¤űBÂŔĘ®Y`/źlţž‚^ Ó—mZóFç#‡/×{ořMx^ę†"BV¶öVćfjĆÖÎJŁRY·mcşöŰĺ+.ź;ĽwďĄŘ)sfďٸôĐ©ýźŻ?·kŰB›ľž­šaLěVľ·´Kďţ-[ą®űüë[1çw_N2Y·ů`ëŽęŢÖĹ3¬Ąµ­µą™šÚÚYiÔjKG{ŰúďETlddddddddž‚yę߀RP(̲3ăÓŞkŞ ňăâbr Ë۶÷*ËĽ{öüĄ˘ĘĘśűw#"CăŇňýz÷knÉ|µáo;NGR–shëîëۡ4>rőňU·Ró€đúň˛Ň‹Bă’2ÝŚIĎČ˝üú-xkžź§ăŤKWZŽš`ĺÔ\ˇ`8Ě`Ë–î.îm•ćć*3đ«YX†a¬\Zúw®ĐĂ2/ĄŇ ¶\ýnẔ́ěôô¬ôśÂŠňň˘ââśěĚ„{Iä¤eüE,wî%>xťţ ż˘˘¬¸¸¸ 7íRÔ˝yý´~áŘşâ¤_~;)FčĎîs)ĆT—F^Ťő lŹ‹Ö,\•R0¦ä‰RަđsˇF…MĐĐU™±GŽť¸}ýf%¶ öO»||ď‘ŰŃ1yEĺáçÎ8ë ˇµY±ű÷+ŞŞ­*ĘŽşu[G„Úš˛č›·˛3Ă"n·čŢŁŹÎ)±WŽ;[Z^ Ź‹IŃYęť;•`>lŘk’r||BVjüůđXlg–y=Éł[—»—Ź_ŹľÓĽ{÷ť’r-ę4Ą%Ďń±FFz-9ŢXRRrá|h|R&ˇ`Ł›G÷F§6™pŚđĆşôÄŘ䜼Üě¤ř¤ôÜĽĚđKaa·[ůůőëŢ)9&ňä™K9÷ă®% (JĽrđĐŃëQQĹ•L˙Ŕ‚&ëěůK‚™…XىŚ8w'ŁŇßżwaęŐCGNDEE=HN¸źť››ť”p?#3ó~LB˛ÎČČČČČČČČ4Ę«ßÍxęˇńłgË0B/S7aL iŐľSpP ®.>rnä©H‡-(Ć€11č u&Ą…Ć!$YŚu&Ĺ“–g۸č=3ÖdnˇD@M±¬Â8jĘeť^ř?tPŔ٧ř¸Ú=HŠ9ţÂ|ŮMh=1ěçMÂ3öFľ®É |ą”ÄŹ””3kďź·®ŹľâŃ{ÜôA®}ý’J„&ođżňHç%˝÷eFúRŢőŞk(########Ď‹j†E@¤j,ÇQ">^µťá8DDA¤ Ç›L€aQ1Ă"*Šő;N„PJ!–aéNö ű‚€R…ąůĽ‹*+k"Š!‘7QŔ ËP„M&Ŕ fĺ4~~›GÁÄ3´G±,EŠ€RDOŽśČ¦”Á(áM”P‚0ÇqŇÄü-¨Ŕ đîü …’E@yžŤÂďSLü+ž={jÂ1FD$€L yNT1F˘(b†J†ÁŇ?1B„„ĐgÚ4!–e¤63¨Hð”ŠŤD&1¸^™ÁXx‚ěÖ¬_< »A›µyĂ—ańéR]Χušô±§GJ)Ąa†e€ÔęŐJÁ`†E”ĐúO#ŚAÄgGІ‚Đú9F,Ë"B0ËbJQ”:†f$ "`ڤßĎcĘ˙‚E—‘‘‘‘‘‘‘ůo¤±¨FŠ.lZ ŔÔ•žż,Ő lŃÖ˝WĎş˛ÜČ?n™"í¶Ěś\Z;Xe&§ P…Ň1`đ@K¤»Qj4! aňx\ô8 ĂŠ˘0iÎ/ß}Q“vmÖä9~űN©l2łN.,&&˝ÎXSKD'"6§€IDAT ké܉&7Ş«‰ RĚ<·ŤQox̄שˇFGDaÎÂĄ&ĽZ)kk‰@ĂY87C„ :Ă_š»´TđU#k?µâŚąŮ5z‚ŰyÚÔTÔö:÷Á¦ýËĆöľyfű„ąńő!_eí§ěçhÉ#".LĎŤ”†ĹQJDQ¤†‘jąHáĘăm€çyWźÉA=Böě)(K©@ $ĹA Šäy÷Uę[<©üĐ+B@ę÷ýOé4ácMŤPĚYy{{ŞXÄőE÷îçÚô Ś1Ć(Ą!"Š„6ŇCŚŚ°ŽÓgĽqüN¦<|'u?şó×2žHa!!őÓK‘bĽ§”˙ü"ËČČČČČČČüO‚0«6ť/ÜN+ÎË.*×\Úý=đÚ´Ĺ•şş¬ÔÔňĘęČß7ą`ěĽjĂŹéĹšśÄ«-T lŢáäőÄҢÜÜ‚’äˇŢ­,ˇŃş›OR_…sţ»ى»yZŘ:Ľµo«…ťµ™­ŁĘŇܶó€Ůa÷ćďúV©bÍěju+˙ŃKn¤,>ąĎĚĘÜĚΩń6G-ŤÎ»ă•Ů–ů;ľ23Ăćvö*µŞÍŔˢSßÚöĄ‚Ąµ-«P´č7lŃŤ”ż…S(š˝čŇő뼱rjKÎRacÍżŮőÂů±fJÖĘ©ă/găR"÷±ŻP…óáGĚ?ßRYˇ)(*ŮňĄĄdo¤%zˇĐ=Ŕ ]Ć.Ő–ĺt3gë=ÝÇ—é1B ˝™i¤ăB§é‘ÖWáôv/§,7;+ż¤üî•Cj®ńéhęŰé!B! Śşsv™vůx?éOžgĄ%ĹtU3đ?“:Ź·ŚŚŚŚŚŚŚĚ3Oç@ĂDŁ&.îďÁôňé`p}ßż˙r>îÖşeoíůő€çđŐ×O|üË÷»t2¬oŇ׺um­Ŕú ěŰ2¸]ókĽOqÖcG$m áTÎÁ#‘ŠĚł/ĽBˇ¶F—.Z;X'Rç‘?mu±Őkćő˝G^ ˛—noěÝkeČŘóĆÔč¤ţÝ©(:˝ľőWgŰÚĆŰD&őps÷™{÷X>˛ŃŮď͇–[‘)=śZôšńĐ{5őŻcŮýĆô»7Ćę:j·,ül÷(çśÔY%ôéíbm‹Kď§d•č$ľÚÓ{Ě0DÚOYą`č’>Ţďé!;?ś|úŕŽĐ†ÁÂco<˘8˙Qăúúx”神ŻvrOĽpúBÜ}Şpp}cĘ„VÖčĚÉ#±I9ń…ŘCFöěęŐ  @Útě:áőeIĺ:>íÎŤôü ĄŁŰ´Éăé ÎqÔ!ŤúŘËŹ”R–ĺ«VΖZcIŚF^2> ^=ü{wmݤ¦ĽÂŞ[ť>¸7­ Jéä6mŇŁ@›ŽľF ŇÖU•–Ho>}z éĺŞřaÓ×yÎĹ}XCBŞ7fܨ¤ŰQ˘­űk=;)QR9°'˝PűjÇäddddddddţëy:Ş‘Ę•ŘZ»ä$]OΫKb345ý»ôÚ¶ë—¬{"@QqVm­UZAÎůž~çÍZąŻ×<"ŠPVR\Z^ăáÚ^CÚé*K ŠŠ@áŢkǡ}Ú«{Ă"n_xycc(fÔî^­;XµíÖ>·˛€b'¨­(Kş[U'(”¶Îv˘ E¬ą˝‡Wk/ËFÚp [';Bj+K“îVţß´đ¬Â©ŤSzlžE§@OŐGßäŔ˝äZN€áôŞđ1"`€OßĘô¨SyĚÎMÓ1ti×BÂäa3 V}ąóÓĹŁ˘oĹxř,3‡˛Maä§-› o2öíÍysÜČ!qYÚ5_ýúÉ‘ѷb<»,çŚÇ2*ěZ{ŻűňZĆĆmŃŚá}|Ł mŹź=éĆ”gVÂÜYSgŹ~7—ÁX”§a†ŃŇkČćż/MľÍŘ·[ÍÔá­mKz?(- ¬ťz;‰kĚ@Cᙇ<ŐPŔbaa˛»ß‚áýş îc<šŰ^7đŔaA§oľÝX—qúb"` T°ˇ#Ââsß˙ÇŮK5 ÖŢŹ ;ByY\t’>-‹Hň/|ĐL%s˘!ď҆U×ÄŇÄó7^űb Í;wš˛c« –ÍZ;’şlb̋ذęšPňÜ6°XžőÖSc´mŢĆŮÇ»¶2;ř[BťłS°‹•¶†g0ĽŇMš§AšJí˛?i©ż{)¶NĄxÂfU­­X8aóŰÖî µiŰŢ×Bqĺ›oÎhălmaăőí·ëúvŠ+ăW-ž´yŮص»BíÜ;*kň,ĽÇ(nýÜa\˛s#<˝ÝTΓ»·Ř°řł8 lÜňË;s'†/˙¶!‚’ŠäÔĄ_ź9sV[ Żďż_Űׯ{+§ ®\I‡Î=hω‰·WUę‚&ĚęŰlĂ’O%ť5sÇ_\ö5Ż­ŚŹNŇ˝ČÇ©´Hˇ! ‰ ŃĹY%˝¦,śöĆë˛Ë›Ě·Ćrřř÷ă-[]>šŇlv ßÔÍ‚|\6ľýil|ł}Ǣ)c]ŠÚ¶PywéÁôš’p~‹”Çzצe7Rď†~ż”R°Ŕëju"S[Ł3LJ:öýĆŰ*˙ÁV—Ýs\=e?ń"ů?”ŻOFFFFFFFćßĆÓQŤ(R8~`ű¶ ľ{BÂółÓ+ĘĘ… Fő×»Źňů/)!Ŕ)0o’öŁa0ڶ°Ż»ę­ń#3ó}[Nýáö“¦Ň[Cűtz(˙Ňű1D(o¤ćŽCľŰĺ;¬éĹ-ś[ź!źmv°¨ ]8=ú×#Cću*Ţ@ŐŤ´9»ŕÍč]G 5g[÷ř?kŃź^0ëÖÎCÚŰg^˙#;ÝńÇMýüp˝’ÔßöřçďYS]sŻ‹ťófLš<ççS*ć‰âE! ŕîëĆéK._Ť}Af/¸ůmݶÝEeČČ.ÄÔPS'¸tsçtĹR]^z…‰÷ń3Ój ďĆhe4šX;WçVnŐĺeý¦Ě¦R”g&%!€†Ú8cBÄÖý¶ý")!^§c-ś]Ý Snŕ¤”Ň -VŰ:·ń¬.×<Ň)(@†‚¨!/ô±ĆGŠÇq”R4]úĄ‰ŁŻĎ}}(5!4xpď„ígĄŽ˝č÷ĂŇrMµÖ¤Ő«Y‘łqic^­)ë7e^°JQ‘~7IŁmáŐ®8%¦ €ÜKĚ×TslýŹÎŚÁ Ć Ë©Ť1@b(×TkŐZ˝ S˘*­ŽĐČČČČČČČČ4ÂÓQM}AÄŞoOôŽçÜŢ7ňÜéĄŮđá÷˙x#Ŕ60pH\Z1ĆXJîÄ ÄÄ› ŕÚŞ©&űXř #ĺR*LÝ<Ü€qđřäo+LYWţţăAá…U9&„Ć”¬şmĐ'?87SÔUVŠ&# Ě(UfÖ¶Îö6•Őą˘Ř™“Ú¸4ŇĆŇŃ&_›CHgFi¦úżj±łt´-/϶w¶*­…ůk{ôue?ąWÝą§*C @˙‰°†€"m‰˝µíwďżyážił[óÓŐxxľÇ(É+gͬZz¶…̆c†Žť×§·÷R׼gZ\¨‚cJň4¬ÚZjŕ‰”. †PJ :cµŢTť=uHPą,§¨OÚFëŚ)!<®AąWFÂŽŻÓŘ»uµ›·¶µ´$úZŁVoŞ~0uČ`I#J8»¶ë?XÓ¨Ź5:Ň3şJpë5lŰ7źÝ<0{Ĺ—< ©,P^¤„65Ö…(xCU•ŘQWc¬&¦šě)—  4SëŚ >eáÔ0NÍÔJ®!AłÎČŚF˝ŔŹă”,kˇ`°(Bé#e* Ďä¸FFFFFFFFćžąW0ĄDeĺ{řĐ×I7Ď÷źĽJY–°}ořčąźoX>#5áĆę÷żsmm÷ýgď‹ýló®aým;ö󆏮߸h·açží?ä‰Ă::˝űÉPątZąbaŐM»Ť?ž݆RJ¤=%„Ż­Ń«±‚7 +3ŔŘ”qóÜß–°Ć’´đëľ\ŽůZ‘×ŐjJÔk¬MÔŔŻWaša̸uţ˙¦…3§„ßzmĂOî.ű¶UFśIzpR[ja5˛§Ý…-"P/ŇW¬ZCD aŽ%ä.ë=pęŢ@OgCΞ đđ0PJ€&.ě૶ďvťuţâ§Ţ#'žÚľ.11ŢqÍŞ÷ß[Ó2ř wg{sk[šuöa›ô9ůÜŻkB‹ÄÍśYlďŕěâ¨Řd÷·ź\ =ĽwߥŘIłfŮňáć_ϲ,#mńĄ"?/öĹIŮŐŃÁĆ–;tâ˙٢ĐS‡5öfDa!ÇÓ}Ń stŰÇ?ěQ·ň}žŹ5:Ň}!a`e×,°—O6OÁ/ĐéË6­yŁó‘Ă—gα5ćžż ĎË@ÝP5ÓĘÖŢĘÜLÍŘÚYiT*ë¶mlB×~»|Ĺĺs‡÷î˝;eÎě=—:µ˙óőçvműAhÓ׳U3Ś €ÝĘ÷–véÝże+×uź}+ćüîËI&ë6lÝQ@ÝŰş8b†µ´¶µ67SS[;+ŤZmého[˙˝ĘŤŚŚŚŚŚŚŚĚS0ŤZŢččŢ.ŕµAů·Î,Zş:W«7W©d&Ć'¦U×TäÇĹĹä–·mďU–y÷ěůKE••9÷ďFD†ĆĄĺűőî×Ü’Ůřńß¶ľ@D^_^VzńBh\RfŁ›1éą—_żoÍókçx#ürëqÓĚm[4÷é¨P0ő§­0Ë(YylעߪĽtôFíآK§Ć۬|żăÁ °fÜ˙Kß•ď{đ &ÖŚÁ"ĎŘ6ď»ęýÎýĽZ×]ďßĂ.7YS§4_˝¦{K şu˙ąë6Ľhśľ8é—ßNŠ€úłű\Š1"ŐĄ‘Wc}Űă˘5 —DĄ"Śéă×H0…ź 5*l‚†¬ĘŚ=rěÄíë7+±Up°Úĺă{Ź„ÜŽŽÉ+*?wÎŔY ¨ÍŠÝż˙XQUmUQvÔ­Ű:"ÔÖ”E߼•ť‘q»E÷ý{tN‰˝räŘŮŇňjxx\LŠÎRďÜ©óaĂ^“”ăă˛RăχÇb;łĚëIžÝşÜ˝|üzô­đČćÝ»7č””k©P§)-yŽŹ52ŇkÉů’“—””\8ź”I(âčćáëۡ4>rőňUŃi…M&ŁĽ±.=169'/7;)>)=7/3üRXXÄíV~~ýşwJމ5¨—A“łéłőgogŠs9ź¶óüC2–ĺyŽÖw& âu1,¤ţSa0˘ť5bŰŽ-§_ß-ęěř*üú…K€űq“FĘěČč+@(ÔŐŞo%fP[—ÚŚëiú™GN8)ţ6irÖýŇN–wŇRuŻś8ă¤,řmŇäô;ůŢ`".˝çíÚiiČÚőʬ؋)˝ťxâÜkŢî]–†¬]s^‰»ß1Ö9/}ţÚ¸Ţ݇°,«ÄÂÚ7glŤHq±µÔÖÖ2Ş ď¤e«u^îí=f"đG…­~cäňţ~jżW#v|<óÄľíQI ůÇv<˘dCĆO ň÷®(H?věhY 9!0 ĂkŁ"Ž$gb „˛˘NeaÖŢťűy˝ľ¸´Âؤa];$ź9q&ń>•;xľ6ÍÍťµ áŘůÔRwč»}˙źš+»ĎFß4¶X4Ž1Ă`ŕŔZڰ浥ĄfŠlf®˛6ĂJk™ąŞ¶¬ÔL^‚•f–6ć¤CmUYĘÝŞęD^&·u˛# î‘ÄĆŮŽZ‘–\h˛3'˙@3‚¨ÄV«ŁťöČI.˛čä4ĚGąö»‡ř^Zť\Ž <íÝ{`çî`-żz!ŞşËYô˛/đ1"`PUfĚńfdžلşw €¨$‚0iPBáíő;>_6>öVĽ·˙ (ßp–üĽySQňMĆľăŇ…s¦Ś MĚŃ„˙ű·O—Ś‹˝ďÓ}ĄĚXx8«ŇÎÝďőŐ16^KçŽéß#Ćŕq$ňS‘]‹ćĎzmęÄówóŚ…úÚ-†ÁŇ7tÓOËRăűŽËż:vŘŔÖ7"ęŚumAóZ€·Ó˘ ý3«¬ŽDFzbµhgÁ´©çîä¨:öo.Ć(@źq:¨ß±áóW˙Ůw1ˇŹŞSB@[덧~(QBaÂâŹ×M÷}X*¨—>Öú™ëO<ĺŤËł«ŕősfʧó<ĄŇäiäÖľm,Ĺ4©× ńá«&›éŠbś¨0é:ő ÝöKxHTD5éđËŽťź­śmęĽ`Ëň~ĘÔµS?2}ÍĎ€h\#!!!!!!!!ŃŔÓY  Ąv|cĚ€ľĂDď¶¶× pś`ë|Ő’i›Ţš¶ć÷(ŹN¶¸JŞćĚ™ŰŢŮZeăűý÷uM,çŢ^6cÓŠÉk~ʞëĐEQ[ ň›('Úu‹FďMrÉÍŹöńóR:ĎŐ«ÝgËľHTĂ7›·ľ»húů•ß7ć â}ćőyóć{¸Ř¨l|7n\ŘËÍi𧬴s·Ţ´ĎôäsŰŞ«´!ÓćŹěŃćłĺź‹vÂM=·â[NSu;6EŰ|ŚQ…śĹ?ËŐŕCô\o4űű‘á#żą­6Âęҡ´6Ż ś5·MżË7o}žPßmŰľ4l˛K±G;y±_÷ŢLß°¤Ó›Ĺ>ÖżoXq#ýnÔĆ7)óś¶N+0uµZŁÁ¤ŁĂż‰Saui˙=ÇwÂÉŕgN ­ę¨&!!!!!!!ńڧÝŔ‘˝Ű¶„íŠ8˙07ł˛ĽËâ_ÇĚfnĚůëD4&Ł@ÍżýýŔx_~řĺĄd2ŕ¸zť˝'˘!d,ľ1˛×óĎ{Ł SYEďÎÎ(źş'Ňž):´úť»—3|Ěnf¦•Nű3ŇŽ)<´úťÄS‰-ăúĹ&•>ňŤ9±ż4Ň‚uď-J˘–ÎŤýý`űO§– účßťý\ :#†7• ™ňź)ŃÖ™lT°8çfnn¦ĂOěŰtC-ˇH%§ëß™ý˱$ĄBn2q,žŔ^iALú¶ľ—9Ěť1sá/Ç•ĚPbݶ=ĽdşŇKW˘@W]Íń^ţ!żnŮć˘4dĺaj¨Őó.=;Č´%˘Ž¶ łŇÄůšiÔEw‹A°2M¬ť§ł›WMEů€°×G+ĺŮ)©EĹ q6ĆÁ˝Kŕ–­˘ĺbÄiµ¬ĘŮÓ«(íVNI+«Ô`s[çö>5ęGv €ˇ0&´ĺCőąj(–ˇ€˲@xb­eo4űűai…şFcŇč” ¬ łqioQŁ.¶x”R^™y7E­iç۱$-ľ€ÜK~¨®‘±ő?:333¬ ŔHĹlŚR_ Ä@…şFc®Ń)1%J Lý˛%$$$$$$$$žá™Ňâ@ÄęoMţ®™ĚąSŹč¨eů2Ç9aoý5łZŔ #š~~%ŘvذĐÄŚ†a8Žlר8™}‡u…›r.őÓ>ľĹ©€02Yí,s_źmdj—6•YŵöyÎľ>Yłę6mËďçů–aąŇĚÚVĺloéhóP“GH7Fa¦´¶U9Ű«lÔš<°Á„7 Gx± B ó•Ľ €S‹R }ý˝ŢAžěg÷jşĽÚ>ë0đśsćži“WŰ5jh8‚ űĄ¬™•«Źd§128~ääĹ®8ł“O¨ľmźŚÄ(ąŚ)-PłćÖ˘`€#b»d€B)1hŤ5:SMî¬Đ X™Ľľi[Ăa0Ś)!0jJŁĺľYI'eś^SThď` ´u·µ´$ş:ŁFgŞy0+t„h#Jdv­‰1Ž'&ÎÄ€ĄýĆěŘřUńÍ}‹ÂżćźçŤ¦˘@𜡺Zh‹ ­5ÖSmnŘ<(ĚF˝ńŤŹĆ«ś\ăÔĆ\!klЬ5rŁQÇsŔaÉ*€UÉ,„PúČ2řĆgR^#!!!!!!!ń ř©‹Íš”V=ŽGžů$üÍ=–•$ޏ˝'ľ:Ş»ó_ö„iŻůŐęů5UÚw>ü!ćňąé#ű@ź©Ż5ę0@ŢĆőŞ%oĚžĚć›Ů` ”žÁh´őď]“qřťeGV/»źÝ®Ok˙š¤S‡ŢY~dő˛”řL÷AťoĘąuę_Ë˙ś=ĺćÉŻÁc05łnť%'bĽŹĹś–3ýQ㣺 ç˙±u–ŃŔ{vsíée}2eŢÂse*«q}mµzäěělˇ”5¸Żľš†LmU÷ág!€łg'ĺ«ű žµűĎżś y»"Î@Ăa0 ” uâŮ}7ň~ř=ň•oď?vqáôŕääŰŽîľţÇž-śí-¬miÎąťŐ".. XŁÚµqf°˝ł‹ŁüäÁ?tvC˘ě^±|ŐĹ›K_ lC<1ËzXeŰÎ˙Ă÷Ă˙Řó«§ŁŤ­ěôŃýśm˙¨ăöüńą˝‘›Á©#;µŹŰ™;ĚÝz4c$űdĂŽoŢYfëŘ~÷™3+gŤ;÷a}ýôę Ŕą3GZđĆ3Ô‡•­˝•…™ąĘÖÎĘL©´öhoučw­ÝŕSvŻXľ*úzě3í?ľÇĽÓß·ü¸mó×>nm0&v«ß_űÁŇ0W7ĎľüöÍącň郎Űôëö-›˙íáâÖŇÚÖşŢ˛ąąąĄŁ˝mý÷JÍĐ$$$$$$$$žˇéc6p(‡ăř˙–)%aŚ@3ŇLV1F‚ `†J†Áâ?1B„„Đgt !–eDŚT „aXJ…&2„\o™ÁXŕ9‚ěÂ×}0jP/&gÓgëĎŢÎçr>mçą1†°\ĆňG(eY–"‚fXHçZ獧Ŕ ‹(ˇőą Âx^xöJ1ĂPBZo±,K@Á,‹)ĺ3Šf$đ`Ś(}Ęňß˙+h:«ÁĹľ}öôjsďÖŤ»9ů ·i:lŁ/;}ţ’‘#€ö]zwéPWUxéĘMŁI°uqíŕŢŽň Ś-ÉÍ)PW!„1ĆDhfš!ð‚ŔĎX¸ő‡Żk3ŻÍ›ţZßýçě-JîßçcŐÖMiˇ@ĹSgfrţ»$޶í­-čDí–2C~nťNŔ>>Ö5U†ŃÓ/čôlPż>¶J—t»¬Z‡(Rő ´5ű˙G˘ęŐw€­˝ťt«˘J2…íŕA¶f46îfŢĂrŮşm‡–Ž Ľą}Ú˘µ\ý\™—ą÷H¦0d%kŚŽľd05[ĄĂ0,ĆR"ĄŔ0ŚXĽ"¦+ŹëÇq6žţ3CzGěÚUĚS–R^JÄ<fęUę5ž´\żZ„R˙Ü˙”Ä0L 1&&ĚJ'—ö–Ů©™Mn5¶ŢO,‹á ”R„B›X!Ć FXÇWçNJŚ>r'[ Ţ=¦‡ô:´ă·rŽi!!őî%„9ŢS–_ü&KHHHHHHHü_E,¶^ńĹĎŐuşÜü‡Ĺi·şĘ@ćęw.>٤ ·¸B}ţ÷Ťf>#ç¨kdgWT×\=üŁ `ʲďÔ5U˛ł2Ň3ęęŞ?s&0ěs:ä2, c_Ż07ypĎŽf–Vož=äoćÔ®mgOs×^ŻEß{ďćŤn}ş9»ţ}3·>‹ÎĹŢş5ďťW=ä¶ćööбŻő95ŢşS÷CWRj«Ę3T•dŚň‘Sç©5•˙˙H:ťĽqŻ®˛$óA®¦$u´ öŹ]˝[]^ś–ů ˘ŕިNćŕЮ떓‰i˙dĹ;öÂäâG,ľÜQU©.,.ŚŘĽŢňŃÍJµhč‰`†€î“ßÔ”çő´`‹őLOZiÍŠqg¦‰…ż€±M˛óęO7e–¨ó’Żt1—ׯęeĽŃŠo{f…!± cŢ-·\łrj ř'źó3RâĚ™gÖÓÜuHHHHHHHHH4ÍÓťť1fÚ±ö­9ë_Y˙WL÷Ţ}R96cůo¦O·Î§q‰×wĎ>¸ă÷—Ďuú\L‡ŕe)g7εcëŻg\ß]SVÔ{ÂĽ_…_żp 0c?nŇR™Ýb…şZő­Ä pt-ą°35W>gď9;KmAě= •yáŮźÓrs÷ťłSŐý]’Ľ¸4•J3жí-ř|Ç8çĽôźÄuňč7'lqpYźî>©ůşYs¦Ą&ľ¶`XYď®Ţiú˙~ɢˇ]”˝»v%÷ ř‰Ż-ń7ďÝŐ7­@?=lňĂZuá˝ôőŕnÂË˝˝Ç Cľă¨°ŐoŚ\ŢßOí÷jÄŽŹgžŘ·=*‰a0˙ŘŽB”‚lČř)AţŢéÇŽ-«!#'tbxmTÄ‘äĚBŚPVÔ©,ĚÚ»s?Ż×—Vř›4¬k‡ä3'Î$Ţ rĎW¦ąYŁ“Ç&¤­÷ĆźB@(řöŇ/ “‚š ŠŞ;wq;±owFaµÂÉköŚG+€ö]zL?\]¤Ż.+5rü{‡öő”˙¸áŰž€ĚĄĂč~].EDTŮÄ)ăSâbŰCűt•Łh9bď®Ě"MëĘ“$$$$$$$$ţĎńlV„@pżÁµEŠÎcŽ^}ŕ·oď8ڵͻw=µ@/”&d«ëúűîúíÂ5_—nť{¶WTTWŃgÜ»c`Ůđ© ÇΧ–€ĽCßíű˙Ô\Ů}6ú¦±Ĺ˘qŚ @"śş¦¤ĐĘŇŐkÄpBp™ľ¨ş¶¤ĐĘ»Ýß& nF*2Ź>´đućŁ\űÝCBĐÝT}`O™ŻŹoţĂ[öď8nn HćŰŃ· 0öVrpß~HśÂ‰ĺěKwůĹ€AţAU™1Ç fbčŢ1˘’¤A …·×ďř|ŮřŘ[ńŢţ+, |ĂYňóćMEÉ7űŽKΙ2.41Gţďß>]2.öVĽO÷•2cáá¬J;wżÖTÇŘx-ť;¦ŹÇ‘Čc^LEv,š?뵩ĎßÍg0ÄĂiˇD°ô ÝôÓƲÔ8Ćľă˛ĹŻŽ60‡őŤ:c][PĹĽŕí´hB˙Ě*«#‘‘žX-ÚY0męą;9ŞŽý›‹1JĐgśęwlřüŐö]LčŁę„ĐÖzăIF”P°řăuÓ}V ęŔĆĄŹµ~ćú“OFyăňě*x}ÁśŮc†ĹéĚš°ő ž3ĽëˇÝ‡yŔW©cS’3rhĽĹÍ€«Ó¸„ĽćícyhÎČ_GŽ<˝ń7˝V×6dľ·Źĺˇ9ˇŁäÔŰx$ăMÄÓU±og’ŢŮiôȶčÉc˙}§z߉ Ĺż}·ÖŕČáź.ţ3$l\çÄPń©öďxgŹÔUšꪻ{!!K)"ĹE!”îĂW-™¶é­W‚GŚčÖwȱkYP’0gÎÜź˙ŘóŰŽ˝J—ÎÁA]Á˛ĎŰËf:ţý_¸ŁRYȉvݢŃ={L®6këăç5l̬Q˝ÚíŰşyËć͵V>ď.šŐ‡ź8Fźy}ŢĽů˘eó6ť‚{ 9ĎSV: [ď±ó×TęIu•6dÚü‘=Ú4Ú _48MŐícŚ <(ä,FđřY.JécÚ-yŁ9X>˛ń›-‡ăîžŰĽöó_ŰΚ»8Äße˙¶_·lţµÎĆgiŘäńŁ'µ“uď=nţÇuT!ö±ţ}ĂŠ‰K?ĺĄ`žÓÖi¦®Vk4ärt¸Áň‡źüâ;h € äĹOĹIHHHHHHHüď§éG7ąÂÜPšúFŘĽ“̧OÖ”ˇŁćýűÇ_§ŤŘqţanfey9°,¤_9Ř/ ŞďWţřĺ_ŻMřˡk0fĆ4scÎŢŃ N–,ľ1˛×ĂĎyG¨Ŕ±–®ă6í뛕YtýÔĄ_żPyv4ˇďŘŤőĎĎ-şö÷HŠŻť:˙óçn=üF;XeîŚ{ářÓ†{Ľnb•U9IÓ‡÷2ĽÇ ŃźĽ˙~Ń˝¤5ŰŽNÔ·˙ ˙Ç%kŠSî®ŮvTôÖxď‰IßÖwđ2点3f.ü帒y˘ !D:ôđ’éJ/]‰]av5Ç{ů‡üşe›‹Ň•[„©ˇVĎ»ôě Ó–:Ú‚ĚJçh¦QÝM,ÁĘh4±vžÎn^5ĺÂ^­”Wd§¤#hlŞcB÷.[¶Š–‹§Ő˛*gOŻ˘´[E8%­¬RÍmťŰűÔT¨Ů),D†ÂĐ–cLś[Jj(–ˇ€˲@É·ěŤf?,­P×hLť’d6.í-jÔĺÂŹRĘ+3令5í|;–¤Ĺ—{ÉŐ52¶ţGgĆ`c†•©Ť1@ęK¨P×hĚ5:%¦D ”©_¶„„„„„„„„Ä3<ťŐSŢ+ô•Ľ žĽˇ&^F Y9#$śENťzDGEä”ćâůZťú|ÄˇŇ uňîp äNsÂ&Ţ<úkfµ€8™}‡u…›r.őÓ>ľĹ©€`†áęŞY•{»Ŕ!ľĂ‡ŢN¨Ę+LuU¶ â;ěď‘t68/!±<ŁĐĄ›uy-ĽţŻŢAžěg)µť{˛E¶ŽeWÎźľrţBđ„1í]äŔX°řĘ…ÓW.üOKĆŹöri˙aJ#ŢĺbM©˝µíÎ9sĎ´É«í‰5 †$î•T°fV®>ťĆČXŕř‘“»âĚN>ˇú¶}2Łä2¦´@Íš[‹:€YŽí’bĄÄ 5ÖčL5ąłBC*x`eňú¦m ‡Á0¦„Ŕ¨)Ť–űf%ť”qzMQˇ˝W€ €ĐÖÝÖŇ’čꌝ©ćÁ¬Đ˘Ś(Ůy´&Ć8ž8G"”zôłcăWĹ7÷- ˙šž7šŠVA Ŕs†ęjˇ- ‚¶ÖXCLµąa#FTđ 0SőĆ7>ŻrrEŚSs…¬±AłÖČŚFχA&S¨XV%g° Bé#ËTŕžIyŤ„„„„„„„ÄsAëÚçNAÍÍ;ľűe7ĺk^ é¸säŮčŻ×…_˝_ôđŢ9€Ů+ţ+ó~üçk?9yĺľPűpę@č3ýJą9C<€•ÉŔ˘ëd-Ą…7ö+šlő$ö@łčÝ´;r;—Ůß}čŘ­Gŕ‚ĺˇĂ+ĆďąúúoßÚův \ř7J‚µżýxěŐY=<•ŢýÝGZ·íŘć×_ ˙0?/ëßëŢ۲;’RĂkC;›ýfiqîż×˝ű?.řÚ•S‰~[˝átJô ŔKô@[rá¶~‰yĺ×öďŢ}řfuá=7Ý™łŞú)*QSśýÁĘ·#ÎŢX8=xŕ”kËľz?|×ĹŰ”Ň÷ß ›ĎŢŃg°rőńł7—„ t2ßPSŇ] ëZ¦§k‡Čťű>¨ŕnŮ˝bůŞ«q‰+ڶˇ?žŘ(oÂÂ/µŐ%Ť–WÍ 6k7¨¨Z¸qüŔ‰ë‰śV=k¨Ź…ŰŔµ©ŃÎ[‹&€uŔ´ćc ČĚ>ٰ#!9ł®¶:öňą•łĆ@ĎIK)Ą®îQ"¦m×–ĽńŚ Y đͱÄßV/\ľóü-wKNÜ!źţiĄĆŘ#»W,_u=ńÎ3ŮtĄćčź[~üătĄôőqţv«ß_űÇŃłÚšŠÝ›6˝9wŚĘ=(§ĘpňŻí[ö\¤”Λ1ćÝß.í~÷uŃňŇw~y|ZÖĚ/HBBBBBBBBâ™c6”bŚMáĹ+ ]CFxٲźľ;2qđô:üá­“Kß Ď×čxΠ´¶ę`*Kúř˝·Ž]NĄmíłî^Ůs(ÚHĹ“@”ňúŠň˛sg˘S˛›|ËŚ1¦„řxcÁâŔŽŽ1g/řĚ_. •ůy„µ ZýQ§^>HfNµŐŐ „‘`V ş*MA>aUVŘą§7A R[U™ź'`‹ľo}ŘĄ‡‡»ńć Ţ¶ů÷ŐzĄĹŰďôtłb~Ýš`¤f=úô·bŞ×Żűđ÷Č˝®Ž0ţ˝ű[˙ĎK>úíđ˛7>ůeí’Éú’”­;Ź €zŃřcDjĘ.^Ič2‡/Y“V„0¦ŹO‰AŔtţT”Qn2rpuvÂÁĂGă®ß¬ÂVŁF ɸtd÷Á¸Řř‚âŠó§NdÖ!#kröě9\\]W]śs+NKřşÚňŘ›·rł’ĎFǵëŐ{`ďni —Ž,«¨†ăb”¤ßąSŁG-ßľť”“~űôůlg–}=ŧg÷»—Ž\Ź˝uţb|Ű^˝í”Vh(ŻW—•¶c€‡ŹŹ:'9ňô…MufbÂýĽB7•–”ś9•’ “šŇçxăY'pF}frBj^A~nĘí”Ěü‚ěóΞŤŽs Đ«kjüĹc'/äÝOĽ–\<*4¸8ůňľý‡®ÇÄ”T1‡ ćŐ9‘§/đf*ˇV}1úÔť¬Ş!CúĄ_ŮđhLLLaQńÔ¤űąůůą)I÷ł˛łďÇ'ĄJű4MŇĚ‹_„ž,OxęTOS­ĚűOµrSBÜ:u2 ×”DFś{ô‚Ł››Ŕ3Ĩ7ju•+¬,%áÖJtz޵VZY‰S ‰AgŇëměTć6Ö F`Đé@[9A÷•ąĚdeeހ赦Rµ!ṯ:-×x5ĐSĺç˙ł @ťÖÝÓîAJüÉó-öË~Žëź¸ËOßófĺM|]sź}Lˇ5-‰YFSJ̬ý~ůu]ěąď~S^îRĘcŚČË ý{ĽŃ 3O_i«L˝ě=”€ć˛„pc)7Ă2D(`9Ë8šÚ2 Ű8Í3 %ĄH&cžb:B,Ă%Ľ mçą1†°\ĆňG(eY–"‚fXHçZ獧Ŕ ‹(ˇőą Âx^xöJ1ĂPBZo±,K@Á,‹)ĺ3Šf$đ`Ś(}Ęňßsß%$$$$$$$ţ×ŃTV#f6íB‡ bőe§Î_2rD|¶2srqw°ĚNÍä)¨×aôe§ÓQ:ą´Ż×©7Ç0Lcţó, Ă ĤFމľ4=ťĘ¬Z!±tîäËšŞĆëľ´’ňrëtîčc]Se=íĽN°> 6¨/ÜĽ-îr ĄcP˙¬ˇěĘŤë]WõÓäÂe“I  [·íĐŇq·"·O[´–«ź+ó2÷Ɇ łdŤŃŃ— ¦f·×†ĹQJA †‹nÄtĺqŕ8ÎĆÓfHď]»ŠyĘRĘ‹Ci ŇĚV^˝Ć“–˘!¤ţą˙);ω11¤ź‰Ě—ôĆ Ćc î""„6±BŚŚ°ŽŻÎť”}äN¶Ľ{LéuhÇoĺÓBBęÝKsĽ§,żřM–ř?‰Ř-ŔŞ}·3q%ąĹęs;ľ·ÖĺíĎ~Ę,Qç%_ńS)ŔĆŁ%ť.ćrh]iłŘ-`ěëďć&îŮŃÜŇzŮéSŢţťmÜ=mŰ9z†˝ývRÎę+—ĽşřŘş{µFb×ÎÁ#ěÝđ„äësŢ™ë!·5··WŚ}­Ď©ČqmýzťŽĎ*/~XŞ©ľ°k“5€•W÷čŰ™ĺEyeŐwn´°hď6ľáş~űÁ Ŕ˘}·äěöďl°]›®[N&¦]ü“x‰n ±řrsDUĄş°¸0bózKQŢ„&jŃĐ+Ŕ Ý'ż©)ĎëiÁÖÇzzŤ­Y1B îĚ4±đ°#¶Iv^ýé¦ć#łőŢhĹ·=łB„Řž1ď–[®Y95Pü“Ďů)ńć ´ć—"5 hž§;;c ńÓ— ôfúúw68Ť»}}Ď´[¶§z„ŤJąz­g@{9¦0~úňćuÜY{P,s?1”TfGF·XBˇ®V}+1ś\KĎď.©`Ăöś–•^ÝýÚ˛ü°9ć)ŠŐě+OĘJž/‘—\Ýą`eÁäŐT˝uOÁ翍sÎK_đI\GŹ éł—÷kOőě¨w{çĆźŁ¶ý\Űyî€l/?_Ó¸;7ţ Ýľą¶óĽ!e˝ü:‹’Ńżo©í<˙"IĽşkƉý[ŽÇUßKĎQî&ĽÜŰ{Ě0Dŕ;Ž [ýĆČĺýýÔ~ŻFěřxć‰}ŰŁ’óŹíx D)Č†Śźäď]Q~ěŘѲ2rB`@'†×FEIÎ,ÄeEťĘ¬˝;÷óz}qi…˙°IĂşvH>sâLâ}*wđ|%lš›5:yě`BĘ&*m;8t\źßFˤ}—€i“Ć–gĄThąŚ;72V*˝fĎśúČj>ĆĆ”ŞÎý§Źě˙xdľś7žřBÁ·÷~ťÔTPTÝą‹Ű‰}»3 «N^łgÝWĘŚ…‡ł*íÜý>X˙Qcăµtîţ=b G"Źy1ŮU°hţ¬×¦N<7źÁX¬BˇD°ô ÝôÓƲÔ8Ćľă˛ĹŻŽ60‡őŤ:c][PĹĽŕí´hB˙Ě*«#‘‘žX-ÚY0męą;9ŞŽý›‹1JĐgśęwě±Člv„€¶ÖOýP0˘„„ĹŻ›îű° TP6.}¬ő3ן U‰±)şŚq¦gËŤž?ďüź"$ŢUź~[猑ugďĽß·Ó“Ďm«®Ň†L›?˛G›Ď–.Ú _4őÜŠo9MŐíŘmó1FţÉČl×ű=×Íţ~dřČĆon«†Ť°şt(­ÍkĂgÍmâďňÍ[ź'”Ăw۶/ ›ěRěŃN^ě×˝7Ó7,éôf±ŹőďVÜHżµńMJŔ<§­Ó L]­Öh0)äčđĆoâ”CFX]ÚĎńť°A2ř™ČK7g“ř_ĚÓŹn‚@ŕČŢm[B‚vEś›YY^Žd ńŻTśËNDť­[†÷oNG,IŔKnŚěßµÁüsžÇ“¶ÖyPXꄬý3]üşXş¶1U—»Ś›Ő7čĘ™ÁÎ]:·Bâk޶1ęYsyöŤĽĽlÇź6 Ř·)ĆÜÉáČŢ­î>ţ»N\(xˇ./·q´=˝qëÖŮăĹ«řçK*ËË‘\ô3úĎ+-IßÖwđ2点3f.ü帒ybxBtčá%Ó•^ş şÂějŽ÷ňůuË6Ą!+·SC­žwéŮA¦-u´™•&Î?ĐLŁ.ş›X ‚•Ńhbí<ťÝĽj*Ę„˝>Z)ŻČNI-*FĐŘf0Ć„î]·l-#N«eUΞ^Ei·ŠpJZYĄ›Ű:·÷©©P?˛SX …1ˇ-Çz:2( IJ,"žXkŮÍţ~XZˇ®Ń4:%+Čl\Ú[Ԩ˄-Ą”WfŢMQkÚův,I‹/ ÷’ŞkdlýŹÎŚÁ Ć +0R1c€Ô—1Pˇ®ŃktJL‰(Sżl ‰gŔO ÄŐŢš>ÜĹÝuěâ·9Ę<(Ë˙ČńÄÄ™L‚¨:·Ž ”Ęě;|±aóÚab]{ K©mNÂýńĎđřÔW~ř·5kthďĚQůĐM{ÂăZ/1ŘşµqpP”×Ář…˝<ŮôdŤK{+Ş~đvcżm"čş zŐôaâUüó%Źůů?z_O@±¦ÔŢÚv÷wkÎÜ3u÷j[Zن†#аŹQZPÁšYąúx#c`ääĹ®8ÓϧsčĽ55–ËŇ5kn-ęf€í’bĄÄ 5ÖčL5ąłB‡ö 2čÝďPh8 †1€QSłLd2NŻ)*´÷ęl`ŃÖÝÖŇ’čꌝ©ćAŁť÷~8Hdv­‰±Ç" }Çśą˙Ç÷ď±@žëŤf˘ĎŞ«@DĐÖkt¦ÚܰCűő<řóźh+ *'WŔ8µ1WČ4kŤśÁhÔńpd2…ŠP¨ä B(}d™ |ăÁ3)Ż‘x.bł&ĄUŹSçż^~ő~Q^Ň ¬ül㎄äĚşÚęŘ+–Mŕsâ\tł:—Ď­9,»OÓRZxcż ÉVOb´1‹ŢM»sA €Í­Wś9Ýú„Ţ şz8ÚűŽ\˙ŕť«—űNŰ{ak%vťGŻŽżýÜ”ž^fŢýÝGZ»tpţóŔH§¶GNž]˙É»WďĺÝ9ç®lpâtĂ•ţó%IgZţî–ś¸C>ýÓJŤ±GvŻXľęzâť7f˛é6JÍŃ?·üřÇé8JéëăüěVżżöŹŁgµ5»7mzsî•{PN•áä_Ű·ěąH)ť7cĚ»ż]Úýîë˘ĺĄďüň0ů´¬™_„„„„„„„„DÓÇlxÎčءcđĐáoť\ş">ŹEfÂýĽB7•–”ś9•’ “šŇçxăY'pF}frBj^A~nĘí”Ěü‚ěóΞŤŽs Đ«kjüĹc'/äÝOĽ–\<*4¸8ůňľý‡®ÇÄ”T1‡ ćŐ9‘§/đf*ˇV}1úÔť¬Ş!CúĄ_ŮđhLLLaQńÔ¤űąůůą)I÷ł˛łďÇ'ĄJű4MŇŠż!h˘4ýńŞe„0B”ĽĚ(H±÷.qëÔuTČ0\SqjĚ᳎îí Tڵµâx…• Ă *đ­púZK´u_™łF •(1řňJÓř°KZß|˝őł-ÚţqŚ(ˇ8xBż§Ý”ř“ç[ě—ýĽ;ű¸#šsË3ň&ľîą%ě­kIüČ2BRbfí÷ËŻëbĎEx÷›ňępĎŕ Ŕ”Rcôr‘öĽ/ÇRúh/vČŻń'ĐÔ]l…©—˝‡đBç Ë2ń&Ž Ě šß„B,Ă%Ľ <ç Jĺ‹#ÎËU‚‰Ă¬3HlERŠYë$ŁwSŽ3,Ă"^ŻKÜű§Ŕ™drcÄ›ť?x8‹ăč“)b0¦@ţÁ’żëć#Ś`SBšÉj0ĆHĚ0@ !Ŕ0Xü'FúŚÎŁĹł,#ę`Ě  ! ĂR*4q91¸Ţ2±ŔsŮ…Żű`Ô ^MΦĎÖź˝ť)Îĺ|ÚÎsc aąŚĺ9ŽPʲ,%DL–ŇŠČlĚ0(!TĚd0B„6qĄa(b Ľ@(°,C)'tRBxAŔ ¦„"„EŔb–%fIch,Č‘x Ô”„b™•źź·’E€XŁ®řŢýüĆwĐ®ľÝlőŐÉy6.n>íŰž@‘’Üę*ń©ÎŐ·›­®*9˙! † B3Ó †>xĆ­?|]›qmţĚ}öDÚŘÚ Ľ¶šXĄĘ(Ą”b–ĺµU­–¨L ¤0ËňÚJŁ ·V1ݞ˙¶’óóętęčm­©ŇŹ<¦Őr€k§®ŤkĆ@ E®ľ˙<‰ľ:%Ż€€|ݶKÇŢŠÜ>mŃZ®~®ĚËÜ{$S 2Ě’5FG_2Hsš ĂbŚ(%‚ P ĂE7bşň¸pgăé?3¤wÄ®]ĹmR˙~. ŞŐ˛˙úâ±5S@ţÄUü“$cZYt/=G=¸›đroř1Ăď8*lő#—÷÷Sű˝±ăă™'ömŹJbĚ?¶ăĄ 2~JżwEAú±cGËj`ČČ ť^q$9łc ”u* łöîÜĎëőĹĄţĂ& ëÚ!ů̉3‰÷¨ÜÁó•°inÖčä± )š¨´AěŕĐq}|-ö]¦M[ž•Rˇĺ2îÜČ|X©pôš=sę#;Hć8~bÓ1†0¦DPuî?}d˙”«×z¸łÍDŕÓ1ωÂćť(î˙¸x‡† ›Ú-ńę…żťĘĘ-€A#&öîŢ鱫 Îîť§NgĄ ©I7Źź˝îÓąwčôť};Ź™<§]™.îŇéô‚ŠÖ•IHHHHHHHH4nRŠ0eĚíV®ůb}ř˛öŽ–BaÂźŹđ’ý±÷ŠĘŇpšĘŚ”äüâň>ç$‹N-€ K>«×±˛Ŕ@éÝoűţ?7|Ľ¬ţÝwóoĄ1f0* »Ľ“ŰłŠägLĺ««)% űĽ“ŰłŠ/+™ÂUVÉĚd9w‹,:9 óQî?řîgjuZÓČź7®™e(™°ä‹¬¤ŃX΢—ĉ€AţAU™1Ç ‹fbčŢ1ÂŹ©Ąđö×;˘öü:tčÇßţ×+“‚KĐĎ›7…ő ťĽčÄń#=˝¬ đo~«×ů÷¦ąa++*íÜý>X˙ŃěyK÷;ÔNöŹDFľ;BŕĐńÇŽ ńw§”27Ä–ľˇ›~Ú(Z>yâh×¶rsw˙¨3+fM^ýÉw'Źîܵ XxÔ1ďd)vĆ—_®€N=FĽ6‚çL¦Ě_µô5_wshͶ¦„„„„„„„„D34ťŐ`Áś›’SÚ7lÉ©č¨nmÍX˙Żßźµö­·âčĚÎ(ŘzĎŢőđžŁ&kĎŔ/ĂĂéˇJť›’ś‘CÄůé-@@¬R¨L»öŰÁ~lgŤ(’+­í„ŠÔkżřÁG/+Á+”f&Łŕé¦Ř·3Iďě4z¤«ş´Ú5`Ŕ—ď†}üÖ[ńtć2AËą{ď/ßťąö)1c8@<ŃôwĽ×Gę*ÍŠŹ?uŐÝ˝Ą”?±q‡0C((݇ŻZ2mÓ[ŻŹŃ­ďcײ $aÎśą?˙±ç·{•.ťş‚eź·—ÍuüűľpGĄ˛íşEŁ{ö\mÖÖÇĎkŘYŁzµŰ·uó–Í›k­|Ţ]4Ë ÄfúĚëóćÍ-›·éŘkČČyž˛ŇÝzŹťż¦ROŞ«´!ÓćŹěѦŃNř˘©ŔiŞn·cTŕ@!gń“çÁčcN|<ćŁÎť đ°§”6?ŻFĚ—PiLÄč}Żć×ĆîűĄGďţ{ĎŢeKnż2çŐ˙ř† kŻľÝ=ŹîřbÄ~CF„± {ňŻŻűO7Rá«×Gô 9yă!˙ŤÝ$$$$$$$$ţođô 4ńÝv]ć…é. <'űö@vrT˙ü|úÚ›”n~Á=Ű›99Ě´?*†3cšą1ç@ä÷ć'm Ť:łĆí9c(ľ9˛×Fó-?¸Qą•yÚÎ_JM–´<3ónzU•¬03ŁâŔŽRŁ”g˝ś$ënzu•2'î¶Ż•eÖŤ¤Ž?mxđż®§k™×V~ÖŃĆ`ććÜł˝ĘY7zp/źńůŘÍÜ˙‰Ń«ű#cŔ őŻkbŇ·őĽĚą` 9®dž^„˘zxÉtĄ—®D€®0»šă˝üC~ݲÍEiČĘ-ÂÔP«ç]zviKDmAfĄ‰ó4Ó¨‹î&`e4šX;Og7ŻšŠňaŻŹVĘ+˛SR‹Š@ă4Ś1!‚{—Ŕ-[EËĹÓjY•ł§WQÚ­"ś’VV©Áć¶Îí}j*ÔŹě"CaLhË1†ÄŘ&¨ˇV†B,ËÄhÚG1ż?=)jÔ~IŰ"Ĺ…=ד \?0 ÔÝżń*Чĺĺ*(»¸eÇŮk÷˝úŻšÓ;Y±ć›:`1E‰i’ú:KHHHHHHHüG4łWz%sc¦¬®|p/S˝xŮ’€Î–¬µçÔq#@ŔŻÎšxóčÁT5ŔVV<¸—őÎř cďýĺĆÍkW„‰uí-,ĄţąĐdPşů»y´I?şż¤´˘®8ż >ŢĚ«·›G›´cű^NR\ŞÖçĺßNqrł)ÓŔř…˝<™ŚdM»Öe9)YęĹË–t¶d­îN:pupĘş{eϡh# @iý ÇÇuŻŻ(/;w&*1%»É7Ňâ;rßŔo,XܧŁÓŤ —Ü&Δ1 o2'XşyصwŁ&ŁŔó/€ée$ČÄ[¸y9ş:ů řA}ě Ň* ć–áďöngĹüą/ĂhOů¬{‰qI©€1ˇ”ú·K ˇ@¨1ű^BÜí4Bë'VŠ‹˝“-K’âď¤ĘľńÉ/k—LÖ—¤lÝyLŚĐ‹ľě§#RSvńJBŹö¸8|Éň´"„1}Ľş!ÓůSQFąMČČÁŐŮ Ť»~ł [Ť5$ăŇ‘Ý#âbă Š+Îź:eY‡Ś¬ÉIŘłçpqu]uqnĚ­8-áëjËcoŢĘÍJ>×®Wď˝»Ą%\>x8˛¬˘Ž‹Q~çNXŚ=T´|űvRNúíÓç°ťYöőźžÝď^:r=öÖů‹ńm{őj´SZˇˇĽ^]VÚBŚB>>ęśäČÓJ4Ő™‰ ÷ó ŢTZRrćtÔí”lBÁČ“§cľµMɨѠ»{#­ !HKzě*ť‹‹{Wfáě2`hp[…îÓŹŢ:›1&¦ę7RÉLrR\ˇş!$íŮHHHHHHHHĽ­‚Jë· Іęâß1ĂÄÖżß‘#5cJ[§®ŁB†áš’ȧÇ:Ą˛¶ćMśLeÉ 0iëa „°–,¦Ćş•¨0ĆP[>A˙µ9k´°€ÉŔ—WLJ]Ňęxń˙gn@ĂtyÚ: F@(žćďi÷ %ţäůűe?ěćśđŚĽ‰Ż{®[—!<˛Ś¦”YűýňëşŘsŢý¦Ľ:Ü38(0Ą”Ç‘˙ţÂúÇ"ü…ÝÚŇí@# „ÂKß5 ‰gř˝<ń *f>ž}~EeX D¬ÉnŮ P*·°X|₹­ Ď™Ă""X&C”A·^‚)áÂbęĂÝŢ@±c@@9NŘ0Ó 7129+cmçą1†°\ĆňG(eY–"‚fXHý§ţ“q1 Ë!!O]E€e°@Č3é> eA^6’x6«źílť]˝=Úž@ ¦33ęXU§î„ăaŚhInNşJ®t:| Ö]9]f05ô–Wßn¶şŞäü‡€P`€ŹÍv|†až±pë_×f\›¶°÷ÎĂf¬Ěd0"VféÜŁŔS3+c•ZWg4ł˛ HiýÂ#˛PZY›ŞËuµ&Ąµ !je©4ł±6U•ëęŚĘúOaĄµ•©ş\Wű·IÖÖrŞ›¨˙Ę’ŐççÖéîčm­©ŇŹ<¦ŐrfN.î–Ů©™mŃZNÜży©{ŹdŠC†Y˛ĆččKiN“aXŚĄDJaqSNLW׎ăl<ýg†ôŽŘµ«§,Ą®ÔÖ’:mͧoLe›·RËŠóó KSoDůąYŠ\ý†¦”čcŹü,hÍhA±[ŔŘ×ß+ĚMÔĂŰŇÁeÎĆOĚ-•fvŽrss÷ˇ“–\ą›8ęÝ·TV ĄďČĄ1iďÜşűöÍ„Ń/. ż7ćÝ–V23ßËcî‡ßĽ˝ęú­ĐŐË-T¬ÜgřWî­şžř·K>Ă_żr˙ťk1W/Ď~{¶«ĚRncÍŚ~µÇé¨ńŞv+ÖýU˘ÎKľŇĹB ë˛úÓM™ÍIĚĺ€Ů¶ńŰr21íâźbׯíĐđ‹/7GTUŞ ‹ #6Ż·ĺMh¶<Śň‰`†€î“ßÔ”çő´`A,ŢGOݱ5+FÄť™&ţv Ćů)>ŢR@ÜÖ3÷}/Ż#ćg¤Ä3ĐdŤ&®CBBBBBBBB˘ižžW#ľí>¶ç«;—wę*JzO÷ŰWáwăośÍż4(ĺxmyqď óţřć_玟č5aů(˙6#|Ú\ăüKr®Nś˛9°Ůż>˙ÔŰâ–;Ž›J*ł#Ł[¬ˇPW«Ž˝ťEíŰRC´éýĘŽíV†ě]sfÇ_ž2Ř:ű\B»Aůiz"P¤”›Š˛s.Ü~Q‰±(;űÂť¶ć§yŇL(ĘÍ»śě:hP~aÍĚŤE©»„"Fiˇ¬¸Sşý@É;Ć:çĄĎ_›ÔŰßĚąÇŔi#úĄ\»ÖŁ»;‹¨:÷ź>˛ĘŐk=š’C«ŠSŇsÔ» /÷ö3 řŽŁÂVż1ry?µß«;>žybßö¨$†Áüc;Q ˛!ă§ů{W¤;v´¬†ŚśЉáµQG’3 1BYQ§˛0kďÎýĽ^_\Zá?lҰ®’Ďś8“x€Ę<_ ›ćfŤN;ň ‰_:®O€oŁeŇľKŔ´IcËłR*´\Ćť™+Ž^łgN}dÉÇOl:ĆĆ”Ďú°‰¤,ËUŻž;ćlz­%19QŘĽ ľ˝‡ô 褠¦‚˘ęÎ]ÜNěŰťQX­pňš=ăŃ  }—ÓĆWé«ËJŤ˙ޡ}=ĺ?nř¶€§ sé0ş_—KŐD6qĘř”¸Á¶ĂĐ>]ĺÄ(ZŽŘ»+łHóź““ř_ĚÓYŤřČg¬®ĘÔT – źšźpěB†€IKľŰ 9z­D袩.©¨őöě¤&µUĄĹ%Ĺ0yÉg#Ľdě˝Ň·˝Ć”Ţý¶ď˙Sse÷Ůč›Ć‹0f<ĄaimUů˝»ŐšŰ‚\icŻ˘Ş¶wí×%íýý“c€0đFlí6q×ţşÄ=˙|ÉźĄÉ•löÝB _§a>ʵß= ©—{fČŔý!óWďîłč3Nő;:|ţę?ű6%ˇŹęF°śE/ű#BůUeĆ/`vlMˇ{ÇJ"5 Ţ^żăóeăcoĹ{űݰ€ň gÉĎ›7%ßdě;.]8gʸĐÄMřżűtɸŘ[ń>ÝWĘŚ…‡ł*íÜý>X˙Qcăµtîţ=b G"Źy1ŮU°hţ¬×¦N<7źÁbµ ĚP"Xú†núicYjcßqŮâWÇĂúFDť±®-¨b^ đvZ4ˇf•Ő‘ČHO¬í,6őÜťUÇţÍĹ8UVźq:¨ß±g}ër€0eĚíV®ůbŘ˝ô?˙Ř^TUŐb 0˘„„ĹŻ›îű° TP6.}¬ő3ןy4NáÚóówf¬]0ÍjđC˝8±LC¨R'Ʀč2rmąNA ”#ÔŮwöí0ĺ%V¬Ű¸;@9meu}#` a)1ţ?"A€8xş*öíLŇ;;Ťr±Ş©ă@!gęQˇeÉŁ4ćďxgŹÔUšꪻ{!AŻ”? 3„J÷á«–LŰôÖ´5żGŮxt˛ĹuPR5gÎÜöÎÖ*ßďż˙ 8¨kb9÷ö˛›VL^ó{”]‡.ŠÚ•ßD9Ń®[4zo’Kn~´Źź—Ňyć¨^í>[öE˘ľŮĽőÝEÓĎŻüľ1Ç_ę3ŻĎ›7ßĂĹFeă»qăš Ŕ^nNÓ˙ŤCrRlINiß°%ł_™<88)·âąýÖX>˛ń›ŰŞa#¬.JkóÚ°ŔYsŰ„ř»|óÖç ĺđݶíKĂ&»{´“űuďÍô K:˝Yěcýű†7ŇďFm|“R0Ďië´SW«5L 9:Ľń›8ĺV—ößs|'l ~ćň?ŘŻOBBBBBBBâKÓY FT3cšą1çŻŃD€Éţ¨‹0fćëA” ¦ŽË"Î÷n™5ctUÇW;9pJ7żŕžíÍśfŤÚs"ĆP|sd˙® †źó<†(ÂÔ¤g=ú†~±ÉÁҵt^ěo'}ą”ӚꋾR DţźP"đ„™™,űFú ‡ź6 Řűăő*Bëű`€*TZ!yä­˙bŇ·őĽĚą` 9®dž^„˘zxÉtĄ—®D€®0»šă˝üC~ݲÍEiČĘ-ÂÔP«ç]zviKDmAfĄ‰ó4Ó¨‹î&`e4šX;Og7ŻšŠňaŻŹVĘ+˛SR‹Š@ălŚ1!‚{—Ŕ-[EËĹÓjY•ł§WQÚ­"ś’VV©Áć¶Îí}j*ÔŹě"CaLhË1Ö„) IJ,Aě¦ÍĽ0}ÂeçdßîOOŠ5˘_ҶHqa-ý~XZˇ®Ń4:%+Čl\Ú[Ԩ˄-Ą”WfŢMQkÚův,I‹/ ÷’ŞkdlýŹÎŚÁ Ć +¨ř úR *Ô5sŤN‰)Qeę—-!!!!!!!!ń ¸ B‚@ŰÍ ›xóčÁĚj1,Ç Ť’4µ <\}Mµą‡Ďß˝•Ş6vé¬+ĎĽ›Yńú˛%ť-YkĎ©ăG0Ś˝÷—7Ż]&Öµ·°”úń7€ŁP*­mUm]-mt5ą‚Ŕ(mě-l-±Biáä¤P˛„PĄŤĂ˙+•ł3òÎćĄ:~aď O6ă^Ť‹«á)p1q&Žhx$çř–$Ź»ëĺ € XSjom»ű»5g{µ-­Qj0,îc”T°fV®>ŔČX9y±+Îôóé:oM ‡ĺ2¦´@Íš[‹:€Y b»d€B)1hŤ5:SMî¬Đˇý‚‚‚‡ z÷űaL`Ô”Ç,™ŚÓkŠ í˝:ŰX´u·µ´$ş:ŁFgŞyĐhç˝R™ťÇžcŹů }Çśą˙Ç÷ď‰}Äi°@9úĽś±ŢK‚@€ç ŐŐ "hkŤ5:Smnءýú üůĎ´••“+`śÚ+db×oĐ9Ѩă9ŕ0Čd   PÉ,„PúČ2řĆgR^#!!!!!!!ŃÄ6V=§®¦”›3ÄärŮSč7eĄ–ĐýŰünËB ďLćrřxW|iÂ~ńŤ´ŞŰd-Ą…7ö+šlő$ö@łčÝ´;r»6sľý—\iÝqĚô.!CX ţtׇ)©ŁÂßtďć)SŘxO÷ó‰’ďŤz÷˙Iűŕ)“7şuuVĄw÷ÖÎ^Žž`ekůŃwŰ“3ëj«cŻś{sZ(|öÓÍJ.ź{sĆhŃo«7śN‰ţx‰hbK.ÜÖ/1ŻüÚţÝ»߬.Ľçď¦Üxô­ŢŞę§¨DMqö+ߎ8{cáôŕSţe¬-űęýđ]oSJßc,l>{GSśýÁĘŐÇĎŢ\6Đ}Č|CMIw%€¬k™ž®Y"wîű ‚»ud÷Šĺ«®Ć%®\0X–i&,üR[]ŇhyŐĽ`łvŠŞ…Çś¸žČiŐł†úX¸ ĚQ›íĽµhXLk>Ć€Ěě“ ;|¸rÖ8č9i)ĄôÁŐ=,ŔüU›îĹ]\÷îşË÷ň+ îxŮ?áŤg\Čb€oŽ%ţ¶záâđťç˙řhů»[râyřôO+5ĆŮ˝bůŞë‰wŢ9ȦŰ(5G˙Üňă§ă(ĄŻŹó°[ýţÚ?ŽžŐÖTěŢ´éÍącTîA9U†“m߲ç"ĄtŢŚ1ďţvi÷»Ż‹–—ľóËĂäÓ˛f~AĚł"±FşťSöÝ+{E)}Z‚¦Ć'¦ĺöé?°­%óÍ'˙Úrŕ$đŕ8CĆÝř¸;é€đúŠň˛sg˘S˛›|Ë,ľ#÷ đĆ‚Ĺ>Ž7Î_tź6×ÂFE9#¶sđö‡]u'&SEĆ=#Ř{ö  D°ëŇÓÉŐ®ô^’ýS$ήÖĺ÷n‘m‡ľÝ)ě»ôpp±.ą›h ¶Ý8řté×.§wË‚űzĄĹ;á˝ÚZŔîýYíÚ{Uä¦Dž:_R­ÉLŚOÍ/öčä[ž}7ňô…&$MÎí¸”Ľâ%źm]űĆ$}IĘÖťÇŔ˝č |Š1"5eŻ$ôaŹ‹Ă—,ŹI+BÓÇËH0ť?e”Ű„Ś\ťťpđđѸë7«°Ő¨QC2.Ů}0".6ľ ¸âü©S™uČČÁšś„={W×UçĆÜŠÓľ®¶<öć­Ü¬äłŃqízőŘ»[ZÂĺ‡#Ë*j a'Š€ô;wŞŔbô衢ĺŰ·“rŇoź>ź€íĚ˛Ż§řôě~÷ґ뱷Î_ŚoŰ«WŁťŇ ĺőę˛Ňb ňđńQç$GžľP˘©ÎLL¸źW(đ¦Ň’’3§Łn§d Fž8zy÷čŃąěöĹwVľ›Qô܆c€3ę3“Ró ňsSn§dćdźżpöltś[`ŕ€^]Să/;y!ď~âµäâQˇÁĹÉ—÷í?t=&¦¤Š8l0ŻÎ‰<}7S µę‹Ń§îdU ŇŻ(ýĘţGcbb ‹Š¤&ÝĎÍĎĎMIşź•ť}?>)UÚ§‘h’Ǧv „zj<"j8–óH‹Á zĽ.5Ŕ'ĐBgxö+Ü:u2 ×”DFśw,ÚÁŐ•R‚€š´uśÁH)¬ldrFŕ #c¸:ŤAgRZŰţ$¬Ś1Őijô¬ŇĆŽ•ŐK¸şj… [ŘŰËd beDóp”ú•’·°P “+.Ó…Ž9^§ĺž¬˙~ÖmO¶* €'„ů{Ú=H‰?yľĹ~ŮĎqý“_ÜLú3ň&ľîą%ě­kIüČ2BRbfí÷ËŻëbĎEx÷›ňępĎŕ Ŕ”Rţąü ˇ—›zůě•¶Şľ˙e„„„„„„„Ôg51 Of/2–ľÉ;„0P# ˘òôÉŮçâ|tJ) Ä2,P"Öd·¸”Ę-,GśSZYA@˘”ĐúĽőĄ÷„R@ ź˙Ç%„PLąŽÂ-ŕ 2981 čŕˇôęŞZ ”–©Ěĺ…k&ť–€•Éä Ăî?Áq¤ńňźľ ­‘üG7aŚ@3XĽ´¦µ03 PB0 ˙‰"D „>ŁÓh±,#ę`Ě  ! ĂR*4‘™ ÄŕzË ĆĎdľîQz49›>[öv¦8—ói;ĎŤ1„ĺ2–ç8B)˲”„0Ă2@ę?őrŁ`0Ă"Jhý§ĆŔóÂłWІ‚0€X–%D „`–Ĺ”ň‚€L E3 x0Fâďç1ËĎ}—ř_G}V”*ť\Ú;Xf§fňôY  í<:ôíÓ[[žńę-O@nÓ.tŘ V_vęü%q¶ŕł:a"M?‹3 +|đŚ…[řş6ăÚü™ ˙Ś0cŃ` Xnnnm„Ókj PJţQ –Ö*f’ţ K™!?·N'ŕŽ>Öš*ýذëŽ6NJ†cŇ«ł «[¸ş{(Xĺô%ůKĚÚĎŻ‚@2“®ř^J>•YwmQr˙~>Oeë¶Z:.đVäöi‹Örőse^ćŢ#™bŔa–¬1:ú’ÁÔl˛Ä0,ĆR"ĄŔ0ŚXt#¦+ŹëÇq6žţ3CzGěÚUĚS–R^JÄ<fłzŤ'-ׯ!„€Ô?÷?eç91ÖD„?í ŠeV~~>J±F]ń˝űů„>cŚ1J)BˇM¬c#¬ă«s'%Fą“­ďÓCzÚń[9GÄ´z÷BÄď)Ë/ý;—řßbśWöSf‰:/ůJ 0¬ËęO7‰’®Vć0bö˛Ę:}NzzEUÍĹ˝ťĚÜ»ťŤĎ()Č-®P_řcŁ `čěeUÚzťč=?´©Żdη‹ÝĆľţ^anňŕžÍ­í–=ŕęd/ł˛•+X×ŃóW%ĺŻ:s˘­ťŤĚú'QČ™vŁľw?ćúśw^őŰšŰŰ+Ć.čsęÄ(çŢ“’ó+ ó‹+4)7Ž™!°č<&­°˘čanIEŐý‡-(;Žľ—_žź›ó°´"ůę!€˘EÉÝËű-ä€ěÚvÝr21íâźbׯŻ ?bńĺćŞJuaqaÄćő–Mß.Ô˛őúîÓ :b«‰î“ßÔ”çő´`A,oGOݱ5+FÄť™&ţvP}„7Äss9ŔśÖ4šHHHHHHHH4 *ß~3FöOąz­g€;‹¨:÷źŢ aEŮÉď/ź÷×Î>cÂo˙lđ†ŤşŻ é(ëĺ×Ůŕ4îöő?_ůkëéÄ[¬X°ë·˝>cŢąvřÓ±»˙Ř~ú®Lážybßö¨$†Áüc;Q ˛!ă§ů{W¤;v´¬†ŚśЉáµQG’3 1BYQ§˛0kďÎýĽ^_\Zá?lҰ®’Ďś8“x€Ę<_ ›ćfŤN;ň ‰_:®O€oŁeŇľKŔ´IcËłR*´\Ćť™+Ž^łgN}dÉÇO m2ĆĆ”ŹÇ3ŰLRʲÜ#?9QŘĽ ľ˝‡ô 褠¦‚˘ęÎ]ÜNěŰťQX­pňš=ăŃ  }—ÓĆWé«ËJĹ]M˙ޡ}=ĺ?nř¶€§ sé0ş_—KŐD6qĘř”¸Á¶ĂĐ>]ĺÄ(ZŽŘ»+łHórÇä$$$$$$$$ţ×Ă€>óL˙ľÇ‡Ď_ýgßĹDŕ@źq*¨ßQQ"¤Ć^»‡)@qIvťÖD¨ÜÖĆ9ďŢőÔ˝PšĄ®Ř˝ďgßšsO(.ÉŃę8ń¸Ň»ďöýj®ě>}ÓŘbńĆ Ć”– ¦˘k߯µkëÝ=l¶c¤,o*ĽöýZ»6ţi’®3縴Sf'Zř: óQ®ýî!NN­“+2ł]ńÁÁÉi{vţ^XˇfÄŰ­\óŰ{é/'ůóŹíEUU2„ X΢—}Ź!üŞ2cŽ0;6Ě&ÄĐ˝cD%„H oŻßńů˛ń±·â˝ýWX@ů†łäçÍ›Š’o2ö—.ś3e\hbŽ&üßż}şd\ě­xźî+eĆÂĂY•vî~¬˙¨Ž±ńZ:wL˙1Ź#‘ÇĽŠě*X4ÖkS'žż›Ď`,Ö!ĚP"Xú†núicYjcßqŮâWÇĂúFDť±®-¨b^ đvZ4ˇf•Ő‘ČHO¬í,6őÜťUÇţÍĹ%č3Nő;VáôQu z¬aú”ź[L!F”P°řăuÓ}V ęŔĆĄŹµ~ćú“OFyăňě*x}ÁśŮc†Ĺé|Ő’i›Ţz%xÄn}‡»–% sćĚýůŹ=żíŘ«téÔ,űĽ˝l†¨ăß?řđ…;*•…śh×-ÝłÇäjł¶>~^ĂĆĚŐ«Ýľ­›·lŢ\kĺóî˘é@PýX±Ďž>óúĽyóEËćm:ö2rž§¬t@·Ţc篩ԓę*mČ´ů#{´i´ľh*pšŞŰ-ĆŘ“ţ(¤Ź9 †ä¤ŘÇýL)m~^M=¬ ŮřÍ–ĂqwĎm^űůŻígÍ]âď˛ŰŻ[6˙Zgăł4lňřŃ“ÚÉ‹ş÷7˙ă:ŞűX˙ľaĹÄĄźňŚ‚R0Ďië´SW«5Lr9:Ü`ůĂO~ń4H@"Í«‘x–GÓŘ)!¨ˇNâi 85˙ö÷ă}ůáC—éNŘţë ]çćfV–—3r%Ś :C–—€1ŇÇŚěßµáëžó$ŽĎ1–.×}%łP ĹwwNź’“”ÓgĎ Źż4łµŠîüŁ$¤čÎŽ)S˛ďćw°µČŚI}éřÓ†{Ľn”µŃe^› Čţ˝?=)jě¸áÉۢfLľi4dßľĽdThż¤­‘Đx“ţIßÖwđ2点3f.ü帒ybxBtčá%Ó•^ş şÂějŽ÷ňůuË6Ą!+·SC­žwéŮA¦-u´™•&Î?ĐLŁ.ş›X ‚•Ńhbí<ťÝĽj*Ę„˝>Z)ŻČNI-*FŤłq0Ć„î]·l-#N«eUΞ^Ei·ŠpJZYĄ›Ű:·÷©©P?˛SX …1ˇ-ÇŘłbY ö@Óf^>á˛Ŕső~Ń/i[¤¸°–~?,­P×hLť’d6.í-jÔĺÂŹRĘ+3令5í|;–¤Ĺ—{ÉŐ52¶>{4c01ĂĘŚTĚĆ őĄ@ T¨k4ćťS˘ĘÔ/[BBBBBBBBâX€ú'%Ž'&ÎÄŐo—<’ ¬ k~řů•`ŰaĂBÓ‹0Ƥ2ç­éĂß5“9węqńÔ‰eąđńĆ˙Ş×É(aF™}‡u…›r.őÓ>ľĹ©´áo0#— ,gqý!ĚĘôĎ“ 9‹18ęŘĆĽĽ^˙Wď OöÓ{µÝ•ÄŁ\”Ä޿а)ńźIąëĄ­kJí­mřpΙ{¦M^mOÔ¨5·1J *X3+WČNcd,püČÉ‹]qf'źP}Ű>‰QrSZ fÍ­EŔ,GÄvÉ1„RbĐkt¦šÜYˇ!<°2y}Ó¶†Ă`âT¤QS-÷ÍJ:)ăôš˘B{ݎ­»­Ą%ŃŐ5:SÍYˇ#D;Q łóhMŚ=áPęŃoĚŽŤ_ßÜűÚŞő Ü0šé)?7­‚@€ç ŐŐB[@m­±†jsĂFڍŕAa¦0ęŤo|4^ĺ䊧6ć Ycf’ű7/€IDAT­‘3Ť:žL¦P±¬JÎ`A „ŇG–©Ŕ7<“ň ‰g`@Ŕ˛Ożß:nř [GÇÝgĎěÚôí:űŮOŚ:ŔÖŃqĎ…ó×}\ĺ0ř«Őó3’nĽóá^îößúÖˇ‹üÉӿܽ5pćŰŠň¤m»ĎOXôĺg+ç¦'ÝxçĂ<Ýl\żv˙©ć®«W-©ľi÷ÍOűřćëj(Ą„ç(VeYzaOÔĄ;vĽËož®fÜ:őí’µăڱˎť:—Ý8őO“Ô(<Úů´iGśzz™EźLyp¬¦ŇĆz ŹŇaŃw Ćů;|9xŢBááľýŃ3–~˙ɢ^ű÷]ö˛[cţés7ë=f0 ô%×ŔŮ3‡“ňWôHNľíţö‡ď‡»ŽzĄł˝…µ-͉lĐůŻľăfžú-<ŞXh×Ć™ŔöÎ.Žň=˙řţÓkQv˙y!aĆüą7Ľé·H–eÄG|1‡xXeŰnˇhŮÓŃÁĆV¶˙č~QǨĽíÍÜ "ŽěÔ~ÝhçĐ–O~ÜaîÖŁůCT@˛O6l2ŘÖŃq÷™3ţú㦽'í\܇őőĎĺîÉYŕxúęŠ áŻt;xŕR˝źĎßŇěh úÜÉĘÖŢĘÂĚś±µłR+•Öím˘Ö|żrŐĄSvďľ¶đµ]߼ą˙řž/םú}ËŹ|ű ·6»ŐďżŮ˝ß@W7ĎľüöVüé?.Ą¬ŰôëöBÚÁĂĹ3¬Ąµ­µ…™9µµłR››[:ÚŰÖ/˘Rb#!!!!!!!ńőÝ—=:ú¨ł“#O_(©Öd&Ƨć{tň-ĎľyúB‰¦*#áv^iUAazâÝôšÚꇉI‰ů…ťĽ;2üá­“Kß|'_ŁłPš?ČNľťśQS[]Tô0ńv|~ˇš uYéą3Q‰)ŮM>ډďČ}Ľ±`Q ŹăŤó—ݧÎĐ—–Ô©ŐýB?ZgçhÁsĆŞĽ!|lgo&¦ç~ŠpşŞĽşĘJ ÷>ˇŻłw03tOŮáŚúI`ȇëěm°‡7°·mţ}µ^ińvxW ôÇţLkWŻ^˝ş”ŢľřÎĘ·ďä—QÄŘ{tčŃŁsŮ ‰Í(żńÉĎk—LÖ—¤lÝyLŚĐ‹>çRŚ©)»x%ˇGČ{\ľdyLZÂ>>%!ÓůSQFąMČČÁŐŮ Ť»~ł [Ť5$ăŇ‘Ý#âbă Š+Îź:eY‡Ś¬ÉIŘłçpqu]uqnĚ­8-áëjËcoŢĘÍJ>×®Wď˝»Ą%\>x8˛¬˘vĹě,ýÎť*°=z¨hůöí¤śôۧĎ'`;łěë)>=»ß˝täzě­óăŰöęŐh§´BCy}Ë1yřř¨s’#O_(ŃTg&&ÜĎ+xSiIÉ™ÓQ·S˛ #O˝Ľ÷ósŽQΨĎLNHÍ+ČĎMąť’™_}ţÂŮłŃqnzuMŤżxěä…Ľű‰×’‹G…'_Ţ·˙Đő’*fŕ°ÁĽ:'ňôŢL%ÔŞ/Fźş“U5dHż˘ô+űŤ‰‰),*~št?7??7%é~Vvöýř¤T)ť‘h’ż«ôřĺ[Î"Ś)!nťşŽ †kJ"#˘ĆG\qpm+pBÔXŁá9Q+--(!Qcm oäĄĹăÎȱJsĄĄę‘މ·lăffi!<Ęiëx#gĐć@ 5ŐÖp&ľţS‚H”ÄÜAˇRA@šęjgçlgamI uLkfˇ°0'D@@ uZ–×NŇ}Ą`ô ÔdĘ+M!ŁŹÔŐqŤM¶0BOMQ| B€Š'„ů{Ú=H‰?yľĹ~ŮĎ»iđ¤ń&ďŕ3ň&ľ®ąĎľ`|<˛Ś¦”YűýňëşŘsŢý¦Ľ:Ü38(0Ą”Ç‘—:úb<{/ZíTôT+‡ç:§§JHHHHHHHH´–ú¬†aY @BDŕ2ËsG!RúčB –Á<˙¨Ď,fXËp&#ˇŔ˛lýxx„X†JÄšěׂ€Rą…Ĺâç•––ĽŃ129ˇ„P* ŔČdâc˘ŔqőźĂ‘É ĽÉ€€RĚĘ(!×¶ţÄs‚R!§o˘Ś|ŕ˘%¬\.˛B ĄĐxĘaYŕĽtW(o’+e^@ś@÷ěIÖht ĂPJfX9Kxee ®ź@ɵŢü-39#ößbZ[«?YĚ™řzŻ!„ćY „`†A” „`Ě  /*ůűn>Âf0%¤™¬cŚAŔ ” Ĺb„Ä»üŚNŁ!ĞڍÓx! ĂR*4‘™ ÄŕzË ĆĎdľîQz49›>[öv¦8—ói;­Ź1†fffXHý§0Ă`Dyž€¸H[•Ô`†Ebý˘”"ڎ±l†ˇ”B1ĂP" Ä0 xP`Ů†Ţ ‰kŔ ¦„"„EŔQÚśe ‰Ç©oÇ$<ůÄ„pśŕŰwpO/—{±7ďfçcąu×Î2$3éŠďÝĎG”ň<řöÜÓ«Í˝[7îćä*ŤĽŇÉĄ˝evj&i¨ý§”¶đ"šaXAŕg,ÜúĂ×µ×ć‡-ŇiµÖ\‰5j*Ü’A&ŁA0ł˛ÄkŞ+ o”«,1JBTŻ.€1ł¶·0†Ę˘Â˛kWőĄyĺµP©¬=Ú9čĆŤCć–@)B@3 /c„@¨‚ˇ]¬dúĽě:ť€;ůXWUił"“mͬ(˘QľD]ei©*))ŃŠůf˝ÝÝA–źźaŞźľbîéî® ĺ Ö¬Ý?oM/Ôd\Ţ#Ż7Ž›ż‚żí ¨„„„„„„„Ä˙eاţŤ1CĐ~ŔڵoÍY˙zČúżbşöěS(dŚ©zőÜŃgÓk­×š ý€™Ż|ĄQ§@GÍ»M í—rőZĎw^ĎËÇO %•Ů‘Ń-V€P¨«UÇ&e!gw &ÁD @ŔM‚I9'Ś€8ťA!WšŮ5j-ß®ĆHĚŤĺEśŤ‹ą¬¬şĽ®­ÜH-HMąAݏťđĺ̡ ”Q›±#ş\;űŁ˙;›Cş÷JřxivŤÍ¤őź“: ‘[[Ýţbu¶ĆjÂ×_*¸ĘÚśĎÝSřĹŽ±Îyéó×ĆöězúŔ——Ž˙ĘŐ”NxmۧKĎďŘ1`Ř`•“‹ą‹Á—8µłd ®Z`TNíĚŰđíPq›övşĘ’vÇ÷uźćçvŮÔ­$çę¤qCS6źŔ &˙Ů9"JehUqJzŽzp7áĺj10Ăď8*lő#—÷÷Sű˝±ăă™'ömŹJbĚ Źö˘dCĆO ň÷®(H?věhY 9!0 ĂkŁ"Ž$gb „˛˘NeaÖŢťűy˝ľ¸´Âؤa];$ź9q&ń>•;xľ6Í͝ۿßńáóW˙Ůw1ˇ˘@ÍĽűnß˙§ćĘîłŃ7Ť-ŤcĚŕúŇGŻłB¨áeżř_JĚÂ9{ę**o|óI6×kŘp[‡Nídŕ -Wß8±6ËÔsÔXg•§ěâí›tѲ ÎŞĆ~ţ˛/˘ŻŇąclś•ćf2“RĺäĚł”lîlŻ4SĘ J Gg•@ś(´đućŁ\űÝCHÉĐť&űA ,>Ąđvä©Äč·ô÷o·~=@–ôĂ©[öE˘ľŮĽőÝEÓĎŻüľ1gs}ćőyóć{¸Ř¨l|7n\ŘËÍi𧬴s·Ţ´ĎôäsŰŞ«´!ÓćŹěŃćłĺź‹vÂM=·â[NSu;6EŰ|ŚaFĆ"ÎDśľűţ}Vô‰łÉ <ꎀžëŤ¦ś‡€‚yŰ®]Ű˙±fő{›ÎÚŰXčŚÁT őwG]RL1‹A(%c `–ˇ„PJdmÜśSvsě7şŁK˝’äůĘ;íŘ{—vwč7şÖÝż|ÇqĐ”‰?ť>ť6tĚP…±ç›g˛«ë†Ęd!€†Jb“)€ú2 ”5“eßHáđÓ†{Ľ^Ec cgL37ä썺™‰é3Ś~›śÂŰŤßçІOîńÚéďÎ~};…·7ľĎń ‡B&.ŘŃlÁÔqYÄyďźż.ť7éămÇ0ĆB+útµŽ˙´"ôm}/s.;cćÂ_Ž+ćńż"„(@‡^2]éĄ+Ń +Ě®ćx/˙_·lsQ˛r‹05Ôęy—ždÚQG[YiâüÍ4ꢻ‰Ĺ XŤ&ÖÎÓŮÍ«¦˘|@Ř룕ňŠě”Ô˘bĐ8cLŕŢ%pËVŃr1â´ZVĺěéU”v«§¤•Uj°ą­s{źš ő#;……ŔPÚRŚ!*p&0˙ö÷ă}ůáC–—Ŕ˙{ďVŵ5ţŻ˝÷ĚázA‘"Š˘bď(bď Ť-±k4š˘‰i&÷&7Íkn,1FcI,±ˇ˘X°7DńT¤Ę9púĚŢż?¬ĆÜ÷ű{óf>Ď}ž{Y®YłgĎ:÷Ůkö^kaDp<”JQbĂłń4ŚR`H?ż~ăńŮîňžîč/k^_öĄQJä"` @ÁQé¦)ĚĽW ŢMÎ,PŃ…Ó9ŻDvkߤw as“ËO:m0*W9“‘‘‘‘‘‘‘yđK]ŢËŤ‚(ťĽaVQÚA©ý Î¬"Ą@°ĄÜP)>Ą‚@-V‹•R ŚńnAź}·öĂ…1R^{C©-bEoçd2YygW;g&‰Ť‚·u4š¬ŠZ‰a…[XPŮý[ŞFáM}Ü ď§©Zą·ň+˝KŐ(ÜĎÇ5?'Ĺľm6•ť<·Żi×1c{Ć9iĄŔĆÁ“‡«_,•AG#»-6ŁC·.ă–λ±Ęj08OŽqyßî4Ť÷rR]›4ÎNL8~ůŽ·Ź÷ťś¬;Ůiž>Ţى Ç/§7ňqżž^âéŰF¬ÉÝ{âRâÉĂw*,í‚€ţéj’FţĚW}é-iKÜś\¶~˝,ţ–Ąm O‰Nč}Ć $żśS96öÂs0`Ô¬Ć834¸eô´e:+Vđ¤$_ĂŮ:I:€ĄÚhTE@PƨIoÖ,şÜ‰Ń}ştëŮ»çŰßěbŔjńRÜ8pô#–)Ď[ŤÚ·Ŕ–Îv>~.ÔPcÖ,ş»ěĽóínŔ»ú7ŕc1°ěŰ^‰téŰ7:)Łă8J™çÁńç®mţćčsgŁŢYdU­X4ŇŻ±ßë˙ÚłxůčȦR±2¦·P©`€ÍöŽîŽ9ůűzĐĄ\;9Á[Ľ{Űä4iX‹çŻÁrÇ™—äɨF:"ű{4Ý´mă×k6lăľ÷DüČ_¦^>öŃŰť<ąËŃwţrňĹ„ĂyÔoÓ¶Ť_ŻŮ4°•[BňôŮęM_ľ5ßĹŁéÖřř…†€˛q»%‹çΙ4޶îÔ3VőŚ1*X€`6¸µ‰„ôłűÍŰ÷ÖÜ»i5ˇM‚»ˇĚsűÍÝ÷ÖÜ»iŐAáAÄΛ·óńW…“g­¤ĄĆŕěęlcĂźOHdÎÁ!äěńSŔ©H( Źl ¨ !d1 ­}ŰŞ©§Í8^jď8˘›{Yą6bú€0ď_wm—®É*®TÜNľ~ăú-RS‘ť^VĂ×JRxŁ.űjú5˙žżlř÷×ë7jĺyęZ2ŕ˙Ę#ŔL‘˝dą_*28ż'%OÓĄ×Ä­Ű~ő2ÝŰu±+0F€&ůŘŽK÷ľÝtxů˘7wî?5c\djęużđ÷ß]şyűú /7;'–sĽNgÉ®ŘSsczč ˘o#/»ą{y{(íŢlpí·kë‹O]Ľ6ş#¸{űőíÖ=˘5Çă÷60őĽLŔľősW®.›;©C+?«¶´0żZú×[ɧwqŕŕžy1=ŽíůŐä±îç˙¬úy]'wńčÉ“WŇîşş:gžââíC8 €EłŢb43Ne§P*€!Ѭ7MđJ‹¶R˘r˛·čt°ŇŃѢ­Űą:ë îŻe1čk›Ž0¦°ł›uđ„Ťť=V( ł^Ź0F)gkO5L6Č<\˙™Š3ŰŮŮ Ć,f±¬Â<,ć´ŢđżĄę.`€#‡Ç„¸ŢU_;t˘ÁzŮϱőŘ#=ëź’×s»çNNCĹďęł,US9…®Y÷QâńŘf]FOéŮ­ŁşDŔý7Š.<}sŚŘ*Ϣç>Ôk !Śjʦ!‚HÍ7_ö ĘČČČČČČČČŔłŞ ÚoÜ„#T€cµ}âxŽ+ „’iĹĆń<Ô&W ŽpŔ¨đŚ,yéO~ú­ j)öŕ1˛čká0˘‚Ů„ `V} lPÁlA„€hŐk4 {{PCy9"<Q’đ ęË4@řNÓfXÍ•#FÔ Óá8QQ¤!Ä Äa(ąĄFT@VʨŮbĎÚOmnĘŢN,VىBÂaŔŐ*"ŕ˙@" "F0Ź<F-AĘJgSŞPÁdb5(­& !X¸Ś™•·á10«…š­âÎÝ™§b=őČľôëÇÓÚž-ř٧­0!HEL0JiíëĆ„`„()eOé<\Ĺs‘t0&H)!cb=‘ ‚k-ŚE«‹Ôyc&Ď0is&Ź}W]" „¤:cŹŘyžŹ!„VqĎ(Š!L8RëĄđHíiN_pRâ0©€1ńasF8NšQ¤ ”2ŚQĆ›Žc‚ Ç422222222†úöjĆÎľŃ}{rĆŇ#'N›­´Nڇ´v1T¦ćÝ—ţVyzűą;f§e Ô^EŚĄGë®@„*>Ł›!!ś( ‘ăgüří?«3ÎOźđZ§ßŽş{:߼a[Ď–-°`¦@8ž–Ą©­ě…%˘ĹĆÉ•ăA“¦¶€Ę«e °š ÚŽ¸ f3r <Ó¤©-ĚÖłes"ZěUĽ$UíUŚp<Ő¤Ý~ľäÎmÉλ*í¶T-[*­UĂô˙pŕŤyą5Š›;WWÔt:ňÁwŰŽęrůІ±3?´Öö•y™wŹx›î˝ű:pć„„Ó&Ë3ó:á0FŚQQB”ĽRŇ<Ô«Őę6!ŞCě–-E㤭 $ĹA Š”>#‚ŞŐxÜrťW"„€RVźť†|LÚ&ňőęÜ©ľ,ďÔą+á 5Ŕ0ďŘŞU3ŹqfCŃ­Űy”= cŚĄ?ĆBT)«!PZ;BŚ Fq“§ŽLNŘ{#[îÍÚŤ‹Šř}ăĎeVŠĆQQ”ľPJ!ň”ĺ?ţ’edddddddţ–H‡˛›¶‰żšQśź[T®9öÓ×ÎP[ŰŞqhu±ńęŢ5<ď%ź~źY¬ą—z6Ô^ ÎţO]ĎŻůE8†Ě~§ 7µWD •ťý˘3{Śîhëíăčëĺ3hîW3Ţľ–ŇuD;ŰFľHňÖ•kť‡´UyxŮ{ąy÷źąđňí·Ż]ď:˘}ĂW˝{-©űđ¶öŤ9űşűš˝äjÚÓvŢş|őZâŕĺęŐÎâ‹ÉçĎN\2©1ď pv"'‡ÇĄ˛á=[ýx8ůΩmRŐŻ?^YMşÄî󵱕š‚˘‚Řµ_8Ô˙ľPĂÖëĘb×ę`Â@ŰQŻkËîµ·ă×µOEOÝţyC”Žkˇ'¤Č!ô›4Ż˘Ć“ž^^©KŘţm#ňŘŁJy˙¶!nÝ+ËËÍą_R~óĚN[ţĹÜ÷ÉA×3B„Bm›Ü2í˘1Ą î?=C}-Ü–ÔÎŇs-ËČČČČČČČČ<'O a "…aăć÷hF:‡µ4yM>żeÂÁťë\¤|oĺ'Íśŕ*á€]H·ńŃ]ÔçηoŞŔ †Ť[đđŞs[bí\·˙*Żô44ŠVdNh0„AMµćJR:sń®J=vĺDÁČź÷6âs6Nśz[=±ąMĘ•cc·ôć˛˙äjBŃÉť;SšŰ¤\9v˙ąW]:ţđŞ[ęWęµó?/ÉÚ0qFFÚŕ톅źmću/}ú‡)]»x;ŮăâŇŰwrĘ{¶_îë=&„ŠBó1Kć XĐ5T:%v㊠wü—BŮń@1ŕ{Ý-¬Yy~úţýűJuĐ{ŔđŽá- Ź‹Ý›šY€1PĆI:Yżý˛S0‹JĘĂúŽěŰ:(5ţ`|ňm¦px%fl'th˙î$őÝz2m×+zh§đ–hÓVácG)ËR—ë­7.eŢŻ°ńś4aĚC;÷6"ş^ŁŚ@aÖ­wLűő—]Áß:żç“![7ýt4•ŕÇž”1ŽłV-™:řXzµ5›­’đŮs€2éĐ»Kx fÉ/¬jŮŞÉÁ[3 Şl<'Ť8BhÚŞÝŘaý4…ĆŞŇi3¬CtçĹżżű*_`Ŕ{ ęŇętllĺGڦľzQt ęÓ©µ‚š%˱żmÉ,ÔľXz’ŚŚŚŚŚŚŚĚߎ'Ł©A‡‹“÷=ő…´|ŁX’”]®ďŇ*lý«#|Ţ?ßüŰŮÎM•€1óH×.{űM_˛­ó,FpqňzxU…ľc‹¶ëŕŞ"¸ëO;·iĎn=–pŮÜ`Ň8Ć€’+ąxŃ9¤opcŢŃ?¬EiŇmgU’s‹öľÁŤí9‡ż‘¤m Í5ő]ßrűć}•~}nĄŐđ „'če?ŕcD)@ϰn•™䓍ßM˘ÔÔ¶y8ÄĄP„hťPo~±qĺüa‰W®5 [heߣ?¬]]z™¸5ź7cňčˇŃÉ9ÚĄ˙úů“ąCŻ\ n»7ěÉŞpő ]ţĹ5Ä9pŢÔÁ]Ű]4ůď=Ľ?”gWÂĚé_3âÄÍ<‚±(NĂ„QŃ!$ző÷«JÓ®·ćógMŇ·GďTť_I^ oć9sx×ĚJÇ˝‡`Ťdçµ±cŽßȱoţL“’…ŇĎÝąF@QqŽŢ`}číŹ$ň#Ě­ë˘eźő˝•ľmóO…•• †#F źµâŁq!÷+ÁjŔŮ»““q‡vŠk†Ë˛+aök“' î{Őp(.Î^{O«p iä …R=‡-]ŞŰ–Ř÷s3µĺĄŐ‚íô…ź6w1«š„F¶oŞňtź8¬ëޏËBíz´vĺ·÷·×÷ë*]UQV†xĚE—tm]gţůë1'ś`°2Q` 2† OŐb¤˘řď"a€2WňY—ňrł=ľ˙®űo˙ľPIkł=ţ|žµ}BzÍ÷Ęź:~ÂŚ5”ä±ćE!Ô.7”ś>›†‚ě*«µnýoĄ)+·3SµQđnÄë‹%}~f…ĹÖQĄŐŢL.ŃŃl¶p®^MuĺeÝcfR*ĘłŐi…E葪ccJEżV×˙(Y.BV˝žł÷ ,ĽsĄ«ď”Vh±­‹WÓ`]ąćˇť‚`*¸ÝŹ!&Z-`űŐ¦]ĂB„~˝”PŔQĎĄhú̓㆟+˙ŐÎô”¸ý»¤l8, ¬ˇßÇĘ5:­EkP9‘wönj§Ó”uŹ™5P©¨ČĽ©Öh}CšßąV@oĄŢ×čx®öG§"`L8Ŕ̤hŚ(×č´¶Z3ŞFj222222222OźH «îľ1®ź·_ă!łŢ´Rś§+*ÔćßÎ,›9nxKÎ)`ôĐ(Ž1Dj±Z,˘¨*÷áUŚÜ-Í×ŔĎľ[űáÂ)Ż˝ˇHë5jAnaŤ+ďć‰ŘÁ†X*îf)ü‚=ZzUŢÍ˙ŰIr˛ž\Y ›Ńˇ[—qKçÝXI ěO„5 iKÜś\¶~˝,ţ–Ąm O‰NuG@:|U’_Ωűá90jVcśÜ2zÚ2ť+xR’Żálť$©Ş­-ę €eŚšôfťÁ˘ËťݧK·n‘˝{ľýÍ.¬6`ŔŔŔŃŹX¦°óη»ďę߀Ź!ÄŔ˛ox%ŇĄoß褌bŽă(eţťÇź»¶ů›w¤şR7X`V‘QöĽ¨±v–D‘‚`5UU‰€¨¨Ż6ë –ęÜţ}ştíÖ'˛×Ęvé+LöžŤńldkĂ BíSëÍV“Ůl¬`ĹŔó6ö€Ť˝‚`QÚHz`™‰Âgr\########óOĺŐ ĚU:¶Űµó+őĺŁ=&Ľ‰ “Ź9‘[sęĚFd0 +¶\{˝őőqsVŠĽÝ'߮گ—‹‡ÇÖcÇ7ŻútÍŢÂÇÖß:×c›|qňďNçşdńÜŞË®_~żCxv^ cŚ V`5č=»·Űţęďo.đu(*§ý»7wĆĽ=Y»çÍ>ö…‰Ż}aőÜ#Ěąµ T%RßÝŻ-µwÚÉ5~­Ěd}ţ®*28ż'%oa—^·ö ö2ÝŰu‡Á€1Š4ÉÇv\ş÷í¦Ă^˙üˇËĐq6,OM˝î±ôÍ÷ß]Úxŕ+A^nvN.,çpťÎ:ťpäçĄqE˘o#/»ą{y{(¶ďŢüÍ'çăvmÝv2iüô©»×®XýóaŽ#Ň_Š!îçgąřÎ,x¸;»đ;÷í´~6/îŔ.Ť{37U¨ vď/ú ěüľţă˙kۤݳ|ŚpDıł?˙Ç’éé)—Ţz˙Ű€&.«żüpGÜ%wożľťĂr­·X6eáwK_ił{×éľÓf¸óŽž¸ ô™=|jk8ş¸9Ú©l‰‹«ŁF©tňoę·ě›E‹OٵuëɤŻnůňőť¶ţŃ‘Më˙-4íܤĆŔuɻݷíŇŁq“€ĺźuĺÚŃͧŐ§¦¬ű©€ů{{`Â98ą8Ů©l™‹«ŁĆÖÖÁĂÍĄöľÉŤŚŚŚŚŚŚŚĚzĄ‚ŐěÔ<˛OżűWÍ{}i¦Ć€"eV«)ăćµÄw!˙ŕ`MNęáŁ'‹«Ş˛R’Óň˛=›µęÓ»öŞŚ2=PÁX^Vz<>.Yť]ďbLúFұűś×fujîy1ţxŕ´%ˇ}şWe¤fťJ°kŃ%bä băÔ%B—­ÎL8ůâÄŮvl§ÍLÍLH°kŢąý°h¬přëHNÚ¶čÜqhT°x©G—ĽŰŁŇî­ĄŤíŮşíwg._ýńĽŃ†bőŹżě#ôG׹ cDuĄ§Î&µ‹ę–Î]pńN!Âř±^”XN‰3+śŁôŞĘNÚ˝gßŐ —+±ăŔ˝3NďÝş;öjâµü˘ňGŽx§¨˝´9IŰ·ď)ŞŞ©*Ę˝x媞 5Őe‰—ŻäfĄK¸ęѡG‡6w’ÎěŢs¸´\uÇŤč,ýĆŤJ°4¨Źdůúő”śôëGO$aWUöupű¶7Oď˝xĺÄ©k>ě””k™`Ô”–ÔëcR7Y…B•›ťz=5CW]UXx?99é^A™(XŠ‹‹ăŹĆ]WgSfz6k×®eéőSo-z31ŁđąÇ€ŐlĚLMJ»—ź—«ľ®ÎĚËĎ>qňر„«M:věŃ:íÚ©ý‡NŢ»ť|>µh`tdQę™;żpńbq%éŃ·— É9|ô¤ ˛«5§ŽÜČŞěÝ»KaúŮť»÷]Ľx± °čnZĘíÜĽĽ\uĘí¬ěěŰ×RŇäpFFFFFFFF¦^^äBŔa©Ç˘Ô(#ôĽž€/Z„aĚ(mҢőŔ¨ľXW|řŕŃÁ»âś<Ľm+nś:·eOÇďş8ň+ěÝ\ËSH8ŠlěÜ\ĘSNťŰş·ÓS’ÎŻż×¨‰#6öîn) g~ŮÓiÁ»ÎŽĂÉé-űzĚ_8ŤűJLŽ* Ěd°––ű <Ř)rTD3ŹőŐC'¬—ýüËžń<[^Ďíž›ÂţbŢđв´g¨r ]łîŁÄă±ÍşŚžŇ/ ˛[Gu‰đÜ ţ˙ /ŕá/ú¤/”ß˙˛ďPFFFFFFFF^$ŞA°§—¤a„jŹč`Âa ‚ žX¬Vxtm‡G8`TĘÉnřNŔÂÖnöᥣŐ`$J[…­J0Ö"`Ä‹•ب¶*Á¨§ !ĨH9%occ5ę)eŚŠ”(”6¶*ceyâ–MÔb˛L”ł±wtLJ`0ë ”˙ HěÁ¤ 4_â±h2¬"µµS VqÇîL«ő%Ďž=ů 1˘˘ô1ŁôQ Ɖ˘ F)B°ô'FR‘Rö”ÎCă¤1AŔDJ áë‰L"¸Ö2ÁX¬ą.ýhůŔž&mÎęOż8v=SęËů¤ťçůTÁYşáx`˘(R„0áĐÚ«0!1A  í˛ j0á٬ÖßĆ âÓOŠ a”"uVÇq”Š”RĚq1A1ÁŚ2„0!HDŔ1ö„ĺ˙ÂK—‘‘‘‘‘‘‘ůżŞWÄöoŐ®C«f5•÷Oť˝l¶.ŢŤü|™`„1bĹą9ůšJ…ŇłOżŽŘpöxB©É"­żTžŢ~îŮi™BÝgwBĹúâ„p˘(DŽźńă·˙¬Î8?=ffűźvÚ;8s*%ѬŐ"[ÍfŞrt&µ:Ä«ŔŞ·XDĚ`TĚ«‚(bL¤RąŁđë´1} gďŕmĎW~ĂŚňé5üő«w—%] éҦÓ'{¦~>Űłó …×r—]»Ú˛G‡KľîŐ‚Ř:Ű::(śTŽníú7ʞqtQ9:(śě\mCú§Đ·V®ÎÖhóÓ.¶v´^éűÖçk˛ËŞţB…˛ŃŇĎ˙“SVQpű\G@€»4 ](ůΩmRŐŻć'.±ű|mle…¦ ¨ ví’ĽMÔ ˇÇF ůOŰQŻkËîµ·ăj}=9Ć1BŇ~ zBú‡ě<íϧw~çCžxÔźŤçşž"„Â@lŰä–iŤé(ýSp˙éęká¶^ä—ňç+yËČČČČČČČüßĺ©.śT€˛ôk3&DO¸9_}lŐ¤\·"ăÂV]ia‡áÓ6ůŢń#†/Ö¨pŁóÖ°âÜ ĂúwRÇ“AÝŐçη÷ă€c óĂFDÓŠěĂ f€0¨©Ö$¦d!/?`fF9{G%±c˘HX{G%2µZ]ôgŰ:‹řĆ?ßĘÖ:Ťüb%­ŃP…“ăőĎ–dW9Žůö_Ú¬ÔC˙>0şo§[§Oµk뇩¶ÁťĆôëzútűđżŚDÜetżÎ7Oźmî‡ xB+‹Ôé9š^mÄ—űzŹ ˇ˘Đ|`Ě’9t Ő„N‰Ý¸bÂÁ?ĹĄ‚…Gv<b řŢĂFw kVžźľ˙ľRô0Ľcx "čăb÷¦f` ”q’NEAÖożěŚĆ˘’ň°ľ#ű¶JŤ?ź|€)Ü^‰ŰÄ Úż;I}·žLÄőŠÚ)<äeÚ´UřŘ‘CʲÔĺzkĆŤK™÷+l<'MóĐz¶ŹŐăĎń«†üeÝá‚kźôĹgă±9D@„tčÝ%Ľ… łäVµlŐä஭U6ž“Ć?!4mŐně°~šBcUi‰´SÖ!şs€âßß}•/0ŕ˝uiu:6¶Šň#FS_˝(şőéÔZAÍ’ĺŘß¶dj_4YMFFFFFFFFFú,Lx·ĐVmĆľőĺýĽě1]€‚|±ëŇ­“ Ő€©ů÷ógö oÝ}Ę˝űůÓ‡EH÷›ľ¤(ďvk–ěضZ&˛¬S[lęý$]»W3ëťLőY€ŇłÉČ7F:6k?ü‡ß&­ůŢŰĎŁă'{§ýs¦ť_ëk~›´ćűFŤÂßúéýŰŮËłîGŹčß»™%o]ą±äRęűw ŁÇ ôŹ˙^Nů„ ¶.nĐoú›Ey·[«(l”ŹŚđŻ(Á^ÄŇu wNm©Ýuřň9ŔŚw6¤źrői±ďb¦(ľ_<0÷0ĐĹŕÍ/¶ču•§ŽĎ/)^2µ7x÷Ľ•“ěŔî“çoäŢľŇ>Đ –ţk«¤sż´äő‰=uUŞ3g¨S’ďäVĺ©»ů*Ŕ­ůá+·Ó®ť;tü\~ćő¨0?NXÂZNÉČ•,ß»“ŘÚGaëv#·$7őÚőŰY̢›10ěüăÓŘéß6B‡7čcőű3!RŠzÁŮxÂ&‡ŢYB_q?=ű~Aöť‚ňŞťźÎ[ď‰w¤dßěÝÂݶIÇÔܲ»7®Ą¤ešŞŠg iŻ.ů>ýŢýĽ´Äv޶6b~YQN[>¤ ŞzÎÔaŻţsźEWTkYS±űź ‘gŤGFFFFFFFćoM=‹$©źzËŢw¬˙¦ąż×ŤcżĆ]ľł‚K`ää~­7,ůŇÎKľ·ě?‡OV­öö©cńI€9 ‚Ť‚ñ˛<9QmČȡR˙ô Ŕ(V(ôY·R~ýY‰kJňĘx‚x…>O}}ű&%®.ą[ękÉV+ERĘDQ´ZEAdŚŠV Ł”R‘Z-˘@™(@Ýx(ţĹ%ě˙ŤoöHS©]¸â“Ɔ›'“ŚJĹc΀0ˇTTúő[ ]ŁO¬”ě,ť9ćřÂŻ¬ÚĘë‰jý3|¬~DVG@ĎťŤgţ~xĽwŐ—×íűöw<ýűťFŻöí8qjٍ0ď/ßX™T_oři^Ě(ď"_EQhۤsLĘѵRëMß-Ľ”~3nŐëR¬ú˝@jŞőf“ĹFö¬úňŞ˛wÇÓ;oyĽÓ“‡¬"}ˇŠj222222223ęYşQ*Ŕťł»ş†Çwî˙Ęć5ď˝:¶ÇšßĎŔŕńcmÍ9;ăNŔŕ ł»)_34‹zý¶mÝÜi#?ܰjW¨HJIŔ™‹/ čÚşÎöóŞA&‚AďŢą˙¨68ŕ˛]Ó'XĚVĘŚní˘Fý°Ů—íž1Éb0c‚™´EjS?¤rQ€Ő% 'Ć#-_˙ň’GfëĎ@-Fź^ó˝ň§Žź0cÍ%y¬yBµ ä %§Ď&€ˇ »Ę*†E­[żÁ[iĘĘ-ÄĚTmĽŰńúbIGźźYa±†uTi5…7“‹@t4›-śk€W“@]yY÷Ů”ŠňluZaxĐG <üZu\˙ŁdąYőzÎŢ+ °đΕB¬ľSZˇĹ¶.^Mu嚇v €©ŕbôł}¬ÓcÍžó„p€P*ťXkx6žůűáXąF§µh J"'ňÎŢMítš˛î1ł*™7Ő­oHóâ;×Ęč­Ôű_·˙Ł"`L8Ŕ̤hŚ(×č´¶Z3Ş&ŤGhddddddddęˇţ҄㙠V[4'b/ůîÍZś…çä—÷­»Ł±€ăKuîž—ĚŚżSai ]+ÔbµX)ĘďôŃK-9gţńýˇÁ® €`©Ŕ/O™hĄLD<‡„GL(Ç×®í L&×±µ\'a Ç#‰­Â_[ň`ş^Š´%nN.ßľ?9ţ–eu ĎAťPťai/¨$żśS96ö‡ě;„çŔ* 5«1Îlmô锑§ŕIIľ†łu’tsV*•KFPƨIoÖ,şÜ‰ŃQĺpĽ˘¶h«-UŚ1ŁŽ~`ąsVĘ!ŢjÔ¸†;>~.ÔPcÖ,ş»ŁűKv0b €wőoŘÇžđç!çý» ^˙őʢË;f.ý§đĽŮ¨Ď[D‘‚`5UU‰>€ŠújłŽZŞscú÷/ŔFec6šç|0ĚŢł1 žŤlmřšőf«Él6V°bŕy{€łW,Š”2öĐ2Qř/Ľx™˙Łŕ'ţ–r ¦ĽţÝíÔ++?üřPÂńpWóŮóg Ó)Űzýşk»¤™tű˛sPź-ţýőúÍ[yťşz V~żůË·ć»x4ÝżpÂPP6n·dńÜ9“F5śÂŁ‚j­&+cŚR“Y)µR«I`Ŕ(5ë™…J»ć-Jj%‚(BŔ¨€ůŹľţńË7çąx4Ýz,ţő±Ń‚`ýôűÍ˙’FřW”ÄÇż>~E`&‹Č^˛k ‹ß“’§éŇkâÖmżz™îm‰Ť‡şĂ`ŔEšäc;.ÝűvÓáĺ‹ŢÜą˙ÔŚq‘©©×=üÂßwéćíëĽÜěś\XÎń:ť%»bOÍŤéˇ3ľŤĽ8ěćîĺíˇ8´{łÁµwÜ®­ ,>uńňĽ)€ăj÷C¤(ë~~–‹oŘűď.ÝĽ}]€‡»ł tßN«K׸»¶o^馢 Ůű‹ţQ;S€m“gúX}ţl:sö 8ąůôíÖ=˘5Çă÷60OQ{G7G;•­˝‹«ŁJ©tňoę÷ű&˝kŻ#»¶.\°8áBâś =wŘnۢ˙¦ő˙ްöźÁMaL\—Ľűáňy1Ť›,˙ü«×§λˇ¶85ý`ÝOë×ţËßŰÎÁÉũֲ­­­‡›Kí}ĺbh22222222Ońä^ŤÔ$äŇ©=Qx·n悳ă>ůa˙ůL`Ĺ™ď.Y{>Ŕĺ˝?Ś™X=ű•±ˇ`ť9iĚ/{ÎQdgŢ>T–Yˇ3z4r/**SaŇŇĄËÍůÉV€’A€6}önű~É;˙PµîŃÁO…©YľĹčiŠfžv¤KÄčPBMâ[Śś‚7µš­ ÓFÝ(-Jf1Â#ÁhnÔm€ŤŮ†™Ť€0PńîÝ;‡~Ď«Đ<ykŕnöťşţ%%ĄĹĄ0÷Ó—MŹÔ\ßAÄ?Ţ—ž1Š1˘…ęQ>ţčmwČ™0üÓ›ů5㇝dc«ysň{‹5¤,ăňµë·od%żŮŇuÜŕÁWOmź·ű§›Wł`ń¤áŮ‹ 5T“yéüĹŰ ]ľâÓ"€~ôŢŇ›Ią–’¬Á†-Xüę‘n];uţ<¸—(PÝôíRO2ˇÎrę­cAF˙¨á#Gv1¤¶l<ŰÍŮAź~Ř óŢ^kç\ ď?ÓÇęőç}ç3 /őüŇĺUf_±2@‹…·šŤ§¦P:=÷Ë*Űüë¨Hw»"˝ÔŽ–»äf^>hřoÍ1rĐÍł/§äVĄç7gĹüń™7ă—ÄmIL+ Dˇ*L=żâÜQ÷FŤmUŽ5yqŻÍ|kŮÜ‘†äÓKďKľyGU˝îvyZ>»/Y^Yî,6ř ’‘‘‘‘‘‘‘ů;ób~kÓVBô<Dĺ%kÎ"ŚĄMZ´ŐëŠv輋»łˇŞŰ882«Ad ^MŐ:Xéŕ‚Él˛cĽ˝#FÔ\]¤~…”ňöŽD«Ńd®©Ů8"ÚTS ʤWK÷zúî! `€#‡Ç„¸ŢU_;t˘ÁzŮ/ňZë˙ ž-ŻçvĎMa1÷xh!ĚU9…®Y÷QâńŘf]FOéŮ­ŁşDŔŃ—i:úĽá"Ścđ’IůO?é ™zŮw(########ĎĘ«Á„ă˛X¬T۲]ę@#Š€0B쉥c !ŚPíxŽçˇ6ąq„F…‡GĹCZ˛ç§ßÚˇĆvvŘj0lB‰3i+s,‚ )ěěPsu%cđ¶ĺ—_~Ý›SRY»ÎŻÇNC>†ÂţˇíŁúôňóqÎËËŻ{RFk«;#ŔĽch›Đ¦M||}ýÜ]H™FË^`'!ŔK©<#Zű¨OŽ!L0CĽÇ´×^aÚ{%•J©[Př´ńCsŻ'×!ŚbŚ2ĆÄ`őY–‘‘‘‘‘‘‘‘y!¤˛˛^K>YťY¬ą—z¶µ Z šW¦»›ť]^Ą;·çßöŁç­ŃUŢÍÎĘHĎĐtĎ~'®gçç•kŽýôµ3ŔóűCÖváśýNAnjĎvÍÜ˝'ŻúŘÖA©rőPŘÚúő1ďÜÍĄ.¶něččÚ´‰[«‹nŚo˛w·! Żĺ.KşҵM§OöLý|¶gçA’¤e÷đ¶K~TWŇÎěVtýzµ`Xłtĺ‰KYWüU%Łćë]˝ŕpkŇvýˇä;§¶qŇ$˙á ré»Ď×ĆVVh Š b×~áP˙űB [Ż­>]§ mG˝®-»×ŢŽ©)&zrŚ/2b„!ü¤ć´#Ť'$zĘď=˝ó;ňŘŁJ=@mCÝşW–—›sż¤ü晝¶ü‹ąď“®g„!„0Ű6ąeÚEc:J˙Üz†úZ¸-zŰÓ>mYFFFFFFFFćí˨Cë–&ϡÉç¶ÄÚąn˙U^é5hh­Č>śĐ`šjMâő,ććĂL5ШĂ+r4eoyebŇůěÎAůwŇ ŻŚ÷Tć˙ů6S¸Ľ3¶‰:´w’ún=™6ë=´SxČË´i«đ±#‡”e©ËőÖŚ—2ďWŘxNš0ćˇÄ{ ]ŻŹ±§˝7~Őđż¬;śBđcOĘÇY«–L|,˝ÚšÍµ•ž=‡(˝»„·°a–üÂŞ–­šܱ5Ł ĘĆ3pŇř‡#€¦­ÚŤÖOSh¬*-1[)„uî ř÷w_ĺ xď A]ZťŽŤ­˘üŃĂÔW/Š.A}:µVPłd9ö·-™…Ú—Î^“‘‘‘‘‘‘‘ůżÍ“QŤ´ 4fíÖeżéK¶užE©šÜŰ nˇ­Ú´lßTS”_^UÔqë†E„ůýFç'í˝\jśďÝônęů´|ŁX’”]ˇďآí:¸ŞîúÓÎmÚł[Ź%\674Ž1!ĆáXueŮ­›UÚëoăěáŚUZŢÖ®ş´DĄ(ĆJ•˝ł 01”ÜĎL·MUQ‰!p†âüZIa‰·ŤŇ˘­¸«ŃŚś±Ô;Đ)+»@ˇRµwď™FÍükJśrr 6\íl)¸—®ň‹Ą=ĂşUf^€5’ť×ĆŽ9~#Çľů3}L:±V~/-ˇ°Î{‹óËĘ5Ŕ¤m•‡•$±u]´ěłľ·Ň·mţ©°˛˛ÁaÄ(áłV|4.ä~%ŘC 8{wr2NřâĐîCqÍpYv%Ě~mň¤Á}ŻĹĹŮkďiN!Ť¤P*˘ç°Ą‹G© …w,·ZtŠŢ°fiT\l Złń—OM˛´|mý‚.wËD{¨'Ď.îtܲxиFFFFFFFFF¦\Ż”‰Ř(8Ś@:UĂZö¸óŕń_˙őN‘úrÜĺű€9‹.‘“űµţ}ë(+ąŘqČŕîáb¦4óq1­ V•''ŞS3rj 4řˇYdŚáEMöŤ/®űIkqňô˛wěî˛wÚŕŤ_ĺ»…ů "ro_ßľéÚoż–ä•qő×@“öĄ`ŕÎŮ]]Ăă;÷eóš÷^ŰcÍďç`đř±¶ćś­űŹŔîmëzöéĽ%öÄýÜĚŠ˛2Äs`.ş4 kë:»ĎY‰K‰ÝĚbäü;G¶ÚÝÁtxÎää]ńŞ~\Vzńm‡ÝHÁîĹo%Möć[ű~Ł~Řě€ËvMź`1[)3şµ‹őĂfG\¶kĆDsŤAéčsńÜŐ.WoĽŕÝ̨ÂÎţîĹsWŰôú+JnĽŕ9¸­AuSőg—·Ôbô é5ß+ęř 3ÖP’ÇJG „@P»@ŢPrúl ˛«¬B`XÔşőĽ•¦¬ÜBĚLŐFÁ»}Ż/–tôů™kXG•VSx3ąDGłŮÂąx5 Ô•—uŹ™=H©(ĎV§ˇGŞŽaŚ)ýZu\˙ŁdąYőzÎŢ+ °đΕB¬ľSZˇĹ¶.^Mu嚇v €©ŕbôł}ŚRžđŢ1=Öě9OĄhú̓㆟+˙ŐÎô”¸ý»¤l8, ¬ˇßÇĘ5:­EkP9‘wönj§Ó”uŹ™5P©¨ČĽ©Öh}CšßąV@oĄŢ×čx®öG§"`L8Ŕ̤hŚ­Mm#P®Ńimµ%fT Lz;ňń3™zŕř7«@-V‹•R O±Ú˘9ű{Éw´hÖŕ<(<'ÇŚ¸Ľo]fĄ Ę»oŚë÷¶Š÷jŃ.!.öniظľżl‰%çĚ?ľß!4Ř•Ł¶š Dl”J'{obďîTd(¬ĘíĽ[6ďŃÓ‰Óřř¸—©ďů4±Á(P&"žĂaÂ#& "Ç#`"%ÔR:>˛`ŰźĆĚ·XĚ”j)ß·ó_R‚”?OXj±Z˙ü[—Ş{iKÜś\ľ}rü-Ëę@ź: <(§&mc”ä—s*ÇĆÁţ}‡đX…Łf5Ć™-‚ŁŤ>ť2’ă<)É×p¶N’`ŔJk‹z BŁ&˝Yg°čr'FG• Ŕń ”« ¤–<G?°Ü9+ĺo5j ÜĂťD?j¨1k Ý݉Ńý%;1Ŕ»úôÁŇ|Śp<{Ä{CZ„ś÷ď2xý×+‹.˙öęâ/¬€p]_ f}nýłÚ["ÁjŞŞ}őŐfµTçĆôď_.€ŤĘĆl4Ďů`˝gc@<ŮÚđ 4ëÍV“Ůl¬`ĹŔó6ögŻ X)eěˇe& žÉqŤŚŚŚŚŚŚŚĚS<} €řŹżŰřĺ[ó]<šţzňřä~˝ÇL]yçöµ•~|(áx¸«ůěůłĐiÄ”m˝~ݵmŰ8˙ńŇ×·oŰĂ'˙~ŕ$p^ˇKĎť3iTmÝ©g,cT°J_¬&ć¬ËGß[°mŇč+‡/ůwîçŢ*T{ýčďo-Ř»dľ:)«iďP‹Öhµ Ą&łRjĄV“ ID“žY™§Ł»­”…B©“›»‹Ł»›‡­ň/+aÔŮÍĂŮ޶vÂL‘=«ĂĚs "€cń{Rň4]zMÜşíW/Ó˝-±ńPw Ł@“|lÇĄ{ßn:Ľ|Ń›;÷źš1.25őş‡_řűď.ÝĽ}}—›ť“ Ë9^§łdW쩹1=tŃ·‘€ÝÜ˝Ľ=‡vo6¸öŽŰµuá‚ŧ.^ž7ep\íîCÜĎĎrń {˙ÝĄ›·Ż đpwváŹîŰiuéw`×öÍ+ÝTTˇ‚#{Ń?jgę °mŇîY>Ćq¦ĽţÝíÔ+uŢk:sö 8ąůôíÖ=˘Ť‚6eáw7ŻśřčíŹNžÜĺbÎ;zâ2@móĄú¨˝‡Ł‹›ŁťĘÖŢĹŐQĄT:ů7uŽű}“޵ב][.Xśp!q΄ž;l·mŃÓúoXűĎŕ&Ť0¦®KŢýpůĽĆM–ţŐëSçÝP[śš~°î§ők˙ĺďí çŕäâTkŮÖÖÖÁĂÍĄöľň 4™§¨ż_ ä¬ÉI=|ôd±¶*ëzRJz–§{ë¶m-Ą)+Ţyc˙™4ŕăć‘uóěößĚ €`5{5ŹěÓďţ•Có^_šQ¦*ËËJŹÇÇ%«łëýĘ,}#éŘ}Îkł:{\:qĘoěT;g{f5c×ĆÝß\ŇĄ…Â=ŘżŤźćŽÚ`Utz}y›ÎˇfSŤ˝O Wł ”qJסÎ*ĄGSďŕ ÄDŕUNAiGvś9™PTQ„ł ęä«×ÓŐW“ Ju˙mIâŤôÔkIWî—VAVłAťt%I}3ńęĺBM Ś5©×®\OKý“ŁQŻľv1-Żxî§ëWĚe,V˙řË~0Bô>ĂQ]驳Ií˘ú»á˘Ąs\ĽS0~¬%B–GâĚ ç¨˝Ş˛“vďŮwőÂĺJě8p`ďŚÓ{·îŽ˝šx-ż¨üÄ‘#&Ţ)j@/mNŇöí{ŠŞjŞŠr/^ąŞ§BMuYâĺ+ąY©Ç®úFtčѡ͝¤3»÷.-×AÝq1):KżqŁě ę#Yľ~=%'ýúŃIŘU•}AÜľíÍÓ{/$^9qęšODÄ;%ĺZ&5Ą%Ďň1ĆŔh4Ú:;µ ·”¦¬xgńľ3i XM%%%ńG㮫ł)ł@=›µkײôú©·˝™QřÜ‚c Ŕj6f¦&ĄÝËĎËU_Wgćĺgź8yěXÂŐ&;vŹhťvíÔţC'ďÝN>źZ40:˛(őĚŽťż_¸x±¸’ôčŰKĐä>zRPً՚S GndUöîÝĄ0ýěÎÝű.^ĽXPXt7-ĺvn^^®:ĺvVvöík)iň>ŤŚŚŚŚŚŚŚL˝Ľě‡ßGŠGaÂA=­â_´-ÂQÚ¤EëQ}±®řp쑡űÜ7fŚ"`}ŤŐdfĽĘž·á€ZL&˝ž¨xĚ55Ś!Gf5LÁ+°ą¦š2¤rv1U”üĐż·Őhxt¨˙ď§— ˘ř_ë ŹŕČá1a®wŐםh°^ösl=63Ďš§§äőÜîąsübŢđĐ2B1Şr ]łîŁÄă±ÍşŚžŇ/ ˛[Gu‰€1˘˛#ĺ ¸Fľ”ŰÔőýCw—~‡222222222đ̨aĎ V+eŚă9©9ĐűÖ•Ë7sň! Ś>˘BĹú×â„p˘(DŽźńă·˙¬Î8?}ÂŚŽŰ( ¶L€9{ĎF­&“©ZG‘abďéÍaj1čÍŐ5T¤@8/$Z«Ů¤«Ő±óôV`†mlĚ55T;ŻÇěHQ‹ˇĆTSCŠoďݬ&†¦VMVş•)<‚[płTë¬V¦tt@@ÍŐZ¤°çŔj2‹*G˘©şFÁˇńđµ=1äĺÖ(nŢĚI[ięÓ˙wŁĹ>zp´Tź?YeţźăF˙hĂďó†vĽrř§±3?´Öö•y™wŹx›î˝ű:pć„„Ó&Ë3%B8ŚcTEĆ€"%Ż<·č€Őju›Ő!vË–"qŚ µá˘(Ňgĺ«Ôjwľ}¸ ď­ü¤™\# @“î1+Ţxĺ‹ŮQ_üz14<˘bö@ç*"€b…ÇĐáŃ´"űpB jŞ5‰É™˘“'F‘y ýať·‹~ďŚé¶ěřj[*zŚX·ŮËĄfďkÓ.lÝÝ'Ę ĽŰż˛u«Ł)kË+Ô="(zŽ\÷ł—KÍ«5Ş˝jĆôKŰ÷ |µ- ďv.˙ş·O”öí4ĄÎNĘĄÜÎAů¦WĆ{*ó12óć};ž &Ć(Ł+PŽ`ŽZ)Ďs幥?í*ţló0Ż{éŻ~tŁu nŘÔ łG´ťŘ˝őUˇŐťäCÓ&î˙|ŰqBČźů6S¸Ľ3¶‰:´w’ún=™6ë=´SxČË´i«đ±#‡”e©ËőÖŚ—2ďWŘxNš0ćˇÄ{ Ń€Ź!ÄDö¤g˛Š/>OţXbŚyůµ3j¨Ł U'_:xâ"X >X0[ci2+Ě*ÂŚ‰îM[Ž1ĉîů-­Ň~ܠΗŹîË׉€yźQĂ»^? »Dř˙3LFFFFFFFćŻĚ“Q Ł"3Žv벿ßô%Ű:ϢŚ€@Ůđ9+űň›;Ű=Č–DvéYSaÓrđŢ=K~ýń_i)f>·V§sS;Ś((›uůiç6íŮ­Ç.›LÇ` "ĂÄÖ=(ÄŻĄŁű÷ŞŠ(k‡‰­[łż˙ö-ň* ö„šŠ2őÍŞŞdAaăâĺ* ZÄŮ=ĐyúŞçŰámś=ś±JËŰÚU—–¨ĹX©rp±ŁŮˇÚŠşH:…¤­>µ+”|öÍB»ĎľÁĘżľĎKNŁ‘}{e'KÍ3ŠE ·ň»‡·†mÇößř4/­ĹÜKWůĹR€žaÝ*3/Č'ż›D©©mópKˇĐ:5  ŢübăĘůĂŻ\k¶ĐĘľ;FX»ş0ő2qk>oĆäŃCŁ“s´K˙őó's‡&^ąÜvo.Ř“Uáęşü‹js༩»¶»hňß{x )Ď®„™Ó'ľ:fĉ›yc)Ga¨č˝úűUĄiW‰[óůł¦ éŰ#‡ ‰Ť‹wŞÎŻ$݆7óś9ĽkfĄăŢǰF˛óÚŘ1ÇoäŘ7ďÚ€Ź‰ÂCďíÜÔ®nÖ¤ÄţťŤ'ę€CŔĎ»÷4W–çVńGő>}aDµ‘QÚé@9ßĐ݇·PV•PĎůÓGŤ[đź/W­űrćťőq·Ŕ·{ôşu˙Ňĺpv‰€’˘ÉČČČČČČČĽőwád˘6 #@ŽM#>;ćĂc{˝Ő V3óiÖ®čí[:q÷‘“ŻDµý5EőĹ»“>ś>Ú±×ň>V)1A¬Ô$'Ş 9@ęźŢ”0Š0/šňO~úćy±4őčĹŢź-"LĚ źľy^(I=z©Ď?ß$¨PĐÜ˝±cŁ%7©ĘäĐČĎs©9żNç髞oGkqňô˛wěî˛wÚ`Ě݇µ Ě>g®ý®˙č§tĆ€H hV hlłă—Ł—çPS‘ Ů{{ŠŐTTVn rAŇüéěšZ/T`îy¦4•Ú…+>il¸y2ɨT<ć JEĄ_żĹsÇ®~cě˛MqÎţ-\p WNž<µ©—“˝sČ7ß,ŹěÖ:ąĚúćüń«ŽZ¶)Î5¨•Muľ}čŐ4sĐo)Ţąy ÁˇJŻ #|?ť˙Y˛ľ\űăŰ3ÇťXôÍAj’cĚĽ0mÚtog{çU«–uëŃÄsl_ҲMÖi\ęń U•ú¨±Ó´kôé‚•’ťĄ3Ç_ř•U[y=Q­ŻĎÇƢ@íýÚ×yďň>RS‹XŁçÎF}“‡€­O뎭›n^¶äťŐÇÜśí fD0iíŰ‘NžŤ9=˘‘еe¸Ú’‘qit'×KéŮŤÚź9ąźtUđ­(I;ťk€—«ş&########óŚĄ›´§Â`ú˘O›»”MB#Űű©<<†t W)8Cńť91ÓÔ>¤KV‡H›N}‚]-Ş&ˇ‘훪<Ý'ë¶ýŕESŃĺ][×Ů}áU˘ĚjfvŃßmŹę¦K?ŽP«‰ŮzD»©Ý ĄÇ×ň]Ł?[ínoŚ›;%ńçÝŃłÚjužľęěž39yWĽŞ—•^›†‚ě*«µnýoĄ)+·3SµQđnÄë‹%}~f…ĹÖQĄŐŢL.ŃŃl¶p®^MuĺeÝcfR*ĘłŐi…E†cJEżV×˙(Y.BV˝žł÷ ,ĽsĄ«ď”Vh±­‹WÓ`]ąćˇť‚`*¸ý # ŹyďCĎdóa”JQbĂłń4ŚR`H?ż~ăńŮîňžîč/k^_öĄ ‚€´]é¨tÓfŢ+ď&g¨ŚčÂéśW"»µoŇ»ްąÉĺ'ť6Š•«śÉČČČČČČČĽ }¶ ÔbµZEŞ*îŢĘrž5®««qj4~xŻŘĽRADŇé«Uŕ”ĘĘŠÜÔ¬3çĎuuuŕ<Ç ëżăŕEćÖěÓošłĎüăűBŇ? „8[˙¨OţíŐHa¬Ş,fĆDŢÖ?ę“Ő^ŢĽ±˛R´abŁT9ąŘ{ąŮ»;WęňD± /]ĺ]ßU/dÇ©ČPX•-Úy·lŢŁ§§ńńő(»uĎ/ÔÍhĘS8»Úa fbŁŕmŤ¦\…ł«¶2ŃŚx…‡·mY Ě^Öˇ[ů\mđ"eŢtééC 5jäqí°0†?]–™=ö_/i@‘¶ÄÍÉĺŰ÷'Ç߲¬ô9¨Óz`ź1(É/çTŽŤý!űá9° FÍjŚ3[G}:e$Ç)xR’Żálť$Ŕ€•Jĺ’"”1jŇ›u‹.wbtTąݍ-ÚĆjń3Jaŕč–;gĄâ­Fma[`¸3€čăçâŕ@ 5f­Á˘»;1şżd#ĆxW˙Ź>XjÉ©ÇǤ­´ŞŠÜ[Y.łęuńňĽ)öl‘˘¬űůY.ľaďż»tóöuîÎ.üŃ};­.]ăěÚľyĄ›Š*Tpdď/úGíL¶MÚ-Y\żŹ‰Řö/ŢčŢł×CĎśő1¸zřFvëŃZĘĺ:ż·Ů¨ç%`ö­‡ś»ruŮÜIZůYµĄ…ůŐŇżŢJ>ݸă÷Ě‹éqlĎŻ&§u?˙gŐĎë:ą‹GOžĽ’v×ŐŐ9óäÁĂÇn»zx¤]»>Ţ•‘‘‘‘‘‘‘ůŰňŚc6ůkrR=Y¬­ĘĽž”vŻRĆłZM7®&ÝH§şű g“ZGőtáV._şĺŕe$"°ZM7Ż]˝‘Ψ`,/+=—¬Î®÷»7ĆQұűś×fulîqéÄiżŃ“ě\|}ÂZ)DZ¤"@Déć۶µBAjkcŽŘp Z±«o÷7?íŮV0­G­ÎÓW˝ť!]Z(ÜýŰřiî¨ VEçEËC#‚‘­§űVšô[fjŰmńr˙FÄÎłiť¤Ë’÷Z6 {vrËżSa˛uxkiDSwŰŐß9uöf‡ÁQ¶şeóşžÉ)8 cŚĄ¶˘/Sđ a‘ńs>^óáÜQĆbőŹżě#ôG 1ŚŐ•ž:›Ô.Şż.Z:wÁĹ;…cöxUˉ#qf…sÔ€^UŮI»÷ě»zár%v8°wĆé˝[wÇ^MĽ–_T~âČď5 —6'iűö=EU5UEąŻ\ŐSˇ¦ş,ńň•ܬÔc W}#:ôčĐćNŇ™Ý{—–ë î¸ťĄß¸Q vő‘,_żž’“~ýč‰$ěŞĘľ nßöć齯ś8uÍ'"âť’r-ŚšŇ’| Xm‹QÉ3o¤3Q0—””ÄŤKVgĆTWňśŮ¨j˛Řşą÷ěëccřä7%Ţ„X¶:1#OĂóŠň»ég.ťKHTwëék',_:wb«®®ĐjvÇîMR«KsÔžŃÓ˙B3#™ż- 7!„1f˘HĄŚő'Wĺ’LÚGřËu„1Ł´I‹ÖŁúb]ńáŘ#Cöťôhěm¨¨ ¬öł;"ĽŤ˝Ň¬«¦¬.ł›SŘ88‚Q‹ľĆb4aNń¤ÎÓW5l§¦Úb¶0^ĺŔŰ𠚍fŁ 8Ą­BĄÁ¤·ÍŹJ¬&#*G˙ˇ"f;{ĚbJ+-ĂcNë Ď©Ő[[Q­iÚ–Ł”áČá1a®wŐםh°^öóŢčŁ{ÖPëëWÉž§SĎ“ľ€S<´,US9…®Y÷QâńŘf]FOéŮ­ŁşDŔŃ?ŢtôIď­G#ĆŘĂĽT‘eŔ´%Bý¦ü-˝UđçKHČČČČČČČČü˝y~TC81Q)ÂHútM8BE‘1DĹ'ŹT=ą~E#0*Ď;|…0¦°ł›uč¤ŇÁ©6FbLj»Ž!B‚G$Re2ŚŃgčĽäq;€đcŹŔ¤čQŔa€:I‚ a– ëeS‘0 ˛,;veS (c€0bVcůfoĂc`V 5[Ĺť»3­Ö˙˙Î!„1˘"LpívF˝Z‰˘ F)é˝cB0B”Š”˛§tBG$Ś &RJÇXOd‚Áµ– Ƣ`ĄČuéGËöŚ0isVúűë™R_Î'í<×ÇB€Řăö„#@_Ŕ3śCc‘ŠcĎŚá8Ž1*Š”2Ś‘XIÓÂŚEAc™?ŞWÄöoŐ.˘UPMeÁé3—ĚVŠoÓ˝w_{b>uę´ÉB1ĘpHçíÝşréfNHQĆ!m\ •©y÷Ąę·„*>#šN…Čń3~üöźŐç§ÇĚŚři‡’W˘âvŽĚF] ` ŔţßIü"ÂŰ9QD µDlě]ťŔX]#Ć(VŘ)Éxôµ=1äĺÖ(ně\QŞűZЧ‹; €f÷33j8űA~Ôj„ź–`ĚJďfÝÓTĽaßܡŻţiěĚ­µ}e^ćÝKďÔ3'$ś6Yž_ÂIĎ!Š"c@‘¶Ź¤pĺQ°Z­Îa˘:ÄnŮR$0Ž1AjJ¤8D‘ŇgäÔjôŚëedddddddd^’'+;3*@Yúµ˘Ź'\ŠśźvâűţÝVşľ5wĐś.-5ˇSb7~8bÇşŘĘÖż9ĺ‹™Q_üz14<˘b xoĺ'Íśŕ*"€b…ÇĐáŃ´"űpB jŞ5WS˛¬*{Ż>ŻLî=Ů8čnÜőńĆĆÍą:Ť›ň{ÎŢU—űżPâŰĽ‘łÓ„Éżx ńiáëć4qÚľAś˝«.ĺŔŽŹ6µčѦřÎo/ţěçˇ^÷ҧܡ]ô±=_ś=´ÁŞ+í0lÚĎ˙XzóÚĄcy§{ŞT—u^Źdă?–^8q4ů7Ňs4˝Ú/÷}BEˇůŔ%s,čŞ ť»qĹ„;~ŠK! Źěx Ä𽇍îÖ¬ů6S¸Ľ3¶‰:´w’ún=™6ë=´SxČË´i«đ±#‡”e©ËőÖŚ—2ďWŘxNš0ćˇÄ{ QżŹaL(›v˙ᓿ]ç«bíÖĘcČ8ÎZµdęŕcéŐÔl¶J†¦Qz/ż–cF u´ˇęäKO\KÁ fk,Mf…YE@1Ń˝iË1#†8ĂÁ=żĄUÚŹÔůňŃ}ů:0ď3jx×ëçd—/™Ě########ó·çÉĘÎRŹň{i ç2C[µi×Ţż˘$/ݰ*˛]ŻŠ¬‹ňÉk3'›űµŃYW”iÓrđŢ=»[záJنĎ]Ů?ßüŰY{G;Ś”Íşü´sŰw+ć×~ű~ö§iŚ &Śa…­­‡·kc÷ĽS ^x7u§ŔŰ{4ú‹JňO%x¶ŕעQúŐ{ö-˝ú+wîľę =5hłď¨ó Ë:ő“—´˙d†L†;©7óŠę—ä'í?žV‚1¬ŕĐË~čÇ@ϰn•™µď”RSŰćá@ŮXŔ7˙ą1nűşč>}V|őźWFöďn?¬]Ý-"zÔĚö¶t˘–~ůs­ÎżVOŤéQQ^áęşü‹&M›·s˙înľ pkľ÷đá·§ďŘgŘţý{ŁÂücŇÁ-¨Ű-q‰^ýý*ÉňˇűZű(lýÂbăâNµäăŻíű­WëF`ç˙¨ťţm€]ó®Ďň1é‘]zUf<đŐr“bŹďC!Ě­ë˘eź}±t~SOńyAaĆ8üĽ{ϒ׆÷2îł•ď9¨ ĄŤtä Čů†î>|äŁE“'ÎűŕČÁ]۶˙rŐşÁ=ZJv|»GŻ[÷}sűş}-™?N= ďĄEsËŢw<ţëżŢ.T_NČŞ˛łS–VT.\ńIcĂÍS×3U6JłIô l×?´Iiµ¸űČÉľŕÚę‹w'}řĆ×îTÄ*Ąi•šäDujF` ç)HYŚ Xźźšz%ąů¨áÄj ”2„őyIÉÍ+×G '‚ÁbAŤmvü’bôňŘßG[-HŲ\‚"'÷k˝gë€W‘K`ý’ß% đÇęĚ= ¤©ÔJďôdR–RńŘĆ„2Púő[Áˇ}Oá»ăǵë×®­v ~{ć8x$‚’ÚÂ3/L›6]˛lۨE·Ž˝L ŕKş·é0dú˛ #­ŞÔGŤť> ]Łv–ÎVmĺőgú«™ú4«óŐ¸ăS†c„`öČ$bŃ”š’¨Î)é37îřˇp7Ćnŕ%B`ëÓşcë¦ű6~Öżw׾&̵{”’aéńFŚśŃHčß-Ľ]§ľ–FFwr˝”žÝ8 ýđ™_|=o\×`ߊ’´Óą& ňNŤŚŚŚŚŚŚŚĚKÁ=-˘T€;gwu ŹďÜňÖ—MŚŚĐ••5n5jľç˝©ă'Ěůń c…ÂÖXvgNĚ4µ…é’Ő!$ҦSź`W‹ŞIhdű¦*O÷‰Ăşm?xŃTty@×Öu¶źłj«]BRĆŰŰeď>dĺBC"ZYŚzŕíír÷ü+J.4$˘ĄYŻw°Ud_ʸ›áţýwÝű÷…J 32~¬­9ç× T€ÁĎüv0Xí ±?ű]źZŚ>!˝ć{ĺO?aĆšJňXó"„jČJNźMCAv•U ‹Z·~·Ň”•[™©Ú(x·âőĹ’Ž>?łÂb ë¨Ňj o&čh6[8ׯ&şň˛î1ł)ĺŮę´Â"đ  ĆRŃŻUÇő?J–‹UŻçě˝ ď\)Ŕę;ĄZlëâŐ4XW®yh§  .F7čc Išä«Í:fFµëąő¨šaÂc¨(Ő@Ógž7üŚ(XůŻv¦§Ä ěß%eĂai`őÎŁŇĎŻßx|ö‡;¦Ľ§;úËš×—}iÄ `€1GĄ›¦0ó^9€x79ł@eDNçĽŮ­}“Ţ-p„ÍM.?é´QŔţ+MZedddddddţ~ŕzĄ„ă™ VWkNÄî.1óˇÍ›hň]ěť¶}˝,ţ–%,ĐWS]Xl®˘uÍí­VS*++rSł43çĎ oéŔ9ŚÖź·fźŻZűáÂ)Ż˝ˇ°Ú|Ć0ljşÇâ|űptâEć8Q{ăŘ‘ż¦$ÚŮ…·E»-6ŁC·.ă–λ±Ęj08OŽqyßîĚ*Î*€]áXÝt˝4R9ë"m‰›“ËÖŻ—Ĺ߲´ ô)ŃiŐ–vJňË9•că` <FÍjŚ3C[FO[¦łbOJň5ś­“¤*•KFPƨIoÖ,şÜ‰Ń}ştëŮ»çŰßěbŔjńRt7pô#–)Ď[ŤÚ·Ŕ–Îv>~.ÔPcÖ,ş»ěĽóínŔ»úö]ý>&=ią±BAÚɱŠT*iŕßyĐŃs×6óŽtnMę+i°ýŚśE€PŐŠE#ýű˝ţŻÝ1‹—ŹŽlZ»ß¨·PT[ˇMÄf{GwG€śü}˝ čR®ť Śśŕ-Ţ˝mrš4¬Ĺ‰ó×€`ągŤŚŚŚŚŚŚŚĚKňdTĂq¦ĽţÝíÔ++?üřPÂń0'ăµk—®ś=zł ˘s݉[·ýęaČÝ—pęě©řűb“MŰ6~˝ć稷¤Űg÷˙řaŻ=ztëň[˘–_7ű@ĺÓzńsçLEHU`ŚQÁ*ý^eW’źŁ6´:D4Ö0^eWšt,űŻ*JMz‹™¶iÜ>P•pH=mĆńR{ÇÝÜËĘŰÓ„y˙şk;đ@űQÓ¶őz–„Ôqf&‹Č^˛× ‹ß“’§éŇkâÖmżz™îm‰Ť‡şĂ`ŔEšäc;.ÝűvÓáĺ‹ŢÜą˙ÔŚq‘©©×=üÂßwéćíëĽÜěś\XÎń:ť%»bOÍŤéˇ3ľŤĽ8ěćîĺíˇ8´{łÁµwÜ®­ ,>uńňĽ)@:€u±Çýü,ß°÷ß]şyűşwgţčľťV—®qvm߼ŇME*8˛÷ýŁv¦Ű&í–,®ßǤňĐq±ż@ÓMŰ6~˝fÓŔ–® ×Ď€«»OßÎaÝ#Ú(8`S~wóʉŹŢţčäÉ].漣'.Ŕł*PC].}ë!ç®\]6wR‡V~Vmia~µôŻ·’O7î8âŔÁ=óbzŰó«É)bÝĎ˙YőóşNîâŃ“'ݤÝuuuÎ^óáÜQĆbőŹżě#ôGżň3ŚŐ•ž:›$˝ÓĄs\ĽS0fŹv‰AŔrâHśYá5 WUvŇî=ű®^¸\‰ěťqzďÖݱWŻĺ•ź8rÄÄ;E čĄÍIÚľ}OQUMUQîĹ+WőT¨©.KĽ|%7+őXÂUß=:´ą“tf÷žĂĄĺ:¨;.&Egé7nT‚Ý A}$Ëׯ§ä¤_?z" »Ş˛/¨Ű·˝yzď…Ä+'N]ó‰x`§¤\Ë٦´¤~c c$j Nť}ÄWc/2«ĹTRR4îş:›20 Ô#°Y»v-KŻźzkŃ›‰…őTi{ j˛Řşą÷ěëccřä7%Ţ„X¶:1#OĂóŠň»ég.ťKHTwëék',_:wb«®®ĐjvÇîMR«KsÔžŃÓ?·ű&#######ó÷ćĹr3ŠÎ˛O”ˇE€cŚQQ /ÔÂaĚ(mҢőŔ¨ľXW|ř`üŘă—\}šXMŐ5F)ăUöF]ő_NbŇé¶Q0ýpýçvĽEeË#`V -)3ö| ¦Ć‚b ~0źŇZ=-ÁŁ”áČá1a®wŐםh°^ös_ë#ođYe…ź’×s»ç–$~‘áQËaƨĘ)tÍşŹŹÇ6ë2zJż€ČnŐ%ĆţѦŁč)_}ŢŚa„^&w0$íA ‚đŚ›×ý-ýřŘ ŤGFFFFFFFF¦ęŹj0&EŔ#öDËvŚ1cŹ–ŹBŁôŃU &„QĘ„8ÂŁRNösáаq) ž'„MFÄ)bfłĹĆĆć/+1qn!\°šÍ %&XÍ%űܵXDŽăDAxtUËqDĆ%˙Ť—Ź0FT¤€ f”>#ŞÁ#Q1!Ŕ(Ą@–ţÄQ*RĘžŇy`q‘t0&H)!cb=‘ B×Z&‹‚•"ץ-Ř3¤ÍYýéÇ®gJ}9ź´ó\{äI äÝ7Ŕ€IDATG¨Hca µWaB0b‚@ë<üĹ‚„8ŚE*JµźřĄpÇEŠ ‘ĂaŚD‘>`üÄŰ—‘‘‘‘‘‘‘‘ůÂMC#ĆŽ›00Ş» _›Ťăë4fBĚ€ľ]\má'˙Đ1ăĆxD‚źeś"ÇĎČĚ/I9¬ÄĽ­cH/ɢ·Źź‡#Ż´ulŃÄó/-ńtäx;wżIâîŰŇŮÁÖŃÍ;<,`˙Đp_—çHü9ŕŹ7(.*<đÓ§<~ąjhó6=Ł ŘW©Ŕ hÂń<ĎqDJY!„pÇqÜŁ…Ź%žçŔ9 lά×Ůp@÷ ) ÂÇń<‡ń3ď…ëł\w9’„őŮyŽŹ˝Č“ľřlőŘb.8•ďg?ü^ Í8ł]Qkü±é‰1ËáŚŚŚŚŚŚŚŚĚź‡{®Ł"”Ą_›1!úxÂĺ ČůiÇWŹŠüţhJⲅŻmůů·ŕÁo]Řűéľ?lI:;+fŕŃ"ç«ăW ¸eÝá뼍נˇQ´"űpB ô:Mbň-@ ĄT`VÂD0`”Šq eRDF1F’Şă Â`h@vä˛,uąŢšqăRćý ŹŔIĆ<´€xŹa#˘ëő±yŇźŤ'ÁËŻĺQCm¨:ůŇÁÁRđÁ‚ŮK“YaVfLtoÚrĚ!NÄppĎoi•öăuľ|t_ľNĚűŚŢőúůŮ%ÂsÓ“dddddddddęĺ˘ĆPů˝´„B·ĐVmZ¶oZZxŻÚh(NËŘyS(*ΩŃ[x…’Vdś:Ż—t4Ĺůe•`Ôů§ťŰ´g·K¸ln0iaîA_öJ”1Ś1*K€RĆj—ě+8ô˛ú1˘ gX·ĘĚ‹ňÉĆď&QjjŰ<âR(´N (7żŘ¸rţ°Ä+ך…-´˛ďŽŃÖ®.L˝Lܚϛ1yôĐčäíŇýüÉܡ‰W®·]Ä› ödU¸ú….˙ââ8oęŕ®í.šü÷ŢHĘł+aćô‰ŻŽqâfÁX¤&ŚŠ!Ń«ż_Ušv•¸5ź?kĘľ=r¸Ř¸x§ęüJňjx3ϙûfV:î=|8k$;ŻŤsüFŽ}ó®Ďň±†ź!ěEgăIwE1 ?ďŢÓ\Yž[ĹOŐűô…ŐFDiC¤sLä|Cw>ÜBYUB=çO5nÁľ\µîË™wÖÇÝßîŃëÖýcJ—ĂŮ%FH”Ă™?Îóٍký޲÷ŔëżiîďuăŘŻq—ďá Xđüú›/ŤY ‡NÜ€ć=Ł[W«säŇ=°Vh’Ő†Ś©9aĄžŘĂĎâµ ö‡uPÝÁťżłäŃQôJŠ=¤©Ô.\ńIcĂÍ“IFĄâ1g@P**ýú-ž;vőc—mŠsöoá‚k ¸rňä©M˝śěťCľůfyd·ÖÉeÖ7çŹ_˝pÔ˛Mq®A­lŞóíCG(¨ţŁ™~KńÎÍK TzMáűéüĎ’5đĺÚßž9îĢoÄ R“cć…iÓ¦ű{;Ű;‡¬Zµ¬[Ç&žcř’–m:°NăRŹo¨ŞÔGŤť> ]ŁO¬”ě,ť9ćřÂŻ¬ÚĘë‰j}C>V˙“ÖÍ!zîlÔ7yŘú´îŘşéćeKŢY}ĚÍŮÎ`F3‘ÖZ–NžŤ9=˘‘еe¸Ú’‘qit'×KéŮŤÚź9ąźtUđ­(I;ťk’^ęź~§22222222G^(ŞˇT€;gwu ŹďÜ˙•ÍkŢ{ulĎ5żź3ýW›v úő^P$F(íĚCťéŁ{¬ŮsŢZzy@×Öu–žłj«]]2`"Ĺ€aĘ„(ţí%D¤ ?śŞ?›‘A-Fź^ó˝ň§Žź0cÍ%y¬yBµ ä %§Ď&€ˇ »Ę*†E­[żÁ[iĘĘ-ÄĚTmĽŰńúbIGźźYa±†uTi5…7“‹@t4›-śk€W“@]yY÷Ů”ŠňluZaxĐG ›ýZu\˙ŁdąYőzÎŢ+ °đΕB¬ľSZˇĹ¶.^Mu嚇v €©ŕbt>V˙“"Äó<ٵťbžŤ§a”"CúůőŹĎţpÇ”÷tGYóú˛/Ťµ…30Ŕ€‚ŁŇMSyŻ@Ľ›śY 2˘ §s^‰ěÖľIď8Âć&—źtÚ(`ô…ęĘČČČČČČČČ<Á‹Öz"ϱşZs"ö÷3Ň,>X˝ć•H—ľ}Ł“2Š1BpŹę´nĽ{đgß­ýpaŚ”×Ţđ]¤ő¨E¬â9•  Ô†+LBĎŮĘ[ء=EÚ7'—­_/‹żeičS˘ÓŞŰévJňË9•că` <FÍjŚ3C[FO[¦łbOJň5ś­“¤*•KFPƨIoÖ,şÜ‰Ń}ştëŮ»çŰßěbŔjńRŢĐŔŃŹX¦°óη»ďę˙,«÷IKő•ŘyĐŃs×6óôąłQď,2„ŞV,é×ŘďőíŽYĽ|tdS*mé-Qé‹ŘlďčŻ7]ʵ“‘ĽĹ»·MN“†µ8qţ:,÷¬‘‘‘‘‘‘‘‘ůă0L_üźtőŐ•~|čěmf,ě×Â=zňGڱ;×/nß¶+ńÂÉč­GĎü*ëN’¤#VçŹî NmÇč+¸´Ó@*őR ´Á3ßNK9a§ě1®Ô@÷lZýí†ßĄrt'×đáf&KDŁfbd3iŢ–|wTť°ąvÓánŰHUą°Ohň˝˛ó;·nÝsąŞŕVX{xXRąÖŞý÷qÉÚ˘ěĺ‹ŢŚ=viƸČŁß3W—ţăÝĄ[N]gŚ˝;g¬=vC[”˝|Ń’Ç.ĎŤéá×{şIWÜV Ŕ·.5˛eł˘^ťď–[ŻěÝşpÁâsW“˝6¤n•@8Ăg|®Ż*~`yń´H•oĎÂ*ńŇ]/$[őš‰}‚íšôČŃXŘycćpp ű,«÷IŰů;@űáscwĎmWJĺŞ}Z74OĎ!&ŕĐfčą+W?^<çź?îŇgökć*ýkôä÷t5•Gî™ÓĂ;°g^ŤÝňźU›öŇš’!Í]H`?3c;VΛłb3cÖ‘í<€Ľdn /˘ÄŤF[g§6áá–Ň”ßyăpâ]G;Ç»97ݧf誫 ď'_OJĎÉwńp mŰÖRš˛âťĹ{Ϥ€h5”—•ŹŹKVg×ű)cĚ( éŘ}ÎŚY›űś;|47çVâ­¬®˝Ů‹ź}đîď'oŠ3/ÝĚčĐíď.ů|Ĺ{;Ź$Qŕç|ĽćĂąŁŚĹęŮ/FčŹ~ĺg#Ş+=u6©]T7\´tî‚‹w ĆěŃćXN‰3+śŁôŞĘNÚ˝gßŐ —+±ăŔ˝3NďÝş;öjâµü˘ňGŽx§¨˝´9IŰ·ď)ŞŞ©*Ę˝x媞 5Őe‰—ŻäfĄK¸ęѡG‡6w’ÎěŢs¸´\uÇŤý§ô7*ÁnĐ >’ĺë×SrŇŻ=‘„]UŮÔÁíŰŢ<˝÷Bâ•§®ůDD<°SR®e‚QSZň «çIϧÝÁj.))‰?—¬ÎŚ©®ä9łQÔd±usďŮ×ÇĆđÉoJĽ °lubFž†çĺwÓĎ\:—¨îÖ;Ň×NXľtîţÄV]]ˇŐěŽÝ›¤V—ć¨<٧ ďÔČČČČČČČČĽ4 |ŠĆ!FĹúVv/RöĹęÔ"ŚĄMZ´Đ/’7hölßYjĄŹ_ú´ˇż­D*đ…#‡Ç„¸ŢU_;t˘ÁzŮä=ëu=%ŻçvĎ}Ő ż«Ď˛TaLĺşfÝG‰Çc›u=Ą_@d·ŽęcDźlĽĚ“ľŘlĽř<ň,ŹBŁÚŁi`@E /ýedddddddd^„0„iĆ ©÷ĚSý:qĎ=/÷ú CÇÉ’gJţ[ „Imb&řYÇŘ„ŔD*VŚüÉqśtFë)ť‡W?ĐÁŚ€®ţ“]čˇežç1Ŕ®K?ůúD©Cű6F· –nTŹťçúŘ“OZ;fŽäŞ›Ť§-s„H C y,KŤpĎó’“',>2-'ź<“‘‘‘‘‘‘‘ů“Ôł”Ö|ľţAýŁŁý<óň ¤Ý_˙fѢýĽňó ‘şx7nÝŞe#//ßĆľŤŐ¤Ő4ë? ÚĎË!/ď~ÝŹë°g}Ş'„cŚFŽźqüčŃŁzžŢąGc)E!ť{F÷ę‚ Ő%•ZŚAA– cuYĄ–⣠{wţřmĎ–N{bOQŔ襾ô#Ä)zôŤnÝÜ/ď^®đĚ–ž cÂq“60Ć„„ĄbݶI­FHE瀰iㆪSk¤&”už€ &SJźµŮ‚µĚńâéc[~ůĺם±9%•K>OŰyŽŹ=ăIĄôA1垍ÇŔ#ŚĄ}M„pmw'„9Ž Jií¶ Â3˘đśöÚ+L{ݤŇ@)u j7müĐÜëÉzĘj"Ć@ íĆ!c${˙ŤJŢ22222222á ß¤y5ĆśôôňJÝ™ť«zOśWĄŻ•śÝąĘ`ĚüŻ5şĘ»ŮYéFSÍ»Súw8Ig¶H: ŰżmT›ÉţĽ;r ™ýÎýÜ›}:…ňŔÂĎ~¨Ş1äćÝ/L»nŻ€7>_#K Ó.µµçŔͧőúCÉwNm“Ş~ýŃju—Ř}ľ6¶˛BSPT»ö ‡ú_jŘz݆\­&´őş¶ě^{;¤ä}ôä_dÄRśđ¤ć¶ó"OúâłńÜ»Ő3B„T´€Ř¶É-Ó.ÓQú§ŕţÓ3Ô×Âm Ô[FăiË222222222/ÂZvę9kÚx;€đÁŏ)˝Ľ[v÷ZŚ$Ń›jfô˘jݶ­_#ŹŃs–j+Kű4µoÖy:5Ćš™Ă@ˇô1ö•a}»Hm<žľcmT3ëťô› R 4˙ĘŞµďMęm":Ű4î:N–@hx„›ŇFšĹ7WK;őËËŐ@“bŹćg¨ůŐÎ͆˝ö ctćŕpŕ?F…â{›°ěý÷gOëéHHľűŢ»o-lě ?Ô™3}‚#‚Đ3îÜJž>lěŇeďhßJ2eăđÚëoôţ;ˇőq˝Ś|Ô24mţÖň÷§Ž9dČŕĆ® ô|Ěć=žĺc/ň¤/>ŹYF!zOź9{ÎŚéŤ|ë­…Í}ť@éůŘ i«öo˝űö´)ݧ¨3ć Ź€°ŃłfĎ^0s‚›€MŁf#G w&1ft3?Ý_ť5çĺ`§—yÓ22222222ž\´I]ŢÓĎý´uŹ ¨8§şĆlٞ-N»ö@RŁ·"‚A4f¨oć•uę7ćŢŐ˝çîŐä߼úă–Ý’ŽŢ`•Jß*›uţiç¶ďVĚç^•!\›?ŮĄWua†MËÁ{÷ěőF5QÝűČ’˝{v·ôÂĺ&3'ĺc(8ô˛đ1˘Đ3¬[ećĹůäµ™“(5µmáGÔ€1xóźă¶Ż‹îÓgĹW˙yedđîöĂÚŐŃÝ"˘GÍtp_k…­_Xl\üÂ‰Ł–|üőˇ}żőjÝěüµÓżm °kŢőY>Öđ“"„ śŤ'˝ źµâ‡}řćňĎ6üç‹7—Ż\9g,Řzď>'Ť06ö@ďî¶M:Š‹}Ň„%ËßńuD =‡-}˙ĂwßzËO©€ť˘7¬YŐ”ŕ‚ÖlüĄ_źŽ}b–®˙ćăZËË>ůÇÂÉřŹä§ÉČČČČČČČüm¨? žCV őüú›/ŤY ăSóYę$ÇRqAp ŚśÜŻő†ĹŠ DłĺNüu°–k’Ő†Ś©9a RHVłčÓ¬]˙ĐŰ·tô·CÇ ťĂ5}“ć{‰¸;îřÔ!Ý·UüWň,¦R»pĹ'Ť 7O&•ŠÇśaB©¨ôë·xîŘŐoŚ]¶)ÎŮż… ®âĘÉ“§6őr˛wůć›ĺ‘ÝZ'—Yßś?~őÂQË6Ĺąµ˛©Î·ˇ úŹfú-Ĺ;7/!84Pé5a`„ď§ó?KÖŔ—k|{渋ľˇĐ/ŢyaÚ´éţŢÎöÎ!«V-ëÖ1˘‰çŘľ¤e›¬Ó¸ÔăŞ*őQc§h×čÓ+%;KgŽ9ľđ+«¶ňz˘ZߏŐ˙¤usž;ĎüýđxďŞ/ŻŰ÷íďxú÷;Ť^íŰqâÔFQaŢ_ľ±2© ľŢđÓĽQŢEţľŠ˘Đ¶H産kĄ:Ö›ľ[x)ýfÜŞ× V}Ť^ 5ŐzłÉbŁ@{V}yUŮ»żăéť·<ŢŠéÉĂV‘ľdq6™˙ÓÔ»tCL´ZŔö«M»†…ýz/(a@Ő,<"ˇŔó`µÂŕńcmÍ9żLŃbŮCťb0FƢ‹ş¶®łüśőô•]ack*I›3MmáCşfő ďqÁ ƲtYҬcfT»^[ŹŞĄŽ?ůî©ĹčŇkľWţÔńf¬9 |Ľ†Bµ ä %§Ď&€ˇ »Ę*†E­[żÁ[iĘĘ-ÄĚTmĽŰńúbIGźźYa±†uTi5…7“‹@t4›-śk€W“@]yY÷Ů”ŠňluZaxĐ cL©čתăú%ËEČŞ×sö^…w®`őťŇ -¶uńj¬+×<´SP€LŁô±úź!žç­íÓđl<ó÷ñrŤNkŃ”DNäť˝›Úé4eÝcf T**2oŞ5ZßćĹw®•Đ[©÷5:ľ®˘ťŠ`‚1áx3“˘1 B \ŁÓÚj J̨49 ‘‘‘‘‘‘‘‘©‡z˘©łČ˛ox%ŇĄoßčäŚbB°(ŇÇ%Äj»NŽqyßşĚ*"Šâ:˘(ňnA}°Ô’sćßďž×—CZjŚ‚Ňww‹˛–**KU¤˙•¬qŠ´%nN.ßľ?9ţ–eu ĎAť”S“ö1JňË9•că`ČľCxţ?öŢ;ŞŞc{ŕß3çśKďJEQD{C±wEcď=ŚQ“úbş1/1ĆŁ1±ÄFŚQTş ‚4)Ňďî… Ürf~ŔvA’÷ľżőű=çłŢZ/nćě3{Ďܵfź™Ů[§;mEśŐÍ3¨Ţąfb¸LŕĘ ĺĽ©•Ô0 #„čE⥤A­QŐiUyszŕB€ŠŇ»0¦„@đôfͲ“Ď şzeq‘ť‡Ż5€čÜŃĆ‚ÔŐj”uZŐĂąAc$=Q Řşµ4Ç Zz^]Ćřô˝’ŰÇ–‡~¨‘7 ¸D‘€^×P]-:#"Şk4*˘­É 3Fˇ##M˝fŐ“Ěí; ÎŢÉÔHĐë­Vkt Mť^: ‚`dÎđć2‹"!”>ÖLE˝¨úÍ Á`0Ś–áxf®ü€Rš‘sô—wnFű{˝ňFł$ćĎ+!ă@˙Y[)ŐÍŃx%úɧbţĽ2gÜ °ę=CMiŃ­ăFS=IŮĆ/ß–ži&®îŞnźřáÓoŽPQµ`”Xű¤×2 ŐU/ î%ůmó—ҢýłlRJ.ěěť_qăř‘#§nWÝóq5€Ç•dµšž¨,ÉŮąqKXÄ­eł†N]SSţŻíˇ‡Ł“(ĄŰWM€ýw•%9;7n>q{uČĐŽ#7¨J{=ËëéŽ2‡ş;§ŹlX·éŻ¸ÄŤK'ĎsOÎşÉË>PW—6kŢ´(ŔÄeXqµxë܉?n&ęÔňą#=Í\‡ćʵÍz^]>¬|g¶4Ç ZÚÇÍúN^E)}ř×QcŔ9÷lÍϹǝMüqó˛ˇ?Gzcݶąqż»yĘ(ÓÄž>˛aݦ›‰wWÍfÝ+X®ŁżřňĐĄ8Jéʉ>¶›·żučL„ZĄ8˛oßú…ăÍ;έj8˙ë÷ŽFSJÍżíÇkG¶­”4ŻŮúÍŁÔKB ż Á`0 ĆłÇlĄ “™äĺ¤&ĄfŞjŞ‹‹Ĺ'$–)ęJĄ'¦4J“âó‹äÚŰgÝýóčďQ€gźJLŠ/(’S±A^^vĺrxbZŽÁŻĚcJ—˙UËVřwsľQ©(¸“Ô+(¨łťđÁέż]LëË®^‹ď8ĆÆoÇÖŁá ´áĄ”ě ýĺŹŰ„UoóÖęiőĄißý|VŚĐßý€O1FDU}=ˇOŕ;\şz]LF1Â>YI!mäĹpŤĚ:pěđęś„“§ÎÄÝĽ]…-Gd^;}ädX\l|a‰"ňâĹÁ*pěpenÂŃŁ§JŞk«KňbîÄ©‰ľ¶¦"ööťĽěÔ¨8ż~CűőĘHřóä© ĺ 4íÎQđŕîÝ*07n¤¤9))9÷AŇĄČlk’s3Íłoď”k§oĆŢ‰ŚŽwöókÖS¦PR}}ËsĚ€Ą7Ň€^§)++»|)<1-0&Ş˛xăy'č4őY© éů…yiIiY…9‘W#"˘â\ýý‡řőLŹŹ>{ţjţýÄ©%ÁA%©;ţűÍŇ*nč¨ázyî…KWő&ćbŤ<:ęâÝěŞ#?¸~ü䙢⒇éÉ÷ó ňŇ’ďgçäÜŹONgű4 Á`0ů/|řEŇůˇ˙ěq×n=ÇŽŕëä§Ź/בgNŞaŽŁbsYD„1/§„ĂH$0rrHowۇińç#o“ťëkŮőO]o)ć8JB@µ"žç  !ç1ĄzQ”:†ć8$ęEŔQúŚć˙ÚĐ3 Á`üoa Ń »y÷=rXGgë‚üťH€RJ(Ż^ťL…˛j DDŃČޱs''ĄĽ’˘”đVÎăĆOčîfź›űP§—ÖßRqzÚҧzŽă)%ł—E^ľ´pâиóçv|4tbPGçöĆîžv'tňőq¸{ßTW§¬Uk¨e{Çž=<ť\@W[Łn€—HbďäăŐ©¸¤ęÍďNźřî‹aÝ­N…EŔč}çG— ÔłkÇ‚ü<˝Ř’Š1Çó’bŚ1Çq¨)¤y˛ FHEkwźEł&§ĄÖÂ”ŠŤmć0ÇaBHK›-čIÍ€ÖÇ\‹8üóĎżË-«j\çĐó‚9Ö‚Ą”DZK›˝ń´fcé ’QC–"„9L‘Đ~ŃŇW¨2ż¬ŞŽb×ŮwŃě‰yI‰µz‚ĆQJ(ĄÍ»=†43 Á`0Ú€TgÝ+hAA…ęaNŽ˘ZuířÎâ ÷Č´Ňú¸ÓßŔ9n~÷«¬Ry~ęuosc°vëu9.ł´0ŻD!ŹřţSk€¦\Í­!e °ňµ˘Ľ”a~ÝÍM…«§„.pă-k~Ü+>çÂgţ‘yëĚ>€űň䍻·Ďźľ‘|űÜż_6Éżßʉ=k`áŘó» IŃżđ’“˙öABéłö‡UUĘ‹JŠÂöď±0<^¨uíŇ›ŰHó§÷´őĘŠüľfą µ‘ŃžC“˝ëířpĐYK«ęçďÔaĐL-Ő… íä2`††ęB†˝\’RżdśŹä·-ű"ŇŁţg9Фأkđ˛:˘Y2 Ë¤ĄďPJ–Ź÷žĂOĎ$Ś4gÇ®]+δ·ä¸c§…n}űÖ ˝<]ăÇmV-žc‰Ŕ;xYĆ˝ÄĹ“f†îŘ>¶oI•Q;÷Ąë·íŢőšź·»áN#~řŘ©Oj€N=|·îܵpöÔ &xv°ăöOéŔBű–ćX[,m»7žŇŚĽúŤXĽ|ĺŞe‹ÇŹ›şu놮.Ö`l˙T SŹľ[·o[´`}rZćŞÉ~ŕÓ/hĹĘ•ë–ϱ“a0rę2uÚdk SfLďŇŃŃ˝÷%+V5köt¶ú'#Í`0 ń€ěĽ{ôšąőŁG9ł†t€ik?ysŕÇËÉWŹ`™ Ś^Ľą¤ŕľŻ9ß?s˙ĆadIŹä?l_潦V4;úp뙝'¬x-+íş€u{óŹVwtéí3{\/s4gýKKáĂŻĄĆž;“óŰW’ŽĎů+őÎK&ůuđ<€ĐoŁ2˘˙ł¨FҰ쵊DŰ:w;“%Šu_mZ \ÂHZ¸oŮsX­ŞŠľrĄ°¬tóÂŕ8ě^naÄą“WoÜÍ»§Ż‡„~|Djó¨ĽlýܡNţÓĘUšĚ´äÄŚĽę‚´Á.2°ëzáÎýôřżÎ_ů«0+)Ч#p¸1f@‹îă“3ó$Íů±=ťe¦}îć•ĺĄĆ'ÝϦZŐ˛`o0s ŹMoÖ3¦·XxOni޵n)BjŁ7žůŤH™ť_;©®|ô çQQNF‘˘úř»ËÁÔńŹŘ ©‡E9)#şµ3uőOÍ«xx7>9=«ˇştŮ„>°dóWň¤Çö±4ź)k+Jr{^EŐ5«NZňá­Ş¤QłĽňä‡ëq-ő‡Á`0 ń4sĐ3đ•{9ĄZ‘Ć_úM`Ú±ojNţśţ+Ţ<óŕÚŹâd0~嶲Âű˝-d0c퇍bŇßq w×ń덋ŔÄuŘĺ[÷Îx[0x¤ç‰¨ćOŔĆŢüýĄ®C'xn]Ůud Gđ籋9Ů‚ŤÇŔ»ŹTęâű#˝żâ;x N.Tľ„#ŔÖýW˙ă¨ćÇ»·ĎîŢ,.ňTDlĘÁ×–Ăëx)Ň0î8ş şîĂ%ăŔÚ­›»‡ €©ďŕ€)Ó¦ľ˛äőŇJŐÖůCŔ˘q­FjcŰą‡“˝…gŔüęęĘąśŔ¤O^EŐ˘ ŻQ v‹zĺî•ó&Mźwż¬öňľ­đÔĎ[ő4RŇ\ˇT®ś2hÜŠĎTE).Îýg)”Ő3¸.z[ÔU?ÖóŐ60î0,˘…9ÖKŰŇć9Łšť?FykĂ–ŹĎ\üöŐe›öÜ;0wő›ő ŐoŻzeŇôW(ęŹí^1gĺ•Ĺ)N„Č«Ş–Ťď-©đż"7󮯅řNX‘—•ÚÓ@đ~PXşxöŘUźťýíÝM’ćĹëßO˝qŚŐ«a0 Áh ‹6BDȸ~bďĺcć˙rŕőiţ˛AˇÝítĆ®Ţ};™Ř·›;iбđŰzJ@c¦ÝÓż}w`ô Ăa‘Źň˛*+*Ŕ€¦äÖŘA=›tż8­Ą”7‘eßzceýŐޡż}yłJOŚMŤKro˙•2Eu-­0/€ľ,#ć·čÔ©/ź„P©ĚüşĽ%ÚzgŻák Ξłě›sĆÜS©#B sˇ®ěÚő(¨+Ę©Öé=|ż=pĐѸ!;ŻÓ†šz˝cß΂şTjŁ.ĚŞÔę|üM”ňâ”Ä-5-oëîŕęˇRT Y9ÎX¦ČIK/.AÍŐ`0Ć„{řřNŇ\‚tj5oîŕîQśq§§e”W*±©ŤC'O•BţXOQh(Š ju޶!A(!RNĺÖ˝Ńâ ąJ©UÖs ň˘`íŘÉL%ݞ"ŘXV™•’&Wşxu-ÍŻ ÷RÉUBS¤dÂacŽ4€ @ ąJiެ3Ć”•úĂR0 Á`Ŕ𵎨^¬©‘Gţń{i=öîŮ«Ş,ó^ŽbĹÚŐľÝ-x+÷éyJ@/­N«E@ŐyŻÎíرÄ[t”{X^F¶ď±˙­ !Ň˝öÖ{ž¶w2-­ÔOZÖo°;źyOĺŘÁXÔQŐu]­Ô’"xi%Ťň˙`Ô) (Q–ŮYŮůtÇĺ{ÚŢÎe*94§S“Ňp•*xËžnŔ <Śť¶˘Îňöě´h‡J‡eWV(çM­¤6€y Rşd€8B)iPkTuZUŢÜ ‘1lŰg'(ĐĆTĹSžţ„f"şzeq‘ťGwk3çŽ6¤®VŁ¬ÓŞ6ëyíó“@°ukiŽ´´\]Ć]ú+ţĐgŻń@^č .Q$ ×5TW‹€¨®Ń¨ę´5y!cF4xdŔđ÷ľ>ˇ®l0·ď€8{'S#ˇ9AłZŁkĐhęô:Đa#sŔČ\ĆaQ$„ŇÇš©¨ő˙…g0 Ářĺ٨F:Ťł`ý÷SďĽ÷ÖŰçŻFřŘh“nGź˙îÍ!C‡<đ·X%W3kŐ{ śŮ;{Řłe­MűNG"®¬ť>‚Đ.a—ŻĽşţč/§„ŇÄßĎ]ŢÁ{ó¦Ő«ćMký¬ĄTÔ뀦AďŢ«C?Oó¨ó÷-»Rnn9q€­ş^sŰöímÍAŇć6í^BIŁÇ´"ý‡µkH âň©äůŔásŹüň«CCţá°Ëиů&ŤAňÄc·ň?˙éÂÎŤ[ŽźŤ^6+ 55©}Gß]ŰC=ĐŮÁÎĚʆć^ijłůDXôꡪ:ŃĹÉŔvíŰËÎźĹUyôő„>cěpIčęu1ĹcúdY„´‘Ă52ëŔ±Ă«sNž:wóv¶ ‘yíô‘“aq±ń…%ŠČ‹«Ŕ±Ă•ą GŹž*©®­.É‹ą§&úÚšŠŘŰwň˛S#˘â\üú í×+#áĎ“§.”+TĐt\LŠÎÜ˝[făĆŤ”4'%%ç>Hş™€mMrn¦yöíťríôÍŘ;‘ŃńÎ~~ÍzĘJŞŻ———µ0Ç Xz#ýčuš˛˛˛Ë—ÂÓrc˘*{7žw"€NSź•šž_X—–”–UPy5""*ÎŐß_ĎôřčłçŻćßOĽ‘ZP’úç±ăżßډ)­â†Ž®—ç^¸tUob.ÖČŁŁ.ŢÍ®1b`ńëÇOž‰‰‰)*.yž|?Ż  /-ů~vNÎýřät¶OĂ`0 a¶}řE¤ZěQ$RĄÎ>h[Şł#Ś)!®ÝzŽUyägź›ědoflĚ!  őş˛Šú  aµµ:Ś(ˇ!J®c„¨TXţe’ĄpŔäwۇińç#o“¶ŚFËĂÚÂż eą×µôěßś Ź5#„)%&VŢß|»;öJX—ÓŚvěźV¦Ç‘ż[‘˛-–¶ÍmxŐł–¶IŐ?CÁ`0 ´Ő<ą2ĂŁT¤đX„0F”žĂŇ%^€zB›%c TZ”ó”čE±-dÜěin„Š:=A›™™‘üv"S§#Ź;×tëIţłÁGRŃzŔ¦„´Ő`Ś‘(Šă€B€ă°ôOŚ!"!ôą6ÍŠĎsRŚ9T$„ăxJE‘ BnÔĚa,ęuنîŢ<ĚŻA™»ďÝ=IYR]ÎgőĽpŽ=k)ĄR”Îs@šžj›7žs<˘RŔI)E^/>o)ć8JB@µ"žç  !ç1ĄzQ”:†ć8$ęEŔIżź'4˙wĆťÁ`0 ădPDvëѧ_Ź.µUŹ˘˙ĽĄŃµ8Ě×]żUŢ EH02b”9§‰ŽľÖ %Í ^u­A+Ĺ!ă8iŹÇÇ‹˘>`ö˛ď>ű—úáť9c§>ĐYąwtĺy¤Q+ ‹ó)Ą2+űî]:É0hëTY9­ţ%•dćčtzÂ™čçÂ÷3—żĄŚŕďn^4´ˇń2eů1ÁĂűe:n]5nŐ@/ą÷‚°ŢšüëţqPčęq«v—{/űáÍ9çŹ!I0v71Tć\jőµJ›Ľ «ň§ôěő¨ĂŔääk«fL<+×`uńôŢ=$Éš9/©$!>rÍś©k?9¤(ş÷ W>Ľ—řĎľŢcŽ#˘ľkpČćUc× ňnŻ?Ž}žĚqX˙ÄŽB”‚0bŇôÁ>]…Ξ=S®‚c'űűvăôęđ°Ó©YEˇĽÔ¦˛(ű·źŹëëëKĘ>٦ŽęŮ9őň—ďPY;÷WBfşZˇógO&¤=4pÓńĂ&ö÷őjÖ @:őđť9uBEvšB­ËĽ{+ëQĄQ{Źysf<Ö€„ö“¦ścm±´íŢxʇŻ~#úv3˘ÚÂâęî=\˙8v$ł¨ÚČŢcŢěÇ=€N=úĚś4Z^\_]^&í|úô ŕ.űň‹O őÇÎăö¸VM„)Ó'ĄĹĹ6ťGöď)#IsŘo‡łŠ•m˝¬Ć`0 ń’ń\TC)Rä§GŰy÷čŐ˝o§Š’‚JeU'/SÔtqď&']ŞKłsËGőťS™s®űá‹yš®ný¬ź"÷¶$!¤Á·[_¸$óôýń_”׏DDÝÖ´zias€Q¤2‹!S§T{jŞËł qfND0:uŠ‚I ˝%ă˙q–_Ść3¸*+¦yĽzwő…đd‚0ij„–=?Ľ·vRěťř.>Ě â‹ňőţ}Ĺ©·9»®k–Íź>1(1WúńŹď¬ž{'Ţł÷FASt*»Ň¶Ł÷Î=oÔrÖkŽÔ'¦Áíô…łś"§ –/ž»dƔȔcQ:ś†9JD Ż }_í-OŹăěş®]±`¨ˇąĽWXře«šÂ*n‰oűĺ“eUYžľpÁË%=Kgθr7׼k‹s¬uK˘@ŰęŤgg+˘„Âäoîžĺő¨ ̡¬ű[ŐĎŮsţäůđ.¸"§ V.ť?oü¨¸:÷óááćĘ|ĄĚĘËÉB “ü†M Ý4ͤ®8ćÄ m]·ţAż «&ťżůáçw7ÎÓv_z`ÝŔ‡˘9Ô‚•ýŔvdÖŽŻsĐ\¸†Á`0 Ń˙ĽH*ôŢ}Dđ±źuus¸{ĺ·č´ÚzáZÂĂš_¸Z¦ęĚ?#bĚlZˇ¨Üđć;ęR˘“Ld$ŻVJ’« őĆ2ÄjEblZ]f.ę§· mĽ&AôŘÄfŶ]–öíeŐńYůfýúsĆ6Ë_z‰'ŹËĚ—|őßřfŹäUĎŽ×ăżaŽѸăčM«gî{u掟­ݺŮŕZ(­š?a'+skŻĎ>Ű0¸gb…nËÚŮű6LŰńS¸mçF5…ćŢSdD˝{ů¸ß’ó ˘<˝=ŚćűąĽ»öýD9|´˙»mËgEnü¬9fĘÂÔgÝ\´h±›Łµąµ×Ţ˝;űűąÚĎtĘş÷ęGűĎJ˝r°şJ8sńŘ>Nď®{OŇş|Ć• źč”UI±ięÖćaK›ó_ĽĐ-ţ~|zďGIćŁĆX^ű=ĂiÉ(˙ą ť}?ző˝„ řôŕ÷kB¦9–¸ąČJĽ{÷ă„$_Ú/ĺ±ţé‹ ·¤„ď]O©XŻSתE®¶F­iĐÉĐ©˝ĹŹcyíř˝ö[C† đµN$˙89Á`0 Ć˙0řy!"d\?1Č·çřY›;řÎí3x₡]M—Î8aÚ\Ô©ďęécŐň2ÁÝßر«ńŐ¨ť» o’!MÉ­±zN[ő¶Ú¶CĽŚ( ŤčďćŇýTş°gËB¬×)-f’ ᣭ‹š6hţÓ›D[ďěőxĽŚą§ŠI×C:÷ńęĘ®]Ź€ş˘ś‡ąE>ż=đă—¸dŢLjęőŽ}; ęR©Ťş0«¤ĽĆÄÔD)/NI,]ťFŁĺmÝ\=TŠŠ!!+w…®W䤥— €ćj0Ň…űŽ=ü|÷Ó—¸d^ҩռą»GqĆťb€˛´ŚňJ%6µqčä©RČë)*B E1A­Î1Ă–"$ßduëŢh ž§ ąJY©¬Ór ň˘`íŘ©›J^1$dĹ®më*łRŇäJŻ®ĄńĹ÷RÉUß/™pĂă R4Ć`ÇA“fL‰1P©?, a0 Á06(ĺxęĹšydŘďĺZěáÖÝ͡łVť:ňVěŐ é•:O—ĘĘG6f–ż|şăň=­ŹGEmq~ŤÜÖĚB’ôöp.«Q€ĚÎăý/öżµ!Dş×Ţ–>QBt:­V/T<|TęŕÚI&ă4 =yŮ%…Ą®Ť$7ýA %Ę2;+›#Í㥒jZ7Kűe… ŢIJ§pc§­č€łĽ=»-ÚˇŇa™Ŕ•ĘyS+© `”. ŽPJÔUťV•77häŔÁF ŰöŮ ´1U1Ć‚§?ˇ™‚®^Y\dçŃÝŔĚąŁŤ…©«Ő(ë´Ş‡Íz^űü$lÝZšc--WW€Ç€q—ţŠ?ôŮk<zĂ€ @ ču ŐŐ" "Şk4Ş:mM^Č‘ 0ü˝ŻO¨+Ěí; ÎŢÉÔHhNЬÖč4š:˝tÁČś02—qX ˇô±f*ꛞ±¸†Á`0 ă…đ<€Ĺ›ţý -î˝·Ţ>ý>­+ëÝľÇŘU ”˙ţËOĄT»eĘ0sK.ŞĽqüČ‘S·•%÷{9đĐÎ+ąP.IŞ‹îőîd>ÓŐ”Ý:n`0Ő“”müňméɑƓWżN(˝tňŘĹčŰ"w. śż–2Iôm­ľaĎ«ł$żmţâRZÔˇĆM‡żáH)ą°łwb~Eóxů¸š€”ťů ­ć_…'*KrvnÜqk٬€ˇÓ_×Ô”˙k{čáč$JéöU`Ä]eIÎÎŤ›ĎEÜ^2´ăĹ ŞŇŢĆBĎňzşcE ĚaŔC…îÎé#Ömú+.qăŇ Ŕó\Óŕ`ň˛ÔŐĄÍš7- 0qV\-Ţ:w⏛‰:µ|îHO3סąrmłžW—O+ß™-Í1–öqł€ľ“WQJţuÔpÎ=[óĆs.ä1ŔGgÜĽlEčĎ‘‡ŢX·í@nÜďnž2Ę4±§ŹlX·éfâÝUs†Y÷ –ëč/ľ+Fčď~Ŕ§#˘*ŹľžĐ<^1ĹcJž*ú  ŤĽ®‘YŽ^ť“pňÔ™¸›·«°epđĚk§Źś ‹‹Ť/,QD^ĽŘ XŽ®ĚM8zôTIumuI^Ěť85Ń×ÖTÄŢľ“—ťçâ×ohż^ ž+5!=ż° /-)-« 0'ňjDDTś«ż˙żžéńŃgĎ_Íżźx#µ$8( $őĎcÇżSZĹ 5\/Ď˝péŞŢÄ\¬‘GG]Ľ›]5bÄŔâ׏ź<ST\ň0=ů~^AA^ZňýěśśűńÉélź†Á`0 Ă műđű\I{„€”jŠ6I€ÂS™Űx­aL qíÖsěčľN~účńry2;ďóz^f F@(99¤·»íĂ´řó‘­ćË~ëŰ4^ĎÉ Ľî…cݶ”Äč‰é„)%&VŢß|»;öJX—ÓŚvěźV¦Ç‘ż[t´-–¶ÍmxŐł–¶IŐ?CÁ`0 F‹`ŽoľÓܦö¸±Z"Ćăžľ«Ď?ľ“ÝžŤŚŠ>懠>žŇ‹ čyá{ÖŇĆ>óOd hŁ7žs<‡1ĆÇaŚ9éLÝó=ćµ"ž¤ çůF«1BcNx,9ô9Í Á`0 ^*! "ŻĂ‚†Äő5eUJG×^=ş;9::»¸tpvât Şş†î]ĆŚ ęč`QPP$JźĎ)%”včŢ«“©¬\©jÜÁiÜé1ü-šăxJIŔěeW.]Z6mصă§ä:âîí7zä°ŽÎÖ……z‘âÜĽű2IaÁ#Q$ÂîgŽ÷ů°îV§Â˘ `ôŹľó#ÄË†Ž ęٵcA~žľĹ’žTZU٦ÔŰŇZ!DŘ´mŇŘ#$Š˘µ»Ď˘YŠÓRkaJç ĚaŽĂ„–6[Đ“š)­Źąqřçź=–[V…±´ ňĽÔúkÁRJ!MŹ´ŮOkFO†ô4šúl¦Hhżhé+T™_VUG±ëě»höÄĽ¤ÄZ=‘BJ Ą´y·ÇfÁ`0 FŰľ'ox˙ëęÚşĽ‚GeYwܦ¬ţXˇŞz“ťů łľˇvű‚1ţÁóTmîŠ*UÔŃĎť8Ä@ď‘iĄő±§żnĽÜü˘×IŮ&¬|íQ^ʨA>ŕ´ @®z“ٍV]˙ý+;€.c2IµęÚń˝.< [çžÎ'fD˙"eýúűű7Ň#f쫪”•…íßcńxđźi‰ZUôT0Ç@ďië•ů}Íx®·ŁgűŘ–#ág[ţm=m±´íŢxáŰ ô!$%-ŕL{ĺU(7Îđ—ţä9fqfZĽŻ)mIŔ’0 Á`´Ś9č4dvEŤňőy gź~¶2ł´éŢłWG§öÓW…*«ĘGv2wő°zI€ďř­µőµË}ńżO˙©©Żżqć[)1'k?eć+“F Ä—fŤQ͊פD™ íÜzŚ5:¬ŐčtËÇtǶnÁ™¤A«]=ˇŹä·-ű"ŇŁţg9Фأkđ˛:˘Y2 Ë¤ĄďPJ–Ź÷ţé„ aĤ9;víZąp¦˝%ŔŤ;-tűëŰ·nčĺé?nłjńKŢÁË2î%.ž43tÇö±}{HŞŚÚą/]żm÷®×üĽÝ wńĂÇN}R3tęá»uç®…ł§N0Áł-·÷xJZścm±´íŢxJ3đę7bńň•«–-?nęÖ­şşX€±ýS=€N=únÝľmŃ‚őÉi™«&ű€Oż +W®[>ÇN†ŔČ©ËÔi“­9$L™1˝KGG÷ŢC–¬XŐ¬ŮÓŮꟌ4Á`0 ĆËÁł—g0B `ŕđšâLŁîăOźÚüëwßKŇN™™V%RX;zF~ÜéżňkEű.-ž””ćŞët‡`Úš÷Ćx‡~»> “ĆŚ» üţř/ĘëG"˘nkZ˝4Ž0/­ĺy÷ŁŠěĽ{ôęŢ·SEIAUM ©|ő—šI䥅ŠŠĆ‘’ńčź~ŔÇ€a>«˛bÎr?|1ʆŢ]}!<™ @ššˇ°eĎď­ť{'ľ‹Ď3¨ř"‚|˝_qęmήëšeó§O JĚU†~üă;«'Ćމ÷ě˝QНʮ´íč˝sϵśµÇš…ăő‰ip;}ᬧȩ‚ĺ‹ç.™1%2Ą€ĂX$ć(-Ľ‚ö}µ·<=ŽłëşvĹ‚ نćň^aá—­j «¸%ľ]ě—O”UeyúÂw,—ô,ť9ăĘÝ\ó®Zšc­[ŠÚVo<;[%&Żxs÷,ŻGU`µ`íŘߪ~Ξó'χwÁ9U°réüyăGĹŐąź7Wć+eV^Nz‘€ß°Iˇ›¦™ÔÇśřCˇ­ëÖ?čŕ7ˇáaŐ¤ó7?üüîĆyÚîK¬ř°B4‡Z°˛ŘŽĚÚń5`š ×0 Á`0ZBŞWóĘú} ˘ţĆéźż=tŚŠŐ ‚˝Řxď~Yíĺ}[qÍA5ž·ę=h¤¤ąB©\9eиź©ŠR\śűĎR(«g p \ô¶¨«~¬ç«m`ÜaXD s¬-–¶ĄÍs4Ö«ŮůcÔ‘·6lůřĚĹo_]¶iĎŤ°sWżYßPýöŞW&Mĺ˘ţŘîsV~PYśâĐa@ĽŞjŮřŢ’ Żń+r3ďúZ€ď„yY©=Ťď…Ą‹gŹ]őŮŮßŢÝ$i^ĽţýÔÇX˝Á`0Ś–0|ŔFfdÚP–ľ*dŃęĹ ®fTö`ü왦šÜ_˙Q«ŃęM?ůéÄ$/qöâµr€U›ß÷´n0vőčŰÉÄŢsî¤Á ˇäöŘA=§­z[mJ–+@Ćő|{ŽźµÉ©Ď¨EÓ‡@ćŤSLâÔgÔ’™CIăćÁşĽ%ÚzgŻák»ż±cW522~:‡*uîă!Ô•]»uE9s‹<|{ŕÇ/?ţpÉĽ16ÔÔëűvÔĄRuaVIyŤ‰©‰R^ś’Xş:ŤFËŰş;¸z¨CBVî ]ŻČIK/.AÍŐ`¤l`{řřî§/?ţpÉĽ ¤S«yswŹâŚ;Ĺeiĺ•JljăĐÉSĄ?ÖST„Šb‚Zťc†-EHx"ZëŢh ž§ ąJY©¬Ór ň˘`íŘ©›J^1$dĹ®më*łRŇäJŻ®ĄńĹ÷RÉUÍŮM8ĚaĚń‚ôă˘@Ŕ Ž&Íc RXĘÁ`0 <ŐP@ ¨ŻÔ‹ŇÉŞIă^€Ě~~Č”ŰgNfU‹ăD ;>˙ú•›QŁ‚îÜ/Ŕ•ňÜ{ŮňkWűv·ŕ­ÜgLĂpv]>Ř»˙­ !Ň˝ö¶ô‰çŞkjä‘aż—iř®ť=a’2 ß­sWřŰu7źĺe™ť•Í‘Ow\ľ§ííá\¦’@s:5éWYˇ‚7±ěŕéśŔŔŘi+:ŕ,oĎîA‹v¨tX&pe…rŢÔJjB^ ŽPJÔUťV•77häŔÁF ŰöŮ Tlś…@đô'4AĐŐ+‹‹ě<ş[9w´±° uµeťVő°YĎkźź¤‚­Űű_žc--WW€Ç€q—ţŠ?ôŮk<zĂ€ @ ču ŐŐ" "Şk4Ş:mM^Č‘ 0ü˝ŻO¨+Ěí; ÎŢÉÔHĐë­Vkt Mť^: ‚`dΙË8,Š„PúX3őÍĎX\Ă`0 ńBĆŔwč·PuűÄź~s„ꪗLđ€ţł·RŞ›?˘ńôĚ•PJ3’bŽţr"ćĎ+łÇú€™‰oŽ/K8.}‘6ď5MMiŃ­ăFĎĎH'ĐĆ/ß–žia °dóż¤Ĺ˝÷ÖŰçŻß§őE]§Żü<;=IÄšÂi<$żmţâRZÔˇvMJÉ…ť˝ó+n?räÔíę˘{>®ćđ¸’LŁVóŻÂ•%9;7n ‹¸µlVŔĐéŻkjĘ˙µ=ôptĄtűŞ °?⮲$gçĆÍç"nŻÚqÄâUiocˇgy=ݱ"Pć0ŕˇBwçô‘ ë6ý—¸qéh®ÁÂńL^öşş´Yó¦E&.ĂŠ«Ĺ[çNüq3Q§–Ďéić:4W®mÖóęňÉ`ĺ;łĄ9fĐŇ>nÖĐwň*JéĂżŽ#ÎągkŢxÎ…Ň ´ŹÎ&ţ¸yŮŠĐź#˝±nŰܸßÝ<e”ibOٰnÓÍÄ»«ć łî,×Ń_|yčRĄtĺDŰÍŰß:t&B­RŮ·oýÂńćçV5ś˙őűGŁ)Ą‹fŹßöăµ#ŰVJš×lýćQę%vŤÁ`0 Ł%ž;fC)ĆHTE_Oč8ĆÆogč‘° ŕlŰ>;ĺúŃߣ4 “™äĺ¤&ĄfŞjŞ‹‹%&%Ë)B©N×™w÷ úzEEů•Ëá‰i9ż2cŚ)!^ţCV-[Ńż›Ë•ßĎ—Ő×Ű´łńéă«WÜÝ˝}óů;ŮTÔ™XYöôőŐ–'żąmSx\u¦/ˇäµÍç®gP­zűë·VO«/Műîçł"`ô·wn(ƨʣŻ'ô c‡KBWŻ‹É(FÓ'ˢ  ŤĽ®‘YŽ^ť“pňÔ™¸›·«°epđĚk§Źś ‹‹Ť/,QD^ĽŘ XŽ®ĚM8zôTIumuI^Ěť85Ń×ÖTÄŢľ“—ťçâ×ohż^ ž+5!=ż° /-)-« 0'ňjDDTś«ż˙żžéńŃgĎ_Íżźx#µ$8( $őĎcÇżSZĹ 5\/Ď˝péŞŢÄ\¬‘GG]Ľ›]5bÄŔâ׏ź<ST\ň0=ů~^AA^ZňýěśśűńÉélź†Á`0 Ă -Ö“úz‚ôÝžR s¨„ç0ć&˘ž ¶–ASB\»ő;:€Ż“ź>zĽ\GZ{!„%äĺ”`„ €É!>î¶ÓâĎGŢ&mőô FůŮ1oQnŕu-=ű¤)ôĹ—ŞkFSJL¬Ľżůvwě•°.§/í0Ř?­LŹ1"·"e[,m›7ÚđŞg-m“Ş:† Á`0 h)ŞA7_ĺćxŽäů%é“«7©R#h=~ !žă˝(¶ĄC‚€çÎń22â0¦aQOętÇOfétD*ł”Rx\ĐK*ůo >’ŠÖć0%¤…¨cŚDQÄ”‡Ąb„ ˇĎµiV„xž“Ú`Ě! "!ÇS*LâpŁfcQŻ#Č6t÷Îŕa~ ĘÜ}ďî‰HĘ’ęr>«ç…sěYK)Ą!Ěń¦§ÚćŤgŔŹ(ˇŤż„1čőâó–bŽŁ„ ´Q+âyž‘‚ySŞE©caŽC˘^Ś‘ôűyBómč Á`0ţ·0”č ! Tfí2nü„nöóňő"±qěĐłGw'gg—.NXŁV5đ˝zűtěŕäěұť5®+)é©înöąóô"mT‡méS=Çń”’€ŮË"/_Z8qhÜůsŻď<)¨Łs{c7OŰÉ“<Ľ»Y^ą®u±·”Ë•›wO3Đňf¶Ý<Ú••WżLpóîi#Ő5 ońý§Ö Ôp>1#ú)ë×ßżA.=böÁţ°ŞJyQIQŘţ=†Ç µ®]úcsĚńĐ{ÚzeE~_3¤ëíčŮ>¶ĄÇ´3c ăCO[,m»7^ř6=DII 8Ó^yĘŤ3üĄ?yŽYś™ďkĘA[’°$ Á`0-ól‘AŚA$0iÖÚˇ]¸>Ýě'&Ţ8ĽüÔ/ßíß•qă°ŞĽ¸ßäE?~řÚŤČh0ÁkŞ6ĎŽxPcŤu€sÖéÜôÔ_‡CÎ˙ölś`ě0nb ©ĚąŐę  ęůť„tÁҸ2Żěű%ďçXąôíÄ.nCÍLd”‚¨ "’ÖŮCDńĄ’čĄř…wäʇ÷jۆÂs`Ž#˘ľkpČćUc× ň–{/űáÍ9ű><™ă°^||“!JA1iú`ź.ŠÂgĎž)WÁ±“ý}»qzuxŘéÔ¬"ŚP^jSY”ýŰĎÇőőő%e źQSGőěśzůŹË‰÷¨¬ťű+!3]­Đůł'ҸiřáAűűz5k ťzřÎś:ˇ";MˇÖeŢ˝•ő¨Ň¨˝ÇĽ93ë@BűIS‚ α¶XÚvo<ĺC„‚Wż}»Qmaqu÷®;’YTmdď1oöă@§}fN-/®Ż./Óčřô ŕ.űň‹O őÇÎăö¸VM„)Ó'ĄĹĹ6ťGöď)#IsŘo‡łŠ•m»žÄ`0 ńŇńlT#ťe˛±rĚO»™^X/–%ä(Ôý˝z÷Gâű÷t:qíč…‰ç"óꍻ‰ÍĆ—yěČÁ …ťŤÓă§*ŐţÝz q2ĎAß˙EyýHDÔmM«—Ćć€`ÄçŢ-6ó˛ŐŐx÷gŹÁij?_QfŢ~áâůµÔÔÁŇX'RBtFí.zĄĚ^‰ŁĄńĂĆE6Ć2ýÓř0ĚgpUVĚąBî‡/ćŇĐ»«/„'„HS3 ¶ěůή“bďÄwńŮ`_DŻ÷ď+N˝ÍŮu]łlţô‰A‰ąĘĐŹ|gőÄŘ;ńž˝7 š˘SŮ•¶˝wîyŁ–łöXłpü >1 n§/śőŕ9U°|ńÜ%3¦D¦p‹„Â%˘…WĐľŻö–§Çqv]×®X0aÔĐ\Ţ+,ü˛UMa·Ä·‹ýňɲŞ,O_¸ŕŽĺ’žĄ3g\ą›k޵Ĺ9ÖşĄQ mőĆłłQBaňŠ7wĎňzTćP ÖŽý­ęçě9ň|x\‘S+—Îź7~T\ťűůđpseľRfĺĺd!…I~Ă&…nšfRWsâ…¶®[˙ ß„†‡U“Îßüđó»çi»/=°nŕĂ ŃjÁĘ~`;2kÇ×€9h.\Ă`0 Áhâ٨†‹‹Ó;űŻ?ÄWßyJg›› :ĐéDŹ€ůŁ{Üňˇ†{IwJrË„¬Z°hzo/źěÜĎmë›źŠ©×€X­HŚM«ËĚ•jz¶z„„@§ĄîŚŹJ®w°źĐA+ŻŐ›Řtܸĺ5‘pťlMu„Ń›ŘtÚ¸e»H_.Éőƨ†ü7ľŮ#y•rĂ›ďt¨KąšPo,{j2 Ě"w˝iőĚ}ŻÎÜńS¸µ[7\ ĄUóç/ěä`eníőŮg;÷L¬ĐmY;{߆i;~ ·íÜè¦ĐÜ{ŠŚ¨w/÷[˛c^A”§·‡±Ăś`?—w׾ź(‡Źö·mů¬ČŤź5Ç Rž·ú¬›‹-vs´6·öÚ»wÇ`?Wű™îBY÷^ýh˙Y©WVW©g.ŰÇéÝuďIzB—ϸ˛áť˛*)6MÝÚ3li“Ń ˝ŃâďGŔ§÷~”d>jŚĺµß3ś–Śňź»Đ)ĐÇńŁWßK¨€O~ż&dšc‰›‹¬Ä»w?n@HňĄýRëźľŘpëAJřŢő”Š€ő:u­ZŕjkÔš­‘ ťÚűQśń1–׎ßkż5d_ëDňŹ“ł1 Á`üóěŇM)śţíŕŔÁ‡Ă"ĺeUVT`™‘ô×ńłgšjr »5WfNŽő:á“ăî^š3kÔžľ<8) ů)$đ )ą5vPĎ&ő/^ŹQJyYö­‡9VÖ_ívňë*ňF&ĘÂÄA~*bq-ăľ Ź1V& ě7˘ć%’GŢK3i\mŁ˙ü¦ŃÖ;{ _ëP¸pöśeßś3ćžJ˘ťűxue×®G@]QNµNďáříŽĆ ŮyĹ6ÔÔëűvÔĄRuaVĄVçăo˘”§$–€h©Ńhy[wW•˘bHČĘqĆ2ENZzq ‚ć4{€1&DěŘĂ˙Ŕw’ć¤S«yswŹâŚ;Ĺ8-ŁĽR‰Mm:yŞňÇzŠŠ@CQLP«s̰Ą ‚@ ‘N¬µîŤ?ĹUyôő„>cěpIčęu1ĹcúdY„´‘Ă52ëŔ±Ă«sNž:wóv¶ ‘yíô‘“aq±ń…%ŠČ‹«Ŕ±Ă•ą GŹž*©®­.É‹ą§&úÚšŠŘŰwň˛S#˘â\üú í×+#áĎ“§.”+TĐt\LŠÎÜ˝[făĆŤ”4'%%ç>Hş™€mMrn¦yöíťríôÍŘ;‘ŃńÎ~~ÍzĘJŞŻ———µ0Ç Xz#ý‘4ÉËĘĘ._ OLËډŞěŢxމ:M}VjBz~aA^ZRZVAaNäŐ¨8W˙!~=ÓăŁĎžżš?ńFjIpP@IęźÇŽ˙~3&¦´Š:j¸^ž{áŇU˝‰ąX#ŹŽşx7»jÄĹ®?y&&&¦¨¸äazňýĽ‚‚Ľ´äűŮ99÷ă“ÓŮ> Á`0 †Ař•Ęj§ď'K˙‡0‡Ŕ@EŚĐÓĹŘŰš„aL qíÖ38p¨Ę#˙8sřě$GscŐ6eňúŃÁgÔjÇaJáÉbŚ^.  €&‡ř¸Ű>L‹?Ůjľě¸ţ©‹ç-]CNnŕu/ĽÂ޶ŮđX3BRbbĺýÍ·»cŻ„u8}Áh÷€ÁţiezŚů»)ŰbiۼцW=ki›TýÓ1d0 Á`@cT‡Đ“6ÂFzQ©.'5Pµťăx ˘Tńs"Ő$Ťąz›žBçx DŇöBϙՅŠ:ŤND€0”ňçţ(ĐhtO6ĂJž\ľ<’˙Ţŕ#©h}ó¶Ő`Ś‘(Šă€B€ă°ôOŚ!"!ôą6ÍŠĎsRŚ9T$„ăxJE‘ BnÔĚa,ęuنîŢ<ĚŻA™»ďÝ=IYR]ÎgőĽpŽ=k)Ą”"„9žŇôTŰĽń ă%´1–A^/>o)ć8JB@µ"žç  !ç1ĄzQ”:†ć8$ęEŔIżź'4˙LÁ`0Ś˙Ł ÔŘޱS;‹śô,=€Ł!#FY𚨨?´"'XvďŃĹX@‚¶®$-˝@$`dí2fÔ0®ľüRä5ŤŽP™µKĐc‰&!ŽăŘB5CŽăEQ0{ŮwźýKýđvHđŚŚ:ŃĄŁ—©Ś)á9ľ˘âQUUe÷^˝ÍŤx‘^J=,(*ďŢË÷e“‚PVô0ďQĹŰĎ®žčçÂ÷3—żĄk¬+óOĆţ‰QľÖ mń–Çń#J‰(Š”ÇqŇĺ)\y˛ čt:kwź9ýÂ.ŃSžR˝č")Q$¤…ű*Ť-žÖÜŘ[„ҸîFĎ ćX[,m»7žę0ĆŇ6Ą!DD‘P=ÄĂß~Á©‰Q§ďćČ ]—>łý~˙áÇ ‘ÂBBÝK‘bĽg4˙_8Á`0 ĆËâ6żűUV©Ţxî©´x aĤ9;víZąp¦˝%ŔŤ;-tűëŰ·nčĺé?nłjńKŢÁË2î%.ž43tÇö±}{HŞŚÚą/]żm÷®×üĽÝ wńĂÇN}R3tęá»uç®…ł§N0Áł-·÷xJÚ·4ÇÚbiŰ˝ń”fŕŐoÄâĺ+W-[<~ÜÔ­[7tu±cű§zťzôÝş}ۢë“Ó2WMöź~A+V®\·|Žť €‘S—©Ó&[sH2cz—ŽŽî˝‡,Y±ŞYł§łŐ?iÁ`0Ś—ęł.pnôâÍż X Ŕ°^CŞ˛bÎr?|1Ź‚Ö×ÓĄ–!3»Ť;Ţu/óŘ‘… ;§ü´›é…őbYBne­_׾é¦6ďß’$9•j˙n˝ż…8™ç ďŹ˙˘Ľ~$"궦ŐKăó T#Rż~Ă&ę;µ·2ţóň UŃ`’vV˛ż®†W×`˙ŹłübDŔ0źÁÍŁLHC﮾žL MÍ€Pزç‡÷ÖNŠ˝ßĹgT|AľŢżŻ8ő6g×uͲůÓ'%ć*C?ţńťŐcďÄ{öŢ(hŠNeWÚvôŢąçŤZÎÚcÍÂńúÄ4¸ťľpÖSäTÁňĹs—Ě™RŔa,6ŢÂâ(-Ľ‚ö}µ·<=ŽłëşvĹ‚ نćň^aá—­j «¸%ľ]ě—O”UeyúÂw,—ô,ť9ăĘÝ\ó®-αÖ-Em«7žť­ “WĽą{–ף*0‡Z°věoU?gĎů“çĂ»ŕŠś*XątţĽńŁâęÜχ‡›+ó•2+/' ˝HŔoؤĐMÓLęŠcNüˇĐÖuëtđ›ĐŔđ°jŇů›~~wăŮą)žŰÖŹâ«ď<Ął­ľˇ¶¤6Ăç$éâlSݱZ‘›V—™K¤úé-ÓX!Ş%xĚôĄ~Á´˝9ŢV“U¬ÓŽIÚ™ĂëEw3 *ˇ©JćĆ“Ł\o,{Ş$+Â!˘qÇŃ›VĎÜ÷ęĚ?…[»ułÁµPZ5ţÂNVćÖ^ź}¶3`pĎÄ Ý–µł÷m¶ă§pŰÎ=Śj Í˝§Čz÷ňqż%;ćDyz{;Ě ösywíű‰rřh˙wŰ–ĎŠÜřYsĚ |}ÖÍE‹»9Z›[{íÝ»c°żź«ýLwˇ¬{Ż~´˙¬Ô+««Ô3Źíăôîş÷$=ˇËg\Ůđ‰NY•›¦nm޶´É‡č…Ţhń÷#ŕÓ{?J25ĆňÚďNKFůĎ]ččăřŃ«ď%TŔ§ż_2ͱÄÍEVâÝ»7 $ůŇ~)ŹőO_l¸ő %|ďzJEŔzťşV-pµ5jMÖH†Níý(ÎxÄËkÇďµß2L€Żu"ůÇÉŮ Á`0ţ‡‘îe4®,O¸mťł×”µ… gĎYöÍ93SKŞź5y˛®ˇ^řäř»—ć̵ç‡/N 8ů(/K^QaÝŢćŇŢďľ›7I’TVT MÉ­±z6˝®Më1B‘ą@÷lť·˙\˛‰±q}C6íe‰Lbl$Óju<=˙ü¦ŃÖ;{ oecî©âE! ĐąŹ‡PWvízÔĺTëô>ß8čhÜťWŚiCM˝Ţ±ogA]*µQfUju>ţ&JyqJb –Ť–·uwpőP)*†„¬g,S䤥— €ćj0cBÄŽ=ü|'i.A:µš7wp÷(θS €Ó2Ę+•ŘÔơ“§J!¬§¨4ŵ:Ç [Š ”)§rëŢhń÷ĂS…\ĄÔ*ëŚ9yQ°věd¦’W Yl,«ĚJI“+]Ľş–fÄW{©Źä*oŚ—L8ĚaĚń€†JŃ Ž…\Ą4UÖcJŚJýa Á`0 †x€Ć•’NO´:­”ů©XYfgeóů®ů—ďi÷y8‡©äDŞ©('PĽ:{ôf;tëöP^ P˝iÖ¨­×()/#[Ź];6ks˙ü×WÇôm«ĘA)EQ1čôz@Ŕ$ôz=ˇTÚRűO–·”<=ʨäŇëšGĘ Ľ‰eO7ČÉŕtú±ÓVtŔYÝ<ęťűg&†Ë®¬PΛZImó:"ĄKF#”’µFU§UĺÍ TčdŤIŰhcŞbŚ)!<˝Yó€ěäó‚®^Y\dçák :w´±° uµeťVőpnĐIF”¶n»ß58Ç Zz^]Ćřô˝’ŰÇ–‡~¨‘7 ¸D‘€^×P]-:#"Şk4*˘­É 3Fˇ##M˝fŐ“Ěí; ÎŢÉÔHhNЬÖč4š:˝tÁČśŕÍeEB(}¬™Šúćg,®a0 Áx "ŢŮűăG[×Ú´ďtčâĄuSG^<ńsJqŐŔásŹüň«‹ľđ«źÎĚ^űůÝ[»·íľző„ŤöŃ…KŃ`ÜëĚß]ô—SBYňńß#±™ď™°&Iiâďç®ďŕ˝yÓęUó¦µ~ŻťR*ęuŇŠ !ľ˝˝©‰đÄ“4IoÓĐ­HŰ”}řyH âň©äą4Ę ů‡Ă.Có)@J 'F»•˙ůOvnÜrülô˛Y©©Ií;úîÚzččÎvfV64÷JS›Í'¢W‡ UŐ‰.N<¶kçŕŘ^vţäˇ:Űá'ŽlX·):ćöšc€ç÷C¤ŘăQa¶Ť‹Ď®íˇ‡Ž~ëŢľťµŤpéĚqťÍ đs'ŽzĎ΄ČLŕâéźŐOęY8L]ű´4Ç ZúKXXÚ:Ťŕ3ÄŻ'W.źnĹĎŃřK;K3Sscc+·NÖáż˙¤¶~ńÄ‘ ë6EÝŚ]5gŘńsGM»ŤůéŔ—÷čéę„1°ÝĽý­ťkB:¸şďüŕ“ő ÇÜMÓZuzăŰďě˙Řͱ=ćx +«Fͦ¦¦íílßËň0 Á`ĹUyôő„>cěpIčęu1Ĺcúd•„´‘Ă52ëŔ±Ă«sNž:wóv¶ ‘yíô‘“aq±ń…%ŠČ‹«Ŕ±Ă•ą GŹž*©®­.É‹ą§&úÚšŠŘŰwň˛S#˘â\üú í×+#áĎ“§.”+TĐt\LŠÎÜ˝[făĆŤ”4'%%ç>Hş™€mMrn¦yöíťríôÍŘ;‘ŃńÎ~~ÍzĘJŞŻ———µ0Ç Xz#ýčuš˛˛˛Ë—ÂÓrc˘*{7žw"€NSź•šž_X—–”–UPy5""*ÎŐß_ĎôřčłçŻćßOĽ‘ZP’úç±ăżßډ)­â†Ž®—ç^¸tUob.ÖČŁŁ.ŢÍ®1b`ńëÇOž‰‰‰)*.yž|?Ż  /-ů~vNÎýřät¶OĂ`0 a>ü>}%ůů ʡ§j"„ž˝ľŢZşł§ÚaL qíÖsěčľN~účńrAc„ë6ľ”IJ)ŕ€É!>î¶ÓâĎGŢ&m;×÷Fąą×˝đ {ŰfĂcÍaJ‰‰•÷7ßÖeŕôŁÝű§•é1Fäďm‹ĄmóF^…žű%´AŐ?CÁ`0 FËd2© ĎóśT„¤G*ťţd-F)Ţ0¤¦±ÎúSmâyoŰÝëĆgn>›#&yRň_¬X‚~\łĄĹ:aŽăsĆ5˙“çyŚ‘ˇ6Źźnn1Ça Ç?Ýćń›š•‚€¶ }çÓȨčóg~ęă)˝Č€žαg-mě3/<ńTŰĽń ă9Ś1ć8cĚI#ő|1Ç!„đc­çéÇ‚yžo´#„0ćÇ’CźÓĚ`0 Áh„ŔŘޱ[O=–Ř;vëŃ•G m鸸u™1'dlŔ ™´t,{ůöőďççď?ŔÇ»Ł´ 4˛v™8=dʸQFBsůBÄq<×r5CŽă `ö˛¬Â˛ä?Ďy™É@fd4iĆĚIc­›ěň2&km$€°űŕąŇ’âsßż+4„üűĂ€ŁaăĆŹ2–áVZr/ĎsŇŞśă8žç›Ă•'Ű‚Öî>«V,u2âĄu}Ó,Ă<Ď ßú>6¤ąéqÔA=§çs¬-–¶ÝOuc©Ă’O02ÜCŚ9žCĽ‘ý’+{wn' Űuéłfőňö‚ôŐsÜc÷"„!Í Á`0ŚAśĂćwżĘ*•ç§^ďafď¸ůť}’¤§Ą)Ś™·¶˛¶>÷ÁE•*ú·/ídÝĆĄTäĺ>*S¤ţuŇŔ̵WD|fia^‰Bńý§ÖŤáQ«p<Vľö(/e䀞<@N˙>~Cˇ(-©ß>˝ż§Ż™¤B~űô×®< [çžÎ'fD˙"ećţű+^éłö‡UUĘ‹JŠÂöď±0<^¨uíMrŤm0Ç@ďië•ů}ÍxŔz¶Źmé1B€žß›úŰzÚbiŰ˝ń·č!B! śiŻĽ ĺĆţŇź<Ç,ÎL‹÷5ĺ˝ôBÍ Á`0ŚV°đžrëNüŮ“g ˛}ĚŤŔŞ×´Űq’¤·ąx¶bŃl3ßń[Őšú%#ÝŔ90űaÎŚÁžvŽ®íí`^č~uUNOW“.~łTuő«§ú€ĚŘaĘĚW&Ť .ÍŁšŻ=H‰27˛Q§©ťŇŰŐąg°Z¤ŻNňó»ŠőL˘ŇęwÍ’ü¶e_DzôĎ­g–k )ö輬Žh– č2ié;”’ĺă}€zÇ!$Ś4gÇ®]+δ·ä¸c§…n}űÖ ˝<]@:±(µYµxŽ%ďŕe÷OšşcűŘľ=$UFíÜ—®ß¶{×k~Ţî†;Ťřác§>©:őđÝşs×ÂŮS'LŕŮÁŚŰ{<Ą í[šcm±´íŢxJ3đę7bńň•«–-?nęÖ­şşX€±ýS=€N=únÝľmŃ‚őÉi™«&ű€Oż +W®[>ÇN†ŔČ©ËÔi“­9$L™1˝KGG÷ŢC–¬XŐ¬ŮÓŮꟌ4Á`0 ĆËőY— 87zńć_¬ ˘ę3/xF’˘ŇcoÜŹĂ ¤4·¶N€)6µÝ¸ăýQ÷2Ź9XXˇ°łqĘO»™^X/–%äTŞý»őţâdžľ?ţ‹ňú‘¨ŰšV/Ť#Žç1@ĎŢCJĆ^N-l ĄŃiyýzű×k}ĺĆ1IÔ˝‚ÁľŢđK‡eü?Îň‹!Ă|WeĹś+ä~řb! ˝»úBx2A€45BaËžŢ[;)öN|ź fPńEůz˙ľâÔŰś]×5ËćOꔫ ýřÇwVOŚ˝ďŮ{Ł ):•]iŰŃ{çž7j9kŹ5 ÇęÓŕvúÂYN‘SËĎ]2cJdJ‡±H ĚQ"Zxíűjoyzg×uíŠF Íĺ˝ÂÂ/[ŐVqK|»Ř/ź<(«Ęňô… îX.éY:sĆ•»ąć][śc­[ŠÚVo<3[1˘„Âäoîžĺő¨ ̡¬ű[ŐĎŮsţäůđ.¸"§ V.ť?oü¨¸:÷óááćĘ|ĄĚĘËÉB/đ6)tÓ4“şâ(´uÝúü&40<¬štţ懟ßÝ8OŰ}éuVćP VöŰ‘Y;ľĚAsáÁ`0 F<PQF2ľů.Ĺ'$ŇugNKě?ý죆¬¨?®ĺyz¦$Ü)Î-˛jÁ˘é˝˝|˛sS<·­?ÄWßyJg›zŐŠÄŘ´şĚ\ ŐOoJ‰`bďPWšG  ŠŠz 'S3Ń^]RŔ$ M—v< @Ú”`î y•rĂ›ďt¨KąšPoÜtŤ§ńo#D4î8zÓę™ű^ťąă§pk·n6¸J«ćĎ_ŘÉÁĘÜÚëłĎv î™XˇŰ˛vöľ ÓvünŰą‡QMˇą÷Qď^>î·dÇĽ‚(Ooc‡9Á~.ď®}?Qí˙nŰňY‘?kޤ˛0őY7-ZěćhmníµwďŽÁţ~®ö3Ý…˛î˝úŃţłRݬ®RÎ\<¶ŹÓ»ëŢ“ô„.źqeĂ':eURlšşµ9fŘŇ&˘zŁĹߏ€Oďý(É|ÔËkżg8-ĺ?wˇS ŹăGŻľ—Pźü~MČ4Ç7Y‰wď~Ü€äKűĄ<Ö?}±áÖ”đ˝ë)ëuęZµŔŐÖ¨5 Z#:µ÷Ł8ăc,Żż×~kČ0ľÖ‰ä'gc0 ÁřFş—и˛DM÷$ž•PQ§Ą¦źütb’—~ôŐr.7jĆäk˘^'|rüÁÝKsfŤÚóĂ—'‹|”—UYQ4%·ĆęŮôş¤˙•Ţ«WkećŇŠ—7ć žŠZ­ĚÜżěĘaĐ>á®˙ ˘­wöľÖˇpáě9Ëľ9güt1„čÜÇC¨+»v= ęŠrŞuzźŔot4nČÎ+Ć´ˇ¦^ďŘ·ł .•Ú¨ ł*µ:ĄĽ8%±DKŤFËŰş;¸z¨CBVŽ3–)rŇŇ‹K@s5Ś1!bÇţľ“4— ťZÍ›;¸{gÜ)Ŕiĺ•JljăĐÉSĄ?ÖST„Šb‚Zťc†-EHJ”Sąuo´řűá©B®Rj•uĆĽ(X;v2SÉ+†„¬6–UfĄ¤É•.^]K3â+Č˝ÔGr•Ŕ7ĆK&ć0ćx@CĄhŚÇB®Rš*ëŚ1%Ć@Ąţ°€†Á`0 Ă qĄ¤Ó­N«#š†’DOv|ţő+6ŁF%d–bŚ(Pé;Ő‰T“(^ť=Ú±c‡ +¶č(÷°ĽŚl=Ţ˙b˙[BÇO-B)(ÍKµvéáÂN~Ýś=Č{qßε‡óË.qî×Ő!/Wţź/l) (Q–ŮYŮůtÇĺ{ÚŢÎe*9 ¦uł´ŹQV¨ŕM,;xş'đ0vÚŠ8ËŰł{Т*– \Yˇś7µ’Ú怢E@#”’µFU§UĺÍ 9pđŕ€ö}v‚g!¦<ý ÍDtőĘâ";ŹîÖfÎm,,H]­FY§U=lÖóÚç')€`ëÖŇ3hiąş <Ś»ôWüˇĎ^ăĽĐ\˘H@Żk¨®Q]ŁQŐikňBĆŚ8hđČ€áď}}B]Ů`nßpöN¦F‚^ßhµZŁkĐhęô:Đa#sŔČ\ĆaQ$„ŇÇš©¨o>xĆâÁ`0 Ă&ďěý1!5«¶¦:öú•ő3ŕÝŻ%¦fŐÖT'ÜŚZ8dbČJ餣żś˝y5¨ź÷´ĺ{Ň˙Ü˝m÷ź÷ *ÝëŐŃŚ{ťż|őĂݡÝ/ÎOľěn‰Ŕ˘×45ĄE·ŽLő$e ż|[zr¤™`ď9:«LsěÇĽPö0eH'SS×áąĺZ&)ÍIŮŐ^ňŰć/.ĄEúg٤”\ŘŮ;1żâĆń#GNÝ®.şçăjŹS*7j5˙*ü먱”OŮągkŢx΅ҰŹÎ&ţ¸yŮŠĐź#˝±nŰܸßÝ<e”ibOٰnÓÍÄ»«ć łî,×Ń_|yčRĄtĺDŰÍŰß:t&B­RŮ·oýÂńćçV5ś˙őűGŁ)Ą‹fŹßöăµ#ŰVJš×lýćQę%ˇ…_Á`0 ŁqUěÖŐSž“záŇŐŇjeVb|zA‰[7ŻŠś” —®–*«2’ňËŞ ‹$¦˝ÇÚbiŰ˝ńT‡1–‚J)B"ˇz1‡ľý‚…SŁNßÍ‘@».}fúýţĂŹ:‚ĆŇč^B Ä=§ůźüÄ Á`0^ç°ůÝŻ˛Jĺů©×{Ç;n~g_ŁÄTÇ@ď‘iĄő ç@ŔĽuUęúÜUި_?wć6~đMum]^ÁŁâô[˝ÍhĂíf)[Ŕ„•Ż=ĘK5ČzŚ^PX®ĚÉÎ|T\žsďV_ĎöîCćÉUL’u÷Ż!}<ŔÎĄ×ó‰ŃżHYżţţŽôŮűĂŞ*ĺE%Eaű÷XHr-Q«Šžęćxč=m˝˛"żŻß8Đł}lKŹ„đł-˙¶ž¶XÚvoĽđmz’Ôq¦˝ň*”gřKňł83;˙KG"—IDAT-Ţ×”¶$`GÔ Á`0ZÇÂ{Ę­;ńgOž-ČJô17«^ÓbnÇ5JLŕ1ýűôźšúú›çľ€ný‡Ż\4Ç ŔwüÖ:mý‚a-}&UŐŐĽ>o0xűúŮ™'´ź2ó•IŁZĽŇŐ¬xíAJ”ą@ŻqË+*ŠCFuqő›ŻĽöăď€Yň2&ąť[yçäÇŇQ¤-_]IŹţůźĺ@“bŹ®ÁËęfÉ€.“–ľC)Y>Ţř¦SX aĤ9;víZąp¦˝%ŔŤ;-tűëŰ·nčĺé?nłjńKŢÁË2î%.ž43tÇö±}{HŞŚÚą/]żm÷®×üĽÝ wńĂÇN}R3tęá»uç®…ł§N0Áł-·÷xJny޵ĹҶ{ă)ÍŔ«ßĹËW®Z¶xü¸©[·nčęb ĆöOő:őč»uű¶E Ö'§e®šě>ý‚V¬\ąnů;#§.S§M¶ć0eĆô.Ý{Y˛bUłfOg«2Ň Á`0/<Ôg]4ŕÜčĹ›°‚:¨ĎĽ8xŕ™F % 'tňŞ÷Ćx‡~»> “‰1ŔŘż˛0()Í­«×Ő7ŕ€ĂkŠ3ŤşŹ?}jóŻß}ś–ÜF]|üĺő#Q·5€˘-ťŁAX:ÂD)PQ§Sfe_MĘ™lď,ň´Z­ŠI’˛çÚ»Čę8C˙ô>F„ ó\•s®űá‹y„4ôîę áÉDŞ~ÚŘ …-{~xoí¤Ř;ń]|6AĹäëýűŠSosv]×,›?}bPb®2ôăßY=1öNĽg¦čTvĄmGďť{ިĺ¬=Ö,?¨OLŰé g=8EN,_+răgÍ1Tε>ëć˘E‹Ý­Í­˝öîÝ1ŘßĎŐ~¦»PÖ˝W?ÚVę•ŐUęŔ™‹ÇöqzwÝ{’žĐĺ3®lřD§¬JŠMS·6Ç [Úý zŁĹߏ€Oďý(É|ÔËkżg8-ĺ?wˇS ŹăGŻľ—Pźü~MČ4Ç7Y‰wď~Ü€äKűĄ<Ö?}±áÖ”đ˝ë)ëuęZµŔŐÖ¨5 Z#:µ÷Ł8ăc,Żż×~kČ0ľÖ‰!`÷k Á`0žáń™Jjş'ńXÂqo|·«M±«w@ßN&öžó& Á˘F«7űäЉI^úé ×5XšŐ•¦Ż Y´zń‚«ŐŁ{€ú˛;cőś¶ęm´y=F(ŘZ·Ó*r×- ys˙%#SK˝^´yé%kĎÚőĹ1®1řüO#m˝ł×đµÁÝßر«?ťCL:ěÔąŹ‡PWvízÔĺ<Ě-ňđüíżüřĂ%óĆ`ÚPSŻwěŰYP—JmÔ…Y%ĺ5&¦&JyqJb čę4-oëîŕęˇRT Yą+t˝"'-˝¸4W‘.ÜwěáໟľüřĂ%ó‚N­ćÍÜ=Š3e”W*±©ŤC'O•BţXOQh(Š ju޶!á‰h­{Ł%xž*ä*eĄ˛NËČ‹‚µc§n*yĹ»¶­«ĚJI“+]Ľş–fÄWßK}$W MyM8ĚaĚńP)ă0ă I3¦Ä¨ÔĐ0 Á`€h\)éôD«ÓꦅˇNO´:ťN şňá˝lëkWŰÚZ–3'Žţ叛Ű?˙zţH›QقӋ B­Đ‹ŇyމtCZ°ëüćë›´ąţë«cúĆß5˙ň=í>ç?Tr@MŠĄ}ڞBobŮÁÓ r28ť~ě´pV7Ď zçţ™‰á2++”ó¦VRŔ<€Ž˘E@#”’µFU§UĺÍ TčdŤIۨ(˝ cJOoÖ< ;ůĽ «WŮyřZÎm,,H]­FY§U=ś4FŇĄ‚­Űî7B Î1–žWW€Ç€q>}Żäö±ĺˇę_ä .Q$ ×5TW‹Î€ęŤŠhkňBĆŚQčÁČÄHSŻYőĆ$sű€łw25ôúF«Ő]FS§×€ ™óĽąŚĂ˘HĄŹ5SQß|đŚĹ5 Á`0Ď@ÄÂ;{ühëZ›öťŽD\^?3HŻ×˝űŐˇŹ·®µißń§‹ëgţ`ý°€€ˇţ«*nM]őîô•îŮĽHYĄŢşëó[]ťŕöŰ‘RÎă§_~řô›ź‚»ŰF%ßcÇž›7­^5oZë÷Ú)Ą˘^'ý7'3qrr°4Ă <“4K¤˘%Ҳ–6hEÚ¦ěĂĎCD —O%Čź{ä—_ň‡]†¦Ă`@)AňÄc·ň?˙éÂÎŤ[ŽźŤ^6+ 55©}Gß]ŰC=ĐŮÁÎĚʆć^ijłůDXôꡪ:ŃĹÉŔvíŰËÎź_Ö ©.yxýŻ[ 5!„PJő˘N^”sýĆí˙ş„‚ľŞäáŤ;5"Ć Šä˙î]˙ą„Š(©°r÷×o­šVW’öÝáł"`„ţî|Š1"Şňčë }ÇŘá’ĐŐëb2Š9A©Zdă˙8J"/†kdÖc‡Wç$śHş™€mMrn¦yöíťríôÍŘ;‘ŃńÎ~~ÍzĘJJëĺe‘W.&¦gÓgŻ÷°ôFú#Đë4eee—/…'¦ĺĆDUöŚ7Ć´Ő:@@§©ĎJMHĎ/,ČKKJË*(̉Ľçęď?ÄŻgz|ôŮóWóď'ŢH-  (IýóŘńßoĆÄ”VqCG ×Ës/\şŞ71käŃQďfWŤ1°řÁőă'ĎÄÄÄ—ůťˇŢ>ÖŚ¦”XyóíîŘ+a]N_<ÖsäŔľ©%:Śů»EG[µ”çy"ŠDSj°Mk`„?gĐŇ6¨ú˙ĎŚg0 Ář˙ c™{×WdĆćRЧÓŐe§˙Ś1×\×\l:Š9#Ş×‹s‡ĄB Š"!cN:µĂa,JËt„xŽJÚ¸j‡ćő®¨bĄřbIÓŻ_ř” ŕŮÝLM˝VG(`i4â‰ßsD(Ń“żóö*yv˝ű7,ýo€1G)@â…ľóňFĆŤ1ĆzMC⯇EťŽă8Q1Ç%„Çaéź!BDB(Bcô¸ E|cÄ‚xž“Ú`Ě! "!ÇS*L’¦ ć8cQŻ#Č6t÷Îŕáý*3ŢßůţíʤЖŤz0<5kŠŤ0ŹyJ‰HDŞŁuájú\d×d)`„šŇIc!Ó˛9ĺt[&+PŠ1‡Ąa ŇOăK1ÇQBÚţ˘ćÓw€˘EĚaJ(Bă¨cD)mŚ’5˙÷gÁ`0 Ć˙H,ĆNżadlGâuZĺĹSőşZŽçQlqy‡0–?a„(%˘H¤%8B Š„Ť˙–¶€1‡A|ű §&Fťľ›#GI+Z TEiAĘqB(‰H©=ŹŰ ¤×é¬Ý}ćö ;|¸DOyJőOl_!Ťýiě9JÁĚL¸ziŞĄĄLŻ'ŔóXĄŇŽ>«Vëxž§­ÚţĽŤť ˘H(p&Dlţô/E€Riˇ*Š"'ŘŻ\ż´ť)Ę˝űŰą«Es?ĺ±çlçŹ)ĺěć˝2Yz;´ëŇgV ßď?üXˇ#RŃř.B¤5:ĆăĆ@T:ŢÖä D)527ßđgś±•5ŃëóB˛ú«ţšÚZž(%b«q)ÇńKÓ •ń’JîoŚ—´;e2{ţ¬ĚKÉŮĺ!Biăbß9üäŚĚ1Hł†C´–”-)¦ő´őŮ‹0F„pÖÎsfĎěájsî÷ĂwîV­ßşÖĆrďÝ9vöŞH:‹GD‘pG !ôńč€^ŻoĹŠ§~ĂŔłE˛â˙Ý;Á`0 Ć˙,Ő4Čę+¤˙i*4 riżCÔ‹­,ë€"Š"E˝^ßt„‡čőzťNOi^âëőz˝^OE¨yAO)ç°ű_źŚđu#J^Ż×7†4!$JŠŤÁ3z¤FRiŃÜÉwřÇ˙ÚíÄHçÁ4÷GŻÜź§úO©BŃ —7( Í˙!­őEQßZ8gČ Đř.JĄÍ«Çű-@EQ$„˘ŘÔg\şxĚ_űúî-KůF͸ŃŤ–öˇ¨×7htzhßüv°qď˝yĂj5żQz—H @Bôz˝$ň”mň†Z!WW”×Ę+jĺęŠrµB.5Ó‹úVCiĽô:ťNŻo}Ľô:ťNlóx!„Ą¤ĎśI·ĎľřzxďN ĹEÍUiÄj‘T‰b•(V‰¤J«űŮúb `Í_úĆzďnÝ쬰sŹůk¶ďŢĽTĘ:@šcl±ń!z<:Z·˘ůW ÍýÓłcvëźÁ`0 ăżńóhÓ ”čo;yÚx˘Čľu›@:>$™cjĘ_>?%ćϙׯθ~uFĚź3/‡O7Â`ÔuBK¶#Ś ˝çäÜrÉ Ť˛|a`w°t˙+˝ 8;9)ł8ďî˙ˇSr‹ĺ«Ć÷”žr˝üŃĂ´Đőëň•úÜŚ»‰yŐů©CÜ,ĄżnÝ•}X &xďč{yĹŮɉŹ Äöp@Bçŕ”¬üV|¸lBX˛ů«ůŹ ŇcűXš@ŻńË+höýäÄŚ+răg=›ú÷iÓ)â@_­HŠMS˛c$ž¶Đ•Ôłw?n@Č˝Čďęję¦,íë$öďꛦőĘĚĽ5˝żí­9ÜűN^>¸@ť+ËŇŁsŞ·i«6/÷k’CNîż=nć݆¦´rŇ"wĘÔĹ~NúAÝ}Ó4ÝäÄ-™˝ĺËă‹—.umgaŢ‚ĄĽČ?}±áÖ”đ˝ë©TÚR©Zţć2ÉW»y{Úy,qJ»÷ęGűĎLşx@lÓŐsŠ8DŞI±÷ ză˙lĽĐ§ż_2ͱÄÍEVâÝÂlia)`«‰±ig/%"*‹ 7ôÂÁ«‰üŐo–üë ćyžĐ§v®DťşV-ŕZu}]ťě{őëŮéűm_ŰŃŢÎR€1oő[˙ŰV0 Á`0ţ˘JLµe±cö||çÂĐĂ2™qyQúĺ»Y ¦şŇÖĹłV!2gĺ8cYŐĂôÔ‡=şůď˙öGg#uV^ Ň©ëx3űNîe™ ĺô^ę#ąJ*4Ů Btîă!Ô•]»uE9Ő:˝‡Oŕ·:7dçcÚPSŻwěŰYP—JmÔ…Y•ZťŹż‰R^ś’X˘ĄFŁĺmÝ\=TŠŠ!!+ÇË9iéĹ% ő $@ x}ůí±z6Ůüt˛j"@»'¬(”+9ÎŘŇŘVQś•Ż&f™ÔŁ›×r_ Ü×uD7ěg’fT˙{Cť¦^%ż›P ˘EN4˛hר“C@c–Ćvň&=ńЬŕŕä·˙»CŽ|m‹>l:3fÂacŽ4Xj«Ęî&–±Ôh´ĽťkűNťŠ3b‹pÚňŞÚć§ZuÁ2SmElĐ žMnxÚ˙Găe"«ĚJI“+]Ľş–fÄWCłĄĄN#Ň”ß;¨'Á'1ÂĎúAJ @)P Ŕˇ”ČL̡üĘ®,}ëŘ‚×UGľ[˛ý$5ŠŇ˙ľ Á`0ŚżˇBDŻĺmÝŢ~ëumε}uLßB5 JžP„0BTj­ęáÜ ± =™ię5kvwEÝ<ęťd'źçuuIą™cW öN¦FÂ3ÉjĄ/ĺe… ŢIJ§ädp:ýŘi+:ŕ¬nžAőÎý3ĂeWV(çM­¤6€y!D/Š€G(% jŤŞN«Ę›¨Đ/Č ´Ő= „¨^Ź­=Ţ{ű5ŤAŰ1BęKËÍFt’¬03DQ+bŤąĄť%‚ZdĺćâXŞäř«onü¤řÁ­űÚvó&ą˝ł.IĄuBTę!&˘Řś“ŤŞµ5ţKÄsËv–j‘u—v7juc§ŻpĹ™žžŁ_čCµF× ŃÔ鵄čő"BŔ ĐP§*.¶óđµť;Úš›‰¤ {5˝–łv{çmĂ3á˙nĽ¤ůłęŤIćögh¶´<‚T°îĽűíPmÎź­Ě^¤őđ8wóX4Fľ:ÁČśŕ-dčEßŘ0őí-¦WďţőăťśüęNM=Ą˙V0 Á`0ţ÷#„ ŐÉś|6żşjŐĽiR>(d(c“‘©ą};[ ŤµáĂOý¬¶~âȆu›ŁcbO–z/©}Gß]ŰCýÖ˝};+KîňµłĆťGýtŕËű?ôtuâ8îI…” OŚ8v+˙óź.ěܸĺřŮčełRS›őčě`gfeCsŻ4µŮ|",zuČPUťčâäŔ`»vŽíeçOŞł~âȆu›˘cnŻY0× 1ÂD­ŘN(€?Ď5Yáîä`iÉG^k°ôýöÇďýńŰţíÄKWŻţ?íÝ{lEđßĚě^-E•đĐH‚š¨$j|ŢbůËFč?B4!˘Ń˙ &>bDcLk -´@ˇíu{-¸–×®iŻ/ZĘ=vfücŻx´wW©­ü~ţąěěîěĽţ¸}ĚüŞš33'žŢżł¨´~b¦»ŢŞ0ďČĚš2Ů "é.'ϵf>ňÜŽť«—Ď/-ř14an,·**)iédN{0Er®2×®ű`ýęĺ3ďśőţ†ĎŢxi‘+ÍĚĘr›­‘ĺvíţmktŇăĹ;~ŢňýÇS2dTżc\EŁiÉ[cěú«¬ÜZąlÁ¶[ĆÝ÷L˛Ń’¬Ě%×ô9k׬J:z6'NvOÎHwR4×úbgaÁ[//đ[µŃIłŢűúŰo6}2{ş›ěÍČ)·Şß]µ"űţ»˘=mĎ]ľmüx·{ʨ×nŕÂu÷=Ë„HSĘÖZ1FR†Î6ţ Ł˝íÁ˝%ŵ>âx´Vv´ĹĘňÖ)­9g—ŰüĄeŢ™ŮŮół¬÷(ܵÇ{¸˘‹ß‘—·đÔ_7ç=Rw¦Î:|"÷LnŕřÁ­Űň+1uęĽ's§§őôţš’ş'jo9SiyGX  ©˘pRŇOw’ä5dÝdJ°,2ýťŘCK¸ đ0Y%*Ďuç¦Â™:óˇ{§\Űv^ hJŮíJ”Ť¬ ăŽçZ«ô |őő‡ÖŢÂŮŹ-}ő©YąóńµÚ±XŚQĘ(śĂ^rŚú+Ő)ŚHǧ~7mpÎ׺_ÝŕŕMTňasHtŔŤŐF!e¸ˇî a¤k ‹®dXÉ0¦AÎä‡$ś8ë¶m;›ZkÎ#-•ÂĐZ*EB)ĄkÝ 3Ď…ĐJ2&„`Ň–I"¬s!s"i•0źÁÇhf.m[3 »Öŕň\7<‘_n:–ćN8cáŚD$FŞşkMCkÁ… §XśI©h š!¸mŰĸRB'?aN˝¤TóaLđaŰ0îË:ĆH)©ôµk C([]í řNťá•×C=MŻĽ¸Î×j3ÎcłyśP›áČÁŤźi·i'B(çv8d‡ĂDdĂŚ„±ëݤŁĹůŤčŢ{Éb·ŚtTSÄiüaËLN/Kż(ŮŕľĐJ1§ă¤­‚K%‰8ç$ĺŔ®×FŚ!´=Ü*1>x%4­ě›]°‰,~.ąÖZĘ[ő9:‚+)‰ Îôő/(břĺž•ýź CçŢcŤ1¸ĺ1Jz`Ěi¬n @DŚw™f|ôsçŐMüŁt¤ eTSc:‰(Ľ­€Ń`dÜź·÷§ŤYiŃ"çC4.8iRqńL‚”ŃKŃš™éŞsĺŇÜŇ“ý‚3‰›řgŚđĄúĎ7|4N¨kÁ'Ś”Šű݉¤Śf ăB‡Z#D¤0»ţç1&8żŮĹ€˙Ą$ŢÓŚĐźeŔ˙˘oŕ%tEXtdate:create2013-12-25T12:44:48-08:00žŽC%tEXtdate:modify2013-12-25T12:44:48-08:00mĂ6˙IEND®B`‚libtorrent-rasterbar-1.1.13/docs/client_test.rst000066400000000000000000000040511351156116000217420ustar00rootroot00000000000000=========================== client_test example program =========================== Client test is a, more or less, complete bittorrent client. It lacks most settings and you can't start or stop torrents once you've started it. All the settings are hardcoded. The commandline arguments are:: client_test ... You can start any number of torrent downloads/seeds via the commandline. If one argument starts with ``http://`` it is interpreted as a tracker announce url, and it expects an info-hash as the next argument. The info-hash has to be hex-encoded. For example: ``2410d4554d5ed856d69f426c38791673c59f4418``. If you pass an announce url and info-hash, a torrent-less download is started. It relies on that at least one peer on the tracker is running a libtorrent based client and has the metadata (.torrent file). The metadata extension in libtorrent will then download it from that peer (or from those peers if more than one). While running, the ``client_test`` sample will look something like this: .. image:: client_test.png The commands available in the client are: * ``q`` quits the client (there will be a delay while the client waits for tracker responses) * ``l`` toggle log. Will display the log at the bottom, informing about tracker and peer events. * ``i`` toggles torrent info. Will show the peer list for each torrent. * ``d`` toggle download info. Will show the block list for each torrent, showing downloaded and requested blocks. * ``p`` pause all torrents. * ``u`` unpause all torrents. * ``r`` force tracker reannounce for all torrents. * ``f`` toggle show file progress. Displays a list of all files and the download progress for each file. The list at the bottom (shown if you press ``d``) shows which blocks has been requested from which peer. The green background means that it has been downloaded. It shows that fast peers will prefer to request whole pieces instead of dowloading parts of pieces. It may make it easier to determine which peer that sent the corrupt data if a piece fails the hash test. libtorrent-rasterbar-1.1.13/docs/complete_bit_prefixes.png000066400000000000000000000171631351156116000237640ustar00rootroot00000000000000‰PNG  IHDR ôňžąJ PLTE˙˙˙   ˙Ŕ€˙Ŕ˙îîŔ@ČČAiá˙Ŕ €@Ŕ€˙0`€‹@€˙€˙˙ÔĄ**˙˙@ŕĐ333MMMfff™™™łłłŔŔŔĚĚĚĺĺĺ˙˙˙đ22î­ŘćđUđŕ˙˙îÝ‚˙¶ÁŻîî˙×˙d˙"‹".‹W˙‹p€Í‡Îë˙˙˙˙ÎŃ˙“˙Pđ€€˙Eú€ré–zđ挽·k¸† őőÜ € ˙Ąî‚î”ÓÝ ÝP@Uk/€€€@€@€€`Ŕ€`˙€€˙€@˙ @˙ `˙ p˙ŔŔ˙˙€˙˙ŔÍ·žđ˙đ ¶ÍÁ˙ÁÍŔ°|˙@ ˙ ľľľĚŹK pHYsÄÄ•+ůIDATxśíťŤš«¨ECâäýůL'QQůŮ@ěużëśîNvkâj ňxB!„B!„B!„BČcĚç?˝÷‰ď˙ !ÄůüŹââTbB&Ď[ŰÇĆäE?Ť‘’#ŁN§ë|$j/“ąy8I˝NAŮ7Ňj?x'ť3¸K’ ćĽh!Y ý#Čőţ$!9@ý#Č÷9Çű“p×ÍŚDEŞb ű|ůáÍUqóć>VŁ·gÁî•ă*–Yo¨ź¶±”,ÚPą!Ă-°îî¸î‰DF¦wŇ÷]¸î!qÔÄÖäüݤȜNúń7S’ ÚŹ[AÎh— §¤ą űb'ť‘©\ü(Žt˛ÝO߿ˡ&ŚTŮDë8¬´SU° dlŕ–§“~‘•x¦R҇ ~x+ÚxǬH†¦ş ČH‘‚¬› Štů퀠 Śěél?(!_jX„ŚB?(J~śîw\ÎɬÁЧŕ\Řad<A€}×ó–=9P„˝©Ś„EúrAÖ6b×ŕw7dm5 VôĺrO­Ëľ{nOÇřęń3g}؇˙‚XŹĄ ¤9,4jßů=âŃh°âŢ|Ů9„ÜRĎŹS/c—ˇý`EsÜöAKŔD'Ý\K,{Űh°˘yPFćQUMŹÓĹýÇ­+^Á/ÚŔŤÖMxž^Ĺ_^ăFaÁ˘ ë™ěŤŠ˝Ś0‚Š dĄ›©yKPÝĚČ”ČlAŠ÷RŚ Ű'öśr h?OfŤĚo@ĆÄ—CHÇ +î,f°"QIG?˘ÎÂq+•td]Ćäs§Î×w>ŹKL}B0ŠňR|ÖČA}łÝÄţŤâ˝|g˛ř?ď żČ}[¶kptź'łF–4 XA<ßů~7!2ń Á2==+, B¤ÓŐŹ]sÄú!Ýč-Č>˘×xľ3Îe^ŐĄř¬‘E‚đFašĎ“Y#Ë B§o…µŽM‡ň .ߵ⢟Î~85ť„ '‘‚(.Ĺ'Ť,őŁÂ^‚ČźQźp‹ÚódÚH ˛?ˇÁ˘ D+¬N}&«šeôŁ_'ýRbqU“é7KĹřw·Ă*XŐ¤r ňd¤ŞČňÄż—ďâě2(#‹#ë ňîí‡LA*ęő@JôýeČ[Ő„‹W“ťj‚5ť©‘@tSIţĺŐ"QZŠO‰đñ—"ô  Ś,ެ#?d BQ§Ŕ*÷}"‚‚ ş„H Š2s)ĘR|ŇHŚ ‡HI~PF–E‘quwC¤ D đ ¦ű DhApÍ!ý‘ë ‘‚(,Ĺ'ŤD ňŤÖýř@AY D ŮĂÝů1Đä®°Ŕ~ôę\Ä  ó^^ő𞭔ť›@†é. Kć˘ ¶ řEž"¸Ý<ńˇO‡•đö,°Đ·€Ăşl m°Ĺb'}ÖHXňzWŘK"!*@ ňŞR`śË]«$ŚH:~ô^´!? ĆŹW%?PđF!#3#!‚Ľź+X#őAđ Tť'łF"ý  d8‚ĽV?jĐ­“ŽO AřńŮR2"ĺ‚Ôő…HA4•âłF– ň:řÁ>HŠÎ“Y#‹ýřý—‚!)ääG Ř!ý(óăŐŔ B:R$Čkű—ôúC¨ zJńY#KqůÁ>HjΓY# üx9Ű BF"_—őďşVßáî.Ú01üčĽh‡»ĎL® ݆~ Č[´ÁľGK)>id¶‡Ż‚ŚÔY×ĹÚľć˘ °Íłí!,™äý¶×áË·€wđfS°h?'}ę}ľąW ý¨ÂCA楥 ŻÓׄHgl?Pä.Ú`őAŔ{ô€ÉĎČPdľÉ{ůşőŁÂyŁPů©§#˛ť g=(‘O»+ÂŹtëŕH{šůq)ŻZu@() • W=4őĐBQ^Ý+,ň#a/cý`$ ͧžŠČ˛ö#z/ĺ•§ý  Dm ,—í ,öAH.3řAAH6-q–WĘ:čDquŻ!˛ÔŹ˝tëá„}$Ôžz*"‹ŰŹ˝Lô‚9Ô/°<ĺUŰ‹}’E?Ľ?™BuF!‡»«d?Pä-ÚŔ9éj#‚÷Ň[^ý§b®sұ;ô‡ÎSOE$˘ íĄ_ŹqOYżĚ(Ä/ÚŔM­ÍRůwĽC´=Ôň55îm8/€eŰń_˘€Ę=IťŚ'&ĆzĎ(^ęúń–ä ‘‹6€ägä)ä‡g/Kô¨pŕ ¸hĂD‘U)j>” bbN–XZ¨Z`‰+ŻŞ÷A~~Üž˙D sůŃF&„‚(ˇ˘ CvĎżD_uŻ çÇe/Ëő`$ e§žŠH`űqŢK@óˇTF ¤ő ,ˇĺU>HĚĎ)Şů!¶űŃć*;éPK©zŔ)®ę^C$Ôk/Q~hěČO ˘ ň#±íÇľ—°öCŁ ±§>K,ůÔ)°Äv?>4ꤷH •©äG•T„D2Ą(ĘűŕěHŹD ňŮK¬ű Ć3ĺđ<‘Ђʄ7 źąµřH™$_ćýSu>RŁŔ’Ý=˙Ň©bVm(*˘@ŹŽťôßtŰ]®j"yłŕC/OŇms#Đůó‹ ˇ„<žŚ±,đČ÷[ĂĂHjŇ`éQČůVqŕ8ňyÔmA†Y»@ÎĄNÚÚ "ť«W+Ő1‘Ł)L¦n>pj˘ŁČ•ąúŚ\ý}ŕ`(Č‘{y‹ÜË+É'}mŢřŇ –W-:éq )<čÇŁÉU¬¨ …D“_˝Ă>Čh‘'=‘'=„x(Č`‘çćyn>dx%D BrayµŃâNzŁ‚‚˝ó BÎĐ<"ŃQäJ‹t—WE‘îňJÚW…‚Śéi>J"=͇°ŻKŢŚÂĘźQHŇayu˘ĹŤB×ĚoFaÍOą%©đęŐ…^óA.bPţ°ů¨Fřôv 5ą,ű_´á)`Ćţýć‰}ć=mÁŕ[ýŰSľ‰X´Á%PäÉČ$B H^d°ýsŕ Čž“ÎK đK{÷ăČąěu±(Đ‚Ś G7A~e/óĘ~Ô„«»«Ź ’Q^I9đ&¤ŻîźŤŽw@HäM’Ó|9đ6¤wŇSH} Ö@ĺU·N:>”€d ?(ůôc«»hîV5)JČEG‘+#ňNřČh=dx#ÂëbĹśü¤gämß|8đV„DްĆ+Żę÷A¸xµ@‚ §GŁN:[áĐŹÚĽŠĄŁČ•y/HLd˘Ľááîě¤ËŽŚh@î#“»Ľá;éÎ ! ¤. kÔňŞAÄ-mýĐK.Ú ‡rAĆ»ş‹ć¦ré…4Y´AG‘Ű?2F`džýĽ!™ëbYOä˘ ¸Í3éKéŻ|Źüö”o˘mđ}Ó„DPXa ^^=11·óAÜß5wŹ"Ő)őłbé*ą}©N™ Łű"µÄú–evqĆNzźČH?Ü‘E啎×D°áśtÁ‘%‚”5:^K"Çb‘ *¬)Ę«}¸Ď¦ ]Č÷cđ«W+­:é‹%“lAćІHAtą]#Łý8GüĐńZ‚H_Ý=)!ď€FA 啎×ÄýU¬»nK¬dX3•W-:éŤH2y‚Ě䙚A&ązF¤ :ŠÜŽ‘ ~l‘8=tĽ– (ĆČ A€Í‡Ž×„HAHôkÂňŠ}yId>=:  SÝIdF?PÜťŢ×EMĚľŤJČ@G‘Ű-2ÉŹ'ľĽŇńZ‚Hjb~¦(Ađ͇Ž×DúX¬K‰…_´›Đfą|ď˘÷îöÚ”Ż©q3ŽÄ7΄}Î\v1\<11™‹6Pnśýđ nKD^ĹÉ?fäI;=Ć9đ>¤žŢ\´ˇwäQŰćcśďCů|(–XMIôc^Ş÷ALżUMź ôĂO«N:…í»ç 9KG‘Ű%Ň$NŹQĽDUd˛Łx7¸.–*vAX^ÝŔ>ČŚ¬‚°űq ™ÍŹ®{1\KSäO?Ć8đ~ÜVäĘŠr"3üăŔ;"ň*qó„ÝŹ8ôAZ%Hţˇ‘Pé )Ž"·yä’ăÇŢÎQ™ĺÇŢ•äÓű·ŞÉţD–XŤXŘ=OˇSd]Őd&iĂB=’h!ó&ČE®j’¶(I&Ó.OŇms{Đ8oĄ7hAžGµňGf—WbĽu$ Ň4r /úćŤ|?^ż-™ŹŽHy‚°“žGîGÓćű1/ çRç¬e×ĹĘ ÷“éG-i”0 7ĺ•—żŢéEňęîŹHFG‘›ه+˛ĐŹţŢ)„Č S:޴Ȩćăů.m?şxŻH"˘6g•Aő>Hä|) AI÷~äŃ “węS[ňŻ^ýA?z"ň*–Ž"7:2ŢŹCäzďĽLÁ^Ë椏ĹJJČCÇ;™R^Ů‘?Ćz-;~'=%–W,°˛iҡ ĄőÎ˙  ™P Đí\›WG‘{™|u÷ůFú1ĘkŮ ^ĹŞ™Ţ||"홤7™§·Ő´°ÄňP\^±Ŕ*ˇŐh^ç8'ý–Ě›çoú˘ŐP ’ ů  x@ĺEžf쮾ú>|ů’p”jßžňMä`D'üśôÇŹ|ż‘öCĹk9^'ýÁ+w~ą—óş%,°Š9—}dö«XôC8 ńO˛Ĺ™S´—uŻč‡ DÎ(TPä.ŘČźV$JŻeťHá‹‹6xX°‘kó±GÂůŻeĄHÁ$jÖíŚ%¶Ŕr,+Ę«ťôF ú€ âZu—‚Óę*‡»_ˇó rŃáEîŚ<–WżH¤Â_Ëz‘ 8$  §ć‚Cd "`ĺţĐXZÜ(dÄÎχâĐĽŠŐ ľĎŚ˘ ’`$‘ÝŹÂH—‘`?$ż–U#Ap¨IVűQé.ŻžřöCđkY7„HA*°üÉÉ D×ŃĽÓ.Ú@?ÔĐu>ȬsŇ1~>Ň™~H#ďô®,Ř"÷ HndŕĎźxAÄľ–µ#Adw·K¬ymX_>óRŢźľ$ĄÚ·˝ą˝Qř±Ä‚XöăĹ ČCAâč~°˙…‚´áGŕgôC$™wŇë^ćÉŹŤĽř‘y[^‰t˝“›0e~řÄ«W )°"×#HTdZó!ěŔUG‚|FaäG´ /݉‰L,Żd¸îH\8+4uĺUexٰů‚°wŢ“ťt˙ç¤×ý”[IĐŹąÉżĚ;Çśô€áȬňJĐ«Ź‘}‘j’ů ą‚ä5‚\}$€ řEäl–Ěçľ_~Ö ›Ě55¬Íý˘ źÇżĆ#·šűAÚđÄÄäžŢ3˛dú\B”‘;'=2!ü…‘wzř" ôqŕcD‚Č]›7.!ďŔmóá‰,i>Dř ‘ DŽĹ’Ë+ĺ´čř—ŤMPK®Ř˝ ů4ÄDŤWQěî9v7HDŽĹę]äĆéqŤ,öŁ÷Ź BäŚÂÎď@dóqŽt?tśz:"A°“~ĺŐ´čÜ=ŕ6A!ě~ BuA:Î(ě—"gö+rSü°"Qw?tT÷:"Ađ*–MRű±Gš§žŽHy3 ctÁîÇH4Í;Q„K†˘Őp÷iZ6ÄHAząé~|"±~č¨îuD‚ČžQXsU“ď@Fűń n?tśz:"AdžŢuç4'ł{Îî‡`š w÷µ ĆóoĄ°ű1 ]‡šŘćčXŐd áZ`q’×[ŔaqSqsżŞÉ¶qýDUdÉ‹ ,aő~ă÷RIuŻ#ńZ`ú­ĺ<™6D~DÓÚĽYťŚĐşě~ä\îU,«‚Ů‘š ýŕŐ+ôšPť AnĘ+2 "W¤KzdLyĄŁź6Ä‚,é‘Q啎ódÚH"Á’^`±ĽöA"IöÝŹ!  q¤űá˙Ż^͇HAé’ß|č(ŧŤ1ş k™R^é8O¦Ť!R‰Ë«q`$†4AŘ; ý …U‘Z~ÜGşîňJG)>m$ ň#˝ůĐqžL "ďô¶§ZÉ-±R ,–WŁŃµ˘bNz‚ÉĺO_A4̉„Íń‘ß‚l%~ц'"e‰~đË˙ÓwŕiOüJOp^ť äíQ°‰üüÂ礟ÚdöŘDÄ^2˛Z$‘‚ -°xó|P@ÎJ,ĚŽ€AřŮ҉®‚_´!ŇNý 7 zŁđ"3˛¬ĽŇQŠO bLA® +˛°ůĐqžL B¤ ĄDX,ŻĆ¦oš'F®›88ÄKˇĽşKvD R&żÓŹc$¤ĽŇQŠO bJA0啎ódÚH")âľŔby5츹őWŻć€‚¸ą„WŻH ")ßçÇ ôCG)>m$Áń¶ßČ`y•Ú~č8O¦Ť!R|ÂË«‰čÝ9š—~•΂îôZ^‘YČťRµÉ•? Čß|č(ŧŤ‘=ŁpD̢ Kŕ§ŻŔsßy{ĘEß,Ú`$öAíË«ůxbbZYŃ/{çŇU‡Ŕ„~ $G~ŻßňĘYT^ţD1˛N$‘7 ‘‚Ľ‘e͇ŽódÚH"ÉŕĆ',ŻF¦w€Ŕ-Ż^Í ±ńřxő 1$Yţ{?Α?t”âÓF‚WcyuŚ„”W:Γi#A$§ÇłĽšöA6čąBA6®‚đę!R4ů~"qzč(ŧŤ1˘ ®ćcŤ6:Γi#A$‰ł,ŻČě|ąřx,őľ‚Čů¶“ ô@ů!ž ňýđ—WO|yĄŁź6„ČŹŽąN~"ń͇ŽódÚH€eđ‹6ÄoűËw>űÇM·Můš‹6ü!aNúˇaX<11ĂÂ+¸¤Ş;é¶ Č):„ČËĽ‘/W‚JŢTFŠCóŤÂ]–WäL÷>.!úAL/Čć»ç¤"‰’$Nu3#šV4?”Ľ©Ś‡HAbř ÂňЏ™ĽňőÝâcnA~~ôřŐd*D r/˙G?tÔÍŚ‡NA’ýPň¦2R"ąăĎv?H™ű »䎉ˇ¤"ą‘ÉđCGÝĚHq(îžă‡’7•‘â9a*ČÂî9‰ {¤— ÔÄĐ[ŇUM–L¸< 7Ť6E«šÇżRđ~¬ůOďO˛Ë+d6Ś "·“^–đzĺ ’_^éxS)ŽüNzAÂ+Ü€xa÷Dr.S«8KOČôWŻH])Iř[`:GęA:Đ\ż¦ďüpÉ_臎ş™‘âh-HŚŽ—«¸ĽŇń¦2RŤů|@AzĹňФ˘±ň˘¤ ů}ľMŞ ĽzEúŃPx?ňcôĐQ73RÍy%´öËj>tĽ©ŚG+A¶ŹL+°X^‘LtőArýHÜBVT B?RZb}zyś _ůˇĺ•Žş™‘âh őéĺ‘ ČçĺÂ6:ŢTFŠŁţ„©t?>°Ľ"EôíXA¬ň*E^˝"…ôťŰ‚Řz¤ř‘şC„ÔPbmxg"`Ćţýć‰} 8¬űÍłű´ŮT_´áT'Ĺ7 Ďôýa$#«PQSť”7 ť<@ÎŐäÜŹ  ¤%]ą_´árŠ~ŤTşQxą •ä‡Ž"—‘˘#AÔäz™–‚0˛m$‚8îň±Ŕ"ŤéÜI$8îňŃұ‚¸î‚S˘´ ÎAT©~č(r):Xç ŞäöCÇ;ŔHŃ‘ °‚¸˛Ŕ"íŘńŚQ§¤ňńŚQ§D18A|s82ŃQä2Rt$” Ţ)€9 H…Îe$#;Ĺ|üS)#5Gb†»cýür1r¶ČěOąÝ?'=°Â‚‰đñˇťŠŮaFöŠŚ:ťTEć.Ú°?ÓVX  “ERýY› „ L± „„†~€~B!„B!„]ŕŻhĺŢŔĽ D `űÝa5Vtiäľ1屿ÝűŰnűúű˘ ďq‰5fĹţ݇¸íµtÝą6×'ź¸çě­ë™ľ_U ü/‚߇\ßMěĎýŤ-OüĆ-Ż(r ŰĎ_ł?7ďaÖwßFe¬_Çi˙®űf¶Gúvú˘ÜţŹu{|¶éOESáž!Zí¶ v\q¬uÂěŰŇČmkěŻr“Ź*fާod¨ýzYçîöZÚŤÝľ·Ţxççś™Ëöa‘ŹüW"ń%ÖzBñţ˙¤î;·ťq…Ż€9ť'ÇS/sŤťş·*ŃŻëá$6§3Ő<®ŻŔí«a b·loŻ‘ŹŘťF ˘1ńodBćăµyßEĐţťNÉě´Kj‚ Ç?÷–µ‡źnť‡ýľDşýpD¶ôCŤ Đ~ňŃéO[Ń©Ľe¸BANmĆö•ÝŽĘ8î‘ëḎ̌p-äđQ›gݬ˙łżcµ‡‰ ű·¬VâňŻË -ëďăoą<âđŻußÚĎŹ ätú™ýßŰÜÁÓ#íżá§Ä}r»N˙Ó }Äu2ë:]xě„Üâdűéń‘×äqKW*Č5ÍŢźÓ.R›íô3×ëVUłT×Ë‘a˙–ó2.—´ußěçŇűÜÚ‘­;|´ÁżfÚc=‰í®´1ŹSÄ–iý–ÇÚ/9FŇÎÉ„ŚĎsBPB!„˘‘˙»i{ť˘˝IEND®B`‚libtorrent-rasterbar-1.1.13/docs/contributing.html000066400000000000000000000141671351156116000223010ustar00rootroot00000000000000 libtorrent manual

libtorrent manual

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

Table of contents

contributing to libtorrent

There are several ways to contribute to libtorrent at various levels. Any help is much appreciated. If you're interested in something libtorrent related that's not enumerated on this page, please contact arvid@libtorrent.org or the mailing list.

  1. Testing

    This is not just limited to finding bugs and ways to reproduce crashes, but also sub-optimal behavior is certain scenarios and finding ways to reproduce those. Please report any issue to the bug tracker at github.

    New features that need testing are streaming (set_piece_deadline()), the different choking algorithms (especially the new BitTyrant choker), the disk cache options (such as explicit_cache).

  1. Documentation

    Finding typos or outdated sections in the documentation. Contributing documentation based on your own experience and experimentation with the library or with BitTorrent in general. Non-reference documentation is very much welcome as well, higher level descriptions on how to configure libtorrent for various situations for instance. The reference documentation for libtorrent is generated from the header files. For updates, please submit a pull request.

  2. Code

    Contributing code for new features or bug-fixes is highly welcome. If you're interested in adding a feature but not sure where to start, please contact the mailing list or #libtorrent @ irc.freenode.net. For proposed fixes or udpates, please submit a pull request.

    New features might be better support for integrating with other services, new choking algorithms, seeding policies, ports to new platforms etc.

For an overview of the internals of libtorrent, see the hacking page.

For outstanding things to do, see the todo list.

libtorrent-rasterbar-1.1.13/docs/contributing.rst000066400000000000000000000042601351156116000221360ustar00rootroot00000000000000================= libtorrent manual ================= :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none contributing to libtorrent ========================== There are several ways to contribute to libtorrent at various levels. Any help is much appreciated. If you're interested in something libtorrent related that's not enumerated on this page, please contact arvid@libtorrent.org or the `mailing list`_. .. _`mailing list`: https://lists.sourceforge.net/lists/listinfo/libtorrent-discuss 1. Testing This is not just limited to finding bugs and ways to reproduce crashes, but also sub-optimal behavior is certain scenarios and finding ways to reproduce those. Please report any issue to the bug tracker at `github`_. New features that need testing are streaming (``set_piece_deadline()``), the different choking algorithms (especially the new BitTyrant choker), the disk cache options (such as ``explicit_cache``). .. _`github`: https://github.com/arvidn/libtorrent/issues 2. Documentation Finding typos or outdated sections in the documentation. Contributing documentation based on your own experience and experimentation with the library or with BitTorrent in general. Non-reference documentation is very much welcome as well, higher level descriptions on how to configure libtorrent for various situations for instance. The reference documentation for libtorrent is generated from the header files. For updates, please submit a `pull request`_. 3. Code Contributing code for new features or bug-fixes is highly welcome. If you're interested in adding a feature but not sure where to start, please contact the `mailing list`_ or ``#libtorrent`` @ ``irc.freenode.net``. For proposed fixes or udpates, please submit a `pull request`_. New features might be better support for integrating with other services, new choking algorithms, seeding policies, ports to new platforms etc. For an overview of the internals of libtorrent, see the hacking_ page. For outstanding things to do, see the `todo list`_. .. _hacking: hacking.html .. _`pull request`: https://github.com/arvidn/libtorrent .. _`todo list`: todo.html libtorrent-rasterbar-1.1.13/docs/cwnd.png000066400000000000000000000450661351156116000203470ustar00rootroot00000000000000‰PNG  IHDR KF?É#PLTE˙˙˙ľľľ ˙ |˙@ÍŔ°Á˙Á ¶Íđ˙đÍ·ž˙˙Ŕ˙˙€˙ŔŔ˙ p˙ `˙ @˙€@€€€`˙€`Ŕ€@€€@€€Uk/P@Ý Ý”Óî‚î˙Ą € őőܸ† ˝·kđćŚé–zú€r˙Eđ€€˙P˙“ÎŃ˙˙˙˙‡ÎëÍ€p‹˙.‹W"‹"˙d˙˙ׯîî˙¶ÁîÝ‚ŕ˙˙đUđ­Řćîđ22˙˙˙ĺĺĺĚĚĚŔŔŔłłł™™™fffMMM333@ŕĐ˙˙Ą**˙Ô˙€˙@€‹0`€Ŕ€˙€@˙Ŕ AiáČČŔ@îîŔ˙€˙Ŕ˙   ˙˙˙//őtRNS@ćŘf IDATxśíťm–Ł*@]Rú‡Ëy'ěoşŁňŤ(ĄćŢ3Óé(BĘ4Ćž Ŕ—2ő Sďz1ő Sďz1ő Sďz1ő Sďz1ő Sďz1ő Sďz1ő Sďz1őľ•÷»wSďŕ[A€Đź©wđ­ @čĎÔ;x:ď÷Żę–˙ďĎłí +SďŕálćKduź\říX¨›ů°Ď sżÝď˘e č/<)I¨›ŁśçăĘء›ť_âŇďěłmz˘LÍ5 Ŕăd ňČyťý¦­?Ű[Ь?Ł)ŕşť˙m f€Ď‚D÷Äšá­Ú[Ą¸~/Ă-™ź$Î5xŻ *‚Ähvľi›š®—­x'‹GÉ5Áx˘LÍ5 ŔSÄ€ Ě5t'tá­ vC€_Ä 2i®9>'Ŕ`ˇ=‡x.ďA”©ąxř3]pŇ ż•ÚĎB‚Aüe‰Ű± źŚĹ{ej®ÎGm†átď‰-×I#Ŕz R`jvá đäĚoŰÜ{ej®"@Đ€b6‚č1 ŔLÍ5 @8r”™ůŢî{ű™ř¸w§n†p/z ‚AŠBJo×»ś%%·TÜ ˇz ‚AGXÚ>±aY€…«śąÂU(şˇ  l_BłÝ{=ßŰźŃys·uˇűř¶o[ub˙«pÎŞ77C¸Ăß] _×đôTúÇöm˙Ř×/b˘*Âoy8—EWÝ á§†Ą!ďA”©ą†ĂQÉěúŔ##@;/ ʆ_׎sëč˙ĆÍv9xE€Ů ]í• •– ń«śD€YdÔUŞĺćÓ4`-ő¶.¬8ö–_€ŢÁíVSş–pŤł›!8ÎŐ-®M0˝óʧ7zŰÍ lŢáź @‡ăHjÝť†Güp4ăţľwşx°nK˙Ő/< LÔ‘­ÂE\/Ŕ–L¸R€Űđ9~ăÍ`v”Ř ‡x¨{ęł2W˙Z3üIL.zG đţłĺp’çđXhłłËŐ pűqd«,Ź I/?„".ďŤ-Ľű\śĺ@jť|ŁżO€É†ĽkýŢ^ůő·9,Ř6Čűň=Ó+Q€ź_öă¸<äcÉ Đľ 8™śÎ=%Ŕóą쉅łnëëŠD×úmĺ}&ëö70¶älźś;8¤ťźŃňç °KN€Žŕ'űaŮú˘ţfw ĐÔ pö8çęN đ˝ p®ş:Íóč/?ü®·/ŔŰ”Ň.ŔěĆ Ś\NgËö ýF›{9ßßĺźOeg\–—˝ÂoÉy^.±â|ço†0Ż×šĺ˛Ăřű$˙UὲçĐ\-Ŕű”rB€3ELí2Ůŕ;ĽýAň«iŮ/vĽť{|ÎZ7ľÓ7C˝Zf§-X~řÄÇŕô3äëqÚu 0.— ÷Ą˙ Ą/o¸3@_€& gĘU€Ţ<ŇŐ!çËĎű ÜÎęś«X5ň©]&)@g^÷yşÉ(<” żďë Đ[güźŃexöjč1řóŹ”—˙xËÂ绉ółl8żę ~˛±|N'Vů›ü> @ýĚ?ó_jUZí\ε[Ąv­X€ďđĆ/ŰĂĎOč»ô!đçŮoéŹĐ–ř9ÇĚ˙^ĆÚÂßÂuółţQ€˙ößý”`$Ľă?ßÝů—â‰ę;ß)q=kěYĆ«~ĽG8ü´Ýů›%ťË¿̈́hě‡ á đçs’o†`>>[ăsçtńÍf«×_~>µŰŁëŁŚ(@S/Ŕ9`ň Ŕ[ö Îp p–`ľ©&XĽT÷:lc?ţfëĽúU€§ů6&/ M€óçúwř>ö«ǬX€ź«Z*EnŰfygř}7CČ p®`ęĂ“ář9Qťč8"ő4r̵5'XÉüţ=E÷žkËż¶żÇ¶źßĘ-Ř-üíϲĽŔŁÝYÁÔ\ĂAš/়C•?’ţŢ<Ą«°çeŽ'â˶’8Ř#`f×O đ7YĽ‚ ·"C€ą^@€#bX™ĺKcźS™,, ÔLţŠłś`ŔÔ\CZ€ć€M…ýEnfŢX2Çrß“M€É-µâ 0[öW€gäsxŽ-Ss · ĐY6Š ě@öoë´]?Î* p^ŽTĘ, ôÇÝŞ(ŔS×ú#Ŕ©ą†kčoŢ\m.Ë+ř°žĄŁ×TU,ŔşLÍ5\"Ŕ`tfcŽ đ&Ě"Ś“‘»Dźf«ź?•ŕŚK 0q/]•Ľ>­ iŕ|µMG¶'ô“č /ŕö#»H^€gŹ8­ fŘ‹®3Ŕ•’ł|Ľ‰3 Ŕ•H€îćJ¸<"ŔŰŃ-ŔŇßŔZ8ŻOsi‰ĄškĐ!@›)Ź )®nŚ.TH °TÁG€fŢ+:†ť}ŕkíŮWÍ&ŔŇ<:P%@ç÷p›ÚׂcŠLěŮ?uśý­ĚaęďÓWť Ŕ/a`úÖŹŃ»ö°p.šĽMR€ł·m0ňAzÍ<–§˙p)üśűú$¶´ p.ü˝@e wxX€®ŢśźË’xR8­śŕÜ-ŔĤŇ-xg)Š]ö˝ –´ż[€~ÍĚ„űP#@wQö8¤N€sńoĐýeÍĹśS4{\´ýúř)ü3P٨î Őžó×Ć; 0¨>‹’ç :rË]%@;‘°cˇ×/«:Ús7Ű&!°E€ł·•·$%@+Îł<5@RŚű Ş˘(ŔÄ€ +@w’ŕújť}Ö yŚíR~Żft{pJĽ˝bş[†“ÂKxn Ř$Ŕy`˘ź *öčŹL›g+Ŕô4í”·ÓŠEÎë}­ 3 íwů °«ë-“ “?ńéÁ}&ns<4¤B€Ţ˛Fš8ëýr§řůąs<Ű Ă3z®Ń>U€·;[Îa*Ś&ŔÔľaţRbÎ0^t<4¤ Ŕů€KW7[Á¸ůxżŤýäs&pG€îkZN"@sX€ˇřfw#‰¸ĂWŁÝ$/ đ· ‚’ŤŁŚ•śŤ)ڎ SmÔ đóÓ¶îč,#@'Ě%ő#Ŕ /™ÝEŽŻN pş3ôŕVCI€C^€ëËő̦¦'@o‹¨ÜEÜö"XËIn(%Ŕ-·ÚxŕÖY®żË ö  Ŕĺç3¸>ě Đ=ěu7,°9qżL€áXÜ(ŔúmSśŰ8Gńřˇ"Ŕ|«çśÝÂÎďy¶'îÓ¸}Ű1)Ŕh,Ć i`⛥n¨°÷ 0Ú"*wB€öšzG€łQ&ŔíDÜçS‰řô\SkP 0Ě,{Ú0'@çýęÎmüűp›?fľĘYŔ.Ü!ŔůbşÁíĐű’éöZN pľJ€›ęŢąh+ZÚ`”,®?ÎĘ0Î,…4›í}2Ó´9•ŕ¶gĚfçk L;0řóąŞá“ôkś 9$Ŕb$ÎűLŢŮŐőŘş #@{K'÷ízůüĎ`®/óž-¦{k`đŞ]Îóş7—čÝug}Ě pvXßá±+·µtň6#ŔmYJ€vĎ]&CE€÷3›‚W;·ĐçÇ…,çAâM<`ŘĐ_‘4ž×źó˛GA¦ľlöYľüĚ}K­‚élP6ŠÄ˛¤·éŢ*Ŕmo. Đ qx Ă#Ön[+ŔŮÉČ9-Ŕő}j$)ŔeČ–!ľJ€‰Â× 0jÔ™Rě pž/ŕJR€É/«Ő3µuL€fW€óě×r‡·“‘ö©¬Íľ—‹±·ÓąřýŘ8xzçzä*\'Ŕđ(ř”Í!šĺŤŕVrĽjĹžt&ĚŽZÜ~sd Ŕyý&Çět÷ú-ĎőŰ pÉžĄ[Ľ€‚Xýě„;H đ“)Jmş+Ŕŕ®VŰŻGh\Úwë¸ü˘J€îŃ€;—^űf 9'@ ĐŘ’:hč=ŁĺčÎŞŕ=¤8ź`zŘô0hěłMť×¶Wه ö´ ¶Ë`Ü\pµâ pYX s”ě®Ë pÖ"@##Ŕt:úo*uqB#ľ·„3›ęI/íśU P†©ą1&Žś[“9ë\:ăěĽ!?C€É |¶'Tá 0~Ç}˛íkŮâ›?Ŕ"Ŕ_¤jčď‰?"±˝q^€aţ!@5¤¸]xz˝ˇR€©ÝÇä}jâŹh"şShDÁ˘·Îo`ÝĆÂLĆ/C€·“ŕzXxŻçZ†[ąQ&«˛J€ë'šŢźŐĐ. 8 w”{©+7®ŕ|F€ÉŁ_€đܮܼ_€ć”íŻÍś >úC€uü,Ú|^ş”J ĺt®ä3Ž·…őL Îťž¸`ÚH p]w@€vä2on9Ú TŚš¤M,@gwý"zWŕNk#Đ´×9CN€ń>‘8-č˝`Ż%¸)®ŹÉ“};- 0üꂳ…»»~“?ß,@‹+@S`ÂeńDÁ už m ФG;'@'.`¶®&Ş@€fŰőgéw đ/ü#LďAá4®F^€©YÚäč(Ŕőg^€ŃĐ™ő»¸“s•ß&@s…1ŕ \$Ŕč˛Ď¬?Ű—>÷¸Ş¦šk@€f‹&ŃĘ:>%@c·ŚC ;.D‹kg€‰Ă ¸ę8»l`|ÓĆżóëB+@·ŘĎşĺYţ$7NňJI€??ţ‰€ż¸3Ą»lW€đzfgL Đśm'X2lö“%ŕŹűÇs ĐűăłţžS'@?Q¶Ř¸fĂýń·@€;4;ŚŢŤ3Ś%ţ>žŕßĆUõ+Ŕ5̢Łé˝đÄ·ăü®B€×3{’«`đöĺî\sçś}ç|ć—m´O“4^¸tŐ˛Ď&ŔşáŞŕÖŇ|\€‰ăx7Bt‹Ő Đh A€+{Lč%<ćű®ßbr6@€# A€SÁyŢŢE Ss Šö¸8$— ĐY_%Ŕĺbj®AŻŁG€p’«č®ßŕŚ[+H05×ŕĎĎÖĂĐo H,ŔđŰŽÁďĐ 0śŹ÷Z`ŠŚŤF¦?Ú .ťÖŚü4öąç¬çU€3”fj®AT€uRśO Đű^EÎ’E€Uăĺ 0¨g‹EP€®«ŕŤĐź4.´Ă »±2÷Ŕ 8#Ŕc4ÁŠŘ tëT€ÎĂí|ÝÔŽBÖűüq^€Ţ&ËÝWnŕ|Ť˝qŚ_dŮ© ±?  ×Wđr>)˝ $@sL€¶’süT`|ʧJ€nyăA€‹ýcŰ;8[–¸>ę ßWđrş 0 ĺ"FuTĐ/oü"péŽ8]d8çTçe[Ć*fDŢaŔׇZŇH(Ŕ9Ěččw¸Î^`­ă›?U ĐŚ$ŔY\€wMżŐ~& 0ˇP€łX-Ŕul˝Ź A€""Ŕ9Ř$ü«×‡÷et?c6¦»[Uŕ§Î'(Ś/GF€Ćßdö‡ń° 㾠ЩݻŁő`s˘"ŔOˇnIü)—ˇzp±Ăé\ř{˝·1E€;<_€áYT€Ţµ#©k˘çĄŘ4ńR¸Š„íĘ6Ú1}¦gx@€AMIÎg¸ý0E®YqĆ~ 𬠠iŕúÇ$wX1\đK@€0Çí<}üů±ţ%˙O­ÚGśĂM<Îs_&Žw`qTźŽX†ÓJĘ®Í 0U“Ľ·xćŔXëÜB€M°‰/@cú 0®V1®Mh¬P€»ă…ż„k8;Yx•˝Ă15×P!ŔůBÎ8× pĽŕ— 0:ž Żŕgůš‹WĐ?żÓC€îłĆ+ˇG ąP€ć°?› @8@(Ŕř„^ř{˝·7ă ľ)â#/@»ëť.Këi» Ű%Śî„ÇG@€a^8tÎă\%Ŕ9)Ŕ0÷}`šÖÄvÓ…kĽř"?ć…óE]ç3Ó"ŔŮ_Ţ*Ŕ°ş řmç;ß‘_•ײáŔ'č˝Ë#Ŕ©ą†koŕś`xÖ¶bűű‡gUw››ďµ;ĂÎçí0ŘßäFÎ0ĂÔ\CoÎeš¤ÝEľŤ3ąá™0 )·â9Ś*ŔO†Îń&˛ś‹\+Ô%ŔďŰpYę Đ p® A€÷3¨Mo:Çßę7Z.K/ z ł5ů÷Ë]Ś_CŔ°'#0Ěőí&sF™íYI¸.jŕü|&^CĽ”sŹÝš¸45Gť˛ \–z4»\mµ•V€ůs&đRš8_-Ŕů¤ o­°Š˘CŐ™4ç¸yG‹Ăra¸†v&6‘ qřÜŇ7`éŘâRÎ'@Ó{ř)ŕ=z:[úg[ŕ—Ń*ŔČu©Ä– “(Žm…*¸ĚJ ]Ô$@s·í‰\ř4 0žě]$@“ŕ¬L€Š đłČoŢ[{ ü* Đ–MĄtB€šk@€Űfđ©č ˙¨^€źO'ŕşH­íů KÎÁKůŠ/ĂőŁU€! żď9ë W%pYÚQ€łé"@ł'@怗2š?[˙x ťEpcj®aÎg¸}Ź=ŕgSř @QÎ0Xt‡m‰ÜËŮŇn…żÝśĂ»ţtšU!@ŕgµ3Xö —đ›P.ŔĐc°š©ą†ďŕV‰+Ŕ~ Z§¶ýY)ŔčQІóZ„x¸,í&Ŕ9 A€ßRćRű€ ™…«xĽťśšíů–ĎWŕw Z€Ţě cÚiňŹĎŁ rQ© qÖ$.  h7÷ň ~şFŞ_€ç®Zí4ü'»Ź‡lT 0ůV”`|Ořč`Â0ó˛Ęô઻˙QšmU˝ç0Kŕ÷ Y€á۲A€ö[]­÷¶őďő@yA›Ť‰äeWÔ ĐD7µD€_ìX€a¨ŰŹZ¦O„›0QżU€ëIżX‚٨  ťE( Ď`ć“@&ę— päCŕ’“×?yçn ť`PHN€Ů4µáŔU @YžLÖüî±L_ęm|P€qU"L|câp zĄÄÚÍJ±Ű~ZEš,v,k‡`Ľ`ysř±ďA?v…Ý̱Cе'ÓőocŻžŤŤ ¨âÇk4Őü6•ŤŢńfo‘mć'ŞťµFŐ¤ç6ľź­¸W W‘ˇěĽ>ąµ}ĎËÜ—Ýg‚c ·…ßóO©:Żg‹Ýx~Škă•?ŰŽÖ’Ů‘w@€§8Ź/Ŕvş­íG‹Ăťř{ă*ăä „# @·µýh`¸ · „ó<[€76B€gC€6‚  »Ś$ŔżËŽ Đ ŔŕVS_rĚe<[€—´"Ŕł Ŕ­&řPž/ŔÔKD€U Ŕ­&řP ŰÚ~´0\đL)ź t[ŰŹ†  č¶¶íw 0>źŠ%8#Ŕ^ @·µýhoŕz«ŞěŤZ*ưĽF€f0ľKőćé4É—¨]€ďÝ{•V0Śa7*˙@€ÎÖĐŻEB€ďý›5W0Śa/*/ŕŚ;Ł2w 0ţłCërűKňďU0(›‰.\€ŕëH) Ŕ¨L˙ {·ćđĎŐ3Śa?*ačlŤýZ$ú«őLcŘŹ*/Ŕĺ&ŠźE°ĐfR€ö"5B°čŤܦęřůőÇ}D€é6Ď P—ź-Ŕ¸"Î_!Ŕĺ´ŕzvP˙e0ź_ŕU䯂\Ćhô WóĘ05×€·§đˇ %Ŕ`j®nOŕCI€aj®nOŕCA€Q¸‚·§ť˙*Ľ •A€+p{Š ŚĘ Ŕ¸=E€Feŕ Üž®~> •A€+Đ>E€Ďd,ΰž©ąhź"Ŕg2”—31°Š©ąhź"Ŕg‚Ł2pÚ§đ™ Ŕ¨ \A€öig*»Âs@€Q¸‚íÓľ4Ě/Feŕ ´Oŕ3A€Q¸Ň$@·–¦šR.Ŕźź‹řó»ňx&öXI‹c 1 p{° _kŞlY€ĺ@ D€C3– ¬gj®!ŚjF€đa @·µ}ľ[€q?!@84ĐmmčŕĐ @·µ} D€CÝÖöA€> t[Űú|ŹgřD ŰÚ>Đçkhŕ#A€nkű @‡ş­í} č¶¶ôA€ph ŰÚ>Đ"ŔˇA€nkű @‡ş­í}ľG€>č¶¶ôA€pdâŰ{Ä ŔxS¸pąíОěȸŰÖ 0Z%s•ăjÎ 0Ü ^F«íXI‹Ź IDATЦ— ÓÂúS‰ăr[„ś łş(ŔT+?ŢÓ‚+ďmľkjIÖĽ/ŔD4đ"T Đ+'®śKíďtçŮ7xFń!ŔŢ Ŕ°ý˝‚Đ"Ŕ‘A€aű{ D€#Ăö÷ "@ŹźŘ0°T Űß+s Ŕ *`Řţ^AÖ€UĂö÷ "Ŕ°¨ ¶żWć@€5 @U Ŕ°ý˝‚0¬Ş†íďD€9` P0lŻ Ěk@€Ş@€aű{`XT Űß+s Ŕ *`Řţ^AÖ€UĂö÷ "Ŕ°¨ ¶żWć@€5 @U Ŕ°ý˝‚0¬Ş†íďD€9` P0lŻŕsřţGî!D€†íď|ŚE÷ďň!D€†íď|”MĆ™¨ |0lŻŕřެ÷Ü"ÎA1D€†íď|3öcż¶żWđyä8±~0lŻ Ěk@€Ş@€aű{#@.A€€Ăö÷ >G€ű @·.ř@`Řţ^AÖpZ€˙8Đ ÔĂö÷ "Ŕ°†ł4L݆íďD€9` P0lŻ Ěk@€Ş@€aű{`XT Űß+s Ŕ *`Řţ^AÖ€UĂö÷ "Ŕ°¨ ¶żWć@€5 @U Ŕ°ý˝‚_-Ŕ ‡!Ŕ2P0lŻŕw °TÖ€UĂö÷ "Ŕ°¨ ¶żWć@€5 @U Ŕ°ý˝‚0¬Ş†íďD€9` P0lŻ Ěkh †íďD€9` d ( Űß+s Ŕ *`Řţ^AÖ€UĂö÷ "Ŕ°¨ ¶żWć@€5 @U Ŕ°ý˝‚0¬Ş†íďĽG€ź?Î[ř“˝LcŘŹ "Ŕ‡Ăö÷ Ţ"ŔŐx˙ý‡z¦1ěF…ŕÓ@€aű{ďŕÇvĐ_Ť 80lŻ ¬ß+ŃrłXď<Ô3(›‰.xŽŕĂ@€aű{ďn í‡ĂĺŰŹD+ö@€aű{o ‡ŔÎjĹA€aű{`XT Űß+x‡×Ó‚\ă®F€P¶żWđž Ss Đ­ >¶żWđ›X~Y]ř.`¸fiFV€[2EŻ"nü(NÍŘ©ŕš'†;C1Ż’íďD€ryŐíÝI ?ÜUś{†Y0›T%¦\0đžŞŰS•g ?‹[@€Â ŔPŘ–@€âÔ|"ÜŢÓ@u{ŞňlŠ›@€Â ŔPŘ–@€â Ŕ& 0°6…A€% 8° ( ,ĹA€M @a` (l K @q`_&ŔvŠ›@€Â ŔPŘ–@€â Ŕ& 0°6…A€% 8° ( ,ĹA€M|ť›kÜ–@€â Ŕ& 0°6…A€% 8° ( ,ĹA€M @a` (l K @q`PXŠ›@€Â ŔPŘ–@€â Ŕ& 0°6…A€% 8° ( ,ĹA€M @a` (l K @q`PXŠ›@€Â ŔPŘ–@€â Ŕ& 0°6…A€% 8° ( ,Ą™`PXJ3ŘÂĐ|˙ńy4áC2*Ř( lcpn˙ţűé¨`_ 4°ŤÁř^u‡¬é”¶1¸W彣‡­Č{=PţöJŰZ€ͦěÇ °¸¦PŘĆ3Č!đˇ5ý@€Ň Ŕ6†`Ň|pwM? 4°Ťˇľţ…Ë`öÖôJŰ[€ű @U @i`PXJŰ@€Â Ŕ?Řś±hnÚ]ż‡»(ŞÄD€żŘřÝ Ŕ6ŕŕ Ŕď¶ŃY€î%{ ¦S‘x řO`oT50°Ť®ôîSb:ÍÖ„˙öFePŰč)Ŕí‹ŮÓůpÖ6ü§°7*ŘFWîn9ť %ŃĆěŤĘ ¶Á‡ żŘřÝ Ŕ6z đ]Üp:Š_ż ěŤĘ ¶Ńý2>i~7°ŤŢüý‡@€ß lŁ»‹ÇŔÓŮ`ś<`oT50°ŤŢôţ‚QÄt6Ű€˙öFePŰŕCÁA€ß lŁ·ËLnígO2ş Ŕި j``˝Xţ2đd‹íÝ7!Wż˙öFePŰč°N€ĺO‹ řO`oT50°Ťţ,­žlĺ'Ź 2T50°Ťţ‡ŔĄµ“-Vü´¸Pż˙öFePŰč?¬ű&‚d@€ß lcž*CePŰč}\f˛µźű jCePŰD€śĚżŘĆ w„ćSŕđ»A€m ň7A`řÝ Ŕ6ů«p{kŁB€˝QÔŔ Ŕ6ú \Z=ąů$ün`— đ˝ŕ:G™¤ŁB€˝QÔŔ Ŕ6®ŕŻď–OníĂa¦|§˘B€ÝQÔŔ Ŕ6ŕŰś«jCePŰhćýN~–ńY˛ÜĆęěݬ¦­¶íÇŃčü§°7*ŘĆĺ3Ŕ·ópi­ŚOs Ŕď¶1Ä!đy˙QͰ7*ŘĆlřž] Ŕń˛eřÝ ŇŞó».0[ö@€ßÍ€)­*1ÇąÂ9‡"@e¨ j`LiU‰Ů[€ĺŰ\M¶ŘI"@e¨ j`LiU‰©ŕ«pµ7D=u;¨ •A Ě€)­*1{ °¬µ)hF ŔďfŔ”V•p9ţE€đ»0ĄU%foV˙aôs—"@m¨ j`LiU‰Ů˙CŞűž*CeP3`J«JĚŢ,3­uźü.1T‡Ę fŔ”V•˝Xu,ö]ŕłeřÝ ŇŞss€ËÇ0ünLiU‰Ů_€ĄŐÓV5Ě€ż›SZUbö?.­ťÖBç@€ßÍ€)­*1űĎ9ŘünLiU‰9„@€ĘPÔŔ ŇŞł÷!p™©ą¨ •A Ě€)­*15`đ»0ĄU%&řÝ ŇŞł§?ßďŕ`Ă pţĄwĎaŔ”V•pp†ŕď(Ć€)­*1{ \Ţr:JŘŔƀٲünLiU‰É9ŔÁA€ßÍ€)­*150ĎÔ\Te0ĄU%fWîn2ťŁÔĬŞďE@€_ÍĎŹÎa.ˇ*âo`s…Đ…„VĂî}Çw:ÎÚ„÷ vŠBB«BPZ CPZ *@€˘Đjâ2ňĄ7PZ *@€˘Đj@€P…„VC'ţ~†Ľ|’ě?¤˘B€ÝA€˘Đjč#Ŕ÷ß-e–;˸ɨ`w ($´şđŤŠBB«ˇ‡?ľűÜW0xŘJ¸[#Ŕî @QHh5tŕ6çKH0ě…„VCr<PZ ý>F€E!ˇŐŔe0P…„VBCPZ *@€˘Đj@€P…„V„  ($´ T€E!ˇŐ€ˇ( ­ @QHh5 @¨ŠBB«BPZ *@€˘Đj@€P…„V„  ($´ T€E!ˇŐ€ˇ( ­ @QHh5 @¨ŠBB«BPZ *řpF2Đj@€PÁ2PZ *@€˘Đj@€P…„V„  ($´ T€E!ˇŐ€ˇ( ­ @QHh5 @¨ŠBB«BPZ *@€˘Đj@€P…„V„  ($´ T€E!ˇŐ€ˇ( ­ @QHh5 @¨ŠBB«B? @!Hh5 @¨JAB«B-P Z jA€RĐj@€P ”‚„V„Z $´ Ô‚Ą ˇŐ€ˇ( ­µ @)Hh5|˙#z8Ę$Ô”‚„VŻš_ßýűď?fŽ ęJAB«!  =xI8*¨( Ýť÷JĽt‘ß{}8ĚÔť÷ ęJAB«!süv3 G…u€Ą ˇŐŔ9@¨JAB«B-P Z \µ @)Hh5p!4Ô‚Ą ˇŐ€ˇ( ­µ @)Hh5 @¨JAB«B-P Z jA€RĐj@€P ”‚„V„Z $´ Ô‚Ą ˇŐ€ˇ( ­µ @)Hh5 @¨JAB«B-P Z jA€RĐj@€P ”‚„V„Z $´ Ô‚Ą ˇŐ€ˇ( ­µ @)Hh5 @¨JAB«B-P Z =üÓsŢ_ C€A€RĐjč Ŕäö˙1Ô”‚„VC§C`8 P Z ý˙¬÷śďĺ xž  $´zÎCűĄg€3ů˘( ­ý‡ŔP P Z ú?A€Z@€RĐjĐ Ô”‚„Vţ ˇ  $´ Ô‚Ą ˇŐ€ˇ( ­µ @)Hh5 @¨JAB«B-P Z jA€RĐj@€P ”‚„V„Z $´ Ô‚Ą ˇŐ€ˇ( ­µ @)Hh5 @¨ Á~ő€ˇ(Ô~ţüŁąB€‚~ÔĂ$_ÔŔ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁ ÔĂ@@7ęB= „tŁbľßż˙®G™ZB€Ja D ő î×y®‡™DŁB€z` D őúíŤ! !ÝŘź÷J¸xű·=fjŹÎůęnÔď·męçJđ0“hTP „tŁBr yčF=$?F€„nÔ—Á@= „tُęa D ő€ˇBşQęa D ő€ˇBşQęa D ő€ˇžúx]ĹđĎz@€P|Ö„z äł ÔS9Ż×Ć20äł ÔĂ PňYęA€"Ďzč!@˙›Ć©ď#@ť @Čg=t Ż™äťg NčF= @¨‡nÔC§s€Ń}÷ýŰď»7ěG€z` D őĐE€ďĺžŮŰď3Ô !ݨ‡>‚¬?9 BşQťÎ8" „tŁşđ˝sĚX0"ĐŤzŕBh¨‡nÔ„zčF= @¨‡nÔ„zř‚›äł ÔE źő€ˇ(ů¬•p›S)Čg= @¨ű‰@>ëB=PňYęA€"Ďz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨő0"ĐŤz@€P!ݨĄś-P „tŁ” ĐůęnÔ„zčF= @¨‡nÔ„zčF= @¨‡nÔ„zčF= @¨‡nÔ„z~/m- @¨‡ŕ‡|VÄ-0Љš@€Pc!ť¨ ő0Љš@€Pc!ť¨ ő0Љš@€Pc!ť¨ ő0Љš@€Pc!ť¨ ő0Љš@€Pc!ť¨ ő0Љš@€Pc!ť¨ ő0Љš@€Pc!ť¨ ő0Љš@€Pc!ť¨ őĽzđHhM @¨ @Bk"ŕűŃĂQ&ѨČ= @HhMř‚űőÝż˙ţĂa&ѨČ%Ľ>ôcxHhM @¨ű @Bkŕ˝’Xóů·=fjŹÎůť|Ń€„ÖDč·÷gÚçJđ0“hTä‹^źPZч ëOÁ˛±€NÔD|Đ @A€rЉšř^Źą ^˝cy$´&¸*`(ť¨‰.\Ď4f/şF€Ę@€rЉšč!ŔŐxůk 2 t˘&:pą¶Žc!ť¨‰~‡ŔţŐÖţE×îeÚä‹ čDMô`h?f€Úa, 5ŃuČ!đ`0Љš@€Pc!ť¨‰‚$ݶć2`, 5Á…ĐPc!ť¨ ő0Љš@€Pc!ť¨ ő0Љš@€Pc!ť¨ ő0Љš@€Pc!ť¨ ¨]· ýNÔÔ |t˘& j”Ý„ţ«ÇB :QP1ęţÇŹ…t˘& j4ŮĎ|ůXHA'jF¨Q޶ůű®ĎîDMéZŚĐ4„şü÷đ}÷&žÝ‰Şňµˇi5ĹbžľďŢÄł;QYĆî‚}”vŞ äĂł÷Ý›P݉m©Żlď©FŚ5€—ăö†ę}wTwbsňʶó ŔQxSPćNĽ2A˙ŐÝVű ;Ď ťĹ_™;u_ń z(qÝ)\€Ft G`°G @ô ĐĚ›¸ą˝g đ\oŽ Ŕý÷jf€JiëÄkżÔZ{{Xkhnp@xÍ}(’ŞůíăŻmˇŕ^‚…iěÄK?f}5Îŕ/Ss 7 đÚ)·@B¤«x•(Ň´­t­  ëÄĘ»B€‡Ţ6/<ě8ţľŤóśNŃěşN€©W†G ݉•űýNRźtăË­ű´[;¶‰çŢ™í—÷¤ý8g4{Ígf€ĺmvkôcą®4UIĽđ”>ü:^‰gź#"(I‹÷ŇżI€•Ű—g€‡¦oîŰő îßg4šlŞĽG…»ofK>˝Üĺ-vj G¨Q€é”ĚíÉăâLŐ{‹üIÁŇ ىőoěĺOľNä›ŰtrűýI—ÝX¬!ś_ @oéQ.uŮ Kyř)şWłě 0—’Ů<¬Ő73Ŕn$:±ňŕs)›^|¬–L•ąSË;4 Đ}“-ŐľŔo<ÎľęX€•‡&`ˇ¬m:yä˛îPéř;¬,Š%Y]•5'_"@I˛3ŔşÍ3Ĺ 3¸ÝJüL JűŰ1’—„u4áěç g€ąąŹ»»®Ë*úěŢKŤNé˛Îŕ§hÎŃw :#ŔťLf€ůH­ůŠ“ťVl›ĎĎ«zŻ,•Ř}¶Ü6óöŔŠ-„®M€fťë…{Żuźë¨â”Đźćl‹’yE=ż»ç5˛ąSÔ¨»ubŰĺ÷ăô_hP Śu™ç­ @âN<˛+O‰Ô‹4Ú6ń$Ůdş„÷ëyš˝`ĐvłţFŕ+!@㯻n,©¨ť×şźű•;놷:Ă·ĐdţĄ4…ŐµÇâť´‰^Nćĺ¦2={ěňéMx™`ĺÖą‚N^&K¤ŢĂ÷ăř‰I¤F"„đ˝ąvżĺzLžčr7ň2¶®Í§ Đ—ČŢIÁŚĂ¶‡/7Ń–u¶ŠÜs«lťą­ Ó4¨,ő{”ÖŃI•ÄëuOşUČśÔNegĽ$]ŇÔ0ÎźH€É­k°ë>î ·RN0ý¦í¶{dŢé3 ÷¦wÁ¬¦<ąOLçü¶ç/ë׬/Çş)ß$ µŤutźŕË}-Żŕ•¸ń‡É±çc+Ü řU. @Şg€q‚»ľ=V еN°AZ€ÁL-5 u6|Ey '±2˙ŕÍO^îK]ľF€B|^KŽŘ˛ţSuÝŕ¬Ů-¶uÎx;m'BMÇžx%n•űcď{ĘŻŃ\\§ßľ_Ňń.P€¤S“ďđŻŕůRŇćjjÜüÁ^’&Q2§N·dşă§ŤSŔťĄ_b>ćtJ{LUĹ`4ą]ółĆŘ}<1Ż`'Ź/x—u%)@ŰHę]y{‡5^f8Ő'^VüĘň4Ë[źźzĄnr˛g[• _• P'ŔxTĽ‡HŹA¦ŞsóR)ť0îâeđÔ’_&z#ö^@ř.oWef€ažżľ ˝ĂN€CüUĆýĐ(nďąčd[ílîű®ŻN˙o[^ ćw.Ţj·µ 'ĽlµąŕDě”°ŻĚF»uËVťq_Ď–[n›ćĺĽ&(@ą˝”Ů–ůĘ*X§ŃzXä`”sąIZ¸O…ÇßţtÍÉQ/Ý×ęńĄç3Á1±ąťz%ŮWPd@¦ş&۵źâóĆIO€qZxvófďh{'V/€°ĆUZŰúÔĆ f€iă»Övw¦E€~nú{ĎëĺGž 8N'žĘĹżFďőŻp3gČĽ$XWn‰çM˘„ Ţť¶śßŁ©˘ß˘'ŔőU¸űT°~Ť0¶ὧ»;¤·2WO™Q¸>sŹeă łŢŃW<ă ~łcéĆÝĚzÎĎ6w`˘Ú×< ŇĺeŰöť>pşĆËzż˙ü0|Iúéí‚p;Ń3™źżn^„—–D’{­©ę¬ŤK»{Á¶2Ć>1Ż0<+±pŠŹ{Ý´LĎLśÚâöÂńuV}„±čľČ`6N%{$ŻĘĄŔÖŽ±Brl¶°~Ĺń·.7«m㽟ć.  čgÓf Ú‰×ĹéľÉ¶×ËËA§´·ńË$ŇoM›üÄËU¨Ů._që16Em¶űńą\ ¦¸c@Űiń´`Źa÷ű 0öÍ1úm†n¦çQFÍ&žÚwąč- ŘÂĎ‚5“÷_¬l(@0ôsSŹg¬đy0KŽY´ęő˛‡‰«SÄĎ|ÝNÚ Ň[ŘęśÂ»/ůU<Űl Á§Ŕa¦~–»oĽ^a“Ú:=fA"†;ĆşUN€ŢD"< 2ÎçÁ‰„Źç~ˇäDďeçŘnMĘP±AĐQ•Ś"Ŕô ‹’ÄDĂžNś&ćf€~śQ~„Ű:íd.o°•ů ÝKżBĚɸC ®oŢź_컩ż6ÜíMA€&NóTŃuÔ“űHQ€®ßr3@ď-Ř{-ĆKKß] zí§ňůłŕýĚHŚ/·o‹ p[—Z–OĽhă’L w[¦A&şčĹš0żüvóq‡ @rt“ šż/Ď’ú'|“LѰšČ`îÁĂNţ$÷¦P’‘ý _ŻőÜ‘łÉö$NÖdüĎ`â­Ëß·ťĹ‰çÁ†ů… “műuďPňŔ Đxů·–ęóëĘ4™ęÁmq˛S ŃßyMţ^K¨¦P™ű3ŔOąbÂfRŁF€‰ o2Řô»˛źĺ©×đP¦ű˙安KEÇÍͿг •˝‚7<·ęp¨ů¤ĄĚď Đ‰'hJ3@“~»L5Ľóî÷·*)ŔRĄÉâ#· oÓż_·#ô`Â’ořCĐ$v_ۇI˝EŁ 0«OSѤ­˘ć¸‰Ä Đk-sô<đ*ěx 0ᱲŁÜ†÷YöŁâŐŮÝ/ąß¸ń¬żf(Ä Lvö6#ŠĆîŚ )ëzR†^Ľőc— q^ďNe§ăŕwbqXqL¸;ý ŽZk«.Tą'Ŕô&ŢÓŞŤöD€ĺs€;µŐuqyř3‹ťĽh›zad?ŁyŐ$ëVÔ悸¶OЬy°·ľ ŔÔ{g©ĘŚÓţŰÚ­e§ÄŃĽüa…-äMňÂé_n:x:Â+f€‡- Î…­ÔľńÖĺ^M‘b[±«š®ŤĎDiŹE‰†áĺŽhq*ź,t~¸Ź€Jęŕť\+ŔŠá­sUąŠAß/”šQü2ŽŻ&74•,.G€°˘O€UĄöŹž#8ʆ¦ö8Ćdę@/¦Ţôbę@/¦ŢôbjŻBűgÂźčôFąD¦7Ŕ"\/ŠĐŕúEÝí^Đ…Ss 2—ă\Ç:°ZŁ\"ÓŕÚŻ( p Ko„#Śń%NÍ5hď3Pá[˝ßkt:#Toč_®č©ą™ď$_Č*@ĹQ~ö^µn{ŻÖ×ţÓ îßęÇŘ8˝¨L€ßIľő܆Ú(×Sj4[ŢéŚĐŮ1tř‡ęGčÂKv䩹Ýłfcg€ZŁ\?¤Qŕ/ŞŹ0—óăzüCu„#śądGžškPÝgż( ţł/#ěĘTß…ęĽj˘0µWˇ÷“ó_Ô_€ >Ŕ.‘0Ú/Ńß…ę´3Ń'Áş†bę@/¦Ţôbę@/¦Ţôbę@/¦Ţôbę@/¦Ţôbę@/¦ŢŔă)~uÉ]©ö[XđX¦ŢŔcY}VňšżÂÍL˝€ÇRŁ3]™zOĺmo0b¶{-7óďđá^¦ŢŔcńÓ7- ďń†á^¦ŢŔcI Đ›†?÷2őK @{ś+˙ŕn¦ŢŔc±§ýLöžëţß+@€p3Sďŕąl·úwĚŢŃÜýd˙ÁÝL˝čĹÔ;€^L˝čĹÔ;€^L˝čĹ˙%O„6˧IEND®B`‚libtorrent-rasterbar-1.1.13/docs/cwnd_thumb.png000066400000000000000000000727771351156116000215570ustar00rootroot00000000000000‰PNG  IHDRúʧDbKGD˙˙˙ ˝§“ pHYsHHFÉk> vpAgú@©Éu(IDATxÚí˝w|]Çuŕ˙˝÷ľ^Ń;`ď¤*E‰Ş–,Y’mɲÜd;Nv×I6N˛±“źłűű­Ów“őn˛Ů8±Sě8±7‰eٲ¬bYŐ)‘")öN‚ :đz/÷ŢßóŢ@€ŕ˘óĹGńŢ-3÷ÎĚ™9çĚ9Šiš&‰D"‘Luľ ‘H$’ʼn ‰D"™R€H$‰dFH"‘\@6›ĺŕÁčş>ßE‘H4–ů.€D2:t‘‘Ün76›ŤááaŇé4K—.ĺěŮłĽôŇKüéźţ)ŞŞŇÝÝM2™¤˝˝‡Ă1ßE—H R€H®9t]ç‡?ü!kÖ¬áąçžăÔ©S¦ÉşuëxňÉ'©©©Án·`š&ďĽó===|ć3ź‘D""Ýx%×";wî$PSSCOOn·ŹÇC<'•JˇŞ*=ôš¦ÍwQ%’‹ ‰D"™Ň.‘H$’Q–1 l6[úmšféď\.'˝U$‰dcš&†aL9–Ď„˛ŚčĎ<ó gÎśˇ˛˛’‘‘jjjřý~‰>źŹ'žx‹ĹB:ť&—ËÍ÷ó’H$’kUUqą\(ŠRúěé§źĆĺrŤF ‡ĂTTTpöěYjkkfÝşuÜ˙ýÓľWY$‰pěŘ1©­­ĺÍ7ߤşşšp8\2>>öŘc8ťN^xá\.—4>J$ÉŁ( ĂĂĂÜzë­477—>O$ANś8ŞŞt]筷ޢ˛˛’l6{ĺČŠ+áć›o&°mŰ6•••DŁQ***°Űí躎Óéäî»ď–D"‘Hć={öÉdJ†¦idłY6lŘŔĐĐË–-# RSSĂĐĐ6lŃ˝Ę 7Üp[¶lÁjµ’Ëĺ°X,äóy, ş®ŁŞj©€‰D2?~ś@ €ŞÎż/˘(¦‰˘(hš†®ë†ŞŞ\ččŞ( ŞŞ–lÇĹż Ăŕrśb5Măşë®§ş*^˙‘G(ݧ8†ŹÓgBYgišVZQŘl¶qżÂË»šĐŃŮÇ>Öł;öiźżźýX±˛†5ó]‰äŠaš&'Nś`ŐŞU8ťÎ+vŹl6‹Ĺb) â ›ĎçůÁ~Ŕý÷ßOuu5±XŚ^xÎÎNzzzX˝z5`űöí444”ŽTUe``€žž®»î:rąď˝÷6lŔëő  ”_N„Đyď˝÷X±bĹ„ďE)Ť×cąp,ź r'ú#GާxŠvÚ§-@2dŘĹ.ި’DrŐăp8hiiąbä•W^ˇ»»«ŐŠaôôô`š&uuuär9Nť:Emm-ŤŤŤĽýö۸\."‘oĽń466räČŽ?Ža$ t]§ŞŞŠC‡áp8…B(ŠÂ®]»¸ńĆiii!Ož Fě’‚ÄĤŽ:VłšS§NÍů;d‘!Cšô´f E i$A‚$I\¸ć»:ɢĺÜąsdłY‰Ď?˙<•••¬ZµŠb±Xđz˝%uQSS{öěÁn·SUU…®ë$ ÖŻ_Ź˘(†ÁłĎ>K0äľűî#ťN“ĎçŮ˝{7ŤŤŤĄxlíííhh,cyňe•ÓĆĚW—Ë5!@úéGCŁŽşů.Ę% ¤‚ 4.î„ Ŕöp/÷NřÎ^řŃ‘{s$’éR´A(ŠÂÇ>ö1z{{©««ăŃG-©± Ă@×u’É$555´µµńŘcʎ( 7ß|3 $“IęëëKöŽÖÖV2™ +V¬Ŕď÷322BSSK—.Ĺçó‰{ŁŕĂ7ߏ˘,® Ć0Ă Îw1ĘĆŤ{Ęď#DŘÉÎ’îS"ąÖP…®®.JźĄÓiöďßO.—Ă4ÍŇc7:g2^zé%2™ ş®Ź3Zżţúë<ůä“ěŰ·Ź§ź~«ŐJ__>źŹúúzR© ©©‰X,FsssÉřśN§yď˝÷ô÷÷‰DŘ·o‘Hv䦦&šššđűýäóyęëëůÔ§>ĹÝwßM:ť. ĹÄ‚Z$H`Á2#ăńTśŕ¬bŐ|WQ"‘Ě|ď{ߣ®®ŽÚÚZ†††xűí·ůň—żĚ+ŻĽ‚ÇăÁăńËĺřčG?ŠŰí&Ť˛oß>\.oľů&K—.Ĺ4M</ľř"÷ß?V«•cÇŽqĂ 7°wď^jkkyűí· ś={–`0Č+ŻĽÂăŹ?ÎÝwß ŔűďżĎůóç …B<÷Üs<üđĂ(ŠÂĎ~ö3ěv{ÉbN§“Çł}űvÂá0¦iňć›oňżń´··Ł'GşIfÓ(—Đd›&Tş|´W5ÍË;XPäMŢ$J”ÇxlVŻk~.†ŽÎ;ĽĂu\‡ů ם$IşđăÇ?Że‘H26›Ť‘‘R©§OźĆápĐÜÜĚńăÇ9tč«V­"—ËČfł¸ÝnŇé4ÝÝÝTUU‘N§9xđ Ű·oÇăńP]]Msssi·v0äÜąsś8q‚Ý»wÓŮŮÉ’%K8vě---rąV«•ÚÚÚRX§ŠŠ rąŮl–U«V•\zwíŮE`$ŔGţ™L†X,Ć[o˝Ess3v»ť@ @{{;Ş˘Đč«!Żë\Ňj‚Íbť·w° ŽNšô¬_WA!Fě˘ßçČń,ϲŠUó.@XÎr†˘žúIŹI‘"L$É ę.… B„đâť×şH®>ŢäMV°bAŘ;::¸ăŽ;čččŕ§?ý)őőő¬YłUUٸq#‹UUI$%÷X›ĂƧ?űiŞüU8EaÉ’%,Y˛„ŞŞ*†††řŘÇ>†Ëĺâ~áČd2ÔÖÖ˛qăFÖŻ_ŹišX­V˘ŃhI…ŐŃŃÁG>ňEaÆ ,Y˛„T*Ess3š¦aę&»Űws§r'ťéNš››©««cxx††>đP]] őśßá™ďÇ[ F€ččŕĆŤ‰9#/¤ÉŘÉNz饎:Ҥ'Ç9Nś8I’TS=ŻĎÁ‚…zꧬ”(kXsQŇDI’óZÉŐÉ+ĽB%•ó.@LÓ¤˘˘‚ 6`·ŰyüńÇq8SďK3 VŁş¶šĺ,/mô+ŇÜÜĚ‹/ľČő×_ĎŇĄKbßľ}<üđĂlÚ´ ›Í6.ÂFcc#@€˝{÷r×]w•ŚäK–,!—ËńţűďSQQĎçCŃ,K-X±Ňm¦o¨ŹĆĆFžxâ ňů|)ÄČTDŁ`±€k9WΩ10f*ް2~ŮĄŁăĹ;ëöŹ0aîäNşé&GnR'Î}ÜGKX2—ŹdFhh8peňť˙ęß“|řĆ}.ąv11馛F§ĺ#F†Ě¬÷ŃËĄ°ÎźÖjs*@NsšňC>ČŮĦ ß+(¤IŁŁc™Ą˘9qŇJ+Ă 3ÂȤjž3Ŕ*(Lúť‰ÉÔRË4Ó<á)@¸˙ţżÎŻSAĹ|WM˛ĐŃů6ßć6nănî.űĽ!ś8ËŢź°ŘłgĂĂĂś śŁ»ľ›Á#<ôđCĄťŰÉd’={öŕp8Řąs'K–,ˇŻŻŹD"Á®]»xăŤ7Čd3T·VŁGu®ż˙z†¦şşšóçĎsüřqNś8¦i†Aoo/étš¦¦&˛Ů,y%Oמ.rĂ9ÜŹşÉ¤3Ľ7üGß>J…VÇëá¶Űn»hům6hiżRÁ9 2\Ďő“Ú#LL¬XI“&FŚJ*gíľ •T. ·W:ęč¦ű˘ß»pŃB ľ71©˘ŠăD§·bť5• dń$ČJV’ Ž>垣±D°–µ 14cŹĆnş±aن9­ó'?ůIűŮß°źŁ=GůĄG~‰ććf<akđűý|ţóź'‰PQQA*•Âçóń裏ŇÔÔÄżűĹÇAßA^<ý"7ť˝‰c–câż|Ë/ÓÓÓŔ'>ń t]'ÓŇŇBkk+­­­X­VFň#„;Ăb?Z¦ŽŹ~řŁĽąäMňCy6ů6±|ĺršš.îIeµÂm·˘ŔBŠ5§$F +ÖIőóA‚ččTQµ(ú+A”(]tŃF[i%Vnç.bbâĹKŤł›îEł)I27äÉSG äČ•ÝƆ˘–ÚËZ©żÇ{ĉóY>;ëőĘĺrôööŇÚÚ:Ξ`š&---´´´!BnIGÖA~ _ €¨( «V­âŤ7Ţ ®®ŽĘĘJ†‡‡‰D"lÝş•ŽŽäć%7ó…{żŔ~m?.\$R úúú¸ĺ–[ĆŮGňůň‘YkÓaΉIÖČqf°ź'Á…ă˘ôÇFH éT6e Í0 ŢÚK›ŻŤNéZç8GM—4ć"p8~–e55&;ü|lw¤Ĺói&PózúPO’¶((µę¸X2ĺ(ń|’“˝4VV36ÜU8%6˘s”sd-*f-('– Ó2qU÷oü×qëY?•–Ě)˝z•üŹÄGóé(žKâyňččÄÂiNĆ{qű­ĚÔC<śs*ÔljŞÓ¬t-»äń#Ç·‚˙ČżŻú­ #Ął{÷nöîÝK*%lŹmmm躎Ëĺ" ł'łŹý;Ńjáß˙Â/’Mçč;8Bő±(›ŻĎË­7Ü**RU=$„ĂĄ0Ma™'ćL€$IĘ…I [é΢xÇ7Ü N»8°łźwtB‹ř|ČĆŢ]…łÍW* đ ľÁŻň«´Ń6î:?姬f5ťtŕ UŃŕ­f$ŕHň«lăĂçÉs*|Ž{•dBůy Ö´ťĐů,Žĺ6Ş# ¨•Ú8×Gßâ›,OŻĄn¤F‹eś±%ť,ÍvR_]ĹČ@ኯźm×L:P„]‘}7’…Ëąüy†űâ f¤Ë {ŮËnv ™Ük˝=lĚH€ś‰śçîä}ä¬ ¸„V⧸žë'='ź× ŐIß”™0ůKĄRś={‡C¬ LÓdëÖ­$ Ö­[WŠ/•ĎçÉf˛ě9@Ű}«iięıĆÁMöéëëĂét˘ë:7Ţx#Á`°´É0›ÍŇŮŮI6›eËő×±TIě¦ç\7»{ާ*\ÇÚęMhj–Í›7sÝuבÍf …B¦IMM mmmx\Ž$NŃnŁĘ¬"ŃŢÚA˝z9šBC!TĘ™3gDĺěÓđtËĺP ;ä•Km]źećL€„aÇA…ÍÇyu˘ÎuAj•4mh\|š.şh#ô*=¬@řYgɲ†5ŚńA‚ĽĆkă tÍBť§_ŢÍáI˦©5îJ†Ł“?w(¸¬ęßÇŢíŕÎáYž˝äŕ)Žq”a†ŃĐč§Ź!†Ć}ßGËč,ĹËk$ Í4O¨Wq»Ě2żdłpäěß/6‹•K_ěŮçĎOď~vě8qbbb1µ)W§I’ÜÁ|–ϲťít҉qŃ} A‚śćôEŻ&B–,,śç<&&*j)ÎĹpâÂvŽa´Ҋú ÷°ôÓĎ N ]Ö©”ěIâ/…(QŢgße\s~™sŹb Ť$ "¨“ b™$yr“îHŹś54j©%Bd\cV ?ăĎKs᪦›nţ‰×h’¤źvť‚yžç/;|QđNQa„ ă—ł**uÔ•-”ÂĐ«“'61gˇP–1 l6[ú]Ě\Ěó;6ŢţĹH‘ÂŔ@CŁ‘Ćq jÄ0MŻÝ=n N’ĉ“ R…Ď‹łg;ö’n4GŽa†±a+5ŕ vł«ôwqŕŐаb-u†âL¨†šIW&—"J”Ą,e„‘Y{1&bÉ?Ńh(C€((ôчŠ:éÓĤ’Ę şĺ"9rÝ /™]Ľ^(ä%*Ećć™m*3M“*·ŹÍëČG&¶Žýěç=Ţ#Mz†^ŃŻňäĆ_ޞ´ n<ü˙ĚŰĽMmm‡)RúÁLPQ‰'E zé%JtĘsâÄŮÁâÄKSÓ zč)M>‚ů{ţľ4vüłŻ°Ş+LĹůiňč 1ÄYÎ,č3Ą,ČsĎ=Ç‘#Ghlldppšš@ÉHäóůxâ‰'P…\.G4Ĺjµât:K›k†¦‰% L˛ ÝŔ bF¨µÔâR]äČ“"…+qâTQE“L i$Nęi@EĹŤŰ RÁćČQMő¸ű{ĽG 5,eé´Żg`RC úA˙§HŤqAĐ ůb†ŹqŚ(((TSÍ9NŤű~€|ř¦T7Ěž@”,LĚ Ž"(]˝1aó®‚‚źŠ í&D8q¬X/ąI°^ˇ¨«ŁŽCšô<6ÜxĘ 2X°LPËöÓŹ ö‚°‚zQO±ţŽżŁžzšićĂ|]ěb ­ă4>üX±2Č ŤĎł˘ĂŠ  2H0**^|”Ł=XL”%@–/_ŽŞŞTTTضm@€ĘĘJ˘Ń(Ą¬[MMMÜsĎ=ă¶őO‡âK8K­´˘  ˘ aa!Úi/ëÁC®°”.6lx nŞFlÇÎ Sş–K1\üXŐŰËĽĚV¶ÎH€\HŠßá;´Đ‚+ĂŽjŞ/*@˛d9ŔVs+ŽBžô"yňäČŃJ+}ô]ô=$H%»ŕ˘ŻJ®G9J+­ść4Ť4–m#;Ĺ)V˛’z»dÔ5¬ˇ•:šhÂ]ř)޸gĘ“<É&6±–µă>71©Ł®n*†âŔ‰“wy—f𩎆˘Ž¶ ‚Ě‚ĄdűPQ饗FKZ• Ašiź Ţ«…č ˝F/—b8ű ĂÚO‡˛Č>H>źÇjµ’Ëĺ°X,äóy, ş®ŁŞ*š¦‘Í^Ú­Äěöb.¨ -4ó}ľÇGy¤ ěăܨ.ÔĤ‚ Ţĺ]Ö°¦ôÄ€›$yQá``Đ@I’ěg˙eëXmب ˘4cĎ’-EřťAB%‹…®ë9Ŕ&u0›ŘTĚ“([N† ň4ÓĚ-ÜB&.Ř räxЧ8Ç9–±Ś0a Ś şX68衧´)S˛Ř1ÉF›bB1˛Ô%›pbhŮ\Žh6A•kj ˇÜôS%/Ł ‰!G“fšXÎRüT”ĺŽ^Ά×1qE?ý4ŃDm$Śž%›HĐbQ&XłdYÍj"DCCŁ™fvň.KYŤ Ă ăĆ(ŚöŚĂ)Ć‘:ę(Mnłdpâ3ťUÂŔ(l]Pf%L~>źGQň“„@y饗p:ťlÝş•ů—Á0 R© ô÷÷łfÍn˝őÖißł,±ŁŞ*6› EQ°ŮlĄżUUĹjµN{µáÇ_ňlČá4§‰/|ĹĚ·ź~q‘‚eTÎĺÉ“!A -¸p1ŔoóvIĎ_M5aÂ(…|á:DG/ąĐ}—ď ‚‚ Ă —^|ą\č}•%‹ç´b ů|AßkĐéiĂ3\SřN¨´ĆňE5Ănvó}ľŹŽÎ %cdU<Î㨨¸p&ÂÎđŻü+ç8G7ݸp•„Đ…((´Ó>®CŽí’ĹG‚$éÂ`v!Ă #ĘÉÄYvśxŹáăčśéáĺ“»e—ĚĎ})ÂDh ?ř aTTâ÷d¸q‘ yI7ůJ*9‘qö>ł4Á»•[ąNŮ™pÝ'ÂXă]Ř účÉ“$IěرaŁšjB…`Żnwpa˛ÓŢ[Qz9ýŘNY|cÄfĽłÝĽ Ż6Ńt'•‰ +ń‚łÁÔe„jŕ6năNî,ĺspábÁ :ëbŠť6BDĆÍşĘ(N´Â„PQ颋:ő,ĺq>Îj:9¤ś¦ä=^Űiy˘vŃE#Ť%A#®UŢÍ­XYÁ Ţçý’ ´čµYŽ'áqŽ܆M–~@ŚQźŕ“ś@ěÚfN–Ł '^ÔŽź,jhĺ(«YÍv¶s–>ňčÔRWĐÎľ1˝hÇĚžńđĂ‹m^/k×®Ĺ4M2™ .—‹d2‰Ó93Źş9Y%öd>Ď::wpä%Ý©Ří)ö1¸pabRM6l%ŐW‘•¬d3›YËZę© ‘FöłźÁ‚’¨¨Ř„W\]8pPI%m´˛šU—śÝL®ÂćÄËĎM>ŔNśňź/…łśťŇ§\Aˇź~îćîqÉ€šićÇüw|q%)lö:Ăę©çÇfő9Ićáä%B6ă1V°Ű4őń bĹŠŠĘ ĽŔy&n‘Ď‘ťr˛&Ě3¬3Ě‘;'+!*©,ÍÄmŘĆmŘóá+čţÇiSQ©¦šžŇŇÖ$Gn\W…O™źĆ˘ŚÎň›if9Ë 3ćK/»UT0A7 ± ˝ŕ”ˇ‚;ˇ›r1 p*®˛gnS‘#Ç]ÜUJIk˘ś:`ň>Ŕ>Ţżh“4L/¨¨ăl†aR­Vsśă´Ó>.¬{­´˘ˇ1Ä'9Éc<Ć+ĽB‚Ä´ž…daˇ›†ţWh›F!‘[+mXpń~ŢJ㮇Ž;~üřđ±ÝŘÎő ,aI©­ 2„ę&& ŞŞ˛‡=D‰ršÓôŇ[p­wŁ•awË“ŢP¦‚˘¨ă®ďĆ]tyş(Ĺ˝e·sű”›!MLîç~*ŚjtÓ –ZÎr˝[%I˘¤ ů¨ůQÂf¤4Mę{yb˛řóđĚÉ D)¨ `t´‚xéI’ť]´Đ2Aýsa¸’x6Ĺ GŢćÔđřʆF_|î“ALeôú* çĂĽ~f/壴°›ĹB"›â@˙É ËÝ3ś™–A\KŘ9u|·{÷Mšľwňˇ2t"M$™4$łgI€ęÂis cŕÄĹR–bdžÔ°dFĄ˘(Ś$Âúl>‡aذaĹJA‚ÝiúŹ”Ľ6¦ş¦Ş(¤óă ÁŤ4`dBńJA!’Šs6(ě)E»ŚMłrcë:Bů±K„8¸–ś••®ŞŚęiEŽçHgłS60Ý4°iVZ«Xn.ç—ř%¬XńáăxęZ~ŚŇů,ÁD„Ľ‘g«J;oÓů,š˘’Ëç©§žëąţ˘ĆϱtĐqQ7MÉÂÇÄdym+Ëk[1ĚŃ?ĺŞpMLi×§$J†t>˘öe`ŽSá$y’™Ě8o®t>‹ŞŞTĺkfćoń.\ĺ(iŇe0TͰJĆČäł—ytjŤ:‚É™|®ÔłL̲Úń™‘b™ä¸>™Č¦ŘĐ´ źÓM‡ŮÎCńf)ÔŐ‚µ¤Ű]ÁŠ ĎĐ,Ôu2Łš‚‚¦ŞĄŽaĂ6®宜ć|^äĎL˝N]ÓWTT迬 | FgŘĹv•#GŤĄHš4ç”® “#VEAxX&I%J -8pPK])đ`9ŚmÓ@AUdŠ{(Łe ć0‡/y=EQ'–Yµ0~Ý˙FŁ|ű‘Ş(hŚ3Ĺ0.˛WĹJdÎn¤srzľŕ–š'L±˛dŽI’ĽMF%U¸Ű‰ĆŞŁ“'?é…ł‰JtĚŕib–6G Ź0“wy·dO’¤‰&4´’p릛1ińlÝÄd{É•±Éj¶ŃŃI’,Ä Ë2ÂH陓„żKâqxńE._.K 1„Ki€+ö‡,ą’ĘLa„F÷,9q$oiĎVŠgéš´Ż v;ëXÇzÖóeľĚ'ůä¬x+őÓ‡Ď8/KaĐ«aä˛ Ű* 4Ó–Ň6n‰8Ĺ ?ů V 5T_t`·_’:C† ҤKúÜ(Ń h±3ęč ”Ęx±Ýµ"Ä~̸#WQÍílż¬¨Ą“ˇ˘˛Ź}ĄíÁsQ•ă\ĐÖ&ň`¬[wů×’ o˝ôÎ8چ‰I†LiŁÁ«¨*E˝ľÔDj˛\>E44j¨™2gH†,Í4aĹJ%UÓŽ/5ŐýËEC»ä5Ä–„šR(…u¬ťÔíy107^XşŠ’ÓJ’ň†N*?ýě}¬tŇYÖ «aˇŤ6rä/«aUfm´ń·ü-§9ÍR–ňďMë‘&]đF±ŕÇŹ ÷”!ĆRLőy9˝'łKqCÖXßů¦ľ~úąŔ%“c``Ă:NL‡4i’$Ç­Š×uă¦rä8ÉÉBˇ™Qôś1ŔرO{cđCs’›ĂÄ š*€ŇäÖ‹o\¸¦ĹÄśk‰=$ňť«(“ş 3\¶ĘEGçfnća.űľ›Ů4.ÄÉ0Ă—lęBmŘe€zéĄ*¨(ůŞńâ%Ŕvl(¨8°ăĆ}Ů*źąÜtt1®}­d<&bÖ<ŔŕŚW”qâřđŤ[Á(¨Ąę::˝ô!Â&6ÍÉĘŐ˝äf|±ľŁ 24fCźŽNuWĽl&"tË˝ÜWňµ`aAÎrv^í‹3anTX¦‚b*ĄŤ<ş©SiVMÓ +˛Zg¸ůĆ,Dťj–/6ąč¦›Ł÷ů2–ńŹp”ŁÔR[2:ćČ•„ Č]’c¬Žąžú) éŞŮ.¤hÄž‹Üĺ“uncŃ5ęůÄ0 ·HâM,a 7sÓŚgß LÚ6EĐZÖ°†Ýě&Ta7›âăG&Ř#Á¤–ZTáůE×$aÔMŞ©Ć†­4ą+ĆÖ› TTîâNĽ…ř^UT'ÎźńgăÔt80­móÁĽĺS´bĺ,gćtf[4j_¬w­¦IóMľYň30褓۸ 8Jk?ýĄ˝~üá( âeů®Hů>ďO˛a˛äČ’Ĺ‡ďŠ .Žqlśş­"BdÜJQGźőĐ/W gĎÂĎ~ÉËŹ`3'Řqđ0.ËŘ-Á »9ÇŹřqâťą uM5·r+M4îQľ *EÓ60&Ř@vł›·xë‚{Ó̆ȓ§ťśô1ţL•ż}.QQy„Gx€JźéčśáLY)‚ç·ěó„+óa6ły++Ľ°¦Ú¨ 0Ě0 4”ňzŚĄ—^¬Xńă§ź~,XJ;Ä«¨â$'i¦m’oN˛aë,gI“žp ;öq.´W’h¤‘$Ł›¦ŠyS>ĆÇJ ŮŠ•4iŢäÍ+^¦Ĺ†i ÷âsç q•&s4 Fŕzyč˘ë’řŐ¬fۦ=ĆVđ˘ŽÝ6Â,6¶…V’$ç9?i®›…†¸zJ{ă€ŇÖ…žĚmÎĹ› dô¦iNČ·|%ŢWµD‰bŕ ę‚\ČăË(–ÁI’%÷ÜâçµÔ’'?AťćĂÇ|'DHŚ #pĂśŕŹđ0Ŕm˛(Ĺ PEĺ%3+Î& âĂWHź+~¶ŚÉËnÁÂťÜÉnvĎI™Š×]+V@UŐ|—ćĘaĹĘÍÜ„†FŽÜŚ đSQěk¶BČs‘ht¸:‘Ň^Ą qâÄ…“^zi˘iŇţS Ą”š…`§ł…ŠZJ{ÔR+W c1M—ŐŽEŐ áćv–±ě’ęĄ;¸ťN:šćf66°qś:Ŕ^z10x“7Ç͢"DJńŔ 4`‚ÂĂÍLĆtö·\kX­W·đ1!ş™­4ÓĚ kšÜĆm—e”VPpศ‹ó0iŇ6#«¨¨hĽĂ;TS=©Ćˇâ`:‘"®4-´ĐO?‡9Ě»Ľ» Ôk—bnŕ˛9¸~Él–ąŹD©Łó!>ÄŤÜ4…a[a·–ň±_^}E.ć!†8Ďyę©/mV*ÔëY_X- ›BŃhç›b…¦˘2Ěđ„°2’k‹Ő¬¦“ÎIú‰É­ÜJőe¬LTTľŔ `ü@zžó$HPC őÔ—ÂźŚ=®†¬Xi¤ń".˝f!Ňď™á×RK–,::e¦úťoÎÓ›LŔű>ëC-¸úÍ<‰P í`g9K+­%ťć‹Ľ •TŇOAe4żÔRK]—fE˙ţ^z§4’«›U¬b%3Ëdw)”BĆÍÄ${¨^ŕ…R2,±B1‰a íś!Á*V–™~Ů,üţgűV¬ 3L 5X°\öÎřą ¬H6›%“É ë:ŮlÓ4Éfł†A.—CׯŢč¬Y˛ă^äŘݦ&ć”.¸"ţżź˙ĘEG§‰&ňäfÝěf/{±aÇD™gŐ­đSNgŞžb7żäÚ ”KçŠcbÇ^˛8qr;·b%"„[abhâqą¸‘§¸˘¸f?¤H$8ă-łEŃ3,K–ŹňQZiť×ň”CY+Ó§OóěłĎâóůFŁÔÔÔđűý$ |>O<ńŠ˘ËĺFŁX­VśN'Ú"Ţ2<Ě0nÜĄŕŤ6l<Áxń%ĘŽ`Ç>ÎČ|!Ĺ0 "4Đ@š4‡9Ě&6ˇ˘âÇ?âG4Ó´ fB’«ŁŚôÇ ‘b(#€đÚi/ ®Ť4NČRZî5EöBiŇTS}ɬ…sÁCxđ\±•ÝlS–©ŞŞ"ťN“Ëĺ8yň$Á`ęęjÂá0n·ŹÇĂcŹ=†Ăá`xx]»váp8زe ^Ż·ś[,8šhb/{fĺ,„Jk+j>A‚vÚÇ’› łŕEâÇŹßä›|•ß-yˇŤÖý°‚‰O#bńĺ   N–삱ÉHfŹ^z©¤†+‘ŹűJŁ ”\wÇ®,¬XŃĐf­Á‚…i"DĚjÚ‹wQ¬<Š”%@zzzŘşu+n·UU©ŻŻ'PYYI4Ą˘˘»ÝŽa455qĎ=÷LXyÄŇIrzľÔtcé$ńLŠśžĂnµ‘Î •išDÓ Â©šŞ‘̦Čę9"éé|–L>KŢ*3…P*ŠnÄ2IB©h)IŽŞ¨„Sq‘ş0L“p:†7í"™M“̦ §b$˛iŠÁ:’ŮÁd• {%#F€L^'mÉČĄQ(†#аâdH!Ş'KúVq߆i’Îe$"Šaâ¤ČáS«°é.‚Š0BÇÍ=–~rôk•“ú)Ú2íDH`b FÉ-VxNşiĎ6=WÚíźŐó“˛ůşˇLFÉäł"§ł˘IÇŃ p:†/ĺƢj(ŞFÎ09m9C.c¦pmMĂ4MtC¤AŐL•¨’ ÚQ9Ź;‰$ÓEÍk¨IŁh0‰Ą$˛©R˛Ąp*†nDÓ "é8±LÝ0&Ł$ł˘ýÇ3IB©‹Ťt.ašĄát¬Ô®Ľ)W©?*($˛)ě+¦IéúŃLÝĐ Ą˘XTmśÁŰŞZg’¤r‚É(ŮĽÎfóTÍJRM“Ée )1L;čŞN8#«ç §â¸¬Ś‚ ĽřźÇb± ë:ŞŞ˘iŮěä^fađľeéjÜ•xě.¬šĆÓ‡^Ĺëpá˛:éPíňóŃőwňćŮ}¨ŠĘůđ m•ŤllZN"›ć­łďs ď$už*Ö5vbb@]U·”X&AW°UQ9>tŽt>C6źă†Öµ8­ę˝Uś öqr¸›ý§¨vWđý˝/ĘĄ9:x–?}č7pZřÎ…¦âńźÂštň~â$v‹ §ŐÎŃÁłÔ¸+č¬nˇ#˝†#ÝüEř{Ü˝âFśV;ŻžÚĂÚ†N66­`(ä\¨MU©ěigźý$>—›ë˘wŞKU BSÁŻbEŤ—űë¶á´:ČćrЉk¤’Á˙z0Y߸ŚS#=ln^I(ĺű{_ ­˛‘ §‡áxëZÄŞ¦Ů_ËP<ČS^ˇÉ_KµËOŢĐY×ŘÉłGޢ72D‹żžUuK &ŁTąĽT»*$"ěď;ANĎŁ* Ő.?/źÜM(ĺž7±¶ˇ“`2JW°öV!™M3aSÓ –TÖÓŕL MŃXSßA(%‘{Źz#ĂŘ,V‚‰±LĂ4ŠńZP«Ľ•xEżĂK,ź Ęí㌫›ťGOQ«ÔÉgé Ť¦Ü5L“ë—¬á\¨źˇX…zo©\šX—‰şÔĘŢcTŃ‚ĂjWćÁX€t.KodF_ 5® ,š…ĽiPĺň1 đv×~Ú*鏎P2Ę›&5îJŞ\>úŁĂĽŰ}mK7’ĘĄůÎîźđˇŐ·˘)őŢjNŽś§+ÔĎp÷ů„ťs˛ďĘľö|WNrqś…śscĘ(8Ws•ŢS"ą<LLtň“zSJ®, \€üÓĽfŚ}ĹĽÍÚ\ÎŞĚňď$ž$śA"‰d¶01é ť5¬™†Ű«ÉÄ–>ó>VrşY°DQ”RŘ ˰X&9©‹ÚŐŞ(Ś$ÂĄÝąsqżH:^ö˝TV±ŠłtÍó“’\ë¨hÓĘëá¶9±jŁ;ÎcéÁB(‘™ŕ¶;±i Ć0§ĚI­ibIŮÇ›xín>Ľîv|ö®Y«;—Ý€Ş¨Ąř:W3n»“j—źă‰(Î++@Š;s·wl&ŚL–—(Ş’JŚ’MQrmb\ĎÝŘ9D×%łŤ¦É}+oˇÚ-ŚĆŞ˘0śÓŕ­Ćiť™Mďľ•[ÇíTż–¸â+¸z۸>yą,©¨çl —p*6î»ÖŠŽź»bĎôJ0o–EQ°Ş–Y}9ĺ * « yIĘ‘ô&Pĺň±¤˘~b€%őôE†Ë›5&»«t˙ń_™řěîK–Ćn±–r}ŚýܦYY߸lÂMľÚË~fm•Ť—} ÉB`ö¦k¦iÍ$'̢g …ÎęŇą ÝáŃ•“ Ô{«°Y¬W|ňiŐ,,Ż)ß{´LÓ¤ÖS2|ގÖSI­»Ý4Í–Čy U㡵ۥ׏D˛H™żAn1dĘYf’Ód>™×Ń{¦~׉D"™®I#şD"‘H.ź˛adłŮŇoÓ4Kçr9t]zčH$ɵFY*¬§ź~šˇˇ!jkk9}ú4µµµü~?‰DźĎÇO<˘(är9˘Ń(V«§Ó‰ ę*‘H$W'e ÷Ţ{]×y˙ý÷ÉĺrĽőÖ[TWW‡q»Ýx<{ě1ĂĂĂěÚµ ‡ĂÁ–-[pyg"@"‘H$ —˛ČM7ÝÄ©S§Xąr%@€šš•••DŁQ***°Űí†ASS÷ÜsZ!í[ţ’ąí$‹™(1˛d±qŤd_’H$%Ę >ř ů|«ŐJ.—Ăb±Ďç±X,čşŽŞŞhšF6+c6]+Tŕ'@/űŘÇMÜ4ßE’H$sLYFtUU±Ůl(Š‚Íf+ý­Ş*V«µ´Ú\K8q±™Í×L’/‰D2ž9qăMç3×D‰D"ą–Jƨpzç»®‰D"™EćD€XT§Eî:ż:Q™ďBH$’y`nv˘+Ę|Α\!L ި"Cą™T"ąÖ‘ %3Ć,X°#W—ÉBćرchšF>ź§ŻŻŹúúzúűűinn¦··—ŽŽ:;;§}])@$‰d‘cš&$ qć̶oß>î»W_}›ÍĆąsçčîî&ťNăőz‰FُÝnV¬XÁďüÎďLűľ G€”Ô\S:l•“vă Nq¤yŃ´VćëŚůt’ÂŤ?s.Ś»§9és1Í™9Ń^XŹâS0Çý%ąV)§]MhCłÖdƶÄÉúÜŘľ`Ž?í’ežěNÓ-řhůĘ>Ă4/¨•rŃăfR.EQhoo§µµÓ4Ç……W·ŰÍđđ0V«Ż×‹ÇăAQş®ă÷ű§ů s"@´¤ *¦>Ćf± Š˘ŕ˛9._w‰kiVě[!ÝěĹqXíč†ßá—áDF´ľČ†i˘*ęslX 9LTEŢYpYĄóťVŞ:}ÓR&ź%“ź¸3’ŽSă®Ŕeu``Nx.N«}FIąĆÖCQ@7 TE!«çЇđ\ŮđBEˇ;ÔO$cĐŔtIł 1wŮ™4.Ő®4UĄŇĺĂŞZ Ç;đŘťúŃL°¨Z©YT­”öa´˝ĆDŚÄ€hąxDEQD˛¦1e4 ţčČ´ĽDÇ–Ż\VVUC×,SŽMâ8ËŚúµ×ë÷{loąĺt]Çëő’L&©¬¬ddd„ÚÚÚŇď™ ł€7›Í˛cÇŽqˇLtĂŕŻwţ€ŹoĽ—:OĺEĎMfÓ€h¸átżĂ3ic,ç˛9.z­T.˘(üüř;T:˝ÜÚ±yŇă˘é8†i’̦©÷UŁŤ9=ĎP<„UÓ¨uW–],“ÄŞYpXlĄ\áé\źĂŤŞ¨„S1K a(äió'üZó¨˘zşMF2G$ňIŢ<÷>÷´ßŚVĆŕw©v•Óó ĆÔ¸+pXl„S1rFž*§Ňë‡S1ľűŢł|q룗śfőśś©™|ŽL>‹Ďá&•Ëŕ°Ú0M1±ŞpxĐM@"Â3G^guÝR6·¬Âeu\´ĚˇT”wEiRϤ§˘Ôz*±[Ę Ç3¶|ĺË$±iŚÂęŕbcS±_fóYśVGIhN‡={öPYYIGGÇ´Ďť.s˛QĚKĎJĆ „Ę)fS Ž"ĺ&Şň9<“Î>¬š…f˙D©ě“˙ܢjXTmܬg¦ű]©\fB=ŠuąXť|ŽKĄÁťś ëQç©„€ňžËTŘ-6šüµXU µńt®LjSÉěĐKç´łhĘ-e©veŐ,´řëJĎć>/›6*`ěkIű€˘ŚŽEŁŢ[…ßáĆn±]TxË\lçEźgëÖ­ś>}ĂzďNirîÜ9Nž<Éđđümü“H$É•Ĺ,lü5/˛řg?ű‹…¦¦¦K Ň ¤±±‘d2‰ŰížďúI$‰dš†ŞŞý|>ĎsĎ=G:ťĆëő ©««Ł··—m۶±|ůňqÇßpĂ ěرÎÎNššš¦Ľ§ ›Í˘( wÝu©Tj–«%Ă\H$É•Ä4MöíŰG ŕÜąsÜu×]ĄďR©Żżţ:‰D‚x<ŽĹb!ă÷űQUu‚©««ăńÇ/뾀`0Č~đÜn7źůĚgfµb}ôŁÉ´#‰DrĹP…Ť7˘( ď˝÷Š2şłWUUęëëI§Ó¦I6›Ąłł“X,Ć’%K&˝ža†Ĺ2őN @CC=ôĎ>ű,Ď>ű,K—.ĹëőÎJĹ iÄDnV–H$’+Eq°żpĐw»Ý|ń‹_Ä0 l6©T ŹÇC,Ł˘˘bŇk=ůä“D"xŕš››/zĎŇŇŔn·ăńxXąr%étzľź…D"‘Hf Ż×‹ßďÇétRUU…ÍfŁşşzŇ8W¦iŇŐŐĹ»ďľK4ťňş*@$açÎťÄăqÖ¬YCmmí|×W"‘H$ó€˘(ÔÖÖ’N§/ĘDčííĺرc¤R)Nś81ßĺ—H$É<˛oß>,Ky ›ššxôŃGÉĺr¬[·nľË.‘H$’y"źĎóńŹśĂ‡“L&§<ÖPQQÁĂ?<ßĺ–,R’¤xť×YN;ë‰d1óÚkŻqŕŔΞ=Ë–-[¦<öŠGă51Éú|?“yÇÄśrGŚY8f1bÇN;íôŃ+Č% ˇî«ÖŇ\di•/5Ě'k×®ĄłłĂ0.ą±üŠ H*Žiš(WiĂ-—u ťŘ-OŮ×QÝL&ż8ť44*đdhľ‹"™ÝĐÉv[Xçoźď˘\165ŻÄďđĚw1ĘćRăÁ|rčĐ!věŘAuu5˙řǧ<öŠ Ă4¨pÎΞ’ĹLGuË”ß7űëĘĽ’D2= Ó¤ĂÖÎúú嗱ʊÚÖů.´¸Ôx0ź455ńŕRQQAUUՔǖ-@Âá0ů|žD"AUUˇPżßO"‘Ŕn·S]]}Ńs•k}ů!‘Ě7ĘBUHkÖ¬)űزH__ôG„iš8ŽRŔ®˘`đx<|ő«_EÓ4"‘ÇŽĂfłŃŃÚÎ5Ż»’H$’«”˛ČkŻ˝F("ťNSSSC(Âçó•VŮl]×±X,†A6›źaK"‘H$ĺS–ůŘÇ>Ćš5k…BÄb1ššščďď§¶¶–p8Ś×ëĹĺr‘Ë娬¬dÆ Ł[äcRH$ÉŐHYÄjµ˛qăĆů.«D"‘H2ÎşD"‘Hf„ ‰D"™R€H$‰dFH"™TT†&Jôň/&‘HR€Hfh —Ţů.ŚD"™#¤‘Ě **ŐTŁ\­Ńú$ɤ‘H$ÉŚD"‘H$3B Ée“7 ‚É(©|Z*°$’k)@$—MłŻ–ÁX€==Ǹj3I$’ H"ąlšüµln^‰¦¨ 8ĎšD"™m®xB)‰D"‘Ěş®óÖ[o•ŇqŚŚŚP__Ooo/×_=ÍÍÍ3ľ¶ ‰D˛Č1M“ýű÷čęęâŽ;î(}ŤFůîwżËŠ+čęę"•J‘H$p»ÝÄăq>ýéOĎřľR€H$É"GQV®\‰aŘíöqY`- ÍÍÍ„Ăa\.N§“ęęj˛Ů,•••—u_)@$‰ä*ŔétŽű]Äëőň«żú«čşŽÓé$‹QQQA0¤ˇˇá˛î)D"‘\ĺÔŐŐ•ţ]QQÁrąH/,‰D"‘Ě)@$‰D2#¤‘H$ÉŚD"‘H$3˘,#z$!‰PYYI0¤ŞŞŠP(„ßď'‘H`·Ű©®®žďşH$‰d)K€8p€çźMÓÇăhš†a%_cŹÇĂWżúU4M#‰pěŘ1l6KZ[QFh¤uľë)‘H$’Y¦,R[[‹Ăáŕĉ8ťNÂá0>źŻ´úČfłčşŽĹbÁ0 ˛Ů¬HqjŽ%K=őó]O‰D"‘Ě2eďąîşëxŕčë룹ą™ţţ~jkk ‡Ăx˝^\.ą\ŽĘĘJ6lŘ€¦i 3Dm¬;öů®§D"‘Hf™˛ČŞU«XµjŐ|—U"‘H$ é…%‘H$’!Dr•cbb`Ěw1$W!WT€(†J0'›ĎÍw=%WE|Ú„śś“,4zÓůł¨rľ(™e®h‹˛dí śHJG绞’+ŚÝjc“c=Ökľ‹"ąŚĆrk'ŠĚ6,™e®ě »&=°®j\r–»QQđؤ`—Ě>˛·K$‰dFH"‘H$’!D"‘Hf„ ‰D"™R€H$‰dFH"‘H$’!D"‘Hf„ ‰D"™R€H$‰dF”ťD"‘H$‹“|>ĎPU•d2I]]}}}¬]»ö˛Ň‘K"‘H$‹Ó49räápÓ§Ołm۶qßýô§?ĺ›ßü&555Řív2™ V«•{ď˝—O|â3ľŻ ‰D˛ČQ…ććfęęęH§ÓăľËd2D"p8hš€Ý~y± Ą‘H$’«€ŠŠŠŇoeLče‡ĂÁç>÷9îĽóNt]'•JQ]]Íŕŕ —uO)@$óŠ®i‚E¶D‰äŠŇÚÚ:îďúúúËľ¦ô’̆ű÷Ăo@Nć“HeÍű"‘ápŞŞ*‚Á UUU„B!ü~?‰D»Ý~Y–|ɵ‰®C_„BÍ‚Ő:ß%’H$Óˇ,˛cÇöîÝK&“!—ˡŞ*†a”ôlʇŻ~ő«hšF$áرcŘm6Ľµ•2 šä˘X­p×]ĎŰ=ߥ‘H$ÓĄ,âp8đűýD"FFFFŁř|ľŇę#›Í˘ë:‹Ă0Čfł(Š‚išó]?ÉÇ%ĺI$‹–˛ÓédÝşuÔ××ÓÝÝMss3ýýýÔÖÖ‡ńz˝¸\.rą•••lذA¬FŇq¤ ‘H$’«“˛Čí·ß^ú÷¦M›ć»Ě’J2P…͢BŐ|—D"‘ĚŇyR2+Äă°űM6,m§sľK$‘H®4R€HfŹnľ=†Ő˘ŕtzć»8‰dD2kŘ+CXT D"ą %ł‡ ý¶%’k)@$‰D2#¤‘H$ÉŚD"‘H$3B ‰D"‘ĚY÷Â21É“GAAG§`Y•H$ÉUƬ •>úx™—±`!I†$I¤gŽD"‘\}ĚŞ10h¤‘;¸ "Ä9ÎÓČUD"‘\}Ěş KEņ +VLQy%’y¤_$gó] ÉUČ5˘¦Á˛ę%tÖ,™ďzJ$×$&&‡‡Nă“áe$W€+,@L:kšiŻjšďzJ$×$†iŇnmŁĂß2ßE‘\…H7^‰äjG‘*dÉ•A ‰D"‘Ě)@$ł‡Šô¸“H®d8wɬ‘‹úAf$”H¦irúôi ĂŔĺrŤF©®®fppŽŽ<ž™;XH"™ xçU?V+´|Lf$”Hć’˘FŁś:uŠëŻżľô]:ťćµ×^ăřńă$“It]Ç4MLÓäľűîă‘G™ń}Ą KrY&= ‘0X])6^ź–ÂC"™<UUUV‹…t:ÍŠ+°Ůläóyâń8ů|ž\.wY÷,k’ÉdĹbx<©ŞŞ" á÷űI$ŘívŞ««çűůIćáaxţy¨®‚='â¬X§Žů.–DrMˇ( e|ř¨őë×ăőzŮşu+‘H„şş:úúúX»víeÝ·,˛sçNvîÜÉĘ•+yőŐW±Ůl†Q*¤Çăá«_ý*š¦‰D8věv› Wµëꦮ~ü>¨^ž˘®QjE%’…„Őjeűöí>_¶lŮe_»¬Ţ^QQA"‘ żżźxđ¸óNńď‹‘H”'܉QuiÂȸo"1;Ď)†§ž‚ď~W„ éí*ąÚÚńť źüţő_ˇ§G”§»[<÷©ę•ËM~źlvü37M!”ĹFÂW^=?;ő–̆?ţ1>\ţ9;vŔßţ­hvűäÇd2đŢ{—7(¦R°{·h“»w‹zéRŃćĆbšăŰu&#ŽżpRU<Ö0`×.1žĚDť Ťö“X }˙ç?ýńRăÓţýăcşşŕźţizď`!1ç$ł—żýۉ/9›…o|ž|Nźm€¦)l+W^Ú04ř‡â˙ň/âeđĄ/‰Á8™üĽÁAxńE1hź?/Ž»pfm˘Ńâú˙đŁisł8çR$•š8PëúřŮ\ _˙şhäˇĐDŐX"!fc˙öoâzš6ú]&#žÁиfm-<ţ8lŮ"Ę9–‘q}Ă€wŢőFżĎçĹ€ńꫣe8v ľ÷=xűmř˝ß—_‚ýĚ™‰×—,lvě€ßüMˇ*g0-öÇ~>ö±‰}1źâÎťđ?˙'*„Á˙üź˘,ú§đĐCâřÝ»EĂ_şT<›¸˙~¸ţzQNźO<›ęj18˘Q›¦„˝˝°~ýč ?2"îđźţ“¨ßľ}â¸ÇőČ1Ŕ¬Z›6A}˝4?ú‘8vh~é—ÄóXľN.ŇNp-Ŕ7ż)&m÷Ţ+ÚOÓýŔn?)ĐuŃ>ęęD˙şĹ$mŐ*Ńţúűaőę‰+Ă‚ˇąY|‹‰6µfŤčSn·h[‹hó>¬X!Vş âłbľő-q˝?ůńym­(˙éÓbĽ(N®Îžł¦Fôˇ›o}ŮăŞt]”ű•W„ lőjqďsç cÂu×Áşuâ9ůý˘.†!ʡ(˘Očëv»8WÓÄ8e˘ďz“áż˙wŃî¸Ct‹E”˙Ă×NĄÄ2·ŁC äV«¸×ç>':Ë˙ýż˘,mmB0>ő”xwÜ!žÍ·ż-řŻüĘčJÄéżź{NÜgĹ qť={Dą{LĚ´l6qťšQ—HD<‡XL¨ďşK ’ĹĹđ°xüÂ/Ŕ=÷Ś˙®ż_‚Ď~VLz@ Č™ŚhŹxçÇŹ‹öZ&‰„čłË–Á#ŹsŇi1I»Z9q~ă7ÄD哟š‚˙ń?ŕ×~M¬ ÚÚDżýüçŵ‹«‰›oż±XDßý·÷g>#ţözĹ8LŠLµ¶Š±Äည áPóÜsBljť;Ĺ +•ă•Ă!®ăp{?đ€čĂýý˘ž>(úČm·‰úŻ]+úě˙ţߢLkÖľwĂ đĂŠ{tu‰g×Ů)λţzŃŹ_|QL?ô!q›máŻęݏ«vWU†xŽO}üU°ď¬_ (°f|ű;Đľ^{>˙čę†pŽź€wvA}ěŢ?x›á áaU]µ5đˇ‡Áá‚›o3¬_‚šzčď+śŢ~xw·ţ˙ýŔŞ•˘áőôĂ ËVŔ›o‹Fd± •ÖO~ůüĆáŘqč„SgÄj©u ¬Xw}®żAÔőąŕĐaH§ ŹůÜvlÜ,Î ‡á§ĎОÜy'¤2BýúoVCđçD#}ŕA1€˙ŃźŔŤ7ÁíwŔşőÎ@΀?ůną–vŠNúÔŹĹsGá­ť00$®µi ŹŔ—~bIčîW^…×^ űč1Ь°ó]xěqxđa'ŕ˙ţ+|ó[pűí°f-|âăă…‡ąŔűµŠ9&@™nöśÉÂďţ¸n ¤łpň D#pă Đ׋ߩ 8»w‰6÷•ßúo‰ Üu×ÁüqÝ~.ÚšÝ żhWňßEűüČĂ`łÂąnřöwaßű`s~Ľóm†D?·X`Ď>¨­‡? ą<ě?XXµoUŢ>HĄá|Ź(ß¶íŕőĂ«Ż‹•L:-ńµëĹ}Uů7Ńľoąľđ‹°u›$=őcqżcÇŕ»˙0bRW]Ďţś.1YýࡽęáĎľ.®µőf¨đ‹ľ¨(pľ^~–vç82źĎ>ú¨XQíx^Ţ!„ěöŰEŮzzÄ·n#Ľű<ň¨Ż›ďÖ35ł+@ňyá2qř0h´Tgt"` ÁÎż…Ű«ááÁ¦6xýŻ„ŞÇýi±ě ‡`Y ü˝`?ď}’çŕĂË Ł<]Ü ‚µ*Ľđ?!ň&ôÂ-7ÁĂżg€ďýŘĽQ –˝?Ű·ĂMËŕL+,ŤÁ}ŤŕY)tů›4!¸¬'ÄňüĆŤđżţ"AŘ„/ţ2ôŘĹ˝Wn€L ţéĐŐ »–Á×~ľ¸vżOý>|îóđĄ;ˇaX̨<^Ȥˇ=k6ŔŰ˙ďﳲ'>ůµ°¦–e#Ł ý|Yx° VçaÓČÔ‚ľôôü vĂ’ĄP3/C0 9ŕ´6BkVdáńµ|Îż /ś„3ËŬŞí °<Oţ¤’pťÚ[auÜ^"pâGđç/ ˇqţmđő@GB”«>ŐVŠ‚=؇EŐ ",wi-ÓŔÝw %h§Ž‰Ől0›6Âővř»o©ӢţáCs <Ü ë,đ‡ż%T36+X`e#śÚ Ďý%čA¨[ĆQGŔr 6hđÖ7a ZĽŕë…żřE°|îşţîŔóĎŔ/> V/o˝¶sđ•ŹŔ'Zč=/†˝ŕ"ľ<«:ŔqžzŽ‚HAŐłFG?/&’Ď?ůý°¤Îš¦_L¬Ž=Í!¸{;üü%vÂď>ąNĚüĂoŔ-pĚ?ţ=0Lˇ©;Äą©,„Ćâú›áťźÂ®ď@břu9vL¨ĺăüđkđËżëŞ`e>µ–¶ÁËŻÂŢýĐT§Ăý-ŕď†MíPÝŻü9ś:mâďłńč—۱{îF0Ĺ4gݧgC!vüŢďqOm-š¦‘̦ §b4ůëČë&?ů‰ \· î˝ŢŰ š"„Ć’V±"°ŰÄňúđaX˝FĚú»ÎÂŇv8uR,Ä ¦±A̤˛Y±ś'Ôań? O|vŠÜ+®ů‹°m«¸ź®ĘŐ?zZ|ľi3Řě0Ř/ÔmŤŤbĐŽĹ`÷{böŐ¶{Ţ~W¨˛–·ĂĆőĐÖýÝáá´s§ĺ<$ęşf ěŮ+Tr«W ;Guµ™)•P"gN‹ŮSQ7ěv ú5káÖmâ9˙ěeXŢ!:ŚŞ ÁfŃÄ ,ĆÇS§ĹłY»ü5ĐÔ –ŕĄr˘0’ˇ)*•.?ČÜč Ó4éĐâŻGł¨ś8/ţĽ`żSŕáÄűml6‚PTE¬2ÄŞ h[ŰĽI¬::ŕç/Šţâv µĐúőb%żóxwܶU´Ź@žA¬PnŰ&Tˇš (ÂŃŮ)ÔKMͰë]Ń?b11ńk]*VîgÎ@s“dĎľŐ•ĐP/Ôá×]+W‰ş&bćołB®`Đöř sĽńş¨×Ý÷žwE}n¸A¬ş÷î;¶‹Éco/Ľţ` Í‚^°_Üxٰç{`íŃÇ’)¨®…XěV15űî÷~úi±bqşa¨6n‚Ę*1YËçEżL&„:ůÇ?ç,_%ĆŚHŘ$lúYóÇźaŰ>¬ÓęďŮł‡ĘĘJ:ĆŻł»q»…e÷ž{@ÓH%# „úij^Ĺł?Ż|Ě:řČ_±^ú3aSČTÁő«Ŕ“h˝ďÁ7ţۧ­iĎ~âxĺČf`×yřúzÍďţ ŻźKŃLľ˙ç˘Ó<ň(,n‚ř»°ďIhű0üßúĎ€÷ ëś„O+•#Ľ:› řźö[š°Y=«î€oôÉżřxř7Ŕ~ě‡ŕdÎ;áÓźÂDg:rţę°x6/AĄ óë`; ßţřŇpĎDCŹĹDă6ěđÝoÂŹwæVřă?‚3g᏿güđËÁÚǡĂÎďŔĘ{Ŕ˝\Ü3źÓN Xwţń+b†őőßg hŐŔŇńŻ?4ŇŤEµPYŐtĹ®¤< Ó ÷Ü!š[×MXxęđĚq¸e›P›Ú?˙ž| *> ?z ޶*Hś*%§ęĽ0ř¶XüŻ˙ń•ÂßuˇĂ„ď~6˙'Ř÷mXţiц+ đüŁXőD7‡ ŻWŘţv?|ň¨˝âř‹p~C°f>q7<ţTçát/üĂßÁ÷­ŕU Ó ýqřŹŰ ósçš48n“%U…żű;1)mł@Ď a·Üˇ‰ŐÇꂥęĂŢ?† źŻnű Ô?"Ô`O˝,V:˙íO`ÉZČś†g˙7ŠgërCËZxďŤB çíb’Ú{»—îřU¨iŞľż{ţó'ŕ ż®‚F1C¦Ş‚÷FřŮ‹ŕĽţüĎáîOŔáCp÷J¦%<ćík_űÚ×fëbş®ÓŐŐEGGŞŞ’Ęe¦ăTŮjřÁŕµWáW>ô€xŕÍMBĘŻ_ďď~xüăpËV±|o·p—u9 ŤÝ?{A,U·n…G>*ÍŻŔ«‹_ýqýukĹ,ŞşzÔÍŻŞR‹7¬÷>|X đ^/ĽűŽxé b…±¤E”ş‚˛­Uč‡]N8tP4„ăÇ…ÎuŐJ!37o™ů†‡EÇj_*ęÖÜ$Ę78_ü°íqÜĂÁ_üą08Ţp˝(((V5µµbôă ­ iŔgź3Żłg…1ĽŁCŚ5«aĹrxńgbƶe‹8żĄň9ظAÜóągĹ*퓟+ž=wÝ)V"µ5BŹ 7]ŹG<;Ó¶Łş:ńL?őIńÎ**&ľ˙`"‚ŞŞT:˝óÝ®%LLz#4űëĐó*‡ CřđPź&đż˙~ř¤Póęy±*¸÷^Řó|ôŁđËż,úăĎ_žxźů´0ü./¨BŁQ±Ş°ŮD;»ëNŃżóyˇď_ľ\¬ę˙ő_Ĺunş¤ŁGEŮv‹h«Ë– ĎCźZšĹýńÂj /V&§O‹•Bc|äĂb•üę«bđް^Ü/›#‡…á­·Ä56n«ś­7‹>˘Ş˘ť'˘űßĺĽá†‚ öUQ×Ţ7ZÎ|@Ř"++áŽŰ…řŁ?‚ӧ౏¦čW+–‹1㙟ń˘¶–uŠ•ž×;şÄ3ik+›Ý»á§Ďţ+şiÚAúűűq:ťTVV^ńöuEe›BAÝó‚h<żök˘1ş\ŁÇüĺ_ Ož˝{Ĺ ÷î»ĹLá3źŮ»ďŠFÓŢ.ËpX4žíŰG˝?>ţq1ČîŮ#Ľ˘«/á÷ż/Ěç>7jôµŮ„/{¶ [˛DüŁ^)©”pu …żwC¸ÇŞU⿪*Q¦TjÔŰcëV1đŽŚ†ÂęCž›6 AóĐCđĚ3Â{ëČŃ(3ńŮ /Ż”ęj1C9xP îżő[âŢr‹ś~?üúŻ‹çibP÷űĹwąśëÖ‰çc· ÁqóÍâłßů!@‹n™EoŹłgáď˙^zI8&  /»ľđ…‹o“,lLS –źý¬p5?rD Öé´<ňpÉu‡Ý¶M¨nŞ«EŰܲEôU•Ń}÷ /˘“'E۵Kô×»ďířĺ—ĹŕZU˙ń?Š~Tܵ~˝i?ő”čttÉĄiŠöŤŠÁôo˙Vx.ý?˙ŹČ|™Éľtě8§ąYx<}÷»pë­˘¬ b2řĹ/ÂýŻÂˇä7SĹŰn¦č&ër‰IĺO~"úŐľ}âŮÜr‹č/ý¨0n˙Ţď‰~řío‹~–Ď !ér !÷±Ź‰çúłź ŻËM›Dß>vL<§|^x}mÚ$„Ý.žKŃ}ÚfîĹ÷Ü#ęĐÚ*ężbĹ|·ś©ą˘ÄDĽ(·[<@ŻwĽ×N8,ţ«Ş/÷Ţ{Ĺ, €_ú%1+˙ŔÄöůÄŔŰŇ">+˛z5ü˙źp,Î,@ Ş##BŘÜt“¸w6+rU•xůGŹŠFţ ®«(Łi8,ěĎ> 7 A÷ă‹Y×N Âď˙>|ůËâĄkš UUŤßŕ÷Ŕ˘sŻíp†ŐĐ ±Âp8ÄJŢéĽř˝·m+ Źg|XźÚZ!8,1«ć±BřĚgD+ ¬5kDĎç…0,ŞgÇ ¶Ź\L¬<ˇíE=Š÷)2V°Ůíâ9głB(,]*&Ä VőEçšööÉëµĐ…ĚŐFBeâf% hÍšé]Ęá(oăš® Ĺîݢ1Éĺ„ČĺDøőVř›ż/zě ¨HQČđÝwŹ~çőŠÎřąĎŤî÷záß˙{ŃY¦jôŇ×ögB%µ|ůhŠ!Fľđ1kř/˙EtŠ±Â¶×ČbK}EťíL—öv±ü·ŰĄ7îŐ‚i 5ńÁbezĄU#uuău÷Ş*ě&ó7Bu6¶íNFQĺZ]-ŃT©¦‰>ýW%6>Ul0Ú—@L†B!q˙±}˝şZh04mrŰ~UT™ßpCůő!Ô~é—Fă‡BU,!1 ŘľůŘlăg%š&V«V ˝eńóuë.~ E™8ÓËdôtŤ^Ea·s§Păµ·‹‹ Ú»ďŠYVK‹¨Ăd‚ipPětíďő;ś.n·hđ0=!(Y¸(ę¨AöÖ[ç§ 6ŰÔ}íBęęÄjˇśN18Odůr1Ń›lĐok»rő€ń¶ĂŞ*ˇVďí]üjá+.@r9áŘÚ6·‘¦ őĐĘ•bV2ÖöPNc&™LŇ6ť–u™eýŁ?ť»Ř **„qnýz1ë™j¶ÓĐ fl»vŤjŮl–“'O˛fÍ”iLw.6›OČçó´´´ĚwQ.‹t:ÍŮłgY]Tv_aô¬Ťwv*ÔŐŮülŇÓÓĹbˇa¦ËÝYŔbő‚śL-[DQ..`‰===¬Ľś™W™(ŠŘßVtXĚ\V4^Ó4éíí%PáŞ(Łn}™´ĘOź¶óđĂÂKbl´ÍąŔfłvuµŚĹbŚŚŚĚYYťN1CĽĐ^Q_/f+—ľŠ"Î˙íßčN×uúúúć¦Wx9‡ ]IâsąÜś˝“HHĺĺ]Ćg>Ąń+ż2űaĂCˇá˘1`q8Äl~&} “ÉĐ?Ó„3@UçVx ŃßßĎ,î.sŇÝÝÍ_üĹ_ŕőzůęWżJ `á…btu㡇4Ţ|Ëä;˙Ë 0|Ś“'…ţŐĺZřzuUU9ţ<ŃhźĎ‡ľH˛˝ăó#dčííĺرcÓZLUQé ‰P&9ÜݪŞŇŐŐE>źÇáp,šwr!Š˘H$JďäĘŢK„Ňé>Ł`&ű÷‹źú”¨\n?Ô4ŤsçÎa±XPUăJ¤˝śE!‰ĐÓÓĂńăÇg}ťKTUĄŻŻoÜX,Ć_ţĺ_’H$říßţíY]-^–IĄRrąş®ŁëLs ŮlślÖ —…{ďŹ}Ě ©IHÝĹ’B˛¦¦†ęęjŇ“eYZD(ŠÂĆŤÉĎÁňŻÎY‰˘@&[FÖŻPlř‹ýťX,ÖŻ_On¶r OAc#üÚ—ŕßý{1ëěúřŮ臹\®¤NĚ”“émăp8X»v-ŮĹ2@MA{{;Ťctyů|žÁÁAt]źőľsY±°R©Ż˝ö>źŹ­[· ýýŁˇŹŁQŃP‹®k‰D"™[t]ç­·Ţ"“ɰ}űv쳸xV)$q‰55µ¨3UH.b±V«•L&C6›Ą¦¦ćŠŞ€®d=’É$>źŹp8LMM ÖKĄu\€¦Éđđ0v»Ó4Ńuťęęęů.ÖŚ‰F٦I*•˘¦¦ËbÉ 4†l6Ëđđ0N§Ó4Q…ޱ ;‘H„l6‹Óé$ŹS[»ŘČd2ŚŚŚ`łŮP‹ĹBĹô™ŐVkš&˙đßćĚ™3|á _`ýúőWúy]ňů<ßüć7Y»v-dxx/}éK,)Ć:Y$¦ÉóĎ?ĎáÇ1M“x<Î}÷ÝÇ}÷Ý7ßE›6ńxś§žzŠţţ~t]'źĎó›żů›Ô××ĎwѦ͹sçřo˙íżáv»1 Ź|ä#lßľ}ľ‹5mNž<É׿ţunşé&Îź?Źiš|ůË_ž“LłI&“áĎ˙üĎ©©©!•JŃŰŰ˧>ő)n(Ć"ZDäóy^{í5Ž9‚aX­Vľň•Żŕ™l7ń,0«Ä0 ş»» D.–||`±Xp:ťÎź?O,#>6iů"AQ\.~żźĂ‹4ů˛ŐjĄµµ•}űö‘H$°Ůl¤R©ů.Ö´1 gź}–ˇˇ!Ľ^oÉ‹q±QSSĂ?řA~ţóź“JĄp8‹Ň>•Ëĺ&‘H000€˘(Áů.ÖŚp:ťA6mÚÄsĎ=‡Őj˝˘vťY š¦ńČ#ŹĐÓÓĂšén1_@d2q8<üđĂ$ Z‹A¤¦i˘Ş*«WŻć–[nˇ««‹›i\’b]}ôŃŇ d>÷ĚUUůÜç>ÇÚµkI&“Äb1nĽńĆů.ÖŚđx÷ůß2qŰźfůgŢSţţ˛?_÷ö%ýo‹÷smýj·R€pž8{Ç’ď:űy=oö™kV˙ňJ¸úç~߆jö%ýď˙>S€Đ¸źBz|ßg{¤W&oŇôý×óĂ[y|÷PV• đçnaú×Îď2NŹŮÁ?‹/)ăßs>ŢÎňý™Óűß§a0ď[?żc–”Uňđ6i°ďx¤˙őö¸ř·T{éçĽ}ďwĹVîGţ~!ůwçźůÖĐďw_“SMď&8P€oßŕű©Ľ¤mţĽřş/–´ßRľ×R^€ŻŻâC&źšß”ôsÓ˙úýO8rđŻüŁÁfřWźÍľĽ´ćßűůć}öÇߕݫ|űüĺ˙P€0€·{eÓăŻZ›Ęň#Ě? đýľŰ†?ľř÷ýśyKţyÎů'§g}żÎłŔ0‚·' ŇGď~>%ą+–<Mž·Hď§˝ýô3ç×ýq˛÷żďýŕôýřŹśŢÎňx˙Ě·{€ń‡ňř˝č„ŔŞź(i‚că†Ď+˙˘ÎRĺÖ*@hÜe˝·_ŮÝłşgůűÔáOp KĂR€Ŕ° 0, KĂR€Ŕ° 0, KĂR€Ŕ° 0, KĂR€Ŕ° 0, KĂR€Ŕ° 0, KĂR€Ŕ° 0, KĂR€Ŕ° 0, KĂR€Ŕ° 0, K˛×˙žîţ2 śdŻ˙ýßH {)@şˇŮKŇ Č^ n(@öÚ^€ž.!8Č^; Đ}EbS€ěĄé†d/H7 {)@şˇŮKŇ Č^ n(@öR€tC˛—¤ ˝ ÝP€ěĄé†d/H7 {)@şˇŮkwzK˘R€ěµ»Ý$*Č^ n(@öR€tC˛—¤ ˝¶?µˇ N˛×[­­Ö $¸Ă}őx|ýű•ß OH+ŢjmµÜ Áí«ŻÎ{vŕ÷‡ÂŇŚčŐ€Äs°Ż p\ Đý@â9ÖWĎÚű÷Ďχź«^Ş}‰ś äţ¤yE=ős×/-Áďëę}Ťś§¤Ž Ý8X€7NÂTö,°lWüô¤ đ215P€î1r>/„S[čŢ 'Q€cj¬Ýä pL &8*“•„IŽŞőô´U(Ŕ15_€îR“„IŽęXĽx*@zˇÇt°óBS€´LŽI¤GĄaR€ŁR€0)ŔQ)@ਠL pTŻŮůó Î(Ŕ1•’¤ pL &8Şł pďĎŚ,ţ]ŮĺüZ8LŽ#ýŽßé¸óľââß•]ÎŻ…Ăŕ8ţ¨ČŘŕ8 Ě(Ŕq(@Q€ăP€0ŁÇˇaFŽCÂŚ‡„83 p fŕ8N,Ŕě]e MP€ă8±— J›‡„83 p fŕ8˘ŕúň+@. Ç®W˙vČŕ8ú(Ŕ·÷šŢü•Ăźŕ8:)ŔC_9üIŽŁ•Ěďă)@N˘ÇŃLfźŻ9‰‡„83 p fŕ8 Ě(Ŕq(@Q€ăP€0ŁÇˇaFŽCÂŚ‡„8Žk đíŮ6`úÖX Ë(Ŕq\T€{ď%nřÚ 'Q€ăP€0ŁÇˇaFŽCÂŚ‡„83 p fŕ8 Ě(Ŕq(@Q€ăP€0ŁÇˇaFŽCÂŚ‡„83 p! 0Ű« _›ä$ p! p˝Öľ6ČIŕ8 Ě(Ŕq(@Q€ăP€0ŁÇŃ|ćżmäymňÄ ě˘ÇŃ|n8ě˘Ç±~?jÇ ˝P€ăXŻ‘vý—]–`zćü ľrČa pE¸·ÖŽ}f~|ĂW®9ěX_=ţóĚ'ä‹5˛áIH—őŐłő¦Wí˝ţ«ä„\aK)üł 6î«řř.?Ř€MĄ´poPŇĄ˛‡Ŕßw“ÇŔŹoŐľD*ŮQJéźÚňô‡¤-Ĺ=őűř÷ńűhxr0°¸ĄĐ mŞR€7AÂLé“ ° fJľ8űPrB.𡔲$oúS –y!ô8öVŮî?UţtI~|ýkHŻ­=\Ś@ŽăôÜy~Číŕ8(ŔĄ7ĽR€śCŽŁÜű™éµµ‡‹(Ŕq(@Q€ăP€0ŁÇˇaFŽCÂŚ‡„83 p fŕ8 Ě(Ŕq(@Q€ăP€0ŁÇˇaFŽCÂŚ‡„83 p; *ůíŔ Ž)ŔqśXS 6)Ŕq(@Q€ăP€0ŁÇˇaFŽŁÉ\˙U›éyN7:¦ÇŃdn?ĎIŁF×ŕ8 Ě(Ŕq(@Q€ăP€Ć'yů7P€ăP€›Ć§ř<4DŽCnźâóĐ8¸i|ŠĎCCŕ8ŕ¦ń)> Q€ăP€›Ć§ř<4DŽCnźâóĐ8¸i|ŠĎCCŕ8ŕ¦ń)> Q€ăP€›Ć§ř<4DŽCnźâóĐ8‚o*Őn¦·«|”ŕxŕn®©kÎ\k”ŠĎCCŕŐ”$8‚@5Ą‰DŽ PM)@"Q€#TS HŕŐ”$8‚@5Ą‰DŽ PM)@"Q€#TS HŕŐ”$8‚@5Ą‰DŽŕ`Ąű bH;ŕN)1HűŕB”ž$8‚Ą _ę×GiĂyč†AŇ‹P€«ź©¤G˘ô ń(Ŕ„(=H< p!JOŹAŇS€ÄŁG˘ô ń(Ŕ„(˝› 0}éKţ™ë×Ň=8‚ĄwWćăBé)Ŕ)Ŕ„(˝8¸Pz p@ p!JOŹAŇS€ÄŁG˘ô ń(Ŕ„(˝4Ďx§ÁőŰžŹĂҵăLsŕB”ŢŮą~ŰóqXşöđ(Ó 8‚uv®ßö|–®=<Ę4HŽ DAťťë·=‡ĄkŹ2 R€};ń÷zDËĄČ+nˇôŕ€`ßBTÓ5™ßö´üóŃHŹd—„ě[jş&×oűú‘ě2P€} QM×äúm_?’]f °o!Şéš\żíëG˛Ë âX_=ţ“}(9!' QM×äúm_?’]f‡úę«ďţű÷ýCÉ 9Kjş&×oűú‘ě28ÜW ° !Şéš\żíëG˛Ë ˘ä!đżň{|H®zĽ=(ćF!Şéš\żíëG˛Ët®¸§ŢŰĎ=Ŕ BTÓ5ą~Ű׏d—„‡Ŕ} QM×äúm_?’˝dšAx¤o!Şéš\żí[Ž,ś‡ŽyLßBTÓ5ą~Ű·Y8óBčľ…¨¦krý¶o9˛p:¦ű˘š®ÉőŰľĺČÂyčě[jş&×oű–# çˇc °o!Şéš\żí[Ž,ś‡Ž)Ŕľ…¨¦krý¶o9˛p:¦ű˘š®Éçí}Ęoű–#Éqˇű˘š®Éôöć·}Ë‘ä8P€} QM×dz{óŰľĺHrśA(Ŕľ…¨¦k2˝˝ůmßr$9Î `ßBTÓ5™ŢŢü¶o9’g °o!ŞéšÜţ;ŕ–Ž$Ç„ě[jş>óŰľĺHrśA(Ŕľ…¨Łë3żí[Ž$Ç„ě[:ş>óŰľĺHrśA(Ŕľ…¨Łë3żí[Ž$Ç„ě[:ş>óŰľĺHrśA(Ŕľ…¨Łë3żíésÄKź“g °o!ęčúÜrŰ—>§âčžě[:ş>·ÜöĄĎ©8ú„§ű˘Ž®Ď-·}és*Ž>á)Ŕľ…¨ŁësËm_úśŠŁOx °o!ęčúÜrŰ—>§âčžě[:ş>·ÜöĄĎ©8ú„§ű˘Ž®Ď-·}és*Ž>á)Ŕľ…¨ŁësËm_úśŠŁOx °o!ęčúÜrŰ—>§âčžě[:ş>óźűČsi|*ϡ)Ŕľ…¨Łą4>UÇźŕ`ßBTMĚ\źŞăOp °o!Ş&f.ŤOŐń'8Ř·U3—Ƨęřśě[މ™KăSuü Nö-DŐÄĚĄń©:ţ§ű˘jbćŇřT‚S€} Q51si|ŞŽ?Á)Ŕľ…¨šą4>UÇźŕ`ßBTMĚ\úi‘ęs@` °o!ަ­¬<„¦ű˘RÚĘĘ3@h °o!*Ą­¬<„¦ű˘RÚĘĘ3@h °ŻoéçÇ#TJ[Y:´Döai󆨔¶˛p&hŠěT€ ű  (Ŕ>(@Č ° Pr€ěT€ ű Ď.ŔĄŃ4Řxz®^KŁ``µ\IůÎy!8Řxöý@Ř%ب9@öA*@P€}P€ ` äب9@öA*@P€}P€ `ňíąřkĺ–Ě^­»¤űđGF¨‘Ösu„éŔ§ľzě­4x ¨9ŕC_=˙ýł«Óŕ- ä€OřőŹŚO*@řX€{+Ŕ[(@Č=®xBΡ x¤Żí™Ľx#D´žů—MŃxLňm˘@ZĎ|„‹f‰p6ô•{€ x۶^]+ó.ž)Bůü,°ď¶ D]ô—ůÎÁl(@Ď7 D]ô—ůÎÁ¬öŐăĄÚ 9Kşč/ó.ś'‚ŮR€őNČYBÔE™Źpá<Ě–‡Ŕ5OČ9BÔE™Źpá<Ś—Áô!D]ô—ůÎÁ||!ôź? ü|`üýůía˛ĽEşč/ó.ś'‚9ôŁpĎÖűľâő_ŰNČ9BÔE™Źpá<̡7Cř)ŔÇwů)Ŕ›…¨‹ţ2áÂy"Ăďóşřü<~yí ĄBÔE™Źpá<ÄĆžZúśźŻösđf!ꢿĚG¸pžćó“ ÷_rÉCŕBÔE™Źpá<̡ľzöC˘.úË|„ ç‰`ý,đěő/^sżuŃ_ć#\8Oóů—"y7„¨‹ţ2áÂy"cß,8!•y÷?ČaűjďëYŕĹBEŻ™ŹsŮdÍZ_zIźĽX˘č5óq.›,˘Q€­ Q˝f>Îe“E4Ţ ¦u!Š˘×ĚÇąl˛ĆŻĹl]˘č5óq.›,˘ń2Ö…(Š^3ç˛É"ż©u!Š˘×ĚÇąl˛F¶.DQôšů8—MŃ|7Ă'¤¶EŃkćă\6YDă'AZ˘(zÍ|śË&‹hĽ ¦u!Š˘×ĚÇąl˛fË=@# Q˝f>Îe“E4ľŘşEŃkćă\6YDsřw‚ţ(\ĺR[Z'Ó1/ž;n·ĺÍjžÚBÔÂ8™ŽyńÜq;Řşµ0N¦c^?ŢůRx±U0fV=nöńYŕ‡ß [*3+Ě7S€­ Qcf…Ůăfžn]*3źă˙T:ŤÜĂ/Ej]*3Óń/śEnâe0­ Qcf:ţ…łČMÜl]*3Óń/śEnâ{€­ Qcf:ţ…łČM¶Ľ‚Ś,DŚ™éřÎ"7Q€­ Qcf:ţ…łČMÖúęń­Ö 9A*3Óń/śEnr¬_ß?|>!'Qcf:ţ…łČMőŐWß˝~Hř÷CÉ 9.DŚ™éřÎ"7Ůň2ěs` !Ş`ĚLÇżpąÉá—Á<›ďńűáíOě}ő ‡…¨‚13˙ÂYäb{jéĎ»}i ~_SőËäŁU0f¦ă_8‹Üdâ.ő8‚U0f¦ă_8‹ÜäĐCŕ×A*3Óń/śEnr°ߏ˝ ćn!Ş`ĚLÇżpą‰wi]*3ý‚¤öy3„Ö…¨Y<ŹÜB¶.Äö—ĹóČ-¶ô•Ś,Äö—ĹóČ-`ëBlY<ŹÜÂCŕÖ…Řţ˛xąĹ–¬yBj ±ýeńłt2ą‡lWŤ/źY:™ÜC¶+ÄĆ—Ď,ťLîˇŰbăËg–N&÷P€í ±ńĺ3K'“{(Ŕv…Řřň™Ą“É=`»Bl|ůĚŇÉä °]!6ľ|fédrŘ®_>łt2ą‡lWŤ/źY:™ÜC¶+ÄĆ—Ď,ťLîˇŰbăËg–N&÷P€í ±ńĺ3K'“{(Ŕv…Řřň™Ą“É=`»Bl|ůĚŇÉä °]!6ľ|fédrŘ®_>łt2ą‡lWŤ/źY:™ÜC¶+ÄĆ—Ď,ťLîˇŰbăËg–N&÷P€í ±ńĺ3K'“{(Ŕv…Řřň™Ą“É=`»Bl|ůĚŇÉä °E˙{аńĺ3źŠg–‹)Ŕ…Řň2ϲiĺz °-îű…ÎÂŮĺr °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °-!¶ą\ʢąĺ °~R™ÎTÁ\sŘŠ\®g:SG'š+)ŔřÜ÷k&źó•\&8_­-·d:_g›K)ŔřBlmą%Óů:8Ű\JĆbkË-™Î×ÁŮćR 0ľ[[nÉtľÎ6—:ŘWŹÇżřňüXzB–…ŘÚrK¦óup¶ąÔ±ľzĽ đő?ź!¶¶Ü’é|śm.u¨ŻÉ=@xş[[nÉtľÎ6—*{üďß˙üŕ·J_ S­-·d:_g›KöÔĎź{µź{€' ±µĺ–Lçëŕls©*č!đ©Blmą%Óů:8Ű\ŞÎC`x¦[[nÉtľÎ6—:ö$Čűë_Ľ ć\!¶¶Ü’é|śm.ĺ…Đń…ŘÚrK¦óup¶ą”Ś/ÄÖ–[2ťŻłÍĄ`|!¶¶Ü’é|śm.Ąă ±µĺ–LßąŃűB·@ĆbkËcytŇą†Ś/ÄF–Çňč¤s _Ť,ŹĺŃIç 0ľYËŁ“Î5`|!6˛<–G'ťk(ŔřBldy,ŹN:×P€ń…ŘČňXťt®ˇă ±‘ĺ±<:é\CĆb#ËcytŇą†Ś/ÄF–Çňč¤s _Ť,ŹĺŃIç 0ľYË|6ýŚp$ 0ľYËĄŮÜ:ůśKĆb#Ëcą4›['źs)ŔČŢŢ^I¶Ů^ŠŚ,Ä–ĺ™ĎéžeŔy`d!6Ż,Ď|N÷,ÎŁ# ±yeyćsşgpYÍ+Ë3źÓ=Ë€ó(ŔČBl^YžÉS! 0YÍ+ke:§»×§P€‘…ض˛V¦sş{-p Ym+ke:§»×§P€‘…ض˛V¦sş{-p Ym+ke:§»×§P€‘…ض˛V¦sş{-p Ym+ke:§»×§P€‘…ض˛V¦sş{-p Ym+ke:§»×§P€‘…ض˛V¦sş{-p Ym+ke:§»×§P€‘…ض˛V¦sş{-p Ym+ke:§»×§P€‘…ض˛V¦sş{-p Ym+ke:§»×§P€‘…ض˛V¦sş{-p Ym+ke:§»×§P€1ů…ćsf“ËÜNĆbĂĘş™ÎěŢÁ9`L!6¬¬›é˝ú#‹‚ú`L!6¬R€u)@y[.¬:V(Ŕş T€ Q€u)@©˘ëR€R6DÖĄĄl¬KJŘX×K1{)ĎČlň‘¬ëŹĄ¸tDĘş™­@>R€u˝–_ţ&–~÷‡<;Ó¸´@y§ë ±ä™®@o‰°Ť¬+ÄFcfľ˙XˇĽQ€u…ŘrĚĚWŕ+”7 °®AŽ™ů üc…ňFÖb#Č13_¬PŢ(ŔşBl9fć+đŹĘXWŤ ÇĚ|ţ±ByŁkńJ?©›Łk ±äČ™ŻĂ|™ňNÖb Č‘3_‡ů2坬%Ä#gľóUę'DŢ)ŔZBl9rćëpi•fLJĄËyúC†Č|¦ëóy99Τk±řĄĚ3]źŮe&XCĄ.ežéúĚ.3)ŔB,u)óL×gv™IÖb©K™gş>łËL pżü…!–ş”y¦ë3»Ě¤÷Ë—PĄ.ežŮŠU€3 p/(›ÉlĹ*Ŕ¸×Űrň @9ó›\fR€űý±ś¤Ś™K+vâEîĄe3ą´b'^ŕ^ P6“K+vâEîĄe3ą´b'^ŕ^ P6“K+vâEîĄe3ą´b'^ŕ^ P6“éË´˛Őˤ÷S€˛ÉĚV/“ÜOĘ&3[˝L p?(›Ělő2)Ŕý l2łŐˤ÷{-!?,ŰĘtőNĽ(Ŕ˝B,f)÷fşz'^ŕ^!ł”{3]˝/%}őřçů±Ę ›b1Ką7ÓŐ;ńRV€?~PJ1ÓŐ;ńR|PJŮB¦«÷y9űí6*˝řßż˙ţů-Ŕoľ¸B,f)÷fşzłËĂ©ŐSŻösPĘŘ™®Ţěň°Ş ‡ŔRFĎtőf—‡Uç!°”2x¦«7»<¬˘ľz=„ö2)ĂgşzłËĂňBč˝B,f)÷fşzłËĂR€{…XĚRîÍtőf—‡Ą÷ ±ĄÜ›éęÍ.Kîb1Ką7ÓŐ›]–Ü+Äb–ro¦«7»<,¸WĹ,ĺŢLWovyX pŻ‹YĘ˝™®Ţěň°ŕşüĆC,f)÷fşzÓ÷3ű-ŕşü˙’!ł”{s}őNR€ë ě$×Wď4(¸îmńřEH˛Ý\_˝Ó ŕş·Ś°ŚĄ<#§A)Ŕ%o˙Ç|‰°PĄ<#SÓ@ŕ’ü˙Ź!Ş”gç4¸DĘAs\˘ĺ 9 D.Q€rĐś˘—(@9hNQ€K 4§(Ŕ% PšÓ@ŕ(Íi p‰”ć4¸ämIř)`9NNQ€©ôGB,E)ŻĎi 0•.€KQĘësLĄ ÄR”ňúś˘S鱥Ľ>§(ŔTşB,E)ŻĎi 0•.€KQĘësLĄ ÄR”ňúś˘S鱥Ľ>§(ŔTşB,E)ŻĎi 0•.€KQĘësLĄ ÄR”ňúś˘S鱥Ľ>§(ŔTşB,E)ŻĎi 0•.€KQĘësLĄ ÄR”ňúś˘S鱥Ľ>§(ŔTşB,E)ŻĎi 0őZŢ˙YŽśÓ—˙Ą¦n)ŔTĺ'ĺ˝9}ÉŹtIz|)ß24ukä|›čç‘ËOĘh9ukč̦8Äb“2ZNÝR€é‡XlRFËéK—O‹(Ŕ·)Ž°Ř¤Ś–Ó—üHŕâK)źŮďÓ" 0ťĐ‹MĘř9uB¦biI?§N(ŔWúé)·çÔ (ĄÜťS' ”rwNťP€RĘÝŮË« ”ň`NÍł=ĺ!e…śš7hFX,Kjé<ŰĎ/ĄŚÓ—˝ź?­˙©őĎ™ć~Ž×)Ŕä1đ 5 đ1Ő8@sŢ DăŞóH€<'ĽÍ÷ëĐŤ×'ĎńyŤ“áZő3F–Ö'ď ŞÚXyUŕFŻA2^ź|/Đ×]®żc5YZëŢT˝±2ęýü_Úx­zL p«ź±˛´>:±ÝńŢâwäŤ×şď|®Źf˙ł0V«~©ŢXyk„~Gźeß÷j ×g?cc¬>úţ>iݱrż{Źé¶đx»÷4V+ľź[ó=Ŕ{ŘŃ[)ŔíŚŐVďĎU+Ď˝oäu›Ě^Óa¸V«íŚp@ň“Hë?•ôÇ•~Š hZZbk…öçuh™Ć4ű->ßůÇ»˙|Âű{xh@ Qó7e[y[Ďßn|»NŤú)Ŕź7g{}|(Ŕ·¦hQrđçż—Ţ«7˝nrhÝźżřX*Ŕü13@kŢžI߬üʧ8ŇgFŢ2tHÁcňëjNń˙čűJł<IEND®B`‚libtorrent-rasterbar-1.1.13/docs/delays_thumb.png000066400000000000000000000252721351156116000220710ustar00rootroot00000000000000‰PNG  IHDRúņ pHYsHHFÉk> vpAgú@©É)őIDATxÚíÝypW~đďëžűŇŚftŤ !qĽFŕµŰë5v’ĘnśMĄ¶˛•8•­rŐVíÖn*˙çŻüťŞ¤’ޤöÇö®mŚíx±1ŕ5lc0Cdt!Ťf4§ć>úČÝH !„¤žă÷)— Ó-ÍŻ‡Ńw^ż~ý“e„R8­ „ĺzŔ˘¶!D[ş%¶I’tôčŃńńqžç·nÝ:<<ěóůz{{•M”_důcô†Y˝2÷Ăqcěž— ¬cÇŽý×ý×ĆŤëëë/]ş”Íf{{{3™Ě‰'L&“ÖGTZc‰DB’$§ÓIoÁůcˇPČn·›L&zeć“eyzzş®®Žă¨sfc,—Ë555íŢ˝űžMK–×ë}îąçśN§ŇžĘd2>ź@.—łŮlĎ=÷ś$IZZ áyţúőëĹbqçÎť˘(j]N á8îĚ™3ŤŤŤôž™OĹĎ>űěŮgźŐëőZ×RBcŃhôúőë 7-X;wîěěě!—Ë9ŽT*e±X”M<ĎëtK}ou2ŤŚ1žçyž×ş–Ňb4 ˝gî!Š˘ŃhÔëőAëZJ‹^Ż_x>Ą €rŢgłŮÔÖÖj}Ą®µµ•Z‹Úľ}»ŮlÖşŠő–ÍfŹ=jµZźţyŽă&''‡Ýn—eYůmä8n×®]ł9~űöm·Ű=33ŤF;:: (ŠŁŁŁŤŤŤccc555ÇŤĆH$Ňßß˙˝ď}/•Jy<žP(äńxÂá°ËĺĘd2:ťŽă¸\.×ŃŃÁóü… <O{{űąsçşşşjjj”'Ş­­µZ­J%óżÎŻ˙Â… űöíkjjşçĐĚfs{{»ň×Ůď•$ittÔëő2Ć8ŽˇŁŁă8LL Äb1¤â8tvbÉ÷ }â­¦*üť\&‡Ăˇu `ŚY,–T*őŃGq÷é§ź>ýôÓfł9?˙üóÍÍÍ~ż˙äÉ“fłŮb±‚pěر^xˇŻŻotttÇŽíííz˝ţ“O>ůł?űłĂ‡×ÔÔÔÔÔtuu ;ťÎăÇŹ_Ľxńé§źž`±X¦¦¦ …cLĹBˇđĎ˙üĎ>źďüůó@ŕĺ—_>wîśŰí>věÇă9räČl6ŰÄÄD]]],!źĎ÷ôôřýţ™™™P(dłŮ¶mŰf±X”ţµ‹/F"‘\.—L&»şşnŢĽŮÜÜ|öěYĆÓéŚĹbO>ůäĆŤŁŃčżýŰżýÓ?ýÓéÓ§ëęęŔŤ7~ő«_555áĚ„B°X°h?&c…`·Ăç[â%ĄŔ"d­L¦ŽŤŤýÇü‡,Ë555©Tęüůó˘(ú|ľ@  Ż\ąŤF].cĚĺr%‰X,&ËňđđđÉ“'öłźét:%)ěv»ÝnŹÇ㌱–––ŃŃQ˝^/˲Çă ŮlvçÎť/^,‹ÉdRŻ×‡Bˇd2©ôNś>}zbbbbbbdddff¦®®.Źź;w.—ËełYŽăĚfsww7ÇqÇŹQšrśíöľuëÖ… ŘívťN …R©ÔŘŘ,ËŃhÔl6ďÚµ €ĹbŃëőńxĽµµŐétŽŚŚtvvş\.Đé Š(ďűz1†őĄP`˛¶š››˙úŻ˙:źĎ·´´LMME"‘´··żűî»´Űíőőőńx\ĹĆĆĆp8Ľk×®h4jłŮt:]>ź·X,---mmm6›-‘HpçrąÜn÷–-[¶lŮâőz§¦¦ľ˙ýďG"‘ćććÍ›7ëőzžç•~篿ţzßľ}˘(ćrąp8\WW·oß>‹Ĺb·ŰÁŕO<ŹÇŔçźľ˙ţîîîÚÚÚ—_~ąX,ş\.Žă”óGĹ<ŹÓéĚç󇣹ą™ă¸'ź|RŻ×ńĹCCCĘéEˇPxá…Ěfóčč¨Á`čîî¶Z­ę5Đţ‰ÄRŻĎŁľ~és%c@˘ŃčŐ«W÷îÝ«ő;ň“N§M&Sé\–)‹‰D˘¦¦fĹ—Drą\:ťv:ť«xP÷ ja˛®¬V«Ö%ÜEŻ×»ÝîGů &“iÝFeŇp5BHŮ Ŕ"„” ,BHŮ Ŕ"„” ,BHŮ Ŕ"„” ,BHŮ Ŕ"„” 8Z­2ś<‰\{ö µUëjYjaU«d7obr·o?`ĎT ± ­+&„ZXŐĚl†Ń¸ÔüD˛YĽöŇiěŢŤ}ű´®T;jaU1YĆo}Ep‘Ďk].!X„ňAE–'źG6 Z\hŠ‹,ĎŮłřĎ˙D_źÖu޶¬NwI’8ŽSľj]0YU˛Ś0†ĆFŘíp:ď;Gmm-śNdłZWLŞÚR%Šâ{ď˝—Ëĺrą\CCC0ôů|ű÷ďW6‹E´žšö$ ’žŔőľűą}““ŘlřۿłőQîrĺ ŠEěاSëĂ&•I’$%^ť y©Ŕú裏ţ÷˙—çůşşşP(ärąĆĆĆöďßĎÁjµîŮł‡–TÓ’$áÝw1=ŤÎN<˙üJ~Bm-t:p$ ‚pßÝ,#Âąsp»X˛¬Ý2V¤¤*MMM]»v-‘H,,KÖćÍ›_xá…P(Ä۲eK<ďěě ËňćÍ›÷îÝ+ËréLM]Ą$ ń8ěvÄb+ü ˛ IÂrNöCM ĚćeĐÔŢ}˛Śý.ĆÇa2aÆ.‹BŞś×ëmjjRÖ^\¸u©Ŕęęęňů|’$)KwdłŮŮ™›9ŽŁţ¬RÁqĐéÖ©Ł ÝZÎÂ%™ŚÚpËĺpů2ΞÇáç?§sI˛4Ʋ|úJV~Vş¨ŚF#»Ý®ő±Őóđ«%=ĆŔqs1ÚŘLF}RIÂŃŁŃىŢ^­_RNčÖśę“Ďă˝÷ "—C]Ý:=©$ÍE¤$ab:&&´~-H™ˇÓşęS,"Ýľ¬~«5Âó0™´,€”'zÇTĆpăŢ{7o.kž‡^OďH١SÂň45…ÉI8hoW)ŔdBgçę<…,ăŹÄÄÂaŘlZ0!VąúúkÜşť?˙ąúѸĘgy˘ďľĂJ—/'d-Đ)ayâ845ÁbYŰ‹}:,ęi"Ą>?ËÖüën«B0: ĆŕőRH‘ŇDďKrG.‡Ó§ńűß#™ÔşBG-,rĎŁĄ™ $iąßrů2®]ŰŤçź_î=7ą’I 0›µ>`R~(°Č<{š92˘.Q±wď\` čďÇ­[(a4޵?cřđCŚŹ#ĆÎťZ-)?Xäp¬V¤Ów=ŹăÄ Č2ŚĆ{K–!hnž»M‡‡AEÍÂÜ‘e80Ji]©4Xd1Ĺ"Ň饦ÇZ(źG±“Iť9‹Pd P`‘Ăţ€‘„Bxüńe}‹$áĐ!„Ăčęž=Z©X4¬, ËČfŐ©Ë$I(ŃĐ@“ľ“5EEĂŘJnŤ¦»©ÉŁŔ"„” ,ňDĂâEUÉúŁŔ"1".]ÂáĂž¦[É:Ł«„e‹1 žF.«ő!ľqůwŢÜďyď˙Ŕ!ň‡X ożŤÉIlß>÷x:Ť?D.‡gžÁ¦MZĽŽ¤ś,ő ™JĄ@<źśśL$@ “Éh]0ą18|s˝ÝŃ(ü~är‹‹$áđúë©|ţ0+ŽĂä$Ţxcchp1á®[C&Łfîô´Ö/()Kµ°ľýöŰ'När9Aşşş†††6mÚôꫯ2Ć"‘Čđđ°^Ż÷z˝´4ˇfL&´µ!›ť‹Ź«W1>ްq#Îť^ʧźVŰ_ˇ" Ăí^ů"†‹Ňé`·C§[Ş‘%Ëŕ8L‹Ü#­×Ób«dV2™ ‡Ă±X,źĎ/ÜşT`544đ<ßÖÖf2™>űě3ŁŃh˝sę‘Ífc±ÉdjZzes˛>fŰÝnx<Čd08Č2şşÔŔ:zÓÓ‡áő®r:(M'ęĎ"«!źĎÇb±x<..vUg©Ŕ*‹;věhkk»}űö/~ń‹©©© 6eąĄĄe×®]ZYŚ(âÓOŐ)Içăy´¶ŢŐ[1Äăx˙}ŚŤaëÖ‡~jĆÍ"Ôn2RĹ<ŹÇă‰FŁWŻ^]¸u©ŔÚľ}űöíŰôôôh}dŮÇŁÎĹ~O6)]ăl^­x.SĆĐׇ‰ Č2MwEÖ5ă+‘I+;ďă8X,ĐëWřÔ55hiYů·˛$ ,BHŮ Ŕ"„” ¬Š&I™A:MłS‘Ę@UŃŇi|đ~ű[šü“Tş5§˘ét¨«C6 AĐ`d&cś?Źdň!¦Ö"äţ¨…EÖ’$ÁďG(´¶Ď‹ah~?ťůV‡‡1<ŚŰ·ńŘcw=ľ>w&’RµT`ÍĚĚĚĚ̸\®h4Z[[‹ĹśN§ĂáĐşćj‹AŞeb©Ë—11D‚N÷Č|KÖŐ«WŹ=Ęó|*•ây^Ĺ 6üň—żdŚMOO߼ySŻ×?öŘc´ňóúŃë+dŚűń<<$ÔžŞ6333@ ŹçęĽÔ»ż®®Îd2 šÍćxüˇ6lĐş\˛:”l) ‹fË>®wíÚőŇK/ůýţććfżßßŢŢ@–妦¦îîn­ŤęŐŞ4µµµµµµ+Yůą«««««köŻ´6=Y[‚€@‡¦¦ĺ~ c0›iěhő¨ŽRúx7oâĂÁ^y…Ć+EQ`­1YĆW_A§Ű I‚(j])QX¤Č2r9đů}}Ô¤" Q`‘#IČdĘ{~T˛f(°H‰á8Ří˛ŽYm4¬”žĄG6Č2®\NG ÓW! ¬’$čő´Čč˝”Q¦xµµZWCÖVé‘$Ľý6B!tvâŵ®¦ô0§uęÔřř¸ÖőĹLNâđaôőQ`=cqâNžÔş˛&ÔŔ:ţüńăÇoܸˇu=džT ÓÓ( ˨­E}=uĂ?ŐŠćfżV©Ô«„{öě©««kmmŐşrÇáúu„B°Űát˘P é –E–!IX•J ŤľýöŰ’$ɲĽyóf­K"Y†Ó ŻH§ˇÓQ`˘ ×ëkkkeYŢľ}űümáp8‹™Íćd2év»gffÜnw-ݱždv;jk‘Ji] !ÚÓ°Űí555—.]jllśÝöĹ_\Ľx1‰čőú¶¶¶‘‘‘Ť7ţć7żaŚMMMőőő źĎ§«’µ5DąHŐFŁ“““ńx<›Í.ÜŞfŤÓéĚd2¦»çôĐëő‡ăąçž>}ú´Á`HÝůśçyŢ`0čőzFÝŔ„ŐŁd‹Á`X4[ÔŔ2ŤmmmüÝ]•µµµ»wďnjjĐŰŰ;55ĄôĘ˲\__ßŮŮ©őˇU˘ë×qű6¦§iíuRťjjjjjj–ZůůěŮłďż˙~ooďümO=ő”ň%›¶mۦő±TsçN#“ˇ „,¤°sçÎP(ôĚ3Ďčőz­ë©zĚfÄbÔoEČB‹Ĺ"Ër___Š.E•eîja˛€:´Çápô÷÷ó4ÜŽT6I˘¦kYS;ÝS©Tccc:ťÖşBV(BďďÎó¸v gÎŔjĹË/ĂbŃşD˛j`ů|ľÖÖÖ;vh]!ŹŚăpĺ Âa<ů$<őAĆçŹ#ź§Ŕ*Sę)áĄK—&''űűűAĐş$B™Ĺ«±Ř]]ŚA§ŁŰ ËšXííííííccc‹Ž.%¤Ěp”0¨ÇŞ˛¨§„˝˝˝÷ Â"„RĂ(‹ZśRňt>˙üóX,ćóů6nÜčt:µ.‰BÇhnnľxńâkŻ˝666¦u=„r_€-[¶üŕ?0™Lt‰T”±1\»A Ű*ĆÜŔŃBˇ¤Ą†I%E8 “ ÖĄŐˇÖóĎ?żuëÖ6eY7B*Ë…ÚZĐÇpQËjµŇlî¤Ň(·‘“ ˘‹ŇZބҦÖW_}őůçźďY/—BJ‰X6›íرc4¬T ĆP(ŕČś>­u)äQ©}X333fł9“Éh]!k€1 ?jgf33°Ů@ËÖ•µ…ĺőz§¦¦\.—Öő˛6 ĚÎÎŽÁ[oá¨WľĽpA‡Ă^Ż7Źk]!kO™şµGU^tÎť;wâĉb±xĎśîß|óÍččhSS“ßďWš`6lčéé ˲ň•Ö%$ĺŠ˘Ş„É÷ů×Ńxę©§nÝşučĐ!ĎěôŚ€D"qíÚµcÇŽFŁN§łąąą§§‡1688X,­VëîÝ» 4ڞJ&''Ż_żžH$ ŽăňůĽ,Ë÷,:<O[[Űőë×›šš2™ĚěBŞ7n|ć™gĐĘ`¤ś0†b““H&a·k] YDCCŰíŽFŁ7nÜX¸UM¨úúz»Ý>===ŰÁóůĽÍfËd2555333‡C٤×ëďYמň03Ź>B$‚'žĐş˛ťN§ÓéL&Çq‹lUţçrąÚŰŰďi.ąÝnĺJN™Íf­Ź…Gf0 ­ Ĺ"őa•#5ĂŇéôč訝ɤĐ=†eK ¬p8ěóůŚFŁÖőBČ}©§„ÝÝݲ,S KK˛ Q„$a±SwBfëńÇüńǵ.¦şť9«W11ď}OëR)Qôa^2˘QŘí´"1!K Ŕ*ĘşÄtç!÷GE)X„˛AE)X„˛AE)X% źG8LËç­7ĆÍ"D:­u)dątŹţ#ČĘE"čëĂ͛lj€Ćî®'Ć02‚7ހχżüK­«!ËBĄ©ŃQ\¸€tíít;®l6´´@µ®,–¦Ëž§´Ň ˝ňe…ú°´FSť˛lX„˛AE)Ô‡ĄY†$ˇPĐşrGˇY†^Oó‘•2 ¬u'Ĺä$ľý33hlÔş ŞÇ ĽńňyüŕčîÖş r_X´lęęĹpř0ŠEX,đxh>™’ĎC–ałáI©Y*°††††††¬Vëőë×»»»‡‡‡}>_oo/I’$I’e™çy­ˇÜH H§)­Jct2X dY–eYĹE^*°Âáđůóç÷ěŮ Ż^˝šÍf{{{çŻüĽgĎZůůˇŃPBîĂď÷_»vm©•ź—ËĺjkkxžĎd2>ź€,ËĎ>ű,hĺgBČŞjjjŞŻŻŹFŁ×Ż__¸u©ŔÚ¶m›ĎçłX,›6mr8©TĘrgĆqžç)ŞH%` ÓÓ¸q…ŚFŚŤˇ¶mm e5ÂqÇqz˝~ŃóĄËáp(k>Űl6µµµZ !«Ť1ŚŤ!•‚ ŔnǵkÁŹ~„-[´®Ś,‚zIu“eÔÔ ąĘC}=<ęa,YX¤ş1¦âUH])e4p”ąr·oٱ==Z—Rí(°ÖQ±Ď?ÇÄR)¸ÝZWCĂŠEś:…ŃQ<ű,ěv¸t ™ ü~<ńŤŐŇ˝úë(źG?2™ąRšňy `l é´zˇËoX(°Ö—É»t{@‰łŮ Ëřŕüö·ęÍ:Ô«U(°Öuč–YĎŁą:î *X„˛AE)X„˛AĂÖ…,c`~?’IŘlZWC†2””ş±JÖşE|ö™ŚÖĄec Ń(~÷;LMaÇ­«!ť®“ ŤŤ ).Ę ÇÁn‡ŃH×vKµ°ÖR8Ś@6š›ĐxŃňĂq0ht{é ŔZK_|ˇ!0†żů ¸3›)o˛ŚdR]ŕž1 Ľž(°ÖchiA?~˙{D"  Ĺ*C,†×^C>ŹBf3~üclÚ¤uMŐ‚kŤIL&´µ!›Őş˛JŠEŤ°Ű‘NĂlF>ŻuAU„NÎ !eZX„,› ŕôiš HCX«'Á˙IÂľ}hhĐş˛r9ô÷Ă`  ľZYÖ)ˇ$Ił_É}Ĺbđű "Öş˛fh‚ M-ŐÂđů|gĎžőz˝Á`Đçóíßż€(ŠĹb´.á|ŚÁd˘1;Ž&Zc’$)ńňĐ+?'‰ľľľééésçÎĹb1—Ë566¶˙~ĆŘĐĐ ´ňó"čÝ\= ૯05…§ž‚Ő:·I Đë©-ö°¦¦¦V¸ňł$I‚ 8ťÎÖÖÖîîîx<ŢŮŮ @–ĺÍ›7ďÝ»W–ežţ=b É$&&ÔaIâq\ľŚÎÎąŔ’$Ľý6B!tvâŵ.±Ěx˝Ţ¦¦¦H$ŇßßżpëRµcÇŽ-[¶L¦—^zÉb±dłY“ɤlRVgŐúĐJĎăŇ%|ý5˘Qşi¶Â1‡ăŢ>xIB&·ɤÖő•ĆcŚçů‡^ůŮzçCÉ)»˛‚™OŃߏHccÇÁŘÜý7ŤŤt-©z1ŽŁIiV kx4ą>ýŇix<(ç6QZUĆN#Ů §S}P!Šŕ8"‚A¨Ż‡ †Á€ş:şDó°(°Ť,Ăj…ŐŠ@Fă]EŞ ÇáÔ)ô÷ĂjĹ+ݍŹ\ą‚pO>‰†Ľů&üä'qć ôzüă?˘¦FëşË ü#ŁëÜŐ€ă06†#GŤŢ÷DO’ĐŇA}?Č2,X­Ĺ I¨­…Ű­¶ąšš`±P|¨…ő¦¦09 ‡‹Ľq•uŹC&ĆF­k%«JN«łĘÜŹ2źň,ŽN,řT»g7˛lÔÂZ†bÓÓęřőŻżĆ©Sřřc ÷ݟގIŻżď÷t‡ăÂđ<ü~üţ÷Łţ©µ@-¬e¸q}ŽĂß˙=t:45©+ߏŮL­ýę µ1ĄLR:?עQĹE&Čž™ÁĚ l6š+m™(°–ˇXTŻ @ (˛c0Ő<â¸{ďĐş|CC°ŮŔóG,6÷]GŽ`r^y…Ć@,µZ—‡Š¬ŰŤćfđĽzĎĺËęăĘęa­­ŕ8z-µ°žŇł‹Í ·!diJ1¦ö',ÜD–‡kyC>Ź 06·SSxóM0†ť;imTň“Í"„Ăq×íÓd1tJ¸lĹ"€É„ÖV„ø~ťÖF%Ź„ă02‚7ŢŔ±cZ—R¨…µ$Y†$©#xŹÚď®°XĐĐ@7¸’G"˰ŮĐŇň€^ÖLMáđa„ĂhjÜHcÜÉjYřF’$dł` f3]@śEuĘ ŕ­[ex<ôŽ!«‰1dł¸rÁ úY¨  P@Gü~9ť?ý)ęëµ®¸TP`ݡ4—Sł)Ă‘#EÍ )UÉŞËdđí·ž†×«>Ââq|ü1"ŚFÔÔ X¤uçŁŔşăÄ ŚŚ Ąú§e‹p8`0Ü{šUˇ×Ł©éŢ+6˛ ‡w¦É@Mű{P`Ý ÂhD0l‡azĹ"5ĹÉZ´TąÎCî†5ܡÜĆq(‘LÂí¦ĺ)5ÔÂZ Çç©5NÖÇáÚ5ÄbH&ď:%$wŁÖݲYőduF'ËđŕV2™L$v»=“É8Ëě" •GsüŢ{HĄhş˛ŢôzX,H$´®Ł¤=8°>řŕoľůf۶mCCC›6mzőŐWc‘HdxxXŻ×{˝ŢĘYšPsÜÖ¦ż"dý-|ăq††pó&\.<őTĹĎ L&Ăáp,Ë/6žăÁµcÇŽT*uęÔ)ŁŃ8»đW6›ŤĹb&“©iţ°7BČŞc ×®abĂĂčéѨuAk+źĎÇb±x<..vŻŇ«¶¶¶ľľţżřĹÔÔÔ† ȲÜŇҲk×.­ŤęŔóp:0ĎmĄđx<Ź'Ť^˝zuáÖVkkkkk«ÖGAHu«‚¨ZŽ ?ľŻHź}†Ó§irRşC.‡/żÄĐ8Ń(NžD_źÖei©úĆa)Źqń"d>&'qó&FGáói]©zŚA–ńÍ7꒢۷Ť˘±Ů,nÝÂČ6mRgUV†:W“ę ¬ŮcĽ^őnçk×09©NĺAćd33ŕ8¸\ŕ8¸ÝęŚlęëńí·řź˙A"»==číŐşÜuU]ń ™ t:xŹ|ľÜ/‘—OV±|:Ý]Ó*d28yąöě™ű‡q»±Řôő„T#Ćp馧‘NĂbÁčîÖş¦•+źŔ:u WŻ˘¦÷wĐëŐ“IÜĽ žGM ¦§qę"lެu­„”»N'r9ŘlHĄÉŕĆ pĽ^ ečő°ŮÔťS)‹°ZKsčĎJkú”§=łËdP_ŹL˘ˇ!DŁhiŮ “ NťÇ–đ!dˇŮů””Ż8q‰¬Vdł¨©ÝŽřčőČfńÚkH§±{7öíÓşîE¬(°8_~©Nx¸¨ë×á÷َańƧ ¨súÜŹ2x÷ăŹ!ËxńE´´¨Ź+łÇćrřă!IřĂŕő"›E]ęëŐI‚¨ëŠE1I‰ŕyx<0ŕr!@c#R)äóŕ8äó` ŤŤ÷vŞřý€Ĺ‚žčtEuŽfe֦͛ŃÖ¶>±ÜŔÁď÷;ťN‡2˘,#@.‡©)đzQ,bzZ˝6qâôz\ąłY˝látÂn‡^ŹďľĂńăŕyüŐ_ˇ¶Ĺ"b1¤R°ŰQW‡©)„ĂřňK$ĐéÔ…p3LO#…ÉżŻżŽ`;w"™DM …ą9ůćŚ,üoţă´3í\…;sl¶ą¶‚ň Nż_mXY,DPSŁîP,B–ÁóčëĂŔ2Í@,†ĆFěÝ‹/ľ€N‡|^ ,YVŻFŁĐëQ_ŻŽ%âůą'U~ćüŐéîjgd‹-JřuáÂ…7ß|łŁŁă—żü%c,\(ÜÇ©Ł{•Ýf;Ľf˙Ş4€igÚ™v^ÁÎĘﵲřîô‚ÍţFsśÚ±3ŰLS® ÂÜoşň«­ü4FŁúD Żŕ+÷)dYŮA’冶¶ĆĆƶ°víÚ•Ë唕źu:]×öíËüFBY-ËmaÝ#‹I’äv»µ®ż„‹ĹP(d4c˛,Ó‹Ł!•JY­Öp8ět:‰„ŐjµÍ^DŻbŮlVĹb±Ífív{*•r»Ý†’L°ždYV~ŹdYEŃh4¦Óéşş:Žă°˛«„©Tęß˙ýßÓéôo~󛆆­°TŚŹŹ˙ëżţkOOO ČçóżţőŻéĹpéŇĄcÇŽőôô?~Üb± Ż×ű«_ýбjżžűţűď‹Ĺ`0H$ęëëGFFöîÝűńZץ±P(ô»ßý.‰˘(‚Á`Čd2ŻĽňJww7VX…Báöíۢ(fłY­Ź®„8ťÎ—^zéÓO?M$FŁ‘^…ĂáČd2±XlllĚn·3Ć$Iâ+zžąĺ°ŰíĂĂĂň'rćĚ™/żüŇb±A­‹ŇžÉdâ8®±±ńâĹ‹˘(f2«Ő:33Łl]I`9Žźüä'ů|~a—X5SÎt~üăK’T,éĹQ‚ĐŃѱuëV‡Ăáőz@ss3Ą•,ËV«µ©©É`0tttôööNNN>ńÄZץ=A\.×ćÍ›}>_±X´Ůl‘HdëÖ­ĘÖöaBČú«¸Ů!•‹‹¬ŽX,vĺĘĺĎ—.]^t·ˇˇˇńńńůŹřýţ7nh]>)ĺ3[)aÓÓÓ‡őz˝&“IŻ×_ľ|Ůfł™ÍfŹÇłiÓ¦\.wčС?˙ó?çťw‡ÍfËçó>źďСCżţőŻi¬y ,˛ Îź?ëÖ-żß˙Î;ď<őÔSV«U„O>ůäĉn·Űl6÷öönÚ´I–eI’âńř›oľi4wďŢ-BKK‹$IÔ—J–:ÝÉ*_}ő•Őj•$ÉăńD"6›mjjjăĆŤ˙÷˙÷ÜsĎíßżŔŔŔ@ˇPČd2˘(ʲśÍf7nÜÉdvěءőA2@EÖV&“ ÍÍÍ4†›<ş˙?µýHiÚ Ś%tEXtdate:create2010-10-22T01:01:29-07:00âĺúś%tEXtdate:modify2010-10-22T01:01:29-07:00“¸B IEND®B`‚libtorrent-rasterbar-1.1.13/docs/dht_extensions.html000066400000000000000000000137631351156116000226310ustar00rootroot00000000000000 dht_extensions.rst
Author: Arvid Norberg, arvid@libtorrent.org

Mainline DHT extensions

libtorrent implements a few extensions to the Mainline DHT protocol.

get_peers response

libtorrent always responds with nodes to a get_peers request. If it has peers for the specified info-hash, it will return values as well. This is because just because some peer announced to us, doesn't mean that we are among the 8 closest nodes of the info hash. libtorrent also keeps traversing nodes using get_peers until it has found the 8 closest ones, and then announces to those nodes.

forward compatibility

In order to support future DHT messages, any message which is not recognized but has either an info_hash or target argument is interpreted as find node for that target. i.e. it returns nodes. This allows future messages to be properly forwarded by clients that don't understand them instead of being blocked.

client identification

In each DHT packet, an extra key is inserted named "v". This is a string describing the client and version used. This can help alot when debugging and finding errors in client implementations. The string is encoded as four characters, two characters describing the client and two characters interpreted as a binary number describing the client version.

Currently known clients:

uTorrent UT
libtorrent LT
MooPolice MP
GetRight GR

IPv6 support

This extension is superseeded by BEP 32.

The DHT messages that don't support IPv6 are the nodes replies. They encode all the contacts as 6 bytes packed together in sequence in a string. The problem is that IPv6 endpoints cannot be encoded as 6 bytes, but needs 18 bytes. The extension libtorrent applies is to add another key, called nodes2.

nodes2 may be present in replies that contains a nodes key. It is encoded as a list of strings. Each string represents one contact and is encoded as 20 bytes node-id and then a variable length encoded IP address (6 bytes in IPv4 case and 18 bytes in IPv6 case).

As an optimization, libtorrent does not include the extra key in case there are only IPv4 nodes present.

libtorrent-rasterbar-1.1.13/docs/dht_extensions.rst000066400000000000000000000046311351156116000224670ustar00rootroot00000000000000:Author: Arvid Norberg, arvid@libtorrent.org Mainline DHT extensions ======================= libtorrent implements a few extensions to the Mainline DHT protocol. get_peers response ------------------ libtorrent always responds with ``nodes`` to a get_peers request. If it has peers for the specified info-hash, it will return ``values`` as well. This is because just because some peer announced to us, doesn't mean that we are among the 8 closest nodes of the info hash. libtorrent also keeps traversing nodes using get_peers until it has found the 8 closest ones, and then announces to those nodes. forward compatibility --------------------- In order to support future DHT messages, any message which is not recognized but has either an ``info_hash`` or ``target`` argument is interpreted as find node for that target. i.e. it returns nodes. This allows future messages to be properly forwarded by clients that don't understand them instead of being blocked. client identification --------------------- In each DHT packet, an extra key is inserted named "v". This is a string describing the client and version used. This can help alot when debugging and finding errors in client implementations. The string is encoded as four characters, two characters describing the client and two characters interpreted as a binary number describing the client version. Currently known clients: +---------------+--------+ | uTorrent | ``UT`` | +---------------+--------+ | libtorrent | ``LT`` | +---------------+--------+ | MooPolice | ``MP`` | +---------------+--------+ | GetRight | ``GR`` | +---------------+--------+ IPv6 support ------------ **This extension is superseeded by** `BEP 32`_. .. _`BEP 32`: https://bittorrent.org/beps/bep_0032.html The DHT messages that don't support IPv6 are the ``nodes`` replies. They encode all the contacts as 6 bytes packed together in sequence in a string. The problem is that IPv6 endpoints cannot be encoded as 6 bytes, but needs 18 bytes. The extension libtorrent applies is to add another key, called ``nodes2``. ``nodes2`` may be present in replies that contains a ``nodes`` key. It is encoded as a list of strings. Each string represents one contact and is encoded as 20 bytes node-id and then a variable length encoded IP address (6 bytes in IPv4 case and 18 bytes in IPv6 case). As an optimization, libtorrent does not include the extra key in case there are only IPv4 nodes present. libtorrent-rasterbar-1.1.13/docs/dht_rss.html000066400000000000000000000520551351156116000212360ustar00rootroot00000000000000 BitTorrent extension for DHT RSS feeds

BitTorrent extension for DHT RSS feeds

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

This proposal has been superseded by the dht_put feature. This may still be implemented on top of that.

This is a proposal for an extension to the BitTorrent DHT to allow for decentralized RSS feed like functionality.

The intention is to allow the creation of repositories of torrents where only a single identity has the authority to add new content. For this repository to be robust against network failures and resilient to attacks at the source.

The target ID under which the repository is stored in the DHT, is the SHA-1 hash of a feed name and the 512 bit public key. This private key in this pair MUST be used to sign every item stored in the repository. Every message that contain signed items MUST also include this key, to allow the receiver to verify the key itself against the target ID as well as the validity of the signatures of the items. Every recipient of a message with feed items in it MUST verify both the validity of the public key against the target ID it is stored under, as well as the validity of the signatures of each individual item.

As with normal DHT announces, the write-token mechanism is used to prevent IP spoof attacks.

terminology

In this document, a storage node refers to the node in the DHT to which an item is being announce. A subscribing node refers to a node which makes look ups in the DHT to find the storage nodes, to request items from them.

linked lists

Items are chained together in a geneal singly linked list. A linked list does not necessarily contain RSS items, and no RSS related items are mandatory. However, RSS items will be used as examples in this BEP:

key = SHA1(name + key)
+---------+
| head    |           key = SHA1(bencode(item))
| +---------+         +---------+
| | next    |-------->| item    |          key = SHA1(bencode(item))
| | key     |         | +---------+        +---------+
| | name    |         | | next    |------->| item    |
| | seq     |         | | key     |        | +---------+
| | ...     |         | | ...     |        | | next    |--->0
| +---------+         | +---------+        | | key     |
| sig     |           | sig     |          | | ...     |
+---------+           +---------+          | +---------+
                                           | sig     |
                                           +---------+

The next pointer is at least 20 byte ID in the DHT key space pointing to where the next item in the list is announced. The list is terminated with an ID of all zeros.

The ID an items is announced to is determined by the SHA1 hash of the bencoded representation of the item iteself. This contains all fields in the item, except the signature. The only mandatory fields in an item are next, key and sig.

The key field MUST match the public key of the list head node. The sig field MUST be the signature of the bencoded representation of item or head (whichever is included in the message).

All subscribers MUST verify that the item is announced under the correct DHT key and MUST verify the signature is valid and MUST verify the public key is the same as the list-head. If a node fails any of these checks, it must be ignored and the chain of items considered terminated.

Each item holds a bencoded dictionary with arbitrary keys, except two mandatory keys: next and key. The signature sig is transferred outside of this dictionary and is the signature of all of it. An implementation should stora any arbitrary keys that are announced to an item, within reasonable restriction such as nesting, size and numeric range of integers.

skip lists

The next key stored in the list head and the items is a string of at least length 20 bytes, it may be any length divisible by 20. Each 20 bytes are the ID of the next item in the list, the item 2 hops away, 4 hops away, 8 hops away, and so on. For simplicity, only the first ID (1 hop) in the next field is illustrated above.

A publisher of an item SHOULD include as many IDs in the next field as the remaining size of the list warrants, within reason.

These skip lists allow for parallelized lookups of items and also makes it more efficient to search for specific items. It also mitigates breaking lists missing some items.

Figure of the skip list in the first list item:

n      Item0  Item1  Item2  Item3  Item4  Item5  Item6  Item7  Item8  Item9  Item10
0        O----->
20       O------------>
40       O-------------------------->
60       O------------------------------------------------------>

n refers to the byte offset into the next field.

list-head

The list head item is special in that it can be updated, without changing its DHT key. This is required to prepend new items to the linked list. To authenticate that only the original publisher can update the head, the whole linked list head is signed. In order to avoid a malicious node to overwrite the list head with an old version, the sequence number seq must be monotonically increasing for each update, and a node hosting the list node MUST not downgrade a list head from a higher sequence number to a lower one, only upgrade.

The list head's DHT key (which it is announced to) MUST be the SHA1 hash of the name (n) and key fields concatenated.

Any node MUST reject any list head which is announced under any other ID.

messages

These are the messages to deal with linked lists.

The id field in these messages has the same semantics as the standard DHT messages, i.e. the node ID of the node sending the message, to maintain the structure of the DHT network.

The token field also has the same semantics as the standard DHT message get_peers and announce_peer, when requesting an item and to write an item respectively.

nodes and nodes6 has the same semantics as in its get_peers response.

requesting items

This message can be used to request both a list head and a list item. When requesting a list head, the n (name) field MUST be specified. When requesting a list item the n field is not required.

{
   "a":
   {
      "id": <20 byte ID of sending node>,
      "key": <64 byte public curve25519 key for this list>,
      "n": <list name>
      "target": <target-id for 'head' or 'item'>
   },
   "q": "get_item",
   "t": <transaction-id>,
   "y": "q",
}

When requesting a list-head the target MUST always be SHA-1(feed_name + public_key). target is the target node ID the item was written to.

The n field is the name of the list. If specified, It MUST be UTF-8 encoded string and it MUST match the name of the feed in the receiving node.

request item response

This is the format of a response of a list head:

{
   "r":
   {
      "head":
      {
         "key": <64 byte public curve25519 key for this list>,
         "next": <20 bytes item ID>,
         "n": <name of the linked list>,
         "seq": <monotonically increasing sequence number>
      },
      "sig": <curve25519 signature of 'head' entry (in bencoded form)>,
      "id": <20 byte id of sending node>,
      "token": <write-token>,
      "nodes": <n * compact IPv4-port pair>,
      "nodes6": <n * compact IPv6-port pair>
   },
   "t": <transaction-id>,
   "y": "r",
}

This is the format of a response of a list item:

{
   "r":
   {
      "item":
      {
         "key": <64 byte public curve25519 key for this list>,
         "next": <20 bytes item ID>,
         ...
      },
      "sig": <curve25519 signature of 'item' entry (in bencoded form)>,
      "id": <20 byte id of sending node>,
      "token": <write-token>,
      "nodes": <n * compact IPv4-port pair>,
      "nodes6": <n * compact IPv6-port pair>
   },
   "t": <transaction-id>,
   "y": "r",
}

A client receiving a get_item response MUST verify the signature in the sig field against the bencoded representation of the item field, using the key as the public key. The key MUST match the public key of the feed.

The item dictionary MAY contain arbitrary keys, and all keys MUST be stored for items.

announcing items

The message format for announcing a list head:

{
   "a":
   {
      "head":
      {
         "key": <64 byte public curve25519 key for this list>,
         "next": <20 bytes item ID>,
         "n": <name of the linked list>,
         "seq": <monotonically increasing sequence number>
      },
      "sig": <curve25519 signature of 'head' entry (in bencoded form)>,
      "id": <20 byte node-id of origin node>,
      "target": <target-id as derived from public key and name>,
      "token": <write-token as obtained by previous request>
   },
   "y": "q",
   "q": "announce_item",
   "t": <transaction-id>
}

The message format for announcing a list item:

{
   "a":
   {
      "item":
      {
         "key": <64 byte public curve25519 key for this list>,
         "next": <20 bytes item ID>,
         ...
      },
      "sig": <curve25519 signature of 'item' entry (in bencoded form)>,
      "id": <20 byte node-id of origin node>,
      "target": <target-id as derived from item dict>,
      "token": <write-token as obtained by previous request>
   },
   "y": "q",
   "q": "announce_item",
   "t": <transaction-id>
}

A storage node MAY reject items and heads whose bencoded representation is greater than 1024 bytes.

re-announcing

In order to keep feeds alive, subscriber nodes SHOULD help out in announcing items they have downloaded to the DHT.

Every subscriber node SHOULD store items in long term storage, across sessions, in order to keep items alive for as long as possible, with as few sources as possible.

Subscribers to a feed SHOULD also announce items that they know of, to the feed. Since a feed may have many subscribers and many items, subscribers should re-announce items according to the following algorithm.

1. pick one random item (i) from the local repository (except
   items already announced this round)
2. If all items in the local repository have been announced
  2.1 terminate
3. look up item i in the DHT
4. If fewer than 8 nodes returned the item
  4.1 announce i to the DHT
  4.2 goto 1

This ensures a balanced load on the DHT while still keeping items alive

timeouts

Items SHOULD be announced to the DHT every 30 minutes. A storage node MAY time out an item after 60 minutes of no one announcing it.

A storing node MAY extend the timeout when it receives a request for it. Since items are immutable, the data doesn't go stale. Therefore it doesn't matter if the storing node no longer is in the set of the 8 closest nodes.

RSS feeds

For RSS feeds, following keys are mandatory in the list item's item dictionary.

ih
The torrent's info hash
size
The size (in bytes) of all files the torrent
n
name of the torrent

example

This is an example of an announce_item message:

{
   "a":
   {
      "item":
      {
         "key": "6bc1de5443d1a7c536cdf69433ac4a7163d3c63e2f9c92d
            78f6011cf63dbcd5b638bbc2119cdad0c57e4c61bc69ba5e2c08
            b918c2db8d1848cf514bd9958d307",
         "info-hash": "7ea94c240691311dc0916a2a91eb7c3db2c6f3e4",
         "size": 24315329,
         "n": "my stuff",
         "next": "c68f29156404e8e0aas8761ef5236bcagf7f8f2e"
      }
      "sig": <signature>
      "id": "b46989156404e8e0acdb751ef553b210ef77822e",
      "target": "b4692ef0005639e86d7165bf378474107bf3a762"
      "token": "23ba"
   },
   "y": "q",
   "q": "announce_item",
"t": "a421"
}

Strings are printed in hex for printability, but actual encoding is binary.

Note that target is in fact SHA1 hash of the same data the signature sig is the signature of, i.e.:

d9:info-hash20:7ea94c240691311dc0916a2a91eb7c3db2c6f3e43:key64:6bc1de5443d1
a7c536cdf69433ac4a7163d3c63e2f9c92d78f6011cf63dbcd5b638bbc2119cdad0c57e4c61
bc69ba5e2c08b918c2db8d1848cf514bd9958d3071:n8:my stuff4:next20:c68f29156404
e8e0aas8761ef5236bcagf7f8f2e4:sizei24315329ee

(note that binary data is printed as hex)

RSS feed URI scheme

The proposed URI scheme for DHT feeds is:

magnet:?xt=btfd:<base16-curve25519-public-key> &dn= <feed name>

Note that a difference from regular torrent magnet links is the btfd versus btih used in regular magnet links to torrents.

The feed name is mandatory since it is used in the request and when calculating the target ID.

rationale

The reason to use curve25519 instead of, for instance, RSA is compactness. According to https://cr.yp.to/, curve25519 is free from patent claims and there are open implementations in both C and Java.

libtorrent-rasterbar-1.1.13/docs/dht_rss.rst000066400000000000000000000321211351156116000210720ustar00rootroot00000000000000====================================== BitTorrent extension for DHT RSS feeds ====================================== :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none This proposal has been superseded by the dht_put_ feature. This may still be implemented on top of that. .. _dht_put: dht_store.html This is a proposal for an extension to the BitTorrent DHT to allow for decentralized RSS feed like functionality. The intention is to allow the creation of repositories of torrents where only a single identity has the authority to add new content. For this repository to be robust against network failures and resilient to attacks at the source. The target ID under which the repository is stored in the DHT, is the SHA-1 hash of a feed name and the 512 bit public key. This private key in this pair MUST be used to sign every item stored in the repository. Every message that contain signed items MUST also include this key, to allow the receiver to verify the key itself against the target ID as well as the validity of the signatures of the items. Every recipient of a message with feed items in it MUST verify both the validity of the public key against the target ID it is stored under, as well as the validity of the signatures of each individual item. As with normal DHT announces, the write-token mechanism is used to prevent IP spoof attacks. terminology ----------- In this document, a *storage node* refers to the node in the DHT to which an item is being announce. A *subscribing node* refers to a node which makes look ups in the DHT to find the storage nodes, to request items from them. linked lists ------------ Items are chained together in a geneal singly linked list. A linked list does not necessarily contain RSS items, and no RSS related items are mandatory. However, RSS items will be used as examples in this BEP:: key = SHA1(name + key) +---------+ | head | key = SHA1(bencode(item)) | +---------+ +---------+ | | next |-------->| item | key = SHA1(bencode(item)) | | key | | +---------+ +---------+ | | name | | | next |------->| item | | | seq | | | key | | +---------+ | | ... | | | ... | | | next |--->0 | +---------+ | +---------+ | | key | | sig | | sig | | | ... | +---------+ +---------+ | +---------+ | sig | +---------+ The ``next`` pointer is at least 20 byte ID in the DHT key space pointing to where the next item in the list is announced. The list is terminated with an ID of all zeros. The ID an items is announced to is determined by the SHA1 hash of the bencoded representation of the item iteself. This contains all fields in the item, except the signature. The only mandatory fields in an item are ``next``, ``key`` and ``sig``. The ``key`` field MUST match the public key of the list head node. The ``sig`` field MUST be the signature of the bencoded representation of ``item`` or ``head`` (whichever is included in the message). All subscribers MUST verify that the item is announced under the correct DHT key and MUST verify the signature is valid and MUST verify the public key is the same as the list-head. If a node fails any of these checks, it must be ignored and the chain of items considered terminated. Each item holds a bencoded dictionary with arbitrary keys, except two mandatory keys: ``next`` and ``key``. The signature ``sig`` is transferred outside of this dictionary and is the signature of all of it. An implementation should stora any arbitrary keys that are announced to an item, within reasonable restriction such as nesting, size and numeric range of integers. skip lists ---------- The ``next`` key stored in the list head and the items is a string of at least length 20 bytes, it may be any length divisible by 20. Each 20 bytes are the ID of the next item in the list, the item 2 hops away, 4 hops away, 8 hops away, and so on. For simplicity, only the first ID (1 hop) in the ``next`` field is illustrated above. A publisher of an item SHOULD include as many IDs in the ``next`` field as the remaining size of the list warrants, within reason. These skip lists allow for parallelized lookups of items and also makes it more efficient to search for specific items. It also mitigates breaking lists missing some items. Figure of the skip list in the first list item:: n Item0 Item1 Item2 Item3 Item4 Item5 Item6 Item7 Item8 Item9 Item10 0 O-----> 20 O------------> 40 O--------------------------> 60 O------------------------------------------------------> *n* refers to the byte offset into the ``next`` field. list-head --------- The list head item is special in that it can be updated, without changing its DHT key. This is required to prepend new items to the linked list. To authenticate that only the original publisher can update the head, the whole linked list head is signed. In order to avoid a malicious node to overwrite the list head with an old version, the sequence number ``seq`` must be monotonically increasing for each update, and a node hosting the list node MUST not downgrade a list head from a higher sequence number to a lower one, only upgrade. The list head's DHT key (which it is announced to) MUST be the SHA1 hash of the name (``n``) and ``key`` fields concatenated. Any node MUST reject any list head which is announced under any other ID. messages -------- These are the messages to deal with linked lists. The ``id`` field in these messages has the same semantics as the standard DHT messages, i.e. the node ID of the node sending the message, to maintain the structure of the DHT network. The ``token`` field also has the same semantics as the standard DHT message ``get_peers`` and ``announce_peer``, when requesting an item and to write an item respectively. ``nodes`` and ``nodes6`` has the same semantics as in its ``get_peers`` response. requesting items ................ This message can be used to request both a list head and a list item. When requesting a list head, the ``n`` (name) field MUST be specified. When requesting a list item the ``n`` field is not required. .. parsed-literal:: { "a": { "id": *<20 byte ID of sending node>*, "key": *<64 byte public curve25519 key for this list>*, "n": ** "target": ** }, "q": "get_item", "t": **, "y": "q", } When requesting a list-head the ``target`` MUST always be SHA-1(*feed_name* + *public_key*). ``target`` is the target node ID the item was written to. The ``n`` field is the name of the list. If specified, It MUST be UTF-8 encoded string and it MUST match the name of the feed in the receiving node. request item response ..................... This is the format of a response of a list head: .. parsed-literal:: { "r": { "head": { "key": *<64 byte public curve25519 key for this list>*, "next": *<20 bytes item ID>*, "n": **, "seq": ** }, "sig": **, "id": *<20 byte id of sending node>*, "token": **, "nodes": **, "nodes6": ** }, "t": **, "y": "r", } This is the format of a response of a list item: .. parsed-literal:: { "r": { "item": { "key": *<64 byte public curve25519 key for this list>*, "next": *<20 bytes item ID>*, ... }, "sig": **, "id": *<20 byte id of sending node>*, "token": **, "nodes": **, "nodes6": ** }, "t": **, "y": "r", } A client receiving a ``get_item`` response MUST verify the signature in the ``sig`` field against the bencoded representation of the ``item`` field, using the ``key`` as the public key. The ``key`` MUST match the public key of the feed. The ``item`` dictionary MAY contain arbitrary keys, and all keys MUST be stored for items. announcing items ................ The message format for announcing a list head: .. parsed-literal:: { "a": { "head": { "key": *<64 byte public curve25519 key for this list>*, "next": *<20 bytes item ID>*, "n": **, "seq": ** }, "sig": **, "id": *<20 byte node-id of origin node>*, "target": **, "token": ** }, "y": "q", "q": "announce_item", "t": ** } The message format for announcing a list item: .. parsed-literal:: { "a": { "item": { "key": *<64 byte public curve25519 key for this list>*, "next": *<20 bytes item ID>*, ... }, "sig": **, "id": *<20 byte node-id of origin node>*, "target": **, "token": ** }, "y": "q", "q": "announce_item", "t": ** } A storage node MAY reject items and heads whose bencoded representation is greater than 1024 bytes. re-announcing ------------- In order to keep feeds alive, subscriber nodes SHOULD help out in announcing items they have downloaded to the DHT. Every subscriber node SHOULD store items in long term storage, across sessions, in order to keep items alive for as long as possible, with as few sources as possible. Subscribers to a feed SHOULD also announce items that they know of, to the feed. Since a feed may have many subscribers and many items, subscribers should re-announce items according to the following algorithm. .. parsed-literal:: 1. pick one random item (*i*) from the local repository (except items already announced this round) 2. If all items in the local repository have been announced 2.1 terminate 3. look up item *i* in the DHT 4. If fewer than 8 nodes returned the item 4.1 announce *i* to the DHT 4.2 goto 1 This ensures a balanced load on the DHT while still keeping items alive timeouts -------- Items SHOULD be announced to the DHT every 30 minutes. A storage node MAY time out an item after 60 minutes of no one announcing it. A storing node MAY extend the timeout when it receives a request for it. Since items are immutable, the data doesn't go stale. Therefore it doesn't matter if the storing node no longer is in the set of the 8 closest nodes. RSS feeds --------- For RSS feeds, following keys are mandatory in the list item's ``item`` dictionary. ih The torrent's info hash size The size (in bytes) of all files the torrent n name of the torrent example ....... This is an example of an ``announce_item`` message: .. parsed-literal:: { "a": { "item": { "key": "6bc1de5443d1a7c536cdf69433ac4a7163d3c63e2f9c92d 78f6011cf63dbcd5b638bbc2119cdad0c57e4c61bc69ba5e2c08 b918c2db8d1848cf514bd9958d307", "info-hash": "7ea94c240691311dc0916a2a91eb7c3db2c6f3e4", "size": 24315329, "n": "my stuff", "next": "c68f29156404e8e0aas8761ef5236bcagf7f8f2e" } "sig": ** "id": "b46989156404e8e0acdb751ef553b210ef77822e", "target": "b4692ef0005639e86d7165bf378474107bf3a762" "token": "23ba" }, "y": "q", "q": "announce_item", "t": "a421" } Strings are printed in hex for printability, but actual encoding is binary. Note that ``target`` is in fact SHA1 hash of the same data the signature ``sig`` is the signature of, i.e.:: d9:info-hash20:7ea94c240691311dc0916a2a91eb7c3db2c6f3e43:key64:6bc1de5443d1 a7c536cdf69433ac4a7163d3c63e2f9c92d78f6011cf63dbcd5b638bbc2119cdad0c57e4c61 bc69ba5e2c08b918c2db8d1848cf514bd9958d3071:n8:my stuff4:next20:c68f29156404 e8e0aas8761ef5236bcagf7f8f2e4:sizei24315329ee (note that binary data is printed as hex) RSS feed URI scheme -------------------- The proposed URI scheme for DHT feeds is: .. parsed-literal:: magnet:?xt=btfd:** &dn= ** Note that a difference from regular torrent magnet links is the **btfd** versus **btih** used in regular magnet links to torrents. The *feed name* is mandatory since it is used in the request and when calculating the target ID. rationale --------- The reason to use curve25519_ instead of, for instance, RSA is compactness. According to https://cr.yp.to/, curve25519 is free from patent claims and there are open implementations in both C and Java. .. _curve25519: https://cr.yp.to/ecdh.html libtorrent-rasterbar-1.1.13/docs/dht_sec.html000066400000000000000000000363531351156116000212040ustar00rootroot00000000000000 BitTorrent DHT security extension

BitTorrent DHT security extension

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

BitTorrent DHT security extension

The purpose of this extension is to make it harder to launch a few specific attacks against the BitTorrent DHT and also to make it harder to snoop the network.

Specifically the attack this extension intends to make harder is launching 8 or more DHT nodes which node-IDs selected close to a specific target info-hash, in order to become the main nodes hosting peers for it. Currently this is very easy to do and lets the attacker not only see all the traffic related to this specific info-hash but also block access to it by other peers.

The proposed guard against this is to enforce restrictions on which node-ID a node can choose, based on its external IP address.

considerations

One straight forward scheme to tie the node ID to an IP would be to hash the IP and force the node ID to share the prefix of that hash. One main draw back of this approach is that an entities control over the DHT key space grows linearly with its control over the IP address space.

In order to successfully launch an attack, you just need to find 8 IPs whose hash will be closest to the target info-hash. Given the current size of the DHT, that is quite likely to be possible by anyone in control of a /8 IP block.

The size of the DHT is approximately 8.4 million nodes. This is estmiated by observing that a typical routing table typically has about 20 of its top routing table buckets full. That means the key space is dense enough to contain 8 nodes for every combination of the 20 top bits of node IDs.

2^20 * 8 = 8388608

By controlling that many IP addresses, an attacker could snoop any info-hash. By controlling 8 times that many IP addresses, an attacker could actually take over any info-hash.

With IPv4, snooping would require a /8 IP block, giving access to 16.7 million Ips.

Another problem with hashing the IP is that multiple users behind a NAT are forced to run their DHT nodes on the same node ID.

Node ID restriction

In order to avoid the number node IDs controlled to grow linearly by the number of IPs, as well as allowing more than one node ID per external IP, the node ID can be restricted at each class level of the IP.

Another important property of the restriction put on node IDs is that the distribution of the IDs remoain uniform. This is why CRC32C (Castagnoli) was chosen as the hash function.

The expression to calculate a valid ID prefix (from an IPv4 address) is:

crc32c((ip & 0x030f3fff) | (r << 29))

And for an IPv6 address (ip is the high 64 bits of the address):

crc32c((ip & 0x0103070f1f3f7fff) | (r << 61))

r is a random number in the range [0, 7]. The resulting integer, representing the masked IP address is supposed to be big-endian before hashed. The "|" operator means bit-wise OR.

The details of implementing this is to evaluate the expression, store the result in a big endian 64 bit integer and hash those 8 bytes with CRC32C.

The first (most significant) 21 bits of the node ID used in the DHT MUST match the first 21 bits of the resulting hash. The last byte of the hash MUST match the random number (r) used to generate the hash.

ip_id_v4.png ip_id_v6.png

Example code code for calculating a valid node ID:

uint8_t* ip; // our external IPv4 or IPv6 address (network byte order)
int num_octets; // the number of octets to consider in ip (4 or 8)
uint8_t node_id[20]; // resulting node ID

uint8_t v4_mask[] = { 0x03, 0x0f, 0x3f, 0xff };
uint8_t v6_mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
uint8_t* mask = num_octets == 4 ? v4_mask : v6_mask;

for (int i = 0; i < num_octets; ++i)
        ip[i] &= mask[i];

uint32_t rand = std::rand() & 0xff;
uint8_t r = rand & 0x7;
ip[0] |= r << 5;

uint32_t crc = 0;
crc = crc32c(crc, ip, num_octets);

// only take the top 21 bits from crc
node_id[0] = (crc >> 24) & 0xff;
node_id[1] = (crc >> 16) & 0xff;
node_id[2] = ((crc >> 8) & 0xf8) | (std::rand() & 0x7);
for (int i = 3; i < 19; ++i) node_id[i] = std::rand();
node_id[19] = rand;

test vectors:

IP           rand  example node ID
============ ===== ==========================================
124.31.75.21   1   5fbfbf f10c5d6a4ec8a88e4c6ab4c28b95eee4 01
21.75.31.124  86   5a3ce9 c14e7a08645677bbd1cfe7d8f956d532 56
65.23.51.170  22   a5d432 20bc8f112a3d426c84764f8c2a1150e6 16
84.124.73.14  65   1b0321 dd1bb1fe518101ceef99462b947a01ff 41
43.213.53.83  90   e56f6c bf5b7c4be0237986d5243b87aa6d5130 5a

The bold parts of the node ID are the important parts. The rest are random numbers. The last bold number of each row has only its most significant bit pulled from the CRC32C function. The lower 3 bits are random.

bootstrapping

In order to set ones initial node ID, the external IP needs to be known. This is not a trivial problem. With this extension, all DHT responses SHOULD include a top-level field called ip, containing a compact binary representation of the requestor's IP and port. That is big endian IP followed by 2 bytes of big endian port.

The IP portion is the same byte sequence used to verify the node ID.

It is important that the ip field is in the top level dictionary. Nodes that enforce the node-ID will respond with an error message ("y": "e", "e": { ... }), whereas a node that supports this extension but without enforcing it will respond with a normal reply ("y": "r", "r": { ... }).

A DHT node which receives an ip result in a request SHOULD consider restarting its DHT node with a new node ID, taking this IP into account. Since a single node can not be trusted, there should be some mechanism to determine whether or not the node has a correct understanding of its external IP or not. This could be done by voting, or only restart the DHT once at least a certain number of nodes, from separate searches, tells you your node ID is incorrect.

rationale

The choice of using CRC32C instead of a more traditional cryptographic hash function is justified primarily of these reasons:

  1. it is a fast function
  2. produces well distributed results
  3. there is no need for the hash function to be one-way (the input set is so small that any hash function could be reversed).
  4. CRC32C (Castagnoli) is supported in hardware by SSE 4.2, which can significantly speed up computation

There are primarily two tests run on SHA-1 and CRC32C to establish the distribution of results. The first one is the number of bits in the output set that contain every possible combination of bits. The CRC32C function has a longer such prefix in its output than SHA-1. This means nodes will still have well uniformly distributed IDs, even when IP addresses in use are not uniformly distributed.

The following graph illustrate a few different hash functions with regard to this property.

complete_bit_prefixes.png

This test takes into account IP addresses that are not globally routable, i.e. reserved for local networks, multicast and other things. It also takes into account that some /8 blocks are not in use by end-users and exremely unlikely to ever run a DHT node. This makes the results likely to be very similar to what we would see in the wild.

These results indicate that CRC32C provides the best uniformity in the results in terms of bit prefixes where all possibilities are represented, and that no more than 21 bits should be used from the result. If more than 21 bits were to be used, there would be certain node IDs that would be impossible to have, which would make routing sub-optimal.

The second test is more of a sanity test for the uniform distribution property. The target space (32 bit interger) is divided up into 1000 buckets. Every valid IP and r input is run through the algorithm and the result is put in the bucket it falls in. The expectation is that each bucket has roughly an equal number of results falling into it. The following graph shows the resulting histogram, comparing SHA-1 and CRC32C.

hash_distribution.png

The source code for these tests can be found here.

The reason to use CRC32C instead of the CRC32 implemented by zlib is that Intel CPUs have hardware support for the CRC32C calculations. The input being exactly 4 bytes is also deliberate, to make it fit in a single instruction.

enforcement

Once enforced, write tokens from peers whose node ID does not match its external IP should be considered dropped. In other words, a peer that uses a non-matching ID MUST never be used to store information on, regardless of which request. In the original DHT specification only announce_peer stores data in the network, but any future extension which stores data in the network SHOULD use the same restriction.

Any peer on a local network address is exempt from this node ID verification. This includes the following IP blocks:

10.0.0.0/8
reserved for local networks
172.16.0.0/12
reserved for local networks
192.168.0.0/16
reserved for local networks
169.254.0.0/16
reserved for self-assigned IPs
127.0.0.0/8
reserved for loopback

backwards compatibility and transition

During some transition period, this restriction should not be enforced, and peers whose node ID does not match this formula relative to their external IP should not be blocked.

Requests from peers whose node ID does not match their external IP should always be serviced, even after the transition period. The attack this protects from is storing data on an attacker's node, not servicing an attackers request.

forward compatibility

If the total size of the DHT grows to the point where the inherent size limit in this proposal is too small, the modulus constants can be updated in a new proposal, and another transition period where both sets of modulus constants are accepted.

libtorrent-rasterbar-1.1.13/docs/dht_sec.rst000066400000000000000000000240151351156116000210400ustar00rootroot00000000000000================================= BitTorrent DHT security extension ================================= :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none BitTorrent DHT security extension --------------------------------- The purpose of this extension is to make it harder to launch a few specific attacks against the BitTorrent DHT and also to make it harder to snoop the network. Specifically the attack this extension intends to make harder is launching 8 or more DHT nodes which node-IDs selected close to a specific target info-hash, in order to become the main nodes hosting peers for it. Currently this is very easy to do and lets the attacker not only see all the traffic related to this specific info-hash but also block access to it by other peers. The proposed guard against this is to enforce restrictions on which node-ID a node can choose, based on its external IP address. considerations -------------- One straight forward scheme to tie the node ID to an IP would be to hash the IP and force the node ID to share the prefix of that hash. One main draw back of this approach is that an entities control over the DHT key space grows linearly with its control over the IP address space. In order to successfully launch an attack, you just need to find 8 IPs whose hash will be *closest* to the target info-hash. Given the current size of the DHT, that is quite likely to be possible by anyone in control of a /8 IP block. The size of the DHT is approximately 8.4 million nodes. This is estmiated by observing that a typical routing table typically has about 20 of its top routing table buckets full. That means the key space is dense enough to contain 8 nodes for every combination of the 20 top bits of node IDs. ``2^20 * 8 = 8388608`` By controlling that many IP addresses, an attacker could snoop any info-hash. By controlling 8 times that many IP addresses, an attacker could actually take over any info-hash. With IPv4, snooping would require a /8 IP block, giving access to 16.7 million Ips. Another problem with hashing the IP is that multiple users behind a NAT are forced to run their DHT nodes on the same node ID. Node ID restriction ------------------- In order to avoid the number node IDs controlled to grow linearly by the number of IPs, as well as allowing more than one node ID per external IP, the node ID can be restricted at each class level of the IP. Another important property of the restriction put on node IDs is that the distribution of the IDs remoain uniform. This is why CRC32C (Castagnoli) was chosen as the hash function. The expression to calculate a valid ID prefix (from an IPv4 address) is:: crc32c((ip & 0x030f3fff) | (r << 29)) And for an IPv6 address (``ip`` is the high 64 bits of the address):: crc32c((ip & 0x0103070f1f3f7fff) | (r << 61)) ``r`` is a random number in the range [0, 7]. The resulting integer, representing the masked IP address is supposed to be big-endian before hashed. The "|" operator means bit-wise OR. The details of implementing this is to evaluate the expression, store the result in a big endian 64 bit integer and hash those 8 bytes with CRC32C. The first (most significant) 21 bits of the node ID used in the DHT MUST match the first 21 bits of the resulting hash. The last byte of the hash MUST match the random number (``r``) used to generate the hash. .. image:: ip_id_v4.png .. image:: ip_id_v6.png Example code code for calculating a valid node ID:: uint8_t* ip; // our external IPv4 or IPv6 address (network byte order) int num_octets; // the number of octets to consider in ip (4 or 8) uint8_t node_id[20]; // resulting node ID uint8_t v4_mask[] = { 0x03, 0x0f, 0x3f, 0xff }; uint8_t v6_mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; uint8_t* mask = num_octets == 4 ? v4_mask : v6_mask; for (int i = 0; i < num_octets; ++i) ip[i] &= mask[i]; uint32_t rand = std::rand() & 0xff; uint8_t r = rand & 0x7; ip[0] |= r << 5; uint32_t crc = 0; crc = crc32c(crc, ip, num_octets); // only take the top 21 bits from crc node_id[0] = (crc >> 24) & 0xff; node_id[1] = (crc >> 16) & 0xff; node_id[2] = ((crc >> 8) & 0xf8) | (std::rand() & 0x7); for (int i = 3; i < 19; ++i) node_id[i] = std::rand(); node_id[19] = rand; test vectors: .. parsed-literal:: IP rand example node ID ============ ===== ========================================== 124.31.75.21 1 **5fbfbf** f10c5d6a4ec8a88e4c6ab4c28b95eee4 **01** 21.75.31.124 86 **5a3ce9** c14e7a08645677bbd1cfe7d8f956d532 **56** 65.23.51.170 22 **a5d432** 20bc8f112a3d426c84764f8c2a1150e6 **16** 84.124.73.14 65 **1b0321** dd1bb1fe518101ceef99462b947a01ff **41** 43.213.53.83 90 **e56f6c** bf5b7c4be0237986d5243b87aa6d5130 **5a** The bold parts of the node ID are the important parts. The rest are random numbers. The last bold number of each row has only its most significant bit pulled from the CRC32C function. The lower 3 bits are random. bootstrapping ------------- In order to set ones initial node ID, the external IP needs to be known. This is not a trivial problem. With this extension, *all* DHT responses SHOULD include a *top-level* field called ``ip``, containing a compact binary representation of the requestor's IP and port. That is big endian IP followed by 2 bytes of big endian port. The IP portion is the same byte sequence used to verify the node ID. It is important that the ``ip`` field is in the top level dictionary. Nodes that enforce the node-ID will respond with an error message ("y": "e", "e": { ... }), whereas a node that supports this extension but without enforcing it will respond with a normal reply ("y": "r", "r": { ... }). A DHT node which receives an ``ip`` result in a request SHOULD consider restarting its DHT node with a new node ID, taking this IP into account. Since a single node can not be trusted, there should be some mechanism to determine whether or not the node has a correct understanding of its external IP or not. This could be done by voting, or only restart the DHT once at least a certain number of nodes, from separate searches, tells you your node ID is incorrect. rationale --------- The choice of using CRC32C instead of a more traditional cryptographic hash function is justified primarily of these reasons: 1. it is a fast function 2. produces well distributed results 3. there is no need for the hash function to be one-way (the input set is so small that any hash function could be reversed). 4. CRC32C (Castagnoli) is supported in hardware by SSE 4.2, which can significantly speed up computation There are primarily two tests run on SHA-1 and CRC32C to establish the distribution of results. The first one is the number of bits in the output set that contain every possible combination of bits. The CRC32C function has a longer such prefix in its output than SHA-1. This means nodes will still have well uniformly distributed IDs, even when IP addresses in use are not uniformly distributed. The following graph illustrate a few different hash functions with regard to this property. .. image:: complete_bit_prefixes.png This test takes into account IP addresses that are not globally routable, i.e. reserved for local networks, multicast and other things. It also takes into account that some /8 blocks are not in use by end-users and exremely unlikely to ever run a DHT node. This makes the results likely to be very similar to what we would see in the wild. These results indicate that CRC32C provides the best uniformity in the results in terms of bit prefixes where all possibilities are represented, and that no more than 21 bits should be used from the result. If more than 21 bits were to be used, there would be certain node IDs that would be impossible to have, which would make routing sub-optimal. The second test is more of a sanity test for the uniform distribution property. The target space (32 bit interger) is divided up into 1000 buckets. Every valid IP and ``r`` input is run through the algorithm and the result is put in the bucket it falls in. The expectation is that each bucket has roughly an equal number of results falling into it. The following graph shows the resulting histogram, comparing SHA-1 and CRC32C. .. image:: hash_distribution.png The source code for these tests can be found here_. .. _here: https://github.com/arvidn/hash_complete_prefix The reason to use CRC32C instead of the CRC32 implemented by zlib is that Intel CPUs have hardware support for the CRC32C calculations. The input being exactly 4 bytes is also deliberate, to make it fit in a single instruction. enforcement ----------- Once enforced, write tokens from peers whose node ID does not match its external IP should be considered dropped. In other words, a peer that uses a non-matching ID MUST never be used to store information on, regardless of which request. In the original DHT specification only ``announce_peer`` stores data in the network, but any future extension which stores data in the network SHOULD use the same restriction. Any peer on a local network address is exempt from this node ID verification. This includes the following IP blocks: 10.0.0.0/8 reserved for local networks 172.16.0.0/12 reserved for local networks 192.168.0.0/16 reserved for local networks 169.254.0.0/16 reserved for self-assigned IPs 127.0.0.0/8 reserved for loopback backwards compatibility and transition -------------------------------------- During some transition period, this restriction should not be enforced, and peers whose node ID does not match this formula relative to their external IP should not be blocked. Requests from peers whose node ID does not match their external IP should always be serviced, even after the transition period. The attack this protects from is storing data on an attacker's node, not servicing an attackers request. forward compatibility --------------------- If the total size of the DHT grows to the point where the inherent size limit in this proposal is too small, the modulus constants can be updated in a new proposal, and another transition period where both sets of modulus constants are accepted. libtorrent-rasterbar-1.1.13/docs/dht_store.html000066400000000000000000000617671351156116000215750ustar00rootroot00000000000000 BitTorrent extension for arbitrary DHT store

BitTorrent extension for arbitrary DHT store

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

This is a proposal for an extension to the BitTorrent DHT to allow storing and retrieving of arbitrary data.

It supports both storing immutable items, where the key is the SHA-1 hash of the data itself, and mutable items, where the key is the public key of the key pair used to sign the data.

There are two new proposed messages, put and get.

terminology

In this document, a storage node refers to the node in the DHT to which an item is being announced and stored on. A requesting node refers to a node which makes look-ups in the DHT to find the storage nodes, to request items from them, and possibly re-announce those items to keep them alive.

messages

The proposed new messages get and put are similar to the existing get_peers and announce_peer.

Responses to get should always include nodes and nodes6. Those fields have the same semantics as in its get_peers response. It should also include a write token, token, with the same semantics as int get_peers. The write token MAY be tied specifically to the key which get requested. i.e. the token can only be used to store values under that one key. It may also be tied to the node ID and IP address of the requesting node.

The id field in these messages has the same semantics as the standard DHT messages, i.e. the node ID of the node sending the message, to maintain the structure of the DHT network.

The token field also has the same semantics as the standard DHT message get_peers and announce_peer, when requesting an item and to write an item respectively.

The k field is the 32 byte ed25519 public key, which the signature can be authenticated with. When looking up a mutable item, the target field MUST be the SHA-1 hash of this key concatenated with the salt, if present.

The distinction between storing mutable and immutable items is the inclusion of a public key, a sequence number, signature and an optional salt (k, seq, sig and salt).

get requests for mutable items and immutable items cannot be distinguished from eachother. An implementation can either store mutable and immutable items in the same hash table internally, or in separate ones and potentially do two lookups for get requests.

The v field is the value to be stored. It is allowed to be any bencoded type (list, dict, string or integer). When it's being hashed (for verifying its signature or to calculate its key), its flattened, bencoded, form is used. It is important to use the verbatim bencoded representation as it appeared in the message. decoding and then re-encoding bencoded structures is not necessarily an identity operation.

Storing nodes MAY reject put requests where the bencoded form of v is longer than 1000 bytes. In other words, it's not safe to assume storing more than 1000 bytes will succeed.

immutable items

Immutable items are stored under their SHA-1 hash, and since they cannot be modified, there is no need to authenticate the origin of them. This makes immutable items simple.

A node making a lookup SHOULD verify the data it receives from the network, to verify that its hash matches the target that was looked up.

put message

Request:

{
        "a":
        {
                "id": <20 byte id of sending node (string)>,
                "v": <any bencoded type, whose encoded size <= 1000>
        },
        "t": <transaction-id (string)>,
        "y": "q",
        "q": "put"
}

Response:

{
        "r": { "id": <20 byte id of sending node (string)> },
        "t": <transaction-id (string)>,
        "y": "r",
}

get message

Request:

{
        "a":
        {
                "id": <20 byte id of sending node (string)>,
                "target": <SHA-1 hash of item (string)>,
        },
        "t": <transaction-id (string)>,
        "y": "q",
        "q": "get"
}

Response:

{
        "r":
        {
                "id": <20 byte id of sending node (string)>,
                "token": <write token (string)>,
                "v": <any bencoded type whose SHA-1 hash matches 'target'>,
                "nodes": <IPv4 nodes close to 'target'>,
                "nodes6": <IPv6 nodes close to 'target'>
        },
        "t": <transaction-id>,
        "y": "r",
}

mutable items

Mutable items can be updated, without changing their DHT keys. To authenticate that only the original publisher can update an item, it is signed by a private key generated by the original publisher. The target ID mutable items are stored under is the SHA-1 hash of the public key (as it appears in the put message).

In order to avoid a malicious node to overwrite the list head with an old version, the sequence number seq must be monotonically increasing for each update, and a node hosting the list node MUST not downgrade a list head from a higher sequence number to a lower one, only upgrade. The sequence number SHOULD not exceed MAX_INT64, (i.e. 0x7fffffffffffffff. A client MAY reject any message with a sequence number exceeding this. A client MAY also reject any message with a negative sequence number.

The signature is a 64 byte ed25519 signature of the bencoded sequence number concatenated with the v key. e.g. something like this:

3:seqi4e1:v12:Hello world!

If the salt key is present and non-empty, the salt string must be included in what's signed. Note that if salt is specified and an empty string, it is as if it was not specified and nothing in addition to the sequence number and the data is signed. The salt string may not be longer than 64 bytes.

When a salt is included in what is signed, the key salt with the value of the key is prepended in its bencoded form. For example, if salt is "foobar", the buffer to be signed is:

4:salt6:foobar3:seqi4e1:v12:Hello world!

put message

Request:

{
        "a":
        {
                "cas": <optional expected seq-nr (int)>,
                "id": <20 byte id of sending node (string)>,
                "k": <ed25519 public key (32 bytes string)>,
                "salt": <optional salt to be appended to "k" when hashing (string)>
                "seq": <monotonically increasing sequence number (integer)>,
                "sig": <ed25519 signature (64 bytes string)>,
                "token": <write-token (string)>,
                "v": <any bencoded type, whose encoded size < 1000>
        },
        "t": <transaction-id (string)>,
        "y": "q",
        "q": "put"
}

Storing nodes receiving a put request where seq is lower than or equal to what's already stored on the node, MUST reject the request. If the sequence number is equal, and the value is also the same, the node SHOULD reset its timeout counter.

If the sequence number in the put message is lower than the sequence number associated with the currently stored value, the storing node MAY return an error message with code 302 (see error codes below).

Note that this request does not contain a target hash. The target hash under which this blob is stored is implied by the k argument. The key is the SHA-1 hash of the key (k).

In order to support a single key being used to store separate items in the DHT, an optional salt can be specified in the put request of mutable items. If the salt entry is not present, it can be assumed to be an empty string, and its semantics should be identical as specifying a salt key with an empty string. The salt can be any binary string (but probably most conveniently a hash of something). This string is appended to the key, as specified in the k field, when calculating the key to store the blob under (i.e. the key get requests specify to retrieve this data).

This lets a single entity, with a single key, publish any number of unrelated items, with a single key that readers can verify. This is useful if the publisher doesn't know ahead of time how many different items are to be published. It can distribute a single public key for users to authenticate the published blobs.

Note that the salt is not returned in the response to a get request. This is intentional. When issuing a get request for an item is expected to know what the salt is (because it is part of what the target ID that is being looked up is derived from). There is no need to repeat it back for bystanders to see.

CAS

CAS is short for compare and swap, it has similar semantics as CAS CPU instructions. It is used to avoid race conditions when multiple nodes are writing to the same slot in the DHT.

The cas field is optional. If present it specifies the sequence number of the data blob being overwritten by the put. When present, the storing node MUST compare this number to the current sequence number it has stored under this key. Only if the cas matches the stored sequence number is the put performed. If it mismatches, the store fails and an error is returned. See errors below.

The cas field only applies to mutable puts. If there is no current value, the cas field SHOULD be ignored.

When sending a put request to a node that did not return any data for the get, the cas field SHOULD NOT be included.

response

Response:

{
        "r": { "id": <20 byte id of sending node (string)> },
        "t": <transaction-id (string)>,
        "y": "r",
}

errors

If the store fails for any reason an error message is returned instead of the message template above, i.e. one where "y" is "e" and "e" is a tuple of [error-code, message]). Failures include cas mismatches and the sequence number is outdated.

The error message (as specified by BEP5) looks like this:

{
        "e": [ <error-code (integer)>, <error-string (string)> ],
        "t": <transaction-id (string)>,
        "y": "e",
}

In addition to the error codes defined in BEP5, this specification defines some additional error codes.

error-code description
205 message (v field) too big.
206 invalid signature
207 salt (salt field) too big.
301 the CAS hash mismatched, re-read value and try again.
302 sequence number less than current.

An implementation MUST emit 301 errors if the cas mismatches. This is a critical feature in synchronization of multiple agents sharing an immutable item.

get message

Request:

{
        "a":
        {
                "id": <20 byte id of sending node (string)>,
                "target:" <20 byte SHA-1 hash of public key and salt (string)>
        },
        "t": <transaction-id (string)>,
        "y": "q",
        "q": "get"
}

Response:

{
        "r":
        {
                "id": <20 byte id of sending node (string)>,
                "k": <ed25519 public key (32 bytes string)>,
                "nodes": <IPv4 nodes close to 'target'>,
                "nodes6": <IPv6 nodes close to 'target'>,
                "seq": <monotonically increasing sequence number (integer)>,
                "sig": <ed25519 signature (64 bytes string)>,
                "token": <write-token (string)>,
                "v": <any bencoded type, whose encoded size <= 1000>
        },
        "t": <transaction-id (string)>,
        "y": "r",
}

signature verification

In order to make it maximally difficult to attack the bencoding parser, signing and verification of the value and sequence number should be done as follows:

  1. encode value and sequence number separately
  2. concatenate ("4:salt" length-of-salt ":" salt) "3:seqi" seq "e1:v" len ":" and the encoded value. sequence number 1 of value "Hello World!" would be converted to: "3:seqi1e1:v12:Hello World!". In this way it is not possible to convince a node that part of the length is actually part of the sequence number even if the parser contains certain bugs. Furthermore it is not possible to have a verification failure if a bencoding serializer alters the order of entries in the dictionary. The salt is in parenthesis because it is optional. It is only prepended if a non-empty salt is specified in the put request.
  3. sign or verify the concatenated string

On the storage node, the signature MUST be verified before accepting the store command. The data MUST be stored under the SHA-1 hash of the public key (as it appears in the bencoded dict) and the salt (if present).

On the requesting nodes, the key they get back from a get request MUST be verified to hash to the target ID the lookup was made for, as well as verifying the signature. If any of these fail, the response SHOULD be considered invalid.

expiration

Without re-announcement, these items MAY expire in 2 hours. In order to keep items alive, they SHOULD be re-announced once an hour.

Any node that's interested in keeping a blob in the DHT alive may announce it. It would simply repeat the signature for a mutable put without having the private key.

test vectors

test 1 (mutable)

value:

12:Hello World!

buffer being signed:

3:seqi1e1:v12:Hello World!

public key:

77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548

private key:

e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74d
b7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d

target ID:

4a533d47ec9c7d95b1ad75f576cffc641853b750

signature:

305ac8aeb6c9c151fa120f120ea2cfb923564e11552d06a5d856091e5e853cff
1260d3f39e4999684aa92eb73ffd136e6f4f3ecbfda0ce53a1608ecd7ae21f01

test 2 (mutable with salt)

value:

12:Hello World!

salt:

foobar

buffer being signed:

4:salt6:foobar3:seqi1e1:v12:Hello World!

public key:

77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548

private key:

e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74d
b7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d

target ID:

411eba73b6f087ca51a3795d9c8c938d365e32c1

signature:

6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17d
df9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08

test 3 (immutable)

value:

12:Hello World!

target ID:

e5f96f6f38320f0f33959cb4d3d656452117aadb

resources

Libraries that implement ed25519 DSA:

libtorrent-rasterbar-1.1.13/docs/dht_store.rst000066400000000000000000000354761351156116000214370ustar00rootroot00000000000000============================================ BitTorrent extension for arbitrary DHT store ============================================ :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none This is a proposal for an extension to the BitTorrent DHT to allow storing and retrieving of arbitrary data. It supports both storing *immutable* items, where the key is the SHA-1 hash of the data itself, and *mutable* items, where the key is the public key of the key pair used to sign the data. There are two new proposed messages, ``put`` and ``get``. terminology ----------- In this document, a *storage node* refers to the node in the DHT to which an item is being announced and stored on. A *requesting node* refers to a node which makes look-ups in the DHT to find the storage nodes, to request items from them, and possibly re-announce those items to keep them alive. messages -------- The proposed new messages ``get`` and ``put`` are similar to the existing ``get_peers`` and ``announce_peer``. Responses to ``get`` should always include ``nodes`` and ``nodes6``. Those fields have the same semantics as in its ``get_peers`` response. It should also include a write token, ``token``, with the same semantics as int ``get_peers``. The write token MAY be tied specifically to the key which ``get`` requested. i.e. the ``token`` can only be used to store values under that one key. It may also be tied to the node ID and IP address of the requesting node. The ``id`` field in these messages has the same semantics as the standard DHT messages, i.e. the node ID of the node sending the message, to maintain the structure of the DHT network. The ``token`` field also has the same semantics as the standard DHT message ``get_peers`` and ``announce_peer``, when requesting an item and to write an item respectively. The ``k`` field is the 32 byte ed25519 public key, which the signature can be authenticated with. When looking up a mutable item, the ``target`` field MUST be the SHA-1 hash of this key concatenated with the ``salt``, if present. The distinction between storing mutable and immutable items is the inclusion of a public key, a sequence number, signature and an optional salt (``k``, ``seq``, ``sig`` and ``salt``). ``get`` requests for mutable items and immutable items cannot be distinguished from eachother. An implementation can either store mutable and immutable items in the same hash table internally, or in separate ones and potentially do two lookups for ``get`` requests. The ``v`` field is the *value* to be stored. It is allowed to be any bencoded type (list, dict, string or integer). When it's being hashed (for verifying its signature or to calculate its key), its flattened, bencoded, form is used. It is important to use the verbatim bencoded representation as it appeared in the message. decoding and then re-encoding bencoded structures is not necessarily an identity operation. Storing nodes MAY reject ``put`` requests where the bencoded form of ``v`` is longer than 1000 bytes. In other words, it's not safe to assume storing more than 1000 bytes will succeed. immutable items --------------- Immutable items are stored under their SHA-1 hash, and since they cannot be modified, there is no need to authenticate the origin of them. This makes immutable items simple. A node making a lookup SHOULD verify the data it receives from the network, to verify that its hash matches the target that was looked up. put message ........... Request: .. parsed-literal:: { "a": { "id": *<20 byte id of sending node (string)>*, "v": ** }, "t": **, "y": "q", "q": "put" } Response: .. parsed-literal:: { "r": { "id": *<20 byte id of sending node (string)>* }, "t": **, "y": "r", } get message ........... Request: .. parsed-literal:: { "a": { "id": *<20 byte id of sending node (string)>*, "target": **, }, "t": **, "y": "q", "q": "get" } Response: .. parsed-literal:: { "r": { "id": *<20 byte id of sending node (string)>*, "token": **, "v": **, "nodes": **, "nodes6": ** }, "t": **, "y": "r", } mutable items ------------- Mutable items can be updated, without changing their DHT keys. To authenticate that only the original publisher can update an item, it is signed by a private key generated by the original publisher. The target ID mutable items are stored under is the SHA-1 hash of the public key (as it appears in the ``put`` message). In order to avoid a malicious node to overwrite the list head with an old version, the sequence number ``seq`` must be monotonically increasing for each update, and a node hosting the list node MUST not downgrade a list head from a higher sequence number to a lower one, only upgrade. The sequence number SHOULD not exceed ``MAX_INT64``, (i.e. ``0x7fffffffffffffff``. A client MAY reject any message with a sequence number exceeding this. A client MAY also reject any message with a negative sequence number. The signature is a 64 byte ed25519 signature of the bencoded sequence number concatenated with the ``v`` key. e.g. something like this:: 3:seqi4e1:v12:Hello world! If the ``salt`` key is present and non-empty, the salt string must be included in what's signed. Note that if ``salt`` is specified and an empty string, it is as if it was not specified and nothing in addition to the sequence number and the data is signed. The salt string may not be longer than 64 bytes. When a salt is included in what is signed, the key ``salt`` with the value of the key is prepended in its bencoded form. For example, if ``salt`` is "foobar", the buffer to be signed is:: 4:salt6:foobar3:seqi4e1:v12:Hello world! put message ........... Request: .. parsed-literal:: { "a": { "cas": **, "id": *<20 byte id of sending node (string)>*, "k": **, "salt": ** "seq": **, "sig": **, "token": **, "v": ** }, "t": **, "y": "q", "q": "put" } Storing nodes receiving a ``put`` request where ``seq`` is lower than or equal to what's already stored on the node, MUST reject the request. If the sequence number is equal, and the value is also the same, the node SHOULD reset its timeout counter. If the sequence number in the ``put`` message is lower than the sequence number associated with the currently stored value, the storing node MAY return an error message with code 302 (see error codes below). Note that this request does not contain a target hash. The target hash under which this blob is stored is implied by the ``k`` argument. The key is the SHA-1 hash of the key (``k``). In order to support a single key being used to store separate items in the DHT, an optional ``salt`` can be specified in the ``put`` request of mutable items. If the salt entry is not present, it can be assumed to be an empty string, and its semantics should be identical as specifying a salt key with an empty string. The salt can be any binary string (but probably most conveniently a hash of something). This string is appended to the key, as specified in the ``k`` field, when calculating the key to store the blob under (i.e. the key ``get`` requests specify to retrieve this data). This lets a single entity, with a single key, publish any number of unrelated items, with a single key that readers can verify. This is useful if the publisher doesn't know ahead of time how many different items are to be published. It can distribute a single public key for users to authenticate the published blobs. Note that the salt is not returned in the response to a ``get`` request. This is intentional. When issuing a ``get`` request for an item is expected to know what the salt is (because it is part of what the target ID that is being looked up is derived from). There is no need to repeat it back for bystanders to see. CAS ... CAS is short for *compare and swap*, it has similar semantics as CAS CPU instructions. It is used to avoid race conditions when multiple nodes are writing to the same slot in the DHT. The ``cas`` field is optional. If present it specifies the sequence number of the data blob being overwritten by the put. When present, the storing node MUST compare this number to the current sequence number it has stored under this key. Only if the ``cas`` matches the stored sequence number is the put performed. If it mismatches, the store fails and an error is returned. See errors_ below. The ``cas`` field only applies to mutable puts. If there is no current value, the ``cas`` field SHOULD be ignored. When sending a ``put`` request to a node that did not return any data for the ``get``, the ``cas`` field SHOULD NOT be included. response ........ Response: .. parsed-literal:: { "r": { "id": *<20 byte id of sending node (string)>* }, "t": **, "y": "r", } errors ...... If the store fails for any reason an error message is returned instead of the message template above, i.e. one where "y" is "e" and "e" is a tuple of [error-code, message]). Failures include ``cas`` mismatches and the sequence number is outdated. The error message (as specified by BEP5_) looks like this: .. _BEP5: https://www.bittorrent.org/beps/bep_0005.html .. parsed-literal:: { "e": [ **, ** ], "t": **, "y": "e", } In addition to the error codes defined in BEP5_, this specification defines some additional error codes. +------------+-----------------------------+ | error-code | description | +============+=============================+ | 205 | message (``v`` field) | | | too big. | +------------+-----------------------------+ | 206 | invalid signature | +------------+-----------------------------+ | 207 | salt (``salt`` field) | | | too big. | +------------+-----------------------------+ | 301 | the CAS hash mismatched, | | | re-read value and try | | | again. | +------------+-----------------------------+ | 302 | sequence number less than | | | current. | +------------+-----------------------------+ An implementation MUST emit 301 errors if the cas mismatches. This is a critical feature in synchronization of multiple agents sharing an immutable item. get message ........... Request: .. parsed-literal:: { "a": { "id": *<20 byte id of sending node (string)>*, "target:" *<20 byte SHA-1 hash of public key and salt (string)>* }, "t": **, "y": "q", "q": "get" } Response: .. parsed-literal:: { "r": { "id": *<20 byte id of sending node (string)>*, "k": **, "nodes": **, "nodes6": **, "seq": **, "sig": **, "token": **, "v": ** }, "t": **, "y": "r", } signature verification ---------------------- In order to make it maximally difficult to attack the bencoding parser, signing and verification of the value and sequence number should be done as follows: 1. encode value and sequence number separately 2. concatenate ("4:salt" *length-of-salt* ":" *salt*) "3:seqi" *seq* "e1:v" *len* ":" and the encoded value. sequence number 1 of value "Hello World!" would be converted to: "3:seqi1e1:v12:Hello World!". In this way it is not possible to convince a node that part of the length is actually part of the sequence number even if the parser contains certain bugs. Furthermore it is not possible to have a verification failure if a bencoding serializer alters the order of entries in the dictionary. The salt is in parenthesis because it is optional. It is only prepended if a non-empty salt is specified in the ``put`` request. 3. sign or verify the concatenated string On the storage node, the signature MUST be verified before accepting the store command. The data MUST be stored under the SHA-1 hash of the public key (as it appears in the bencoded dict) and the salt (if present). On the requesting nodes, the key they get back from a ``get`` request MUST be verified to hash to the target ID the lookup was made for, as well as verifying the signature. If any of these fail, the response SHOULD be considered invalid. expiration ---------- Without re-announcement, these items MAY expire in 2 hours. In order to keep items alive, they SHOULD be re-announced once an hour. Any node that's interested in keeping a blob in the DHT alive may announce it. It would simply repeat the signature for a mutable put without having the private key. test vectors ------------ test 1 (mutable) ................ value:: 12:Hello World! buffer being signed:: 3:seqi1e1:v12:Hello World! public key:: 77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548 private key:: e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74d b7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d **target ID**:: 4a533d47ec9c7d95b1ad75f576cffc641853b750 **signature**:: 305ac8aeb6c9c151fa120f120ea2cfb923564e11552d06a5d856091e5e853cff 1260d3f39e4999684aa92eb73ffd136e6f4f3ecbfda0ce53a1608ecd7ae21f01 test 2 (mutable with salt) .......................... value:: 12:Hello World! salt:: foobar buffer being signed:: 4:salt6:foobar3:seqi1e1:v12:Hello World! public key:: 77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548 private key:: e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74d b7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d **target ID**:: 411eba73b6f087ca51a3795d9c8c938d365e32c1 **signature**:: 6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17d df9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08 test 3 (immutable) .................. value:: 12:Hello World! **target ID**:: e5f96f6f38320f0f33959cb4d3d656452117aadb resources --------- Libraries that implement ed25519 DSA: * NaCl_ * libsodium_ * `nightcracker's ed25519`_ .. _NaCl: https://nacl.cr.yp.to/ .. _libsodium: https://github.com/jedisct1/libsodium .. _`nightcracker's ed25519`: https://github.com/nightcracker/ed25519 libtorrent-rasterbar-1.1.13/docs/disk_cache.diagram000066400000000000000000000003771351156116000223050ustar00rootroot00000000000000 <---- "recently" "frequently" ---> "used" "used" L1 ghost L1 L2 L2 ghost AAAAAAAAFFFFFFFF FFFFFFFFAAAAAAAA cache size <---------------> 2x cache size <-------------------------------> libtorrent-rasterbar-1.1.13/docs/disk_cache.png000066400000000000000000000071511351156116000214620ustar00rootroot00000000000000‰PNG  IHDRc|3WFL0IDATxśíÝL÷˙đ;ç[Šf–ţTÇ6Di«ŕV tŚ%˘3üQܦcq[2̲?F—ŕČ’©aŮ?$Kć¶,s?Č–€aŮ\ŚÎÜäçfg ±(HŔ„ÓSîóÇeýöË•ľËő¸+íóńw÷~żď}ď{ß«ď^Ź{Ó ĂP-“ş h×®]4MżöÚkRWDHGŹ]µj•L&űî»ď¤®Kb¶pś2RĐ4MÓ´€. n·űŔˇkÚÚÚÚŰŰĄŞĎbüńŤ7&''wíÚ%ňŢ“ˇ…EĆăRŤ5RĐ!†‰Űď2_ýőOýŢ{ď±766ž>}Z„ăĐ·ß~»|ůrŹÇŁ×ë{챫WŻRѵgmmmJJJaaaQQ‘Űív»Ý‹…ÝdµZąą’ł…ĹÇ^Şˇo´yxXüĂY°µ}ăŤ7 nŢĽéóůŔőë×ÍfłÇăÍËËěîî6™LCCCÁ,Ü4.—kĹŠ^ŻW«Őz<†a.^ĽřŇK/ÍŮ]{{űˇC‡Řż§¦¦6mÚôđáĂ@ °yóćţů‡w›KejjĘd2…®!¶gOOONNÎŘŘX__ź\.wą\.—+??źÍ^XXčt:ŃÂÄŰ´<–B)ŠbŁ‘T˝G}ôСC™™™ěâŔŔ@WW—FŁaűűű}>_QQŃÚµkY¸iňňň6mÚÄ~0fddÜż?š]+ ›ÍÖŇҢP(¶oßľrĺJ!L"Äö¤(*??_©T*•ĘŚŚŚ9Ů@Ř\há Ń®©(/aţ‘‚µ„âEhĘĚĚ´X,­­­iiiěšţţţ'NŚŚŚčőúůҸÝîGydN±4M»Ýnżß?==­T*ĂîúđáĂv»]&“}ôŃG•t"·gWWWggç˝{÷îÜą300@Q”L&ž¸}űöźţ6ZXL şl…ąŁÉŽO–ĐMÁPUUe4SRRöíŰç÷űłłłív»Ůl–ËĺĺĺĺăăăÜ4a‹2™L·oßNMMeżEź;wŽ¦éŠŠŠććfš¦?ř੢´ZmFF†\.ňÉ'Ĺű,Ă0ha©đ౏hßłŕ˙x˝Ţ’’’ŽŽ©+"Ťüü|—˵¨»Hňž§XtýőWzzşŐj Ţůaˇ…Eë}Š$a±Xľ˙ţ{­VË#ŻÁ`ňÎđĆ@–‘"ě3? Ź?ţ¸Bˇ¨­­e7qźzőŐWW®\ąmŰ6öA¬ä„Ö*™ż}|öŮgżýö[đ‰‡ĂŃŇŇâp8–-[¶sçÎÝ»wŹŹŹ_˝zuppĐívoÝşUÚÚĆ´^˛IşHÁ>óCQTsss}}˝Çă©ŞŞŞ«« űěĹbY˝zőęիׯ_/YŤă Z/yIýă‹&&&Ö¬Y3>>ŢŰŰ«P(śNgpÓż˙ţ»yóć»wď:‹Ĺ2::ÜtíÚµgžyf||ĽŻŻŹýď ŞĐzŔ$ÉŻ¤Üg~ü~˙Îť;e2™FŁŮşu«V« űěŃhdź2 L|?şxĐz@QŤSDI1¦€!R"!R"!R"!R"!R"Ĺ)˛˛˛Ünwčš;wîÔŐŐĺććF_Hč‹R ‚4»8¸UmjjR«ŐJĄňرcRŐj>‚Ô6QűłcŠü±´´T´×¨s§«LN"7{,Ľ^osssooo__ß§ź~:44$uŤ"ą¶qŢź…ŚĎ?˙üŔŔ@uuuädŻżţzjjjYYYaa!ű޵™™™çž{N.—żüňËlšwß}WĄRĄĄĄŐ×׳k漎-šé*“D”ÍÔjµĂáĐëő:ťŽ=żR×(’(k›,ý9–—[lܸ‘ű†—Ëe6›çËňÇ ŹÇsíÚ5™Lćt:çLE9::ÚŰŰk0†‡‡].—Á`čîîf&33óćÍ›ˇE…ť®2đhv©„­Şßď?xđ`KK‹$UŠ€Gm“§?‹}Gsdd$//O­V›Íf“ÉÄ®d§˘T©TÓÓÓEEEkÖ¬ŃjµV«ŐétR˙˝Ž­¸¸řĚ™3"×466¶wď^»ÝľgĎ©ëBF¬mňôg±#…Z­îéé™čééq8ěĘ9SQ®_żţňĺËŹÇĺr]ąr%++‹˘¨’’’oľůć‡~řä“OFFF¨é*˙ţűo‘Źřńűý••• ĄĄĄR×…,šÚ&OŽ5Rčt:vŽĂłgĎ^ąr…¦iťN×ŐŐEÓôŃŁGąé·lŮb2™ôzý;ďĽc4ĂÎcš““S]]m4łłł÷ďßo4ąŻcŁ8ÓU&•…6»„B«úóĎ?wttlٲ…]ÓÖÖ&uíćZhm“§?KövĽáááÜÜÜ[·nÉĺrI*  „ďĎsH^ÉŰgŽDęĎŃăWRfq†I‹”‘»5¸ş>řˇĺ"T,÷č"ą>’´±‚–zćG°1EŘ5‚l?cčÖĐ.5ßH5ĘEŢ©ŘŕßŃ4…ő‘¤}’Ş?Ď—,!DZĶ[ĹĎv+ď*` –†úKHŕţĽPűHČĆ ýPe˙Že1>ˉléׂ2.R˙‰‡ŚÄ­D1}űŕ˝W7şNt‚áwÉóŹ<đnn C¤2D C¤2D C¤2D C¤2DŠ!ěÄąçÎť«©©Ş4HxF3A¸ÝîÝ»wwvvJ]HLSÄŁS§Ni4š””›Í699ůđáĂĘĘJą\®T*?üđði¨pç666¦ĄĄéőú¦¦¦ůö5g*]Š˘rssišŽ)Ün7ÂçóEY2$âLww·ÉdânşqăĆSO=6 wâÜëׯ›ÍfŹÇ3::š——788vwÜ©t†ůĺ—_ěv{pqvv6?~üäÉ“ ĂDY2$’Xßâ‚c'Ľ]»vmpÍäädMMÍĹ‹ďßżŻÓ馡ţ›8—˘¨ŕÄą]]]Ť†ÝÚßßźžžÎÝ;•®Çă©ŞŞŞ«« [ĄŮŮŮ·ŢzËl68p€Ý{4%C"Á·Ź¸łqăĆË—/łłÚ˛.\¸055uëÖ­óçĎ?xđŔď÷sÓPś‰s333-Ëčč(ű™`łŮÂ3•î<Řżyy9&˘/ "EÜÉÎζŰífłY.————ŹŹŹçç珌Ś<ýôÓ]]]YYYµµµÜ4Ür CUU•ŃhLIIŮ·oźßďç¦áNĄŰÖÖFÓtQQŃ_|AÓô‰'.]şôĺ—_Úl6ö>Eggg4%C‚Áo@†1!R"!R,{cOęZ8<ŕŽćĐ˙RXŇp6‘"*čU‰ g6J¦&\  RD%4^ ĹÎé‚ R,Ć«KÎ @†_I€ ‘Č)€ ‘Č)€ ‘Č)€ ‘Č)€ ‘Č)€ ‘BMMMjµZ©T;vlQw„™ŤAř1 x˝Ţm۶]¸pavv¶  ŕ×_]·nÝ"í 3 0¦€Z­v8z˝^§Ó©TŞ´´4§ÓiµZ§§§'&&,ËđđpŘŚŮ$#Ú ¨0‡ßď?xđ`KK »řÓO?˝řâ‹ŐŐŐ—.] ›3„0¦ĆŘŘŘŢ˝{ívűž={Ř5;věđz˝~żż¸¸8l–°3WVV¦¦¦ŤĆ™™™°i¨˙f6V©Tsf6V©Tż˙ţ{Řݱ3ź9sfľŁťť=|řpzzz}}=2łqä’a)B¤€ßﯬ¬lhh(-- ®üüóĎł˛˛4ÍW_}6f6)I7śI^çĎź=­­­===Ű·oź™™ńů|EEEýýýa3ž libtorrent Examples

libtorrent Examples

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

examples

Except for the example programs in this manual, there's also a bigger example of a (little bit) more complete client, client_test. There are separate instructions for how to use it here if you'd like to try it. Note that building client_test also requires boost.regex and boost.program_options library.

simple client

This is a simple client. It doesn't have much output to keep it simple:

#include <stdlib.h>
#include <boost/make_shared.hpp>
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/torrent_info.hpp"

int main(int argc, char* argv[]) try
{
  if (argc != 2)
  {
    std::cerr << "usage: ./simple_client torrent-file\n"
      "to stop the client, press return.\n";
    return 1;
  }

  lt::session s;
  lt::add_torrent_params p;
  p.save_path = "./";
  lt::error_code ec;
  p.ti = boost::make_shared<lt::torrent_info>(std::string(argv[1]), 0);
  s.add_torrent(p);

  // wait for the user to end
  char a;
  scanf("%c\n", &a);
  return 0;
}
catch (std::exception const& e)
{
  std::cerr << "ERROR: " << e.what() << "\n";
}

make_torrent

Shows how to create a torrent from a directory tree:

#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/file.hpp"
#include "libtorrent/storage.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/create_torrent.hpp"
#include "libtorrent/file.hpp"
#include "libtorrent/file_pool.hpp"
#include "libtorrent/hex.hpp" // for from_hex

#include <boost/bind.hpp>
#include <fstream>

#ifdef TORRENT_WINDOWS
#include <direct.h> // for _getcwd
#endif

std::vector<char> load_file(std::string const& filename)
{
  std::vector<char> ret;
  std::fstream in;
  in.exceptions(std::ifstream::failbit);
  in.open(filename.c_str(), std::ios_base::in | std::ios_base::binary);
  in.seekg(0, std::ios_base::end);
  size_t const size = in.tellg();
  in.seekg(0, std::ios_base::beg);
  ret.resize(size);
  in.read(ret.data(), ret.size());
  return ret;
}

std::string branch_path(std::string const& f)
{
  if (f.empty()) return f;

#ifdef TORRENT_WINDOWS
  if (f == "\\\\") return "";
#endif
  if (f == "/") return "";

  int len = f.size();
  // if the last character is / or \ ignore it
  if (f[len-1] == '/' || f[len-1] == '\\') --len;
  while (len > 0)
  {
    --len;
    if (f[len] == '/' || f[len] == '\\')
      break;
  }

  if (f[len] == '/' || f[len] == '\\') ++len;
  return std::string(f.c_str(), len);
}

// do not include files and folders whose
// name starts with a .
bool file_filter(std::string const& f)
{
  if (f.empty()) return false;

  char const* first = f.c_str();
  char const* sep = strrchr(first, '/');
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
  char const* altsep = strrchr(first, '\\');
  if (sep == NULL || altsep > sep) sep = altsep;
#endif
  // if there is no parent path, just set 'sep'
  // to point to the filename.
  // if there is a parent path, skip the '/' character
  if (sep == NULL) sep = first;
  else ++sep;

  // return false if the first character of the filename is a .
  if (sep[0] == '.') return false;

  fprintf(stderr, "%s\n", f.c_str());
  return true;
}

void print_progress(int i, int num)
{
  fprintf(stderr, "\r%d/%d", i+1, num);
}

void print_usage()
{
  fputs("usage: make_torrent FILE [OPTIONS]\n"
    "\n"
    "Generates a torrent file from the specified file\n"
    "or directory and writes it to standard out\n\n"
    "OPTIONS:\n"
    "-m file       generate a merkle hash tree torrent.\n"
    "              merkle torrents require client support\n"
    "              the resulting full merkle tree is written to\n"
    "              the specified file\n"
    "-w url        adds a web seed to the torrent with\n"
    "              the specified url\n"
    "-t url        adds the specified tracker to the\n"
    "              torrent. For multiple trackers, specify more\n"
    "              -t options\n"
    "-c comment    sets the comment to the specified string\n"
    "-C creator    sets the created-by field to the specified string\n"
    "-p bytes      enables padding files. Files larger\n"
    "              than bytes will be piece-aligned\n"
    "-s bytes      specifies a piece size for the torrent\n"
    "              This has to be a multiple of 16 kiB\n"
    "-l            Don't follow symlinks, instead encode them as\n"
    "              links in the torrent file\n"
    "-o file       specifies the output filename of the torrent file\n"
    "              If this is not specified, the torrent file is\n"
    "              printed to the standard out, except on windows\n"
    "              where the filename defaults to a.torrent\n"
    "-r file       add root certificate to the torrent, to verify\n"
    "              the HTTPS tracker\n"
    "-S info-hash  add a similar torrent by info-hash. The similar\n"
    "              torrent is expected to share some files with this one\n"
    "-L collection add a collection name to this torrent. Other torrents\n"
    "              in the same collection is expected to share files\n"
    "              with this one.\n"
    "-M            make the torrent compatible with mutable torrents\n"
    "              this means aligning large files and pad them in order\n"
    "              for piece hashes to uniquely indentify a file without\n"
    "              overlap\n"
    , stderr);
}

int main(int argc, char* argv[]) try
{
  std::string creator_str = "libtorrent";
  std::string comment_str;

  if (argc < 2) {
    print_usage();
    return 1;
  }

  std::vector<std::string> web_seeds;
  std::vector<std::string> trackers;
  std::vector<std::string> collections;
  std::vector<lt::sha1_hash> similar;
  int pad_file_limit = -1;
  int piece_size = 0;
  int flags = 0;
  std::string root_cert;

  std::string outfile;
  std::string merklefile;
#ifdef TORRENT_WINDOWS
  // don't ever write binary data to the console on windows
  // it will just be interpreted as text and corrupted
  outfile = "a.torrent";
#endif

  std::string full_path = argv[1];
  argv += 2;
  argc -= 2;

  for (; argc > 0; --argc, ++argv) {
    if (argv[0][0] != '-') {
      print_usage();
      return 1;
    }

    char const flag = argv[0][1];

    switch (flag)
    {
      case 'M':
        flags |= lt::create_torrent::mutable_torrent_support;
        pad_file_limit = 0x4000;
        continue;
      case 'l':
        flags |= lt::create_torrent::symlinks;
        continue;
    }

    if (argc < 2) {
      print_usage();
      return 1;
    }

    switch (flag)
    {
      case 'w': web_seeds.push_back(argv[1]); break;
      case 't': trackers.push_back(argv[1]); break;
      case 's': piece_size = atoi(argv[1]); break;
      case 'o': outfile = argv[1]; break;
      case 'C': creator_str = argv[1]; break;
      case 'c': comment_str = argv[1]; break;
      case 'r': root_cert = argv[1]; break;
      case 'L': collections.push_back(argv[1]); break;
      case 'p':
        pad_file_limit = atoi(argv[1]);
        flags |= lt::create_torrent::optimize_alignment;
        break;
      case 'm':
        merklefile = argv[1];
        flags |= lt::create_torrent::merkle;
        break;
      case 'S':
        {
        if (strlen(argv[1]) != 40)
        {
          std::cerr << "invalid info-hash for -S. "
            "Expected 40 hex characters\n";
          print_usage();
          return 1;
        }
        lt::sha1_hash ih;
        if (!lt::from_hex(argv[1], 40, ih.data()))
        {
          std::cerr << "invalid info-hash for -S\n";
          print_usage();
          return 1;
        }
        similar.push_back(ih);
        }
        break;
      default:
        print_usage();
        return 1;
    }
    ++argv;
    --argc;
  }

  lt::file_storage fs;
#ifdef TORRENT_WINDOWS
  if (full_path[1] != ':')
#else
  if (full_path[0] != '/')
#endif
  {
    char cwd[TORRENT_MAX_PATH];
#ifdef TORRENT_WINDOWS
    _getcwd(cwd, sizeof(cwd));
    full_path = cwd + ("\\" + full_path);
#else
    getcwd(cwd, sizeof(cwd));
    full_path = cwd + ("/" + full_path);
#endif
  }

  lt::add_files(fs, full_path, file_filter, flags);
  if (fs.num_files() == 0) {
    std::cerr << "no files specified.\n";
    return 1;
  }

  lt::create_torrent t(fs, piece_size, pad_file_limit, flags);
  int tier = 0;
  for (std::vector<std::string>::iterator i = trackers.begin()
    , end(trackers.end()); i != end; ++i, ++tier)
    t.add_tracker(*i, tier);

  for (std::vector<std::string>::iterator i = web_seeds.begin()
    , end(web_seeds.end()); i != end; ++i)
    t.add_url_seed(*i);

  for (std::vector<std::string>::iterator i = collections.begin()
    , end(collections.end()); i != end; ++i)
    t.add_collection(*i);

  for (std::vector<lt::sha1_hash>::iterator i = similar.begin()
    , end(similar.end()); i != end; ++i)
    t.add_similar_torrent(*i);

  lt::error_code ec;
  set_piece_hashes(t, branch_path(full_path)
    , boost::bind(&print_progress, _1, t.num_pieces()), ec);
  if (ec) {
    std::cerr << ec.message() << "\n";
    return 1;
  }

  fprintf(stderr, "\n");
  t.set_creator(creator_str.c_str());
  if (!comment_str.empty()) {
    t.set_comment(comment_str.c_str());
  }

  if (!root_cert.empty()) {
    std::vector<char> const pem = load_file(root_cert);
    t.set_root_cert(std::string(&pem[0], pem.size()));
  }

  // create the torrent and print it to stdout
  std::vector<char> torrent;
  lt::bencode(back_inserter(torrent), t.generate());
  if (!outfile.empty()) {
    std::fstream out;
    out.exceptions(std::ifstream::failbit);
    out.open(outfile.c_str(), std::ios_base::out | std::ios_base::binary);
    out.write(&torrent[0], torrent.size());
  }
  else {
    std::cout.write(&torrent[0], torrent.size());
  }

  if (!merklefile.empty()) {
    std::fstream merkle;
    merkle.exceptions(std::ifstream::failbit);
    merkle.open(merklefile.c_str(), std::ios_base::out | std::ios_base::binary);
    merkle.write(reinterpret_cast<char const*>(&t.merkle_tree()[0]), t.merkle_tree().size() * 20);
  }
  return 0;
}
catch (std::exception& e) {
  std::cerr << "ERROR: " << e.what() << "\n";
  return 1;
}

dump_torrent

This is an example of a program that will take a torrent-file as a parameter and print information about it to std out:

#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/announce_entry.hpp"
#include "libtorrent/bdecode.hpp"
#include "libtorrent/magnet_uri.hpp"

#include <fstream>

std::vector<char> load_file(std::string const& filename)
{
  std::vector<char> ret;
  std::fstream in;
  in.exceptions(std::ifstream::failbit);
  in.open(filename.c_str(), std::ios_base::in | std::ios_base::binary);
  in.seekg(0, std::ios_base::end);
  size_t const size = in.tellg();
  in.seekg(0, std::ios_base::beg);
  ret.resize(size);
  in.read(ret.data(), ret.size());
  return ret;
}

int main(int argc, char* argv[]) try
{
  if (argc < 2 || argc > 4) {
    fputs("usage: dump_torrent torrent-file [total-items-limit] [recursion-limit]\n", stderr);
    return 1;
  }

  int item_limit = 1000000;
  int depth_limit = 1000;

  if (argc > 2) item_limit = atoi(argv[2]);
  if (argc > 3) depth_limit = atoi(argv[3]);

  std::vector<char> buf = load_file(argv[1]);
  lt::bdecode_node e;
  int pos = -1;
  lt::error_code ec;
  std::cout << "decoding. recursion limit: " << depth_limit
    << " total item count limit: " << item_limit << "\n";
  int const ret = lt::bdecode(&buf[0], &buf[0] + buf.size(), e, ec, &pos
    , depth_limit, item_limit);

  printf("\n\n----- raw info -----\n\n%s\n", print_entry(e).c_str());

  if (ret != 0) {
    std::cerr << "failed to decode: '" << ec.message() << "' at character: " << pos<< "\n";
    return 1;
  }

  lt::torrent_info const t(e);
  e.clear();
  std::vector<char>().swap(buf);

  // print info about torrent
  printf("\n\n----- torrent file info -----\n\n"
    "nodes:\n");

  typedef std::vector<std::pair<std::string, int> > node_vec;
  node_vec const& nodes = t.nodes();
  for (node_vec::const_iterator i = nodes.begin(), end(nodes.end());
    i != end; ++i)
  {
    printf("%s: %d\n", i->first.c_str(), i->second);
  }
  puts("trackers:\n");
  for (std::vector<lt::announce_entry>::const_iterator i = t.trackers().begin();
    i != t.trackers().end(); ++i)
  {
    printf("%2d: %s\n", i->tier, i->url.c_str());
  }

  char ih[41];
  lt::to_hex(t.info_hash().data(), 20, ih);
  printf("number of pieces: %d\n"
    "piece length: %d\n"
    "info hash: %s\n"
    "comment: %s\n"
    "created by: %s\n"
    "magnet link: %s\n"
    "name: %s\n"
    "number of files: %d\n"
    "files:\n"
    , t.num_pieces()
    , t.piece_length()
    , ih
    , t.comment().c_str()
    , t.creator().c_str()
    , make_magnet_uri(t).c_str()
    , t.name().c_str()
    , t.num_files());
  lt::file_storage const& st = t.files();
  for (int i = 0; i < st.num_files(); ++i)
  {
    int const first = st.map_file(i, 0, 0).piece;
    int const last = st.map_file(i, (std::max)(boost::int64_t(st.file_size(i))-1, boost::int64_t(0)), 0).piece;
    int const flags = st.file_flags(i);
    printf(" %8" PRIx64 " %11" PRId64 " %c%c%c%c [ %5d, %5d ] %7u %s %s %s%s\n"
      , st.file_offset(i)
      , st.file_size(i)
      , ((flags & lt::file_storage::flag_pad_file)?'p':'-')
      , ((flags & lt::file_storage::flag_executable)?'x':'-')
      , ((flags & lt::file_storage::flag_hidden)?'h':'-')
      , ((flags & lt::file_storage::flag_symlink)?'l':'-')
      , first, last
      , boost::uint32_t(st.mtime(i))
      , st.hash(i) != lt::sha1_hash(0) ? lt::to_hex(st.hash(i).to_string()).c_str() : ""
      , st.file_path(i).c_str()
      , (flags & lt::file_storage::flag_symlink) ? "-> " : ""
      , (flags & lt::file_storage::flag_symlink) ? st.symlink(i).c_str() : "");
  }

  return 0;
}
catch (std::exception const& e)
{
  std::cerr << "ERROR: " << e.what() << "\n";
}
libtorrent-rasterbar-1.1.13/docs/examples.rst000066400000000000000000000021621351156116000212440ustar00rootroot00000000000000=================== libtorrent Examples =================== :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none examples ======== Except for the example programs in this manual, there's also a bigger example of a (little bit) more complete client, ``client_test``. There are separate instructions for how to use it here__ if you'd like to try it. Note that building ``client_test`` also requires boost.regex and boost.program_options library. __ client_test.html simple client ------------- This is a simple client. It doesn't have much output to keep it simple: .. include:: ../examples/simple_client.cpp :code: c++ :tab-width: 2 :start-after: */ make_torrent ------------ Shows how to create a torrent from a directory tree: .. include:: ../examples/make_torrent.cpp :code: c++ :tab-width: 2 :start-after: */ dump_torrent ------------ This is an example of a program that will take a torrent-file as a parameter and print information about it to std out: .. include:: ../examples/dump_torrent.cpp :code: c++ :tab-width: 2 :start-after: */ libtorrent-rasterbar-1.1.13/docs/extension_protocol.html000066400000000000000000000444411351156116000235250ustar00rootroot00000000000000 extension_protocol.rst
Author: Arvid Norberg, arvid@libtorrent.org Ludvig Strigeus, ludde@utorrent.com

extension protocol for bittorrent

The intention of this protocol is to provide a simple and thin transport for extensions to the bittorrent protocol. Supporting this protocol makes it easy to add new extensions without interfering with the standard bittorrent protocol or clients that don't support this extension or the one you want to add.

To advertise to other clients that you support, one bit from the reserved bytes is used.

The bit selected for the extension protocol is bit 20 from the right (counting starts at 0). So (reserved_byte[5] & 0x10) is the expression to use for checking if the client supports extended messaging.

Once support for the protocol is established, the client is supposed to support 1 new message:

name id
extended 20

This message is sent as any other bittorrent message, with a 4 byte length prefix and a single byte identifying the message (the single byte being 20 in this case). At the start of the payload of the message, is a single byte message identifier. This identifier can refer to different extension messages and only one ID is specified, 0. If the ID is 0, the message is a handshake message which is described below. The layout of a general extended message follows (including the message headers used by the bittorrent protocol):

size description
uint32_t length prefix. Specifies the number of bytes for the entire message. (Big endian)
uint8_t bittorrent message ID, = 20
uint8_t extended message ID. 0 = handshake, >0 = extended message as specified by the handshake.

handshake message

The payload of the handshake message is a bencoded dictionary. All items in the dictionary are optional. Any unknown names should be ignored by the client. All parts of the dictionary are case sensitive. This is the defined item in the dictionary:

name description
m

Dictionary of supported extension messages which maps names of extensions to an extended message ID for each extension message. The only requirement on these IDs is that no extension message share the same one. Setting an extension number to zero means that the extension is not supported/disabled. The client should ignore any extension names it doesn't recognize.

The extension message IDs are the IDs used to send the extension messages to the peer sending this handshake. i.e. The IDs are local to this particular peer.

Here are some other items that an implementation may choose to support:

name description
p Local TCP listen port. Allows each side to learn about the TCP port number of the other side. Note that there is no need for the receiving side of the connection to send this extension message, since its port number is already known.
v Client name and version (as a utf-8 string). This is a much more reliable way of identifying the client than relying on the peer id encoding.
yourip A string containing the compact representation of the ip address this peer sees you as. i.e. this is the receiver's external ip address (no port is included). This may be either an IPv4 (4 bytes) or an IPv6 (16 bytes) address.
ipv6 If this peer has an IPv6 interface, this is the compact representation of that address (16 bytes). The client may prefer to connect back via the IPv6 address.
ipv4 If this peer has an IPv4 interface, this is the compact representation of that address (4 bytes). The client may prefer to connect back via this interface.
reqq An integer, the number of outstanding request messages this client supports without dropping any. The default in in libtorrent is 250.

The handshake dictionary could also include extended handshake information, such as support for encrypted headers or anything imaginable.

An example of what the payload of a handshake message could look like:

Dictionary
m
Dictionary
LT_metadata 1
ut_pex 2
p 6881
v "uTorrent 1.2"

and in the encoded form:

d1:md11:LT_metadatai1e6:ut_pexi2ee1:pi6881e1:v12:uTorrent 1.2e

To make sure the extension names do not collide by mistake, they should be prefixed with the two (or one) character code that is used to identify the client that introduced the extension. This applies for both the names of extension messages, and for any additional information put inside the top-level dictionary. All one and two byte identifiers are invalid to use unless defined by this specification.

This message should be sent immediately after the standard bittorrent handshake to any peer that supports this extension protocol. It is valid to send the handshake message more than once during the lifetime of a connection, the sending client should not be disconnected. An implementation may choose to ignore the subsequent handshake messages (or parts of them).

Subsequent handshake messages can be used to enable/disable extensions without restarting the connection. If a peer supports changing extensions at run time, it should note that the m dictionary is additive. It's enough that it contains the actual CHANGES to the extension list. To disable the support for LT_metadata at run-time, without affecting any other extensions, this message should be sent: d11:LT_metadatai0ee. As specified above, the value 0 is used to turn off an extension.

The extension IDs must be stored for every peer, becuase every peer may have different IDs for the same extension.

This specification, deliberately, does not specify any extensions such as peer-exchange or metadata exchange. This protocol is merely a transport for the actual extensions to the bittorrent protocol and the extensions named in the example above (such as p) are just examples of possible extensions.

rationale

The reason why the extension messages' IDs would be defined in the handshake is to avoid having a global registry of message IDs. Instead the names of the extension messages requires unique names, which is much easier to do without a global registry. The convention is to use a two letter prefix on the extension message names, the prefix would identify the client first implementing the extension message. e.g. LT_metadata is implemented by libtorrent, and hence it has the LT prefix.

If the client supporting the extensions can decide which numbers the messages it receives will have, it means they are constants within that client. i.e. they can be used in switch statements. It's easy for the other end to store an array with the ID's we expect for each message and use that for lookups each time it sends an extension message.

The reason for having a dictionary instead of having an array (using implicitly assigned index numbers to the extensions) is that if a client want to disable some extensions, the ID numbers would change, and it wouldn't be able to use constants (and hence, not use them in a switch). If the messages IDs would map directly to bittorrent message IDs, It would also make it possible to map extensions in the handshake to existing extensions with fixed message IDs.

The reasoning behind having a single byte as extended message identifier is to follow the the bittorrent spec. with its single byte message identifiers. It is also considered to be enough. It won't limit the total number of extensions, only the number of extensions used simultaneously.

The reason for using single byte identifiers for the standardized handshake identifiers is 1) The mainline DHT uses single byte identifiers. 2) Saves bandwidth. The only advantage of longer messages is that it makes the protocol more readable for a human, but the BT protocol wasn't designed to be a human readable protocol, so why bother.

extensions

These extensions all operates within the extension protocol. The name of the extension is the name used in the extension-list packets, and the payload is the data in the extended message (not counting the length-prefix, message-id nor extension-id).

Note that since this protocol relies on one of the reserved bits in the handshake, it may be incompatible with future versions of the mainline bittorrent client.

These are the extensions that are currently implemented.

metadata from peers

Extension name: "LT_metadata"

Note

This extension is deprecated in favor of the more widely supported ut_metadata extension, see BEP 9.

The point with this extension is that you don't have to distribute the metadata (.torrent-file) separately. The metadata can be distributed through the bittorrent swarm. The only thing you need to download such a torrent is the tracker url and the info-hash of the torrent.

It works by assuming that the initial seeder has the metadata and that the metadata will propagate through the network as more peers join.

There are three kinds of messages in the metadata extension. These packets are put as payload to the extension message. The three packets are:

  • request metadata
  • metadata
  • don't have metadata

request metadata:

size name description
uint8_t msg_type Determines the kind of message this is 0 means 'request metadata'
uint8_t start The start of the metadata block that is requested. It is given in 256:ths of the total size of the metadata, since the requesting client don't know the size of the metadata.
uint8_t size The size of the metadata block that is requested. This is also given in 256:ths of the total size of the metadata. The size is given as size-1. That means that if this field is set 0, the request wants one 256:th of the metadata.

metadata:

size name description
uint8_t msg_type 1 means 'metadata'
int32_t total_size The total size of the metadata, given in number of bytes.
int32_t offset The offset of where the metadata block in this message belongs in the final metadata. This is given in bytes.
uint8_t[] metadata The actual metadata block. The size of this part is given implicit by the length prefix in the bittorrent protocol packet.

Don't have metadata:

size name description
uint8_t msg_type 2 means 'I don't have metadata'. This message is sent as a reply to a metadata request if the the client doesn't have any metadata.

dont_have

Extension name: "lt_donthave"

The dont_have extension message is used to tell peers that the client no longer has a specific piece. The extension message should be advertised in the m dictionary as lt_donthave. The message format mimics the regular HAVE bittorrent message.

Just like all extension messages, the first 2 bytes in the mssage itself are 20 (the bittorrent extension message) and the message ID assigned to this extension in the m dictionary in the handshake.

size name description
uint32_t piece index of the piece the peer no longer has.

The length of this message (including the extension message prefix) is 6 bytes, i.e. one byte longer than the normal HAVE message, because of the extension message wrapping.

libtorrent-rasterbar-1.1.13/docs/extension_protocol.rst000066400000000000000000000403441351156116000233670ustar00rootroot00000000000000:Author: Arvid Norberg, arvid@libtorrent.org Ludvig Strigeus, ludde@utorrent.com extension protocol for bittorrent ================================= The intention of this protocol is to provide a simple and thin transport for extensions to the bittorrent protocol. Supporting this protocol makes it easy to add new extensions without interfering with the standard bittorrent protocol or clients that don't support this extension or the one you want to add. To advertise to other clients that you support, one bit from the reserved bytes is used. The bit selected for the extension protocol is bit 20 from the right (counting starts at 0). So (reserved_byte[5] & 0x10) is the expression to use for checking if the client supports extended messaging. Once support for the protocol is established, the client is supposed to support 1 new message: +------------------------+----+ |name | id | +========================+====+ |``extended`` | 20 | +------------------------+----+ This message is sent as any other bittorrent message, with a 4 byte length prefix and a single byte identifying the message (the single byte being 20 in this case). At the start of the payload of the message, is a single byte message identifier. This identifier can refer to different extension messages and only one ID is specified, 0. If the ID is 0, the message is a handshake message which is described below. The layout of a general ``extended`` message follows (including the message headers used by the bittorrent protocol): +----------+---------------------------------------------------------+ | size | description | +==========+=========================================================+ | uint32_t | length prefix. Specifies the number of bytes for the | | | entire message. (Big endian) | +----------+---------------------------------------------------------+ | uint8_t | bittorrent message ID, = 20 | +----------+---------------------------------------------------------+ | uint8_t | extended message ID. 0 = handshake, >0 = extended | | | message as specified by the handshake. | +----------+---------------------------------------------------------+ handshake message ----------------- The payload of the handshake message is a bencoded dictionary. All items in the dictionary are optional. Any unknown names should be ignored by the client. All parts of the dictionary are case sensitive. This is the defined item in the dictionary: +-------+-----------------------------------------------------------+ | name | description | +=======+===========================================================+ | m | Dictionary of supported extension messages which maps | | | names of extensions to an extended message ID for each | | | extension message. The only requirement on these IDs | | | is that no extension message share the same one. Setting | | | an extension number to zero means that the extension is | | | not supported/disabled. The client should ignore any | | | extension names it doesn't recognize. | | | | | | The extension message IDs are the IDs used to send the | | | extension messages to the peer sending this handshake. | | | i.e. The IDs are local to this particular peer. | +-------+-----------------------------------------------------------+ Here are some other items that an implementation may choose to support: +--------+-----------------------------------------------------------+ | name | description | +========+===========================================================+ | p | Local TCP listen port. Allows each side to learn about | | | the TCP port number of the other side. Note that there is | | | no need for the receiving side of the connection to send | | | this extension message, since its port number is already | | | known. | +--------+-----------------------------------------------------------+ | v | Client name and version (as a utf-8 string). | | | This is a much more reliable way of identifying the | | | client than relying on the peer id encoding. | +--------+-----------------------------------------------------------+ | yourip | A string containing the compact representation of the ip | | | address this peer sees you as. i.e. this is the | | | receiver's external ip address (no port is included). | | | This may be either an IPv4 (4 bytes) or an IPv6 | | | (16 bytes) address. | +--------+-----------------------------------------------------------+ | ipv6 | If this peer has an IPv6 interface, this is the compact | | | representation of that address (16 bytes). The client may | | | prefer to connect back via the IPv6 address. | +--------+-----------------------------------------------------------+ | ipv4 | If this peer has an IPv4 interface, this is the compact | | | representation of that address (4 bytes). The client may | | | prefer to connect back via this interface. | +--------+-----------------------------------------------------------+ | reqq | An integer, the number of outstanding request messages | | | this client supports without dropping any. The default in | | | in libtorrent is 250. | +--------+-----------------------------------------------------------+ The handshake dictionary could also include extended handshake information, such as support for encrypted headers or anything imaginable. An example of what the payload of a handshake message could look like: +------------------------------------------------------+ | Dictionary | +===================+==================================+ | ``m`` | +--------------------------+ | | | | Dictionary | | | | +======================+===+ | | | | ``LT_metadata`` | 1 | | | | +----------------------+---+ | | | | ``ut_pex`` | 2 | | | | +----------------------+---+ | | | | +-------------------+----------------------------------+ | ``p`` | 6881 | +-------------------+----------------------------------+ | ``v`` | "uTorrent 1.2" | +-------------------+----------------------------------+ and in the encoded form: ``d1:md11:LT_metadatai1e6:ut_pexi2ee1:pi6881e1:v12:uTorrent 1.2e`` To make sure the extension names do not collide by mistake, they should be prefixed with the two (or one) character code that is used to identify the client that introduced the extension. This applies for both the names of extension messages, and for any additional information put inside the top-level dictionary. All one and two byte identifiers are invalid to use unless defined by this specification. This message should be sent immediately after the standard bittorrent handshake to any peer that supports this extension protocol. It is valid to send the handshake message more than once during the lifetime of a connection, the sending client should not be disconnected. An implementation may choose to ignore the subsequent handshake messages (or parts of them). Subsequent handshake messages can be used to enable/disable extensions without restarting the connection. If a peer supports changing extensions at run time, it should note that the ``m`` dictionary is additive. It's enough that it contains the actual *CHANGES* to the extension list. To disable the support for ``LT_metadata`` at run-time, without affecting any other extensions, this message should be sent: ``d11:LT_metadatai0ee``. As specified above, the value 0 is used to turn off an extension. The extension IDs must be stored for every peer, becuase every peer may have different IDs for the same extension. This specification, deliberately, does not specify any extensions such as peer-exchange or metadata exchange. This protocol is merely a transport for the actual extensions to the bittorrent protocol and the extensions named in the example above (such as ``p``) are just examples of possible extensions. rationale --------- The reason why the extension messages' IDs would be defined in the handshake is to avoid having a global registry of message IDs. Instead the names of the extension messages requires unique names, which is much easier to do without a global registry. The convention is to use a two letter prefix on the extension message names, the prefix would identify the client first implementing the extension message. e.g. ``LT_metadata`` is implemented by libtorrent, and hence it has the ``LT`` prefix. If the client supporting the extensions can decide which numbers the messages it receives will have, it means they are constants within that client. i.e. they can be used in ``switch`` statements. It's easy for the other end to store an array with the ID's we expect for each message and use that for lookups each time it sends an extension message. The reason for having a dictionary instead of having an array (using implicitly assigned index numbers to the extensions) is that if a client want to disable some extensions, the ID numbers would change, and it wouldn't be able to use constants (and hence, not use them in a ``switch``). If the messages IDs would map directly to bittorrent message IDs, It would also make it possible to map extensions in the handshake to existing extensions with fixed message IDs. The reasoning behind having a single byte as extended message identifier is to follow the the bittorrent spec. with its single byte message identifiers. It is also considered to be enough. It won't limit the total number of extensions, only the number of extensions used simultaneously. The reason for using single byte identifiers for the standardized handshake identifiers is 1) The mainline DHT uses single byte identifiers. 2) Saves bandwidth. The only advantage of longer messages is that it makes the protocol more readable for a human, but the BT protocol wasn't designed to be a human readable protocol, so why bother. extensions ========== These extensions all operates within the `extension protocol`_. The name of the extension is the name used in the extension-list packets, and the payload is the data in the extended message (not counting the length-prefix, message-id nor extension-id). .. _`extension protocol`: extension_protocol.html Note that since this protocol relies on one of the reserved bits in the handshake, it may be incompatible with future versions of the mainline bittorrent client. These are the extensions that are currently implemented. metadata from peers ------------------- Extension name: "LT_metadata" .. note:: This extension is deprecated in favor of the more widely supported ``ut_metadata`` extension, see `BEP 9`_. The point with this extension is that you don't have to distribute the metadata (.torrent-file) separately. The metadata can be distributed through the bittorrent swarm. The only thing you need to download such a torrent is the tracker url and the info-hash of the torrent. It works by assuming that the initial seeder has the metadata and that the metadata will propagate through the network as more peers join. There are three kinds of messages in the metadata extension. These packets are put as payload to the extension message. The three packets are: * request metadata * metadata * don't have metadata request metadata: +-----------+---------------+----------------------------------------+ | size | name | description | +===========+===============+========================================+ | uint8_t | msg_type | Determines the kind of message this is | | | | 0 means 'request metadata' | +-----------+---------------+----------------------------------------+ | uint8_t | start | The start of the metadata block that | | | | is requested. It is given in 256:ths | | | | of the total size of the metadata, | | | | since the requesting client don't know | | | | the size of the metadata. | +-----------+---------------+----------------------------------------+ | uint8_t | size | The size of the metadata block that is | | | | requested. This is also given in | | | | 256:ths of the total size of the | | | | metadata. The size is given as size-1. | | | | That means that if this field is set | | | | 0, the request wants one 256:th of the | | | | metadata. | +-----------+---------------+----------------------------------------+ metadata: +-----------+---------------+----------------------------------------+ | size | name | description | +===========+===============+========================================+ | uint8_t | msg_type | 1 means 'metadata' | +-----------+---------------+----------------------------------------+ | int32_t | total_size | The total size of the metadata, given | | | | in number of bytes. | +-----------+---------------+----------------------------------------+ | int32_t | offset | The offset of where the metadata block | | | | in this message belongs in the final | | | | metadata. This is given in bytes. | +-----------+---------------+----------------------------------------+ | uint8_t[] | metadata | The actual metadata block. The size of | | | | this part is given implicit by the | | | | length prefix in the bittorrent | | | | protocol packet. | +-----------+---------------+----------------------------------------+ Don't have metadata: +-----------+---------------+----------------------------------------+ | size | name | description | +===========+===============+========================================+ | uint8_t | msg_type | 2 means 'I don't have metadata'. | | | | This message is sent as a reply to a | | | | metadata request if the the client | | | | doesn't have any metadata. | +-----------+---------------+----------------------------------------+ .. _`BEP 9`: https://bittorrent.org/beps/bep_0009.html dont_have --------- Extension name: "lt_donthave" The ``dont_have`` extension message is used to tell peers that the client no longer has a specific piece. The extension message should be advertised in the ``m`` dictionary as ``lt_donthave``. The message format mimics the regular ``HAVE`` bittorrent message. Just like all extension messages, the first 2 bytes in the mssage itself are 20 (the bittorrent extension message) and the message ID assigned to this extension in the ``m`` dictionary in the handshake. +-----------+---------------+----------------------------------------+ | size | name | description | +===========+===============+========================================+ | uint32_t | piece | index of the piece the peer no longer | | | | has. | +-----------+---------------+----------------------------------------+ The length of this message (including the extension message prefix) is 6 bytes, i.e. one byte longer than the normal ``HAVE`` message, because of the extension message wrapping. libtorrent-rasterbar-1.1.13/docs/features.html000066400000000000000000000513101351156116000213770ustar00rootroot00000000000000 libtorrent manual

libtorrent manual

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

introduction

libtorrent is a feature complete C++ bittorrent implementation focusing on efficiency and scalability. It runs on embedded devices as well as desktops. It boasts a well documented library interface that is easy to use. It comes with a simple bittorrent client demonstrating the use of the library.

features

libtorrent is an ongoing project under active development. Its current state supports and includes the following features:

extensions

  • plugin interface for implementing custom bittorrent extensions without having to modify libtorrent
  • supports trackerless torrents (using the Mainline kademlia DHT protocol) with some DHT extensions. BEP 5.
  • supports the bittorrent extension protocol. See extensions. BEP 10.
  • supports the uTorrent metadata transfer protocol BEP 9 (i.e. magnet links).
  • supports the uTorrent peer exchange protocol (PEX).
  • supports local peer discovery (multicasts for peers on the same local network)
  • multitracker extension support (supports both strict BEP 12 and the uTorrent interpretation).
  • tracker scrapes
  • supports lt_trackers extension, to exchange trackers between peers
  • HTTP seeding, as specified in BEP 17 and BEP 19.
  • supports the udp-tracker protocol. (BEP 15).
  • supports the no_peer_id=1 extension that will ease the load off trackers.
  • supports the compact=1 tracker parameter.
  • super seeding/initial seeding (BEP 16).
  • private torrents (BEP 27).
  • upload-only extension (BEP 21).
  • support for IPv6, including BEP 7 and BEP 24.
  • support for merkle hash tree torrents. This makes the size of torrent files scale well with the size of the content.
  • share-mode. This is a special mode torrents can be put in to optimize share ratio rather than downloading the torrent.

disk management

  • can use multipled disk I/O threads to not have the disk block network or client interaction.
  • supports verifying the SHA-1 hash of pieces in multiple threads, to take advantage of multi core machines.
  • supports files > 2 gigabytes.
  • fast resume support, a way to avoid the costly piece check at the start of a resumed torrent. Saves the storage state, piece_picker state as well as all local peers in a fast-resume file.
  • has an adjustable read and write disk cache for improved disk throughput.
  • queues torrents for file check, instead of checking all of them in parallel.
  • does not have any requirements on the piece order in a torrent that it resumes. This means it can resume a torrent downloaded by any client.
  • seed mode, where the files on disk are assumed to be complete, and each piece's hash is verified the first time it is requested.
  • implements an ARC disk cache, tuned for performing well under bittorrent work loads

network

  • a high quality uTP implementation (BEP 29). A transport protocol with delay based congestion control. See separate article.
  • adjusts the length of the request queue depending on download rate.
  • serves multiple torrents on a single port and in a single thread
  • piece picking on block-level (as opposed to piece-level). This means it can download parts of the same piece from different peers. It will also prefer to download whole pieces from single peers if the download speed is high enough from that particular peer.
  • supports http proxies and basic proxy authentication
  • supports gzipped tracker-responses
  • can limit the upload and download bandwidth usage and the maximum number of unchoked peers
  • possibility to limit the number of connections.
  • delays have messages if there's no other outgoing traffic to the peer, and doesn't send have messages to peers that already has the piece. This saves bandwidth.
  • selective downloading. The ability to select which parts of a torrent you want to download.
  • ip filter to disallow ip addresses and ip ranges from connecting and being connected.
  • NAT-PMP and UPnP support (automatic port mapping on routers that supports it)
  • implements automatic upload slots, to optimize download rate without spreading upload capacity too thin. The number of upload slots is adjusted based on the peers' download capacity to work even for connections that are orders of magnitude faster than others.

highlighted features

disk caching

All disk I/O in libtorrent is done asynchronously to the network thread, by the disk io threads. When a block is read, the disk io thread reads all subsequent blocks from that piece into the read cache, assuming that the peer requesting the block will also request more blocks from the same piece. This decreases the number of syscalls for reading data. It also decreases delay from seeking.

Similarly, for write requests, blocks are cached and flushed to disk once one full piece is complete or the piece is the least recently updated one when more cache space is needed. The cache dynamically allocates space between the write and read cache. The write cache is strictly prioritized over the read cache.

The cache blocks that are in used, are locked into physical memory to avoid it being paged out to disk. Allowing the disk cache to be paged out to disk means that it would become extremely inefficient to flush it, since it would have to be read back into physical memory only to be flushed back out to disk again.

In order to conserve memory, and system calls, iovec file operations are used to flush multiple cache blocks in a single call.

On low-memory systems, the disk cache can be disabled altogether or set to smaller limit, to save memory.

The disk caching algorithm is configurable between 'LRU' and 'largest contiguous'. The largest contiguous algorithm is the default and flushes the largest contiguous block of buffers, instead of flushing all blocks belonging to the piece which was written to least recently.

network buffers

On CPUs with small L2 caches, copying memory can be expensive operations. It is important to keep copying to a minimum on such machines. This mostly applies to embedded systems.

In order to minimize the number of times received data is copied, the receive buffer for payload data is received directly into a page aligned disk buffer. If the connection is encrypted, the buffer is decrypted in-place. The buffer is then moved into the disk cache without being copied. Once all the blocks for a piece have been received, or the cache needs to be flushed, all the blocks are passed directly to writev() to flush them in a single syscall. This means a single copy into user space memory, and a single copy back into kernel memory, as illustrated by this figure:

write_disk_buffers.png

When seeding and uploading in general, unnecessary copying is avoided by caching blocks in aligned buffers, that are copied once into the peer's send buffer. The peer's send buffer is not guaranteed to be aligned, even though it is most of the time. The send buffer is then encrypted with the peer specific key and chained onto the iovec for sending. This means there is one user space copy in order to allow unaligned peer requests and peer-specific encryption. This is illustrated by the following figure:

read_disk_buffers.png

piece picker

The piece picker is a central component in a bittorrent implementation. The piece picker in libtorrent is optimized for quickly finding the rarest pieces. It keeps a list of all available pieces sorted by rarity, and pieces with the same rarity, shuffled. The rarest first mode is the dominant piece picker mode. Other modes are supported as well, and used by peers in specific situations.

The piece picker allows to combine the availability of a piece with a priority. Together they determine the sort order of the piece list. Pieces with priority 0 will never be picked, which is used for the selective download feature.

In order to have as few partially finished pieces as possible, peers have an affinity towards picking blocks from the same pieces as other peers in the same speed category. The speed category is a coarse categorization of peers based on their download rate. This makes slow peers pick blocks from the same piece, and fast peers pick from the same piece, and hence decreasing the likelihood of slow peers blocking the completion of pieces.

The piece picker can also be set to download pieces in sequential order.

share mode

The share mode feature in libtorrent is intended for users who are only interested in helping out swarms, not downloading the torrents.

It works by predicting the demand for pieces, and only download pieces if there is enough demand. New pieces will only be downloaded once the share ratio has hit a certain target.

This feature is especially useful when combined with RSS, so that a client can be set up to provide additional bandwidth to an entire feed.

merkle hash tree torrents

merkle_tree.png

Merkle hash tree torrents is an extension that lets a torrent file only contain the root hash of the hash tree forming the piece hashes. The main benefit of this feature is that regardless of how many pieces there is in a torrent, the .torrent file will always be the same size. It will only grow with the number of files (since it still has to contain the file names).

With regular torrents, clients have to request multiple blocks for pieces, typically from different peers, before the data can be verified against the piece hash. The larger the pieces are, the longer it will take to download a complete piece and verify it. Before the piece is verified, it cannot be shared with the swarm, which means the larger piece sizes, the slower turnaround data has when it is downloaded by peers. Since on average the data has to sit around, waiting, in client buffers before it has been verified and can be uploaded again.

Another problem with large piece sizes is that it is harder for a client to pinpoint the malicious or buggy peer when a piece fails, and it will take longer to re-download it and take more tries before the piece succeeds the larger the pieces are.

The piece size in regular torrents is a tradeoff between the size of the .torrent file itself and the piece size. Often, for files that are 4 GB, the piece size is 2 or 4 MB, just to avoid making the .torrent file too big.

Merkle torrents solves these problems by removing the tradeoff between .torrent size and piece size. With merkle torrents, the piece size can be the minimum block size (16 kB), which lets peers verify every block of data received from peers, immediately. This gives a minimum turnaround time and completely removes the problem of identifying malicious peers.

The root hash is built by hashing all the piece hashes pair-wise, until they all collapse down to the root.

customizable file storage

storage.png

libtorrent's storage implementation is customizable. That means a special purpose bittorrent client can replace the default way to store files on disk.

When implementing a bittorrent cache, it doesn't matter how the data is stored on disk, as long as it can be retrieved and seeded. In that case a new storage class can be implemented (inheriting from the storage_interface class) that avoids the unnecessary step of mapping slots to files and offsets. The storage can ignore the file boundaries and just store the entire torrent in a single file (which will end up being all the files concatenated). The main advantage of this, other than a slight cpu performance gain, is that all file operations would be page (and sector) aligned. This enables efficient unbuffered I/O, and can potentially lead to more efficient read caching (using the built in disk cache rather than relying on the operating system's disk cache).

The storage interface supports operating systems where you can ask for sparse regions (such as Windows and Solaris). The advantage of this is that when checking files, the regions that are known to be sparse can be skipped, which can reduce the time to check a torrent significantly.

easy to use API

One of the design goals of the libtorrent API is to make common operations simple, but still have it possible to do complicated and advanced operations. This is best illustrated by example code to implement a simple bittorrent client:

#include <iostream>
#include "libtorrent/session.hpp"

// usage a.out [torrent-file]
int main(int argc, char* argv[]) try
{
        lt::session s;
        lt::add_torrent_params p;
        p.save_path = "./";
        p.ti = new torrent_info(argv[1]);
        s.add_torrent(p);

        // wait for the user to end
        char a;
        std::cin.unsetf(std::ios_base::skipws);
        std::cin >> a;
        return 0;
}
catch (std::exception& e)
{
        std::cerr << ec.what() << std::endl;
        return 1;
}

This client doesn't give the user any status information or progress about the torrent, but it is fully functional.

libtorrent also comes with python bindings for easy access for python developers.

portability

libtorrent runs on most major operating systems, including Windows, MacOS X, Linux, BSD and Solaris. It uses Boost.Thread, Boost.Asio, Boost.Chrono, Boost.Random, Boost.Date_time and various other boost libraries. At least version 1.49 of boost is required.

Since libtorrent uses Boost.Asio it will take full advantage of high performance network APIs on the most popular platforms. I/O completion ports on windows, epoll on linux and kqueue on MacOS X and BSD.

libtorrent does not build with the following compilers:

  • GCC 2.95.4
  • Visual Studio 6, 7.0, 7.1
libtorrent-rasterbar-1.1.13/docs/features.rst000066400000000000000000000350311351156116000212450ustar00rootroot00000000000000================= libtorrent manual ================= :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none introduction ============ libtorrent is a feature complete C++ bittorrent implementation focusing on efficiency and scalability. It runs on embedded devices as well as desktops. It boasts a well documented library interface that is easy to use. It comes with a simple bittorrent client demonstrating the use of the library. features ======== libtorrent is an ongoing project under active development. Its current state supports and includes the following features: extensions ---------- * plugin interface for implementing custom bittorrent extensions without having to modify libtorrent * supports trackerless torrents (using the Mainline kademlia DHT protocol) with some `DHT extensions`_. `BEP 5`_. * supports the bittorrent `extension protocol`_. See extensions_. `BEP 10`_. * supports the uTorrent metadata transfer protocol `BEP 9`_ (i.e. magnet links). * supports the uTorrent peer exchange protocol (PEX). * supports local peer discovery (multicasts for peers on the same local network) * multitracker extension support (supports both strict `BEP 12`_ and the uTorrent interpretation). * tracker scrapes * supports lt_trackers extension, to exchange trackers between peers * `HTTP seeding`_, as specified in `BEP 17`_ and `BEP 19`_. * supports the udp-tracker protocol. (`BEP 15`_). * supports the ``no_peer_id=1`` extension that will ease the load off trackers. * supports the ``compact=1`` tracker parameter. * super seeding/initial seeding (`BEP 16`_). * private torrents (`BEP 27`_). * upload-only extension (`BEP 21`_). * support for IPv6, including `BEP 7`_ and `BEP 24`_. * support for merkle hash tree torrents. This makes the size of torrent files scale well with the size of the content. * share-mode. This is a special mode torrents can be put in to optimize share ratio rather than downloading the torrent. .. _article: utp.html .. _extensions: manual-ref.html#extensions .. _`http seeding`: manual-ref.html#http-seeding disk management --------------- * can use multipled disk I/O threads to not have the disk block network or client interaction. * supports verifying the SHA-1 hash of pieces in multiple threads, to take advantage of multi core machines. * supports files > 2 gigabytes. * fast resume support, a way to avoid the costly piece check at the start of a resumed torrent. Saves the storage state, piece_picker state as well as all local peers in a fast-resume file. * has an adjustable read and write disk cache for improved disk throughput. * queues torrents for file check, instead of checking all of them in parallel. * does not have any requirements on the piece order in a torrent that it resumes. This means it can resume a torrent downloaded by any client. * seed mode, where the files on disk are assumed to be complete, and each piece's hash is verified the first time it is requested. * implements an ARC disk cache, tuned for performing well under bittorrent work loads network ------- * a high quality uTP implementation (`BEP 29`_). A transport protocol with delay based congestion control. See separate article_. * adjusts the length of the request queue depending on download rate. * serves multiple torrents on a single port and in a single thread * piece picking on block-level (as opposed to piece-level). This means it can download parts of the same piece from different peers. It will also prefer to download whole pieces from single peers if the download speed is high enough from that particular peer. * supports http proxies and basic proxy authentication * supports gzipped tracker-responses * can limit the upload and download bandwidth usage and the maximum number of unchoked peers * possibility to limit the number of connections. * delays have messages if there's no other outgoing traffic to the peer, and doesn't send have messages to peers that already has the piece. This saves bandwidth. * selective downloading. The ability to select which parts of a torrent you want to download. * ip filter to disallow ip addresses and ip ranges from connecting and being connected. * NAT-PMP and UPnP support (automatic port mapping on routers that supports it) * implements automatic upload slots, to optimize download rate without spreading upload capacity too thin. The number of upload slots is adjusted based on the peers' download capacity to work even for connections that are orders of magnitude faster than others. .. _`DHT extensions`: dht_extensions.html .. _`BEP 5`: https://bittorrent.org/beps/bep_0005.html .. _`BEP 7`: https://bittorrent.org/beps/bep_0007.html .. _`BEP 9`: https://bittorrent.org/beps/bep_0009.html .. _`BEP 10`: https://bittorrent.org/beps/bep_0010.html .. _`BEP 12`: https://bittorrent.org/beps/bep_0012.html .. _`BEP 15`: https://bittorrent.org/beps/bep_0015.html .. _`BEP 16`: https://bittorrent.org/beps/bep_0016.html .. _`BEP 17`: https://bittorrent.org/beps/bep_0017.html .. _`BEP 19`: https://bittorrent.org/beps/bep_0019.html .. _`BEP 21`: https://bittorrent.org/beps/bep_0021.html .. _`BEP 24`: https://bittorrent.org/beps/bep_0024.html .. _`BEP 27`: https://bittorrent.org/beps/bep_0027.html .. _`BEP 29`: https://bittorrent.org/beps/bep_0029.html .. _`extension protocol`: extension_protocol.html highlighted features ==================== disk caching ------------ All disk I/O in libtorrent is done asynchronously to the network thread, by the disk io threads. When a block is read, the disk io thread reads all subsequent blocks from that piece into the read cache, assuming that the peer requesting the block will also request more blocks from the same piece. This decreases the number of syscalls for reading data. It also decreases delay from seeking. Similarly, for write requests, blocks are cached and flushed to disk once one full piece is complete or the piece is the least recently updated one when more cache space is needed. The cache dynamically allocates space between the write and read cache. The write cache is strictly prioritized over the read cache. The cache blocks that are in used, are locked into physical memory to avoid it being paged out to disk. Allowing the disk cache to be paged out to disk means that it would become extremely inefficient to flush it, since it would have to be read back into physical memory only to be flushed back out to disk again. In order to conserve memory, and system calls, iovec file operations are used to flush multiple cache blocks in a single call. On low-memory systems, the disk cache can be disabled altogether or set to smaller limit, to save memory. The disk caching algorithm is configurable between 'LRU' and 'largest contiguous'. The largest contiguous algorithm is the default and flushes the largest contiguous block of buffers, instead of flushing all blocks belonging to the piece which was written to least recently. network buffers --------------- On CPUs with small L2 caches, copying memory can be expensive operations. It is important to keep copying to a minimum on such machines. This mostly applies to embedded systems. In order to minimize the number of times received data is copied, the receive buffer for payload data is received directly into a page aligned disk buffer. If the connection is encrypted, the buffer is decrypted in-place. The buffer is then moved into the disk cache without being copied. Once all the blocks for a piece have been received, or the cache needs to be flushed, all the blocks are passed directly to ``writev()`` to flush them in a single syscall. This means a single copy into user space memory, and a single copy back into kernel memory, as illustrated by this figure: .. image:: write_disk_buffers.png :width: 100% When seeding and uploading in general, unnecessary copying is avoided by caching blocks in aligned buffers, that are copied once into the peer's send buffer. The peer's send buffer is not guaranteed to be aligned, even though it is most of the time. The send buffer is then encrypted with the peer specific key and chained onto the ``iovec`` for sending. This means there is one user space copy in order to allow unaligned peer requests and peer-specific encryption. This is illustrated by the following figure: .. image:: read_disk_buffers.png :width: 100% piece picker ------------ The piece picker is a central component in a bittorrent implementation. The piece picker in libtorrent is optimized for quickly finding the rarest pieces. It keeps a list of all available pieces sorted by rarity, and pieces with the same rarity, shuffled. The rarest first mode is the dominant piece picker mode. Other modes are supported as well, and used by peers in specific situations. The piece picker allows to combine the availability of a piece with a priority. Together they determine the sort order of the piece list. Pieces with priority 0 will never be picked, which is used for the selective download feature. In order to have as few partially finished pieces as possible, peers have an affinity towards picking blocks from the same pieces as other peers in the same speed category. The speed category is a coarse categorization of peers based on their download rate. This makes slow peers pick blocks from the same piece, and fast peers pick from the same piece, and hence decreasing the likelihood of slow peers blocking the completion of pieces. The piece picker can also be set to download pieces in sequential order. share mode ---------- The share mode feature in libtorrent is intended for users who are only interested in helping out swarms, not downloading the torrents. It works by predicting the demand for pieces, and only download pieces if there is enough demand. New pieces will only be downloaded once the share ratio has hit a certain target. This feature is especially useful when combined with RSS, so that a client can be set up to provide additional bandwidth to an entire feed. merkle hash tree torrents ------------------------- .. image:: merkle_tree.png :align: right Merkle hash tree torrents is an extension that lets a torrent file only contain the root hash of the hash tree forming the piece hashes. The main benefit of this feature is that regardless of how many pieces there is in a torrent, the .torrent file will always be the same size. It will only grow with the number of files (since it still has to contain the file names). With regular torrents, clients have to request multiple blocks for pieces, typically from different peers, before the data can be verified against the piece hash. The larger the pieces are, the longer it will take to download a complete piece and verify it. Before the piece is verified, it cannot be shared with the swarm, which means the larger piece sizes, the slower turnaround data has when it is downloaded by peers. Since on average the data has to sit around, waiting, in client buffers before it has been verified and can be uploaded again. Another problem with large piece sizes is that it is harder for a client to pinpoint the malicious or buggy peer when a piece fails, and it will take longer to re-download it and take more tries before the piece succeeds the larger the pieces are. The piece size in regular torrents is a tradeoff between the size of the .torrent file itself and the piece size. Often, for files that are 4 GB, the piece size is 2 or 4 MB, just to avoid making the .torrent file too big. Merkle torrents solves these problems by removing the tradeoff between .torrent size and piece size. With merkle torrents, the piece size can be the minimum block size (16 kB), which lets peers verify every block of data received from peers, immediately. This gives a minimum turnaround time and completely removes the problem of identifying malicious peers. The root hash is built by hashing all the piece hashes pair-wise, until they all collapse down to the root. customizable file storage ------------------------- .. image:: storage.png :align: right libtorrent's storage implementation is customizable. That means a special purpose bittorrent client can replace the default way to store files on disk. When implementing a bittorrent cache, it doesn't matter how the data is stored on disk, as long as it can be retrieved and seeded. In that case a new storage class can be implemented (inheriting from the ``storage_interface`` class) that avoids the unnecessary step of mapping slots to files and offsets. The storage can ignore the file boundaries and just store the entire torrent in a single file (which will end up being all the files concatenated). The main advantage of this, other than a slight cpu performance gain, is that all file operations would be page (and sector) aligned. This enables efficient unbuffered I/O, and can potentially lead to more efficient read caching (using the built in disk cache rather than relying on the operating system's disk cache). The storage interface supports operating systems where you can ask for sparse regions (such as Windows and Solaris). The advantage of this is that when checking files, the regions that are known to be sparse can be skipped, which can reduce the time to check a torrent significantly. easy to use API --------------- One of the design goals of the libtorrent API is to make common operations simple, but still have it possible to do complicated and advanced operations. This is best illustrated by example code to implement a simple bittorrent client:: #include #include "libtorrent/session.hpp" // usage a.out [torrent-file] int main(int argc, char* argv[]) try { lt::session s; lt::add_torrent_params p; p.save_path = "./"; p.ti = new torrent_info(argv[1]); s.add_torrent(p); // wait for the user to end char a; std::cin.unsetf(std::ios_base::skipws); std::cin >> a; return 0; } catch (std::exception& e) { std::cerr << ec.what() << std::endl; return 1; } This client doesn't give the user any status information or progress about the torrent, but it is fully functional. libtorrent also comes with python bindings for easy access for python developers. portability =========== libtorrent runs on most major operating systems, including Windows, MacOS X, Linux, BSD and Solaris. It uses Boost.Thread, Boost.Asio, Boost.Chrono, Boost.Random, Boost.Date_time and various other boost libraries. At least version 1.49 of boost is required. Since libtorrent uses Boost.Asio it will take full advantage of high performance network APIs on the most popular platforms. I/O completion ports on windows, epoll on linux and kqueue on MacOS X and BSD. libtorrent does not build with the following compilers: * GCC 2.95.4 * Visual Studio 6, 7.0, 7.1 libtorrent-rasterbar-1.1.13/docs/hacking.diagram000066400000000000000000000037531351156116000216350ustar00rootroot00000000000000+--------------+ "pimpl" +----------------+ | "session" +--------------------------->| "session_impl" | +--------------+ +------+-----+---+ "m_torrents[]" | | +---------------------+ | | | "torrent_handle" +--------+ | | +---------------------+ "weak" | +--------------+ | | | | "m_connections[]" | | +-------------+ +--+ | | | | | "m_picker" v v | v v "peers we are connected to" +----------------+ +----------++ +-------------------+ | "piece_picker" |<---+-+ "torrent" ++ +--+ "peer_connection" ++ +----------------+ | ++----------+| | ++------------------+| "m_torrent_file" | +-----------+ | +-------------------+ +-------------------+ | | | "torrent_info" |<---+ | "m_socket" +-------------------+ | | +----------------------------+ | +->| "socket_type (variant)" | "m_peer_list" v | | "(TCP/uTP/SSL/socks5/...)" | +--------------+ | +----------------------------+ | "peer_list" | | +------------+-+ | "m_peer_info" "list of all" | "m_peers[]" | "contains contact information" "peers we" | | "for peers we're not necessarily" "know of" | v "connected to" | +----------------+ +---->| "torrent_peer" ++ ++---------------+| +----------------+ libtorrent-rasterbar-1.1.13/docs/hacking.html000066400000000000000000000274471351156116000212030ustar00rootroot00000000000000 libtorrent hacking

libtorrent hacking

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

This describe some of the internals of libtorrent. If you're looking for something to contribute, please take a look at the todo list.

terminology

This section describes some of the terminology used throughout the libtorrent source. Having a good understanding of some of these keywords helps understanding what's going on.

A piece is a part of the data of a torrent that has a SHA-1 hash in the .torrent file. Pieces are almost always a power of two in size, but not necessarily. Each piece is plit up in blocks, which is a 16 kiB. A block never spans two pieces. If a piece is smaller than 16 kiB or not divisible by 16 kiB, there are blocks smaller than that.

16 kiB is a de-facto standard of the largest transfer unit in the bittorrent protocol. Clients typically reject any request for larger pieces than this.

The piece picker is the part of a bittorrent client that is responsible for the logic to determine which requests to send to peers. It doesn't actually pick full pieces, but blocks (from pieces).

The file layout of a torrent is represented by file storage objects. This class contains a list of all files in the torrent (in a well defined order), the size of the pieces and implicitly the total size of the whole torrent and number of pieces. The file storage determines the mapping from pieces to files. This representation may be quite complex in order to keep it extremely compact. This is useful to load very large torrents without exploding in memory usage.

A torrent object represents all the state of swarm download. This includes a piece picker, a list of peer connections, file storage (torrent file). One important distiction is between a connected peer (peer_connection) and a peer we just know about, and may have been connected to, and may connect to in the future (torrent_peer). The list of (not connected) peers may grow very large if not limited (through tracker responses, DHT and peer exchange). This list is typically limited to a few thousand peers.

The peer_list maintains a potentially large list of known peers for a swarm (not necessarily connected).

structure

This is the high level structure of libtorrent. Bold types are part of the public interface:

hacking.png

session_impl

This is the session state object, containing all session global information, such as:

  • the list of all torrents m_torrent.
  • the list of all peer connections m_connections.
  • the global rate limits m_settings.
  • the DHT state m_dht.
  • the port mapping state, m_upnp and m_natpmp.

session

This is the public interface to the session. It implements pimpl (pointer to implementation) in order to hide the internal representation of the session_impl object from the user and make binary compatibility simpler to maintain.

torrent_handle

This is the public interface to a torrent. It holds a weak reference to the internal torrent object and manipulates it by sending messages to the network thread.

torrent

peer_connection

peer_list

piece_picker

torrent_info

threads

libtorrent starts 3 to 5 threads.

  • The first thread is the main thread that will sit idle in a select() call most of the time. This thread runs the main loop that will send and receive data on all connections. In reality it's typically not actually in select(), but in kqueue(), epoll_wait() or poll, depending on operating system.
  • The second thread is the disk I/O thread. All disk read and write operations are passed to this thread and messages are passed back to the main thread when the operation completes.
  • The third thread is the SHA-1 hash thread. By default there's only one hash thread, but on multi-core machines downloading at very high rates, libtorrent can be configured to start any number of hashing threads, to take full use of multi core systems. (see settings_pack::aio_threads).
  • The fourth and fifth threads are spawned by asio on systems that don't support asynchronous host name resolution, in order to simulate non-blocking getaddrinfo().

disk cache

The disk cache implements ARC, Adaptive Replacement Cache. This consists of a number of LRUs:

  1. lru L1 (recently used)
  2. lru L1 ghost (recently evicted)
  3. lru L2 (frequently used)
  4. lru L2 ghost (recently evicted)
  5. volatile read blocks
  6. write cache (blocks waiting to be flushed to disk)
disk_cache.png

These LRUs are stored in block_cache in an array m_lru.

The cache algorithm works like this:

if (L1->is_hit(piece)) {
        L1->erase(piece);
        L2->push_back(piece);
} else if (L2->is_hit(piece)) {
        L2->erase(piece);
        L2->push_back(page);
} else if (L1->size() == cache_size) {
        L1->pop_front();
        L1->push_back(piece);
} else {
        if (L1->size() + L2->size() == 2*chache_size) {
                L2->pop_front();
        }
        L1->push_back(piece);
}

It's a bit more complicated since within L1 and L2 in this pseudo code have to separate the ghost entries and the in-cache entries.

Note that the most recently used and more frequently used pieces are at the back of the lists. Iterating over a list gives you low priority pieces first.

In libtorrent pieces are cached, not individual blocks, a single peer would typically trigger many cache hits when downloading a piece. Since ARC is sensitive to extra cache hits (a piece is moved to L2 the second time it's hit) libtorrent only move the cache entry on cache hits when it's hit by another peer than the last peer that hit it.

Another difference compared to the ARC paper is that libtorrent caches pieces, which aren't necessarily fully allocated. This means the real cache size is specified in number of blocks, not pieces, so there's not clear number of pieces to keep in the ghost lists. There's an m_num_arc_pieces member in block_cache that defines the arc cache size, in pieces, rather than blocks.

libtorrent-rasterbar-1.1.13/docs/hacking.png000066400000000000000000000365141351156116000210160ustar00rootroot00000000000000‰PNG  IHDR9)?9{ü=IDATxśíÝPSgľ?đ籊CŔ"$@Pc4ă†ÝRs/‚{Ëâ pU´‹u¶?tî®SÖîČËtFۡ3wZvÜ;:‹?ŘöŢŇŃ{G‡?Fi `ZŘ\nnHB@,XšTń|˙8÷ćËž BNÂűőy8çyžsňś‡ON>‡˘iš/ůşŕcőőőĹĹĹ Ú%''‡˘¨cÇŽy©KŕDa] űěăŹ?öuGÖŐVЎˇ!µZť‘‘~äȦpçÎťE9×Ő***rrrbccOť:ŃÖÖćr/X6«fľ (ĘWýϸż,*ş»»ŰŰŰ%ÉŢ˝{»şş¶mŰvďŢ˝ÖÖVçň@ HLLܱc‡Íf«¬¬lkk‹ŹŹçîĺ­XvôV|ţkë5FłYčź™­[·¦¦¦BvďŢ=88č2ęR©T‰$**Ęh4şą€˙¤· (ż†Ď@W“Éd4>|ŘŢŢ.•J]nDţ~^sg/đÄj+T*---•JĄjµZ©T666R•žž~ůňeŠ˘Îś9ăÎ^őőőEeeeŐÔÔPuîÜąĺ=€•ĺďľJQřZ¨?YĐűe±XňňňîŢ˝» &<Ű Ŕ_`Ň[!đF_Ăşń%Vó #+,Htt´Ëcžís[Şą¬V|ęÇđ~,ŇĘĽV`.ë•ůFCŔXÔşZeeedddxxřŃŁG™’ŞŞ*‘HS]]=Ű6ÜVFVBHEEETT”H$*//'„ iµÚĚĚĚĐĐP,ż€;(ŠZÚL |›ńË`Ą g`˝ś—L&ëííuľěîîV©TV«Őfł©Őęţţ~î6.Khšnii)**b~îęę’ËĺfłY.—wttÍć;wŚDGG[­Öő3P-ôýXf›îśżuż*Îx3ëyçťwNž<ůöŰo;v¬şşú˝÷Ţ3›ÍBˇĐ`0Řl6ĄRŮŮŮÉlyăĆŤ˛˛2÷<`¶żĆÎ…» 555ĺĺĺV«5??˙ĉ&“IŻ×‹Ĺbć·!..޵ w/nµ&“)===66–˘Őjűúú$IJJŠFŁ!„ÄÇÇOMM-¦Ű ÜÇ\/´§ź…ńĆC.k€€´¨X-#####cjjJŁŃĘd2ŤFóůçź‹D˘Ů¶‰‰‰á–°ŞMHH8}ú´Őj}öěYkkë›oľI‹éj ňřŻŔ 1ó˙™E^/üźńfËe˝aÆööößţö·Ô >çůýj‡#;;[(ŠĹâ]»vEGGËĺňüü|…BVPPŕp8¸ŰpK¸YÓŇŇ  …R©,))Q(KxŔ°B0÷«Íüa1µńmĆC.k€•ßőcxż)°/"ä˛v ě7_ň«€Kv»ťâxúô©ŻűËëj~ ďŔ"á"Z!đF_Ăş!Vŕ/Äjü…X €żŘąp‘VLzŔsřj €ßóŻď9úWo|źđb5ţB¬Ŕ_ŐŔż1Ď\zůĺ—™—yyyE­ZĹţ‟B¬ `±X>Ě«¶âââhšľ~ý:óňłĎ>ŁizăĆŤ^îŔ2A¬„RQQ‘““{ęÔ©¶¶6î6ŁŁŁ‰¤®®Ž˘(ťNçÜ1**J$•——3%YYYWŻ^•JĄBˇĐl6»,©ŞŞ‰D111ŐŐŐ„ˇˇ!­V›™™Z\\<[[•••‘‘‘áááGŹőţ)ŕ|L„"wěŘałŮ*++ŰÚÚ~öłź±¶‰DMMMuuuuuuLÉýű÷Ż\ąŇŐŐ%^z饂‚‚íŰ·oٲĄşşúöíŰR©488Â*éééihhčéé ĘÎÎÎËË{îąçôz}SSS||ĽR©ٰa«-BČ'ź|ňŐW_Éd˛e:)<€u5ř_*•*&&F©TĆÄÄüôÓOîěb2™ŇÓÓcccŁŁŁµZm__!$88¸¬¬L&“=÷ÜsAAAÜ“ɤ×ëĹbqTTÔ×_m0!)))Ť&***>>~jjĘes555ĺĺĺ?˙ůĎ?řŕĄ;n^C¬˙‹‰«ćÎăOQ”Ĺbq8ccc„„„„ććf«Őj6›[[[“’’Í"""X;Î,‘ÉdŤĆfłŃ4MÓtnn.!D ĚÝ!$##ăÚµk7oެ­­^ÔŃř Äj°ÉÉÉkÖ¬aî!KKK+,,T(JĄ˛¤¤DˇP¸S‰\.ĎĎĎW(aaa‡Ăť¶Gvv¶P(‹Ĺ»v튎Ž^Âăŕ-<čüۦM›úűűÝ) `ţőÔ&˙ę-€Ďa] \°ŰíÇÓ§O}Ý/¸ůŐ}Ű+€Ą‚nüž­TůWo|ëjü…X €ż«đb5ţú»gLÍťŔ«pŻ1űy ř{ >˙\Âg ü…X €ż«đb5ţ śXÍb±h4šĹÔ Ńh, !$''‡˘¨cÇŽ-Q×<´¨XÍb±>|x©şÂź¶oܸ±))éčŃŁÜmT*•Bˇxá…Nž<)—Ëiš®ŻŻ§(*++«¦¦†˘¨sçÎąŮC€%GÍüŕ‰˘¨ţ9ř ĆÎ,†Ť˙ę-€Ďůe~5»ÝNq<}úÔ×ýXb~«…„„¸ř–ÄŞĄüžž¤¤$»Ýîë^,Ś_Ćj+b5đ3*•Ę™µřńăÇ*•Šp’sÓ ;uvvętşńńńĺď9€đą!ř™ÔÔÔžžžÚÚZ@P\\¬T*{zzzzz‚‚‚˛łłóňňâââţýß˙ťň_˙ő_YYYŻżţ:łoOOĎŰoż}ýúőµk×úňÜĆŽŐxN©T~óÍ7“““„Á°mŰ6n˛âČČHndBHii©N§C ćL<ńw±ľD ü—ššzîÜą_ţň—v»ýĘ•+gΜٰaFŁůüóĎE"łÍ_˙úW& ˛^Ż/**r8Ď=÷!¤ąąůź˙ůźţéźţɧá0!đîW?ŁT*oßľť“““••ŐŇŇ’––ĆMV<[äŐ«W_ĽxńěŮłFŁŃ·Gŕ&$$ŕľeĺ[ `t,ÖŐř ±!VđŤúúúâââĹ×ÉD’’’˘Ńh!ńńńSSS!!!¬îŁSěv;«fw¸Ó:!¤¦¦¦ĽĽÜjµćççź8qÂł“ţź@ "s>--11±ąąyxxŘY’ĐÜÜlµZÍfskkkRR!D °vd•Čd2ŤFcłŮhš¦i:77—[3Ó‹Ĺâp8ĆĆĆ\öÇÍÖ322®]»vóćÍÚÚZV¨«ŔJ¤T*‹ŠŠT*Uhhčľ}űĆÇÇÓŇŇ  …R©,))Q(îÔĂ}t ·fBHrrňŔŔŔš5kűŐ)ŠJOOż|ů2EQgÎśq§u‡Ă‘ťť- Ĺbń®]»˘ŁŁ—öś?­Ä/ăđ߾Ƿţ@ ÁčZrřh`Ăş8»ÝNq<}úÔ×ýZ2Üüjľí,üsŔ |[iŕ[ `t,ÖŐř ±!Vŕ/䀥4G:+đ!ĎîĂ» ŔŐ`‰á¶qľYLČ…wŔçđ(!Vŕ/Äjü…X €ż«đb5ţB¬Ŕ_Őř ±!Vŕ/Äjü…X €ż«đb5ţB¬ĆGőőőĹĹĹsl””d·Ű—­?ŔŤĆb±řş°ô«ńŃ.]şäë^Ŕ2ˇ(ĘĺϾ·ţ€ÇXoźűď¦Ç;Â’łX,‡ćO=ŕŐĽĄ˘˘"'''66öÔ©SmmmÜm†††ÔjuFFFxxř‘#GÂť;wR5s]íüůób±8,,,77wbbÂYŢŮŮ©ÓéĆÇÇ !UUU"‘(&&¦şşšůmVVÖŐ«WĄR©P(4›Í^;;{||ś¦éîîn•JeµZm6›Z­îďď§iúřńă{öěéííµŰíÓÓÓŢ·jŮ‚ÂůçO"‘DEEŤF—ŰlÝş555•˛{÷îÁÁÁm۶±60™Lééé7nd•—––ętşµk×2Űčőz±XĚüĘ`0ÄĹĹ—••Éd˛%=&×XłŮłgĎXóMÓîĚT555ĺĺĺV«5??˙ĉ܆”Jĺ7ß|399ÉT˛mŰ6—s k݉‰‰ââ⦦¦©©)‰D²Zw§îśĚ”ÄĆĆB´Zm__źD"IIIŃh4„řřř©©©V ·-»Ýîr¶gµÎÚ‹˘ŃhÖ­[·nÝş„„„9öĺĹĚ>oßľÝÍ}aůá3P/ "óÝęa2™ŚFăÇŰŰŰĄR)wÄÄÄćććááaVyssłÉdjhh „Čd2ŤFcłŮ<77—Ů&""biŽüMÓžÝmćÁŽ¬ŮŚ;/ą9Sedd\»víćÍ›µµµÜ©Ź’ššzýúuŤFłm۶+W®l۶m¶šgşuëÖäääwß}÷Ĺ_üř㏇ۺ;őpçä„„„ććffń¬µµ5))‰"X;˛J¸mąśí)ОX,‡cllĚĺ^111÷îÝ{ôč‘Á`čííĺvŘe=.ű Ľ…XÍǤRiii©T*U«ŐJĄ˛±±‘˘¨ôôôË—/SućĚĄRYTT¤R©BCC÷íŰÇÜťFY˝zőŋϞ=k4ĺry~~ľBˇ +((pÎAˆ5›qç%wf*‡Ă‘ťť- Ĺbń®]»˘ŁŁąŰ(•ĘŰ·oçäädeeµ´´¤ĄĄąSóîÝ»‡‡‡·l٢×ë“’’\Ţ çN=Ü99--­°°PˇP(•Ę’’…BáÎéâ¶ĺr¶ONNXłf sźw/•JĄP(^xá…“'OĘĺrzĆ= 3±ęń¬Ďŕ+Ôlď+,‹Ĺ’——w÷î]_w|Ź˘řu1zÜľey71›xÖŐ–‰Ýn§8|Ý)ŕ;ü Ŕ_XWŕ/Äjü…X €ż|ź ·ŘŻqs$F¬÷Ćń KX$\}|ćűXŤ`¬ ô·#Öi„ř†%x WĎá3PţB¬Ŕ_Őř ±B¬V___\\Ě“¶’’’ěvűňtfZÎ?ËĆ÷Ď °'='%%uuu…„„řş#Ľ3ďÍ|‰˙aˇ#Öb±ĽőÖ[µµµŢë’OÚZr6,łxöüz´óÍ ?ţĹźÖŐ†††ÔjuFFFxxř‘#GÂť;wR5ó©ŞŞ*‘HS]]Í”ś?^,‡……ĺććNLL¸ÜĆł¶¸53:;;u:Ýřř¸Ë¶˛˛˛®^˝*•J…BˇŮl^Şóăż'ŮÓ4x“Ĺčč¨D"©««Ł(J§Ó1…QQQ"‘¨ĽĽś)á n k iµÚĚĚĚĐĐPfLşl €ĹĺäĆť¦<›¸¸S"k´sÇ-·Äe묚]Žvî=oëţö5÷ű`6›…BˇÁ`°ŮlJĄ˛łł“)oii)**b~îîîV©TV«Őfł©ŐęţţţŽŽŽäää8ëánăY[ÜšišNLLÔëőŮŮŮăăăłµuüřń={öôööÚíöééi7ßßą|Ły2dˇnjj:xđ óeWW—\.4›ÍrąĽŁŁv5$X%Üd6›CBBîÜą322mµZąmůż Ľ˛‰”;ş<›¸¸S"w´sÇ-·Äťiśže´Ďś˘ÝiÝíŕpőń/ráşoëÖ­©©©„Ý»wn۶ŤµÉdŇëőb±yi0ěv{zzúĆŤçŘ&..Îł¶X53JKKu:ÝÚµkgk+88¸¬¬L&“-üŽ™©WTFfŘÄĆĆB´Zm__ßöíŰąC‚UÂHjµ:%%EŁŃBâă㧦¦–ýP–ŢÜ#eW>ŚvÖäöěŮ3Öč˘iÚ‰‹;%rG»D"aŤŰV‰;Ó¸;Üi}A¶•pőů/ú ”b2™ŚFăÇŰŰŰĄR)w™L¦Ńhl6Šććć&&&677ϱŤgmqkf477›L¦†††9ÚŠđě  ć„pK–ŤÇ-.ôH)ОX,‡cllŚ’ĐÜÜĚ,'´¶¶&%%1›q‡ÄĚ—I ĚÝ–ßYÂÓÎgž Ľ%–¬ÉŤ;ş<›¸¸S˘ËŃη¬w¦qâĆhwłu`¬«ĎOůY¬&•JKKKĄR©Z­V*•ŤŤŤEĄ§§_ľ|™˘¨3gÎČĺňüü|…BVPPŕp8”JeQQ‘JĄ Ý·oßřř8wĎÚâÖĚě¸zőę‹/ž={Öh4şŮÖŠ5s:`îZóuŹ–^rrňŔŔŔš5k»jŇŇŇ  …R©,))Q(îTâć@bµŕkrăŽ.Ď&.TŁÝĺdËíÜ)ÚłÖxČ÷_ýp˙ë'‹%//ďîÝ»ŢîŇ2·µBđí{F÷‡o0ć>±sÚ˝t üśHÁ_¬«ĎůŮşš7ŘívŠĂםŕ/—ÓćÓ§O}Ý/€Ŕäű`ű Á·7ëj|łBţł÷ůş× ąúüÖŐř ±!Vŕ/^äÂĹ˝üŕ_0b‡0,•ďc5ܱţeî‹›pÁ'0,>ŕ/Äjü…X €ż«đb5?vúôéçź^(~úé§LI}}}qq±sśśŠ˘Ž;ćŁŔb!Vs—Ĺb9|ř0Úšžžľpá‚ŃhśČÉÉa 8péŇ%ç6ŤŤŤ7nÜđbGŔË«‘ŠŠŠśśśŘŘŘS§NEDD´µµq·•H$uuuEét:çŽQQQ"‘¨ĽĽś)ÉĘĘşzőŞT* …fłŮeIUU•H$Љ‰©®®&„ iµÚĚĚĚĐĐPfIĚe[,ׯ__µj•ŐjŤ‰‰Y˝zu{{;!dçÎťEÍ\Wăbµ<‡XŤ‚ÄÄÄ#GŽLLLTVVşŚŐD"QSSÓÁišľuë!äţýűW®\éęęúöŰo?űěłÎÎNBČ–-[Ş««oßľýčŃ#±XĚ-éééihhčéé1 —/_z˝ľ˘˘b``ŕöíŰ###ܶ¸^~ůĺÉÉÉäädš¦išÖjµ„{÷îµ´´Ěq¤ÜÖöŔ«|ź —T*ŐÄÄ„D"‰ŠŠ2Ťîěb2™ŇÓÓccc !Z­¶ŻŻoűöíÁÁÁeee2™Ěą«Äd2éőz&Ś#„ µZť’’˘Ńh!ńńńSSSK|lßgVëqqqŢknĹBúxŕ! K˙…XŤB‚‚‚Č|sEQ‹Ĺáp<~üxýúő §Oź¶Z­Ďž=kmm}óÍ7™Í"""X;Î,‘ÉdŤćóĎ?‰DL‰Ĺbs·µ¸ű˙¸­Ă’Cvxŕ! Kż†Ď@Ý•śś<00°fÍ沴´´ÂÂB…BˇT*KJJ …;•Čĺňüü|…BVPPŕp8ÜiËŤŤŤEĄ§§_ľ|™˘¨3gÎÔ××S•••USSCQÔąsçÜlř‰ OHźŔŔ€…Âş›Ýn§8ž>}ŠţŔňĂx°Bay| ëjü…X €ż«đ×ň«!•",nÓX¨…ĺÂĹßZđb}ŕ3PţB¬Ŕ_Őř ±­ X­ľľľ¸¸xîmNź>ýüóĎ …ÂO?ýtyzŕIIIv»Ý×˝€ůy+VłX,‡öRĺžµuŕŔK—.ͱÁôôô… ŚFăÄÄDNNÎŇuŔ/-çLłńJ¬6::*‘Hęęę(ŠŇétLaEEETT”H$*//gJ˛˛˛®^˝*•J…BˇŮlvYRUU%‰bbbŞ«« !CCCZ­633344”Y$sŮ×Îť;)Šr®«qëą~ýúŞU«¬VkLLĚęŐ«ŰŰŰ]öĽ‡y,˝Ż{Ŕ#999±±±§NťŠhkksąYeeedddxxřŃŁGť;˛ć®óçĎ‹Ĺâ°°°ÜÜ܉‰ çľťťť:ťn||śpć[7gWđ:Úm Ú¸©©éŕÁΗ]]]rą|ppĐl6ËĺňŽŽš¦Ź?ľgĎžŢŢ^»Ý>==Í-éîîV©TV«Őfł©Őęţţ~łŮrçÎť‘‘‘ččh«ŐĘmk6---EEEĚĎ.뙜śLNNž»Ďŕ1—ăÇłˇč˝ţxŰ‚Ţ;ďĽsňäÉ·ß~űرcŐŐŐď˝÷žËÍd2YooŻó%wîęččHNN~đŕÁĚ˝őz}vvöřř8MÓÜů–v{vŻZX.\Ź™L¦ôôôŘŘXBV«íëëŰľ}{pppYY™L&snĆ*1™Lz˝^,3/ Z­NIIŃh4„řřř©©)Ź»4o=.űěqsŔÂ]Bâ—JĄšH$QQQFŁŃĺ6555ĺĺĺV«5??˙ĉÜąkzz:==}ăĆŤ¬KKKu:ÝÚµk‰«ů6..΋nóÖýjEY,‡Ă166FIHHhnn¶Z­fłąµµ5))‰Ů,""‚µăĚ™L¦Ńhl6WćććBÁÜmą‰[Ël}†%ÁĽ§Ü’eă«X   2ß2×®]»yófmmíđđ0wîJLLlnnfíŘÜÜl2™Č,ó­gł+,-oĹjÉÉÉkÖ¬aîrHKK+,,T(JĄ˛¤¤DˇP¸S‰\.ĎĎĎW(aaa‡Ăť¶¸)ŠJOOż|ů2EQgÎśq§uĎú 23xÂýjp8ŮŮŮBˇP,ďÚµ+::š;w)•ʢ˘"•Jşoß>ćî4BČęŐ«/^ĽxöěYŁŃčrľťwv€e@ążŔ@Q Ř€…oă‡oý*Đň«ŮívŠăéÓ§ľîŔba~X™°®Ë„oă‡oý*ĐÖŐ b5ţB¬Ŕ_ŐřkaĎ-@ú+Âä °H|ţŇĎb5>Ŕ ‡)Ŕc<˙oźđb5ţB¬Ŕ_Ő`~IIIv»Ý×˝ÇÇ÷ěŮ3::şČzęëë‹‹‹Y‰ŐjÝ˝{÷řřř"ë@¬KĂb±>|xńŰxěřĂżüËżD˘EÖsŕŔK—.-hîq‰Ĺâ7ß|łĽĽ|‘ť@¬ŔkĚ3Ú—°ÂĘĘĘČČČđđđŁGŹ2%QQQ"‘ČXś?^,‡……ĺććNLL8÷íěěÔétĚZQUU•H$Љ‰©®®&„ŚŽŽJ$’şş:Š˘t:ťË¦ąŰĽ˙ţűďľű.óŰŞŞŞ÷ßhhH­Vgdd„‡‡9rÄů«™mąôřńă[·nĺçç;á­·Ţb~ţýď_]]ýäÉ“ýű÷‡††®_żţĂ?d~•••uőęU©T* Íf3!dçÎťE9×Ő¸{ iµÚĚĚĚĐĐPfłŮŽýŔ7oŢśššrëŤ °"ađOx<đćž·T­L&ëííuľěęę’ËĺfłY.—wttttt$''?xđ`ć^‰‰‰z˝>;;{||ś¦éîîn•JeµZm6›Z­îďď§iş©©éŕÁs·ÎÚfrr2%%ĺÉ“'ÓÓÓ©©©ß˙˝Ůl …Áfł)•ĘÎÎN—mq}ůĺ—żţőŻť/>|¸iÓ&‡Ăa·Ű7oŢüý÷ß;e47oŢĚü|üřń={öôööÚíöééi¦°ĄĄĄ¨¨Użs/łŮrçÎť‘‘‘ččh«Ő:DZ˙ęWżjii™űś€Ďńü/ÂÂráŔ"-~‘Ś©ö4ˇZMMMyyąŐjÍĎĎ?qâ„ÉdJOOŹŤŤ%„hµÚľľľéééôôôŤ7˛v,--Őétk×®%„L&˝^/‹™_ †¸¸8:ž››ŰĐĐľwďŢ©©©­[·¦¦¦BvďŢ=88řěŮ3wÚzôčQdd¤óedd¤N§űë_˙úÓO?ĺććFDDLLL755MMMI$fłŕŕಲ2™L6[]î•’’˘Ńh!ńńńs/›­_żţáÇ ;)ź,«Ĺü{=łŹ;‘‘qíÚµ›7oÖÖÖ'$$477[­VłŮÜÚÚš”””ŘÜÜ<<<ĚÚ±ąąŮd2544Bd2™FٱŮlLgrss !EY,‡Ă1666[ëÜm^ýő?ýéO}ôŃoĽÁ”L&ŁŃřđáĂööv©Tę˛-®µkײnäíµ×jkk˙üç?żöÚk„[·nMNN~÷Ýw_|ńĹŹ?ţčp8Í"""ć8].÷óclllÝşusÔ0/Äj~€ąkm‘Q!Äápdgg …B±XĽk×®ččč´´´ÂÂB…BˇT*KJJŠŠŠT*Uhhčľ}űśĐęŐ«/^ĽxöěYŁŃ(—Ëóóó EXXXAAÁ$'' ¬Yłf¶űŐ\nşiÓ&¦D*•–––JĄRµZ­T*]¶ĹĄR©:::fžĄR9==˝víÚ„„BČîÝ»‡‡‡·l٢×ë“’’ś·ëÍÔŘŘHQTzzúĺË—)Š:sćŚ;{Ívě4MwttěرcÖ÷Ŕ Ô"/{?ĹüŮóu/`Ĺń`ŕąó‰§_Źg›ÍVXXřî»ď2ź*Z,–ĽĽĽ»wďzPŐ«ŻľúŹ˙řŹÎŻřÜ•+WľüňËŹ>úČ×yđü ÂşŻ-~-Í'ěv;ĹńôéSÖfűŰßâââ´Z-¨-ŇŮłgĎź??Ç'°Ëiddä>8{ö¬Ż;~Ź×$€÷đüż(T^xĎ‹Áó+ëj+''‡˘¨cÇŽÍ,\’dÜŕďfć÷?tčĐĚĄŻC‡B.]şżvíÚßýîw„{÷î1ż]»vmQQ뎱_ţň—===ÜV¸5»SĎłgĎŽ?ůűß˙žÂ-!„$%%Y,—G7[ÜäΞ{î9ć@BBB–¶u<í¸«¬ĆĆĆ7n° çMĆíŐ”â<13cÂҦđ3óű×ŐŐŃ4]UUUUUEÓt]]Ýđđđ‰'®\ąb2™z{{™[Ç233išţî»ď?~ü—żüĹYUccă¦M› ·nÍîÔÓŢŢţí·ßöőőŤĆćććoľů†[2ǡÍŃźĄňý÷ßo۶ŤůlšŐ-ľu<í¸«yKEEENNNllě©S§"""ÚÚڸ۸™ž›[ÂM´íV2nÂI_îNÚqđw¬üţ\_~ůĺľ}űvîÜ)‰ţíßţmćťdĎž=s8ÎŤ'Ožś={¶˘˘‚b±Xś[jµÚţţţ9ú0G="‘hxxŘ`0¬_żţîÝ»©©©Ü’ŮŞťYYş'4°ęűꫯ(ЉDď˝÷Ţl­{ O;ÄjŢ"Ź9211QYYé2VÝÝÝ~řa˙˝{÷şşşzzzzzz ĂĺË—¸%„-[¶TWWßľ}űŃŁGÎüóşwď^KKËĚ’O>ů䫯ľzüřń… !"‘Č™zűÖ­[‹>ŔGz˝~ÇŽs,*ŹŤŤmذUř˙ńEmÝş5..®  €)ü×ý×_˙ú× zţćĽőČd˛şşş .$''żńƇ[2[ĺ¬ţ°¦¸ű÷ď_ąrĄ««ëŰożýěłĎ:;;;;;kkkőzý?üđůçź;­őôôś>}úúőëLr]V=[¶laŐľţúë?˙ůĎß~ű­ÇgĂ%Š˘věŘŃŃѱČz `ŕą^¤R©&&&$ITT”ŃhtąÍĽéąišć&ěž7Ѷ›XéËY›ż išů+Ĺó›IĽ„•ßźkÆ wîÜafffŢĽysfÉŘŘŘ_ţň—ÖÖVn ÓÓÓłUîN=Z­V«Ő>}ú´¨¨¨®®®¬¬Ś[­™[Ď’<ˇ[ŹsłÍ›7ďÝ»÷ż˙űżSRRć8ŔÓ`&¬«yQPP™ďލyÓsĎ–°{îDŰnbĄ/'ĆÍďϲwďŢ[·nÝ˝{wbbâ•W^ąvíšËÍţřÇ?ţáf^ …ÂÁÁÁGŹÝżßąÔäV=üńŃŁG'&&~üńÇ   ééin‰;ő%zB·ž»wďţâż°Ůl&“é?˙ó?•JĄËÖO;€™«ůŘĽéąÝLŘÍR__OQTVVVMM EQçÎťă&ăć¦/'ĆÍďĎ"‰.\¸pđŕÁŤ7ýęWżânÓÓÓó?˙ó?ű÷ďw–<˙üóZ­6::úµ×^۱c‡›‹ÖÜzJJJ~řá‡^xá…^ „>|[Âl)‘H/cÖŐŐqëYŞ'4pëŃh4»ví’ÉdżřĹ/~÷»ßĹÇÇs[güö·ż}óÍ7Z‚§ >ňĄĹ¤ç†Ĺp.vbüĂ2ăI~5ľĺ÷‡™đ´ĺÇó[b°®¶L\¦đö^ÍÜäŕ0sMňůĘđ*^ĺ÷‡™đ´ŕâu ŕ=<˙/ OÖŐ`&ž_AXWŠĎ—%€b5ţB¬Ŕ_Č… ËOŢô;řŘżŕTŐ`ůŕożÁ~˙‚‹ €á3PţB¬Ŕ_Őř ±!V•%''‡˘¨cÇŽÍ,¬ŻŻ/..öU—ć€X V–ĆĆĆ7n° 8péŇĄ9ö˛X,‡öfż\C¬Ľ344¤V«322ÂĂĂŹ9ÂVUU‰D˘ęęęŮJ˛˛˛®^˝*•J…BˇŮlvłąť;wR5s]­˛˛2222<<üčŃŁ„ŃŃQ‰DRWWGQ”N§[˛ăpň«ď‚îîîööv‰D˛wďŢ®®®Ő«W744ôôôeggçĺĺýđì’¸¸¸-[¶TWWßľ}[*•»ŮÜ˝{÷Z[[?ţřcgÉ'ź|ňŐW_Éd2ćĄH$jjjŞ«««««[ňb5࣭[·¦¦¦BvďŢ=88řěŮ3˝^/‹™ß š¦Y%qqqÁÁÁeeeÎËc555ĺĺĺV«5??˙ĉ‹¬ `1đ(đ‘Éd2Ť>loo—JĄ2™LŁŃŘl6š¦išÎÍÍĺ–0;FDD,ľőŚŚŚk׮ݼył¶¶vxxBQ”Ĺbq8ccc‹ŻŔ}Ő€Ź¤Riii©T*U«ŐJĄR.—ççç+а°°‚‚‡ĂÁ-q§ÚúúzŠ˘˛˛˛jjj(Š:wî\cc#EQééé—/_¦(ęĚ™3‡#;;[(ŠĹâ]»vEGGB’““Ö¬YűŐ`™QxŠ,Šrw°Y,–ĽĽĽ»wďz»K0÷ß/XśXX(¬«A`˛ŰíÇÓ§O}Ý/€…Áx°L°śŕ_đ~y N,,ÖŐ`Vî<ŐăôéÓĎ?˙ĽP(üôÓO—§W+ ţĂe‚ĺ˙‚÷ËKďÄNOOÇĆĆvvvŠD"Bű© ŔMXWŕ©ŠŠŠśśśŘŘŘS§NEDD´µµq·ńęs>XOőŇjµ™™™ˇˇˇLáőë×W­ZeµZcbbVŻ^ÝŢŢÎt;**J$•——/éůXˇ €§AbbâŽ;l6[eee[[ŰĎ~ö3î6Ţ{Îë©@Ż×755ĹÇÇ+•Ę‘‘‘—_~yrrRŁŃ|űí·Ě6÷ďßżrĺJWW—@ x饗 ¶oßî˝S° VĺCQ”Ż»ŕgT*ŐÄÄ„D"‰ŠŠ2Ť.·YÎç|¤¤¤h4BH||üÔÔw“É”žžKŃjµ}}}Ő źÂ2ˇÁsć}=d€B‚‚‚Č|˙ç,çs>ÁÜ$$$477[­VłŮÜÚÚš””´Đ&€±€óŇs>¸OőpgŻ´´´ÂÂB…BˇT*KJJ Ĺ˘Ž đ=P€Ŕxß:ôwKţŽŕ9ëjţÁĺÓ8ĽW3žóŔŐ`±rrr(Š:věŘĚBwňgzƬ›Üşě3ř"ĽüňËĚËĽĽ<Š˘V­ÂW ţWHH÷¶ÂčččĹ/Şą¬yŽ3ď˝ Ü·ř–ř7PĎ3,Ç÷8ݸq٬¬lA»ÍćC‡-´ˇ§OźŠĹâáááź~úé§ź~rGn=čł?ňŻk<..ÎÍB˙ĹówÄł s9yÖĂĄ:.ʧ yń˙Ěa] –+&!¤˛˛2222<<üčŃŁ„ŃŃQ‰DRWWGQ”N§›­VFM—Y7Yž==M»4ÖŐ–NGGGrrňś%Ü Ęl6‡„„Üąsgdd$::ÚjµŇł_¬ jćďďňô[ó{±Ě{\¬)Čĺ4Ĺís\Y3ĎŞËÎ쏛çÖŐ`9ÔÔÔ”——˙üç?˙ŕÜÜĹ™Q3::šÉ¨éÎ^ű÷ď_łfŤBˇřé§źŃĺ•HĄRĹÄÄ(•ĘŮΓvU$1iWM&“d5**ę믿6 ÜB3íęsĎ=Ç$ ó`H€űËgăĆŤ¬ÖĹ$¶ŤŠŠš-±­›·žyß_ďő[łÇćmÝeźąŁťuey6Myv~ 0 Vĺ‘‘qíÚµ›7oÖÖÖB(ОX,‡cllĚĺ.žeÔĽuëÖäääwß}÷Ĺ_üřăŹnć‘Ćr¦]ĺ XB‰‰‰ÍÍÍ3O¬Ë Š›Ř–uaşyAqë™÷ýő^ą5s÷r“ljYŁťueą<«žM‰óöb5X”úúzŠ˘˛˛˛jjj(Š:wî7¦ĂáČÎÎ …b±x×®]ŃŃŃ„äää5kÖĚvűg5wďŢ=<<ĽeË˝^ź””tôčQną%KyFÝ’¤]u9$` )•ʢ˘"•Jşoßľńńq7/(Ö…É˝ ÜIëÎűë˝rkćîŵś‰ą}ćöŰ$^É' -mćUo§]Ý´iSż;…ţ ى`ˇ°®.x/íę¸ůŐ˝Ý.źá?<€@†UľÁ; …u5€Ŕ´śö—ÓéÓ§˙řÇ?úşüĹzÚÓ« ééé .ŤĆ‰‰‰śś_wg)­[·.22r˛X,‡^††–Ö.]şä|ŮŘŘxăĆ ö±řĆrfŔW©TLVtBČăÇŹU*•Ëš†göąőđó:ŤŚŚś«qŰš÷ęv™ńźu~ÜyÚËz¸­sźĐŔ݆{~X%.źĐŔ=vîÓXŢ˙ýwß}×ŮŐ÷ß¶-G|‘€Ŕëđg:xđŕť;w~ó›ßĽúę«mmm‡rYs@Â5Î7ËöŽxaź[‰_\§¬¶ÜŮ‹›ńßĺů™÷iÜz¸­sźŁŕ˛‡Üó3Ű›ů´—ď ë±1ôß?¦err2%%ĺÉ“'ÓÓÓ©©©ß˙ý<±Ę×±"¬\L|B“˙ŮłgLľ{ć·¦iVI\\ś3'¸ł&CşŐjÍĎĎ?qâ·!ĄRůÍ7ßLNN2•l۶͙[fÍ^=Xßrf˝'„0Yď·oßνš¸řť˛Úrs/&ă?!„ÉřďňüĚqZ樇պÝnç>ˇŰCîůa•LLL755MMMI$—ÇîŽđđđÜÜ܆††đđđ˝{÷˛Ňö?á3Pđ™eË€źššzýúuŤFłm۶+W®l۶m¶š•›öąüâ:ťŮ–›{±2ţ»˝ţĂşŔJń·żý-..N«Ő"Pó#ţô,”­â¬xG`ˇ°®>“””d·Ű—­ą@Íáŕ_4ŤĹbńu/ü b5X8‡;ř Š˘ćxé×–*żżź>'ŔŰ«Źuvvętşńńqw2‰»ĚlÎĹÚËew€ŔĆ͕Ϻ.¸WśgYř]ć÷çîőĘ+ŻDDDĽřâ‹ÎKĹe=Üg-¬DŢI± 0żÄÄD˝^źťť=>>N»—Iś›Ůś[­Ëüă¬î+®q^qÎşŢ~_¸ąňą×÷Šó, ?ÍÉďĎÝ«łł355ull¬»»;,,Ěl6»ě6«—×2Ŕ „ç€/•––ętşµk×2/çÍ$ÎÍlέÓăüăą XąňY×…D"a]q!!!dáwŮ:k/BFŁY·nÝşuët¸–đ(řRssłÉdjhh`^ΛIś›Ůś[çlůŮxbľ ĘÍ•ďňş`]qÜw˛đN~î^111÷îÝ{ôč‘Á`čí흭۬zp-0«/­^˝úâĹ‹gĎž5ŤÜßr3‰s3›»ł—÷Ź€_¸ąň=».ÜÉÂO8ůýą{©T*…Bń /śŕ/Äjü…X €ż«đb5ţB¬^GQÔ’őĎb±>|xiëäC[,ŐŔëhš¦išú?‹ŻpttT"‘ÔŐŐQĄÓéÂŠŠŠ¨¨(‘HT^^Δdee]˝zU*• …BłŮě˛¤ŞŞJ$ĹÄÄTWWB†††´Zmfffhhhqqńlm± ©ŐꌌŚđđđ#GŽ0…¬š]–pűŔF@ŕâç5îrňYhW›šš<č|ŮŐŐ%—ËÍfł\.ďčč iúřńă{öěéííµŰíÓÓÓÜ’îîn•JeµZm6›Z­îďď7›Í!!!wîÜ‰ŽŽ¶Z­Ü¶¸ĚfłP(4 6›M©Tvvvrkć–¸ěáJŔĎa Ŕ[Č… ŕx›w”é˝DůoM&Szzzll,!D«Őöőőmßľ=88¸¬¬L&“97c•L&˝^/‹™—A­V§¤¤h4BH||üÔÔ”›Řşukjj*!d÷îÝĎž=cŐLÓ4«$..ŽŰĂbŽaąTC `ŕ3P€@ćă9¸}óřĐ(ОX,‡cllŚ’ĐÜÜlµZÍfskkkRRłYDDkÇ™%2™LŁŃŘl6¦3ąąą„@0w[.™L&ŁŃřđáĂööv©TĘ­Ůe[.{¸¸3B€X ĽÎy§ÚţUNNNXłf sYZZZaaˇBˇP*•%%% …ÂťJäry~~ľBˇ +((p8î´ĺ’T*---•JĄjµZ©Trkvł- ˙Ç0‘śŻ{á!‹Ĺ’——w÷î]_wÄ?Ěń^űő0đ¬«,€Ýn§8|Ý)dřx *+ÖŐëjü…X €ż«đrá_ŕ&}.ÄjŔ sßQŽ[Î`ĹÂg ü…X €ż«đb5ţB¬Ŕ_Őř ±!Vŕ/äÂ˙€§ŔĘ„Tŕü…Ď@ř ±!Vŕ/Äjü…X €ż«đb5ţB¬Ŕ_Őř ±!Vŕ/Äjüő˙aš¬ż$ŞęIEND®B`‚libtorrent-rasterbar-1.1.13/docs/hash_distribution.png000066400000000000000000000215131351156116000231250ustar00rootroot00000000000000‰PNG  IHDR ôňžąJ PLTE˙˙˙   ˙Ŕ€˙Ŕ˙îîŔ@ČČAiá˙Ŕ €@Ŕ€˙0`€‹@€˙€˙˙ÔĄ**˙˙@ŕĐ333MMMfff™™™łłłŔŔŔĚĚĚĺĺĺ˙˙˙đ22î­ŘćđUđŕ˙˙îÝ‚˙¶ÁŻîî˙×˙d˙"‹".‹W˙‹p€Í‡Îë˙˙˙˙ÎŃ˙“˙Pđ€€˙Eú€ré–zđ挽·k¸† őőÜ € ˙Ąî‚î”ÓÝ ÝP@Uk/€€€@€@€€`Ŕ€`˙€€˙€@˙ @˙ `˙ p˙ŔŔ˙˙€˙˙ŔÍ·žđ˙đ ¶ÍÁ˙ÁÍŔ°|˙@ ˙ ľľľĚŹK pHYsÄÄ•+ IDATxśíť‹‚ŁŞ1ć˙ży7Q AžÇŞ{7'[hĄÉĚ(ŕ°,‹xjżĐŻE^xËţO‰˙ŠŻý"‚Ŕc1‚l7Šýv±řŹE,±”ľe,ű˛ę(Čpk~QÄy®Kń˙c Š;sYÉŚŻiŞd’çM‚ !™ jňă·Ť‡\b‰Ă®·k,Éśyh2Ţ ű–ÜnĎťušżEż[cIćDČC“9ĹÝK2'B&I¦éö|A^ĹÄ\B2ăkJ†4šÓÓĐH°-ŹŽď¨VÎFż‰}wuqßf­ű®ß4‚Ügq{uM$óSťŮîw–›ŢAîÓóW×D2?…ď>,ţ·ĺěł5‰ž@€¦,ú˙Ę|FP|ަÚS~Aŕjäâ*v),Aŕďń׹ĎâöęšHć§ů6ŻłIŻűô:‚üB2ăkâ;éż‚  Á_ä>‹Ű«k"™źBdlÉŚŻ©ä]¬ĺđC}uż‚aAšrü>Hč{"EĹ ü=ü+úŻVÓ« â˙í;‰WL˙d“Ügq{uM$óSČ>łÍ÷Óí«|Ô䊒_SŃĎ>‡U7٧ )fqĺ}"«r."üMśźIođŢőĂŠMá…cCHf|M|'˝>ćiÓŕęšî“Ě_űÍŠ=hŁ"‚$@€rźĹíŐ5‘̉F HuÉŚŻéşd& ĐH0 ]Ü’LßF HuÉŚŻ‰M:ŔI ;`Aş¸%™ľ!Ť@ę’_›t€“ @w Á‚F·g9ü 8ńw®*©!!HáŮYbé"×Ăk©ĘR‚ž§ĺ«Tx[>GÎN`óG®Ś.ć 嬤vä%§_D˝9Ęnň—xň˛/ęťz{=ž " ż Q]@Őž¶^"Čj«Ô™‰ÜF}^WABKň‹±]sCA̸UžD] ČŞ3˛Ďň ň¸Ť/Xb‰QSňĘhľ”wcS‡Ą‹Đ3Éő- {í´˙i$o~9-×­«|&ú)#ÓťN•ë¨Ďé'0·Ä2Ý{D¶>/ȡ Qť'î3=$ë‚č;äQgžDÔ¤§v… r>ĹŃS˛BŮ^+ČÚěЦíUpJ1NŐ‚¨K˛µ~µ—sO=1WźDL%ŠQžŠBő0o+±iŻâČ‚čóNQAtWÄtÂËNő€¸tÖ -Ě3»¦Ź ň/ů‚ČÉf v&ż­1*îcG›˝ČJ9{S¬’1/'WeÇĄů˝c^ ň/Ú^q»A™"üŮ–ÄVD´ok¶[jD{Al*>v™ ď÷·¦÷§î·Rď÷ű“Ăçń›Íű#ČűËç•÷şľ÷u;ü9˙˝ł·óűÚ˙ĐĎ˙cż'Ľßv½÷ ţ?ůÔü-ňŰđMoôŢ[2Z˝0Ýĺź§{bűË[GţŹR{[Ä÷ŮVű7Z}sÝ*xď«íČŢľýü˝vÝ´Ď9ď=‰o“¶°÷{ł­ÖýA'ĄŢű”T{¦[÷ž1ł]÷á{Ki;ßä˝×őí†u2ťľˇO˘ďo+tKöŢŢ+Ü«_uÔÖŘ·Ľ@ě])˘ç¤N@í |Żćđ.îBS¦äýֵ蹴 ÚŰL´ŐÎ*“ši‘žŠ[rp_ú/±6I÷ëÉ~˝ÝŻzë~?0XsŐSo%®żŰ•äýŢŻćSîŞ/łú©]ÚĽM™úĘ).Ţoýő67Ř÷–ś{Y·kć÷ş˛•iîQ[„ľ!ľőĺgĎVí—Ó˝†o3ßű‹ú˘÷­foąÔë•ÜVó7Z)Ý˝6x›µá÷Ňľőě[ßőÝŕmoJęm.´ş—ĚÎĆÜ=í5x_›Ľí]xµ©®˛Ţ[Íf9ĽÝž”í}“Ńv§ŮŇ_őMü;šű CwĘÖ¤ďŘl ő¶K¬ýÖ˛WŁěúŇÜjĹýTéf|§ŃŞoY{żÚ%Ö>ßzÜ>Ĺ_ľ9 ˘× zV AVëZß¶”€ zi»:+Ô}&Ůű¸đ ův›^b­Ž J ˘u2‰Żv`÷Ť]ŐŠu´Ä9Ĺé ˝3[ łFĎ "ÎpŰą/Ö„ fý-1‰÷Ţ€ zéhÚŞ»p= ˘—+Jt‘î°]»9Ý&ŚY(ŻĆyS€d_ކ1Ëż¸ kTŐ ˛ŽÄLa#ܤëYˇŰ´ÚWd)Ę„9ţ2ŰD%ëPF˝*­v:Ů~v¶ůęM§®Q2ŘŇô<2‚ř5|ţ÷rа¤ˇľźĐgë!4Ózo˝ąĘu“ýâM7iAT~“ľ˙lŻą9ăé¬ őA¸ Ó ň˛ĄšŢ2RHAL3”˛y›¤k6“IÉ8]śé,;-Ěť˙ól*A„¦-V;\z^HAVG;=ÚbGÇÔč bŻ|¶G9’˝±Sę´ 6:&ÎOdâŢĚ› ˘‡ŔÄÎ}!ž/Ž(v^™ŃöM'hŹ™îş-‚Řó1łÂśmó°łR)Wwz)= ~ÄL&“ULcë^iVŻň¤ «ůZ b'Çj#äsť ěgŮ¦Ł ^í¦×ň‚(§5ŁYçD&oçBXeJ±Zóµ±eA쑌 zćW bfŞ’/¬6» “·Ä•Âv’ÄLঠ‡ÚmĘQ¦+DÉ$ď"ŃZ¤¨“6ÓŔÄôKDŰp_Z-ĽF˙ ČŢlq¤B;M-aAlĽŃ3' ~PnČöőDŮFžD+1ˇ ÎĐ©ďovj!Č+!ČŢ!‡=H3A„„J¤™äD˙G©ý3ÁNÍ˝1¸¸ ŻAL×1/8‚hÖ}R*LˇBŁ„ÄΔë±Öö–+ČŢ7ÎťęgŃĂÔXˇÁťÓ@OÓMŰŠŐÄ^Çb‚ţď$ną93+™T‚(ŰG×b« ˘}řQe‡HŚ—5Ý,t(ÄćzVý‚/™ćö§#”xnk>)NÄÎ{k¨śŔAśMbe b_0Ex‚ŘY,zK7¬X[˝-W‰˘ł‚¨1‚ضśD™žĐ±¦*DÉW=i˛É[w’Ţ«±í°Z†y].ߍKU/'Ő‚ňżđ»ź ˛!gYD÷JF#Rµ ¶ÓóĹ‚¸żG~€ b~|˝čÖ bĽg«lRVqŠť‰r¬ś2íě6ĂŐTŰů^㕜4Aäó€ ně)ADÂ{m˛^“pNŮCiAŽĄ›WŚ>AÜésDţąI•D™ö‹ü~}AĚ}ą?+ěw=Ĺ‚(éýę(ßřA˘ČŮR.®Ş± rĽ^˛Ă{$Ô w&*ń+ "ľžDý¤ą N\#A̬Ź®'Ä;í¤ ¶&[Ą'<ÇÄvPŤ Jś+ŞTţ,Gb IíA‚ç†9îAäYş&w˘ę࣠6ć(ČK)– ˘łIěAĽŠä™qAô‹ëa®żň‚Ľ'y%Ů‹q›ÄmSP˝őňĚ*Äë)!b˙Ľ b¦ü.-úZAôś"8ŮÄťČ'±DuÄď˝Ő}.ňť\ŕ=+Ä)BvŮA' DoÚ»„Ć/*Hčüú%– «ÄÎÔŕ%u?+"ň&ń‹>”{lM@]Ŕ ‚\ôWn•S÷YAÖ5:ű:ÝŚ Çör•‚Hó˘/Č~ ŕyJŻÜŰ ®Ę­Ö-94#Ő-Q1/4$N¸Ś‘‚¸g%q‹WnďůĎäů·D”5Ł µ{ /ď”ÜD]'ŮÄyŮ×bňŤ„„ňŤÄD±{ ź×{c•=ȱó…}uő“qĎ Ňbjgäą-]ÎGV‚8ŐßUóڱʫ± Č "ł8 ’Ůx! bĽgqA˘·í´ Á“ęIƉ|ťĚ·Ô52,Ę rhH´Ę¨ áóë—X¶é-Hŕĺś ę΂¸Ç ›ă©%–¨Äa„ öy± ń±ţEjî1O•ę´Ľ â[ ’Ę,Đ™k´ć? H€}q›:şńŠĽ.cĎîAľÇÍŠo¸IS»‰Ô™Ůí|ŽĽň¦şM ‡×ďAT@HϬŽhĎ RÎĚ‚$–Ńô‚Ţ18ÖßGÔ$]“dAŞ©?«f‰•Dśtv‰őĹK¬ŇÓ˛k ŕI…‡‚„ę/«Ľh˝š$‚Îx¬ ™ţ  m¨dYńtńĎ_Ľ×{x¦ Ö¨5‚dJ!Čo¤´ň{˛s–Ă‘˙‹{¬ ü ö 6$7á+÷ ~ěAŞkŞŰ”Ĺř‚¤‹)ß$C¦Džłß(öŰĹ"ţŮbg$ÁšqŠAxL|I{ť Ăö Îä_ôă×–€ _>‰Ľš=lźÖiWŢţ°Ö¦q|m]űgę×Ѧ;gşęgëZšA6®aQeKh‘Ç_XéRüˇč6ôX٫첺$ľSf}ëřiRUx“ †őŐPżI÷ż”» i’IşZ঍Ť_6éß˙.r‰%»~ÜiR›LÁ¤G2Í÷ íB‚ß,(¦K“Q-Ů™+˝=wÖi C¤"f  mŠűŤB¨çŠe\;®^b ä™üAAfAîĆ˝¦Ü˝˛ 0 wÝL2{2E{>É´AŞCH¦"f  ěA`~.± ;`AţŔJ{\ÉtAŞCHf|MlŇN‚ ÝA€ňĐĹ-Éô i‚T‡ĚřšŘ¤śAş &䡋[’éŇ©!™ń5±I8 ‚tAL ČC·$Ó7¤RB2ăkb“pč‚$@‡.nI¦oH#¤:„dĆ×Ä&ŕ$ĐH0 ]Ü’LßF HuÉŚŻ‰M:ŔI ;`Aş¸%™ľ!Ť@ę’_›t€“ @w’x9?ÁnMjý89ĂŮô !™Î¤9 Aľ!$Óą čÁ›tö đtx An_pyčâ–dú†4"ó.›ô‘5‘̉K6陀XbÁ z ˛lTdTW>Ŕ ČŢA:–0=Ľ‹őĐĹ-Éô i‚T‡Ěřšćؤ—t=ŕW.Ú¤'7)ÇŁ‹>3[>Ŕ H’ Řdpă˙4[ÓŕmÜýv±¶Xö }CH¦3ç·›۲* Č—O{^ćá5QČ˙©2OČ–ň$!çJ¶™ďʇ˙ĎF ŕŐ$äÔ‡ť›‚ŔäĚŰĽË1`ńü@¸7§ŮWÎ2Ě˙€#›ôľ!$Ó™ě¤ců;·ëy’^ÓuÉLđQ€\ ?“Og‚ß‹0/üÚź‡.nI¦oH#¤:„dĆ×4É&ť=ÜŢĹčNúr~z#Üö Ő!$3ľ&6éő1ť$Ó· čÁďbń‹ăŕáđ.@‚üDu,㡋[’éŇ ~őčízžd†×4Ë&ý<,±`×|Ôä,·ćÜo59W>ŔôLđ.ÖC·$Ó7¤RB2ăkšc“Ţ–X0č‚$@‡.nI¦oH#¤:„dĆ×4Ĺ&˝‰;,±`˝Y’GKA¸5 ÷ËŰ/ř¨ÉC·$Ó7¤é |Éäv=O2Ăkšb“Ţ–X0+>îÎϤĂĂÉţŔw‡'3 ]Ü’LßF HuÉŚŻi’M:{¸/Ľ‹ĐH0 ]Ü’LßF HuÉŚŻi’MzXbÁ  ;`‚_=úĐĹ-Éô iÄżĽúv=O2Ăkšc“Îow‡sĹGMJséZŔ8říî &x롋[’éŅ̌yű–żq»ž'™á5ͲIçď¤ĂmA€î @‚ ~˘đˇ‹[’éŇŢĹŞ!™ń5ͱIßAç°|ń^g‰¸čŁ&÷¸˝¸ÇnM­ ‹wŮţŁ—ĂY·&÷Y¬äKia–}YäËçVöú3݉BţŻć ŮRž$äüCćŁVKäĎř +ä˙ EyĺCţâîŹdú†\±I/8Ă.ą~ ’¸,‹\b‰®÷¦ö¦Ě‚ĘnωüŤ=‚Ŕ­á;éŐ!$3ľ¦’dÚ0ÁŹÜŢ®çIfxMslŇů‰B¸1ŁŢĹŞAŕÖpH›ŔlŇÖD2'B‘ťŔýď ·ëy’^Ó›ô˛€®§üĘEďbçS_>Ŕôđ.@‚úĎb5,㡋[’éŇľ“^B2ăkšc“~‘ =¸âăt-`|' Áďb=tqK2}C Ő!$3ľ¦96é-`‰@€î @‚ yčâ–dú†4AŞCHf|MlŇN‚ ÝA€ňĐĹ-Éô i‚T‡ĚřšŘ¤śAş &䡋[’éŇ©!™ń5±I8 ‚tAL ČC·$Ó7¤RB2ăkb“pč‚$@‡.nI¦oH#¤:„dĆ×Ä&ŕ$ĐH0 ]Ü’LßF HuÉŚŻ‰M:ŔI ;`Aş¸%™ľ!Ť@ę’_›t€“ @w Á‚¨‹ 6"Ý)ȧÁśĚŹß’żfÉäKąź ůbl:Q™­Í^m*j±ĹDw´ą’ĽËq<$ťŹs4·űK5ËďůpČÉ´j’;Űâ~\’L‹>L»ĚLţ™L©Î;ráăeď&KjSŠw42S¶ŠŇÍĘ–˛ŢÉ4jŇ"˙“t¨2öîŤŃçÇŠ M>~Ŕő>3”> đ$ Á"~\Ńühťůp«ů€g˛x?,±ŘŤĐŞ<—ăOďkXO@sQOÇ,±äĎ´/Rłěżrn‘›tó\/¸Á?<řů3.9}ęIEND®B`‚libtorrent-rasterbar-1.1.13/docs/img/000077500000000000000000000000001351156116000174475ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/docs/img/bg.png000066400000000000000000000053251351156116000205520ustar00rootroot00000000000000‰PNG  IHDR&u2Á AiCCPICC ProfileH ť–wTSهϽ7˝Đ" %ôz Ň;HQ‰I€P†„&vDF)VdTŔG‡"cE ‚b× ňPĆÁQDEĺÝŚk ď­5óŢšýÇYßŮç·×Ůgď}׺Pü‚ÂtX€4ˇXîëÁ\ËÄ÷XŔáffGřDÔü˝=™™¨HĆłöî.€d»Ű,żP&sÖ˙‘"7C$ EŐ6<~&ĺ”SłĹ2˙Ęô•)2†12ˇ ˘¬"ăÄŻlö§ć+»É—&äˇYÎĽ4žŚ»PŢš%ᣌˇ\%ŕgŁ|e˝TIšĺ÷(ÓÓřśL0™_Ěç&ˇl‰2Eî‰ň”Ä9Ľr‹ů9hžx¦g䊉Ib¦×iĺčČfúńłSůb1+”ĂMáxLĎô´ Ž0€Żo–E%Ym™h‘í­ííYÖćhůżŮß~Sý=ČzűUń&ěĎžAŚžYßlě¬/˝ö$Z›łľ•U´m@ĺá¬Oď ň´Ţśó†l^’Äâ ' ‹ěělsźk.+č7űź‚oĘż†9÷™ËîűV;¦?#I3eE妧¦KDĚĚ —Ďdý÷˙ăŔ9iÍÉĂ,śźŔń…čUQč” „‰h»…Ř A1ŘvjpÔzĐN‚6p\WŔ p €G@ †ÁK0Ţi‚đ˘Aޤ™BÖZyCAP8ĹC‰’@ůĐ&¨*ŞˇCP=ô#tş]ú Đ 4ý}„Óa ض€Ů°;GÂËŕDxśŔŰáJ¸>·Âáđ,…_“@ČŃFXńDBX$!k‘"¤©Eš¤ąŤH‘q䇡aĆă‡YŚábVaÖbJ0ŐcVLć6f3ů‚ĄbŐ±¦X'¬?v 6›Ť-ÄV`Ź`[°—±Řaě;ÇŔâp~¸\2n5®·׌»€ëĂ á&ńxĽ*Ţď‚Ásđb|!ľ ߏĆż' Zk‚!– $l$Tçý„Â4Q¨Ot"†yÄ\b)±ŽŘAĽI&N“I†$R$)™´TIj"]&=&˝!“É:dGrY@^O®$ź _%’?P”(&OJEBŮN9Ją@y@yCĄR ¨nÔXŞşťZO˝D}J}/G“3—ó—ăÉ­“«‘k•ë—{%O”×—w—_.ź'_!Jţ¦ü¸QÁ@ÁSٰVˇFá´Â=…IEš˘•bbšb‰bâ5ĹQ%Ľ’’·O©@é°Ň%Ą!BÓĄyҸ´M´:ÚeÚ0G7¤űÓ“éĹôč˝ô e%e[ĺ(ĺĺĺłĘRÂ0`ř3RĄŚ“Ś»ŚŹó4ćąĎăĎŰ6Żi^˙Ľ)•ů*n*|•"•f••ŹŞLUoŐŐťŞmŞOÔ0j&jajŮjűŐ.«ŤĎ§ĎwžĎť_4˙äü‡ę°ş‰z¸újőĂę=ꓚľU—4Ć5šnšÉšĺšç4Ç´hZ µZĺZçµ^0•™îĚTf%ł‹9ˇ­®í§-Ń>¤Ý«=­c¨łXgŁNłÎ]’.[7A·\·SwBOK/X/_ŻQďˇ>Qź­ź¤żGż[ĘŔĐ Ú`‹A›Á¨ˇŠˇżažaŁác#Ş‘«Ń*ŁZŁ;Ć8c¶qŠń>ă[&°‰ťI’IŤÉMSŘÔŢT`şĎ´Ď kćh&4«5»Ç˘°ÜYY¬FÖ 9Ă<Č|Ły›ů+ =‹X‹ťÝ_,í,S-ë,Y)YXm´ę°úĂÚÄšk]c}džjăcłÎ¦Ýćµ­©-ßvżí};š]°Ý»N»Ďöö"ű&ű1=‡x‡˝÷Řtv(»„}Őëčá¸ÎńŚă'{'±ÓI§ßťYÎ)Î ÎŁ đÔ-rŃqá¸r‘.d.Ś_xpˇÔUŰ•ăZëúĚM׍çvÄmÄÝŘ=Ůý¸ű+K‘G‹Ç”§“çĎ ^—ŻW‘WŻ·’÷bďjď§>:>‰>Ť>ľvľ«}/řaýývúÝó×đçú×űO8¬ č ¤FV> 2 uĂÁÁ»‚/Ň_$\ÔBüCv…< 5 ]ús.,4¬&ěy¸Ux~xw-bEDCÄ»HŹČŇČG‹ŤKwFÉGĹEŐGME{E—EK—X,YłäFŚZŚ ¦={$vr©÷ŇÝK‡ăěâ ăî.3\–łěÚrµĺ©ËĎ®_ÁYq*ß˙‰©ĺL®ô_ąwĺד»‡ű’çĆ+çŤń]řeü‘—„˛„ŃD—Ä]‰cI®IIăOAµŕu˛_ňä©””Ł)3©Ń©Íi„´ř´ÓB%aа+]3='˝/Ă4Ł0CşĘiŐîU˘@Ń‘L(sYf»ŽţLőHŚ$›%Y łj˛ŢgGeźĘQĚćôäšänËÉóÉű~5f5wugľvţ†üÁ5îk­…Ö®\ŰąNw]ÁşáőľëŹm mHŮđËFËŤeßnŠŢÔQ Q°ľ`hłďćĆBąBQá˝-Î[lĹllíÝfł­jŰ—"^ŃőbËâŠâO%Ü’ëßY}WůÝĚö„í˝ĄöĄűwŕvwÜÝéşóX™bY^ŮĐ®ŕ]­ĺĚň˘ň·»WěľVa[q`iŹdŹ´2¨˛˝JŻjGŐ§ę¤ęŹšć˝ę{·íťÚÇŰ׿ßmÓŤĹ>ĽČ÷Pk­AmĹaÜá¬ĂĎë˘ęşżg_DíHń‘ĎG…GĄÇÂŹuŐ;Ô×7¨7”6ÂŤ’ƱăqÇoýŕőC{«éP3Łąř8!9ńâÇřďž <ŮyŠ}Şé'ýźö¶ĐZŠZˇÖÜÖ‰¶¤6i{L{ßé€ÓťÎ-?›˙|ôŚö™šłĘgKϑΜ›9źw~ňBĆ…ń‹‰‡:Wt>ş´äŇť®°®ŢË—Ż^ńąr©Ű˝űüU—«g®9];}ť}˝í†ýŤÖ»ž–_ě~iéµďm˝épłý–ă­Žľ}çú]ű/Ţöş}ĺŽ˙ť‹úî.ľ{˙^Ü=é}ŢýŃ©^?Ěz8ýhýcěă˘' O*žŞ?­ýŐř×f©˝ôě ×`Ďłgʆ¸C/˙•ůŻOĂĎ©Ď+F´FęG­GĎŚůŚÝz±ôĹđËŚ—Óă…ż)ţ¶÷•Ń«ź~wű˝gbÉÄđkŃë™?Jިľ9úÖömçdčäÓwi獵ŠŢ«ľ?öýˇűcôÇ‘éěOřO•źŤ?w| üňx&mfćß÷„óű2:Y~ pHYs  šś:IDAT8cdnc^a†ĎoÉ`02dĚi#0¬…ŘL‰\‹ˇúFÝOV°Ăcm4üGÓ˙HÎż†3ËëxpU¶IEND®B`‚libtorrent-rasterbar-1.1.13/docs/img/blue_bottom.png000066400000000000000000000315211351156116000224720ustar00rootroot00000000000000‰PNG  IHDR>ëፗ pHYs  šś MiCCPPhotoshop ICC profilexÚťSwX“÷>ß÷eVBŘđ±—l"#¬ČY˘’a„@Ĺ… VśHUÄ‚Ő Hťâ (¸gAŠZ‹U\8îܧµ}zďííű×űĽçśçüÎyĎ€&‘ć˘j9R…<:ŘŹOHÄÉ˝€Hŕ ćËÂgĹđyx~t°?üŻopŐ.$Çá˙şP&W ‘ŕ"ç RČ.TČȰSłd ”ly|B"Ş ěôI>Ř©“Üآ©Ť™(G$@»`UR,Ŕ ¬@".Ŕ®€Y¶2G€˝vŽX@`€™B,Ě 8CÍ L 0Ňżŕ©_p…¸HŔ˕͗KŇ3¸•Đwňđŕâ!âÂl±Ba)f ä"ś—›#HçLÎ ůŃÁţ8?çćäáćfçlďôŢţkđo">!ńßţĽŚNĎďÚ_ĺĺÖpǰużk©[ÚVhßů]3Ű  Z Đzů‹y8ü@žˇPČ< í%bˇ˝0ă‹>˙3áoŕ‹~öü@ţŰzđqš@™­ŔŁýqanv®RŽçËB1n÷ç#ţÇ…ýŽ)Ńâ4±\,ŠńX‰¸P"MÇyąR‘D!É•âé2ń–ý “w ¬†OŔN¶µËlŔ~î‹XŇv@~ó-Ś ‘g42y÷“żůŹ@+Í—¤ăĽč\¨”LĆD *°A Á¬ŔśÁĽŔaD@ $Ŕ<Bä€ ˇ–ATŔ:ص° šá´Á18 çŕ\ëp`žÂĽ† AČa!:bŽŘ"ΙŽ"aH4’€¤ éQ"ĹČr¤©Bj‘]H#ň-r9Ť\@úŰČ 2ŠüŠĽG1”˛QÔu@ą¨ŠĆ sŃt4]€–˘kŃ´=€¶˘§ŃKčut}ŠŽc€Ń1fŚŮa\ڇE`‰X&ÇcĺX5VŹ5cX7vŔžaď$‹€ě^„Âl‚GXLXC¨%ě#´şW „1Â'"“¨O´%zůÄxb:±XF¬&î!!ž%^'_“H$É’äN !%2I IkHŰH-¤S¤>ŇiśL&ëmÉŢ䲀¬ —‘·O’űÉĂä·:ĹâL ˘$R¤”J5e?奟2B™ ŞQÍ©žÔŞ:źZIm vP/S‡©4uš%Í›Cˤ-ŁŐĐšigi÷h/étş ÝE—Đ—Ňkčéçéôw † ÇHb(k{§·/™L¦Ó—™ČT0×2™goUX*ö*|‘Ę•:•V•~•çŞTUsU?ŐyŞ T«U«^V}¦FUłPă© Ô«Ő©U»©6®ÎRwRŹPĎQ_Łľ_ý‚úc ˛†…F †HŁTc·ĆŤ!Ć2eńXBÖrVë,kMb[˛ůěLvűv/{LSCsŞf¬f‘fťćqÍƱŕđ9ŮśJÎ!Î Î{--?-±Öj­f­~­7ÚzÚľÚbírííëÚďupť@ť,ťő:m:÷u ş6şQş…şŰuĎę>Ócëyé őĘőéÝŃGőmôŁőęďÖďŃ7046l18cđĚcčki¸Ńđ„á¨Ëhş‘ÄhŁŃIŁ'¸&î‡gă5x>f¬ob¬4ŢeÜkVyVőV׬IÖ\ë,ëmÖWlPW› ›:›Ë¶¨­›­Äv›mßâŹ)Ň)őSnÚ1ěüě ěšěí9öaö%ömöĎĚÖ;t;|rtuĚvlpĽë¤á4éĩĂéWggˇsťó5¦KË—v—Sm§Š§nźzË•ĺîşŇµÓőŁ›»›Ü­ŮmÔÝĚ=Ĺ}«űM.›É]Ă=ďAôđ÷XâqĚăť§›§Âóç/^v^Y^ű˝Ołś&žÖ0mČŰÄ[ŕ˝Ë{`:>=eúÎé>Ć>źzź‡ľ¦ľ"ß=ľ#~Ö~™~üžű;úËýŹřżáyňńN`Á彳k™Ą5Ť»/ >B Yr“oŔňůc3Üg,šŃĘťZú0Ě&LÖކĎß~o¦ůLé̶ŕGl¸i™ů})*2Ş.ęQ´Stqt÷,Ö¬äYűg˝ŽńŹ©Śą;Űj¶rvg¬jlRlc웸€¸Ş¸x‡řEń—t$ í‰äÄŘÄ=‰ăsçlš3śäšT–tc®ĺܢąćéÎËžwç|ţü/÷„óű%Ňź3gAMA±Ž|űQ“ cHRMz%€ů˙€éu0ę`:o’_ĹF(nIDATxÚěÔMJBa†Ń×›HšD%4ĎYÓÓŢZLc'á…MÂ?.ę× EzΞÁÓx˙•·×çś‹JŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă“0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>Ŕř$ŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0> ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚOŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă“0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>Ŕř$ŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0> ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚOŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă“0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>Ŕř$ŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0> ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚOŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă“0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>Ŕř$ŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0> ă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>ă0>ă0>ă0>ă0>ŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřă0>ă0>ă0>ă0>ă0>ă0>ă0>ăŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚŔřŚ0>ă0>ă0>ă0>€˙_ë˘J}8*ś…úpLusu™ĺz§p–ë]Ş—§ű Çł”"pÚJI†ăYĄ”ň9™gň˝Ę —^·ťN«©p26ő>óŐ6Łé"Ź·Ýżń%É×Ď&Łé"óŐ6›zŻp2:­fzÝvý»<\wň;ă‚\ 7?IEND®B`‚libtorrent-rasterbar-1.1.13/docs/img/blue_top.png000066400000000000000000000056111351156116000217710ustar00rootroot00000000000000‰PNG  IHDR? Dň+Ż pHYs  šś MiCCPPhotoshop ICC profilexÚťSwX“÷>ß÷eVBŘđ±—l"#¬ČY˘’a„@Ĺ… VśHUÄ‚Ő Hťâ (¸gAŠZ‹U\8îܧµ}zďííű×űĽçśçüÎyĎ€&‘ć˘j9R…<:ŘŹOHÄÉ˝€Hŕ ćËÂgĹđyx~t°?üŻopŐ.$Çá˙şP&W ‘ŕ"ç RČ.TČȰSłd ”ly|B"Ş ěôI>Ř©“Üآ©Ť™(G$@»`UR,Ŕ ¬@".Ŕ®€Y¶2G€˝vŽX@`€™B,Ě 8CÍ L 0Ňżŕ©_p…¸HŔ˕͗KŇ3¸•Đwňđŕâ!âÂl±Ba)f ä"ś—›#HçLÎ ůŃÁţ8?çćäáćfçlďôŢţkđo">!ńßţĽŚNĎďÚ_ĺĺÖpǰużk©[ÚVhßů]3Ű  Z Đzů‹y8ü@žˇPČ< í%bˇ˝0ă‹>˙3áoŕ‹~öü@ţŰzđqš@™­ŔŁýqanv®RŽçËB1n÷ç#ţÇ…ýŽ)Ńâ4±\,ŠńX‰¸P"MÇyąR‘D!É•âé2ń–ý “w ¬†OŔN¶µËlŔ~î‹XŇv@~ó-Ś ‘g42y÷“żůŹ@+Í—¤ăĽč\¨”LĆD *°A Á¬ŔśÁĽŔaD@ $Ŕ<Bä€ ˇ–ATŔ:ص° šá´Á18 çŕ\ëp`žÂĽ† AČa!:bŽŘ"ΙŽ"aH4’€¤ éQ"ĹČr¤©Bj‘]H#ň-r9Ť\@úŰČ 2ŠüŠĽG1”˛QÔu@ą¨ŠĆ sŃt4]€–˘kŃ´=€¶˘§ŃKčut}ŠŽc€Ń1fŚŮa\ڇE`‰X&ÇcĺX5VŹ5cX7vŔžaď$‹€ě^„Âl‚GXLXC¨%ě#´şW „1Â'"“¨O´%zůÄxb:±XF¬&î!!ž%^'_“H$É’äN !%2I IkHŰH-¤S¤>ŇiśL&ëmÉŢ䲀¬ —‘·O’űÉĂä·:ĹâL ˘$R¤”J5e?奟2B™ ŞQÍ©žÔŞ:źZIm vP/S‡©4uš%Í›Cˤ-ŁŐĐšigi÷h/étş ÝE—Đ—Ňkčéçéôw † ÇHb(k{§·/™L¦Ó—™ČT0×2™goUX*ö*|‘Ę•:•V•~•çŞTUsU?ŐyŞ T«U«^V}¦FUłPă© Ô«Ő©U»©6®ÎRwRŹPĎQ_Łľ_ý‚úc ˛†…F †HŁTc·ĆŤ!Ć2eńXBÖrVë,kMb[˛ůěLvűv/{LSCsŞf¬f‘fťćqÍƱŕđ9ŮśJÎ!Î Î{--?-±Öj­f­~­7ÚzÚľÚbírííëÚďupť@ť,ťő:m:÷u ş6şQş…şŰuĎę>Ócëyé őĘőéÝŃGőmôŁőęďÖďŃ7046l18cđĚcčki¸Ńđ„á¨Ëhş‘ÄhŁŃIŁ'¸&î‡gă5x>f¬ob¬4ŢeÜkVyVőV׬IÖ\ë,ëmÖWlPW› ›:›Ë¶¨­›­Äv›mßâŹ)Ň)őSnÚ1ěüě ěšěí9öaö%ömöĎĚÖ;t;|rtuĚvlpĽë¤á4éĩĂéWggˇsťó5¦KË—v—Sm§Š§nźzË•ĺîşŇµÓőŁ›»›Ü­ŮmÔÝĚ=Ĺ}«űM.›É]Ă=ďAôđ÷XâqĚăť§›§Âóç/^v^Y^ű˝Ołś&žÖ0mČŰÄ[ŕ˝Ë{`:>=eúÎé>Ć>źzź‡ľ¦ľ"ß=ľ#~Ö~™~üžű;úËýŹřżáyňńN`Á彳k™Ą5Ť»/ >B Yr“oŔňůc3Üg,šŃĘťZú0Ě&LÖކĎß~o¦ůLé̶ŕGl¸i™ů})*2Ş.ęQ´Stqt÷,Ö¬äYűg˝ŽńŹ©Śą;Űj¶rvg¬jlRlc웸€¸Ş¸x‡řEń—t$ í‰äÄŘÄ=‰ăsçlš3śäšT–tc®ĺܢąćéÎËžwç|ţü/÷„óű%Ňź3gAMA±Ž|űQ“ cHRMz%€ů˙€éu0ę`:o’_ĹF¦IDATxÚě×± ‚`…áŁH TCRA íŃÔý_C.ŃžP b¤‰¤_Cwá˙>—đŽgf&IYÝęö|©|Ôö_ŔTDł@ń<Ôq·ŇvI’<3łëŁÔĺžSŔäťťö±‚¬n•¤ 7$i®ő"”꤅ţǦĎLJŇB~ŐtÔŕ”Şéä÷ĂH Né‡Q>¸ńŔř€+~˙˙•Ń7°îÉIEND®B`‚libtorrent-rasterbar-1.1.13/docs/img/dotline.gif000066400000000000000000000002651351156116000215770ustar00rootroot00000000000000GIF89a Č‘˙˙˙ŮŮŮ, ČŽŚ#§–{í˘‚Ž.+kĆ/{Îlž(ČX’‘ů©ú®.¦6ÝÝ1~ęź“ůh¦"°&LňŚCS‰{ɱµĄżm5¸ü"ĂÍ1µŚ%gÁçµ™(N·§Z ·®¦łáhVüîôwřŘ7(ču¨č&Ç×č·ŘŘ"™×5™Yyi‰Ç9÷éů¨WBčh‰¸éQ;libtorrent-rasterbar-1.1.13/docs/img/minus.gif000066400000000000000000000004061351156116000212710ustar00rootroot00000000000000GIF89a Ő˙˙˙ĺĺĺâââŕŕŕÝÝÝŰŰŰŮŮŮŘŘŘÖÖÖÔÔÔÓÓÓŃŃŃĐĐĐËËËĘĘĘČČČÇÇÇÂÂÂŔŔŔżżż˝˝˝¸¸¸¶¶¶µµµ´´´±±±®®®­­­ŞŞŞ¨¨¨¤¤¤˘˘˘ťťť^^^, +Ŕ€hH „BÁ …CbŮ ˘Ć3D4"•L4\<&Î'd,A;libtorrent-rasterbar-1.1.13/docs/img/orange.png000066400000000000000000000604011351156116000214310ustar00rootroot00000000000000‰PNG  IHDRźt›ŕˇ pHYs  šś MiCCPPhotoshop ICC profilexÚťSwX“÷>ß÷eVBŘđ±—l"#¬ČY˘’a„@Ĺ… VśHUÄ‚Ő Hťâ (¸gAŠZ‹U\8îܧµ}zďííű×űĽçśçüÎyĎ€&‘ć˘j9R…<:ŘŹOHÄÉ˝€Hŕ ćËÂgĹđyx~t°?üŻopŐ.$Çá˙şP&W ‘ŕ"ç RČ.TČȰSłd ”ly|B"Ş ěôI>Ř©“Üآ©Ť™(G$@»`UR,Ŕ ¬@".Ŕ®€Y¶2G€˝vŽX@`€™B,Ě 8CÍ L 0Ňżŕ©_p…¸HŔ˕͗KŇ3¸•Đwňđŕâ!âÂl±Ba)f ä"ś—›#HçLÎ ůŃÁţ8?çćäáćfçlďôŢţkđo">!ńßţĽŚNĎďÚ_ĺĺÖpǰużk©[ÚVhßů]3Ű  Z Đzů‹y8ü@žˇPČ< í%bˇ˝0ă‹>˙3áoŕ‹~öü@ţŰzđqš@™­ŔŁýqanv®RŽçËB1n÷ç#ţÇ…ýŽ)Ńâ4±\,ŠńX‰¸P"MÇyąR‘D!É•âé2ń–ý “w ¬†OŔN¶µËlŔ~î‹XŇv@~ó-Ś ‘g42y÷“żůŹ@+Í—¤ăĽč\¨”LĆD *°A Á¬ŔśÁĽŔaD@ $Ŕ<Bä€ ˇ–ATŔ:ص° šá´Á18 çŕ\ëp`žÂĽ† AČa!:bŽŘ"ΙŽ"aH4’€¤ éQ"ĹČr¤©Bj‘]H#ň-r9Ť\@úŰČ 2ŠüŠĽG1”˛QÔu@ą¨ŠĆ sŃt4]€–˘kŃ´=€¶˘§ŃKčut}ŠŽc€Ń1fŚŮa\ڇE`‰X&ÇcĺX5VŹ5cX7vŔžaď$‹€ě^„Âl‚GXLXC¨%ě#´şW „1Â'"“¨O´%zůÄxb:±XF¬&î!!ž%^'_“H$É’äN !%2I IkHŰH-¤S¤>ŇiśL&ëmÉŢ䲀¬ —‘·O’űÉĂä·:ĹâL ˘$R¤”J5e?奟2B™ ŞQÍ©žÔŞ:źZIm vP/S‡©4uš%Í›Cˤ-ŁŐĐšigi÷h/étş ÝE—Đ—Ňkčéçéôw † ÇHb(k{§·/™L¦Ó—™ČT0×2™goUX*ö*|‘Ę•:•V•~•çŞTUsU?ŐyŞ T«U«^V}¦FUłPă© Ô«Ő©U»©6®ÎRwRŹPĎQ_Łľ_ý‚úc ˛†…F †HŁTc·ĆŤ!Ć2eńXBÖrVë,kMb[˛ůěLvűv/{LSCsŞf¬f‘fťćqÍƱŕđ9ŮśJÎ!Î Î{--?-±Öj­f­~­7ÚzÚľÚbírííëÚďupť@ť,ťő:m:÷u ş6şQş…şŰuĎę>Ócëyé őĘőéÝŃGőmôŁőęďÖďŃ7046l18cđĚcčki¸Ńđ„á¨Ëhş‘ÄhŁŃIŁ'¸&î‡gă5x>f¬ob¬4ŢeÜkVyVőV׬IÖ\ë,ëmÖWlPW› ›:›Ë¶¨­›­Äv›mßâŹ)Ň)őSnÚ1ěüě ěšěí9öaö%ömöĎĚÖ;t;|rtuĚvlpĽë¤á4éĩĂéWggˇsťó5¦KË—v—Sm§Š§nźzË•ĺîşŇµÓőŁ›»›Ü­ŮmÔÝĚ=Ĺ}«űM.›É]Ă=ďAôđ÷XâqĚăť§›§Âóç/^v^Y^ű˝Ołś&žÖ0mČŰÄ[ŕ˝Ë{`:>=eúÎé>Ć>źzź‡ľ¦ľ"ß=ľ#~Ö~™~üžű;úËýŹřżáyňńN`Á彳k™Ą5Ť»/ >B Yr“oŔňůc3Üg,šŃĘťZú0Ě&LÖކĎß~o¦ůLé̶ŕGl¸i™ů})*2Ş.ęQ´Stqt÷,Ö¬äYűg˝ŽńŹ©Śą;Űj¶rvg¬jlRlc웸€¸Ş¸x‡řEń—t$ í‰äÄŘÄ=‰ăsçlš3śäšT–tc®ĺܢąćéÎËžwç|ţü/÷„óű%Ňź3gAMA±Ž|űQ“ cHRMz%€ů˙€éu0ę`:o’_ĹFVIDATxÚ̽˓%I–ćő;Şjvîů®WWU÷L3 3ĂȬŕ/ ‚°@a5˙+ţÖ ‚Ś l†îCÓ3Ő=ÝYY•YY™‘Źxůă>ĚLU‹Łf¦÷úőČčŽ/‰Ęp÷ëvÍTŹžóťď|ź¨Şrü‘{6˙ř2<ýG@Ź„Ä_ĹGzyůżżââ_;gń“% $žoŮý únˇ¬oMűQ[;ĐŞĺO@†5ÚÜ€oŮţ˘cóŮ- \ţÝÇ´?hŃ|…$xűĹ ÜyŁNč~łgűg{ţ»ĆŰďB$őö* ĽĐ}yÍöĎ#—˙Ö%n!»ň3ĺCDćż— QßxІÝ{¶ą·ëĚ®|_.×­@äáż~Axś€HşYsőGW,~X˙Á˘ŚoM đčŕرg˙Ő˛°ú`Éň'-˛rŕ:4'6˙ĚŃ?ßíšsĺâď´Č˛Ľ§¨f{?j÷Q{áꏷ,>Öżż€A"żÚÍä>AÎö`UíŢ9OóŢ9áÉžtÝłűT¸ýÓkäĎ”ĺG –?[ăĎB¨żŮ‚Ô čďŘ~r yMó^Yxâ¦oëŻ^~ *hČC, <eńŤ7Ş,[ă˘t¨Fű9{/”Ť4~Te^#0x$,čżÝłýó yyDśŇ<ňôß ščľŮâ.JPP´OhĐ–ßë@ĽÝKYV?khß]Úâţü–ýW°úÉšćy ÝW=Şjo3GÂE@Ú 1Ď›U]yKŠHBUÉCŹ&g›0'Ű(Žň,#š3˘| ÷‘ܧ7’ł×ś~†ĂĹwýŹţ}t÷§çŻéCŽ>źH˛ …ĆŁHĘń¤ ˘i€Śżô¬˙ÎËź'†Ż7ě~ÝńňŹ^Ň”§®©ěo7t̤G–‹ź.hpNü6sű‹W\ýIWrŃĆnÚÂqö7Ţa÷«[†;4®N/qѨ”Ż8p%:JI$Ďß+z¸hÇ…8+´Ú¬s´”ńW•(Ó WT;ÄgÂű->&ď»Ďoٵ!í`˙Ĺ€H°cµĽ^÷m¤>ŕŰÎâknŔ-ʉáЬ¸éwŐŽŔňiĽÄ9V?ą„Ď7ÄëÔ1ĽŔ)y€î›=ÍăŚĆ„ćlן’˘ąwhv WĘîó ‚¦€Ş˘Ѭ–ťŘ$uň‚ÝŻş/3ÄÁ.7+ ĽóźA n‰O˙;púćÖ^žW˙Á‚”Ánş %Ń I˘Ú‘üˇÇ}&4‹3Üұ˙ęqű 2°ţ˝snţŮwäťŕ/Ö¨ę´64[tDŠŘ—ńëö”$x[0Iěř)EŽŻŽo)Wk C˱¬Ř¦¦®ătŚF(#k¶V<-,>jč_ yŰßlçč¦ĺXĎE‰›8ú«„~˛A“’÷™ĽĽĂ±ßť˛-ś¬¨é¦A“gűËkrĚĺ]xş§»„ŰżLl§4üđé4VtĐ0ĽÜ‘^ĄiC©Ě›TD,Ě©4(kČ{Đ"ixEś…Ŕ°ů?˙á›]xÁOJ„ă%޶hş@˛·“öV°řÄę'k4őtßtŕáö/®KËßöźeÚ˘ýšXŽ™XniËÓËčź 9µě>͸eʦl÷+©Ćeć¤ÔJ· ňŠŰ?Ůŕ|BS•Nd-…k.kΞ¨*hj€TŞÉÍ»>u(p˝sJ©ľçBZÔŃ_EúWůnT/×ÎC9Hi3r¦ÄëŚřĚâý–x^îńË€fµÍ‹"^XýpŤżp  :ťą‡›ż¸Ą}O8űé Éb‹Ň řrzdŹH>Żŕú˙~Áęg-‹Ź=Ä€¨·üQěÍ„áŰ˙őnúë>në|©ÎźĆ=—Ôˇr‹‡hËđebxeĘŐ˙őŚÜ©Ý€9Eú=+6żŚlął#TĘb¸§dP'gluk˙î ťHCëÁ•Í9Ş#w© :ÁXĺ)ó‚-G´Ř&wńÎlĐňu;ŞÄS Ą5â2⡵"e‘‰7‰›z‹¨'vě—Št\ŕ~-\ü«¸5%ńďŃÍšW˙ř%áұţYŕćO#íG˛â—°j×{†o;ÂŮ9íűkđrn@‡„üRń O8÷–Sk´ëd™nNvj¸r„Î{DËI”Ť6ć|zű×\dśxŕz´Kţ$y9—ăeáM‚#ŹÜţłW„Ďë­ ď¤I  ‰‰ĂXd— ü"$4š<"y‚ŰDʱ;ţĺM~Č?ŤyPť·×p–+–÷¸µ°üé›ĎzÂ"°řxÍđÍžáą0ĽĚÄëHX ˙͆řrÉć—;ö_mŃY˙ü˙čĚŽÔŰÍđŕ/2B"<ÜzŹ_Żypą.`h¬®Ů•ęTśEé‘Đ!RrĹęűOćąX´śh•rüĘ ×d‹ÔH.ѤܧVĘĄJ3>ôF–Ý}(X9‚Ó‰ęqěŘE ą\2|·gřâű/öhrtżíÄůOŕĎü…§y˙!ýÓ=ŰO¶ĽüßžÓľX˙|Ťż ¸ĆˇšK$rÄť®/×áç‚Áž>âD±źŐhąŽ–ä[ëîĆŃ˝™5Ţxô˝ UÝšr4϶`g9—l?Ţ\€;ĎVHQrO×á/3é©’wj§ ißkđëPą-úćŁ%áÉ’ýo{v_ttż¸)iP Ţ$ňŕpĺwž“Q˛mČFQ˘m2 ü+qu#Ŕěď‚Ěoa™˙"'p1=ů©xGxäé~“‘ĺšöe÷Egŕë ?^CŢ îi‹žÓľ˙€ţ«Ű_żâŐ?ľfůqĂâĂwÖ2<!Ŕ°€Ą']‰V+Ŕ#Ň•0îî‚I^+Ó±#HŤůüUć/hęµÎÇ7ii‡–˙Z¤‹:㉀TŤ•Ár‹Ź=˛U?$#!ŕ×-ĐÓ?‹vťB˘}ż±ß/ąŁmYV?]±x?qű—W /hCŢ+Ä,-×Ă (Ą˝hŔ+99Oę!ít\2\ϑz´¨ôţ‚ţŚX|ŕŮ} yëđ«č@”ł?8Ă_‚F–¶›Ý€´™ĹO=íŃÝłů‹[vżp˛'«ĺ•y·Á=ĐŇR´Ü·-˝Ý* OF(¤ü›çTgßnŮZdšV_Š™áĺ(Wô%J ¸ŇIPmPKEěÄΠ™ć‰çě÷W¸¶ŕmâ‘,( ÍBĽ±•şűÍm–Ŕź7¸¶!ď†ŇŔ0ŚOÓŘzO¸„x: ä}fóg;¤ ĄůSľ?e¤ßŁd˛*Ú¬Ř~˛cűIBŇP°{»ďAßJč»˙_ż·öŃ\ú©póO_€·đîτŇňÎl<›©ŕ{„,‹ź´´|ÄţÓ=›O_<.ŇżÚ>8ł×ËX>ă„M©ÔŞ(ç2ę rÁ•¤IS1•¶=eéĘ)3.˛¦ż1ź›ß—–ŁOł%řh¶<+Ş'ŢŚłµ2çQévź%H’"$•˛ÇĂM´«J±"6=WÔŰBËĄ@ąó1XŚ_šJ÷˘"ŠÇŹ[ä„hƵŠk„ä•< „‡ŽpŢŃ#”łÖÉ[8vď=~ď/O•Ęn®uÄý\E…µŘŤrľt¶%Oth´FŁ=Ô\fńcAÖ›Ozt+l?KhŚ,>lŃ>BnÉą5°{ÄíĘÂéż4.Ř~šqŇC 3¸«%Rdp‹®D:ű7-ÝÍj‹j\”uűIç°î!+ŤEtJ Ý5Ó­’n÷ÓwúĄGĽĺłâ<áŇ“†HşÍSĺ˝x‰k@šr<{oí]y¶KđäAąůÓÍ;ŐOÖě>ßŇ}q+Çúç+šË„Śe°ó Ď<×˙φՏšwŐ8’­÷[Úr፞şú;Ŕ×Ó ¸•gńŃ9ńłkĂÎ˙đnˇa«°¶ż¸¦{ž öđ5Vßá có« Ű_UdÄkz¤t쬰H±d˙Ű=N#Ş˝µĄ9\HŇć‚á ŇŠĄmŰ“ôăçc.ć­bô|BB ^{6żş™ZT‹vdŰXŮY|´`ý7ĎA÷8×!xvżąfóĎ÷öľXý~Ŕ/}É e¦tŤ¸átß3˛WČ ~ áaćüAKűµcű뎛_t¬X~´4&T.Ýě86¤ĹUě}‹‘o‚űç‚Cʉ.ŻÉĹ í{žÍoě$p Á-ťQ‚¤-E ř G“±öŽĆ"ŇzúëL÷eĎęÇ š'KşĎ{şg;#)śµ¬~ľ&ś—BB˘±ľa÷yÇţ‹‡żÁµ#ă$•ăIľ˝%ÇÄâŁK$HÁő˛absĂע jEĘůŃ˘Ž‘nýł˝E^)é_ ˝ĂŞIűć·Lhě@-ZúłÖ§ŔĆÚ‚ÚBňĚu”IŧHF;“Y|°&ś/Ů}ľeűËáëŽőďµřÇ i—G&EIcäÎ~K‹o&w ‡I·ŢµŽ*ŠŕđgŽtCég&ÜÂŁ˝o s4±řÉŠEÍPqmĎĽÇ}µeř2łúq‹,8çÉQȤ®c÷«ČŮĎZšw—ČÂŰNöŕâdáq­72çHÝČ nŔEdq;á‰Ăç+5íF™*\UźČ›Čţ·…„JFÓčćů*e ;ú‰í?ßpö·Îp2Fę?ż„´×©Uh'‚›+÷<^”’3ĆâÍ9ĂłCuŔŻgsEűNĂîÓ=W˙tCx(ä˝+ąłTC¦č¬U‡ă-T»‡«Iů}zŠuL‰*ÍóX==MFXÄ—Î\i†çq—ŰâФźó vĺ!FH{š÷\>ľ@c"wžÝ/·–ë\t¬o˛˛–WŢ#9"©-DÉŠ)•Şč˙Ĺň,Ç‚ÂBK'©Zś+m)Ďđ¬łBk@v›˝}•´Şş…îk%<€ŐĎСµ~µćŠ&–Č „ڞ/ĚWqÄ}2|P\Ü18Çu๬ZÂŁ3vżÚłűj`ěĆ]¦Ő¦<—˛ KůŤć|L‰%o.hĽ‹JîKt. ¬;c䋉Söź%ňNćŁBJ«©äc"ů,ÚŚ‹Ô!J; ma*ń´G|BĽÇ-2çwIĽ lţůžW˛#\füĺŔđ"vý Ü5dAtUX-ăMÎ3×/űęx”š pw“12~=úď:Ł«ł7ş:e3ŤĚŃjí–ÎHát"-4ÄÁđŐ8|ýÂú·˘ö}Ş÷…TÄšÚ—Ý–bŽR° ¬~Ô’{Ą{‘ ô¤č®Ar.’{)Ďăm»ż“ÁÚťzçŢ“nşŻ®qKOîă´`™Ę÷5‡wV«|R§vß™Ăĺ‚oÍđuĎö—[vż)0Šř˛Ç°2řE«ČÝŚČJ9ľr*üÔ4ń|śčÚŃFhČ˝oâѵOČő\PuXĘ\GşéA—¤]¤ű¦Gń@‹Ćžî«HXzr7·ýĆj]S‰ÔŮča:€vôżÝ“ź'HZX;jX˘Vő„[!y…‘ţ«Hü*–ť+řćM/>y]5«Dϱ1<«J|ł°˙<˘ .ţŢ97˙ě Ćaó9óAÁ˘w/H‡-¤Ę˝¦b'Hbń%á˛ĺöĎnčž÷Ö¶Ü+rÚ—€eô­áFĐäpŻň„k‘A7ĺ­$\4ç‚啜+YŹ7'GÜ&RWß<˝6¨F(t_îŘ?Ý•„|űö‰&aűëť±éF‚ŃD„-xYĚâĆ»·Ůž¶°tĆÎM¸P~>Ř{čľí‰»Áśđ­˛úq?kęŢîŰÂ÷Ž€¬ąžŽÂőy—Ř1Đ~ŘŇĽç çŽř, t¨:÷.z:ňVĐ‚ęýKt@cÄ­—?łŹÍ§·ě»Ecžy™ žŮýúöč}ž€ŇEćÓV¤ĚOl’öĘá­ßŹĐ+„•°üř Ö{t÷€Ý§dٱü˝–í'¤ĚůßYÓ,KĹí ©ÖF´0ĺvx‡î/˙ř†öĂŔŮĎe ‡ňŚJ±•śĎ ú«ösqŐľëYü°µV^)şÂ©Y7Ú^űŢŐ:bP˛Đµ!÷™őOĎpM˛Ĺ÷]*¸›Nřë˙7ôGď>L1D¦qBŁ+ůŰ@Ú%ň`]p‹Ź.46ŽrÓ8ÚwVH[ľÄrř(řˇ}>¶Ô2âÝW7~=§rĽöô¨X™?÷çĹŹÎ`1żqhެ>p,>„î«LşÉ4~Q˘°«F7§—ISZÇ3uܤU{ťD¤ ,pĆpµ1Ľ˛I,Ţ[Y„×áp†ă_žŹ¬-7ĆYŮľűĽŁ}w[[µęšÂÚŮô¸ó‘(3{_Ô;d~ŞšÎ+;Î]bńˇg˙Őľö0Ö?oXü`a=†ď:4%Ú t2îě*‚Âńę/[SF†O®®űB 'Ňi{ťćakiHú§×Öo…ó‰°tä« ˝BKŞěN.dˇ"KŚ[ó +Ť ›±b>ÚŤ_Ş´g‚;·›x÷ÖÖ™V°žb:WĹI¶f{˙Ű=i ëż±´9€ě oú[Ă &‚ĺďpÜÖŐ7ZĄrsˇVtăÖJűn[p+!^¶awÂĂĺň¶ˇűn }ĽŔ­ŕqMˇ‰ mU=çŁ?zô§¦‚hJĘ‹xD[ü$DŇô}áˇ"M_P…)@ć{é+GŮłłaž>łýlGűžÇ_ŽOp€ŽáŞCłáI˘G­’ăÔBő€Őtx*ÚŇył˝¶ziá”™[Ą}d©0ä‘5ľ¦†Ł_xš<ˇIiŻËޏ×9Ą(ďí`ôĄşq ,ßopçq‰ţ[!EĄý¨5śNmČI¸QÜĺ©Üý8ě8• »GKŃ n}%l6sÄ —ˇ\»)5ڤŰđÖ0ćß•ý˘6ńŐ?ÝwĘęo_"ÍşB ŮŘĹi§ä.ášv>Ž^ő÷©€®‹Ś‘Xô°6áut 6ŹŠCśĐľ7.¦…EŢ‘@¨6ĺRĘQĺâĽäôۆŻ;úą€ËőĐ;Üź§+~éXýč ;öß*~-řÇă𷛎óᦣőÉöGnż‡f¤»jćů,xëx˛ŽżĂ- \`#ŁŇso¶ĂˇG™]=<®bU“b»Ů Úe¶żJ4Ź»=ô‹ň#ŮU(m!]­đďĄúrsĄĘkÔy”utćö‰ S[>Ęćíi.~±˛cVr—ŹŢ¤TŐ´Îôd9ČátŽ]y ąŁ{ş)NJvśő¨¸€8ë7gëÜ„…Ă-zp‘ř˘%]ďX˙¨E\´NFn¦…‘÷ÄĄIuĐ©„:s1ĆżVsĘ5¦#Ś“j,źąy’®)ąn%R¦·qěękpżyk<ęlň†o#i“8űkĂ‘z_f,ňś”gŻ2Í“<%ÝZZ@ÜW$žDôĺ0jJéŁNů±Ti ÁF-EOÔňG”fu§[‹9ŐŢđ~ËĺHŽ »OşĂdS-ĎРM Á±ű2"‹' ÄÇ2_1˙Ţ´·jݵr/„#r*rčéÍ\§ŐËóçÁže’yPŞ|OĐ·}ě ňEÜgLLµAűČöÓ Í#ˇyß8szS*Óé8ĽÜ°ĘËąSÜs„T_¨űĹŞ'˘äŚqčtV» ›kź,*~®ü{OO··ë”×őw.8ZÜY†ÝáÂŻýHüČf'ň.1ĽhÜy(C>RÇV™ćť’÷nqŞ@{=,zĎM-?V€_űŞX9¬ňßJµ«zĎî™&»Ęŕ”ř4oM@ÇvďQ„ńŕšÇ-ńf@‡QgĄ")ŤXš~ř|_×€ N(=ćĽĎ8ďpçŤU±"Ż)žNüĆz<ŕ`T@ ľÖ[)żt÷ŔRr€;+şŚĆ†áĄ’;X|T@ŕ<Ňńçč§*ämšj˙{@÷ăfÁń{-Ú5ńföé\€nRw·¤ţküsĽÓއ {6żěix»ă,DŞš!–çł”;%]§)ß«şcůŠS¤U^ß=ŰÚĂđj(?›&•‚™ŁČÝ˙r*¸ś€zФ†ý*ʦĚđ˛;‘§V9bő^R‚ắ"~éhS`‘YMe˛±kâ&ťßź1ˇ§ZăĐżcx9*-äŠË—«žô[Š|rśßSU•ŤźĎq“Yý޲ô ĺ°@Đ4íŞpé‘ńUamhĄç]Żą2Ő»­żqg”ëË›ĚpMˇ€ĚIŘMćĆ<üô=ź©x,Oňu˛…^Ë\U¸›VǦýoo^,ž´H#3†Ü)˛Ňí,ó7Że-]ną éQKđřĂ9CöăI›çžą]îMľ×m¬©Ź Ú )±ý4á×Bó~kŞK.G )[A¸đ ĎăůI5=Ż|˙/×ďˇŘTÝ–ţEdîśEZäNµ8ýŚV‘B9$-śü(9S‚ţYgB çy3$$“ĄýŢáŐ€s°ü0uDĹ›ôEÚRć+ć(=ÂOżS9 G9˛ń6‘Ł–ŞżŢ‡Căo§ĂqgAj5ĹźĎaxYýÔA“ŞÖŚ?˝·ú’`;<ć"Dó » ş˙ S{\TET1Süe™5q2Ő^d‹Â*ĂŰ8vőÄîÖ *ßdş§™ŐO¸%[8ČŽ(đ“HŽ"Ť# DçÓľSÔ8=Š^b·)>K y2FéăŁJîđ;_űh!ȉ$‡2yg)Ă\RwUܢŘP&â˛ĺťáÂ#Ë\ľ"ÖęIđ;\ź8w:)ă+ŐÜĚÉ7tâGŢ+i3Γ’p‘žpR7ßtÇŰŔZŽçDçÉW™ýW‘ĺW¸Ąćc6VQzX˘ćďá4»&Ě ·÷>¶+ůN*VŞlG÷M‡?·ę!›Ý”sVm·Ó-(Dz읜¸ćďŮBNÝ,VÎţŕśłź·ĄŹ-Ó!Î]IcňÄţ>ŚŔő"ĚÖ{âM*ÓG˘ŢÇꂆĄ6i y°`jôĚ…ŃX|ä˛ß|Î7bśR±<˛Ń“˛°˙Mg2_?*|±\ĺŮMąÔ }ýDDq­ĂŻ„ţYŃi‘ş;ˇwľÜ˝H9ŹSdí“Ö™ęč]ľĆć‡YďÂ2eHFČł-JRF¨Ł!@Úĺ˘Oč ^,ŽZ‡Ůęý™'ďÚçI)ţNލ÷äĆ2ľżLşUš‡ˇPčďI±”©KővSÔ.ÂÔŰÄîłžĺ[Üşřtąń´( LpL8[]•ó›Ö^xhhx9Ta˙ÄúÓ=K=<•´ŹÄW‘ö]Aš ąTŕNQM¤ëHÚ5*W*F'Öű­«Ň.‘÷énu­G@ńőb6ÁťŁ()ŤA:ľçb¦g‘OŃčg%9xĺ¨%\4&$żËłD—Ţ‹€Ď÷|-x•Đ áAs°¨e|X2oĽÚ= űc˘Ţ}a“`‹ŹË @.jŁš!Ő)ém©„ĂD~:zn%Öç}Őˇ1OGňÁ ˘wŠkw(_‘6‘ÜAó¸)xM&^ĺé4qبŢ5BşI‡Ýăa%ť!—#m_SKťÚ&,Y„8†Lş*Čń” X·śĚ~iĘisJ§Ň6U^GŻ‘ë˛´ŰZŹ_;[˘›­ rí…WCVCYĦ.źúHęÂ%r±żJ3™ÁŐ˘‘[ßxo·P«q„=i—Ů}1˙e¸[žźFjď&eÓ§sYذĘđ˛źAh}xců<ĚÂ=%IÖn !ˇ!˝˛\­yf~^†\ ŘgÂĚen¨»-M#ëş7vąřpśjŻ” 2Ć;‰˘EYľˇű&Îú+Ş“e«îgEŻŢv>DI꿇 Čý€v:“>%Ͱ1‡É·Ř›µJ ^Ű×üĄÜA$¦˙•!¤ —}ă‘oR@3•őś2Ë6V!é÷1Cô5łz+…L8â+łóTJÇCÝĽkłGsEn¨lڬŕéżuř•Ăź‹ĺ7ΓwqŘžńZÄ yČ Ďöh´¨8|·'íŠ%k*5ôůÖµĄ¤Ý‹ŻtN4§C’B)ŇŇ>Ó=ÝŻ˘©žŽÄR1}BíFhŕ@â˘2Ó<Ľ§ż¶B#Ýöń¤B0ş"é”ëÚŘhQ¶Š-Ă đËĆÔaÇ)µ\ČŻąă#ę\íľq3:xâUĂî‹DűNKxěJ˙ő äÔ ĚřŕďšË†¸Ih? Ű€MqW?ß‚ŞęíBó>_%š·( šA‰WѰĽrM‡ ĐY_„vüyŔ52‰G‚´Učg•ÎĂ!í˘ŐÍö=ýwŰ_Z®Î}±“ŇJ'Sg±Ę‰ěš9„ŮĐe%ú•QÄŇŤĚNă#{şT‚ VJ±ŢB!ď=馴őŠj©YÚ.t’C’ą‘Ü„Ţ ČĽű¬çúŹm|ńq6«‚ű(Ní'ůČws6hŇÍ!ä;´*9•o9‡âlńÝö4ďíż¬ä›h §žˇ«‡ŃťĂ-Ă$îía†Î¦…ąWŇ&ͤÔB’Đ–֬řs«ÚghŁĽ*äd&áa3ąÉxîŹí,_ĺɵä˝TłÄĺ¸6Â˙­ĺŕš}™Wž6B91’ˇ â2yŃAiŠiVçňoŁ\†(2˘á9[AG~ó8_÷|c©Qi/„„węś|ÝäŢ‘ţ;°ŘÝ5«„Ëž§*’jŃUsżuF®žáUŹ1ěJ3Ú%âu<(ôBĄŞŢá`$¤6˛ĄYŇmšÍňtľ-b<ŇŔňĂŐaĺŢŕWÂňCűď, e×/Ó4¶/•ëĹcIlHËU­JőBÜ ¨îˇ‰¨ďQףn@]Ńc x{ß>1\u¸ü4s-MĚŮm?/¤ź§ÚńŤŤÇťż~)č.˛ü0ŕšP¨?˙"¬Ůűk9oYXô^e–Ń´K,ç(bŰ’$qóÜ!±5čéżÝ·†§%%^Ůh¦x_¨\z‡ź*~ŽPŁ}ÂÁN‘˙´7¨ÉťÍ$µ'Ł iŰňrbŞ…Ąsö—4Ź"ý×ijś¦•C˙ҢčîŐUq8ÔŠÎě|Ä_ŔđÜ|C;†ëŃ> n «“;‰'Âî‹-.Ě#®  ĺˇ;a÷YÇđ2Α(ąŽ’{L4^•|,ö¤­ÉÜŢţů5Ú‰‘-Cá˘ĺÁčá8†—:Mz‰Ý§|Ëńňt_±śÇ»ť˛Śţy>™KĚ‚;R­F$§¸…EąĽźĺ/¦ű”JŃÓŽĂHŠxoö ÎŻ#iiź4ć›&R ŰL÷t 9°řř̦ßĚdpŘüŇ$ęV?nŃ®e÷ëÝwśýˇGÚ€ä%Ę ôßy¶źîYľhµs~íß’>źŞcd»É©O¤ď ű.[%ő:?ŢÓ}ş)ŃÚť¨őpE¬Ý%Ec8^gŇ®3=ee˛h7$ˇ!öł)%9‚[ŇŢčč#ž&­ŕW iŻä]4ű-§S—ĂÁ­‚)@‰ŕ—Ügroą™SĹ­ÄyRĚ–ÖŃ>Zâ–ľD“lF…úďvěľĘQß‚FÂą˛ţÉ#eDŘ|zEęg®Łˇ]šG ÜjS”J›‘–]Ü7‰ć„óHNEř2ŮË» ą·¶aN†‚í,2Ć[SµgH¤í@ó0X۱Č{HJ&Ź­ !áÚ€ř4µMÇMůĆs>ł ČH#„Ëńˇčâ9âmOÚ¦ßcżÂĂĹĽ`Ęń*.GEGÚ$†«8AR—öýL871ÂţyGĽÉ´5ř•›d6p·űĎöřÇ íű/ Ď3ý·É†Ő»Lşˇ€ ţ˘!Ľw›„›»ŠĺĂMş”·h5ËĆe#nľćˇÄÉfjőĆ<“NĄtrśÁ»îĽÁ-Ü„‚¨S4*ńŞÇ-áń r´îLďčžő„žö±§˙¶7ĎU!=äĚŐ >¶Ôşf ą*ň˝ÁĐçÄŽáÂ|jňĺŇ»óŕ]OŰDĽŤSnŁ´Ä‘ćq0Ě+ĆĎ,7TšGˇh7—ĘoPúoz—ĽĎ Ď"ţĚUĘöe>â˛|MĂ‹HÚfü—Éy\ZG8s Wvl6†Wą` edŃŔá´-<@¬nµ%—‹H(„"ylmÇź¨˛ühAűdäav|śT¤üÜ2›(N•(·Łpč¦Oë„˙áŠřrKÚ™*AÚÂćÓhĘVçßR¬łŠó@š›żŘ–c.›ú“bůŕ7{şW;«ô’°˙¦łă«?ý7˝-„Ş‹"ĂM>ňËU źţŢżč+mÓ1šgúçń8)m„ :Ř}ŞUĆÖ‰&ňFŃN|Ź%ĄGcQc>+ ·r¦Hş°ö¸Ą#ď#iËĚ<Ňí@¸ph—‰ŻÔ”§%/ÚwřeiéŐ ZŤ0\†g‘ö}G{ÖVB°RoP×/”´W“K“ i* ăfž4›Ľě7żrHpeŹ#1đ™l`mIi=í“`ť˝%šńŽđĐΆúˇtqš×ě~˝#ެ>,>ôĚŮ“,ĽĂżdë|ŠŐ¨g÷YÇ~Űłţ±ŁyGéľĘtĎ„đČ[~]|ĺ49Ë3›Śj*iŚŮ¦6„ŐŹůµLőĂř3ĂłDűŘŃ\Ö2,§Đ[‰|íĂ`xś+Ôńx>Ńş +)-ŻcŢžĘŃ—?pöŻá7ží§g?^ŃţŔ‹„׎î#ÄM!DjF‚°ţ‡[nlţb bô$ɶëçBX Š‘HÝ;–řËř»]Ë6ÄkçŠk ȬbÇC!ľ41śyâm2‚§ŹhüZ Ź{n¬ç–6®;îŇ®×:MyR[Âud¸ÚŚ,éý<¬3 'UmBÓ¶)R"ŮşęçăZB´­ľĹéŔâqÄ˙ş/`űą6e™Ď®@:C\ŹHÇâ‰ŕ\WĽG*ćC*vŻŮ!’Đ} g´›©Ę[0ţk.Ů}}KÚŞůSHĎ]ŦďQo”ą•%ÁáJÜ-Ľ sÇ‚Ç=TÖŹYŃ?ď鯻®;Cü-9kÁ_–VĄ÷%/ÍŐ” #1Ě EK–ŢLă¤oşÚ5K'ľĺŇě!-fşĎÉŮŚc©öą‘ŻŮ,ß'w‰xm/ qăqm™ ËÉčK2¶aAę#ŰO:»îIoyëĺ`îVăţÎč´ć"çě~ąeŻ}Ń@P¶źî«RĽíÉąî+x)ŕyŞŠ¨ą§Ą‡[H®ŤÎ ĎEץ Ţd{M&V…UBŞWžśF»g!g5•&­eű+áHQ;tźŮ}ѡ±7úĐ8ś#ˇj“XQ0Ľ°–’4Đ?Ű[ő7¶‡r„Ń®č˙y†WiŇúűÍÝ×3'µű&Í z% 7ĽČ“ÚŔ*ăÖ–Ś÷Ćęr*cŤY7Š_hIĘíuÓSŠ6Ő¸<śEZĂŤ#n2í;Đ,ú—™Řg–ď\“¬@‰žý7FÓZýhIŢl? ,?ZnŇsŮ„E(¬Íă% 쿼eńń’ö‘ &,m0ŽĽY+n_dŐ9s¨Îł°Ľ†0:S"ËW€Tˇ˘„OŘW,kĐAňµ#‚sVŮćm@žI“ł W e#ż”…ę[OsL †›DĽđ Á‰˘´‘ ¦‚ëdÄ«Ľ Ĺ<¦µt·Ç ţWFIG <ň4gÖű¦ÉdÉě>žŚ΄óźzd፠šrŃŃs3,ĄyD‘âţŁřĚú苏ˇYEâ&#QX~¸BBWđWĎp•I·żHře„ĎÁ/ícŹ>L,>4:†W‘áU6 1B|µ'Ç<ó…RąúZ~ ‡&«čüÚ·ĂwžĹ‡RÍSÜOgÖă¶[ĄÓ§Ç@— *±Ô& 9k9űĂw¤.qó§×„‡ gŘ’÷=W˙d‡ ™öť‘D8ó ţyd¸ĐÉŞ,5Ćjö ±ß€ĎÖ•(ŞP řeÂ?#ś#<Ěć}-“Ą[@ó“üµ*=łiµ<Ö>áVTŽŽă5Ř'o;tȸ•”ţkĄCX‹o&7Ť{ÎŁ ¦wztđ&¤™`÷› ‹wÁź—‰¶3ÇpeâC#5߯]9FÓD€ — Ú÷ ‰Ď#ńfîgÇWJ{ŮL3ľ†_É›ďíŽyűÁ‚üŰH˙ ´ďI!RęĚă»0ăp«vďą#Ř”‡=€ôČĘF&ýB —nweg >V¤¸ÍÎtáQ ÷ úç‰áŮžÍ_”ü)ŘŹ äméoŠĺ5şw¤<˛ ®˛Á)ŮY'ŔŕŘ6„%ń˘Á„Ąr¶â:e4w¸¶@%­ĂźÁđĽÇ/=ą‡á:á-*óĺŁMčŠŇ—TéŤsłvĚ^ľŽÄ­LÝ·‰Ü9–? řµ·=qcx*.˘!ŁÄjH^Ń4 OűÎş§‘x[`žśIű4ý~•v¬~ ‹ÎÔ'&‡o„a—龆«Dű.´—­©Ö»|@ť{Ł˝ÝÉ-;$Íca÷4o<Í…J˛ţ®Át(„Ń\Ó±8áYÉFG¸´‚B‡qŇÍ™dÄE–\˙üĚDoFF¦łŁÝovô/"á2ŕCůNđ+Ź?o^î/4 Żzň.ĆfĆ- }ĎŞ]Í Î[¤o2ńşŔ$Ş,ŢuČ…‡ ě›‰Ż"ÍĄ§y ýsqÚ'Ůş;ăô\îK”™%Ě&Ű06”ÝQ”p¨ZŐ®šh.!\F\Č„U¦—‘ mDŃą?M†Í‹ŞŘ?™2C.›2•I­‚Gľ&3UR2íŽÝSčź:šóRŐsÇĂăz¤l𵍵ۧÜFŽŐëëŔ[,Ö/šâŚ8G›H+ĂI˘–óť 4C!­R”QC™Ťß ÎëôsÎe|#ä`ÇĽ ç˘u9¸¨8—$nŐY$qBX$’›ű!“÷[ü*Ďc—a-&·Uä"Ď*¦ZŢúB˝źOŤŽ´­ŮŐE©Š€jGX ‹x\“ĚtúĚă&2çkyÂWë¨)÷,yŕ„C8­|ŹŞz·ńˇř µô]B÷Ě’¬Şßç2µH¨Ť0Ś2e,\aL§©gť¶wblš¦4ŕX8Ě´Şkě}‹Lć“hć¸ędKxł łşĂ(č•ćI ˙Ô@ĘŇn>šĺDĄ[űx"-s…'îĐ.ęHGŐ*ÔájĚ­ ¸]ŽÄÂkÉsDçŞŕ­’•j¨ŚQĆlaŠfŁĐŹ3®¤bKUOŽ×šĚÁ—®-ţÔ).I9J"Źň…ľhGSe®GF°ylřďĄ8e ĺ8-J qݤ_E¤ż˛&P]<'+„jaeťý· ZKž;KÇIď< wodÎ~¦ű”ü¤}DZű\čľI´ď•JÓI%nx‚ěR‰Ë¦vů43R: ó#í˛I˙WÚč:X]9†ňTüäÎ#Ů‘Ł9šę{IqLÚ˘H‡őąČZŚOĆ@¶Ť"j´ńšŢCˇ ‹Sdá!¤Ň äAđëůHQ§¸µÁTi›IŰň=•ć ’&…S2hÉ}>ř–É»*‡‡®´"SuŤľĺőî†8_ŻL˙ŢĆŃÄZv±čµ['šwĂS%o2î˘*r©ő(g”9⸥’i»’÷člĚRS‡ÚÄ$ďńĆü9üŮŇű*šŽ±P+˙łđŔÓ<ń´O!ŤŚ,Źé/wU¸…A6áĚ.\iÖIT ĎłDX#^–?$‡sW&ľ$‰áqŃ™Ůa9Ę$[.tjc‘ŠŤÚë[‹× q6[oó\őŕć¶SvŐ=Ë6tŹpť·–č<9É®VÍšpä«[ŰśŐĎ2č›äó•üFŠŐé§…sGXŘpĘâco Űś«ZĹÎ=Ň–i.ŕh»Ę‘ß…,°u¤Ű„4·Ěřńf$…<ĆB@(NŤŮÚoK‡,yhč_ěć×Ěň„pޢ1• QŽ6Ő™ ť ÷‚sagÇyäbČě|6¶™ Ú%4ůyʤ‘µńgżp¤M&÷Ĺ“M*<´DWk NšĚŞG=ő:˛V¶ …ë[+!ˇY3SˇÚ$ć”Ú› rEI>.Qozo ďˇ}"lż„᪥yŻ3ŔR}Oń•ׄL•¤U_|we¬§&ÁoŁ”§^ʦD8Ďřł[ü9ln ]{úoŇ`ôź‘ĄÁcčʉśÍĆ}J3G]¬ŕČC_ňóĚ”Ý3¶h†áEBĽ+ąˇqŢüLÁ,ÜŘ ‘#äĘ,¬ÎBHŮfVüĹ‚îŮž¸v”Ü˝ńŐR?-ˇ(ڞĽ’J§dĚ™őŞS*t-㊺9’iˇGk."ज़!G ýšň6rľ™Y‘«ŠŐűćq‹|ąeřnCó¤Ř0ŽRŽ~¶r׿Ś;>Ät®vc1ˇK›lĂăNp „­=I›ž¸°U˝ŠŃYŞ6­Ţ…ýŽařér'Ć4q·’Ů,9UŽŤýčĂáôł§…Łó8ŔśÇşU˝ď«ź…ÎQNŁZą„kžWľ±üuÂÍů8ŹÄ+ˇL“MÖĄ®JşĆVě˛aůńü˛1Zâö~]ëjá™ ˝Íĺj*ŚÜ ÓČÜ:» ¤ÚĨ÷ża¨eFÖçżD͸•9âm&mĽ ±‰Ů'ßłYőD‚™­{Ú ygÎŹvÜf\Sęď.OVP#±TGŐÍăçňşŁDäî“;z“Č‚TIx»6§!ˇ¸•ŞZcĺĂ6¶˛Ębö뢺)’•»Ąx‹Zń¦§űn‹©ŔS2[v˝¶RT4§)ĆĄm*–21¸ąo%Ü7Ęj™Šâ”ęÉ;Č D&EÔI(Ń™G/ q›^$†gą ĺ¤2źQ KMť|7‘´‘đ¨°Ánd˛6S¦#SŽóŇűŘăŐ[qK“ż/wjíĹÚ€|^Ş9—'Ë_yË®“ő@x=eŹBČ4ďZ3;^ Ő$ÖÁhÚďÎ[ííĘ öŇ!\ÜÉýlČ,Ţ#ÎjŤĺ¸NG™ń)aüŞjťěgÝk‚óä9Rµ;ÓČ»câ;˘î†xDO4ѢčUUü…m°xť&ŻŹÚZµąôł!Mqž”űXă5‚äg„ÁtiŠą Ü‡hđÚĹü– ŽŃ6©XPIś©Bbü3ÉŽć‘}{˙]4 ’H%@čN3]&˘Şi˘#uŘYţç‚ě…řLčź)ĂV‹+wBBŹ„Lî9ú©:u‡â?ž©:˝­i2«©2{şMGžÍ µXčP˘U–Ă`Q›„đr@˛ŕĽ±±ÓVH·u‰ěŐČ›1ÓśC¸4Ĺ*koŞÔr%Ą ;R˛ÔTÜBŚĄ-/Ç qăć5:Śâž%/¬•f'ćrőߎŮóĽ­˝gY1\†W‰ĽsĆ(tďű4ĂO™RFH;µąÔFŚş®ą|ÍŽŽYĄÔxŚtdµ5zh¸»•öq^ŁGnDŁŚ%Kä„­Łeůˇňt;uŃĘuŇ^#\e¸žib5©ł}'ĚłŇręĹ9đ3qMgĘň­ő®Ó®ĐƬQN8Gé]ly 6÷S&ś6őË Ť6#Úľgá}x6ŢDťLNî­<|¨y;Š_y[|y8|ÄÇgSvpśgHTĆ1ĆjŁä{@dw|Ë!WoRŻ•â_!UÎ(3 í×*ţLĚ80śŐÓxĺć˘Îm3]çâ)bt|0 ÝŻ<ÍŁpT‹ś¸‰UpŐ¦µ ŮăVŢ`ˇA'¸ç Gý»8ľ˝śďô‰9Bäš2ÍcđÁŕuî…ß#§ˇ§h ť©>IAé] í;Ââýżn8đ˙”rŁ˝Q˘FŠ”ĺoŐBqőÍ®úr´ă«…ĄG;ţNĄ{$.Í!<ö¦ÚżpG‰lÍpSUŹ:łüZ ii7WŇ2±¬ÇÉ3f’)§»®đĐđF©|ëýĘÍUďhËUńM_‹ ĽµÇ=•鼦ʜDd‘ŹÚBüTĂüŇšńËSMÎŔm6GĹepa,Uʶřť‰á9«ŮJڤj'wť·îą±z˘s5E¬3sü3v*ňlĺĺZ55I%w8Ć6+q„J [X\"<´ë×s?}©pG âôl ÍcʦXµŐT ÓiN2µkę`ľ˙ŘIęíővďÁë¶Ą+FOlo /ŐfĽÜ›ÜOŰ» Ăy(7ĄŕÚŚ_›î˛j,‘Ń%¨ OćÚCje¶!=Hµ*â¨ÖGŹ-ÄůE¦ö ?ĚṈ́Ńí8ż©ÓÄ`žIˇ5#jÖÄíÜŞtµoeŞšĄň›Şx˝§şXĽ×°xŻ1YŚz­'“ů¤ŤŐKJEü}GÜQD|;8ßkĂráÓĹŚż,z!Ď!ďÝëĎY-­˛ŃĚtW°=Źu4ZĹ/íá§ŰllăÝn Á5Eeb(aÂĄzć®#×ôâČňôîq}tR)s6±I_µÚ#\Ř˦ňýcĺ›đgćĹ›¶±üąyĽeH¤n6ć8,âćë÷KŠŚIíżVňÚq+Čهéč>~ßc•Ó÷Fß´…ä䎱sQâl{šw ř^ĘvľĎm\^/mJ;mn™l·z‹iWş^+ˇq7;‚úQÍBO/‘ð*B¤Ęýf¨ĺH‹]Ž#ŤĚß; ˘#÷ö€MşJů"&¤ľ´ľJt©ř shŠ·FŻŇ *6‘wŐĹE2ŔéÖĆEť—Ší3.ŔŚ_Ë-ejśNî<okQ=Őđ5lmd€H@Ą¨@ą†áůPĽ7äÎ ř1ŻĎ|#,ďqË"N,)Śş.‡´::‰÷ Î‡n®ľďř´’» ÷täŽçž•2~Ő"„â/Â!íčXşM˝ŤöÚ,źÂĘFů¬bĄÁŻ”đ@·™xë‹3U®¨ó©J¸ ŃY›J‚aw&´mŕ¨7+CŚ“X¤A¬«áÚńx+vďw* ™'3ġ ÔP © {YkVçÝ©÷‰Ą}5ć¤b&ŮzŔűKĹDG*ĚO'¬Ń/~‘Č[!÷RěVĹcÝ‚¤¦ĂĎŞ®‘"aâŠ÷,K'I&âÚLÚ@|™‘ÉĹ|¦TrŰkod–űŞÖ2·[¦®LŤ=Ň<¶¤¸vĆpńąPľeÂŁ&95Ţ^Ž†Ě‹ČÔJ5ńë\t„™h[ődµ™jgśŘPąF?aľ˘ĎçX|›÷ŕ;NĺجECq2Íú7ČzŕÖRal'cFŹ, Î á|@¤­)¤A‰·%»ŐęͨâDhšsˇ9ü˘ä–YćÔxŮăš–ČŃćb:mč"*WČĎú6"ź–Č”«!d=hďĎÖo|ńyO޸räĚđÂÔrBŽ®¸˙UWÁBQÍÓśöQéi»Pڤ)î?ĹĹčŕŃ×⨪ú®f&OءĚŽI¬ˇĘ+GmăîÉÇÔĹĘÎľ÷áÂ0Ŕx“ŃlÎâć/,ÇĂŚ„ aůË/XýÄăÖeĚÓeÔ%kÉáG»y7łnxm_űđf¸S3m‹o(ťŽ!ßÍ*ĄůůLłČ$M¤y×p»řŞ-A¤čm•cQ%Ű·oéÖáóÇŻHS)"—ů ©™%G °1~m$3÷\tÜOĽ¬đ2ŐÓ€ękZÁ3Seę€(yČF-ěńc¨Đ©čĐŠH§Çŕ»fsřn¬(Č˝ą4ŤÜĹújđKóŁł*-MěqMxę‹Ď@Ô"µ!Y«1i­S*wxs´Âű*,áÍDľy&VŽŞÝS ë<‡Ń<±NE|62kŹd5śiŘĹ­’ł âˆęŹäyNNUŞ'{—ŻÇ„ćüo| éé˘u Ď3ÍŇś_éq4tĄxóÖëÍ˝oÔrżšŽ3>óµ‰6ŢZŤŘęŃűÓj6Ä5żňVŃ Ö©‡CUŻéż;i.ßÜąëä.Üp<Ő4ÝÍ|HZ*ÍĄ'^'ňޡ9šfÎSÓ\ł…hcG¨kç#Ns1¤<‰r×ȧÝ`×8ŁŤ÷GĘ "'‡˝NëÉë#aÝ*ˇ käŔlybÓ¨őUU«B ľ—ąTóšńçĄv«ÓpŇAH;Y+Ź»úZާ_'–ŻJfË]řéj‘އ]®`ń“đ檍IݨöĹŐW\gZ¸|Ţ<ÓT•ţYFg’˛Ĺ^AŐˇťY¸vÜÉł-‡©SUę5§îűYšíRÜŤňP€Ô(ĺn9¨7RWýţÎŽő‚UŃţ¨ŞZÔ¤8‚[îć”ó€SĆ-3®âuśś2g2…Ýâpa®î15ĎŻcÔ/«|čţjĹž[–iäíČVş›¨ž> ÜĹż÷_y­7°öJµćFI%=˝Č<¬ďäŽÇr®óľŁQ홸0vŚA9rS—(Ô<ňó\nÍ8ĘŐ†ĚzT7ŘŤňËŇßUáúúôc&´7g,˙ÖôĆN^™*É»ń¨80SćË xÁ𤍄‡Bę<ńĆčQŁ©K÷eO˙uˇýř"gćťi±čH±ŇYW+&pÝ7ÍTŐs)V2sżŐńÚí”ëäaX<ĂdÎLJ\ĺn÷e¦`59ĺV2)GDT~]ŽĎZ•ľ$tľ)#š'aŁ‘{Xőçë“jgăIşÖ\”ňزÔÓ©îqcŔ<ř˙ nýÓ7°ňtnOťş¸Ôă–|űépk e¸RňŢo2ŕÉQĚJŢ»)?›8s“BUĄ#”Ź™Ő1,łŃĚ(§b9Ż+×S– Ç˝«{”«bA}EŚ_)ĆŹ÷q$ŚŁ÷LćŁSŔŻ‚©Éçă“_ńgÁ(Vő¬‰«ćLĆhčîd¸¨hď—MVťj·ĽţT´Zžü§‹˙öß!ľü“żľj×Ď A|щ·ŁL˝¨Ksga„?˛/˘B ˙` ľ}÷Ţ“nvÓ‰ăWŠh@$f˛łlzŽM[Hf:™M×ÚĎÂB®ŔY,ň9ő%"YŤ2ľŕÔúýĹnÖÓ vŇ,·ü6eĹ%›+Ń^ˇóäˇ/°ŠY˛I̤Ţ[>¦JŢ—«Č÷Ї<´FđůP<ĘĄ’RŹ ři^ČBŐJłJ;•™aí•´s¸°0ó›aŐŻ”Ľń°6x,w‚ë®…e6oşň”cMf×ňř?ţ#şOţG®ţ§˙¤˙«_|ťąçôß ~ÓX‡!Ťâ'°HŽžjhrźéžîÉ"j9_şM¤ŕHâŕĆžn oǡ y®ÂŘľ™ś•¸“ą@ÉZu],Ş×V(‰wfďÄ­]qő‘|/qň_HTRŮt›f.ś3Ůćf-úĄM†çń€p ˇ˛ÔďgiĘć›_uÄW‡§O}!Ş5Âcžmq»ÇŻ…ĺ‚ ЬĹÜRwĺ¤rÉ6S>â~¸iÄňŽJŐâ÷˙Ţű/źqý?˙tźü÷hŢ©Ő˙˙qcc€ű§§Şň»řËé'3ćĺý‹®‚,"Ą®žľNżöV~6Ů=ů˝2łI&"-đMÝo{ Ĺ˙ŢPx:3VUčŹ5î|UQZM9dŢLÓxŁ„Ůřž¤š,łŔ#?ď» * download_ * features_ * tutorial_ * examples_ * overview_ * documentation_ * contributing_ * building_ * troubleshooting_ * `tuning`_ * screenshot_ * `mailing list`_ (archive_) * `who's using libtorrent?`_ * `report bugs`_ * `github page`_ * `blog`_ -------- Extensions * `uTP`_ * `extensions protocol`_ * `plugin interface`_ * `streaming`_ * `DHT extensions`_ * `DHT security extension`_ * `DHT store extension`_ * `UDP tracker protocol`_ * `HTTP seed`_ * multitracker_ -------- Bindings * python_ * java_ * go_ * node_ -------- * `Introduction, slides`_ .. raw:: html
========== libtorrent ========== .. _download: https://github.com/arvidn/libtorrent/releases .. _features: features.html .. _tutorial: tutorial.html .. _contributing: contributing.html .. _building: building.html .. _examples: examples.html .. _overview: manual-ref.html .. _documentation: reference.html .. _troubleshooting: troubleshooting.html .. _`tuning`: tuning.html .. _screenshot: client_test.png .. _`uTP`: utp.html .. _`extensions protocol`: extension_protocol.html .. _`plugin interface`: reference-Plugins.html .. _`streaming`: streaming.html .. _`DHT extensions`: dht_extensions.html .. _`DHT security extension`: dht_sec.html .. _`DHT store extension`: dht_store.html .. _`UDP tracker protocol`: udp_tracker_protocol.html .. _`HTTP seed`: http://www.getright.com/seedtorrent.html .. _multitracker: https://bittorrent.org/beps/bep_0012.html .. _mailing list: https://lists.sourceforge.net/lists/listinfo/libtorrent-discuss .. _archive: https://sourceforge.net/p/libtorrent/mailman/libtorrent-discuss/ .. _`who's using libtorrent?`: projects.html .. _`report bugs`: https://github.com/arvidn/libtorrent/issues .. _`github page`: https://github.com/arvidn/libtorrent .. _blog: https://blog.libtorrent.org .. _java: https://github.com/frostwire/frostwire-jlibtorrent/ .. _python: python_binding.html .. _go: https://github.com/steeve/libtorrent-go .. _node: https://github.com/fanatid/node-libtorrent .. _`Introduction, slides`: bittorrent.pdf libtorrent is a feature complete C++ bittorrent implementation focusing on efficiency and scalability. It runs on embedded devices as well as desktops. It boasts a well documented library interface that is easy to use. It comes with a `simple bittorrent client`__ demonstrating the use of the library. __ client_test.html The main goals of libtorrent are: * to be cpu efficient * to be memory efficient * to be very easy to use Getting started =============== The tutorial_ is an introduction to using libtorrent (C++). Also see the `reference documentation`_. .. _`reference documentation`: reference.html Contribute ========== If your organization use libtorrent, please consider supporting its development. See the contribute_ page for other ways to help out. .. raw:: html
bitcoin:373ZDeQgQSQNuxdinNAPnQ63CRNn4iEXzg
Support ======= Please direct questions to the `mailing list`__, general libtorrent discussion. __ https://lists.sourceforge.net/lists/listinfo/libtorrent-discuss You can usually find me as hydri in ``#libtorrent`` on ``irc.freenode.net``. license ======= libtorrent is released under the BSD-license_. .. _BSD-license: https://opensource.org/licenses/bsd-license.php This means that you can use the library in your project without having to release its source code. The only requirement is that you give credit to the author of the library by including the libtorrent license in your software or documentation. It is however greatly appreciated if additional features are contributed back to the open source project. Patches can be emailed to the mailing list or posted to the `bug tracker`_. .. _`bug tracker`: https://github.com/arvidn/libtorrent/issues Acknowledgements ================ Written by Arvid Norberg. Copyright |copy| 2003-2018 Contributions by Steven Siloti, Magnus Jonsson, Daniel Wallin and Cory Nelson Thanks to Reimond Retz for bugfixes, suggestions and testing Thanks to `UmeĂĄ University`__ for providing development and test hardware. __ http://www.cs.umu.se Project is hosted by github__. __ https://www.github.com/arvidn/libtorrent .. |copy| unicode:: 0xA9 .. copyright sign .. raw:: html
libtorrent-rasterbar-1.1.13/docs/ip_id_v4.png000066400000000000000000000133011351156116000210740ustar00rootroot00000000000000‰PNG  IHDRX,(ă=¤2PLTE˙˙˙   ˙Ŕ€˙Ŕ˙îîŔ@ČČAiá˙Ŕ €@Ŕ€˙0`€‹@€˙€˙˙ÔĄ**˙˙@ŕĐ333MMMfff™™™łłłŔŔŔĚĚĚĺĺĺ˙˙˙đ22î­ŘćđUđŕ˙˙îÝ‚˙¶ÁŻîî˙×˙d˙"‹".‹W˙‹p€Í‡Îë˙˙˙˙ÎŃ˙“˙Pđ€€˙Eú€ré–zđ挽·k¸† őőÜ € ˙Ąî‚î”ÓÝ ÝP@Uk/€€€@€@€€`Ŕ€`˙€€˙€@˙ @˙ `˙ p˙ŔŔ˙˙€˙˙ŔÍ·žđ˙đ ¶ÍÁ˙ÁÍŔ°|˙@ ˙ ľľľßßß???żżżźźź___‘˲' pHYsÄÄ•+5IDATxśíť ¶Ł*Eˇßť…“ó—ůOáÉWPT EAĎ^݉‰ Î… ` Ľ>ę÷Í1W9o#Ós/uí`@yĹ{p'ĽoŐűf€¨«j÷*aÉŁĘ*>üÔűf€¨«ĐŇ$GŰ”U&ś5ŁzW%klěx7L/˘Ęć~§ń­8=t˝Ž=ôň+Ń#éË͡թ©“”Ť˘yçn î^ÖĆWBŃńĺűČWáج6qÖ\”ÂT)˛B]u]ëÔ/¬żňޱĽÝ€Ů!`”Kµéa ý”u’k ŠEKŰN“Ý)ŔÝOŔŰńŔ^Ř®Ůf‡¦˝Uĺ®ÝsŕďďŻŔj,/Gůńv0ËΛ`b忦kÔšńŚŮťĽýĽěú•vď°°.Üs@¨Š•XŤ_´±ĚfXĆĘjK¤™4Ő‰úo%ÝJyű x;Ř] 캸ÂşĘ+N©ŠeÖ¦©e»@űűEay;°ŐSVlčĹ?±J®r<2;xű p÷e±"ř¦°®ŮsŕĎĘ*§°6=H­ăč0©Şýµ‹Ó嵡ů1ŹRąűŮ>sł%2)dj«ŮŞF­S¸Oç|a±ŕÁJX=ţ# »Şb«qŰÔ:ŽĘĹ‚‡Ąé…ĺíŔ–OYą5ŕ»vúrúĂś›1ąS€÷ô–mËZ,sĄd|U±ŚŐ¸íA*{?ńěY•Çňç|QXf‡€Ţ-¨Ŕ&żźxčÚ˙Ľ­ ű x²[x6Öŕ kpŻ”ČJV«±]ŢŰŘÄ:óŞÎ/sđEkľ+lĄ şˇuž˛ĘO’F!4fŤxłS€÷<Ö–˝+´{x;#Řgöř Č*oűŕßŰŘÄě«üű\ýś/ Ëî ·hě8–’)“AăÜlohv pźÇz²¶ăXŢÎöCúžAU±„µö µÂĆű°2ńżŘVË–¬n–{Dzč ĺpĂĘÄ—ˇ˙‰Ćŕ/Ż&Đę_ţŚ©5Ţ%Ś÷* [V3÷ŘXž©u•CXÍeĂ 5ŰX•q¤*v°Ö¤ÖqT Ž®§ŘCX…ˇ*v°¤Ţ#ťŐ“t…%§*Vb5–—# ‰V+±ËËQlÚƉŃ<”#Z’ĂĄŞÚËŃcŔĆJ€*,oëhc˛}ë 4YAXĎDĽ˛°Üýa5đ3CéŹrôĺ娸qEn¦w1Śc|Ťí …öTöťlL‰o•<]U;[!žĘ´Ý ›XCX WäfzoĹĐ ń56'ö™4ˇĹJ’ˇĹPÔ”[Â#đcRw=G`Ƹ"«çśY_csBa=•ÂBÖ{Bč\8ŠN?[Źoâ=HĹiűĂéĚ. ĂĐďăk<ź-[x;Ű»Ân=˘®*F¨F®§qÄFń mŮ•«Í@X˘Ô»ß(]܌ײ=;đ`‹µŕŹÂVŽ˘˛>ýOy#Ć…Źô ]uč 0ú‘Ý™öť”ľĆľ°Öó36„u¦­rrĂt›!KUÄĐ:Ď 7|AXOÚX4Ue¶±î¤Ľ]Ďs¢6V% ëŘ59Ř·~AXqK¨Égc»&K°é]ܨ*–±#\“%+ĎÁ/ŘXpݬ2 +Â5Yž]ą:AXwŰXé}`y6V{ěš,X=ţDWx«°„Şî˙ŰyÔ5™-–(ĎžŁďq§˝îń¤k2 Lţ‚°®ä)U±„µ·¸­cĂ;±Ţäšüä‹j¬IüůĹm×&Ś÷Kâ-şŔ˝”Ms≯Ćö×q¶šSłÇ‘k2cl=Żű]afa­ «Íz‡Ął)®‰®ĆˇS·+Gě^{Ç5™…l÷O+'sýHUg®F=ÍqŘzNłyí×ä`ňÖĘP#T#wţgĺ 6V&â«ĚŞb”KM«˝hăŕľ ¬6Ö®ŞĽUÝdcµăzˇ´«ůBWx˝°âçDĐÚŞFŢĹ]a·Úh÷zľ ¬«ˇXV9óáP^5–—ŁÂ)ɲš)Żż`c]HŤ•$ădŠcR±čÎ'gB_fcĹĘę_ެňŰXădĽ‹ŐŁH#0ţË˝9%70Âi„é(ĚÄj3q,Äń?L$¤RJ„Ň•W2-G_˘2U1B5ęÇĹŃ«&·Ç¤Ýgm,"Ő©ŠHm€ôČ”Kë‹P"şŤŐżTUÝ˙ă«q yrÇŁ&%Gő]]~řŔś„•xíRĎů}Ž•ęAúľ—IUÎGˇŞrńň°©:…™Đ,-«§ň‘±+ x|\{Ç´ą¶+,ŰT"rUřŮŤ÷Rqă=ÄVcU^N7!y˛ĐĚĺŁkďx—7TÄ~um÷ou€4™/k‡ęű@ u€”âóžĆ—…őU±«Ü=ˇ+ę'‚ĽHUŚtWŘ÷w˝ś/+1jpˇśĆQ^ÇS^Ž®gY]±Ť„u‚ňr”™wu† Ő¨=¬Ö;ćěäÍQÉÔ=ľľMĽi¬k˛öµ~˘1I9ZRQ?ay«¬(ăX‘·ĆsÔú‰Ć$ĺhIEÂRé}`E?‘<@zN{Žú~˘I9ŞQ])ŤŐ…ĺz)ěŃŻ¦_c’rT5ďí5ń6Vüź/Ěă’rT4„I3oąô‰ď ăç>$¬rú Âl«wŞŠĺiNv…•ŻA:©ę0Ü?uôGu ˝µÔ%y~ŮxŹk«dS—ĐV•Ó(’MXľźčÁAîÝşŔ™lÂňüDŹrçč *ŹlÂş˙‘ÎŁýaq„¨Š•Ř>Ô(,jc•šâ+m,ú:ďi”'őH} z‡°fÖyOŁ2aÁ˛ CšĄĂáóľ€ŢX}ŇChó?+őŘXń}ŕ·+I|‹…uŢ=„Şâ"Tk&aťw‚+iJŽäl[a9×y'¸’¦ĺč!î?Hľj¤¸’Ţ“Ł+Ŕřz$ůŞ‘âJzIŽň7÷KUíDü¶ŞXNaµ˙™Kr”[Xë¶j3⑪`c™P)<ţč9şťKBXçxŤ°`Y‘É/¬Ű»ĽĐ¶Ćˇo+÷ ă}§±ZFDc5C %.ďJš”Ł%9„µßz)} l¬ęÎöâőz泬 ¬u@ň]aĄŹt`Żźôš}ĆmžĆg‰®Ć‘f©ČčĆ*yk›u€4‰‡…uKëÍŃM] „őćöúu@X¸Y]J¤ŤEZmfű+)9GN5÷IŤUrŠŕŽöáŘ•ô’ťVbcu»ĺË%•ôćů<0ŮšÝYŰńŘ5nÝ$Ž]I“rt •‰řg…bú×ČFz˝·Çţ3I9:T•ŹřG:SźĆŰÄz?ňřKĘŃj«mUuqKU0Ţ·ˇ<„–“U_",·±ÚʏÓXÁxß&^XăĐ˙n3~*ĺx*UĹŞĘ´•=Ĺ˙¬ĺrYN)ĂR#çâöMÚŰdŠ2Ţ1z÷ĘG®¤÷ĺăë7qٰr{FaU…"FÝÂxßć†q,›ĘĂŹt6«uÄČĆ Ćű67ŚcÉ”@Q>ˇÖĚ©q, 9€űíÜ3ŽEáň Ş'¸g‹Âµ6VŚŞtDşŞ`ĽosĎ8…+…•wgď;ĽŘ´ŕ.Âz€krT°ŞľA|5bQřmVlú('ĺh›˘ĆľIÖEA–lú('ĺh‰mµ©˛şżźřńăXŞţ‡w…Ű>ĘI9Z˘Ş9ˇ¬Çr©'§yY^bÓG9)G!*é!¬m]ťY¤ÝtĚJĘŃšJdő Č6ÖąEA6\I“r´÷EA¸+ěůé»ÂlŠßŮ& *ď¸y+¦+$»&˙§Vźýř/´úl-żőäôŽU‰|2ď5ö€0ŢúŞÉÉ]蚌™ĄB6ŢŰóÉ]ĺš U u¸á¤ŰĚ–ŹrBŽVŞÚlµsílă}‡;Hi íj¬6Ş9ßÎ6yß!ţ‘NA«&×hŻ{@X3#oĘX53« ľ+Ľkáäý«ă©M%TĺčWŘÎ60Ţw¨GX‡]ŕ\Í7ílă}‡‡„µt%ućčeO!¬L,]I]9‚aUŹkĺJę˛ĚŃË«Żđ°V®¤ŢI÷CŃ;ŰŔxßáaµK˙'G´.°"6V6|ŹżŐÁ‹-++G‚eU9Ź kÓôż˙˘ťó¬>‹—S/÷{ZöŚ÷Ä>°˘~â<*¬°ijX‘°`ceĂw%]ť9¦jË ÂĘĆ•tufźŞUő ęymŞj 6a•´ł Ś÷ŞÖ~[U‘°`c=ŔfŽ^ÔBX°‘ŁÉęÔ!,ŘëŐń´°ÚŐt˛uŽJÝŮĆű K»’ş,żn¬*l¬ĚWRď‹—öVfŚ+©÷ť=z©ŞľÁłÂjĐ_ĐU•üSę‰XONź¶±¶„•ŇVUTxV~ÂJě+*<+?kaWđ„<=ťđN , dš7iLlR¤oď©]އžš]±ĄE´éPËÇÍ ©|lDjůL9ü­’~śŕr·±q×nÎ1±~íôźš˘Nkč[±-=˘ŘóŞíŁ#Út¨ĺăfT>sŠÄňű¶ýýXJŮä$¸Üm$}›"¬ŕRJG—j™˛˝‰8ŠßąžH˛…M‡Z>nIĺc#RËG¬ń"ąl˛’TÍŠßôxH.v_Ĺ ßĄš¬·¬/žZ>2É”ňáiĺă/ĺ‰]pŤż(ÄźqĘŻÄŢS ±+lÝ´(Ąn"vDˤ“T>2)ĺ#"&”OŰŰ%Ϩ±ŮŕÎ+ ůWśôç1rÎ,9­†[FäŇƢÉY¤“T>"bRů¨Ž—Z>żn^ňŚX6ůH–ě#R„5 ă4ŢŚ¶Ř´Z˘E¨„%H]šL'Ą|dÄ”ň‘SĘÇţÉPË&©]áOţňŰl,7-ęţë|ŽMĘŻL'Ą|DĤňQ)Šjůđg÷¦żŽTă=ÝŃ:µŤ41Če§m,jŞ*ť„ň‘SĘGĄV>ÜąB—»Ť&i¸AĽ$·Xô0gY_BWhŇ!—Ź—AJůÔň‘2ň—%”M>‚ËÝF“lcým¬!aŘ@Fż“`ĽŰt¨ĺăg8@*ˇ–O#m˛!­l2rć‘Nâ]aZŠŞGKč€UhÚóŽ9bný 2šś"kô# ”˛q}ĺžÁ ś=Ž˙˝ň™[úŰĄ­UŠ‚o˘9_m +ü˝ö:í<ť¦­Uʢa-VBH[«”;1{YT±ňä„ítqR OöA‡sRUP>t˝sŽ™™f} é˙ît…&¶ýbîŐ< ©ŻÄµ*@YšëYYšu "NŚ˝z—‹ ™ŤsĐśsúë¶Ő„›»Â2±[¶XŤč ĺôŻÔµ*@Yp˝~ЬgޛE†¸×4ńÖ;§żţ™/Ô+,›1OXC7Ú‚ŇתE!Ĺ1U«UËŻ­´ĚD?hVĂ, ďgÎÜuuŕ ËF˛äMˇčЧ˙r6¨ěy!¬ú13ÍgÓű7B­{Ň™Ž–{Nţ7óî¸{Ť aŮ|LMśháŇתe±j±ÄGcc™Ë6Bayç¸)ŞĹš3ҵ"|)›‚ł¬l,ő­z3sĺ•Ő…ĺťł6–čÖÖ6–sé•°~żyi čŞ~ÄČŔ8µ˛­Z%ŁqsçßĘ(&¨\ÜĘ…§Ôía¬°ç®ÂŞg‹5˘ j{gĄ»FŹcŤÝؤľ˝Ää21ÖE^ĎÎ2*oň@‘ŤdŠÍ\ :ůí= *—¤ŇÍ%,±5AYUÂĆY~{Ę%íšÉ§„eŐ kÚI~Ë‚%6ő¬—^dü‡aäI#ßË“Ç~PgŹŘĹ[$•Ýz´LZIQ)ęofÄělÍůR(ę|ń=±ÝqÍŞ6žŞłµ°Š(GXËž–đ"ăĺ<ňízľ±ËćF0ß/ŇÖ:H­“¸J¦ľYżíř&[yľşŞ9ź˙ź¸l7Ç5nŧłµ°Šh aq1UĄúcZ™őĆę§f—É®U{tS4ߍ•%˙Ád»Ƭ¬e2Ű×l„eę2P˘lÇŤ°ëCȨ[*^o¬ŰíZ÷¨ŤyD™ëo¶=ÍdkĚqŽ‘ÉŚ:ΖÎÔ‚hŤ†°ôaęéŢVă´ßĂ‹Ľ—]ę›m2Ů®çŰÇě/߸‡¨ }P ˘t¦6¶Ć2)‚}%¶Żß–OĺütĆDzO3Ů®yŰ™[c9ÇŮλuP˛P†N kÜ Ëń±ĆÁ–t„ě]ćh“´^d­¤v>Ö84Ö‘ĚĘÚő±śăT®kwĂj¸ő×>H ‰ďŤ°řcŻ.řcYÇË{-?â©P­“ř \ú›Ů‰vXgŰŘW5çˇAF'/*uĄłµ`ţěE_POŐXv?ÖÔ8ä»¶LvmŻú“TŇ"UŃ1ĄżeŤµíÇ2]śëĄuÖN?–sś=¤Łł/•(„Ša| ófŇÉ7&;€(d´8Á8tÍČŐéq˘e´8—ę¬u^ĺ 6ŞŕIkę [iL)¬¶g S:˛§®°„%^á[x©$"tdO]a «5Żă!($"$łľtnO5ĐÍ „N"ßµ´ô´jhî_SČŔË(#,{ę s ˇ+Źó´ăE‰őYtË]8Ăęc©©+L;(źewC›bÇ‹ëłčYÂr§®ŕŚr*ÝAÚĄŘń˘Äú,z–°Ü©+8˝iyÉ!8ó/Ł– Úńß{ăh?)NólÂş61ř)WľÖyľ°ţŐR µŘqz™€IĚצśQ5Ţşv ‰ ůĚĄ]/nY.Ŕ^TŔYöŔdl&n3Sşé˙nN9ř·PMčP{Î2z.ÓuĄ}źţŻí[9q|ÓĺśEśeĚ$–fZXůŕ˘j**ĎoűXz™ÝA㎷š{Ó.šęyů·­”ŞĄśEśeĚŇfr\ʰňÄÇ)U™_!'ŢR“ä,eGuňČ-,g™€f7ŢŞţŹO•+Ł“ôr΢ĚţŘL îV–…VU5ů…ĺŤ 5IÝÜ™^řrv< ¦>™ýłs3÷fQČRWµ˘”•Z/W#°Gç\a5äĆNXÄ 4NeeýJŮđGš$á#¦Ťľg™€Ć®±ÔOJ7}·ěěúq­ĆÄrÎ8†#,S—ÔX:§łlTŐd/Đ@©Nr)dÇĂĐË ö-#V*gţ9ĚÎzP›Ea™ő k´…5Ú9ťc/«ěÚmźhÖK褱]ţčÚ¤¦°:O)·Źµ>vBýŘYă­â'A+şZ=QĽ^.Ŕ™u„ež ÍÎňć‡ضŤeFVÜ'çr‹÷™¤ŤV'Źě=ďz™µî@kú±¤ôÝĄ3Îô‡zą{dÖ–ÉŘôc9Ë#N/<@«Ş)&,:‚T|MÜy¶żÄ·›Â§â•U1aŮzÚÔX‹ý˙cŐÇ›ˇÚ@ő›_Aj?„lŻ‹ëQüóxV+%},7‚To‰ťiO…ŐyJĄŁęćPTśBÂÚGš$ácÍI>Vuňř°°˘TŐÖ>‚tMBés‰TUSOÖbđ[Y j)ĐZěx(ŢŰçIôČ čŃM-•˘ŞEë'R…嬭ť2¶^‘(+ëęÄB$ ËŢP?ŚŢAč¤6đȢ‹©&âđ™0ŠÜ.ßüŤskl$*RŮ Ń·ŢS'T•RžĽç©ë‹­d ]ý„En>Â?7&ÖX'HĚt|ŤuJVMBňN©ĹÚĂ!đăŇn@wCnt(˛ń`Ť‰5Ö ©Lk$Ľ÷3m •q˝[?8á8‚”%BWç)Őécɨ¤±ĺŃ1:ÖŘEă]ł‰v6O…ý°‰d:µ9¶(ň@b>!"‚tâšÂN^´~Ćľź'(j˘–MęşÁč¦Đć_>‹˘LźĆa–ŃˇŁŽ#HąKů€BëG4gťĽ™"ÖŘÖÍdź¸˝÷żUU¶E1,ŹHqU5ŃÂ*ąřŽşB ±šćă÷Äű)_Yqމ†ó~Mâ^U·úXĺ©ĹŽâÜ*,Ş˛zаŽC“Ş›ő3ÂşŹkÚ@Mn+"4™<Â*ĚĄŞj˛hDhrĂŁÓ„Uť§ô8çýjYeVDh˛ˇţ®°®żčÁ[Ďđ±şăĐd÷őŁBvÔ˵’˘şă/čúĐd1Ĺűw…u!×zë®M:ę˛VnîTUSLXţĐd9—ß^Xď MľíC´€·YpÇä¶ž^ 8ď·m`Ý>V7÷¬Ů…zr „&Ó—ýLSXľ/µÉ#¬±çŻnS3GP9B“éË~FXĄąŮµ2D¨zÍqôŹŘ89B“ÉËBX9¸ő9Đ%ş@™ő˙j;Şó”~đ±Šńăě0ą‰Ż±äkµyN´Ł:yTçĽßŇąžŃÇę¦ý˘»yřLSX  ľqŐSaϧ·,Âg„•ťŞŔ•Z ´šPÖgQ—_eQMy~ĆÇĘI­Şâd™â8‚ôŰoBçËבUużhtŤ5-Î;ź}đ ô8‚t:˘źµ–šł8/µÉ#,%…ŇRݰËć3ÂĘBÍm "©ôř„¨R*+žČ*ˇ@ŐÄCł&w¤2=ĹŽęęůť÷jź7$u61¤G¤‚öł ü’o`Ô¦ş_4ľ #;HŹ"H9Ý|ŢŽ‡ó˘Q›LÂŠĺ ‚T°×g0‚‹Şj0#öăŽRQď}¦Ć:ĂCÜ*‡Ä¦p7w¨'ÇPi7™|ĆÇJ¦oýď…ś÷@é(:˛Čł|T'ŹëśwKUwý.gÎLŠ my´=9ú#Hűô—)ŢDR!W0jă•U.aĹužć3ЧPؤť˝LjéqĚ{a;ľB!ĎęĽRIö±îXş:O©¸óţtY%=Äú®×ŘQť<Ę:ďž6đW‹<íZˇ_´–&¨šĂŇUˇQ›€·TFXŐ”g-vÜJş»^Ú?Oö=Ž ťÂď˛~•w&–řŇ\svzůŘx;Şó”ň;ď'Ľőše•ŇŹő8AÚZ5‘vT'ŹĚ멬âóÝ6oŐý˘É¤GGŠťnŰ˙™¦0˨;˛z®°ěx?Ýq©t´ó®x]¨÷±b»°Ž"H·AZ‰vĽŠSűd•ŇĆ.ŇtAzBXŐŐóyś÷·VV‚ë#H=MáWć 5©|&ŹDs˙ţ®6÷äý»%‚Î{ăié欮j.$¬@©ěnh‹ŰQ-ä¨Í=Ýă)$¬@©ě íŠŰQ'ľŞęzK SHXˇ9HÓ‡tŞ«çO&Ň/TUSOMńaIUmSYŐdńŠťçýµĽô¨Mő3+zž÷sĽ[XtŹŐ;Ű@EŇ[: 1ďgř ¬ˇő˙v„ň­®žOHô9ěď–UB)ćy?“HŻksqđ|¬7ˇ˝¤¦—ÁЍ9aÇ“ *«óŻ€>+1í©đx#o© í–<öÓ7ĽNXĽ˝–řó;Č\ ţR8ú±ÂĘ–µą…ĚęŹ 5zŁ9/ž*˘dVçŤ 5Ń2c»4…í[_¦8ZÖ¦2sË%F†˛Ç/ ŕ‹ ]?ůű>/}ý+˘˛ŞN·;ď±x"HŤ°&îĽ_},hm.‹ 5MˇÇÇzú¤Ďš$´đGRiě˛ržR㼿2ćÝ®¬Â>ÖWHę Mśç݉ 5Łb竞 7m śwN´°âV¦h¬§Bב2ŁÂÇšßăc%zVŐ)ŕna±Í÷ÁűR3¤óŞIAŕ°{H„n6cöńoB3Li%žť í3äď -`ÇýÂr‡‘XÖ¦‚ßĺšÄZš Zě q#~k˙ŞSŔŰ…U‹!{rĘę3TSžµŘ±ĹŤź‚·M¤Ź?ŰĚqi÷Uě˙|•Őůl?Cîšâ8‚”wµ&Bß#¬M¬gô¨ śwÎő¤ßHŇą]e•« ¬Nw ‹Kˇë‰H*7»ăR±qŢŽ řŰ9V𬒉+äŻMÍ>ˇ;Ž íÓ}¬+)VU}Śř!ťĄb]ěXa ‚” «ŇĐäUU<Ń«*8 B‹—Uă„%*+:‚”Y5‘v\%,»˛ú/Ôcçý01^XÓ8ĚŃa3RO ßí¤˘ 4?ĘĘŞŔ…ž5eęIR†ţ&ĆxËF»Ţ6G¤2))‚ôĐ˝ž™B1ďŇ6˝),d•ťB DňŤŞśwzÔ¦Đ5?Cć~,“c ‚t¬iH§Ŕ¨ śwNć~¬ÓÜÓFmJQťîVt?Ö9®=ń şBł’ż«°?BĎ#„Q›ěäďÇ*`G®Úz#*•H‹ ÎűoäďÇ:iGČ,…ĽŻŞxbQ8ď͇"Hé‰ônhkSŔíÎ{aŠÚAĎĎżŞ$ń:ňIA"–YU1|űźŻBV5•Ő§Č>)ŠM^ç˛őoDŰńKmŐ-ÄgďÇ’JŘ ňmбÉ&9°mÇér ··«:gčn+rR›ěF"Ó§ěH Ę6°:Ü-,ĺ]M Ň鸙F}6NŮM•˛úÉ>Öń¤ lÍ—7NŮú×ď&á©p`QO…„•V!ŰŁ6&‘ś÷‚đmÎ5…ˇĐä„ĐÚETűĐÚET N|śŹőEhňyîsŢ}9ňÎEu^öÝÎ»ě Ť5ą±şÚŕĆ9;ĽŔ[݉d罋Ë҉Dölś˛ĂFm*#µ»!"lFf™sHç°BöVVŐµź!wi;ʱ˛Q›°°Şłčn«ÖY“źÖV§€»…5±ö¶Y“ýŔaŻ•ř¦°ěÄɧň„¬ęĄš@żäĐdÝ:‰ŰöęZŻđŚĐä]QU=jçťs…°Ľ1Ąçěđ 0ź5ďZŞSŔs…ĺŤ)=cÇFm@s…°ü1ĄévŔ[ ĺ…ĺŹ)Ť¶CÖągz¬Şk!>CyauŢ@šh;ž5jçťsÉSˇ'ô/ÁŽ÷´Ő)ŕĂÂzÚ¨ h®ÖŮRgÎYóńď_‰PK|äů(·®ĄË먬vunžumŕĽä:ať }㍠śwÎuÂJŚ %gH{Á \Ő)ŕńÂJŇyö¨ h*ŠnX71jóŞĄŞBëÚŔy/H]ÂzŨ śwNMÂzO÷z€ęđva±OČę3ÔAŠQ›wqʰvkŤSv_×Î{An–Š) ÚńÜQ8ďś;„ĄcJ˝vĽ ÝKu x‘°tL)iÇ›Eő%îVG\Vîđ*häłëłč–»P RXŢŞŞşaý–XBX°şaý–XŽ˝°Ŕ˸CVőtř—a"@X x Ô»ŃNş·ßk\ΤçTáĎă@e­;"Ńś@eç¶3Ę$’F-ŮÍ^‹L"e‘ą–ç6C¨Ű$÷ufHŹ0hM¤ ęB·¨ ݢ‹!§»µÓ÷ˇĚ:eî–˙Ô™ęśqčřÂež ‡qŮtÍ ”Qvn;ŁÖ3)٦ˇëćŮc‘I¤,2×ňÜ&cu›Ô™„Eă`6ö™DŇ ˝Ź4Č:Á_nANwk1t>É)‘8:üY0ötâÄŻ»y±Ăś@eç¶3Ę$’FÉéP<™DĘ"s-Ďm2†P·IîŁ,2óýP™DĘ łŹ2Č>Á_nᕇdžĽC@bŇš}AlĂźť(®KÂBF±Q,`”µŹ¨[Śęv¨kŃC(‹Ô>ʢ٭†ťHdöQY'Ęí"zës˙“đ8ň•ŁZ˘)ěěs¶…¬{ŹG#Nđ%«ŹQ<ŃgT7´öaT"i‘ľi‘1„˛HďŁ,ćž­kÇoëmťHdöQ­'„Ęí"őąCüEx śÓăöŻç´ű#D"ţ!K~‚×(žč5J¶¤QsżN ¶łH%R™kQ™DʢuaQßÚ~×Ć “HdöQ™Äpą]CPX˘nő8qŻtďěşůu„÷&…ĹO÷­ť8Ág”Hô%˝F™˘,’EEXd®EYd)‹Ěľ€E~D"eŮG K'Ëí"BMˇlí“},űj…t¶±Ď[śŕ3Š'zŤ’gzŤŇ;É5Űy"aŃz-Â"“HYdťéµ ≤vô'u‹tb¸Ü."伇C¨CuťŢKŢ4ĺc‘gËÖđ±ĆΖéën=Sse”›ŰĆ(“HŐ 7g¤-2‰”EćZŢŰt(,ʢIwšR™DĘ łŹ2Č=áfaéž ýgĘÖÎS!Ë=Ä`Ćz‘µ›K8sŹU!-҉ž(ýlçůe…Ež9éĘ ťH=ęu»°|;®/>˝ÁBL„ľ1÷:î~…ç„ÓůކĎ‚y¶Nłvů…EďWˇ®˝ĄSz~đ4j–=·‚g~đ4Š˙f˛]’Ż›x"ßćˇŢŁ:šż´©Ţ»űÁJkô+N»ŐęłÍ޵!–/-ôˇ'ŔŁŕ%Í'ë1˛ŕ%?©'Lü3úp™Mëˇs3ZiŤ~Ýf’o·ł™-,}vÓlk¬–7†ü]+ďŚŕY05YĎŞëm(íë zFćTM¬sŇÔîYď¶°ôŮMăkě'3ŤwĆ đ,„8–b5j™‡É«~«N´Şa•…“ĆëŤwąa Ëśd~…\GËń"¨oĆ đ0ú\]ďyq„:;Ńz‰ť–ť&ţ“Żľ{„eěXŞ8QĂ9'粫±řŹÚÇŇ5–©„a9iĚ:)ŞĆZ é;qĽgĆ đ4v>–Ü+ż´Ż#ý¨–“f|,Ţí},+ëť°f9M©w~đ,xĎŔ´T˘®;)­ţpç>ŠSôˇŞsx*TłĂňÇĂXaŤň©Á;żxV?VÓrĎhyÜ_;›ZŐŹ5ők_Őz¨”…ťfú±äÄÝŰ~,˝Ńlŕ¨ZĘ7ż€đ?óŁéúOLIEND®B`‚libtorrent-rasterbar-1.1.13/docs/manual-ref.html000066400000000000000000003570111351156116000216170ustar00rootroot00000000000000 libtorrent API Documentation

libtorrent API Documentation

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

overview

The interface of libtorrent consists of a few classes. The main class is the session, it contains the main loop that serves all torrents.

The basic usage is as follows:

Each class and function is described in this manual, you may want to have a look at the tutorial as well.

For a description on how to create torrent files, see create_torrent.

forward declarations

Forward declaring types from the libtorrent namespace is discouraged as it may break in future releases. Instead include libtorrent/fwd.hpp for forward declarations of all public types in libtorrent.

trouble shooting

A common problem developers are facing is torrents stopping without explanation. Here is a description on which conditions libtorrent will stop your torrents, how to find out about it and what to do about it.

Make sure to keep track of the paused state, the error state and the upload mode of your torrents. By default, torrents are auto-managed, which means libtorrent will pause, resume, scrape them and take them out of upload-mode automatically.

Whenever a torrent encounters a fatal error, it will be stopped, and the torrent_status::error will describe the error that caused it. If a torrent is auto managed, it is scraped periodically and paused or resumed based on the number of downloaders per seed. This will effectively seed torrents that are in the greatest need of seeds.

If a torrent hits a disk write error, it will be put into upload mode. This means it will not download anything, but only upload. The assumption is that the write error is caused by a full disk or write permission errors. If the torrent is auto-managed, it will periodically be taken out of the upload mode, trying to write things to the disk again. This means torrent will recover from certain disk errors if the problem is resolved. If the torrent is not auto managed, you have to call set_upload_mode() to turn downloading back on again.

For a more detailed guide on how to trouble shoot performance issues, see troubleshooting

network primitives

There are a few typedefs in the libtorrent namespace which pulls in network types from the boost::asio namespace. These are:

typedef boost::asio::ip::address address;
typedef boost::asio::ip::address_v4 address_v4;
typedef boost::asio::ip::address_v6 address_v6;
using boost::asio::ip::tcp;
using boost::asio::ip::udp;

These are declared in the <libtorrent/socket.hpp> header.

The using statements will give easy access to:

tcp::endpoint
udp::endpoint

Which are the endpoint types used in libtorrent. An endpoint is an address with an associated port.

For documentation on these types, please refer to the asio documentation.

exceptions

Many functions in libtorrent have two versions, one that throws exceptions on errors and one that takes an error_code reference which is filled with the error code on errors.

There is one exception class that is used for errors in libtorrent, it is based on boost.system's error_code class to carry the error code.

For more information, see libtorrent_exception and error_code_enum.

translating error codes

The error_code::message() function will typically return a localized error string, for system errors. That is, errors that belong to the generic or system category.

Errors that belong to the libtorrent error category are not localized however, they are only available in english. In order to translate libtorrent errors, compare the error category of the error_code object against libtorrent::libtorrent_category(), and if matches, you know the error code refers to the list above. You can provide your own mapping from error code to string, which is localized. In this case, you cannot rely on error_code::message() to generate your strings.

The numeric values of the errors are part of the API and will stay the same, although new error codes may be appended at the end.

Here's a simple example of how to translate error codes:

std::string error_code_to_string(boost::system::error_code const& ec)
{
        if (ec.category() != libtorrent::libtorrent_category())
        {
                return ec.message();
        }
        // the error is a libtorrent error

        int code = ec.value();
        static const char const* swedish[] =
        {
                "inget fel",
                "en fil i torrenten kolliderar med en fil fran en annan torrent",
                "hash check misslyckades",
                "torrentfilen ar inte en dictionary",
                "'info'-nyckeln saknas eller ar korrupt i torrentfilen",
                "'info'-faltet ar inte en dictionary",
                "'piece length' faltet saknas eller ar korrupt i torrentfilen",
                "torrentfilen saknar namnfaltet",
                "ogiltigt namn i torrentfilen (kan vara en attack)",
                // ... more strings here
        };

        // use the default error string in case we don't have it
        // in our translated list
        if (code < 0 || code >= sizeof(swedish)/sizeof(swedish[0]))
                return ec.message();

        return swedish[code];
}

queuing

libtorrent supports queuing. Queuing is a mechanism to automatically pause and resume torrents based on certain criteria. The criteria depends on the overall state the torrent is in (checking, downloading or seeding).

To opt-out of the queuing logic, make sure your torrents are added with the add_torrent_params::flag_auto_managed bit cleared. Or call torrent_handle::auto_managed(false) on the torrent handle.

The overall purpose of the queuing logic is to improve performance under arbitrary torrent downloading and seeding load. For example, if you want to download 100 torrents on a limited home connection, you improve performance by downloading them one at a time (or maybe two at a time), over downloading them all in parallel. The benefits are:

  • the average completion time of a torrent is half of what it would be if all downloaded in parallel.
  • The amount of upload capacity is more likely to reach the reciprocation rate of your peers, and is likely to improve your return on investment (download to upload ratio)
  • your disk I/O load is likely to be more local which may improve I/O performance and decrease fragmentation.

There are fundamentally 3 seaparate queues:

  • checking torrents
  • downloading torrents
  • seeding torrents

Every torrent that is not seeding has a queue number associated with it, this is its place in line to be started. See torrent_status::queue_position.

On top of the limits of each queue, there is an over arching limit, set in settings_pack::active_limit. The auto manager will never start more than this number of torrents (with one exception described below). Non-auto-managed torrents are exempt from this logic, and not counted.

At a regular interval, torrents are checked if there needs to be any re-ordering of which torrents are active and which are queued. This interval can be controlled via settings_pack::auto_manage_interval.

For queuing to work, resume data needs to be saved and restored for all torrents. See torrent_handle::save_resume_data().

queue position

The torrents in the front of the queue are started and the rest are ordered by their queue position. Any newly added torrent is placed at the end of the queue. Once a torrent is removed or turns into a seed, its queue position is -1 and all torrents that used to be after it in the queue, decreases their position in order to fill the gap.

The queue positions are always contiguous, in a sequence without any gaps.

Lower queue position means closer to the front of the queue, and will be started sooner than torrents with higher queue positions.

To query a torrent for its position in the queue, or change its position, see: torrent_handle::queue_position(), torrent_handle::queue_position_up(), torrent_handle::queue_position_down(), torrent_handle::queue_position_top() and torrent_handle::queue_position_bottom().

checking queue

The checking queue affects torrents in the torrent_status::checking or torrent_status::allocating state that are auto-managed.

The checking queue will make sure that (of the torrents in its queue) no more than settings_pack::active_checking_limit torrents are started at any given time. Once a torrent completes checking and moves into a diffferent state, the next in line will be started for checking.

Any torrent added force-started or force-stopped (i.e. the auto managed flag is not set), will not be subject to this limit and they will all check independently and in parallel.

Once a torrent completes the checking of its files, or fastresume data, it will be put in the queue for downloading and potentially start downloading immediately. In order to add a torrent and check its files without starting the download, it can be added in stop_when_ready mode. See add_torrent_params::flag_stop_when_ready. This flag will stop the torrent once it is ready to start downloading.

This is conceptually the same as waiting for the torrent_checked_alert and then call:

h.auto_managed(false);
h.pause();

With the important distinction that it entirely avoids the brief window where the torrent is in downloading state.

downloading queue

Similarly to the checking queue, the downloading queue will make sure that no more than settings_pack::active_downloads torrents are in the downloading state at any given time.

The torrent_status::queue_position is used again here to determine who is next in line to be started once a downloading torrent completes or is stopped/removed.

seeding queue

The seeding queue does not use torrent_status::queue_position to determine which torrent to seed. Instead, it estimates the demand for the torrent to be seeded. A torrent with few other seeds and many downloaders is assumed to have a higher demand of more seeds than one with many seeds and few downloaders.

It limits the number of started seeds to settings_pack::active_seeds.

On top of this basic bias, seed priority can be controller by specifying a seed ratio (the upload to download ratio), a seed-time ratio (the download time to seeding time ratio) and a seed-time (the absolute time to be seeding a torrent). Until all those targets are hit, the torrent will be prioritized for seeding.

Among torrents that have met their seed target, torrents where we don't know of any other seed take strict priority.

In order to avoid flapping, torrents that were started less than 30 minutes ago also have priority to keep seeding.

Finally, for torrents where none of the above apply, they are prioritized based on the download to seed ratio.

The relevant settings to control these limits are settings_pack::share_ratio_limit, settings_pack::seed_time_ratio_limit and settings_pack::seed_time_limit.

queuing options

In addition to simply starting and stopping torrents, the queuing mechanism can have more fine grained control of the resources used by torrents.

half-started torrents

In addition to the downloading and seeding limits, there are limits on actions torrents perform. The downloading and seeding limits control whether peers are allowed at all, and if peers are not allowed, torrents are stopped and don't do anything. If peers are allowed, torrents may:

  1. announce to trackers
  2. announce to the DHT
  3. announce to local peer discovery (local service discovery)

Each of those actions are associated with a cost and hence may need a separate limit. These limits are controlled by settings_pack::active_tracker_limit, settings_pack::active_dht_limit and settings_pack::active_lsd_limit respectively.

Specifically, announcing to a tracker is typically cheaper than announcing to the DHT. active_dht_limit will limit the number of torrents that are allowed to announce to the DHT. The highest priority ones will, and the lower priority ones won't. The will still be considered started though, and any incoming peers will still be accepted.

If you do not wish to impose such limits (basically, if you do not wish to have half-started torrents) make sure to set these limits to -1 (infinite).

prefer seeds

In the case where active_downloads + active_seeds > active_limit, there's an ambiguity whether the downloads should be satisfied first or the seeds. To disambiguate this case, the settings_pack::auto_manage_prefer_seeds determines whether seeds are preferred or not.

inactive torrents

Torrents that are not transferring any bytes (downloading or uploading) have a relatively low cost to be started. It's possible to exempt such torrents from the download and seed queues by setting settings_pack::dont_count_slow_torrents to true.

Since it sometimes may take a few minutes for a newly started torrent to find peers and be unchoked, or find peers that are interested in requesting data, torrents are not considered inactive immadiately. There must be an extended period of no transfers before it is considered inactive and exempt from the queuing limits.

fast resume

The fast resume mechanism is a way to remember which pieces are downloaded and where they are put between sessions. You can generate fast resume data by calling save_resume_data() on torrent_handle. You can then save this data to disk and use it when resuming the torrent. libtorrent will not check the piece hashes then, and rely on the information given in the fast-resume data. The fast-resume data also contains information about which blocks, in the unfinished pieces, were downloaded, so it will not have to start from scratch on the partially downloaded pieces.

To use the fast-resume data you simply give it to async_add_torrent() and add_torrent(), and it will skip the time consuming checks. It may have to do the checking anyway, if the fast-resume data is corrupt or doesn't fit the storage for that torrent, then it will not trust the fast-resume data and just do the checking.

file format

The file format is a bencoded dictionary containing the following fields:

file-format string: "libtorrent resume file"
file-version integer: 1
info-hash string, the info hash of the torrent this data is saved for.
blocks per piece integer, the number of blocks per piece. Must be: piece_size / (16 * 1024). Clamped to be within the range [1, 256]. It is the number of blocks per (normal sized) piece. Usually each block is 16 * 1024 bytes in size. But if piece size is greater than 4 megabytes, the block size will increase.
pieces A string with piece flags, one character per piece. Bit 1 means we have that piece. Bit 2 means we have verified that this piece is correct. This only applies when the torrent is in seed_mode.
slots list of integers. The list maps slots to piece indices. It tells which piece is on which slot. If piece index is -2 it means it is free, that there's no piece there. If it is -1, means the slot isn't allocated on disk yet. The pieces have to meet the following requirement:
total_uploaded integer. The number of bytes that have been uploaded in total for this torrent.
total_downloaded integer. The number of bytes that have been downloaded in total for this torrent.
active_time integer. The number of seconds this torrent has been active. i.e. not paused.
seeding_time integer. The number of seconds this torrent has been active and seeding.
num_seeds integer. An estimate of the number of seeds on this torrent when the resume data was saved. This is scrape data or based on the peer list if scrape data is unavailable.
num_downloaders integer. An estimate of the number of downloaders on this torrent when the resume data was last saved. This is used as an initial estimate until we acquire up-to-date scrape info.
last_upload integer. The number of seconds since epoch when we last uploaded payload to a peer on this torrent.
last_download integer. The number of seconds since epoch when we last downloaded payload from a peer on this torrent.
last_scrape integer. The number of seconds since epoch when we last sent a scrape request to a tracker on this torrent.
upload_rate_limit integer. In case this torrent has a per-torrent upload rate limit, this is that limit. In bytes per second.
download_rate_limit integer. The download rate limit for this torrent in case one is set, in bytes per second.
max_connections integer. The max number of peer connections this torrent may have, if a limit is set.
max_uploads integer. The max number of unchoked peers this torrent may have, if a limit is set.
seed_mode integer. 1 if the torrent is in seed mode, 0 otherwise.
file_priority list of integers. One entry per file in the torrent. Each entry is the priority of the file with the same index.
piece_priority string of bytes. Each byte is interpreted as an integer and is the priority of that piece.
auto_managed integer. 1 if the torrent is auto managed, otherwise 0.
sequential_download integer. 1 if the torrent is in sequential download mode, 0 otherwise.
paused integer. 1 if the torrent is paused, 0 otherwise.
trackers list of lists of strings. The top level list lists all tracker tiers. Each second level list is one tier of trackers.
mapped_files list of strings. If any file in the torrent has been renamed, this entry contains a list of all the filenames. In the same order as in the torrent file.
url-list list of strings. List of url-seed URLs used by this torrent. The urls are expected to be properly encoded and not contain any illegal url characters.
httpseeds list of strings. List of httpseed URLs used by this torrent. The urls are expected to be properly encoded and not contain any illegal url characters.
merkle tree string. In case this torrent is a merkle torrent, this is a string containing the entire merkle tree, all nodes, including the root and all leaves. The tree is not necessarily complete, but complete enough to be able to send any piece that we have, indicated by the have bitmask.
save_path string. The save path where this torrent was saved. This is especially useful when moving torrents with move_storage() since this will be updated.
peers string. This string contains IPv4 and port pairs of peers we were connected to last session. The endpoints are in compact representation. 4 bytes IPv4 address followed by 2 bytes port. Hence, the length of this string should be divisible by 6.
banned_peers string. This string has the same format as peers but instead represent IPv4 peers that we have banned.
peers6 string. This string contains IPv6 and port pairs of peers we were connected to last session. The endpoints are in compact representation. 16 bytes IPv6 address followed by 2 bytes port. The length of this string should be divisible by 18.
banned_peers6 string. This string has the same format as peers6 but instead represent IPv6 peers that we have banned.
info If this field is present, it should be the info-dictionary of the torrent this resume data is for. Its SHA-1 hash must match the one in the info-hash field. When present, the torrent is loaded from here, meaning the torrent can be added purely from resume data (no need to load the .torrent file separately). This may have performance advantages.
unfinished

list of dictionaries. Each dictionary represents an piece, and has the following layout:

piece integer, the index of the piece this entry refers to.
bitmask string, a binary bitmask representing the blocks that have been downloaded in this piece.
adler32 The adler32 checksum of the data in the blocks specified by bitmask.
file sizes list where each entry corresponds to a file in the file list in the metadata. Each entry has a list of two values, the first value is the size of the file in bytes, the second is the time stamp when the last time someone wrote to it. This information is used to compare with the files on disk. All the files must match exactly this information in order to consider the resume data as current. Otherwise a full re-check is issued.
allocation The allocation mode for the storage. Can be either full or sparse. If this is full, the file sizes and timestamps are disregarded. Pieces are assumed not to have moved around even if the files have been modified after the last resume data checkpoint.

storage allocation

There are two modes in which storage (files on disk) are allocated in libtorrent.

  1. The traditional full allocation mode, where the entire files are filled up with zeros before anything is downloaded. Files are allocated on demand, the first time anything is written to them. The main benefit of this mode is that it avoids creating heavily fragmented files.
  2. The sparse allocation, sparse files are used, and pieces are downloaded directly to where they belong. This is the recommended (and default) mode.

sparse allocation

On filesystems that supports sparse files, this allocation mode will only use as much space as has been downloaded.

The main drawback of this mode is that it may create heavily fragmented files.

  • It does not require an allocation pass on startup.

full allocation

When a torrent is started in full allocation mode, the disk-io thread will make sure that the entire storage is allocated, and fill any gaps with zeros. It will of course still check for existing pieces and fast resume data. The main drawbacks of this mode are:

  • It may take longer to start the torrent, since it will need to fill the files with zeros. This delay is linear to the size of the download.
  • The download may occupy unnecessary disk space between download sessions.
  • Disk caches usually perform poorly with random access to large files and may slow down the download some.

The benefits of this mode are:

  • Downloaded pieces are written directly to their final place in the files and the total number of disk operations will be fewer and may also play nicer to filesystems' file allocation, and reduce fragmentation.
  • No risk of a download failing because of a full disk during download, once all files have been created.

HTTP seeding

There are two kinds of HTTP seeding. One with that assumes a smart (and polite) client and one that assumes a smart server. These are specified in BEP 19 and BEP 17 respectively.

libtorrent supports both. In the libtorrent source code and API, BEP 19 urls are typically referred to as url seeds and BEP 17 urls are typically referred to as HTTP seeds.

The libtorrent implementation of BEP 19 assumes that, if the URL ends with a slash ('/'), the filename should be appended to it in order to request pieces from that file. The way this works is that if the torrent is a single-file torrent, only that filename is appended. If the torrent is a multi-file torrent, the torrent's name '/' the file name is appended. This is the same directory structure that libtorrent will download torrents into.

dynamic loading of torrent files

Note

This feature will be removed in the next major release of libtorrent. As an alternative, torrents can be loaded on demand by plugins.

libtorrent has a feature that can unload idle torrents from memory. The purpose of this is to support being active on many more torrents than the RAM permits. This is useful for both embedded devices that have limited RAM and servers seeding tens of thousands of torrents.

The most significant parts of loaded torrents that use RAM are the piece hashes (20 bytes per piece) and the file list. The entire info-dictionary of the .torrent file is kept in RAM.

In order to activate the dynamic loading of torrent files, set the load function on the session. See set_load_function().

When a load function is set on the session, the dynamic load/unload feature is enabled. Torrents are kept in an LRU. Every time an operation is performed, on a torrent or from a peer, that requires the metadata of the torrent to be loaded, the torrent is bumped up in the LRU. When a torrent is paused or queued, it is demoted to the least recently used torrent in the LRU, since it's a good candidate for eviction.

To configure how many torrents are allowed to be loaded at the same time, set settings_pack::active_loaded_limit on the session.

Torrents can be exempt from being unloaded by being pinned. Pinned torrents still count against the limit, but are never considered for eviction. You can either pin a torrent when adding it, in add_torrent_params (see async_add_torrent() and add_torrent()), or after ading it with the set_pinned() function on torrent_handle.

Torrents that start out without metadata (e.g. magnet links or http downloads) are automatically pinned. This is important in order to give the client a chance to save the metadata to disk once it's received (see metadata_received_alert).

Once the metadata is saved to disk, it might make sense to unpin the torrent.

piece picker

The piece picker in libtorrent has the following features:

  • rarest first
  • sequential download
  • random pick
  • reverse order picking
  • parole mode
  • prioritize partial pieces
  • prefer whole pieces
  • piece affinity by speed category
  • piece priorities

internal representation

It is optimized by, at all times, keeping a list of pieces ordered by rarity, randomly shuffled within each rarity class. This list is organized as a single vector of contigous memory in RAM, for optimal memory locality and to eliminate heap allocations and frees when updating rarity of pieces.

Expensive events, like a peer joining or leaving, are evaluated lazily, since it's cheaper to rebuild the whole list rather than updating every single piece in it. This means as long as no blocks are picked, peers joining and leaving is no more costly than a single peer joining or leaving. Of course the special cases of peers that have all or no pieces are optimized to not require rebuilding the list.

picker strategy

The normal mode of the picker is of course rarest first, meaning pieces that few peers have are preferred to be downloaded over pieces that more peers have. This is a fundamental algorithm that is the basis of the performance of bittorrent. However, the user may set the piece picker into sequential download mode. This mode simply picks pieces sequentially, always preferring lower piece indices.

When a torrent starts out, picking the rarest pieces means increased risk that pieces won't be completed early (since there are only a few peers they can be downloaded from), leading to a delay of having any piece to offer to other peers. This lack of pieces to trade, delays the client from getting started into the normal tit-for-tat mode of bittorrent, and will result in a long ramp-up time. The heuristic to mitigate this problem is to, for the first few pieces, pick random pieces rather than rare pieces. The threshold for when to leave this initial picker mode is determined by settings_pack::initial_picker_threshold.

reverse order

An orthogonal setting is reverse order, which is used for snubbed peers. Snubbed peers are peers that appear very slow, and might have timed out a piece request. The idea behind this is to make all snubbed peers more likely to be able to do download blocks from the same piece, concentrating slow peers on as few pieces as possible. The reverse order means that the most common pieces are picked, instead of the rarest pieces (or in the case of sequential download, the last pieces, intead of the first).

parole mode

Peers that have participated in a piece that failed the hash check, may be put in parole mode. This means we prefer downloading a full piece from this peer, in order to distinguish which peer is sending corrupt data. Whether to do this is or not is controlled by settings_pack::use_parole_mode.

In parole mode, the piece picker prefers picking one whole piece at a time for a given peer, avoiding picking any blocks from a piece any other peer has contributed to (since that would defeat the purpose of parole mode).

prioritize partial pieces

This setting determines if partially downloaded or requested pieces should always be preferred over other pieces. The benefit of doing this is that the number of partial pieces is minimized (and hence the turn-around time for downloading a block until it can be uploaded to others is minimized). It also puts less stress on the disk cache, since fewer partial pieces need to be kept in the cache. Whether or not to enable this is controlled by setting_pack::prioritize_partial_pieces.

The main benefit of not prioritizing partial pieces is that the rarest first algorithm gets to have more influence on which pieces are picked. The picker is more likely to truly pick the rarest piece, and hence improving the performance of the swarm.

This setting is turned on automatically whenever the number of partial pieces in the piece picker exceeds the number of peers we're connected to times 1.5. This is in order to keep the waste of partial pieces to a minimum, but still prefer rarest pieces.

prefer whole pieces

The prefer whole pieces setting makes the piece picker prefer picking entire pieces at a time. This is used by web connections (both http seeding standards), in order to be able to coalesce the small bittorrent requests to larger HTTP requests. This significantly improves performance when downloading over HTTP.

It is also used by peers that are downloading faster than a certain threshold. The main advantage is that these peers will better utilize the other peer's disk cache, by requesting all blocks in a single piece, from the same peer.

This threshold is controlled by the settings_pack::whole_pieces_threshold setting.

TODO: piece priorities

predictive piece announce

In order to improve performance, libtorrent supports a feature called predictive piece announce. When enabled, it will make libtorrent announce that we have pieces to peers, before we truly have them. The most important case is to announce a piece as soon as it has been downloaded and passed the hash check, but not yet been written to disk. In this case, there is a risk the piece will fail to be written to disk, in which case we won't have the piece anymore, even though we announced it to peers.

The other case is when we're very close to completing the download of a piece and assume it will pass the hash check, we can announce it to peers to make it available one round-trip sooner than otherwise. This lets libtorrent start uploading the piece to interested peers immediately when the piece complete, instead of waiting one round-trip for the peers to request it.

This makes for the implementation slightly more complicated, since piece will have more states and more complicated transitions. For instance, a piece could be:

  1. hashed but not fully written to disk
  2. fully written to disk but not hashed
  3. not fully downloaded
  4. downloaded and hash checked

Once a piece is fully downloaded, the hash check could complete before any of the write operations or it could complete after all write operations are complete.

peer classes

The peer classes feature in libtorrent allows a client to define custom groups of peers and rate limit them individually. Each such group is called a peer class. There are a few default peer classes that are always created:

  • global - all peers belong to this class, except peers on the local network
  • local peers - all peers on the local network belongs to this class TCP peers
  • tcp class - all peers connected over TCP belong to this class

The TCP peers class is used by the uTP/TCP balancing logic, if it's enabled, to throttle TCP peers. The global and local classes are used to adjust the global rate limits.

When the rate limits are adjusted for a specific torrent, a class is created implicitly for that torrent.

The default peer class IDs are defined as enums in the session class:

enum {
        global_peer_class_id,
        tcp_peer_class_id,
        local_peer_class_id
};

The default peer classes are automatically created on session startup, and configured to apply to each respective type of connection. There's nothing preventing a client from reconfiguring the peer class ip- and type filters to disable or customize which peers they apply to. See set_peer_class_filter() and set_peer_class_type_filter().

A peer class can be considered a more general form of lables that some clients have. Peer classes however are not just applied to torrents, but ultimately the peers.

Peer classes can be created with the create_peer_class() call (on the session object), and deleted with the delete_peer_class() call.

Peer classes are configured with the set_peer_class() get_peer_class() calls.

Custom peer classes can be assigned based on the peer's IP address or the type of transport protocol used. See set_peer_class_filter() and set_peer_class_type_filter() for more information.

peer class examples

Here are a few examples of common peer class operations.

To make the global rate limit apply to local peers as well, update the IP-filter based peer class assignment:

std::uint32_t const mask = 1 << lt::session::global_peer_class_id;
ip_filter f;

// for every IPv4 address, assign the global peer class
f.add_rule(make_address("0.0.0.0"), make_address("255.255.255.255"), mask);

// for every IPv6 address, assign the global peer class
f.add_rule(make_address("::")
        , make_address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
        , mask);
ses.set_peer_class_filter(f);

To make uTP sockets exempt from rate limiting:

peer_class_type_filter flt = ses.get_peer_class_type_filter();
// filter out the global and local peer class for uTP sockets, if these
// classes are set by the IP filter
flt.disallow(peer_class_type_filter::utp_socket, session::global_peer_class_id);
flt.disallow(peer_class_type_filter::utp_socket, session::local_peer_class_id);

// this filter should not add the global or local peer class to utp sockets
flt.remove(peer_class_type_filter::utp_socket, session::global_peer_class_id);
flt.remove(peer_class_type_filter::utp_socket, session::local_peer_class_id);

ses.set_peer_class_type_filter(flt);

To make all peers on the internal network unthrottled:

std::uint32_t const mask = 1 << lt::session::global_peer_class_id;
ip_filter f;

// for every IPv4 address, assign the global peer class
f.add_rule(make_address("0.0.0.0"), make_address("255.255.255.255"), mask);

// for every address on the local metwork, set the mask to 0
f.add_rule(make_address("10.0.0.0"), make_address("10.255.255.255"), 0);
ses.set_peer_class_filter(f);

SSL torrents

Torrents may have an SSL root (CA) certificate embedded in them. Such torrents are called SSL torrents. An SSL torrent talks to all bittorrent peers over SSL. The protocols are layered like this:

utp_stack.png

During the SSL handshake, both peers need to authenticate by providing a certificate that is signed by the CA certificate found in the .torrent file. These peer certificates are expected to be privided to peers through some other means than bittorrent. Typically by a peer generating a certificate request which is sent to the publisher of the torrent, and the publisher returning a signed certificate.

In libtorrent, set_ssl_certificate() in torrent_handle is used to tell libtorrent where to find the peer certificate and the private key for it. When an SSL torrent is loaded, the torrent_need_cert_alert is posted to remind the user to provide a certificate.

A peer connecting to an SSL torrent MUST provide the SNI TLS extension (server name indication). The server name is the hex encoded info-hash of the torrent to connect to. This is required for the client accepting the connection to know which certificate to present.

SSL connections are accepted on a separate socket from normal bittorrent connections. To pick which port the SSL socket should bind to, set settings_pack::ssl_listen to a different port. It defaults to port 4433. This setting is only taken into account when the normal listen socket is opened (i.e. just changing this setting won't necessarily close and re-open the SSL socket). To not listen on an SSL socket at all, set ssl_listen to 0.

This feature is only available if libtorrent is build with openssl support (TORRENT_USE_OPENSSL) and requires at least openSSL version 1.0, since it needs SNI support.

Peer certificates must have at least one SubjectAltName field of type dNSName. At least one of the fields must exactly match the name of the torrent. This is a byte-by-byte comparison, the UTF-8 encoding must be identical (i.e. there's no unicode normalization going on). This is the recommended way of verifying certificates for HTTPS servers according to RFC 2818. Note the difference that for torrents only dNSName fields are taken into account (not IP address fields). The most specific (i.e. last) Common Name field is also taken into account if no SubjectAltName did not match.

If any of these fields contain a single asterisk ("*"), the certificate is considered covering any torrent, allowing it to be reused for any torrent.

The purpose of matching the torrent name with the fields in the peer certificate is to allow a publisher to have a single root certificate for all torrents it distributes, and issue separate peer certificates for each torrent. A peer receiving a certificate will not necessarily be able to access all torrents published by this root certificate (only if it has a "star cert").

testing

To test incoming SSL connections to an SSL torrent, one can use the following openssl command:

openssl s_client -cert <peer-certificate>.pem -key <peer-private-key>.pem -CAfile \
   <torrent-cert>.pem -debug -connect 127.0.0.1:4433 -tls1 -servername <info-hash>

To create a root certificate, the Distinguished Name (DN) is not taken into account by bittorrent peers. You still need to specify something, but from libtorrent's point of view, it doesn't matter what it is. libtorrent only makes sure the peer certificates are signed by the correct root certificate.

One way to create the certificates is to use the CA.sh script that comes with openssl, like thisi (don't forget to enter a common Name for the certificate):

CA.sh -newca
CA.sh -newreq
CA.sh -sign

The torrent certificate is located in ./demoCA/private/demoCA/cacert.pem, this is the pem file to include in the .torrent file.

The peer's certificate is located in ./newcert.pem and the certificate's private key in ./newkey.pem.

session statistics

libtorrent provides a mechanism to query performance and statistics counters from its internals. This is primarily useful for troubleshooting of production systems and performance tuning.

The statistics consists of two fundamental types. counters and gauges. A counter is a monotonically increasing value, incremented every time some event occurs. For example, every time the network thread wakes up because a socket became readable will increment a counter. Another example is every time a socket receives n bytes, a counter is incremented by n.

Counters are the most flexible of metrics. It allows the program to sample the counter at any interval, and calculate average rates of increments to the counter. Some events may be rare and need to be sampled over a longer period in order to get userful rates, where other events may be more frequent and evenly distributed that sampling it frequently yields useful values. Counters also provides accurate overall counts. For example, converting samples of a download rate into a total transfer count is not accurate and takes more samples. Converting an increasing counter into a rate is easy and flexible.

Gauges measure the instantaneous state of some kind. This is used for metrics that are not counting events or flows, but states that can fluctuate. For example, the number of torrents that are currenly being downloaded.

It's important to know whether a value is a counter or a gauge in order to interpret it correctly. In order to query libtorrent for which counters and gauges are available, call session_stats_metrics(). This will return metadata about the values available for inspection in libtorrent. It will include whether a value is a counter or a gauge. The key information it includes is the index used to extract the actual measurements for a specific counter or gauge.

In order to take a sample, call post_session_stats() in the session object. This will result in a session_stats_alert being posted. In this alert object, there is an array of values, these values make up the sample. The value index in the stats metric indicates which index the metric's value is stored in.

The mapping between metric and value is not stable across versions of libtorrent. Always query the metrics first, to find out the index at which the value is stored, before interpreting the values array in the session_stats_alert. The mapping will not change during the runtime of your process though, it's tied to a specific libtorrent version. You only have to query the mapping once on startup (or every time libtorrent.so is loaded, if it's done dynamically).

The available stats metrics are:

name type
peer.error_peers counter
peer.disconnected_peers counter

error_peers is the total number of peer disconnects caused by an error (not initiated by this client) and disconnected initiated by this client (disconnected_peers).

name type
peer.eof_peers counter
peer.connreset_peers counter
peer.connrefused_peers counter
peer.connaborted_peers counter
peer.notconnected_peers counter
peer.perm_peers counter
peer.buffer_peers counter
peer.unreachable_peers counter
peer.broken_pipe_peers counter
peer.addrinuse_peers counter
peer.no_access_peers counter
peer.invalid_arg_peers counter
peer.aborted_peers counter

these counters break down the peer errors into more specific categories. These errors are what the underlying transport reported (i.e. TCP or uTP)

name type
peer.piece_requests counter
peer.max_piece_requests counter
peer.invalid_piece_requests counter
peer.choked_piece_requests counter
peer.cancelled_piece_requests counter
peer.piece_rejects counter

the total number of incoming piece requests we've received followed by the number of rejected piece requests for various reasons. max_piece_requests mean we already had too many outstanding requests from this peer, so we rejected it. cancelled_piece_requests are ones where the other end explicitly asked for the piece to be rejected.

name type
peer.error_incoming_peers counter
peer.error_outgoing_peers counter

these counters break down the peer errors into whether they happen on incoming or outgoing peers.

name type
peer.error_rc4_peers counter
peer.error_encrypted_peers counter

these counters break down the peer errors into whether they happen on encrypted peers (just encrypted handshake) and rc4 peers (full stream encryption). These can indicate whether encrypted peers are more or less likely to fail

name type
peer.error_tcp_peers counter
peer.error_utp_peers counter

these counters break down the peer errors into whether they happen on uTP peers or TCP peers. these may indicate whether one protocol is more error prone

name type
peer.connect_timeouts counter
peer.uninteresting_peers counter
peer.timeout_peers counter
peer.no_memory_peers counter
peer.too_many_peers counter
peer.transport_timeout_peers counter
peer.num_banned_peers counter
peer.banned_for_hash_failure counter
peer.connection_attempts counter
peer.connection_attempt_loops counter
peer.incoming_connections counter

these counters break down the reasons to disconnect peers.

name type
peer.num_tcp_peers gauge
peer.num_socks5_peers gauge
peer.num_http_proxy_peers gauge
peer.num_utp_peers gauge
peer.num_i2p_peers gauge
peer.num_ssl_peers gauge
peer.num_ssl_socks5_peers gauge
peer.num_ssl_http_proxy_peers gauge
peer.num_ssl_utp_peers gauge
peer.num_peers_half_open gauge
peer.num_peers_connected gauge
peer.num_peers_up_interested gauge
peer.num_peers_down_interested gauge
peer.num_peers_up_unchoked_all gauge
peer.num_peers_up_unchoked_optimistic gauge
peer.num_peers_up_unchoked gauge
peer.num_peers_down_unchoked gauge
peer.num_peers_up_requests gauge
peer.num_peers_down_requests gauge
peer.num_peers_end_game gauge
peer.num_peers_up_disk gauge
peer.num_peers_down_disk gauge

the number of peer connections for each kind of socket. these counts include half-open (connecting) peers. num_peers_up_unchoked_all is the total number of unchoked peers, whereas num_peers_up_unchoked only are unchoked peers that count against the limit (i.e. excluding peers that are unchoked because the limit doesn't apply to them). num_peers_up_unchoked_optimistic is the number of optimistically unchoked peers.

name type
net.on_read_counter counter
net.on_write_counter counter
net.on_tick_counter counter
net.on_lsd_counter counter
net.on_lsd_peer_counter counter
net.on_udp_counter counter
net.on_accept_counter counter
net.on_disk_queue_counter counter
net.on_disk_counter counter

These counters count the number of times the network thread wakes up for each respective reason. If these counters are very large, it may indicate a performance issue, causing the network thread to wake up too ofte, wasting CPU. mitigate it by increasing buffers and limits for the specific trigger that wakes up the thread.

name type
net.sent_payload_bytes counter
net.sent_bytes counter
net.sent_ip_overhead_bytes counter
net.sent_tracker_bytes counter
net.recv_payload_bytes counter
net.recv_bytes counter
net.recv_ip_overhead_bytes counter
net.recv_tracker_bytes counter

total number of bytes sent and received by the session

name type
net.limiter_up_queue gauge
net.limiter_down_queue gauge

the number of sockets currently waiting for upload and download bandwidht from the rate limiter.

name type
net.limiter_up_bytes gauge
net.limiter_down_bytes gauge

the number of upload and download bytes waiting to be handed out from the rate limiter.

name type
net.recv_failed_bytes counter

the number of bytes downloaded that had to be discarded because they failed the hash check

name type
net.recv_redundant_bytes counter

the number of downloaded bytes that were discarded because they were downloaded multiple times (from different peers)

name type
net.has_incoming_connections gauge

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.

name type
ses.num_checking_torrents gauge
ses.num_stopped_torrents gauge
ses.num_upload_only_torrents gauge
ses.num_downloading_torrents gauge
ses.num_seeding_torrents gauge
ses.num_queued_seeding_torrents gauge
ses.num_queued_download_torrents gauge
ses.num_error_torrents gauge

these gauges count the number of torrents in different states. Each torrent only belongs to one of these states. For torrents that could belong to multiple of these, the most prominent in picked. For instance, a torrent with an error counts as an error-torrent, regardless of its other state.

name type
ses.non_filter_torrents gauge

the number of torrents that don't have the IP filter applied to them.

name type
ses.num_loaded_torrents gauge
ses.num_pinned_torrents gauge

the number of torrents that are currently loaded

name type
ses.num_piece_passed counter
ses.num_piece_failed counter
ses.num_have_pieces counter
ses.num_total_pieces_added counter

these count the number of times a piece has passed the hash check, the number of times a piece was successfully written to disk and the number of total possible pieces added by adding torrents. e.g. when adding a torrent with 1000 piece, num_total_pieces_added is incremented by 1000.

name type
ses.torrent_evicted_counter counter

this counts the number of times a torrent has been evicted (only applies when dynamic loading of torrent files is enabled).

name type
ses.num_unchoke_slots gauge

the number of allowed unchoked peers

name type
ses.num_incoming_choke counter
ses.num_incoming_unchoke counter
ses.num_incoming_interested counter
ses.num_incoming_not_interested counter
ses.num_incoming_have counter
ses.num_incoming_bitfield counter
ses.num_incoming_request counter
ses.num_incoming_piece counter
ses.num_incoming_cancel counter
ses.num_incoming_dht_port counter
ses.num_incoming_suggest counter
ses.num_incoming_have_all counter
ses.num_incoming_have_none counter
ses.num_incoming_reject counter
ses.num_incoming_allowed_fast counter
ses.num_incoming_ext_handshake counter
ses.num_incoming_pex counter
ses.num_incoming_metadata counter
ses.num_incoming_extended counter
ses.num_outgoing_choke counter
ses.num_outgoing_unchoke counter
ses.num_outgoing_interested counter
ses.num_outgoing_not_interested counter
ses.num_outgoing_have counter
ses.num_outgoing_bitfield counter
ses.num_outgoing_request counter
ses.num_outgoing_piece counter
ses.num_outgoing_cancel counter
ses.num_outgoing_dht_port counter
ses.num_outgoing_suggest counter
ses.num_outgoing_have_all counter
ses.num_outgoing_have_none counter
ses.num_outgoing_reject counter
ses.num_outgoing_allowed_fast counter
ses.num_outgoing_ext_handshake counter
ses.num_outgoing_pex counter
ses.num_outgoing_metadata counter
ses.num_outgoing_extended counter

bittorrent message counters. These counters are incremented every time a message of the corresponding type is received from or sent to a bittorrent peer.

name type
ses.waste_piece_timed_out counter
ses.waste_piece_cancelled counter
ses.waste_piece_unknown counter
ses.waste_piece_seed counter
ses.waste_piece_end_game counter
ses.waste_piece_closing counter

the number of wasted downloaded bytes by reason of the bytes being wasted.

name type
picker.piece_picker_partial_loops counter
picker.piece_picker_suggest_loops counter
picker.piece_picker_sequential_loops counter
picker.piece_picker_reverse_rare_loops counter
picker.piece_picker_rare_loops counter
picker.piece_picker_rand_start_loops counter
picker.piece_picker_rand_loops counter
picker.piece_picker_busy_loops counter

the number of pieces considered while picking pieces

name type
picker.reject_piece_picks counter
picker.unchoke_piece_picks counter
picker.incoming_redundant_piece_picks counter
picker.incoming_piece_picks counter
picker.end_game_piece_picks counter
picker.snubbed_piece_picks counter
picker.interesting_piece_picks counter
picker.hash_fail_piece_picks counter

This breaks down the piece picks into the event that triggered it

name type
disk.write_cache_blocks gauge
disk.read_cache_blocks gauge

These gauges indicate how many blocks are currently in use as dirty disk blocks (write_cache_blocks) and read cache blocks, respectively. deprecates cache_status::read_cache_size. The sum of these gauges deprecates cache_status::cache_size.

name type
disk.request_latency gauge

the number of microseconds it takes from receiving a request from a peer until we're sending the response back on the socket.

name type
disk.pinned_blocks gauge
disk.disk_blocks_in_use gauge

disk_blocks_in_use indicates how many disk blocks are currently in use, either as dirty blocks waiting to be written or blocks kept around in the hope that a peer will request it or in a peer send buffer. This gauge deprecates cache_status::total_used_buffers.

name type
disk.queued_disk_jobs gauge
disk.num_running_disk_jobs gauge
disk.num_read_jobs gauge
disk.num_write_jobs gauge
disk.num_jobs gauge
disk.blocked_disk_jobs gauge
disk.num_writing_threads gauge
disk.num_running_threads gauge

queued_disk_jobs is the number of disk jobs currently queued, waiting to be executed by a disk thread. Deprecates cache_status::job_queue_length.

name type
disk.queued_write_bytes gauge
disk.arc_mru_size gauge
disk.arc_mru_ghost_size gauge
disk.arc_mfu_size gauge
disk.arc_mfu_ghost_size gauge
disk.arc_write_size gauge
disk.arc_volatile_size gauge

the number of bytes we have sent to the disk I/O thread for writing. Every time we hear back from the disk I/O thread with a completed write job, this is updated to the number of bytes the disk I/O thread is actually waiting for to be written (as opposed to bytes just hanging out in the cache)

name type
disk.num_blocks_written counter
disk.num_blocks_read counter

the number of blocks written and read from disk in total. A block is 16 kiB. num_blocks_written and num_blocks_read deprecates cache_status::blocks_written and cache_status::blocks_read respectively.

name type
disk.num_blocks_hashed counter

the total number of blocks run through SHA-1 hashing

name type
disk.num_blocks_cache_hits counter

the number of blocks read from the disk cache Deprecates cache_info::blocks_read_hit.

name type
disk.num_write_ops counter
disk.num_read_ops counter

the number of disk I/O operation for reads and writes. One disk operation may transfer more then one block. These counters deprecates cache_status::writes and cache_status::reads.

name type
disk.num_read_back counter

the number of blocks that had to be read back from disk in order to hash a piece (when verifying against the piece hash)

name type
disk.disk_read_time counter
disk.disk_write_time counter
disk.disk_hash_time counter
disk.disk_job_time counter

cumulative time spent in various disk jobs, as well as total for all disk jobs. Measured in microseconds

name type
disk.num_fenced_read gauge
disk.num_fenced_write gauge
disk.num_fenced_hash gauge
disk.num_fenced_move_storage gauge
disk.num_fenced_release_files gauge
disk.num_fenced_delete_files gauge
disk.num_fenced_check_fastresume gauge
disk.num_fenced_save_resume_data gauge
disk.num_fenced_rename_file gauge
disk.num_fenced_stop_torrent gauge
disk.num_fenced_cache_piece gauge
disk.num_fenced_flush_piece gauge
disk.num_fenced_flush_hashed gauge
disk.num_fenced_flush_storage gauge
disk.num_fenced_trim_cache gauge
disk.num_fenced_file_priority gauge
disk.num_fenced_load_torrent gauge
disk.num_fenced_clear_piece gauge
disk.num_fenced_tick_storage gauge

for each kind of disk job, a counter of how many jobs of that kind are currently blocked by a disk fence

name type
dht.dht_nodes gauge

The number of nodes in the DHT routing table

name type
dht.dht_node_cache gauge

The number of replacement nodes in the DHT routing table

name type
dht.dht_torrents gauge

the number of torrents currently tracked by our DHT node

name type
dht.dht_peers gauge

the number of peers currently tracked by our DHT node

name type
dht.dht_immutable_data gauge

the number of immutable data items tracked by our DHT node

name type
dht.dht_mutable_data gauge

the number of mutable data items tracked by our DHT node

name type
dht.dht_allocated_observers gauge

the number of RPC observers currently allocated

name type
dht.dht_messages_in counter
dht.dht_messages_out counter

the total number of DHT messages sent and received

name type
dht.dht_messages_out_dropped counter

the number of outgoing messages that failed to be sent

name type
dht.dht_bytes_in counter
dht.dht_bytes_out counter

the total number of bytes sent and received by the DHT

name type
dht.dht_ping_in counter
dht.dht_ping_out counter
dht.dht_find_node_in counter
dht.dht_find_node_out counter
dht.dht_get_peers_in counter
dht.dht_get_peers_out counter
dht.dht_announce_peer_in counter
dht.dht_announce_peer_out counter
dht.dht_get_in counter
dht.dht_get_out counter
dht.dht_put_in counter
dht.dht_put_out counter

the number of DHT messages we've sent and received by kind.

name type
dht.dht_invalid_announce counter
dht.dht_invalid_get_peers counter
dht.dht_invalid_put counter
dht.dht_invalid_get counter

the number of failed incoming DHT requests by kind of request

name type
utp.utp_packet_loss counter
utp.utp_timeout counter
utp.utp_packets_in counter
utp.utp_packets_out counter
utp.utp_fast_retransmit counter
utp.utp_packet_resend counter
utp.utp_samples_above_target counter
utp.utp_samples_below_target counter
utp.utp_payload_pkts_in counter
utp.utp_payload_pkts_out counter
utp.utp_invalid_pkts_in counter
utp.utp_redundant_pkts_in counter

uTP counters. Each counter represents the number of time each event has occurred.

name type
utp.num_utp_idle gauge
utp.num_utp_syn_sent gauge
utp.num_utp_connected gauge
utp.num_utp_fin_sent gauge
utp.num_utp_close_wait gauge
utp.num_utp_deleted gauge

the number of uTP sockets in each respective state

name type
sock_bufs.socket_send_size3 counter
sock_bufs.socket_send_size4 counter
sock_bufs.socket_send_size5 counter
sock_bufs.socket_send_size6 counter
sock_bufs.socket_send_size7 counter
sock_bufs.socket_send_size8 counter
sock_bufs.socket_send_size9 counter
sock_bufs.socket_send_size10 counter
sock_bufs.socket_send_size11 counter
sock_bufs.socket_send_size12 counter
sock_bufs.socket_send_size13 counter
sock_bufs.socket_send_size14 counter
sock_bufs.socket_send_size15 counter
sock_bufs.socket_send_size16 counter
sock_bufs.socket_send_size17 counter
sock_bufs.socket_send_size18 counter
sock_bufs.socket_send_size19 counter
sock_bufs.socket_send_size20 counter
sock_bufs.socket_recv_size3 counter
sock_bufs.socket_recv_size4 counter
sock_bufs.socket_recv_size5 counter
sock_bufs.socket_recv_size6 counter
sock_bufs.socket_recv_size7 counter
sock_bufs.socket_recv_size8 counter
sock_bufs.socket_recv_size9 counter
sock_bufs.socket_recv_size10 counter
sock_bufs.socket_recv_size11 counter
sock_bufs.socket_recv_size12 counter
sock_bufs.socket_recv_size13 counter
sock_bufs.socket_recv_size14 counter
sock_bufs.socket_recv_size15 counter
sock_bufs.socket_recv_size16 counter
sock_bufs.socket_recv_size17 counter
sock_bufs.socket_recv_size18 counter
sock_bufs.socket_recv_size19 counter
sock_bufs.socket_recv_size20 counter

the buffer sizes accepted by socket send and receive calls respectively. The larger the buffers are, the more efficient, because it reqire fewer system calls per byte. The size is 1 << n, where n is the number at the end of the counter name. i.e. 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576 bytes

libtorrent-rasterbar-1.1.13/docs/manual-ref.rst000066400000000000000000001575161351156116000214730ustar00rootroot00000000000000============================ libtorrent API Documentation ============================ :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 1 :backlinks: none overview ======== The interface of libtorrent consists of a few classes. The main class is the ``session``, it contains the main loop that serves all torrents. The basic usage is as follows: * construct a `session`__ * load `session`__ state from settings file (see `load_state()`__) * start extensions (see `add_extension()`__). * start DHT, LSD, UPnP, NAT-PMP etc (see start_dht(), start_lsd(), start_upnp() and start_natpmp()). * parse .torrent-files and add them to the `session`__ (see `torrent_info`__, `async_add_torrent()`__ and `add_torrent()`__) * main loop (see `session`__) * poll for alerts (see `wait_for_alert()`__, `pop_alerts()`__) * handle updates to torrents, (see `state_update_alert`__). * handle other alerts, (see `alert`__). * query the `session`__ for information (see session::status()). * add and remove torrents from the `session`__ (`remove_torrent()`__) * save resume data for all torrent_handles (optional, see `save_resume_data()`__) * save `session`__ state (see `save_state()`__) * destruct `session`__ object Each class and function is described in this manual, you may want to have a look at the tutorial_ as well. .. _tutorial: tutorial.html For a description on how to create torrent files, see `create_torrent`__. .. _make_torrent: make_torrent.html forward declarations ==================== Forward declaring types from the libtorrent namespace is discouraged as it may break in future releases. Instead include ``libtorrent/fwd.hpp`` for forward declarations of all public types in libtorrent. trouble shooting ================ A common problem developers are facing is torrents stopping without explanation. Here is a description on which conditions libtorrent will stop your torrents, how to find out about it and what to do about it. Make sure to keep track of the paused state, the error state and the upload mode of your torrents. By default, torrents are auto-managed, which means libtorrent will pause, resume, scrape them and take them out of upload-mode automatically. Whenever a torrent encounters a fatal error, it will be stopped, and the ``torrent_status::error`` will describe the error that caused it. If a torrent is auto managed, it is scraped periodically and paused or resumed based on the number of downloaders per seed. This will effectively seed torrents that are in the greatest need of seeds. If a torrent hits a disk write error, it will be put into upload mode. This means it will not download anything, but only upload. The assumption is that the write error is caused by a full disk or write permission errors. If the torrent is auto-managed, it will periodically be taken out of the upload mode, trying to write things to the disk again. This means torrent will recover from certain disk errors if the problem is resolved. If the torrent is not auto managed, you have to call `set_upload_mode()`__ to turn downloading back on again. For a more detailed guide on how to trouble shoot performance issues, see troubleshooting_ .. _troubleshooting: troubleshooting.html network primitives ================== There are a few typedefs in the ``libtorrent`` namespace which pulls in network types from the ``boost::asio`` namespace. These are:: typedef boost::asio::ip::address address; typedef boost::asio::ip::address_v4 address_v4; typedef boost::asio::ip::address_v6 address_v6; using boost::asio::ip::tcp; using boost::asio::ip::udp; These are declared in the ```` header. The ``using`` statements will give easy access to:: tcp::endpoint udp::endpoint Which are the endpoint types used in libtorrent. An endpoint is an address with an associated port. For documentation on these types, please refer to the `asio documentation`_. .. _`asio documentation`: https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio.html exceptions ========== Many functions in libtorrent have two versions, one that throws exceptions on errors and one that takes an ``error_code`` reference which is filled with the error code on errors. There is one exception class that is used for errors in libtorrent, it is based on boost.system's ``error_code`` class to carry the error code. For more information, see `libtorrent_exception`__ and `error_code_enum`__. translating error codes ----------------------- The error_code::message() function will typically return a localized error string, for system errors. That is, errors that belong to the generic or system category. Errors that belong to the libtorrent error category are not localized however, they are only available in english. In order to translate libtorrent errors, compare the error category of the ``error_code`` object against ``libtorrent::libtorrent_category()``, and if matches, you know the error code refers to the list above. You can provide your own mapping from error code to string, which is localized. In this case, you cannot rely on ``error_code::message()`` to generate your strings. The numeric values of the errors are part of the API and will stay the same, although new error codes may be appended at the end. Here's a simple example of how to translate error codes: .. code:: c++ std::string error_code_to_string(boost::system::error_code const& ec) { if (ec.category() != libtorrent::libtorrent_category()) { return ec.message(); } // the error is a libtorrent error int code = ec.value(); static const char const* swedish[] = { "inget fel", "en fil i torrenten kolliderar med en fil fran en annan torrent", "hash check misslyckades", "torrentfilen ar inte en dictionary", "'info'-nyckeln saknas eller ar korrupt i torrentfilen", "'info'-faltet ar inte en dictionary", "'piece length' faltet saknas eller ar korrupt i torrentfilen", "torrentfilen saknar namnfaltet", "ogiltigt namn i torrentfilen (kan vara en attack)", // ... more strings here }; // use the default error string in case we don't have it // in our translated list if (code < 0 || code >= sizeof(swedish)/sizeof(swedish[0])) return ec.message(); return swedish[code]; } magnet links ============ Magnet links are URIs that includes an info-hash, a display name and optionally a tracker url. The idea behind magnet links is that an end user can click on a link in a browser and have it handled by a bittorrent application, to start a download, without any .torrent file. The format of the magnet URI is: **magnet:?xt=urn:btih:** *Base16 encoded info-hash* [ **&dn=** *name of download* ] [ **&tr=** *tracker URL* ]* In order to download *just* the metadata (.torrent file) from a magnet link, set file priorities to 0 in `add_torrent_params::file_priorities`__. It's OK to set the priority for more files than what is in the torrent. It may not be trivial to know how many files a torrent has before the metadata has been downloaded. Additional file priorities will be ignored. By setting a large number of files to priority 0, chances are that they will all be set to 0 once the metadata is received (and we know how many files there are). In this case, when the metadata is received from the swarm, the torrent will still be running, but it will disconnect the majority of peers (since connections to peers that already have the metadata are redundant). It will keep seeding the *metadata* only. queuing ======= libtorrent supports *queuing*. Queuing is a mechanism to automatically pause and resume torrents based on certain criteria. The criteria depends on the overall state the torrent is in (checking, downloading or seeding). To opt-out of the queuing logic, make sure your torrents are added with the `add_torrent_params::flag_auto_managed`__ bit *cleared*. Or call ``torrent_handle::auto_managed(false)`` on the torrent handle. The overall purpose of the queuing logic is to improve performance under arbitrary torrent downloading and seeding load. For example, if you want to download 100 torrents on a limited home connection, you improve performance by downloading them one at a time (or maybe two at a time), over downloading them all in parallel. The benefits are: * the average completion time of a torrent is half of what it would be if all downloaded in parallel. * The amount of upload capacity is more likely to reach the *reciprocation rate* of your peers, and is likely to improve your *return on investment* (download to upload ratio) * your disk I/O load is likely to be more local which may improve I/O performance and decrease fragmentation. There are fundamentally 3 seaparate queues: * checking torrents * downloading torrents * seeding torrents Every torrent that is not seeding has a queue number associated with it, this is its place in line to be started. See `torrent_status::queue_position`__. On top of the limits of each queue, there is an over arching limit, set in `settings_pack::active_limit`__. The auto manager will never start more than this number of torrents (with one exception described below). Non-auto-managed torrents are exempt from this logic, and not counted. At a regular interval, torrents are checked if there needs to be any re-ordering of which torrents are active and which are queued. This interval can be controlled via `settings_pack::auto_manage_interval`__. For queuing to work, resume data needs to be saved and restored for all torrents. See `torrent_handle::save_resume_data()`__. queue position -------------- The torrents in the front of the queue are started and the rest are ordered by their queue position. Any newly added torrent is placed at the end of the queue. Once a torrent is removed or turns into a seed, its queue position is -1 and all torrents that used to be after it in the queue, decreases their position in order to fill the gap. The queue positions are always contiguous, in a sequence without any gaps. Lower queue position means closer to the front of the queue, and will be started sooner than torrents with higher queue positions. To query a torrent for its position in the queue, or change its position, see: `torrent_handle::queue_position()`__, `torrent_handle::queue_position_up()`__, `torrent_handle::queue_position_down()`__, `torrent_handle::queue_position_top()`__ and `torrent_handle::queue_position_bottom()`__. checking queue -------------- The checking queue affects torrents in the torrent_status::checking or `torrent_status::allocating`__ state that are auto-managed. The checking queue will make sure that (of the torrents in its queue) no more than settings_pack::active_checking_limit torrents are started at any given time. Once a torrent completes checking and moves into a diffferent state, the next in line will be started for checking. Any torrent added force-started or force-stopped (i.e. the auto managed flag is *not* set), will not be subject to this limit and they will all check independently and in parallel. Once a torrent completes the checking of its files, or fastresume data, it will be put in the queue for downloading and potentially start downloading immediately. In order to add a torrent and check its files without starting the download, it can be added in ``stop_when_ready`` mode. See `add_torrent_params::flag_stop_when_ready`__. This flag will stop the torrent once it is ready to start downloading. This is conceptually the same as waiting for the ``torrent_checked_alert`` and then call:: h.auto_managed(false); h.pause(); With the important distinction that it entirely avoids the brief window where the torrent is in downloading state. downloading queue ----------------- Similarly to the checking queue, the downloading queue will make sure that no more than `settings_pack::active_downloads`__ torrents are in the downloading state at any given time. The `torrent_status::queue_position`__ is used again here to determine who is next in line to be started once a downloading torrent completes or is stopped/removed. seeding queue ------------- The seeding queue does not use `torrent_status::queue_position`__ to determine which torrent to seed. Instead, it estimates the *demand* for the torrent to be seeded. A torrent with few other seeds and many downloaders is assumed to have a higher demand of more seeds than one with many seeds and few downloaders. It limits the number of started seeds to `settings_pack::active_seeds`__. On top of this basic bias, *seed priority* can be controller by specifying a seed ratio (the upload to download ratio), a seed-time ratio (the download time to seeding time ratio) and a seed-time (the absolute time to be seeding a torrent). Until all those targets are hit, the torrent will be prioritized for seeding. Among torrents that have met their seed target, torrents where we don't know of any other seed take strict priority. In order to avoid flapping, torrents that were started less than 30 minutes ago also have priority to keep seeding. Finally, for torrents where none of the above apply, they are prioritized based on the download to seed ratio. The relevant settings to control these limits are `settings_pack::share_ratio_limit`__, `settings_pack::seed_time_ratio_limit`__ and `settings_pack::seed_time_limit`__. queuing options --------------- In addition to simply starting and stopping torrents, the queuing mechanism can have more fine grained control of the resources used by torrents. half-started torrents ..................... In addition to the downloading and seeding limits, there are limits on *actions* torrents perform. The downloading and seeding limits control whether peers are allowed at all, and if peers are not allowed, torrents are stopped and don't do anything. If peers are allowed, torrents may: 1. announce to trackers 2. announce to the DHT 3. announce to local peer discovery (local service discovery) Each of those actions are associated with a cost and hence may need a separate limit. These limits are controlled by `settings_pack::active_tracker_limit`__, `settings_pack::active_dht_limit`__ and `settings_pack::active_lsd_limit`__ respectively. Specifically, announcing to a tracker is typically cheaper than announcing to the DHT. ``active_dht_limit`` will limit the number of torrents that are allowed to announce to the DHT. The highest priority ones will, and the lower priority ones won't. The will still be considered started though, and any incoming peers will still be accepted. If you do not wish to impose such limits (basically, if you do not wish to have half-started torrents) make sure to set these limits to -1 (infinite). prefer seeds ............ In the case where ``active_downloads`` + ``active_seeds`` > ``active_limit``, there's an ambiguity whether the downloads should be satisfied first or the seeds. To disambiguate this case, the `settings_pack::auto_manage_prefer_seeds`__ determines whether seeds are preferred or not. inactive torrents ................. Torrents that are not transferring any bytes (downloading or uploading) have a relatively low cost to be started. It's possible to exempt such torrents from the download and seed queues by setting `settings_pack::dont_count_slow_torrents`__ to true. Since it sometimes may take a few minutes for a newly started torrent to find peers and be unchoked, or find peers that are interested in requesting data, torrents are not considered inactive immadiately. There must be an extended period of no transfers before it is considered inactive and exempt from the queuing limits. fast resume =========== The fast resume mechanism is a way to remember which pieces are downloaded and where they are put between sessions. You can generate fast resume data by calling `save_resume_data()`__ on `torrent_handle`__. You can then save this data to disk and use it when resuming the torrent. libtorrent will not check the piece hashes then, and rely on the information given in the fast-resume data. The fast-resume data also contains information about which blocks, in the unfinished pieces, were downloaded, so it will not have to start from scratch on the partially downloaded pieces. To use the fast-resume data you simply give it to `async_add_torrent()`__ and `add_torrent()`__, and it will skip the time consuming checks. It may have to do the checking anyway, if the fast-resume data is corrupt or doesn't fit the storage for that torrent, then it will not trust the fast-resume data and just do the checking. file format ----------- The file format is a bencoded dictionary containing the following fields: +--------------------------+--------------------------------------------------------------+ | ``file-format`` | string: "libtorrent resume file" | | | | +--------------------------+--------------------------------------------------------------+ | ``file-version`` | integer: 1 | | | | +--------------------------+--------------------------------------------------------------+ | ``info-hash`` | string, the info hash of the torrent this data is saved for. | | | | +--------------------------+--------------------------------------------------------------+ | ``blocks per piece`` | integer, the number of blocks per piece. Must be: piece_size | | | / (16 * 1024). Clamped to be within the range [1, 256]. It | | | is the number of blocks per (normal sized) piece. Usually | | | each block is 16 * 1024 bytes in size. But if piece size is | | | greater than 4 megabytes, the block size will increase. | | | | +--------------------------+--------------------------------------------------------------+ | ``pieces`` | A string with piece flags, one character per piece. | | | Bit 1 means we have that piece. | | | Bit 2 means we have verified that this piece is correct. | | | This only applies when the torrent is in seed_mode. | +--------------------------+--------------------------------------------------------------+ | ``slots`` | list of integers. The list maps slots to piece indices. It | | | tells which piece is on which slot. If piece index is -2 it | | | means it is free, that there's no piece there. If it is -1, | | | means the slot isn't allocated on disk yet. The pieces have | | | to meet the following requirement: | +--------------------------+--------------------------------------------------------------+ | ``total_uploaded`` | integer. The number of bytes that have been uploaded in | | | total for this torrent. | +--------------------------+--------------------------------------------------------------+ | ``total_downloaded`` | integer. The number of bytes that have been downloaded in | | | total for this torrent. | +--------------------------+--------------------------------------------------------------+ | ``active_time`` | integer. The number of seconds this torrent has been active. | | | i.e. not paused. | +--------------------------+--------------------------------------------------------------+ | ``seeding_time`` | integer. The number of seconds this torrent has been active | | | and seeding. | +--------------------------+--------------------------------------------------------------+ | ``num_seeds`` | integer. An estimate of the number of seeds on this torrent | | | when the resume data was saved. This is scrape data or based | | | on the peer list if scrape data is unavailable. | +--------------------------+--------------------------------------------------------------+ | ``num_downloaders`` | integer. An estimate of the number of downloaders on this | | | torrent when the resume data was last saved. This is used as | | | an initial estimate until we acquire up-to-date scrape info. | +--------------------------+--------------------------------------------------------------+ | ``last_upload`` | integer. The number of seconds since epoch when we last | | | uploaded payload to a peer on this torrent. | +--------------------------+--------------------------------------------------------------+ | ``last_download`` | integer. The number of seconds since epoch when we last | | | downloaded payload from a peer on this torrent. | +--------------------------+--------------------------------------------------------------+ | ``last_scrape`` | integer. The number of seconds since epoch when we last sent | | | a scrape request to a tracker on this torrent. | +--------------------------+--------------------------------------------------------------+ | ``upload_rate_limit`` | integer. In case this torrent has a per-torrent upload rate | | | limit, this is that limit. In bytes per second. | +--------------------------+--------------------------------------------------------------+ | ``download_rate_limit`` | integer. The download rate limit for this torrent in case | | | one is set, in bytes per second. | +--------------------------+--------------------------------------------------------------+ | ``max_connections`` | integer. The max number of peer connections this torrent | | | may have, if a limit is set. | +--------------------------+--------------------------------------------------------------+ | ``max_uploads`` | integer. The max number of unchoked peers this torrent may | | | have, if a limit is set. | +--------------------------+--------------------------------------------------------------+ | ``seed_mode`` | integer. 1 if the torrent is in seed mode, 0 otherwise. | +--------------------------+--------------------------------------------------------------+ | ``file_priority`` | list of integers. One entry per file in the torrent. Each | | | entry is the priority of the file with the same index. | +--------------------------+--------------------------------------------------------------+ | ``piece_priority`` | string of bytes. Each byte is interpreted as an integer and | | | is the priority of that piece. | +--------------------------+--------------------------------------------------------------+ | ``auto_managed`` | integer. 1 if the torrent is auto managed, otherwise 0. | +--------------------------+--------------------------------------------------------------+ | ``sequential_download`` | integer. 1 if the torrent is in sequential download mode, | | | 0 otherwise. | +--------------------------+--------------------------------------------------------------+ | ``paused`` | integer. 1 if the torrent is paused, 0 otherwise. | +--------------------------+--------------------------------------------------------------+ | ``trackers`` | list of lists of strings. The top level list lists all | | | tracker tiers. Each second level list is one tier of | | | trackers. | +--------------------------+--------------------------------------------------------------+ | ``mapped_files`` | list of strings. If any file in the torrent has been | | | renamed, this entry contains a list of all the filenames. | | | In the same order as in the torrent file. | +--------------------------+--------------------------------------------------------------+ | ``url-list`` | list of strings. List of url-seed URLs used by this torrent. | | | The urls are expected to be properly encoded and not contain | | | any illegal url characters. | +--------------------------+--------------------------------------------------------------+ | ``httpseeds`` | list of strings. List of httpseed URLs used by this torrent. | | | The urls are expected to be properly encoded and not contain | | | any illegal url characters. | +--------------------------+--------------------------------------------------------------+ | ``merkle tree`` | string. In case this torrent is a merkle torrent, this is a | | | string containing the entire merkle tree, all nodes, | | | including the root and all leaves. The tree is not | | | necessarily complete, but complete enough to be able to send | | | any piece that we have, indicated by the have bitmask. | +--------------------------+--------------------------------------------------------------+ | ``save_path`` | string. The save path where this torrent was saved. This is | | | especially useful when moving torrents with move_storage() | | | since this will be updated. | +--------------------------+--------------------------------------------------------------+ | ``peers`` | string. This string contains IPv4 and port pairs of peers we | | | were connected to last session. The endpoints are in compact | | | representation. 4 bytes IPv4 address followed by 2 bytes | | | port. Hence, the length of this string should be divisible | | | by 6. | +--------------------------+--------------------------------------------------------------+ | ``banned_peers`` | string. This string has the same format as ``peers`` but | | | instead represent IPv4 peers that we have banned. | +--------------------------+--------------------------------------------------------------+ | ``peers6`` | string. This string contains IPv6 and port pairs of peers we | | | were connected to last session. The endpoints are in compact | | | representation. 16 bytes IPv6 address followed by 2 bytes | | | port. The length of this string should be divisible by 18. | +--------------------------+--------------------------------------------------------------+ | ``banned_peers6`` | string. This string has the same format as ``peers6`` but | | | instead represent IPv6 peers that we have banned. | +--------------------------+--------------------------------------------------------------+ | ``info`` | If this field is present, it should be the info-dictionary | | | of the torrent this resume data is for. Its SHA-1 hash must | | | match the one in the ``info-hash`` field. When present, | | | the torrent is loaded from here, meaning the torrent can be | | | added purely from resume data (no need to load the .torrent | | | file separately). This may have performance advantages. | +--------------------------+--------------------------------------------------------------+ | ``unfinished`` | list of dictionaries. Each dictionary represents an | | | piece, and has the following layout: | | | | | | +-------------+--------------------------------------------+ | | | | ``piece`` | integer, the index of the piece this entry | | | | | | refers to. | | | | +-------------+--------------------------------------------+ | | | | ``bitmask`` | string, a binary bitmask representing the | | | | | | blocks that have been downloaded in this | | | | | | piece. | | | | +-------------+--------------------------------------------+ | | | | ``adler32`` | The adler32 checksum of the data in the | | | | | | blocks specified by ``bitmask``. | | | | | | | | | | +-------------+--------------------------------------------+ | | | | +--------------------------+--------------------------------------------------------------+ | ``file sizes`` | list where each entry corresponds to a file in the file list | | | in the metadata. Each entry has a list of two values, the | | | first value is the size of the file in bytes, the second | | | is the time stamp when the last time someone wrote to it. | | | This information is used to compare with the files on disk. | | | All the files must match exactly this information in order | | | to consider the resume data as current. Otherwise a full | | | re-check is issued. | +--------------------------+--------------------------------------------------------------+ | ``allocation`` | The allocation mode for the storage. Can be either ``full`` | | | or ``sparse``. If this is full, the file sizes and | | | timestamps are disregarded. Pieces are assumed not to have | | | moved around even if the files have been modified after the | | | last resume data checkpoint. | +--------------------------+--------------------------------------------------------------+ storage allocation ================== There are two modes in which storage (files on disk) are allocated in libtorrent. 1. The traditional *full allocation* mode, where the entire files are filled up with zeros before anything is downloaded. Files are allocated on demand, the first time anything is written to them. The main benefit of this mode is that it avoids creating heavily fragmented files. 2. The *sparse allocation*, sparse files are used, and pieces are downloaded directly to where they belong. This is the recommended (and default) mode. sparse allocation ----------------- On filesystems that supports sparse files, this allocation mode will only use as much space as has been downloaded. The main drawback of this mode is that it may create heavily fragmented files. * It does not require an allocation pass on startup. full allocation --------------- When a torrent is started in full allocation mode, the disk-io thread will make sure that the entire storage is allocated, and fill any gaps with zeros. It will of course still check for existing pieces and fast resume data. The main drawbacks of this mode are: * It may take longer to start the torrent, since it will need to fill the files with zeros. This delay is linear to the size of the download. * The download may occupy unnecessary disk space between download sessions. * Disk caches usually perform poorly with random access to large files and may slow down the download some. The benefits of this mode are: * Downloaded pieces are written directly to their final place in the files and the total number of disk operations will be fewer and may also play nicer to filesystems' file allocation, and reduce fragmentation. * No risk of a download failing because of a full disk during download, once all files have been created. HTTP seeding ============ There are two kinds of HTTP seeding. One with that assumes a smart (and polite) client and one that assumes a smart server. These are specified in `BEP 19`_ and `BEP 17`_ respectively. libtorrent supports both. In the libtorrent source code and API, BEP 19 urls are typically referred to as *url seeds* and BEP 17 urls are typically referred to as *HTTP seeds*. The libtorrent implementation of `BEP 19`_ assumes that, if the URL ends with a slash ('/'), the filename should be appended to it in order to request pieces from that file. The way this works is that if the torrent is a single-file torrent, only that filename is appended. If the torrent is a multi-file torrent, the torrent's name '/' the file name is appended. This is the same directory structure that libtorrent will download torrents into. .. _`BEP 17`: https://bittorrent.org/beps/bep_0017.html .. _`BEP 19`: https://bittorrent.org/beps/bep_0019.html dynamic loading of torrent files ================================ .. note:: This feature will be removed in the next major release of libtorrent. As an alternative, torrents can be loaded on demand by plugins. libtorrent has a feature that can unload idle torrents from memory. The purpose of this is to support being active on many more torrents than the RAM permits. This is useful for both embedded devices that have limited RAM and servers seeding tens of thousands of torrents. The most significant parts of loaded torrents that use RAM are the piece hashes (20 bytes per piece) and the file list. The entire info-dictionary of the .torrent file is kept in RAM. In order to activate the dynamic loading of torrent files, set the load function on the `session`__. See `set_load_function()`__. When a load function is set on the `session`__, the dynamic load/unload feature is enabled. Torrents are kept in an LRU. Every time an operation is performed, on a torrent or from a peer, that requires the metadata of the torrent to be loaded, the torrent is bumped up in the LRU. When a torrent is paused or queued, it is demoted to the least recently used torrent in the LRU, since it's a good candidate for eviction. To configure how many torrents are allowed to be loaded at the same time, set `settings_pack::active_loaded_limit`__ on the `session`__. Torrents can be exempt from being unloaded by being *pinned*. Pinned torrents still count against the limit, but are never considered for eviction. You can either pin a torrent when adding it, in ``add_torrent_params`` (see `async_add_torrent()`__ and `add_torrent()`__), or after ading it with the `set_pinned()`__ function on `torrent_handle`__. Torrents that start out without metadata (e.g. magnet links or http downloads) are automatically pinned. This is important in order to give the client a chance to save the metadata to disk once it's received (see `metadata_received_alert`__). Once the metadata is saved to disk, it might make sense to unpin the torrent. piece picker ============ The piece picker in libtorrent has the following features: * rarest first * sequential download * random pick * reverse order picking * parole mode * prioritize partial pieces * prefer whole pieces * piece affinity by speed category * piece priorities internal representation ----------------------- It is optimized by, at all times, keeping a list of pieces ordered by rarity, randomly shuffled within each rarity class. This list is organized as a single vector of contigous memory in RAM, for optimal memory locality and to eliminate heap allocations and frees when updating rarity of pieces. Expensive events, like a peer joining or leaving, are evaluated lazily, since it's cheaper to rebuild the whole list rather than updating every single piece in it. This means as long as no blocks are picked, peers joining and leaving is no more costly than a single peer joining or leaving. Of course the special cases of peers that have all or no pieces are optimized to not require rebuilding the list. picker strategy --------------- The normal mode of the picker is of course *rarest first*, meaning pieces that few peers have are preferred to be downloaded over pieces that more peers have. This is a fundamental algorithm that is the basis of the performance of bittorrent. However, the user may set the piece picker into sequential download mode. This mode simply picks pieces sequentially, always preferring lower piece indices. When a torrent starts out, picking the rarest pieces means increased risk that pieces won't be completed early (since there are only a few peers they can be downloaded from), leading to a delay of having any piece to offer to other peers. This lack of pieces to trade, delays the client from getting started into the normal tit-for-tat mode of bittorrent, and will result in a long ramp-up time. The heuristic to mitigate this problem is to, for the first few pieces, pick random pieces rather than rare pieces. The threshold for when to leave this initial picker mode is determined by `settings_pack::initial_picker_threshold`__. reverse order ------------- An orthogonal setting is *reverse order*, which is used for *snubbed* peers. Snubbed peers are peers that appear very slow, and might have timed out a piece request. The idea behind this is to make all snubbed peers more likely to be able to do download blocks from the same piece, concentrating slow peers on as few pieces as possible. The reverse order means that the most common pieces are picked, instead of the rarest pieces (or in the case of sequential download, the last pieces, intead of the first). parole mode ----------- Peers that have participated in a piece that failed the hash check, may be put in *parole mode*. This means we prefer downloading a full piece from this peer, in order to distinguish which peer is sending corrupt data. Whether to do this is or not is controlled by `settings_pack::use_parole_mode`__. In parole mode, the piece picker prefers picking one whole piece at a time for a given peer, avoiding picking any blocks from a piece any other peer has contributed to (since that would defeat the purpose of parole mode). prioritize partial pieces ------------------------- This setting determines if partially downloaded or requested pieces should always be preferred over other pieces. The benefit of doing this is that the number of partial pieces is minimized (and hence the turn-around time for downloading a block until it can be uploaded to others is minimized). It also puts less stress on the disk cache, since fewer partial pieces need to be kept in the cache. Whether or not to enable this is controlled by setting_pack::prioritize_partial_pieces. The main benefit of not prioritizing partial pieces is that the rarest first algorithm gets to have more influence on which pieces are picked. The picker is more likely to truly pick the rarest piece, and hence improving the performance of the swarm. This setting is turned on automatically whenever the number of partial pieces in the piece picker exceeds the number of peers we're connected to times 1.5. This is in order to keep the waste of partial pieces to a minimum, but still prefer rarest pieces. prefer whole pieces ------------------- The *prefer whole pieces* setting makes the piece picker prefer picking entire pieces at a time. This is used by web connections (both http seeding standards), in order to be able to coalesce the small bittorrent requests to larger HTTP requests. This significantly improves performance when downloading over HTTP. It is also used by peers that are downloading faster than a certain threshold. The main advantage is that these peers will better utilize the other peer's disk cache, by requesting all blocks in a single piece, from the same peer. This threshold is controlled by the `settings_pack::whole_pieces_threshold`__ setting. *TODO: piece priorities* predictive piece announce ========================= In order to improve performance, libtorrent supports a feature called ``predictive piece announce``. When enabled, it will make libtorrent announce that we have pieces to peers, before we truly have them. The most important case is to announce a piece as soon as it has been downloaded and passed the hash check, but not yet been written to disk. In this case, there is a risk the piece will fail to be written to disk, in which case we won't have the piece anymore, even though we announced it to peers. The other case is when we're very close to completing the download of a piece and assume it will pass the hash check, we can announce it to peers to make it available one round-trip sooner than otherwise. This lets libtorrent start uploading the piece to interested peers immediately when the piece complete, instead of waiting one round-trip for the peers to request it. This makes for the implementation slightly more complicated, since piece will have more states and more complicated transitions. For instance, a piece could be: 1. hashed but not fully written to disk 2. fully written to disk but not hashed 3. not fully downloaded 4. downloaded and hash checked Once a piece is fully downloaded, the hash check could complete before any of the write operations or it could complete after all write operations are complete. peer classes ============ The peer classes feature in libtorrent allows a client to define custom groups of peers and rate limit them individually. Each such group is called a *peer class*. There are a few default peer classes that are always created: * global - all peers belong to this class, except peers on the local network * local peers - all peers on the local network belongs to this class TCP peers * tcp class - all peers connected over TCP belong to this class The TCP peers class is used by the uTP/TCP balancing logic, if it's enabled, to throttle TCP peers. The global and local classes are used to adjust the global rate limits. When the rate limits are adjusted for a specific torrent, a class is created implicitly for that torrent. The default peer class IDs are defined as enums in the ``session`` class: .. code:: c++ enum { global_peer_class_id, tcp_peer_class_id, local_peer_class_id }; The default peer classes are automatically created on `session`__ startup, and configured to apply to each respective type of connection. There's nothing preventing a client from reconfiguring the peer class ip- and type filters to disable or customize which peers they apply to. See `set_peer_class_filter()`__ and `set_peer_class_type_filter()`__. A peer class can be considered a more general form of *lables* that some clients have. Peer classes however are not just applied to torrents, but ultimately the peers. Peer classes can be created with the `create_peer_class()`__ call (on the `session`__ object), and deleted with the `delete_peer_class()`__ call. Peer classes are configured with the `set_peer_class()`__ `get_peer_class()`__ calls. Custom peer classes can be assigned based on the peer's IP address or the type of transport protocol used. See `set_peer_class_filter()`__ and `set_peer_class_type_filter()`__ for more information. peer class examples ------------------- Here are a few examples of common peer class operations. To make the global rate limit apply to local peers as well, update the IP-filter based peer class assignment: .. code:: c++ std::uint32_t const mask = 1 << lt::session::global_peer_class_id; ip_filter f; // for every IPv4 address, assign the global peer class f.add_rule(make_address("0.0.0.0"), make_address("255.255.255.255"), mask); // for every IPv6 address, assign the global peer class f.add_rule(make_address("::") , make_address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") , mask); ses.set_peer_class_filter(f); To make uTP sockets exempt from rate limiting: .. code:: c++ peer_class_type_filter flt = ses.get_peer_class_type_filter(); // filter out the global and local peer class for uTP sockets, if these // classes are set by the IP filter flt.disallow(peer_class_type_filter::utp_socket, session::global_peer_class_id); flt.disallow(peer_class_type_filter::utp_socket, session::local_peer_class_id); // this filter should not add the global or local peer class to utp sockets flt.remove(peer_class_type_filter::utp_socket, session::global_peer_class_id); flt.remove(peer_class_type_filter::utp_socket, session::local_peer_class_id); ses.set_peer_class_type_filter(flt); To make all peers on the internal network unthrottled: .. code:: c++ std::uint32_t const mask = 1 << lt::session::global_peer_class_id; ip_filter f; // for every IPv4 address, assign the global peer class f.add_rule(make_address("0.0.0.0"), make_address("255.255.255.255"), mask); // for every address on the local metwork, set the mask to 0 f.add_rule(make_address("10.0.0.0"), make_address("10.255.255.255"), 0); ses.set_peer_class_filter(f); SSL torrents ============ Torrents may have an SSL root (CA) certificate embedded in them. Such torrents are called *SSL torrents*. An SSL torrent talks to all bittorrent peers over SSL. The protocols are layered like this: .. image:: utp_stack.png During the SSL handshake, both peers need to authenticate by providing a certificate that is signed by the CA certificate found in the .torrent file. These peer certificates are expected to be privided to peers through some other means than bittorrent. Typically by a peer generating a certificate request which is sent to the publisher of the torrent, and the publisher returning a signed certificate. In libtorrent, `set_ssl_certificate()`__ in `torrent_handle`__ is used to tell libtorrent where to find the peer certificate and the private key for it. When an SSL torrent is loaded, the `torrent_need_cert_alert`__ is posted to remind the user to provide a certificate. A peer connecting to an SSL torrent MUST provide the *SNI* TLS extension (server name indication). The server name is the hex encoded info-hash of the torrent to connect to. This is required for the client accepting the connection to know which certificate to present. SSL connections are accepted on a separate socket from normal bittorrent connections. To pick which port the SSL socket should bind to, set `settings_pack::ssl_listen`__ to a different port. It defaults to port 4433. This setting is only taken into account when the normal listen socket is opened (i.e. just changing this setting won't necessarily close and re-open the SSL socket). To not listen on an SSL socket at all, set ``ssl_listen`` to 0. This feature is only available if libtorrent is build with openssl support (``TORRENT_USE_OPENSSL``) and requires at least openSSL version 1.0, since it needs SNI support. Peer certificates must have at least one *SubjectAltName* field of type dNSName. At least one of the fields must *exactly* match the name of the torrent. This is a byte-by-byte comparison, the UTF-8 encoding must be identical (i.e. there's no unicode normalization going on). This is the recommended way of verifying certificates for HTTPS servers according to `RFC 2818`_. Note the difference that for torrents only *dNSName* fields are taken into account (not IP address fields). The most specific (i.e. last) *Common Name* field is also taken into account if no *SubjectAltName* did not match. If any of these fields contain a single asterisk ("*"), the certificate is considered covering any torrent, allowing it to be reused for any torrent. The purpose of matching the torrent name with the fields in the peer certificate is to allow a publisher to have a single root certificate for all torrents it distributes, and issue separate peer certificates for each torrent. A peer receiving a certificate will not necessarily be able to access all torrents published by this root certificate (only if it has a "star cert"). .. _`RFC 2818`: https://www.ietf.org/rfc/rfc2818.txt testing ------- To test incoming SSL connections to an SSL torrent, one can use the following *openssl* command:: openssl s_client -cert .pem -key .pem -CAfile \ .pem -debug -connect 127.0.0.1:4433 -tls1 -servername To create a root certificate, the Distinguished Name (*DN*) is not taken into account by bittorrent peers. You still need to specify something, but from libtorrent's point of view, it doesn't matter what it is. libtorrent only makes sure the peer certificates are signed by the correct root certificate. One way to create the certificates is to use the ``CA.sh`` script that comes with openssl, like thisi (don't forget to enter a common Name for the certificate):: CA.sh -newca CA.sh -newreq CA.sh -sign The torrent certificate is located in ``./demoCA/private/demoCA/cacert.pem``, this is the pem file to include in the .torrent file. The peer's certificate is located in ``./newcert.pem`` and the certificate's private key in ``./newkey.pem``. session statistics ================== libtorrent provides a mechanism to query performance and statistics counters from its internals. This is primarily useful for troubleshooting of production systems and performance tuning. The statistics consists of two fundamental types. *counters* and *gauges*. A counter is a monotonically increasing value, incremented every time some event occurs. For example, every time the network thread wakes up because a socket became readable will increment a counter. Another example is every time a socket receives *n* bytes, a counter is incremented by *n*. *Counters* are the most flexible of metrics. It allows the program to sample the counter at any interval, and calculate average rates of increments to the counter. Some events may be rare and need to be sampled over a longer period in order to get userful rates, where other events may be more frequent and evenly distributed that sampling it frequently yields useful values. Counters also provides accurate overall counts. For example, converting samples of a download rate into a total transfer count is not accurate and takes more samples. Converting an increasing counter into a rate is easy and flexible. *Gauges* measure the instantaneous state of some kind. This is used for metrics that are not counting events or flows, but states that can fluctuate. For example, the number of torrents that are currenly being downloaded. It's important to know whether a value is a counter or a gauge in order to interpret it correctly. In order to query libtorrent for which counters and gauges are available, call `session_stats_metrics()`__. This will return metadata about the values available for inspection in libtorrent. It will include whether a value is a counter or a gauge. The key information it includes is the index used to extract the actual measurements for a specific counter or gauge. In order to take a sample, call `post_session_stats()`__ in the `session`__ object. This will result in a `session_stats_alert`__ being posted. In this `alert`__ object, there is an array of values, these values make up the sample. The value index in the stats metric indicates which index the metric's value is stored in. The mapping between metric and value is not stable across versions of libtorrent. Always query the metrics first, to find out the index at which the value is stored, before interpreting the values array in the `session_stats_alert`__. The mapping will *not* change during the runtime of your process though, it's tied to a specific libtorrent version. You only have to query the mapping once on startup (or every time ``libtorrent.so`` is loaded, if it's done dynamically). The available stats metrics are: .. include:: stats_counters.rst __ reference-Core.html#session __ reference-Core.html#session __ reference-Plugins.html#load_state() __ reference-Core.html#add_extension() __ reference-Core.html#session __ reference-Core.html#torrent_info __ reference-Core.html#async_add_torrent() __ reference-Core.html#add_torrent() __ reference-Core.html#session __ reference-Core.html#wait_for_alert() __ reference-Core.html#pop_alerts() __ reference-Alerts.html#state_update_alert __ reference-Alerts.html#alert __ reference-Core.html#session __ reference-Core.html#session __ reference-Core.html#remove_torrent() __ reference-Core.html#save_resume_data() __ reference-Core.html#session __ reference-Plugins.html#save_state() __ reference-Core.html#session __ reference-Create_Torrents.html#create_torrent __ reference-Core.html#set_upload_mode() __ reference-Error_Codes.html#libtorrent_exception __ reference-Error_Codes.html#error_code_enum __ reference-Core.html#file_priorities __ reference-Core.html#flag_auto_managed __ reference-Core.html#queue_position __ reference-Settings.html#active_limit __ reference-Settings.html#auto_manage_interval __ reference-Core.html#save_resume_data() __ reference-Core.html#queue_position() __ reference-Core.html#queue_position_up() __ reference-Core.html#queue_position_down() __ reference-Core.html#queue_position_top() __ reference-Core.html#queue_position_bottom() __ reference-Core.html#allocating __ reference-Core.html#flag_stop_when_ready __ reference-Settings.html#active_downloads __ reference-Core.html#queue_position __ reference-Core.html#queue_position __ reference-Settings.html#active_seeds __ reference-Settings.html#share_ratio_limit __ reference-Settings.html#seed_time_ratio_limit __ reference-Settings.html#seed_time_limit __ reference-Settings.html#active_tracker_limit __ reference-Settings.html#active_dht_limit __ reference-Settings.html#active_lsd_limit __ reference-Settings.html#auto_manage_prefer_seeds __ reference-Settings.html#dont_count_slow_torrents __ reference-Core.html#save_resume_data() __ reference-Core.html#torrent_handle __ reference-Core.html#async_add_torrent() __ reference-Core.html#add_torrent() __ reference-Core.html#session __ reference-Core.html#set_load_function() __ reference-Core.html#session __ reference-Settings.html#active_loaded_limit __ reference-Core.html#session __ reference-Core.html#async_add_torrent() __ reference-Core.html#add_torrent() __ reference-Core.html#set_pinned() __ reference-Core.html#torrent_handle __ reference-Alerts.html#metadata_received_alert __ reference-Settings.html#initial_picker_threshold __ reference-Settings.html#use_parole_mode __ reference-Settings.html#whole_pieces_threshold __ reference-Core.html#session __ reference-Core.html#set_peer_class_filter() __ reference-Core.html#set_peer_class_type_filter() __ reference-Core.html#create_peer_class() __ reference-Core.html#session __ reference-Core.html#delete_peer_class() __ reference-Core.html#set_peer_class() __ reference-Core.html#get_peer_class() __ reference-Core.html#set_peer_class_filter() __ reference-Core.html#set_peer_class_type_filter() __ reference-Core.html#set_ssl_certificate() __ reference-Core.html#torrent_handle __ reference-Alerts.html#torrent_need_cert_alert __ reference-Settings.html#ssl_listen __ reference-Core.html#session_stats_metrics() __ reference-Core.html#post_session_stats() __ reference-Core.html#session __ reference-Alerts.html#session_stats_alert __ reference-Alerts.html#alert __ reference-Alerts.html#session_stats_alert libtorrent-rasterbar-1.1.13/docs/merkle_tree.png000066400000000000000000000445501351156116000217070ustar00rootroot00000000000000‰PNG  IHDRĆřą’điCCPICC Profilex­Xyźµ×w=ëű|×Úű9k?gm° o8Ŕ@4×ŐŔŮś:Ť#(@ °@ Ŕťý ꦦ†ĺ_Ęú€‘†EIZ˙Bú73 šD`t?Ŕj$ět€-Iř|!âx°ł‡Ł „Ă ,B´4ׄp)„iÜp= ;ŕW$äěNňMŹwńÄ@¶aWgh4Ż‹‹żł„S Ţ€ŹŹ/¤Ź…*t&!_l„yIĎjˇrŇ•®€t’˙±AZµqđűýcă8LłÜáűǶjľ˙¬`L}ţnŇRűr0* Pc{{«üć v®ďímîí퀠ÉŰ9´Ď…ëŕ˙ęÜóo(Ŕ0[ř*2µH扙¤pĄś >I35§ëdĐf|Îtśą–U­ŕ0+G νxÄ™÷ żŽ@őQśP˘đş¨X«„d’Ô˘Śń±29¸Ľ­ÂE¤’•r±Ę’š˘z”F»•¶±N˘n—Z_Ý ÄđŃ´ «©ˇY¨yąĹ°ŇZ⤭ÍĺSe§űm×ϲŰ)Ű;:\rĽíÔä<ć˛ĺĆâ.íaěéqî˘Wş÷}ź—řwľó„"­?W€x JÁy›`—|hđ…Ȱ¸đäÔ‹Y‘ą—ň.DÝŽľ}Ą &/6'.ójęµäř« Q‰aIţÉ^)N×mnßÔL•OIÇe0f’enf}ÍÍéÉmĽu?ďV~|Ačm÷B«"Ťb±;l%č’ĺ»J[Ë*Ë3+"ďyÜ7ýK®’ăâÁlUOuőĂôšÚ3ŹTë¸ëáőSŹ[ž?Ť~ćÜ ŢČŐ¸ó|¤éŃ‹ë/˝šµ[¸Z~¶öµ•´‡˝2ďŕďřŐŮӕ׍ďQéĄę}ßWŇď÷Zq9Đ3xóŤÍ[Ž·ź† ß9 ó É{úÓ‡ţŃ«Ő?nŽUŽ»|býÔó9rBjbj2mJ{j}úÎŚĺä—‡łNstsM_ ó¸ůţ…KߤľM}Ď\4\‚-Ő-ăWV>Żćţ8µĆĽ6´žńóôÇĆÄfé/–ü6|»k'}×qO|oďwümQĂdÖŠ”ŤÔ24XnştzĆčC[Ěx–)6Kö6Žăś÷p‡ąŻńüâuăëP,Âűô‰I‰_—ř.Ą#ť/ł.«'—#?w\V1B©M…BŐH-I˝[“\KSű‚ÎÝi=}=`ĂŁAăS33ó`‹[–/­ľś¤°=ero›tćţŮ.»Y”#·“˘łµ Ţ5Ö-Ͻ֣ËsüÜŞ7™+^ČW‘ ďgCtó÷¸x-(ő|npqHEhŐ…ş°§áĎ#^\|Ů|©ůrsÔ‹čçWbÇÖÄU^-żv'>/!#1%).ůbJĐuďÎ7OĄ§i¦Ëgfňd1e“gďä,ĺNÜz“×–__P~;§0ˇ(¬ŘçΙĂ»ŠĄBelĺňőŠ©{÷ź˙uŻ2űAL•µýCąZžGÔŹ6ë&ë{×?)|šđ,°Á®Q÷ąxsÓî‹™—=Í[˛Z#ÚśŰu_ wĐt,wvŐt§őôZôIőcű^ż(Ś€˘/1„úôîŃpüý{éčĂŁeCĆôƙǧ?UŽĐ›dś›ş;M‘›ŮýŇ27gřűőőüŤóotßzż_[Ô^Ü]Ş]>·ÂąŇżůCěÇűµ¨uˇőľź~ô•›ú›SżB·°[…Ű2ŰÍ;f;ă»çv×öB÷㏀‹ T‘V(tY:¦ŚĽbržNĂH+€U˘3ŁwbdŚ=”ĹTÁü„Ą“u„í ű:ś“‚‹ ÇĹÍÇ#|DŚW‚O’_R@LPč(ż‡0Fä—čĽŘńv‰jÉ\©+ŇŢ2ćÇde™e7ä†ĺëŇŽŤ•„”QĘc*őŞ×Ő<Ő54Ř4V4;µ µCuĚu…NŔOĽ×«Ňżfŕl¨hÄhô͸ͤŔ4ÄĚÂ\ÔmńÉň±UŞ5ţä ›íSC§Ř^;ăzVŮŽŮnŮľÓˇČ1ĚÉÚYĚ…ĚeܵÎ-ÉÝÍCŮ“ŃsţÜKŻLoĽŹž˙Ý·…éçCTógôź xäp^*<RrA?Ś=l!üYDâĹł‘b‘»—ú.çG˘ŐŻĐ_™Ś©‰ŤŤłą*tuçÚëřâ„ŕDĂ$î¤őäî”Âë!7Lo ¦‚Ô‘´ęô„ ·Lő¬ĂYŮosŞr“oůää P|˝Ý]xż(ąxÇşäř]®RTé|Ů`ůÓŠâ{I÷Cţr­4 V%^Íő®YłQűýŃtÝÇúwŹźô?í6Đ0Ô8ú|şiůĹ^3¶…ŻUąíT{đ«ÜŽ–ÎŐnľ»ŢÜľOŻ…Âßľ•*¦‰˙@=š1&2ŢńŮw’kęÝLö¬ËWąşo«‹—{V[ך6l6muâűH9- @€“ĽX› Ą:+(W•`J €Ą€źĘp»zssżó”CP€ĐvŔ¤€0öŔD,P ÚŔŘ€1ŔÄaF0/X<쬶g†+Ă]ŕ đGđO*„‘x…ř…E:!3‘}(2”:* UŹZGC˘ëĐżČTÉ®ő`0Žű-r}ňňď™K”z”ĹT0*Ş&jęę šZAÚ4,€ýBgK÷š^źľ…A•ˇQ™±ńúˇv&c¦wĚNĚßYÂYiX ŘŽ±u±»°ďÎáPŕá ăÂqµá|ąŮ¸ŰxŹđáMâÓáŰă*"¨ ¸}ô…Pś°ą§Č7ŃF±ëâl’RĂŇOdňŹĹĘúÉŮÉ)¨—VR:˘ĚĄÂ©Ę©ĆŁ.¨!ˇ©¨uBŰFçśnĉ4˝Jý.9# c1KÓpłRó!K´•‚µďÉ»6§q¶gŠÎÎŮK9„;v9v!¸¶»ă<´©˝V×üńLŻ1µişY©5Ł}­Ó¦űYß‘××W†N7}ŕý3>3ˇ1•3ł<§9ăŰřŇŃÂŹGë››Ş[Q;­¤ßĎďř3€Ă@Hu` ?ą t‚I°c…YŔüa7aµ°aŘ6ś®÷gĂ[ŕK„â˘ńI‡ÔF^@V#çQ|(Ô-Ô4Ú} ý™L€ Oö0†,Ě,ąy<ů'Šc‰_(5(ó)w©ě©^PóS'PŻŃŘÓtŃ*Đ–aٱÉtHş0şuz?úď > ‹ŚDĆőCáLh¦ćĂĚ,Š,=¬Ž¬?Ů’ŘŮ›;rŽBNmÎy®8EÜî4-žő#ĺĽ|,|ü)&‚XÁÁŁYBŽÂ"›"ŻDłĹđâZ›’ď¤ę¤łe.s—5“S–Qŕ›  hdh‘oŮmµuRÔĆáTúéŢ3ägµí˘íŰÉťLś3\&ÜÄÜĂ=zĎńxť÷î˛_ aš¨ă_ >?bÚ&^z‘+2ë2sTúÖü8Á«5ńš o“ĽR×oßTOťNOĚTČšËÉ˝e™Ź-č/L+¶+ľ»Sö¦˘ň~rĄŐهµ*uÇK=•n8ţ\çĹÉf|k|{eÇH7EŻfô@ď[ŢwŃ#KŁçĆV?'NÉϬĎu.<\|¸Ň±ösSjűň~ü€0šŔ@<(í`F “ť…ĹA1ź†vĽü2ü | !Ť "jHd,r…CPÍh4ÝE&HG6Ź1Ă<&ç#OĄ@S„Qü $P.QůSmRGŃ`inÓĘŇöb=é0tôfôżJ­‘zÎĚ,ĂĽÂRĂ̦ʎas¸#€S—‹k ×Ď}Ź'ᯟŢĐ9jżĐBW*Ľ“± ÔŇCµĹ?ČBjˇĽ†Ü%çéb˛H€H€H€$@ÂA¸ šH€H€üJ€ „_s–é"   Pp.&  ż áלeşH€H€HŔAT „Ë I€H€HŔݍ@ř5g™.  pá2h  đ+*~ÍY¦‹H€H€$@ÂA¸ šH€H€üJ€ „_s–é"   Pp.&  ż áלeşH ÎÔŠĽrňäɸ…úűďż Â¤#đ&*ŢĚ7JMŽ5k–¬]»6SřK–,‘úőëg:í‰ *Čşu뢽ť÷‘ $™$gŁ'·7nś@Y # P¨@„˘Ăk$ŕ!řšďÖ­›Ś5JZµj%ţů§ ,×]—…=sŚV ڱ([¶¬>…Á“Ś ‡ń¸^˝zuýź?$@Ţ'@ÂűyČ @ p D‘3B  đ>*ŢĎC¦€H€H€N€ D‘3B  đ>*ŢĎC¦€H€H€N€ D‘3B  đ>*ŢĎC¦€H€H€N€ D‘3B  đ>*ŢĎC¦€H€H€N€ D‘3B  đ>*ŢĎC¦€H€H€N€‹i%9#$äذa^X«QŁFR§N©[·®T­ZUŞU«&ĄK—)Üźţ)7n”ďż˙^Ö¬Y#ß|óŤ|űí·ňĂ?čeĽ«T©ň~^$đ*ţÉK¦„Â"3gNÉ“'Ź|ýő×z;묳ô˙ß˙]ţ÷ż˙éE·°řÖęŐ«áŐ¬YS/Í˝cÇí‹l=zTű‡'„™?ţ€ řź»0üźÇL! d pţůçK®\ąçŽ?.Đ«ež|¸nQ@wZ$}ôQy衇twDv$1bôčŃ2pŕ@Ak6Ü˙ŔH‡dٲe:něúé'©QنV&ŚbĄÂŢú‘]<#©šj*©šó)nc°Č>nÁnöŮ( *THA:Lr$ xšnÓZĄÝXvĄ-T*"!Kż^"@ÂKąEYĂ&€Ą¦ł2ű ¦ űpĚ>‡)=¦4ýű÷gR*víÚĄ• 3H{ÎÂIéÇÄW‰§á«ěLÍijϦ - NéGłĎ©™ĂŢMőľ}ű2ڧ@kĹîÝ»3 Ň„RĄĐŮRáÝ|NUÉ©@¤jÎ{8ÝŮ™}6]hY@ˇś*fź=ś•)):” űx (0.†g›é©T©•Š”|BĽ“h*ŢÉ«””ÔnöŮ´0 ĹÁ®,ŕfźSňńđM˘÷îÝ›I©€˘a” ŁX@© #· á–ś Ú‚ ľĆěífźŤŇP¦LŇ"ßŔłoZ*đ^ŕă,L …Q**V¬č{L ; Ppgľř^ŞěĚ>Ă2 }#śůţQ`# đŰożeR*0#J…Q,°çL˘ ŇkÔ¨@DŤŽ7†KŔ}6]haXłf Í>‡ ţH ŁTÖ ěaşŰ®T µ‚JEĽ*QaăMˇlܸ1C7š_aöŮtA …Fxhö9E^#č `¦ ŁTŕÄ"cÁJEůň壏„w¦<*)˙ÄŔĽyóäŮgźŐëEäĎź?C7š}Žg†BŃ€MŠ`ĄâčŃŁZ©8p \zéĄŃÍűR”ÍřX“mĆ0`EĘ=z|áŕ˧M›6’––kĽźHŔ!ćý]˛d‰îÚŔ”ç–-[Ę”)S¤I“&|âî·`sú-ALOü ` ĂÚµku·Ä‚ äË/ż¬N‰é”üń‡îŽhÝşµ 0jßľ˝äĘ•K·8 @şňĘ+墋.Ň‹Ĺ_2†H$p:§{WŻ^-XvďoÇŽůţž(ŻGN@UéjŁK1Ý»w·rćĚi©/KuMXęÉ lůňĺł^ýő D|đA çŤ?Ü ÷_qĹ–<™Á?˙ 8G€ďŻsl˛e± #r]*ĄîčÚµ«nÖv‡–‰%JČÖ­[í§yL$ŕľże°š>! `)ă˛eË ćš›ýĄK—–uëÖ‰jm0§űź~úIj×®-XýŇî ,(¸VĽxqűi“ 8D€ďŻC`¬&H ô‹ćÎť;Ă”K´>|ňÉ'Y* ćvGŽ™ázž}úČĹ_¬•( PTꎶíżbĹŠ÷ň" @lś|W®\›pĽ;ő¨Â?ťFRŔoĽať{îąÖC=d©yâ:Ń;věĐ#•R6eËßR-z¦˛–§ď{çťw¬bĹŠYC† ±Nž<vXôH$§ß_” |ĂË żűâÔÓłM1>öíŰW`\fܸqzC¶žĂĽ€Á—ę%t{·}űvA öożý¶\pÁć÷$@Qŕű%8Ţ5vaDŤÎ?7Ş–yâ‰'¤yóćrýő×ëůŕ‡±vĺažwŢy2sćLąë®»ö#žyćmO"ń1 H5|S-ÇÝ“^*îÉ‹¤H˛páBÝŇ·čŰĽűî»%GŽÄ<˝{÷–ĺË—ËÜąsĺ /”őë×'…#%ŻŕűëŐśó‡Ü‰©)üÁĘW©Řżż Ů©S':t¨L›6MOÍLt"Ë”)Łž={j%⥗^Ęd?"Ń21>p;·ľżĂ† ăűëö‡'ŽňQ#LŻőŢ{ďIÍš5ő¬ ¬en‹d»;î¸C›Ę†"Ř6lH¶HŚź\IŔÍďďÔ©Sůţşň©qF¨3Ă VŤşmĄübŁó(-[¶č©™¨¤ŐlÝ1 nq… –^˝zɡC‡D™ŕłŞ' WŃ‘@Şŕű›ęO€űŇĎ÷ĺIÜ%‚WhZ¬_żľ( ; °ëŕFeaŔ€˛xńb­äŔ~ÄĎ?˙ěFQ) $„ßß„`f$Q 4/ÝeËóÎ1CĎ®xřá‡őj{nOCĺĘ•İ4xÆ ő´R·ËLůH ŢřţĆ›(Ă‹'°Ű†Ő\ţtńxFΰś#[Ź=öL0Až{î9Á EŻ:,%ŢŁGQlDÉ‘´´4Ż&…r“@Xüřţ*rúC€ďoXŹ€'<±ÂŮ™ü±65˝k×.QËg{Zy@ĘkÔ¨ˇ[OZ´h!uëÖŐƧ"#Bß$ŕ~}ag†ďŻwžĂp$e D8”<âgçÎťrĎ=÷čŮ XŻâŇK/őäዹzőj=Ŕ˛\ąr2fĚ˝—ůţfĎĆÍW¨@¸9wl˛ýűß˙–jժ饱±~E‡lWyyóć•áÇËäÉ“ĺÎ;ď”[o˝U¨@¸-G‚äç¶mŰĘSO=%Ó§O×Íó  ňĹżv_|±|÷Ýw’;wnÝĺÂéH řţFNťďoäĚ’ud‘?MĽ°˙â‹/ęő+đB-_ľ\7n|š»xŮŔB\ŻľúŞŚ?^z÷î-ýű÷—#GŽËÜ“€ŁřţƆ—ďolüu7D‘Ž LOÄúłgĎÖFˇ ¤—ŢŽ zý‹ăBkÄńăÇukÄüůóɆ%Ŕ÷7~xůţĆŹeRCÂZjŁsŔáÇ­{ď˝×*Q˘„ĄÖ°p0¦Ô zćĚ™–š±b©Ő>-µÖ@jB`Ş#Ŕ÷×1´:`ľżÎňŤ&t¶@$U-ű;rőrčő+öîݫׯ¸ůć›˙ľČُŔX´FüöŰoÚ’%¦ŽŃ‘@<đýŤĹĐađý ÍÇŐW•v’Ť†Â{BŘľ}»uăŤ7Z•*U˛>˙üóĐžy5n¦Nťj•,YŇz衇,Ő˝·pPjŕű›śüćű›îÁ±˛"Ij›Ę˝,(V©REŻ_Ńşuë$I“zŃb,Ö Ů¸qٍŠ~k:—ßßpI9ăŹďŻ3\# őĚpoPf“[)żŘčb$;×]wťžYcP7ÝtIĆČ4šŰóĺË'ť:u’˘E‹ şŚ:¤MźyfŘŻE4ŃňŹŕűëŽ äű›ü|` DóŕĉňŘcŹÉE]$ÝşuÓëWÔ¬Y30ެ@CkÄĘ•+őě—U«VeĺŤçRśß_w>Áď/,ŇŇ%†@ŘźZl-Cľřâ m*GŽňŃG ş+Î8#ěĄHb‹śwź–@´‰pĚ?ďŢ˝» ˛hŢĽą żčH€ďŻ»źűű‹Ź3,VĆ÷×ů< »S}~éJś!΋äŻöíŰ'<đ€Ě™3G^yĺąćškü•@¦ć—_~Ńf°÷ěŮ#o˝ő–^´Ě‡Éd’ Ŕ÷7 H.óÂ÷7qÂĎ+YOš4IOÍ„vĽvíZ*˛ŽgĐiiiÚWßľ}ĄeË–ňüóĎËźţĎ(–đýő@&e!"ßß, 8tŠ-€ĹŞ{ť;w–ť;wĘرcĄQŁFÄ AkÜrË-Ú’ĺŚ3¤H‘"‰–q$‘ßß$ÂŹsÔ|ă 4(8*A@âń÷ąçžÓă”UIήĐ$‡){č~RÖ+EŮŤH˛4ŚŢi|ť&śŘđůţ:Ç›(ăČv÷îÝzjf×®]ĺ˙űźT¨P!ގ3¨d8yň¤nŔTĎyóć šH9Ő3Yąá\Ľx‡µRĎ8.S¦ óŮ9Ü ŐŹ;¦gľ-X°@0ýĄéb'Ŕ1±3Ô!¨µ+¤xńâŇ®];­<´jŐJŹęGADç]?ţřŁî¶¸ä’Kt"°Ż\ą˛ŕ<ť0źý“—ö” ü…­”ÇřŔ veVP^ÓĹN€ DŚ ·lŮ"XnűŽ;îÜąs‹Y9łX±bňá‡J“&M-tŢ#€uęÔŃ]W_}µNÄ­[·ęó¸Nç}ĚgďçaV)Ŕš7M›6ŐÓćˇ4äĘ•+P>ŁĽFąŤň›.zT ˘d‡~µ#FHµjŐ‹29rD? ¨`ŕ` KHŻYłFc':oP«*JÇŽĄ˙ţşé3Ož<ҦM-<öřŹ&Q\‡?ř§óćł÷ň,\‰QŢÖ¨QC/ž‡rĺ4> H ĽFąŤó(Ç9Ë*\˛ýQČČ#¬›7oÖ_ Ź<ň®H0Ţ-XŰ®aÆz %šÍĐ–Śâ§s7ĺË—ë.ŠYłfé–H‹Fiŕš5k¦˙ă*á]¸ŹÎ;ĎŢÉ«H%E9kZ~Qţb Ęc8”Ď(§áPnăCĺx˝zőĺ:ťC`HJmtŠ€Bl©‡Rďql6e-ŔG5‹ZĘţCŕüäÍ›×zę©§~xŕ.#GŽ´Î:ë¬ y†|S „Ą "-,öřoňÜěqî§s?ćłűó(Z ź~úi]Κ÷{”Ă(ŹŤSd3˝ż¦<7~¸Ź[ Ô©{řá‡EUn;űěłus¶9Y˝zőLfŤ¶Ëć2CÉ]{̰Čʡ;*gÎśúöŮ­_’ÝýY…ÉsÉ#]>1ź“—'ńYUy‚˛ĺ¬ÝÁ=Ęc㮿ţzAymw(Ďq/]d¨@DĆKű:t¨Lžř@ĆŹ/°jň…Ďĺ—_žARü7kdŔüă>ÜOç~Ěg÷çQ´˘;ĺ,Ę[ăPŰ”ÓFÉŔű‹1M°:Šrť.2T "ăĄ}ŁďL5•É˙ýß˙i+“xŃ·V°`Á@h¨Tđýox ńúôéňřăŹSPr×ň Ęň c0_VOµ;ŚŢF…kđ‡üGˇ„űéÜO€ůěţ<ŠFBäëO<ˇÇšÁÎŢcśC9Śň×8üGyŤ÷V‚ˇt <Çx :‡¨ćˇôđzEüďëÁ´Ôh^ťĐ?ţřĂR«7ZĘdj¦„cLDŐŞU­eË–é>85°'“žpµ4°Ą?ÓBMť:ŐR¦«-őµ’AHüÇy\‡8p uăŤ7fđĂ?î&Ŕ|vwţÄ"ťšşiýë_˙Ňĺ®ęްěcÓL¸(Ż•UY ĺ7ś˛aÝ˙ýć2÷a[ÝP᥇¦Ż˝©Q÷–˛Pg©9ƧMçŢ˝{~Ô¸KŤ¶&Nś8ÇwPv;¬J•*eRN'%Š*UŞXS¦L9ťW^wćł 2Á!TײUż~} ĺ­qörŘś Ţ«•w­˛eËZjGđ%ţA€ D8Á—Ô2±V‰%,e5řRX˙.\h•.]ÚR}raů§§ÄPË6[çťw^Ly«ŚŐXáV‰Kc &Ŕ|&âź˙PäńqKů¬¬ [۶mó‡Sv§­’#]iCÂÖ8|ćQ5u ĚĂP,ŁuUÓ©6pňŘcŹůŚ·“7ŃO:jÔ¨¨2`ŔÁJŽ´P5BÇod>;Ž8i<ů䓲zőjůĎţµ Ď<óŚĚś9Sľřâ ®5Ĺ,n„á°2ăęŕ}ôQK ”ËĐ4ŤŔč{+\¸°…Ö :w={¶uţůç[Ę2aLáţrĺĘYüqLáđfg0źťáę†PýőW=.iÓ¦M1‰®5ÚRłĺb 'UnÎBUČú”’ž*P‚ÓůŮgźéćíť;w_ŠężjÁ°zôčŐ˝Ľ)ľ<¨›=çÎť—€ń¬ › áŇą‡óŮ=yá„$={ö´Ô¬¸¸˝k×.]ŢÇ«L‹P. „]YëKłęa’şuëĘ;C»0b8€ ~5čNOë4&VcŽ·Ć@‹ę`Zîرcc%ă­ýúőÓ'^ýőŚř/iĎICďxÄj–›^+ŞÚ§kĆ1ş0şté"+W®5î-– x/((ÝĄJcbˇ9 Ýčľ·S†‡,µľBĽexP…„•––f©q ÜuzŻĐ­źţůé=Ó‡ăĎŽ#Nj^xˇőĆoÄ]5NM—˙fŞgÜ#đA€4$B?„q¬'źžŽńŁńu˝zőŇ–*cđ_‰R+4X§ëÝ»· • PˇBqM<ĚäŽ3F‡o·F×HXXĎaaň¬§÷Ţ{Oݬ‰ň4ŢÝa\ ő]ÖŘ…‘5QS.6ÓW¬X!ĄJ•ĘĆWl§Őt#Q »ČúőëµŐ´ŘBăÝ‘¸ďľűÝSčšrĘ©~Y9çśsôrÁNĹÁpC`>‡ćăĺ«'Nśe¨OĎzjٲĄ#IQ3EŮ•÷ß_Z´háH)¨jmI÷A‹KXI€Q „Ń(§]ÇŽ-eÝéhľŤŔâĹ‹µ=äł“6!`ö?čO€ůśx扌+«Ź<ǣĬ*Ôát\—E¶ňŁäNw™ěމ3Ő0Wť·qăF=ýhűöí‰.ĺă8~ü¸6/®ľ(ÂB-ܤ­T"^şÄ`>'Žu2bByY´hQkÆ ‰fŻQ/Ř-\&$b—GÂ1A*Ô°aĂd÷îݢ´Ű +Îü-Z+©1IDAT_ľĽÜvŰm1§rF2†úĎţS.¸ŕ‚ KŻ;™RtŐ©SÇ‘q4NĘíő°™Ď^ĎÁĐňc¬[o˝U*T¨Úcś®bĄNŐ!¨č˘  ˇt—+C1‹·téR«X±bÖćÍ›c+’0GMÝXt‹Î9ŕ‹üŤ—=Źp%E|0‘űÍ7ß„{ ýĹ@€ů<ÜŞĆĄé.HĚvJ¤ŔsĎ=×B=AwŠ@ŘŞ„ňžîghʧZ’¶ ’˛C`©eŁýŚ8©iSłi,µ„ŻĄM&E,˘¦Z>,ČAçćłslÝňĹ_l©YNI ±ÁjmĽ§~'%1q”]©P}úôŐÇ%×]w]ŘJU<=bJˇŇ¨Eő™Ç3X†őLĹR«í‰ZÚ7)Lşví*ĘĚuºƒ’HDĘ|vA&8(‚ZńVÔ‚hşűÂÁh˛ úÚkŻ•öíŰ'-ţlsűĄ¬¤ÇAaqeŁGʶęŐ«g%{ ŰĽyóôZ É–Ă•™Pß~ű­p•ěőG?š@Ő‚?1¤†·fG€ůśśWÓ6uůlmőĹ+ŻĽâ°1¤"l˝EĹ‘C<®˝U™*Ő…ş2ę ;tč`©á\!‹„PfŞ­ XăĆŤsEr`1OÍ+· ]ü0źăÇŇ­!=űěł–jp…x¨/đ1€ú#•]J+‡ŇSě&Mšäšgŕ§ź~Ň_Ë;věpŤL^…ÎĄ—^ęŞ$Ŕ<:•Äřf ó9ľ<Ý"cÚ¦[>ôŔgňäÉVĺĘ•-Ô#©ęRÚe÷îݵČx.¤¶FÂŁ˛A!jN\x ťo/ýđĂҬY3Yľ|ą¨O®I§Í­­Ű)CGzQ5×ćQAĎ͸ÄîŰ·ŻŔDü /ĽÁ]Î{…\0—î¤E[çS‘€”†•î'- ‹YŐ¬YÓR™ďşdaz¦®ZµĘu˛yE ,€ĹĘ^~ůeWŠ ą ę‰-{ϱńóÂÝ(1 ÚŤ3P A}’Š.%ga|˙ý÷‚Ż|,Ä’7oި_‘EMűńÇ—{îą'˛é;@@UĐ’#Géßżŕś› äśtŃ`>GĎÎ+w˘„a°x/zŹôŁţ@=‚úő ]6”v•î Ë+#…ÁnŔÔ©Sý€=ˇi€ypô—ަí„ĆidčĎ-R¤yé"'Ŕ|Žś™×î€Ý|á»}б›[´ťĚó”łŃjš¤L0!UÉ=§çÎť+·ß~»¬]»VrçÎíÁ\.É%—\"m۶•xŔĺ’ŠĽřâ‹2cĆ QSx]/«Űd>»-Gâ+Ź2 &5jÔ×^{MÔŔăřî@hnSç@RAĆMPZN PűAvçí~u¬FÍę¦0eRX (÷hĎ8ăÎx¦†K”…J×V†N¤9–ŚÁ€X5eS-Z$gžyf,Ae{o<Ó¬çŃ=aײÜęâ™ćx¤‘ůŠ™ĂpS>Cąţď˙+Ó§OĎ,hĎÄ+͇–† ęuoştéG cʤфüßśŹtłaŻ,qlßě  L¦SÓ#kĆ«ĄYĄvíÚQ‰ ˙ÓŹsĆ_<Ň ąˇ@(#9˘ćG%w,7™´0‘fW¤űm۶IăĆŤĺłĎ>“ęŐ«Gz{Ŕ˘ÓŚľS5ŐT”Ť}QËäHäA˘ÓKÚĎŃÓóJ>cá*”ÓP ÔTÉč¬îLdš•‘8ąęŞ«´Ü•*UŠIîXn6iÎjo?gŽŁŤ+jÂ^q˘˘4ŠCvÇF@ű}ćśÓ{4…ˇąłWŻ^“Ő±8ܤĂü7ašóřo®ŮĎ‘ě $ĘúYŇV‚ N‡ůoŇ`Oźąf?gü9˝ďرŁ4iŇDÔŇ»1GśóßlOźąf?gü…»îąç䫯ľJŞ)óŕt˙& öô™kösĆźÓ{ćsl„óÎü7ˇÚóÔ\łź3ţśÜß{ď˝’'OQöRâMp:̸=}ćšýśńÎ- o˝ő–  iH†CL:Ě1mc3˙÷ŃČ“ŔŘ 4¨éTz|lü@8Ł\D#h,÷`éWeD7mGŽÉ űýHŹÝŮýŕ™Ź4cŤŚ /ĽPŢ˙}©V­š=JGŹíé1%*Í&ľp÷ ýꫯʧź~*9sć ÷¶Lţ’•f5HL®¸â ąăŽ;äĆoĚ$—“'’•ćhŇÄ|Ž†Ú©{Ľ’ĎëÖ­“n¸A.\óĚ‹dĄcíÔTü¤¬}4Ű7Ł4]»Ř‚ĎGódEĄ@ĄŔ®< „qÎ9çD#ď!  pŚâ#Ë(P6ŚriÔŃŞ©đ5 ĄÁ(č*€b‘JŔ4ű?Ç™ĎţĎc¤ůĚ|ö+<ۨ§íő ű˙Hұ°Ay°+'OžÔýô‘DNż$@$@$@‰#¬@@©Ŕ†ú-‘¸¸Q"Đú€ üŽ;f.sO$@$@$ŕ2¨§QCi°ŹF̨Ł< ű4šd)XHG­Ë.ĘŞź´nÝZîż˙ţµ¨hŔąáž~ýúiSÍuęÔq8ŽĘ°bĹ y饗D­R*íŰ·—›nşIRr4Ň$ţÉ'źZţ[ż_—_~ąôěŮÓű%INf–ŃcÁ1 &ť?ľ^đ.KO>9ůüóĎ‹Zď!µüĽ`‚źÝîÝ»eÄzşc™2eä‰'žŠ+ú6ÉXlköěŮ™Ň7pŕ@©U«V¦óNž€a”3ö!šÖČY{Ĺ_©‚a”´>$K€áT&Pb†* ,Lyô»ű裏´Ńˇ1cĆčU;ýž^ĚBAzŢyç ¦;Â&?»őë×KŹ=ôě›G}T”Is­(ű9Í&m(Ě0Šý믿ÖÍŞćĽ_÷ň×®];ýL㹆Ý?;Ôx¶13V&aűä®»îňs’µ’Ł€fkÔ¨‘Ě™3G”Éý„§ ¬1cŘi@žŔ™}¸BĹÔ‘ě1řBìł Z®żţz=w8ŇľśpąÁ*ĐüůóKÁ‚Ý Žă2,Y˛DŞV­Ş•D†…ĆJ•*%06“ ăZŽ'XE°lŮ2éÜął 0@G‡ŻqLaċ߾Čaô ,ľ”üěP`ďŮłÇ÷ ±=ˇŞuL´Q?śÇÇźßMąŁ•ŘŢR …qҤIşłłIÄ1z °4‚™ü€z[¤ĘdŤŞÂ$Ňh/¦M"¬4Ö­[7%šŔöîÝ«+–ŔI<üđĂÚ¨”Zɇ©Ëś¤f͚ɔ)SĐŇK“~UĐ®]»ĘČ‘#uĺňĹ_č53 űÝ­\ąRfÍš•2+Ńnßľ]O©Ý4ecí›}űöů:›7lŘ M>c…MřĂsĐ©âŢ|óM­Ăúl2ÔŮ¨Ż±‡âŤňŮ#V LDĐXŕL+„ ˇO&čgóćÍ‚ĄŻŤKKKÓsäČsŠ{@—(QB§Z;Ć~ <Ř);}`Í• *´Âř١YµwďŢşYSËRÁáK]±:tĐ-§ á÷îąM›6É´iÓ{X†Ľűî»/˛[w 2DŹůHV‚ˇ8`3­öş#Í[·n•J•*éi…‰!ýw,ÉHóß±'çiĆtpŚŻ „B… i“č2C91„‘<Qµ@@űć2°`€•_Y%'ătĽSÉţZq ˙K‘ŠeŇ\ŁF ˙g®ËR!¸ľ6˙±ŹÄĹ4ÂDj2b"€~I€H€H€Cőµ©łÍ>ÚŁR Śâ€HÍ1”CG$@$@$ŕN¦®6őµ©ĂŁ‘6â. Df0[Îś9u˙ ®Ůýż~ß3Í~ĎáSéc>3źýJ€Ď¶_s6cşPWC‰0ŠňÝl}žţ_Ä „ ŇD40%éŕÁ‹h‰—ŘěĆ+L8^Ú8ŇmŔ›˙& öé1ö´zy€ĄI#Óü·âĚ|>µ /žk>Űćí÷Ćžďs]~Ł7ÎOď3Ň…:!±ťuÖYÚ¦f)bĚ Mâ|°aXDşŹJ0Ę©LńPB Śâ„ŔFY€0¸qͰTČdűG!iOłiu1™ůLA ĹÇx(±7üzÉAn¦9c_!ó™Ď6ßgď”b©X†!wĚG˝©w1ĂÂl¨źÍl ŁLšz,š:**B"2`¬îá°´7*QsÂBˇŔÔOśGĹęEgFdŽŽtŕ?®›=ŽŤ"aÎG“9:Ŕ$ţ0ͧ´ydóůÔ3ÎgűÔ{Î÷9‰SQ§Z†ç[p˝ +8ĂĘ)ędŁDŕĂßÔi` xŤJ€€›éş€@ha€pH” »ň\©¤půy!¦˝ŕ0•Š]|ř5JüâżÓüw‹óůÔĚg›ďł—ʲT,Ă?(ŻP˙˘nƆzŘ´@=Ρ…ţ°á{Ýn>G­@ 2#$1ýH8ap¢E¬üeü„+ś[üŮ•¤›y8ͲšcěíÇđď5ůŤcš˙Îo0±ç­ýůlžwďůlł 3ď­ßŢg¤ĺQ ěřřČ7c"ě-đkĘ.ł÷ ŽX0@I€B€ČˇÍ—ŇMwZ&ě­Ćo¸B&ŰźIł˝eÁ¤˛™ôŕśy0±Ç†sv?úŹ~ćS/"óů”"Ég›ďł)ç›gÁk{űól?6éŕłÍgŰ< ^Ů›şŮěˇřlź>WüúlGš÷§#ÂI(C]ł‡á§c¦ŮOą™}ZĎŮłńÓćłźr3ű´¤b>gO#ű+\;{6ĽB$@$@$ ”P Đ›ńr0ĺ‡&®xń`8$@$@©G é Ä˝÷Ţ+Ź>ú¨Łä—,Y"őë׏[*TuëÖĹ-<D$@$@^#÷1‘€ňŔţ¦H©Ń? $—@BZ ľüňKąçž{äţűď—2eĘČ%—\"›7oÖ)çťwä>ĐÇGŽ‘>}úh?­[·–ĄK—čĚś9SŞU«¦ŻÝ}÷ÝrěŘ1}mőęŐҲeK}ľWŻ^rđŕÁŔ=öt9 2DĘ•+' 6”ďľű.p9==]‡]·n]yę©§ççĎź/W]u•+VL:uę$ű÷ď\›3gŽ4jÔH‡÷ꫯÎg'Ďożý&ýúő“óÎ;O4h _}őUŕ €×$DŘ»wŻŚ5JÎ9ç2QĄJŇń?ňČ#Úߊ+äăŹ?–)S¦Č»ďľ+ŁGŹ–U«Vék˙řÇ?äĐŠ ěĽňĘ+°'Ož,#FŚĐÝ/đ…ĘKvňĽöÚkz-v(˝{÷–ľ}űÂâ ř–€ú‚OW[TnÚ´iV©RĄ,U ëű>l©ŻzK™ş¶TË„Ą” K) –˛#a)…"‡j…°&L`=ůä“–Rç•a˝÷Ţ{Öرc­ĆŤÎďŢ˝ŰRvľ-„ow‹-˛ *¤ăĂyĄlX•+WÖ^6lŘ`ýřăŹúX)/:Ľ7ŢxĂR%­’%KZJiĐǰ¶nÝŞýĄĄĄYJéĐÇřż•+W†”çöŰo·:tč`íÚµKß·fÍšŔý<   ŻHH ´ŞJ•*Ć:äĎź_[¬´Dܸqٶö§)^Ľ¸ŢĐĚżsçNÝÝQ˝ző€r†.n¸A~úé'ůöŰoţkÔ¨ˇĂ@+F°S LŔJ&â7] yóć•_|QJ”(!Íš5“;vč[±ŕČ1cdذaR¤H0`@†™čŠ1΄J´¸ ‹]­Zµ´ĽĐ‘ €W $L8tčP€‘ú ׊úrśS-zĄ0T¬¸Žm۶mrÇw”‡íŰ·üŞÝe{®¸âŠ€ÜłeË="ŕůŻXÉĚĘaĚĂľ}űa®_ż^j×®­˝aŞfóćÍőů… j…Ăt{ŔCVf@CÉřgĎž-żţú«těŘQĐŐ˘ZJ˛‰çH€H€HŔő¦@¨&{Á§ş%´R€/{ăĘ—//Şk@&Mš¤O©î©Ył¦®Ô۵k'ź}ö™ >Ń€– ´ś7ožVpîĹ×}$ł:Đ růĺ—ËŮgź­[ ,¨f$=žˇ^˝z˘ş.´R1ŹĘ…’§gĎž2kÖ,ÝZrË-·heéřń㡂ă5  p-„MăÄěĚd€€Šsâĉ ŕ }üřńrăŤ7ęn( TâpP"0 ]h%¸îşëôr¤Ŕ¨Ć3HĹŠufuDâîşë.Qădܸq:<´ :T·\}őŐ‚®Ź&MšfQ >}ş®|çÎť«Ç3 µ!»V‚'NčVTÜÁ,Z h «ŔîPą˙ňË/şU#wîÜöKac<„€)eË–ŐţŃ‚qpnŠëö1úBźěäAW ÂĂXŤm@ˇP(| ŹOP( źÂăÓ …§đř´…Bˇđ)<>m@ˇP(| ŹOP( źÂăÓ …§đř´…Bˇđ)<>m@ˇP(| ŹOP( źÂăÓ …§đř´…Bˇđ)<>m@ˇP(| ŹOP( źÂăÓŔĎ;üIÔ*Ţǧ (,NmE€…/ÂăÓ@`áKńř´…ż€ź;Ű×˙???’ń^…Ď‹ű_Ö@VÜŕGˇpź6 đĐŹŔ2VâŐ”e€#‹ —áńi –AYËčžW\üé顦Ľ"Ŕµx|Ú€Â@ŠŮy.WTĹ…Âőx|Ú€Â@’{¨’=OŠáČBáZ<>m@á/€=°hřÓ¶˛ě!ş¸ńíńŹRA oŔăÓţ*’÷ëřýÁBáÍx|Ú€ÂĘ(â+|ŹOP( źÂăÓ …§đř´…Bˇđ)<>m@ˇP(| ŹOP( źÂăÓ …§đř´…Bˇđ)<>m@ˇP(| ŹOP( źÂăÓ …§đ”‹ď*ýŻ°ËŻ·ăďsÖÚ …Â4ŢĆ;Ź‘úÇÚĎT‚đ5őę'Ť …Â4ŢÇ;Źś1ÖűĂ•’t‹ …ÂqĽ‡wC+óß|ë†Čź±ÜmEż|^( ·óÎ1ܤÎŻĎ„”WPżmÉ­`ý©T°P(HXV¸šw0 Ó¬!Oą{4š,2Ŕ±ĘBˇ°RxŠw0ł~HąŹUvčm·ż/, ’îŕŚÇČ®~‹Q"YÖ„ÍQW …B߼’w0Yű®Ł­"ŔBˇ á°Â…Ľń¸Y>@`ˇPř+<ŢŻ˛°P(H …żl÷ŤxuéCCůxżĘ"ŔBáţR …)dBčG??ŕ/b„Ď@áŤĎ"Š …Â|j ®š‹=á¨,qń I …)|Ę<2Î*uâăíxĽ_e`ˇp űĂçÇĎţ±˙Żż>±±:ĽL¶k5÷żĽ®ńß Éň[•fQX(śÂ‹x$}é/ŔęŚĎ|al“RXUđe˛Ć´Î×ÎF[`Šú˛ …,=O÷ż,!Ä(ĘźaaBČ#“č÷E|ĽŹ÷«,,NĺgűiĺŮróúcëŞÔě§mÍ"Ŕ$Š …SPÔőóĂÎFh7ŻěuämsČ“yd`E€…Â)Ř˝čĎÖhßŘňŹM4P7“ŕmŞ-çűi}6Ůńńv<ŢŻ˛°P8…|%yé`övEó"ŔBˇ0…kCč`ęvUsńńv<ŢŻ˛°P8…żBE€…Ba )„Š …Âl‰ůÜËíUA­“÷CmâăíxĽ_e`ˇp 1Cť$@X)q±^I˘°P8…ď Ŕú1„c(,NA…Pú—äŹ(¨ŹVg˙Ëeę/Ëý„?†đ)ľ …dąßÚ0ßscgŕ«#˝Ž•Ůż2’ů1„"ŔE€…Â)88üĄőĺ7VŃ!@A¦ŕ (&Ŕ}), 9¸`;Çéť“×mFśŞ˘*žB`ˇp ‰-°JöPMĽĆü©+Tł0Ť"ŔBᢇ Ń/đ‡ }+«żË«¨ňźú1„Ă(,Na.„¦foŇPX(¦0B'Şŕ~ E€…Ba ? Ż.}h(ďWYX($Š …²(, ˢ°P(,‹"ŔBáp˙áîÂ[PX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY }Ú€ĹQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …@đj`ˇPXE€…BaY …eQX(–E`ˇPX_N€?˙8yťţ÷ńRxŚpBÚE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPXE€…BaY …eQX(~®ü ±"ŔBˇ°,Š …²(¨ßç}7jÄ źD`ˇPXE€…ÂoAĄË—ٰP(,‹"ŔBˇ°,Š …ÂüŤýx`ˇP8_Í„E€…BaY …eQX(–E`ˇPXE€…BaY …eQX(–E`ˇPř"Ľ÷µÂ"ŔBˇ°,Š …ÂďƉ¤±°PX żú»kٰP(,‹"ŔBˇ°,Š …²(,–ĆEwéŤĹ"ŔBˇ°(¨°P(|‚ňľô˛°P¸żtř÷Q(P~Z(ü $Cą°P(,‹"ŔÂoFĄěwŕÝŁú>}FS`ˇPř(>ąŠ …€i¶°P(\€_t7‚™ZX(áQž‹"ŔBˇđĄ¸źb‹ …²(,tü…=M!@M°"ŔÂעµp=Š ë 8´˘°P(Ü_°ü …oŔ鲫*,ţ4îć•7ńÖeję`ˇPř˝¸”q‹ ×ăÜűů%X|$Ăî_26E€…?‰Ĺ™ĂAŤŠF`ˇđ>]ŤôÚŠ˙]), sxŤÓŤJ¨ž …wa’ËŢ’&ż” …pßŢşTX(.ÁĺDyHŕ ‘*.,ţnĎÖ(<ťl}ťGŰ żQÄÓ¸JٶŔ…?„ üĎCĎÁ s’9» …ß„ąMQo`ákń{»ßciř­á?"ŔBáoŕÓ•Ô™Ó"üE€…B!€O穌ŽČ ŕlŘVW/,ţ>ť˙}7ÜŃ), źÇGm“ňV$ŔZ+=ÔČ ń׆hŞ?—<=:‚/ÁL`E,ţ >MČCý3’_źŕáy ߇Kyĺh‹ëqĘőÄÄ?‰´ÖCßŕ$…_‹ßî>ăěëbÉçdxBÁőzR`á—á}tzěźŢ8ißUś8¤»°E€…oÇoĎ«ţ4>59G~ µ),܉o%ŻoµëWăŢA˝FzÝ,~ ľ‰Ąg~ěĺđŰ2°ŕŢQ(,üI|y,‚Üß÷ ĺ)gňÉE€…Â/Ä řc?xOóM)PWXř»x/KŰž°ńĂ$xŃŹŇ_ŇępŤ"ŔBá·ŕÝŚw­ľ7HŁđť KŻN~[âbśHónÁkŦńîş,‹·ÍÝ79Ém9úÍÝé—®ő+ĎÓ}.,ţ8&Xaúé‚ý~šľHęsZË­(,V$­A?Ű*ü;ř"Gý"Sţ<ô=ţ™7–ŻTü1ń3?}`QXř+řŰ´{čźĎÚŁKĹF&_[‰«ăŇ\ŻŠ ÷°áwpl`…yçă'©ëÄ«‘ŕ¶b?6Ż=‡Śű­řó?“×鯓ç+łÍĄ” Ě.ĆpÇY¦ŰAź=Üő#W§ß‹LÉ-Q±UQ¬pď`<ââ§ }ĺźä˝Ś}z˛ îjˇp_Çj‡ :M‚×í˘G{Ü+żu"®KV¸w0cű…f‡¶ŕG‘îß#ŔݏŻ0âkQŁ13,é¬íőűŞoZV¸šw0#uąËďéĎžŠJ^Ţmý±¸c şPř~D_đź•rľÎiÝb;ńü#ŃĆyD‚ŮŕŢÁxŚ»ÄEp~}&¤\ ·`˙h»ötXËřš8=ďsŹ(É+¸Ď„űžßS_´C/ďÍi é0ÎŻŕŚG\ěŇÉ×Mţ [ŕ‚ôNďçéţÍÚMŃ)ĘjŐĄ­Ń;ŚúKo±˛äk6čEč«yă3Ň•ŇD6ŞîIöę`!Âôw·ľţK™Fg•O˛ÜôÂŁßII¶zÇl9A.äŚÇČ.q‹QŇD›š°9ęjáoá–`ąHčŃîŐpDmhË9ófJp%l 3’ń_ľx%ď`<ĺLńU(üf|I\›{5Ă'ťžD&ś»y7P—­î°ÂíŹJ7ËxţÍ0ľżhÔî5ő+ŇŘŃÝÄk1˙ĘálË }.-zĽ_ee€oG~˙sżŧl¸Qď/ůM›w}Š żß@nyŚž,Ü(ýI—¨¸ĺ= Ă(,üĽ+žNę1›°ôsßŃű†đÖŰŢ'L°`JŮčÇE€…›đ ü §én}ĄŃůšC˛Ţ Ľž©|)Đ©/×>Ó V4ď~#n Ĺo˘¤řĺćwŚYćή}Qů¸%'ú0nZXřbÜ’¸ÜĄ÷č×Ţ(łgóňŻĹř]•s6˝‰ôĎl«3:‹ Ăßx:|–ńnMp®křŃÉşB9–QXř(>Ç— ;Úü _0–"ŽżŘ ®Ż"ŔÂ<ęnÜĹÜ“ŠŠo ±Ă ×źáŁÝě3o§ Ę­B·°đk±0ËyąáÜpý‰ÁţŞ]ęx=Ĺ˝€:Ea`á}8÷¦ÚŰăűÎäęäCQäÇoGŢđäć"Ń“>3«­°đYĽ‹Öb=™Íĺ• p4®{Ă}Fçą aGͧňöá÷ÄÖ€íÄŁVE€…ß‹ü%Ůŕ¬ÂË}‡äě«uÓÜ.ă”8ýLcľ±¨"ŔBá gÇű%·˝đ>đěŮŠ ›Ő6|Śr\›,,|áKÜĽ0Â÷OÔą}`ËZŽçµôä¤w\űÍCs€6Ű'’˝éBŚ"ŔÂŻÄÍľ8µ f_9§×:yg6ŮŁSO©đ[pÜ.í͉ ޡ6WOEb/wd§řž1ëť6¦­VŇę?’v®‚·q齊Ćq­ţĚĘdB‚FŁ ő)y®ÖĂŹ$ś\-ySńT…‰Ză¶}†Š·S÷d˙úý÷ă#ńĆđ‚eś^ś¸ź¦Ą\1¦Nçź·`ę¶ť±č#Ź4f—’VŤ@`ż!ŞŹáŘ.gşŢoŔ«Rڏ0Řľ'ž%ĚO…Ţ3N±Ânk1ČCžÄw«Ń*ň:-,Ś@đđ€Äĺt 0^ő٬v9kŰ;ö0'f~éŽYíKś˙@)T:cJŕ—ĂŮjý–ŚęjäűŤü©ťéˇg—1ĂÇÚçpúWĄ.ęѡŽq¶÷&¨đ—âFŻ=xGđĺâÂhď˛hćąBĚS.mő„Ő&]Ţš»ŇÄÖŁđ7â-Ţ(AËé7ĺ đ8ÄťXîT_ď¨)ÉźšŻëâ„úŤ‹ NzĆ/#‚“8ŇŰ™üéF3Biço.y-§Y &Ůť´3Y×HĹA Îą_é ’Ű!ťÍ™Žk^/ŢśĽ×wŚ!6%ëHµLÚq  řŚÎcvěÍf÷m{ʎ°p'ć=ýtF’¬Iřä6•1bĐĄ‡ŇĚÁ¶łą¬˝ëzÚĐÁ2”’$ě¦Gŕjr?/¬'+]ÓÉ{†ęŔ¶çPÎ7®N ŃőćKŻáĺď/śŮĎ#)ŞÜ''-qEüžüű<.N]>44W¨ýĆ›úGË‚Š†~‰ý÷…8–˝ťi0WE,4$î®]’[Ż|C{OŢXnK:®Z>ÎÓĂ@f˘O©¬3“¸:/ éŰ ±Ń¦;WÁ—˙ţŢŽ#“üËÜ~IU»sz¦Hi ú›cGáľé˛Ň@ś˝äÜżôĹ^±~Čۦv%'S*,, ÷8™—EóĄ´$ăBa–nĆż{s€Ä†Äo‡F –2Ť~»CW¸"ô"ů•ůgî;Z”ZOŔ śŔËů×d>Đž!\r÷ňTăąÍÂa›V$Ŕëďj]Ź·«˙öA™˝Su:ă¨öqv:˙ÄdKg+{wěů'ňN¨śeĽkŕśž·7…Ň%a˙1öěęŢ'ô:L_“ňĽŰd®Ä#Óu…BĽBšHýÂ;éٵ°a4ßÎ$—ŕ}Yҩ辱NxÝF{`Ź›Ęý%ăpöľŔd{˛GŤ×â!-ŁJ~9¶ÝT)ü¦®űé¦ď>ş†dxä+¤Ł·©¦Úͦ}oÂ0‘1™oż$vĐ˝J;›Ü.F€_)7ŕ@żÎ Ĺ•yPÖLłß3ďgłµ~a”Çľ ł őRÂx‚fiIłý€KçVSEą˙>#Ŕ†ŃŇr>b®Ť9,í÷Ä5Ý »4Ľĺ5Š/–ti«CwŘܢ(;9'áýĆ9Éą ÷FW%Ŕ_Š”Ă\pë*Ű`X㎠'â‘Ěaxej{vĨÖŰON“áÁ¸11†šĎ®d©ÖŤFĚ\), X^ČVĎ62†ŮĆŮdäŇśy…ˇPd×ĂÝ\j«÷‰-ŮŹ$5Ç)9*˘¦ÍŚHňŇ"Ŕ_ßľóťŔ¨«ąü×I×0`-ah»r“u®ąI“Ú&juNbEčÄŐÁrP˛mpU™h“‘w'Š3H­ĆoĂ`Ďä_›)żŁĺ9Ułtp< /ź$ĺ'GÝŮ€·Ł#†HwŮČ śéä|“®2ÇĚ\F„)F•1"ŕĎ~©đ(ná·‘Ëp±….fŐáöiJĐQ´NÔŢ şˇ˛ů~ú Ű˙˝!qŹé?„y ‚U`nĂŚ«0–S]ř“Fă Żm:\ҧ*Çé[1Hě˛CtUZË =;îą+!kÚôvJÓ—#ÇXą0ÄWÄŮa#÷T®Äi±3;¸Ë Â)µűM §3ÂR+„ŁÉˇ¬ěLIË}^š[śÚ¤UŐą=Ĺ‹? ä°ř‚ąśúšÔAŞxçţţ+™¸x {˘ŮtŢť”`’ú™ Š…d’?Gi`K6Ą„™ăâx6řŢĽs‹Ű7Ŕ‹ÁŮöz…?`ĂwŹ›·Ő…Ćş2˝ÁÜlĆwľˇžóšëů<ş'ş˘lq|â»Ăc„Ű­Ď'7čłËöĄŞďč‡Ý^nÇ%~9‚Çî—Ť+ĎöB-(“ˇŘ g×ĆţŘXŚ­L€—¸ţŰ©ó›¸:gËŃ<0ÍyN´ 9b 3Yâ­ícŇ Oç¨apgĺĚZ2·k•'=Ů#>>>… 'ىŕ0Ěfk |.$Ł©]NBÂńf3>>)ţnňvč%V›Ucx«ilW˘ĺ LĘ2©oô9+;ńa ¤+ «ÎéDR‹Ç8ĺKďŃy…ůi zBóÍĽĄd3ąwźÜl WăgÇ©®ó€–ä$:žůpt„j"¦!3¶Ź»ÓDfX¤c¦ń-»ĎËŘäT‡ŢĄţkzť»»dC¤s˘fŇìĚÄć.-öŞÝľ(ÔF±&,sU35`n.X˛§`1ĚŻhąĺ0%ó”{ ĘnÄ1Ţ©-ČL!}‰:óuŻó›Đ©]ĺü"Qč–C§ĘčÔJg+/F€A‚|éćoă3¤w :kÖYóqűw±m.o&ó÷:śbIpWną&Ľú7zü­ Ň•Úq\»(+’l9XľĄ¤Äně—u^¨®>–#ŔwŕâÉľ-€ŹcgmHQě˛WŞVNµ=l+ą'c©–ůŰPˇň †_ݬ4żş·*-q»ć;Ĺ´@ÓźE€÷Ąjłnô椪sl0—#/—łş—z´„ű§há`7ř-TŽ5…3“쬵Ó•/ćŔ–qţyȢ0U‡IłiUř1xSžqBÇäąĹ †óRÝHe¶ěěZ`LŇŃ2ąĂ‹¨3 Ö#ć ÁIȝˎĂqsëúŤö§Űq& ň\äó!űËd–őýźK& ÷‰ęń…S8 í͉ä޶‡_ZśOĘš’čfĹ!›ÍĹ’ĘQ†®4»Îe $XóU”űÎ9 $b—ýAŁ~@¶Ţ^đoúeăSg`S1é_FF|ÁM¬ĎÉň^ëĦ$Wőě€f«Ë·ĂňDŠÖP˛ĺ*)]ÉŠ'ü -güvM/uě§féĚą—™ćîËg€ß•ÝŹSý=‘yŠpČeHX^Pűô\B0#ą ă‰4xtnůx Ľ›Ós}´O#¦“`¸ŽŚNćóâܨN2(&íI ôĹčQLĺPKHŤSG‰ 2â•Đ©˘[Ł…ńJyH~ŽßŘ&űcŰt—á\"dVăLučl­Bŕť8K_Ý®Ď~Ţ,éJ•©{ěŮŚnľ‹ťžIs°äxÜĚî5WGÔ·)Éżţ—yG bĚŐŁ«Ö‘€˙ÔťŚXXgaü@pĎ€”f™ŁQÍYÉw58´® ®l•–8ź“z 4” lÓąP×–9á‹Äz¦˝Śeé¶Í ›p»IKŕ5pfhĽć‹@Őß!y‡é$‹~â<»¶C"Ăź?±1‰*Ç„G™ASiÜ8Ú&úýŚ4˙Űqĺi»źÂ 1xŰ˝1lQĂőĆ1t9ÎŮ’J0ÄŐ7s©ő`úLĐĆđbŁ|/› Y~Ň—:É9_¨MěĐ”çü[BŔ˛$úW5bZ~kaROďÂŘ”˙*®L€×äc‰VůŤĹ ¸Ľ—č'ôqN<”e 3)NT×y”9˛` ^RęśGüĂ>řBX;Ld~~—&×%SŇPĽ™EŇĆź´lUďăIkŕűqě‹ćÓmvg ź]ŽŔ”‚‡Aa"I‘őgmP~ó (sB×+´5ÇĎ<·˙Äë˛Ý΀ňÁö–4fľäąĺ{ç_=%6&Ýçůۤ[źt˛{¶ů!ť6—RřetQ›+’Î8oŚÇ§aŢQeZ|üł+ŕ_b’@ććČĂÉšźö>ł=*[ËÝŐfa<ógł§ůĽŚĐŞs=M_,qĐw2AwŹk;ŠP˘ vÔ8EőÓ@§:JlÜ%Cń…µ•ä°ŹśČŮ={fŞŚ•±Đ]ŃýĐď7ę•Pv ˇŰ˛-L€ň‘s:ĆŢ•Zj×>äÜśžV€—÷Í:yNŘTň„•F ÁÉťŇi§ ŢýŘë ĹéÂŕţŹ©gś9Fv”w#…OI[١kjłŘ›Í´Ů€Îúm/©“–&@wüĚô$Űĺ‘ŚŁ·`Ę”ĚIuł•‹âěĂ^ŞX>x IDAT8?Đ€­ĐĚ|qô•1ÜTP;RîHőhŃežö`ÁÓS;¬ ZoTsÓoĺ<ţLĹ=ä‹Ń¶$ÎFÉäăR Iy©WĆ´|]'eš’‘ÓŽÂ×Ëň†‚“Ť˘ńVö<÷ĺ˙#µNéăř:űů¦[vn˝«r¨HNŇš8‚§şd-y ŕw’µ‰*0ëĚP®1ÓĺaĄoËŹą ‰?ά Đ=ÉS ŢXÔčE#ŔiŁgD^%#PÖůř=Ŕ÷aüŕĺ3,PsJlXf<đŠ^©Ž`źQP3*~Ś“1"Aę.·ţX•|J_¶›úČ,Ó L%í©ťOHúŞ˛±µdNßpÇĚŢ—Šeŕ9+ Źé„ăBi¶Á…D,ÇN¦žł5d´$č$_$¸}Íb'Ř&_ĺ ‹T2uaO­T’E溔ɀ$/2ăTBHH!\ýŐĆv·B· )Ď"cľ” Ăžź'Ŕźwqü=Ŕ á„Á “Cf¸ë¦S˙µ§#˛°Ą3Ü~좮ËěĐůŤS­ą‡Wś*‰8̬1ő÷(ńŕű_h …“­ŁÄl›zc^|Ŕ. KܱÁ,嶲ť‚w*÷{±č/Č~>ůDÁw†\ e䝤ćµá[üčdCľ’¶}š±ĚÚťé;ä™±^ż…i‘˘+–†§|bŠÚćĎ·6úÄ“Ň Â_Ëa?ŕ ‡†*sŮô¶+>»ńěĚţžB7›Đ䯉?ž ż°|ž˙űďŤě1®$O ňV&Wŕ󆸋˛­7+9•wDróđаî?59h„zÉN%LSŔÉ•Sšü+;>"tĘtŚKđn*(5®ů¤ĆY4ĐzAEď†Ćŕ»öŔ5üΊ3lz€yúčµlëG/V5ü"> )2ăěš‘ËP\ő ľâW’ÖFk’ ˘ `¸ u®SB'úén ä ^6ďb(»¤Ľ8h̨zbń0˙F¬ůyü·~‡Ę?š›Ľmz“&ŚbnÄ'öúq ,eݧTő,^OJ÷ (ĐÂ]ŞÇ„ëóĘäL$’>·´ĆÝ!}ĹlJC®@…ŃWySÜ-Ą.~UÔFCmť­›+—%a4ě=}~ä!\=Á•K bNň]v$äČ5˝4ĚęĹ â‰;5Lš‘öäeśym*ŹŤ .DŹcˇu!â Ţ*úíţk„·őlĺĐpŤÔD˝ľ[Âţ˛b§ÇâŮúç đmřxhÖSAg˝ › ŐŰóužŐfÖ„i2S2ëů´IA('©i,źGĄ|"Ô7á3ęÄ Ö‚żą@ů驯ľÍη$´§şŰ›÷ĹŃpňÓŹŕűľ śS2^'ĺ§]–ň Bő¨,/ę?ÓiwWŚj[eřĚńuť9Icµˇv‘?Üyr%nr€Ě^żü˘ôYŔ§P¤KˇŔâב(Ď.C@ |":?ŘŁ ˇ`ČMˇf€) =Ó†nEÄË72šŞź×ĽĚX˝ŹŽ~˙öř•âźĹD‘zdÜ.K:‰rz°iS_c64Ł´y4ÉË^*VR-bÓ]ux(·ĐŇ{uő…)ĄÚ†="uďĄFÖgİ'#Ýż±A+&ßc\«v}1ŁďRzĆlŇSSnţq|ţë* wjđ<ú47ž·€ˇŠäÔŢ€#g‘`QWu»ĘăŔëŃćd-IA}Çlo;ą!.k÷z8 •/nÜUbž—J6\ÍŇyčnX¨řÇîĆX '!^LF˙P&JőÄď6,F€G—ŻůŃa2wŐ.ŚÔ$% •?Qqc#5Á) T(ŻGĐ7f´ŕÄ ť™BÉTŠL™Í<ť đbI¦&R@Ş…) g^Ď–¦X­ýť|căˇđ#ö \•IJżŤpš=FŤŃJI &ĄĚ&ÖÍ6\©_â¶€1Ő¦—ćeÁ˘˛˙Îź0âLw˛”PnÔh`É(KŃëH˛ÂĄ„⑪Ő-Đć{ڵО”aÁŕň•ĐGFśĐ]·h7ŇćsăZ ]ârÄdO=ŚŘD¨ H—Dl‘ĽŞČţMNĽŽ<ʱďŰp6(Fc­»«TËśä0řB˝±˛ňµ‹ yżvŕh6BĘčHLcLmý(™3'Ç'Ä#Uˤ”»¶žľ˛ÔfÁnëŹ%Ŕ9Ő˙Ř_P°…Sś ů#5rŤ]ă›.¤+Ëdž,tŞ“zĐš3’}túcUpÓX#ô~ŹnÚMôŻ4h—–">q)Čđ–/ČŽ”!“şWQŤôhůJŤ‰á2ÍčŇ–´â˙ń6¸’pBrYŘ 52Łł˙á­W”vÚű‹,~6wĺ  ńŕŞŢóx"«ŻAäťÜŠłFhĂlí­•X‘ôvćqţ·A#‘c†ÂŃ Ť'Äž |üüNśZr˛_hE`8ÂY˘Ť?Ć2'^$ĹÚŐ®KĐ–[nÇîĺľ.V•dMę˙ëĺĹ´–7[^'ďz4ŞńŘ~~~î§Ű§&xU­nąw>j,†Q3$×ŰD­ŚĄĺDEEľ=»ßŞ_ó­UÜS8sÎ{˝ă¸§~• .cďâyŐĺ4Á+ böľg/“>Ú Hůµ$FD˛ŞrŔ˘›ţű9(Čđ\mú+XűĎŕŰţet_Iꬄď öpč˝óˇXo•55rÖvŔĄĚIĹ~–LĄ”şČ׌Ç46±?“—zŕ8Ńl/[FÝ$Qľp̼鍩+¬pLb&cŻÂsĚ1ŠI&\Ug†Š^Ź”©+{wŠB)˘ç:Š|Ť©űi F8őgŮ@´t&ĆDňeA±!¨…šŘG Áżŕ{>HQmńßHÂ1® H¬ŞČ­pŰDŔ˝łŚP@öVúČ[IŹ?Ó7ËŽž,É˝$ú´źŮ§÷ĨŞU‘ü_ ęK¸żňĐ®ÉđÄç pňËÇ1Ę˝ÓIľs}T3s T×S­ˇL]fꀉP ·üĺ™%)I׌Çm@ďĽʰA‹ŤŔŠăL·]É˙Ş+|‘wS [ÝPO€oCëŞYáÚĐicřĐ79•¶A®™?‡Äç­ŃßóŻ’>pŤđ˛ ŇĚ‹_ńq«$Ť(_—ăń©ľN-´2tŹtĄ@ˆ®¨çĐ»«+·UĐr çưNý˘„*Ó6}lŞiBČji*5tߦv'NMđź'Ŕ/Ř7ÄK–ŞĺWEŰÚµź&ÖŮ;ciŘsW4"=<µďyJÍ·Ř KÖL˛ŻćM’ąfÎÄ ó‹Ł$ŮéľŘ…A‚ú·-ŁkŠě?¬Äëűt­»Rťůń&y4lą~„—yl¤k’ô BÇO|śżáŕ Ţ}fg‚’«Ë0‚·ćíÖ‘â˛z^ í>Ĺůvj …É ö~;hëjŹ0˙ł!+a•áé^ŕ)eNd;¦AµÁ#ő ĹÔnŢ€:Żó ĽŘłf™ŠČbŃRT°×ĺo Ŕ7©ü1CéM^%#$f7ř‡žm,ÇĚŮ>DmÇeü‚ż‘ÍAPşUP97ÁŔS‹C´ŮËp 0:üQŢĹ °M'ý*Ë{ĺ]Ěs3\˙X©?đɬ·¤´«˝™oŁSů.0žŤ*sqĺô;'“¨Ć„d›>N€oz z›ęę<¶ę^ŚľZr˙ôümëUym}ňvîé˘Ć[ČxnBüćŠ@ôűěĂ›¶JN7PHů"…ˇFˇ“µÖ„ B×Ú° g˛Ýňx>¶•߯älʆŮa9iÔ€ĺ"ĺ¶ŤďŃŕ»·ŔłÔt&bĚÂ6' .—9˛ ‚ZŞŕp–ĐýÔ[˝ˇ~´,‡ęüR×;A¸, fBFí~ä|ĄË0>\LÚ’ÁéĎűń­„ŮŻµ›ľ¦ ¸-R?2}rmăě2d*ááś í-ôę±˙ş"< OÚµy{ŻA»úbh=ŚÁ›T8ÂúäźK$ü—jm©äPë_voĘp®†KGß%Óí[u7b»Č‘$e V˝†PűKžµj xŻZkµwMľö!çƦ¸ŕLŔŔz+%ňvŐ@Ź—i×V -Ý𬫲öżĎ űťŐűńOI÷έť„áqB8ë®833CçaçvEní¬¨ĎĂ®›|í`ŘwáńľPĐ äZheĚÎ\°ę(©ÖŘátj](O >Ŕ7«Ű’e-o˘±®q&@!•%‰ÚőŞ×^R㼤·î4Č$ŐVRwŮĚäsQŻwwV#@`ÔhăÓ2+O0w:°Ô±Ş‘`Q*ĺµĘ‡Ĺ„a¤ŤŐ'žEÝG=Gµ=MŃ|Ź=TÁľsě6‡6Đ.yżáÇŚ‰äö–Vxß ćÜQ…ĺđCţ/QĚ´x°5IúJĺőîűýőBĺ~Ý= ;+>fÎĎdc»Ś›‹ ß˙ĽÍ ůcĂĐ—®gŞ Üźě›™˙,qE1—ˇĄ?˘¦‰m·8Ć`Ąjm˘ŻÉSQnaůFĘjx·!s‹ ó*Ͷ˝î¦ÁxTRoŕ*ĺ mT;ÚĚ,ęn"Qä;n„($°~s0lö7ěÉ®•'Ŕx3Š„ăß',őRyN[Íŕ\ł·ÄpxČ܇Řu) ’©¸ äHČ—˝ľ:ţăT—Ă‘?ŐŹŚ˘©ŃÄ8tn¦Äˇe>ěxÓ1 ?wĚ’J] ‚ˇő—/o$câ"Mí/C! Ú0‰ě–\¬™Y80˘@`‰‰ŻŐż+ßA€oř9,řâťÎ$hSWLÄUy"ŁŚ~OŠ0˝AnŔd%(_Ň«+Ů>ëÓŻŽôFx)´›KŽ90@J˝^É‘ť§ó íęüĹËĆv¸Źw‡/9¬Ú†B8á5ůi¸Í±ĚŃČク”íÉB˝;ç‡ đťęŮ‹ĐۆçeŰĐđzn/š~'đŕ„'+±«đČfŕ7îăÔÄÓĚ.8FŤŕs«Ł#‰T;iźžsŇWL ľ¸đż.ÝÝ`ÇlÄ-‹”b«Ś!óF•geĚËń<şŮĚŕő§ß1Š(.Z‡É1qä%6‹ …3p˘˙Ăś 㡵Žű­çű»1›\„, ${ ‰čřQx12`N˙˘DĎó˙[aqAgű­üŔ °O@ŃÚ’ĐÇ E Ů·+uńŁ7íšă2oZřva)„€K•K¤ŹĚ…°ů«ČŹDąĽíÇşš*“VŞPćéŽĎNC+H›ÜbÄÜ«Ôlúę^¨¤t­Ż®bIrďÍÓÄźR™´ůÓŞ÷ýćŠŮOtUĐFŚ”Y)î"‘ąŰÔ賍j†Cźć\őé‹ňÁ{€Ł`óhD\™ú_ŕ]  ¬ĹĎŰć…É$Î4Zż*™1íĄźŹ„:¸k'‚Bxď$ëů{]° Ó´ZgL0™ő C¤YB• 9—Í_Ä ^ ҆ÉN`nę©Ű̉śyJ˙Ö tÎ )uúNQÓc6[¨#,F€ć1ÓD Ť_1ý›Ôdéµ/Ľ„m<óíŠăc¦±ş |*˛뺲Ík„Ă[’^÷Ą.ÄŘä>˘ĺIÔ7 e©”5îlcAB*˛KźřńŔ<@7GääpŘe•6ăŰ&…·ÓÜnĂІ–/!n©×`’4]‰WŤ˘zäI˘)c9ăşžÓ UkŮ%™V™—éKYoAżÓÝÖáŚő"ĐJc4*׌S+-š(CŘjČÄýSQ <@Ц8ĺ$R[aűĐ]şq“ÄÉŇđo>·€ÚÔ"`hťJ  6ĂJ§l¤ć‹Äß^m9´Ă8›_:¬ŔŹô"ČO˝ëHŞśDT&ŻÉu^X+ ˇ€5µ¨N(řń$hmLĽJÉa¨0—UA—‘-sô‹PÓA›ZU >}î÷TżĽVlă…l–e§äÜÚ[°ä ç¬ř*ˇ\N5á—ňyňuě•ȸ­‰a=Uađü»ş'Fäżă„ '8lÔ°źŞ]ý‡Őł¨Ŕf>gúˇN™yŕ’ yĄ‡˘‰Eś€3±Ą‚ŕqȆ–˘źH˛•q m°P·'kz&ńĆŐzO¬~.©‘Łó^$ž;ÁJ…oˇf÷ţ·ą #`äJ’7_«`‡łĚ‰5(ŘÍľAÜL Ż“W"ťGxîЧ˝ƵŞ' 2T$4ýXżç~lM%S'FD˛¤ITďĚ<1z´^äÖ§G°7Ů[ Ć9jWLde‡¦zݵ ™j]@Dďč%¦µßřŹF;ľĂŔaYĚŻ^đDť‡ ő`™¨ç´"ú*oiÝ/k¸÷żń˝4P\ ű–XČ1ÖôÜ[ ÷®€µD1"yi›bö^ur\ ź/%5»Č3i§X;ü©ŰŚ" „ĎâÝٴφô çÍ©2ěëĚ…5š%]řy¶$Ž‚Pąv8° ©Ś)§b$ Đ1H×WçžžîŔdÂÓ{©µ°±őŃMďN$Á–f$eŕ Ş(XđóËČB§:B]{ˇČH‚ľ¤ŢŹłĆ‘.7”ü:ÄśP©or¤ zż€KŰXMfbIče¸G*D–$@ĎďĽAŤ†Ű`ý§^É” &7.IąŻçYľ3Z·1 0›ßj óG2/ ş¸ÉńoŤÚĄ})ĆÂái b)]÷éßg÷jxżŃŁ…ÉČd˘eZ·ď”…RŢ-ův·ěI3Ť ř§0ąő=&ě»ÜśÎ‰µCŻ_”˘ę1 IDATyÓą×Ö$@9´—ÂaŰ\_ ×(ÖĆ˝‡Mě?~y@±­µŠ?}›ĺEť×9Lŕ3ćdiÚîŘűýmßę~›Sß‚ěľöÜęi`Agĺďy!}% 5׬Ł:)±K>Ó9”ÚŤC ?3]›,4;7sÝX°/٨ൕ.ĄÜÔtŹ‘)‚H_K`´:lś^HúŐ–E1Ř–đĆ‹$µH»‚ÉE$˛ âşE°’=GÄm|,ÖD¶©=0ÎbŢŁBA°Ô÷ óHÄŐ¬:±ţÚî¨ ÖYĄnjÚÍ$¦ë%T®*ńAKP3Ę #ťl‡›3^ îÍĚ@wĄŻăĺy~2ë»± Nť;łćöżÓěx|żÜQq‰wߍPŢmbW±`ŰRŹ)XŐ¨_¬Ž’$bHĆ‹}¸B2wÔâą]ČÔĺ\Çć"©U°íů×ă‹Ć©ąëŕ™´[±ŕ˝Ý ĎłFŤöŘVBĚÍí#ŐĂýXŞ`öî–#@5ĂöţńÁőCť3Ź-á…Žźz–aGqލď2,xĺn›{7‹¬zĹmV01ŮşXufl6]šÚ.¶±Ą1ÄŇV»ęÄÄ*:Íé±4&Ëö®P㨶°Ĺ”¨Ý´EAÎ#´{H‚]k(ÄqËťĺqDĽ¶ťăµP†„u br™ľ˘H4î Ě?ąŹ†Ţ#ʤĺÝOAp;Ľ.Ú[0–ŕzQ‹9 ’Šúa·‰l˝·ţőŽŕ*Śí¨3"Ȩ#=>rŘ}E—đbbO­A;Qů¤…wťî Tźyž"GŁ«‰UŹŚÖ>ž.M/€JxŽZ¬E€ŻGPÚ5´‰ŹÍžqîAq Ĺ1#đ±n÷Ô~k»5ˇ!ĎłZU+€>Ľ—cýŻJŲĄ#noŔ¦Y'yd0[L©‘ĄG„ňáGÓ“’•x9A‰cĂę’đ$ýŇ:7X˙Ą;)Ľ…óęJżźgc5ÉÂârżľ*깍Ćq~aŐáN XgR4x-ˇq$mdŁ6©B×…®îˇCÇi†}ďJ[Ăs~5ÔÚŻřłŽ÷Ú›`lŁ91kČ@,f¬ţşŔ&wL„űUťT‰Ş»<č/®eBPü]ţ^čYäjٵ­Q4ĺ  ç>¶(>?6öĂń]ÚC •:˘<ĐXá:žŚř\‘a(t,0"IśľD‰Ř´€Ĺj†ű;Y´·™ŮRn¨¦,ýIŽ™LÓ¦Ď<×Âs/hĺ]˛Öla˙]g6ZHJĂtîĚ5Ü}‚L-ÜkÖÇ °ÝA)x°‚ /¶Ř%áň%ÜŚďŤhÚěy^×˙[Ł|á(Ŕ@|­]Ę÷І–ČQ’Ł—i%y·‹ l±°KN˙ ñO~[ĺXńR«*ąY¬ŞFŞ6¨Č¨)7Çúfo;ˇć*Ě8ʞÓ`Ű€í·D@­'Ö#@ןÜ1Ź#8!á#ÚĘďZĚH—Äö„a·Đ±Ćđî´^Ép3±A¬´±S{#Pek•Ť!Ńt/†;Ôg’ľÚM­ťˇŽ ČjĘCGŢŁÄ3%¶kmnŔ5q…ŐŃÓXc¤Ű€Á$Ö­`óĹ{Ň-4:-í(.F€ěĆ z žóK0ÝŢ« BM˛)ă““fM•ŽBz0ş,bBSFGő-÷Zřp~Ě7­ÔډŞ:·Ý[ôąĚČn© ŐGłO×đĚ[psw2ť“Dć[-:)Űń´ľEOFłň+ŠŃô´ô8vÓ€ĄźE‹ w07›ď$ŹâUKůŰŕn‘™ÎŢ*GLŻÚřj–•Ť=ľgeă-0ǫ̸ÓX-Í7v°R‰«=˛ľO¨˘_ť·t„m¦ßś»Qđ¬Ń¦HÓ㝍•bđ™öÁÁ…˘7Ţä"D˛Xtm'ť a®N¬&‰fş;ř™ë;ňX˙'Đ%FZC˝dŁWěű|.ÉŮ@ăíA’˛´ˇAAF…vü7« tÇřţ»´řgŽłZ6é$ŘČÁeWNâ6”bGÓBp k|{ĐsQň›lGňĐM˛,GŞ™J~Üu[†üâ6kc‰1” tËĄÄš·Őc z- V ÝP=53 %by"G*í° ź1^ '•ů«/®J€xD%b×aY†ŘĐ×3Źâ"oÖč#ć0 °tÓŢ/ dµ®CŮĚěOÎŤć¬!˛"¦ŕŤ©^ŞmÝŢd¦ÂĆ`\. ˝PΚžn-ń üi¦ÓMÜîj”ěu¦ťń‰Ô«“Ťş§ĹX»^%˘̷ŇĆ;üĽi W®ÖµĹM ;${Í…qĺ.ěĚĆ6Đă%~ńă6«Č|řm¬kĐNL|Kmy Ľ^gaśŢö©@=ÎjtU {KZ2rCPÉ˝Ś GAí¶ÇśŹPCE’†Ü_Ú'ś8¬ˇ¨Ç÷lcb;Ҥ>+±"ŰęöM6ţĐ Öt˙\‰ť¦îÔ­H{±Śű˝Ŕˇá>+AöO[źÁV„HŇÜ0Ń'†DÇž¬ęĽŹ-¦×zRXŇŻŻy˛ž!2]aÖąěÄČ“6ń =.Ţ4Bv´»‡ű.Ź]MŚČ)ĽÚ,E€/ŔµD-"̈́Ũr޶W•öjoÔĄ¤±ËlOu×¶w UŔČćÁ:фþŐE’-6Ő€ťhřw#Q©g`7Ú„µŢrĂ  ľ}čtD’™ÝŻ€~č:|ö™ćŚ÷Sśâ'.ŽĆ2ˇfaá—Ż(€+ĹŠÖą`¸Ťăę—ś&}QtëŽËÚÄEWtäkbžIµ@¶â `¤ë!1źÁ'ĎV0x&zP ýĚÜTÓ­BFڵU'¤tqŹx{.ŽH[źíŚIÜp—J€\ÔV—żdú^ăUËVÉL;®††Ä/H€˙aŔw$ýşDcjĐDŢŕfŐ5źŻů÷U×s‹ĐIˇÝ„B{ŕ‘h%ćGŹÝĆ[źř[8ÂHˇIT’|$— ś¬aŽ\×÷âěfśU'Ä€Ű îaş Z‡ÜÄ»‘mµ©±ÜXŻ}ĺ,éôYŤŤ"—ŔŇG×e>şĆË”ť>0ˇŔCaţc¤bŔóȨęÂÚ›aR® WżH/h˝3f?4˛L< 4Ł+Â0łĎ1cCě?,Öĺżč.źşÜq’•A„3¶EŽXčFŇfTťlťć_X’ffW  Ž~ŔŽ`e›‹şä GX.é^‰ágű6ôP­çéZHŰţ$A‘A`cŁ& …๷!Ě$Iq(:tt±dťdhÓĐěTQ„\«j’°ˇ(mbö~« .YîÁďőăĐĺ0‰Ťb]ó—"rĹdÂú((}ěLÍg‰ź‹¶Ü˝Ťý·@…KęÁ”.şëcŇ =ć<Ú+Úý{4đhźL 2c…25=0Ni€6óÉK}Âć É^ö¸^÷ w&&ýzB<¬ŰŁ/E€˙Á¬ zBDą:SľËS´ĽéCUWSčľîdJ`5ß^5Sďré›Üš6Ű5+Čm’5Q„&I ĺP¨Đj;4‘8‹ÁçĚŰ9_šĎůO\µómú(—»µÁß f ĽH{j}ńć6‘°c˘WŁ—NôżÍţßš­ľďŹ=CL°;˙řvź*˙1ÜrČ&ÍceÄä˘Aj&vˇ›ś˘ć{¬†ÇXŠYm<’ĚM„ýJé€=—š…e A¤˘GĚt‰(^$˙…ÓĺĐ«¦5‡âĹ™S¨ËäWeśŤ3Ň!Ď7ŮŘu’mHv ›ÉÎ8-›0-©_­ńIĚ 5łe![č° şĹE8}‰ćŚŰ©(tMěŚý«HŻ5,líRݤďč-{ăţ¬-7I‰x7Ń’¬y(–+¸LE۲1x9ÇĄC‚6ńG“­3^—4ů!áylFBŞAlĎ”µr[ ¨2“V ‘_ëśĚo~aĐ.€T˝Ż*„›d +‰,N_ƶ´łW–&@äףŤ/Ş}އ¤ŮÂis}X±Tb«Ŕ+Íí«2cBŢDŽĄ9HŔ=˛Qí(kĚEĽşy8lcéŔe#‡*"nI×ĎĐD8käťlrĺ”C«DKžs,ÝŔń~%¤Ěgĺó îSOüĎŠ8â’ą‰;ű|8‰NLŻ h Id1nĄX!sYŰŕšZé »t˛MäŁô`š’BâyőĹťgódë‚ŰIΊr›ş•żF°~ŕqâ†Ć|ë] 9ĐřF@YęhrśŚŕ)6Ěň2óš“#aż&ŁÇImߢ¨Đf†LćVŃPÓ¦8zů@G“&˛‘¤íĽĽł+KŤL+®ÄpEčBĘP˛2ŘB~J‘üeVµwu?âqĆ2±ť‰®K^3DLmŔX=†N¤»SŹm¦‚?–,ˇŮ*íPş,_Řm§áBĺy7"'LX´mH°¶t Ĺ«a«-J€lđe„ş±ĘŹôîŔÖß™¨ťşO'¬ 2»T?4őަß<Áöž)[€y öżSł•µ8¨"Żů?ě!Y›i)-¶afRťţ¸ęőżCjő0®ËĐŘ‘ŰlĚÖ‡–9Wͨ¦đY0#jNŃ;Mâ1 đ5qÜťÓaRÝNNU+M®I€!ˇ¸Qc˘0ňk-ZWÝcΩŚB1’nśńŐłŽ‹/¬60Yhůbh*LZKźböŸíl®ATĘŻBh”'hÂó.L|îLC˝ßŘ…ˇD÷ß#Rţ„~Ž.ĆÎ;𻱆¦Q íqŘŢ^Ý6qe+ śE±4·¨Ň3-ű˛ô5»č źqbĂ]_vŠf‘šăż”Hcťľ1^ŤD’Ó‹Hţ%Đ›­Źb9 …¸nbç#¦#®ű‰Ly=‰zÜÇ˛ŻµÁ x8Źü{-˙]}¦OŚnž§­~»FÓěŞeŢPőÇ&„ćşsݱ"ţÚdBEfzĐă~Mî4U5]Öĺť˘Ś•ňD‡I-…&±YŢ MVűŰ/pwHż"çhtĄCFްˇ"¨őÂ@ň€÷ĹŞ‚f5®c٦†×8Šś5żóŁž™Ňčć¬Ú‰v“…!’ĐgŤlö30}çwöyź¶ÔťÁ°yŰ‚h™ýŻaF=^štů‹U§és\fŻëŤÎ%ÓĚ n^©Ő1 âLµ7©Ď+ŚÚUČęv˛ť©ő‡/ ±öżß„čě“hŘY[Âu@őžO‰cśb+qŘYöÄ„·{Ąbě÷ŰŮýbî} ľŃ=÷ś ŕ“žô˘•Đ2YvOŃNˇĎ‘©‹[…´Äí:e ŮęČ"%L'°>ŠKEÂ]1öVÍ ŃěCcéŇ3PŽ·ČßY‚Î-–d`®6J”Ě*»¬‡Ďz`©şäĹ~áÔâÝvęő±ŞRö€‡9Ż˘N€¨Ă»{’)bQç·%+`Öw?"ý7đ'oB”FÚ'0.žaŹlE°ďלîză ĆŔ &ĂU,…0‚ngČ>>đ†#ˇµzY›`Ë1n´<ęT€äč¬*ľ9ńĂŃ(‘”oük0»RsfŤ÷ZŰ"\7`¬9Ă€Ł•c-KÓÖË›‚W…·9ĐlŠsƳѻÎtĹ(°xoĂĐő)Ç3´ÄĆ‚$Dţô®í+ņnt×d…ńa TG™i"0' ĺČ·}úb5Ű”IíĂ!}^K-^8‘®>;pŰr(Ç ›Ç!"G‘8#’ś"řPĄ·A+L(a]<°F«6b[eë#iď¨+CŤZńšhÁŮ˙:ŹCHvG đç%IÖ‰˛,_Â:úaŞ8ö¬7#Y/PęUŇ˝Őňe…8oíEULŽ}×.F€ΖơÄ ›5ůTÖĽ`Úl;e9ŃZ"­™•!G†Ś†—.˛Y0ŻĎĂ şş¸Żľ˙‘w૱ô÷ÖtHŕőÍSzî†Qż—ńá#`’ÖĎśň–¸č‘Ó­TŁ_š–Ű-íÂhEµV‡ú®€¶$úi^űôÝş‘˘o;˛uOS ˇ¨¤¤ÂŤ¤[ę~´ .Ä^ä ^–C†ůQÖŻąË†&U®€›Ë•đö şÖŇĐwgY0Őöq¬±—[îĐ,–ŁĺŚÓë]đv sŃJŚ™‹¬` SqÄá  ą­e.°2¤ŞŚďc´Bâł/oęVfťÍ´“ šS辬·^Ťlxyމ" đ1éë v±!’€Użz*ŮЬ}4ý~ඤOöÔ™o˝6úˇš$Ćł_#ësš¬˘çĹ0źÓ\͸=#qíiżíW˙čłńÖ‡Ś%ŘnY0'=˝€óRŹę ˘°!*Äž*LYŤ%AÁ´+ŹâGßošÓJzµkmŚy€ťĹŢ0ô ‚ $í“–%4"¶Cd«ÁBuâ|Ł•‰‘ě%b«Ú‘ľŔeAub–đXš¶’` /jSpn'wźZ0ĆŞ%rŻ] vü˝7€’ĎÖÝJ-A„ď-“-;V#Ŕ˙`–Gp®ąQĐŹBňć.ôsF׍^Yˇř"‰üi©Đ;˘€%“śZó5-R§äZâOČ1Ľ,Ép°䍴}›OňŹiK¦‹Z€¶ßŐo*h^0ăĎžý™Ř oĆ”˙ ĂUŔ»¤o˝8ű‘$ĺ °z: ± Y‹‡ŽÂ/GQk _’_‚[Ô©wpK #o{ä®@íŇm”:ř®ľ¦mđ v\˛§í@Ô CÚ¦řk\>LI)ĽífŽ=‹Ôö´tyĚÄz´XrŠß<­÷›mĄđ\śë@©ßčµÜŔ÷~8[ĺpHp…kČKžŻE€{$@—ŐÇóo-‘ZÍuç]*uJś©Ä52[äË/“bŠWm…]ÎTęJ± ·µ ©.Ł?…Zä4Ę)ăúH5q§δr RgÔí”:Ѱa»ô5 @Şkš=oĂć;p4[ééCĺ ƲJĚ‘ĺI[o1ěŽkY/˝Ř¨Ů6gÖ›ď€ěU,cH!v7ŐŢľ¦Ăú]DÇ„-#-rŔ†·zନC›Lk^źŔ@G«+ ,żB\< ‚'ţŢ3eH—3O3‹s®GÍ̡ípZ“Ďš"…G%Ńئ•p_y¤'KČÁ%5îβ ‰GÔÄęÚR(µ/Ź^ŠscˇőVł5Ťr[ĺ5¨¶Ę̜홳!–g¤)‹żŞ·ßŽsś^ł“KłŔ¶°"9 ™ éŻőmíŢ%w_—šeŔEÖ¦8"2D»śPDNąC·Í>óp¶™bÓ°0Z‹ź:j,°ŹďźîÍ!Ń<ŁŽ†ŘJ„2ń睤ăí!RO?šűšç śúĆo†ŮÎaŽ%~ÍV‡ŰŠDř§˛gŕčšGLB›3Fdl"lb¸GŻ.ĹÎó #.[––‡cH}BÉÖ“s:Ö"@ÝaS@"şµmKV_8ŤĘÄëĐwÖqűóLo!UH÷d{uެ>Hńd„«va|á Âh5>łrM¤ţB˘SśćEčf©]6ĎQ±ţ§pa…šm°ëĹĐŚ dž:Ç*†»ĐDĘňiJ2›DŰ.žáRČěś đŔÇ´łą+­Ţó; ¤×Ąx&ş5]ţń¤ŕóžŞÖcçfŁngOĽQĂرin:¨0rěG—OËd±źL†—Uńj¸Í¸ž®ĐŽđZ¦×Mµę“Â×ZĂ´ć"ţŘD”9ŃM;]§îPgÔeŔ¨ÎfëčmI¬:űľ‡i; ş&ô5ĆŻí)5dó"37đcA˙”­ü%†ĽÎÉţ{”~”Đŕ|U‰ő+e€9o´l± ţ‡}čśwÖ_• q‰ë›çnČ`= ®Á`p9 ­dý±ňK€ëŮ‘Ë ĺ®ůFşD¤`XŇ6Á‚™jDfBÔb¶C ”†O‚LsD¶ÝÓÁö6tŘj7>ÂĘ_‡µ/9#Ęz×l•ŕ`J×1·=P¸±ë©ióŹa2ÔŞmŻÁXÉśŕ}ćô‹Aó[ViB« V—¶ř‰h#ŹĚˇ­Ąťŕź)ޢa†ĆŠSOKŇ#µm`TT‰¸9ĚkŁ‘óö«I_ś3j©ěO”o~ŇRý˘•đß_ůľ ,oŹN7†ô1Ź)<űę*_Yî|ŘV–ĚďcŽy磰ŤtQ=î®t“˛1”ËbĎPx3’L7Ś °ÜżhÍĺjV·- BâaŰA/µ)¤4§‰ Y“*QĄľWď×Ř•ĹrřÄ`D‚"îăÁÔ…,ĹoőísRY,pdÁ‡2"}ÇFt‚$îÍčK*lq×(%Qg*đ+ÎdŹě(Y‹;MřOč˝xU×­CEVz—cJÜGŕÚĽBż é)ŢrÜňr*\‹ő¤ĄµD»0ž{ÉR¸OjäŐ„ ܇Ň4ÇŽ—űhcˇńţ¤S3tw®NeöÔČ':·@Đ[*@u<čUf$ ±EóŚËÄÝ\üĄȲy yŽ,^J抲 ä9ná/;í’Ý+řZć±nvrO013šžc`Ż1 7&˘ ŤaRQ—ď%ąd4¨cÓ—„«vľýä”+čr±¸Ž2wÖ¨~ ąDĎi ł•Kě2gÍÆCŔ›Ř`)dvyňá5ga€ÂŔĘýç‡+Z|“×#@>¶ĐY[Ů×aÔËoöËPř»b›W…ĺ ÄOEőMů övů€Ş‡řjâö[}_o›Ž ¸…CAZµ0_óRđ6ĐsÝF+ě¤pÄ=†M75Ö)ÓcŔXLŞB‚kyŤSÔĘńĹÝ÷Ą/E€„ä˘Éďc‹ű>j©$޸KŰvvčÁaęŠ(fAlIó@ ô⣤Ű Ř čĆöfýÄť˘¤ ¤zČH›lIÖ]€µó|"*eĺDľ9Ę)äčQ0śĆK¶-˝*nĎNřő’¬$–ŁLvśüć,E€˙@ÜŹ`d6xÍ«ňYwĐź.¸ű ÷Eᤪ —ž|h"­ ×e|‡ ‹BEÂbç Š#C?ŚŕDcë’„&ŤŤĹ5öxU2W<ýŞOiáä8ҰfžÁ`%LŚ6ń18´´śC¬˛ť­G€ N:UĽY'˝ŽŞ·ś†NZx /cü˝‰ĂmĚÜĎđ@SŐÎŁ˝[–[óZŔ˘‘85â-J±’D»)Zîq«ÇSsŹOľ!­ÎąZC^ňĘe«ÉsËÉÓL縤şâÍčH§ťĄ•9‚tPĎGŐÚ6 YC2v[-CĄÉ7|i*E’™,/ŞwpE+ŃžŔéűśô Ť®:CM0ę’¬)ĚŮ©6(°Ŕ˘Cě®ÚĎcÇAćoTw,ĚwRâ\ŔČD–ÎP®•¨‘ăŃLÜi|+ţüâäuúßÇóäu‘•ŮćRĘë|ÁCyŇziŠ™^&JĄŤÝŤáĆIA’ ü‡dŤ:LJ6‚ń6¨ťóńľJô©"UpáďH[űÍ —C’"ŢÓ1™eË€O ĂQ Ů +» &d2ňqťĐÓZÉűuÜŔ;ŹY?Ď˙ĺ•’÷2f™©ÇŞôrYQNÎŔ­ŘěéĂç‘›¤ůr÷ !˙ë÷ E™ŤrýÎfůP7QŇťWß*ł7ł _ťnkÎţŤ“Ö†dK8J\˙BĂt¤µ>cnvÂĎ\…Т8ĆSë–§€Y)QťeÚÄK€ĽÖ–Ç ®IV¸w0q±QŃě`†tBŢ:鸿ŚĹťRňŚäřR˛ ovŔ[†é#˛ áćőŽÄŞş_íâr®ČP,¨QqµK¬áfÂK)jh‘ehF€ÉÇ7=ő­×ľîőĄAhÂogÍ/Ľw>[G-eÔE-OöfÚ€-đŐĽńX®“Č]~ç_©Ś[÷ó:ŇřóC˙ţ—š<~čţ Ę ÜÂ{˛€ĘD¶˝íčK%fQG(Š[w¸dÚPg’žĹ{"d–v¤z«‚ąî“âZü*“Ó±ŔýŮhĹñaŕjm. é˝ZÔč–č83'y[Z/ˇ!˝JöĎÚĹŢŔ;ʱ‰\ç×gBĘ«p öʶkl•Ç«Ř4…^Móß…v#“d]AU"”ôK¦ľ´ŢO±Ú9żűĘLHĂ?*ĺ#i¶$6€Ž‰ćÁ ‡ wŽŮX«»D¶‘0/řKć`P`ŤÚëO“PK¶Čˇ»ŰřČQrĽqVHś^Á;Ź„iÖ=űTU:Ck“Í=@¶“ńĚ6›`QGáäDĎÖ}ă>&°]ň 3Ż›/öŞä"´‡ő±É7W”ý$;*Śpâ^0ņߵ5r=XĐ@źĘ2Áá°~ľ‘wlë_9–„3žĂŃ©ZţtmҸ‡)C©ń|žâŚG\ĚHWJ“w!M*:|ňô$÷‘Ł i˝BP?Ś[p7]::ö×Á\+AÂbĚdšl´-/Şz©7c$­W¦8Óoc„}içV ‰«örYiëH‚? 5m®ýMv,ŇQňú´së h,ńxŐ¸FPŠBçś5ŽcÇËŽ‚óäBŢÁxÄĹű^Ľ}CúmHS6ç]Łć$üŽ(bŕ “ó†=%őż(3łÉÂomŚ|‹Ř¸¸×’ Ë’ľáÖEš`5;@Q}0śQ3@ąFŁînľˇ;¨šˇpä=ł eÁaK/ĆpH¬7ŕ{€WňĆcPÎ_…ţ|8çkÇ’P}oă±k&gĂĄ›m×燉ŠuI¦†¤׺/čXv3*ťŘäťl);(᲌ÍBezŮ@sg {¦¸Ő`ĄaĎ흯Á›Śk{¦ [ٵpOË:ÖĐá— yăqł|€ףç™e¸—µÜ‡;ŞbąťŚrű´—[ťŻR>yäŔ„–5ŽŘžMsŽ!$÷ř#»qÓŕѢžĂî¦$Hź#ĹǸ·Ç3<Ś}pNUŞť3ă)˝xm™7ݡOn“¸›č<<ŢŻňyPPŠřľÄ«:újÁĆçÓ¦¨Ükxß'• ¤\)•9Ü ‘«kă¶ł#˛Ö‹"~MڤJjśo±B‘AU\`ęe Ę©µríŠĹfť‰DýîxXד/aé‹›šŽˇĽNńâh:0L,E€»×ŚTl&!|5E’m~`îÍą-„)X:mC ™ń0ęşřB,[©Ř)\«Ń™čň΢ca¬C‹ôĺvĹ‹©ě5Uç˛Yś'”צřflMşáÄ·ŃF/U\‰A¸ăXĄ=î9đś¦1©il‹śm㩚_č¶;+­+ełÜăjŁM>jŘ@§ů4l·ĚşˇV˝„šq6ź$¬,2)ŃİŰfN»şUN÷Nň)%+ |ÂŞŃŞ§÷ٰr07ž\•Ę?©‡ˇkßęâfĄŰŽÉč ěÄn¬ëk"íýĂh˘Ä®42ŻSuąDćč2ŃMËů×›łk¸@ň)ÓŤ­« ExŮ„t룆-E€żO…ňÁ†gPőč–•łs¦Ů´cś‡ě"}¬Č‡Ôˇ%jËȸ&€/†Đâ4‰š] ‡ăŻQpĄÚ }3Bľ­}<–Ć +çD,F€-Ĺ  .‹2ŕM€ň pBZI2Có4;5ţIvîů“rźn m»Ľż¦¨Ż3›dĽ·…\Łťëś2K'’ÂQľzĺăpµ@ˡ1píĚ0 'ó—Ł.\Ó˝á„PC‹ ó{°L f$\‡AÓJĹ*¶LQŻE›ť^É4Tc•`mć·’ĺ±w»™–g3 Ë´}!ĂŠ™CÖ3g‘Ă+¦ÔŃ©A3ŞáéÜ÷ç 1ĂĆN…`(’‚ö'v9|fJ›7úŐqÂđkbwa¬¶µÓűů~»«?îőu6ŃmČ<Řf5tl´ÖöČk»ČďlđSęä:öŠ®’ů ÄĄG&†ß^¬"v‘*Ťě›ÎV>%őđZ’l}ák ‚|(ě|€ř—ßô}-5žJ‚˘ĺňeńł ‚“íľ‰ÁMI4"HćľŃÇXP Ô¤t4;µpV%~rO”+U“ŹrTk÷ĘŚĐLÝ[Çb·¸Ű‚Č€ŐĐń˝žfÁŻň†HŘ%ÁŽč&˝/&ë%ćf—¦TCµ2IlߣÍz'˘6ŇGňa‡ż¶{ ŐÇÍS¨W‡xčfĂŤËŚ kÂ.e[«rA–ĺOĎHřś‰e.E€ť Č:ů\A˝ ‰d5ä[4´±zĘ3L‚eFaű‹R45¨cĄ˙erSMBŤĺMÔéAÄLŇľÓŢôťt©kśµ%WŹ „ŐĂiqš…tź+Áă%ł„!sĺůJł c:R:=l¦Đ K HE‚á1ůá´§źŽČ¦–x{ÍÚ¦0Â*Ť@@†ńÍ_h?¸ĆŘ_FüëČrZľş/Ŕ˙ÂNĄgŐ«vmĂčϰfÎ×cšcÂWÔ߇ĹŘ|ńO{˘ÜŇpâÔ|sť"ŃÓ\đR"”Ç™4J]P•(LXľ,ŰLĎ"î2ăîî§ TĎZ#ŰüJ5„/‡­rËökҵ“­‚úśŁô`k §Dâ—^WŐęŚv˘w~Á}C‘_¨RÎzͲ5ŤőćČ;–„;~\*_=Nma!™ąŐ|G ŢhŤx÷jŔ<.ArÎĂ·¨Éşăó+s»¬?« çˇŘ_©ăÓaŘĚ˝Oed˝w ¤tHCÉnGĂuÄ´çPxYś¸¸š¶Au`”3śě›mNÁeµł«ÄÜ25¶íőg˛KWbBYľę«ćj¸ČVQ0ÎĄô©ń Úźµ¶}.ćŃWŐ^źĹ‚Víćiú^ŇŘtăБ۠Űb¦ďx0ü±Câ;°g\Ĺm“ÎŻŽĹwʡüň&řňóÇpŔÓä §jNÄV"@uü·^R0ąŁW‰KeśÖ‹P0H!üőµůT˛Z ĺ:>Iv&ľ–ůŮDwXřTŠ-żX=‘Ţ‚™J'«×˛Çő¤xŠ˙oZ¨n–‰šŻD€íČĎĺ^çD„äuŐF?KÂľ»'k{Úç™ŕ\š Hż0T8ą4đć©eBë™Qel łş"ć,”ŹiŮoÁ±—ÂŻĐ{mă% đ‰8Ů&骴'c0őH&^ÚBB‚J•)2~}´Ćݏý~ŕ1Ѳ‡)×ăĎŮ'}ŐMęĆő.ĺąLňü%@†łĂ+űđ+ĆăÖ"ŔÜ*Źŕ®&šľňÍüKH¬bŔ˝¬‚ĺN´ŔÔˇ#Žëżµ2ČťÁ%•°™3°@Šź^sá¨ÂÁD:W=hŹÍÖ† §š–ü© Ě LbŠ-`Ĺé€x´MĐÍN»wJŔC± ÚćâűDă‰Ěę†űJŢś7ęĘ´:‰•^Ý|Şgjdţöëűďă·”-Nű?zµ §Á2™ú©´nď‹9­ž ~žäź3 }ćĄ HÓ鎜^ÖŽ¨ň×±Ľ¬á*ś¶ázÜ'{5t}ÎMĎřŰž{°đ6)ĄźÖGŃÚ/ŁÜ…·ŢĽv@„ąő\>tHĐ|íóíŽCMGÚ€ă–ŢÔÇôä¬,Xá#ě?Ą«ŕ¦Bu´Í’@ż_ĘĘ2«8d‡ÎŹ"W„ň2´ŤěęÝŮ0uŚ9ćëS­LJ’•Jź79EWřT ş‘4cš›séä_Á‚řôÂ-34S‡?î`jy‹6ű‹ç7Y=K›“9Ëya:Ě8Jr˝—;®te΂ںĆ]E 9ńŇrłň+ô]`ęĺN/°Ę Ć?*Żľűď őa{4÷€3`)řmjFĆNV)˘9K® ‚# ]Ö‡ďâŘIU<>bW““3Ő38IdźH%s:W#ŔMçoŤýśĽf'jčŐ;]Ëů–qóJ~űŤDžĄ,U§ҧ!S9p$0sŮ' Łqq>©ľTô<ÂĽř´ 9ÉpPŮ'رa%l4GŚ~ž°kdç'•4ň“CjRŠĹŞ•dCÔłąd®FŇć+~ .+áĘŤŐr¦oVĽźú%?€ÄŢhRŔ´”ŐĐ'QšĄ0.¦—Ć”9B˙]ë`ź‡,šÔrď:ť–óí/Ť~»uU;˝›%ńş_ò1Ö#@;511X2˝·ő¬lP/ż µjMĂAşč_ąoŢšl &MI™o §f˦j™ć˘űUŻ“s›Ëk¸č—0Z Ó‰•ĐKąS»F÷GB{™#'d´c’!4ÂRéĹٰ"k|ľĺW!5YGŚöÖ¤v|ÇH\ŕGóę>Ť•Pd5Ă0ŇŁ¦)VÂgńĺ¶­˛"Ľ—µ5®eŮlrwźŞ{X×ÉÝăµÇ“đŁ8?H5XđWţ+ C¬D€íČî)đ´{Żľi¨{Ţř÷MAĺM˛Ž;Mb[Žl›AłfjCo‘Ď™˝ďTĄ$bw,µ—8Ž qB._Tż#űú,E€űěg6zĂ’Ů«n#˙±žr“Ő“ĂśkR:·›h=ćn5ŕ˝Éé¬úć^ŘÓŚk_3·AľK`´”IęoĽöÂÚ©/ĆŤj Lh>2í9«Ë;b…+č¶&ÚÎăvźîë`˙5l° #Ŕ`‘ŕ˙ ”89ŞÖ˝<ăSt¬,¸Hú"Ľ›3ąç›«¤+J+'älśi>;(á?3xË®ů’!ý ¤ś·ŃŻąRř»{űN™ëQ›ýťóąa°[>ęˇg<ű@Rë–}0Âä×µŁNNŃĎŘŢŇů»”ĽućîV¶ţ÷wOôřM{ôŁ0‰źbÁĄŢť-$Óäfó:OŔÝ’9ÍŮ`úďËÍÓúąî˝łŮč…zďÄ7®)ż+ŕóC‘Žü EŃf© ‘-¨v=ţĂĂCĽĂÁOn+ôţÎP>–źĺbT|ĄĹá]‹„=YĄ×!Ţ`Đ #Ŕxă3•ßÍÄŢDŐ˙ú® ňŐ¤!M|”ţÄ9đć/ńéËn^ťďŕG†h¸Í/¬F€O€M÷s/ ôpáż—ЧdĹs†Ůkń¤/Ă1iFó=çŰ^Łd> ľŘ€o1öRë#@¸ˇIěĺr%¸\3ëÇăóHF”ŘŠ)tÉíü×·řöD5’˙0UáŁXŠe ;®¬s$ď8ŤhŹ*Íą`—¨ŞoÓ•eµ÷ŔŞ<›*«â!j=‹c=,ڰžŰ^dăťtĺăIj4sęĄpGSÂ+đľűĚ ľK —dQĽWńS8Ŕ‰Wxţ¬ŚŮ8<—Qď!JťNŽ×•ÜD_CŠOŮr?3÷Î`)Ü> ‘Č}XÇ9ăŰ\ů$-!ĺňşŁ& bŇ„,çgľ!xÄŚ5ůç×özIô<Ů+´÷¦çî2%=äÝw”3"n ŠËod*Žr$˙‚1ąlŁQŘÖ"@űëV>ßÉü.x@1H6ó‰Ç)/?ŃřpS2—čĂ{SĂž@ ),E€ůoâĎmG'`~z&­áîí܉űś^b‡3ĺĚć> é3IĐđ.ĆŻĂý#żżKŕ?ÜżµçźóĎô­©Qˇ×ŕŮđqq3KŹßÁ 8 ˝ŕě8ď]Ąpt ę=ônu]WřĚí·#j®;寧ąg:‡Tł÷ćáVŕáwMçîäR8ůQfJ¤ëć &ÜO•Ä{i•]ďäŕ2Š*_‘1Žn*Î ŐU?Ż7đ7a%ÔŰ6ăH‰,ff_9ă¨+ĽšýłqľyÇŰÎh™´8mÉ©'>×·|Ţlăo+ŕ~`a.ľ“3‹SwŰ2©Ôq|eÜß"úňmşżŞ›ëੜbÜî˘Ůwvډ-Ţ‘śK.ÇÜ*pg@y·“ő~7ţd§&±{xŕÚW›¤ňĚös·ÁUż7=‘8ť¤5~·ő—c)|âd*”~Ú‘NcŕŤ»©}´÷„!ßüf[?żYť{\qÍŁ“Ŕ–·5ú6ü‰N\€ đ…[ă[ĺlyťß$ăj§áűOK rńBáŤX—9ćî9Ý˙JEü¬ůÉţVţ¨Svăßa…#śkó]Ľű]Ö¬‡e Ý·Ű€?ĆęH¶öŘkBĆŻxA/\8JŹu=ÝęŤě/2uË „y1f2 É N÷—_s&š]%&aŢíĎ·“×<>Ń|ţÉ ”Qdđ«PÓĄ±<λÄź#śW•ŢkG·.0ç‚AűuÁűë ^+ŕĚÓ›śřě“ÜSfE?Dý…HxC?rŻîĹ˝ăż"žó‹~3®Ćőoł $Ž'Şwđń˙¬L€×„qňE˝÷*JgM3/Ó8wŹ*?‰/YG~ĄŇBĂJ8÷¶ß6ř7’&…MŐ>őîo§ßĄÉU~ŰłśOäÜĹM `%D7Ď®}w6ĚśhF’üón#‘ßţ¬6cÜőOÁ żuđ"®:˙´Ä’ÚîçšűĺüÓśźű|éćűđ;t1ĽđqÁ}¦źŃžyžqŞö˝R®Ă·ŮSř,F€É×Ý’7ěfÜ»~+®č|~ëh®€/ž›u ĐŰ®’úŚpŰMýÄ}˝ß|ç‡uÝëű_Yż«ŹŕşŘq†ŚŽ=T™PcnćŮËňęş˝@Ďčâ›±s Ńű_ŕ–L8ĂÝ×őřđ „Bá,F€Ýîđ1Ç™—±účyËgę´Žk_4*.ĹZxUÍż˘|m€ĎܧĽLŽóCŞ/Ĩqz;Ö"@ßŮn}qdž»Nă›Âęn[ľ©ŻW ús3Ö%ŔďÁ˝ßNą ôŃĽ÷Â_„…ĎaQŚcŕ†W˙nĂřźi:hě™>ÂWŚľhĐ …%Ŕđ^xľúU–/ŹüŻ5ú L(üu¬L€ŰŐŮ˙†Ç{Ł÷/Ö +…ż‡Ĺ0’W="9ßî ŻH~ďŔwYSř‹XŚŹ Ę®n ŃE˘˙­Ý\dLďÁ§ď^˝E€‡řÎI1˛oSvŮŠ"–Â/EŕřćŰŕ†}ěĹř$»łîE Ŕ·}Ĺ÷Jx˝Ż˙Ç­pßíËŕůé’đŮŻĚÝ+µPĐřuž¶ţĂö[¦ţ·ŘY(Ľk Ŕ9FřäCýF¬ŃËÂoÁÂxÍ«-7>ł˝š+ćľĚ{‰ö˘»Âwâ˙žXŠŻű1,ůyî©pP+PÔrj ×Á˙,E€ďWůÂY"‹đŕP_őű€…—òދűţauĽŕ»q_ŚQŽZ8‚ĚďGĚz ËŕĚoĹü­Ó35Ţ!â×i>Šßgqá ҬǰžBř ćÖŕ•ňżKć»ńúđëńußöřnĚz E€—ď)<ý&ÜeÚwąđ[q’őV%ŔDX~˙·tçô~;NóťÁ˘¸üąNţą’¸"×ĂX”`xÝő›7 čˇđ‡pŮ&7Ć—ŕĎ˙ N^§˙}Ç{ –.#śʤy*ĄÜµőÄTŞ~ĺ§Ď#M€;rŞţż˝»ÁN 0ú–4{™ýŻb¦;Q@5UĹ»÷ś™t˘‘ĘŹ_ó@m±O'ďŰB ¶ ÎŞöƤ÷ă\ţ0\?|äňćşj¸o8y°IDATľrĹ1×4ômq•Ňľ™ő=ŔŹ‚łŞ˝>áý<—Ł‘“_ß™ŹüË%đIňńá—q’ď‘rfoôRśUíµé†ÉOp»ú>ćŕAmx¶™ţ ‘R'oná$ČÁYŐ^ovŔńfžń´ôx•‡ź÷čKýÜĎý˝ýěĽ/¦ Ďd]ĺ®z| p“ŕ¬joŽąA˝f¶P±z aŮ"8«Úη˙Ŕ®_Rş—ʀݤ=Ąńľ€ÇýŐŽßäÎ_ŞmÇ÷2Ąódo$€3îy0ZÚŐ«˝©Îč!đÔů»wŐY%}$oNˇkťěę-Ŕ—¶Öż[ó ÓÍ÷śIöÁ†ú9˛÷:|âw]SC2Q˝%gd_ď%§b™űŽžxŘËŕď|«pGó~§§żIŘÍŇ) ĺ{‡B)˛·%„$oYŮ×ŰťÎ{ÖÂ9’D˛ü0–í ßđÝI˛ÜĂHJóŕű’•,Ů8ĽKô Ę7|ÄN›ľŚú ŕK‘K^Âäăń‡ŢeÖo9­/ŘŃ+BgÜu(Ě ·„©^]oZ9ťˇ{Ĺ ĽJöNGá)Á;+|‡s$±«×„?Öě)ßi Ý˝~ ýŇ»îő@GđphŹ™žxü&ÉÂ*—‡3=V ŕ;,˘k=^$€Ô·´Ŕ>ž@j“=>Đc-dë<6Ńc)Ę"—­ ÉÉű@rR= €¤ŕD.pşG$‚Ţ‘‚r ‹\r@ö§y$%€ěĹľé `UźĎâD.Ĺ ź“=ŠŔ÷dÜďŠ#y'€ĽÇľ'"€<±|`Oř¨NY#zśš2q9Ä©xôAgz>Ça‘K°sîŃ3ě”čvÉ\¸'€§'|°Dź+zfDđŕ<‹\x“–çčü–V¦xđ‘~řďhO¶$€É-/pµ>%€ď8ň|°âÁî0ÉĂ`vö €Ć>$€Çr:é)€ÁOi>Ȧ§~~o'Tđ 3ÜaKVąPnuó˘ĺŕKź»X3ŐÂđń•Wæzp8»Îęá;Ý“Ŕď _éšęÁÉtŔőE.Đľ¸ĽÄU>čPO”<`¦·ľY Ż®xř6Ôú ŕ˙Q$ŕ#tKn Đ-ş%€@·č–Ý@ [tKn Đ-ş%€@·č–Ý@ [tKn Đ-ş%€@·0©ZÓ×´;Ş5®&UkÚbăšvGµĆŔ¤jM[l\Óî¨Ö¸T­i‹ŤkÚŐW“Ş5m±qM»ŁZăf ŕđżŮ;?ďţy3^ö˙ŰÉe÷ź>ż•Og>T­i‹ŤkÚŐ÷fÚşóX{2Öđýßü#oyrŮĺ˝»ëý\e~+Ą0ŮŐ×´;Ş5î|Ú=şóX{a´áîťa6ČĎ{óč `ZăšvGµĆ˝źvëî<ÖžÍułyąýŮédc×7—Y‡űĚíŢťßđk~Óľ~ď‚N·2ťŕňćşjŻőęî´_ 2 ăć'ýž.Éç#?/1Ŕčî´çC\orľŽťŢôÝ®čűkq€oÇu§=ťdzńfi‚o®ůđÓ^pXwÚË˝><ŔvďNŰůöŇjŃDiŃDiŃDiŰ,rNřöDTţˇOŠĺSë›[jÚńarwoŠž¶µˇQ‘GÎKTačˇÎ¸µľąĄ¦8RaäđiŰAŰ™ČűÓ©ň+t5 ŕW™ĘWúU=8íČńÓ¶¶3ńýXĆă·ű ĂôyŐɇľ YbÜńIM¦ý*5í%)C‰_‡ëXAÓ¶¶3qŮQIďç·~öâiÝßC3ŹkÚýüđ¶'IGľ®‚îkí íL¤Ýż1 —˙g^D\ü vpAgú@©É€IDATxÚě˝Yśçuß}zźéîéž˝gĂ,l÷ťÔb˶"—íČVâ”íÜ$©ĘER•›ä6WĽĚUî|áŞ8NŞ\NYqĹNLZ_¤H”DJ")$@Ř}_z™é}ű.sžnĚ`#@ ¨÷TˇĚôňľĎűúHNť:%«««$ŹË˙ů?˙G&&&$źĎˉ'$ Ęčč¨ĚĎĎKOOŹT«Uéîî–ÝÝ]I&“ňŐŻ~Uü~/3ěÉŻ†xÄ“_i6›R(¤ŮlJ,źĎwË×ŻŻŻKĄR‘ľľ>i4ŤF=ăŕ‰'ŽxÄO<ńÄ“{’;ŞÔëu©×ë‡÷˙_­V% J0”FŁ!ŤFCšÍćţź@ ŤFC€–Ď«Őj""âóůZ>×O<ńÄ“;“JĄŇ˘_+•Š„B!Ń»V«‰ßď—Z­¶˙şfł)ˇP¨%o6›R­V% íëö;‰¶ďČ€Ľűî»ň‹_üBľóťďČôô´dłYyýő×% ĘóĎ?/ßýîwehhHĘĺ˛<őÔSňî»ďJ©TźĎ'˙äźü ‡ĂŇ××'×®]“D"!?ůÉO¤Z­J$‘Bˇ ôG$###R«Ő¤X,>ěçâ‰'žxr¤$ Jggçţ˙3™ŚüÍßüŤÄb1ů“?ůůřăŹĺűß˙ľüń˙±üä'?‘˝˝=•FŁ!×®]“L&#/ľř˘ĽüňËR­V%ŤĘĚĚŚ4 yçťwd``@Ňé´ś8qB~˙÷˙ö×u'ßßß/;;;˛ĽĽ,ăăăŇÓÓ#ů|^†††äĎ˙üĎe~~^ĆĆĆ$ťNK"‘JĄ"O?ý´\ştIţôO˙TňůĽ<óĚ3ňŁýH&''%źĎK˝^—cÇŽÉĘĘŠ|ýë_—‘‘y˙ý÷e}}ÝC­xâ‰'ž|&ÍfSŇé´üăüŹ÷ŤH$‘îîn9ţĽ‹E ‡Ă’H$DDäý÷ß—żű»ż“—_~Y"‘tvvĘĆĆ†ŚŹŹËĐĐĽóÎ;ňĆoHooŻôôôČ›oľ)O?ý´d2™ťť•d2)ŰŰŰ÷Ď€dłY–`0(˙ů?˙ç}hâ7ľń ™šš’7ß|SÖ××ĺ©§ž’ťťůµ_ű5”ŐŐUéďď—\.'ŻľúŞ|ő«_•®®.ůńŹ,""###233#ĂĂĂ"˘©­W^yER©ÔĂ~fžxâ‰'GF~řĂî§ţE4]U©TäńÇ—ř‡P($ÝÝÝň›żů›299)>źOććć$ČŘŘlmmÉ·ľő-ůůĎ.'Nś©©)‰ÇăňµŻ}M~ă7~CjµšĽóÎ;rňäIŮÜÜ”ÁÁÁ;ş®;*˘Sńűý˛ąą)ű¸y÷÷Ô>Ŕ~N-ß”S;¬ňöŰoËÉ“'=â‰'žČÜÜś¬¬¬´ä㩯¶«.ţ+tÝťĽĆçóIŁŃ¸é5ÍfS|>źř|ľ›ľ›ëk4űźá÷űĄ^Żßó˝7 éîî–łgĎĘřCy饗¤««k˙÷•JE‚Áŕ>;B"‘hą>Woűý~ R©TÄď÷ߤ{Ń×Á`PjµÚý­¸…˘…Ă~ď>„H$""rS‘< ¶Ľ×O<ńä ąrĺŠ H*•’rą,ÍfS>üđCyâ‰'$Ť¶(ňŤŤ yăŤ7ä_ţËą_$ŢŢŢ–ď}ď{ň‡ř‡‰Däý÷ß—ĺĺeůÎwľ#ŐjußŮEáűý~y˙ý÷eooOľţőŻď„FŁ!ŮlV~üăËwľóéččŘWĐ\g “'OJłŮ”l6+—.]’—_~ůžu\ĄR‘ .Č™3gü=zµ»»ű¦ßů|ľ}ÓAďiŐ˝>źo˙wwjň:Ń=ńÄ“#+ą\NŢzë-9~ü¸T«U9qℼńĆ’JĄäĆŤâóů$•JÉąsçä[ßú–Ôj5–ŽŽYYY‘üŕríÚ5ůîwż+===ňřăŹËââ˘Ôëuůîwż+’JĄ$‹É·żými6›réŇ%ů­ßú­ýT{˝^—b±(đ ŐjU®]»& ’H$d{{[˘Ń¨är9‰FŁ˛ąąąŹxzď˝÷äţŕöé$“éę9ČĆT*ąvíšĺN Ď€xâ‰'GV2™ŚĚÍÍÉââ˘<öŘc‰Däřń㲼Ľ,×®]“\.'ńx\–––dxxXööööŃF™LF677% ĘÜÜśDŁQY[[“L&#?ýéOĺňĺËűµÚQO|||\Ö××e~~^677errR2™Ś¬ŻŻK ÉÉIéččFŁ!?úŃŹdxxXĆĆĆ$›ÍJ(’·ŢzK^~ůei4’N§ehhčĐű‹ĹDĺŢÔ#ŐHčŐ@<ńÄWľ÷˝ďI2™”ÉÉIŮŰŰ“ľľ>©ŐjR­VĹçóÉîî®tuuI.—“ÎÎNÉçóR.—ĺ˝÷Ţ“Ç\FGGED{#čQ+ ŇÓÓłŹ}óÍ7ehhHľůÍoŠH±X”ÝÝÝ}L&eooOĄR©ě‹Ĺ˘¤Ói©×ëR.—ED Đ?üĂ?Č«Żľ*±XL†‡‡L1݉T*yóÍ7ĺ›ßü¦üřÇ?ľ©rÄ‹@<ńÄ“#+ťťťrćĚéííÝ˙Y±X”ď}ď{ň»żű»2::*ź~ú©ĚĚĚ´ŔNź{îąýg2ůńŹ,ßţö·[ňţ©TJšÍć~}ąqă†ěîîĘ+ŻĽŇ=ěěěČ/ůKůíßţíýľ `µsssâóůdllLţÍżů7R©TäúőëűĐZiŠÜŤ«~–ť##žńÄOŮŮŮ‘˙ń?ţ‡ś;wNfgg%ŤĘłĎ>+—/_–_|QľűÝďJ$‘ţţ~‰D"ňío[ŠĹ˘Ľţúë’ÉdäĉňË_ţRŽ?.ëëë222"~řˇüÖoý–|ĺ+_‘fł)ŻżţşüĆoü†üő_˙µT«U)‹ň{ż÷{rńâEéěě”ŐŐU‰Ĺb’N§ĄŁŁC666¤««Kş»»÷‹ëďľű®ü§˙ôź$‰HnIdűÚť†fS¤ď¤HçĐí_ű°Ĺ3 žxâÉ##étZ%Ë'ź|"gÎś‘ÍÍMÉçóňöŰoË… ¤««Kâń¸tvvĘ·żýmńů|’H$äňĺË2;;+łłłű©¬ÍÍMY__ßďÚöů|2::*;;;rőęUŮŮŮ‘ńńqÉĺr˛ąą)ˇPH†††öaľßűŢ÷dddD¦¦¦$“Éßď—wŢyG^yĺńů|’Ëĺd``@âC"Ńľ;żOH¤vdŠ ‡‹g@<ńÄ“#+ŐjU>ýôSI&“"˘=dĎ=÷śÔëuéččŘďwšš’X,&żţëż.˝˝˝ňöŰoK­V“O>ůDJĄ’<ű쳏Ç%Ëôô´ôôôH.—“T*%ŇŃŃ!ü±?~\rąś<÷Üs˛µµ%˝˝˝˛»»+;;;ňŘcŹIŁŃ±±1ŮÝÝ•oűŰRŻ×%źĎKżtvvJ±X”b±(O>ů¤|úé§-é·»‘JĄ"ů|ţ¶¬ŃSĽ"ş'ž| ¤Ů|tňćw#[[[˛ąąŮ˘DŰű*čăŕďz˝.…BA:::ö›ćü~Kaű{ÜĆDšńÚ_KÔá6 şŻŃŃȵZmźúźë{‘fł)‰DBFGGl$< âE žxň%ť‘dR$ř%;ŃýýýŇßß˙°/Ă“CäK¶Ý<ńäWSzzí~OMń¶ś'ž| Ä3ž< ń¶ť'žxâ‰'÷$žńÄO<ńäžÄ3 žxâ‰'žÜ“xÄO<ńÄ“{Ď€xâ‰'žxrOâO<ńÄOîI<â‰'žxâÉ=‰g@<ńÄO<ą'ń 'žxâ‰'÷$žńÄ“/ "źqüyâÉ&žńÄ“/xĆĂ“‡!™˘'ž| $ŘWŕÉŻ˘x'žxâ‰'÷$žńÄO<ńäžÄ3 žxâ‰'žÜ“xÄO<ńÄ“{Ď€xâ‰'žxrOâO<ńÄOîI<â‰'žxâÉ=‰g@<ńÄOŽ€‹"µÚĂľŠ»Ď€xâ‰'žyŮîŞ}ggGfffdzzZúúú¤T*ÉĺË—Ą^ŻË“O>)333Ňl6ĄT*Iooݬ­­É±cÇdllLDDŠĹ˘řý~ą~ýş„ĂaÉĺrňÄOH8~Řká‰'žxňĐ$»ó×V*ůä“O$‘HČôô´4›Mą~ýşlllČOźT«U©×벽˝-«««’JĄduuUNś8!˝˝˝w|we@¶¶¶ä˙üź˙#Ď>ű¬üáţˇ„B!Y]]•sçÎÉÚÚšüŮźý™üĆoü†lllČ·ľő-ůůĎ.ccc˛łł#§Nť’źţô§2::*WŻ^•rą,ńx\*•ŠüŰűoĺÉ'ź”b±(|đôőőÉřř¸ ?ěgę‰'žxňP#Q*•diiI^~ůĺýß‹EůĹ/~!ŰŰŰň˙ă”@ >źOţöo˙Vjµšüůź˙ąŚŚŚH"‘I$277'ďĽóŽDŁQÉçó˛°° ~ż_Ö××% I(’çž{NţĹżřw|Ťw• …BŇÓÓ#ĹbQ~öłźÉÎÎŽĚÍÍÉŻ˙úŻËęęŞôööJ©T’ŃŃQI&“ŤF%ŹËĹ‹esss˙sęőş„Ăa™žž–rąĽ˙ó`0(ĂĂĂ2::*qŹÜÇO<ů–@ CCC266&]]]Ňl6÷çóů¤ŻŻODD.\¸ —.]’ĹĹEyę©§¤T*I4•`0(őz]žţyYZZ’—^zIŢ}÷]Y[[“jµ*>źOźĺͦ¦¦$ě˙˙NĹ×tŻę6˛˝˝-ׯ_—‰‰ yóÍ7ĺ•W^‘jµ* …dffF¶··%‰H__źř|>ééé‘K—.Éřř¸$‰–V$‘\.'Ź?ţ¸„Ăayűí·ĺäÉ“’JĄöłóÄO<92ňĂţP^zé%éęęŤN.^Ľ(===˛¸¸(>źO¦¦¦$Ťî—Ą^ŻËč訋E™šš’Ë—/K$‘ÉÉIi4ű)¬ˇˇ!Y]]•ééé»JaÝ•yĐâO<ńÄ“›ĄÝ€ńPXžxâ‰'žÜ“xÄO<ńÄ“{Ď€xâÉ—@JĄGłŹŔ“G[<â‰'_ÉdDęő‡}žüމg@<ńäK ]]"ŔĂľ O~ŐÄ›‰î‰'_ą›.fO<ą_âE žxâ‰'žÜ“xÄO<ńÄ“{Ď€xâ‰'ž|NŮÝU:ö_5yd Hˇ ˛·÷°ŻÂ“ű%;;ŠčaHµ*rt¸(]éě‰DöU|ńňČŽ‘hôa_…'÷K’I˙#»]Éç­!FŐŞő»%Ă ţjîßGö–ýţ_Íöe•@@Äç{ŘWń«'ÝÝ"ˇĐĂľŠ;—bŃ"U/j}řâÁx=ńÄ“GF űwOŹçD>lń–ßOŻ- GdňČŻÄ“ĂäA*«‡%Źši—`PˇÚíRŻ+ý˝ŠĎ§Ĺôűˇ\«UÝ7Áŕ­‘i>ßçëůh4Av:hďFŁ"ńřŃtY¢zâI»4›ŹVoĂťH<.?쫸˙ŇhÜýłj}±xž÷î®:ĄŔí׺ŁăŢŤ–ß/Ň×wóűłYuj5ý!iO%ydaĽGuA©VŐÓú2®c p˙rϞܝ%Ý)µ|(tw}(Ť†H.'ŇŰŰúłű!îg$…‚~×ýp\:w±—HäŃöČF ž|>yÔ: ‘ííűű™őşzťžÜ?)—?_Jęvâ÷߬čc1u†ÚĄRąw%|PşčAł_ĐÍ^*‰¬­=ýžů•Őü Ŕ ~żćşď§ř|+ž/łÜoݶVÓ‰FżĹ×ţ˝I±xo÷[,ěX¸©¤öBz©d{˙óî˙PHĎçööŃŹB<âÉ}•jőÁaßďWăXଠÂďWĽGI>@ VI§ďďőrAŠĹ[#îUšÍVeÚţ˝I2yoBGÇÍ)ĐZÍĐPH$bĄ\ÖżK%Ý[ŐŞţą)•4*f_vuéçe@g@<ąŻ«÷t”%T%ń(Ęç·Ďóß­´+ňPčółŇ–J7+ßjµ5âh˙Ţ[­IĄ˘ŃÂťŠiěîjíc~^FýĂçkÝCɤţ¬łSéVjµ›QsÍć­‘tµšE/ͦȕ+šĘ:ĘŚÉžńäľËQ)̆đ -Áök˙<÷ŮÜ®ÖlŞm6őµ››·‡ź?-ŘžÚŰS%ę:&.‘bą|łp P0hiµz]ß{§ŇŮ©†bzÚRîuşßCť®Ů´÷ąR*éuÖj§¦âq˝WîexXS‚‰ÄŃ9SíâO }s7BÚá¨Jł©÷ô°ĽË;I úýŞtkµGąźĎ·v“Ó„ČóŽF[SŤÍ¦*UżßÖĐ%Yt ÷€QóűŐ»ßw«ş…KÓŢÓÓęx4›Şđ›M‘Ť ýĚînUö}f©¤ëAt…ëéęŇ{éěTrÔ éGÖ€őâŃŁ.G5$>L|ľ»ŹÂá›×ľh9l/ßË=ÝOéěĽu´ŕó©ňŢÚR%ß»'|«óě*éjUë4¬MŁŃ:şşĎ§J×E~ů|šŞ;čZů™ëX " ŃÜ)ŠĚç3A"đĺ|^Ť†ˇŮÔ×ö÷ëžl?ëëVGąqCŻĄŁăč<ެI§o.·Ő<ą;I§ďľČ÷0…üňŁ(éôᩢÎN˝·FăÁőrůÎę&µš)±LĆÎ_w÷ťĄÍ(ßɸ÷‰˘ …LÁvvęu¸ű4ĐH ›µďęč8ĽhťĎ«b'"¨VoćΊFEĆĆôł›MK‰eł‡°+Uôz/ťťzÝĄ’ţ.Óëâ: ˝˘“rŮîźČ%Đ?ĂĂG7eŐ.GÖ€ôöŢş©Pđ¨–?Ź´‡ăBnW4ĽWąßÜG‡I˝~ľ§·÷öž$ąń!µÚťQ˙¸5ŁhT˝ţöbńíľç0§®·WG~˙°ű­ŐD–—- hß§ ĽÚźK"qđ~ŽD4rĘĺ¬/Ä…„głjTĐ5>źŇf‡ť‘PH_ĂýÄăş_˛Y˝~ŚO4Ş?ŹĹô5ŰŰjt(ş—ËúúBÁž“[4o4ÔřŐŚÁ‘ ngŹznđ¨Ëáá 0î7ňoň^ďoővMa{{Ş€>/bëN®“TČXĚRC·ę˘M‘Ţ mĘ­>›žÎía==€ČČ*Ól¶5I“b"ˇł^Ą’*ćŃQM­b¸ÝĎ)•4-Ź[°7"Ú µŞű˘§GßŰ×§Ż őď®.MSˇđÝ÷omÝ<;˝ŮÔóŃŐĄ÷ěFG\pŢŁ(G6ąrT­öŻŠ÷ßxčÁ˙<Ĺ\źOßď¦ńÚ+É䝏ĎK)~Xăš+Ůě­só{{7\ őPčŕg±»k‘PŮPJÝăÝž/·}ěîZ!ąŮ …ĂzůĽŃ~čëűúě5€zýíĎ–ţźtZ߉čµ×O¨«” ő}é´ýpX#ŠLĆŇW~żľ7Ôç†á+•ôg Úx.ůĽľfiI÷)9öéQ®|© H&óĺjó¨ĂGáZéˇŃ¬Ůüü]żź'­Öѡžé­¨ZşşnÝ_ŤŢQµ7¤Üv$˘žuw÷ÍϱŃ0Ä‘H«ŃŰŰłÚ‰*?R2®Ôëö:Ň=®„Búľbń`*tŠú°Ů’ …t ÷öDfgőOĄbťÖÖěů|"CCzß{{77 ˝”J­™űćšM3<##f #+ô7›öÜŘ_—;x_vthT.ëźë×mopÍ€ţ,Ô×–Jú˝ŐŞ~×â˘~G±¨…ń‘5$™Ś~oµÚŠ>«Vő3´řľĂ H»~­T*R©T¤ŮlJłŮÜ×ĹüŤŽć5­űˇą˙sŢs'rGÁŃ»ďľ+żřĹ/ä;ßůŽLOOK6›•×_]‚Á <˙üóňÝď~W†††¤\.K(’`0(ĹbQŇé´üÓúO%K__ź\»vM‰„üä'?‘jµ*‘HD …‚üŃý‘ŚŚŚHŁŃ˝˝=‰Ĺb‡%ü9ů«ŹbŢ/ěnHŮd~ü0)ußmřĚćo—FCÎađĘ)ˇP«7K±Řőč1é´ĄC>ŻĐXÖŐĄ µ˝CĹ@ľľ]!fłŞ„•ç `€u˘@Ś˘n4T şý(Ďd˛u};;Í‹Ç0őőµćň}>30€}îí¦öôč{¨]ärú#k¸Ă0ÁžËúşĎ›„ë__7ď?·ű±c¶8I[[ú9ŤˇËŕ[şŞłSŤÉĘŠ"ľĘe;cĄ’EV Đ–—ˇŚ/J$R“`°Ü˛™LFţćoţFb±üÉźü‰|üńÇňýď_ţřŹ˙X~ň“źČŢŢžř|>‰FŁ’ËĺdttT>řŕyá…äĺ—_–jµ*ŃhTfff¤ŃhČ;ďĽ#’N§ĺĉňűż˙ű·ÝGw¤úűűeggG–——e||\zzz$źĎËĐĐüůź˙ąĚĎĎËŘŘlllČőë×ehhHĆĆĆ¤ŁŁCţôO˙TňůĽ<óĚ3ňŁýH&''%źĎK˝^—cÇŽÉĘĘŠ|ýë_—‘‘)‹rîÜ9éíí•“'OĘřřřדÉč‚?Şóî6Őń0 áçIEt˝Ě@xĐŇhčan4Ěđ’@Âá÷ŽßŻďq âOďTü~-ěF?˛łŁŠĄPĐëA™Ó ÖÓcž˛«T÷öôłŁŃ›Ď…ßo”ýµÚÁFČ…EGŁV#±Ž÷Ť ý;»9š!_żłŁßE\űďłY˝ÇŽŽÖµŤÇ­N:*“Ń»{ĄŃPĹK“ť[-&˘÷ÚÝ­ëCš)‘Đď§0ź×ďŻVősÚź3Päˇ!ÜR‹™1dűXĚśŁXL×ub¶{}©”~ČĽjU×/™4—N[Zó­·f¤V[“ŮŮeůú×í`E"éîî–óçĎK±X”p8,‰Ď¬đűďż/÷w'/żü˛DŁQązőŞlooK©T’'NČ;ďĽ#oĽń†ôööJOOŹĽůć›ňôÓOK&“‘ŮŮYI&“˛˝˝}G$đÚkŻ˝v»ݸqC˛Ů¬ŚŽŽĘ_˙ő_Ëč訬­­É·ľő-—Z­&ŮlVž|ňI‘“'OJĄR‘ŃŃQééé‘W_}UľńŤoČ׾ö5©V«266&gÎś‘`0(ŻľúŞtuuÉÚÚš<÷Üsňä“OJň`xĆ÷S±A|ť¶®şui÷ÚďE8|wŠ"µT©|ľ5ŞVőpÇăúYůü­‹áîőŃ-Üţý<·[íŹtşÉÄgW«ßO$b9nWѰ¤°ř7FşR_îY`ô)Úᰮŝ @ őő››źĺłCĄąŇhXDŤ±C±RGH&ősÓiý?ĹńFĂ"rPZÔ ¸V /CŁ(’»Ń"Peě÷ëýF"ú˙pXdnN_čş”Jú=flq H;ń{ę%çΉ<ý´^żĎ§źÝ×§źI=Ťë ą°ŁCŤ0ŻíčĐď§–’Í*j‹g‹Ó,yh(%‰Ä´T*™•Čg P(ČĺË—%•JÉőë×ĺúőëR­VĺäÉ“ňĚ3ĎH2™”ÝÝ] …BňꫯJŁŃT*%===‡exxXŽ;&ż÷{ż'/ľř˘ś:uJJĄ’Ľř⋇ĺäÉ“ňŘcŹÝö¬ůšÍŰű™őz]ęőşřý~ŮÜÜ”ńů|řě¤đűfł)@@Ť†A©V«‡ĄZ­J0˙g'„Ú‰Ďç“z˝ľźŞzűí·ĺäÉ“’JĄn{á÷[ ~öŕÁ÷GšMU‰„zźˇĐí©S|^B>ÇÜbg$r°ňÇŘÜÍXT· ęJŁqóô8ŠĹ ~îFč5ÖÓŁ 8mĄ˘ç‰źř|>i6›-zÝĚ˙ŰuołŮÜ×ÓµZ­E_ßJîČ߇% Ęđđ°Á}ăáţ>‰H0”p8,~ż_"‘ř|ľý˙#Á`p˙3>oťă~ E·ű-í,˘RĄŹ‚Z Í“»Őu‹¨’ŠÇW&í]ÄíĹqÄťrçóĚŚęľÖť˝ĐŽ\:¨CĘnuP!›ü~ą5…ÝBÄţżTjÝ+ˇE=xşu]7j¤K%]÷Ť [{‘›;Ń™CÔő ´)®…Šň1EXíŔmľ»UDĎg éRŔаîkk%ąEÖÝď××°Ţ>źł“I­Ypɤ~Nł©†%™Ô˝T,ꚤboR«*E®]ÓĎh­]ŻŰZşĎŰm¨-—ŤBfuU˙źĎ>OÄ-Ş· zµ»»[ş»»÷ Ţ·O‚Á D" …B‡[ôx»îuőt»ľľ•|©aĽGA8T÷««ůVBXü ÄťŃ|'ŇÝmFăV3ť)\F]ăľz˝ő5xÄ(~w\Ăr«ŽbSĐóąĘ´Ś{˝ˇ˝¦ť‘ő ÍsZ«)E8?:¤Ýи×׍ę}ĆHáť?ń„ľ‡Ďjďç1ĚŕF66ĚHů| /%żßlšwčµD"ŞLŰä25lí=.Żhôp ű˘żßşá;;µ=4dŃűbkKżŹú—[¬?vĚú7čű€2ĄX4'§»[żgęR °7Ö×ő}. &,˝{{şv»»­kAŞŽgBm*‘°úVg§Ńž°Wx¶Ł(žyŔâ÷ëfeČĚ(JîâzÁhçŕBy“Zp˝>K1‰AđÚ‹ĹÖf0R‘°ČŢ)ă-FÇ5Â4‹!ëëú} †|>ëŚ^Zşy˝67〠…”"ś¦µľ>˝ć{í­Ś“ϧckËšü-˙îz˛±Xë= ­éHŰŰj”ü~+psź~ŞĎĆ}®ě7 ±îµú|–š‚ÓŠôĚť˛/X_ö5HÄ"«tZ×G‚z ëÇýbř@áĺóú{˘-7ŠéîVC«/ăŹqăqŁSš™ŃźQC"uЎ8°ą©{§RŃ臽ŔĎŕĂBW¸LGµ·ę÷8,•Šá¬%9ęC–n'÷cÎŃp· LŻí”á‡!¦DL R„ĹÓŁP[(ÜzRűˇdµČáű«§G÷`ˇĐŞŘŠE}­f5†•ËVŚv›Ö\/™"n.§Ż2ş»«×_©XU­ęż1ĽůĽ*ĐĎđ+űkł»«TI†ĂŞů\bQČ™Ś~<†;9uĘÖw{Ű(6Ügă—lVŻ ÄȨxÜ굚ľ(/Mű3˘6@:‡gµ±ˇ˙×÷qîűđôłY˝·ÁAçRř&ş>¨FG”G1ÝŤŞAĎ…Ăúďž˝˘EŠńᰢȠ1Y\™šR¨.uj5ˇ^ßć¦F_~˙Í0éŁ$ŹTânŠŁj‘A˘$Ĺâ#€t;cďôőČýJŻqČş™ŠN ˝»›âÂUý~óěčoč겔č®ÍM{? ©{{†ĹŹĹôĎA0f>‡Z ˛Ľ¬ż#ęé겙Ů@`Enž´‡3T(¨RŮŮŃ´–›:aÝr9ó´©uđ™Ń¨€Ý{ŁfSź=kż_żłV3ôQ”GĘ€ŔŰá8lQA= #s+%x4ÖĄĄ˙Ľ”Ţ.Et»¸ôÜ"¶~ŐŞuĚ"¤„Ąď”,`qŃ •®šÖčJµzxŢż]@ʵ )‚Ž=đ|)ňé®4f0x.nʦ\Ö5sź5RD(ńńq‹8\’?wf¶BI]ž©JESb"6 udDď“|xOŹĺő‹EKź@ŘŃa0ŕÖ$نéw#¦ÍMŤ*©JЇ~ öôŕ Ą«RĎĺô3(vG"ŞWWőO0¨×ľşj÷‹aíî6BA"ÉÝ]ŁŮŢÖçB ĂźĎëÖŮçÓć=w-¨­Őëjd‰˘Ů/ěA"ŞHDď…Xˇ`ě˝°“–ÚÚŇź±Ŕ‰EZ‰;;őą>öîzŘ?ąś–bŃŚôŕ ~ÖµkV+:ŠňHĄ°\|8žÁAM_xw+(ŠĎÓ r«D0¨›…NWÝ @NnÎß©ŕĺ&nĂ•»~é´Q¶47ᥢTęu=dtÔ®®*e„zVx®`ń]X,řýpŘč¸;:Li»†Ťk8H\ďĚý™ŰA Ý\tgşüHt@ÓO3[±¨Żăpęúđ™4ËQxćú ůzâ„][G‡A=€şőx\ß—ËiľOšČç3.§®.{ľb1~ůĽ*Ż‘űlR9››«6ę*ĎpXŻ‹ĎÂëwé)éę2Ţ«PH˙Oú…sH/O(¤ű ăŚW~+„>׌Âfműűm߆ňűőďŃQ#’L&ízˇRgş#Ń= ŁŇi}}OŹŠőuk@ôůĚx¸ÜZ##z^Ů—DV ă |ˇ‘˛XTXŻ«C‘Hč=DĐďXY±˙ŢóR)Ň^Ŕ;¬;ôČť6°%´ľ’énş’ÝÎ`‘›óńşZM˝˝öoÎNĘÎŽ*rŠüÜ+©˘ZÍö‘Iw·ľ?0犜: Q!  }íŘî§ť‹ä÷°V đr&&,"K&ő™stŻÓ$ŠóB4s”GRą":Áëx+¦HźqP?Á˝t„ÓµšËµ{]eçĘAsP@w#îá±Â!Šô ˇ{–÷G"¶!Ăa3¬x©|ťŇxz®Qä0‰´ůńŽ@ÇŕU¶.ĺ˛^4Ő¤ˇ\ŁĎvsÓ¤Ű$‰rŕ`áń3¶TÄĽ}(>\~-ę9"ƦÚîę ‡-âŢ#i1·‹x}]Ż˝§Çŕ|XwUf„H˝e2­=#%‹ÚcrţĽ!ąHOANʡŁßEýe2ú ĺ¸n`ĐDr•Šw”0YŚ‹Ű3S*©ÜŘh}&DP¬çŮłŞH!‰0z’xÜÖč,űł»[ż‡ÇÔ”ţýÔSÖßŃÓŁÂŇ’:J/˘ !µšůW0Ä ŁĹż××m0QďęŞő#őőYß ŽŁp‰†»şôYĎÍY”´¸ŘŞ›ÜŮ#ׯë:éÁ‚|™ĹEŽ AQB-ĐŽĐiW䄦n™&&×›ĂăĽÁµ×îu´i<~sqťű ç 3(ć‚|‰Ç ťs°ąßpXďĂőÎ((·ż/1…â~ľ›ÚCÁcŔđPݨĚ}?9móQŢîĘd §Ź’ á‚gĆ,¨Âa»Źr٢(š˛(^˘»»Íă¶Ę÷Ѝ"áz˝îDBF $™q\^Öó R ±1­Á„B­ćBÁPpD¬Ě)1ş҉++Ć˙• Ś·¤č¨_ôő™łQ,*x%ť¶(EÄŚDOŹÁl×Ö¬G˝”Č(‘°Bx0¨ëÄltŤk5Me‚˛»zUżT)ĽtÚŚOg§=ÇŁhDŽś‡/r0|­ýgĚ5pQW`±‘zÝŇ*w‚§Fi´íďF8Ô‡‰›g¸ůŮŽóžÉń#äbę'9ł‰”€Őnx­;č‡:őw-éÚĄčŤ7Ř!Çŕąťă"f(@ E"6ąŤëŕ€ÂJŠ·HZÜ>^.ĎďKIIµĂ'Űé.r9]#rŇ„ń]]şţ'NŘ˝Âj;8ŘÚŤL§yG‡‘Ó‘v¤Ă8‘¸™t…NÚďďńÇUˇ ýýíu{g€;»ł)B!KéЉĚňLÇĆT±=¸Ď‚÷,.Şň2úŹpŘ ˙@¶“I˝7:ŇjONšáE©îîę3˘XÎŐPHN?Íôs@ü‡A j)•ěŮbśD4Uóî»úÚxÜębŁŁ–rÁKKúýnÔ‹ŞŠőŁ’kxXפ·WŻ…şW<®Q€[hÇ©`°S2iýťť–ŽŤFuď¦R6$ @ŤčŘ1«ÉZŰŰÓh"źątÉ Ű•ŠČóĎë÷¤RşŻu=Ňi«ăĺr¶÷Ăa˝~ś8 ×zĚĽʑ-˘‹b¦HH~ň0DŰöt•K‘ĐŰkM†‡Q\܉ß1Ąs–ńüańtsč($ż_7>ý„Ń‘H+}´«ŔQ~|/ŠÄżżuÎ@{–¤7Pú@‰|\ Šż.Ű-ß56ÖZl'•äRŹS "çŽ!jÉ!˘8/bÝÜü].›˛¶NrUŠ.šĄŃ0\˝{îXz č]iovôű-‚ííµ‚s:m}9ší¨!@˝Ní‰"şKaÂw đÖÖĚ 'úEĺv]COŹcDˇ×±Š¬¬‘1„#ő«pŘ”čđ°­ Ţ7kż˛˘űlcCď#źoíÜĎç[Sž ť¨?‘&J§ a59i†(8) ŐD-ÝÝúÖž˘u2©F·^·ŢžŤ Kaą0ßBA?ź¨D™ËË–^˘öčSĐ•¬7ľJĹRŻŰŰďîV8ĎitÔtTż5s†ĐŁFÄčáŁ(GήąŃ ťöa=íB±ş¬ź®;%´w……+‡şŇôÂAůáő&ŕłP:tßâE‰¨˘ů‰şŢ[€ń“©{‡ĺÜIçmoŰďČ˙noëçąM_—›u Ęk§+!ĽwÓÜ#EJ"?R1tR»źAD@ń™ű`-IˇŃmÎĐ,±âzäręéŇŔUŻkj4}ŐÜ ˇ‡\8M‚¤¸¨á` 9đÔ~¨đůůĽţ¬P°”Ϥ^ןçr†¸bOˇPÓi«Óń3 ëxăě} P_ź*Zj1™ŚťŘ¨±ĹbĆ[Fý‡FQjMś5`ć(úú4]C Ц<˘ěŢ^s‹B1d‘>‹LƢž+uęuÁ 5€¦R6*–Z óĎYËŃQ˝ĎË—uŤ”ŠEKg^»fµ-ś%"jqÔ<¶¶ěß Ô†‡Íabď|ú©ţdŇôLÔ"6l*—ł”j±x3MĚQ’;šHřEÉ‚ttôI>ß?Đ ©Çh_H‰ëˇ“Úď:ôÉ.\‘Nˇ`; ”DŐDP|xp4ˇa×ÜŰł<)=^žł›>`cĺž\äEÄF±˘d@¤p°XWw vđ ©?Đç6;ÁńĂ3éFŠ‚â7ĘŹô"Ń ąwżßĽR˘—µÖm„6Özťşx˝®WĎ÷RX %;ye )Zóp„•˦G”RµŞkĎ÷¸ý%Ôvw šM/đYŚX4jEi7ŤŹ<3ökłiF>6j‘bŃ"Ý@ŔzO$ ÔŢ~[˙ʞÂXěFőZI=±W¨gT Ĺâói ‹Č0‘0öâJĹŠíě)ţtvjÄpőŞÍŽŮÜ´š@4ju.j"33VśńEg9ű”4Q"ađmfš4¦ř‰©sííY dś;ňęUs GG­f™ËYou—JEŻČúËî®0˘LŘ ¸F¶ë×őó67getÔ&9rv-3Řg8|8ŞP¸Ůk7î6¬ąB4…ŤĂAA!’ćbóşh$˘ `Ź;5›Őß ˘>'7Ň" ADŰ)!ź‰1± ĐD»ő ôŇť‰A>™´†´ÍM»w §ëÓi{.ĚE÷ÎŽŢźË÷ä~N<®ß··g×ÍMS°(#őJ××m$-Är(JRDťŔ-Ý™%[[ŞÜQ~Ôr‚Aëć¸Ŕ  LFW©čÚc\fĽZęgΨw‰čĎâqýŚímUt4tş3Dř<ŚĹÚšyŐÔ\šMŤxž8"818Y|&  "wnÇôtëţI&EľúUk¨ ÍآiEĚ5ełú{Š÷PË‹(L7Ó(fßžý~'ň2‹†4ŤK´ŚĆäŮ‹Ę ŔĹö¶î+`ĎËËznqô˘Q;[€ôúŢ}WďwyŮúSčVÔçJŹSł©i¨‘+žł_vwmč×ŢžE:€ŃüPß6G’ýÎţŤDô5|n&#rĺŠM1ńůâ-‘Č–Ś^«+( fA»ďs xîpę®·K„@1–ĐžşĘËĄZ@¸~7jr©r€R7Y_7Ě;Ś”ŤŰ©ťĎŰáBI±°P°ž đÁ`|ŕ9"Z‚â¤łÓ ŇÔ)°¨á€$î׍Z‚‰k\' €FËlÖrÚDRîZWu;s #÷ţ2;(Ż]Ş<Ôőu}-\Q ěXspůɤ¦APbp$qŻîě Rýý¶źČWăő3ăamÍř¦Ü)µš>j>»»¶ž¤`ŕĂ"b‚—”iłisHzz i…3´ąiĎ€)ţŹ* ĺ58¨źEZošë"2 SśµbZ_­¦{ˇ§Ç Ćóó­×V(XjŘ*µ˝în«[QçPĐŮ©ëNš•É蚬ŻëýŃöůôűęuÝ 8Ąőş‘0 rf¨[ŕ„‰ŘsĐ2;«ďçwőşEšŕĐâüZ˙@˛˝m„ٙڱ#Űy™•ăÇG%>ZČ‘,˘ŁhH- ´ Ľ‰ÜÜ÷ĺ29Č\N7ąád¤ř <)-Re®€|!„ć˙ĐZsäˇ.p Á.©"tŚE!–JşIQę¤aVVě5n70×ä¦íđT‰P,tćR÷±R(ču¤RzPˇËpY7Ňq"RVV(Ć»c-H ‘z"śÇ+e¶D$ŇZŚ um––ôpşťŐîó!2e÷10  :ŹŤ K;q .ź‘»'Y[D0h(*·řN/ÁcŹ™rO$ô¶·mo¦ÓzÝ}}–R"Rčď·Ć5jXÁ ©‹E‘Ź>yńE»6ÖaoOy_źÍ-!r©VÍůŕ{p  ŕ BBI®Żëő÷öęż#ë=hR(čďOťŇďżpAż7צ@˘(ęÔĘ6q1`ěuΞ 24ëOĘ•ćNŇEśMŕčd.ž^ź=EýpŘ@ éďpZ©űQ;˘ËG-Ö˝‰č>#+±±ˇĎŔmxě1lŹŚčóžnŤ*ŹšI⦫8xîaBËâŕşŢůAĚ• Óßłâ'ŻO‹ĎÇsBčNvSI®P$§PLQ—Pš&<Đ2^šÄPž(+ş®9Dnç8ĹŐÁAët…ôß@8űűőŢÝ\ż»®É¤~7ög /ĐŢ^+<ÔĄ;Qoľżßňö¤iH ĐđIš°]SĽtŮsÁäÓ}ľ´dŠ•4$}¤ ×Ör o”{ݤI„ĂzźDE0ÉbŔŻ_×˙wtčwW«j¸p^č¦&Z •đďÄ ýŮđ°®w·1ň..ę÷ŤŽZTCJŚFDh,Ü+ĐíÁA‹‚‰B$"rú´:¤Ž0|DŇ8 Vo‰ĹÔLNýÍň˛9Ô*HóÝ@Ą/^´ž¬hÔęU Ňi]ççź7č2×CÚ”~ "c€˝xlĚŁ1ĐĹŹÇuŤWVtÄ/ ‡ůyM9rmDę¤1''u­NśP's<4¤ű›ĆŐ|^?ďüyýýüĽń‹-/[ŐŰkéżXLż'Ők$ęRgaÁz”p¤Ž˘ń9˘)¬¸AL§[ať"Ć#DŃ… KšçŠ‹’Ęf[?)6?#"gM˝ϡ˝HOhŢ^śqăŇY¸¤‹x0 U@$qÍlx·KÉfUŃá3ë!łń˘±X+ť<Ť{8® ·xޤa8¸źźÍZä‚'Š·Čó¤ĐÍڳ޼ŻăMźi|n1žÎe7÷ͤ@Ľe˘ Ą‰„Ą`H,/Űúrš‹RíčhŤŠ8@ĹNO 0tŚ/”)4:öôXs%Í“xĎ©”®FCß‹™óŔDD7âëî¶q ľ»»–G'„Ă-*q. $bÎ` î›ôeąli<öĐ™3z} zíÔB0` Ť@(B;v¬ţî‚"{@ÄM‘}`@żżŻO Ď–”ÚŔ€Í_§Łž:$Ń3éÉ“ş.ś›ĹE5…‚FO““Ć‚@*—č ä×ƆŢ?,ĘÝÝz=ýý 0 `ľ˛bŮRhÔHoLÚěÚ5‘tzVĆÇ˝"ú] čkČrë!.uyĹĐ" }±ä`ÄyŹŰ}Íá Ř^«éÁ¤«†ĎĽšÉÜß“ŁOĄlrµ<Rí´ îd7č¦A8ݸa“ę\u„yvCŐ´ 첤“ŕX$phŕ ĆLŇ{(ë0¬"V E)0TéĘ[[Ô@Ç´;h(¶űĚzzě^××m-/\°FDU<##VcâYsčE ńF:eĺň\QÜ/Mloë=@+al¬ŐŃ`´+µMŁŁ–’‚Ť‡h{[ŻirR?hp4ŞŠ+‘°6Rĺ72b)9”8pŮ|^לý=8¨ß98¨ëxń˘E­ťťşßňy} ×?:j=C˝˝F÷ţÓźę=ÓĐé÷ëkűúĚb’áÔ”­=‘HĄRI×`mM˙ 꽤RvOëë “i5°™ŚČ'źhT@ÎÖ–{źO‹˙8.;;úŢ“'­ÎFjgGźóŢž~Öđ°r}ĄRz=ssj@ˇQůč#{îÔw OE€†ţś˝=«}źȎ@P~E3”Š;ĺŤ~6ÝŇ䬩׬¬ôĎť7@ÔáťEl8ݬx)PM»ó>đŞ]‘ő sš†:R:L‘#ÝÁÄDR.¬ îLĆhÂ1,‰¶řťßohî„ăfˇ{DôóˇĆŔ`€ Â`ďě0‚b,ôęˇ^•ÉáSW"őł˛˘‡Łxů˛E(1w–;Č/jQ¤čúúL!Ń$Ćl RxéLůo]âŇ[Ź·v]cŮotů“Ţ›źoĺŔ˘:Đ{dͱc˝lmYZ %LSČ5®9ůńŹ-! 8„4f˝®Š%Ěů1PÄîklĚŚ~oŻ~'&l ŕ€ĐÂÚ‹©ńa6(˛Ý]5 ś_‘Ź(#{ýşiôÇŔ@+ŤŮ8č ŠđđlQş|ŮRͤâH‚Śh”éââ¬LM=)‹ňłźýL‚Á Ľúę«ý"ő( âăAR€ĺPD§k´}ÂŢ EqČŇJ%=$xyđɤy^|?˝ "ú~Ľ xxhň˘G¤ łŮ¬Ői$ëďżyRŕƆ~6Ţ9˝(ŚŚyo™ŚAiÝÎő¦ăEť¸ÍQüž” ^5Ţ8} á°*@SütÓZŞRŃ{§W »ŰŚ- Č&T&ŔUžyqh'‚Aýwż§©Űܸa QŤ;kcDGö䤮űÎŽÜA‰čzŚŹŰđ'x«PţŔDa #EK´9>n).ĽSG)Ţô‘‘23eňöÇŹëuŔŘËÄĹEŁşűHřŤF R,š˘†ćŁ\VĹF}‘N{¦ z=ԜΞµh€(†ž˘­-s€HÇ1#Ł·WŹ!ĹŽ:Îđ°®q.g¨aŽŽę^ü>Né˱1˝·Ą%Ku€ŁŮç˘ŮÔ¦A¨Üŕ<1˙ťB·˙ĺËzýҵ››úúłg ~],Şˇ+—µ'Ą·WďieĹÎ$töGQŻ˝öÚkďż˙ľÜ¸qCŇé´AÔü‹›Â"w·K~Żn_< \í="­¬¦ëŕ}"}ÄçÖ놢íäÂSÝq› “đbAĹ^ŕçüŤ×Cg/ź#b {îTÂťSŢ(vP3`ăÓi›±°łŁ‡Ęl·Z«YŘďvĄ»3†`{»5  éŤÉ~}>ëzęKÚĆőI˛tY»đW ”?ĎŹŰŃŃÚ zŽČ…ô ŃÜƆz¨D'[[úsG~H·¶ô »TŐŞ­k2ió/@Ě]ĽŘJŞI_ ô&4̉X#k€‰„AQ¦ Iw ±4Ný €}Ćö¶*@ęJěҸŢ*¬lV_GDŤMČö¶*ŕË" JŃhŘ@1"ÄBAäŮgmoôô¨RĹ`íěčĎ—–,MJmg%ŃšŃ9µ4 ŕ¤KaZ #rĹx\Ď=A4JҧłáRĹs]nź{ż_ן®őáaűKł!kË ŞFĂö­Ýłé1ç}pPß·´$˛±q4#ŔkŻ˝öÚđđ°ôôôßď—T*%=w2ěűk@°ônţ›<ĹJĐ@n!$F!är­#(—rX@ŢÎW*ęŮŕÁşŻ¬´Î}Ć;¦ ŽFĹbŃŇD(éÍM»—Bť´JÔ›Ś z Żžđ˝żßş‚Áľ‹XjʍĹŰÓc丹@SťŰoAd@Ť‡”JŹ´Kˇ ˇ;t,~ż"G`.Ą~€QMD$r†LryµHËA17׺ËË9ň>hp€F»i¨8č1Y^ÖĂŹ ô˙¤ęđn—–tMhŘ#’ąrĹ”ĎČŃŕL¨Ő4 HÔ×lŞâ ™‘â1{ Ď?“±ć2š/‰0˘Q.olÁĹişqCŻ#™ÔôU$bÍm¤űúlÜlw·El×pőŞ®ÉČ!‹ęu«qÍĚčwŔ$ë:e¬]­fTňé´ćůIíQX¦Ćué’*RxíHmA%ĎľeʤKÜČľ$µIMeeĹę CCť'}7?oŕŇÍîŕŻĺes0â©”A°©s®Żřϵ·Wvů˛:4WŻęšŽŚŽ,0gšĚĘ©SGŻ‘0đÚkŻ˝ć÷űĺ?řĽóÎ;’H$dzzúˇ\Ě‚ôöŞÁŁ"}J oš®YHéHÇ€řał¸UF¬Ő%´ÝÚ˛Ć#hŻÉÉăÓÁ즒ü„ÇBţÚMŁ1Â’śý'źwŽ"`€Řt÷ľ@żěîš"w)Á9€(0äÔXCŇ<„Ä…‚*ä®®V´Q]đPWtvęçE`L1(ëëVÓÁPąťóŰۆ4Y_7VXŚ"pĐŮYC!­­™×[«Yú?ë@DE}"5ND&c)™Ť KqĄRú;ú1H˝ ć‚RÖëüŮĎTá&“úłĹE›•7ĺŃQ©¤ Ă= Ď›nqýŚTJSZŞlq˘¨aĚÍé÷Ń|H¤G- ľ'ę KÁ6 €gÉf†×ĚÜvŔ,¤‹™±Ž±ŚFőÚ†‡ő\MLč>f”/F2©ë|ţĽöŞ[%MČ9]YŃ×1§śşž;ć–âz­¦krěÉÝ]ăŘZ[3`<[DŃШÓ'58hig˘ĘRI×Ă…š“ZëíŐ=°ľ®kzů˛‘O˘‡XSj§@ŕˇĚ!%Kú”5™ť Ž&Śwżľ˛˛"sssňĚ3ĎȨ;0ű ”ŮŮńűűd` ľß7Á@˛éí5 MÔ@ŘMz†…GJŞ„ôÇŕ ˇ¶Čm»ŤČS;ŔËĺńđۡvŔđˇ <9HŇ ˝ˇÜ Ń)CN‡—{"*â0łnôÝ”†'2H$‚MZĐM (¬Kťţ ůčLFÉü ŘĐyĂѨ*šľ>S x`¤óÜž Ň3++ú9ëëúYcczŻô áŃąWYŚ ‘ž[‡pÇôŇ2ŠYëăă­Ţ"D’Ô,F˛jaA×jhČ:ţS)‹˛{zŚ´8îƆziúËd,u‹éZ“ĺ{I‘â„fž\==ć%Ă4Ëä=ŇŞt[SĎ ş$ĆŠtS2iő)cDÝÝÖHX*YŹ †‡×Rd'2Ěĺ,J"’i4T9—JV˙8uĘŔ< RwŰŰćá?ő”ž§ˇ!Ë.ĐGăö·Đ‹•Hč3Ľ~]ŻoyŮ \,f͇ôt`ÄH—±ŢĄž?Ż ž™Ś>Î:őŮńqKÉ_»fS'c1‘••Y9qâčE űíd‘HDĘ岬­­=´‹ˇ1MÄţvX©č†˘đěÂfń„ב‹Ą[”ôěşô8€Ö˘G¤Ň×gąN˘Ľ˘®—b·‹ńÇę’Ná»QÂô¦`ČHĎqßú]xÁo™ăÍ$„ćÚ ! â €Â@qťD é´yX++–ęY]µâáőë"Ď<ŁźÉ|h˘'-HcXşşôŔärć…a ×ÖDž{N×ypĐČ IqqżŔQą7P)ăăÖě1Özş™ëP©¨‚doa¬XŢ‹YZ CŠÂÎd¬čNDW—+†)­Ż«Âtő Žůy ő[[ć%»ÍtÂ{íš®1\_çÎRľ5ú(žzĘĆŞâś ™!2ßÓŁi§ ,˛ĺ<0ŽyxŘR‹¬m"ˇé,(l0şśß“'-ZőűŐˆPIЬ[ˇ ?„îăÇősü~u"8źłłŞ°źxBßi@Î[>o37ęu}4"č"‡ ŚUăŽÓCMC çÎJ&cŔ †RáŘŠ„ăÓOőßÓÓşŽKK:äěH÷4›MY]]•\.'_ůĘW¤ć –ĹʼnĹú¤PËÚZ+{*p@”­ţ@;EĚĂĄXč}"·Oý4ž5—oň>·{š”P{óžÍA¦/€b"é w ,yk ńîô5“I˝wŕ‚tÜS‰F=Ţ%9m))fP 0[.ëummé¦ďé±´d†‰I©%“V°_ZşąŮ“ŢľWD?#•2X%ĆřĆ ý›”dµŞ?Ł7‚Z@„pX•hżE}(u .źÇ÷qý±1±şcj!“ĽrĹR @Pq/¤qööT‰T*‰ŤŽý Ń©BzL`ľĹs‡€Ů04-B§’Ď‹|řˇ!÷vvôý©”ľ®»»5Ą[,}F2ipu7˛±HŐĺâęî¶!]¤#ˇ2'ŇâĚ ęó`Î:EřDB×b{[÷÷ÓÓŁ×54d{íÎŽE.8+8aW®č5ŽŚ¨CC ű-ďYYŃk7˙â˘iB!‘źüDŻwdDŻatTżcpІ:U‘†#ÝF´„#Â2&Xú|zť¬Ľ_4dşŁÝÝj<:;í3çćfezúčE ×^{íµwß}Wććć$ŤJ2™Ľ% «ŮlŠĎŃí˙wvĐďnőy Ň߯),ę««­^Ś yZđç@k9ä ˛H_A9­„K…Â!˘IŚÜ53®ˇ*§8IĎĹívÚ—ČçůóCugn¸ł)@±ěí™Q+•ŚbD‹›¶BaŃ˝žJY8śNëAůăݢ„1´tyëççxíÔO §ż0|aA˙O Ťg´¸¨ßĂę겑¬p[MLŘ€ťbŃčÇC!ő ˛âĄRűĘĺěuŰŰiuçkăQ»Ě­0S·4Áľ™FŢź~ "UĽkbbÉaŁT)Ćž9c©•˝=ýsýş\ ˝Ô„Ęe5CCşĆ@¤çćZy˝€ëâT؇[Y€‰NłY}Ć##jŘ––ôłOź¶â0Ńäĺ˛^»K%D'uCĐh¤’Řßš»7ÚśŤą9C9NLÂe\q2i´/5}5:j}\4µ./gFúv ţ8JĄ’Ew4A°WhřŃë7ăL#")+@<÷µµV µÖÖl?S‹ˇćą·§ç¨ż_÷Ăô45Ú› ď—.ľ}} I&“˛˛˛"±XLžţy 2üĆŤňúëŻK(’) ňć›oĘ{ď˝'ÇŽ“˙őżţ—\»vM>úč#YYY‘÷Ţ{O’ɤ4 ˛¸¸(•JEľ˙ýďË'ź|"ׯ_—>ř@ĆĆƤłłSćç祷·[‰Řgˇ­o?‰˘ă‡Ăşq‰"ŕf‚Ç(,Ť‡¤§€đB_-˘Ż˘ŠražĽ8ŔQHlPR=í,O6kđ]şÚڇKp×Ůiďb1Ką“Ó¸\Ö\ÖôŤMŔ14(е/ÚűůŚť5ÜͦM]›ź××ĂÉ|#ÇĚ„DÂĽ«bQ˝ŐtÚ(5X×TĘ”ŕŇ’ĄO ›ěď×ďI68hNĹ}¦ĐÁrŚBčëłh´ŁC9rzO¨!QŔdJ" ĹM— Ť^Ňąś~?Ćkk­üj×®Ů3‰Ć$ĽcÇZ§A˛Ď1|ôŹ|ü±ţ%ę :wÖ Č\u8Ľ66ě\ŤíčĐűOĄôýD»««Ć) ZÔ43Ł×KĚ Ś±1ŤFFÔŕMR#2ç¬ŃPĂJię J˘jN.Öć¦>ú1čŤÁÉaßAŃĂ "1‘îę€ÔBŇiŁ­‰D´'ÚŮö¶,Ć"-ĐCTB-&aśXŇWőşhnmé^eB#9ß›N«±ťśÔtYłŮX¬)kk7äر±}˛»»+˙÷/ËËËrüřqůđĂĺő×_—ááayçťwäÜąsňá‡Ęęꪼűî»R©T$ŹKµZ•˝˝=ŮÚÚ’O?ýT~üăËúúşüěg?“p8,. řm$("ŇŐŐ%ßţö·oűâP($‹‹‹ …äěŮłŤFĄŃhČÎÎŽüŐ_ý•Ľţúëňťď|GÖÖÖä?řô÷÷ËęęŞ\ż~]ž|ňIůÁ~ ’Ëĺ¤R©H*•’L&#§Oź–çź^ …‚üô§?•žž9sćŚLLLě#$ౢů ď&ź—ŽŽ …BR,ĺěŮłw4 ©×ëâóů cÖÖÖd~~^ĺí·ß–®®.ąpá‚|ó›ß”X,&ĄRIööödllL¦¦¦$źĎKŁŃP($O?ý´ś>}Zž}öYÉfłŇßß/Ź=öäóyůęWż*===˛şş*O?ý´<őÔS’L&Ą\öď+-Ź!G4–Á“Äá#íEç3u ňŮĺ˛yë”ë× i“ÍŞ"ÓďYX0=•2 éëΞµt%„[‡‚Ęx( ĄCt•HčAUBZ†fUj5ŁŁ6‚ľ) ŔŞ$††ěůb@`D,E;3Ł{djJ•ł:p*R)+ŘŻ­é=CH§;E<®×… ÎK,¦ëT*Y ŽšdŚ4ŹB{B {Šú\,fHBRZ‹ţřckĺlâh–ÂÁ`¨żŁŢ ŠĽń†~ď㏛ăáRmnÚYv GAG‚~VŽŽ ŚŚ}ĺĚęŞŢ Ŕ“z]ÖŐ%ňÁú}O<ˇëĐŰkPبÝQ7Łá0 4&Ô’Ĺs©Ő‚ŇŰ›’ÁÁQ)•22==¶_ÉçórĺĘ ‡Ă˛µµ%óó󲳳#ÇŽ“ééi©×ëR(ÄçóÉ+ŻĽ"{{{R˙¬AlrrR^zé%9yň¤Äăq©ŐjňěłĎJą\–Ç{LNť:uÇÄ×lj·Â›oľ)/^”ßú­ß:ÔU*Éfł’L&ĺňĺË2>>.Á`P˘Ń¨řý~ÉçóR*•¤ŮlJgg§”J%éęę’ÝÝ]‰ÇăűáWR©Hww·ř|>yűí·ĺäÉ“’JĄZôP¦ ,Ü8|<îXOĽb63xqŚP]:‹I3"Ó·łc ›.Í…›¦ Ae¸łÓéŕ‰Ń8GĂ›Ť|ýĘŠ!uz{ŤWŠQ¦pUA ěřd&Łk3Ëmăc訙ŽĺYC˱·g…mö۵k–Kg {H7y}x™L4ťË™Á\uÖccC•·ß݆ś™ĺxË##6‚•ó˛íî6 ťŢ^ŤAŹńs"×Ý]í{yć9˝µ%ňóźk7;ýôĂp¶··ŐĐolX:¶łSĎáä¤:IĚc§Ć ýMî 0ҵ8* ď¨q6iBÜÜÔ=K# 7˙žĐ×ŇL¸¸hťç?üˇéşŻÝ /Á>ýô‡ňŇK/I×gVłŃhH:ť–h4*ëź±“¦R) ‡Ą\.KˇPZ­&ťťťR©T¤łłSŞŐŞř|ľýĎ)—Ë’Ďç%‹ÉŢŢž$“I ¶Ďz¸“äťwŢ‘ýčGrúôi™šš:řĹ€Äb1 ’JĄ¤ŁŁCÂáđ~ć%ŤJ4Ý˙w h4ÚrQ‘HD"‘„Ăaéěěl)˘Ó‰ÎÄ3€ňŻŚ\řÎŽAÁĐ»t"şIíg4ňľc™ĹË1/ÍíĹ ¨ ˛‚<4Dn}}ÖńĚaŁß^ľ—4–Űqżľ®J†–ÍęFľR%-?RµŞ‡Ť»ŁĂŚ„†××Ué¸ĘkqŃşÖÉĺ2Ś Ôi¬őuű°[ŇqŞ‹hnś>şű1Ú —Č©—Ëz` Őŕđá‡Vđ^Y1éD7íŇ]ó­zlVŰęę˛Ć­±1U.[[VŔ¦ťîqŤ(¨+ą. W jhřN¨ÇxŔÉE:/•ҦĝC.ѸG]Ť(g4u@ž3“aÔ%u—N«Ń€ĽŹôć¦PőL-â ÁPϵ>›ĺetOOëÚ..ęúÁ€=6¦{‹ľúR`:`;)0uz_xôĚŃ”c.ggpuU #şş |13cý5€,DôĚň~»pX÷č쬦îÖ×­Ţ98hjeĹŚB$˘kŮlÚ`)¦:ŽŤéwâ0 »÷­?T9ÇŽY˝=öµŻé}ĎĚčç•..ÎĘč¨Ń}>źDŁQ …BŇÝÝ-ÝÝÝ …$đ٢Aéěě”X,¶ŻgCˇĐľîExťÜŤěkőÁÁA)•J}¬]„}"kĄg`rŇxଖůĚKYß,•tŁ3XHÉh•Ad‘â BHHϡ4…ąÎNÝHůĽz;L§Ëĺô°ĄRz°hŇ#<¦Ŕť(ŤńqCó-fŞĹ:8ˇhsi«“Iësqa‡^c{!C„D’4M(¤›÷™gôßëëF㱳ŁF.÷…B»v͸Ăh°K§ Q$bô#}}z(‚AM7­­é5ŃËéá˘@őH­fĘđé§ő€ĎÎęőˇŇS°±a\J®ˇQY]ŐĎzňIÍé}ÖďşqĂŇd>ź"pNźÖőAqÇbş.ÇŽYĘçd~^ďmlĚhOh|Äx€Î‚<ó±ÇZ‡PAŰśxyYä•WlröÜś*3gt-ŞU˝§éi˝źü@ë .eG"Ń:Rö½ŔőşˇŠ0¦îU«űEŁú]nĆ… ş6Đž‹–Âm6ő^ÜZ&iŮ­-k]X0tÓ‚‘u⨸#~éÔ稥ř|A'úyă ˝.žĎ §ĄłSďz˘rYź}OŹÁí©É€ŔĘfuŻ\şdŽëéÓúů››ú;&×ÖÔčđ,:;m­é‚?Ţ Ü==­ôLGQö#7ŢxC>ýôSůťßůI‘ţ‚effA:;űÄďŹďsţŘÜŠžäJáł18 ÝíĎ 'N$rß„˛„˝ uđŚh®˘»}n΂KďÎkü~óľ  ťQ¦( ňô0(ieĹVőß››úYôá ‚ TS2iuĐ)Đ}s8äb‰k&…@wďÎŽaá ÍcÓµ˝­ 6×95e°TĐKRGFÔHĹăÖ1žLęϡ±Ç˦“—g¶˝m]÷©”y‹DRx° ŮęěÔ5I„w'b)<ÖecĂę^ćr„Ŕ ‡qŕÝwMmm»/Š1•˛>†=ˇř1HîÜŇP §âł©mmé0& Â4­Ń‹1&i{8?8 $hŔu'>jT5>®ççSxˇhŠ|ě1Cä‹úůŰŰV{JĹÚš¸ 3đ¤Šq®,%zé’­-ꇳľ»kđÜhTďXóµkö=(y–ׯ[ÄŐŃ!ňýďë}P/Ą'çÇ?Ö÷NNÚš‡~„lD­f‘*Žŕ:ÉA—14Ë-ęÍ®!RaŽ `˘~F(¬¬a*“FŁ!ŃhT‰„ôööĘŘŘŘCą•ĺŠDâűů]KE0č…Ń”™ŚnjBI Ůkk¶iaâÜŰ3O`kËĽm ś˛9n#đCŕ’"ĄŘN‘e{őŞnN—yÖĄ?1/†Ń› Őś>­ßă÷ëĎ)BbÇ!L§Ő`‘±F-˘F‘µËĘŠuZ h~š™Đ¬!0˙ćçM™3fŠ ‹ţ Óëë69Źb:…ř˝=˝úc\d® ô ď4×Ţž~. ăĚótgOdłzh9 ¤;ńćHcŕ‚AŽ[.(ŇŁŁ†ÁÓâľ)ľ÷ôčÚŚ5şkxȨ-ABMn^ ”]]ať”qqQ×úÜ9KO€¨#‚‰•Ů×Ŕh©‘Š!Ť’JYOĎćfkt[*©21Ĺ‹>ź|8i9ÖzbÂŚéŢžľžzM”ďżoő ŮxĂx é´)÷óç- ŠäţčQaę¤;ööŮgőűVWő™ť?Żź?=<Łý~›×MťŤúÉƆ! ‹E}Ý /č÷ lyɤ!&ś:eMľ Ö®^5ŹťĆĆ2D"6ë9$ń¸ě3V0{IoX8¬i<ö`­f{–ž`Ü8¬¤©pČúűmŽ;-rÔ[HKćrúYĎüĐŇCDnŚMť›łYŰ>ź!e–—íp4č/=&xń>źĄĺÎź×ĂĺÎvá90¨7Ţ8yćľ>ăWJĄ,Ĺ–Íšá%ĺ…ňGYuǧÇ"ť¶Yĺ.Ł1ëD 4©'Rm/ęßSSćUŻ®Ş˛Ąză†őy ¶Aj ŠűHÄ•RI•?cUQ–++ęI?ý´E2 §Dôˇ!Y^6`Çî®^7˝ 0VS“.Ž×O®şŚéĘŠí3ÖH(Ś“q,çç-z‘ jíĆ «GÔëjé/IĄ¬F >µlVĎ85Ł\NőÇô´ŢĂ/©WµjDŤÔáÜÎvPaL&%MĐu#ö®;Ôl{ŰĐš››GŘ€T*éëë“gź}VFGGĄR¨/Xć礣ŁOúűăű^ćĺËzŹ3¨(…±fÓĽ…`ĐB`jćEB\Ó'^M>Ż?gJ™KăĚeH ¤‚‹‹kŰQ©Ń,,X“i5R+¤*ŁńöiHf gŃ(Ř>ˇî ÉŹaKxŕ‘$"$ ń"–:uĘjKxôRIßO}ˇ`…0Ľ™ŚEäĚ™—BĘO71ŕ´×CЎDP đPúÔ©2Cćř|z˝ĽĺeýÝÉ“FrIĄLŃšâb~^÷ŕ Á”3™VFd(`H#’îĂčONÚ(ZĐB(¶TĘĽYćbC‚ăCš#—SŹř‰'¬©rvVżŞďÎN=<“hT»ÇI!’.Úݵ”M>ßJ“ŐĘř¸ľg{Űx@Oˇ âłşşl6J4Ş˝ŇP»˛˘Ďh,۰4zíäý‰ĘY­íË쬥“I}ÎĎ>«Ż…y€4äúşD·9úˇńńÖş©Ż}Ýô´íc8ň`{‡µ°ŻĎ¦޸ˇďĎd¬łĂ ,2ś‹Ą%}ĎüĽ^ë™3VE§0/ho{űf.¬Ł ×^{íµD"!“““200đĐŚ‡¦°’É>Ůىď§0Č{3c€MŇŰkŢ ĽGxřŔpé!×Lç'łzĎ{ ‡ĄłłSSgî|ĺînó D¬ĎşyŠetµCWNŞ„ü.żź™QĹCOÁÜś~/łľPŚűäýĽĹEëU›3ďÜśEDÔvĹ$7˝ 3˝é&†ďŠ”ëEż),”H4®ˇXÔם:eëžË™’Ô÷'†€Ż«Ë:Ô˙ÂS$˘ ’ć0f›1±(†olÜ\X¦ĆEB¤b+—UˇRÓˇákcĂŠ˝äŔůNF_ľzŐúX;šXËeóĐi,ŁŽŇÓcĽ\Ť†A á°Äb–f˘‡îxúH0˛—.")E®«P0`ŤRI˙ţńŹŤÇ ŕٍ)áŕP‡#× ęŢ+•T9no|ś¦W ̤m?ýÔXDlv DŁKKzo§NYÔÔѡƷ§ÇŇĚ Ö§áBő‡‡ÍîĂűë-+Úòrs}ÝRŐ@ŹIâ€ĐŚ,˘Ď’.~fA/Ô Á04¤×ßŰ«Č0ša€ăŽzíŔ€˘°Ü>Ł"ű0ŢŁ ‹‹ ’JőÉčh|?Ą›(Ťcä´iD"ŢÚŇÍ60 XtáPÔ­A䬯[č MČň˛)ŹfS˝ ć 0É…“‚r\'^ľExÂô€Ä"-B±źÜ2ąZI±Ą:0d¬ © pćá°ÎN=ČÝÝŠěb Ô-¨ąŔ»¸¨ˇżßÖ ŹüÜ9ÝřäĽIOA#’ŽĹZ›ĽJ%Uüăă–^Áł…ş›đ_ÄŇä1ăăŞřŻ^Őűď˝ Ęqů˛Qc15ÖĚ:ďď7`MŢŁŹhjĘhô˸Âş»uż]ą˘ĎŠĚđîëÓý31aď ‡Ťĺ€} &ŁwuÍP@l ˝wc µŻüKÔţ čŁN5R˘5‚6î<5ehżµ5Ks‚D)–J†[Z2Ęś®ÇÓk9~ÜR‚4ŞŢ¸acśˇµďíŐ{)ôÜ̢®ň Ăä’€‚ôbj!Q$9KŰŰjä¨MLč÷‘%řŕŁńéď×ďĚf­ÇĹď×kE&˘ë>8hő‘7Śľ<†LT Ký©§ôű|ŞË€:CŕH]őHٰöE 20 0^ŠČ~ż†ˇxz"ÖüÇ(KwůVú Dě°Pk1R8č5gˇ»†Łć㏍NŁŻOź·Ľl”ͦ‘Ľáe2v /]2żßňí§Oëć>9iŁZő^>üĐ ŢPvooŰ”9ŚH#h<†‡ N µČ-r×kkUQ¸#ˇ«›5e­FFĚcç€8a†ÂÖ–ţŚßťq$|[«©ý”Jz- úŮ ş‚â…f¬®.K đ™pB˘â7›Ş†‡[Çľ^şd»`¶Ą®- MĄĐڏťúĚ˝Ć1`ŤAf'§^U«iDĎ[­ŠtŽwHQőČŹןěÔg[´‡„"’-­7drR˙Ď]˘=ę)¤˙Ž·´Q+$Ŕ–IŃB>Čü÷Ą%SâîčÝjU× JśRÉŇ™@ƙű° ĎřřqýŢX̲ }dFŁ»Ű®uoO#ř˝H{Áü‹!‡-˛L$¬ q}Ýj ę8”?Ć)"b% źNë=1'dx¸ŕNč\_?Â(¬‡}ČÂ‚Ž´ŤĹâű4Ţ‹‹úOťRooŤôÓ‹á˘AŕŃŠDLůîîÚ°–ůyýw~D&cS™/B<3ľ)Ůôä>a‚…v¤§Ç nD. WVŚ÷ „ÔÎŽ¸Ą%CÉ6˘łwuŐ”¬´'OÇE;ćž“SĹ`ˇ¨Č]“~aMPÝÝzŘđ˘ççmn]ä¤DNť˛Ô °`˘Ç•k¬śźW8&3DTŇUMŞXëä¤ő…`řD¬—ffĆŔ ârYSSSú9yU9‘–,—  EÉüĽ^űřP  ŻHĺ |qŤďŚ!cc¦ĚEŚ%™†O·ż…ôµú€Ó)ď÷ë™@ůŇŃľµĄ{â:ĆeEťă‰sK;4T..Ú˝ŃSrîśíńtZŻĺÂ[X&&,ŐĺÎÝ!Ý60`M®Đ—`zzlŚôŘŐC©m‰¨ŁCú’Ń Ĺ˘>§W^1ĂE$Ní”!uĆ:3«·×"bRLP§ćŇOÝÄgßťĚČjÎ`RxD‘Ô°3tw‹\˝:+'OŃ"úĂľdaaA††´dfĆŇ7ˈѦ¸쑦1=Ř}Yeyh4ÎAý|.QĹőë–2y÷.b9^Đ/̉Ĺl*tčPąwwë†Îç-TćŔíîZ~—p–kÂ˙Ž›A~ö¬EP™ĚĚX^}oOJRXůĽĄ™0ž\0ö¬ÁŇ’Çůyć ˇP°ĆBFś Ö`ŮŮiÍhgÎ҇µ = ŻäŚvČQp##¦l“I›âöřă6 –®âdŇŇ ¤«ŽÓű>vLżóęUU\FATJ- Şôz]ßÇ„@jA°Płc5űőëć¸ttXú®TJ‡@ÁC”ëzßÔžHÎζNIć —Ít¬_ľl”ĺBA݉Q¬[[ĆőDÝ ć€^0ĄíŚ·Ě,9v̨ý~Ý;›ÝŮ6}}†ěč°é„D;DŻ÷'ź4Z ŕębČ´F##‰ßŘĐłĹ~m{oĎřÂÜiŤnF‚5TřCűK ţ7¸Đ._Öß»<ŔzĽwÜ߯ĎssÓ:çŻ\™•3g<rKY\\p¸OăűÍwłłęU3QOÄ::Elń)¶’FbúץKÖÍůá‡FŇŮi¤gá°ő†*Z·jOŹ!c2Ůź–čö+_†˘›: 48h››ŹŤYA”B.…é•;xÖµëÎŚG!ĐĚ%bČ5Âĺáa€†‰D¬‰úxÁh¤-uㆡ>üĐ`† 45ŔdG1R §Ë:Žą9 Ó§¦¬đÝĺËúsḣ‡­/aaA_#bŠĽż_ŻĺI{hČ-'.Ď›}uㆱť°^}}6…nqQ• ĂşHmŔr@˝…4sÝé'çNŢ-ć‹`ŘФ0ł;“ŃĎ&ÝMIÂţK™Ak"-.-YýŠÂúđ°®ÓéÓ6kfdDź/©4ęđ§5šŇĹ©#DôCšÉXx2ý)đµAŇIÄľşju-zś€ě./ëyĄ&™Íę¤7†&K`ö ůNĄô3Ě 0Ö—Sˇ`\fÜőI˛>źń}QÝÝ5č÷•+ÖLŹÓŘeÖÖôşÝDŁşÎĐ •ËeiőٰöE ss ŇŐŐ'ń}ta= CnmYn–F!1Ă:;őllâ*“1ʓэ[[V¸Ć ű}ę)Ł™ Ń©VÓM”łµeđD Oś0ĂÂ`<üNŰŰŞp14xíŚĹdNôúşĄ\"ă‘…WMă)łŮYŤTđĽÖ× y č0Z™KAÄ Zťň9˘»dR×ňÂÁA˝źą9Ťęu˝V".ŕÖĂĂZCÁ›®ŐŚ@\=ŠźTHçP褹 ľ$˘)š1 ăOĂ51R”Ěc€Ďlt´už ŃiG‡^µ R.t$Ń i î ´™¦Č…dr oŠřH‰ŃXH$»łŁŻźťµSôčŤ!UŠ#ĹFĘn răµDZÝÝúü|>cŚÝذ˝­Aˇ»»-˝vň¤±6 ä§˙Eóáa#>„4—Ó=G‘›:lżŐŞ˛«OôŤëTEׯës`~:ő¸Ź?V¤"Î}RhFŁ6ž4"Ď/WŁÖlj䎡aŕ)-hR`3RÍÍM˝ďŹ>I§˝ä¶rá‚|üqźś8ß/ ŃP”Éčf;{Öş†áŻ!EDčĘśj!kkćÁŠR¨Üň˛a÷ń\(ÎÍ©"Éĺô»{{uC0É ¨đŕ ĄąŔőăiXCŢݶĐ^ś3ZďímMĺś9c)żßŠt4UöőYĄ0’Đ,ŔJ Á…ób¨0:.Ę 6ŕK—ô0--éőôö¶v‹ÓgB4Ě3Ď%Ěň˛ŤĄN^őĘe]g5ťÓŕç™ď^,굉XáŰťBI~)h¬ű‚dş=;đcŐëŠĚ4ŽÁŢžp:­©iţ˘§hnNßóé§z4ťńąłłúýşŢŚ%¨VUů€Ćý×ÝmŽH%x•DôŮcŘčŁblďđ°Q„0žvoO݉ćWŠţCCúąůĽ­ßĹ|ťŐUkXŐç hayŮzRVFFôçDYĐč¸ő`í ůJ&md‘čÄ„ ĚbĚHC›şWµjŚĘ§NY$‰Ř8tĚ 7©«¸łă™,‰X˘Vł´&×M/H{O٬™ěH-vi .:/ą­,--H,Ö'gÎÄ÷,©l>Đ9Śv±Ľ#¨daA­<Ѝů]ČŰ€vâŮ …Lé‘“'5‘!ěştÇëMĄŚĽPƱ©Đ2thČŇpá°E[4N’J#+“±A:Ł!aL$ôşH·F2($PSĐ1‰„nb ˘¬ńÄ„¦7 &Kź3ËŁQkFeôńÇzh{z áĆá1ڦ5Ś;L"%"©cÇLÁ Y·;ČČ6™¬‡Óók*e”Ýx.2÷őuŁřwŮŽ§§ő{?ůÄhIP4”ňč¨ţŚç@ťá‰'ĚË]\Ô5__7â>żßjé´5ІĂÚÁžNëďaseůęŞ9/ŰŰfxĎź×?ĂĂĆtKz…==mPřîď×˝AĎCżžŻfSť ńŔUGFŚÉ>,»\NS~Ś^,ňÁV“"$}.lîîZs'ç…ą)§O[íV_öFn˛BA I:­@ śhoؤĂ+ë'IÇ0˛ŃQ];şńgfĚ`Ň—'Ô78 "tçĎʱcžąĄ,,,Hźń}Śx+­Jśô‘Ks,rĘ ›8ČÔH¨›ĐŤę!¬Ő4źľąiÜ?Ô4ŇiýCŢÚďW%\.«‘C!Îζ2ü^»fi6pëtěb,H]ݸa4Pž7Fňřă–3©ĂlHô(ĺ"—HIŞŁďeC? …×x\•ýÉ“ĆB‹‚g€Ż«T4]ĺ̀к»H˛ăÇ­CĹ…±l4¬ž: Sô€NCÄŘÝm.ŚÓÝŮQEΔTw·ĄG¨Mť?o…^ •<KLŠÜŘPGÔ”q:®›š2Î-'ł á#Ę"ň.GĢŠdŇŇs(ĂÇÓßVÓ"݇c{B(¤†š&5wꏩ®Ź÷ďěču¸€D=ÓÓn±ńË0ÁÍdtďONęëH%şă~77­>ŔŕŇ%eÚ=~\ŻçŇ%˝¶—^˛šĎŤz&(ĆCŰNs-Pc>—{Ą§gyŮ(`b€÷ :@«UÝ—póˇŹř,˛" 1Nř\Ňćđg15ÄŰúşŐd’I«ŻxädaaAâń>)âŇŰ«üňeKmméfb„x0Ţ^ą˘ż'üäpAŽGÁ‘~‰ą9ë¸ýäe˘$†‡ősčC‰DĚË;sưń>ź5\ÁÉ”Jé\XL MQěfspŤô`Éńnm™·Ĺ\fˇllŘp%ę0Śç1˘GĽ{0ňűřΙ}-a8č`ËîüišĎvw-őAJ°\¶ô3K––ĚH2ńďäI3ôs0Cú€;“ü2)8RŽ[[6ŤÎôÓ§-MA~Čt0¨Š•"%9lĐlëëş— ńű­PĂ.ŃđĎ®.‹hĘeUŠĄ’*|ĽKFąŽŤ©‘ÁĆIŔŕĆ)•Ô€ł¦ÓÓŞŚQ†ŁŁúçĆ U´Ů¬9DŽ×®YĆsĺ˙PúPCčî6¨öéÓzłłşn—.ŮëI­¬ŘcÖ†ÚFY$.Űq±¨÷ĺαlĐ|8˝&'-ŠgVwÁ FŚđ@‚ÓSSVَß)µČAť)›5ăŹбPĐ=µ·§÷LC$ć##¦c@/vw[ó-(t;8ú±XËÁAëLOĄDĽFÂŰĘěě‚ř|}ŤĆ÷ů…lÜëÎŽŤ´¤€ć. Łßťť5â@ň¸p$1v3™Ô:ĂÔ”1®Ňť|ę”)=r“̆I•éwD=##FOň ú> ¦ÝÝ6(‰A.$Ł^ń„‹EU\ÓÓF”Ča§đ8&Ľ;lbŘnét§űś<+ŢnW—^w_ź5c®­BŤh‡!IůĽE"`XY1ęv •ŕýűűőZČűâ=¦R{u;Ąé=™‚ńŁx˝±ˇ0\hăßyGď/=•2"§^·Ć.oSAśmnęuž=kµ0qaľÎf­«Š`Îź~Ş÷Č ŢÁ„y›!S"¶nŔ®»»m @2%sxŘPŽt™‹“Fkí˛şĹÉ@“ćÂóçő{@ŻáüA«ł¸héIŚńĽ}>Ó››FWCä_©h„§uˇY™šň Č-eqQ©LăűđÁľ>»˝myÓlÖŕ‰4Ĺ1JęÄ„A¬üŚ?E3Śě·««6Ą j«\6«Q[QŕˇA%NzĚ;MT(XY žđ&Ý]!ąśq„ýěgŞ»»5Š‚śprŇĽr`ŞDh˝‘‚jäĚ3Ŕ9éÚM$lËŢž±!3›űĘő¶ŞUćŇA-"ŇĎ˝xŃŠ˘ô|řˇ!f˛YI2'{yŮ ´L|¤÷W.b¬0“0z RŹĐ~0\Śł4ZhFĂ`h:;µ¨Ľ»Ű ‹ĺ?B,€WÁIDATĂ*óS0l°÷÷ňŠ˝»¶¦Ď;›ŐhŠŮ•Цk0„ĂĂIĹwü¸)Yv‰@©;0Ć7(P"ŢĺeëŹať*˝(Kčměâň»Ý¸aÝô¤žá°µk‚Ö'ź×ď$U<1ˇë S÷Ô9›Őě|WśŰlVť®DBłŹ?nŠ:“±Ô7}Uˇě7,ŐCEë}$“úŕĺünqŃrI¨ăµ´dłşf¬aµjđv"+H>9űWŻęZEŁi<Öme~^GÚ†Ăńýů @ ń^čÜÍděŕ/-éFµf& ĄKKş9;;Ő›ťśÔ3:j›1”şą–—uR€ĆęéŃ |ú´śŮYK q0)\ž>m Bˇ]ˇu `Ë—ź˙ÜRn¤ÔDl°“;+Jk”Źĺ”É‘Ó]Ö|sS=,›Í10 ×‹‘siča)ë‘LZŃ6•RăENîd 4 @Ć,ѰHJ'‘°b1(6;"zđww-%E4GĎG8lý=.ł* ‘¤=!y¤wHŹřď‚E˘ŢÝ5Ö@IeŃ/PX6=P¤Üáű#B…:fhHk ďmäęŐ©T” ‹\+yYňŚŁ ;o"ŃšĎf¨„s tŤVY”7řBň˝=54ôO?Ő´›ńű-ď[,ÚT´®.+ Sç;é€ŢŰł)}@E,ĹDÉƆˇAhĂ+dĹ{—:ś"*ýđsŃ©-bë”ÍjdŔ•Ą%‘_üB›(«UU CCÖŁAc©žéiőGFô»Ż^5Š{hą]&X˘R§OëáázˇˇX_·tYą¬źOAłX´ćCŠĂ@yAťˇ|Ëe]»ý=ś\¤b`=X[Ó?n?P8¬ĎźÚM"ał¬IÇ`xH;ô÷k$Ŕ¬‰_ţR×H1NŃÉ“ć@J­ipĐŠřLëjTë:8¨ĎoeEźiŇ‘cŽv!ݬM­fő&j*nŻ ÍĄá°®Ńř¸GhĐ„Q¦¸Ý]‘çź×źÍĎ‘zŘŕQ ‡mÄ0ČËlÖ†4ôćóş7âG´ÉDQjđ©Ra¸Tˇ kŔdŘxADČrN-fG&b®€‚Ł…‰ßŻ‘ ű©«ËęłśC@3"V=Gă*póŮYĎ€ÜVVW$í“d2.٬y(r`ŞĐaă9Ń؇bŁQ Úí™+†ň3DU*ęA ‡raNťăxňŔ/iĐ ·±a( ˝˝6ÔŠĽŁCl(d^ăXA¸@é.˘ îĂUA`0ÜŢŠűxÖKKú·‹¬ÁCĄaoÚ÷ÍÍÖ0ľźŮÖxĽ¤ Ż‹FGÉQÓŘĄs¨DĚ3O&uͨD˙CĄ˘ëIS#”őpś‘îâ`‘ˇźDt! Â"ýB:zţĆŔăŠXmŠ"rOŹ:¤/NśĐß;f†€Ć5xłDtŻqßĺr+ Í ŁčAZ]µb.ž,đjžď⢍f%żëľb?1ô‹aV4˙U«–Naâ']ţ©/_6Ť41ĹojTxöĽ÷ńÇ­VyězŮtŢ7›ú=Ŕá~3ÂÄhT÷J>Ż{”´Ôđ°­©%ŕĺD÷ě]”r:mŃ×—Ë€AZ ×Řźd:†‡őú0,śŕń±E44âärD0ŰŰúůLפ1CÉ+sĺ12¤»çç˝"úm…‘¶Á`|_é˘ 8ŘlNň‡< ŇDL…#$&%rćŚ>ě±1‹T 2˛y劥y¶·uc]ľ¬2Łc ]©MPÔţč#Ł!¤…÷j{[Ó]¤<ĤAňđ’Řä@t‹E‘Ż|ĹR_@({zÔ“Q…!ý¤ ęuýŚŮY›;P,Z(3Ä‚"z¸€4şE^ ú¤†ZčzßîHŘbQ Ť%`·°ľ6ŞŚH™ś<©ßÉĐĆ”®ŻŇŔ€5`ţß˙kdt˝˝–N Ę ą‘zŽ>źńqëÚlęł;+‘¬Č1ĽzĽb˘ Ňlô"B"5·Ľ»«÷pń˘‘ţůeD”•ËŮ`«­-] ş¸#ݤtłY‹(>ýT˙¦M„K˝ z® Z `ď%ĐGłąi{ťÚÉúş*~đ9µšľçńÇuŹú|zů<ęA¤}Lűúô˙ ú^—KÄ"v&<’†l4ôž]ôNő$Rޤ™‰!Č$’ŁsX/HJ¨X\’T˘Ŕ ÔÂh@ä÷‘^7iVŠřşűo~^#S§<*“[Ęââ‚tvö‰ßß§&܆ů’ —5čŐÉQÂË÷l•ü"c+-*ŕđâµ'“6<†yËFášž6řŕÔ”1’˘`P¶ HŃ‚!i ˛p(Aaĺ7† cLµKg>n@ĺrV_éďWcxę”8V*ş9I7ŤŤéá&jb>Ĺŕ ®íŤÖ§R(č˝Ó ™ĎM<˝äi˘*—-7N=,͡"†&˘Ż"—3.­zÝ:˘©a-/kdBţ:•+WT2ßc~Ţ>Żż_)EŔ {Hg2 ZUc3;kd™Eí ţ-ż i4lhłf\şpř°aDŔ51—›h89,ćÔÓŕĘ€'ö5ĎŽäń¸5ńĺrşŹÎC'ŇŮ©ĘůÓOőóÓi}Ł\IutX~~^×izÚ¦đÁÉĹş‰Ě—–ůć¤áđhz<;kHDčoŽ96fH/ú-0޵‰Ä€ťăŇ[D- h÷ö¶Ý'ô5.ô—!W © 2TĆ>§&bä©tˇłż”=Áëą­\˝ş ÍfźT*ń}ŹhqŃ”ůtBgĽťËçŇĺË<élÖBT:®‹EÝ$FÉá÷ŰA ‡U)PŇ9}}şI©+‹vřA+—Jú€/^4¨/Q˝˝­c1Ł+ ë©§ll)\E rđHS)ó@ůůÄ„ĺ’IăĐŰBÍřL6Ł…čkk¶FŔ ¬~Ă,’ľ>C ­Ż[*˘4ťţ6‚^ZZjý )ąŢDÂđí@b1şÇŹžü1ĺcŹ™G š9 °Ü޸a÷i"†Xł©Žž.ôňđ0áőţěgşĆcc¦ ýľ7Śv$M˘{{úľÉIý>˘¸x\=w ĽÔĚÜ~ ®ád}}Ö“S­Z?ÝÎŚ,†ČSÄşÖ=ĄŇ:‹´$˙¦Bí/Đë%˛ ĘâÄJĹú–PŔ6~}RăuýşÁEô>1XD¤Ű¸Nęi]a,I—m@óÎ!Kńľ˙ľFÚÇŽYfGJ2©ŹÄă¦VV lË} 5" CĆÖÖôő ›ă¬omYż-˝ZssöťÚíŃo+ׯ/H2Ů'őz\Ë“Öjş Đj˘Ţ€çţžb7]˘Ś ĎƢIŽü8ŕTĘŠ|DĐvGŁÖ”·ľ®ÍS¸ŃDŹ[O±\Veóüó†[Ż#äB‡‰.FFtó2˘ypsÓĽpr¨ mťýY*Ů ŢS©Ř° ňä|ˇ_XY1^2Ö€´ ł›94tÍSěŮ30 ßËä8ľ‹ô t'PšĐŮŚ×GŃŻ}ЇLLXń˝Ń0¸,EˇőÔŁ'38¨űĺęUëKI$lĚ(h(ö8`ĹĚOÖk^[Ó{¶qŻĹ\»Ö \џݸa57@${{Ćś@žźT!ó-`×éĆšÖjú=îŻ7 ŤÁŕ“Oš×+bMŤÔ#XçLĆ`Ě@°©S‚t»vMďšPHĎ0|lo’nU˘Ů4h÷‰Ć‡ärŽÁë pÝ÷Lˇ$Â[YŃĎŁřx†uX]µłKŻ`«I¬®ę˙a[v÷:Łmi|¤–ú † ÎG·ź†P…߯—ÂşŤĚÎ.H(¤} z0Iá¤ÓꅆêřPŕL"Á@ÚgvÖŮąsúfP“צůŚ0šp tÔŕĹAéĐ! k*ML¤Ş€€ éať™±Đydł6<#Ez ľ-j¤-@z X™sĺ<)†c!¤„0šccz Ů:>®Źž'Śu.§k®^D Ź‹D™EŢ9üNąś> Źé)`.79˙sçô@Ţ‘k^^6`]Ľ7nčł§ń“AF0ČúýFšGş…ëŢŃaô-Ą’“fÓćźĂ‘Ň :PSçĎ[>|jĘÖÚM—@jXŻkC v"µ•C ŤŹëç‰Ň„Ś W®č=ÁŽ@ ©9:ő1ÖÔh HšGDĎ’ŰE_ĂÄ„1ă\LOŰü›ť˝^j<ĂĂV,gp{"bú­ ‰rH˙0ŞŁ1iR®— …ô}čä>]“ÍM}?̽ᰠCĂ0:# BŁ dš‘ĽL*u9ËĘĺÖńËoŁő'MGÄDZŠ´×Bă1űt¤"6˝䶲´´ »»}’JĹ÷7ěř¸zĎL*&ĘüŇ<¨lÖ8‚––ôđT«Ş4ww-íâÁÚÝUEüŘc6q­Vłh…á5@€™Ó10`©2XJK%ËaŹŚčˇ‚1ús łô6Přßذć´RIżĂçSŻ"űô´y/¤(†ööęgÓčEł#9l¨7ŕëëÓÔpŘą9ó˘@w}đ}§!Ľ€ÎϵJßíVc_*éó\Z˛ţč87·ŽFŤA-Ę#›µé‚ăăöůt¦SŻ¨Ő´©ŻR1ĎÄ4!đ>ń]¤'é<_\Ô€ţ ćžMuç^#Ąř‰GN] şýÇ·.pćÂĽű®1?’ٰڇKĎ‹KqŤ9䎝!?tÁÔ3hf#ĎNÍöXúRŇi=ô?ô÷4»ZŐ3pú´ţ ~8ýüĽî+ś‹­-[3`8‡D Žúúě^Xéőë­ă[·¶Ś»čěYýyOŹqH…Ăf{ĚşŹAdĐ FZ†nm ‚wĐ ·LôŐUÝŚ4źń]¤LHÇŕ™˘¨*›~HŧźÂŕiäxxÄ ’Ŕű3 …šםľľ®÷48¨ď]]Ő˙ź8asEčx†3‰\6˙†oŠaB â€E’ĘşzUÍć¦!Ď|>U ١.¨V“ý –<_ŕ®4ěyЇâżrEź ©źŽëtţđC+¤S—‚VPśZ¤ˇÜ”%Š‚˝Ť [oęe¤鉠.Í Ĺgö †=J×>ô++úý““–âÝŰł÷G"ŞPq<._Ö} Ą=Ó%9##öű™Łóa0÷J2ëŘ1SîPzŔ‚€W mŤËú Ůł¤nČs¦î ‘!ě®3ŃÁ0Ú!·óT `ąP áÄ‹úÜ*3r"I§ď˝DFĂ…úŤut(óŻ;U“=‹Ó:0`i[ŇŮŤ†ČŇŇ#l@˛Ů¬lllH4•@ …BAĄ\.Kgg§lmmI.—“ÝÝ]ŮÝÝ•ťť) ˛µµ%ˇPHÂźą^ŤĎ`’ĎçĄR©ČÖÖ–tuu‰Ďç“™-˘?ßźśFřHzHÄĆÁ‹ŞĚĆĆtĂâąPřćĐăe@†ŹP_źćj?ţX˝B(3D,w‰"ç€ÇbÖŚµ° ×·° ňň˺ѹ-˘Š‡ĂËS Ayíîě3ł",#8ŮŕWŻÚéâEÝ CCú$ĐFÇᡇBćSŘm´Ľ¬‡›FL0ë Űrç@C;ń"|I°CVI#&9z8¨ßÉ< ×K…v&VEÉ!MŹĺ˛^ç‹/Z¤Ęŕ%Ś˝4ŐŞ^')BR0§Nť*ŐĘĄĆŢF‹w ™ŠÁc"FŚIJ#“1¤^َł.ŕĚBÁş~Đy΀IR ţFdU,Z5 tiIŻ…Á€*¨3Đ|Jz—‰–¤}©óQo\_×µŁáłąiůű•ăłsG?SŁšÎţpç¸olŘđ'Ұ|S']‹¨ÇHŽ÷ą5Îi,>‘%úĂŘr%Ęsfť:;uŹ˝'ýĽ¶f}2DÇŔá18Ě$Áą ÇGÇR·z˝.ËËËRŻ×ĄłłSšÍ¦¬ŻŻËĆƆtvvJˇP\.'›››R«ŐdmmMJĄ’lmmI©T’X,&>źOšÍ¦4›M)—˲ĽĽ,@@Ö××ĹçóÝ‘± މ•ąpá‚üýß˙˝üóţĎĺŮgź•FŁ!oľů¦Ôj5—żú«ż’^xAÖ××ekkK†‡‡ĄŃhČîî®üîďţ®ěîîĘä䤼űî»’JĄäý÷ß—rą,R«Őä_˙ë-§Oź–˝˝˛\Ľ¸,/ĽP‘`°Gş»ăűĂčq¸rĹňőtGsřäŃ|C.ŁcQx2 łFG5ŐçőĚ ÇÓiU&Ěüčî6ďÜśzv/żlC8ÄnoëźW^±Ęâ=:cÉívw[‚Ë_Ĺż­60<¬ßóôÓFĄOĹ\ÂsčD ]ÚLŞ«VSzôŃQÎĂ?yRßá‚MgdP¨j#++"oż-ň›żiÍ—D9ăăú}ź|˘ëĐŮ©ŃÜG‰|íkŞH“Iý?ʨ»[ßS©ŘÁ,T ±/b1Ť††¬óybBϸßńq3Şťťúšť#đ$…ÎśF˛ÍMŁc‰ĹŚíE2;kh»TĘîÎ2H-ôÂ`śÉč¨áauͰS tQäÔżčz¦©’ńŢŃŮŔ1Fť<<^? ćHóA 8:Ş×“É´*9Ś›9\‰„ľž~'RĎxď"–ŇriC@WŽŹ[Š™ ę Ă€Ř’b±>Ţ^k>xÁ÷Ă8Ŕş Ěą.€Ü?´űÇŽ™ăópP}>+ .Kł›jc0Ń>㏡nR‚Ď©Vł˛µµŐ˘“sąśüíßţ­T*ů˙á?H ťťůo˙íżÉźüÉźČ_ţĺ_Jgg§$ Y[[“˝˝=yňÉ'ĺüůóň /ČÉ“'ĄT*IOOŹĽűî»ŇÝÝ-çĎź—ŽŽńů|rěŘ1ůw˙îßÝź¤X,ĘĄK—dppPŤ†tuuÉ[o˝%Ď>ű¬üčG?’l6+ŁŁŁŇ××'_ůĘWduuUR©”ô÷÷Ë믿.333Ňh4äťwŢ‘b±(ëëëR©Täرc2;;+Ď=÷ś ˬtv†d}˝Cúűٞ¶Ůďĺ _IŠ&"”.ťľ.=6kaA ¤zXć9p0P ZZZ2ŽhťççuůýţOOŰl§ź¶śyyŠÜxZëëúť““Ö őHMf~˙űš†! :Ş»ŰőÖ–MÉ›šŇ˙_şd(›Č÷É'–˛ą~ÝRf˝˝FyÍĎ]* PGîŘŇBÁPnź|bŁLA„Bz}Ţ>>ŁRŃűIĄ I40`ý Ô[ĚkŐĹ`* ŹůĽ1Ŕ¬ÚÝ­Żťž¶Ćб1˝˘ ){ Ü_ ĚĎü ©—.¬™ô[oŻEđ‘1,“±"üęŞî'źÔűĚç­»¸hű›ąŕ¬^n,fM©Đŕ€řˇľ·łcł/ŕôAó×ĚŚ›K‹čZĄRö;"N·Ů—t”đPzpÍDbJ€ńKKę¨0ö`PnF!}śťŐ´$¬« »ÂŃXZ2jsF CŁCý„´ŰÚšîŃgž1 &0FoméçB˝_«;ożîÁůy›ý],ZŠ”Ł9•v/ FćrÖ 0%*s;ó©I NĄ qH´…SGq™lÔ<Ôô•ڎpevÖ ¸Ž†Őű§ŢAż€ČBÇŕ÷[ô(>9wÚ"éh" ůęŞ9µ@ö©§ąiaˇOůá(/˝ô’t}ćµ ůŮĎ~&===˛şş*~ż_&''Ą««KFGGĺňĺË233#""'Ož”ĄĄ%I&“R.—ĄŃhßď—z˝.Ď?˙Ľ”J%) rţüy™śś”ŐŐU‘§žzęţWęőşÜ)5÷Qţż˙ďm™ź?)_˙zj?˙řŇK6çúřq]ü…=¸x ަ PHĂĂJŤ>2˘EpňľóóĆń3&”>PĺđÜsš2yńEŹZŻëç=ý´zúLk6Ťµ÷ĂŐ‹¤Z6;«×Gľ—ü­Ű¤X,ÚřMŠ }~Ä,`şäda•M$4˘ÁSYF?ź·ZB&Łkş˛˘®źźţTŤŰSO2LD_[Ż5>óĽąç‘Kó4şŔçćTIś:ŐĘ/ŐŰ«kB'7Š”ľŇtŮăĹ3Kc{ŰÖŽ,XtÂgłF©}őŞ˝G÷ł5ŁhB‘–Íęż_xAď}}ÝPD"úĚ-·Ž·šĎ[˝ h1ŽN˝®é:˘((]řn6ˇĆŔ#§ŔÍÔB"Uf|C8J:%°=GOBg4é!ÖŹźÚÉúşţ{|Üر{z,Ő$b@”ÍM‹čᛣóźgČşă´Y tq˘€ŮŠ”žóËb ×Ŕ§¦ @C!Ę·čňĘŃż( ‡­“ę&ÂUĹÄ06pÔçź×Hˇ(9nćL|ô‘**hD,eA#â訡¤ČY“sŽFŐ046 Ąłľn‡¤V3x"śOx/ÔVŘśäÔ?ř@# áa‹F@†¬­©á7(hł©‡Ô4٤ĺffôß4ŻÍĚŘ *ŘC›M›żµeiA ©tT3źő"¤9Eď Ťpâ şN¤Îj5őć™çŇÓc˝ĐŔ"(ä@ôç×®µÂŤI ĹăÖ´ůüóú=Á :ĂĂ6–&TK‰XJ‡tŤ"ćE“‚H§ő:) »0RX•éÓeĆ~ÄQ±ż€,öö¬“$ś_ŔŻEŚSŤĎpg¤†x“â8¬´®Đ»ę*“Ńu:{Ö”h»żČŕĄDB__.LYÄ`őÔ®(ŘSQIşX«Ť9÷ І4}Ô1Üą4˙ńPšárö7Q„ڤ"ÖÇŤyď?Öź•ľ·[ž:)/c}`­© ’ †—>­Ł&GŞd~~A‚A(”ą^WEĎ‹Ľő–|(¶»şô˙°«^ż®)#Ć@˙€Ç±·§‡¦»ŰwwŤ·joOSfóózsHČŰR\%Dfő&§íîęA†Ă46fźńńÇ6ÝŹ&ąą9ÝÄ˝˝ę‰–Ëz/"şá>ýÔBâĺeő¨i”{ë-+ţ‹V¸~î9˝vî…\kˇ`ĹaŇjđjÍÍéýĎĚŘúlo2nmMŻŹčĂ…E éHă€ű’Ş@ńâyâťíîí}W—XË»‹¨RĄx_*éuMNjťŠz”8¨»ştí¬EdFz‚ČŠu(dźfÓĽî|^×/›}‚BÂK_Z˛ś7UŠŻ"˝5Ąć0ű¬µlT*^ÔxzÇŽă*!sŁa]âÎ2}ŘŞ8»»µyjurÎz]ÓÓŞ@ ôc®:iw.čzE Ă ‹•kŮÜTEÉŚwŕśµšM‘ …¬†ŰŃQëgˇłTŃgź:Ą÷H¨Ń€xqxŘú-ĆĆ,rŕ»ŕ::vĚš0!ó‰hE"Öc00`tüôŤŔ¶KŁ3 L …týáB* Ś’(˛GřÇDLńŃlÇ#b$xÔ›ööôą’Ş Ł!”´ ßXot7.—)Xx”@ ‘Ä3§Ę,—3Ú‘Ý]Łq™ź7ç€TŤË–Nëg-.ÚčaCŘąµ1ś7łˇÔA‚GÎýŠŘj4`¨ë}cT¨— .—ŐąĂ[wÓ„DřđߡôIźaŕrç¤D®_·P"6›(¦^7ĹÄ˝“wÇ A‘B˝ODżkkËj2Ѩ*VćaÝQŁt)áéĹpď÷ Á›'uH¤!Ňš:qĹł%­IއHGĺ MÔ@Jo‡kĚš ŮłüÎĄ4áűA­ąźçŠ©!Ţť„¦»/DŽnr¤ ȵk ’ËőÉôt|ż[<™´Ô4ľű®md:ĘÉM./ꆱôĽóŽŃkĐ|íš*jŚË¨×rň¤~s7­F=ĐtŠšü=EI/˝˝ŞxF·qoŻ*ş±1=ř¤I\÷’AP0ß>ń„4!‹ăŕŇČÜ’ľĽlž!Ţ3D€É¤FGůĽŢi rěkk†®"w ň†C35ei ]€5E˘hC!c3¦CHšW0hÉîný˛;€ěx™@Í@=ĂĐ#˘5&ÎŃä‹©1gţŰTFÄ@:Ź~öťČLŔt‹ÁD€°:Ł´zšÚÜ4ĹN= qggݬXC'éÓlV_Cý/–†V7UÓlšˇ ×Ď\ („ŚL€ĺ—{ăo »;.—{#’…%BÄĆö͡ŚÉ˙ÓźŃ%@I§Ď}€ň¸ăL$E±"攑¶cMxV(¶%HńQŰÇëV U[^Ă€2héÝďň ČČ'ź,H8Ü'ýýqą~ݡá“ď%UÉX·ňÄ„z¤4ĄRĆ÷Ă(Ő•ý¦\¶ßoś;0Ľfłęá;fT ó?ůÄrË{{¦Ř\8ńĘŠĄpHz …HcTW—QŤăÝÝŐ>Âv$bTßôĐÔWą¸hëÇá#—Ť·39iŢ'pHóä¸&چśžj5 \PŇ(bŠî¤M€Ž˘0 V§żř/4đp=AÇ´AW›Mďzîś˝4·šú †Űí]@ŠHÁťECÁÜť)b#e*bÍŞ §®Ě(` ŢgË! X¤€óŕöQCÂË%"ČdŚ÷‹´%éDŇQÔ¶P°ěÉvĐ[  ćGqšŠZ#Ţ8{)loµß+E÷ö†ľváüPźÄ¸aĐĐ{Táú1Ф·î\ G.gűv{Ű~Çsg,?ŁŹĂ5Vt¦ó3"÷ö¶:w­Ď€Ü,--ČÄDźÄăń–A1™ŚyŚşčťť6(‡C@Š„ -Äň˛*|Xi™ýÍ<Ľš÷đdÍbĚ'śRäYáúÓφ÷T^Šž‘ž‡ -ONZô@.ź™t0?ö~°\¶YĐ—/źÝąP‹”JĆ»U.«1ňűŤą4đĐžKĹŔ⊠"Č*ÇťľF ^,š»\Ţ(<>7‚s‹ˇ6ŚŠT°jSÖ «@nASwô¤VŘ_ÓęîîZÁĺ‚b E4źăţC%˘ßń"ž%Ę›ůíDfí“űÚ'„ů*"FÄÉ^ŚWN4• ÷•;g“}KdĆú 岞ž-Ń-ŕćo &ě[އa§9g®ńöőA‰#ăRă»3ŢąWRŠD{ôGń]토Ýx°V\'ŃŚkPĐeĽGťŹŁi@ŽTťÁF%$_ZR TńرV)ĹŽ…·‚"ˇXN—p­¦†#ťÖtpORUng{"ˇ…đBA ă§Oy#­Ę!>0ź–JÚä´˛bŠm`@Sf©•°şşŚyuyYßsü¸±Ď’!Ş(•ŚďLl”'×Ô7ĆĆě02–FŃBÁ¨Nť2Ü7ůuf‰vvŚÉ÷ĘCő`óyë#a’´#ŁŁşFăăvč cyíëłÂ#(”ŕxš"Q?ŔĂ  D@±Ü­k0Ń‘ş ŃŚ›‚ݤOž ]WĐvnČÝ8©ŰďWă™ĎtÔUDYş0T:żI+ąÓó0PîśyćĚŔŁĺ"®Üş H:mQ%T3Ěx'mF¤€¤ ßď×5G” lFöR$bLĎkk¶n"ú†Jń¬č˘ÚKÄŠăÔ¨Ř ô\°îccúą@P{z,•„t ö(hŚkŹëőŇżĺ¦ 'k ΖH«ńŕ=uw–: ˝Z=ĽćÁm/Š#‡˝§‡Ak ĎźënŻŮ59R)¬…iŰŐoS2˝Ť‡2ěB{ÉŮó> ÷«Rçš™óŕ2(ĐŰŔűH'AԙɴržQsr©4H«Q ŕëÖN~Ä1ŇC[ň¬DZÓu(+Ŕěy^ mzg§:`Ô0TÔ8‚AŁĄaŔű·ÝËĆxŔŇL(©Ă ­4í"VÜw‹ďîw¸pn÷gíB/U;h‚5:¨~áá­ŚÚaďĺyň=]“»O\ńj w 7n,H>ŻĄŇiU„„ÝôfJ\‚Ĺ„c‡"`¤®·t•^BA7‘;úŢ) }‹‹VpFˇ¨vS(łľ>c‘ĄŔŤŇ¤Żh¦k¸Pî8Tă¨Â;»rĹ8·đFˇBˇxÍA§†ŇÓcý/46RTw•E^Ŕ Śu=äÎVp•3JďD¤uđ€Ŕłr‹Ń§čĚgŠ\vą¦Ü3†q!RB±÷„ŚŹšôH=ľc ˙ó^’IýŮÎNkÁžËOOŠ5ł‰ŘsiÉT†@Í %8©ř®Hň=ewö©> ĐîüśDâŕ>żw›ýHc˛ç1dŚĘA)öé<ś§vŹ>7ăá 9»·J˙܉×NĆâV…öv!Z9ěó‰ôo5ąĂ2‘úA\µGŐ€©9Ô­-cXĺPëXÇÖąĚĚá ßâřq+r’ZYчrö¬)Č] ěŽĐ´VÓH٬¦„ óVÖו•5•2Ô…ëé—ËV´‡Ť­¶Vłš ¨Ś©)š9Ś(‹ Şä˛Ăa­ipHťˇě)řâÉą×S­ękˇ 'ßĘřPř—h2#*Á+%ĹѦ€Ąö榮7 “¤6HńńcFJjzX< ­ű0zVÄxź\ˇ6"b©=ş»AćPč'ŹŹ˛‚Rc}ÝŚ*(śťűnrŚEĹ+fé-‘V`Cűs<Č[e=D,%€`}Ű ·D®˘%špjEô™¸éBoÔ‚0¦îś"FŚ<×Ăĺö© ¤”ďÄ»ÇčÜJîUŮ‹´ÖEZ#˛»j—í­=ťv§r/ď9 r¤l›Ž”ŠtzÚ °(ÍHD0^ŠN<éźýÄđ$¨ĎńhVcC0{"“±śďô´ÍÁkĄĆ!§Sšü?^=9o·“•?±ÎčJĹ Űxšpî"}’ÍšâßŰk]Ă@ŔŘt]’87mBşÂď7š|†é,,čőAi``ů1b©”ţś”ZąlđQĽ\ŠĘä˝Čĵ`l!dŘPw·uüÓ čŇUđě+«ąĐŔwë˘úÂaBŤęľ‚Ŕ1ť6'ç´Ď"ĹÁAëšćyQf!Ó H4DFÄŘžI=‚¬‹ÇŐ1âů ,/·Ň†ˇ´ ÓňÚ…"5F—ž č98,ě˝"­Eo‘›k.ĘéA óĂo%îł;č˙w#îŮwĹ5¸żJr¤nąZµ@äęÉIÓ0FłÚůóćÓ'"b‡ň2ščPF4ÂÁäö]tuéßKKF]A:DT KK–+'˝ŮÚ(OWV,§N¤A:±†<Ľ-·#¤ ęxx–—·ř2Elč] Q¤Ł u ‹ZÁŢžţśÚ ƨ/°Sٱ¦ŻC¤• »»Űš1Ť†ŤčJíË…{ňůPť0wÉĺ´VÖl¶Ň“ÂŰÚ2ĆŽý>ŢO4Ź+H!‚s%Őő@‘PP†Zťő ľO0¨·żßXHϱOI§Ńçᦂ0Ŕn‘ť˝q'bŹu¤áŽÎgŚ~»"dô,#}ďUp~î§ÔxyżXýQ…Ň>L9RJ›F©DÂčJܦ¬ťë ĎŹë„ĂŞ<)ÎŃăĐ@§íÂQÖ°‘š:ŔxĐť^©hŠčpX=~áçŠDĚCëë3Š ťŤ†Áš0DŤËď#bMMÔB(dRç€ÄuŮÝŐź­®Ş˘rŻCĆ\®.őę]/Ń…CŠXDA—3…gh, ó€Ęnt®O€ŰĄж«Kźi;Ç ž®qj3(ĂÎNe čëÓźó™A(Ź·µÄȡąő za ©7Ń(GS#Ć.->‡ý ÜZ„ű{źĎ˘7ÖZ„´›­(BC)ŇŮîöQ´÷_X=­ýgDń…‚1ÜÎĎk”ŤZ„ç®Ó˝H{Gúý»)€ą›šÎ˝ŠKŃ˙¨Č‘şT ™P­W*Vô…S˛ĂÍMë× ­…Äăú3fˇ«lO+P4uŃ7PhDÝ@ŁRW—AMűűMa€ç'E]Ę(8`›Ź?ns9ÜÍCôś—®/¶.9ô¬ÝŇ’^7Q\{ž15et%D(čż!đ±ú(!şďK%륀ÂÁďWăr"UBŢÜlĺé1ĺ ”™z„Żőuó I kż·ĹEK™Q?bô®]j8#ŕő‰”08\Ńo»¸Ôtżˇ!4F¶§VbäţŰUdna™îwK×¶‹›Şm˙ôÔD\4—+LGĽaOaß/=ÂÎÜýúÉt?="Ź’©$°Á6kkć]›ÝÝŐŤÎ0&č6ÜîZóvÇĆěáŹDl.ÍxD ¤’IcE峡W?~\ßű VxĹĐŕőćSXŰŘĐôÉ©Sú{ËWS¬PeFúx,ŰÍM«Ż0_ÄĄo‚Qµ ú+P6„ô{{FdÇgˇĚ1bx… Čr9ËŤĂ}•JŮ':ŐEô˝ §¤ĹőqpÜ9ŁŁFi <ŮťA ň¦]ĐđF_ ESW‰Đ?ŔxVôî4%á"mŕ׌3]ÖîwB“ú á2ŤYrM™Lkä‚R=¨®á*˘öů Î\!% )Ł[«!şu÷őíPF®€;#ÉM'!ÔŘďî{»˙[ÉAMníBd(ôÚÝ ©ČĂľď ˝wĐúxňŕäHĽ`ŇMl>fN¸^(9wňďĐ\Óĺ˦cÖ9ž5ci]Šç\TIoŻ*ÔĄ%+|·ŁJććôó‰”P830ţÔph.äc ŢůţĂ —Ť| Ęx­K Â÷C!±±ap@fAÜ”Júy¤˙ŕŞ×-ň€dÄŽ;]Ťnţ; ťxÍÇŹ+ÂÄÝAe?2$Ýw'ďąuř%îü ‘Ďq)Ţ«Q|âhzrÄj ®ňbvd{¤!&&Ě›M$4-Ú‰ü'2)“|^óöä«ńî zp‹ n.9‘Đnv ®î´µÍM«´żŻRi…P˘ě!Rëíµ” 3ŠE›!BŽ™š źA3‡ÔťHHO ŻĄ‰Ź¨cyŮ>‹†0řľ–— @có¬·¶T1śh6A&cl¸üŚć:îܹޤË3Sv»ŢłY3Üĺňáď%˘ĽSo˝_öݸŹfÓf[VŻ pmD?r7Ęţ uűĽrŻJżťzüAĄÁ€|?ăŇ>|©]h0ý<=,îw=ŠĹî%G*ˇ ŤÄbŞ(@7ŃG!˘˙ŹÇ•Ö„ć:ć63§Á•fSg…Ôj+e®‡őb´@Eôukk­Tp'ąĘúi:’Ů´nę,4ą§ŕX.…‰;_›č Ć`Ra¬î ĂÉüŽöźutčÚ¶÷ H0Ţ|_OŹuÝćíq]îúAÁ"b??(z!ú¸“ÎbÖiŹ^Üű %T­Žĺw瀴ďÇĎc¦°ÇŰĄýg<ă-.Âýw\ďAňń°5¸ˇÉÖ•#P ¤gF[—nuUNŁTí^#ô x÷Đ„¸ â0&Y—zÂíŕ}PC'“–¦ÁsçzPfô|0 ŞZ5¤c`ˇíiUôîaaÚÝadln#"ëă^ŹEfíȡĹE˝ ÎŚ=ě°RôNëB¬ICBńŢŽĚ!ŤFăa»¸ÓĺDŚ2…»ë]¶§µ gň „‚·žŰŁŞ,pşÚŃeą\ëÚQň~ŐEîEÚ &Ű×Ŕ“{—#w<đ|XůĽĄ2™V.$ş×C!+ÔşźADŹěFScyĄ±Ph­'dł¦Ü›Mk4^‹Âr{ČĎCäć^ĐD÷€ÓaîÎ  _"ťnŤĚÜy "j$xo»đşzÝčOÜq§¬ĎA…S&şĂmn…ö^ ×pjŇr°Ż›J9Č@‹zýî¶O Ľ•ú˝-&¤Ű ř5i§;q×ŕQ‘vpĂýĎ» äQŻV3Ž&ţĎśŠ˝˝ÖÂW‘Vĺ)b="Ŕ1ŐPÄ Ćatpçr7W+•VD׳˝}ëtŠĎ§Ć(µÚĘaÍ[tł±ÝĎĹëvëËI±‰Ř|yŁ$ŃuľUS\\7f›wwë5µ#ŹţĹvDŁĆ*,rg mÉíňĺCšyňĹÉÝvfT(o4uÜ«dńDĺČř°ÜIt( 7 ®V-"ŕçŚdĄםŤĚčZ#é#-BO)ę D$"V mW*Ŕk]…ĂÄ>H }ľÖK{ׯ+ý!Ü÷튄LoŁ`1Ť“"­Ăsş»[=1”·«ŰçKt­ôµĐ3âę9íB/‹+4(&Ô24‡é|ž<|a*ĺť{V]>ż¤˝žňŔţ˛#Š";R5† MO[ťy¤ČŮ2H(ťVEŇet-}(Dh˝!ďŰÚRĺGt$bôď.ÉDô?´ u˘ w6»ëńçó6?ĺŢŢ݌ᡞYŕayjú\P䥒%ŽŽ¶ÎV9Čř´×@!ÝŢDwĐ3"Í÷Şý`aPşĎ[u&S±ĂovŻ5@ž<9¬±őH˘¦>«GĘg†ˇĽ+Ҩ}–ZÎé˙}>‘zM¤°)ę)eEäFÄGĘ€ŔŐäŹîçzýćÂÔäýýú{"‰žžÖ¦t/“ÓFqMNZž>ź·Nsĺă*ÓáDTŃĆbSa ŕ [t‘QŚúäÚÝĐ<ťnťJ(bu„[ ś^ôWôôhď©4"˛vqű;¸včZÚ?V÷˝wŠő‡ ]y~Ţ펽˝WafČťB;?/‹l:}ëČÓ“›Ĺe/¸ů"Ö´äA°éF]¤^)gDš 1…ßÉoŠ4Ş">żHeO¤Z DDšu‘jQ¤łG_ߨ‰DűD‚ť"Á¦H<%ŇŃ-rőçr$Ł»6 ÍfS|ަi˙˙a?»“ĎŁ°ĚŘĚĺeăľrŤą}”Íu.i"wČW˘]ĺ“/¨7­Âk(¦Łôë wĄ]Ô|Ć{Ú(÷ëBÝĆ=—©—±ŞĚŘf™űú\ŔĚ‘§¸ˇ8Ű›z\ ÄÚŻőnĎÓśv~d~ĆÝŠĎwwďűĽ3'č7ňäÎĺ‹ PżS©W>35‘JI¤ą«Šż^Ń(Á):éîW4EÄ'Ô×uö‰t$Eú;P$p›Tť˙‡í~éb^s7zą«cqăĆ yűí·ĺ…^łgĎJˇP·ŢzK¶··ĺw÷wĺţᤣŁCJĄ’Ôëu)‹ň­o}K’ɤÄb1YYY‘h4*?űŮϤ^ŻK$‘ÝÝ]ůťßůéé鑦4ui4’ĎűÄď?řföö¬Ű;—ke{uIóDn? ł=Eäľž4ł0 úN§íw‡ÉÝŚŕ<(5ăÎłÎç-`’ Q†[/qI˘Ť;‘ű1¨ç^”»+ŰŰ7{î Üš1~§rĐVOŽ–|ž1¶w#őŞH­(RÜÖ@DD|"Ą´F ľ€ţ;ء˙öÉgQCź*öŽŤş'?3 a}Ý}ą¶ĎŇŤ6kş»»+˙÷˙ţ_I&“ňÍo~SDD>üđCůŕä÷~ď÷ä“O>‘L&#•JE"‘¬¬¬Č+ŻĽ"gÎśżß/•JEňůĽ,--ÉĚĚŚ¤R)Y]]•_|QΞ={Ç×wW*" Éâ⢄B!9{ö¬DŁQi4˛łł#őW%Żżţş|ç;ß‘ŐŐU RŻ×ĺżţ×˙*ŰŰŰňÄOČ~đ™\.'•JER©”d29}ú´<˙üó˛łS+W~*ŁŁ=25uF¦¦&TíTÝÔźę í¶tˇŤóžďµŁőNÝěÔo\Ă ĹIűg}ž3ÉűI·}?äViź(ä=ńdżđ,"ő˛HĄ QBÓŃÁĹŤüA‘Ę®řD!‘¨ĂÚŕ‰ôśPŕ„ť™ >ßý3‡I©T’wß}WŠĹ˘ĚĎĎË«Żľş˙»@ ĺrYŢzë-ůĆ7ľ!ˇPHz{{eiiIŢ{ď=ů‹żř yţů祫ë˙oďÜ~›®˙?ţřô|Z×měČ6Řo ľ€"Qb¸.”/Đ[oLLL4ńßđžDŁ1¨WFŃD"ó@2‘pбC·µk»~zúôóů]Ľ?ź¶›¶Ń­Ýö~$$kË>y·kßĎľ_ĎסŽŮŮY\.ß~ű-ź}öYqß˝{÷.---¤R)<N§“t:˝z˘Ş*~żźĎLJ~ȱcÇĺĺ—_fbb‚‰‰ ĆĆĆčěědĎž=\ąr…™™čééáŤ7Ţ ©©‰ . ( ===\»vÍ<}@]ťŹ˙ýo/ťťmäóĘ’63ËďxXˇ’•Ťeâä`·—˛©R©RýÄbűrbőłłĄIuP[ţ->+‰ŮŁĹJ>KąÁ]žĺĄÖń•ŔJ®5‘žÄÂ(”„A×ÄĆo ĽuĘĽż ~¶™_°rsB!ľ&đn»PŔéRK¸Ýnžţy Ăŕ—_~Á(‹‹çr9t]§­­Ťü·ą=z”îîn^zé%b±™L†'N0<<Ěť;wđűýtttpôčQb±‘H„‘‘žzę)FFFčęęZÖĂXz›±\.G<§ľľž7nĐÝÝŤĂáŔçóałŮHĄRd2 Ă(†˛‚Á Éd’@ P|’ łťĄÝn'—Ë …P…‹/ŇŰŰGss+éôłw毥ÔcęQX}Ł,)ýiĺt§ÓĄaI+Ĺš)bmňĺó8,–z±ćeXˇ¦…żgUo†ÍŐę|[s™4’Ў›Ĺ›şŮx™Xf>:¨ÓBPÄÉŔ˘áp‹’aź-BQŔéćőzć矦żżź:sSÔuťX,†ĎçcҬ°mmmĹn·ărąČfł¨ŞŠ¦ix<˛Ů,~żź|>Ź˘(ĹëdłYR©~żźąą9ęëëq,#v˝,Ym.^ĽHGG­­­‹†&Ę7˙…5 ‹amÄV†ÓbĚWzXIuŃ2Ą DťBľĚkPlK‰ÓĂ+îÓóŕŰ(bă÷w?w»ÎĽßľúŢ‚¤rÔ”€d2Źn`Íú°f%,vv˛N(°´°‡5&´ÜłBQÖXZůíwőXÉünÉ*`Ď…o~Ał ćL\<žŠP>-NNź¸ßĐĹmßslp7xĹ Âĺ§xQ”ŇĎ’ÍCÍ Č㲀¬Ç­ţKŹ«3ť-µ±fd/•…ýb•Ůş.˛¦*](ö¨ŠWIőĐuńžzŘÇ5[‡6?-DČHK‹S„:%ÂLŮ„i<#ÄĂá­1,ÓÚŰ NŢFa<[^Ăfń$OFMżMʇD-ü°Z>‰5*6ZÜX/ź˙ń$…gÖµž4–S·T¬Öőµ“ްq(Żę_ «ń÷^ČbćłQ=’˛fO¤ôŚyŮyŞ!ŮB$‚ťŕ Ŕšäé–a$Iĺ¨i‡ĐËO V8a1q°BbOZ kç…X#{k­&c#N/m TĄŃ5q"0ŚR¤GËŠ gń:-ÂH3ź ü­"ĽÔÔgf$•żç•R]D˛Ô´€<ŞŇŘšw %Ylõ:ż®'ŹB†­VŹŠdA˙=.4źÓ13„ľ‚ž˙śţůżăô !0tqbđÔ‹“‡4ź%ëšňˇOŹcáđ¦‡aÍ_O"©2‹Ďš*„ =ęŚůţ+{OĺS 8Jćs!/ęÜő꾂û Úy‰ďa‰¤V©)±ćT’Ĺ| ÉćĆŞ|6 qBĐ5!éHKͧ7źíNŃ©ˇ×ĚD2ßWćli>K65őv÷ůDŐ8ĚŻ:¬n˝˛Ň|ăSn>ë9qRHM2Ď0 ĄĘgĂa&o ‹ŽŞ.s<°4ź%’ÇSSRNyŐů“ ‹Ô6Z¦Ô)5ÓX5˛xĺłŐiŐß"ç•÷)Ż|VlňÔ ‘¬”šýčČŤăbÍxÖó"őÔBË‚¶ÔÔ;Łffʼn@Q„`řšEĐľ–‡T>ŰĄÇ ‘¬&5+ ›•uSĺĽĂáˇâm]řÖý©`C:SóiłuŚ*Ň5ńscŻ'Ż…9ŹÁ¬|–UĎIm°áD×…9żRckĄĘy!†yZ°Č§…±¬Ě’!Ģ+}ű·ŇVÝ!ń¸ÍţfpřDşŞŰL§uxeI"YŻlŠŹn-ÍV~kQĺlaNĘÄn)§ş"M5ń€b+mˇĄÜś¨n! ®€8EŘś˘ČÍß,N 6Wéwíń¸D"٬[Yj/,›­2UčëC˝‘ ]´ÁČĄĚ&zIMćk“Ž–˛Ś 9´A •yĆł3P(M|“H$›—u+ ĺ•č ÉdD#ÄŤ´ÁYĆł…Uůlč"]UK˙×|Ö5qrp…÷ŕm§čŘ[jŞWś÷l“UωdéÔ”€,§yŕŁ*ѳهĎ9ŻEôóÄAËŠŤßJQ-5 ޞîŔVĺłUěćŰ ˙'ş«_OŔá>¬z–H$•¦¦dnŚľ'żNů )U§‘jd6ÍgC„ 9‘Žš›3żé›Mô2qSLď ź·ť>‘˘ęđA]Giś§¬|–H$µ@íl?†YPáć«Ń˝5O |CtU-hź"ˇ8DXÉ2źóŞ!ˇÓ‚Ăm>Ušv ŹÁzî˛ňY"‘¬jG@Ńj;‘„¶öĘ]vąRČŠ“ź!Ąx2Č&Dď¤lRś¬aÖľfłĺvK©¶Á2źEf$I$’ŤEí9j9SÖD6- Z®”4¤ëćçŕď6! Ö Ç0Äě]˙¬qž DÂî·uMlú–Ż ŘD#=wP¤±–#Íg‰D˛Ů¨ÁěK`¶u7t‘rZ.JG…`dÂ?°;ĹĎ®@ɰz#9}ż ©űf:«Ů$Ď0ŻíoH–ůlkpůKb EA"‘H§vD›ü˝_Am‚®‰’§<ł¨ BD®±éŰ]ćgď7{C‡ć=BŚLÓY†‘$‰¤"ÔŽ€ „"Ř ťű…€` ßľÂM_±Íź'‘H$’ĘQSÜ Ť;DQ›D"‘Hjá—H$ÉŠ"‘H$’!D"‘H$+B D"‘HV„‰D"‘¬) k@,cxx¸ÚËX†‡‡‰ĹbŐ^FĹI$Üľ}»Ú˨8†apíÚ5rą\µ—RqR©7oެö26Kx<Îýű÷ÉçĹ\SUUfbb‚Bˇ@$a||ś±±1@láp‘‘ćććŠ×ŃuĂ0śśdrr’h4J8F_/#WH*•"‰T{«B$!•JU{'ťN399Yíe¬ ccc …'żPŤ‘ÍfŻö2Ö„Bˇ@8&Ťâ‹ÁÄÄĂĂĂd2‰SSSÜ˝{UUÉçóŚŤŤ122ÂřřxqĎ5 ]×Éd2Ü»wŹd2Éýű÷I$KZÇ’ę@ţůçľůć^ýužyćt]çÜąshšFww7źţ9‡bzzšW^y…Ź>úL&CSS'Nś ™L˛}űvhmm寿ţ"›ÍâńxĐ4Ť7ß|“]»vˇiCCCĚÎÎb¬FÝ*`łŮ'‰ĐŘظˇÄŇfł‡Éfł¨Şşaž›˘(ĚĚ̇ąuëÖ†y^ 6ŚpëÖ-Ün÷“_°FP…x<Îčč(7oŢÜ0ű‡E4E)č“H$řꫯČĺrĽűî»Řív˘Ń(ź|ň §OźćěŮłx˝^ü~?]]]8ľţúk|>ýýýôőő‘Édhhh```€P(Äŕŕ ŹEQčęęâí·ß~ěş–$ ÍÍÍx<fff˘˝˝ťX,ĆáÇůá‡ńěěěäĎ?˙dvv–ŁGŹât:ůôÓOq8:t¶mŰF$AQ:::ř÷ß‹ß`÷îÝËŘŘ؆;^‡B!‚Á Ůl¶ÚK©8;věŔfłm¸çć÷űŮ˝{÷†{^@Q”bDaŁŕńxŘ»wď†Ű? Ă`˙ţýř|ľâ}.—‹ÎÎN ‡Ăx<’É$===Ü»wʉ‰ vďŢŤÝnÇĺr100€ĎçăÔ©Sś;wŽóçĎłmŰ6ÚÚÚ¸té;wî$™Lâv»qą\K>É)ƤúĆŤÜąs‡ľľ>ľűî;^{í5Âá0ýýý¤R)ţřăâń8íííěßżź[·n‘Íf‰Ĺb´µµ1>>Ξ={8ťN®\ą‚aA"‘/ľř"Í:¸\"‘H–‰ŞŞ\şt‰P(Äřř86›ŤíŰ·SWWÇÖ­[ąqăCCC†ÁÓO?M ŕęŐ«ĹSŚÍfŁP(pđŕA2™ ŞŞ288ČöíŰ§ŁŁýű÷?vKr …öUď—Ď癞ž¦±±qCŻ-¨®®Žl6‹ÝnGQrą[¶l™w$]ʤÓirąétš`0ČÜÜ^Ż—şşőŰ‹Ć0 ¦¦¦p»ÝEźŔét’N§inn^׳D"A&“!H$…BÄb±u˙yK&“8ťN˛Ů,ů|Ż×ËÜÜÁ`ŮŮYšššpą\Ő^ć˛Éçó¨ŞZŚţ444ŹÇ©««#ťNăv»©««+†Xm¶µĎ‰Zv/¬Ő€sçÎńý÷ßóÜsĎqúôé51*Í˝{÷řňË/I$hš†aŘívrąďĽó]]]Ő^âŠI&“|đÁLMMá÷űq8hšFSSď˝÷GMµY[2CCCś9s†}űö144D.—+ Č[o˝E__f.W]×ůřăŹyđŕ@€d2‰ÇăAUUž}öŮuűyÓ4Ť3gΰwď^®^˝ĘÔÔʇL&Çă!•JqěŘ1Nť:UíĄ.›ß˙ťË—/säČÎź?O.—+~Őuť@ Ŕűďż_UńŻ©Oůää$333LLLT{)Áď÷Ł( ---üý÷ßhš†¦iĹoHë™ .pűöm’É$ÍÍÍĚÎÎâ÷űÉĺr …u+ ‰D‚x<Îŕŕ ÓÓÓd2|>Éd˛ÚË[1…Bh4J:ťćÁx˝^˘Ń(őőőëúóćp8đz˝Ĺ¤‡d2ÉÜܡP¨řüÖk6]0$‘Hŕóůp8ŘívÂá0©T ŹÇC0$źĎK±xá…p88p ÚK©ů|žÖÖVöěŮömŰpą\Řl6TUĄ»»»ÚË{"Ž?ÎÖ­[EUUÚÚÚšš˘ąąy]† ,věŘÁÉ“'‹IŠ˘ŕrąH$ôööV{y+Ćáppüřqfffhjjbrr’ŽŽFGGŮ·o_µ—·b˛Ů,íííx<^}őUTU% 155E[[ŁŁŁ|ţţ~ěv;š¦a·Űą{÷.—/_¦§§EQä D˛é‘&şdS2::Ęŕŕ`±j·ŁŁŁX‘Édhoo/f& ®_żNcc#ׯ_gßľ}D":´ŞuQI­#D"‘H$+â˙»tçĐ?=§%tEXtdate:create2010-10-22T00:54:40-07:00űúK%tEXtdate:modify2010-10-22T00:54:40-07:00Ч°÷IEND®B`‚libtorrent-rasterbar-1.1.13/docs/projects.html000066400000000000000000000244631351156116000214230ustar00rootroot00000000000000 projects using libtorrent

projects using libtorrent

These are some of the public projects that uses libtorrent. If you want your project listed here, let me know.

Wyzo

wyzo is a media browser with built-in bittorrent support.

deluge

deluge Torrent is a more full-featured yet still lightweight bittorrent client. It has the ability to automatically resume partial downloads and background to the system tray.

qBittorrent

qBittorrent is a QT bittorrent client available for linux (likely portable to most other desktops as well). Written by Christophe Dumez.

tonidoplug

Tonidoplug is a tiny, low-power, low-cost home server and NAS device powered by Tonido software that allows you to access your apps, files, music and media from anywhere.

Folx

Folx is a torrent client and download manager for Mac OS X. The Free version of Folx has all the basic functionality of the torrent client, which allows users to download and create torrent files. Folx PRO (available for a small fee) features the possibility to search for torrent files just from Folx interface. So there is no need to browse through multiple torrent trackers searching for particular file.

Miro

Miro is a free application for channels of internet video (also known as video podcasts and video rss). Miro is designed to be easy to use and to give you an elegant fullscreen viewing experience.

MooPolice

MooPolice is a windows bittorrent client with a unique look.

LeechCraft

LeechCraft LeechCraft is a free open source cross-platform extensible software, which primary goal is support of file sharing networks and protocols like HTTP and FTP

Free download manager

FDM is a powerful, easy-to-use and absolutely free download accelerator and manager. Moreover, FDM is 100% safe, open-source software distributed under GPL License.

btg

btg is a unix bittorrent client which is run as a daemon. It has multiple user interfaces which connects to the daemon. One GUI (Gtkmm), one terminal interface (ncurses) and one web interface (accessable through a web browser). Written by Michael Wojciechowski and Johan Strom.

electric sheep

electric sheep is a screensaver which collectively generates animations and lets the users vote which one to live on.

Tvitty

tvitty is a bittorrent client for Vista Media Center, which allows searching and downloading of torrents directly on your TV.

hrktorrent

hrktorrent hrktorrent is a light console torrent client written in C++.

FatRat

FatRat is an open source download manager for Linux/Unix systems written in C++ with the help of the Trolltech Qt 4 library. It's simple to use and install.

halite BitTorrent

Halite is a windows bittorrent client controllabel via an xml-rpc interface.

Arctic Torrent

Arctic Torrent is a light-weight bittorrent client for windows. Written by Cory Nelson.

bubba

Bubba is a mini-sized server, designed to fit your home better than an always running PC. Boasting Torrent downloader, DAAP streaming, Web, E-mail, printer and FTP server etc.

tvblob

The BLOBbox represents the ability to harness all of the content available on the web, without any filtering or pre-selection by a third party just like surfing the web.

This means that anyone will have the ability to reach viewers via the Internet directly on TV, without them having to connect a PC.

Flush

Flush is a GTK-based BitTorrent client.

Lince

Lince is a bittorrent client using libtorrent to handle bittorrent protocol and gtkmm for the interface, it has been designed to be a light and full featured client.

Linkage

Linkage is a gtkmm client that aims to be middle weight.

Bitfox

Bitfox is a firefox plugin integrating bittorrent downloads in firefox.

BitSlug

BitSlug is a MacOSX cocoa client.

DelCo

DelCo is a research project at Tampere university of technology, finland.

Torrent2Exe

Torrent2Exe Torrent2exe is a small BitTorrent client. Its basic idea is to let users download a custom-built EXE program with the torrent file integrated into it.

ZyXEL NSA-220

ZyXEL NSA220 makes it easy to store, protect and share files between users on your home network. The built-in DLNA server works with many set top boxes to allow you to play back music, watch video files, or view photos on your home theater system, while the built in download manager can automatically download video and audio podcasts as well as allow you to download bittorrent files without needing to leave your computer on.

libtorrent-rasterbar-1.1.13/docs/projects.rst000066400000000000000000000135401351156116000212610ustar00rootroot00000000000000projects using libtorrent ========================= These are some of the public projects that uses libtorrent. If you want your project listed here, let me_ know. .. _me: mailto:arvid@libtorrent.org Wyzo ---- wyzo_ is a media browser with built-in bittorrent support. .. _wyzo: http://www.wyzo.com/ deluge ------ `deluge Torrent`_ is a more full-featured yet still lightweight bittorrent client. It has the ability to automatically resume partial downloads and background to the system tray. .. _`deluge Torrent`: http://deluge-torrent.org/ qBittorrent ----------- qBittorrent_ is a QT bittorrent client available for linux (likely portable to most other desktops as well). Written by Christophe Dumez. .. _qBittorrent: http://www.qbittorrent.org/ tonidoplug ---------- Tonidoplug_ is a tiny, low-power, low-cost home server and NAS device powered by Tonido software that allows you to access your apps, files, music and media from anywhere. .. _Tonidoplug: http://www.tonidoplug.com/ Folx ---- Folx_ is a torrent client and download manager for Mac OS X. The Free version of Folx has all the basic functionality of the torrent client, which allows users to download and create torrent files. Folx PRO (available for a small fee) features the possibility to search for torrent files just from Folx interface. So there is no need to browse through multiple torrent trackers searching for particular file. .. _folx: http://www.mac-downloader.com/ Miro ---- Miro_ is a free application for channels of internet video (also known as video podcasts and video rss). Miro is designed to be easy to use and to give you an elegant fullscreen viewing experience. .. _Miro: http://getmiro.com MooPolice --------- MooPolice_ is a windows bittorrent client with a unique look. .. _MooPolice: http://www.moopolice.de LeechCraft ---------- LeechCraft_ LeechCraft is a free open source cross-platform extensible software, which primary goal is support of file sharing networks and protocols like HTTP and FTP .. _LeechCraft: http://leechcraft.org/ Free download manager --------------------- FDM_ is a powerful, easy-to-use and absolutely free download accelerator and manager. Moreover, FDM is 100% safe, open-source software distributed under GPL License. .. _FDM: http://www.freedownloadmanager.org/ btg --- btg_ is a unix bittorrent client which is run as a daemon. It has multiple user interfaces which connects to the daemon. One GUI (Gtkmm), one terminal interface (ncurses) and one web interface (accessable through a web browser). Written by Michael Wojciechowski and Johan Strom. .. _btg: http://btg.berlios.de// electric sheep -------------- `electric sheep`_ is a screensaver which collectively generates animations and lets the users vote which one to live on. .. _`electric sheep`: http://electricsheep.org Tvitty ------ tvitty_ is a bittorrent client for Vista Media Center, which allows searching and downloading of torrents directly on your TV. .. _tvitty: http://tvitty.com hrktorrent ---------- hrktorrent_ hrktorrent is a light console torrent client written in C++. .. _hrktorrent: http://50hz.ws/hrktorrent/ FatRat ------ FatRat_ is an open source download manager for Linux/Unix systems written in C++ with the help of the Trolltech Qt 4 library. It's simple to use and install. .. _FatRat: http://fatrat.dolezel.info halite BitTorrent ----------------- Halite_ is a windows bittorrent client controllabel via an xml-rpc interface. .. _Halite: http://www.binarynotions.com/halite-bittorrent-client Arctic Torrent -------------- `Arctic Torrent`_ is a light-weight bittorrent client for windows. Written by Cory Nelson. .. _`Arctic Torrent`: http://www.int64.org/arctic.html bubba ----- Bubba_ is a mini-sized server, designed to fit your home better than an always running PC. Boasting Torrent downloader, DAAP streaming, Web, E-mail, printer and FTP server etc. .. _Bubba: http://excito.com/bubba/about-bubba.html tvblob ------ The BLOBbox_ represents the ability to harness all of the content available on the web, without any filtering or pre-selection by a third party just like surfing the web. This means that anyone will have the ability to reach viewers via the Internet directly on TV, without them having to connect a PC. .. _BLOBbox: http://www.tvblob.com Flush ----- Flush_ is a GTK-based BitTorrent client. .. _Flush: https://sourceforge.net/projects/flush/ Lince ----- Lince_ is a bittorrent client using libtorrent to handle bittorrent protocol and gtkmm for the interface, it has been designed to be a light and full featured client. .. _Lince: http://lincetorrent.sourceforge.net/ Linkage ------- Linkage_ is a gtkmm client that aims to be middle weight. .. _Linkage: http://code.google.com/p/linkage/ Bitfox ------ Bitfox_ is a firefox plugin integrating bittorrent downloads in firefox. .. _Bitfox: http://code.google.com/p/bitfox/ BitSlug ------- BitSlug_ is a MacOSX cocoa client. .. _BitSlug: http://bitslug.sourceforge.net/ DelCo ----- DelCo_ is a research project at Tampere university of technology, finland. .. _DelCo: http://delco.cs.tut.fi/ Torrent2Exe ----------- Torrent2Exe_ Torrent2exe is a small BitTorrent client. Its basic idea is to let users download a custom-built EXE program with the torrent file integrated into it. .. _Torrent2Exe: http://torrent2exe.com ZyXEL NSA-220 ------------- ZyXEL_ NSA220 makes it easy to store, protect and share files between users on your home network. The built-in DLNA server works with many set top boxes to allow you to play back music, watch video files, or view photos on your home theater system, while the built in download manager can automatically download video and audio podcasts as well as allow you to download bittorrent files without needing to leave your computer on. .. _ZyXEL: http://us.zyxel.com/Products/details.aspx?PC1IndexFlag=20050125090459&CategoryGroupNo=758BFE64-3A95-463C-9E1E-3D30E3B58D9C libtorrent-rasterbar-1.1.13/docs/python_binding.html000066400000000000000000000334471351156116000226070ustar00rootroot00000000000000 libtorrent python binding

libtorrent python binding

Author: Arvid Norberg, arvid@libtorrent.org

building

Building the libtorrent python bindings will produce a shared library (DLL) which is a python module that can be imported in a python program.

building using setup.py

There is a setup.py shipped with libtorrent that can be used on windows. On windows the setup.py will invoke bjam and assume that you have boost sources at $BOOST_PATH. The resulting executable is self-contained, it does not depend any boost or libtorrent dlls.

On other systems, the setup.py is generated by running ./configure --enable-python-binding.

To build the Python bindings do:

  1. Run:

    python setup.py build
    
  2. As root, run:

    python setup.py install
    

building using boost build

To set up your build environment, you need to add some settings to your $BOOST_BUILD_PATH/user-config.jam.

Make sure your user config contains the following line:

using python : 2.3 ;

Set the version to the version of python you have installed or want to use. If you've installed python in a non-standard location, you have to add the prefix path used when you installed python as a second option. Like this:

using python : 2.6 : /usr/bin/python2.6 : /usr/include/python2.6 : /usr/lib/python2.6 ;

The bindings require at least python version 2.2.

For more information on how to install and set up boost-build, see the building libtorrent section.

Once you have boost-build set up, you cd to the bindings/python directory and invoke bjam with the apropriate settings. For the available build variants, see libtorrent build options.

For example:

$ bjam dht-support=on link=static

On Mac OS X, this will produce the following python module:

bin/darwin-4.0/release/dht-support-on/link-static/logging-none/threading-multi/libtorrent.so

using libtorrent in python

The python interface is nearly identical to the C++ interface. Please refer to the library reference. The main differences are:

asio::tcp::endpoint
The endpoint type is represented as a tuple of a string (as the address) and an int for the port number. E.g. ('127.0.0.1', 6881) represents the localhost port 6881.
libtorrent::time_duration
The time duration is represented as a number of seconds in a regular integer.

The following functions takes a reference to a container that is filled with entries by the function. The python equivalent of these functions instead returns a list of entries.

  • torrent_handle::get_peer_info
  • torrent_handle::file_progress
  • torrent_handle::get_download_queue
  • torrent_handle::piece_availability

create_torrent::add_node() takes two arguments, one string and one integer, instead of a pair. The string is the address and the integer is the port.

session::apply_settings() accepts a dictionary with keys matching the names of settings in settings_pack. When calling apply_settings, the dictionary does not need to have every settings set, keys that are not present are not updated.

To get a python dictionary of the settings, call session::get_settings.

For an example python program, see client.py in the bindings/python directory.

A very simple example usage of the module would be something like this:

import libtorrent as lt
import time
import sys

ses = lt.session({'listen_interfaces': '0.0.0.0:6881'})

info = lt.torrent_info(sys.argv[1])
h = ses.add_torrent({'ti': info, 'save_path': '.'})
print('starting', h.name())

while (not h.is_seed()):
  s = h.status()

  print('\r%.2f%% complete (down: %.1f kB/s up: %.1f kB/s peers: %d) %s' % \
    (s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \
    s.num_peers, s.state), end=' ')

  alerts = ses.pop_alerts()
  for a in alerts:
    if a.category() & lt.alert.category_t.error_notification:
      print(a)

  sys.stdout.flush()

  time.sleep(1)

print(h.name(), 'complete')
libtorrent-rasterbar-1.1.13/docs/python_binding.rst000066400000000000000000000072351351156116000224470ustar00rootroot00000000000000========================= libtorrent python binding ========================= :Author: Arvid Norberg, arvid@libtorrent.org .. contents:: Table of contents :depth: 2 :backlinks: none building ======== Building the libtorrent python bindings will produce a shared library (DLL) which is a python module that can be imported in a python program. building using setup.py ----------------------- There is a ``setup.py`` shipped with libtorrent that can be used on windows. On windows the setup.py will invoke ``bjam`` and assume that you have boost sources at ``$BOOST_PATH``. The resulting executable is self-contained, it does not depend any boost or libtorrent dlls. On other systems, the setup.py is generated by running ``./configure --enable-python-binding``. To build the Python bindings do: 1. Run:: python setup.py build 2. As root, run:: python setup.py install building using boost build -------------------------- To set up your build environment, you need to add some settings to your ``$BOOST_BUILD_PATH/user-config.jam``. Make sure your user config contains the following line:: using python : 2.3 ; Set the version to the version of python you have installed or want to use. If you've installed python in a non-standard location, you have to add the prefix path used when you installed python as a second option. Like this:: using python : 2.6 : /usr/bin/python2.6 : /usr/include/python2.6 : /usr/lib/python2.6 ; The bindings require *at least* python version 2.2. For more information on how to install and set up boost-build, see the `building libtorrent`__ section. .. __: building.html#step-2-setup-bbv2 Once you have boost-build set up, you cd to the ``bindings/python`` directory and invoke ``bjam`` with the apropriate settings. For the available build variants, see `libtorrent build options`_. .. _`libtorrent build options`: building.html#step-3-building-libtorrent For example:: $ bjam dht-support=on link=static On Mac OS X, this will produce the following python module:: bin/darwin-4.0/release/dht-support-on/link-static/logging-none/threading-multi/libtorrent.so using libtorrent in python ========================== The python interface is nearly identical to the C++ interface. Please refer to the `library reference`_. The main differences are: asio::tcp::endpoint The endpoint type is represented as a tuple of a string (as the address) and an int for the port number. E.g. ``('127.0.0.1', 6881)`` represents the localhost port 6881. libtorrent::time_duration The time duration is represented as a number of seconds in a regular integer. The following functions takes a reference to a container that is filled with entries by the function. The python equivalent of these functions instead returns a list of entries. * torrent_handle::get_peer_info * torrent_handle::file_progress * torrent_handle::get_download_queue * torrent_handle::piece_availability ``create_torrent::add_node()`` takes two arguments, one string and one integer, instead of a pair. The string is the address and the integer is the port. ``session::apply_settings()`` accepts a dictionary with keys matching the names of settings in settings_pack. When calling ``apply_settings``, the dictionary does not need to have every settings set, keys that are not present are not updated. To get a python dictionary of the settings, call ``session::get_settings``. .. _`library reference`: reference.html For an example python program, see ``client.py`` in the ``bindings/python`` directory. A very simple example usage of the module would be something like this: .. include:: ../bindings/python/simple_client.py :code: python :tab-width: 2 :start-after: from __future__ import print_function libtorrent-rasterbar-1.1.13/docs/read_disk_buffers.diagram000066400000000000000000000024001351156116000236560ustar00rootroot00000000000000 "copy into peer's" "encrypt in place" +------------------+ "send buffer" +---------------+ "(no copy)" +---------------+ | "receive buffer" +----------------->| "send buffer" +------------------>| "encrypted" | | | | | | "send buffer" | +------------------+ +---------------+ +---------------+ ^ | | "read() from file" "write() to socket" | | "(copy)" "user space" "(copy)" | - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - | "kernel space" | | v +-------+-------------+ +-----------------+ | "kernel page cache" | | "socket kernel" | | | | "buffer" | +---------------------+ +-----------------+ libtorrent-rasterbar-1.1.13/docs/read_disk_buffers.png000066400000000000000000000065641351156116000230550ustar00rootroot00000000000000‰PNG  IHDRsŁŻýĐź ;IDATxśíÝŰ’«(P{Ş˙˙—3©qhŔ-*Ţ×z8•öD$Qp‡›?źĎg€Íţ9;<„Č€>D–ô!˛ ‘%},čCd @"KúYĐÇďŮ€•~~~†a"5ű†˝ťžxŞĆµ˝ ~SřR–ˇ…Č’»şE-‹LÂSm/€ßŇř‰,ŮŃXŹő{ş%«¬Ó-Ůë©dÓw¦/Ň÷T7Vď7e~Z>Ez,˙ŮQ4~đleůÍJĺĐPv˛2AéžĘLśź©]¦ŢSýŤ[ŕ%Śłd/eH—m+Ür˨Z)>źt{¶{vçß\ 1«Ç ň\Ý2uÜňŰ(3ĎP-ÝY©ś-;A —îRK-Ńr¬ŮOÚ¸ŢC›%»{@ UŢ‚;ÓT4\ŢrŕaٍĆß×ô-Ëqlú%¦äťD–ěnQ,÷oÉŔ]n„Y“ĆŤnĄĐ¨ńŞꄥ5Ŕń?Ő¦zđËĚ(㼓Ű;jˇż'Hv|ۢA]Ť)—‰Le»eeű.¸©E Űk€©ňµtŘôҡ–í}ŮŐhظj^KdÉ™ŞUü‰ýĹşŞátŐ߇ǔJ5l'˛ä4AóŔ‰a剀—;·ĺO ],čĂ ®¨e1ËöŃýĂô. ŻëĚN»NNŕU¬gÉ÷ö[EąH^°l°ÝĄ‚ąęôm`oÚ,ŮQ<’rvBwKâźÚ“{ŇŤÁ–ˇh¬¦ąâĂ Ą%·qą†8ťę^S“ľË×ăľJ1I›%{)ź:łô)±l÷ę˙¦G/·4>MXj¶tŻ~ÂM¶ZdµPxáDÚ,uТ±ÖţLƉ‘ÁéĎî˘ §ăUzĂŮËĎľ¦}ĐéöLÖS6őü´áܫ|]n)óÍĆ-ŁMĘ÷Lí5µei®€ľ~˛b©s‘s…<ÉŠłyŔPŤM«¦Kń\NÁc,:•Ú,čć°'ĺLőB– ę*«mśŤ»h„MĽ¤Ĺş  Š­ áÁĚg»ĆCU°ÎU"ËŤ§­}EÜ–Á:ŔőĄ^…lg±ýuÖG–Ő5Ą[~ýg[ŇÓ6µęu5ťĄą˛z6l´GÔ•ŐĂß•ľĄkW©`'ÝkEŞ0f)dŞ3D§b 1ĺjëÇY¶¬5ݲeś2ś&›©žţ8WŐtŇ%vł ә˳źŢé[L~Lkójˇ+_ K Ł vŇ·6XZđgc‰1“鋬ŘĹ\ߢCořX#§çŁúžŃltX‘}äŕS¸Ôü’c´±ţýXN˙ň®v.¸¬íŃXăĹD[¸Ô{é?Îr¶Ńqę=ëRŢÉăď[řrúý¦/Ű ş$Ű×Őňs¤7vő­ ¶né0•r4Ű­_ϲz>â!SŐ÷”Ă#Ş™‰OHđ¦ŽäĐ•·č"á‘â»Čęjd ŕgbŕuşcślą=ŁB()ďÄvŞ f ~µlféďiy[\ɼͲSą:˛äť\$oÖŇ2á yg“)—­ ŞCk\Ć-:•WYu¸>µ3đuÁÚ č#őéH"KŕövšŮĂR"Kஂ);ëfóL ٤‘ç†w„}K祋bËdÓf ś,^[¦:U|(fplj<ŽëÚ,€3ĄÁYőą8ĺ‹aÉĽďl÷ę˙¦G/·Ě>híG ‘%p¦´ 2m†ĽÚsqŢü8®vzĂ€“y×chłÎôóźďźitÖŠ™Ę†?–o«—,÷*_—[ĘüÇ}9m–Ŕ™âáŹŮźéö¸qęg›凌6KúYЇČ€>D–ô!˛ Ź|n¸ÉóŔFŞŕKmđBő'ŔRzĂčCd @"Kúxcdi@1\–Űô­˝1˛`Ż‹,żż„üčîu‘%;yidůů|4[ôőŇČ€îŢYţüü˙Ě!Í–}˝+˛`?ďŠ,ł‡¤{f:@GďŠ,ŘŹČ€>~ĎÎŔ“Ť3„Ňn÷tQúzi:;ĺđ»ĺűg:ĂÉČ ‹´’)˙Św\Ty¶§ĽE{ý|L~ŕtÚ,wTÖ YmŘ2?}¬Śv +ł”Ó?÷8(đrë*–•ç1T’Ńf9c żŇ¦SM}Ő-YRŐCLŐMă.Y6‚üdGYšrĽ×°g*p_ßzlüwÜ>f#ŐŢ’ :M:ő_-);¶ä'K'8V5?đHÚ,g¤•ÂÔëxËöŁÉŻüŮüTkşĆ”őć\¤Á¸ ±>I+Ť˛’)+˘^•Ěş”Óp°˝ÂO÷Ť«čę—#˛l•vCř»ö”ü,úߍĔ@) ĽR+ŞŁł*™´†źÍC(ʉôÍÜ‚Ţđ•Ҥڡü`~p™íŕ~Se¬lžŚ“ÍîŮ`ĐĆáIšć&?LăŚěńÍăë©ËęoôęxÇjouăź2Ů©A?Ů€§júqĘSÚ‡co“Ő9-;ńÄáo-·¨ňß(xĽW5ŮĎô÷Ůšdݦąš7žĽs/ŮĄ g0¨<ßÄů˝5ă,ŹÖŇW@Fĺ · ˛lµÇĘ—ŚËŢCU /'˛l˛SËü{&ýo ŞĚ ź¬y^—LŽůL¬˛®g‡vŮtŮŮÁ$©Ä«‚YíKIó`ŞJ`ĐfąÂě¹銸ĂDm¨~¤Ł–ĺť«‹6Çé”Wr5µöĄ¤yU%Ľ“Čr˝r™\8ĹÔňÎŐ¨.X¶şe™č ^ĚJ„’/U%ĽŠŢđő­v;Ű [T—w^qĄÍ64ĎČJ„ëś/U%ĽĘ‡­¬¬“íҸpnüžŐ™¸9°z¶,q߲2ü\€ňjÇó¨*éÂéľµ7žĽu—ě˘˝ŞżŃËŤ ·¨µ U%=8ă·öĆ“·÷%;;KnA»#»RU2EdykoD–ô!˛ ‘%}ü¶,ŚWµÓŽÁľ«wÜâ ßĎ–/Ö÷ě8»ŻďÇ÷ě8»ŻďÇ÷ě8»ďĄľźŐîőýĽÄ§_™t—ĺ6}kzĂčCd @"KúYĐÇďŮ*ľs÷^îŔyúŇf ďňů|”ěD›%\ZÚx967¦~>ź¬2kďĚöZtÜŞ ĺ–-<6K¸®2¬ ^Ź¦Ű«iăqÇdÇ 0Nąe Ď&˛„KËšúŞńŮlsŕҨ.mqśÚ7łw1%Ŕ{č ‡KËžE±®OyĹ^ł ŤŐľřěżV€›YÂu}ă¶4†Kű©Ç×Á“Đ‚˝†éčSeůţ,in«GoýäÜÓÍ餰Ô~« dܦoÍ8K`FÚ0ynN¸8˝áŔ Ť4Ňf @"KŕĘ)†ÜŽČ€>D–Ŕ%Ś+Ł™~_"KúYЇČ€>D–ŔUŚĎ†=;#¬$˛ ‘%p!,oMd @"KúYĐÇź5î=Ł“X°÷wľ¸ŐţH(©ővĺţÎ1·Z˝áô!˛ ‘%}äă,Ű}{ë÷·‘Ž¸Ĺ¸1Ă·Č-ôŐ~ڇ]ď§·‹.nęŠm–źĎç^§ü^ą€ľzÝw˝źŢ.ş¸©őm–Łô÷GökcüŻě‡Bö“eéo”lrSN–4'Á±¦Ň™ÍóĆĎWP6ď5އ†¨`hëö<>ş ‹­m–ĺ‰^Ź¦Ű«—NlĽDât¦ÂĘřXA:łyŢřąŕti@6uOźşÇµDiĘqŽ.č˘CoxőÇJüžŇöłŢ~ő´ÇŻß4”ËÜEÚć7{+ď-QÁ˘lé]°TźŢđôÔ®ksŢŢR]mßr¬ '˝|ĎĆcŔu”ŤSĘű`cT0{˝HtÁRůÓŰĎAuäbuÄĂřgąWµCąĽË\C@Z20•l–xKĘíź«z,ľ]{,˛âţ>jgŮTw¬ý¬čâŮŽąŐ®Ź,ĎŐ2řwÝ›9ĹŤ®=€ŰQÇ2utč ?^:ą,nH_+Q{»edŮ&Š&ŽtËČŘIű˛öE.y‘%°XuVř ¦|=‘% ćh§-CŰüń>GąâsĂ€se¶©ţďPľŤ6K —öYg&Ó牏ŻË-ă^Ő÷řQ8”6KŕńŔĘ-{iż|m–ô!˛ ‘%},čCd @ůÜp Ŕó¸żsŚúS›`)˝áô!˛ ‘%},čCd @"KúYЇČ€>D–ô!˛ ‘%},čCd @˙®0`´köFIEND®B`‚libtorrent-rasterbar-1.1.13/docs/reference-Alerts.html000066400000000000000000004432361351156116000227630ustar00rootroot00000000000000 reference-Alerts.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Alerts

Table of contents

The pop_alerts() function on session is the main interface for retrieving alerts (warnings, messages and errors from libtorrent). If no alerts have been posted by libtorrent pop_alerts() will return an empty list.

By default, only errors are reported. settings_pack::alert_mask can be used to specify which kinds of events should be reported. The alert mask is comprised by bits from the category_t enum.

Every alert belongs to one or more category. There is a cost associated with posting alerts. Only alerts that belong to an enabled category are posted. Setting the alert bitmask to 0 will disable all alerts (except those that are non-discardable). Alerts that are responses to API calls such as save_resume_data() and post_session_stats() are non-discardable and will be posted even if their category is disabled.

There are other alert base classes that some alerts derive from, all the alerts that are generated for a specific torrent are derived from torrent_alert, and tracker events derive from tracker_alert.

Alerts returned by pop_alerts() are only valid until the next call to pop_alerts(). You may not copy an alert object to access it after the next call to pop_alerts(). Internal members of alerts also become invalid once pop_alerts() is called again.

alert

Declared in "libtorrent/alert.hpp"

The alert class is the base class that specific messages are derived from. alert types are not copyable, and cannot be constructed by the client. The pointers returned by libtorrent are short lived (the details are described under session_handle::pop_alerts())

class alert
{
   time_point timestamp () const;
   virtual int type () const = 0;
   virtual char const* what () const = 0;
   virtual std::string message () const = 0;
   virtual int category () const = 0;

   enum category_t
   {
      error_notification,
      peer_notification,
      port_mapping_notification,
      storage_notification,
      tracker_notification,
      debug_notification,
      status_notification,
      progress_notification,
      ip_block_notification,
      performance_warning,
      dht_notification,
      stats_notification,
      session_log_notification,
      torrent_log_notification,
      peer_log_notification,
      incoming_request_notification,
      dht_log_notification,
      dht_operation_notification,
      port_mapping_log_notification,
      picker_log_notification,
      file_progress_notification,
      piece_progress_notification,
      block_progress_notification,
      all_categories,
   };
};

timestamp()

time_point timestamp () const;

a timestamp is automatically created in the constructor

type()

virtual int type () const = 0;

returns an integer that is unique to this alert type. It can be compared against a specific alert by querying a static constant called alert_type in the alert. It can be used to determine the run-time type of an alert* in order to cast to that alert type and access specific members.

e.g:

std::vector<alert*> alerts;
ses.pop_alerts(&alerts);
for (alert* i : alerts) {
        switch (a->type()) {

                case read_piece_alert::alert_type:
                {
                        auto* p = static_cast<read_piece_alert*>(a);
                        if (p->ec) {
                                // read_piece failed
                                break;
                        }
                        // use p
                        break;
                }
                case file_renamed_alert::alert_type:
                {
                        // etc...
                }
        }
}

what()

virtual char const* what () const = 0;

returns a string literal describing the type of the alert. It does not include any information that might be bundled with the alert.

message()

virtual std::string message () const = 0;

generate a string describing the alert and the information bundled with it. This is mainly intended for debug and development use. It is not suitable to use this for applications that may be localized. Instead, handle each alert type individually and extract and render the information from the alert depending on the locale.

category()

virtual int category () const = 0;

returns a bitmask specifying which categories this alert belong to.

enum category_t

Declared in "libtorrent/alert.hpp"

name value description
error_notification 1

Enables alerts that report an error. This includes:

  • tracker errors
  • tracker warnings
  • file errors
  • resume data failures
  • web seed errors
  • .torrent files errors
  • listen socket errors
  • port mapping errors
peer_notification 2 Enables alerts when peers send invalid requests, get banned or snubbed.
port_mapping_notification 4 Enables alerts for port mapping events. For NAT-PMP and UPnP.
storage_notification 8 Enables alerts for events related to the storage. File errors and synchronization events for moving the storage, renaming files etc.
tracker_notification 16 Enables all tracker events. Includes announcing to trackers, receiving responses, warnings and errors.
debug_notification 32 Low level alerts for when peers are connected and disconnected.
status_notification 64 Enables alerts for when a torrent or the session changes state.
progress_notification 128 Alerts for when blocks are requested and completed. Also when pieces are completed.
ip_block_notification 256 Alerts when a peer is blocked by the ip blocker or port blocker.
performance_warning 512 Alerts when some limit is reached that might limit the download or upload rate.
dht_notification 1024 Alerts on events in the DHT node. For incoming searches or bootstrapping being done etc.
stats_notification 2048 If you enable these alerts, you will receive a stats_alert approximately once every second, for every active torrent. These alerts contain all statistics counters for the interval since the lasts stats alert.
session_log_notification 8192 Enables debug logging alerts. These are available unless libtorrent was built with logging disabled (TORRENT_DISABLE_LOGGING). The alerts being posted are log_alert and are session wide.
torrent_log_notification 16384 Enables debug logging alerts for torrents. These are available unless libtorrent was built with logging disabled (TORRENT_DISABLE_LOGGING). The alerts being posted are torrent_log_alert and are torrent wide debug events.
peer_log_notification 32768 Enables debug logging alerts for peers. These are available unless libtorrent was built with logging disabled (TORRENT_DISABLE_LOGGING). The alerts being posted are peer_log_alert and low-level peer events and messages.
incoming_request_notification 65536 enables the incoming_request_alert.
dht_log_notification 131072 enables dht_log_alert, debug logging for the DHT
dht_operation_notification 262144 enable events from pure dht operations not related to torrents
port_mapping_log_notification 524288 enables port mapping log events. This log is useful for debugging the UPnP or NAT-PMP implementation
picker_log_notification 1048576 enables verbose logging from the piece picker.
file_progress_notification 2097152 alerts when files complete downloading
piece_progress_notification 4194304 alerts when pieces complete downloading or fail hash check
block_progress_notification 8388608 alerts on individual blocks being requested, downloading, finished, rejected, time-out and cancelled. This is likely to post alerts at a high rate.
all_categories 2147483647

The full bitmask, representing all available categories.

since the enum is signed, make sure this isn't interpreted as -1. For instance, boost.python does that and fails when assigning it to an unsigned parameter.

torrent_alert

Declared in "libtorrent/alert_types.hpp"

This is a base class for alerts that are associated with a specific torrent. It contains a handle to the torrent.

struct torrent_alert : alert
{
   virtual std::string message () const override;
   char const* torrent_name () const;

   torrent_handle handle;
};

message()

virtual std::string message () const override;

returns the message associated with this alert

handle
The torrent_handle pointing to the torrent this alert is associated with.

peer_alert

Declared in "libtorrent/alert_types.hpp"

The peer alert is a base class for alerts that refer to a specific peer. It includes all the information to identify the peer. i.e. ip and peer-id.

struct peer_alert : torrent_alert
{
   virtual int category () const override;
   virtual std::string message () const override;

   static const int alert_type = 1;
   static const int static_category = alert::peer_notification;
   tcp::endpoint ip;
   peer_id pid;
};
ip
The peer's IP address and port.
pid
the peer ID, if known.

tracker_alert

Declared in "libtorrent/alert_types.hpp"

This is a base class used for alerts that are associated with a specific tracker. It derives from torrent_alert since a tracker is also associated with a specific torrent.

struct tracker_alert : torrent_alert
{
   virtual int category () const override;
   virtual std::string message () const override;
   char const* tracker_url () const;

   static const int alert_type = 2;
   static const int static_category = alert::tracker_notification;
};

tracker_url()

char const* tracker_url () const;

returns a null-terminated string of the tracker's URL

torrent_removed_alert

Declared in "libtorrent/alert_types.hpp"

The torrent_removed_alert is posted whenever a torrent is removed. Since the torrent handle in its base class will always be invalid (since the torrent is already removed) it has the info hash as a member, to identify it. It's posted when the status_notification bit is set in the alert_mask.

Even though the handle member doesn't point to an existing torrent anymore, it is still useful for comparing to other handles, which may also no longer point to existing torrents, but to the same non-existing torrents.

The torrent_handle acts as a weak_ptr, even though its object no longer exists, it can still compare equal to another weak pointer which points to the same non-existent object.

struct torrent_removed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   sha1_hash info_hash;
};

read_piece_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when the asynchronous read operation initiated by a call to torrent_handle::read_piece() is completed. If the read failed, the torrent is paused and an error state is set and the buffer member of the alert is 0. If successful, buffer points to a buffer containing all the data of the piece. piece is the piece index that was read. size is the number of bytes that was read.

If the operation fails, ec will indicate what went wrong.

struct read_piece_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification;
   error_code ec;
   boost::shared_array<char> buffer;
   int piece;
   int size;
};

file_completed_alert

Declared in "libtorrent/alert_types.hpp"

This is posted whenever an individual file completes its download. i.e. All pieces overlapping this file have passed their hash check.

struct file_completed_alert final : torrent_alert
{
   virtual std::string message () const override;

   int index;
};
index
refers to the index of the file that completed.

file_renamed_alert

Declared in "libtorrent/alert_types.hpp"

This is posted as a response to a torrent_handle::rename_file() call, if the rename operation succeeds.

struct file_renamed_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* new_name () const;

   static const int static_category = alert::storage_notification;
   int index;
};
index
refers to the index of the file that was renamed,

file_rename_failed_alert

Declared in "libtorrent/alert_types.hpp"

This is posted as a response to a torrent_handle::rename_file() call, if the rename operation failed.

struct file_rename_failed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification;
   int index;
   error_code error;
};
index error
refers to the index of the file that was supposed to be renamed, error is the error code returned from the filesystem.

performance_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a limit is reached that might have a negative impact on upload or download rate performance.

struct performance_alert final : torrent_alert
{
   virtual std::string message () const override;

   enum performance_warning_t
   {
      outstanding_disk_buffer_limit_reached,
      outstanding_request_limit_reached,
      upload_limit_too_low,
      download_limit_too_low,
      send_buffer_watermark_too_low,
      too_many_optimistic_unchoke_slots,
      too_high_disk_queue_limit,
      aio_limit_reached,
      bittyrant_with_no_uplimit,
      too_few_outgoing_ports,
      too_few_file_descriptors,
      num_warnings,
   };

   static const int static_category = alert::performance_warning;
   performance_warning_t warning_code;
};

enum performance_warning_t

Declared in "libtorrent/alert_types.hpp"

name value description
outstanding_disk_buffer_limit_reached 0 This warning means that the number of bytes queued to be written to disk exceeds the max disk byte queue setting (settings_pack::max_queued_disk_bytes). This might restrict the download rate, by not queuing up enough write jobs to the disk I/O thread. When this alert is posted, peer connections are temporarily stopped from downloading, until the queued disk bytes have fallen below the limit again. Unless your max_queued_disk_bytes setting is already high, you might want to increase it to get better performance.
outstanding_request_limit_reached 1 This is posted when libtorrent would like to send more requests to a peer, but it's limited by settings_pack::max_out_request_queue. The queue length libtorrent is trying to achieve is determined by the download rate and the assumed round-trip-time (settings_pack::request_queue_time). The assumed round-trip-time is not limited to just the network RTT, but also the remote disk access time and message handling time. It defaults to 3 seconds. The target number of outstanding requests is set to fill the bandwidth-delay product (assumed RTT times download rate divided by number of bytes per request). When this alert is posted, there is a risk that the number of outstanding requests is too low and limits the download rate. You might want to increase the max_out_request_queue setting.
upload_limit_too_low 2 This warning is posted when the amount of TCP/IP overhead is greater than the upload rate limit. When this happens, the TCP/IP overhead is caused by a much faster download rate, triggering TCP ACK packets. These packets eat into the rate limit specified to libtorrent. When the overhead traffic is greater than the rate limit, libtorrent will not be able to send any actual payload, such as piece requests. This means the download rate will suffer, and new requests can be sent again. There will be an equilibrium where the download rate, on average, is about 20 times the upload rate limit. If you want to maximize the download rate, increase the upload rate limit above 5% of your download capacity.
download_limit_too_low 3 This is the same warning as upload_limit_too_low but referring to the download limit instead of upload. This suggests that your download rate limit is much lower than your upload capacity. Your upload rate will suffer. To maximize upload rate, make sure your download rate limit is above 5% of your upload capacity.
send_buffer_watermark_too_low 4

We're stalled on the disk. We want to write to the socket, and we can write but our send buffer is empty, waiting to be refilled from the disk. This either means the disk is slower than the network connection or that our send buffer watermark is too small, because we can send it all before the disk gets back to us. The number of bytes that we keep outstanding, requested from the disk, is calculated as follows:

min(512, max(upload_rate * send_buffer_watermark_factor / 100, send_buffer_watermark))

If you receive this alert, you might want to either increase your send_buffer_watermark or send_buffer_watermark_factor.

too_many_optimistic_unchoke_slots 5 If the half (or more) of all upload slots are set as optimistic unchoke slots, this warning is issued. You probably want more regular (rate based) unchoke slots.
too_high_disk_queue_limit 6 If the disk write queue ever grows larger than half of the cache size, this warning is posted. The disk write queue eats into the total disk cache and leaves very little left for the actual cache. This causes the disk cache to oscillate in evicting large portions of the cache before allowing peers to download any more, onto the disk write queue. Either lower max_queued_disk_bytes or increase cache_size.
aio_limit_reached 7  
bittyrant_with_no_uplimit 8  
too_few_outgoing_ports 9 This is generated if outgoing peer connections are failing because of address in use errors, indicating that settings_pack::outgoing_ports is set and is too small of a range. Consider not using the outgoing_ports setting at all, or widen the range to include more ports.
too_few_file_descriptors 10  
num_warnings 11  

state_changed_alert

Declared in "libtorrent/alert_types.hpp"

Generated whenever a torrent changes its state.

struct state_changed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   torrent_status::state_t state;
   torrent_status::state_t prev_state;
};
state
the new state of the torrent.
prev_state
the previous state.

tracker_error_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated on tracker time outs, premature disconnects, invalid response or a HTTP response other than "200 OK". From the alert you can get the handle to the torrent the tracker belongs to.

The times_in_row member says how many times in a row this tracker has failed. status_code is the code returned from the HTTP server. 401 means the tracker needs authentication, 404 means not found etc. If the tracker timed out, the code will be set to 0.

struct tracker_error_alert final : tracker_alert
{
   virtual std::string message () const override;
   char const* error_message () const;

   static const int static_category = alert::tracker_notification | alert::error_notification;
   int times_in_row;
   int status_code;
   error_code error;
};

error_message()

char const* error_message () const;

the message associated with this error

tracker_warning_alert

Declared in "libtorrent/alert_types.hpp"

This alert is triggered if the tracker reply contains a warning field. Usually this means that the tracker announce was successful, but the tracker has a message to the client.

struct tracker_warning_alert final : tracker_alert
{
   virtual std::string message () const override;
   char const* warning_message () const;

   static const int static_category = alert::tracker_notification | alert::error_notification;
};

warning_message()

char const* warning_message () const;

the message associated with this warning

scrape_reply_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a scrape request succeeds.

struct scrape_reply_alert final : tracker_alert
{
   virtual std::string message () const override;

   int incomplete;
   int complete;
};
incomplete complete
the data returned in the scrape response. These numbers may be -1 if the response was malformed.

scrape_failed_alert

Declared in "libtorrent/alert_types.hpp"

If a scrape request fails, this alert is generated. This might be due to the tracker timing out, refusing connection or returning an http response code indicating an error.

struct scrape_failed_alert final : tracker_alert
{
   virtual std::string message () const override;
   char const* error_message () const;

   static const int static_category = alert::tracker_notification | alert::error_notification;
   error_code error;
};

error_message()

char const* error_message () const;

if the error indicates there is an associated message, this returns that message. Otherwise and empty string.

error
the error itself. This may indicate that the tracker sent an error message (error::tracker_failure), in which case it can be retrieved by calling error_message().

tracker_reply_alert

Declared in "libtorrent/alert_types.hpp"

This alert is only for informational purpose. It is generated when a tracker announce succeeds. It is generated regardless what kind of tracker was used, be it UDP, HTTP or the DHT.

struct tracker_reply_alert final : tracker_alert
{
   virtual std::string message () const override;

   int num_peers;
};
num_peers
tells how many peers the tracker returned in this response. This is not expected to be greater than the num_want settings. These are not necessarily all new peers, some of them may already be connected.

dht_reply_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated each time the DHT receives peers from a node. num_peers is the number of peers we received in this packet. Typically these packets are received from multiple DHT nodes, and so the alerts are typically generated a few at a time.

struct dht_reply_alert final : tracker_alert
{
   virtual std::string message () const override;

   int num_peers;
};

tracker_announce_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated each time a tracker announce is sent (or attempted to be sent). There are no extra data members in this alert. The url can be found in the base class however.

struct tracker_announce_alert final : tracker_alert
{
   virtual std::string message () const override;

   int event;
};
event

specifies what event was sent to the tracker. It is defined as:

  1. None
  2. Completed
  3. Started
  4. Stopped

hash_failed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a finished piece fails its hash check. You can get the handle to the torrent which got the failed piece and the index of the piece itself from the alert.

struct hash_failed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   int piece_index;
};

peer_ban_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer is banned because it has sent too many corrupt pieces to us. ip is the endpoint to the peer that was banned.

struct peer_ban_alert final : peer_alert
{
   virtual std::string message () const override;
};

peer_unsnubbed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer is unsnubbed. Essentially when it was snubbed for stalling sending data, and now it started sending data again.

struct peer_unsnubbed_alert final : peer_alert
{
   virtual std::string message () const override;
};

peer_snubbed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer is snubbed, when it stops sending data when we request it.

struct peer_snubbed_alert final : peer_alert
{
   virtual std::string message () const override;
};

peer_error_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer sends invalid data over the peer-peer protocol. The peer will be disconnected, but you get its ip address from the alert, to identify it.

struct peer_error_alert final : peer_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::peer_notification;
   int operation;
   error_code error;
};
operation
a NULL-terminated string of the low-level operation that failed, or NULL if there was no low level disk operation.
error
tells you what error caused this alert.

peer_connect_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted every time an outgoing peer connect attempts succeeds.

struct peer_connect_alert final : peer_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::debug_notification;
   int socket_type;
};

peer_disconnected_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer is disconnected for any reason (other than the ones covered by peer_error_alert ).

struct peer_disconnected_alert final : peer_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::debug_notification;
   int socket_type;
   operation_t operation;
   error_code error;
   close_reason_t reason;
};
socket_type
the kind of socket this peer was connected over
operation
the operation or level where the error occurred. Specified as an value from the operation_t enum. Defined in operations.hpp.
error
tells you what error caused peer to disconnect.
reason
the reason the peer disconnected (if specified)

invalid_request_alert

Declared in "libtorrent/alert_types.hpp"

This is a debug alert that is generated by an incoming invalid piece request. ip is the address of the peer and the request is the actual incoming request from the peer. See peer_request for more info.

struct invalid_request_alert final : peer_alert
{
   virtual std::string message () const override;

   peer_request request;
   bool we_have;
   bool peer_interested;
   bool withheld;
};
request
the request we received from the peer
we_have
true if we have this piece
peer_interested
true if the peer indicated that it was interested to download before sending the request
withheld
if this is true, the peer is not allowed to download this piece because of super-seeding rules.

torrent_finished_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a torrent switches from being a downloader to a seed. It will only be generated once per torrent. It contains a torrent_handle to the torrent in question.

struct torrent_finished_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
};

piece_finished_alert

Declared in "libtorrent/alert_types.hpp"

this alert is posted every time a piece completes downloading and passes the hash check. This alert derives from torrent_alert which contains the torrent_handle to the torrent the piece belongs to.

struct piece_finished_alert final : torrent_alert
{
   virtual std::string message () const override;

   int piece_index;
};
piece_index
the index of the piece that finished

request_dropped_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer rejects or ignores a piece request.

struct request_dropped_alert final : peer_alert
{
   virtual std::string message () const override;

   int block_index;
   int piece_index;
};

block_timeout_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a block request times out.

struct block_timeout_alert final : peer_alert
{
   virtual std::string message () const override;

   int block_index;
   int piece_index;
};

block_finished_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a block request receives a response.

struct block_finished_alert final : peer_alert
{
   virtual std::string message () const override;

   int block_index;
   int piece_index;
};

block_downloading_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a block request is sent to a peer.

struct block_downloading_alert final : peer_alert
{
   virtual std::string message () const override;

   int block_index;
   int piece_index;
};

unwanted_block_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a block is received that was not requested or whose request timed out.

struct unwanted_block_alert final : peer_alert
{
   virtual std::string message () const override;

   int block_index;
   int piece_index;
};

storage_moved_alert

Declared in "libtorrent/alert_types.hpp"

The storage_moved_alert is generated when all the disk IO has completed and the files have been moved, as an effect of a call to torrent_handle::move_storage. This is useful to synchronize with the actual disk. The storage_path() member return the new path of the storage.

struct storage_moved_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* storage_path () const;

   static const int static_category = alert::storage_notification;
};

storage_path()

char const* storage_path () const;

the path the torrent was moved to

storage_moved_failed_alert

Declared in "libtorrent/alert_types.hpp"

The storage_moved_failed_alert is generated when an attempt to move the storage, via torrent_handle::move_storage(), fails.

struct storage_moved_failed_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* file_path () const;

   static const int static_category = alert::storage_notification;
   error_code error;
   char const* operation;
};

file_path()

char const* file_path () const;

If the error happened for a specific file, this returns its path.

operation
If the error happened in a specific disk operation this is a NULL terminated string naming which one, otherwise it's NULL.

torrent_deleted_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a request to delete the files of a torrent complete.

The info_hash is the info-hash of the torrent that was just deleted. Most of the time the torrent_handle in the torrent_alert will be invalid by the time this alert arrives, since the torrent is being deleted. The info_hash member is hence the main way of identifying which torrent just completed the delete.

This alert is posted in the storage_notification category, and that bit needs to be set in the alert_mask.

struct torrent_deleted_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification;
   sha1_hash info_hash;
};

torrent_delete_failed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a request to delete the files of a torrent fails. Just removing a torrent from the session cannot fail

struct torrent_delete_failed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification
   | alert::error_notification;
   error_code error;
   sha1_hash info_hash;
};
error
tells you why it failed.
info_hash
the info hash of the torrent whose files failed to be deleted

save_resume_data_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated as a response to a torrent_handle::save_resume_data request. It is generated once the disk IO thread is done writing the state for this torrent.

struct save_resume_data_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification;
   boost::shared_ptr<entry> resume_data;
};
resume_data
points to the resume data.

save_resume_data_failed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated instead of save_resume_data_alert if there was an error generating the resume data. error describes what went wrong.

struct save_resume_data_failed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification
   | alert::error_notification;
   error_code error;
};
error
the error code from the resume_data failure

torrent_paused_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated as a response to a torrent_handle::pause request. It is generated once all disk IO is complete and the files in the torrent have been closed. This is useful for synchronizing with the disk.

struct torrent_paused_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
};

torrent_resumed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated as a response to a torrent_handle::resume() request. It is generated when a torrent goes from a paused state to an active state.

struct torrent_resumed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
};

torrent_checked_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when a torrent completes checking. i.e. when it transitions out of the checking files state into a state where it is ready to start downloading

struct torrent_checked_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
};

url_seed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a HTTP seed name lookup fails.

struct url_seed_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* server_url () const;
   char const* error_message () const;

   static const int static_category = alert::peer_notification | alert::error_notification;
   error_code error;
};

server_url()

char const* server_url () const;

the URL the error is associated with

error_message()

char const* error_message () const;

in case the web server sent an error message, this function returns it.

error
the error the web seed encountered. If this is not set, the server sent an error message, call error_message().

file_error_alert

Declared in "libtorrent/alert_types.hpp"

If the storage fails to read or write files that it needs access to, this alert is generated and the torrent is paused.

struct file_error_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* filename () const;

   static const int static_category = alert::status_notification
   | alert::storage_notification;
   error_code error;
   char const* operation;
};

filename()

char const* filename () const;

the file that experienced the error

error operation
the error code describing the error.

metadata_failed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when the metadata has been completely received and the info-hash failed to match it. i.e. the metadata that was received was corrupt. libtorrent will automatically retry to fetch it in this case. This is only relevant when running a torrent-less download, with the metadata extension provided by libtorrent.

struct metadata_failed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::error_notification;
   error_code error;
};
error
indicates what failed when parsing the metadata. This error is what's returned from lazy_bdecode().

metadata_received_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when the metadata has been completely received and the torrent can start downloading. It is not generated on torrents that are started with metadata, but only those that needs to download it from peers (when utilizing the libtorrent extension).

There are no additional data members in this alert.

Typically, when receiving this alert, you would want to save the torrent file in order to load it back up again when the session is restarted. Here's an example snippet of code to do that:

torrent_handle h = alert->handle();
if (h.is_valid()) {
        boost::shared_ptr<torrent_info const> ti = h.torrent_file();
        create_torrent ct(*ti);
        entry te = ct.generate();
        std::vector<char> buffer;
        bencode(std::back_inserter(buffer), te);
        FILE* f = fopen((to_hex(ti->info_hash().to_string()) + ".torrent").c_str(), "wb+");
        if (f) {
                fwrite(&buffer[0], 1, buffer.size(), f);
                fclose(f);
        }
}
struct metadata_received_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
};

udp_error_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when there is an error on the UDP socket. The UDP socket is used for all uTP, DHT and UDP tracker traffic. It's global to the session.

struct udp_error_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::error_notification;
   udp::endpoint endpoint;
   error_code error;
};
endpoint
the source address associated with the error (if any)
error
the error code describing the error

external_ip_alert

Declared in "libtorrent/alert_types.hpp"

Whenever libtorrent learns about the machines external IP, this alert is generated. The external IP address can be acquired from the tracker (if it supports that) or from peers that supports the extension protocol. The address can be accessed through the external_address member.

struct external_ip_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   address external_address;
};
external_address
the IP address that is believed to be our external IP

listen_failed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when none of the ports, given in the port range, to session can be opened for listening. The endpoint member is the interface and port that failed, error is the error code describing the failure.

libtorrent may sometimes try to listen on port 0, if all other ports failed. Port 0 asks the operating system to pick a port that's free). If that fails you may see a listen_failed_alert with port 0 even if you didn't ask to listen on it.

struct listen_failed_alert final : alert
{
   virtual std::string message () const override;
   char const* listen_interface () const;

   enum socket_type_t
   {
      tcp,
      tcp_ssl,
      udp,
      i2p,
      socks5,
      utp_ssl,
   };

   enum op_t
   {
      parse_addr,
      open,
      bind,
      listen,
      get_peer_name,
      accept,
   };

   static const int static_category = alert::status_notification | alert::error_notification;
   error_code error;
   int operation;
   socket_type_t sock_type;
   tcp::endpoint endpoint;
};

listen_interface()

char const* listen_interface () const;

the interface libtorrent attempted to listen on that failed.

enum socket_type_t

Declared in "libtorrent/alert_types.hpp"

name value description
tcp 0  
tcp_ssl 1  
udp 2  
i2p 3  
socks5 4  
utp_ssl 5  

enum op_t

Declared in "libtorrent/alert_types.hpp"

name value description
parse_addr 0  
open 1  
bind 2  
listen 3  
get_peer_name 4  
accept 5  
error
the error the system returned
operation
the specific low level operation that failed. See op_t.
sock_type
the type of listen socket this alert refers to.
endpoint
the address and port libtorrent attempted to listen on

listen_succeeded_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when the listen port succeeds to be opened on a particular interface. endpoint is the endpoint that successfully was opened for listening.

struct listen_succeeded_alert final : alert
{
   virtual std::string message () const override;

   enum socket_type_t
   {
      tcp,
      tcp_ssl,
      udp,
      i2p,
      socks5,
      utp_ssl,
   };

   static const int static_category = alert::status_notification;
   tcp::endpoint endpoint;
   socket_type_t sock_type;
};

enum socket_type_t

Declared in "libtorrent/alert_types.hpp"

name value description
tcp 0  
tcp_ssl 1  
udp 2  
i2p 3  
socks5 4  
utp_ssl 5  
endpoint
the endpoint libtorrent ended up listening on. The address refers to the local interface and the port is the listen port.
sock_type
the type of listen socket this alert refers to.

portmap_error_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a NAT router was successfully found but some part of the port mapping request failed. It contains a text message that may help the user figure out what is wrong. This alert is not generated in case it appears the client is not running on a NAT:ed network or if it appears there is no NAT router that can be remote controlled to add port mappings.

struct portmap_error_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::port_mapping_notification
   | alert::error_notification;
   int mapping;
   int map_type;
   error_code error;
};
mapping
refers to the mapping index of the port map that failed, i.e. the index returned from add_mapping().
map_type
is 0 for NAT-PMP and 1 for UPnP.
error
tells you what failed.

portmap_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a NAT router was successfully found and a port was successfully mapped on it. On a NAT:ed network with a NAT-PMP capable router, this is typically generated once when mapping the TCP port and, if DHT is enabled, when the UDP port is mapped.

struct portmap_alert final : alert
{
   virtual std::string message () const override;

   enum protocol_t
   {
      tcp,
      udp,
   };

   static const int static_category = alert::port_mapping_notification;
   int mapping;
   int external_port;
   int map_type;
   int protocol;
};

enum protocol_t

Declared in "libtorrent/alert_types.hpp"

name value description
tcp 0  
udp 1  
mapping
refers to the mapping index of the port map that failed, i.e. the index returned from add_mapping().
external_port
the external port allocated for the mapping.
map_type
0 for NAT-PMP and 1 for UPnP.
protocol
the protocol this mapping was for. one of protocol_t enums

portmap_log_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated to log informational events related to either UPnP or NAT-PMP. They contain a log line and the type (0 = NAT-PMP and 1 = UPnP). Displaying these messages to an end user is only useful for debugging the UPnP or NAT-PMP implementation. This alert is only posted if the alert::port_mapping_log_notification flag is enabled in the alert mask.

struct portmap_log_alert final : alert
{
   virtual std::string message () const override;
   char const* log_message () const;

   static const int static_category = alert::port_mapping_log_notification;
   int map_type;
};

log_message()

char const* log_message () const;

the message associated with this log line

fastresume_rejected_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a fastresume file has been passed to add_torrent() but the files on disk did not match the fastresume file. The error_code explains the reason why the resume file was rejected.

struct fastresume_rejected_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* file_path () const;

   static const int static_category = alert::status_notification
   | alert::error_notification;
   error_code error;
   char const* operation;
};

file_path()

char const* file_path () const;

If the error happened to a specific file, this returns the path to it.

operation
If the error happened in a disk operation. a NULL-terminated string of the name of that operation. operation is NULL otherwise.

peer_blocked_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when an incoming peer connection, or a peer that's about to be added to our peer list, is blocked for some reason. This could be any of:

  • the IP filter
  • i2p mixed mode restrictions (a normal peer is not allowed on an i2p swarm)
  • the port filter
  • the peer has a low port and no_connect_privileged_ports is enabled
  • the protocol of the peer is blocked (uTP/TCP blocking)
struct peer_blocked_alert final : torrent_alert
{
   virtual std::string message () const override;

   enum reason_t
   {
      ip_filter,
      port_filter,
      i2p_mixed,
      privileged_ports,
      utp_disabled,
      tcp_disabled,
      invalid_local_interface,
   };

   static const int static_category = alert::ip_block_notification;
   address ip;
   int reason;
};

enum reason_t

Declared in "libtorrent/alert_types.hpp"

name value description
ip_filter 0  
port_filter 1  
i2p_mixed 2  
privileged_ports 3  
utp_disabled 4  
tcp_disabled 5  
invalid_local_interface 6  
ip
the address that was blocked.

dht_announce_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a DHT node announces to an info-hash on our DHT node. It belongs to the dht_notification category.

struct dht_announce_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   address ip;
   int port;
   sha1_hash info_hash;
};

dht_get_peers_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a DHT node sends a get_peers message to our DHT node. It belongs to the dht_notification category.

struct dht_get_peers_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   sha1_hash info_hash;
};

stats_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted approximately once every second, and it contains byte counters of most statistics that's tracked for torrents. Each active torrent posts these alerts regularly. This alert has been superceded by calling post_torrent_updates() regularly on the session object. This alert will be removed

struct stats_alert final : torrent_alert
{
   virtual std::string message () const override;

   enum stats_channel
   {
      upload_payload,
      upload_protocol,
      download_payload,
      download_protocol,
      upload_ip_protocol,
      deprecated1,
      deprecated2,
      download_ip_protocol,
      deprecated3,
      deprecated4,
      num_channels,
   };

   static const int static_category = alert::stats_notification;
   int transferred[num_channels];
   int interval;
};

enum stats_channel

Declared in "libtorrent/alert_types.hpp"

name value description
upload_payload 0  
upload_protocol 1  
download_payload 2  
download_protocol 3  
upload_ip_protocol 4  
deprecated1 5  
deprecated2 6  
download_ip_protocol 7  
deprecated3 8  
deprecated4 9  
num_channels 10  
transferred[num_channels]
an array of samples. The enum describes what each sample is a measurement of. All of these are raw, and not smoothing is performed.
interval
the number of milliseconds during which these stats were collected. This is typically just above 1000, but if CPU is limited, it may be higher than that.

cache_flushed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when the disk cache has been flushed for a specific torrent as a result of a call to torrent_handle::flush_cache(). This alert belongs to the storage_notification category, which must be enabled to let this alert through. The alert is also posted when removing a torrent from the session, once the outstanding cache flush is complete and the torrent does no longer have any files open.

struct cache_flushed_alert final : torrent_alert
{
   static const int static_category = alert::storage_notification;
};

anonymous_mode_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when a bittorrent feature is blocked because of the anonymous mode. For instance, if the tracker proxy is not set up, no trackers will be used, because trackers can only be used through proxies when in anonymous mode.

struct anonymous_mode_alert final : torrent_alert
{
   virtual std::string message () const override;

   enum kind_t
   {
      tracker_not_anonymous,
   };

   static const int static_category = alert::error_notification;
   int kind;
   std::string str;
};

enum kind_t

Declared in "libtorrent/alert_types.hpp"

name value description
tracker_not_anonymous 0 means that there's no proxy set up for tracker communication and the tracker will not be contacted. The tracker which this failed for is specified in the str member.
kind str
specifies what error this is, see kind_t.

lsd_peer_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when we receive a local service discovery message from a peer for a torrent we're currently participating in.

struct lsd_peer_alert final : peer_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::peer_notification;
};

trackerid_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted whenever a tracker responds with a trackerid. The tracker ID is like a cookie. The libtorrent will store the tracker ID for this tracker and repeat it in subsequent announces.

struct trackerid_alert final : tracker_alert
{
   virtual std::string message () const override;
   char const* tracker_id () const;

   static const int static_category = alert::status_notification;
};

tracker_id()

char const* tracker_id () const;

The tracker ID returned by the tracker

dht_bootstrap_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when the initial DHT bootstrap is done.

struct dht_bootstrap_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
};

torrent_error_alert

Declared in "libtorrent/alert_types.hpp"

This is posted whenever a torrent is transitioned into the error state.

struct torrent_error_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* filename () const;

   static const int static_category = alert::error_notification | alert::status_notification;
   error_code error;
};

filename()

char const* filename () const;

the filename (or object) the error occurred on.

error
specifies which error the torrent encountered.

torrent_need_cert_alert

Declared in "libtorrent/alert_types.hpp"

This is always posted for SSL torrents. This is a reminder to the client that the torrent won't work unless torrent_handle::set_ssl_certificate() is called with a valid certificate. Valid certificates MUST be signed by the SSL certificate in the .torrent file.

struct torrent_need_cert_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   error_code error;
};

incoming_connection_alert

Declared in "libtorrent/alert_types.hpp"

The incoming connection alert is posted every time we successfully accept an incoming connection, through any mean. The most straight-forward ways of accepting incoming connections are through the TCP listen socket and the UDP listen socket for uTP sockets. However, connections may also be accepted through a Socks5 or i2p listen socket, or via an SSL listen socket.

struct incoming_connection_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::peer_notification;
   int socket_type;
   tcp::endpoint ip;
};
socket_type

tells you what kind of socket the connection was accepted as:

  1. none (no socket instantiated)
  2. TCP
  3. Socks5
  4. HTTP
  5. uTP
  6. i2p
  7. SSL/TCP
  8. SSL/Socks5
  9. HTTPS (SSL/HTTP)
  10. SSL/uTP
ip
is the IP address and port the connection came from.

add_torrent_alert

Declared in "libtorrent/alert_types.hpp"

This alert is always posted when a torrent was attempted to be added and contains the return status of the add operation. The torrent handle of the new torrent can be found in the base class' handle member. If adding the torrent failed, error contains the error code.

struct add_torrent_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   add_torrent_params params;
   error_code error;
};
params
a copy of the parameters used when adding the torrent, it can be used to identify which invocation to async_add_torrent() caused this alert.
error
set to the error, if one occurred while adding the torrent.

state_update_alert

Declared in "libtorrent/alert_types.hpp"

This alert is only posted when requested by the user, by calling session::post_torrent_updates() on the session. It contains the torrent status of all torrents that changed since last time this message was posted. Its category is status_notification, but it's not subject to filtering, since it's only manually posted anyway.

struct state_update_alert final : alert
{
   state_update_alert (aux::stack_allocator& alloc
      , std::vector<torrent_status> st);
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   std::vector<torrent_status> status;
};
status
contains the torrent status of all torrents that changed since last time this message was posted. Note that you can map a torrent status to a specific torrent via its handle member. The receiving end is suggested to have all torrents sorted by the torrent_handle or hashed by it, for efficient updates.

session_stats_alert

Declared in "libtorrent/alert_types.hpp"

The session_stats_alert is posted when the user requests session statistics by calling post_session_stats() on the session object. Its category is status_notification, but it is not subject to filtering, since it's only manually posted anyway.

struct session_stats_alert final : alert
{
   session_stats_alert (aux::stack_allocator& alloc, counters const& cnt);
   virtual std::string message () const override;

   static const int static_category = alert::stats_notification;
   boost::uint64_t values[counters::num_counters];
};
values[counters

An array are a mix of counters and gauges, which meanings can be queries via the session_stats_metrics() function on the session. The mapping from a specific metric to an index into this array is constant for a specific version of libtorrent, but may differ for other versions. The intended usage is to request the mapping, i.e. call session_stats_metrics(), once on startup, and then use that mapping to interpret these values throughout the process' runtime.

For more information, see the session statistics section.

dht_error_alert

Declared in "libtorrent/alert_types.hpp"

posted when something fails in the DHT. This is not necessarily a fatal error, but it could prevent proper operation

struct dht_error_alert final : alert
{
   virtual std::string message () const override;

   enum op_t
   {
      unknown,
      hostname_lookup,
   };

   static const int static_category = alert::error_notification | alert::dht_notification;
   error_code error;
   op_t operation;
};

enum op_t

Declared in "libtorrent/alert_types.hpp"

name value description
unknown 0  
hostname_lookup 1  
error
the error code
operation
the operation that failed

dht_immutable_item_alert

Declared in "libtorrent/alert_types.hpp"

this alert is posted as a response to a call to session::get_item(), specifically the overload for looking up immutable items in the DHT.

struct dht_immutable_item_alert final : alert
{
   dht_immutable_item_alert (aux::stack_allocator& alloc, sha1_hash const& t
      , entry const& i);
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   sha1_hash target;
   entry item;
};
target
the target hash of the immutable item. This must match the SHA-1 hash of the bencoded form of item.
item
the data for this item

dht_mutable_item_alert

Declared in "libtorrent/alert_types.hpp"

this alert is posted as a response to a call to session::get_item(), specifically the overload for looking up mutable items in the DHT.

struct dht_mutable_item_alert final : alert
{
   dht_mutable_item_alert (aux::stack_allocator& alloc
      , boost::array<char, 32> k
      , boost::array<char, 64> sig
      , boost::uint64_t sequence
      , std::string const& s
      , entry const& i
      , bool a);
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   boost::array<char, 32> key;
   boost::array<char, 64> signature;
   boost::uint64_t seq;
   std::string salt;
   entry item;
   bool authoritative;
};
key
the public key that was looked up
signature
the signature of the data. This is not the signature of the plain encoded form of the item, but it includes the sequence number and possibly the hash as well. See the dht_store document for more information. This is primarily useful for echoing back in a store request.
seq
the sequence number of this item
salt
the salt, if any, used to lookup and store this item. If no salt was used, this is an empty string
item
the data for this item
authoritative
the last response for mutable data is authoritative.

dht_put_alert

Declared in "libtorrent/alert_types.hpp"

this is posted when a DHT put operation completes. This is useful if the client is waiting for a put to complete before shutting down for instance.

struct dht_put_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   sha1_hash target;
   boost::array<char, 32> public_key;
   boost::array<char, 64> signature;
   std::string salt;
   boost::uint64_t seq;
   int num_success;
};
target
the target hash the item was stored under if this was an immutable item.
public_key signature salt seq
if a mutable item was stored, these are the public key, signature, salt and sequence number the item was stored under.
num_success
DHT put operation usually writes item to k nodes, maybe the node is stale so no response, or the node doesn't support 'put', or the token for write is out of date, etc. num_success is the number of successful responses we got from the puts.

i2p_alert

Declared in "libtorrent/alert_types.hpp"

this alert is used to report errors in the i2p SAM connection

struct i2p_alert final : alert
{
   i2p_alert (aux::stack_allocator& alloc, error_code const& ec);
   virtual std::string message () const override;

   static const int static_category = alert::error_notification;
   error_code error;
};
error
the error that occurred in the i2p SAM connection

dht_outgoing_get_peers_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when we send a get_peers request It belongs to the dht_notification category.

struct dht_outgoing_get_peers_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   sha1_hash info_hash;
   sha1_hash obfuscated_info_hash;
   udp::endpoint ip;
};
info_hash
the info_hash of the torrent we're looking for peers for.
obfuscated_info_hash
if this was an obfuscated lookup, this is the info-hash target actually sent to the node.
ip
the endpoint we're sending this query to

log_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted by some session wide event. Its main purpose is trouble shooting and debugging. It's not enabled by the default alert mask and is enabled by the alert::session_log_notification bit. Furthermore, it's by default disabled as a build configuration.

struct log_alert final : alert
{
   virtual std::string message () const override;
   char const* msg () const;

   static const int static_category = alert::session_log_notification;
};

msg()

char const* msg () const;

returns the log message

torrent_log_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted by torrent wide events. It's meant to be used for trouble shooting and debugging. It's not enabled by the default alert mask and is enabled by the alert::torrent_log_notification bit. By default it is disabled as a build configuration.

struct torrent_log_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* msg () const;

   static const int static_category = alert::torrent_log_notification;
};

msg()

char const* msg () const;

returns the log message

peer_log_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted by events specific to a peer. It's meant to be used for trouble shooting and debugging. It's not enabled by the default alert mask and is enabled by the alert::peer_log_notification bit. By default it is disabled as a build configuration.

struct peer_log_alert final : peer_alert
{
   virtual std::string message () const override;
   char const* msg () const;

   enum direction_t
   {
      incoming_message,
      outgoing_message,
      incoming,
      outgoing,
      info,
   };

   static const int static_category = alert::peer_log_notification;
   char const* event_type;
   direction_t direction;
};

msg()

char const* msg () const;

returns the log message

enum direction_t

Declared in "libtorrent/alert_types.hpp"

name value description
incoming_message 0  
outgoing_message 1  
incoming 2  
outgoing 3  
info 4  
event_type
string literal indicating the kind of event. For messages, this is the message name.

lsd_error_alert

Declared in "libtorrent/alert_types.hpp"

posted if the local service discovery socket fails to start properly. it's categorized as error_notification.

struct lsd_error_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::error_notification;
   error_code error;
};
error
The error code

dht_lookup

Declared in "libtorrent/alert_types.hpp"

holds statistics about a current dht_lookup operation. a DHT lookup is the traversal of nodes, looking up a set of target nodes in the DHT for retrieving and possibly storing information in the DHT

struct dht_lookup
{
   char const* type;
   int outstanding_requests;
   int timeouts;
   int responses;
   int branch_factor;
   int nodes_left;
   int last_sent;
   int first_timeout;
};
type
string literal indicating which kind of lookup this is
outstanding_requests
the number of outstanding request to individual nodes this lookup has right now
timeouts
the total number of requests that have timed out so far for this lookup
responses
the total number of responses we have received for this lookup so far for this lookup
branch_factor
the branch factor for this lookup. This is the number of nodes we keep outstanding requests to in parallel by default. when nodes time out we may increase this.
nodes_left
the number of nodes left that could be queries for this lookup. Many of these are likely to be part of the trail while performing the lookup and would never end up actually being queried.
last_sent
the number of seconds ago the last message was sent that's still outstanding
first_timeout
the number of outstanding requests that have exceeded the short timeout and are considered timed out in the sense that they increased the branch factor

dht_routing_bucket

Declared in "libtorrent/alert_types.hpp"

struct to hold information about a single DHT routing table bucket

struct dht_routing_bucket
{
   int num_nodes;
   int num_replacements;
   int last_active;
};
num_nodes num_replacements
the total number of nodes and replacement nodes in the routing table
last_active
number of seconds since last activity

dht_stats_alert

Declared in "libtorrent/alert_types.hpp"

contains current DHT state. Posted in response to session::post_dht_stats().

struct dht_stats_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::stats_notification;
   std::vector<dht_lookup> active_requests;
   std::vector<dht_routing_bucket> routing_table;
};
active_requests
a vector of the currently running DHT lookups.
routing_table
contains information about every bucket in the DHT routing table.

incoming_request_alert

Declared in "libtorrent/alert_types.hpp"

posted every time an incoming request from a peer is accepted and queued up for being serviced. This alert is only posted if the alert::incoming_request_notification flag is enabled in the alert mask.

struct incoming_request_alert final : peer_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::incoming_request_notification;
   peer_request req;
};
req
the request this peer sent to us

dht_log_alert

Declared in "libtorrent/alert_types.hpp"

struct dht_log_alert final : alert
{
   dht_log_alert (aux::stack_allocator& alloc
      , dht_module_t m, char const* msg);
   virtual std::string message () const override;
   char const* log_message () const;

   enum dht_module_t
   {
      tracker,
      node,
      routing_table,
      rpc_manager,
      traversal,
   };

   static const int static_category = alert::dht_log_notification;
   dht_module_t module;
};

log_message()

char const* log_message () const;

the log message

enum dht_module_t

Declared in "libtorrent/alert_types.hpp"

name value description
tracker 0  
node 1  
routing_table 2  
rpc_manager 3  
traversal 4  
module
the module, or part, of the DHT that produced this log message.

dht_pkt_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted every time a DHT message is sent or received. It is only posted if the alert::dht_log_notification alert category is enabled. It contains a verbatim copy of the message.

struct dht_pkt_alert final : alert
{
   dht_pkt_alert (aux::stack_allocator& alloc, char const* buf, int size
      , dht_pkt_alert::direction_t d, udp::endpoint ep);
   virtual std::string message () const override;
   int pkt_size () const;
   char const* pkt_buf () const;

   enum direction_t
   {
      incoming,
      outgoing,
   };

   static const int static_category = alert::dht_log_notification;
   direction_t dir;
   udp::endpoint node;
};

pkt_size() pkt_buf()

int pkt_size () const;
char const* pkt_buf () const;

returns a pointer to the packet buffer and size of the packet, respectively. This buffer is only valid for as long as the alert itself is valid, which is owned by libtorrent and reclaimed whenever pop_alerts() is called on the session.

enum direction_t

Declared in "libtorrent/alert_types.hpp"

name value description
incoming 0  
outgoing 1  
dir
whether this is an incoming or outgoing packet.
node
the DHT node we received this packet from, or sent this packet to (depending on dir).

dht_get_peers_reply_alert

Declared in "libtorrent/alert_types.hpp"

struct dht_get_peers_reply_alert final : alert
{
   dht_get_peers_reply_alert (aux::stack_allocator& alloc
      , sha1_hash const& ih
      , std::vector<tcp::endpoint> const& v);
   virtual std::string message () const override;
   int num_peers () const;
   std::vector<tcp::endpoint> peers () const;

   static const int static_category = alert::dht_operation_notification;
   sha1_hash info_hash;
};

dht_direct_response_alert

Declared in "libtorrent/alert_types.hpp"

This is posted exactly once for every call to session_handle::dht_direct_request. If the request failed, response() will return a default constructed bdecode_node.

struct dht_direct_response_alert final : alert
{
   dht_direct_response_alert (aux::stack_allocator& alloc, void* userdata
      , udp::endpoint const& addr, bdecode_node const& response);
   virtual std::string message () const override;
   dht_direct_response_alert (aux::stack_allocator& alloc, void* userdata
      , udp::endpoint const& addr);
   bdecode_node response () const;

   static const int static_category = alert::dht_notification;
   void* userdata;
   udp::endpoint addr;
};

dht_direct_response_alert() message()

virtual std::string message () const override;
dht_direct_response_alert (aux::stack_allocator& alloc, void* userdata
      , udp::endpoint const& addr);

for when there was a timeout so we don't have a response

picker_log_alert

Declared in "libtorrent/alert_types.hpp"

this is posted when one or more blocks are picked by the piece picker, assuming the verbose piece picker logging is enabled (see picker_log_notification).

struct picker_log_alert final : peer_alert
{
   virtual std::string message () const override;
   std::vector<piece_block> blocks () const;

   enum picker_flags_t
   {
      partial_ratio,
      prioritize_partials,
      rarest_first_partials,
      rarest_first,
      reverse_rarest_first,
      suggested_pieces,
      prio_sequential_pieces,
      sequential_pieces,
      reverse_pieces,
      time_critical,
      random_pieces,
      prefer_contiguous,
      reverse_sequential,
      backup1,
      backup2,
      end_game,
   };

   static const int static_category = alert::picker_log_notification;
   boost::uint32_t picker_flags;
};

enum picker_flags_t

Declared in "libtorrent/alert_types.hpp"

name value description
partial_ratio 1 the ratio of partial pieces is too high. This forces a preference for picking blocks from partial pieces.
prioritize_partials 2  
rarest_first_partials 4  
rarest_first 8  
reverse_rarest_first 16  
suggested_pieces 32  
prio_sequential_pieces 64  
sequential_pieces 128  
reverse_pieces 256  
time_critical 512  
random_pieces 1024  
prefer_contiguous 2048  
reverse_sequential 4096  
backup1 8192  
backup2 16384  
end_game 32768  
picker_flags
this is a bitmask of which features were enabled for this particular pick. The bits are defined in the picker_flags_t enum.

alert_cast()

Declared in "libtorrent/alert.hpp"

template <class T> T* alert_cast (alert* a);
template <class T> T const* alert_cast (alert const* a);

When you get an alert, you can use alert_cast<> to attempt to cast the pointer to a specific alert type, in order to query it for more information.

Note

alert_cast<> can only cast to an exact alert type, not a base class

operation_name()

Declared in "libtorrent/alert_types.hpp"

char const* operation_name (int op);

maps an operation id (from peer_error_alert and peer_disconnected_alert) to its name. See peer_connection for the constants

enum operation_t

Declared in "libtorrent/operations.hpp"

name value description
op_bittorrent 0 this is used when the bittorrent logic determines to disconnect
op_iocontrol 1 a call to iocontrol failed
op_getpeername 2 a call to getpeername failed (querying the remote IP of a connection)
op_getname 3 a call to getname failed (querying the local IP of a connection)
op_alloc_recvbuf 4 an attempt to allocate a receive buffer failed
op_alloc_sndbuf 5 an attempt to allocate a send buffer failed
op_file_write 6 writing to a file failed
op_file_read 7 reading from a file failed
op_file 8 a non-read and non-write file operation failed
op_sock_write 9 a socket write operation failed
op_sock_read 10 a socket read operation failed
op_sock_open 11 a call to open(), to create a socket socket failed
op_sock_bind 12 a call to bind() on a socket failed
op_available 13 an attempt to query the number of bytes available to read from a socket failed
op_encryption 14 a call related to bittorrent protocol encryption failed
op_connect 15 an attempt to connect a socket failed
op_ssl_handshake 16 establishing an SSL connection failed
op_get_interface 17 a connection failed to satisfy the bind interface setting

enum alert_priority

Declared in "libtorrent/alert_types.hpp"

name value description
alert_priority_normal 0  
alert_priority_high 1  
alert_priority_critical 2  
libtorrent-rasterbar-1.1.13/docs/reference-Bdecoding.html000066400000000000000000000443471351156116000234070ustar00rootroot00000000000000 reference-Bdecoding.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Bdecoding

bdecode_node

Declared in "libtorrent/bdecode.hpp"

Sometimes it's important to get a non-owning reference to the root node ( to be able to copy it as a reference for instance). For that, use the non_owning() member function.

There are 5 different types of nodes, see type_t.

struct bdecode_node
{
   friend int bdecode (char const* start, char const* end, bdecode_node& ret
      , error_code& ec, int* error_pos, int depth_limit
      , int token_limit);
   bdecode_node ();
   bdecode_node (bdecode_node const&);
   bdecode_node& operator= (bdecode_node const&);
   type_t type () const;
   operator bool () const;
   bdecode_node non_owning () const;
   std::pair<char const*, int> data_section () const;
   bdecode_node list_at (int i) const;
   std::string list_string_value_at (int i
      , char const* default_val = "") const;
   int list_size () const;
   boost::int64_t list_int_value_at (int i
      , boost::int64_t default_val = 0) const;
   std::string dict_find_string_value (char const* key
      , char const* default_value = "") const;
   bdecode_node dict_find_string (char const* key) const;
   int dict_size () const;
   boost::int64_t dict_find_int_value (char const* key
      , boost::int64_t default_val = 0) const;
   bdecode_node dict_find (std::string key) const;
   bdecode_node dict_find_list (char const* key) const;
   bdecode_node dict_find (char const* key) const;
   bdecode_node dict_find_dict (std::string key) const;
   bdecode_node dict_find_dict (char const* key) const;
   std::pair<std::string, bdecode_node> dict_at (int i) const;
   bdecode_node dict_find_int (char const* key) const;
   boost::int64_t int_value () const;
   int string_length () const;
   std::string string_value () const;
   char const* string_ptr () const;
   void clear ();
   void swap (bdecode_node& n);
   void reserve (int tokens);
   void switch_underlying_buffer (char const* buf);

   enum type_t
   {
      none_t,
      dict_t,
      list_t,
      string_t,
      int_t,
   };
};

bdecode_node()

bdecode_node ();

creates a default constructed node, it will have the type none_t.

bdecode_node() operator=()

bdecode_node (bdecode_node const&);
bdecode_node& operator= (bdecode_node const&);

For owning nodes, the copy will create a copy of the tree, but the underlying buffer remains the same.

type()

type_t type () const;

the type of this node. See type_t.

bool()

operator bool () const;

returns true if type() != none_t.

non_owning()

bdecode_node non_owning () const;

return a non-owning reference to this node. This is useful to refer to the root node without copying it in assignments.

data_section()

std::pair<char const*, int> data_section () const;

returns the buffer and length of the section in the original bencoded buffer where this node is defined. For a dictionary for instance, this starts with d and ends with e, and has all the content of the dictionary in between.

list_at() list_string_value_at() list_int_value_at() list_size()

bdecode_node list_at (int i) const;
std::string list_string_value_at (int i
      , char const* default_val = "") const;
int list_size () const;
boost::int64_t list_int_value_at (int i
      , boost::int64_t default_val = 0) const;

functions with the list_ prefix operate on lists. These functions are only valid if type() == list_t. list_at() returns the item in the list at index i. i may not be greater than or equal to the size of the list. size() returns the size of the list.

dict_size() dict_find_dict() dict_find_string() dict_find_int() dict_at() dict_find_list() dict_find_int_value() dict_find() dict_find_string_value()

std::string dict_find_string_value (char const* key
      , char const* default_value = "") const;
bdecode_node dict_find_string (char const* key) const;
int dict_size () const;
boost::int64_t dict_find_int_value (char const* key
      , boost::int64_t default_val = 0) const;
bdecode_node dict_find (std::string key) const;
bdecode_node dict_find_list (char const* key) const;
bdecode_node dict_find (char const* key) const;
bdecode_node dict_find_dict (std::string key) const;
bdecode_node dict_find_dict (char const* key) const;
std::pair<std::string, bdecode_node> dict_at (int i) const;
bdecode_node dict_find_int (char const* key) const;

Functions with the dict_ prefix operates on dictionaries. They are only valid if type() == dict_t. In case a key you're looking up contains a 0 byte, you cannot use the null-terminated string overloads, but have to use std::string instead. dict_find_list will return a valid bdecode_node if the key is found _and_ it is a list. Otherwise it will return a default-constructed bdecode_node.

Functions with the _value suffix return the value of the node directly, rather than the nodes. In case the node is not found, or it has a different type, a default value is returned (which can be specified).

int_value()

boost::int64_t int_value () const;

this function is only valid if type() == int_t. It returns the value of the integer.

string_ptr() string_length() string_value()

int string_length () const;
std::string string_value () const;
char const* string_ptr () const;

these functions are only valid if type() == string_t. They return the string values. Note that string_ptr() is not null-terminated. string_length() returns the number of bytes in the string.

clear()

void clear ();

resets the bdecoded_node to a default constructed state. If this is an owning node, the tree is freed and all child nodes are invalidated.

swap()

void swap (bdecode_node& n);

Swap contents.

reserve()

void reserve (int tokens);

preallocate memory for the specified numbers of tokens. This is useful if you know approximately how many tokens are in the file you are about to parse. Doing so will save realloc operations while parsing. You should only call this on the root node, before passing it in to bdecode().

switch_underlying_buffer()

void switch_underlying_buffer (char const* buf);

this buffer MUST be identical to the one originally parsed. This operation is only defined on owning root nodes, i.e. the one passed in to decode().

enum type_t

Declared in "libtorrent/bdecode.hpp"

name value description
none_t 0 uninitialized or default constructed. This is also used to indicate that a node was not found in some cases.
dict_t 1 a dictionary node. The dict_find_ functions are valid.
list_t 2 a list node. The list_ functions are valid.
string_t 3 a string node, the string_ functions are valid.
int_t 4 an integer node. The int_ functions are valid.

print_entry()

Declared in "libtorrent/bdecode.hpp"

std::string print_entry (bdecode_node const& e
   , bool single_line = false, int indent = 0);

print the bencoded structure in a human-readable format to a string that's returned.

bdecode()

Declared in "libtorrent/bdecode.hpp"

int bdecode (char const* start, char const* end, bdecode_node& ret
   , error_code& ec, int* error_pos = 0, int depth_limit = 100
   , int token_limit = 1000000);

This function decodes/parses bdecoded data (for example a .torrent file). The data structure is returned in the ret argument. the buffer to parse is specified by the start of the buffer as well as the end, i.e. one byte past the end. If the buffer fails to parse, the function returns a non-zero value and fills in ec with the error code. The optional argument error_pos, if set to non-null, will be set to the byte offset into the buffer where the parse failure occurred.

depth_limit specifies the max number of nested lists or dictionaries are allowed in the data structure. (This affects the stack usage of the function, be careful not to set it too high).

token_limit is the max number of tokens allowed to be parsed from the buffer. This is simply a sanity check to not have unbounded memory usage.

The resulting bdecode_node is an owning node. That means it will be holding the whole parsed tree. When iterating lists and dictionaries, those bdecode_node objects will simply have references to the root or owning bdecode_node. If the root node is destructed, all other nodes that refer to anything in that tree become invalid.

However, the underlying buffer passed in to this function (start, end) must also remain valid while the bdecoded tree is used. The parsed tree produced by this function does not copy any data out of the buffer, but simply produces references back into it.

libtorrent-rasterbar-1.1.13/docs/reference-Bencoding.html000066400000000000000000000563051351156116000234160ustar00rootroot00000000000000 reference-Bencoding.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Bencoding

Bencoding is a common representation in bittorrent used for for dictionary, list, int and string hierarchies. It's used to encode .torrent files and some messages in the network protocol. libtorrent also uses it to store settings, resume data and other state between sessions.

Strings in bencoded structures are not necessarily representing text. Strings are raw byte buffers of a certain length. If a string is meant to be interpreted as text, it is required to be UTF-8 encoded. See BEP 3.

There are two mechanims to decode bencoded buffers in libtorrent.

The most flexible one is bdecode() bencode(), which returns a structure represented by entry. Oncea buffer has been decoded with this function, it can be discarded. The entry does not contain any references back to it. This means that bdecode() copies all the data out of the buffer and into its own hierarchy. This makes this function expensive, which might matter if you're parsing large amounts of data.

Another consideration is that bdecode() bencode() is a recursive parser. For this reason, in order to avoid DoS attacks by triggering a stack overflow, there is a recursion limit. This limit is a sanity check to make sure it doesn't run the risk of busting the stack.

The second mechanism is the decode function for bdecode_node. This function builds a tree that points back into the original buffer. The returned bdecode_node will not be valid once the buffer it was parsed out of is discarded.

Not only is this function more efficient because of less memory allocation and data copy, the parser is also not recursive, which means it probably performs a little bit better and can have a higher recursion limit on the structures it's parsing.

entry

Declared in "libtorrent/entry.hpp"

The entry class represents one node in a bencoded hierarchy. It works as a variant type, it can be either a list, a dictionary (std::map), an integer or a string.

class entry
{
   data_type type () const;
   entry (list_type const&);
   entry (integer_type const&);
   entry (dictionary_type const&);
   entry (string_type const&);
   entry (preformatted_type const&);
   entry (data_type t);
   void operator= (string_type const&);
   void operator= (integer_type const&);
   void operator= (entry const&);
   void operator= (dictionary_type const&);
   void operator= (list_type const&);
   void operator= (preformatted_type const&);
   void operator= (bdecode_node const&);
   preformatted_type& preformatted ();
   const integer_type& integer () const;
   const string_type& string () const;
   const preformatted_type& preformatted () const;
   const dictionary_type& dict () const;
   string_type& string ();
   list_type& list ();
   dictionary_type& dict ();
   integer_type& integer ();
   const list_type& list () const;
   void swap (entry& e);
   entry& operator[] (std::string const& key);
   const entry& operator[] (std::string const& key) const;
   entry& operator[] (char const* key);
   const entry& operator[] (char const* key) const;
   entry const* find_key (char const* key) const;
   entry* find_key (char const* key);
   entry* find_key (std::string const& key);
   entry const* find_key (std::string const& key) const;
   std::string to_string () const;
   std::string to_string (bool single_line) const;

   enum data_type
   {
      int_t,
      string_t,
      list_t,
      dictionary_t,
      undefined_t,
      preformatted_t,
   };

   mutable boost::uint8_t m_type_queried:1;
};

type()

data_type type () const;

returns the concrete type of the entry

entry()

entry (list_type const&);
entry (integer_type const&);
entry (dictionary_type const&);
entry (string_type const&);
entry (preformatted_type const&);

constructors directly from a specific type. The content of the argument is copied into the newly constructed entry

entry()

entry (data_type t);

construct an empty entry of the specified type. see data_type enum.

operator=()

void operator= (string_type const&);
void operator= (integer_type const&);
void operator= (entry const&);
void operator= (dictionary_type const&);
void operator= (list_type const&);
void operator= (preformatted_type const&);
void operator= (bdecode_node const&);

copies the structure of the right hand side into this entry.

string() integer() dict() preformatted() list()

preformatted_type& preformatted ();
const integer_type& integer () const;
const string_type& string () const;
const preformatted_type& preformatted () const;
const dictionary_type& dict () const;
string_type& string ();
list_type& list ();
dictionary_type& dict ();
integer_type& integer ();
const list_type& list () const;

The integer(), string(), list() and dict() functions are accessors that return the respective type. If the entry object isn't of the type you request, the accessor will throw libtorrent_exception (which derives from std::runtime_error). You can ask an entry for its type through the type() function.

If you want to create an entry you give it the type you want it to have in its constructor, and then use one of the non-const accessors to get a reference which you then can assign the value you want it to have.

The typical code to get info from a torrent file will then look like this:

entry torrent_file;
// ...

// throws if this is not a dictionary
entry::dictionary_type const& dict = torrent_file.dict();
entry::dictionary_type::const_iterator i;
i = dict.find("announce");
if (i != dict.end())
{
        std::string tracker_url = i->second.string();
        std::cout << tracker_url << "\n";
}

The following code is equivalent, but a little bit shorter:

entry torrent_file;
// ...

// throws if this is not a dictionary
if (entry* i = torrent_file.find_key("announce"))
{
        std::string tracker_url = i->string();
        std::cout << tracker_url << "\n";
}

To make it easier to extract information from a torrent file, the class torrent_info exists.

swap()

void swap (entry& e);

swaps the content of this with e.

operator[]()

entry& operator[] (std::string const& key);
const entry& operator[] (std::string const& key) const;
entry& operator[] (char const* key);
const entry& operator[] (char const* key) const;

All of these functions requires the entry to be a dictionary, if it isn't they will throw libtorrent::type_error.

The non-const versions of the operator[] will return a reference to either the existing element at the given key or, if there is no element with the given key, a reference to a newly inserted element at that key.

The const version of operator[] will only return a reference to an existing element at the given key. If the key is not found, it will throw libtorrent::type_error.

find_key()

entry const* find_key (char const* key) const;
entry* find_key (char const* key);
entry* find_key (std::string const& key);
entry const* find_key (std::string const& key) const;

These functions requires the entry to be a dictionary, if it isn't they will throw libtorrent::type_error.

They will look for an element at the given key in the dictionary, if the element cannot be found, they will return 0. If an element with the given key is found, the return a pointer to it.

to_string()

std::string to_string () const;
std::string to_string (bool single_line) const;

returns a pretty-printed string representation of the bencoded structure, with JSON-style syntax

enum data_type

Declared in "libtorrent/entry.hpp"

name value description
int_t 0  
string_t 1  
list_t 2  
dictionary_t 3  
undefined_t 4  
preformatted_t 5  
m_type_queried
in debug mode this is set to false by bdecode to indicate that the program has not yet queried the type of this entry, and should not assume that it has a certain type. This is asserted in the accessor functions. This does not apply if exceptions are used.

bdecode() bencode()

Declared in "libtorrent/bencode.hpp"

template<class InIt> entry bdecode (InIt start, InIt end);
template<class OutIt> int bencode (OutIt out, const entry& e);
template<class InIt> entry bdecode (InIt start, InIt end, int& len);

These functions will encode data to bencoded or decode bencoded data.

If possible, bdecode() producing a bdecode_node should be preferred over this function.

The entry class is the internal representation of the bencoded data and it can be used to retrieve information, an entry can also be build by the program and given to bencode() to encode it into the OutIt iterator.

The OutIt and InIt are iterators (InputIterator and OutputIterator respectively). They are templates and are usually instantiated as ostream_iterator, back_insert_iterator or istream_iterator. These functions will assume that the iterator refers to a character (char). So, if you want to encode entry e into a buffer in memory, you can do it like this:

std::vector<char> buffer;
bencode(std::back_inserter(buf), e);

If you want to decode a torrent file from a buffer in memory, you can do it like this:

std::vector<char> buffer;
// ...
entry e = bdecode(buf.begin(), buf.end());

Or, if you have a raw char buffer:

const char* buf;
// ...
entry e = bdecode(buf, buf + data_size);

Now we just need to know how to retrieve information from the entry.

If bdecode() encounters invalid encoded data in the range given to it it will return a default constructed entry object.

operator<<()

Declared in "libtorrent/entry.hpp"

inline std::ostream& operator<< (std::ostream& os, const entry& e);

prints the bencoded structure to the ostream as a JSON-style structure.

libtorrent-rasterbar-1.1.13/docs/reference-Core.html000066400000000000000000010722511351156116000224150ustar00rootroot00000000000000 reference-Core.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Core

stats_metric

Declared in "libtorrent/session_stats.hpp"

describes one statistics metric from the session. For more information, see the session statistics section.

struct stats_metric
{
   enum metric_type_t
   {
      type_counter,
      type_gauge,
   };

   char const* name;
   int value_index;
   metric_type_t type;
};

enum metric_type_t

Declared in "libtorrent/session_stats.hpp"

name value description
type_counter 0  
type_gauge 1  

peer_class_info

Declared in "libtorrent/peer_class.hpp"

holds settings for a peer class. Used in set_peer_class() and get_peer_class() calls.

struct peer_class_info
{
   bool ignore_unchoke_slots;
   int connection_limit_factor;
   std::string label;
   int upload_limit;
   int download_limit;
   int upload_priority;
   int download_priority;
};
ignore_unchoke_slots
ignore_unchoke_slots determines whether peers should always unchoke a peer, regardless of the choking algorithm, or if it should honor the unchoke slot limits. It's used for local peers by default. If any of the peer classes a peer belongs to has this set to true, that peer will be unchoked at all times.
connection_limit_factor
adjusts the connection limit (global and per torrent) that applies to this peer class. By default, local peers are allowed to exceed the normal connection limit for instance. This is specified as a percent factor. 100 makes the peer class apply normally to the limit. 200 means as long as there are fewer connections than twice the limit, we accept this peer. This factor applies both to the global connection limit and the per-torrent limit. Note that if not used carefully one peer class can potentially completely starve out all other over time.
label
not used by libtorrent. It's intended as a potentially user-facing identifier of this peer class.
upload_limit download_limit
transfer rates limits for the whole peer class. They are specified in bytes per second and apply to the sum of all peers that are members of this class.
upload_priority download_priority
relative priorities used by the bandwidth allocator in the rate limiter. If no rate limits are in use, the priority is not used either. Priorities start at 1 (0 is not a valid priority) and may not exceed 255.

session_handle

Declared in "libtorrent/session_handle.hpp"

struct session_handle
{
   session_handle ();
   session_handle (aux::session_impl* impl);
   bool is_valid () const;
   void load_state (bdecode_node const& e, boost::uint32_t flags = 0xffffffff);
   void save_state (entry& e, boost::uint32_t flags = 0xffffffff) const;
   void refresh_torrent_status (std::vector<torrent_status>* ret
      , boost::uint32_t flags = 0) const;
   void get_torrent_status (std::vector<torrent_status>* ret
      , boost::function<bool(torrent_status const&)> const& pred
      , boost::uint32_t flags = 0) const;
   void post_torrent_updates (boost::uint32_t flags = 0xffffffff);
   void post_session_stats ();
   void post_dht_stats ();
   torrent_handle find_torrent (sha1_hash const& info_hash) const;
   std::vector<torrent_handle> get_torrents () const;
   void async_add_torrent (add_torrent_params const& params);
   torrent_handle add_torrent (add_torrent_params const& params, error_code& ec);
   torrent_handle add_torrent (add_torrent_params const& params);
   void resume ();
   void pause ();
   bool is_paused () const;
   void set_load_function (user_load_function_t fun);
   void get_cache_info (cache_status* ret, torrent_handle h = torrent_handle(), int flags = 0) const;
   bool is_dht_running () const;
   dht_settings get_dht_settings () const;
   void set_dht_settings (dht_settings const& settings);
   void set_dht_storage (dht::dht_storage_constructor_type sc);
   void add_dht_node (std::pair<std::string, int> const& node);
   void dht_get_item (sha1_hash const& target);
   void dht_get_item (boost::array<char, 32> key
      , std::string salt = std::string());
   sha1_hash dht_put_item (entry data);
   void dht_put_item (boost::array<char, 32> key
      , boost::function<void(entry&, boost::array<char,64>&
      , boost::uint64_t&, std::string const&)> cb
      , std::string salt = std::string());
   void dht_get_peers (sha1_hash const& info_hash);
   void dht_announce (sha1_hash const& info_hash, int port = 0, int flags = 0);
   void dht_direct_request (udp::endpoint ep, entry const& e, void* userdata = 0);
   void add_extension (boost::function<boost::shared_ptr<torrent_plugin>(
      torrent_handle const&, void*)> ext);
   void add_extension (boost::shared_ptr<plugin> ext);
   ip_filter get_ip_filter () const;
   void set_ip_filter (ip_filter const& f);
   void set_port_filter (port_filter const& f);
   void set_key (int key);
   bool is_listening () const;
   unsigned short listen_port () const;
   unsigned short ssl_listen_port () const;
   ip_filter get_peer_class_filter () const;
   void set_peer_class_filter (ip_filter const& f);
   void set_peer_class_type_filter (peer_class_type_filter const& f);
   peer_class_type_filter get_peer_class_type_filter () const;
   peer_class_t create_peer_class (char const* name);
   void delete_peer_class (peer_class_t cid);
   peer_class_info get_peer_class (peer_class_t cid);
   void set_peer_class (peer_class_t cid, peer_class_info const& pci);
   void remove_torrent (const torrent_handle& h, int options = 0);
   settings_pack get_settings () const;
   void apply_settings (settings_pack const& s);
   void pop_alerts (std::vector<alert*>* alerts);
   void set_alert_notify (boost::function<void()> const& fun);
   alert* wait_for_alert (time_duration max_wait);
   void delete_port_mapping (int handle);
   int add_port_mapping (protocol_type t, int external_port, int local_port);
   aux::session_impl* native_handle () const;

   enum save_state_flags_t
   {
      save_settings,
      save_dht_settings,
      save_dht_state,
   };

   enum options_t
   {
      delete_files,
      delete_partfile,
   };

   enum session_flags_t
   {
      add_default_plugins,
      start_default_features,
   };

   enum protocol_type
   {
      udp,
      tcp,
   };
};

load_state() save_state()

void load_state (bdecode_node const& e, boost::uint32_t flags = 0xffffffff);
void save_state (entry& e, boost::uint32_t flags = 0xffffffff) const;

loads and saves all session settings, including dht_settings, encryption settings and proxy settings. save_state writes all keys to the entry that's passed in, which needs to either not be initialized, or initialized as a dictionary.

load_state expects a bdecode_node which can be built from a bencoded buffer with bdecode().

The flags argument is used to filter which parts of the session state to save or load. By default, all state is saved/restored (except for the individual torrents). see save_state_flags_t

When saving settings, there are two fields that are not loaded. peer_fingerprint and user_agent. Those are left as configured by the session_settings passed to the session constructor or subsequently set via apply_settings().

get_torrent_status() refresh_torrent_status()

void refresh_torrent_status (std::vector<torrent_status>* ret
      , boost::uint32_t flags = 0) const;
void get_torrent_status (std::vector<torrent_status>* ret
      , boost::function<bool(torrent_status const&)> const& pred
      , boost::uint32_t flags = 0) const;

Note

these calls are potentially expensive and won't scale well with lots of torrents. If you're concerned about performance, consider using post_torrent_updates() instead.

get_torrent_status returns a vector of the torrent_status for every torrent which satisfies pred, which is a predicate function which determines if a torrent should be included in the returned set or not. Returning true means it should be included and false means excluded. The flags argument is the same as to torrent_handle::status(). Since pred is guaranteed to be called for every torrent, it may be used to count the number of torrents of different categories as well.

refresh_torrent_status takes a vector of torrent_status structs (for instance the same vector that was returned by get_torrent_status() ) and refreshes the status based on the handle member. It is possible to use this function by first setting up a vector of default constructed torrent_status objects, only initializing the handle member, in order to request the torrent status for multiple torrents in a single call. This can save a significant amount of time if you have a lot of torrents.

Any torrent_status object whose handle member is not referring to a valid torrent are ignored.

The intended use of these functions is to start off by calling get_torrent_status() to get a list of all torrents that match your criteria. Then call refresh_torrent_status() on that list. This will only refresh the status for the torrents in your list, and thus ignore all other torrents you might be running. This may save a significant amount of time, especially if the number of torrents you're interested in is small. In order to keep your list of interested torrents up to date, you can either call get_torrent_status() from time to time, to include torrents you might have become interested in since the last time. In order to stop refreshing a certain torrent, simply remove it from the list.

post_torrent_updates()

void post_torrent_updates (boost::uint32_t flags = 0xffffffff);

This functions instructs the session to post the state_update_alert, containing the status of all torrents whose state changed since the last time this function was called.

Only torrents who has the state subscription flag set will be included. This flag is on by default. See add_torrent_params. the flags argument is the same as for torrent_handle::status(). see torrent_handle::status_flags_t.

post_session_stats()

void post_session_stats ();

This function will post a session_stats_alert object, containing a snapshot of the performance counters from the internals of libtorrent. To interpret these counters, query the session via session_stats_metrics().

For more information, see the session statistics section.

post_dht_stats()

void post_dht_stats ();

This will cause a dht_stats_alert to be posted.

find_torrent() get_torrents()

torrent_handle find_torrent (sha1_hash const& info_hash) const;
std::vector<torrent_handle> get_torrents () const;

find_torrent() looks for a torrent with the given info-hash. In case there is such a torrent in the session, a torrent_handle to that torrent is returned. In case the torrent cannot be found, an invalid torrent_handle is returned.

See torrent_handle::is_valid() to know if the torrent was found or not.

get_torrents() returns a vector of torrent_handles to all the torrents currently in the session.

add_torrent() async_add_torrent()

void async_add_torrent (add_torrent_params const& params);
torrent_handle add_torrent (add_torrent_params const& params, error_code& ec);
torrent_handle add_torrent (add_torrent_params const& params);

You add torrents through the add_torrent() function where you give an object with all the parameters. The add_torrent() overloads will block until the torrent has been added (or failed to be added) and returns an error code and a torrent_handle. In order to add torrents more efficiently, consider using async_add_torrent() which returns immediately, without waiting for the torrent to add. Notification of the torrent being added is sent as add_torrent_alert.

The overload that does not take an error_code throws an exception on error and is not available when building without exception support. The torrent_handle returned by add_torrent() can be used to retrieve information about the torrent's progress, its peers etc. It is also used to abort a torrent.

If the torrent you are trying to add already exists in the session (is either queued for checking, being checked or downloading) add_torrent() will throw libtorrent_exception which derives from std::exception unless duplicate_is_error is set to false. In that case, add_torrent() will return the handle to the existing torrent.

all torrent_handles must be destructed before the session is destructed!

pause() resume() is_paused()

void resume ();
void pause ();
bool is_paused () const;

Pausing the session has the same effect as pausing every torrent in it, except that torrents will not be resumed by the auto-manage mechanism. Resuming will restore the torrents to their previous paused state. i.e. the session pause state is separate from the torrent pause state. A torrent is inactive if it is paused or if the session is paused.

set_load_function()

void set_load_function (user_load_function_t fun);

the feature of dynamically loading/unloading torrents is deprecated and discouraged

This function enables dynamic loading of torrent files. When a torrent is unloaded but needs to be available in memory, this function is called from within the libtorrent network thread. From within this thread, you can not use any of the public APIs of libtorrent itself. The the info-hash of the torrent is passed in to the function and it is expected to fill in the passed in vector<char> with the .torrent file corresponding to it.

If there is an error loading the torrent file, the error_code (ec) should be set to reflect the error. In such case, the torrent itself is stopped and set to an error state with the corresponding error code.

Given that the function is called from the internal network thread of libtorrent, it's important to not stall. libtorrent will not be able to send nor receive any data until the function call returns.

The signature of the function to pass in is:

void fun(sha1_hash const& info_hash, std::vector<char>& buf, error_code& ec);

get_cache_info()

void get_cache_info (cache_status* ret, torrent_handle h = torrent_handle(), int flags = 0) const;

Fills in the cache_status struct with information about the given torrent. If flags is session::disk_cache_no_pieces the cache_status::pieces field will not be set. This may significantly reduce the cost of this call.

get_dht_settings() is_dht_running() set_dht_settings()

bool is_dht_running () const;
dht_settings get_dht_settings () const;
void set_dht_settings (dht_settings const& settings);

set_dht_settings sets some parameters available to the dht node. See dht_settings for more information.

is_dht_running() returns true if the DHT support has been started and false otherwise.

get_dht_settings() returns the current settings

set_dht_storage()

void set_dht_storage (dht::dht_storage_constructor_type sc);

set_dht_storage set a dht custom storage constructor function to be used internally when the dht is created.

Since the dht storage is a critical component for the dht behavior, this function will only be effective the next time the dht is started. If you never touch this feature, a default map-memory based storage is used.

If you want to make sure the dht is initially created with your custom storage, create a session with the setting settings_pack::enable_dht to false, set your constructor function and call apply_settings with settings_pack::enable_dht to true.

add_dht_node()

void add_dht_node (std::pair<std::string, int> const& node);

add_dht_node takes a host name and port pair. That endpoint will be pinged, and if a valid DHT reply is received, the node will be added to the routing table.

dht_get_item()

void dht_get_item (sha1_hash const& target);

query the DHT for an immutable item at the target hash. the result is posted as a dht_immutable_item_alert.

dht_get_item()

void dht_get_item (boost::array<char, 32> key
      , std::string salt = std::string());

query the DHT for a mutable item under the public key key. this is an ed25519 key. salt is optional and may be left as an empty string if no salt is to be used. if the item is found in the DHT, a dht_mutable_item_alert is posted.

dht_put_item()

sha1_hash dht_put_item (entry data);

store the given bencoded data as an immutable item in the DHT. the returned hash is the key that is to be used to look the item up again. It's just the SHA-1 hash of the bencoded form of the structure.

dht_put_item()

void dht_put_item (boost::array<char, 32> key
      , boost::function<void(entry&, boost::array<char,64>&
      , boost::uint64_t&, std::string const&)> cb
      , std::string salt = std::string());

store a mutable item. The key is the public key the blob is to be stored under. The optional salt argument is a string that is to be mixed in with the key when determining where in the DHT the value is to be stored. The callback function is called from within the libtorrent network thread once we've found where to store the blob, possibly with the current value stored under the key. The values passed to the callback functions are:

entry& value
the current value stored under the key (may be empty). Also expected to be set to the value to be stored by the function.
boost::array<char,64>& signature
the signature authenticating the current value. This may be zeros if there is currently no value stored. The function is expected to fill in this buffer with the signature of the new value to store. To generate the signature, you may want to use the sign_mutable_item function.
boost::uint64_t& seq
current sequence number. May be zero if there is no current value. The function is expected to set this to the new sequence number of the value that is to be stored. Sequence numbers must be monotonically increasing. Attempting to overwrite a value with a lower or equal sequence number will fail, even if the signature is correct.
std::string const& salt
this is the salt that was used for this put call.

Since the callback function cb is called from within libtorrent, it is critical to not perform any blocking operations. Ideally not even locking a mutex. Pass any data required for this function along with the function object's context and make the function entirely self-contained. The only reason data blob's value is computed via a function instead of just passing in the new value is to avoid race conditions. If you want to update the value in the DHT, you must first retrieve it, then modify it, then write it back. The way the DHT works, it is natural to always do a lookup before storing and calling the callback in between is convenient.

dht_direct_request()

void dht_direct_request (udp::endpoint ep, entry const& e, void* userdata = 0);

Send an arbitrary DHT request directly to the specified endpoint. This function is intended for use by plugins. When a response is received or the request times out, a dht_direct_response_alert will be posted with the response (if any) and the userdata pointer passed in here. Since this alert is a response to an explicit call, it will always be posted, regardless of the alert mask.

add_extension()

void add_extension (boost::function<boost::shared_ptr<torrent_plugin>(
      torrent_handle const&, void*)> ext);
void add_extension (boost::shared_ptr<plugin> ext);

This function adds an extension to this session. The argument is a function object that is called with a torrent_handle and which should return a boost::shared_ptr<torrent_plugin>. To write custom plugins, see libtorrent plugins. For the typical bittorrent client all of these extensions should be added. The main plugins implemented in libtorrent are:

metadata extension
Allows peers to download the metadata (.torrent files) from the swarm directly. Makes it possible to join a swarm with just a tracker and info-hash.
#include <libtorrent/extensions/metadata_transfer.hpp>
ses.add_extension(&libtorrent::create_metadata_plugin);
uTorrent metadata
Same as metadata extension but compatible with uTorrent.
#include <libtorrent/extensions/ut_metadata.hpp>
ses.add_extension(&libtorrent::create_ut_metadata_plugin);
uTorrent peer exchange
Exchanges peers between clients.
#include <libtorrent/extensions/ut_pex.hpp>
ses.add_extension(&libtorrent::create_ut_pex_plugin);
smart ban plugin
A plugin that, with a small overhead, can ban peers that sends bad data with very high accuracy. Should eliminate most problems on poisoned torrents.
#include <libtorrent/extensions/smart_ban.hpp>
ses.add_extension(&libtorrent::create_smart_ban_plugin);

get_ip_filter() set_ip_filter()

ip_filter get_ip_filter () const;
void set_ip_filter (ip_filter const& f);

Sets a filter that will be used to reject and accept incoming as well as outgoing connections based on their originating ip address. The default filter will allow connections to any ip address. To build a set of rules for which addresses are accepted and not, see ip_filter.

Each time a peer is blocked because of the IP filter, a peer_blocked_alert is generated. get_ip_filter() Returns the ip_filter currently in the session. See ip_filter.

set_port_filter()

void set_port_filter (port_filter const& f);

apply port_filter f to incoming and outgoing peers. a port filter will reject making outgoing peer connections to certain remote ports. The main intention is to be able to avoid triggering certain anti-virus software by connecting to SMTP, FTP ports.

set_key()

void set_key (int key);

sets the key sent to trackers. If it's not set, it is initialized by libtorrent. The key may be used by the tracker to identify the peer potentially across you changing your IP.

listen_port() ssl_listen_port() is_listening()

bool is_listening () const;
unsigned short listen_port () const;
unsigned short ssl_listen_port () const;

is_listening() will tell you whether or not the session has successfully opened a listening port. If it hasn't, this function will return false, and then you can set a new settings_pack::listen_interfaces to try another interface and port to bind to.

listen_port() returns the port we ended up listening on.

get_peer_class_filter() set_peer_class_filter()

ip_filter get_peer_class_filter () const;
void set_peer_class_filter (ip_filter const& f);

Sets the peer class filter for this session. All new peer connections will take this into account and be added to the peer classes specified by this filter, based on the peer's IP address.

The ip-filter essentially maps an IP -> uint32. Each bit in that 32 bit integer represents a peer class. The least significant bit represents class 0, the next bit class 1 and so on.

For more info, see ip_filter.

For example, to make all peers in the range 200.1.1.0 - 200.1.255.255 belong to their own peer class, apply the following filter:

ip_filter f = ses.get_peer_class_filter();
peer_class_t const my_class = ses.create_peer_class("200.1.x.x IP range");
f.add_rule(make_address("200.1.1.0"), make_address("200.1.255.255")
        , 1 << my_class);
ses.set_peer_class_filter(f);

This setting only applies to new connections, it won't affect existing peer connections.

This function is limited to only peer class 0-31, since there are only 32 bits in the IP range mapping. Only the set bits matter; no peer class will be removed from a peer as a result of this call, peer classes are only added.

The peer_class argument cannot be greater than 31. The bitmasks representing peer classes in the peer_class_filter are 32 bits.

The get_peer_class_filter() function returns the current filter.

For more information, see peer classes.

set_peer_class_type_filter() get_peer_class_type_filter()

void set_peer_class_type_filter (peer_class_type_filter const& f);
peer_class_type_filter get_peer_class_type_filter () const;

Sets and gets the peer class type filter. This is controls automatic peer class assignments to peers based on what kind of socket it is.

It does not only support assigning peer classes, it also supports removing peer classes based on socket type.

The order of these rules being applied are:

  1. peer-class IP filter
  2. peer-class type filter, removing classes
  3. peer-class type filter, adding classes

For more information, see peer classes.

create_peer_class()

peer_class_t create_peer_class (char const* name);

Creates a new peer class (see peer classes) with the given name. The returned integer is the new peer class identifier. Peer classes may have the same name, so each invocation of this function creates a new class and returns a unique identifier.

Identifiers are assigned from low numbers to higher. So if you plan on using certain peer classes in a call to set_peer_class_filter(), make sure to create those early on, to get low identifiers.

For more information on peer classes, see peer classes.

delete_peer_class()

void delete_peer_class (peer_class_t cid);

This call dereferences the reference count of the specified peer class. When creating a peer class it's automatically referenced by 1. If you want to recycle a peer class, you may call this function. You may only call this function once per peer class you create. Calling it more than once for the same class will lead to memory corruption.

Since peer classes are reference counted, this function will not remove the peer class if it's still assigned to torrents or peers. It will however remove it once the last peer and torrent drops their references to it.

There is no need to call this function for custom peer classes. All peer classes will be properly destructed when the session object destructs.

For more information on peer classes, see peer classes.

get_peer_class() set_peer_class()

peer_class_info get_peer_class (peer_class_t cid);
void set_peer_class (peer_class_t cid, peer_class_info const& pci);

These functions queries information from a peer class and updates the configuration of a peer class, respectively.

cid must refer to an existing peer class. If it does not, the return value of get_peer_class() is undefined.

set_peer_class() sets all the information in the peer_class_info object in the specified peer class. There is no option to only update a single property.

A peer or torrent belonging to more than one class, the highest priority among any of its classes is the one that is taken into account.

For more information, see peer classes.

remove_torrent()

void remove_torrent (const torrent_handle& h, int options = 0);

remove_torrent() will close all peer connections associated with the torrent and tell the tracker that we've stopped participating in the swarm. This operation cannot fail. When it completes, you will receive a torrent_removed_alert.

The optional second argument options can be used to delete all the files downloaded by this torrent. To do so, pass in the value session::delete_files. The removal of the torrent is asynchronous, there is no guarantee that adding the same torrent immediately after it was removed will not throw a libtorrent_exception exception. Once the torrent is deleted, a torrent_deleted_alert is posted.

get_settings() apply_settings()

settings_pack get_settings () const;
void apply_settings (settings_pack const& s);

Applies the settings specified by the settings_pack s. This is an asynchronous operation that will return immediately and actually apply the settings to the main thread of libtorrent some time later.

pop_alerts() wait_for_alert() set_alert_notify()

void pop_alerts (std::vector<alert*>* alerts);
void set_alert_notify (boost::function<void()> const& fun);
alert* wait_for_alert (time_duration max_wait);

Alerts is the main mechanism for libtorrent to report errors and events. pop_alerts fills in the vector passed to it with pointers to new alerts. The session still owns these alerts and they will stay valid until the next time pop_alerts is called. You may not delete the alert objects.

It is safe to call pop_alerts from multiple different threads, as long as the alerts themselves are not accessed once another thread calls pop_alerts. Doing this requires manual synchronization between the popping threads.

wait_for_alert will block the current thread for max_wait time duration, or until another alert is posted. If an alert is available at the time of the call, it returns immediately. The returned alert pointer is the head of the alert queue. wait_for_alert does not pop alerts from the queue, it merely peeks at it. The returned alert will stay valid until pop_alerts is called twice. The first time will pop it and the second will free it.

If there is no alert in the queue and no alert arrives within the specified timeout, wait_for_alert returns NULL.

In the python binding, wait_for_alert takes the number of milliseconds to wait as an integer.

The alert queue in the session will not grow indefinitely. Make sure to pop periodically to not miss notifications. To control the max number of alerts that's queued by the session, see settings_pack::alert_queue_size.

Some alerts are considered so important that they are posted even when the alert queue is full. Some alerts are considered mandatory and cannot be disabled by the alert_mask. For instance, save_resume_data_alert and save_resume_data_failed_alert are always posted, regardless of the alert mask.

To control which alerts are posted, set the alert_mask (settings_pack::alert_mask).

the set_alert_notify function lets the client set a function object to be invoked every time the alert queue goes from having 0 alerts to 1 alert. This function is called from within libtorrent, it may be the main thread, or it may be from within a user call. The intention of of the function is that the client wakes up its main thread, to poll for more alerts using pop_alerts(). If the notify function fails to do so, it won't be called again, until pop_alerts is called for some other reason. For instance, it could signal an eventfd, post a message to an HWND or some other main message pump. The actual retrieval of alerts should not be done in the callback. In fact, the callback should not block. It should not perform any expensive work. It really should just notify the main application thread.

add_port_mapping() delete_port_mapping()

void delete_port_mapping (int handle);
int add_port_mapping (protocol_type t, int external_port, int local_port);

add_port_mapping adds a port forwarding on UPnP and/or NAT-PMP, whichever is enabled. The return value is a handle referring to the port mapping that was just created. Pass it to delete_port_mapping() to remove it.

native_handle()

aux::session_impl* native_handle () const;

This function is intended only for use by plugins. This type does not have a stable API and should be relied on as little as possible.

enum save_state_flags_t

Declared in "libtorrent/session_handle.hpp"

name value description
save_settings 1 saves settings (i.e. the settings_pack)
save_dht_settings 2 saves dht_settings
save_dht_state 4 saves dht state such as nodes and node-id, possibly accelerating joining the DHT if provided at next session startup.

enum options_t

Declared in "libtorrent/session_handle.hpp"

name value description
delete_files 1 delete the files belonging to the torrent from disk. including the part-file, if there is one
delete_partfile 2 delete just the part-file associated with this torrent

enum session_flags_t

Declared in "libtorrent/session_handle.hpp"

name value description
add_default_plugins 1 this will add common extensions like ut_pex, ut_metadata, lt_tex smart_ban and possibly others.
start_default_features 2 this will start features like DHT, local service discovery, UPnP and NAT-PMP.

enum protocol_type

Declared in "libtorrent/session_handle.hpp"

name value description
udp 1  
tcp 2  

session_proxy

Declared in "libtorrent/session.hpp"

this is a holder for the internal session implementation object. Once the session destruction is explicitly initiated, this holder is used to synchronize the completion of the shutdown. The lifetime of this object may outlive session, causing the session destructor to not block. The session_proxy destructor will block however, until the underlying session is done shutting down.

class session_proxy
{
   ~session_proxy ();
   session_proxy (session_proxy const&) = default;
   session_proxy& operator= (session_proxy const&) = default;
   session_proxy ();
};

session_proxy() ~session_proxy() operator=()

~session_proxy ();
session_proxy (session_proxy const&) = default;
session_proxy& operator= (session_proxy const&) = default;
session_proxy ();

default constructor, does not refer to any session implementation object.

session

Declared in "libtorrent/session.hpp"

The session holds all state that spans multiple torrents. Among other things it runs the network loop and manages all torrents. Once it's created, the session object will spawn the main thread that will do all the work. The main thread will be idle as long it doesn't have any torrents to participate in.

You have some control over session configuration through the session::apply_settings() member function. To change one or more configuration options, create a settings_pack. object and fill it with the settings to be set and pass it in to session::apply_settings().

see apply_settings().

class session : public boost::noncopyable, public session_handle
{
   session (settings_pack const& pack = settings_pack()
      , int flags = start_default_features | add_default_plugins);
   session (settings_pack const& pack
      , io_service& ios
      , int flags = start_default_features | add_default_plugins);
   ~session ();
   session_proxy abort ();
};

session()

session (settings_pack const& pack = settings_pack()
      , int flags = start_default_features | add_default_plugins);

Constructs the session objects which acts as the container of torrents. It provides configuration options across torrents (such as rate limits, disk cache, ip filter etc.). In order to avoid a race condition between starting the session and configuring it, you can pass in a settings_pack object. Its settings will take effect before the session starts up.

The flags parameter can be used to start default features (UPnP & NAT-PMP) and default plugins (ut_metadata, ut_pex and smart_ban). The default is to start those features. If you do not want them to start, pass 0 as the flags parameter.

session()

session (settings_pack const& pack
      , io_service& ios
      , int flags = start_default_features | add_default_plugins);

overload of the constructor that takes an external io_service to run the session object on. This is primarily useful for tests that may want to run multiple sessions on a single io_service, or low resource systems where additional threads are expensive and sharing an io_service with other events is fine.

Warning

The session object does not cleanly terminate with an external io_service. The io_service::run() call _must_ have returned before it's safe to destruct the session. Which means you MUST call session::abort() and save the session_proxy first, then destruct the session object, then sync with the io_service, then destruct the session_proxy object.

~session()

~session ();

The destructor of session will notify all trackers that our torrents have been shut down. If some trackers are down, they will time out. All this before the destructor of session returns. So, it's advised that any kind of interface (such as windows) are closed before destructing the session object. Because it can take a few second for it to finish. The timeout can be set with apply_settings().

abort()

session_proxy abort ();

In case you want to destruct the session asynchronously, you can request a session destruction proxy. If you don't do this, the destructor of the session object will block while the trackers are contacted. If you keep one session_proxy to the session when destructing it, the destructor will not block, but start to close down the session, the destructor of the proxy will then synchronize the threads. So, the destruction of the session is performed from the session destructor call until the session_proxy destructor call. The session_proxy does not have any operations on it (since the session is being closed down, no operations are allowed on it). The only valid operation is calling the destructor:

class session_proxy
{
public:
        session_proxy();
        ~session_proxy()
};

bt_peer_connection_handle

Declared in "libtorrent/peer_connection_handle.hpp"

struct bt_peer_connection_handle : public peer_connection_handle
{
   explicit bt_peer_connection_handle (peer_connection_handle pc);
   bool support_extensions () const;
   bool packet_finished () const;
   bool supports_encryption () const;
   void switch_recv_crypto (boost::shared_ptr<crypto_plugin> crypto);
   void switch_send_crypto (boost::shared_ptr<crypto_plugin> crypto);
   boost::shared_ptr<bt_peer_connection> native_handle () const;
};

torrent_status

Declared in "libtorrent/torrent_status.hpp"

holds a snapshot of the status of a torrent, as queried by torrent_handle::status().

struct torrent_status
{
   bool operator== (torrent_status const& st) const;

   enum state_t
   {
      checking_files,
      downloading_metadata,
      downloading,
      finished,
      seeding,
      allocating,
      checking_resume_data,
   };

   torrent_handle handle;
   std::string save_path;
   std::string name;
   boost::weak_ptr<const torrent_info> torrent_file;
   time_duration next_announce;
   std::string current_tracker;
   boost::int64_t total_download;
   boost::int64_t total_upload;
   boost::int64_t total_payload_download;
   boost::int64_t total_payload_upload;
   boost::int64_t total_failed_bytes;
   boost::int64_t total_redundant_bytes;
   bitfield pieces;
   bitfield verified_pieces;
   boost::int64_t total_done;
   boost::int64_t total_wanted_done;
   boost::int64_t total_wanted;
   boost::int64_t all_time_upload;
   boost::int64_t all_time_download;
   time_t added_time;
   time_t completed_time;
   time_t last_seen_complete;
   storage_mode_t storage_mode;
   float progress;
   int progress_ppm;
   int queue_position;
   int download_rate;
   int upload_rate;
   int download_payload_rate;
   int upload_payload_rate;
   int num_seeds;
   int num_peers;
   int num_complete;
   int num_incomplete;
   int list_seeds;
   int list_peers;
   int connect_candidates;
   int num_pieces;
   int distributed_full_copies;
   int distributed_fraction;
   float distributed_copies;
   int block_size;
   int num_uploads;
   int num_connections;
   int uploads_limit;
   int connections_limit;
   int up_bandwidth_queue;
   int down_bandwidth_queue;
   int time_since_upload;
   int time_since_download;
   int active_time;
   int finished_time;
   int seeding_time;
   int seed_rank;
   int last_scrape;
   int priority;
   state_t state;
   bool need_save_resume;
   bool ip_filter_applies;
   bool upload_mode;
   bool share_mode;
   bool super_seeding;
   bool paused;
   bool auto_managed;
   bool sequential_download;
   bool is_seeding;
   bool is_finished;
   bool has_metadata;
   bool has_incoming;
   bool seed_mode;
   bool moving_storage;
   bool is_loaded;
   bool announcing_to_trackers;
   bool announcing_to_lsd;
   bool announcing_to_dht;
   bool stop_when_ready;
   sha1_hash info_hash;
};

operator==()

bool operator== (torrent_status const& st) const;

compares if the torrent status objects come from the same torrent. i.e. only the torrent_handle field is compared.

enum state_t

Declared in "libtorrent/torrent_status.hpp"

name value description
checking_files 1 The torrent has not started its download yet, and is currently checking existing files.
downloading_metadata 2 The torrent is trying to download metadata from peers. This assumes the metadata_transfer extension is in use.
downloading 3 The torrent is being downloaded. This is the state most torrents will be in most of the time. The progress meter will tell how much of the files that has been downloaded.
finished 4 In this state the torrent has finished downloading but still doesn't have the entire torrent. i.e. some pieces are filtered and won't get downloaded.
seeding 5 In this state the torrent has finished downloading and is a pure seeder.
allocating 6 If the torrent was started in full allocation mode, this indicates that the (disk) storage for the torrent is allocated.
checking_resume_data 7 The torrent is currently checking the fastresume data and comparing it to the files on disk. This is typically completed in a fraction of a second, but if you add a large number of torrents at once, they will queue up.
handle
a handle to the torrent whose status the object represents.
save_path
the path to the directory where this torrent's files are stored. It's typically the path as was given to async_add_torrent() or add_torrent() when this torrent was started. This field is only included if the torrent status is queried with torrent_handle::query_save_path.
name
the name of the torrent. Typically this is derived from the .torrent file. In case the torrent was started without metadata, and hasn't completely received it yet, it returns the name given to it when added to the session. See session::add_torrent. This field is only included if the torrent status is queried with torrent_handle::query_name.
torrent_file
set to point to the torrent_info object for this torrent. It's only included if the torrent status is queried with torrent_handle::query_torrent_file.
next_announce
the time until the torrent will announce itself to the tracker.
current_tracker
the URL of the last working tracker. If no tracker request has been successful yet, it's set to an empty string.
total_download total_upload
the number of bytes downloaded and uploaded to all peers, accumulated, this session only. The session is considered to restart when a torrent is paused and restarted again. When a torrent is paused, these counters are reset to 0. If you want complete, persistent, stats, see all_time_upload and all_time_download.
total_payload_download total_payload_upload
counts the amount of bytes send and received this session, but only the actual payload data (i.e the interesting data), these counters ignore any protocol overhead. The session is considered to restart when a torrent is paused and restarted again. When a torrent is paused, these counters are reset to 0.
total_failed_bytes
the number of bytes that has been downloaded and that has failed the piece hash test. In other words, this is just how much crap that has been downloaded since the torrent was last started. If a torrent is paused and then restarted again, this counter will be reset.
total_redundant_bytes
the number of bytes that has been downloaded even though that data already was downloaded. The reason for this is that in some situations the same data can be downloaded by mistake. When libtorrent sends requests to a peer, and the peer doesn't send a response within a certain timeout, libtorrent will re-request that block. Another situation when libtorrent may re-request blocks is when the requests it sends out are not replied in FIFO-order (it will re-request blocks that are skipped by an out of order block). This is supposed to be as low as possible. This only counts bytes since the torrent was last started. If a torrent is paused and then restarted again, this counter will be reset.
pieces
a bitmask that represents which pieces we have (set to true) and the pieces we don't have. It's a pointer and may be set to 0 if the torrent isn't downloading or seeding.
verified_pieces
a bitmask representing which pieces has had their hash checked. This only applies to torrents in seed mode. If the torrent is not in seed mode, this bitmask may be empty.
total_done
the total number of bytes of the file(s) that we have. All this does not necessarily has to be downloaded during this session (that's total_payload_download).
total_wanted_done
the number of bytes we have downloaded, only counting the pieces that we actually want to download. i.e. excluding any pieces that we have but have priority 0 (i.e. not wanted).
total_wanted
The total number of bytes we want to download. This may be smaller than the total torrent size in case any pieces are prioritized to 0, i.e. not wanted
all_time_upload all_time_download
are accumulated upload and download payload byte counters. They are saved in and restored from resume data to keep totals across sessions.
added_time
the posix-time when this torrent was added. i.e. what time(NULL) returned at the time.
completed_time
the posix-time when this torrent was finished. If the torrent is not yet finished, this is 0.
last_seen_complete
the time when we, or one of our peers, last saw a complete copy of this torrent.
storage_mode
The allocation mode for the torrent. See storage_mode_t for the options. For more information, see storage allocation.
progress
a value in the range [0, 1], that represents the progress of the torrent's current task. It may be checking files or downloading.
progress_ppm

progress parts per million (progress * 1000000) when disabling floating point operations, this is the only option to query progress

reflects the same value as progress, but instead in a range [0, 1000000] (ppm = parts per million). When floating point operations are disabled, this is the only alternative to the floating point value in progress.

queue_position
the position this torrent has in the download queue. If the torrent is a seed or finished, this is -1.
download_rate upload_rate
the total rates for all peers for this torrent. These will usually have better precision than summing the rates from all peers. The rates are given as the number of bytes per second.
download_payload_rate upload_payload_rate
the total transfer rate of payload only, not counting protocol chatter. This might be slightly smaller than the other rates, but if projected over a long time (e.g. when calculating ETA:s) the difference may be noticeable.
num_seeds
the number of peers that are seeding that this client is currently connected to.
num_peers
the number of peers this torrent currently is connected to. Peer connections that are in the half-open state (is attempting to connect) or are queued for later connection attempt do not count. Although they are visible in the peer list when you call get_peer_info().
num_complete num_incomplete
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. set to -1 if the tracker did not send any scrape data in its announce reply.
list_seeds list_peers
the number of seeds in our peer list and the total number of peers (including seeds). We are not necessarily connected to all the peers in our peer list. This is the number of peers we know of in total, including banned peers and peers that we have failed to connect to.
connect_candidates
the number of peers in this torrent's peer list that is a candidate to be connected to. i.e. It has fewer connect attempts than the max fail count, it is not a seed if we are a seed, it is not banned etc. If this is 0, it means we don't know of any more peers that we can try.
num_pieces
the number of pieces that has been downloaded. It is equivalent to: std::accumulate(pieces->begin(), pieces->end()). So you don't have to count yourself. This can be used to see if anything has updated since last time if you want to keep a graph of the pieces up to date.
distributed_full_copies
the number of distributed copies of the torrent. Note that one copy may be spread out among many peers. It tells how many copies there are currently of the rarest piece(s) among the peers this client is connected to.
distributed_fraction

tells the share of pieces that have more copies than the rarest piece(s). Divide this number by 1000 to get the fraction.

For example, if distributed_full_copies is 2 and distributed_fraction is 500, it means that the rarest pieces have only 2 copies among the peers this torrent is connected to, and that 50% of all the pieces have more than two copies.

If we are a seed, the piece picker is deallocated as an optimization, and piece availability is no longer tracked. In this case the distributed copies members are set to -1.

distributed_copies

the number of distributed copies of the file. note that one copy may be spread out among many peers. This is a floating point representation of the distributed copies.

the integer 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).
block_size
the size of a block, in bytes. A block is a sub piece, it is the number of bytes that each piece request asks for and the number of bytes that each bit in the partial_piece_info's bitset represents, see get_download_queue(). This is typically 16 kB, but it may be larger if the pieces are larger.
num_uploads
the number of unchoked peers in this torrent.
num_connections
the number of peer connections this torrent has, including half-open connections that hasn't completed the bittorrent handshake yet. This is always >= num_peers.
uploads_limit
the set limit of upload slots (unchoked peers) for this torrent.
connections_limit
the set limit of number of connections for this torrent.
up_bandwidth_queue down_bandwidth_queue
the number of peers in this torrent that are waiting for more bandwidth quota from the torrent rate limiter. This can determine if the rate you get from this torrent is bound by the torrents limit or not. If there is no limit set on this torrent, the peers might still be waiting for bandwidth quota from the global limiter, but then they are counted in the session_status object.
time_since_upload time_since_download
the number of seconds since any peer last uploaded from this torrent and the last time a downloaded piece passed the hash check, respectively. Note, when starting up a torrent that needs its files checked, piece may pass and that will be considered downloading for the purpose of this counter. -1 means there either hasn't been any uploading/downloading, or it was too long ago for libtorrent to remember (currently forgetting happens after about 18 hours)
active_time finished_time seeding_time
These keep track of the number of seconds this torrent has been active (not paused) and the number of seconds it has been active while being finished and active while being a seed. seeding_time should be <= finished_time which should be <= active_time. They are all saved in and restored from resume data, to keep totals across sessions.
seed_rank
A rank of how important it is to seed the torrent, it is used to determine which torrents to seed and which to queue. It is based on the peer to seed ratio from the tracker scrape. For more information, see queuing. Higher value means more important to seed
last_scrape
the number of seconds since this torrent acquired scrape data. If it has never done that, this value is -1.
priority
the priority of this torrent
state
the main state the torrent is in. See torrent_status::state_t.
need_save_resume
true if this torrent has unsaved changes to its download state and statistics since the last resume data was saved.
ip_filter_applies
true if the session global IP filter applies to this torrent. This defaults to true.
upload_mode
true if the torrent is blocked from downloading. This typically happens when a disk write operation fails. If the torrent is auto-managed, it will periodically be taken out of this state, in the hope that the disk condition (be it disk full or permission errors) has been resolved. If the torrent is not auto-managed, you have to explicitly take it out of the upload mode by calling set_upload_mode() on the torrent_handle.
share_mode
true if the torrent is currently in share-mode, i.e. not downloading the torrent, but just helping the swarm out.
super_seeding
true if the torrent is in super seeding mode
paused
set to true if the torrent is paused and false otherwise. It's only true if the torrent itself is paused. If the torrent is not running because the session is paused, this is still false. To know if a torrent is active or not, you need to inspect both torrent_status::paused and session::is_paused().
auto_managed
set to true if the torrent is auto managed, i.e. libtorrent is responsible for determining whether it should be started or queued. For more info see queuing
sequential_download
true when the torrent is in sequential download mode. In this mode pieces are downloaded in order rather than rarest first.
is_seeding
true if all pieces have been downloaded.
is_finished
true if all pieces that have a priority > 0 are downloaded. There is only a distinction between finished and seeding if some pieces or files have been set to priority 0, i.e. are not downloaded.
has_metadata
true if this torrent has metadata (either it was started from a .torrent file or the metadata has been downloaded). The only scenario where this can be false is when the torrent was started torrent-less (i.e. with just an info-hash and tracker ip, a magnet link for instance).
has_incoming
true if there has ever been an incoming connection attempt to this torrent.
seed_mode
true if the torrent is in seed_mode. If the torrent was started in seed mode, it will leave seed mode once all pieces have been checked or as soon as one piece fails the hash check.
moving_storage
this is true if this torrent's storage is currently being moved from one location to another. This may potentially be a long operation if a large file ends up being copied from one drive to another.
is_loaded
true if this torrent is loaded into RAM. A torrent can be started and still not loaded into RAM, in case it has not had any peers interested in it yet. Torrents are loaded on demand.
announcing_to_trackers announcing_to_lsd announcing_to_dht
these are set to true if this torrent is allowed to announce to the respective peer source. Whether they are true or false is determined by the queue logic/auto manager. Torrents that are not auto managed will always be allowed to announce to all peer sources.
stop_when_ready
this reflects whether the stop_when_ready flag is currently enabled on this torrent. For more information, see torrent_handle::stop_when_ready().
info_hash
the info-hash for this torrent

announce_entry

Declared in "libtorrent/announce_entry.hpp"

this class holds information about one bittorrent tracker, as it relates to a specific torrent.

struct announce_entry
{
   announce_entry (std::string const& u);
   announce_entry (announce_entry const&) = default;
   ~announce_entry ();
   announce_entry& operator= (announce_entry const&) = default;
   announce_entry ();
   int next_announce_in () const;
   int min_announce_in () const;
   void reset ();
   void failed (aux::session_settings const& sett, int retry_interval = 0);
   bool can_announce (time_point now, bool is_seed) const;
   bool is_working () const;
   void trim ();

   enum tracker_source
   {
      source_torrent,
      source_client,
      source_magnet_link,
      source_tex,
   };

   std::string url;
   std::string trackerid;
   std::string message;
   error_code last_error;
   time_point next_announce;
   time_point min_announce;
   int scrape_incomplete;
   int scrape_complete;
   int scrape_downloaded;
   boost::uint8_t tier;
   boost::uint8_t fail_limit;
   boost::uint8_t fails:7;
   bool updating:1;
   boost::uint8_t source:4;
   bool verified:1;
   bool start_sent:1;
   bool complete_sent:1;
   bool send_stats:1;
};

announce_entry() ~announce_entry() operator=()

announce_entry (std::string const& u);
announce_entry (announce_entry const&) = default;
~announce_entry ();
announce_entry& operator= (announce_entry const&) = default;
announce_entry ();

constructs a tracker announce entry with u as the URL.

min_announce_in() next_announce_in()

int next_announce_in () const;
int min_announce_in () const;

returns the number of seconds to the next announce on this tracker. min_announce_in() returns the number of seconds until we are allowed to force another tracker update with this tracker.

If the last time this tracker was contacted failed, last_error is the error code describing what error occurred.

reset()

void reset ();

reset announce counters and clears the started sent flag. The announce_entry will look like we've never talked to the tracker.

failed()

void failed (aux::session_settings const& sett, int retry_interval = 0);

updates the failure counter and time-outs for re-trying. This is called when the tracker announce fails.

can_announce()

bool can_announce (time_point now, bool is_seed) const;

returns true if we can announce to this tracker now. The current time is passed in as now. The is_seed argument is necessary because once we become a seed, we need to announce right away, even if the re-announce timer hasn't expired yet.

is_working()

bool is_working () const;

returns true if the last time we tried to announce to this tracker succeeded, or if we haven't tried yet.

trim()

void trim ();

trims whitespace characters from the beginning of the URL.

enum tracker_source

Declared in "libtorrent/announce_entry.hpp"

name value description
source_torrent 1 the tracker was part of the .torrent file
source_client 2 the tracker was added programatically via the add_troacker()_ function
source_magnet_link 4 the tracker was part of a magnet link
source_tex 8 the tracker was received from the swarm via tracker exchange
url
tracker URL as it appeared in the torrent file
trackerid
the current &trackerid= argument passed to the tracker. this is optional and is normally empty (in which case no trackerid is sent).
message
if this tracker has returned an error or warning message that message is stored here
last_error
if this tracker failed the last time it was contacted this error code specifies what error occurred
next_announce
the time of next tracker announce
min_announce
no announces before this time
scrape_incomplete scrape_complete scrape_downloaded
if this tracker has returned scrape data, these fields are filled in with valid numbers. Otherwise they are set to -1. the number of current downloaders
tier
the tier this tracker belongs to
fail_limit
the max number of failures to announce to this tracker in a row, before this tracker is not used anymore. 0 means unlimited
fails
the number of times in a row we have failed to announce to this tracker.
updating
true while we're waiting for a response from the tracker.
source
a bitmask specifying which sources we got this tracker from.
verified
set to true the first time we receive a valid response from this tracker.
start_sent
set to true when we get a valid response from an announce with event=started. If it is set, we won't send start in the subsequent announces.
complete_sent
set to true when we send a event=completed.
send_stats
this is false the stats sent to this tracker will be 0

peer_class_type_filter

Declared in "libtorrent/peer_class_type_filter.hpp"

peer_class_type_filter is a simple container for rules for adding and subtracting peer-classes from peers. It is applied after the peer class filter is applied (which is based on the peer's IP address).

struct peer_class_type_filter
{
   peer_class_type_filter ();
   void remove (socket_type_t st, peer_class_t peer_class);
   void add (socket_type_t st, peer_class_t peer_class);
   void disallow (socket_type_t st, peer_class_t peer_class);
   void allow (socket_type_t st, peer_class_t peer_class);
   boost::uint32_t apply (int st, boost::uint32_t peer_class_mask);

   enum socket_type_t
   {
      tcp_socket,
      utp_socket,
      ssl_tcp_socket,
      ssl_utp_socket,
      i2p_socket,
      num_socket_types,
   };
};

remove() add()

void remove (socket_type_t st, peer_class_t peer_class);
void add (socket_type_t st, peer_class_t peer_class);

add() and remove() adds and removes a peer class to be added to new peers based on socket type.

disallow() allow()

void disallow (socket_type_t st, peer_class_t peer_class);
void allow (socket_type_t st, peer_class_t peer_class);

disallow() and allow() adds and removes a peer class to be removed from new peers based on socket type.

The peer_class argument cannot be greater than 31. The bitmasks representing peer classes in the peer_class_type_filter are 32 bits.

apply()

boost::uint32_t apply (int st, boost::uint32_t peer_class_mask);

takes a bitmask of peer classes and returns a new bitmask of peer classes after the rules have been applied, based on the socket type argument (st).

enum socket_type_t

Declared in "libtorrent/peer_class_type_filter.hpp"

name value description
tcp_socket 0 these match the socket types from socket_type.hpp shifted one down
utp_socket 1  
ssl_tcp_socket 2  
ssl_utp_socket 3  
i2p_socket 4  
num_socket_types 5  

block_info

Declared in "libtorrent/torrent_handle.hpp"

holds the state of a block in a piece. Who we requested it from and how far along we are at downloading it.

struct block_info
{
   void set_peer (tcp::endpoint const& ep);
   tcp::endpoint peer () const;

   enum block_state_t
   {
      none,
      requested,
      writing,
      finished,
   };

   unsigned bytes_progress:15;
   unsigned block_size:15;
   unsigned state:2;
   unsigned num_peers:14;
};

set_peer() peer()

void set_peer (tcp::endpoint const& ep);
tcp::endpoint peer () const;

The peer is the ip address of the peer this block was downloaded from.

enum block_state_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
none 0 This block has not been downloaded or requested form any peer.
requested 1 The block has been requested, but not completely downloaded yet.
writing 2 The block has been downloaded and is currently queued for being written to disk.
finished 3 The block has been written to disk.
bytes_progress
the number of bytes that have been received for this block
block_size
the total number of bytes in this block.
state
the state this block is in (see block_state_t)
num_peers
the number of peers that is currently requesting this block. Typically this is 0 or 1, but at the end of the torrent blocks may be requested by more peers in parallel to speed things up.

partial_piece_info

Declared in "libtorrent/torrent_handle.hpp"

This class holds information about pieces that have outstanding requests or outstanding writes

struct partial_piece_info
{
   int piece_index;
   int blocks_in_piece;
   int finished;
   int writing;
   int requested;
   block_info* blocks;
};
piece_index
the index of the piece in question. blocks_in_piece is the number of blocks in this particular piece. This number will be the same for most pieces, but the last piece may have fewer blocks than the standard pieces.
blocks_in_piece
the number of blocks in this piece
finished
the number of blocks that are in the finished state
writing
the number of blocks that are in the writing state
requested
the number of blocks that are in the requested state
blocks

this is an array of blocks_in_piece number of items. One for each block in the piece.

Warning

This is a pointer that points to an array that's owned by the session object. The next time get_download_queue() is called, it will be invalidated.

torrent_handle

Declared in "libtorrent/torrent_handle.hpp"

You will usually have to store your torrent handles somewhere, since it's the object through which you retrieve information about the torrent and aborts the torrent.

Warning

Any member function that returns a value or fills in a value has to be made synchronously. This means it has to wait for the main thread to complete the query before it can return. This might potentially be expensive if done from within a GUI thread that needs to stay responsive. Try to avoid querying for information you don't need, and try to do it in as few calls as possible. You can get most of the interesting information about a torrent from the torrent_handle::status() call.

The default constructor will initialize the handle to an invalid state. Which means you cannot perform any operation on it, unless you first assign it a valid handle. If you try to perform any operation on an uninitialized handle, it will throw invalid_handle.

Warning

All operations on a torrent_handle may throw libtorrent_exception exception, in case the handle is no longer referring to a torrent. There is one exception is_valid() will never throw. Since the torrents are processed by a background thread, there is no guarantee that a handle will remain valid between two calls.

struct torrent_handle
{
   torrent_handle ();
   torrent_handle (torrent_handle const& t);
   torrent_handle& operator= (torrent_handle const&) = default;
   void add_piece (int piece, char const* data, int flags = 0) const;
   void read_piece (int piece) const;
   bool have_piece (int piece) const;
   void get_peer_info (std::vector<peer_info>& v) const;
   torrent_status status (boost::uint32_t flags = 0xffffffff) const;
   void get_download_queue (std::vector<partial_piece_info>& queue) const;
   void reset_piece_deadline (int index) const;
   void clear_piece_deadlines () const;
   void set_piece_deadline (int index, int deadline, int flags = 0) const;
   void set_priority (int prio) const;
   void file_progress (std::vector<boost::int64_t>& progress, int flags = 0) const;
   void file_status (std::vector<pool_file_status>& status) const;
   void clear_error () const;
   std::vector<announce_entry> trackers () const;
   void replace_trackers (std::vector<announce_entry> const&) const;
   void add_tracker (announce_entry const&) const;
   void add_url_seed (std::string const& url) const;
   void remove_url_seed (std::string const& url) const;
   std::set<std::string> url_seeds () const;
   void add_http_seed (std::string const& url) const;
   void remove_http_seed (std::string const& url) const;
   std::set<std::string> http_seeds () const;
   void add_extension (
      boost::function<boost::shared_ptr<torrent_plugin>(torrent_handle const&, void*)> const& ext
      , void* userdata = 0);
   bool set_metadata (char const* metadata, int size) const;
   bool is_valid () const;
   void pause (int flags = 0) const;
   void resume () const;
   void stop_when_ready (bool b) const;
   void set_upload_mode (bool b) const;
   void set_share_mode (bool b) const;
   void flush_cache () const;
   void apply_ip_filter (bool b) const;
   void force_recheck () const;
   void save_resume_data (int flags = 0) const;
   bool need_save_resume_data () const;
   void auto_managed (bool m) const;
   void queue_position_down () const;
   void queue_position_top () const;
   int queue_position () const;
   void queue_position_bottom () const;
   void queue_position_up () const;
   void queue_position_set (int p) const;
   void set_ssl_certificate (std::string const& certificate
      , std::string const& private_key
      , std::string const& dh_params
      , std::string const& passphrase = "");
   void set_ssl_certificate_buffer (std::string const& certificate
      , std::string const& private_key
      , std::string const& dh_params);
   storage_interface* get_storage_impl () const;
   shared_ptr<const torrent_info> torrent_file () const;
   void piece_availability (std::vector<int>& avail) const;
   int piece_priority (int index) const;
   std::vector<int> piece_priorities () const;
   void piece_priority (int index, int priority) const;
   void prioritize_pieces (std::vector<int> const& pieces) const;
   void prioritize_pieces (std::vector<std::pair<int, int> > const& pieces) const;
   int file_priority (int index) const;
   void prioritize_files (std::vector<int> const& files) const;
   void file_priority (int index, int priority) const;
   std::vector<int> file_priorities () const;
   void force_reannounce (int seconds = 0, int tracker_index = -1) const;
   void force_reannounce (int seconds, int tracker_index, int flags) const;
   void force_dht_announce () const;
   void scrape_tracker (int idx = -1) const;
   int upload_limit () const;
   int download_limit () const;
   void set_upload_limit (int limit) const;
   void set_download_limit (int limit) const;
   void set_pinned (bool p) const;
   void set_sequential_download (bool sd) const;
   void connect_peer (tcp::endpoint const& adr, int source = 0
      , int flags = 0x1 + 0x4 + 0x8) const;
   int max_uploads () const;
   void set_max_uploads (int max_uploads) const;
   int max_connections () const;
   void set_max_connections (int max_connections) const;
   void move_storage (std::string const& save_path, int flags = 0) const;
   void rename_file (int index, std::string const& new_name) const;
   void super_seeding (bool on) const;
   sha1_hash info_hash () const;
   bool operator!= (const torrent_handle& h) const;
   bool operator< (const torrent_handle& h) const;
   bool operator== (const torrent_handle& h) const;
   boost::uint32_t id () const;
   boost::shared_ptr<torrent> native_handle () const;

   enum flags_t
   {
      overwrite_existing,
   };

   enum status_flags_t
   {
      query_distributed_copies,
      query_accurate_download_counters,
      query_last_seen_complete,
      query_pieces,
      query_verified_pieces,
      query_torrent_file,
      query_name,
      query_save_path,
   };

   enum deadline_flags
   {
      alert_when_available,
   };

   enum file_progress_flags_t
   {
      piece_granularity,
   };

   enum pause_flags_t
   {
      graceful_pause,
   };

   enum save_resume_flags_t
   {
      flush_disk_cache,
      save_info_dict,
      only_if_modified,
   };

   enum reannounce_flags_t
   {
      ignore_min_interval,
   };
};

torrent_handle()

torrent_handle ();

constructs a torrent handle that does not refer to a torrent. i.e. is_valid() will return false.

add_piece()

void add_piece (int piece, char const* data, int flags = 0) const;

This function will write data to the storage as piece piece, as if it had been downloaded from a peer. data is expected to point to a buffer of as many bytes as the size of the specified piece. The data in the buffer is copied and passed on to the disk IO thread to be written at a later point.

By default, data that's already been downloaded is not overwritten by this buffer. If you trust this data to be correct (and pass the piece hash check) you may pass the overwrite_existing flag. This will instruct libtorrent to overwrite any data that may already have been downloaded with this data.

Since the data is written asynchronously, you may know that is passed or failed the hash check by waiting for piece_finished_alert or hash_failed_alert.

read_piece()

void read_piece (int piece) const;

This function starts an asynchronous read operation of the specified piece from this torrent. You must have completed the download of the specified piece before calling this function.

When the read operation is completed, it is passed back through an alert, read_piece_alert. Since this alert is a response to an explicit call, it will always be posted, regardless of the alert mask.

Note that if you read multiple pieces, the read operations are not guaranteed to finish in the same order as you initiated them.

have_piece()

bool have_piece (int piece) const;

Returns true if this piece has been completely downloaded, and false otherwise.

get_peer_info()

void get_peer_info (std::vector<peer_info>& v) const;

takes a reference to a vector that will be cleared and filled with one entry for each peer connected to this torrent, given the handle is valid. If the torrent_handle is invalid, it will throw libtorrent_exception exception. Each entry in the vector contains information about that particular peer. See peer_info.

status()

torrent_status status (boost::uint32_t flags = 0xffffffff) const;

status() will return a structure with information about the status of this torrent. If the torrent_handle is invalid, it will throw libtorrent_exception exception. See torrent_status. The flags argument filters what information is returned in the torrent_status. Some information in there is relatively expensive to calculate, and if you're not interested in it (and see performance issues), you can filter them out.

By default everything is included. The flags you can use to decide what to include are defined in the status_flags_t enum.

get_download_queue()

void get_download_queue (std::vector<partial_piece_info>& queue) const;

get_download_queue() takes a non-const reference to a vector which it will fill with information about pieces that are partially downloaded or not downloaded at all but partially requested. See partial_piece_info for the fields in the returned vector.

clear_piece_deadlines() reset_piece_deadline() set_piece_deadline()

void reset_piece_deadline (int index) const;
void clear_piece_deadlines () const;
void set_piece_deadline (int index, int deadline, int flags = 0) const;

This function sets or resets the deadline associated with a specific piece index (index). libtorrent will attempt to download this entire piece before the deadline expires. This is not necessarily possible, but pieces with a more recent deadline will always be prioritized over pieces with a deadline further ahead in time. The deadline (and flags) of a piece can be changed by calling this function again.

The flags parameter can be used to ask libtorrent to post an alert once the piece has been downloaded, by passing alert_when_available. When set, the read_piece_alert alert will be delivered, with the piece data, when it's downloaded.

If the piece is already downloaded when this call is made, nothing happens, unless the alert_when_available flag is set, in which case it will have the same effect as calling read_piece() for index.

deadline is the number of milliseconds until this piece should be completed.

reset_piece_deadline removes the deadline from the piece. If it hasn't already been downloaded, it will no longer be considered a priority.

clear_piece_deadlines() removes deadlines on all pieces in the torrent. As if reset_piece_deadline() was called on all pieces.

set_priority()

void set_priority (int prio) const;

This sets the bandwidth priority of this torrent. The priority of a torrent determines how much bandwidth its peers are assigned when distributing upload and download rate quotas. A high number gives more bandwidth. The priority must be within the range [0, 255].

The default priority is 0, which is the lowest priority.

To query the priority of a torrent, use the torrent_handle::status() call.

Torrents with higher priority will not necessarily get as much bandwidth as they can consume, even if there's is more quota. Other peers will still be weighed in when bandwidth is being distributed. With other words, bandwidth is not distributed strictly in order of priority, but the priority is used as a weight.

Peers whose Torrent has a higher priority will take precedence when distributing unchoke slots. This is a strict prioritisation where every interested peer on a high priority torrent will be unchoked before any other, lower priority, torrents have any peers unchoked.

file_progress()

void file_progress (std::vector<boost::int64_t>& progress, int flags = 0) const;

This function fills in the supplied vector with the the number of bytes downloaded of each file in this torrent. The progress values are ordered the same as the files in the torrent_info. This operation is not very cheap. Its complexity is O(n + mj). Where n is the number of files, m is the number of downloading pieces and j is the number of blocks in a piece.

The flags parameter can be used to specify the granularity of the file progress. If left at the default value of 0, the progress will be as accurate as possible, but also more expensive to calculate. If torrent_handle::piece_granularity is specified, the progress will be specified in piece granularity. i.e. only pieces that have been fully downloaded and passed the hash check count. When specifying piece granularity, the operation is a lot cheaper, since libtorrent already keeps track of this internally and no calculation is required.

file_status()

void file_status (std::vector<pool_file_status>& status) const;

This function fills in the passed in vector with status about files that are open for this torrent. Any file that is not open in this torrent, will not be reported in the vector, i.e. it's possible that the vector is empty when returning, if none of the files in the torrent are currently open.

see pool_file_status.

clear_error()

void clear_error () const;

If the torrent is in an error state (i.e. torrent_status::error is non-empty), this will clear the error and start the torrent again.

add_tracker() replace_trackers() trackers()

std::vector<announce_entry> trackers () const;
void replace_trackers (std::vector<announce_entry> const&) const;
void add_tracker (announce_entry const&) const;

trackers() will return the list of trackers for this torrent. The announce entry contains both a string url which specify the announce url for the tracker as well as an int tier, which is specifies the order in which this tracker is tried. If you want libtorrent to use another list of trackers for this torrent, you can use replace_trackers() which takes a list of the same form as the one returned from trackers() and will replace it. If you want an immediate effect, you have to call force_reannounce(). See announce_entry.

add_tracker() will look if the specified tracker is already in the set. If it is, it doesn't do anything. If it's not in the current set of trackers, it will insert it in the tier specified in the announce_entry.

The updated set of trackers will be saved in the resume data, and when a torrent is started with resume data, the trackers from the resume data will replace the original ones.

url_seeds() add_url_seed() remove_url_seed()

void add_url_seed (std::string const& url) const;
void remove_url_seed (std::string const& url) const;
std::set<std::string> url_seeds () const;

add_url_seed() adds another url to the torrent's list of url seeds. If the given url already exists in that list, the call has no effect. The torrent will connect to the server and try to download pieces from it, unless it's paused, queued, checking or seeding. remove_url_seed() removes the given url if it exists already. url_seeds() return a set of the url seeds currently in this torrent. Note that URLs that fails may be removed automatically from the list.

See http seeding for more information.

http_seeds() remove_http_seed() add_http_seed()

void add_http_seed (std::string const& url) const;
void remove_http_seed (std::string const& url) const;
std::set<std::string> http_seeds () const;

These functions are identical as the *_url_seed() variants, but they operate on BEP 17 web seeds instead of BEP 19.

See http seeding for more information.

add_extension()

void add_extension (
      boost::function<boost::shared_ptr<torrent_plugin>(torrent_handle const&, void*)> const& ext
      , void* userdata = 0);

add the specified extension to this torrent. The ext argument is a function that will be called from within libtorrent's context passing in the internal torrent object and the specified userdata pointer. The function is expected to return a shared pointer to a torrent_plugin instance.

set_metadata()

bool set_metadata (char const* metadata, int size) const;

set_metadata expects the info section of metadata. i.e. The buffer passed in will be hashed and verified against the info-hash. If it fails, a metadata_failed_alert will be generated. If it passes, a metadata_received_alert is generated. The function returns true if the metadata is successfully set on the torrent, and false otherwise. If the torrent already has metadata, this function will not affect the torrent, and false will be returned.

is_valid()

bool is_valid () const;

Returns true if this handle refers to a valid torrent and false if it hasn't been initialized or if the torrent it refers to has been aborted. Note that a handle may become invalid after it has been added to the session. Usually this is because the storage for the torrent is somehow invalid or if the filenames are not allowed (and hence cannot be opened/created) on your filesystem. If such an error occurs, a file_error_alert is generated and all handles that refers to that torrent will become invalid.

pause() resume()

void pause (int flags = 0) const;
void resume () const;

pause(), and resume() will disconnect all peers and reconnect all peers respectively. When a torrent is paused, it will however remember all share ratios to all peers and remember all potential (not connected) peers. Torrents may be paused automatically if there is a file error (e.g. disk full) or something similar. See file_error_alert.

To know if a torrent is paused or not, call torrent_handle::status() and inspect torrent_status::paused.

The flags argument to pause can be set to torrent_handle::graceful_pause which will delay the disconnect of peers that we're still downloading outstanding requests from. The torrent will not accept any more requests and will disconnect all idle peers. As soon as a peer is done transferring the blocks that were requested from it, it is disconnected. This is a graceful shut down of the torrent in the sense that no downloaded bytes are wasted.

Note

Torrents that are auto-managed may be automatically resumed again. It does not make sense to pause an auto-managed torrent without making it not auto-managed first. Torrents are auto-managed by default when added to the session. For more information, see queuing.

stop_when_ready()

void stop_when_ready (bool b) const;

set or clear the stop-when-ready flag. When this flag is set, the torrent will force stop whenever it transitions from a non-data-transferring state into a data-transferring state (referred to as being ready to download or seed). This is useful for torrents that should not start downloading or seeding yet, but want to be made ready to do so. A torrent may need to have its files checked for instance, so it needs to be started and possibly queued for checking (auto-managed and started) but as soon as it's done, it should be stopped.

Force stopped means auto-managed is set to false and it's paused. As if auto_manage(false) and pause() were called on the torrent.

Note that the torrent may transition into a downloading state while calling this function, and since the logic is edge triggered you may miss the edge. To avoid this race, if the torrent already is in a downloading state when this call is made, it will trigger the stop-when-ready immediately.

When the stop-when-ready logic fires, the flag is cleared. Any subsequent transitions between downloading and non-downloading states will not be affected, until this function is used to set it again.

The behavior is more robust when setting this flag as part of adding the torrent. See add_torrent_params.

The stop-when-ready flag fixes the inherent race condition of waiting for the state_changed_alert and then call pause(). The download/seeding will most likely start in between posting the alert and receiving the call to pause.

A downloading state is one where peers are being connected. Which means just downloading the metadata via the ut_metadata extension counts as a downloading state. In order to stop a torrent once the metadata has been downloaded, instead set all file priorities to dont_download

set_upload_mode()

void set_upload_mode (bool b) const;

Explicitly sets the upload mode of the torrent. In upload mode, the torrent will not request any pieces. If the torrent is auto managed, it will automatically be taken out of upload mode periodically (see settings_pack::optimistic_disk_retry). Torrents are automatically put in upload mode whenever they encounter a disk write error.

m should be true to enter upload mode, and false to leave it.

To test if a torrent is in upload mode, call torrent_handle::status() and inspect torrent_status::upload_mode.

set_share_mode()

void set_share_mode (bool b) const;

Enable or disable share mode for this torrent. When in share mode, the torrent will not necessarily be downloaded, especially not the whole of it. Only parts that are likely to be distributed to more than 2 other peers are downloaded, and only if the previous prediction was correct.

flush_cache()

void flush_cache () const;

Instructs libtorrent to flush all the disk caches for this torrent and close all file handles. This is done asynchronously and you will be notified that it's complete through cache_flushed_alert.

Note that by the time you get the alert, libtorrent may have cached more data for the torrent, but you are guaranteed that whatever cached data libtorrent had by the time you called torrent_handle::flush_cache() has been written to disk.

apply_ip_filter()

void apply_ip_filter (bool b) const;

Set to true to apply the session global IP filter to this torrent (which is the default). Set to false to make this torrent ignore the IP filter.

force_recheck()

void force_recheck () const;

force_recheck puts the torrent back in a state where it assumes to have no resume data. All peers will be disconnected and the torrent will stop announcing to the tracker. The torrent will be added to the checking queue, and will be checked (all the files will be read and compared to the piece hashes). Once the check is complete, the torrent will start connecting to peers again, as normal.

save_resume_data()

void save_resume_data (int flags = 0) const;

save_resume_data() asks libtorrent to generate fast-resume data for this torrent.

The flags argument is a bitmask of flags ORed together. see save_resume_flags_t

This operation is asynchronous, save_resume_data will return immediately. The resume data is delivered when it's done through an save_resume_data_alert.

The fast resume data will be empty in the following cases:

  1. The torrent handle is invalid.
  2. The torrent hasn't received valid metadata and was started without metadata (see libtorrent's metadata from peers extension)

Note that by the time you receive the fast resume data, it may already be invalid if the torrent is still downloading! The recommended practice is to first pause the session, then generate the fast resume data, and then close it down. Make sure to not remove_torrent() before you receive the save_resume_data_alert though. There's no need to pause when saving intermittent resume data.

Warning

If you pause every torrent individually instead of pausing the session, every torrent will have its paused state saved in the resume data!

Warning

The resume data contains the modification timestamps for all files. If one file has been modified when the torrent is added again, the will be rechecked. When shutting down, make sure to flush the disk cache before saving the resume data. This will make sure that the file timestamps are up to date and won't be modified after saving the resume data. The recommended way to do this is to pause the torrent, which will flush the cache and disconnect all peers.

Note

It is typically a good idea to save resume data whenever a torrent is completed or paused. In those cases you don't need to pause the torrent or the session, since the torrent will do no more writing to its files. If you save resume data for torrents when they are paused, you can accelerate the shutdown process by not saving resume data again for paused torrents. Completed torrents should have their resume data saved when they complete and on exit, since their statistics might be updated.

In full allocation mode the resume data is never invalidated by subsequent writes to the files, since pieces won't move around. This means that you don't need to pause before writing resume data in full or sparse mode. If you don't, however, any data written to disk after you saved resume data and before the session closed is lost.

It also means that if the resume data is out dated, libtorrent will not re-check the files, but assume that it is fairly recent. The assumption is that it's better to loose a little bit than to re-check the entire file.

It is still a good idea to save resume data periodically during download as well as when closing down.

Example code to pause and save resume data for all torrents and wait for the alerts:

extern int outstanding_resume_data; // global counter of outstanding resume data
std::vector<torrent_handle> handles = ses.get_torrents();
ses.pause();
for (torrent_handle const& h : handles)
{
        if (!h.is_valid()) continue;
        torrent_status s = h.status();
        if (!s.has_metadata) continue;
        if (!s.need_save_resume_data()) continue;

        h.save_resume_data();
        ++outstanding_resume_data;
}

while (outstanding_resume_data > 0)
{
        alert const* a = ses.wait_for_alert(seconds(10));

        // if we don't get an alert within 10 seconds, abort
        if (a == nullptr) break;

        std::vector<alert*> alerts;
        ses.pop_alerts(&alerts);

        for (alert* i : alerts)
        {
                if (alert_cast<save_resume_data_failed_alert>(a))
                {
                        process_alert(a);
                        --outstanding_resume_data;
                        continue;
                }

                save_resume_data_alert const* rd = alert_cast<save_resume_data_alert>(a);
                if (rd == nullptr)
                {
                        process_alert(a);
                        continue;
                }

                torrent_handle h = rd->handle;
                torrent_status st = h.status(torrent_handle::query_save_path
                        | torrent_handle::query_name);
                std::ofstream out((st.save_path
                        + "/" + st.name + ".fastresume").c_str()
                        , std::ios_base::binary);
                out.unsetf(std::ios_base::skipws);
                bencode(std::ostream_iterator<char>(out), *rd->resume_data);
                --outstanding_resume_data;
        }
}

Note

Note how outstanding_resume_data is a global counter in this example. This is deliberate, otherwise there is a race condition for torrents that was just asked to save their resume data, they posted the alert, but it has not been received yet. Those torrents would report that they don't need to save resume data again, and skipped by the initial loop, and thwart the counter otherwise.

need_save_resume_data()

bool need_save_resume_data () const;

This function returns true if any whole chunk has been downloaded since the torrent was first loaded or since the last time the resume data was saved. When saving resume data periodically, it makes sense to skip any torrent which hasn't downloaded anything since the last time.

Note

A torrent's resume data is considered saved as soon as the save_resume_data_alert is posted. It is important to make sure this alert is received and handled in order for this function to be meaningful.

auto_managed()

void auto_managed (bool m) const;

changes whether the torrent is auto managed or not. For more info, see queuing.

queue_position() queue_position_up() queue_position_bottom() queue_position_down() queue_position_top()

void queue_position_down () const;
void queue_position_top () const;
int queue_position () const;
void queue_position_bottom () const;
void queue_position_up () const;

Every torrent that is added is assigned a queue position exactly one greater than the greatest queue position of all existing torrents. Torrents that are being seeded have -1 as their queue position, since they're no longer in line to be downloaded.

When a torrent is removed or turns into a seed, all torrents with greater queue positions have their positions decreased to fill in the space in the sequence.

queue_position() returns the torrent's position in the download queue. The torrents with the smallest numbers are the ones that are being downloaded. The smaller number, the closer the torrent is to the front of the line to be started.

The queue position is also available in the torrent_status.

The queue_position_*() functions adjust the torrents position in the queue. Up means closer to the front and down means closer to the back of the queue. Top and bottom refers to the front and the back of the queue respectively.

queue_position_set()

void queue_position_set (int p) const;

updates the position in the queue for this torrent. The relative order of all other torrents remain intact but their numerical queue position shifts to make space for this torrent's new position

set_ssl_certificate_buffer() set_ssl_certificate()

void set_ssl_certificate (std::string const& certificate
      , std::string const& private_key
      , std::string const& dh_params
      , std::string const& passphrase = "");
void set_ssl_certificate_buffer (std::string const& certificate
      , std::string const& private_key
      , std::string const& dh_params);

For SSL torrents, use this to specify a path to a .pem file to use as this client's certificate. The certificate must be signed by the certificate in the .torrent file to be valid.

The set_ssl_certificate_buffer() overload takes the actual certificate, private key and DH params as strings, rather than paths to files. This overload is only available when libtorrent is built against boost 1.54 or later.

cert is a path to the (signed) certificate in .pem format corresponding to this torrent.

private_key is a path to the private key for the specified certificate. This must be in .pem format.

dh_params is a path to the Diffie-Hellman parameter file, which needs to be in .pem format. You can generate this file using the openssl command like this: openssl dhparam -outform PEM -out dhparams.pem 512.

passphrase may be specified if the private key is encrypted and requires a passphrase to be decrypted.

Note that when a torrent first starts up, and it needs a certificate, it will suspend connecting to any peers until it has one. It's typically desirable to resume the torrent after setting the SSL certificate.

If you receive a torrent_need_cert_alert, you need to call this to provide a valid cert. If you don't have a cert you won't be allowed to connect to any peers.

get_storage_impl()

storage_interface* get_storage_impl () const;

Returns the storage implementation for this torrent. This depends on the storage constructor function that was passed to add_torrent.

torrent_file()

shared_ptr<const torrent_info> torrent_file () const;

Returns a pointer to the torrent_info object associated with this torrent. The torrent_info object may be a copy of the internal object. If the torrent doesn't have metadata, the pointer will not be initialized (i.e. a NULL pointer). The torrent may be in a state without metadata only if it was started without a .torrent file, e.g. by using the libtorrent extension of just supplying a tracker and info-hash.

piece_availability()

void piece_availability (std::vector<int>& avail) const;

Fills the specified std::vector<int> with the availability for each piece in this torrent. libtorrent does not keep track of availability for seeds, so if the torrent is seeding the availability for all pieces is reported as 0.

The piece availability is the number of peers that we are connected that has advertised having a particular piece. This is the information that libtorrent uses in order to prefer picking rare pieces.

piece_priority() prioritize_pieces() piece_priorities()

int piece_priority (int index) const;
std::vector<int> piece_priorities () const;
void piece_priority (int index, int priority) const;
void prioritize_pieces (std::vector<int> const& pieces) const;
void prioritize_pieces (std::vector<std::pair<int, int> > const& pieces) const;

These functions are used to set and get the priority of individual pieces. By default all pieces have priority 4. That means that the random rarest first algorithm is effectively active for all pieces. You may however change the priority of individual pieces. There are 8 priority levels. 0 means not to download the piece at all. Otherwise, lower priority values means less likely to be picked. Piece priority takes precedence over piece availability. Every piece with priority 7 will be attempted to be picked before a priority 6 piece and so on.

The default priority of pieces is 4.

Piece priorities can not be changed for torrents that have not downloaded the metadata yet. Magnet links won't have metadata immediately. see the metadata_received_alert.

piece_priority sets or gets the priority for an individual piece, specified by index.

prioritize_pieces takes a vector of integers, one integer per piece in the torrent. All the piece priorities will be updated with the priorities in the vector. The second overload of prioritize_pieces that takes a vector of pairs will update the priorities of only select pieces, and leave all other unaffected. Each pair is (piece, priority). That is, the first item is the piece index and the second item is the priority of that piece. Invalid entries, where the piece index or priority is out of range, are not allowed.

piece_priorities returns a vector with one element for each piece in the torrent. Each element is the current priority of that piece.

It's possible to cancel the effect of file priorities by setting the priorities for the affected pieces. Care has to be taken when mixing usage of file- and piece priorities.

file_priorities() prioritize_files() file_priority()

int file_priority (int index) const;
void prioritize_files (std::vector<int> const& files) const;
void file_priority (int index, int priority) const;
std::vector<int> file_priorities () const;

index must be in the range [0, number_of_files).

file_priority() queries or sets the priority of file index.

prioritize_files() takes a vector that has at as many elements as there are files in the torrent. Each entry is the priority of that file. The function sets the priorities of all the pieces in the torrent based on the vector.

file_priorities() returns a vector with the priorities of all files.

The priority values are the same as for piece_priority().

Whenever a file priority is changed, all other piece priorities are reset to match the file priorities. In order to maintain special priorities for particular pieces, piece_priority() has to be called again for those pieces.

You cannot set the file priorities on a torrent that does not yet have metadata or a torrent that is a seed. file_priority(int, int) and prioritize_files() are both no-ops for such torrents.

Since changing file priorities may involve disk operations (of moving files in- and out of the part file), the internal accounting of file priorities happen asynchronously. i.e. setting file priorities and then immediately querying them may not yield the same priorities just set. However, the piece priorities are updated immediately.

when combining file- and piece priorities, the resume file will record both. When loading the resume data, the file priorities will be applied first, then the piece priorities.

force_reannounce() force_dht_announce()

void force_reannounce (int seconds = 0, int tracker_index = -1) const;
void force_reannounce (int seconds, int tracker_index, int flags) const;
void force_dht_announce () const;

force_reannounce() will force this torrent to do another tracker request, to receive new peers. The seconds argument specifies how many seconds from now to issue the tracker announces.

If the tracker's min_interval has not passed since the last announce, the forced announce will be scheduled to happen immediately as the min_interval expires. This is to honor trackers minimum re-announce interval settings.

The tracker_index argument specifies which tracker to re-announce. If set to -1 (which is the default), all trackers are re-announce.

The flags argument can be used to affect the re-announce. See reannounce_flags_t.

force_dht_announce will announce the torrent to the DHT immediately.

scrape_tracker()

void scrape_tracker (int idx = -1) const;

scrape_tracker() will send a scrape request to a tracker. By default (idx = -1) it will scrape the last working tracker. If idx is >= 0, the tracker with the specified index will scraped.

A scrape request queries the tracker for statistics such as total number of incomplete peers, complete peers, number of downloads etc.

This request will specifically update the num_complete and num_incomplete fields in the torrent_status struct once it completes. When it completes, it will generate a scrape_reply_alert. If it fails, it will generate a scrape_failed_alert.

set_upload_limit() upload_limit() download_limit() set_download_limit()

int upload_limit () const;
int download_limit () const;
void set_upload_limit (int limit) const;
void set_download_limit (int limit) const;

set_upload_limit will limit the upload bandwidth used by this particular torrent to the limit you set. It is given as the number of bytes per second the torrent is allowed to upload. set_download_limit works the same way but for download bandwidth instead of upload bandwidth. Note that setting a higher limit on a torrent then the global limit (settings_pack::upload_rate_limit) will not override the global rate limit. The torrent can never upload more than the global rate limit.

upload_limit and download_limit will return the current limit setting, for upload and download, respectively.

Local peers are not rate limited by default. see peer classes.

set_pinned()

void set_pinned (bool p) const;

A pinned torrent may not be unloaded by libtorrent. When the dynamic loading and unloading of torrents is enabled (by setting a load function on the session), this can be used to exempt certain torrents from the unloading logic.

Magnet links, and other torrents that start out without having metadata are pinned automatically. This is to give the client a chance to get the metadata and save it before it's unloaded. In this case, it may be useful to unpin the torrent once its metadata has been saved to disk.

For more information about dynamically loading and unloading torrents, see dynamic loading of torrent files.

set_sequential_download()

void set_sequential_download (bool sd) const;

set_sequential_download() enables or disables sequential download. When enabled, the piece picker will pick pieces in sequence instead of rarest first. In this mode, piece priorities are ignored, with the exception of priority 7, which are still preferred over the sequential piece order.

Enabling sequential download will affect the piece distribution negatively in the swarm. It should be used sparingly.

connect_peer()

void connect_peer (tcp::endpoint const& adr, int source = 0
      , int flags = 0x1 + 0x4 + 0x8) const;

connect_peer() is a way to manually connect to peers that one believe is a part of the torrent. If the peer does not respond, or is not a member of this torrent, it will simply be disconnected. No harm can be done by using this other than an unnecessary connection attempt is made. If the torrent is uninitialized or in queued or checking mode, this will throw libtorrent_exception. The second (optional) argument will be bitwise ORed into the source mask of this peer. Typically this is one of the source flags in peer_info. i.e. tracker, pex, dht etc.

flags are the same flags that are passed along with the ut_pex extension.

0x01 peer supports encryption.
0x02 peer is a seed
0x04 supports uTP. If this is not set, the peer will only be contacted over TCP.
0x08 supports hole punching protocol. If this flag is received from a peer, it can be used as a rendezvous point in case direct connections to the peer fail

max_uploads() set_max_uploads()

int max_uploads () const;
void set_max_uploads (int max_uploads) const;

set_max_uploads() sets the maximum number of peers that's unchoked at the same time on this torrent. If you set this to -1, there will be no limit. This defaults to infinite. The primary setting controlling this is the global unchoke slots limit, set by unchoke_slots_limit in settings_pack.

max_uploads() returns the current settings.

max_connections() set_max_connections()

int max_connections () const;
void set_max_connections (int max_connections) const;

set_max_connections() sets the maximum number of connection this torrent will open. If all connections are used up, incoming connections may be refused or poor connections may be closed. This must be at least 2. The default is unlimited number of connections. If -1 is given to the function, it means unlimited. There is also a global limit of the number of connections, set by connections_limit in settings_pack.

max_connections() returns the current settings.

move_storage()

void move_storage (std::string const& save_path, int flags = 0) const;

Moves the file(s) that this torrent are currently seeding from or downloading to. If the given save_path is not located on the same drive as the original save path, the files will be copied to the new drive and removed from their original location. This will block all other disk IO, and other torrents download and upload rates may drop while copying the file.

Since disk IO is performed in a separate thread, this operation is also asynchronous. Once the operation completes, the storage_moved_alert is generated, with the new path as the message. If the move fails for some reason, storage_moved_failed_alert is generated instead, containing the error message.

The flags argument determines the behavior of the copying/moving of the files in the torrent. see move_flags_t.

  • always_replace_files = 0
  • fail_if_exist = 1
  • dont_replace = 2

always_replace_files is the default and replaces any file that exist in both the source directory and the target directory.

fail_if_exist first check to see that none of the copy operations would cause an overwrite. If it would, it will fail. Otherwise it will proceed as if it was in always_replace_files mode. Note that there is an inherent race condition here. If the files in the target directory appear after the check but before the copy or move completes, they will be overwritten. When failing because of files already existing in the target path, the error of move_storage_failed_alert is set to boost::system::errc::file_exists.

The intention is that a client may use this as a probe, and if it fails, ask the user which mode to use. The client may then re-issue the move_storage call with one of the other modes.

dont_replace always keeps the existing file in the target directory, if there is one. The source files will still be removed in that case. Note that it won't automatically re-check files. If an incomplete torrent is moved into a directory with the complete files, pause, move, force-recheck and resume. Without the re-checking, the torrent will keep downloading and files in the new download directory will be overwritten.

Files that have been renamed to have absolute paths are not moved by this function. Keep in mind that files that don't belong to the torrent but are stored in the torrent's directory may be moved as well. This goes for files that have been renamed to absolute paths that still end up inside the save path.

rename_file()

void rename_file (int index, std::string const& new_name) const;

Renames the file with the given index asynchronously. The rename operation is complete when either a file_renamed_alert or file_rename_failed_alert is posted.

super_seeding()

void super_seeding (bool on) const;

Enables or disabled super seeding/initial seeding for this torrent. The torrent needs to be a seed for this to take effect.

info_hash()

sha1_hash info_hash () const;

info_hash() returns the info-hash of the torrent. If this handle is to a torrent that hasn't loaded yet (for instance by being added) by a URL, the returned value is undefined.

operator!=() operator<() operator==()

bool operator!= (const torrent_handle& h) const;
bool operator< (const torrent_handle& h) const;
bool operator== (const torrent_handle& h) const;

comparison operators. The order of the torrents is unspecified but stable.

native_handle()

boost::shared_ptr<torrent> native_handle () const;

This function is intended only for use by plugins and the alert dispatch function. This type does not have a stable API and should be relied on as little as possible.

enum flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
overwrite_existing 1  

enum status_flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
query_distributed_copies 1 calculates distributed_copies, distributed_full_copies and distributed_fraction.
query_accurate_download_counters 2 includes partial downloaded blocks in total_done and total_wanted_done.
query_last_seen_complete 4 includes last_seen_complete.
query_pieces 8 includes pieces.
query_verified_pieces 16 includes verified_pieces (only applies to torrents in seed mode).
query_torrent_file 32 includes torrent_file, which is all the static information from the .torrent file.
query_name 64 includes name, the name of the torrent. This is either derived from the .torrent file, or from the &dn= magnet link argument or possibly some other source. If the name of the torrent is not known, this is an empty string.
query_save_path 128 includes save_path, the path to the directory the files of the torrent are saved to.

enum deadline_flags

Declared in "libtorrent/torrent_handle.hpp"

name value description
alert_when_available 1  

enum file_progress_flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
piece_granularity 1 only calculate file progress at piece granularity. This makes the file_progress() call cheaper and also only takes bytes that have passed the hash check into account, so progress cannot regress in this mode.

enum pause_flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
graceful_pause 1  

enum save_resume_flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
flush_disk_cache 1 the disk cache will be flushed before creating the resume data. This avoids a problem with file timestamps in the resume data in case the cache hasn't been flushed yet.
save_info_dict 2 the resume data will contain the metadata from the torrent file as well. This is default for any torrent that's added without a torrent file (such as a magnet link or a URL).
only_if_modified 4 if nothing significant has changed in the torrent since the last time resume data was saved, fail this attempt. Significant changes primarily include more data having been downloaded, file or piece priorities having changed etc. If the resume data doesn't need saving, a save_resume_data_failed_alert is posted with the error resume_data_not_modified.

enum reannounce_flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
ignore_min_interval 1 by default, force-reannounce will still honor the min-interval published by the tracker. If this flag is set, it will be ignored and the tracker is announced immediately.

cache_status

Declared in "libtorrent/disk_io_thread.hpp"

this struct holds a number of statistics counters relevant for the disk io thread and disk cache.

struct cache_status
{
   cache_status ();

   std::vector<cached_piece_info> pieces;
};

cache_status()

cache_status ();

initializes all counters to 0

web_seed_entry

Declared in "libtorrent/torrent_info.hpp"

the web_seed_entry holds information about a web seed (also known as URL seed or HTTP seed). It is essentially a URL with some state associated with it. For more information, see BEP 17 and BEP 19.

struct web_seed_entry
{
   web_seed_entry (std::string const& url_, type_t type_
      , std::string const& auth_ = std::string()
      , headers_t const& extra_headers_ = headers_t());
   bool operator== (web_seed_entry const& e) const;
   bool operator< (web_seed_entry const& e) const;

   enum type_t
   {
      url_seed,
      http_seed,
   };

   std::string url;
   std::string auth;
   headers_t extra_headers;
   boost::uint8_t type;
};

operator==()

bool operator== (web_seed_entry const& e) const;

URL and type comparison

operator<()

bool operator< (web_seed_entry const& e) const;

URL and type less-than comparison

enum type_t

Declared in "libtorrent/torrent_info.hpp"

name value description
url_seed 0  
http_seed 1  
url
The URL of the web seed
auth
Optional authentication. If this is set, it's passed in as HTTP basic auth to the web seed. The format is: username:password.
extra_headers
Any extra HTTP headers that need to be passed to the web seed
type
The type of web seed (see type_t)

torrent_info

Declared in "libtorrent/torrent_info.hpp"

TODO: there may be some opportunities to optimize the size if torrent_info. specifically to turn some std::string and std::vector into pointers

class torrent_info
{
   torrent_info (std::string const& filename, int flags = 0);
   torrent_info (std::string const& filename, error_code& ec, int flags = 0);
   torrent_info (char const* buffer, int size, error_code& ec, int flags = 0);
   torrent_info (sha1_hash const& info_hash, int flags = 0);
   torrent_info (bdecode_node const& torrent_file, error_code& ec, int flags = 0);
   torrent_info (char const* buffer, int size, int flags = 0);
   torrent_info (bdecode_node const& torrent_file, int flags = 0);
   torrent_info (torrent_info const& t);
   ~torrent_info ();
   file_storage const& files () const;
   file_storage const& orig_files () const;
   void rename_file (int index, std::string const& new_filename);
   void remap_files (file_storage const& f);
   std::vector<announce_entry> const& trackers () const;
   void add_tracker (std::string const& url, int tier = 0);
   std::vector<sha1_hash> similar_torrents () const;
   std::vector<std::string> collections () const;
   void add_url_seed (std::string const& url
      , std::string const& extern_auth = std::string()
      , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t());
   std::vector<web_seed_entry> const& web_seeds () const;
   void set_web_seeds (std::vector<web_seed_entry> seeds);
   void add_http_seed (std::string const& url
      , std::string const& extern_auth = std::string()
      , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t());
   boost::int64_t total_size () const;
   int num_pieces () const;
   int piece_length () const;
   const sha1_hash& info_hash () const;
   int num_files () const;
   std::vector<file_slice> map_block (int piece, boost::int64_t offset, int size) const;
   peer_request map_file (int file, boost::int64_t offset, int size) const;
   void unload ();
   void load (char const* buffer, int size, error_code& ec);
   std::string ssl_cert () const;
   bool is_valid () const;
   bool priv () const;
   bool is_i2p () const;
   sha1_hash hash_for_piece (int index) const;
   char const* hash_for_piece_ptr (int index) const;
   int piece_size (int index) const;
   bool is_loaded () const;
   std::vector<sha1_hash> const& merkle_tree () const;
   void set_merkle_tree (std::vector<sha1_hash>& h);
   boost::optional<time_t> creation_date () const;
   const std::string& name () const;
   const std::string& comment () const;
   const std::string& creator () const;
   nodes_t const& nodes () const;
   void add_node (std::pair<std::string, int> const& node);
   bool parse_info_section (bdecode_node const& e, error_code& ec, int flags);
   bdecode_node info (char const* key) const;
   void swap (torrent_info& ti);
   int metadata_size () const;
   boost::shared_array<char> metadata () const;
   bool is_merkle_torrent () const;
   bool parse_torrent_file (bdecode_node const& libtorrent, error_code& ec, int flags);
};

torrent_info()

torrent_info (std::string const& filename, int flags = 0);
torrent_info (std::string const& filename, error_code& ec, int flags = 0);
torrent_info (char const* buffer, int size, error_code& ec, int flags = 0);
torrent_info (sha1_hash const& info_hash, int flags = 0);
torrent_info (bdecode_node const& torrent_file, error_code& ec, int flags = 0);
torrent_info (char const* buffer, int size, int flags = 0);
torrent_info (bdecode_node const& torrent_file, int flags = 0);
torrent_info (torrent_info const& t);

The constructor that takes an info-hash will initialize the info-hash to the given value, but leave all other fields empty. This is used internally when downloading torrents without the metadata. The metadata will be created by libtorrent as soon as it has been downloaded from the swarm.

The constructor that takes a bdecode_node will create a torrent_info object from the information found in the given torrent_file. The bdecode_node represents a tree node in an bencoded file. To load an ordinary .torrent file into a bdecode_node, use bdecode().

The version that takes a buffer pointer and a size will decode it as a .torrent file and initialize the torrent_info object for you.

The version that takes a filename will simply load the torrent file and decode it inside the constructor, for convenience. This might not be the most suitable for applications that want to be able to report detailed errors on what might go wrong.

There is an upper limit on the size of the torrent file that will be loaded by the overload taking a filename. If it's important that even very large torrent files are loaded, use one of the other overloads.

The overloads that takes an error_code const& never throws if an error occur, they will simply set the error code to describe what went wrong and not fully initialize the torrent_info object. The overloads that do not take the extra error_code parameter will always throw if an error occurs. These overloads are not available when building without exception support.

The flags argument is currently unused.

~torrent_info()

~torrent_info ();

frees all storage associated with this torrent_info object

orig_files() files()

file_storage const& files () const;
file_storage const& orig_files () const;

The file_storage object contains the information on how to map the pieces to files. It is separated from the torrent_info object because when creating torrents a storage object needs to be created without having a torrent file. When renaming files in a storage, the storage needs to make its own copy of the file_storage in order to make its mapping differ from the one in the torrent file.

orig_files() returns the original (unmodified) file storage for this torrent. This is used by the web server connection, which needs to request files with the original names. Filename may be changed using torrent_info::rename_file().

For more information on the file_storage object, see the separate document on how to create torrents.

rename_file()

void rename_file (int index, std::string const& new_filename);

Renames a the file with the specified index to the new name. The new filename is reflected by the file_storage returned by files() but not by the one returned by orig_files().

If you want to rename the base name of the torrent (for a multi file torrent), you can copy the file_storage (see files() and orig_files() ), change the name, and then use remap_files().

The new_filename can both be a relative path, in which case the file name is relative to the save_path of the torrent. If the new_filename is an absolute path (i.e. is_complete(new_filename) == true), then the file is detached from the save_path of the torrent. In this case the file is not moved when move_storage() is invoked.

remap_files()

void remap_files (file_storage const& f);

Remaps the file storage to a new file layout. This can be used to, for instance, download all data in a torrent to a single file, or to a number of fixed size sector aligned files, regardless of the number and sizes of the files in the torrent.

The new specified file_storage must have the exact same size as the current one.

trackers() add_tracker()

std::vector<announce_entry> const& trackers () const;
void add_tracker (std::string const& url, int tier = 0);

add_tracker() adds a tracker to the announce-list. The tier determines the order in which the trackers are to be tried.

The trackers() function will return a sorted vector of announce_entry. Each announce entry contains a string, which is the tracker url, and a tier index. The tier index is the high-level priority. No matter which trackers that works or not, the ones with lower tier will always be tried before the one with higher tier number. For more information, see announce_entry.

collections() similar_torrents()

std::vector<sha1_hash> similar_torrents () const;
std::vector<std::string> collections () const;

These two functions are related to BEP 38 (mutable torrents). The vectors returned from these correspond to the "similar" and "collections" keys in the .torrent file. Both info-hashes and collections from within the info-dict and from outside of it are included.

add_url_seed() set_web_seeds() add_http_seed() web_seeds()

void add_url_seed (std::string const& url
      , std::string const& extern_auth = std::string()
      , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t());
std::vector<web_seed_entry> const& web_seeds () const;
void set_web_seeds (std::vector<web_seed_entry> seeds);
void add_http_seed (std::string const& url
      , std::string const& extern_auth = std::string()
      , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t());

web_seeds() returns all url seeds and http seeds in the torrent. Each entry is a web_seed_entry and may refer to either a url seed or http seed.

add_url_seed() and add_http_seed() adds one url to the list of url/http seeds. Currently, the only transport protocol supported for the url is http.

set_web_seeds() replaces all web seeds with the ones specified in the seeds vector.

The extern_auth argument can be used for other authorization schemes than basic HTTP authorization. If set, it will override any username and password found in the URL itself. The string will be sent as the HTTP authorization header's value (without specifying "Basic").

The extra_headers argument defaults to an empty list, but can be used to insert custom HTTP headers in the requests to a specific web seed.

See http seeding for more information.

piece_length() num_pieces() total_size()

boost::int64_t total_size () const;
int num_pieces () const;
int piece_length () const;

total_size(), piece_length() and num_pieces() returns the total number of bytes the torrent-file represents (all the files in it), the number of byte for each piece and the total number of pieces, respectively. The difference between piece_size() and piece_length() is that piece_size() takes the piece index as argument and gives you the exact size of that piece. It will always be the same as piece_length() except in the case of the last piece, which may be smaller.

info_hash()

const sha1_hash& info_hash () const;

returns the info-hash of the torrent

num_files()

int num_files () const;

If you need index-access to files you can use the num_files() along with the file_path(), file_size()-family of functions to access files using indices.

map_block()

std::vector<file_slice> map_block (int piece, boost::int64_t offset, int size) const;

This function will map a piece index, a byte offset within that piece and a size (in bytes) into the corresponding files with offsets where that data for that piece is supposed to be stored. See file_slice.

map_file()

peer_request map_file (int file, boost::int64_t offset, int size) const;

This function will map a range in a specific file into a range in the torrent. The file_offset parameter is the offset in the file, given in bytes, where 0 is the start of the file. See peer_request.

The input range is assumed to be valid within the torrent. file_offset + size is not allowed to be greater than the file size. file_index must refer to a valid file, i.e. it cannot be >= num_files().

unload() load()

void unload ();
void load (char const* buffer, int size, error_code& ec);

load and unload this torrent info

ssl_cert()

std::string ssl_cert () const;

Returns the SSL root certificate for the torrent, if it is an SSL torrent. Otherwise returns an empty string. The certificate is the the public certificate in x509 format.

is_valid()

bool is_valid () const;

returns true if this torrent_info object has a torrent loaded. This is primarily used to determine if a magnet link has had its metadata resolved yet or not.

priv()

bool priv () const;

returns true if this torrent is private. i.e., it should not be distributed on the trackerless network (the kademlia DHT).

is_i2p()

bool is_i2p () const;

returns true if this is an i2p torrent. This is determined by whether or not it has a tracker whose URL domain name ends with ".i2p". i2p torrents disable the DHT and local peer discovery as well as talking to peers over anything other than the i2p network.

hash_for_piece_ptr() hash_for_piece() piece_size()

sha1_hash hash_for_piece (int index) const;
char const* hash_for_piece_ptr (int index) const;
int piece_size (int index) const;

hash_for_piece() takes a piece-index and returns the 20-bytes sha1-hash for that piece and info_hash() returns the 20-bytes sha1-hash for the info-section of the torrent file. hash_for_piece_ptr() returns a pointer to the 20 byte sha1 digest for the piece. Note that the string is not null-terminated.

set_merkle_tree() merkle_tree()

std::vector<sha1_hash> const& merkle_tree () const;
void set_merkle_tree (std::vector<sha1_hash>& h);

merkle_tree() returns a reference to the merkle tree for this torrent, if any.

set_merkle_tree() moves the passed in merkle tree into the torrent_info object. i.e. h will not be identical after the call. You need to set the merkle tree for a torrent that you've just created (as a merkle torrent). The merkle tree is retrieved from the create_torrent::merkle_tree() function, and need to be saved separately from the torrent file itself. Once it's added to libtorrent, the merkle tree will be persisted in the resume data.

creator() creation_date() name() comment()

boost::optional<time_t> creation_date () const;
const std::string& name () const;
const std::string& comment () const;
const std::string& creator () const;

name() returns the name of the torrent.

comment() returns the comment associated with the torrent. If there's no comment, it will return an empty string. creation_date() returns the creation date of the torrent as time_t (posix time). If there's no time stamp in the torrent file, the optional object will be uninitialized.

Both the name and the comment is UTF-8 encoded strings.

creator() returns the creator string in the torrent. If there is no creator string it will return an empty string.

nodes()

nodes_t const& nodes () const;

If this torrent contains any DHT nodes, they are put in this vector in their original form (host name and port number).

add_node()

void add_node (std::pair<std::string, int> const& node);

This is used when creating torrent. Use this to add a known DHT node. It may be used, by the client, to bootstrap into the DHT network.

parse_info_section()

bool parse_info_section (bdecode_node const& e, error_code& ec, int flags);

populates the torrent_info by providing just the info-dict buffer. This is used when loading a torrent from a magnet link for instance, where we only have the info-dict. The bdecode_node e points to a parsed info-dictionary. ec returns an error code if something fails (typically if the info dictionary is malformed). flags are currently unused.

info()

bdecode_node info (char const* key) const;

This function looks up keys from the info-dictionary of the loaded torrent file. It can be used to access extension values put in the .torrent file. If the specified key cannot be found, it returns NULL.

swap()

void swap (torrent_info& ti);

swap the content of this and ti.

metadata_size() metadata()

int metadata_size () const;
boost::shared_array<char> metadata () const;

metadata() returns a the raw info section of the torrent file. The size of the metadata is returned by metadata_size().

is_merkle_torrent()

bool is_merkle_torrent () const;

returns whether or not this is a merkle torrent. see BEP 30.

add_torrent_params

Declared in "libtorrent/add_torrent_params.hpp"

The add_torrent_params is a parameter pack for adding torrents to a session. The key fields when adding a torrent are:

  • ti - when you have a .torrent file
  • url - when you have a magnet link
  • info_hash - when all you have is an info-hash (this is similar to a magnet link)

one of those fields need to be set. Another mandatory field is save_path. The add_torrent_params object is passed into one of the session::add_torrent() overloads or session::async_add_torrent().

If you only specify the info-hash, the torrent file will be downloaded from peers, which requires them to support the metadata extension. For the metadata extension to work, libtorrent must be built with extensions enabled (TORRENT_DISABLE_EXTENSIONS must not be defined). It also takes an optional name argument. This may be left empty in case no name should be assigned to the torrent. In case it's not, the name is used for the torrent as long as it doesn't have metadata. See torrent_handle::name.

struct add_torrent_params
{
   add_torrent_params (storage_constructor_type sc = default_storage_constructor);

   enum flags_t
   {
      flag_seed_mode,
      flag_override_resume_data,
      flag_upload_mode,
      flag_share_mode,
      flag_apply_ip_filter,
      flag_paused,
      flag_auto_managed,
      flag_duplicate_is_error,
      flag_merge_resume_trackers,
      flag_update_subscribe,
      flag_super_seeding,
      flag_sequential_download,
      flag_use_resume_save_path,
      flag_pinned,
      flag_merge_resume_http_seeds,
      flag_stop_when_ready,
   };

   int version;
   boost::shared_ptr<torrent_info> ti;
   std::vector<std::string> trackers;
   std::vector<std::string> url_seeds;
   std::vector<std::pair<std::string, int> > dht_nodes;
   std::string name;
   std::string save_path;
   std::vector<char> resume_data;
   storage_mode_t storage_mode;
   storage_constructor_type storage;
   void* userdata;
   std::vector<boost::uint8_t> file_priorities;
   std::string trackerid;
   std::string url;
   std::string uuid;
   std::string source_feed_url;
   boost::uint64_t flags;
   sha1_hash info_hash;
   int max_uploads;
   int max_connections;
   int upload_limit;
   int download_limit;
};

add_torrent_params()

add_torrent_params (storage_constructor_type sc = default_storage_constructor);

The constructor can be used to initialize the storage constructor, which determines the storage mechanism for the downloaded or seeding data for the torrent. For more information, see the storage field.

enum flags_t

Declared in "libtorrent/add_torrent_params.hpp"

name value description
flag_seed_mode 1

If flag_seed_mode is set, libtorrent will assume that all files are present for this torrent and that they all match the hashes in the torrent file. Each time a peer requests to download a block, the piece is verified against the hash, unless it has been verified already. If a hash fails, the torrent will automatically leave the seed mode and recheck all the files. The use case for this mode is if a torrent is created and seeded, or if the user already know that the files are complete, this is a way to avoid the initial file checks, and significantly reduce the startup time.

Setting flag_seed_mode on a torrent without metadata (a .torrent file) is a no-op and will be ignored.

If resume data is passed in with this torrent, the seed mode saved in there will override the seed mode you set here.

flag_override_resume_data 2 If flag_override_resume_data is set, flags set for this torrent in this add_torrent_params object will take precedence over whatever states are saved in the resume data. For instance, the paused, auto_managed, sequential_download, seed_mode, super_seeding, max_uploads, max_connections, upload_limit and download_limit are all affected by this flag. The intention of this flag is to have any field in add_torrent_params configuring the torrent override the corresponding configuration from the resume file, with the one exception of save resume data, which has its own flag (for historic reasons). "file_priorities" and "save_path" are not affected by this flag.
flag_upload_mode 4

If flag_upload_mode is set, the torrent will be initialized in upload-mode, which means it will not make any piece requests. This state is typically entered on disk I/O errors, and if the torrent is also auto managed, it will be taken out of this state periodically. This mode can be used to avoid race conditions when adjusting priorities of pieces before allowing the torrent to start downloading.

If the torrent is auto-managed (flag_auto_managed), the torrent will eventually be taken out of upload-mode, regardless of how it got there. If it's important to manually control when the torrent leaves upload mode, don't make it auto managed.

flag_share_mode 8

determines if the torrent should be added in share mode or not. Share mode indicates that we are not interested in downloading the torrent, but merely want to improve our share ratio (i.e. increase it). A torrent started in share mode will do its best to never download more than it uploads to the swarm. If the swarm does not have enough demand for upload capacity, the torrent will not download anything. This mode is intended to be safe to add any number of torrents to, without manual screening, without the risk of downloading more than is uploaded.

A torrent in share mode sets the priority to all pieces to 0, except for the pieces that are downloaded, when pieces are decided to be downloaded. This affects the progress bar, which might be set to "100% finished" most of the time. Do not change file or piece priorities for torrents in share mode, it will make it not work.

The share mode has one setting, the share ratio target, see settings_pack::share_mode_target for more info.

flag_apply_ip_filter 16 determines if the IP filter should apply to this torrent or not. By default all torrents are subject to filtering by the IP filter (i.e. this flag is set by default). This is useful if certain torrents needs to be exempt for some reason, being an auto-update torrent for instance.
flag_paused 32 specifies whether or not the torrent is to be started in a paused state. I.e. it won't connect to the tracker or any of the peers until it's resumed. This is typically a good way of avoiding race conditions when setting configuration options on torrents before starting them.
flag_auto_managed 64

If the torrent is auto-managed (flag_auto_managed), the torrent may be resumed at any point, regardless of how it paused. If it's important to manually control when the torrent is paused and resumed, don't make it auto managed.

If flag_auto_managed is set, the torrent will be queued, started and seeded automatically by libtorrent. When this is set, the torrent should also be started as paused. The default queue order is the order the torrents were added. They are all downloaded in that order. For more details, see queuing.

If you pass in resume data, the auto_managed state of the torrent when the resume data was saved will override the auto_managed state you pass in here. You can override this by setting override_resume_data.

flag_duplicate_is_error 128  
flag_merge_resume_trackers 256 defaults to off and specifies whether tracker URLs loaded from resume data should be added to the trackers in the torrent or replace the trackers. When replacing trackers (i.e. this flag is not set), any trackers passed in via add_torrent_params are also replaced by any trackers in the resume data. The default behavior is to have the resume data override the .torrent file _and_ the trackers added in add_torrent_params.
flag_update_subscribe 512 on by default and means that this torrent will be part of state updates when calling post_torrent_updates().
flag_super_seeding 1024 sets the torrent into super seeding mode. If the torrent is not a seed, this flag has no effect. It has the same effect as calling torrent_handle::super_seeding(true) on the torrent handle immediately after adding it.
flag_sequential_download 2048 sets the sequential download state for the torrent. It has the same effect as calling torrent_handle::sequential_download(true) on the torrent handle immediately after adding it.
flag_use_resume_save_path 4096 if this flag is set, the save path from the resume data file, if present, is honored. This defaults to not being set, in which case the save_path specified in add_torrent_params is always used.
flag_pinned 8192 indicates that this torrent should never be unloaded from RAM, even if unloading torrents are allowed in general. Setting this makes the torrent exempt from loading/unloading management.
flag_merge_resume_http_seeds 32768 defaults to off and specifies whether web seed URLs loaded from resume data should be added to the ones in the torrent file or replace them. No distinction is made between the two different kinds of web seeds (BEP 17 and BEP 19). When replacing web seeds (i.e. when this flag is not set), any web seeds passed in via add_torrent_params are also replaced. The default behavior is to have any web seeds in the resume data take precedence over whatever is passed in here as well as the .torrent file.
flag_stop_when_ready 16384 the stop when ready flag. Setting this flag is equivalent to calling torrent_handle::stop_when_ready() immediately after the torrent is added.
version
filled in by the constructor and should be left untouched. It is used for forward binary compatibility.
ti
torrent_info object with the torrent to add. Unless the url or info_hash is set, this is required to be initialized.
trackers
If the torrent doesn't have a tracker, but relies on the DHT to find peers, the trackers can specify tracker URLs for the torrent.
url_seeds
url seeds to be added to the torrent (BEP 17).
dht_nodes name
a list of hostname and port pairs, representing DHT nodes to be added to the session (if DHT is enabled). The hostname may be an IP address.
save_path

the path where the torrent is or will be stored. Note that this may also be stored in resume data. If you want the save path saved in the resume data to be used, you need to set the flag_use_resume_save_path flag.

Note

On windows this path (and other paths) are interpreted as UNC paths. This means they must use backslashes as directory separators and may not contain the special directories "." or "..".

Setting this to an absolute path performs slightly better than a relative path.

resume_data
The optional parameter, resume_data can be given if up to date fast-resume data is available. The fast-resume data can be acquired from a running torrent by calling save_resume_data() on torrent_handle. See fast resume. The vector that is passed in will be swapped into the running torrent instance with std::vector::swap().
storage_mode
One of the values from storage_mode_t. For more information, see storage allocation.
storage
can be used to customize how the data is stored. The default storage will simply write the data to the files it belongs to, but it could be overridden to save everything to a single file at a specific location or encrypt the content on disk for instance. For more information about the storage_interface that needs to be implemented for a custom storage, see storage_interface.
userdata
The userdata parameter is optional and will be passed on to the extension constructor functions, if any (see torrent_handle::add_extension()).
file_priorities
can be set to control the initial file priorities when adding a torrent. The semantics are the same as for torrent_handle::prioritize_files(). The file priorities specified in here take precedence over those specified in the resume data, if any.
trackerid
the default tracker id to be used when announcing to trackers. By default this is empty, and no tracker ID is used, since this is an optional argument. If a tracker returns a tracker ID, that ID is used instead of this.
url
If you specify a url, the torrent will be set in downloading_metadata state until the .torrent file has been downloaded. If there's any error while downloading, the torrent will be stopped and the torrent error state (torrent_status::error) will indicate what went wrong. The url may be set to a magnet link.
uuid
if uuid is specified, it is used to find duplicates. If another torrent is already running with the same UUID as the one being added, it will be considered a duplicate. This is mainly useful for RSS feed items which has UUIDs specified.
source_feed_url
should point to the URL of the RSS feed this torrent comes from, if it comes from an RSS feed.
flags

flags controlling aspects of this torrent and how it's added. See flags_t for details.

Note

The flags field is initialized with default flags by the constructor. In order to preserve default behavior when clearing or setting other flags, make sure to bitwise OR or in a flag or bitwise AND the inverse of a flag to clear it.

info_hash
set this to the info hash of the torrent to add in case the info-hash is the only known property of the torrent. i.e. you don't have a .torrent file nor a magnet link.
max_uploads max_connections upload_limit download_limit

max_uploads, max_connections, upload_limit, download_limit correspond to the set_max_uploads(), set_max_connections(), set_upload_limit() and set_download_limit() functions on torrent_handle. These values let you initialize these settings when the torrent is added, instead of calling these functions immediately following adding it.

-1 means unlimited on these settings just like their counterpart functions on torrent_handle

For fine grained control over rate limits depending on various properties of the peer connection, see peer classes.

peer_info

Declared in "libtorrent/peer_info.hpp"

holds information and statistics about one peer that libtorrent is connected to

struct peer_info
{
   enum peer_flags_t
   {
      interesting,
      choked,
      remote_interested,
      remote_choked,
      supports_extensions,
      local_connection,
      handshake,
      connecting,
      on_parole,
      seed,
      optimistic_unchoke,
      snubbed,
      upload_only,
      endgame_mode,
      holepunched,
      i2p_socket,
      utp_socket,
      ssl_socket,
      rc4_encrypted,
      plaintext_encrypted,
   };

   enum peer_source_flags
   {
      tracker,
      dht,
      pex,
      lsd,
      resume_data,
      incoming,
   };

   enum connection_type_t
   {
      standard_bittorrent,
      web_seed,
      http_seed,
   };

   enum bw_state
   {
      bw_idle,
      bw_limit,
      bw_network,
      bw_disk,
   };

   std::string client;
   bitfield pieces;
   boost::int64_t total_download;
   boost::int64_t total_upload;
   time_duration last_request;
   time_duration last_active;
   time_duration download_queue_time;
   boost::uint32_t flags;
   boost::uint32_t source;
   int up_speed;
   int down_speed;
   int payload_up_speed;
   int payload_down_speed;
   peer_id pid;
   int queue_bytes;
   int request_timeout;
   int send_buffer_size;
   int used_send_buffer;
   int receive_buffer_size;
   int used_receive_buffer;
   int num_hashfails;
   int download_queue_length;
   int timed_out_requests;
   int busy_requests;
   int requests_in_buffer;
   int target_dl_queue_length;
   int upload_queue_length;
   int failcount;
   int downloading_piece_index;
   int downloading_block_index;
   int downloading_progress;
   int downloading_total;
   int connection_type;
   int remote_dl_rate;
   int pending_disk_bytes;
   int pending_disk_read_bytes;
   int send_quota;
   int receive_quota;
   int rtt;
   int num_pieces;
   int download_rate_peak;
   int upload_rate_peak;
   float progress;
   int progress_ppm;
   int estimated_reciprocation_rate;
   tcp::endpoint ip;
   tcp::endpoint local_endpoint;
   char read_state;
   char write_state;
};

enum peer_flags_t

Declared in "libtorrent/peer_info.hpp"

name value description
interesting 1 we are interested in pieces from this peer.
choked 2 we have choked this peer.
remote_interested 4 the peer is interested in us
remote_choked 8 the peer has choked us.
supports_extensions 16 means that this peer supports the extension protocol.
local_connection 32 The connection was initiated by us, the peer has a listen port open, and that port is the same as in the address of this peer. If this flag is not set, this peer connection was opened by this peer connecting to us.
handshake 64 The connection is opened, and waiting for the handshake. Until the handshake is done, the peer cannot be identified.
connecting 128 The connection is in a half-open state (i.e. it is being connected).
on_parole 512 The peer has participated in a piece that failed the hash check, and is now "on parole", which means we're only requesting whole pieces from this peer until it either fails that piece or proves that it doesn't send bad data.
seed 1024 This peer is a seed (it has all the pieces).
optimistic_unchoke 2048 This peer is subject to an optimistic unchoke. It has been unchoked for a while to see if it might unchoke us in return an earn an upload/unchoke slot. If it doesn't within some period of time, it will be choked and another peer will be optimistically unchoked.
snubbed 4096 This peer has recently failed to send a block within the request timeout from when the request was sent. We're currently picking one block at a time from this peer.
upload_only 8192 This peer has either explicitly (with an extension) or implicitly (by becoming a seed) told us that it will not downloading anything more, regardless of which pieces we have.
endgame_mode 16384 This means the last time this peer picket a piece, it could not pick as many as it wanted because there were not enough free ones. i.e. all pieces this peer has were already requested from other peers.
holepunched 32768 This flag is set if the peer was in holepunch mode when the connection succeeded. This typically only happens if both peers are behind a NAT and the peers connect via the NAT holepunch mechanism.
i2p_socket 65536 indicates that this socket is runnin on top of the I2P transport.
utp_socket 131072 indicates that this socket is a uTP socket
ssl_socket 262144 indicates that this socket is running on top of an SSL (TLS) channel
rc4_encrypted 1048576 this connection is obfuscated with RC4
plaintext_encrypted 2097152 the handshake of this connection was obfuscated with a diffie-hellman exchange

enum peer_source_flags

Declared in "libtorrent/peer_info.hpp"

name value description
tracker 1 The peer was received from the tracker.
dht 2 The peer was received from the kademlia DHT.
pex 4 The peer was received from the peer exchange extension.
lsd 8 The peer was received from the local service discovery (The peer is on the local network).
resume_data 16 The peer was added from the fast resume data.
incoming 32 we received an incoming connection from this peer

enum connection_type_t

Declared in "libtorrent/peer_info.hpp"

name value description
standard_bittorrent 0 Regular bittorrent connection
web_seed 1 HTTP connection using the BEP 19 protocol
http_seed 2 HTTP connection using the BEP 17 protocol

enum bw_state

Declared in "libtorrent/peer_info.hpp"

name value description
bw_idle 0 The peer is not waiting for any external events to send or receive data.
bw_limit 1 The peer is waiting for the rate limiter.
bw_network 2 The peer has quota and is currently waiting for a network read or write operation to complete. This is the state all peers are in if there are no bandwidth limits.
bw_disk 4 The peer is waiting for the disk I/O thread to catch up writing buffers to disk before downloading more.
client
a string describing the software at the other end of the connection. In some cases this information is not available, then it will contain a string that may give away something about which software is running in the other end. In the case of a web seed, the server type and version will be a part of this string.
pieces
a bitfield, with one bit per piece in the torrent. Each bit tells you if the peer has that piece (if it's set to 1) or if the peer miss that piece (set to 0).
total_download total_upload
the total number of bytes downloaded from and uploaded to this peer. These numbers do not include the protocol chatter, but only the payload data.
last_request last_active
the time since we last sent a request to this peer and since any transfer occurred with this peer
download_queue_time
the time until all blocks in the request queue will be downloaded
flags
tells you in which state the peer is in. It is set to any combination of the peer_flags_t enum.
source
a combination of flags describing from which sources this peer was received. See peer_source_flags.
up_speed down_speed
the current upload and download speed we have to and from this peer (including any protocol messages). updated about once per second
payload_up_speed payload_down_speed
The transfer rates of payload data only updated about once per second
pid
the peer's id as used in the bit torrent protocol. This id can be used to extract 'fingerprints' from the peer. Sometimes it can tell you which client the peer is using. See identify_client()_
request_timeout
the number of seconds until the current front piece request will time out. This timeout can be adjusted through settings_pack::request_timeout. -1 means that there is not outstanding request.
send_buffer_size used_send_buffer
the number of bytes allocated and used for the peer's send buffer, respectively.
receive_buffer_size used_receive_buffer
the number of bytes allocated and used as receive buffer, respectively.
num_hashfails
the number of pieces this peer has participated in sending us that turned out to fail the hash check.
download_queue_length
this is the number of requests we have sent to this peer that we haven't got a response for yet
timed_out_requests
the number of block requests that have timed out, and are still in the download queue
busy_requests
the number of busy requests in the download queue. A busy request is a request for a block we've also requested from a different peer
requests_in_buffer
the number of requests messages that are currently in the send buffer waiting to be sent.
target_dl_queue_length
the number of requests that is tried to be maintained (this is typically a function of download speed)
upload_queue_length
the number of piece-requests we have received from this peer that we haven't answered with a piece yet.
failcount
the number of times this peer has "failed". i.e. failed to connect or disconnected us. The failcount is decremented when we see this peer in a tracker response or peer exchange message.
downloading_piece_index downloading_block_index downloading_progress downloading_total
You can know which piece, and which part of that piece, that is currently being downloaded from a specific peer by looking at these four members. downloading_piece_index is the index of the piece that is currently being downloaded. This may be set to -1 if there's currently no piece downloading from this peer. If it is >= 0, the other three members are valid. downloading_block_index is the index of the block (or sub-piece) that is being downloaded. downloading_progress is the number of bytes of this block we have received from the peer, and downloading_total is the total number of bytes in this block.
connection_type
the kind of connection this peer uses. See connection_type_t.
remote_dl_rate
an estimate of the rate this peer is downloading at, in bytes per second.
pending_disk_bytes
the number of bytes this peer has pending in the disk-io thread. Downloaded and waiting to be written to disk. This is what is capped by settings_pack::max_queued_disk_bytes.
pending_disk_read_bytes
number of outstanding bytes to read from disk
send_quota receive_quota
the number of bytes this peer has been assigned to be allowed to send and receive until it has to request more quota from the bandwidth manager.
rtt
an estimated round trip time to this peer, in milliseconds. It is estimated by timing the the TCP connect(). It may be 0 for incoming connections.
num_pieces
the number of pieces this peer has.
download_rate_peak upload_rate_peak
the highest download and upload rates seen on this connection. They are given in bytes per second. This number is reset to 0 on reconnect.
progress
the progress of the peer in the range [0, 1]. This is always 0 when floating point operations are disabled, instead use progress_ppm.
progress_ppm
indicates the download progress of the peer in the range [0, 1000000] (parts per million).
estimated_reciprocation_rate
this is an estimation of the upload rate, to this peer, where it will unchoke us. This is a coarse estimation based on the rate at which we sent right before we were choked. This is primarily used for the bittyrant choking algorithm.
ip
the IP-address to this peer. The type is an asio endpoint. For more info, see the asio documentation.
local_endpoint
the IP and port pair the socket is bound to locally. i.e. the IP address of the interface it's going out over. This may be useful for multi-homed clients with multiple interfaces to the internet.
read_state write_state
bitmasks indicating what state this peer is in with regards to sending and receiving data. The states are declared in the bw_state enum.

peer_request

Declared in "libtorrent/peer_request.hpp"

represents a byte range within a piece. Internally this is is used for incoming piece requests.

struct peer_request
{
   bool operator== (peer_request const& r) const;

   int piece;
   int start;
   int length;
};

operator==()

bool operator== (peer_request const& r) const;

returns true if the right hand side peer_request refers to the same range as this does.

piece
the index of the piece in which the range starts.
start
the offset within that piece where the range starts.
length
the size of the range, in bytes.

dht_storage_counters

Declared in "libtorrent/kademlia/dht_storage.hpp"

This structure hold the relevant counters for the storage

struct dht_storage_counters
{
   boost::int32_t torrents;
   boost::int32_t peers;
   boost::int32_t immutable_data;
   boost::int32_t mutable_data;
};

dht_storage_interface

Declared in "libtorrent/kademlia/dht_storage.hpp"

The DHT storage interface is a pure virtual class that can be implemented to customize how the data for the DHT is stored.

The default storage implementation uses three maps in RAM to save the peers, mutable and immutable items and it's designed to provide a fast and fully compliant behavior of the BEPs.

libtorrent comes with one built-in storage implementation: dht_default_storage (private non-accessible class). Its constructor function is called dht_default_storage_constructor().

struct dht_storage_interface
{
   virtual bool get_peers (sha1_hash const& info_hash
      , bool noseed, bool scrape
      , entry& peers) const = 0;
   virtual void announce_peer (sha1_hash const& info_hash
      , tcp::endpoint const& endp
      , std::string const& name, bool seed) = 0;
   virtual bool get_immutable_item (sha1_hash const& target
      , entry& item) const = 0;
   virtual void put_immutable_item (sha1_hash const& target
      , char const* buf, int size
      , address const& addr) = 0;
   virtual bool get_mutable_item_seq (sha1_hash const& target
      , boost::int64_t& seq) const = 0;
   virtual bool get_mutable_item (sha1_hash const& target
      , boost::int64_t seq, bool force_fill
      , entry& item) const = 0;
   virtual void put_mutable_item (sha1_hash const& target
      , char const* buf, int size
      , char const* sig
      , boost::int64_t seq
      , char const* pk
      , char const* salt, int salt_size
      , address const& addr) = 0;
   virtual void tick () = 0;
   virtual dht_storage_counters counters () const = 0;
   virtual ~dht_storage_interface ();
};

get_peers()

virtual bool get_peers (sha1_hash const& info_hash
      , bool noseed, bool scrape
      , entry& peers) const = 0;

This function retrieve the peers tracked by the DHT corresponding to the given info_hash. You can specify if you want only seeds and/or you are scraping the data.

For future implementers: If the torrent tracked contains a name, such a name must be stored as a string in peers["n"]

If the scrape parameter is true, you should fill these keys:

peers["BFpe"] - with the standard bit representation of a
                256 bloom filter containing the downloaders
peers["BFsd"] - with the standard bit representation of a
                256 bloom filter containing the seeders

If the scrape parameter is false, you should fill the key peers["values"] with a list containing a subset of peers tracked by the given info_hash. Such a list should consider the value of dht_settings::max_peers_reply. If noseed is true only peers marked as no seed should be included.

returns true if an entry with the info_hash is found and the data is returned inside the (entry) out parameter peers.

announce_peer()

virtual void announce_peer (sha1_hash const& info_hash
      , tcp::endpoint const& endp
      , std::string const& name, bool seed) = 0;

This function is named announce_peer for consistency with the upper layers, but has nothing to do with networking. Its only responsibility is store the peer in such a way that it's returned in the entry with the lookup_peers.

The name parameter is the name of the torrent if provided in the announce_peer DHT message. The length of this value should have a maximum length in the final storage. The default implementation truncate the value for a maximum of 50 characters.

get_immutable_item()

virtual bool get_immutable_item (sha1_hash const& target
      , entry& item) const = 0;

This function retrieves the immutable item given its target hash.

For future implementers: The value should be returned as an entry in the key item["v"].

returns true if the item is found and the data is returned inside the (entry) out parameter item.

put_immutable_item()

virtual void put_immutable_item (sha1_hash const& target
      , char const* buf, int size
      , address const& addr) = 0;

Store the item's data. This layer is only for storage. The authentication of the item is performed by the upper layer.

For implementers: This data can be stored only if the target is not already present. The implementation should consider the value of dht_settings::max_dht_items.

get_mutable_item_seq()

virtual bool get_mutable_item_seq (sha1_hash const& target
      , boost::int64_t& seq) const = 0;

This function retrieves the sequence number of a mutable item.

returns true if the item is found and the data is returned inside the out parameter seq.

get_mutable_item()

virtual bool get_mutable_item (sha1_hash const& target
      , boost::int64_t seq, bool force_fill
      , entry& item) const = 0;

This function retrieves the mutable stored in the DHT.

For implementers: The item sequence should be stored in the key item["seq"]. if force_fill is true or (0 <= seq and seq < item["seq"]) the following keys should be filled item["v"] - with the value no encoded. item["sig"] - with a string representation of the signature. item["k"] - with a string representation of the public key.

returns true if the item is found and the data is returned inside the (entry) out parameter item.

put_mutable_item()

virtual void put_mutable_item (sha1_hash const& target
      , char const* buf, int size
      , char const* sig
      , boost::int64_t seq
      , char const* pk
      , char const* salt, int salt_size
      , address const& addr) = 0;

Store the item's data. This layer is only for storage. The authentication of the item is performed by the upper layer.

For implementers: The sequence number should be checked if the item is already present. The implementation should consider the value of dht_settings::max_dht_items.

tick()

virtual void tick () = 0;

This function is called periodically (non-constant frequency).

For implementers: Use this functions for expire peers or items or any other storage cleanup.

session_stats_metrics()

Declared in "libtorrent/session_stats.hpp"

std::vector<stats_metric> session_stats_metrics ();

This free function returns the list of available metrics exposed by libtorrent's statistics API. Each metric has a name and a value index. The value index is the index into the array in session_stats_alert where this metric's value can be found when the session stats is sampled (by calling post_session_stats()).

find_metric_idx()

Declared in "libtorrent/session_stats.hpp"

int find_metric_idx (char const* name);

given a name of a metric, this function returns the counter index of it, or -1 if it could not be found. The counter index is the index into the values array returned by session_stats_alert.

make_magnet_uri()

Declared in "libtorrent/magnet_uri.hpp"

std::string make_magnet_uri (torrent_handle const& handle);
std::string make_magnet_uri (torrent_info const& info);

Generates a magnet URI from the specified torrent. If the torrent handle is invalid, an empty string is returned.

For more information about magnet links, see magnet links.

parse_magnet_uri()

Declared in "libtorrent/magnet_uri.hpp"

void parse_magnet_uri (std::string const& uri
   , add_torrent_params& p, error_code& ec);

This function parses out information from the magnet link and populates the add_torrent_params object.

to_hex()

Declared in "libtorrent/hex.hpp"

void to_hex (char const *in, int len, char* out);
std::string to_hex (std::string const& s);

The overload taking a std::string converts (binary) the string s to hexadecimal representation and returns it. The overload taking a char const* and a length converts the binary buffer [in, in + len) to hexadecimal and prints it to the buffer out. The caller is responsible for making sure the buffer pointed to by out is large enough, i.e. has at least len * 2 bytes of space.

from_hex()

Declared in "libtorrent/hex.hpp"

bool from_hex (char const *in, int len, char* out);

converts the buffer [in, in + len) from hexadecimal to binary. The binary output is written to the buffer pointed to by out. The caller is responsible for making sure the buffer at out has enough space for the result to be written to, i.e. (len + 1) / 2 bytes.

version()

Declared in "libtorrent/version.hpp"

char const* version ();

returns the libtorrent version as string form in this format: "<major>.<minor>.<tiny>.<tag>"

generate_fingerprint()

Declared in "libtorrent/fingerprint.hpp"

std::string generate_fingerprint (std::string name
   , int major, int minor = 0, int revision = 0, int tag = 0);

This is a utility function to produce a client ID fingerprint formatted to the most common convention.

The name string should contain exactly two characters. These are the characters unique to your client, used to identify it. Make sure not to clash with anybody else. Here are some taken id's:

id chars client
'AZ' Azureus
'LT' libtorrent (default)
'BX' BittorrentX
'MT' Moonlight Torrent
'TS' Torrent Storm
'SS' Swarm Scope
'XT' Xan Torrent

There's an informal directory of client id's here.

The major, minor, revision and tag parameters are used to identify the version of your client.

hash_value()

Declared in "libtorrent/torrent_handle.hpp"

std::size_t hash_value (torrent_status const& ts);

allows torrent_handle to be used in unordered_map and unordered_set.

hash_value()

Declared in "libtorrent/torrent_handle.hpp"

std::size_t hash_value (torrent_handle const& h);

for boost::hash (and to support using this type in unordered_map etc.)

is_utp_stream_logging()

Declared in "libtorrent/utp_stream.hpp"

bool is_utp_stream_logging ();

set_utp_stream_logging()

Declared in "libtorrent/utp_stream.hpp"

void set_utp_stream_logging (bool enable);

This function should be used at the very beginning and very end of your program.

verify_message()

Declared in "libtorrent/kademlia/msg.hpp"

bool verify_message (bdecode_node const& msg, key_desc_t const desc[]
   , bdecode_node ret[], int size, char* error, int error_size);

dht_default_storage_constructor()

Declared in "libtorrent/kademlia/dht_storage.hpp"

dht_storage_interface* dht_default_storage_constructor (sha1_hash const& id
   , dht_settings const& settings);

sign_mutable_item()

Declared in "libtorrent/kademlia/item.hpp"

void sign_mutable_item (
   std::pair<char const*, int> v
   , std::pair<char const*, int> salt
   , boost::uint64_t seq
   , char const* pk
   , char const* sk
   , char* sig);

given a byte range v and an optional byte range salt, a sequence number, public key pk (must be 32 bytes) and a secret key sk (must be 64 bytes), this function produces a signature which is written into a 64 byte buffer pointed to by sig. The caller is responsible for allocating the destination buffer that's passed in as the sig argument. Typically it would be allocated on the stack.

libtorrent-rasterbar-1.1.13/docs/reference-Create_Torrents.html000066400000000000000000000644241351156116000246320ustar00rootroot00000000000000 reference-Create_Torrents.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Create Torrents

This section describes the functions and classes that are used to create torrent files. It is a layered API with low level classes and higher level convenience functions. A torrent is created in 4 steps:

  1. first the files that will be part of the torrent are determined.
  2. the torrent properties are set, such as tracker url, web seeds, DHT nodes etc.
  3. Read through all the files in the torrent, SHA-1 all the data and set the piece hashes.
  4. The torrent is bencoded into a file or buffer.

If there are a lot of files and or deep directory hierarchies to traverse, step one can be time consuming.

Typically step 3 is by far the most time consuming step, since it requires to read all the bytes from all the files in the torrent.

All of these classes and functions are declared by including libtorrent/create_torrent.hpp.

example:

file_storage fs;

// recursively adds files in directories
add_files(fs, "./my_torrent");

create_torrent t(fs);
t.add_tracker("http://my.tracker.com/announce");
t.set_creator("libtorrent example");

// reads the files and calculates the hashes
set_piece_hashes(t, ".");

ofstream out("my_torrent.torrent", std::ios_base::binary);
bencode(std::ostream_iterator<char>(out), t.generate());

create_torrent

Declared in "libtorrent/create_torrent.hpp"

This class holds state for creating a torrent. After having added all information to it, call create_torrent::generate() to generate the torrent. The entry that's returned can then be bencoded into a .torrent file using bencode().

struct create_torrent
{
   create_torrent (torrent_info const& ti);
   create_torrent (torrent_info const& ti, bool use_preformatted);
   create_torrent (file_storage& fs, int piece_size = 0
      , int pad_file_limit = -1, int flags = optimize_alignment
      , int alignment = -1);
   entry generate () const;
   file_storage const& files () const;
   void set_comment (char const* str);
   void set_creator (char const* str);
   void set_hash (int index, sha1_hash const& h);
   void set_file_hash (int index, sha1_hash const& h);
   void add_url_seed (std::string const& url);
   void add_http_seed (std::string const& url);
   void add_node (std::pair<std::string, int> const& node);
   void add_tracker (std::string const& url, int tier = 0);
   void set_root_cert (std::string const& pem);
   bool priv () const;
   void set_priv (bool p);
   int num_pieces () const;
   int piece_length () const;
   int piece_size (int i) const;
   std::vector<sha1_hash> const& merkle_tree () const;
   void add_similar_torrent (sha1_hash ih);
   void add_collection (std::string c);

   enum flags_t
   {
      optimize_alignment,
      merkle,
      modification_time,
      symlinks,
      mutable_torrent_support,
   };
};

create_torrent()

create_torrent (torrent_info const& ti);
create_torrent (torrent_info const& ti, bool use_preformatted);
create_torrent (file_storage& fs, int piece_size = 0
      , int pad_file_limit = -1, int flags = optimize_alignment
      , int alignment = -1);

The piece_size is the size of each piece in bytes. It must be a multiple of 16 kiB. If a piece size of 0 is specified, a piece_size will be calculated such that the torrent file is roughly 40 kB.

If a pad_file_limit is specified (other than -1), any file larger than the specified number of bytes will be preceded by a pad file to align it with the start of a piece. The pad_file_limit is ignored unless the optimize_alignment flag is passed. Typically it doesn't make sense to set this any lower than 4 kiB.

The overload that takes a torrent_info object will make a verbatim copy of its info dictionary (to preserve the info-hash). The copy of the info dictionary will be used by create_torrent::generate(). This means that none of the member functions of create_torrent that affects the content of the info dictionary (such as set_hash()), will have any affect.

The flags arguments specifies options for the torrent creation. It can be any combination of the flags defined by create_torrent::flags_t.

alignment is used when pad files are enabled. This is the size eligible files are aligned to. The default is -1, which means the piece size of the torrent. The use_preformatted parameter can be set to true to preserve invalid encoding of the .torrent file.

generate()

entry generate () const;

This function will generate the .torrent file as a bencode tree. In order to generate the flat file, use the bencode() function.

It may be useful to add custom entries to the torrent file before bencoding it and saving it to disk.

If anything goes wrong during torrent generation, this function will return an empty entry structure. You can test for this condition by querying the type of the entry:

file_storage fs;
// add file ...
create_torrent t(fs);
// add trackers and piece hashes ...
e = t.generate();

if (e.type() == entry::undefined_t)
{
        // something went wrong
}

For instance, you cannot generate a torrent with 0 files in it. If you don't add any files to the file_storage, torrent generation will fail.

files()

file_storage const& files () const;

returns an immutable reference to the file_storage used to create the torrent from.

set_comment()

void set_comment (char const* str);

Sets the comment for the torrent. The string str should be utf-8 encoded. The comment in a torrent file is optional.

set_creator()

void set_creator (char const* str);

Sets the creator of the torrent. The string str should be utf-8 encoded. This is optional.

set_hash()

void set_hash (int index, sha1_hash const& h);

This sets the SHA-1 hash for the specified piece (index). You are required to set the hash for every piece in the torrent before generating it. If you have the files on disk, you can use the high level convenience function to do this. See set_piece_hashes().

set_file_hash()

void set_file_hash (int index, sha1_hash const& h);

This sets the sha1 hash for this file. This hash will end up under the key sha1 associated with this file (for multi-file torrents) or in the root info dictionary for single-file torrents.

add_url_seed() add_http_seed()

void add_url_seed (std::string const& url);
void add_http_seed (std::string const& url);

This adds a url seed to the torrent. You can have any number of url seeds. For a single file torrent, this should be an HTTP url, pointing to a file with identical content as the file of the torrent. For a multi-file torrent, it should point to a directory containing a directory with the same name as this torrent, and all the files of the torrent in it.

The second function, add_http_seed() adds an HTTP seed instead.

add_node()

void add_node (std::pair<std::string, int> const& node);

This adds a DHT node to the torrent. This especially useful if you're creating a tracker less torrent. It can be used by clients to bootstrap their DHT node from. The node is a hostname and a port number where there is a DHT node running. You can have any number of DHT nodes in a torrent.

add_tracker()

void add_tracker (std::string const& url, int tier = 0);

Adds a tracker to the torrent. This is not strictly required, but most torrents use a tracker as their main source of peers. The url should be an http:// or udp:// url to a machine running a bittorrent tracker that accepts announces for this torrent's info-hash. The tier is the fallback priority of the tracker. All trackers with tier 0 are tried first (in any order). If all fail, trackers with tier 1 are tried. If all of those fail, trackers with tier 2 are tried, and so on.

set_root_cert()

void set_root_cert (std::string const& pem);

This function sets an X.509 certificate in PEM format to the torrent. This makes the torrent an SSL torrent. An SSL torrent requires that each peer has a valid certificate signed by this root certificate. For SSL torrents, all peers are connecting over SSL connections. For more information, see the section on ssl torrents.

The string is not the path to the cert, it's the actual content of the certificate, loaded into a std::string.

priv() set_priv()

bool priv () const;
void set_priv (bool p);

Sets and queries the private flag of the torrent. Torrents with the private flag set ask clients to not use any other sources than the tracker for peers, and to not advertise itself publicly, apart from the tracker.

num_pieces()

int num_pieces () const;

returns the number of pieces in the associated file_storage object.

piece_length() piece_size()

int piece_length () const;
int piece_size (int i) const;

piece_length() returns the piece size of all pieces but the last one. piece_size() returns the size of the specified piece. these functions are just forwarding to the associated file_storage.

merkle_tree()

std::vector<sha1_hash> const& merkle_tree () const;

This function returns the merkle hash tree, if the torrent was created as a merkle torrent. The tree is created by generate() and won't be valid until that function has been called. When creating a merkle tree torrent, the actual tree itself has to be saved off separately and fed into libtorrent the first time you start seeding it, through the torrent_info::set_merkle_tree() function. From that point onwards, the tree will be saved in the resume data.

add_collection() add_similar_torrent()

void add_similar_torrent (sha1_hash ih);
void add_collection (std::string c);

Add similar torrents (by info-hash) or collections of similar torrents. Similar torrents are expected to share some files with this torrent. Torrents sharing a collection name with this torrent are also expected to share files with this torrent. A torrent may have more than one collection and more than one similar torrents. For more information, see BEP 38.

enum flags_t

Declared in "libtorrent/create_torrent.hpp"

name value description
optimize_alignment 1 This will insert pad files to align the files to piece boundaries, for optimized disk-I/O. This will minimize the number of bytes of pad- files, to keep the impact down for clients that don't support them.
merkle 2 This will create a merkle hash tree torrent. A merkle torrent cannot be opened in clients that don't specifically support merkle torrents. The benefit is that the resulting torrent file will be much smaller and not grow with more pieces. When this option is specified, it is recommended to have a fairly small piece size, say 64 kiB. When creating merkle torrents, the full hash tree is also generated and should be saved off separately. It is accessed through the create_torrent::merkle_tree() function.
modification_time 4 This will include the file modification time as part of the torrent. This is not enabled by default, as it might cause problems when you create a torrent from separate files with the same content, hoping to yield the same info-hash. If the files have different modification times, with this option enabled, you would get different info-hashes for the files.
symlinks 8 If this flag is set, files that are symlinks get a symlink attribute set on them and their data will not be included in the torrent. This is useful if you need to reconstruct a file hierarchy which contains symlinks.
mutable_torrent_support 16 to create a torrent that can be updated via a mutable torrent (see BEP 38). This also needs to be enabled for torrents that update another torrent.

add_files()

Declared in "libtorrent/create_torrent.hpp"

void add_files (file_storage& fs, std::string const& file
   , boost::function<bool(std::string)> p, boost::uint32_t flags = 0);
void add_files (file_storage& fs, std::string const& file
   , boost::uint32_t flags = 0);

Adds the file specified by path to the file_storage object. In case path refers to a directory, files will be added recursively from the directory.

If specified, the predicate p is called once for every file and directory that is encountered. Files for which p returns true are added, and directories for which p returns true are traversed. p must have the following signature:

bool Pred(std::string const& p);

The path that is passed in to the predicate is the full path of the file or directory. If no predicate is specified, all files are added, and all directories are traversed.

The ".." directory is never traversed.

The flags argument should be the same as the flags passed to the create_torrent constructor.

set_piece_hashes()

Declared in "libtorrent/create_torrent.hpp"

void set_piece_hashes (create_torrent& t, std::string const& p
   , boost::function<void(int)> const& f, error_code& ec);
inline void set_piece_hashes (create_torrent& t, std::string const& p);
inline void set_piece_hashes (create_torrent& t, std::string const& p, error_code& ec);

This function will assume that the files added to the torrent file exists at path p, read those files and hash the content and set the hashes in the create_torrent object. The optional function f is called in between every hash that is set. f must have the following signature:

void Fun(int);

The overloads that don't take an error_code& may throw an exception in case of a file error, the other overloads sets the error code to reflect the error, if any.

libtorrent-rasterbar-1.1.13/docs/reference-Custom_Storage.html000066400000000000000000001416071351156116000244640ustar00rootroot00000000000000 reference-Custom_Storage.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Custom Storage

libtorrent provides a customization point for storage of data. By default, (default_storage) downloaded files are saved to disk according with the general conventions of bittorrent clients, mimicing the original file layout when the torrent was created. The libtorrent user may define a custom storage to store piece data in a different way.

A custom storage implementation must derive from and implement the storage_interface. You must also provide a function that constructs the custom storage object and provide this function to the add_torrent() call via add_torrent_params. Either passed in to the constructor or by setting the add_torrent_params::storage field.

This is an example storage implementation that stores all pieces in a std::map, i.e. in RAM. It's not necessarily very useful in practice, but illustrates the basics of implementing a custom storage.

struct temp_storage : storage_interface
{
        temp_storage(file_storage const& fs) : m_files(fs) {}
        virtual bool initialize(storage_error& se) { return false; }
        virtual bool has_any_file() { return false; }
        virtual int read(char* buf, int piece, int offset, int size)
        {
                std::map<int, std::vector<char> >::const_iterator i = m_file_data.find(piece);
                if (i == m_file_data.end()) return 0;
                int available = i->second.size() - offset;
                if (available <= 0) return 0;
                if (available > size) available = size;
                memcpy(buf, &i->second[offset], available);
                return available;
        }
        virtual int write(const char* buf, int piece, int offset, int size)
        {
                std::vector<char>& data = m_file_data[piece];
                if (data.size() < offset + size) data.resize(offset + size);
                std::memcpy(&data[offset], buf, size);
                return size;
        }
        virtual bool rename_file(int file, std::string const& new_name)
        { assert(false); return false; }
        virtual bool move_storage(std::string const& save_path) { return false; }
        virtual bool verify_resume_data(bdecode_node const& rd
                , std::vector<std::string> const* links
                , storage_error& error) { return false; }
        virtual bool write_resume_data(entry& rd) const { return false; }
        virtual boost::int64_t physical_offset(int piece, int offset)
        { return piece * m_files.piece_length() + offset; };
        virtual sha1_hash hash_for_slot(int piece, partial_hash& ph, int piece_size)
        {
                int left = piece_size - ph.offset;
                assert(left >= 0);
                if (left > 0)
                {
                        std::vector<char>& data = m_file_data[piece];
                        // if there are padding files, those blocks will be considered
                        // completed even though they haven't been written to the storage.
                        // in this case, just extend the piece buffer to its full size
                        // and fill it with zeros.
                        if (data.size() < piece_size) data.resize(piece_size, 0);
                        ph.h.update(&data[ph.offset], left);
                }
                return ph.h.final();
        }
        virtual bool release_files() { return false; }
        virtual bool delete_files() { return false; }

        std::map<int, std::vector<char> > m_file_data;
        file_storage m_files;
};

storage_interface* temp_storage_constructor(storage_params const& params)
{
        return new temp_storage(*params.files);
}

storage_interface

Declared in "libtorrent/storage.hpp"

The storage interface is a pure virtual class that can be implemented to customize how and where data for a torrent is stored. The default storage implementation uses regular files in the filesystem, mapping the files in the torrent in the way one would assume a torrent is saved to disk. Implementing your own storage interface makes it possible to store all data in RAM, or in some optimized order on disk (the order the pieces are received for instance), or saving multi file torrents in a single file in order to be able to take advantage of optimized disk-I/O.

It is also possible to write a thin class that uses the default storage but modifies some particular behavior, for instance encrypting the data before it's written to disk, and decrypting it when it's read again.

The storage interface is based on pieces. Avery read and write operation happens in the piece-space. Each piece fits 'piece_size' number of bytes. All access is done by writing and reading whole or partial pieces.

libtorrent comes with two built-in storage implementations; default_storage and disabled_storage. Their constructor functions are called default_storage_constructor() and disabled_storage_constructor respectively. The disabled storage does just what it sounds like. It throws away data that's written, and it reads garbage. It's useful mostly for benchmarking and profiling purpose.

struct storage_interface
{
   virtual void initialize (storage_error& ec) = 0;
   virtual int writev (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) = 0;
   virtual int readv (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) = 0;
   virtual bool has_any_file (storage_error& ec) = 0;
   virtual void set_file_priority (std::vector<boost::uint8_t>& prio
      , storage_error& ec) = 0;
   virtual int move_storage (std::string const& save_path, int flags
      , storage_error& ec) = 0;
   virtual bool verify_resume_data (bdecode_node const& rd
      , std::vector<std::string> const* links
      , storage_error& ec) = 0;
   virtual void write_resume_data (entry& rd, storage_error& ec) const = 0;
   virtual void release_files (storage_error& ec) = 0;
   virtual void rename_file (int index, std::string const& new_filename
      , storage_error& ec) = 0;
   virtual void delete_files (int options, storage_error& ec) = 0;
   virtual bool tick ();
   aux::session_settings const& settings () const;

   aux::session_settings* m_settings;
};

initialize()

virtual void initialize (storage_error& ec) = 0;

This function is called when the storage on disk is to be initialized. The default storage will create directories and empty files at this point. If allocate_files is true, it will also ftruncate all files to their target size.

This function may be called multiple time on a single instance. When a torrent is force-rechecked, the storage is re-initialized to trigger the re-check from scratch.

The function is not necessarily called before other member functions. For instance has_any_files() and verify_resume_data() are called early to determine whether we may have to check all files or not. If we're doing a full check of the files every piece will be hashed, causing readv() to be called as well.

Any required internals that need initialization should be done in the constructor. This function is called before the torrent starts to download.

If an error occurs, storage_error should be set to reflect it.

writev() readv()

virtual int writev (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) = 0;
virtual int readv (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) = 0;

These functions should read and write the data in or to the given piece at the given offset. It should read or write num_bufs buffers sequentially, where the size of each buffer is specified in the buffer array bufs. The file::iovec_t type has the following members:

struct iovec_t { void* iov_base; size_t iov_len; };

These functions may be called simultaneously from multiple threads. Make sure they are thread safe. The file in libtorrent is thread safe when it can fall back to pread, preadv or the windows equivalents. On targets where read operations cannot be thread safe (i.e one has to seek first and then read), only one disk thread is used.

Every buffer in bufs can be assumed to be page aligned and be of a page aligned size, except for the last buffer of the torrent. The allocated buffer can be assumed to fit a fully page aligned number of bytes though. This is useful when reading and writing the last piece of a file in unbuffered mode.

The offset is aligned to 16 kiB boundaries most of the time, but there are rare exceptions when it's not. Specifically if the read cache is disabled/or full and a peer requests unaligned data. Most clients request aligned data.

The number of bytes read or written should be returned, or -1 on error. If there's an error, the storage_error must be filled out to represent the error that occurred.

has_any_file()

virtual bool has_any_file (storage_error& ec) = 0;

This function is called when first checking (or re-checking) the storage for a torrent. It should return true if any of the files that is used in this storage exists on disk. If so, the storage will be checked for existing pieces before starting the download.

If an error occurs, storage_error should be set to reflect it.

set_file_priority()

virtual void set_file_priority (std::vector<boost::uint8_t>& prio
      , storage_error& ec) = 0;

change the priorities of files. This is a fenced job and is guaranteed to be the only running function on this storage when called

move_storage()

virtual int move_storage (std::string const& save_path, int flags
      , storage_error& ec) = 0;

This function should move all the files belonging to the storage to the new save_path. The default storage moves the single file or the directory of the torrent.

Before moving the files, any open file handles may have to be closed, like release_files().

If an error occurs, storage_error should be set to reflect it.

returns one of: | no_error = 0 | fatal_disk_error = -1 | need_full_check = -2 | file_exist = -4

verify_resume_data()

virtual bool verify_resume_data (bdecode_node const& rd
      , std::vector<std::string> const* links
      , storage_error& ec) = 0;

This function should verify the resume data rd with the files on disk. If the resume data seems to be up-to-date, return true. If not, set error to a description of what mismatched and return false.

The default storage may compare file sizes and time stamps of the files.

If an error occurs, storage_error should be set to reflect it.

This function should verify the resume data rd with the files on disk. If the resume data seems to be up-to-date, return true. If not, set error to a description of what mismatched and return false.

If the links pointer is non-null, it has the same number of elements as there are files. Each element is either empty or contains the absolute path to a file identical to the corresponding file in this torrent. The storage must create hard links (or copy) those files. If any file does not exist or is inaccessible, the disk job must fail.

write_resume_data()

virtual void write_resume_data (entry& rd, storage_error& ec) const = 0;

This function should fill in resume data, the current state of the storage, in rd. The default storage adds file timestamps and sizes.

Returning true indicates an error occurred.

If an error occurs, storage_error should be set to reflect it.

release_files()

virtual void release_files (storage_error& ec) = 0;

This function should release all the file handles that it keeps open to files belonging to this storage. The default implementation just calls file_pool::release_files().

If an error occurs, storage_error should be set to reflect it.

rename_file()

virtual void rename_file (int index, std::string const& new_filename
      , storage_error& ec) = 0;

Rename the file with index file to name new_name.

If an error occurs, storage_error should be set to reflect it.

delete_files()

virtual void delete_files (int options, storage_error& ec) = 0;

This function should delete some or all of the storage for this torrent. The options parameter specifies whether to delete all files or just the partfile. options are set to the same value as the options passed to session::remove_torrent().

If an error occurs, storage_error should be set to reflect it.

The disk_buffer_pool is used to allocate and free disk buffers. It has the following members:

struct disk_buffer_pool : boost::noncopyable
{
        char* allocate_buffer(char const* category);
        void free_buffer(char* buf);

        char* allocate_buffers(int blocks, char const* category);
        void free_buffers(char* buf, int blocks);

        int block_size() const { return m_block_size; }

        void release_memory();
};

tick()

virtual bool tick ();

called periodically (useful for deferred flushing). When returning false, it means no more ticks are necessary. Any disk job submitted will re-enable ticking. The default will always turn ticking back off again.

settings()

aux::session_settings const& settings () const;

access global session_settings

m_settings
initialized in disk_io_thread::perform_async_job

default_storage

Declared in "libtorrent/storage.hpp"

The default implementation of storage_interface. Behaves as a normal bittorrent client. It is possible to derive from this class in order to override some of its behavior, when implementing a custom storage.

class default_storage : public storage_interface, boost::noncopyable
{
   default_storage (storage_params const& params);
   virtual int move_storage (std::string const& save_path, int flags
      , storage_error& ec) override;
   virtual bool verify_resume_data (bdecode_node const& rd
      , std::vector<std::string> const* links
      , storage_error& error) override;
   virtual void initialize (storage_error& ec) override;
   virtual bool tick () override;
   virtual void delete_files (int options, storage_error& ec) override;
   virtual void set_file_priority (std::vector<boost::uint8_t>& prio
      , storage_error& ec) override;
   virtual void rename_file (int index, std::string const& new_filename
      , storage_error& ec) override;
   virtual void write_resume_data (entry& rd, storage_error& ec) const override;
   virtual void release_files (storage_error& ec) override;
   virtual bool has_any_file (storage_error& ec) override;
   int readv (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) override;
   int writev (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) override;
   file_storage const& files () const;
   static void disk_write_access_log (bool enable);
   static bool disk_write_access_log ();
};

default_storage()

default_storage (storage_params const& params);

constructs the default_storage based on the give file_storage (fs). mapped is an optional argument (it may be NULL). If non-NULL it represents the file mapping that have been made to the torrent before adding it. That's where files are supposed to be saved and looked for on disk. save_path is the root save folder for this torrent. file_pool is the cache of file handles that the storage will use. All files it opens will ask the file_pool to open them. file_prio is a vector indicating the priority of files on startup. It may be an empty vector. Any file whose index is not represented by the vector (because the vector is too short) are assumed to have priority 1. this is used to treat files with priority 0 slightly differently.

files()

file_storage const& files () const;

if the files in this storage are mapped, returns the mapped file_storage, otherwise returns the original file_storage object.

file_pool

Declared in "libtorrent/file_pool.hpp"

this is an internal cache of open file handles. It's primarily used by storage_interface implementations. It provides semi weak guarantees of not opening more file handles than specified. Given multiple threads, each with the ability to lock a file handle (via smart pointer), there may be windows where more file handles are open.

struct file_pool : boost::noncopyable
{
   ~file_pool ();
   file_pool (int size = 40);
   file_handle open_file (void* st, std::string const& p
      , int file_index, file_storage const& fs, int m, error_code& ec);
   void release (void* st = NULL);
   void release (void* st, int file_index);
   void resize (int size);
   int size_limit () const;
   void close_oldest ();
};

~file_pool() file_pool()

~file_pool ();
file_pool (int size = 40);

size specifies the number of allowed files handles to hold open at any given time.

open_file()

file_handle open_file (void* st, std::string const& p
      , int file_index, file_storage const& fs, int m, error_code& ec);

return an open file handle to file at file_index in the file_storage fs opened at save path p. m is the file open mode (see file::open_mode_t).

release()

void release (void* st = NULL);
void release (void* st, int file_index);

release all files belonging to the specified storage_interface (st) the overload that takes file_index releases only the file with that index in storage st.

resize()

void resize (int size);

update the allowed number of open file handles to size.

size_limit()

int size_limit () const;

returns the current limit of number of allowed open file handles held by the file_pool.

close_oldest()

void close_oldest ();

close the file that was opened least recently (i.e. not accessed least recently). The purpose is to make the OS (really just windows) clear and flush its disk cache associated with this file. We don't want any file to stay open for too long, allowing the disk cache to accrue.

disk_buffer_holder

Declared in "libtorrent/disk_buffer_holder.hpp"

The disk buffer holder acts like a scoped_ptr that frees a disk buffer when it's destructed, unless it's released. release returns the disk buffer and transfers ownership and responsibility to free it to the caller.

A disk buffer is freed by passing it to session_impl::free_disk_buffer().

get() returns the pointer without transferring responsibility. If this buffer has been released, buffer() will return 0.

struct disk_buffer_holder
{
   disk_buffer_holder (buffer_allocator_interface& alloc, disk_io_job const& j);
   ~disk_buffer_holder ();
   char* release ();
   char* get () const;
   void reset (char* buf = 0);
   void reset (disk_io_job const& j);
   void swap (disk_buffer_holder& h);
   block_cache_reference ref () const;
};

disk_buffer_holder()

disk_buffer_holder (buffer_allocator_interface& alloc, disk_io_job const& j);

construct a buffer holder that will free the held buffer using a disk buffer pool directly (there's only one disk_buffer_pool per session)

~disk_buffer_holder()

~disk_buffer_holder ();

frees any unreleased disk buffer held by this object

release()

char* release ();

return the held disk buffer and clear it from the holder. The responsibility to free it is passed on to the caller

get()

char* get () const;

return a pointer to the held buffer

reset()

void reset (char* buf = 0);
void reset (disk_io_job const& j);

set the holder object to hold the specified buffer (or NULL by default). If it's already holding a disk buffer, it will first be freed.

swap()

void swap (disk_buffer_holder& h);

swap pointers of two disk buffer holders.

enum move_flags_t

Declared in "libtorrent/storage.hpp"

name value description
always_replace_files 0 replace any files in the destination when copying or moving the storage
fail_if_exist 1 if any files that we want to copy exist in the destination exist, fail the whole operation and don't perform any copy or move. There is an inherent race condition in this mode. The files are checked for existence before the operation starts. In between the check and performing the copy, the destination files may be created, in which case they are replaced.
dont_replace 2 if any file exist in the target, take those files instead of the ones we may have in the source.
libtorrent-rasterbar-1.1.13/docs/reference-Error_Codes.html000066400000000000000000001170101351156116000237230ustar00rootroot00000000000000 reference-Error_Codes.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Error Codes

libtorrent_exception

Declared in "libtorrent/error_code.hpp"

struct libtorrent_exception : std::exception
{
   libtorrent_exception (libtorrent_exception const&) = default;
   virtual const char* what () const TORRENT_EXCEPTION_THROW_SPECIFIER;
   virtual ~libtorrent_exception () TORRENT_EXCEPTION_THROW_SPECIFIER;
   libtorrent_exception& operator= (libtorrent_exception const&) = default;
   libtorrent_exception (error_code const& s);
   error_code error () const;
};

storage_error

Declared in "libtorrent/error_code.hpp"

used by storage to return errors also includes which underlying file the error happened on

struct storage_error
{
   storage_error (error_code e);
   storage_error ();
   operator bool () const;
   char const* operation_str () const;

   error_code ec;
   boost::int32_t file:24;
   boost::uint32_t operation:8;
};

operation_str()

char const* operation_str () const;

Returns a string literal representing the file operation that failed. If there were no failure, it returns an empty string.

ec
the error that occurred
file
the file the error occurred on
operation
A code from file_operation_t enum, indicating what kind of operation failed.

libtorrent_category()

Declared in "libtorrent/error_code.hpp"

boost::system::error_category& libtorrent_category ();

return the instance of the libtorrent_error_category which maps libtorrent error codes to human readable error messages.

http_category()

Declared in "libtorrent/error_code.hpp"

boost::system::error_category& http_category ();

returns the error_category for HTTP errors

upnp_category()

Declared in "libtorrent/upnp.hpp"

boost::system::error_category& upnp_category ();

the boost.system error category for UPnP errors

gzip_category()

Declared in "libtorrent/gzip.hpp"

boost::system::error_category& gzip_category ();

get the error_category for zip errors

bdecode_category()

Declared in "libtorrent/bdecode.hpp"

boost::system::error_category& bdecode_category ();

socks_category()

Declared in "libtorrent/socks5_stream.hpp"

boost::system::error_category& socks_category ();

returns the error_category for SOCKS5 errors

i2p_category()

Declared in "libtorrent/i2p_stream.hpp"

boost::system::error_category& i2p_category ();

returns the error category for I2P errors

enum error_code_enum

Declared in "libtorrent/error_code.hpp"

name value description
no_error 0 Not an error
file_collision 1 Two torrents has files which end up overwriting each other
failed_hash_check 2 A piece did not match its piece hash
torrent_is_no_dict 3 The .torrent file does not contain a bencoded dictionary at its top level
torrent_missing_info 4 The .torrent file does not have an info dictionary
torrent_info_no_dict 5 The .torrent file's info entry is not a dictionary
torrent_missing_piece_length 6 The .torrent file does not have a piece length entry
torrent_missing_name 7 The .torrent file does not have a name entry
torrent_invalid_name 8 The .torrent file's name entry is invalid
torrent_invalid_length 9 The length of a file, or of the whole .torrent file is invalid. Either negative or not an integer
torrent_file_parse_failed 10 Failed to parse a file entry in the .torrent
torrent_missing_pieces 11 The pieces field is missing or invalid in the .torrent file
torrent_invalid_hashes 12 The pieces string has incorrect length
too_many_pieces_in_torrent 13 The .torrent file has more pieces than is supported by libtorrent
invalid_swarm_metadata 14 The metadata (.torrent file) that was received from the swarm matched the info-hash, but failed to be parsed
invalid_bencoding 15 The file or buffer is not correctly bencoded
no_files_in_torrent 16 The .torrent file does not contain any files
invalid_escaped_string 17 The string was not properly url-encoded as expected
session_is_closing 18 Operation is not permitted since the session is shutting down
duplicate_torrent 19 There's already a torrent with that info-hash added to the session
invalid_torrent_handle 20 The supplied torrent_handle is not referring to a valid torrent
invalid_entry_type 21 The type requested from the entry did not match its type
missing_info_hash_in_uri 22 The specified URI does not contain a valid info-hash
file_too_short 23 One of the files in the torrent was unexpectedly small. This might be caused by files being changed by an external process
unsupported_url_protocol 24 The URL used an unknown protocol. Currently http and https (if built with openssl support) are recognized. For trackers udp is recognized as well.
url_parse_error 25 The URL did not conform to URL syntax and failed to be parsed
peer_sent_empty_piece 26 The peer sent a 'piece' message of length 0
parse_failed 27 A bencoded structure was corrupt and failed to be parsed
invalid_file_tag 28 The fast resume file was missing or had an invalid file version tag
missing_info_hash 29 The fast resume file was missing or had an invalid info-hash
mismatching_info_hash 30 The info-hash did not match the torrent
invalid_hostname 31 The URL contained an invalid hostname
invalid_port 32 The URL had an invalid port
port_blocked 33 The port is blocked by the port-filter, and prevented the connection
expected_close_bracket_in_address 34 The IPv6 address was expected to end with ']'
destructing_torrent 35 The torrent is being destructed, preventing the operation to succeed
timed_out 36 The connection timed out
upload_upload_connection 37 The peer is upload only, and we are upload only. There's no point in keeping the connection
uninteresting_upload_peer 38 The peer is upload only, and we're not interested in it. There's no point in keeping the connection
invalid_info_hash 39 The peer sent an unknown info-hash
torrent_paused 40 The torrent is paused, preventing the operation from succeeding
invalid_have 41 The peer sent an invalid have message, either wrong size or referring to a piece that doesn't exist in the torrent
invalid_bitfield_size 42 The bitfield message had the incorrect size
too_many_requests_when_choked 43 The peer kept requesting pieces after it was choked, possible abuse attempt.
invalid_piece 44 The peer sent a piece message that does not correspond to a piece request sent by the client
no_memory 45 memory allocation failed
torrent_aborted 46 The torrent is aborted, preventing the operation to succeed
self_connection 47 The peer is a connection to ourself, no point in keeping it
invalid_piece_size 48 The peer sent a piece message with invalid size, either negative or greater than one block
timed_out_no_interest 49 The peer has not been interesting or interested in us for too long, no point in keeping it around
timed_out_inactivity 50 The peer has not said anything in a long time, possibly dead
timed_out_no_handshake 51 The peer did not send a handshake within a reasonable amount of time, it might not be a bittorrent peer
timed_out_no_request 52 The peer has been unchoked for too long without requesting any data. It might be lying about its interest in us
invalid_choke 53 The peer sent an invalid choke message
invalid_unchoke 54 The peer send an invalid unchoke message
invalid_interested 55 The peer sent an invalid interested message
invalid_not_interested 56 The peer sent an invalid not-interested message
invalid_request 57 The peer sent an invalid piece request message
invalid_hash_list 58 The peer sent an invalid hash-list message (this is part of the merkle-torrent extension)
invalid_hash_piece 59 The peer sent an invalid hash-piece message (this is part of the merkle-torrent extension)
invalid_cancel 60 The peer sent an invalid cancel message
invalid_dht_port 61 The peer sent an invalid DHT port-message
invalid_suggest 62 The peer sent an invalid suggest piece-message
invalid_have_all 63 The peer sent an invalid have all-message
invalid_have_none 64 The peer sent an invalid have none-message
invalid_reject 65 The peer sent an invalid reject message
invalid_allow_fast 66 The peer sent an invalid allow fast-message
invalid_extended 67 The peer sent an invalid extension message ID
invalid_message 68 The peer sent an invalid message ID
sync_hash_not_found 69 The synchronization hash was not found in the encrypted handshake
invalid_encryption_constant 70 The encryption constant in the handshake is invalid
no_plaintext_mode 71 The peer does not support plaintext, which is the selected mode
no_rc4_mode 72 The peer does not support rc4, which is the selected mode
unsupported_encryption_mode 73 The peer does not support any of the encryption modes that the client supports
unsupported_encryption_mode_selected 74 The peer selected an encryption mode that the client did not advertise and does not support
invalid_pad_size 75 The pad size used in the encryption handshake is of invalid size
invalid_encrypt_handshake 76 The encryption handshake is invalid
no_incoming_encrypted 77 The client is set to not support incoming encrypted connections and this is an encrypted connection
no_incoming_regular 78 The client is set to not support incoming regular bittorrent connections, and this is a regular connection
duplicate_peer_id 79 The client is already connected to this peer-ID
torrent_removed 80 Torrent was removed
packet_too_large 81 The packet size exceeded the upper sanity check-limit
reserved 82  
http_error 83 The web server responded with an error
missing_location 84 The web server response is missing a location header
invalid_redirection 85 The web seed redirected to a path that no longer matches the .torrent directory structure
redirecting 86 The connection was closed because it redirected to a different URL
invalid_range 87 The HTTP range header is invalid
no_content_length 88 The HTTP response did not have a content length
banned_by_ip_filter 89 The IP is blocked by the IP filter
too_many_connections 90 At the connection limit
peer_banned 91 The peer is marked as banned
stopping_torrent 92 The torrent is stopping, causing the operation to fail
too_many_corrupt_pieces 93 The peer has sent too many corrupt pieces and is banned
torrent_not_ready 94 The torrent is not ready to receive peers
peer_not_constructed 95 The peer is not completely constructed yet
session_closing 96 The session is closing, causing the operation to fail
optimistic_disconnect 97 The peer was disconnected in order to leave room for a potentially better peer
torrent_finished 98 The torrent is finished
no_router 99 No UPnP router found
metadata_too_large 100 The metadata message says the metadata exceeds the limit
invalid_metadata_request 101 The peer sent an invalid metadata request message
invalid_metadata_size 102 The peer advertised an invalid metadata size
invalid_metadata_offset 103 The peer sent a message with an invalid metadata offset
invalid_metadata_message 104 The peer sent an invalid metadata message
pex_message_too_large 105 The peer sent a peer exchange message that was too large
invalid_pex_message 106 The peer sent an invalid peer exchange message
invalid_lt_tracker_message 107 The peer sent an invalid tracker exchange message
too_frequent_pex 108 The peer sent an pex messages too often. This is a possible attempt of and attack
no_metadata 109 The operation failed because it requires the torrent to have the metadata (.torrent file) and it doesn't have it yet. This happens for magnet links before they have downloaded the metadata, and also torrents added by URL.
invalid_dont_have 110 The peer sent an invalid dont_have message. The don't have message is an extension to allow peers to advertise that the no longer has a piece they previously had.
requires_ssl_connection 111 The peer tried to connect to an SSL torrent without connecting over SSL.
invalid_ssl_cert 112 The peer tried to connect to a torrent with a certificate for a different torrent.
not_an_ssl_torrent 113 the torrent is not an SSL torrent, and the operation requires an SSL torrent
banned_by_port_filter 114 peer was banned because its listen port is within a banned port range, as specified by the port_filter.
unsupported_protocol_version 120 The NAT-PMP router responded with an unsupported protocol version
natpmp_not_authorized 121 You are not authorized to map ports on this NAT-PMP router
network_failure 122 The NAT-PMP router failed because of a network failure
no_resources 123 The NAT-PMP router failed because of lack of resources
unsupported_opcode 124 The NAT-PMP router failed because an unsupported opcode was sent
missing_file_sizes 130 The resume data file is missing the 'file sizes' entry
no_files_in_resume_data 131 The resume data file 'file sizes' entry is empty
missing_pieces 132 The resume data file is missing the 'pieces' and 'slots' entry
mismatching_number_of_files 133 The number of files in the resume data does not match the number of files in the torrent
mismatching_file_size 134 One of the files on disk has a different size than in the fast resume file
mismatching_file_timestamp 135 One of the files on disk has a different timestamp than in the fast resume file
not_a_dictionary 136 The resume data file is not a dictionary
invalid_blocks_per_piece 137 The 'blocks per piece' entry is invalid in the resume data file
missing_slots 138 The resume file is missing the 'slots' entry, which is required for torrents with compact allocation. DEPRECATED
too_many_slots 139 The resume file contains more slots than the torrent
invalid_slot_list 140 The 'slot' entry is invalid in the resume data
invalid_piece_index 141 One index in the 'slot' list is invalid
pieces_need_reorder 142 The pieces on disk needs to be re-ordered for the specified allocation mode. This happens if you specify sparse allocation and the files on disk are using compact storage. The pieces needs to be moved to their right position. DEPRECATED
resume_data_not_modified 143 this error is returned when asking to save resume data and specifying the flag to only save when there's anything new to save (torrent_handle::only_if_modified) and there wasn't anything changed.
http_parse_error 150 The HTTP header was not correctly formatted
http_missing_location 151 The HTTP response was in the 300-399 range but lacked a location header
http_failed_decompress 152 The HTTP response was encoded with gzip or deflate but decompressing it failed
no_i2p_router 160 The URL specified an i2p address, but no i2p router is configured
no_i2p_endpoint 161 i2p acceptor is not available yet, can't announce without endpoint
scrape_not_available 170 The tracker URL doesn't support transforming it into a scrape URL. i.e. it doesn't contain "announce.
invalid_tracker_response 171 invalid tracker response
invalid_peer_dict 172 invalid peer dictionary entry. Not a dictionary
tracker_failure 173 tracker sent a failure message
invalid_files_entry 174 missing or invalid 'files' entry
invalid_hash_entry 175 missing or invalid 'hash' entry
invalid_peers_entry 176 missing or invalid 'peers' and 'peers6' entry
invalid_tracker_response_length 177 udp tracker response packet has invalid size
invalid_tracker_transaction_id 178 invalid transaction id in udp tracker response
invalid_tracker_action 179 invalid action field in udp tracker response
error_code_max 180 the number of error codes

enum http_errors

Declared in "libtorrent/error_code.hpp"

name value description
cont 100  
ok 200  
created 201  
accepted 202  
no_content 204  
multiple_choices 300  
moved_permanently 301  
moved_temporarily 302  
not_modified 304  
bad_request 400  
unauthorized 401  
forbidden 403  
not_found 404  
internal_server_error 500  
not_implemented 501  
bad_gateway 502  
service_unavailable 503  

enum error_code_enum

Declared in "libtorrent/upnp.hpp"

name value description
no_error 0 No error
invalid_argument 402 One of the arguments in the request is invalid
action_failed 501 The request failed
value_not_in_array 714 The specified value does not exist in the array
source_ip_cannot_be_wildcarded 715 The source IP address cannot be wild-carded, but must be fully specified
external_port_cannot_be_wildcarded 716 The external port cannot be wildcarded, but must be specified
port_mapping_conflict 718 The port mapping entry specified conflicts with a mapping assigned previously to another client
internal_port_must_match_external 724 Internal and external port value must be the same
only_permanent_leases_supported 725 The NAT implementation only supports permanent lease times on port mappings
remote_host_must_be_wildcard 726 RemoteHost must be a wildcard and cannot be a specific IP addres or DNS name
external_port_must_be_wildcard 727 ExternalPort must be a wildcard and cannot be a specific port

enum error_code_enum

Declared in "libtorrent/gzip.hpp"

name value description
no_error 0 Not an error
invalid_gzip_header 1 the supplied gzip buffer has invalid header
inflated_data_too_large 2 the gzip buffer would inflate to more bytes than the specified maximum size, and was rejected.
data_did_not_terminate 3 available inflate data did not terminate
space_exhausted 4 output space exhausted before completing inflate
invalid_block_type 5 invalid block type (type == 3)
invalid_stored_block_length 6 stored block length did not match one's complement
too_many_length_or_distance_codes 7 dynamic block code description: too many length or distance codes
code_lengths_codes_incomplete 8 dynamic block code description: code lengths codes incomplete
repeat_lengths_with_no_first_length 9 dynamic block code description: repeat lengths with no first length
repeat_more_than_specified_lengths 10 dynamic block code description: repeat more than specified lengths
invalid_literal_length_code_lengths 11 dynamic block code description: invalid literal/length code lengths
invalid_distance_code_lengths 12 dynamic block code description: invalid distance code lengths
invalid_literal_code_in_block 13 invalid literal/length or distance code in fixed or dynamic block
distance_too_far_back_in_block 14 distance is too far back in fixed or dynamic block
unknown_gzip_error 15 an unknown error occurred during gzip inflation
error_code_max 16 the number of error codes

enum error_code_enum

Declared in "libtorrent/bdecode.hpp"

name value description
no_error 0 Not an error
expected_digit 1 expected digit in bencoded string
expected_colon 2 expected colon in bencoded string
unexpected_eof 3 unexpected end of file in bencoded string
expected_value 4 expected value (list, dict, int or string) in bencoded string
depth_exceeded 5 bencoded recursion depth limit exceeded
limit_exceeded 6 bencoded item count limit exceeded
overflow 7 integer overflow
error_code_max 8 the number of error codes

enum socks_error_code

Declared in "libtorrent/socks5_stream.hpp"

name value description
no_error 0  
unsupported_version 1  
unsupported_authentication_method 2  
unsupported_authentication_version 3  
authentication_error 4  
username_required 5  
general_failure 6  
command_not_supported 7  
no_identd 8  
identd_error 9  
num_errors 10  

enum i2p_error_code

Declared in "libtorrent/i2p_stream.hpp"

name value description
no_error 0  
parse_failed 1  
cant_reach_peer 2  
i2p_error 3  
invalid_key 4  
invalid_id 5  
timeout 6  
key_not_found 7  
duplicated_id 8  
num_errors 9  
libtorrent-rasterbar-1.1.13/docs/reference-Filter.html000066400000000000000000000231761351156116000227530ustar00rootroot00000000000000 reference-Filter.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Filter

Table of contents

ip_filter

Declared in "libtorrent/ip_filter.hpp"

The ip_filter class is a set of rules that uniquely categorizes all ip addresses as allowed or disallowed. The default constructor creates a single rule that allows all addresses (0.0.0.0 - 255.255.255.255 for the IPv4 range, and the equivalent range covering all addresses for the IPv6 range).

A default constructed ip_filter does not filter any address.

struct ip_filter
{
   void add_rule (address first, address last, boost::uint32_t flags);
   int access (address const& addr) const;
   filter_tuple_t export_filter () const;

   enum access_flags
   {
      blocked,
   };
};

add_rule()

void add_rule (address first, address last, boost::uint32_t flags);

Adds a rule to the filter. first and last defines a range of ip addresses that will be marked with the given flags. The flags can currently be 0, which means allowed, or ip_filter::blocked, which means disallowed.

precondition: first.is_v4() == last.is_v4() && first.is_v6() == last.is_v6()

postcondition: access(x) == flags for every x in the range [first, last]

This means that in a case of overlapping ranges, the last one applied takes precedence.

access()

int access (address const& addr) const;

Returns the access permissions for the given address (addr). The permission can currently be 0 or ip_filter::blocked. The complexity of this operation is O(log n), where n is the minimum number of non-overlapping ranges to describe the current filter.

export_filter()

filter_tuple_t export_filter () const;

This function will return the current state of the filter in the minimum number of ranges possible. They are sorted from ranges in low addresses to high addresses. Each entry in the returned vector is a range with the access control specified in its flags field.

The return value is a tuple containing two range-lists. One for IPv4 addresses and one for IPv6 addresses.

enum access_flags

Declared in "libtorrent/ip_filter.hpp"

name value description
blocked 1 indicates that IPs in this range should not be connected to nor accepted as incoming connections

port_filter

Declared in "libtorrent/ip_filter.hpp"

the port filter maps non-overlapping port ranges to flags. This is primarily used to indicate whether a range of ports should be connected to or not. The default is to have the full port range (0-65535) set to flag 0.

class port_filter
{
   void add_rule (boost::uint16_t first, boost::uint16_t last, boost::uint32_t flags);
   int access (boost::uint16_t port) const;

   enum access_flags
   {
      blocked,
   };
};

add_rule()

void add_rule (boost::uint16_t first, boost::uint16_t last, boost::uint32_t flags);

set the flags for the specified port range (first, last) to flags overwriting any existing rule for those ports. The range is inclusive, i.e. the port last also has the flag set on it.

access()

int access (boost::uint16_t port) const;

test the specified port (port) for whether it is blocked or not. The returned value is the flags set for this port. see access_flags.

enum access_flags

Declared in "libtorrent/ip_filter.hpp"

name value description
blocked 1 this flag indicates that destination ports in the range should not be connected to
libtorrent-rasterbar-1.1.13/docs/reference-Plugins.html000066400000000000000000001253111351156116000231410ustar00rootroot00000000000000 reference-Plugins.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Plugins

libtorrent has a plugin interface for implementing extensions to the protocol. These can be general extensions for transferring metadata or peer exchange extensions, or it could be used to provide a way to customize the protocol to fit a particular (closed) network.

In short, the plugin interface makes it possible to:

  • register extension messages (sent in the extension handshake), see extensions.
  • add data and parse data from the extension handshake.
  • send extension messages and standard bittorrent messages.
  • override or block the handling of standard bittorrent messages.
  • save and restore state via the session state
  • see all alerts that are posted

a word of caution

Writing your own plugin is a very easy way to introduce serious bugs such as dead locks and race conditions. Since a plugin has access to internal structures it is also quite easy to sabotage libtorrent's operation.

All the callbacks in this interface are called with the main libtorrent thread mutex locked. And they are always called from the libtorrent network thread. In case portions of your plugin are called from other threads, typically the main thread, you cannot use any of the member functions on the internal structures in libtorrent, since those require the mutex to be locked. Furthermore, you would also need to have a mutex on your own shared data within the plugin, to make sure it is not accessed at the same time from the libtorrent thread (through a callback). See boost thread's mutex. If you need to send out a message from another thread, it is advised to use an internal queue, and do the actual sending in tick().

Since the plugin interface gives you easy access to internal structures, it is not supported as a stable API. Plugins should be considered specific to a specific version of libtorrent. Although, in practice the internals mostly don't change that dramatically.

plugin-interface

The plugin interface consists of three base classes that the plugin may implement. These are called plugin, torrent_plugin and peer_plugin. They are found in the <libtorrent/extensions.hpp> header.

These plugins are instantiated for each session, torrent and possibly each peer, respectively.

For plugins that only need per torrent state, it is enough to only implement torrent_plugin and pass a constructor function or function object to session::add_extension() or torrent_handle::add_extension() (if the torrent has already been started and you want to hook in the extension at run-time).

The signature of the function is:

boost::shared_ptr<torrent_plugin> (*)(torrent_handle const&, void*);

The second argument is the userdata passed to session::add_torrent() or torrent_handle::add_extension().

The function should return a boost::shared_ptr<torrent_plugin> which may or may not be 0. If it is a null pointer, the extension is simply ignored for this torrent. If it is a valid pointer (to a class inheriting torrent_plugin), it will be associated with this torrent and callbacks will be made on torrent events.

For more elaborate plugins which require session wide state, you would implement plugin, construct an object (in a boost::shared_ptr) and pass it in to session::add_extension().

custom alerts

Since plugins are running within internal libtorrent threads, one convenient way to communicate with the client is to post custom alerts.

The expected interface of any alert, apart from deriving from the alert base class, looks like this:

static const int alert_type = <unique alert ID>;
virtual int type() const { return alert_type; }

virtual std::string message() const;

virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new name(*this)); }

static const int static_category = <bitmask of alert::category_t flags>;
virtual int category() const { return static_category; }

virtual char const* what() const { return <string literal of the name of this alert>; }

The alert_type is used for the type-checking in alert_cast. It must not collide with any other alert. The built-in alerts in libtorrent will not use alert type IDs greater than user_alert_id. When defining your own alert, make sure it's greater than this constant.

type() is the run-time equivalence of the alert_type.

The message() virtual function is expected to construct a useful string representation of the alert and the event or data it represents. Something convenient to put in a log file for instance.

clone() is used internally to copy alerts. The suggested implementation of simply allocating a new instance as a copy of *this is all that's expected.

The static category is required for checking whether or not the category for a specific alert is enabled or not, without instantiating the alert. The category virtual function is the run-time equivalence.

The what() virtual function may simply be a string literal of the class name of your alert.

For more information, see the alert section.

plugin

Declared in "libtorrent/extensions.hpp"

this is the base class for a session plugin. One primary feature is that it is notified of all torrents that are added to the session, and can add its own torrent_plugins.

struct plugin
{
   virtual boost::uint32_t implemented_features ();
   virtual boost::shared_ptr<torrent_plugin> new_torrent (torrent_handle const&, void*);
   virtual void added (session_handle);
   virtual void register_dht_extensions (dht_extensions_t&);
   virtual void on_alert (alert const*);
   virtual bool on_unknown_torrent (sha1_hash const& /* info_hash */
      , peer_connection_handle const& /* pc */, add_torrent_params& /* p */);
   virtual void on_tick ();
   virtual bool on_optimistic_unchoke (std::vector<peer_connection_handle>& /* peers */);
   virtual void save_state (entry&) const;
   virtual void load_state (bdecode_node const&);

   enum feature_flags_t
   {
      optimistic_unchoke_feature,
      tick_feature,
   };
};

implemented_features()

virtual boost::uint32_t implemented_features ();

This function is expected to return a bitmask indicating which features this plugin implements. Some callbacks on this object may not be called unless the corresponding feature flag is returned here. Note that callbacks may still be called even if the corresponding feature is not specified in the return value here. See feature_flags_t for possible flags to return.

new_torrent()

virtual boost::shared_ptr<torrent_plugin> new_torrent (torrent_handle const&, void*);

this is called by the session every time a new torrent is added. The torrent* points to the internal torrent object created for the new torrent. The void* is the userdata pointer as passed in via add_torrent_params.

If the plugin returns a torrent_plugin instance, it will be added to the new torrent. Otherwise, return an empty shared_ptr to a torrent_plugin (the default).

added()

virtual void added (session_handle);

called when plugin is added to a session

register_dht_extensions()

virtual void register_dht_extensions (dht_extensions_t&);

called after a plugin is added allows the plugin to register DHT requests it would like to handle

on_alert()

virtual void on_alert (alert const*);

called when an alert is posted alerts that are filtered are not posted

on_unknown_torrent()

virtual bool on_unknown_torrent (sha1_hash const& /* info_hash */
      , peer_connection_handle const& /* pc */, add_torrent_params& /* p */);

return true if the add_torrent_params should be added

on_tick()

virtual void on_tick ();

called once per second

on_optimistic_unchoke()

virtual bool on_optimistic_unchoke (std::vector<peer_connection_handle>& /* peers */);

called when choosing peers to optimistically unchoke. peer's will be unchoked in the order they appear in the given vector. if the plugin returns true then the ordering provided will be used and no other plugin will be allowed to change it. If your plugin expects this to be called, make sure to include the flag optimistic_unchoke_feature in the return value from implemented_features().

save_state()

virtual void save_state (entry&) const;

called when saving settings state

load_state()

virtual void load_state (bdecode_node const&);

called when loading settings state

enum feature_flags_t

Declared in "libtorrent/extensions.hpp"

name value description
optimistic_unchoke_feature 1 include this bit if your plugin needs to alter the order of the optimistic unchoke of peers. i.e. have the on_optimistic_unchoke() callback be called.
tick_feature 2 include this bit if your plugin needs to have on_tick() called

torrent_plugin

Declared in "libtorrent/extensions.hpp"

Torrent plugins are associated with a single torrent and have a number of functions called at certain events. Many of its functions have the ability to change or override the default libtorrent behavior.

struct torrent_plugin
{
   virtual boost::shared_ptr<peer_plugin> new_connection (peer_connection_handle const&);
   virtual void on_piece_pass (int /*index*/);
   virtual void on_piece_failed (int /*index*/);
   virtual void tick ();
   virtual bool on_resume ();
   virtual bool on_pause ();
   virtual void on_files_checked ();
   virtual void on_state (int /*s*/);
   virtual void on_unload ();
   virtual void on_load ();
   virtual void on_add_peer (tcp::endpoint const&,
      int /*src*/, int /*flags*/);
};

new_connection()

virtual boost::shared_ptr<peer_plugin> new_connection (peer_connection_handle const&);

This function is called each time a new peer is connected to the torrent. You may choose to ignore this by just returning a default constructed shared_ptr (in which case you don't need to override this member function).

If you need an extension to the peer connection (which most plugins do) you are supposed to return an instance of your peer_plugin class. Which in turn will have its hook functions called on event specific to that peer.

The peer_connection_handle will be valid as long as the shared_ptr is being held by the torrent object. So, it is generally a good idea to not keep a shared_ptr to your own peer_plugin. If you want to keep references to it, use weak_ptr.

If this function throws an exception, the connection will be closed.

on_piece_failed() on_piece_pass()

virtual void on_piece_pass (int /*index*/);
virtual void on_piece_failed (int /*index*/);

These hooks are called when a piece passes the hash check or fails the hash check, respectively. The index is the piece index that was downloaded. It is possible to access the list of peers that participated in sending the piece through the torrent and the piece_picker.

tick()

virtual void tick ();

This hook is called approximately once per second. It is a way of making it easy for plugins to do timed events, for sending messages or whatever.

on_resume() on_pause()

virtual bool on_resume ();
virtual bool on_pause ();

These hooks are called when the torrent is paused and resumed respectively. The return value indicates if the event was handled. A return value of true indicates that it was handled, and no other plugin after this one will have this hook function called, and the standard handler will also not be invoked. So, returning true effectively overrides the standard behavior of pause or resume.

Note that if you call pause() or resume() on the torrent from your handler it will recurse back into your handler, so in order to invoke the standard handler, you have to keep your own state on whether you want standard behavior or overridden behavior.

on_files_checked()

virtual void on_files_checked ();

This function is called when the initial files of the torrent have been checked. If there are no files to check, this function is called immediately.

i.e. This function is always called when the torrent is in a state where it can start downloading.

on_state()

virtual void on_state (int /*s*/);

called when the torrent changes state the state is one of torrent_status::state_t enum members

on_unload() on_load()

virtual void on_unload ();
virtual void on_load ();

called when the torrent is unloaded from RAM and loaded again, respectively unload is called right before the torrent is unloaded and load is called right after it's loaded. i.e. the full torrent state is available when these callbacks are called.

on_add_peer()

virtual void on_add_peer (tcp::endpoint const&,
      int /*src*/, int /*flags*/);

called every time a new peer is added to the peer list. This is before the peer is connected to. For flags, see torrent_plugin::flags_t. The source argument refers to the source where we learned about this peer from. It's a bitmask, because many sources may have told us about the same peer. For peer source flags, see peer_info::peer_source_flags.

peer_plugin

Declared in "libtorrent/extensions.hpp"

peer plugins are associated with a specific peer. A peer could be both a regular bittorrent peer (bt_peer_connection) or one of the web seed connections (web_peer_connection or http_seed_connection). In order to only attach to certain peers, make your torrent_plugin::new_connection only return a plugin for certain peer connection types

struct peer_plugin
{
   virtual char const* type () const;
   virtual void add_handshake (entry&);
   virtual void on_disconnect (error_code const& /*ec*/);
   virtual void on_connected ();
   virtual bool on_handshake (char const* /*reserved_bits*/);
   virtual bool on_extension_handshake (bdecode_node const&);
   virtual bool on_have (int /*index*/);
   virtual bool on_bitfield (bitfield const& /*bitfield*/);
   virtual bool on_have_all ();
   virtual bool on_reject (peer_request const&);
   virtual bool on_request (peer_request const&);
   virtual bool on_unchoke ();
   virtual bool on_interested ();
   virtual bool on_allowed_fast (int /*index*/);
   virtual bool on_have_none ();
   virtual bool on_choke ();
   virtual bool on_not_interested ();
   virtual bool on_piece (peer_request const& /*piece*/
      , disk_buffer_holder& /*data*/);
   virtual bool on_suggest (int /*index*/);
   virtual bool on_cancel (peer_request const&);
   virtual bool on_dont_have (int /*index*/);
   virtual void sent_unchoke ();
   virtual void sent_payload (int /* bytes */);
   virtual bool can_disconnect (error_code const& /*ec*/);
   virtual bool on_extended (int /*length*/, int /*msg*/,
      buffer::const_interval /*body*/);
   virtual bool on_unknown_message (int /*length*/, int /*msg*/,
      buffer::const_interval /*body*/);
   virtual void on_piece_pass (int /*index*/);
   virtual void on_piece_failed (int /*index*/);
   virtual void tick ();
   virtual bool write_request (peer_request const&);
};

type()

virtual char const* type () const;

This function is expected to return the name of the plugin.

add_handshake()

virtual void add_handshake (entry&);

can add entries to the extension handshake this is not called for web seeds

on_disconnect()

virtual void on_disconnect (error_code const& /*ec*/);

called when the peer is being disconnected.

on_connected()

virtual void on_connected ();

called when the peer is successfully connected. Note that incoming connections will have been connected by the time the peer plugin is attached to it, and won't have this hook called.

on_handshake()

virtual bool on_handshake (char const* /*reserved_bits*/);

this is called when the initial bittorrent handshake is received. Returning false means that the other end doesn't support this extension and will remove it from the list of plugins. this is not called for web seeds

on_extension_handshake()

virtual bool on_extension_handshake (bdecode_node const&);

called when the extension handshake from the other end is received if this returns false, it means that this extension isn't supported by this peer. It will result in this peer_plugin being removed from the peer_connection and destructed. this is not called for web seeds

on_bitfield() on_have_none() on_suggest() on_unchoke() on_cancel() on_have() on_choke() on_piece() on_request() on_reject() on_not_interested() on_interested() on_allowed_fast() on_have_all() on_dont_have()

virtual bool on_have (int /*index*/);
virtual bool on_bitfield (bitfield const& /*bitfield*/);
virtual bool on_have_all ();
virtual bool on_reject (peer_request const&);
virtual bool on_request (peer_request const&);
virtual bool on_unchoke ();
virtual bool on_interested ();
virtual bool on_allowed_fast (int /*index*/);
virtual bool on_have_none ();
virtual bool on_choke ();
virtual bool on_not_interested ();
virtual bool on_piece (peer_request const& /*piece*/
      , disk_buffer_holder& /*data*/);
virtual bool on_suggest (int /*index*/);
virtual bool on_cancel (peer_request const&);
virtual bool on_dont_have (int /*index*/);

returning true from any of the message handlers indicates that the plugin has handled the message. it will break the plugin chain traversing and not let anyone else handle the message, including the default handler.

sent_unchoke()

virtual void sent_unchoke ();

called after a choke message has been sent to the peer

sent_payload()

virtual void sent_payload (int /* bytes */);

called after piece data has been sent to the peer this can be used for stats book keeping

can_disconnect()

virtual bool can_disconnect (error_code const& /*ec*/);

called when libtorrent think this peer should be disconnected. if the plugin returns false, the peer will not be disconnected.

on_extended()

virtual bool on_extended (int /*length*/, int /*msg*/,
      buffer::const_interval /*body*/);

called when an extended message is received. If returning true, the message is not processed by any other plugin and if false is returned the next plugin in the chain will receive it to be able to handle it. This is not called for web seeds. thus function may be called more than once per incoming message, but only the last of the calls will the body size equal the length. i.e. Every time another fragment of the message is received, this function will be called, until finally the whole message has been received. The purpose of this is to allow early disconnects for invalid messages and for reporting progress of receiving large messages.

on_unknown_message()

virtual bool on_unknown_message (int /*length*/, int /*msg*/,
      buffer::const_interval /*body*/);

this is not called for web seeds

on_piece_failed() on_piece_pass()

virtual void on_piece_pass (int /*index*/);
virtual void on_piece_failed (int /*index*/);

called when a piece that this peer participated in either fails or passes the hash_check

tick()

virtual void tick ();

called approximately once every second

write_request()

virtual bool write_request (peer_request const&);

called each time a request message is to be sent. If true is returned, the original request message won't be sent and no other plugin will have this function called.

crypto_plugin

Declared in "libtorrent/extensions.hpp"

struct crypto_plugin
{
   virtual void set_incoming_key (unsigned char const* key, int len) = 0;
   virtual void set_outgoing_key (unsigned char const* key, int len) = 0;
   virtual int encrypt (std::vector<boost::asio::mutable_buffer>& /*send_vec*/) = 0;
   virtual void decrypt (std::vector<boost::asio::mutable_buffer>& /*receive_vec*/
      , int& /* consume */, int& /*produce*/, int& /*packet_size*/) = 0;
};

encrypt()

virtual int encrypt (std::vector<boost::asio::mutable_buffer>& /*send_vec*/) = 0;

encrypted the provided buffers and returns the number of bytes which are now ready to be sent to the lower layer. This must be at least as large as the number of bytes passed in and may be larger if there is additional data to be inserted at the head of the send buffer. The additional data is retrieved from the passed in vector. The vector must be cleared if no additional data is to be inserted.

decrypt()

virtual void decrypt (std::vector<boost::asio::mutable_buffer>& /*receive_vec*/
      , int& /* consume */, int& /*produce*/, int& /*packet_size*/) = 0;

decrypt the provided buffers. consume is set to the number of bytes which should be trimmed from the head of the buffers, default is 0

produce is set to the number of bytes of payload which are now ready to be sent to the upper layer. default is the number of bytes passed in receive_vec

packet_size is set to the minimum number of bytes which must be read to advance the next step of decryption. default is 0

create_ut_metadata_plugin()

Declared in "libtorrent/extensions/ut_metadata.hpp"

boost::shared_ptr<torrent_plugin> create_ut_metadata_plugin (torrent_handle const&, void*);

constructor function for the ut_metadata extension. The ut_metadata extension allows peers to request the .torrent file (or more specifically the 'info'-dictionary of the .torrent file) from each other. This is the main building block in making magnet links work. This extension is enabled by default unless explicitly disabled in the session constructor.

This can either be passed in the add_torrent_params::extensions field, or via torrent_handle::add_extension().

create_smart_ban_plugin()

Declared in "libtorrent/extensions/smart_ban.hpp"

boost::shared_ptr<torrent_plugin> create_smart_ban_plugin (torrent_handle const&, void*);

constructor function for the smart ban extension. The extension keeps track of the data peers have sent us for failing pieces and once the piece completes and passes the hash check bans the peers that turned out to have sent corrupt data. This function can either be passed in the add_torrent_params::extensions field, or via torrent_handle::add_extension().

create_ut_pex_plugin()

Declared in "libtorrent/extensions/ut_pex.hpp"

boost::shared_ptr<torrent_plugin> create_ut_pex_plugin (torrent_handle const&, void*);

constructor function for the ut_pex extension. The ut_pex extension allows peers to gossip about their connections, allowing the swarm stay well connected and peers aware of more peers in the swarm. This extension is enabled by default unless explicitly disabled in the session constructor.

This can either be passed in the add_torrent_params::extensions field, or via torrent_handle::add_extension().

libtorrent-rasterbar-1.1.13/docs/reference-Settings.html000066400000000000000000007126231351156116000233300ustar00rootroot00000000000000 reference-Settings.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Settings

You have some control over session configuration through the session::apply_settings() member function. To change one or more configuration options, create a settings_pack object and fill it with the settings to be set and pass it in to session::apply_settings().

The settings_pack object is a collection of settings updates that are applied to the session when passed to session::apply_settings(). It's empty when constructed.

You have control over proxy and authorization settings and also the user-agent that will be sent to the tracker. The user-agent will also be used to identify the client with other peers.

dht_settings

Declared in "libtorrent/session_settings.hpp"

structure used to hold configuration options for the DHT

The dht_settings struct used to contain a service_port member to control which port the DHT would listen on and send messages from. This field is deprecated and ignored. libtorrent always tries to open the UDP socket on the same port as the TCP socket.

struct dht_settings
{
   dht_settings ();

   int max_peers_reply;
   int search_branching;
   int max_fail_count;
   int max_torrents;
   int max_dht_items;
   int max_peers;
   int max_torrent_search_reply;
   bool restrict_routing_ips;
   bool restrict_search_ips;
   bool extended_routing_table;
   bool aggressive_lookups;
   bool privacy_lookups;
   bool enforce_node_id;
   bool ignore_dark_internet;
   int block_timeout;
   int block_ratelimit;
   bool read_only;
   int item_lifetime;
};

dht_settings()

dht_settings ();

initialized dht_settings to the default values

max_peers_reply
the maximum number of peers to send in a reply to get_peers
search_branching
the number of concurrent search request the node will send when announcing and refreshing the routing table. This parameter is called alpha in the kademlia paper
max_fail_count
the maximum number of failed tries to contact a node before it is removed from the routing table. If there are known working nodes that are ready to replace a failing node, it will be replaced immediately, this limit is only used to clear out nodes that don't have any node that can replace them.
max_torrents
the total number of torrents to track from the DHT. This is simply an upper limit to make sure malicious DHT nodes cannot make us allocate an unbounded amount of memory.
max_dht_items
max number of items the DHT will store
max_peers
the max number of peers to store per torrent (for the DHT)
max_torrent_search_reply
the max number of torrents to return in a torrent search query to the DHT
restrict_routing_ips

determines if the routing table entries should restrict entries to one per IP. This defaults to true, which helps mitigate some attacks on the DHT. It prevents adding multiple nodes with IPs with a very close CIDR distance.

when set, nodes whose IP address that's in the same /24 (or /64 for IPv6) range in the same routing table bucket. This is an attempt to mitigate node ID spoofing attacks also restrict any IP to only have a single entry in the whole routing table

restrict_search_ips
determines if DHT searches should prevent adding nodes with IPs with very close CIDR distance. This also defaults to true and helps mitigate certain attacks on the DHT.
extended_routing_table
makes the first buckets in the DHT routing table fit 128, 64, 32 and 16 nodes respectively, as opposed to the standard size of 8. All other buckets have size 8 still.
aggressive_lookups
slightly changes the lookup behavior in terms of how many outstanding requests we keep. Instead of having branch factor be a hard limit, we always keep branch factor outstanding requests to the closest nodes. i.e. every time we get results back with closer nodes, we query them right away. It lowers the lookup times at the cost of more outstanding queries.
privacy_lookups
when set, perform lookups in a way that is slightly more expensive, but which minimizes the amount of information leaked about you.
enforce_node_id
when set, node's whose IDs that are not correctly generated based on its external IP are ignored. When a query arrives from such node, an error message is returned with a message saying "invalid node ID".
ignore_dark_internet
ignore DHT messages from parts of the internet we wouldn't expect to see any traffic from
block_timeout
the number of seconds a DHT node is banned if it exceeds the rate limit. The rate limit is averaged over 10 seconds to allow for bursts above the limit.
block_ratelimit
the max number of packets per second a DHT node is allowed to send without getting banned.
read_only
when set, the other nodes won't keep this node in their routing tables, it's meant for low-power and/or ephemeral devices that cannot support the DHT, it is also useful for mobile devices which are sensitive to network traffic and battery life. this node no longer responds to 'query' messages, and will place a 'ro' key (value = 1) in the top-level message dictionary of outgoing query messages.
item_lifetime
the number of seconds a immutable/mutable item will be expired. default is 0, means never expires.

settings_pack

Declared in "libtorrent/settings_pack.hpp"

The settings_pack struct, contains the names of all settings as enum values. These values are passed in to the set_str(), set_int(), set_bool() functions, to specify the setting to change.

These are the available settings:

name type default
user_agent string "libtorrent/" LIBTORRENT_VERSION

this is the client identification to the tracker. The recommended format of this string is: "ClientName/ClientVersion libtorrent/libtorrentVersion". This name will not only be used when making HTTP requests, but also when sending extended headers to peers that support that extension. It may not contain r or n

name type default
announce_ip string 0

announce_ip is the ip address passed along to trackers as the &ip= parameter. If left as the default, that parameter is omitted.

name type default
handshake_client_version string 0

this is the client name and version identifier sent to peers in the handshake message. If this is an empty string, the user_agent is used instead

name type default
outgoing_interfaces string ""

sets the network interface this session will use when it opens outgoing connections. By default, it binds outgoing connections to INADDR_ANY and port 0 (i.e. let the OS decide). Ths parameter must be a string containing one or more, comma separated, adapter names. Adapter names on unix systems are of the form "eth0", "eth1", "tun0", etc. When specifying multiple interfaces, they will be assigned in round-robin order. This may be useful for clients that are multi-homed. Binding an outgoing connection to a local IP does not necessarily make the connection via the associated NIC/Adapter. Setting this to an empty string will disable binding of outgoing connections.

name type default
listen_interfaces string "0.0.0.0:6881"

a comma-separated list of IP port-pairs. These are the listen ports that will be opened for accepting incoming uTP and TCP connections. It is possible to listen on multiple IPs and multiple ports. Binding to port 0 will make the operating system pick the port. The default is "0.0.0.0:6881", which binds to all interfaces on port 6881.

If binding fails because the port is busy, the port number will be incremented by one, settings_pack::max_retry_port_bind times.

if all retry attempts fail, the socket will be bound to port 0, meaning the operating system will pick a port. This behavior can be disabled by disabling settings_pack::listen_system_port_fallback.

if binding fails, the listen_failed_alert is posted, potentially more than once. Once/if binding the listen socket(s) succeed, listen_succeeded_alert is posted.

Each port will attempt to open both a UDP and a TCP listen socket, to allow accepting uTP connections as well as TCP. If using the DHT, this will also make the DHT use the same UDP ports.

Note

The current support for opening arbitrary UDP sockets is limited. In this version of libtorrent, there will only ever be two UDP sockets, one for IPv4 and one for IPv6.

name type default
proxy_hostname string ""

when using a poxy, this is the hostname where the proxy is running see proxy_type.

name type default
proxy_username string ""
proxy_password string ""

when using a proxy, these are the credentials (if any) to use whne connecting to it. see proxy_type

name type default
i2p_hostname string ""

sets the i2p SAM bridge to connect to. set the port with the i2p_port setting.

name type default
peer_fingerprint string "-LT11D0-"

this is the fingerprint for the client. It will be used as the prefix to the peer_id. If this is 20 bytes (or longer) it will be truncated to 20 bytes and used as the entire peer-id

There is a utility function, generate_fingerprint() that can be used to generate a standard client peer ID fingerprint prefix.

name type default
dht_bootstrap_nodes string "dht.libtorrent.org:25401"

This is a comma-separated list of IP port-pairs. They will be added to the DHT node (if it's enabled) as back-up nodes in case we don't know of any. This setting will contain one or more bootstrap nodes by default.

Changing these after the DHT has been started may not have any effect until the DHT is restarted.

name type default
allow_multiple_connections_per_ip bool false

determines if connections from the same IP address as existing connections should be rejected or not. Multiple connections from the same IP address is not allowed by default, to prevent abusive behavior by peers. It may be useful to allow such connections in cases where simulations are run on the same machine, and all peers in a swarm has the same IP address.

name type default
send_redundant_have bool true

send_redundant_have controls if have messages will be sent to peers that already have the piece. This is typically not necessary, but it might be necessary for collecting statistics in some cases. Default is false.

name type default
lazy_bitfields bool false

if this is true, outgoing bitfields will never be fuil. If the client is seed, a few bits will be set to 0, and later filled in with have messages. This is to prevent certain ISPs from stopping people from seeding.

name type default
use_dht_as_fallback bool false

use_dht_as_fallback determines how the DHT is used. If this is true, the DHT will only be used for torrents where all trackers in its tracker list has failed. Either by an explicit error message or a time out. This is false by default, which means the DHT is used by default regardless of if the trackers fail or not.

name type default
upnp_ignore_nonrouters bool false

upnp_ignore_nonrouters indicates whether or not the UPnP implementation should ignore any broadcast response from a device whose address is not the configured router for this machine. i.e. it's a way to not talk to other people's routers by mistake.

name type default
use_parole_mode bool true

use_parole_mode specifies if parole mode should be used. Parole mode means that peers that participate in pieces that fail the hash check are put in a mode where they are only allowed to download whole pieces. If the whole piece a peer in parole mode fails the hash check, it is banned. If a peer participates in a piece that passes the hash check, it is taken out of parole mode.

name type default
use_read_cache bool true

enable and disable caching of blocks read from disk. the purpose of the read cache is partly read-ahead of requests but also to avoid reading blocks back from the disk multiple times for popular pieces.

name type default
coalesce_reads bool false
coalesce_writes bool false

allocate separate, contiguous, buffers for read and write calls. Only used where writev/readv cannot be used will use more RAM but may improve performance

name type default
auto_manage_prefer_seeds bool false

prefer seeding torrents when determining which torrents to give active slots to, the default is false which gives preference to downloading torrents

name type default
dont_count_slow_torrents bool true

if dont_count_slow_torrents is true, torrents without any payload transfers are not subject to the active_seeds and active_downloads limits. This is intended to make it more likely to utilize all available bandwidth, and avoid having torrents that don't transfer anything block the active slots.

name type default
close_redundant_connections bool true

close_redundant_connections specifies whether libtorrent should close connections where both ends have no utility in keeping the connection open. For instance if both ends have completed their downloads, there's no point in keeping it open.

name type default
prioritize_partial_pieces bool false

If prioritize_partial_pieces is true, partial pieces are picked before pieces that are more rare. If false, rare pieces are always prioritized, unless the number of partial pieces is growing out of proportion.

name type default
rate_limit_ip_overhead bool true

if set to true, the estimated TCP/IP overhead is drained from the rate limiters, to avoid exceeding the limits with the total traffic

name type default
announce_to_all_tiers bool false
announce_to_all_trackers bool false

announce_to_all_trackers controls how multi tracker torrents are treated. If this is set to true, all trackers in the same tier are announced to in parallel. If all trackers in tier 0 fails, all trackers in tier 1 are announced as well. If it's set to false, the behavior is as defined by the multi tracker specification. It defaults to false, which is the same behavior previous versions of libtorrent has had as well.

announce_to_all_tiers also controls how multi tracker torrents are treated. When this is set to true, one tracker from each tier is announced to. This is the uTorrent behavior. This is false by default in order to comply with the multi-tracker specification.

name type default
prefer_udp_trackers bool true

prefer_udp_trackers is true by default. It means that trackers may be rearranged in a way that udp trackers are always tried before http trackers for the same hostname. Setting this to false means that the trackers' tier is respected and there's no preference of one protocol over another.

name type default
strict_super_seeding bool false

strict_super_seeding when this is set to true, a piece has to have been forwarded to a third peer before another one is handed out. This is the traditional definition of super seeding.

name type default
disable_hash_checks bool false

when set to true, all data downloaded from peers will be assumed to be correct, and not tested to match the hashes in the torrent this is only useful for simulation and testing purposes (typically combined with disabled_storage)

name type default
allow_i2p_mixed bool false

if this is true, i2p torrents are allowed to also get peers from other sources than the tracker, and connect to regular IPs, not providing any anonymization. This may be useful if the user is not interested in the anonymization of i2p, but still wants to be able to connect to i2p peers.

name type default
low_prio_disk bool true

low_prio_disk determines if the disk I/O should use a normal or low priority policy. This defaults to true, which means that it's low priority by default. Other processes doing disk I/O will normally take priority in this mode. This is meant to improve the overall responsiveness of the system while downloading in the background. For high-performance server setups, this might not be desirable.

name type default
volatile_read_cache bool false

volatile_read_cache, if this is set to true, read cache blocks that are hit by peer read requests are removed from the disk cache to free up more space. This is useful if you don't expect the disk cache to create any cache hits from other peers than the one who triggered the cache line to be read into the cache in the first place.

name type default
guided_read_cache bool false

guided_read_cache enables the disk cache to adjust the size of a cache line generated by peers to depend on the upload rate you are sending to that peer. The intention is to optimize the RAM usage of the cache, to read ahead further for peers that you're sending faster to.

name type default
no_atime_storage bool true

no_atime_storage this is a linux-only option and passes in the O_NOATIME to open() when opening files. This may lead to some disk performance improvements.

name type default
incoming_starts_queued_torrents bool false

incoming_starts_queued_torrents defaults to false. If a torrent has been paused by the auto managed feature in libtorrent, i.e. the torrent is paused and auto managed, this feature affects whether or not it is automatically started on an incoming connection. The main reason to queue torrents, is not to make them unavailable, but to save on the overhead of announcing to the trackers, the DHT and to avoid spreading one's unchoke slots too thin. If a peer managed to find us, even though we're no in the torrent anymore, this setting can make us start the torrent and serve it.

name type default
report_true_downloaded bool false

when set to true, the downloaded counter sent to trackers will include the actual number of payload bytes downloaded including redundant bytes. If set to false, it will not include any redundancy bytes

name type default
strict_end_game_mode bool true

strict_end_game_mode defaults to true, and controls when a block may be requested twice. If this is true, a block may only be requested twice when there's ay least one request to every piece that's left to download in the torrent. This may slow down progress on some pieces sometimes, but it may also avoid downloading a lot of redundant bytes. If this is false, libtorrent attempts to use each peer connection to its max, by always requesting something, even if it means requesting something that has been requested from another peer already.

name type default
broadcast_lsd bool true

if broadcast_lsd is set to true, the local peer discovery (or Local Service Discovery) will not only use IP multicast, but also broadcast its messages. This can be useful when running on networks that don't support multicast. Since broadcast messages might be expensive and disruptive on networks, only every 8th announce uses broadcast.

name type default
enable_outgoing_utp bool true
enable_incoming_utp bool true
enable_outgoing_tcp bool true
enable_incoming_tcp bool true

when set to true, libtorrent will try to make outgoing utp connections controls whether libtorrent will accept incoming connections or make outgoing connections of specific type.

name type default
ignore_resume_timestamps bool false

ignore_resume_timestamps determines if the storage, when loading resume data files, should verify that the file modification time with the timestamps in the resume data. This defaults to false, which means timestamps are taken into account, and resume data is less likely to accepted (torrents are more likely to be fully checked when loaded). It might be useful to set this to true if your network is faster than your disk, and it would be faster to redownload potentially missed pieces than to go through the whole storage to look for them.

name type default
no_recheck_incomplete_resume bool false

no_recheck_incomplete_resume determines if the storage should check the whole files when resume data is incomplete or missing or whether it should simply assume we don't have any of the data. By default, this is determined by the existence of any of the files. By setting this setting to true, the files won't be checked, but will go straight to download mode.

name type default
anonymous_mode bool false

anonymous_mode defaults to false. When set to true, the client tries to hide its identity to a certain degree. The user-agent will be reset to an empty string (except for private torrents). Trackers will only be used if they are using a proxy server. The listen sockets are closed, and incoming connections will only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the same machine as the tracker proxy). Since no incoming connections are accepted, NAT-PMP, UPnP, DHT and local peer discovery are all turned off when this setting is enabled.

If you're using I2P, it might make sense to enable anonymous mode as well.

name type default
report_web_seed_downloads bool true

specifies whether downloads from web seeds is reported to the tracker or not. Defaults to on. Turning it off also excludes web seed traffic from other stats and download rate reporting via the libtorrent API.

name type default
announce_double_nat bool false

if this is true, the &ip= argument in tracker requests (unless otherwise specified) will be set to the intermediate IP address if the user is double NATed. If the user is not double NATed, this option does not have an affect

name type default
seeding_outgoing_connections bool true

seeding_outgoing_connections determines if seeding (and finished) torrents should attempt to make outgoing connections or not. By default this is true. It may be set to false in very specific applications where the cost of making outgoing connections is high, and there are no or small benefits of doing so. For instance, if no nodes are behind a firewall or a NAT, seeds don't need to make outgoing connections.

name type default
no_connect_privileged_ports bool false

when this is true, libtorrent will not attempt to make outgoing connections to peers whose port is < 1024. This is a safety precaution to avoid being part of a DDoS attack

name type default
smooth_connects bool true

smooth_connects is true by default, which means the number of connection attempts per second may be limited to below the connection_speed, in case we're close to bump up against the limit of number of connections. The intention of this setting is to more evenly distribute our connection attempts over time, instead of attempting to connect in batches, and timing them out in batches.

name type default
always_send_user_agent bool false

always send user-agent in every web seed request. If false, only the first request per http connection will include the user agent

name type default
apply_ip_filter_to_trackers bool true

apply_ip_filter_to_trackers defaults to true. It determines whether the IP filter applies to trackers as well as peers. If this is set to false, trackers are exempt from the IP filter (if there is one). If no IP filter is set, this setting is irrelevant.

name type default
use_disk_read_ahead bool true

use_disk_read_ahead defaults to true and will attempt to optimize disk reads by giving the operating system heads up of disk read requests as they are queued in the disk job queue.

name type default
lock_files bool false

lock_files determines whether or not to lock files which libtorrent is downloading to or seeding from. This is implemented using fcntl(F_SETLK) on unix systems and by not passing in SHARE_READ and SHARE_WRITE on windows. This might prevent 3rd party processes from corrupting the files under libtorrent's feet.

name type default
contiguous_recv_buffer bool true

contiguous_recv_buffer determines whether or not libtorrent should receive data from peers into a contiguous intermediate buffer, to then copy blocks into disk buffers from, or to make many smaller calls to read(), each time passing in the specific buffer the data belongs in. When downloading at high rates, the latter may save some time copying data. When seeding at high rates, all incoming traffic consists of a very large number of tiny packets, and enabling contiguous_recv_buffer will provide higher performance. When this is enabled, it will only be used when seeding to peers, since that's when it provides performance improvements.

name type default
ban_web_seeds bool true

when true, web seeds sending bad data will be banned

name type default
allow_partial_disk_writes bool true

when set to false, the write_cache_line_size will apply across piece boundaries. this is a bad idea unless the piece picker also is configured to have an affinity to pick pieces belonging to the same write cache line as is configured in the disk cache.

name type default
force_proxy bool false

If true, disables any communication that's not going over a proxy. Enabling this requires a proxy to be configured as well, see proxy_type and proxy_hostname settings. The listen sockets are closed, and incoming connections will only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the same machine as the tracker proxy).

name type default
support_share_mode bool true

if false, prevents libtorrent to advertise share-mode support

name type default
support_merkle_torrents bool true

if this is false, don't advertise support for the Tribler merkle tree piece message

name type default
report_redundant_bytes bool true

if this is true, the number of redundant bytes is sent to the tracker

name type default
listen_system_port_fallback bool true

if this is true, libtorrent will fall back to listening on a port chosen by the operating system (i.e. binding to port 0). If a failure is preferred, set this to false.

name type default
use_disk_cache_pool bool false

use_disk_cache_pool enables using a pool allocator for disk cache blocks. Enabling it makes the cache perform better at high throughput. It also makes the cache less likely and slower at returning memory back to the system, once allocated.

name type default
announce_crypto_support bool true

when this is true, and incoming encrypted connections are enabled, &supportcrypt=1 is included in http tracker announces

name type default
enable_upnp bool true

Starts and stops the UPnP service. When started, the listen port and the DHT port are attempted to be forwarded on local UPnP router devices.

The upnp object returned by start_upnp() can be used to add and remove arbitrary port mappings. Mapping status is returned through the portmap_alert and the portmap_error_alert. The object will be valid until stop_upnp() is called. See upnp and nat pmp.

name type default
enable_natpmp bool true

Starts and stops the NAT-PMP service. When started, the listen port and the DHT port are attempted to be forwarded on the router through NAT-PMP.

The natpmp object returned by start_natpmp() can be used to add and remove arbitrary port mappings. Mapping status is returned through the portmap_alert and the portmap_error_alert. The object will be valid until stop_natpmp() is called. See upnp and nat pmp.

name type default
enable_lsd bool true

Starts and stops Local Service Discovery. This service will broadcast the infohashes of all the non-private torrents on the local network to look for peers on the same swarm within multicast reach.

name type default
enable_dht bool true

starts the dht node and makes the trackerless service available to torrents.

name type default
prefer_rc4 bool false

if the allowed encryption level is both, setting this to true will prefer rc4 if both methods are offered, plaintext otherwise

name type default
proxy_hostnames bool true

if true, hostname lookups are done via the configured proxy (if any). This is only supported by SOCKS5 and HTTP.

name type default
proxy_peer_connections bool true

if true, peer connections are made (and accepted) over the configured proxy, if any. Web seeds as well as regular bittorrent peer connections are considered "peer connections". Anything transporting actual torrent payload (trackers and DHT traffic are not considered peer connections).

name type default
auto_sequential bool true

if this setting is true, torrents with a very high availability of pieces (and seeds) are downloaded sequentially. This is more efficient for the disk I/O. With many seeds, the download order is unlikely to matter anyway

name type default
proxy_tracker_connections bool true

if true, tracker connections are made over the configured proxy, if any.

name type default
tracker_completion_timeout int 30

tracker_completion_timeout is the number of seconds the tracker connection will wait from when it sent the request until it considers the tracker to have timed-out. Default value is 60 seconds.

name type default
tracker_receive_timeout int 10

tracker_receive_timeout is the number of seconds to wait to receive any data from the tracker. If no data is received for this number of seconds, the tracker will be considered as having timed out. If a tracker is down, this is the kind of timeout that will occur.

name type default
stop_tracker_timeout int 5

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

name type default
tracker_maximum_response_length int 1024*1024

this is the maximum number of bytes in a tracker response. If a response size passes this number of bytes it will be rejected and the connection will be closed. On gzipped responses this size is measured on the uncompressed data. So, if you get 20 bytes of gzip response that'll expand to 2 megabytes, it will be interrupted before the entire response has been uncompressed (assuming the limit is lower than 2 megs).

name type default
piece_timeout int 20

the number of seconds from a request is sent until it times out if no piece response is returned.

name type default
request_timeout int 60

the number of seconds one block (16kB) is expected to be received within. If it's not, the block is requested from a different peer

name type default
request_queue_time int 3

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.

name type default
max_allowed_in_request_queue int 500

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.

name type default
max_out_request_queue int 500

max_out_request_queue is the maximum number of outstanding requests to send to a peer. This limit takes precedence over request_queue_time. i.e. no matter the download speed, the number of outstanding requests will never exceed this limit.

name type default
whole_pieces_threshold int 20

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.

name type default
peer_timeout int 120

peer_timeout is the number of seconds the peer connection should wait (for any activity on the peer connection) before closing it due to time out. This defaults to 120 seconds, since that's what's specified in the protocol specification. After half the time out, a keep alive message is sent.

name type default
urlseed_timeout int 20

same as peer_timeout, but only applies to url-seeds. this is usually set lower, because web servers are expected to be more reliable.

name type default
urlseed_pipeline_size int 5

controls the pipelining size of url-seeds. i.e. the number of HTTP request to keep outstanding before waiting for the first one to complete. It's common for web servers to limit this to a relatively low number, like 5

name type default
urlseed_wait_retry int 30

time to wait until a new retry of a web seed takes place

name type default
file_pool_size int 40

sets the upper limit on the total number of files this session will keep open. The reason why files are left open at all is that some anti virus software hooks on every file close, and scans the file for viruses. deferring the closing of the files will be the difference between a usable system and a completely hogged down system. Most operating systems also has a limit on the total number of file descriptors a process may have open.

name type default
max_failcount int 3

max_failcount is the maximum times we try to connect to a peer before stop connecting again. If a peer succeeds, the failcounter is reset. If a peer is retrieved from a peer source (other than DHT) the failcount is decremented by one, allowing another try.

name type default
min_reconnect_time int 60

the number of seconds to wait to reconnect to a peer. this time is multiplied with the failcount.

name type default
peer_connect_timeout int 15

peer_connect_timeout the number of seconds to wait after a connection attempt is initiated to a peer until it is considered as having timed out. This setting is especially important in case the number of half-open connections are limited, since stale half-open connection may delay the connection of other peers considerably.

name type default
connection_speed int 30

connection_speed is the number of connection attempts that are made per second. If a number < 0 is specified, it will default to 200 connections per second. If 0 is specified, it means don't make outgoing connections at all.

name type default
inactivity_timeout int 600

if a peer is uninteresting and uninterested for longer than this number of seconds, it will be disconnected. default is 10 minutes

name type default
unchoke_interval int 15

unchoke_interval is the number of seconds between chokes/unchokes. On this interval, peers are re-evaluated for being choked/unchoked. This is defined as 30 seconds in the protocol, and it should be significantly longer than what it takes for TCP to ramp up to it's max rate.

name type default
optimistic_unchoke_interval int 30

optimistic_unchoke_interval is the number of seconds between each optimistic unchoke. On this timer, the currently optimistically unchoked peer will change.

name type default
num_want int 200

num_want is the number of peers we want from each tracker request. It defines what is sent as the &num_want= parameter to the tracker.

name type default
initial_picker_threshold int 4

initial_picker_threshold specifies the number of pieces we need before we switch to rarest first picking. This defaults to 4, which means the 4 first pieces in any torrent are picked at random, the following pieces are picked in rarest first order.

name type default
allowed_fast_set_size int 10

the number of allowed pieces to send to peers that supports the fast extensions

name type default
suggest_mode int settings_pack::no_piece_suggestions

suggest_mode controls whether or not libtorrent will send out suggest messages to create a bias of its peers to request certain pieces. The modes are:

  • no_piece_suggestions which is the default and will not send out suggest messages.
  • suggest_read_cache which will send out suggest messages for the most recent pieces that are in the read cache.
name type default
max_queued_disk_bytes int 1024 * 1024

max_queued_disk_bytes is the maximum number of bytes, to be written to disk, that can wait in the disk I/O thread queue. This queue is only for waiting for the disk I/O thread to receive the job and either write it to disk or insert it in the write cache. When this limit is reached, the peer connections will stop reading data from their sockets, until the disk thread catches up. Setting this too low will severely limit your download rate.

name type default
handshake_timeout int 10

the number of seconds to wait for a handshake response from a peer. If no response is received within this time, the peer is disconnected.

name type default
send_buffer_low_watermark int 10 * 1024
send_buffer_watermark int 500 * 1024
send_buffer_watermark_factor int 50

send_buffer_low_watermark the minimum send buffer target size (send buffer includes bytes pending being read from disk). For good and snappy seeding performance, set this fairly high, to at least fit a few blocks. This is essentially the initial window size which will determine how fast we can ramp up the send rate

if the send buffer has fewer bytes than send_buffer_watermark, we'll read another 16kB block onto it. If set too small, upload rate capacity will suffer. If set too high, memory will be wasted. The actual watermark may be lower than this in case the upload rate is low, this is the upper limit.

the current upload rate to a peer is multiplied by this factor to get the send buffer watermark. The factor is specified as a percentage. i.e. 50 -> 0.5 This product is clamped to the send_buffer_watermark setting to not exceed the max. For high speed upload, this should be set to a greater value than 100. For high capacity connections, setting this higher can improve upload performance and disk throughput. Setting it too high may waste RAM and create a bias towards read jobs over write jobs.

name type default
choking_algorithm int settings_pack::fixed_slots_choker
seed_choking_algorithm int settings_pack::round_robin

choking_algorithm specifies which algorithm to use to determine which peers to unchoke.

The options for choking algorithms are:

  • fixed_slots_choker is the traditional choker with a fixed number of unchoke slots (as specified by settings_pack::unchoke_slots_limit).
  • rate_based_choker opens up unchoke slots based on the upload rate achieved to peers. The more slots that are opened, the marginal upload rate required to open up another slot increases.
  • bittyrant_choker attempts to optimize download rate by finding the reciprocation rate of each peer individually and prefers peers that gives the highest return on investment. It still allocates all upload capacity, but shuffles it around to the best peers first. For this choker to be efficient, you need to set a global upload rate limit (settings_pack::upload_rate_limit). For more information about this choker, see the paper. This choker is not fully implemented nor tested.

seed_choking_algorithm controls the seeding unchoke behavior. The available options are:

  • round_robin which round-robins the peers that are unchoked when seeding. This distributes the upload bandwidht uniformly and fairly. It minimizes the ability for a peer to download everything without redistributing it.
  • fastest_upload unchokes the peers we can send to the fastest. This might be a bit more reliable in utilizing all available capacity.
  • anti_leech prioritizes peers who have just started or are just about to finish the download. The intention is to force peers in the middle of the download to trade with each other.
name type default
cache_size int 1024
cache_buffer_chunk_size int 0
cache_expiry int 300

cache_size is the disk write and read cache. It is specified in units of 16 KiB blocks. Buffers that are part of a peer's send or receive buffer also count against this limit. Send and receive buffers will never be denied to be allocated, but they will cause the actual cached blocks to be flushed or evicted. If this is set to -1, the cache size is automatically set to the amount of physical RAM available in the machine divided by 8. If the amount of physical RAM cannot be determined, it's set to 1024 (= 16 MiB).

Disk buffers are allocated using a pool allocator, the number of blocks that are allocated at a time when the pool needs to grow can be specified in cache_buffer_chunk_size. Lower numbers saves memory at the expense of more heap allocations. If it is set to 0, the effective chunk size is proportional to the total cache size, attempting to strike a good balance between performance and memory usage. It defaults to 0. cache_expiry is the number of seconds from the last cached write to a piece in the write cache, to when it's forcefully flushed to disk. Default is 60 second.

On 32 bit builds, the effective cache size will be limited to 3/4 of 2 GiB to avoid exceeding the virtual address space limit.

name type default
disk_io_write_mode int settings_pack::enable_os_cache
disk_io_read_mode int settings_pack::enable_os_cache

determines how files are opened when they're in read only mode versus read and write mode. The options are:

enable_os_cache
This is the default and files are opened normally, with the OS caching reads and writes.
disable_os_cache
This opens all files in no-cache mode. This corresponds to the OS not letting blocks for the files linger in the cache. This makes sense in order to avoid the bittorrent client to potentially evict all other processes' cache by simply handling high throughput and large files. If libtorrent's read cache is disabled, enabling this may reduce performance.

One reason to disable caching is that it may help the operating system from growing its file cache indefinitely.

name type default
outgoing_port int 0
num_outgoing_ports int 0

this is the first port to use for binding outgoing connections to. This is useful for users that have routers that allow QoS settings based on local port. when binding outgoing connections to specific ports, num_outgoing_ports is the size of the range. It should be more than a few

Warning

setting outgoing ports will limit the ability to keep multiple connections to the same client, even for different torrents. It is not recommended to change this setting. Its main purpose is to use as an escape hatch for cheap routers with QoS capability but can only classify flows based on port numbers.

It is a range instead of a single port because of the problems with failing to reconnect to peers if a previous socket to that peer and port is in TIME_WAIT state.

name type default
peer_tos int 0x20

peer_tos determines the TOS byte set in the IP header of every packet sent to peers (including web seeds). The default value for this is 0x0 (no marking). One potentially useful TOS mark is 0x20, this represents the QBone scavenger service. For more details, see QBSS.

name type default
active_downloads int 3
active_seeds int 5
active_checking int 1
active_dht_limit int 88
active_tracker_limit int 1600
active_lsd_limit int 60
active_limit int 500
active_loaded_limit int 0

for auto managed torrents, these are the limits they are subject to. If there are too many torrents some of the auto managed ones will be paused until some slots free up. active_downloads and active_seeds controls how many active seeding and downloading torrents the queuing mechanism allows. The target number of active torrents is min(active_downloads + active_seeds, active_limit). active_downloads and active_seeds are upper limits on the number of downloading torrents and seeding torrents respectively. Setting the value to -1 means unlimited. For example if there are 10 seeding torrents and 10 downloading torrents, and active_downloads is 4 and active_seeds is 4, there will be 4 seeds active and 4 downloading torrents. If the settings are active_downloads = 2 and active_seeds = 4, then there will be 2 downloading torrents and 4 seeding torrents active. Torrents that are not auto managed are not counted against these limits.

active_checking is the limit of number of simultaneous checking torrents.

active_limit is a hard limit on the number of active (auto managed) torrents. This limit also applies to slow torrents.

active_dht_limit is the max number of torrents to announce to the DHT. By default this is set to 88, which is no more than one DHT announce every 10 seconds.

active_tracker_limit is the max number of torrents to announce to their trackers. By default this is 360, which is no more than one announce every 5 seconds.

active_lsd_limit is the max number of torrents to announce to the local network over the local service discovery protocol. By default this is 80, which is no more than one announce every 5 seconds (assuming the default announce interval of 5 minutes).

You can have more torrents active, even though they are not announced to the DHT, lsd or their tracker. If some peer knows about you for any reason and tries to connect, it will still be accepted, unless the torrent is paused, which means it won't accept any connections.

active_loaded_limit is the number of torrents that are allowed to be loaded at any given time. Note that a torrent can be active even though it's not loaded. If an unloaded torrents finds a peer that wants to access it, the torrent will be loaded on demand, using a user-supplied callback function. If the feature of unloading torrents is not enabled, this setting have no effect. If this limit is set to 0, it means unlimited. For more information, see dynamic loading of torrent files.

name type default
auto_manage_interval int 30

auto_manage_interval is the number of seconds between the torrent queue is updated, and rotated.

name type default
seed_time_limit int 24 * 60 * 60

this is the limit on the time a torrent has been an active seed (specified in seconds) before it is considered having met the seed limit criteria. See queuing.

name type default
auto_scrape_interval int 1800
auto_scrape_min_interval int 300

auto_scrape_interval is the number of seconds between scrapes of queued torrents (auto managed and paused torrents). Auto managed torrents that are paused, are scraped regularly in order to keep track of their downloader/seed ratio. This ratio is used to determine which torrents to seed and which to pause.

auto_scrape_min_interval is the minimum number of seconds between any automatic scrape (regardless of torrent). In case there are a large number of paused auto managed torrents, this puts a limit on how often a scrape request is sent.

name type default
max_peerlist_size int 3000
max_paused_peerlist_size int 1000

max_peerlist_size is the maximum number of peers in the list of known peers. These peers are not necessarily connected, so this number should be much greater than the maximum number of connected peers. Peers are evicted from the cache when the list grows passed 90% of this limit, and once the size hits the limit, peers are no longer added to the list. If this limit is set to 0, there is no limit on how many peers we'll keep in the peer list.

max_paused_peerlist_size is the max peer list size used for torrents that are paused. This default to the same as max_peerlist_size, but can be used to save memory for paused torrents, since it's not as important for them to keep a large peer list.

name type default
min_announce_interval int 5 * 60

this is the minimum allowed announce interval for a tracker. This is specified in seconds and is used as a sanity check on what is returned from a tracker. It mitigates hammering misconfigured trackers.

name type default
auto_manage_startup int 60

this is the number of seconds a torrent is considered active after it was started, regardless of upload and download speed. This is so that newly started torrents are not considered inactive until they have a fair chance to start downloading.

name type default
seeding_piece_quota int 20

seeding_piece_quota is the number of pieces to send to a peer, when seeding, before rotating in another peer to the unchoke set. It defaults to 3 pieces, which means that when seeding, any peer we've sent more than this number of pieces to will be unchoked in favour of a choked peer.

name type default
max_rejects int 50

TODO: deprecate this max_rejects is the number of piece requests we will reject in a row while a peer is choked before the peer is considered abusive and is disconnected.

name type default
recv_socket_buffer_size int 0
send_socket_buffer_size int 0

recv_socket_buffer_size and send_socket_buffer_size specifies the buffer sizes set on peer sockets. 0 (which is the default) means the OS default (i.e. don't change the buffer sizes). The socket buffer sizes are changed using setsockopt() with SOL_SOCKET/SO_RCVBUF and SO_SNDBUFFER.

name type default
read_cache_line_size int 32
write_cache_line_size int 16

read_cache_line_size is the number of blocks to read into the read cache when a read cache miss occurs. Setting this to 0 is essentially the same thing as disabling read cache. The number of blocks read into the read cache is always capped by the piece boundary.

When a piece in the write cache has write_cache_line_size contiguous blocks in it, they will be flushed. Setting this to 1 effectively disables the write cache.

name type default
optimistic_disk_retry int 10 * 60

optimistic_disk_retry is the number of seconds from a disk write errors occur on a torrent until libtorrent will take it out of the upload mode, to test if the error condition has been fixed.

libtorrent will only do this automatically for auto managed torrents.

You can explicitly take a torrent out of upload only mode using set_upload_mode().

name type default
max_suggest_pieces int 10

max_suggest_pieces is the max number of suggested piece indices received from a peer that's remembered. If a peer floods suggest messages, this limit prevents libtorrent from using too much RAM. It defaults to 10.

name type default
local_service_announce_interval int 5 * 60

local_service_announce_interval is the time between local network announces for a torrent. By default, when local service discovery is enabled a torrent announces itself every 5 minutes. This interval is specified in seconds.

name type default
dht_announce_interval int 15 * 60

dht_announce_interval is the number of seconds between announcing torrents to the distributed hash table (DHT).

name type default
udp_tracker_token_expiry int 60

udp_tracker_token_expiry is the number of seconds libtorrent will keep UDP tracker connection tokens around for. This is specified to be 60 seconds, and defaults to that. The higher this value is, the fewer packets have to be sent to the UDP tracker. In order for higher values to work, the tracker needs to be configured to match the expiration time for tokens.

name type default
default_cache_min_age int 1

default_cache_min_age is the minimum number of seconds any read cache line is kept in the cache. This defaults to one second but may be greater if guided_read_cache is enabled. Having a lower bound on the time a cache line stays in the cache is an attempt to avoid swapping the same pieces in and out of the cache in case there is a shortage of spare cache space.

name type default
num_optimistic_unchoke_slots int 0

num_optimistic_unchoke_slots is the number of optimistic unchoke slots to use. It defaults to 0, which means automatic. Having a higher number of optimistic unchoke slots mean you will find the good peers faster but with the trade-off to use up more bandwidth. When this is set to 0, libtorrent opens up 20% of your allowed upload slots as optimistic unchoke slots.

name type default
default_est_reciprocation_rate int 16000
increase_est_reciprocation_rate int 20
decrease_est_reciprocation_rate int 3

default_est_reciprocation_rate is the assumed reciprocation rate from peers when using the BitTyrant choker. This defaults to 14 kiB/s. If set too high, you will over-estimate your peers and be more altruistic while finding the true reciprocation rate, if it's set too low, you'll be too stingy and waste finding the true reciprocation rate.

increase_est_reciprocation_rate specifies how many percent the estimated reciprocation rate should be increased by each unchoke interval a peer is still choking us back. This defaults to 20%. This only applies to the BitTyrant choker.

decrease_est_reciprocation_rate specifies how many percent the estimated reciprocation rate should be decreased by each unchoke interval a peer unchokes us. This default to 3%. This only applies to the BitTyrant choker.

name type default
max_pex_peers int 50

the max number of peers we accept from pex messages from a single peer. this limits the number of concurrent peers any of our peers claims to be connected to. If they claim to be connected to more than this, we'll ignore any peer that exceeds this limit

name type default
tick_interval int 500

tick_interval specifies the number of milliseconds between internal ticks. This is the frequency with which bandwidth quota is distributed to peers. It should not be more than one second (i.e. 1000 ms). Setting this to a low value (around 100) means higher resolution bandwidth quota distribution, setting it to a higher value saves CPU cycles.

name type default
share_mode_target int 3

share_mode_target specifies the target share ratio for share mode torrents. This defaults to 3, meaning we'll try to upload 3 times as much as we download. Setting this very high, will make it very conservative and you might end up not downloading anything ever (and not affecting your share ratio). It does not make any sense to set this any lower than 2. For instance, if only 3 peers need to download the rarest piece, it's impossible to download a single piece and upload it more than 3 times. If the share_mode_target is set to more than 3, nothing is downloaded.

name type default
upload_rate_limit int 0
download_rate_limit int 0

upload_rate_limit and download_rate_limit sets the session-global limits of upload and download rate limits, in bytes per second. By default peers on the local network are not rate limited.

A value of 0 means unlimited.

For fine grained control over rate limits, including making them apply to local peers, see peer classes.

name type default
dht_upload_rate_limit int 4000

dht_upload_rate_limit sets the rate limit on the DHT. This is specified in bytes per second and defaults to 4000. For busy boxes with lots of torrents that requires more DHT traffic, this should be raised.

name type default
unchoke_slots_limit int 8

unchoke_slots_limit is the max number of unchoked peers in the session. The number of unchoke slots may be ignored depending on what choking_algorithm is set to.

name type default
connections_limit int 200

connections_limit sets a global limit on the number of connections opened. The number of connections is set to a hard minimum of at least two per torrent, so if you set a too low connections limit, and open too many torrents, the limit will not be met.

name type default
connections_slack int 10

connections_slack is the the number of incoming connections exceeding the connection limit to accept in order to potentially replace existing ones.

name type default
utp_target_delay int 100
utp_gain_factor int 3000
utp_min_timeout int 500
utp_syn_resends int 2
utp_fin_resends int 2
utp_num_resends int 3
utp_connect_timeout int 3000
utp_loss_multiplier int 50

utp_target_delay is the target delay for uTP sockets in milliseconds. A high value will make uTP connections more aggressive and cause longer queues in the upload bottleneck. It cannot be too low, since the noise in the measurements would cause it to send too slow. The default is 50 milliseconds. utp_gain_factor is the number of bytes the uTP congestion window can increase at the most in one RTT. This defaults to 300 bytes. If this is set too high, the congestion controller reacts too hard to noise and will not be stable, if it's set too low, it will react slow to congestion and not back off as fast. utp_min_timeout is the shortest allowed uTP socket timeout, specified in milliseconds. This defaults to 500 milliseconds. The timeout depends on the RTT of the connection, but is never smaller than this value. A connection times out when every packet in a window is lost, or when a packet is lost twice in a row (i.e. the resent packet is lost as well).

The shorter the timeout is, the faster the connection will recover from this situation, assuming the RTT is low enough. utp_syn_resends is the number of SYN packets that are sent (and timed out) before giving up and closing the socket. utp_num_resends is the number of times a packet is sent (and lossed or timed out) before giving up and closing the connection. utp_connect_timeout is the number of milliseconds of timeout for the initial SYN packet for uTP connections. For each timed out packet (in a row), the timeout is doubled. utp_loss_multiplier controls how the congestion window is changed when a packet loss is experienced. It's specified as a percentage multiplier for cwnd. By default it's set to 50 (i.e. cut in half). Do not change this value unless you know what you're doing. Never set it higher than 100.

name type default
mixed_mode_algorithm int settings_pack::peer_proportional

The mixed_mode_algorithm determines how to treat TCP connections when there are uTP connections. Since uTP is designed to yield to TCP, there's an inherent problem when using swarms that have both TCP and uTP connections. If nothing is done, uTP connections would often be starved out for bandwidth by the TCP connections. This mode is prefer_tcp. The peer_proportional mode simply looks at the current throughput and rate limits all TCP connections to their proportional share based on how many of the connections are TCP. This works best if uTP connections are not rate limited by the global rate limiter (which they aren't by default).

name type default
listen_queue_size int 5

listen_queue_size is the value passed in to listen() for the listen socket. It is the number of outstanding incoming connections to queue up while we're not actively waiting for a connection to be accepted. The default is 5 which should be sufficient for any normal client. If this is a high performance server which expects to receive a lot of connections, or used in a simulator or test, it might make sense to raise this number. It will not take affect until the listen_interfaces settings is updated.

name type default
torrent_connect_boost int 30

torrent_connect_boost is the number of peers to try to connect to immediately when the first tracker response is received for a torrent. This is a boost to given to new torrents to accelerate them starting up. The normal connect scheduler is run once every second, this allows peers to be connected immediately instead of waiting for the session tick to trigger connections. This may not be set higher than 255.

name type default
alert_queue_size int 1000

alert_queue_size is the maximum number of alerts queued up internally. If alerts are not popped, the queue will eventually fill up to this level.

name type default
max_metadata_size int 3 * 1024 * 10240

max_metadata_size is the maximum allowed size (in bytes) to be received by the metadata extension, i.e. magnet links.

name type default
checking_mem_usage int 1024

the number of blocks to keep outstanding at any given time when checking torrents. Higher numbers give faster re-checks but uses more memory. Specified in number of 16 kiB blocks

name type default
predictive_piece_announce int 0

if set to > 0, pieces will be announced to other peers before they are fully downloaded (and before they are hash checked). The intention is to gain 1.5 potential round trip times per downloaded piece. When non-zero, this indicates how many milliseconds in advance pieces should be announced, before they are expected to be completed.

name type default
aio_threads int 4
aio_max int 300

for some aio back-ends, aio_threads specifies the number of io-threads to use, and aio_max the max number of outstanding jobs.

name type default
network_threads int 0

network_threads is the number of threads to use to call async_write_some (i.e. send) on peer connection sockets. When seeding at extremely high rates, this may become a bottleneck, and setting this to 2 or more may parallelize that cost. When using SSL torrents, all encryption for outgoing traffic is done within the socket send functions, and this will help parallelizing the cost of SSL encryption as well.

name type default
ssl_listen int 0

ssl_listen sets the listen port for SSL connections. If this is set to 0, no SSL listen port is opened. Otherwise a socket is opened on this port. This setting is only taken into account when opening the regular listen port, and won't re-open the listen socket simply by changing this setting.

name type default
tracker_backoff int 250

tracker_backoff determines how aggressively to back off from retrying failing trackers. This value determines x in the following formula, determining the number of seconds to wait until the next retry:

delay = 5 + 5 * x / 100 * fails^2

This setting may be useful to make libtorrent more or less aggressive in hitting trackers.

name type default
share_ratio_limit int 200
seed_time_ratio_limit int 700

when a seeding torrent reaches either the share ratio (bytes up / bytes down) or the seed time ratio (seconds as seed / seconds as downloader) or the seed time limit (seconds as seed) it is considered done, and it will leave room for other torrents. These are specified as percentages. Torrents that are considered done will still be allowed to be seeded, they just won't have priority anymore. For more, see queuing.

name type default
peer_turnover int 4
peer_turnover_cutoff int 90
peer_turnover_interval int 300

peer_turnover is the percentage of peers to disconnect every turnover peer_turnover_interval (if we're at the peer limit), this is specified in percent when we are connected to more than limit * peer_turnover_cutoff peers disconnect peer_turnover fraction of the peers. It is specified in percent peer_turnover_interval is the interval (in seconds) between optimistic disconnects if the disconnects happen and how many peers are disconnected is controlled by peer_turnover and peer_turnover_cutoff

name type default
connect_seed_every_n_download int 10

this setting controls the priority of downloading torrents over seeding or finished torrents when it comes to making peer connections. Peer connections are throttled by the connection_speed and the half-open connection limit. This makes peer connections a limited resource. Torrents that still have pieces to download are prioritized by default, to avoid having many seeding torrents use most of the connection attempts and only give one peer every now and then to the downloading torrent. libtorrent will loop over the downloading torrents to connect a peer each, and every n:th connection attempt, a finished torrent is picked to be allowed to connect to a peer. This setting controls n.

name type default
max_http_recv_buffer_size int 4*1024*204

the max number of bytes to allow an HTTP response to be when announcing to trackers or downloading .torrent files via the url provided in add_torrent_params.

name type default
max_retry_port_bind int 10

if binding to a specific port fails, should the port be incremented by one and tried again? This setting specifies how many times to retry a failed port bind

name type default
alert_mask int alert::error_notification

a bitmask combining flags from alert::category_t defining which kinds of alerts to receive

name type default
out_enc_policy int settings_pack::pe_enabled
in_enc_policy int settings_pack::pe_enabled

control the settings for incoming and outgoing connections respectively. see enc_policy enum for the available options. Keep in mind that protocol encryption degrades performance in several respects:

  1. It prevents "zero copy" disk buffers being sent to peers, since each peer needs to mutate the data (i.e. encrypt it) the data must be copied per peer connection rather than sending the same buffer to multiple peers.
  2. The encryption itself requires more CPU than plain bittorrent protocol. The highest cost is the Diffie Hellman exchange on connection setup.
  3. The encryption handshake adds several round-trips to the connection setup, and delays transferring data.
name type default
allowed_enc_level int settings_pack::pe_both

determines the encryption level of the connections. This setting will adjust which encryption scheme is offered to the other peer, as well as which encryption scheme is selected by the client. See enc_level enum for options.

name type default
inactive_down_rate int 2048
inactive_up_rate int 2048

the download and upload rate limits for a torrent to be considered active by the queuing mechanism. A torrent whose download rate is less than inactive_down_rate and whose upload rate is less than inactive_up_rate for auto_manage_startup seconds, is considered inactive, and another queued torrent may be started. This logic is disabled if dont_count_slow_torrents is false.

name type default
proxy_type int settings_pack::none

proxy to use, defaults to none. see proxy_type_t.

name type default
proxy_port int 0

the port of the proxy server

name type default
i2p_port int 0

sets the i2p SAM bridge port to connect to. set the hostname with the i2p_hostname setting.

name type default
cache_size_volatile int 256

this determines the max number of volatile disk cache blocks. If the number of volatile blocks exceed this limit, other volatile blocks will start to be evicted. A disk cache block is volatile if it has low priority, and should be one of the first blocks to be evicted under pressure. For instance, blocks pulled into the cache as the result of calculating a piece hash are volatile. These blocks don't represent potential interest among peers, so the value of keeping them in the cache is limited.

name type default
urlseed_max_request_bytes int 16 * 1024 * 1024

The maximum request range of an url seed in bytes. This value defines the largest possible sequential web seed request. Default is 16 * 1024 * 1024. Lower values are possible but will be ignored if they are lower then piece size. This value should be related to your download speed to prevent libtorrent from creating too many expensive http requests per second. You can select a value as high as you want but keep in mind that libtorrent can't create parallel requests if the first request did already select the whole file. If you combine bittorrent seeds with web seeds and pick strategies like rarest first you may find your web seed requests split into smaller parts because we don't download already picked pieces twice.

name type default
web_seed_name_lookup_retry int 1800

time to wait until a new retry of a web seed name lookup

name type default
close_file_interval int CLOSE_FILE_INTERVAL

the number of seconds between closing the file opened the longest ago. 0 means to disable the feature. The purpose of this is to periodically close files to trigger the operating system flushing disk cache. Specifically it has been observed to be required on windows to not have the disk cache grow indefinitely. This defaults to 120 seconds on windows, and disabled on other systems.

name type default
utp_cwnd_reduce_timer int 100

When uTP experiences packet loss, it will reduce the congestion window, and not reduce it again for this many milliseconds, even if experiencing another lost packet.

struct settings_pack
{
   void set_int (int name, int val);
   void set_str (int name, std::string val);
   void set_bool (int name, bool val);
   bool has_val (int name) const;
   void clear ();
   void clear (int name);
   bool get_bool (int name) const;
   int get_int (int name) const;
   std::string get_str (int name) const;

   enum type_bases
   {
      string_type_base,
      int_type_base,
      bool_type_base,
      type_mask,
      index_mask,
   };

   enum string_types
   {
      user_agent,
      announce_ip,
      deprecated12,
      handshake_client_version,
      outgoing_interfaces,
      listen_interfaces,
      proxy_hostname,
      proxy_username,
      proxy_password,
      i2p_hostname,
      peer_fingerprint,
      dht_bootstrap_nodes,
      max_string_setting_internal,
   };

   enum bool_types
   {
      allow_multiple_connections_per_ip,
      deprecated1,
      send_redundant_have,
      lazy_bitfields,
      use_dht_as_fallback,
      upnp_ignore_nonrouters,
      use_parole_mode,
      use_read_cache,
      deprecated7,
      deprecated10,
      deprecated13,
      coalesce_reads,
      coalesce_writes,
      auto_manage_prefer_seeds,
      dont_count_slow_torrents,
      close_redundant_connections,
      prioritize_partial_pieces,
      rate_limit_ip_overhead,
      announce_to_all_tiers,
      announce_to_all_trackers,
      prefer_udp_trackers,
      strict_super_seeding,
      deprecated8,
      disable_hash_checks,
      allow_i2p_mixed,
      low_prio_disk,
      volatile_read_cache,
      guided_read_cache,
      no_atime_storage,
      incoming_starts_queued_torrents,
      report_true_downloaded,
      strict_end_game_mode,
      broadcast_lsd,
      enable_outgoing_utp,
      enable_incoming_utp,
      enable_outgoing_tcp,
      enable_incoming_tcp,
      ignore_resume_timestamps,
      no_recheck_incomplete_resume,
      anonymous_mode,
      report_web_seed_downloads,
      deprecated2,
      announce_double_nat,
      seeding_outgoing_connections,
      no_connect_privileged_ports,
      smooth_connects,
      always_send_user_agent,
      apply_ip_filter_to_trackers,
      use_disk_read_ahead,
      lock_files,
      contiguous_recv_buffer,
      ban_web_seeds,
      allow_partial_disk_writes,
      force_proxy,
      support_share_mode,
      support_merkle_torrents,
      report_redundant_bytes,
      listen_system_port_fallback,
      use_disk_cache_pool,
      announce_crypto_support,
      enable_upnp,
      enable_natpmp,
      enable_lsd,
      enable_dht,
      prefer_rc4,
      proxy_hostnames,
      proxy_peer_connections,
      auto_sequential,
      proxy_tracker_connections,
      max_bool_setting_internal,
   };

   enum int_types
   {
      tracker_completion_timeout,
      tracker_receive_timeout,
      stop_tracker_timeout,
      tracker_maximum_response_length,
      piece_timeout,
      request_timeout,
      request_queue_time,
      max_allowed_in_request_queue,
      max_out_request_queue,
      whole_pieces_threshold,
      peer_timeout,
      urlseed_timeout,
      urlseed_pipeline_size,
      urlseed_wait_retry,
      file_pool_size,
      max_failcount,
      min_reconnect_time,
      peer_connect_timeout,
      connection_speed,
      inactivity_timeout,
      unchoke_interval,
      optimistic_unchoke_interval,
      num_want,
      initial_picker_threshold,
      allowed_fast_set_size,
      suggest_mode,
      max_queued_disk_bytes,
      handshake_timeout,
      send_buffer_low_watermark,
      send_buffer_watermark,
      send_buffer_watermark_factor,
      choking_algorithm,
      seed_choking_algorithm,
      cache_size,
      cache_buffer_chunk_size,
      cache_expiry,
      deprecated11,
      disk_io_write_mode,
      disk_io_read_mode,
      outgoing_port,
      num_outgoing_ports,
      peer_tos,
      active_downloads,
      active_seeds,
      active_checking,
      active_dht_limit,
      active_tracker_limit,
      active_lsd_limit,
      active_limit,
      active_loaded_limit,
      auto_manage_interval,
      seed_time_limit,
      auto_scrape_interval,
      auto_scrape_min_interval,
      max_peerlist_size,
      max_paused_peerlist_size,
      min_announce_interval,
      auto_manage_startup,
      seeding_piece_quota,
      max_rejects,
      recv_socket_buffer_size,
      send_socket_buffer_size,
      deprecated14,
      read_cache_line_size,
      write_cache_line_size,
      optimistic_disk_retry,
      max_suggest_pieces,
      local_service_announce_interval,
      dht_announce_interval,
      udp_tracker_token_expiry,
      default_cache_min_age,
      num_optimistic_unchoke_slots,
      default_est_reciprocation_rate,
      increase_est_reciprocation_rate,
      decrease_est_reciprocation_rate,
      max_pex_peers,
      tick_interval,
      share_mode_target,
      upload_rate_limit,
      download_rate_limit,
      deprecated3,
      deprecated4,
      dht_upload_rate_limit,
      unchoke_slots_limit,
      deprecated5,
      connections_limit,
      connections_slack,
      utp_target_delay,
      utp_gain_factor,
      utp_min_timeout,
      utp_syn_resends,
      utp_fin_resends,
      utp_num_resends,
      utp_connect_timeout,
      deprecated6,
      utp_loss_multiplier,
      mixed_mode_algorithm,
      listen_queue_size,
      torrent_connect_boost,
      alert_queue_size,
      max_metadata_size,
      deprecated9,
      checking_mem_usage,
      predictive_piece_announce,
      aio_threads,
      aio_max,
      network_threads,
      ssl_listen,
      tracker_backoff,
      share_ratio_limit,
      seed_time_ratio_limit,
      peer_turnover,
      peer_turnover_cutoff,
      peer_turnover_interval,
      connect_seed_every_n_download,
      max_http_recv_buffer_size,
      max_retry_port_bind,
      alert_mask,
      out_enc_policy,
      in_enc_policy,
      allowed_enc_level,
      inactive_down_rate,
      inactive_up_rate,
      proxy_type,
      proxy_port,
      i2p_port,
      cache_size_volatile,
      urlseed_max_request_bytes,
      web_seed_name_lookup_retry,
      close_file_interval,
      utp_cwnd_reduce_timer,
      max_int_setting_internal,
   };

   enum settings_counts_t
   {
      num_string_settings,
      num_bool_settings,
      num_int_settings,
   };

   enum suggest_mode_t
   {
      no_piece_suggestions,
      suggest_read_cache,
   };

   enum choking_algorithm_t
   {
      fixed_slots_choker,
      rate_based_choker,
      bittyrant_choker,
   };

   enum seed_choking_algorithm_t
   {
      round_robin,
      fastest_upload,
      anti_leech,
   };

   enum io_buffer_mode_t
   {
      enable_os_cache,
      deprecated,
      disable_os_cache,
   };

   enum bandwidth_mixed_algo_t
   {
      prefer_tcp,
      peer_proportional,
   };

   enum enc_policy
   {
      pe_forced,
      pe_enabled,
      pe_disabled,
   };

   enum enc_level
   {
      pe_plaintext,
      pe_rc4,
      pe_both,
   };

   enum proxy_type_t
   {
      none,
      socks4,
      socks5,
      socks5_pw,
      http,
      http_pw,
      i2p_proxy,
   };
};

clear()

void clear ();

clear the settings pack from all settings

clear()

void clear (int name);

clear a specific setting from the pack

enum type_bases

Declared in "libtorrent/settings_pack.hpp"

name value description
string_type_base 0  
int_type_base 16384  
bool_type_base 32768  
type_mask 49152  
index_mask 16383  

enum string_types

Declared in "libtorrent/settings_pack.hpp"

name value description
user_agent   this is the client identification to the tracker. The recommended format of this string is: "ClientName/ClientVersion libtorrent/libtorrentVersion". This name will not only be used when making HTTP requests, but also when sending extended headers to peers that support that extension. It may not contain r or n
announce_ip 1 announce_ip is the ip address passed along to trackers as the &ip= parameter. If left as the default, that parameter is omitted.
deprecated12 2  
handshake_client_version 3 this is the client name and version identifier sent to peers in the handshake message. If this is an empty string, the user_agent is used instead
outgoing_interfaces 4 sets the network interface this session will use when it opens outgoing connections. By default, it binds outgoing connections to INADDR_ANY and port 0 (i.e. let the OS decide). Ths parameter must be a string containing one or more, comma separated, adapter names. Adapter names on unix systems are of the form "eth0", "eth1", "tun0", etc. When specifying multiple interfaces, they will be assigned in round-robin order. This may be useful for clients that are multi-homed. Binding an outgoing connection to a local IP does not necessarily make the connection via the associated NIC/Adapter. Setting this to an empty string will disable binding of outgoing connections.
listen_interfaces 5

a comma-separated list of IP port-pairs. These are the listen ports that will be opened for accepting incoming uTP and TCP connections. It is possible to listen on multiple IPs and multiple ports. Binding to port 0 will make the operating system pick the port. The default is "0.0.0.0:6881", which binds to all interfaces on port 6881.

If binding fails because the port is busy, the port number will be incremented by one, settings_pack::max_retry_port_bind times.

if all retry attempts fail, the socket will be bound to port 0, meaning the operating system will pick a port. This behavior can be disabled by disabling settings_pack::listen_system_port_fallback.

if binding fails, the listen_failed_alert is posted, potentially more than once. Once/if binding the listen socket(s) succeed, listen_succeeded_alert is posted.

Each port will attempt to open both a UDP and a TCP listen socket, to allow accepting uTP connections as well as TCP. If using the DHT, this will also make the DHT use the same UDP ports.

Note

The current support for opening arbitrary UDP sockets is limited. In this version of libtorrent, there will only ever be two UDP sockets, one for IPv4 and one for IPv6.

proxy_hostname 6 when using a poxy, this is the hostname where the proxy is running see proxy_type.
proxy_username 7 when using a proxy, these are the credentials (if any) to use whne connecting to it. see proxy_type
proxy_password 8  
i2p_hostname 9 sets the i2p SAM bridge to connect to. set the port with the i2p_port setting.
peer_fingerprint 10

this is the fingerprint for the client. It will be used as the prefix to the peer_id. If this is 20 bytes (or longer) it will be truncated to 20 bytes and used as the entire peer-id

There is a utility function, generate_fingerprint() that can be used to generate a standard client peer ID fingerprint prefix.

dht_bootstrap_nodes 11

This is a comma-separated list of IP port-pairs. They will be added to the DHT node (if it's enabled) as back-up nodes in case we don't know of any. This setting will contain one or more bootstrap nodes by default.

Changing these after the DHT has been started may not have any effect until the DHT is restarted.

max_string_setting_internal 12  

enum bool_types

Declared in "libtorrent/settings_pack.hpp"

name value description
allow_multiple_connections_per_ip   determines if connections from the same IP address as existing connections should be rejected or not. Multiple connections from the same IP address is not allowed by default, to prevent abusive behavior by peers. It may be useful to allow such connections in cases where simulations are run on the same machine, and all peers in a swarm has the same IP address.
deprecated1 1  
send_redundant_have 2 send_redundant_have controls if have messages will be sent to peers that already have the piece. This is typically not necessary, but it might be necessary for collecting statistics in some cases. Default is false.
lazy_bitfields 3 if this is true, outgoing bitfields will never be fuil. If the client is seed, a few bits will be set to 0, and later filled in with have messages. This is to prevent certain ISPs from stopping people from seeding.
use_dht_as_fallback 4 use_dht_as_fallback determines how the DHT is used. If this is true, the DHT will only be used for torrents where all trackers in its tracker list has failed. Either by an explicit error message or a time out. This is false by default, which means the DHT is used by default regardless of if the trackers fail or not.
upnp_ignore_nonrouters 5 upnp_ignore_nonrouters indicates whether or not the UPnP implementation should ignore any broadcast response from a device whose address is not the configured router for this machine. i.e. it's a way to not talk to other people's routers by mistake.
use_parole_mode 6 use_parole_mode specifies if parole mode should be used. Parole mode means that peers that participate in pieces that fail the hash check are put in a mode where they are only allowed to download whole pieces. If the whole piece a peer in parole mode fails the hash check, it is banned. If a peer participates in a piece that passes the hash check, it is taken out of parole mode.
use_read_cache 7 enable and disable caching of blocks read from disk. the purpose of the read cache is partly read-ahead of requests but also to avoid reading blocks back from the disk multiple times for popular pieces.
deprecated7 8  
deprecated10 9  
deprecated13 10  
coalesce_reads 11 allocate separate, contiguous, buffers for read and write calls. Only used where writev/readv cannot be used will use more RAM but may improve performance
coalesce_writes 12  
auto_manage_prefer_seeds 13 prefer seeding torrents when determining which torrents to give active slots to, the default is false which gives preference to downloading torrents
dont_count_slow_torrents 14 if dont_count_slow_torrents is true, torrents without any payload transfers are not subject to the active_seeds and active_downloads limits. This is intended to make it more likely to utilize all available bandwidth, and avoid having torrents that don't transfer anything block the active slots.
close_redundant_connections 15 close_redundant_connections specifies whether libtorrent should close connections where both ends have no utility in keeping the connection open. For instance if both ends have completed their downloads, there's no point in keeping it open.
prioritize_partial_pieces 16 If prioritize_partial_pieces is true, partial pieces are picked before pieces that are more rare. If false, rare pieces are always prioritized, unless the number of partial pieces is growing out of proportion.
rate_limit_ip_overhead 17 if set to true, the estimated TCP/IP overhead is drained from the rate limiters, to avoid exceeding the limits with the total traffic
announce_to_all_tiers 18

announce_to_all_trackers controls how multi tracker torrents are treated. If this is set to true, all trackers in the same tier are announced to in parallel. If all trackers in tier 0 fails, all trackers in tier 1 are announced as well. If it's set to false, the behavior is as defined by the multi tracker specification. It defaults to false, which is the same behavior previous versions of libtorrent has had as well.

announce_to_all_tiers also controls how multi tracker torrents are treated. When this is set to true, one tracker from each tier is announced to. This is the uTorrent behavior. This is false by default in order to comply with the multi-tracker specification.

announce_to_all_trackers 19  
prefer_udp_trackers 20 prefer_udp_trackers is true by default. It means that trackers may be rearranged in a way that udp trackers are always tried before http trackers for the same hostname. Setting this to false means that the trackers' tier is respected and there's no preference of one protocol over another.
strict_super_seeding 21 strict_super_seeding when this is set to true, a piece has to have been forwarded to a third peer before another one is handed out. This is the traditional definition of super seeding.
deprecated8 22  
disable_hash_checks 23 when set to true, all data downloaded from peers will be assumed to be correct, and not tested to match the hashes in the torrent this is only useful for simulation and testing purposes (typically combined with disabled_storage)
allow_i2p_mixed 24 if this is true, i2p torrents are allowed to also get peers from other sources than the tracker, and connect to regular IPs, not providing any anonymization. This may be useful if the user is not interested in the anonymization of i2p, but still wants to be able to connect to i2p peers.
low_prio_disk 25 low_prio_disk determines if the disk I/O should use a normal or low priority policy. This defaults to true, which means that it's low priority by default. Other processes doing disk I/O will normally take priority in this mode. This is meant to improve the overall responsiveness of the system while downloading in the background. For high-performance server setups, this might not be desirable.
volatile_read_cache 26 volatile_read_cache, if this is set to true, read cache blocks that are hit by peer read requests are removed from the disk cache to free up more space. This is useful if you don't expect the disk cache to create any cache hits from other peers than the one who triggered the cache line to be read into the cache in the first place.
guided_read_cache 27 guided_read_cache enables the disk cache to adjust the size of a cache line generated by peers to depend on the upload rate you are sending to that peer. The intention is to optimize the RAM usage of the cache, to read ahead further for peers that you're sending faster to.
no_atime_storage 28 no_atime_storage this is a linux-only option and passes in the O_NOATIME to open() when opening files. This may lead to some disk performance improvements.
incoming_starts_queued_torrents 29 incoming_starts_queued_torrents defaults to false. If a torrent has been paused by the auto managed feature in libtorrent, i.e. the torrent is paused and auto managed, this feature affects whether or not it is automatically started on an incoming connection. The main reason to queue torrents, is not to make them unavailable, but to save on the overhead of announcing to the trackers, the DHT and to avoid spreading one's unchoke slots too thin. If a peer managed to find us, even though we're no in the torrent anymore, this setting can make us start the torrent and serve it.
report_true_downloaded 30 when set to true, the downloaded counter sent to trackers will include the actual number of payload bytes downloaded including redundant bytes. If set to false, it will not include any redundancy bytes
strict_end_game_mode 31 strict_end_game_mode defaults to true, and controls when a block may be requested twice. If this is true, a block may only be requested twice when there's ay least one request to every piece that's left to download in the torrent. This may slow down progress on some pieces sometimes, but it may also avoid downloading a lot of redundant bytes. If this is false, libtorrent attempts to use each peer connection to its max, by always requesting something, even if it means requesting something that has been requested from another peer already.
broadcast_lsd 32 if broadcast_lsd is set to true, the local peer discovery (or Local Service Discovery) will not only use IP multicast, but also broadcast its messages. This can be useful when running on networks that don't support multicast. Since broadcast messages might be expensive and disruptive on networks, only every 8th announce uses broadcast.
enable_outgoing_utp 33 when set to true, libtorrent will try to make outgoing utp connections controls whether libtorrent will accept incoming connections or make outgoing connections of specific type.
enable_incoming_utp 34  
enable_outgoing_tcp 35  
enable_incoming_tcp 36  
ignore_resume_timestamps 37 ignore_resume_timestamps determines if the storage, when loading resume data files, should verify that the file modification time with the timestamps in the resume data. This defaults to false, which means timestamps are taken into account, and resume data is less likely to accepted (torrents are more likely to be fully checked when loaded). It might be useful to set this to true if your network is faster than your disk, and it would be faster to redownload potentially missed pieces than to go through the whole storage to look for them.
no_recheck_incomplete_resume 38 no_recheck_incomplete_resume determines if the storage should check the whole files when resume data is incomplete or missing or whether it should simply assume we don't have any of the data. By default, this is determined by the existence of any of the files. By setting this setting to true, the files won't be checked, but will go straight to download mode.
anonymous_mode 39

anonymous_mode defaults to false. When set to true, the client tries to hide its identity to a certain degree. The user-agent will be reset to an empty string (except for private torrents). Trackers will only be used if they are using a proxy server. The listen sockets are closed, and incoming connections will only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the same machine as the tracker proxy). Since no incoming connections are accepted, NAT-PMP, UPnP, DHT and local peer discovery are all turned off when this setting is enabled.

If you're using I2P, it might make sense to enable anonymous mode as well.

report_web_seed_downloads 40 specifies whether downloads from web seeds is reported to the tracker or not. Defaults to on. Turning it off also excludes web seed traffic from other stats and download rate reporting via the libtorrent API.
deprecated2 41  
announce_double_nat 42 if this is true, the &ip= argument in tracker requests (unless otherwise specified) will be set to the intermediate IP address if the user is double NATed. If the user is not double NATed, this option does not have an affect
seeding_outgoing_connections 43 seeding_outgoing_connections determines if seeding (and finished) torrents should attempt to make outgoing connections or not. By default this is true. It may be set to false in very specific applications where the cost of making outgoing connections is high, and there are no or small benefits of doing so. For instance, if no nodes are behind a firewall or a NAT, seeds don't need to make outgoing connections.
no_connect_privileged_ports 44 when this is true, libtorrent will not attempt to make outgoing connections to peers whose port is < 1024. This is a safety precaution to avoid being part of a DDoS attack
smooth_connects 45 smooth_connects is true by default, which means the number of connection attempts per second may be limited to below the connection_speed, in case we're close to bump up against the limit of number of connections. The intention of this setting is to more evenly distribute our connection attempts over time, instead of attempting to connect in batches, and timing them out in batches.
always_send_user_agent 46 always send user-agent in every web seed request. If false, only the first request per http connection will include the user agent
apply_ip_filter_to_trackers 47 apply_ip_filter_to_trackers defaults to true. It determines whether the IP filter applies to trackers as well as peers. If this is set to false, trackers are exempt from the IP filter (if there is one). If no IP filter is set, this setting is irrelevant.
use_disk_read_ahead 48 use_disk_read_ahead defaults to true and will attempt to optimize disk reads by giving the operating system heads up of disk read requests as they are queued in the disk job queue.
lock_files 49 lock_files determines whether or not to lock files which libtorrent is downloading to or seeding from. This is implemented using fcntl(F_SETLK) on unix systems and by not passing in SHARE_READ and SHARE_WRITE on windows. This might prevent 3rd party processes from corrupting the files under libtorrent's feet.
contiguous_recv_buffer 50 contiguous_recv_buffer determines whether or not libtorrent should receive data from peers into a contiguous intermediate buffer, to then copy blocks into disk buffers from, or to make many smaller calls to read(), each time passing in the specific buffer the data belongs in. When downloading at high rates, the latter may save some time copying data. When seeding at high rates, all incoming traffic consists of a very large number of tiny packets, and enabling contiguous_recv_buffer will provide higher performance. When this is enabled, it will only be used when seeding to peers, since that's when it provides performance improvements.
ban_web_seeds 51 when true, web seeds sending bad data will be banned
allow_partial_disk_writes 52 when set to false, the write_cache_line_size will apply across piece boundaries. this is a bad idea unless the piece picker also is configured to have an affinity to pick pieces belonging to the same write cache line as is configured in the disk cache.
force_proxy 53 If true, disables any communication that's not going over a proxy. Enabling this requires a proxy to be configured as well, see proxy_type and proxy_hostname settings. The listen sockets are closed, and incoming connections will only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the same machine as the tracker proxy).
support_share_mode 54 if false, prevents libtorrent to advertise share-mode support
support_merkle_torrents 55 if this is false, don't advertise support for the Tribler merkle tree piece message
report_redundant_bytes 56 if this is true, the number of redundant bytes is sent to the tracker
listen_system_port_fallback 57 if this is true, libtorrent will fall back to listening on a port chosen by the operating system (i.e. binding to port 0). If a failure is preferred, set this to false.
use_disk_cache_pool 58 use_disk_cache_pool enables using a pool allocator for disk cache blocks. Enabling it makes the cache perform better at high throughput. It also makes the cache less likely and slower at returning memory back to the system, once allocated.
announce_crypto_support 59 when this is true, and incoming encrypted connections are enabled, &supportcrypt=1 is included in http tracker announces
enable_upnp 60

Starts and stops the UPnP service. When started, the listen port and the DHT port are attempted to be forwarded on local UPnP router devices.

The upnp object returned by start_upnp() can be used to add and remove arbitrary port mappings. Mapping status is returned through the portmap_alert and the portmap_error_alert. The object will be valid until stop_upnp() is called. See upnp and nat pmp.

enable_natpmp 61

Starts and stops the NAT-PMP service. When started, the listen port and the DHT port are attempted to be forwarded on the router through NAT-PMP.

The natpmp object returned by start_natpmp() can be used to add and remove arbitrary port mappings. Mapping status is returned through the portmap_alert and the portmap_error_alert. The object will be valid until stop_natpmp() is called. See upnp and nat pmp.

enable_lsd 62 Starts and stops Local Service Discovery. This service will broadcast the infohashes of all the non-private torrents on the local network to look for peers on the same swarm within multicast reach.
enable_dht 63 starts the dht node and makes the trackerless service available to torrents.
prefer_rc4 64 if the allowed encryption level is both, setting this to true will prefer rc4 if both methods are offered, plaintext otherwise
proxy_hostnames 65 if true, hostname lookups are done via the configured proxy (if any). This is only supported by SOCKS5 and HTTP.
proxy_peer_connections 66 if true, peer connections are made (and accepted) over the configured proxy, if any. Web seeds as well as regular bittorrent peer connections are considered "peer connections". Anything transporting actual torrent payload (trackers and DHT traffic are not considered peer connections).
auto_sequential 67 if this setting is true, torrents with a very high availability of pieces (and seeds) are downloaded sequentially. This is more efficient for the disk I/O. With many seeds, the download order is unlikely to matter anyway
proxy_tracker_connections 68 if true, tracker connections are made over the configured proxy, if any.
max_bool_setting_internal 69  

enum int_types

Declared in "libtorrent/settings_pack.hpp"

name value description
tracker_completion_timeout   tracker_completion_timeout is the number of seconds the tracker connection will wait from when it sent the request until it considers the tracker to have timed-out. Default value is 60 seconds.
tracker_receive_timeout 1 tracker_receive_timeout is the number of seconds to wait to receive any data from the tracker. If no data is received for this number of seconds, the tracker will be considered as having timed out. If a tracker is down, this is the kind of timeout that will occur.
stop_tracker_timeout 2 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
tracker_maximum_response_length 3 this is the maximum number of bytes in a tracker response. If a response size passes this number of bytes it will be rejected and the connection will be closed. On gzipped responses this size is measured on the uncompressed data. So, if you get 20 bytes of gzip response that'll expand to 2 megabytes, it will be interrupted before the entire response has been uncompressed (assuming the limit is lower than 2 megs).
piece_timeout 4 the number of seconds from a request is sent until it times out if no piece response is returned.
request_timeout 5 the number of seconds one block (16kB) is expected to be received within. If it's not, the block is requested from a different peer
request_queue_time 6 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.
max_allowed_in_request_queue 7 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.
max_out_request_queue 8 max_out_request_queue is the maximum number of outstanding requests to send to a peer. This limit takes precedence over request_queue_time. i.e. no matter the download speed, the number of outstanding requests will never exceed this limit.
whole_pieces_threshold 9 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.
peer_timeout 10 peer_timeout is the number of seconds the peer connection should wait (for any activity on the peer connection) before closing it due to time out. This defaults to 120 seconds, since that's what's specified in the protocol specification. After half the time out, a keep alive message is sent.
urlseed_timeout 11 same as peer_timeout, but only applies to url-seeds. this is usually set lower, because web servers are expected to be more reliable.
urlseed_pipeline_size 12 controls the pipelining size of url-seeds. i.e. the number of HTTP request to keep outstanding before waiting for the first one to complete. It's common for web servers to limit this to a relatively low number, like 5
urlseed_wait_retry 13 time to wait until a new retry of a web seed takes place
file_pool_size 14 sets the upper limit on the total number of files this session will keep open. The reason why files are left open at all is that some anti virus software hooks on every file close, and scans the file for viruses. deferring the closing of the files will be the difference between a usable system and a completely hogged down system. Most operating systems also has a limit on the total number of file descriptors a process may have open.
max_failcount 15 max_failcount is the maximum times we try to connect to a peer before stop connecting again. If a peer succeeds, the failcounter is reset. If a peer is retrieved from a peer source (other than DHT) the failcount is decremented by one, allowing another try.
min_reconnect_time 16 the number of seconds to wait to reconnect to a peer. this time is multiplied with the failcount.
peer_connect_timeout 17 peer_connect_timeout the number of seconds to wait after a connection attempt is initiated to a peer until it is considered as having timed out. This setting is especially important in case the number of half-open connections are limited, since stale half-open connection may delay the connection of other peers considerably.
connection_speed 18 connection_speed is the number of connection attempts that are made per second. If a number < 0 is specified, it will default to 200 connections per second. If 0 is specified, it means don't make outgoing connections at all.
inactivity_timeout 19 if a peer is uninteresting and uninterested for longer than this number of seconds, it will be disconnected. default is 10 minutes
unchoke_interval 20 unchoke_interval is the number of seconds between chokes/unchokes. On this interval, peers are re-evaluated for being choked/unchoked. This is defined as 30 seconds in the protocol, and it should be significantly longer than what it takes for TCP to ramp up to it's max rate.
optimistic_unchoke_interval 21 optimistic_unchoke_interval is the number of seconds between each optimistic unchoke. On this timer, the currently optimistically unchoked peer will change.
num_want 22 num_want is the number of peers we want from each tracker request. It defines what is sent as the &num_want= parameter to the tracker.
initial_picker_threshold 23 initial_picker_threshold specifies the number of pieces we need before we switch to rarest first picking. This defaults to 4, which means the 4 first pieces in any torrent are picked at random, the following pieces are picked in rarest first order.
allowed_fast_set_size 24 the number of allowed pieces to send to peers that supports the fast extensions
suggest_mode 25

suggest_mode controls whether or not libtorrent will send out suggest messages to create a bias of its peers to request certain pieces. The modes are:

  • no_piece_suggestions which is the default and will not send out suggest messages.
  • suggest_read_cache which will send out suggest messages for the most recent pieces that are in the read cache.
max_queued_disk_bytes 26 max_queued_disk_bytes is the maximum number of bytes, to be written to disk, that can wait in the disk I/O thread queue. This queue is only for waiting for the disk I/O thread to receive the job and either write it to disk or insert it in the write cache. When this limit is reached, the peer connections will stop reading data from their sockets, until the disk thread catches up. Setting this too low will severely limit your download rate.
handshake_timeout 27 the number of seconds to wait for a handshake response from a peer. If no response is received within this time, the peer is disconnected.
send_buffer_low_watermark 28

send_buffer_low_watermark the minimum send buffer target size (send buffer includes bytes pending being read from disk). For good and snappy seeding performance, set this fairly high, to at least fit a few blocks. This is essentially the initial window size which will determine how fast we can ramp up the send rate

if the send buffer has fewer bytes than send_buffer_watermark, we'll read another 16kB block onto it. If set too small, upload rate capacity will suffer. If set too high, memory will be wasted. The actual watermark may be lower than this in case the upload rate is low, this is the upper limit.

the current upload rate to a peer is multiplied by this factor to get the send buffer watermark. The factor is specified as a percentage. i.e. 50 -> 0.5 This product is clamped to the send_buffer_watermark setting to not exceed the max. For high speed upload, this should be set to a greater value than 100. For high capacity connections, setting this higher can improve upload performance and disk throughput. Setting it too high may waste RAM and create a bias towards read jobs over write jobs.

send_buffer_watermark 29  
send_buffer_watermark_factor 30  
choking_algorithm 31

choking_algorithm specifies which algorithm to use to determine which peers to unchoke.

The options for choking algorithms are:

  • fixed_slots_choker is the traditional choker with a fixed number of unchoke slots (as specified by settings_pack::unchoke_slots_limit).
  • rate_based_choker opens up unchoke slots based on the upload rate achieved to peers. The more slots that are opened, the marginal upload rate required to open up another slot increases.
  • bittyrant_choker attempts to optimize download rate by finding the reciprocation rate of each peer individually and prefers peers that gives the highest return on investment. It still allocates all upload capacity, but shuffles it around to the best peers first. For this choker to be efficient, you need to set a global upload rate limit (settings_pack::upload_rate_limit). For more information about this choker, see the paper. This choker is not fully implemented nor tested.

seed_choking_algorithm controls the seeding unchoke behavior. The available options are:

  • round_robin which round-robins the peers that are unchoked when seeding. This distributes the upload bandwidht uniformly and fairly. It minimizes the ability for a peer to download everything without redistributing it.
  • fastest_upload unchokes the peers we can send to the fastest. This might be a bit more reliable in utilizing all available capacity.
  • anti_leech prioritizes peers who have just started or are just about to finish the download. The intention is to force peers in the middle of the download to trade with each other.
seed_choking_algorithm 32  
cache_size 33

cache_size is the disk write and read cache. It is specified in units of 16 KiB blocks. Buffers that are part of a peer's send or receive buffer also count against this limit. Send and receive buffers will never be denied to be allocated, but they will cause the actual cached blocks to be flushed or evicted. If this is set to -1, the cache size is automatically set to the amount of physical RAM available in the machine divided by 8. If the amount of physical RAM cannot be determined, it's set to 1024 (= 16 MiB).

Disk buffers are allocated using a pool allocator, the number of blocks that are allocated at a time when the pool needs to grow can be specified in cache_buffer_chunk_size. Lower numbers saves memory at the expense of more heap allocations. If it is set to 0, the effective chunk size is proportional to the total cache size, attempting to strike a good balance between performance and memory usage. It defaults to 0. cache_expiry is the number of seconds from the last cached write to a piece in the write cache, to when it's forcefully flushed to disk. Default is 60 second.

On 32 bit builds, the effective cache size will be limited to 3/4 of 2 GiB to avoid exceeding the virtual address space limit.

cache_buffer_chunk_size 34  
cache_expiry 35  
deprecated11 36  
disk_io_write_mode 37

determines how files are opened when they're in read only mode versus read and write mode. The options are:

enable_os_cache
This is the default and files are opened normally, with the OS caching reads and writes.
disable_os_cache
This opens all files in no-cache mode. This corresponds to the OS not letting blocks for the files linger in the cache. This makes sense in order to avoid the bittorrent client to potentially evict all other processes' cache by simply handling high throughput and large files. If libtorrent's read cache is disabled, enabling this may reduce performance.

One reason to disable caching is that it may help the operating system from growing its file cache indefinitely.

disk_io_read_mode 38  
outgoing_port 39

this is the first port to use for binding outgoing connections to. This is useful for users that have routers that allow QoS settings based on local port. when binding outgoing connections to specific ports, num_outgoing_ports is the size of the range. It should be more than a few

Warning

setting outgoing ports will limit the ability to keep multiple connections to the same client, even for different torrents. It is not recommended to change this setting. Its main purpose is to use as an escape hatch for cheap routers with QoS capability but can only classify flows based on port numbers.

It is a range instead of a single port because of the problems with failing to reconnect to peers if a previous socket to that peer and port is in TIME_WAIT state.

num_outgoing_ports 40  
peer_tos 41 peer_tos determines the TOS byte set in the IP header of every packet sent to peers (including web seeds). The default value for this is 0x0 (no marking). One potentially useful TOS mark is 0x20, this represents the QBone scavenger service. For more details, see QBSS.
active_downloads 42

for auto managed torrents, these are the limits they are subject to. If there are too many torrents some of the auto managed ones will be paused until some slots free up. active_downloads and active_seeds controls how many active seeding and downloading torrents the queuing mechanism allows. The target number of active torrents is min(active_downloads + active_seeds, active_limit). active_downloads and active_seeds are upper limits on the number of downloading torrents and seeding torrents respectively. Setting the value to -1 means unlimited.

For example if there are 10 seeding torrents and 10 downloading torrents, and active_downloads is 4 and active_seeds is 4, there will be 4 seeds active and 4 downloading torrents. If the settings are active_downloads = 2 and active_seeds = 4, then there will be 2 downloading torrents and 4 seeding torrents active. Torrents that are not auto managed are not counted against these limits.

active_checking is the limit of number of simultaneous checking torrents.

active_limit is a hard limit on the number of active (auto managed) torrents. This limit also applies to slow torrents.

active_dht_limit is the max number of torrents to announce to the DHT. By default this is set to 88, which is no more than one DHT announce every 10 seconds.

active_tracker_limit is the max number of torrents to announce to their trackers. By default this is 360, which is no more than one announce every 5 seconds.

active_lsd_limit is the max number of torrents to announce to the local network over the local service discovery protocol. By default this is 80, which is no more than one announce every 5 seconds (assuming the default announce interval of 5 minutes).

You can have more torrents active, even though they are not announced to the DHT, lsd or their tracker. If some peer knows about you for any reason and tries to connect, it will still be accepted, unless the torrent is paused, which means it won't accept any connections.

active_loaded_limit is the number of torrents that are allowed to be loaded at any given time. Note that a torrent can be active even though it's not loaded. If an unloaded torrents finds a peer that wants to access it, the torrent will be loaded on demand, using a user-supplied callback function. If the feature of unloading torrents is not enabled, this setting have no effect. If this limit is set to 0, it means unlimited. For more information, see dynamic loading of torrent files.

active_seeds 43  
active_checking 44  
active_dht_limit 45  
active_tracker_limit 46  
active_lsd_limit 47  
active_limit 48  
active_loaded_limit 49  
auto_manage_interval 50 auto_manage_interval is the number of seconds between the torrent queue is updated, and rotated.
seed_time_limit 51 this is the limit on the time a torrent has been an active seed (specified in seconds) before it is considered having met the seed limit criteria. See queuing.
auto_scrape_interval 52

auto_scrape_interval is the number of seconds between scrapes of queued torrents (auto managed and paused torrents). Auto managed torrents that are paused, are scraped regularly in order to keep track of their downloader/seed ratio. This ratio is used to determine which torrents to seed and which to pause.

auto_scrape_min_interval is the minimum number of seconds between any automatic scrape (regardless of torrent). In case there are a large number of paused auto managed torrents, this puts a limit on how often a scrape request is sent.

auto_scrape_min_interval 53  
max_peerlist_size 54

max_peerlist_size is the maximum number of peers in the list of known peers. These peers are not necessarily connected, so this number should be much greater than the maximum number of connected peers. Peers are evicted from the cache when the list grows passed 90% of this limit, and once the size hits the limit, peers are no longer added to the list. If this limit is set to 0, there is no limit on how many peers we'll keep in the peer list.

max_paused_peerlist_size is the max peer list size used for torrents that are paused. This default to the same as max_peerlist_size, but can be used to save memory for paused torrents, since it's not as important for them to keep a large peer list.

max_paused_peerlist_size 55  
min_announce_interval 56 this is the minimum allowed announce interval for a tracker. This is specified in seconds and is used as a sanity check on what is returned from a tracker. It mitigates hammering misconfigured trackers.
auto_manage_startup 57 this is the number of seconds a torrent is considered active after it was started, regardless of upload and download speed. This is so that newly started torrents are not considered inactive until they have a fair chance to start downloading.
seeding_piece_quota 58 seeding_piece_quota is the number of pieces to send to a peer, when seeding, before rotating in another peer to the unchoke set. It defaults to 3 pieces, which means that when seeding, any peer we've sent more than this number of pieces to will be unchoked in favour of a choked peer.
max_rejects 59 TODO: deprecate this max_rejects is the number of piece requests we will reject in a row while a peer is choked before the peer is considered abusive and is disconnected.
recv_socket_buffer_size 60 recv_socket_buffer_size and send_socket_buffer_size specifies the buffer sizes set on peer sockets. 0 (which is the default) means the OS default (i.e. don't change the buffer sizes). The socket buffer sizes are changed using setsockopt() with SOL_SOCKET/SO_RCVBUF and SO_SNDBUFFER.
send_socket_buffer_size 61  
deprecated14 62  
read_cache_line_size 63

read_cache_line_size is the number of blocks to read into the read cache when a read cache miss occurs. Setting this to 0 is essentially the same thing as disabling read cache. The number of blocks read into the read cache is always capped by the piece boundary.

When a piece in the write cache has write_cache_line_size contiguous blocks in it, they will be flushed. Setting this to 1 effectively disables the write cache.

write_cache_line_size 64  
optimistic_disk_retry 65

optimistic_disk_retry is the number of seconds from a disk write errors occur on a torrent until libtorrent will take it out of the upload mode, to test if the error condition has been fixed.

libtorrent will only do this automatically for auto managed torrents.

You can explicitly take a torrent out of upload only mode using set_upload_mode().

max_suggest_pieces 66 max_suggest_pieces is the max number of suggested piece indices received from a peer that's remembered. If a peer floods suggest messages, this limit prevents libtorrent from using too much RAM. It defaults to 10.
local_service_announce_interval 67 local_service_announce_interval is the time between local network announces for a torrent. By default, when local service discovery is enabled a torrent announces itself every 5 minutes. This interval is specified in seconds.
dht_announce_interval 68 dht_announce_interval is the number of seconds between announcing torrents to the distributed hash table (DHT).
udp_tracker_token_expiry 69 udp_tracker_token_expiry is the number of seconds libtorrent will keep UDP tracker connection tokens around for. This is specified to be 60 seconds, and defaults to that. The higher this value is, the fewer packets have to be sent to the UDP tracker. In order for higher values to work, the tracker needs to be configured to match the expiration time for tokens.
default_cache_min_age 70 default_cache_min_age is the minimum number of seconds any read cache line is kept in the cache. This defaults to one second but may be greater if guided_read_cache is enabled. Having a lower bound on the time a cache line stays in the cache is an attempt to avoid swapping the same pieces in and out of the cache in case there is a shortage of spare cache space.
num_optimistic_unchoke_slots 71 num_optimistic_unchoke_slots is the number of optimistic unchoke slots to use. It defaults to 0, which means automatic. Having a higher number of optimistic unchoke slots mean you will find the good peers faster but with the trade-off to use up more bandwidth. When this is set to 0, libtorrent opens up 20% of your allowed upload slots as optimistic unchoke slots.
default_est_reciprocation_rate 72

default_est_reciprocation_rate is the assumed reciprocation rate from peers when using the BitTyrant choker. This defaults to 14 kiB/s. If set too high, you will over-estimate your peers and be more altruistic while finding the true reciprocation rate, if it's set too low, you'll be too stingy and waste finding the true reciprocation rate.

increase_est_reciprocation_rate specifies how many percent the estimated reciprocation rate should be increased by each unchoke interval a peer is still choking us back. This defaults to 20%. This only applies to the BitTyrant choker.

decrease_est_reciprocation_rate specifies how many percent the estimated reciprocation rate should be decreased by each unchoke interval a peer unchokes us. This default to 3%. This only applies to the BitTyrant choker.

increase_est_reciprocation_rate 73  
decrease_est_reciprocation_rate 74  
max_pex_peers 75 the max number of peers we accept from pex messages from a single peer. this limits the number of concurrent peers any of our peers claims to be connected to. If they claim to be connected to more than this, we'll ignore any peer that exceeds this limit
tick_interval 76 tick_interval specifies the number of milliseconds between internal ticks. This is the frequency with which bandwidth quota is distributed to peers. It should not be more than one second (i.e. 1000 ms). Setting this to a low value (around 100) means higher resolution bandwidth quota distribution, setting it to a higher value saves CPU cycles.
share_mode_target 77 share_mode_target specifies the target share ratio for share mode torrents. This defaults to 3, meaning we'll try to upload 3 times as much as we download. Setting this very high, will make it very conservative and you might end up not downloading anything ever (and not affecting your share ratio). It does not make any sense to set this any lower than 2. For instance, if only 3 peers need to download the rarest piece, it's impossible to download a single piece and upload it more than 3 times. If the share_mode_target is set to more than 3, nothing is downloaded.
upload_rate_limit 78

upload_rate_limit and download_rate_limit sets the session-global limits of upload and download rate limits, in bytes per second. By default peers on the local network are not rate limited.

A value of 0 means unlimited.

For fine grained control over rate limits, including making them apply to local peers, see peer classes.

download_rate_limit 79  
deprecated3 80  
deprecated4 81  
dht_upload_rate_limit 82 dht_upload_rate_limit sets the rate limit on the DHT. This is specified in bytes per second and defaults to 4000. For busy boxes with lots of torrents that requires more DHT traffic, this should be raised.
unchoke_slots_limit 83 unchoke_slots_limit is the max number of unchoked peers in the session. The number of unchoke slots may be ignored depending on what choking_algorithm is set to.
deprecated5 84  
connections_limit 85 connections_limit sets a global limit on the number of connections opened. The number of connections is set to a hard minimum of at least two per torrent, so if you set a too low connections limit, and open too many torrents, the limit will not be met.
connections_slack 86 connections_slack is the the number of incoming connections exceeding the connection limit to accept in order to potentially replace existing ones.
utp_target_delay 87

utp_target_delay is the target delay for uTP sockets in milliseconds. A high value will make uTP connections more aggressive and cause longer queues in the upload bottleneck. It cannot be too low, since the noise in the measurements would cause it to send too slow. The default is 50 milliseconds. utp_gain_factor is the number of bytes the uTP congestion window can increase at the most in one RTT. This defaults to 300 bytes. If this is set too high, the congestion controller reacts too hard to noise and will not be stable, if it's set too low, it will react slow to congestion and not back off as fast.

utp_min_timeout is the shortest allowed uTP socket timeout, specified in milliseconds. This defaults to 500 milliseconds. The timeout depends on the RTT of the connection, but is never smaller than this value. A connection times out when every packet in a window is lost, or when a packet is lost twice in a row (i.e. the resent packet is lost as well).

The shorter the timeout is, the faster the connection will recover from this situation, assuming the RTT is low enough. utp_syn_resends is the number of SYN packets that are sent (and timed out) before giving up and closing the socket. utp_num_resends is the number of times a packet is sent (and lossed or timed out) before giving up and closing the connection. utp_connect_timeout is the number of milliseconds of timeout for the initial SYN packet for uTP connections. For each timed out packet (in a row), the timeout is doubled. utp_loss_multiplier controls how the congestion window is changed when a packet loss is experienced. It's specified as a percentage multiplier for cwnd. By default it's set to 50 (i.e. cut in half). Do not change this value unless you know what you're doing. Never set it higher than 100.

utp_gain_factor 88  
utp_min_timeout 89  
utp_syn_resends 90  
utp_fin_resends 91  
utp_num_resends 92  
utp_connect_timeout 93  
deprecated6 94  
utp_loss_multiplier 95  
mixed_mode_algorithm 96 The mixed_mode_algorithm determines how to treat TCP connections when there are uTP connections. Since uTP is designed to yield to TCP, there's an inherent problem when using swarms that have both TCP and uTP connections. If nothing is done, uTP connections would often be starved out for bandwidth by the TCP connections. This mode is prefer_tcp. The peer_proportional mode simply looks at the current throughput and rate limits all TCP connections to their proportional share based on how many of the connections are TCP. This works best if uTP connections are not rate limited by the global rate limiter (which they aren't by default).
listen_queue_size 97 listen_queue_size is the value passed in to listen() for the listen socket. It is the number of outstanding incoming connections to queue up while we're not actively waiting for a connection to be accepted. The default is 5 which should be sufficient for any normal client. If this is a high performance server which expects to receive a lot of connections, or used in a simulator or test, it might make sense to raise this number. It will not take affect until the listen_interfaces settings is updated.
torrent_connect_boost 98 torrent_connect_boost is the number of peers to try to connect to immediately when the first tracker response is received for a torrent. This is a boost to given to new torrents to accelerate them starting up. The normal connect scheduler is run once every second, this allows peers to be connected immediately instead of waiting for the session tick to trigger connections. This may not be set higher than 255.
alert_queue_size 99 alert_queue_size is the maximum number of alerts queued up internally. If alerts are not popped, the queue will eventually fill up to this level.
max_metadata_size 100 max_metadata_size is the maximum allowed size (in bytes) to be received by the metadata extension, i.e. magnet links.
deprecated9 101  
checking_mem_usage 102 the number of blocks to keep outstanding at any given time when checking torrents. Higher numbers give faster re-checks but uses more memory. Specified in number of 16 kiB blocks
predictive_piece_announce 103 if set to > 0, pieces will be announced to other peers before they are fully downloaded (and before they are hash checked). The intention is to gain 1.5 potential round trip times per downloaded piece. When non-zero, this indicates how many milliseconds in advance pieces should be announced, before they are expected to be completed.
aio_threads 104 for some aio back-ends, aio_threads specifies the number of io-threads to use, and aio_max the max number of outstanding jobs.
aio_max 105  
network_threads 106 network_threads is the number of threads to use to call async_write_some (i.e. send) on peer connection sockets. When seeding at extremely high rates, this may become a bottleneck, and setting this to 2 or more may parallelize that cost. When using SSL torrents, all encryption for outgoing traffic is done within the socket send functions, and this will help parallelizing the cost of SSL encryption as well.
ssl_listen 107 ssl_listen sets the listen port for SSL connections. If this is set to 0, no SSL listen port is opened. Otherwise a socket is opened on this port. This setting is only taken into account when opening the regular listen port, and won't re-open the listen socket simply by changing this setting.
tracker_backoff 108

tracker_backoff determines how aggressively to back off from retrying failing trackers. This value determines x in the following formula, determining the number of seconds to wait until the next retry:

delay = 5 + 5 * x / 100 * fails^2

This setting may be useful to make libtorrent more or less aggressive in hitting trackers.

share_ratio_limit 109 when a seeding torrent reaches either the share ratio (bytes up / bytes down) or the seed time ratio (seconds as seed / seconds as downloader) or the seed time limit (seconds as seed) it is considered done, and it will leave room for other torrents. These are specified as percentages. Torrents that are considered done will still be allowed to be seeded, they just won't have priority anymore. For more, see queuing.
seed_time_ratio_limit 110  
peer_turnover 111 peer_turnover is the percentage of peers to disconnect every turnover peer_turnover_interval (if we're at the peer limit), this is specified in percent when we are connected to more than limit * peer_turnover_cutoff peers disconnect peer_turnover fraction of the peers. It is specified in percent peer_turnover_interval is the interval (in seconds) between optimistic disconnects if the disconnects happen and how many peers are disconnected is controlled by peer_turnover and peer_turnover_cutoff
peer_turnover_cutoff 112  
peer_turnover_interval 113  
connect_seed_every_n_download 114 this setting controls the priority of downloading torrents over seeding or finished torrents when it comes to making peer connections. Peer connections are throttled by the connection_speed and the half-open connection limit. This makes peer connections a limited resource. Torrents that still have pieces to download are prioritized by default, to avoid having many seeding torrents use most of the connection attempts and only give one peer every now and then to the downloading torrent. libtorrent will loop over the downloading torrents to connect a peer each, and every n:th connection attempt, a finished torrent is picked to be allowed to connect to a peer. This setting controls n.
max_http_recv_buffer_size 115 the max number of bytes to allow an HTTP response to be when announcing to trackers or downloading .torrent files via the url provided in add_torrent_params.
max_retry_port_bind 116 if binding to a specific port fails, should the port be incremented by one and tried again? This setting specifies how many times to retry a failed port bind
alert_mask 117 a bitmask combining flags from alert::category_t defining which kinds of alerts to receive
out_enc_policy 118

control the settings for incoming and outgoing connections respectively. see enc_policy enum for the available options. Keep in mind that protocol encryption degrades performance in several respects:

  1. It prevents "zero copy" disk buffers being sent to peers, since each peer needs to mutate the data (i.e. encrypt it) the data must be copied per peer connection rather than sending the same buffer to multiple peers.
  2. The encryption itself requires more CPU than plain bittorrent protocol. The highest cost is the Diffie Hellman exchange on connection setup.
  3. The encryption handshake adds several round-trips to the connection setup, and delays transferring data.
in_enc_policy 119  
allowed_enc_level 120 determines the encryption level of the connections. This setting will adjust which encryption scheme is offered to the other peer, as well as which encryption scheme is selected by the client. See enc_level enum for options.
inactive_down_rate 121 the download and upload rate limits for a torrent to be considered active by the queuing mechanism. A torrent whose download rate is less than inactive_down_rate and whose upload rate is less than inactive_up_rate for auto_manage_startup seconds, is considered inactive, and another queued torrent may be started. This logic is disabled if dont_count_slow_torrents is false.
inactive_up_rate 122  
proxy_type 123 proxy to use, defaults to none. see proxy_type_t.
proxy_port 124 the port of the proxy server
i2p_port 125 sets the i2p SAM bridge port to connect to. set the hostname with the i2p_hostname setting.
cache_size_volatile 126 this determines the max number of volatile disk cache blocks. If the number of volatile blocks exceed this limit, other volatile blocks will start to be evicted. A disk cache block is volatile if it has low priority, and should be one of the first blocks to be evicted under pressure. For instance, blocks pulled into the cache as the result of calculating a piece hash are volatile. These blocks don't represent potential interest among peers, so the value of keeping them in the cache is limited.
urlseed_max_request_bytes 127 The maximum request range of an url seed in bytes. This value defines the largest possible sequential web seed request. Default is 16 * 1024 * 1024. Lower values are possible but will be ignored if they are lower then piece size. This value should be related to your download speed to prevent libtorrent from creating too many expensive http requests per second. You can select a value as high as you want but keep in mind that libtorrent can't create parallel requests if the first request did already select the whole file. If you combine bittorrent seeds with web seeds and pick strategies like rarest first you may find your web seed requests split into smaller parts because we don't download already picked pieces twice.
web_seed_name_lookup_retry 128 time to wait until a new retry of a web seed name lookup
close_file_interval 129 the number of seconds between closing the file opened the longest ago. 0 means to disable the feature. The purpose of this is to periodically close files to trigger the operating system flushing disk cache. Specifically it has been observed to be required on windows to not have the disk cache grow indefinitely. This defaults to 120 seconds on windows, and disabled on other systems.
utp_cwnd_reduce_timer 130 When uTP experiences packet loss, it will reduce the congestion window, and not reduce it again for this many milliseconds, even if experiencing another lost packet.
max_int_setting_internal 131  

enum settings_counts_t

Declared in "libtorrent/settings_pack.hpp"

name value description
num_string_settings    
num_bool_settings    
num_int_settings    

enum suggest_mode_t

Declared in "libtorrent/settings_pack.hpp"

name value description
no_piece_suggestions 0  
suggest_read_cache 1  

enum choking_algorithm_t

Declared in "libtorrent/settings_pack.hpp"

name value description
fixed_slots_choker 0  
rate_based_choker 2  
bittyrant_choker 3  

enum seed_choking_algorithm_t

Declared in "libtorrent/settings_pack.hpp"

name value description
round_robin 0  
fastest_upload 1  
anti_leech 2  

enum io_buffer_mode_t

Declared in "libtorrent/settings_pack.hpp"

name value description
enable_os_cache 0  
deprecated 1  
disable_os_cache 2  

enum bandwidth_mixed_algo_t

Declared in "libtorrent/settings_pack.hpp"

name value description
prefer_tcp 0 disables the mixed mode bandwidth balancing
peer_proportional 1 does not throttle uTP, throttles TCP to the same proportion of throughput as there are TCP connections

enum enc_policy

Declared in "libtorrent/settings_pack.hpp"

name value description
pe_forced 0 Only encrypted connections are allowed. Incoming connections that are not encrypted are closed and if the encrypted outgoing connection fails, a non-encrypted retry will not be made.
pe_enabled 1 encrypted connections are enabled, but non-encrypted connections are allowed. An incoming non-encrypted connection will be accepted, and if an outgoing encrypted connection fails, a non- encrypted connection will be tried.
pe_disabled 2 only non-encrypted connections are allowed.

enum enc_level

Declared in "libtorrent/settings_pack.hpp"

name value description
pe_plaintext 1 use only plaintext encryption
pe_rc4 2 use only rc4 encryption
pe_both 3 allow both

enum proxy_type_t

Declared in "libtorrent/settings_pack.hpp"

name value description
none 0 This is the default, no proxy server is used, all other fields are ignored.
socks4 1 The server is assumed to be a SOCKS4 server that requires a username.
socks5 2 The server is assumed to be a SOCKS5 server (RFC 1928) that does not require any authentication. The username and password are ignored.
socks5_pw 3 The server is assumed to be a SOCKS5 server that supports plain text username and password authentication (RFC 1929). The username and password specified may be sent to the proxy if it requires.
http 4 The server is assumed to be an HTTP proxy. If the transport used for the connection is non-HTTP, the server is assumed to support the CONNECT method. i.e. for web seeds and HTTP trackers, a plain proxy will suffice. The proxy is assumed to not require authorization. The username and password will not be used.
http_pw 5 The server is assumed to be an HTTP proxy that requires user authorization. The username and password will be sent to the proxy.
i2p_proxy 6 route through a i2p SAM proxy

min_memory_usage() high_performance_seed()

Declared in "libtorrent/session.hpp"

void min_memory_usage (settings_pack& set);
void high_performance_seed (settings_pack& set);

The default values of the session settings are set for a regular bittorrent client running on a desktop system. There are functions that can set the session settings to pre set settings for other environments. These can be used for the basis, and should be tweaked to fit your needs better.

min_memory_usage returns settings that will use the minimal amount of RAM, at the potential expense of upload and download performance. It adjusts the socket buffer sizes, disables the disk cache, lowers the send buffer watermarks so that each connection only has at most one block in use at any one time. It lowers the outstanding blocks send to the disk I/O thread so that connections only have one block waiting to be flushed to disk at any given time. It lowers the max number of peers in the peer list for torrents. It performs multiple smaller reads when it hashes pieces, instead of reading it all into memory before hashing.

This configuration is inteded to be the starting point for embedded devices. It will significantly reduce memory usage.

high_performance_seed returns settings optimized for a seed box, serving many peers and that doesn't do any downloading. It has a 128 MB disk cache and has a limit of 400 files in its file pool. It support fast upload rates by allowing large send buffers.

setting_by_name() name_for_setting()

Declared in "libtorrent/settings_pack.hpp"

char const* name_for_setting (int s);
int setting_by_name (std::string const& name);

default_settings()

Declared in "libtorrent/settings_pack.hpp"

settings_pack default_settings ();

returns a settings_pack with every setting set to its default value

libtorrent-rasterbar-1.1.13/docs/reference-Storage.html000066400000000000000000000713311351156116000231260ustar00rootroot00000000000000 reference-Storage.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Storage

storage_params

Declared in "libtorrent/storage_defs.hpp"

see default_storage::default_storage()

struct storage_params
{
   storage_params ();

   file_storage const* files;
   file_storage const* mapped_files;
   std::string path;
   file_pool* pool;
   storage_mode_t mode;
   std::vector<boost::uint8_t> const* priorities;
   torrent_info const* info;
};

file_slice

Declared in "libtorrent/file_storage.hpp"

represents a window of a file in a torrent.

The file_index refers to the index of the file (in the torrent_info). To get the path and filename, use file_path() and give the file_index as argument. The offset is the byte offset in the file where the range starts, and size is the number of bytes this range is. The size + offset will never be greater than the file size.

struct file_slice
{
   int file_index;
   boost::int64_t offset;
   boost::int64_t size;
};
file_index
the index of the file
offset
the offset from the start of the file, in bytes
size
the size of the window, in bytes

file_storage

Declared in "libtorrent/file_storage.hpp"

The file_storage class represents a file list and the piece size. Everything necessary to interpret a regular bittorrent storage file structure.

class file_storage
{
   bool is_valid () const;
   void reserve (int num_files);
   void add_file (std::string const& path, boost::int64_t file_size, int file_flags = 0
      , std::time_t mtime = 0, std::string const& symlink_path = "");
   void add_file_borrow (char const* filename, int filename_len
      , std::string const& path, boost::int64_t file_size
      , boost::uint32_t file_flags = 0, char const* filehash = 0
      , boost::int64_t mtime = 0, std::string const& symlink_path = "");
   void rename_file (int index, std::string const& new_filename);
   std::vector<file_slice> map_block (int piece, boost::int64_t offset
      , int size) const;
   peer_request map_file (int file, boost::int64_t offset, int size) const;
   int num_files () const;
   boost::int64_t total_size () const;
   void set_num_pieces (int n);
   int num_pieces () const;
   void set_piece_length (int l);
   int piece_length () const;
   int piece_size (int index) const;
   std::string const& name () const;
   void set_name (std::string const& n);
   void swap (file_storage& ti);
   void unload ();
   bool is_loaded () const;
   void optimize (int pad_file_limit = -1, int alignment = -1
      , bool tail_padding = false);
   sha1_hash hash (int index) const;
   std::string file_name (int index) const;
   boost::int64_t file_offset (int index) const;
   time_t mtime (int index) const;
   bool pad_file_at (int index) const;
   std::string const& symlink (int index) const;
   std::string file_path (int index, std::string const& save_path = "") const;
   boost::int64_t file_size (int index) const;
   boost::uint32_t file_path_hash (int index, std::string const& save_path) const;
   void all_path_hashes (boost::unordered_set<boost::uint32_t>& table) const;
   std::vector<std::string> const& paths () const;
   int file_flags (int index) const;
   bool file_absolute_path (int index) const;
   int file_index_at_offset (boost::int64_t offset) const;
   char const* file_name_ptr (int index) const;
   int file_name_len (int index) const;
   void apply_pointer_offset (ptrdiff_t off);

   enum flags_t
   {
      pad_file,
      attribute_hidden,
      attribute_executable,
      attribute_symlink,
   };

   enum file_flags_t
   {
      flag_pad_file,
      flag_hidden,
      flag_executable,
      flag_symlink,
   };
};

is_valid()

bool is_valid () const;

returns true if the piece length has been initialized on the file_storage. This is typically taken as a proxy of whether the file_storage as a whole is initialized or not.

reserve()

void reserve (int num_files);

allocates space for num_files in the internal file list. This can be used to avoid reallocating the internal file list when the number of files to be added is known up-front.

add_file() add_file_borrow()

void add_file (std::string const& path, boost::int64_t file_size, int file_flags = 0
      , std::time_t mtime = 0, std::string const& symlink_path = "");
void add_file_borrow (char const* filename, int filename_len
      , std::string const& path, boost::int64_t file_size
      , boost::uint32_t file_flags = 0, char const* filehash = 0
      , boost::int64_t mtime = 0, std::string const& symlink_path = "");

Adds a file to the file storage. The add_file_borrow version expects that filename points to a string of filename_len bytes that is the file name (without a path) of the file that's being added. This memory is borrowed, i.e. it is the caller's responsibility to make sure it stays valid throughout the lifetime of this file_storage object or any copy of it. The same thing applies to filehash, which is an optional pointer to a 20 byte binary SHA-1 hash of the file.

if filename is NULL, the filename from path is used and not borrowed. In this case filename_len is ignored.

The path argument is the full path (in the torrent file) to the file to add. Note that this is not supposed to be an absolute path, but it is expected to include the name of the torrent as the first path element.

file_size is the size of the file in bytes.

The file_flags argument sets attributes on the file. The file attributes is an extension and may not work in all bittorrent clients.

For possible file attributes, see file_storage::flags_t.

The mtime argument is optional and can be set to 0. If non-zero, it is the posix time of the last modification time of this file.

symlink_path is the path the file is a symlink to. To make this a symlink you also need to set the file_storage::flag_symlink file flag.

If more files than one are added, certain restrictions to their paths apply. In a multi-file file storage (torrent), all files must share the same root directory.

That is, the first path element of all files must be the same. This shared path element is also set to the name of the torrent. It can be changed by calling set_name.

rename_file()

void rename_file (int index, std::string const& new_filename);

renames the file at index to new_filename. Keep in mind that filenames are expected to be UTF-8 encoded.

map_block()

std::vector<file_slice> map_block (int piece, boost::int64_t offset
      , int size) const;

returns a list of file_slice objects representing the portions of files the specified piece index, byte offset and size range overlaps. this is the inverse mapping of map_file().

Preconditions of this function is that the input range is within the torrents address space. piece may not be negative and

piece * piece_size + offset + size

may not exceed the total size of the torrent.

map_file()

peer_request map_file (int file, boost::int64_t offset, int size) const;

returns a peer_request representing the piece index, byte offset and size the specified file range overlaps. This is the inverse mapping over map_block(). Note that the peer_request return type is meant to hold bittorrent block requests, which may not be larger than 16 kiB. Mapping a range larger than that may return an overflown integer.

num_files()

int num_files () const;

returns the number of files in the file_storage

total_size()

boost::int64_t total_size () const;

returns the total number of bytes all the files in this torrent spans

num_pieces() set_num_pieces()

void set_num_pieces (int n);
int num_pieces () const;

set and get the number of pieces in the torrent

piece_length() set_piece_length()

void set_piece_length (int l);
int piece_length () const;

set and get the size of each piece in this torrent. This size is typically an even power of 2. It doesn't have to be though. It should be divisible by 16 kiB however.

piece_size()

int piece_size (int index) const;

returns the piece size of index. This will be the same as piece_length(), except for the last piece, which may be shorter.

set_name() name()

std::string const& name () const;
void set_name (std::string const& n);

set and get the name of this torrent. For multi-file torrents, this is also the name of the root directory all the files are stored in.

swap()

void swap (file_storage& ti);

swap all content of this with ti.

unload()

void unload ();

deallocates most of the memory used by this instance, leaving it only partially usable

is_loaded()

bool is_loaded () const;

returns true when populated with at least one file

optimize()

void optimize (int pad_file_limit = -1, int alignment = -1
      , bool tail_padding = false);

if pad_file_limit >= 0, files larger than that limit will be padded, default is to not add any padding (-1). The alignment specifies the alignment files should be padded to. This defaults to the piece size (-1) but it may also make sense to set it to 16 kiB, or something divisible by 16 kiB. If pad_file_limit is 0, every file will be padded (except empty ones). tail_padding indicates whether aligned files also are padded at the end to make them end aligned. This is required for mutable torrents, since piece hashes are compared

file_path_hash()

boost::uint32_t file_path_hash (int index, std::string const& save_path) const;

returns the crc32 hash of file_path(index)

all_path_hashes()

void all_path_hashes (boost::unordered_set<boost::uint32_t>& table) const;

this will add the CRC32 hash of all directory entries to the table. No filename will be included, just directories. Every depth of directories are added separately to allow test for collisions with files at all levels. i.e. if one path in the torrent is foo/bar/baz, the CRC32 hashes for foo, foo/bar and foo/bar/baz will be added to the set.

file_flags()

int file_flags (int index) const;

returns a bitmask of flags from file_flags_t that apply to file at index.

file_absolute_path()

bool file_absolute_path (int index) const;

returns true if the file at the specified index has been renamed to have an absolute path, i.e. is not anchored in the save path of the torrent.

file_index_at_offset()

int file_index_at_offset (boost::int64_t offset) const;

returns the index of the file at the given offset in the torrent

file_name_len() file_name_ptr()

char const* file_name_ptr (int index) const;
int file_name_len (int index) const;

low-level function. returns a pointer to the internal storage for the filename. This string may not be null terminated! the file_name_len() function returns the length of the filename.

apply_pointer_offset()

void apply_pointer_offset (ptrdiff_t off);

if the backing buffer changed for this storage, this is the pointer offset to add to any pointers to make them point into the new buffer

enum flags_t

Declared in "libtorrent/file_storage.hpp"

name value description
pad_file 1 the file is a pad file. It's required to contain zeros at it will not be saved to disk. Its purpose is to make the following file start on a piece boundary.
attribute_hidden 2 this file has the hidden attribute set. This is primarily a windows attribute
attribute_executable 4 this file has the executable attribute set.
attribute_symlink 8 this file is a symbolic link. It should have a link target string associated with it.

enum file_flags_t

Declared in "libtorrent/file_storage.hpp"

name value description
flag_pad_file 1 this file is a pad file. The creator of the torrent promises the file is entirely filled with zeros and does not need to be downloaded. The purpose is just to align the next file to either a block or piece boundary.
flag_hidden 2 this file is hidden (sets the hidden attribute on windows)
flag_executable 4 this file is executable (sets the executable bit on posix like systems)
flag_symlink 8 this file is a symlink. The symlink target is specified in a separate field

default_storage_constructor()

Declared in "libtorrent/storage_defs.hpp"

storage_interface* default_storage_constructor (storage_params const&);

the constructor function for the regular file storage. This is the default value for add_torrent_params::storage.

disabled_storage_constructor()

Declared in "libtorrent/storage_defs.hpp"

storage_interface* disabled_storage_constructor (storage_params const&);

the constructor function for the disabled storage. This can be used for testing and benchmarking. It will throw away any data written to it and return garbage for anything read from it.

zero_storage_constructor()

Declared in "libtorrent/storage_defs.hpp"

storage_interface* zero_storage_constructor (storage_params const&);

enum storage_mode_t

Declared in "libtorrent/storage_defs.hpp"

name value description
storage_mode_allocate 0 All pieces will be written to their final position, all files will be allocated in full when the torrent is first started. This is done with fallocate() and similar calls. This mode minimizes fragmentation.
storage_mode_sparse 1 All pieces will be written to the place where they belong and sparse files will be used. This is the recommended, and default mode.
libtorrent-rasterbar-1.1.13/docs/reference-Utility.html000066400000000000000000000516541351156116000231730ustar00rootroot00000000000000 reference-Utility.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

Utility

sha1_hash

Declared in "libtorrent/sha1_hash.hpp"

This type holds a SHA-1 digest or any other kind of 20 byte sequence. It implements a number of convenience functions, such as bit operations, comparison operators etc.

In libtorrent it is primarily used to hold info-hashes, piece-hashes, peer IDs, node IDs etc.

class sha1_hash
{
   sha1_hash ();
   static sha1_hash max ();
   static sha1_hash min ();
   explicit sha1_hash (char const* s);
   void assign (char const* str);
   explicit sha1_hash (std::string const& s);
   void assign (std::string const& s);
   char* data ();
   char const* data () const;
   void clear ();
   bool is_all_zeros () const;
   sha1_hash& operator<<= (int n);
   sha1_hash& operator>>= (int n);
   bool operator!= (sha1_hash const& n) const;
   bool operator< (sha1_hash const& n) const;
   bool operator== (sha1_hash const& n) const;
   sha1_hash operator~ () const;
   sha1_hash operator^ (sha1_hash const& n) const;
   sha1_hash& operator^= (sha1_hash const& n);
   sha1_hash operator& (sha1_hash const& n) const;
   sha1_hash& operator&= (sha1_hash const& n);
   sha1_hash& operator|= (sha1_hash const& n);
   boost::uint8_t& operator[] (int i);
   boost::uint8_t const& operator[] (int i) const;
   const_iterator begin () const;
   iterator begin ();
   iterator end ();
   const_iterator end () const;
   std::string to_string () const;
};

sha1_hash()

sha1_hash ();

constructs an all-zero sha1-hash

max()

static sha1_hash max ();

returns an all-F sha1-hash. i.e. the maximum value representable by a 160 bit number (20 bytes). This is a static member function.

min()

static sha1_hash min ();

returns an all-zero sha1-hash. i.e. the minimum value representable by a 160 bit number (20 bytes). This is a static member function.

assign() sha1_hash()

explicit sha1_hash (char const* s);
void assign (char const* str);
explicit sha1_hash (std::string const& s);
void assign (std::string const& s);

copies 20 bytes from the pointer provided, into the sha1-hash. The passed in string MUST be at least 20 bytes. NULL terminators are ignored, s is treated like a raw memory buffer.

clear()

void clear ();

set the sha1-hash to all zeros.

is_all_zeros()

bool is_all_zeros () const;

return true if the sha1-hash is all zero.

operator<<=()

sha1_hash& operator<<= (int n);

shift left n bits.

operator>>=()

sha1_hash& operator>>= (int n);

shift r n bits.

operator!=() operator<() operator==()

bool operator!= (sha1_hash const& n) const;
bool operator< (sha1_hash const& n) const;
bool operator== (sha1_hash const& n) const;

standard comparison operators

operator~()

sha1_hash operator~ () const;

returns a bit-wise negated copy of the sha1-hash

operator^()

sha1_hash operator^ (sha1_hash const& n) const;

returns the bit-wise XOR of the two sha1-hashes.

operator^=()

sha1_hash& operator^= (sha1_hash const& n);

in-place bit-wise XOR with the passed in sha1_hash.

operator&()

sha1_hash operator& (sha1_hash const& n) const;

returns the bit-wise AND of the two sha1-hashes.

operator&=()

sha1_hash& operator&= (sha1_hash const& n);

in-place bit-wise AND of the passed in sha1_hash

operator|=()

sha1_hash& operator|= (sha1_hash const& n);

in-place bit-wise OR of the two sha1-hash.

operator[]()

boost::uint8_t& operator[] (int i);
boost::uint8_t const& operator[] (int i) const;

accessors for specific bytes

begin() end()

const_iterator begin () const;
iterator begin ();
iterator end ();
const_iterator end () const;

start and end iterators for the hash. The value type of these iterators is boost::uint8_t.

to_string()

std::string to_string () const;

return a copy of the 20 bytes representing the sha1-hash as a std::string. It's still a binary string with 20 binary characters.

bitfield

Declared in "libtorrent/bitfield.hpp"

The bitfield type stores any number of bits as a bitfield in a heap allocated array.

struct bitfield
{
   bitfield (int bits);
   bitfield (bitfield const& rhs);
   bitfield (bitfield&& rhs);
   bitfield (int bits, bool val);
   bitfield ();
   bitfield (char const* b, int bits);
   void assign (char const* b, int bits);
   bool operator[] (int index) const;
   bool get_bit (int index) const;
   void clear_bit (int index);
   void set_bit (int index);
   bool all_set () const;
   bool none_set () const;
   int size () const;
   int num_words () const;
   bool empty () const;
   char const* data () const;
   bitfield& operator= (bitfield const& rhs);
   int count () const;
};

bitfield()

bitfield (int bits);
bitfield (bitfield const& rhs);
bitfield (bitfield&& rhs);
bitfield (int bits, bool val);
bitfield ();
bitfield (char const* b, int bits);

constructs a new bitfield. The default constructor creates an empty bitfield. bits is the size of the bitfield (specified in bits). val is the value to initialize the bits to. If not specified all bits are initialized to 0.

The constructor taking a pointer b and bits copies a bitfield from the specified buffer, and bits number of bits (rounded up to the nearest byte boundary).

assign()

void assign (char const* b, int bits);

copy bitfield from buffer b of bits number of bits, rounded up to the nearest byte boundary.

operator[]()

bool operator[] (int index) const;

query bit at index. Returns true if bit is 1, otherwise false.

set_bit() clear_bit()

void clear_bit (int index);
void set_bit (int index);

set bit at index to 0 (clear_bit) or 1 (set_bit).

all_set()

bool all_set () const;

returns true if all bits in the bitfield are set

size()

int size () const;

returns the size of the bitfield in bits.

empty()

bool empty () const;

returns true if the bitfield has zero size.

data()

char const* data () const;

returns a pointer to the internal buffer of the bitfield.

operator=()

bitfield& operator= (bitfield const& rhs);

copy operator

count()

int count () const;

count the number of bits in the bitfield that are set to 1.

hasher

Declared in "libtorrent/hasher.hpp"

this is a SHA-1 hash class.

You use it by first instantiating it, then call update() to feed it with data. i.e. you don't have to keep the entire buffer of which you want to create the hash in memory. You can feed the hasher parts of it at a time. When You have fed the hasher with all the data, you call final() and it will return the sha1-hash of the data.

The constructor that takes a char const* and an integer will construct the sha1 context and feed it the data passed in.

If you want to reuse the hasher object once you have created a hash, you have to call reset() to reinitialize it.

The sha1-algorithm used was implemented by Steve Reid and released as public domain. For more info, see src/sha1.cpp.

class hasher
{
   hasher ();
   hasher (const char* data, int len);
   hasher& operator= (hasher const& h);
   hasher (hasher const& h);
   hasher& update (std::string const& data);
   hasher& update (const char* data, int len);
   sha1_hash final ();
   void reset ();
   ~hasher ();
};

hasher()

hasher (const char* data, int len);

this is the same as default constructing followed by a call to update(data, len).

update()

hasher& update (std::string const& data);
hasher& update (const char* data, int len);

append the following bytes to what is being hashed

final()

sha1_hash final ();

returns the SHA-1 digest of the buffers previously passed to update() and the hasher constructor.

reset()

void reset ();

restore the hasher state to be as if the hasher has just been default constructed.

hash_value()

Declared in "libtorrent/sha1_hash.hpp"

inline std::size_t hash_value (sha1_hash const& b);

operator<<()

Declared in "libtorrent/sha1_hash.hpp"

inline std::ostream& operator<< (std::ostream& os, sha1_hash const& peer);

print a sha1_hash object to an ostream as 40 hexadecimal digits

operator>>()

Declared in "libtorrent/sha1_hash.hpp"

inline std::istream& operator>> (std::istream& is, sha1_hash& peer);

read 40 hexadecimal digits from an istream into a sha1_hash

libtorrent-rasterbar-1.1.13/docs/reference-ed25519.html000066400000000000000000000124341351156116000225170ustar00rootroot00000000000000 reference-ed25519.rst
Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

home

ed25519

ed25519_create_seed()

Declared in "libtorrent/ed25519.hpp"

void ed25519_create_seed (unsigned char *seed);

ed25519_key_exchange() ed25519_create_keypair() ed25519_verify() ed25519_sign() ed25519_add_scalar()

Declared in "libtorrent/ed25519.hpp"

void ed25519_add_scalar (unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar);
void ed25519_key_exchange (unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key);
void ed25519_create_keypair (unsigned char *public_key, unsigned char *private_key, const unsigned char *seed);
int ed25519_verify (const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *private_key);
void ed25519_sign (unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key);
libtorrent-rasterbar-1.1.13/docs/reference.html000066400000000000000000000671261351156116000215330ustar00rootroot00000000000000 reference documentation

reference documentation

single-page version

Alerts

libtorrent-rasterbar-1.1.13/docs/rst.css000066400000000000000000000103521351156116000202160ustar00rootroot00000000000000.document a { border: none; color: black; } .document a:hover { background: none; } .document a.reference { color: #8D370A; border-bottom: dotted 1px #8D370A; } .document a.reference:hover { border-bottom: solid 1px #8D370A; background: #eee; } div.section { margin-bottom: 3em; } div.section div.section div.section { margin-bottom: 2em; } div.section p, div.section ul, div.section dl { } div.main-toc { column-count: 4; -webkit-column-count: 4; -moz-column-count: 4; column-width: 13em; -webkit-column-width: 13em; -moz-column-width: 13em; border: 1px solid #999; padding: 5px; margin-bottom: 10px; } .rubric { margin-top: 5px; margin-bottom: 5px; font-size: 120%; font-weight: bold; } table.docinfo { text-align: left; float: right; width: 200px; margin-right: 0px; margin-left: 20px; margin-bottom: 20px; } table.docinfo th { border-top: none; font-size: 72%; padding-left: 10px; } table.docinfo td { padding-left: 10px; font-size: 88%; } table.docinfo tr.field td, table.docinfo tr.field th {display: none;} #h1.title { display: none; } dt { margin-bottom: 0.5em; color: #000; font-weight: bold; } dd { margin-left: 2em; margin-bottom: 1em; } tt { font: 1em "Courier New", "Courier"; font-size: 110%; } pre { font-family: "Courier", monospace; margin-right: 10px; background: #C1E5F6; border-left: solid 2px #6185A6; border-right: solid 2px #6185A6; padding: 5px 10px 5px 10px; background: #f6f6f6; border: solid 1px #ddd; margin: 1em 0; } div.warning, div.note, div.important { width: 80%; margin: 1.5em auto; background: #C1E5F6; background: #F1FFF5; border: solid 1px #D1DFD5; padding: 5px 10px 5px 10px; } p.admonition-title { font-family: Georgia, "Lucida Grande"; font-size: 128%; letter-spacing: 2px; text-transform: uppercase; margin: 0 0 0.5em 0; border-bottom: solid 1px #D1DFD5 } h1 { font-size: 200%; } h2 { font-size: 130%; } h3 { font-size: 100%; font-style: italic; } h1.title { text-align: center; } table { margin-bottom: 1em; border-collapse: collapse; } table, th, td { border: none; } th, td { padding: 0.3em; } th { text-align: left; background: #f0f0e0; border-right: solid 1px #f0f0e0; border-top: solid 1px #e8e8d8; border-bottom: solid 1px #e8e8d8; } td { background: #f8f8e8; border-right: solid 1px #f8f8e8; border-bottom: solid 1px #e8e8d8; } td td { background: #e8e8d8; border-right: solid 1px #e8e8d8; border-bottom: solid 1px #d8d8c8; } div.topic { border-left: solid 1px #eee; padding-left: 1em; margin: 0 0 1.5em; } p.topic-title { font: 1.3em Georgia, "Times New Roman", serif; } /* TOC */ div.contents { border: none; } #table-of-contents { margin-left: 20px; padding: 0 0 1em; width: 200px; float: right; clear: right; background: url('img/blue_bottom.png') no-repeat bottom left; border-right: solid 1px #A1C5D6; } #table-of-contents p { font-family: Georgia, "Times New Roman", serif; background: #A1C5D6 url('img/blue_top.png') no-repeat top left; color: #AD370A; padding: 0.5em; margin: 0; } #table-of-contents li { margin: 0 0.5em 0 0.5em; } #table-of-contents ul { margin: 0; padding: 0 0 0 0.8em; list-style: none; text-align: left; line-height: 1.5em; } #table-of-contents ul ul { background: url('img/dotline.gif') repeat-y; } #table-of-contents a.reference { border: none; font: 0.88em Tahoma; font-weight: bold; color: #000050; margin-right: 1em; background: url('img/minus.gif') no-repeat left 50%; padding-left: 15px; } #table-of-contents li li a.reference { font-weight: normal; background: none; padding: 0; } #table-of-contents a.reference:hover { border-bottom: solid 1px #8D370A; } #librarySidebar { float: left; width: 170px; } #libraryBody { border-left: solid 1px #eee; padding-left: 10px; margin-left: 178px; margin-right: 10px; } .keyword { font-weight: bold } .string { color: #771; } .comment { font-style: italic; color: #559; } .preproc { font-style: italic; color: #959; } .number { color: #595; } libtorrent-rasterbar-1.1.13/docs/settings.rst000066400000000000000000003155551351156116000213030ustar00rootroot00000000000000.. _user_agent: .. raw:: html +------------+--------+----------------------------------+ | name | type | default | +============+========+==================================+ | user_agent | string | "libtorrent/" LIBTORRENT_VERSION | +------------+--------+----------------------------------+ this is the client identification to the tracker. The recommended format of this string is: "ClientName/ClientVersion libtorrent/libtorrentVersion". This name will not only be used when making HTTP requests, but also when sending extended headers to peers that support that extension. It may not contain \r or \n .. _announce_ip: .. raw:: html +-------------+--------+---------+ | name | type | default | +=============+========+=========+ | announce_ip | string | 0 | +-------------+--------+---------+ ``announce_ip`` is the ip address passed along to trackers as the ``&ip=`` parameter. If left as the default, that parameter is omitted. .. _handshake_client_version: .. raw:: html +--------------------------+--------+---------+ | name | type | default | +==========================+========+=========+ | handshake_client_version | string | 0 | +--------------------------+--------+---------+ this is the client name and version identifier sent to peers in the handshake message. If this is an empty string, the user_agent is used instead .. _outgoing_interfaces: .. raw:: html +---------------------+--------+---------+ | name | type | default | +=====================+========+=========+ | outgoing_interfaces | string | "" | +---------------------+--------+---------+ sets the network interface this session will use when it opens outgoing connections. By default, it binds outgoing connections to INADDR_ANY and port 0 (i.e. let the OS decide). Ths parameter must be a string containing one or more, comma separated, adapter names. Adapter names on unix systems are of the form "eth0", "eth1", "tun0", etc. When specifying multiple interfaces, they will be assigned in round-robin order. This may be useful for clients that are multi-homed. Binding an outgoing connection to a local IP does not necessarily make the connection via the associated NIC/Adapter. Setting this to an empty string will disable binding of outgoing connections. .. _listen_interfaces: .. raw:: html +-------------------+--------+----------------+ | name | type | default | +===================+========+================+ | listen_interfaces | string | "0.0.0.0:6881" | +-------------------+--------+----------------+ a comma-separated list of IP port-pairs. These are the listen ports that will be opened for accepting incoming uTP and TCP connections. It is possible to listen on multiple IPs and multiple ports. Binding to port 0 will make the operating system pick the port. The default is "0.0.0.0:6881", which binds to all interfaces on port 6881. If binding fails because the port is busy, the port number will be incremented by one, ``settings_pack::max_retry_port_bind`` times. if all retry attempts fail, the socket will be bound to port 0, meaning the operating system will pick a port. This behavior can be disabled by disabling ``settings_pack::listen_system_port_fallback``. if binding fails, the listen_failed_alert is posted, potentially more than once. Once/if binding the listen socket(s) succeed, listen_succeeded_alert is posted. Each port will attempt to open both a UDP and a TCP listen socket, to allow accepting uTP connections as well as TCP. If using the DHT, this will also make the DHT use the same UDP ports. .. note:: The current support for opening arbitrary UDP sockets is limited. In this version of libtorrent, there will only ever be two UDP sockets, one for IPv4 and one for IPv6. .. _proxy_hostname: .. raw:: html +----------------+--------+---------+ | name | type | default | +================+========+=========+ | proxy_hostname | string | "" | +----------------+--------+---------+ when using a poxy, this is the hostname where the proxy is running see proxy_type. .. _proxy_username: .. _proxy_password: .. raw:: html +----------------+--------+---------+ | name | type | default | +================+========+=========+ | proxy_username | string | "" | +----------------+--------+---------+ | proxy_password | string | "" | +----------------+--------+---------+ when using a proxy, these are the credentials (if any) to use whne connecting to it. see proxy_type .. _i2p_hostname: .. raw:: html +--------------+--------+---------+ | name | type | default | +==============+========+=========+ | i2p_hostname | string | "" | +--------------+--------+---------+ sets the i2p_ SAM bridge to connect to. set the port with the ``i2p_port`` setting. .. _i2p: http://www.i2p2.de .. _peer_fingerprint: .. raw:: html +------------------+--------+------------+ | name | type | default | +==================+========+============+ | peer_fingerprint | string | "-LT11D0-" | +------------------+--------+------------+ this is the fingerprint for the client. It will be used as the prefix to the peer_id. If this is 20 bytes (or longer) it will be truncated to 20 bytes and used as the entire peer-id There is a utility function, generate_fingerprint() that can be used to generate a standard client peer ID fingerprint prefix. .. _dht_bootstrap_nodes: .. raw:: html +---------------------+--------+----------------------------+ | name | type | default | +=====================+========+============================+ | dht_bootstrap_nodes | string | "dht.libtorrent.org:25401" | +---------------------+--------+----------------------------+ This is a comma-separated list of IP port-pairs. They will be added to the DHT node (if it's enabled) as back-up nodes in case we don't know of any. This setting will contain one or more bootstrap nodes by default. Changing these after the DHT has been started may not have any effect until the DHT is restarted. .. _allow_multiple_connections_per_ip: .. raw:: html +-----------------------------------+------+---------+ | name | type | default | +===================================+======+=========+ | allow_multiple_connections_per_ip | bool | false | +-----------------------------------+------+---------+ determines if connections from the same IP address as existing connections should be rejected or not. Multiple connections from the same IP address is not allowed by default, to prevent abusive behavior by peers. It may be useful to allow such connections in cases where simulations are run on the same machine, and all peers in a swarm has the same IP address. .. _send_redundant_have: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | send_redundant_have | bool | true | +---------------------+------+---------+ ``send_redundant_have`` controls if have messages will be sent to peers that already have the piece. This is typically not necessary, but it might be necessary for collecting statistics in some cases. Default is false. .. _lazy_bitfields: .. raw:: html +----------------+------+---------+ | name | type | default | +================+======+=========+ | lazy_bitfields | bool | false | +----------------+------+---------+ if this is true, outgoing bitfields will never be fuil. If the client is seed, a few bits will be set to 0, and later filled in with have messages. This is to prevent certain ISPs from stopping people from seeding. .. _use_dht_as_fallback: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | use_dht_as_fallback | bool | false | +---------------------+------+---------+ ``use_dht_as_fallback`` determines how the DHT is used. If this is true, the DHT will only be used for torrents where all trackers in its tracker list has failed. Either by an explicit error message or a time out. This is false by default, which means the DHT is used by default regardless of if the trackers fail or not. .. _upnp_ignore_nonrouters: .. raw:: html +------------------------+------+---------+ | name | type | default | +========================+======+=========+ | upnp_ignore_nonrouters | bool | false | +------------------------+------+---------+ ``upnp_ignore_nonrouters`` indicates whether or not the UPnP implementation should ignore any broadcast response from a device whose address is not the configured router for this machine. i.e. it's a way to not talk to other people's routers by mistake. .. _use_parole_mode: .. raw:: html +-----------------+------+---------+ | name | type | default | +=================+======+=========+ | use_parole_mode | bool | true | +-----------------+------+---------+ ``use_parole_mode`` specifies if parole mode should be used. Parole mode means that peers that participate in pieces that fail the hash check are put in a mode where they are only allowed to download whole pieces. If the whole piece a peer in parole mode fails the hash check, it is banned. If a peer participates in a piece that passes the hash check, it is taken out of parole mode. .. _use_read_cache: .. raw:: html +----------------+------+---------+ | name | type | default | +================+======+=========+ | use_read_cache | bool | true | +----------------+------+---------+ enable and disable caching of blocks read from disk. the purpose of the read cache is partly read-ahead of requests but also to avoid reading blocks back from the disk multiple times for popular pieces. .. _coalesce_reads: .. _coalesce_writes: .. raw:: html +-----------------+------+---------+ | name | type | default | +=================+======+=========+ | coalesce_reads | bool | false | +-----------------+------+---------+ | coalesce_writes | bool | false | +-----------------+------+---------+ allocate separate, contiguous, buffers for read and write calls. Only used where writev/readv cannot be used will use more RAM but may improve performance .. _auto_manage_prefer_seeds: .. raw:: html +--------------------------+------+---------+ | name | type | default | +==========================+======+=========+ | auto_manage_prefer_seeds | bool | false | +--------------------------+------+---------+ prefer seeding torrents when determining which torrents to give active slots to, the default is false which gives preference to downloading torrents .. _dont_count_slow_torrents: .. raw:: html +--------------------------+------+---------+ | name | type | default | +==========================+======+=========+ | dont_count_slow_torrents | bool | true | +--------------------------+------+---------+ if ``dont_count_slow_torrents`` is true, torrents without any payload transfers are not subject to the ``active_seeds`` and ``active_downloads`` limits. This is intended to make it more likely to utilize all available bandwidth, and avoid having torrents that don't transfer anything block the active slots. .. _close_redundant_connections: .. raw:: html +-----------------------------+------+---------+ | name | type | default | +=============================+======+=========+ | close_redundant_connections | bool | true | +-----------------------------+------+---------+ ``close_redundant_connections`` specifies whether libtorrent should close connections where both ends have no utility in keeping the connection open. For instance if both ends have completed their downloads, there's no point in keeping it open. .. _prioritize_partial_pieces: .. raw:: html +---------------------------+------+---------+ | name | type | default | +===========================+======+=========+ | prioritize_partial_pieces | bool | false | +---------------------------+------+---------+ If ``prioritize_partial_pieces`` is true, partial pieces are picked before pieces that are more rare. If false, rare pieces are always prioritized, unless the number of partial pieces is growing out of proportion. .. _rate_limit_ip_overhead: .. raw:: html +------------------------+------+---------+ | name | type | default | +========================+======+=========+ | rate_limit_ip_overhead | bool | true | +------------------------+------+---------+ if set to true, the estimated TCP/IP overhead is drained from the rate limiters, to avoid exceeding the limits with the total traffic .. _announce_to_all_tiers: .. _announce_to_all_trackers: .. raw:: html +--------------------------+------+---------+ | name | type | default | +==========================+======+=========+ | announce_to_all_tiers | bool | false | +--------------------------+------+---------+ | announce_to_all_trackers | bool | false | +--------------------------+------+---------+ ``announce_to_all_trackers`` controls how multi tracker torrents are treated. If this is set to true, all trackers in the same tier are announced to in parallel. If all trackers in tier 0 fails, all trackers in tier 1 are announced as well. If it's set to false, the behavior is as defined by the multi tracker specification. It defaults to false, which is the same behavior previous versions of libtorrent has had as well. ``announce_to_all_tiers`` also controls how multi tracker torrents are treated. When this is set to true, one tracker from each tier is announced to. This is the uTorrent behavior. This is false by default in order to comply with the multi-tracker specification. .. _prefer_udp_trackers: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | prefer_udp_trackers | bool | true | +---------------------+------+---------+ ``prefer_udp_trackers`` is true by default. It means that trackers may be rearranged in a way that udp trackers are always tried before http trackers for the same hostname. Setting this to false means that the trackers' tier is respected and there's no preference of one protocol over another. .. _strict_super_seeding: .. raw:: html +----------------------+------+---------+ | name | type | default | +======================+======+=========+ | strict_super_seeding | bool | false | +----------------------+------+---------+ ``strict_super_seeding`` when this is set to true, a piece has to have been forwarded to a third peer before another one is handed out. This is the traditional definition of super seeding. .. _disable_hash_checks: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | disable_hash_checks | bool | false | +---------------------+------+---------+ when set to true, all data downloaded from peers will be assumed to be correct, and not tested to match the hashes in the torrent this is only useful for simulation and testing purposes (typically combined with disabled_storage) .. _allow_i2p_mixed: .. raw:: html +-----------------+------+---------+ | name | type | default | +=================+======+=========+ | allow_i2p_mixed | bool | false | +-----------------+------+---------+ if this is true, i2p torrents are allowed to also get peers from other sources than the tracker, and connect to regular IPs, not providing any anonymization. This may be useful if the user is not interested in the anonymization of i2p, but still wants to be able to connect to i2p peers. .. _low_prio_disk: .. raw:: html +---------------+------+---------+ | name | type | default | +===============+======+=========+ | low_prio_disk | bool | true | +---------------+------+---------+ ``low_prio_disk`` determines if the disk I/O should use a normal or low priority policy. This defaults to true, which means that it's low priority by default. Other processes doing disk I/O will normally take priority in this mode. This is meant to improve the overall responsiveness of the system while downloading in the background. For high-performance server setups, this might not be desirable. .. _volatile_read_cache: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | volatile_read_cache | bool | false | +---------------------+------+---------+ ``volatile_read_cache``, if this is set to true, read cache blocks that are hit by peer read requests are removed from the disk cache to free up more space. This is useful if you don't expect the disk cache to create any cache hits from other peers than the one who triggered the cache line to be read into the cache in the first place. .. _guided_read_cache: .. raw:: html +-------------------+------+---------+ | name | type | default | +===================+======+=========+ | guided_read_cache | bool | false | +-------------------+------+---------+ ``guided_read_cache`` enables the disk cache to adjust the size of a cache line generated by peers to depend on the upload rate you are sending to that peer. The intention is to optimize the RAM usage of the cache, to read ahead further for peers that you're sending faster to. .. _no_atime_storage: .. raw:: html +------------------+------+---------+ | name | type | default | +==================+======+=========+ | no_atime_storage | bool | true | +------------------+------+---------+ ``no_atime_storage`` this is a linux-only option and passes in the ``O_NOATIME`` to ``open()`` when opening files. This may lead to some disk performance improvements. .. _incoming_starts_queued_torrents: .. raw:: html +---------------------------------+------+---------+ | name | type | default | +=================================+======+=========+ | incoming_starts_queued_torrents | bool | false | +---------------------------------+------+---------+ ``incoming_starts_queued_torrents`` defaults to false. If a torrent has been paused by the auto managed feature in libtorrent, i.e. the torrent is paused and auto managed, this feature affects whether or not it is automatically started on an incoming connection. The main reason to queue torrents, is not to make them unavailable, but to save on the overhead of announcing to the trackers, the DHT and to avoid spreading one's unchoke slots too thin. If a peer managed to find us, even though we're no in the torrent anymore, this setting can make us start the torrent and serve it. .. _report_true_downloaded: .. raw:: html +------------------------+------+---------+ | name | type | default | +========================+======+=========+ | report_true_downloaded | bool | false | +------------------------+------+---------+ when set to true, the downloaded counter sent to trackers will include the actual number of payload bytes downloaded including redundant bytes. If set to false, it will not include any redundancy bytes .. _strict_end_game_mode: .. raw:: html +----------------------+------+---------+ | name | type | default | +======================+======+=========+ | strict_end_game_mode | bool | true | +----------------------+------+---------+ ``strict_end_game_mode`` defaults to true, and controls when a block may be requested twice. If this is ``true``, a block may only be requested twice when there's ay least one request to every piece that's left to download in the torrent. This may slow down progress on some pieces sometimes, but it may also avoid downloading a lot of redundant bytes. If this is ``false``, libtorrent attempts to use each peer connection to its max, by always requesting something, even if it means requesting something that has been requested from another peer already. .. _broadcast_lsd: .. raw:: html +---------------+------+---------+ | name | type | default | +===============+======+=========+ | broadcast_lsd | bool | true | +---------------+------+---------+ if ``broadcast_lsd`` is set to true, the local peer discovery (or Local Service Discovery) will not only use IP multicast, but also broadcast its messages. This can be useful when running on networks that don't support multicast. Since broadcast messages might be expensive and disruptive on networks, only every 8th announce uses broadcast. .. _enable_outgoing_utp: .. _enable_incoming_utp: .. _enable_outgoing_tcp: .. _enable_incoming_tcp: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | enable_outgoing_utp | bool | true | +---------------------+------+---------+ | enable_incoming_utp | bool | true | +---------------------+------+---------+ | enable_outgoing_tcp | bool | true | +---------------------+------+---------+ | enable_incoming_tcp | bool | true | +---------------------+------+---------+ when set to true, libtorrent will try to make outgoing utp connections controls whether libtorrent will accept incoming connections or make outgoing connections of specific type. .. _ignore_resume_timestamps: .. raw:: html +--------------------------+------+---------+ | name | type | default | +==========================+======+=========+ | ignore_resume_timestamps | bool | false | +--------------------------+------+---------+ ``ignore_resume_timestamps`` determines if the storage, when loading resume data files, should verify that the file modification time with the timestamps in the resume data. This defaults to false, which means timestamps are taken into account, and resume data is less likely to accepted (torrents are more likely to be fully checked when loaded). It might be useful to set this to true if your network is faster than your disk, and it would be faster to redownload potentially missed pieces than to go through the whole storage to look for them. .. _no_recheck_incomplete_resume: .. raw:: html +------------------------------+------+---------+ | name | type | default | +==============================+======+=========+ | no_recheck_incomplete_resume | bool | false | +------------------------------+------+---------+ ``no_recheck_incomplete_resume`` determines if the storage should check the whole files when resume data is incomplete or missing or whether it should simply assume we don't have any of the data. By default, this is determined by the existence of any of the files. By setting this setting to true, the files won't be checked, but will go straight to download mode. .. _anonymous_mode: .. raw:: html +----------------+------+---------+ | name | type | default | +================+======+=========+ | anonymous_mode | bool | false | +----------------+------+---------+ ``anonymous_mode`` defaults to false. When set to true, the client tries to hide its identity to a certain degree. The user-agent will be reset to an empty string (except for private torrents). Trackers will only be used if they are using a proxy server. The listen sockets are closed, and incoming connections will only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the same machine as the tracker proxy). Since no incoming connections are accepted, NAT-PMP, UPnP, DHT and local peer discovery are all turned off when this setting is enabled. If you're using I2P, it might make sense to enable anonymous mode as well. .. _report_web_seed_downloads: .. raw:: html +---------------------------+------+---------+ | name | type | default | +===========================+======+=========+ | report_web_seed_downloads | bool | true | +---------------------------+------+---------+ specifies whether downloads from web seeds is reported to the tracker or not. Defaults to on. Turning it off also excludes web seed traffic from other stats and download rate reporting via the libtorrent API. .. _announce_double_nat: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | announce_double_nat | bool | false | +---------------------+------+---------+ if this is true, the ``&ip=`` argument in tracker requests (unless otherwise specified) will be set to the intermediate IP address if the user is double NATed. If the user is not double NATed, this option does not have an affect .. _seeding_outgoing_connections: .. raw:: html +------------------------------+------+---------+ | name | type | default | +==============================+======+=========+ | seeding_outgoing_connections | bool | true | +------------------------------+------+---------+ ``seeding_outgoing_connections`` determines if seeding (and finished) torrents should attempt to make outgoing connections or not. By default this is true. It may be set to false in very specific applications where the cost of making outgoing connections is high, and there are no or small benefits of doing so. For instance, if no nodes are behind a firewall or a NAT, seeds don't need to make outgoing connections. .. _no_connect_privileged_ports: .. raw:: html +-----------------------------+------+---------+ | name | type | default | +=============================+======+=========+ | no_connect_privileged_ports | bool | false | +-----------------------------+------+---------+ when this is true, libtorrent will not attempt to make outgoing connections to peers whose port is < 1024. This is a safety precaution to avoid being part of a DDoS attack .. _smooth_connects: .. raw:: html +-----------------+------+---------+ | name | type | default | +=================+======+=========+ | smooth_connects | bool | true | +-----------------+------+---------+ ``smooth_connects`` is true by default, which means the number of connection attempts per second may be limited to below the ``connection_speed``, in case we're close to bump up against the limit of number of connections. The intention of this setting is to more evenly distribute our connection attempts over time, instead of attempting to connect in batches, and timing them out in batches. .. _always_send_user_agent: .. raw:: html +------------------------+------+---------+ | name | type | default | +========================+======+=========+ | always_send_user_agent | bool | false | +------------------------+------+---------+ always send user-agent in every web seed request. If false, only the first request per http connection will include the user agent .. _apply_ip_filter_to_trackers: .. raw:: html +-----------------------------+------+---------+ | name | type | default | +=============================+======+=========+ | apply_ip_filter_to_trackers | bool | true | +-----------------------------+------+---------+ ``apply_ip_filter_to_trackers`` defaults to true. It determines whether the IP filter applies to trackers as well as peers. If this is set to false, trackers are exempt from the IP filter (if there is one). If no IP filter is set, this setting is irrelevant. .. _use_disk_read_ahead: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | use_disk_read_ahead | bool | true | +---------------------+------+---------+ ``use_disk_read_ahead`` defaults to true and will attempt to optimize disk reads by giving the operating system heads up of disk read requests as they are queued in the disk job queue. .. _lock_files: .. raw:: html +------------+------+---------+ | name | type | default | +============+======+=========+ | lock_files | bool | false | +------------+------+---------+ ``lock_files`` determines whether or not to lock files which libtorrent is downloading to or seeding from. This is implemented using ``fcntl(F_SETLK)`` on unix systems and by not passing in ``SHARE_READ`` and ``SHARE_WRITE`` on windows. This might prevent 3rd party processes from corrupting the files under libtorrent's feet. .. _contiguous_recv_buffer: .. raw:: html +------------------------+------+---------+ | name | type | default | +========================+======+=========+ | contiguous_recv_buffer | bool | true | +------------------------+------+---------+ ``contiguous_recv_buffer`` determines whether or not libtorrent should receive data from peers into a contiguous intermediate buffer, to then copy blocks into disk buffers from, or to make many smaller calls to ``read()``, each time passing in the specific buffer the data belongs in. When downloading at high rates, the latter may save some time copying data. When seeding at high rates, all incoming traffic consists of a very large number of tiny packets, and enabling ``contiguous_recv_buffer`` will provide higher performance. When this is enabled, it will only be used when seeding to peers, since that's when it provides performance improvements. .. _ban_web_seeds: .. raw:: html +---------------+------+---------+ | name | type | default | +===============+======+=========+ | ban_web_seeds | bool | true | +---------------+------+---------+ when true, web seeds sending bad data will be banned .. _allow_partial_disk_writes: .. raw:: html +---------------------------+------+---------+ | name | type | default | +===========================+======+=========+ | allow_partial_disk_writes | bool | true | +---------------------------+------+---------+ when set to false, the ``write_cache_line_size`` will apply across piece boundaries. this is a bad idea unless the piece picker also is configured to have an affinity to pick pieces belonging to the same write cache line as is configured in the disk cache. .. _force_proxy: .. raw:: html +-------------+------+---------+ | name | type | default | +=============+======+=========+ | force_proxy | bool | false | +-------------+------+---------+ If true, disables any communication that's not going over a proxy. Enabling this requires a proxy to be configured as well, see proxy_type and proxy_hostname settings. The listen sockets are closed, and incoming connections will only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the same machine as the tracker proxy). .. _support_share_mode: .. raw:: html +--------------------+------+---------+ | name | type | default | +====================+======+=========+ | support_share_mode | bool | true | +--------------------+------+---------+ if false, prevents libtorrent to advertise share-mode support .. _support_merkle_torrents: .. raw:: html +-------------------------+------+---------+ | name | type | default | +=========================+======+=========+ | support_merkle_torrents | bool | true | +-------------------------+------+---------+ if this is false, don't advertise support for the Tribler merkle tree piece message .. _report_redundant_bytes: .. raw:: html +------------------------+------+---------+ | name | type | default | +========================+======+=========+ | report_redundant_bytes | bool | true | +------------------------+------+---------+ if this is true, the number of redundant bytes is sent to the tracker .. _listen_system_port_fallback: .. raw:: html +-----------------------------+------+---------+ | name | type | default | +=============================+======+=========+ | listen_system_port_fallback | bool | true | +-----------------------------+------+---------+ if this is true, libtorrent will fall back to listening on a port chosen by the operating system (i.e. binding to port 0). If a failure is preferred, set this to false. .. _use_disk_cache_pool: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | use_disk_cache_pool | bool | false | +---------------------+------+---------+ ``use_disk_cache_pool`` enables using a pool allocator for disk cache blocks. Enabling it makes the cache perform better at high throughput. It also makes the cache less likely and slower at returning memory back to the system, once allocated. .. _announce_crypto_support: .. raw:: html +-------------------------+------+---------+ | name | type | default | +=========================+======+=========+ | announce_crypto_support | bool | true | +-------------------------+------+---------+ when this is true, and incoming encrypted connections are enabled, &supportcrypt=1 is included in http tracker announces .. _enable_upnp: .. raw:: html +-------------+------+---------+ | name | type | default | +=============+======+=========+ | enable_upnp | bool | true | +-------------+------+---------+ Starts and stops the UPnP service. When started, the listen port and the DHT port are attempted to be forwarded on local UPnP router devices. The upnp object returned by ``start_upnp()`` can be used to add and remove arbitrary port mappings. Mapping status is returned through the portmap_alert and the portmap_error_alert. The object will be valid until ``stop_upnp()`` is called. See upnp-and-nat-pmp_. .. _enable_natpmp: .. raw:: html +---------------+------+---------+ | name | type | default | +===============+======+=========+ | enable_natpmp | bool | true | +---------------+------+---------+ Starts and stops the NAT-PMP service. When started, the listen port and the DHT port are attempted to be forwarded on the router through NAT-PMP. The natpmp object returned by ``start_natpmp()`` can be used to add and remove arbitrary port mappings. Mapping status is returned through the portmap_alert and the portmap_error_alert. The object will be valid until ``stop_natpmp()`` is called. See upnp-and-nat-pmp_. .. _enable_lsd: .. raw:: html +------------+------+---------+ | name | type | default | +============+======+=========+ | enable_lsd | bool | true | +------------+------+---------+ Starts and stops Local Service Discovery. This service will broadcast the infohashes of all the non-private torrents on the local network to look for peers on the same swarm within multicast reach. .. _enable_dht: .. raw:: html +------------+------+---------+ | name | type | default | +============+======+=========+ | enable_dht | bool | true | +------------+------+---------+ starts the dht node and makes the trackerless service available to torrents. .. _prefer_rc4: .. raw:: html +------------+------+---------+ | name | type | default | +============+======+=========+ | prefer_rc4 | bool | false | +------------+------+---------+ if the allowed encryption level is both, setting this to true will prefer rc4 if both methods are offered, plaintext otherwise .. _proxy_hostnames: .. raw:: html +-----------------+------+---------+ | name | type | default | +=================+======+=========+ | proxy_hostnames | bool | true | +-----------------+------+---------+ if true, hostname lookups are done via the configured proxy (if any). This is only supported by SOCKS5 and HTTP. .. _proxy_peer_connections: .. raw:: html +------------------------+------+---------+ | name | type | default | +========================+======+=========+ | proxy_peer_connections | bool | true | +------------------------+------+---------+ if true, peer connections are made (and accepted) over the configured proxy, if any. Web seeds as well as regular bittorrent peer connections are considered "peer connections". Anything transporting actual torrent payload (trackers and DHT traffic are not considered peer connections). .. _auto_sequential: .. raw:: html +-----------------+------+---------+ | name | type | default | +=================+======+=========+ | auto_sequential | bool | true | +-----------------+------+---------+ if this setting is true, torrents with a very high availability of pieces (and seeds) are downloaded sequentially. This is more efficient for the disk I/O. With many seeds, the download order is unlikely to matter anyway .. _proxy_tracker_connections: .. raw:: html +---------------------------+------+---------+ | name | type | default | +===========================+======+=========+ | proxy_tracker_connections | bool | true | +---------------------------+------+---------+ if true, tracker connections are made over the configured proxy, if any. .. _tracker_completion_timeout: .. raw:: html +----------------------------+------+---------+ | name | type | default | +============================+======+=========+ | tracker_completion_timeout | int | 30 | +----------------------------+------+---------+ ``tracker_completion_timeout`` is the number of seconds the tracker connection will wait from when it sent the request until it considers the tracker to have timed-out. Default value is 60 seconds. .. _tracker_receive_timeout: .. raw:: html +-------------------------+------+---------+ | name | type | default | +=========================+======+=========+ | tracker_receive_timeout | int | 10 | +-------------------------+------+---------+ ``tracker_receive_timeout`` is the number of seconds to wait to receive any data from the tracker. If no data is received for this number of seconds, the tracker will be considered as having timed out. If a tracker is down, this is the kind of timeout that will occur. .. _stop_tracker_timeout: .. raw:: html +----------------------+------+---------+ | name | type | default | +======================+======+=========+ | stop_tracker_timeout | int | 5 | +----------------------+------+---------+ 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 .. _tracker_maximum_response_length: .. raw:: html +---------------------------------+------+-----------+ | name | type | default | +=================================+======+===========+ | tracker_maximum_response_length | int | 1024*1024 | +---------------------------------+------+-----------+ this is the maximum number of bytes in a tracker response. If a response size passes this number of bytes it will be rejected and the connection will be closed. On gzipped responses this size is measured on the uncompressed data. So, if you get 20 bytes of gzip response that'll expand to 2 megabytes, it will be interrupted before the entire response has been uncompressed (assuming the limit is lower than 2 megs). .. _piece_timeout: .. raw:: html +---------------+------+---------+ | name | type | default | +===============+======+=========+ | piece_timeout | int | 20 | +---------------+------+---------+ the number of seconds from a request is sent until it times out if no piece response is returned. .. _request_timeout: .. raw:: html +-----------------+------+---------+ | name | type | default | +=================+======+=========+ | request_timeout | int | 60 | +-----------------+------+---------+ the number of seconds one block (16kB) is expected to be received within. If it's not, the block is requested from a different peer .. _request_queue_time: .. raw:: html +--------------------+------+---------+ | name | type | default | +====================+======+=========+ | request_queue_time | int | 3 | +--------------------+------+---------+ 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. .. _max_allowed_in_request_queue: .. raw:: html +------------------------------+------+---------+ | name | type | default | +==============================+======+=========+ | max_allowed_in_request_queue | int | 500 | +------------------------------+------+---------+ 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. .. _max_out_request_queue: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | max_out_request_queue | int | 500 | +-----------------------+------+---------+ ``max_out_request_queue`` is the maximum number of outstanding requests to send to a peer. This limit takes precedence over ``request_queue_time``. i.e. no matter the download speed, the number of outstanding requests will never exceed this limit. .. _whole_pieces_threshold: .. raw:: html +------------------------+------+---------+ | name | type | default | +========================+======+=========+ | whole_pieces_threshold | int | 20 | +------------------------+------+---------+ 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. .. _peer_timeout: .. raw:: html +--------------+------+---------+ | name | type | default | +==============+======+=========+ | peer_timeout | int | 120 | +--------------+------+---------+ ``peer_timeout`` is the number of seconds the peer connection should wait (for any activity on the peer connection) before closing it due to time out. This defaults to 120 seconds, since that's what's specified in the protocol specification. After half the time out, a keep alive message is sent. .. _urlseed_timeout: .. raw:: html +-----------------+------+---------+ | name | type | default | +=================+======+=========+ | urlseed_timeout | int | 20 | +-----------------+------+---------+ same as peer_timeout, but only applies to url-seeds. this is usually set lower, because web servers are expected to be more reliable. .. _urlseed_pipeline_size: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | urlseed_pipeline_size | int | 5 | +-----------------------+------+---------+ controls the pipelining size of url-seeds. i.e. the number of HTTP request to keep outstanding before waiting for the first one to complete. It's common for web servers to limit this to a relatively low number, like 5 .. _urlseed_wait_retry: .. raw:: html +--------------------+------+---------+ | name | type | default | +====================+======+=========+ | urlseed_wait_retry | int | 30 | +--------------------+------+---------+ time to wait until a new retry of a web seed takes place .. _file_pool_size: .. raw:: html +----------------+------+---------+ | name | type | default | +================+======+=========+ | file_pool_size | int | 40 | +----------------+------+---------+ sets the upper limit on the total number of files this session will keep open. The reason why files are left open at all is that some anti virus software hooks on every file close, and scans the file for viruses. deferring the closing of the files will be the difference between a usable system and a completely hogged down system. Most operating systems also has a limit on the total number of file descriptors a process may have open. .. _max_failcount: .. raw:: html +---------------+------+---------+ | name | type | default | +===============+======+=========+ | max_failcount | int | 3 | +---------------+------+---------+ ``max_failcount`` is the maximum times we try to connect to a peer before stop connecting again. If a peer succeeds, the failcounter is reset. If a peer is retrieved from a peer source (other than DHT) the failcount is decremented by one, allowing another try. .. _min_reconnect_time: .. raw:: html +--------------------+------+---------+ | name | type | default | +====================+======+=========+ | min_reconnect_time | int | 60 | +--------------------+------+---------+ the number of seconds to wait to reconnect to a peer. this time is multiplied with the failcount. .. _peer_connect_timeout: .. raw:: html +----------------------+------+---------+ | name | type | default | +======================+======+=========+ | peer_connect_timeout | int | 15 | +----------------------+------+---------+ ``peer_connect_timeout`` the number of seconds to wait after a connection attempt is initiated to a peer until it is considered as having timed out. This setting is especially important in case the number of half-open connections are limited, since stale half-open connection may delay the connection of other peers considerably. .. _connection_speed: .. raw:: html +------------------+------+---------+ | name | type | default | +==================+======+=========+ | connection_speed | int | 30 | +------------------+------+---------+ ``connection_speed`` is the number of connection attempts that are made per second. If a number < 0 is specified, it will default to 200 connections per second. If 0 is specified, it means don't make outgoing connections at all. .. _inactivity_timeout: .. raw:: html +--------------------+------+---------+ | name | type | default | +====================+======+=========+ | inactivity_timeout | int | 600 | +--------------------+------+---------+ if a peer is uninteresting and uninterested for longer than this number of seconds, it will be disconnected. default is 10 minutes .. _unchoke_interval: .. raw:: html +------------------+------+---------+ | name | type | default | +==================+======+=========+ | unchoke_interval | int | 15 | +------------------+------+---------+ ``unchoke_interval`` is the number of seconds between chokes/unchokes. On this interval, peers are re-evaluated for being choked/unchoked. This is defined as 30 seconds in the protocol, and it should be significantly longer than what it takes for TCP to ramp up to it's max rate. .. _optimistic_unchoke_interval: .. raw:: html +-----------------------------+------+---------+ | name | type | default | +=============================+======+=========+ | optimistic_unchoke_interval | int | 30 | +-----------------------------+------+---------+ ``optimistic_unchoke_interval`` is the number of seconds between each *optimistic* unchoke. On this timer, the currently optimistically unchoked peer will change. .. _num_want: .. raw:: html +----------+------+---------+ | name | type | default | +==========+======+=========+ | num_want | int | 200 | +----------+------+---------+ ``num_want`` is the number of peers we want from each tracker request. It defines what is sent as the ``&num_want=`` parameter to the tracker. .. _initial_picker_threshold: .. raw:: html +--------------------------+------+---------+ | name | type | default | +==========================+======+=========+ | initial_picker_threshold | int | 4 | +--------------------------+------+---------+ ``initial_picker_threshold`` specifies the number of pieces we need before we switch to rarest first picking. This defaults to 4, which means the 4 first pieces in any torrent are picked at random, the following pieces are picked in rarest first order. .. _allowed_fast_set_size: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | allowed_fast_set_size | int | 10 | +-----------------------+------+---------+ the number of allowed pieces to send to peers that supports the fast extensions .. _suggest_mode: .. raw:: html +--------------+------+-------------------------------------+ | name | type | default | +==============+======+=====================================+ | suggest_mode | int | settings_pack::no_piece_suggestions | +--------------+------+-------------------------------------+ ``suggest_mode`` controls whether or not libtorrent will send out suggest messages to create a bias of its peers to request certain pieces. The modes are: * ``no_piece_suggestions`` which is the default and will not send out suggest messages. * ``suggest_read_cache`` which will send out suggest messages for the most recent pieces that are in the read cache. .. _max_queued_disk_bytes: .. raw:: html +-----------------------+------+-------------+ | name | type | default | +=======================+======+=============+ | max_queued_disk_bytes | int | 1024 * 1024 | +-----------------------+------+-------------+ ``max_queued_disk_bytes`` is the maximum number of bytes, to be written to disk, that can wait in the disk I/O thread queue. This queue is only for waiting for the disk I/O thread to receive the job and either write it to disk or insert it in the write cache. When this limit is reached, the peer connections will stop reading data from their sockets, until the disk thread catches up. Setting this too low will severely limit your download rate. .. _handshake_timeout: .. raw:: html +-------------------+------+---------+ | name | type | default | +===================+======+=========+ | handshake_timeout | int | 10 | +-------------------+------+---------+ the number of seconds to wait for a handshake response from a peer. If no response is received within this time, the peer is disconnected. .. _send_buffer_low_watermark: .. _send_buffer_watermark: .. _send_buffer_watermark_factor: .. raw:: html +------------------------------+------+------------+ | name | type | default | +==============================+======+============+ | send_buffer_low_watermark | int | 10 * 1024 | +------------------------------+------+------------+ | send_buffer_watermark | int | 500 * 1024 | +------------------------------+------+------------+ | send_buffer_watermark_factor | int | 50 | +------------------------------+------+------------+ ``send_buffer_low_watermark`` the minimum send buffer target size (send buffer includes bytes pending being read from disk). For good and snappy seeding performance, set this fairly high, to at least fit a few blocks. This is essentially the initial window size which will determine how fast we can ramp up the send rate if the send buffer has fewer bytes than ``send_buffer_watermark``, we'll read another 16kB block onto it. If set too small, upload rate capacity will suffer. If set too high, memory will be wasted. The actual watermark may be lower than this in case the upload rate is low, this is the upper limit. the current upload rate to a peer is multiplied by this factor to get the send buffer watermark. The factor is specified as a percentage. i.e. 50 -> 0.5 This product is clamped to the ``send_buffer_watermark`` setting to not exceed the max. For high speed upload, this should be set to a greater value than 100. For high capacity connections, setting this higher can improve upload performance and disk throughput. Setting it too high may waste RAM and create a bias towards read jobs over write jobs. .. _choking_algorithm: .. _seed_choking_algorithm: .. raw:: html +------------------------+------+-----------------------------------+ | name | type | default | +========================+======+===================================+ | choking_algorithm | int | settings_pack::fixed_slots_choker | +------------------------+------+-----------------------------------+ | seed_choking_algorithm | int | settings_pack::round_robin | +------------------------+------+-----------------------------------+ ``choking_algorithm`` specifies which algorithm to use to determine which peers to unchoke. The options for choking algorithms are: * ``fixed_slots_choker`` is the traditional choker with a fixed number of unchoke slots (as specified by ``settings_pack::unchoke_slots_limit``). * ``rate_based_choker`` opens up unchoke slots based on the upload rate achieved to peers. The more slots that are opened, the marginal upload rate required to open up another slot increases. * ``bittyrant_choker`` attempts to optimize download rate by finding the reciprocation rate of each peer individually and prefers peers that gives the highest *return on investment*. It still allocates all upload capacity, but shuffles it around to the best peers first. For this choker to be efficient, you need to set a global upload rate limit (``settings_pack::upload_rate_limit``). For more information about this choker, see the paper_. This choker is not fully implemented nor tested. .. _paper: http://bittyrant.cs.washington.edu/#papers ``seed_choking_algorithm`` controls the seeding unchoke behavior. The available options are: * ``round_robin`` which round-robins the peers that are unchoked when seeding. This distributes the upload bandwidht uniformly and fairly. It minimizes the ability for a peer to download everything without redistributing it. * ``fastest_upload`` unchokes the peers we can send to the fastest. This might be a bit more reliable in utilizing all available capacity. * ``anti_leech`` prioritizes peers who have just started or are just about to finish the download. The intention is to force peers in the middle of the download to trade with each other. .. _cache_size: .. _cache_buffer_chunk_size: .. _cache_expiry: .. raw:: html +-------------------------+------+---------+ | name | type | default | +=========================+======+=========+ | cache_size | int | 1024 | +-------------------------+------+---------+ | cache_buffer_chunk_size | int | 0 | +-------------------------+------+---------+ | cache_expiry | int | 300 | +-------------------------+------+---------+ ``cache_size`` is the disk write and read cache. It is specified in units of 16 KiB blocks. Buffers that are part of a peer's send or receive buffer also count against this limit. Send and receive buffers will never be denied to be allocated, but they will cause the actual cached blocks to be flushed or evicted. If this is set to -1, the cache size is automatically set to the amount of physical RAM available in the machine divided by 8. If the amount of physical RAM cannot be determined, it's set to 1024 (= 16 MiB). Disk buffers are allocated using a pool allocator, the number of blocks that are allocated at a time when the pool needs to grow can be specified in ``cache_buffer_chunk_size``. Lower numbers saves memory at the expense of more heap allocations. If it is set to 0, the effective chunk size is proportional to the total cache size, attempting to strike a good balance between performance and memory usage. It defaults to 0. ``cache_expiry`` is the number of seconds from the last cached write to a piece in the write cache, to when it's forcefully flushed to disk. Default is 60 second. On 32 bit builds, the effective cache size will be limited to 3/4 of 2 GiB to avoid exceeding the virtual address space limit. .. _disk_io_write_mode: .. _disk_io_read_mode: .. raw:: html +--------------------+------+--------------------------------+ | name | type | default | +====================+======+================================+ | disk_io_write_mode | int | settings_pack::enable_os_cache | +--------------------+------+--------------------------------+ | disk_io_read_mode | int | settings_pack::enable_os_cache | +--------------------+------+--------------------------------+ determines how files are opened when they're in read only mode versus read and write mode. The options are: enable_os_cache This is the default and files are opened normally, with the OS caching reads and writes. disable_os_cache This opens all files in no-cache mode. This corresponds to the OS not letting blocks for the files linger in the cache. This makes sense in order to avoid the bittorrent client to potentially evict all other processes' cache by simply handling high throughput and large files. If libtorrent's read cache is disabled, enabling this may reduce performance. One reason to disable caching is that it may help the operating system from growing its file cache indefinitely. .. _outgoing_port: .. _num_outgoing_ports: .. raw:: html +--------------------+------+---------+ | name | type | default | +====================+======+=========+ | outgoing_port | int | 0 | +--------------------+------+---------+ | num_outgoing_ports | int | 0 | +--------------------+------+---------+ this is the first port to use for binding outgoing connections to. This is useful for users that have routers that allow QoS settings based on local port. when binding outgoing connections to specific ports, ``num_outgoing_ports`` is the size of the range. It should be more than a few .. warning:: setting outgoing ports will limit the ability to keep multiple connections to the same client, even for different torrents. It is not recommended to change this setting. Its main purpose is to use as an escape hatch for cheap routers with QoS capability but can only classify flows based on port numbers. It is a range instead of a single port because of the problems with failing to reconnect to peers if a previous socket to that peer and port is in ``TIME_WAIT`` state. .. _peer_tos: .. raw:: html +----------+------+---------+ | name | type | default | +==========+======+=========+ | peer_tos | int | 0x20 | +----------+------+---------+ ``peer_tos`` determines the TOS byte set in the IP header of every packet sent to peers (including web seeds). The default value for this is ``0x0`` (no marking). One potentially useful TOS mark is ``0x20``, this represents the *QBone scavenger service*. For more details, see QBSS_. .. _`QBSS`: http://qbone.internet2.edu/qbss/ .. _active_downloads: .. _active_seeds: .. _active_checking: .. _active_dht_limit: .. _active_tracker_limit: .. _active_lsd_limit: .. _active_limit: .. _active_loaded_limit: .. raw:: html +----------------------+------+---------+ | name | type | default | +======================+======+=========+ | active_downloads | int | 3 | +----------------------+------+---------+ | active_seeds | int | 5 | +----------------------+------+---------+ | active_checking | int | 1 | +----------------------+------+---------+ | active_dht_limit | int | 88 | +----------------------+------+---------+ | active_tracker_limit | int | 1600 | +----------------------+------+---------+ | active_lsd_limit | int | 60 | +----------------------+------+---------+ | active_limit | int | 500 | +----------------------+------+---------+ | active_loaded_limit | int | 0 | +----------------------+------+---------+ for auto managed torrents, these are the limits they are subject to. If there are too many torrents some of the auto managed ones will be paused until some slots free up. ``active_downloads`` and ``active_seeds`` controls how many active seeding and downloading torrents the queuing mechanism allows. The target number of active torrents is ``min(active_downloads + active_seeds, active_limit)``. ``active_downloads`` and ``active_seeds`` are upper limits on the number of downloading torrents and seeding torrents respectively. Setting the value to -1 means unlimited. For example if there are 10 seeding torrents and 10 downloading torrents, and ``active_downloads`` is 4 and ``active_seeds`` is 4, there will be 4 seeds active and 4 downloading torrents. If the settings are ``active_downloads`` = 2 and ``active_seeds`` = 4, then there will be 2 downloading torrents and 4 seeding torrents active. Torrents that are not auto managed are not counted against these limits. ``active_checking`` is the limit of number of simultaneous checking torrents. ``active_limit`` is a hard limit on the number of active (auto managed) torrents. This limit also applies to slow torrents. ``active_dht_limit`` is the max number of torrents to announce to the DHT. By default this is set to 88, which is no more than one DHT announce every 10 seconds. ``active_tracker_limit`` is the max number of torrents to announce to their trackers. By default this is 360, which is no more than one announce every 5 seconds. ``active_lsd_limit`` is the max number of torrents to announce to the local network over the local service discovery protocol. By default this is 80, which is no more than one announce every 5 seconds (assuming the default announce interval of 5 minutes). You can have more torrents *active*, even though they are not announced to the DHT, lsd or their tracker. If some peer knows about you for any reason and tries to connect, it will still be accepted, unless the torrent is paused, which means it won't accept any connections. ``active_loaded_limit`` is the number of torrents that are allowed to be *loaded* at any given time. Note that a torrent can be active even though it's not loaded. If an unloaded torrents finds a peer that wants to access it, the torrent will be loaded on demand, using a user-supplied callback function. If the feature of unloading torrents is not enabled, this setting have no effect. If this limit is set to 0, it means unlimited. For more information, see dynamic-loading-of-torrent-files_. .. _auto_manage_interval: .. raw:: html +----------------------+------+---------+ | name | type | default | +======================+======+=========+ | auto_manage_interval | int | 30 | +----------------------+------+---------+ ``auto_manage_interval`` is the number of seconds between the torrent queue is updated, and rotated. .. _seed_time_limit: .. raw:: html +-----------------+------+--------------+ | name | type | default | +=================+======+==============+ | seed_time_limit | int | 24 * 60 * 60 | +-----------------+------+--------------+ this is the limit on the time a torrent has been an active seed (specified in seconds) before it is considered having met the seed limit criteria. See queuing_. .. _auto_scrape_interval: .. _auto_scrape_min_interval: .. raw:: html +--------------------------+------+---------+ | name | type | default | +==========================+======+=========+ | auto_scrape_interval | int | 1800 | +--------------------------+------+---------+ | auto_scrape_min_interval | int | 300 | +--------------------------+------+---------+ ``auto_scrape_interval`` is the number of seconds between scrapes of queued torrents (auto managed and paused torrents). Auto managed torrents that are paused, are scraped regularly in order to keep track of their downloader/seed ratio. This ratio is used to determine which torrents to seed and which to pause. ``auto_scrape_min_interval`` is the minimum number of seconds between any automatic scrape (regardless of torrent). In case there are a large number of paused auto managed torrents, this puts a limit on how often a scrape request is sent. .. _max_peerlist_size: .. _max_paused_peerlist_size: .. raw:: html +--------------------------+------+---------+ | name | type | default | +==========================+======+=========+ | max_peerlist_size | int | 3000 | +--------------------------+------+---------+ | max_paused_peerlist_size | int | 1000 | +--------------------------+------+---------+ ``max_peerlist_size`` is the maximum number of peers in the list of known peers. These peers are not necessarily connected, so this number should be much greater than the maximum number of connected peers. Peers are evicted from the cache when the list grows passed 90% of this limit, and once the size hits the limit, peers are no longer added to the list. If this limit is set to 0, there is no limit on how many peers we'll keep in the peer list. ``max_paused_peerlist_size`` is the max peer list size used for torrents that are paused. This default to the same as ``max_peerlist_size``, but can be used to save memory for paused torrents, since it's not as important for them to keep a large peer list. .. _min_announce_interval: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | min_announce_interval | int | 5 * 60 | +-----------------------+------+---------+ this is the minimum allowed announce interval for a tracker. This is specified in seconds and is used as a sanity check on what is returned from a tracker. It mitigates hammering misconfigured trackers. .. _auto_manage_startup: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | auto_manage_startup | int | 60 | +---------------------+------+---------+ this is the number of seconds a torrent is considered active after it was started, regardless of upload and download speed. This is so that newly started torrents are not considered inactive until they have a fair chance to start downloading. .. _seeding_piece_quota: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | seeding_piece_quota | int | 20 | +---------------------+------+---------+ ``seeding_piece_quota`` is the number of pieces to send to a peer, when seeding, before rotating in another peer to the unchoke set. It defaults to 3 pieces, which means that when seeding, any peer we've sent more than this number of pieces to will be unchoked in favour of a choked peer. .. _max_rejects: .. raw:: html +-------------+------+---------+ | name | type | default | +=============+======+=========+ | max_rejects | int | 50 | +-------------+------+---------+ TODO: deprecate this ``max_rejects`` is the number of piece requests we will reject in a row while a peer is choked before the peer is considered abusive and is disconnected. .. _recv_socket_buffer_size: .. _send_socket_buffer_size: .. raw:: html +-------------------------+------+---------+ | name | type | default | +=========================+======+=========+ | recv_socket_buffer_size | int | 0 | +-------------------------+------+---------+ | send_socket_buffer_size | int | 0 | +-------------------------+------+---------+ ``recv_socket_buffer_size`` and ``send_socket_buffer_size`` specifies the buffer sizes set on peer sockets. 0 (which is the default) means the OS default (i.e. don't change the buffer sizes). The socket buffer sizes are changed using setsockopt() with SOL_SOCKET/SO_RCVBUF and SO_SNDBUFFER. .. _read_cache_line_size: .. _write_cache_line_size: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | read_cache_line_size | int | 32 | +-----------------------+------+---------+ | write_cache_line_size | int | 16 | +-----------------------+------+---------+ ``read_cache_line_size`` is the number of blocks to read into the read cache when a read cache miss occurs. Setting this to 0 is essentially the same thing as disabling read cache. The number of blocks read into the read cache is always capped by the piece boundary. When a piece in the write cache has ``write_cache_line_size`` contiguous blocks in it, they will be flushed. Setting this to 1 effectively disables the write cache. .. _optimistic_disk_retry: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | optimistic_disk_retry | int | 10 * 60 | +-----------------------+------+---------+ ``optimistic_disk_retry`` is the number of seconds from a disk write errors occur on a torrent until libtorrent will take it out of the upload mode, to test if the error condition has been fixed. libtorrent will only do this automatically for auto managed torrents. You can explicitly take a torrent out of upload only mode using set_upload_mode(). .. _max_suggest_pieces: .. raw:: html +--------------------+------+---------+ | name | type | default | +====================+======+=========+ | max_suggest_pieces | int | 10 | +--------------------+------+---------+ ``max_suggest_pieces`` is the max number of suggested piece indices received from a peer that's remembered. If a peer floods suggest messages, this limit prevents libtorrent from using too much RAM. It defaults to 10. .. _local_service_announce_interval: .. raw:: html +---------------------------------+------+---------+ | name | type | default | +=================================+======+=========+ | local_service_announce_interval | int | 5 * 60 | +---------------------------------+------+---------+ ``local_service_announce_interval`` is the time between local network announces for a torrent. By default, when local service discovery is enabled a torrent announces itself every 5 minutes. This interval is specified in seconds. .. _dht_announce_interval: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | dht_announce_interval | int | 15 * 60 | +-----------------------+------+---------+ ``dht_announce_interval`` is the number of seconds between announcing torrents to the distributed hash table (DHT). .. _udp_tracker_token_expiry: .. raw:: html +--------------------------+------+---------+ | name | type | default | +==========================+======+=========+ | udp_tracker_token_expiry | int | 60 | +--------------------------+------+---------+ ``udp_tracker_token_expiry`` is the number of seconds libtorrent will keep UDP tracker connection tokens around for. This is specified to be 60 seconds, and defaults to that. The higher this value is, the fewer packets have to be sent to the UDP tracker. In order for higher values to work, the tracker needs to be configured to match the expiration time for tokens. .. _default_cache_min_age: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | default_cache_min_age | int | 1 | +-----------------------+------+---------+ ``default_cache_min_age`` is the minimum number of seconds any read cache line is kept in the cache. This defaults to one second but may be greater if ``guided_read_cache`` is enabled. Having a lower bound on the time a cache line stays in the cache is an attempt to avoid swapping the same pieces in and out of the cache in case there is a shortage of spare cache space. .. _num_optimistic_unchoke_slots: .. raw:: html +------------------------------+------+---------+ | name | type | default | +==============================+======+=========+ | num_optimistic_unchoke_slots | int | 0 | +------------------------------+------+---------+ ``num_optimistic_unchoke_slots`` is the number of optimistic unchoke slots to use. It defaults to 0, which means automatic. Having a higher number of optimistic unchoke slots mean you will find the good peers faster but with the trade-off to use up more bandwidth. When this is set to 0, libtorrent opens up 20% of your allowed upload slots as optimistic unchoke slots. .. _default_est_reciprocation_rate: .. _increase_est_reciprocation_rate: .. _decrease_est_reciprocation_rate: .. raw:: html +---------------------------------+------+---------+ | name | type | default | +=================================+======+=========+ | default_est_reciprocation_rate | int | 16000 | +---------------------------------+------+---------+ | increase_est_reciprocation_rate | int | 20 | +---------------------------------+------+---------+ | decrease_est_reciprocation_rate | int | 3 | +---------------------------------+------+---------+ ``default_est_reciprocation_rate`` is the assumed reciprocation rate from peers when using the BitTyrant choker. This defaults to 14 kiB/s. If set too high, you will over-estimate your peers and be more altruistic while finding the true reciprocation rate, if it's set too low, you'll be too stingy and waste finding the true reciprocation rate. ``increase_est_reciprocation_rate`` specifies how many percent the estimated reciprocation rate should be increased by each unchoke interval a peer is still choking us back. This defaults to 20%. This only applies to the BitTyrant choker. ``decrease_est_reciprocation_rate`` specifies how many percent the estimated reciprocation rate should be decreased by each unchoke interval a peer unchokes us. This default to 3%. This only applies to the BitTyrant choker. .. _max_pex_peers: .. raw:: html +---------------+------+---------+ | name | type | default | +===============+======+=========+ | max_pex_peers | int | 50 | +---------------+------+---------+ the max number of peers we accept from pex messages from a single peer. this limits the number of concurrent peers any of our peers claims to be connected to. If they claim to be connected to more than this, we'll ignore any peer that exceeds this limit .. _tick_interval: .. raw:: html +---------------+------+---------+ | name | type | default | +===============+======+=========+ | tick_interval | int | 500 | +---------------+------+---------+ ``tick_interval`` specifies the number of milliseconds between internal ticks. This is the frequency with which bandwidth quota is distributed to peers. It should not be more than one second (i.e. 1000 ms). Setting this to a low value (around 100) means higher resolution bandwidth quota distribution, setting it to a higher value saves CPU cycles. .. _share_mode_target: .. raw:: html +-------------------+------+---------+ | name | type | default | +===================+======+=========+ | share_mode_target | int | 3 | +-------------------+------+---------+ ``share_mode_target`` specifies the target share ratio for share mode torrents. This defaults to 3, meaning we'll try to upload 3 times as much as we download. Setting this very high, will make it very conservative and you might end up not downloading anything ever (and not affecting your share ratio). It does not make any sense to set this any lower than 2. For instance, if only 3 peers need to download the rarest piece, it's impossible to download a single piece and upload it more than 3 times. If the share_mode_target is set to more than 3, nothing is downloaded. .. _upload_rate_limit: .. _download_rate_limit: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | upload_rate_limit | int | 0 | +---------------------+------+---------+ | download_rate_limit | int | 0 | +---------------------+------+---------+ ``upload_rate_limit`` and ``download_rate_limit`` sets the session-global limits of upload and download rate limits, in bytes per second. By default peers on the local network are not rate limited. A value of 0 means unlimited. For fine grained control over rate limits, including making them apply to local peers, see peer-classes_. .. _dht_upload_rate_limit: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | dht_upload_rate_limit | int | 4000 | +-----------------------+------+---------+ ``dht_upload_rate_limit`` sets the rate limit on the DHT. This is specified in bytes per second and defaults to 4000. For busy boxes with lots of torrents that requires more DHT traffic, this should be raised. .. _unchoke_slots_limit: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | unchoke_slots_limit | int | 8 | +---------------------+------+---------+ ``unchoke_slots_limit`` is the max number of unchoked peers in the session. The number of unchoke slots may be ignored depending on what ``choking_algorithm`` is set to. .. _connections_limit: .. raw:: html +-------------------+------+---------+ | name | type | default | +===================+======+=========+ | connections_limit | int | 200 | +-------------------+------+---------+ ``connections_limit`` sets a global limit on the number of connections opened. The number of connections is set to a hard minimum of at least two per torrent, so if you set a too low connections limit, and open too many torrents, the limit will not be met. .. _connections_slack: .. raw:: html +-------------------+------+---------+ | name | type | default | +===================+======+=========+ | connections_slack | int | 10 | +-------------------+------+---------+ ``connections_slack`` is the the number of incoming connections exceeding the connection limit to accept in order to potentially replace existing ones. .. _utp_target_delay: .. _utp_gain_factor: .. _utp_min_timeout: .. _utp_syn_resends: .. _utp_fin_resends: .. _utp_num_resends: .. _utp_connect_timeout: .. _utp_loss_multiplier: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | utp_target_delay | int | 100 | +---------------------+------+---------+ | utp_gain_factor | int | 3000 | +---------------------+------+---------+ | utp_min_timeout | int | 500 | +---------------------+------+---------+ | utp_syn_resends | int | 2 | +---------------------+------+---------+ | utp_fin_resends | int | 2 | +---------------------+------+---------+ | utp_num_resends | int | 3 | +---------------------+------+---------+ | utp_connect_timeout | int | 3000 | +---------------------+------+---------+ | utp_loss_multiplier | int | 50 | +---------------------+------+---------+ ``utp_target_delay`` is the target delay for uTP sockets in milliseconds. A high value will make uTP connections more aggressive and cause longer queues in the upload bottleneck. It cannot be too low, since the noise in the measurements would cause it to send too slow. The default is 50 milliseconds. ``utp_gain_factor`` is the number of bytes the uTP congestion window can increase at the most in one RTT. This defaults to 300 bytes. If this is set too high, the congestion controller reacts too hard to noise and will not be stable, if it's set too low, it will react slow to congestion and not back off as fast. ``utp_min_timeout`` is the shortest allowed uTP socket timeout, specified in milliseconds. This defaults to 500 milliseconds. The timeout depends on the RTT of the connection, but is never smaller than this value. A connection times out when every packet in a window is lost, or when a packet is lost twice in a row (i.e. the resent packet is lost as well). The shorter the timeout is, the faster the connection will recover from this situation, assuming the RTT is low enough. ``utp_syn_resends`` is the number of SYN packets that are sent (and timed out) before giving up and closing the socket. ``utp_num_resends`` is the number of times a packet is sent (and lossed or timed out) before giving up and closing the connection. ``utp_connect_timeout`` is the number of milliseconds of timeout for the initial SYN packet for uTP connections. For each timed out packet (in a row), the timeout is doubled. ``utp_loss_multiplier`` controls how the congestion window is changed when a packet loss is experienced. It's specified as a percentage multiplier for ``cwnd``. By default it's set to 50 (i.e. cut in half). Do not change this value unless you know what you're doing. Never set it higher than 100. .. _mixed_mode_algorithm: .. raw:: html +----------------------+------+----------------------------------+ | name | type | default | +======================+======+==================================+ | mixed_mode_algorithm | int | settings_pack::peer_proportional | +----------------------+------+----------------------------------+ The ``mixed_mode_algorithm`` determines how to treat TCP connections when there are uTP connections. Since uTP is designed to yield to TCP, there's an inherent problem when using swarms that have both TCP and uTP connections. If nothing is done, uTP connections would often be starved out for bandwidth by the TCP connections. This mode is ``prefer_tcp``. The ``peer_proportional`` mode simply looks at the current throughput and rate limits all TCP connections to their proportional share based on how many of the connections are TCP. This works best if uTP connections are not rate limited by the global rate limiter (which they aren't by default). .. _listen_queue_size: .. raw:: html +-------------------+------+---------+ | name | type | default | +===================+======+=========+ | listen_queue_size | int | 5 | +-------------------+------+---------+ ``listen_queue_size`` is the value passed in to listen() for the listen socket. It is the number of outstanding incoming connections to queue up while we're not actively waiting for a connection to be accepted. The default is 5 which should be sufficient for any normal client. If this is a high performance server which expects to receive a lot of connections, or used in a simulator or test, it might make sense to raise this number. It will not take affect until the ``listen_interfaces`` settings is updated. .. _torrent_connect_boost: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | torrent_connect_boost | int | 30 | +-----------------------+------+---------+ ``torrent_connect_boost`` is the number of peers to try to connect to immediately when the first tracker response is received for a torrent. This is a boost to given to new torrents to accelerate them starting up. The normal connect scheduler is run once every second, this allows peers to be connected immediately instead of waiting for the session tick to trigger connections. This may not be set higher than 255. .. _alert_queue_size: .. raw:: html +------------------+------+---------+ | name | type | default | +==================+======+=========+ | alert_queue_size | int | 1000 | +------------------+------+---------+ ``alert_queue_size`` is the maximum number of alerts queued up internally. If alerts are not popped, the queue will eventually fill up to this level. .. _max_metadata_size: .. raw:: html +-------------------+------+------------------+ | name | type | default | +===================+======+==================+ | max_metadata_size | int | 3 * 1024 * 10240 | +-------------------+------+------------------+ ``max_metadata_size`` is the maximum allowed size (in bytes) to be received by the metadata extension, i.e. magnet links. .. _checking_mem_usage: .. raw:: html +--------------------+------+---------+ | name | type | default | +====================+======+=========+ | checking_mem_usage | int | 1024 | +--------------------+------+---------+ the number of blocks to keep outstanding at any given time when checking torrents. Higher numbers give faster re-checks but uses more memory. Specified in number of 16 kiB blocks .. _predictive_piece_announce: .. raw:: html +---------------------------+------+---------+ | name | type | default | +===========================+======+=========+ | predictive_piece_announce | int | 0 | +---------------------------+------+---------+ if set to > 0, pieces will be announced to other peers before they are fully downloaded (and before they are hash checked). The intention is to gain 1.5 potential round trip times per downloaded piece. When non-zero, this indicates how many milliseconds in advance pieces should be announced, before they are expected to be completed. .. _aio_threads: .. _aio_max: .. raw:: html +-------------+------+---------+ | name | type | default | +=============+======+=========+ | aio_threads | int | 4 | +-------------+------+---------+ | aio_max | int | 300 | +-------------+------+---------+ for some aio back-ends, ``aio_threads`` specifies the number of io-threads to use, and ``aio_max`` the max number of outstanding jobs. .. _network_threads: .. raw:: html +-----------------+------+---------+ | name | type | default | +=================+======+=========+ | network_threads | int | 0 | +-----------------+------+---------+ ``network_threads`` is the number of threads to use to call ``async_write_some`` (i.e. send) on peer connection sockets. When seeding at extremely high rates, this may become a bottleneck, and setting this to 2 or more may parallelize that cost. When using SSL torrents, all encryption for outgoing traffic is done within the socket send functions, and this will help parallelizing the cost of SSL encryption as well. .. _ssl_listen: .. raw:: html +------------+------+---------+ | name | type | default | +============+======+=========+ | ssl_listen | int | 0 | +------------+------+---------+ ``ssl_listen`` sets the listen port for SSL connections. If this is set to 0, no SSL listen port is opened. Otherwise a socket is opened on this port. This setting is only taken into account when opening the regular listen port, and won't re-open the listen socket simply by changing this setting. .. _tracker_backoff: .. raw:: html +-----------------+------+---------+ | name | type | default | +=================+======+=========+ | tracker_backoff | int | 250 | +-----------------+------+---------+ ``tracker_backoff`` determines how aggressively to back off from retrying failing trackers. This value determines *x* in the following formula, determining the number of seconds to wait until the next retry: delay = 5 + 5 * x / 100 * fails^2 This setting may be useful to make libtorrent more or less aggressive in hitting trackers. .. _share_ratio_limit: .. _seed_time_ratio_limit: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | share_ratio_limit | int | 200 | +-----------------------+------+---------+ | seed_time_ratio_limit | int | 700 | +-----------------------+------+---------+ when a seeding torrent reaches either the share ratio (bytes up / bytes down) or the seed time ratio (seconds as seed / seconds as downloader) or the seed time limit (seconds as seed) it is considered done, and it will leave room for other torrents. These are specified as percentages. Torrents that are considered done will still be allowed to be seeded, they just won't have priority anymore. For more, see queuing_. .. _peer_turnover: .. _peer_turnover_cutoff: .. _peer_turnover_interval: .. raw:: html +------------------------+------+---------+ | name | type | default | +========================+======+=========+ | peer_turnover | int | 4 | +------------------------+------+---------+ | peer_turnover_cutoff | int | 90 | +------------------------+------+---------+ | peer_turnover_interval | int | 300 | +------------------------+------+---------+ peer_turnover is the percentage of peers to disconnect every turnover peer_turnover_interval (if we're at the peer limit), this is specified in percent when we are connected to more than limit * peer_turnover_cutoff peers disconnect peer_turnover fraction of the peers. It is specified in percent peer_turnover_interval is the interval (in seconds) between optimistic disconnects if the disconnects happen and how many peers are disconnected is controlled by peer_turnover and peer_turnover_cutoff .. _connect_seed_every_n_download: .. raw:: html +-------------------------------+------+---------+ | name | type | default | +===============================+======+=========+ | connect_seed_every_n_download | int | 10 | +-------------------------------+------+---------+ this setting controls the priority of downloading torrents over seeding or finished torrents when it comes to making peer connections. Peer connections are throttled by the connection_speed and the half-open connection limit. This makes peer connections a limited resource. Torrents that still have pieces to download are prioritized by default, to avoid having many seeding torrents use most of the connection attempts and only give one peer every now and then to the downloading torrent. libtorrent will loop over the downloading torrents to connect a peer each, and every n:th connection attempt, a finished torrent is picked to be allowed to connect to a peer. This setting controls n. .. _max_http_recv_buffer_size: .. raw:: html +---------------------------+------+------------+ | name | type | default | +===========================+======+============+ | max_http_recv_buffer_size | int | 4*1024*204 | +---------------------------+------+------------+ the max number of bytes to allow an HTTP response to be when announcing to trackers or downloading .torrent files via the ``url`` provided in ``add_torrent_params``. .. _max_retry_port_bind: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | max_retry_port_bind | int | 10 | +---------------------+------+---------+ if binding to a specific port fails, should the port be incremented by one and tried again? This setting specifies how many times to retry a failed port bind .. _alert_mask: .. raw:: html +------------+------+---------------------------+ | name | type | default | +============+======+===========================+ | alert_mask | int | alert::error_notification | +------------+------+---------------------------+ a bitmask combining flags from alert::category_t defining which kinds of alerts to receive .. _out_enc_policy: .. _in_enc_policy: .. raw:: html +----------------+------+---------------------------+ | name | type | default | +================+======+===========================+ | out_enc_policy | int | settings_pack::pe_enabled | +----------------+------+---------------------------+ | in_enc_policy | int | settings_pack::pe_enabled | +----------------+------+---------------------------+ control the settings for incoming and outgoing connections respectively. see enc_policy enum for the available options. Keep in mind that protocol encryption degrades performance in several respects: 1. It prevents "zero copy" disk buffers being sent to peers, since each peer needs to mutate the data (i.e. encrypt it) the data must be copied per peer connection rather than sending the same buffer to multiple peers. 2. The encryption itself requires more CPU than plain bittorrent protocol. The highest cost is the Diffie Hellman exchange on connection setup. 3. The encryption handshake adds several round-trips to the connection setup, and delays transferring data. .. _allowed_enc_level: .. raw:: html +-------------------+------+------------------------+ | name | type | default | +===================+======+========================+ | allowed_enc_level | int | settings_pack::pe_both | +-------------------+------+------------------------+ determines the encryption level of the connections. This setting will adjust which encryption scheme is offered to the other peer, as well as which encryption scheme is selected by the client. See enc_level enum for options. .. _inactive_down_rate: .. _inactive_up_rate: .. raw:: html +--------------------+------+---------+ | name | type | default | +====================+======+=========+ | inactive_down_rate | int | 2048 | +--------------------+------+---------+ | inactive_up_rate | int | 2048 | +--------------------+------+---------+ the download and upload rate limits for a torrent to be considered active by the queuing mechanism. A torrent whose download rate is less than ``inactive_down_rate`` and whose upload rate is less than ``inactive_up_rate`` for ``auto_manage_startup`` seconds, is considered inactive, and another queued torrent may be started. This logic is disabled if ``dont_count_slow_torrents`` is false. .. _proxy_type: .. raw:: html +------------+------+---------------------+ | name | type | default | +============+======+=====================+ | proxy_type | int | settings_pack::none | +------------+------+---------------------+ proxy to use, defaults to none. see proxy_type_t. .. _proxy_port: .. raw:: html +------------+------+---------+ | name | type | default | +============+======+=========+ | proxy_port | int | 0 | +------------+------+---------+ the port of the proxy server .. _i2p_port: .. raw:: html +----------+------+---------+ | name | type | default | +==========+======+=========+ | i2p_port | int | 0 | +----------+------+---------+ sets the i2p_ SAM bridge port to connect to. set the hostname with the ``i2p_hostname`` setting. .. _i2p: http://www.i2p2.de .. _cache_size_volatile: .. raw:: html +---------------------+------+---------+ | name | type | default | +=====================+======+=========+ | cache_size_volatile | int | 256 | +---------------------+------+---------+ this determines the max number of volatile disk cache blocks. If the number of volatile blocks exceed this limit, other volatile blocks will start to be evicted. A disk cache block is volatile if it has low priority, and should be one of the first blocks to be evicted under pressure. For instance, blocks pulled into the cache as the result of calculating a piece hash are volatile. These blocks don't represent potential interest among peers, so the value of keeping them in the cache is limited. .. _urlseed_max_request_bytes: .. raw:: html +---------------------------+------+------------------+ | name | type | default | +===========================+======+==================+ | urlseed_max_request_bytes | int | 16 * 1024 * 1024 | +---------------------------+------+------------------+ The maximum request range of an url seed in bytes. This value defines the largest possible sequential web seed request. Default is 16 * 1024 * 1024. Lower values are possible but will be ignored if they are lower then piece size. This value should be related to your download speed to prevent libtorrent from creating too many expensive http requests per second. You can select a value as high as you want but keep in mind that libtorrent can't create parallel requests if the first request did already select the whole file. If you combine bittorrent seeds with web seeds and pick strategies like rarest first you may find your web seed requests split into smaller parts because we don't download already picked pieces twice. .. _web_seed_name_lookup_retry: .. raw:: html +----------------------------+------+---------+ | name | type | default | +============================+======+=========+ | web_seed_name_lookup_retry | int | 1800 | +----------------------------+------+---------+ time to wait until a new retry of a web seed name lookup .. _close_file_interval: .. raw:: html +---------------------+------+---------------------+ | name | type | default | +=====================+======+=====================+ | close_file_interval | int | CLOSE_FILE_INTERVAL | +---------------------+------+---------------------+ the number of seconds between closing the file opened the longest ago. 0 means to disable the feature. The purpose of this is to periodically close files to trigger the operating system flushing disk cache. Specifically it has been observed to be required on windows to not have the disk cache grow indefinitely. This defaults to 120 seconds on windows, and disabled on other systems. .. _utp_cwnd_reduce_timer: .. raw:: html +-----------------------+------+---------+ | name | type | default | +=======================+======+=========+ | utp_cwnd_reduce_timer | int | 100 | +-----------------------+------+---------+ When uTP experiences packet loss, it will reduce the congestion window, and not reduce it again for this many milliseconds, even if experiencing another lost packet. libtorrent-rasterbar-1.1.13/docs/single-page-ref.html000066400000000000000000036610761351156116000225510ustar00rootroot00000000000000 libtorrent API Documentation

libtorrent API Documentation

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

overview

The interface of libtorrent consists of a few classes. The main class is the session, it contains the main loop that serves all torrents.

The basic usage is as follows:

Each class and function is described in this manual, you may want to have a look at the tutorial as well.

For a description on how to create torrent files, see create_torrent.

forward declarations

Forward declaring types from the libtorrent namespace is discouraged as it may break in future releases. Instead include libtorrent/fwd.hpp for forward declarations of all public types in libtorrent.

trouble shooting

A common problem developers are facing is torrents stopping without explanation. Here is a description on which conditions libtorrent will stop your torrents, how to find out about it and what to do about it.

Make sure to keep track of the paused state, the error state and the upload mode of your torrents. By default, torrents are auto-managed, which means libtorrent will pause, resume, scrape them and take them out of upload-mode automatically.

Whenever a torrent encounters a fatal error, it will be stopped, and the torrent_status::error will describe the error that caused it. If a torrent is auto managed, it is scraped periodically and paused or resumed based on the number of downloaders per seed. This will effectively seed torrents that are in the greatest need of seeds.

If a torrent hits a disk write error, it will be put into upload mode. This means it will not download anything, but only upload. The assumption is that the write error is caused by a full disk or write permission errors. If the torrent is auto-managed, it will periodically be taken out of the upload mode, trying to write things to the disk again. This means torrent will recover from certain disk errors if the problem is resolved. If the torrent is not auto managed, you have to call set_upload_mode() to turn downloading back on again.

For a more detailed guide on how to trouble shoot performance issues, see troubleshooting

network primitives

There are a few typedefs in the libtorrent namespace which pulls in network types from the boost::asio namespace. These are:

typedef boost::asio::ip::address address;
typedef boost::asio::ip::address_v4 address_v4;
typedef boost::asio::ip::address_v6 address_v6;
using boost::asio::ip::tcp;
using boost::asio::ip::udp;

These are declared in the <libtorrent/socket.hpp> header.

The using statements will give easy access to:

tcp::endpoint
udp::endpoint

Which are the endpoint types used in libtorrent. An endpoint is an address with an associated port.

For documentation on these types, please refer to the asio documentation.

exceptions

Many functions in libtorrent have two versions, one that throws exceptions on errors and one that takes an error_code reference which is filled with the error code on errors.

There is one exception class that is used for errors in libtorrent, it is based on boost.system's error_code class to carry the error code.

For more information, see libtorrent_exception and error_code_enum.

translating error codes

The error_code::message() function will typically return a localized error string, for system errors. That is, errors that belong to the generic or system category.

Errors that belong to the libtorrent error category are not localized however, they are only available in english. In order to translate libtorrent errors, compare the error category of the error_code object against libtorrent::libtorrent_category(), and if matches, you know the error code refers to the list above. You can provide your own mapping from error code to string, which is localized. In this case, you cannot rely on error_code::message() to generate your strings.

The numeric values of the errors are part of the API and will stay the same, although new error codes may be appended at the end.

Here's a simple example of how to translate error codes:

std::string error_code_to_string(boost::system::error_code const& ec)
{
        if (ec.category() != libtorrent::libtorrent_category())
        {
                return ec.message();
        }
        // the error is a libtorrent error

        int code = ec.value();
        static const char const* swedish[] =
        {
                "inget fel",
                "en fil i torrenten kolliderar med en fil fran en annan torrent",
                "hash check misslyckades",
                "torrentfilen ar inte en dictionary",
                "'info'-nyckeln saknas eller ar korrupt i torrentfilen",
                "'info'-faltet ar inte en dictionary",
                "'piece length' faltet saknas eller ar korrupt i torrentfilen",
                "torrentfilen saknar namnfaltet",
                "ogiltigt namn i torrentfilen (kan vara en attack)",
                // ... more strings here
        };

        // use the default error string in case we don't have it
        // in our translated list
        if (code < 0 || code >= sizeof(swedish)/sizeof(swedish[0]))
                return ec.message();

        return swedish[code];
}

queuing

libtorrent supports queuing. Queuing is a mechanism to automatically pause and resume torrents based on certain criteria. The criteria depends on the overall state the torrent is in (checking, downloading or seeding).

To opt-out of the queuing logic, make sure your torrents are added with the add_torrent_params::flag_auto_managed bit cleared. Or call torrent_handle::auto_managed(false) on the torrent handle.

The overall purpose of the queuing logic is to improve performance under arbitrary torrent downloading and seeding load. For example, if you want to download 100 torrents on a limited home connection, you improve performance by downloading them one at a time (or maybe two at a time), over downloading them all in parallel. The benefits are:

  • the average completion time of a torrent is half of what it would be if all downloaded in parallel.
  • The amount of upload capacity is more likely to reach the reciprocation rate of your peers, and is likely to improve your return on investment (download to upload ratio)
  • your disk I/O load is likely to be more local which may improve I/O performance and decrease fragmentation.

There are fundamentally 3 seaparate queues:

  • checking torrents
  • downloading torrents
  • seeding torrents

Every torrent that is not seeding has a queue number associated with it, this is its place in line to be started. See torrent_status::queue_position.

On top of the limits of each queue, there is an over arching limit, set in settings_pack::active_limit. The auto manager will never start more than this number of torrents (with one exception described below). Non-auto-managed torrents are exempt from this logic, and not counted.

At a regular interval, torrents are checked if there needs to be any re-ordering of which torrents are active and which are queued. This interval can be controlled via settings_pack::auto_manage_interval.

For queuing to work, resume data needs to be saved and restored for all torrents. See torrent_handle::save_resume_data().

queue position

The torrents in the front of the queue are started and the rest are ordered by their queue position. Any newly added torrent is placed at the end of the queue. Once a torrent is removed or turns into a seed, its queue position is -1 and all torrents that used to be after it in the queue, decreases their position in order to fill the gap.

The queue positions are always contiguous, in a sequence without any gaps.

Lower queue position means closer to the front of the queue, and will be started sooner than torrents with higher queue positions.

To query a torrent for its position in the queue, or change its position, see: torrent_handle::queue_position(), torrent_handle::queue_position_up(), torrent_handle::queue_position_down(), torrent_handle::queue_position_top() and torrent_handle::queue_position_bottom().

checking queue

The checking queue affects torrents in the torrent_status::checking or torrent_status::allocating state that are auto-managed.

The checking queue will make sure that (of the torrents in its queue) no more than settings_pack::active_checking_limit torrents are started at any given time. Once a torrent completes checking and moves into a diffferent state, the next in line will be started for checking.

Any torrent added force-started or force-stopped (i.e. the auto managed flag is not set), will not be subject to this limit and they will all check independently and in parallel.

Once a torrent completes the checking of its files, or fastresume data, it will be put in the queue for downloading and potentially start downloading immediately. In order to add a torrent and check its files without starting the download, it can be added in stop_when_ready mode. See add_torrent_params::flag_stop_when_ready. This flag will stop the torrent once it is ready to start downloading.

This is conceptually the same as waiting for the torrent_checked_alert and then call:

h.auto_managed(false);
h.pause();

With the important distinction that it entirely avoids the brief window where the torrent is in downloading state.

downloading queue

Similarly to the checking queue, the downloading queue will make sure that no more than settings_pack::active_downloads torrents are in the downloading state at any given time.

The torrent_status::queue_position is used again here to determine who is next in line to be started once a downloading torrent completes or is stopped/removed.

seeding queue

The seeding queue does not use torrent_status::queue_position to determine which torrent to seed. Instead, it estimates the demand for the torrent to be seeded. A torrent with few other seeds and many downloaders is assumed to have a higher demand of more seeds than one with many seeds and few downloaders.

It limits the number of started seeds to settings_pack::active_seeds.

On top of this basic bias, seed priority can be controller by specifying a seed ratio (the upload to download ratio), a seed-time ratio (the download time to seeding time ratio) and a seed-time (the absolute time to be seeding a torrent). Until all those targets are hit, the torrent will be prioritized for seeding.

Among torrents that have met their seed target, torrents where we don't know of any other seed take strict priority.

In order to avoid flapping, torrents that were started less than 30 minutes ago also have priority to keep seeding.

Finally, for torrents where none of the above apply, they are prioritized based on the download to seed ratio.

The relevant settings to control these limits are settings_pack::share_ratio_limit, settings_pack::seed_time_ratio_limit and settings_pack::seed_time_limit.

queuing options

In addition to simply starting and stopping torrents, the queuing mechanism can have more fine grained control of the resources used by torrents.

half-started torrents

In addition to the downloading and seeding limits, there are limits on actions torrents perform. The downloading and seeding limits control whether peers are allowed at all, and if peers are not allowed, torrents are stopped and don't do anything. If peers are allowed, torrents may:

  1. announce to trackers
  2. announce to the DHT
  3. announce to local peer discovery (local service discovery)

Each of those actions are associated with a cost and hence may need a separate limit. These limits are controlled by settings_pack::active_tracker_limit, settings_pack::active_dht_limit and settings_pack::active_lsd_limit respectively.

Specifically, announcing to a tracker is typically cheaper than announcing to the DHT. active_dht_limit will limit the number of torrents that are allowed to announce to the DHT. The highest priority ones will, and the lower priority ones won't. The will still be considered started though, and any incoming peers will still be accepted.

If you do not wish to impose such limits (basically, if you do not wish to have half-started torrents) make sure to set these limits to -1 (infinite).

prefer seeds

In the case where active_downloads + active_seeds > active_limit, there's an ambiguity whether the downloads should be satisfied first or the seeds. To disambiguate this case, the settings_pack::auto_manage_prefer_seeds determines whether seeds are preferred or not.

inactive torrents

Torrents that are not transferring any bytes (downloading or uploading) have a relatively low cost to be started. It's possible to exempt such torrents from the download and seed queues by setting settings_pack::dont_count_slow_torrents to true.

Since it sometimes may take a few minutes for a newly started torrent to find peers and be unchoked, or find peers that are interested in requesting data, torrents are not considered inactive immadiately. There must be an extended period of no transfers before it is considered inactive and exempt from the queuing limits.

fast resume

The fast resume mechanism is a way to remember which pieces are downloaded and where they are put between sessions. You can generate fast resume data by calling save_resume_data() on torrent_handle. You can then save this data to disk and use it when resuming the torrent. libtorrent will not check the piece hashes then, and rely on the information given in the fast-resume data. The fast-resume data also contains information about which blocks, in the unfinished pieces, were downloaded, so it will not have to start from scratch on the partially downloaded pieces.

To use the fast-resume data you simply give it to async_add_torrent() and add_torrent(), and it will skip the time consuming checks. It may have to do the checking anyway, if the fast-resume data is corrupt or doesn't fit the storage for that torrent, then it will not trust the fast-resume data and just do the checking.

file format

The file format is a bencoded dictionary containing the following fields:

file-format string: "libtorrent resume file"
file-version integer: 1
info-hash string, the info hash of the torrent this data is saved for.
blocks per piece integer, the number of blocks per piece. Must be: piece_size / (16 * 1024). Clamped to be within the range [1, 256]. It is the number of blocks per (normal sized) piece. Usually each block is 16 * 1024 bytes in size. But if piece size is greater than 4 megabytes, the block size will increase.
pieces A string with piece flags, one character per piece. Bit 1 means we have that piece. Bit 2 means we have verified that this piece is correct. This only applies when the torrent is in seed_mode.
slots list of integers. The list maps slots to piece indices. It tells which piece is on which slot. If piece index is -2 it means it is free, that there's no piece there. If it is -1, means the slot isn't allocated on disk yet. The pieces have to meet the following requirement:
total_uploaded integer. The number of bytes that have been uploaded in total for this torrent.
total_downloaded integer. The number of bytes that have been downloaded in total for this torrent.
active_time integer. The number of seconds this torrent has been active. i.e. not paused.
seeding_time integer. The number of seconds this torrent has been active and seeding.
num_seeds integer. An estimate of the number of seeds on this torrent when the resume data was saved. This is scrape data or based on the peer list if scrape data is unavailable.
num_downloaders integer. An estimate of the number of downloaders on this torrent when the resume data was last saved. This is used as an initial estimate until we acquire up-to-date scrape info.
last_upload integer. The number of seconds since epoch when we last uploaded payload to a peer on this torrent.
last_download integer. The number of seconds since epoch when we last downloaded payload from a peer on this torrent.
last_scrape integer. The number of seconds since epoch when we last sent a scrape request to a tracker on this torrent.
upload_rate_limit integer. In case this torrent has a per-torrent upload rate limit, this is that limit. In bytes per second.
download_rate_limit integer. The download rate limit for this torrent in case one is set, in bytes per second.
max_connections integer. The max number of peer connections this torrent may have, if a limit is set.
max_uploads integer. The max number of unchoked peers this torrent may have, if a limit is set.
seed_mode integer. 1 if the torrent is in seed mode, 0 otherwise.
file_priority list of integers. One entry per file in the torrent. Each entry is the priority of the file with the same index.
piece_priority string of bytes. Each byte is interpreted as an integer and is the priority of that piece.
auto_managed integer. 1 if the torrent is auto managed, otherwise 0.
sequential_download integer. 1 if the torrent is in sequential download mode, 0 otherwise.
paused integer. 1 if the torrent is paused, 0 otherwise.
trackers list of lists of strings. The top level list lists all tracker tiers. Each second level list is one tier of trackers.
mapped_files list of strings. If any file in the torrent has been renamed, this entry contains a list of all the filenames. In the same order as in the torrent file.
url-list list of strings. List of url-seed URLs used by this torrent. The urls are expected to be properly encoded and not contain any illegal url characters.
httpseeds list of strings. List of httpseed URLs used by this torrent. The urls are expected to be properly encoded and not contain any illegal url characters.
merkle tree string. In case this torrent is a merkle torrent, this is a string containing the entire merkle tree, all nodes, including the root and all leaves. The tree is not necessarily complete, but complete enough to be able to send any piece that we have, indicated by the have bitmask.
save_path string. The save path where this torrent was saved. This is especially useful when moving torrents with move_storage() since this will be updated.
peers string. This string contains IPv4 and port pairs of peers we were connected to last session. The endpoints are in compact representation. 4 bytes IPv4 address followed by 2 bytes port. Hence, the length of this string should be divisible by 6.
banned_peers string. This string has the same format as peers but instead represent IPv4 peers that we have banned.
peers6 string. This string contains IPv6 and port pairs of peers we were connected to last session. The endpoints are in compact representation. 16 bytes IPv6 address followed by 2 bytes port. The length of this string should be divisible by 18.
banned_peers6 string. This string has the same format as peers6 but instead represent IPv6 peers that we have banned.
info If this field is present, it should be the info-dictionary of the torrent this resume data is for. Its SHA-1 hash must match the one in the info-hash field. When present, the torrent is loaded from here, meaning the torrent can be added purely from resume data (no need to load the .torrent file separately). This may have performance advantages.
unfinished

list of dictionaries. Each dictionary represents an piece, and has the following layout:

piece integer, the index of the piece this entry refers to.
bitmask string, a binary bitmask representing the blocks that have been downloaded in this piece.
adler32 The adler32 checksum of the data in the blocks specified by bitmask.
file sizes list where each entry corresponds to a file in the file list in the metadata. Each entry has a list of two values, the first value is the size of the file in bytes, the second is the time stamp when the last time someone wrote to it. This information is used to compare with the files on disk. All the files must match exactly this information in order to consider the resume data as current. Otherwise a full re-check is issued.
allocation The allocation mode for the storage. Can be either full or sparse. If this is full, the file sizes and timestamps are disregarded. Pieces are assumed not to have moved around even if the files have been modified after the last resume data checkpoint.

storage allocation

There are two modes in which storage (files on disk) are allocated in libtorrent.

  1. The traditional full allocation mode, where the entire files are filled up with zeros before anything is downloaded. Files are allocated on demand, the first time anything is written to them. The main benefit of this mode is that it avoids creating heavily fragmented files.
  2. The sparse allocation, sparse files are used, and pieces are downloaded directly to where they belong. This is the recommended (and default) mode.

sparse allocation

On filesystems that supports sparse files, this allocation mode will only use as much space as has been downloaded.

The main drawback of this mode is that it may create heavily fragmented files.

  • It does not require an allocation pass on startup.

full allocation

When a torrent is started in full allocation mode, the disk-io thread will make sure that the entire storage is allocated, and fill any gaps with zeros. It will of course still check for existing pieces and fast resume data. The main drawbacks of this mode are:

  • It may take longer to start the torrent, since it will need to fill the files with zeros. This delay is linear to the size of the download.
  • The download may occupy unnecessary disk space between download sessions.
  • Disk caches usually perform poorly with random access to large files and may slow down the download some.

The benefits of this mode are:

  • Downloaded pieces are written directly to their final place in the files and the total number of disk operations will be fewer and may also play nicer to filesystems' file allocation, and reduce fragmentation.
  • No risk of a download failing because of a full disk during download, once all files have been created.

HTTP seeding

There are two kinds of HTTP seeding. One with that assumes a smart (and polite) client and one that assumes a smart server. These are specified in BEP 19 and BEP 17 respectively.

libtorrent supports both. In the libtorrent source code and API, BEP 19 urls are typically referred to as url seeds and BEP 17 urls are typically referred to as HTTP seeds.

The libtorrent implementation of BEP 19 assumes that, if the URL ends with a slash ('/'), the filename should be appended to it in order to request pieces from that file. The way this works is that if the torrent is a single-file torrent, only that filename is appended. If the torrent is a multi-file torrent, the torrent's name '/' the file name is appended. This is the same directory structure that libtorrent will download torrents into.

dynamic loading of torrent files

Note

This feature will be removed in the next major release of libtorrent. As an alternative, torrents can be loaded on demand by plugins.

libtorrent has a feature that can unload idle torrents from memory. The purpose of this is to support being active on many more torrents than the RAM permits. This is useful for both embedded devices that have limited RAM and servers seeding tens of thousands of torrents.

The most significant parts of loaded torrents that use RAM are the piece hashes (20 bytes per piece) and the file list. The entire info-dictionary of the .torrent file is kept in RAM.

In order to activate the dynamic loading of torrent files, set the load function on the session. See set_load_function().

When a load function is set on the session, the dynamic load/unload feature is enabled. Torrents are kept in an LRU. Every time an operation is performed, on a torrent or from a peer, that requires the metadata of the torrent to be loaded, the torrent is bumped up in the LRU. When a torrent is paused or queued, it is demoted to the least recently used torrent in the LRU, since it's a good candidate for eviction.

To configure how many torrents are allowed to be loaded at the same time, set settings_pack::active_loaded_limit on the session.

Torrents can be exempt from being unloaded by being pinned. Pinned torrents still count against the limit, but are never considered for eviction. You can either pin a torrent when adding it, in add_torrent_params (see async_add_torrent() and add_torrent()), or after ading it with the set_pinned() function on torrent_handle.

Torrents that start out without metadata (e.g. magnet links or http downloads) are automatically pinned. This is important in order to give the client a chance to save the metadata to disk once it's received (see metadata_received_alert).

Once the metadata is saved to disk, it might make sense to unpin the torrent.

piece picker

The piece picker in libtorrent has the following features:

  • rarest first
  • sequential download
  • random pick
  • reverse order picking
  • parole mode
  • prioritize partial pieces
  • prefer whole pieces
  • piece affinity by speed category
  • piece priorities

internal representation

It is optimized by, at all times, keeping a list of pieces ordered by rarity, randomly shuffled within each rarity class. This list is organized as a single vector of contigous memory in RAM, for optimal memory locality and to eliminate heap allocations and frees when updating rarity of pieces.

Expensive events, like a peer joining or leaving, are evaluated lazily, since it's cheaper to rebuild the whole list rather than updating every single piece in it. This means as long as no blocks are picked, peers joining and leaving is no more costly than a single peer joining or leaving. Of course the special cases of peers that have all or no pieces are optimized to not require rebuilding the list.

picker strategy

The normal mode of the picker is of course rarest first, meaning pieces that few peers have are preferred to be downloaded over pieces that more peers have. This is a fundamental algorithm that is the basis of the performance of bittorrent. However, the user may set the piece picker into sequential download mode. This mode simply picks pieces sequentially, always preferring lower piece indices.

When a torrent starts out, picking the rarest pieces means increased risk that pieces won't be completed early (since there are only a few peers they can be downloaded from), leading to a delay of having any piece to offer to other peers. This lack of pieces to trade, delays the client from getting started into the normal tit-for-tat mode of bittorrent, and will result in a long ramp-up time. The heuristic to mitigate this problem is to, for the first few pieces, pick random pieces rather than rare pieces. The threshold for when to leave this initial picker mode is determined by settings_pack::initial_picker_threshold.

reverse order

An orthogonal setting is reverse order, which is used for snubbed peers. Snubbed peers are peers that appear very slow, and might have timed out a piece request. The idea behind this is to make all snubbed peers more likely to be able to do download blocks from the same piece, concentrating slow peers on as few pieces as possible. The reverse order means that the most common pieces are picked, instead of the rarest pieces (or in the case of sequential download, the last pieces, intead of the first).

parole mode

Peers that have participated in a piece that failed the hash check, may be put in parole mode. This means we prefer downloading a full piece from this peer, in order to distinguish which peer is sending corrupt data. Whether to do this is or not is controlled by settings_pack::use_parole_mode.

In parole mode, the piece picker prefers picking one whole piece at a time for a given peer, avoiding picking any blocks from a piece any other peer has contributed to (since that would defeat the purpose of parole mode).

prioritize partial pieces

This setting determines if partially downloaded or requested pieces should always be preferred over other pieces. The benefit of doing this is that the number of partial pieces is minimized (and hence the turn-around time for downloading a block until it can be uploaded to others is minimized). It also puts less stress on the disk cache, since fewer partial pieces need to be kept in the cache. Whether or not to enable this is controlled by setting_pack::prioritize_partial_pieces.

The main benefit of not prioritizing partial pieces is that the rarest first algorithm gets to have more influence on which pieces are picked. The picker is more likely to truly pick the rarest piece, and hence improving the performance of the swarm.

This setting is turned on automatically whenever the number of partial pieces in the piece picker exceeds the number of peers we're connected to times 1.5. This is in order to keep the waste of partial pieces to a minimum, but still prefer rarest pieces.

prefer whole pieces

The prefer whole pieces setting makes the piece picker prefer picking entire pieces at a time. This is used by web connections (both http seeding standards), in order to be able to coalesce the small bittorrent requests to larger HTTP requests. This significantly improves performance when downloading over HTTP.

It is also used by peers that are downloading faster than a certain threshold. The main advantage is that these peers will better utilize the other peer's disk cache, by requesting all blocks in a single piece, from the same peer.

This threshold is controlled by the settings_pack::whole_pieces_threshold setting.

TODO: piece priorities

predictive piece announce

In order to improve performance, libtorrent supports a feature called predictive piece announce. When enabled, it will make libtorrent announce that we have pieces to peers, before we truly have them. The most important case is to announce a piece as soon as it has been downloaded and passed the hash check, but not yet been written to disk. In this case, there is a risk the piece will fail to be written to disk, in which case we won't have the piece anymore, even though we announced it to peers.

The other case is when we're very close to completing the download of a piece and assume it will pass the hash check, we can announce it to peers to make it available one round-trip sooner than otherwise. This lets libtorrent start uploading the piece to interested peers immediately when the piece complete, instead of waiting one round-trip for the peers to request it.

This makes for the implementation slightly more complicated, since piece will have more states and more complicated transitions. For instance, a piece could be:

  1. hashed but not fully written to disk
  2. fully written to disk but not hashed
  3. not fully downloaded
  4. downloaded and hash checked

Once a piece is fully downloaded, the hash check could complete before any of the write operations or it could complete after all write operations are complete.

peer classes

The peer classes feature in libtorrent allows a client to define custom groups of peers and rate limit them individually. Each such group is called a peer class. There are a few default peer classes that are always created:

  • global - all peers belong to this class, except peers on the local network
  • local peers - all peers on the local network belongs to this class TCP peers
  • tcp class - all peers connected over TCP belong to this class

The TCP peers class is used by the uTP/TCP balancing logic, if it's enabled, to throttle TCP peers. The global and local classes are used to adjust the global rate limits.

When the rate limits are adjusted for a specific torrent, a class is created implicitly for that torrent.

The default peer class IDs are defined as enums in the session class:

enum {
        global_peer_class_id,
        tcp_peer_class_id,
        local_peer_class_id
};

The default peer classes are automatically created on session startup, and configured to apply to each respective type of connection. There's nothing preventing a client from reconfiguring the peer class ip- and type filters to disable or customize which peers they apply to. See set_peer_class_filter() and set_peer_class_type_filter().

A peer class can be considered a more general form of lables that some clients have. Peer classes however are not just applied to torrents, but ultimately the peers.

Peer classes can be created with the create_peer_class() call (on the session object), and deleted with the delete_peer_class() call.

Peer classes are configured with the set_peer_class() get_peer_class() calls.

Custom peer classes can be assigned based on the peer's IP address or the type of transport protocol used. See set_peer_class_filter() and set_peer_class_type_filter() for more information.

peer class examples

Here are a few examples of common peer class operations.

To make the global rate limit apply to local peers as well, update the IP-filter based peer class assignment:

std::uint32_t const mask = 1 << lt::session::global_peer_class_id;
ip_filter f;

// for every IPv4 address, assign the global peer class
f.add_rule(make_address("0.0.0.0"), make_address("255.255.255.255"), mask);

// for every IPv6 address, assign the global peer class
f.add_rule(make_address("::")
        , make_address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
        , mask);
ses.set_peer_class_filter(f);

To make uTP sockets exempt from rate limiting:

peer_class_type_filter flt = ses.get_peer_class_type_filter();
// filter out the global and local peer class for uTP sockets, if these
// classes are set by the IP filter
flt.disallow(peer_class_type_filter::utp_socket, session::global_peer_class_id);
flt.disallow(peer_class_type_filter::utp_socket, session::local_peer_class_id);

// this filter should not add the global or local peer class to utp sockets
flt.remove(peer_class_type_filter::utp_socket, session::global_peer_class_id);
flt.remove(peer_class_type_filter::utp_socket, session::local_peer_class_id);

ses.set_peer_class_type_filter(flt);

To make all peers on the internal network unthrottled:

std::uint32_t const mask = 1 << lt::session::global_peer_class_id;
ip_filter f;

// for every IPv4 address, assign the global peer class
f.add_rule(make_address("0.0.0.0"), make_address("255.255.255.255"), mask);

// for every address on the local metwork, set the mask to 0
f.add_rule(make_address("10.0.0.0"), make_address("10.255.255.255"), 0);
ses.set_peer_class_filter(f);

SSL torrents

Torrents may have an SSL root (CA) certificate embedded in them. Such torrents are called SSL torrents. An SSL torrent talks to all bittorrent peers over SSL. The protocols are layered like this:

utp_stack.png

During the SSL handshake, both peers need to authenticate by providing a certificate that is signed by the CA certificate found in the .torrent file. These peer certificates are expected to be privided to peers through some other means than bittorrent. Typically by a peer generating a certificate request which is sent to the publisher of the torrent, and the publisher returning a signed certificate.

In libtorrent, set_ssl_certificate() in torrent_handle is used to tell libtorrent where to find the peer certificate and the private key for it. When an SSL torrent is loaded, the torrent_need_cert_alert is posted to remind the user to provide a certificate.

A peer connecting to an SSL torrent MUST provide the SNI TLS extension (server name indication). The server name is the hex encoded info-hash of the torrent to connect to. This is required for the client accepting the connection to know which certificate to present.

SSL connections are accepted on a separate socket from normal bittorrent connections. To pick which port the SSL socket should bind to, set settings_pack::ssl_listen to a different port. It defaults to port 4433. This setting is only taken into account when the normal listen socket is opened (i.e. just changing this setting won't necessarily close and re-open the SSL socket). To not listen on an SSL socket at all, set ssl_listen to 0.

This feature is only available if libtorrent is build with openssl support (TORRENT_USE_OPENSSL) and requires at least openSSL version 1.0, since it needs SNI support.

Peer certificates must have at least one SubjectAltName field of type dNSName. At least one of the fields must exactly match the name of the torrent. This is a byte-by-byte comparison, the UTF-8 encoding must be identical (i.e. there's no unicode normalization going on). This is the recommended way of verifying certificates for HTTPS servers according to RFC 2818. Note the difference that for torrents only dNSName fields are taken into account (not IP address fields). The most specific (i.e. last) Common Name field is also taken into account if no SubjectAltName did not match.

If any of these fields contain a single asterisk ("*"), the certificate is considered covering any torrent, allowing it to be reused for any torrent.

The purpose of matching the torrent name with the fields in the peer certificate is to allow a publisher to have a single root certificate for all torrents it distributes, and issue separate peer certificates for each torrent. A peer receiving a certificate will not necessarily be able to access all torrents published by this root certificate (only if it has a "star cert").

testing

To test incoming SSL connections to an SSL torrent, one can use the following openssl command:

openssl s_client -cert <peer-certificate>.pem -key <peer-private-key>.pem -CAfile \
   <torrent-cert>.pem -debug -connect 127.0.0.1:4433 -tls1 -servername <info-hash>

To create a root certificate, the Distinguished Name (DN) is not taken into account by bittorrent peers. You still need to specify something, but from libtorrent's point of view, it doesn't matter what it is. libtorrent only makes sure the peer certificates are signed by the correct root certificate.

One way to create the certificates is to use the CA.sh script that comes with openssl, like thisi (don't forget to enter a common Name for the certificate):

CA.sh -newca
CA.sh -newreq
CA.sh -sign

The torrent certificate is located in ./demoCA/private/demoCA/cacert.pem, this is the pem file to include in the .torrent file.

The peer's certificate is located in ./newcert.pem and the certificate's private key in ./newkey.pem.

session statistics

libtorrent provides a mechanism to query performance and statistics counters from its internals. This is primarily useful for troubleshooting of production systems and performance tuning.

The statistics consists of two fundamental types. counters and gauges. A counter is a monotonically increasing value, incremented every time some event occurs. For example, every time the network thread wakes up because a socket became readable will increment a counter. Another example is every time a socket receives n bytes, a counter is incremented by n.

Counters are the most flexible of metrics. It allows the program to sample the counter at any interval, and calculate average rates of increments to the counter. Some events may be rare and need to be sampled over a longer period in order to get userful rates, where other events may be more frequent and evenly distributed that sampling it frequently yields useful values. Counters also provides accurate overall counts. For example, converting samples of a download rate into a total transfer count is not accurate and takes more samples. Converting an increasing counter into a rate is easy and flexible.

Gauges measure the instantaneous state of some kind. This is used for metrics that are not counting events or flows, but states that can fluctuate. For example, the number of torrents that are currenly being downloaded.

It's important to know whether a value is a counter or a gauge in order to interpret it correctly. In order to query libtorrent for which counters and gauges are available, call session_stats_metrics(). This will return metadata about the values available for inspection in libtorrent. It will include whether a value is a counter or a gauge. The key information it includes is the index used to extract the actual measurements for a specific counter or gauge.

In order to take a sample, call post_session_stats() in the session object. This will result in a session_stats_alert being posted. In this alert object, there is an array of values, these values make up the sample. The value index in the stats metric indicates which index the metric's value is stored in.

The mapping between metric and value is not stable across versions of libtorrent. Always query the metrics first, to find out the index at which the value is stored, before interpreting the values array in the session_stats_alert. The mapping will not change during the runtime of your process though, it's tied to a specific libtorrent version. You only have to query the mapping once on startup (or every time libtorrent.so is loaded, if it's done dynamically).

The available stats metrics are:

name type
peer.error_peers counter
peer.disconnected_peers counter

error_peers is the total number of peer disconnects caused by an error (not initiated by this client) and disconnected initiated by this client (disconnected_peers).

name type
peer.eof_peers counter
peer.connreset_peers counter
peer.connrefused_peers counter
peer.connaborted_peers counter
peer.notconnected_peers counter
peer.perm_peers counter
peer.buffer_peers counter
peer.unreachable_peers counter
peer.broken_pipe_peers counter
peer.addrinuse_peers counter
peer.no_access_peers counter
peer.invalid_arg_peers counter
peer.aborted_peers counter

these counters break down the peer errors into more specific categories. These errors are what the underlying transport reported (i.e. TCP or uTP)

name type
peer.piece_requests counter
peer.max_piece_requests counter
peer.invalid_piece_requests counter
peer.choked_piece_requests counter
peer.cancelled_piece_requests counter
peer.piece_rejects counter

the total number of incoming piece requests we've received followed by the number of rejected piece requests for various reasons. max_piece_requests mean we already had too many outstanding requests from this peer, so we rejected it. cancelled_piece_requests are ones where the other end explicitly asked for the piece to be rejected.

name type
peer.error_incoming_peers counter
peer.error_outgoing_peers counter

these counters break down the peer errors into whether they happen on incoming or outgoing peers.

name type
peer.error_rc4_peers counter
peer.error_encrypted_peers counter

these counters break down the peer errors into whether they happen on encrypted peers (just encrypted handshake) and rc4 peers (full stream encryption). These can indicate whether encrypted peers are more or less likely to fail

name type
peer.error_tcp_peers counter
peer.error_utp_peers counter

these counters break down the peer errors into whether they happen on uTP peers or TCP peers. these may indicate whether one protocol is more error prone

name type
peer.connect_timeouts counter
peer.uninteresting_peers counter
peer.timeout_peers counter
peer.no_memory_peers counter
peer.too_many_peers counter
peer.transport_timeout_peers counter
peer.num_banned_peers counter
peer.banned_for_hash_failure counter
peer.connection_attempts counter
peer.connection_attempt_loops counter
peer.incoming_connections counter

these counters break down the reasons to disconnect peers.

name type
peer.num_tcp_peers gauge
peer.num_socks5_peers gauge
peer.num_http_proxy_peers gauge
peer.num_utp_peers gauge
peer.num_i2p_peers gauge
peer.num_ssl_peers gauge
peer.num_ssl_socks5_peers gauge
peer.num_ssl_http_proxy_peers gauge
peer.num_ssl_utp_peers gauge
peer.num_peers_half_open gauge
peer.num_peers_connected gauge
peer.num_peers_up_interested gauge
peer.num_peers_down_interested gauge
peer.num_peers_up_unchoked_all gauge
peer.num_peers_up_unchoked_optimistic gauge
peer.num_peers_up_unchoked gauge
peer.num_peers_down_unchoked gauge
peer.num_peers_up_requests gauge
peer.num_peers_down_requests gauge
peer.num_peers_end_game gauge
peer.num_peers_up_disk gauge
peer.num_peers_down_disk gauge

the number of peer connections for each kind of socket. these counts include half-open (connecting) peers. num_peers_up_unchoked_all is the total number of unchoked peers, whereas num_peers_up_unchoked only are unchoked peers that count against the limit (i.e. excluding peers that are unchoked because the limit doesn't apply to them). num_peers_up_unchoked_optimistic is the number of optimistically unchoked peers.

name type
net.on_read_counter counter
net.on_write_counter counter
net.on_tick_counter counter
net.on_lsd_counter counter
net.on_lsd_peer_counter counter
net.on_udp_counter counter
net.on_accept_counter counter
net.on_disk_queue_counter counter
net.on_disk_counter counter

These counters count the number of times the network thread wakes up for each respective reason. If these counters are very large, it may indicate a performance issue, causing the network thread to wake up too ofte, wasting CPU. mitigate it by increasing buffers and limits for the specific trigger that wakes up the thread.

name type
net.sent_payload_bytes counter
net.sent_bytes counter
net.sent_ip_overhead_bytes counter
net.sent_tracker_bytes counter
net.recv_payload_bytes counter
net.recv_bytes counter
net.recv_ip_overhead_bytes counter
net.recv_tracker_bytes counter

total number of bytes sent and received by the session

name type
net.limiter_up_queue gauge
net.limiter_down_queue gauge

the number of sockets currently waiting for upload and download bandwidht from the rate limiter.

name type
net.limiter_up_bytes gauge
net.limiter_down_bytes gauge

the number of upload and download bytes waiting to be handed out from the rate limiter.

name type
net.recv_failed_bytes counter

the number of bytes downloaded that had to be discarded because they failed the hash check

name type
net.recv_redundant_bytes counter

the number of downloaded bytes that were discarded because they were downloaded multiple times (from different peers)

name type
net.has_incoming_connections gauge

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.

name type
ses.num_checking_torrents gauge
ses.num_stopped_torrents gauge
ses.num_upload_only_torrents gauge
ses.num_downloading_torrents gauge
ses.num_seeding_torrents gauge
ses.num_queued_seeding_torrents gauge
ses.num_queued_download_torrents gauge
ses.num_error_torrents gauge

these gauges count the number of torrents in different states. Each torrent only belongs to one of these states. For torrents that could belong to multiple of these, the most prominent in picked. For instance, a torrent with an error counts as an error-torrent, regardless of its other state.

name type
ses.non_filter_torrents gauge

the number of torrents that don't have the IP filter applied to them.

name type
ses.num_loaded_torrents gauge
ses.num_pinned_torrents gauge

the number of torrents that are currently loaded

name type
ses.num_piece_passed counter
ses.num_piece_failed counter
ses.num_have_pieces counter
ses.num_total_pieces_added counter

these count the number of times a piece has passed the hash check, the number of times a piece was successfully written to disk and the number of total possible pieces added by adding torrents. e.g. when adding a torrent with 1000 piece, num_total_pieces_added is incremented by 1000.

name type
ses.torrent_evicted_counter counter

this counts the number of times a torrent has been evicted (only applies when dynamic loading of torrent files is enabled).

name type
ses.num_unchoke_slots gauge

the number of allowed unchoked peers

name type
ses.num_incoming_choke counter
ses.num_incoming_unchoke counter
ses.num_incoming_interested counter
ses.num_incoming_not_interested counter
ses.num_incoming_have counter
ses.num_incoming_bitfield counter
ses.num_incoming_request counter
ses.num_incoming_piece counter
ses.num_incoming_cancel counter
ses.num_incoming_dht_port counter
ses.num_incoming_suggest counter
ses.num_incoming_have_all counter
ses.num_incoming_have_none counter
ses.num_incoming_reject counter
ses.num_incoming_allowed_fast counter
ses.num_incoming_ext_handshake counter
ses.num_incoming_pex counter
ses.num_incoming_metadata counter
ses.num_incoming_extended counter
ses.num_outgoing_choke counter
ses.num_outgoing_unchoke counter
ses.num_outgoing_interested counter
ses.num_outgoing_not_interested counter
ses.num_outgoing_have counter
ses.num_outgoing_bitfield counter
ses.num_outgoing_request counter
ses.num_outgoing_piece counter
ses.num_outgoing_cancel counter
ses.num_outgoing_dht_port counter
ses.num_outgoing_suggest counter
ses.num_outgoing_have_all counter
ses.num_outgoing_have_none counter
ses.num_outgoing_reject counter
ses.num_outgoing_allowed_fast counter
ses.num_outgoing_ext_handshake counter
ses.num_outgoing_pex counter
ses.num_outgoing_metadata counter
ses.num_outgoing_extended counter

bittorrent message counters. These counters are incremented every time a message of the corresponding type is received from or sent to a bittorrent peer.

name type
ses.waste_piece_timed_out counter
ses.waste_piece_cancelled counter
ses.waste_piece_unknown counter
ses.waste_piece_seed counter
ses.waste_piece_end_game counter
ses.waste_piece_closing counter

the number of wasted downloaded bytes by reason of the bytes being wasted.

name type
picker.piece_picker_partial_loops counter
picker.piece_picker_suggest_loops counter
picker.piece_picker_sequential_loops counter
picker.piece_picker_reverse_rare_loops counter
picker.piece_picker_rare_loops counter
picker.piece_picker_rand_start_loops counter
picker.piece_picker_rand_loops counter
picker.piece_picker_busy_loops counter

the number of pieces considered while picking pieces

name type
picker.reject_piece_picks counter
picker.unchoke_piece_picks counter
picker.incoming_redundant_piece_picks counter
picker.incoming_piece_picks counter
picker.end_game_piece_picks counter
picker.snubbed_piece_picks counter
picker.interesting_piece_picks counter
picker.hash_fail_piece_picks counter

This breaks down the piece picks into the event that triggered it

name type
disk.write_cache_blocks gauge
disk.read_cache_blocks gauge

These gauges indicate how many blocks are currently in use as dirty disk blocks (write_cache_blocks) and read cache blocks, respectively. deprecates cache_status::read_cache_size. The sum of these gauges deprecates cache_status::cache_size.

name type
disk.request_latency gauge

the number of microseconds it takes from receiving a request from a peer until we're sending the response back on the socket.

name type
disk.pinned_blocks gauge
disk.disk_blocks_in_use gauge

disk_blocks_in_use indicates how many disk blocks are currently in use, either as dirty blocks waiting to be written or blocks kept around in the hope that a peer will request it or in a peer send buffer. This gauge deprecates cache_status::total_used_buffers.

name type
disk.queued_disk_jobs gauge
disk.num_running_disk_jobs gauge
disk.num_read_jobs gauge
disk.num_write_jobs gauge
disk.num_jobs gauge
disk.blocked_disk_jobs gauge
disk.num_writing_threads gauge
disk.num_running_threads gauge

queued_disk_jobs is the number of disk jobs currently queued, waiting to be executed by a disk thread. Deprecates cache_status::job_queue_length.

name type
disk.queued_write_bytes gauge
disk.arc_mru_size gauge
disk.arc_mru_ghost_size gauge
disk.arc_mfu_size gauge
disk.arc_mfu_ghost_size gauge
disk.arc_write_size gauge
disk.arc_volatile_size gauge

the number of bytes we have sent to the disk I/O thread for writing. Every time we hear back from the disk I/O thread with a completed write job, this is updated to the number of bytes the disk I/O thread is actually waiting for to be written (as opposed to bytes just hanging out in the cache)

name type
disk.num_blocks_written counter
disk.num_blocks_read counter

the number of blocks written and read from disk in total. A block is 16 kiB. num_blocks_written and num_blocks_read deprecates cache_status::blocks_written and cache_status::blocks_read respectively.

name type
disk.num_blocks_hashed counter

the total number of blocks run through SHA-1 hashing

name type
disk.num_blocks_cache_hits counter

the number of blocks read from the disk cache Deprecates cache_info::blocks_read_hit.

name type
disk.num_write_ops counter
disk.num_read_ops counter

the number of disk I/O operation for reads and writes. One disk operation may transfer more then one block. These counters deprecates cache_status::writes and cache_status::reads.

name type
disk.num_read_back counter

the number of blocks that had to be read back from disk in order to hash a piece (when verifying against the piece hash)

name type
disk.disk_read_time counter
disk.disk_write_time counter
disk.disk_hash_time counter
disk.disk_job_time counter

cumulative time spent in various disk jobs, as well as total for all disk jobs. Measured in microseconds

name type
disk.num_fenced_read gauge
disk.num_fenced_write gauge
disk.num_fenced_hash gauge
disk.num_fenced_move_storage gauge
disk.num_fenced_release_files gauge
disk.num_fenced_delete_files gauge
disk.num_fenced_check_fastresume gauge
disk.num_fenced_save_resume_data gauge
disk.num_fenced_rename_file gauge
disk.num_fenced_stop_torrent gauge
disk.num_fenced_cache_piece gauge
disk.num_fenced_flush_piece gauge
disk.num_fenced_flush_hashed gauge
disk.num_fenced_flush_storage gauge
disk.num_fenced_trim_cache gauge
disk.num_fenced_file_priority gauge
disk.num_fenced_load_torrent gauge
disk.num_fenced_clear_piece gauge
disk.num_fenced_tick_storage gauge

for each kind of disk job, a counter of how many jobs of that kind are currently blocked by a disk fence

name type
dht.dht_nodes gauge

The number of nodes in the DHT routing table

name type
dht.dht_node_cache gauge

The number of replacement nodes in the DHT routing table

name type
dht.dht_torrents gauge

the number of torrents currently tracked by our DHT node

name type
dht.dht_peers gauge

the number of peers currently tracked by our DHT node

name type
dht.dht_immutable_data gauge

the number of immutable data items tracked by our DHT node

name type
dht.dht_mutable_data gauge

the number of mutable data items tracked by our DHT node

name type
dht.dht_allocated_observers gauge

the number of RPC observers currently allocated

name type
dht.dht_messages_in counter
dht.dht_messages_out counter

the total number of DHT messages sent and received

name type
dht.dht_messages_out_dropped counter

the number of outgoing messages that failed to be sent

name type
dht.dht_bytes_in counter
dht.dht_bytes_out counter

the total number of bytes sent and received by the DHT

name type
dht.dht_ping_in counter
dht.dht_ping_out counter
dht.dht_find_node_in counter
dht.dht_find_node_out counter
dht.dht_get_peers_in counter
dht.dht_get_peers_out counter
dht.dht_announce_peer_in counter
dht.dht_announce_peer_out counter
dht.dht_get_in counter
dht.dht_get_out counter
dht.dht_put_in counter
dht.dht_put_out counter

the number of DHT messages we've sent and received by kind.

name type
dht.dht_invalid_announce counter
dht.dht_invalid_get_peers counter
dht.dht_invalid_put counter
dht.dht_invalid_get counter

the number of failed incoming DHT requests by kind of request

name type
utp.utp_packet_loss counter
utp.utp_timeout counter
utp.utp_packets_in counter
utp.utp_packets_out counter
utp.utp_fast_retransmit counter
utp.utp_packet_resend counter
utp.utp_samples_above_target counter
utp.utp_samples_below_target counter
utp.utp_payload_pkts_in counter
utp.utp_payload_pkts_out counter
utp.utp_invalid_pkts_in counter
utp.utp_redundant_pkts_in counter

uTP counters. Each counter represents the number of time each event has occurred.

name type
utp.num_utp_idle gauge
utp.num_utp_syn_sent gauge
utp.num_utp_connected gauge
utp.num_utp_fin_sent gauge
utp.num_utp_close_wait gauge
utp.num_utp_deleted gauge

the number of uTP sockets in each respective state

name type
sock_bufs.socket_send_size3 counter
sock_bufs.socket_send_size4 counter
sock_bufs.socket_send_size5 counter
sock_bufs.socket_send_size6 counter
sock_bufs.socket_send_size7 counter
sock_bufs.socket_send_size8 counter
sock_bufs.socket_send_size9 counter
sock_bufs.socket_send_size10 counter
sock_bufs.socket_send_size11 counter
sock_bufs.socket_send_size12 counter
sock_bufs.socket_send_size13 counter
sock_bufs.socket_send_size14 counter
sock_bufs.socket_send_size15 counter
sock_bufs.socket_send_size16 counter
sock_bufs.socket_send_size17 counter
sock_bufs.socket_send_size18 counter
sock_bufs.socket_send_size19 counter
sock_bufs.socket_send_size20 counter
sock_bufs.socket_recv_size3 counter
sock_bufs.socket_recv_size4 counter
sock_bufs.socket_recv_size5 counter
sock_bufs.socket_recv_size6 counter
sock_bufs.socket_recv_size7 counter
sock_bufs.socket_recv_size8 counter
sock_bufs.socket_recv_size9 counter
sock_bufs.socket_recv_size10 counter
sock_bufs.socket_recv_size11 counter
sock_bufs.socket_recv_size12 counter
sock_bufs.socket_recv_size13 counter
sock_bufs.socket_recv_size14 counter
sock_bufs.socket_recv_size15 counter
sock_bufs.socket_recv_size16 counter
sock_bufs.socket_recv_size17 counter
sock_bufs.socket_recv_size18 counter
sock_bufs.socket_recv_size19 counter
sock_bufs.socket_recv_size20 counter

the buffer sizes accepted by socket send and receive calls respectively. The larger the buffers are, the more efficient, because it reqire fewer system calls per byte. The size is 1 << n, where n is the number at the end of the counter name. i.e. 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576 bytes

home

Core

stats_metric

Declared in "libtorrent/session_stats.hpp"

describes one statistics metric from the session. For more information, see the session statistics section.

struct stats_metric
{
   enum metric_type_t
   {
      type_counter,
      type_gauge,
   };

   char const* name;
   int value_index;
   metric_type_t type;
};

enum metric_type_t

Declared in "libtorrent/session_stats.hpp"

name value description
type_counter 0  
type_gauge 1  

peer_class_info

Declared in "libtorrent/peer_class.hpp"

holds settings for a peer class. Used in set_peer_class() and get_peer_class() calls.

struct peer_class_info
{
   bool ignore_unchoke_slots;
   int connection_limit_factor;
   std::string label;
   int upload_limit;
   int download_limit;
   int upload_priority;
   int download_priority;
};
ignore_unchoke_slots
ignore_unchoke_slots determines whether peers should always unchoke a peer, regardless of the choking algorithm, or if it should honor the unchoke slot limits. It's used for local peers by default. If any of the peer classes a peer belongs to has this set to true, that peer will be unchoked at all times.
connection_limit_factor
adjusts the connection limit (global and per torrent) that applies to this peer class. By default, local peers are allowed to exceed the normal connection limit for instance. This is specified as a percent factor. 100 makes the peer class apply normally to the limit. 200 means as long as there are fewer connections than twice the limit, we accept this peer. This factor applies both to the global connection limit and the per-torrent limit. Note that if not used carefully one peer class can potentially completely starve out all other over time.
label
not used by libtorrent. It's intended as a potentially user-facing identifier of this peer class.
upload_limit download_limit
transfer rates limits for the whole peer class. They are specified in bytes per second and apply to the sum of all peers that are members of this class.
upload_priority download_priority
relative priorities used by the bandwidth allocator in the rate limiter. If no rate limits are in use, the priority is not used either. Priorities start at 1 (0 is not a valid priority) and may not exceed 255.

session_handle

Declared in "libtorrent/session_handle.hpp"

struct session_handle
{
   session_handle ();
   session_handle (aux::session_impl* impl);
   bool is_valid () const;
   void load_state (bdecode_node const& e, boost::uint32_t flags = 0xffffffff);
   void save_state (entry& e, boost::uint32_t flags = 0xffffffff) const;
   void refresh_torrent_status (std::vector<torrent_status>* ret
      , boost::uint32_t flags = 0) const;
   void get_torrent_status (std::vector<torrent_status>* ret
      , boost::function<bool(torrent_status const&)> const& pred
      , boost::uint32_t flags = 0) const;
   void post_torrent_updates (boost::uint32_t flags = 0xffffffff);
   void post_session_stats ();
   void post_dht_stats ();
   torrent_handle find_torrent (sha1_hash const& info_hash) const;
   std::vector<torrent_handle> get_torrents () const;
   void async_add_torrent (add_torrent_params const& params);
   torrent_handle add_torrent (add_torrent_params const& params, error_code& ec);
   torrent_handle add_torrent (add_torrent_params const& params);
   void resume ();
   void pause ();
   bool is_paused () const;
   void set_load_function (user_load_function_t fun);
   void get_cache_info (cache_status* ret, torrent_handle h = torrent_handle(), int flags = 0) const;
   bool is_dht_running () const;
   dht_settings get_dht_settings () const;
   void set_dht_settings (dht_settings const& settings);
   void set_dht_storage (dht::dht_storage_constructor_type sc);
   void add_dht_node (std::pair<std::string, int> const& node);
   void dht_get_item (sha1_hash const& target);
   void dht_get_item (boost::array<char, 32> key
      , std::string salt = std::string());
   sha1_hash dht_put_item (entry data);
   void dht_put_item (boost::array<char, 32> key
      , boost::function<void(entry&, boost::array<char,64>&
      , boost::uint64_t&, std::string const&)> cb
      , std::string salt = std::string());
   void dht_get_peers (sha1_hash const& info_hash);
   void dht_announce (sha1_hash const& info_hash, int port = 0, int flags = 0);
   void dht_direct_request (udp::endpoint ep, entry const& e, void* userdata = 0);
   void add_extension (boost::function<boost::shared_ptr<torrent_plugin>(
      torrent_handle const&, void*)> ext);
   void add_extension (boost::shared_ptr<plugin> ext);
   ip_filter get_ip_filter () const;
   void set_ip_filter (ip_filter const& f);
   void set_port_filter (port_filter const& f);
   void set_key (int key);
   bool is_listening () const;
   unsigned short listen_port () const;
   unsigned short ssl_listen_port () const;
   ip_filter get_peer_class_filter () const;
   void set_peer_class_filter (ip_filter const& f);
   void set_peer_class_type_filter (peer_class_type_filter const& f);
   peer_class_type_filter get_peer_class_type_filter () const;
   peer_class_t create_peer_class (char const* name);
   void delete_peer_class (peer_class_t cid);
   peer_class_info get_peer_class (peer_class_t cid);
   void set_peer_class (peer_class_t cid, peer_class_info const& pci);
   void remove_torrent (const torrent_handle& h, int options = 0);
   settings_pack get_settings () const;
   void apply_settings (settings_pack const& s);
   void pop_alerts (std::vector<alert*>* alerts);
   void set_alert_notify (boost::function<void()> const& fun);
   alert* wait_for_alert (time_duration max_wait);
   void delete_port_mapping (int handle);
   int add_port_mapping (protocol_type t, int external_port, int local_port);
   aux::session_impl* native_handle () const;

   enum save_state_flags_t
   {
      save_settings,
      save_dht_settings,
      save_dht_state,
   };

   enum options_t
   {
      delete_files,
      delete_partfile,
   };

   enum session_flags_t
   {
      add_default_plugins,
      start_default_features,
   };

   enum protocol_type
   {
      udp,
      tcp,
   };
};

load_state() save_state()

void load_state (bdecode_node const& e, boost::uint32_t flags = 0xffffffff);
void save_state (entry& e, boost::uint32_t flags = 0xffffffff) const;

loads and saves all session settings, including dht_settings, encryption settings and proxy settings. save_state writes all keys to the entry that's passed in, which needs to either not be initialized, or initialized as a dictionary.

load_state expects a bdecode_node which can be built from a bencoded buffer with bdecode().

The flags argument is used to filter which parts of the session state to save or load. By default, all state is saved/restored (except for the individual torrents). see save_state_flags_t

When saving settings, there are two fields that are not loaded. peer_fingerprint and user_agent. Those are left as configured by the session_settings passed to the session constructor or subsequently set via apply_settings().

get_torrent_status() refresh_torrent_status()

void refresh_torrent_status (std::vector<torrent_status>* ret
      , boost::uint32_t flags = 0) const;
void get_torrent_status (std::vector<torrent_status>* ret
      , boost::function<bool(torrent_status const&)> const& pred
      , boost::uint32_t flags = 0) const;

Note

these calls are potentially expensive and won't scale well with lots of torrents. If you're concerned about performance, consider using post_torrent_updates() instead.

get_torrent_status returns a vector of the torrent_status for every torrent which satisfies pred, which is a predicate function which determines if a torrent should be included in the returned set or not. Returning true means it should be included and false means excluded. The flags argument is the same as to torrent_handle::status(). Since pred is guaranteed to be called for every torrent, it may be used to count the number of torrents of different categories as well.

refresh_torrent_status takes a vector of torrent_status structs (for instance the same vector that was returned by get_torrent_status() ) and refreshes the status based on the handle member. It is possible to use this function by first setting up a vector of default constructed torrent_status objects, only initializing the handle member, in order to request the torrent status for multiple torrents in a single call. This can save a significant amount of time if you have a lot of torrents.

Any torrent_status object whose handle member is not referring to a valid torrent are ignored.

The intended use of these functions is to start off by calling get_torrent_status() to get a list of all torrents that match your criteria. Then call refresh_torrent_status() on that list. This will only refresh the status for the torrents in your list, and thus ignore all other torrents you might be running. This may save a significant amount of time, especially if the number of torrents you're interested in is small. In order to keep your list of interested torrents up to date, you can either call get_torrent_status() from time to time, to include torrents you might have become interested in since the last time. In order to stop refreshing a certain torrent, simply remove it from the list.

post_torrent_updates()

void post_torrent_updates (boost::uint32_t flags = 0xffffffff);

This functions instructs the session to post the state_update_alert, containing the status of all torrents whose state changed since the last time this function was called.

Only torrents who has the state subscription flag set will be included. This flag is on by default. See add_torrent_params. the flags argument is the same as for torrent_handle::status(). see torrent_handle::status_flags_t.

post_session_stats()

void post_session_stats ();

This function will post a session_stats_alert object, containing a snapshot of the performance counters from the internals of libtorrent. To interpret these counters, query the session via session_stats_metrics().

For more information, see the session statistics section.

post_dht_stats()

void post_dht_stats ();

This will cause a dht_stats_alert to be posted.

find_torrent() get_torrents()

torrent_handle find_torrent (sha1_hash const& info_hash) const;
std::vector<torrent_handle> get_torrents () const;

find_torrent() looks for a torrent with the given info-hash. In case there is such a torrent in the session, a torrent_handle to that torrent is returned. In case the torrent cannot be found, an invalid torrent_handle is returned.

See torrent_handle::is_valid() to know if the torrent was found or not.

get_torrents() returns a vector of torrent_handles to all the torrents currently in the session.

add_torrent() async_add_torrent()

void async_add_torrent (add_torrent_params const& params);
torrent_handle add_torrent (add_torrent_params const& params, error_code& ec);
torrent_handle add_torrent (add_torrent_params const& params);

You add torrents through the add_torrent() function where you give an object with all the parameters. The add_torrent() overloads will block until the torrent has been added (or failed to be added) and returns an error code and a torrent_handle. In order to add torrents more efficiently, consider using async_add_torrent() which returns immediately, without waiting for the torrent to add. Notification of the torrent being added is sent as add_torrent_alert.

The overload that does not take an error_code throws an exception on error and is not available when building without exception support. The torrent_handle returned by add_torrent() can be used to retrieve information about the torrent's progress, its peers etc. It is also used to abort a torrent.

If the torrent you are trying to add already exists in the session (is either queued for checking, being checked or downloading) add_torrent() will throw libtorrent_exception which derives from std::exception unless duplicate_is_error is set to false. In that case, add_torrent() will return the handle to the existing torrent.

all torrent_handles must be destructed before the session is destructed!

pause() resume() is_paused()

void resume ();
void pause ();
bool is_paused () const;

Pausing the session has the same effect as pausing every torrent in it, except that torrents will not be resumed by the auto-manage mechanism. Resuming will restore the torrents to their previous paused state. i.e. the session pause state is separate from the torrent pause state. A torrent is inactive if it is paused or if the session is paused.

set_load_function()

void set_load_function (user_load_function_t fun);

the feature of dynamically loading/unloading torrents is deprecated and discouraged

This function enables dynamic loading of torrent files. When a torrent is unloaded but needs to be available in memory, this function is called from within the libtorrent network thread. From within this thread, you can not use any of the public APIs of libtorrent itself. The the info-hash of the torrent is passed in to the function and it is expected to fill in the passed in vector<char> with the .torrent file corresponding to it.

If there is an error loading the torrent file, the error_code (ec) should be set to reflect the error. In such case, the torrent itself is stopped and set to an error state with the corresponding error code.

Given that the function is called from the internal network thread of libtorrent, it's important to not stall. libtorrent will not be able to send nor receive any data until the function call returns.

The signature of the function to pass in is:

void fun(sha1_hash const& info_hash, std::vector<char>& buf, error_code& ec);

get_cache_info()

void get_cache_info (cache_status* ret, torrent_handle h = torrent_handle(), int flags = 0) const;

Fills in the cache_status struct with information about the given torrent. If flags is session::disk_cache_no_pieces the cache_status::pieces field will not be set. This may significantly reduce the cost of this call.

get_dht_settings() is_dht_running() set_dht_settings()

bool is_dht_running () const;
dht_settings get_dht_settings () const;
void set_dht_settings (dht_settings const& settings);

set_dht_settings sets some parameters available to the dht node. See dht_settings for more information.

is_dht_running() returns true if the DHT support has been started and false otherwise.

get_dht_settings() returns the current settings

set_dht_storage()

void set_dht_storage (dht::dht_storage_constructor_type sc);

set_dht_storage set a dht custom storage constructor function to be used internally when the dht is created.

Since the dht storage is a critical component for the dht behavior, this function will only be effective the next time the dht is started. If you never touch this feature, a default map-memory based storage is used.

If you want to make sure the dht is initially created with your custom storage, create a session with the setting settings_pack::enable_dht to false, set your constructor function and call apply_settings with settings_pack::enable_dht to true.

add_dht_node()

void add_dht_node (std::pair<std::string, int> const& node);

add_dht_node takes a host name and port pair. That endpoint will be pinged, and if a valid DHT reply is received, the node will be added to the routing table.

dht_get_item()

void dht_get_item (sha1_hash const& target);

query the DHT for an immutable item at the target hash. the result is posted as a dht_immutable_item_alert.

dht_get_item()

void dht_get_item (boost::array<char, 32> key
      , std::string salt = std::string());

query the DHT for a mutable item under the public key key. this is an ed25519 key. salt is optional and may be left as an empty string if no salt is to be used. if the item is found in the DHT, a dht_mutable_item_alert is posted.

dht_put_item()

sha1_hash dht_put_item (entry data);

store the given bencoded data as an immutable item in the DHT. the returned hash is the key that is to be used to look the item up again. It's just the SHA-1 hash of the bencoded form of the structure.

dht_put_item()

void dht_put_item (boost::array<char, 32> key
      , boost::function<void(entry&, boost::array<char,64>&
      , boost::uint64_t&, std::string const&)> cb
      , std::string salt = std::string());

store a mutable item. The key is the public key the blob is to be stored under. The optional salt argument is a string that is to be mixed in with the key when determining where in the DHT the value is to be stored. The callback function is called from within the libtorrent network thread once we've found where to store the blob, possibly with the current value stored under the key. The values passed to the callback functions are:

entry& value
the current value stored under the key (may be empty). Also expected to be set to the value to be stored by the function.
boost::array<char,64>& signature
the signature authenticating the current value. This may be zeros if there is currently no value stored. The function is expected to fill in this buffer with the signature of the new value to store. To generate the signature, you may want to use the sign_mutable_item function.
boost::uint64_t& seq
current sequence number. May be zero if there is no current value. The function is expected to set this to the new sequence number of the value that is to be stored. Sequence numbers must be monotonically increasing. Attempting to overwrite a value with a lower or equal sequence number will fail, even if the signature is correct.
std::string const& salt
this is the salt that was used for this put call.

Since the callback function cb is called from within libtorrent, it is critical to not perform any blocking operations. Ideally not even locking a mutex. Pass any data required for this function along with the function object's context and make the function entirely self-contained. The only reason data blob's value is computed via a function instead of just passing in the new value is to avoid race conditions. If you want to update the value in the DHT, you must first retrieve it, then modify it, then write it back. The way the DHT works, it is natural to always do a lookup before storing and calling the callback in between is convenient.

dht_direct_request()

void dht_direct_request (udp::endpoint ep, entry const& e, void* userdata = 0);

Send an arbitrary DHT request directly to the specified endpoint. This function is intended for use by plugins. When a response is received or the request times out, a dht_direct_response_alert will be posted with the response (if any) and the userdata pointer passed in here. Since this alert is a response to an explicit call, it will always be posted, regardless of the alert mask.

add_extension()

void add_extension (boost::function<boost::shared_ptr<torrent_plugin>(
      torrent_handle const&, void*)> ext);
void add_extension (boost::shared_ptr<plugin> ext);

This function adds an extension to this session. The argument is a function object that is called with a torrent_handle and which should return a boost::shared_ptr<torrent_plugin>. To write custom plugins, see libtorrent plugins. For the typical bittorrent client all of these extensions should be added. The main plugins implemented in libtorrent are:

metadata extension
Allows peers to download the metadata (.torrent files) from the swarm directly. Makes it possible to join a swarm with just a tracker and info-hash.
#include <libtorrent/extensions/metadata_transfer.hpp>
ses.add_extension(&libtorrent::create_metadata_plugin);
uTorrent metadata
Same as metadata extension but compatible with uTorrent.
#include <libtorrent/extensions/ut_metadata.hpp>
ses.add_extension(&libtorrent::create_ut_metadata_plugin);
uTorrent peer exchange
Exchanges peers between clients.
#include <libtorrent/extensions/ut_pex.hpp>
ses.add_extension(&libtorrent::create_ut_pex_plugin);
smart ban plugin
A plugin that, with a small overhead, can ban peers that sends bad data with very high accuracy. Should eliminate most problems on poisoned torrents.
#include <libtorrent/extensions/smart_ban.hpp>
ses.add_extension(&libtorrent::create_smart_ban_plugin);

get_ip_filter() set_ip_filter()

ip_filter get_ip_filter () const;
void set_ip_filter (ip_filter const& f);

Sets a filter that will be used to reject and accept incoming as well as outgoing connections based on their originating ip address. The default filter will allow connections to any ip address. To build a set of rules for which addresses are accepted and not, see ip_filter.

Each time a peer is blocked because of the IP filter, a peer_blocked_alert is generated. get_ip_filter() Returns the ip_filter currently in the session. See ip_filter.

set_port_filter()

void set_port_filter (port_filter const& f);

apply port_filter f to incoming and outgoing peers. a port filter will reject making outgoing peer connections to certain remote ports. The main intention is to be able to avoid triggering certain anti-virus software by connecting to SMTP, FTP ports.

set_key()

void set_key (int key);

sets the key sent to trackers. If it's not set, it is initialized by libtorrent. The key may be used by the tracker to identify the peer potentially across you changing your IP.

listen_port() ssl_listen_port() is_listening()

bool is_listening () const;
unsigned short listen_port () const;
unsigned short ssl_listen_port () const;

is_listening() will tell you whether or not the session has successfully opened a listening port. If it hasn't, this function will return false, and then you can set a new settings_pack::listen_interfaces to try another interface and port to bind to.

listen_port() returns the port we ended up listening on.

get_peer_class_filter() set_peer_class_filter()

ip_filter get_peer_class_filter () const;
void set_peer_class_filter (ip_filter const& f);

Sets the peer class filter for this session. All new peer connections will take this into account and be added to the peer classes specified by this filter, based on the peer's IP address.

The ip-filter essentially maps an IP -> uint32. Each bit in that 32 bit integer represents a peer class. The least significant bit represents class 0, the next bit class 1 and so on.

For more info, see ip_filter.

For example, to make all peers in the range 200.1.1.0 - 200.1.255.255 belong to their own peer class, apply the following filter:

ip_filter f = ses.get_peer_class_filter();
peer_class_t const my_class = ses.create_peer_class("200.1.x.x IP range");
f.add_rule(make_address("200.1.1.0"), make_address("200.1.255.255")
        , 1 << my_class);
ses.set_peer_class_filter(f);

This setting only applies to new connections, it won't affect existing peer connections.

This function is limited to only peer class 0-31, since there are only 32 bits in the IP range mapping. Only the set bits matter; no peer class will be removed from a peer as a result of this call, peer classes are only added.

The peer_class argument cannot be greater than 31. The bitmasks representing peer classes in the peer_class_filter are 32 bits.

The get_peer_class_filter() function returns the current filter.

For more information, see peer classes.

set_peer_class_type_filter() get_peer_class_type_filter()

void set_peer_class_type_filter (peer_class_type_filter const& f);
peer_class_type_filter get_peer_class_type_filter () const;

Sets and gets the peer class type filter. This is controls automatic peer class assignments to peers based on what kind of socket it is.

It does not only support assigning peer classes, it also supports removing peer classes based on socket type.

The order of these rules being applied are:

  1. peer-class IP filter
  2. peer-class type filter, removing classes
  3. peer-class type filter, adding classes

For more information, see peer classes.

create_peer_class()

peer_class_t create_peer_class (char const* name);

Creates a new peer class (see peer classes) with the given name. The returned integer is the new peer class identifier. Peer classes may have the same name, so each invocation of this function creates a new class and returns a unique identifier.

Identifiers are assigned from low numbers to higher. So if you plan on using certain peer classes in a call to set_peer_class_filter(), make sure to create those early on, to get low identifiers.

For more information on peer classes, see peer classes.

delete_peer_class()

void delete_peer_class (peer_class_t cid);

This call dereferences the reference count of the specified peer class. When creating a peer class it's automatically referenced by 1. If you want to recycle a peer class, you may call this function. You may only call this function once per peer class you create. Calling it more than once for the same class will lead to memory corruption.

Since peer classes are reference counted, this function will not remove the peer class if it's still assigned to torrents or peers. It will however remove it once the last peer and torrent drops their references to it.

There is no need to call this function for custom peer classes. All peer classes will be properly destructed when the session object destructs.

For more information on peer classes, see peer classes.

get_peer_class() set_peer_class()

peer_class_info get_peer_class (peer_class_t cid);
void set_peer_class (peer_class_t cid, peer_class_info const& pci);

These functions queries information from a peer class and updates the configuration of a peer class, respectively.

cid must refer to an existing peer class. If it does not, the return value of get_peer_class() is undefined.

set_peer_class() sets all the information in the peer_class_info object in the specified peer class. There is no option to only update a single property.

A peer or torrent belonging to more than one class, the highest priority among any of its classes is the one that is taken into account.

For more information, see peer classes.

remove_torrent()

void remove_torrent (const torrent_handle& h, int options = 0);

remove_torrent() will close all peer connections associated with the torrent and tell the tracker that we've stopped participating in the swarm. This operation cannot fail. When it completes, you will receive a torrent_removed_alert.

The optional second argument options can be used to delete all the files downloaded by this torrent. To do so, pass in the value session::delete_files. The removal of the torrent is asynchronous, there is no guarantee that adding the same torrent immediately after it was removed will not throw a libtorrent_exception exception. Once the torrent is deleted, a torrent_deleted_alert is posted.

get_settings() apply_settings()

settings_pack get_settings () const;
void apply_settings (settings_pack const& s);

Applies the settings specified by the settings_pack s. This is an asynchronous operation that will return immediately and actually apply the settings to the main thread of libtorrent some time later.

pop_alerts() wait_for_alert() set_alert_notify()

void pop_alerts (std::vector<alert*>* alerts);
void set_alert_notify (boost::function<void()> const& fun);
alert* wait_for_alert (time_duration max_wait);

Alerts is the main mechanism for libtorrent to report errors and events. pop_alerts fills in the vector passed to it with pointers to new alerts. The session still owns these alerts and they will stay valid until the next time pop_alerts is called. You may not delete the alert objects.

It is safe to call pop_alerts from multiple different threads, as long as the alerts themselves are not accessed once another thread calls pop_alerts. Doing this requires manual synchronization between the popping threads.

wait_for_alert will block the current thread for max_wait time duration, or until another alert is posted. If an alert is available at the time of the call, it returns immediately. The returned alert pointer is the head of the alert queue. wait_for_alert does not pop alerts from the queue, it merely peeks at it. The returned alert will stay valid until pop_alerts is called twice. The first time will pop it and the second will free it.

If there is no alert in the queue and no alert arrives within the specified timeout, wait_for_alert returns NULL.

In the python binding, wait_for_alert takes the number of milliseconds to wait as an integer.

The alert queue in the session will not grow indefinitely. Make sure to pop periodically to not miss notifications. To control the max number of alerts that's queued by the session, see settings_pack::alert_queue_size.

Some alerts are considered so important that they are posted even when the alert queue is full. Some alerts are considered mandatory and cannot be disabled by the alert_mask. For instance, save_resume_data_alert and save_resume_data_failed_alert are always posted, regardless of the alert mask.

To control which alerts are posted, set the alert_mask (settings_pack::alert_mask).

the set_alert_notify function lets the client set a function object to be invoked every time the alert queue goes from having 0 alerts to 1 alert. This function is called from within libtorrent, it may be the main thread, or it may be from within a user call. The intention of of the function is that the client wakes up its main thread, to poll for more alerts using pop_alerts(). If the notify function fails to do so, it won't be called again, until pop_alerts is called for some other reason. For instance, it could signal an eventfd, post a message to an HWND or some other main message pump. The actual retrieval of alerts should not be done in the callback. In fact, the callback should not block. It should not perform any expensive work. It really should just notify the main application thread.

add_port_mapping() delete_port_mapping()

void delete_port_mapping (int handle);
int add_port_mapping (protocol_type t, int external_port, int local_port);

add_port_mapping adds a port forwarding on UPnP and/or NAT-PMP, whichever is enabled. The return value is a handle referring to the port mapping that was just created. Pass it to delete_port_mapping() to remove it.

native_handle()

aux::session_impl* native_handle () const;

This function is intended only for use by plugins. This type does not have a stable API and should be relied on as little as possible.

enum save_state_flags_t

Declared in "libtorrent/session_handle.hpp"

name value description
save_settings 1 saves settings (i.e. the settings_pack)
save_dht_settings 2 saves dht_settings
save_dht_state 4 saves dht state such as nodes and node-id, possibly accelerating joining the DHT if provided at next session startup.

enum options_t

Declared in "libtorrent/session_handle.hpp"

name value description
delete_files 1 delete the files belonging to the torrent from disk. including the part-file, if there is one
delete_partfile 2 delete just the part-file associated with this torrent

enum session_flags_t

Declared in "libtorrent/session_handle.hpp"

name value description
add_default_plugins 1 this will add common extensions like ut_pex, ut_metadata, lt_tex smart_ban and possibly others.
start_default_features 2 this will start features like DHT, local service discovery, UPnP and NAT-PMP.

enum protocol_type

Declared in "libtorrent/session_handle.hpp"

name value description
udp 1  
tcp 2  

session_proxy

Declared in "libtorrent/session.hpp"

this is a holder for the internal session implementation object. Once the session destruction is explicitly initiated, this holder is used to synchronize the completion of the shutdown. The lifetime of this object may outlive session, causing the session destructor to not block. The session_proxy destructor will block however, until the underlying session is done shutting down.

class session_proxy
{
   ~session_proxy ();
   session_proxy (session_proxy const&) = default;
   session_proxy& operator= (session_proxy const&) = default;
   session_proxy ();
};

session_proxy() ~session_proxy() operator=()

~session_proxy ();
session_proxy (session_proxy const&) = default;
session_proxy& operator= (session_proxy const&) = default;
session_proxy ();

default constructor, does not refer to any session implementation object.

session

Declared in "libtorrent/session.hpp"

The session holds all state that spans multiple torrents. Among other things it runs the network loop and manages all torrents. Once it's created, the session object will spawn the main thread that will do all the work. The main thread will be idle as long it doesn't have any torrents to participate in.

You have some control over session configuration through the session::apply_settings() member function. To change one or more configuration options, create a settings_pack. object and fill it with the settings to be set and pass it in to session::apply_settings().

see apply_settings().

class session : public boost::noncopyable, public session_handle
{
   session (settings_pack const& pack = settings_pack()
      , int flags = start_default_features | add_default_plugins);
   session (settings_pack const& pack
      , io_service& ios
      , int flags = start_default_features | add_default_plugins);
   ~session ();
   session_proxy abort ();
};

session()

session (settings_pack const& pack = settings_pack()
      , int flags = start_default_features | add_default_plugins);

Constructs the session objects which acts as the container of torrents. It provides configuration options across torrents (such as rate limits, disk cache, ip filter etc.). In order to avoid a race condition between starting the session and configuring it, you can pass in a settings_pack object. Its settings will take effect before the session starts up.

The flags parameter can be used to start default features (UPnP & NAT-PMP) and default plugins (ut_metadata, ut_pex and smart_ban). The default is to start those features. If you do not want them to start, pass 0 as the flags parameter.

session()

session (settings_pack const& pack
      , io_service& ios
      , int flags = start_default_features | add_default_plugins);

overload of the constructor that takes an external io_service to run the session object on. This is primarily useful for tests that may want to run multiple sessions on a single io_service, or low resource systems where additional threads are expensive and sharing an io_service with other events is fine.

Warning

The session object does not cleanly terminate with an external io_service. The io_service::run() call _must_ have returned before it's safe to destruct the session. Which means you MUST call session::abort() and save the session_proxy first, then destruct the session object, then sync with the io_service, then destruct the session_proxy object.

~session()

~session ();

The destructor of session will notify all trackers that our torrents have been shut down. If some trackers are down, they will time out. All this before the destructor of session returns. So, it's advised that any kind of interface (such as windows) are closed before destructing the session object. Because it can take a few second for it to finish. The timeout can be set with apply_settings().

abort()

session_proxy abort ();

In case you want to destruct the session asynchronously, you can request a session destruction proxy. If you don't do this, the destructor of the session object will block while the trackers are contacted. If you keep one session_proxy to the session when destructing it, the destructor will not block, but start to close down the session, the destructor of the proxy will then synchronize the threads. So, the destruction of the session is performed from the session destructor call until the session_proxy destructor call. The session_proxy does not have any operations on it (since the session is being closed down, no operations are allowed on it). The only valid operation is calling the destructor:

class session_proxy
{
public:
        session_proxy();
        ~session_proxy()
};

bt_peer_connection_handle

Declared in "libtorrent/peer_connection_handle.hpp"

struct bt_peer_connection_handle : public peer_connection_handle
{
   explicit bt_peer_connection_handle (peer_connection_handle pc);
   bool support_extensions () const;
   bool packet_finished () const;
   bool supports_encryption () const;
   void switch_recv_crypto (boost::shared_ptr<crypto_plugin> crypto);
   void switch_send_crypto (boost::shared_ptr<crypto_plugin> crypto);
   boost::shared_ptr<bt_peer_connection> native_handle () const;
};

torrent_status

Declared in "libtorrent/torrent_status.hpp"

holds a snapshot of the status of a torrent, as queried by torrent_handle::status().

struct torrent_status
{
   bool operator== (torrent_status const& st) const;

   enum state_t
   {
      checking_files,
      downloading_metadata,
      downloading,
      finished,
      seeding,
      allocating,
      checking_resume_data,
   };

   torrent_handle handle;
   std::string save_path;
   std::string name;
   boost::weak_ptr<const torrent_info> torrent_file;
   time_duration next_announce;
   std::string current_tracker;
   boost::int64_t total_download;
   boost::int64_t total_upload;
   boost::int64_t total_payload_download;
   boost::int64_t total_payload_upload;
   boost::int64_t total_failed_bytes;
   boost::int64_t total_redundant_bytes;
   bitfield pieces;
   bitfield verified_pieces;
   boost::int64_t total_done;
   boost::int64_t total_wanted_done;
   boost::int64_t total_wanted;
   boost::int64_t all_time_upload;
   boost::int64_t all_time_download;
   time_t added_time;
   time_t completed_time;
   time_t last_seen_complete;
   storage_mode_t storage_mode;
   float progress;
   int progress_ppm;
   int queue_position;
   int download_rate;
   int upload_rate;
   int download_payload_rate;
   int upload_payload_rate;
   int num_seeds;
   int num_peers;
   int num_complete;
   int num_incomplete;
   int list_seeds;
   int list_peers;
   int connect_candidates;
   int num_pieces;
   int distributed_full_copies;
   int distributed_fraction;
   float distributed_copies;
   int block_size;
   int num_uploads;
   int num_connections;
   int uploads_limit;
   int connections_limit;
   int up_bandwidth_queue;
   int down_bandwidth_queue;
   int time_since_upload;
   int time_since_download;
   int active_time;
   int finished_time;
   int seeding_time;
   int seed_rank;
   int last_scrape;
   int priority;
   state_t state;
   bool need_save_resume;
   bool ip_filter_applies;
   bool upload_mode;
   bool share_mode;
   bool super_seeding;
   bool paused;
   bool auto_managed;
   bool sequential_download;
   bool is_seeding;
   bool is_finished;
   bool has_metadata;
   bool has_incoming;
   bool seed_mode;
   bool moving_storage;
   bool is_loaded;
   bool announcing_to_trackers;
   bool announcing_to_lsd;
   bool announcing_to_dht;
   bool stop_when_ready;
   sha1_hash info_hash;
};

operator==()

bool operator== (torrent_status const& st) const;

compares if the torrent status objects come from the same torrent. i.e. only the torrent_handle field is compared.

enum state_t

Declared in "libtorrent/torrent_status.hpp"

name value description
checking_files 1 The torrent has not started its download yet, and is currently checking existing files.
downloading_metadata 2 The torrent is trying to download metadata from peers. This assumes the metadata_transfer extension is in use.
downloading 3 The torrent is being downloaded. This is the state most torrents will be in most of the time. The progress meter will tell how much of the files that has been downloaded.
finished 4 In this state the torrent has finished downloading but still doesn't have the entire torrent. i.e. some pieces are filtered and won't get downloaded.
seeding 5 In this state the torrent has finished downloading and is a pure seeder.
allocating 6 If the torrent was started in full allocation mode, this indicates that the (disk) storage for the torrent is allocated.
checking_resume_data 7 The torrent is currently checking the fastresume data and comparing it to the files on disk. This is typically completed in a fraction of a second, but if you add a large number of torrents at once, they will queue up.
handle
a handle to the torrent whose status the object represents.
save_path
the path to the directory where this torrent's files are stored. It's typically the path as was given to async_add_torrent() or add_torrent() when this torrent was started. This field is only included if the torrent status is queried with torrent_handle::query_save_path.
name
the name of the torrent. Typically this is derived from the .torrent file. In case the torrent was started without metadata, and hasn't completely received it yet, it returns the name given to it when added to the session. See session::add_torrent. This field is only included if the torrent status is queried with torrent_handle::query_name.
torrent_file
set to point to the torrent_info object for this torrent. It's only included if the torrent status is queried with torrent_handle::query_torrent_file.
next_announce
the time until the torrent will announce itself to the tracker.
current_tracker
the URL of the last working tracker. If no tracker request has been successful yet, it's set to an empty string.
total_download total_upload
the number of bytes downloaded and uploaded to all peers, accumulated, this session only. The session is considered to restart when a torrent is paused and restarted again. When a torrent is paused, these counters are reset to 0. If you want complete, persistent, stats, see all_time_upload and all_time_download.
total_payload_download total_payload_upload
counts the amount of bytes send and received this session, but only the actual payload data (i.e the interesting data), these counters ignore any protocol overhead. The session is considered to restart when a torrent is paused and restarted again. When a torrent is paused, these counters are reset to 0.
total_failed_bytes
the number of bytes that has been downloaded and that has failed the piece hash test. In other words, this is just how much crap that has been downloaded since the torrent was last started. If a torrent is paused and then restarted again, this counter will be reset.
total_redundant_bytes
the number of bytes that has been downloaded even though that data already was downloaded. The reason for this is that in some situations the same data can be downloaded by mistake. When libtorrent sends requests to a peer, and the peer doesn't send a response within a certain timeout, libtorrent will re-request that block. Another situation when libtorrent may re-request blocks is when the requests it sends out are not replied in FIFO-order (it will re-request blocks that are skipped by an out of order block). This is supposed to be as low as possible. This only counts bytes since the torrent was last started. If a torrent is paused and then restarted again, this counter will be reset.
pieces
a bitmask that represents which pieces we have (set to true) and the pieces we don't have. It's a pointer and may be set to 0 if the torrent isn't downloading or seeding.
verified_pieces
a bitmask representing which pieces has had their hash checked. This only applies to torrents in seed mode. If the torrent is not in seed mode, this bitmask may be empty.
total_done
the total number of bytes of the file(s) that we have. All this does not necessarily has to be downloaded during this session (that's total_payload_download).
total_wanted_done
the number of bytes we have downloaded, only counting the pieces that we actually want to download. i.e. excluding any pieces that we have but have priority 0 (i.e. not wanted).
total_wanted
The total number of bytes we want to download. This may be smaller than the total torrent size in case any pieces are prioritized to 0, i.e. not wanted
all_time_upload all_time_download
are accumulated upload and download payload byte counters. They are saved in and restored from resume data to keep totals across sessions.
added_time
the posix-time when this torrent was added. i.e. what time(NULL) returned at the time.
completed_time
the posix-time when this torrent was finished. If the torrent is not yet finished, this is 0.
last_seen_complete
the time when we, or one of our peers, last saw a complete copy of this torrent.
storage_mode
The allocation mode for the torrent. See storage_mode_t for the options. For more information, see storage allocation.
progress
a value in the range [0, 1], that represents the progress of the torrent's current task. It may be checking files or downloading.
progress_ppm

progress parts per million (progress * 1000000) when disabling floating point operations, this is the only option to query progress

reflects the same value as progress, but instead in a range [0, 1000000] (ppm = parts per million). When floating point operations are disabled, this is the only alternative to the floating point value in progress.

queue_position
the position this torrent has in the download queue. If the torrent is a seed or finished, this is -1.
download_rate upload_rate
the total rates for all peers for this torrent. These will usually have better precision than summing the rates from all peers. The rates are given as the number of bytes per second.
download_payload_rate upload_payload_rate
the total transfer rate of payload only, not counting protocol chatter. This might be slightly smaller than the other rates, but if projected over a long time (e.g. when calculating ETA:s) the difference may be noticeable.
num_seeds
the number of peers that are seeding that this client is currently connected to.
num_peers
the number of peers this torrent currently is connected to. Peer connections that are in the half-open state (is attempting to connect) or are queued for later connection attempt do not count. Although they are visible in the peer list when you call get_peer_info().
num_complete num_incomplete
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. set to -1 if the tracker did not send any scrape data in its announce reply.
list_seeds list_peers
the number of seeds in our peer list and the total number of peers (including seeds). We are not necessarily connected to all the peers in our peer list. This is the number of peers we know of in total, including banned peers and peers that we have failed to connect to.
connect_candidates
the number of peers in this torrent's peer list that is a candidate to be connected to. i.e. It has fewer connect attempts than the max fail count, it is not a seed if we are a seed, it is not banned etc. If this is 0, it means we don't know of any more peers that we can try.
num_pieces
the number of pieces that has been downloaded. It is equivalent to: std::accumulate(pieces->begin(), pieces->end()). So you don't have to count yourself. This can be used to see if anything has updated since last time if you want to keep a graph of the pieces up to date.
distributed_full_copies
the number of distributed copies of the torrent. Note that one copy may be spread out among many peers. It tells how many copies there are currently of the rarest piece(s) among the peers this client is connected to.
distributed_fraction

tells the share of pieces that have more copies than the rarest piece(s). Divide this number by 1000 to get the fraction.

For example, if distributed_full_copies is 2 and distributed_fraction is 500, it means that the rarest pieces have only 2 copies among the peers this torrent is connected to, and that 50% of all the pieces have more than two copies.

If we are a seed, the piece picker is deallocated as an optimization, and piece availability is no longer tracked. In this case the distributed copies members are set to -1.

distributed_copies

the number of distributed copies of the file. note that one copy may be spread out among many peers. This is a floating point representation of the distributed copies.

the integer 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).
block_size
the size of a block, in bytes. A block is a sub piece, it is the number of bytes that each piece request asks for and the number of bytes that each bit in the partial_piece_info's bitset represents, see get_download_queue(). This is typically 16 kB, but it may be larger if the pieces are larger.
num_uploads
the number of unchoked peers in this torrent.
num_connections
the number of peer connections this torrent has, including half-open connections that hasn't completed the bittorrent handshake yet. This is always >= num_peers.
uploads_limit
the set limit of upload slots (unchoked peers) for this torrent.
connections_limit
the set limit of number of connections for this torrent.
up_bandwidth_queue down_bandwidth_queue
the number of peers in this torrent that are waiting for more bandwidth quota from the torrent rate limiter. This can determine if the rate you get from this torrent is bound by the torrents limit or not. If there is no limit set on this torrent, the peers might still be waiting for bandwidth quota from the global limiter, but then they are counted in the session_status object.
time_since_upload time_since_download
the number of seconds since any peer last uploaded from this torrent and the last time a downloaded piece passed the hash check, respectively. Note, when starting up a torrent that needs its files checked, piece may pass and that will be considered downloading for the purpose of this counter. -1 means there either hasn't been any uploading/downloading, or it was too long ago for libtorrent to remember (currently forgetting happens after about 18 hours)
active_time finished_time seeding_time
These keep track of the number of seconds this torrent has been active (not paused) and the number of seconds it has been active while being finished and active while being a seed. seeding_time should be <= finished_time which should be <= active_time. They are all saved in and restored from resume data, to keep totals across sessions.
seed_rank
A rank of how important it is to seed the torrent, it is used to determine which torrents to seed and which to queue. It is based on the peer to seed ratio from the tracker scrape. For more information, see queuing. Higher value means more important to seed
last_scrape
the number of seconds since this torrent acquired scrape data. If it has never done that, this value is -1.
priority
the priority of this torrent
state
the main state the torrent is in. See torrent_status::state_t.
need_save_resume
true if this torrent has unsaved changes to its download state and statistics since the last resume data was saved.
ip_filter_applies
true if the session global IP filter applies to this torrent. This defaults to true.
upload_mode
true if the torrent is blocked from downloading. This typically happens when a disk write operation fails. If the torrent is auto-managed, it will periodically be taken out of this state, in the hope that the disk condition (be it disk full or permission errors) has been resolved. If the torrent is not auto-managed, you have to explicitly take it out of the upload mode by calling set_upload_mode() on the torrent_handle.
share_mode
true if the torrent is currently in share-mode, i.e. not downloading the torrent, but just helping the swarm out.
super_seeding
true if the torrent is in super seeding mode
paused
set to true if the torrent is paused and false otherwise. It's only true if the torrent itself is paused. If the torrent is not running because the session is paused, this is still false. To know if a torrent is active or not, you need to inspect both torrent_status::paused and session::is_paused().
auto_managed
set to true if the torrent is auto managed, i.e. libtorrent is responsible for determining whether it should be started or queued. For more info see queuing
sequential_download
true when the torrent is in sequential download mode. In this mode pieces are downloaded in order rather than rarest first.
is_seeding
true if all pieces have been downloaded.
is_finished
true if all pieces that have a priority > 0 are downloaded. There is only a distinction between finished and seeding if some pieces or files have been set to priority 0, i.e. are not downloaded.
has_metadata
true if this torrent has metadata (either it was started from a .torrent file or the metadata has been downloaded). The only scenario where this can be false is when the torrent was started torrent-less (i.e. with just an info-hash and tracker ip, a magnet link for instance).
has_incoming
true if there has ever been an incoming connection attempt to this torrent.
seed_mode
true if the torrent is in seed_mode. If the torrent was started in seed mode, it will leave seed mode once all pieces have been checked or as soon as one piece fails the hash check.
moving_storage
this is true if this torrent's storage is currently being moved from one location to another. This may potentially be a long operation if a large file ends up being copied from one drive to another.
is_loaded
true if this torrent is loaded into RAM. A torrent can be started and still not loaded into RAM, in case it has not had any peers interested in it yet. Torrents are loaded on demand.
announcing_to_trackers announcing_to_lsd announcing_to_dht
these are set to true if this torrent is allowed to announce to the respective peer source. Whether they are true or false is determined by the queue logic/auto manager. Torrents that are not auto managed will always be allowed to announce to all peer sources.
stop_when_ready
this reflects whether the stop_when_ready flag is currently enabled on this torrent. For more information, see torrent_handle::stop_when_ready().
info_hash
the info-hash for this torrent

announce_entry

Declared in "libtorrent/announce_entry.hpp"

this class holds information about one bittorrent tracker, as it relates to a specific torrent.

struct announce_entry
{
   announce_entry (std::string const& u);
   announce_entry (announce_entry const&) = default;
   ~announce_entry ();
   announce_entry& operator= (announce_entry const&) = default;
   announce_entry ();
   int next_announce_in () const;
   int min_announce_in () const;
   void reset ();
   void failed (aux::session_settings const& sett, int retry_interval = 0);
   bool can_announce (time_point now, bool is_seed) const;
   bool is_working () const;
   void trim ();

   enum tracker_source
   {
      source_torrent,
      source_client,
      source_magnet_link,
      source_tex,
   };

   std::string url;
   std::string trackerid;
   std::string message;
   error_code last_error;
   time_point next_announce;
   time_point min_announce;
   int scrape_incomplete;
   int scrape_complete;
   int scrape_downloaded;
   boost::uint8_t tier;
   boost::uint8_t fail_limit;
   boost::uint8_t fails:7;
   bool updating:1;
   boost::uint8_t source:4;
   bool verified:1;
   bool start_sent:1;
   bool complete_sent:1;
   bool send_stats:1;
};

announce_entry() ~announce_entry() operator=()

announce_entry (std::string const& u);
announce_entry (announce_entry const&) = default;
~announce_entry ();
announce_entry& operator= (announce_entry const&) = default;
announce_entry ();

constructs a tracker announce entry with u as the URL.

min_announce_in() next_announce_in()

int next_announce_in () const;
int min_announce_in () const;

returns the number of seconds to the next announce on this tracker. min_announce_in() returns the number of seconds until we are allowed to force another tracker update with this tracker.

If the last time this tracker was contacted failed, last_error is the error code describing what error occurred.

reset()

void reset ();

reset announce counters and clears the started sent flag. The announce_entry will look like we've never talked to the tracker.

failed()

void failed (aux::session_settings const& sett, int retry_interval = 0);

updates the failure counter and time-outs for re-trying. This is called when the tracker announce fails.

can_announce()

bool can_announce (time_point now, bool is_seed) const;

returns true if we can announce to this tracker now. The current time is passed in as now. The is_seed argument is necessary because once we become a seed, we need to announce right away, even if the re-announce timer hasn't expired yet.

is_working()

bool is_working () const;

returns true if the last time we tried to announce to this tracker succeeded, or if we haven't tried yet.

trim()

void trim ();

trims whitespace characters from the beginning of the URL.

enum tracker_source

Declared in "libtorrent/announce_entry.hpp"

name value description
source_torrent 1 the tracker was part of the .torrent file
source_client 2 the tracker was added programatically via the add_troacker()_ function
source_magnet_link 4 the tracker was part of a magnet link
source_tex 8 the tracker was received from the swarm via tracker exchange
url
tracker URL as it appeared in the torrent file
trackerid
the current &trackerid= argument passed to the tracker. this is optional and is normally empty (in which case no trackerid is sent).
message
if this tracker has returned an error or warning message that message is stored here
last_error
if this tracker failed the last time it was contacted this error code specifies what error occurred
next_announce
the time of next tracker announce
min_announce
no announces before this time
scrape_incomplete scrape_complete scrape_downloaded
if this tracker has returned scrape data, these fields are filled in with valid numbers. Otherwise they are set to -1. the number of current downloaders
tier
the tier this tracker belongs to
fail_limit
the max number of failures to announce to this tracker in a row, before this tracker is not used anymore. 0 means unlimited
fails
the number of times in a row we have failed to announce to this tracker.
updating
true while we're waiting for a response from the tracker.
source
a bitmask specifying which sources we got this tracker from.
verified
set to true the first time we receive a valid response from this tracker.
start_sent
set to true when we get a valid response from an announce with event=started. If it is set, we won't send start in the subsequent announces.
complete_sent
set to true when we send a event=completed.
send_stats
this is false the stats sent to this tracker will be 0

peer_class_type_filter

Declared in "libtorrent/peer_class_type_filter.hpp"

peer_class_type_filter is a simple container for rules for adding and subtracting peer-classes from peers. It is applied after the peer class filter is applied (which is based on the peer's IP address).

struct peer_class_type_filter
{
   peer_class_type_filter ();
   void remove (socket_type_t st, peer_class_t peer_class);
   void add (socket_type_t st, peer_class_t peer_class);
   void disallow (socket_type_t st, peer_class_t peer_class);
   void allow (socket_type_t st, peer_class_t peer_class);
   boost::uint32_t apply (int st, boost::uint32_t peer_class_mask);

   enum socket_type_t
   {
      tcp_socket,
      utp_socket,
      ssl_tcp_socket,
      ssl_utp_socket,
      i2p_socket,
      num_socket_types,
   };
};

remove() add()

void remove (socket_type_t st, peer_class_t peer_class);
void add (socket_type_t st, peer_class_t peer_class);

add() and remove() adds and removes a peer class to be added to new peers based on socket type.

disallow() allow()

void disallow (socket_type_t st, peer_class_t peer_class);
void allow (socket_type_t st, peer_class_t peer_class);

disallow() and allow() adds and removes a peer class to be removed from new peers based on socket type.

The peer_class argument cannot be greater than 31. The bitmasks representing peer classes in the peer_class_type_filter are 32 bits.

apply()

boost::uint32_t apply (int st, boost::uint32_t peer_class_mask);

takes a bitmask of peer classes and returns a new bitmask of peer classes after the rules have been applied, based on the socket type argument (st).

enum socket_type_t

Declared in "libtorrent/peer_class_type_filter.hpp"

name value description
tcp_socket 0 these match the socket types from socket_type.hpp shifted one down
utp_socket 1  
ssl_tcp_socket 2  
ssl_utp_socket 3  
i2p_socket 4  
num_socket_types 5  

block_info

Declared in "libtorrent/torrent_handle.hpp"

holds the state of a block in a piece. Who we requested it from and how far along we are at downloading it.

struct block_info
{
   void set_peer (tcp::endpoint const& ep);
   tcp::endpoint peer () const;

   enum block_state_t
   {
      none,
      requested,
      writing,
      finished,
   };

   unsigned bytes_progress:15;
   unsigned block_size:15;
   unsigned state:2;
   unsigned num_peers:14;
};

set_peer() peer()

void set_peer (tcp::endpoint const& ep);
tcp::endpoint peer () const;

The peer is the ip address of the peer this block was downloaded from.

enum block_state_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
none 0 This block has not been downloaded or requested form any peer.
requested 1 The block has been requested, but not completely downloaded yet.
writing 2 The block has been downloaded and is currently queued for being written to disk.
finished 3 The block has been written to disk.
bytes_progress
the number of bytes that have been received for this block
block_size
the total number of bytes in this block.
state
the state this block is in (see block_state_t)
num_peers
the number of peers that is currently requesting this block. Typically this is 0 or 1, but at the end of the torrent blocks may be requested by more peers in parallel to speed things up.

partial_piece_info

Declared in "libtorrent/torrent_handle.hpp"

This class holds information about pieces that have outstanding requests or outstanding writes

struct partial_piece_info
{
   int piece_index;
   int blocks_in_piece;
   int finished;
   int writing;
   int requested;
   block_info* blocks;
};
piece_index
the index of the piece in question. blocks_in_piece is the number of blocks in this particular piece. This number will be the same for most pieces, but the last piece may have fewer blocks than the standard pieces.
blocks_in_piece
the number of blocks in this piece
finished
the number of blocks that are in the finished state
writing
the number of blocks that are in the writing state
requested
the number of blocks that are in the requested state
blocks

this is an array of blocks_in_piece number of items. One for each block in the piece.

Warning

This is a pointer that points to an array that's owned by the session object. The next time get_download_queue() is called, it will be invalidated.

torrent_handle

Declared in "libtorrent/torrent_handle.hpp"

You will usually have to store your torrent handles somewhere, since it's the object through which you retrieve information about the torrent and aborts the torrent.

Warning

Any member function that returns a value or fills in a value has to be made synchronously. This means it has to wait for the main thread to complete the query before it can return. This might potentially be expensive if done from within a GUI thread that needs to stay responsive. Try to avoid querying for information you don't need, and try to do it in as few calls as possible. You can get most of the interesting information about a torrent from the torrent_handle::status() call.

The default constructor will initialize the handle to an invalid state. Which means you cannot perform any operation on it, unless you first assign it a valid handle. If you try to perform any operation on an uninitialized handle, it will throw invalid_handle.

Warning

All operations on a torrent_handle may throw libtorrent_exception exception, in case the handle is no longer referring to a torrent. There is one exception is_valid() will never throw. Since the torrents are processed by a background thread, there is no guarantee that a handle will remain valid between two calls.

struct torrent_handle
{
   torrent_handle ();
   torrent_handle (torrent_handle const& t);
   torrent_handle& operator= (torrent_handle const&) = default;
   void add_piece (int piece, char const* data, int flags = 0) const;
   void read_piece (int piece) const;
   bool have_piece (int piece) const;
   void get_peer_info (std::vector<peer_info>& v) const;
   torrent_status status (boost::uint32_t flags = 0xffffffff) const;
   void get_download_queue (std::vector<partial_piece_info>& queue) const;
   void reset_piece_deadline (int index) const;
   void clear_piece_deadlines () const;
   void set_piece_deadline (int index, int deadline, int flags = 0) const;
   void set_priority (int prio) const;
   void file_progress (std::vector<boost::int64_t>& progress, int flags = 0) const;
   void file_status (std::vector<pool_file_status>& status) const;
   void clear_error () const;
   std::vector<announce_entry> trackers () const;
   void replace_trackers (std::vector<announce_entry> const&) const;
   void add_tracker (announce_entry const&) const;
   void add_url_seed (std::string const& url) const;
   void remove_url_seed (std::string const& url) const;
   std::set<std::string> url_seeds () const;
   void add_http_seed (std::string const& url) const;
   void remove_http_seed (std::string const& url) const;
   std::set<std::string> http_seeds () const;
   void add_extension (
      boost::function<boost::shared_ptr<torrent_plugin>(torrent_handle const&, void*)> const& ext
      , void* userdata = 0);
   bool set_metadata (char const* metadata, int size) const;
   bool is_valid () const;
   void pause (int flags = 0) const;
   void resume () const;
   void stop_when_ready (bool b) const;
   void set_upload_mode (bool b) const;
   void set_share_mode (bool b) const;
   void flush_cache () const;
   void apply_ip_filter (bool b) const;
   void force_recheck () const;
   void save_resume_data (int flags = 0) const;
   bool need_save_resume_data () const;
   void auto_managed (bool m) const;
   void queue_position_down () const;
   void queue_position_top () const;
   int queue_position () const;
   void queue_position_bottom () const;
   void queue_position_up () const;
   void queue_position_set (int p) const;
   void set_ssl_certificate (std::string const& certificate
      , std::string const& private_key
      , std::string const& dh_params
      , std::string const& passphrase = "");
   void set_ssl_certificate_buffer (std::string const& certificate
      , std::string const& private_key
      , std::string const& dh_params);
   storage_interface* get_storage_impl () const;
   shared_ptr<const torrent_info> torrent_file () const;
   void piece_availability (std::vector<int>& avail) const;
   int piece_priority (int index) const;
   std::vector<int> piece_priorities () const;
   void piece_priority (int index, int priority) const;
   void prioritize_pieces (std::vector<int> const& pieces) const;
   void prioritize_pieces (std::vector<std::pair<int, int> > const& pieces) const;
   int file_priority (int index) const;
   void prioritize_files (std::vector<int> const& files) const;
   void file_priority (int index, int priority) const;
   std::vector<int> file_priorities () const;
   void force_reannounce (int seconds = 0, int tracker_index = -1) const;
   void force_reannounce (int seconds, int tracker_index, int flags) const;
   void force_dht_announce () const;
   void scrape_tracker (int idx = -1) const;
   int upload_limit () const;
   int download_limit () const;
   void set_upload_limit (int limit) const;
   void set_download_limit (int limit) const;
   void set_pinned (bool p) const;
   void set_sequential_download (bool sd) const;
   void connect_peer (tcp::endpoint const& adr, int source = 0
      , int flags = 0x1 + 0x4 + 0x8) const;
   int max_uploads () const;
   void set_max_uploads (int max_uploads) const;
   int max_connections () const;
   void set_max_connections (int max_connections) const;
   void move_storage (std::string const& save_path, int flags = 0) const;
   void rename_file (int index, std::string const& new_name) const;
   void super_seeding (bool on) const;
   sha1_hash info_hash () const;
   bool operator!= (const torrent_handle& h) const;
   bool operator< (const torrent_handle& h) const;
   bool operator== (const torrent_handle& h) const;
   boost::uint32_t id () const;
   boost::shared_ptr<torrent> native_handle () const;

   enum flags_t
   {
      overwrite_existing,
   };

   enum status_flags_t
   {
      query_distributed_copies,
      query_accurate_download_counters,
      query_last_seen_complete,
      query_pieces,
      query_verified_pieces,
      query_torrent_file,
      query_name,
      query_save_path,
   };

   enum deadline_flags
   {
      alert_when_available,
   };

   enum file_progress_flags_t
   {
      piece_granularity,
   };

   enum pause_flags_t
   {
      graceful_pause,
   };

   enum save_resume_flags_t
   {
      flush_disk_cache,
      save_info_dict,
      only_if_modified,
   };

   enum reannounce_flags_t
   {
      ignore_min_interval,
   };
};

torrent_handle()

torrent_handle ();

constructs a torrent handle that does not refer to a torrent. i.e. is_valid() will return false.

add_piece()

void add_piece (int piece, char const* data, int flags = 0) const;

This function will write data to the storage as piece piece, as if it had been downloaded from a peer. data is expected to point to a buffer of as many bytes as the size of the specified piece. The data in the buffer is copied and passed on to the disk IO thread to be written at a later point.

By default, data that's already been downloaded is not overwritten by this buffer. If you trust this data to be correct (and pass the piece hash check) you may pass the overwrite_existing flag. This will instruct libtorrent to overwrite any data that may already have been downloaded with this data.

Since the data is written asynchronously, you may know that is passed or failed the hash check by waiting for piece_finished_alert or hash_failed_alert.

read_piece()

void read_piece (int piece) const;

This function starts an asynchronous read operation of the specified piece from this torrent. You must have completed the download of the specified piece before calling this function.

When the read operation is completed, it is passed back through an alert, read_piece_alert. Since this alert is a response to an explicit call, it will always be posted, regardless of the alert mask.

Note that if you read multiple pieces, the read operations are not guaranteed to finish in the same order as you initiated them.

have_piece()

bool have_piece (int piece) const;

Returns true if this piece has been completely downloaded, and false otherwise.

get_peer_info()

void get_peer_info (std::vector<peer_info>& v) const;

takes a reference to a vector that will be cleared and filled with one entry for each peer connected to this torrent, given the handle is valid. If the torrent_handle is invalid, it will throw libtorrent_exception exception. Each entry in the vector contains information about that particular peer. See peer_info.

status()

torrent_status status (boost::uint32_t flags = 0xffffffff) const;

status() will return a structure with information about the status of this torrent. If the torrent_handle is invalid, it will throw libtorrent_exception exception. See torrent_status. The flags argument filters what information is returned in the torrent_status. Some information in there is relatively expensive to calculate, and if you're not interested in it (and see performance issues), you can filter them out.

By default everything is included. The flags you can use to decide what to include are defined in the status_flags_t enum.

get_download_queue()

void get_download_queue (std::vector<partial_piece_info>& queue) const;

get_download_queue() takes a non-const reference to a vector which it will fill with information about pieces that are partially downloaded or not downloaded at all but partially requested. See partial_piece_info for the fields in the returned vector.

clear_piece_deadlines() reset_piece_deadline() set_piece_deadline()

void reset_piece_deadline (int index) const;
void clear_piece_deadlines () const;
void set_piece_deadline (int index, int deadline, int flags = 0) const;

This function sets or resets the deadline associated with a specific piece index (index). libtorrent will attempt to download this entire piece before the deadline expires. This is not necessarily possible, but pieces with a more recent deadline will always be prioritized over pieces with a deadline further ahead in time. The deadline (and flags) of a piece can be changed by calling this function again.

The flags parameter can be used to ask libtorrent to post an alert once the piece has been downloaded, by passing alert_when_available. When set, the read_piece_alert alert will be delivered, with the piece data, when it's downloaded.

If the piece is already downloaded when this call is made, nothing happens, unless the alert_when_available flag is set, in which case it will have the same effect as calling read_piece() for index.

deadline is the number of milliseconds until this piece should be completed.

reset_piece_deadline removes the deadline from the piece. If it hasn't already been downloaded, it will no longer be considered a priority.

clear_piece_deadlines() removes deadlines on all pieces in the torrent. As if reset_piece_deadline() was called on all pieces.

set_priority()

void set_priority (int prio) const;

This sets the bandwidth priority of this torrent. The priority of a torrent determines how much bandwidth its peers are assigned when distributing upload and download rate quotas. A high number gives more bandwidth. The priority must be within the range [0, 255].

The default priority is 0, which is the lowest priority.

To query the priority of a torrent, use the torrent_handle::status() call.

Torrents with higher priority will not necessarily get as much bandwidth as they can consume, even if there's is more quota. Other peers will still be weighed in when bandwidth is being distributed. With other words, bandwidth is not distributed strictly in order of priority, but the priority is used as a weight.

Peers whose Torrent has a higher priority will take precedence when distributing unchoke slots. This is a strict prioritisation where every interested peer on a high priority torrent will be unchoked before any other, lower priority, torrents have any peers unchoked.

file_progress()

void file_progress (std::vector<boost::int64_t>& progress, int flags = 0) const;

This function fills in the supplied vector with the the number of bytes downloaded of each file in this torrent. The progress values are ordered the same as the files in the torrent_info. This operation is not very cheap. Its complexity is O(n + mj). Where n is the number of files, m is the number of downloading pieces and j is the number of blocks in a piece.

The flags parameter can be used to specify the granularity of the file progress. If left at the default value of 0, the progress will be as accurate as possible, but also more expensive to calculate. If torrent_handle::piece_granularity is specified, the progress will be specified in piece granularity. i.e. only pieces that have been fully downloaded and passed the hash check count. When specifying piece granularity, the operation is a lot cheaper, since libtorrent already keeps track of this internally and no calculation is required.

file_status()

void file_status (std::vector<pool_file_status>& status) const;

This function fills in the passed in vector with status about files that are open for this torrent. Any file that is not open in this torrent, will not be reported in the vector, i.e. it's possible that the vector is empty when returning, if none of the files in the torrent are currently open.

see pool_file_status.

clear_error()

void clear_error () const;

If the torrent is in an error state (i.e. torrent_status::error is non-empty), this will clear the error and start the torrent again.

add_tracker() replace_trackers() trackers()

std::vector<announce_entry> trackers () const;
void replace_trackers (std::vector<announce_entry> const&) const;
void add_tracker (announce_entry const&) const;

trackers() will return the list of trackers for this torrent. The announce entry contains both a string url which specify the announce url for the tracker as well as an int tier, which is specifies the order in which this tracker is tried. If you want libtorrent to use another list of trackers for this torrent, you can use replace_trackers() which takes a list of the same form as the one returned from trackers() and will replace it. If you want an immediate effect, you have to call force_reannounce(). See announce_entry.

add_tracker() will look if the specified tracker is already in the set. If it is, it doesn't do anything. If it's not in the current set of trackers, it will insert it in the tier specified in the announce_entry.

The updated set of trackers will be saved in the resume data, and when a torrent is started with resume data, the trackers from the resume data will replace the original ones.

url_seeds() add_url_seed() remove_url_seed()

void add_url_seed (std::string const& url) const;
void remove_url_seed (std::string const& url) const;
std::set<std::string> url_seeds () const;

add_url_seed() adds another url to the torrent's list of url seeds. If the given url already exists in that list, the call has no effect. The torrent will connect to the server and try to download pieces from it, unless it's paused, queued, checking or seeding. remove_url_seed() removes the given url if it exists already. url_seeds() return a set of the url seeds currently in this torrent. Note that URLs that fails may be removed automatically from the list.

See http seeding for more information.

http_seeds() remove_http_seed() add_http_seed()

void add_http_seed (std::string const& url) const;
void remove_http_seed (std::string const& url) const;
std::set<std::string> http_seeds () const;

These functions are identical as the *_url_seed() variants, but they operate on BEP 17 web seeds instead of BEP 19.

See http seeding for more information.

add_extension()

void add_extension (
      boost::function<boost::shared_ptr<torrent_plugin>(torrent_handle const&, void*)> const& ext
      , void* userdata = 0);

add the specified extension to this torrent. The ext argument is a function that will be called from within libtorrent's context passing in the internal torrent object and the specified userdata pointer. The function is expected to return a shared pointer to a torrent_plugin instance.

set_metadata()

bool set_metadata (char const* metadata, int size) const;

set_metadata expects the info section of metadata. i.e. The buffer passed in will be hashed and verified against the info-hash. If it fails, a metadata_failed_alert will be generated. If it passes, a metadata_received_alert is generated. The function returns true if the metadata is successfully set on the torrent, and false otherwise. If the torrent already has metadata, this function will not affect the torrent, and false will be returned.

is_valid()

bool is_valid () const;

Returns true if this handle refers to a valid torrent and false if it hasn't been initialized or if the torrent it refers to has been aborted. Note that a handle may become invalid after it has been added to the session. Usually this is because the storage for the torrent is somehow invalid or if the filenames are not allowed (and hence cannot be opened/created) on your filesystem. If such an error occurs, a file_error_alert is generated and all handles that refers to that torrent will become invalid.

pause() resume()

void pause (int flags = 0) const;
void resume () const;

pause(), and resume() will disconnect all peers and reconnect all peers respectively. When a torrent is paused, it will however remember all share ratios to all peers and remember all potential (not connected) peers. Torrents may be paused automatically if there is a file error (e.g. disk full) or something similar. See file_error_alert.

To know if a torrent is paused or not, call torrent_handle::status() and inspect torrent_status::paused.

The flags argument to pause can be set to torrent_handle::graceful_pause which will delay the disconnect of peers that we're still downloading outstanding requests from. The torrent will not accept any more requests and will disconnect all idle peers. As soon as a peer is done transferring the blocks that were requested from it, it is disconnected. This is a graceful shut down of the torrent in the sense that no downloaded bytes are wasted.

Note

Torrents that are auto-managed may be automatically resumed again. It does not make sense to pause an auto-managed torrent without making it not auto-managed first. Torrents are auto-managed by default when added to the session. For more information, see queuing.

stop_when_ready()

void stop_when_ready (bool b) const;

set or clear the stop-when-ready flag. When this flag is set, the torrent will force stop whenever it transitions from a non-data-transferring state into a data-transferring state (referred to as being ready to download or seed). This is useful for torrents that should not start downloading or seeding yet, but want to be made ready to do so. A torrent may need to have its files checked for instance, so it needs to be started and possibly queued for checking (auto-managed and started) but as soon as it's done, it should be stopped.

Force stopped means auto-managed is set to false and it's paused. As if auto_manage(false) and pause() were called on the torrent.

Note that the torrent may transition into a downloading state while calling this function, and since the logic is edge triggered you may miss the edge. To avoid this race, if the torrent already is in a downloading state when this call is made, it will trigger the stop-when-ready immediately.

When the stop-when-ready logic fires, the flag is cleared. Any subsequent transitions between downloading and non-downloading states will not be affected, until this function is used to set it again.

The behavior is more robust when setting this flag as part of adding the torrent. See add_torrent_params.

The stop-when-ready flag fixes the inherent race condition of waiting for the state_changed_alert and then call pause(). The download/seeding will most likely start in between posting the alert and receiving the call to pause.

A downloading state is one where peers are being connected. Which means just downloading the metadata via the ut_metadata extension counts as a downloading state. In order to stop a torrent once the metadata has been downloaded, instead set all file priorities to dont_download

set_upload_mode()

void set_upload_mode (bool b) const;

Explicitly sets the upload mode of the torrent. In upload mode, the torrent will not request any pieces. If the torrent is auto managed, it will automatically be taken out of upload mode periodically (see settings_pack::optimistic_disk_retry). Torrents are automatically put in upload mode whenever they encounter a disk write error.

m should be true to enter upload mode, and false to leave it.

To test if a torrent is in upload mode, call torrent_handle::status() and inspect torrent_status::upload_mode.

set_share_mode()

void set_share_mode (bool b) const;

Enable or disable share mode for this torrent. When in share mode, the torrent will not necessarily be downloaded, especially not the whole of it. Only parts that are likely to be distributed to more than 2 other peers are downloaded, and only if the previous prediction was correct.

flush_cache()

void flush_cache () const;

Instructs libtorrent to flush all the disk caches for this torrent and close all file handles. This is done asynchronously and you will be notified that it's complete through cache_flushed_alert.

Note that by the time you get the alert, libtorrent may have cached more data for the torrent, but you are guaranteed that whatever cached data libtorrent had by the time you called torrent_handle::flush_cache() has been written to disk.

apply_ip_filter()

void apply_ip_filter (bool b) const;

Set to true to apply the session global IP filter to this torrent (which is the default). Set to false to make this torrent ignore the IP filter.

force_recheck()

void force_recheck () const;

force_recheck puts the torrent back in a state where it assumes to have no resume data. All peers will be disconnected and the torrent will stop announcing to the tracker. The torrent will be added to the checking queue, and will be checked (all the files will be read and compared to the piece hashes). Once the check is complete, the torrent will start connecting to peers again, as normal.

save_resume_data()

void save_resume_data (int flags = 0) const;

save_resume_data() asks libtorrent to generate fast-resume data for this torrent.

The flags argument is a bitmask of flags ORed together. see save_resume_flags_t

This operation is asynchronous, save_resume_data will return immediately. The resume data is delivered when it's done through an save_resume_data_alert.

The fast resume data will be empty in the following cases:

  1. The torrent handle is invalid.
  2. The torrent hasn't received valid metadata and was started without metadata (see libtorrent's metadata from peers extension)

Note that by the time you receive the fast resume data, it may already be invalid if the torrent is still downloading! The recommended practice is to first pause the session, then generate the fast resume data, and then close it down. Make sure to not remove_torrent() before you receive the save_resume_data_alert though. There's no need to pause when saving intermittent resume data.

Warning

If you pause every torrent individually instead of pausing the session, every torrent will have its paused state saved in the resume data!

Warning

The resume data contains the modification timestamps for all files. If one file has been modified when the torrent is added again, the will be rechecked. When shutting down, make sure to flush the disk cache before saving the resume data. This will make sure that the file timestamps are up to date and won't be modified after saving the resume data. The recommended way to do this is to pause the torrent, which will flush the cache and disconnect all peers.

Note

It is typically a good idea to save resume data whenever a torrent is completed or paused. In those cases you don't need to pause the torrent or the session, since the torrent will do no more writing to its files. If you save resume data for torrents when they are paused, you can accelerate the shutdown process by not saving resume data again for paused torrents. Completed torrents should have their resume data saved when they complete and on exit, since their statistics might be updated.

In full allocation mode the resume data is never invalidated by subsequent writes to the files, since pieces won't move around. This means that you don't need to pause before writing resume data in full or sparse mode. If you don't, however, any data written to disk after you saved resume data and before the session closed is lost.

It also means that if the resume data is out dated, libtorrent will not re-check the files, but assume that it is fairly recent. The assumption is that it's better to loose a little bit than to re-check the entire file.

It is still a good idea to save resume data periodically during download as well as when closing down.

Example code to pause and save resume data for all torrents and wait for the alerts:

extern int outstanding_resume_data; // global counter of outstanding resume data
std::vector<torrent_handle> handles = ses.get_torrents();
ses.pause();
for (torrent_handle const& h : handles)
{
        if (!h.is_valid()) continue;
        torrent_status s = h.status();
        if (!s.has_metadata) continue;
        if (!s.need_save_resume_data()) continue;

        h.save_resume_data();
        ++outstanding_resume_data;
}

while (outstanding_resume_data > 0)
{
        alert const* a = ses.wait_for_alert(seconds(10));

        // if we don't get an alert within 10 seconds, abort
        if (a == nullptr) break;

        std::vector<alert*> alerts;
        ses.pop_alerts(&alerts);

        for (alert* i : alerts)
        {
                if (alert_cast<save_resume_data_failed_alert>(a))
                {
                        process_alert(a);
                        --outstanding_resume_data;
                        continue;
                }

                save_resume_data_alert const* rd = alert_cast<save_resume_data_alert>(a);
                if (rd == nullptr)
                {
                        process_alert(a);
                        continue;
                }

                torrent_handle h = rd->handle;
                torrent_status st = h.status(torrent_handle::query_save_path
                        | torrent_handle::query_name);
                std::ofstream out((st.save_path
                        + "/" + st.name + ".fastresume").c_str()
                        , std::ios_base::binary);
                out.unsetf(std::ios_base::skipws);
                bencode(std::ostream_iterator<char>(out), *rd->resume_data);
                --outstanding_resume_data;
        }
}

Note

Note how outstanding_resume_data is a global counter in this example. This is deliberate, otherwise there is a race condition for torrents that was just asked to save their resume data, they posted the alert, but it has not been received yet. Those torrents would report that they don't need to save resume data again, and skipped by the initial loop, and thwart the counter otherwise.

need_save_resume_data()

bool need_save_resume_data () const;

This function returns true if any whole chunk has been downloaded since the torrent was first loaded or since the last time the resume data was saved. When saving resume data periodically, it makes sense to skip any torrent which hasn't downloaded anything since the last time.

Note

A torrent's resume data is considered saved as soon as the save_resume_data_alert is posted. It is important to make sure this alert is received and handled in order for this function to be meaningful.

auto_managed()

void auto_managed (bool m) const;

changes whether the torrent is auto managed or not. For more info, see queuing.

queue_position() queue_position_up() queue_position_bottom() queue_position_down() queue_position_top()

void queue_position_down () const;
void queue_position_top () const;
int queue_position () const;
void queue_position_bottom () const;
void queue_position_up () const;

Every torrent that is added is assigned a queue position exactly one greater than the greatest queue position of all existing torrents. Torrents that are being seeded have -1 as their queue position, since they're no longer in line to be downloaded.

When a torrent is removed or turns into a seed, all torrents with greater queue positions have their positions decreased to fill in the space in the sequence.

queue_position() returns the torrent's position in the download queue. The torrents with the smallest numbers are the ones that are being downloaded. The smaller number, the closer the torrent is to the front of the line to be started.

The queue position is also available in the torrent_status.

The queue_position_*() functions adjust the torrents position in the queue. Up means closer to the front and down means closer to the back of the queue. Top and bottom refers to the front and the back of the queue respectively.

queue_position_set()

void queue_position_set (int p) const;

updates the position in the queue for this torrent. The relative order of all other torrents remain intact but their numerical queue position shifts to make space for this torrent's new position

set_ssl_certificate_buffer() set_ssl_certificate()

void set_ssl_certificate (std::string const& certificate
      , std::string const& private_key
      , std::string const& dh_params
      , std::string const& passphrase = "");
void set_ssl_certificate_buffer (std::string const& certificate
      , std::string const& private_key
      , std::string const& dh_params);

For SSL torrents, use this to specify a path to a .pem file to use as this client's certificate. The certificate must be signed by the certificate in the .torrent file to be valid.

The set_ssl_certificate_buffer() overload takes the actual certificate, private key and DH params as strings, rather than paths to files. This overload is only available when libtorrent is built against boost 1.54 or later.

cert is a path to the (signed) certificate in .pem format corresponding to this torrent.

private_key is a path to the private key for the specified certificate. This must be in .pem format.

dh_params is a path to the Diffie-Hellman parameter file, which needs to be in .pem format. You can generate this file using the openssl command like this: openssl dhparam -outform PEM -out dhparams.pem 512.

passphrase may be specified if the private key is encrypted and requires a passphrase to be decrypted.

Note that when a torrent first starts up, and it needs a certificate, it will suspend connecting to any peers until it has one. It's typically desirable to resume the torrent after setting the SSL certificate.

If you receive a torrent_need_cert_alert, you need to call this to provide a valid cert. If you don't have a cert you won't be allowed to connect to any peers.

get_storage_impl()

storage_interface* get_storage_impl () const;

Returns the storage implementation for this torrent. This depends on the storage constructor function that was passed to add_torrent.

torrent_file()

shared_ptr<const torrent_info> torrent_file () const;

Returns a pointer to the torrent_info object associated with this torrent. The torrent_info object may be a copy of the internal object. If the torrent doesn't have metadata, the pointer will not be initialized (i.e. a NULL pointer). The torrent may be in a state without metadata only if it was started without a .torrent file, e.g. by using the libtorrent extension of just supplying a tracker and info-hash.

piece_availability()

void piece_availability (std::vector<int>& avail) const;

Fills the specified std::vector<int> with the availability for each piece in this torrent. libtorrent does not keep track of availability for seeds, so if the torrent is seeding the availability for all pieces is reported as 0.

The piece availability is the number of peers that we are connected that has advertised having a particular piece. This is the information that libtorrent uses in order to prefer picking rare pieces.

piece_priority() prioritize_pieces() piece_priorities()

int piece_priority (int index) const;
std::vector<int> piece_priorities () const;
void piece_priority (int index, int priority) const;
void prioritize_pieces (std::vector<int> const& pieces) const;
void prioritize_pieces (std::vector<std::pair<int, int> > const& pieces) const;

These functions are used to set and get the priority of individual pieces. By default all pieces have priority 4. That means that the random rarest first algorithm is effectively active for all pieces. You may however change the priority of individual pieces. There are 8 priority levels. 0 means not to download the piece at all. Otherwise, lower priority values means less likely to be picked. Piece priority takes precedence over piece availability. Every piece with priority 7 will be attempted to be picked before a priority 6 piece and so on.

The default priority of pieces is 4.

Piece priorities can not be changed for torrents that have not downloaded the metadata yet. Magnet links won't have metadata immediately. see the metadata_received_alert.

piece_priority sets or gets the priority for an individual piece, specified by index.

prioritize_pieces takes a vector of integers, one integer per piece in the torrent. All the piece priorities will be updated with the priorities in the vector. The second overload of prioritize_pieces that takes a vector of pairs will update the priorities of only select pieces, and leave all other unaffected. Each pair is (piece, priority). That is, the first item is the piece index and the second item is the priority of that piece. Invalid entries, where the piece index or priority is out of range, are not allowed.

piece_priorities returns a vector with one element for each piece in the torrent. Each element is the current priority of that piece.

It's possible to cancel the effect of file priorities by setting the priorities for the affected pieces. Care has to be taken when mixing usage of file- and piece priorities.

file_priorities() prioritize_files() file_priority()

int file_priority (int index) const;
void prioritize_files (std::vector<int> const& files) const;
void file_priority (int index, int priority) const;
std::vector<int> file_priorities () const;

index must be in the range [0, number_of_files).

file_priority() queries or sets the priority of file index.

prioritize_files() takes a vector that has at as many elements as there are files in the torrent. Each entry is the priority of that file. The function sets the priorities of all the pieces in the torrent based on the vector.

file_priorities() returns a vector with the priorities of all files.

The priority values are the same as for piece_priority().

Whenever a file priority is changed, all other piece priorities are reset to match the file priorities. In order to maintain special priorities for particular pieces, piece_priority() has to be called again for those pieces.

You cannot set the file priorities on a torrent that does not yet have metadata or a torrent that is a seed. file_priority(int, int) and prioritize_files() are both no-ops for such torrents.

Since changing file priorities may involve disk operations (of moving files in- and out of the part file), the internal accounting of file priorities happen asynchronously. i.e. setting file priorities and then immediately querying them may not yield the same priorities just set. However, the piece priorities are updated immediately.

when combining file- and piece priorities, the resume file will record both. When loading the resume data, the file priorities will be applied first, then the piece priorities.

force_reannounce() force_dht_announce()

void force_reannounce (int seconds = 0, int tracker_index = -1) const;
void force_reannounce (int seconds, int tracker_index, int flags) const;
void force_dht_announce () const;

force_reannounce() will force this torrent to do another tracker request, to receive new peers. The seconds argument specifies how many seconds from now to issue the tracker announces.

If the tracker's min_interval has not passed since the last announce, the forced announce will be scheduled to happen immediately as the min_interval expires. This is to honor trackers minimum re-announce interval settings.

The tracker_index argument specifies which tracker to re-announce. If set to -1 (which is the default), all trackers are re-announce.

The flags argument can be used to affect the re-announce. See reannounce_flags_t.

force_dht_announce will announce the torrent to the DHT immediately.

scrape_tracker()

void scrape_tracker (int idx = -1) const;

scrape_tracker() will send a scrape request to a tracker. By default (idx = -1) it will scrape the last working tracker. If idx is >= 0, the tracker with the specified index will scraped.

A scrape request queries the tracker for statistics such as total number of incomplete peers, complete peers, number of downloads etc.

This request will specifically update the num_complete and num_incomplete fields in the torrent_status struct once it completes. When it completes, it will generate a scrape_reply_alert. If it fails, it will generate a scrape_failed_alert.

set_upload_limit() upload_limit() download_limit() set_download_limit()

int upload_limit () const;
int download_limit () const;
void set_upload_limit (int limit) const;
void set_download_limit (int limit) const;

set_upload_limit will limit the upload bandwidth used by this particular torrent to the limit you set. It is given as the number of bytes per second the torrent is allowed to upload. set_download_limit works the same way but for download bandwidth instead of upload bandwidth. Note that setting a higher limit on a torrent then the global limit (settings_pack::upload_rate_limit) will not override the global rate limit. The torrent can never upload more than the global rate limit.

upload_limit and download_limit will return the current limit setting, for upload and download, respectively.

Local peers are not rate limited by default. see peer classes.

set_pinned()

void set_pinned (bool p) const;

A pinned torrent may not be unloaded by libtorrent. When the dynamic loading and unloading of torrents is enabled (by setting a load function on the session), this can be used to exempt certain torrents from the unloading logic.

Magnet links, and other torrents that start out without having metadata are pinned automatically. This is to give the client a chance to get the metadata and save it before it's unloaded. In this case, it may be useful to unpin the torrent once its metadata has been saved to disk.

For more information about dynamically loading and unloading torrents, see dynamic loading of torrent files.

set_sequential_download()

void set_sequential_download (bool sd) const;

set_sequential_download() enables or disables sequential download. When enabled, the piece picker will pick pieces in sequence instead of rarest first. In this mode, piece priorities are ignored, with the exception of priority 7, which are still preferred over the sequential piece order.

Enabling sequential download will affect the piece distribution negatively in the swarm. It should be used sparingly.

connect_peer()

void connect_peer (tcp::endpoint const& adr, int source = 0
      , int flags = 0x1 + 0x4 + 0x8) const;

connect_peer() is a way to manually connect to peers that one believe is a part of the torrent. If the peer does not respond, or is not a member of this torrent, it will simply be disconnected. No harm can be done by using this other than an unnecessary connection attempt is made. If the torrent is uninitialized or in queued or checking mode, this will throw libtorrent_exception. The second (optional) argument will be bitwise ORed into the source mask of this peer. Typically this is one of the source flags in peer_info. i.e. tracker, pex, dht etc.

flags are the same flags that are passed along with the ut_pex extension.

0x01 peer supports encryption.
0x02 peer is a seed
0x04 supports uTP. If this is not set, the peer will only be contacted over TCP.
0x08 supports hole punching protocol. If this flag is received from a peer, it can be used as a rendezvous point in case direct connections to the peer fail

max_uploads() set_max_uploads()

int max_uploads () const;
void set_max_uploads (int max_uploads) const;

set_max_uploads() sets the maximum number of peers that's unchoked at the same time on this torrent. If you set this to -1, there will be no limit. This defaults to infinite. The primary setting controlling this is the global unchoke slots limit, set by unchoke_slots_limit in settings_pack.

max_uploads() returns the current settings.

max_connections() set_max_connections()

int max_connections () const;
void set_max_connections (int max_connections) const;

set_max_connections() sets the maximum number of connection this torrent will open. If all connections are used up, incoming connections may be refused or poor connections may be closed. This must be at least 2. The default is unlimited number of connections. If -1 is given to the function, it means unlimited. There is also a global limit of the number of connections, set by connections_limit in settings_pack.

max_connections() returns the current settings.

move_storage()

void move_storage (std::string const& save_path, int flags = 0) const;

Moves the file(s) that this torrent are currently seeding from or downloading to. If the given save_path is not located on the same drive as the original save path, the files will be copied to the new drive and removed from their original location. This will block all other disk IO, and other torrents download and upload rates may drop while copying the file.

Since disk IO is performed in a separate thread, this operation is also asynchronous. Once the operation completes, the storage_moved_alert is generated, with the new path as the message. If the move fails for some reason, storage_moved_failed_alert is generated instead, containing the error message.

The flags argument determines the behavior of the copying/moving of the files in the torrent. see move_flags_t.

  • always_replace_files = 0
  • fail_if_exist = 1
  • dont_replace = 2

always_replace_files is the default and replaces any file that exist in both the source directory and the target directory.

fail_if_exist first check to see that none of the copy operations would cause an overwrite. If it would, it will fail. Otherwise it will proceed as if it was in always_replace_files mode. Note that there is an inherent race condition here. If the files in the target directory appear after the check but before the copy or move completes, they will be overwritten. When failing because of files already existing in the target path, the error of move_storage_failed_alert is set to boost::system::errc::file_exists.

The intention is that a client may use this as a probe, and if it fails, ask the user which mode to use. The client may then re-issue the move_storage call with one of the other modes.

dont_replace always keeps the existing file in the target directory, if there is one. The source files will still be removed in that case. Note that it won't automatically re-check files. If an incomplete torrent is moved into a directory with the complete files, pause, move, force-recheck and resume. Without the re-checking, the torrent will keep downloading and files in the new download directory will be overwritten.

Files that have been renamed to have absolute paths are not moved by this function. Keep in mind that files that don't belong to the torrent but are stored in the torrent's directory may be moved as well. This goes for files that have been renamed to absolute paths that still end up inside the save path.

rename_file()

void rename_file (int index, std::string const& new_name) const;

Renames the file with the given index asynchronously. The rename operation is complete when either a file_renamed_alert or file_rename_failed_alert is posted.

super_seeding()

void super_seeding (bool on) const;

Enables or disabled super seeding/initial seeding for this torrent. The torrent needs to be a seed for this to take effect.

info_hash()

sha1_hash info_hash () const;

info_hash() returns the info-hash of the torrent. If this handle is to a torrent that hasn't loaded yet (for instance by being added) by a URL, the returned value is undefined.

operator!=() operator<() operator==()

bool operator!= (const torrent_handle& h) const;
bool operator< (const torrent_handle& h) const;
bool operator== (const torrent_handle& h) const;

comparison operators. The order of the torrents is unspecified but stable.

native_handle()

boost::shared_ptr<torrent> native_handle () const;

This function is intended only for use by plugins and the alert dispatch function. This type does not have a stable API and should be relied on as little as possible.

enum flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
overwrite_existing 1  

enum status_flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
query_distributed_copies 1 calculates distributed_copies, distributed_full_copies and distributed_fraction.
query_accurate_download_counters 2 includes partial downloaded blocks in total_done and total_wanted_done.
query_last_seen_complete 4 includes last_seen_complete.
query_pieces 8 includes pieces.
query_verified_pieces 16 includes verified_pieces (only applies to torrents in seed mode).
query_torrent_file 32 includes torrent_file, which is all the static information from the .torrent file.
query_name 64 includes name, the name of the torrent. This is either derived from the .torrent file, or from the &dn= magnet link argument or possibly some other source. If the name of the torrent is not known, this is an empty string.
query_save_path 128 includes save_path, the path to the directory the files of the torrent are saved to.

enum deadline_flags

Declared in "libtorrent/torrent_handle.hpp"

name value description
alert_when_available 1  

enum file_progress_flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
piece_granularity 1 only calculate file progress at piece granularity. This makes the file_progress() call cheaper and also only takes bytes that have passed the hash check into account, so progress cannot regress in this mode.

enum pause_flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
graceful_pause 1  

enum save_resume_flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
flush_disk_cache 1 the disk cache will be flushed before creating the resume data. This avoids a problem with file timestamps in the resume data in case the cache hasn't been flushed yet.
save_info_dict 2 the resume data will contain the metadata from the torrent file as well. This is default for any torrent that's added without a torrent file (such as a magnet link or a URL).
only_if_modified 4 if nothing significant has changed in the torrent since the last time resume data was saved, fail this attempt. Significant changes primarily include more data having been downloaded, file or piece priorities having changed etc. If the resume data doesn't need saving, a save_resume_data_failed_alert is posted with the error resume_data_not_modified.

enum reannounce_flags_t

Declared in "libtorrent/torrent_handle.hpp"

name value description
ignore_min_interval 1 by default, force-reannounce will still honor the min-interval published by the tracker. If this flag is set, it will be ignored and the tracker is announced immediately.

cache_status

Declared in "libtorrent/disk_io_thread.hpp"

this struct holds a number of statistics counters relevant for the disk io thread and disk cache.

struct cache_status
{
   cache_status ();

   std::vector<cached_piece_info> pieces;
};

cache_status()

cache_status ();

initializes all counters to 0

web_seed_entry

Declared in "libtorrent/torrent_info.hpp"

the web_seed_entry holds information about a web seed (also known as URL seed or HTTP seed). It is essentially a URL with some state associated with it. For more information, see BEP 17 and BEP 19.

struct web_seed_entry
{
   web_seed_entry (std::string const& url_, type_t type_
      , std::string const& auth_ = std::string()
      , headers_t const& extra_headers_ = headers_t());
   bool operator== (web_seed_entry const& e) const;
   bool operator< (web_seed_entry const& e) const;

   enum type_t
   {
      url_seed,
      http_seed,
   };

   std::string url;
   std::string auth;
   headers_t extra_headers;
   boost::uint8_t type;
};

operator==()

bool operator== (web_seed_entry const& e) const;

URL and type comparison

operator<()

bool operator< (web_seed_entry const& e) const;

URL and type less-than comparison

enum type_t

Declared in "libtorrent/torrent_info.hpp"

name value description
url_seed 0  
http_seed 1  
url
The URL of the web seed
auth
Optional authentication. If this is set, it's passed in as HTTP basic auth to the web seed. The format is: username:password.
extra_headers
Any extra HTTP headers that need to be passed to the web seed
type
The type of web seed (see type_t)

torrent_info

Declared in "libtorrent/torrent_info.hpp"

TODO: there may be some opportunities to optimize the size if torrent_info. specifically to turn some std::string and std::vector into pointers

class torrent_info
{
   torrent_info (std::string const& filename, int flags = 0);
   torrent_info (std::string const& filename, error_code& ec, int flags = 0);
   torrent_info (char const* buffer, int size, error_code& ec, int flags = 0);
   torrent_info (sha1_hash const& info_hash, int flags = 0);
   torrent_info (bdecode_node const& torrent_file, error_code& ec, int flags = 0);
   torrent_info (char const* buffer, int size, int flags = 0);
   torrent_info (bdecode_node const& torrent_file, int flags = 0);
   torrent_info (torrent_info const& t);
   ~torrent_info ();
   file_storage const& files () const;
   file_storage const& orig_files () const;
   void rename_file (int index, std::string const& new_filename);
   void remap_files (file_storage const& f);
   std::vector<announce_entry> const& trackers () const;
   void add_tracker (std::string const& url, int tier = 0);
   std::vector<sha1_hash> similar_torrents () const;
   std::vector<std::string> collections () const;
   void add_url_seed (std::string const& url
      , std::string const& extern_auth = std::string()
      , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t());
   std::vector<web_seed_entry> const& web_seeds () const;
   void set_web_seeds (std::vector<web_seed_entry> seeds);
   void add_http_seed (std::string const& url
      , std::string const& extern_auth = std::string()
      , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t());
   boost::int64_t total_size () const;
   int num_pieces () const;
   int piece_length () const;
   const sha1_hash& info_hash () const;
   int num_files () const;
   std::vector<file_slice> map_block (int piece, boost::int64_t offset, int size) const;
   peer_request map_file (int file, boost::int64_t offset, int size) const;
   void unload ();
   void load (char const* buffer, int size, error_code& ec);
   std::string ssl_cert () const;
   bool is_valid () const;
   bool priv () const;
   bool is_i2p () const;
   sha1_hash hash_for_piece (int index) const;
   char const* hash_for_piece_ptr (int index) const;
   int piece_size (int index) const;
   bool is_loaded () const;
   std::vector<sha1_hash> const& merkle_tree () const;
   void set_merkle_tree (std::vector<sha1_hash>& h);
   boost::optional<time_t> creation_date () const;
   const std::string& name () const;
   const std::string& comment () const;
   const std::string& creator () const;
   nodes_t const& nodes () const;
   void add_node (std::pair<std::string, int> const& node);
   bool parse_info_section (bdecode_node const& e, error_code& ec, int flags);
   bdecode_node info (char const* key) const;
   void swap (torrent_info& ti);
   int metadata_size () const;
   boost::shared_array<char> metadata () const;
   bool is_merkle_torrent () const;
   bool parse_torrent_file (bdecode_node const& libtorrent, error_code& ec, int flags);
};

torrent_info()

torrent_info (std::string const& filename, int flags = 0);
torrent_info (std::string const& filename, error_code& ec, int flags = 0);
torrent_info (char const* buffer, int size, error_code& ec, int flags = 0);
torrent_info (sha1_hash const& info_hash, int flags = 0);
torrent_info (bdecode_node const& torrent_file, error_code& ec, int flags = 0);
torrent_info (char const* buffer, int size, int flags = 0);
torrent_info (bdecode_node const& torrent_file, int flags = 0);
torrent_info (torrent_info const& t);

The constructor that takes an info-hash will initialize the info-hash to the given value, but leave all other fields empty. This is used internally when downloading torrents without the metadata. The metadata will be created by libtorrent as soon as it has been downloaded from the swarm.

The constructor that takes a bdecode_node will create a torrent_info object from the information found in the given torrent_file. The bdecode_node represents a tree node in an bencoded file. To load an ordinary .torrent file into a bdecode_node, use bdecode().

The version that takes a buffer pointer and a size will decode it as a .torrent file and initialize the torrent_info object for you.

The version that takes a filename will simply load the torrent file and decode it inside the constructor, for convenience. This might not be the most suitable for applications that want to be able to report detailed errors on what might go wrong.

There is an upper limit on the size of the torrent file that will be loaded by the overload taking a filename. If it's important that even very large torrent files are loaded, use one of the other overloads.

The overloads that takes an error_code const& never throws if an error occur, they will simply set the error code to describe what went wrong and not fully initialize the torrent_info object. The overloads that do not take the extra error_code parameter will always throw if an error occurs. These overloads are not available when building without exception support.

The flags argument is currently unused.

~torrent_info()

~torrent_info ();

frees all storage associated with this torrent_info object

orig_files() files()

file_storage const& files () const;
file_storage const& orig_files () const;

The file_storage object contains the information on how to map the pieces to files. It is separated from the torrent_info object because when creating torrents a storage object needs to be created without having a torrent file. When renaming files in a storage, the storage needs to make its own copy of the file_storage in order to make its mapping differ from the one in the torrent file.

orig_files() returns the original (unmodified) file storage for this torrent. This is used by the web server connection, which needs to request files with the original names. Filename may be changed using torrent_info::rename_file().

For more information on the file_storage object, see the separate document on how to create torrents.

rename_file()

void rename_file (int index, std::string const& new_filename);

Renames a the file with the specified index to the new name. The new filename is reflected by the file_storage returned by files() but not by the one returned by orig_files().

If you want to rename the base name of the torrent (for a multi file torrent), you can copy the file_storage (see files() and orig_files() ), change the name, and then use remap_files().

The new_filename can both be a relative path, in which case the file name is relative to the save_path of the torrent. If the new_filename is an absolute path (i.e. is_complete(new_filename) == true), then the file is detached from the save_path of the torrent. In this case the file is not moved when move_storage() is invoked.

remap_files()

void remap_files (file_storage const& f);

Remaps the file storage to a new file layout. This can be used to, for instance, download all data in a torrent to a single file, or to a number of fixed size sector aligned files, regardless of the number and sizes of the files in the torrent.

The new specified file_storage must have the exact same size as the current one.

trackers() add_tracker()

std::vector<announce_entry> const& trackers () const;
void add_tracker (std::string const& url, int tier = 0);

add_tracker() adds a tracker to the announce-list. The tier determines the order in which the trackers are to be tried.

The trackers() function will return a sorted vector of announce_entry. Each announce entry contains a string, which is the tracker url, and a tier index. The tier index is the high-level priority. No matter which trackers that works or not, the ones with lower tier will always be tried before the one with higher tier number. For more information, see announce_entry.

collections() similar_torrents()

std::vector<sha1_hash> similar_torrents () const;
std::vector<std::string> collections () const;

These two functions are related to BEP 38 (mutable torrents). The vectors returned from these correspond to the "similar" and "collections" keys in the .torrent file. Both info-hashes and collections from within the info-dict and from outside of it are included.

add_url_seed() set_web_seeds() add_http_seed() web_seeds()

void add_url_seed (std::string const& url
      , std::string const& extern_auth = std::string()
      , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t());
std::vector<web_seed_entry> const& web_seeds () const;
void set_web_seeds (std::vector<web_seed_entry> seeds);
void add_http_seed (std::string const& url
      , std::string const& extern_auth = std::string()
      , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t());

web_seeds() returns all url seeds and http seeds in the torrent. Each entry is a web_seed_entry and may refer to either a url seed or http seed.

add_url_seed() and add_http_seed() adds one url to the list of url/http seeds. Currently, the only transport protocol supported for the url is http.

set_web_seeds() replaces all web seeds with the ones specified in the seeds vector.

The extern_auth argument can be used for other authorization schemes than basic HTTP authorization. If set, it will override any username and password found in the URL itself. The string will be sent as the HTTP authorization header's value (without specifying "Basic").

The extra_headers argument defaults to an empty list, but can be used to insert custom HTTP headers in the requests to a specific web seed.

See http seeding for more information.

piece_length() num_pieces() total_size()

boost::int64_t total_size () const;
int num_pieces () const;
int piece_length () const;

total_size(), piece_length() and num_pieces() returns the total number of bytes the torrent-file represents (all the files in it), the number of byte for each piece and the total number of pieces, respectively. The difference between piece_size() and piece_length() is that piece_size() takes the piece index as argument and gives you the exact size of that piece. It will always be the same as piece_length() except in the case of the last piece, which may be smaller.

info_hash()

const sha1_hash& info_hash () const;

returns the info-hash of the torrent

num_files()

int num_files () const;

If you need index-access to files you can use the num_files() along with the file_path(), file_size()-family of functions to access files using indices.

map_block()

std::vector<file_slice> map_block (int piece, boost::int64_t offset, int size) const;

This function will map a piece index, a byte offset within that piece and a size (in bytes) into the corresponding files with offsets where that data for that piece is supposed to be stored. See file_slice.

map_file()

peer_request map_file (int file, boost::int64_t offset, int size) const;

This function will map a range in a specific file into a range in the torrent. The file_offset parameter is the offset in the file, given in bytes, where 0 is the start of the file. See peer_request.

The input range is assumed to be valid within the torrent. file_offset + size is not allowed to be greater than the file size. file_index must refer to a valid file, i.e. it cannot be >= num_files().

unload() load()

void unload ();
void load (char const* buffer, int size, error_code& ec);

load and unload this torrent info

ssl_cert()

std::string ssl_cert () const;

Returns the SSL root certificate for the torrent, if it is an SSL torrent. Otherwise returns an empty string. The certificate is the the public certificate in x509 format.

is_valid()

bool is_valid () const;

returns true if this torrent_info object has a torrent loaded. This is primarily used to determine if a magnet link has had its metadata resolved yet or not.

priv()

bool priv () const;

returns true if this torrent is private. i.e., it should not be distributed on the trackerless network (the kademlia DHT).

is_i2p()

bool is_i2p () const;

returns true if this is an i2p torrent. This is determined by whether or not it has a tracker whose URL domain name ends with ".i2p". i2p torrents disable the DHT and local peer discovery as well as talking to peers over anything other than the i2p network.

hash_for_piece_ptr() hash_for_piece() piece_size()

sha1_hash hash_for_piece (int index) const;
char const* hash_for_piece_ptr (int index) const;
int piece_size (int index) const;

hash_for_piece() takes a piece-index and returns the 20-bytes sha1-hash for that piece and info_hash() returns the 20-bytes sha1-hash for the info-section of the torrent file. hash_for_piece_ptr() returns a pointer to the 20 byte sha1 digest for the piece. Note that the string is not null-terminated.

set_merkle_tree() merkle_tree()

std::vector<sha1_hash> const& merkle_tree () const;
void set_merkle_tree (std::vector<sha1_hash>& h);

merkle_tree() returns a reference to the merkle tree for this torrent, if any.

set_merkle_tree() moves the passed in merkle tree into the torrent_info object. i.e. h will not be identical after the call. You need to set the merkle tree for a torrent that you've just created (as a merkle torrent). The merkle tree is retrieved from the create_torrent::merkle_tree() function, and need to be saved separately from the torrent file itself. Once it's added to libtorrent, the merkle tree will be persisted in the resume data.

creator() creation_date() name() comment()

boost::optional<time_t> creation_date () const;
const std::string& name () const;
const std::string& comment () const;
const std::string& creator () const;

name() returns the name of the torrent.

comment() returns the comment associated with the torrent. If there's no comment, it will return an empty string. creation_date() returns the creation date of the torrent as time_t (posix time). If there's no time stamp in the torrent file, the optional object will be uninitialized.

Both the name and the comment is UTF-8 encoded strings.

creator() returns the creator string in the torrent. If there is no creator string it will return an empty string.

nodes()

nodes_t const& nodes () const;

If this torrent contains any DHT nodes, they are put in this vector in their original form (host name and port number).

add_node()

void add_node (std::pair<std::string, int> const& node);

This is used when creating torrent. Use this to add a known DHT node. It may be used, by the client, to bootstrap into the DHT network.

parse_info_section()

bool parse_info_section (bdecode_node const& e, error_code& ec, int flags);

populates the torrent_info by providing just the info-dict buffer. This is used when loading a torrent from a magnet link for instance, where we only have the info-dict. The bdecode_node e points to a parsed info-dictionary. ec returns an error code if something fails (typically if the info dictionary is malformed). flags are currently unused.

info()

bdecode_node info (char const* key) const;

This function looks up keys from the info-dictionary of the loaded torrent file. It can be used to access extension values put in the .torrent file. If the specified key cannot be found, it returns NULL.

swap()

void swap (torrent_info& ti);

swap the content of this and ti.

metadata_size() metadata()

int metadata_size () const;
boost::shared_array<char> metadata () const;

metadata() returns a the raw info section of the torrent file. The size of the metadata is returned by metadata_size().

is_merkle_torrent()

bool is_merkle_torrent () const;

returns whether or not this is a merkle torrent. see BEP 30.

add_torrent_params

Declared in "libtorrent/add_torrent_params.hpp"

The add_torrent_params is a parameter pack for adding torrents to a session. The key fields when adding a torrent are:

  • ti - when you have a .torrent file
  • url - when you have a magnet link
  • info_hash - when all you have is an info-hash (this is similar to a magnet link)

one of those fields need to be set. Another mandatory field is save_path. The add_torrent_params object is passed into one of the session::add_torrent() overloads or session::async_add_torrent().

If you only specify the info-hash, the torrent file will be downloaded from peers, which requires them to support the metadata extension. For the metadata extension to work, libtorrent must be built with extensions enabled (TORRENT_DISABLE_EXTENSIONS must not be defined). It also takes an optional name argument. This may be left empty in case no name should be assigned to the torrent. In case it's not, the name is used for the torrent as long as it doesn't have metadata. See torrent_handle::name.

struct add_torrent_params
{
   add_torrent_params (storage_constructor_type sc = default_storage_constructor);

   enum flags_t
   {
      flag_seed_mode,
      flag_override_resume_data,
      flag_upload_mode,
      flag_share_mode,
      flag_apply_ip_filter,
      flag_paused,
      flag_auto_managed,
      flag_duplicate_is_error,
      flag_merge_resume_trackers,
      flag_update_subscribe,
      flag_super_seeding,
      flag_sequential_download,
      flag_use_resume_save_path,
      flag_pinned,
      flag_merge_resume_http_seeds,
      flag_stop_when_ready,
   };

   int version;
   boost::shared_ptr<torrent_info> ti;
   std::vector<std::string> trackers;
   std::vector<std::string> url_seeds;
   std::vector<std::pair<std::string, int> > dht_nodes;
   std::string name;
   std::string save_path;
   std::vector<char> resume_data;
   storage_mode_t storage_mode;
   storage_constructor_type storage;
   void* userdata;
   std::vector<boost::uint8_t> file_priorities;
   std::string trackerid;
   std::string url;
   std::string uuid;
   std::string source_feed_url;
   boost::uint64_t flags;
   sha1_hash info_hash;
   int max_uploads;
   int max_connections;
   int upload_limit;
   int download_limit;
};

add_torrent_params()

add_torrent_params (storage_constructor_type sc = default_storage_constructor);

The constructor can be used to initialize the storage constructor, which determines the storage mechanism for the downloaded or seeding data for the torrent. For more information, see the storage field.

enum flags_t

Declared in "libtorrent/add_torrent_params.hpp"

name value description
flag_seed_mode 1

If flag_seed_mode is set, libtorrent will assume that all files are present for this torrent and that they all match the hashes in the torrent file. Each time a peer requests to download a block, the piece is verified against the hash, unless it has been verified already. If a hash fails, the torrent will automatically leave the seed mode and recheck all the files. The use case for this mode is if a torrent is created and seeded, or if the user already know that the files are complete, this is a way to avoid the initial file checks, and significantly reduce the startup time.

Setting flag_seed_mode on a torrent without metadata (a .torrent file) is a no-op and will be ignored.

If resume data is passed in with this torrent, the seed mode saved in there will override the seed mode you set here.

flag_override_resume_data 2 If flag_override_resume_data is set, flags set for this torrent in this add_torrent_params object will take precedence over whatever states are saved in the resume data. For instance, the paused, auto_managed, sequential_download, seed_mode, super_seeding, max_uploads, max_connections, upload_limit and download_limit are all affected by this flag. The intention of this flag is to have any field in add_torrent_params configuring the torrent override the corresponding configuration from the resume file, with the one exception of save resume data, which has its own flag (for historic reasons). "file_priorities" and "save_path" are not affected by this flag.
flag_upload_mode 4

If flag_upload_mode is set, the torrent will be initialized in upload-mode, which means it will not make any piece requests. This state is typically entered on disk I/O errors, and if the torrent is also auto managed, it will be taken out of this state periodically. This mode can be used to avoid race conditions when adjusting priorities of pieces before allowing the torrent to start downloading.

If the torrent is auto-managed (flag_auto_managed), the torrent will eventually be taken out of upload-mode, regardless of how it got there. If it's important to manually control when the torrent leaves upload mode, don't make it auto managed.

flag_share_mode 8

determines if the torrent should be added in share mode or not. Share mode indicates that we are not interested in downloading the torrent, but merely want to improve our share ratio (i.e. increase it). A torrent started in share mode will do its best to never download more than it uploads to the swarm. If the swarm does not have enough demand for upload capacity, the torrent will not download anything. This mode is intended to be safe to add any number of torrents to, without manual screening, without the risk of downloading more than is uploaded.

A torrent in share mode sets the priority to all pieces to 0, except for the pieces that are downloaded, when pieces are decided to be downloaded. This affects the progress bar, which might be set to "100% finished" most of the time. Do not change file or piece priorities for torrents in share mode, it will make it not work.

The share mode has one setting, the share ratio target, see settings_pack::share_mode_target for more info.

flag_apply_ip_filter 16 determines if the IP filter should apply to this torrent or not. By default all torrents are subject to filtering by the IP filter (i.e. this flag is set by default). This is useful if certain torrents needs to be exempt for some reason, being an auto-update torrent for instance.
flag_paused 32 specifies whether or not the torrent is to be started in a paused state. I.e. it won't connect to the tracker or any of the peers until it's resumed. This is typically a good way of avoiding race conditions when setting configuration options on torrents before starting them.
flag_auto_managed 64

If the torrent is auto-managed (flag_auto_managed), the torrent may be resumed at any point, regardless of how it paused. If it's important to manually control when the torrent is paused and resumed, don't make it auto managed.

If flag_auto_managed is set, the torrent will be queued, started and seeded automatically by libtorrent. When this is set, the torrent should also be started as paused. The default queue order is the order the torrents were added. They are all downloaded in that order. For more details, see queuing.

If you pass in resume data, the auto_managed state of the torrent when the resume data was saved will override the auto_managed state you pass in here. You can override this by setting override_resume_data.

flag_duplicate_is_error 128  
flag_merge_resume_trackers 256 defaults to off and specifies whether tracker URLs loaded from resume data should be added to the trackers in the torrent or replace the trackers. When replacing trackers (i.e. this flag is not set), any trackers passed in via add_torrent_params are also replaced by any trackers in the resume data. The default behavior is to have the resume data override the .torrent file _and_ the trackers added in add_torrent_params.
flag_update_subscribe 512 on by default and means that this torrent will be part of state updates when calling post_torrent_updates().
flag_super_seeding 1024 sets the torrent into super seeding mode. If the torrent is not a seed, this flag has no effect. It has the same effect as calling torrent_handle::super_seeding(true) on the torrent handle immediately after adding it.
flag_sequential_download 2048 sets the sequential download state for the torrent. It has the same effect as calling torrent_handle::sequential_download(true) on the torrent handle immediately after adding it.
flag_use_resume_save_path 4096 if this flag is set, the save path from the resume data file, if present, is honored. This defaults to not being set, in which case the save_path specified in add_torrent_params is always used.
flag_pinned 8192 indicates that this torrent should never be unloaded from RAM, even if unloading torrents are allowed in general. Setting this makes the torrent exempt from loading/unloading management.
flag_merge_resume_http_seeds 32768 defaults to off and specifies whether web seed URLs loaded from resume data should be added to the ones in the torrent file or replace them. No distinction is made between the two different kinds of web seeds (BEP 17 and BEP 19). When replacing web seeds (i.e. when this flag is not set), any web seeds passed in via add_torrent_params are also replaced. The default behavior is to have any web seeds in the resume data take precedence over whatever is passed in here as well as the .torrent file.
flag_stop_when_ready 16384 the stop when ready flag. Setting this flag is equivalent to calling torrent_handle::stop_when_ready() immediately after the torrent is added.
version
filled in by the constructor and should be left untouched. It is used for forward binary compatibility.
ti
torrent_info object with the torrent to add. Unless the url or info_hash is set, this is required to be initialized.
trackers
If the torrent doesn't have a tracker, but relies on the DHT to find peers, the trackers can specify tracker URLs for the torrent.
url_seeds
url seeds to be added to the torrent (BEP 17).
dht_nodes name
a list of hostname and port pairs, representing DHT nodes to be added to the session (if DHT is enabled). The hostname may be an IP address.
save_path

the path where the torrent is or will be stored. Note that this may also be stored in resume data. If you want the save path saved in the resume data to be used, you need to set the flag_use_resume_save_path flag.

Note

On windows this path (and other paths) are interpreted as UNC paths. This means they must use backslashes as directory separators and may not contain the special directories "." or "..".

Setting this to an absolute path performs slightly better than a relative path.

resume_data
The optional parameter, resume_data can be given if up to date fast-resume data is available. The fast-resume data can be acquired from a running torrent by calling save_resume_data() on torrent_handle. See fast resume. The vector that is passed in will be swapped into the running torrent instance with std::vector::swap().
storage_mode
One of the values from storage_mode_t. For more information, see storage allocation.
storage
can be used to customize how the data is stored. The default storage will simply write the data to the files it belongs to, but it could be overridden to save everything to a single file at a specific location or encrypt the content on disk for instance. For more information about the storage_interface that needs to be implemented for a custom storage, see storage_interface.
userdata
The userdata parameter is optional and will be passed on to the extension constructor functions, if any (see torrent_handle::add_extension()).
file_priorities
can be set to control the initial file priorities when adding a torrent. The semantics are the same as for torrent_handle::prioritize_files(). The file priorities specified in here take precedence over those specified in the resume data, if any.
trackerid
the default tracker id to be used when announcing to trackers. By default this is empty, and no tracker ID is used, since this is an optional argument. If a tracker returns a tracker ID, that ID is used instead of this.
url
If you specify a url, the torrent will be set in downloading_metadata state until the .torrent file has been downloaded. If there's any error while downloading, the torrent will be stopped and the torrent error state (torrent_status::error) will indicate what went wrong. The url may be set to a magnet link.
uuid
if uuid is specified, it is used to find duplicates. If another torrent is already running with the same UUID as the one being added, it will be considered a duplicate. This is mainly useful for RSS feed items which has UUIDs specified.
source_feed_url
should point to the URL of the RSS feed this torrent comes from, if it comes from an RSS feed.
flags

flags controlling aspects of this torrent and how it's added. See flags_t for details.

Note

The flags field is initialized with default flags by the constructor. In order to preserve default behavior when clearing or setting other flags, make sure to bitwise OR or in a flag or bitwise AND the inverse of a flag to clear it.

info_hash
set this to the info hash of the torrent to add in case the info-hash is the only known property of the torrent. i.e. you don't have a .torrent file nor a magnet link.
max_uploads max_connections upload_limit download_limit

max_uploads, max_connections, upload_limit, download_limit correspond to the set_max_uploads(), set_max_connections(), set_upload_limit() and set_download_limit() functions on torrent_handle. These values let you initialize these settings when the torrent is added, instead of calling these functions immediately following adding it.

-1 means unlimited on these settings just like their counterpart functions on torrent_handle

For fine grained control over rate limits depending on various properties of the peer connection, see peer classes.

peer_info

Declared in "libtorrent/peer_info.hpp"

holds information and statistics about one peer that libtorrent is connected to

struct peer_info
{
   enum peer_flags_t
   {
      interesting,
      choked,
      remote_interested,
      remote_choked,
      supports_extensions,
      local_connection,
      handshake,
      connecting,
      on_parole,
      seed,
      optimistic_unchoke,
      snubbed,
      upload_only,
      endgame_mode,
      holepunched,
      i2p_socket,
      utp_socket,
      ssl_socket,
      rc4_encrypted,
      plaintext_encrypted,
   };

   enum peer_source_flags
   {
      tracker,
      dht,
      pex,
      lsd,
      resume_data,
      incoming,
   };

   enum connection_type_t
   {
      standard_bittorrent,
      web_seed,
      http_seed,
   };

   enum bw_state
   {
      bw_idle,
      bw_limit,
      bw_network,
      bw_disk,
   };

   std::string client;
   bitfield pieces;
   boost::int64_t total_download;
   boost::int64_t total_upload;
   time_duration last_request;
   time_duration last_active;
   time_duration download_queue_time;
   boost::uint32_t flags;
   boost::uint32_t source;
   int up_speed;
   int down_speed;
   int payload_up_speed;
   int payload_down_speed;
   peer_id pid;
   int queue_bytes;
   int request_timeout;
   int send_buffer_size;
   int used_send_buffer;
   int receive_buffer_size;
   int used_receive_buffer;
   int num_hashfails;
   int download_queue_length;
   int timed_out_requests;
   int busy_requests;
   int requests_in_buffer;
   int target_dl_queue_length;
   int upload_queue_length;
   int failcount;
   int downloading_piece_index;
   int downloading_block_index;
   int downloading_progress;
   int downloading_total;
   int connection_type;
   int remote_dl_rate;
   int pending_disk_bytes;
   int pending_disk_read_bytes;
   int send_quota;
   int receive_quota;
   int rtt;
   int num_pieces;
   int download_rate_peak;
   int upload_rate_peak;
   float progress;
   int progress_ppm;
   int estimated_reciprocation_rate;
   tcp::endpoint ip;
   tcp::endpoint local_endpoint;
   char read_state;
   char write_state;
};

enum peer_flags_t

Declared in "libtorrent/peer_info.hpp"

name value description
interesting 1 we are interested in pieces from this peer.
choked 2 we have choked this peer.
remote_interested 4 the peer is interested in us
remote_choked 8 the peer has choked us.
supports_extensions 16 means that this peer supports the extension protocol.
local_connection 32 The connection was initiated by us, the peer has a listen port open, and that port is the same as in the address of this peer. If this flag is not set, this peer connection was opened by this peer connecting to us.
handshake 64 The connection is opened, and waiting for the handshake. Until the handshake is done, the peer cannot be identified.
connecting 128 The connection is in a half-open state (i.e. it is being connected).
on_parole 512 The peer has participated in a piece that failed the hash check, and is now "on parole", which means we're only requesting whole pieces from this peer until it either fails that piece or proves that it doesn't send bad data.
seed 1024 This peer is a seed (it has all the pieces).
optimistic_unchoke 2048 This peer is subject to an optimistic unchoke. It has been unchoked for a while to see if it might unchoke us in return an earn an upload/unchoke slot. If it doesn't within some period of time, it will be choked and another peer will be optimistically unchoked.
snubbed 4096 This peer has recently failed to send a block within the request timeout from when the request was sent. We're currently picking one block at a time from this peer.
upload_only 8192 This peer has either explicitly (with an extension) or implicitly (by becoming a seed) told us that it will not downloading anything more, regardless of which pieces we have.
endgame_mode 16384 This means the last time this peer picket a piece, it could not pick as many as it wanted because there were not enough free ones. i.e. all pieces this peer has were already requested from other peers.
holepunched 32768 This flag is set if the peer was in holepunch mode when the connection succeeded. This typically only happens if both peers are behind a NAT and the peers connect via the NAT holepunch mechanism.
i2p_socket 65536 indicates that this socket is runnin on top of the I2P transport.
utp_socket 131072 indicates that this socket is a uTP socket
ssl_socket 262144 indicates that this socket is running on top of an SSL (TLS) channel
rc4_encrypted 1048576 this connection is obfuscated with RC4
plaintext_encrypted 2097152 the handshake of this connection was obfuscated with a diffie-hellman exchange

enum peer_source_flags

Declared in "libtorrent/peer_info.hpp"

name value description
tracker 1 The peer was received from the tracker.
dht 2 The peer was received from the kademlia DHT.
pex 4 The peer was received from the peer exchange extension.
lsd 8 The peer was received from the local service discovery (The peer is on the local network).
resume_data 16 The peer was added from the fast resume data.
incoming 32 we received an incoming connection from this peer

enum connection_type_t

Declared in "libtorrent/peer_info.hpp"

name value description
standard_bittorrent 0 Regular bittorrent connection
web_seed 1 HTTP connection using the BEP 19 protocol
http_seed 2 HTTP connection using the BEP 17 protocol

enum bw_state

Declared in "libtorrent/peer_info.hpp"

name value description
bw_idle 0 The peer is not waiting for any external events to send or receive data.
bw_limit 1 The peer is waiting for the rate limiter.
bw_network 2 The peer has quota and is currently waiting for a network read or write operation to complete. This is the state all peers are in if there are no bandwidth limits.
bw_disk 4 The peer is waiting for the disk I/O thread to catch up writing buffers to disk before downloading more.
client
a string describing the software at the other end of the connection. In some cases this information is not available, then it will contain a string that may give away something about which software is running in the other end. In the case of a web seed, the server type and version will be a part of this string.
pieces
a bitfield, with one bit per piece in the torrent. Each bit tells you if the peer has that piece (if it's set to 1) or if the peer miss that piece (set to 0).
total_download total_upload
the total number of bytes downloaded from and uploaded to this peer. These numbers do not include the protocol chatter, but only the payload data.
last_request last_active
the time since we last sent a request to this peer and since any transfer occurred with this peer
download_queue_time
the time until all blocks in the request queue will be downloaded
flags
tells you in which state the peer is in. It is set to any combination of the peer_flags_t enum.
source
a combination of flags describing from which sources this peer was received. See peer_source_flags.
up_speed down_speed
the current upload and download speed we have to and from this peer (including any protocol messages). updated about once per second
payload_up_speed payload_down_speed
The transfer rates of payload data only updated about once per second
pid
the peer's id as used in the bit torrent protocol. This id can be used to extract 'fingerprints' from the peer. Sometimes it can tell you which client the peer is using. See identify_client()_
request_timeout
the number of seconds until the current front piece request will time out. This timeout can be adjusted through settings_pack::request_timeout. -1 means that there is not outstanding request.
send_buffer_size used_send_buffer
the number of bytes allocated and used for the peer's send buffer, respectively.
receive_buffer_size used_receive_buffer
the number of bytes allocated and used as receive buffer, respectively.
num_hashfails
the number of pieces this peer has participated in sending us that turned out to fail the hash check.
download_queue_length
this is the number of requests we have sent to this peer that we haven't got a response for yet
timed_out_requests
the number of block requests that have timed out, and are still in the download queue
busy_requests
the number of busy requests in the download queue. A busy request is a request for a block we've also requested from a different peer
requests_in_buffer
the number of requests messages that are currently in the send buffer waiting to be sent.
target_dl_queue_length
the number of requests that is tried to be maintained (this is typically a function of download speed)
upload_queue_length
the number of piece-requests we have received from this peer that we haven't answered with a piece yet.
failcount
the number of times this peer has "failed". i.e. failed to connect or disconnected us. The failcount is decremented when we see this peer in a tracker response or peer exchange message.
downloading_piece_index downloading_block_index downloading_progress downloading_total
You can know which piece, and which part of that piece, that is currently being downloaded from a specific peer by looking at these four members. downloading_piece_index is the index of the piece that is currently being downloaded. This may be set to -1 if there's currently no piece downloading from this peer. If it is >= 0, the other three members are valid. downloading_block_index is the index of the block (or sub-piece) that is being downloaded. downloading_progress is the number of bytes of this block we have received from the peer, and downloading_total is the total number of bytes in this block.
connection_type
the kind of connection this peer uses. See connection_type_t.
remote_dl_rate
an estimate of the rate this peer is downloading at, in bytes per second.
pending_disk_bytes
the number of bytes this peer has pending in the disk-io thread. Downloaded and waiting to be written to disk. This is what is capped by settings_pack::max_queued_disk_bytes.
pending_disk_read_bytes
number of outstanding bytes to read from disk
send_quota receive_quota
the number of bytes this peer has been assigned to be allowed to send and receive until it has to request more quota from the bandwidth manager.
rtt
an estimated round trip time to this peer, in milliseconds. It is estimated by timing the the TCP connect(). It may be 0 for incoming connections.
num_pieces
the number of pieces this peer has.
download_rate_peak upload_rate_peak
the highest download and upload rates seen on this connection. They are given in bytes per second. This number is reset to 0 on reconnect.
progress
the progress of the peer in the range [0, 1]. This is always 0 when floating point operations are disabled, instead use progress_ppm.
progress_ppm
indicates the download progress of the peer in the range [0, 1000000] (parts per million).
estimated_reciprocation_rate
this is an estimation of the upload rate, to this peer, where it will unchoke us. This is a coarse estimation based on the rate at which we sent right before we were choked. This is primarily used for the bittyrant choking algorithm.
ip
the IP-address to this peer. The type is an asio endpoint. For more info, see the asio documentation.
local_endpoint
the IP and port pair the socket is bound to locally. i.e. the IP address of the interface it's going out over. This may be useful for multi-homed clients with multiple interfaces to the internet.
read_state write_state
bitmasks indicating what state this peer is in with regards to sending and receiving data. The states are declared in the bw_state enum.

peer_request

Declared in "libtorrent/peer_request.hpp"

represents a byte range within a piece. Internally this is is used for incoming piece requests.

struct peer_request
{
   bool operator== (peer_request const& r) const;

   int piece;
   int start;
   int length;
};

operator==()

bool operator== (peer_request const& r) const;

returns true if the right hand side peer_request refers to the same range as this does.

piece
the index of the piece in which the range starts.
start
the offset within that piece where the range starts.
length
the size of the range, in bytes.

dht_storage_counters

Declared in "libtorrent/kademlia/dht_storage.hpp"

This structure hold the relevant counters for the storage

struct dht_storage_counters
{
   boost::int32_t torrents;
   boost::int32_t peers;
   boost::int32_t immutable_data;
   boost::int32_t mutable_data;
};

dht_storage_interface

Declared in "libtorrent/kademlia/dht_storage.hpp"

The DHT storage interface is a pure virtual class that can be implemented to customize how the data for the DHT is stored.

The default storage implementation uses three maps in RAM to save the peers, mutable and immutable items and it's designed to provide a fast and fully compliant behavior of the BEPs.

libtorrent comes with one built-in storage implementation: dht_default_storage (private non-accessible class). Its constructor function is called dht_default_storage_constructor().

struct dht_storage_interface
{
   virtual bool get_peers (sha1_hash const& info_hash
      , bool noseed, bool scrape
      , entry& peers) const = 0;
   virtual void announce_peer (sha1_hash const& info_hash
      , tcp::endpoint const& endp
      , std::string const& name, bool seed) = 0;
   virtual bool get_immutable_item (sha1_hash const& target
      , entry& item) const = 0;
   virtual void put_immutable_item (sha1_hash const& target
      , char const* buf, int size
      , address const& addr) = 0;
   virtual bool get_mutable_item_seq (sha1_hash const& target
      , boost::int64_t& seq) const = 0;
   virtual bool get_mutable_item (sha1_hash const& target
      , boost::int64_t seq, bool force_fill
      , entry& item) const = 0;
   virtual void put_mutable_item (sha1_hash const& target
      , char const* buf, int size
      , char const* sig
      , boost::int64_t seq
      , char const* pk
      , char const* salt, int salt_size
      , address const& addr) = 0;
   virtual void tick () = 0;
   virtual dht_storage_counters counters () const = 0;
   virtual ~dht_storage_interface ();
};

get_peers()

virtual bool get_peers (sha1_hash const& info_hash
      , bool noseed, bool scrape
      , entry& peers) const = 0;

This function retrieve the peers tracked by the DHT corresponding to the given info_hash. You can specify if you want only seeds and/or you are scraping the data.

For future implementers: If the torrent tracked contains a name, such a name must be stored as a string in peers["n"]

If the scrape parameter is true, you should fill these keys:

peers["BFpe"] - with the standard bit representation of a
                256 bloom filter containing the downloaders
peers["BFsd"] - with the standard bit representation of a
                256 bloom filter containing the seeders

If the scrape parameter is false, you should fill the key peers["values"] with a list containing a subset of peers tracked by the given info_hash. Such a list should consider the value of dht_settings::max_peers_reply. If noseed is true only peers marked as no seed should be included.

returns true if an entry with the info_hash is found and the data is returned inside the (entry) out parameter peers.

announce_peer()

virtual void announce_peer (sha1_hash const& info_hash
      , tcp::endpoint const& endp
      , std::string const& name, bool seed) = 0;

This function is named announce_peer for consistency with the upper layers, but has nothing to do with networking. Its only responsibility is store the peer in such a way that it's returned in the entry with the lookup_peers.

The name parameter is the name of the torrent if provided in the announce_peer DHT message. The length of this value should have a maximum length in the final storage. The default implementation truncate the value for a maximum of 50 characters.

get_immutable_item()

virtual bool get_immutable_item (sha1_hash const& target
      , entry& item) const = 0;

This function retrieves the immutable item given its target hash.

For future implementers: The value should be returned as an entry in the key item["v"].

returns true if the item is found and the data is returned inside the (entry) out parameter item.

put_immutable_item()

virtual void put_immutable_item (sha1_hash const& target
      , char const* buf, int size
      , address const& addr) = 0;

Store the item's data. This layer is only for storage. The authentication of the item is performed by the upper layer.

For implementers: This data can be stored only if the target is not already present. The implementation should consider the value of dht_settings::max_dht_items.

get_mutable_item_seq()

virtual bool get_mutable_item_seq (sha1_hash const& target
      , boost::int64_t& seq) const = 0;

This function retrieves the sequence number of a mutable item.

returns true if the item is found and the data is returned inside the out parameter seq.

get_mutable_item()

virtual bool get_mutable_item (sha1_hash const& target
      , boost::int64_t seq, bool force_fill
      , entry& item) const = 0;

This function retrieves the mutable stored in the DHT.

For implementers: The item sequence should be stored in the key item["seq"]. if force_fill is true or (0 <= seq and seq < item["seq"]) the following keys should be filled item["v"] - with the value no encoded. item["sig"] - with a string representation of the signature. item["k"] - with a string representation of the public key.

returns true if the item is found and the data is returned inside the (entry) out parameter item.

put_mutable_item()

virtual void put_mutable_item (sha1_hash const& target
      , char const* buf, int size
      , char const* sig
      , boost::int64_t seq
      , char const* pk
      , char const* salt, int salt_size
      , address const& addr) = 0;

Store the item's data. This layer is only for storage. The authentication of the item is performed by the upper layer.

For implementers: The sequence number should be checked if the item is already present. The implementation should consider the value of dht_settings::max_dht_items.

tick()

virtual void tick () = 0;

This function is called periodically (non-constant frequency).

For implementers: Use this functions for expire peers or items or any other storage cleanup.

session_stats_metrics()

Declared in "libtorrent/session_stats.hpp"

std::vector<stats_metric> session_stats_metrics ();

This free function returns the list of available metrics exposed by libtorrent's statistics API. Each metric has a name and a value index. The value index is the index into the array in session_stats_alert where this metric's value can be found when the session stats is sampled (by calling post_session_stats()).

find_metric_idx()

Declared in "libtorrent/session_stats.hpp"

int find_metric_idx (char const* name);

given a name of a metric, this function returns the counter index of it, or -1 if it could not be found. The counter index is the index into the values array returned by session_stats_alert.

make_magnet_uri()

Declared in "libtorrent/magnet_uri.hpp"

std::string make_magnet_uri (torrent_handle const& handle);
std::string make_magnet_uri (torrent_info const& info);

Generates a magnet URI from the specified torrent. If the torrent handle is invalid, an empty string is returned.

For more information about magnet links, see magnet links.

parse_magnet_uri()

Declared in "libtorrent/magnet_uri.hpp"

void parse_magnet_uri (std::string const& uri
   , add_torrent_params& p, error_code& ec);

This function parses out information from the magnet link and populates the add_torrent_params object.

to_hex()

Declared in "libtorrent/hex.hpp"

void to_hex (char const *in, int len, char* out);
std::string to_hex (std::string const& s);

The overload taking a std::string converts (binary) the string s to hexadecimal representation and returns it. The overload taking a char const* and a length converts the binary buffer [in, in + len) to hexadecimal and prints it to the buffer out. The caller is responsible for making sure the buffer pointed to by out is large enough, i.e. has at least len * 2 bytes of space.

from_hex()

Declared in "libtorrent/hex.hpp"

bool from_hex (char const *in, int len, char* out);

converts the buffer [in, in + len) from hexadecimal to binary. The binary output is written to the buffer pointed to by out. The caller is responsible for making sure the buffer at out has enough space for the result to be written to, i.e. (len + 1) / 2 bytes.

version()

Declared in "libtorrent/version.hpp"

char const* version ();

returns the libtorrent version as string form in this format: "<major>.<minor>.<tiny>.<tag>"

generate_fingerprint()

Declared in "libtorrent/fingerprint.hpp"

std::string generate_fingerprint (std::string name
   , int major, int minor = 0, int revision = 0, int tag = 0);

This is a utility function to produce a client ID fingerprint formatted to the most common convention.

The name string should contain exactly two characters. These are the characters unique to your client, used to identify it. Make sure not to clash with anybody else. Here are some taken id's:

id chars client
'AZ' Azureus
'LT' libtorrent (default)
'BX' BittorrentX
'MT' Moonlight Torrent
'TS' Torrent Storm
'SS' Swarm Scope
'XT' Xan Torrent

There's an informal directory of client id's here.

The major, minor, revision and tag parameters are used to identify the version of your client.

hash_value()

Declared in "libtorrent/torrent_handle.hpp"

std::size_t hash_value (torrent_status const& ts);

allows torrent_handle to be used in unordered_map and unordered_set.

hash_value()

Declared in "libtorrent/torrent_handle.hpp"

std::size_t hash_value (torrent_handle const& h);

for boost::hash (and to support using this type in unordered_map etc.)

is_utp_stream_logging()

Declared in "libtorrent/utp_stream.hpp"

bool is_utp_stream_logging ();

set_utp_stream_logging()

Declared in "libtorrent/utp_stream.hpp"

void set_utp_stream_logging (bool enable);

This function should be used at the very beginning and very end of your program.

verify_message()

Declared in "libtorrent/kademlia/msg.hpp"

bool verify_message (bdecode_node const& msg, key_desc_t const desc[]
   , bdecode_node ret[], int size, char* error, int error_size);

dht_default_storage_constructor()

Declared in "libtorrent/kademlia/dht_storage.hpp"

dht_storage_interface* dht_default_storage_constructor (sha1_hash const& id
   , dht_settings const& settings);

sign_mutable_item()

Declared in "libtorrent/kademlia/item.hpp"

void sign_mutable_item (
   std::pair<char const*, int> v
   , std::pair<char const*, int> salt
   , boost::uint64_t seq
   , char const* pk
   , char const* sk
   , char* sig);

given a byte range v and an optional byte range salt, a sequence number, public key pk (must be 32 bytes) and a secret key sk (must be 64 bytes), this function produces a signature which is written into a 64 byte buffer pointed to by sig. The caller is responsible for allocating the destination buffer that's passed in as the sig argument. Typically it would be allocated on the stack.

home

Plugins

libtorrent has a plugin interface for implementing extensions to the protocol. These can be general extensions for transferring metadata or peer exchange extensions, or it could be used to provide a way to customize the protocol to fit a particular (closed) network.

In short, the plugin interface makes it possible to:

  • register extension messages (sent in the extension handshake), see extensions.
  • add data and parse data from the extension handshake.
  • send extension messages and standard bittorrent messages.
  • override or block the handling of standard bittorrent messages.
  • save and restore state via the session state
  • see all alerts that are posted

a word of caution

Writing your own plugin is a very easy way to introduce serious bugs such as dead locks and race conditions. Since a plugin has access to internal structures it is also quite easy to sabotage libtorrent's operation.

All the callbacks in this interface are called with the main libtorrent thread mutex locked. And they are always called from the libtorrent network thread. In case portions of your plugin are called from other threads, typically the main thread, you cannot use any of the member functions on the internal structures in libtorrent, since those require the mutex to be locked. Furthermore, you would also need to have a mutex on your own shared data within the plugin, to make sure it is not accessed at the same time from the libtorrent thread (through a callback). See boost thread's mutex. If you need to send out a message from another thread, it is advised to use an internal queue, and do the actual sending in tick().

Since the plugin interface gives you easy access to internal structures, it is not supported as a stable API. Plugins should be considered specific to a specific version of libtorrent. Although, in practice the internals mostly don't change that dramatically.

plugin-interface

The plugin interface consists of three base classes that the plugin may implement. These are called plugin, torrent_plugin and peer_plugin. They are found in the <libtorrent/extensions.hpp> header.

These plugins are instantiated for each session, torrent and possibly each peer, respectively.

For plugins that only need per torrent state, it is enough to only implement torrent_plugin and pass a constructor function or function object to session::add_extension() or torrent_handle::add_extension() (if the torrent has already been started and you want to hook in the extension at run-time).

The signature of the function is:

boost::shared_ptr<torrent_plugin> (*)(torrent_handle const&, void*);

The second argument is the userdata passed to session::add_torrent() or torrent_handle::add_extension().

The function should return a boost::shared_ptr<torrent_plugin> which may or may not be 0. If it is a null pointer, the extension is simply ignored for this torrent. If it is a valid pointer (to a class inheriting torrent_plugin), it will be associated with this torrent and callbacks will be made on torrent events.

For more elaborate plugins which require session wide state, you would implement plugin, construct an object (in a boost::shared_ptr) and pass it in to session::add_extension().

custom alerts

Since plugins are running within internal libtorrent threads, one convenient way to communicate with the client is to post custom alerts.

The expected interface of any alert, apart from deriving from the alert base class, looks like this:

static const int alert_type = <unique alert ID>;
virtual int type() const { return alert_type; }

virtual std::string message() const;

virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new name(*this)); }

static const int static_category = <bitmask of alert::category_t flags>;
virtual int category() const { return static_category; }

virtual char const* what() const { return <string literal of the name of this alert>; }

The alert_type is used for the type-checking in alert_cast. It must not collide with any other alert. The built-in alerts in libtorrent will not use alert type IDs greater than user_alert_id. When defining your own alert, make sure it's greater than this constant.

type() is the run-time equivalence of the alert_type.

The message() virtual function is expected to construct a useful string representation of the alert and the event or data it represents. Something convenient to put in a log file for instance.

clone() is used internally to copy alerts. The suggested implementation of simply allocating a new instance as a copy of *this is all that's expected.

The static category is required for checking whether or not the category for a specific alert is enabled or not, without instantiating the alert. The category virtual function is the run-time equivalence.

The what() virtual function may simply be a string literal of the class name of your alert.

For more information, see the alert section.

plugin

Declared in "libtorrent/extensions.hpp"

this is the base class for a session plugin. One primary feature is that it is notified of all torrents that are added to the session, and can add its own torrent_plugins.

struct plugin
{
   virtual boost::uint32_t implemented_features ();
   virtual boost::shared_ptr<torrent_plugin> new_torrent (torrent_handle const&, void*);
   virtual void added (session_handle);
   virtual void register_dht_extensions (dht_extensions_t&);
   virtual void on_alert (alert const*);
   virtual bool on_unknown_torrent (sha1_hash const& /* info_hash */
      , peer_connection_handle const& /* pc */, add_torrent_params& /* p */);
   virtual void on_tick ();
   virtual bool on_optimistic_unchoke (std::vector<peer_connection_handle>& /* peers */);
   virtual void save_state (entry&) const;
   virtual void load_state (bdecode_node const&);

   enum feature_flags_t
   {
      optimistic_unchoke_feature,
      tick_feature,
   };
};

implemented_features()

virtual boost::uint32_t implemented_features ();

This function is expected to return a bitmask indicating which features this plugin implements. Some callbacks on this object may not be called unless the corresponding feature flag is returned here. Note that callbacks may still be called even if the corresponding feature is not specified in the return value here. See feature_flags_t for possible flags to return.

new_torrent()

virtual boost::shared_ptr<torrent_plugin> new_torrent (torrent_handle const&, void*);

this is called by the session every time a new torrent is added. The torrent* points to the internal torrent object created for the new torrent. The void* is the userdata pointer as passed in via add_torrent_params.

If the plugin returns a torrent_plugin instance, it will be added to the new torrent. Otherwise, return an empty shared_ptr to a torrent_plugin (the default).

added()

virtual void added (session_handle);

called when plugin is added to a session

register_dht_extensions()

virtual void register_dht_extensions (dht_extensions_t&);

called after a plugin is added allows the plugin to register DHT requests it would like to handle

on_alert()

virtual void on_alert (alert const*);

called when an alert is posted alerts that are filtered are not posted

on_unknown_torrent()

virtual bool on_unknown_torrent (sha1_hash const& /* info_hash */
      , peer_connection_handle const& /* pc */, add_torrent_params& /* p */);

return true if the add_torrent_params should be added

on_tick()

virtual void on_tick ();

called once per second

on_optimistic_unchoke()

virtual bool on_optimistic_unchoke (std::vector<peer_connection_handle>& /* peers */);

called when choosing peers to optimistically unchoke. peer's will be unchoked in the order they appear in the given vector. if the plugin returns true then the ordering provided will be used and no other plugin will be allowed to change it. If your plugin expects this to be called, make sure to include the flag optimistic_unchoke_feature in the return value from implemented_features().

save_state()

virtual void save_state (entry&) const;

called when saving settings state

load_state()

virtual void load_state (bdecode_node const&);

called when loading settings state

enum feature_flags_t

Declared in "libtorrent/extensions.hpp"

name value description
optimistic_unchoke_feature 1 include this bit if your plugin needs to alter the order of the optimistic unchoke of peers. i.e. have the on_optimistic_unchoke() callback be called.
tick_feature 2 include this bit if your plugin needs to have on_tick() called

torrent_plugin

Declared in "libtorrent/extensions.hpp"

Torrent plugins are associated with a single torrent and have a number of functions called at certain events. Many of its functions have the ability to change or override the default libtorrent behavior.

struct torrent_plugin
{
   virtual boost::shared_ptr<peer_plugin> new_connection (peer_connection_handle const&);
   virtual void on_piece_pass (int /*index*/);
   virtual void on_piece_failed (int /*index*/);
   virtual void tick ();
   virtual bool on_resume ();
   virtual bool on_pause ();
   virtual void on_files_checked ();
   virtual void on_state (int /*s*/);
   virtual void on_unload ();
   virtual void on_load ();
   virtual void on_add_peer (tcp::endpoint const&,
      int /*src*/, int /*flags*/);
};

new_connection()

virtual boost::shared_ptr<peer_plugin> new_connection (peer_connection_handle const&);

This function is called each time a new peer is connected to the torrent. You may choose to ignore this by just returning a default constructed shared_ptr (in which case you don't need to override this member function).

If you need an extension to the peer connection (which most plugins do) you are supposed to return an instance of your peer_plugin class. Which in turn will have its hook functions called on event specific to that peer.

The peer_connection_handle will be valid as long as the shared_ptr is being held by the torrent object. So, it is generally a good idea to not keep a shared_ptr to your own peer_plugin. If you want to keep references to it, use weak_ptr.

If this function throws an exception, the connection will be closed.

on_piece_failed() on_piece_pass()

virtual void on_piece_pass (int /*index*/);
virtual void on_piece_failed (int /*index*/);

These hooks are called when a piece passes the hash check or fails the hash check, respectively. The index is the piece index that was downloaded. It is possible to access the list of peers that participated in sending the piece through the torrent and the piece_picker.

tick()

virtual void tick ();

This hook is called approximately once per second. It is a way of making it easy for plugins to do timed events, for sending messages or whatever.

on_resume() on_pause()

virtual bool on_resume ();
virtual bool on_pause ();

These hooks are called when the torrent is paused and resumed respectively. The return value indicates if the event was handled. A return value of true indicates that it was handled, and no other plugin after this one will have this hook function called, and the standard handler will also not be invoked. So, returning true effectively overrides the standard behavior of pause or resume.

Note that if you call pause() or resume() on the torrent from your handler it will recurse back into your handler, so in order to invoke the standard handler, you have to keep your own state on whether you want standard behavior or overridden behavior.

on_files_checked()

virtual void on_files_checked ();

This function is called when the initial files of the torrent have been checked. If there are no files to check, this function is called immediately.

i.e. This function is always called when the torrent is in a state where it can start downloading.

on_state()

virtual void on_state (int /*s*/);

called when the torrent changes state the state is one of torrent_status::state_t enum members

on_unload() on_load()

virtual void on_unload ();
virtual void on_load ();

called when the torrent is unloaded from RAM and loaded again, respectively unload is called right before the torrent is unloaded and load is called right after it's loaded. i.e. the full torrent state is available when these callbacks are called.

on_add_peer()

virtual void on_add_peer (tcp::endpoint const&,
      int /*src*/, int /*flags*/);

called every time a new peer is added to the peer list. This is before the peer is connected to. For flags, see torrent_plugin::flags_t. The source argument refers to the source where we learned about this peer from. It's a bitmask, because many sources may have told us about the same peer. For peer source flags, see peer_info::peer_source_flags.

peer_plugin

Declared in "libtorrent/extensions.hpp"

peer plugins are associated with a specific peer. A peer could be both a regular bittorrent peer (bt_peer_connection) or one of the web seed connections (web_peer_connection or http_seed_connection). In order to only attach to certain peers, make your torrent_plugin::new_connection only return a plugin for certain peer connection types

struct peer_plugin
{
   virtual char const* type () const;
   virtual void add_handshake (entry&);
   virtual void on_disconnect (error_code const& /*ec*/);
   virtual void on_connected ();
   virtual bool on_handshake (char const* /*reserved_bits*/);
   virtual bool on_extension_handshake (bdecode_node const&);
   virtual bool on_have (int /*index*/);
   virtual bool on_bitfield (bitfield const& /*bitfield*/);
   virtual bool on_have_all ();
   virtual bool on_reject (peer_request const&);
   virtual bool on_request (peer_request const&);
   virtual bool on_unchoke ();
   virtual bool on_interested ();
   virtual bool on_allowed_fast (int /*index*/);
   virtual bool on_have_none ();
   virtual bool on_choke ();
   virtual bool on_not_interested ();
   virtual bool on_piece (peer_request const& /*piece*/
      , disk_buffer_holder& /*data*/);
   virtual bool on_suggest (int /*index*/);
   virtual bool on_cancel (peer_request const&);
   virtual bool on_dont_have (int /*index*/);
   virtual void sent_unchoke ();
   virtual void sent_payload (int /* bytes */);
   virtual bool can_disconnect (error_code const& /*ec*/);
   virtual bool on_extended (int /*length*/, int /*msg*/,
      buffer::const_interval /*body*/);
   virtual bool on_unknown_message (int /*length*/, int /*msg*/,
      buffer::const_interval /*body*/);
   virtual void on_piece_pass (int /*index*/);
   virtual void on_piece_failed (int /*index*/);
   virtual void tick ();
   virtual bool write_request (peer_request const&);
};

type()

virtual char const* type () const;

This function is expected to return the name of the plugin.

add_handshake()

virtual void add_handshake (entry&);

can add entries to the extension handshake this is not called for web seeds

on_disconnect()

virtual void on_disconnect (error_code const& /*ec*/);

called when the peer is being disconnected.

on_connected()

virtual void on_connected ();

called when the peer is successfully connected. Note that incoming connections will have been connected by the time the peer plugin is attached to it, and won't have this hook called.

on_handshake()

virtual bool on_handshake (char const* /*reserved_bits*/);

this is called when the initial bittorrent handshake is received. Returning false means that the other end doesn't support this extension and will remove it from the list of plugins. this is not called for web seeds

on_extension_handshake()

virtual bool on_extension_handshake (bdecode_node const&);

called when the extension handshake from the other end is received if this returns false, it means that this extension isn't supported by this peer. It will result in this peer_plugin being removed from the peer_connection and destructed. this is not called for web seeds

on_bitfield() on_have_none() on_suggest() on_unchoke() on_cancel() on_have() on_choke() on_piece() on_request() on_reject() on_not_interested() on_interested() on_allowed_fast() on_have_all() on_dont_have()

virtual bool on_have (int /*index*/);
virtual bool on_bitfield (bitfield const& /*bitfield*/);
virtual bool on_have_all ();
virtual bool on_reject (peer_request const&);
virtual bool on_request (peer_request const&);
virtual bool on_unchoke ();
virtual bool on_interested ();
virtual bool on_allowed_fast (int /*index*/);
virtual bool on_have_none ();
virtual bool on_choke ();
virtual bool on_not_interested ();
virtual bool on_piece (peer_request const& /*piece*/
      , disk_buffer_holder& /*data*/);
virtual bool on_suggest (int /*index*/);
virtual bool on_cancel (peer_request const&);
virtual bool on_dont_have (int /*index*/);

returning true from any of the message handlers indicates that the plugin has handled the message. it will break the plugin chain traversing and not let anyone else handle the message, including the default handler.

sent_unchoke()

virtual void sent_unchoke ();

called after a choke message has been sent to the peer

sent_payload()

virtual void sent_payload (int /* bytes */);

called after piece data has been sent to the peer this can be used for stats book keeping

can_disconnect()

virtual bool can_disconnect (error_code const& /*ec*/);

called when libtorrent think this peer should be disconnected. if the plugin returns false, the peer will not be disconnected.

on_extended()

virtual bool on_extended (int /*length*/, int /*msg*/,
      buffer::const_interval /*body*/);

called when an extended message is received. If returning true, the message is not processed by any other plugin and if false is returned the next plugin in the chain will receive it to be able to handle it. This is not called for web seeds. thus function may be called more than once per incoming message, but only the last of the calls will the body size equal the length. i.e. Every time another fragment of the message is received, this function will be called, until finally the whole message has been received. The purpose of this is to allow early disconnects for invalid messages and for reporting progress of receiving large messages.

on_unknown_message()

virtual bool on_unknown_message (int /*length*/, int /*msg*/,
      buffer::const_interval /*body*/);

this is not called for web seeds

on_piece_failed() on_piece_pass()

virtual void on_piece_pass (int /*index*/);
virtual void on_piece_failed (int /*index*/);

called when a piece that this peer participated in either fails or passes the hash_check

tick()

virtual void tick ();

called approximately once every second

write_request()

virtual bool write_request (peer_request const&);

called each time a request message is to be sent. If true is returned, the original request message won't be sent and no other plugin will have this function called.

crypto_plugin

Declared in "libtorrent/extensions.hpp"

struct crypto_plugin
{
   virtual void set_incoming_key (unsigned char const* key, int len) = 0;
   virtual void set_outgoing_key (unsigned char const* key, int len) = 0;
   virtual int encrypt (std::vector<boost::asio::mutable_buffer>& /*send_vec*/) = 0;
   virtual void decrypt (std::vector<boost::asio::mutable_buffer>& /*receive_vec*/
      , int& /* consume */, int& /*produce*/, int& /*packet_size*/) = 0;
};

encrypt()

virtual int encrypt (std::vector<boost::asio::mutable_buffer>& /*send_vec*/) = 0;

encrypted the provided buffers and returns the number of bytes which are now ready to be sent to the lower layer. This must be at least as large as the number of bytes passed in and may be larger if there is additional data to be inserted at the head of the send buffer. The additional data is retrieved from the passed in vector. The vector must be cleared if no additional data is to be inserted.

decrypt()

virtual void decrypt (std::vector<boost::asio::mutable_buffer>& /*receive_vec*/
      , int& /* consume */, int& /*produce*/, int& /*packet_size*/) = 0;

decrypt the provided buffers. consume is set to the number of bytes which should be trimmed from the head of the buffers, default is 0

produce is set to the number of bytes of payload which are now ready to be sent to the upper layer. default is the number of bytes passed in receive_vec

packet_size is set to the minimum number of bytes which must be read to advance the next step of decryption. default is 0

create_ut_metadata_plugin()

Declared in "libtorrent/extensions/ut_metadata.hpp"

boost::shared_ptr<torrent_plugin> create_ut_metadata_plugin (torrent_handle const&, void*);

constructor function for the ut_metadata extension. The ut_metadata extension allows peers to request the .torrent file (or more specifically the 'info'-dictionary of the .torrent file) from each other. This is the main building block in making magnet links work. This extension is enabled by default unless explicitly disabled in the session constructor.

This can either be passed in the add_torrent_params::extensions field, or via torrent_handle::add_extension().

create_smart_ban_plugin()

Declared in "libtorrent/extensions/smart_ban.hpp"

boost::shared_ptr<torrent_plugin> create_smart_ban_plugin (torrent_handle const&, void*);

constructor function for the smart ban extension. The extension keeps track of the data peers have sent us for failing pieces and once the piece completes and passes the hash check bans the peers that turned out to have sent corrupt data. This function can either be passed in the add_torrent_params::extensions field, or via torrent_handle::add_extension().

create_ut_pex_plugin()

Declared in "libtorrent/extensions/ut_pex.hpp"

boost::shared_ptr<torrent_plugin> create_ut_pex_plugin (torrent_handle const&, void*);

constructor function for the ut_pex extension. The ut_pex extension allows peers to gossip about their connections, allowing the swarm stay well connected and peers aware of more peers in the swarm. This extension is enabled by default unless explicitly disabled in the session constructor.

This can either be passed in the add_torrent_params::extensions field, or via torrent_handle::add_extension().

home

Create Torrents

This section describes the functions and classes that are used to create torrent files. It is a layered API with low level classes and higher level convenience functions. A torrent is created in 4 steps:

  1. first the files that will be part of the torrent are determined.
  2. the torrent properties are set, such as tracker url, web seeds, DHT nodes etc.
  3. Read through all the files in the torrent, SHA-1 all the data and set the piece hashes.
  4. The torrent is bencoded into a file or buffer.

If there are a lot of files and or deep directory hierarchies to traverse, step one can be time consuming.

Typically step 3 is by far the most time consuming step, since it requires to read all the bytes from all the files in the torrent.

All of these classes and functions are declared by including libtorrent/create_torrent.hpp.

example:

file_storage fs;

// recursively adds files in directories
add_files(fs, "./my_torrent");

create_torrent t(fs);
t.add_tracker("http://my.tracker.com/announce");
t.set_creator("libtorrent example");

// reads the files and calculates the hashes
set_piece_hashes(t, ".");

ofstream out("my_torrent.torrent", std::ios_base::binary);
bencode(std::ostream_iterator<char>(out), t.generate());

create_torrent

Declared in "libtorrent/create_torrent.hpp"

This class holds state for creating a torrent. After having added all information to it, call create_torrent::generate() to generate the torrent. The entry that's returned can then be bencoded into a .torrent file using bencode().

struct create_torrent
{
   create_torrent (torrent_info const& ti);
   create_torrent (torrent_info const& ti, bool use_preformatted);
   create_torrent (file_storage& fs, int piece_size = 0
      , int pad_file_limit = -1, int flags = optimize_alignment
      , int alignment = -1);
   entry generate () const;
   file_storage const& files () const;
   void set_comment (char const* str);
   void set_creator (char const* str);
   void set_hash (int index, sha1_hash const& h);
   void set_file_hash (int index, sha1_hash const& h);
   void add_url_seed (std::string const& url);
   void add_http_seed (std::string const& url);
   void add_node (std::pair<std::string, int> const& node);
   void add_tracker (std::string const& url, int tier = 0);
   void set_root_cert (std::string const& pem);
   bool priv () const;
   void set_priv (bool p);
   int num_pieces () const;
   int piece_length () const;
   int piece_size (int i) const;
   std::vector<sha1_hash> const& merkle_tree () const;
   void add_similar_torrent (sha1_hash ih);
   void add_collection (std::string c);

   enum flags_t
   {
      optimize_alignment,
      merkle,
      modification_time,
      symlinks,
      mutable_torrent_support,
   };
};

create_torrent()

create_torrent (torrent_info const& ti);
create_torrent (torrent_info const& ti, bool use_preformatted);
create_torrent (file_storage& fs, int piece_size = 0
      , int pad_file_limit = -1, int flags = optimize_alignment
      , int alignment = -1);

The piece_size is the size of each piece in bytes. It must be a multiple of 16 kiB. If a piece size of 0 is specified, a piece_size will be calculated such that the torrent file is roughly 40 kB.

If a pad_file_limit is specified (other than -1), any file larger than the specified number of bytes will be preceded by a pad file to align it with the start of a piece. The pad_file_limit is ignored unless the optimize_alignment flag is passed. Typically it doesn't make sense to set this any lower than 4 kiB.

The overload that takes a torrent_info object will make a verbatim copy of its info dictionary (to preserve the info-hash). The copy of the info dictionary will be used by create_torrent::generate(). This means that none of the member functions of create_torrent that affects the content of the info dictionary (such as set_hash()), will have any affect.

The flags arguments specifies options for the torrent creation. It can be any combination of the flags defined by create_torrent::flags_t.

alignment is used when pad files are enabled. This is the size eligible files are aligned to. The default is -1, which means the piece size of the torrent. The use_preformatted parameter can be set to true to preserve invalid encoding of the .torrent file.

generate()

entry generate () const;

This function will generate the .torrent file as a bencode tree. In order to generate the flat file, use the bencode() function.

It may be useful to add custom entries to the torrent file before bencoding it and saving it to disk.

If anything goes wrong during torrent generation, this function will return an empty entry structure. You can test for this condition by querying the type of the entry:

file_storage fs;
// add file ...
create_torrent t(fs);
// add trackers and piece hashes ...
e = t.generate();

if (e.type() == entry::undefined_t)
{
        // something went wrong
}

For instance, you cannot generate a torrent with 0 files in it. If you don't add any files to the file_storage, torrent generation will fail.

files()

file_storage const& files () const;

returns an immutable reference to the file_storage used to create the torrent from.

set_comment()

void set_comment (char const* str);

Sets the comment for the torrent. The string str should be utf-8 encoded. The comment in a torrent file is optional.

set_creator()

void set_creator (char const* str);

Sets the creator of the torrent. The string str should be utf-8 encoded. This is optional.

set_hash()

void set_hash (int index, sha1_hash const& h);

This sets the SHA-1 hash for the specified piece (index). You are required to set the hash for every piece in the torrent before generating it. If you have the files on disk, you can use the high level convenience function to do this. See set_piece_hashes().

set_file_hash()

void set_file_hash (int index, sha1_hash const& h);

This sets the sha1 hash for this file. This hash will end up under the key sha1 associated with this file (for multi-file torrents) or in the root info dictionary for single-file torrents.

add_url_seed() add_http_seed()

void add_url_seed (std::string const& url);
void add_http_seed (std::string const& url);

This adds a url seed to the torrent. You can have any number of url seeds. For a single file torrent, this should be an HTTP url, pointing to a file with identical content as the file of the torrent. For a multi-file torrent, it should point to a directory containing a directory with the same name as this torrent, and all the files of the torrent in it.

The second function, add_http_seed() adds an HTTP seed instead.

add_node()

void add_node (std::pair<std::string, int> const& node);

This adds a DHT node to the torrent. This especially useful if you're creating a tracker less torrent. It can be used by clients to bootstrap their DHT node from. The node is a hostname and a port number where there is a DHT node running. You can have any number of DHT nodes in a torrent.

add_tracker()

void add_tracker (std::string const& url, int tier = 0);

Adds a tracker to the torrent. This is not strictly required, but most torrents use a tracker as their main source of peers. The url should be an http:// or udp:// url to a machine running a bittorrent tracker that accepts announces for this torrent's info-hash. The tier is the fallback priority of the tracker. All trackers with tier 0 are tried first (in any order). If all fail, trackers with tier 1 are tried. If all of those fail, trackers with tier 2 are tried, and so on.

set_root_cert()

void set_root_cert (std::string const& pem);

This function sets an X.509 certificate in PEM format to the torrent. This makes the torrent an SSL torrent. An SSL torrent requires that each peer has a valid certificate signed by this root certificate. For SSL torrents, all peers are connecting over SSL connections. For more information, see the section on ssl torrents.

The string is not the path to the cert, it's the actual content of the certificate, loaded into a std::string.

priv() set_priv()

bool priv () const;
void set_priv (bool p);

Sets and queries the private flag of the torrent. Torrents with the private flag set ask clients to not use any other sources than the tracker for peers, and to not advertise itself publicly, apart from the tracker.

num_pieces()

int num_pieces () const;

returns the number of pieces in the associated file_storage object.

piece_length() piece_size()

int piece_length () const;
int piece_size (int i) const;

piece_length() returns the piece size of all pieces but the last one. piece_size() returns the size of the specified piece. these functions are just forwarding to the associated file_storage.

merkle_tree()

std::vector<sha1_hash> const& merkle_tree () const;

This function returns the merkle hash tree, if the torrent was created as a merkle torrent. The tree is created by generate() and won't be valid until that function has been called. When creating a merkle tree torrent, the actual tree itself has to be saved off separately and fed into libtorrent the first time you start seeding it, through the torrent_info::set_merkle_tree() function. From that point onwards, the tree will be saved in the resume data.

add_collection() add_similar_torrent()

void add_similar_torrent (sha1_hash ih);
void add_collection (std::string c);

Add similar torrents (by info-hash) or collections of similar torrents. Similar torrents are expected to share some files with this torrent. Torrents sharing a collection name with this torrent are also expected to share files with this torrent. A torrent may have more than one collection and more than one similar torrents. For more information, see BEP 38.

enum flags_t

Declared in "libtorrent/create_torrent.hpp"

name value description
optimize_alignment 1 This will insert pad files to align the files to piece boundaries, for optimized disk-I/O. This will minimize the number of bytes of pad- files, to keep the impact down for clients that don't support them.
merkle 2 This will create a merkle hash tree torrent. A merkle torrent cannot be opened in clients that don't specifically support merkle torrents. The benefit is that the resulting torrent file will be much smaller and not grow with more pieces. When this option is specified, it is recommended to have a fairly small piece size, say 64 kiB. When creating merkle torrents, the full hash tree is also generated and should be saved off separately. It is accessed through the create_torrent::merkle_tree() function.
modification_time 4 This will include the file modification time as part of the torrent. This is not enabled by default, as it might cause problems when you create a torrent from separate files with the same content, hoping to yield the same info-hash. If the files have different modification times, with this option enabled, you would get different info-hashes for the files.
symlinks 8 If this flag is set, files that are symlinks get a symlink attribute set on them and their data will not be included in the torrent. This is useful if you need to reconstruct a file hierarchy which contains symlinks.
mutable_torrent_support 16 to create a torrent that can be updated via a mutable torrent (see BEP 38). This also needs to be enabled for torrents that update another torrent.

add_files()

Declared in "libtorrent/create_torrent.hpp"

void add_files (file_storage& fs, std::string const& file
   , boost::function<bool(std::string)> p, boost::uint32_t flags = 0);
void add_files (file_storage& fs, std::string const& file
   , boost::uint32_t flags = 0);

Adds the file specified by path to the file_storage object. In case path refers to a directory, files will be added recursively from the directory.

If specified, the predicate p is called once for every file and directory that is encountered. Files for which p returns true are added, and directories for which p returns true are traversed. p must have the following signature:

bool Pred(std::string const& p);

The path that is passed in to the predicate is the full path of the file or directory. If no predicate is specified, all files are added, and all directories are traversed.

The ".." directory is never traversed.

The flags argument should be the same as the flags passed to the create_torrent constructor.

set_piece_hashes()

Declared in "libtorrent/create_torrent.hpp"

void set_piece_hashes (create_torrent& t, std::string const& p
   , boost::function<void(int)> const& f, error_code& ec);
inline void set_piece_hashes (create_torrent& t, std::string const& p);
inline void set_piece_hashes (create_torrent& t, std::string const& p, error_code& ec);

This function will assume that the files added to the torrent file exists at path p, read those files and hash the content and set the hashes in the create_torrent object. The optional function f is called in between every hash that is set. f must have the following signature:

void Fun(int);

The overloads that don't take an error_code& may throw an exception in case of a file error, the other overloads sets the error code to reflect the error, if any.

home

Error Codes

libtorrent_exception

Declared in "libtorrent/error_code.hpp"

struct libtorrent_exception : std::exception
{
   libtorrent_exception (libtorrent_exception const&) = default;
   virtual const char* what () const TORRENT_EXCEPTION_THROW_SPECIFIER;
   virtual ~libtorrent_exception () TORRENT_EXCEPTION_THROW_SPECIFIER;
   libtorrent_exception& operator= (libtorrent_exception const&) = default;
   libtorrent_exception (error_code const& s);
   error_code error () const;
};

storage_error

Declared in "libtorrent/error_code.hpp"

used by storage to return errors also includes which underlying file the error happened on

struct storage_error
{
   storage_error (error_code e);
   storage_error ();
   operator bool () const;
   char const* operation_str () const;

   error_code ec;
   boost::int32_t file:24;
   boost::uint32_t operation:8;
};

operation_str()

char const* operation_str () const;

Returns a string literal representing the file operation that failed. If there were no failure, it returns an empty string.

ec
the error that occurred
file
the file the error occurred on
operation
A code from file_operation_t enum, indicating what kind of operation failed.

libtorrent_category()

Declared in "libtorrent/error_code.hpp"

boost::system::error_category& libtorrent_category ();

return the instance of the libtorrent_error_category which maps libtorrent error codes to human readable error messages.

http_category()

Declared in "libtorrent/error_code.hpp"

boost::system::error_category& http_category ();

returns the error_category for HTTP errors

upnp_category()

Declared in "libtorrent/upnp.hpp"

boost::system::error_category& upnp_category ();

the boost.system error category for UPnP errors

gzip_category()

Declared in "libtorrent/gzip.hpp"

boost::system::error_category& gzip_category ();

get the error_category for zip errors

bdecode_category()

Declared in "libtorrent/bdecode.hpp"

boost::system::error_category& bdecode_category ();

socks_category()

Declared in "libtorrent/socks5_stream.hpp"

boost::system::error_category& socks_category ();

returns the error_category for SOCKS5 errors

i2p_category()

Declared in "libtorrent/i2p_stream.hpp"

boost::system::error_category& i2p_category ();

returns the error category for I2P errors

enum error_code_enum

Declared in "libtorrent/error_code.hpp"

name value description
no_error 0 Not an error
file_collision 1 Two torrents has files which end up overwriting each other
failed_hash_check 2 A piece did not match its piece hash
torrent_is_no_dict 3 The .torrent file does not contain a bencoded dictionary at its top level
torrent_missing_info 4 The .torrent file does not have an info dictionary
torrent_info_no_dict 5 The .torrent file's info entry is not a dictionary
torrent_missing_piece_length 6 The .torrent file does not have a piece length entry
torrent_missing_name 7 The .torrent file does not have a name entry
torrent_invalid_name 8 The .torrent file's name entry is invalid
torrent_invalid_length 9 The length of a file, or of the whole .torrent file is invalid. Either negative or not an integer
torrent_file_parse_failed 10 Failed to parse a file entry in the .torrent
torrent_missing_pieces 11 The pieces field is missing or invalid in the .torrent file
torrent_invalid_hashes 12 The pieces string has incorrect length
too_many_pieces_in_torrent 13 The .torrent file has more pieces than is supported by libtorrent
invalid_swarm_metadata 14 The metadata (.torrent file) that was received from the swarm matched the info-hash, but failed to be parsed
invalid_bencoding 15 The file or buffer is not correctly bencoded
no_files_in_torrent 16 The .torrent file does not contain any files
invalid_escaped_string 17 The string was not properly url-encoded as expected
session_is_closing 18 Operation is not permitted since the session is shutting down
duplicate_torrent 19 There's already a torrent with that info-hash added to the session
invalid_torrent_handle 20 The supplied torrent_handle is not referring to a valid torrent
invalid_entry_type 21 The type requested from the entry did not match its type
missing_info_hash_in_uri 22 The specified URI does not contain a valid info-hash
file_too_short 23 One of the files in the torrent was unexpectedly small. This might be caused by files being changed by an external process
unsupported_url_protocol 24 The URL used an unknown protocol. Currently http and https (if built with openssl support) are recognized. For trackers udp is recognized as well.
url_parse_error 25 The URL did not conform to URL syntax and failed to be parsed
peer_sent_empty_piece 26 The peer sent a 'piece' message of length 0
parse_failed 27 A bencoded structure was corrupt and failed to be parsed
invalid_file_tag 28 The fast resume file was missing or had an invalid file version tag
missing_info_hash 29 The fast resume file was missing or had an invalid info-hash
mismatching_info_hash 30 The info-hash did not match the torrent
invalid_hostname 31 The URL contained an invalid hostname
invalid_port 32 The URL had an invalid port
port_blocked 33 The port is blocked by the port-filter, and prevented the connection
expected_close_bracket_in_address 34 The IPv6 address was expected to end with ']'
destructing_torrent 35 The torrent is being destructed, preventing the operation to succeed
timed_out 36 The connection timed out
upload_upload_connection 37 The peer is upload only, and we are upload only. There's no point in keeping the connection
uninteresting_upload_peer 38 The peer is upload only, and we're not interested in it. There's no point in keeping the connection
invalid_info_hash 39 The peer sent an unknown info-hash
torrent_paused 40 The torrent is paused, preventing the operation from succeeding
invalid_have 41 The peer sent an invalid have message, either wrong size or referring to a piece that doesn't exist in the torrent
invalid_bitfield_size 42 The bitfield message had the incorrect size
too_many_requests_when_choked 43 The peer kept requesting pieces after it was choked, possible abuse attempt.
invalid_piece 44 The peer sent a piece message that does not correspond to a piece request sent by the client
no_memory 45 memory allocation failed
torrent_aborted 46 The torrent is aborted, preventing the operation to succeed
self_connection 47 The peer is a connection to ourself, no point in keeping it
invalid_piece_size 48 The peer sent a piece message with invalid size, either negative or greater than one block
timed_out_no_interest 49 The peer has not been interesting or interested in us for too long, no point in keeping it around
timed_out_inactivity 50 The peer has not said anything in a long time, possibly dead
timed_out_no_handshake 51 The peer did not send a handshake within a reasonable amount of time, it might not be a bittorrent peer
timed_out_no_request 52 The peer has been unchoked for too long without requesting any data. It might be lying about its interest in us
invalid_choke 53 The peer sent an invalid choke message
invalid_unchoke 54 The peer send an invalid unchoke message
invalid_interested 55 The peer sent an invalid interested message
invalid_not_interested 56 The peer sent an invalid not-interested message
invalid_request 57 The peer sent an invalid piece request message
invalid_hash_list 58 The peer sent an invalid hash-list message (this is part of the merkle-torrent extension)
invalid_hash_piece 59 The peer sent an invalid hash-piece message (this is part of the merkle-torrent extension)
invalid_cancel 60 The peer sent an invalid cancel message
invalid_dht_port 61 The peer sent an invalid DHT port-message
invalid_suggest 62 The peer sent an invalid suggest piece-message
invalid_have_all 63 The peer sent an invalid have all-message
invalid_have_none 64 The peer sent an invalid have none-message
invalid_reject 65 The peer sent an invalid reject message
invalid_allow_fast 66 The peer sent an invalid allow fast-message
invalid_extended 67 The peer sent an invalid extension message ID
invalid_message 68 The peer sent an invalid message ID
sync_hash_not_found 69 The synchronization hash was not found in the encrypted handshake
invalid_encryption_constant 70 The encryption constant in the handshake is invalid
no_plaintext_mode 71 The peer does not support plaintext, which is the selected mode
no_rc4_mode 72 The peer does not support rc4, which is the selected mode
unsupported_encryption_mode 73 The peer does not support any of the encryption modes that the client supports
unsupported_encryption_mode_selected 74 The peer selected an encryption mode that the client did not advertise and does not support
invalid_pad_size 75 The pad size used in the encryption handshake is of invalid size
invalid_encrypt_handshake 76 The encryption handshake is invalid
no_incoming_encrypted 77 The client is set to not support incoming encrypted connections and this is an encrypted connection
no_incoming_regular 78 The client is set to not support incoming regular bittorrent connections, and this is a regular connection
duplicate_peer_id 79 The client is already connected to this peer-ID
torrent_removed 80 Torrent was removed
packet_too_large 81 The packet size exceeded the upper sanity check-limit
reserved 82  
http_error 83 The web server responded with an error
missing_location 84 The web server response is missing a location header
invalid_redirection 85 The web seed redirected to a path that no longer matches the .torrent directory structure
redirecting 86 The connection was closed because it redirected to a different URL
invalid_range 87 The HTTP range header is invalid
no_content_length 88 The HTTP response did not have a content length
banned_by_ip_filter 89 The IP is blocked by the IP filter
too_many_connections 90 At the connection limit
peer_banned 91 The peer is marked as banned
stopping_torrent 92 The torrent is stopping, causing the operation to fail
too_many_corrupt_pieces 93 The peer has sent too many corrupt pieces and is banned
torrent_not_ready 94 The torrent is not ready to receive peers
peer_not_constructed 95 The peer is not completely constructed yet
session_closing 96 The session is closing, causing the operation to fail
optimistic_disconnect 97 The peer was disconnected in order to leave room for a potentially better peer
torrent_finished 98 The torrent is finished
no_router 99 No UPnP router found
metadata_too_large 100 The metadata message says the metadata exceeds the limit
invalid_metadata_request 101 The peer sent an invalid metadata request message
invalid_metadata_size 102 The peer advertised an invalid metadata size
invalid_metadata_offset 103 The peer sent a message with an invalid metadata offset
invalid_metadata_message 104 The peer sent an invalid metadata message
pex_message_too_large 105 The peer sent a peer exchange message that was too large
invalid_pex_message 106 The peer sent an invalid peer exchange message
invalid_lt_tracker_message 107 The peer sent an invalid tracker exchange message
too_frequent_pex 108 The peer sent an pex messages too often. This is a possible attempt of and attack
no_metadata 109 The operation failed because it requires the torrent to have the metadata (.torrent file) and it doesn't have it yet. This happens for magnet links before they have downloaded the metadata, and also torrents added by URL.
invalid_dont_have 110 The peer sent an invalid dont_have message. The don't have message is an extension to allow peers to advertise that the no longer has a piece they previously had.
requires_ssl_connection 111 The peer tried to connect to an SSL torrent without connecting over SSL.
invalid_ssl_cert 112 The peer tried to connect to a torrent with a certificate for a different torrent.
not_an_ssl_torrent 113 the torrent is not an SSL torrent, and the operation requires an SSL torrent
banned_by_port_filter 114 peer was banned because its listen port is within a banned port range, as specified by the port_filter.
unsupported_protocol_version 120 The NAT-PMP router responded with an unsupported protocol version
natpmp_not_authorized 121 You are not authorized to map ports on this NAT-PMP router
network_failure 122 The NAT-PMP router failed because of a network failure
no_resources 123 The NAT-PMP router failed because of lack of resources
unsupported_opcode 124 The NAT-PMP router failed because an unsupported opcode was sent
missing_file_sizes 130 The resume data file is missing the 'file sizes' entry
no_files_in_resume_data 131 The resume data file 'file sizes' entry is empty
missing_pieces 132 The resume data file is missing the 'pieces' and 'slots' entry
mismatching_number_of_files 133 The number of files in the resume data does not match the number of files in the torrent
mismatching_file_size 134 One of the files on disk has a different size than in the fast resume file
mismatching_file_timestamp 135 One of the files on disk has a different timestamp than in the fast resume file
not_a_dictionary 136 The resume data file is not a dictionary
invalid_blocks_per_piece 137 The 'blocks per piece' entry is invalid in the resume data file
missing_slots 138 The resume file is missing the 'slots' entry, which is required for torrents with compact allocation. DEPRECATED
too_many_slots 139 The resume file contains more slots than the torrent
invalid_slot_list 140 The 'slot' entry is invalid in the resume data
invalid_piece_index 141 One index in the 'slot' list is invalid
pieces_need_reorder 142 The pieces on disk needs to be re-ordered for the specified allocation mode. This happens if you specify sparse allocation and the files on disk are using compact storage. The pieces needs to be moved to their right position. DEPRECATED
resume_data_not_modified 143 this error is returned when asking to save resume data and specifying the flag to only save when there's anything new to save (torrent_handle::only_if_modified) and there wasn't anything changed.
http_parse_error 150 The HTTP header was not correctly formatted
http_missing_location 151 The HTTP response was in the 300-399 range but lacked a location header
http_failed_decompress 152 The HTTP response was encoded with gzip or deflate but decompressing it failed
no_i2p_router 160 The URL specified an i2p address, but no i2p router is configured
no_i2p_endpoint 161 i2p acceptor is not available yet, can't announce without endpoint
scrape_not_available 170 The tracker URL doesn't support transforming it into a scrape URL. i.e. it doesn't contain "announce.
invalid_tracker_response 171 invalid tracker response
invalid_peer_dict 172 invalid peer dictionary entry. Not a dictionary
tracker_failure 173 tracker sent a failure message
invalid_files_entry 174 missing or invalid 'files' entry
invalid_hash_entry 175 missing or invalid 'hash' entry
invalid_peers_entry 176 missing or invalid 'peers' and 'peers6' entry
invalid_tracker_response_length 177 udp tracker response packet has invalid size
invalid_tracker_transaction_id 178 invalid transaction id in udp tracker response
invalid_tracker_action 179 invalid action field in udp tracker response
error_code_max 180 the number of error codes

enum http_errors

Declared in "libtorrent/error_code.hpp"

name value description
cont 100  
ok 200  
created 201  
accepted 202  
no_content 204  
multiple_choices 300  
moved_permanently 301  
moved_temporarily 302  
not_modified 304  
bad_request 400  
unauthorized 401  
forbidden 403  
not_found 404  
internal_server_error 500  
not_implemented 501  
bad_gateway 502  
service_unavailable 503  

enum error_code_enum

Declared in "libtorrent/upnp.hpp"

name value description
no_error 0 No error
invalid_argument 402 One of the arguments in the request is invalid
action_failed 501 The request failed
value_not_in_array 714 The specified value does not exist in the array
source_ip_cannot_be_wildcarded 715 The source IP address cannot be wild-carded, but must be fully specified
external_port_cannot_be_wildcarded 716 The external port cannot be wildcarded, but must be specified
port_mapping_conflict 718 The port mapping entry specified conflicts with a mapping assigned previously to another client
internal_port_must_match_external 724 Internal and external port value must be the same
only_permanent_leases_supported 725 The NAT implementation only supports permanent lease times on port mappings
remote_host_must_be_wildcard 726 RemoteHost must be a wildcard and cannot be a specific IP addres or DNS name
external_port_must_be_wildcard 727 ExternalPort must be a wildcard and cannot be a specific port

enum error_code_enum

Declared in "libtorrent/gzip.hpp"

name value description
no_error 0 Not an error
invalid_gzip_header 1 the supplied gzip buffer has invalid header
inflated_data_too_large 2 the gzip buffer would inflate to more bytes than the specified maximum size, and was rejected.
data_did_not_terminate 3 available inflate data did not terminate
space_exhausted 4 output space exhausted before completing inflate
invalid_block_type 5 invalid block type (type == 3)
invalid_stored_block_length 6 stored block length did not match one's complement
too_many_length_or_distance_codes 7 dynamic block code description: too many length or distance codes
code_lengths_codes_incomplete 8 dynamic block code description: code lengths codes incomplete
repeat_lengths_with_no_first_length 9 dynamic block code description: repeat lengths with no first length
repeat_more_than_specified_lengths 10 dynamic block code description: repeat more than specified lengths
invalid_literal_length_code_lengths 11 dynamic block code description: invalid literal/length code lengths
invalid_distance_code_lengths 12 dynamic block code description: invalid distance code lengths
invalid_literal_code_in_block 13 invalid literal/length or distance code in fixed or dynamic block
distance_too_far_back_in_block 14 distance is too far back in fixed or dynamic block
unknown_gzip_error 15 an unknown error occurred during gzip inflation
error_code_max 16 the number of error codes

enum error_code_enum

Declared in "libtorrent/bdecode.hpp"

name value description
no_error 0 Not an error
expected_digit 1 expected digit in bencoded string
expected_colon 2 expected colon in bencoded string
unexpected_eof 3 unexpected end of file in bencoded string
expected_value 4 expected value (list, dict, int or string) in bencoded string
depth_exceeded 5 bencoded recursion depth limit exceeded
limit_exceeded 6 bencoded item count limit exceeded
overflow 7 integer overflow
error_code_max 8 the number of error codes

enum socks_error_code

Declared in "libtorrent/socks5_stream.hpp"

name value description
no_error 0  
unsupported_version 1  
unsupported_authentication_method 2  
unsupported_authentication_version 3  
authentication_error 4  
username_required 5  
general_failure 6  
command_not_supported 7  
no_identd 8  
identd_error 9  
num_errors 10  

enum i2p_error_code

Declared in "libtorrent/i2p_stream.hpp"

name value description
no_error 0  
parse_failed 1  
cant_reach_peer 2  
i2p_error 3  
invalid_key 4  
invalid_id 5  
timeout 6  
key_not_found 7  
duplicated_id 8  
num_errors 9  

home

Storage

storage_params

Declared in "libtorrent/storage_defs.hpp"

see default_storage::default_storage()

struct storage_params
{
   storage_params ();

   file_storage const* files;
   file_storage const* mapped_files;
   std::string path;
   file_pool* pool;
   storage_mode_t mode;
   std::vector<boost::uint8_t> const* priorities;
   torrent_info const* info;
};

file_slice

Declared in "libtorrent/file_storage.hpp"

represents a window of a file in a torrent.

The file_index refers to the index of the file (in the torrent_info). To get the path and filename, use file_path() and give the file_index as argument. The offset is the byte offset in the file where the range starts, and size is the number of bytes this range is. The size + offset will never be greater than the file size.

struct file_slice
{
   int file_index;
   boost::int64_t offset;
   boost::int64_t size;
};
file_index
the index of the file
offset
the offset from the start of the file, in bytes
size
the size of the window, in bytes

file_storage

Declared in "libtorrent/file_storage.hpp"

The file_storage class represents a file list and the piece size. Everything necessary to interpret a regular bittorrent storage file structure.

class file_storage
{
   bool is_valid () const;
   void reserve (int num_files);
   void add_file (std::string const& path, boost::int64_t file_size, int file_flags = 0
      , std::time_t mtime = 0, std::string const& symlink_path = "");
   void add_file_borrow (char const* filename, int filename_len
      , std::string const& path, boost::int64_t file_size
      , boost::uint32_t file_flags = 0, char const* filehash = 0
      , boost::int64_t mtime = 0, std::string const& symlink_path = "");
   void rename_file (int index, std::string const& new_filename);
   std::vector<file_slice> map_block (int piece, boost::int64_t offset
      , int size) const;
   peer_request map_file (int file, boost::int64_t offset, int size) const;
   int num_files () const;
   boost::int64_t total_size () const;
   void set_num_pieces (int n);
   int num_pieces () const;
   void set_piece_length (int l);
   int piece_length () const;
   int piece_size (int index) const;
   std::string const& name () const;
   void set_name (std::string const& n);
   void swap (file_storage& ti);
   void unload ();
   bool is_loaded () const;
   void optimize (int pad_file_limit = -1, int alignment = -1
      , bool tail_padding = false);
   sha1_hash hash (int index) const;
   std::string file_name (int index) const;
   boost::int64_t file_offset (int index) const;
   time_t mtime (int index) const;
   bool pad_file_at (int index) const;
   std::string const& symlink (int index) const;
   std::string file_path (int index, std::string const& save_path = "") const;
   boost::int64_t file_size (int index) const;
   boost::uint32_t file_path_hash (int index, std::string const& save_path) const;
   void all_path_hashes (boost::unordered_set<boost::uint32_t>& table) const;
   std::vector<std::string> const& paths () const;
   int file_flags (int index) const;
   bool file_absolute_path (int index) const;
   int file_index_at_offset (boost::int64_t offset) const;
   char const* file_name_ptr (int index) const;
   int file_name_len (int index) const;
   void apply_pointer_offset (ptrdiff_t off);

   enum flags_t
   {
      pad_file,
      attribute_hidden,
      attribute_executable,
      attribute_symlink,
   };

   enum file_flags_t
   {
      flag_pad_file,
      flag_hidden,
      flag_executable,
      flag_symlink,
   };
};

is_valid()

bool is_valid () const;

returns true if the piece length has been initialized on the file_storage. This is typically taken as a proxy of whether the file_storage as a whole is initialized or not.

reserve()

void reserve (int num_files);

allocates space for num_files in the internal file list. This can be used to avoid reallocating the internal file list when the number of files to be added is known up-front.

add_file() add_file_borrow()

void add_file (std::string const& path, boost::int64_t file_size, int file_flags = 0
      , std::time_t mtime = 0, std::string const& symlink_path = "");
void add_file_borrow (char const* filename, int filename_len
      , std::string const& path, boost::int64_t file_size
      , boost::uint32_t file_flags = 0, char const* filehash = 0
      , boost::int64_t mtime = 0, std::string const& symlink_path = "");

Adds a file to the file storage. The add_file_borrow version expects that filename points to a string of filename_len bytes that is the file name (without a path) of the file that's being added. This memory is borrowed, i.e. it is the caller's responsibility to make sure it stays valid throughout the lifetime of this file_storage object or any copy of it. The same thing applies to filehash, which is an optional pointer to a 20 byte binary SHA-1 hash of the file.

if filename is NULL, the filename from path is used and not borrowed. In this case filename_len is ignored.

The path argument is the full path (in the torrent file) to the file to add. Note that this is not supposed to be an absolute path, but it is expected to include the name of the torrent as the first path element.

file_size is the size of the file in bytes.

The file_flags argument sets attributes on the file. The file attributes is an extension and may not work in all bittorrent clients.

For possible file attributes, see file_storage::flags_t.

The mtime argument is optional and can be set to 0. If non-zero, it is the posix time of the last modification time of this file.

symlink_path is the path the file is a symlink to. To make this a symlink you also need to set the file_storage::flag_symlink file flag.

If more files than one are added, certain restrictions to their paths apply. In a multi-file file storage (torrent), all files must share the same root directory.

That is, the first path element of all files must be the same. This shared path element is also set to the name of the torrent. It can be changed by calling set_name.

rename_file()

void rename_file (int index, std::string const& new_filename);

renames the file at index to new_filename. Keep in mind that filenames are expected to be UTF-8 encoded.

map_block()

std::vector<file_slice> map_block (int piece, boost::int64_t offset
      , int size) const;

returns a list of file_slice objects representing the portions of files the specified piece index, byte offset and size range overlaps. this is the inverse mapping of map_file().

Preconditions of this function is that the input range is within the torrents address space. piece may not be negative and

piece * piece_size + offset + size

may not exceed the total size of the torrent.

map_file()

peer_request map_file (int file, boost::int64_t offset, int size) const;

returns a peer_request representing the piece index, byte offset and size the specified file range overlaps. This is the inverse mapping over map_block(). Note that the peer_request return type is meant to hold bittorrent block requests, which may not be larger than 16 kiB. Mapping a range larger than that may return an overflown integer.

num_files()

int num_files () const;

returns the number of files in the file_storage

total_size()

boost::int64_t total_size () const;

returns the total number of bytes all the files in this torrent spans

num_pieces() set_num_pieces()

void set_num_pieces (int n);
int num_pieces () const;

set and get the number of pieces in the torrent

piece_length() set_piece_length()

void set_piece_length (int l);
int piece_length () const;

set and get the size of each piece in this torrent. This size is typically an even power of 2. It doesn't have to be though. It should be divisible by 16 kiB however.

piece_size()

int piece_size (int index) const;

returns the piece size of index. This will be the same as piece_length(), except for the last piece, which may be shorter.

set_name() name()

std::string const& name () const;
void set_name (std::string const& n);

set and get the name of this torrent. For multi-file torrents, this is also the name of the root directory all the files are stored in.

swap()

void swap (file_storage& ti);

swap all content of this with ti.

unload()

void unload ();

deallocates most of the memory used by this instance, leaving it only partially usable

is_loaded()

bool is_loaded () const;

returns true when populated with at least one file

optimize()

void optimize (int pad_file_limit = -1, int alignment = -1
      , bool tail_padding = false);

if pad_file_limit >= 0, files larger than that limit will be padded, default is to not add any padding (-1). The alignment specifies the alignment files should be padded to. This defaults to the piece size (-1) but it may also make sense to set it to 16 kiB, or something divisible by 16 kiB. If pad_file_limit is 0, every file will be padded (except empty ones). tail_padding indicates whether aligned files also are padded at the end to make them end aligned. This is required for mutable torrents, since piece hashes are compared

file_path_hash()

boost::uint32_t file_path_hash (int index, std::string const& save_path) const;

returns the crc32 hash of file_path(index)

all_path_hashes()

void all_path_hashes (boost::unordered_set<boost::uint32_t>& table) const;

this will add the CRC32 hash of all directory entries to the table. No filename will be included, just directories. Every depth of directories are added separately to allow test for collisions with files at all levels. i.e. if one path in the torrent is foo/bar/baz, the CRC32 hashes for foo, foo/bar and foo/bar/baz will be added to the set.

file_flags()

int file_flags (int index) const;

returns a bitmask of flags from file_flags_t that apply to file at index.

file_absolute_path()

bool file_absolute_path (int index) const;

returns true if the file at the specified index has been renamed to have an absolute path, i.e. is not anchored in the save path of the torrent.

file_index_at_offset()

int file_index_at_offset (boost::int64_t offset) const;

returns the index of the file at the given offset in the torrent

file_name_len() file_name_ptr()

char const* file_name_ptr (int index) const;
int file_name_len (int index) const;

low-level function. returns a pointer to the internal storage for the filename. This string may not be null terminated! the file_name_len() function returns the length of the filename.

apply_pointer_offset()

void apply_pointer_offset (ptrdiff_t off);

if the backing buffer changed for this storage, this is the pointer offset to add to any pointers to make them point into the new buffer

enum flags_t

Declared in "libtorrent/file_storage.hpp"

name value description
pad_file 1 the file is a pad file. It's required to contain zeros at it will not be saved to disk. Its purpose is to make the following file start on a piece boundary.
attribute_hidden 2 this file has the hidden attribute set. This is primarily a windows attribute
attribute_executable 4 this file has the executable attribute set.
attribute_symlink 8 this file is a symbolic link. It should have a link target string associated with it.

enum file_flags_t

Declared in "libtorrent/file_storage.hpp"

name value description
flag_pad_file 1 this file is a pad file. The creator of the torrent promises the file is entirely filled with zeros and does not need to be downloaded. The purpose is just to align the next file to either a block or piece boundary.
flag_hidden 2 this file is hidden (sets the hidden attribute on windows)
flag_executable 4 this file is executable (sets the executable bit on posix like systems)
flag_symlink 8 this file is a symlink. The symlink target is specified in a separate field

default_storage_constructor()

Declared in "libtorrent/storage_defs.hpp"

storage_interface* default_storage_constructor (storage_params const&);

the constructor function for the regular file storage. This is the default value for add_torrent_params::storage.

disabled_storage_constructor()

Declared in "libtorrent/storage_defs.hpp"

storage_interface* disabled_storage_constructor (storage_params const&);

the constructor function for the disabled storage. This can be used for testing and benchmarking. It will throw away any data written to it and return garbage for anything read from it.

zero_storage_constructor()

Declared in "libtorrent/storage_defs.hpp"

storage_interface* zero_storage_constructor (storage_params const&);

enum storage_mode_t

Declared in "libtorrent/storage_defs.hpp"

name value description
storage_mode_allocate 0 All pieces will be written to their final position, all files will be allocated in full when the torrent is first started. This is done with fallocate() and similar calls. This mode minimizes fragmentation.
storage_mode_sparse 1 All pieces will be written to the place where they belong and sparse files will be used. This is the recommended, and default mode.

home

Custom Storage

libtorrent provides a customization point for storage of data. By default, (default_storage) downloaded files are saved to disk according with the general conventions of bittorrent clients, mimicing the original file layout when the torrent was created. The libtorrent user may define a custom storage to store piece data in a different way.

A custom storage implementation must derive from and implement the storage_interface. You must also provide a function that constructs the custom storage object and provide this function to the add_torrent() call via add_torrent_params. Either passed in to the constructor or by setting the add_torrent_params::storage field.

This is an example storage implementation that stores all pieces in a std::map, i.e. in RAM. It's not necessarily very useful in practice, but illustrates the basics of implementing a custom storage.

struct temp_storage : storage_interface
{
        temp_storage(file_storage const& fs) : m_files(fs) {}
        virtual bool initialize(storage_error& se) { return false; }
        virtual bool has_any_file() { return false; }
        virtual int read(char* buf, int piece, int offset, int size)
        {
                std::map<int, std::vector<char> >::const_iterator i = m_file_data.find(piece);
                if (i == m_file_data.end()) return 0;
                int available = i->second.size() - offset;
                if (available <= 0) return 0;
                if (available > size) available = size;
                memcpy(buf, &i->second[offset], available);
                return available;
        }
        virtual int write(const char* buf, int piece, int offset, int size)
        {
                std::vector<char>& data = m_file_data[piece];
                if (data.size() < offset + size) data.resize(offset + size);
                std::memcpy(&data[offset], buf, size);
                return size;
        }
        virtual bool rename_file(int file, std::string const& new_name)
        { assert(false); return false; }
        virtual bool move_storage(std::string const& save_path) { return false; }
        virtual bool verify_resume_data(bdecode_node const& rd
                , std::vector<std::string> const* links
                , storage_error& error) { return false; }
        virtual bool write_resume_data(entry& rd) const { return false; }
        virtual boost::int64_t physical_offset(int piece, int offset)
        { return piece * m_files.piece_length() + offset; };
        virtual sha1_hash hash_for_slot(int piece, partial_hash& ph, int piece_size)
        {
                int left = piece_size - ph.offset;
                assert(left >= 0);
                if (left > 0)
                {
                        std::vector<char>& data = m_file_data[piece];
                        // if there are padding files, those blocks will be considered
                        // completed even though they haven't been written to the storage.
                        // in this case, just extend the piece buffer to its full size
                        // and fill it with zeros.
                        if (data.size() < piece_size) data.resize(piece_size, 0);
                        ph.h.update(&data[ph.offset], left);
                }
                return ph.h.final();
        }
        virtual bool release_files() { return false; }
        virtual bool delete_files() { return false; }

        std::map<int, std::vector<char> > m_file_data;
        file_storage m_files;
};

storage_interface* temp_storage_constructor(storage_params const& params)
{
        return new temp_storage(*params.files);
}

storage_interface

Declared in "libtorrent/storage.hpp"

The storage interface is a pure virtual class that can be implemented to customize how and where data for a torrent is stored. The default storage implementation uses regular files in the filesystem, mapping the files in the torrent in the way one would assume a torrent is saved to disk. Implementing your own storage interface makes it possible to store all data in RAM, or in some optimized order on disk (the order the pieces are received for instance), or saving multi file torrents in a single file in order to be able to take advantage of optimized disk-I/O.

It is also possible to write a thin class that uses the default storage but modifies some particular behavior, for instance encrypting the data before it's written to disk, and decrypting it when it's read again.

The storage interface is based on pieces. Avery read and write operation happens in the piece-space. Each piece fits 'piece_size' number of bytes. All access is done by writing and reading whole or partial pieces.

libtorrent comes with two built-in storage implementations; default_storage and disabled_storage. Their constructor functions are called default_storage_constructor() and disabled_storage_constructor respectively. The disabled storage does just what it sounds like. It throws away data that's written, and it reads garbage. It's useful mostly for benchmarking and profiling purpose.

struct storage_interface
{
   virtual void initialize (storage_error& ec) = 0;
   virtual int writev (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) = 0;
   virtual int readv (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) = 0;
   virtual bool has_any_file (storage_error& ec) = 0;
   virtual void set_file_priority (std::vector<boost::uint8_t>& prio
      , storage_error& ec) = 0;
   virtual int move_storage (std::string const& save_path, int flags
      , storage_error& ec) = 0;
   virtual bool verify_resume_data (bdecode_node const& rd
      , std::vector<std::string> const* links
      , storage_error& ec) = 0;
   virtual void write_resume_data (entry& rd, storage_error& ec) const = 0;
   virtual void release_files (storage_error& ec) = 0;
   virtual void rename_file (int index, std::string const& new_filename
      , storage_error& ec) = 0;
   virtual void delete_files (int options, storage_error& ec) = 0;
   virtual bool tick ();
   aux::session_settings const& settings () const;

   aux::session_settings* m_settings;
};

initialize()

virtual void initialize (storage_error& ec) = 0;

This function is called when the storage on disk is to be initialized. The default storage will create directories and empty files at this point. If allocate_files is true, it will also ftruncate all files to their target size.

This function may be called multiple time on a single instance. When a torrent is force-rechecked, the storage is re-initialized to trigger the re-check from scratch.

The function is not necessarily called before other member functions. For instance has_any_files() and verify_resume_data() are called early to determine whether we may have to check all files or not. If we're doing a full check of the files every piece will be hashed, causing readv() to be called as well.

Any required internals that need initialization should be done in the constructor. This function is called before the torrent starts to download.

If an error occurs, storage_error should be set to reflect it.

writev() readv()

virtual int writev (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) = 0;
virtual int readv (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) = 0;

These functions should read and write the data in or to the given piece at the given offset. It should read or write num_bufs buffers sequentially, where the size of each buffer is specified in the buffer array bufs. The file::iovec_t type has the following members:

struct iovec_t { void* iov_base; size_t iov_len; };

These functions may be called simultaneously from multiple threads. Make sure they are thread safe. The file in libtorrent is thread safe when it can fall back to pread, preadv or the windows equivalents. On targets where read operations cannot be thread safe (i.e one has to seek first and then read), only one disk thread is used.

Every buffer in bufs can be assumed to be page aligned and be of a page aligned size, except for the last buffer of the torrent. The allocated buffer can be assumed to fit a fully page aligned number of bytes though. This is useful when reading and writing the last piece of a file in unbuffered mode.

The offset is aligned to 16 kiB boundaries most of the time, but there are rare exceptions when it's not. Specifically if the read cache is disabled/or full and a peer requests unaligned data. Most clients request aligned data.

The number of bytes read or written should be returned, or -1 on error. If there's an error, the storage_error must be filled out to represent the error that occurred.

has_any_file()

virtual bool has_any_file (storage_error& ec) = 0;

This function is called when first checking (or re-checking) the storage for a torrent. It should return true if any of the files that is used in this storage exists on disk. If so, the storage will be checked for existing pieces before starting the download.

If an error occurs, storage_error should be set to reflect it.

set_file_priority()

virtual void set_file_priority (std::vector<boost::uint8_t>& prio
      , storage_error& ec) = 0;

change the priorities of files. This is a fenced job and is guaranteed to be the only running function on this storage when called

move_storage()

virtual int move_storage (std::string const& save_path, int flags
      , storage_error& ec) = 0;

This function should move all the files belonging to the storage to the new save_path. The default storage moves the single file or the directory of the torrent.

Before moving the files, any open file handles may have to be closed, like release_files().

If an error occurs, storage_error should be set to reflect it.

returns one of: | no_error = 0 | fatal_disk_error = -1 | need_full_check = -2 | file_exist = -4

verify_resume_data()

virtual bool verify_resume_data (bdecode_node const& rd
      , std::vector<std::string> const* links
      , storage_error& ec) = 0;

This function should verify the resume data rd with the files on disk. If the resume data seems to be up-to-date, return true. If not, set error to a description of what mismatched and return false.

The default storage may compare file sizes and time stamps of the files.

If an error occurs, storage_error should be set to reflect it.

This function should verify the resume data rd with the files on disk. If the resume data seems to be up-to-date, return true. If not, set error to a description of what mismatched and return false.

If the links pointer is non-null, it has the same number of elements as there are files. Each element is either empty or contains the absolute path to a file identical to the corresponding file in this torrent. The storage must create hard links (or copy) those files. If any file does not exist or is inaccessible, the disk job must fail.

write_resume_data()

virtual void write_resume_data (entry& rd, storage_error& ec) const = 0;

This function should fill in resume data, the current state of the storage, in rd. The default storage adds file timestamps and sizes.

Returning true indicates an error occurred.

If an error occurs, storage_error should be set to reflect it.

release_files()

virtual void release_files (storage_error& ec) = 0;

This function should release all the file handles that it keeps open to files belonging to this storage. The default implementation just calls file_pool::release_files().

If an error occurs, storage_error should be set to reflect it.

rename_file()

virtual void rename_file (int index, std::string const& new_filename
      , storage_error& ec) = 0;

Rename the file with index file to name new_name.

If an error occurs, storage_error should be set to reflect it.

delete_files()

virtual void delete_files (int options, storage_error& ec) = 0;

This function should delete some or all of the storage for this torrent. The options parameter specifies whether to delete all files or just the partfile. options are set to the same value as the options passed to session::remove_torrent().

If an error occurs, storage_error should be set to reflect it.

The disk_buffer_pool is used to allocate and free disk buffers. It has the following members:

struct disk_buffer_pool : boost::noncopyable
{
        char* allocate_buffer(char const* category);
        void free_buffer(char* buf);

        char* allocate_buffers(int blocks, char const* category);
        void free_buffers(char* buf, int blocks);

        int block_size() const { return m_block_size; }

        void release_memory();
};

tick()

virtual bool tick ();

called periodically (useful for deferred flushing). When returning false, it means no more ticks are necessary. Any disk job submitted will re-enable ticking. The default will always turn ticking back off again.

settings()

aux::session_settings const& settings () const;

access global session_settings

m_settings
initialized in disk_io_thread::perform_async_job

default_storage

Declared in "libtorrent/storage.hpp"

The default implementation of storage_interface. Behaves as a normal bittorrent client. It is possible to derive from this class in order to override some of its behavior, when implementing a custom storage.

class default_storage : public storage_interface, boost::noncopyable
{
   default_storage (storage_params const& params);
   virtual int move_storage (std::string const& save_path, int flags
      , storage_error& ec) override;
   virtual bool verify_resume_data (bdecode_node const& rd
      , std::vector<std::string> const* links
      , storage_error& error) override;
   virtual void initialize (storage_error& ec) override;
   virtual bool tick () override;
   virtual void delete_files (int options, storage_error& ec) override;
   virtual void set_file_priority (std::vector<boost::uint8_t>& prio
      , storage_error& ec) override;
   virtual void rename_file (int index, std::string const& new_filename
      , storage_error& ec) override;
   virtual void write_resume_data (entry& rd, storage_error& ec) const override;
   virtual void release_files (storage_error& ec) override;
   virtual bool has_any_file (storage_error& ec) override;
   int readv (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) override;
   int writev (file::iovec_t const* bufs, int num_bufs
      , int piece, int offset, int flags, storage_error& ec) override;
   file_storage const& files () const;
   static void disk_write_access_log (bool enable);
   static bool disk_write_access_log ();
};

default_storage()

default_storage (storage_params const& params);

constructs the default_storage based on the give file_storage (fs). mapped is an optional argument (it may be NULL). If non-NULL it represents the file mapping that have been made to the torrent before adding it. That's where files are supposed to be saved and looked for on disk. save_path is the root save folder for this torrent. file_pool is the cache of file handles that the storage will use. All files it opens will ask the file_pool to open them. file_prio is a vector indicating the priority of files on startup. It may be an empty vector. Any file whose index is not represented by the vector (because the vector is too short) are assumed to have priority 1. this is used to treat files with priority 0 slightly differently.

files()

file_storage const& files () const;

if the files in this storage are mapped, returns the mapped file_storage, otherwise returns the original file_storage object.

file_pool

Declared in "libtorrent/file_pool.hpp"

this is an internal cache of open file handles. It's primarily used by storage_interface implementations. It provides semi weak guarantees of not opening more file handles than specified. Given multiple threads, each with the ability to lock a file handle (via smart pointer), there may be windows where more file handles are open.

struct file_pool : boost::noncopyable
{
   ~file_pool ();
   file_pool (int size = 40);
   file_handle open_file (void* st, std::string const& p
      , int file_index, file_storage const& fs, int m, error_code& ec);
   void release (void* st = NULL);
   void release (void* st, int file_index);
   void resize (int size);
   int size_limit () const;
   void close_oldest ();
};

~file_pool() file_pool()

~file_pool ();
file_pool (int size = 40);

size specifies the number of allowed files handles to hold open at any given time.

open_file()

file_handle open_file (void* st, std::string const& p
      , int file_index, file_storage const& fs, int m, error_code& ec);

return an open file handle to file at file_index in the file_storage fs opened at save path p. m is the file open mode (see file::open_mode_t).

release()

void release (void* st = NULL);
void release (void* st, int file_index);

release all files belonging to the specified storage_interface (st) the overload that takes file_index releases only the file with that index in storage st.

resize()

void resize (int size);

update the allowed number of open file handles to size.

size_limit()

int size_limit () const;

returns the current limit of number of allowed open file handles held by the file_pool.

close_oldest()

void close_oldest ();

close the file that was opened least recently (i.e. not accessed least recently). The purpose is to make the OS (really just windows) clear and flush its disk cache associated with this file. We don't want any file to stay open for too long, allowing the disk cache to accrue.

disk_buffer_holder

Declared in "libtorrent/disk_buffer_holder.hpp"

The disk buffer holder acts like a scoped_ptr that frees a disk buffer when it's destructed, unless it's released. release returns the disk buffer and transfers ownership and responsibility to free it to the caller.

A disk buffer is freed by passing it to session_impl::free_disk_buffer().

get() returns the pointer without transferring responsibility. If this buffer has been released, buffer() will return 0.

struct disk_buffer_holder
{
   disk_buffer_holder (buffer_allocator_interface& alloc, disk_io_job const& j);
   ~disk_buffer_holder ();
   char* release ();
   char* get () const;
   void reset (char* buf = 0);
   void reset (disk_io_job const& j);
   void swap (disk_buffer_holder& h);
   block_cache_reference ref () const;
};

disk_buffer_holder()

disk_buffer_holder (buffer_allocator_interface& alloc, disk_io_job const& j);

construct a buffer holder that will free the held buffer using a disk buffer pool directly (there's only one disk_buffer_pool per session)

~disk_buffer_holder()

~disk_buffer_holder ();

frees any unreleased disk buffer held by this object

release()

char* release ();

return the held disk buffer and clear it from the holder. The responsibility to free it is passed on to the caller

get()

char* get () const;

return a pointer to the held buffer

reset()

void reset (char* buf = 0);
void reset (disk_io_job const& j);

set the holder object to hold the specified buffer (or NULL by default). If it's already holding a disk buffer, it will first be freed.

swap()

void swap (disk_buffer_holder& h);

swap pointers of two disk buffer holders.

enum move_flags_t

Declared in "libtorrent/storage.hpp"

name value description
always_replace_files 0 replace any files in the destination when copying or moving the storage
fail_if_exist 1 if any files that we want to copy exist in the destination exist, fail the whole operation and don't perform any copy or move. There is an inherent race condition in this mode. The files are checked for existence before the operation starts. In between the check and performing the copy, the destination files may be created, in which case they are replaced.
dont_replace 2 if any file exist in the target, take those files instead of the ones we may have in the source.

home

Utility

sha1_hash

Declared in "libtorrent/sha1_hash.hpp"

This type holds a SHA-1 digest or any other kind of 20 byte sequence. It implements a number of convenience functions, such as bit operations, comparison operators etc.

In libtorrent it is primarily used to hold info-hashes, piece-hashes, peer IDs, node IDs etc.

class sha1_hash
{
   sha1_hash ();
   static sha1_hash max ();
   static sha1_hash min ();
   explicit sha1_hash (char const* s);
   void assign (char const* str);
   explicit sha1_hash (std::string const& s);
   void assign (std::string const& s);
   char* data ();
   char const* data () const;
   void clear ();
   bool is_all_zeros () const;
   sha1_hash& operator<<= (int n);
   sha1_hash& operator>>= (int n);
   bool operator!= (sha1_hash const& n) const;
   bool operator< (sha1_hash const& n) const;
   bool operator== (sha1_hash const& n) const;
   sha1_hash operator~ () const;
   sha1_hash operator^ (sha1_hash const& n) const;
   sha1_hash& operator^= (sha1_hash const& n);
   sha1_hash operator& (sha1_hash const& n) const;
   sha1_hash& operator&= (sha1_hash const& n);
   sha1_hash& operator|= (sha1_hash const& n);
   boost::uint8_t& operator[] (int i);
   boost::uint8_t const& operator[] (int i) const;
   const_iterator begin () const;
   iterator begin ();
   iterator end ();
   const_iterator end () const;
   std::string to_string () const;
};

sha1_hash()

sha1_hash ();

constructs an all-zero sha1-hash

max()

static sha1_hash max ();

returns an all-F sha1-hash. i.e. the maximum value representable by a 160 bit number (20 bytes). This is a static member function.

min()

static sha1_hash min ();

returns an all-zero sha1-hash. i.e. the minimum value representable by a 160 bit number (20 bytes). This is a static member function.

assign() sha1_hash()

explicit sha1_hash (char const* s);
void assign (char const* str);
explicit sha1_hash (std::string const& s);
void assign (std::string const& s);

copies 20 bytes from the pointer provided, into the sha1-hash. The passed in string MUST be at least 20 bytes. NULL terminators are ignored, s is treated like a raw memory buffer.

clear()

void clear ();

set the sha1-hash to all zeros.

is_all_zeros()

bool is_all_zeros () const;

return true if the sha1-hash is all zero.

operator<<=()

sha1_hash& operator<<= (int n);

shift left n bits.

operator>>=()

sha1_hash& operator>>= (int n);

shift r n bits.

operator!=() operator<() operator==()

bool operator!= (sha1_hash const& n) const;
bool operator< (sha1_hash const& n) const;
bool operator== (sha1_hash const& n) const;

standard comparison operators

operator~()

sha1_hash operator~ () const;

returns a bit-wise negated copy of the sha1-hash

operator^()

sha1_hash operator^ (sha1_hash const& n) const;

returns the bit-wise XOR of the two sha1-hashes.

operator^=()

sha1_hash& operator^= (sha1_hash const& n);

in-place bit-wise XOR with the passed in sha1_hash.

operator&()

sha1_hash operator& (sha1_hash const& n) const;

returns the bit-wise AND of the two sha1-hashes.

operator&=()

sha1_hash& operator&= (sha1_hash const& n);

in-place bit-wise AND of the passed in sha1_hash

operator|=()

sha1_hash& operator|= (sha1_hash const& n);

in-place bit-wise OR of the two sha1-hash.

operator[]()

boost::uint8_t& operator[] (int i);
boost::uint8_t const& operator[] (int i) const;

accessors for specific bytes

begin() end()

const_iterator begin () const;
iterator begin ();
iterator end ();
const_iterator end () const;

start and end iterators for the hash. The value type of these iterators is boost::uint8_t.

to_string()

std::string to_string () const;

return a copy of the 20 bytes representing the sha1-hash as a std::string. It's still a binary string with 20 binary characters.

bitfield

Declared in "libtorrent/bitfield.hpp"

The bitfield type stores any number of bits as a bitfield in a heap allocated array.

struct bitfield
{
   bitfield (int bits);
   bitfield (bitfield const& rhs);
   bitfield (bitfield&& rhs);
   bitfield (int bits, bool val);
   bitfield ();
   bitfield (char const* b, int bits);
   void assign (char const* b, int bits);
   bool operator[] (int index) const;
   bool get_bit (int index) const;
   void clear_bit (int index);
   void set_bit (int index);
   bool all_set () const;
   bool none_set () const;
   int size () const;
   int num_words () const;
   bool empty () const;
   char const* data () const;
   bitfield& operator= (bitfield const& rhs);
   int count () const;
};

bitfield()

bitfield (int bits);
bitfield (bitfield const& rhs);
bitfield (bitfield&& rhs);
bitfield (int bits, bool val);
bitfield ();
bitfield (char const* b, int bits);

constructs a new bitfield. The default constructor creates an empty bitfield. bits is the size of the bitfield (specified in bits). val is the value to initialize the bits to. If not specified all bits are initialized to 0.

The constructor taking a pointer b and bits copies a bitfield from the specified buffer, and bits number of bits (rounded up to the nearest byte boundary).

assign()

void assign (char const* b, int bits);

copy bitfield from buffer b of bits number of bits, rounded up to the nearest byte boundary.

operator[]()

bool operator[] (int index) const;

query bit at index. Returns true if bit is 1, otherwise false.

set_bit() clear_bit()

void clear_bit (int index);
void set_bit (int index);

set bit at index to 0 (clear_bit) or 1 (set_bit).

all_set()

bool all_set () const;

returns true if all bits in the bitfield are set

size()

int size () const;

returns the size of the bitfield in bits.

empty()

bool empty () const;

returns true if the bitfield has zero size.

data()

char const* data () const;

returns a pointer to the internal buffer of the bitfield.

operator=()

bitfield& operator= (bitfield const& rhs);

copy operator

count()

int count () const;

count the number of bits in the bitfield that are set to 1.

hasher

Declared in "libtorrent/hasher.hpp"

this is a SHA-1 hash class.

You use it by first instantiating it, then call update() to feed it with data. i.e. you don't have to keep the entire buffer of which you want to create the hash in memory. You can feed the hasher parts of it at a time. When You have fed the hasher with all the data, you call final() and it will return the sha1-hash of the data.

The constructor that takes a char const* and an integer will construct the sha1 context and feed it the data passed in.

If you want to reuse the hasher object once you have created a hash, you have to call reset() to reinitialize it.

The sha1-algorithm used was implemented by Steve Reid and released as public domain. For more info, see src/sha1.cpp.

class hasher
{
   hasher ();
   hasher (const char* data, int len);
   hasher& operator= (hasher const& h);
   hasher (hasher const& h);
   hasher& update (std::string const& data);
   hasher& update (const char* data, int len);
   sha1_hash final ();
   void reset ();
   ~hasher ();
};

hasher()

hasher (const char* data, int len);

this is the same as default constructing followed by a call to update(data, len).

update()

hasher& update (std::string const& data);
hasher& update (const char* data, int len);

append the following bytes to what is being hashed

final()

sha1_hash final ();

returns the SHA-1 digest of the buffers previously passed to update() and the hasher constructor.

reset()

void reset ();

restore the hasher state to be as if the hasher has just been default constructed.

hash_value()

Declared in "libtorrent/sha1_hash.hpp"

inline std::size_t hash_value (sha1_hash const& b);

operator<<()

Declared in "libtorrent/sha1_hash.hpp"

inline std::ostream& operator<< (std::ostream& os, sha1_hash const& peer);

print a sha1_hash object to an ostream as 40 hexadecimal digits

operator>>()

Declared in "libtorrent/sha1_hash.hpp"

inline std::istream& operator>> (std::istream& is, sha1_hash& peer);

read 40 hexadecimal digits from an istream into a sha1_hash

home

Bencoding

Bencoding is a common representation in bittorrent used for for dictionary, list, int and string hierarchies. It's used to encode .torrent files and some messages in the network protocol. libtorrent also uses it to store settings, resume data and other state between sessions.

Strings in bencoded structures are not necessarily representing text. Strings are raw byte buffers of a certain length. If a string is meant to be interpreted as text, it is required to be UTF-8 encoded. See BEP 3.

There are two mechanims to decode bencoded buffers in libtorrent.

The most flexible one is bdecode() bencode(), which returns a structure represented by entry. Oncea buffer has been decoded with this function, it can be discarded. The entry does not contain any references back to it. This means that bdecode() copies all the data out of the buffer and into its own hierarchy. This makes this function expensive, which might matter if you're parsing large amounts of data.

Another consideration is that bdecode() bencode() is a recursive parser. For this reason, in order to avoid DoS attacks by triggering a stack overflow, there is a recursion limit. This limit is a sanity check to make sure it doesn't run the risk of busting the stack.

The second mechanism is the decode function for bdecode_node. This function builds a tree that points back into the original buffer. The returned bdecode_node will not be valid once the buffer it was parsed out of is discarded.

Not only is this function more efficient because of less memory allocation and data copy, the parser is also not recursive, which means it probably performs a little bit better and can have a higher recursion limit on the structures it's parsing.

entry

Declared in "libtorrent/entry.hpp"

The entry class represents one node in a bencoded hierarchy. It works as a variant type, it can be either a list, a dictionary (std::map), an integer or a string.

class entry
{
   data_type type () const;
   entry (list_type const&);
   entry (integer_type const&);
   entry (dictionary_type const&);
   entry (string_type const&);
   entry (preformatted_type const&);
   entry (data_type t);
   void operator= (string_type const&);
   void operator= (integer_type const&);
   void operator= (entry const&);
   void operator= (dictionary_type const&);
   void operator= (list_type const&);
   void operator= (preformatted_type const&);
   void operator= (bdecode_node const&);
   preformatted_type& preformatted ();
   const integer_type& integer () const;
   const string_type& string () const;
   const preformatted_type& preformatted () const;
   const dictionary_type& dict () const;
   string_type& string ();
   list_type& list ();
   dictionary_type& dict ();
   integer_type& integer ();
   const list_type& list () const;
   void swap (entry& e);
   entry& operator[] (std::string const& key);
   const entry& operator[] (std::string const& key) const;
   entry& operator[] (char const* key);
   const entry& operator[] (char const* key) const;
   entry const* find_key (char const* key) const;
   entry* find_key (char const* key);
   entry* find_key (std::string const& key);
   entry const* find_key (std::string const& key) const;
   std::string to_string () const;
   std::string to_string (bool single_line) const;

   enum data_type
   {
      int_t,
      string_t,
      list_t,
      dictionary_t,
      undefined_t,
      preformatted_t,
   };

   mutable boost::uint8_t m_type_queried:1;
};

type()

data_type type () const;

returns the concrete type of the entry

entry()

entry (list_type const&);
entry (integer_type const&);
entry (dictionary_type const&);
entry (string_type const&);
entry (preformatted_type const&);

constructors directly from a specific type. The content of the argument is copied into the newly constructed entry

entry()

entry (data_type t);

construct an empty entry of the specified type. see data_type enum.

operator=()

void operator= (string_type const&);
void operator= (integer_type const&);
void operator= (entry const&);
void operator= (dictionary_type const&);
void operator= (list_type const&);
void operator= (preformatted_type const&);
void operator= (bdecode_node const&);

copies the structure of the right hand side into this entry.

string() integer() dict() preformatted() list()

preformatted_type& preformatted ();
const integer_type& integer () const;
const string_type& string () const;
const preformatted_type& preformatted () const;
const dictionary_type& dict () const;
string_type& string ();
list_type& list ();
dictionary_type& dict ();
integer_type& integer ();
const list_type& list () const;

The integer(), string(), list() and dict() functions are accessors that return the respective type. If the entry object isn't of the type you request, the accessor will throw libtorrent_exception (which derives from std::runtime_error). You can ask an entry for its type through the type() function.

If you want to create an entry you give it the type you want it to have in its constructor, and then use one of the non-const accessors to get a reference which you then can assign the value you want it to have.

The typical code to get info from a torrent file will then look like this:

entry torrent_file;
// ...

// throws if this is not a dictionary
entry::dictionary_type const& dict = torrent_file.dict();
entry::dictionary_type::const_iterator i;
i = dict.find("announce");
if (i != dict.end())
{
        std::string tracker_url = i->second.string();
        std::cout << tracker_url << "\n";
}

The following code is equivalent, but a little bit shorter:

entry torrent_file;
// ...

// throws if this is not a dictionary
if (entry* i = torrent_file.find_key("announce"))
{
        std::string tracker_url = i->string();
        std::cout << tracker_url << "\n";
}

To make it easier to extract information from a torrent file, the class torrent_info exists.

swap()

void swap (entry& e);

swaps the content of this with e.

operator[]()

entry& operator[] (std::string const& key);
const entry& operator[] (std::string const& key) const;
entry& operator[] (char const* key);
const entry& operator[] (char const* key) const;

All of these functions requires the entry to be a dictionary, if it isn't they will throw libtorrent::type_error.

The non-const versions of the operator[] will return a reference to either the existing element at the given key or, if there is no element with the given key, a reference to a newly inserted element at that key.

The const version of operator[] will only return a reference to an existing element at the given key. If the key is not found, it will throw libtorrent::type_error.

find_key()

entry const* find_key (char const* key) const;
entry* find_key (char const* key);
entry* find_key (std::string const& key);
entry const* find_key (std::string const& key) const;

These functions requires the entry to be a dictionary, if it isn't they will throw libtorrent::type_error.

They will look for an element at the given key in the dictionary, if the element cannot be found, they will return 0. If an element with the given key is found, the return a pointer to it.

to_string()

std::string to_string () const;
std::string to_string (bool single_line) const;

returns a pretty-printed string representation of the bencoded structure, with JSON-style syntax

enum data_type

Declared in "libtorrent/entry.hpp"

name value description
int_t 0  
string_t 1  
list_t 2  
dictionary_t 3  
undefined_t 4  
preformatted_t 5  
m_type_queried
in debug mode this is set to false by bdecode to indicate that the program has not yet queried the type of this entry, and should not assume that it has a certain type. This is asserted in the accessor functions. This does not apply if exceptions are used.

bdecode() bencode()

Declared in "libtorrent/bencode.hpp"

template<class InIt> entry bdecode (InIt start, InIt end);
template<class OutIt> int bencode (OutIt out, const entry& e);
template<class InIt> entry bdecode (InIt start, InIt end, int& len);

These functions will encode data to bencoded or decode bencoded data.

If possible, bdecode() producing a bdecode_node should be preferred over this function.

The entry class is the internal representation of the bencoded data and it can be used to retrieve information, an entry can also be build by the program and given to bencode() to encode it into the OutIt iterator.

The OutIt and InIt are iterators (InputIterator and OutputIterator respectively). They are templates and are usually instantiated as ostream_iterator, back_insert_iterator or istream_iterator. These functions will assume that the iterator refers to a character (char). So, if you want to encode entry e into a buffer in memory, you can do it like this:

std::vector<char> buffer;
bencode(std::back_inserter(buf), e);

If you want to decode a torrent file from a buffer in memory, you can do it like this:

std::vector<char> buffer;
// ...
entry e = bdecode(buf.begin(), buf.end());

Or, if you have a raw char buffer:

const char* buf;
// ...
entry e = bdecode(buf, buf + data_size);

Now we just need to know how to retrieve information from the entry.

If bdecode() encounters invalid encoded data in the range given to it it will return a default constructed entry object.

operator<<()

Declared in "libtorrent/entry.hpp"

inline std::ostream& operator<< (std::ostream& os, const entry& e);

prints the bencoded structure to the ostream as a JSON-style structure.

home

Alerts

The pop_alerts() function on session is the main interface for retrieving alerts (warnings, messages and errors from libtorrent). If no alerts have been posted by libtorrent pop_alerts() will return an empty list.

By default, only errors are reported. settings_pack::alert_mask can be used to specify which kinds of events should be reported. The alert mask is comprised by bits from the category_t enum.

Every alert belongs to one or more category. There is a cost associated with posting alerts. Only alerts that belong to an enabled category are posted. Setting the alert bitmask to 0 will disable all alerts (except those that are non-discardable). Alerts that are responses to API calls such as save_resume_data() and post_session_stats() are non-discardable and will be posted even if their category is disabled.

There are other alert base classes that some alerts derive from, all the alerts that are generated for a specific torrent are derived from torrent_alert, and tracker events derive from tracker_alert.

Alerts returned by pop_alerts() are only valid until the next call to pop_alerts(). You may not copy an alert object to access it after the next call to pop_alerts(). Internal members of alerts also become invalid once pop_alerts() is called again.

alert

Declared in "libtorrent/alert.hpp"

The alert class is the base class that specific messages are derived from. alert types are not copyable, and cannot be constructed by the client. The pointers returned by libtorrent are short lived (the details are described under session_handle::pop_alerts())

class alert
{
   time_point timestamp () const;
   virtual int type () const = 0;
   virtual char const* what () const = 0;
   virtual std::string message () const = 0;
   virtual int category () const = 0;

   enum category_t
   {
      error_notification,
      peer_notification,
      port_mapping_notification,
      storage_notification,
      tracker_notification,
      debug_notification,
      status_notification,
      progress_notification,
      ip_block_notification,
      performance_warning,
      dht_notification,
      stats_notification,
      session_log_notification,
      torrent_log_notification,
      peer_log_notification,
      incoming_request_notification,
      dht_log_notification,
      dht_operation_notification,
      port_mapping_log_notification,
      picker_log_notification,
      file_progress_notification,
      piece_progress_notification,
      block_progress_notification,
      all_categories,
   };
};

timestamp()

time_point timestamp () const;

a timestamp is automatically created in the constructor

type()

virtual int type () const = 0;

returns an integer that is unique to this alert type. It can be compared against a specific alert by querying a static constant called alert_type in the alert. It can be used to determine the run-time type of an alert* in order to cast to that alert type and access specific members.

e.g:

std::vector<alert*> alerts;
ses.pop_alerts(&alerts);
for (alert* i : alerts) {
        switch (a->type()) {

                case read_piece_alert::alert_type:
                {
                        auto* p = static_cast<read_piece_alert*>(a);
                        if (p->ec) {
                                // read_piece failed
                                break;
                        }
                        // use p
                        break;
                }
                case file_renamed_alert::alert_type:
                {
                        // etc...
                }
        }
}

what()

virtual char const* what () const = 0;

returns a string literal describing the type of the alert. It does not include any information that might be bundled with the alert.

message()

virtual std::string message () const = 0;

generate a string describing the alert and the information bundled with it. This is mainly intended for debug and development use. It is not suitable to use this for applications that may be localized. Instead, handle each alert type individually and extract and render the information from the alert depending on the locale.

category()

virtual int category () const = 0;

returns a bitmask specifying which categories this alert belong to.

enum category_t

Declared in "libtorrent/alert.hpp"

name value description
error_notification 1

Enables alerts that report an error. This includes:

  • tracker errors
  • tracker warnings
  • file errors
  • resume data failures
  • web seed errors
  • .torrent files errors
  • listen socket errors
  • port mapping errors
peer_notification 2 Enables alerts when peers send invalid requests, get banned or snubbed.
port_mapping_notification 4 Enables alerts for port mapping events. For NAT-PMP and UPnP.
storage_notification 8 Enables alerts for events related to the storage. File errors and synchronization events for moving the storage, renaming files etc.
tracker_notification 16 Enables all tracker events. Includes announcing to trackers, receiving responses, warnings and errors.
debug_notification 32 Low level alerts for when peers are connected and disconnected.
status_notification 64 Enables alerts for when a torrent or the session changes state.
progress_notification 128 Alerts for when blocks are requested and completed. Also when pieces are completed.
ip_block_notification 256 Alerts when a peer is blocked by the ip blocker or port blocker.
performance_warning 512 Alerts when some limit is reached that might limit the download or upload rate.
dht_notification 1024 Alerts on events in the DHT node. For incoming searches or bootstrapping being done etc.
stats_notification 2048 If you enable these alerts, you will receive a stats_alert approximately once every second, for every active torrent. These alerts contain all statistics counters for the interval since the lasts stats alert.
session_log_notification 8192 Enables debug logging alerts. These are available unless libtorrent was built with logging disabled (TORRENT_DISABLE_LOGGING). The alerts being posted are log_alert and are session wide.
torrent_log_notification 16384 Enables debug logging alerts for torrents. These are available unless libtorrent was built with logging disabled (TORRENT_DISABLE_LOGGING). The alerts being posted are torrent_log_alert and are torrent wide debug events.
peer_log_notification 32768 Enables debug logging alerts for peers. These are available unless libtorrent was built with logging disabled (TORRENT_DISABLE_LOGGING). The alerts being posted are peer_log_alert and low-level peer events and messages.
incoming_request_notification 65536 enables the incoming_request_alert.
dht_log_notification 131072 enables dht_log_alert, debug logging for the DHT
dht_operation_notification 262144 enable events from pure dht operations not related to torrents
port_mapping_log_notification 524288 enables port mapping log events. This log is useful for debugging the UPnP or NAT-PMP implementation
picker_log_notification 1048576 enables verbose logging from the piece picker.
file_progress_notification 2097152 alerts when files complete downloading
piece_progress_notification 4194304 alerts when pieces complete downloading or fail hash check
block_progress_notification 8388608 alerts on individual blocks being requested, downloading, finished, rejected, time-out and cancelled. This is likely to post alerts at a high rate.
all_categories 2147483647

The full bitmask, representing all available categories.

since the enum is signed, make sure this isn't interpreted as -1. For instance, boost.python does that and fails when assigning it to an unsigned parameter.

torrent_alert

Declared in "libtorrent/alert_types.hpp"

This is a base class for alerts that are associated with a specific torrent. It contains a handle to the torrent.

struct torrent_alert : alert
{
   virtual std::string message () const override;
   char const* torrent_name () const;

   torrent_handle handle;
};

message()

virtual std::string message () const override;

returns the message associated with this alert

handle
The torrent_handle pointing to the torrent this alert is associated with.

peer_alert

Declared in "libtorrent/alert_types.hpp"

The peer alert is a base class for alerts that refer to a specific peer. It includes all the information to identify the peer. i.e. ip and peer-id.

struct peer_alert : torrent_alert
{
   virtual int category () const override;
   virtual std::string message () const override;

   static const int alert_type = 1;
   static const int static_category = alert::peer_notification;
   tcp::endpoint ip;
   peer_id pid;
};
ip
The peer's IP address and port.
pid
the peer ID, if known.

tracker_alert

Declared in "libtorrent/alert_types.hpp"

This is a base class used for alerts that are associated with a specific tracker. It derives from torrent_alert since a tracker is also associated with a specific torrent.

struct tracker_alert : torrent_alert
{
   virtual int category () const override;
   virtual std::string message () const override;
   char const* tracker_url () const;

   static const int alert_type = 2;
   static const int static_category = alert::tracker_notification;
};

tracker_url()

char const* tracker_url () const;

returns a null-terminated string of the tracker's URL

torrent_removed_alert

Declared in "libtorrent/alert_types.hpp"

The torrent_removed_alert is posted whenever a torrent is removed. Since the torrent handle in its base class will always be invalid (since the torrent is already removed) it has the info hash as a member, to identify it. It's posted when the status_notification bit is set in the alert_mask.

Even though the handle member doesn't point to an existing torrent anymore, it is still useful for comparing to other handles, which may also no longer point to existing torrents, but to the same non-existing torrents.

The torrent_handle acts as a weak_ptr, even though its object no longer exists, it can still compare equal to another weak pointer which points to the same non-existent object.

struct torrent_removed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   sha1_hash info_hash;
};

read_piece_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when the asynchronous read operation initiated by a call to torrent_handle::read_piece() is completed. If the read failed, the torrent is paused and an error state is set and the buffer member of the alert is 0. If successful, buffer points to a buffer containing all the data of the piece. piece is the piece index that was read. size is the number of bytes that was read.

If the operation fails, ec will indicate what went wrong.

struct read_piece_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification;
   error_code ec;
   boost::shared_array<char> buffer;
   int piece;
   int size;
};

file_completed_alert

Declared in "libtorrent/alert_types.hpp"

This is posted whenever an individual file completes its download. i.e. All pieces overlapping this file have passed their hash check.

struct file_completed_alert final : torrent_alert
{
   virtual std::string message () const override;

   int index;
};
index
refers to the index of the file that completed.

file_renamed_alert

Declared in "libtorrent/alert_types.hpp"

This is posted as a response to a torrent_handle::rename_file() call, if the rename operation succeeds.

struct file_renamed_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* new_name () const;

   static const int static_category = alert::storage_notification;
   int index;
};
index
refers to the index of the file that was renamed,

file_rename_failed_alert

Declared in "libtorrent/alert_types.hpp"

This is posted as a response to a torrent_handle::rename_file() call, if the rename operation failed.

struct file_rename_failed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification;
   int index;
   error_code error;
};
index error
refers to the index of the file that was supposed to be renamed, error is the error code returned from the filesystem.

performance_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a limit is reached that might have a negative impact on upload or download rate performance.

struct performance_alert final : torrent_alert
{
   virtual std::string message () const override;

   enum performance_warning_t
   {
      outstanding_disk_buffer_limit_reached,
      outstanding_request_limit_reached,
      upload_limit_too_low,
      download_limit_too_low,
      send_buffer_watermark_too_low,
      too_many_optimistic_unchoke_slots,
      too_high_disk_queue_limit,
      aio_limit_reached,
      bittyrant_with_no_uplimit,
      too_few_outgoing_ports,
      too_few_file_descriptors,
      num_warnings,
   };

   static const int static_category = alert::performance_warning;
   performance_warning_t warning_code;
};

enum performance_warning_t

Declared in "libtorrent/alert_types.hpp"

name value description
outstanding_disk_buffer_limit_reached 0 This warning means that the number of bytes queued to be written to disk exceeds the max disk byte queue setting (settings_pack::max_queued_disk_bytes). This might restrict the download rate, by not queuing up enough write jobs to the disk I/O thread. When this alert is posted, peer connections are temporarily stopped from downloading, until the queued disk bytes have fallen below the limit again. Unless your max_queued_disk_bytes setting is already high, you might want to increase it to get better performance.
outstanding_request_limit_reached 1 This is posted when libtorrent would like to send more requests to a peer, but it's limited by settings_pack::max_out_request_queue. The queue length libtorrent is trying to achieve is determined by the download rate and the assumed round-trip-time (settings_pack::request_queue_time). The assumed round-trip-time is not limited to just the network RTT, but also the remote disk access time and message handling time. It defaults to 3 seconds. The target number of outstanding requests is set to fill the bandwidth-delay product (assumed RTT times download rate divided by number of bytes per request). When this alert is posted, there is a risk that the number of outstanding requests is too low and limits the download rate. You might want to increase the max_out_request_queue setting.
upload_limit_too_low 2 This warning is posted when the amount of TCP/IP overhead is greater than the upload rate limit. When this happens, the TCP/IP overhead is caused by a much faster download rate, triggering TCP ACK packets. These packets eat into the rate limit specified to libtorrent. When the overhead traffic is greater than the rate limit, libtorrent will not be able to send any actual payload, such as piece requests. This means the download rate will suffer, and new requests can be sent again. There will be an equilibrium where the download rate, on average, is about 20 times the upload rate limit. If you want to maximize the download rate, increase the upload rate limit above 5% of your download capacity.
download_limit_too_low 3 This is the same warning as upload_limit_too_low but referring to the download limit instead of upload. This suggests that your download rate limit is much lower than your upload capacity. Your upload rate will suffer. To maximize upload rate, make sure your download rate limit is above 5% of your upload capacity.
send_buffer_watermark_too_low 4

We're stalled on the disk. We want to write to the socket, and we can write but our send buffer is empty, waiting to be refilled from the disk. This either means the disk is slower than the network connection or that our send buffer watermark is too small, because we can send it all before the disk gets back to us. The number of bytes that we keep outstanding, requested from the disk, is calculated as follows:

min(512, max(upload_rate * send_buffer_watermark_factor / 100, send_buffer_watermark))

If you receive this alert, you might want to either increase your send_buffer_watermark or send_buffer_watermark_factor.

too_many_optimistic_unchoke_slots 5 If the half (or more) of all upload slots are set as optimistic unchoke slots, this warning is issued. You probably want more regular (rate based) unchoke slots.
too_high_disk_queue_limit 6 If the disk write queue ever grows larger than half of the cache size, this warning is posted. The disk write queue eats into the total disk cache and leaves very little left for the actual cache. This causes the disk cache to oscillate in evicting large portions of the cache before allowing peers to download any more, onto the disk write queue. Either lower max_queued_disk_bytes or increase cache_size.
aio_limit_reached 7  
bittyrant_with_no_uplimit 8  
too_few_outgoing_ports 9 This is generated if outgoing peer connections are failing because of address in use errors, indicating that settings_pack::outgoing_ports is set and is too small of a range. Consider not using the outgoing_ports setting at all, or widen the range to include more ports.
too_few_file_descriptors 10  
num_warnings 11  

state_changed_alert

Declared in "libtorrent/alert_types.hpp"

Generated whenever a torrent changes its state.

struct state_changed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   torrent_status::state_t state;
   torrent_status::state_t prev_state;
};
state
the new state of the torrent.
prev_state
the previous state.

tracker_error_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated on tracker time outs, premature disconnects, invalid response or a HTTP response other than "200 OK". From the alert you can get the handle to the torrent the tracker belongs to.

The times_in_row member says how many times in a row this tracker has failed. status_code is the code returned from the HTTP server. 401 means the tracker needs authentication, 404 means not found etc. If the tracker timed out, the code will be set to 0.

struct tracker_error_alert final : tracker_alert
{
   virtual std::string message () const override;
   char const* error_message () const;

   static const int static_category = alert::tracker_notification | alert::error_notification;
   int times_in_row;
   int status_code;
   error_code error;
};

error_message()

char const* error_message () const;

the message associated with this error

tracker_warning_alert

Declared in "libtorrent/alert_types.hpp"

This alert is triggered if the tracker reply contains a warning field. Usually this means that the tracker announce was successful, but the tracker has a message to the client.

struct tracker_warning_alert final : tracker_alert
{
   virtual std::string message () const override;
   char const* warning_message () const;

   static const int static_category = alert::tracker_notification | alert::error_notification;
};

warning_message()

char const* warning_message () const;

the message associated with this warning

scrape_reply_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a scrape request succeeds.

struct scrape_reply_alert final : tracker_alert
{
   virtual std::string message () const override;

   int incomplete;
   int complete;
};
incomplete complete
the data returned in the scrape response. These numbers may be -1 if the response was malformed.

scrape_failed_alert

Declared in "libtorrent/alert_types.hpp"

If a scrape request fails, this alert is generated. This might be due to the tracker timing out, refusing connection or returning an http response code indicating an error.

struct scrape_failed_alert final : tracker_alert
{
   virtual std::string message () const override;
   char const* error_message () const;

   static const int static_category = alert::tracker_notification | alert::error_notification;
   error_code error;
};

error_message()

char const* error_message () const;

if the error indicates there is an associated message, this returns that message. Otherwise and empty string.

error
the error itself. This may indicate that the tracker sent an error message (error::tracker_failure), in which case it can be retrieved by calling error_message().

tracker_reply_alert

Declared in "libtorrent/alert_types.hpp"

This alert is only for informational purpose. It is generated when a tracker announce succeeds. It is generated regardless what kind of tracker was used, be it UDP, HTTP or the DHT.

struct tracker_reply_alert final : tracker_alert
{
   virtual std::string message () const override;

   int num_peers;
};
num_peers
tells how many peers the tracker returned in this response. This is not expected to be greater than the num_want settings. These are not necessarily all new peers, some of them may already be connected.

dht_reply_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated each time the DHT receives peers from a node. num_peers is the number of peers we received in this packet. Typically these packets are received from multiple DHT nodes, and so the alerts are typically generated a few at a time.

struct dht_reply_alert final : tracker_alert
{
   virtual std::string message () const override;

   int num_peers;
};

tracker_announce_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated each time a tracker announce is sent (or attempted to be sent). There are no extra data members in this alert. The url can be found in the base class however.

struct tracker_announce_alert final : tracker_alert
{
   virtual std::string message () const override;

   int event;
};
event

specifies what event was sent to the tracker. It is defined as:

  1. None
  2. Completed
  3. Started
  4. Stopped

hash_failed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a finished piece fails its hash check. You can get the handle to the torrent which got the failed piece and the index of the piece itself from the alert.

struct hash_failed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   int piece_index;
};

peer_ban_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer is banned because it has sent too many corrupt pieces to us. ip is the endpoint to the peer that was banned.

struct peer_ban_alert final : peer_alert
{
   virtual std::string message () const override;
};

peer_unsnubbed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer is unsnubbed. Essentially when it was snubbed for stalling sending data, and now it started sending data again.

struct peer_unsnubbed_alert final : peer_alert
{
   virtual std::string message () const override;
};

peer_snubbed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer is snubbed, when it stops sending data when we request it.

struct peer_snubbed_alert final : peer_alert
{
   virtual std::string message () const override;
};

peer_error_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer sends invalid data over the peer-peer protocol. The peer will be disconnected, but you get its ip address from the alert, to identify it.

struct peer_error_alert final : peer_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::peer_notification;
   int operation;
   error_code error;
};
operation
a NULL-terminated string of the low-level operation that failed, or NULL if there was no low level disk operation.
error
tells you what error caused this alert.

peer_connect_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted every time an outgoing peer connect attempts succeeds.

struct peer_connect_alert final : peer_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::debug_notification;
   int socket_type;
};

peer_disconnected_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer is disconnected for any reason (other than the ones covered by peer_error_alert ).

struct peer_disconnected_alert final : peer_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::debug_notification;
   int socket_type;
   operation_t operation;
   error_code error;
   close_reason_t reason;
};
socket_type
the kind of socket this peer was connected over
operation
the operation or level where the error occurred. Specified as an value from the operation_t enum. Defined in operations.hpp.
error
tells you what error caused peer to disconnect.
reason
the reason the peer disconnected (if specified)

invalid_request_alert

Declared in "libtorrent/alert_types.hpp"

This is a debug alert that is generated by an incoming invalid piece request. ip is the address of the peer and the request is the actual incoming request from the peer. See peer_request for more info.

struct invalid_request_alert final : peer_alert
{
   virtual std::string message () const override;

   peer_request request;
   bool we_have;
   bool peer_interested;
   bool withheld;
};
request
the request we received from the peer
we_have
true if we have this piece
peer_interested
true if the peer indicated that it was interested to download before sending the request
withheld
if this is true, the peer is not allowed to download this piece because of super-seeding rules.

torrent_finished_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a torrent switches from being a downloader to a seed. It will only be generated once per torrent. It contains a torrent_handle to the torrent in question.

struct torrent_finished_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
};

piece_finished_alert

Declared in "libtorrent/alert_types.hpp"

this alert is posted every time a piece completes downloading and passes the hash check. This alert derives from torrent_alert which contains the torrent_handle to the torrent the piece belongs to.

struct piece_finished_alert final : torrent_alert
{
   virtual std::string message () const override;

   int piece_index;
};
piece_index
the index of the piece that finished

request_dropped_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a peer rejects or ignores a piece request.

struct request_dropped_alert final : peer_alert
{
   virtual std::string message () const override;

   int block_index;
   int piece_index;
};

block_timeout_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a block request times out.

struct block_timeout_alert final : peer_alert
{
   virtual std::string message () const override;

   int block_index;
   int piece_index;
};

block_finished_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a block request receives a response.

struct block_finished_alert final : peer_alert
{
   virtual std::string message () const override;

   int block_index;
   int piece_index;
};

block_downloading_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a block request is sent to a peer.

struct block_downloading_alert final : peer_alert
{
   virtual std::string message () const override;

   int block_index;
   int piece_index;
};

unwanted_block_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a block is received that was not requested or whose request timed out.

struct unwanted_block_alert final : peer_alert
{
   virtual std::string message () const override;

   int block_index;
   int piece_index;
};

storage_moved_alert

Declared in "libtorrent/alert_types.hpp"

The storage_moved_alert is generated when all the disk IO has completed and the files have been moved, as an effect of a call to torrent_handle::move_storage. This is useful to synchronize with the actual disk. The storage_path() member return the new path of the storage.

struct storage_moved_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* storage_path () const;

   static const int static_category = alert::storage_notification;
};

storage_path()

char const* storage_path () const;

the path the torrent was moved to

storage_moved_failed_alert

Declared in "libtorrent/alert_types.hpp"

The storage_moved_failed_alert is generated when an attempt to move the storage, via torrent_handle::move_storage(), fails.

struct storage_moved_failed_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* file_path () const;

   static const int static_category = alert::storage_notification;
   error_code error;
   char const* operation;
};

file_path()

char const* file_path () const;

If the error happened for a specific file, this returns its path.

operation
If the error happened in a specific disk operation this is a NULL terminated string naming which one, otherwise it's NULL.

torrent_deleted_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a request to delete the files of a torrent complete.

The info_hash is the info-hash of the torrent that was just deleted. Most of the time the torrent_handle in the torrent_alert will be invalid by the time this alert arrives, since the torrent is being deleted. The info_hash member is hence the main way of identifying which torrent just completed the delete.

This alert is posted in the storage_notification category, and that bit needs to be set in the alert_mask.

struct torrent_deleted_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification;
   sha1_hash info_hash;
};

torrent_delete_failed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a request to delete the files of a torrent fails. Just removing a torrent from the session cannot fail

struct torrent_delete_failed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification
   | alert::error_notification;
   error_code error;
   sha1_hash info_hash;
};
error
tells you why it failed.
info_hash
the info hash of the torrent whose files failed to be deleted

save_resume_data_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated as a response to a torrent_handle::save_resume_data request. It is generated once the disk IO thread is done writing the state for this torrent.

struct save_resume_data_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification;
   boost::shared_ptr<entry> resume_data;
};
resume_data
points to the resume data.

save_resume_data_failed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated instead of save_resume_data_alert if there was an error generating the resume data. error describes what went wrong.

struct save_resume_data_failed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::storage_notification
   | alert::error_notification;
   error_code error;
};
error
the error code from the resume_data failure

torrent_paused_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated as a response to a torrent_handle::pause request. It is generated once all disk IO is complete and the files in the torrent have been closed. This is useful for synchronizing with the disk.

struct torrent_paused_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
};

torrent_resumed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated as a response to a torrent_handle::resume() request. It is generated when a torrent goes from a paused state to an active state.

struct torrent_resumed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
};

torrent_checked_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when a torrent completes checking. i.e. when it transitions out of the checking files state into a state where it is ready to start downloading

struct torrent_checked_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
};

url_seed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a HTTP seed name lookup fails.

struct url_seed_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* server_url () const;
   char const* error_message () const;

   static const int static_category = alert::peer_notification | alert::error_notification;
   error_code error;
};

server_url()

char const* server_url () const;

the URL the error is associated with

error_message()

char const* error_message () const;

in case the web server sent an error message, this function returns it.

error
the error the web seed encountered. If this is not set, the server sent an error message, call error_message().

file_error_alert

Declared in "libtorrent/alert_types.hpp"

If the storage fails to read or write files that it needs access to, this alert is generated and the torrent is paused.

struct file_error_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* filename () const;

   static const int static_category = alert::status_notification
   | alert::storage_notification;
   error_code error;
   char const* operation;
};

filename()

char const* filename () const;

the file that experienced the error

error operation
the error code describing the error.

metadata_failed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when the metadata has been completely received and the info-hash failed to match it. i.e. the metadata that was received was corrupt. libtorrent will automatically retry to fetch it in this case. This is only relevant when running a torrent-less download, with the metadata extension provided by libtorrent.

struct metadata_failed_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::error_notification;
   error_code error;
};
error
indicates what failed when parsing the metadata. This error is what's returned from lazy_bdecode().

metadata_received_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when the metadata has been completely received and the torrent can start downloading. It is not generated on torrents that are started with metadata, but only those that needs to download it from peers (when utilizing the libtorrent extension).

There are no additional data members in this alert.

Typically, when receiving this alert, you would want to save the torrent file in order to load it back up again when the session is restarted. Here's an example snippet of code to do that:

torrent_handle h = alert->handle();
if (h.is_valid()) {
        boost::shared_ptr<torrent_info const> ti = h.torrent_file();
        create_torrent ct(*ti);
        entry te = ct.generate();
        std::vector<char> buffer;
        bencode(std::back_inserter(buffer), te);
        FILE* f = fopen((to_hex(ti->info_hash().to_string()) + ".torrent").c_str(), "wb+");
        if (f) {
                fwrite(&buffer[0], 1, buffer.size(), f);
                fclose(f);
        }
}
struct metadata_received_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
};

udp_error_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when there is an error on the UDP socket. The UDP socket is used for all uTP, DHT and UDP tracker traffic. It's global to the session.

struct udp_error_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::error_notification;
   udp::endpoint endpoint;
   error_code error;
};
endpoint
the source address associated with the error (if any)
error
the error code describing the error

external_ip_alert

Declared in "libtorrent/alert_types.hpp"

Whenever libtorrent learns about the machines external IP, this alert is generated. The external IP address can be acquired from the tracker (if it supports that) or from peers that supports the extension protocol. The address can be accessed through the external_address member.

struct external_ip_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   address external_address;
};
external_address
the IP address that is believed to be our external IP

listen_failed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when none of the ports, given in the port range, to session can be opened for listening. The endpoint member is the interface and port that failed, error is the error code describing the failure.

libtorrent may sometimes try to listen on port 0, if all other ports failed. Port 0 asks the operating system to pick a port that's free). If that fails you may see a listen_failed_alert with port 0 even if you didn't ask to listen on it.

struct listen_failed_alert final : alert
{
   virtual std::string message () const override;
   char const* listen_interface () const;

   enum socket_type_t
   {
      tcp,
      tcp_ssl,
      udp,
      i2p,
      socks5,
      utp_ssl,
   };

   enum op_t
   {
      parse_addr,
      open,
      bind,
      listen,
      get_peer_name,
      accept,
   };

   static const int static_category = alert::status_notification | alert::error_notification;
   error_code error;
   int operation;
   socket_type_t sock_type;
   tcp::endpoint endpoint;
};

listen_interface()

char const* listen_interface () const;

the interface libtorrent attempted to listen on that failed.

enum socket_type_t

Declared in "libtorrent/alert_types.hpp"

name value description
tcp 0  
tcp_ssl 1  
udp 2  
i2p 3  
socks5 4  
utp_ssl 5  

enum op_t

Declared in "libtorrent/alert_types.hpp"

name value description
parse_addr 0  
open 1  
bind 2  
listen 3  
get_peer_name 4  
accept 5  
error
the error the system returned
operation
the specific low level operation that failed. See op_t.
sock_type
the type of listen socket this alert refers to.
endpoint
the address and port libtorrent attempted to listen on

listen_succeeded_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when the listen port succeeds to be opened on a particular interface. endpoint is the endpoint that successfully was opened for listening.

struct listen_succeeded_alert final : alert
{
   virtual std::string message () const override;

   enum socket_type_t
   {
      tcp,
      tcp_ssl,
      udp,
      i2p,
      socks5,
      utp_ssl,
   };

   static const int static_category = alert::status_notification;
   tcp::endpoint endpoint;
   socket_type_t sock_type;
};

enum socket_type_t

Declared in "libtorrent/alert_types.hpp"

name value description
tcp 0  
tcp_ssl 1  
udp 2  
i2p 3  
socks5 4  
utp_ssl 5  
endpoint
the endpoint libtorrent ended up listening on. The address refers to the local interface and the port is the listen port.
sock_type
the type of listen socket this alert refers to.

portmap_error_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a NAT router was successfully found but some part of the port mapping request failed. It contains a text message that may help the user figure out what is wrong. This alert is not generated in case it appears the client is not running on a NAT:ed network or if it appears there is no NAT router that can be remote controlled to add port mappings.

struct portmap_error_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::port_mapping_notification
   | alert::error_notification;
   int mapping;
   int map_type;
   error_code error;
};
mapping
refers to the mapping index of the port map that failed, i.e. the index returned from add_mapping().
map_type
is 0 for NAT-PMP and 1 for UPnP.
error
tells you what failed.

portmap_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a NAT router was successfully found and a port was successfully mapped on it. On a NAT:ed network with a NAT-PMP capable router, this is typically generated once when mapping the TCP port and, if DHT is enabled, when the UDP port is mapped.

struct portmap_alert final : alert
{
   virtual std::string message () const override;

   enum protocol_t
   {
      tcp,
      udp,
   };

   static const int static_category = alert::port_mapping_notification;
   int mapping;
   int external_port;
   int map_type;
   int protocol;
};

enum protocol_t

Declared in "libtorrent/alert_types.hpp"

name value description
tcp 0  
udp 1  
mapping
refers to the mapping index of the port map that failed, i.e. the index returned from add_mapping().
external_port
the external port allocated for the mapping.
map_type
0 for NAT-PMP and 1 for UPnP.
protocol
the protocol this mapping was for. one of protocol_t enums

portmap_log_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated to log informational events related to either UPnP or NAT-PMP. They contain a log line and the type (0 = NAT-PMP and 1 = UPnP). Displaying these messages to an end user is only useful for debugging the UPnP or NAT-PMP implementation. This alert is only posted if the alert::port_mapping_log_notification flag is enabled in the alert mask.

struct portmap_log_alert final : alert
{
   virtual std::string message () const override;
   char const* log_message () const;

   static const int static_category = alert::port_mapping_log_notification;
   int map_type;
};

log_message()

char const* log_message () const;

the message associated with this log line

fastresume_rejected_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a fastresume file has been passed to add_torrent() but the files on disk did not match the fastresume file. The error_code explains the reason why the resume file was rejected.

struct fastresume_rejected_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* file_path () const;

   static const int static_category = alert::status_notification
   | alert::error_notification;
   error_code error;
   char const* operation;
};

file_path()

char const* file_path () const;

If the error happened to a specific file, this returns the path to it.

operation
If the error happened in a disk operation. a NULL-terminated string of the name of that operation. operation is NULL otherwise.

peer_blocked_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when an incoming peer connection, or a peer that's about to be added to our peer list, is blocked for some reason. This could be any of:

  • the IP filter
  • i2p mixed mode restrictions (a normal peer is not allowed on an i2p swarm)
  • the port filter
  • the peer has a low port and no_connect_privileged_ports is enabled
  • the protocol of the peer is blocked (uTP/TCP blocking)
struct peer_blocked_alert final : torrent_alert
{
   virtual std::string message () const override;

   enum reason_t
   {
      ip_filter,
      port_filter,
      i2p_mixed,
      privileged_ports,
      utp_disabled,
      tcp_disabled,
      invalid_local_interface,
   };

   static const int static_category = alert::ip_block_notification;
   address ip;
   int reason;
};

enum reason_t

Declared in "libtorrent/alert_types.hpp"

name value description
ip_filter 0  
port_filter 1  
i2p_mixed 2  
privileged_ports 3  
utp_disabled 4  
tcp_disabled 5  
invalid_local_interface 6  
ip
the address that was blocked.

dht_announce_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a DHT node announces to an info-hash on our DHT node. It belongs to the dht_notification category.

struct dht_announce_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   address ip;
   int port;
   sha1_hash info_hash;
};

dht_get_peers_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when a DHT node sends a get_peers message to our DHT node. It belongs to the dht_notification category.

struct dht_get_peers_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   sha1_hash info_hash;
};

stats_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted approximately once every second, and it contains byte counters of most statistics that's tracked for torrents. Each active torrent posts these alerts regularly. This alert has been superceded by calling post_torrent_updates() regularly on the session object. This alert will be removed

struct stats_alert final : torrent_alert
{
   virtual std::string message () const override;

   enum stats_channel
   {
      upload_payload,
      upload_protocol,
      download_payload,
      download_protocol,
      upload_ip_protocol,
      deprecated1,
      deprecated2,
      download_ip_protocol,
      deprecated3,
      deprecated4,
      num_channels,
   };

   static const int static_category = alert::stats_notification;
   int transferred[num_channels];
   int interval;
};

enum stats_channel

Declared in "libtorrent/alert_types.hpp"

name value description
upload_payload 0  
upload_protocol 1  
download_payload 2  
download_protocol 3  
upload_ip_protocol 4  
deprecated1 5  
deprecated2 6  
download_ip_protocol 7  
deprecated3 8  
deprecated4 9  
num_channels 10  
transferred[num_channels]
an array of samples. The enum describes what each sample is a measurement of. All of these are raw, and not smoothing is performed.
interval
the number of milliseconds during which these stats were collected. This is typically just above 1000, but if CPU is limited, it may be higher than that.

cache_flushed_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when the disk cache has been flushed for a specific torrent as a result of a call to torrent_handle::flush_cache(). This alert belongs to the storage_notification category, which must be enabled to let this alert through. The alert is also posted when removing a torrent from the session, once the outstanding cache flush is complete and the torrent does no longer have any files open.

struct cache_flushed_alert final : torrent_alert
{
   static const int static_category = alert::storage_notification;
};

anonymous_mode_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when a bittorrent feature is blocked because of the anonymous mode. For instance, if the tracker proxy is not set up, no trackers will be used, because trackers can only be used through proxies when in anonymous mode.

struct anonymous_mode_alert final : torrent_alert
{
   virtual std::string message () const override;

   enum kind_t
   {
      tracker_not_anonymous,
   };

   static const int static_category = alert::error_notification;
   int kind;
   std::string str;
};

enum kind_t

Declared in "libtorrent/alert_types.hpp"

name value description
tracker_not_anonymous 0 means that there's no proxy set up for tracker communication and the tracker will not be contacted. The tracker which this failed for is specified in the str member.
kind str
specifies what error this is, see kind_t.

lsd_peer_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when we receive a local service discovery message from a peer for a torrent we're currently participating in.

struct lsd_peer_alert final : peer_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::peer_notification;
};

trackerid_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted whenever a tracker responds with a trackerid. The tracker ID is like a cookie. The libtorrent will store the tracker ID for this tracker and repeat it in subsequent announces.

struct trackerid_alert final : tracker_alert
{
   virtual std::string message () const override;
   char const* tracker_id () const;

   static const int static_category = alert::status_notification;
};

tracker_id()

char const* tracker_id () const;

The tracker ID returned by the tracker

dht_bootstrap_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted when the initial DHT bootstrap is done.

struct dht_bootstrap_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
};

torrent_error_alert

Declared in "libtorrent/alert_types.hpp"

This is posted whenever a torrent is transitioned into the error state.

struct torrent_error_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* filename () const;

   static const int static_category = alert::error_notification | alert::status_notification;
   error_code error;
};

filename()

char const* filename () const;

the filename (or object) the error occurred on.

error
specifies which error the torrent encountered.

torrent_need_cert_alert

Declared in "libtorrent/alert_types.hpp"

This is always posted for SSL torrents. This is a reminder to the client that the torrent won't work unless torrent_handle::set_ssl_certificate() is called with a valid certificate. Valid certificates MUST be signed by the SSL certificate in the .torrent file.

struct torrent_need_cert_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   error_code error;
};

incoming_connection_alert

Declared in "libtorrent/alert_types.hpp"

The incoming connection alert is posted every time we successfully accept an incoming connection, through any mean. The most straight-forward ways of accepting incoming connections are through the TCP listen socket and the UDP listen socket for uTP sockets. However, connections may also be accepted through a Socks5 or i2p listen socket, or via an SSL listen socket.

struct incoming_connection_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::peer_notification;
   int socket_type;
   tcp::endpoint ip;
};
socket_type

tells you what kind of socket the connection was accepted as:

  1. none (no socket instantiated)
  2. TCP
  3. Socks5
  4. HTTP
  5. uTP
  6. i2p
  7. SSL/TCP
  8. SSL/Socks5
  9. HTTPS (SSL/HTTP)
  10. SSL/uTP
ip
is the IP address and port the connection came from.

add_torrent_alert

Declared in "libtorrent/alert_types.hpp"

This alert is always posted when a torrent was attempted to be added and contains the return status of the add operation. The torrent handle of the new torrent can be found in the base class' handle member. If adding the torrent failed, error contains the error code.

struct add_torrent_alert final : torrent_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   add_torrent_params params;
   error_code error;
};
params
a copy of the parameters used when adding the torrent, it can be used to identify which invocation to async_add_torrent() caused this alert.
error
set to the error, if one occurred while adding the torrent.

state_update_alert

Declared in "libtorrent/alert_types.hpp"

This alert is only posted when requested by the user, by calling session::post_torrent_updates() on the session. It contains the torrent status of all torrents that changed since last time this message was posted. Its category is status_notification, but it's not subject to filtering, since it's only manually posted anyway.

struct state_update_alert final : alert
{
   state_update_alert (aux::stack_allocator& alloc
      , std::vector<torrent_status> st);
   virtual std::string message () const override;

   static const int static_category = alert::status_notification;
   std::vector<torrent_status> status;
};
status
contains the torrent status of all torrents that changed since last time this message was posted. Note that you can map a torrent status to a specific torrent via its handle member. The receiving end is suggested to have all torrents sorted by the torrent_handle or hashed by it, for efficient updates.

session_stats_alert

Declared in "libtorrent/alert_types.hpp"

The session_stats_alert is posted when the user requests session statistics by calling post_session_stats() on the session object. Its category is status_notification, but it is not subject to filtering, since it's only manually posted anyway.

struct session_stats_alert final : alert
{
   session_stats_alert (aux::stack_allocator& alloc, counters const& cnt);
   virtual std::string message () const override;

   static const int static_category = alert::stats_notification;
   boost::uint64_t values[counters::num_counters];
};
values[counters

An array are a mix of counters and gauges, which meanings can be queries via the session_stats_metrics() function on the session. The mapping from a specific metric to an index into this array is constant for a specific version of libtorrent, but may differ for other versions. The intended usage is to request the mapping, i.e. call session_stats_metrics(), once on startup, and then use that mapping to interpret these values throughout the process' runtime.

For more information, see the session statistics section.

dht_error_alert

Declared in "libtorrent/alert_types.hpp"

posted when something fails in the DHT. This is not necessarily a fatal error, but it could prevent proper operation

struct dht_error_alert final : alert
{
   virtual std::string message () const override;

   enum op_t
   {
      unknown,
      hostname_lookup,
   };

   static const int static_category = alert::error_notification | alert::dht_notification;
   error_code error;
   op_t operation;
};

enum op_t

Declared in "libtorrent/alert_types.hpp"

name value description
unknown 0  
hostname_lookup 1  
error
the error code
operation
the operation that failed

dht_immutable_item_alert

Declared in "libtorrent/alert_types.hpp"

this alert is posted as a response to a call to session::get_item(), specifically the overload for looking up immutable items in the DHT.

struct dht_immutable_item_alert final : alert
{
   dht_immutable_item_alert (aux::stack_allocator& alloc, sha1_hash const& t
      , entry const& i);
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   sha1_hash target;
   entry item;
};
target
the target hash of the immutable item. This must match the SHA-1 hash of the bencoded form of item.
item
the data for this item

dht_mutable_item_alert

Declared in "libtorrent/alert_types.hpp"

this alert is posted as a response to a call to session::get_item(), specifically the overload for looking up mutable items in the DHT.

struct dht_mutable_item_alert final : alert
{
   dht_mutable_item_alert (aux::stack_allocator& alloc
      , boost::array<char, 32> k
      , boost::array<char, 64> sig
      , boost::uint64_t sequence
      , std::string const& s
      , entry const& i
      , bool a);
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   boost::array<char, 32> key;
   boost::array<char, 64> signature;
   boost::uint64_t seq;
   std::string salt;
   entry item;
   bool authoritative;
};
key
the public key that was looked up
signature
the signature of the data. This is not the signature of the plain encoded form of the item, but it includes the sequence number and possibly the hash as well. See the dht_store document for more information. This is primarily useful for echoing back in a store request.
seq
the sequence number of this item
salt
the salt, if any, used to lookup and store this item. If no salt was used, this is an empty string
item
the data for this item
authoritative
the last response for mutable data is authoritative.

dht_put_alert

Declared in "libtorrent/alert_types.hpp"

this is posted when a DHT put operation completes. This is useful if the client is waiting for a put to complete before shutting down for instance.

struct dht_put_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   sha1_hash target;
   boost::array<char, 32> public_key;
   boost::array<char, 64> signature;
   std::string salt;
   boost::uint64_t seq;
   int num_success;
};
target
the target hash the item was stored under if this was an immutable item.
public_key signature salt seq
if a mutable item was stored, these are the public key, signature, salt and sequence number the item was stored under.
num_success
DHT put operation usually writes item to k nodes, maybe the node is stale so no response, or the node doesn't support 'put', or the token for write is out of date, etc. num_success is the number of successful responses we got from the puts.

i2p_alert

Declared in "libtorrent/alert_types.hpp"

this alert is used to report errors in the i2p SAM connection

struct i2p_alert final : alert
{
   i2p_alert (aux::stack_allocator& alloc, error_code const& ec);
   virtual std::string message () const override;

   static const int static_category = alert::error_notification;
   error_code error;
};
error
the error that occurred in the i2p SAM connection

dht_outgoing_get_peers_alert

Declared in "libtorrent/alert_types.hpp"

This alert is generated when we send a get_peers request It belongs to the dht_notification category.

struct dht_outgoing_get_peers_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::dht_notification;
   sha1_hash info_hash;
   sha1_hash obfuscated_info_hash;
   udp::endpoint ip;
};
info_hash
the info_hash of the torrent we're looking for peers for.
obfuscated_info_hash
if this was an obfuscated lookup, this is the info-hash target actually sent to the node.
ip
the endpoint we're sending this query to

log_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted by some session wide event. Its main purpose is trouble shooting and debugging. It's not enabled by the default alert mask and is enabled by the alert::session_log_notification bit. Furthermore, it's by default disabled as a build configuration.

struct log_alert final : alert
{
   virtual std::string message () const override;
   char const* msg () const;

   static const int static_category = alert::session_log_notification;
};

msg()

char const* msg () const;

returns the log message

torrent_log_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted by torrent wide events. It's meant to be used for trouble shooting and debugging. It's not enabled by the default alert mask and is enabled by the alert::torrent_log_notification bit. By default it is disabled as a build configuration.

struct torrent_log_alert final : torrent_alert
{
   virtual std::string message () const override;
   char const* msg () const;

   static const int static_category = alert::torrent_log_notification;
};

msg()

char const* msg () const;

returns the log message

peer_log_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted by events specific to a peer. It's meant to be used for trouble shooting and debugging. It's not enabled by the default alert mask and is enabled by the alert::peer_log_notification bit. By default it is disabled as a build configuration.

struct peer_log_alert final : peer_alert
{
   virtual std::string message () const override;
   char const* msg () const;

   enum direction_t
   {
      incoming_message,
      outgoing_message,
      incoming,
      outgoing,
      info,
   };

   static const int static_category = alert::peer_log_notification;
   char const* event_type;
   direction_t direction;
};

msg()

char const* msg () const;

returns the log message

enum direction_t

Declared in "libtorrent/alert_types.hpp"

name value description
incoming_message 0  
outgoing_message 1  
incoming 2  
outgoing 3  
info 4  
event_type
string literal indicating the kind of event. For messages, this is the message name.

lsd_error_alert

Declared in "libtorrent/alert_types.hpp"

posted if the local service discovery socket fails to start properly. it's categorized as error_notification.

struct lsd_error_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::error_notification;
   error_code error;
};
error
The error code

dht_lookup

Declared in "libtorrent/alert_types.hpp"

holds statistics about a current dht_lookup operation. a DHT lookup is the traversal of nodes, looking up a set of target nodes in the DHT for retrieving and possibly storing information in the DHT

struct dht_lookup
{
   char const* type;
   int outstanding_requests;
   int timeouts;
   int responses;
   int branch_factor;
   int nodes_left;
   int last_sent;
   int first_timeout;
};
type
string literal indicating which kind of lookup this is
outstanding_requests
the number of outstanding request to individual nodes this lookup has right now
timeouts
the total number of requests that have timed out so far for this lookup
responses
the total number of responses we have received for this lookup so far for this lookup
branch_factor
the branch factor for this lookup. This is the number of nodes we keep outstanding requests to in parallel by default. when nodes time out we may increase this.
nodes_left
the number of nodes left that could be queries for this lookup. Many of these are likely to be part of the trail while performing the lookup and would never end up actually being queried.
last_sent
the number of seconds ago the last message was sent that's still outstanding
first_timeout
the number of outstanding requests that have exceeded the short timeout and are considered timed out in the sense that they increased the branch factor

dht_routing_bucket

Declared in "libtorrent/alert_types.hpp"

struct to hold information about a single DHT routing table bucket

struct dht_routing_bucket
{
   int num_nodes;
   int num_replacements;
   int last_active;
};
num_nodes num_replacements
the total number of nodes and replacement nodes in the routing table
last_active
number of seconds since last activity

dht_stats_alert

Declared in "libtorrent/alert_types.hpp"

contains current DHT state. Posted in response to session::post_dht_stats().

struct dht_stats_alert final : alert
{
   virtual std::string message () const override;

   static const int static_category = alert::stats_notification;
   std::vector<dht_lookup> active_requests;
   std::vector<dht_routing_bucket> routing_table;
};
active_requests
a vector of the currently running DHT lookups.
routing_table
contains information about every bucket in the DHT routing table.

incoming_request_alert

Declared in "libtorrent/alert_types.hpp"

posted every time an incoming request from a peer is accepted and queued up for being serviced. This alert is only posted if the alert::incoming_request_notification flag is enabled in the alert mask.

struct incoming_request_alert final : peer_alert
{
   virtual std::string message () const override;

   static const int static_category = alert::incoming_request_notification;
   peer_request req;
};
req
the request this peer sent to us

dht_log_alert

Declared in "libtorrent/alert_types.hpp"

struct dht_log_alert final : alert
{
   dht_log_alert (aux::stack_allocator& alloc
      , dht_module_t m, char const* msg);
   virtual std::string message () const override;
   char const* log_message () const;

   enum dht_module_t
   {
      tracker,
      node,
      routing_table,
      rpc_manager,
      traversal,
   };

   static const int static_category = alert::dht_log_notification;
   dht_module_t module;
};

log_message()

char const* log_message () const;

the log message

enum dht_module_t

Declared in "libtorrent/alert_types.hpp"

name value description
tracker 0  
node 1  
routing_table 2  
rpc_manager 3  
traversal 4  
module
the module, or part, of the DHT that produced this log message.

dht_pkt_alert

Declared in "libtorrent/alert_types.hpp"

This alert is posted every time a DHT message is sent or received. It is only posted if the alert::dht_log_notification alert category is enabled. It contains a verbatim copy of the message.

struct dht_pkt_alert final : alert
{
   dht_pkt_alert (aux::stack_allocator& alloc, char const* buf, int size
      , dht_pkt_alert::direction_t d, udp::endpoint ep);
   virtual std::string message () const override;
   int pkt_size () const;
   char const* pkt_buf () const;

   enum direction_t
   {
      incoming,
      outgoing,
   };

   static const int static_category = alert::dht_log_notification;
   direction_t dir;
   udp::endpoint node;
};

pkt_size() pkt_buf()

int pkt_size () const;
char const* pkt_buf () const;

returns a pointer to the packet buffer and size of the packet, respectively. This buffer is only valid for as long as the alert itself is valid, which is owned by libtorrent and reclaimed whenever pop_alerts() is called on the session.

enum direction_t

Declared in "libtorrent/alert_types.hpp"

name value description
incoming 0  
outgoing 1  
dir
whether this is an incoming or outgoing packet.
node
the DHT node we received this packet from, or sent this packet to (depending on dir).

dht_get_peers_reply_alert

Declared in "libtorrent/alert_types.hpp"

struct dht_get_peers_reply_alert final : alert
{
   dht_get_peers_reply_alert (aux::stack_allocator& alloc
      , sha1_hash const& ih
      , std::vector<tcp::endpoint> const& v);
   virtual std::string message () const override;
   int num_peers () const;
   std::vector<tcp::endpoint> peers () const;

   static const int static_category = alert::dht_operation_notification;
   sha1_hash info_hash;
};

dht_direct_response_alert

Declared in "libtorrent/alert_types.hpp"

This is posted exactly once for every call to session_handle::dht_direct_request. If the request failed, response() will return a default constructed bdecode_node.

struct dht_direct_response_alert final : alert
{
   dht_direct_response_alert (aux::stack_allocator& alloc, void* userdata
      , udp::endpoint const& addr, bdecode_node const& response);
   virtual std::string message () const override;
   dht_direct_response_alert (aux::stack_allocator& alloc, void* userdata
      , udp::endpoint const& addr);
   bdecode_node response () const;

   static const int static_category = alert::dht_notification;
   void* userdata;
   udp::endpoint addr;
};

dht_direct_response_alert() message()

virtual std::string message () const override;
dht_direct_response_alert (aux::stack_allocator& alloc, void* userdata
      , udp::endpoint const& addr);

for when there was a timeout so we don't have a response

picker_log_alert

Declared in "libtorrent/alert_types.hpp"

this is posted when one or more blocks are picked by the piece picker, assuming the verbose piece picker logging is enabled (see picker_log_notification).

struct picker_log_alert final : peer_alert
{
   virtual std::string message () const override;
   std::vector<piece_block> blocks () const;

   enum picker_flags_t
   {
      partial_ratio,
      prioritize_partials,
      rarest_first_partials,
      rarest_first,
      reverse_rarest_first,
      suggested_pieces,
      prio_sequential_pieces,
      sequential_pieces,
      reverse_pieces,
      time_critical,
      random_pieces,
      prefer_contiguous,
      reverse_sequential,
      backup1,
      backup2,
      end_game,
   };

   static const int static_category = alert::picker_log_notification;
   boost::uint32_t picker_flags;
};

enum picker_flags_t

Declared in "libtorrent/alert_types.hpp"

name value description
partial_ratio 1 the ratio of partial pieces is too high. This forces a preference for picking blocks from partial pieces.
prioritize_partials 2  
rarest_first_partials 4  
rarest_first 8  
reverse_rarest_first 16  
suggested_pieces 32  
prio_sequential_pieces 64  
sequential_pieces 128  
reverse_pieces 256  
time_critical 512  
random_pieces 1024  
prefer_contiguous 2048  
reverse_sequential 4096  
backup1 8192  
backup2 16384  
end_game 32768  
picker_flags
this is a bitmask of which features were enabled for this particular pick. The bits are defined in the picker_flags_t enum.

alert_cast()

Declared in "libtorrent/alert.hpp"

template <class T> T* alert_cast (alert* a);
template <class T> T const* alert_cast (alert const* a);

When you get an alert, you can use alert_cast<> to attempt to cast the pointer to a specific alert type, in order to query it for more information.

Note

alert_cast<> can only cast to an exact alert type, not a base class

operation_name()

Declared in "libtorrent/alert_types.hpp"

char const* operation_name (int op);

maps an operation id (from peer_error_alert and peer_disconnected_alert) to its name. See peer_connection for the constants

enum operation_t

Declared in "libtorrent/operations.hpp"

name value description
op_bittorrent 0 this is used when the bittorrent logic determines to disconnect
op_iocontrol 1 a call to iocontrol failed
op_getpeername 2 a call to getpeername failed (querying the remote IP of a connection)
op_getname 3 a call to getname failed (querying the local IP of a connection)
op_alloc_recvbuf 4 an attempt to allocate a receive buffer failed
op_alloc_sndbuf 5 an attempt to allocate a send buffer failed
op_file_write 6 writing to a file failed
op_file_read 7 reading from a file failed
op_file 8 a non-read and non-write file operation failed
op_sock_write 9 a socket write operation failed
op_sock_read 10 a socket read operation failed
op_sock_open 11 a call to open(), to create a socket socket failed
op_sock_bind 12 a call to bind() on a socket failed
op_available 13 an attempt to query the number of bytes available to read from a socket failed
op_encryption 14 a call related to bittorrent protocol encryption failed
op_connect 15 an attempt to connect a socket failed
op_ssl_handshake 16 establishing an SSL connection failed
op_get_interface 17 a connection failed to satisfy the bind interface setting

enum alert_priority

Declared in "libtorrent/alert_types.hpp"

name value description
alert_priority_normal 0  
alert_priority_high 1  
alert_priority_critical 2  

home

Filter

ip_filter

Declared in "libtorrent/ip_filter.hpp"

The ip_filter class is a set of rules that uniquely categorizes all ip addresses as allowed or disallowed. The default constructor creates a single rule that allows all addresses (0.0.0.0 - 255.255.255.255 for the IPv4 range, and the equivalent range covering all addresses for the IPv6 range).

A default constructed ip_filter does not filter any address.

struct ip_filter
{
   void add_rule (address first, address last, boost::uint32_t flags);
   int access (address const& addr) const;
   filter_tuple_t export_filter () const;

   enum access_flags
   {
      blocked,
   };
};

add_rule()

void add_rule (address first, address last, boost::uint32_t flags);

Adds a rule to the filter. first and last defines a range of ip addresses that will be marked with the given flags. The flags can currently be 0, which means allowed, or ip_filter::blocked, which means disallowed.

precondition: first.is_v4() == last.is_v4() && first.is_v6() == last.is_v6()

postcondition: access(x) == flags for every x in the range [first, last]

This means that in a case of overlapping ranges, the last one applied takes precedence.

access()

int access (address const& addr) const;

Returns the access permissions for the given address (addr). The permission can currently be 0 or ip_filter::blocked. The complexity of this operation is O(log n), where n is the minimum number of non-overlapping ranges to describe the current filter.

export_filter()

filter_tuple_t export_filter () const;

This function will return the current state of the filter in the minimum number of ranges possible. They are sorted from ranges in low addresses to high addresses. Each entry in the returned vector is a range with the access control specified in its flags field.

The return value is a tuple containing two range-lists. One for IPv4 addresses and one for IPv6 addresses.

enum access_flags

Declared in "libtorrent/ip_filter.hpp"

name value description
blocked 1 indicates that IPs in this range should not be connected to nor accepted as incoming connections

port_filter

Declared in "libtorrent/ip_filter.hpp"

the port filter maps non-overlapping port ranges to flags. This is primarily used to indicate whether a range of ports should be connected to or not. The default is to have the full port range (0-65535) set to flag 0.

class port_filter
{
   void add_rule (boost::uint16_t first, boost::uint16_t last, boost::uint32_t flags);
   int access (boost::uint16_t port) const;

   enum access_flags
   {
      blocked,
   };
};

add_rule()

void add_rule (boost::uint16_t first, boost::uint16_t last, boost::uint32_t flags);

set the flags for the specified port range (first, last) to flags overwriting any existing rule for those ports. The range is inclusive, i.e. the port last also has the flag set on it.

access()

int access (boost::uint16_t port) const;

test the specified port (port) for whether it is blocked or not. The returned value is the flags set for this port. see access_flags.

enum access_flags

Declared in "libtorrent/ip_filter.hpp"

name value description
blocked 1 this flag indicates that destination ports in the range should not be connected to

home

Settings

You have some control over session configuration through the session::apply_settings() member function. To change one or more configuration options, create a settings_pack object and fill it with the settings to be set and pass it in to session::apply_settings().

The settings_pack object is a collection of settings updates that are applied to the session when passed to session::apply_settings(). It's empty when constructed.

You have control over proxy and authorization settings and also the user-agent that will be sent to the tracker. The user-agent will also be used to identify the client with other peers.

dht_settings

Declared in "libtorrent/session_settings.hpp"

structure used to hold configuration options for the DHT

The dht_settings struct used to contain a service_port member to control which port the DHT would listen on and send messages from. This field is deprecated and ignored. libtorrent always tries to open the UDP socket on the same port as the TCP socket.

struct dht_settings
{
   dht_settings ();

   int max_peers_reply;
   int search_branching;
   int max_fail_count;
   int max_torrents;
   int max_dht_items;
   int max_peers;
   int max_torrent_search_reply;
   bool restrict_routing_ips;
   bool restrict_search_ips;
   bool extended_routing_table;
   bool aggressive_lookups;
   bool privacy_lookups;
   bool enforce_node_id;
   bool ignore_dark_internet;
   int block_timeout;
   int block_ratelimit;
   bool read_only;
   int item_lifetime;
};

dht_settings()

dht_settings ();

initialized dht_settings to the default values

max_peers_reply
the maximum number of peers to send in a reply to get_peers
search_branching
the number of concurrent search request the node will send when announcing and refreshing the routing table. This parameter is called alpha in the kademlia paper
max_fail_count
the maximum number of failed tries to contact a node before it is removed from the routing table. If there are known working nodes that are ready to replace a failing node, it will be replaced immediately, this limit is only used to clear out nodes that don't have any node that can replace them.
max_torrents
the total number of torrents to track from the DHT. This is simply an upper limit to make sure malicious DHT nodes cannot make us allocate an unbounded amount of memory.
max_dht_items
max number of items the DHT will store
max_peers
the max number of peers to store per torrent (for the DHT)
max_torrent_search_reply
the max number of torrents to return in a torrent search query to the DHT
restrict_routing_ips

determines if the routing table entries should restrict entries to one per IP. This defaults to true, which helps mitigate some attacks on the DHT. It prevents adding multiple nodes with IPs with a very close CIDR distance.

when set, nodes whose IP address that's in the same /24 (or /64 for IPv6) range in the same routing table bucket. This is an attempt to mitigate node ID spoofing attacks also restrict any IP to only have a single entry in the whole routing table

restrict_search_ips
determines if DHT searches should prevent adding nodes with IPs with very close CIDR distance. This also defaults to true and helps mitigate certain attacks on the DHT.
extended_routing_table
makes the first buckets in the DHT routing table fit 128, 64, 32 and 16 nodes respectively, as opposed to the standard size of 8. All other buckets have size 8 still.
aggressive_lookups
slightly changes the lookup behavior in terms of how many outstanding requests we keep. Instead of having branch factor be a hard limit, we always keep branch factor outstanding requests to the closest nodes. i.e. every time we get results back with closer nodes, we query them right away. It lowers the lookup times at the cost of more outstanding queries.
privacy_lookups
when set, perform lookups in a way that is slightly more expensive, but which minimizes the amount of information leaked about you.
enforce_node_id
when set, node's whose IDs that are not correctly generated based on its external IP are ignored. When a query arrives from such node, an error message is returned with a message saying "invalid node ID".
ignore_dark_internet
ignore DHT messages from parts of the internet we wouldn't expect to see any traffic from
block_timeout
the number of seconds a DHT node is banned if it exceeds the rate limit. The rate limit is averaged over 10 seconds to allow for bursts above the limit.
block_ratelimit
the max number of packets per second a DHT node is allowed to send without getting banned.
read_only
when set, the other nodes won't keep this node in their routing tables, it's meant for low-power and/or ephemeral devices that cannot support the DHT, it is also useful for mobile devices which are sensitive to network traffic and battery life. this node no longer responds to 'query' messages, and will place a 'ro' key (value = 1) in the top-level message dictionary of outgoing query messages.
item_lifetime
the number of seconds a immutable/mutable item will be expired. default is 0, means never expires.

settings_pack

Declared in "libtorrent/settings_pack.hpp"

The settings_pack struct, contains the names of all settings as enum values. These values are passed in to the set_str(), set_int(), set_bool() functions, to specify the setting to change.

These are the available settings:

name type default
user_agent string "libtorrent/" LIBTORRENT_VERSION

this is the client identification to the tracker. The recommended format of this string is: "ClientName/ClientVersion libtorrent/libtorrentVersion". This name will not only be used when making HTTP requests, but also when sending extended headers to peers that support that extension. It may not contain r or n

name type default
announce_ip string 0

announce_ip is the ip address passed along to trackers as the &ip= parameter. If left as the default, that parameter is omitted.

name type default
handshake_client_version string 0

this is the client name and version identifier sent to peers in the handshake message. If this is an empty string, the user_agent is used instead

name type default
outgoing_interfaces string ""

sets the network interface this session will use when it opens outgoing connections. By default, it binds outgoing connections to INADDR_ANY and port 0 (i.e. let the OS decide). Ths parameter must be a string containing one or more, comma separated, adapter names. Adapter names on unix systems are of the form "eth0", "eth1", "tun0", etc. When specifying multiple interfaces, they will be assigned in round-robin order. This may be useful for clients that are multi-homed. Binding an outgoing connection to a local IP does not necessarily make the connection via the associated NIC/Adapter. Setting this to an empty string will disable binding of outgoing connections.

name type default
listen_interfaces string "0.0.0.0:6881"

a comma-separated list of IP port-pairs. These are the listen ports that will be opened for accepting incoming uTP and TCP connections. It is possible to listen on multiple IPs and multiple ports. Binding to port 0 will make the operating system pick the port. The default is "0.0.0.0:6881", which binds to all interfaces on port 6881.

If binding fails because the port is busy, the port number will be incremented by one, settings_pack::max_retry_port_bind times.

if all retry attempts fail, the socket will be bound to port 0, meaning the operating system will pick a port. This behavior can be disabled by disabling settings_pack::listen_system_port_fallback.

if binding fails, the listen_failed_alert is posted, potentially more than once. Once/if binding the listen socket(s) succeed, listen_succeeded_alert is posted.

Each port will attempt to open both a UDP and a TCP listen socket, to allow accepting uTP connections as well as TCP. If using the DHT, this will also make the DHT use the same UDP ports.

Note

The current support for opening arbitrary UDP sockets is limited. In this version of libtorrent, there will only ever be two UDP sockets, one for IPv4 and one for IPv6.

name type default
proxy_hostname string ""

when using a poxy, this is the hostname where the proxy is running see proxy_type.

name type default
proxy_username string ""
proxy_password string ""

when using a proxy, these are the credentials (if any) to use whne connecting to it. see proxy_type

name type default
i2p_hostname string ""

sets the i2p SAM bridge to connect to. set the port with the i2p_port setting.

name type default
peer_fingerprint string "-LT11D0-"

this is the fingerprint for the client. It will be used as the prefix to the peer_id. If this is 20 bytes (or longer) it will be truncated to 20 bytes and used as the entire peer-id

There is a utility function, generate_fingerprint() that can be used to generate a standard client peer ID fingerprint prefix.

name type default
dht_bootstrap_nodes string "dht.libtorrent.org:25401"

This is a comma-separated list of IP port-pairs. They will be added to the DHT node (if it's enabled) as back-up nodes in case we don't know of any. This setting will contain one or more bootstrap nodes by default.

Changing these after the DHT has been started may not have any effect until the DHT is restarted.

name type default
allow_multiple_connections_per_ip bool false

determines if connections from the same IP address as existing connections should be rejected or not. Multiple connections from the same IP address is not allowed by default, to prevent abusive behavior by peers. It may be useful to allow such connections in cases where simulations are run on the same machine, and all peers in a swarm has the same IP address.

name type default
send_redundant_have bool true

send_redundant_have controls if have messages will be sent to peers that already have the piece. This is typically not necessary, but it might be necessary for collecting statistics in some cases. Default is false.

name type default
lazy_bitfields bool false

if this is true, outgoing bitfields will never be fuil. If the client is seed, a few bits will be set to 0, and later filled in with have messages. This is to prevent certain ISPs from stopping people from seeding.

name type default
use_dht_as_fallback bool false

use_dht_as_fallback determines how the DHT is used. If this is true, the DHT will only be used for torrents where all trackers in its tracker list has failed. Either by an explicit error message or a time out. This is false by default, which means the DHT is used by default regardless of if the trackers fail or not.

name type default
upnp_ignore_nonrouters bool false

upnp_ignore_nonrouters indicates whether or not the UPnP implementation should ignore any broadcast response from a device whose address is not the configured router for this machine. i.e. it's a way to not talk to other people's routers by mistake.

name type default
use_parole_mode bool true

use_parole_mode specifies if parole mode should be used. Parole mode means that peers that participate in pieces that fail the hash check are put in a mode where they are only allowed to download whole pieces. If the whole piece a peer in parole mode fails the hash check, it is banned. If a peer participates in a piece that passes the hash check, it is taken out of parole mode.

name type default
use_read_cache bool true

enable and disable caching of blocks read from disk. the purpose of the read cache is partly read-ahead of requests but also to avoid reading blocks back from the disk multiple times for popular pieces.

name type default
coalesce_reads bool false
coalesce_writes bool false

allocate separate, contiguous, buffers for read and write calls. Only used where writev/readv cannot be used will use more RAM but may improve performance

name type default
auto_manage_prefer_seeds bool false

prefer seeding torrents when determining which torrents to give active slots to, the default is false which gives preference to downloading torrents

name type default
dont_count_slow_torrents bool true

if dont_count_slow_torrents is true, torrents without any payload transfers are not subject to the active_seeds and active_downloads limits. This is intended to make it more likely to utilize all available bandwidth, and avoid having torrents that don't transfer anything block the active slots.

name type default
close_redundant_connections bool true

close_redundant_connections specifies whether libtorrent should close connections where both ends have no utility in keeping the connection open. For instance if both ends have completed their downloads, there's no point in keeping it open.

name type default
prioritize_partial_pieces bool false

If prioritize_partial_pieces is true, partial pieces are picked before pieces that are more rare. If false, rare pieces are always prioritized, unless the number of partial pieces is growing out of proportion.

name type default
rate_limit_ip_overhead bool true

if set to true, the estimated TCP/IP overhead is drained from the rate limiters, to avoid exceeding the limits with the total traffic

name type default
announce_to_all_tiers bool false
announce_to_all_trackers bool false

announce_to_all_trackers controls how multi tracker torrents are treated. If this is set to true, all trackers in the same tier are announced to in parallel. If all trackers in tier 0 fails, all trackers in tier 1 are announced as well. If it's set to false, the behavior is as defined by the multi tracker specification. It defaults to false, which is the same behavior previous versions of libtorrent has had as well.

announce_to_all_tiers also controls how multi tracker torrents are treated. When this is set to true, one tracker from each tier is announced to. This is the uTorrent behavior. This is false by default in order to comply with the multi-tracker specification.

name type default
prefer_udp_trackers bool true

prefer_udp_trackers is true by default. It means that trackers may be rearranged in a way that udp trackers are always tried before http trackers for the same hostname. Setting this to false means that the trackers' tier is respected and there's no preference of one protocol over another.

name type default
strict_super_seeding bool false

strict_super_seeding when this is set to true, a piece has to have been forwarded to a third peer before another one is handed out. This is the traditional definition of super seeding.

name type default
disable_hash_checks bool false

when set to true, all data downloaded from peers will be assumed to be correct, and not tested to match the hashes in the torrent this is only useful for simulation and testing purposes (typically combined with disabled_storage)

name type default
allow_i2p_mixed bool false

if this is true, i2p torrents are allowed to also get peers from other sources than the tracker, and connect to regular IPs, not providing any anonymization. This may be useful if the user is not interested in the anonymization of i2p, but still wants to be able to connect to i2p peers.

name type default
low_prio_disk bool true

low_prio_disk determines if the disk I/O should use a normal or low priority policy. This defaults to true, which means that it's low priority by default. Other processes doing disk I/O will normally take priority in this mode. This is meant to improve the overall responsiveness of the system while downloading in the background. For high-performance server setups, this might not be desirable.

name type default
volatile_read_cache bool false

volatile_read_cache, if this is set to true, read cache blocks that are hit by peer read requests are removed from the disk cache to free up more space. This is useful if you don't expect the disk cache to create any cache hits from other peers than the one who triggered the cache line to be read into the cache in the first place.

name type default
guided_read_cache bool false

guided_read_cache enables the disk cache to adjust the size of a cache line generated by peers to depend on the upload rate you are sending to that peer. The intention is to optimize the RAM usage of the cache, to read ahead further for peers that you're sending faster to.

name type default
no_atime_storage bool true

no_atime_storage this is a linux-only option and passes in the O_NOATIME to open() when opening files. This may lead to some disk performance improvements.

name type default
incoming_starts_queued_torrents bool false

incoming_starts_queued_torrents defaults to false. If a torrent has been paused by the auto managed feature in libtorrent, i.e. the torrent is paused and auto managed, this feature affects whether or not it is automatically started on an incoming connection. The main reason to queue torrents, is not to make them unavailable, but to save on the overhead of announcing to the trackers, the DHT and to avoid spreading one's unchoke slots too thin. If a peer managed to find us, even though we're no in the torrent anymore, this setting can make us start the torrent and serve it.

name type default
report_true_downloaded bool false

when set to true, the downloaded counter sent to trackers will include the actual number of payload bytes downloaded including redundant bytes. If set to false, it will not include any redundancy bytes

name type default
strict_end_game_mode bool true

strict_end_game_mode defaults to true, and controls when a block may be requested twice. If this is true, a block may only be requested twice when there's ay least one request to every piece that's left to download in the torrent. This may slow down progress on some pieces sometimes, but it may also avoid downloading a lot of redundant bytes. If this is false, libtorrent attempts to use each peer connection to its max, by always requesting something, even if it means requesting something that has been requested from another peer already.

name type default
broadcast_lsd bool true

if broadcast_lsd is set to true, the local peer discovery (or Local Service Discovery) will not only use IP multicast, but also broadcast its messages. This can be useful when running on networks that don't support multicast. Since broadcast messages might be expensive and disruptive on networks, only every 8th announce uses broadcast.

name type default
enable_outgoing_utp bool true
enable_incoming_utp bool true
enable_outgoing_tcp bool true
enable_incoming_tcp bool true

when set to true, libtorrent will try to make outgoing utp connections controls whether libtorrent will accept incoming connections or make outgoing connections of specific type.

name type default
ignore_resume_timestamps bool false

ignore_resume_timestamps determines if the storage, when loading resume data files, should verify that the file modification time with the timestamps in the resume data. This defaults to false, which means timestamps are taken into account, and resume data is less likely to accepted (torrents are more likely to be fully checked when loaded). It might be useful to set this to true if your network is faster than your disk, and it would be faster to redownload potentially missed pieces than to go through the whole storage to look for them.

name type default
no_recheck_incomplete_resume bool false

no_recheck_incomplete_resume determines if the storage should check the whole files when resume data is incomplete or missing or whether it should simply assume we don't have any of the data. By default, this is determined by the existence of any of the files. By setting this setting to true, the files won't be checked, but will go straight to download mode.

name type default
anonymous_mode bool false

anonymous_mode defaults to false. When set to true, the client tries to hide its identity to a certain degree. The user-agent will be reset to an empty string (except for private torrents). Trackers will only be used if they are using a proxy server. The listen sockets are closed, and incoming connections will only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the same machine as the tracker proxy). Since no incoming connections are accepted, NAT-PMP, UPnP, DHT and local peer discovery are all turned off when this setting is enabled.

If you're using I2P, it might make sense to enable anonymous mode as well.

name type default
report_web_seed_downloads bool true

specifies whether downloads from web seeds is reported to the tracker or not. Defaults to on. Turning it off also excludes web seed traffic from other stats and download rate reporting via the libtorrent API.

name type default
announce_double_nat bool false

if this is true, the &ip= argument in tracker requests (unless otherwise specified) will be set to the intermediate IP address if the user is double NATed. If the user is not double NATed, this option does not have an affect

name type default
seeding_outgoing_connections bool true

seeding_outgoing_connections determines if seeding (and finished) torrents should attempt to make outgoing connections or not. By default this is true. It may be set to false in very specific applications where the cost of making outgoing connections is high, and there are no or small benefits of doing so. For instance, if no nodes are behind a firewall or a NAT, seeds don't need to make outgoing connections.

name type default
no_connect_privileged_ports bool false

when this is true, libtorrent will not attempt to make outgoing connections to peers whose port is < 1024. This is a safety precaution to avoid being part of a DDoS attack

name type default
smooth_connects bool true

smooth_connects is true by default, which means the number of connection attempts per second may be limited to below the connection_speed, in case we're close to bump up against the limit of number of connections. The intention of this setting is to more evenly distribute our connection attempts over time, instead of attempting to connect in batches, and timing them out in batches.

name type default
always_send_user_agent bool false

always send user-agent in every web seed request. If false, only the first request per http connection will include the user agent

name type default
apply_ip_filter_to_trackers bool true

apply_ip_filter_to_trackers defaults to true. It determines whether the IP filter applies to trackers as well as peers. If this is set to false, trackers are exempt from the IP filter (if there is one). If no IP filter is set, this setting is irrelevant.

name type default
use_disk_read_ahead bool true

use_disk_read_ahead defaults to true and will attempt to optimize disk reads by giving the operating system heads up of disk read requests as they are queued in the disk job queue.

name type default
lock_files bool false

lock_files determines whether or not to lock files which libtorrent is downloading to or seeding from. This is implemented using fcntl(F_SETLK) on unix systems and by not passing in SHARE_READ and SHARE_WRITE on windows. This might prevent 3rd party processes from corrupting the files under libtorrent's feet.

name type default
contiguous_recv_buffer bool true

contiguous_recv_buffer determines whether or not libtorrent should receive data from peers into a contiguous intermediate buffer, to then copy blocks into disk buffers from, or to make many smaller calls to read(), each time passing in the specific buffer the data belongs in. When downloading at high rates, the latter may save some time copying data. When seeding at high rates, all incoming traffic consists of a very large number of tiny packets, and enabling contiguous_recv_buffer will provide higher performance. When this is enabled, it will only be used when seeding to peers, since that's when it provides performance improvements.

name type default
ban_web_seeds bool true

when true, web seeds sending bad data will be banned

name type default
allow_partial_disk_writes bool true

when set to false, the write_cache_line_size will apply across piece boundaries. this is a bad idea unless the piece picker also is configured to have an affinity to pick pieces belonging to the same write cache line as is configured in the disk cache.

name type default
force_proxy bool false

If true, disables any communication that's not going over a proxy. Enabling this requires a proxy to be configured as well, see proxy_type and proxy_hostname settings. The listen sockets are closed, and incoming connections will only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the same machine as the tracker proxy).

name type default
support_share_mode bool true

if false, prevents libtorrent to advertise share-mode support

name type default
support_merkle_torrents bool true

if this is false, don't advertise support for the Tribler merkle tree piece message

name type default
report_redundant_bytes bool true

if this is true, the number of redundant bytes is sent to the tracker

name type default
listen_system_port_fallback bool true

if this is true, libtorrent will fall back to listening on a port chosen by the operating system (i.e. binding to port 0). If a failure is preferred, set this to false.

name type default
use_disk_cache_pool bool false

use_disk_cache_pool enables using a pool allocator for disk cache blocks. Enabling it makes the cache perform better at high throughput. It also makes the cache less likely and slower at returning memory back to the system, once allocated.

name type default
announce_crypto_support bool true

when this is true, and incoming encrypted connections are enabled, &supportcrypt=1 is included in http tracker announces

name type default
enable_upnp bool true

Starts and stops the UPnP service. When started, the listen port and the DHT port are attempted to be forwarded on local UPnP router devices.

The upnp object returned by start_upnp() can be used to add and remove arbitrary port mappings. Mapping status is returned through the portmap_alert and the portmap_error_alert. The object will be valid until stop_upnp() is called. See upnp and nat pmp.

name type default
enable_natpmp bool true

Starts and stops the NAT-PMP service. When started, the listen port and the DHT port are attempted to be forwarded on the router through NAT-PMP.

The natpmp object returned by start_natpmp() can be used to add and remove arbitrary port mappings. Mapping status is returned through the portmap_alert and the portmap_error_alert. The object will be valid until stop_natpmp() is called. See upnp and nat pmp.

name type default
enable_lsd bool true

Starts and stops Local Service Discovery. This service will broadcast the infohashes of all the non-private torrents on the local network to look for peers on the same swarm within multicast reach.

name type default
enable_dht bool true

starts the dht node and makes the trackerless service available to torrents.

name type default
prefer_rc4 bool false

if the allowed encryption level is both, setting this to true will prefer rc4 if both methods are offered, plaintext otherwise

name type default
proxy_hostnames bool true

if true, hostname lookups are done via the configured proxy (if any). This is only supported by SOCKS5 and HTTP.

name type default
proxy_peer_connections bool true

if true, peer connections are made (and accepted) over the configured proxy, if any. Web seeds as well as regular bittorrent peer connections are considered "peer connections". Anything transporting actual torrent payload (trackers and DHT traffic are not considered peer connections).

name type default
auto_sequential bool true

if this setting is true, torrents with a very high availability of pieces (and seeds) are downloaded sequentially. This is more efficient for the disk I/O. With many seeds, the download order is unlikely to matter anyway

name type default
proxy_tracker_connections bool true

if true, tracker connections are made over the configured proxy, if any.

name type default
tracker_completion_timeout int 30

tracker_completion_timeout is the number of seconds the tracker connection will wait from when it sent the request until it considers the tracker to have timed-out. Default value is 60 seconds.

name type default
tracker_receive_timeout int 10

tracker_receive_timeout is the number of seconds to wait to receive any data from the tracker. If no data is received for this number of seconds, the tracker will be considered as having timed out. If a tracker is down, this is the kind of timeout that will occur.

name type default
stop_tracker_timeout int 5

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

name type default
tracker_maximum_response_length int 1024*1024

this is the maximum number of bytes in a tracker response. If a response size passes this number of bytes it will be rejected and the connection will be closed. On gzipped responses this size is measured on the uncompressed data. So, if you get 20 bytes of gzip response that'll expand to 2 megabytes, it will be interrupted before the entire response has been uncompressed (assuming the limit is lower than 2 megs).

name type default
piece_timeout int 20

the number of seconds from a request is sent until it times out if no piece response is returned.

name type default
request_timeout int 60

the number of seconds one block (16kB) is expected to be received within. If it's not, the block is requested from a different peer

name type default
request_queue_time int 3

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.

name type default
max_allowed_in_request_queue int 500

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.

name type default
max_out_request_queue int 500

max_out_request_queue is the maximum number of outstanding requests to send to a peer. This limit takes precedence over request_queue_time. i.e. no matter the download speed, the number of outstanding requests will never exceed this limit.

name type default
whole_pieces_threshold int 20

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.

name type default
peer_timeout int 120

peer_timeout is the number of seconds the peer connection should wait (for any activity on the peer connection) before closing it due to time out. This defaults to 120 seconds, since that's what's specified in the protocol specification. After half the time out, a keep alive message is sent.

name type default
urlseed_timeout int 20

same as peer_timeout, but only applies to url-seeds. this is usually set lower, because web servers are expected to be more reliable.

name type default
urlseed_pipeline_size int 5

controls the pipelining size of url-seeds. i.e. the number of HTTP request to keep outstanding before waiting for the first one to complete. It's common for web servers to limit this to a relatively low number, like 5

name type default
urlseed_wait_retry int 30

time to wait until a new retry of a web seed takes place

name type default
file_pool_size int 40

sets the upper limit on the total number of files this session will keep open. The reason why files are left open at all is that some anti virus software hooks on every file close, and scans the file for viruses. deferring the closing of the files will be the difference between a usable system and a completely hogged down system. Most operating systems also has a limit on the total number of file descriptors a process may have open.

name type default
max_failcount int 3

max_failcount is the maximum times we try to connect to a peer before stop connecting again. If a peer succeeds, the failcounter is reset. If a peer is retrieved from a peer source (other than DHT) the failcount is decremented by one, allowing another try.

name type default
min_reconnect_time int 60

the number of seconds to wait to reconnect to a peer. this time is multiplied with the failcount.

name type default
peer_connect_timeout int 15

peer_connect_timeout the number of seconds to wait after a connection attempt is initiated to a peer until it is considered as having timed out. This setting is especially important in case the number of half-open connections are limited, since stale half-open connection may delay the connection of other peers considerably.

name type default
connection_speed int 30

connection_speed is the number of connection attempts that are made per second. If a number < 0 is specified, it will default to 200 connections per second. If 0 is specified, it means don't make outgoing connections at all.

name type default
inactivity_timeout int 600

if a peer is uninteresting and uninterested for longer than this number of seconds, it will be disconnected. default is 10 minutes

name type default
unchoke_interval int 15

unchoke_interval is the number of seconds between chokes/unchokes. On this interval, peers are re-evaluated for being choked/unchoked. This is defined as 30 seconds in the protocol, and it should be significantly longer than what it takes for TCP to ramp up to it's max rate.

name type default
optimistic_unchoke_interval int 30

optimistic_unchoke_interval is the number of seconds between each optimistic unchoke. On this timer, the currently optimistically unchoked peer will change.

name type default
num_want int 200

num_want is the number of peers we want from each tracker request. It defines what is sent as the &num_want= parameter to the tracker.

name type default
initial_picker_threshold int 4

initial_picker_threshold specifies the number of pieces we need before we switch to rarest first picking. This defaults to 4, which means the 4 first pieces in any torrent are picked at random, the following pieces are picked in rarest first order.

name type default
allowed_fast_set_size int 10

the number of allowed pieces to send to peers that supports the fast extensions

name type default
suggest_mode int settings_pack::no_piece_suggestions

suggest_mode controls whether or not libtorrent will send out suggest messages to create a bias of its peers to request certain pieces. The modes are:

  • no_piece_suggestions which is the default and will not send out suggest messages.
  • suggest_read_cache which will send out suggest messages for the most recent pieces that are in the read cache.
name type default
max_queued_disk_bytes int 1024 * 1024

max_queued_disk_bytes is the maximum number of bytes, to be written to disk, that can wait in the disk I/O thread queue. This queue is only for waiting for the disk I/O thread to receive the job and either write it to disk or insert it in the write cache. When this limit is reached, the peer connections will stop reading data from their sockets, until the disk thread catches up. Setting this too low will severely limit your download rate.

name type default
handshake_timeout int 10

the number of seconds to wait for a handshake response from a peer. If no response is received within this time, the peer is disconnected.

name type default
send_buffer_low_watermark int 10 * 1024
send_buffer_watermark int 500 * 1024
send_buffer_watermark_factor int 50

send_buffer_low_watermark the minimum send buffer target size (send buffer includes bytes pending being read from disk). For good and snappy seeding performance, set this fairly high, to at least fit a few blocks. This is essentially the initial window size which will determine how fast we can ramp up the send rate

if the send buffer has fewer bytes than send_buffer_watermark, we'll read another 16kB block onto it. If set too small, upload rate capacity will suffer. If set too high, memory will be wasted. The actual watermark may be lower than this in case the upload rate is low, this is the upper limit.

the current upload rate to a peer is multiplied by this factor to get the send buffer watermark. The factor is specified as a percentage. i.e. 50 -> 0.5 This product is clamped to the send_buffer_watermark setting to not exceed the max. For high speed upload, this should be set to a greater value than 100. For high capacity connections, setting this higher can improve upload performance and disk throughput. Setting it too high may waste RAM and create a bias towards read jobs over write jobs.

name type default
choking_algorithm int settings_pack::fixed_slots_choker
seed_choking_algorithm int settings_pack::round_robin

choking_algorithm specifies which algorithm to use to determine which peers to unchoke.

The options for choking algorithms are:

  • fixed_slots_choker is the traditional choker with a fixed number of unchoke slots (as specified by settings_pack::unchoke_slots_limit).
  • rate_based_choker opens up unchoke slots based on the upload rate achieved to peers. The more slots that are opened, the marginal upload rate required to open up another slot increases.
  • bittyrant_choker attempts to optimize download rate by finding the reciprocation rate of each peer individually and prefers peers that gives the highest return on investment. It still allocates all upload capacity, but shuffles it around to the best peers first. For this choker to be efficient, you need to set a global upload rate limit (settings_pack::upload_rate_limit). For more information about this choker, see the paper. This choker is not fully implemented nor tested.

seed_choking_algorithm controls the seeding unchoke behavior. The available options are:

  • round_robin which round-robins the peers that are unchoked when seeding. This distributes the upload bandwidht uniformly and fairly. It minimizes the ability for a peer to download everything without redistributing it.
  • fastest_upload unchokes the peers we can send to the fastest. This might be a bit more reliable in utilizing all available capacity.
  • anti_leech prioritizes peers who have just started or are just about to finish the download. The intention is to force peers in the middle of the download to trade with each other.
name type default
cache_size int 1024
cache_buffer_chunk_size int 0
cache_expiry int 300

cache_size is the disk write and read cache. It is specified in units of 16 KiB blocks. Buffers that are part of a peer's send or receive buffer also count against this limit. Send and receive buffers will never be denied to be allocated, but they will cause the actual cached blocks to be flushed or evicted. If this is set to -1, the cache size is automatically set to the amount of physical RAM available in the machine divided by 8. If the amount of physical RAM cannot be determined, it's set to 1024 (= 16 MiB).

Disk buffers are allocated using a pool allocator, the number of blocks that are allocated at a time when the pool needs to grow can be specified in cache_buffer_chunk_size. Lower numbers saves memory at the expense of more heap allocations. If it is set to 0, the effective chunk size is proportional to the total cache size, attempting to strike a good balance between performance and memory usage. It defaults to 0. cache_expiry is the number of seconds from the last cached write to a piece in the write cache, to when it's forcefully flushed to disk. Default is 60 second.

On 32 bit builds, the effective cache size will be limited to 3/4 of 2 GiB to avoid exceeding the virtual address space limit.

name type default
disk_io_write_mode int settings_pack::enable_os_cache
disk_io_read_mode int settings_pack::enable_os_cache

determines how files are opened when they're in read only mode versus read and write mode. The options are:

enable_os_cache
This is the default and files are opened normally, with the OS caching reads and writes.
disable_os_cache
This opens all files in no-cache mode. This corresponds to the OS not letting blocks for the files linger in the cache. This makes sense in order to avoid the bittorrent client to potentially evict all other processes' cache by simply handling high throughput and large files. If libtorrent's read cache is disabled, enabling this may reduce performance.

One reason to disable caching is that it may help the operating system from growing its file cache indefinitely.

name type default
outgoing_port int 0
num_outgoing_ports int 0

this is the first port to use for binding outgoing connections to. This is useful for users that have routers that allow QoS settings based on local port. when binding outgoing connections to specific ports, num_outgoing_ports is the size of the range. It should be more than a few

Warning

setting outgoing ports will limit the ability to keep multiple connections to the same client, even for different torrents. It is not recommended to change this setting. Its main purpose is to use as an escape hatch for cheap routers with QoS capability but can only classify flows based on port numbers.

It is a range instead of a single port because of the problems with failing to reconnect to peers if a previous socket to that peer and port is in TIME_WAIT state.

name type default
peer_tos int 0x20

peer_tos determines the TOS byte set in the IP header of every packet sent to peers (including web seeds). The default value for this is 0x0 (no marking). One potentially useful TOS mark is 0x20, this represents the QBone scavenger service. For more details, see QBSS.

name type default
active_downloads int 3
active_seeds int 5
active_checking int 1
active_dht_limit int 88
active_tracker_limit int 1600
active_lsd_limit int 60
active_limit int 500
active_loaded_limit int 0

for auto managed torrents, these are the limits they are subject to. If there are too many torrents some of the auto managed ones will be paused until some slots free up. active_downloads and active_seeds controls how many active seeding and downloading torrents the queuing mechanism allows. The target number of active torrents is min(active_downloads + active_seeds, active_limit). active_downloads and active_seeds are upper limits on the number of downloading torrents and seeding torrents respectively. Setting the value to -1 means unlimited. For example if there are 10 seeding torrents and 10 downloading torrents, and active_downloads is 4 and active_seeds is 4, there will be 4 seeds active and 4 downloading torrents. If the settings are active_downloads = 2 and active_seeds = 4, then there will be 2 downloading torrents and 4 seeding torrents active. Torrents that are not auto managed are not counted against these limits.

active_checking is the limit of number of simultaneous checking torrents.

active_limit is a hard limit on the number of active (auto managed) torrents. This limit also applies to slow torrents.

active_dht_limit is the max number of torrents to announce to the DHT. By default this is set to 88, which is no more than one DHT announce every 10 seconds.

active_tracker_limit is the max number of torrents to announce to their trackers. By default this is 360, which is no more than one announce every 5 seconds.

active_lsd_limit is the max number of torrents to announce to the local network over the local service discovery protocol. By default this is 80, which is no more than one announce every 5 seconds (assuming the default announce interval of 5 minutes).

You can have more torrents active, even though they are not announced to the DHT, lsd or their tracker. If some peer knows about you for any reason and tries to connect, it will still be accepted, unless the torrent is paused, which means it won't accept any connections.

active_loaded_limit is the number of torrents that are allowed to be loaded at any given time. Note that a torrent can be active even though it's not loaded. If an unloaded torrents finds a peer that wants to access it, the torrent will be loaded on demand, using a user-supplied callback function. If the feature of unloading torrents is not enabled, this setting have no effect. If this limit is set to 0, it means unlimited. For more information, see dynamic loading of torrent files.

name type default
auto_manage_interval int 30

auto_manage_interval is the number of seconds between the torrent queue is updated, and rotated.

name type default
seed_time_limit int 24 * 60 * 60

this is the limit on the time a torrent has been an active seed (specified in seconds) before it is considered having met the seed limit criteria. See queuing.

name type default
auto_scrape_interval int 1800
auto_scrape_min_interval int 300

auto_scrape_interval is the number of seconds between scrapes of queued torrents (auto managed and paused torrents). Auto managed torrents that are paused, are scraped regularly in order to keep track of their downloader/seed ratio. This ratio is used to determine which torrents to seed and which to pause.

auto_scrape_min_interval is the minimum number of seconds between any automatic scrape (regardless of torrent). In case there are a large number of paused auto managed torrents, this puts a limit on how often a scrape request is sent.

name type default
max_peerlist_size int 3000
max_paused_peerlist_size int 1000

max_peerlist_size is the maximum number of peers in the list of known peers. These peers are not necessarily connected, so this number should be much greater than the maximum number of connected peers. Peers are evicted from the cache when the list grows passed 90% of this limit, and once the size hits the limit, peers are no longer added to the list. If this limit is set to 0, there is no limit on how many peers we'll keep in the peer list.

max_paused_peerlist_size is the max peer list size used for torrents that are paused. This default to the same as max_peerlist_size, but can be used to save memory for paused torrents, since it's not as important for them to keep a large peer list.

name type default
min_announce_interval int 5 * 60

this is the minimum allowed announce interval for a tracker. This is specified in seconds and is used as a sanity check on what is returned from a tracker. It mitigates hammering misconfigured trackers.

name type default
auto_manage_startup int 60

this is the number of seconds a torrent is considered active after it was started, regardless of upload and download speed. This is so that newly started torrents are not considered inactive until they have a fair chance to start downloading.

name type default
seeding_piece_quota int 20

seeding_piece_quota is the number of pieces to send to a peer, when seeding, before rotating in another peer to the unchoke set. It defaults to 3 pieces, which means that when seeding, any peer we've sent more than this number of pieces to will be unchoked in favour of a choked peer.

name type default
max_rejects int 50

TODO: deprecate this max_rejects is the number of piece requests we will reject in a row while a peer is choked before the peer is considered abusive and is disconnected.

name type default
recv_socket_buffer_size int 0
send_socket_buffer_size int 0

recv_socket_buffer_size and send_socket_buffer_size specifies the buffer sizes set on peer sockets. 0 (which is the default) means the OS default (i.e. don't change the buffer sizes). The socket buffer sizes are changed using setsockopt() with SOL_SOCKET/SO_RCVBUF and SO_SNDBUFFER.

name type default
read_cache_line_size int 32
write_cache_line_size int 16

read_cache_line_size is the number of blocks to read into the read cache when a read cache miss occurs. Setting this to 0 is essentially the same thing as disabling read cache. The number of blocks read into the read cache is always capped by the piece boundary.

When a piece in the write cache has write_cache_line_size contiguous blocks in it, they will be flushed. Setting this to 1 effectively disables the write cache.

name type default
optimistic_disk_retry int 10 * 60

optimistic_disk_retry is the number of seconds from a disk write errors occur on a torrent until libtorrent will take it out of the upload mode, to test if the error condition has been fixed.

libtorrent will only do this automatically for auto managed torrents.

You can explicitly take a torrent out of upload only mode using set_upload_mode().

name type default
max_suggest_pieces int 10

max_suggest_pieces is the max number of suggested piece indices received from a peer that's remembered. If a peer floods suggest messages, this limit prevents libtorrent from using too much RAM. It defaults to 10.

name type default
local_service_announce_interval int 5 * 60

local_service_announce_interval is the time between local network announces for a torrent. By default, when local service discovery is enabled a torrent announces itself every 5 minutes. This interval is specified in seconds.

name type default
dht_announce_interval int 15 * 60

dht_announce_interval is the number of seconds between announcing torrents to the distributed hash table (DHT).

name type default
udp_tracker_token_expiry int 60

udp_tracker_token_expiry is the number of seconds libtorrent will keep UDP tracker connection tokens around for. This is specified to be 60 seconds, and defaults to that. The higher this value is, the fewer packets have to be sent to the UDP tracker. In order for higher values to work, the tracker needs to be configured to match the expiration time for tokens.

name type default
default_cache_min_age int 1

default_cache_min_age is the minimum number of seconds any read cache line is kept in the cache. This defaults to one second but may be greater if guided_read_cache is enabled. Having a lower bound on the time a cache line stays in the cache is an attempt to avoid swapping the same pieces in and out of the cache in case there is a shortage of spare cache space.

name type default
num_optimistic_unchoke_slots int 0

num_optimistic_unchoke_slots is the number of optimistic unchoke slots to use. It defaults to 0, which means automatic. Having a higher number of optimistic unchoke slots mean you will find the good peers faster but with the trade-off to use up more bandwidth. When this is set to 0, libtorrent opens up 20% of your allowed upload slots as optimistic unchoke slots.

name type default
default_est_reciprocation_rate int 16000
increase_est_reciprocation_rate int 20
decrease_est_reciprocation_rate int 3

default_est_reciprocation_rate is the assumed reciprocation rate from peers when using the BitTyrant choker. This defaults to 14 kiB/s. If set too high, you will over-estimate your peers and be more altruistic while finding the true reciprocation rate, if it's set too low, you'll be too stingy and waste finding the true reciprocation rate.

increase_est_reciprocation_rate specifies how many percent the estimated reciprocation rate should be increased by each unchoke interval a peer is still choking us back. This defaults to 20%. This only applies to the BitTyrant choker.

decrease_est_reciprocation_rate specifies how many percent the estimated reciprocation rate should be decreased by each unchoke interval a peer unchokes us. This default to 3%. This only applies to the BitTyrant choker.

name type default
max_pex_peers int 50

the max number of peers we accept from pex messages from a single peer. this limits the number of concurrent peers any of our peers claims to be connected to. If they claim to be connected to more than this, we'll ignore any peer that exceeds this limit

name type default
tick_interval int 500

tick_interval specifies the number of milliseconds between internal ticks. This is the frequency with which bandwidth quota is distributed to peers. It should not be more than one second (i.e. 1000 ms). Setting this to a low value (around 100) means higher resolution bandwidth quota distribution, setting it to a higher value saves CPU cycles.

name type default
share_mode_target int 3

share_mode_target specifies the target share ratio for share mode torrents. This defaults to 3, meaning we'll try to upload 3 times as much as we download. Setting this very high, will make it very conservative and you might end up not downloading anything ever (and not affecting your share ratio). It does not make any sense to set this any lower than 2. For instance, if only 3 peers need to download the rarest piece, it's impossible to download a single piece and upload it more than 3 times. If the share_mode_target is set to more than 3, nothing is downloaded.

name type default
upload_rate_limit int 0
download_rate_limit int 0

upload_rate_limit and download_rate_limit sets the session-global limits of upload and download rate limits, in bytes per second. By default peers on the local network are not rate limited.

A value of 0 means unlimited.

For fine grained control over rate limits, including making them apply to local peers, see peer classes.

name type default
dht_upload_rate_limit int 4000

dht_upload_rate_limit sets the rate limit on the DHT. This is specified in bytes per second and defaults to 4000. For busy boxes with lots of torrents that requires more DHT traffic, this should be raised.

name type default
unchoke_slots_limit int 8

unchoke_slots_limit is the max number of unchoked peers in the session. The number of unchoke slots may be ignored depending on what choking_algorithm is set to.

name type default
connections_limit int 200

connections_limit sets a global limit on the number of connections opened. The number of connections is set to a hard minimum of at least two per torrent, so if you set a too low connections limit, and open too many torrents, the limit will not be met.

name type default
connections_slack int 10

connections_slack is the the number of incoming connections exceeding the connection limit to accept in order to potentially replace existing ones.

name type default
utp_target_delay int 100
utp_gain_factor int 3000
utp_min_timeout int 500
utp_syn_resends int 2
utp_fin_resends int 2
utp_num_resends int 3
utp_connect_timeout int 3000
utp_loss_multiplier int 50

utp_target_delay is the target delay for uTP sockets in milliseconds. A high value will make uTP connections more aggressive and cause longer queues in the upload bottleneck. It cannot be too low, since the noise in the measurements would cause it to send too slow. The default is 50 milliseconds. utp_gain_factor is the number of bytes the uTP congestion window can increase at the most in one RTT. This defaults to 300 bytes. If this is set too high, the congestion controller reacts too hard to noise and will not be stable, if it's set too low, it will react slow to congestion and not back off as fast. utp_min_timeout is the shortest allowed uTP socket timeout, specified in milliseconds. This defaults to 500 milliseconds. The timeout depends on the RTT of the connection, but is never smaller than this value. A connection times out when every packet in a window is lost, or when a packet is lost twice in a row (i.e. the resent packet is lost as well).

The shorter the timeout is, the faster the connection will recover from this situation, assuming the RTT is low enough. utp_syn_resends is the number of SYN packets that are sent (and timed out) before giving up and closing the socket. utp_num_resends is the number of times a packet is sent (and lossed or timed out) before giving up and closing the connection. utp_connect_timeout is the number of milliseconds of timeout for the initial SYN packet for uTP connections. For each timed out packet (in a row), the timeout is doubled. utp_loss_multiplier controls how the congestion window is changed when a packet loss is experienced. It's specified as a percentage multiplier for cwnd. By default it's set to 50 (i.e. cut in half). Do not change this value unless you know what you're doing. Never set it higher than 100.

name type default
mixed_mode_algorithm int settings_pack::peer_proportional

The mixed_mode_algorithm determines how to treat TCP connections when there are uTP connections. Since uTP is designed to yield to TCP, there's an inherent problem when using swarms that have both TCP and uTP connections. If nothing is done, uTP connections would often be starved out for bandwidth by the TCP connections. This mode is prefer_tcp. The peer_proportional mode simply looks at the current throughput and rate limits all TCP connections to their proportional share based on how many of the connections are TCP. This works best if uTP connections are not rate limited by the global rate limiter (which they aren't by default).

name type default
listen_queue_size int 5

listen_queue_size is the value passed in to listen() for the listen socket. It is the number of outstanding incoming connections to queue up while we're not actively waiting for a connection to be accepted. The default is 5 which should be sufficient for any normal client. If this is a high performance server which expects to receive a lot of connections, or used in a simulator or test, it might make sense to raise this number. It will not take affect until the listen_interfaces settings is updated.

name type default
torrent_connect_boost int 30

torrent_connect_boost is the number of peers to try to connect to immediately when the first tracker response is received for a torrent. This is a boost to given to new torrents to accelerate them starting up. The normal connect scheduler is run once every second, this allows peers to be connected immediately instead of waiting for the session tick to trigger connections. This may not be set higher than 255.

name type default
alert_queue_size int 1000

alert_queue_size is the maximum number of alerts queued up internally. If alerts are not popped, the queue will eventually fill up to this level.

name type default
max_metadata_size int 3 * 1024 * 10240

max_metadata_size is the maximum allowed size (in bytes) to be received by the metadata extension, i.e. magnet links.

name type default
checking_mem_usage int 1024

the number of blocks to keep outstanding at any given time when checking torrents. Higher numbers give faster re-checks but uses more memory. Specified in number of 16 kiB blocks

name type default
predictive_piece_announce int 0

if set to > 0, pieces will be announced to other peers before they are fully downloaded (and before they are hash checked). The intention is to gain 1.5 potential round trip times per downloaded piece. When non-zero, this indicates how many milliseconds in advance pieces should be announced, before they are expected to be completed.

name type default
aio_threads int 4
aio_max int 300

for some aio back-ends, aio_threads specifies the number of io-threads to use, and aio_max the max number of outstanding jobs.

name type default
network_threads int 0

network_threads is the number of threads to use to call async_write_some (i.e. send) on peer connection sockets. When seeding at extremely high rates, this may become a bottleneck, and setting this to 2 or more may parallelize that cost. When using SSL torrents, all encryption for outgoing traffic is done within the socket send functions, and this will help parallelizing the cost of SSL encryption as well.

name type default
ssl_listen int 0

ssl_listen sets the listen port for SSL connections. If this is set to 0, no SSL listen port is opened. Otherwise a socket is opened on this port. This setting is only taken into account when opening the regular listen port, and won't re-open the listen socket simply by changing this setting.

name type default
tracker_backoff int 250

tracker_backoff determines how aggressively to back off from retrying failing trackers. This value determines x in the following formula, determining the number of seconds to wait until the next retry:

delay = 5 + 5 * x / 100 * fails^2

This setting may be useful to make libtorrent more or less aggressive in hitting trackers.

name type default
share_ratio_limit int 200
seed_time_ratio_limit int 700

when a seeding torrent reaches either the share ratio (bytes up / bytes down) or the seed time ratio (seconds as seed / seconds as downloader) or the seed time limit (seconds as seed) it is considered done, and it will leave room for other torrents. These are specified as percentages. Torrents that are considered done will still be allowed to be seeded, they just won't have priority anymore. For more, see queuing.

name type default
peer_turnover int 4
peer_turnover_cutoff int 90
peer_turnover_interval int 300

peer_turnover is the percentage of peers to disconnect every turnover peer_turnover_interval (if we're at the peer limit), this is specified in percent when we are connected to more than limit * peer_turnover_cutoff peers disconnect peer_turnover fraction of the peers. It is specified in percent peer_turnover_interval is the interval (in seconds) between optimistic disconnects if the disconnects happen and how many peers are disconnected is controlled by peer_turnover and peer_turnover_cutoff

name type default
connect_seed_every_n_download int 10

this setting controls the priority of downloading torrents over seeding or finished torrents when it comes to making peer connections. Peer connections are throttled by the connection_speed and the half-open connection limit. This makes peer connections a limited resource. Torrents that still have pieces to download are prioritized by default, to avoid having many seeding torrents use most of the connection attempts and only give one peer every now and then to the downloading torrent. libtorrent will loop over the downloading torrents to connect a peer each, and every n:th connection attempt, a finished torrent is picked to be allowed to connect to a peer. This setting controls n.

name type default
max_http_recv_buffer_size int 4*1024*204

the max number of bytes to allow an HTTP response to be when announcing to trackers or downloading .torrent files via the url provided in add_torrent_params.

name type default
max_retry_port_bind int 10

if binding to a specific port fails, should the port be incremented by one and tried again? This setting specifies how many times to retry a failed port bind

name type default
alert_mask int alert::error_notification

a bitmask combining flags from alert::category_t defining which kinds of alerts to receive

name type default
out_enc_policy int settings_pack::pe_enabled
in_enc_policy int settings_pack::pe_enabled

control the settings for incoming and outgoing connections respectively. see enc_policy enum for the available options. Keep in mind that protocol encryption degrades performance in several respects:

  1. It prevents "zero copy" disk buffers being sent to peers, since each peer needs to mutate the data (i.e. encrypt it) the data must be copied per peer connection rather than sending the same buffer to multiple peers.
  2. The encryption itself requires more CPU than plain bittorrent protocol. The highest cost is the Diffie Hellman exchange on connection setup.
  3. The encryption handshake adds several round-trips to the connection setup, and delays transferring data.
name type default
allowed_enc_level int settings_pack::pe_both

determines the encryption level of the connections. This setting will adjust which encryption scheme is offered to the other peer, as well as which encryption scheme is selected by the client. See enc_level enum for options.

name type default
inactive_down_rate int 2048
inactive_up_rate int 2048

the download and upload rate limits for a torrent to be considered active by the queuing mechanism. A torrent whose download rate is less than inactive_down_rate and whose upload rate is less than inactive_up_rate for auto_manage_startup seconds, is considered inactive, and another queued torrent may be started. This logic is disabled if dont_count_slow_torrents is false.

name type default
proxy_type int settings_pack::none

proxy to use, defaults to none. see proxy_type_t.

name type default
proxy_port int 0

the port of the proxy server

name type default
i2p_port int 0

sets the i2p SAM bridge port to connect to. set the hostname with the i2p_hostname setting.

name type default
cache_size_volatile int 256

this determines the max number of volatile disk cache blocks. If the number of volatile blocks exceed this limit, other volatile blocks will start to be evicted. A disk cache block is volatile if it has low priority, and should be one of the first blocks to be evicted under pressure. For instance, blocks pulled into the cache as the result of calculating a piece hash are volatile. These blocks don't represent potential interest among peers, so the value of keeping them in the cache is limited.

name type default
urlseed_max_request_bytes int 16 * 1024 * 1024

The maximum request range of an url seed in bytes. This value defines the largest possible sequential web seed request. Default is 16 * 1024 * 1024. Lower values are possible but will be ignored if they are lower then piece size. This value should be related to your download speed to prevent libtorrent from creating too many expensive http requests per second. You can select a value as high as you want but keep in mind that libtorrent can't create parallel requests if the first request did already select the whole file. If you combine bittorrent seeds with web seeds and pick strategies like rarest first you may find your web seed requests split into smaller parts because we don't download already picked pieces twice.

name type default
web_seed_name_lookup_retry int 1800

time to wait until a new retry of a web seed name lookup

name type default
close_file_interval int CLOSE_FILE_INTERVAL

the number of seconds between closing the file opened the longest ago. 0 means to disable the feature. The purpose of this is to periodically close files to trigger the operating system flushing disk cache. Specifically it has been observed to be required on windows to not have the disk cache grow indefinitely. This defaults to 120 seconds on windows, and disabled on other systems.

name type default
utp_cwnd_reduce_timer int 100

When uTP experiences packet loss, it will reduce the congestion window, and not reduce it again for this many milliseconds, even if experiencing another lost packet.

struct settings_pack
{
   void set_int (int name, int val);
   void set_str (int name, std::string val);
   void set_bool (int name, bool val);
   bool has_val (int name) const;
   void clear ();
   void clear (int name);
   bool get_bool (int name) const;
   int get_int (int name) const;
   std::string get_str (int name) const;

   enum type_bases
   {
      string_type_base,
      int_type_base,
      bool_type_base,
      type_mask,
      index_mask,
   };

   enum string_types
   {
      user_agent,
      announce_ip,
      deprecated12,
      handshake_client_version,
      outgoing_interfaces,
      listen_interfaces,
      proxy_hostname,
      proxy_username,
      proxy_password,
      i2p_hostname,
      peer_fingerprint,
      dht_bootstrap_nodes,
      max_string_setting_internal,
   };

   enum bool_types
   {
      allow_multiple_connections_per_ip,
      deprecated1,
      send_redundant_have,
      lazy_bitfields,
      use_dht_as_fallback,
      upnp_ignore_nonrouters,
      use_parole_mode,
      use_read_cache,
      deprecated7,
      deprecated10,
      deprecated13,
      coalesce_reads,
      coalesce_writes,
      auto_manage_prefer_seeds,
      dont_count_slow_torrents,
      close_redundant_connections,
      prioritize_partial_pieces,
      rate_limit_ip_overhead,
      announce_to_all_tiers,
      announce_to_all_trackers,
      prefer_udp_trackers,
      strict_super_seeding,
      deprecated8,
      disable_hash_checks,
      allow_i2p_mixed,
      low_prio_disk,
      volatile_read_cache,
      guided_read_cache,
      no_atime_storage,
      incoming_starts_queued_torrents,
      report_true_downloaded,
      strict_end_game_mode,
      broadcast_lsd,
      enable_outgoing_utp,
      enable_incoming_utp,
      enable_outgoing_tcp,
      enable_incoming_tcp,
      ignore_resume_timestamps,
      no_recheck_incomplete_resume,
      anonymous_mode,
      report_web_seed_downloads,
      deprecated2,
      announce_double_nat,
      seeding_outgoing_connections,
      no_connect_privileged_ports,
      smooth_connects,
      always_send_user_agent,
      apply_ip_filter_to_trackers,
      use_disk_read_ahead,
      lock_files,
      contiguous_recv_buffer,
      ban_web_seeds,
      allow_partial_disk_writes,
      force_proxy,
      support_share_mode,
      support_merkle_torrents,
      report_redundant_bytes,
      listen_system_port_fallback,
      use_disk_cache_pool,
      announce_crypto_support,
      enable_upnp,
      enable_natpmp,
      enable_lsd,
      enable_dht,
      prefer_rc4,
      proxy_hostnames,
      proxy_peer_connections,
      auto_sequential,
      proxy_tracker_connections,
      max_bool_setting_internal,
   };

   enum int_types
   {
      tracker_completion_timeout,
      tracker_receive_timeout,
      stop_tracker_timeout,
      tracker_maximum_response_length,
      piece_timeout,
      request_timeout,
      request_queue_time,
      max_allowed_in_request_queue,
      max_out_request_queue,
      whole_pieces_threshold,
      peer_timeout,
      urlseed_timeout,
      urlseed_pipeline_size,
      urlseed_wait_retry,
      file_pool_size,
      max_failcount,
      min_reconnect_time,
      peer_connect_timeout,
      connection_speed,
      inactivity_timeout,
      unchoke_interval,
      optimistic_unchoke_interval,
      num_want,
      initial_picker_threshold,
      allowed_fast_set_size,
      suggest_mode,
      max_queued_disk_bytes,
      handshake_timeout,
      send_buffer_low_watermark,
      send_buffer_watermark,
      send_buffer_watermark_factor,
      choking_algorithm,
      seed_choking_algorithm,
      cache_size,
      cache_buffer_chunk_size,
      cache_expiry,
      deprecated11,
      disk_io_write_mode,
      disk_io_read_mode,
      outgoing_port,
      num_outgoing_ports,
      peer_tos,
      active_downloads,
      active_seeds,
      active_checking,
      active_dht_limit,
      active_tracker_limit,
      active_lsd_limit,
      active_limit,
      active_loaded_limit,
      auto_manage_interval,
      seed_time_limit,
      auto_scrape_interval,
      auto_scrape_min_interval,
      max_peerlist_size,
      max_paused_peerlist_size,
      min_announce_interval,
      auto_manage_startup,
      seeding_piece_quota,
      max_rejects,
      recv_socket_buffer_size,
      send_socket_buffer_size,
      deprecated14,
      read_cache_line_size,
      write_cache_line_size,
      optimistic_disk_retry,
      max_suggest_pieces,
      local_service_announce_interval,
      dht_announce_interval,
      udp_tracker_token_expiry,
      default_cache_min_age,
      num_optimistic_unchoke_slots,
      default_est_reciprocation_rate,
      increase_est_reciprocation_rate,
      decrease_est_reciprocation_rate,
      max_pex_peers,
      tick_interval,
      share_mode_target,
      upload_rate_limit,
      download_rate_limit,
      deprecated3,
      deprecated4,
      dht_upload_rate_limit,
      unchoke_slots_limit,
      deprecated5,
      connections_limit,
      connections_slack,
      utp_target_delay,
      utp_gain_factor,
      utp_min_timeout,
      utp_syn_resends,
      utp_fin_resends,
      utp_num_resends,
      utp_connect_timeout,
      deprecated6,
      utp_loss_multiplier,
      mixed_mode_algorithm,
      listen_queue_size,
      torrent_connect_boost,
      alert_queue_size,
      max_metadata_size,
      deprecated9,
      checking_mem_usage,
      predictive_piece_announce,
      aio_threads,
      aio_max,
      network_threads,
      ssl_listen,
      tracker_backoff,
      share_ratio_limit,
      seed_time_ratio_limit,
      peer_turnover,
      peer_turnover_cutoff,
      peer_turnover_interval,
      connect_seed_every_n_download,
      max_http_recv_buffer_size,
      max_retry_port_bind,
      alert_mask,
      out_enc_policy,
      in_enc_policy,
      allowed_enc_level,
      inactive_down_rate,
      inactive_up_rate,
      proxy_type,
      proxy_port,
      i2p_port,
      cache_size_volatile,
      urlseed_max_request_bytes,
      web_seed_name_lookup_retry,
      close_file_interval,
      utp_cwnd_reduce_timer,
      max_int_setting_internal,
   };

   enum settings_counts_t
   {
      num_string_settings,
      num_bool_settings,
      num_int_settings,
   };

   enum suggest_mode_t
   {
      no_piece_suggestions,
      suggest_read_cache,
   };

   enum choking_algorithm_t
   {
      fixed_slots_choker,
      rate_based_choker,
      bittyrant_choker,
   };

   enum seed_choking_algorithm_t
   {
      round_robin,
      fastest_upload,
      anti_leech,
   };

   enum io_buffer_mode_t
   {
      enable_os_cache,
      deprecated,
      disable_os_cache,
   };

   enum bandwidth_mixed_algo_t
   {
      prefer_tcp,
      peer_proportional,
   };

   enum enc_policy
   {
      pe_forced,
      pe_enabled,
      pe_disabled,
   };

   enum enc_level
   {
      pe_plaintext,
      pe_rc4,
      pe_both,
   };

   enum proxy_type_t
   {
      none,
      socks4,
      socks5,
      socks5_pw,
      http,
      http_pw,
      i2p_proxy,
   };
};

clear()

void clear ();

clear the settings pack from all settings

clear()

void clear (int name);

clear a specific setting from the pack

enum type_bases

Declared in "libtorrent/settings_pack.hpp"

name value description
string_type_base 0  
int_type_base 16384  
bool_type_base 32768  
type_mask 49152  
index_mask 16383  

enum string_types

Declared in "libtorrent/settings_pack.hpp"

name value description
user_agent   this is the client identification to the tracker. The recommended format of this string is: "ClientName/ClientVersion libtorrent/libtorrentVersion". This name will not only be used when making HTTP requests, but also when sending extended headers to peers that support that extension. It may not contain r or n
announce_ip 1 announce_ip is the ip address passed along to trackers as the &ip= parameter. If left as the default, that parameter is omitted.
deprecated12 2  
handshake_client_version 3 this is the client name and version identifier sent to peers in the handshake message. If this is an empty string, the user_agent is used instead
outgoing_interfaces 4 sets the network interface this session will use when it opens outgoing connections. By default, it binds outgoing connections to INADDR_ANY and port 0 (i.e. let the OS decide). Ths parameter must be a string containing one or more, comma separated, adapter names. Adapter names on unix systems are of the form "eth0", "eth1", "tun0", etc. When specifying multiple interfaces, they will be assigned in round-robin order. This may be useful for clients that are multi-homed. Binding an outgoing connection to a local IP does not necessarily make the connection via the associated NIC/Adapter. Setting this to an empty string will disable binding of outgoing connections.
listen_interfaces 5

a comma-separated list of IP port-pairs. These are the listen ports that will be opened for accepting incoming uTP and TCP connections. It is possible to listen on multiple IPs and multiple ports. Binding to port 0 will make the operating system pick the port. The default is "0.0.0.0:6881", which binds to all interfaces on port 6881.

If binding fails because the port is busy, the port number will be incremented by one, settings_pack::max_retry_port_bind times.

if all retry attempts fail, the socket will be bound to port 0, meaning the operating system will pick a port. This behavior can be disabled by disabling settings_pack::listen_system_port_fallback.

if binding fails, the listen_failed_alert is posted, potentially more than once. Once/if binding the listen socket(s) succeed, listen_succeeded_alert is posted.

Each port will attempt to open both a UDP and a TCP listen socket, to allow accepting uTP connections as well as TCP. If using the DHT, this will also make the DHT use the same UDP ports.

Note

The current support for opening arbitrary UDP sockets is limited. In this version of libtorrent, there will only ever be two UDP sockets, one for IPv4 and one for IPv6.

proxy_hostname 6 when using a poxy, this is the hostname where the proxy is running see proxy_type.
proxy_username 7 when using a proxy, these are the credentials (if any) to use whne connecting to it. see proxy_type
proxy_password 8  
i2p_hostname 9 sets the i2p SAM bridge to connect to. set the port with the i2p_port setting.
peer_fingerprint 10

this is the fingerprint for the client. It will be used as the prefix to the peer_id. If this is 20 bytes (or longer) it will be truncated to 20 bytes and used as the entire peer-id

There is a utility function, generate_fingerprint() that can be used to generate a standard client peer ID fingerprint prefix.

dht_bootstrap_nodes 11

This is a comma-separated list of IP port-pairs. They will be added to the DHT node (if it's enabled) as back-up nodes in case we don't know of any. This setting will contain one or more bootstrap nodes by default.

Changing these after the DHT has been started may not have any effect until the DHT is restarted.

max_string_setting_internal 12  

enum bool_types

Declared in "libtorrent/settings_pack.hpp"

name value description
allow_multiple_connections_per_ip   determines if connections from the same IP address as existing connections should be rejected or not. Multiple connections from the same IP address is not allowed by default, to prevent abusive behavior by peers. It may be useful to allow such connections in cases where simulations are run on the same machine, and all peers in a swarm has the same IP address.
deprecated1 1  
send_redundant_have 2 send_redundant_have controls if have messages will be sent to peers that already have the piece. This is typically not necessary, but it might be necessary for collecting statistics in some cases. Default is false.
lazy_bitfields 3 if this is true, outgoing bitfields will never be fuil. If the client is seed, a few bits will be set to 0, and later filled in with have messages. This is to prevent certain ISPs from stopping people from seeding.
use_dht_as_fallback 4 use_dht_as_fallback determines how the DHT is used. If this is true, the DHT will only be used for torrents where all trackers in its tracker list has failed. Either by an explicit error message or a time out. This is false by default, which means the DHT is used by default regardless of if the trackers fail or not.
upnp_ignore_nonrouters 5 upnp_ignore_nonrouters indicates whether or not the UPnP implementation should ignore any broadcast response from a device whose address is not the configured router for this machine. i.e. it's a way to not talk to other people's routers by mistake.
use_parole_mode 6 use_parole_mode specifies if parole mode should be used. Parole mode means that peers that participate in pieces that fail the hash check are put in a mode where they are only allowed to download whole pieces. If the whole piece a peer in parole mode fails the hash check, it is banned. If a peer participates in a piece that passes the hash check, it is taken out of parole mode.
use_read_cache 7 enable and disable caching of blocks read from disk. the purpose of the read cache is partly read-ahead of requests but also to avoid reading blocks back from the disk multiple times for popular pieces.
deprecated7 8  
deprecated10 9  
deprecated13 10  
coalesce_reads 11 allocate separate, contiguous, buffers for read and write calls. Only used where writev/readv cannot be used will use more RAM but may improve performance
coalesce_writes 12  
auto_manage_prefer_seeds 13 prefer seeding torrents when determining which torrents to give active slots to, the default is false which gives preference to downloading torrents
dont_count_slow_torrents 14 if dont_count_slow_torrents is true, torrents without any payload transfers are not subject to the active_seeds and active_downloads limits. This is intended to make it more likely to utilize all available bandwidth, and avoid having torrents that don't transfer anything block the active slots.
close_redundant_connections 15 close_redundant_connections specifies whether libtorrent should close connections where both ends have no utility in keeping the connection open. For instance if both ends have completed their downloads, there's no point in keeping it open.
prioritize_partial_pieces 16 If prioritize_partial_pieces is true, partial pieces are picked before pieces that are more rare. If false, rare pieces are always prioritized, unless the number of partial pieces is growing out of proportion.
rate_limit_ip_overhead 17 if set to true, the estimated TCP/IP overhead is drained from the rate limiters, to avoid exceeding the limits with the total traffic
announce_to_all_tiers 18

announce_to_all_trackers controls how multi tracker torrents are treated. If this is set to true, all trackers in the same tier are announced to in parallel. If all trackers in tier 0 fails, all trackers in tier 1 are announced as well. If it's set to false, the behavior is as defined by the multi tracker specification. It defaults to false, which is the same behavior previous versions of libtorrent has had as well.

announce_to_all_tiers also controls how multi tracker torrents are treated. When this is set to true, one tracker from each tier is announced to. This is the uTorrent behavior. This is false by default in order to comply with the multi-tracker specification.

announce_to_all_trackers 19  
prefer_udp_trackers 20 prefer_udp_trackers is true by default. It means that trackers may be rearranged in a way that udp trackers are always tried before http trackers for the same hostname. Setting this to false means that the trackers' tier is respected and there's no preference of one protocol over another.
strict_super_seeding 21 strict_super_seeding when this is set to true, a piece has to have been forwarded to a third peer before another one is handed out. This is the traditional definition of super seeding.
deprecated8 22  
disable_hash_checks 23 when set to true, all data downloaded from peers will be assumed to be correct, and not tested to match the hashes in the torrent this is only useful for simulation and testing purposes (typically combined with disabled_storage)
allow_i2p_mixed 24 if this is true, i2p torrents are allowed to also get peers from other sources than the tracker, and connect to regular IPs, not providing any anonymization. This may be useful if the user is not interested in the anonymization of i2p, but still wants to be able to connect to i2p peers.
low_prio_disk 25 low_prio_disk determines if the disk I/O should use a normal or low priority policy. This defaults to true, which means that it's low priority by default. Other processes doing disk I/O will normally take priority in this mode. This is meant to improve the overall responsiveness of the system while downloading in the background. For high-performance server setups, this might not be desirable.
volatile_read_cache 26 volatile_read_cache, if this is set to true, read cache blocks that are hit by peer read requests are removed from the disk cache to free up more space. This is useful if you don't expect the disk cache to create any cache hits from other peers than the one who triggered the cache line to be read into the cache in the first place.
guided_read_cache 27 guided_read_cache enables the disk cache to adjust the size of a cache line generated by peers to depend on the upload rate you are sending to that peer. The intention is to optimize the RAM usage of the cache, to read ahead further for peers that you're sending faster to.
no_atime_storage 28 no_atime_storage this is a linux-only option and passes in the O_NOATIME to open() when opening files. This may lead to some disk performance improvements.
incoming_starts_queued_torrents 29 incoming_starts_queued_torrents defaults to false. If a torrent has been paused by the auto managed feature in libtorrent, i.e. the torrent is paused and auto managed, this feature affects whether or not it is automatically started on an incoming connection. The main reason to queue torrents, is not to make them unavailable, but to save on the overhead of announcing to the trackers, the DHT and to avoid spreading one's unchoke slots too thin. If a peer managed to find us, even though we're no in the torrent anymore, this setting can make us start the torrent and serve it.
report_true_downloaded 30 when set to true, the downloaded counter sent to trackers will include the actual number of payload bytes downloaded including redundant bytes. If set to false, it will not include any redundancy bytes
strict_end_game_mode 31 strict_end_game_mode defaults to true, and controls when a block may be requested twice. If this is true, a block may only be requested twice when there's ay least one request to every piece that's left to download in the torrent. This may slow down progress on some pieces sometimes, but it may also avoid downloading a lot of redundant bytes. If this is false, libtorrent attempts to use each peer connection to its max, by always requesting something, even if it means requesting something that has been requested from another peer already.
broadcast_lsd 32 if broadcast_lsd is set to true, the local peer discovery (or Local Service Discovery) will not only use IP multicast, but also broadcast its messages. This can be useful when running on networks that don't support multicast. Since broadcast messages might be expensive and disruptive on networks, only every 8th announce uses broadcast.
enable_outgoing_utp 33 when set to true, libtorrent will try to make outgoing utp connections controls whether libtorrent will accept incoming connections or make outgoing connections of specific type.
enable_incoming_utp 34  
enable_outgoing_tcp 35  
enable_incoming_tcp 36  
ignore_resume_timestamps 37 ignore_resume_timestamps determines if the storage, when loading resume data files, should verify that the file modification time with the timestamps in the resume data. This defaults to false, which means timestamps are taken into account, and resume data is less likely to accepted (torrents are more likely to be fully checked when loaded). It might be useful to set this to true if your network is faster than your disk, and it would be faster to redownload potentially missed pieces than to go through the whole storage to look for them.
no_recheck_incomplete_resume 38 no_recheck_incomplete_resume determines if the storage should check the whole files when resume data is incomplete or missing or whether it should simply assume we don't have any of the data. By default, this is determined by the existence of any of the files. By setting this setting to true, the files won't be checked, but will go straight to download mode.
anonymous_mode 39

anonymous_mode defaults to false. When set to true, the client tries to hide its identity to a certain degree. The user-agent will be reset to an empty string (except for private torrents). Trackers will only be used if they are using a proxy server. The listen sockets are closed, and incoming connections will only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the same machine as the tracker proxy). Since no incoming connections are accepted, NAT-PMP, UPnP, DHT and local peer discovery are all turned off when this setting is enabled.

If you're using I2P, it might make sense to enable anonymous mode as well.

report_web_seed_downloads 40 specifies whether downloads from web seeds is reported to the tracker or not. Defaults to on. Turning it off also excludes web seed traffic from other stats and download rate reporting via the libtorrent API.
deprecated2 41  
announce_double_nat 42 if this is true, the &ip= argument in tracker requests (unless otherwise specified) will be set to the intermediate IP address if the user is double NATed. If the user is not double NATed, this option does not have an affect
seeding_outgoing_connections 43 seeding_outgoing_connections determines if seeding (and finished) torrents should attempt to make outgoing connections or not. By default this is true. It may be set to false in very specific applications where the cost of making outgoing connections is high, and there are no or small benefits of doing so. For instance, if no nodes are behind a firewall or a NAT, seeds don't need to make outgoing connections.
no_connect_privileged_ports 44 when this is true, libtorrent will not attempt to make outgoing connections to peers whose port is < 1024. This is a safety precaution to avoid being part of a DDoS attack
smooth_connects 45 smooth_connects is true by default, which means the number of connection attempts per second may be limited to below the connection_speed, in case we're close to bump up against the limit of number of connections. The intention of this setting is to more evenly distribute our connection attempts over time, instead of attempting to connect in batches, and timing them out in batches.
always_send_user_agent 46 always send user-agent in every web seed request. If false, only the first request per http connection will include the user agent
apply_ip_filter_to_trackers 47 apply_ip_filter_to_trackers defaults to true. It determines whether the IP filter applies to trackers as well as peers. If this is set to false, trackers are exempt from the IP filter (if there is one). If no IP filter is set, this setting is irrelevant.
use_disk_read_ahead 48 use_disk_read_ahead defaults to true and will attempt to optimize disk reads by giving the operating system heads up of disk read requests as they are queued in the disk job queue.
lock_files 49 lock_files determines whether or not to lock files which libtorrent is downloading to or seeding from. This is implemented using fcntl(F_SETLK) on unix systems and by not passing in SHARE_READ and SHARE_WRITE on windows. This might prevent 3rd party processes from corrupting the files under libtorrent's feet.
contiguous_recv_buffer 50 contiguous_recv_buffer determines whether or not libtorrent should receive data from peers into a contiguous intermediate buffer, to then copy blocks into disk buffers from, or to make many smaller calls to read(), each time passing in the specific buffer the data belongs in. When downloading at high rates, the latter may save some time copying data. When seeding at high rates, all incoming traffic consists of a very large number of tiny packets, and enabling contiguous_recv_buffer will provide higher performance. When this is enabled, it will only be used when seeding to peers, since that's when it provides performance improvements.
ban_web_seeds 51 when true, web seeds sending bad data will be banned
allow_partial_disk_writes 52 when set to false, the write_cache_line_size will apply across piece boundaries. this is a bad idea unless the piece picker also is configured to have an affinity to pick pieces belonging to the same write cache line as is configured in the disk cache.
force_proxy 53 If true, disables any communication that's not going over a proxy. Enabling this requires a proxy to be configured as well, see proxy_type and proxy_hostname settings. The listen sockets are closed, and incoming connections will only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the same machine as the tracker proxy).
support_share_mode 54 if false, prevents libtorrent to advertise share-mode support
support_merkle_torrents 55 if this is false, don't advertise support for the Tribler merkle tree piece message
report_redundant_bytes 56 if this is true, the number of redundant bytes is sent to the tracker
listen_system_port_fallback 57 if this is true, libtorrent will fall back to listening on a port chosen by the operating system (i.e. binding to port 0). If a failure is preferred, set this to false.
use_disk_cache_pool 58 use_disk_cache_pool enables using a pool allocator for disk cache blocks. Enabling it makes the cache perform better at high throughput. It also makes the cache less likely and slower at returning memory back to the system, once allocated.
announce_crypto_support 59 when this is true, and incoming encrypted connections are enabled, &supportcrypt=1 is included in http tracker announces
enable_upnp 60

Starts and stops the UPnP service. When started, the listen port and the DHT port are attempted to be forwarded on local UPnP router devices.

The upnp object returned by start_upnp() can be used to add and remove arbitrary port mappings. Mapping status is returned through the portmap_alert and the portmap_error_alert. The object will be valid until stop_upnp() is called. See upnp and nat pmp.

enable_natpmp 61

Starts and stops the NAT-PMP service. When started, the listen port and the DHT port are attempted to be forwarded on the router through NAT-PMP.

The natpmp object returned by start_natpmp() can be used to add and remove arbitrary port mappings. Mapping status is returned through the portmap_alert and the portmap_error_alert. The object will be valid until stop_natpmp() is called. See upnp and nat pmp.

enable_lsd 62 Starts and stops Local Service Discovery. This service will broadcast the infohashes of all the non-private torrents on the local network to look for peers on the same swarm within multicast reach.
enable_dht 63 starts the dht node and makes the trackerless service available to torrents.
prefer_rc4 64 if the allowed encryption level is both, setting this to true will prefer rc4 if both methods are offered, plaintext otherwise
proxy_hostnames 65 if true, hostname lookups are done via the configured proxy (if any). This is only supported by SOCKS5 and HTTP.
proxy_peer_connections 66 if true, peer connections are made (and accepted) over the configured proxy, if any. Web seeds as well as regular bittorrent peer connections are considered "peer connections". Anything transporting actual torrent payload (trackers and DHT traffic are not considered peer connections).
auto_sequential 67 if this setting is true, torrents with a very high availability of pieces (and seeds) are downloaded sequentially. This is more efficient for the disk I/O. With many seeds, the download order is unlikely to matter anyway
proxy_tracker_connections 68 if true, tracker connections are made over the configured proxy, if any.
max_bool_setting_internal 69  

enum int_types

Declared in "libtorrent/settings_pack.hpp"

name value description
tracker_completion_timeout   tracker_completion_timeout is the number of seconds the tracker connection will wait from when it sent the request until it considers the tracker to have timed-out. Default value is 60 seconds.
tracker_receive_timeout 1 tracker_receive_timeout is the number of seconds to wait to receive any data from the tracker. If no data is received for this number of seconds, the tracker will be considered as having timed out. If a tracker is down, this is the kind of timeout that will occur.
stop_tracker_timeout 2 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
tracker_maximum_response_length 3 this is the maximum number of bytes in a tracker response. If a response size passes this number of bytes it will be rejected and the connection will be closed. On gzipped responses this size is measured on the uncompressed data. So, if you get 20 bytes of gzip response that'll expand to 2 megabytes, it will be interrupted before the entire response has been uncompressed (assuming the limit is lower than 2 megs).
piece_timeout 4 the number of seconds from a request is sent until it times out if no piece response is returned.
request_timeout 5 the number of seconds one block (16kB) is expected to be received within. If it's not, the block is requested from a different peer
request_queue_time 6 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.
max_allowed_in_request_queue 7 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.
max_out_request_queue 8 max_out_request_queue is the maximum number of outstanding requests to send to a peer. This limit takes precedence over request_queue_time. i.e. no matter the download speed, the number of outstanding requests will never exceed this limit.
whole_pieces_threshold 9 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.
peer_timeout 10 peer_timeout is the number of seconds the peer connection should wait (for any activity on the peer connection) before closing it due to time out. This defaults to 120 seconds, since that's what's specified in the protocol specification. After half the time out, a keep alive message is sent.
urlseed_timeout 11 same as peer_timeout, but only applies to url-seeds. this is usually set lower, because web servers are expected to be more reliable.
urlseed_pipeline_size 12 controls the pipelining size of url-seeds. i.e. the number of HTTP request to keep outstanding before waiting for the first one to complete. It's common for web servers to limit this to a relatively low number, like 5
urlseed_wait_retry 13 time to wait until a new retry of a web seed takes place
file_pool_size 14 sets the upper limit on the total number of files this session will keep open. The reason why files are left open at all is that some anti virus software hooks on every file close, and scans the file for viruses. deferring the closing of the files will be the difference between a usable system and a completely hogged down system. Most operating systems also has a limit on the total number of file descriptors a process may have open.
max_failcount 15 max_failcount is the maximum times we try to connect to a peer before stop connecting again. If a peer succeeds, the failcounter is reset. If a peer is retrieved from a peer source (other than DHT) the failcount is decremented by one, allowing another try.
min_reconnect_time 16 the number of seconds to wait to reconnect to a peer. this time is multiplied with the failcount.
peer_connect_timeout 17 peer_connect_timeout the number of seconds to wait after a connection attempt is initiated to a peer until it is considered as having timed out. This setting is especially important in case the number of half-open connections are limited, since stale half-open connection may delay the connection of other peers considerably.
connection_speed 18 connection_speed is the number of connection attempts that are made per second. If a number < 0 is specified, it will default to 200 connections per second. If 0 is specified, it means don't make outgoing connections at all.
inactivity_timeout 19 if a peer is uninteresting and uninterested for longer than this number of seconds, it will be disconnected. default is 10 minutes
unchoke_interval 20 unchoke_interval is the number of seconds between chokes/unchokes. On this interval, peers are re-evaluated for being choked/unchoked. This is defined as 30 seconds in the protocol, and it should be significantly longer than what it takes for TCP to ramp up to it's max rate.
optimistic_unchoke_interval 21 optimistic_unchoke_interval is the number of seconds between each optimistic unchoke. On this timer, the currently optimistically unchoked peer will change.
num_want 22 num_want is the number of peers we want from each tracker request. It defines what is sent as the &num_want= parameter to the tracker.
initial_picker_threshold 23 initial_picker_threshold specifies the number of pieces we need before we switch to rarest first picking. This defaults to 4, which means the 4 first pieces in any torrent are picked at random, the following pieces are picked in rarest first order.
allowed_fast_set_size 24 the number of allowed pieces to send to peers that supports the fast extensions
suggest_mode 25

suggest_mode controls whether or not libtorrent will send out suggest messages to create a bias of its peers to request certain pieces. The modes are:

  • no_piece_suggestions which is the default and will not send out suggest messages.
  • suggest_read_cache which will send out suggest messages for the most recent pieces that are in the read cache.
max_queued_disk_bytes 26 max_queued_disk_bytes is the maximum number of bytes, to be written to disk, that can wait in the disk I/O thread queue. This queue is only for waiting for the disk I/O thread to receive the job and either write it to disk or insert it in the write cache. When this limit is reached, the peer connections will stop reading data from their sockets, until the disk thread catches up. Setting this too low will severely limit your download rate.
handshake_timeout 27 the number of seconds to wait for a handshake response from a peer. If no response is received within this time, the peer is disconnected.
send_buffer_low_watermark 28

send_buffer_low_watermark the minimum send buffer target size (send buffer includes bytes pending being read from disk). For good and snappy seeding performance, set this fairly high, to at least fit a few blocks. This is essentially the initial window size which will determine how fast we can ramp up the send rate

if the send buffer has fewer bytes than send_buffer_watermark, we'll read another 16kB block onto it. If set too small, upload rate capacity will suffer. If set too high, memory will be wasted. The actual watermark may be lower than this in case the upload rate is low, this is the upper limit.

the current upload rate to a peer is multiplied by this factor to get the send buffer watermark. The factor is specified as a percentage. i.e. 50 -> 0.5 This product is clamped to the send_buffer_watermark setting to not exceed the max. For high speed upload, this should be set to a greater value than 100. For high capacity connections, setting this higher can improve upload performance and disk throughput. Setting it too high may waste RAM and create a bias towards read jobs over write jobs.

send_buffer_watermark 29  
send_buffer_watermark_factor 30  
choking_algorithm 31

choking_algorithm specifies which algorithm to use to determine which peers to unchoke.

The options for choking algorithms are:

  • fixed_slots_choker is the traditional choker with a fixed number of unchoke slots (as specified by settings_pack::unchoke_slots_limit).
  • rate_based_choker opens up unchoke slots based on the upload rate achieved to peers. The more slots that are opened, the marginal upload rate required to open up another slot increases.
  • bittyrant_choker attempts to optimize download rate by finding the reciprocation rate of each peer individually and prefers peers that gives the highest return on investment. It still allocates all upload capacity, but shuffles it around to the best peers first. For this choker to be efficient, you need to set a global upload rate limit (settings_pack::upload_rate_limit). For more information about this choker, see the paper. This choker is not fully implemented nor tested.

seed_choking_algorithm controls the seeding unchoke behavior. The available options are:

  • round_robin which round-robins the peers that are unchoked when seeding. This distributes the upload bandwidht uniformly and fairly. It minimizes the ability for a peer to download everything without redistributing it.
  • fastest_upload unchokes the peers we can send to the fastest. This might be a bit more reliable in utilizing all available capacity.
  • anti_leech prioritizes peers who have just started or are just about to finish the download. The intention is to force peers in the middle of the download to trade with each other.
seed_choking_algorithm 32  
cache_size 33

cache_size is the disk write and read cache. It is specified in units of 16 KiB blocks. Buffers that are part of a peer's send or receive buffer also count against this limit. Send and receive buffers will never be denied to be allocated, but they will cause the actual cached blocks to be flushed or evicted. If this is set to -1, the cache size is automatically set to the amount of physical RAM available in the machine divided by 8. If the amount of physical RAM cannot be determined, it's set to 1024 (= 16 MiB).

Disk buffers are allocated using a pool allocator, the number of blocks that are allocated at a time when the pool needs to grow can be specified in cache_buffer_chunk_size. Lower numbers saves memory at the expense of more heap allocations. If it is set to 0, the effective chunk size is proportional to the total cache size, attempting to strike a good balance between performance and memory usage. It defaults to 0. cache_expiry is the number of seconds from the last cached write to a piece in the write cache, to when it's forcefully flushed to disk. Default is 60 second.

On 32 bit builds, the effective cache size will be limited to 3/4 of 2 GiB to avoid exceeding the virtual address space limit.

cache_buffer_chunk_size 34  
cache_expiry 35  
deprecated11 36  
disk_io_write_mode 37

determines how files are opened when they're in read only mode versus read and write mode. The options are:

enable_os_cache
This is the default and files are opened normally, with the OS caching reads and writes.
disable_os_cache
This opens all files in no-cache mode. This corresponds to the OS not letting blocks for the files linger in the cache. This makes sense in order to avoid the bittorrent client to potentially evict all other processes' cache by simply handling high throughput and large files. If libtorrent's read cache is disabled, enabling this may reduce performance.

One reason to disable caching is that it may help the operating system from growing its file cache indefinitely.

disk_io_read_mode 38  
outgoing_port 39

this is the first port to use for binding outgoing connections to. This is useful for users that have routers that allow QoS settings based on local port. when binding outgoing connections to specific ports, num_outgoing_ports is the size of the range. It should be more than a few

Warning

setting outgoing ports will limit the ability to keep multiple connections to the same client, even for different torrents. It is not recommended to change this setting. Its main purpose is to use as an escape hatch for cheap routers with QoS capability but can only classify flows based on port numbers.

It is a range instead of a single port because of the problems with failing to reconnect to peers if a previous socket to that peer and port is in TIME_WAIT state.

num_outgoing_ports 40  
peer_tos 41 peer_tos determines the TOS byte set in the IP header of every packet sent to peers (including web seeds). The default value for this is 0x0 (no marking). One potentially useful TOS mark is 0x20, this represents the QBone scavenger service. For more details, see QBSS.
active_downloads 42

for auto managed torrents, these are the limits they are subject to. If there are too many torrents some of the auto managed ones will be paused until some slots free up. active_downloads and active_seeds controls how many active seeding and downloading torrents the queuing mechanism allows. The target number of active torrents is min(active_downloads + active_seeds, active_limit). active_downloads and active_seeds are upper limits on the number of downloading torrents and seeding torrents respectively. Setting the value to -1 means unlimited.

For example if there are 10 seeding torrents and 10 downloading torrents, and active_downloads is 4 and active_seeds is 4, there will be 4 seeds active and 4 downloading torrents. If the settings are active_downloads = 2 and active_seeds = 4, then there will be 2 downloading torrents and 4 seeding torrents active. Torrents that are not auto managed are not counted against these limits.

active_checking is the limit of number of simultaneous checking torrents.

active_limit is a hard limit on the number of active (auto managed) torrents. This limit also applies to slow torrents.

active_dht_limit is the max number of torrents to announce to the DHT. By default this is set to 88, which is no more than one DHT announce every 10 seconds.

active_tracker_limit is the max number of torrents to announce to their trackers. By default this is 360, which is no more than one announce every 5 seconds.

active_lsd_limit is the max number of torrents to announce to the local network over the local service discovery protocol. By default this is 80, which is no more than one announce every 5 seconds (assuming the default announce interval of 5 minutes).

You can have more torrents active, even though they are not announced to the DHT, lsd or their tracker. If some peer knows about you for any reason and tries to connect, it will still be accepted, unless the torrent is paused, which means it won't accept any connections.

active_loaded_limit is the number of torrents that are allowed to be loaded at any given time. Note that a torrent can be active even though it's not loaded. If an unloaded torrents finds a peer that wants to access it, the torrent will be loaded on demand, using a user-supplied callback function. If the feature of unloading torrents is not enabled, this setting have no effect. If this limit is set to 0, it means unlimited. For more information, see dynamic loading of torrent files.

active_seeds 43  
active_checking 44  
active_dht_limit 45  
active_tracker_limit 46  
active_lsd_limit 47  
active_limit 48  
active_loaded_limit 49  
auto_manage_interval 50 auto_manage_interval is the number of seconds between the torrent queue is updated, and rotated.
seed_time_limit 51 this is the limit on the time a torrent has been an active seed (specified in seconds) before it is considered having met the seed limit criteria. See queuing.
auto_scrape_interval 52

auto_scrape_interval is the number of seconds between scrapes of queued torrents (auto managed and paused torrents). Auto managed torrents that are paused, are scraped regularly in order to keep track of their downloader/seed ratio. This ratio is used to determine which torrents to seed and which to pause.

auto_scrape_min_interval is the minimum number of seconds between any automatic scrape (regardless of torrent). In case there are a large number of paused auto managed torrents, this puts a limit on how often a scrape request is sent.

auto_scrape_min_interval 53  
max_peerlist_size 54

max_peerlist_size is the maximum number of peers in the list of known peers. These peers are not necessarily connected, so this number should be much greater than the maximum number of connected peers. Peers are evicted from the cache when the list grows passed 90% of this limit, and once the size hits the limit, peers are no longer added to the list. If this limit is set to 0, there is no limit on how many peers we'll keep in the peer list.

max_paused_peerlist_size is the max peer list size used for torrents that are paused. This default to the same as max_peerlist_size, but can be used to save memory for paused torrents, since it's not as important for them to keep a large peer list.

max_paused_peerlist_size 55  
min_announce_interval 56 this is the minimum allowed announce interval for a tracker. This is specified in seconds and is used as a sanity check on what is returned from a tracker. It mitigates hammering misconfigured trackers.
auto_manage_startup 57 this is the number of seconds a torrent is considered active after it was started, regardless of upload and download speed. This is so that newly started torrents are not considered inactive until they have a fair chance to start downloading.
seeding_piece_quota 58 seeding_piece_quota is the number of pieces to send to a peer, when seeding, before rotating in another peer to the unchoke set. It defaults to 3 pieces, which means that when seeding, any peer we've sent more than this number of pieces to will be unchoked in favour of a choked peer.
max_rejects 59 TODO: deprecate this max_rejects is the number of piece requests we will reject in a row while a peer is choked before the peer is considered abusive and is disconnected.
recv_socket_buffer_size 60 recv_socket_buffer_size and send_socket_buffer_size specifies the buffer sizes set on peer sockets. 0 (which is the default) means the OS default (i.e. don't change the buffer sizes). The socket buffer sizes are changed using setsockopt() with SOL_SOCKET/SO_RCVBUF and SO_SNDBUFFER.
send_socket_buffer_size 61  
deprecated14 62  
read_cache_line_size 63

read_cache_line_size is the number of blocks to read into the read cache when a read cache miss occurs. Setting this to 0 is essentially the same thing as disabling read cache. The number of blocks read into the read cache is always capped by the piece boundary.

When a piece in the write cache has write_cache_line_size contiguous blocks in it, they will be flushed. Setting this to 1 effectively disables the write cache.

write_cache_line_size 64  
optimistic_disk_retry 65

optimistic_disk_retry is the number of seconds from a disk write errors occur on a torrent until libtorrent will take it out of the upload mode, to test if the error condition has been fixed.

libtorrent will only do this automatically for auto managed torrents.

You can explicitly take a torrent out of upload only mode using set_upload_mode().

max_suggest_pieces 66 max_suggest_pieces is the max number of suggested piece indices received from a peer that's remembered. If a peer floods suggest messages, this limit prevents libtorrent from using too much RAM. It defaults to 10.
local_service_announce_interval 67 local_service_announce_interval is the time between local network announces for a torrent. By default, when local service discovery is enabled a torrent announces itself every 5 minutes. This interval is specified in seconds.
dht_announce_interval 68 dht_announce_interval is the number of seconds between announcing torrents to the distributed hash table (DHT).
udp_tracker_token_expiry 69 udp_tracker_token_expiry is the number of seconds libtorrent will keep UDP tracker connection tokens around for. This is specified to be 60 seconds, and defaults to that. The higher this value is, the fewer packets have to be sent to the UDP tracker. In order for higher values to work, the tracker needs to be configured to match the expiration time for tokens.
default_cache_min_age 70 default_cache_min_age is the minimum number of seconds any read cache line is kept in the cache. This defaults to one second but may be greater if guided_read_cache is enabled. Having a lower bound on the time a cache line stays in the cache is an attempt to avoid swapping the same pieces in and out of the cache in case there is a shortage of spare cache space.
num_optimistic_unchoke_slots 71 num_optimistic_unchoke_slots is the number of optimistic unchoke slots to use. It defaults to 0, which means automatic. Having a higher number of optimistic unchoke slots mean you will find the good peers faster but with the trade-off to use up more bandwidth. When this is set to 0, libtorrent opens up 20% of your allowed upload slots as optimistic unchoke slots.
default_est_reciprocation_rate 72

default_est_reciprocation_rate is the assumed reciprocation rate from peers when using the BitTyrant choker. This defaults to 14 kiB/s. If set too high, you will over-estimate your peers and be more altruistic while finding the true reciprocation rate, if it's set too low, you'll be too stingy and waste finding the true reciprocation rate.

increase_est_reciprocation_rate specifies how many percent the estimated reciprocation rate should be increased by each unchoke interval a peer is still choking us back. This defaults to 20%. This only applies to the BitTyrant choker.

decrease_est_reciprocation_rate specifies how many percent the estimated reciprocation rate should be decreased by each unchoke interval a peer unchokes us. This default to 3%. This only applies to the BitTyrant choker.

increase_est_reciprocation_rate 73  
decrease_est_reciprocation_rate 74  
max_pex_peers 75 the max number of peers we accept from pex messages from a single peer. this limits the number of concurrent peers any of our peers claims to be connected to. If they claim to be connected to more than this, we'll ignore any peer that exceeds this limit
tick_interval 76 tick_interval specifies the number of milliseconds between internal ticks. This is the frequency with which bandwidth quota is distributed to peers. It should not be more than one second (i.e. 1000 ms). Setting this to a low value (around 100) means higher resolution bandwidth quota distribution, setting it to a higher value saves CPU cycles.
share_mode_target 77 share_mode_target specifies the target share ratio for share mode torrents. This defaults to 3, meaning we'll try to upload 3 times as much as we download. Setting this very high, will make it very conservative and you might end up not downloading anything ever (and not affecting your share ratio). It does not make any sense to set this any lower than 2. For instance, if only 3 peers need to download the rarest piece, it's impossible to download a single piece and upload it more than 3 times. If the share_mode_target is set to more than 3, nothing is downloaded.
upload_rate_limit 78

upload_rate_limit and download_rate_limit sets the session-global limits of upload and download rate limits, in bytes per second. By default peers on the local network are not rate limited.

A value of 0 means unlimited.

For fine grained control over rate limits, including making them apply to local peers, see peer classes.

download_rate_limit 79  
deprecated3 80  
deprecated4 81  
dht_upload_rate_limit 82 dht_upload_rate_limit sets the rate limit on the DHT. This is specified in bytes per second and defaults to 4000. For busy boxes with lots of torrents that requires more DHT traffic, this should be raised.
unchoke_slots_limit 83 unchoke_slots_limit is the max number of unchoked peers in the session. The number of unchoke slots may be ignored depending on what choking_algorithm is set to.
deprecated5 84  
connections_limit 85 connections_limit sets a global limit on the number of connections opened. The number of connections is set to a hard minimum of at least two per torrent, so if you set a too low connections limit, and open too many torrents, the limit will not be met.
connections_slack 86 connections_slack is the the number of incoming connections exceeding the connection limit to accept in order to potentially replace existing ones.
utp_target_delay 87

utp_target_delay is the target delay for uTP sockets in milliseconds. A high value will make uTP connections more aggressive and cause longer queues in the upload bottleneck. It cannot be too low, since the noise in the measurements would cause it to send too slow. The default is 50 milliseconds. utp_gain_factor is the number of bytes the uTP congestion window can increase at the most in one RTT. This defaults to 300 bytes. If this is set too high, the congestion controller reacts too hard to noise and will not be stable, if it's set too low, it will react slow to congestion and not back off as fast.

utp_min_timeout is the shortest allowed uTP socket timeout, specified in milliseconds. This defaults to 500 milliseconds. The timeout depends on the RTT of the connection, but is never smaller than this value. A connection times out when every packet in a window is lost, or when a packet is lost twice in a row (i.e. the resent packet is lost as well).

The shorter the timeout is, the faster the connection will recover from this situation, assuming the RTT is low enough. utp_syn_resends is the number of SYN packets that are sent (and timed out) before giving up and closing the socket. utp_num_resends is the number of times a packet is sent (and lossed or timed out) before giving up and closing the connection. utp_connect_timeout is the number of milliseconds of timeout for the initial SYN packet for uTP connections. For each timed out packet (in a row), the timeout is doubled. utp_loss_multiplier controls how the congestion window is changed when a packet loss is experienced. It's specified as a percentage multiplier for cwnd. By default it's set to 50 (i.e. cut in half). Do not change this value unless you know what you're doing. Never set it higher than 100.

utp_gain_factor 88  
utp_min_timeout 89  
utp_syn_resends 90  
utp_fin_resends 91  
utp_num_resends 92  
utp_connect_timeout 93  
deprecated6 94  
utp_loss_multiplier 95  
mixed_mode_algorithm 96 The mixed_mode_algorithm determines how to treat TCP connections when there are uTP connections. Since uTP is designed to yield to TCP, there's an inherent problem when using swarms that have both TCP and uTP connections. If nothing is done, uTP connections would often be starved out for bandwidth by the TCP connections. This mode is prefer_tcp. The peer_proportional mode simply looks at the current throughput and rate limits all TCP connections to their proportional share based on how many of the connections are TCP. This works best if uTP connections are not rate limited by the global rate limiter (which they aren't by default).
listen_queue_size 97 listen_queue_size is the value passed in to listen() for the listen socket. It is the number of outstanding incoming connections to queue up while we're not actively waiting for a connection to be accepted. The default is 5 which should be sufficient for any normal client. If this is a high performance server which expects to receive a lot of connections, or used in a simulator or test, it might make sense to raise this number. It will not take affect until the listen_interfaces settings is updated.
torrent_connect_boost 98 torrent_connect_boost is the number of peers to try to connect to immediately when the first tracker response is received for a torrent. This is a boost to given to new torrents to accelerate them starting up. The normal connect scheduler is run once every second, this allows peers to be connected immediately instead of waiting for the session tick to trigger connections. This may not be set higher than 255.
alert_queue_size 99 alert_queue_size is the maximum number of alerts queued up internally. If alerts are not popped, the queue will eventually fill up to this level.
max_metadata_size 100 max_metadata_size is the maximum allowed size (in bytes) to be received by the metadata extension, i.e. magnet links.
deprecated9 101  
checking_mem_usage 102 the number of blocks to keep outstanding at any given time when checking torrents. Higher numbers give faster re-checks but uses more memory. Specified in number of 16 kiB blocks
predictive_piece_announce 103 if set to > 0, pieces will be announced to other peers before they are fully downloaded (and before they are hash checked). The intention is to gain 1.5 potential round trip times per downloaded piece. When non-zero, this indicates how many milliseconds in advance pieces should be announced, before they are expected to be completed.
aio_threads 104 for some aio back-ends, aio_threads specifies the number of io-threads to use, and aio_max the max number of outstanding jobs.
aio_max 105  
network_threads 106 network_threads is the number of threads to use to call async_write_some (i.e. send) on peer connection sockets. When seeding at extremely high rates, this may become a bottleneck, and setting this to 2 or more may parallelize that cost. When using SSL torrents, all encryption for outgoing traffic is done within the socket send functions, and this will help parallelizing the cost of SSL encryption as well.
ssl_listen 107 ssl_listen sets the listen port for SSL connections. If this is set to 0, no SSL listen port is opened. Otherwise a socket is opened on this port. This setting is only taken into account when opening the regular listen port, and won't re-open the listen socket simply by changing this setting.
tracker_backoff 108

tracker_backoff determines how aggressively to back off from retrying failing trackers. This value determines x in the following formula, determining the number of seconds to wait until the next retry:

delay = 5 + 5 * x / 100 * fails^2

This setting may be useful to make libtorrent more or less aggressive in hitting trackers.

share_ratio_limit 109 when a seeding torrent reaches either the share ratio (bytes up / bytes down) or the seed time ratio (seconds as seed / seconds as downloader) or the seed time limit (seconds as seed) it is considered done, and it will leave room for other torrents. These are specified as percentages. Torrents that are considered done will still be allowed to be seeded, they just won't have priority anymore. For more, see queuing.
seed_time_ratio_limit 110  
peer_turnover 111 peer_turnover is the percentage of peers to disconnect every turnover peer_turnover_interval (if we're at the peer limit), this is specified in percent when we are connected to more than limit * peer_turnover_cutoff peers disconnect peer_turnover fraction of the peers. It is specified in percent peer_turnover_interval is the interval (in seconds) between optimistic disconnects if the disconnects happen and how many peers are disconnected is controlled by peer_turnover and peer_turnover_cutoff
peer_turnover_cutoff 112  
peer_turnover_interval 113  
connect_seed_every_n_download 114 this setting controls the priority of downloading torrents over seeding or finished torrents when it comes to making peer connections. Peer connections are throttled by the connection_speed and the half-open connection limit. This makes peer connections a limited resource. Torrents that still have pieces to download are prioritized by default, to avoid having many seeding torrents use most of the connection attempts and only give one peer every now and then to the downloading torrent. libtorrent will loop over the downloading torrents to connect a peer each, and every n:th connection attempt, a finished torrent is picked to be allowed to connect to a peer. This setting controls n.
max_http_recv_buffer_size 115 the max number of bytes to allow an HTTP response to be when announcing to trackers or downloading .torrent files via the url provided in add_torrent_params.
max_retry_port_bind 116 if binding to a specific port fails, should the port be incremented by one and tried again? This setting specifies how many times to retry a failed port bind
alert_mask 117 a bitmask combining flags from alert::category_t defining which kinds of alerts to receive
out_enc_policy 118

control the settings for incoming and outgoing connections respectively. see enc_policy enum for the available options. Keep in mind that protocol encryption degrades performance in several respects:

  1. It prevents "zero copy" disk buffers being sent to peers, since each peer needs to mutate the data (i.e. encrypt it) the data must be copied per peer connection rather than sending the same buffer to multiple peers.
  2. The encryption itself requires more CPU than plain bittorrent protocol. The highest cost is the Diffie Hellman exchange on connection setup.
  3. The encryption handshake adds several round-trips to the connection setup, and delays transferring data.
in_enc_policy 119  
allowed_enc_level 120 determines the encryption level of the connections. This setting will adjust which encryption scheme is offered to the other peer, as well as which encryption scheme is selected by the client. See enc_level enum for options.
inactive_down_rate 121 the download and upload rate limits for a torrent to be considered active by the queuing mechanism. A torrent whose download rate is less than inactive_down_rate and whose upload rate is less than inactive_up_rate for auto_manage_startup seconds, is considered inactive, and another queued torrent may be started. This logic is disabled if dont_count_slow_torrents is false.
inactive_up_rate 122  
proxy_type 123 proxy to use, defaults to none. see proxy_type_t.
proxy_port 124 the port of the proxy server
i2p_port 125 sets the i2p SAM bridge port to connect to. set the hostname with the i2p_hostname setting.
cache_size_volatile 126 this determines the max number of volatile disk cache blocks. If the number of volatile blocks exceed this limit, other volatile blocks will start to be evicted. A disk cache block is volatile if it has low priority, and should be one of the first blocks to be evicted under pressure. For instance, blocks pulled into the cache as the result of calculating a piece hash are volatile. These blocks don't represent potential interest among peers, so the value of keeping them in the cache is limited.
urlseed_max_request_bytes 127 The maximum request range of an url seed in bytes. This value defines the largest possible sequential web seed request. Default is 16 * 1024 * 1024. Lower values are possible but will be ignored if they are lower then piece size. This value should be related to your download speed to prevent libtorrent from creating too many expensive http requests per second. You can select a value as high as you want but keep in mind that libtorrent can't create parallel requests if the first request did already select the whole file. If you combine bittorrent seeds with web seeds and pick strategies like rarest first you may find your web seed requests split into smaller parts because we don't download already picked pieces twice.
web_seed_name_lookup_retry 128 time to wait until a new retry of a web seed name lookup
close_file_interval 129 the number of seconds between closing the file opened the longest ago. 0 means to disable the feature. The purpose of this is to periodically close files to trigger the operating system flushing disk cache. Specifically it has been observed to be required on windows to not have the disk cache grow indefinitely. This defaults to 120 seconds on windows, and disabled on other systems.
utp_cwnd_reduce_timer 130 When uTP experiences packet loss, it will reduce the congestion window, and not reduce it again for this many milliseconds, even if experiencing another lost packet.
max_int_setting_internal 131  

enum settings_counts_t

Declared in "libtorrent/settings_pack.hpp"

name value description
num_string_settings    
num_bool_settings    
num_int_settings    

enum suggest_mode_t

Declared in "libtorrent/settings_pack.hpp"

name value description
no_piece_suggestions 0  
suggest_read_cache 1  

enum choking_algorithm_t

Declared in "libtorrent/settings_pack.hpp"

name value description
fixed_slots_choker 0  
rate_based_choker 2  
bittyrant_choker 3  

enum seed_choking_algorithm_t

Declared in "libtorrent/settings_pack.hpp"

name value description
round_robin 0  
fastest_upload 1  
anti_leech 2  

enum io_buffer_mode_t

Declared in "libtorrent/settings_pack.hpp"

name value description
enable_os_cache 0  
deprecated 1  
disable_os_cache 2  

enum bandwidth_mixed_algo_t

Declared in "libtorrent/settings_pack.hpp"

name value description
prefer_tcp 0 disables the mixed mode bandwidth balancing
peer_proportional 1 does not throttle uTP, throttles TCP to the same proportion of throughput as there are TCP connections

enum enc_policy

Declared in "libtorrent/settings_pack.hpp"

name value description
pe_forced 0 Only encrypted connections are allowed. Incoming connections that are not encrypted are closed and if the encrypted outgoing connection fails, a non-encrypted retry will not be made.
pe_enabled 1 encrypted connections are enabled, but non-encrypted connections are allowed. An incoming non-encrypted connection will be accepted, and if an outgoing encrypted connection fails, a non- encrypted connection will be tried.
pe_disabled 2 only non-encrypted connections are allowed.

enum enc_level

Declared in "libtorrent/settings_pack.hpp"

name value description
pe_plaintext 1 use only plaintext encryption
pe_rc4 2 use only rc4 encryption
pe_both 3 allow both

enum proxy_type_t

Declared in "libtorrent/settings_pack.hpp"

name value description
none 0 This is the default, no proxy server is used, all other fields are ignored.
socks4 1 The server is assumed to be a SOCKS4 server that requires a username.
socks5 2 The server is assumed to be a SOCKS5 server (RFC 1928) that does not require any authentication. The username and password are ignored.
socks5_pw 3 The server is assumed to be a SOCKS5 server that supports plain text username and password authentication (RFC 1929). The username and password specified may be sent to the proxy if it requires.
http 4 The server is assumed to be an HTTP proxy. If the transport used for the connection is non-HTTP, the server is assumed to support the CONNECT method. i.e. for web seeds and HTTP trackers, a plain proxy will suffice. The proxy is assumed to not require authorization. The username and password will not be used.
http_pw 5 The server is assumed to be an HTTP proxy that requires user authorization. The username and password will be sent to the proxy.
i2p_proxy 6 route through a i2p SAM proxy

min_memory_usage() high_performance_seed()

Declared in "libtorrent/session.hpp"

void min_memory_usage (settings_pack& set);
void high_performance_seed (settings_pack& set);

The default values of the session settings are set for a regular bittorrent client running on a desktop system. There are functions that can set the session settings to pre set settings for other environments. These can be used for the basis, and should be tweaked to fit your needs better.

min_memory_usage returns settings that will use the minimal amount of RAM, at the potential expense of upload and download performance. It adjusts the socket buffer sizes, disables the disk cache, lowers the send buffer watermarks so that each connection only has at most one block in use at any one time. It lowers the outstanding blocks send to the disk I/O thread so that connections only have one block waiting to be flushed to disk at any given time. It lowers the max number of peers in the peer list for torrents. It performs multiple smaller reads when it hashes pieces, instead of reading it all into memory before hashing.

This configuration is inteded to be the starting point for embedded devices. It will significantly reduce memory usage.

high_performance_seed returns settings optimized for a seed box, serving many peers and that doesn't do any downloading. It has a 128 MB disk cache and has a limit of 400 files in its file pool. It support fast upload rates by allowing large send buffers.

setting_by_name() name_for_setting()

Declared in "libtorrent/settings_pack.hpp"

char const* name_for_setting (int s);
int setting_by_name (std::string const& name);

default_settings()

Declared in "libtorrent/settings_pack.hpp"

settings_pack default_settings ();

returns a settings_pack with every setting set to its default value

home

Bdecoding

bdecode_node

Declared in "libtorrent/bdecode.hpp"

Sometimes it's important to get a non-owning reference to the root node ( to be able to copy it as a reference for instance). For that, use the non_owning() member function.

There are 5 different types of nodes, see type_t.

struct bdecode_node
{
   friend int bdecode (char const* start, char const* end, bdecode_node& ret
      , error_code& ec, int* error_pos, int depth_limit
      , int token_limit);
   bdecode_node ();
   bdecode_node (bdecode_node const&);
   bdecode_node& operator= (bdecode_node const&);
   type_t type () const;
   operator bool () const;
   bdecode_node non_owning () const;
   std::pair<char const*, int> data_section () const;
   bdecode_node list_at (int i) const;
   std::string list_string_value_at (int i
      , char const* default_val = "") const;
   int list_size () const;
   boost::int64_t list_int_value_at (int i
      , boost::int64_t default_val = 0) const;
   std::string dict_find_string_value (char const* key
      , char const* default_value = "") const;
   bdecode_node dict_find_string (char const* key) const;
   int dict_size () const;
   boost::int64_t dict_find_int_value (char const* key
      , boost::int64_t default_val = 0) const;
   bdecode_node dict_find (std::string key) const;
   bdecode_node dict_find_list (char const* key) const;
   bdecode_node dict_find (char const* key) const;
   bdecode_node dict_find_dict (std::string key) const;
   bdecode_node dict_find_dict (char const* key) const;
   std::pair<std::string, bdecode_node> dict_at (int i) const;
   bdecode_node dict_find_int (char const* key) const;
   boost::int64_t int_value () const;
   int string_length () const;
   std::string string_value () const;
   char const* string_ptr () const;
   void clear ();
   void swap (bdecode_node& n);
   void reserve (int tokens);
   void switch_underlying_buffer (char const* buf);

   enum type_t
   {
      none_t,
      dict_t,
      list_t,
      string_t,
      int_t,
   };
};

bdecode_node()

bdecode_node ();

creates a default constructed node, it will have the type none_t.

bdecode_node() operator=()

bdecode_node (bdecode_node const&);
bdecode_node& operator= (bdecode_node const&);

For owning nodes, the copy will create a copy of the tree, but the underlying buffer remains the same.

type()

type_t type () const;

the type of this node. See type_t.

bool()

operator bool () const;

returns true if type() != none_t.

non_owning()

bdecode_node non_owning () const;

return a non-owning reference to this node. This is useful to refer to the root node without copying it in assignments.

data_section()

std::pair<char const*, int> data_section () const;

returns the buffer and length of the section in the original bencoded buffer where this node is defined. For a dictionary for instance, this starts with d and ends with e, and has all the content of the dictionary in between.

list_at() list_string_value_at() list_int_value_at() list_size()

bdecode_node list_at (int i) const;
std::string list_string_value_at (int i
      , char const* default_val = "") const;
int list_size () const;
boost::int64_t list_int_value_at (int i
      , boost::int64_t default_val = 0) const;

functions with the list_ prefix operate on lists. These functions are only valid if type() == list_t. list_at() returns the item in the list at index i. i may not be greater than or equal to the size of the list. size() returns the size of the list.

dict_size() dict_find_dict() dict_find_string() dict_find_int() dict_at() dict_find_list() dict_find_int_value() dict_find() dict_find_string_value()

std::string dict_find_string_value (char const* key
      , char const* default_value = "") const;
bdecode_node dict_find_string (char const* key) const;
int dict_size () const;
boost::int64_t dict_find_int_value (char const* key
      , boost::int64_t default_val = 0) const;
bdecode_node dict_find (std::string key) const;
bdecode_node dict_find_list (char const* key) const;
bdecode_node dict_find (char const* key) const;
bdecode_node dict_find_dict (std::string key) const;
bdecode_node dict_find_dict (char const* key) const;
std::pair<std::string, bdecode_node> dict_at (int i) const;
bdecode_node dict_find_int (char const* key) const;

Functions with the dict_ prefix operates on dictionaries. They are only valid if type() == dict_t. In case a key you're looking up contains a 0 byte, you cannot use the null-terminated string overloads, but have to use std::string instead. dict_find_list will return a valid bdecode_node if the key is found _and_ it is a list. Otherwise it will return a default-constructed bdecode_node.

Functions with the _value suffix return the value of the node directly, rather than the nodes. In case the node is not found, or it has a different type, a default value is returned (which can be specified).

int_value()

boost::int64_t int_value () const;

this function is only valid if type() == int_t. It returns the value of the integer.

string_ptr() string_length() string_value()

int string_length () const;
std::string string_value () const;
char const* string_ptr () const;

these functions are only valid if type() == string_t. They return the string values. Note that string_ptr() is not null-terminated. string_length() returns the number of bytes in the string.

clear()

void clear ();

resets the bdecoded_node to a default constructed state. If this is an owning node, the tree is freed and all child nodes are invalidated.

swap()

void swap (bdecode_node& n);

Swap contents.

reserve()

void reserve (int tokens);

preallocate memory for the specified numbers of tokens. This is useful if you know approximately how many tokens are in the file you are about to parse. Doing so will save realloc operations while parsing. You should only call this on the root node, before passing it in to bdecode().

switch_underlying_buffer()

void switch_underlying_buffer (char const* buf);

this buffer MUST be identical to the one originally parsed. This operation is only defined on owning root nodes, i.e. the one passed in to decode().

enum type_t

Declared in "libtorrent/bdecode.hpp"

name value description
none_t 0 uninitialized or default constructed. This is also used to indicate that a node was not found in some cases.
dict_t 1 a dictionary node. The dict_find_ functions are valid.
list_t 2 a list node. The list_ functions are valid.
string_t 3 a string node, the string_ functions are valid.
int_t 4 an integer node. The int_ functions are valid.

print_entry()

Declared in "libtorrent/bdecode.hpp"

std::string print_entry (bdecode_node const& e
   , bool single_line = false, int indent = 0);

print the bencoded structure in a human-readable format to a string that's returned.

bdecode()

Declared in "libtorrent/bdecode.hpp"

int bdecode (char const* start, char const* end, bdecode_node& ret
   , error_code& ec, int* error_pos = 0, int depth_limit = 100
   , int token_limit = 1000000);

This function decodes/parses bdecoded data (for example a .torrent file). The data structure is returned in the ret argument. the buffer to parse is specified by the start of the buffer as well as the end, i.e. one byte past the end. If the buffer fails to parse, the function returns a non-zero value and fills in ec with the error code. The optional argument error_pos, if set to non-null, will be set to the byte offset into the buffer where the parse failure occurred.

depth_limit specifies the max number of nested lists or dictionaries are allowed in the data structure. (This affects the stack usage of the function, be careful not to set it too high).

token_limit is the max number of tokens allowed to be parsed from the buffer. This is simply a sanity check to not have unbounded memory usage.

The resulting bdecode_node is an owning node. That means it will be holding the whole parsed tree. When iterating lists and dictionaries, those bdecode_node objects will simply have references to the root or owning bdecode_node. If the root node is destructed, all other nodes that refer to anything in that tree become invalid.

However, the underlying buffer passed in to this function (start, end) must also remain valid while the bdecoded tree is used. The parsed tree produced by this function does not copy any data out of the buffer, but simply produces references back into it.

home

ed25519

ed25519_create_seed()

Declared in "libtorrent/ed25519.hpp"

void ed25519_create_seed (unsigned char *seed);

ed25519_key_exchange() ed25519_create_keypair() ed25519_verify() ed25519_sign() ed25519_add_scalar()

Declared in "libtorrent/ed25519.hpp"

void ed25519_add_scalar (unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar);
void ed25519_key_exchange (unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key);
void ed25519_create_keypair (unsigned char *public_key, unsigned char *private_key, const unsigned char *seed);
int ed25519_verify (const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *private_key);
void ed25519_sign (unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key);
libtorrent-rasterbar-1.1.13/docs/stats_counters.rst000066400000000000000000001643421351156116000225170ustar00rootroot00000000000000.. _peer.error_peers: .. _peer.disconnected_peers: .. raw:: html +-------------------------+---------+ | name | type | +=========================+=========+ | peer.error_peers | counter | +-------------------------+---------+ | peer.disconnected_peers | counter | +-------------------------+---------+ ``error_peers`` is the total number of peer disconnects caused by an error (not initiated by this client) and disconnected initiated by this client (``disconnected_peers``). .. _peer.eof_peers: .. _peer.connreset_peers: .. _peer.connrefused_peers: .. _peer.connaborted_peers: .. _peer.notconnected_peers: .. _peer.perm_peers: .. _peer.buffer_peers: .. _peer.unreachable_peers: .. _peer.broken_pipe_peers: .. _peer.addrinuse_peers: .. _peer.no_access_peers: .. _peer.invalid_arg_peers: .. _peer.aborted_peers: .. raw:: html +-------------------------+---------+ | name | type | +=========================+=========+ | peer.eof_peers | counter | +-------------------------+---------+ | peer.connreset_peers | counter | +-------------------------+---------+ | peer.connrefused_peers | counter | +-------------------------+---------+ | peer.connaborted_peers | counter | +-------------------------+---------+ | peer.notconnected_peers | counter | +-------------------------+---------+ | peer.perm_peers | counter | +-------------------------+---------+ | peer.buffer_peers | counter | +-------------------------+---------+ | peer.unreachable_peers | counter | +-------------------------+---------+ | peer.broken_pipe_peers | counter | +-------------------------+---------+ | peer.addrinuse_peers | counter | +-------------------------+---------+ | peer.no_access_peers | counter | +-------------------------+---------+ | peer.invalid_arg_peers | counter | +-------------------------+---------+ | peer.aborted_peers | counter | +-------------------------+---------+ these counters break down the peer errors into more specific categories. These errors are what the underlying transport reported (i.e. TCP or uTP) .. _peer.piece_requests: .. _peer.max_piece_requests: .. _peer.invalid_piece_requests: .. _peer.choked_piece_requests: .. _peer.cancelled_piece_requests: .. _peer.piece_rejects: .. raw:: html +-------------------------------+---------+ | name | type | +===============================+=========+ | peer.piece_requests | counter | +-------------------------------+---------+ | peer.max_piece_requests | counter | +-------------------------------+---------+ | peer.invalid_piece_requests | counter | +-------------------------------+---------+ | peer.choked_piece_requests | counter | +-------------------------------+---------+ | peer.cancelled_piece_requests | counter | +-------------------------------+---------+ | peer.piece_rejects | counter | +-------------------------------+---------+ the total number of incoming piece requests we've received followed by the number of rejected piece requests for various reasons. max_piece_requests mean we already had too many outstanding requests from this peer, so we rejected it. cancelled_piece_requests are ones where the other end explicitly asked for the piece to be rejected. .. _peer.error_incoming_peers: .. _peer.error_outgoing_peers: .. raw:: html +---------------------------+---------+ | name | type | +===========================+=========+ | peer.error_incoming_peers | counter | +---------------------------+---------+ | peer.error_outgoing_peers | counter | +---------------------------+---------+ these counters break down the peer errors into whether they happen on incoming or outgoing peers. .. _peer.error_rc4_peers: .. _peer.error_encrypted_peers: .. raw:: html +----------------------------+---------+ | name | type | +============================+=========+ | peer.error_rc4_peers | counter | +----------------------------+---------+ | peer.error_encrypted_peers | counter | +----------------------------+---------+ these counters break down the peer errors into whether they happen on encrypted peers (just encrypted handshake) and rc4 peers (full stream encryption). These can indicate whether encrypted peers are more or less likely to fail .. _peer.error_tcp_peers: .. _peer.error_utp_peers: .. raw:: html +----------------------+---------+ | name | type | +======================+=========+ | peer.error_tcp_peers | counter | +----------------------+---------+ | peer.error_utp_peers | counter | +----------------------+---------+ these counters break down the peer errors into whether they happen on uTP peers or TCP peers. these may indicate whether one protocol is more error prone .. _peer.connect_timeouts: .. _peer.uninteresting_peers: .. _peer.timeout_peers: .. _peer.no_memory_peers: .. _peer.too_many_peers: .. _peer.transport_timeout_peers: .. _peer.num_banned_peers: .. _peer.banned_for_hash_failure: .. _peer.connection_attempts: .. _peer.connection_attempt_loops: .. _peer.incoming_connections: .. raw:: html +-------------------------------+---------+ | name | type | +===============================+=========+ | peer.connect_timeouts | counter | +-------------------------------+---------+ | peer.uninteresting_peers | counter | +-------------------------------+---------+ | peer.timeout_peers | counter | +-------------------------------+---------+ | peer.no_memory_peers | counter | +-------------------------------+---------+ | peer.too_many_peers | counter | +-------------------------------+---------+ | peer.transport_timeout_peers | counter | +-------------------------------+---------+ | peer.num_banned_peers | counter | +-------------------------------+---------+ | peer.banned_for_hash_failure | counter | +-------------------------------+---------+ | peer.connection_attempts | counter | +-------------------------------+---------+ | peer.connection_attempt_loops | counter | +-------------------------------+---------+ | peer.incoming_connections | counter | +-------------------------------+---------+ these counters break down the reasons to disconnect peers. .. _peer.num_tcp_peers: .. _peer.num_socks5_peers: .. _peer.num_http_proxy_peers: .. _peer.num_utp_peers: .. _peer.num_i2p_peers: .. _peer.num_ssl_peers: .. _peer.num_ssl_socks5_peers: .. _peer.num_ssl_http_proxy_peers: .. _peer.num_ssl_utp_peers: .. _peer.num_peers_half_open: .. _peer.num_peers_connected: .. _peer.num_peers_up_interested: .. _peer.num_peers_down_interested: .. _peer.num_peers_up_unchoked_all: .. _peer.num_peers_up_unchoked_optimistic: .. _peer.num_peers_up_unchoked: .. _peer.num_peers_down_unchoked: .. _peer.num_peers_up_requests: .. _peer.num_peers_down_requests: .. _peer.num_peers_end_game: .. _peer.num_peers_up_disk: .. _peer.num_peers_down_disk: .. raw:: html +---------------------------------------+-------+ | name | type | +=======================================+=======+ | peer.num_tcp_peers | gauge | +---------------------------------------+-------+ | peer.num_socks5_peers | gauge | +---------------------------------------+-------+ | peer.num_http_proxy_peers | gauge | +---------------------------------------+-------+ | peer.num_utp_peers | gauge | +---------------------------------------+-------+ | peer.num_i2p_peers | gauge | +---------------------------------------+-------+ | peer.num_ssl_peers | gauge | +---------------------------------------+-------+ | peer.num_ssl_socks5_peers | gauge | +---------------------------------------+-------+ | peer.num_ssl_http_proxy_peers | gauge | +---------------------------------------+-------+ | peer.num_ssl_utp_peers | gauge | +---------------------------------------+-------+ | peer.num_peers_half_open | gauge | +---------------------------------------+-------+ | peer.num_peers_connected | gauge | +---------------------------------------+-------+ | peer.num_peers_up_interested | gauge | +---------------------------------------+-------+ | peer.num_peers_down_interested | gauge | +---------------------------------------+-------+ | peer.num_peers_up_unchoked_all | gauge | +---------------------------------------+-------+ | peer.num_peers_up_unchoked_optimistic | gauge | +---------------------------------------+-------+ | peer.num_peers_up_unchoked | gauge | +---------------------------------------+-------+ | peer.num_peers_down_unchoked | gauge | +---------------------------------------+-------+ | peer.num_peers_up_requests | gauge | +---------------------------------------+-------+ | peer.num_peers_down_requests | gauge | +---------------------------------------+-------+ | peer.num_peers_end_game | gauge | +---------------------------------------+-------+ | peer.num_peers_up_disk | gauge | +---------------------------------------+-------+ | peer.num_peers_down_disk | gauge | +---------------------------------------+-------+ the number of peer connections for each kind of socket. these counts include half-open (connecting) peers. ``num_peers_up_unchoked_all`` is the total number of unchoked peers, whereas ``num_peers_up_unchoked`` only are unchoked peers that count against the limit (i.e. excluding peers that are unchoked because the limit doesn't apply to them). ``num_peers_up_unchoked_optimistic`` is the number of optimistically unchoked peers. .. _net.on_read_counter: .. _net.on_write_counter: .. _net.on_tick_counter: .. _net.on_lsd_counter: .. _net.on_lsd_peer_counter: .. _net.on_udp_counter: .. _net.on_accept_counter: .. _net.on_disk_queue_counter: .. _net.on_disk_counter: .. raw:: html +---------------------------+---------+ | name | type | +===========================+=========+ | net.on_read_counter | counter | +---------------------------+---------+ | net.on_write_counter | counter | +---------------------------+---------+ | net.on_tick_counter | counter | +---------------------------+---------+ | net.on_lsd_counter | counter | +---------------------------+---------+ | net.on_lsd_peer_counter | counter | +---------------------------+---------+ | net.on_udp_counter | counter | +---------------------------+---------+ | net.on_accept_counter | counter | +---------------------------+---------+ | net.on_disk_queue_counter | counter | +---------------------------+---------+ | net.on_disk_counter | counter | +---------------------------+---------+ These counters count the number of times the network thread wakes up for each respective reason. If these counters are very large, it may indicate a performance issue, causing the network thread to wake up too ofte, wasting CPU. mitigate it by increasing buffers and limits for the specific trigger that wakes up the thread. .. _net.sent_payload_bytes: .. _net.sent_bytes: .. _net.sent_ip_overhead_bytes: .. _net.sent_tracker_bytes: .. _net.recv_payload_bytes: .. _net.recv_bytes: .. _net.recv_ip_overhead_bytes: .. _net.recv_tracker_bytes: .. raw:: html +----------------------------+---------+ | name | type | +============================+=========+ | net.sent_payload_bytes | counter | +----------------------------+---------+ | net.sent_bytes | counter | +----------------------------+---------+ | net.sent_ip_overhead_bytes | counter | +----------------------------+---------+ | net.sent_tracker_bytes | counter | +----------------------------+---------+ | net.recv_payload_bytes | counter | +----------------------------+---------+ | net.recv_bytes | counter | +----------------------------+---------+ | net.recv_ip_overhead_bytes | counter | +----------------------------+---------+ | net.recv_tracker_bytes | counter | +----------------------------+---------+ total number of bytes sent and received by the session .. _net.limiter_up_queue: .. _net.limiter_down_queue: .. raw:: html +------------------------+-------+ | name | type | +========================+=======+ | net.limiter_up_queue | gauge | +------------------------+-------+ | net.limiter_down_queue | gauge | +------------------------+-------+ the number of sockets currently waiting for upload and download bandwidht from the rate limiter. .. _net.limiter_up_bytes: .. _net.limiter_down_bytes: .. raw:: html +------------------------+-------+ | name | type | +========================+=======+ | net.limiter_up_bytes | gauge | +------------------------+-------+ | net.limiter_down_bytes | gauge | +------------------------+-------+ the number of upload and download bytes waiting to be handed out from the rate limiter. .. _net.recv_failed_bytes: .. raw:: html +-----------------------+---------+ | name | type | +=======================+=========+ | net.recv_failed_bytes | counter | +-----------------------+---------+ the number of bytes downloaded that had to be discarded because they failed the hash check .. _net.recv_redundant_bytes: .. raw:: html +--------------------------+---------+ | name | type | +==========================+=========+ | net.recv_redundant_bytes | counter | +--------------------------+---------+ the number of downloaded bytes that were discarded because they were downloaded multiple times (from different peers) .. _net.has_incoming_connections: .. raw:: html +------------------------------+-------+ | name | type | +==============================+=======+ | net.has_incoming_connections | gauge | +------------------------------+-------+ 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. .. _ses.num_checking_torrents: .. _ses.num_stopped_torrents: .. _ses.num_upload_only_torrents: .. _ses.num_downloading_torrents: .. _ses.num_seeding_torrents: .. _ses.num_queued_seeding_torrents: .. _ses.num_queued_download_torrents: .. _ses.num_error_torrents: .. raw:: html +----------------------------------+-------+ | name | type | +==================================+=======+ | ses.num_checking_torrents | gauge | +----------------------------------+-------+ | ses.num_stopped_torrents | gauge | +----------------------------------+-------+ | ses.num_upload_only_torrents | gauge | +----------------------------------+-------+ | ses.num_downloading_torrents | gauge | +----------------------------------+-------+ | ses.num_seeding_torrents | gauge | +----------------------------------+-------+ | ses.num_queued_seeding_torrents | gauge | +----------------------------------+-------+ | ses.num_queued_download_torrents | gauge | +----------------------------------+-------+ | ses.num_error_torrents | gauge | +----------------------------------+-------+ these gauges count the number of torrents in different states. Each torrent only belongs to one of these states. For torrents that could belong to multiple of these, the most prominent in picked. For instance, a torrent with an error counts as an error-torrent, regardless of its other state. .. _ses.non_filter_torrents: .. raw:: html +-------------------------+-------+ | name | type | +=========================+=======+ | ses.non_filter_torrents | gauge | +-------------------------+-------+ the number of torrents that don't have the IP filter applied to them. .. _ses.num_loaded_torrents: .. _ses.num_pinned_torrents: .. raw:: html +-------------------------+-------+ | name | type | +=========================+=======+ | ses.num_loaded_torrents | gauge | +-------------------------+-------+ | ses.num_pinned_torrents | gauge | +-------------------------+-------+ the number of torrents that are currently loaded .. _ses.num_piece_passed: .. _ses.num_piece_failed: .. _ses.num_have_pieces: .. _ses.num_total_pieces_added: .. raw:: html +----------------------------+---------+ | name | type | +============================+=========+ | ses.num_piece_passed | counter | +----------------------------+---------+ | ses.num_piece_failed | counter | +----------------------------+---------+ | ses.num_have_pieces | counter | +----------------------------+---------+ | ses.num_total_pieces_added | counter | +----------------------------+---------+ these count the number of times a piece has passed the hash check, the number of times a piece was successfully written to disk and the number of total possible pieces added by adding torrents. e.g. when adding a torrent with 1000 piece, num_total_pieces_added is incremented by 1000. .. _ses.torrent_evicted_counter: .. raw:: html +-----------------------------+---------+ | name | type | +=============================+=========+ | ses.torrent_evicted_counter | counter | +-----------------------------+---------+ this counts the number of times a torrent has been evicted (only applies when `dynamic loading of torrent files`_ is enabled). .. _ses.num_unchoke_slots: .. raw:: html +-----------------------+-------+ | name | type | +=======================+=======+ | ses.num_unchoke_slots | gauge | +-----------------------+-------+ the number of allowed unchoked peers .. _ses.num_incoming_choke: .. _ses.num_incoming_unchoke: .. _ses.num_incoming_interested: .. _ses.num_incoming_not_interested: .. _ses.num_incoming_have: .. _ses.num_incoming_bitfield: .. _ses.num_incoming_request: .. _ses.num_incoming_piece: .. _ses.num_incoming_cancel: .. _ses.num_incoming_dht_port: .. _ses.num_incoming_suggest: .. _ses.num_incoming_have_all: .. _ses.num_incoming_have_none: .. _ses.num_incoming_reject: .. _ses.num_incoming_allowed_fast: .. _ses.num_incoming_ext_handshake: .. _ses.num_incoming_pex: .. _ses.num_incoming_metadata: .. _ses.num_incoming_extended: .. _ses.num_outgoing_choke: .. _ses.num_outgoing_unchoke: .. _ses.num_outgoing_interested: .. _ses.num_outgoing_not_interested: .. _ses.num_outgoing_have: .. _ses.num_outgoing_bitfield: .. _ses.num_outgoing_request: .. _ses.num_outgoing_piece: .. _ses.num_outgoing_cancel: .. _ses.num_outgoing_dht_port: .. _ses.num_outgoing_suggest: .. _ses.num_outgoing_have_all: .. _ses.num_outgoing_have_none: .. _ses.num_outgoing_reject: .. _ses.num_outgoing_allowed_fast: .. _ses.num_outgoing_ext_handshake: .. _ses.num_outgoing_pex: .. _ses.num_outgoing_metadata: .. _ses.num_outgoing_extended: .. raw:: html +---------------------------------+---------+ | name | type | +=================================+=========+ | ses.num_incoming_choke | counter | +---------------------------------+---------+ | ses.num_incoming_unchoke | counter | +---------------------------------+---------+ | ses.num_incoming_interested | counter | +---------------------------------+---------+ | ses.num_incoming_not_interested | counter | +---------------------------------+---------+ | ses.num_incoming_have | counter | +---------------------------------+---------+ | ses.num_incoming_bitfield | counter | +---------------------------------+---------+ | ses.num_incoming_request | counter | +---------------------------------+---------+ | ses.num_incoming_piece | counter | +---------------------------------+---------+ | ses.num_incoming_cancel | counter | +---------------------------------+---------+ | ses.num_incoming_dht_port | counter | +---------------------------------+---------+ | ses.num_incoming_suggest | counter | +---------------------------------+---------+ | ses.num_incoming_have_all | counter | +---------------------------------+---------+ | ses.num_incoming_have_none | counter | +---------------------------------+---------+ | ses.num_incoming_reject | counter | +---------------------------------+---------+ | ses.num_incoming_allowed_fast | counter | +---------------------------------+---------+ | ses.num_incoming_ext_handshake | counter | +---------------------------------+---------+ | ses.num_incoming_pex | counter | +---------------------------------+---------+ | ses.num_incoming_metadata | counter | +---------------------------------+---------+ | ses.num_incoming_extended | counter | +---------------------------------+---------+ | ses.num_outgoing_choke | counter | +---------------------------------+---------+ | ses.num_outgoing_unchoke | counter | +---------------------------------+---------+ | ses.num_outgoing_interested | counter | +---------------------------------+---------+ | ses.num_outgoing_not_interested | counter | +---------------------------------+---------+ | ses.num_outgoing_have | counter | +---------------------------------+---------+ | ses.num_outgoing_bitfield | counter | +---------------------------------+---------+ | ses.num_outgoing_request | counter | +---------------------------------+---------+ | ses.num_outgoing_piece | counter | +---------------------------------+---------+ | ses.num_outgoing_cancel | counter | +---------------------------------+---------+ | ses.num_outgoing_dht_port | counter | +---------------------------------+---------+ | ses.num_outgoing_suggest | counter | +---------------------------------+---------+ | ses.num_outgoing_have_all | counter | +---------------------------------+---------+ | ses.num_outgoing_have_none | counter | +---------------------------------+---------+ | ses.num_outgoing_reject | counter | +---------------------------------+---------+ | ses.num_outgoing_allowed_fast | counter | +---------------------------------+---------+ | ses.num_outgoing_ext_handshake | counter | +---------------------------------+---------+ | ses.num_outgoing_pex | counter | +---------------------------------+---------+ | ses.num_outgoing_metadata | counter | +---------------------------------+---------+ | ses.num_outgoing_extended | counter | +---------------------------------+---------+ bittorrent message counters. These counters are incremented every time a message of the corresponding type is received from or sent to a bittorrent peer. .. _ses.waste_piece_timed_out: .. _ses.waste_piece_cancelled: .. _ses.waste_piece_unknown: .. _ses.waste_piece_seed: .. _ses.waste_piece_end_game: .. _ses.waste_piece_closing: .. raw:: html +---------------------------+---------+ | name | type | +===========================+=========+ | ses.waste_piece_timed_out | counter | +---------------------------+---------+ | ses.waste_piece_cancelled | counter | +---------------------------+---------+ | ses.waste_piece_unknown | counter | +---------------------------+---------+ | ses.waste_piece_seed | counter | +---------------------------+---------+ | ses.waste_piece_end_game | counter | +---------------------------+---------+ | ses.waste_piece_closing | counter | +---------------------------+---------+ the number of wasted downloaded bytes by reason of the bytes being wasted. .. _picker.piece_picker_partial_loops: .. _picker.piece_picker_suggest_loops: .. _picker.piece_picker_sequential_loops: .. _picker.piece_picker_reverse_rare_loops: .. _picker.piece_picker_rare_loops: .. _picker.piece_picker_rand_start_loops: .. _picker.piece_picker_rand_loops: .. _picker.piece_picker_busy_loops: .. raw:: html +----------------------------------------+---------+ | name | type | +========================================+=========+ | picker.piece_picker_partial_loops | counter | +----------------------------------------+---------+ | picker.piece_picker_suggest_loops | counter | +----------------------------------------+---------+ | picker.piece_picker_sequential_loops | counter | +----------------------------------------+---------+ | picker.piece_picker_reverse_rare_loops | counter | +----------------------------------------+---------+ | picker.piece_picker_rare_loops | counter | +----------------------------------------+---------+ | picker.piece_picker_rand_start_loops | counter | +----------------------------------------+---------+ | picker.piece_picker_rand_loops | counter | +----------------------------------------+---------+ | picker.piece_picker_busy_loops | counter | +----------------------------------------+---------+ the number of pieces considered while picking pieces .. _picker.reject_piece_picks: .. _picker.unchoke_piece_picks: .. _picker.incoming_redundant_piece_picks: .. _picker.incoming_piece_picks: .. _picker.end_game_piece_picks: .. _picker.snubbed_piece_picks: .. _picker.interesting_piece_picks: .. _picker.hash_fail_piece_picks: .. raw:: html +---------------------------------------+---------+ | name | type | +=======================================+=========+ | picker.reject_piece_picks | counter | +---------------------------------------+---------+ | picker.unchoke_piece_picks | counter | +---------------------------------------+---------+ | picker.incoming_redundant_piece_picks | counter | +---------------------------------------+---------+ | picker.incoming_piece_picks | counter | +---------------------------------------+---------+ | picker.end_game_piece_picks | counter | +---------------------------------------+---------+ | picker.snubbed_piece_picks | counter | +---------------------------------------+---------+ | picker.interesting_piece_picks | counter | +---------------------------------------+---------+ | picker.hash_fail_piece_picks | counter | +---------------------------------------+---------+ This breaks down the piece picks into the event that triggered it .. _disk.write_cache_blocks: .. _disk.read_cache_blocks: .. raw:: html +-------------------------+-------+ | name | type | +=========================+=======+ | disk.write_cache_blocks | gauge | +-------------------------+-------+ | disk.read_cache_blocks | gauge | +-------------------------+-------+ These gauges indicate how many blocks are currently in use as dirty disk blocks (``write_cache_blocks``) and read cache blocks, respectively. deprecates ``cache_status::read_cache_size``. The sum of these gauges deprecates ``cache_status::cache_size``. .. _disk.request_latency: .. raw:: html +----------------------+-------+ | name | type | +======================+=======+ | disk.request_latency | gauge | +----------------------+-------+ the number of microseconds it takes from receiving a request from a peer until we're sending the response back on the socket. .. _disk.pinned_blocks: .. _disk.disk_blocks_in_use: .. raw:: html +-------------------------+-------+ | name | type | +=========================+=======+ | disk.pinned_blocks | gauge | +-------------------------+-------+ | disk.disk_blocks_in_use | gauge | +-------------------------+-------+ ``disk_blocks_in_use`` indicates how many disk blocks are currently in use, either as dirty blocks waiting to be written or blocks kept around in the hope that a peer will request it or in a peer send buffer. This gauge deprecates ``cache_status::total_used_buffers``. .. _disk.queued_disk_jobs: .. _disk.num_running_disk_jobs: .. _disk.num_read_jobs: .. _disk.num_write_jobs: .. _disk.num_jobs: .. _disk.blocked_disk_jobs: .. _disk.num_writing_threads: .. _disk.num_running_threads: .. raw:: html +----------------------------+-------+ | name | type | +============================+=======+ | disk.queued_disk_jobs | gauge | +----------------------------+-------+ | disk.num_running_disk_jobs | gauge | +----------------------------+-------+ | disk.num_read_jobs | gauge | +----------------------------+-------+ | disk.num_write_jobs | gauge | +----------------------------+-------+ | disk.num_jobs | gauge | +----------------------------+-------+ | disk.blocked_disk_jobs | gauge | +----------------------------+-------+ | disk.num_writing_threads | gauge | +----------------------------+-------+ | disk.num_running_threads | gauge | +----------------------------+-------+ ``queued_disk_jobs`` is the number of disk jobs currently queued, waiting to be executed by a disk thread. Deprecates ``cache_status::job_queue_length``. .. _disk.queued_write_bytes: .. _disk.arc_mru_size: .. _disk.arc_mru_ghost_size: .. _disk.arc_mfu_size: .. _disk.arc_mfu_ghost_size: .. _disk.arc_write_size: .. _disk.arc_volatile_size: .. raw:: html +-------------------------+-------+ | name | type | +=========================+=======+ | disk.queued_write_bytes | gauge | +-------------------------+-------+ | disk.arc_mru_size | gauge | +-------------------------+-------+ | disk.arc_mru_ghost_size | gauge | +-------------------------+-------+ | disk.arc_mfu_size | gauge | +-------------------------+-------+ | disk.arc_mfu_ghost_size | gauge | +-------------------------+-------+ | disk.arc_write_size | gauge | +-------------------------+-------+ | disk.arc_volatile_size | gauge | +-------------------------+-------+ the number of bytes we have sent to the disk I/O thread for writing. Every time we hear back from the disk I/O thread with a completed write job, this is updated to the number of bytes the disk I/O thread is actually waiting for to be written (as opposed to bytes just hanging out in the cache) .. _disk.num_blocks_written: .. _disk.num_blocks_read: .. raw:: html +-------------------------+---------+ | name | type | +=========================+=========+ | disk.num_blocks_written | counter | +-------------------------+---------+ | disk.num_blocks_read | counter | +-------------------------+---------+ the number of blocks written and read from disk in total. A block is 16 kiB. ``num_blocks_written`` and ``num_blocks_read`` deprecates ``cache_status::blocks_written`` and ``cache_status::blocks_read`` respectively. .. _disk.num_blocks_hashed: .. raw:: html +------------------------+---------+ | name | type | +========================+=========+ | disk.num_blocks_hashed | counter | +------------------------+---------+ the total number of blocks run through SHA-1 hashing .. _disk.num_blocks_cache_hits: .. raw:: html +----------------------------+---------+ | name | type | +============================+=========+ | disk.num_blocks_cache_hits | counter | +----------------------------+---------+ the number of blocks read from the disk cache Deprecates ``cache_info::blocks_read_hit``. .. _disk.num_write_ops: .. _disk.num_read_ops: .. raw:: html +--------------------+---------+ | name | type | +====================+=========+ | disk.num_write_ops | counter | +--------------------+---------+ | disk.num_read_ops | counter | +--------------------+---------+ the number of disk I/O operation for reads and writes. One disk operation may transfer more then one block. These counters deprecates ``cache_status::writes`` and ``cache_status::reads``. .. _disk.num_read_back: .. raw:: html +--------------------+---------+ | name | type | +====================+=========+ | disk.num_read_back | counter | +--------------------+---------+ the number of blocks that had to be read back from disk in order to hash a piece (when verifying against the piece hash) .. _disk.disk_read_time: .. _disk.disk_write_time: .. _disk.disk_hash_time: .. _disk.disk_job_time: .. raw:: html +----------------------+---------+ | name | type | +======================+=========+ | disk.disk_read_time | counter | +----------------------+---------+ | disk.disk_write_time | counter | +----------------------+---------+ | disk.disk_hash_time | counter | +----------------------+---------+ | disk.disk_job_time | counter | +----------------------+---------+ cumulative time spent in various disk jobs, as well as total for all disk jobs. Measured in microseconds .. _disk.num_fenced_read: .. _disk.num_fenced_write: .. _disk.num_fenced_hash: .. _disk.num_fenced_move_storage: .. _disk.num_fenced_release_files: .. _disk.num_fenced_delete_files: .. _disk.num_fenced_check_fastresume: .. _disk.num_fenced_save_resume_data: .. _disk.num_fenced_rename_file: .. _disk.num_fenced_stop_torrent: .. _disk.num_fenced_cache_piece: .. _disk.num_fenced_flush_piece: .. _disk.num_fenced_flush_hashed: .. _disk.num_fenced_flush_storage: .. _disk.num_fenced_trim_cache: .. _disk.num_fenced_file_priority: .. _disk.num_fenced_load_torrent: .. _disk.num_fenced_clear_piece: .. _disk.num_fenced_tick_storage: .. raw:: html +----------------------------------+-------+ | name | type | +==================================+=======+ | disk.num_fenced_read | gauge | +----------------------------------+-------+ | disk.num_fenced_write | gauge | +----------------------------------+-------+ | disk.num_fenced_hash | gauge | +----------------------------------+-------+ | disk.num_fenced_move_storage | gauge | +----------------------------------+-------+ | disk.num_fenced_release_files | gauge | +----------------------------------+-------+ | disk.num_fenced_delete_files | gauge | +----------------------------------+-------+ | disk.num_fenced_check_fastresume | gauge | +----------------------------------+-------+ | disk.num_fenced_save_resume_data | gauge | +----------------------------------+-------+ | disk.num_fenced_rename_file | gauge | +----------------------------------+-------+ | disk.num_fenced_stop_torrent | gauge | +----------------------------------+-------+ | disk.num_fenced_cache_piece | gauge | +----------------------------------+-------+ | disk.num_fenced_flush_piece | gauge | +----------------------------------+-------+ | disk.num_fenced_flush_hashed | gauge | +----------------------------------+-------+ | disk.num_fenced_flush_storage | gauge | +----------------------------------+-------+ | disk.num_fenced_trim_cache | gauge | +----------------------------------+-------+ | disk.num_fenced_file_priority | gauge | +----------------------------------+-------+ | disk.num_fenced_load_torrent | gauge | +----------------------------------+-------+ | disk.num_fenced_clear_piece | gauge | +----------------------------------+-------+ | disk.num_fenced_tick_storage | gauge | +----------------------------------+-------+ for each kind of disk job, a counter of how many jobs of that kind are currently blocked by a disk fence .. _dht.dht_nodes: .. raw:: html +---------------+-------+ | name | type | +===============+=======+ | dht.dht_nodes | gauge | +---------------+-------+ The number of nodes in the DHT routing table .. _dht.dht_node_cache: .. raw:: html +--------------------+-------+ | name | type | +====================+=======+ | dht.dht_node_cache | gauge | +--------------------+-------+ The number of replacement nodes in the DHT routing table .. _dht.dht_torrents: .. raw:: html +------------------+-------+ | name | type | +==================+=======+ | dht.dht_torrents | gauge | +------------------+-------+ the number of torrents currently tracked by our DHT node .. _dht.dht_peers: .. raw:: html +---------------+-------+ | name | type | +===============+=======+ | dht.dht_peers | gauge | +---------------+-------+ the number of peers currently tracked by our DHT node .. _dht.dht_immutable_data: .. raw:: html +------------------------+-------+ | name | type | +========================+=======+ | dht.dht_immutable_data | gauge | +------------------------+-------+ the number of immutable data items tracked by our DHT node .. _dht.dht_mutable_data: .. raw:: html +----------------------+-------+ | name | type | +======================+=======+ | dht.dht_mutable_data | gauge | +----------------------+-------+ the number of mutable data items tracked by our DHT node .. _dht.dht_allocated_observers: .. raw:: html +-----------------------------+-------+ | name | type | +=============================+=======+ | dht.dht_allocated_observers | gauge | +-----------------------------+-------+ the number of RPC observers currently allocated .. _dht.dht_messages_in: .. _dht.dht_messages_out: .. raw:: html +----------------------+---------+ | name | type | +======================+=========+ | dht.dht_messages_in | counter | +----------------------+---------+ | dht.dht_messages_out | counter | +----------------------+---------+ the total number of DHT messages sent and received .. _dht.dht_messages_out_dropped: .. raw:: html +------------------------------+---------+ | name | type | +==============================+=========+ | dht.dht_messages_out_dropped | counter | +------------------------------+---------+ the number of outgoing messages that failed to be sent .. _dht.dht_bytes_in: .. _dht.dht_bytes_out: .. raw:: html +-------------------+---------+ | name | type | +===================+=========+ | dht.dht_bytes_in | counter | +-------------------+---------+ | dht.dht_bytes_out | counter | +-------------------+---------+ the total number of bytes sent and received by the DHT .. _dht.dht_ping_in: .. _dht.dht_ping_out: .. _dht.dht_find_node_in: .. _dht.dht_find_node_out: .. _dht.dht_get_peers_in: .. _dht.dht_get_peers_out: .. _dht.dht_announce_peer_in: .. _dht.dht_announce_peer_out: .. _dht.dht_get_in: .. _dht.dht_get_out: .. _dht.dht_put_in: .. _dht.dht_put_out: .. raw:: html +---------------------------+---------+ | name | type | +===========================+=========+ | dht.dht_ping_in | counter | +---------------------------+---------+ | dht.dht_ping_out | counter | +---------------------------+---------+ | dht.dht_find_node_in | counter | +---------------------------+---------+ | dht.dht_find_node_out | counter | +---------------------------+---------+ | dht.dht_get_peers_in | counter | +---------------------------+---------+ | dht.dht_get_peers_out | counter | +---------------------------+---------+ | dht.dht_announce_peer_in | counter | +---------------------------+---------+ | dht.dht_announce_peer_out | counter | +---------------------------+---------+ | dht.dht_get_in | counter | +---------------------------+---------+ | dht.dht_get_out | counter | +---------------------------+---------+ | dht.dht_put_in | counter | +---------------------------+---------+ | dht.dht_put_out | counter | +---------------------------+---------+ the number of DHT messages we've sent and received by kind. .. _dht.dht_invalid_announce: .. _dht.dht_invalid_get_peers: .. _dht.dht_invalid_put: .. _dht.dht_invalid_get: .. raw:: html +---------------------------+---------+ | name | type | +===========================+=========+ | dht.dht_invalid_announce | counter | +---------------------------+---------+ | dht.dht_invalid_get_peers | counter | +---------------------------+---------+ | dht.dht_invalid_put | counter | +---------------------------+---------+ | dht.dht_invalid_get | counter | +---------------------------+---------+ the number of failed incoming DHT requests by kind of request .. _utp.utp_packet_loss: .. _utp.utp_timeout: .. _utp.utp_packets_in: .. _utp.utp_packets_out: .. _utp.utp_fast_retransmit: .. _utp.utp_packet_resend: .. _utp.utp_samples_above_target: .. _utp.utp_samples_below_target: .. _utp.utp_payload_pkts_in: .. _utp.utp_payload_pkts_out: .. _utp.utp_invalid_pkts_in: .. _utp.utp_redundant_pkts_in: .. raw:: html +------------------------------+---------+ | name | type | +==============================+=========+ | utp.utp_packet_loss | counter | +------------------------------+---------+ | utp.utp_timeout | counter | +------------------------------+---------+ | utp.utp_packets_in | counter | +------------------------------+---------+ | utp.utp_packets_out | counter | +------------------------------+---------+ | utp.utp_fast_retransmit | counter | +------------------------------+---------+ | utp.utp_packet_resend | counter | +------------------------------+---------+ | utp.utp_samples_above_target | counter | +------------------------------+---------+ | utp.utp_samples_below_target | counter | +------------------------------+---------+ | utp.utp_payload_pkts_in | counter | +------------------------------+---------+ | utp.utp_payload_pkts_out | counter | +------------------------------+---------+ | utp.utp_invalid_pkts_in | counter | +------------------------------+---------+ | utp.utp_redundant_pkts_in | counter | +------------------------------+---------+ uTP counters. Each counter represents the number of time each event has occurred. .. _utp.num_utp_idle: .. _utp.num_utp_syn_sent: .. _utp.num_utp_connected: .. _utp.num_utp_fin_sent: .. _utp.num_utp_close_wait: .. _utp.num_utp_deleted: .. raw:: html +------------------------+-------+ | name | type | +========================+=======+ | utp.num_utp_idle | gauge | +------------------------+-------+ | utp.num_utp_syn_sent | gauge | +------------------------+-------+ | utp.num_utp_connected | gauge | +------------------------+-------+ | utp.num_utp_fin_sent | gauge | +------------------------+-------+ | utp.num_utp_close_wait | gauge | +------------------------+-------+ | utp.num_utp_deleted | gauge | +------------------------+-------+ the number of uTP sockets in each respective state .. _sock_bufs.socket_send_size3: .. _sock_bufs.socket_send_size4: .. _sock_bufs.socket_send_size5: .. _sock_bufs.socket_send_size6: .. _sock_bufs.socket_send_size7: .. _sock_bufs.socket_send_size8: .. _sock_bufs.socket_send_size9: .. _sock_bufs.socket_send_size10: .. _sock_bufs.socket_send_size11: .. _sock_bufs.socket_send_size12: .. _sock_bufs.socket_send_size13: .. _sock_bufs.socket_send_size14: .. _sock_bufs.socket_send_size15: .. _sock_bufs.socket_send_size16: .. _sock_bufs.socket_send_size17: .. _sock_bufs.socket_send_size18: .. _sock_bufs.socket_send_size19: .. _sock_bufs.socket_send_size20: .. _sock_bufs.socket_recv_size3: .. _sock_bufs.socket_recv_size4: .. _sock_bufs.socket_recv_size5: .. _sock_bufs.socket_recv_size6: .. _sock_bufs.socket_recv_size7: .. _sock_bufs.socket_recv_size8: .. _sock_bufs.socket_recv_size9: .. _sock_bufs.socket_recv_size10: .. _sock_bufs.socket_recv_size11: .. _sock_bufs.socket_recv_size12: .. _sock_bufs.socket_recv_size13: .. _sock_bufs.socket_recv_size14: .. _sock_bufs.socket_recv_size15: .. _sock_bufs.socket_recv_size16: .. _sock_bufs.socket_recv_size17: .. _sock_bufs.socket_recv_size18: .. _sock_bufs.socket_recv_size19: .. _sock_bufs.socket_recv_size20: .. raw:: html +------------------------------+---------+ | name | type | +==============================+=========+ | sock_bufs.socket_send_size3 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size4 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size5 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size6 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size7 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size8 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size9 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size10 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size11 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size12 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size13 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size14 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size15 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size16 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size17 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size18 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size19 | counter | +------------------------------+---------+ | sock_bufs.socket_send_size20 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size3 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size4 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size5 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size6 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size7 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size8 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size9 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size10 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size11 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size12 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size13 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size14 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size15 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size16 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size17 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size18 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size19 | counter | +------------------------------+---------+ | sock_bufs.socket_recv_size20 | counter | +------------------------------+---------+ the buffer sizes accepted by socket send and receive calls respectively. The larger the buffers are, the more efficient, because it reqire fewer system calls per byte. The size is 1 << n, where n is the number at the end of the counter name. i.e. 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576 bytes libtorrent-rasterbar-1.1.13/docs/storage.png000066400000000000000000000112451351156116000210500ustar00rootroot00000000000000‰PNG  IHDR@3V¦+“lIDATxśíÝŮ’«¶P÷©Ľ˙+ű\¸B)šŁőăµ.Rn6FŘiřZ#ď÷űŃüďŰ'{0B`„ôOúĂßßß·ÎfŁ{&÷Oöł‹^ţ4!’ $@H €ňAó[z×—ń&ź-Őá'ĺΫGŢ4Ś%}˦˛Ć~âž$^ ¬Ľ›żßďÖ-~Ó­żsś}'vÜčVË7§?f˛lăk¬˘V=N+H–˛#—Ő˛÷űť­U‰l} J‘j`e•u¦4 Ó«sś¬m°|WőíYt­ľîo *R€ŤHk0Yđ|7>ĄwމŞ\›<-Ŕ^í®¬KbëÁß˙z©oěň—µłM~Ý‘Ő^¨ŐĂö÷Żľ·,+=Č«]ýę—>rśnţk`p×Ě/Ř(ÄSTk<˝ €‹üb€í q0•kq„YWŽ]üâiŚśÉ g đXÄ^„‹qśxă{Ę0ŕ7]Ő„ŢU[ęúă÷Ę}Ň-ýá…­Ĺ8:ç3¸ÇČ–Ňîă¬~ËörŕNwĚ«.r‘-?Qľ(÷yµÓ«ÜçŐ®NĄ+h¤Ç\]ŚcdKéČq¶–đ;î›Čś-E1r#żY·ęOÇoôŐ#TשZµĽ«óąVŰ0EŔÇ×F!V§úîŘç#»ég—#ÍkťE=˛0^Őźć\=ňŕůü «j`›îéűd ŚiAé°˝lßj×Tz„e{ZÖňOĺĘOĺ>ĄÖzQ­ĎŐ:áNYR řA×®ÄqÝŕ‚՚ʥoź‡áńĹÂü®mB<ýĐ^¸ő ¸IE?€Ý‚­ÄqĘýÚMŕř8~ $@Hyy˛„~żI"! 0B`„$ŔfgX @• $6µěť,! °y=`±|€ë0B`“J«_ď÷[7@F€’›TÖűĄ3 #ŔI€’ $@H €! 0B`„$ŔI€’ $@H €ؤ˛€y@F€’›TúćôéĚ|0B`óJ+ad! °©}şľt€”! °Ů©~T 0B`„$ŔéźôłŽ`ˇ÷&÷Oöł‹^ţ4!’ $@HO°żż?=ż&Ä‘'?(R€ĄŐ¬Oh•[ŇŤ›¶´ j˝ëó:}Ń/«|Kö)ĘítÄkBĚnúŮ˝>Ť“Ďë‘-Ő"˛cfď*_ôËŞ^ë5«âŘV,Ąń°Úm–=ʤxŮ[F:ä:Á)ĂFDjB×iĚi\ŮĐ·oź}Ą‰W˚ݖ¨řĽN··čĘ}¶–>¸OÖŔřQ–žîżéd~Ů_ÖŞ6sÍ t/Qč“˙A“_ Ŕ+bâw–˛ęsVŐq’)Ŕ® €GřqG¬3íidçŐ#źuśÁťw;RÓŇş°ĂŃ+GÜunÄ#ĂóÎ=ÎmŮ0>€ľő^69`Ůýşż.Ćëżwę‘ Vő8yŁű˛ŚŮ±šF§¬ţ ´VĺH, ŕ×ěF_†Gk]ŚňźúÁ3˛ľĆ˛ç«]ďI§|UWľŘşšF§¬ŇȪ㟀̵8–›u5í.-şc0Za|Ő/€ľËG!¶*_śg3XîÁ•;2˛ ďĐDć­•’˛ŰéµÖUí6RÝ-k쬱[-˝U\µOkő8ďâ~&2Ăüć]‰ăÁU´ÇęZŞfśČüÔ*U9N4c€=őćţÔĎđńVŁ€— (@HyŐÁXaBŇ„@H €! °ŮVP%ŔI€M-}š)@Hl^žčĐ!ŔI€M*­~UźÝ đă! °Ie˝_:Ă2 €! 0B`„$ŔI€’ $@H €! 0B`Ľţţţö­vżăŤŐ·ě>VËşód€#ţůö ŘYK ßąT±e‘á1<ówRËóŔ>/źÎĽTŇG-Ż—.ű—OËöYݡşĄzśę>ĺqŞĄgˇő©;Çi}đΗÓ*„&ÄçČnÄý{ýűýţě>63˝‰·ŽłükyüôPĺqŞe•Ç©–žľ(?TµÜjéiYŐ#—gLK€M*­ť VżŇ÷¶öOk'›zz˛´«îPfŔ«9Ç °ę‡SoŮnGĆ/¤-rehëIÎ`ţ3„§ljY/ÎŽýű·ŕŐnůo˙LŞű¬v°ełĆ˝Án°‘>°ě€Ů‘·~íŔW°©ą“ę×ö€GŇ„8µŐž'^IĚkńźbá xřMj`łsw¨`„$ŔI€R}‡Ń\°ŐĂz+Ý8č†+˘`[.^OĽpöq¸Ó=iB ¤<Ŕž÷W$Ücd…-ŕDj`„ôźSý‚#TÂŕN–’ÚĂұi= ˇőďÎ䆲ř.·šáŻźÓϡzŔęóŻĎ*±Ă“T‡[—ýA—­}Ţz€V¶sů«r·ę><Ŕlu‚skEĺ1xäţo{ůü¶Ö»\5?ë?ť^úŔJŐkŁúüĂěą­čj…VçůŠ’]D±ę»ďŐßŘrcyŚ<§tßUă"ú®{ŇD lĹraě»Ňz >˘>Ö-ŹľěfýíÓ©řîąůmç¶.˝íţűşÚşŇ)Ž'™3Ćf8źyľ "Ň„¸b°!ľőcu˲˝ż›˙­^D3ÄĆČ9Śß Ękd仑®˛ę«}`Őkmő|8×=i"Ŕľo†;gyĚE4ĂqiÄĄěáT¶ ŐŞ·ÍĄĚI€}ŤkR­+býôÝ)ŇëOH€óšgŠ´uÂ&$Ŕ€ŁŞł—-­}V§<^4EşµĎŕéju°_±=pHÖ ·ÜÇłů'ĺ>é‹ę‘ÓC-S*Ws˘ŐŔŘßgdËęçęź§Snrç-~°¬4#[K F&÷`ŔMîldŰ4‰­lŐ¬îs°,N'Ŕ€CŇşK«Ż(«ßlť"]M—NҬ–Ő?ç׿͛Ő}ZÇ˙\śĹDf8Óc.˘#Ädîą âΔU›ŕ:š3©xq›ě‹m&·µTě.¨5łäžE4Ív4ŔľŰâ?ů}ůë‹X;x°CÖÔ™~ßŮçUˤę»ö=gˇżĄZú«hÓŻ>?eä]-"ĐZÔ@tĎvÚ ŽěnŰ™~˙úo¤÷âÖ€×ň]ĺÎű¦ÖŻ–žť@vŻvNܶ@uQĂŔ€Ç;yǧU úUeçSîą#鳚Єwü‘oL‹«ůcr׎BěLnOeU‡#)ŇiĚjrűJżç’9ý[\j’?ć ăP€ ŢCűKł´:ĆZÇ)ßŐš~_N¤/÷,˝<Ôęý~Xç;śŘż©–öݱ6W8a%ŽMďÚ×R7Uűtř[nsÂ Ž­éőÚŘ·ď]<Űů}`##ײ´űť|ŠŤâ®(ë¬#W‡ČűKře'Xyc q«˝n@ÄYGžü ¸ß9™łĺ°…Öř‹ę¤ŕę»O&-4{]=çW­e˛<ĂţgĎŠśJśľ«śćÜ™Đ˙V«ĄË?ŕyö÷•wĎęÔÝlK6Jpđ8#úG+'{µ¦•g¸úŮ÷M%.˙©úť”§ŃúVËŇűź ´»§’ÖúĂĘŃ©ŚLJ+ϰÜç”sÎň&+bSöT«\ťOÚ§ŇŞ(Tw>Ţ…Víɰĺ ËŇŹ3rnŹĐi'\ýˇšÖę»Z6Žlyµ«ĺ>{[ŐŻjEg°BÖ*+=HÖ Vý°ĺˇ–n°ę§hUÎ:_~ë{ćţV€Űś0‘X¸ŕ6w÷Ŕ)!ť0lGIkŇRżĂ)űWm5żě;5°Î¨‘ť—íü¬ý5°Áő :o,\]E˘:ďX= ŕíŻŤ¬Q*¨¬{µV‘±¬"÷řÂDćUKŰ`5íľvZđK´Ďó-ă÷ůsěôßőV5N!ÜƵĆý6ĄÉˇ{'Ď­_"§ßĐ÷.uźţëę˘ĺń]cżéh ¬µPúř[6•bř'2K/€_8Ŕře3ŽBÂé/ŻsâóÁM§a!ŔŕVŹĽ˙V?T9ĆĐ|Î%Ŕŕr'Ö?&T.Ę3řy·.Ü“ąłPyŮëęŮV÷aržgĘ.˘ĐuŽń»AµV]říŐ™ń…ĽłýűĎ’­ĆŘŕ‹ÁĎÎą6Ĺ\hđyâş'zG¦~ľö~źýł)ŞG·Ů0¸\Ü»Úę·‘~u»k·Ő¶Í}çĂT4!™V/˘(16~7ČRađ9G#]e味íť>°Ö–NéĎbS 08Óc.˘Ç|ľ(Oü}`@<ŐzŇ eIݏţł‡gĂ?Rk18h"•=˙oÓ7ů÷Ż}eťkÇggKIŮ—+»ßĺŽĐň&ÄO%Ě˙TŘę—/ś­ÓŤ[“®ŞŹ\ŢőŞŤ'\m7ŞŽ©6!¶&;÷·´ŠëLµć,•†DŘJz˝’ MúüŁr Áň]Ą/^µńՉϝ×Uéɤűw¦]—çÓÚŇ/®u¶śĄ>C†Á&?›^ݱéĆď˙>ÉvÓ‘«ďZ’ňÜ;U§¬ţ>|Esâ/_Ŕ&ÓŤ·ćÍřäłt¦Łe5ą˘îjżŰît™Čśm/¨Ő ¶éŽßę9+OűřÔćjYťď§?ŐšŽý™>žÔ«g¶r ›~ń Łž,Č“X‰cvOúCîçňy0ś Ő]4Ď:ąźG€M­:¬ fS͆r]¨ď6âąžG€‡”kOěŞ×ú×lź‘ÁŤŮ»ŞCů[뀔ۙ–AóRń"„r퉑5WGĎW§WWôčo)ϧżô D€_łÚ¨X®M5ň.~„›Tö·¤+–Gz˙kpŐ#RúŔ€Cú}`­ćłu7eRçŐ-Ůůś¸Z#ߥ—evzÂřŠ(żxýč:ĺ€ÜÉJŔO(WŮŘ˝îĆßż^Ň+M@TeŇěΡ‘! 0B`„¤ ¨3¦śÉ 0 Â ć§ €! 0B`„$ŔI€M*Ál@3@F€’›”‡Xô °˘<™ ŕN €ŘĽ´"t0B`Sűt}é( 0B`łSý¨`„$Ŕé?Od6hoar˙d?»háĺŹ9@"! 0B`„$Ŕ)ÄÁVKo˙2ţĺłĺóc:Ŕ€ °CҬZ¤?–1Ŕ)Ř~K,-16Xß*+mlĄlż%~Ţď÷˛lüj&µŘD€}‡č8Hâwh<8H€í×ď+ëXénşÁ`ű•Ů3FB ŕ8}`„$Ŕé„&Äżżż´Měž•)ć\áâşž-}f™Ł5°jzeÓˇŇGfJŤ8ë8çşî”&ü°ßu¨– ´»teŠ}ű”'°zJĺÉgŻGĘj•Ţ?ç¬čΑ8łěş•)öí“fCöŢΉĄ{¶^÷·¤çÓ/+;Nů˘zd^ó â¬d Oşĺł±¬Ó¬–¸dpy†# AŤ”eA)€Ýf °÷żú»u µ0|9–d_Ó\z†µ˘Ő˛Ô®vˇ`l˝™¦oÉîŃYXË`WYÖç´ŁÖRžO§,Ű!ŰX–ľúaWËJ’uŤś6çÚq-7;`»ß3ó[ ó;ˇ ŃuŔýîX ±:(ü”Ř»hŇ´żľćwG€]­QŮëÝ˝e2 `fűěs‹_ţ»lo gČâ¤ZUęŚY/KĎŽł{ŇtëŤ2 `f§ ŁŻ1/GĆ/?V÷oM@®—í38 GYLčP€e°Č¦Ź*}űklşńw–ŔEö7!Nóęč4úe•¤sëFw–ŔEÍK»Á^c‹-U;˝ú]h›¦÷­vץ2IöËüŔüNČü<ľüŔüfY q*î\ó`„$ŔI€’ $@HůDf«Q‚É.„¤ €! 0B`„$ŔI€’ $@H €! 0B`„$ŔI€’ $@H €! 0B`„$ŔI€Ň˙ЇMB`6„IEND®B`‚libtorrent-rasterbar-1.1.13/docs/streaming.html000066400000000000000000000217541351156116000215630ustar00rootroot00000000000000 Streaming implementation

Streaming implementation

This documents describes the algorithm libtorrent uses to satisfy time critical piece requests, i.e. streaming.

piece picking

The standard bittorrent piece picker is peer-centric. A peer unchokes us or we complte a block from a peer and we want to make another request to that peer. The piece picker answers the question: which block should we request from this peer.

When streaming, we have a number of time critical pieces, the ones the video or audio player will need next to keep up with the stream. To keep the deadlines of these pieces, we need a mechanism to answer the question: I want to request blocks from this piece, which peer is the most likely to be able to deliver it to me the soonest.

This question is answered by torrent::request_time_critical_pieces() in libtorrent.

At a high level, this algorithm keeps a list of peers, sorted by the estimated download queue time. That is, the estimated time for a new request to this peer to be received. The bottom 10th percentile of the peers (the 10% slowest peers) are ignored and not included in the peer list. Peers that have choked us, are not interesting, is on parole, disconnecting, have too many outstanding block requests or is snubbed are also excluded from the peer list.

The time critical pieces are also kept sorted by their deadline. Pieces with an earlier deadline first. This list of pieces is iterated, starting at the top, and blocks are requested from a piece until we cannot make any more requests from it. We then move on to the next piece and request blocks from it until we cannot make any more. The peer each request is sent to is the one with the lowest download queue time. Each time a request is made, this estimate is updated and the peer is resorted in this list.

Any peer that doesn't have the piece is ignored until we move on to the next piece.

If the top peer's download queue time is more than 2 seconds, the loop is terminated. This is to not over-request. request_time_critical_pieces() is called once per second, so this will keep the queue full with margin.

download queue time

Each peer maintains the number of bytes that have been requested from it but not yet been received. This is referred to as outstanding_bytes. This number is incremented by the size of each outgoing request and decremented for each payload byte received.

This counter is divided by an estimated download rate from the peer to form the estimated download queue time. That is, the estimated time it will take any new request to this peer to begin being received.

The estimated download rate of a peer is not trivial. There may not be any outstanding requests to the peer, in which case the payload download rate will be zero. That would not be a reasonable estimate of the rate we would see once we make a request.

If we have not received any payload from a peer in the last 30 seconds, we must use an alternative estimate of the download rate. If we have received payload from this peer previously, we can use the peak download rate.

If we have received less than 2 blocks (32 kiB) and we have been unchoked for less than 5 seconds ago, use the average download rate of all peers (that have outstanding requests).

timeouts

An observation that is useful to keep in mind when streaming is that your download capacity is likely to be saturated by your peers. In this case, if the swarm is well seeded, most peers will send data to you at close to the same rate. This makes it important to support streaming from many slow peers. For instance, this means you can't make assumptions about the download time of a block being less than some absolute time. You may be downloading at well above the bitrate of the video, but each individual peer only transfers at 5 kiB/s.

In this state, your download rate is a zero-sum-game. Any block you request that is not urgent, will take away from the bandwidth you get for peers that are urgent. Make sure to limit requests to useful blocks only.

Some requests will stall. It appears to be very hard to have enough accuracy in the prediction of download queue time such that all requests come back within a reasonable amount of time.

To support adaptive timeuts, each torrent maintains a running average of how long it takes to complete a piece. There is also a running average of the deviation from the mean download time.

This download time is used as the benchmark to determine when blocks have timed out, and should be re-requested from another peer.

If any time-critical piece has taken more than the average piece download time + a half average deviation form that, the piece is considered to have timed out. This means we are allowed to double-request blocks. Subsequent passes over this piece will make sure that any blocks we don't already have are requested one more time.

In fact, this scales to multiple time-outs. The time since a download was started is divided by average download time + average deviation time / 2. The resulting integer is the number if times the piece has timed out.

Each time a piece times out, another busy request is allowed to try to make it complete sooner. A busy request is where a block is requested from a peer even though it has already been requested from another peer.

This has the effect of getting more and more aggressive in requesting blocks the longer it takes to complete the piece. If this mechanism is too aggressive, a significant amount of bandwidht may be lost in redundant download (keep in mind the zero-sum game).

It never makes sense to request a block twice from the same peer. There is logic in place to prevent this.

optimizations

One optimization is to buffer all piece requests while looping over the time- critical pieces and not send them until one round is complete. This increases the chances that the request messages are coalesced into the same packet. This in turn lowers the number of system calls and network overhead.

libtorrent-rasterbar-1.1.13/docs/streaming.rst000066400000000000000000000136561351156116000214310ustar00rootroot00000000000000Streaming implementation ======================== This documents describes the algorithm libtorrent uses to satisfy time critical piece requests, i.e. streaming. piece picking ------------- The standard bittorrent piece picker is peer-centric. A peer unchokes us or we complte a block from a peer and we want to make another request to that peer. The piece picker answers the question: which block should we request from this peer. When streaming, we have a number of *time critical* pieces, the ones the video or audio player will need next to keep up with the stream. To keep the deadlines of these pieces, we need a mechanism to answer the question: I want to request blocks from this piece, which peer is the most likely to be able to deliver it to me the soonest. This question is answered by ``torrent::request_time_critical_pieces()`` in libtorrent. At a high level, this algorithm keeps a list of peers, sorted by the estimated download queue time. That is, the estimated time for a new request to this peer to be received. The bottom 10th percentile of the peers (the 10% slowest peers) are ignored and not included in the peer list. Peers that have choked us, are not interesting, is on parole, disconnecting, have too many outstanding block requests or is snubbed are also excluded from the peer list. The time critical pieces are also kept sorted by their deadline. Pieces with an earlier deadline first. This list of pieces is iterated, starting at the top, and blocks are requested from a piece until we cannot make any more requests from it. We then move on to the next piece and request blocks from it until we cannot make any more. The peer each request is sent to is the one with the lowest `download queue time`_. Each time a request is made, this estimate is updated and the peer is resorted in this list. Any peer that doesn't have the piece is ignored until we move on to the next piece. If the top peer's download queue time is more than 2 seconds, the loop is terminated. This is to not over-request. ``request_time_critical_pieces()`` is called once per second, so this will keep the queue full with margin. download queue time ------------------- Each peer maintains the number of bytes that have been requested from it but not yet been received. This is referred to as ``outstanding_bytes``. This number is incremented by the size of each outgoing request and decremented for each *payload* byte received. This counter is divided by an estimated download rate from the peer to form the estimated *download queue time*. That is, the estimated time it will take any new request to this peer to begin being received. The estimated download rate of a peer is not trivial. There may not be any outstanding requests to the peer, in which case the payload download rate will be zero. That would not be a reasonable estimate of the rate we would see once we make a request. If we have not received any payload from a peer in the last 30 seconds, we must use an alternative estimate of the download rate. If we have received payload from this peer previously, we can use the peak download rate. If we have received less than 2 blocks (32 kiB) and we have been unchoked for less than 5 seconds ago, use the average download rate of all peers (that have outstanding requests). timeouts -------- An observation that is useful to keep in mind when streaming is that your download capacity is likely to be saturated by your peers. In this case, if the swarm is well seeded, most peers will send data to you at close to the same rate. This makes it important to support streaming from many slow peers. For instance, this means you can't make assumptions about the download time of a block being less than some absolute time. You may be downloading at well above the bitrate of the video, but each individual peer only transfers at 5 kiB/s. In this state, your download rate is a zero-sum-game. Any block you request that is not urgent, will take away from the bandwidth you get for peers that are urgent. Make sure to limit requests to useful blocks only. Some requests will stall. It appears to be very hard to have enough accuracy in the prediction of download queue time such that all requests come back within a reasonable amount of time. To support adaptive timeuts, each torrent maintains a running average of how long it takes to complete a piece. There is also a running average of the deviation from the mean download time. This download time is used as the benchmark to determine when blocks have timed out, and should be re-requested from another peer. If any time-critical piece has taken more than the average piece download time + a half average deviation form that, the piece is considered to have timed out. This means we are allowed to double-request blocks. Subsequent passes over this piece will make sure that any blocks we don't already have are requested one more time. In fact, this scales to multiple time-outs. The time since a download was started is divided by average download time + average deviation time / 2. The resulting integer is the number if *times* the piece has timed out. Each time a piece times out, another *busy request* is allowed to try to make it complete sooner. A busy request is where a block is requested from a peer even though it has already been requested from another peer. This has the effect of getting more and more aggressive in requesting blocks the longer it takes to complete the piece. If this mechanism is too aggressive, a significant amount of bandwidht may be lost in redundant download (keep in mind the zero-sum game). It never makes sense to request a block twice from the same peer. There is logic in place to prevent this. optimizations ------------- One optimization is to buffer all piece requests while looping over the time- critical pieces and not send them until one round is complete. This increases the chances that the request messages are coalesced into the same packet. This in turn lowers the number of system calls and network overhead. libtorrent-rasterbar-1.1.13/docs/style.css000066400000000000000000000045741351156116000205570ustar00rootroot00000000000000.entry { min-height: 135px; } #main { font-family: Verdana; text-align: left; margin-top: 10px; } /* Base elements */ * {margin: 0; padding: 0;} body, table { font: 0.9em Verdana, sans-serif;} h1, h2, h3 { font-family: Georgia "Times New Roman", serif; padding-bottom: 0.5em; font-weight: bold; } div.sidebar { background: #f8f8e8; float: right; width: 20em; margin-right: 1em; border: solid 1px #e5e5d5; padding: 1.3em; } div.sidebar p.sidebar-title { font: 1.3em Georgia; border-bottom: solid 1px #e5e5d5; padding-bottom: 0.5em; margin: 0 0 0.5em 0; } a { text-decoration: none; color: #8D370A; border-bottom: dotted 1px #8D370A; } ul, ol { line-height: 1.8em; } ul { list-style: square; } li { margin-left: 2.8em; } p, ul, ol, img {margin-bottom: 1em;} .align-right { float: right; } .document { margin: 0px; } div.section { margin-bottom: 3em; } div.section div.section div.section { margin-bottom: 2em; } div.section p, div.section ul, div.section dl { } #container { text-align: left; max-width: 65em; margin: 5px auto; position: relative; padding: 3px ; } #header { height: 116px; width: 100%; border: none; margin-top: 1em; margin-bottom: 1em; } #header tr td { border: none; } #orange { margin: 0; padding: 0; width: 159px; height: 116px; border-top-left-radius: 6px; border-bottom-left-radius: 6px; background: url('img/orange.png') no-repeat top left; } #logo { margin: 0; padding: 0; text-align: center; color: white; font-size: 50pt; height: 116px; font-family: Georgia; border-top-right-radius: 6px; border-bottom-right-radius: 6px; background: url('img/bg.png'); } #gradient { width: 100%; clear: both; height: 30px; background: linear-gradient(#bbb, #ddd); } #footer { margin: 0px; padding: 0px; text-align: center; background: #ddd; width: 100%; color: #777; overflow: hidden; } #footer table { margin-left: auto; margin-right: auto; font-size: 1em; } #footer tr td { border: none; background: none; color: #777; text-align: left; } #footer a { color: #777; } #footer a:hover { color: #000; } #filler { height: 200px; background: linear-gradient(#ddd, #fff); } li p, li li { font-size: 100%; } libtorrent-rasterbar-1.1.13/docs/todo.html000066400000000000000000016207641351156116000205460ustar00rootroot00000000000000

libtorrent todo-list

0 urgent 22 important 62 relevant 7 feasible 176 notes
relevance 3../src/peer_connection.cpp:2981instead of having to ask the torrent whether it's in graceful pause mode or not, the peers should keep that state (and the torrent should update them when it enters graceful pause). When a peer enters graceful pause mode, it should cancel all outstanding requests and clear its request queue.
relevance 3../src/peer_connection.cpp:3862once peers are properly put in graceful pause mode, they can cancel all outstanding requests and this test can be removed.
relevance 3../src/upnp.cpp:127listen_interface is not used. It's meant to bind the broadcast socket. it would probably have to be changed to a vector of interfaces to bind to though, since the broadcast socket opens one socket per local interface by default
relevance 3../src/natpmp.cpp:560it would be nice to have a separate NAT-PMP error category
relevance 3../src/session_impl.cpp:3888it would probably make sense to have a separate list of peers that are eligible for optimistic unchoke, similar to the torrents perhaps this could even iterate over the pool allocators of torrent_peer objects. It could probably be done in a single pass and collect the n best candidates. maybe just a queue of peers would make even more sense, just pick the next peer in the queue for unchoking. It would be O(1).
relevance 3../src/session_impl.cpp:3912peers should know whether their torrent is paused or not, instead of having to ask it over and over again
relevance 3../src/session_impl.cpp:4189there should be a pre-calculated list of all peers eligible for unchoking
relevance 3../src/torrent.cpp:9853this really needs to be moved to do_async_save_resume_data. flags need to be passed on
relevance 3../src/peer_list.cpp:1124this is not exception safe!
relevance 3../src/file.cpp:559find out what error code is reported when the filesystem does not support hard links.
relevance 3../src/kademlia/routing_table.cpp:695the call to compare_ip_cidr here is expensive. peel off some layers of abstraction here to make it quicker. Look at xoring and using _builtin_ctz()
relevance 3../src/kademlia/node_id.cpp:54the XORing should be done at full words instead of bytes
relevance 3../src/kademlia/node_id.cpp:66the XORing should be done at full words instead of bytes
relevance 3../src/kademlia/node_id.cpp:82the xoring should be done at full words and _builtin_clz() could be used as the last step
relevance 3../src/kademlia/rpc_manager.cpp:87move this into it's own .cpp file
relevance 3../include/libtorrent/stat.hpp:263everything but payload counters and rates could probably be removed from here
relevance 3../include/libtorrent/torrent_handle.hpp:247consider replacing all the setters and getters for pause, resume, stop-when-ready, share-mode, upload-mode, super-seeding, apply-ip-filter, resolve-countries, pinned, sequential-download, seed-mode with just set_flags() and clear_flags() using the flags from add_torrent_params. Perhaps those flags should have a more generic name.
relevance 3../include/libtorrent/torrent_handle.hpp:494unify url_seed and http_seed with just web_seed, using the web_seed_entry.
relevance 3../include/libtorrent/torrent.hpp:1375factor out the links (as well as update_list() to a separate class that torrent can inherit)
relevance 3../include/libtorrent/web_peer_connection.hpp:131if we make this be a disk_buffer_holder instead we would save a copy use allocate_disk_receive_buffer and release_disk_receive_buffer
relevance 3../include/libtorrent/kademlia/routing_table.hpp:99to improve memory locality and scanning performance, turn the routing table into a single vector with boundaries for the nodes instead. Perhaps replacement nodes should be in a separate vector.
relevance 3../include/libtorrent/aux_/allocating_handler.hpp:77make sure the handlers we pass in are potentially movable!
relevance 2../test/test_piece_picker.cpp:281split this up into smaller tests (where we print_title)
relevance 2../test/test_storage.cpp:509split this test up into smaller parts
relevance 2../test/test_dht.cpp:516split this test up into smaller test cases
relevance 2../test/test_dht.cpp:2143split this up into smaller test cases
relevance 2../src/ut_metadata.cpp:123if we were to initialize m_metadata_size lazily instead, we would probably be more efficient initialize m_metadata_size
relevance 2../src/instantiate_connection.cpp:43peer_connection and tracker_connection should probably be flags
relevance 2../src/instantiate_connection.cpp:44move this function into libtorrent::aux namespace
relevance 2../src/peer_connection.cpp:2399this should probably be based on time instead of number of request messages. For a very high throughput connection, 300 may be a legitimate number of requests to have in flight when getting choked
relevance 2../src/peer_connection.cpp:3145since we throw away the queue entry once we issue the disk job, this may happen. Instead, we should keep the queue entry around, mark it as having been requested from disk and once the disk job comes back, discard it if it has been cancelled. Maybe even be able to cancel disk jobs?
relevance 2../src/peer_connection.cpp:4831use a deadline_timer for timeouts. Don't rely on second_tick()! Hook this up to connect timeout as well. This would improve performance because of less work in second_tick(), and might let use remove ticking entirely eventually
relevance 2../src/utp_socket_manager.cpp:245we may want to take ec into account here. possibly close connections quicker
relevance 2../src/storage.cpp:1197we probably need to do this unconditionally in this function. Even if the resume data file appears stale, we need to create these hard links, right?
relevance 2../src/storage.cpp:1221is this risky? The upper layer will assume we have the whole file. Perhaps we should verify that at least the size of the file is correct
relevance 2../src/http_tracker_connection.cpp:393returning a bool here is redundant. Instead this function should return the peer_entry
relevance 2../src/session_impl.cpp:459is there a reason not to move all of this into init()? and just post it to the io_service?
relevance 2../src/session_impl.cpp:1946the udp socket(s) should be using the same generic mechanism and not be restricted to a single one we should open a one listen socket for each entry in the listen_interfaces list
relevance 2../src/session_impl.cpp:3664make a list for torrents that want to be announced on the DHT so we don't have to loop over all torrents, just to find the ones that want to announce
relevance 2../src/session_impl.cpp:6250this should be factored into the udp socket, so we only have the code once
relevance 2../src/torrent.cpp:720post alert
relevance 2../src/torrent.cpp:1973add a unit test where we don't have metadata, connect to a peer that sends a bitfield that's too large, then we get the metadata
relevance 2../src/torrent.cpp:5092abort lookups this torrent has made via the session host resolver interface
relevance 2../src/torrent.cpp:8347if peer is a really good peer, maybe we shouldn't disconnect it perhaps this logic should be disabled if we have too many idle peers (with some definition of idle)
relevance 2../src/torrent.cpp:10825this should probably be removed
relevance 2../src/utp_stream.cpp:372it would be nice if not everything would have to be public here
relevance 2../src/udp_tracker_connection.cpp:83support authentication here. tracker_req().auth
relevance 2../src/alert_manager.cpp:88keep a count of the number of threads waiting. Only if it's > 0 notify them
relevance 2../src/receive_buffer.cpp:209should this take a boost::array<..., 2> instead? it could return the number of buffers added, just like reserve.
relevance 2../src/peer_list.cpp:507it would be nice if there was a way to iterate over these torrent_peer objects in the order they are allocated in the pool instead. It would probably be more efficient
relevance 2../src/piece_picker.cpp:1990make the 2048 limit configurable
relevance 2../src/piece_picker.cpp:2621the first_block returned here is the largest free range, not the first-fit range, which would be better
relevance 2../src/piece_picker.cpp:3407it would be nice if this could be folded into lock_piece() the main distinction is that this also maintains the m_num_passed counter and the passed_hash_check member Is there ever a case where we call write filed without also locking the piece? Perhaps write_failed() should imply locking it.
relevance 2../src/web_peer_connection.cpp:329do we really need a special case here? wouldn't the multi-file case handle single file torrents correctly too?
relevance 2../src/web_peer_connection.cpp:551just make this peer not have the pieces associated with the file we just requested. Only when it doesn't have any of the file do the following
relevance 2../src/web_peer_connection.cpp:603create a mapping of file-index to redirection URLs. Use that to form URLs instead. Support to reconnect to a new server without destructing this peer_connection
relevance 2../src/escape_string.cpp:209this should probably be moved into string_util.cpp
relevance 2../src/tracker_manager.cpp:203some of these arguments could probably be moved to the tracker request itself. like the ip_filter and settings
relevance 2../src/alert.cpp:1474the salt here is allocated on the heap. It would be nice to allocate in in the stack_allocator
relevance 2../src/udp_socket.cpp:817the udp_socket should really just be a single socket, and the session should support having more than one, just like with TCP sockets for now, just make bind failures non-fatal
relevance 2../src/block_cache.cpp:1708turn these return values into enums returns -1: block not in cache -2: out of memory
relevance 2../src/file.cpp:588test this on a FAT volume to see what error we get!
relevance 2../src/kademlia/node.cpp:655it would be nice to have a bias towards node-id prefixes that are missing in the bucket
relevance 2../src/kademlia/node.cpp:739use the non deprecated function instead of this one
relevance 2../src/kademlia/node.cpp:898find_node should write directly to the response entry
relevance 2../src/kademlia/routing_table.cpp:139use the non deprecated function instead of this one
relevance 2../src/kademlia/routing_table.cpp:989move the lowest priority nodes to the replacement bucket
relevance 2../src/kademlia/dht_storage.cpp:111make this configurable in dht_settings
relevance 2../include/libtorrent/peer_connection.hpp:1112rename this target queue size
relevance 2../include/libtorrent/piece_picker.hpp:588having 8 priority levels is probably excessive. It should probably be changed to 3 levels + dont-download
relevance 2../include/libtorrent/session_handle.hpp:78the ip filter should probably be saved here too
relevance 2../include/libtorrent/enum_net.hpp:151this could be done more efficiently by just looking up the interface with the given name, maybe even with if_nametoindex()
relevance 2../include/libtorrent/build_config.hpp:40instead of using a dummy function to cause link errors when incompatible build configurations are used, make the namespace name depend on the configuration, and have a using declaration in the headers to pull it into libtorrent.
relevance 2../include/libtorrent/proxy_base.hpp:278use the resolver interface that has a built-in cache
relevance 2../include/libtorrent/heterogeneous_queue.hpp:56add emplace_back() version
relevance 2../include/libtorrent/broadcast_socket.hpp:53facto these functions out
relevance 2../include/libtorrent/socks5_stream.hpp:142add async_connect() that takes a hostname and port as well
relevance 2../include/libtorrent/alert_types.hpp:1473should the alert base class have this object instead?
relevance 2../include/libtorrent/tracker_manager.hpp:315this class probably doesn't need to have virtual functions.
relevance 2../include/libtorrent/aux_/session_impl.hpp:1171the throttling of saving resume data could probably be factored out into a separate class
relevance 2../include/libtorrent/aux_/session_interface.hpp:136make this interface a lot smaller. It could be split up into several smaller interfaces. Each subsystem could then limit the size of the mock object to test it.
relevance 2../include/libtorrent/aux_/session_interface.hpp:145the IP voting mechanism should be factored out to its own class, not part of the session
relevance 2../include/libtorrent/aux_/session_interface.hpp:170remove this. There's already get_resolver()
relevance 2../include/libtorrent/aux_/session_interface.hpp:223factor out the thread pool for socket jobs into a separate class used to (potentially) issue socket write calls onto multiple threads
relevance 1../src/session_impl.cpp:5611report the proper address of the router as the source IP of this understanding of our external address, instead of the empty address
relevance 1../src/torrent.cpp:1289make this depend on the error and on the filesystem the files are being downloaded to. If the error is no_space_left_on_device and the filesystem doesn't support sparse files, only zero the priorities of the pieces that are at the tails of all files, leaving everything up to the highest written piece in each file
relevance 1../src/torrent.cpp:7445save the send_stats state instead of throwing them away it may pose an issue when downgrading though
relevance 1../src/torrent.cpp:8694should disconnect all peers that have the pieces we have not just seeds. It would be pretty expensive to check all pieces for all peers though
relevance 1../src/disk_io_thread.cpp:199it would be nice to have the number of threads be set dynamically
relevance 1../src/http_seed_connection.cpp:129in chunked encoding mode, this assert won't hold. the chunk headers should be subtracted from the receive_buffer_size
relevance 1../include/libtorrent/ip_voter.hpp:124instead, have one instance per possible subnet, global IPv4, global IPv6, loopback, 192.168.x.x, 10.x.x.x, etc.
relevance 0../test/test_resume.cpp:274test what happens when loading a resume file with both piece priorities and file priorities (file prio should take presedence)
relevance 0../test/test_resume.cpp:277make sure a resume file only ever contain file priorities OR piece priorities. Never both.
relevance 0../test/test_resume.cpp:280generally save
relevance 0../test/test_resume.cpp:831test all other resume flags here too. This would require returning more than just the torrent_status from test_resume_flags. Also http seeds and trackers for instance
relevance 0../test/test_ssl.cpp:392test using a signed certificate with the wrong info-hash in DN
relevance 0../test/test_ssl.cpp:490also test using a hash that refers to a valid torrent but that differs from the SNI hash
relevance 0../test/test_peer_list.cpp:976test erasing peers
relevance 0../test/test_peer_list.cpp:977test update_peer_port with allow_multiple_connections_per_ip and without
relevance 0../test/test_peer_list.cpp:978test add i2p peers
relevance 0../test/test_peer_list.cpp:979test allow_i2p_mixed
relevance 0../test/test_peer_list.cpp:980test insert_peer failing with all error conditions
relevance 0../test/test_peer_list.cpp:981test IPv6
relevance 0../test/test_peer_list.cpp:982test connect_to_peer() failing
relevance 0../test/test_peer_list.cpp:983test connection_closed
relevance 0../test/test_peer_list.cpp:984connect candidates recalculation when incrementing failcount
relevance 0../test/test_tracker.cpp:54test scrape requests
relevance 0../test/test_tracker.cpp:55test parse peers6
relevance 0../test/test_tracker.cpp:56test parse tracker-id
relevance 0../test/test_tracker.cpp:57test parse failure-reason
relevance 0../test/test_tracker.cpp:58test all failure paths, including invalid bencoding not a dictionary no files entry in scrape response no info-hash entry in scrape response malformed peers in peer list of dictionaries uneven number of bytes in peers and peers6 string responses
relevance 0../test/test_timestamp_history.cpp:54test the case where we have > 120 samples (and have the base delay actually be updated)
relevance 0../test/test_timestamp_history.cpp:55test the case where a sample is lower than the history entry but not lower than the base
relevance 0../test/test_upnp.cpp:109store the log and verify that some key messages are there
relevance 0../test/test_file_storage.cpp:307add more optimize() tests
relevance 0../test/test_file_storage.cpp:308test map_block
relevance 0../test/test_file_storage.cpp:309test piece_size(int piece)
relevance 0../test/test_file_storage.cpp:310test file_index_at_offset
relevance 0../test/test_file_storage.cpp:311test file attributes
relevance 0../test/test_file_storage.cpp:312test symlinks
relevance 0../test/test_file_storage.cpp:313test pad_files
relevance 0../test/test_file_storage.cpp:314test reorder_file (make sure internal_file_entry::swap() is used)
relevance 0../test/test_torrent_info.cpp:169test remap_files
relevance 0../test/test_torrent_info.cpp:170merkle torrents. specifically torrent_info::add_merkle_nodes and torrent with "root hash"
relevance 0../test/test_torrent_info.cpp:171torrent with 'p' (padfile) attribute
relevance 0../test/test_torrent_info.cpp:172torrent with 'h' (hidden) attribute
relevance 0../test/test_torrent_info.cpp:173torrent with 'x' (executable) attribute
relevance 0../test/test_torrent_info.cpp:174torrent with 'l' (symlink) attribute
relevance 0../test/test_torrent_info.cpp:175creating a merkle torrent (torrent_info::build_merkle_list)
relevance 0../test/test_torrent_info.cpp:176torrent with multiple trackers in multiple tiers, making sure we shuffle them (how do you test shuffling?, load it multiple times and make sure it's in different order at least once)
relevance 0../test/test_torrent_info.cpp:177torrents with a zero-length name
relevance 0../test/test_torrent_info.cpp:178torrents with a merkle tree and add_merkle_nodes
relevance 0../test/test_torrent_info.cpp:179torrent with a non-dictionary info-section
relevance 0../test/test_torrent_info.cpp:180torrents with DHT nodes
relevance 0../test/test_torrent_info.cpp:181torrent with url-list as a single string
relevance 0../test/test_torrent_info.cpp:182torrent with http seed as a single string
relevance 0../test/test_torrent_info.cpp:183torrent with a comment
relevance 0../test/test_torrent_info.cpp:184torrent with an SSL cert
relevance 0../test/test_torrent_info.cpp:185torrent with attributes (executable and hidden)
relevance 0../test/test_torrent_info.cpp:186torrent_info::add_tracker
relevance 0../test/test_torrent_info.cpp:187torrent_info::unload
relevance 0../test/test_torrent_info.cpp:188torrent_info constructor that takes an invalid bencoded buffer
relevance 0../test/test_torrent_info.cpp:189verify_encoding with a string that triggers character replacement
relevance 0../test/test_block_cache.cpp:469test try_evict_blocks
relevance 0../test/test_block_cache.cpp:470test evicting volatile pieces, to see them be removed
relevance 0../test/test_block_cache.cpp:471test evicting dirty pieces
relevance 0../test/test_block_cache.cpp:472test free_piece
relevance 0../test/test_block_cache.cpp:473test abort_dirty
relevance 0../test/test_block_cache.cpp:474test unaligned reads
relevance 0../test/test_fast_extension.cpp:1020test sending invalid requests (out of bound piece index, offsets and sizes)
relevance 0../test/test_file_progress.cpp:109test the update function too
relevance 0../test/test_bloom_filter.cpp:130test size()
relevance 0../test/test_bloom_filter.cpp:131test clear()
relevance 0../test/test_settings_pack.cpp:227load_pack_from_dict
relevance 0../test/test_dht.cpp:103ideally the mock_socket would contain this queue of packets, to make tests independent
relevance 0../test/test_dht.cpp:420check to make sure the "best" items are stored
relevance 0../test/test_dht.cpp:515test obfuscated_get_peers
relevance 0../test/test_resolve_links.cpp:80test files with different piece size (negative test)
relevance 0../test/test_resolve_links.cpp:83it would be nice to test resolving of more than just 2 files as well. like 3 single file torrents merged into one, resolving all 3 files.
relevance 0../test/test_transfer.cpp:211these settings_pack tests belong in their own test
relevance 0../test/test_transfer.cpp:290factor out the disk-full test into its own unit test
relevance 0../src/ut_metadata.cpp:320we really need to increment the refcounter on the torrent while this buffer is still in the peer's send buffer
relevance 0../src/disk_buffer_pool.cpp:254perhaps we should sort the buffers here?
relevance 0../src/string_util.cpp:59warning C4146: unary minus operator applied to unsigned type, result still unsigned
relevance 0../src/peer_connection.cpp:525it would be neat to be able to print this straight into the alert's stack allocator
relevance 0../src/peer_connection.cpp:1046this should be the global download rate
relevance 0../src/peer_connection.cpp:3393sort the allowed fast set in priority order
relevance 0../src/peer_connection.cpp:6184The stats checks can not be honored when authenticated encryption is in use because we may have encrypted data which we cannot authenticate yet
relevance 0../src/part_file.cpp:248what do we do if someone is currently reading from the disk from this piece? does it matter? Since we won't actively erase the data from disk, but it may be overwritten soon, it's probably not that big of a deal
relevance 0../src/part_file.cpp:372instead of rebuilding the whole file header and flushing it, update the slot entries as we go
relevance 0../src/packet_buffer.cpp:180use compare_less_wrap for this comparison as well
relevance 0../src/storage.cpp:1011make this more generic to not just work if files have been renamed, but also if they have been merged into a single file for instance maybe use the same format as .torrent files and reuse some code from torrent_info
relevance 0../src/storage.cpp:1325ideally, if we end up copying files because of a move across volumes, the source should not be deleted until they've all been copied. That would let us rollback with higher confidence.
relevance 0../src/session_impl.cpp:1635it would be nice to reserve() these vectors up front
relevance 0../src/session_impl.cpp:1874instead of having a special case for this, just make the default listen interfaces be "0.0.0.0:6881,[::]:6881" and use the generic path. That would even allow for not listening at all.
relevance 0../src/session_impl.cpp:2836should this function take a shared_ptr instead?
relevance 0../src/session_impl.cpp:3235have a separate list for these connections, instead of having to loop through all of them
relevance 0../src/session_impl.cpp:3268this should apply to all bandwidth channels
relevance 0../src/session_impl.cpp:4074use a lower limit than m_settings.connections_limit to allocate the to 10% or so of connection slots for incoming connections cap this at max - 1, since we may add one below
relevance 0../src/session_impl.cpp:4225post a message to have this happen immediately instead of waiting for the next tick
relevance 0../src/session_impl.cpp:4613it might be a nice feature here to limit the number of torrents to send in a single update. By just posting the first n torrents, they would nicely be round-robined because the torrent lists are always pushed back. Perhaps the status_update_alert could even have a fixed array of n entries rather than a vector, to further improve memory locality.
relevance 0../src/session_impl.cpp:4967this logic could probably be less spaghetti looking by being moved to a function with early exits
relevance 0../src/session_impl.cpp:5575perhaps this function should not exist when logging is disabled
relevance 0../src/torrent.cpp:102factor out cache_status to its own header
relevance 0../src/torrent.cpp:507if the existing torrent doesn't have metadata, insert the metadata we just downloaded into it.
relevance 0../src/torrent.cpp:619if the existing torrent doesn't have metadata, insert the metadata we just downloaded into it.
relevance 0../src/torrent.cpp:1625is verify_peer_cert called once per certificate in the chain, and this function just tells us which depth we're at right now? If so, the comment makes sense. any certificate that isn't the leaf (i.e. the one presented by the peer) should be accepted automatically, given preverified is true. The leaf certificate need to be verified to make sure its DN matches the info-hash
relevance 0../src/torrent.cpp:2117this could be optimized by looking up which files are complete and just look at those
relevance 0../src/torrent.cpp:2133this could be optimized by looking up which files are complete and just look at those
relevance 0../src/torrent.cpp:2300there may be peer extensions relying on the torrent extension still being alive. Only do this if there are no peers. And when the last peer is disconnected, if the torrent is unloaded, clear the extensions m_extensions.clear();
relevance 0../src/torrent.cpp:3027this pattern is repeated in a few places. Factor this into a function and generalize the concept of a torrent having a dedicated listen port
relevance 0../src/torrent.cpp:3916add one peer per IP the hostname resolves to
relevance 0../src/torrent.cpp:4865update suggest_piece?
relevance 0../src/torrent.cpp:5010really, we should just keep the picker around in this case to maintain the availability counters
relevance 0../src/torrent.cpp:7128make this more generic to not just work if files have been renamed, but also if they have been merged into a single file for instance maybe use the same format as .torrent files and reuse some code from torrent_info The mapped_files needs to be read both in the network thread and in the disk thread, since they both have their own mapped files structures which are kept in sync
relevance 0../src/torrent.cpp:7265if this is a merkle torrent and we can't restore the tree, we need to wipe all the bits in the have array, but not necessarily we might want to do a full check to see if we have all the pieces. This is low priority since almost no one uses merkle torrents
relevance 0../src/torrent.cpp:7369re-enable this code once there's a non-inlined encoder function. Or perhaps this should not be used until saving resume_data via add_torrent_params and a free function, similar to read_resume_data boost::shared_array const info = torrent_file().metadata(); int const size = torrent_file().metadata_size(); ret["info"].preformatted().assign(&info[0], &info[0] + size);
relevance 0../src/torrent.cpp:7518make this more generic to not just work if files have been renamed, but also if they have been merged into a single file for instance. using file_base
relevance 0../src/torrent.cpp:9801add a flag to ignore stats, and only care about resume data for content. For unchanged files, don't trigger a load of the metadata just to save an empty resume data file
relevance 0../src/torrent.cpp:11466instead of resorting the whole list, insert the peers directly into the right place
relevance 0../src/choker.cpp:353optimize this using partial_sort or something. We don't need to sort the entire list
relevance 0../src/choker.cpp:356make the comparison function a free function and move it into this cpp file
relevance 0../src/choker.cpp:361make configurable
relevance 0../src/choker.cpp:375make configurable
relevance 0../src/web_connection_base.cpp:83introduce a web-seed default class which has a low download priority
relevance 0../src/utp_stream.cpp:1814this loop is not very efficient. It could be fixed by having a separate list of sequence numbers that need resending
relevance 0../src/metadata_transfer.cpp:365this is not safe. The torrent could be unloaded while we're still sending the metadata
relevance 0../src/bt_peer_connection.cpp:698this could be optimized using knuth morris pratt
relevance 0../src/bt_peer_connection.cpp:2253if we're finished, send upload_only message
relevance 0../src/torrent_peer.cpp:188how do we deal with our external address changing?
relevance 0../src/piece_picker.cpp:2069this could probably be optimized by incrementally calling partial_sort to sort one more element in the list. Because chances are that we'll just need a single piece, and once we've picked from it we're done. Sorting the rest of the list in that case is a waste of time.
relevance 0../src/piece_picker.cpp:2591when expanding pieces for cache stripe reasons, the !downloading condition doesn't make much sense
relevance 0../src/file_progress.cpp:160it would be nice to not depend on alert_manager here
relevance 0../src/random.cpp:87use a thread local mt19937 instance instead!
relevance 0../src/create_torrent.cpp:317this should probably be optional
relevance 0../src/disk_io_thread.cpp:873it would be nice to optimize this by having the cache pieces also ordered by
relevance 0../src/disk_io_thread.cpp:916instead of doing a lookup each time through the loop, save cached_piece_entry pointers with piece_refcount incremented to pin them
relevance 0../src/disk_io_thread.cpp:1117instead of doing this. pass in the settings to each storage_interface call. Each disk thread could hold its most recent understanding of the settings in a shared_ptr, and update it every time it wakes up from a job. That way each access to the settings won't require a mutex to be held.
relevance 0../src/disk_io_thread.cpp:1160a potentially more efficient solution would be to have a special queue for retry jobs, that's only ever run when a job completes, in any thread. It would only work if counters::num_running_disk_jobs > 0
relevance 0../src/disk_io_thread.cpp:1841maybe the tailqueue_iterator should contain a pointer-pointer instead and have an unlink function
relevance 0../src/disk_io_thread.cpp:2127this is potentially very expensive. One way to solve it would be to have a fence for just this one piece.
relevance 0../src/disk_io_thread.cpp:2388we should probably just hang the job on the piece and make sure the hasher gets kicked
relevance 0../src/disk_io_thread.cpp:2538introduce a holder class that automatically increments and decrements the piece_refcount
relevance 0../src/disk_io_thread.cpp:2789it would be nice to not have to lock the mutex every turn through this loop
relevance 0../src/settings_pack.cpp:621it would be nice to reserve() these vectors up front
relevance 0../src/enum_net.cpp:296get the MTU (and other interesting metrics) from the rt_msghdr instead
relevance 0../src/udp_socket.cpp:330it would be nice to detect this on posix systems also
relevance 0../src/udp_socket.cpp:884use the system resolver_interface here
relevance 0../src/block_cache.cpp:1033it's somewhat expensive to iterate over this linked list. Presumably because of the random access of memory. It would be nice if pieces with no evictable blocks weren't in this list
relevance 0../src/block_cache.cpp:1104this should probably only be done every n:th time
relevance 0../src/block_cache.cpp:1797create a holder for refcounts that automatically decrement
relevance 0../src/kademlia/node.cpp:720in the future, this function should update all the dht related counter. For now, it just update the storage related ones.
relevance 0../src/kademlia/put_data.cpp:93what if o is not an isntance of put_data_observer? This need to be redesigned for better type saftey.
relevance 0../src/kademlia/dht_storage.cpp:448c++11 use a lambda here instead
relevance 0../src/kademlia/dht_tracker.cpp:227Use `{}` instead of spelling out the whole function type when this is merged to master
relevance 0../include/libtorrent/config.hpp:99figure out which version of clang this is supported in
relevance 0../include/libtorrent/config.hpp:365Make this count Unicode characters instead of bytes on windows
relevance 0../include/libtorrent/performance_counters.hpp:139should keepalives be in here too? how about dont-have, share-mode, upload-only
relevance 0../include/libtorrent/performance_counters.hpp:474some space could be saved here by making gauges 32 bits
relevance 0../include/libtorrent/performance_counters.hpp:475restore these to regular integers. Instead have one copy of the counters per thread and collect them at convenient synchronization points
relevance 0../include/libtorrent/peer_connection.hpp:209make this a raw pointer (to save size in the first cache line) and make the constructor take a raw pointer. torrent objects should always outlive their peers
relevance 0../include/libtorrent/peer_connection.hpp:1040factor this out into its own class with a virtual interface torrent and session should implement this interface
relevance 0../include/libtorrent/piece_picker.hpp:750should this be allocated lazily?
relevance 0../include/libtorrent/upnp.hpp:158support using the windows API for UPnP operations as well
relevance 0../include/libtorrent/block_cache.hpp:222make this 32 bits and to count seconds since the block cache was created
relevance 0../include/libtorrent/settings_pack.hpp:1140deprecate this ``max_rejects`` is the number of piece requests we will reject in a row while a peer is choked before the peer is considered abusive and is disconnected.
relevance 0../include/libtorrent/proxy_base.hpp:192it would be nice to remember the bind port and bind once we know where the proxy is m_sock.bind(endpoint, ec);
relevance 0../include/libtorrent/peer_connection_interface.hpp:47make this interface smaller!
relevance 0../include/libtorrent/announce_entry.hpp:97include the number of peers received from this tracker, at last announce
relevance 0../include/libtorrent/heterogeneous_queue.hpp:184if this throws, should we do anything?
relevance 0../include/libtorrent/identify_client.hpp:47hide this declaration when deprecated functions are disabled, and remove its internal use
relevance 0../include/libtorrent/receive_buffer.hpp:272Detect when the start of the next crpyto packet is aligned with the start of piece data and the crpyto packet is at least as large as the piece data. With a little extra work we could receive directly into a disk buffer in that case.
relevance 0../include/libtorrent/alert_types.hpp:178Once the backwards compatibility of clone() is removed, and once C++11 is required, this can be simplified to just say = delete
relevance 0../include/libtorrent/file.hpp:175move this into a separate header file, TU pair
relevance 0../include/libtorrent/utp_stream.hpp:445implement blocking write. Low priority since it's not used (yet)
relevance 0../include/libtorrent/tracker_manager.hpp:416this should be unique_ptr in the future
relevance 0../include/libtorrent/torrent_info.hpp:117there may be some opportunities to optimize the size if torrent_info. specifically to turn some std::string and std::vector into pointers
relevance 0../include/libtorrent/torrent.hpp:208make this a raw pointer. perhaps keep the shared_ptr around further down the object to maintain an owner
relevance 0../include/libtorrent/torrent.hpp:1272this wastes 5 bits per file
relevance 0../include/libtorrent/torrent.hpp:1330These two bitfields should probably be coalesced into one
relevance 0../include/libtorrent/kademlia/item.hpp:61since this is a public function, it should probably be moved out of this header and into one with other public functions.
relevance 0../include/libtorrent/aux_/session_impl.hpp:865should this be renamed m_outgoing_interfaces?
relevance 0../include/libtorrent/aux_/session_impl.hpp:910replace this by a proper asio timer
relevance 0../include/libtorrent/aux_/session_impl.hpp:915replace this by a proper asio timer
relevance 0../include/libtorrent/aux_/session_impl.hpp:922replace this by a proper asio timer
relevance 0../include/libtorrent/aux_/session_settings.hpp:80make this a bitfield
relevance 0../include/libtorrent/aux_/session_interface.hpp:247it would be nice to not have this be part of session_interface
libtorrent-rasterbar-1.1.13/docs/troubleshooting.dot000066400000000000000000000140231351156116000226320ustar00rootroot00000000000000digraph no_download { node [shape=box]; node_peers [label="Do you have any peers?\n(torrent_status::num_peers)"]; node_unchoked [label="Have any of the peers unchoked you?\n(peer_info::flags & peer_info::remote_unchoked)"]; node_error [label="Does the torrent have an error state?\n(torrent_status::error)"]; node_paused [label="Is the torrent paused?\n(torrent_status::paused)"]; node_end_error [label="The error string in the torrent describes what\nwent wrong in the torrent. This is typically\nindicative of a fatal error that, once resolved,\nrequires you to call torrent_handle::clear_error()\nto clear before resuming"]; node_tracker [label="Do you have any trackers in the torrent?\n(torrent_handle::trackers())"]; node_auto [label="Is the torrent auto managed?\n(torrent_status::auto_managed)"]; node_outstanding_reqs [label="Do you have any outstanding requests to any peer?\n(peer_info::download_queue_length)"]; node_interested [label="Are you interested in any peers?\n(peer_info::flags & peer_info::interesting)"]; node_tracker_peers [label="Did any tracker return any peers?\n(tracker_reply_alert::num_peers)"]; node_dht_enabled [label="Is DHT enabled?\n(session::is_dht_running())"]; node_dht_nodes [label="Do you see more than 5 DHT nodes?\n(session_status::dht_nodes)"] node_peers_for_sure [label="Do you know for sure the torrent has peers?"]; node_peers_connected [label="Were any of the peers connected to?\n(peer_info::flags & peer_info::connecting)"]; node_end_wireshark_tracker [label="Wireshark the tracker announce\nfrom all your peers and make sure\nthey all send identical info-hashes."]; node_connect_speed [label="connect_speed, connection_limit, ip-filter"]; node_upload_mode [label="Is the torrent in upload mode?\n(torrent_status::upload_mode)"]; node_bwstate [label="What is the peer read_state set to?\n(peer_info::read_state)"]; node_dl_limit [label="There is a download rate limit in affect on your peers.\nDo you have a download rate limit set?\n(settings_pack::download_rate_limit)"]; node_dl_disk [label="Peers are blocked waiting on the disk.\nThis typically means your disk is overloaded"]; node_dl_socket [label="The peer is listening on the socket,\nbut no data is coming down"]; // end states node_end_queued [label="This means the torrent is 'queued'. i.e. it will\nbe started once the torrents in front of it\ncompletes downloading. To know the queue\norder, see torrent_status::queue_position. To\nconfigure the number of simultaneous downloads,\nsee settings_pack::active_limit and\nsettings_pack::active_downloads."]; node_end_stopped [label="This means the torrent is\n'stopped'. To start it call\ntorrent_handle::resume()."]; node_end_no_peer_source [label="You don't have any peers and you don't\nhave a way to acquire peers. You either\nneed a torrent with a tracker or you need\nDHT to be enabled. The DHT will not help\nfor private torrents"]; node_end_dht_broken [label="The DHT is probably not working correctly.\nYou might want to add a DHT bootstrap node\nthrough session::add_dht_router(), or have a\ntorrent with DHT nodes in it, or peer\nconnections of peers that are part of the DHT\nnetwork."]; node_end_no_peers [label="The torrent you are trying to\ndownload may not have any peers,\n and all peers you may see are stale\nand not responding anymore."]; node_end_supply_demand [label="There might be a much higher demand\nthan supply in this torrent. Waiting\naround will probably get you unchoked\neventually."]; node_end_flash_crowd [label="The torrent might have a very large number\nof peers and only very few seeds. This sometimes\nhappens with torrents that gain popularity\nvery fast, much faster than the initial seed\nand early peers can keep up distributing\nto. Typically when this happens all your peers,\nand you, will get pieces in lock-step."]; node_end_no_download [label="This means the torrent is configured to not\ndownload anything. If you want to download,\ntake the torrent out of upload only mode. If\nthe disk the torrent is downloading to is full,\nor if writing to the disk failed in some way, the\ntorrent may have switched into upload mode\nautomatically"]; node_peers -> node_error [label="no"]; node_peers -> node_unchoked [label="yes"]; node_error -> node_end_error [label="yes"]; node_error -> node_paused [label="no"]; node_paused -> node_auto [label="yes"]; node_paused -> node_tracker [label="no"]; node_auto -> node_end_queued [label="yes"]; node_auto -> node_end_stopped [label="no"]; node_tracker -> node_tracker_peers [label="yes"]; node_tracker -> node_dht_enabled [label="no"]; node_tracker_peers -> node_peers_for_sure [label="no"]; node_tracker_peers -> node_peers_connected [label="yes"]; node_peers_for_sure -> node_end_wireshark_tracker [label="yes"]; node_peers_for_sure -> node_dht_enabled [label="no"]; node_peers_connected -> node_connect_speed [label="no"]; node_peers_connected -> node_end_no_peers [label="yes"]; node_dht_enabled -> node_end_no_peer_source [label="no"]; node_dht_enabled -> node_dht_nodes [label="yes"]; node_dht_nodes -> node_end_dht_broken [label="no"]; node_dht_nodes -> node_end_no_peers [label="yes"]; node_unchoked -> node_outstanding_reqs [label="yes"]; node_unchoked -> node_interested [label="no"]; node_outstanding_reqs -> node_bwstate [label="yes"]; node_outstanding_reqs -> node_upload_mode [label="no"]; node_upload_mode -> node_end_flash_crowd [label="no"]; node_upload_mode -> node_end_no_download [label="yes"]; node_interested -> node_upload_mode [label="no"]; node_interested -> node_end_supply_demand [label="yes"]; node_bwstate -> "?" [label="peer_info::bw_idle"]; node_bwstate -> node_dl_limit [label="peer_info::bw_limit"]; node_bwstate -> node_dl_socket [label="peer_info::bw_network"]; node_bwstate -> node_dl_disk [label="peer_info::bw_disk"]; node_dl_limit -> "Your download rate limit may be set too low.\nKeep in mind that it is specified in bytes\nper second." [label="yes"]; node_dl_limit -> "mixed mode?" [label="no"]; node_dl_disk -> "e" [label="yes"]; node_dl_disk -> "f" [label="no"]; } libtorrent-rasterbar-1.1.13/docs/troubleshooting.html000066400000000000000000000072231351156116000230140ustar00rootroot00000000000000 libtorrent manual

libtorrent manual

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

The following troubleshooting chart may help in finding out why torrents fail to download. It is not complete, please submit suggestions via pull requests at https://github.com/arvidn/libtorrent or to the mailing list. Ideally in the form of patches against docs/troubleshooting.dot.

thumb

libtorrent-rasterbar-1.1.13/docs/troubleshooting.png000066400000000000000000011705471351156116000226470ustar00rootroot00000000000000‰PNG  IHDR oq<ÇţbKGD˙˙˙ ˝§“ IDATxśěÝw´VŐą/ŕßG±!*bĂ® €±F!šhŮHĐŤ¸‰‚#$bŤđ¨;xމ9ŁnŻI<š±\EDDi’ " ‚4)ľű‡—}D©R>ĘóڱąÖšë7ç^c3{˝ß[(‹Ĺ›Ś*Ą¬ۉQ ›jĄ+2hĐ Lš4©Ô1`‹tüńÇgĎ=÷,u V P,‹ĄËSVV–ÇĽÔ1`‹ô裏¦M›6ĄŽŔ č ŔF­uëÖ)///u آ …RG`Ş”:°fŔ&F1lbŔ&F1lbŔ&F1lAfÍšUęŔ: €-ÖsĎ=—ď~÷») ) 9ĺ”SrĘ)§¤iÓ¦9묳ňź˙ůźY¸pa©c®·ŢzkN:é¤Ô®]»ÔQXMąýöŰÓĄK—´k×.-Z´ČăŹ?^ęXl$ Ĺb±Xę°űlĺجYłŇµk×üęWżJ—.]rÚi§ĄK—.™9sć çéׯ_jÔ¨‘Bˇ^˝zeńâĹI’‡~8[o˝uzčˇŐšűH•*UR(’$łgĎÎí·ßľĚŘŞ|ňÉ'iÝşuj×®ťĆŤgČ!•ÇĆŽ›˛˛˛tëÖ-íŰ·O‹-2räČ$ÉăŹ?žÚµk§P(äšk®©ĽćľűîKŐŞUóŔ$IćĎźź[ną%]tQš6mšV­Zĺí·ß^i¦•Ý÷É'źLÇŽSż~ýĚś93çź~ęÔ©“&MšdčСk´ż_5xđŕ\yĺ•Ůwß}3uęÔĘ}iҤIžxâ‰ĘóVµ¦_˛dI^zéĄ\~ůĺŮwß}óá‡ćä“OÎŢ{ďť™3gfČ!9öŘcsÉ%—äÚkŻMőęŐ3wîÜ$É?ţńŹüęWżŞĽÇ“O>™jŐŞ-ł÷lą Ĺb±Xę°_ţ•˙ňĆľęŕλロ뮻.\pAFŤ•3Î8#Çw\^}őŐ$ÉA”%K–dܸq©¨¨HÝşułçž{Vľ÷Ýw§S§Nůűß˙žÓO?˝r ÝşuKż~ý’$:tH—.]Ň A$Éi§ť–#Fděر©Yłćrł­ěľ“'ONÆ 3gÎśôčŃ#çťw^ óÎ;/Íš5ËŕÁWkżjÉ’%ůűß˙ž˛˛˛|ţůçéÔ©SĘĘĘňÁ¤cÇŽ™={văŹ?~•kZŃńQŁFeĚ1iٲećÍ›—ßţö·iÚ´iyä‘ÜqÇ9ňČ#3mÚ´Lź>=IҶmŰÜu×]©[·î2Y-Z”C9$×_}Î=÷Ü~Ź×•BˇG}4mÚ´Yď÷ŕ›Q ŔFkc)ŘkŻ˝˛xńâLž<9Ý»wOŹ=2eĘ”ěşë®•çü×ýWÚ·oź«®ş*˝zőZî<ź~úiöŮgź´mŰ6}űöM’ôěŮ3Mš4Égś±Ús/}©˙Ëżň_ŢŘW-=gÉ’%•]ęÔ©“Ď?˙ĽňÓč÷»ße·ÝvK۶mS,sŕć_˙úW.\ä‹—Ň8ŕ€~řáůë_˙š$ąöÚkóŁý(‡~x^ýő4kÖlą÷ę©§rĆg,÷ŘŞî۰aĂĽ÷Ţ{ˬo×]wÍĚ™33ţüŐÚßiĐ AĆŚ“ąsçf»í¶K’ôîÝ;—]vYÚ¶m›Ë/ż|ĄkŞ[·î*׼4˙§ź~šZµjUße—]ňÉ'ź¤wďŢéÔ©SŢyçťěµ×^_+ščÓ§O>˙üó\vŮe+\Çş¤`ăWĄÔ`c¶hѢLť:5‡~x’dŕŔIňµ—µ[´h‘$•ź°ż<;ďĽs:uꔇz(~řa’äů矯ü„ýµ™{M,-H’şuëfŢĽy•żüňËóýď?÷Ţ{ozôč‘ dѢE•Ç«WŻžK/˝4O=őT&LE‹ĺ˝÷ޫܟ7Ţx#Ť7N±XüÚ×Ę^Č_Ő}żśy©ZµjeÁ‚•_Őţ®H•*_Ľ>±´ IÎ<óĚ$ÉرcWą¦ŐYóŇü_.H’űî»/5kÖĚĄ—^šcŽ9&sćĚYn÷„ńăÇo°B6 Š`%^xá…,\¸0§žzj’˙yq|âĉ˜WŻ^˝$ÉŽ;î¸Ňů®¸âŠlµŐVąăŽ;2tčĐsĚ1©Zµę:™{]xăŤ7ҤI“ě·ß~éŢ˝{¶ß~űŻťsŃEĄFŤąűî»ó—żü%­[·®<6}úôL0a™Ą–,Y˛V÷]+Űß5±űî»'IęׯżĘ5}Ó5'ÉŮgźťáÇç´ÓNË!CŇĽyó<ôĐCËśóůçźç#ŽXă5°yS +°páÂüú׿ÎG‘Îť;'ůźOéúé§—9wҤII’–-[®tÎÚµkçç?˙yîż˙ţÜyçťůŮĎ~Vyluç^ú)ó .L’‹Ĺ|öŮgkľŔĺhßľ}-ZTůiúË{™}‡vČE]”|0Ź>úh~řĂVkذaćÍ›—^˝z-sÍčŃŁs÷ÝwŻŐ}WÇĘöwMLź>=É{ľŞ5}Ó5'Éu×]—ýöŰ/Ď<óLţô§?eѢEéŢ˝ű2çl»í¶ůńŹüŤÖŔć«Z©@)}ţůçI’ůóç/3>lذ\vŮe™1cFž~úéT«öĹŻŘŻşęŞ<ţřăąë®»Ňľ}űěşë®I’{îą''śpB.ąä’UŢłK—.ąóÎ;óŻý+űďżĺřęÎݰaĂŚ=:7ÝtSÚ·oź§žz* ,H’üă˙H«V­*» |٬Ył’$łgĎNÍš5“¤˛`îÜą©QŁF¦L™’YłfĄ˙ţůä“O2sćĚ$É믿žÝwß={îąg’¤sçÎąóÎ;sÄGTîM’śuÖYŮożýrăŤ7ć>Č©§žšŃŁGçő×_ĎăŹ?ľÂ=YŐ}żúýYşŽ$©¨¨X&ĂŠöwU/^\ŮEŕąçžËQG•Ž;fńâĹ+]ÓV[mµĘ5/Íżtź—şőÖ[sůĺ—g§ťvJëÖ­óżţ×˙Ę{ě±L®Îť;güřń_+`ËVőú믿ľÔ!`yĘËË“$mÚ´Y/ó807ß|s† –™3g楗^Ę#Ź<’ňňň 80ß˙ţ÷Ó§Oźěľűî•×TŻ^=íŰ·ĎŚ3r˙ý÷gÄyţůçłÓN;ĺHőęŐWyßí·ß>Ż˝öZ~ň“źäĐC]㹏<ňČĽţúëůë_˙š‘#Gć˛Ë.Ë ArŇI'eŻ˝öJ *_jOľčpŰm·ĺ‰'žHňEDóćÍÓ»wďüůĎN’,X° 'ź|rjŐŞ•W^y%o˝őVÎ=÷Üě·ß~yíµ×ňŻý+­[·Î¶Űn›$Ůi§ť2cĆŚtéŇĄr,IŞU«–üŕ™0aBž}öŮ<˙üóŮsĎ=sĎ=÷¤V­Z+Ü“vŘa…÷ýřăŹ+sVŻ^=‡~xîż˙ţĘçcѢE9ńÄ+ V´ż+rĎ=÷dÚ´iŮa‡Ň AĚť;7 Č}÷Ý—m¶Ůf•kZŮń­·Ţ:={ö¬Ě?mÚ´ÔŻ_ż˛ĐŁ{÷îéßżfÎś™ľ}ű¦^˝zyđÁłÓN;Ućűóź˙śO>ů$?ýéOWą–uĺ†nHYYY5j´Áî Ŕš)‹Ĺb©CŔň”••%ůź˘€ÍĹĽyórŘa‡ĺ­·ŢZćEzÖŤ5Ýß>8ďľűnĽBń? …B}ôŃőVŔÚűzO@`˝şçž{Ň©S'…ë‰ý`KP­Ô`KđÚkŻĄC‡™7o^/^śwß}·Ô‘6+kłżsçέüłFŤë+"¬S:ŔPŁFŤĚš5+UŞTÉĂ?ś­¶ÚŞÔ‘6+ßdçÎť›«Żľ:“&MJ’tîÜ9^ßQ`ť(‹Ĺb©CŔň”••%IĘËËKś¶,…B!Ź>úhÚ´iSę(¬€Î°‰Q ›ۉQ Ŕfkěرąí¶ŰJ6¨Ĺ‹§[·n™řŕ&Z3ë#ë7™sSÚ3ľPµjŐtíÚ5ť;wÎűďż_ę8¬'ŠŘěŚ=:íŰ·Ď]wÝ•ęŐ«'I&NśvíÚ•8ŮęYYżÉś›Ňž±¬Zµjĺşë®Ë™gž™ąsç–:ëb6+Ĺb1çťw^.¸ŕ‚ěĽóÎI’É“'ç{ßű^>ůä“§[µő‘ő›Ěą)íËw衇f˙ý÷Ď/ůËRG`=P ŔfĺÉ'źĚ›oľ™ÓO?˝rěřCFŤ•Ź>ú(?˙ůĎ+ÇgÍš•®]»ćWżúUşté’ÓN;-]ştÉĚ™3łdÉ’ĽôŇKąüňËłďľűćĂ?ĚÉ'źśúőëçŻýë×Ć÷Ţ{ďĚś93óçĎĎ-·Ü’‹.ş(M›6M«V­ňöŰoWfëرcęׯź™3gćüóĎOť:uҤI“ :tĄYWÇ!Crě±Çć’K.ɵ×^›ęŐ«gîÜą+śsěر)++K·nÝŇľ}ű´hŃ"#GŽ\aŽx UŞTIˇPH’Ěž=;·ß~ű2c+Ë‘$/ľřbęׯź—_~ąňüŻŽ­Î>­N–yóćĄ_ż~i×®]N8á„ <8GydöŮgź 80cĆŚÉřĂÔ­[7|pĺÜ«kđŕÁąňĘ+łďľűfęÔ©iÝşuj×®ť&Mšä‰'ž¨š6mÚ¬ö5lXŠŘh}“b€}÷Ý73gÎĚŚ3–˙ę‹íÝ»wOŹ=2eĘ”ěşë®•çý×ýWÚ·oź«®ş*˝zőJÆ óŢ{ďĺÓO?M­Zµ*Ď[Ţř믿žfÍš-7×SO=•3Î8Łňş/˙ş~×]w­ě*°Ľ¬«k—]vÉ'ź|’Ţ˝{§S§Nyçťw˛×^{Ąf͚˝ówżű]vŰm·´mŰ6Ĺb1x`ţőŻeáÂ…+ĚqđÁçÝwß]&˙WÇV–#I/^śŞU«.“ý«c«łO«“eyëŘsĎ=3yňäeΩWŻ^.\řµçfU4h1cĆdîÜąŮn»í’$˝{÷Îe—]–¶mŰćňË/_é3Q·nÝŐ~fľú ®jź“dĘ”)Ů}÷ÝóÝď~7O?ýôjŻK1ŔĆŻJ©ŔşôŃG-óÂôŠ 80I–yq:IZ´h‘$yőŐW“|ńRt’ŻÍąĽń7Ţx#Ť7®üTú/ťqĆË\÷eµjŐĘ‚ Vk}+sß}÷ĄfÍšąôŇKsĚ1ÇdÎś9_[ß—]~ůĺůţ÷żź{ď˝7=zôČ‚ ˛hѢőžă«…Ë[źű´Ľ=Ůyçť3sćĚ5ž«J•/^˝XZ$gžyf’děر«|&Öä™ůę3¸:ßďťvÚ)I2uęÔ5^7ĹlVŞV­šĹ‹ŻňĽĄ/qOś8q™ńzőę%IvÜqÇ5ľ÷ôéÓ3a„̛7ďkÇ–,Y˛Ćó­©łĎ>;ÇĎi§ť–!C†¤yóćy衇VxţoĽ‘&MšdżýöK÷îÝłýöŰ—$Çćf÷ÝwO’ÔŻ_•ĎÄÚ<3«łĎË+Ş`ó €ÍĘn»í¶ÜOx/ ©¨¨¨üűŇO?ýô2çMš4)IҲeË5ľwÆ 3oŢĽôęŐk™ńŃŁGçî»ď^íyľšuu]wÝuŮożýňĚ3ĎäOúS-Z”îÝ»ŻpÎöíŰgѢE9ýôÓ“|ýĺóĺ]łôĺň… &IŠĹb>űěłŐΑdąĹ«SŔńU«“Ą¦Oźžä‹ghUĎÄÚ<3«Úç$™1cF’d×]w]Űe°‘Q Ŕf夓NĘěŮł3gÎśeĆ÷ß˙L™2Ąňe˙«®ş*Ť7Î]wÝ•Ź>ú¨ňĽ{îą''śpB.ąä’$Éüůó“$sçÎ]fľĺŤźuÖYŮożýrăŤ7ć /ĚĂ?śk®ą&—]vY.¸ŕ‚e®ű˛Ůłg'Iĺ‹÷_Íşşn˝őÖĘBÖ­[gÇwĚ{ě±Â9§L™’É“'§˙ţyřá‡+Ż}ýő×óÁ,÷š† &Inşé¦Ś7.˝{÷΂ ’$˙řÇ?˛dÉ’•ćxúé§łÓN;ĺ™gž©śsyc«łO«“eé<Ĺb±ržE‹%É2ĎČŇóľi‡/3<÷Üs9ꨣұcÇU>kňĚ|ő\Ů>/5mÚ´$ɉ'žřŤÖŔĆK1›•öíۧX,fĐ AËŚ—••e‡vČoĽ‘$ŮvŰm3hĐ ´k×.?ýéOsĺ•W¦k×®©]»v^xá…,\¸0żůÍo2qâÄ$ÉW\‘áÇgŢĽyËO’­·Ţ:/ĽđBÎ<óĚüĺ/I—.]ňńǧ_ż~©Yłfî˝÷ŢĘëzôč‘YłfĄwďŢ™­*ˇ~«Żľ:I2qâÄ<˙üóyöŮgóĎţ3IrőŐWçÓO?ÍÝwß]9vŰm·U~˛˙š¸ăŽ;2}úô|ňÉ'™2eJ^zéĄT«Vm•ĎÄĘŽW­Zu…ĎÚŞľßK 80U«VM›6mÖxMlÜ Ĺ/—ľŔF¤¬¬,IR^^ľFםqĆ9č ň»ßýn}Ä‚J|pŢ}÷Ýl¬Ż_śyć™Ůu×]Ó·oß5ş®P(äŃGUD°Ó€ÍÎď˙űüíoËÔ©SKeť( «üzď˝÷Jsł˛9ěůkŻ˝–1cĆä¶Űn+uÖĹlvvŮe—üź˙órůĺ—gŢĽyĄŽłÖŠĹâ*ż4hPꛕŐÝóąsç&Iĺź‹)S¦¤GŹyîąçRłfÍRÇ`=P Ŕf©qăĆéŃŁGîąçžRGa34wîÜ\}őŐ™4iR’¤sçÎ=uęÔ)uX.Ĺl´Ž;î¸RGŘhÍź??Æ ˇ~oűŰ©Yłf©#mV¦NťšW^y%őęŐK“&M˛ÓN;•:ŇŐşuëÔŻ_żÔ1X‰B±X,–:°úĘËËó‹_ü"Űoż}xŕ´lٲԑ6K J·nÝ2`Ŕ€´nÝ:=zôČXęX$©RęŔę™:ujZ·nťsÎ9'?úŃŹňÖ[o)XŹŽ;ôŇKyöŮgóŢ{ďĄQŁFéرc¦L™Ręh 6ĺĺĺiܸq†šţýű§Oź>©Yłf©cmZ¶l™aÆĄ_ż~yöŮgsŔ¤[·nůěłĎJ €-bŘMť:5gź}ve7€‘#GćÔSO-u¬-N•*URVV–÷Ţ{/żűÝďňŕf˙ý÷OŻ^˝2ţüRÇ` T(‹ĹR‡ľ®ĽĽĘŹ~ôŁśsÎ99űěłóÖ[o)ŘČÔŞU+={öĚرcóťď|'_|q=ôĐ”———:[Ű)//OăĆŤ3|řđ<˙üóéÓ§O¶ß~űRÇböŘcŹôéÓ'ożýv5j”sÎ9'Ç|^~ůĺRG`3§6}ôQ~řĂ.Ó ŕ[ßúV©c±š4hÇ{,Î6Űl““N:)­ZµĘ[o˝Uęhl¦@‰•——§QŁFyë­·ň /č° ;ćcň /¤˙ţ™>}zŽ8â´iÓ&ďż˙~©Ł°™Q %2eĘ”üŕ?Č9眓֭[ç­·ŢĘÉ'ź\ęX¬-[¶ĚСCóČ#ŹäÍ7ßLÆ Ó±cÇ|üńÇĄŽŔfB1”@yyy7nś‘#GćĹ_Lź>}RŁFŤRÇb* )++ËčŃŁs×]wĺÉ'źĚnÝşeöěŮĄŽŔ&N1l@S¦LÉYgť•¶mŰVv8餓J‹ő¨zőęéСCĆŤ—«Żľ:÷Ýw_>řŕôíŰ7ĄŽŔ&J1l K»Ś5J7€-PŤ5ҵk׌?>çťw^:wîśĆŤ§ĽĽ<Ĺb±ÔńŘÄ(€őlĘ”)9óĚ3+»Ś1"-Z´(u,J¤Nť:éŮłgĆŚ““N:)?ţńŹÓ¬YłĽđ ĄŽŔ&D1¬'Ĺb1üăÓ¨QŁŚ=Z7–±×^{ĄOź>1bDöŮgźśzę©iŐŞUŢ|óÍRG` Öţóźůö·żť /Ľ0?ůÉOt`…5j”Ç{,ŻĽňJćĎźźŁŹ>:mڴɸqăJ €ŤbX‡ŠĹbúöí›&MšäĂ?ĚŔÓ»wďl·ÝvĄŽĆFî„NČ€ňěłĎfôčŃ9äCұcÇ|ôŃGĄŽŔFH1¬#'NĚ·żýí\|ńĹůĹ/~‘7ß|3ÇsL©c±‰iٲe† –»ďľ;O=őT8ŕ€tëÖ-ź}öY©Ł°)‹Ĺb©Cž…> IDATŔ¦¬X,ćČ•W^™˝öÚ+ż˙ýďÓ´iÓRÇb30oŢĽÜu×]éŮłgŞV­š_ţň—ąôŇKłÍ6Ű”:%¦ÖÂĉsŃE套^J—.]rĂ 7dë­·.u,63ź~úinąĺ–Üyçť©[·n®ľúę\xá…©Zµj©ŁP"UJ6EĹb1}űöM“&M2uęÔĽúę«éŮł§BÖ‹ťwŢ9={öĚ1crúé§çâ‹/Îa‡–ňňňRG DŔz˙ý÷Ó˛eË\|ńĹąřâ‹3dČ4mÚ´Ô±ŘěąçžéÓ§OFŽ™C9$çśsNN8á„ 0 ÔŃŘŔŔjZÚ ŕĐCÍÇśAé@I4lŘ0Ź=öX ”­¶Ú*-Z´H«V­2räČRG`Q «áý÷ßĎ©§žZŮ `čС9účŁK‹-\łfÍňâ‹/¦˙ţ™6mZ?üđ´iÓ&'N,u4Ö3Ű_î0mÚ´ <8={öĚV[mUęhP©eË–:thyä‘ :4Ť5J·nÝ2cĆŚRG`=)‹Ĺb©CŔĆh„ ąđ óĘ+ݤK—.ąńưŃ[¸paţđ‡?äÚkŻÍÂ… Óµk×tîÜ9Űn»m©Ł°é _ńĺnź~úi^{í5ÝŘdlµŐVéСCĆŤ—®]»ćć›oÎA”ľ}ű¦˘˘˘ÔńXGŔ—Ś?>ßúÖ·rÉ%—ä’K.ÉoĽ‘#Ź<˛Ô±`ŤmżýöéÚµkĆŹźsĎ=7ť;wN“&MR^^žb±Xęx¬%ĹdÉ’%éŰ·o;ě°Ě1C76uęÔIĎž=óŢ{ďĄE‹i۶mŽ;ř⋥ŽŔZP ŔoyÝŽ8âRÇ‚ujď˝÷Nź>}2bÄÔ­[7§śrJZµj•áÇ—:߀b¶X_îđŮgź­´@ż~ýRŁFŤ …ôęŐ+‹/N’<üđĂŮzë­óĐC%IćĎźź[ną%]tQš6mšV­Zĺí·ß®śgČ!9öŘcsÉ%—äÚkŻMőęŐ3wîÜ ł`HҸqăü÷˙wú÷ďź3f䨣ŽJ›6m2a„µš÷É'źLÇŽSż~ýĚś93çź~ęÔ©“&MšdčС•çÍš5+]»vÍŻ~ő«téŇ%§ťvZşté’™3g®íҶ(…b±X,uŘĐĆŤ— /Ľ0 ĘW\‘ßüć7©^˝úJŻąćškrÓM7eÔ¨Q9äC’$“&MĘĄ—^š'žx"IҡC‡téŇ% 4H’śvÚi1bDĆŽ›š5k¦A™6mZ¦Oźž$i۶mîşë®Ô­[w=®–ŻX,ćńÇĎŐW_ťţóź9˙üósăŤ7¦^˝zk<×äɓӰaĂĚ™3'=zôČyçť—äĽóÎKłfÍ2xđŕĚ™3'GuTÚµk—ë®».IňÉ'źäÄOLEEEŢ|óÍě¸ăŽëz™›%ťآTTT¤wďŢ9ě°Ă2kÖ¬ĽţúëéŮłç* ’äňË/OÍš5sÇwTŽőë×/^xa’äő×_Ď<† ¦P(¤P(äŮgźÍÔ©SóňË/'IfĚ‘O?ý4wŢygŠĹb®ąćšlłÍ6ëg±° …B!eee5jTîşë®ü÷˙w8ŕ€tëÖ-łfÍZŁąöŘcŹě±ÇI’_˙ú×ŮkŻ˝rîąç¦^˝z>|x’¤gĎž3fL:věXy]ÝşuÓ˝{÷L0!7ß|óş[ŔfN1[ŚwŢy''śpBşuë–kŻ˝6C† ÉᇾÚ×ďĽóÎéÔ©Szčˇ|řá‡I’çź>§ź~z’äŤ7ŢHăĆŤS,żöuĆg$Iî»ďľÔ¬Y3—^ziŽ9ćĚ™3'5kÖ\÷‹…5P˝zőtčĐ!ăĆŤK÷îÝs˙ý÷g˙ý÷OŻ^˝˛`Á‚Őž§P(|m¬V­Z•s 80IľöĚ·hŃ"Iňꫯ~Ó%lq°Ů«¨¨HŻ^˝rä‘G¦P(äÍ7ßL×®]SµjŐ5žëŠ+®ČV[m•;î¸#C‡Í1ÇS9ĎôéÓ3a„̛7ďk×-Y˛$IröŮggřđá9í´Ó2dČ4oŢ<=ôĐÚ-Ö‘í¶Ű.]»vÍřńăsá…ćúëŻO Ň·oßĘgxmT©ňĹ«*'N\fĽ^˝zI’wÜq­ď°ĄP ŔfmÔ¨Q9ţřăsýő×ç†nČŔsđÁăůj×®ťź˙üçą˙ţűsçťwćg?űY屆 fŢĽyéŐ«×2׌=:wß}w’äşë®Ë~űí—gžy&úÓź˛hѢtďŢýçőˇvíÚéŮłgĆŚ“ÓN;-żřĹ/rŘa‡ĄĽĽ|­ć]Úŕé§ź^f|ҤII’–-[®Őü[’B±X,–:¬kąí¶ŰrÝu×ĺ#ŽČ>¸VE_6uęÔě˝÷Ţ9î¸ăňâ‹/VŽ/X° ‡rH&Lźýěg9őÔS3zôčĽţúëyüńÇSłfÍÔ¨Q#“'OÎN;í”ŠŠŠÔ©S' 6ĚŕÁ×I6XŢyçť\ýő)//Ď 'ś^˝zĺ„NřÚyűî»o&Nś/żŽ˛çž{fňäÉY´hQ-Z”cŽ9&3gÎĚoĽ‘]wÝ5IrŮe—eČ!ůż˙÷˙¦Zµjl]›2ťŘěĽýöŰ9î¸ă*»ĽňĘ+ë¬ IęŐ«—V­Zĺ /\f|ë­·Î /Ľ3Ď<3ůË_ŇĄK—|üńÇéׯ_jÖ¬™$™7o^N=őÔôęŐ+çź~š7ožGydťeőáCÉcŹ=–AĄjŐŞiŢĽyÚ´i“±cÇVžsď˝÷fâĉI’=zdÖ¬YéÝ»w&Ožś$ąćškR(2hĐ ´k×.?ýéOsĺ•W¦k×®©]»v^xá…k@g6_îpä‘GćÁLÆ ×ů}ćÍ›—Ă;,o˝őV¶ÝvŰu>?lěž{îątéŇ%ŁGŹÎ\믿>»í¶[©clQt`ł°´Ŕ 7Ün¸! X/…IrĎ=÷¤S§N ŘbµlŮ2Æ Kż~ýŇż˙pŔéÖ­[fÎśYęh[ ťؤ-ípíµ×ć裏Î> ¬óűĽöÚkéСCćÍ›—Ĺ‹çÝwßÍV[mµÎ… ćřC®ąćšTTT䪫®ĘĄ—^šm¶Ů¦ÔŃ6k:°É9rdŽ=öŘÜpĂ ąńĆóňË/Ż—B€$©QŁFfÍš•*UŞäá‡V˙ßV[m•:düřńąęŞ«ŇŁGŹtĐAéŰ·o/^\ęx›-ťŘäl¨nŔš›:5kÖÔ ¶0µk×NĎž=óŢ{ďĺä“OÎŹüă4kÖ,Ď?˙|©Łl´üŻ %őÚkŻĺČ#ŹĚ}÷Ý—{ď˝7/˝ôR8ŕ€RÇJ`Ż˝öJź>}2bÄěłĎ>iٲeZµj•7ß|łÔŃ6:Š(‰ĄÝN8á„ÔŻ_?#GŽL‡R(J (±FŤĺ±ÇËŔł`Á‚}ôŃiÓ¦MĆŤWęh ĹlpÎGQŮ ŕ™gžÉ^{íUęXŔFćřăŹĎË/żśgź}6ďľűn9ätěŘ1S¦L)u4€’S Ŕ:óöŰoŻôřŇn'žxböŢ{oÝ€ŐҲeË ><ýúőËłĎ>›<0ÝşuËgź}Vęh%Ł€u˘˙ţ9účŁ3räČĺ4hPŽ8âÜ˙ýą÷Ţ{ó÷ż˙]7`µU©R%eee5jT®ąćšôíŰ7űďżzőę•ůóçŻňúgžy&‹/ŢI6 ŬµÉ“'çśsÎÉÂ… sŢy祢˘˘ňŘçźžnÝşĄyóćŮgź}tÖĘvŰm—®]»füřńąč˘‹rĂ 7¤AéŰ·ď _ö˙řăŹÓşuëüŰżý[ŠĹâN °~(`­TTT¤¬¬,sćĚI±X̨QŁŇ«WŻ$É«ŻľşL7€żýío©_ż~‰›ZµjĄgĎž3fLN?ýô\|ńĹ9ôĐCS^^ţµsoşé¦,X° =ôPşuëV‚´ë^ˇ¨Ü€µĐ­[·Üzë­Ë|"wµjŐŇ®]»üď˙ýżóťď|'}űöÍî»ď^”ŔćîÝwß͵×^›Ç<Ç{lzőę•ćÍ›gâĉ9đŔ+;– …ÜrË-ąňĘ+Kś`í(ŕűŰßţ–ď}ď{ůꯞ«U«–Úµkç·żým.¸ŕ‚Ą¶DŻ˝öZ~ő«_ĺĹ_LË–-S­Zµ<˙üóY´hQĺ9…B!˙ń˙‘źýěg%L °vŞ”:›¦I“&ĺÜsĎMˇPřÚ±ŠŠŠL›6-}ôQ ’[˛fÍšĺ…^H˙ţý3iҤüă˙X¦ IŠĹbţíßţ-O<ńD‰R¬=ťXc‹-JóćÍóć›o~í%Ű/«^˝zŢ|óÍ4nÜx¦řB«V­ňŇK/-÷çTˇPHőęŐóÜsĎĄyóć%H°vt`Ťýň—żĚ!CVZ|ńéŰíŰ·OEEĹJđ…—_~9Ď=÷Ü N‹ĹTTTä;ßůN† ¶Ó¬=Ŭ‘?˙ůĎąóÎ;łxń╞WµjŐ‹Ĺ 6,<đŔJđ….]ş¤Zµj+=gÉ’%Y°`AZ¶l™1cĆl dëFˇX,K€MĂ?˙ůĎz衙3gN–,Y˛Ě±ŞU«¦P(¤˘˘";í´SN=őÔ4oŢ<'žxbŽ<ňČ …Ą¶4O<ńDÎ>űěT­Zu•…KIR˝zőěľűîyíµ×RŻ^˝ `í)`µ,X° Íš5ËČ‘#łdÉ’ …T­Z5Ůa‡ň­o}+-[¶Ě·ľő­4jÔ¨Ôq-ŘСC3`Ŕ€Ľűî»5jTŢyçť|úé§Iľ(\Ş^˝z,X/ż6S­Zµ4lŘ0ŻĽňJvÜqÇREXmŠŘ( 4(·ß~{©c_2lذŚ?>É/ÍÖ©S'őęŐË.»ě’vŘÁ'˙łNwÜqąâŠ+JŁdüű·ţ,Z´(łgĎάYł2{öěĚž=;3gÎĚçźľLQ@ť:uŇĽyóT­Zµ„iaĂŰŇţlŠŞ•:,ϤI“ňř㏧uëÖĄŽ$™2eJćÎť›&Mšd—]vÉN;íäĺÖąÁ—:BÉů÷oý©^˝zvŢyçěĽóÎËŚ/Y˛$sçÎ]¦H`ěرiذa‰’†çç/Ŕ¦I1µňňňRG`)+++u„Ť†˙€ ÉĎ_€MS•RÖŚbŘÄ(€MŚbŘÄ(€MŚb`‹7kÖ¬RGŘb¬‹˝^´hQ¸Ň|Ýgź}¶^ćVĚĎŕŐgŻ€/S ŘË/żśüŕ) ) ąôŇK3nܸ$ɸqăŇąsçĘcgťuV^~ůĺ'Ţ4Üu×]éÖ­[N9ĺ”´hŃ"cĆŚYĺ5·ŢzkN:é¤Ô®]{$ܲ­‹˝ž1cF~ýë_§V­Z9ńÄ×Y¶ äć›oÎńÇżQ= ąýöŰÓĄK—´k×.-Z´ČăŹ?^ęXldž{îą|÷»ß­üwă”SNÉ)§ś’¦M›ć¬łÎĘţçfáÂ…ĄŽą\~Ż>{ĹŠř·`ËV(‹˙Ź˝;«˛Zô8ţۢ˘28` ŕ@†ĄeÚ e&Ů©°:ˇfG9¦Ą·Á¨c)™&·+•&Ą9UÉ!¤2ŹyňŠÖ)Ĺ4QÓś)ÁYP†u˙ča_Q† ˘đűyźÖ;ý޵ֻÖ>‡wmŚ˝C—Z¸pˇúőë§Úúë¬C‡ÉËËKÍš5ÓŃŁG/ŰŢĽys;vLşá†ě°fůđĂőć›o*++KŮŮŮ\ďż˙ľFŚaçtWGź>}$I‰‰‰vNb?•™˙222äíí­–-[*--M’dŚŃŇĄKőĘ+ݍNť:úć›oÔ®]»«Űf×r ľřZŐ]yY™Żj¦ĘôÁŠSUsă/@ÍÄ_ě č˙Ňľá·¨ś…¶™6mšĽ˝˝ĺŕŕ WWW}őŐW%.Ř»wŻž~úiëĎÎÎÎj֬ٵŚzÝŞĘşöđđ¨’ó\¬^˝zjŢĽy•ź÷JĚž=»ŘbˇđđpIĽ¨‰ËyyyIúó9+b±Xô裏jŐŞUĘÎÎVďŢ˝uîÜ9{E”tmÇŕKŻUťŮ’•ůŞć©L¬Ě1Ě×7¨ń8 ‹ĹRć>ééézôŃGuěرk” ¸2………Z´h‘őçăÇŹK’|}}í 5Đ 7Ü ˙ůź˙Ńüˇ‰'Ú-ǵkŇx_“˛Âv•i×Ęöć €ë‹€"55U}úôQdd¤ÂĂĂuď˝÷ę·ß~“$}ůĺ—jҤ‰,‹ĆŽk=fÚ´irppЬYł$IçΝӻᆱgź}VwÜq‡BBB´uëÖJ_÷_˙ú—† &___eeeiĐ AjÚ´©Ú·oŻ 6H’ćÍ›'Y,ĹĆĆŞ  @’4ţ|9;;kÎś9Ą^űôéÓ5j”Ţxă Ť1B˝zőŇ#”••%IZşt©žţyĺääčđáĂzţůç­?_ęź˙ü§¶mŰfÝďRÇŽSXXš4i˘ŕŕ`Ą¤¤X·U´ŢÖ®]«×^{M-[¶Ô‘#G¬çmßľ˝ľţúk›Ď[ÚöÂÂBýřăŹzőŐWŐ˛eKedd¨{÷îňóóSVV–RRRtçťw꥗^Ň[o˝%''§ëD’fÍšĄ:uęXSś9sF“&M*VfK;KRNNŽĆŹŻ*""BÝ»wW\\\…꺼6/ÉĉUŻ^=˝öÚkJNN¶©nĎž=«#Fhذa;v¬FŹ]j±µ/—w¶Ôą$ýď˙ţŻŢxă ëĎ˙ú׿äččXěl&-_ľÜZfËłVŢsT‘±ćZŽÁĄ]«¬{.m\őőőŐâĹ‹KoËĘfËŘY^˝”¤¬şŞ©źJSŃy°ĽöŞL(:¶$Ą=ĄµkYőTÚ1ĺµsŔuÎŐPBB‚©íżÎ’Tîż‹µnÝÚcŚÉËË3nnn&88ŘşýŁŹ>2’Ěwß}g-Űżżyúé§­??÷ÜsfçÎťÖź|đAăééiNź>]jβ®{đŕAÓ°aC#ÉDGG›}űö™ąsçI¦K—.ÖsŚ3ĆH2۶m+–í‰'ž(őşgÎś1mÚ´1QQQÖ˛ŁGŹš6mÚ€€“••U¬.‚‚J=WYűIfܸqfďŢ˝féŇĄF’ąë®»¬űT¤Ţ Ě·ß~kęׯo$™áÇ›ź~úÉĚź?ß4jÔČH2ÉÉÉ6ť·´íÇŹ7kÖ¬1 40’Ě;ďĽcV¬Xaž}öY“ťťmÚ´ic<<<¬Çőë×Ď=z´Ôz Ľ¬ż]\fK;çĺĺ™îÝ»›šÂÂBcŚ1łgĎ6’Ě’%KlŞk[ŰĽč<ĆsňäI3pŕ@łeË–bůËŞŰüü|ÓĄKóÜsĎY·˙ńÇĆŃѱÜq§Ľľlë=”Wç—şpá‚iŐŞ•™;wn™ůjş°°0fďvUŮůŻĽqđ†n0Mš41ĆŘŢOËŁ*:Ö\‹1¸´k•wĎGŹ-q\ 7IIIĄŽ·eełuŽ´ułĄ®jňg…’Tt,«˝>\©>PÔÖ%)ë(©]Ëkź’Ž©H{\É\Ář P3Őî˙÷5Öő˛ ´—˙.~ٹȤI“Ě‚ Ś1ĆšŔŔ@ăäädÝ~áÂsăŤ7šŢ˝{[ËĆŽk6mÚdŚ1fÝşuĄ.:řöŰoKÍYŢu۶m{YVOOOăěělýůĉ¦QŁFĹ^ľ~çťwĘĽî›oľi$™C‡+ŹŹŹ7’ĚČ‘#­eU± čvcŚiҤ‰iĐ 1¦ňőÖ¦M#ÉäääXË&Ožl$™§žzŞÜóÚrݢş?yňd±k7kÖĚH2qqq¦°°ĐlÝşµĚ—8Kęo—–•×Γ&M2’Ě®]»¬ŰóóóÍěŮłMff¦Mumk›ť'--Í 2Ä;v¬ŘţĺŐÝ”)SŚ$łcÇŽbǵYYĘëË˝‡‹•TVdúôéć>(3[mŔ˨Wo1€ŻŻŻńňň2ĆŘÖOm*:Ö\Ë1řŇkŮúl–6®–T^‘qúb—Α] PZ]Sł?+\ęJćÁ’ĘŻ´”¤¬g ¤v-Żž.=¦˘íq%să/@ÍTGj„W_}Uˇˇˇúř㏭óçĎ+//ĎşÝÉÉIúöŰo•––¦ĽĽ<íÚµK:t$­_ż^ÁÁÁ2~iX±Ź<ňHĄŻk±X.;ĆÝÝ]çĎź·ţěááˇáÇkÎś9ĘČČ$­\ąR=ôP©×MNN–$5jÔ¨Xů˝÷Ţ+IZłfM©ÇVĆĹ÷ѬY3ĺććJŞ|˝Ő©óçŻc4h`-ëÝ»·$)55µÜóÚrݢĚîîîĹ®=mÚ45jÔHęÜął˛łł/«Ç+©ź"·óţóI’ŹŹŹu» $77·RĎuq]W´Íyäĺää¨iÓ¦ĹĘË«»ĺË—K’üýý‹WÔfe)Ż/_­~űÇč•W^©Ô±@^^žŽ9bťlé§¶ŚAU9ÖTő|)[źÍŇĆŐ’Ę+2N_ěŇ9˛˘J«+©fV¸Ô•Ě%•_i(IEźňę©2up1ć €ë‹€býúőjßľ˝4fĚ5lŘđ˛}ž}öYą¸¸hĘ”)úć›ofÝvâÄ ĄĄĄ{i°Haaá]×˙řÇ?T·n]Mžůä“úő×_Ő«W/Ą¤¤¨[·nš3gN…˛VÔ‘#G$ýąĐˇ˛*Úćďż˙ľ[¬ĽĽşKOO·îWeőĺ«ŃoĎž=«Ž;V*+ Iß˙˝.\¸ x@’mýÔ–1čZŚ5U5_Ťgł*燪RÓ?+\¬Şë÷jôŠ>­§ŠÔsŔő‰Ĺ@5TŇKnáááĘËËł~yIű4nÜXĎ>ű¬>űě3%$$č‰'ž°n Rnnîe/NďرCS¦L)5‹-×µE“&MôüóĎkúôéúđĂ5xđŕ2÷/ú¦ŢĄK—+?pŕ€$©gĎžÎ`±X”źź_ˇc*[o%ő’ⲠIDAT)zůĽgĎžĺž÷J®;nÜ8hٲeZ°`ňňň4fĚR÷/úä .H’Ś1:uęT…îíÖ[o•$EGGËc-ß·oźľűî;›ÎQŃ6řá‡5zôhŤ=şŘ5l©Ű’®c«˛ú˛­÷P‘:Ż_żľú÷ď_©¬Ŕ… 4zôhuěŘQ/żü˛$Űú©-cPeĆšk5_z­«1§TŐüP™z)MM˙¬p±ŞśĄ«ÓĘzJj×ňęéŇc*RĚ×)TC ¦6˙:+''ÇH2®®®¦°°°Ř¶ÂÂBÓ A#Éś;wÎZîęęj,‹Yľ|ą™7ožiŢĽą‘dÖ­[g8`ÝoĎž=ĆÁÁÁŚ?ľŘyĎť;gŚ$3xđ`3oŢ<3fĚóŕšÓ§O—šµĽëúűű_ÖVŢŢŢF’ÉËË+V~řđaăěělşwď^nĺććšŕŕ`ăăăc:d-Ź0÷ÜsŹőÜ'Ož4’L@@@ąçlŐŞ•qqq1ű÷ď·–yyyIĹęŕ†n0’LvvvĄë-((ČH2ůůůÖ˛9sćŰożÝäĺĺ•{^[®[T÷ŮŮŮŮݠA“™™iŚ1&//ϸşşš.]ş”šő‰'ž0’ĚرcMjjŞůŕڇ‡‡‘d–-[f Ęmç´´4ăââb$™=z©S§š±cÇšaÆYűxyumk›·lŮŇH2………&??ßôčŃĂ¸ąą™M›6cĘďëżţú«qtt4Mš41Ë–-3ąąąćűďż7Ť76’Ěž={J­«"Ąőe[ďÁ–:/2|řpóđĂ—›©6 3aaaöŽaW•™˙rssŤ$ăďď_¬|ăĆŤćŢ{ď5-[¶4Ű·o/¶yýÔ–1¨˘c͵/˝–­ĎfiăjIĺ§/véYR˝”¤Ľş2¦v|V¨LýÚŇ^WÚJRÖ3PR»–WO—S‘ö¸Ňą‚ń frŠŠŠŞŇŐ@ضm›ľüňKŐĆ_g%''+&&F7nÔůóçuţüyůůů©I“&JMMŐĉőĂ?H’RSSĺáᡖ-[ŞqăĆZ˝zµ¶l٢żýío Đşuë´˙~………©~ýú’$777effjÄÖ2IrttÔăŹ?®´´4-_ľ\+W®”ŹŹŹ¦Nť*ww÷Ró–uÝŁGŹjѢE’$'''učĐAÓ§OWbb˘$)//O]»v•ŁŁŁ$©aÆZ·nť¨[nąĄĚzrrrRxx¸2335}útmŢĽY+W®”›››fÍš%'''mÝşU111JIIŃ©S§d±Xäîî.OOĎĎyřđaíرC:tPPP&Nś¨ŻżţZ’töěYuëÖMqqqÖ{:ţĽzöě©'ź|˛Âő6uęT?~\Ť7V۶m•““ŁU«ViÚ´iŞWŻ^ąíQÖvgggĹÄÄXs?~\ľľľjѢ…$iĚ1JJJRVV–fÎś)OOO}öŮgrss+1ëm·Ý¦_~ůE‹/Öożý¦W^yE?˙üłî»ď>ÝxăŤZąrĄĘlçG}Týë_•––¦ 6híÚµjŐŞ•&L ggg›ę:$$DĎ<óL©mžťť­‰'ZŹqvvV«V­äęęŞ hÁ‚˛X,ęرŁúőëWjݶhŃB÷ßż6oެ)S¦hÎś9jѢ…Îś9Łżüĺ/ňňň’źźźőŰűKRZ_¶ĄßÚRçm۶•$iѢE:věţţ÷ż—š§¶(ęS}űöµsű©čü—śś¬ &hÓ¦MĘĘĘŇŹ?ţ¨/ľřB‰‰‰JNNVhh¨fĚ!///ë1¶ôS[挊Ž5×r ľřZ7ÝtSą÷ś——Wâ¸Ú¸qăRÇŰňęčăŹ?Öüůó­u^ÚyüřńbY/eڱ©®şwď.ww÷˙YÁ–,ĄÍeµWeű@ŃÜZ’˛žKű`yő¦S§N;¦"íqĄsă/@Íd1梿ĎT .Tż~ýÄŻłj—ÜÜ\Ýzë­Ú˛eK±—kŁ›nşI;wî¤×R×S_ľ–úôé#é˙_J˝1˙°Ć_€š©Ž˝¸~Lť:UÇçĺiÔxôe`oŽĺď•·nÝ: :Tąąą*((ĐÎť;íéšČÉɱţ×ĹĹĹÎiP®×ľ Ş'ţ2€«ĘĹĹE§OźVť:u4ţ|Ő­[×Ţ‘®Şśś˝ůć›:pŕ€$éĺ—_ÖÚµkíś UázëË ză/¸Ş‚µgĎ{Ǹf\\\­ččh{GA»Ţú2¨ŢřËÔ0, †a1€ ;zô¨5aÂ{Gj˝Ó§O_ń9ňňň”śś\i.węÔ©«r^P6 VJMMŐĉíĂfwŢy§FŽY©c?úč#EFFŞGŹş÷Ţ{µ{÷î*NWüÍ›7×€Ô·o_}ţůçU~­Ę:věÂÂÂt˙ý÷ë‘GŃŞU«ě骨î}Uçý÷ß×}÷ݧ&MšTú™™™=z´ÜÝÝŐµk×*ËvţüyM0Awß}·5_AA"##•žž^e×.VÓćvT?ŚS¨mX €ZçÇTTT”^~ůe{G±YË–-UŻ^˝ ÷á‡jôčŃŠŽŽÖ˘E‹Ô¬Ył*˙–îKŻŃ­[7˝őÖ[UzŤŞđ_˙ő_úî»ď´lŮ2˝÷Ţ{zć™gě骨Î}Ąş:xđ ˝#TĘđáõmŰ6ĺççWúîîîš0a‚\\\Ş0™äěě¬üăÚµk— $I5j”^~ůeíŮł§JŻ”4·×¤gűjd­Ě9kRť] ŚS¨mí¨J;věPxx¸6mÚ$'''{DZق *uÜ´iÓäíí-ąşşę«ŻľŞâd×ćU!))IŽŽŽrrrRýúőuňäI{Gş*Şs_©ŽöîÝ«đđpýôÓOöŽRaÎÎÎjÖ¬™Nś8qĹçňđđĐŃŁG« Ő˙«WŻžš7o^ěYsww׸qăÔ»wo­]»¶Ę!ŕúTŇÜ^“ží«‘µ2ç¬Iuv51N 6á/ Ö0ĆhŔ€zć™gäááaď8×Äd±Xjü5ŞÂ 7Ü Ó§OkÇŽš4i’†jďHŐJMiÇŞ”žž®G}TÇŽłw”ëĘ-·Ü˘ŔŔ@˝ţúëöŽ‚Z ¤ą˝&=ŰW#keÎY“ęěZ`ś@mÁbÔ˙ú׿´qăF=ôĐCÖ˛µk×ęµ×^SË–-uäČ………©I“&jßľ˝ľţúkë~çΝӻᆱgź}VwÜq‡BBB´uëÖr·ęÇÔ«ŻľŞ–-[*##CÝ»w—źźź˛˛˛ĘÍ\XX¨ÄÄD 4H÷Ýwźő>† &___eeeiĐ AjÚ´©Ú·oŻ 6H’–.]Şçź^999:|ř°žţyëϧOźÖ¨QŁôĆohÄęŐ«—FŚQ,Ď?ü __ßbß|iYY×(IjjŞúôéŁČČH…‡‡ëŢ{ďŐożýVlź)S¦hŕŔzá…TŻ^=Y,ë?IJIIŃťwŢ©—^zIo˝ő–śśśJ˝ŢĄÂĂĂ­˙uwwׄ 4~üxőďß_ýű÷WTTT™ÇÓW*×Wƻݞę+77WóćÍÓÓO?­{îąGk×®Őm·Ý&%''k÷îÝzâ‰'Ô¬Y3ÝtÓMÖ{*R^ţţóźÚ¶m›őľmmĂňĚš5KuęÔ±öŰ3gÎhҤIĹĘliIĘÉÉŃřńă5pŕ@EDD¨{÷‹»ěšÇŽłöÉŕŕ`Ą¤¤Ř\%™8q˘ęŐ«§×^{MÉÉÉ6ŐËŮłg5bÄ 6LcÇŽŐčŃŁK}>{őęĄYłf)--ÍćzJRŇÜ^Úł]ÖłPÚXĺëë«Ĺ‹Wj łĺ9/-«-J›K;gYópIÇŘ2–••C˛íó„Tńą±Ľv©L[_:ß2N V0@5”`*úë¬ţýű‹ĹbňňňŚ1ĆożýÖÔŻ_ßH2Ç7?ýô“™?ľiÔ¨‘‘d’““Ť1Ć<÷ÜsfçÎťÖs=řŕĆÓÓÓś>}şĚíÇŹ7kÖ¬1 40’Ě;ďĽcV¬Xaž}öY“ťťmSîýű÷I&((ČcĚÁMÆ Ť$möíŰgćÎťk$™.]ş;öâăŚ1ćĚ™3¦M›6&**ĘZvôčQÓ¦M`˛˛˛Ś1Ć,^ĽŘ4hĐŔ,Y˛Äş_Ie%]Ł´ňÖ­[›ŔŔ@cŚ1yyyĆÍÍÍ[·ôŃGĆÁÁÁś8qÂcĚ;ďĽc$™#FX÷iÓ¦Ťńđđ°ţÜŻ_?sôčŃňŞĐěŰ·ĎüőŻ5 407ß|ł)((°nkѢ…;vl™ÇÓWţT™ľrţüů2﫬ú*,,4ż˙ţ»‘d\]]ÍŇĄKÍöíŰŤ$ăďďoŢ{ď=sęÔ)łiÓ&#ÉtďŢ˝ÂůKężĺµˇ-/Ł..łĄmňňňL÷îÝÍŔMaaˇ1ĆŮłgIÖú 2’̸qăĚŢ˝{ÍŇĄKŤ$s×]wU¨ŠÎcŚ1'Ož44[¶l±ą^ňóóM—.]ĚsĎ=gÝţÇGGÇÇę˘6{çťwl®ScŚ 3aaa:¦¶©ĚüW›]:·©čvôčŃÇŞđđp“””T©1ĚÖ1¸´y´3ţţţfٲeć˝÷Ţ3’Ě'ź|bŚ1fĎž=¦S§N%ŢWIč+•ď+%Ý—-őQR6ooďËî©yóćĹžo[ó_zn[3•çâ—ëK++Żm&Mšd$™]»vY·ççç›Ůłg›ĚĚĚbç,Z,`Ś1Mš41 4¨P=ť'--Í 2Ä;v¬ŘţĺŐË”)SŚ$łcÇŽbÇ=3—ĘČČ0’ĚĂ?\F-^Ž—QY p©ŇćöĘŽiĄŤÁ•Ăl+» ¬9±¤s–7—tŚ-cYyssysĕ̍%•_i[©ě8U[1ţÔLuÔ‡–»»űeĺuęüůk± XËz÷î-IJMMŐúőë,óç—iű÷Č#Ź”»]’,‹$•xýĘ(:ßĹÜÝÝuţüů2ŹKNN–$5jÔ¨Xů˝÷Ţ+IZłfŤµĚÁÁá˛ăK*łŐ«ŻľŞĐĐP}üńÇŠŽŽÖůóç•——gݢÂÂB-]şT’TŻ^=IRŹ=¬űL›6MŤ5RDD„:wî¬ěěěËîĺb‰‰‰zîąç´dÉőęŐK/ľř˘Z´hˇ±cÇ*''GŁGŹÖçźnó}ŃW*ßWJş/[ęŁ$%µą‡‡‡˛˛˛*•˙b•ÍTĺµÍţóI’ŹŹŹu» $77·RĎŐ¬Y3ĺććJŞx=<ňČ#ĘÉÉQÓ¦M‹•—W/Ë—/—$ůűű;®č™ąTQţ#GŽ”¸°UisűĄl}J+;†Uv ¶EEçÄňćá«•ŁĽ9âJćĆ’ĘŻ´­‹0N 6`1j Ř´Ż———$É××W'NśPZZšőĺÚ‹–»˝:)z1wďŢ˝ĹĘ===%I®®®WíÚëׯWűöí 1cƨaÆŶżôŇKúä“O4dČ˝ţúë1b„Ţ~űm˝ýöŰÖ}ž|ňIýúëŻęŐ«—RRRÔ­[7Í™3§ÔkFEEÉËËKÁÁÁ’¤úőë+&&F‡Ň>¨nÝş)((čŠî‹ľRyWł>*›ż:µQŃ ¨©©©•>GEëáý÷ßWBB‚bcc‹•—W/éééÖýlQŇ Ň@eŘ:·_Ť1ÍŢăEEçÄňćák•ăRU]ŹUŐÖŚS¨ X €Ză†n(öÍáe)zˇµgĎž Rnnîe/ÇîرCS¦L)w{uRô­¸Eßľ_äŔ’ţĽß"%˝\iëbŠ’„‡‡+//O=ô¤Ë_đ+((ĐÖ­[µvíZ˝÷Ţ{úć›o4věŘbß>>ćСCÖňsĎ=÷XŹýöŰoMÆ Íwß}gݧ¤˛’®QtIĆßßßZćęęj,‹Yľ|ą™7ožiŢĽą‘dÖ­[g8`Ţ~űmh>ýôSłlŮ2łfÍł{÷îbíŇ A“™™iŚ1&//ϸşşš.]ş”Zw?ü°‘d|đAsŕŔsâÄ óÁW^yĹÜvŰmF’yřá‡ÍÔ©SMdd¤ůěłĎĘh úŠ1•ď+%Ý—-őqöěY#É´mŰÖz\`` ‘TěY.:AAA…ň·jŐʸ¸¸ýű÷ŰśÉO<ń„‘dĆŽkRSSÍ|`<<<Ś$łlŮ2SPPPnۤĄĄ#ÉôčŃĂLť:ŐŚ;Ö 6ĚcŚńňň2’Še»á†¬umk=´lŮŇH2………&??ßôčŃĂ¸ąą™M›6ŮT/żţú«qtt4Mš41Ë–-3ąąąćűďż7Ť76’Ěž={ŠÝç–-[Ś$óÎ;ďŘ\§ĆfÂÂÂ*tLmS™ůŻ6+mnżôٶőY(m ®ěfË|iV[•5'–tÎňćá’Ž±e,++‡-sĕ̍%•_i[©ě8U[1ţÔLQQQQUą¸¨ ۶mÓ—_~©Šü:ËÍÍMłgĎÖý÷߯ŔŔ@kůÔ©Suüřq5nÜXm۶UNNŽV­ZĄiÓ¦©^˝zrttÔăŹ?®´´4-_ľ\+W®”ŹŹŹ¦Nť*ww÷2·;;;+&&F‹-’$?~\ľľľ6Ëlnn®˘ŁŁµzőjť9sF®®®úé§źôĺ—_J’śśśÔˇCMź>]‰‰‰’¤ĽĽ<ąąąéý÷ßWJJŠNť:%‹Ĺ"wwwůřř(<<\™™™š>}ş6oެ•+WĘÍÍMłfÍ’“““¤?żQ˙»ďľSßľ}Ő˛eË˶nÝŞË®‘››«ńăÇkÝşuĘĘĘ’»»»‚‚‚ÔĽys­^˝Z[¶lŃßţö7hÝşuÚżżÂÂÂäčč¨/ľřBóçĎ×Üąső駟꣏>Ň´iÓ¨›nşIcĆŚQRR’˛˛˛4sćLyzzęłĎ>“››[‰ő÷ŔčŔJNNÖ¬YłôóĎ?+44T/ľř˘ÂĂĂućĚýôÓOúţűďőŔčĹ_,ł=č+ď+žžžĄŢWyőuôčQ˝ýöŰúůçź•ťť­»îşKż˙ţ»>ţřc*''G]ştѧź~Ş/ľřB’Ô°aC©qăĆ6ĺ?|ř°věء:覛n*7“­n»í6ýňË/ZĽx±~űí7˝ňĘ+úůçźuß}÷éĆoÔĘ•+•PfŰ<účŁúë_˙Ş´´4mذAk×®U«V­4aÂ9;;kâĉúúëŻ%IgĎžU·nÝg­ëóçĎ+$$DĎ<óL©őťť­‰'ZŹqvvV«V­äęęŞ hÁ‚˛X,ęرŁúőëWj˝´hŃB÷ßż6oެ)S¦hÎś9jѢ…Îś9Łżüĺ/ňňň’źźźő[Ć-Z¤eË–iúôéŞ×˘úéŰ·ŻÍÇÔ6•™˙jłŇćöKźm''§2Ç„ĽĽĽÇŞĆŤWz űřăŹ5ţ|IĄ?ç]»vŐńăÇ‹eµUYsâĄ÷/IŤ7.s>uęÔeÇ”7–µmŰVăĆŤ+5‡-ź'*37–Ő.•mëKçŰĘŽSµă/@Íd1梿5T .Tż~ýTŃ_g=ňČ#jÓ¦Ť>řŕkŮM7ݤť;wVř\¨ZłgĎÖńăÇőúëŻK’ •‘‘ˇ~řAŻ˝öšŽ9bç„ô ŞôîÝ[-Z´ĐĚ™3+t\ź>}$ý˙K©×ŁĘεYIs;pĄ*;NŐVŚż5S{ŞŇěŮłőď˙»ZĽXn±XĘý·k×.{ÇĽ&bcc5xđ` 2ÄZV§Nůřř¨k×®ňöö¶c:űŁŻT´Ĺ•[·nťvďŢ­‰'Ú; j‰ę4·WĆűcś@mÁbÔ*Í›7×W_}ĄW_}Uąąą’¤śśśb˙˝VŚ1ĺţk۶í5Íd/«WŻ–$Mź>]'Nś°–oܸQ‘‘‘š;w®˝˘C_mqe:¤ččh­X±BŤ5˛wÔ%Íí5ăŚ}1N 6a1jťŕŕ`EGGëÝwßŐ›oľ©H’^~ůe­]»ÖÎé®OsćĚŃđáĂőé§źĘÇÇG÷ÜsŹúöí«Ť7jîÜąj×®ť]óĺääĐW€+”źźŻřřxÍ›7O>>>öŽZ¦hnź:uŞ˝Ł cś@mc1Ć{‡.µpáBőë×Oü: ®}úô‘$%&&Ú9‰ý0˙°Ć_€š‰ż @ ĂbjPð€†ĹÔ0Žö”Ĺb±Ř;ŕ łw„jůŔµĆř PóXŚ1ĆŢ!€K}úČŃŃŃŢ1@ ĺç秸¸8íÝ»W ŇŰoż-EEEéÔ©SöŽ\3,pĹ µbĹ őíŰW-Z´Đ믿®.]şhŐŞUÚľ}»FŤĄfÍšŮ;&¨E<==Ł˝{÷ę…^ĐäÉ“uăŤ7*22R'Ož´w<ŕŞc1€J;pŕ€bcc¨ĄĄĄéŁŹ>RFF†f̡®]»Ú;"¨ĺš5k¦¨¨(íßż_ŁGŹÖ¬Yłäçç§:tČŢń€«†Ĺ*äÜąsJLLTHHüüü§~ýú)55U))):t¨\\\ě\g7n¬QŁFißľ}?~ĽŐşukEDD(==ÝŢń€*Çb6ٶm›"##ĺăăŁţýűK’´˙~ĹÄĨU«VvN 5lŘPJMMUtt´ľúę+(<<\ż˙ţ»˝ăžFç IDATU†ĹJ•••Ą™3gę¶ŰnSpp°/^¬×_]éééJJJRź>}äččhď—qqqQDD„ŇŇŇ4kÖ,ýüóĎj×®ťÂĂõk×.{Ç®‹SXX¨+V(<<\ŢŢŢzíµ×¬¤¤$mßľ]ŁFŤ’§§§˝cؤnÝş ×öíŰőÉ'źč—_~Q»víŞM›6Ů;Pi, IJOOWll¬ZµjĄmßľ]|đ222Żž={Ęb±Ř;&@Ą899Y|óÍ7ĘČČĐí·ß®ĐĐP­_żŢŢń€ c1p;ţĽ*???Mž}úČÉÉÉŢ1®ąž={jÍš5Zµj•ÜÝÝŐ»woÝsĎ=Z˛d‰Ś1öŽ”Ĺ@-fŚŃęŐ«5lŘ0y{{+""B^^^JJJŇÎť;ĄoĽŃŢ1Ş…˘ż śś,=öŘcęرŁâăăUXXhďx@1,jˇŚŚ ĹĆĆŞuëÖęÖ­›6lŘ ččhĄ§§káÂ…ęŮł§,‹˝cTKwß}·–,Y˘M›6é–[nŃŕÁuë­·*>>^ůůůöŽHb1Pk\¸pAK–,Qßľ}ĺçç§ŘŘX=đŔúő×_•’’˘yxxŘ;&@ŤQ´`óćÍęŘ±Ł† ˘¶mŰjćĚ™ĘËËłw<\çX Ôp;věPdd¤|}}őřăŹ+33SóçĎ×áÇ5cĆ Ýzë­öŽPŁÝ|óÍŠŹŹ×®]»ÔłgO˝ôŇKjÝşµâââtîÜ9{ÇŔuŠĹ@ túôiĹÇÇ+$$DíÚµÓüůóőĚ3ĎčŹ?ţPRR’úô飺uëÚ;&@­ 3f(55UŹ=öŢxă ůűű+66VąąąöŽ€ë ‹€dÆ 6lĽ˝˝5lŘ0ą»»+))IűöíSLLŚüýýí ÖóóóS\\śöěŮŁAéí·ß–żżż˘˘˘”••eďx¸N°¨ć:¤¸¸8µoß^ť:uŇęŐ«5fĚů䥧§kĆŚęŘ±Ł˝# Ť7Ö¨QŁ´oß>Ť?^‰‰‰jÝşµ"""tđŕA{Ç@-ĂbŔÎΞ=«ÄÄD…„„覛nŇÜąs5hĐ ýţűďJJJRxx¸4h`ď°QÆ ˇ={öhҤIúú믨đđpýţűďöŽ€Z‚Ĺ€ťlذAÆ “§§§(www-^ĽXűöíSLLŚZ¶liď¸ÎÎÎ:t¨ţřăÍš5K?˙üłÚµk§đđpíÚµËŢńPñ¸†Nž<©™3gęÖ[oU§Nť´zőj˝ůć›:xđ .\¨ĐĐP988Ř;&ŞPÝşu®íŰ·ë“O>ŃúőëŐ®];…††jăĆŤöŽ€ŠĹŔUVXX¨+V¨oßľjѢ…FŽ©;ďĽS«V­Ň¶mŰ4jÔ(5mÚÔŢ1p•999)<<\۶mÓ7ß|ŁŚŚ uęÔIˇˇˇZż~˝˝ă †a1p•¤¦¦***JzđÁ•‘‘ˇ)S¦(==]3fĚP×®]ívP§N…††*%%E‹/ÖŃŁGŐąsguíÚU?üđ˝ă †`1P…Îť;§ÄÄD…„„¨m۶š5k–žzę)Ą¦¦jőęŐ:t¨\\\ěŐ€ĹbQhh¨Ö­[§U«VÉŮŮY=zôP×®]µdÉ{Ç@5Çb  lذAňööÖ€TŻ^=%$$hßľ}Љ‰Q`` ˝# ëÚµ«V®\©U«VÉÝÝ]˝{÷Ö=÷ÜŁ%K–Čcďx¨†X TRff¦fÎś©Ž;ŞS§NZľ|ąFŽ©hÉ’%ęÓ§ŹË<ÇĽyóäââ"‹Ĺ˘ŘŘXH’ćĎź/gggÍ™3GŇźqŕÝwßŐłĎ>«;î¸C!!!Úşu«ő<)))şóÎ;őŇK/é­·Ţ’“““rrr®ŢÍăŞKIIѰaĂäë뫬¬, 4HM›6UűöíµaĂë~§OźÖ¨QŁôĆohÄęŐ«—Fڎ¬¬,;¦Ř óGÍWôW’““ĺááˇÇ{L;vT||ĽőłbeńŮ v±–Ť6+,,Ô÷߯řřx}ůĺ—rrrŇcŹ=¦đđpőěŮłRç;v¬ĆŹŻm۶©]»v’¤(""B_ýµ$ičС1b„Ú¶m+IęŐ«—6oެÔÔT5jÔHm۶ŐńăÇuâÄ IŇSO=ĄŹ>úHÍš5«‚»Ćµf±X4mÚ4˝ţúëĘÎÎVtt´  U«ViŔ€ęŇĄ‹Ö®]«ěělÝ~űízúé§5nÜ8IұcÇÔµkWĺççkăĆŤruuµóÝ®ćŹÚiË–-z˙ý÷5ţ|iäČ‘zúé§Ë]tZ>{ÔüeŔTll¬˘íŰ·kňäÉJOOW|||ĄHŇ«ŻľŞFŤiňäÉÖ˛yóćiČ!’¤_~ůEłfÍRPP,‹,‹–/_®#GŽč§ź~’ôç_)8yň¤>üđCc4věXŐ«WďĘnvĺáá!oooIŇčŃŁuăŤ7ęoű›<==ő믿J’bbb´{÷n 6Ěz\łfÍ4fĚĄĄĄi„ vɰćŹÚç–[nQ||Ľ6oެŰn»MC† Q›6m§óçĎWř||ö¨=X ”âüůóJLLTHHnĽńFĹĹĹ©wďŢúí·ß”’’˘ˇC‡ŞaÆW| >\sćĚQFF†$iĺĘ•z衇$IëׯWpp°Ś1—ý{ä‘G$IÓ¦MSŁFŤˇÎť;+;;[Ť5şâl°/‹ĹrY™»»»őĹżäädIş¬­ď˝÷^IŇš5k®rB@uÄüQ;Ý|óÍŠŹŹ×îÝ»ŞQŁF©mŰ¶Š‹‹ÓŮłgm>ź=j—ضm›"##ĺăăŁţýűK’´oß>ĹĹĹ)88¸ĘŻůŹüCuëÖŐäÉ“µaĂuîÜY’¤'N(--Mąąą—WXX(IzňÉ'ő믿ŞWŻ^JIIQ·nÝ4gÎś*ωęĄNť?ĺ»wďŢbĺžžž’$WW×k P0Ôl-[¶T\\śvíÚĄÇ{LoĽń†üýý[âçĹ’đŮ v`1 éÔ©Sš9s¦nżývë›oľŃkŻ˝¦ôôt%%%©Oź>rrrşj×oҤ‰žţyMź>]~řˇlݤÜÜ\ĹĆĆ;fÇŽš2eŠ$iܸq вeË´`ÁĺĺĺiĚ1W-/އ˘op^şti±ňH’zöěyÍ3Ş?ćŹÚÁĎĎOqqqÚ»wŻžyć˝ýöŰňóóSTT”˛˛˛Ę<–ĎžµĹcě°‡ÂÂB­YłFźţąćÎť+cŚ}ôQ :T<đ€,Ë5ÍsäČůůůé®»îŇ?ü`-?ţĽÚµk§´´4 űě3ąąą]óűŔ•űď˙ţoąşşZ_ĚsrrR‡4}út%&&J’ňňňÔ˝{w }ş6oެ•+WĘÍÍMłfÍşŞÍPý0\ß4h îÝ»ë…^PýúőőŃGiâĉ:rä:tč FŤ۟Ϟ5×…óçĎkůňĺúüóϵhŃ"5nÜXaaazńĹ‹˝üdOąąąşőÖ[µeËŐŻ_ßŢq`GEßěÜ·o_{GÔ Ě¸Xvv¶>ýôSĹĆĆęäÉ“úűß˙®±cÇĘÇÇGź=j:ö\M;věPdd¤|}}őřăŹ+33SóçĎ×áÇ5cĆŚjł@’¦NťŞáÇó2®XÆ ˇ={öčĂ?Ôż˙ýo*<<\ż˙ţ;ź=jG{ŞÚéÓ§őĹ_(>>^ÉÉÉjÝşµ^xá=óĚ3ňóółwĽbÖ­[§ˇC‡*77WÚąs§˝# qvvÖСC5hĐ EGG+66VsçΕ‹‹‹Ö¬Ycďx¸üeÔ6lаaĂäĺĺĄyyy)))I»víRTTTµ[ I...:}ú´ęÔ©Łůóç«nÝşöŽ€Z¨nÝşęÓ§ŹZ´h!OOO5iŇD:tPhh¨6nÜhďx¨ FËČČPll¬Z·n­Nť:iÆ ŠŽŽVzzş.\¨ž={Ęb±Ř;f©‚µgĎíÚµKwŢy§˝ă  ÖŢ˝{učĐ!ĄĄĄé›oľQFF†:uę¤ĐĐPýňË/öŽ€ `1jś‚‚­X±B}űö•źźźbbbÔŁGmÚ´I)))Ї‡‡˝cŐVť:uŞ””-^ĽXGŹU—.]ÔµkW}˙ý÷öް5ĆÎť;)///őęŐK™™™úôÓO•‘‘ˇ3f¨C‡öŽÔ(‹EˇˇˇZ·nťV­Z%ggg=đŔęÚµ«–,Ybďx(‹P­ť9sFńńń Q»ví4oŢ<=óěÝy\”U˙˙ń÷ ¨¸ˇY**ŢTš­.ĺľdw%m -BŢjš¦_5ÜJMoË…l#͵nsĎě.łúfâRjf©eĺîWoWTCdťóűŁsÎŔřz><ÎąÎuÎç:ç\ç xť™üCGŽQLLŚ"""äëëëî0€2Żm۶ڰa¶l٢5j($$D÷ßżľüňKcÜ®Âf”J»víŇŔU·n] 0@5jÔĐşuëtâÄ Mź>]Ť5rw@ą”ý­Ű¶mSÍš5ő裏ęŽ;îĐâĹ‹•••ĺîđđ˙±ĄĆŮłg­Űoż]÷ÜsʶnÝŞńăÇëÔ©Súä“OÔµkWY,w‡ \Ú´iŁ/żüR»wďV‹-Ô·o_µhŃB‹/Vff¦»Ă¸î±n•••Ąőë׫gĎžjĐ &Nś¨6mÚh×®]Ú»wŻĆŚŁZµją;Lŕşuűí·kńâĹúí·ßt×]w©_ż~jÚ´©˘ŁŁ•––ćîđ®[l€[:tH“&MRPPşwď®ŘŘXÍš5K±±±š7ožîşë.w‡ ‡Űn»M‹/ÖˇC‡ÔŁGŤ3FűŰß­+W®¸;<€ë›PbRSSµjŐ*uëÖMÁÁÁúŕôôÓOëđáĂÚşu«  J•*ą;Ly Ttt´:¤G}T/żü˛5j¤¨¨(Ą¤¤¸;<€ë›PěvíÚĄę†nPxx¸|||´rĺJ;vLÓ§OWPP»CP@ 4Ptt´Ž;¦AięÔ©jذˇ&M𤄄w‡Pî±ĹâĎ?˙ÔüůóuÇwčž{îŃ–-[4nÜ8ťÓŮłgŐŞU+=ôĐCš1c†<(«Ő*IĘČȰm¸|ů˛›#(Ř ĄĄĄiŐŞUęŃŁ‡5j¤wß}W=zôĐďż˙®ť;wjŔ€ŞRĄŠ»ĂPY,=öŘcÚ±c‡¶l٢´´4Íž=[žžžąň±! 76\Çöíۧ±cÇŞ~ýúzúé§•ššŞ+VčĉŠŽŽVóćÍÝ"€ëH۶m5bÄ?~\YYY×ĎČČĐŻżţʆ±ŕşséŇ%Íź?_m۶Őm·Ý¦Ď?˙\/ľř˘Ž=Ş………©B… îŔuęő×_————ĂăŮşuëƆp]süŽ ĺ†ŐjŐ¶mŰ´dÉ-]şTV«U=zôPLLŚşté"‹Ĺâî@111úé§źňÍ—‘‘ˇ_~ůEÝşuSLLŚüüüJ :€Ň…o(ÇbccĄ¦M›Ş]»vÚµk—ŢyçĹĹĹé“O>Q×®]Ů Ôxýőםţ%##C»víŇß˙ţw%''sdĄß PΤ§§ëŰożŐ’%KôůçźËĎĎOaaaú÷ż˙­-Z¸;<°ëňĺËjßľ˝5j¤cÇŽéřń㊍ŤUFF†-OĹŠe±X”‘‘!«ŐŞĚĚLýđĂęŢ˝»Ö­[§J•*ąń J›ʉýű÷kѢEZ¸pˇ.\¸ Îť;kůňĺzôŃGU±bEw‡ řFŻż¤§§ŰM˙á‡TąrĺŽ /44T«V­rwĘ86”a‰‰‰Z˝zµ–,Y˘őë×+ @˙řÇ?4hĐ 5lŘĐÝápˇáÇ«M›6îŁÔÉĚĚÔĹ‹ŻŞU«Ş^˝zî ňôÎ;ď¸;ĺ›Ę ]»viţüůZľ|ą233ŐŁGĹÄĨK—.|b(PNµiÓF={ötw€"⸠›Ę3gÎhńâĹúđĂuřđaÝzë­?~Ľú÷ď/w‡(Al(Ų˛˛´iÓ&Íź?_«WŻVĺʕճgO­\ąRwŢy§»Ă¸ ›JˇjáÂ…ú裏tţüyuîÜY|đÂÂÂäëëëîđnĆf€RâĘ•+úꫯ4ţ|mذAőęŐSź>}4pŕ@ş;<@)Âf7۵k—ćĎźŻ+V(==]!!!úâ‹/ôĐCÉÓÓÓÝáJ!wp=şxń˘ćĎźŻ-Zčž{îŃÖ­[5nÜ8ť:uJź|ň‰zôčÁFĄ^bb˘»C@)Âx(šK—.ą;»ĘJż–•8Ŕ•Ř PB¬V«ÖŻ_Żž={ęĆoÔčŃŁŐşukmٲE{÷îŐ1cT«V-w‡ ŕ:µiÓ&Y,U«VM-Z´PëÖ­e±Xäëë«Ö­[«yóćňőő•ĹbѨQŁÔˇCůűű»;ě2­uëÖ=zt™ŻďÍ7ßd<RZZš¦NťŞűT´_öÉĘĘRTT”ÚµkW*âĘ ăŔőŚÍĹěđáĂš4i’Ő˝{wĹĆĆjÖ¬Y:}ú´ćÍ›§¶mŰş;DĐ•+WÔ©S'ť9sFżýö›¶oß.IjÔ¨‘¶oß®?ţřC§OźV“&MÔŻ_?íÝ»W™™™nŽşô9uę”ÓyĺăăSŚŃ_}9ŻsčСĹ: ҦîVĐX˝˝˝őŇK/éŕÁĘĘĘ*¦¨ś—=F<==5lŘ0íŰ·ŻÔßç®ei¬€$yą;€ň(55U_~ůĄćĎźŻ 6覛nRxx¸žţy5nÜŘÝáŔ5®\ą˘ŃŁG«RĄJóÔ¬YS ’ŐjUíÚµ_‚–~ÇŽSDD„6oŢěTţ+VsDĹSßŐ×éíí]l㡠męN…ŤŐÇÇG7Üp.^ĽXL‘9/ç)MqĺĹUăŻ,Ť5ČĆfÚµk—/^¬ĄK—*))I<đ€V®\©Ç\^^ü× €Ň롇RĹŠóÍ7xđ`yxx”@DeËéÓ§őČ#Ź”ŠOw/N%yťe©MËR¬¸ý ¬â@ýůçźš?ľîĽóNÝsĎ=Љ‰ŃčŃŁuňäI}ůĺ— c#€RĎ××WžžžůćóööV… lŻĎź?ŻĐĐPůűű«YłfÚąs§íXjjŞŢxă őďß_÷Ţ{Żşuë¦={öäYľŁs¬V«ľ˙ţ{Ť1BŠŤŤUÇŽ /ľřâšô† *!!A‰‰‰3fŚ^~ůeEFFŞ{÷ŚTBB‚Ă2łĎµgçÎťjÝşµ† ˘W_}U*TPrr˛>úč#íÝ»WgĎžµ}{‚ٞ/^Ľ¨U«V©Oź>ęС$iÍš58p ” >}ú¨V­ZjŢĽąvíÚ•+†Yłf)<<\–ŹŹŹ,‹íÇ«ŐZ¤úrşú:ŻćĘńਮÂôi~ă$ŻŘśi«üÚ%§+W®(22RBgůG IDATÔ„ ôĘ+Ż(999WžüÚĘŃ8”¤ääd˝ţúë ×°aĂÔ±cGEGGxLćô˙÷ QÍš5Ő˛eK}÷Ýw.‰őj›6m’···üüü´eË]ştIááá˛X,ęÔ©“öîÝ+Iúő×_U·n]Íź??×ůyŤżĂ‡+,,LcÇŽUDD„Ú·oŻ?ţřŁŔýĄŠ@eee™n|}}MŐŞUMxx¸‰‰‰qwhĘIfĺĘ•îPư~”oîę_I&88Řî±ŕŕ`#ÉLś8Ń;vĚ|ýő×F’iÓ¦Ť-ĎóĎ?o8`{ýŔ:uęÄÄD‡u::çÂ… f۶m¦RĄJF’™6mšYż~˝‰0111פ÷ďßßś={Ö4mÚÔLš4ÉV^\\śiÚ´© 2qqqvËěßżżIJJ˛_Ó¦MMÍš5mŻ{őęeâââ®iŻ´´´<Ë>qâD®ü§Nť2UŞT1’Ě”)SĚńăÇÍŇĄKŤ$ÓŞU+[}3gÎ4žžž&>>ŢcĚ´iÓŚ$é°MŤ1…®Ď{㢸ĆĂŐu]ľ|ąP}š×8IJJĘ36gŰ*Żű%[ff¦iŐŞ•yţůçmiGŽ1^^^&ç#ťůµ•Łq‘‘a:věhÂĂĂŤŐj5ĆłpáB#Éüűß˙.Đ4ćżý:|řpcćÍ›g*W®l<==Íďż˙^¤XgĘ1¦xĆ˝şŠÚ§öŇ 2îňj+g&ź5k–‘döďßź+˝iÓ¦¶ňť‰ÇŃ8|űí·Ť$sđŕA[Ů™™™fáÂ…ćĎ?˙Ěłmě]Cvżćܰm$™çž{®H±:˛oß>#ÉĚž=Ű–bŞT©bűűËš5kĚĽyó®‰ÓŃř3Ć·ß~۬X±ÂcŚŐj5Ť76*TpxíʼnÍ\ĹĂΗ ‡´´4­ZµJÝşuS ­^˝zéСCÚąs§  *U޸;LeÔ /Ľ Ž;ćúńóóÓ”)SrĄuéŇE§Oźvw¸€R‚őĄ‰Ĺb±ý»víÚJII‘$íرCÍš5“ůëCksý<üđĂvËrćśěújÔ¨a7Žśé?üđ$ÉĎĎ/WŢöíŰK’¶mŰ–g™öĚ™3G~~~6lZ¶l©¤¤¤kĘĎ/®üňćTŁF ĄĄĄŮ^wëÖMV«U_ýµ$ÉÇÇG’Ôąsç|Ë/L}E)ł¨ăÁž˘ö©˝ô‚Ś»ś ÓVëÖ­“$5jÔ(Wş‡Ççt&Găđ»ďľ“$ŐŻ_ßVž§§§úôéŁęŐ«çŮ6yÉŮŢŹ=ö$ißľ}EŠŐ‘[nąEť;wÖĽyó$IÇŹWVV–ŇÓÓµbĹ IŇâŋջwďkÎu4ţ$iÄęŃŁ‡fĎž­)S¦(--MN·”F^î ´Ú»wŻ–,Y˘?üPţů§:uꤕ+Wę±ÇS… Ü€râ†n°=ä’Óďż˙žëuPPęŐ«WRaJ9Ö”ńńń:zô¨RRRT©RĄ\ǬVk®‡ź‹rN^˛ó;vL·Ýv›-˝Nť:’¤jŐŞ¨¬-Z¸,ۺAEŠ5Ż{fČ!zâ‰'´cÇ-\¸PoĽń†^{í5-X°@÷ßż5jtM}ůٱc‡zőęĄŮłgkđŕÁZ¶lYÁ/Jľ ‡„„Íź?_wß}·š5k¦Ő«WkäČ‘:}ú´bbbĆF.őĚ3Ďä›§B… ęÓ§Oń(3X?P+%%EQQQąŇ÷ď߯YłfąěśĽdZ|ö§čg;yň¤$©k×®.sâĉ ŇÚµkµbĹ eddhüřń’ţúTňĚĚĚ—YYYYÚłgʶoß®3fhőęŐš0a‚<==‹µŢś sť…íŰ«ë*Ž>uŐ¸s¦]‚%]Aăq4ł7L™2EĆ۹Ǐ×7ß|ăôµä%»­yä‘"Ĺš—hҤIJNNÖ­·ŢŞ^xA;věĐŕÁ5hĐ ÇˇŚŚ =řŕ’ţÚ¬SIÜżŕj“s¶¸Y­Vm۶MK–,ŃŇĄKeŚŃ#Ź<˘¨K—.ąľbŠCóć͵wď^ĺőß·‡R“&MJ0*@iÇúq}±X,ZąrĄzöěYbu&''«J•*jĐ Ž?~Íńzőę)66V‰‰‰ňóó“$Ő­[WgÎśQRR’ĽĽĽtë­·ęčŃŁęŰ·Żşté˘ýű÷ëçźÖ§ź~j;'§´´´|Ď Ô±cÇ”””¤Ę•+Űε—~ĺʵlŮR Ú±c‡nĽńFIŇđáõsçN}÷ÝwňňňrX¦=•+WÖéÓ§U˝zueffŞV­Z ÖöíŰŐ¤Iť9sFű÷ďW@@€Ă¸$)))I~~~Ş[·®íÓâłó漯ëׯŻÓ§O+##C^^^zíµ×´hŃ"˝ňĘ+ŞWŻžŞV­ŞZµj)(((Ď …­Ď{×YăÁ^]EíS{éwyµ•˝vąÚożý¦{îąGŐŞUÓ˛eËÔľ}{mßľ]Ź=öőź˙üG7ÝtSľń8‡+V¬PóćÍ•śś¬Îť;ëÉ'źÔŮłg§9sćČb±hLŢzë­Úżż.^Ľ¨5jH’^|ńEť>}Z«WŻvŞíňşgň2eĘM0Aüń‡í[ nąĺ5iŇDk֬ɕ7żńWąreUŻ^]‰‰‰úöŰouţüyŤ1Bqqqúé§źT·n]uęÔ)ßţs•°°0IŇŞU«Šµĺźç¤I“&ą;€˘úóĎ?5ţ|µnÝÚésNź>­9sć¨Oź>zűí·%IăĆŤÓ˘E‹Ô»wo±@‰HNNÖ¦M›®ůdJéŻ˙î¸ă˝ňĘ+n Pš±~\_ţůĎ*,,Ěö@lq[·nťf̡ݻwëŇĄK:wîśüüüÔ¨Q#côÖ[oéłĎ>“ô×÷íÚµStt´>˙üsI=\ݵkW=ůä“:zô¨Ö­[§ 6¨~ýúz˙ý÷m_ÍËËKŹ=öÝsĽ˝˝5}út[.\P@@€ŞV­j7ýĆoT… ˇ?˙üSsçÎŐożý¦ 6¨zőęZ°`222žëČřńăcűfą:uęč_˙ú—ŞWŻ®łgĎj˙ţýşăŽ;Ô°aC‡e§¤¤hĘ”)Úşu«._ľ¬jŐŞióćÍúôÓO%ýőÍwÜq‡ćÎťk{X8##Cm۶•1Fü±–/_®ĄK—ęĂ?ÔĚ™35gÎ5nÜX·ÜrË51Ą>{r^gppp±Ť‡«ëşĺ–[ ݧyŤ“ĽĆ]Ť54{öl-_ľ<ß¶şpáB®XíąńĆŐ©S'ýöŰoš5k–-Z¤oĽQ—/_Öß˙ţwŐ­[WŤ7ÖăŹ?žg[9‡ ŃŃŁGµk×.mßľ]7ß|ł¦Nť*«ŐZ 1٬Y3Ý~űíJHHĐ’%KôăŹ?jýúőşĺ–[4cĆ Y,–|Ű.ż{&/·Ýv›233Ő»wo[Z•*UôÄO¨~ýú’äô|Ô±cGŐ¨QC[·nŐďż˙®gź}VAAAúé§źtâÄ …††ęŇĄKůöź«dŹť’Üŕ |â›@™wäČuďŢ]'NśPll¬jŐŞĺ0oZZšÖ¬YŁĹ‹ë›oľQµjŐŞ_|Q·ß~{ F ˙uňäI5lŘĐî';{yyiĆŚ>|¸"”f¬×w|3J§… ęÂ… 5j”¤żľí.66V›6mŇČ‘#uîÜ97G ?|3WńpwE±uëVÝsĎ=:qℌ1Zşt©Ý|űöíÓرc §žzJ©©©Z±b…Ξ=«yóć±€[¨uëÖňđ¸öżpł˛˛lŠëpý‰ŠŠRßľ}ŐŻ_?[š‡‡‡ę×ŻŻ¶mŰŞ^˝znŚ”46€2ë“O>Q—.]tůňeedd(++Ksçεżté’ćĎźŻ¶mŰę¶ŰnÓgź}¦ÁëčŃŁŠ‰‰QXX*T¨ŕĆ+€˙ —ĹbÉ•ćááÁC]€<±~×—­[·J’ćÎť«řřx[ú/żü˘±cÇ:Ü Ę'6€2Çٍ¨(=őÔS¶MŮéÔ‚ ®›nşI#FŚĐÍ7߬͛7ëŕÁš4i’6lčć+€kőęŐ뚇9-‹"""Ü ,`ý®/‹-ŇСCőᇪ~ýúş˙ţűŐłgOýňË/Zşt©n˝őVw‡JĹcÜ€łŇŇŇÔ·o_}üńDzZ­×ŻX±˘5j$???…‡‡+<<\5kÖtC¤Pp>ř 6lŘ ĚĚLI’———Îť;Ç<ČëÇőÁb±hĺĘ•ęŮł§»CQXX$iŐŞUnŽ@YçĺýôÓOv7HRzzşNť:ĄóçĎ«RĄJ%!MďŢ˝#éŻ9|đAää‹ő¸>y¸;g>|X÷Ţ{Żvěء¬¬¬<󦦦ęÓO?-ˇČŔuüqU¬XQ’”••ĄŢ˝{»9"@YŔú\źŘ J˝őë×ë®»îŇ©S§”‘‘áÔ9óćÍ+ć¨Ŕő*W®¬=zH’|||l˙ /¬ *11ŃÝ!\7hëâ§U«VięÔ©y¦@yÇfPŞÍź?_>ř ’““ťŢ`µZőăŹ?ęđáĂŸ޳Ď>+éŻOy®T©’›Ł”¬@ůŇşukŤ=Úĺĺľůć›ęСüýý]^öő`Ó¦M˛X,ŞV­šZ´hˇÖ­[Ëb±Č××W­[·VóćÍĺëë+‹Ĺ˘QŁF9ÝÖ®čďâ3®âĘř8 É“'«gĎžZ˛d‰Ă4¸xą;Čf±XÜ€rÄ٦M›ş; ¸Phh¨V­ZUl峡´Yľ|ą–/_îî0IŇĘ•+ŐłgOw‡2€őÔýX?Ü‹ů˛xś:uJőë×ww%*00P>>>.)+gű :TÓ§OWff¦KĘΫ®Ň®0±^ąrEť:uŇW_}eŰxe±XÔ¨Q#mßľ]’tńâEµnÝZýúőÓÂ… ťjkWô·+Ę(ÎţsĺÖ[o˝Ą÷ß?Ď4¸°@©2|řpµiÓĆÝa(®\ą˘ .¨bĹŠňňň’ŹŹŹ<<<äëëëîĐŕďĽóN‰ÔĂ:„ŇbٲeęŐ«—ĽĽř/]¸_Ż^˝ÜĘÖS÷aýp/ćËâqěŘ1EDDhóćÍîĄD­X±Â%ĺ\Ý~ŢŢŢŞ]»¶âăă]R~^u•f…ŤőĘ•+=ztžßŔRłfM 4HV«Őé¶vEµŚâî?WŤélŢŢŢNĄ@yÇ;ĄJ›6mř=Ŕ5ŠórbBiń裏ň J nEA±žşë‡{1_şŢéÓ§őČ#Ź(++Ëݡ”I%Ů~e©ŻŠëC=¤Š+ć›ođŕÁňđđ(LxnQ–ú[ŮYmŕ:Áś€Â`ý@qŮ´i“Ľ˝˝ĺçç§-[¶čŇĄK —ĹbQ§Nť´wď^IŇŻżţŞşuëjţüů’¤ÔÔT˝ńĆęßżżî˝÷^uëÖM{öě‘$­YłFT@@€Ô§OŐŞUKÍ›7×®]»$I}ô‘öîÝ«łgĎjĐ A.‹oîÜąúţűď5bÄ*66V;vTÆ • ÄÄDŤ3F/żü˛"##Ő˝{wEFF*!!Áéس͚5Kááá>ŢcĚ´iÓŚ$™çµś8q"×u;[ź=öÚ/88ŘH2'N4ÇŽ3_ýµ‘dÚ´icË“Wß9[×ĺË—ól˸¸8»ýabbběöORRR‰Ž+gäU–3m}ucLÓ¦MMÍš5mŻ{őęeâââĆŕŠ2ě]ÇřńăŤ$łwďŢ\u=ţřă&++Ë|őŐWĆ×××H2C‡5›7o6Ë—/7~~~F’ůá‡ěĆ—‘‘a:věhÂĂĂŤŐj5ĆłpáB#É|ůĺ—Ćcš4ib7nlË_˝zuÓ¬Ył|cľ:-Żkp§’ř;€ëß ČÓ!C”ššŞeË–I’|||ÔŞU+­^˝ZIII’¤˙ýß˙ŐłĎ>+Iúůçźµ`ÁŰ> Ýşu:wîś6oެzőę©^˝z’¤W^yE 4ĐłĎ>«:uęh÷îÝĹ_ĹŠŐ¦MH’¨.]şhÁ‚š9s¦:¤ÚĘ®]»¶ĆŹŻŁGŹjęÔ©NÇ#cŚüüü$I=zô$ýđĂy^Kv\Ů\ÝVŮ&Nś¨† ꡇ’żżż~űí7Iů÷ťł¦Oźžg[ľůć›vűaѢEęÚµ«ÝţŮ»wo‰Ž+WqÔÖҵý-Iţů§.^Ľ¨÷Ţ{OĆM0A>>>ËwEöŚ1B~~~z÷ÝwmiË–-Sż~ýäáᡇ~ŘV÷ôéÓŐ®];=ýôÓzíµ×$I3gδßĚ™3őÝwßiüřń¶oÉ×Â… Ő¶m[IŇ AôúëŻK’<==ĺďďŻ(ţü®Ę6ňtË-·¨sçΚ7ož$éřńăĘĘĘRzzşV¬X!IZĽx±z÷î-IÚ±c‡š5k&cĚ5??ü°$ŮΩFŤJKK+öřrÖ_ŁF [ZöúŮđgkßľ˝$i۶mNÇŢ­[7Y­V}ýő×’d{»sçÎľ>W¶•˝2k×®­””IÎőť3 Ú–9űÁQzIŹ+WqÔÖŽĚ™3G~~~6lZ¶l©¤¤¤kÚ1?®(ŁfÍš:t¨-Z¤ŘŘXI҆ ôŕÚňxxüőjĄJ•li!!!’¤Ă‡Ű-÷»ďľ“$ŐŻ_ß–ćéé©>}ú¨zőę’ţzżGŹš={¶¦L™˘´´4edd(~gŻĘ26ň5dČýöŰoÚ±c‡˘˘˘ôĆoč‰'žĐ‚ ´oß>5jÔČö@p||ĽŽ=j÷ˇg«ŐęöřÉ~°ůرcąŇëÔ©#IŞV­ZâůŕÔŻ_?Ť5J‘‘‘š>^’ă>nѢ…$iĘ”)2ĆŘŇŹ?®oľůF’ˇŚŚ Ű'řełGa®Ę 6(“ÖŻ_݇zH‹E‹Eť;wVçÎťuď˝÷ęŃGŐ‡~¨ôôtw‡éoľů¦:tč w‡‚”––¦©S§ęľűď[·n­ŃŁG»)2 `233őöŰo+22RĎ<óŚÚ·oŻO?ýÔÝa]¬V«î»ď>ĄĄĄą;”s=ĎŹy­ö\ĎmUÜ÷×Řşu«^~ůeŰď|Ď=÷śÖ¬Yă’˛ů«|s´&2?»†§§§¨oľůĆö^˘C‡ VŐŞUdËű裏*((H“'OVż~ý´|ůrM0AÇ×?ţńIRjję5u\ľ|Y’l%7nÜXgÎś±=@îŞřrÖźśślK=z´š5k¦™3gęěŮł¶ô÷ß_÷ßż† âtěS§NŐ—_~©-[¶čŰożŐŹ?ţ¨Ă‡çűŔyRR’¤Ü›–ś©Ď{í—]nöůŇ7Ď$'';ŐwÎÔUжĚŮŽŇ‹s\Mť:Uµk×ÖńăÇ^ăŐ˛cł÷MRţm-Ůďď7ß|S ’¤ĐĐPU«VMőęŐs‡+ĘČë^‹ŚŚTzzşNś8ˇĆŤŰ=?ç¸^ż~˝îľűn 8Đn|cÇŽUĺĘ•µjŐ*uíÚUłgĎÖ«ŻľŞiÓ¦Ůţ?sćŚNź>­-_ľÜv-?˙üłNť:Ą+W®HĘÝßöŇ r P±@™ÔµkW}đÁ’¤ŔŔ@mܸQ7nÔĎ?˙¬çź^Ó¦MSłfÍ´oß>7GZtC‡ŐŢ˝{ ü)n(S§Nkţ‚ňööÖK/˝¤^ó@`` |||еţňÂýTÜ}]ŢMž¨űî»OM›6ŐM7ݤĎ>űĚnü)))š:uŞ$)66VďĽóŽ˘˘˘śŞĎžśígŚŃ›oľ©ŘŘXIŇ«ŻľŞäädÍ1CgÎś‘ô×§É[,–<űΑ«ű*ż¶LOO·ŰyőOqŽ«J•*©jŐŞňňňrxŤ9­[·NC‡•$ť8qB Ň÷ß/IN·uBBÂ5ý}ůňeĄ¤¤¨K—.ŠŠŠRź>}Ô®];}üńÇvă°7f Z†˝ţË©Nť:ęÖ­›úőëçđüwß}Wńńń:ţĽÎś9Łďż˙^^^^vă«U«–¶oß®îÝ»ë×_ŐÔ©Suůňe˝ńƲX,’ţÚśQµjUŤ?^Ť7Ö¸qăTŁF Mť:UçÎťÓرc%IÇŽStt´~ýő×kҲ78{ PYLÎďX7˛X,ZąrĄzöěY s‚µ˙ţ\égÎśŃÝwß­J•*iĎž=eţa·[nąEŇ-ÇŽSDD„6oŢ\,ů‹‚ľ/úH]şt‘ô×§V«VM­ZµŇöíŰť.',,L’´jŐŞb‰S*Ü:Tš=účŁÚ˝{·ŞT©˘={öب@nĄĺ>¨¨  IDATwU¬îçŞyŻ4)oó#Š—«Ç‹+ć5{slqÍ—Ąe]qFYе0®îă’ź‹{ľd>.{.\¨ .hÔ¨Q’ţúö®ŘŘXmÚ´I#GŽÔąsçÜ!ŕś””µhŃBż˙ţ»|}}s++żäu îPçp}ŕ›”K7Ýt“^{í59r„OůCžNź>­GyDçĎź/–üpWô}íV«Uźţąíő… $ýő0ŠĎďż˙®›oľY‘‘‘Ú·oźÖ®]ëîJĄŇrź—–8ŕĚ{@éR’slYšĎËR¬®ÂüŚ’Ąľ}űćúrŐŻ__m۶U˝zőÜP0ďż˙ľ†Z*˘/¬ňp `›”[ˇˇˇňôôÔşuëli‰‰‰3fŚ^~ůeEFFŞ{÷ŚĚő•ŃW[¶l™*W®,‹Ĺ˘¨¨(eeeI’–/_.ooo-Z´Č©˛,X Ű'3_ľ|Yożýv®´üś?^ˇˇˇň÷÷WłfÍ´sçN۱Ç+,,LcÇŽUDD„Ú·oŻ?ţřC’ôé§źĘßß_‹E&L°ť3gÎyzzjÁ‚’¤ÔÔT˝ńĆęßżżî˝÷^uëÖM{öěÉ3¦Ľę]łfŤ¨€€%$$¨Oź>ŞU«–š7o®]»v¨}íqE›ôŃGÚ»wŻÎž=«AŮĘŢąs§Z·n­!C†čŐW_U… ”śśě0QŰA’®\ą˘ČČH 8P&LĐ+ŻĽ˘äädŰq«ŐŞU«V©Oź>ęСCĘ–¤Yłf)<<\–ŹŹŹ,‹íçjV«Uß˙˝FڎŔŔ@ĹĆĆŞcÇŽjذˇň+úç?˙©hܸqzá…Ą[o˝Őéľ‘ň“í'GůíqTFaćÉ}÷ĘöíŰ5räHęÜąs¶9¤yóćúěłĎśnkGÇó+ß~ű­^~ůe[9kÖ¬‘——W®ą®7{öl >\ýúőSÍš5ŻŮWÔ{üjî^+ ;?:şĎ ;Ţ]=gĺW^~ë†+ŰĘ‘ëunËk=aŢŠź+ŢÓeËëw¬‚®‡…y˙čhľ Đ_|Q¨5§(kŕŐśťoóZś]ßóęWW¬‰ĚĎ(i[·n•$Íť;Wńńń¶ô_~ůEcÇŽŐŇĄKÝŕ”ź~úI-Z´P“&M4gνđ vóeĎ·ůý.âÎ^”iJ IfĺĘ•>'88Řáń›nşÉřűűcŚą|ů˛iÚ´©™4i’íx\\śiÚ´© 2 Ë?~Ľ‘döîÝkK;qâ„yüńÇ TvăĆŤÍŐšµ—vµŕŕ`#ÉLś8Ń;vĚ|ýő×F’iÓ¦Ť-O“&MLăĆŤŤ1ĆdddęŐ«›fَ͚Ϝ9ÓH2ß|óM®kxć™glŻźţysŕŔŰëxŔÔ©SÇ$&&:Ś-ŻzOť:eŞT©b$™)S¦ăÇŹ›ĄK—I¦U«V¶2ňk_{\ŮćöĆQÓ¦MMÍš5mŻ{őęeâââć/j;dffšV­Z™çźŢVć‘#GŚ——W®XOś8‘«~gŰxćĚ™ĆÓÓÓÄÇÇcŚ™6mš‘d"##í¶oZZšŮ¶m›©T©’‘d¦M›fÖŻ_oú÷ďo’’’ň+YYY¦C‡ć©§ž2V«ŐcLrr˛ńóóËu-ÎôM~c˛ ý”W~{®.Ł(ó;¬,óŐW____#É :ÔlŢĽŮ,_ľÜÖ?üđ1&˙¶vtüÂ… yŽ•śŇÓÓÍÍ7ßl–.]ę°ť 5ˇˇˇ>Ż ł•FçĎź7ýű÷·˝7nś‘d~ýőW[ZQîqGÜ˝Vv~´7WvĽ»zÎĘ«§°ăÝŐsV~ĺdÝpU[Ůs˝Îmή'E™÷J“ň2?˘d¸zĽŘ[Šúž.gąŽ~Ç*ězXŘ÷ŹŽć›Â®9…YÉoľ5&˙őŔ™őÝQżÇšX\ósqĎ—ĚÇP~°€«xĘ©ŚŚ ť;wNwÜq‡$é‡~$ůůůĺĘ׾}{IҶmŰ–UłfM :T‹-Rll¬$iÆ zđÁ‹\vAX,Űżk×®­””Űë#F¨GŹš={¶¦L™˘´´4eddŘŽW¨PAÆ ÓW_}ĄŁGŹ*##C´µĎŽ;Ô¬Y3™ż>H&×ĎĂ?ě0¦üęÍs¶5j(--Íö:żöµ§¸Ű|Îś9ňóóÓ°aĂÔ˛eK%%%]SWNEm‡uëÖI’5j”+ʇGţĘw¦Ť»uë&«ŐŞŻżţZ’äăă#IęÜąłSeרQĂ––ßXY»v­$©I“&ůĆ™gĆdAű© ůŻV”qç®{Eúď8ŞT©’--$$D’třđá|ŰÚ™ľ°7V®väČ ><ĎXQ4z˙ý÷uÇwČb±Čb±čĆoTjjŞ>ţřcť>}:WţÂÜ㎔–µ2'gî««vĽ»zÎĘŻĽ˘¬ö¦­¤ëwns¶/™÷€âQÔ÷t99ú«°ëáŐś]˙Í7…]s ;ŻŰ“ß|+ĺż8ĂQżÇšČü €ň‚ÍĘ­Ť7*==]]şt‘ôߎ;–+_ť:u$IŐŞUËłĽ—^zI+VԻᆱ]»v©eË–ňôôtIŮ®°cÇ5oŢ\AAA?~ĽŞT©rMžţýű«rĺĘš5k–VŻ^­ĐĐP۱řřx=z4×lV«µHő:#Żöµ§¸ŰüÉ'źÔîݻս{wíÜąSíÚµÓ˘E‹ć/j;d? _¤¸2d>řŕőë×OŁFŤRdd¤&Ož¬É“'¸¬üĆʉ'$IçÎť+RĚÎŚÉ‚öSAó_­(ăÎ]÷Š#uëÖ•$äŰÖ…ťrşrĺŠîĽóÎlj‚Yµj•FŤuÍrË–-SFF†fÎś™oEéďŇľV:ٰ×ďę9+żňŠ{ÝpÖő:·9Ó—Ě{@ń)ę{:g¸âýŹT<ëź«b+Šśó­äšőŔQżşzMd~@yÂfĺRzzş^yĺÝyçťúź˙ůI˙ýäĹěOFĎvňäIIR×®]ó,Óßß_ ŇÜąsőŢ{ď©oßľ¶cΖťýéŚééé’$cŚ.]şTđ ´#""B¶Oѵ÷HŐŞUŐżýë_˙ŇĘ•+őřăŹŰŽ+%%EQQQąÎŮżżfÍšU¤zť‘WűÚăĘ6·X,ĘĚĚĚ•6qâDiíÚµZ±b…2224~üx‡ů‹ÚÁÁÁvŻÇU˛˛˛´gĎmßľ]3fĚĐęŐ«5a„B=h™ßXąůć›%I_}őU®ă©©©ą^ç×7ÎŚÉ‚öS^ůíąşŚ˘Ě#îşWÉ~XŞk×®ů¶ua燜|}}őôÓO*V8ÇjµjĆŚęÝ»÷5ÇBCCuĂ 7hŢĽyJJJĘłś˘ôwi_+íąú>/ěő»zÎrćľ”ŠoÝpÖő:·9łž0ďŧ¨ďéśQŘ9•ď][~±DÎůVĘ=pf}wÔŻ®^™źKNqľ‡CÉ‹‹‹ÓŞU«4uęTw‡R,Ýr(éńĆ| L3PJH2+W®t:JJŠ‘d5j”+ý—_~1íŰ·7fßľ}ąň7kÖĚÔŻ_ßś9sĆ–>lŘ0s˙ý÷›ŚŚŚ|ë<{ö¬ńöö6;vĽ&gĘ~üńÇŤ$3aÂsřđaóÎ;ďš5kIfíÚµ&++Ën˝uëÖ5’Lbb˘-í¦›n2’LRR’1ĆjŐŞ‹ĹbÖ­[g–-[fn¸á#ÉüôÓOćäÉ“¶óţóź˙OOOóúëŻçŞ#55ŐI¦oßľfٲefüřńćČUďŐň«·QŁFćę?G׫WĎHş¦ÍµŻ=®ló›oľŮT®\Ůś8qÂVNĄJ•ĚźţiŚ1&##ĂT«VÍ´jŐĘcěć/j;ěŢ˝ŰxyyłvíZ“’’b6nÜhŞV­j$™˙üç?Ćc._ľl$™şuëÚĘq¦Ť'Ožl7nl>üđCłvíZłmŰ6sčĐ!“™™™g;g—ť=ΌɬlܸŃH2ŐŞU3Ë—/7»wď6łfÍ2UŞTÉg~}“’’’ď,h?ĺ•ßž«Ë(Ę<â®{Ĺc‚Ť¤\ý˝hŃ"s÷Ýw›ŚŚŚ|űÔ™ůÁŢXÉičС桇r*^{BCCMhhhˇĎwFAסŇfÉ’%¦K—.Ź÷íŰ×H2˙üç?mi…ąÇó㮵˛°óăŐ÷yaÇ»3çdÎĘŻnŐŞ•5jTˇĎOMM5S¦L1mÚ´1žžž.-ŰY%UOAĽ÷Ţ{fĚ1¦S§N¦]»vćŕÁeŞüýű÷›_|ŃH2ÁÁÁ.-ŰÝfĚaÚ·ooĽĽĽÜ ţż’oyÍW%ˇ$ţÎŕúŔfĄFAţS{ëÖ­¦_ż~F’‘d:věhşwďnBBBĚ“O>iŢ˙}»¤^ľ|ŮŚ=Ú<đŔ&22ŇŚ=ÚLž<٤ĄĄ9ç#Źţřăkâ°Z­fĆŚ¶ë>|¸IJJ2oĽń†--22ҤĄĄ™÷ßßT«VÍ´lŮŇlßľÝDGG›5jG}ÔÄÇÇç*wřđáפc̱cÇLHH©Ył¦ąńĆÍ€ĚůóçólŹĽę}íµ×lqľţúëćŇĄKćÝwßµĄŤ;Ö\ąrĹ©öµÇUmţňË/››nşÉüűß˙¶ť'ÉÜu×]fúôéćŮgź5Ź<ňí!{ů]Ń›7o6÷ßżńóó3AAAfúôé¦}űöć…^06l0—/_6/żü˛íĽ·ß~ŰLź>Ý©˛cbbLť:uléŮ?µk×ÎuŮ’““ÍäÉ“mů `~ýőW§ÇʢE‹L“&MŚżżżéŃŁ‡Ůżżí!˘‚ôM~ő´źňĘoŹ˝2 ;ʏó^Énű7ß|Ó\¸pÁÄĹĹ™éÓ§çš'ókkGÇó+ŮžţyÓąsg§âµ‡ÍyűěłĎLť:uŚżżż™3gÎ5Ç?˙üss÷ÝwIĆ×××Lś8±H÷x~Jr­4ćŻ9«°óŁ˝űĽ°ăÝŐsV~ĺĺ·nŘŰ8Q”¶˛çzťŰśYOŠ:ď•&ey~DÉsŐxٲe‹;v¬í>|öŮgÍ_|a«Ł°ďé ň;VaÖĂ‚ľt4ßeÍy˙ý÷ ˝ÚăĚ|›ßďŁÎ¬ďyő«+×ÄâśźËĂf€§žzĘL0ˇHe\ąrŶŮĂŐe_-ç‡g=EmŞT©b233MBB‚yâ‰'ĚĎ?˙ěöňíµ]^RSSËÍf€ś×žššjüýýŻŻE-ESRăÍŃ|UŘ ŔU,Ć#(,‹V®\©ž={ş;‡RRRÔ˘E ýţűďňőőuw8ĺí[|.\¨ .hÔ¨Q’$«ŐŞŘŘXmÚ´I#GŽÔąsçŠ=†[nąE˙5Qt˝WĘCۇ……ý?öî<,ĘŞ˙ř{ÓT$EM!۵Tô)·_ĄfÚ"Yą–FF*)*‘ ¸¦â––{Šä÷±ÄôIÓʵLE5ÜAQŮ”m>ż?Ľf–aćža`yż®‹ËkÎĚ}Îçśű, ž3‰‰©´2ŞĂ:Tp.'SŐÄą­şŕüHĆ`yřpľU®˛űu_UŃo.^Ľ€€üńÇ•V†9´mŰ"‚ÄÄD«ÉßÔ¶S©TđôôDBB‚±aZ ]u7G­.ý±:©Şţf©u®*ţÎAD5ťĄ """""""""ŞN/^ڱcÇrsc%aűVŽČČH#==]›fcc-Z K—.xüńÇ-™‚c…¬ű'™Š}‡”şví^ýuY:®\ą«Éż:µťąUVÝkr›‘ĺń0‘qqq1brssQTTTiźäVS±}+ßţýűË–-ĂČ‘#áääř믿‰őë×Wz "‚Ű·o agÇ˙˘0VEĆJNNŽößzőęUVTq.'Sqn#"˛~śokµZŤ-[¶`űöí¸pá~˙ýwüôÓOŘľ};~ţůgś:u ź~ú)bccѬY3¬^˝Ď>ű,ŕŢ˝{ Avv6š4i‚˘˘"mż)/oŕAźš?>’’’ШQ#ś8q @`` ŕÜąs4iÜÝÝ‘’’‚‹/bńâĹđööĆęŐ«qúôi888`ôčŃXşt©Îr~řáŚ9·oßFHHľúę+ŔŇĄK1fĚ,[¶ ÇÇýű÷±páBś={'Nś€ćĎź//ŻrŰ,33ááá°±±A~~>âăăáĺĺ…)S¦ŔÁÁŰ·oGll,rrrpýúuŚ=0gÎťcI_}uŃ—JJŠQmgJůĺ)Ż-۵k‡}űöaëÖ­Řşu+8€·ß~˙ü󢢢đŰoż•HżpáNś8›rŰąA:óÔ\ëŕŕP"¶ňę®qóćMŚ={÷îŐöóçž{Î`űĘ×C}IéX44¦ŚéçjµZoŰÖ©SGo^ÇŽĂ1cđÜsϡQŁF1c222PŻ^=őŐĹ\cŮĐ|EDT- ‘• ŃŃŃ–ŁŚS§N‰«««xxxȡC‡,ÎC‡í[ůŇÓÓeěرâćć&uęÔ‘—^zIüüüdĹŠ’źź_éĺ'$$H`` $ŮŮŮ•^îĂĆ”±’ťť-“&MҶý|PmÇ™ŻŻŻřúúVjÖşUśËÉT5}n«.8?’1Ř_śoŤWŮýż˛óż|ů˛OOOązőŞÔŻ__Hxx¸\ştIÖŻ_/¤cÇŽ""RXX(;v”áÇkóůçźÄÎÎNŠoM+ťwAAtďŢ]† "jµZDDV­Z%d۶m""Ňşukqww׾ŢÁÁAĽĽĽJ´‡&żňĘY´h‘;v”xÝŰoż­}<|řpILLÔ>îŐ«—8;;Kff¦Î¶ĘĘĘ Ő¦ĄĄĄ‰‡‡‡¸ąąIFF†Ţ8u1TßňčĘß”¶SRľ’ş”×–·nÝ’JÝşu€Ě1CvďŢ-˛k×®2éÆ “ëׯëmç´´4ťy6¬Üż;誧§§iÓ¦ÉĹ‹eűöí@^|ńĹ µ©Jú’’±¨dLÓĎóňňô¶­ˇĽ<<<¤QŁFÚç $iii;ËJ竪Rç ˘š‡ČjpÓ•‡‡,‡ó#ý…j˛ę~@SFńÍ·mÚ´)łIÖŮŮYj×®-""QQQ@JĽĆĂĂŁĚuĹóž7ož¤¤$íó………˛jŐ*ąsçŽö57nµZ-îîîR«V­rc-/=??_žxâ éׯź6mĘ”)rüřq‰‹‹Óz)ý«łť&Ož,$55µDúÚµk€L0Á`śĄŞoytĺoJŰ))ßP]”´Ą¦OÝľ}»ÄµşŇ•¶syyę˘ď0€f˝“““Ô­[WqűzŔŘ:W|,S¦ôóâĺo[%y5nÜXČ‚ D­VK||ĽdffVhěTt,3_U "sáwđQ*•ŞLšŁŁ#nܸřĺ—_®®®%^cccŁ7ßß~ű ТE mš­­-†Ş}>>€§ąękj^ć(_I[jú”ŁŁŁÁtĄí\^žĆ*Ţß7nŚÄÄDícsޟ⌭cqĹǢˇ1eJ?/^nń¶U’×ŇĄKńţűď#00ëÖ­CTTěíí+4v*:–ű÷ďŔřůŠČÚq#""""""""""""""""""Ł]»v žžnÔuš ĚçÎť+÷5GŹ…··7ÜÜÜ‚úőë›ç°aĂPŻ^=DEEaëÖ­đőőŐ>—žžŽäädäćć–ąN­VëĚOłyřâĹ‹%Ňťťť 64:FsÖ×”ĽĚQľ)m©Oe´ł©ĚyŠ3W Ť)sŢ%y 8˙ý7z÷îŤcÇŽˇk×®XłfM…ë[‘±lę|EDdíx€jś»wďZ:@ZZbbbQ%ĺYK˝-ĄŞŰ[‰ĚĚLK‡@T-Xăř%""˘‡CMźTQžžž€íŰ·u]‡ááámúĄK—°cÇ@@@ ЧOe7,«T***ŻA6lľűî;DGGcŔ€%ę››‹ČČČ×$$$ **Jg~šO1/]ď+W®zôčˇ(®â Őלyéj;s”oJ[ęSílLż)Îśý±8sŐŃĐ2ç˝Q’×´iÓŕć憝;wbăĆŤ(((@HHH…ë[‘±lę|EDdíx€Č ť;wsçεtŠuęÔ &L0éÚE‹!88/żü2|||pöěY3G÷@^^"""đŇK/ÁÉÉ©RĘ0Fbb"ÂÂÂŕďďŹuëÖUZ9ÖVoK©ŞöVjÎś9čÖ­›Ţ{RTT„ŕŕ`í'בuăĽm^ĹËhҤ Ţ}÷]«ż7oŢ„ŻŻ/ţýďăµ×^Ăľ}ű,RĄ°ćľR‘جUeÔ‰ë U'Őm=ĄęK×ű$Η@vv6€’‡–ďßż_ćuYYY€ÂÂBŚ?vvv4iţűß˙âŢ˝{Ř»w/RRRüďÓżKçŚzőę!&&=zôŔ’%K0uęTĚ1C»Ů:55×®]Ă®]»đý÷ß###päČ\˝zîîîHMMŐn".Żź|ň ˛łłńôÓOĂÎÎN›Ţż¸ąą!,, ~ř!ľ˙ţ{L™2ź~ú)Ţ˙}ťm5aÂxyyaѢE¸~ýş6}ńâĹčÜą3ĆŚ¸sç ??_g>ĹŞŻ.ĺĺoJŰşćŢ˝{t÷ %m©ą>''§ÄµşŇ•¶syyꢫîšţ˘éŰŔ˙ iň4ĄMçÎť‹öíŰcÓ¦MĺĆcl‹+> Ť)Súyńr‹·­’ĽćĚ™Łm#___4lŘŹ?ţ¸âúęëo¦ŽeĄó•’űFDdMx€ČĘüţűď Ĺ'ź|béPkŐŞęÔ©côu .ĤI“Ž˙üç?hܸqĄ}*cíÚµńŮgź!)) EEE•R†1<==«dŁ“µŐŰRŞŞ˝ő)ľycěر8}ú´ŢO ´µµĹĉńÉ'źŕÂ… U"™ó¶yçíŇetíÚS§N5kć0jÔ(ěر;wîÄěŮłőn ©Î¬ąŻ[y›éŞš®8L­“>\O¨şĐµžZËxU˘2b5%ĎęÔf–¤ë}RMź/sssµß”’’‚ůóç#22R»96<<™™™X°`öŔÄ”)SЦMěŮłžžžđóó——Ž9‚§žz ŁFŤBrr2˛łłËäýŘcŹáđáĂčÝ»7Ž?ŽdeeaÖ¬YP©T€4hĐ!!!pwwÇäÉ“áččÔ­[~~~hĐ Ž=ZnŠoîvuuĹرc1zôčuŻ]»6öěŮ~ýúaëÖ­ BZZ6lŘ{{{ťíő裏âСCxűí·ńŢ{ďáóĎ?Çĉáää„={öŔÎÎńńń4i€›ŚĂÂÂpňäÉrďˇú–ŢĆG÷ IDAT¦/cŰÎĐ57nÜ@pp°¶¬ h7z+mK[[[|őŐWÚ>őŮgźáďż˙Fnn®Ît%íśźź_îµĺ)^wÁś9s´›Á§NťŠśśĚž=©©©|Â}~~ľImšśśŚÄÄD|ţůçĺĆŁ¤/-Y˛ÄŕXlÖ¬™Ţ1el?×w_”ä•››‹W^y‘‘‘:t(şvíŠM›6)Şď… ôö7SÇr‡ ÎWjµZŃ}#"˛&*)ţť0DDDDDDDDDD¤R© K‡b1 čÓ§Ž?ŽFŤY:śJ×¶m[«´ĚÄÄDXËźÇU*<==‘P©ĺX[˝-ĄŞÚ»´‹/" üń‡6Mé=9yň$Ţyç>|őęŐ«ěP­–źź &&¦ŇĘ0eâĽ]ueXjü–§AP©T¸sç.]ş„gź}·oß¶tXVĂkĽşćăšGEÖţžNĆ0×zj-ăU‰ĘŐ”<«S›Y ]ż“[ó|Éů¨ú9{ö,přđaK‡BF¨ŠűVç ˘šß @DDDDDDDDDd%Dďľű.Ţ˙ý±ˇ®\ą˘ýÄ?˘‡Őµk×đúëŻăćÍ›&]˙Żý îîî?~Ľ™#ŁŠâĽ]}Ë0‡fÍš!33 7oFŚa鬊5ŢÇŠÎÇŐ=®'d­t­§Ö2^•¨ŚXMÉł:µ™µă|IDć’››‹E‹aĺĘ•–…ŚŔűFDŐ Y‰ź~ú ýőúôéŁM;|ř0>˙üs´jŐ 7nÜ€ŻŻ/śśśŕííŤ˙űż˙Óľîţýű5k† †çź={öD||ĽÁçŐj5~˙ýwŚ7­ZµBJJ şw-[ęüş÷ŇÔj5bbb0tčPtëÖM[Ź‘#GÂĹĹ:t({ě1x{{ăĎ?˙lßľŁGŹFNN®_żŽŃŁGkgffbâĉřâ‹/„Ţ˝{#((¨D<{÷î…‹‹K‰OĽÔ•vďŢ=aäČ‘2e &Mš„śśśu0TŢŢ˝{Q»vmŘŰŰcßľ}¸{÷.† •J…˙űß8}ú4ŕřńăhŢĽ9ľůćEmP%ő?wîüüüŚ€€řřřŕÔ©SFŐ[‰‚‚|ůĺ—1b&OžŚQŁF!22íÚµ¬X±666ÚÍžYYY7o^‰4@˙4Gˇ/_cîcTT† ‚Ź>úuęÔJĄŇţ¬^˝§OźÖöőŇnŢĽ©Ű^^^8věX™×ôîÝ+V¬@rrr…ëLćĂyŰĽó¶ľ2t14úÇ&;v ť:uÂ1c0uęTÔŞUKń| ý×ŃŃ>}:Ţzë-ĽőÖ[ Ő{=űŠé}ĹÔŘĘ›ŹMmCC÷¦ĽţĄ+Së¤a¨Żkp=!k¤k=-oĽę›GĘł...řńÇMË™[”0fžôŻ}ş®Qú»¶ľőPÉĽ¬tMÓż9b54/ó>‰ó%™Crr2"""ŕĺĺeéPČĽoDTí‘• ŃŃŃ–ĂbŢzë-Q©TRPP ""EEE+Ź>ú¨±cÇĘü!ß˙˝ŘŰŰ 9pŕ€ >\µyőęŐKśťť%33Sďó·nÝ’JÝşu€Ě1CvďŢ-Æ “ěělEq_ľ|Y§§§\˝zUęׯ/$<<\.]ş$ëׯұcÇ׿ND$++K<<<$44T›–––&âćć&""ňăŹ?JÝşue۶mÚוN+,,”Ž;ĘđáõŻůçźÄÎÎN4WZŢG}$uęÔ‘»wďŠČ˝{÷ÄŮŮYŢ}÷]íu………âăăSˇ6POëÖ­ĹÝÝ]DD ÄÁÁAĽĽĽ×[‰˘˘"éÖ­› ×´iS™2eŠŢëŮW¨H_156]ýËÔ64toôő/]qZ'%}]ĂÔő¤¦˙žNĆ©čzZ<cć‘´´4ťc6 @víÚeŇX®Čܢ„±ó„ˇµO×5J~×Ö‡ˇyŮ5ÍPüŤUß˝4ö}’µÎ—śŹ‰Uńw"Şx€¬FM˙OmWWWqpp(“îáá!$''G›öő×_ ¦ÄĎkŻ˝fđyP©T ł|Shň+ÎŃŃyyyzŻ;pŕŔŢŢľDşŹŹŕŕÁÚ4[[Ű2×Oűĺ—_®®®%^ŁiOcĘk۶-^~ůe,_ľpéŇ%!??7n¬]»ďľű®6SÚ@i<ăĆŤCßľ}±dÉ„‡‡#//Šë­ÄÎť;­[·.‘®«^ú(éU‘‡©ů*ąŹ={ö„Z­ĆöíŰuęÔĽüňËŠâ(^FăĆŤ‘››[ć5€7n(¬UÎŰćť·ŤĄo.”ŤÍĄK—ÂŢŢxá…ťť]¦.ĹĹÄÄ`řđáضmz÷?ţM›6Ĺ”)S““I“&aÝşuŠëĹľbŢľbJl¦¶ˇ’ëŚí_¦ÖÉuë YŁňÖÓŇ”Î#ĺÍ{¦ŽeSç=%Śť' ­}•‡’yŮĐšf®řË‹ŐĐ˝4ö}çK""""Ş.x€ČJŘÚÚ˘¨¨HŃk›7opqqAzz:’““un V«Őź·&šŤ/^,‘îěě hذˇâĽ®]»HOO7KycĆŚÁ‰'pôčQDFFbÖ¬YxóÍ7±bĹ ś9s®®®%6ľBiŐSóɶW®\đ ľş6 OÓlĆ,ť—©ĺőë×... ENNÚµk‡QŁFáčŃŁř裏0zôhĂ4@i<(((@ź>}”ܨ¤¤ŢJ<ůä“€ŘŘŘé÷ďß/ńXłI&?? "¸{÷n‰x ő?säa sĺ[TT„řřx>|łgĎĆÖ­[1eĘ”ź–ŞR©PXXhr¬wîÜ4mÚÔä<Čü8o›wŢ6–ľąP“·ˇ±9mÚ4¸ąąaçΝظq# Rn™—/_Fjjj‰Ť„ď˝÷zôč"++«Äë5c×ě+ćď+Ĺ•žŹMmC%×éë_]ŠSŇ×5¸ž5*o=-=NŚ™G”2×řŕٰa„„„HŻ^˝$33Óŕó""®®®@˛łłŤŽ;++KHóć͵išüŠ{üńÇ€ČíŰ·€¸ąąi_“››+^^^ҢE IMMŐ¦JçÎťµ×ĆĆĆJýúőeÇŽÚ×”NűűďżĹÎÎNśśśdçÎť’››+{öě‘ ąpá‚âň4¦Oź.*•JâăăµižžžŇ·oß2í˘¤ rss€¸şşU˙† ŠJĄ’_~ůE6lŘ Mš4'±±±ë­Äž={€4lŘPľ˙ţ{ůűďż%**Jęׯ_˘^ 2eĘ9wîśĚź?_5j$´ĺęćČC‰ŇímĚŘĐwĂÂÂÄÝÝ]ľýö[Ůąs§ůä“RŻ^=ą|ů˛6­yóć Dš5k¦s,ž+‘¶jŐ*9|řp‰´Ĺ‹l3gÎČ'ź|˘MűúëŻĺÎť;ŠâYĽx±4lŘP^xá9|ř°,X°@Ą˙ţ’žž®¨ŢJ¬YłFZ·n-NNNŇ·o_IHHĐöGŤłgĎJÇŽĄ^˝zŇ«W/9{ö¬tíÚU† "›6m’ĽĽ<ýÓy’śś¬ł˝őĺ«ä>Ţ»wOvíÚ%ÎÎÎÚtÍOăĆŤeË–-""ňĹ_HłfÍdË–-˘V«eöěŮÚ×}úé§’ťť-łfÍҦ•¸çK—.[[[ůçź×ůacŤ‡8o›wŢ>uę”Î2Ęż†ćB%c€<óĚ32sćLyçťwäő×_×{h*55UüýýĹÉÉIš4i"}úô‘ť;wŠČMŞź|ň‰<ńÄâěě,łfÍ2x?ŘWLë+¦ĆvďŢ˝óqEŰĐĐ˝Ń׿JÇQ‘:)é릮'5ý÷t2ŽąÖS]ăUß}püřq4jÔH›~ňäIĽóÎ;8|ř0ęŐ«gTžUńw"Şl,ýĎŞU«đóĎ?[ĹE•Jeđ'))ÉŇa’ ¶{kÉúDFFâ>Ŕ‡~¨Mł±±A‹-ĐĄK<ţřă.#..gĎžĹÜąs+ś™çmëTcł:c_yxÓ׹ž5ł¦őÔ8ĎVoś/©˘®]»†×_7oŢ´t(RťęQťbĄęGDđî»ďâý÷ß/qţőŻÁÝÝ]{0—Čx€ČŠ4iŇ[¶lÁ¸qă›› ČÉÉ)ńoU?mÚ´©ŇČ:cíÝ»7V¬XäädEu#"2;!"""""""""˛$::ÚŇaX…ääd™6mšLš4IůŕäСC–Ťj¨„„ ÔöÇ   ÉÎζtX—žž.cÇŽ777©S§ŽĽôŇKâçç'+V¬üüü ĺ]PP 3gΔĚĚL3E[˝ůúúŠŻŻoĄ–Q‘uó¶u©Ě±iŮŮŮě+dJúş9ÖţžNƨčz:kÖ,3GD•éaZÓŞĂ|©4˙ĽĽ<9xđ Ô­[WČŚ3d÷îÝ2lŘ0ÉÎΖáÇKbb˘öő˝zőgggÉĚĚ”˘˘"éÖ­› ?hĐ IKK+· BBB€ś>}şD< ‘¬¬,ńđđĐĐPíóiiiâáá!nnn’‘‘ˇ¸žşhę­ˇ´ĽŇ ¤{÷î2dČíýXµj•m۶‰HëÖ­ĹÝÝ]űzńňňŇOyi­GZZšÎű »ví2éţ_˝zUęׯ/$<<\.]ş$ëׯұcGŁęŁ‹§§§iÓ¦ÉĹ‹eűöí@^|ńEíkôµ±’ř ĄcÇŽ2|řpmž˙üóŹŘŮŮ•éKĄëahlüřăŹR·n]mĐ•¦¤™RvyíbhĽ+Q^Ů·nÝ2şŹ]ż~ݤ~«‰÷­·Ţ•J%:c=~ü¸ö:cTĹß9¨fŕa"""""""""˛ÜdDDDĺ±öĂDD3ÎŹd öŞÉ¬ĺ0€F›6m€Üľ}[›§=ĽQú'66V–-[&äĉ:óŇĐl .NWZéÍŦÄ$"Ҹqc ,µZ-ńńńzn¤§§‹˝˝}‰Í×3fĚĐć7yňd ©©©%®[»v­ &UĎŇJ×[iyĄÍ›7OHRR’6­°°PV­Z%wîÜŃľfăĆŤ""˘V«ĹÝÝ]jŐŞĄ7žňŇĚU]÷ąĽt%÷żt˙qvv–ÚµkU]4÷SłI^DÄÉÉIęÖ­«}l¨Ť Ĺ%$!!ˇÄk<<<ôŽ%m#ň O”V±nÝ:DEEÁŢŢľÜ×7jÔcÇŽĹś9sŠćÍ›ă×_Ĺřńă€2yřřřVŃ~{ýúu4kÖ¬ÜXŔ¬ý‘ČúW""""""""""""""""""z(¤§§#99ąąąežS«Ő¸|ů2€ŞÝÔj(&8p ţţűoôîÝÇŽC×®]±fÍ˝ů~öŮgxä‘Gđő×_ăĎ?˙Ä /Ľ Ý ­Ů„}ńâĹ×8;;6lXŃj•`jyšűpîÜąró>zô(Ľ˝˝áćć†ÔŻ_ß ëVí¦äţ[ZEŰřÚµkÔŐćj%ýČ”˛+«ď™»OT´ßÚÚÚ˘¨¨¨ÜçÍuXŠČT< @DDDDDDDDDDDDDDDDDTxzz"77‘‘‘%Ň…'ź|[âůű÷ď—x¬ŮüšźźÜ˝{·Rb€iÓ¦ÁÍÍ ;wîÄĆŤQPP€˝ů:99aôčŃX¶l.\>ř@űśćÁ·oß^âš+W®zôčQˇzŞT*]^i:t„‡‡CD´é—.]ÂŽ;(((@ź>}w˝ąęˇŹ’űoJ¬ćTŃ6öôôP¶Ý”\§¤mtmT/ž¦¤™Rveő=ső ŤŠöŰfÍš!##ŁÜçďÜąhÚ´©Ń±™ťĄ """"""""""""""""""óŇlŕĎÉÉA˝zőýű÷‡››ÂÂÂpőęUĽňĘ+HHHŔ‘#GđĂ?ŔŰŰ›7oFHHśśśĐ®];ěßż·nÝ*‘·§§'0}út 66yyy€˙ţ÷żčŮł§öS˝333+Ě™3ăĆŤ|}}1jÔ(<ţřăŰ (( .ÄĺË—áîî®Mź0a~řá,Z´ÚMĽ‹/FçÎť1fĚĹőÔ|ęxqîîîHMMĹ•+Wŕâ⢸ĽŇ‚±aĂÄÄÄ ==Äőëב––†ĄK—RSS‘™™‰]»váćÍ›ÚMËGŽAóćÍáääT˘íŕŢ˝{eŇt1µşîsyéJîż®8ł˛˛………°łł+«Ršţ™••{{{ĐřĐÄi¨Ť Ĺ7~üxDGGcҤIhٲ%|||přđa¤¤¤xđ‰ő®®®eî‹’¶Ůľ};Śí¦üŇiJú‘)e+m—ŇýŔcú„’>VŃ~Ű­[7|÷ÝwČÎÎÖůíšů±K—.ŠëHDdN< @DDDDDDDDDVĺСC–*YAA V­ZŽ„Ş“«WŻ˘E‹•^ס‡KAA×"" ŕzJ%"ÚOd'ăĺććbîÜą¸xń"ŕłĎ>ĂčŃŁńÔSOˇvíÚŘłg>ůälÝş?˙ü3úőë‡ 6ŔŢŢ˙ţ÷ż±fÍLź>cÇŽĹK/˝„YłfˇE‹HLLÔ–‰””Ě›7qqqŠŠÂ˙ýß˙ÁŐŐČĚĚĬYł)))ÇÝ»wMŠIS§W^yţţţ8uęşvíŠE‹l gggôěŮ *‘ţ裏âСCřꫯđŢ{ďÁŰ۶¶¶prrž={`gg§¨ž………xä‘GĘ”6ä IDATëçç‡Ő«WăčŃŁpqqQ\^i­ZµÂáÇńůçźăČ‘#HJJ‚źźfÍšĄ#4iBBB°páBLž<ˇˇˇŔ”)S0{öl6ś/X°>>>X˝zu‰´÷Ţ{®G~~>fĚQć>{xxÜ'—,Y˘˝.<<cÇŽĹŞU«píÚ5Ŕ”)S0mÚ´2±"";w®vCţÔ©S1}út,Y˛©©©|#EDD„Ţ6~îąçĹ·gĎ|ńĹđóóCăĆŤ1bÄ<őÔSh×®’““QTT„… –ą/†ĆFíڵѠAÔ®][[·Ňi†úŃ… đő×_]vyí‚öíŰëĽßJčë¶¶¶řꫯŚęc¦ö[MĽřöŰoqčĐ!ôěŮłLĽ€­­-üýýŐŹČÜTRü{_,˙ŃODDúřúú"&&¦Ňňç:DDTľččhnp"E¸žRMW™óĄJĄ˛Ř|ܶm[$&&˘şm5ËÍÍE‡pňäI<ú裖‡Ş©×^{ ?~™çúő뇦M›â›oľ1*O???¨ÔżsQÍŔo """""""""«QÝţC™ô+**Âďż˙ŽM›6aË–-¸sç^zé% 4 B“&M,âCeöěŮřꫯpůňeťźćGD†qŞţ ńÝwß!44YYYřřăŹ1yňdí'WV'–ÜěGDT\O-ëaX?rrr0gÎĚź?vvv?~<ĆŚzőęY:4ކ/^ڱcÇň @ ¤äpZbb"Ú´iSŃFu˝/«V­B×®] gggmz\\Ξ=‹ 6X0:"Şél,=\ţüóOÂĹĹŻĽň 8€±cÇâÜąsŘż?ĆŽË•`äČ‘°±±ÁĘ•+- ‘EěŢ˝O?ý4ĆŚľ}űâüůó9sfµ<@DDT“Ő«WÓ¦MĂ•+W0~üxĚ1®®®ŚŚÄ˝{÷,^Ť$"¸}ű6€‡/­]\\:tč€Ö­[céŇĄ5j”ĄC" ?Ö¶áĽ&¨®÷ĄI“&زe ĆŤ‡ÜÜ\@jj*ÂĂñ{÷nľď$"‹âa""""""""""ްӧO#44O>ů$ž{î9ěŢ˝#FŚ@bb˘ö9wwwK‡ůPkĐ >řŕ|ýő×ČĎĎ·t8DDU&..ÝşuCŻ^˝Đ¶m[ś9sË—//ńiŤDDDTýŘŰŰcâĉřçźđńÇ#<<\{(ŕţýű–ŻĆHLLĸqă––FNNŽ…ŁŇŻ^˝zČĚĚ„ŤŤ ľ˙ţ{<ňČ#–‰^^^ÇâĹ‹QXXµk×bÆ hѢ…ĄC#˘N%ü~6""""""""""2Á™3g°yóflÚ´ IIIhٲ%ú÷ď???téŇĹŇáŐH—.]“O>‰Ő«Wăťwޱt8DD•ęěŮł Á?ü€Nť:aöěŮčÜąłĄĂ2•J…ččhřűű[:""ŞFćőăćÍ›;w..\Ç{ AAA5jj×®méĐŞĚĂ|‰j???@LLŚ…#!˘ęŽß @DDDDDDDDDDŠ]şt ,@—.]Đľ}{¬\ą˝{÷Ćľ}űpáÂísd-[¶ÄŔ1kÖ,đł€čauëÖ-ĂŰŰńńńŽŽĆÁŞDDDTVăĆŤ1sćLś={ @pp0Ú´ioľů………–ŹČ"x€ôşvíšv“«V­777ěÚµ —/_Ö>§R©,*řüóĎqňäIěÝ»×ҡ™UNN"##áîîŽőë×cѢE8uę”öÓ‰¨fhѢ,X€¤¤$ôîÝü1<<<đÍ7ß ¨¨ČŇáU) """""""""˘2nßľŤµkעoßľhٲ%BCCáćć†üׯ_ÇÚµkŃŁGŘŘđĎĚÖćąçžŹŹćÎťkéP̢°°ß|ó Z·nŤéÓ§côčŃHLLÄ#`kkkéđČBžxâ ,_ľçÎťCĎž=ńńÇă_˙úÖ®] µZméđĚ&-- 111°X ™™™+›ôă˙Ň ##C{ iÓ¦5j`ăĆŤ¸qă†öąZµjY8R2$((;věŔéÓ§- Q…ěŢ˝O?ý4ĆŚľ}űâüůó9s&ęׯoéĐČJ¸şşbůňĺ8uęž}öY|đÁčСbbb "–ʍB¬[·®JË.**Bdd$şví '''łäąhŃ"ăĺ—_†ŹŹΞ=k–|vť:u„ ,Y) """""""""ŞÁîÝ»‡m۶ÁßßÎÎÎ9r$`ĺĘ•HKKömŰŕçç‡GyÄ‘’1úöí OOOĚź?ßҡ™$..ÝşuCŻ^˝Đ¶m[ś9sË—/‡łłłĄC#"""+ĺé鉵k×âäÉ“h۶- „§žz 111–ŤRWŻ^­ô2<==-öͶ¶¶ Ä™3gPXXXáü.\I“&!<<˙ůĎиqcÜ˝{× ‘>üZµj…:uęX: ""˛R< @DDDDDDDDDTĂÜż۶mC@@š4i‚ %%‹-ÂŤ7´ĎńS—«/•J…ŔŔ@¬_żׯ_·t8DDŠ%%%Áßß/ľř" ±oß>lŢĽO>ů¤ĄC#""˘j˘]»vŘĽy3Nś86mÚ`Đ Ax饗°mŰ6K‡F‘‹/âí·ß®’˛j×®]%ĺčR§N4iŇÄ,y-]şŹ?ţ8lmmѰaClٲĎ?˙ĽYň~Řmܸaaa–¬ŐEEEŘż?FŽ gggĽńĆHNNĆôéÓ‘’’‚ýű÷cÄhĐ ĄC%3yď˝÷ŕŕŕ€%K–X:""nÝş…ŔŔ@xyy!>>ŃŃŃ8pŕ:wîléШšňööĆćÍ›qčĐ!899ˇ_ż~čŇĄ öîÝkéШš»ví^ýuÜĽyÓҡT+W®\JĄ˛tDDD """""""""zH©ŐjěßżhŢĽ9şvíŠ?˙üaaa¸zőŞö9s}ÂY—:uę`Ô¨QXĽx1rrr,‘N999ŚŚ„»»;¶lقŋăÔ©SđóółthDDDôčر#¶mۆ Nť:xůĺ—ŃĄKüńÇ–ŤŞ©Ő«WăôéÓ¸~ý:FŹ­MĎĚĚÄĉńĹ_ ((˝{÷FPP222ôć—““éÓ§cČ! D÷îݱ`Á˝×ÜżłfͰaĂđüóĎŁgĎžŹŹ×>îÜ9řůů!88đńńÁ©S§ V«ńűďżcܸqhŐŞRRRĐ˝{w´lŮŇ`śçĎźGż~ýШQ#Ľđ ří·ßĹł}űvŚ=999Ú6Ó<Ö×f†b5ÔĹU4ŻcÇŽˇS§N3f ¦NťŠZµj!''+V¬€ŤŤŤöCVVćÍ›W"-776lŔŰożŤÎť;ăđáĂxć™gŕęęŠŕěŮł0`7nڶmŰâĎ?˙,wLL †ŠnÝş~úé'Ś9...ČČČŔСCńŘcŹÁŰۻĵ…!C†ŕŁŹ>Bť:u R©´?DDô""""""""""z¨;vL>ůäiÖ¬™víÚÉ´iÓäüůó–ŤŞXZZš<ú裲dÉK‡BDTBAA,_ľ\š6m*őë×—iÓ¦Inn®ĄĂ˛*$::ÚŇaQ5Ăőð}űöI÷îÝ€ôčŃCŽ=jéăýµÄÓÓSű8++K<<<$44T›–––&âćć&:ó)((îÝ»Ë!CD­V‹ČŞU«€l۶­Üň†.‰‰‰ÚÇ˝zőgggÉĚ֭̑[‹»»»¶ ńňň’ĽĽ<9xđ Ô­[WČŚ3d÷îÝ2lŘ0ÉÎÎÖ٧§§O?ýTvíÚ%Ë—/—zőꉭ­­śű IIIđđđaÝşuxăŤ7ŕŕŕP¦Ľ#GŽ cÇŽ:㊍ŤĹkŻ˝†ůóçŁYłfĹ·aęJ+]˙-ZŕÚµk%^ăěěŚüü|Üąs§DĄŻŐÔŁřµM›6Ő~Ëôďß±±±¸˙>jŐŞ…Ó§OĂËË ť:u¡C‡tÖ—Ş–ćńbbb, Uw6–€LsćĚ„††ÂÓÓ^^^X˝z5^{í5ěŰ·§OźFhh(‚‚‚pńâEl۶ÍҡQ ‡nÝşˇWŻ^h۶-°|ůr """‹ř˙ď˙!..[·nĹůóçѡC <gÎś±thT 8p´›ĺ5|||ÔyÝożýŕÁĆp [[[ :T{ ´ŁGŹÂËË "RćGł ~ܸqčŰ·/–,Y‚đđpäĺ塠 @›‡JĄŠ+^·7ŢxŔżM)‰GĄm¦+VSË45ŻĄK—ÂŢŢxá…ťť]&ncéşľQŁFČČČ0x­¦Ĺ9::"//Oű¸gĎžP«ŐŘľ}; Nť:€—_~ŮÔ‰ČJń0Q5réŇ%,X°]ştAűöí±rĺJôîÝűöíĂ… ´ĎixxxŕŐW_ĹÜąs- ŐPIIIđ÷÷Ç‹/ľÂÂBěŰ·›7o†»»»ĄC#""˘NĄRˇ_ż~8~ü8¶nÝŠsçÎÁŰŰţţţHLL´txTŤŘŘ<؆wńâĹ隯 6ÔyÝŤ7çÎťS\Vzz:’““‘››[ć9µZ ŕÁwooo¸ąą!$$őë×Wśżšz=ńÄŠâŃĹÔ6”µRJň8p ţţűoôîÝÇŽC×®]±fÍŁĘ©jcĆŚÁĘ•+ńá‡büřń BXXÂÂÂ,™Yąk×®i7ů·jŐ aaapssĂ®]»půňeísş>Śxđíű÷ďÇáÇ- Ő ·nÝB`` ĽĽĽŹččh8pť;w¶thDDDD%¨T*ôíŰÇŽÓ hßľ=üýý‘””déđČ ©T*jk>Í^ó)ěW®\ôčŃCg>:t„‡‡CD´é—.]ÂŽ;t^ăéé‰ÜÜ\DFF–HOHH@TT čÓ§ă7Ȣ©×믿®(]Lm3@Y(Ą$ŻiÓ¦ÁÍÍ ;wîÄĆŤQPP€˙ű”ţüü|€ŕîÝ»FĹPŠŠŠŹĂ‡cöěŮŘşu+¦L™[[[K‡FDDffg騬۷o#66111رcěííŃ·o_Lś8}úôA­Zµ,"U#Ý»wÇ /Ľ€yóćaóćÍ–‡r999ŠŠBDDěíí±xńb|řá‡ÜtBDDDVOs(ŕµ×^Ă–-[0uęT´k×ÄôéÓáááaéÉJ¸»»#55W®\‹‹ &L€~ř‹-B@@š6m XĽx1:wîŚ1cĆčĚ'886l@LL ŇÓÓ1pŕ@\ż~iiiXşt)ŕŢ˝{€ű÷ďú÷ď777„……áęŐ«xĺ•W€#GŽŕ‡~¤¦¦"33»víÂÍ›7‘‘‘8räš7o®Í+''őęŐÓ[WÍf÷;wîŔŃŃ0ţ|ôďßC‡E^^žÁxîÜąŕć(n3]±*i]LÍkÎś97nŕëë‹QŁFáńÇđŕ0ABB¦OźŽ€€ÄĆĆ"//đß˙ţ={ö,qP@Ł  ťť­ýćM|jµZűÍ ŮŮŮ€ĚĚĚ2ő(.++ PXX;;;DDD`۶mđööFrr24h€Ç{ nnn|oFDô± µtDDDDDDDDDDddd`óćÍ ÁčŃŁńóĎ?ĂÍÍ _}őV®\ ___´iÓ†˙aG&©_ż>"""đÎ;ď QŁF–‡BřöŰońć›ob÷îÝřěłĎŤNť:i7˛r_~ů%üüüĐľ}{K‡BDDŐ×óP©Thßľ=>úč#´oß›6mBXXNź>Ť§ź~Úbď©x­Çőëב€§žz m۶E­Zµ€;wî`ٲe8qâ~ýőW888`ĹŠĺ~¨ŁŁ#úőë‡äädüůçź8|ř0ž|ňIDDD Nť:¸pá¦OźŽ¸¸8dddŔŃŃíŰ·ÇŕÁ‘śśŚ_~ůżţú+Z´hĹ‹k7ë7hĐű÷ďÇÉ“'ńÎ;ďŔÍÍ qqqHLLÄůóç±sçNľÍËĹĹE»_—6mÚ ##ë֭áC‡°{÷n´mŰłgφJĄ‚ťťŢxăŤr㉏ŹÇĚ™3qěŘ1Ü˝{*• ŽŽŽhѢ…Ţ6+((ŔĚ™3ńź˙ü§L¬†Ę,-77·By…„„`×®]ČČČŔ7ß|ggg|÷ÝwpppŔ3Ď<#GŽŕÇÄ©S§đé§źâСCčÖ­žxâ 8::"<<‡Bvv6^|ńEś?K–,Z­FNN:věożý›6mđŕo8žžž|kÄţýű‘••…† âŹ?ţĐR¨U«žzę),[¶ 111Ľ/ëŇĄ D›6mÂ÷ߏőë×ăŰożĹ˘E‹°téR¸»»Łm۶Ćuz2;Í=ó÷÷·p$DTÝ©¤řq3""""""""""ŞR÷îÝĂîÝ»±nÝ:üř㏰±±AŹ=ŕçç‡7ß|SűÉ`DUXř˙Ů»÷¸(ëĽ˙ăď9©Y‚›r05k®)´tu×-™Ě6jµf´¤I©°ŇśXlŁŁHZ({kl® n«Ťi»`u/xJhÓ⤀‡€DËČđŔ›_m'OpÁđz>üŃ|Ż™yÍ01—3ó™ë¬BBB4aÂ-\¸Đč.&;;[łfÍRII‰ţđ‡?čŹüŁüüüŚÎjŐL&“Ö¬YĂCç…珦ŃĐĐ żýíoš;w®öíۧɓ'kţüů nÖ~żÎŐňĺËőůçźëŃG•ôźżcUUUzď˝÷ôČ#ʍşşÚŕBDGGKú˙Cpˇř hf§Nť’ĂáPLLŚüüü4qâDUUU)%%EŐŐŐÎ5p)µk×N=ôŇŇŇôĹ_ťŔElßľ]7Üp~ó›ß¨oßľ***Rjj*ŔĄ¸ąą)::ZEEEZąrĄ¶mۦľ}ű*&&FĺĺĺFçŔw$%%ięÔ©š6mšó4777(<<\W]u•u€KŤahőőőĘÍÍ•Őj•żżżóđ㉉‰ŞŞŞRnn®,‹:uędt*\Řý÷߯víÚ)--Íč­\II‰ĚfłFŚá|ŽËČČhöoÇhNß *--M[·nUßľ}eµZUQQatH’rss%IŻ˝öÚwľâĂ?Ôś9sôĆo•h @ihhPnn®l6›zôčˇĺçç+!!AÎ5ľ=ÍĹ××W÷ßż-Z¤3gÎť úüóĎełŮÔż}ňÉ'ZłfŤ¶l٢#FťĐl<<<Ł˘˘"-[¶LŮŮŮ –ŐjUeeĄŃyÚ¸×_]±±±úóź˙¬€€Ť9RfłY~řˇŢxă ………ť¸„€K,??_6›MŠPvv¶f̡ŇŇRĺĺĺÉfł©{÷îFg˘Ťzřá‡uäČ­^˝Úč­ČńăÇ•””¤ŕŕ`ýíoÓâĹ‹őńÇ+::Úč4Ă|{( %%Eożý¶‚‚‚dµZUUUet€6ŞK—.Z´h‘ĘĘĘtňäImٲEşďľűäááatŕc.‚‚Ůív…„„hčСĘÎΖĹbQqq±s-88ŘčL@W]u•˘ŁŁő /¨±±Ńč-\]]ť–.]Ş%&&jÖ¬YÚ»wŻ,‹ÜÝÝŤÎh<==e±XT^^®””mذÁ9đŮgźťĆ0\ ÂÂBŮív…††Ş˙ţZ±b…ĆŤ§śśç@ź>}ŚÎľç±ÇÓ'ź|˘ěělŁS´`ŮŮŮ>>F§´Hß ěŰ·O‹-’ĂáPďŢ˝ełŮtčĐ!Łóŕ‚€ópŕŔ%''+<<\ýúőSZZšĆŚŁśśíŰ·Ďą´dÔŤ7ި_|Ńč-ĐöíŰuĂ 7č7żůŤÂÂÂTTT¤ÔÔTůůůťĐ*|3PZZŞ (##C!!!˛ŮlŞ®®6:.„ař•••Îů*!!AAAAĘĘĘŇÁťk&“ÉčTŕśĹĹĹé˙ř‡víÚet €˘¤¤DfłY#Fڧ§§vîÜ©ŚŚ ťĐ*µoß^6›Í9°fÍ…„„hÎś9:zô¨Ńyp팀–ččŃŁÚ°a233µiÓ&ůúú*22Rńńń;v¬<<<ŚN.Ęď~÷;………iáÂ…Zľ|ąŃ9 täČ%&&jÉ’%ęÝ»·Ö¬YŁččhŁł\F‡dłŮ4uęTĄ¤¤čĹ_ÔkŻ˝¦‡~X?ü°:wî|Ηµm۶&,4—ŠŠ ťŔŤŽ€– ¦¦Fo˝ő–233őŹüCíÚµÓ-·Ü˘EEEÉÓÓÓčDŕ’JKKÓ>¨ýű÷«{÷îFçhfÇŹ×+ŻĽ˘ č˛Ë.ÓĽyó4mÚ4ą»»ťI&“IkÖ¬‘Ůl6:ĐŠđüŃ:;vLÉÉÉzůĺ—ŐŘŘ(›ÍvNC‘\ˤI“”™™it€VŽamÚÉ“'•ťť­żüĺ/Zż~˝ÜÜÜ4jÔ(EGGë¶ŰnSÇŽŤNšĚéÓ§Ő«W/Mť:U ,0:@3©««ÓňĺË5ţ|ŐÖÖ*..Nńńńňńń1: ߇9‚çŹÖĺ믿֒%K”””¤łgĎęP||Ľ.żürŁÓĐJ¸ÍíÔ©Sr8Љ‰‘źźź&Nś¨ŞŞ*Ą¤¤¨şşÚąĆ \ť———f̡W_}UµµµFçh‡CýúőÓĚ™35~üx•••Én·3`___ĹÇÇëŕÁzňÉ'µlŮ2őěŮSsćĚŃ—_~itZ†´ őőőĘÍÍ•Őj•żżż&L ňňr%&&ŞŞŞJąąą˛X,ęÔ©“Ń©@łš9s¦Îś9Ł+Vť  mßľ]7Üp˘˘˘4hĐ )55U~~~F§´y;vT||Ľ8ŕ ¸úę«5gÎ=zÔč<´` pY ĘÍÍ•ÍfSŹ=ˇüü|%$$¨˘˘ÂąĆ!Ń–uéŇE111z饗T__ot€K¬¤¤DfłY×_˝<==•——§ŚŚ ť€˙ňíˇ€ąsç~çH ŕ‡0 ŔĺäççËfł) @ĘÎÎÖŚ3TZZŞĽĽ<Ůl6uďŢÝčL Ĺ5k–8 uëÖťŕ9räl6›ú÷﯂‚edd(;;[C† 1: ?ˇś+†¸;;î© IDAT„‚‚Ůív…„„hčСĘÎΖĹbQqq±sŤoA~XďŢ˝©¤¤$ŁS\¤ăÇŹ+))IÁÁÁzóÍ7µxńbíŢ˝[ŃŃŃF§ŕ<1€źĂ0€V«°°Pv»]ˇˇˇęßżżV¬XˇqăĆ)''Ç9ЧOŁ3V!..N;wîÔÖ­[ŤNpęęę´téR…„„(11QłgĎÖž={d±Xäîînt.·‡µbĹ † ‰a­Ě”śś¬đđpőë×Oiii3fŚrrr´oß>ç€óˇáÇëĹ_4:Ŕyr8ęׯźfÎś©ńăÇ«¬¬Lv»]>>>F§ŕęرŁl6›JKKż3`łŮT]]mt Ŕ0€ݞ˛Ňů!˙ŔŔ@%$$(((HYYY:xđ sÍd2ť ´jłfÍŇşuëTZZjt €s°}űvÝpĂ ŠŠŠŇ ATTT¤ÔÔTůůůť€&ôßCkÖ¬QHHCmĂZ¤ŁGŹ*==]‘‘‘ęŮł§ěv»‚‚‚´~ýz:tHééé5j”ÜÜx™¸Tnżýv*99Ůč?ˇ¸¸XfłY×_˝<==•——§ŚŚ ť€fÄPx— @‹QSSăčÖ­›¦Oź.IZµj•Ş««ťk—®ÉÝÝ]±±±úź˙ů}ńĹFçř/GŽ‘ÍfÓ€TPP ŚŚ eggkČ!F§Ŕ@ß ”••)11Q ´ 0ÔÉ“'ĺp8d6›ĺďď/«Ő*IJKKÓáÇĺp8-OOOK¶áľűî“···RSSŤNđŽ?®¤¤$ëÍ7ßÔâĹ‹µ{÷nEGGť€¤C‡ß9RC®ŹaÍîÔ©Sr8Љ‰‘źźź&Nś¨ŞŞ*Ą¤¤¨şşÚąÖ±cGŁS6§C‡ş˙ţűµhŃ"ť:uĘč M«««ÓŇĄK˘ höěŮÚłgŹ,‹ÜÝÝŤÎ@ őCCÁÁÁ˛Ůl:tčŃy¸„Đ,ęëë•››+«Ő*M0AĺĺĺJLLTUU•rsse±XÔ©S'ŁS6Ň—_~©U«Vť´Y‡CýúőSll¬ĆŹŻŇŇRŮívůřřť€VâŰC ,řΑ p h2 ĘÍÍ•ÍfSŹ=ˇüü|%$$¨˘˘Âąćççgt*€oéŃŁ‡î¸ă˝ôŇKjll4:hS¶oß®EEEiĐ A***Rjj*Ď•¸` ¸.†\růůů˛Ůl PDD„˛łł5cĆ •––*//O6›MÝ»w7:ŔOxôŃGUPP üăF§mBqq±Ěfł®żţzyyy)//O 2: .⿇233 hĺpIČn·+$$DC‡Uvv¶,‹Š‹‹ťkÁÁÁFg8G Đ-·Ü˘_|ŃčŔĄUUUÉjµjŔ€*((PFF†˛łł5dČŁÓࢾ Ř»w/C­Ă.Xaaˇěv»BCCŐż­X±BăĆŤSNNŽs Oź>Fg¸@qqqĘÎÎÖż˙ýoŁS—süřq%%%)44T7nÔâĹ‹µ{÷nEGGť€6‚ˇ€ÖŹaçĺŔJNNVxx¸úőë§´´4Ť3F999Ú·oźs @ë7věX 8P/żü˛Ń)€Ë¨««ÓŇĄK¬¤¤$=ůä“ÚłgŹ,‹ÜÝÝŤÎ@ôSCź}ö™Ńyř řY•••Îů*!!AAAAĘĘĘŇÁťk&“ÉčT—ÍfÓęŐ«őé§źť´z‡CaaaŠŤŤUTT”JJJ/ŁÓçP@ii©s( wďŢ ´` řAGŹUzzş"##ŐłgOŮíviýúő:tčŇÓÓ5jÔ(ąąń2#ŕʦL™˘+ŻĽR‹/6:hµ¶mۦEEEiđŕÁ***RjjŞşvíjtđ=íŰ·w<ýôÓZłfŤz÷î­GyDŐŐŐFçŕ[x—€SMMŤs [·nš>}ş$iŐŞUŞ®®v®yxx\  ąxyyićĚ™zőŐWuěŘ1Łs€VĄ¸¸XfłY#FŚ···ňňň”‘‘ˇ   ŁÓ€źŐľ}{ĹĹĹ©ĽĽ\ ZąrĄ‚‚‚4{ölŽĐB0 ´q'Ož”ĂáŮl–żżż¬V«$)--M‡–ĂáPtt´<== .`”3f¨ˇˇAË—/7:hŞŞŞdµZ5`Ŕ(##CYYY2dŃiŔykßľ˝fĎž­ýű÷ëĺ—_VFF†zőę%«ŐŞŠŠ ŁóÚ4†€6čÔ©Sr8Љ‰‘źźź&Nś¨ŞŞ*Ą¤¤¨şşÚąÖ±cGŁS´—_~ąî˝÷^-\¸PgĎž5:h±jkk•””¤ĐĐPmܸQ‹/ÖîÝ»mtpŃĽĽĽd±XT^^®””mܸQÁÁÁ a Ť¨ŻŻWnn®¬V«üýý5a•——+11QUUUĘÍÍ•ĹbQ§NťŚNĐÍž=[ź~ú©Ţ|óMŁS€§®®NK—.UHH’’’ôä“OjĎž=˛X,rww7:¸¤<==e±XTVV¦eË–ióćÍ VLLŚĘĘĘŚÎhS\XCCrssełŮÔŁGEDD(??_ ި¨p®ůůůť  … TTT”žţyŁS€Ĺáp(,,L±±±ŠŠŠRII‰âăăĺăăctФ<==Ł˘˘"-[¶L۶mSßľ}ŁŇŇRŁóÚ†”źź/›Í¦€€EDD(;;[3fĚPii©ňňňdłŮÔ˝{wŁ3´2ńńńĘËËSNNŽŃ)€á¶mۦEEEiđŕÁ***RjjŞşvíjtЬ<<<ŁÂÂBĄĄĄiűöí SLLŚöîÝkt€Kcp˛Űí ŃСC•ťť-‹Ĺ˘ââbçZpp°Ń™Z±aÆéúëŻ×‹/ľht `ââb™ÍfŤ1BŢŢŢĘËËSFF†‚‚‚ŚN őÍP@AAŇŇŇ´cÇ…††Ęl6«¤¤Äč<—Ä0ĐŠĘn·+44Týű÷׊+4nÜ8ĺää8úôéct&§·ŢzKĹĹĹF§ÍŞ˛˛RV«U Paaˇ222”••Ą!C†ť´(ß iőęŐÚ˝{·ÂÂÂd6›ů·$Ŕ%Ć0ĐĘ8p@ÉÉÉ Wż~ý”––¦1cĆ(''Gűöís®@S0a‚‚µhŃ"ŁS€fQ[[«¤¤$őíŰW7nÔâĹ‹µk×.EGGť´hnnnŠŽŽVaaˇVŻ^­Ź?ţXýúő“ŮlVQQ‘Ńy.a ¨¬¬t~Č?00P RVV–<č\űë_˙Ş:Čd2)))Iőőő’¤żţőŻňňňŇ믿.I:uꔞ{î9Ýwß}şîşë4zôh}ňÉ'ÎëËËËÓđáĂ5sćL=őÔSňđđĐńăÇ ąíZwwwŮl6­X±Bźţąóô·ŢzKV«UżřĹ/TSSŁ{ď˝WW^yĄ  üü|çvÇŽS||ĽüqĹĹĹiĚ1Š‹‹SMMŤ7řQuuuZşt©BBB”””¤'ź|R{÷î•ĹbŃŰożÍăÍ"77—};ŔyËËËc_-Ę7CZ·nťJKKŐżEFFęß˙ţw“_˙Ę•+٧.ËÔŘŘŘht€ď;zô¨6lŘ ĚĚLmÚ´IľľľŠŚŚTtt´ĆŽ+Ź<߼y󔨂‚………I’>ýôSŮl6˝ůć›’$‹Ĺ˘¸¸8őéÓG’4fĚíÚµK{÷î•ŻŻŻúôéŁĎ?˙\_|ń…$iňäÉJIIQ×®]›á–héNś8ˇ«ŻľZ6›MóćÍ“ôźˇĄĐĐPŐÖÖjÁ‚úýﯜśýţ÷ż×Ż~ő+mßľ]µµµúĺ/©)S¦hţüů’¤#GŽ(<<\gĎžŐ‡~¨Ë.»ĚČ›¨±±Qk×®ŐO<ˇęŢ{ďUbbâwžyĽŁ9L&­YłFü1űv€sf2™ôꫯęŃGe_-VCCŢ~űmŮívýűß˙Ö¸qăôôÓOkČ!MvťĽ^\GZššĄ§§+22RÝşuÓôéÓ%I«V­RuuµsíÇ$iÖ¬YňőőŐÂ… ť§­\ąRÓ¦M“$}đÁZ¶l™BCCe2™d2™ôĎţSŐŐŐú׿ţ%IúňË/uôčQ-Z´HŤŤŤš7ožĽ˝˝›đ–hMÚ·o/«ŐŞW^yE'Ož”$]uŐUşęŞ«$IO<ń„®ľújÝu×]ň÷÷×G}$IzöŮgµgĎY­VçeuíÚUsçÎUyyąžyć™ćż1Ŕ·l۶MşăŽ;4xđ`)55ő{îáńŽćÄľŕ|uéŇ…}´hnnnŠŚŚT^^žÖŻ_ŻĎ>űLC‡užÖا®ŠaŔ`'Ož”ĂáŮl–żżżóŤř´´4>|X‡CŃŃŃňôô<§ËëŇĄ‹bccőúëŻ«ŞŞJ’´yófŤ;V’´sçNőďß_ŤŤŤßű7nś$éŐW_•ŻŻŻl6›† ¦ÚÚZůúú6Á­ĐZ=ôĐC:věV®\é<Íd2}o»Ë/ż\§Oź–$mٲE’ľ÷÷ä†n$mÝşµ©rźT\\,łŮ¬#FČÇÇGůůůĘČČPPPĐŹž‡Ç;š űv€ Áľ Z“ɤČČHíÜąSëׯWuuµ®»î:Ť=Z;w×Ĺ>pU 8uꔇbbbäç秉'ŞŞŞJ)))Ş®®v®uěŘń‚.öěŮňôôÔÂ… •źźŻaÆÉÝÝ]’ôĹ_¨ĽĽ\'NśřŢů$I·ß~»>úč#Ť3FyyyŠĐ믿~á7€Ëń÷÷×äÉ“ő /8˙vü7·˙Ľ-±˙ţď]–$]vŮe—´ř9•••˛Z­0`€ ĺp8”••ĄÁ_ôeóxÇĄÄľŕRc_-É7C|𲲲těŘ1 6LŁGŹÖ|pÉ®‡}*੹I}}˝rsseµZĺďďŻ &¨ĽĽ\‰‰‰ŞŞŞRnn®,‹:uętŃ×uĹWhĆŚzíµ×´hŃ"Mť:ŐąŞ'N())é;ç)**Ň+ŻĽ"Iš?ľ‚‚‚ôÎ;ďhŐŞUŞ««ÓÜąs/ş €k‰‹‹Óž={´iÓ¦sÚţ›o}űí·żsú§ź~*I5jÔĄ ~Dmm­’’’Ô·o_mÚ´I‹/Ö®]»të­·^˛ëŕńŽK‰};ŔĄĆľ ZŞQŁFiÇŽĘĘĘŇ×_­_ýęW=z´¶oß~Ń—Í>pE¦ĆĆĆFŁ#WŐĐĐ ­[·*33S«WŻÖáÇőË_ţRwß}·ĚfłşwďŢd×]]]­ž={ęúëŻ×{ď˝ç<ýôéÓ Syyą¦NťŞ[nąEEEEúŕ´víZůúúŞC‡ެ¬TçÎťuöěY]yĺ• ˝$oĽp-cÇŽŐ™3gôî»ď*00Pű÷ď×·ßzPeeĄęęęTWW§aÆ©¦¦F;wîT·nÝ$I?ü°ňňňôţűď«]»vFÝ´uuuZľ|ąžzę)ť9sFńńń˛Ůlňöö>ďËâńަf2™´fÍ™ÍfIěŰÎÍ7Ďńńńě«Ŕ%äććę©§žŇ{ď˝§‘#GęŹüŁnşé¦ ľ<ö©€«áČ@ČĎĎ—ÍfS@@€"""”ťť­3f¨´´Tyyy˛ŮlM: Iţţţ=z´¦M›öťÓ˝ĽĽôî»ďjüřńZ·nťâââtřđa­\ąRľľľ’¤'Nč–[nQRR’î˝÷^EDDhőęŐMÚ  uŠ‹‹Ó{ď˝§Ç{Lű÷ď—$-X°@ÇŽSrr˛*++%IóćÍ“ÉdҶmŰ4eĘÝsĎ=zä‘GŻ+®¸Bďľű.6B“illTff¦Â«¨¨(•””(>>ţ‚–,YÂăÍŽ};Ŕąúç?˙Éľ \Fxx¸Ţ}÷]ĺääČËËK7ß|łó´ Á>p5¸D ”™™©7ŢxCeee Stt´îĽóNőéÓ§Ů{Nś8ˇj÷îÝňńńiöëĐv ú¨¶nÝŞI“&éŮgźUPPŃYŔĎúď#°o8˙ýü¸šÜÜ\ŮívmŢĽY#GŽT||Ľ"##ĎůüěSWĂ‘€‹PXX(»Ý®ĐĐPőďß_+V¬Đ¸q㔓“Ł‚‚ŮívC$ińâĹŠŤŤĺŤMMnÖ¬YĘČČĐÁŤNśŠŠŠd6›5bÄůřř(??_  Őbßŕ?G ČÎÎVNNŽ.żürŤ?^ááár8çt~ö©€«á^Ŕy:pŕ€Ö­[§ĚĚLmٲEW]u•nżývĄĄĄiäČ‘2™L†µíرC‹E'NśP}}˝Š‹‹ kĐvÜyçťš;w®RRRôüóĎť6®˛˛R úóź˙¬ĐĐP9Ýzë­Fg¤´´Tdßŕż„‡‡+<<\[¶lŃłĎ>«ńăÇkÄš3gÎ÷ŽŔëeŔ•qdŕTVV*99Yááá TBB‚‚‚‚”••Ą:׌¤:čرcrssÓ_˙úWyzzÚ mđđđĐ>¨ÔÔT}őŐWFç ŤŞ­­URR’BCCµiÓ&-Y˛D»víb­š——űv?aäČ‘r8Ú˛e‹|}}5~üxŤ9RďĽóŽs^/®ĚÔŘŘŘhtĐ=zT6lPff¦6mÚ$___EFF*::ZcÇŽ•‡‡‡Ń‰ĐbÔÔÔčżř…ž~úiÍž=Űč´!uuuZľ|ąžzę)ť9sFńńń˛Ůlňöö6: ¸(&“IkÖ¬‘Ůl6:ĐŠđü¶nÇŽJHHĐĆŤ5lŘ0Íť;W·Ţz«á_ŕĐT82đ-555JOOWdd¤şuë¦éÓ§K’V­ZĄęęjçđ]ť;wÖ´iÓôňË/«®®Îč´ŤŤŤĘĚĚTXXbccĄ’’ĹÇÇ3´QżúŐŻôöŰo룏>RĎž=Ą*==]őőőFç\r  Í;yň¤‡ĚfłüýýeµZ%Iiii:|ř°‡˘ŁŁ9„8üڇ~X‡ŇÚµkŤN‹Űşu«"""4yňd }Úč<€ Ć0\ZCCrssełŮÔŁGEDD(??_ ި¨p®ůůůť .áşë®Sxx8GŔ%Q[[+»Ý®Ţ˝{kÓ¦MZ˛d‰víÚĄ[o˝Őč4­PPPRSSµwď^EFF*>>^}úôQrr˛Nť:etŔyc.)??_6›MŠPvv¶f̡ŇŇRĺĺĺÉfł©{÷îFg€KŠ‹‹ÓŰoż­˘˘"ŁSĐJŐŐŐiéŇĄ QJJŠćĎźŻ={öČb±ČÝÝÝč<­\Ż^˝”śś¬={ö(**JsćĚq)ŕäÉ“Fçś3†ŕ2 d·Ű˘ˇC‡*;;[‹EĹĹĹεŕŕ`Ł3ŔĺŤ?^}úôŃÂ… ŤNA+ÓŘبĚĚLőíŰW±±±şăŽ;TVV¦řřxy{{ťŔĹ\}őŐJNNÖţýű5eĘ=ţřăęŐ«—’’’tâÄ Łó~ĂhŐ e·ŰŞţýűkĹŠ7nśrrrś}úô1:Ú777=ôĐCzýő×učĐ!ŁsĐJlÝşUšůD=zô0"Đ L&­YłFfłŮčŔĄ|ýő×Z˛d‰ž{î9ť9sFS§NŐăŹ?®nÝşťÚ8Ž €Ą¦¦Féé銌ŚT·nÝ4}útIŇŞU«T]]í\cZ>???Ýu×]z饗ÔĐĐ`t VWW§ĄK—*88X)))š?ľöěŮ#‹Ĺ ڼßţö·úúëŻU[[ëü9}ú´Ž?îüďăÇŹëšk®aŔľľľŠŹŹ×”¨ŚŚ …„„Čfł©ŞŞĘč<І1 Ăť}úť 0‡‡‡bcc•––¦ššŁsp‘•™™©ľ}ű*66V“'OVYY™âăăĺíímtĐjŤ?^íŰ·˙Îiőőőşë®» *ŔĹpssSdd¤ňňň´nÝ:}öŮg:t¨"##µsçNŁó€ `çíŔJNNVxx¸úőë§´´4Ť3F999Ú·oźs €oűćŰâ—-[ft .ÂćÍ›uÝu×iňäÉ2dŠ‹‹•śś¬Îť;ť´zŢŢŢš8q˘<<<ś§ůřřhěرVŕb}3°sçN­_ż^‡Ö°aĂ4zôhíرĂč<ĐŠ1 €sRYYéü`` ¤¬¬,„Ŕ•¬YłFfłŮč —Édâ˙kŔE|ôŃGzć™g´víZ 0@sçÎe(|Ź›Ńh>őőőĘÍÍ•Őj•żżż&L ňňr%&&ŞŞŞJąąą˛X, š\\\śŢyçťI›7oÖСC5yňd 2DĹĹĹJNNfŕ'¬YłFŤŤŤüđÓd?©©©jhh0Ľ×ţ-× A”‘‘ˇŹ>úHÔťwŢ©kŻ˝Véé骯Ż7:´ ¸¸††ĺććĘfł©GŹŠP~~ľTQQá\óóó3:ІÜzë­ ŐË/żltJ›VXX(łŮ¬QŁF©K—.ĘĎĎWFF†ŤNÚĽűďżźo}€s`×®]3:§Í©¨¨ŐjŐµ×^«ňňrmŢĽYYYY4hŃiţř¶~ýú)==]%%% ×´iÓtÍ5×héŇĄ І1 ŕB d·Ű˘ˇC‡*;;[‹EĹĹĹεŕŕ`Ł3$ÝsĎ=ęÜął–,Yň˝µšš>ĚĐjkke·ŰuÍ5×čťwŢŃ’%KôÁčć›o6: p‚•ššŞ={öhôčŃš9s¦z÷î­äädť>}Úč<ĐĚÚ€‹SXX¨ŚŚ ­^˝Z%%%ęŮł§˘˘˘­đđpŁóřQ^^^š>}şRRR4gÎučĐAűöíSrr˛ŇŇŇT\\¬€€Ł3]B]]ť–/_®yóćéěŮłš?ľl6›Ľ˝˝ŤNŔÚ˝{·ęęęľwzyyąňóóżsZź>}Ô±cÇćJĐ •ššŞ'žxB/˝ô’ćĚ™Ł—^zIłgĎ–Ĺb‘ŹŹŹŃ‰ ŤŽŔů9pŕ€Ö­[§ĚĚLmٲEW]u•nżývEGGkäČ‘2™LF'pNŽ9˘ž={jćĚ™*//×ß˙ţwąąąéěŮłĘĎĎ×!CŚNlŐµvíZ=ţřăúôÓO5}út=ýôÓęÜąłŃi­šÉdŇš5kd6›ŤN€‹Âß3 u‹ŠŠŇ[o˝őłŰyyy©şşZ—]vY3T0ʧź~Ş^xAË–-S§Nť4kÖ,ĹĆĆŞ}űöF§€&äftÎMeeĄ’““®ŔŔ@%$$(((HYYY:xđ sŤA@kŃĐĐ íŰ·«WŻ^zţůçőÖ[o©ˇˇAgĎž•$>|ŘŕÂÖmóćÍ:t¨&Ož¬!C†¨¤¤DÉÉÉ ¸;ďĽóg_rww×رcÚ€_üâJNNÖţýűuď˝÷*!!A˝ző’Ýn×±cÇŚÎM„a€ěčŃŁJOOWdd¤zöě)»Ý®   ­_ż^‡RzzşFŤ%77^ć´§OźVzzşúôéŁńăÇkĎž=’¤şş:ç6îî®6*±Eúâ‹/Îi»ÂÂB™ÍfŤ5J]ştQ~~ľ222Ô«Wݦ Ä%Ňu.í}Ŕýů˙]č}Ń”÷aKřý|őŐWF'\4W¸ ÎĎřńăĺííý“Ű444čî»ďn¦"-źźźž}öYíßż_<đ€.\¨ŕŕ`ŮívŐÔÔť.1Ţ%hajjjśÝşuÓôéÓ%I«V­RuuµsÍĂĂĂŕRÎßéÓ§uÝu×éž{îQYY™$©ľľţ{Űą»»ëČ‘#Íť×bmذAÆ Ó©S§~t›ŠŠ Y­V]{íµÚ·oź6oެ¬¬, 4¨KńCŢ{ď=™L&]vŮe8p †.“É$ >\ ŹŹŹL&“}ôQýú׿ÖW\atö%3|řp=öŘcçĽý /ĽpIîúúz%%%)""ÂĄîĎ uˇ÷ëůśĎ¨ßő…:}ú´žyćŤ1˘Ő>F\á6¸píŰ·×ĉň5"ýîwżkĆ*-E×®]e·ŰUVV¦|PÉÉÉęŮł§ćĚ™Ł/żüňgĎ˙÷ż˙]6lh†Rp1¸„rrrÔŘŘxŢç;yň¤‡ĚfłüýýeµZ%Iiii:|ř°‡˘ŁŁĺééy©“hV^^^Zµj•:wî,ww÷ÝÎd2qd€˙łuëVMš4Iĺĺĺzĺ•Wľ·ţő×_Ën·ëšk®Ń;C%K–hÇŽşůć› ¨Ĺ9yň¤nşé&}öŮgÚµk—¶oß.IęŐ«—¶oß®Ź?ţX•••ęÝ»·¦M›¦‚‚ť={ÖŕęK'00đgżµřŰbccĎů>¨¨¨řŃ5wwwŮl6şÔýyˇÎç~˝Đó5ĺďş)xyyiöěŮ*))ůÁÁ´ÖŔn€‹3eĘ”ďaëŰ<<<4iŇ$ůřř4s€–äŠ+®Ýn×ÁőÄOhٲeΡ€ŁGŹţŕyőä“Oę¶ŰnÓĆŤ›ąś†.ĆĆF=ű쳺馛´uëÖs:Ď©S§äp8#???Mś8QUUUJIIQuuµs­cÇŽM\@óęׯźŢ˙}ůřřüč@ŔŮłg92€¤ÂÂBýö·żu~Pöé§źÖ_|!I:s挖.]ŞĄ¤¤hţüů*))‘Ĺb‘›oµ$'OžÔcŹ=¦öíŰ˙č6]ştŃŚ3ÔĐĐ ®]»6c]Ó[µj•Îy{//Żsşöď߯)S¦üä6ŢŢŢňóó;çëveçzż^ĚůšęwÝ”\á1â ·Ŕ…űÍo~ŁË.»ě×ęęęt×]w5s€–Ę××Wńńń:pŕ€ţřÇ?ęő×_WĎž=ełŮôŮgź}gŰőë׫¨¨HgĎžŐ„ ôÎ;ďT ~Ż\¤ŻżţZ·Ýv›ž|ňIIŇęŐ«tŰúúzĺććĘjµĘßß_&LPyyąUUUĄÜÜ\Y,uęÔ©ąň0ÄŔőĎţSžžž?řÁőúúzUUUPÖrTVVjôčŃ:qâ„óŰžOź>­?ýéOĘĚĚTßľ}őđĂëřĘĘĘ^ßČŤćó»ßýNŁGŹţŮíxŕőîÝ»ŠZżĘĘJÝzë­ Ú<Mž<ůŹ&ŮąsgŽŕ{:vě(›Í¦ŇŇR%&&*33S˝{÷–Ífsţ;üé§ź–»»»uöěYEFFęí·ß6¸ü†.ž={4tčPmذA ŞŻŻ×Ę•+ťßŢ+I ĘÍÍ•ÍfSŹ=ˇüü|%$$¨˘˘ÂąĆ79ÚšáÇËápČÝÝýÚň0ŔW_}ĄŃŁGëČ‘#ßŮŻ¨««ÓË/ż¬;ďĽSżţőݵgĎ=űěłęÜąłµř9?uŚoóňň’‡‡‡óżŹ9˘I“&éŠ+®P˙ţý•——ç\;uꔞ{î9Ýwß}şîşë4zôh}ňÉ'jhhĐ˙ţď˙jÖ¬Y TUU•nĽńFőěŮS555?zľsâÄ ­\ąRS¦LŃČ‘#µ}űv 2D˝zőŇ–-[´gĎMś8Q]»vUßľ}•źźďçűýžĎ÷sNf‰Cnn.z÷U«˘Yłf%¶Łć(°<óĚ3¸uëV‰eˇˇˇxţůç⡨ČŰEDD?đî»ďbéŇĄhذ!ß~űÍüˇtÁ`@×®]ů"""o$DDDDDDDD¤É÷ß/*J<Ö¬Y#;věJ­Zµ€ÜqÇ2zôh9zô¨§C'""ň*ß}÷ť‹N§+1źVŻ^ÝÓˇyDAA´nÝÚbŤ@BBBä±Çót €dff:ĽŤ¤¤$‹Ď%%% =z´?~\V¬X!äľűî3ż¦_ż~rđŕAó˙yä©Qن\ştI¶lŮ"*T2nÜ8YłfŤôíŰW®_żnu˝ĽĽ<‹±ŤF9zô¨ČČHY±b…ěßż_H||ĽL0A®^˝*»wď’’’Rbý“'O–9ÖiÓ¦Ipp°äääȸq〠2Ä®6°Ő†–ÚsđŕÁ˛zőjů裏$""B‚eĎž=6ŰÓZ»4jÔHDDDŻ×KTT”$''[}˝Łm™ Ą˙Ľ[|™^Ż—””éŮł§ŤF™;w®~řAu»ÚjC[ëą˛ŻµŽ÷ŇŠŠŠ¤U«VŇŻ_?ó˛?˙üSBBBJ´­­ńŕh?Ú7§Oź–Š+ ÉČČ'NČüůó€´jŐĘ®cPËůŚ<Ďh4šď?lŢĽŮÓˇ‘ąqă†L›6Mš6mj®-Š?‚‚‚¤\ąr˛fÍO‡JDDDĹčDDśőÁ"""""""˘@`0žžŽwß}:ťFٱÄ󡡡¨QŁNź>Ť¦M›˘GŹxúé§Ń¨Q#ELDDäýľúę+¤ĄĄ•WCBBpëÖ­2ßíĎ şuë†+V”řE€Ňt:věŘ-Z¸1:2ŃétČĚĚDZZšCŰHJJÂĘ<פIýôSôéÓÆ Ă!CžžŽôôt·ĹPŁF @Ýşu5·ËöíŰѬY34hĐ#GŽDĹŠ]ݦ|yäČŹĆQś3úÚ™ăöĚ™3ćmşc–8:nÔ¦-Z !!pëÖ-<ýôÓŽ|ÍĆŤ±yófŧ˙@Ŕ† ÜYĂ©0ţ|´nÝçĎźWőmꎎˇXĽx±"#""ň/ »ďľkţvv¶Łqź3f`Ě1Vßh‚ŕŕ`ó˙cbbĐ®];=zÔ]!’IJJBAAĆŹ_bů0}út§Żçl{÷îEVV&L€ożýŁFŤ*1ĆŐĐétŠoT˛ćÔ©S€Ç\s»ôęŐ z˝Ź>ú(× ÂôíΦo}‘żžr×]w222 "ćĺ'NśŔŹ?ţčŇجqF_;sÜ&%%€ů— \˝?K7jŽWĎž= 6ÄťwŢéáhČ׌3Ŕí_)W®śÍך>đ裏bÓ¦Mîʬ°ţ›>DDDDDDä–,Yâé|ZQQćÍ›‡U«Vٵž^ŻÇ’%KĐ©S'Ĺ?śxłűďż±±±žČa[·n5żń“Ľ_ăĆŤŃĄK|˙ý÷řöŰoýţ×v˛˛˛đᇂ!"ć7‡FFF˘nÝş¨[·.ęÔ©¸¸8Ô©Sćő­ć÷‡ú$??,~ó8äĺĺ®]»†J•*€ůMßůůůxňÉ'Ń A¤§§ăôéÓx衇pŕŔüúëŻX¶lŕćÍ›ćכƋšő,1m«řĚM˝~ýşů›ÍMŻ3Ť 2?_ü`ěرřá‡ЬY3;v •+WFŐŞUŃ Aó›Ä•Ú "" 8wîNť:…¸¸8«ń›ŢDĺĘDGG&OžŚ'ź|˝{÷Faaˇ¦v9wîňňň°zőj\Ľxąąą€_ýµk×¶8Niˤ¤$8pcĆŚAŻ^˝°|ůr~úé'Ľůć›X°`–.]ŠśśtëÖ çĎźGvv6fÍšĄş]-Qłž«úZëx·dذaČĚĚÄ#PŻ^=´oßYYY8{ö,ŕřńăvíOK?*ŤÓ:Ĺ]»v ŔíëS5Çoµ Č˝Nź>Ť-[¶x: QQQ€{îą'ŕjDň¬¸¸8Üwß}žĂe!ź‹Ú¶m‹ÄÄDäää ''ŮŮٸpá._ľ\˘F Fpp0 nܸ‡z#GŽDbb˘Ź€Č1iiižH;!""""""ż€>řŕCó#33ÓÓiŚČ)RSS=žSůŕç<Ľˇ>q$Žź~úIţůĎšŹç•W^‘őë׋Ńh” &ź|8ćĎźď©ĐČÉŘ×DDDDDDDD®Ç‘[Ě›7 Ŕś9s‹6mÚ -- »víÂüůóqÇwx:Drö5‘ë…x:""""""""""""" 111:u*¦NťęéPČĹŘ×DDDDDDDD®Ç_ """"""""""""""""""""""ň1ü0‘Źá‡Čeňňň<‚ĂüáĽÍŐ«W=‘W`ťá»˛łł±téRŚ;Ö-űóÖúÉ[ă žCÜ}:*Đű‹ČćGĎŇëőŘĽył§ĂpŽ7 4óäkxźH~€ĚFމňĺËC§Óá‘GÁ¦M›pćĚüß˙ýt:t:şuë†őë×›×Yż~=Z·nŤŕŕ` 8EEEřŕđŔ J•*n‹˝uëÖxóÍ7ť¶=O?+,,Äرcq˙ý÷łMÝ ¨¨“&MÂ!CđěłĎ˘}űöX¶l™§Ă""ň)›6mÂ[o˝e®^xá|˙ý÷NŮ6ë ű9»ÖsÄÁ‘žžŽ´´4|ůĺ—.ŰŹ·ÖOZăb}â<Ě!®9]•gěé/oĘuŢĘY÷-ÔRŰ'ŇZ‡ IDATě;r–5kÖ sçÎćńܡCtčĐ÷Ţ{/ž|ňIĚ™3·nÝňtNÁůĚł®\ą‚#F ::m۶őt8fŢ0ÖéžĂűD·Y»ĎŔüDD¤? @DDDDDDfcĆŚÁoĽ¸ë®»Đ¶m[Ô©S3fĚ@×®]]»vEJJŠyť””ôéÓ˝zőÂÔ©S‚`ßľ}výÝšÓ§O«z]ýúőî´}9ó ĂoĽC‡Á`08uŰjÇŁë8›Ú´ÄšžžŽŽ;bâĉX¸p!ŇŇŇĐ˝{wLś8Ńîm޶mŰbܸq¨WŻ`öěŮčŇĄ‹ćíąłÎđ†yN-wÖzÎ’””ä–9Ő•ő“#´ĆĹúÄ1ĽV)Éçˇ3óŚÖţröu­·Ó«łî[¨e©O,ĹíMóů¶‡~ź~ú)€ŰăęçźĆĎ?˙Ś_ýýúőøqăśśŚýű÷{8RÇq>łÍŐů<::cÇŽEDD„K÷c/oŹÝ—·++ëtĎá}÷pg¬ľÔ.ŢÄÚ}ć'""őřa""""""*ačС¨Pˇ/^ ŁŃh^>jÔ(ŔĽyóʬłeËĽňĘ+ć˙‡……ˇZµjÇrüřq<ű쳪^»hŃ"¤§§;m_Î:úźđđpTŻ^ݩ۴gŚ8˛Žł©ŤAk¬sçÎEvv¶ů˙˝ző,]şÔîmşňĺË—řW wÖŢ0Ď©ĺÎZĎŮÂÂÂܲWÔOΠ%.Ö'ÚńZĹ2gź‡ÎĘ3Žô—łŻk˝™#±:ăľ…ZĄűÄZÜŢ6O‘o«]»6€’yN§ÓáńÇÇĆŤqýúutéŇ7oŢôTNÁůĚ:wćó·ěG-oŹÝ—7ł+ëtĎă}×qg¬ľÔ.ŢČŇ}ć'""őřa""""""*!::]»vĹéÓ§ńÓO?™—7oŢ111X»v-Ž;f^^PP€={ö U«VNŤăĚ™3xüńÇqńâE§n×Óű"çŃŇoŢĐ×jcp$VŁŃoľůĆü˙K—.âââěŢ9†5Ťeľ+9ëmx®řOö—/ŤGcőÔ} _jcň_µjŐ»ヒ?˙ü“ߊë§kÇk0ˬĹĘ:Ý÷qĚ[Ćvń}ĚODDęńĂDDDDDDTĆ /Ľćź%€uëÖ!"""Rbů˛eËlţtíĹ‹‘ššŠ*UŞ 99;vě0?wäČtďŢÇGŻ^˝Đľ}{üńÇ€Ď?˙űöíĂůóçńꫯZÝľŃhÄŇĄKŃ»wo<đŔćĺ;vě@ëÖ­Ńżüűß˙Fhh(ňóó-nCi_¶ŽáćÍ›x˙ý÷Ń·o_Ü{ď˝čر#öîÝk5ެ¬, :őë×Ç… ĚŰmÖ¬ľţúkŐ۵öĽŃhÄ/żü‚×_őë×ÇŮłg‘’’‚zőę!77·LŰŮz­R ¶únܸ!C†ŕĺ—_ƨQŁ0bÄ«}`‹µľ´ÖoZĆ•˝ýh+.Ąí©ŰÖ^———‡ýë_xë­·0dČtęÔ C† )Ńż?ýôŢzë-ó˙ż˙ţ{„„„ż)“ă­u†–ąĂZ=‡ďľűNSťđý÷ßăĺ—_F\\rssŃ»woT­ZÍš5ĂÎť;Uµ‹‰ĄZOÍö-ůä“OťN¸ví&MšTb™ÚZÍ5s´+ę§‚‚,X°Ď>ű,Ú´i¬¬,´hŃńńńŘĽy3>Ś®]»˘ZµjhҤI™6R·š¸”Ć+ëmÉ!¶Ć›Öó°žťuŽi=,X€čt:Ś?°páB„……Yü¶xŔąyFkąňşÖ—ç[śqßÂÖ9b©O,Ĺíčř™>}:zöě‰×^{ áááĐétć‘5©©©ĆŞU«ĚËÔĚ祩͛JŰV“˙•hťĎ–-[†*UŞ@§Ó•¨)fÍš…ŕŕ`|ňÉ'´Ý÷qtŐ:/•Î5J÷ď”ęËüü|Ś3={öÄ A’’‚)S¦X=î‰'"<<C‡ĹćÍ›m¶źRlöĚcŢ0Ű:V{÷ĺKó/ëtďÄűî­9mĺR5óś#÷ňKsôß×Js  \Ç*Ť-5÷źě DDDDDDä×Hff¦]ë ‰ŤŤ•ĐĐP9ţĽ<ű쳲~ýz©X±˘Ô¬YSôz˝<đŔräČ‘2ŰHJJ2zôh9~ü¸¬X±BČ}÷Ýg~MŁFŤ$!!ADDôz˝DEEIrrr‰Ř“’’ă=yňd™×6nÜXbbbĚ˙ďŃŁ‡dgg[݆Ą}©9†~ýúÉÁÍ˙ä‘G¤FŤ’——WfA–/_.ĺË—2`Ŕٰa,\¸P*UŞ$dóćÍŞ¶kíůK—.É–-[¤B… @ĆŤ'kÖ¬‘ľ}űĘőë×KÄSXXhóµJ1Ř꿢˘"iŐŞ•ôë×ĎĽţźţ)!!!bďí[}i©ß´Ś+{úQM\JŰS;¶KżîÚµkҸqcyçťwĚ˲łłĄqăĆŇ AÉÍÍ-łŤ[·nIÆ eţüůŠűł´{ó‘·JMM•ÔÔT»×3ŐĹycťam_JsGvv¶Ĺz WŻ^˛zőjMuÂéÓ§ĄbĹŠ@222äĉ2ţ| ­Zµ˛Ů.–”®őÔnß’„„„2ýiZfO­V:~µs´+ę'ŁŃ(GŹ)+V¬ýű÷ ‰ŹŹ— &ČŐ«We÷îÝ@RRRĚ몉[m\öŚW¨O܇ÖbkĽ9rŮĘŽžc7nÔ|ŠŚ9RČľ}űĚËNž<)]»vµyLÎĚ3ZűË×µţ0XăŚűJ׎–úÄRÜZÇĎ´iÓ$88XrrrDDdܸq@† ˘ą]´đ–ĽęŻ233íľ"˘|ŽÔŞUKŞT©""Úî(ĺMµŰ¶•˙mqĆýĂiÓ¦ ůńÇKĂłĎ>kţż–şÚó¨Öy©x˙+Ýżł§^Ż—””éŮł§ŤF™;w®~řˇD\ľ|Yzöě){öě)ŹÖűöÎcŢ0ú5Ö:]ëő¶/ŃšĎŐŕ}ĎלJsŤšyÎ÷ňE»ĆW:µí¦fîUŞcmőź–űŽÜGPâĘó›Č]ĹüśÖ?ę>\ČřńăĺňĺËrĎ=÷Hź>}€|ýő×rôčQą˙ţű-®oşŃlúC“H•*U¤B… ć˙Oš4I-Z$"·op&$$Hhhh‰ŘŐľ9 ôk«U«&dĘ”)b4eďŢ˝vÝŔVs ۶mË—/·şŻĆŤ ÉĎĎ7/űđĂ€<ýôÓŠŰUłßÄÄD —/_Vl;KŻUł[ý7}út °xěö°Ő—–úÍŢqĄµ­ĹĄf{jÇvé×˝ýöŰ@Îť;Wâu_|ń…7ß|łĚ6fĎž-“'OVÜ—µýóM!Dä/śůao®3´ÎÖj­u‚i˝âjÔ¨!aaa6ŰEíq©Ůľ%–úłô2ĄZÍRLjŰŮ•őSé6ŞS§N™őŞWŻ.QQQć˙«‰[M\öŽW¨O܇–"˘\k=Źlĺ@gťcZÎC‘śś©T©R‰7•Ś7ÎfŢ´¶-­íٵż,­ëč|ă/s€5ŽŢ·P:G,Ĺi-n-ă§K—.$·nÝ‘˝{÷ iÝşµ=Íŕ0oÉ«ţĘU‹‹“Úµk‹¶{&JySí¶ŐäKś1źÝşuKęÖ­+]şt1/5j”ěŢ˝[D´×ŐÎGµÎK–úßZ®¶ç¤I“€:tČüú˘˘"™;w®\ąrEDţ×ÇŽ“>}úČĹ‹Klß‘űöÎc–ŽÝťó1ŻÁ´×éü0€cxßÁó5§RÎW3ĎimkJoOÍ5ľšcQÓnJŰPŞc•úOËýGî#(á‡ČČ‚^xđé§źbţüůxúé§}űö|üńÇřüóĎńÜsĎŮÜNńźŻV­ Ě˙ýő×ńÄO`ćĚ™ČČČ@aa!ôz˝Sâź5k*UŞ„AˇeË–¸~ý:*Uޤi[ÖŽaűöíHNN†Üţ°}‰ÇcŹ=fu{AA·/Ç+T¨`^ÖĄK·~Ui»jökŠ9::Zőń­š}Řę?ÓOŐÇÇÇ[8zß•÷$Ôčßż?>ýôSôéÓÆ Ă!CžžŽôôt·ĆAľççźĆ­[·đĐCpü\·•7]‘GěĄć\íŰ·/"""0}út|űí·HMM5?§5o9+G¸z^˛ç… Řţ ťÉ|€ĚĚLŚ?ľÄrGňľ;®›ś)ĐŻÁX§{ŢwpoÍ銺ĐSs¨3ŽEiJu¬R˙Ů{˙ů‰H? @DDDDDDV™ľeďŢ{ďE­ZµĚËűôéŁŃżýíoeľ-Ę˝ző‚^Ż7V雸:ťEEEš¶=zôh4hĐ+W®Ä˘E‹ ×ë1räH«Ż×˛Ż¤¤$”ů#Ů0}út»¶eşéůđĂ+n×™űµFÍ>lő_RRżĆ¶úŇRżŮ;®´¶§µ¸ÔlOíx+ý:Ó·•n×S§N¸=~Š+_ľ<žyćĹý‘}ĽąÎptîpelJ±şéđnÝş\˝zUq˝âµš%jŰŮ]ő“ZjâV—=c‚ő‰ý´ž+Ju±VJő9ŕüsLík J•*xőŐW1{ölLť:/ľř˘ňA9‘3s›ŁóŤ/ÎÁ®xąoˇĺqf˙ ěÝ»YYY0aľýö[Ś5Ęg?ĽBîqëÖ-Ś1wß}7ŔńsÝVŢT»m­ů_ 5çjĺʕѷo_|öŮgČĚĚD×®]ÍĎiÍ[ΚGµĚKöä[qŢu×]€ŚŚ yů‰'đăŹ?–ŘNçÎť1bÄŚ1˘ÄsŽä}{ç1gâ5şX‹cťî]xßÁ˝÷ÔüM°=ĎąŁ]ÔpĆüĄ´ Ą:VÍ߸ő÷?źT""""""ňk$33SÓş999R®\ą2ë_ĽxQĘ•+'K–,±şníÚµ€äĺĺ™—ŐŞUKČőë×ED$22Rt:ť¬ZµJ,X Ő«W˛mŰ69uę”4lŘP"""ääÉ“6ăĽvíšÚµk›—U¨PA®\ą"""z˝^"##ĄU«VV·ai_JÇpóćMiĐ _|Q,X #GŽ”Gy¤Ä:Ą%%% )**2/›7ožÜsĎ=˘×ë·«fżńńń%ÚÚKŻUł[ý·|ůr ‘*UŞČĘ•+Ą  @~ţůg©\ą˛żţúK1.[}i©ßěWZűŃZ\j¶§vl—~]AA$''Kll¬ś;wÎüşAI›6mDŻ×—XŔ€ŇąsgUíl‰#ůČۤ¦¦JjjŞÝëŐ­[WH~~ľy™7×ZçkµÖ:Á´^quęÔć}:Rë©Ůľ%]»v2jÔ(9räLž±·Öń“žž. 2gÎYąrĄlٲE>\bÜ»·äU•™™Yf,(±–ßvíÚ%íŰ·—úőëËţýűKĽŢž{–XË›j·­&˙[âŚůĚ䯿ţ’ŕŕ`3fL‰}h­«ť9ŹÚ;/YĘ5Örµ­87lŘ @:tč 3fĚQŁFÉË/ż,FŁQDDęׯ/Äh4JQQ‘tčĐA˘˘˘d÷îÝŞŰĎZlöÎcžžŹýĚ‘:]ëő¶/Ń’ĎŐâ}ĎßwPĘůjć9Gďĺ§ő_ͱ¨i7Ąm(Ő±Jý§ć>CqŽŢGPâĘó›Č]‚ßyçťw@DDDDDD~ë?˙ůşwM›Ú˝nůňĺqňäIĽţúë 1/ŻPˇNž<‰Á—XÜţF”‰'â믿pűç;۵k‡)S¦ŕ›oľ"%%ŃŃŃŘ´iöěŮçž{ 4Ŕ¶mŰpňäI¤¦¦âęŐ«8pŕš7oŽ&MšXڱ  Ř´i®]»†ČČH$''#==«WŻFnn.>ţřcÔ¨Qź}ö˘˘˘,nçüůóć}%%%©:†‡~ÝşuñcǰjŐ*¬]»±±±1c˘ŁŁ­¶ëŚ3péŇ%T®\‰‰‰ČĎĎÇĆŤ1kÖ,„‡‡#$$O=ő”ŐíÚz>,, ď˝÷ž9ÎK—.!..5kÖ´ŘvÖ^«pű[׬ő_˙ţýŃąsgüţűď>}:ćÍ›‡š5kâÚµkřűß˙ŽÚµkŁ^˝zćoÔ±eäČ‘Vű˛xż™Ć­¸,Ť+5ÇjO\U«VUÜžĄ¸-)ýşĐĐPôęŐ W®\ÁěŮłńűďżcíÚµŠŠÂ'ź|‚ĐĐĐëóÍ7¸xń˘ůŰ2íĺHţ "ň6K—.¤ĄĄ©zý¦M›0kÖ,󷔝8q!!!HLL´97YâÎ:ĂŢąCŻ×[¬*W®¬ąN9s&.\ EóćÍ1{ölsčőz´mŰ—.]ŇTëmذË–-SÜ~é:Z´h_ýß}÷ţřă <[·nĹ<€şuë"11łg϶Y«ýő×_3f ¶mۆÜÜ\DGG#99/˝ô’âíŠú);;éééŘşu+®_żŽűî»GŹĹĚ™3a4‘źźŹV­ZaÎś9XĽx1 bĹŠHJJBĺĘ•k‹š5kâÁ´WBBşvíŞjĽúK}âÎ8´äĄë­ěělóëí=Źlĺ@gśc€ň5“Ąó0))ÉĽ~ĹŠ±mŰ6ôěŮwŢy§ÍöuvžŃŇ_-[¶Ä{ď˝çÔëZ_śvî܉ݻwŁ_ż~¨\ą˛Í~3ŃrßÂÄVNîÜą3&MšT¦O._ľ\"nGĆŹ`ńâĹX¸p!ćĎźŹ9sć`Ú´i5kl^+:“·äUµoß>,[¶ jß–±yófŚ;»wďFnn.~ůĺ,^ĽK—.ĹćÍ›ńÄO࣏>BíÚµÍëŘ{ŻŔkySí¶Őä˙âżzáĚű‡ĺË—DEEáĘ•+2dyÍ÷}lĺ{çQ{ć% d>ŻWŻžÍ{}¶âěׯzôčcÇŽaçÎťČĘĘBÆ 1věXܸq'N4o7,, 6Ddd$-Z„E‹A§Óáî»ď6oĂŢűö\7yĂ|č×`ŽÔéö^oű"{óąĽďŕ÷ĺż)´iÓFqž»xń˘C÷ňMąĆŻPˇ‚S毛íbłŽmÖ¬™ÍţSsźˇřýGď#(qĹůMDän:‘bżFDDDDDD~G§Ó!33ÓŻoÄűš&MšŕŕÁŕ%9y;ć"ň'Ý»wđż7)YĂZÍ»yK}â-qř"5çŁçaAAîşë.ěŮł§ÄB‰ĽÉÜąsqéŇ% 6 `4qöěY¬[·C‡Ĺ… Üó™k-Y˛=zôđúş‚yӵؾäďázŰWň9‘;xKë,<ż‰ČXţ""""""""Só«Dbb˘˘!"""""1cĆ 0€o¸$Ż5~üx >999ćeAAAŤŤE۶mQ§NFGyӵؾDDä/XÇy'~€ČÍňóóÍ˙FDDx8Ďá·¬‘7b­FäZjÎ1-çá¶mŰđŇK/ˇ  tNŔD.°iÓ&ŔěŮłńňË/ŁJ•*€]»vaüřń?ľ'ĂŁÁĽéZl_""ňG¬c‰ĽS§ """""" ůůůxűí·qęÔ)ŔŔ‘••ĺá¨`­FäjjÎ1GÎĂäĺĺ!(( .Dąrĺś{DN4oŢ< 0sćĚAll,Ú´i´´4ěÚµ óçĎÇwÜáé)0oşŰ—üëX""ď¤~ ‘_ÓétČĚĚDZZš§C!"ĂüADţ¤{÷K—z8"r„·Ô'Ţ‘ŁĎ\kÉ’%čŃŁ‘üZ \o3źů/žßDäřËDDDDDDDDDDDDDDDDDDDDDDD>† """"""""""""""""""""""ň1ü0yĚŐ«W=‘*ŮŮŮXşt)ĆŽë–ýůJťä+q’vîęcKç»Ď;[ňňň\ľ o:^˛Ěă@Ż×cóćÍN¦,ćd"ĎsFžĐŞxđ—9Ĺ•9“|k_"˘˙á‡Č/9r'Nôt޵nÝoľů¦¦u§M›†áÇŁC‡hßľ=>ěäč\«°°cÇŽĹý÷ߏ*UŞx:»  >gÎśńt(DDäfľVks}ÚĄëă_˙úżţúËî}‘o˛Tkh™s<űşzÎő6IIInyă†+ë$gŇ'ë(ß⎱XüšČŇ9ć®óΖŕŕ` 4ű÷ďGQQ‘K·á Çk‰/çng0`öíۧy@tt4ĆŽ‹'Fć;sž@«ˇť‘'´°”Ü1§¸ş/]•3íU|[«gYç’’@ˇ®Ú¦/µ™%ľż·°4ď1ńĂDDDDDDäă8€^˝zaÚ´i őt8Ş-Z´ééévŻ7kÖ,Ô©SÁÁÁŚŚÄW_}…{ď˝×şVxx8ŞWŻîÔm?~Ď>ű¬Ë×‰ŽŽĆčŃŁŃĄKäçç۵.ůKµ†–ůĂS\«»ć\oć–ý¸˘Nr-q˛Žň-®‹ĄŻ‰,ťcî:ďlqF;¨Ý†7oqţ»ť%,, ŐŞUsʶbbbś˛ťâ|eî Ŕ5´3ó„˝,ĺWÎ)îěKWäLµ,Ťckő,ë\˛&óˇ+¶éKmf‰ŻÇďm,Í{ĚĂDčřa""""""ňY"‚çź˙üç?=ú‡!w:uęt:ť§Ăđ:gÎśÁăŹ?Ž‹/şt“;ďĽ 6lÝë‘ď°Tk82¸›+bu÷śKţ‡u‘o`î&"­XCű·@i[÷ť­Őł¬s©4ćCçlÓ—ÚĚ_Źß—0Q ă‡Čg}˙ý÷صk}ôQ󲬬, :őë×Ç… ššŠ*UŞ YłfřúëŻÍŻ»yó&Ţ˙}ôíŰ÷Ţ{/:vě˝{÷*>o4ńË/żŕő×_GýúőqöěY¤¤¤ ^˝zČÍÍUŚŮh4béŇĄčÝ»7xŕóqĽüňË‹‹Cnn.z÷U«˘YłfŘąs'`ĹŠxőŐW‘źźŹóçĎăŐW_5˙?//˙ú׿đÖ[oaČ!čÔ©† R"žuëÖ!..6l°¸L鸔ÚëČ‘#čŢ˝;†Ž^˝zˇ}űöřăŹ?ĚĎ߸qC† ÁË/żŚQŁFaÄšľťeÇŽhÝş5„ëÉF IDATú÷ďŹ˙űß E~~>>˙üsěŰ·ĎÜ6jⲶŽŇ±štęÔ ź|ň Ž;f÷q‘o°TkX›?lÍÇÖćٸ¸8|÷Ýwšć_5őµXŐpőśűÉ'ź ((Čü!Çk×®aҤI%–ŮŠP®oÔîGmýh‰š:ĚuRAA,X€gź}mÚ´AVVZ´hřřxlŢĽ‡F×®]Q­Z54iŇÄ}űövďŢŤÚµkăăŹ?¶xMäĄsŘŃ6.îčŃŁčŇĄ bbbвeK¬_żŢüśŇą®fĄ-X°Đét?~< `áÂ… ĂĽyól¶ÍôéÓŃłgOĽöÚk‡N§3?Űç¬Ú9DmŚÎË–8óČĎĎÇ1cĐłgO 4)))2eJ™}^ĽxŃ<Î’““±cÇósöŚ'“‰'"<<C‡ĹćÍ›űpŢ56‘+r mb+OŘš/\9gĘąEM}]ş}”ćYĄůQmî5±'g*ĹfëZĂŇ8.ÎZ=u.YČůĐ_î)(Ŧ&-}®–Ł÷ś1'©ąräş°oŢc&˘€%DDDDDDä×Hff¦§Ăp‰gžyFt:ťčőz1 ˛|ůr)_ľĽȆ dáÂ…R©R% ›7o‘~ýúÉÁÍŰzä‘G¤FŤ’——góůK—.É–-[¤B… @ĆŤ'kÖ¬‘ľ}űĘőë×UĹ}ňäI III""rúôi©X±˘ŚŚ 9qâ„Ěź?_H«V­J¬[|=‘k×®IăĆŤĺťwŢ1/ËÎΖƍK $77WDDľűî;©Pˇ‚üđĂć×_VXXhó¸”Ú«QŁF’ ""z˝^˘˘˘$99YDDŠŠŠ¤U«VŇŻ_?óúţů§„„„˝·&7n,111ć˙÷čŃC˛łł-¶ŤR\ÖÖQ:V“Ý»w›ŰĘ_ůsţ ˘Ŕ“šš*©©©v­SşÖ0±w>ÎÎζ8ĎöęŐKVŻ^­iţU[?XšëÔpÇś›P¦(˝ĚVJőŤšýŘS?–>µu;ę$ŁŃ(GŹ)+V¬ýű÷ ‰ŹŹ— &ČŐ«WÍőKJJŠy]5ǡ6NwÔQŢRźŘ‡µq`«?ţĽŞ1¦t.©ÉŽŚĹ×^{MÂĂĂĺęŐ«""răĆ ©Qن<˙üóć×IűöíÍ˙/}Md˘v™‰šsxÝşu·qRR’ÁËęի壏>’ –={örTł KÇ;räH űöí+Ń~]»vµŐ-2mÚ4 –śś7nś!C†_ŁtÎŞťC”bT›/ŐĚ –8ăĐëő’’’"={öŁŃ(""sçÎć9ĹÔ‡ŁGŹ–ăÇŹËŠ+€Üwß}v§i;""—/_–ž={–"¶űĆ™×Ř"Ţ“WýUff¦¦~ńe\C+ĺ Űó…łçěŇǡ”÷ŐÖ×Ĺ·«tOŃÖńÚ“{EěĎ™J÷qm]kXÇ&ÖęŮ@¸_h‰–ëm_Ł%źr>ô§{ J±©‰Cëßu”8r/@éŘÔŽĄöqôşÄŢÚWKÄzŤüł‘źóç?ęĆÇÇKTTT™ĺŤ7’źźo^öᇠyúé§e۶mŔâcůňĺŠĎ‹$&& ą|ů˛¦ŘKßü5mݏ5jHXXÍőŢ~űm çÎť+ńş/ľřBČ›oľi^VTTT&ŽŇË,—šö4i’,Z´HDn߀NHHĐĐP™>}ş”Ř—©źěQ­Z5 S¦LŁŃ({÷îµůF[qYZGͱšś={VHçÎťí:_âĎůŹ–7'X«5´ÎÇÖę­óŻšúAëî]=犔|cʵe¶âQWߨُRýhé8Ôö»»ę¤Ňń‰Ô©S§ĚvŞWŻ^b\«95qş«Žň–úÄŢ8”ÎKy@íS3Ć•ň…#cŃôf“™3gš—uéŇE*V¬(×®]‘ďż˙^>účŁëYĘ j—•¦ćvFĎ?S¦Lň /r«Ů†ĄăÍÉÉ‘J•*•xó˸qăĘśWĄuéŇE‚‚‚äÖ­[""˛wď^ ­[·uç¬Ú9D)FgŽeKśqLš4IȡC‡ĚĎÉÜąsĺĘ•+%¶izĂŞH•*U¤B… šŽóرcҧOąxńb‰×+őŤ+ćoČ«ţ*ß\Č5´RžQ_X‹ĎžPü8ÔÖjjękKíc­źlŻ=ąWkδ›­k kăŘÄZ=÷ -á‡, ä|čO÷ÔÖů¶âĐÚçj•Ţľš{jŽMÍQÚ†Ł×%öÖľZňp ÖkDä‚@DDDDDDäŁÎź?Źččč2Ë‚n_îV¨PÁĽ¬K—.n˙déöíŰ‘śś ąý!ůŹÇ{Lńyćź/µ´-Š˙t­Itt4 m®gú9ěJ•*•XŢľ}{Ŕ–-[Ě˂ˬ_z™ĄăRÓŻżţ:žxâ Ěś9(,,„^ݬZµ _b_¦~˛Ç¬YłP©R% 4-[¶Äőë×Ë{q¶â˛DͱšDEE.\¸`÷q‘o°Vk”¦v>¶V?hťµÖj¸zÎuVję5”ęGKÔö»»ę$K,őYLL rssí:5q˛Ž˛Mé±”ě©ő•(ĺ GĆb“&MСC|ôŃG€'NŔ`0ŕÖ­[X´hŕ‹/ľŔóĎ?Ż:^{©9‡ťŃĆĹ_÷ÔSOöďß@}´µ Kbbb0`ŔĚ›7gĎž¬]»Ź>ú¨Őu cÇŽ0ŤX±b <<СCöťłJ”btćXÖJéXż~= 66Öü|pp0z÷îmÎY–¶U­Z5°˙8{ě1äççŁjŐŞ%–+őŤ«ç"Gr ]:6 džÔ×ÖâÓšÔć}­őµµ~˛uĽöä^­9ÓVl¶®5”Ʊµz6ë\˛.óˇ?ÝSpEl®®ŹŐÜ ź“ÔlĂŃë{ç=ća" TĽ#@DDDDDD>+88AŐkk×® ‹‹CNNŽ;Vâq&FŁQńyobşáyüřńËkÔ¨ŚŚtxjÚcűöíhÖ¬4h€‘#G˘bĹŠćל9sĆĽGuëÖ żýö:uę„;v ]»v7ožŐ×ŰŠË{úŢŇŤp""ň/jk WĚÇž®G\=çş*g*^?Z˘¶ßÝU'iĄć8ÔÄÉ:Ę6-ç;j}GÇb˙ţýńűďżcűöí?~<Ţ˙}üă˙Ŕ'ź|‚ýű÷#>>ľÄőÝAékcÓkęÖ­ @[—ކ5oĽńĘ•+‡?ü;wîDË–-ßŮż|úé§čÓ§† †!C† ==éééś?ĎŘŠŃťcY+Ó›…¬}L {Źó>@ff&ĆŹ_bąRßxĂÜAdK ×Đj8Z7kÍžj[ÇkOîŐš3m±u­ˇ4Ž­ŐłXç’uśýéž‚+bó–úŘǦ´ GŻKěť÷‡‰(PńĂDDDDDDäłjŐŞUć›L¬1Ý(|řᇑ””„‚‚‚2<:pŕ¦Oź®řĽ71}SŚé[ULNť:ŕöńšXúĂš?F¨iŹ^˝zAŻ×›żů±ř’’’,ƨĹčŃŁŃ A¬\ą‹-‚^ŻÇČ‘#ÜľÉ[TTTâő¶â˛´Ž=}ĺĘ@Íš5>.""ňNÖjŤŇó‡=ó±ZÎŞG,ÍŹj¸zÎ5-€[·nDWŻ^U ®ľQłKŠ×Ź–¨íwwŐIZ©95q˛Ž˛Mé±DíÓ:Ć‹st,véŇqqqxçťwźźŹ;Ľň ¶oߎ×^{ Żľúަí:BéËߦ×<ţřă´őqémXSĄJĽúę«={6¦NťŠ_|QqŰ{÷îEVV&L€ożýŁFŤ2żA_Í9kĎb+FWŹegśwÝu ##"b^~âÄ üř㏪¶aďxęÜą3FŚ#F”؇š{–öCä-ą†VCË|QśÖ ¶mÔÖ×jŰÇÖńÚ“{µćL[l]k(Ýw¶VĎbťKÖr>ô§{ jbSŠĂ}®…Łs’šm8z]bďĽÇǬ-łDÍ9ěH7iŇDČĺË—ÍŻyíµ×äÉ'ź4˙_©ŹŐlĂÖńž?^ÂÂÂ$%%Ĺf[¤§§KBB‚Ě™3GV®\)[¶l‘Ç›ŰHÍ9kďb-FgŽeKśq;vL"""€tčĐAfĚ!ŁFŤ’—_~YŚFŁÔ®][”ČiµjŐ2Ź+µÇYż~} FŁQŠŠŠ¤C‡%»wďVŐ7Î8_‹ó–ĽęŻ233ËŚ=Č5´RžQ_X‹Om(=§¨iµőµĄö±ÖO¶ŽwÆ Šą×Ńśi+6[×ÖƱ‰µz6îZ˘ĺzŰ×hÉçśýéž‚RljâĐÚçjh˝ ćŘÔŚĄm8z]boí«%b˝FDţ'řťwŢyDDDDDDä·ţóź˙ {÷îhÚ´©§Cqş¨¨(Ěť;>ř ĚËgĚK—.ˇrĺĘHLLD~~>6nÜYłf!<<!!!xę©§pěŘ1¬Zµ k×®Ell,fĚččh›Ď‡……á˝÷ŢĂ7ß|¸téâââTËHAA222°iÓ&\»v ‘‘‘ذa–-[ EóćÍ1{öl,]ş ×ë…>ř;věŔŐ«WˇÓéŤŘŘXôęŐ W®\ÁěŮłńűďżcíÚµŠŠÂ'ź|‚ĐĐPŔÉ“'ńăŹ?"-- őë×/ł¬FŤVŹK©˝ rĺĘŘ´iöěŮçž{ 4Ŕ¶mŰpňäIôďßť;wĆďż˙ŽéÓ§cŢĽy¨Ył&®]»†ż˙ýď¨]»6ęŐ«§ęç[GŽ‰Ő«W#77ü1jÔ¨Ď>ű QQQ8ţ<8€ćÍ›ŁI“&ŠqĄ¦¦âęŐ«%ÖQs¬&ß|ó V®\‰Ůłg—yÎ_řsţ ˘ŔcšWÓŇŇTŻc­Ö(=焆†ÚśŹőz˝Ĺy¶rĺĘšçß™3gbáÂ…¬×m۶ťK—ĘĚŹj¸z΀-Zŕ×_Ĺwß}‡?ţřĆÖ­[ńŔ nÝşHLLÄčŃŁ­ĆˇTß–©ŮĎěŮłmÖŹýőĆŚm۶!77ŃŃŃHNNĆK/˝¤X‡ąŁNĘÎÎFzz:¶nÝŠëׯăľűîĂŃŁG1sćLŤFäççŁU«V3g/^ ¨X±"’’’PąreĹz˛fÍšxđÁmĆ™€®]»şĽŽň–úÄŢ8¬ŤâčŃŁXąr%€’y@)·ĆŇ_»v-233XĎÝşuCÇŽ‹M›6EQQžţyó˛Š+â˙řbccÍË,]EDD`„ %α°°0|đÁ%–%%%!<<ĽĚľm]ŤF«ąVm'&&"77_~ů%¶nÝŠ5kÖ I“&0a‚ąM”ň`óćÍmnĂRž)~Ľ+VĶmŰĐłgOÜy睊cNŻ×cńâĹX¸p!ćĎźŹ9sć`Ú´i5kЬY3ĹkKůŢk1:k,'&&šżA´8gśŹ?ţ8ţńŹŕرcŘąs'˛˛˛Đ°aCŚ;aaa8q"ľţúkŔŤ7Đ®];L™2Ĺ<® ѱcGüóź˙´zś×Ż_ÇĉÍë„……ˇaĂ†ŚŚÄ˘E‹°hŃ"čt:Ü}÷ÝčŃŁ‡ŐľQ““Ő^cŢ“WýŐľ}ű°lŮ2ŇŰ2±†Uy"%%ŃŃŃVç‹ěěló뙳ŤF#222JĚ)M›6ĹÓO?m3ď«­Ż‹÷e˝zőlŢ+µ5?öë×ĎśďJçŢ7n8ś3•îăÚşć±6ŽM¬ŐłpżĐ-×ŰľFK>Ä|hâO÷”bkÓ¦Ťb/^´«ĎMő±GîT¨PÁ汩™“Ú¶m‹›íâĐu‰˝µŻ–<őůťH±ßÚ"""""""żŁÓé™™é·7â{ě14nÜ“'O6/kҤ <^ň’+uéŇ5kÖÄÇěéP\Ćßó–îÝ»řߛԲTk˙aýč^ŽÔQŢRźxKt[ śĂ¸ë®»°gĎ”/_^ńősçÎĹĄK—0lŘ0€ŃhÄŮłg±nÝ: :.\đxŚäĎ\kÉ’%čŃŁ‡_ç'KXC“?°5Ž­ŐłpżĐ­×ŰľDk>g>¤@çîë-y8Pë5"ň/Až€ČsçÎĹ˙ű_—Ľ‘Á^:ťNńqčĐ!O‡é•|­í¶mۆÇcâĉž…\Ě›j gđµ9—üŻĎXGi3cĆ 0@Ő›ěÇŹŹ_|}úô1/ Bll,Ú¶m‹:uęx‡±mŰ6Üu×]hÔ¨fÍš…W^yEŐz›6mĚž=999ćĺ»víÂđáĂ1ţ|ŹÇHDţŤ54ůKăŘZ=Ë:—¬a>$­üˇ­Ýy]ÂëG7aE®ŕďçpDDňňň„… ˘\ąrŞÖ›7o €9sć 66mÚ´AZZvíÚ…ůóçăŽ;îđxŚDä˙XC“?(>Ž­Őł¬sI ó!*w]—0:OADDDDDD®ŁÓé™™‰´´4O‡BD>†ůüI÷îÝK—.őp$Däo©OĽ%""G1źąÖ’%KĐŁGđmDäĎáz›ůśČńü&"Ŕ_ """"""""""""""""""""""ň1ü0‘Źá‡üPvv6–.]бcÇÚ\FDDDDDDDDľ‰ """""ňS"‚#GŽx: """""""ň€"==iiiřňË/­.#""""""""ßâéČq'NśŔľ}ű°sçNěÜą[·nENN`ňäÉXşt©‡Łô-yyy¸yó&ŞWŻîéPçĎźGLL Ę•+çéP|VVVşwďîé0Č˝^ŹĐĐPO‡áW®\ATTt:ť§C!"/uúôiÄĆĆ:m{III8q"fĚasąkPr”Á`@pp°§Ă ˛(++ ­[·ötnÁ|Nľ"??7oŢD•*U<Š×;}ú´§C "r? @DDDDDäcôz=öîÝ‹]»va÷îÝصk~˙ýw \ąrHNNF‹-đî»ď˘E‹x˙ý÷ĆS+''ÄąsçPłfMż˙0Ŕ¦M›P˝zu4nÜŘÓˇ FŁ;vě@QQâăăѰaCT¬XŃĺűMMME\\śË÷CDä÷Ýwź§C z˝?ýôš5k†zőęy:§2 Ř´iĘ—/Ź{q‘‘žÉg±>!uüřqôęŐ 6lpęvĂÂÂT-#"׋‹CjjŞ§Ă —››‹ 6ࡇBDD„Űö{ŕŔŤF4mÚÔmű$ßÔşukżżćf>,çĎźÇîÝ»ń÷ż˙ÝÓˇhvňäIěŰ·µk×Frr2*W®ěéĽVll,Ďo"ňy:OADDDDDD–éőz>|Řüm˙;wîÄ®]»păĆ „††˘QŁF¸çž{ĚŹżýío÷tŘ>iÓ¦M?~<–/_Ž-Z`Đ Axîąçüţ·îľűntîÜž…\ż~ .ÄĉqôčQtîÜ ÂĂ?ěéĐţź˝{Źëůţ˙Ç{˝Ę1Ą׌(‡%ˇĂ6«áMl({Ůčŕ”ЦPJs(ćđ"‡jN9Ś&>łÉlo•ˇ^EE)‡B)j:Ľžż?ö]żŁrxt¸]/xľ^ŻÇëözĽŻgĎ^=îŹŃ+1sćLěرiiiurĺşŚŚ 8;;#66žžžX¸p!'äÖb2™ {ö죣Łč(Tܸq Byy9.^ĽřĘŰ—Éd022z˘ígŁú‰ç3˘šo„ P*•8ţüÝejýúőđđđŔÉ“'afföĆž—H´Ś3µ}ZĺŃŁG1gÎ$%%aÔ¨Q B§NťDÇ""˘×€KCŐ>Dll,BCCááá+++hjjÂÄÄÓ§O‡R©„™™6nÜ„„<|ř)))ݏ? ŞFĄRáŕÁ077‡µµ5ňňňĄR ''§:_rą*•Jt Ş„fÍšÁŐŐ/^Ä?ü€ĽĽ<ŘŮ١Oź>GYY™čDDDŐ–śśŚőë×#((¨N€ľľ>Ž;†uëÖaÝşu033Ă©S§DÇ"Ş÷ţřă,[¶ ÎÎÎ077‡ťť’““ß˙=Z¶l ™L__ߊÇlذjjj {aQQQpssCűö푟źŹĎ?˙­Zµ‚©©)”J%`ëÖ­HIIÁ­[·0eĘ@XXäryŤĎÂÂB¬\ąň‰cpéŇ%888ŔŰŰNNN°±±Áůóç«Ô;w†d2‚‚‚P^^صk5j„m۶U§k‰čČÍÍEDDfÎśůF `ňäÉčßż?&Mš„ŇŇŇ7úÜDDôňlmmˇT*±{÷nś9sFFFpssCnn®čhDDôŠqg"""""" pîÜą'VüOMM…JĄBóćÍabbňÄŠ˙ĆĆĆËYĎýŞ<~ü{öěÁâĹ‹+VX_°`ĚÍÍEG{ăĚÍÍ1pŕ@‰ŽBŐ‡•+Wâ‡~€žžfÍšQ­"I>řŕáäÉ“őâş7++ ...8věśťť±rĺJhhhŽEUŔ•´ëWWWxzzÂĐĐ0hĐ $%%áŇĄKĐÔÔDpp0ÜÝÝqřđa <píÚ5x{{cçÎť/lŁ  FFFxřđ!/^ŚqăĆ!&&ăĆŤ……Nž< ŕŮ+őwéŇO¬HúĎcP©T¸|ů2ĘĘĘĐşukĽóÎ;OTfg___,Z´)))čÖ­[ĹëôđđŔľ}ű^m§SŤÂóQÍ€Ő«WăúőëhÚ´éţôôtôěŮ .Äś9sŢřó‰PWvř»ŇŇRlٲ ,@QQfĎž ///4iŇDt4""zęţ7ęDDDDDD‚ĺçç#66 …NNNčŢ˝;´µµammŤ… "33¶¶¶Ř˛e ’““‘——÷ÔýëĂ„¨7ˇ°° …ť;w†««+,,,pńâĹŠÝę#™LĆťj1KKKěÝ»ééé:t(ĽĽĽ ŻŻ…BââbŃń*eűö퉉Appp˝ąîíر#Ž9‚-[¶ŕűďżGŹ=pěŘ1ѱęťÓ§O#,, FFFÉdÉd8ränßľŤăÇŹÜÜÜСClذˇâqaaařꫯ*ŐF»víĐ®];ŔĽyóСC|öŮgh۶-Ξ=űÜ| 4xá±)S¦`ѢE555´lŮiiiUî‹YłfASS«WŻ®8¶sçNLš4©ĘmŃ«QZZŠĐĐP¸ąą )ţ,:óńńżż?§Őz IDAT._ľ,$˝Ľ ŔŐŐ—/_Ćüůó±jŐ*tíÚˇˇˇÜu¨¨ߪ˝!9998xđ üýýáččîÝ»CGGÖÖÖ B^^pŕŔäääŕţýűOMüÓŰ=×wîÜżż?:tč___Ś=™™™‡čxBÉĺň:µşM}ĄŻŻŹµk×";;ăÇŹÇĽyóбcGˇ¨¨Ht<""˘UPPoooLž<}űö獒ÉdprrBrr2zôč[[[¸ąąˇ°°Pt4˘z#>>&&&$é©C‡đç¤:t™™™(--EZZzőęUé6žő{ľŽŽ?~üŇŻaÖ¬Y°··Çúőë±xńb<~üĄĄĄUn§E‹pwwǶmŰ““řĺ—_*vC "˘7/""·nÝ‚›››Đ^^^022‚łł3żG$"Şĺš5k///ddd`ܸq1cLMMÉs<Q-Ćb"""""˘jĘČČ@dd$ćÎť‹ÁŁM›6h×®FŚ]»vA.—c„ ŽŽĆ˝{÷ž(°··‡®®®č—PçeffÂĂĂ;vĆ ŕáá«WŻBˇPŕí·ßŻFËĺÜ iÝş5‘••…/ľř, "˘Í××%%%E]]]ěßż{öěÁľ}ű`jjŠ#GŽŽET/Ü»w™™™ĎĽVţűďIÎÎÎĐĐĐ@pp0~řáŚ=şĘmĽ.ńńń055EçÎťáăăfÍšU»­ŮłgŁaÆX˝z5”J%úöí 55µW–ŞbíÚµ9r$:vě(4‡şş:6oŢŚ¸¸8lÚ´Ih""z5Zµj…ŔŔ@¤ĄĄÁĆĆź~ú)ú÷ďŹß~űMt4""ŞUÂ?'ň·iÓ]ştÁ˙ű_DEEAKK S§NETTrss‘žžŽÝ»wcÎś9°µµE‹-Dż„z%)) NNN000ŔˇC‡°téRdeeÁßßÚÚÚ˘ăŐ(,¨›ţ* ČĚĚ„““ ŻŻŹU«Vˇ¸¸Xt<"""@rr2ÖŻ_ŹeË–ˇeË–˘ăçŕŕ€””ŘŘŘ`Đ ApttÄ˝{÷DÇ"ŞÓŚŚŚPTT„   'Ž_ĽxÁÁÁ˙×ŇŇ‚łł36oŢŚ={öŕ“O>©r/"“ÉPVVöÔ1())H’„ĚĚĚŕččĚĚLŃѨX @DDDDDőÚ?Wü×ŐŐE‡0räHlŢĽ 4ŔW_}…\\\Đż\¸pááá044ŻÖc1Éĺr|ńĹHKK§§'/^Śž={â·ß~ŤęI’0}útôîÝ&L§V˛¶¶FRRÜÜÜ0uęT 2WŻ^‹j9 @.—c×®]hذˇčHDDőÖşuë0zôhĽóÎ;˘Ł<—››ĚÍÍ1}útN%"Ş'Zµj…ŔŔ@¤§§ă˝÷Ţçź~Š~ýúá×_Ť¨Ţc1ŐX>¬ôÄ˙›7o>5ńżM›6˘_˝„‡BˇP S§N={6FމĚĚL„‡‡ŁK—.˘ăŐ2™ŚĹŕĎ @ ,@zz:LLLđÁŔŃŃąąą˘ŁQ±mŰ6ÄĹĹ!$$r9˙ŚŻżţ#FŚŔ;ďĽ#ú%Đkvűömx{{COOëׯ‡‡‡®^˝ŠŔŔ@´hŃBtĽ:ŹĹô< Äďż˙ŽÂÂBĽűî»ŔăÇŹEG#"˘Zäüůóظq#‚‚‚ ŁŁ#:NťÔłgOśA“&M°k×.ŃQ¨–¸rĺ ĆŹĄR‰ŔŔ@Ě1;‰č_mٲ...ŹŹGďŢ˝EÇ©7ţ*â›?>ôőő±yóf™™‰ŽU§Čd2ôë׏»ŮQ­÷ý÷ßł€H°ożýÓ¦MCvv6Ţzë-ŃqŞ%##&&&Xşt)fÎś):ŃKa1Ŕ«“››‹Żżţ7nD×®]„aƉŽEDTç°€*Ą¬¬ iiiP*•‹‹Cll,RSSˇR© ««[1áßĚĚ  @Ë–-EG¦&55Řłgşv특sçâłĎ>ă* ‚Ť5 4ŔîÝ»EGˇZDĄRaíÚµ3glll°uëV´k×Nt,""Şa `dd„ŃŁGcÍš5˘ăÔKpqqALL <==±páB4jÔHt¬:ÁÁÁAt"˘WföěŮčßżżčDőVŻ^˝ĐłgOl۶Mt”—âăăµk×"--­Ö5,x˛łłáëë‹;v _ż~Xľ|9,--EÇ""Ş3X @DDDDDĎ”™™‰ŘŘŘ'Vý˙ăŹ? ©©‰=zTLü·¶¶F§NťDÇĄ,==_ý5ľűî;Â××ŽŽŽË墣ţśÄ$—˱gĎŃQ¨ŠŹŹÇ¸qăpçά_ż˙ýďEG""˘dúô錌DZZ´µµEÇ©·$IBXX<==ˇ§§‡M›6ÁÂÂBt,""""đ믿bŕŔ8}ú4ĚÍÍEÇy)EEEčÖ­ěěě&:Qµ±ŕő9}ú4ćĚ™ăÇŹcôčŃXşt)ôőőEÇ""Şő8ó‚““Âßßöööhٲ%ôőőáě쌣GŹ˘{÷î Arr2ňóó …B'''ĐżĘĘĘ‚››şwďŽ'N`ýúő8wî>ýôSÔ rą*•Jt ŞĄĚÍÍqöěY899aěرprrBaaˇčXDDTś?!!!X¶l “ÉdpuuĹůó硫«‹ŔÍÍ Ź=Ť¨Ţ[łf ,--k}!4mÚK—.ĹćÍ›qúôiŃq¨ęŰ·/~űí79r/^„±±1ÜÜÜpçÎŃŃj5î @DDDDTĎŕÜąs«ýÇĆĆâĘ•+€Îť;ĂŇҲbŐ˙>}ú qăĆ‚Sm“ťťŤ%K–`óćÍhßľ=Ľ˝˝1qâD¨««‹ŽFĎđß˙ţ%%%Ř»wŻč(TË8p...ĐŇŇ®]»Đ·o_Ń‘HI’`ii ™L†ŘŘXČd2Ń‘č˙‘$ Ű·oǬYł ­­Ť°°0 8Pt,"""˘z);;úúúŘąs'ĆŚ#:Î+óţű¤qqqü]€j%î đf”••aóćÍđóóĂăÇŹáĺĺţmš¨¸#QVZZŠ””„††ÂÉÉ Ý»w‡ŽŽ¬­­„ĽĽ<899!** ÷îÝCFFÂĂĂááá+++~ŮBU’ťť âČ‘#X·nŇÓÓáęęĘB€Ś;Đ«2bÄś;w]ştŤŤ 6lŘ : ˛eËś>}ëÖ­ăäźF&“ÁÉÉ ÉÉÉčŮł'lmmáććĆť}F۶m1räHŃQ^©Ő«WăôéÓرc‡č(DDT©««ĂŐŐ—/_†——/^Ś®]»"44ĺĺĺ˘ăŐ*Ü€¨Ž(//GjjjĹŠ˙JĄ xüř1´´´`jj 333XYYÁÚÚo˝ő–čČTG\˝z+V¬@HHŢzë-Ě›7Ź;Ô"ăĆŤCaa!8 : Ő’$aٲe?>>ýôS„„„@CCCt,""zCňňň`dd„O?ý …BtzČČHLť: ĹG}$:Q˝PTT„öíŰĂÓÓóćÍç•›2e öďߏôôthii‰ŽCDT%Ü@Śśś,\¸›7o†ˇˇ!‚‚‚0tčPѱjî @DDDDTKÝľ}ŔĽyóđÁ@[[&&&2e ”J%ĚÍͱeË\şt <@ll, X@ŻÄµk×*v8pŕ‚‚‚––Ćťją\Î/´é•’Édđňň¡C‡đÓO?aŔ€¸|ů˛čXDDô†řřř@&“aáÂ…˘ŁP%888 %%6664hqďŢ=ѱęĽm۶áŃŁGpvvĺµXĽx1ĘĘʰdÉŃQ¨–xűí·‚óçĎŁ[·n6lěěě””$:QŤÇťj’’$&&âäÉ“8uę~˙ýwdeeA.—ĂČČčׯúöí NĦ×ęÎť;Xąr% Ú´iŮłgcňäÉhÔ¨‘čhT źţ9îŢ˝‹C‡‰ŽBuPvv6FŤ…+W® 22‰^ŁÄÄD››cóćÍprr‡ŞčŕÁ2e ĘË˱nÝ:Ś9Rt$"""˘:«GŹ077ǦM›DGym‚áéé‰óçĎĂŔŔ@t"˘JăÎ5É'đŐW_áäÉ“5j–/_===ѱj$Ő@999P*•‹‹Cll,Îś9ââbhiiÁÔÔVVV°´´Ä€вeKŃq©žř«`Íš5hŐŞ<==áćć†ĆŤ‹ŽF/aâĉ¸uë~üńGŃQ¨ŽúăŹ?ŕââ‚Ý»wcĹŠ1c†čHDDôH’KKKČĺrÄÄÄ@&“‰ŽDŐźź///„††ÂÁÁëÖ­CëÖ­EÇ""""ŞS˘ŁŁńŃGáěŮłčŮł§č8ŻMyy9Ţ}÷]tčЇ¨ŇX PsH’„ďż˙ŢŢ޸uëÜÝÝ1wî\4oŢ\t4"˘…ĹDDDDD‚=zô‰‰‰P*•P*•‰‰AVVÔÔÔ`hh33łŠÉ˙ĆĆĆËĺ˘#S=“››‹+V`Íš5ĐÔÔÄěŮłáááÁ"€:ÂŮŮׯ_ÇO?ý$: Őa’$á믿†żż?¦NťŠŐ«Ws"˘:fÓ¦MpssCBBzőę%:˝¤ź~ú ®®®xřđ!áęę*:QťaooŹÂÂBüöŰo˘ŁĽvżţú+#GŽŔÎÎNt"˘Ja1@ÍSRR‚­[·ÂÇÇ*• _}őfÎśÉ]ˉţ˝a999+ţ+•JÄÇÇŁ¤¤o˝őúôé333™™ÁÚÚÚÚÚ˘ăR=v÷î]|óÍ7X»v-š5k†ŮłgcĆŚhҤ‰čhô ą¸¸ ;;GŽ…ęďż˙&LŔŔ±{÷nhhhŽDDDŻ@^^ 1věX¬^˝ZtzE ŕëë‹ŕŕ` 27nÄ;ďĽ#:Q­–‘‘DFFbäČ‘˘ăĽ#FŚ@VVąŘŐ ,¨ąňňň…B:`ѢE=z4w¨$˘zŹĹDDDDDŻQaa!’’’*&˙ź:u ąąąPWW‡AĹŠ˙fffčŢ˝»č¸DţÜ `ٲeXż~=š7o///¸ąąq'€:ĘÍÍ ™™™ŽŽ…ę‰Ó§OĂŢŢzzz8tčÚ´i#:˝¤iÓ¦aďŢ˝HMMeAsgggÜľ}Ë–-‹‹ ˙ČNDDDTM3gÎÄţýű‘‘‘QovMLKK©©)ÂÂÂ0aÂŃq^Ĺ5ßµk×°hŃ"|űí·čÓ§–/_ѱ„aÉ-Ń+R^^Ž””„‡‡ĂĂĂ}úô¶¶6¬­­ˇP(žžž‰‰Aaa!RRR'''PŤPPP???čëëcűöíX´h222ŕááÁB€:L.—CĄR‰ŽAőHßľ}ńűďż#??ýű÷GzzşčHDDôÎś9,[¶Ś…u”µµ5Ξ=‹É“'cęÔ©2d®^˝*:Q­SXX­[·búôéő¦ 1iŇ$Ěź?EEE˘ăQĐľ}{„„„ŕôéÓĐĐĐŔ{ď˝;;;¤¤¤ŽFD$‹ŞéÁ8zô(üýýaooŹÖ­[ĂÄÄS§N…R©„ĄĄ%¶lŮ‚¬¬,äääŕŕÁđňň‚••'VSŤRRR‚ĐĐP`ŐŞU:u*ŇÓÓ1kÖ,4iŇDtűě3¬ZµJt )) 'NÄĹ‹áçç‡/żüjjj˘cŐH?ţř#†Ф¤$ôčŃCt!Š‹‹ahháÇ#88Xt"˘Ĺb€Ú­¨¨k׮Œ%K ©©‰ `ҤIü΂ę4Q˝W\\ ĄR‰“'Oâ÷ßÇ©S§păĆ ¨©©ˇ{÷îčßż?úőë A&“‰ŽLTmGŹĹW_}…s7‚3% IDATçÎaÔ¨QXşt)ôőőEÇ"fÎś‰„„ÄĆĆŠŽBőXII ĆŽ‹Ă‡ăŕÁ, "ަNťŠ}űö!55ÚÚÚ˘ă eeeX±büüüĐ»wolÚ´ Ýşu‹¨Ć.\ѱ^ ąčDDDDDoÚ­[·pđŕAx{{ĂĘĘ :::°¶¶Ć˘E‹PPPgggDEE!77IIIظq#>˙üsł€j­cÇŽÁÂÂ}ôşté‚””DDD°€ —ˡR©DÇ z®aÆؽ{7FŚáÇs§ "˘îĚ™3 ĹňĺËYPĎ©««ĂËË P©TčŐ«Ľ˝˝QRR":QŤqéŇ%DGGĂÝÝ]táţú[ËüůóEG!"˘:®U«V Äůóçabb‚1cĆŔÖÖgÎśŤč•c1Őiĺĺĺ8wîÖŻ_ŹńăÇŁsçÎĐŐŐĹ'ź|‚ź~ú ˝ző¦M›™™‰üü|DGGĂßßöööĐŃŃźčĄ%%%aČ!řđĂѢE $$$ 22FFF˘ŁQ ÁbŞ)ÔŐŐ±}űvŘŰŰcČ!, "ŞˇT*¦M›†ţýűcܸq˘ăP abb‚¸¸8,_ľÁÁÁ077GBB‚čXDDDD5‚Bˇ@‡0tčPŃQ„SSSC`` öďßĎťJ‰čŤ000@DDNś8’’ôéÓŽŽŽ¸rĺŠčhDDŻ ‹¨Nyôčbcc{{{´iÓ={ö„——®\ąGGÇŠU˙Ďž=‹ŕŕ`|öŮgčÔ©“ččDŻÔµk×ŕćć333Ü˝{żüň >Śwß}Wt4ŞaX @5‰ššÂĂĂ1pŕ@ŘŰŰs!Q ´yóf$$$ 88;§ŃÔŐŐááᤤ$´lŮýű÷‡··7?~,:‘0Řľ};fĚ555Ńqj„˙üç?°µµ…§§'$I‡ę‰~ýúá˙űvďŢŤ3gΠ[·nđňňB~~ľčhDD/M&ńĘšj±śś(•JÄĹĹ!66ńńń())®®.¬¬¬`ii 333XXX A˘ă˝v÷ďßDzeË P(жm[Ě›7ÎÎÎËY NĎćĺĺ…_~ů…“®©Fyüř1FŚĄR‰ŘŘXŠŽDDDňňň`hhĎ>ű «V­‡j0I’†/żü:tŔ¦M›`aa!:Ń·rĺJřůůáúőëhŢĽąč85Ć™3g`nnŽČČHŚ9Rt"˘'DDD`Ě1,XŞĂJKK±eË,X°ĄĄĄ3gfÎś‰FŤ‰ŽFDT-, """˘ZŁĽĽ©©©˙cccqĺʨ««ĂŔŔ bňżŤŤ :vě(:.ŃUTT„µk×"00jjjřꫯřĄUĘÜąsńóĎ?ăĚ™3˘Ł=ˇ¸¸vvvČÎÎƉ'Đľ}{Ń‘ę˝)S¦`˙ţýHMM…¶¶¶č8T deeÁŐŐżüň śťť±rĺJhhhŽEDDDôF¨T*`đŕÁ§ĆqttÄ… pîÜ9.fCD5 ‹ęŹ`ńâĹXłf ôôô°lŮ2Ś1Bt,"˘*c1ŐX………8uębcc+VÎĎχ¦¦&,,,*Vý·±±áŠ:To©T*ěÝ»sćĚAnn.¦OźŽąsçň3A•6ţ|üřăŹHLL…č)÷îݵµ5ÔÔÔpüřqčččŽDDTo)•JXXX`ëÖ­7nśč8TËDFFbňäÉĐÖÖFXX(:Ńk…Ź?ţ.\€‘‘‘č85Î… ĐŁGěÜącĆŚ‡¨‹ęźk×®aţüůرc,,,°rĺJôďß_t,"˘Jc1Ő999«ţÇĹĹ!11*• şşş«ţ[YYˇwďŢ\%†ŔˇC‡ŕĺĺ…K—.ÁŮŮ ,Ŕ[o˝%:Ő2ľľľŠŠBRR’č(DĎ”ťťŤŔŔŔ?˙ü36l(:Q˝ŁR©`ii uuu?~2™Lt$Ş…nŢĽ‰iÓ¦á‡~€‹‹ ľůćhjjŠŽEDDDôÚŘÚÚ˘A8|ř°č(5Ö¸q㏔”¨««‹ŽCD€ĹőY||<<==‹ŃŁGcٲečرŁčXDD/ÄTDDDD$DYY”J% śśś §§‡víÚaěر‹‹ĄĄ%vďŢŤŰ·o#''đđđ€™™ ¨Ţ;{ö,>üđCŘŰŰŁ[·nHIIÁúőëY@Ő"—ˡR©DÇ úWzzz8|ř0”J%<<!**Jt,"""˘×"%%ÇŽĂŚ3DG©Ńüýý‘™™‰ďľűNt"""››ăřńă8pŕÎś9nÝşÁŰۢŁ=gQŃQPP€ŁGŹÂßßvvvĐŇŇBź>}ŕç燛7oâ‹/ľ@tt4 ‘…B´iÓFtt˘#''nnnčÓ§ đż˙ý‘‘‘čÚµ«čhT‹Éd2PŤ×ŁGlßľˇˇˇ ‡¨^ą˙>ćĎźwwwôěŮStŞ’’‚?ü#FŚ€ŁŁ#îÝ»':Ń+µfÍtéŇ ĄFëŇĄ ĆŤ‡… ˘´´Tt"""€˝˝=.^ĽĄK—"$$úúúP((//Ťč™X @DDDDŻEff&ÂĂĂáćć†îÝ»C[[vvvŘľ};tuu±zőj$''#//ŃŃŃđ÷÷‡­­-7n,:z­'je‚;wî 22K–,ňüuYQQ‚‚‚`dd„üëׯǩS§`cc#:ZťPÓVóxđŕAµ[ZZЏ¸¸*=†;Ľ:5m,Ő6/ż#FŚ€ŹŹ¦OźŽăÇŹżÁdTĆößĎťĽ&!zµćÍ›ą\???ŃQ¨iÓ¦ ÂĂĂqđŕAś8q&&&Ř·oźčXDDDDŻD^^vî܉3fp§ăJđ÷÷ǵk×.: Q… ŔĂĂ4iĽĽĽ`jjŠC‡‰ŽFDôţÖADDDD/­´´JĄ …ŽŽŽhÝş5ôőő1eʤ¤¤ŔÖÖ{öěÁÝ»w‘‘‘đđp¸şş˘{÷îÉd˘ăWKż~ý0gÎśj=víÚµđööĆŔaccôôô—ÎS^^Ž   X[[ŁeË–/Ý^UĄ¦¦" ŽŽŽŘľ}ű·˝L_=Ë«nŻ&S©TŚŚD·nÝđő×_cöěŮHOO‡««k­ű#?3Ď÷řńc,Y˛ x*Oeú.//óćÍŽŽ¬¬¬ŞôÜ2™ ’$U9ł(KuOUĆŻźź† ‚1cĆŕĆŤo(á›Á±ýlĎë—gť;źwMBDU§T*ńí·ßbĹŠhŢĽąč8T 6 ÉÉÉ>|8FŤGGGäć抎EDDDôR —Ëáää$:J­ §§‡Ď?˙())‡č -Z´@`` Îť;ŘŰŰĂÎÎçÎťŤ¨Bíš=BDDDD5ÂíŰ·±oß>Ěž=ýúő††úôéĺË—C&“ÁÇǧOźĆ …B‡:5I˛S§NŐÚĹ`Íš57o/^Śýű÷ŁuëÖ/µ ř_ÔÔÔŕáá . ¬¬ěĄŰ«*###¬X±â™·U·ŻţÍłÚ»~ýú+kż¦8věúôéO?ý666ČČČ€żż?š4i":Zµđ3ó|Ť5ÂěŮł‘––öÔŁ•é;,Y˛U~îÚ¶3ÇŇë#ę\Z•ń+—˱}űvhkkăłĎ>«Uc÷E8¶źíyýň¬sçó®I¨jT*¦M›†ţýűcěر˘ăP¦­­Ť>|§Nť‚ˇˇ!BCCEÇ""""Ş–ňňrlܸ“&M‚–––č8µ†źźrss±yófŃQžÉŔŔ8qâ=z„Ţ˝{ĂÉÉ ·nÝŤ2©6-˙GDDDDBdff"&&111ŤŤEZZÔÔÔ`jj KKK 0–––ĐÓÓµĆ366†$IHMM}mí§¦¦ [ĺ[&“ÁČČ/^|cĎ™••'''?~üŤ=ç딞žDFFÂÖÖ+V¬@Ź=DǦ®fţéeóTçńK—.ŦM›půňĺj=gmQßĆRUŐ„siUú099ćććđ÷÷‡——×HWsql?;Łk˘ş&44Ó¦MR©¬×ףôfŔ××ÁÁÁ2d6nÜwŢyGt,"""˘JŰ»w/šš ŃqjwwwěŰ·—/_®µ‹âQÝ1cĆÔčďDI,I’đý÷ßcÎś9ČÍÍĹ—_~ ///ţü""a¸3=%33ˇˇˇprrB§Nť ŻŻgggś={ BDDîÜąÄÄDcěر,¨¤k×®A&“‰ŽQgܸqÆ Cnn®č(/íţýűđöö†©©)’““qčĐ!DGG×ű‰WüĚĽ~µmg€ęâXúwµń\jbb‚€€řúú">>^tˇ8¶‰ču¸˙>ćĎźŹ3fÔűëQzł´´´ P(đŰożáŇĄK011Ahh('`Q­±fÍ 6Ś…Ő0oŢ<äçç#,,Lt""˘ç’Édppp@JJ |}}±jŐ*tíÚˇˇˇőâonDT󰀨ž+++R©„Bˇ€ŁŁ#Zµj}}}xzzâćÍ›0a˘ŁŁQXX„„( 888 E‹Ż%ĎÉ“'ńĺ—_˘S§N¸}ű6FŹŤ–-[ÂÔÔűöí«¸ßüeË–ÁŮŮććć°łłCrrň oW©Třß˙ţ‡YłfˇS§NČÉÉÁűďż===äççż0źJĄBdd$>˙üsĽ÷Ţ{€¨¨(¸ąąˇ}űöČĎĎÇ矎V­ZÁÔÔJĄđ˙÷2e =z„[·naĘ”)˙/((€——ćÎť OOO 4žžžOäůő×_Ńľ}ű'Vl~Ö±ż\ľ|ÇG‹-Đ·o_üöŰoŹiÔ¨455`üřńÉdřŕ’’HLLÄŰożŤĐĐĐ*Ľ{/˘"ěÜącÇŽ…ĄĄ%Nž<‰wß};vD\\ŇÓÓńÉ'ź uëÖ066®čżkoëÖ­HII©čÓÚ¨¤¤ …úúúŘ´i–-[†óçĎcčС•zp,ŐαT™>xŃűö¬siuó>ŻO«:^4~===ammŤ & ¨¨č_űc»vŽí;ľčÜůo^ôţŃ“ćÎť uuu,X° âXećTćDô"ÖÖÖ8{ö,&OžŚ©S§bČ!ČÎÎ~évwîÜ Čd2ˇĽĽ°k×.4jÔ۶mđâźUą&'""˘ú#99ÇŹ‡»»;^{T•®®.¦L™‚ĄK—˘¸¸Xt""žĂé…š6m ///¤¦¦bđŕÁ:u*,,,óJÚç÷qDTiŐ+>”bbb¤ŔŔ@ÉÖÖVjҤ‰@j۶­4lŘ0)00PЉ‰‘JJJŢx¶ňňréСC™ÜÝÝĄăÇŹK»ví’455%R\\ś$I’äââ"Ą¦¦V<öŁŹ>’Ú¶m+<÷ö»wďJ'Nśš6m*–.]*=zTrvv–>|X©śWŻ^•HFFF’$IŇőëץfÍšI¤Ĺ‹KŮŮŮŇŽ;$’……ĹŹýűă$I’ %ÉßßżâŘť;w$©sçÎR~~ľ$I’tŕŔ©iÓ¦ŇÁ+î÷¬cFFFićĚ™Rtt´"ihhHjjjŇąsç$I’¤©S§JŤ7–xŃűö¬siUóľ¨OďÜąóÜ1Pťń{íÚ5IGGGš>}ú3oçyňOµql?«_*{î|VżĽčý%˘˙_BB‚$—ËĄť;w>qĽ2ç¦ĘžŞâĉ’±±±Ô´iS)00P*//©ö|||$RJJJű«WŻJź|ňIĹ˙_ôsăy×äDDDTMś8QęÖ­›¤R©*ŽńÚŁjnßľ-5mÚTZ»v­č(DTŹíŮłGŔs8UYRR’dgg'FŹ-effľT{ü>Ž*‹ĹDDDDuÜíŰ·Ą¨¨(ÉËËK˛´´”6l(tuu%)$$DJNN~âËiŃ $ŇŁGŹ*Ž­^˝Z }úé§Ň©S§$ĎüwčСŢ.I’dhh(îßż_­Ś˙ś`öW{×¶m[©QŁFĎ}Üüůó%ŇÍ›7ź¸_xx¸@š3gNű˛˛˛§rüóŘ_öţ>±MˇPH¤ &H’$ULľ_ż~}Ĺ}†.5kÖL*,,”$éĎIę!!!ĎígyÖdŇĘk×®ÝSýצMI[[ű…ĎQ‹Nś8!őë×O’ËĺŇ„ ¤ëׯżT{üĚÔľĎLpp°@şxńâÇ˙z/˙îź}Đşuk €¤P($•J%%''Wä˙ëődffJ“&M’rss_eĹŠŇ;ďĽóÄós,Őž±$I/îĘĽ/Ď:—V5oeűôßĆ@uĆŻ$IŇöíŰ%ą\.ťÁ‘#G`ff†ť;wâćÍ›ČÉÉADD\]]Ń˝{wČd2ŃŃ+Čĺ^¦6mÚ´âŘđáĂ—.]B||ţřcŔ… ĆĆĆ8p BBBŮŮŮ(//GII ľűî;@xx8ĆŤ÷ÜěŻŇ?_?´hѢÎm]““'''XZZ˘I“&ŹŹÇÖ­[Ń®]»—j—ź™Ú÷™9rä cÇŽO˙ë˝|ž 6@SSčŰ·/>|řT :Ź=B«V­^Řž\.‡JĄzâů9–jĎX^Ü•y_žĄŞy+ۧ/Uż0nÜ8|đÁpqqAiié3ďñ];Çö?U÷ÜYÝĎQ}ĄR‰uëÖ=ó<ô˘sSUÎADUѸqcřűű#>>ĹĹĹčÝ»7‚‚‚P^^^ĺ¶Z´hwwwl۶ 999€_~ůPąź•ą&'""˘ú%$$Ť5Âřńăź8ÎkŹŞóôôÄÍ›7!: Ős<‡SuŮÚÚâĚ™3X»v-ľűî;čëëCˇPTë{ ~GD•Áb"""˘Z.-- aaa?~<:tč€Îť;cňäɸtéFŹŤĂ‡ăţýű8sć  FŹŤ·ŢzKtě*{űí·íŰ·Ç˝{÷™™‰˘˘˘§î§R©^x{Mň×䵬¬¬'Ž·mŰĐĽyó—~ŽżÚęСCűéÓ§#)) ńńń ²eË0räH„……áÂ… čرă&éĺcѢE000@\\öîÝ‹cÇŽáÝwß}mĎÉĎLő˝‰ĎĚŤ7÷îÝ«rľQŁFáěŮł4h`mmŤm۶=qźoľů{öěAPPĐ Űű{1Ŕłp,U_M9˙ľĚűR•ĽŻŞO«2~˙˛aĂ\şt k×®­ôc8¶«OÔخ6˝D"Ýż>>>đđđ@Ź=ŞŐĆ›8QýÖłgOś:u ~~~đóó••UEqZUĚž= 6ÄęŐ«ˇT*Ń·oߊ‚¸ĘüܨĚ59Őeeeظq#\\\ ˇˇńÔíĽö¨šNť:aĚ1 ‚$I˘ăQ=Çs8UW ŕęꊴ´48;;cÎś9055ĹáÇ_éóđű8"X @DDDTëdff"44NNNĐÓÓ‘‘fÎś‰+W®`ěŘ±ŽŽF^^Ž?ŽE‹ađŕÁĐŇŇűĄý5éËÖÖFFF(**zj’ŕĹ‹üÂŰk’˙Ź˝;Ź«)üţş-˛Ę-HdĎš%M1¨0S C &{1ŚblăĂø˛ĹŘ2c)ćóˇ ¦c§,‘,C3Ů+RáVďßóíţDËm}·ĽžŹÇ<ćáÜsĎyť÷ű}ÎyßÓyź“>"˙Ŕ¦?xđŔżŰ›.ł'hňô€ôe9::ާ9;;ĂŘŘóçĎGbb"š5k†qăĆ!,, &LŔřńăsż1’( ¤¤¤ČŽ‘Ą   4oŢ‹/ĆôéÓqíÚ5 8°Đ×Ë}¦xď3M›6Ít;41oŢ<™™áСCŘąs'T*fĎžťažľ}űbÖ¬Y5kVŽs Ŕ¶TĽŰ’&4©—¬ŽĄąÉ››2ÍNnÚoşĆŤcĆŚ3gÎĽłÂ¶]ňÚv^Źť%©ţdňöö†ŽŽćĚ™“çeÔą€(;:::đňňBxx8ŇŇŇĐşukx{{ăíŰ·/ŁFŤ?~<ÖŻ_ŹU«VaÔ¨QęĎ49ohŇ''""˘˛cĎž=xřđa–ż}Ů÷Č=///DFFâ×_•…Ę8Ă)żŞU«\˝zVVVčŰ·/ňôpĚđz[)))"22Rřůů WWWahh(Ę•+ {{{1oŢ<"^ż~-;jjÚ´© RRRÔÓ¶mŰ&Úµk'T*•xýúµ033ĨQŁÄĎ?˙,fĎž-zőę%^ľ|™ăçBabb"WŻ^ĺ:_BB‚ ęÖ­«ž–ľĽwŐ«WO*•J!Dll¬ ĚĚĚÔó$%% +++Qż~}ńđáCőtOOOaccŁţnpp°¨\ą˛8xđ zžĚ¦YZZ "66V=m„ ˘˙ţlÇÂ… …Bˇ‘‘‘ęiM›6NNNą.“ôm LLLÔÓ2+«ääd@XXX¨§™›› "!!A=-˝LSSSł]^ŁFŤDĄJ•Äýű÷ó”»°\ż~]ôîÝ[( áęę*îÝ»Whëâ>Sňö™Ë—/ QŁF qčĐ!‘””$Ž9"ŞV­*»wď !2/»Š+Џ¸8!„*•Jčë닎; !„055DZZšHIIvvvÂŔŔ@\şt)Ë,«WŻFFFęía[*YmI“2Ф^˛;–jšWÓ2ÍŞ äĄýľ+99Y4nÜX¸¸¸|đŰvÉlŰď—‹¦ÇÎ÷ű$šÔQY.´´´ÄŽ;˛ť/§c“¦Ç ˘‚˘R©ÄĘ•+EĄJ•DË–-EXXĆß}ôč‘ĐÓÓ¶¶¶¦krŢČ®ONDDDeO—.]ÄŔłť‡}ŹÜëŰ·ŻčÖ­›ěDTíŢ˝;ĂőĂ© …††Š–-[ ]]]áîî.ž>}šíüĽGDšĐž?ţüü) """˘‚ššŠK—.!00+V¬Ŕ„ °|ůrüńǨQن †yóćÁ××#GŽ„­­-ĚĚĚ ŁŁ#;zZłf ž={†ŞU«Â‰‰‰8qâÖ­[‡ňĺËCGG @tt4~űí7üţűď¨_ż>Ö¬YjŐŞeűąžž|||đß˙ţđěŮ3ŁvíÚeKJJ¢E‹pňäI$$$@__ÇŹÇž={üűşżÖ­[cýúő ¨T*ŕűďżGxx8^Ľx…BjŐŞˇ~ýúpssC\\ÖŻ_Źüţűď000ŔĆŤˇ«« ¸˙>S»vmôěŮX˝z5¶mۆڵk#!!}úôAÝşuadd„ďľűî6ż`Á„„„ >>6l@­Zµ°|ůrlÚ´I]OzzzhÔ¨ôőő±sçNěÜą …-Z´@ůňĺ3dąpáŽ9‚3f°-•Ŕ¶´víZěر#Ű2čŃŁ\\\˛¬7xôčQ–ÇRMóęęęf[¦*•*Ó6 §§‡ĺË—ç©ýľKGG 6ÄÜąsáŕŕ€ ¨?cŰ.ym;łrů裏лwďlŹťiiiX´hQ†>IóćÍńŮgźe»•eiiiřôÓOajjŠĺË—°o§Óäśckk‹QŁFĺx "*(ZZZčÔ©ŚĐĐPĚť;IIIčÖ­[Ž×J*W®ŚsçÎařđáhٲĄzzNý={ö}ňÍ›7ĂŔŔ P·—Šź‹/bÎś9X·nLLL˛śŹ}ŹÜ366Ć·ß~űÁu"˘ÂvíÚ5ěŮłé·UňNÉĚĚ cÇŽ…‰‰ |}}±jŐ*”/_ÖÖÖĐŇŇĘ0/ŻÇ‘¦B!;QY•’’‚„††âäÉ“8yň$âăăQłfMtčĐ]»v…˝˝=Ú´ióÁżŇĚŇŇ7nÜ»ŞT’¤¤¤`óćÍ={6ttt0ţ|Ś3¦Hö]î3”~~~9s&bccŮ–¨Tř裏””„Ó§O«oheŰ&"ĘÚúőë1yňd\¸p!Ă´‰J!6nÜéÓ§ŁAŘ´i:věĺüIIIhŐŞ®\ą‚ *aR"""*MÜÜÜpéŇ%\ąr%˵űyeccCCCěŰ·Ov"*C0xđ`őődĂ©°ÄÇÇĂÇÇ+W®„‰‰ ľ˙ţ{8::ĘŽED%PŮąŁŚ¨HNNĆŃŁG1ţ|ôěŮU«V…µµ5|}}QµjUřřř ** Ź?FPPĽĽĽĐ®]»25@&…B‘ă7oŢ”łČ±\rväČ´mŰ“&MÂ!CpăĆ ¸»»—ú}—m#s%­\´´´––&5CI+ł˘ÂrɛŋăÜąsŘżżě(¬Ă,°\ŠŹŘŘXĚ™3S¦Lá@*ń ÜÝÝqĺĘÔ­[]ştÁرcńęŐ«Lç_łf &OžĚ9(Ďž€m‰J“ďľűŃŃŃřńÇٶ‰˛ŽÍ›7ă‡~€ľľľě8DÎŐŐ×®]C«V­`ooʱcÇ^ľ| ---ěرĺĘ•“ś’J*•J…Ť7běر¨X±b¦óTŞT‰}Ź|R(>}:věŘ{÷îÉŽCDe ŹáTÔzöě‰ .`Ó¦MŘż?,,,°dÉĽyóFv4"*悏â""""Ęł÷źüęÔ)$''«źüoooššĘŽJD@={ö`úôéxůň%Ľ˝˝1uęT^¤mëÖ­8q˘úÉéDĄĹčŃŁńÇŕćÍ›ĐÖÖ–‡¨XIKKCçÎťˇ§§‡?ţřÔ©Ô Ä„ P®\9¬[·ÎÎβ#Q ÷ÓO?á‹/ľ@tt4ŚŤŤeÇ)ŐT*ĚÍÍńé§źâ‡~‡Ę€€€ }úŕĎ?˙„——P‰—ţf˘ŇfÖ¬Y¸wďöěŮ#; Q±ăç燋/bÍš5@eBú[>úč#ôďß ÂóçĎeÇ"""˘Ě×ן|ň ]]]xzzbóćÍHHH‡¨HT­Z‹/FTTZ´hGGGôíŰ7nÜŤŠ! """ĘFjjj†›˙ ammŤ•+W˘B… X¶lŮ7˙7oŢ\vl"*@±±±đôôDçÎťQľ|y\¸pëׯ‡ˇˇˇěhD‚¨´277‡‹‹ ľűî;>Á‰č±±±;w.¦NťŠ-ZČŽCTdjÖ¬ áôéÓ°˛˛Â/żü";•@gÎśÁůóçááá!;J™1zôh¤¦¦bË–-˛Ł)SSSâرcxřđ!Z¶l‰±cÇâŮłg˛ŁQ1ÂÁDDDDďHżů_©TbĐ Aę'˙˙đèPˇ–.]ŠČČH<|ř7˙•riiiđ÷÷‡……öěŮ-[¶ŕčŃŁhŐŞ•ěhDЍ4›3g"##,; Q±áĺĺĚž=[v") ggg|úé§4hž>}*;• «V­B›6m`cc#;J™a``€áÇĂ××—×2‰¨LęŃŁ.\¸€ü˙űß˙`aaĄR‰ÔÔTŮѨŕ`"""*ÓŇŇŇpńâE|˙ý÷čŰ·/ `mmŤ%K–@GGK—.ĹÍ›7ńčŃ#ŢüOT†„……ˇsçÎ3f †ŠëׯĂÍÍ …Bv4˘ÇÁTš5kÖ NNNđńń‘…¨XÇćÍ›ńĂ? jŐŞ˛ăIc``???úľľľ˛ŁIe``\˝zőęŐCŹ=ŕää„»wďĘŽFD’p0•zŹ=B`` ĆŽ‹† ÂŇŇ3fĚŔ‹/0cĆ „‡‡ăáÇصkĆŽ‹¦M›ĘŽLDE,%%JĄćććŘ»w/¶lŮ‚#GŽđM T&hiiAÁTjuëÖ íڵÚ5kdG!"’ĘĎĎ/^ÄęŐ«ůĆ+˘wT­ZJĄÇŽĂźţ +++lذýc"""úŔ›7o°qăFŚ?ĺË——§LšŚ›7oĘŽBDD$]“&MŚDGGŁyóćđööFBB‚ěhDTÄ8€Jť§Oź"00žžž°¶¶Fť:u0tčP\¸pC† AHHbcc///´k×ZZě•UÇŹG۶mńő×_cĸqăÜÜÜdÇ"*2éçŔ´´4ÉI Ďřńă±k×.<~üXv"")ž?Žąsç⫯ľB‹-dÇ!*–şuë†Ë—/cܸq0aúôé{÷îÉŽEDDDĹČŽ; wwwŮQĘ,GGG4lŘ~~~˛Łööö¸|ů2/^Śőë×ĂŇŇţţţ|ĐQ»ިÄKLLDhh(Ľ˝˝ammŤÚµkcČ!8uęěíí‚„„„‡‡ĂÇÇööö(W®śěŘD$ŮÇáćć[[[Ô¬YP*•¨\ą˛ěhDEЍ,:t(*W®ŚM›6ÉŽBD$…——tttđÍ7ßČŽBT¬U¨P>>>8qâîßżŹfÍšaÉ’%ě+`íÚµpuuE˝ződG)ł´´´0zôhřűűăőëײăşşşđôôÄť;wđé§źbÔ¨QčÔ©Ξ=+; ""˘'%%.\Ŕ’%Kŕŕŕ€ęŐ«ĂÁÁh×®víÚ…gĎže¸ůźŻk%˘t*• JĄM›6ĹŃŁG±uëV„††ÂŇŇRv4")Ňđé TšU¨PŁFŤÂúő둚š*;Q‘ Ă–-[°rĺJT­ZUv˘ˇsçθxń"ľţúkĚ™3Ý»wÇÍ›7eÇ""""‰Nś8đđpxxxČŽRćŤ3/_ľÄŢ˝{eG!""*vjÔ¨ĄR‰óçĎCOO]şt››ßśLTĘ)˙ÚODDDĹ\jj*ÂÂÂđűďżăČ‘#8}ú4^ż~ŤFŤÁÎÎ}ôzöě ###ŮQ‰¨;vě&Mš„ččhxxx`Îś9¨T©’ěXDRíßżýű÷Grr2ĎQ©vçÎ4nÜ@ź>}dÇ!"*p?ýô°páBT©RŔżoţéÜą3Ę—/ŹcÇŽAˇPHNITňDDD`Ô¨Q¸~ý:ćÍ›‡éÓ§C[[[v,"""*b®®®¸{÷.ÂĂĂeG!ź~ú)ž>}ŠăÇŹËŽBD%\rr2>|aÚŕáá;wîd®­­Ť† e<˘| ‚‡‡ž>}ŠéÓ§cćĚ™ĐÓÓ“‹ Q±ŤĐĐPőqqq¨U«şwď{{{ôęŐ &&&˛cQ ooolßľŽŽŽXµjLMMeÇ"*‚áää„ÄÄDT¬XQv˘BŐ˝{wÔ­[»ví’…¨ŔMś8k×®E­Zµŕëë WWW¬]»S§NĹĺË—ů&,˘|HIIÁňĺË1oŢ<´iÓ›6mBłfÍdÇ"""˘"ňĎ?˙ŔÔÔ[·nĹСCeÇ!żýöz÷îŤČČH4oŢ\v"*Áž?ŽÚµk#%%%Çy{÷îŤC‡A*˘‚•””___,\¸uęÔÁ˘E‹ŕęę*; -ŮŕéӧصkĆŚ››cúôéxóć ľýö[DEEáŃŁG€»»;‘FT*”J%š6mŠ3gÎ 88AAA@ô-­/ ¤ĄĄINBTřFŚ}űö!..Nv"˘węÔ)˙ţľ4hşvíŠoľůS¦Lá@˘|ŇŃŃ——.\¸€´´4´nÝŢŢŢxűöm¶ß»~ýz%$""˘Âäëë CCC¸¸¸ČŽB˙ÇŢŢćććظqŁě(DTÂŐ¨Qęż•dEˇP`Č!E”Ѝ`U¬X^^^ŚŚDëÖ­1hĐ ôíŰ·nÝ’Ť ‘ÉÉÉ …··7¬­­Q»vm 6 —/_Ćgź}†<{ö űöíĂäÉ“yăĺZhh(¬¬¬0kÖ,x{{#22ýúő“‹¨Řá`*K ľ€Jť·oß"** Ŕ˙?§ź?/^ĽŔË—/‘ 3Q©ŃĽysś9s«WŻĆęŐ«Ńľ}{„‡‡g:ď­[·`mmŤĂ‡qJ"""*HÉÉÉřńÇ1~üx”+WNvú?ZZZ5j~ţůçhĺdذa9ÎŁŁŁA˘ÂÓ°aCŕرc‰‰A‹-ŕííŤWŻ^ÉŽFDůÄÁDDDTd˘ŁŁ±aĂ899ˇzőęppp@`` Úµk‡]»váŮłg‡ŹŹěííyQ•ň$&&C† š7oŽëׯcÖ¬YĐÓÓ“Ť¨XJ „ś„¨đU®\ÄÖ­[eG!"*PP©T¦©T*!°iÓ&››Ăßß_R:˘ŇEKK îîî@Ť5Đąsgx{{ăÍ›7ęyŇŇŇ0räH$''cđŕÁxđŕÄÄDDD”?ýô^˝zwwwŮQč=#FŚ@\\8 ; •pýű÷ĎöŢ899A__żSž=zŕâŋظq#6mÚ řűűóo…D%Qˇyřđ!1věXÔ«Wććć={6*T¨ĄR‰żţú wîÜźź\]]a`` ;2•`iiiذa,--qîÜ9ă—_~A dG#*Ö ľ€ĘŽáÇăüůó¸sçŽě(DD&,, :::™~¦R©đüůsŚ1öööxöěY§#*ťĚÍÍńűďżcÍš5X»v-¬¬¬püřqŔš5kpöěY!””„đ‰µDDD%ÔęŐ«ńŮgźˇV­Z˛ŁĐ{ęŐ«[[[l۶Mv"*á*UŞgggčęęfúyjjŞFo *I´´´ŕć憛7oÂĹĹŁFŤ‚­­-®\ą";ĺQyőęBCCáíí kkkÔ­[#FŚ@tt4<<<ŽGŹ! îîîhذˇěČDTJ\Ľxť;wƤI“0räH\ąrýúő“‹¨DH3PYagg‡š5k"00Pv"˘žíçB( 888ŔĐаR•~ …îîî¸rĺ LMMŃłgO|ţůçđňňR?MOĄRáĘ•+>}şä´DDD”[GŽÁ•+W0qâDŮQ( #FŚŔđřńcŮQ¨„űüóĎ?xëbş *ŕăŹ?.âDDEŁzőęP*•8ţŚĐĐP„……A¶mŰÂŢް±±žžžě¨DTJĹÇÇcŢĽyXłf şv튵kעYłf˛c•(GŹ…ťťžßľ};ź&IDDT‚ 0Ďź?lj'dGˇ,$%%ˇvíÚřĎţOOOŮq¨S©T044ÄË—/3L×ŐŐĹ矎-[¶HJFTt„Řľ};fĚ•J…ąsçbҤIĐÖÖ–ŤrŔ7Q®Ü»w6lŔ§ź~ CCCtëÖ ?ýôZ´hť;wâÉ“' ĂâĹ‹aggÇDT(„đ÷÷‡……°yóf=z”ň€o ˛ČŐŐ/^ĚňĆY"˘’äŐ«WŽŽÎô3]]]âÔ©S@T^ĽxÄÄÄL¤żE **JB2"""Ę­{÷î!88“'O–…˛Q±bE¸şşb۶m˛ŁQ §««‹AAWW7Ăt•J…ˇC‡JJET´ ÜÜÜpăĆ |ůĺ—1c:tč€3gÎČŽFD9ŕ`"""ĘVrr2BCCáíí kkk`Ę”)xůň%fÎś‰đđpÜ˝{6l€««+jÔ¨!;2•r7oŢ„ľřâ 07nÜ€›› …ěhD%PYÔłgOaďŢ˝˛ŁĺŰ… 2=Źëčč uëÖ@۶m%$#*[bbb0mÚ4dőBn!T*śśśPÄé(·|}}Q»vm 8PvĘÁ°aĂpéŇ%ܸqCv"*ᆠ•J•aZµjŐĐłgOI‰ä000€ŹŹ®\ąCCCŘŘŘŔÍÍ Ź?–Ť˛ŔÁDDDôččhlذNNN¨^˝:víÚa˙ţýŤŤEHHĽĽĽĐ®];Ůq‰¨ŚHJJÂüůóѲeKÄĹĹáĚ™3đóóľľľěhD%Zú`€¬nZ"*ŤtttĐżüňË/˛Łĺ[XXŘO­ÓŇŇ‚‹‹ Ž?ŽZµjIJFT¶|ůĺ—xýúu¶ó¤¤¤ŕţýű7n\Ą"""˘ĽHJJ–-[0qâÄúÚTüôčŃuëÖĹ®]»dG!˘®GŹ022R˙»\ąr>|8ttt$¦"’LJƾ}űpüřq4mÚJĄ2Ó7"‘\ @DDDxőę‚‚‚0věX4lŘććć5k*T¨ĄR‰ű÷ďăÎť;đóó““Ę—//;2•1AAAhÖ¬”J%–.]ŠóçĎŁC‡˛c• éoŐŕ›¨¬qttDxx8źdCD%ŢůóçŐçq---hiiá»ďľĂÎť;ůűť¨ŕ×_…ÚÚÚŮΛ’’‚ť;wÂĎĎŻŇQnmÝş‰‰‰=z´ě(¤ôÁĐ @DůĄĄĄ…aÆ©‚˝}űC† ‘śŠH>'''DEEÁÓÓ^^^hѢBBBdÇ"˘w(ýGDDT椦¦âňĺË Ehh(ţř㤥ĄˇuëÖ°··‡˝˝=lmm9Âť¤‹ŽŽĆäÉ“ń믿ÂŐŐ«WŻFÍš5eÇ"*UΞ=‹Îť;ăŢ˝{hĐ ě8DE&11†††Xż~=FŚ!;QžŐŻ_˙üóttt ««‹]»vÁŮŮYv,˘2'::ˇˇˇ Ahh(âăăŐ×Ö2{bžŽŽÎś9kk뢎JDDDŮB yóćčŇĄ ~üńGŮqHC§Oź†ŤŤ """вeKŮq¨ S?¬^˝zxđŕúˇJDÜľ}S¦LÁŕčč5kÖđď‹DĹß @DDTF<~üpssCÍš5ammŤ•+W˘Nť:řůçźńôéS„‡‡ĂÇÇööö@DR©T*(•J´lŮ·oßFHH8€¨hiý{i€o ˛¦RĄJčŢ˝;~ýőWŮQňěůó牉Ô­[ááá@$‰™™ÜÝÝŘŘX\ąr+V¬€łł3ŞU«ŕßé×ÜRSS1pŕ@ÄĹĹÉŚMDDDď Áőë×1yňdŮQ(:wî ěŢ˝[v"*áÚ·oSSSŔČ‘#9€č=Ť5Bpp0öďߏ¨¨(XZZbţüůxóćŤěhDeß @DDTJ©T*ś}ЧOźâÉ“'xűö- Nť:°±±‘śŽ({_}ő:wî,;Q‘pttDbb"Ž=*; ĺŇŚ3đË/żŕöíŰ˛Ł”hgÎśÁŠ+dÇ ’*** QQQčŐ«ŞV­*;‘TŮ]'zýú5–-[ÔŻ_ľľľčŐ«W¦#˘t|3Q)ňäÉl۶  ‚‘‘ěěěŚŢ˝{ăđáĂŤŤĹÁ1eĘ ˘bçÉ“'>|8ěííaii‰¨¨(Ě17g˛ô7đYTőë×/^ĽŔéÓ§eG)öěŮż˙ţ[v "Ę…¸¸8šš˘{÷î@ůö÷ßcĎž=…şŽ3gÎŕěŮł…şŽâŞjŐŞ077G§NťŕěěŚ^˝zˇm۶ĐŃŃÁ_ý%;Q–öěŮČŽAT$nßľŤÂĂĂCvĘÜąs˛Ł”h<(ôßDĹť±±1ôőő9€Ę4M®•/_sćĚATT¬¬¬Đ»wo 4˙üóOĄ$˘t:˛Qţ\»v ÁÁÁ űcÇ;vÄĚ™3áěěĚ›ţ‰¨ŘB`űöí6mĘ•+‡€€>™¨Ążâ6--Mr˘˘×¨Q#™™!44=zô§X:u* $;ičňĺËhÝşµěTJ`đŕÁ…ľžNť:ęۨ`Ą_7 * |}}all gggŮQ(Ú·occcü÷ż˙E«V­dÇ)ńŘg§˛îŕÁčÓ§ŹěDŇäć:QÆ ±wď^9r'ND“&Mđő×_cÖ¬Y|€ Qá›J¤¤$„††ÂÓÓĆĆư˛˛ÂňĺËQ§NěرĎź?ÇÉ“'áĺĺĹDTěÝşu ööö=z4†Š7np QK3PYŐłgO=zT&wÄ? IDATv "˘<á@""""˘‚‘€m۶aҤIĐÖÖ–‡ň@ˇPŔŮŮűöí“…J Ę=;;;DDD`îÜąX˛d Z¶l‰ĐĐPٱĘ ""*îŢ˝‹ 6ŔÉÉ Ő«WGďŢ˝qęÔ)|ţůç8qâ?~ ¸şşňUuDT"$''cţüůhٲ%âââpúôi(•JT©REv4˘2‡¨¬ëŮł'Îź?ŹWŻ^ÉŽBDDDDDDD’lٲ*• ŁFŤ’…ňˇ˙ţ¸|ů2îŢ˝+; Q™T®\9xyy!22ćććčŐ«ÜÜÜđäÉŮŃJ5 ""*†RSSqňäIx{{ĂÚÚfffřꫯ«V­ÂtíÚ•Żé%˘ĺčŃŁhÓ¦ V®\‰%K– ,, íŰ·—‹¨Ěâ`*ëěěě’’‚S§NÉŽBDDDDDDD!°víZ¸ąąˇzőę˛ăP>ŘÚÚ˘Zµj|;‘dććć8pŕöíۇăÇŹĂÂÂJĄ©©©˛Ł•J @DDTL<{ö pssˇˇ!şuë†ŔŔ@ŘŘŘ $$±±± ‚»»;ęÖ­+;.Q®=zônnn°łłCăĆŤqőęUxzzň•ËD’ĄBHNB$Gť:u`aaŁGŹĘŽBDDDDDDDüú미yó&&L ; ĺ“®®.úöíËÁDDDĹ„““˘˘˘ŕéé ///´oßgĎž•‹¨Ôá`"""‰®]»†%K–ŔÁÁuęÔÁСC oooDEEáÎť;P*•°··GąrĺdÇ%"Ę!üýýaee…#GŽ`ďŢ˝ ‚±±±ěhD¨ß0Ä7PYÖłgOüńDzc‘«V­‚Z´h!; €ţýűăĉxöě™ě(DDD bĹŠ?>®\ąCCCŘŘŘŔÍÍŤçj˘ÄÁDDDE(99AAAřňË/QŻ^=XYYÁ×צ¦¦ D||Ú·oŹ·oßâěŮłP*•¨\ą˛ěhDô :wî •J…K—.ÉŽRć=z …úúúhŐŞ:uę…B * S§NhѢ*T¨…BŻżţ=zô@Ť5dÇ.:uę„3fh<˙÷ß_`őSˢĚݸq ,Ŕ A°}űvŮqŠĄÔÔT,Y˛Ýşuc[,A 뼒ŰcbYĂăvî•´6ĺëë oooŘŮ١{÷î¸uëV±Ţ†7oŢŕ»ďľC—.]Ř.‰čJĄ&&&čŰ·Żě(T@*W®ŚŹ>úűöí“…ňýx9ŘŹĎ˝ü´©ÄÄDŕСCśŞheÖ×Î϶ő~*s}™]Ź+ ×č † †ëׯĂŐŐ#FŚ€˝˝=nÝş%;Q‰ĆÁDDD…ŕÉ“'đ÷÷‡““j×®Ť‘#GâŃŁGX´hîßżŹłgĎböěŮhÓ¦  …ě¸DDęŔhŢĽ9”J%–.]ŠóçĎŁ]»v˛cQ8€077GíÚµqúôiŮQĘĽäädôěŮ>DDDΞ= 011ÁŮłgqőęUüóĎ?hܸ1FŹŤk×®!%%ĄP˛üý÷ß…˛ÜÂPYMMMQľ|yŤçź|‰‰‰˛ŁP°_tŘŹĎ\Qôăuuuń÷ß#)))Oß/.2ëkçgŰŠz?Íl}…ŮVß]_f×ăĘŇ5:¬^˝áááHHH€••Ľ˝˝ńúőkŮŃJ$ ""* ŃŃŃP*•čÚµ+j×®ŤńăÇ6nÜçĎźăäÉ“đôôD˝ző$'%"*111pqqŁŁ#şté‚7nŔÓÓS}Ł1Oéű(_ÁIe]ÇŽqćĚŮ1ĘĽäädĚ1+VĚržęŐ«cüřńHKK‘‘Qˇäř믿0tčĐBYvA+ެ;wîÄ‚ 4ž_OOOŁúŃ$ż¦Ë˘üŃÓÓ“ˇŘ+_ľđňňBŻ^˝Ę\'ťĘ¦´´4řůůaćĚ™022ÂożýٱHCéo)⛨¬ëÜą3V­Z%;F™×·o_”+W.Çů&LPh˙ůç8::jôÔXŮJRÖĚ”ôüDTü‡óJiW”Çň’tŢ(IYsëÁ066–ŁPÇz+Ž™J‹´´4¬]»#GŽ„ľľľě8TŔjÖ¬‰Nť:aßľ}0`€ě8”KěÇ>öă3WÔY›5k†FŤÉşŠZIܶ’ÔVKxzzÂŐŐžžžčÝ»7\]]±zőj>HCěĺBJJŠú ˙ĆĆư¶¶†żż?‚‡ÂßßNNN@DeÂźţ {{{xxx`ĸ|ů2•0é,á`*ëÚ·oŹ<|řPv”2­B… ĐÖÖÎq>===čęęŞ˙ýôéS¸¸¸ FŤ°˛˛Bxx¸úł×Ż_céŇĄ3f Ú·oDFFfąě­[·âÚµkxôč‘úŤođňĺKxyyaćĚ™6mz÷îŤiÓ¦!>>iiiřăŹ?0uęTšš"&&¶¶¶066Ćľ}ű>ްaCÄÇÇg›m˙ţý;v,ŚŤŤŹ‘#GÂĐĐ-Z´Ŕ… ˛Íúľ¤¤$üüóĎ:t(lllpöěY´mŰ&&&8uęnÝş…ÂČČ–––ęĺ˙ž1räHôčŃ#ĂrWŻ^ŤáÇc„ (_ľ< …úżweU?šć×dYŔż}SWWWx{{ĂÍÍ Ý»wÇŐ«W{öěAŤ5 P(0gÎőwÖ­[mmmlܸ@îŰKNëÍL~łdŐŢ6l5kÖ RĄJP(X˛d‰úŹ—;v쀞ž¶mۦQ9ç´mŮeŹŹsűČmYk˛OlܸZZZęu$$$`ĹŠ¦ĺgx×íŰ·áěěŚęŐ«ŁC‡8věúłĽÔ]ząQÁ*čóJVÇÄđđptęÔ “&MÂÜąsˇ««‹ÄÄÄLוß6Óľ“Ý1I“ý(·Ű“Ó±śçčěĎqѦ 11 .ÄđáĂáéé [[[(•JŤĘHÓm:pŕĆŹŹÄÄDővŤ? yę+ätĚÎé™SűINNĆ´iÓ0věXĚ™3łfÍʶ óÚƲ˘éţ–Ó˛óŇ_""ÍáîÝ»8q˘ě(THśśśpŕŔ^÷,ŘŹg?ľ¬ôă'Nś¨ľŻDĄRáŰoż…»»;ľůćŚ7K–,AłfÍhvÍ#§ú)ešőµßÝ6Mdw ň}yą~§Éú4­˙ŁGŹBOOUŞTÁ‰'đâĹ > …={öĵk×—.]BÝşu±aÆ\m_YV·n]b˙ţý8ţ<,,, T*y.'Ň„ ""˘l%&&Šýű÷‹áÇ }}}@4kÖLĚ›7O„‡‡ËŽGD$ĹŰ·o…ŹŹŹĐÓÓmÚ´aaa˛#Qýý÷߀8yň¤ě(DRĹĹĹ …B!‚eG‘€Ř˝{·ě š6mšégM›6ÄĽyóÄ_ý%8 Îť;«çůňË/ĹŤ7Ô˙îŐ«—¨U«–xůňĄĆëLHHMš4óçĎWO{ňä‰hҤ‰033Ož<§Oź+VÄâĹ‹Ehh¨pss!!!L3fŚxőęU¶ŮţţűoQąre@,Z´HÜ»wOüôÓO€čرŁFĺ“.--MÜľ}[úúúâŔ"**J&&&bٲeâĹ‹âŇĄK€°µµÍđýű÷ď°___ˇ­­-ž?.„bńâĹ€6mZ®ęG“üš.«qăĆÂÜÜ\!„JĄÂĘĘ*CfâŕÁ¶mčСę祽ä´ŢĚä'ËłgĎ2moéíjöěŮ€¸víZ†e80ŰLB|XYmŰ›7o˛Í IűČmYkşO››‹÷˙ôńî´üîémqĘ”)"$$Dřůů‰J•* mmmqĺĘ•|Őťl»wďţ ě š‹‹‹pqq)Ôuä$żç•ĚŽ‰Mš4Ő«WW˙{đŕÁâÉ“'™®#żm0§}'»c’¦űQn¶'«2ĺ9Zós\~Ű”JĄ¶¶¶břđá"--M!Ä–-[”cĹÇÇkĽMYmW^ű Ůłs:×dWG)))˘cÇŽâË/żT~çÎˇŁŁ“ăq.·m,>>>ÓĺhR¦š.[Ó¶TXŠëď˘ü˛łł}úô‘ ŃŐ«WqîÜ9ŮQJ”˘ř]ěÇł_ÚúńďJMM=zôź}ö™şOź(ŞT©’aĚ隇9×O~—‘źľvN2+ÓĚäĄ jş>Më„ ˘|ůňâĹ‹B!’““E­ZµÄ°aĂÔ󤤤îÝ»g»ľ¬Ö)ű7€ňω‰‰ÂËËKčččqőęUiYJ‚â×{#""*ž>}*¶mŰ&…žžžĐÖÖ666bĺĘ•âÁ˛ăIuâÄ aii)*V¬(|||DJJŠěHD”111€8~ü¸ě(DŇ™ššŠ ČŽ!Mq˝ÉG“?ö¦˙‘L!jÔ¨!*V¬(„âÜąs@¦˙e7đăýu~óÍ7€xřđa†ůüýý1cĆ !„€ŤŤÍ0_fÓ5É–ţ˝wŐŞUKčééiT>9mW˝ző>X~Íš5…AŽßuvvZZZâíŰ·B!"##Ń©S'ő<9ŐOnňk˛¬+Vť;w !ţý#˝ąąąĐŐŐUţöí[Ń Aáě쬞6gÎqéŇ%!DŢŰKNëÍLAdÉŞ˝=ţ\T©R%Ăe/^¬Ń`§÷ë#§mË*CNí#Że­É>‘ŢVޕٴĽîéËz÷ĚJĄR#FŚČWÝÉĆÁšk2[†‘‘‘ ”JĄHKK‘‘‘9Ţ„—6¨IűŇtż}×űűQn·'»›xŽÎŰ9:7u°bĹ @ÜĽyS=-%%ElٲEÄĹĹ庌˛Ű¦ě¶+?}…w˝?-/u´zőj@\ż~=ò›4i’ăq.Żm,39•©¦ËÎM[* ĹőwQ~DFF …B‘ap.•N&&&bîÜą˛c”(˛oţĚ űńěÇż«4ôăßµ~ýz@DDDdşťérę?kRů]F~úښȩüóÚ5]ź¦őź>híÚµęiÎÎ΢rĺĘ"!!A!Äţýű…źź_¶ëËÍ´˘V\Η/_;vşşşÂĂĂC]ľD”‘ ĄR‰®]»˘V­Z7n`ŐŞU‰‰ÁÉ“'áéé‰úőëKNJD$G||<<==ŃŁG™™!** ^^^˝Ş•Š/-­/ đ›D@۶mqéŇ%Ů1(Ţ}Ťµ‘‘’’’aaa°˛˛‚ř÷ˇ(ţëׯźĆË?uę J•*¦wďŢpúôé 9ŞU«–iľw§k’íÝíJW­Z5ĽyóFăěŮy{ zőꏏĎń»HKKĂĺË—ŘŮŮ}0oVő“Ů-kęÔ©prrÂÚµk±hŃ"Ľyó*•Jýą®®.<==Śččh¨T*ÜĽy­[·÷ö’Óz3SY˛joŐ«WÇäÉ“±mŰ6ÄÄÄ~˙ýw|üńÇ9–on·-« 9µŹĽ–uaîąÝŢťŔ€€¨¨¨|Őą=n­[·UŞT§§':tč€WŻ^eÚ¦˛ŁIÔ¤}işßľëýý¨ ¶'łőń­ąÜÔÁ±cÇ ĂukmmmŚ9ą.Łwĺg›rÓWČN^ęč·ß~dXVúďßÜĐ´ü˛Ëţ®wË4?Ë&˘üQ*•hÔ¨zőę%; ˛ľ}űŞĎETú±Ď~|IęÇżëСC€ĆŤgžYŢěDýe_;/ Ş ć—ĄĄ%ěěěŕçç¸wďRSSńöí[ěÜąŕďďŹaÆY¦ŇŞU«V8uę–/_Ž­[·˘U«VęvHD˙Q™véŇ%|óÍ7hÖ¬ĚÍͱhŃ"4mÚűöíCll,‚‚‚ŕî5kĘŽJD$UPP¬¬¬°{÷nlٲÁÁÁhذˇěXDTŇ/Đ !$'!’ŻM›6¸xń˘ěT€ž?ŽčččL˙ř››APéÇĘżţú+ĂôZµjôőőĄe“eҤIřńÇ1zôh|ýő×6m,X€ Hˆ-ZŔĚĚ łgĎFĺĘ•?gĚ1¨T©VŻ^Ť˙ýďpqqQ–×:Ńd˝™)Ś,éľúę+”+W+W®Ä… СC‡< âÍë¶ĺÔ>Jzű_ú± AĄnŰH3ź~ú)._ľŚŢ˝{#<<ÝşuömŰ |=š´ŻĽî·ď*Šíá9:{ą©ÇŹţüóĎL?/Ś2ŇDaörŞŁţůG=_~fůÉŞ˘˛...;v쀧§g‘ݸHňôë×/^T”&zűńąÇ~|öňZ÷ďßđ˙űöyUeP”}íĽ(őśnҤI@XX–,Y‚ĄK—â“O>ÁĆŤT¬X±H3•VÚÚÚ}úŕřńăxřđ!~üńG8::ŞźTDDT–ĹÄÄŕ“O>A˙ţýagg‡k×®ÁÍÍMv,"*@éO–áÍiD˙>aćţýűxůňĄě(T@š6mФ¤$,Y˛$ĂôëׯcőęŐY~OˇP %%Eýďô§’˝˙4Áěíí‹,[NY‹Jjj*"##qöěY,[¶ ˙űß˙0gÎś\ßp^ůÝÜÜ R©ÔOŔĎěÜVµjUŚ3›7oĆîÝ»1pŕ@őgy­MÖ›™ÂČ’®FŤ?~<ÖŻ_ŹU«VaÔ¨Qez_^·-§öQPí?3é}›·oßř÷ZĐ‹/ňµĚś¤  uۨřš7oĚĚĚpčĐ!ěÜą*• łgĎ.đőhŇľňşßľ+·Ű“—c9ĎŃŮËM´jŐ °hѢ ĽďÝ»‡JiB“ľB^ŹŮ9ŐQÓ¦M|¸Íš(Š6–ŰeËęď•V6l€––†.; ž={˘B… 8xđ ě(T ±Ď~Ľ&Ys#ŻmŞQŁF€ŕŕŕ Ó_ż~ýA6 ëţł&eße䵯ťššš«ůłú®¦őś×őĺ¦ţťťťallŚůóç#11Íš5øqㆠ&`üřńyĘ@Y«[·.öîÝ‹   =zŤ7Ɔ dÇ"*Q)ćÍ›'7n,†  qâÄ ‘––&;Q±“šš*üüüD•*UDŁFŤDhh¨ěHDTHž?.ŮQ¤»uë– Îź?/;ŠÄîÝ»eÇČŕŐ«W€hĐ A¦ź×­[W/_ľTO«S§Ž ^˝z%^ż~-ĚĚĚ1jÔ(ńóĎ?‹Ůłg‹^˝zeřÎű5j$*UŞ$îßż/„"))IXYY‰úő닇Şçóôô666BĄR !„011QŻű]™M×$[ú÷ŢUŻ^=@˝Î÷łf%99Yęiććć€HHHř kjjŞzZBB‚ ęÖ­«ž¶`Áann.6mÚ$:$Nź>-nÝş%RRRÔóäT?ąÉŻÉ˛ôőő…Bˇżýö›řůçźEÍš5qîÜ9ńŕÁő÷îŢ˝+´µµĹÂ… 3¬#ŻíEÓőf&?Y˛joé=z$ôôô„­­m¶Ň%%% ÂÄÄDămË*CNí#Że­É>1pŕ@@Ě™3Güůçźâ‡~Ő«WġC‡DjjjľöKKK@ÄĆĆŞ§M0Aôďß_ămË©îdŮ˝{÷ĺ[Đ\\\„‹‹Kˇ®#;ů=Ż‘ů1±bĹŠ"..N!„JĄúúú˘cÇŽYćČkÔ¤}işßľëýý(·Ű“ٱśçhÍĎqůmSŃŃѢRĄJ€°łłkÖ¬sćĚcÇŽiiią.Łě¶)66Vfff9n&}MŽŮy©ŁË—/ QŁF qčĐ!‘””$Ž9"ŞV­*»wďfYymc™É©L5]¶¦m©°Çß Dy•’’"LLLÄÔ©SeGˇ"ÔŻ_?1pŕ@Ů1JŚ˘ř][ěÇł_űńď:rä ôőőĹŽ;ÄĺË—ĹęŐ«EĺĘ•3äÍ©˙ś”””cäwyék/Z´HT«VMüő×_ą.Ó÷ż«I=çg}ąí{/\¸P( ©žÖ´iSáää¤Ńú2»—Ů4Šăůŕ]ńńńÂĂĂChii‰>}úäXßDĄ]ńÝ[‰ň!55Uś8qBxyy©€›ššr‘"""DÇŽ…®®®đňňŻ_ż–‰ Q\\ś >,; ‘t)))BOOOlßľ]v)ŠŰM>‡_|ń… qăƉcÇŽ !„HKKË–-S6eĘńęŐ+±téRő´iÓ¦‰7oŢżţúK8;;‹ęŐ«‹Úµk wwwńôéÓl×=sćLQ§N±wď^ő´„„1cĆ Ń«W/1mÚ41cĆ ±`ÁńćÍ‘(,X ^·»»»¸téR–ÓÓe—mÍš5ęď-\¸PĽxńB¬\ąR=ÍŰŰ[$''gšő}Ź?_}ő• ôôôDhh¨8|ř°ĐŃŃ„‡‡‡xţüąđőő …BK—.Ďž=‰‰‰bćĚ™ęő®X±BĽ|ůR„„„Zµj©§§˙gdd$öěŮŁqýä”?7u˝fÍˇŻŻ/:tč Ξ=+”JĄ¨V­ščßżżxţüy†ĺN™2ĺi9ŐIVrłŢĚä6KNíę]ŽŽŽÓ˘ŁŁ…‡‡‡z™+W®qqqYn›2eJ–˛kéuťŰ˛Ötź¸uë–čŘ±Ł¨T©’čŐ«—¸uë–čÖ­›>|¸Řµk—xđŕAž÷‡ômsrr¶¶¶ÂÝÝ]xxx5kÖd0Pu'Ci P畸¸¸LʉD۶m…ŹŹŹřüóĎ…ŁŁc–7çç,DÎűNvǤ˙üç?íGąŮ!2ž7yŽÎÝ9:«ólnëŕęŐ«˘wďޢZµj˘^˝zbĘ”)âĹ‹•‘¦Ű&ĆŤ'---ńí·ßŠ<őŇË$»cöćÍ›ĹěŮłóTGBqüřqacc#ŞT©"ĚĚĚ„ŹŹŹčŢ˝»řěÝy\ĺţđĎa_D"KQ—Đ0»-ЉĄÖU)MQKŃ[¦âIQÓÔ4sĂr_ŇĘ\r É®ąÜLÜĘ ŻZn(š)‚K˘ €€pŕ|ř:燳ĚĚ™sćřľ_/^ĺś9ó|ç™gž™ç;3gFŚAűöí«Đo[ŇĆŚÚN„,[H[˛&{'0f‰¤¤$rrr˘+W®( łˇ+VPŤ5řZ‡@övó'źÇóy|U>Ź/oÝşuÔ´iSň÷÷§ž={ŇĹ‹)$$¤Âţh.ç!dűȱ ±çÚ .¤† ŇŤ7Dשˇďš‹Ď’ňÄž{ß»wŹĆŽ[aÚš5k(%%ĹlygÎś©”Źűý÷ß ćč”`oÇc>L-Z´ ///š;wn…Đ«NTDĺŢÉcŚ90ŤFŁGŹ")) ?üđnÝş…ŕŕ`ôčŃ}űöE‡”‘1ĆěZQQ0gÎĽôŇKřꫯđěłĎ*cĚĘňňňŕë닟ţY÷ bĆŞłĐĐPôęŐ ź}ö™ŇˇŘśJĄBbb"˘˘˘”…9€5kÖŕŢ˝{?~<€Çcň[·náŔ7nîÜąŁp„¬°°­ZµÂŮłgáééiÓ˛ą}8¦-[¶ _ż~°ćeŁľ}ű’’’¬VcĚ>đ± ęŕq«JÂĂĂQ»vmüôÓş'Z> IDATOJ‡Âl(33 4@rr2^{í5ĄĂ±{¶0Ć„iŢĽ9ŇŇŇxdŠp¤ăZ­Ć‚ 0mÚ4´hŃ_ý5^|ńEĄĂb̦\”€1ĆłDYYŽ;†¤¤$$%%áöíŰhѢ>řŕDEEˇE‹J‡Čc!99#FŚ@vv6/^ŚaÆÁÉÉIé°c6 Ý×!™Ç-„„„ --Mé0łk 8q"˛łłuÓśśś: ^˝z FÇ´–/_ŽŘŘX›?Ŕí1Ć cöčôéÓ8tč’““•…ŮXýúőŠ]»vńĂŚ1ĆXäęꊏ>úÝ»wǰaÆ‘#GböěŮđööV:<Ćl‚ďîaŚ1ćpĘĘĘpřđaÄĹš^˝zčر#öîÝ‹aÆáâĹ‹HMMĹôéÓůAĆŕţýűř׿ţ…nÝşáůçźÇ… 0bÄ~€±jDĄRxü+ŤŚ1~€1!> Xąre…›ü~˙ýwLś86lP*´jďřńăhŐŞš6mŠ/żü#Fڰy Ü>cŚń±€1fŹ–,Y‚-ZŕŐW_U:¦€îÝ»cÇŽJ‡Ác‚rrrĄĄĄ GĂc Ĺ‘#G°|ůr¬]»­ZµÂľ}ű”‹1›ŕ;|cŚ9„ââběÝ»qqq¨[·®î€#FŕŇĄKşBBB”•1ĆĆŽ;вeKüüóĎHLLÄÖ­[Q·n]ĄĂbŚŮöá~€±Çžyć\ľ|™/°0fÂşuë‹Ő«W#00íŰ·GTT~˙ýwlذÎW··7ňňňŕää„M›6ÁÍÍÍć1pű`Ś1ĆÇĆ˝ą{÷.6oŢŚŃŁGë~U/Ý»wÇ_ý…?˙üSéPc̬´´4Ś3YYY€‰'˘  @á¨s *•J÷c˛­ZµB—.]]áAuĆŞ"Ą`Ś1ĆŚyôč’““‘””„íŰ·#77-Z´@LL Ţ}÷]4mÚTécĚ!Ýąs±±±HJJBßľ}ńĺ—_Âßß_é°c ᇫ($$%%%HOOG“&M”‡1»äçç‡%K–`É’%J‡Âô„††âÚµkŠĆŔí1Ć cöfŐŞUđôôÄŔ•…)¤m۶đőőĹž={ř3cĚî…„„`ѢEX´h‘ҡ0ć°ęÖ­‹­[·bÇŽ‰‰Ahh(­thŚYż€1Ć])**ÂŹ?ţ o˝őŇÓÓńé§ź"33S÷NŇ0Ć4IIIxöŮgqâÄ ěŮł[¶lá«ćřaĆ* JĄÂĄK—”…1ĆcŚ1ĆcR«ŐXµj>řŕx{{+S‹‹ """śś¬t(Ś1Ćłˇž={âüůóŚŚÄ!CĐłgOdff*c˛ă‡cŚ)®¤¤;věŔŔQ§NDEEáďż˙ĆÜąsqăĆ üöŰo‹‹C`` Ňˇ2ĆĂJOOG×®]ŃżĽýöŰ8wîşté˘tXŚ1; }€Ž„1űŕăă§ź~iiiJ‡ÂcŚ1ĆcŚ1 ýđø}ű6Fڎt(La]ştÁţýűˇV«•…1Ćc6T«V-¬Zµ Äźţ‰ĐĐP,^Ľ(ŤU)ü0cŚ1E”••áđáĂş›ü###‘––†Ď>ű ™™™8pŕţýďăé§źV:TĆshŤ_}őZ¶l‰[·náČ‘#XµjjÔ¨ˇthŚ1;ˇR©đ›+/$$—/_V: VMĺććÚ¤ś¬¬,$%%aöěŮ6)ŹYź­ÚŽ1yyyŠ–Ď–Ňű‚–­űY{Yoc¸Ź`¶b/ű÷ŚŮŹĄK—âÍ7ßDŁFŤ”…)¬k×®ČĎĎDZcÇ”…Ůź‡>VĎxŰ2ĆÄ Çü1cĆ`„ Ç… L~gĎž=ČÉɱQ„ŚIÇ0ĆłŤFŁ{ ^˝zčر#öîÝ‹‘#GâĘ•+8yň$âââřĆ“Ijj*Ú·oŹQŁFáß˙ţ7Nť:…°°0ĄĂbŚŮí›řaĆţ_Æ ‘‘‘ˇtUĘŇĄK1qâDtîÜáááü°…žââbĚž=íÚµżżżŐËKKKĂŚ3…ďľűÎęĺ)©Ş·=kµť@ĄRÁ××­ZµBXXT*<==†–-[ÂÓÓ*• ăÇŹÇ+ŻĽ"¨ü°°0L0A¶8Au\g%Řş5ÇVý¬˝­·!_|ń…Cô‡ƤI“ R© R©0xđ`lßľ˝Â€1űqęÔ);v ŁGŹV:fš4i‚ŕŕ`$''+ ł˛˛˛2$$$ cÇŽŐúYUĎÄŚ1yrŹĂŞÂ¸Ič:pÎÇ1yzzbúôé8qâÔj5žţyLś8ĹĹĹ•ćÍÎÎĆ;ďĽwŢy‡ŻŁ2»Ç0ĆłşS§NéŢ }`ĸtéRSS1}út4nÜXé0c¬ĘP«ŐHHHŔ‹/ľGŹ!%%sçÎ…»»»Ňˇ1Ć씓“'±+'007nÜP:Ś*cÉ’%řřăŹ1kÖ,üç?˙A@@@•ü52K¸»»cěر¸téĘĘʬ^^HHćĎźoőr”VÚžµÚNQQ"""pűömś9s)))€   ¤¤¤ŕÜąs¸yó&š6mŠ÷ß©©©(--5»ÜFŤÁĂĂC¶8퍡cGU_g{aë~Ô[őłö¶ŢZĺ÷…ŘŘX‡č#:tč€9sć aÆ€•+W"22R÷yu8¦XJčůł5Îłím_¨î}cödѢE Exx¸Ňˇ0;ѵkWěŮłGé0•9;;#...\t*79Ďw,YVU:W:ư´,{'G¬rŽĂ”7ÉąÍĬˇşs¤öSÝ=÷Üs8vě–-[†ĺ˗㥗^Şôö ?üąąąHNNć7ě2»Ç0Ć3éôéÓ8p čďioňoҤ ^zé%ěÝ»Æ ĂĹ‹uź5kÖĚ 3ĆXővúôi„……áÓO?ŧź~Š“'Oâ…^P:,Ɲ㇫(00™™™J‡Qe|ůĺ—¨WŻśťťáëë‹­[·âĺ—_V:,»ăáá'ź|ŇfĺU‡E«KŰłFŰ)**„ ŕĺĺet???ÄÄÄ@ŁŃ @Đr7oŢŚ3fȦ]IOOÇ;ďĽSizU^g{cë~Ô[őłö¶Ţúű‚»»»Cőžžžţ«U]Ž)RëĄÎ'…˝í Őµ`Ěždee!)) cĆŚJĄR:f'şté‚“'OâŢ˝{J‡Â¬L©c¤śç;r,«*ś+X2ư´,{&W¬rŽĂ”7É˝ÍĬ~Ý9RűaŹ999aذa8{ö,ęŐ«‡öíŰcřđáČËËĂĎ?˙Ś 6 ´´Ťź|ň ~ůĺĄCfĚ(Ą`Ś1fżľűî; :jµ3gÎDPPÉůSSS‘””„Í›7ăňĺËhذ!Ţ|óMôíŰ:t°MĐŚ1VMáÓO?Ĺ_|víÚáôéÓüĐcL0•J"R: ĆěF`` rss‘źźĄĂqx™™™¨_żľŇa°jŰžt˙üç?áććfvľ‘#GÂɉsčćÍ›čŃŁ‡Ă˙Ň"c–ŞĘűSŚşÝ«rű`ŚŮ§+VŔÇÇ P:fG^{í58;;c˙ţýŠŠR:VĹČyľĂçNŹŮ˛©Îí5V[Ź›¬QR×Á^· ¦QŁFŘ˝{7ÖŻ_ʱcÇb×®]xôčQ…SS©TŠŠÂ™3gĚŢ?Ç8KĎc¬µZŤ¸¸8 <jµ...řţűď Î{íÚ5$$$ E‹ Ĺ·ß~‹×_‡µk×°xńb~€1ƬěСCxţůç±rĺJĚź?äc˘đ›«H›ěçWúš–——‡Ź>ú“&MB||<şuë†řřx|8¦NťŠŹ?ţŘd™Bë]hůú6nÜooo¨T*$$$č.\mÚ´ îîîX·nťÁďýđĂđ÷÷‡JĄÂÔ©SuÓżüňK8;;ă믿–Ľm–/_.)&SmĎT:pŕÜÝÝáăăC‡!77 ‚JĄBDDRSSüńęÖ­‹ŻľúĘh}šjSR۱–¶#¶Mëóôô„łłłŮůÜÝÝáęęŞű÷Ý»wuűohh(Nž< Đh4HJJÂ!CđĘ+ŻHŽÓÔüćöŤÂÂBlܸďĽóÚ·oŹ””Ľđ  ‘#GpůňeôęŐ hŢĽyĄýĹÔvY»v-RSSuíÍŘ:[3)őéč„ě ćÚ…”ý^j?+$Ŕô1Fčzbé±KČqÂĐľPž>BL=/[¶  ÂČ‘#ááá•JĄűł”óˇÇRsíŕ믿†“““.ţüü|,X° Â4C¤Ç,mć¶»–±ů„ě†p ®`¬ş)))ÁŞU«0lذJo{aŐ[Íš5ńňË/#99YéP ]ąr‘‘‘đóóCëÖ­qđŕAÝgćĆY†{;ß1v|7•Y´h‘ÁeÉ‘30D®|SAAfÎś‰A!..ť:uÂâĹ‹%×·Ô1†śe™Şc۰~ýúřé§źŚć"MĹfÉ9¸Ľ€Ąă°ň Ť›†Ž˙ţ÷żFë@Ž6't,bn;[cc?Cu'&fż˘ŁŁqńâEřřř 77·ÂµSŤF˘˘"ôéÓ%%% FÉÄcŚ•s÷î] 'ggg ű ŃÍ“žžN‹-˘öíŰzâ‰'hذatčĐ!Ňh4 FĎcŐËhذa¤R©¨{÷î”™™©tHŚ1ĺééIk×®U: ĆěFNN _~ůEéPl %&& š7??źš5kFÓ§O×MËĘʢfÍšQpp0=xđ ÂrËŹ)ő•••ŃÎť;ÉÓÓ“Pll,ýöŰo´iÓ&ňńń!täČ""úŕ(--M÷Ý®]»Rť:u(//Ďäç÷îÝŁŁGŹ’—— 9sćĐŢ˝{ičСôđáCjÖ¬ůůůéľ×Ż_?ĘĘĘ2sÓ¦M©qăĆDD¤V«©V­ZJDD7nÜ 5jš5k]ż~ť6lŘ@¨M›6DDTZZJmÚ´ˇ>ř@·Ěżţú‹\\\ČTĘVH˝ )_KŰL™2…PjjŞnZFFőęŐËhLDDK—.%ôóĎ?WřŢ;Cű·Ôm#5&Cëg*m9r$yxxPnn.Qť:uhŕŔşď”––Rxx¸É˛Mµ)©í8//OpŰۦ…0µ/‡„„š6mĄ§§Ó®]»µmŰV7OFFFĄeŤÓŘüBö ŤFCW®\!äëëK»ví˘ . ˘Ď?˙śrsséŹ?ţ Ô©S§ e›Űn†ęGť­ĄŰ=11Ńd$‡>}úPź>},^Ž}Ač±Jě~/µźŹ©cŚÔㇹĺ ]'!}˛ˇ}AJ!4¦ĄK—’łł3eggŃś9sĹÇÇ›¬c´±ę3w>Łe®Ž„¶ĆŤWŠĂĐ4}RŽcr´ ˇőŁ?źsĘň¸ßXBĚ81{±~ýzrqqˇŚŚ ĄCavhÚ´i¨tvËă[ŃžŰ}řᇔśśL«V­"ooorvv¦łgĎ‘ůq–!¦Ć^†Î‹Śß‹‹‹MćDÄć5”Î7©ŐjęÔ© 4HwßČš5kíرCr}KcČQ–ąşÉĘĘ2¸ ŁŁŁ)99Ůč¶5›%çŕbóRÇaBęĎTűţűďżeËq ‹Č™Ë6Vwbľë(ŞŇń@Ś’JĄŞpĎ\ů?ŠŤŤU:LĆ*©~{+cŚ1ŁNť:EuëÖ%WWW'4ńńńôŇK/zňÉ'iäČ‘tđŕA*++S:tĆ«vvěŘAT§NZ·nťŇá0Ćś··7}űí·J‡Á]©QŁ­^˝Zé0lJĚM>“'O&tűöí Óׯ_Oh„ –+ä"HłfÍč¦-Z´P˙ţýéřńăFđ;wî4ű9Ń3ĎŮP›—ÚG‰)22’śśś¨¤¤„Îź?O(,,Ěd}céĂćęHh;0‡±ŘʓҞĺhBëG>1ýwyÜď,!fśŔ­Ý˝{—nÝşUizëÖ­©_ż~ DÄÁ‘#G ö§ě±Ştó§öü©ü¸hńâĹ€,hśe©±—ˇó"ˇç[ú9±y ĄóM , téŇ%Ý祥Ą´fÍş˙ľäú–2Ć«,±uŁż M“'+OČ9¸”Ľ€”qĐe«9sśBĆ"ÖČešWĚwAU:UXXHAAA•~@×Đßúőë•—± śŔcŚřî»ďжm[deeA­VWúÜÍÍ _ý5š7oŽíŰ·ăĆŤXľ|9^yĺ89ńá„1ĆlĺÎť;ŽŽFĎž=Ѷm[ś?ŃŃŃJ‡Ĺs ÇŽĂŢ˝{u۶mżż?Îť;‡¤¤¤ YYYJ‡Ëb‘™™©tvëČ‘#ź ÓĂĂĂGŹ˝LíŘŇËËK7-22Ŕăשź8qˇˇˇ Ç?pRáŻ{÷îf?•J¨]»v…˛żüňKřřř ..­[·ĆÇ+­[ycĆŚAĎž=±bĹ Ěš5 ĹĹĹĆŇÚrĘ«]»6Š‹‹{öě¬c„Ö»ąňŤńóóCll,Ö­[‡[·nöíۇ×_Ýä÷\]]‡ť;wâęŐ«P«Ő¸téžţy°hŰHŤÉ!q4oŢť;wĆŞU«×Ż_GYYJJJ°yófŔúőë1pŕ@“ekS–¶cˇmGl›–Ků¶€ÂÂB“ó‹ŤÓŘü–ôI†ĘóóóÓ˝&Öv,!W Jmw%Ů„¶ )ű˝”~Vh<¦Ž1RŹć–+tť,í“ĹöBbęŇĄ 4 víÚđđđtîÜYPLr3WGÖ8‡*OJ{–ŁmH%µ>¸0ľŢŚU7§NťBÆ 1`Ŕ?~Ŕăýé˙űFŹ­ptĚ^µnݵjŐŇő«¬ę+ü}ë­·.\<ÖłVI?'˘O®ś>ąňMđ8ŻŞĺěěŚ!C† V­ZV[cČU–ŘşŃ߆†¦‹É“•'ä\ŽĽ€Üç˙†ęŔÚ9N}Ö‡±Şă“O>AFFĘĘĘLΧR©0|řp\ĽxŃF‘1fgc¬š+--ĹG}„ččh””” ´´Ôŕ|%%%¨Ył&Ö­[‡ž={ÂŐŐŐĆ‘2ĆKJJBhh(öíۇm۶aË–-xâ‰'”‹1ć`VŻ^Ť.]şčţzőę…ŚŚ ,\¸QQQşżÂÝÝ]épSLÝşuńçź"-- )))Ř˝{7ľ˙ţ{¤¤¤(š]Đ^DLOOŻ0˝Nť:___YĘ©[·. ~ýúČÎÎĆŐ«W Ţ8¨ŃhĚ~nĘŰożŤÓ§OŁ[·n8yň$:věuëÖť˙ĉhٲ%‚1eĘÔ¨QCÔzÝĽyťť-ę{¶¨÷±cÇÂÍÍ ‹-©S§Đşuk8;;›ýŢСCáííŤeË–a۶mčÓ§Źî3K¶Ť%1éǨQŁpćĚś8q 7oz÷żţ.\@PPP…‡V 1Ö¦,mÇBŰŽŘ6­±q›ßÚű†ĄmXBbp”í.!ű‚v!Ç~oŽĐxLc¤?Ě-W ąúdąŚ5 ß|ó Ţ˙}Ś?ńńń1cf̡XL¦ęČÇr±íY®¶!…Ôúŕ>@|ŔXU•““ŇŇRüđĂ Ă?ţńÄÇÇă˙řÚµk§txĚNą¸¸ S§NŘżżŇˇ0hŹ˝ 4<ÖłuIK®ś>ąÎQďÜąŕńŹzb˱µ\eYăüÝšőŕ(y[ĺ–•*Ź9¦ż˙ţß˙=4 śťťáââbt^"‚Z­Ć›oľ‰‡Ú0JĆŚă‡c¬»uëÚµk‡ůóç š˙ĆŤřß˙ţgĺ¨c¬z),,ĉ'LÎsëÖ-ĽőÖ[čׯz÷´4Ľůć›6Š1VŐĽűî»fçqqqA×®]9ĘŞŤümÚ´A“&MđÄOŔÝÝű÷ďÇĆŤŃĽys´mŰoĽń €«WŻ*®]Đţj’öWxµ´oSxíµ×d)G{ńňµ×^CHH ‘Paž‹/bٲef?7eÚ´iĆîÝ»±yóf¨ŐjL™2ĹčüŃŃŃP«Őş_ů{ˇ.$$@ĺú3Çőîď\ąK–,Á{ď˝'č{5kÖÄСCńí·ß"11˝zőŇ}fɶ±$&}B㌌Dýúő1}út E‹1bNś8‘#G"&&ĆlYĆÚ”ĄíXhŰ1צÍýş•­Ý÷ŚÍoí}CHŰQ©TFdCBb[źŽLČľ ¦]ȱߛ#4SÇ©ÇsËĂ\źlí}A_YYÎź?Ź””|ţůçضm¦NťZé[ö{¦ęHh;ĐţfII €Ç7äćć *_l{–ŁmÝîúóIíżąß0VUeggĂŐŐU×·ś={ÇŹÇ•+W0qâDÝŤ±Śé‹ŔŻżţj7c#f;ÚcoŹ=$ç+LŤ˝ ťI=ßŇ_–\9}rŤ©[µj5kH7ýúőëřůçź%×·”1†\eY#ß`ižĚX¬€ăää¬W!íĂVąl[ʇ™Ľžzę)dff⯿ţ·ß~‹ýë_¨WŻ€ÇŰV˙á€ŇŇR\»vMr®š1ŮcڱjéđáĂôÄO««+ôçććFqqqJ‡ÎcUJtt4R^^^ĄĎ4 ­ZµŠ|||¨I“&tŕŔŰČ«rĘĘĘč©§ž2yŢçääD›7oV:TĆlćöíŰäââbvL¤R©(++Kép­%&& š·°°BCC)00nßľ­›GíŰ·'µZMDD999€‚Í.3$$„Pii©nÚşuëčĹ_$µZMŹ=˘ŕŕ`@ď˝÷mܸ‘¦L™B]»vĄĽĽ<łźzřđa…˛˝ĽĽčţýűDD¤V«É××—Ú´ic4V___R©T´gĎÚ¸q#=ůä“€Ž?N™™™şrĘ«WŻ µZM§Oź&ň÷÷§Ý»wSaa!íßżźjÖ¬IčÚµkŐ»ąňµË@AAA•ĘůűďżÉÝÝť:uęd´ ąví9;;ÓĚ™3+L·dŰHŤÉPۇÖĚ™3IĄRŃůóçuÓBBB¨gĎž‚Ę7Ö¦,mÇBŰŽ©6=kÖ,Ş]»6Ą§§ Z"˘‡jĐ ÁĎëÖ­K*ÔăÓO?]a›ćççŞ[·®Ůz[ŻB÷Ť˘˘"@Ď<óŚnžĆŤĘĎĎ×MӶDz˛2"Övš4iBŢŢŢ”‘‘ˇ[ގu¶f bëS_bbbĄľCn}úôˇ>}úXĽ!ű‚Đvˇ%fż—ŇĎ ŤÇÔ1fçÎť’Žć–+äŘUž©>ŮĐľ µŹÓŚ3¨qăĆ´zőjÚ˝{7=z”._ľ\á|BLż× A@şibÎg´ŚŐ‘ĐvĐ«W/@S§NĄ?˙ü“.\H~~~€vďŢ­ëŚÓžĺh†¶»!úó‰ÝOµ¸0Ý|ńĹÔ˘E ŮĆőbĆ ŚŮÚŚ3ČÝÝÝŕŢĹĹ…\\\hŕŔtňäIĄCevćěŮł€Nś8ˇt(vÇă[iŢĽ9 śśÝ´‘#GŇ›oľIDâňĺ™{:/zľĄźŃ_–\9}rĺ›®^˝JŢŢŢ€:wîLË—/§©S§ŇđáĂIŁŃH®o)c ąĘ[7úŰĐĐt1y2cum¬^Ä椎à 16n2Trć8…ŚE¬‘Ë6TwBÇEŽ˘*,qőęUZ»v- 2„ę×ŻŻ»ŽZţtéŇĄJ‡É—Ăë¤) IDAT3kÁ‚8věŇa0ĆhýőÎś9ŤF'''Ý+±ĚQ«ŐXąr%nܸˇűe"¦Ś¶mŰběرJ‡ÁłĐęŐ«±~ýz8;;㣏>Š+tź]ąr|đ>ŚřřxLź> FË«*śśśŤ… B­VśÇŐŐ=zô°qdŚ)ç©§žBź>}°uëVŁűđř×­l™ýňôôıcÇđŮgźađŕÁhٲ%śťťáďďŹýű÷ĂĹĹçĎźÇňĺË<~óŚ3đÖ[oáąçž3ąěE‹aČ!Đh4¸}ű6~ýőW¸¸¸ŔĹĹű÷ďÇčŃŁ±mŰ6ü÷ż˙Edd$6nÜ0úąłł3>űě3Ý« ÇŽ‹<˙üóż­éŐW_ETTÎť;‡Ž;béŇĄFcś={6>ţřcL™2K–,ÁäÉ“1}útĚž=/˝ô’®śYłf!66kÖ¬Ńý"ĺÔ©S1mÚ4ěßż“&MBßľ}€aÆáůçźG‹-pőęU4hĐ Ňx]H˝ŻX±ÂlůŃŃŃXąrĄnŰ,^ĽF­Zµ<~Ev—.]ĐŻ_?!ÍA'((±±±•~=Wű¶ )ŰFKLL¦Úžą6¤5bÄäääŕŮgźŐMű裏мysAua¬M™Ş síŘÇÇ­ZµÔvLµi///Ô¬YÓä«®ËŰłgľ˙ţ{@FFbbbĐżĽňĘ+ "Ěź?·nÝ|ňÉ'9s&V¬XŰ·oxü«tS¦LÁĽyó<~óŮÂ… 1tčPŃűž±ů…ěYYYş_ÝKOOÇľ}űPVV†ëׯ&OžŚiÓ¦aÓ¦MşióçĎÇ{ď˝§[Ž©íÖ·o_¬]»'Nś@ýúőQXXŮłgWXçČČHݸĎ1­OG&t_0×.Ęşß[ŇĎ ‰ÇÔ1ćŰoż•tü0·\ˇÇ.m^ÂTź\~_ ”ÜG”””Š©m۶Xľ|9Ţ˙ý q`ĺĘ•čÝ»· ~ďđáĂصk222Æ CTT‚%ťĎ«#!ý$$$ŕÖ­[X°`Ž?ŽeË–áÇDPPeĘ<űěłFs"úË2ŻĐs$k囦M›†””Ś7˙űß˙péŇ%ôíŰóćÍJĄ’\ßRĆłg϶¸,!ç«%%%3gNĄmجY3Ěź?ßŕ¶5WBëÚP[“0”·:Óżfm(×µkW$''¬9Űś±ÜąlCu7tčPÁă"öرcǰ`ÁĄĂĄM›6xîąçp÷î]Ü˝{YYY(((@\\ţóź˙ŔĎĎOéY5ačľA1łúöí‹””„……) cŚ öŕÁÝ«®¤jÚ´)ߪ í±'))IéPc8wî^~ůexüją={ö S§N?>¦M›†¬^˝/ľř˘ÂŃ2ĆŞšłgĎę^Ů«ĎĹĹo˝őźk°jçرch×®ťŃĎÝÜÜđŃGaĆŚ6ŚĘ¶T*ĄHůÍ›7GZZšŃ9í˘U«V8{ö,<==•€}ĆÄXUłeËôë×ĎŞýpßľ}€Ď5ś=őÉkÖ¬Á˝{÷0~üx€FŁÁ­[·pŕŔŚ7wîÜQ$.{Ş#V=]ľ|ŃŃŃHII±xYJŹ3eđŕÁذa4ŤÁĎ]]]Ńľ}{ěŢ˝îîî6ŽŽŮ»~ýú!??˙ýď•Ĺ®Řb\ŔcĚţYűx ]~ź>}¬˛|[yôčîŢ˝‹‚‚<óĚ3üđ)ł:c÷ ň›,Ä7c2Ćł5íESĆăĘĎĎGŻ^˝PVV¦›ćää„" ýő>űě3Ś3FđŻu2ĆĎ=÷BBBpéŇĄJIĽ˛˛2 0@ˇČSN۶mŃŞU+ś;wÎŕM%%%čÖ­›‘1f{Ë—/Gll¬]ÝĽhŹ11ĆXue/}rBB&NśěělÝ4'''˘C‡¨WŻžb±ŮK±ę©°°K—.Ĺ7ß|Łt(ŚYÝÝ»wM>Š;vđĚ Ś?jµ®®®J‡ĂcŚUK|ď-câ»oď,bŚ1ĆcĚĆŚŚŚ ÝkŠÇ7ßfggĂÓÓgĎžE“&MŚ1V 2S¦L©Đ€‡‡Ţxă …˘bLY~ř!Ţ˙}źyyyˇuëÖ6ލz)((Đý×ŰŰ[áhŞźăÇŹcذa(,,DYYŇŇŇ”É.cbڱęĘűäÇV®\‰áÇĂßßđűďż#!!6l°i<öXG¬zşző*fĎž ĄCaĚ겲˛ NwqqAPP’““QŁF GĹEçÎťńđáCś8qÂäŰ"cŚ1ĆłwNJŔcŚ1ĆXu˛páBl۶ jµşŇgĄĄĄ¸~ý:nßľ­@dڱęfŕŔŢP<ţĹ´·ß~›˝’U[ €ŻŻoĄéÎÎÎxýő×ůW⬤  “'OFff&`ôčŃHIIQ8ŞęÇŰŰyyyprr¦M›ŕćć¦tHvcŚUWöŘ'Ż[·±±±X˝z5Ńľ}{DEEá÷ßdž ТE ›ĆcŹuÄŞ§ĐĐP~€Uĺߣĺęę ěÝ»W÷ c†4kÖ ŘżżŇˇ0ĆcŚ1f~3cŚ1Ćc6rüřqL0Ddt''' <©©©|3.cĚŞęŐ«‡öíŰăčŃŁş×©«Őjôďß_áČSŽ»»;†ŽůóçWzpŹßa=ŢŢŢ5kfÍšĄt(ŐZhh(®]»¦tŘcLŚ1V]Ůcźěçç‡%K–`É’%J‡Ŕ>ë1ĆŞşTř·łł3ĽĽĽpđŕA4hĐ@ˇ¨#‰Ŕ0eĘĄCaŚ1ĆcL2~3cŚ1Ćc6pďŢ=ôęŐËě|eeeČČČŔ´iÓlc¬ş>>čŇĄ‹‚1¦ĽJoÍ(++C×®]Š1ĆcŚ1Ćcúůůůş;99ÁÍÍ űöíCHH‚‘1GŁGŹ˘¨¨HéPcŚ1Ć“Ś`Ś1ĆcĚĘ4 ŢyçÜ»wĄĄĄFçS©TpssCYYV¬XÁż&Çłşľ}űÂŮŮŔăW¨GEEÁÍÍMá¨SV ĐłgO¸şşę¦5iŇ„Q1ĆcŚ1Ćł#yyyş‡ůU*śśś°mŰ6Ľřâ‹ GĆÉ«ŻľŠGŹ!%%EéPcŚ1ÓڰsyyyJ‡`·rss•••…¤¤$Ěž=Ű&ĺŮËzcëú`Ś1ĆÁĚ™3±oß>¨ŐęJźą¸¸xüúâçž{cĆŚArr2îÝ»‡FŤŮ:TĆX5ăëë‹îÝ»ĂŮŮjµýúőS:$ĆěÂčŃŁuÇm777DFF*3„ófÖĂu+ם}ŕ%“J­VăČ‘#6+Źű &FU˝Uúl{żvÇXUu˙ţ}Ý˙«T*$&&ň[ýh 4@pp0öď߯t(Ě9ÚyŚ˝śłTŐóZ{bë±­5ń¸™ÉŰ‘|¸.í? `C€JĄ‚ŻŻ/Zµj…°°0¨T*xzz",, -[¶„§§'T*ĆŹŹW^yţţţJ‡]AXX&L XůĹĹĹ={6Úµkgu“––†3f ** ß}÷ťŐʱ·ő6ĆT}ČÝv,Yž­ŰńŇĄK1qâDtîÜááá¸|ů˛ÉéŚ1ĆŞ–}űöáÓO?…FŁđ˙7˙»şş˘]»vUf«<U˝e«ő˛kÖŐáÇ1iŇ$¨T*¨T* <Ű·oŻ0_cb ČÎÎđřA€•+W˘wďŢ GÄU§NťđŰoż)s0Žrco÷ŮĂy­’c|1÷ŢąsGôňm=¶µ&73c¬u.ç˙Lă}R<[·)›•ÄPTT„ěÜą^^^L‚‚tŻËÉÉAXXŢ˙}¬YłĄĄĄJ†\IŁFŤ˝1ÍÝÝcÇŽĹüůóuŻüSRHHćĎźŹĺË—[µ[­÷Ť7(ůű¦ęCî¶chyBă·e;^˛d &OžŚŕáÇxď˝÷››ktşĄ,݆ŚŔ‚ pěŘ1ĄĂ`¬J(**ÂŢ˝{ˇŃhŕää„ÚµkŁNť:€źźśťť‘ššŠÔÔT,[¶Lép™‚’’’¬¶lî×™9Ť®®®đ÷÷Ç€”‡Ůą¶mŰběرJ‡au*• ~ř!FŽ 777těŘQéX9±±±;w®ÝĺÍą|ŠĄuëHy 9b•łîäŽM(Ąó˝Ö`Ťś­%ąH¦,ˇŰ©víÚ={6VŻ^Ť‚‚«Çbíc™#µO[Ç*gyÖĘóRŐ®GiŮj˝¬ÁšuŐˇCtčĐ›7oĆőë×±rĺJxzzę>·eŰł…ľ}ű*sPYYY€gź}{öěÁž={ŽY‹µóQ;vÄĆŤńčŃŁ*7>bÖcÉyŚ-Ďů>«Ęë­d.DĚ˝………˘—o‹±­5ŮrÜĚ—µîÁ­ŠyRKq.Ë0{ĽGŕ‡lި¨&LĐuB†řůů!&&Ťş§ŮíĹćÍ›•xňÉ'‘““Łt(ź@Ú‚µ×;==ŃŃŃ?ńn¬>än;úËż-Űń—_~‰zőęÁŮŮľľľŘşu+ ::ÚŕtKȵ ;věRRR¦t(Ś9Ľżţú Ť7®pó?cĺݸqC—”°î×™9NNN Dýúő•…Ů9k÷W°eË«—!”··7<<<Đ´iSěرCéptÚµkç0 Qkqww·ËĽ™#0—O±¤n)/!G¬rܱ֝‰aů^k;gkI.’)GĘvňóóÓÝXhÍX¬y,s¤öiëXĺ.ĎXţßZŞĘő(}¶Z/k°v]i(˙ `ű¶gm?üđÂÂÂŞý8‡‰W\\Ś&Mš $$DéPŮ"Ő±cGăäÉ“˙KÚ̶¤śÇ(qľÎ÷YU\o%s!bî,**’\޵öÖdËq3slÖş·ŞćIĄâ\–aözŹ*ŔŘÔ?˙ůO¸ąą™ťoäČ‘prr˛ADŚ=vóćMôčŃĂ.ž–žăĎĚĚ4xS•±éRŮs0ÇfŐ_©fŚ1öŘ–-[ĐŻ_?«—Ăý:3'55Í›7ç±(3ÉżÖh‹>Q¬sçÎŮU\‰‰‰ŠŠR: VČ™Kp¤Ľ„±Zk}©«3ŢNŽÁž¶“-c±§ő6ÇÖ±ZŁ<ąóüŚ UŰŢ1cxśĂD»~ý:4h•JĄt(ĚŠl‘Źjܸ1ęÖ­‹C‡ńĂĚŞé|ťYß;hďL ŢŹ¬ŹsY†Ů{¬ÜÚmČÓÓSĐŻÁş»»ĂŐŐU÷ď»wď˘Oź>đ÷÷Ghh(Nž<©űěŃŁG7o†Š—_~]ştÁůóçˇŃhđ믿bĚ1hÔ¨nÝş…Nť:ˇaÆxđŕŃďŁŃh””„!C†ŕ•W^ŃM?yň$ÂÂÂ0jÔ(|ňÉ'puu5ůšˇ?˙ü}űöĹĉŤđđpś;wÎd}!>>ÇÇÔ©SńńÇW*#//}ô&Mš„řřxtëÖ ńńńxđŕŕŔpww‡ŹŹ:„ÜÜ\ 4*• HMMüńǨ[·.ľúę+lßľÇGýúőńŕÁ 2O<ńZ¶l‰S§N™ŚŮ\,X ›f®­ëKIIÁ¸qăШQ#ÜąsGן¶lŮ?ţřŁn>ső/µżŰO2ĆcŚ)áŮgźĺ„ł‰‰‰ "»ř»|ů2ŇŇŇŹCűÇ*3•735n2V”c<)tŚ-fě(%Ď%%źbŞnő[–©€±ńtýúőńÓO?IĘk ©oc±ŠŮ–Ôť”ť1Ë–-Ă A0räHxxx@ĄRéţĚ•%gžLčľb.'$d9ćňF¦«SąťśśAűŽĐĽłcű“Řkóçχ‡‡ĆŤ‡#GŽź7·¤Ď«,GęŹ ±4§,wyĆň˙†ŽBbÚŹJ‰˙z”в­}­Aź%מ„ÖĄş’ăÚš1bÚcŐAÆ ůA&›Ž;âСCJ‡ÁĘ1u`ţŘmÉą%çg¦Î¤Ţ›#öžľĎĘđz[š 1—‡Bě˝BęEý±­Đő¶F;1Ä–ăfP«ŐřôÓO1lŘ0Lž<#FŚ@BBZ´h@xţÉTŮr,&÷=¸rÜkéxÔ\;0Ő˙Yăç˛,ËeÉuݵhÄ$ëÓ§őéÓǢe ź…„„š6mĄ§§Ó®]»µmŰV7Ď|@iiişwíÚ•ęÔ©C÷îÝŁŁGŹ’—— 9sćĐŢ˝{ičСôđáCŁßËËË3kFFFĄx›5kF~~~ş÷ë׏˛˛˛Ś.ŁiÓ¦Ô¸qc""R«ŐT«V- 5:ii)µiÓ†>řŕÝ´żţú‹\\\HŰ|óóó©Yłf4}útÝEHݢż,s9€¬¬,ăéččhJNN–”×Z߆Ö[ě6ZwRrt†,]ş”śťť);;›ćĚ™C(>>^pYräÉ„ć^̵‡śśÁűś©ĽQyúui¬>Ěĺš„î;ćňbŽ ŞŤO m'sű…v?&"ĘÉɡAŃŮłg+,CJŢ\jź!GYŽÖë“#§lŤň„.Ď\lbrŘúe*u=JhŮRŻłIëS-ąö$d}„Ö•Ôkk†”ď“ĘÚöäÂăĆ’luţ¸lŮ2ŞYł&•––Z˝,{g‹q9ćîŹrě–zn`Éů‘ůs)÷ćÉYđ}V¦ĎŐ¤ćB„äa¤2vn'fl˘ĎÜŘVČz[«ť©kŚ›ËĘĘč•W^ˇţýűëú—‚‚Ýţ­%dŚi®l9–ˇ$kä\ľ©1’vdé}±–Ţ i®ę˙¬u ‚sYŇsYDňÜkmڱóc~Ŕ¶z@{ŕ!"ň÷÷'///"":~ü80ř·sçN""zć™gĺääč–!ä{Bă  ´xńbŇh4tţüy“;î‚ hóćÍDô¸lܸ1ąşşťٲe€.^ĽXazłfÍtÉ“'ş}űv…yÖŻ_Oh„ DDşvĹŠşy"##©FŤ”źźODDŰ·o§U«Vé>×Ö_yuęÔ!wwwŁő"4Su!d˝Ť1´ÍÍ•§żDň¶ˇÓęŐ«Wiýž|ňIŞU«–Ů2Ä$>őçşťő iĎĆâ[ß , téŇ%ÝwJKKiÍš5t˙ţ}Áu ´}J­“ěělňńń©0Ŕš3gŽ®Í-ßP‚[š±¶nvß)((ĐM[´h ţýű›­©ý-‘ř~R~€1ĆŞŽŞvł c¬j㇔gíúq¤ú7—7#2?n77V2Źńbyúcl±cG)y."aů!ukţ˛Äć ôÇÓRóšBęŰĐz‹ÝRëNlŽÎČČHrrr˘’’"":ţ< °°0Áe*OjNČÜľ"´=Ů/…äŤ ­›ąú0•k˛ďË‹9‚Ş6>1´ťĚµm[şző*˝˙ţűt÷îÝ ß—š7—ŇgČU–ŁőÇúäĚ)ËYžĐĺ ŤMH˙§_¦’ףäľÖ _—–ô©úËríIČú©+K®­Â0ĆíÎĎś9Cč÷ß·zYöÎ0w„%çlBďK‘r~&ä\@?!ß“łŕű¬Ěź×JÉ…ÉĂHeěÜNL;×gnlKd~˝­ŮN ±Ő¸yĺĘ•€Îś9c˛>ĚŤă„”-Ç2”TŐ0—ďÖ_†”ű˝¤s„´ˇyÍň,˝a«}ŇPYŽžË26Ż\÷;?vł{ĺ_ €ÂÂBŔ‰'jđµńÝ»wŻđÝÚµkë–!ä{B}ůĺ—đńńA\\Z·nŤ‡ÂÇÇÇčücĆŚAĎž=±bĹ Ěš5 ĹĹĹP«ŐFçßłg ((¨Ât'§˙oşÚWóę—8zô( yóćčÜą3V­Z¸~ý:ĘĘĘPRR‚Í›7ÖŻ_Źę–ačUNµk×Fqq±Ń…ĆcŞ.„¬·1†¶ąąň ‘łťe¨ířůů‰~˝•XR¶3 ľNM1WßęľăěěŚ!C† V­Z‚ËÚ>ĄÖ‰źźbcc±nÝ:Üşu °oß>Ľţúë˘ĘÂX[7D»ďxyyé¦EFFxü*%ső/µżÄ÷“Ś1ĆcŚ1Ć„3–7ĚŹŰÍŤ…Ě#fĽXžţ[ěŘQJžK,Su+„Ř„ţxZj^SjNCÎń»%íR¨.]ş@ŁŃ`×®]@çÎť-*Kjý™ŰW„¶!űĄTćęCL®Ésy1f„îÝ»wGAAžx≠ӭ‘7·äŽÖKŤ_.¶.OKJ˙§äő(ĄŻ5!äÚ“őRWJ\[cŚ1&Ź–-[ÂĎχR:˝?Â’s6ˇ÷ĄH9?“r. ä;br|ź•ńő6FHĚBň0r“clbll _ok¶±ä7ď޽дiSŁe!Çą?ŹlKlľ[Ž|±cŽv 4ŻYžĄ× Lá\–4Öľ‡p`ŮŮٸzőŞÁŽIŁŃČţ=CŢ~űmś>}ÝşuĂÉ“'ѱcG¬[·Îčü'Nś@Ë–-Ś)S¦ FŤ&—óćM]ĚĆhOÜŇÓÓ+LŻS§Ŕ××W7mÔ¨Q8sć Nś8„„Ě›7˝{÷Ć×_Ť . ((¨Â‰ĽBă1UBÖ[,±u/g;©ŞÄÖ©)ćęűÎť;,żč)f‘jěرpssâE‹pęÔ)´nÝÎÎÎ6+_¨şuëęׯo¶ţ-ŮÄö“Ś1ĆcŚ1Ćä!eÜ^~¬(dąň'RĆŽ¶ČsYÂ9kć«l5~—+ź4jÔ(|óÍ7x˙ý÷1~üxÄÇÇcĆŚ1c†ěeIU~_±¤=Ů/…°E}Ę‹1ű ´|ńĹHLLDBBB…é¶Ě›ËU–ŁőÇúlťS¶×¶!J^ŹRúZÜ„¬ŹşâkkŚ1ć¸T*ÚµkÇŘ s÷G(uÎfîüLĘą€ďÉYđ}VňÝgUž<ŚÜähçĆƶr—ŻTžRĘ>—‘‘ŕ˙ű[–mŤe0ë±UľXH;#Żi‹őá\–iÖŢü0€ AaaaĄöĹ‹±lŮ2ŮżgČ´iÓŚÝ»wcóćÍP«Ő2eŠŃůŁŁŁˇV«użÜanG Ý“•†hźřŃź'33đÚkŻé¦EFF˘~ýú>}: ТE Ś1'NśŔČ‘#c2!„ĆcŞ.„¬·Xćę^ĄRˇ´´´B rµ[ĐŹßĶgSĚŐw«V­łfÍé>ż~ý:~ţůgÂę@Ěţ"•żż?bbb°rĺJ,Y˛ď˝÷žčňµOÚ•””ąąąÇVžvřÚkŻ™­KösýdYY™LkÄŞ»ĽĽ<“źgee!)) łgĎ–µ\µZ­{*Wnrď÷LsmJN–lkk¶A&ś-Ű‹#łV_Ě„qÄvĘ}c–‘2n/?V2Ź\ů±96Al“U IDAT@ž<—śůýeY#!W}Zo±Ű@j݉ÍŃSVV†óçĎ#%%źţ9¶mۆ©S§V¸IRÎÜ•ĺ÷KÚţ~)5o$g}ŰN¦ňbĚö m'ˇíŕź˙ü'>ţřc|üńÇşü+ ˝’ŇgČU–ŁőÇúäĚ)+•C—Úo™;/Qňz”Ň×ä&d}„Ô•Đ}Ż Ř7ÎK?Ć9 VuěŘ‘°ćŹ!ćÎĎ„ś Hą7GL΂ﳒď>«ň„äa´óÉEŽvnll+wůJĺ)ĄŚM›4iŘąsg…éŹ=Ş`|'t·tL9RröRirä5m‘˙ć\–iVoSÄ$ëÓ§őéÓGň÷>|H¨A?Ż[·. ĽĽ<Ý´§ź~šĐÇéŃŁGLč˝÷ŢŁŤ7Ň”)S¨k×®şďéć×ň=Cňóó Ő­[W7ÍËË‹îßżODDjµš|}}©M›6F—áëëK*•ŠöěŮC7n¤'ź|’ĐńăÇ)33łŇü§Oź&ň÷÷§Ý»wSaa!íßżźjÖ¬IčÚµkTXXHˇˇˇH·oßÖ}7..ŽÚ·oOjµşÂ2gÎśI*•ŠÎź?Ż›B={ö¬Tľ¶ţĘ«WŻĐ-·°°PPPîßBâ1U;wî4»ŢĆÚćBęľI“&äííMD$˝ťčבá¶STTDč™gžŃMkܸ1 üüüJëSVVfryúńcč»B¶ł!ćę4''‡Pppp…ďšn®ľŻ^˝JŢŢŢ€:wîLË—/§©S§ŇđáĂIŁŃ®ˇíSjťhýý÷ßäîîNť:u’T~Ż^˝Mť:•ţüóOZ¸p!ůůůÚ˝{7•••m놄„„*--ŐM[·n˝ř⋤V«ÍÖżÔţ–Čt?9kÖ,Ş]»6Ą§§›]‡ň,=ţ(˝|)>LÝşu#äääD]ştˇęر#Ť5ŠîÜąŁtŠ(--ĄąsçR‡ČĹĹĹč|/^¤˙ű߀BBBd);''‡&Mš¤ë›äňčŃ#š5kµmŰ–śťťe[®mÚ´ˇńăÇË6źŁůüóĎ)<<Üd›’©m-¤n­Őm-11Ńęń‹í×÷ďßO¨fÍšôÜsĎQ›6myxxP›6m(44”<<<Ť7Î&íĹ^X˛ß›ę‹íĄ?±—8äf«~MNöÚÇŮâ<%&&Zµ Gfíúq¤ú7—7#2?n77V$˛|ڵE±ąHcy1G`ŹăKÚNćÚAŁFŤi4*--ĄÎť;S­ZµčŹ?ţ "éys)}†\e9Z¬OÎś˛śĺË˙"$6!ýź=]Ź’óZˇ>[Klź*őÚ“őrÍRČľ!ćş@ 覉i{r±§qHrr2˝ńĆ€PDDEDDĐK/˝D‘‘‘ôÍ7ßPqq±ÁďšĘ{ŘC^Ú^ŘkN‚9µZMóçϧ±cÇŇ€¨cÇŽ”””$z9¶<|HóćÍÓM‹ŹŹ§ââbJOO§ČČHňó󣧞zІ FwďŢĄ‚‚š1c†nţaÆU8 űž14iŇ$Ýň,X@yyy€^xáš;w.˝űî»ÔŁG“7Ş/_ľś|}}©uëÖ”’’B‹/¦ÚµkÓ›oľIŮŮŮżóŰożQűöíÉÇLJ‚iîÜąN#FŚ }űöQYYĺççÓ„ ¨k×®O&L 3fLöÜ»wŹĆŽ[aÚš5k(%%ĄR¬Úőť9s&ĺććҢE‹tÓ&NśH.\ ŃŁGë¦-Z´îßż/(su!d˝ő·‘©mn®ĽI“&ŃÓO?M[·nŐ}Gl;ązőjĄú¸yófĄ¶sĺĘ;v, wwwÚ»w/ýňË/äââBhôčŃ”ťťMK—.%•JEhŢĽytďŢ=ŁmŃPüú }wîÜąf·sQQ‘Á噪Ó_ý•FŚAŔă—?ýôS:sć ť;wÎŕt!ő}îÜ9ęÖ­Ő®]›ęŐ«G~ř!ĺććę>RDd¶} iűĆꤼ=zĐwß}'ş|"˘Ë—/S›6mČŰŰ›şvíJ—/_¦Ž;Ň AčŰożĄ)S¦më†hę_|ńÝ»wʞ˛˛hîÜąúćę_jkŞź\¸p!5lŘnܸa¶>Ë«ŽÝĽy“PÓ¦MuÓîÜąCŻľú*ŐŞU‹Nž<©`tĘ)**Ň Myôčň=  ĄdČIč:YK˙ţýięÔ©¦zhŃĐ|ŽŞüú=zôüýýmR˙ƶµşµF´%{ĽŮf×®]QáBł~˙‘ťťMM›6Ą‹/Ú¬˝ŘK÷{c}±Řĺę“äŔýšý‘«Ź“«ÍđĂĘł§›p”"&of."d¬héxRč[lŽMKhžKH>ELÝb(/a*`lüđCmNj.ŇX^ĚŢŮăřƶ“±vNcĆŚŃĺŁgĎžM7oޤőë×đřaé9sćĐDçÍőc‘ăŘőv¤ţŘKsĘß˙=ËVž©<ż!Bb3×˙şţ˘Ôő(ˇődÉu¶ň„ö©wîÜ‘|íIČú »finżr]ŕСC4qâD]˝Ľűî»ôÓO?‰n{r±·qöZAŁFŤtÓ4 íر7nLM›6ĄÔÔÔJß3—÷P:/-”µňBú)ďj«:aâLť:•Ξ=«ű÷ŇĄKuÇ:1lyţXRRB^^^ôÍ7ßؤ<{e™ż?ÂܱŰŇs©çgćΤܛ#6gÁ÷Y^ďüü|Éą!y±÷şwPKĚv"züPßgź}fvl;{ölAëmŤvbŚ-ÇÍë֭٦M›’żż?őěŮ“.^Ľ¨Ű組ŚăĚ•-Ç2”äČqîýű÷-ľ/ÖŇcŽąv`Ş˙űěłĎí϶Ęs.Kľ{­Ť1v~¬"*÷>%&Jßľ}III GÂcöĄ°°­ZµÂŮłgáéé©t8hŢĽ9ŇŇŇPUyÖ>ţŘóńMĄR!$$/^ÔMKMMEhh(z÷[·*ťr„¶qCőg«˛íeąR¤§§#::żýö›ŇˇX…ˇőłeý[Z–=µ)¶lŮ‚~ýúY5~±ýúÖ­[áíí­{Í `¸˙X¸p!şu놷ß~ۡ·­YÚWő>IJ÷kr’#n9ŰŚ-ÎU*eµ2™µë§şŐż}ĚQűVő­Ył÷îÝĂřńă<~5ô­[·pŕŔŚ7wîܱia¬ş¨ ýź58rźZ•Řă8ÄX~ĺöíŰxńĹáĺĺ…óçĎĂĂĂCT,öľ/Ú2/dďuˇĹą2űUż~}¬]»Żľú* //ľľľhÓ¦ RRR/ÇÖ珯ľú*4h€5kÖؤ<{d‹q#p”~ن˝ĺaüxźŻĚÚÇ>Ţ0&Ť±óc%‚aŚ1Vµ-_ľ±±±śśg6ѰaCŔÍ›7Ž„UE7oŢDŹ˙cďÜĂŁŞÎ¶O&Â!)@H ßPZűI¤H TŔ!Ť­µ†"g"' ©AŽ"+AšR[KáEQh( $hä Č)!ç@sxÍnf˛÷ěµç¸'YżëâŇY{ĎZĎzÖłN÷Ú;3djkkýmŠWhčő“¸ĆŕÁqŰm·éŢ—‘‘   X$±!ű¬>ŇGöHH$IĂdńâĹ5k ”´   těŘ÷Ýw:tčŕGë$R“H$Ď!ÇT‰QÚ·oŹ_|ŁGŹĆË/żŚçž{Îß&y ąÇŻŹô‰ą±Z­řÇ?ţˇĽ pýúu?Ľ$`fz÷î-_\•H$vHF"‘H$€|rC"‘H$áŕÁčŮł'şu놵k×büřńţ6IˇĽĽÜîż’†Ĺţó@ź>}7oŢÄ’%K0zôhÜsĎ=0`rss•űµ®[­V|ňÉ'xć™gĐĄK\şt )))čÜą3ŠŠŠpřđaÜ{ď˝8q"~˙űߣiÓ¦NcęĚ™3xě±Ç0kÖ, >ÉÉÉ8~ü¸n9zö;ă믿Fjj*"""Đ«W/ěÝ»×éý%%%xöŮg1{öldffbĐ AČĚĚDQQ‘rOyy9ćĎźŹôôtL™2)))XąrĄfž/żü2BCC1}útěßż_¨M*++‘™™‰qăĆ!++ sćĚŃíŻü1BBBŽO?ýĹĹĹHOO‡ĹbAż~ýpâÄ ŔŃŁG…?ţńŹ´ŰřA޶mFŚűďżđç?˙'NśŔĺË—1aÂÍű¶oߎqăĆ!::EEE1bnżývôčŃźţąťí«WŻFzz:222 ‹Ĺ˘üsµ­Ś”_µúŐĺÚµkHKKCdd$qřđaĺš+±Ş×Öjľ`¨˙©Ĺ ÄÍš5Cpp°î}!!!hÚ´©ňŮ“ń’““éÓ§ŁK—.¸r劒oŹ=đ÷ż˙]8_WÇ|G<›jÍW«Ď:óČř Ç5}?já,Ě0ĎęůC"iěěĺ~RbF>űě3Ŕşuë좏9‚YłfaóćÍ>·ÉS}%Pűś™u1‰Dęřç ä*q—´´4ă> ­ż¸˘KÎ5f=cË–- ĹbÁâĹ‹•Ůß~űm„„„`ăĆŤŞĺ:îńőô-gvŢŐ$Śhoîś™¨éëׯGPP˘K•––bůňĺJšłň6mÚä’ĺϸđ·ŽęL{˙ý÷1{ölĄŚíŰ·ŁI“&ČĘĘręKÓ»woś9s×®]ó·)?#×gfÔa$ž…$ 555~¶F"‘H\„—IKKcZZšżÍH$SpüřqĆÄÄ0>>ž˙ţ÷żýmI˛¬¬ŚsćĚ!ŕČ‘#Mc›;x{ţ1óü€ńńń¬­­eAAß}÷]vîÜ™-[¶äW_}E’3fŚň˙$9pŕ@¶k׎%%%NŻ_ż~ť`óćÍ € .ä‡~ČŃŁGł¬¬ŚńńńŚPľ÷ë_˙šWŻ^Ő´µ[·nŚ‹‹#IVWWłuëÖLLLä­[·ś–Łgż Ŕ©S§r÷îÝ|ýő×Ćŕŕ`;vĚÎ $ÉŇŇRĆÇÇó…^P®_˝z•ńńńŚŤŤeQQ«««™’’ÂôôtZ­V’ä† €˙ú׿ěĘ&ÉÂÂB¦§§Ű•©×&555LJJâ1c”ëß|ó ›4iB˝ĄjFFCCCY\\L’¬¬¬d»vířä“O*÷ÔÔÔ099Y·]lś?ŢÎOŽ~Óşď»ďľc‹-€ ,ŕąsç¸yóf`RR’ň˝U«V188$É… 3335ë)ŇV˘ĺ«ˇV?[»>˙üó<{ö,wîÜIěÝ»·rŹŃXmkµ6pÖ˙DbĐĚdggëĆş»xb\W‹žŽ—ÚÚZîر͚5#Nš4‰űöíăŰożÍđđpŕţýű…ňuuĚWĂhlŠúŇhľjmá̢ă׌Źk¤v[™ežŐň‡«řbť€ŮŮŮ^-#ń¶‹˙EöŠ u?)ipҤIŚŤŤehh(ţóźó±ÇăúőëYUUĺS[<ŐW˝Ď™Qs…@ŮźH$ ‰@˙ĽACSf܇číuŰ·oĎČČH峣îáŽ.íLcŐ3ćÎťK}Zą^SSĂ 6đĆŤveçççsÔ¨QĽvíš]~zuZ˝z5đÔ©Svß‹ŹŹ×Ýřť©|GŻ]ÜÁÖ¦uiÓ¦ nÝşĄ|0`¬V+vîÜ  ôďß_3_‘¶-ß(uól۶-***¸«î´µH˙ÓŠA‰ďđdĽ˙ŤŤćÍ›+i©©©~řŮmoŽů˘ťĽ‘Ż‘zÖEd|ăš~śjµ•YćY‰D"‘H$‰D"‘HŐŐŐ¸rĺ ~ň“źhŢăŽV)Şý×ĹQĎŔ¤I“°qăF\şt đŃGáÁÔ-ß-}Ë™ťľŇ$ŚhoŢ831Z^ÝôşčQţŚ ꨢ:ć7ß|©S§:­‡™čÝ»·˘QJ$‰D"‘H$€|@"‘H$Iˇ  ůůůĘ}u±Z­ş×ťń裏â‹/ľŔ ApřđaôíŰ7nÔĽ˙СCčŃŁbcc1wî\´hŃÂműŤĐ®];@§NťTŻŰÄáłgĎŞ~ŻU«V¸rĺ €Äb=–-[†ěěl,^ĽŘ.]ŻN/^Tîs…‰'âË/żÄˇC‡°xńb,Y˛Ź<ňÖŻ_Ź“'O"&&ĆNwĄ]<ÉĉńĆo`Ô¨Q1c2331oŢ<Ě›7Oó;"mĺk\‰UwÚZ¤˙iĹ Ä˙xrl‹ŠŠDGG{uĚĹčÜŕŤ|}QOg4ćq Đn+łĚł‰D"‘H$‰D"‘46öěŮŞŞ*üâżĐĽÇ­ŇSó´iÓpŰm·aĹŠřüóĎŃ«W/»”—Q;E“$ÝĂlqá+UDǬ¬¬ÄOúSĂuđ'˝{÷ơC‡PSSăoS$‰D"‘H$!äˉAJJJüm‚×)..ö· €«WŻb۶mx饗|RžYę­…Żý!‘" ¨¨¨¨'Jź:u «WŻÖ˝îŚçź±±±Řµk¶nÝŠęęjĚť;WóţáĂ‡ŁşşZů‹-"´;ö9ráÂŔ!CTŻŰţ2±í/9;~ď@Ďž= ,IĺžsçÎá˝÷ŢłűŢŕÁ1gÎĚ™3ÇîšH›¨Ů!Jjj*˘ŁŁń / ĽĽwÝuĆŹŹC‡!##&L°»ß•v±X,|kkk‘››‹śś,]şďľű.˛˛˛śŠů"mĺ®ÔĎ•Xu§­EúźV Ú¨­­5\®Ä3xrlłĐ>đŔ^óE1:7x"_Ç>ë‹z:Ł1Źk€v[™ež<;ŹI$‰†ć łŐßlöx 3ëgf×2ľŚwÚąşşZů…)OĐXú˝™1Kż—g-‰Z»™yž3 UUU3g~úÓźbňäÉš÷ąŁUş˘1«‰ &`ÝşuxőŐW1räHÝďŮă;łÓWš„»¸Ş{ŘţŠ}UU€¤×Ç&Ć…ľŇQEôŃfÍšá7żůŤKőđ?˙ůĎQ^^ŽcÇŽůۉ‡‘ë‰D"‘rî ,üş—§ÄeŇŇŇ––ćo3üĘîÝ»ůż˙űż@ěׯűőëÇ˙÷˙ţSSSůĆođÖ­[ţ6Ó#,]ş”ÉÉÉlҤ‰żMń 7oŢä‚ Ř»woűŰž:uŠO?ý40!!Ákĺ­ŢZ8óGRRgĚᱲ<ťź«TWWóĺ—_ć´iÓř›ßü†}űöĺ¶mŰüm–)đöücÖůíÜąsŔÎť;kŢsóćMĆĆĆGŽÉ-[¶pîÜą8p KJJtŻ“dLL °¬¬Ě.ďćÍ›óĆŤ$ĎV­Z1))IÓ–V­ZŃb±đ>ŕ–-[xÇw<Č .¨–#bźÝ»w'*i|řᇕĎŔĺsbb";věČďż˙^ąoĘ”)ěÓ§«««™źźĎ°°0`˙ţýąfÍfeeqܸq´Z­$É.]ş­V+kjjŘż¶nÝšGŹŞÓ_|Á&Mš022’»víbEE÷ěŮĂ–-[żýö[ÍzŰ?>- sss•´„„:Ôp»”––Ł˘˘”ďtíÚ•aaa<ţĽ’¦vź­MëҡC`uu5IrŢĽyŚ‹‹ăźţô'îÚµ‹`^^kjj4ë'ŇV˘ĺ«ˇVż¨¨(°‹»öíŰ+1ëJ¬Š¶µšoťő?˝$É °M›6<{ö¬¦üEvvv˝vó4îŽëeeeŔNť:©^÷FĽ?ôcvýcăĆŤüŮĎ~ĆęęjŻŽůjŤM5ÇbWňuěłFęYÇńAŽk®Ĺ©V[™ežŐňDzeËx×]wqëÖ­šuSĂëDĚÎÎöjzy/âm˙Á˙Î«ŻľĘgź}–ýúőcßľ}yúôi›äs<ˇˇ˛ͦ!zŰWu$ŃďÉßWz˘E{´ű#řŞ?:kg‘.,,äěŮł•µˇ»m <©‰›­ß›á¬Ĺ,g®ŕ)ŰŤ®mÔÚÍźóśŮö!jú I9r„ÉÉÉěŇĄ Ož5}Qď .¸ť‡–?üqfeeąťżłü gll,-ZÄäädŽ?ž}ô‘R-®_żÎiӦ٥mذA5F´Úĺá‡ć… 8{ölĹOË—/gII gĎžÍöíŰóťwŢ!I–——×»oѢEĘçůó糸¸+V¬PŇfÍšĹĘĘJîŢ˝›íÚµSľYn IDATŇm˙Ú¶m«ä݆^[­YłF¨|5ęÖĎjµréŇĄĘ÷¦NťĘ˛˛2.Y˛DIËĚĚä­[·\ŠU˝¶.--UmµţwäČá|ĺ•WŘąsg~÷ÝwNíófŘćý÷ßçSO=Ą´Éřńăąwď^’ôzĽŘ±–-[ĆëׯóęŐ«\´h‘Ý“·Ć|GÔú˝VljÍ jcńĹ‹ çë8&éůAd|(((ăš‹qꬭĚ2ĎŞĹLFF‚‚ءC§ős¤±Ľ `ć˝Ů‘سrĺJ¶hŃ‚555,**â#Ź<Â˙üç?nĺé‹ý¸§qWC D?ÖÍß ˘/íqU—ŐźŚćď+=ŃfÓ\ťaöý‰ţęŹZíl$†m ş‚/ëHs“¨­žÖŘÍÖďý}Öâi˙ęáÉő„í®®mÔÚÍ_óś™ö!ź}öGŤĄěéSRR8hĐ ¦¦¦ňŃGĺš5kę=¬¬Ąë¸ŞK;Ó_|ńEĂzĆ!CřÖ[o Őżî_OßrfgAAW5 #ڛ޽®čyyyLJJbXXČĽĽ<öíŰ—ééé|óÍ79wî\ŐňÜŃŁüţÔQEôŃ1cư˙ţBuQĂ_ç– âO<áórÍ@C{€4ßúĚ[4ĵş·ó $źIĽ§'y’ť;w˛_ż~,//WŇ×đěÖ­óóóýa˘0đ2Ŕ­[·ěôL˛.Ôçç.Łu%ÍŹžô»;ZŹ/Úß{yů2€0ăĂ’ţB+X/]şÄöíŰ3..NscHŘ6Ň łŐŃW˘¦7ëýí·ß˛oßľÉË"Ż'í7RFÇŽůá‡*ź‹‹‹ Ŕé_Űm,4Ć—$’†Â›oľÉ%K–(źkkkyáÂnÚ´‰wÜq‡-“ř‹†đ°Ť·0ŰşT˘Ž×—Ó§O^[7–—ĚĽ1ÓC8’ú$$$đÎ;ďôX~ľŘŹ{ wćń@óŁZţţ\ÇÍQ]?Ó#Ú€ üý‰żăßݲ\ýľ/ëHs“żm5[żog-"ř»ÝŐpgmŁÖnŤýe€†Fyy9»víĘŠŠ ›"1FăÂßcŻ·ń—ľý /ŘýAĚĆDC|€lř}ĹŚë -Ľa«+y’Ď$ę± ˙ö·żŐűËíjkřĺË—›ţŹ+ÂË$Ů­[7ćććęŢgĆxŃĂqî­« 3ÄŁ'ýîN^ľloďĺµÖÇM ‘x‘öíŰăĹ_ÄčŃŁńňË/ăąçžó·I‰Oąxń"† ‚ÚÚZ›âľ°_« «ŐŠüăřĹ/~¸~ý: ::Úk¶H$‰7YĽx1fÍš…‚‚%-((;vÄ}÷݇:řŃ:‰D"1Ž×—ŠŠ ¬Zµ oĽń†żM1%r/"q• .x,N]Op‡@ňŁŮÚÉlö¨vKĚEcŤ#_Ö;|H¶J<‡YŰÝ“kIĂcÍš54iš5kćoS$&BĆ…9čÝ»7^xá|˙ý÷hßľ˝żÍ‘HśbÖuްՕ<ÉguĚÚ†Ćm·Ý¦{_FF‚‚‚|`QĂç®»îB×®]ťŢcÖx1ŠH]ëâďxô¤ßÝÉ«ˇ´żrD‘xť´´4ă>PŇJJJđěłĎböěŮČĚĚÄ A™™‰˘˘"Í|¶lŮ‚°°0X,,^ĽXéśożý6BBB°qăFˇĽ×Ż_Ź   X,@ii)–/_n—¦Çµk×––†ČČH$&&âđáĂʵ3gÎŕ±ÇìYł0|řp$''ăřń〿ýíoŚŚ„ĹbAVV–ňťµk×"88ëׯÜĽyK–,ÁčŃŁqĎ=÷`Ŕ€ČÍÍuj“łrµ¨¬¬Dff&ĆŤ‡¬¬,Ě™3ĺĺĺv÷čůóăŹ?FHHÂĂĂńé§ź˘¸¸ééé°X,čׯNś88zô(˘˘˘đÇ?ţŰ·oǸq㍢˘"Ś1·ß~;zôčĎ?˙Ü©Í"±Łç ‘z×Ĺjµâ“O>Á3Ď<.]şŕŇĄKHIIAçÎťQTTä´Ľ?˙ůĎ8qâ._ľŚ &(yşŇĆjvm۶ #FŚŔý÷ßŕ‡‡z¶lŮ‚'žx}úôANNîľűnÄÄÄ`˙ţýČËËðaĂжm[tďŢÝÎßjůiŮŻĆáÇqď˝÷bâĉřýďʦM›*~uV_­2Ţ˙}Ěž=[ůĽ}űv4iŇÄ®ďH$I ńŮgźÖ­[g÷ŕě‘#G0kÖ,lŢĽŮ_¦I$¦Ä¶Žp¶N“ř9®.ůůůx饗čoSL‰Ü‹wµg{ü5kÖiOŽěÜą&L@yyą˛·µ}ÖÓ(´öŇFöăŽ¸Ł ęfĺĺĺ?>ŇÓÓ1eʤ¤¤`ĺĘ•őlq¦ˇůÓŹ®Ä‡šn©×N®j®jgîŘcT«RÓ‘DěŐźÔîÓó›\Ő±D®Ő%žÁ—ńč·łV ;‹=G^~ůe„††búôéŘżżßë­U–3Í^k\ŤŽŽĆ?˙ůOÍńÖ™m"cŤč<ę‰väY‹‘~`ÄćŐ«W#== …ĹbQţ©áJŚjá‰ŘĐZ۬X±ÂísJI`sđŕAôěŮÝşuĂÚµk1~üx›$1îÄ…ÔQ˝CRR‚‚‚““ăoS-®¬3mëI‘ő™§´-Ľąďt¤!®ŐŤřT+OŁĎňębÎÚöăŹ?Ftt4öíۧܯ–V]]Ť?üá;v,ž{î9Ś?‹/Ć]wÝeČgí቏3†_}ő•ňyŕŔl×®KJJ4mÓ+בšš&%%qĚ1JÚ7ß|Ă&Mš(~őgFFCCCY\\L’¬¬¬d»vířä“OÚ•—śśL’üî»ďآE ŕ‚ xîÜ9nŢĽ™””dggÝصǙ/DęíČ­[·xŕŔ6oŢś¸páB~řá‡=z4ËĘĘt}ŻÖ?\icµĽÎź?o—fµZůő×_[µjĹť;wňäÉ“Ŕ.]ş”ĹĹĹ>žĘç_˙ú׼zőŞP}őĘ¨ŞŞb×®]ąyóf];Ţžäü&‘x‡‚‚Nš4‰±±± ĺĎţs>öŘc\ż~=«ŞŞümžÄOřâçvm\/++ăś9s€8räHţűß˙ö·Yä¸ÖřđĹx€ŮŮŮ^-ĂfŰ‹xŰ?fóżîh+ׯ_wşÇ×Óžśˇ¶·ŐÓ śíĄE÷㎸« čéfŐŐŐLIIazz:­V+IrÆ Ŕýë_$Ĺ44-|áGWăCÔ^w5D#Ú™§ěqE«rÔ‘DíŐźÔîŃ@EúŽ;:–łë®hŹf"Đ÷'ľŠŃvV‹ag±W÷§ß ™žžÎcÇŽ™¦ŢjeéiöWŻ^UW‡ÎÝ»wkŽ·ÎlkDçQŁíäuEOruťćĚŽţóźlŢĽą˘‘©ĄŐÖÖňţűďçăŹ?®hkĺĺĺ ·+GÄ˝XńDΨ­­ĺŽ;جY3ŕ¤I“¸oß>ľýöŰJ}öďß/Ľ.wć[Wő$‘˛ÝŃâDĐŠywö+ľŔŰó7ň72Nčůż°°P7ľ?ýôSˇ>@šGSôF›@{¨Ć›čktt4Ł˘˘H’Ď=÷đűďż·»gÓ¦MŔ™3gjćSPPŔđđp»oá…ܱc‡ˇĽëŠę6ÔұÝcëě$ÉćÍ›+ź—/_έ[·’üáđ5..ŽM›6U®WUU±S§NLMMUҲ˛˛xôčQ’äÁ• »ă?[=ŐĐ+בիWOť:e—ŻřAÔź¶ĺ×^{Mą'55•-Z´`ii)Irűöí|ýőוëwŢyg=·k׎!!!viucKÔgľ©·6› íŇő|ďŘ?\mcµĽDÓ:tčPŻ~wÜq[·n­[†čdÔ¶m[ŕĘ•+iµZ™››Ë’’ˇúꕱnÝ:ľňĘ+ş64äˉDŇpô‡m$Iă˘1ľ `¶˝|ç<ˇ­híńő´'g¨ímő4­˝´V~FpUĐÓÍ–/_N<}ú´r˝¦¦†6lŕŤ7ěîw¦ˇ‰Úm+ÓS~t'>Díő„†(ŞťyÂOjU®h~Zv«Ą‹h "}ÇUKďş;ÚŁôý‰ŻâßH;;ÚälĽ˛ŮšźźĎQŁFńÚµk¦Ş·ZY˘š˝Ö¸Ş–ndś®‹ăXcd5ŇNŽČłăý@ÄćÔÔT)/šçććď˝÷^:ĂŐÍĎHlhĺAŠźSŞ}×Ý5˘+Č}D"ń'ţÔ·GŚÁţýűűĄlb†—ÜYgЬS<­ ¨á­}§ u­^Ł:šŃgyH±ušŢ𰦦¦žíuÓÖ­[GüňË/íîqôťž-"íቂDâeŞ««qĺĘüä'?ĺçlĂĂĂíîKNN8p@3ŻLš4 7nÄĄK—}ô|đA·ó6BÝźKj۶-***”ĎĎ<ó †Š×^{ ,Ŕ­[·P]]­\oÚ´)¦L™‚;v ??ŐŐŐ8}ú´âźC‡!11üáe»=ô¦Mzĺ:ňÁbbběŇ‚ţ;,úł{÷îčßż?^ýuŔąsçP[[‹ŞŞ*lÝş°iÓ&<ů䓪>´Ń¦MÜşuKÓfQ{śůB¤ŢZŘlnÓ¦Ť]şQß»ÚĆîŕč3ŕ‡ţäÉź•Z»v-ÂĂĂ1eĘôęŐ eee÷H}żůćLť:Őc¶J$‰D"‘H$‰r/bN<ˇ­híńő´'ŁčiZ{ioŕ)m`ďŢ˝€Ž;*iÁÁÁ1bZ·nmwŻ3 ÍžôŁ;ńaw4DW´3Wíń¤Vĺ »ëbT‡ÓÂUKďş;ÚŁÄ»x2ţÝig‘ńꡇByy9nżýv#UTĹŰý^Tł×WŐŇŤŚÓuńäXcd^‘g-ÚőÖBÄćŔjµbçÎť€ĐĐP@˙ţýuóŻ‹§Ď }ąv“H$‰˙ąçž{pčĐ!X­V›Ňčpgť)˛Nń…6ŕ­}§( q­nt-ć) Á¨ÁÁÁőľS7m×®]€nÝşŮÝŁć;gx"V<o¶ţŐĽys%-55pćĚáxôĆzŰh_¨‹'÷™îŘ&ÇH ‰ú_/ľEî MŃťxôôřě)}Ţ("gŢš[!ĎF$fˇgĎžhÖ¬:äoSîĚá"ë_콵ďÔ˘1¬ŐŤęhFźĺ±ĄÎ×izkÂÚÚÚz¶×MëÚµ+`ÇŽv÷ÜĽyÓ-˘Ď⸛‡«Řj~ŕ„ăŃčz[$–<ÝÔÚ×UĽŃO;FĆ wü_7ľEîń–¦h–xôôř¬…;ZŹČwEÎ>ĽĄë{J\&--Ťiiiţ6ĂďTTTcbběŇŹ9ÂäädvéŇ…'Ož´»?11‘;vä÷ßݤO™2…}úôauuµn™—/_fHHSRRęŮ"’÷°aĂ€YYYŕ–-[xÇw<Č .(ßűöŰoĚůóçŰ•qóćMĆĆĆGŽÉ-[¶pîÜą8p ]ąŽ–kă‹/ľ`“&MÉ]»v±˘˘‚{öěaË–- €ß~ű­á¶š?>- sss•´„„:´^ů111t‚:tč@JľŽ±%jŹ3_ěرC·ŢZŘl¶µµ¨ď»víʰ°0ž?ž¤ëm¬Ö×JKK €QQQJZee%đÎ;ďTŇââ‥ĄĄőęS7ŢŐňs´_‹ćÍ›óĆŤ$Éęęj¶jŐŠIIIBőuVƤI“8xđ`§e76Ľ=˙ÍĎž=Ŕ–-[ňÇ?ţ1“’’€ˇˇˇLJJbbb"CCC €Ó§Ogrr2›4i˘›oRRgĚáNUĽŠ'í;uęź~úi`BB‚fš$°¸yó&,XŔŢ˝{388ŘîšHüröěŮ «7gŠbö~ä _őgíä‹<˛łł]n_Q̶o©©©á˘E‹xß}÷ ͆'bĘ]ąďŰđWĚĐ~fĆă fgg{µ Q̸ń¶ĚäÜŃV´öř6´´'- €±±±vézšÖ^šTß+/[¶ŚwÝu·nÝęÔw´=Ýě믿VÖ¨ýű÷çš5k••ĹqăĆŃjµ’ÓĐüĺGOÄG]ÔÚɢvć){\ŐŞÔt$»Eő'µűôü¦ĄU;⪎Ąw]Ds53ľ?ńUü‹¶łZ ;ŻştéB´Z­¬©©a˙ţýŮşuk=zÔőV+KTł×WŐŇŤŚÓuqkDumŁíäžĐnÍŚ?Ď'äŮHĂ ¦¦†Ď>ű,żűî;·óň·ľť””ĉ'ú­|ŕ‹}î¬3EÖgîh˘sŻ·öťZ4ĵş¨Oµň4ú,)¶NsfÇŽ;آE ľ÷Ţ{JžŽi¶ç+ZµjĹ·ß~›_|ńWŻ^Í-ZŘůNĎ–ŠŠ ÝöđD"$$$€Ýz}ăĆŤüŮĎ~Ćęęjáxtć[Wő$Ł}ˇ.ޱ»`Á¶iÓ†gĎžöMYY°S§Nő®yâJoâíůŔů'DýŻß"÷xCS4S-ŞY‹˘µ>nx;SâďM‡řěłĎ8jÔ( ¦¤¤pĐ ALMM壏>Ę5k֍𕖖rćĚ™8p 3339sćLΛ7Ź·nÝ.{Č!|ë­·\Ę;//ŹIII ăŔ™——Çľ}ű2==ťůË_ęŮaµZątéRĄžS§NeYY—,Y˘¤effňÖ­[\łf [µjĹ^˝z1''‡+W®d›6mřđĂł  Ŕ.ß©S§ÖK#ÉłgĎ255•üŃŹ~ıcÇňÚµkNýa¤\űöícź>}ÎŘŘX.Z´ÉÉÉ?~‹‹‹ąbĹ %mÖ¬Y˝˛Ec÷•W^açÎť…_v{˙ý÷ůÔSO)ůŚ?ž{÷îµ»ÇĎPz‹@|Ŕč8!â‘řąÇÓš˘™âŃSăó€8uęTÍ9Č­GtîŃ:űĐł]KźÖÚË»ÖúŘBÖů=‰!{ě1Ŕ¶mŰülI㣢˘={öıcÇЬY3›#‘H$>ĹŰóŹŃüßyç„……)?üđóJ 8uę”’öĘ+Ż`Đ AxôŃGńŐW_ÁŰKłgĎbřđáŘ·oźWËń$j~SK“Ý»ww+ćÝý~ ă«>ŕ ?»’Ç_˙úWüú׿öjűšußâ©Ř6ëxo´~f­GcĄ±Ź˝Zřb<±X,ČÎÎĆŻ~ő+Ż•ČxŰ?Ň˙˙ĹĚÚS^^†Žśś›"‘HŤy"‘HĚņ pýúuĚ1`µZqéŇ%|üńÇ>}:®\ąâg Í…·×‡fއś:u >ř Ž=ŠľŐXş~ŕĎó O•-57˙rěŘ1üö·żENNÂÂÂ\ĘĂßëÇ·Ţz ŁGŹFII BBBübŻńĹľ БڌÄňş"m—hăíů Pć‘ř–}@âK´ÖÇMüaŚDâ.kÖ¬Á¤I“Lw+‘H$Ť‘Áă¶ŰnÓ˝/##AAA>°¸xń"† ‚ÚÚZź”'‘H$˙ĐPĆű†R‰D"iHU{ŞÍk™« IDAT¨¨ŔŞU«đĆořۉD"‘H$ݰxńbĚš5 JZPP:věűî»:tđŁućŁ1ŻIâÉ'źÄSO=ĄĽ 5I]d<řź˙řÇ‹‹ĂŚ3đÚkŻůŰ—čŐ«ŞŞŞpěŘ1ÜsĎ=ţ6GbóÜë-,‹î=_}őîĽóNX#©‹hŰH$‰Ä˙řć‰<‰Ä|Ŕĺi۶m1bîż˙~ĺţÇăŢ{ďĹĉńűß˙M›6EyyąjYţóźqâÄ \ľ|&Llٲaaa°X,XĽx±"ţľýöŰ ÁĆŤ‘““éÓ§ŁK—.¸rĺŠb_Ź=đ÷ż˙Ý©}ĺĺĺ?>ŇÓÓ1eʤ¤¤`ĺĘ•Ęő3gÎŕ±ÇìYł0|řp$''ăřńă˘n®زe žxâ ôéÓ999¸űî»ýű÷#//Æ C۶mŃ˝{w|ţůçvßżyó&–,Y‚ŃŁGăž{îÁ€››+T·íŰ·cܸqŽŽFQQFŚŰoż=zô°+ÇHűŠúˇ¤¤Ď>ű,fĎžŤĚĚL 4™™™(**¬_żAAAŠQZZŠĺË—+iV«ź|ň žyćtéŇ—.]BJJ :wî¬äáHee%2331nÜ8deeaÎś9vőđD|żüňË ĹôéÓ±˙~Ő{ýôS#==‹ýúőÉ'GŹETTţřÇ?Ş—D"ń  =ĺçç㥗^Bbb˘żM‘H$‰D"ń ź}ö`Ýşuv/9rłfÍÂćÍ›ýeš)iĚëĂíŰ·ăČ‘#vżZ쨱čéŹzÚ‹žFâšvëImÔ†Ţwťi›"ç j¸z>ˇ†+ç%®řqĹŠŞšŰÇŚččh»_ pLŃmDôKwĎgômO=˝ŰŐ8Ń „őë×#??ßPÝĚB||>ž˙ţ÷żýmŽD"‘ř oĎ?žČTŻ%$$źţyž={–;wî$öîÝ[ąçüůóőňŹŹgDD„ňů׿ţ5Ż^˝jȆąsçOś8aWÖ°aĂX[[Ë;v°YłfŔI“&qßľ}|űí·NÜżżŞ}ŐŐŐLIIazz:­V+IrÆ Ŕýë_$ÉnÝş1..NążuëÖLLLÔµŮ1ÍY´°Z­üúëŻ €­ZµâÎť;yňäI`LL —.]Ęââb=z”’’b÷ý1cĆ𫯾R>8íÚµcII‰nÝľűî;¶hŃ‚¸`Áž;wŽ›7o&&%%)ym_=?”––2>>ž/Ľđ‚rýęŐ«ŚŹŹgll,‹ŠŠH’qqqt\ŰŇnݺŰyóćŔ… ňĂ?äčŃŁYVVVϦšš&%%qĚ1JÚ7ß|Ă&MšŘ•a4ľm}†$ ™žžÎcÇŽiúĆŐrőáŞU«€ď˝÷ž]ŮO<ń„ňY+†®_ż®ëăşuµÉYLж“ł¸ÍC„ěělĂß1ŠY÷-¶Řž:u*wďŢÍ×_ťaaa VbÜYµálα!2‰Ć—#˘ń 7¨ŐCdîpÄÝľŻ7§‰řÉÝyDZ˘sÉŞU«Ě‚‚’äÂ… €™™™šţi?ŃŘČČČ`hh(‹‹‹I’•••l×®ź|ňI»ň’““ ŐËLřb<ŔěělŻ–ČxŰ?ŤÝ˙R{’H$ŤťĆĽ?‘H$桠 €“&Mbll,CCCůóź˙śŹ=öׯ_ĎŞŞ*›×(1ë>ä7żů - «««ëĺgÓô4^gÚ‹î/˘ÝşŁŤjˇW/­2‹ŠŠ„ĎA}Işv>ˇ†+ç%®úQMsűç?˙ÉćÍ›+í¨–&ŞŰčé—îędÎ0r®ĄwÎăŞEtG[Ý.\(\·şaýřŔpÄ~µÁ—řb_ ‘HţË©S§8eĘPÎ4ôÖfˇ¬¬ŚsćĚQl9r¤ÔUŢžĚ>ßÄ·ě µ>6oo ̰éH$I㣡Ľ `ĎI222’Í›7wšG۶m €+W®¤Őjenn®"RŠÚPPPŔđđp»ţ.\Č;v(źăăă €ĺĺĺJÚŠ+€Ź?ţ¸jţË—/'ž>}Zą^SSĂ 6đĆŤĘ=[·n%ůřǦM›ęÚě&RQźtčСŢćęŽ;î`ëÖ­•ĎT6.Ž˙leęŐíÎ;ď¬WN»ví˘|6Úľz~xîąç€ß˙˝Ý÷6mÚDś9s&IűĂŽi6ű 5í!ÉŐ«WOť:e—n‹©ş‰o›=ůůů5jŻ]»ćÔWËqDÔ‡UUUěÔ©SSS•{˛˛˛xôčQ’b1äĚÇuë j“łi'=›Ť´µŤůa׍Á•+W÷»ßŮÝŁö="/úc‘h|9"zcĄZ=Dć5Üéűzsš?ą2ďh}Wd.IMMePPňpHnn.đŢ{ďU­+)Ö~˘u¶äľöÚkv6µhŃ‚ĄĄĄ$ÉíŰ·óő×_7T/3!_đ?f}G"‘H$ ĆĽ?‘H$‰6f݇ÄÄÄi ¤¶ţčL{Ńýő´[wµQ=Ôľ+R¦+ç ¤{çu1z^⎵´ĂššÝ4ÝFDżTłĂN¦‡^{ęůĎ˙ŠčŽ—.]"<ŘpÝHs¬gϞͻîşËŻ6řł?ś)‘H$ßĐŘ_HĚŠÖú8‰D"‘H$~Ŕö©Đ¶m[TTT8˝íڵǔ)SĐ«W/”••!<<ÜP™4i6nÜK—.>úč#»ź úayÔĽys%-55Ŕ?]«ĆŢ˝{;vTŇ‚1bÄ´nÝđĚ3Ď`čСxíµ×°`ÁÜşu ŐŐŐ†ě­(jţ‹°ű âC‡!11Qő'˙zč!ˇşŐmkmÚ´Á­[·”ĎFŰW϶ź`vĚ#99pŕŔçΩÍţ6mÚ8˝ď>ÄÄÄŘĄŰbĘ"ő衇P^^ŽŰoż]ŘvWʱ!ęæM›bĘ”)رcňóóQ]]ŤÓ§Oă'?ů ±ő±¨MÎbR¤ťôlv§­%ő©Űžżüĺ/'Ožôx9zc‘«ă†h<¸2xjî0Ň÷őć4wĆW‘yG ‘ądŔ€°Z­Řąs' 44ĐżÍ|EÚO´ÎÝ»wG˙ţýńúëŻÎť;‡ÚÚZTUUaëÖ­€M›6áÉ'ź4T/‰D"‘H$‰D"‘H$őą|ů˛®žhCKt¦˝čţ6´´[OjŁ˘ő)Ó•sŔsçF5/oř188X7Í›şŤ;:™#zí©ç?wü+˘;ÚúË•+W ×Í,ÜsĎ=řꫯPVVćoS$‰D"‘H$UäÓ*‰D"‘H‚G}_|ń „ÇŁo߾ظqŁá|¦M›†Űn» +V¬Ŕ矎^˝z©Šľu‰ŠŠDGG«^· ÎDňC‡ˇGŹŤŤĹÜąsѢE öŰpĄ®RPP€üü|Ő—5¬V+ĎÔÍ•öuć›ř}öěY»ď´k×ĐŞU+Ă6ęqńâE?řĚ("ő_¶l˛łł±xńb—m4âg#>=z4°zőjĽűî»HKKS®‰Ä(˘69‹I‘vҳٝ¶–8ÇÖ–ť:uňyŮ®Ž˘ńŕĘX驹ĂHß×›Óü1ľŠ2qâDĽńĆ5jfĚĚĚLĚ›7óćÍÓüŽHű©óĉńĺ—_âСCXĽx1–,Y‚GyëׯÇÉ“'cw0+‘H$‰D"‘H$‰Ä5‚Q[[ëVδÝ߆–vëImTWËÔ;±á‰ó Łš—?üčÔmO=˙ąă_ÝQíĄŠ@ăî»ď†ŐjĹ_|áoS$‰D"‘H$UäËI¦şşZů+–ޢ¸¸Ř«ů‹rőęUl۶ /˝ô’OĘ3K˝]Ĺ×ţ’H<ÁóĎ?ŹŘŘXěÚµ [·nEuu5ćÎť«yżĹbAMMM˝ôČČHL0ëÖ­Ă«ŻľŠ‘#Gę–m{@đP˝ŢłgOŔ‚ @RI?wîŢ{ď=ŔđáĂQ]]­ü…wjWęŕ* ¨¨¨¨wqęÔ)¬^˝€gęf´}ç~°ýµfŰ_‡¶qáÂ˙mK›]UU éňź Z¦"őűěłüî»ď5Úľz~¨¨¨`bb";věČďż˙^Iź2e űô飔;lŘ0`VVĎś9ĂW^y…Ŕ]»v±¶¶V±ßźZ|ńĹlҤ ###ąk×.VTTpĎž=lٲ%đŰoż%i<ľ»téB´Z­¬©©a˙ţýŮşuk=zÔ©=îö#QÚřöŰoĚůóçŰĄ‹Ä–Źű€¨MÎbrÇŽşí¤głh[‹ťťí•őZ]ĚşoéŢ˝;°°°PIËČČŕĂ?¬|éŁÎĆűşčŤEFcކh<荕jőĐűŽîö}˝9MÔO®Î;Zu™Kć͛Ǹ¸8ţéOâ®]»xŕŔćĺĺŮÍ鎴źŃŘ?>- sss•´„„:´^ů"ő2ľO0;;Ű«e2ŢöŹôż:ţÖ|Ą{HĽŻ©ÎâŢ›z«-ďšš.Z´÷Ýw›4i╲ę–'ůĆ´?ŃjűW_}•Ď>ű,űőëÇľ}űňôéÓ~łĹŐűĚÎŇĄK™śśěŐľMş?ŽyjNp%¦ęÚç‰u€ŃµLC‰µ†ŽŻúiŢ}ȨQŁh±XětRýl@Kt¦˝ěŰ·OW÷×ÓnÝŃFEPű®H™"ç ž<źpÄčy‰;~T‹‡;v°E‹|ď˝÷ś¦‰č6"úĄ;:™zí©ç?wü+˘;;vڏpáBCő˛a–őăŔ9|řp›á|±/pµ5V ®a|9źű_íéÝÁ{?_ĆśÔëăoÍÖ‘†řěš·ç@o|Ťżćµ¸’ăŽ{Řž“öĆł‘ZëcŮ›ÜŔ,›Ž@aďŢ˝|â‰'XUUĺ‘üÔ~QK{üńÇ™••ĺ‘2 ›pâ *++!Ă ÜĽyÓ'“Ź?ę­ő —;hůËÓýE-?Çúň‘Ga~~ľÇĘmčůe€÷ßźO=őÇŹĎ˝{÷’$­V+—.]Ş\›:u*ËĘʸdÉ%-33“7nÜŕěŮł•´ĺË—ł¤¤„x÷ÝwsѢEüíoË!C†8}ŕvöěŮlßľ=ßyçŐëC† á[o˝U/Ý&š.[¶Ś×Ż_çŐ«WąhŃ"Eŕ,//Wµďřńă4hŰ´iĂ:pęÔ©,..Vň]łf [µjĹ^˝z1''‡+W®d›6mřđĂóđáĂśrä_|ńEĄM^zé%^Ľx‘›6m"¶lŮ’ .dQQQ=;´âÔ¨źE|X—©S§˛   ^şV •——sŢĽyŞ>ÎĎĎWí"69‹É‚‚ÝvŞ­­ŐŤ{‘„Óđ·â+ÝCňŢÔÔ ­¸7ŞѶęćíé~'µc}ÓţD­íW®\É-Z°¦¦†EEE|ä‘Gřź˙üÇ/¶!^=©K×ÍëćÍ›ŚŚŚôÉ8ë‰qĚť9ÁŐr´Ďë#cŞÍ‹żú’Y÷!ź|ň đ>°KŻ«±8ÓI}íEK#),,Ön]ŃFőĐű®ž¶©w˘ĄÍÖEô|B Łç%7nÜpŮŹjšŰîÝ»Ĺ={öh¦‰ę6zúĺ… Ü:źAŻ=IýpŐż"şăÚµkĚoľůF¸Nu1ËúqÖ¬YLLLô·>Áěgj­±e ăŻůÜ×ř[Ks†§ö~ľŽ9­}7ž) Ěg íŮ5łľ H1oÔVÎ jq%Ď%\Ăń9iO?)_đfŮt'Ožd§NťTXq…ożý–}űöŐMkěŘ6ţšżQ|5ůř˛ŢŢŚkLÖZőůňË/™čŇ_=iŚůe€@ˇĽĽś]»veEEE˝kfŰ´pV‡Ć„ô$ĐiLŰH$ŢćÍ7ßä’%K”ϵµµĽpá7mÚÄ;î¸ĂŹ–5äËţǬá4ü˝W˘»ďđw[› w}á®¶ĺ©¶Ú±Ť}’`÷{ý…âŐ“6¨ĺĺËqÖݲÜůľ'cĘë9ż6ţěKfއ ©µ>‚DâeHâÉ'źÄSO=…·ó»xń"† ‚k×®9M“Hť†×Îęóă˙qqq1c†,“4FÖ¬YI“&ˇYłfţ6ĹeB<ôD"‘H`ńâĹ9r$FŤĄ¤ˇcÇŽ¸ďľűСC?Z'‘H$IăÄ,Ú–Yěź .Ŕb±řŐ3Ä«'m0C}ü‰bJŇ0hě}É6lŔ˙ýß˙áĘ•+ţ6ĹŁX,ݧOźö›}ZşĽŮí4‚?<ĽĽ<ĽüňËţ6ĹmîľűnÔÖÖâرcţ6ĄŃ¨k,9ꛇ@Ť!GdLIód«Äs8{NÚĎFĘ—$^gűöí8rä|đA»ôÇăŢ{ďĹĉńűß˙M›6Eyy9ŕćÍ›X˛d FŹŤ{îą @nn.ŕĎţ3Nś8Ë—/c„ šiV«۶mĂ#p˙ý÷+¶Ś7ŃŃŃ(**Â#pűí·ŁGŹřüóĎíě[˝z5ŇÓÓ‘‘‘ĐĐP»Mµžýzäää`úôéčŇĄ ®\ą‚´´4DFF˘GŹřűß˙®ÜwćĚ<öŘc5k†Žääd?~\ą^^^Žůóç#==S¦LAJJ V®\©YîË/żŚĐĐPLź>ű÷ď|üńÇŽŽĆľ}ű”űÔŇ*++‘™™‰qăĆ!++ sćĚ©Wß’’<űěł={62331hĐ dff˘¨¨HÉ7$$ááářôÓOQ\\ŚôôtX,ôë×'Nś=zQQQřă˙h¨ÍŃłGÄÇ"őv†ŐjĹ'ź|‚gžy]ştÁĄK—’’‚Îť;ٍ¨Čiůjq 8ﮢÖ_***°eË<ńÄčÓ§rrrp÷Ýw#&&ű÷ďG^^† †¶mۢ{÷îvíˇ–źV}l 4ëׯG~~ľ[u‘H´8xđ zöě‰nÝşaíÚµ?~Ľę}¶>n¤Żű Ń:4t¤$‰DâČgź}X·n ”ô#GŽ`Ö¬YŘĽyłżL“H$~ŔŮ^[TgpUĐŰłk٦§8˛eË„……Áb±`ńâŨ­­ĽýöŰ ÁĆŤUíŐ¤ôęˇu]ŻFő4=íËm-‚š¦ćŞŹŚ´#ëׯGPP˘O–––bůňĺvi®ć čÇ˝šŢh뤎Złř(,,TÍŰĆ×_ŤÔÔTDDD WŻ^Ř»wݰOüˇK<‹3ť×čř©Öö;wîÄ„ P^^®Ä‰íł#F5îuëÖŠ{3Ä«–Ž+˘·‹ćeăÚµkĘ8•Ç+×\ŃÁ==Ž©ˇ6'8â,¦śÍ]Zö©ˇçWÖ2îĆš#ÎüŞOîśOřÇWÎŻ€˙ĎŢ˝ÇEUí˙ă ¨ —H1#H@Ăôt“0óˇÖIĄ–śI‹-đÍ7ßŕťwŢAyyąÚá(†d˙ÂĂĂ-^ż%×ADňň¶Ž»ˇ-OG˝®uýúuĚš5 {öě···ÚáX-$$ľľľ8räÚˇ8$ąů»\ß/2ć06Ć*))1:†25–›OĘÍ5ĚícíŮź‹”§Č|ÚÚ1™Ž±9˝©}3w®Č·3sć~€ńń¬Ň÷×Ô…cmJ‰|™\›“+kŃ}27WČ÷®ßokîct–ä+ŚťK‚‚‚đÝwßYtŽirç|¦úGľ"w/޵ý«±¶nŹ~M.6c÷IëŘüŢH«s s”ź#stýë_IŁŃV«­µ<,,Śüüüô˙ÖŚe,mkRŚ•«H{˛öú„%×QĚ˝~E$_źćŽ×L±×±dlŰŽ>ÉÎΦąsç*QýeÍuÎË;Gľ®ĄŐj)%%Ĺěë×R)żýâ‹/ŇđáĂŐĂćĚťČÍßEú~Kď Ń‘C‰Ś…MÍ'ĺĆň–ô±öęĎEËSn>míLdNoiŽÉ9sŃąź©y‚Ň÷×ä6¤b7wÎ!Ú6Lµ9ѲŮ'sr…|ďšéůťĄ÷1šbë<‘%ë77_a,o4lŘ0Ú˝{·EçxŃö z®1$Ň/Řó9ů"ů{Q­í_Mµu[÷kr±»OZG©{#ŤŤŹůa+8ҤÑSłfÍę,oŢĽ9 E‹Quu5ťM$÷em]#—SS˘ŚDr¦b3µĚ’u›Óî ۢ©z•Ę™ÓÎuűVłť,Z´Đß˙ţwá2±wîŘY9âüD4ĎkŞ]I1ç!krÜ"íŢÚ«á¶DëAd]DŁş›ĹüýýÉĂĂ,ËŰę<&zťEtßĺú.cź«ąL®|¬˝¶aI[“b¬\ÍiO–\ź°ô:Š%Ƕ\}Z:^“bŻcÉضyÂS‹#ĺ·'MšDť:uR; ›3w^ 77wL_“Ü}!¦–‹Ś…Eć“RcyKűX{öç"ĺ)˛˙Rq‹Ţ3"7§·&ÇdČš1¦1ry,‘yĄąe%·LdţhmľL®^DËZdźĚÉň˝k¦çwÖŢÇ(Ű6odé9^¤=žk Éő Dö˝bN>BGî^Ôš1š[ö"9©ň4¶ODĘĺŚÝ'­ŁÔ˝‘ĆĆÇ.`ĚĆnܸ__ß:Ë?ýôSx{{c„ čÜą3JKKáííŤĚĚLDFFJţĚ]ź>}¬ŽGę'”|}}QQQˇ˙wŻ^˝P]]ŤíŰ·ÜÝÝ=zôŤ_”‹ËĂĎĂĂCż,66Ŕźr€wŢyýúőòeË0kÖ,TTT@«Ő€ţgłőźwuuĹ[o˝…fÍšŐÚVź>}PVV†‡~X2WWW“ËvíÚ–ÜúźĂ5,nÝş8h×®zôčĺË—._ľŚŞŞ*Üż6l¬YłC† ŃŻC¤Î ‰ĆcŞŚEö[”n ŹSŰ—bëăC„T;÷óó3ůsĐ"tíöćÍ›V­‡1ĆcŚ1ĆXĂ&7×–Ë3Xš™ł‹Ć&•K3äçç‡řřx¬^˝×®]üřăŹF‚Öp?Śĺ¤äöCd?Ťí‡9ů4‘Ü—µu-ÇXNM‰2É ZĘ’u[“37OjN;שąľ×^{ púôiáĎ‹R*wĚ”#šçµ¤]YĘš·’ńŮł˝ŠÖąjîCóćÍQ^^Ŕ˛<¸­Ďcr×YD™{M@Š\ů(ym°Ľ?5V®Ö´'‘ë–^G±äŘ–«OKÇkćRňXbŚ1fÜźţô'ś:u ÷îÝS;‡"77wL_“9sxCJŤ…ĄĆň¶čc•îĎ•.Ϛ̽gÄ؜ޚ“![ĚY¬˝/L*@™űkL±6_&W/J–µ9eĚ÷®ßo@™öꌬÍYzŽ·ĺ9Ö06 vż8ö5@ţ^Ôš1š[ö¶hëJĺŚÝ'­cë{#ůafs®®®¨ŞŞŞłüő×_Çożý†—^z ‡BLL VŻ^Ťüü|dgg×:éTWWŰ#dŚ7+W®Ä#đî»ď"11ÉÉÉHNN–Ťß­ZµxpéСBBB””///ý{u'‘‹óçĎGZZRSS-ŠëęŐ«€üü|ŁďŃu—.]޵< ŕăăŁ_6nÜ8;v ™™™HMMĹÜąsń—żüźţ9Nź>ŤŕŕŕZ+-!Ź©2Ůok™ÚľG8>lEj ÂcŚ1ĆcŚ™ËÜą¶!Kó"svkc3”€&Mš`áÂ…8|ř0:wî,™h—S3'%·Öä&Ěɧ‰äľ”.OCĆrj¶*#ĂÜ ’äÖmMĚyRStů˝6mÚŘl¦äŽ™rĚÉ;Ű“=rÜJĹ©D{µw=XrµőyĚÚë,:Jô]rĺcŹk"Ś•«­Ű“5csŹm‘úTjĽf‰ú|M‰1ĆÔđôÓOC«Őâĉj‡âPäćďjŤém9włg[ßúóšsz%÷ÍíĚŢůĄX;ç«%ËÚś2ć{×LĎďśµ˝ZËÇľ3śwů5DÖŻV[‰ÍŘ}Ň:¶ľ7’`6÷Č#ŹH>Ń8cĆ „„„`çΝذa´Z-’’’ňňň: Ő3gÎ`É’%•••µ^—Zf©ŞŞ*ś®­«{l‹Ô§Ră5{KŚ1ĆŚk۶-Ľ˝˝qäȵCq(rówµĆô"caKçŘ–ö±ŽÖź[ş˙Ö¨9§WrßlŃÎlq_1–Ö…T›R"7jŞ^DËZdźĚ)cľwÍôüΞíUMJć+ŚQęÜd‹Ľ¨Ž#_äďEµfýrąhŔ6ýšHlĆî“Ö‘ş7R¤\„łŘ€hŔ€j‡áđFŚAŤ†JJJj-÷đđ ‚‚""ŇjµäăăCQQQtďŢ= !4|řpZ·n%%%QďŢ˝©¸¸üqňôô¤+W®č×'µ¬¤¤„P«V­ô˂ɰé·nÝšV«%"˘ääd Ą/ľř‚vîÜI ¬¬,ެ¬”Ť_TDD¨µÎŐ«WÓÓO?­ŹĂÇLJ4 íÚµ‹Ö­[G-Z´ tđŕAÚ»w/yzzęŃŁ-]ş”¦OźNŁFŤ˘ęęj""zě±ÇUWWSee%őčŃš5kFGŹŐos۶mäĺĺE;vě0şě·ß~ŁFŤ‘żż?íÜą“ĘËËé§ź~˘‡zĐĹ‹©ĽĽś"##)00®_ż®_ׄ (::ZżO:3gÎ$ŤFC'Ož¬U&ýúő«SV"uV^^N(88X˙o‘xL•ń¶mŰd÷[”nJKKk-7µýśśś:íZäřaX^DŇÇËÝ»w …‡‡ë—…††€ZÇ´n˙ŞŞŞL®Oę8Ő9~ü8 9sćďGCeëţ‡ű7Ćłź´´´:ăĄńyť1¦{śOĐĉ)--Ť˙$ţPZZšMËß–ë·ąą¶\žA$"EdÎ.›aţ@jŻsăĆ rssŁîÝ» •Ź\NJn?DöÓŘ~“OËÎΖÍ}Y[×ĆČĺÔ”(#‘Ü ”ţýűš>}:ť?ž>ţřcňóó#´sçNŞŞŞ˛hݢí^*ßcŞ^ĄrAĆڇԺ۵kGčÎť;úecÇŽĄW_}Ő¬2±wîŘY9âüD4Ďk¬]I‘Şű;wî ŽÍ’·H»w„öjąůSë""jŐŞ¨•Ó~ä‘GôedIÜVç1‘ë,Rڵ)ąľK*>Ăq€\ůX:–1V>–ö§ĆĘU´=Yz}ÂÚë(ćŰrő©cjĽ6ţ|jßľ=mذÁd\ö:–¤đ<„1¦&GËowíÚ•ĆŚŁv6eîĽ@nţnîľ&Ă1‡±1–ÔFd,,2ź”Ë[ÚÇÚł?)O‘ý·ćžą9˝59&C˘íĚśąź©y‚Ň÷×Ô…T~PŞMY›/“«ѲŮ'sr…|ďšéůť©˛ťs˛užČ’ő[šŻ0v.±ô/ŇLÝ#gŠ\ż@dżk ćć#ÄîEµ¦ěMµu[ök"±»OZÇđŢČYłf‘ŻŻ/]ştÉD‹¨ËŘř°‚ŁM:Ő˙ţ÷?@»víŞµ=őÔS”’’BűŰߨoßľúÉĄK—(66–üüü¨eË–G·nÝŇvęÔ©ôČ#ŹĐ7ß|ctYYYMť:•Z°`Ą¤¤č˙=sćL***˘… ę—M™2…îŢ˝K»w呂€ýrÝ_óćÍőë7żÝEąůóçÓíŰ·)//ŹRRRjťŕ–.]J>>>ÔąsgĘČČ E‹‘ŻŻ/˝úę«”źźO'Nś —^z‰|}}©uëÖ4qâD***˘;wîЇ~HŤ†ĐěŮłéęŐ«´fÍ@=ôÍ™3‡ i÷îÝÔŞU+úé§źôŰ•Z¶wď^ŠŽŽ&ooo ˇ””ęÖ­Ť=š~üńGŞŞŞ˘’’šÁЉ'ڶ)S}WNNNťřŽ;&9+KĆ2Ö´5)¦úąötóćM‹ŻOYwEôú•\}ćçç×zݱńÚرcÉĹĹ…Z·nm2.{K†xÂS“Łĺ·ÇŚC]»vU; ›˛d^ 7—ëűEî ÉĚĚ”c™šOĘŤ…MÍ'żüňKJJJ2:×°¤ŹµW.RžwďŢ•ťOçääX5&™Ó[šc’"×ÎLŤÓĄĎJµ9kk‹˙ýďéěŮł’ó©ą›ů2ą6'2?É×›+ä{׌﷩˛ťsrćĚÍW;—ČťcL˘çXS÷ČI1§_°Ç5 .X”Ź»Őš˛'2ťk°uż&›±ű¤u ďŤüřăŹéŃGĄÜÜ\ˇ6˘cl|¬ů˙ Y`ŕŔ€M›6©‰ăëÓ§ÂÂÂđńÇ«ŠU«VáöíŰx÷Ýw<ř)•k×®áçźƤI“póćM«·Ń®];ś={|2ö@ll,Z¶l‰+V¨ŠĂłu˙3pŕ@|ýő×6Y7cŚ1i¶ňyť1¦¤p¤Óh4HKKĂ AÔ…™ˇĽĽ;vÄńăÇŃ´iSŮ÷sNĘ1ز¸ŽmËącµlܸ¶ůüŕë*öRźŰ+cÎDnĽ–••…aÆ!##C…čäŮzž Ńhl˛^ĆXýáHů¨ĺË—ăÝwßEaa!\\\ÔÇ&ě1/° 3ĆŘ,™sŘş?¨/ý c5™şOZ©{#ŤĺWYµVĆ­Zµ 1112e ÔǤÔÔTL™2ůůůúe... ÄóĎ?ŹÖ­[›üĽHÂęěŮłVÇÉh}‡‡‡Ű!Ç‹GÔÁ‘••…uëÖ©  80ćhJKK1räHL™2ť:uţśV«Ĺ#0räHtëÖ͆2ćxřĽÎSRPPÚ!0Ć ,]şńńńB0Ƭcmî1{âöĘă05^+//ÇâĹ‹±rĺJ"s iiij‡Poŕ믿ĆĎ?˙Ś   Ě™3Ç)nV>~ü8fÍš…•+WÂŰŰ[ípr¤|TÇŽQRR‚‹/"44Típ<fڱ?đśĂ18ë=}Ě<Ćî“¶Ç˝‘ü0ł‹-Zŕ›oľÁ;5+WÂĂĂCíŚÚż?ŕłĎ>èQŁŕďď8räRSS±víZ“ź}Z­¬¬L˙_OOO+"fjr´§-ׯ_ǬYł°gĎNđ9çž{Ď=÷śÚa0&iëÖ­€I“&ÁĎĎϬϮ]»7nÜŕoşe ź×cڱúçŕÁ‹‹Cyy9ŞŞŞĚúâ ÎI9[Ö×±íX›;fĚž¸˝2¦.ŃńZvv6fĎžÝ ŻŹpľÖzHMMĹâĹ‹áďďŹeË–ařđáhÔČ9n)--…‡‡Fڎv(ŚÉęС\\\pěŘ1~ŔńX1ĆţŔsÇŕŚ÷ô1óIÝ'mŻ{#˙1pVoDFFbÖ¬YXşt©Úˇ´zőjÄÇÇă‹/ľ@`` ˘ŁŁ1hĐ 9rk×®Eűöí­ZYYŢ˙}äääĆŹď°?űÉ­UVVbÍš5X·nŐ‡1ćŇÓÓŃľ}{łüäÖ?ü€{÷îŮ 2ĆcŚ1ĆěÇÓÓĹĹĹpqqÁúőëѤIŮĎpNĘ1ز¸ŽmĎÖącĆ”Äí•1u‰Ž×"##ů¦f±ňňr¤¦¦"44+W®Äżţő/dee!..Îi€ÜÜ\ľNČś†§§'BCCqüřqµCa&đX1ĆţŔsĆě«ć}ŇöĽ7RCüȉĹŘ´i“Ę‘0ĆkH¸˙a YLL Ú·oŹĺË—›ýŮk×®!00Ű·oÇź˙ügDÇcŚ1ćÜ4 ŇŇŇř›9c¬Ú¸q#lÓo*ăĽcŚ9ž'8­V‹U«Váß˙ţ7ŠŠŠ0nÜ8Lť:>>>j‡f‘·ß~/^Äž={Ô…1!Dee%ţď˙ţOíPlÂóĆcŽĎÖý÷7ŚYĆX~•€1ĆcŚ9…ŠŠ :tŃŃŃ}ľU«Vxę©§°eË…#cŚ1ĆcŚ1ĆcŚ1Ćl‹°iÓ&´oßńńńčŰ·/.\¸€””§}ŕ_`ΧcÇŽ8věÚa0ĆcŚ1¦Ç0ĆcŚ1§přđaÜ»wĎ⇠66[¶lá§ËcŚ1ĆcŚ1ĆcŚ9Ť={öŕ™gžÁoĽ?ýéO8sć –/_Ž–-[ŞšŐrrr¤vŚ ëر#.]ş„˘˘"µCaŚ1Ćc ? ŔcŚ1ĆśDzz:jń:bccqíÚ5>|XÁČcŚ1ĆSN^^6mÚ„Ůłg›\foĹĹĹŞm[Š˝âq´ýv$ŽÚVc¶ĹçEóŮŞĚŇ öî_µl5.f[xńĹŃ«W/řůůáČ‘#ظq#BBBÔM1999üËĚ©těŘD„'N¨ cĚBZ­éééj‡árn‡ÇŰŚ1gĆ0ĆcŚ1§žžŽçźŢŞutęÔ Ź=ö¶l٢PTŚ1ĆcĚüüóĎĐh4đńńAÇŽŃĄKh44mÚ]ştA‡Đ´iSh4ÜĽySŃmwéŇ“'OVd]gĎžErr2 „ŻľúĘč2{š?>^xářűűŰ}ŰR̉ǚşq´ýv4ŽŘVc¶ŐĎ‹Ööń¶(łŠŠ Ěž=]»vµy_č(ěŐż[¶öâ¨q1Ű;}ú4 „çž{Z­{÷îĹîݻѱcGµCSTII Š‹‹ů—SiÓ¦ |}}qěŘ1µCaĚ*őa¬h®‚‚L›6 ľľľV_w¶55ęÇÔŘ[éxĄýńx›1V_đĂŚ1ĆcĚá8€ččh«×Ő·o_~€1ĆcL%ąąą6YďÝ»wńâ‹/âúőë8vě222ÁÁÁČČČŔ‰'pőęU´mŰĺĺĺŠnű±Ç»»»"ëŠŔG}$»ĚžâăăqęÔ)TVVŞCÍvcN<ÖÔŤ#ě·#sĶʭú[p¦XuâyQŞ1§îlQfnnnHHHŔąsçPUU%ű~KűBGjŁöę_Ě-[{qÔ¸í\ąrŁFŤÂ“O>‰3gÎ`ăĆŤŘż?bbbÔÍ&rrr€`NçÉ'źä‡Ă’ËI-S2§ĺ,|}}1{ölxzzŞJ-ŽR?¦ĆŢJÇcí|K)<ŢfŚŐŤÔŔŮ}ýő×Đh4j‡Ác¬0`€Ú!0fWYYY¸uë–"ôë׋/ĆĹ‹ńŘcŹ)cŚ1ĆqéŇ% 6 {÷îU|ÝwďŢĹäÉ“áááaô=~~~3f îŢ˝«č¶7lŘ čúÜÜÜ„–Ů‹››š7oŽüü|U¶oŘn̉ǚşQ{żťŁµUְٲŹQš3ĹZSCúůůůX´h7n¬vHŠĐjµ;v,Ţ~űmtîÜYíps8ü )¬ˇIOOGÓ¦MŃ©S'«×Ő˝{w4kÖ [·nĹřńăŽ1ĆcŚÉąző*úöíkłoWzĺ•WФIŮ÷Ť;¶ŢßLSźŘşÝ0Ćęg:W8S¬¬¶†Rw e?s$ĄĄĄXşt)fĎž ooo|ňÉ'>|85j·räääŔÓÓľľľj‡ÂYžxâ üç?˙ńMŚĚaHŤĺx|çظ~ŕrhşv튴´4µĂ`Ě)IŢ7HŚ1§uäČjÓ¦ =ţřătňäIµĂQ\§Nťhâĉj‡ÁcĚ >śşwď®ŘúL={öTl}Ś1ĆcőJKKłÉşgÎśI¨Yłf4zôhýň˘˘"šżtäČzä‘GhůňĺDDA(//O_O<ńeffę×}÷î]JMMĄ#FĐ3Ď}:5jÔJKKŤn_dżMŐ§H;yŹRĺkîţ+ŃVĄ¶_ąóĂŠ+HŁŃî2Gqq1}ôŃGúeUUUôË/żĐĉ)88®^˝J/ĽđµiÓFňŁD]z].sëÇŇŇŇČÖ—Ť @ Ptť–ô1Ćę#006oŢl´ž¬=ŤĹj¨¬¬ŚÖ®]Kýë_©k×®ô믿ҟţô'zôŃGi˙ţýtîÜ9zíµ×čᇦ:çRSÇŻĄ}‘őçĹM›6‘źź ¤¤$ýg–-[F...´bĹ "’?Τ;†,éű¤úcu'×ď[ۇ•——SBBĹĹĹQRRMť:•‚‚‚LŻRű`MµdĚ&wž&?÷Őí_Db’ëË,)[kŹO‘¸Eâ˛ä8±”-ç ż(0 IDAT Yii)Ą¤¤źźůűűÓĽy󨼼\í°ěnĆŚ&ÇŽŚ9Şýű÷şté’Úˇ(ÎógcjľféŃŰ“ËI-łt¬¨łxńb2dŤ3†ÜÜÜ€ţO.~9˘ăCąqžč8]gţüůäććF‰‰‰´˙~"zG ¤˙ýďú÷I-ł&Ńú±ĺ\JJͱ·T<ÖŚ‰Í™oI1UޢíŘ’y@CÄýcÎ…ŹVĆśÔÚµk©iÓ¦ôňË/Óť;wÔÇ&âăăéé§źV; Ćc <<śŢ˙}ĹÖ·nÝ:jܸq˝íCcŚ1Ć,aë›| oŕ*))ˇ°°0úŕôËňňň(,,ŚBBB¨°°PŃíŐ”””DčÔ©SúeW®\ˇţýűSUUm۶Ťš6mJ(>>žöîÝKëׯ'ooo@éééúĎÔÜŽV«ĄîÝ»ÓСC©şşšV­ZEhëÖ­DDÔ¶m[ ŐżżYłf)»á2Sű`ĘرcÉÝÝťŠŠŠčÁE€€2dţ=•••µn˘×] ť1c]şt‰¶oßNčąçžÓżçí·ß¦łgĎę˙Ý»wo  ââbĘÍÍ%///@łf͢˗/ÓÚµk EEE™ŚWŞ,Dâ1¬"˘°°0ňóóÓ˙{đŕÁ”——gtŰ"Ű1Vź"íhßľ}ÂmM‰ň5w˙•j«RäÚŻčů!44´ÎEIÝ˛ŠŠ :pŕ€ţ¦ç9sćĐž={häČ‘unB0ç¸7U¦^ż}ű¶ÉxĚ­[pÖ‡Ěďcňňň$ëcذa´{÷nŁő¤Ä±(rŚTWWÓ… ůřřĐöíŰéôéÓ€‚iŢĽyTTTDGŹŐßÔ^“ÜńkibÍyQgńâĹ€věŘQkŰoľů¦ţßrÇ™SÇ%}źT?břo‘~ßÚ>´˛˛’˘˘˘čí·ßÖżţűďżSŁFŤdŹWĂ}°´ŤZ3f3už6çÜk—hL¦ÚŁĄekÍń)·h\–'–⇔uďŢ=Z´hµlŮ’ĽĽĽ())É깏3>|8őęŐKí03[aa!i4Ú¶m›Úˇ(Žoţ¬ËÔXÓŇ9˘Ąc[ąő‰ÎÓ-+.^Ľ\]])??źćĚ™C(11Q(~SĚšç™3N'"şsç :´Î |÷Ýwäááˇ˙ڱeÖĆ"R?ş˛·Ĺ\JŠá¶ ă±vÎ*2ß2ĆTy‹´ckćX ÷Ś9>Zs2Z­–Ţ{ď=@ăÇŹ×kO}´qăFruuµIâ’1Ćó¸uëi4úţűď[gaa!5nÜÖŻ_ŻŘ:cŚ1Ćśť˝x˙ý÷ ]ż~˝ÖűÖ¬YChňäÉŠnݦüü|ňöö®uŃgÎś9µ.⇅…*++Ó/[¸p! 7ŢxCr; , tîÜ9ýë•••´jŐ*ý·˝.X°€6lŘ@D.ś…††RăĆŤec7\&˛Rtć–-[¦_K^^^TRRBDD[¶lŃ˙*ŃHu-‰üýýÉĂĂ‹1ć$Ž?Žgź}çΝþ}ű0lŘ0µC˛ą–-[âńÇÇľ}űÔ…1ĆŠŇÓÓńÄOŔĎĎOŃőĆĆĆâűďżÇýű÷]/cŚ1ĆŁ»ŔréŇĄZËĺÚ‘á{”*_s÷_ɶ*ĹTűUűüP“9uaM]٢}6d¶hCÎr®ÄŽ_Kű%¶=räHxzzbÉ’%ŘĽy3  ÍŇr–;†”îű±~_ŽÜţ^˝zU˙>µ¨qN–ë+Ec2ŐŐ([‘¸Eâr¦óQCFDŘşu+žzę)ĽńĆčÔ©Îś9ĺË—ŁuëÖj‡ç0rsskÝ É3‰ŚŚÄÉ“'Őٱ±¦­ć–nĎĆŤ‡•+WbÄx÷Ýw‘ääd$''ËĆo Ăńˇ©qž9ăôůóç#-- ©©©ǦT,"l1—r6Öć«aŽĹc¶Ŕ0ć6nÜ®]»"00‡Â3Ď<ŁvHvĂ0ĆX—žžŽččhĹ×űÚkݎ¨¨{÷îU|ÝŚ1Ćc¬.ŤFĘĘJýżu߀Şű&1ťśś@Ďž=őËŞŞŞŹÇßßcĆŚÁgź}†O>ůÇ—ýŚî"QÍŘjęر#`Ö¬Y "ýňË—/cÇŽ€aÆA«Őęż}Ůš‹q–ěđŕÁŘ   |đÁ(++Cűöí1zôhdffběر3fŚYqDDD ĽĽĽÎ…Ó3gÎ`É’%f­Ëa»±ĆŚ3‚ť;wbÆ ĐjµHJJ˛jť–Ô§\;2|ŹRĺkîţ+ŮVĄjż˘çÝ·˝éň&")§9uaM]٢}6$Öô1˘”:•<Ż#rüZÚ‡(±í‡z#GŽÄ—_~‰´´4ôďß_˙šĄĺ,w )Ń÷ÖťHż/GäĽÔm˶¤äńdéyZ®ŻŤÉT{TŁlEâ‰Ë–c/¦Ś={öŕ™gžÁkŻ˝†°°0ś9s7nDhh¨Úˇ9”ÂÂB”””đ/0§őÄOŕĚ™36ÉY0Çbl¬i«9˘ĄŰ¤çJÎAŞŞŞpňäIddd`ŢĽyŘĽy3¦Oź^ëÁb[Ěo LJ¦ĆyćŚÓ_yĺL›6 Ó¦M“ĂK߆ˬŤĹśú±Ĺ\ĘQ–µů*5ćŚ1fÄsXŐŐŐ4cĆ Ňh4G÷ďßW;$»űňË/ÉÝÝťîŢ˝«v(Ś1ĆTpďŢ=rssŁŻľúĘ&ëďСŤ?Ţ&ëfŚ1Ćs6(--ÍfëüńÇÉÓÓ“®\ąBDDĺĺĺItýúuýű&L@ŃŃѤŐj‰hÖ¬YäëëK—.]ŢVii) 6mÚ|ߍ7ČÍÍŤşwď^çµ@•••úe«Wݦ§ź~Z[II  V­ZQvv6yzzęŃŁ-]ş”¦OźNŁFŤ˘ęęj""ňńń!ŤFC»ví˘uëÖQ‹->>etŰ"Ű‘«O‘v$÷ĄĘ×ÜýWş­J1Ö~EĎýű÷'4}út:ţ<}üńÇäççGhçÎťTUUĄ/]ť™bm]SW†ńŞźůóçSűöíiÆ Bĺj©´´´:íHi  (ľ^Kűcő!µ\©cQęĽ&ĺîÝ»€ÂĂĂőËBCC •””Ô‰µŞŞJżLîřŐ1·Q⼨sńâEruuĄ™3gÖÚ†H9K9Ç™Ó÷Iő#†u'Ňď[ۇţöŰoÔ¨Q#ň÷÷§ť;wRyy9ýôÓOôĐCşxń˘Ń2‘ÚKÚ¨čń$Eä<-ŇWö/˘1™jŹŰ¶mł¸l-=>Eâ©sKŹKŮzžPźěŰ·ŹşuëF¨gĎžtôčQµCrh'Nś µÎËŚ9“C‡:{ö¬Úˇ(Ęógcl¬©ÄQÉíIĎ7¤–Y:VLNN¦ĐĐPúâ‹/hçÎťtŕŔĘĘĘŞ5–37˙`Hd|hjś·wď^ŮqúcŹ=F¨şşš*++©GŹÔ¬YłZ}÷¶mŰČËË‹věŘar™µ±ÖŹŽŇs)CRą©x¬™łŠĚ·Ś‘›sʵckćX ÷Ś9>ZsPEEEôꫯ’››}ńĹj‡ŁšóçĎÚżżÚˇ0ĆSÁľ}űeggŰdýIIIô裏ę“-Ś1Ćc ™­oň™:u*=ňČ#ôÍ7ßč—•””ĐäÉ“©wďŢ”H“'O¦äädި¨ĐżçăŹ?¦G}”rss…¶óĂ?Đ?ţń@hôčŃôË/ż}ßľ}%>Ő]xś?>Ýľ}›ňňň(%%EłcYYMť:Użť Pqq1ť8q‚^zé%ňőőĄÖ­[Óĉ©¨¨HżŢĄK—’ŹŹuîÜ™222hѢEäëëKŻľú*:tĆŹŻ_çÂ… éČ‘#u–é.¨Ęí)·oߦ„„„ZËV­ZEúWWWÓĽyóôŰž8q"•––ŇÜąsőË©˘˘‚.]şD±±±äççG-[¶¤¸¸8şuë–~źuďź9s&ŃÂ… ő˦L™bô‹ j¶Ńx $ë=őÔS”’’BűŰߨoßľ’řĚŮoSő™źź/ŰŽDÚ)Rľ˘űŻc‹¶*ĹXű9?deeQTTyzzRďŢ˝)++‹bbbhčСôĺ—_RRR’>ž¸¸8Ůň¬­ SŻ—••Qrr˛ŃxLŐĎرcÉĹĹ…Z·n-[žÖpć‡ĚícŚŐ‡\=)q,JĹjčćÍ›”@ČÍÍŤöěŮC?üđ5jÔĐřńă)??ź/^LŤ†ĐÜąséöíŰúXLť›jéC”µĂ`Ś1¦‚ôôtÄÄÄŘlýĎ>ű,±eË›m1ĆcŚ9®ĄK—">>Ţ©o€¬űŔ.nżbĘË˱xńb¬\ąRíPX=ĂÇ cŚYćÔ©S4h:uꄜślٲű÷ďçĚ”››‹   µĂ`Ě*‘‘‘8uę”Úa0f©©©>|8Fڎ_ćââ‚ŔŔ@<˙üóhÝşµÉĎk4ŮżsçÎŮz7,.ƫߩcě"ÂÜąs1mÚ4Ś1K–,A“&MÔË!ÄÄÄ )) UUUpuuU;ĆcvBDČČČŔűďżołmh4ôéÓ[¶lÁôéÓm¶ĆcŚ1ć8<¸¸8”——ŁŞŞ gĎž•|_YY™ţżžžžö Q–č>0ő‰´#Gnk¶ŕČí×Që";;łgφ···Úˇ°zŔ‘ŹAÖ09ęą—1)/^DJJ ľřâ ´k×iii0`4ŤÚˇ9Ąśś~€9˝ČČH|ýő×j‡Á]ěßżđŮgźaÔ¨Qđ÷÷9r©©©X»v­ÉĎ‹~»8ŹmżÝť1Ćę7ţeĆ@ii)¤¤$Ěž=+V¬ŕj‰‰AQQNž<©v(Ś1ĆěčěŮł¸u뢣Łmşť~ýúáđáĂČÍ͵évcŚ1ĆcđôôDqq1\\\°~ýú:9˛˛2Ľ˙ţűČÉÉŚ?j„j”Ü>0ő‰´#ghk¶ŕí×Ńë"22’`ŠqÄc5LŽ~îe¬¦+W®`Ô¨Q Ăľ}ű°aĂ?~䬛›‹ŔŔ@µĂ`Ě*¸pá´Z­Úˇ0fs«WŻF||<ľřâ ":: ‘#G°víZ´oßŢŞőóř1Ćłś†ř±/ĆTőűďżăµ×^ĂŤ7°qăFĽřâ‹j‡äpŞ««áďďŹ?üăĆŤS;Ćcv˛rĺJLś8hܸ±Í¶SQQćÍ›cîÜą=z´Í¶ĂcŚ1ćč4 ŇŇŇ0hĐ µCaŚ1¦‚Ť7bđŕÁ6ý¶Ŕ6mÚdłm0ĆSVCž'äććbŢĽyXľ|9yäLť:#FŚŕ_2WHDDţú׿bĆŚj‡ÂĹŽ9‚§ź~çÎťCXXÚá(ÂóĆcŽŹűĆś ˙2c*úá‡đěłĎ˘QŁF8tč?`„‹‹ şvíŠ}űö© cŚ1;JOOGTT”M777ôęŐ [¶l±évcŚ1ĆcŚ1ĆcŚ9ľk×®a„ h۶-6oŢŚ%K–ŕüůó‹‹ătőęUţećôÂĂáŃhpöěYµCaŚ1Ćc ? ŔJV¬Xľ}űâĺ—_Fzz:}ôQµCrh111Ř»wŻÚa0ĆłŁôôtDGGŰe[ýúőĂO?ý„’’»lŹ1ĆcŚ1ĆcŚ1Ćcąző*âăăŠożýóçĎÇůóç1räH4jÔHíđę•;wî ´´AAAj‡ÂU<==Č0ĆcŚ1UńĂŚŮŮ˝{÷đÖ[oaěر9s&ÖŻ_µĂrxÝşuĂŤ7páµCaŚ1fyyy8ţĽ]¨ŞŞÂ®]»ě˛=ĆcŚ1Vż©‚jňňň°iÓ&Ěž=Űä2ćxlUOZ­éé銮“1ĆXmÜ˙2fť›7obĘ”)h۶-ľűî;¤¤¤ŕüůóřç?˙‰&Mš¨^˝”›› ü0«ÂĂĂqîÜ9µĂ`¬Ţá|‚ů%'i﹣ě7cŚ©‰`ĚŽrssŃ­[7lÝş;věŔ{ď˝§vHNă™gž‡‡öíۧv(Ś1Ćě ==...čŇĄ‹]¶çďďŹçž{[¶l±ËöcŚ1ĆXýSQQŮłgŁk×®đ÷÷W;»éŇĄ &Ož 8{ö,’““1hĐ |őŐWF—9“šű稔ŃőTPP€iÓ¦Á××Ď?˙Ľ"ëdڱúBÉţĄ>öżŚŮKNN&L€ŕŕ`¬_żsćĚAVV&LwwwµĂ«×rrr*GÂő"""řaf3ΗPš3ĺˇ~-'iŻąŁí7cŚ©Ť`ĚNöďߏgžy÷îÝCff&zőęĄvHNĄI“&xöŮgůaĆk ŇÓÓѡCřřřŘm›ýúőömŰPYYi·m2ĆcڱúĂÍÍ 8wŞÔÇn{ě1ýŤRř裏j˝.µĚÖtß2Ş„šű稔Ńőäëë‹ŮłgĂÓÓSŃőÖ¤d]3ĆęG|XżŽ»wď"11ŁFŤÂôéÓ1mÚ4”••™Üîşuëŕéé ŤFÔÔTýĎXŻ_żnnnX˝z5 ¸¸ď˝÷¦NťŠÄÄDĽôŇKHLLDaa!ŕóĎ?‡‹‹ 4  ¤¤ ,¨µLÔĎ?˙ 777x{{cßľ}(**ÂСCˇŃhđâ‹/âÔ©S€ŁGŹ˘U«VX±bŞ««±iÓ&ĽőÖ[xá…ĚÚž)K–,ÁСC1věX¸»»CŁŃč˙ŕŢ˝{;w.Fމgź}˝zőÂÉ“'˙ůĎpęÔ)ܸqcĆŚ1şŤŚŚ Lš4 Ź=önŢĽ‰Ŕßß:tŔ·ß~ F÷ĎÔö ¬¬ 3gÎÄСC1aÂtďŢ‹-ţüˇC‡ĐĄKŚ7˙ú׿иqcŁmJ*FѶ+J®ŠěłˇŹ>úîîî4i’lžĎXy«kSĺ+RďŚ1őę—Eú7$B$')7˛$hMŢI$Ç$WO"ÔĽ¬ö IDATűmMŰfŚ1§FŚ1Ĺ>|Ú´iCm۶ĄS§N©N˝ňôÓOS||ĽÚa0ĆłˇŮłgSëÖ­UŮöţýű ť>}Z•í3ĆcŚ© ĄĄĄ©†Ý´mŰ–BCC‰H«ŐRłfÍ(22’rssÉËˋЬYłčňĺË´víZ@QQQDDTYYIQQQôöŰoë×ůűďżSŁFŤH.íš””Dj卮\ąBýű÷'"˘’’ Ł>ř@˙z^^………QHHQhhhťmI-1věXrww§˘˘"""ş{÷.Đ!Cô𤋮¬¤nÝşŐŠEDDÔZ—č2C‹/&WWWĘĎĎ'"˘9sćJLLÔżçí·ß¦łgĎę˙Ý»wo  ââbˇíTUUѶmۨiÓ¦€âăăiďŢ˝´~ýzňöö&”žžnt˙Lm_«ŐR÷îÝičСT]]MDD«V­"´uëVˇřĂÂÂČĎĎO˙úŕÁ)//ĎčţĆ(ŇvM©ą.‘v(˛Ďú6yçÎ:t(?~\6ąňŞkcĺ[XX(\ď YZZšEçs 0€ `Óm0çdŞ_&ëó Ď "ç±ęęjşpá Úľ};ť>}šPpp0Í›7ŹŠŠŠččŃŁ€şwďnVÜRç*Ăs·ČąTn;ƶe¸Ln Âgś'\şt‰ĆŹOnnnô裏ŇÂ… éŢ˝{j‡Ĺjh۶-%''«cŠyę©§hҤIj‡ˇ{Ě ęSóUKr"ăFŃ9żH~ĹÜüŽ9yScXĄň ß}÷yxxč?cl™µ±ÔŹ®ěĐŽ;j˝ďÍ7ßÔ˙[®}ČÉIŠćÍÍš“w27ÇDdşžDs±–¶mV÷Ś9>ZSŘW_}EM›6Ą?˙ůĎTPP v8őÎ;ďĽC;vT; Ćc6Ô§O ëׯ×úÜš5kMž<™j_Ő‘Z&Bwăă˛eËôËbccÉËË‹JJJhË–-´|ůňZźSňa€ŘŘXrqqˇű÷ďŃÉ“' uéŇ…ýŃ&łłłiÄtëÖ-Ů8tL•‡á~‹”Żh˝7Tü0S“\ż,ŇçžDűS©Ď¶nÝşÎöZ´hAÍš53+ncýRÍĺ"çRąíŰ–á2ą1cRśižpńâEŠ‹‹ŁĆŤSpp0-_ľś´Z­Úa1 ôĺ—_ŞcŠyóÍ7©_ż~j‡ˇľůSڱůŞĄą ‘q#‘Řś_.żb*~Q"ókScX%ó •••˛Ë¬ŤE´~îßżOmÚ´ˇŘŘXý˛éÓ§ÓŃŁG‰H,w!G$'):˛$(šw27ÇDdşžDs±Ö¶möîs..`Ś)˘˛˛R˙3EăǏǶmŰЬY3µĂŞwbbbpâĉZ?Ĺc¬ţ "üú믎ŽVeű...xĺ•W°eËU¶ĎcŚ1ĆěçťwŢAż~ý°lŮ2Ěš5 Đjµú×u?›^“ŻŻ/***»ví×zŹ‹‹|ĘŐĎĎńńńX˝z5®]»řńÇńňË/€ţ§Î˝˝˝k}®[·n€ě˘YÚµk‡=z`ůňĺ€Ë—/ŁŞŞ ÷ďßdž kÖ¬Á!Cß¶NŻ^˝P]]ŤíŰ·ÜÝÝ=zôdff"22ôŕKnjýőéÓǬméęÉĂĂCż,66Ŕź$—"·ý_~ů¨˙Ś««+Ţzë-4kÖL(ţO?ýŢŢŢ0a:wîŚŇŇŇ:í@Ž\Ű%Ňĺöą¦>}ú ¬¬ ?üpťmµk×®Î`^y”Ż%őÎłą~ŮÖô§Rç??ż:×&”[ä\ŞTůČŤAsV—.]¨QŁĐ¶m[ěÚµ K–,Áůóç‡FŤ©3źźŹňňr© cŠ ÇŮłgŐŮ‘±ůŞ’ą )"s~ąüŠ©řE‰ĚŻMŤa•Ę'č>'·L©Xä4nÜ&LŔ¶mŰťť ­V‹sçΡS§N”Ém‰ä$EçB–ä-É;‰ĆcŞžDs±JäÖcĚńĂŚ)ŕöíŰx饗°dÉlܸ)))B~™ůbbb@D6ąčÍcL}§OźĆť;wT{x¨ĘČČŔŤ7T‹1ĆcŚŮ^ff&:tč€$%%ÁËËˬĎ_˝zŔY,‘€&Mš`áÂ…8|ř0:wP©Ë+]şt©Ög>>>mSθqăpěŘ1dff"55sçÎĹ_ţň|ţůç8}ú4‚k]äµĹöW®\‰#FŕÝwßEbb"’““‘śś ŕAYgggŁĽĽĽÎg«««­Ţ~«V­ŔčMIrŰżyó&ă7•‹Ä˙úëŻă·ß~ĂK/˝„C‡!&&«Wݶh¬%Ňĺöą¦ůóç#-- ©©©u^;sćLť?ŔĽň°´}ČŐ;cĚ>¬í—ĄŘŁ?U"n‘s©’ĺcj Âłąxń˘ţ!€Ý»wcéŇĄü€ČÉÉŔă/VżDDD ;;Ű쇰™ó26_µuîB„\~ĹTüÖ0ś_›Ă*•OĄT,"FŽ OOO,Y˛›7oĆ€ôŻ)Ń>Dr’ć̅쑍ÇT=‰ćb)·ĆcöÄw+3fĄß~ű Ď>ű,Îź?Ź˙ýďµqLy?ü0ÂĂñoß>µCaŚ1féééđňň“O>©Z ˝{÷†»»;ľ˙ţ{Őb`Ś1Ćc¶7lŘ0hµZý7áš{A6""ô߲f.Ś3ź}ö>ůä >\˙šî[± ×­»i¦gĎžţř&®ű÷ďxđK[EEEĹ]bDDĘËËë\ü=sć –,YŕA™TVVZ´}ÝĹD]ů’Ű~ÇŽłfÍé_ż|ů2věŘ!˙Ś3‚ť;wbÆ ĐjµHJJ˛h¬%Ňĺöą¦W^yÓ¦MĂ´iÓęĽfŚ©ň0¬k‘ň•"UďUUUBń1Ć”#×/‹ôy†çŃţÔÖqËőK"çRkÇ-5™0ć,˛˛˛0lŘ0´mŰżüň ľüňK~Ŕ‰üěÝ{XÔuŢ˙ń×pđ|¶<Ŕ€Š'RLM< Ś•ie ÚAí`VVضWwÓrwÓźlۦĄ%¤–ćY·tµ´LaT}úÎ(U’¬9sćÎ(55kÖ´l6›őő×_[3f̰ęŐ«gI˛RRR,—Ëe5nÜŘúýň©żżż%ÉĘÍ͵6nÜhůřřXuëÖµ–,Ybť9sĆúöŰo­5jX’¬={ö\±áŔVĹŠ­Űn»íĽăgÎś±BBB,»ÝníßżżđxLLŚnĺććZ–eY÷Ýwź%É1b„µsçNë˝÷ŢłęÔ©cI˛–,Ybĺçç[˙üç?­V­ZYłfÍ*Ň×ĺí·ß¶l6›őý÷ß ¶"##/¸ěÉ“'-I–źźßy풬ƍ_öŘĹüíołš6mjMž<ŮZ˛d‰µjŐ*ëÇ´ňňň,˲¬łgĎZAAA–$kđŕÁÖŚ3¬áÇ[wŢy§uâÄ Ë˛,«YłfVŐŞU­}űö]öş‚-I…źŰ˛,kęÔ©V‡ żľżż}Wşţôôt«jŐŞ–$«{÷îÖ„ ¬#FXC† ± ŠÔ_ĄJëرc–eYVnn®UłfM«sçΗĽ»®ôŘ˝”ßßOEy^é6[–e5iŇÄ’dXyyyV÷îÝ­ZµjY6l¸ě}tĄŻÇďďë˘|}‹rżŹ5ĘŞ]»¶µwďŢ+öyš9sć\đŘ)n>ř őŕ–ču lşŇyą(çĽß˙\(ęůô—_~±$Y-[¶,ĽLÓ¦M-IÖÉ“' Źýöó5??żČÝ;/ýţgwQ~–^éz®öü{©ßA€‹q§ç [¶l±{ě1ËÇÇÇjҤ‰wŮßoŕž>üđC«víÚ¦3€bućĚËËËËú÷ż˙m:ĺş•ĆóOp©ç«×şvQ”ß-«hĎůŻ´ľrąţ˘*ĘóëËý»rĺĘbYOX¸pˇU­Z5kńâĹ—=v˝-E˝~łgĎËŰŰŰzűí·Ď;^”ÇÇ•eM˛¨Ď…~s5ëEy ^Ë“e]ţ~Z¸pa‘Öb/÷Ř.Ďk>ׂóP¶xŹ9rd1ÍĺF~~ľţň—żč•W^Ń“O>©Ůłg«FŤ¦łĘŤěělMś8QŻľúŞ|}}MçŠŃ믿®Ţ˝{ë¶Űn3ÚqúôiM0A/żü2çPnüőŻUż~ýÔşukÓ)Ą˘FŤJNNÖćÍ›ő裏*((H)))Ú·oź:¤/ľřB’äëë«víÚiâĉš7ož$)77W<đ€zöě©M›6iüřńš:uŞ4h “'OŞWŻ^ňóóSŁFŤ ßÉřbŞU«¦””=öŘcçíŽĺëë«Aéرcš8q˘6mÚ¤oľůFµjŐŇÇ\ř;ę-·Ü˘µk×jÁ‚Ú˛e‹^zé%­^˝Z·Ţz«Ő˛eKÍž=[K–,ŃęŐ«őĘ+Ż\ńëŇşukĺĺĺiŕŔçuŢ˙ý˛Űí…ÇÎś9ŁQŁF)99Y'OžTÍš5UµjU˝óÎ;JIIQvv¶j×®­Š+ęź˙üçyÇ‚U©RĄ ®;77WłgĎÖĚ™35}útMžúč#5mÚTmÚ´Qßľ}•žž®ŻżţZß|óŤěv»&L ÚµkK’8 m۶©]»vşé¦›.y;'L źţY5jÔPË–-uúôi%%%飏>RĄJ•.zűÚ¶m«ţýű_ňúk×®­¨¨(Ą§§+--MkÖ¬QłfÍ4zôhUŞTI>>>Wě>|¸–.]ŞěělĹÇÇ«~ýúš2eŠjŐŞuÁm¸XăĘ•+5ţüÂÇŃĹ»Ľcîž={ôöŰoźw?…„„(::ú˛ŹĂËÝć_~ůEďľűná÷RĹŠŐ¬Y3Ő¬YSłfÍҬYłdłŮÔ¦M›‹>®ôőřý}]”Żď•îwIJKKÓ† ôĚ3Ď”»5ß­[·jţüů*É—Ť~{,öď߿ĮeÓĺÎË>ř ÂĂĂŻxÎ;|řđy?Šr>=tčţö·żiőęŐ:uę”´k×.}řá‡*((ĐéÓ§ŐąsgMž¶ËóšĎµŕ|”-6Ëúźým\ŃŃŁGőđĂkĹŠš8q˘žxâ ÓIĺÎŢ˝{Ő¤I­X±˘p+)@ŮwđŕA5hĐ@_ýµzöěiĽĹĎĎO_~ůĄ"##Ť¶”›Í¦9sćđšĄčĚ™3j۶­6oŢ|Ţ ĹíÇÔ A´fÍš»ŽâđÉ'źčçźÖ믿.é×­Ŕł˛˛´|ůr˝öÚk:xđ`±]×M7ݤíŰ·‹ĺńň…űýňćÎť«”č×§_ż~’ţo(€Ąő;<Éç ÉÉÉŠŤŤŐÂ… Ő®];˝üňË8p eÜ AtäČ-Z´Čt P¬zőęĄúőëëÓO?5ťr]JăyJVi¬Żđüđ|ś€˛…gÉŔUرc‡ÂĂõuëV­\ą’AC7n¬ŔŔ@­\ąŇt  %''ËŰŰ[ť:u2ť˘úőë«cÇŽJHH0ť6a½đ %úGxgÎśŃ| I“&•Řu‡ŘŘX ]­Zµ*–ë9}ú´Ţxă ą\.IŇ‹/ľ¨5kÖËç†űâ~€Ň˙(ŞÜÜ\M›6M!!!ęŰ·Ż4h uëÖiéŇĄ 3ť‡bfY–233Ů)88XÇŹ×L§ +éőž_ŕžl–eY¦#wuňäI=ţřăJLLԻᆱ_|ŃtţkëÖ­ QjjŞ:tč`:pť~ůĺŐŞUKS§NŐC=d:§PóćÍŐŻ_?Ť=Út @‰łŮlš3gŽú÷ďo:`ŔÜąs5`Ŕ•äËFýúő“$Í›7ŻÄ®PĽJęy™3g4yňd˝űî»ĘĘĘŇŁŹ>ŞaÆńn·îđáĂŞWŻžľůćuďŢÝtP¬öďß/???-_ľ\·Ýv›éśkVĎ îŹóP¶°3p »víRXX’““µtéRÜL«V­T·n]%%%™N””ť;wέv¤Ţ˝{+!!Átx„#GŽčŻý«5j¤aÆ)22R;wîÔ'ź| @9đŰ;IŰívĂ%@ńkذˇjŐŞĄ;vN@9Ă0pK–,Q§NťTˇBĄ¦¦–é©mOełŮÎ0x§Ó)»Ýîv[GEEiëÖ­Úąs§é(ł8 ‘#GŞiÓ¦ú׿ţĄGyD»víŇ| FŤ™ÎC)až®E‹  Ô1 ü˲«Ţ˝{«WŻ^r:ť 4ť…Kp8JJJb;"đN§S‡ĂtĆşu릺uë*11Ńt ”9»víRLLŚ7n¬¸¸8˝ôŇKÚ·oźĆŤ§† šÎC)ËČČPÝşuUĄJÓ)@‰hٲĄ¶oßn:ĺ ĂŔť={VŹ?ţ¸Ţxă Ť5J3fĚPĺĘ•Mgá2‡>Ěd=”qJIIQxx¸é” x{{ëî»ďf®BZZš ¤–-[ę?˙ůŹbccµgĎŤ9R5kÖ4ťC\.—Űí §fÍši÷îݦ3PÎ0 č×E‡Ăˇ˙üç?Z˛d‰†j: EСCU«VMIII¦S×aëÖ­:zô¨[HRTT”’’’täČÓ)ŕ¶,ËҲeË©ĐĐPýđĂúä“O´}űvĹÄĨRĄJ¦aXFF†ěv»é  ÄiďŢ˝ĘĎĎ7ť€rÄÇt`ÚĘ•+ŐŻ_?5hĐ@ëÖ­S“&ML'ˇ|||Ôąsg%%%é™gž1ť¸FN§SŐ«WW›6mL§\Ô=÷Ü#-^ĽX4ťP˘  Îx°ůóçËfł™ÎŁśśMź>]ď˝÷ž~řáÝ{ď˝JJJRDD„é4¸—ËĄÖ­[›ÎJLÓ¦MuîÜ9eff*00ĐtÎuáwv€˛a”kńńńzţůçuß}÷iĘ”)ŞZµŞé$\%‡ĂˇO?ýÔtŕ:8ťN………ÉŰŰŰtĘEU«VM·Ţz«mÎś9¦î•W^Qż~ýLg®R×®]/züçźÖG}¤ &(;;[?ü°fĎž­R.DYárąt×]w™ÎJLPP$i÷îÝev k×®¬”1  \ĘÉÉŃsĎ=§O>ůDoľů¦Ţzë-¦šË(‡Ăˇ‘#GĘĺr) Ŕtŕ8ťN=ńĦ3.+**JÆ SNNŽ*V¬h: DôďßßtŔĂ………),,Ětŕ:íŢ˝[ďż˙ľ&Mš$___=ţřăzýő×e·ŰM§ÁŤY–Ą¬¬,^Ó…G«_żľŞU«¦ôôtÝ~űí¦s®‰ÝngŤ Śń2”¶¬¬,Ýzë­š?ľľřâ Ť9’A€2¬K—.ŞPˇ‚’““M§®Á´gĎ…‡‡›Ną¬ľ}űęôéÓúî»ďL§€ÉÉÉęßżżZ¶l©… jôčŃĘĚĚÔ¸qăŔ:tH999 Ŕă5iŇDééé¦3PŽ0 €reŐŞU Ő±cÇ´fÍőéÓÇt®S•*UÔľ}{%%%™N\¤¤$y{{«S§N¦S.Ëßß_íÚµSBB‚é(5JLLT×®]ĺp8”žž®)S¦čÇTLLŚŞV­j:e„Ëĺ’$Gŕńš6mŞÝ»w›Î@9Â0ĘŤřřxÝ~űíjßľ˝Ö®]«›nşÉtЉĂá`Ę(§Ó©víÚ©zőę¦S®(**J ,eY¦S Dť­^˝Z<đ€é$”Úµk«U«VJJJ2ť¸ )))ĘÍÍ-Sď&uď˝÷2 Ŕ#hٲeŠŚŚÔ-·Ü˘Í›7küřńÚłgŹFŽ©:uęN„ÉČČÝn7ť”¸   Ibw”†ŕ‘fÍšĄkÝşu 1ť„ćp8€2ĆétŞQŁFejń?**J6lĐŢ˝{M§Ŕ5ÉÉÉŃ´iÓÔ¦MÝyçť:{ö¬´aĂEGG«RĄJ¦áŘĺEăĆŤĺííÍ0J Ăđ(ůůů6lyä 8P .TíÚµMgˇ8­_ż^§Oź6ť("§Ó©ÓWĺöŰoWŤ5´páBÓ)pU233ő—żüEv»]C† Qxx¸¶nÝŞĄK—*22R6›Ít"<Ëĺ*So\« *Čn·k÷îݦSPN0 ŹqôčQőęŐKăĆŤÓ§ź~޸¸8ůřřÎB)éÖ­›rss•’’b:PZłfŤÂĂĂM§\• *č®»îRbb˘é(’””=üđĂjҤ‰>ůä%ť›e IDAT˝đ úé§źŻ›nşÉtĘ‚‚íßżźťPniĎž=¦3PN0 ʰeËuěŘQ۶mÓĘ•+őř㏛NB)ó÷÷WăĆŤ•””d:P[¶lŃńăÇËÜ0€$EEEiůňĺĘÎÎ6ť•źźŻÄÄDEDD¨K—.Ú±c‡ĆŹŻôôt˝ů曪WŻžéD”#Ôąsç@ąÄÎ(5  ĚűmËĎĎO©©©ęرŁé$âp8€2ÂétŞFŤjÝşµé”«vĎ=÷Ȳ,}őŐW¦Sŕ<ÇŹ׸q㤾}űŞvíÚZşt©ÖŻ_ŻččhU®\Ůt"Ę!—Ë%I˛Űí†K€Ň¤ôôtÓ('@™eY–bccŐ·o_=ôĐCúöŰoUż~}ÓY0ČáphőęŐ:wîśéŔ8ťNuíÚUŢŢަS®Zť:uˇÄÄDÓ) IúńÇ#???˝ůć›ęŰ·ŻvďŢ­ÄÄDőčŃĂtĘ9—Ë%›Í&Ó)@©hÚ´©öíۧÜÜ\Ó)(@™tňäIÝ˙ýzë­·§¸¸8ůúúšÎ‚aÝşuÓ™3g´aĂÓ)€+p:ť 7ťq͢˘˘´hŃ"qSPP eË–)22RÁÁÁúĎţŁŃŁG+++KăĆŤSăĆŤM'’¤ŚŚ Ő«WO+V4ť”Š   ĺĺĺißľ}¦SP0 €2gçÎťęŇĄ‹RRRôÝwßéé§ź6ť7ѢE Ő«WOIII¦S—‘™™©ź~ú©LôéÓGŮŮŮśs”şS§N)>>^!!!ęŮł§Ž;¦ îPµjUÓ‰Ŕy\.—LgĄ¦iÓ¦’¤ôôtĂ%(@™˛xńbuęÔI•*UŇš5kÔĄKÓIp#6›Müa&¸ąäädůřř¨cÇŽ¦S®YPPZ·n­ÄÄDÓ)ʉ={öhذa TLLŚBCCőý÷ß+99Y‘‘‘˛Ůl¦‹ĘČČÝn7ť”š:uę¨V­Z  T0 €2Á˛,ĹĆĆŞwďŢęÝ»·’““h: nČáp(99Y¦S—ŕt:Őľ}{U«VÍtĘuéÓ§ŹľüňKÓ<\rr˛ú÷ďŻ-ZhÚ´izńĹ•‘‘ˇiÓ¦©uëÖ¦ó€+bg”GAAA  T0 ·węÔ)őďß_Ç×čŃŁőŮgź©rĺʦłŕ¦‡Ž=Şm۶™N\‚ÓéTxx¸éŚë©˝{÷jË–-¦SxsçÎiŢĽyęŇĄ‹‡ŇÓÓ5yňdíŰ·O#GŽTÝşuM'EćrąŘĺNÓ¦Mµ{÷nÓ(€[Ű˝{·şvíŞĺ˗뫯ľŇСCM'Á͵k×N5kÖÔĘ•+M§.âÔ©SÚĽyłG tęÔI 6TBB‚éb˙ţý9r¤ěv»¨ŔŔ@­ZµJ©©©4h|||L'W%??_ű÷ďgg”;ě €ŇÂ0ÜÖŠ+&///Ą¦¦Ş{÷Px{{«K—.JJJ2ť¸5kÖ(//Oaaa¦S®›———z÷îÍ0€ë¶|ůr=đŔ T||Ľž{î9íÝ»WsçÎőőT”_P^^Ă(w‚‚‚ŘĄ‚a¸ĄřřxőěŮSÝ»wתU«Ô¸qcÓI(C;€›r:ťjҤ‰üýýM§‹ČČH­[·N™™™¦S”1gĎžŐ´iÓÔ¶m[uďŢ]?ýô“&L ôôtŤ9R 64ť\7—Ë%I˛Űí†K€Ň¤'NčČ‘#¦Sŕá€[9{ö¬ž|ňI=÷Üsúűß˙®Yłf©J•*¦łPĆ8effjďŢ˝¦Sżăt:a:ŁŘôčŃCUŞTѢE‹L§(#vďŢ­aÆÉßß_ŃŃŃjٲĄV­ZĄÔÔTEGG«RĄJ¦bărąäĺĺ%???Ó)@© ”$íŰ·Ďp <Ăp™™™şőÖ[őůçźë‹/ľĐСCełŮLgˇ ęÜął*Uޤ¤¤$Ó)€˙‘źźŻµk×*<<ÜtJ±©\ą˛zöě©„„Ó)ÜXAA–-[¦ţýű«eË–úěłĎô /(##CsçÎUXXéD Dddd¨~ýúŞPˇ‚é TĘfł1 €Ç0Ü‚ÓéThh¨Ž?®””EFFšNBV±bEučĐap3›7oÖńăÇ=j@’"##µlŮ2ť|XwŢy§fÍšĄ˙űß3fŚÇýa Ě ————’““M§ţËét*<<ÜtF‰ŚŚTbb˘ňóóM§(e‡Rll¬š4i˘ľ}űJ’,X ;vhčСŞ]»¶áBŔśŚŚ v@ąČÎ(qüő5JÝúőëŞĚĚL­YłF÷Ýwźé$x š5kŞM›6JJJ2ťĐŻ‹ý.—Ëc‡úôéŁ#GŽhőęŐ¦S”’””=öŘc Ô;C‡zH»wďÖŇĄK)›Íf:0*??_`g”[ě €Ç0JŐŚ3ˇV­ZiíÚµjÝşµé$x0‡ĂÁ0¸‰¤¤$ůúúŞcÇŽ¦SJDpp°Z´hˇ„„Ó)JĐŮłg5oŢ<…‡‡«K—.Úşu«Ţ˙}íŰ·O±±±jܸ±éDŔmdee)//Źa”[ÚżżrssM§Ŕ1 €R‘——§aÆiŕŔzć™g´páB¶ĂD‰s8Ú¸qŁNž}şžţyą\.Íť;W=zô¸¦ĎËş!Ę‹ŚŚ y{{«aÆűÚp9żíбoß>ľPbl–eY¦#ŕą6oެľ}ű*//OźţąBCCŻřo233¬S§NiÔ¨Q8p ’’’4pŕ@uîÜYk֬ѩS§ÔˇC=ňČ#zë­·$I‡VDD„ňňň´~ýzŐ¬Ył¤oĘ€ć͛롇Ňß˙ţwIŇ#ôöŰokëÖ­jŐŞ•¤_ß‘"&&Fźţą$)::ZŻľúŞZ¶l)Işë®»´iÓ&íÜąSŐ«WWË–-őóĎ?ëČ‘#’¤‡zH|đnĽńF·Ü߉'T§NÍť;W÷߿ǞëóóóŐ AýéOŇ믿^xśsP¶8p@S§NŐ‡~¨ŚŚ uďŢ]ŃŃŃş˙ţű/ű¦gWuC”ďľű®Ţ{ď=eddxěkŔĺ¨rĺĘš2eŠn»í6ľP"Ř%fÎś9ęÚµ«ěv»RSS‹4 Iţţţň÷÷—$ýĺ/Q`` }ôQŐŻ__7n”$Ť3F?ţřن RřďnĽńF >\ééé=ztńß ”I‡CIII…˙űĺ—_VőęŐ5věŘÂc3fĚĐSO=%IZ»v­>ţřcËfłÉfłé믿ÖÁµrĺJIұcÇtôčQ˝˙ţű˛,K#FŚPĄJ•J÷†@˛fÍĺçç+,,L’çžë˝˝˝uď˝÷*!!áĽăś{€˛!--M R`` ĆŚŁľ}űj×®]Zşt©úőëWlë†(222d·Ű%yîkŔĺxyyÉn·kßľ}| Ä0 €bgY–FŽ©‡~XŹ>ú¨ľůćŐ«WďŞ>‡Íf»ŕXíÚµ•““#Ir:ť’¤ęŐ«źw™nÝşI’V­Zu-éđ@‡C))):{ö¬$©Nť:zá…4uęTeeeI’ľůćÝ}÷Ý’¤uëÖ)$$D–e]đß˝÷Ţ+Iú裏T˝zuĹÄĨS§N:uęÔŹEŔ˙q:ťjÚ´©6lXxĚSĎőQQQZµj•ú¨Z·n­ůóç«aÆ×őůš4i˘˝{÷ęžv»]™™™ĘÍÍUnn®:uę¤ěěl­[·N 4$˝ôŇKJMMŐwß}'źëj€ç0`€Nť:uŢćŞQŁF ÓňĺË Źçää¨U«VJOO×ŕÁuÇwh۶mZ»v­ćĎźŻęŐ««jŐŞĘĚĚT­Zµ”——§n¸AÁÁÁZłfŤ‰›n---MˇˇˇÚşu«ZµjUxÜ“Ďőqqqzĺ•WtřđaU©RĄđ8çŔŚĚĚL}üńÇúđĂuôčQőęŐK111şăŽ;dłŮڶ±nO•››«Ę•+kÖ¬Yęׯź$Ď~m¸”¸¸8ýéOŇńăÇů@‰`g\˲«¨¨( 0@Ë—/żîA€?ü°pËłQŁFéĉ7nś233%I#FŚÍfÓęŐ«őČ#ŹčńÇ×kŻ˝¦ˇC‡ŞnÝşúöŰoyňó89ťNĺçç«_żľzöě©§žzęĽËV¬XQß~ű­˘˘˘ôĺ—_ęŐW_ŐˇC‡4cĆ UŻ^]’tćĚÝqÇŠŤŤŐOĂ%đTîąâ·öŐW_éá‡VŁFŤ”ššŞFŤ™N.ËËËK]»vŐ—_~©Ź?ţXgÎśQ~~ľ¶oßn: <ÚŢ˝{•‘‘Qn‡Fڎ©S§jěرś{€pđŕA}úé§ŠŹŹWzzş:tč ńăÇë±Çsë?®OIIQtt4ë†đh.—K~~~n;”–ęŐ««V­Z  Ä°3®J||Ľz÷î­»ďľ[N§“A”‡C›6m҉'äĺ奙3gŞB… ¦łŔŁ9ťNůúúŞC‡¦SJÝÍ7߬&MšhÍš5ś{€b–––¦A)00P˙ď˙ý?őčŃC›7oVjjŞ˘ŁŁÝz@’ŞV­Ęş!<^FF†ěv»é Ŕ-Ęĺr™Î€‡bEröěY 2D3fĚШQŁ4tčPÓIŔUq8úóź˙¬Ő«W«Yłf¦s \p:ťęСŞT©b:ĹČČH-_ľ\{öě1ť”yŮŮŮš;w®>řŕ}˙ý÷ęС>řŕ 8°Ě­A†„„°nŹçrą`:p ě €Ăθ˘ŚŚ uëÖM .ÔâĹ‹@™Ô±cGU©REIII¦S Üp:ťŠ0ťaLTT”¶lŮ‹zŔuHKKÓ!CäďďŻ×_]]»vŐ†  w(k@yÁ0đ@‰a—•śś¬ĐĐPť={VëÖ­SĎž=M'פB… ęر#ĂPJNś8ˇ­[·*<<ÜtŠ1ÝşuSíÚµ•`:(SNś8ˇřřxµmŰVˇˇˇJKKÓ{ď˝§ĚĚLĹĹĹ©]»v¦\AFF†ěv»é Ŕ-Čĺr™Î€‡b—ŻîÝ»«S§NJNNVPPé$ŕş8† ”¬ZµJůůůęŇĄ‹éc|}}u÷Ýw3 Ńo»řůů)&&Fm۶UZZZá.ŐŞU3ť Îť;§C‡±3đ_ţţţĘĘĘ’eY¦SŕŔrrrôôÓOëŮgźŐ+ŻĽ˘/żüR5jÔ0ť\7‡Ăˇ]»v)++Ët x<§Ó©ćÍ›«A¦SŚŠŠŠŇĘ•+uěŘ1Ó)€[:qâ„&Nś¨öíŰ+44T)))zçťwtđŕAM›6M·Ür‹éDW)33Sě üWÆ •““ĂëE( ŕ>>r:ť¦SŔă9ťNEDDÎ0®WŻ^ňňňŇâĹ‹M§neÍš5zę©§äçç§W_}UíÚµÓęŐ«µqăFýáŕÍĘ€2ĚĺrI;˙ĺçç'IÚżżáx"ţĘ…ŇŇŇÔĄK9rDkÖ¬Qßľ}M'ĹŞZµjj۶­’’’L§€GËËËÓşuën:Ÿš5kŞ[·nJHH0ťwüřqĹÇÇ«}űö ÓęŐ«5bÄíŰ·Oź|ň‰ştéb:@1ČČČŻŻŻęׯo:p 6”ôëtĹŤaH’¦Oź.‡Ăˇ­]»V­Zµ2ť”nÝşiĺĘ•¦3ŔŁmذA§Nťb࿢˘˘´xńbť;wÎt `DZZš† "ĹÄĨyóćZşt©~řá :TuëÖ5ť ą\.ůůůÉŰŰŰt ŕęÔ©ŁĘ•+ł3JĂĺ\^^ž† ¦AéĹ_ÔÂ… U«V-ÓY@‰q8Ú˛e‹˛łłM§€Çr:ťŞ[·®Z¶li:Ĺ-ôéÓG'OžÔŠ+L§Ą&;;[ńńńj۶­BCC•śś¬#F(33SsçÎUŹ=L'(!˛Űí¦3·Rż~}v@‰đ1s~ţůg 0@k×®ŐÜąsőŕšNJśĂáeYZµj•îąçIŇÎť;•śś¬ĐĐPµiÓĆp!”}N§S]»v•Íf3ťâuóÍ7+!!A={ö4ť”¨´´4ĹÇÇkúôéňööÖĂ?¬ &(""Ât€Rârą`:p+~~~ě €ÁÎĺÔĆŤŐ±cGíÜąSß}÷(7j×®­FŤiěرzđÁuĂ 7¨E‹űě3}öŮgňőőŐC=¤iÓ¦©}űö¦ó”‘‘!»Ýn:p+~~~Zż~˝é x †Ę‘üü|˝ńĆúÇ?ţˇgžyFăÇŹ—ŻŻŻé, D­ZµJwÜq‡Ľ˝˝•źź_řŽĚ˙;đ†ŕú%''«bĹŠęСé·ŇˇCŮív-X°€a”iĐÔ©SőńÇk÷îÝęСƎ«G}TU«V5ťŔ°śś>|ť€ßag”/Ó(GŹŐ=÷ÜŁ±cÇjĘ”)Š‹‹cĺÂí·ß®ÁËfł\LőęŐUąrĺR,Ďät:ŐˇCUŞTÉtŠ[±ŮlęÝ»·M§W-??_‹-Ňý÷߯ŔŔ@ĹĆĆęŢ{ďŐ–-[”ššŞččhHúuW˲Řř†PR(věءđđpmÝşU+W®ÔO???ýňË/ĘÎÎ6ťĂ0€‡[¸pˇ:wuë*55Uť:u2ť”şš5kęĂ?TAAÁ%/Ăb\żěělýđĂ 7ťâ–şwď®jŐŞ±;ÜZNNŽćÍ›§ž={ŞYłfš}4`Ŕ}űí·jĐ é,Ŕx@}űö•ŻŻďóööV`` *đ,«V­’eY 3ťâ–*V¬¨;3a¸Ą~řAÆ “Ýn×Ă?,Iš3gŽöíŰÇ.ŠĚĺrÉßß˙˛»¶ĺŃoĂű÷ď7\Oăc:ĹďäÉ“züńÇ•¨±cÇę…^0ť¸…Ź>úH-Z´PnnîyÇ}||äççg¨ <‡ÓéT‹-tăŤ7šNq[QQQŠŽŽÖ‰'TŁF Ó9(çŽ?®9sćhÚ´ir:ťjŢĽąţřÇ?jđŕÁĽ‘€k’‘‘!»Ýn:p;uëÖUĹŠŮĹŽQlłk×.………)99YK—.eř 4Đ»ďľ+›ÍvŢń‚‚† 8ťN…‡‡›Îpk˝{÷V~~ľľúę+Ó)(ÇŇŇŇ4dČůűű+&&F~~~Zşt©věء‘#G2üť—5 IDATŕšą\.ÎÜŽÍfS ŘĹŽa˛dÉuęÔI*TPjjŞn»í6ÓI€Űyúé§Ő­[7ůúúËÍÍe®Snn®Ö­[Ç0ŔÔ©SG]»vUbb˘é”3ű÷ďWll¬š7o®ĐĐPĄĄĄiÔ¨QĘĚĚÔÜąsŐŁGŹ ŢP ®ĂŔĄ5lŘa;†<€eYŠŤŤUď޽իW/9ťNŢ­¸›Í¦Ź?ţř‚ĹlCEPö>|Xç[ż~˝Îś9ŁCUeGTT”-Z¤ĽĽĽÂcůůůJJJŇÉ“' –ŔÓäççkٲeęßżż5j¤1cƨ{÷îZż~˝RSSŁ:uęÎŕA222d·ŰMgnÉĎĎŹa;Ó¸>gĎžUtt´fÎś©QŁFičС¦“·×ĽysýőŻŐoĽˇ‚‚Ibg¸ Ű·oW·nÝTż~}uďŢ]]»vŐž={tĂ 7¨yóć¦óÜ^ßľ}őÚkŻiٲe:sćŚ,X  čřńăĘĘĘRőęŐM' ŚűńÇ5sćLM™2E™™™ Óřńă5pŕ@U©RĹtqűí·ëäÉ“jҤ‰ěv»üýýuäČť9sF™™™jĐ Ľ˝˝MgnŁaÆڼyłé x›eY–é\\NNŽNť:Ąşuë^ôă.—K÷Ýwźöîݫٳg«GŹĄ\”]yyyjßľ˝¶nÝ*I:wîś||Ź€˘ŘłgŹ‚‚‚$IŢŢ޲ŮlĘËËSĹŠŐµkWÝzë­ŠPçÎťU­Z5õî%++K‰‰‰1b„Ž;¦üü|ůúúęÜąs’¤ăÇŹ«FŤ†+Pť={V‰‰‰ŠŹŹ×7ß|#??? 8PŃŃŃ…ëyPś¬O?ýT’äëë+IĘÍÍŐo†âíí­şuëꦛnҢE‹TµjUS©€[5j”>ýôSíÜąÓt <—é\Zll¬úőë§ĽĽĽ >¶rĺJ…††*77WëÖ­c¸J>>>š>}şĽĽĽT§Nŕ*řůůÉfłIúu›ńß~WÉÉÉŃwß}§ŃŁG«GŹŞU«–ľüňK“©n!==]ożý¶nąĺŮív=˙üó:věňňňdYVá €$Ţ• W---M111ň÷÷×ŔU©R%Í™3G{÷îŐ1cPbÂĂĂĺĺĺU¸Ö}îÜ9ýďűQćççëđáĂjܸ1€~}Ť-++Ët< ;¸©ť;wŞuëÖĘÍÍULLŚĆŽ[ř±řřx=˙üóşďľű4eĘ·~ŇüŰ Ě›3gŽú÷ďo:˘Nť::věŘ%?îăăŁvíÚ)%%E^^ĺ{ţtűöíşĺ–[ôË/ż\ör>>>ĘÍÍ-Ą*¸‹Ă‡kďŢ˝ęرc‘˙Mvv¶ćÎť«Ź>úH7nTpp°žxâ =ů䓪WŻ^ ÖŔ˙ٶm›ZµjuŮËxyyé‡~PË–-K© p_K–,QŻ^˝”ťť­š5kšÎ€‡ŕ­°ÝTtttá˙?nÜ8µiÓFÔsĎ=§O>ůDoľů¦Ţzë­2ńÇö/˝ô’ÂÂÂLguîÜ9Í™3GŹ=öé D 0Ŕt<Śżż˙e‡,ËҤI“Ęý €$k„ }ú0üWÆ %Iű÷ďgņa74cĆ ­X±âĽíóţđ‡?hţüůZłfŤ-Z¤^˝z,Ľ:aaaĽ5ÜÚ< Ę•+›ÎJĂ(nMš4ŃÖ­[u±M¦|||4lŘ0µmŰÖ@™{zňÉ'µlŮ2Í›7ď’ďţĎą |Y˛d‰|đAýňË/Úżż8  \pąĚĚLMź>]ńńńJOOW‡4věX=ňČ#ŞV­šrř•ÍfS×®]őŐW_©  ŕ‚ŹçĺĺéĎţł2Ŕ=ý6 pŕŔ®€§ŕ­ZÝĚńăÇőŇK/]pĽ  @k×®Ubbb™Ęţř®^`` ||.ś+őöö–Ýn×oĽa Ę˝ĹĹĹ) ŕ˘_7‰ó@yŻ{ď˝Wżüň‹ dłŮôŮgź~üÜąsJLLT˙ţýŐ¸qcĹĆĆŞGŹÚ´i“RSSÍ ·ŕp8äíí}ÁqÝ}÷Ýşĺ–[ Tî©nÝşňööÖˇC‡L§Ŕ0 ŕf^ýu?~ü‚wŮÍĎĎ×É“'őĘ+Ż(''ÇPŔŻüýýełŮ.8^PP Ź?ţX•*U2PĺŢŞU«¦ąsç^ôë&IUŞT)ĺ"”¶üü|ĹÄÄhČ!*(((|'íĽĽţřăJŠŚ¨,///Í[Ë@ĄRÁÉÉ .´`TUĂĉŃłgĎ2¸üZw"""""""˘ę+)) /ľř"öîÝ[âľZqöööčÝ»7.]ş„¨¨(4jÔ¨’Ł$"2^­ZµĐ˘E‹eůůů9s¦…""˛nőë×GJJŠĄĂ """˘j„“¬Ä|€‹/–yĂnqEŚŐ«WIII‘Ę Ź¨„ĆŤ—)[ąr%<<<,MŐ˘R©°~ýzÔ«WŻÄDPN """""""Şž<_|III:ż!ňňň°gĎ<|ř°Ł#"*żW_}ööö[[[Ľđ xőŐW-‘uâd""""Şhś `®]»†™3g˘   Ě2[[[ŘŘŘŔŢŢÁÁÁřöŰo‘śśŚůóçCĄRY ZŇçÁ–’’‚­[·"..®Ręł–vëRŮů¨.222,B•ŔţEOŞĆŤk>‹Ô¨Q;vÄ믿nᨪWWW|ńŚɝ*•Š“Şˇ/ľřAAAČČČĐűR°"Ź=ÂW_}U ‘Uś—_~Y3Ů©  łg϶pDDÖËĂĂwď޵tDDDDTŤp2€;vl‰›*•J3 00k×®EZZ¶mۆ޽{ĂÎÎ΂њϥK— •J•J…ŔŔ@$$$”XçăŹ?FăĆŤáěěŚwŢyąąąŠö/ąąą‹‹ĂË/ż wwwK‡óçĎcöěŮčßż?>˙üsłŐcmíÖE_>Ú¶m‹)S¦TX]˝?K(((ŔüůóŃľ}{«>®Ö˘˛Î7"kTłfMÔ©S`ccO>ůÄÂU=‰‰­­-T*-UÂÂBLź> B~~ľÖ—‚i#"XłfŤ™Ł#"ŞX/żü2D*• Ď=÷zôča鬖‡‡ż€*'XŘ×_Ť„„¨ŐjŘŮŮAĄRˇuëÖX±bRRR°wď^„……ÁÉÉÉҡš]łfÍđĺ—_˘WŻ^{{{ôěŮłÄ:Æ ťťľřâ ,_ľ–µDDDŕÂ… Šoäš“żż?/^löz*«Ý7nÜ(×öúňŃ´iSÔ¬Ył\ű7´żňĆ_Ůlmm1aÂś={VŃŠ*›Ň|VVŢ+ë|#˛V 6Ľ˙ţűđńń±p4UÓ{gW^y………OÄç=""""""ŞĹ_¬ĂţđÇ:lmm1oŢ<Đ|;¤"‚ýű÷[<ţęňóĺ—_šëRLDĹ4hĐŤ7†`ćĚ™P©T–‰ČjŐŻ_꓍B•yĹüˇC‡°dÉKÄňÄÉĎĎÇŹ?ţ¨]»6ž~úi4iŇNNNŘ»w/öîÝköÚµk‡ł×Ł”JĄÂ矎_|{öěÁ—_~‰ţýűk–ÇĹĹaÄe& XZÍš5Qż~}¤ĄĄY:¨´Ićn÷•+W†ýű÷—k?şň±eË–rí×Đţ**ţĘfmýąŇ|VvŢ­aRU?UĺóŘýű÷Q§N>|ˇˇˇ•^˙Ö­[ͲßĘÎżłł3ěíí±k×.‹äŃTćĘ?)Ó¶m[Lš4ÉŇa‘ׯ_Ç˝{÷4/+úFHMYŃ‹!ěííQŁF K†[m 0ŔŇ!lÉ’%8tčĄĂ 2ŠŤŤ j×®ŤřřxNÄ!«víÚY4†úőëăţýűP«Őü @DDDD˘Ěd€ëׯc۶m ±D ťťmT\?ýôşwď{{{|÷Ýwřűß˙ŽqăĆaăĆŤ ÂĘ•+ńÜsĎářńăčŮł'fÍš…§žz řî»ďpęÔ)Lś8;vě@ °nÝ:´jŐJgN Ĺ—.]BTT|}}qëÖ-\ąr«V­B‹-·»¸ÂÂB˝ą˝{÷®ÎúÖ­[‡3gÎŔĹĹáááX˝z5ŕŃŁGXľ|9.^Ľ¨9>|đ”u®˙?®˙üç?HHHŔźţ‰źţ999řú믑€«WŻbńâĹ;v,ŇŇҰiÓ&xxx`ęÔ©řő×_QŻ^=lܸQ“oműÓiĺ©×Đ1Ű´iFŤ…śśĚ›7“'O†­­-6oŢŚ7ß|ź|ň Ţxă ˝ąúăŹ?_ýĎ<ó ,X€   “ŹĹ±cÇ0nÜ8Ľřâ‹pssĂÜąs‘žž'''˝űSšO]ë)é˙JÖQĘç˙¨QŁŚŠŞk˙>ޤmŐjµĽřâ‹@BCCĺ˝÷Ţ+łNff¦řůůɬYł4e)))âçç'>>>’žž.""ľľľeúuQYnn®`ŔIIIQ´?%ůÔ¶ž’ţŻôQR§ąÎ˙ň*Ďő*?Źégîü0˙ú1?DDDDDD–W÷÷‰ŞşŞ|_ś×y"˘Šg-RSS€ěÚµËҡQ5acŢ©D¦łłłĂ† żüň fĚQfťyóćáâĹ‹=z´¦ĚĂĂŃŃŃHJJB\\h}ű@Q™˝˝=Úµk///ŔčŃŁŃ©S'¬YłNNNe¶ű裏přđaDDDhĘ|||ŕăăct\ăĆŤĂŁGʰiÓ&@Íš5ѦM|óÍ7ČĘĘ|÷Ýwxýő׍5BŁFŤQQQhҤ ^ýuxzzâĉ:s©4žđđpĚ™3`kk www\¸pAq»K3”[}őisäȬYłţţţP©TP©TŘąs'îÜąýű÷ëÜN›˘Š¨T*řúú4h€=z yóćhÔ¨®\ą‚É“'ŁNť:xţůçQż~ý2ů.˝?ĄĘ[ݎNš4 ÎÎÎXşt©¦lÓ¦M>|¸˘řfĎžŤÎť;cÔ¨Q‹‹CAA/^lň±¸˙>ŇŇҰ|ůrbbbPłfÍ =¶Ą)é˙JĎ‘ŠŞ0ţü'"""""""""""""˘ŞÍŐŐ5jÔ@JJŠĄC!"""˘j‚“ČŞ5oŢŕââ{{ű2Ë8pvv.Q8xđ âşŠľÎŐŐUďz;wîx{{—(·±ůëtRWóćÍѱcG|üńÇ€«WŻ˘  yyyزe `Æ 2dH™8‹suuEnn®Î•Ć3iŇ$ôîÝ~ř!bcc‘›› µZ­¸ÝşčĘ­ľú´9zô( "e~zöěi0S”θąą!==Ý,ő[ݎşąąaüřńXż~=nݺسgşwďnt˙ú׿gĎž5ůX¬^˝ÎÎÎ0aZ·nŤ¬¬,8;;›őŘ*é˙y-1çůODDDDDDDDDDDDDDU—JĄB˝zőp÷î]K‡BDDDDŐ'P•Vô ú•+WJ”{zzęÖ­[áuŢĽyššZ!qŤ7ż˙ţ;Ž=ŠůóçcÁ‚čŰ·/Ö¬YłgĎÂŰŰŽŽŽĺŠYiýúőÉ'Đ­[7;v íŰ·ÇúőëÍzl•ô˙ŠĽ–XŰůODDDDDDDDDDDDDDÖŁ~ýúś @DDDD†“¨J+zÓvBBB‰ňëׯ:wî ŕŻ7ÓçĺĺD<0©N­ušĂËË łfÍBvv6ž}öYŚ3GŹĹرcnRś¦ÄµZ­yk|ń‡°•´ŰXúę·üüü1äää`ţüů%Ö;wîV®\YaqU”Ňń›ˇ€»»;ÂĂĂńŃGaůňĺxë­·LŞ«¨żôęŐËäc1sćLřřřŕ‡~Ŕ–-[ V«­hJóYz=%ýßsÖsź˙Šc!""""""""˘'OFF†ĄC +dę¸U<¤¤¤X: """"Ş&8€¬Zvv6h}[8L™2X±bnßľ­)_µj^yĺŚ7Ŕ_˛Ď™3üń–-[†ÜÜ\ŔŹ?ţÂÂBŔkŻ˝†aÆ™|,-Z¤‰1$$uëÖEŁFŤíOi>Kݧ¤˙sΖfęůţÇĹĹÁĂĂWŻ^Ő›"""""""""s‹ŽŽF­Zµ R©ĐµkWüú미yó&Ţ~űm¨T*¨T*ôë×űöíÓlłoß>´m۶¶¶xçťwĚţ2•ŞlĹŠ6m:věŔŔ@\ĽxŃŕ6‹-B‡ŕîî^ RU››‹¸¸8ĽüňËfëůůůX˛d "##1xđ`b۶mf©‹¨ş¨_ż>'Q…ád˛Z_}őŢ~űmŔµk×0eĘüřăŹ%Ö©U«:„ÁăŤ7ŢŔäÉ“1uęT¸»»cďŢ˝°łłĚź?mÚ´Á’%KđöŰoŁgĎžxîąç0tčPÜşu 3gÎÔ<Ě'N茫eË–Ř»w/üýýŠ€€9rĎ?˙<ĆŚ¤¤$888(Š«Č1c0iŇ$<÷Üsš˛©S§bĆŚ%ÖűđĂ5qĆĆĆ"##Ë–-ĂÍ›71118wî¦M›ŕńúE”ć:uę ::ľľľ1c\]]‡víÚlwé·Ňçääŕý÷ß×™[}ő9::"44uęÔÁŃŁGŘ»w/‚ńÍ7ß 22)))Ř´iśťťµŻ?˙üłL>nÝş…¸¸8Ŕ­[·đÁŕňĺËš|_ąr{öěÁÎť;5]Ď1iiiXąrĄ¦lńâĹHMMENNN™ýeff–‰_›””“ëU’Ă"žžžčŇĄ †®3–â–/_ŽŢ˝{Łoßľ=z4&L€çž{_}ő•ÉÇxÜ':uę„ůóçcذahßľ=ľřâ EűS’Omë)ąN(˝–”¦­)=ߊ(=˙ŔŃŃuęÔŃQe™3g"""<ľoţĎţŤ5ÂŞU«Đ§O@ź>}¤Ů&((ÇGXX–/_Îű\:,_ľQQQŤŤĹ×_ ťou/ţRńăÇăĚ™3śdQÍ?ĆĆrpp@DD.\¸ č[gM©köěŮčŇĄ /^ŚÍ›7Ł˙ţ ĹâĹ‹M ™č‰Pż~}Ü˝{×ŇaQ5ˇ’⯢đĺ—_bŔ€(ULŐPhh(`ëÖ­f«CĄR!>>ýű÷7[DdXNNZ¶l‰“'O˘V­Z–‡,€×㪅źÇô3w~ý""""""Ë«ŚűűJÜżŤ7†››®^˝ ›Çď :~ü8^xátîÜ»ví*±Í›oľ‰1cĆ M›6–ąJhŢĽ9Dçϟ׻ޕ+W†ýű÷—Řöüůóüw{5ˇí›BIż0µ.///¬[·ť:uđř”ëÖ­‹6mÚ 11±\q—WUľ/n-×y"˘ęÄšţ.ÄĹĹaíÚµ¸té’ĄC!"""˘j€ß @DôXµjĆŹωDDDDDDDDDTa\]]ѧOܸqŁÄ7ű>˙üópssĂž={””¤)ĎÉÉÁÉ“'9Ŕ€ëׯCĄRé]çćÍ›čŐ«ß([ŤUć1.O]………řúëŻ5żß»wŔăIDDD¤ť‡‡?ÇQ…ád"˘jęđáĂhٲ%š5k†Ő«WcĚ1–‰Ş™7Ţxđé§źjĘ~úé'899ADJ”o۶ ÁÁÁeöńčŃ#,X°#FŚŔK/˝„.]şŕôéÓ(,,ÄĎ?˙ŚI“&ˇiÓ¦¸uë‚‚‚đôÓO#==]çvú\şt ˇˇˇ6mÂÂÂS§Nľýö[Ś=^^^HOOǰaĂPŻ^=´hŃżýö`Ó¦Mprr‚JĄÂüůóQPPŘĽy3°~ýzťugdd`ęÔ©>}:"##Ń­[7DFF"==€đđpdggăöíŰ×ü^ÚşuëpćĚÍzĄÝ˝{!!!pwwG@@Ž;f0ßş$&&bňäÉhÚ´)îÜąŁŮo‹-đŐW_)ŢŻ©Çůرch۶-ĆŤ‡÷Ţ{5jÔĐš“"ŮŮŮ3g†Š & ((Ë–-S|”ô%ë(ɉľXucCű|řđ!"##1zôhÄÄÄ **JoľôŐe(WđăŹ?búôéšßżýö[ŘŮŮ!&&FoťDDDO˛úőëăÁxô葥C!"""˘ę@J‰ŹŹ-ĹT …„„Hĺ ÓM IDATHHYë ńńńf­´;uę”x{{‹źźź:tČŇá…ńz\µđó~ćÎóŻóCDDDDDdy•q_©‚‚iܸ±Ô¨QCnßľ-""–}űöIíڵ婧žµZ-"":tK—.•ŮÇČ‘#ĺüůóšß»ví*žžžrďŢ=9xđ 8:: ™;w®ěŢ˝[FŚ!YYY:·ËČČĐołfÍÄ××WDDÔjµ¸¸¸H@@€Ü¸qCj×®-$66V®^˝*7nҦMÍ>˘ŁŁ€ś9sFSvíÚ5éÓ§ŹÎz333ĹĎĎOfÍšĄ)KII???ńńń‘ôôtM9ń÷÷׹/}ëůűű ™9s¦\ąrE€´k×NłŽ1y+((;vH­Zµ€Ś?^öďß/›7oggg P´_SŹłźźź¸ąąi¶0`€¤¤¤h͉Z­–   :t¨ŠČÚµk€lßľ]ŃqPŇ”ö}91«®c¬oźůůůҦM9r¤fůĺË—ĹÎÎÎŕýśŇuÓg‹äĺĺÉ3Ď<#7nÔ[We©Ę÷Ĺ­é:ODT]XÓß… ą~ýşĄC!"""˘j€ß @DTMŕĎ?˙Ä… жm[K‡CDDDDDDDDDŐŤŤ † µZŤőë×ăţýű¸pá:tč€ŕöíŰŘľ};._ľ µZŤgžy¦ÄöGŽÁš5kŕďď•J•J…ť;wâÎť;HLLD»víŕĺĺ=z4:uę„5kÖŕĚ™3:·ŰżżÎxĂĂĂ1g΀­­-ÜÝÝqáÂ@ŁFŤĐ¨Q#@TTš4i‚×_žžž8qâ„f“&M‚łł3–.]Ş)Ű´i†®łŢyóćáâĹ‹=z´¦ĚĂĂŃŃŃHJJB\\śŇ”+2sćL<ýôÓčŃŁÜÝÝńűďżĐźomył±±AĎž=5Ç`ŢĽyhßľ= „÷ß°bĹ ű5ő8;99áţýűHKKĂňĺË!"‰‰AÍš5µ¶{ĹŠŘ·o˘ŁŁˇR©C‡ĹÚµkńĎţSŃqPŇ”¬c('†bŐĆĐ>?úč#>|šm|||ŕăăc°Ď”fJźýěłĎđöŰoăő×_7ş>""˘'I˝ző÷îÝłp$DDDDTp2™ěŤ7Ţ|úé§Ř¸q#1bŕ“O>Áşuë´> |ôčQ@DĘüôěŮ4J»şşµť6“&MBďŢ˝ńá‡"66ąąąP«ŐšĺEuçęęŠÜÜ\Íďnnn?~<ÖŻ_Ź[·nöěŮîÝ»ë¬÷Ŕggçĺ€ęÜÖĹŰáááśś¦çÍĆćń˘ŁŁŁ¦,88péŇ%ű5ő8ŔęŐ«áěěŚ & uëÖČĘĘ*“Ç"űöí4nÜXSfkk‹aÆÁĹĹEńqPŇ ­c¨Í†bŐĆĐ>wîÜ đöö.±]Ńń3†)}öňĺË8q˘Ńu=iÜÜÜiiiŽ„ŞN """"""""""""“ůűűŁuëÖ¸téŢ˙}ÍC˙m۶ĹsĎ=‡ť;wâłĎ>C˙ţýËl›ššŠ¤¤$ÍĂęĹę¬ÓÔíŽ=Š-ZŔÇÇŃŃѨ]»¶’&–{{{,]şżýöZ·n [[[ťë=Ś}ĺʕ垞ž€şu뚇±LÍ›6 6xyyÜoyęíׯNś8nÝşáرchßľ=ÖŻ_ŻuÝ;wîx|üăĺ®—čIŕââN """˘ ńÄMxđŕĄCĐĘZă"*SűuFFFGBÖ¤˛ŻwěODDDDDDDDDćWôí/˝ô4h )>|8 ńâ‹/˘^˝ze¶ó÷÷GNNćĎź_˘üÜąsXąrĄÎúLÝ.,, jµZóc€/âîîŽđđp|ôŃGXľ|9Ţzë-˝ë˝M=!!ˇDůőëם;w6:•J…üü|ٶ15oÚ=pŢąsgű-O˝3gÎ„ŹŹ~řálٲjµŃŃŃZ×mٲ% 66"˘)żző*ľ˙ţ{ł] µŮP¬@Ůc¬$Ď@Ůö)Qş.csU«V- 4Ččz‰LˇV«5ß^AUÇî”c®Ş/[[[Ô­[·B&đŮY:ŕń›a±`Áłě?77‹/ĆŽ;päČŁoŚš‹©qĺççcůňĺ¸yó&’““qăĆ ĽóÎ; 1sÄUÇ˙ţ÷?|öŮgXµj`Đ A2dzôčŁGŹbńâĹŹŹÇß˙ţw 8#FŚ€‡‡‡Ec6÷yPYq”ç|[´h¶oߎ–řZfs*O{­ĺ•‡®6¬X±7oŢÔĂO?ýaaa&·×Ř~QŢÜ`ѢEرc+­?UgÚ® ~~~–‹¬ÄŔ1iŇ$ͤ€"C‡Ĺ´iÓ0dČ­Ű˝öÚkđńńÁěŮłqăĆ tęÔ çΝÑ#G°mŰ6ŔŁGŹŮŮŮprrRĽť6ÉÉÉČČČŔ®]»p÷î]¤§§Ž9‚† ję*.33Ŕăń;»ż†Ö"##±|ůr\»v ľľľzó3eĘl۶ +V¬@XXžzę)ŔŞU«đĘ+Ż`ܸq€ű÷ďňňňôî|}}‘śśŚëׯĂËË Ŕ_ fffÂŮŮŔ_/hÉÎÎ69oE 4߀°{÷n´jŐ ŁGŹFAAŢýÚŰŰ›tśÇc“&M‚‹‹ BBB0fĚ4jÔHk|Ó¦MæM›°uëV¤¦¦˘_ż~¸}ű6RRR°zőj):Júˇu ĺúŢ˝{zcŐvŚ íłC‡ŹŹGTTž~úi"11·nÝđř-˙ŢŢŢZsWş.Ą}¶Č;ďĽË—/›4Ęo÷îÝX˛d‰f"É«Żľ ŕqźlذ!‚1tčPŘŰŰ[2Ěr»˙>.\ĺË—#;;»ÄDšŞâIľ×n‰±ŕŞŠą*©ş>#ăććĆo """˘ aß Đ´iSÔ¬YÓlűwpp@DD.\¸€‚‚łŐc,Săš={6şté‚Ĺ‹cóćÍčßż?BCC±xńb3F[µĽđ Xąr%ž~úiŔÚµkŃŁGŹßJ”——‡ţýű#11Ó§O·řDŔüçAeĹalżľqă†ć˙ÇŹŹ3gÎTČ„ťâűŐG[{ËłmUŁ­ Ë—/GTTbccńő×_ĂĂĂ<(W{ŤíĺÍ­­­-&L€łgĎVŘ0Ąý‚¨:Ňu] """""""*âć憰°0—(ŻWŻÂÂÂĐ»wo­Ű988`ďŢ˝Ć7ß|ČČH¤¤¤`Ó¦M°µµĹűᅬ+W®"""pâÄ Ű=ŻM\\ęÔ©ččhřúúbĆŚpuuE\\Ö­[§©+66X¶lnŢĽ ‰‰)ń¸§§'şté‚áÇĚO­ZµpčĐ! <oĽń&OžŚ©S§ÂÝÝ{÷î…ťťNź>Ť¨¨(ŹÚž={6Nž<©sꎎˇ¨S§Ž= Á˘E‹4}ż÷Ţ{ČÎÎĆÂ… ‘śś ŕńöU*•Iy+˛téR¤¦¦âîÝ»HNNĆĎ?˙ ;;;ÇĂÔă 999čÔ©ćĎźŹaÆˇ}űöřâ‹/´Ć×´iS$&&˘[·n8~ü8âââ™™‰ @ĄR):~řˇÁ~°dÉëŢśеô1 ÷ű–-[bďŢ˝đ÷÷Ghh(päČ<˙üó3f ’’’t~F麔䪸GŹiť A•ŁsçÎřôÓO<>öîÝ‹˝{÷âČ‘#9r$ćÎť‹€€ś={Ö‘–OŃőşř„ˇŞäIĽ×n®±`CuY;C±š;WUMu}FĆÍÍM3”¨˙üsͶŹ=‚ 0bÄĽôŇKčŇĄ Nź>­YţđáCDFFbôčщ‰ATT˛łłÍÖŕńěă©S§búô錌D·n݉ôôtŁâ2Ô¶üÓ§O×üţí·ßÂÎÎ111JŮéŃŁG ­­->ůäť7Y ĺ^ßňÄÄDLž<M›6Ĺť;wwww´hŃ_}ő•ÎŘ´ťß~ű-FŹ ///¤§§cذa¨WŻZ´hQ¦ßWŃçŁŇ8L9ßÖ­[‡3gÎŕöíŰ/łüîÝ»šŕرcše—.]Bhh(¦M›†°°0âÔ©SŠö«Ż˝ĺŮŽ;†¶mŰbܸqxď˝÷PŁF ˝yеľŇľd¨żfggcÎś9:t(&L€   ,[¶LgŽěělMÂĂĂ‘™™©µ˝ć¸WäůPÜüŕŕ`¸ąąˇuëÖŘ·o`Ó¦Mprr‚JĄÂüůóQPPŘĽy3°~ýzťýÂPűŤíDÖH×u}ąrşÎQĺ;|ř0Z¶l‰fÍšaőęŐ3fŚĄCŞE÷x_¨ę*+Ýąs§¦LÉřviJĆV”ě{Íš5°±±ŃŚŰfffbÉ’%%Ę”ZĽx1jÖ¬‰É“'ăŔŠÇ– ĹřÓO?ÁÁÁÎÎÎřĺ—_đŕÁ :*• Żľú*Îś98~ü86lO>ůDk|úîµë‹ˇ°°?˙ü3&Mš„¦M›âÖ­[ ÂÓO?­óéź2”oCu{żş¦/ÖęúŚ 'Q…‘RâăăEK±N………ňÇ©[·®$$$ČŮłg€x{{ËÂ… ĺÁrüřq AAA%¶żvíšMYvv¶<űěłŇ´iSÉÍÍ•ŕŕ`ąxńb‰íFŽ)çĎź×ü޵kWńôô”ŚŚ ÉĎĎ—6mÚČČ‘#5Ë/_ľ,vvvzŰVž¶dffŠźźźĚš5KS–’’"~~~âăă#éééŠăŇ×¶Ňňňňä™gž‘Ť7ęl—.!!!bôvĆ ńńńf­C :tňÍ7ßč]ßPîu-OOO—;vH­Zµ€Ś?^öďß/›7oggg ĐYoéóŕĆŤR»vm ±±±rőęUٸqŁ6mÚčÜOEźŹJâ0ő|‘2çľČ_ÇlćĚ™rĺĘIHHŇ®];Í:Íš5___Q«Őâââ"z÷«Ť¶ëOy¶őóó777Íď ””ťűжţíŰ·÷%}ýU­VKPP :T EDdíÚµ@¶oß®ł şr m]s\‡µŐeęů ňWš8q˘ěÚµK>ţřcqrr[[[9yň¤DGG 9sćL‰úôéŁ7'†®ĆösłôőŚcěç1sSzm¬,ćÎŹµäßĎ„•ÉZňCDDDDDô$«Śűű¤Ý©S§ÄŰŰ[üüüäСC–Ç첲˛$**Jyë­·žvSőP•ď‹›zť7t/·Aâîî."ĘĆ·u14¶˘tßľľľeî5j++­hüGD$--M†Ş÷Q6¶¤4ƱcÇJÍš5ĺÁ""ňđáCńôô”!C†h¶ËĎĎ—ŔŔ@˝1‹”=>†bHII‘ŠŁŁŁąsçĘîÝ»eÄ’••Ąµ}ăSúňť››«·.SîW›:\u™šŰ°°0ٵk—IyP:¦©tĚĄ"ĆÍ•ŽWTȵľĺ÷îÝÓŰÇ”ĆZžgd¬íďÂ{ď˝'-Z´°tDDDDT ”{2€fGĄţÁҨQŁ2ű©_żľ¸¸¸ÜVDäرcbgg'íÚµ“µk×–XvřđaÍMÇŇ?;vě•+W 9wî\‰íüüüµÍ”¶Ě1CHrrr‰ő6lŘ dĘ”)Šâ2Ô¶Ň>úč#ůŕ ¶I›'i2Ŕś9s¤FŤâŕŕ {öěŃş®ˇÜ+96EÇ2;;[łßĄK— 8p ŢXK÷»żýíoeúť§§§888lwEžŹ†â(Ďů¦ďPŃě""îîîâčč¨ů}É’%˛eËy<Â××WjÔ¨ˇwżJc(϶@–-[&………rúôi˝7Ąô­o¨/ęŹK–,ráÂÍöůůů˛víZą˙ľŢöęĘAńňĘľ›z>ő§âÇaٲe@Ţxă IMMggçćÎť[âš[:%×cűąYúzLƱ¶‡­9 ňű™°2YC~žtś @DdXUľ/n®É^^^ҰaCQ6ľ­‹ˇ±Ąű.ţPmeĄ­“””$Ç—»wď–YÇĐŘ’Ň‹^Ľöá‡jÖ –ÚµkKff¦|űí·ňńÇëŤY¤ěńQCQ[ŇŇŇ Öˇo|JIľµŐeęýjSĆ‚+Ş®ňćÖÔ<(ÓT:ćRăćĆŽW–wŚÚ•ÎąŇXË󌌵ý]řŕ4×d""""˘ň°™8;;—)sssÓű•‚ŵjŐ S§NĹáÇńŹüŁÄ˛ŁGŹ" ňx2C‰źž={jľÚĐŰŰ»Äv66¦5WI[8 uÝŔŔ@ŔÁĹe¨mĄ]ľ|'N4©]O’3f`۶m(,,ÄkŻ˝†#GŽ”YÇPî•›˘céčč¨Ůopp0€Ç_Ńg m_éęęŠÜÜ\Łö”ď|4GEźoÚęőđđ@NNŽć÷I“&ˇwďŢřđĂ‹ÜÜ\¨ŐęrŐWVŻ^ gggL0­[·FVV–ÖÜ+YßP_2Ô÷íŰhܸ±f{[[[ 6 ...ĺnke_‡Ë{>?˙ú׿gĎžđř\?~<ÖŻ_Ź[·nöěŮîÝ»ëÜź’ë±ý¨8c?éŁV«qçÎ<˙üó”ŤoëbhlĄ<ű6FĎž=‘ťťŤzőę•YfhlIiŚÍ›7GÇŽńńÇ®^˝Š‚‚äĺĺaË–-€ 6`Č!FÇŻ4†˘¶¸şşÜgyǧ´ŐeŽűպƂ+Ş®ňćÖÔ,X€ľ}űbÍš58{ö,Ľ˝˝K<úË—/Ç[o˝Ub›ŇýBÉőŔŘăKDTś©ź;ČňRRR°uëVÄĹĹY:""@^^˘˘˘đŹüďĽóeăŰúčşď˘7„˝PDđŕÁĹíęŃŁ˘˘˘eôř—1톗—fÍš…ěěl<űěł3f Ž=бcÇ"<<ܨşM‰A)}ăS¦ćŰÔűŐ¦Ś»VT]ćČmEÝ·/Ďxti†ĆÍ+bĽŇ1ęňäHI¬Őí777@ZZš…#!"""˘*OJ‰ŹŹ-Ĺz=|řPČßţö7M™ŻŻŻĚĚLM™···‚‚MYff¦† jĘeĐ AšßÇŽ+¶¶¶ňóĎ?‹ČŁGŹÄÇÇGČ[o˝%›6m’ččhéÚµ«dddȉ'ÄÎÎNÜÝÝĺ‡~śśŮ»wŻÔ©SGČźţYámÉÉÉ‘€€iܸ±$''kÖ›0a‚ĽňĘ+˘V«Ĺe¨mĹŤ?^zôčađřč"!!!&oŻ‰ŹŹ7kú4jÔHČŁGŹJ”8PH äěŮł"b¸_)96ţţţ@ňóó5u­_ż^Zµj%jµZgśÚ΢>¦­=úöUŃ磡8Ęsľ=óĚ3âää$×®]Ó”5lŘP”čď 4’••%""uëÖ•J%;wî”M›6Iýúő€>|X®_ż®użÚhkoy¶utt”ű÷ď‹Z­–şuëJ›6mtîCßú†ú’ˇţ””$NNN@:vě(«V­’=z´ęlCZZš˝í5çu¸"χćÍ› IKKÓ”Ť;V^{íµ2ëŢľ}[$((¨Ě˛ŇýBÉőŔŘţ`n–ľ“qLů@bbbäŇĄKňÁ›››~řˇÄ8fqM›6RXX(ůůůұcGqqq‘ăÇŹkÖ14¶dlűçĚ™#*•JNź>­)ó÷÷—Ţ˝{Ě“ö{íJc(jKŃX©>úƧ”ä[[]¦ŢŻ6e,¸˘ę*onMÍ’1MĄăŃ1nněxeyǨŤÉQéś+‰µĽĎČXŰß…sçÎ ůý÷ß- Uqĺž pçΉâŕŕ »wď–üQěě쀼óÎ;’šš*+V¬•J%dÁ‚rďŢ=ÉÎΖéÓ§  K–,‘uëÖ‰‡‡‡„‡‡kꊊâââ"k×®‘+W®Hpp°¸ąąÉSO=%ŁFŤ’»wďj¶ŮżżĽňĘ+âěě,>>>2oŢ< ”1cĆČž={´Ţ8(O[D?Ŕ:eĘéÚµ«DFFĘ”)SdöěŮ’››kT\†ÚVdäȑұcGĹÇŞ´ę<ŕ÷ß/Ń·(;wî‘S§NÉŰoż­YVŻ^=‰ŽŽ–‡Ě˝ˇĺE˙8^´h‘Ü»wORRRdŢĽyzoĚh;ćÍ›§ů}Îś9ňŕÁYşt©¦lÚ´iňđáĂ2űŞčóQi¦śo""Ó§O— Čţó),,”… jö=qâDÉĘĘ’ hĘ"##%77WV­Z%uëÖ•Ö­[Kbb˘,[¶L\]]ĺµ×^“ÔÔÔű5&ďĺÚ€Ľđ 2oŢ˙üó2ĺÚú…ˇöŰĚÍÚnn‘~Öň°ő©S§´^,íI bř:c)Ö’"""""˘'YyîďďŰ·O,yyy•ů 8PbbbŚŢnٲeR»vmÉĎĎ—ôôtéŰ·Ż9r¤BcÓVÇ/żübu“úöí+ŽŽŽňčŃ#9sćŚřúúZ:$ł°ćľRť\ż~˝L™©ą7$--Múöí+IIIFo[•ď‹{ť˙ő×_eřđáš±“   éÖ­›Kż~ýdŐŞUZÇ+•Śo˘klEÉľ/^Ľ(mÚ´'''éÚµ«\ĽxQÚ·o/C‡•/ľř˘LiiiňţűďkĆ9ăââäćÍ›˛aĂ uęÔ‘ąsçJ\\ś˘±%cÚďŢ=‰(Q¶víZILL4#}÷ÚőĹťť-łgĎÖÄ=jÔ¨´Ń7>Ą/ßź}ö™DGGë¬Ë”űŐ¦Ž—·®"¦äÖPÎőŶjŐ*EýÎĐxtEŽ›;^YcÔş–Ę­’XËűŚŚµý]HIIňÓO?Y:""""ŞâT""(ćË/żÄ€PŞŞˇĐĐPŔÖ­[ÍV‡JĄB||<ú÷ďo¶:¬IóćÍqţüyž?TněK•/''-[¶ÄÉ“'Q«V-K‡Sáž´ëqUÇĎcú™;?Ěż~Ě‘ĺ™z˙ÜąsčŢ˝;Ž?777s„fUš7oÁůóç+˝•Jś;wÎluŁNť:P©T¸˙>®^˝ŠV­Z!--ÍŇaYŤĘč+ŐĹ•+W†ýű÷WZť'OžÄ믿ŽÄÄD899)Ţ®*߯ŚqÜŠPÝÇVž4Ő}ŚÚÚţ.äççĂŢŢ۶mCßľ}-Ua6–€Č¬Zµ ăÇŹçÍj""""""""Ş–DC† Á›oľůDL€ëׯCĄRUů:*B ‘‘sçÎaÉ’%5j”ĄC˛*Uĺ8ZÚÍ›7Ń«W/Ü˝{·Rëýűß˙___ĽűZ/Ʊ""ÓŮŮ١Nť:ś IDDDDĺĆÉD(;;»Ä‰LĹľT9>Ś–-[˘YłfX˝z5ĆŚcéĚâŰożĹ˙ţ÷?tďŢ]S–É“'ŁiÓ¦¸sçBBBŕîîŽ-Z૯ľŇ¬÷čŃ#,X°#FŚŔK/˝„.]şŕôéÓ—âçźƤI“Đ´iSÜşu AAAxúé§‘žžn0ćÂÂBlÝşÆ C‡4í=z4ĽĽĽžžŽaÆˇ^˝zhѢ~űí7@BBÂĂĂ‘ťťŤŰ·o#<<\ó{FF¦NťŠéÓ§#22ÝşuCddd‰x~úé'xyy•xóyé2}uhséŇ%„††bÚ´i C`` Nť:Ubť•+WbčС;v,jÖ¬ •JĄů€cÇŽˇm۶7nŢ{ď=Ô¨QCń=ä°°0Í]]]‡9sć`Đ A4hfÍšĄw{öÓű đř^˙ś9s0tčPL0AAAX¶l™fąˇş”´EÉ:JŽ‘ľX×­[‡3gÎhrUžÜ1Ôď‹tëÖ kÖ¬ARR’Áţ@ćű˘ę‹cÔ•ĎÍÍ ©©©–Ş:)%>>^´S5"!!!f­€ÄÇÇ›µk••%QQQ@Č[o˝%‡˛tXT±/U®S§N‰···řůůUű>>’žž.""˙ýďĹŃŃQ¶oß®YO[™¶:t•7kÖL|}}EDD­V‹‹‹‹h–ŻX±Blmm%55UDDćÎť+$22RłŽźźź¸ąąi~0`€¤¤¤Jˇ\˝zUúöí+ŽŽŽňÜsĎIAAfŮSO=%111z·g_yĚÔľ˘V«%((H†*………""˛víZ Ű·oWT—’¶(mŻľcd(Vmą*Oî•ôű"ÇŹ×ô ĄŞň}ńĘÇ5Ő“4¶Bô¤xRƨ­ńďB«V­dęÔ©–Ş8Nx‚q2Ń“×㪅źÇôădËb~,Ď”űűŢŢŢâââR¦ÜĎĎOHvv¶¦léŇĄ@(‡Ö<VúgÇŽ—‹üío’––fR{K?|\´żâ<==ĹÁÁAďv3fĚ’śś\b˝ 6™2eЦ,??żLÚĘ”NX˛d‰lٲEDD Ĺ××WjÔ¨ˇY,666’——'""§OźҶm[Í:@–-[&………rúôiÍCöş|öŮgâíí-?üđ,\¸Pȧź~*""ţů§Ľřâ‹ZŰĄ űŠi}eÉ’%@.\¸PbůÚµkĺţýűŠëRŇCë:†bŐ–+]9TŻ’~_äÖ­[@zôčQf™.Uůľ¸5O "ŞŞ¬ńďB—.]däČ‘–Ş8Qµvűöm¸şş–)·±y@ăĆŤK,6l\\\ץ¤-†Ö1t Ĺj %ń*é÷EŠężsçŽQqY3777¤ĄĄY: """"Şâ8€¨šłµµEAA˘u6lđňňBjj*’’’““Sf˝ÂÂBË­IŃĂěW®\)Qîéé ¨[·®Ůę>zô(Z´hDGGŁvíÚ%–Ź7ź~ú)†Žwß}‘‘‘={6fĎž­Y§_ż~8qâşuë†cÇŽˇ}űöXż~˝Î:gÍš…† " P«V-Ě›7ÉÉÉčÚµ+Ú·o˙rµ‹}ۢ‡×/]şdöş 1t ĹZŃ”ôű"Ú&Uu...HOO·tDDDDTĹq2QňŕÁK‡@DDDDDDDDDUP ?h”šš čÜą3üýý‘““ůóç—XçÜąsXąrĄÁĺÖ¤čMëEo!/rýúuŹŰ[DŰÄ Ą“)´ Z­F÷îÝ”}ř˝  §OźFbb".\oľů111%Ţ0?sćLřřřŕ‡~Ŕ–-[ V«­łÎk×®!99Ys<ŕŤ7Ţ@çÎťqđŕAdff–X˙ţýűF·‹}Ĺp_iٲ% 66"˘)żző*ľ˙ţ{Łę*/CÇŔP¬Ŕă‡ňóóó+$%ýľHQ˙|ę©§*¤n"""kP§N>@DDDDĺfgéHżÜÜ\,^Ľ;věŔ‘#G*ě&;=9:tč€Ď>ű YYYeŢJ<~(·čÜÝ»wŁU«V=z4 ŕăăŮłgăĆŤčÔ©Îť;‡#GŽ`۶m°··×»=zČÎΆ“““Qqgee2224eEű+®čÁöüü|ŘŮŮiÎËËÓ¬3eĘl۶ +V¬@XXćˇâU«Vá•W^Á¸qă<~({ŕŔŘşu«ćá}meÚꀇ–‰399صkîŢ˝«™qäČ4lŘk×®ĹöíŰѢE $%%ˇNť:¨WŻ|||4ÇeѢE4i\\\‚1cĆ QŁF:sďľűĆż˙ýo8::bÆ @ZZ¦NťŠźţ={öÄőë×áçç‡7ß|SĎŃxŚ}Ÿľ2mÚ4lÚ´ [·nEjj*úőë‡Ű·o#%%«WŻFPP˘ş”´ĹĐ:Ż˝öšŢcpďŢ=˝±€ŻŻ/’““qýúuxyy•+÷qqqű}‘{÷îţůĎ–Ů/QUU·n]N """˘ră7Y9DDDŕÂ… ĺzóŃ˙ÇŢť‡Çxď˙M!„ Šk[Ab)*”Rë)µ *ö-–¨j8QµäŇM¨ÚkŐŇj-ˇĘˇŽ[ű’X‚ÖÖZS’őţýŃoó«cŹ$w&y>®k®sÜ3sßĎąM“Ě{>Čązôč!Ă0´gĎžG^?mÚ4ÝĽySׯ_×Ő«Wµ}űvŮŮŮÉÁÁAaaajÝşµÖ¬Y#___]»vMK—.•““ÓŻ·µµŐ§ź~Ş .H’>úč#9rä™›ăââ4qâDIŇ•+W4uęTĄî/ @wîÜŃôéÓuůňeIŇرcuŕŔŤ=Z’táÂM0AÇŽSž}útEGGkâĉʟ?żĆŚŁňĺËë“O>QÁ‚5qâD9::ŞNť:Љ‰Qßľ}őŹüCuëÖŐ믿®âĹ‹kőęŐ©çŁqăĆ RŻ^˝Tż~}-[¶ě±çoÁ‚ęÔ©“<¨5jČËËK+VÔÔ©SőóĎ?ë>Pdd¤&L B… =Ó €Äsĺyź+eË–Uxx¸š7o®Ă‡kâĉş{÷®&Mš$‹ĹňLÇš5kÖSË_|ńÔۆńÄżŁ§µJ’§§§ňçĎŻýű÷żĐąż˙ţ3=ď˙˛k×.ŮÚÚŞS§NĎüĽ «+P ŔĂt@ZXŚżŻń(iĹŠęÜąłţg3˛!OOOIŇĘ•+3ě‹EË—/ç…9 T¬XQ§Nťâë3Ň„ŻÇÖ…źÇž,ŁĎç˙É8?`ľ´ľľß˛eK˝ţúëš:uję6^wĚ.\¨7nhäČ‘’¤””]ąrE[·nŐ#ôűďż›\Čséďyž÷­[·V±bĹ4oŢĽgŢż5ż.žżÇ€ś&+~_řúëŻ5`Ŕ€Ô•Ą€´`e€`áÂ…Ú°aC–xcąĹbyę%**ĘěĚL¤>}ú¨oßľ©ŰlllT˛dIŐ«WO%J”0±Î|}zęőwîÜ‘źźź>ţřcůúúŞyóćňőőMmY»v­Ľ˝˝ĺââ˘ččhőęŐK/˝ô’ÜÝÝuđŕÁÔýÜ»wOľľľňööÖرc5zôčÔFH‹—_~Y«V­ŇđáĂ'I©Ż;fö돆a<őRˇB…Lm2ËĎ?˙,Iš3gŽnŢĽ™şýСC5j”ľůćłŇŔsééYź÷WŻ^U@@€6oŢ,'''SZČ( ôçű €´˛3;@Ε’’˘›7ojÖ¬Yşwďž4lŘ0učĐAŢŢŢęСvíÚĄşuëę>ŻŻoę úÍ›7W“&MtüřqĺĘ•KóćÍS\\ś–,Y˘±cÇjٲe˛··äq˝ĽĽtăĆ …‡‡K’Nź>­¸¸8ĺÍ›÷±Ç9sćŚňäÉŁ÷Ţ{O...Z˛d‰,‹-Z¤Ţ˝{«|ůňjذˇjŐŞĄ®]»jüřń’¤ëׯ«^˝zZłfŤ:¤5jČËËK111š5k–&L ¦M›Ş[·n2dÂĂĂ•śś¬wŢyGUŞTI]îöÜąs𠤕ŤŮ`cóç—˘ż$©uëÖ’¤3gÎh˙ţýrss{äŇż-[¶”¤Ô7ç,Xđ©Ç›={¶śśś4lŘ0˝ů曊‰‰‘““ÓSŹłmŰ6IRÉ’%S÷ekk«^˝zÉŮŮY»ví’¤‡>ťćí·ß–$íŢ˝űÖż+X° âăă%I›6m’$•)Sć‘ç X7†xg)€,é•W^‘$ą¸¸čćÍ›:wîśâââş]JJĘsď»C‡:räš7o®¨~ýúZĽxńSŹóűďżKús@áQţzłţ… Ř^´hQI˙˙ňOsůňeIŇÍ›7źéöŔşäĎź_‹…aĽ†dI˝ľI“&ruuU\\ś‚‚‚¸ÍÉ“'üÜű?~ĽĘ•+§Ť7ę»ďľSbb˘ĆŚóÔăT­ZUŇźËf†‘zý/żü˘ü1u€őë×?p˙‹/¦>–gáęęúČý>>´ţÝÝ»w%IIII9r¤–/_®ŃŁG«téŇzűí·®+W®Húsĺ2eĘhâĉúüóĎuřđa•.]:ţd5ü<öh—.]Ę”ăpţ-łÎ?“…‡‡ó%Lc†,‹ŮivçÎĺË—/uew ++P Ăx! ¸¸¸¨cÇŽf´ “yxx¨Nť:fg©¦M›¦^˝z)%%EWŻ^ŐöíŰegg';;;………é>Đš5k´aõnÝZK—.•­­­>ýôS]¸pA’ôŃGiĐ AŞV­ÚcŹ§ĆŤ«S§NŠPýúő5sćL988<ö8NNNrrrRxx¸Fڎ}űö)**Jžžžš4i’,‹ňäÉŁ={öčÓO?UĎž=ĺîî.[[[.\Xaaa˛łłÓ¬YłR[4tčP-\¸P—/_–$Ť;VăÇŹWXX>ţřcyzzŞH‘"0`€ŞU«¦J•*éÜąs*UŞ”•?~ŮŮ=v® €•âç±'+Y˛d†žÎ˙“eôů<›K—.iĹŠfgd;‰‰‰˛··7;@Çďpa¶;v¨X±bŞPˇ‚Ů)ĎÍ0 íŢ˝[¶¶¶ŞU«–śťťÍNBѱcGą¸¸ťń†đ˘,†afG ű˛X,Zľ|ą:uędv ˛°Š+ęÔ©SâËqřz }xzz¦®š x<^ž_dd¤ÜÝݵyóf5nÜŘěś49wîśú÷ďŻ;vČ××WţţţĘť;·ŮYŔ#Ő©SGuëÖŐ”)SĚN€•bM4+˛rĺJ†Áĺ/wďŢŐôéÓU¦LŮÚÚŞS§N:pŕ€é]ÖxYľ|ą$™ŢÁ…Ë˙^žßŚ3T±bE5jÔČě”4+W®ś6oެÍš5KîîîÚ¶m›ŮYŔ#ĺĎź_wîÜ1;VŚa¦‹ŤŤ}ŕČ(×®]“żżżĘ”)ŁţóźŞ_żľŽ?®ĺË—«FŤfç`šččh}űí·ňńń‘Ĺb1;ç…X, 0@§NťRĺʕըQ#y{{+&&Ćě4ŕ Đüav¬ĂL«O>ůD/^”$}đÁ 7ą ýúëŻ6lĘ”)ŁYłfÉÇÇG—/_Ö’%KTˇBłó0Ý—_~)uëÖÍě”tóĘ+ŻhÍš5Zľ|ąV­ZĄ*UŞhóćÍfg©Ŕ‹b€iňćÍ«€€€ÔĄZ,X łłŮČ™3gÔ·o_˝úę«Z»v­¦L™˘_ýUţţţ*\¸°Ůyd )))š={¶z÷î­üů󛝓î<==uüřq˝ńĆjÖ¬™zôčˇ[·n™ť0 €Ć0˛ťăÇŹ«GŹŞT©’¶nÝŞÉ“'ëÔ©S4hrçÎmvYʆ tţüy 4Čě” S´hQ…††ę‡~Đ–-[äćć¦5kÖť…ŽaĽ(†mDDD¨C‡ŞRĄŠ>¬%K–čĚ™36lĚÎ K VłfÍäęęjvJ†kŐŞ•"##ŐŞU+µk×Nť:uŇŤ7ĚÎBĹ0^ðzQQQz˙ý÷U­Z5ť;wNˇˇˇ:věŢ˙}ŮÚÚšť@–uöěYýôÓO2dŮ)™¦`Á‚š;w®ÖŻ_ŻđđpU®\YK–,1; 9Pľ|ůcv¬ðZżüň‹Ľ˝˝ĺć榣GŹjáÂ…:xđ Úµk'‹ĹbvYŢĚ™3ĺââ˘-Zť’éZ´hˇµmŰV˝zőR«V­tůňełłäÍ›W±±±2 ĂěX);łýíŮłÇěÍ\ĽxQúꫯTşti-\¸P]»v•Ť ź…ŔłŠ‰‰ŃâĹ‹5fĚ»’N4wî\uëÖM}űö•›››‚‚‚Ôż ‘áňĺ˧””Ý»wOŽŽŽfçŔ 1 € 7mÚ4M›6Íě ÄÄÄčóĎ?פI“ôŇK/)88X}úô‘ťżöŕy-Y˛D ęÝ»·Ů)¦«_żľŽ9˘ &hđŕÁúţűď5wî\•*UĘě4dcůňĺ“ôçϸ  -xUŠeĚ@zHHHЬYłôŻýK’4qâD  ¤ĂČ’věءڵk«K—.zë­·tćĚMź>]ÎÎÎf§`ő‚Uż~}Ő¨QĂě”,)wîÜň÷÷×ţýű§ęŐ«+((HÉÉÉf§!a/Šad)/^T—.]Ô A9;;ëŕÁZĽx±J•*evŮÂŻżţŞuëÖiČ!f§dyU«VŐŢ˝{5~üxŤ?^őęŐÓ‰'ĚÎB6‘7o^I  í@– éÓ§«RĄJ:pŕ€V¬XˇM›6©Zµjf§­Ěž=[EŠQűöíÍN± vvvňóóÓÁ•’’˘jŐŞiÔ¨QJHH0; VŽ•đ˘€éÖ­['WWWŤ=ZľľľŠŚŚ”§§§ŮYd;ńńńúꫯ4pŕ@ŮŰŰ›ťcU*W®¬={ö(88XÁÁÁŞU«–8`v¬ťťťrçÎÍ0ŇŚaćĉjҤ‰Ú´iŁ·ŢzKgÎś‘żżżrçÎmvŮŇwß}§Ű·o«_ż~f§X% 0@ÇŽSáÂ…U§NŤ5Jńńńf§ÁJĺË—Źa¤ĂČt÷îÝÓÂó· IDAT1cT˝zuEGGk×®]úúëŻőĘ+Żť@¶6kÖ,yzzň=÷•+WN[¶lQHHfÍš%777mßľÝě,XˇĽyó*66Öě X)†©¶oß®7ŢxCÓ¦MÓ„ ´wď^Ő©SÇě,˛˝={öh˙ţýňńń1;%[°X,0`€"""T®\95jÔHŢŢŢ|Ę;žKľ|ů@š1 €LqëÖ-y{{ëťwŢŃ«ŻľŞ“'OĘĎĎO¶¶¶f§#„„„¨zőę ᥳҥKë?˙ůŹ–-[¦U«V©J•*Ú˛e‹ŮY°ůňĺc€iĆ02”aúňË/őÚkŻiăĆŤúᇴnÝ:ą¸¸ť@ŽqíÚ5…††jčСf§d[žžžŠŚŚÔoĽˇ¦M›ŞGŹş}ű¶ŮYČâŔ‹`ć—_~QÓ¦M5hĐ uëÖM‘‘‘jŐŞ•ŮYä8sćĚ‘“““ştébvJ¶V¬X1…††jůňĺÚ¸qŁ*W®¬~řÁě,da ŕE0 €tg†ćÎť+wwwýöŰoÚłgʦOź.'''łÓČq’’’4ţ|őë×Oyňä1;'GđôôTTT”ZµjĄ¶mŰŞS§Nşqă†ŮYČ‚Ŕ‹`éęęŐ«jÝşµ|||4xđ`>^)))f§Ŕ 1 €§:}ú´<<<ôÓO?éÇT`` ěííÍÎŹ¬¦M›ĘŐŐŐě<#{{{ůůůéŔJNNVŐŞU5jÔ(%$$ť† ”'O†ˇű÷+Ä0žhÝşuŞ]»¶rĺĘĄ}űö©Yłff'€'8{ö¬~úé'ůřřť‚4pssÓ®]»4yňd«V­Z:xđ ŮYČ yňä‘$Ý»wĎäX#†đHÉÉÉň÷÷W۶mŐŞU+ýüóĎ*[¶¬ŮYŕ)fÎś)µhŃÂ중ťťť† ¦ŁGŹŞpáÂňđđШQŁovŇĂx ŕ!7nÜлᆱ   Íť;WK–,I}ŁČşbbb´xńbůřřČÖÖÖ켠ňĺËkË–- ѬYłTŁF íÝ»×ě,¤#†đ"ŔŽ;¦5jčěŮłÚ˝{·úőëgvxFK–,QBB‚z÷îmv ҉ĹbŃ€těŘ1˝ňĘ+Ş[·®Ľ˝˝cvŇŁŁŁ$†6  Ő† TŻ^=•+WNPőęŐÍNĎaÎś9ňňňRáÂ…ÍNA:+S¦Ś6mÚ¤eË–)44TU«VUXXŮYxA¬ €Á0$IóćÍS›6mÔ±cGýç?˙QˇB…ĚNĎ!,,L8p Ů)Č@žžž:~ü¸ŞU«¦&MšČŰŰ[wďŢ5; iÄ0^Ă9\rr˛† ¦ę“O>ŃW_}Ą\ąr™ťžSpp°ęŐ«§5jť‚ V¬X1­ZµJË—/×÷ß/WWWýđĂfg! ţ‹‹3ąÖa€,&&Fm۶ռyó´téRůűű›ťŇŕ×_Őşuëäăăcv 2‘§§§˘˘˘ôŢ{ď©m۶ęÔ©“nŢĽivž+ŕE0 C]ąrEuëÖŐÁµcÇ˝˙ţűf'ÁŠy{{ËĹĹEŃŃŃęŐ«—^zé%ą»»ëŕÁ©·ąsçŽüüüôńÇË××WÍ›7—ŻŻŻ˘ŁŁM,€ěaöěŮ*R¤Ú·o/IZşt©ňćÍ+‹Ĺ˘   %''K’ľýö[988hńâĹ’¤ű÷ďkҤIęׯźjŐŞĄ¦M›*222uż‡‡‡|||4nÜ8ŮŰŰ+666ó «`Á‚š;w®ţýďkĎž=Ş\ą˛V­ZőÂű]»v-ßß3­­­ěíí@šX Ă0ĚŽ@ć:{ö¬š5k¦ÜąskăĆŤ*UŞ”ŮI°R+V¬PçÎť•/_>ĹÄÄ( @ÝşuÓÎť;Ő­[7Ő®][áá኉‰QŤ5ÔµkWŤ?^’týúuŐ«WOIII:tč (`ňŁëŻRĄJiČ!7n\ęö±cÇę_˙ú—Ž?®J•*I’.^Ľ¨aÆiőęŐ’¤Č××W*T$5oŢ\GŹŐ™3gäää¤ *čĆŤ©ź6ߥKÍś9SEŠÉäG‰gńÇčź˙ü§ćĎźŻ–-[jîÜązĺ•WŇ´ŻË—/ËŐŐ•ďď™ŔŮŮY“'OV˙ţýÍN€•ae€ćřńăjذˇ *¤íŰ·3€tQ˘D IŇčŃŁUŞT)yyy©hѢ:rä$)00P§Oź–··wę}Š)˘1cĆčÜąsš8q˘)Ý|÷Ýwş}ű¶úőë÷ŔöáÇËÉÉIÓ¦MKݶtéRőíŰW’´oß>Íź?_®®®˛X,˛X,Ú´i“~˙ýwíرC’tűömÝşuK3fĚa;v¬rçÎťyĎĄ@š;w®~üńGEDDČÍÍMóćÍKÓľJ”(Á÷÷Lâč訸¸8ł3`…ČAöíۧ čµ×^SXXźę‹tc±XÚV°`AĹÇÇK’víÚ%Irrrzŕ6ożý¶$i÷îÝ\Ů׬YłäééůĐ'Ŕ*THC‡ŐâĹ‹uĺĘIŇ–-[ôŹüC’´˙~ąąąÉ0ڇ.-[¶”$Íž=[NNN6lŢ|óMĹÄÄ<ôµYOóćÍuâÄ 0@ RË–-uńâĹçŢßß3G®\ąRĎ)đ<Č!¶l٢ƍ«nÝşÚ°aňçĎovr›?5yáÂ…¶-ZTŇźźd x~{öěŃţýű5dČG^˙ŃG)W®\š6mš<¨7ß|S¶¶¶’¤›7oęÜąsŹüDň””IR‡täČ5oŢ\PýúőµxńâŚ{@H7ŽŽŽ ÔŽ;tîÜ9ą»»kŢĽy2 #ÝŽÁ÷÷ôáŕŕ „„ł3`…Č–-[¦wß}Wžžžúţűď•'Oł“Ăüő Áëׯ`ű_źRܤI“Lo€ě $$DŐ«WWÝşuy}áÂ…5hĐ Í™3G3fĚPź>}RŻsuuU\\ś‚‚‚¸ĎÉ“',I?~ĽĘ•+§Ť7ę»ďľSbb˘ĆŚ“qéî­·ŢҡC‡4pŕ@ üđC8p@۶m“ťťťY¬Ňµk×TŞT)Íž=[˝{÷~ěí~˙ýw•.]ZuęÔŃÖ­[S·ÇÇÇ«RĄJ:wîśúôéŁĆŤëäɓڷoźBCCĺä䤼yóęňĺËrvvVRR’^zé%ąşş*<<<3"ŇŮ‘#GÔ§OEEEiܸq1bDęJŹR¶lYľżg‚š5kŞqăĆ ćOĂĘŮŘęŐ«Ő˝{w :TS¦LaćÂ… ’¤€€ÝąsGÓ§O×ĺË—%IcÇŽ•ĹbŃž={ÔµkWőěŮS#FŚźźź .¬°°0Ţ(i0gÎ9::ŞsçÎOĽ]ѢEŐ´iSőíŰ÷í SëÖ­µfÍůúúęÚµkZşt©śśś$Iqqq©oRîŐ«—ęׯŻeË–eŘcBĆŞV­šöîÝ«qăĆiܸqŞ_żľNž<ůČŰΚ5‹ď•V¬ M­YłFť:uŇ A4}útłsMýµ2żv€Ě•””¤˛eËĘËËKOĽm\\śŞV­ŞcÇŽ)Ož<™T¬.""B}űöUDD„üüüôÉ'źČŢŢŢ쬩QŁFŞPˇ‚fĎžmv ¬ +dC7nT—.]ÔłgOM›6ÍěÎVŻ^­«WŻĘŰŰű©· ŃСCŔÜÝݵ{÷njňäÉŞU«–:dvVŽ”+W.%$$ť+Ä0@6łiÓ&µk×N^^^š;w®,‹ŮI ť«U«V*[¶ě#Żß»wŻŞV­Ş×^{MłgĎÖŔ3ąÖŔÎÎNÆ Ó±cÇT°`AŐ®][ŁFŤR||ĽŮi9ŠçiÂ0@6¦6mÚ¨K—.š?ľllřuŮMdd¤vîÜ)źÇŢ&oŢĽşsçŽlllôí·ß*W®\™XkSľ|y………)$$D!!!ŞQنöíŰgvVŽáŕŕŔĘH^ýČ&Ž=ŞvíÚ©M›6úňË/ ›š1c†*V¬¨FŤ=ö6nnn:ţĽ˘˘˘äáᑉu°V‹E PDD„Š/®:uęČŰŰ[±±±f§e{ąrĺbe¤ Żd—.]R«V­T˝zu-^ĽX¶¶¶f'€ ­ożýVC† ‘Ĺb1;ŮP™2e´iÓ&-\¸PˇˇˇŞRĄŠ¶nÝjvV¶ćŕŕŔ0Ň„a+÷ǨE‹Ęź?żľ˙ţ{988ť2Č_«˙tďŢÝědc‹E=zôPdd¤ŞV­ŞĆŤËŰŰ[wďŢ5;-[rppPBB‚ٰB X±ű÷ď«U«Vşuë–6lŘ ‚ šť2HJJŠfĎž­^˝z)ţüfç (^Ľ¸VŻ^­ĺË—kőęŐrwwצM›ĚÎĘvrĺĘĹĘH†¬TJJŠşwﮣGŹjÆ *UŞ”ŮI mذAçĎź×ŕÁÍNAăéé©ăÇŹëí·ßVóćÍŐ©S'ÝĽyÓě¬lĂÁÁa¤ ĂVjäČ‘Z·nťÖ®]«*UŞť2Xpp°š6m*WWWłS˝üňËZ˛d‰Ö­[§Ý»wËÍÍM«V­2;+[Č•+—Ě΀bŔ }óÍ7š:uŞľúę+5hĐŔěÁΞ=«ź~úI>>>f§ ‡{ď˝÷©Ö­[ËÓÓSť:uŇőë×Íβj¬ €´bŔĘ=zTŢŢŢú裏ԵkWłs@&9s¦\\\Ô˘E łS9;;kîÜąúńǵwď^U¨PAóćÍ3;Ëj±2ŇŠa+rëÖ-µoß^ 4;d‚-^ĽXC† ‘­­­Ů9@ŞćÍ›ëäÉ“0`€ ¤–-[ęŇĄKfgY;;;%%%™ť+Ä0€•HII‘———’’’´lŮ2ŮŮŮ™ťŇŮőë×Ú¶dÉ%$$¨Oź>&Oćčč¨ŔŔ@mßľ]˙ýďĺćć¦yóćÉ0 łÓ¬ĂH+†¬ÄǬm۶)44TEŠ1;d777µk×N[·nMÝ6gÎyyy©páÂ&–OVŻ^=>|XÔŕÁŐ°aCť9sĆě,«Ŕ0ŇŠa+ŞÉ“'kÎś9ŞU«–Ů9 †ˇ›7ojíÚµjÔ¨‘*T¨ ţóźŠĐŕÁÍΞ*Ož< ÔĎ?˙¬7n¨Zµj RJJŠŮiYšťťťÍ΀b ‹»xń˘ú÷ď/oooőěŮÓěAîŢ˝«äääÔ7Nź9sF_|ń…rçέ9sćčĉ&ĎĆĂĂCGŽѸqă4nÜ8ŐŻ__§Nť2;+˲··ge¤ ĂYXJJŠzőęĄâĹ‹kĘ”)fç€ ôÇ<đgĂ0”śś¬ű÷ďkáÂ…rssSÓ¦MőăŹ?ňIëČňěííĺçç§(!!AŐ«W—żż?ź€˙vvv  MČÂ>˙üsíÜąS‹/–ŁŁŁŮ9 EGG?öşÄÄD†ˇ-[¶ČËËKgĎžÍÄ2 íÜÝݵgĎůűű+((HµjŐҡC‡ĚÎĘR@Z1 E>|XcÇŽŐ§ź~ŞZµj™ť2Ř­[·žx˝Ĺb‘˝˝˝Ö®]«×_=“Ş€ggg'???EDDČŮŮY5j”âăăÍNËěí한śĚŠxn dA÷ďßWĎž=őć›ojÄfç€L𤕤?‡–-[¦zőęeRľ^}őUmÝşUÁÁÁ QÍš5µoß>łłLggg'I¬€çĆ0@4räHýúëŻúć›odkkkvČŃŃŃŹýľo±X4ţ|µk×.“«€ôe±X4`Ŕ;vLĹŠÓ[o˝ĄaÆ)66Öě4Ó0 €´b ‹Ůşu«BBB˘ŇĄK›ť2ÉíŰ·9 `±XôŮgź©Oź>&TŁlٲڴi“,X ŻżţZU«VŐÖ­[ÍÎ2…˝˝˝$†đüČB4dČýă˙———Ů9 EGGËb±<°ÍĆĆF ’źźźIU@ƱX,ęŃŁ‡Ž?.www5nÜXŢŢŢş{÷®Ůi™ęŻ•M.€µa ™4i’.\¸ łS@&űăŹ?dFęźíěěäéé©™3gšXdĽâĹ‹ëűďż×ňĺ˵zőjU©RE?ýô“ŮY™ćŻaVŔó˛3;úĺ—_¨qăĆ©lٲfç€Lvűöm%''K’ěííőÖ[oińâŲ±áł‘3xzzęí·ßÖČ‘#Ő¬Y3yzzjÎś9*T¨Ůié&..NďĽóŽâââ-Ă0”śś,'''˝ńƬR˘D …‡‡?´bđ†˛•.]Zľľľf§ŹtőęU 6ěmׯ_WѢEŐ©S§¶—.]Z“'OÎĚ<°z ŘŰŰ«rĺĘZ·nťĚÎ2UѢEµdÉyzzjĐ AŞ\ą˛BBBÔľ}ű'ŢďţýűĘť;w&U¦ťŁŁŁ .¬ýű÷?°$Ý˝{7ő˙[,učĐA<ĂYŔŞU«´~ýzmٲEöööfçŹTĽxqíÝ»WżţúëC×­\ąň?üńÇ™•ŮĆŤ7$IŻĽňŠ6nܨ|ůň™\§U«VŞ_żľüüüÔˇCyzz*$$DEŠyč¶ÉÉÉjÚ´©>űě3Ő«WĎ„ÚçÓŁGmܸń‰·1 C]ştɤ"X+‹ńż#¦ČTwďŢUĄJ•Ô¤I-\¸Đěŕ‰ĆŹŻĎ>űL‰‰‰OĽ]DD„ÜÜÜ2© Č—.]ŇîÝ»ÍÎ@6ćëë«;wîhâĉŹ|Ă3˛Ő©SÇě «±qăFy{{ëîÝ» Ô€¸ţ‹/ľŻŻŻŠ)˘-ZÔ¤Ňgs˙ţ}˝ôŇKŠŤŤ}ěmŠ+¦+W®°2ža“Ť3FłfÍRTToüC–węÔ)U¬Xń‰·©Pˇ‚Nť:•IE@ćY±b…:wîlv€l cÇŽ­Ş'»sçŽĆŽ«ŕŕ`˝ű3gŽJ–,©łgĎĘÍÍMńńń˛··Wť:u&[[[ł“ź¨WŻ^úî»ď”đĐuööö>|¸‚‚‚L(€5±1; '»zőަM›¦ŃŁG3«ŕęę*77·Ç~J­˝˝˝zöě™ÉU@ć2 — ą:tČô.éرŁŮ_ƬRţüů5}útmßľ]gÎś‘›››ćÎť«~ýú)%%E’”¨]»viüřń&×>]·nÝ9 ýů8>Ŕł`ŔDţţţrvvÖ!CĚNžYŹ=űi»IIIęŇĄK&@öP˝zuł€,Ż^˝z:|ř°z÷î­‘#GjÇŽJLLL˝>99Y'NÔš5kL¬|şFŤéĺ—_~äu...zăŤ72ąÖa“ś>}Z_}ő•”'Ołs€gÖĄK%''?´Ýb±¨fÍš*[¶¬ U §pttÔđáĂ•””$Ă0şŢb±¨GŹ:ţĽ uĎĆĆĆFÝ»w—˝˝ýŰsĺĘĹ*Kxf dÔ¨QŞX±˘şuëfv đ\\\\äáá!›Ýhkk«=zTr’~ýú)))é‘ץ¤¤čţýűjŐŞ•îÝ»—ÉeĎÎËËëU $)!!Ať;w6©Ö†a=zTkÖ¬Q@@€lmmÍΞ[÷îÝe±XŘ–’’"OOO“Š@N±hŃ"mŢĽůˇ7Ň˙]bb˘˘˘˘ôŃGebŮó©^˝ş*T¨đŔ¶ňĺËËÍÍͤ"X†L0iŇ$ą»»ë˝÷Ţ3;H“˙}Óż­­­Ţyç-ZÔ¤"üöŰo>|ř3Ý6))IsćĚŃŇĄK3¸*ízöě);;;I’˝˝˝zöěir¬ Ă™ěüůóZ±b…üüüúduŔZĽôŇKjҤIęʆa¨{÷î&W€ěÎ0 ¨cÇŽ*T¨$ÉÎÎ.ő őŹŇŻ_??~<łź‹———’““%ýąšAÇŽM.€5a “M™2EĹ‹č“ŐkÓ­[7†!éϕڶmkr€ĚtçÎť Ůobb˘víÚ•nű˨ÎĚ’Ţç°vĹ‹×ŕÁµbĹ ÝĽyS˙ýďµ`ÁőîÝ[Ĺ‹—ôçĎ%HJJR«V­˛ä×RĄJ©Nť:’¤Ę•+«bĹŠ&Ŕš<~$éîćÍ›Z¸pˇ>űě3ŮŰŰ›ťĽ6mÚČŢŢ^ńńńjٲĄ (`v`•ĆŚŁ)S¦čţýűjÚ´©ĆŤ§˛eËjâĉš5k–$©}űö:t¨6l(IÚ¶m›FŤĄýű÷kČ!úâ‹/Rßřęáᡷß~[“&MĘŢĎ?˙\ëÖ­ÓîÝ»•nű˝}ű¶&Ož¬3f(666uŘ(­2Ş3ł¤÷ů@βbĹ ł2UîÜąŐ¤I5iŇDWŻ^Ő‰'tâÄ EDDčŹ?ţűß IDATPRR’Îź?ŻćÍ›křđáfç>ÄÍÍM»wďVŐŞUsÜßť5sqqIä0‹Ĺŕ_‹™fęÔ©ň÷÷ו+W”7o^łs€ćéé©ĐĐP­ZµJíŰ·7;ČP+V¬PçÎť3äMŮź|ň‰&Nś¨#FhňäÉ©ŰŰ·oŻďż˙^_ýµşuëöŔ}ćĎźŻÝ»wkáÂ…l˙ý÷őÚkŻi„ éŢ)Ińńń*Q˘„nŢĽ™!ç˘hѢşvíZšö}éŇ%•,YRRĆwţýXéEβ¦żV‡Ząre†Ăb±dŘľü©cÇŽúß1Ŕł°1; 'Y´h‘ştéňžžž˛X,\¸~ •$učĐÁô.\,‹Ő~˘ň#äčč¨eË–)%%%uűرc%I‹/~č>»wďÖŔÚţÝwßeŘ €$988¨H‘"¶˙B… Ąé~.\P×®]S˙ś‘ť˙{¬Ś”Öó,_ľ\†apůźËĄK—LoxÔ%((Čô.Ď~éرŁŮ˙‰H’ěĚČ)öîÝ«cÇŽiţüůé˛? ><]ö¤URR’ľűî;uďŢÝě@ť;w6;!Í ,¨víÚiéŇĄúĎţŁwß}W’T­Z5*TH[¶lŃąsçT®\9IR\\śŽ;¦Úµk›™ťe\ľ|Yď˝÷ž’““łŐ±¤ż%JťđH#FŚ0;Va€L˛`ÁąąąéÍ7ßL—ý•,YRť:uJ—}/˘m۶rtt4;°ęaIęŮł§–.]Ş/żü2u`ëÖ­Ę›7ŻnÝşĄ/żüR'N”$…††ŞuëÖÜ?%%E«V­ŇúőëuţüymÝşU;wîÔš5k´fÍíÚµK]»vŐůóçuôčQĺÎť[3fĚĐéÓ§uôčQ9;;kęÔ©rss“$8p@>>>ŞYł¦ *¤Ď>űLŃŃѬnsýúu 4H[·nUńâŵhŃ"Ő¬YS’tćĚŤ=Zĺ˗ו+WtáÂ…„„¨rĺĘOěú_S¦LŃ'ź|"µk×No˝őÖC·Y´h‘Ž?.ggg 4HłgĎ~ŕú'uŢż˙‰çáiÇ yâăą~ýú#Ď»»»$)66VS§NUTT” *¤ŁGŹŞ]»v6lŘ#Ź˙¨óń¸ÇP©RĄ'¶9;;?´˙Çý˝ą»»kíÚµZż~˝6lŘ }řá‡ú÷ż˙ťzNkÔ¨ńL·‘¤;wî( @666JHHPdd¤ÜÜÜ4věX9;;kţüůňööNýî»wďjţüů1bDę¶g=ÖÓÎńó>€Ś`cccv¬‘€ —`(PŔ2eJşěŻcÇŽFÇŽÓe_]H2–/_žaű_ľ|ą‘‘żfONN6J–,iŘŰŰżýö›a†ŃµkWc۶mFľ|ůŚbĹŠ‰‰‰†aF Ś3gÎ<´Ź_ýŐd¸şşńńńĆîÝ» GGGC’ńŮgź›7o6úőëgÄÄÄýű÷7Nť:•zßfÍšE‹5îÜąc†aĽţúëFˇB…RŻďÜąłqíÚ5Ă0 ĂŐŐŐdŚ?޸pႱ~ýzC’Q§NťÔŰżöÚkFůňĺ Ă0ŚÄÄDĂŮŮŮpss{j×_ű6 øuë–Ń˝{wăرcO==îż{–Χť‡§ëiŹçqçáŻ?7lŘĐčŢ˝»‘’’b†a,\¸Đd¬[·îÇđ¤óń¸ÇpăĆŤ'¶=Ę“z/]şdäË—Ďdżüň‹ńÍ7ß’ŚÚµk?ómîŢ˝kĽţú놿żęqŻ]»fĽţúëFąrĺŚččhĂ0 Ł|ůňý7÷÷mĎr¬g9ÇiyĽĚř9.Łż9˙Y…Ĺ0 #łrŞ-[¶¨I“&:{ö¬Ę—/˙Âűóôô”$­\ąň…÷Ů…ĹbŃňĺË3lŐ”+V¨sçÎĘČ_łüńÇ TPPú÷ﯦM›ęŔęׯź,X Ő«W«J•*ęŃŁ‡víÚőČ}X,ąşşęäÉ“’$WWWEEEéÖ­[*X° $ißľ}Ş]»ö#ď˙ď˙[-[¶ÔË/ż¬ëׯkúôé:t¨Nś8ˇRĄJÉÉÉI+VÔ©S§”’’"‹Ĺ"Iz饗tďŢ=ĹĆĆţ?öî<®Ş2ńř犊 .¸`âeµqÜRq‹@ű Úd"ĄŤ9©$¤)2((*(ęd“[ę 5•JŁ6Ň2.:j.€á‚ nČrŮďóűĂ7»ŕÂýĽ_Żű*ž{Îs>ĎrÎĹçą°qăFtîÜoĽń„čÓ§nŢĽ‰ŇŇRŤą¨ęNOOGhh(Ö¬Y:čě»ęí~ş.M9ĄôÔcijʶ~ظq#üüüšš @EEöěŮ)S¦ŔŇŇRgHi¦lęH·§Ď[[[äć梸¸XŇ6 EVVlmmUŰěŮłŢŢŢX˛d ÂĂĂUmşžęeşŽĄ«ŹŻ]»V§9PŤń{\C_‰žwü÷5&†@DDDDDDDDDDDô<8|ř0úőë§—…DDôěš5kÖ¬YĎ>ű ­[·ĆoĽŞĹź~ú)ţô§?aĆŚ’물 ţé›Ŕ“’’ŕää„K—.iÜoëÖ­xűí·áëë‹={ö`óćÍ077W[7těŘ)))Şź-Z„ÂÂBlٲ>DII ĘĘĘ´ćzš››  i!€.šrJé‡ÚŁz{´őĂŹ?ţčÖ­›j{cccřřřÔ¨_SHi®ľ–š÷éşžfee…»wďJަr!Kőůäââ8uę”ÎśRŹĄ«Źő9›‘ˇ=âââŕîînčDDÔÄÉĺr 2iiiXąrĄę¦˙aÆˇ_ż~8vě>˙üózŰ÷žž…BQă=ĄR :u*.\¸WWWś={ŁFŤÂ®]»$#)) ÎÎΰ··G`` Ú¶m[«ŚëׯGLL ÂĂĂkµ_mHé‡úŇÖ•7¬§ĄĄé¬GSč» ő7)ŚŚžÜ®’‘‘QĄÜĆĆ`aaˇ·céęăĆDDDDDD …‹Řőëב––777CG!"˘f`Ö¬Y€ÁŁsçÎŞňŮłgC©TâOúS˝ż-_.—CˇPÔ¸±<99›7o¬X±ööö8räöďߏ˛˛2J>†··7ĘĘĘ0iŇ$µż±úŐW_E@@đď˙[çö2™ ĺĺĺµ:†”~¨ď±´őĂ€ˇˇˇB¨ĘoܸQŁÍšúŁ®m¨K^}©|@\\\•ň[·n&Lŕ÷oý/--!đřńăZKWë»˙š˛ĽĽ<­ďçää 66aaaő>VmĎU""""ŞC """""""""""zÖť:u ­ZµÂ!C …š7Ţx‹-R- ¨4sćL,]şo˝ő–Ć} T˝áł¸¸PXX333ŔäÉ“aooŹÜľ}ăÇŹGrr2ńĺ—_xňMô‹-‚ĄĄ%<==ńŢ{ďˇk×®UęĎĎχąą9€ßoü¬VII """přđa$&&ŞM•——#** ™™™ČĘĘÂíŰ·±páBxzzÖ«mDDDDÄ'5¸3gÎŕĹ_¬rŁ‘&íŰ·‡··wŤ_;tčooo¸»»«ÝOˇP¨ľÍůÎť; Ĺ’%K‘‘đóóĂ… ¦¦¦ŹŹ‡‡‡ľůćřűű#''{÷îUÝ4ŻP(0~üx„‡‡ĂÇÇŁFŤÂţýű±~ýzÜąs°|ůrbÝşuČĘĘđ䉥ĄĄ C»víŢ˝{ăăŹ?†••Q#ףGʰjŐ*Uůš5kp÷î]řřř 77ŁGŹĆš5k4~Ű´——Úµk‡¤¤$!$ĺ”Éd:űA×± V®\©¶źh쇰°0ôë× puuĹůóç†üü|¬]»ąąą’úُ¸XcŚŤŤµfSG[Ţť;wŞę E^^"##‘™™  † tn#“ÉT‹fÍš…?ü}ô¬­­“'ßmގC‡bÆ x˙ý÷áćć†~ýúaćĚ™ČÍÍETT”ÎcuîÜYcËd2Iç5®‘#GbőęŐčŃŁ`۶mµ^...Ş…8;vDdd$^xáŔ /Ľ€¨¨(těŘQ•ËĹĹEEE;v,˛˛˛pńâE$$$zö쉄„\şt ™™™čÓ§ EÝ;¦ŽŚŤŤáëë‹«WŻj}˘‰\.GDDD˝Žejj ???¤¦¦˘˘˘BU‚‰'"""űöíĂ´iÓŕĺĺUďă O?ŤônđŕÁ9r$6nܨ·:˝ĽĽ±±±z«“¨ą“Éd‰‰Á´iÓ¤ţ`úôéŕźŮ‰¨>ă÷¸†ľ6GGG¤¤¤Ôűš+“ÉTO™xZVV „6mÚŕňĺËhŐŞ•Î}Ôĺúꫯ`ff†I“&iÝăĆŤpuuĹţđ‡zµ§®¤ö§¶¶×őXvvvŘąs'ĆŹŕÉS[,,,0tčPŐâ‰ć†˙#""˘¦‚O """""""""""j@ĄĄĄřĺ—_0xđ`CG!""""˘˙ŻsçÎXąr%~ýő×z}Cý«ŻľŠ‰'ęÜîŻý+úôéSçă4gJĄ_ýµęçű÷ďx˛H€ę‡‹ĐÍ›7QZZ ą\nč(DDDDDÍÚŮłg1lŘ0Ěź?Ë—/G‹-PXXXçú<==allŚcÇŽŐąŽÖ­[ĂŘŘXçv¦¦¦hѢ…Ú÷Š‹‹±víZĚ™3ĆĉqůňeŔÁ1wî\ŘŮŮ!77>>>čСśťťqîÜ9UiiiđňňÂŇĄKáíí \şt©Ć±®_ż´oßC† ÁŹ?ţ¨5·¶lPTTĚť;AAA¨1&GŹŲeËT?×®]Bˇ€™™Yťęł°°@§NťpĺĘ•磻¤@&“Ő+ŻT .„żż?úöí puuĹ„ ––†AaĆŚ(((Ŕ–-[‚‰'â­·ŢÂű￯ę 777(•JÄĆƢĽĽ;vÄ›oľYcA@tt4.\×^{ ~~~0aÎź?ggçZgkÓ¦ ĆŽ‹ţýűăÓO?¤§§cÝşuUęprrRýYY˘ŁŁ±sçNôďß_?HDDDôă“PFFÚ¶m kkkCG!""""jÖ=z„‡"** BˇU«VőŞÓÄĦ¦¦5Ęĺr9„5^ú~âWbb"¶oßą\™L™L†cÇŽáîÝ»řůçźŃµkWtíÚ€îÝ»cĆŚ°±±Á… TőĚ›7«V­ĂÚÚ©©©5Ž‚ &ŕ/ů ÂÂÂPQQ:e۶mÎś9???Ő>ööö°··×ŘŢĎ?˙ďż˙>fĚQ§ţ""""˘Ş¸€¨Ýşu‹O """"Ň­[·ÂÜÜľľľ2d `nn^çúĘĘĘp÷î] 8PŹ)k')) NNNj¸ąą€Ú'XYYˇ¤¤Dőó˘E‹ŕîîŽ-[¶ 44%%%(++«±ßÓý5eĘŔŐ«Wë”íرc€ž={VŮĎČHó-iżţú+>řŕŤďQíp1Qzüř1¬¬¬ ¨Ů›:u*.\¸WWWś={ŁFŤÂ®]»ę\_||řŕĽýöŰjó@~~> ĽĽ\•733ß˙=öíۧę§ÄÄDÜľ}2™ Ŕ“óŻŇĆŤ1yňdřřř¨Ů_ş˛-^Ľ&&&ŔŃŁGQTT„~řwîÜdddTÉĽpáB¸ąąIę""""’†‹PQQZµječϤʱš+SSSřůů!55†ŽcpęĆłWŻ^9är9"""Ôľ§ďLęę«ĎÜÖwľ¨¨( 44_ý5:věÇŹW9޶ţjîç©>ięËć@Ýkwjú ýdee…°°0™™5ú±©é;qâ–-[†›7oţň—żŕŕÁžÜ`?~üx„‡‡ĂÇÇŁFŤÂ?˙ůOµőś>rą^^^prrBbb"÷Ţ{éééUžPP\\¬vaŐťLT_ÂJDDDDDDDDDDDDzóć›o˘¸¸Xu“ŤľxyybccőZos‘‘‘oooüüóφŽRoŽŽŽHII©ńͳϓ¦:ž2™ rąÉÉÉŤv̦ÖŽŽŽB %%Eç¶ŐűËm‘Éd‰‰Á´iÓ¤ţ`úôéu:_kÓ—M•şsÂç 鏡? }|CiŚßăúzHôĽ{Ţ˙=FDDDM‡‰îM¨®ĚĚĚđŕÁCÇx¦dffâµ×^ă·é?#8žżkŠ}qëÖ-ŘŮŮŐzż¦ŘC«k_ibdčDDDDDDDDDDDDĎ2333: °°«V­ÂĚ™3áëë‹1cĆ 22Rő~^^>úč#,[¶ ţţţpuu…żż?rssÄÜąsagg‡ÜÜ\řřř C‡pvvĆąsç$oĹĹĹX»v-ćĚ™Ácâĉ¸|ů˛¤¬;wîÄ•+WťťŤyóćŐŞΞ=‹aÆaţüůXľ|9Z´hÂÂB$$$ŕĂ?DŻ^˝p÷î]xzzÂÚÚÎÎÎUžę +·®÷‹ŠŠŕďsç"((užĎňx*•JÄĆĆÂÇÇŁGŹÖk›ęJ]&…B˝{÷âÍ7ßÄ#€?ţńŹčŮł'Nž<‰k×®áő×_GÇŽáččX%şúę3·ŐŐ'u^«‡yó桰°P•gŢĽyČĎĎW;6Őij‹¦ą˘T*ńÓO?aѢEčŐ«îÜą1cĆ GŹŞńm®ÔőĺÜąsńÝwßilŻ®ą^źą§IZZĽĽĽ°téRx{{ĂĹĹ—.]Şwű÷îÝ 333Čd2„‡‡«ěŰ·¦¦¦ŘµkÝç÷öíŰadd™LČĎĎdž TeµťC }ÝוGÓçQ]ĆG굯.źAőťkµťWhŐŞ>üđCś˘wďŢB'×KKKáä䤱 ÚĘŞ Ä•+WTe7oŢŻżţşBÚů-„˝{÷®Ń˙•eÚú´úŞÍůQ×ëľ®9­îó(''Gcj)׾ş~Őw®éšWrą\uü‡Š™3gŠ_~ůĄJşĆ@mý[źyT—,ęÔö÷¸şhčë!Ńó®1Îc"""")¸€¨EGG kkk˝×[Ű›O6lŘ ÔÔTUYyyąŘ±c‡xôč‘řř㏑••UeżÝ»w bÉ’%B!úöí[ă:ajjŞúY×6gÎśÔľ>¬3«użaşcÇŽ€ŚŚJĄR\ľ|Yuź UŰňÉ'€xăŤ7tćÖőţćÍ›‘śś\%SĺqkăyϧËőŮ&©¤Ţř\˝¬k×®52tęÔIXZZę?ésMęĽrssCaa!:tčPĄ\J«SŰţ­NݸŐ5 1ű IDATQCâb"""""""""""˘dgg¸}ű¶AsÜ˝{Ŕď7˙VWy3`FFF•r€………޲==]™™ bܸq"::Z‰ąsç ĄR) …prrÝşuYYYŞý|}}Ĺ#DYY™Bž={ŠębęÚµ« y›ââbaoo/wŢyGěÝ»WŠ—_~YäĺĺéĚ*„/Ľđ‚0337oެUżµiÓFu}!eNhŞO׼ÖćáÇ€°··×yuýU˝-şćĘÓýTPP 5›&DLLLťö•"&&¦Ćü’BS_ŞkŻÔą^źą§Ž………ÉdâرcbďŢ˝˘S§N€8s挸uë–Ú1VW¦Mvv¶055cĆŚ©R.µÍŻżţş ‚‚‚DZZšŘ¸qŁhßľ˝ Ž9"***j5‡úş/„ć9­íóH]ăŁëÚ'ő3HťúĚ5]ą{őę%ĄR)ĘËËŸqㄥĄĄ8ţĽBÚuCmý[×yT×,ęÔö÷¸ş¨ĎőđÚµkbýúőzNT;C‡‹/~f3‹ĐĐP1|řpall,„bÝşuÂĹĹE4Č1›ŠĆŰĘ9\^^.>úč#qűöí*ďk*ŻŤĆ8ʉ¤0®çz""""""""""""ŇâčŃŁ(..ĆäÉ“őVgll,`Ú´i’¶·˛˛‚‡‡ŇÓÓqîÜ9$$$ŕ…^@XXZµj…-ZŔŰŰŹ=¶mŰpńâE?~–––Řľ};Z´h-[¶`ßľ}€-Z`ŕŔضm›*KYY~ůĺ8p@ë6ŁGʆ§§'ŇÓÓqěŘ1?~ÝşuCtt4¬¬¬tf€ěěl$''cŕŔptt”Üořţűď‘››‹O?ý666řüóĎaii‰ččhÜżíÚµCßľ}QXX˙ţ÷żŘşu+ZµjL™2Ecn]ďŰÚÚběر¸xń"6oŢŚ]»vÁÖÖůůůxĺ•WĐĄKôčŃűöíĂ‘#Gpúôiřůů=—ă©P(Š'N ??8p ćĚ™Sď6Ť9&&&ZçÉożý†U«VáĚ™3ČÍÍ…••şwďŽ*™:uę„đđpś>}>|8®_żŽ-[¶@©T˘°°C‡Ĺ?ţńüóź˙´mŰVő ÝŐŰčä䄇ÖŰ{÷îŐ9'Ôő™““¶oß®u^krůňe¬YłgĎžĹăÇŹ!“É`eessóÇ133ĂşuëŞô—\.Gnnn•¶h;GLMM±fÍ|ýő×€ű÷ďĂÎζ¶¶ZÇŞşżýíođňňBż~ýjµźTW®\Á—_~‰Úü™]]_šššbÇŽjŰ+ĺüÍÉÉAHHHťć^›6mÔćl×®Nś8_~ů3fĚ€˝˝=Îś9›7obĐ A¨2Ʀ¦¦Xż~}Ťq×6ŻÚ¶m‹3gÎ`ćĚ™čßżżŞ\J›ŕŹü#ńí·ßâŇĄKřŕpúôiŚ=:tŔ×_ŤÖčSMňşŻkNkű<Şířää䨎ŁéÚ7uęTLś8QçgPĺ·ćWŞď\Ó”űâĹ‹¸qăŽ9055Ĺ /Ľ ěßżű÷ď‡L&Ă‹/ľéÓ§kM´őo]ç‘®ůPµý=®.ęz=üé§źŐ«WĂŘظŇévüřqŘŘŘ`ěرĎd <P(Ć A°téRęüśą}ű6Úµk§÷\ŤˇˇÇöé9lbb‚AaŢĽy2dę\522R[^ŤqI!â©gq‘ŢůůůáĉHLLÔ[ť^^^~ż …ęĎŃŃ)))h >»víĽ˝˝‘`č(ÔDÔuN4ĄyÝd2bbběĆĽ`úôéĎMę›BˇŔ€đË/ż uëÖ†ŽóÜťÔt4Ćďquą&''cҤI8ţ<Ú·oß`ŮčwŐŻCR®KđööĆĎ?˙ÜX1› Ms¸rARBBĚĚĚt–KÁŹQSadčDDDDDDDDDDDDĎ:ggg\ąrĺĺ冎ňĚ’Éd:_©©©†Ž)‰BˇŔ¦M›đŮgź:Ę3©9ÎÎ zVDGGcÁ‚Mb!U%„Ŕ[o˝…·ß~› š°ĚĚLĽöÚk¸wŁ49Úćp˙ţýŃ»wo,^ĽXR9QsÂĹDDDDDDDDDDDD lčСP(¸xń˘ˇŁ<ł„:_}űöŐZGaaa•˙Jzz:ÂÂÂŕäädĐĎ*}Ě•ĆVź9ŃTć5=żÎś9 Oź>Řşu+Ţ{ď=CGRáůAô»â˙ű&MšTĄüěŮł6lćĎźŹĺË—ŁE‹Şs¦¸¸k׮Ŝ9s0xđ`Lś8—/_–´Ż¦÷”J%bccáăăŃŁG«ęĘËËĂG}„eË–Áßß®®®đ÷÷Gnn®*˙Üąsagg‡ÜÜ\řřř C‡pvvĆąsçTőüđðłł«ň­úŐË4eĐÖ)ŠŠŠŕďsç"((÷żwď<==amm '''ś={°sçN\ąrŮŮŮ7ož¤ă*•JüôÓOX´hzőę…;wî`Ě1čŃŁÖ­[###Čd2@~~>6lŘPĄLJßJŮF]żJ7ŘĽy3fÎś‰żţőŻhŐŞU•…ś•u©›Ă•\]]±}űv¤§§K*'"""j.¸€¨9::˘}űö8uꔡŁ………řřăŹqëÖ-ŔÂ… ‘`°űě3$%%ÁČČ&L@~~ľÎ}5˝gdd„aÆa×®]ČÉÉ`đŕÁhÓ¦ VŻ^Ť|ńĹ8|ř0 „ÇŹcĐ AŘ·onßľŤ-[¶ $$‘‘‘¸|ů2Ţ˙}U{ňóóńđáCäĺĺi,S—AW{t©¨¨Ŕرc‘źźŹż˙ýďXąr%ćĚ™¬¬,µŰGGG#""{öěÁ•+W°páBŔÇ °µµĹÖ­[%»ĽĽ-[¶Ä§ź~ŠŚŚ ěŢ˝AAAxůĺ—ń׿ţöööŞmÍÍÍáççWĄLJßJŮF]żJ·Í›7ă>@dd$¶lŮ‚ŕŕ`€żż?„4ĎáJÇGyy98 ©ś¨ą‰ĘߨÁĽňĘ+°˛˛Âľ}űôRź—— 66V/ő= d2bbb0mÚ´©˙Ŕ>}:řgv"ŞŹĆř=®¶×Ă^˝z!77Ź=ŞRŢ©S'Ü»w‘‘‘X°`®^˝ŠîÝ»#99C‡U[×áÇáćć¦q_sss­ďUć—ËĺHNNF`` BCC‘••[[[ŐqöěŮooo,Y˛áááËĺHMM­rŤ¶µµEnn.Š‹‹Ue066®’Y]ŮÓ´ő…”{ŃŃŃ?>’““!—ËUĺ}űöŵk×T™‘’’ĄR©úĆű: ¨¨Hő깤Şěź‡ÂĘĘJU^y̧ű­z™”ľ•Ú˙ŐóKŮoňäÉ8|ř0Š‹‹Ń˘E \ąrNNN6lNź> @ó®”••….]şŕŐW_E\\śÎr]řď1"""j*řd"""""""""""˘F0|řp>€š¤ěěě*7WÚşu+ĚÍÍáëë‹!C†   ćććHJJ‚““„5^nnnZ÷Őő^u'Ož€ﻸ¸€ęwěĘ›çźfee…’’’*eŐoú×T&µ/¤8vě gĎžUĘŤŚÔßşőt[:vě(ů ÚTÖ©nśĄîű´ę}+µ˙ëR÷ĉˇT*U7ë·jŐ 0nÜ8Ő6šćp%KKKŔÝ»w%•5\ @DDDDDDDDDDDÔFŹŤ7n ==ÝĐQŞ066FEEEŤň©S§âÂ… puuĹŮłg1jÔ(ěÚµ <@zzşÚ›Ô•JĄÖ}u˝W]ĺ óUĘmllunwmÔ&su™™™€4dÄgÖüůóńŮgźaöěŮXĽx1üýý‚Ő6šćp%u‹´•5\ @DDDDDDDDDDDÔ†޶mŰâřńㆎBDDM@^^žˇ#¨•““ŘŘX„……i-kJ?~Ü$ëjLM}Ś4ičó ©žgMQçÎť‘››[Ł|ĹŠ°··Ç‘#G°˙~”••!00rą …áááU¶ONNĆćÍ›µî«ë˝ę*źPů­đ•nÝş0aB­ÚŞî†qm7‘WŞMćęär9€šm¨ ™L†ňňňz×ót}PZZ B4ąkaEE._ľŚ„„¬[·ß|ó ‚‚‚Ş<ŃAÓ®ôčŃ#€­­­¤r"""˘ć‚‹AË–-1räH. "zέ_żŁGʆµµµˇŁÔ’’‚L›6 {öěŃXÖ””” ,, /˝ô’¤ľ6l–,Y˘—şôA[žÚŞíŐçŘ›6mÂŇĄK1nÜ8¸¸¸ŕÚµkuާˇĎ¦|ž5UŁGŹF~~> Ş”Ż_ż^uµ§§',,,еkWLž<ööö ÁěŮł±oß>á>ŔŰoż­u_]ďUf¨\̱dÉ899aÓ¦MČÎÎVe‹ŽŽĆ#0ţ|@qqqŤvĺçç€ęćů¸¸8XZZâČ‘#ŞmÔ•UĎ +ł.‹/†‰‰ pôčQá‡~Ŕť;wüţÔĘăUć~_¤TXXčÝ»7˛˛˛T‹!¤ŞěźĘz*U.TXµj®_żŽČČH”””Ž= ĄR)©oĄlŁ®_Ąě†C‡áż˙ý/Ž=ŠÓ§O#--­Ę"Ms¸Ňýű÷#GŽ”TNDDDÔ\p1Q#™0aâăăˇT* …ŃíŰ·U˙ż`Á\ąrEŻßę¬/rą:ËšSSSřůů!55UŇ7z÷ęŐ ­ZµŇK]µőôřKÉS[µŁş;**  Ĺ×_ŤŽ;ÖůŰĂú]Ą\ˇP`üřń‡ŹŹFŤ…ţóź055E||<<<<đÍ7ßŔßß999Ř»w/ĚÍ͵î«í=…BˇzÂĹť;w°qăF”——ăôéÓxóÍ71kÖ,|řá‡ř裏`mmŤřřx`Ë–-ŞęCCC‘——‡ČČHdff‚‚‚P\\ SSS´kצ¦¦Ş6V/S—!??_k{t0`âăă!—Ëáĺĺ'''$&&bŕŔxď˝÷đ믿bíÚµŞĹË—/Gaa!Ö­[‡¬¬,OžLPZZ ///´k×III’Ž­P(°rĺJU˙řůůáÂ… Ş÷ĂĂĂ1tčPlذďż˙>ÜÜÜĐŻ_?Ěś9ąąąŠŠŇŮ·6lĐąÍÇkôkxx¸¤q>|8 0{ölLš4 /˝ôĐąsgüë_˙ yW:yň$ŚŤŤ1mÚ4IĺDDDDÍ…L! ‚čypéŇ%ôď߉‰‰ššb×®]3ĄĄĄÁËË K—.…··7\\\péŇ%˝´÷ěŮł6lćĎźŹĺË—ŁE‹(,,ÔŮžbîÜą°łłCnn.|||СC8;;ăÜąsŞú‹ŠŠŕďsç"((Şú5Q*•ŤŤ…ŹŹFŹ]«şę›YÝřkĘSXXU«VaćĚ™đőőĹ1c©z_ă¦îŘRÚ‡yó桰°PŐ–ĘźóňňđŃGaٲeđ÷÷‡««+üýý‘››+9—¶ó@W»µÍ9)őkc©ő?K:uꄯľú ‹-‚Bˇ0tśfCˇóŐ·oßgöř íĉ€m۶áÁŞň˙ýďXşt)ľřâ U™ş9ś••…ĐĐPüç?˙ąąąj[MĺDDDDÍŽ """""""""""˘Fsřđa!“ÉDfff˝ęńôôžžžzJEDôl bbb¬ţQŰ?łrąĽJ™\.ÄŠ+DFF†‹‹ÄđáĂUŰĽűî»"%%EőóË/ż,lllD^^žBŔŔ@@\ąrEµÍÍ›7Ĺ믿®5Oź>}DďŢ˝…B”•• KKKáää¤3łş˛ęDűöíU?Oź>]äääčlĎíŰ·E۶m*nܸ!ľřâ @ :T!Dyyą:t¨x÷ÝwUuüúëŻÂÄÄDçÜĽyłJ~©uŐ7ł¦~«ž§¬¬LŚ3FĚś9S(•J!„;věġC‡„u7]}!µęęĎĎĎ"88XU–““#„˝˝˝ČÍÍŐšEĘy «ÝÚćś>Î3mő×GcüWźëazzşX»v­žŐÍÄ‚ „˝˝˝hŐŞ•x饗„———Řľ}»(--U»Oĺ.++kÖ¬QťÓ•4•×˙=FDDDM…L!g١C‡Ř´iŢyçť:×ăĺ卍ŐW4"˘fO&“!&&Ó¦Mkú8€éÓ§Ł6f—ÉdËĺHNNV•9::"%%JĄ2™ СCˇ°°‰‰‰:t¨Úú> 777<|ř={öÄoĽO?ý°fÍ8;;ĂÍÍMcžŤ7˘sçÎxăŤ7 „@ź>}póćM”––jͬ®¬şNť:áŢ˝{ŚŚÄ‚ pőęUtďŢÉÉÉ:Ű#—Ë‘ššZĄommm‘››‹ââbDGGcţüůHNN†\.WmÓ·o_\»vMç<ť_J]RĆ@WfmýötůĆŤáçç‡ÔÔT888***°gĎL™2–––u7]}@R;ÔíĐĐPdeeÁÖÖVµÝž={ŕííŤ%K– <<\c]ç {ľjšsćććz9Ď´Ő_Ťń{\C_‰žwü÷5F†@DDDDDDDDDDDô>>°´´аăV×vśůçÎťĂ!C`ll¬µî¤¤$8;;ĂŢŢh۶­ŢrOť:.\€««+Ξ=‹QŁFa×®]’ŰŁMff&€'}S_RęŇGf©îŢ˝ HKKÓ¸MCŽ[]U.žČČȨRncc°°°$''×xIĄ«ÝšćśRƸ>ő5. """""""""""jdîîî(((Ŕ˙ű_CG!"˘&“ÉP^^^«}är9 ÂĂĂ«”'''cóćÍŞź­­­1oŢ2ŇĆŔ€€ĐĐP!Tĺ7nÜŔż˙ýo ;nuUů€ęýxëÖ-Ŕ„ ę} ]íÖ4ç¤2ƺꯨ¨¨OóęÍÄĐž7vvvprrB\\\˝n”űňË/!“ÉôŚô­wďŢČĘĘ­[·`ggČËËäççĂÜÜđřńc@aa!&Ož {{{„„„ŕöíŰ?~<’““‘/żü˛JýţţţŠŠÂÍ›7Ń»woťy˛˛˛——‡ďż˙÷îÝCnn. 11]ştµµ5 ¸¸XµOQQQŤ2uÖŻ_ŹE‹ÁŇŇžžžxď˝÷еkWIíQWw~~> ĽĽ‹/FLL ĐŁG¸¸¸ !!wîÜđäŰé{öě©6WAA€ßű]J]úČlbb˘vü«çYşt)öîÝ‹ŘŘX>Ë–-——:věżüĺ/8p ţđ‡? ==Ý»w‡‘‘QŤLaaa€;wî`ăĆŤ3gޤşô‘ůéń·łłÓ'!!~ř!‘šš ///¬]»Vµčݶă¦nŚÔ»´´Tg;¦NťŠüăŞúCBB0eĘôďß§OźĆĘ•+1kÖ,8;;ĂŘŘÖÖÖŹŹ‡‰‰ú[R„’ĎmíţüóĎŐÎą¨¨(¬_ż^/癦9 mÚ´A»ví4¶ł)ظq#bcc Co fffNB$$$`ذa†ŽADDD™xúsDDDDDDDDDDDDÔ(Nž<‰‘#GâęŐ«ptt4t""’ŕŔ>}:šŇźŮ  €_~ů­[·6t"’ rAgCި˙,-ÍÍÍEjj*nßľ {{{Ľřâ‹†Ž¤wŮŮŮďŹč_ IDAT8ţ<^yĺCGˇZ>|8üüü žsMwy2Ń3lřđáčÔ©:ÄĹDDTgŃŃŃX°`QĎÂNś8đđp?~NNNXµjţüç?ŁE‹†Ž¦w•‹Íž…q#"""˘Će¤{""""""""""""Ň7###ĽňĘ+8t衣Q3sćĚ 0}úôÁÖ­[ńŢ{ď:‘^(•J:tÇǨQŁđčŃ#|űí·¸xń"Ľ˝˝źÉ…DDDDDőÁĹDDDDDDDDDDDDâîîŽÓ§Oăţýű†ŽBDDÍ™™ňňň`dd„}űöˇeË–†ŽDDT/ĄĄĄŘ˝{7śśś0eĘtčЧNťÂ‰'ŕîî™LfčDDDDDM’‰ˇ=Ż\]]abb‚ďľűŢŢކŽCDDÍ„““~űí7CÇ "Ş·üü||ţůçX·nîÝ»‡éӧ㫯ľ‚ŁŁŁˇŁ5 |2‘´mŰŁGŹĆˇC‡ …¨Ńäää 88=zô@`` ¦NťŠ_ý»wďćB""""˘Zŕ“ ČÝÝË–-CII LMM ‡¨Á¤§§#22Ű·o‡ąą9.\___XYY:QłÄ'‡‡ ńÓO?: Q8ţ<Ľ˝˝áŕŕ€Ă‡cőęŐČČČ@pp0ŐP÷îÝáěěŚC‡: ‘^ť8qîîîřă˙K—.áóĎ?Gjj*|}}ŃşukCÇ#""""jöL €čyçîîŽ/ľř›6m2t""’ŔËËËШKHHŔ°aĂ ŁÁ(•JÄĹĹaĺĘ•HJJÂ#pđŕA¸»»:Ń3‡O """""""""""20ܸq/^4t""ŇÂÎΞžž†ŽADÍܰaĂ0|řpCÇĐ»’’ěŢ˝ŽŽŽ2e lllpćĚŐÓH˙řd""""""""""""±±±†ŽADÔ¤äĺĺaÇŽÇÇ1mÚ4:t†ŽFDDDDôĚă“ L&“ÁÍÍ ß~ű­ˇŁI’ťťŤŕŕ`tďŢË—/‡——ŇÓÓ±{÷n. """"j$\ @DDDDDDDDDDDÔLž<˙űß˙pëÖ-CG!""""ŇčňĺËđööF÷îÝń÷ż˙|đnŢĽ‰ČČHtéŇĹĐńž+\ @DDDDDDDDDDDÔŚ7m۶šC‡ …¨†'NŔÝÝýű÷ÇéÓ§±nÝ:üöŰo†………ˇă=—¸€¨ 055…««+>ąąą†ŽBDDDDϱ˛˛2ěŢ˝NNN™™,X€… ˘}űö†ŽFDDDDDđÉDDDDDDDDDDDDMČÔ©S‘——‡ďż˙ŢĐQčuńâEx{{ĂÁÁDXX222Ě…DDDDDÍ5!ÝşuðaĂkč(DDDDôŚ9qâÜÝÝńâ‹/ââĹ‹řÇ?ţ´´4řúú˘M›6†ŽGDDDDDµÄĹDDDDDDDDDDDDMŚ——ľýö[”””: Ń˙cďÎăŞ,ó˙ŹżŹl*˘(™űččX™¦5¸L©}KMGĐI­0łAQ)Íu¬ÜĆLÜM5´š6ťĄÜ—É5-KqWÔD6Eŕ\ż?úy’ŘĽ_ĎÇGžë^®÷}Ý×ÁÓĂűs.@9g·ŰŁ:( @—/_Öşuë´gĎËŮŮŮꊉb€2&((HIIIúňË/­Ž€ręúő늊ŠRË–-Ő§OŐ®][[·nu¬`łŮ¬ŽŕQ PĆ4hĐ@;vÔÚµk­Ž€r&))I‘‘‘jÖ¬™† ¦öíŰëŕÁŽŐT¬óPiҤIşvíš*W®lu”qçĎź×âĹ‹©¬¬,=óĚ37nś4h`u4Ą„•Ę ţýű+%%E_~ůĄŐQP†ýř㏠SÓ¦MµdÉ………éĉŠŚŚ¤¨ŕ((ęŐ«§Ž;jíÚµVG@ôż˙ýOÁÁÁjŢĽąâââ4cĆ ;vLS¦L‘§§§ŐńÜ”QAAAZż~˝®]»fu”›6mRŻ^˝Ô¶m[8p@Ë–-Ówß}§°°0U©RĹęxn#Šʨţýű+%%E±±±VG€…ěv»bbbôŔ( @—/_Öúőë«899Y€((ŁęŐ«§GyD+W®´: ,ššŞČČH5kÖL}úôQ˝ző´cÇÇęîl”a Ň?˙ůO]ştÉę(¸M.^Ľ¨)S¦¨I“&?~Ľ:wî¬Ă‡+&&FíÚµł:€2‚b€2¬_ż~ruuŐÚµk­Ž€Rݰ°05iŇDsçÎŐ AtěŘ1EEEÉ×××ęxĘŠĘ0wwwőîÝ[˙űß­Ž€R˛˙~Ë××WëÖ­Ó´iÓtúôiEFFŞ^˝zVÇPFQ PĆ 4H[¶lŃŃŁG­Ž€´iÓ&őęŐKmÚ´Ńž={ô·żýMGŽQXXÜÝÝ­Ž ŚŁ ŚëŢ˝»îľűn­\ąŇę(¸Ev»]111z衇 Ë—/kÝşuÚ»wŻ‚ĺââbuDĺĹeśłłł  ÷ßßę((¦ëׯ+**J­ZµRź>}äĺĺĄÍ›7;V°ŮlVGPÎP P<ýôÓ:rävîÜiuArr˛"##ĺăăŁaÆéĐţýű«@q9[k×®ťZ´hˇĺË—«]»vVÇ@´hŃ"Í›7OzöŮgőŇK/©QŁFVGPA°2@91tčP­ZµJiiiVG@Ž=ް°05mÚT‹/Ö¨QŁtüřqEFFR DQ PN 2Diiiúä“O¬Ž€_ŮłgŹ‚ĺçç§Mź>]ńńńš2eŠjŐŞeu<ĹĺDť:uôřăŹkٲe9¶­\ąRîîî˛Ůlš9s¦˛˛˛$I«V­’›››V¬X!IşvíšfÍšĄµk×NÝşuÓçٵk—:tč ĐĐPMš4I...JMM˝=PmÚ´I˝zőŇ}÷ݧýű÷kٲeúţűď¦*UŞęëׯ×đáĂŐ¨Q#%&&jčСşë®»Ôşukí޽۱_RR’ĆŹŻ &(<<\=zôPxx¸KëňpńyEĺluŢ3Ď<Łľ}űęÇ”ŹŹŹŁ}Đ A:|ř°Ţxă őęŐKNNN’¤€€=ńÄ2d$iÔ¨Q WóćÍ%I=zôĐŁŹ>Ş#GŽČĂĂC ŇĹ‹µmŰ6IŇ÷߯´´4ą»»ßć+(»ěv»âââôúëŻkçÎťzřᇵ~ýzőěŮS6›­Čçk۶­ ¤””-Z´HS§NU·nÝôôÓOëOú“¶mۦ””µk×NÔäÉ“%I.\Đď~÷;}ňÉ'úß˙ţ§5j”ôĄâ6;v,źçP$¬ PŽ<ńÄŞ[·®˘˘˘rl3fŚ<<<4wî\GŰĘ•+őÜsĎI’věءĄK—Ęßß_6›M6›M_|ń…Îź?ŻŻľúJ’tůňeýôÓOš7ožŚ1ŠPĺĘ•oĎĹ”qéé銊ŠR‹-Ô§OŐ©SGŰ·ow¬PśBIjĐ 4h IzőŐWŐ¸qc 4HuęÔŃž={$I3fĚĐ÷߯áÇ;Ž«]»¶&Nś¨ŁGŹjÚ´i·~°źçPT”#ÎÎÎ’···BCC5|řpU®\ąÔűnÖ¬™âăăuóŁ< 6ÔéÓ§•‘‘ˇŚŚ µoß^‰‰‰Úąs§ęÖ­+I=z´víÚĄ˙üç?rvv.őś(yk֬р÷žĎó(,V(‡žzę)U­ZUË—/ϱ­Nť:ęÖ­›ž{îąlínnnú׿ţĄŢ˝{ë“O>Qxx¸´rĺJyxxH’ŇŇŇôČ#ŹhćĚ™:t¨ôÁÜ–k°‚1F111úÝď~§€€=zTË—/×áÇv[ -Z¤řřxIŇ›oľ©¤¤$EFFęôéÓ’¤Ůl6mÝşUÔ!CôŇK/iüřńňňňŇżţő/ *>Ď °X ś5j”âââtäČUŞôËw@ĄĄĄ©M›6Ú·oźŞT©baB€˛+##C«WŻÖ¬YłtđŕA=üđĂ?~Ľzőęeu4Üa~˝2źçPX¬ PNŤ1BÇŽÓ† ˛µ/\¸P#GŽäÁ!€\¤¤¤(22R>>> Ńý÷߯ýű÷kÓ¦M Lŕó< ‹őÁĘ©-Z( @K–,QŤ5ôüóĎ+--MYYY:|ř°ŐńĘ” .háÂ…š?ľŇÓÓőÜsĎ)<<\Ť7¶: IjÓ¦ źçP$¬ PŽ >\111JMMURR’*UޤU«VÉŐŐŐęhe±cǦ¦M›:ľqýřńăŠŚŚ¤e źçPT¬ PŽjĚ1úúëŻuěŘ1«ă”{÷îŐ[o˝ĄŐ«W«aÆš6mš† ¦ŞU«Z ČźçPT¬ PŽąşşę™gžŃ»ďľ«ĚĚL«ăXnÓ¦MęŐ«—î»ď>íÝ»WűŰßtäČ………Q Bˇ ś{ţůçućĚĹĹĹYŔv»]111ęرŁtůňe­[·N{öěQpp°śťť­Ž%Žb€rÎŰŰ[Ý»wעE‹¬Žp[]ż~]QQQúÍo~Ł>}úč®»îŇÖ­[«Řl6«#@©ˇ  Ő—_~©C‡Y Ô%''+22RŢŢŢ6lÚµk§(&&F:t°:ÜTŹ?ţ¸üüü´`Á«Ł”šóçĎkĘ”)jܸ±"""ÔŻ_?=zTQQQjѢ…Őńජ °ŮlzńĹőŢ{ďé§ź~˛:@‰úńǦ¦M›jńâĹ ÓńăÇ© X,A1@ńĚ3ĎČĹĹEË—/·: @‰řß˙ţ§ŕŕ`5oŢ\±±±š1c†âăă5eĘŐ¬YÓęx`)Š* 2DóçĎWVV–ŐqŠmÓ¦MęŐ«—Ú¶m«hٲeúţűď¦*UŞXĘŠ*‘#GęäÉ“ŠŤŤµ: @‘ŘívĹÄĨ]»v ĐĺË—µ~ýzÇęNNNVG€2…b€ äž{îŃcŹ=¦yóćY PŇÓÓ%őéÓGuëÖŐŽ;«rçlu”¬QŁFé±ÇÓľ}űôŰßţÖę8ąşxń˘,X  (%%Eýű÷Wll¬üüü¬Žĺ+T0Ý»w—żżżćĎźou€Ž?®°°05mÚTożý¶ ¤cÇŽ)**ŠB(Š*›Í¦ŃŁGëý÷ß׹s笎 IÚżż‚ĺëë«5kÖ襗^҉'©zőęYĘŠ* !C†ČÓÓS‹-˛: ¨€öěŮŁŃŁGjßM›6©WŻ^jÓ¦Ťľůć˝űî»:qΩL™˘5j”rR¨¸(¨€*W®¬^xA .TjjŞŐq@˛k×.uîÜY ,бcÇrÝÇn·+&&F=ôtůňe­[·NűöíSpp°\\\nsj¨x(¨ ţô§?éęŐ«Z±b…ŐQ@±mŰ6uíÚUiiiŞT©’Ţzë­lŰŻ_ż®¨¨(µnÝZO>ů¤ĽĽĽ´yófÇę6›Í˘äPńP PAŐ®][ÁÁÁzë­·”••euPÎmÚ´IŹ<ň®]»¦ĚĚLeddčÝwßŐ… ”’’˘ČČHůřř($$Dm۶Ő«JĹŘرcŻuëÖY”c_}ő•şwď®ôôteff:Úł˛˛ô‡?üA 6TDD„  cÇŽ)**J-[¶´01T|T`~~~ęŐ«—fÎśiuPN}ţůçęÖ­›ŇÓÓs¬6”™™©íŰ·kôčŃ:~ü¸fĎž­ X”î,TpáááÚ±c‡¶lŮbuPÎüóź˙TŻ^˝”™™)»Ýžë>ĆŐ¬YS5kÖĽÍéŕÎF1@ öíŰköěŮVGĺHll¬úô飬¬¬< ¤źW>}ş®_ż~Ó(¸Ś7NëÖ­ÓˇC‡¬ŽĘččhőéÓ'ßn– čččŰ pĹw€ľ}űĘßß_łfͲ: (ăŢ˙} 8Pv»]Ć<÷srr’›››\\\dŚŃĚ™3óÝP˛l†˙ ¸#,_ľ\Çב#GÔ¤I«ăČGPPŐܡŽ;¦Ý»wK’l6[އű]]]ĺć榪U«ŞJ•*ŞRĄŠÜÜÜTĄJU®\YµjŐ’Ífł"z®Ö®]ku @k֬р(¦@‘Q p‡ČČČŻŻŻz÷î­yóćY@>l6›:tč † ZŔäâĹ‹úî»ďTąrĺůßx]©R%«cĘ©S§´mŰ6®Fą@1ŠËŮę¸=\\\4věXŤ?^ŻľúŞęÖ­ku$ů3fŚú÷ďou (—n<\ Yů(×@‰ QőęŐ5ţ|«ŁnĹwŞU«jÔ¨QZ°`­Ž(&Šî0#GŽTĄJ•´dÉ«ŁŠ‰b€;LőęŐő /hîÜąJKKł: (¸Ť=ZÉÉÉúë_˙ju@1P pŞS§Ž†®™3gęęŐ«VÇ€]ąrĹęIIIVG@U–ćiQ”×Üp§Łŕ5nÜ8]ąrEűŰ߬ŽŔ"GŽQPPl6›l6›:uꤸ¸¸lűĽóÎ;jذˇ<<<4jÔ(Ą§§ß¶|éééš6mšzč!yyy•čąSSSŐ´iS}öŮg…>föěŮęÜąs‰gAůVšó´4•µ÷ č(¸CŐ­[WÆ Ó´iÓtíÚ5«ă°€ŻŻŻÖ¬YŁž={J’\]]őÄOdŰgčСrvvÖ| yóćÉÍÍí¶ĺsssÓرcőÝwß)++«DĎíââ˘S§N)--ÍŃvęÔ©|Ź9r¤<¨ĚĚĚÍ‚ň­4çii*kď/@ŃQ p{ĺ•W”¨ĺË—[€El6›Ţ˙}ůřřhăĆŤZłfM¶íÓ¦MSHHHŽ"ŰĄrĺĘşűî»KüĽ®®®ňööVóćÍ%Ińńń8p`ľÇ¸ąą©víÚ%žĺ_iÍÓŇV–Ţ_€˘ŁŕVŻ^==űěłš>}ş®_żnuńôôTtt´\\\Ş‹/J’>¬/ľřBŻĽňŠĹ KGË–-uĎ=÷čôéÓęŮł§.\¸`u$ ÂŕýĄŹb€;Üřńă• ÷Ţ{Ďę(,Ô¶m[Mś8Q.\Phh¨Ś1 Ő’%Käěěśmߤ¤$Ť?^&LPxx¸zôčˇđđp%&&J’–.]ŞJ•*ÉfłI’’““5gÎślmyązőŞÂĂĂ5|řpEDDčŐW_Ujjj‘ú_ż~˝†®FŤ)11QC‡Ő]wÝĄÖ­[k÷îÝŽóüéO’›››Ţ{ď=}şŮ°a 1)))ĆĎĎĎÔŞUËqÜ€LBBB©ŤSaći^ ß;ýýU’˘ŁŁ Ľ@YÁ|@qń)&>>޸şşšwß}×ę(ŚuĹĆóí·ßI¦nÝşą˝öÚkF’9{öl¶ö¨¨(#ÉŚ7ÎóËCó7Ë­íf ,0’̡C‡˛µűůů9Ž+l˙Í›7ĎŃWť:uŚ››[®}µŔn·;ÚĽĽĽLŐŞUŻçĚ™cVŻ^mŚ1Ćn·ăâââŘ~ýúuÓ¸qcÓ»woG[DD„ůć›oŚ1ĆlßľÝHĘő'666Ďlő[1ąté’ńđđČöŔřôéÓóí×_îQjjŞŁmîÜąF’ůă˙Xŕ5ćšoä˙é§ź˛ő]»vm#ÉDFF»Ýn8oŃÄ­ŽSaći^ ß;ýýU’x¸ĺ óĹU©čk  ˘iҤ‰žyć˝ńĆş~ýşŐqX¨E‹’$OOOąşşćŘľyófI’‡‡G¶öNť:I’¶lŮRěľżřâ IRÓ¦MłµWŞôË?m¶›Í–ăü5kÖTzzz±óÝěćó×®][iiiŽ×cĆŚQŻ^˝´hŃ"˝ůć›JOOWFF†c»‹‹‹Â«ŁGŹ*##Cß}÷ťî˝÷^IŇÎť;ŐŞU+™źżŕ/ŰĎO<‘g¦‚ú-ĚÔŞUK#GŽÔŠ+tćĚIŇĆŤőŘcŹĺ;7îQŐŞUm˝{÷–$9r¤Ŕk*Ě5ßČ_łfÍl}/^ĽX Sűöí•’’’c~”ä8fžćĄ ńĺýT|™™™ş|ůr¶źÔÔTIĘŃžhqZ”u@’4iŇ$ť?^ďľű®ŐQ”a7ŽŹŹĎÖ^§NIRŤ5Š}îÓ§OK’.]şdI˙%eçÎťjÝşµĽ˝˝5qâDU«V-Ç>!!!rwwׂ ôÉ'ź(00бíŇĄK:zôh¶ěvű-ő[cÇŽ•«««ćÎť«Ý»w«}űörrr*ňyęׯ/IjÔ¨Q×TÜk–¤~ýúiĎž=ęŃŁ‡víÚĄ€€­X±"Ďýouś 3Oó“ßřňţ*ľÄÄDÝ}÷ÝŞU«–ăçŮgꕤlmµjŐR˙ţý-N €˛ŽbHúůˇÍaÆéŤ7ŢĐŐ«W­Ž Śşń áqqqŮÚOž<)IzôŃG%ýňÍá7V1ĆčĘ•+ůžŰßß?×s§˙˘˛ŮlĘĚĚ,Ö±ż¬ŚŚ Ç·˝çö0{őęŐ˘eË–)::Z}űöuló÷÷WZZšfÎś™íC‡iÁ‚·ÔoaxyyiÄZ˛d‰ćÍ›çxHµ¨n˙üó\Ŕ~ůĺ—ĺěě¬W_}Uźţą®^˝Ş˙űß:sćŚ¤źż­Ľ°ý_»v-Çů“““%)ׇ’}||töěYÇCĎyIJJĘv.Iއ°oŚÝŮłguúôi}ůĺ—Zµj•%I;věĐ©S§ÇŤ5J)))şďľűäěěěhňÉ'ĺíí­©S§ęąçžÓŞU«ˇŃŁGë™gžÉ3[AýeLÂĂĂuýúuť8qB>>>ůŽÉͲ˛˛ްaÚ¶m«áÇxM…ąćůoŚó łgĎv\k`` jÔ¨ˇ ”Ú8fž$Żń˝Óß_Ŕťâé§ź.pýḠiPž9M™2eŠŐ!P6T«VMW®\Ńüůó5bÄąąąY ¸#ýůĎVPP~ó›ßÜÖ~?ţřc˝ýöŰÚ»wŻ®\ą˘´´4ŮívÝsĎ=Ž}\\\¬Ë—/kÉ’%Ú»wŻ6nÜ(OOO-]şT...’¤űďż_;věĐşuë´˙~Ť=Z[·nUçÎťŐ¸qc5oŢ\NNNŮúŻ[·®şvíŞ˝{÷jÁ‚Z±b…ęÖ­«äädýß˙ýźęׯŻ{îąGC† É·˙E‹iŐŞUŽĽ÷Ţ{Ż–,Y˘µk×J’222ô»ßý.ŰřçÎťÓˇC‡tď˝÷ŞE‹9ĆĆŁ·ŢzKü±$éęŐ« Pdd¤ţńŹH’ŇÓÓŐĄKŐ¬YS›6mŇľ}ű4hĐ y{{kűöí:qâ„UĄJI’§§§._ľ¬đđpG›$9;;«Oź>:zô¨ľřâ mܸQ 6ÔÂ… UłfÍ<ď_őęŐóě7!!Á‘ł0cR­Z5mßľ]ÖoűŰçÎÂ… uńâEUŻ^]Í›7WjjŞľţúk-^ĽX•+W.đšňŰîćć¦3f8ň_ĽxQŤ5RÝşu%I'NÔ—_~©ÄÄDýőŻUť:u´lŮ2yzz–Ę8őë×OÝşuËwž6iŇÄńíýąÉk|ďÔ÷Wi8xđ >üđCńX Ę"˝őÖ[y®áě쬞={jČ!·9Ę›1ĆXeÇĄK—äíí­W^yE&L°:pG˛ŮlŠŽŽV˙ţý­Ž‚;TZZšÚ´iŁ}űöe+TČK‹-třđańBáu|Qtk֬р“(ł‚‚‚´nÝ:eddäŘfłŮ´víZőë×Ď‚d(O*Ye‹———ÂÂÂ4kÖ,%&&Z`… jäČ‘<¨^J_ Ęse€*UŞčńǿ͉P9Ľ î4áááZ¸pˇćÎť«)S¦Xplßľ]Ď?˙ĽŇŇŇ”••ĄĂ‡úŘÔÔTÇÝÝÝK+bąv+ă  âyüńÇU­Z5%''gkwqqQ`` ĹB(V@5jÔPxx¸Ţ~űm]ştÉę8€ŰŔÝÝ]IIIŞT©’V­Z%WW׏IMMŐkŻ˝¦“'OJ’FŤĄm۶•vÔr©8ă  âruuUPPPŽß4hE©PŢŘŚ1Ćę({RRRtĎ=÷č©§žŇŰożmuŕŽbłŮ­ţýű[ĘĄ5kÖhŔ€â±”e7nÔŁŹ>š­ÍÓÓS.\łłłE©Pž°2rU­Z5MžC‡ŐôéÓőÁäŘf•ÜîçÍó–ßů›3g޶nÝju TU«V•$íÜąSAAA§AE0věXuěŘŃęŕ6`e( @˝zőŇË/ż,»Ýnu%ŔÍÍMµk×¶:F…ŻZŁÄ­^˝ZS§Nµ:F™dłŮôţűďËÇÇG7nÔš5k˛mź6mšBBBĘL!€”ó~ţzŢň{"[·nŐ¶mŰ¬Ž  qăĆŞ^˝şjÔ¨auT~řˇNž¶nÝZü±cż‚Λ×ö‚ňîÚµK:tPhh¨&Mš$Ą¦¦ć{_ňҶm[Mś8Q.\Phh¨Ś1 Ő’%Käěśý; š…ą7yÉo¬~}?ĄĽçí ůýž(î¸ř…(ŇéÓ§MŐŞUÍěŮł­ŽTh’Lttt©÷ăďďo$™É“'›řřxg$™Ž;:öńőő5>>>Ćc222ڧ§§iŐŞ•cűüůóŤ$óé§ź:ÚNś8ačx=lŘ0sřđaÇëîÝ»›:uꤤ¤<łÔon^|ńESąresĺĘcŚ1WŻ^5uęÔ1O?ý´cźĚĚLÓ©S§B÷3qâD#É}zľŮŤÉůPőkŻ˝f$™łgĎfŰ/**ĘH2ăĆŤËó\ůeś3gŽ‘dľűî;Çţ™™™fůňĺćňĺËfÉ’%F’Ů»wo¶s6oŢ<ŰŇ7ćÂÍrkűőuÝ8ĎO?ýäh+î=÷óó3’LjjŞŁmîÜąF’ůă˙Xŕy ÓonyŤ1¦víÚF’‰ŚŚ4v»Ý8p ßb•¸1ëÖ­kŇÓÓsl/ěś(콹YaďAQŠňú=q+ă^š(p§˘€;KĄB/!Hš0a‚\\\4sćL«Ł(!6›ÍńçÚµk+--ÍńzĚ1ęŐ«—-Z¤7ß|SéééĘČČplwqqQXXbccuôčQeddč»ďľÓ˝÷Ţ+IÚąs§Zµj%óó—Őeűyâ‰'ňĚTPżąiѢ…~˙űßëťwŢ‘$?~\YYYş~ýşVŻ^-IŠŠŠŇÓO?]č~jŐŞĄ‘#GjĹŠ:sćŚ$iăĆŤzě±Ç ×›mŢĽY’äáá‘­˝S§N’¤-[¶äyl~˙óź˙H’6lčŘßÉÉIC‡•§§§>űě3I’ŻŻo¶sŢ|ĎoĹŤóÔ¬YÓŃVÜ{^©ŇĎŹ0T­ZŐŃÖ»woIŇ‘#G úČŃ–śślĆŤgşwďnÂĂĂ͸qăĚÔ©SMzzzľç*(ăţýűMŹ=LÍš5M ĚčŃŁÍ•+WÇŻX±Âřúú///Ó«W/sčĐ!ăďďon~dŕűďż7>ř qww7Ý»w7ß˙˝ 06|đą|ů˛™0a‚c~ĽńĆćĺ—_vĽ~ţůçÍ7ß|ă8_qîůŤLłgĎ6/^4 fĆŚ&%%ĄĐçÍk{jjŞ™:ujžy%™űďżßĚ1Ă 4ČôěŮÓ;vĚcĚŰożmš4ibNť:•oţ›}ôŃGfČ!Žţ^~ůeóŮgźĺŘŻ0s˘ {“×üÉo¬RSSłÝĎ9s椤¤ló¶(ż'Š;îĄ)::Ú”öc1&00°HÇś>}ÚH2Íš5s´Ůívc|||ŚŻŻŻ9xđ`IGEtňäIÇźŻ]»fĽĽĽJ}Îć%##Ă<ôĐCfĆŚŽźŮłg›„„„|Ź+Î|>s挑dj×®ťë9k×®m$™3gÎcډ‹‹3]»v5©©©Ž}$ÇëK—.___sôčŃ"_ű­¸zőŞ©U«V‰Ý·łoß>Çëůóç;ţ^,ŠŇţ Ę›17­ÁţóuíÚU6lĐ#Ź’»»»{ě±|Źűí·ŐŁGµlٲČ×p+Jňľ5jÔHď˝÷žă˙«’’’TŁF =řŕÚ¶m[ˇĎĂçHî,• ŢČ]—.]ÔŁGŤ?^v»Ýę8Pj.\¨‘#GVBeS˝zőôúëŻëÇÔ[o˝eu”’Ó§O«gĎžşpá‚ŐQ$IĆÍś9SăÇŹW÷îÝ5yňdĹÇÇßňyKj>?ţřăęÖ­[ű˝řâ‹ňőő-v?eÝn×?ţńÇë‹/JúąH /ŕ–Ě™3GűöíÓňĺË­Ž%jűöíjÓ¦Ť|}}µxńb˝đ VG*ĆýôÓO’¤ĚĚL‹Óü"555Ű;Q`` śśśôĹ_8Ú’’’4~üxM0AáááęŃŁ‡ÂĂĂ•çyV®\)wwwŮl6Íś9SYYY’¤U«VÉÍÍM+V¬(Ôą—.]ŞJ•*ÉfłI’’““5gÎślmyٵk—:tč ĐĐPMš4I...Ž÷÷µk×4kÖ,…„„¨]»vęÖ­›8ŕ8¶ íż–––¦•+WjŕŔzřᇵmŰ6Ý˙ýjÚ´©6oެďż˙^}űöUíÚµŐ˘E íŢ˝;ŰńůŤÝn×˙ű_Ť3FÍš5Ó™3gÔĄK5iŇD‰‰‰EÎúŢ{ďéŕÁ:wî\®ßÄáÂĘËËK­ZµŇ®]»ŰŽ9˘   ˝ňĘ+ V§Nť´˙~IŇúőë5|řp5jÔH‰‰‰:t¨îşë.µnÝ:ÇőţúÚ{ôčˇ:hëÖ­š:uŞüýýőúëŻçyLaĺ6ź‹ŞJ•*rrr*p?777ą¸¸äh/ě¸ć}vőęU…‡‡křđáŠĐ«ŻľšăflćC~ď‹Ď?˙\&LČ–ÝŮŮY…0pç1Ŕ- 5wß}·ILL´: P!H2ŃŃŃVǸăíßżß4mÚÔřůů™­[·Z§D:tČ„……IF’ 7)))–fJII1ŻľúŞ#ÓłĎ>[aĆÖ‰ŽŽ6ĄýXL``  ,ňq’ŚżżžŰëŐ«gĽĽĽŚ1Ć$''???3eĘÇö„„ăççgĽ˝˝óýě5qâD#ÉěŘÖ˝{wS§N“””T¨íżf·ŰÍ?ü`$™5j¸¸8óí·ßI¦iÓ¦ć/ů‹ąrĺŠůć›oŚ$ÓĄKDZŤCBB‚ٲe‹©ZµŞ‘d¦Oźn6lŘ`BBBLJJJ‘ł“űđ÷÷7’ĚäÉ“M||Ľ‰‹‹3’LÇŽűřúúcŚ1ĆÓÓÓ´jŐĘcĚ©S§LµjŐŚ$óć›ošăÇŹ›ż˙ýďF’yđÁóĚrł+W®7ß|Ó8;;IćÝwß-đ˘Ě盏)čçVúĽYaĆĄ0ď…Ě˙ÇŢť‡×|%~_‰‰-Ń4Tб]EűSZ{1Ś(]URm‘j©%-QŞMĄjWÚ™ŞÝ«‹Úбf»Č*ëýýáÉ­¬÷fsĎëy<3ů.ç|Îrďdžśóý¦§›Z·nm6lůš“'Ošű*‹ĄůPĐçâV©©©¦ >˙üs«Úy+ý)""ro1L&S)ď7‘»Üµk×hÔ¨/ľř"łg϶u‘;žÁ` $$„~ýúŮ:ŠČiíÚµôďßźŇ\ăíí @hhhˇî3 ŤF"##ó<_§N222ŽŽ& €ŔŔ@bbbpuu5_łrĺJ|||?~<ÁÁÁy–sőęUÜÝÝ0`Ë–-`ĆŚ4kÖ ///«ËnҤ GŽÉÖ—yËéŕŇĄKĚ›7ŹQŁFqřđaęÔ©Cdd$­[·Îóž~řęŐ«xŢËË+ß:sömíÚµ‰ŽŽÎ–łFŤ¤¦¦ríÚ5«űÁh4rôčQ®^˝Šłł3aaaEĘš×ČęÓĚĚLó[ŞU«Frr˛ůÉńsçÎĺÁdŔ€L&6lČéÓ§IMM0gĽµ˝®®®ć7XkٲeřúúňČ#ŹřVüÚr«[çł5÷X3·,Ő™“Ą~±fÔ©S‡‘#G‰Ńh4_Ó¸qcŽ;†Éd˛j>ä÷ąpttĚvýG}Drr2cĆŚ±ŞŤ·Ňď‘"""÷–r¶ """"""""""""w>gggŢ}÷]ćĎźĎŃŁGmGDDD䎔––Ć… hѢŰ·oȵP¸C‡ěر#߲îż˙~FŤĹŠ+8wî7n¤GŹĹ.ŰK–,ÁŃёѣGóŘcŹ‘€ŁŁ#áááxzzb2™rýóňň˛xľ0r¶ nöKll¬ůgkű!k~ÖF DłfÉŞ zőę$%%™;v,O=ő‹/&00””ŇŇŇňĽ7‹łł3)))…Ę0tčP*V¬Č±cÇŠĐ‚żĺśĎ¶b©_¬™6lŔÝÝ=Ű5ĺĘý˝üΚůßç"§“'Oi#€Ü{´@DDDDDDDDDDDDJ„ŻŻ/Mš4aÔ¨Q¶Ž""""rGÚ´i©©©téŇř{ˇń©S§˛]WŁF ŞV­Z`yăĆŤă˙ř~ř!{÷îĺ±ÇĂÎήDʶäąçžc˙ţýtďŢť={öĐľ}{V¬XÁ•+WŠŠĘ¶Č=Kff¦Ĺó%­8ýp»ł†‡‡Ó¬Y3<<< J•*%^Üě“űďżź «śśóଞfd˝ŮŕĘ•+ů–cÍ|Čďsq«äädZ¶lYäöČ˝E›DDDDDDDDDDDD¤DŘŮŮńá‡ň믿ňăŹ?Ú:Ž”1ׯ_·uâââlˇÄĄĄĄ™źę,w}/d—ššŠżż?-[¶äŤ7Ţţ~2ůúőëł]{ćĚşvíZ`™...Ś1‚ĄK—2ţ|^~ůeó9kËÎzŞzjj*&“ÉŞ±›2e üüóϬ^˝š´´40Ť$%%śíúČČH.\hń|I+N5«Á` ==˝ĐY}||HKK3żÝˇ46ś;wŽsçÎáíí]ä2ňšĎÖ(­6Äš9`4óĽćVÖ̇ü>·ŞX±"Ď?˙|ń%"""÷ m‘Óąsgž{î9FŹMJJŠ­ăŤĄ¤¤ÄO<‹‹‹­ăÜŐÚ´iĂřńăó=?{öl:věxWŤĂµk×đ÷÷ÇŮŮ™víÚŮ:ŽXé^˙^HNNŕĆŤŮŽďŰ·ŹnÝşqíÚ5ľřâ ěíí?~<žžž,X°€óçĎ›Ż_´hm۶eäČ‘ëôóó#55•Ó§OSż~}óqkËÎZýţűďsâÄ ćÍ›gţ=ď—_~ÉwńöěŮł‰ŤŤ oßľT­Z•ZµjѧO<<<6mŻĽň «V­bňäÉŚ3†—^zÉâůüdő©Éd2KKK !!!×uYą­í‡¬űÍ×5kýúő‰‰‰1/6‡ż7kĹÇÇ›ŹemşČŞ3&&†ččh~ýőWV­Zeîß°°0Ξ=›k^ÝZ^~›¦M›ĆčŃŁ9räąť#FŚŕé§źfâĉů¶ ?źóSócbb˛ŤÜ»łgĎäű˙%˛ú"ݧďçÇRżX3Ţzë-ěííń÷÷ç—_~!99™Í›7sîÜ9ŕć[¬™ů}.nőĆoŕĺĺeuűDDDäަÍ""""""""""""R˘>řŕÎť;ǢE‹lEDlĚÁÁqăĆqôčQ222JĄŽ¬Ew‚ŇĚZŻ^=*T¨o}ŁFŤ"""˘HO˘.ŞŇggg‚‚‚¨\ąr©Ö#%ëv|/”UŰ·ogÔ¨QŔÍ…Ăť;w¦GŹôéÓ‡ŔŔ@ú÷ďĎÁiҤ‰ůžŠ+˛sçNČ‹/ľČ›oľÉ„ pqqaÓ¦MŮYç§FŤtëÖŤW^y%Űqk˦uëÖ|đÁĽţúëxyyńĐC1xđ`bccóý^IJJ˘K—.3dČÚ·oĎš5kppp`Ó¦MôîÝ›oľů???.^ĽČ_|ŁŁŁĹóyąxń"“&M2÷íĆŤٰaýő“&MâęŐ«,\¸Đ|lÎś9\ąrĹb?¤¦¦ňŢ{ďqęÔ)ĆŤÇţýűŠ”ŔŰŰ'''ÂĂĂ1™LĚž=ŰĽ¨üťwŢ!11‘YłfÜ|š|jj*AAA899@ýúő™4i’ů»đÓO?5g $..Žyóć ŔäÉ“ó\_§N¶nÝĘ˙ýß˙ń /đúëŻ3tčPľúę+Ę•ËiYQćóöíŰ=z4psŁżż?ÇŹŕřńăLš4ÉĽČ˙Ĺ_dÓ¦MŮęܰaąÎÓ§O3bĶlŮ’oF€Ĺ‹[ěÁ`ńłĐĽys6mÚ„ŃhÄŰŰOOOÂÂÂhѢŻľú*QQQ”/_Ţâ|Čďsq«7nä9V""""y1rn±)¦É“'3oŢ<Ž;†«««­ăÜq !!!ôë×ĎÖQJD“&M8räH®'שS§đńńaëÖ­%Zni¸ÝYóŞŻ´ĆÁÚúKËíl—”śŇ·µk×Ňż˙RťŢŢŢ„††–Z%!))‰ćÍ›óçźR±bE[Ç‘Rv·ý)"""Ó›DDDDDDDDDDDD¤Ä˝ýöŰ8;;3yňd[G‘»Ttt4˝zőâŇĄK¶ŽbŃíÎjëľ±uý"’ݢE‹5j”6Ü…´@DDDDDDDDDDDDJ\ĄJ• âß˙ţ7ááá¶Ž#"Ŕwß}‡ŻŻ/nnnÄĆĆ2dČŞU«FłfÍŘ»wŻůş7n0sćL†J«V­čÖ­‡˛ú|rr2~~~řúú2yňdüýýILL´ďřńăx{{3qâD|||čСĚ÷úO?ý”Îź?Ď#ĚÇăââ0aożý6~~~tďŢ???bccÉĚĚdË–-Ś;–zőęqîÜ9:uę„››ß~űm®ăuëÖ%66¶Ŕ6[ÓŻůeÍióćÍ888ŕččȶm۸~ý:Ć`0Đąsg"""Ř·o5kÖdٲedffĘ!CčرŁUő]şt‰ľ}űâââ‚§§'{öě)ձɯ߳ú×Rů‰‰‰Ľ˙ţű <ŃŁGÓ©S'ćÍ›—oýsć̡B… Ľůć›lßľČŢZĘ–—˘Ě±üĘűâ‹/¨\ą2ŕŕ`222Xµj¬X±ÂbťË—/§\ąr âăăůŕ˛ËKY˙^˘Ű˝{7Í›7§aÆ,Y˛„W_}ŐÖ‘DDDDD¤4DDDDDDDDDDDDDJAff¦©}űö¦'žx”™™ië8"wŔR˘ež={ÖTĄJ` 4ýő×_¦Ď?˙ÜZ·nmľnذa¦#GŽţç?˙iŞQن)..ÎâůôôtSëÖ­MÆ 3ź?yň¤ÉŢŢŢdéĎÓ 64ŐŻ_ßd2™Liii¦űî»ĎäééYŕ=€Éh4šŽŹŹ75jÔČ4uęTó±‹/š5jdňđđ0]ĽxŃ´cÇSĄJ•L€iúôé¦ß~űÍäăăcúő×_s:t¨)!!ˇŔ6[ŰŻ9łćçµ×^3U¨PÁtýúu“Éd2%''›jÔ¨a4hůšôôtS‡Ě?ź>}:WůyŐg4M€iĘ”)¦S§N™ÖŻ_oLŹ?ţx™Š;6)))yö{V˙T~ZZš©S§N¦Á›˙·ä?˙ůŹ 0}˙ý÷ŮÚe2™LWŻ^5 <ŘôçźfË“ß^ľ|ąŔl9uŽĺWžÉd2SDD„ůŘéÓ§MĎ<óŚUuĆĆĆšL&“©~ýúą>gy»UY˙^(ŽR-ßd2™úöíkęŰ·o©ÖQT4ą»»›5jdÚąs§­ăČmTżGŠHŮe0™L¦Űłí@DDDDDDDDDDDDî5űöíă˙ţď˙řüóĎyţůçmGäŽa0 ˇ_ż~%Z®ŃhäčŃŁÜúgbWWWóÓďĂÂÂhÝşuž÷ţđĂTŻ^˝Ŕó§NťbäČ‘DFFb4Íç7ṉcÇ(čĎÓsçÎĺÁdŔ€L&6lČéÓ§IMMÍ÷Á€Ńh$22€€€‰‰‰ÁŐŐŐ|ÝĘ•+ńńńaüřń›űáęŐ«8;;çęź[Ź[ę///‹ýšWÖüDFFŇ´iS/^l~Şź>}Ř´i111T©R…ďż˙ž†žo_äU_“&M8rä™™™ć§ĹW«VŤäääźŇ^cy÷ŻĄňçΝ˸qă8zô(Ť5 ##•+WňôÓOsß}÷™ŰE`` 3fĚ Zµjćň 3†9łĺTÜ9–—«WŻâîî΀X¶l3fĚ YłfxyyY]gV?Ü:ó:–SYţ^(޵k×Ňż˙R+ŔŰŰ€ĐĐĐR«CD¤°Jë÷H)›ěm@DDDDDDDDDDDDî^-[¶dČ!Ś?žŢ˝{Sąre[Gą§e-ż•łł3.\ <<OOO<çý‹-*đ|ź>}pwwĎvĽ\ąrłŤ;–ÄÄD/^ĚŐ«WIII!--Íâ}·Úľ};ŽŽŽŮŽwčĐ€;v÷CÎEÚy·Ô'·Ţw«[űµ0š4i“O>ÉG}Ä#ř믿ČČČ 55•Ő«W3lŘ0>űě3V¬XQč˛óĘ[˝zuŽ9Rŕő%16·Ö›łß *˙ż˙ý/µk×6_oggÇ!Cr•ďĺĺEóćÍłm€ÂŤˇĄ…űĹťcyą˙ţű5jłgĎfęÔ©Ô¬Y“Ť7ňÖ[oŞÎ˘*Ëß """"""R0ý?+)UÓ§O'>>ž™3gÚ:ŠXpĺʢ˘˘HJJĘu.33Óâůččhs9…NłfÍđđđ €*UŞşŚ¬ĹŧNťĘvĽFŤT­ZµĐeZjsi9r$ <<śŕŕ`fΜɳĎ>ËňĺË9|ř0îîîTŞT©TęÎKIŚMQËĎZ~üřq‹ĺĚž=›‚ł/É1,Ť90nÜ8ţńŹđᇲwď^{ě1ěěěJµNkŮň{ADDDDDD ¦Í""""""""""""RŞxŕ&MšÄ¬Yłr-d‘˛Ĺh4’”””k1udd$ .´ę<Ŕúőë ]·ŹŹiiiôčѰn‘¶Á` ==ÝüsÖ“ŇsÖćĚşvíZč\–Úl­śY Ň»woÜÜÜ:u*‰‰‰4mÚ”W_}•đđp^{í5FŚQ˘őYRcSÔň›7o@`` &“É|üŻżţâ§ź~ĘVNĎž=ń÷÷Çßß?Űą’C(ť9ŕââÂ#Xşt)óçĎçĺ—_.tťYOřOMMŔd2qýúő"ĺą•-żD¤äĺü^‹‹łQ’â)‰ď7‘»˝­ČÝoôčŃ|üńÇĽőÖ[„††Ú:ŽČ=ëĆŤąŽĹÇÇžžNź>}đđđ`Ú´iś={–.]şIXXëÖ­ă˙řGç;věHHHţţţÔ­[—:°k×.Îť;Ü|˛ą»»{žŮbbb‹‹ă×_ĺŇĄKÄĆĆFÍš5©]»v®{ęׯOLL gÎśÁÍÍŤńăÇłnÝ:,X€ŹŹ®®®,Z´¶mŰ2räČlýHĺĘ•sőĎ­Ç-ő‰5ýjooź+kAěěěđőőeňäÉ>>GGGŕďĹ”9űâV%16·öSÎş *Đ A|ńĹ„††rĺĘž{î9Îź?ĎĹ‹Y˛d )))ŔÍĹďďľű.;wîdŕŔlŢĽ™-Zj ę Řs¬ ~~~Ěź?źÓ§OSż~ýB×i4‰ŚŚäý÷ßÇÇLJ~řÁÜ7żüň Ýşu3żeŕVeů{AnżăÇŹóÝwßáççgł mÚ´ˇC‡z«T JIIaÎś9üđĂ„……‘žžÎěŮłůţűďٱciii¶Žh•ĽÚQLš4‰QŁFQ«V­J)"""r{ŮMť:uŞ­CČÝÍÎÎŽ† 2iŇ$Ú´iC lI¤L{÷Ýwńöö桇*±2/^ĚŞU«(_ľ<-Z´`éŇĄć :iiitěŘ‘ľ}űņ ظq#µk×fѢE8;;cooĎÓO?ťďyWWW:wîĚX¸p!+V¬ŔŐŐ•řřxţőŻQłfMęÖ­k~‚ů­śśśřý÷ßůóĎ?yá…đđđ`÷îÝś>}šľ}űR±bĹ\÷ś?žČČHZ´hA“&M(_ľ<>>>\»vŤĄK—rŕŔ6nÜČ}÷ÝÇňĺËIKKcĆŚ|ýő×\ľ|777śśśň<îęęj±ÍÖôk»ví¸|ůr¶¬–<ôĐC¤§§3hĐ ó±*UŞđěłĎf[|ź””D`` ż˙ţ;ńńńT­ZOOO®^˝j®Ďh42gÎľúę+’““ißľ=óćÍ3·9%%…Nť:aggWâcS·nÝ|ű×RůÆ Ł˙ţDEE±wď^víÚE  "99™9sćËupp AT­Z•Ő«Włzőj -[¶4—‘s  Ě–SQçX~ĺÝŞJ•*ěŢ˝›ÁóđĂ[]gůňĺxä‘G ăŰożĺŕÁŚ3†ť;wұcGęÔ©CăĆŤsŤoY˙^(ŽÖ­[Gi.‹Éę§~ýú•Z·Ó–-[3gÓ§OĎó»ŕvٸq#5jÔ sçÎ6ËźłgĎâääTę÷”4{{{Zµj…żż?IIILť:•G}”‰'’hńsRÚy·Ł8Ę•+ÇŁŹ>Ę#xě±Çpvv.™ 6VżGŠHŮe0Ýú=‘RäííÍľ}ű8xđ`ž‹GEä&Á@HHČ]łŔTD,KJJ˘yóćüůçźúßȰvíZú÷ďOi.‹ńöö¸+ŢzIŹ=Ř·o÷ßż­ă”I§NťÂÇLJ­[·–ę=Ą©I“&9rÄüąČůs^ĘZŔşÜ…‘µm×®]VżÍĄ,Óď‘"""÷–Üď)%óçĎçňĺËLź>ÝÖQDDDĘ”E‹1jÔ(mŰÎd21hĐ ^zé%mČGtt4˝zőâŇĄKĄzOYs7´Á?ü0őë×筷޲u‘BÓfąm|đA¦L™Bpp0Gޱu›Ú˝{7Í›7§aÆ,Y˛„W_}ŐÖ‘äôÝwßńÇĐŁGŹlÇ÷ěŮC›6m9r$ďĽóĺË—'11€7n0sćL†J«V­čÖ­‡˛ęŢüÎeffĘ!CčŘ±Łą¬¸¸8&LŔŰożŤźźÝ»wÇĎĎŹŘŘXs~___ÜÜÜŤŤeČ!T«VŤfÍš±wďŢBőE~Ů>ýôS"""8ţ<#FŚ0_üřqĽ˝˝™8q">>>tčĐäyĎňĺË)W®€řřx>řŕlÇ,őßćÍ›qssËö¤ţĽŽ%''ăç燯Ż/“'OĆßßß\FN—.]˘oßľ¸¸¸ŕééÉž={ňm5¬Kckm;Š3łtďŢťĺË—eu;EDDDĘ©4߇&""""""""""""’CFF­ZµÂÉɉ͛7g[ř$"7 BBBč×Żź­ŁH):tčO=ő˙řÇ?X±bmÚ´±u¤»ĆÚµkéßż?Ąą,ĆŰŰ€ĐĐĐR«ăv8p kÖ¬!55{{{óńĆŤsůňe®\ąŔ€X°`Ő«WgřđářůůѸqcŕćBępüřq Ľ· sgÎśˇNť:ŤF"##IHHŕŃGeŕŔL™2¸ąp˝]»v¤§§óÇ€Ńh$!!ŔŔ@ ĶmŰ4h­[·f×®]V÷EAŮ 9W–FŤ‘™™É‰'HOO§zőęÔ®]ŰĽ! Ż{4hŔÉ“'łÍÍśÇ ĘńÝwßńüóĎBŻ^˝rËČČ m۶<üđĂ,[¶ €¨¨(7nLzzşąž&MšpäȦL™ÂK/˝DDD^^^<ţřăěر#ß6XmqL¬Ű*UŞXŐŽâĚÇ,ű÷ď§eË–Lź>ť‰'ZÝÖ˛HżGŠÜ[ôfą­ěěěř裏ضmkÖ¬±u›ńôôä˙űGŹŐF±™ť;wRµjŐl®]»ĆŐ«W™?>&“‰É“'SˇBÂÂÂXľ|9FŁÁ€Á``Æ \¸pÁüdúüîµtÎÍÍ-[†3fpěŘ1|}}ÍÇŞWŻN@@QQQQ«V-jŐŞ€żż?uęÔá…^ FŤěßżżP}QP¶ĽŚ1‚÷߸ů;®‹‹ GŹ-°ŽňĺË[uęT¶ăYOqŻZµj‘Űť—Âd› Ń›5k†‡‡T©RĹ&9rŠŽŽŕĘ•+%’§4X3¶Ö´Ł¸ó1K^›DDDDîÚ """"""""""""6áěěĚĚ™3YĽx1»wď¶uąĹĹĹŮ:B‘YĘ~ńâEBCC *v]ׯ_/v–ÜÉcQVä5ć%9JZqÇĽ,·í^óŕćů¦¦)S¦ŕááÁĎ?˙ĚęŐ«IKK# ŁŃHRRÁÁÁŮ®ŹŚŚdáÂ…Ţké\NYO‰_ż~}¶ăgÎś k×®ĹkĽ•m†›‹ĹÓÓÓł]ďăăCZZ=zôţ^|ž%Ż{˛ť§¦¦`2™r}O[꣼6oÜzĚh4ąű­(ňjCI°fl­iGqçc–k×®ŕęęZŚV‰Ü~Ú """"""""""""6ăăăC§Nťxýő×ó\Ô$"–ýöŰoôěŮÁ€Á`ŕÉ'źäÉ'ź¤U«VôéÓ‡O>ůÄĽŕ0ËÖ­[yúé§Í÷Ś=š'NpâÄ Ţxă óą>}ú°uëV6oŢŚÁ` jŐŞ4oŢś6mÚ`0¨X±"mÚ´ˇYłfT¬XÁŔ… J­˝Óľ}{\\\J­žŇ`mö#GŽ0mÚ4úőëÇĘ•+‹TWJJ AAA<ńÄ…î§ÂŚő[o˝EÇŽK|,î…yݦMĆŹä=ć%1JĂěŮł‹=ćeµm÷ŞŽ;+¸ď IDATOBBB¶ăłgĎ6očŰ·/U«VĄV­ZôéÓ¦M›Ć+ŻĽÂŞU«|¸TžT\š¬Ín4™3gN±ęrpp`ܸq=z´Đ›­ 3ÖŻĽň %>wÚĽ.ŠzőęQˇB ď1/‰yP–>ŁŁFŤ*öŰŞm’7L&;wîĚv<))‰.]şĚ!Chßľ=kÖ¬ÁÁÁM›6Ń»woľůćüüü¸xń"_|ńŽŽŽŢ[Đą¤¤$ó›"Îť;ÇÜąsIOOgçÎť 8_|‘7ß|“ &ŕââ¦M›°··gńâĹćĹíÄĹĹ1oŢ<˘ŁŁĄŮ¶µk×Ňż˙RýŽđöö 44´Ôę¸]ĽĽĽhÔ¨sçεu›čÝ»7®®®,[¶ĚÖQŠ­(żGŠČťKo›{çťwpqq᭷޲u‘»Î>Č{ď˝ÇÉ“'‹ő$îž={Ň­[7‹×˝öÚk4lŘ0×ńččhzőęĹĄK—ŠśAnŹâŽőíPVćőÝDźŃ{Űţó~üńG.\¸`ë(ĄÂ`0XüwôčQ[ÇĽ#ÜŤ}ą{÷nŽ;¦7–ČI›DDDDDDDDDDDDÄć*V¬Č˘E‹Xµj7n´u‘»Nßľ}±łłcÆ E.ŁbĹŠŘŮŮYĽÎÁÁňĺËç:ţé§źÁůóç1b„ůx\\&Lŕí·ßĆĎĎŹîÝ»ăççGll¬UąNś8AďŢ˝ą˙ţűyě±Çřď˙k>wăĆ fÎśÉСCiŐŞÝşuăСC–WĐ=ß}÷ľľľ¸ąąË!C¨V­Íš5cďŢ˝ć2Ž?Ž··7'NÄÇLJ:pđŕÁBe/l6€äädüüüđőőeňäÉřűű“hE/fWÔ±ľté}űöĹĹĹOOOöěŮcuö˘( ózóćÍ888ŕččȶm۸~ý:Ć`0Đąsg"""Ř·o5kÖdٲedffĘ!Cčرc‘łg±v^Zú¬ĺ÷-HIŽů®]»xóÍ7©WŻ.\0—۬Y3ľúę«"öŽXëŕË/żděر$%%Ů:N‰3™L˙5nÜŘÖ1ďw[_ĆÄÄČożý†ŁŁŁ­ăšÁt'ľ3QDDDDDDDDDDDDîJĎ<ó üůçźT¨PÁÖqDlĆ`0Bż~ý uŹŃh$222Ďó5kÖ$55•Ë—/g»Ç’‚ţ¤l©NK×'$$đ裏2pŕ@¦L™Ü\\Ü®];ŇÓÓůăŹ?¨Zµjže5iŇ„#GŽ0fĚĽĽĽŠŠbܸqܸq}űöѬY3†ŽźźźyQb÷îÝ9pŕÇŹĎwÁ_A÷ÄĹĹa4IHH 00A±mŰ6 Dë֭ٵkŤ5"33“'NžžNőęŐ©]»¶yC€5Ůóꯂ˛UŞT‰¶mŰňđĂłlŮ2˘˘˘hܸ1éééŽcQĆ.ݱ2e /˝ôxyyńř㏳cNj٠Z|y'Ěë×_ť˙űß\¸p'''nܸ»»;ÝşucĺĘ•dddđä“O˛eËÎś9Cť:urŐ“WÝ–ňDGG[ś—Ö~Ö¬m{IŤyV}üôÓOx{{“śśĚ¨QŁđööćěŮłřúúĎöíŰyâ‰'¬€µk×Ňż˙bĎý‚x{{ZjuÜn˙űß˙X·nťŢÖ$÷„ôôtćĚ™ĂkŻ˝vWm(Ęď‘"""rçŇ›DDDDDDDDDDDD¤Ě?>111Ěš5ËÖQDî:ööö888ä:n4ó|ŞŻŃh,őL3fĚŕرcřúúšŹUŻ^ť€€˘˘˘ ˛XĆ´iÓčÚµ+Ç'((ŚŚ ćĚ™CXXË—/Çh4b00 lذ .°uëÖ<˲tO­Zµ¨U«ţţţÔ©S‡^x5j°˙~s9#FŚŕý÷ßŔÎÎŽ=juö˘d[şt)»wďfܸqć{<<<đđđ°Ř‡%eĘ”)Ô­[—ž={âââ¬Ę^ea^Ź9’7nđĹ_PˇBZ·nÍ7ß|CBB?ţř#/Ľđ‚ů77·«ßšyYźµĽ”Ô—+W///sżĚ1öíŰóüóĎóŢ{ď°`Á‚"e”©WŻž6Č=ĂŢŢž &ÜUDDDäŢŁÍ""""""""""""Rf¸ąą1eĘ‚‚‚8vě­ăÜ5ŇŇҸpá-Z´°u”l¶oßk^‡ĚO/Č­÷>ýôÓ>|đđp<==ó\îĺĺ•gYÖÜ“×SçťťťIII1˙Ś»»{¶î·[IÖ,)É1ŻYł&P˛oSą[h3€”)ööö|ňÉ'ěÜą“eË–Ů:ŽČ/55Z¶lÉoĽaő}ĹY¤ťÁ@zzşů笧’Ż_ż>ŰugÎś k×®…*?ëľ^˝za4IJJ"888Ű5‘‘‘,\¸0Ďű‹rO^|||HKKŁGŹ€u}ykö˘d3Ť@îľĚKFF†ĹkJRIőë­ĘŇĽ†›OŻwsscęÔ©$&&Ň´iS^}őUÂĂĂyíµ×1bD©Ôk-k?k9?ŁEU’cžµÁĄ°ß""""""÷{[É©yóćŚ7ŽńăÇÓłgO= XÄ‚äädnܸ‘ířľ}ű3f ×®]cýúőŘŰ˙ý'â¬'vÇÄÄ`2™0 ćs&“‰łgĎ’’‚C®:ł•cŤúőëĂ™3gpsscüřń¬[·Ž ŕăă««+‹-˘m۶Ś92߲˛ň^»v gggćÎťKź>}2d)))xxx0mÚ4Ξ=K—.]ŚŚ$,,ŚuëÖĺYfź>},Ţ“łŹâăăHOOÇŢŢžâââřő×_ąté±±±„……QłfM‹Ů!÷ZĘÖ±cGBBBđ÷÷§nÝştčĐ]»vqîÜ9ŕćáÝÝÝ böěŮěŰ·ŹşuëZ3lÇ:..ÎÜŽŽŽ\ż~Ý|Ż5ýš—;e^ŘŮŮáëëËäÉ“9xđ ;vÄh4âä䄇‡G¶ë€żű.żöć×9Yš—Ö~Ör~FóSc^PŰ222°łłŕ·ß~ăŃGĹ××·Ŕ>ąŮMť:uŞ­CäÔľ}{V­ZĹöíŰyá…lGä¶z÷Ýwńöö桇˛xíöíŰ bßľ}ÄĆƲeËÖ¬YChh(Ű·oç©§žâŁŹ>˘fÍšŮî™1cüń)))¤¤¤P·n]\\\8~ü8sćĚaóćÍ?~śűďżźzőę™ďß°ałfÍb˙ţý\ż~ť .ŕčč»»{YĎź?Odd$-Z´ I“&”/_®]»ĆŇĄK9pŕ7näľűîcůňĺ”/_>߲7nLll,+W®dçÎťüöŰo4iŇ„Yłfa0°··çé§ź&**Š 6°qăFj׮͢E‹Ě đs˛tĎâĹ‹YµjĺË—§E‹,]ş”ĐĐPŇŇŇh×®÷ß?ż˙ţ;ţů'/ĽđěŢ˝›Ó§OÓ·o_Z´hQ`ö˙ýďĽ˙ţűěŢ˝›ŘŘXśťťy衇0`@ľŮ\]]éÜą3`áÂ…¬X±WWWâăăů׿ţEÍš5©[·.üńűöícذa899Yś_ŤµÉdbÎś9|őŐWŔÍĹÝíŰ·gŢĽy|ýő×ŔÍE÷]»vĺąçž+ÔXÜIó:ËC=Dzz: 2«RĄ Ď>ű,µk×6KJJ"00ß˙ťřřxŞV­JĺĘ•™5kV¶1wpp`öěŮŮŽŤF*T¨­^kće§Nťxůĺ—-~Ör~Fs*©1ĎkŽŤF>ůä._ľŚ““Ť7&11‘m۶±dÉ’\í¶$""‚uëÖQšËb˛ú¸_ż~ĄV‡Hać÷HąóL&“ÉÖ!DDDDDDDDDDDDDň˛eË:wîĚęŐ«éßżż­ăÜ6-0‘{J“&M8rä%±”eíÚµôďßżDĘĘŹ··wo·±ý)""rď°·|‰mtěŘ‘áÇ3räHž|ňIŞWŻnëH""""Ś7ooo[Ç(s.\¸ŔŽ;رc§OźĆĹĹ…Çś~ýúáŕŕ`ëxeŇÎť;ůđĂ ±uąK<ńÄ¶Ž """·‰6H™6sćL~üńGüüüřěłĎlGDDDJIbb˘ů?+W®lă4–=ţřă<ţřă¶ŽQ&DGGłnÝ:BCCٱcÎÎÎxyyáííMĎž=±łłłuÄ2ďĂ?Ô“ÜEDDD¤ĐĘŮ:€HAśśśXşt)+W®äűďż·u)a‰‰‰Lš4‰3gÎđĆo°k×.§K®]»Ćgź}ĆSO=…»»;S¦LÁĂĂożý–óçĎ›Ďi#€HéŃ›DDDDDDDDDDDD¤ĚëŮł'ýű÷gäČ‘tęÔ GGG[G‘Rąre ´u±ŕúőë|űí·„††ňË/ż`ggG×®]ůä“OčŰ·/•*U˛uD‘{ŠŢ """"""""""""w„… ’śśĚ¤I“lEDDDäžqăĆ ľ˙ţ{|||¨U«ÇŕăŹ?ćâĹ‹ćsÚ """"rűéÍ""""""""""""rG¨V­sćĚaČ!ôë׏víÚŮ:’Č])##Í›7óŮgźńÍ7ß””D›6m ä…^ Zµj¶Ž("""""h3€ÜALhh(C‡e˙ţýT¨PÁÖ‘DDDDî ™™™ěرĐĐPVŻ^Í•+WxüńÇyď˝÷čßż?®®®¶Ž("""""9”łu‘ÂX¸p!çÎť#((ČÖQDäµ`Á&NśČ“O>I‡8vě­#eÓ¦MĆŹoë"r‡Ř»w/ŁGʦvíÚ´oßžß~űŤ×^{ŤăÇŹóűďż3zôhm)ŁôfąŁÔ©S‡ŔŔ@ĆŤÇ3Ďú(dĘ”)\şt‰víÚ™ż›Nś8A×®]IJJbúôé´jŐŠ5kÖđá‡RąresŮ™™™üôÓOx{{“śśĚ¨QŁđööćěŮłřúúĎćÍ›qppČ·Ľ«WŻR§Nťl.\ȨQŁřé§źčŃŁpó 'Nä‹/ľ QŁFdffrâÄ ŇÓÓ©^˝:µk×6oČŮn€áÇăççGăĆŤčŢ˝;ŕřńă8::–j˙}öŮg <>}úäŮcÇŽÍ7[\\FŁ‘„„4h۶mcĐ A´nÝš]»vĺŰćĆŤsůňe®\ąŔ€X°`Ő«W/Ú„’RłvíZú÷ďĎ˝˛,ćÚµk|˙ý÷„††ňóĎ?Sąrez÷·7=zôĐďHeÜ˝6_EDDD¤äh3€ÜŃŽ9Â#ŹČ€0™L4lŘÓ§O“šššg™aaa´nÝ:Ďü?üđ^^^·Ą˙ň:nM¶¬űn]2áęęj~«@^Yxŕ.]şÄĽyó5j‡¦Nť:ůn~ŰąW'%%±~ýz>űě36lŘ@ąrĺčÚµ+ŢŢŢôíŰ×üý!eß˝0_EDDD¤t”łu‘â0ŤL›6Ťwß}—?˙üÓÖqDäł}űv€\‹Á;tčŔŽ;€› ˋʕ»ů§ü[ňöîÝ€ăÇŹşĽňĺË3zôh~řᢢ˘HKKăčŃŁćŤcÇŽĺ©§žbńâĹ’’’BZZZľe†‡‡ăéé‰ÉdĘő/żŤy)n˙ĺuÜšlY÷ÝĘŮŮ™”””ó.Y˛GGGFŹÍcŹ=FBB‚6Čm•śśĚ÷ßŹŹŹ<đ`ůňĺ\ĽxŃ|NDDDDDî Ú """"""""""""wĽqăĆѦM|||ĚO±ą˛îź:u*Űń5jPµjŐ©§fÍš¸ąąéţˇC‡Rąre.\Č7ß|Cßľ}łť§Yłfxxx@•*U ,ďĘ•+DEE‘”””ë\ff¦ŐąJŁ˙J*[^ž{î9öďßO÷îÝŮłgíŰ·gĹŠĹ*SÄ’äädľüňKúőëGµjŐxöŮgąxń" ,ŕüůóć Ú"""""rďŃfąă•+WŽO?ý”“'Ohë8"rÉz‚ýúőëł?sć ]»v-‘z®\ąR¬ňśśś:t(˙ţ÷ż á™gžÉvŢÇLJ´´4zôčä^4o0HOO7˙l4IJJ"888Űu‘‘‘,\¸0ß9Ë)Ťţ+j6KY¦L™‚‡‡?˙ü3«WŻ&--Ť€€€Bg±$%%ĹĽČßŐŐ•~ýúqîÜ9‚‚‚8{ö,?˙ü3/˝ô÷Ýwź­ŁŠ i3€ÜęŐ«ÇôéÓ "<<ÜÖqDä.tíÚ5€lo ?~<žžžć'tgY´hm۶eäȑܸq€ÄÄD«ëËČČ0˙÷ß~űŤG}__ßËKHH ...WyoĽń ´lŮ{{űlçbbbŽŽć×_eŐŞUÄĆĆĆŮłg©_ż>111ćEú}úôÁĂĂiÓ¦ńĘ+ݰjŐ*&OžĚ1cx饗ňmSÎrŠŰy·&[Ö}·ŠŹŹ0oČ™`öěŮćľéŰ·/U«VĄV­Zů¶W¤0222řý÷ß=z4µk×ćé§ź&**ŠiÓ¦qöěYóą¬7gh3€Ü5^ýuşté‹/ľçBO‘˘:tčţţţś:uŠiÓ¦ńçźR±bEvîÜÉŔyńĹyóÍ7™0a...lÚ´‰ÔÔTŢ{ď=Nť:Ŕ¸qăŘżżUu~řá‡\ąr…K—.Ă–-[ ,/))‰   Îť;ÇÜąsÍ ÜÜÝÝ5j#FŚČUWPPNNNPż~}&Mš„łł3AAATŞT ooośśśĚ›­Ř´i˝{÷ć›oľÁĎĎŹ‹/ňĹ_ŕččo›r–SÔţKJJĘ·,e[Ľx±ůľŔŔ@âââ7oŃŃŃLž<™7näĘšŐÇ]şt!88!C†Đľ}{Ö¬YcŐxŠäĺÖ 5kÖ¤}űöüöŰoĽů曜>}Ú|îÁ´uT) &“Édë"""""""""""""%ĺěŮł4kÖŚáÇlë8"Eb0 ˇ_ż~¶Ž"6ФIŽ9‚ţś/Rtk×®Ą˙ţeňs”™™ÉŽ; eíÚµś?ž¦M›âííÍ AhĐ ­#ĘmV–ç«”mö–/ąsÔ®]›YłfáëëKŻ^˝hßľ˝­#‰Č=îÖ ˇˇˇÄÄÄĐ´iS|}}8p Ť5˛uDąi3€Üu†Ę×_Í!C8pŕUŞT±u$«%&&š˙łrĺĘ6N#"ĹAhh(+W®$**ЦM›2|řp €Ńh´u<ąĂ•łu‘Ұ|ůrbcc™4i’­ŁX%11‘I“&qćĚŢxă víÚeăT"RXLť:•† âééÉŠ+čŐ«{öě1źÓF) z3€Ü•j֬ɼyóđńńÁËË‹ţóź¶Ž$"R Ę•+H`` ­ŁH!e˝`Íš5=z”:uęđôÓOăííM»vílODDDDDîRÚ """"""""""""w­Ańí·ßâëëËprr˛u$Fűáz IDATůöî<ş¦s˙ăřçd‘"’"ÄÔ/—r5ÇCb(Gt0¬šĘˇZQ:ř™Šöją•*­)'jZC‹Ö5Tkľ4ŤŞ˘RM"‘a˙ţč•K‰$$ŮŢŻµ˛VíýěgžgoçD×ţî€"âřńă Uhh¨Nś8!oooőîÝ[ .TË–-e±XĚŽ ŁEÚű￯úőëë…^Đ‚ ĚŽ ±¨¨(­[·N6›M»wďVĄJ•Ô§O-X°€ůŽbiz÷ÝweµZŐ»wouíÚŐěH ůĺ—_´fÍŮl6íŮłGîîîęÖ­›BBBÔµkW98đř sđŻy}úôŃÓO?­gź}V?ţřŁ<==ÍŽ °_ýU«WŻÎ((S¦Śüýý˘.]şČŃŃŃě ;łůáťwŢQ‰%4hĐ †avPŔÄĆĆjÁ‚jŐŞ•ŞT©˘)S¦¨FŤZ·nťbbb´téRP Ŕ°üźN»víR»víôŢ{ďiřđáfÇ2e±XĚŽEBVŹĹDGGkőęŐ Ó®]»TŞT)őěŮSýúőSçÎťĺää”OIQś…††*00‚UäŮ€üŇŞU+M0AĎ?˙ĽÚ¶m«Úµk› ¸«U«V™Ь›6›M»wď–«««´zőjuéŇE%J”0;"d + XIMMUË–-•––¦={öđĆ_ŠŘŘXmÚ´I6›M_~ůĄśśśäëë+«ŐŞ>}úČŐŐŐě(ĆX÷ËÎě@~rppвeËtęÔ)Mť:Őě8 ŹÄĆĆjéŇĄ P… 4räHIҢE‹téŇ%…‡‡kŕŔ(´Ěä·G}TsćĚŃčŃŁŐ©S'µmŰÖěH ś?^aaa˛ŮlÚ»wŻJ”(!___}üńǬ ČˇĹŇČ‘#őĹ_hŔ€úá‡T¶lYł#€űđ믿jőęŐw,^ĽEĹ(¶-Z¤ż˙ýď ÖŇĄKÍŽ˛é^}űöUÉ’%ÍŽyŽb[žžžúä“OÔµkWuíÚUO>ů¤Ů‘@&(€ŰQ €bÍĎĎOĎ=÷śFŤĄ–-[ŞJ•*fG˙ukŔž={ôĐC©GŹ V÷îÝ)P¬Y Ă0Ě)))IM›6Uٲeµ}űvŮŰŰ› €bëÜąsúüóĎ3 J—.­€€Y­VuîÜYÎÎÎfGrUhh¨Ĺc\Č)V@±W˘D -Y˛D-Z´ĐÜąsőâ‹/š €b%ł€ ’7n¬˙űż˙Óäɓծ];5mÚÔěHi÷*đóó““““Ů @ł¬/H’ŇÓÓŐľ}{ĹĆĆj˙ţý*Y˛¤Ů‘(R2+°Z­ Ř U`` xŚ 9ĹĘŔŮŮŮéÓO?UŁFŤ4věX-X°ŔěHz¬ybŕUŞTŃÂ… Ő§Oµoß^O>ů¤Ů‘(t~ůĺ­Ył†ČCńÄOhäČ‘9r¤|||T˝zuł#Pŕýµ L™2ň÷÷§ňĹ0 Ăě@A“””$999i×®]<ŔŔ]dV`µZ)˛)44Tâ1.ä+wQ˘D …††ŞI“&zőŐW5kÖ,ł#P ° ™¨U«–Ţzë- >\­[·V÷îÝÍŽ€)Ξ=«µk×RĹŔ= :T;věĐłĎ>«Ă‡«bĹŠfG _P›Ĺ0 Ăě@AŻ&Mš¨RĄJÚşu«ěííÍŽ@žČ¬ŔjµRä‘ĐĐPŠÇ¸S¬ dˇT©RZ¶l™Z¶l©7ŢxC'N4;ąć§ź~’Íf“ÍfÓˇC‡äááˇŢ˝{ëŐW_•ŻŻŻxĽ "ţµdĂ?ţńMź>]“&MR›6môř㏛ €űvęÔ)………),,L‡Î(5k–Ú·oOőĄ€l1 C={öÔáÇuřđaą»»› €l‹ŚŚTxx¸l6›vďŢ­rĺĘ©[·n˛Z­ęŇĄ‹ÍŽKˇˇˇ Źq §(ă˛Éb±hѢEjÔ¨‘†®°°0ł#pOÇŽ“Íf“ÍfÓńăÇĺáᡮ]»*$$D]»ve(ÄřžžžZľ|ą:tč  hřđáfGŕ67 V­ZĄ“'OŞrĺĘęÖ­›fÍšE!üëȡ¶mŰ*$$DÁÁÁjŢĽą6lhv$@1wł`ĹŠúĎţ#oooőîÝ[}ô‘Z¶l)‹ĹbvD@.ł†a(lRSSŐ®];]˝zUß}÷ť\]]ÍŽ(fn,_ľ\§OźVŐŞUŐłgOY­V €B$44Tâ1.ä+÷ÁÁÁA+V¬PăĆŤ5lŘ0-_ľÜěH€".==]{öěц ¦ź~úIŐŞUSŹ=ôńÇSĹ ĹŔ}ňööÖŇĄKĺďď/___ :ÔěH€"ćf€ÍfSXX˘ŁŁUŁF ůűűËjµŞU«VfG„bŕtíÚU'NTPP7n¬ĆŤ› PČĄĄĄiďŢ˝˛Ůl˛Ůlşpá‚ęŐ«§aÆ) @Mš41;" °†a(ĚŇÓÓĺç秨¨(8p@ĄK—6; ąµ 44T/^T˝ződµZ¨şuëš@ U`` xŚ 9egv °łłłÓgź}¦„„ 4ąŮ’––¦]»v)88X^^^jÝşµ¶mۦ#FčäÉ“:vě¦L™r_…Ë–-“«««,‹fĎž­´´4IŇňĺËĺěě¬%K–H’’’’4gÎ :TM›6U§NťtôčŃŚ~8 Ť=ZŻľúŞ•;ŕ6#FŚ···âââ4xđ`yxx¨A:xđ`F›k×®)$$D/˝ô’ĆŹ/???Ť?^qqq&&€YXČ%;věPÇŽ5wî\Ť3Ćě8€(99Y[¶lц ´fÍ]ľ|9c€§ź~Z5kÖ̵s˝ňĘ+š>}şŽ;¦zőęI’Îť;§ŕŕ`}ţůç’¤áÇküřńŞ]»¶$ÉĎĎO?üđNź>-777Ő®][±±±şrĺŠ$©˙ţú÷ż˙-OOĎ\Ë w7W(UŞ”âăă5cĆ =óĚ3úöŰoőĚ3ϨyóćŠP||Ľš4i˘§žzJŻ˝öš$éňĺËjŐŞ•RSSučĐ!V¨(f(rŃôéÓ5uęTíرCŹ?ţ¸Ůq@RR’¶nÝ*›Í¦uëÖ)>>^Ź=öüýýőĚ3ĎčŃGÍ“óţöŰoŞV­šú÷ďŻ H’fÍšĄ ¨{÷îúî»ďÔĽyó»»aĂuďŢ]?ü°._ľ¬yóć)((HÇŹW•*Uäćć–'™âčf1@íÚµuęÔ©ŰV™ŞPˇ‚âââ”””¤—_~Y3fĚĐ… TˇB…Ś6ź~ú©¨ &höěŮf &±3;P”Lš4I;vT˙ţýkv€I®_ż®đđp 8P?ü°zőęĄČČHMť:Użüň‹8 )S¦äY!€$ą»»+((HK–,Qtt´$髯ľR—.]$Iű÷ďWýúőeĆ?Ý»w—$˝˙ţűrssSpp°š5k¦řřx €]çĎźĎ(đňň2%_ąrĺ4räH}đÁš?ľž}öŮŚ}uęÔQbb˘fĎž}Ű1'NśĐ;ďĽ#Izíµ×TŁF }ůĺ—Z±b…RRRôňË/çëüéć 7nĽműąsç$I;vĚ÷L0—ĹŕU$@ž;w®BBBôŐW_e<Ľ(üâââ´~ýzŮl6mÝşU©©©ňńń‘ŐjU˙ţý3ŢŇ]PÄÄĨjŐŞjѢ…¶oßž±=99YőęŐSdd¤ž}öYučĐA'NśĐwß}§°°0ąąąÉŐŐUçĎźW™2e”šš*Ő©SG&Ž(ZBCC¨jŐŞ)**ę¶•Ą*W®¬óçĎ+%%E)))jÖ¬™âââ´˙~U¨PA’4věX8p@;věYĂ€ (ňaęŰ·ŻvďŢ­ďż˙^+V4;ŕ>ýöŰoÚ°al6›¶lŮ"‹Ĺ˘Ö­[Ëßß_O=ő”<==ÍŽxO Ô3ĎýôS999™ Ë,†af‡ŠäädµnÝZׯ_WDD„\]]ÍŽĹĘ™3g´zőj………éŕÁ*S¦ŚÔ·o_uîÜYÎÎÎfGP …††*00P<Ć€śbe ź8;;kőęŐjҤ‰†Ş+V ŠĽČČH…‡‡ËfłiĎž=*[¶¬şwď®W_}U~~~Ľ1PhQ ä#ooo­\ąRť;w–ŹŹŹ‚ÍŽEαcÇdłŮdłŮtüřqyxx¨k×® Q—.]äččhvDĹ@>óőőŐ´iÓô /¨QŁFj۶­Ů‘ Đ»Y°rĺJť:uJŢŢŢęÝ»·ćÍ›§víÚÉÁG$E ˙ŇL0qâD:tH:xđ *UŞdv$(TŇÓÓőý÷ß+<<\Ë–-Ó™3gTµjUőěŮS .TË–-e±XĚŽ @žˇ0ĹbŃǬćÍ›ËjµjÇŽrrr2;hiiiÚ»wŻl6›Â­5jČßß_V«•@±B1`777­YłFÍš5Ó /Ľ ůóç› ś[ BCCuńâEŐ«WOÆ S@@€š4ibvDLA1`˘Úµkë“O>Qź>}Ô¸qc <ŘěH`ş¤¤$mÝşU6lĐš5ktůňeŐ«WO#FŚP˙ţýU§Nł#`:Š“őîÝ[ăĆŤÓČ‘#ő÷ż˙]Ť76;ä»ëׯk۶m˛ŮlZ·nťâăăőŘcŹiÔ¨Qzúé§UłfMł#P X Ă0Ěw©©©ęرŁÎž=«¨\ąrfG€<—¨ŻľúJ6›MkÖ¬Qbb˘Z´h!«ŐŞľ}űŞRĄJfG€<ŞŔŔ@ńrŠb €‰‰Q“&MÔ¨Q#­_ż^vvvfG€\wőęU…‡‡kÆ Ú´i“’’’äăă#«ŐŞŔŔ@U¨PÁěg.^Ľ¨·ß~ű¶mQQQÚąs§ tŰvOOOŤ?>?ă ˇ(@"""Ô®];Ť?^3fĚ0;äŠ+W®hăĆŤ˛ŮlÚ˛e‹,‹Z·n-=ůä“zřá‡ÍŽů"==]+VTll¬3m—śś¬   Íź??Ó °q0;€˙ńńńŃ‚ 4xđ`ýýďW`` Ů‘ŕľÄĆĆjÓ¦M˛ŮlÚĽyłěííŐ±cG}ôŃGęŮł§J—.mvDČwvvvzúé§őÎ;ď(99ůžmźzę©|J€ÂŠ•€(((H‹-Ň·ß~«&Mš˛ĺ—_~Ńš5kdłŮ´wď^•(QBľľľ˛Z­ęÝ»·ÜÜÜĚŽ¦Űżżš5kvĎ6^^^úő×_e±Xň) #Š€(55Uť;wVdd¤öďß/OOOł#Ŕ]EEEiÝşu˛ŮlÚłgŹJ—.­€€¨[·nruu5;"85jÔĐĎ?˙|×}NNNzńĹ5}úô|N€Â†b €şr劚5k¦jŐŞióćÍrpp0;H’"##.›Í¦Ý»wËÝÝ]Ý»w—Őj•źźźśśśĚŽÚkŻ˝¦×_])))wÝäČŐŻ_?źS °ˇ(Ŕ~řáµlŮRÆ Ó[o˝evĹرcÇdłŮdłŮtüřqyxx¨k×®˛Z­ęŇĄ‹ÍŽ…ĆÉ“'U·nÝ»î«S§ŽNś8‘ωPńę kذˇ–,Y"«ŐŞúőëkČ!fGPŚÜ,XąrĄNť:%oooőîÝ[óćÍS»víX±îSť:uTż~};vL·ľËŐŃŃQ 21 V Íź?_;wîTłfÍĚŽ JOOמ={´aĂ­^˝ZgÎśQµjŐÔŁGY­VµlŮR‹ĹěP$ĽńĆš4i’RSS3¶Y,ýôÓOŞ^˝ş‰ÉPXP éééęŃŁ‡:¤ČËËËěHŠ´´4íÝ»W6›MaaaŠŽŽVŤ5äďďOäˇsçΩjŐŞ+X,5mÚTűöí39 Š€BâęŐ«jŢĽą<<<´}űv9;;› @!uăĆ }őŐWúüóϵvíZĹĆĆŞQŁFzâ‰'Ô§OŐ«WĎěP,´lŮRJOO—˝˝˝ćÍ›§ţóźfÇ@!A1Pś:uJÍ›7WďŢ˝µxńbłă(D®_ż®Í›7kőęŐÚ°a~˙ýwýă˙Pź>}Ô·o_=ňČ#fG€bç>ĐčŃŁ•––&{{{ť?^ĺË—7; Š€BfÝşuęÝ»·Ţ{ď==÷ÜsfÇP€%&&ꫯľ’ÍfÓÚµk• -Z( @}úôŃŁŹ>jvD(ÖbccUˇBĄ§§Ë××W۶m3; łČ™ž={jĘ”)3fŚj×®­öíŰ› @rőęU…‡‡kÆ Ú´i“’’’äăăŁiÓ¦ÉjµĘËËËě€˙ňđđPÇŽµyóf 0Ŕě8(dX(„ ĂĐÓO?­Í›7+""B5kÖ4;]ąrE7n”ÍfÓ–-[d±XÔşukůűű«˙ţ*_ľĽŮ\gµZfv Ŕ_¬ZµJýúő3;Ĺ+…ĹbѢE‹Ô®];őčŃC{÷îU™2eĚŽ ť;wN_|ń…ÂĂĂőĺ—_ĘŃŃQ:tĐG}¤ž={ŞtéŇfGňśŹŹŹĆŤgv ŕ$''kѢE5j”ŮQ€hvŠV ± .¨YłfŞWŻž6nÜ(ŢeQQQZ·nťl6›öěŮ#ůúúĘjµę‰'žP©RĄĚŽä«Ő*I˛Ůl&'\ll¬<<<ĚŽ<0‹ĹÂĘä#ţO PU¬XQëÖ­SëÖ­5aÂÍť;×ěHrYdd¤ÂĂĂ3 Ę–-«îÝ»+$$Dť;w–łłłŮBÜŠ€B®qăĆZ˛d‰úőë§ÚµkkÄfGđ€Ž;&›Í¦ĐĐPť8qBęÚµ«BBBÔĄK9::šŚb čŰ·Ż&Ož¬   ŐŞUKíŰ·7;€HOO×÷߯đđp-_ľ\§OźV•*UÔ«W/Íź?_íŰ·—˝˝˝Ů1@B1PDLť:U§Oź–ŐjUDD„}ôŃŰö_ĽxQÇŹ—ŻŻŻI Ü*--M{÷î•ÍfÓęŐ«uţüyUŻ^]úřăŹŐ˛eKY,łc€Šb °X,ZĽx±Ú´iŁ=zhďŢ˝*]ş´$éČ‘#ňóó“···öíŰgrR řşµ 44T/^T˝ző4tčP¨I“&fG…„ťŮä­[·N×®]S˙ţý•––¦/ľřB>>>Љ‰Ńwß}§“'Oš(V’’’®Ş\ąrjÝşµ¶mۦ#FčäÉ“:vě¦L™B!P\»vÍěČBn\٬ú¸té’l6›fÎśůŔç*ęRRR´{÷nłcI܇oEŚ———Ö®]«ť;wŞ[·nň÷÷WRR’ŇÓÓĺčč¨Ĺ‹›(ň3 ~řaőęŐK‘‘‘z饗túô錀ڵk›(ö¶oß.‹Ĺ˘ŇĄK«aÆňńń‘Ĺb‘‹‹‹|||Ô Aą¸¸Čb±čĹ_T۶mU®\ą\ͰmŰ6uëÖM‹E‹EľľľňőőUÓ¦MŐłgO-Z´H7nܸíoľůF˝zőĘ8&88XgÎś‘$ť9sFcĆŚÉŘ׳gO}óÍ79kLLL¦y}||4a„\ťÜ––¦Ůłg«uëÖ÷}Ť˛ŰÇÉ“'5uęTőë×Oź~úéýF.ň®^˝ŞI“&©lٲjŐŞU–í ę˝UPq‹a†Ů!ä®´´4őęŐK6l¸cź»»».^Ľ(GGG’E×Ő«W® 6hÓ¦MJJJ’ŹŹŹ¬V«úőë§Š+š(R¬V«$Éfł=P?›6mŇ›oľ© 6¨dÉ’’$‹Ĺ˘:uęčĉ’¤ß~űM>>>Zż~˝ZµjĄ+W®(·ą‰ŽŽVĄJ•T˝zuEFFJ’ ĂĐĆŤ5věXŮŮŮiíÚµŞWŻ^Ć1.\———<==uéŇĄ;ú|řá‡uůňeEGG«bĹŠ9ëćÍ›/l?˝ IDATU˝ző»f}ňÉ'UłfMMť:5Wç 7$%%©RĄJúí·ßîűe·Źääd•(Qâ¶ů+~ýőWU®\Ůě·)_ľĽ.]ş”ĺ5)Č÷VAń×ë[ĐîC‹Ĺ˘U«V©_ż~fG X`e ‰ŹŹWŹ=´iÓ¦»îżzőj¦ű€âč‡~Čx›vNĹĆĆjéŇĄ P… 4bÄ]˝zU3fĚPtt´víÚĄŕŕ` €ěúőëš0aBĆĂńwăî#G*==]žžžy’ĂËËK’äěěś±Íb±Čßß_ß~űmĆ÷{RRRĆţ›ź-™˝Áţćö›ír2ÖëׯgÚfĹŠöaí%Jčá‡Η>n˝VETT”žzę)łcÜÁÝÝ=[í ň˝UÜíúÄű䊀"äÜąsjÖ¬™¶nÝŞôôô»¶±··×‚ ň9P0-\¸PÍ›7×gź}–ícÎť;§yóć©S§NŞX±˘ž{î9IŇG}¤mÝşUÁÁÁü0*€üŃ­[7uęÔ)ËvŁFŤRÍš5ó!Ńť*V¬¨iӦ駟~ŇżţőŻűî§0Ś÷ďüůóň÷÷×ĺË—ÍŽ‚<ŔőwC1PD\ż~]~~~:qâ„RRR2m—ššŞÍ›7ëÂ… ů(X4`Ŕ 6LÉÉÉZąrĺ=ŰGEEiŢĽyjŐŞ•ŞV­Ş)S¦¨lٲZ´h‘.]ş¤đđp 8P=ôP>Ť@nqqq‘˝˝}–íśťťĺččńçË—/«oßľ*W®śęׯŻděKJJŇś9s4tčP5mÚTť:uŇŃŁG(gßľ}eooŻ-[¶Üw÷;Ö›ŇÓÓełŮ4xđ`µmŰ6cűäăăŁŃŁGëŐW_•ŁŁŁîÚwDD„^xáUŻ^]111sŘ A}ţůçJOO×Îť;5nÜ8UŻ^]ŃŃŃj×®ťŞV­Ş¸¸8]»vM!!!z饗4~üxůůůiüřńŠ‹‹»ă\gÎśQŹ=äîî®fÍšiÇŽűNź>-«Őމ'jŕŔjÓ¦ŤŽ9’Ł>ţjٲeruu•ĹbŃěŮł•––&IZľ|ąśťťµdÉ’»·~ýzŤ1BŢŢŢŠ‹‹ÓŕÁĺáᡠčŕÁí˛ű'ź|˘cÇŽéâĹ‹9rd¦9·oß.gggąąąéŰożŐďż˙®Čb±¨}űö:vě$éűďż———WFé˝ÎźŐuű«ýë_*Q˘„^xáíŢ˝ű®÷VvçE’Ţyç 0@ŁFŤR‰%d±X2~2“ éÓ§kŔ€ V»ví4oŢĽlÍwfăőööÖşuë2ť‡¬>î•)»×÷~ďCPHŠŚ+W®AAA†ťťťáŕŕ`HşëŹŁŁŁ1kÖ,łă¦8yň¤Q§Nť;ţŽśüđCĂŐŐŐ°··7~üńGĂ0 ŁfÍšĆ#Ź3$Í›77 Ă0ţřăŹlŤ=»×sÔ¨QF‰%Śß˙Ý0 ø~ýşQľ|yă™gžÉh“ššj´iÓ&[çżtéR¦×->>>c> Ă0~űí7cŔ€·ÍăÍyş5vćĹ0 ăß˙ţ·aooo\ąrĹ0 Ăxýő× IĆřńă3JJŠŃ®];cŔ€Fzzşa†±xńbC’~ßă8p ±uëÖLçá^ź Yeş×őÍŤű0·H2V­Z•çç˘(‚:dřřř‹Ĺ°X,w}0°ZµjĹŧź~j¸¸¸ŽŽŽwČĽţúëĆŃŁGŤ×^{ͨ[·®!ÉđôôĚ(¸qă†ŮńÜÂěb€[żCË•+g”,YŇ0 ĂŘ·o_¦ĺoذáľĎk†áíímxyyĺ8ëś3;Çxzz’ŚyóćéééĆŃŁGďYřP«V-C’‘±íí·ß6$ýű÷7 Ă0j×®mH2~űí·Ś6“'O6$.\¸­żĄK—’Ś &†ńżqßšaŢĽy†$cĐ A†aĆÜąsŤ+V†aéééĆ#Źö¬Ô­[WľľľúđĂ%IgĎžUZZšnܸˇ+VH’–.]Şgžy&GçżŰu»U÷îÝ• ŹlĺĚj^$©S§NJOO×ĆŤ%I%J”$ůúúfÚďŽ;$I•+WÎŘfooŻÁ«L™2<Ţ»mĎęł!«L9qż÷!(|(а€€ť9sF“'O–““Óm3;::jáÂ…&¦ňÇéÓ§Ő´iS˝˙ţű’$Ă0îhc†âăăµiÓ&EFFęŤ7ŢP‹-îú"ÜË•+W™Qp«ôôôűî÷믿֍7ÔˇC‡‰—'úôéŁĂ‡ËĎĎOPëÖ­µdÉ’őáĺĺ%IňööδÍÍ"‚¨¨¨Ű¶—/_^’TştéLŹ˝Ů¦J•*’ţ|0»AŞQن^~ůe•*U*ËŚí#3Ď?˙ĽśśśôöŰoëŕÁjÖ¬™ěííłě˙^dě™=z´~řáíßż_łgĎÖś9sôÄO裏>ŇńăÇU­ZµŚ‚ŤÜ:˙›oľ©U«ViöěŮ9Î{Żq,\¸PC† Ń‹/ľ¨ńăÇkęÔ©š:uj¦ÇÄÄÄHú_ńÉ_ĺĹ|gőŮU¦śĘ‹ű<E\É’%5eĘ?~\’$Ą¤¤hĺƕЋ‹39!wÖ®]«ĆŤëÔ©SJMM˝g[ýç?˙ɧdŠŞ:uę(11ńއťOś8ˇwŢyçľúĽqă†&Mš¤Ç{LcĆŚÉöqR|Ż˝öšjÔ¨ˇ/żüR+V¬PJJŠ^~ůĺőqĺĘIRÇŽ3msó­ě7ßÓąsç˛<öfIŇŔ•’’’ń¦ôěĚŐ_űČLąrĺ4räH}đÁš?ľž}öŮ,űÎJvÇn±X˛üľ»©GŹňööÖ”)S” zőęéąçžÓţýű5jÔ(Ť92ÇçĎJ·nÝ4iŇ$Mš4I_|ńE¶ŽÉJZZšŽ=Ş˝ńĆZ»v­^yĺ•{>řްaCIŇŚ3n+<{ö¬ľřâ‹\ď­˛úlČ*“”łë›ťű0---Çă‹Ĺ¸Ű+Y›7oÖčŃŁućĚI҇~¨áÇ›ś Č]©©©zůĺ—5gÎIw_ ŕŻ,‹|||´gĎžĽŽ X­VI’ÍfËŐ~TŞT)U©REgĎž˝cĄJ•­k×®ÉÍÍMŇźoµżpá‚âăăĺŕŕ zőę)22RĎ>ű¬:tč 'Nč»ďľSXXXĆ1uýúu•,YRŐŞUÓĎ?˙ś±ýűďż×رcuîÜ9mܸQuëÖÍŘ—(WWW•.]ZWŻ^˝m5Ă0TŞT)%&&*))IÎÎÎ9ëÝÄÇÇËÍÍM^^^:ţĽ$ÉŐŐUçĎźW™2e”šš*Ő©SGwíŁnÝş:yň¤RSS3Ř^şt©ćĎźŻ988¨zőꊊŠR||Ľ\]]3ć¨YłfŠ‹‹ÓţýűUˇBIŇرcuŕŔíر#cţOś8ˇß~űMeË–•$ýóź˙ÔůóçµvíZIR™2etíÚ5mŢĽY—/_Ö¸qătéŇ%íŰ·O^^^ęÜąs–}dvͤ?ß<_µjUµhŃBŰ·oĎr^oŽ÷Öď«Ę•+ëüůóJIIQJJJ¶Ć^łfM]¸pA'Nś¸ç* 7Í1CŻĽňŠŽ9˘żýío×§fÍšZż~}F»ěÎýÝ®›$Ő¨QC?˙üłŇÓÓ•žž®Îť;ëСCÚľ}»5j$éî÷VVóâŕŕ iÓ¦iÉ’%š4i’*Uޤ‡zHŞQŁF¦?˙üł4h „„ůúúŞOź>şxń˘.]ş¤÷ß_III4Ţ»mONNľçgCllě=3Y,–»^ßű˝gÎś©7ß|Sß˙˝ŞV­šĺ˝’]‹E«V­Rż~ýr­O9V@ľłZ­˛X,üôÓĄK—ŚBI1b„é™řá'·5{öl†‘­BéĎg÷íۧĽúřPŔmٲEAAA’¤_~ůE#GŽÔÎť;%ýůńć›o*::Z’ôꫯ*!!AoĽń†.\¸ éĎ7ä[,}ýő×ęŃŁ‡Ö®]«ńăÇëŇĄKZ¶lY¦…»wďÎ8oTT”Ú·oŻ.]ş¨gĎžš1c†uäČ‘Ű vďŢ­ŕŕ`IŇďż˙®I“&éôéÓ’¤Ó§OkňäÉJLL”$ 4H_ýu¶Çš™ÄÄDÍś9S’­·ŢzKüń‡ŐˇCÍž=[VëÖ­µrĺĘ,çűí·ßÖ•+Wtůňe]¸pA;wîÔŤ74mÚ4EEEI’žţy>|X’äâ⢽{÷ę©§žŇ Aô /($$DĺĘ•Ó×_-IŇüůó 'žxB#FŚPpp°ţö·żéóĎ?Ď8÷Ě™3őĐCéĺ—_Ö#Ź<˘É“'«lٲš9s¦J–,™e?˙üł&NśqÍćÍ›wŰjKĺË—W§Nť4dČ,çá˝÷ŢËďŚ3tíÚ5Í›7/ăřW^yE‹%[c·Z­z衇´˙ţ,Ď+IĎ=÷śĆŤ—Q I!!!šČ}X˛dI=ôĐC÷(śXůÎjµę×_Ő¸qăĚŽRěýńÇZ˝zµ:tč­·ÇÝőë×őÖ[oé‡~Pź>}+IJJJRBB‚¤?ďűÔÔTĄ§§ëŹ?ţ$ݸqC7nÜ$}đÁ1b„9myµ2ňŢÍ•ŠňcK‰‰‰jذˇ~üńGą¸¸§H[Ľx±bccőâ‹/J’ŇÓÓ­íŰ·ë…^(ÖE~f܇ +ź(ë€)*W®Ě"Ä!C”ššĘ[AQd¸¸¸(00Paaa÷uü͢î׻ᆱ   ňŘěŮł5qâD]ąr%c›ťťť*W®¬V­Z©RĄJ&¦3÷!E˙7…Ŕ-śśśĚŽEÚÍ•ZäęęjršÜłoß> >\‰‰‰JKKÓÉ“'ÍŽTäíÚµKŇ˙Vő)W®ś$éСCš={¶>űě33㙂ű€âĹÎ슾„„MžS˝zőĚŽď¸(^x˝€<çęęŞ3fhĆŚfGÉőë××Ď?˙lvŚbĹÝÝ]óçĎ×üůóÍŽR`pPĽ°2… Ĺ2ŕÚµkfGČ~É?ż˙ţ{®´)ʸÍSÜď=ŠŠ€˙Úľ}»,‹J—.­† ĘÇÇG‹E...ňńńQ äââ"‹Ĺ˘_|Qm۶UąrĺĚŽŤbŔÇÇG&L0;j§Oź–Őj•Ĺb‘ĹbQ›6m´qăĆŰÚ|řᇪ\ą˛ÜÜÜ4fĚ%''›”6{rúýđć›oňűK>HNNÖĚ™3őřăŹg:×Ůisż¶mۦnÝşeÜëľľľňőőUÓ¦MŐłgO-Z´H7nܸíoľůF˝zőĘ8&88XgÎś‘$ť9sFcĆŚÉŘ׳gO}óÍ79úÝ9&&ć¶óĄĄĄiöěŮjÝş5÷c>ËË{ä?Š€˙ş~ýşÚ·oŻ .č‡~PDD„$©ZµjŠĐ‘#GtţüyŐ¬YSC† ѱcÇ”ššš'Y~ýő×<é7/¦¬ů%·ç¤zőę*Q˘D®öy/\SEQÍš5*I’“““şwď~[›ÁËÁÁA+W®ÔüůóĺěělFÔlËé÷CPPP¶)čßŮÍgĆ8śťťőüóĎëÔ©SJKK»ď6÷«cÇŽZ¸pˇ¤?żţZ_ýµľűî; 6LŻżţşęׯŻăÇŹgÓ¦M˝˙ţű’$OOOÍ›7OŹ>ú¨$éŃGŐüůóĺéé)IúŕÔ¦M›ýîśx[F{{{ëřńăyöűôÝäô~(čîG^Ţ{ ˙Q ü×őë×5a•,Y2Ó6îîî9r¤ŇÓÓ3ĘmQQQzę©§ň¤ďÜV˛ć—Ľ“+VhęÔ©ąÚgf¸¦Š2‹Ĺ˘O?ýTŹ<ňľúę+…††Ţ¶ćĚ™:tčEUNżśťťłőűKA˙.Čn>3ÇQ˘D =üđĂÜć~yyyIŇm-‹EţţţúöŰoŻ=z()))cĹŠ%)Ó·ĹßÜ~ł]N~wľ~ýúűňrüw“Óűˇ ˙=xů=÷ ď8((şuë&''§,ŰŤ5JvvyóŢ­óçĎËßßżPĽĄł0eÍ/…}N {~ČŽ2eĘhŐŞUjѢ…FŹ-___yxxčäɓڲe‹ľýö[ł#šŞ d7_A‡™*V¬¨iÓ¦ičСú׿ţĄÉ“'ßW?áwçěĘéýŔý VţËĹĹEöööY¶svv–ŁŁcĆź/_ľ¬ľ}űŞ\ąrŞ_żľ8±/))IsćĚŃСCŐ´iSuęÔIGŹÍ´ďO>ůDÇŽÓĹ‹5räČŚí×®]SHH^zé%Ť?^~~~?~Ľââ┞ž®ť;wjܸqŞ^˝ş˘ŁŁŐ®];y{{kÝşuwlŻZµŞâââî™mýúő1b„Ľ˝˝§ÁËĂĂC 4ĐÁď™őŻ–-[&WWWY,Íž=;㡪ĺË—ËŮŮYK–,ÉrŚ’ôŃGÉÎÎN‹E’ôÇhîÜą·mËĚéÓ§eµZ5qâD 8PmÚ´Ń‘#G2ť»ěĚQv®ß˝ú÷Ýwłś—ôôtŮţź˝{Ź‹˛ĚűţAr Q„ä …ĺÖŻ°µݬ¶’˛P[WÝÓ\}iV”ۦą!™Yš'Ô¬p"[Ű\ÝĺežTŇ<‚šš™ć 59Í Ě÷÷G÷20‡{†aôó~˝|íĂuß÷u}ŻĂÜ×÷‰ű֮ųĎ>‹Şž›‹/Ć1c0yňdxzzBŁŃ(˙ÔÄßŔÚÜąš»ďľiii¸|ů2¦L™Á”)S™™‰ŽŤż[łµö'ťN‡ěělŚ5  @aa!îşë.DDD   ÇŹǰaĂڏ¸8Ł{Ľ©ýˇÚ{żąüEíţľmŰ6xxxŔ××;vě@yy9ĆŚŤFAˇ¤¤°˙~„††bĹŠĚďÉ€cóGďi–â~ý–üÔÔTLś83fĚŔ믿­VkT‡šsěiŰ)))pssæM›ě®ĂŢÜŮ”“'O"99¸÷Ţ{ńż˙űżĘ15sfísëaďŢ˝čßż?¦L™‚7ß|îîîĐjµ6ĺteee稰°ŻĽň "##qéŇ%ĺs×·o_¬[·Îě¨ÍůÔŚŹšµgkŢKDDD턵±””IIIqvŞŘŘX“Çbcc€Ěś9SNź>-7nrß}÷)çL0AŽ;¦üüĐCIHHTTT¨nł˛˛RbbbdÖ¬YJYii©ÄÄÄHTT””––Ę®]»ÄËËKHFF†lŢĽYĆŽ+_ýułňńăÇKUU•ĹŘ~ţůgńńń’žž.?ýô“¬YłFHBB‚Şńi,--MHII‰RvćĚ6lŞ>–••‰Htt´4ý5§©˛¦z÷î-ŃŃŃ""˘×ëĹßß_âă㥦¦ĆäŘ©#sʉµú­ŤKĂĎŤëT;7‹-777ązőŞdddIMMµ8V¶®ż†ąiOňňň¬® "r}-Í'ôz˝üż˙÷˙€ >\Ţ|óÍfç´ćţd0ääÉ“@üüüdăĆŤräČ 2oŢ<)//—ýű÷ IJJ2şľéţ ˘îŢŻ&Q»żOž}şôéÓGČ/żübtž©r5±5\×XHHxxx¨źĆ®^˝*ľľľF!edd(m©ícĂ7fެ©÷ß_rssEäׇ/ŁŁŁĹÝÝ]9nď™bjLĚÍŤµq1W§šąINN–:Hmm­ éßżżŮŘMµĄvnÚľ @tspD>Ńđđ}·nݤ¦¦¦ŮńÖŢźDšßw{ôčŃěş®]»ŠżżżŐkŐÜű­ĺ/¦ę5§aü–.]jŹŹŹTVVŠČúőëeůňĺĘqµ{rcöćŽÜÓ,Ĺ˝xńb GŹ5ş&&&F鋚sěiŰkă.ˇˇˇŞŻQł¦í} ńç .ň§?ýIŐś©ťWS± Y¸pˇ )..Vb±%§ł6G ó¬Őj•˛ yć™gĚŽŹµĎšľ«Y{ö潦đe""˘¶ŐDDDDDDDDDDDDDÔ"ŤFůżˇÓé{öěA||<ä×/é2ú÷ŘcŹ©®ż  ŕëëkTžصk—Q&ăk\®&¶Ćýj€ššŐ±7 ÄÔ©S‘••…óç϶lŮ‚GyĦ>Ú륗^ÂСC±téR¤§§Ł¦¦z˝^9nď©enn¬Ť‹µúk:7C† Á`ŔĆŤžžž€ÁŰ{kĎ ‘3ĹĹĹüýýŃ©S§fÇťqlÚđë~QVVfőZ[îýćň[ÄĹĹađŕÁXľ|9ŕ§ź~B}}=jkk‘›› X˝z5FŹ­\ŁvOnĚŢüŁ©–̧Ą¸7mÚ0ş¦C‡˙>šĄć{Ú¶—^ŻÇĄK—ĐŻ_żŐă(ŤçäÉ'ź9rDŐśµd^—-[___L›6 ÷Ţ{/ŞŞŞL~Ëé¬ÍQĂ<°=Âř IDAT{yy)eÉÉÉ€'NXmŻ±Ćź5}Włö™÷QŰâËDDDDDDDDDDDDD­äęŐ«8uę”ɇë ęzÔ9}ú´QyHHŔĎĎĎi±Ůâĺ—_F§Nť°`Á|÷Ýw¸÷Ţ{áćć uúŘŘž={Đ·o_DEE!-- >>>VŻi«1˛4.-1eĘ|ôŃGxîąçđꫯ"55łgĎĆěŮłmާµç†¨=sµ{ Łîý¶¶yđŕAěŮłsçÎŻヒ§žz +W®Ä‘#Gaô´={˛Ł´d>-Ĺ}îÜ9żćć¨9Çž¶íµuëVÔÖÖâwżű]‹ër´†ů¸őÖ[UÍYKćőé§źĆđđĂcďŢ˝xŕ••esĚöĚQhh( <<Üćö¨é»šµçŚ˙߀/µ’ŘŘXčt:Ěť;רüčŃŁXĽx±Ůë4 ęęꔟľŮłá[~ś={đŕ¶YlÖbµ$((“&MBff&>üđCŚ7N9¦¶Ź ߎZ[[ ”——[m{ěرĐëőĘ7î«y¨ÉQógŤĄqi‰úúzٰ°óćÍĂ?˙ůOĚ1Ăę‹m±ţ\EkďOŽfソ)[ö˛ääd„‡‡cÖ¬YĐjµ¸í¶Űđç?˙{öěÁäÉ“1iŇ$ŁóíŮ“íŤĎ‘{šĄ¸cccMÖŰšsěi۵µµxýő×ń›ßü/Ľđ‚ęëÚęˇđ†ůxüńÇUÍ™-źÓ¦ëfćĚ™ŠŠB~~>rssˇ×ë‘––fö|s왣†‡ó[’K©é»Úőé˙߀ś@ÚXJJФ¤¤8; «ŞŞŞ€Üzë­&ʇ†† ©¨¨PĘşwď.¤ŞŞJ®_ż.QQQ@ĆŤ'ŮŮŮ’––&=ôŃ5MőęŐKĽ˝˝ĺĚ™3""˘Óé$>>^ÂÂÂäÂ… ĘyÓ¦M“^Ż‘ĄíĆL•«‰­áşĆzôč!”6›ĆjÍĹ‹ĹĂĂC’’’ŚĘŐöqذa@fĚ!'Nś>ř@€äççK}}˝ÉvýüüDŁŃȦM›$;;[şví*¤¨¨HΞ=k÷™bjLĚÍŤµq©¬¬Ú¬ľĆšÎÍěŮł%::ZV­Z%ůůů˛k×.9~ü¸ÔŐŐ™ŤÝTüjç¦=ÉËËk6>DtăqD>amŻoíý©şşZHź>}”˛ččh •••JYĂ}żq=¦ö5÷~ků‹íűűŰoż-ŤFŠ‹‹•˛ŘŘX:thłsŐîÉŤŮ›8rOł÷† ¤cÇŽ$ůůů˘ÓédëÖ­ŇĄK ?ţřŁ8pŔę9ö´}öěY“×čt: Fĺűöí“ÄÄD‰ŚŚ”#GŽÓjµ@üüüÄ`03 âĺĺ%äúőë&Ű´öy2%..NČ/żü˘”Mžř ž~úiś:u ›6m–-[†%K– ŔlŰ/^ÄŃŁGŃŻ_?ÄĹĹÁÝÝcǎŵk×™™‰bË–-đ÷÷ÇĘ•+ˇ×ëńÎ;ď(m_ąráááčŇĄ‹ÉňnÝşˇcÇŽxňÉ'ÍƶtéRäääÜÝÝŃŻ_?dff*s§×ëq˙ý÷ăĘ•+F±Zăă㢢"Ś3wÜq‡Rn­Źîîî€»îş ß~ű-ľúę+>|/ľř"vďŢŤâÖ[oEź>}L~űq—.]°sçN:tüă…˘˘";v 'OžD~~ľÍc¤fţzöěivÔŚ‹N§Czz:vî܉ĘĘJřůůaűöířâ‹/¬ÎŤŕłĎ>CNNÖ¬YU«VaѢEX¶l˘ŁŁÍΗ­ëŻanÚ“’’|ńĹŕŻĂ‰nl-Í'Ö­[‡>řDyy9t: ző꥜ӚűSii)fĎžŤÝ»wŁŞŞ ÷ÝwNž<‰ĄK—Â`0@«Ő"!!«V­Âgź}ŕ×ý˘á[ľ›îńńńĐh4fďýQQQř÷ż˙m5IJJÂĺË—mÚßożývÔŐŐaôčŃJ™ŹŹžzę)„……ťknO>sć JKK•X‘8rOł÷”)Sđ裏âŕÁXĽx1˛˛˛Đ­[7TVVâ÷ż˙=BCC‘€Á[<§gĎžĘ_™PŰvJJ :wîlt~AAćĚ™ýű÷٬¬ ß|ó >űě3¬]»:t(–/_ŽĐĐPŁkŢyçěŰ·555¨©©AĎž=„'N`ţüůضmŕĉ Ddd¤r˝ĄÜŮ’>}ú ¬¬ ˙űß±{÷nlŢĽqqq7o4ŤŞ9S;ŻMפĄĄá믿FYYV¬X|üńÇđ÷÷·)§ł6Gü1®\ą‚.]ş Oź>ĐjµŘ±c–-[OOO“cŁ&OJJ¸qă,ö˝[·n4hŵŤaÆٜ÷šňÖ[oařđá¸ýöŰmşŽěŁqvDDDDDDDDDDDDts>|8€˙>ÄG7ťN‡;'jöŕÚͬ5Ćĺ“O>Á•+Wđꫯ Îź?Źm۶á•W^ÁĄK—ŇN{ôůçźcäČ‘ŕŻĂ‰nlĚ'š»™ďýDíY\\Ž;vSä&ŤyyyíúĹo""˘IGg@DDDDDDDDDDDDD7Ź%K–`ęÔ©|  GŹËÜąsńÚkŻáęŐ«JY‡†űďż=zôpH;DDÔ~đŢODDDDtóáËDDDDDDDDDDDDDÔŞŠŠŠđüóĎC§ÓˇľľÇŽsvHíBkŽËÎť;™™™8q"‚‚‚űöíĂÜąs±f͇µEDDíďýDí—V«Uţ×ŰŰŰÉŃŃŤ¤ł """"""""""""˘›··7***СCäää S§NΩ]hÍqÉĘĘÂÔ©S±jŐ*„……aŔ€1böíۇ5kÖŕ¶ŰnsX[DDÔ>đŢOÔţhµZĽńĆ8{ö,ŕ…^@aaˇ“Ł"""˘‰FDÄŮAŃÍeřđဵk×:9"ş}ţůç9r$řëp˘ó "˘öGŁŃ //#FŚpv(DDD7ţe"""""""""""""""""""""""Ă—\ _ """""""""""˘ByyąłC”––bíÚµ3gN›´×^úÝŢUTT8;""rQ®˛‡¸Jś®LMŢĺ¨Ü¬i=ś_×Âů"""˘¶Â—ČeŐÔÔ`Îś9řío‹   g‡cÇŽaöěŮ1bţţ÷ż·Z;í­ßíŮ{grśČf®˛‡¸JśŤőďßÓ§Owvިɻ•›™ŞÇç·-ś8qóçĎG}}=^{í5ś;wÎŮ!ŕ|QŰÓ8;""""""""""""şą >°víÚ×uýúuôčŃżüň ÚĂŻľjjjŕéé‰ŘŘX=z´ŐÚioýv–źţaaafŹ×ÔÔ GʏzőęM=N7›Ď?˙#GŽäśÝŕ™O4hĽŻ¸Ęâ*q6ö‡?ü˝{÷ĆěŮłťŠ*jň.GĺfMë±e~­ĺEÎâ踾ůć¬X±ź~ú)ÜÝÝqíÚ5Ś?ď˝÷"##ÖŽ=\ńóčhŤyyy1b„łC!""ş)đ/‘KóôôD×®]ť†ÂĂĂŁMÚioýv†Ó§OcÔ¨QĎńđđ@pppEDDD®¬éľâ*{«ÄŮXnn®ËĽ¨Ë»•›5­GíüŞÉ‹śÁŃq=zcǎŢE‹ŕîîŔĚ™3‘śś ­Vë°¶ěኟG"""rm|€\ÎąsçđřăŹăňĺËÎ…nÜWČ•µ×őëč¸DŁGŹĆ˙üĎ˙ 00ĐčŘwÜččhĽúę«i‹ČUđe""""""""""""r)ŐŐŐHMMĹĉ1cĆ ĽţúëÍľ´˘˘ůË_đ׿ţ©©©xřá‡‘ššŠ˛˛2Ŕ¶mŰŕáá___ěرĺĺĺ3f 4  „’’ŔţýűŠ+V`ýúő8q"ÂĂĂQVV†gź}·Ür úöí‹ďľűÎbĚÖâ€'N`řđáxíµ×0věX$&&âđáĂ6ő»)ťN‡ěělŚ5  @aa!îşë.DDD   ÇŹǰaĂڏ¸¸fý°Svv6Ľ˝˝ˇŃh0wî\Ô××rrrŕáᬬ,“1©Gkcöé§ź˘¤¤/^ĤI“,ŽCË—/#%%AAAŹŹÇŢ˝{Uőő‹/ľ@PP4 f̡\łlŮ2¸ąąaĺĘ•€ëׯăÝwßĹřńăqĎ=÷`Č!(..¶“ĄvŐŚ•˝óPXXW^y‘‘‘¸té’2.}űöĹşuë”ó¬őÉÜqÁ€oľů/˝ô"##qţüy$%%ˇgĎž(++ĂŢ˝{ŃżL™2oľů&ÜÝÝťţMľDtsł¶ŻÚC¬Ýë¬íe+W®D‡ Ńh•••x˙ý÷ŤĘ,^ĽcĆŚÁäÉ“áéé ŤFŁüł§%–îÇÖöKך;f0°víZ<űěł8p R—µ±R›?l۶ áááŘľ}»Ĺ2Gä]öäf¦ŘRŹąůµ'/j‹ők..{ó‡őë×cßľ}xä‘GLöéá‡ĆĘ•+qęÔ)u˙,őŐZL–¸ZîIDDD.JÚXJJФ¤¤Ř|]]]ť$$$Č„ ”˛~řA:vě( żúެ¬”™5k–rNii©ÄÄÄHTT””••‰ČäÉ“ĹÓÓSĘËËED¤şşZBBBdôčŃFí%&&ŠČĎ?˙,>>>@ŇÓÓĺ§ź~’5kÖIHH0Š€ÄĆĆÚOďŢ˝%::ZDDôz˝řűűK||Ľę~›b0ääÉ“@üüüdăĆŤräČ 2oŢ<)//—ýű÷ IJJ2şŢRL""iii@JJJ”˛3gÎȰaĂĚƤfŐŽYăq¶$66VČĚ™3ĺôéÓ˛qăF ÷Ýwźęľ.Z´HČţ󣾎5Jůy„ rěŘ1ĺç‡zHBBB¤˘˘Âll–ÚU»ćlť‡úúzٰatîÜYČÔ©Seűöí’““#ľľľ@ TőÉÜń+W®Č®]»ÄËËKHFF†lŢĽYĆŹ/UUU#Ęu#GŽ”ŇŇRłăd«ĽĽ<‹ź "ş1Ř›Ocj_±´‡ÔÔÔ˝×]ĽxQŐ^Ýě~Ő´lѢEâćć&WŻ^‘ŚŚ  ©©©Şâ´ÄŇýŘÚ`éZKÇÎś9cs®¤vOüꫯÄËËKţőŻY,kiŢeonÖ”ÚzÔĚŻÚĽ¨A[­_SqŮ›?üáŤF#z˝ŢdźrÚŚŚ Őă`mý•––ZŚÉWÍ=€äĺĺ9´N"""2Ź˙őÚś˝ď-^ĽXČŃŁGŤĘcbb”‡ŽŢxă  .\0:gőęŐ@¦Oź."˘<żtéRĺśäädńńń‘ĘĘJYż~˝,_ľ\9ާOźf<…„„‡‡‡QY㇞ÔĆóţűďKnn®üútt´¸»»«î·%MÂęŃŁGłëşví*ţţţFe–bązőŞřúú=Ŕ–‘‘!6l°ʵqT;f¶ľ `0”˛   ńňňRÝ×ÚÚZąőÖ[%99Y)›1c†ěßż_DDŠŠŠ€É–ĆĂZ»jÖś˝óа~´Z­R¶`Á Ď<óŚŐ>©ésCüżüň‹QŰÁÁÁ@.\(AŠ‹‹->¸f+ľ @tshË—,í!¦îuj÷˛†úMµŮ 99Y:tč µµµ""R\\,¤˙ţ6ĹiŠąű±š{ĽĄ{ąµűĽ=ą’Ú<¬®®®Y?›–µ4ďjinÖ@m=jć×Ö—´öúmWKň‡fůjcçĎźň裏Şîż­ëŻiL¦¸rîé|€¨mu‘‹Ř´i ""¨ĽC‡˙ţÚ«  ŕëëktNbb"`×®]€¸¸8 <Ë—/üôÓO¨ŻŻGmm-rss«WŻĆčŃŁ•:4MłPSSc6fµńĽôŇK:t(–.]ŠôôtÔÔÔ@Ż×«î·-šĆ(++3*łSĂ5S§NEVVÎź?زe yä‹í[Gµcf«ĆíC§Ó)?[ë«»»;¦M›† 6ŕÔ©SĐëőřţűďŃŻ_?Ŕž={ůő ŮŚţ=öŘcfc˛Ö®š5gď<4¬///Ą,99pâÄ «}RÓç†řŚÚ^¶l|}}1mÚ4Ü{ď˝¨ŞŞ2ą.‰Ú K{©{ť#÷˛!C†Ŕ`0`ăĆŤOOOŔŕÁmŠÓs÷c5÷xK÷r[îójÇJmćććÖ켦e-Í»•›ŮZŹ­ó«FkŻß¦Z’?\Ľx±YYcţţţ€K—.©ŽÇÖőg©ý¦\1÷$"""×Ă—Čeś;wpőęUłç4<ŔÓO?Ťŕá‡ĆŢ˝{ńŔ ++Ëćx‰Ú+GîeS¦LÁG}„çž{Żľú*RSS1{ölĚž=»Ĺqš»«ąÇ[ş—ŰrźooűľšĽËQą™łrřŕVűdoź`ćĚ™ŠŠB~~>rssˇ×ë‘––fWĚDDŽâČ}Ĺ–˝ jkk"‚ňňrŁkęëëQ\\ŚÂÂBĚ›7˙üç?1cĆŚż|ż«ąÇ[ş—Űrź·%wSĂÔĂâMËZšw9*7sdŽ×^×oÓ¸Z’?tďŢ˝Ů_˛jěÚµk€nÝşY¬§1GŻ?µ\=÷$""˘vDÚXJJФ¤¤Ř|ݤcÇŽ$ůůů˘ÓédëÖ­ŇĄK ?ţřŁčt:‰ŹŹ—°°0ąpá‚rí´iÓdŔ€˘×ëŤę|űí·EŁŃHqq±R+C‡mÖ~DD„4ý[Ź=€RŻN§ˇü¬&???Ńh4˛iÓ&ÉÎΖ®]» )**’ 6Xí·9ŐŐŐ@úô飔EGG ©¬¬lÖ·úúzĄĚRLgĎžUλxń˘xxxHRR’Ů8lGµcÖ«W/ńöö–3gÎXl/44THEE…RÖ˝{w UUU6őőÇ777yűí·ŤÚ¸~ýşDEE 7nśdggKZZš<ôĐCFí6e­]5k®­ó+¤®®N)ËĘĘ’»ďľ[ôz˝Ő>©ésCü ăÜŔËËK®]»&""z˝^üüü$!!ADDŢ{ď=ąí¶Ű$77WU?LÉËËk6nDtă±7ź0ÇÔľ˘f1uŻS»— 6LČŚ3äĉňÁH`` üü|©ŻŻ—ŮłgKtt´¬ZµJňóóe×®]rüřqŁű·š8M1w?VsŹ·t/·t¬˛˛RHhh¨McĄfOܰařřřČţóĺSe-Í»Ôä¤j¨­GÍüŞÍ‹šjíőŰ4®–äĎ=÷śh4ٶ±C‡ ÉČČu9…­ëĎŇç©+瞎@ňňňRYç6kÖ¬Y|·€Č޵k×FŚaÓuÝşuĂ ApđŕA,^ĽYYYčÖ­*++ńűß˙ˇˇˇčŐ«ţô§?áÚµkČĚĚÄÁ±eËřűűcĺĘ•pww7ŞóöŰoG]]FŹ­”ůřřŕ©§žBXXR¶téRäääÜÝÝŃŻ_?dff*}ŃëőčŃŁŢy硬¬ ŹŹÇóĎ?o5ž.]ş`çÎť8tčţřÇ?"** EEE8sć ¦L™‚G}Ôbż{öě©|;kŇŇRĚž=»wďFUUî»ď>śűLé{ll,ĽĽĽ,Ć”’’‚Îť;+×aĚ1¸ăŽ;,ΡšqLJJ¸q㬎ŮĹ‹qôčQôë×qqqÍÚĚź?ëÖ­TWWăŔÂ… ńĺ—_jjj””„€€U}ő÷÷ǵkךšŞ”@ÇŽńä“OâÔ©SŘ´i¶lŮ‚°°0,Y˛fÇĂŇ—––*qš«űďż;v´y`É’%¸rĺ şté‚>}ú@«ŐbÇŽX¶l<==­öÉŇqĽóÎ;JüW®\Axx¸ňM˝iiiřúëŻQVV†+V $$ü1üýý‘ťťŤüü|ěŢ˝/żü˛Ő~RRR‚/ľřüu8ŃŤÍŢ|ÂśĆűJll¬Ő=äÚµk(,,ÄúőëßëÜÝÝ1věX«{Ů]wÝ…ożý_}ő>Ś_|»wďĆŔqë­·˘Oź>0 řěłĎ““5kÖ`ŐŞUX´h–-[†¨¨(üűß˙Vµ×™úKćîÇ·Ür‹Ő}ÍŇ˝ÜܱNť:!==;wîDee%üüüĐŻ_?Ś?ŢâX©Éîż˙~ś?˙ůĎ0bÄDFFÎś9Ó¬¬ĄyWBBlsnÖ”µÜ¶{÷îřâ‹/”ą´4ż—/_¶5ĄÓéĚîŐŽ\żMăjIţŕďďŹO>ů Btttł>}ůĺ—ČĎĎGff&TĺÖúŞ×ë-Ćԫ瞍Űm‰·Ţz ÇÇí·ßŢâşČ:Ťł """""""""""˘›ËđáĂü÷!>rm:ťwŢy':äČ>¶ÎC\\Ž;†öř+ăăÇŹcěر(,,´ëúĎ?˙#GŽl—}#"ÇąYň‰O>ůW®\Á«Żľ 0 8ţ<¶mۆW^y—.]rr„Dmç±ÇCLL >řŕfÇ’““Ń­[7¬X±B)kiNA¶Óh4ČËËsŘ‹ZDDDdYg@DDDDDDDDDDDDD®mÉ’%:u*_p˛et:-Z„Ź>úČ١9ÝÜąs1nÜ8<÷ÜsJY‡†űďż=zôpbtd‰FٱúďűďżżaŰo-ź|ň ţýď7{ ¦¨¨ÇŹÇüůó•2ćDDDt3ččěČőáů矇N§C}}=Ž;ćěnJ-™­V«üŻ··wk…hłS§NaÎś9đőőuv(DDN·sçN@ff&&Nś   Ŕľ}ű0wî\¬YłĆ™á‘Îţë4ÎnżµtíÚ˙řÇ?đŇK/ᣏ>‚——.\¸€ôôtlŢĽŮ(`NADDD7ţe"""""""""""""˛™··7***СCäää S§NÎé¦dĎ}şŮăď˝÷hsn¶yóf<účŁĐh4Đh4@vrT^«†µ9tV]DDDD7ľ @DDDDDDDDDDDDô<<<đňË/ăűďżG}}˝ÝőÄĆĆbţüůVËn4?˙üłęsů™ ˘›ť-÷LgkÍX###áééi¶˝©S§˘¤¤uuu6Őűŕ⣏>RÚŘşu+¶nÝŠożý&L@FFâăăqäČĺšÄÄD,[¶ Ś… ˘WŻ^€^˝záĂ?Dpp0 33‰‰‰¨®®Ć ApáÂ+DDDDÎŔ—¨EÎť;‡Ç—/_vv(DDíž+Ý3Ű:Ö¶lŻ{÷îřŰßţ†~řˇE©ćŃGĹ!C¬ž7yňdôîÝŰîvnF®ôY!"""rľ @DDDDDDDDDDDD.ëĉ>|8^{í5Ś;‰‰‰8|ř0`ýúő8q"ÂĂĂQVV†gź}·Ür úöí‹ďľűN©Łşş©©©8q"fĚ×_Z­¶Em·Daa!^yĺDFFâŇĄKHIIAPPúöí‹uëÖ)ç]ż~ďľű.ĆŹŹ{îąC† Aqq±Őăß|ó ^zé%DFFâüůóHJJBĎž=QVV†˝{÷˘˙ţ2e Ţ|óM¸»»›Źěělx{{CŁŃ`îÜą¨ŻŻäääŔĂĂYYY€ŠŠ üĺ/Á_˙úW¤¦¦âá‡Fjj*ĘĘĘ+W®D‡ Ńh•••x˙ý÷ŤĘLŃétČÎÎƨQŁ0`Ŕâ®»îBDD püřq 6 ÁÁÁ‹‹3šwŔňŞí›)–ĆĐŢyłÄ\?¬Íµ=m™ëۧź~Š’’\Ľx“&MR5ĆćŘ‘+1wĎ´´_𻧇‡‡ă«Żľ˛ë^Ż&W2kS۶m‡‡|}}±cÇ”——cĚ1Đh44hJJJű÷ďGhh(V¬XÁ€µk×âŮgźĹŔUµwůňe%7ŠŹŹÇŢ˝{[4)))pssæM›ě®ŁsçÎpssłzž‡‡ÜÝÝ›•«ÍY­ĺS€şĽÖÚ>kK.hjŐöGM]jן9ö|¦>;M9;G'"""2KÚXJJФ¤¤´¸žŢ˝{Ktt´čőzń÷÷—řřxůůçźĹÇÇGHzzşüôÓO˛fÍ ""RWW' 2aÂĄÎ~řA:vě(Ö~•f©í$66ÖjYúúzٰatîÜYČÔ©Seűöí’““#ľľľ@ DDd„ rěŘ1ĺÚ‡zHBBB¤˘˘Ââń+W®Č®]»ÄËËKHFF†lŢĽYĆŹ/UUU#Ęu#GŽ”ŇŇRłă––&¤¤¤D);sćŚ 6LDD*++%&&FfÍšĄ/--•‰ŠŠ’˛˛2‰ŽŽn6ć¦Ę3 rňäI ~~~˛qăF9rä™7ož”——Ëţýű€$%%]om­őÍKchďĽ57Ĺ\?jjj,ε=mYꛩµmĎçÄž¸šĘËËłú&"×ç¨|šŢű¬í—ĄĄĄ&ďécÇŽ•ŻżţÚ®{˝š\ÉT¬ćLž-7nrß}÷Ů<ÖMuďŢ]‚‚‚š]cí_KÚlLÍ<¨É§ÔćµÖöY[sÁ¦s¨v]©©KͱlĚŢĎTĂg§±öŁ»’——çě0nüŻDDDDDDDDDDDDÔćőđŢűďż/ąąą"ňëáŃŃŃâîî®ďÓ§Oł‡µBBBÄĂĂCDD/^,äčŃŁFçÄÄÄX}ČËZŰ"¶ż Đ´}­V«”-X°@Č3Ď<#EEEfL۰aŐăŤÇć—_~1j;88XČÂ… Ĺ`0Hqq±Ĺ‡°Ż^˝*ľľľFžedd(íĽńĆ@.\¸`tÝęŐ«€Lź>]Dţű_c¦ĘLi:¦=zôhv]×®]ĹßßߨĚÚZë›9ćĆĐófŠÚĎAăą¶·-KëĂÔÚ¶ősbo\Mńe˘›ĂŤô2€ÚýŇÜţmď˝ŢZ®d*Vs^Č[şt©R–śś,>>>RYY)""ëׯ—ĺË—[ K/ Ą,((HĽĽĽ¬Će-ţđđp U}ŤšüÄÖŘ­Íšőˇ&ŻUł&lÍMőWÍşR[—˝/´ô3eŠ3stW—ÚVą¨—^z C‡ĹŇĄK‘žžŽšščőzĺ¸FŁivM@@jjj›6mDDDťÓˇő_ŁYk»%Ú÷ňňRĘ’““'NśŔž={ůőËżŚţ=öŘcVŹ˙›€€Ł¶—-[___L›6 ÷Ţ{/ŞŞŞŕëëk6ÖŔŔ@Lť:YYY8ţ<`Ë–-xä‘GЬŽÄÄDŔ®]»ě$ LŲ˛2Ł2ksh­oćCGĚ›)j?ŤçÚ޶l]¶~N썋ČŐ©Ý/ÍíßöŢë­ĺJ¶‹‹ĂŕÁ±|ůrŔO?ý„úúzÔÖÖ"77°zőjŚ=Úćş4Ž788:ťÎîş@Ż×ăŇĄKčׯ_‹ęi)kó f}¨ÉkŐ¬ [÷z{úÓZú™2Ĺ™9:‘9|€\Öž={Đ·o_DEE!-- >>>6]îÜ9ŔŐ«WŰĽm[…††ÂĂĂqőęUś:uĘäpÁęqKž~úi8p?ü0öîÝ‹xYYYŻyůĺ—Ń©S',X°ß}÷î˝÷^¸ąąřďCS§Oź6ş&$$ŕççg±îÖ¤f-őÍscŘZófĎZ´·-[ׇ­±µdíą˛ÖŘ/ťqOť2e <={ö`îÜąx÷ÝwńÔSOaĺĘ•8rä"""ڤv¶­[·˘¶¶żűÝEjÖ‡šĽVÍš°'lŹÚ*m«ťČľ @DDDDDDDDDDDD.kěرĐëőĘ7µŰúMll,`ăĆŤm޶­ězđÁ ťN‡ąsçťsôčQ,^ĽŘęqKfÎś‰¨¨(äçç#77z˝iiiŻ Â¤I“™™‰?üăĆŤSŽ5|űjÓ1>{ö¬Ňŕżß‚Z[[ ”——[l·ĄÔ̡Ąľ™cn [kŢěY‹ö¶ei}h4ÔŐŐµ(¶–¬]""WŇôž©vż´…Łî©¦îďć$''#<<łfÍ‚V«Ĺm·Ý†?˙ůĎŘłg&OžŚI“&9´˝–¨­­Ĺ믿Žßüć7xá…T_猇¶Ő¬5y­š5aO.Řšě]­ń™2Ą­rt""""ł„¨ŤĄ¤¤HJJJ‹ëńóóŤF#›6m’ěěléÚµ«˘˘"9{ö¬DDDHÓ_‰őčŃC^Ż—HÇŽ%((HňóóE§ÓÉÖ­[ĄK—.@~üńG»ŰÖét@"""”kL•™+¤®®N)ËĘĘ’»ďľ[ôz˝\ż~]˘˘˘€Ś7N˛łł%--Mzč!©¨¨°z\D”±©ŞŞ2jŰËËK®]»&""z˝^üüü$!!Áę\\ĽxQ<<<$))ɨ\§ÓI||Ľ„……É… ”ňiӦɀDŻ×‹Č°aĂ€Ě1CNś8!|đ ÉĎĎ—úúz“íVWW éÓ§ŹR-¤˛˛R)kčoăz¬Íˇµľ™cn 1o¦¨ý4žk{۲´>zőę%ŢŢŢrćĚŐ±5ýLŘWSyyyÍ>űDtăqT>á Mď™j÷Ksű·˝÷zką’©X­yűí·EŁŃHqq±R+C‡mvneeĄĐĐPłc#"*Śö‚îÝ»›‹ĆĚĺ^űöí“ÄÄD‰ŚŚ”#GŽÓjµ@üüüÄ`03 âĺĺ%äúőë&Ű¬ŞŞrë­·šŤ«)kó f}¨ÉkŐ¬ [sASs¨f]©­ËÔzxď˝÷ä¶Űn“ÜÜ\łuµô3eŠ3stW@ňňňśŃMĂmÖ¬Yłúv‘k×®Ś1˘EőtéŇ;wîġC‡đÇ?ţQQQ(**™3gPZZŠ/żüŕîîŽ~ýú!33Si[Ż×ăé§źĆ!CpđŕA,^ĽYYYčÖ­*++ńűß˙ˇˇˇčŮł§ň­őjŰľűî»1ţ|ˇ¬¬ đđđŔ{ď˝gT OOĎfu/Y˛W®\A—.]ЧOhµZěرË–-§§':vě'ź|§NťÂ¦M›°eË„……aÉ’%°xÜĂĂďĽóŽ26W®\Axx8şuëHKKĂ×_Ť˛˛2¬X±!!!řřăŹáďďoq.|||PTT„1cĆŕŽ;îPĘÝÝÝ1věX\»v ™™™8xđ ¶l٬\ąîîî€»îş ß~ű-ľúę+>|/ľř"vďŢŤâÖ[oEź>}ŕććfÔfii)fĎžŤÝ»wŁŞŞ ÷ÝwNž<‰ĄK—Â`0@«Ő"!!«V­Âgź}¦Ä ///‹s’’‚Îť;[ě›9ćĆđ–[n±{Ţ̶g®ÇŽĂÉ“'‘źźßl®ímËŇú¸xń"Ž=Š~ýú!..Îblć>'·ß~;žyć›ăjޤ¤_|ńřëp˘›Łň ghzĎ´¶_ęőz“űw—.]ĚîëÖîőK—.ENNóąŇý÷ߏ+W®4»ż[rűí·Ł®®ŁGŹVĘ|||đÔSO!,,L)ÓétHOOÇÎť;QYY ???ÄÇÇă—_~QÚ‹ŤŤĹüůó±nÝ:@uu5xŕ,\¸PésMM ’’’šĺ 3göďߏ˛˛2|óÍ7řěłĎ°víZ`čСXľ|9BCCŤ®yçťw°oß>ÔÔÔ ¦¦={öDPPNś8ůóçc۶m€'N 00‘‘‘Ęő›6mÂĽyópŕŔ”——ăŇĄKđőőEDD„ŮńR3III7nśĹ|Ş[·n4hĹĽ6::Æ ł¸ĎÚ’ ššĂíŰ·ă‹/ľ°Řźűďż;v´ZWÓőаţ˛łł‘źźŹÝ»wăĺ—_69®ö~¦rbSś™Ł»’·Ţz ÇÇí·ßîěPn gADDDDDDDDDDDD7—áÇřďC|d,..ÇŽ+ý*O§ÓáÎ;ďġC‡”‡čo7rßnTźţ9FŽéRź!"˛ó ˘›×ńăÇ1věX¶Y›®Ł;FŁA^^žKľ¨EDDäŠ:8;"""""""""""""r}K–,ÁÔ©SoȇĺoäľąťN‡E‹ᣏ>rv(DDDDN×Ńú)DDDDDDDDDDDDDÔ–´Z­ňżŢŢŢNŽĆĽ˘˘"<˙üóĐét¨ŻŻÇ±cÇś’ĂÜČ}#"""re§NťÂś9sŕëëۦíşJŽNDDD7ţe""""""""""""˘vB«ŐâŤ7ŢŔŮłg/Ľđ ť•yŢŢި¨¨@‡““Nť:9;$‡ą‘űFDDDäĘâăăŰôEWËщčćÂż @DDDDDDDDDDDDÔNx{{#==éééÎE•řřxüřăŹÎŁUÜČ}#""""ő\-G'""˘› ˙2‘‹áËDDDDDDDDDDDDDDDDDDDDDDD.†/µ˘ňňrg‡pSŕ8‘+©¨¨pvÔś?"""j/ř2‘ŐÔÔ`Îś9řío‹   g‡sò4Îýű÷ÇôéÓťQűŕŠűá˘E‹đÚkŻađŕÁHLLÄńăÇť’C˝÷Ţ{8p sÄvŔžµÖZó·yóf<účŁĐh4Đh4đëçvëÖ­Řşu+ľýö[L0ŹŹÇ‘#G”k±lŮ2@pp0.\^˝zzőę…?üÁÁÁ€ĚĚL$&&˘şş Â… pđŕA"""PXXÇăÜąsčÝ»7t:ťCűHDDD­/µOOOtíÚµÍÚ;}ú4FŤŐfíµćĆ977łgĎvBDDDDmĎ\ŕjűá˛eËĐŁG¸ąąÁĎĎ˙řÇ?pĎ=÷8;¬i:7ĘÚŽ¬÷FçčţŞ]k­5¦„††*m4Đh4xüńDZcÇTUU!99ׯ_WŽwďŢĚţĄ‚†ň†óŞ««1}útxyy™Ť#00“&MBuuuË:DDDDm‚/ą¸sçÎáńÇÇĺË—ť µ±)8{ö,4ŤłĂpÖš›iÎŐhŤţŞYkíiś»wýíořá‡0ţ|»ëyôŃG1dČ«çMž<˝{÷¶»"""j;|€nz'NśŔđáĂńÚkŻaěرHLLÄáÇëׯÇĉޞ˛2<ű쳸ĺ–[Đ·o_|÷ÝwJŐŐŐHMMĹĉ1cĆ ĽţúëĐjµŰU[wEEţň—żŕŻý+RSSńđĂ#55eee€O?ý%%%¸xń"&Mšd˛-Á€oľů/˝ô"##qţüy$%%ˇgĎž(++Ăőë×ńî»ďbüřń¸çž{0dČ+×ďÝ»ýű÷Ç”)Sđć›oÂÝÝZ­………xĺ•W‰K—.!%%AAAčŰ·/Ö­[§şŽgÁ€µk×âŮgźĹŔmŞ/^Ś1cĆ`ňäÉđôô„FŁQţ™ÓŇő“ťť oooh4Ěť;őőő€śśxxx ++ :ťŮŮŮ5j €ÂÂBÜu×]@AAŽ?ŽaÆ!88qqqÍúeiü[ş6\•V«ĹŰożŤ1cĆ`Ú´iHJJÂÂ… •ăŽŘ»ÔîAÖîµ–b5•ÚŐ'Ŕ|^`޵v7nÜI“&A«Ő*ýhřą)µą‡µ15wÜÚľhKß­ĺh—/_V⏏ŹÇŢ˝{•c–ň 5ąźµz˝˙«ť{âY°`ęţ:r­µdţZ#JII››6mÚdwť;w†›››Őó<<<ŕîînw;DDDÔ†„¨ŤĄ¤¤HJJŠłĂPôîÝ[˘ŁŁEDDŻ×‹żżżÄÇÇ‹ČĎ?˙,>>>@ŇÓÓĺ§ź~’5kÖIHH‘şş:IHH &(uţđĂұcG±ô+95uWVVJLLŚĚš5Ką®´´Tbbb$**JĘĘĘDD€ÄĆĆšm«¦¦FvíÚ%^^^@222dóćÍ2~üx©ŞŞ’ &ȱcÇ”ózč! ‘ŠŠ ‰‰‰‘ŔŔ@ĺřČ‘#ĺâĹ‹˛aĂéÜął©S§ĘöíŰ%''G|}}€¨ę#ÇůĚ™3F㡦n‘E‹‰›››\˝zUDD222€¤¦¦šW‘–Ż‘´´4 %%%Fý6l 9yň¤???ٸqŁ9rDHDD„Ě›7OĘËËe˙ţý@’’’”z¬Ťiii‹ÖF{”——gńłGD7†–äz˝^’’’dĚ1b0DDä“O>ňŻýËa{—Ú}ŔŇ˝ÖZ¬"¦ó€¦űˇŁú$b:/(--59Öjssýh¬ľľ^UîamL-żrĺŠĹ}Ń–ľ›ëSll¬™3gĘéÓ§eăĆŤ@î»ď>ĺKů…š±˛VOKsĂĆl™{ăQÓ_G®5Kç©™?{ó'kquďŢ]‚‚‚š]cí_KÚ´ÉËËsX}DDDd˙뵹öö2Ŕűďż/ąąą"ňëC×ŃŃŃâîî®ďÓ§Oł‡hBBBÄĂĂCDD/^,äčŃŁFçÄÄÄX}řĆZÝoĽń† .ťłzőj Ó§Oőń4´÷Ë/ż(eEEEfÚ°a Y¸pˇ )..6zQ€hµZĄÎ yć™gT÷Á‘ăÜt<¬Ő-"’śś,:tÚÚZ)..Ňż‹cÚŇő#"rőęUńőő5zŃ!##CsýęŃŁGłş»ví*ţţţĘĎ¶Žż­kŁ=âËD7‡–äďż˙ľďż˙^)««ű˙íÝL•çý˙ń×AüQ(ŕ‘:BEěR¬Z]Lk©R‹í6W»LѤ®ÎYWę¦vV­BfŮ`Đę2WA+eŤRf3Ë: ‹şZQUŃh5µˇXTŚ Î)x€ëűGżśŹü<‡ź§Ŕó‘Ă}îűşŢ×űş9×;9÷u¨7ďĽóŽ©¨¨č±µËťs\˝×şŠŐö뀻Ź÷ä:Ş Zr·ßŽĆŃ’«ÚĂUNÝYßÚZ;;ööĆÔô0yÓćcŚ 4>>>Îź]ŐîćĘÝ:Ą'ÖWóŇŐxÜooÜk]™żîÔO®â 5ÁÁÁn_Ókwúě,6Đ·Ľ rk×®Ő3Ď<Łôôt%%%©®®N‡ĂůşĹbiuŤŐjU]]ť$)??_’Öě//×Çąj»  @’äçç×ěśYłfI’Ž;沏¶úłZ­ÎcEEEš$Ť1B’4gÎśŰîîý#IŁFŤŇęŐ«•••ĄëׯK’<¨ď}ď{öÝ2§MmUVV:îlţ;{o@ôź˙üG’â<6dČ-[¶L#GŽě±µËťs\˝×şŠŐ]=9¦Žę‚®öŰ®jW9ug}kk]ěěŘ]ą;ףGŹ–Ýnwţ쪾p—»uJO¬˙®ćĄ«ń¸«7´7˝U?9•••iÚ´iÝŽ lŔ WTT¤)S¦(<<\ńńńş÷Ţ{;uýµk×$Iĺĺĺ=[ÓCUĄĄĄÍŽI’şÝGyyąJJJš=€Ö¤±±Q’´`Áëé§źÖǬÇ\YYYí¶,I í±1ôfž%iŐŞUzűí·ő /hÆ Z·nť•ŘáuÝ˝šĽňĘ+6lţô§?éäÉ“zôŃG5dČ.µu·îäßť{úٞ˛2I˙÷€rK}±ţ6qő^ë*Vwőä:SôU.ď®=\ĺ´;ë[gk˘®ę©ú˘+íôäú÷Ľt5wőĺďmGz«~:tčîÜąŁ'ź|˛;ဆÍô–.]*‡ĂáüöÎ>¤!IÎo”ďIMßdÚ˛í+W®H’˘ŁŁ%}ýͤőőő]ę#""Bv»]©©©ÍŽ_¸pAŰ·o—$ýö·żUxx¸8 ÷Ţ{O‡Cńńńí¶ŮôŔ~tt´Űcp'ζÚé) :wîś őĆohßľ}JHHpů@~wďź&ZąrĄvîÜ©?˙ůĎZľ|y—Úi©;ůwçŢľÎô'S§N•$%%%Éă<~ůňeíßżżÇÖ.w¸zŻu«ä^Đ“cęL]ĐWąĽ»öp•Sw×·¶t¶&ęjŤćŞľp·Ý®Ô)ÝÉOKwĎKWă‘úţďLż-ődţšÜąsGqqqúîwż«5kÖ¸}›7 ĐÇ.\h.\čé0śŚĹb1ůůůfĎž=ć[ßú–‘dNś8a®\ąbÂcÝUHIDATÂÂLËŹÖĆŽk$‡ĂaŠ‹‹Ť··· 40v»Ý:tČřűűIćóĎ?o·oWmŰív3yňdbľüňKç9/żü˛™9s¦q8Ćcxŕăëëkľřâ‹ÇÚÔ_MMŤóXmm­ 7’ĚňĺËÍž={L||Ľyę©§ĚíŰ·Ť1ĆřřřŠŠ cŚ1‡Ă3fcډ0’L}}˝łÍ¬¬,3}úôNŤˇ§ň\]]m$™ŕŕ`·ólŚ1‰‰‰f„ &33Ó8pŔ;vĚ|úé§ÍĆŐ–îŢ?w»qă†>|¸‰ŠŠjŐĎW_}e$™|Đyl„ F’©®®n5Ö††cŚétţ;{o$%%«ŐjJKK;ĚS_ĘÉÉi•sOwꉒ’ăëëk$™9sć´´4“`bccMcccŹ­]îśăę˝ÖU¬Ć´]´\{rLŐ-ąŰď˙ţ÷?#É„‡‡·?q˙ź«ÚĂUNÝYßÚZ;;vcÚž›ŕŕ`#ÉŮ—1Ć|űŰßnÖź«úÂÝÚĎÝ:ĄłëW楫ń´—Ç–ză^ëĘüu5v»ÝH2aaaÍŽź:uĘĚš5ËŚ?Ţ|ňÉ'Í^łŮlF’ pľ4ill4>>>F’©­­młĎšš#ÉÜ˙ý.sá.I&''§ÇÚ˛yóćÍ=µ±pG^^ž$iѢEŽäkţţţ:zô¨Îž=«%K–(<<\'NśĐ_|ˇ›7oęď˙»$ičСš6mšvîÜéĂáĐ‚ 4wî\ť9sFŰ·oWVV–ĆŚŁęęj}˙űßWpp°ĆŤ'‹ĹҬßôôteggwŘvTT”–/_®ŠŠ íÜąSgÎśŃÁ5räHeddhčС’¤7nčÂ… š6mš&NśŘjŚv»])))αܺuKˇˇˇ3fŚĽ˝˝őŁýH%%%ĘĎĎ×Á˘´´4Y­VIR||Ľţý﫲˛R»víRPPţň—żhäČ‘JKKÓ­[·äďďŻ|P6›M}ô‘věء#FhčСZşti‡cp'îäyôčŃJNNÖŃŁGU]]­€€9rDďż˙~‡mGFFĘŁ˝{÷*;;[»wďVff¦Ţ|óMíرC&Lh3Ż=q˙DFFĘŰŰ[’tď˝÷ęĉzţůçőđĂ;ű¸yó¦uüřqŐÔÔč±ÇÓgź}¦ôôt566ĘfłiĆŚĘĚĚÔŢ˝{ťmEDDČßßżĂü;ŽnÝ'OžÔéÓ§őóź˙\ţţţü¦őťóçĎëý÷߇[wę «ŐŞůó竤¤D'OžTaaˇxŕ%''÷čÚuöěYĺććvxÎěŮłµpáÂvßk]Ĺ*µ®ěv»’’’š­‡Ó¦MÓŠ+ş=¦ČČHmŢĽąÝş %wryîÜ9Ą¤¤čăŹ?VUU•,‹¬V«‚‚‚Úś?Wµ‡«ő«Ł×‡Ţîş(u\µĺĐÖ­[ő·żýM’ôŐW_éńÇ×¶mŰśýŐŐŐ)**JV«µÝúbáÂ…ŞŞŞę°ökŇ^ťrńâE}öŮg:pŕ@«qşłţwe^şOË<¶7Ţ޸׺2ŃŃŃZ°`A§ňWPP äädť>}Z•••úđõwď^ĺĺĺ©  @Ď<óŚŢzë-7»&%%E§NťR]]ťęęę4nÜ8ęŇĄKÚşu«>,Işté’FŤĄńăÇ;ŻĎĎĎ×oĽˇââbUUU©¬¬L~~~ kwŽÝńÚkŻ)&&F“&MęV;Ŕ=cîúűa@‰‰‘ôńˇ›8q˘.^Ľ¨ţţŃă;C[·niÆ ’¤ĆĆF]ż~]‡ÖúőëUVVÖë1ŘívMť:UgĎžŐ=÷ÜÓëý TąąąZĽxqżż'tŚzbđ(µÇ@ĂĽ@’,‹rrrľ1żčĽ=xZjjŞ~ýë_«ĽĽÜyĚËËK!!!ŠŚŚÔرcű$Ž´´4­^˝šŤp‰ÍşĹfł9˙÷őőőp4]sôčQIŇÎť;«ŔŔ@IŇ©S§”ššŞÝ»w÷Zß'NśĐ‹/ľ(»Ý®††]Ľx±×ú` µÇ@ÄĽô=/O ˛ŮlÚ´i“®\ą"IZłfŤ =U×deeiőęŐĘĚĚTHHfÎś©E‹éÔ©SÚ˝{·zčˇ^ëŰ××W·oß–———˛łł5lذ^ë €ţl Ő óŕ9cŚńt\bbb$IyyyŽŔ@”››«Ĺ‹‹ŹĂŤzľy,‹rrr´hŃ"O‡Ŕ Ŕ_ źa3ý ›`€p8*((đtčl€~®˘˘Bqqq˛Z­ŠŚŚôt8čŢžÓŐ«W•››ëé0 @ÇŹ÷t€AěęŐ« éó~­V«’““•™™)›ÍÖçý ď±QXX¨Ĺ‹{:  Ç”––jéŇĄ:räÇb5j”nŢĽé±ţĐwŘ €>———çé€uíÚ5ýđ‡?TCC§CŔ áĺéŔ“ţńŹ(66VˇˇˇŞ¬¬Ô˛eËtß}÷iĘ”):yň¤óĽÚÚZ˝ţúëZ±b…yäÍť;WçÎť“$ýőŻŐůóçuăĆ ­\ąR’třđa >\~~~ú裏TUUĄçź^‹EO<ń„Îź?/I:}ú´‚µk×.IŇíŰ·µqăFýć7żŃşuëôôÓOkÝşuެ¬TccŁ>üđC­]»VăÇŹ×őëץqăĆ©˛˛˛ŐضnÝŞ#Fhýúő*((číT ±Ŕ 6}úteggëęŐ«JOOWbb˘¶mۦsçÎé—żüĄóĽ5kÖčŮgźŐŰoż­˘˘"yyy)::ZŐŐŐÚ´i“$iĚ1Ú±c‡$é‰'žĐŠ+T__Ż©S§* @ RHH&Mš$Izřá‡őťď|G/ľř˘jjjôČ#ŹČÇÇGřĂ´uëVíŢ˝[˙üç?5}út•——kذaÚµk—JKKőî»ď*!!AO=ő”†Úl\:sćŚŠŠŠ´eËÍś9łŹ2 €ľŔfÚرc5věXIR\\śîż˙~-Y˛DAAA*..–$ý÷ż˙UFF†"""d±Xd±X”źźŻ˛˛29r¤Ý¶W­ZĄÚÚZíŮłG’4bÄÍ1CűöíSMMŤ$é_˙ú—–,Y"IJIIѧź~ŞŘŘXgŁGŹV||ĽJJJ´eË=öŘc •$ĹĆĆęÉ'źTFF†|}}ť×|ţůçÚ°aţřÇ?jĘ”)=-|S°Ŕ g±XZłZ­Ş««“$iňäÉ2Ć´ú7oŢĽvŰť8q˘ćĚ™Ł·ŢzK’tůňe544čÎť;zď˝÷$Iďľű®~ň“źH’ $I~~~ÍÚ™5k–$éرcÍâµZ­mö;oŢ<Ůl6Ýwß}î%ý›Ŕ…ňňr•””Čn··z­±±±ĂkW­ZĄ3gΨ¨¨H©©©zýő×őă˙Xúä“O&I’—×׏t•––6k#((H’ŕVĽ[¶lQNNŽRSSÝ:ý›Ŕ…ŮíöV×_¸pAŰ·o—ôő·ő×××·şvţüů ŐćÍ›ełŮôĐC饗^RQQ‘~ń‹_hĺĘ•Îs›ţŔ|ЬŤ+W®H’˘ŁŁÝŠ÷?řâââ§ýű÷·z˝ˇˇÁ­vđÍĹf^mmm«cŐŐŐ’¤úúz=űěł Wbb˘^xáegg+!!AżúŐŻôłźýL’4aÂ}ůĺ—Î÷› 2D±±±Úżż^}őUIŇěŮł!…‡‡;Ď}őŐW5yňd˝ů曺qă†óxZZšfÎś©U«V5‹×fł5ë«®®N’dŚŃkŻ˝¦¨¨(=÷Üs*..vž“śś¬ŃŁGëňĺË]KľŘ `PKOOWii©$)))I·oßÖ¶mŰtíÚ5IRBB‚Ś1:tčćĎźŻ}űöiÝşuşyó¦öěŮ#???IRLLŚüýýUTTÔŞŹ—^zIk×®Ő¤I“śÇ6nܨM›65;ďž{îŃńăÇőÜsĎé§?ý©ÖŻ_ŻŤ7*00P‡Ňť;wô»ßýÎď+ŻĽ˘ââbUTTč÷ż˙˝óxJJŠĘĘĘ´lŮ2UVVjöěŮJIIQUU•|||äďď/ooďÎ$ú’Ĺc<Đ111’¤ĽĽ<Ghb±X”““ŁE‹y:ţ2ý ›čgŘ @?Ăfú6Đϰ€~†Íô3l źńöt@W\˝zUąąąž<‚Íč— µxńbO‡a1ĆOÜçĺé@ç°€~†Íô3Ţ’ň<pß˙*ăŐĄ9ö$IEND®B`‚libtorrent-rasterbar-1.1.13/docs/troubleshooting.rst000066400000000000000000000012001351156116000226450ustar00rootroot00000000000000================= libtorrent manual ================= :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none The following troubleshooting chart may help in finding out why torrents fail to download. It is not complete, please submit suggestions via pull requests at https://github.com/arvidn/libtorrent or to the `mailing list`_. Ideally in the form of patches against ``docs/troubleshooting.dot``. .. _`mailing list`: https://lists.sourceforge.net/lists/listinfo/libtorrent-discuss |thumb|__ __ troubleshooting.png .. |thumb| image:: troubleshooting_thumb.png libtorrent-rasterbar-1.1.13/docs/troubleshooting_thumb.png000066400000000000000000001172521351156116000240370ustar00rootroot00000000000000‰PNG  IHDR ‡mťiËgAMA±Ź üasRGB®Îé cHRMz&€„ú€ču0ę`:pśşQ<bKGD˙‡ŹĚż€IDATxÚěý :Űľç‰î;Ö­ę[Ő]÷]ŞßăU÷˘«űUw×ëę{ëÖÎąg>gü§Lr"UTTTTEQEEEQDEDDADD$‚€ ^hN¦9i˙ĚÜŰďďÔXkĹŠĺúÄšël§ťÎ…ŔŹúÚQ1}óÚŘéͨđ«Ú)Xv€ěôSHXÇe©>˙`ˇô\Ňg†EŽ8Jć)™N¦G}*™Ę R,9†Ň„˙(ŕSÔ'Á˝4޲ÓO@8 M›Ĺ˘7‹"•Á¦)Í„Á,fÉäZ–ŐÉĄ” OŞĐę´Í摉0Ýň’>¸+AvúI(ŕĹĐ&€"(Ўč˘)f˘-Ľń±¶8‰ź[¶ĎGń(«Á(Ś»E; •ěŮéGŻ(E§ŃjÎĄŐËčJťîňřňĽ’­Ńju:ĄúňŚVŁ&Ź_;ň/Ą ;]¬W/U/¨żHJŐ[T¤pJµµs•ÎŹvAČťn”™űw]łËGŻżŻ§ ;Ý”:÷\OpKŻĹݦ ;­ J‰tíűť4ůdłŔŢ˝v€ětMpF¬i>čjŞÖŻÓŻŁ ;­ÎIÔŤMÜGŘŢŔŮűם.…äĄĘÚ¦ýQynúµăű5´d§s!E™˘˛EwmOě„7wý^µd§ĄĐ˛\^F°9 7ň27)ƸÇĎ~´Ă ;@vZ­*dĹEÇT@ăS ™ }Ex5¬Cfę”ń×~€Óť0´®’äĎúm{_lG¦|Sź5^´uş÷ťV÷ÚŹđbÚň“ÚPK˛­‰ŽvhOd*{+ĺî`,öŁťŠµä'Ż–Vśľjlç‹[z‡ťŇţ–^Ţ•v€ü´ŐÖ‰RĐÓ‚ČpóŻý/¨ ?eu ÂÄńŔŐz~ĽOv€üD…VžI۬C÷:ťÖüÚôBÚň•ă_GžmÓÁźüeíµźče´äť y’0÷żřćßö1ô^G7G˙îrʱ˙ěŹţş?´‡Â~ŁÚň>eJ»Ű .©č?ţĺ_ýGŢýAHůľµ›Ö¸wř ţĂ˙Hß"FRľ÷µSpCíy—‚yµáŕ)N&“BedkwŤčÇw7šŚ[čfŘoU;@ŢĄ`ńäĺoŇP­ťşž-ě¦ňĺă˙,Úň.µHÇT5Ýě®…ŁU´ż=€~támy±Pľ8YôLWÝÜČPí»f‚KۤĐU{†§ˇ{z|axéşď©îŮéEµHíwžďJa¶Ěaą¨×šWJ0x:Ö©ö}yTĐG“ˇ‚ŰíŤwGápČ\Î8ľřŞA­ń×VŔ_)„BŃHšy­+ę6@zČďÇE_69ě˛Ő5_yĘISŁ7îs:u®ń{-”Šäca¬Nš XŢít™Cˇ´Ą>Iimé §čNjL;@vzQ­Ňř­ń{ŰžŚ¦¦{aÝţA{™Ó4GL¤%}ú˘šŮż&ČN”{ž“P‹B%‘ö´Óăý—żµ“ĺ$±!i©Ćź3™“řµ×˙m€ >Žżűáżđ̇é6‘BÝc‡'§§úoéQő…těžHşó±Šd‚=‡T‚xDR~vŤßOkG$˝üó SXĚ v€ěô˘Z/AHY‚€2¤ČĹ9—LEĹeLg4J0§BÄ:±˘Šâe™řYĘf %<˛SŔ5G\˛CJőbN»Ă Ą‹Ú…k7ą Ξլ7rY)ulÂcËŘŚ*(•JEćâ‰ĹÎ_Ä=‰Z’”0Ş"q%š9Ë‹‹ízŤí¨D Ť#u“Đť^Tk€ sxÎťÂ8źCY‚Á â@p`6 €ŕ „A[®ÂŹŔ…9Ŕ3d¨ˇ:‡ë+oé2¤9M  A/VZˇxŔsĹo‡A‹ŘŔďŠ,ă"¸Ź94Eˇ]#}§—Ő­˝XčĺÇú’Ŕůĺ1zŰÜz“]/¶äťjČtešů¨Ža}ĎŇŕaYqaŐ 'Vq@Kř[<’»¸Rh ţ!†®EwXĎ[G6¤7^AX·žß˛Őě†!ˇ ;˝¤€XłzCÖš˛ő¬á2:˘żT.Ń>ë…°%•Fćŕ§°2ę Ľ¦${\ÚĐaÄ“+czżÓo´„R^ŽĎ›ŔIňuîl"ȇܧ±Š-a¤{ň22YŐ±€[§Š8\Z/ŁŹMŁ^A"Ä1Ăńb±\˛9 Fk0`wëÖ׌ěŮé%…‚°rŇŻĺźĺ‚eÇ$>‡ÇŤ,žĆˇ U4Čőń·IL ü“F641„qJâÓ(zćpd˘źéLć*˙\†Ă :á;J‰Éř^uđ˙SEűJŰş %ąíHDűAKŕ‰X"i‚u%lĺĂbPĚĂ!OBU2YbâwYŮú*÷ ;˝¤–%Hâ‹IŞPK]¦A'ÎKäŤ-‘Ë©ÔBşôHä×Í­ü#&ŕY.ZHÔl·¤BąRb33Y›ŤçŇjÔ"…M®a$Ű]]!ăRśJ•Tk¤qTB3KĺĚlR‚ٵMěUxůV™L’ëT !›#Ő3ĺvk&o\ę Ć&¨č»k>w€ěô’Z¶ARü9 á˙Ŕt¤µÜćAÎ6»A;b/€·) ÂÔI ˙@Đf$-ŻÁgás<ƧîYś‚ń3„ÂТ ˛ž‰o¶AÚ“łMtĐEŘp!\F0děâYžĹ㊠ÓֺǦⵓpCíy—B8r˝^§Ňé—2ô×eĐHXŞłŻ*Íů)•Z‡nx??-\$I6čď“a’L»rJË‘WÖ]Š66śňĘÚň>Ő+–îTĄäe~f×NŠô•Ň–ZßÜ$„"1şz¤ü NÝćmřÚ)¸ˇv€üč4O+d±ŮŤÓŽř׹ýú†žc‡0řl+żľv€ü¸„¶íCő¶µŻ† müđşÉňxíů1 /<ġ;ڏ˝ 6°ňŔ«ĄĘ“´äG#´ëč+w\M@–„€WI–'jČŹD`V) Ü·ąćë‚a}‹ŕ="˛äÇ ´çjK÷›€{m@pDĚ‚đ»k®ďy˙ój‘ďÁnÓ×ĂzfÁóŮâú:ÚňŢ5đ ŐĽđCđď^îAČ×Řč•4a¸ëjĎtޞŚďüÍŰÇÚň®5BĎŮLY•Ű-Vjşw:5ţŚ­×úľBś>’ŮwŰłîQĂFÂŤ§µľůňdČ;ÖĐ/Rĺ.d„N,×AôNdžż!ýĘ(ř ±úýÇý#…í8"1J)ĂAďk¦×c´ä˝ .ë„®ŢU%kíęsń»K # uľBÄHđ2~ Ťô+u”ż =}ă…Ż  ďPpŐ pt¶lßľ-@đŠ–ţ9ö&yqíywš„Ĺň°µ··Čbw«w€Č÷%¤fŘZŹé}{€`XK'JÎ_ÁĚđăµä=i•ČłÇů}‹€,!üË_<Ë6W/¤ ďFHĂ,°4˝ŕ›…ˇ+!ŘĎżůŁßĚVO=]đ3?îy'šĹeŇŘô Ľ@P9O´"ńŻ˙ýżűźibŃsŠ«yľÇŮň„¶¬Sýi[Éľ@`^gş*‚ŔéłjÖ<_˛äí HĘ%‘'·dß č)ĺŕFJv€üdt÷Ú-őĆéŰuÎŘŤ‹0ŠVΚěÝŸ'ŇN/ľ#+Źá^çß|är}Ľ‰žîůÉčľ5´[Ę˙:QŢ‘s/đŮßđŽpŘáu%b PÎóÜwÄZIúŕBČĂ´ęĂH!Ú\ŮĎkŹůSv/iůP®VŠşüÎ/¦ş?—PáPßٵË[ěůič5´Ű)ţ·ż(?=” "­ű˙î%ú˘Š•'vONŽÄM“K•Đü?ˇ@53Č'<*qŹÉ0WGüx 4lĺçŽCŞHt˛wŠh”_(J Güâ'(żIOúÍýßÚ)d%őă™9ş ?~•‹ĐCkh·“ňŹţ$üUbţłoţÍ˝‹·.ÉŇš<U>MQ­ ‹Unä†P·RŔŠą"é‘ö1<Đ"ŇŽ¨O.Ó‘ăBŤČ!`r¦*ě´H‰vN–-łř!q 3(’b ű”ŞZ’±äGŻČżů÷b]ů™ ŹĄÚń×_gí…ýŹuďő @`ćó1ŠŽçsĹf;08ćXćŘć~…góůl†ÁsüšCch>dNgsđSsxŁAŃ9ît,Ţň†5k>]-Í_|ó'ĆNű>7ge °y˙ŰŻ:»Ý`hľ#´VöŻdwÝXxÜ  ť.qÇâĆX0dň–ÍUŔí6+|‚Ľâ-tŮĐdqN›Î†Ís?$÷´ˇT˘ zŇyL©‹ämúH$ťLG™\M ś…Ľä K]ÇPäúďóđŻ…n7ÎŁ(ß´•­BEďĽÓęPĂF{žrö¤= zv×’~ń÷ŢáŻ5D5DfaÎď>;I,ăŻ÷IâÓÜ„xz.ł´Ďěp‹H5üśÇâMGäc;†Mľ ¨´ĺi¤đ¸(Ď~č!rĺyĺ!•o&™Îm;@ްp@âó(oOĚz{ř"Íš–¨Ă„”ĚnˇÉš•mv«•©÷…>·F3¬ZqŇ`BŚŁ=¬79m‡ĎĘ&1ŽŮéCÓ§ŔlIC.ľÝj¶›ä·öV@Rż(š 6+ęśv‹ą×2ÉU<“C+réś>˝Ŕa‰d»&ą|Ú·Ä]ĽÓ(‚*żÚ"łćsĽ•–Áć%ČžŰ x‡Â˛•eD¬…Dg°É”„|O(Ź™Ívź'o˛zör˛,vśhC0´ŕPü;¨SÖ±ĺ$_‚`Á›s†ůbŽ.áíd9_ŕnĐĽuĆjˇK' ÜÖ_,Řň†µ$÷O~xřĹL–ţ‚®H4ég˱ęÄ 9÷(M‹ńŤOÉDݡ@Ŕ?:%ýBÁ"©B‚c h*‡Ŕú9ĂM%óä,”w¨#LŤidÁ©“H~FçśÇťb˙6@@‘ÎQ=hŕ„ĄÝ?"KÄĘ)‡b ¦ÁYCdeę§'§µÂ!í”"ˇÎ ´ßk>šřZ†¶_\y†ÍŮ×ďu‚"őçĘfžJŇţť…ÁŁ[ON?Ôjd:koź.8%0™lZ7˝¨ü\Y4Í‘óýŞŻÚéPY›Á—n®ÝČö8[ËuĎ/‚Ůň†µ¨bńí9›{)ŞŹKśiô*^2^‡0‡PĄ·8BXÖÓĘlV§W)–«t2ˇA­6¨÷ °)ĆŹI0‰”ü„ߡ yźÉE "µŢiĚbB^HDÔŁěě6@ZÁ.O•ư–ĽhŽ„­¦R­3Y>mRpx5ü¨VPk+ÔšvާÖhŁZ¬đŮŻ1ë N0ŇXݨyó*–Řkď5tP9éë.MŘĺÇ5e©ńŃ«é„v·S”ČÚËͧŻ2*Á 3 ĽLč¶rx§L3ÂęšŇÂeő˘Čŕ|Qn”e 7][8XřĽ‡—3¸ °)[”>X_zFČ7,ł–Vđ-ŰËł‹żËÚůÂÎů|Ń8;±¬˛cË EUfŮ8AÔµł6ĘŮĄóĂcćôE»»‘Ž_÷”VĎ—MžłVvČÖĆ*ÚECąŃĘŢĽąÎâĐł§]6Ą°¦>kŢ,›_g7¸HEĎgY„,µÇVu™×#ŚN-Ş#'PÁV®[sâRÚůźĎQd&=Z?>˘„´l†Ĺ *†Ŕď´đËuŠ Ż9úŤ2¦ĆeYLGg © oX @ÎtžWű‡&ř÷éŮD črţúčěµ7˝h%_ü¸ČđâŰlŽ‚če€K­˛b÷ĆEűË+pűriîôúŔĂŻľĚKʮ͡‡—G´9 Č=Cý׳(8†×É3H”’°÷>gµź>Zčf†W.SůßĹaŃÇ‘["ř|ŞúČá§&Š›Â×ďŹÚd ‹uô˝l˛ _NNľś*[*QďÜ˙‚×AD¶”Ǧ4–‰¸ä ë 3A‰~w>¦łńC3ţXšBÓ”’ăńp2Né18›ÂÂ"ߎÇóÉt<G(6eÇ@śŚ˛J']]»¤Qőˇ<šćŘ$;$ŤÉ|xNŚ3‚â čřŮéd„aąţ`˘Áé¬[Ť Í 0śŚ i!›ź Ń l šJÖg“)~óaç|ÖČM§ÉÎd6¦Ó)~ËŮp„ř”1đ: mŹŰăĐű\6_ČčyúĹş¦ăž2â0Oň›Ík7xüXŘîÍą+ľ´c6wÚ|N=ŞÔý—Ëk‰g]_ÄZަýIĎ$_Ö•Ś÷2őw€Ľa­RQ*¨4![(: c”w˘9Ňh4…ŠSq¤Óč"ë WĚ3-,Ýi¸T…D(ňŬ6§Ę9™€qDß7ąB×˝Dž"‘h GLębu™&§éÄľr‰K¨µJ˛A µN `:ę!_"˝¦«Ź4Zeľ$Or"bT±śI¶1•ćĦ€ô$R‘Ž·/9±ĺÇÔý 6Hfá!O!—¦ E)…§“Ĺ˝™)+ ą˝XŹ„ř‚Eo3I„ňţX<-ĄmG˛t$ťňŻM!ż¤ü”ʇŢ î(O¤R…bˇÓhä3‰PŔI—ˇj0Ǥ’–·Ę'‹E$Rx$:őF=žHćSžjŞęo– řé´éHëo ĚçsĄD#UóŐ’ÉxĚ/Tbáx*Ě–šő&WÖ 'Óˇ|%Şř˛ľé ťZ»?Ş5Ű­d˝ŮîőfPc4j Ú-4ťęô°ycÔNđ™˝\rĐî”+­Ć RwNńVKgÔî—Fe+Óőű@Űlv{˝VĹĆŤVw8Âă«ďyËRäf×- <%`1čUSAáÂO̦ř'°Đlónµ9ő6ć¸ăĹ©Nµąřś·ťüsŇ»ę´ĎwS§iÍ9\„ĚšŐ.pÜY¸ó‰/ąhý,ÎDróe¸ýj}ştráXÜoq´üZq•ű3Ŕëxř9Sz`:.ÂoľđÝ®¶Ďn{ ś–Ź»<7ĆM.ŤŤ,á0|9ďX(cĘ™F¤Y%ÚS6kŞ!©é«7ÄŠŮ}=GÎůV¶ÇaI÷OżŚ°âš‹Ć>9Ńą,¦·V1ú”žPĂ<őa€ýEµOרɜzČ[–›#^“TrţE"‘Čřź˙ž(Yw!–°>K—ŻÜ.Ź–źg§ÖýËÚ¶—Ťű“®yą¸Żč».OHĄ'”ł€nŢ}5"‹»I©‡źłÁĽá÷<čĎÜ«çćţâčň†–oáqOsęÜ?==â ŽS?ۆYĄ2 ëôpä<\µýă1—Úń°™˛ď4‘ŔI>=îaY*‘ţ‰Čţ …D˙,(V*,•žtâ<Ů;m`“c+1J“¨©TS2Łßň–…ŢmÎ ęV®,9˝ĺÚ3ÚŇ8úĐÝŕ‚PŢ\qo #^ŰäAď˛őŠ:W—Ŕ4OÝB.Ž—q^ŇćşŐŁ@©U)Oł­öln.ĎŞÓj7ę=lďËőÚ|ÚlµęýÚ0Ó«ŐŞ1W R«–*őzˇ\keĆ˝Ţ ×nç›e\î@ˇTë×&ŐA­›Hôv€ĽOŤŁ2Iŕ.Ćä3ß­«•”®e“W˛jEů¶kkÔ/é•«ÓŕĂ‹'ÁŮť‹¨ŕpő¤‹ő…;@ŢźŔ‚N`˝Çô[Iů¬·Ű‰µ<÷jfß»bOřánŢŃů’haí©ÜňNŢo%lČ;ŇrŐŮ{—„CÂţ¦ˇ=¬yçÖOľ˘]¬±ťąšŽx7 ©P«‰Íkaa·ŢnU+ăăĨ’`«Ý\§ ÷ňťJż]«µ‹ĄJ!źkrwZńŢň®4 K%Áwivm°Ěu3!iˇů–Ű˝Şá¸ľQ:/ŃîÄHPĐ[VŻű„-ÔPŹ}łŁ“oy šHú…Áew ,6—­fńřD]H?dJŹď\Mąäýhž× m›do‹źÁz".´*Sµn»đĘ–[I~ů„÷â#ň¤]™(¸ä î×Ažć*›­%čč:Ŕh"™M,Ö—ËäĐI :—q§ş ďDHÓ.Ôä6ł¶Ęęą{@]ť¸x{îxmÓŁhU!Ż ÷t{ClVťôęÝv«9ÇúĂQuÜő†ĂA ŽzŤádŘčw;ÝN»ŰŕňďL° ďBĂTŢÜň[x{+74±óăwmjöÚ€ŕTUGaFĽp—ŠĹ"ţgńqöíâkńâŰʱǛż+{Č[×<«:ZŰÔšF‚§vC|pçŐ×Ăŕ¬XÓ č Ď!“éÎKşĐń}@;@^@HĂ*Đĺ·5~¦É=í¦ˇéľ®€·ŽHJděmÖĐg×0 ‘Ea‘=«}ÂMŃšLŮĽ×ĹŰoÄ…Ćî׉ĘshČó Ȩ…ÎöŁŞŔsÁăMU÷tâÂ7}+€ŕUÁ¨ŔôŚŁ>/« Ď(¸n苏¶+k‰<ŇăÄÁŹ=¸ářŰ„–möÂ}Eíy.ˇżX{ĘNiuŮŁĽaľ{­ˇß xIä[ß";@žG@J%tužÖ»ŰŹđ”7Ęio <Ĺ|Űó ^_;@žApŐ,0”¬ä<(ß+ůÖT—+›ą|k€`ŘĚĎ·żyDv€ÚsjČULcůüµ‡PŇíÉ“2`BM&ţÂÄÚČ-Ţ6w-ŰćYâw҇+g®8˝ăËłĚřşWż˙¸w¤Ř|ŢĚt‰LŇžXdDŕĹc·­v€‚Í.Úúý•k´?źýÉ_?C#ÎH”t<=¤'jČť‚袢Vhk|Ý tă˙é›Z;Ĺü+¶˘v—ű I—­ť;­ng‚őÍnzܲ)Z^DŻ› űشΛ­VŻ;ě¨ ̦ĄĆÍ0·?îôšč”ëŚń«ÓΠ:é‘úO„ůÍx–óRő§˙üę‡;@îŇčçôŻ…Ż?9¨ôŻ˙ëőę?ůćďî®n,«Xb·kź!`í)ćĹ6obľŚ$<8=AôG{\)%·Ó"ť-&ZÝ{¤±c_Č×Dű'\>É•ÂájŮűdČ® îGůň)&M9ů-%B&·ml*YL˛0>üŕĄç§ÇÔ_*,,“ôéó3á¤} ăMNůźčÚÍ\*Šë7É)VŻë¨ůg˙âź7 ěüćîçÎÜUą:ĆjéjŞ[-WŠů\Ą^Č‚wRćŐ«©Vß0µë6ÓúMt®ëĎ^Ż×+[ýjÖsăţ?ޜΪ,ĎHúifFÉ ëBËŐűŰŤâÝ‹÷îä)V»îHËúcţ‰ANÄ;@¶ÖJ­0.ťVCş‹Ľ3˝µł©ß‘ľÝú¬€X°yvŃ{4żĹŘŰü˛ÚÝ;ëđť\ö ámp¤ŢąńzßR]@Đ]>QsŢŇ VĹSĄ‰Í{XX9{' `¦xíUÓľkNŘU2Ž&×ŕRÓ2p' µĹd´±ÍúŰY«łěžv€l­ î·Ą6júKp˘ßĂ‚VFkŘ@űŮ^·Ýk6ępĄŽ_ŐO&ÜŤĆş [ż @íiŁâD<µfËKî?3 “o¤l UfU¨éé5zůBłŃ*5Üía¦Q„'Íf˝`3ô[ĂćÜ*ĂĺʨÝłĂÍş‰…ÜHLÉé ZťÂë7›Ťf¶™«1˛˝!’¬¦€Ťćí¤ű#ŘŢ­÷ĘĺR»Ń.¦B¦ar4:Q{Ľ^j÷Kç#”w2űÖCkÔfpm8¬µ›MLa·{Ő~Ż ÂÍAµRŚÇöZ©1Včůq­Wjt±jˇÔÂB&f{ĐŔ˘™n«Q,¶j˝bĹăůîD¤ęĎ&rxV¶NmX,T:ŤZ%žXĹIţŮBĆR1x€Žü‘MeŠŽüÓCĂ$9VťŚ±:ˇ4öĺŚL•ˇań©(U˘¦5]Đ3|rrfnú!ˬÄ%‚Ö ‹y@9ŞTÄăb†Ă N~ĐI`†I¬˙žÍ„”G2µ€§á3‹C˙Ä–T›†RFU…żWŘ™’ˇu˛0G~xŔä‚öĹ‹-:89žúŰw"2Ęí#"ÍtN9šŁÓ‹»O$1;" dĚšź@¤Ű(ü±–Ć' X%wč^ ±äZ‚’ô´*•Bý°É¨”0÷ľu!éȰ 4fT)۵˙+Âim3 bň§SŠ¨Ű¤ź0ýLXĚ‹KĺçmX°ńw>R#AˇR>*G…SşúÄČ0SĚd µP¦Sińę1áXA¤sÄűź>Ó¤'úÂg~Kű®{·b¤˛ÔĎD¦–QNOI’C†T:n˘t C PÉ<U*‡Y‘f‘ťz¸¬}Ue†QuTŰl ť%¤“ÉüsŁww2ý˝‡IÓh0ŹP­!ťś ń”peĂâ"‰ę;ťőé Çń§Ďţ_xlŰO™Ů(Tę\%d~ţÁŤ¸\ŃÇ '3SŹIý$r' b>×5˘¸źiĺ9• # „Î#7‘A1CڧÇ\"KZ÷stâG*“tşOłĹLżQ]xÝň-éSp6o4ę•zi´‡Ř°ŢhTęZ>/u*Í×ÇąaĄY.EśÉΠQ­×;yčŽsăŇď5FłÉsUz=x^©7ZjtÚÝNc–ď4­1Đh4&HĄ~ďwŰÝVµSč"ÍfłÝ´‹Ý0šß ȸ‚?^µŢě!ťq­AŘ9ÓfÜŁÍzه5ŞxŤ­=ÁÚzżŮëáO‡×Uzŕ"U†ĺFłYk5sTÁyłěN@ bm2«ĎAK¬Oju¤ŰÄÝφyx^®×î†4jjłVÇo–ë5űtPo4‘óÄwZÝư9Č÷ńôN@ÚŤ€âAwrléĄ5®6šŤNmÔšĺ~łŐĹFx-˛†?młš! K…Qˇ==kíěŮ^÷ôbµn´\ˇůťmĂçäáhWî\µřp/VuÚÇł{g‹‘¤~arűˇ^,řŽĹĆŤm×q=K/R]µgąäZdľĹĎ }ߊ=]4´hę-ěž(6¸žżž´7˝ę‰E—ö0Ś]ł‡¸—¶JŔeÎ[|íNî$ĽŚt=źźh$đđăŢĄµ-p˝ň‹ÍšY«Ů´›ęÎŻ˘ş0\=źcYvF® ĽçM˙i­×Y#ţV@.p=\< ä"´ół·No|5pČc´'j¶e—×<Âę!‡C¤HňÍś-äŹFrŮý 6MřÄ©+çĚŻĄöóŇ´Š"ŃĹi »B&łĹÚi•-ayB™Dąž …Âz=ČÇR©Ď¬gµ;ëˇDr/îLŰÖAŇ![ đÆDţ \wűň^ŹŹ…tAS(hűěJE)|?ÍSMĽľŚŮěJYż?’DBˇ!V ěi+ˇ01§Š‰¶) Q7'™ěÖĎc±âÜl(cuŐ§‚ś'ß ł­ţH*!FŠÎ¨)X4ÖŤYçŤTXHÎÉM‡B&‡9ěń›5zĽ”Oň!?ÇĚ%k•b&ŽX$©Ŕ ŘěŽr m ÂKÖîGB;@Ł5@řQÂéď”$S„` Î1‰¨dó¬Ó扔ˇcf±*XűPĘQ}>f ’¬kÓxź ‰ĎRüÎN$¨X¤Ź|üŐO`jD*!9LŤ»,&™M8ýb<üšKmcy‘yhřu_šb‚k€Ě•źö’ŁLqŹÉ*7żüĆÍSü‰ ˙–yŔ> ň<ŽČźňĘîřĐHŃţG€ĐŚ“=Vć„úѰ˙÷2–ś¤äm u›ć+‰»oüäY=äýŢxĂq4ŐŃłé’[xűxĐńŕJÖŰSŃŘĂJHčg±őo‹?čúF/t˙{DÜW˝O¶ő¤‰Vž;ý8AF%żV,ÖĘă›o¤¨P•ײIó^“ź#—Đ|­¸§ő׏K"ď}Óö¤ax3Q‹Ioŕy=9ŹZIµe°<ő¤Sš ă[iů löqÝ śşçżÉuýčA§µA"ÖúJŁ[K[8#QUnĽE›ŘÄ;ř᯳» *­®ť™Y…÷Ľ„+  ş2™K#ëľĐŐ,9óëEOd(¸¬˝™ťŹćÎÄ‘\/tfn~ä…– óVÜ"©ÜůÁ]M58s»ąŚÖFŁѦ›ÂŢ€t˙Zě˝ĆGt‡nCRŰSvßľ_ďxuČEJW~řP÷Z”*kw´ ‹—âKď˘g˝Ë;ŕĄo‹>T-–žUĹ'Ř9 ^kw@’çë?Ó­€ŕµ9/ďĹš"ďy3bKŚŃÖÝŻhM!+ßSľtä›ÝsŞÖ?_gď-šÜS-é*4o|ÓäuËŰ-Ţ3W˛îo!ęĹĄ—iŠĽO@ĐIÉŁ*śgęhĹů{]n †8e/ąO¬ß~ß˝ŁĽŘŰŰvEńZô<ĎlőN@đ˘Tó"{‹Ľ?@AĆ&iµŤ[Ěc« ń@ ÜÝĽ!~ą /(¸˙GholűŚkňzÖźç™{ţîă|đüĎôľş “XlŚw¶¨q‚žxČQW¶y€ań‹ufĄ˛­¦xŻ3ó!â¦bʢgŤí}€`ŘĚÁO={ ű~ŰQ˝HjÍ ¶J4'4o`«· _ńBy•Öt36HšőjJo6ŚGt§îŻă©äŰnţűŢ `+¬Éůń¶ 1<Ĺ6ĘN}é6![žf÷íN•eÄÍóź¸ĹÍKÉy #Ţsv,<Čâ}hyŢđŰjăpČťĹÉJćYĚĽCÇ‹10ôbć|i*V˝Îke.˛¸8Yľý/—uÂsĹ=­˛ -SůöYŻ IŇ«'ćČ…čÜ"<°mĄŮlBýĚ*Şnäđë ÝÖ€ >ç`Č€ŕ?†Ź}Î)ďo¸Ó‰dçp Ŕ žÁŘ 4źçÔý9 ýů QĚâ+€0É/»aŃ,ß·ćÓ3_6ÍÍç5es3IíĚ–RĺŔľpÔOćĎÁŠ•úŔÜâźâ‡ 8›-¬8ĚgČ|P™Í@h†ÜáM Bâw( f 90@$b€Ž08 ‡8*dËáŽp6CMŞoˇ X$Čü­tlŐ$řď1_hPřŚ•TŘĆoßź®}ťdYS…§đüé}ňod2‰Ąö«jU—L×pŐ|sdTžHl‘{jÉĎ$ _C0łFÉgrt’/ózjůbÎÄŚNUńą‰µĄ –śa–‹9Ň S°đ•Ň”űž«ó-‰"ąT‚YŤDŃ˨H•l"Ž®‚A\š–ĎĄ Y.Ł^J%2ÚĎę¤rT<•aC1ŁŚ¨;rĺÉšéKÄ6V1@áŘfţvĄ˝5¤ LňM~Ôą‹_ŔP™Ű)T>ŁŃů§ÉnÍ\µcíýť~pšŰÚgőˇđX©uĐá*DżŹaźĎ IŽ˙µ÷köwßŮ?ŰOó&§2)µ8đ9şoÇD&˙“F«üžĎ4ěS%[Ć@ Gúýľć“—€ż¬d “*’“Ë#Ź«ůöx‚͉ű“ăCçVÝ]cţŚC˙NFݬŇŢg>’„çÚKľöŻu&P8`© &ÍńÚ[‘<_W/Ę˙ÖzčÓŞC^3R?0ąŮ§Ţň Ö} ˇ.Ö]O…ö‡TŇ™˛gb™hÜës©d©YЧ€rą—Śzľ@8‚ź^V„kbăřĚđ)łäĚů`>’ôřˇD*YŻT2ńńÂW,n úR Ź«ú«Ă‚#ô*ń7đąˇRąZ U'ĹJ+÷˝¶j´]j6Ăů@Ę…·@ŇϩٓÎx°MřýÁ Ż*–ĹPm\Â=ÄŻb{}}@NG°÷Ě+Ë|ĎSMˇG,ěKÖŇü4¦±Ř—˝-ĐĘ^0ËŇ *ŁX%ŹAXţvű(°ď&¨Ékŕ_ň©ŽůäF1Ú?ŰÄf%I tľ2¸˝zĄ62WńĽđ±- ĘZÂ7ă.tHϢÁKăćŤAO$ż ^ń -·Ć_KĽ˘o€ÁĺĄ#´rf|¸› ůχe¦gř}•ęqsÖľ2 H×+‘ž› t3Šp%X<ŢÚ(ZËĚ—€ÔL^!TsóáŐmŹ‚v­ ;]IŃ©!Dx%›MóYo Nä±p® ÚهӶ#ç鏬h4ätEôd$j/śßä: …ďbűÚl(Ó†W!’ĚÄ=ŽJs÷Ľu÷8'͆cc<˛µ±?[ĂjŃ\(k‰ …é¤=ŤYcŢşLpÓÁöÜá©…Bç=[2>đvBĄ·ŚM]2v?ůcéôlŕtyjžR$iŤĘ‚AÉ䂨ŢOz,–Ś7h ňˇb,5ÄSž`¸â‹E#™@ŤĹěas4ô41Ä/N^o1ää2Ď3Ä9 ă>Şůš€ ĂLěí<˙ŘoSd{bS–l¦XűŃŻŞX”¬šúk¦ 3Śż"(i-‘ô˝ŁŃ,Ş€$# ;‰¦"¨,^z" bVş’)$ůŹr[w@qÉ'‚RmŹHbŇIt¦đohúý‹Jüő*řąG§$/L¤°ľgä_âÓ‰‹E<95RG,ţ·ň#9S´OŠ­ôĂC2Gб¸Ô=’’«?*č )ů”HĹ<<•ü’“cú@Îřt$'‰m 'Jni§)ö[©Ž/`Ż‚S¬R?˘%ňő™&UBGăą·oŔxO®ü#Ź™ŐdZ»e¦Śňj ‘htb©ĂÄpKĹ *S(„<.I'á Ú^)›Á(ťěN—Ě…űúŐ.XXČ!pl|_8lpr K!–h rJś‡}ŮiS"• ŚnH$SѨ™&4É:Y6źĎMę\‰Y`iNž@Pęq,F«SŐ|ˇDˇ=•((­Đ¨¦aˇH›p"é|ˇTx ‰r“[×]ľ `Q/0•_d)÷@ˇ{ú0"yÄËĹż6‚}U‚Ěa'0BłĹ˛*ŔA ˙śĘ•uüAp>GQd‚`sĹPtB<ĽĹé‰DÓž˘B~Â2/wĚĽ:Çť-CÂ? )4źĎńOüŤŠLQtÎ!pŽLw†ŃůÁ ŕ…Ű&×:Á3d áń!Ŕ3dţ¤÷đ¶5č-](—€ŔgOaţ¸xş-žŹ+Ś?>`0'ÂSüď…É…µi9ŕ…˝ýQH* ˝”I@Ŕ$}–Éz¨ěÁ¬çŇ3@R‰Ůqä[ž‚×ZőHäúŕZ9…˙¤÷úţíóÚUpčE#häZľ/ç—=yp!…•łď-mŮى†żmĎ ‹đĹ›}ŕ^ý‹Ź­A7™Ú¸i7ć ‘ó ŤËX-ţôVŤ°"Ťđy¬ŻäţÂŕka—'ž^ł·ZS™®MCmË>üż¶1;˙˛€Ěżüé?Ë\ťçî´şT]ŕ~¦j›âcđë&áq@ZľčAµ«±Äťy›Í©aŮ€K_rŁ%o\硹âŘ•MÚC:}ľŰ:HŰť.o)¦SŽ‚-+$zŘ,ô%ůÁ‰ĆĆÓh5p-逋áNX,ö˘-źŻ‚ößáČz´QZŇ[ň•‚_€¦×Ż‹¸KBÖQČçÓd#«~f8AW1đ|pŇČEŁN“Q?cd}4wb DťŽśKˇ/ÂČŰ[Ň–mĐď‹n”Ń×QÂŢ$šJ1ţú4gOš¨XŢŐ2ŃŚr˛÷ÜEë,$Kř}zuŘĺÖ{١\ Y¬pŚ s#“Φü&ś”Ż.X«ţËoţÓ]:×™>ĽmÖ6 ˙í}ó}Z±‚´Ŕ˙ą–âđţúżl?ĚŘ^›á"0€<ůűß«ŽU‡.šŃďí‘?*čsŞţĂ1‰äÝĎar.U&ŞĂ.…/6~úm3*Źĺ{ ÎIĹ*G˝S2WpXîQčěč:’XJ&Ňľ—ÉŹz+€Ś¨˘†Ó_ËDţ AA¦~’đţIČĐđ¸*Ţ‚ĺĐřS¬vČÉ`ËřK"q–řáŰ€äř”D6DşŇŁń~e"­âG$)•üK%kQÇßŮ˙BÝä-eI¬źą\üŃý[H6’$¶č@Éť‘Őß‘~ÇIž¨'.ůh_Ů÷ńŘß«O„|Ţ)¤&~îéI7Ĺ%±UGźČ'–.MHŇ`şŁŐyaă ›»Ĺm˙ç_üńabł mŻfą]Ď(+‡#R;ď¸čć=rúęą"˙ö{Ăł™É=řć_oŤëř˙ü‹ź]kśâ€„ôţ´ôĹ@wČ4˛Ë ‰č ą-†­fŁXČnđúXÍe•8­U ÖYmł±‰Ĺ4»BĄf*ÍŢ Vf´ERąVW “PĄáŇb2™^!S˛ÄU{PĚRę|cŘ!• •V5ŤĎć62Mnô;|<ąX"–ÉOÄÉ^Ť&ÉaImLgár¦€ŃT`s2•’őˇąĹ7ĆÂƨU¶CEٰdŢÚv€üö›żÜd*ŁŮt  QG+ŔŁ”Gç¸UJ™©€ŤťŚMö Ěp’'±5ó‚Hi“xdA¶]"‘Z5ă¸B"ęfä’Ć&’É­€Új°`véęěús@L˘Ëśíµ+uf`Ąl €ďąrÜŠ?iMŢř˙ýÍż~ľŤSrúĎ[űA˙ă7×=á€,ö’ĆŠ"‘9ŚŔ(¶Ř‚a ;G— {‘9š« #ŔrÚ,Ś@¸sÜŚÂ -v9†áĹÖ7ĐâËâüň ŚąÖAüvx«Y\\lž BAČ@?BĎĽ¶„Î)şŘ¨ynč`¸ xŮ]^‚ńŰâeĽŘ˝ ?‡ €-ůcá&ΦÂőŠÍ5@đç^t\áŹ?Ábëhüh™hř“€Řňyćxš@‹/‹=Ľń Č2Ö‹]˘ńt@–Ź„{ĹdőµuČă:ꛀŔăÉě˛ÄĽ´ą^Z;—ĽŞŘÁó‹vzžđt¶É*•ř˛AKńÄ#”úöŹ˙Í/}8ŠźU›:ß!úďI©‡oş6V˘ţÓëeŕE7/ž8ŕ-Í.dŽM/SvĹÁĘWôz‚ ëI˝Đz/ ®~¬ĎóĘl˛ú{Bđęoąâž"ÓĄ“s@Şü6éżů_îw?Ź)Ö­ÝÝ’iWŕ*ĎÍĎ“őââ|2Áź^­Řˇwĺ¸@f“ €Ţb đ6żgć˝ę·RäPÄçvŽ'n~‚ż^đ¬HzsC Ő-ŢuXY‰áÇó b)}P®2„3sĹ·F^Ĺ)oő1üÍ č†}3t±±=zČ”l±?Fr…Ýí¸ß‰C%^>•Dĺx(4‡Ţň '˙¬[w-žÇç4ĘVUy ®,Ś˝Íńćťţ>,Ë+8©˘(€€@ÄSp,LÝáépâB7W™ÁWďÄk€ŕt¤3<Ͳ’ž´ó*4Ç}â^A°5ÄĂŁ:ţc”U˙>W—@üŚš“Á`Ň€ż¬ŃĹ€v$řO‰Ő‰IÝč a|0Uě­ů^Gĺy˙•}Ââę8° g=1µ ‚#<—ty?]PăeŰËhrxţZô…NŹ#.L„ sŇŮâM‚;Ç‹güą/éą´î)é­n żÍqW0žxxX?#ĂďŽ [đâ'›ă…ůĽ:źÁÝâ¤>ôbľ@bÂô>ý´)ËN¬:˝ZÄ i*ž]i•»T”ŻÖ¨MĽAZWą*őih®Rňx%÷T~j¸Ü'Ŕ˘S<$`0ČT žaĤ뉀n°A€ŚĹ/0¸~®óM äO滪Ô ^^Ŕw&÷Ĺ'yrŚL hĺT“ˇ”Č2s3đu^É1ů(68tŘ >G&ă˛N4şÚŕpęŐs©Xśc2kUJJ|DŁËu2*K¬©rš^t(u^ÍλTg•ď…üÜZаÇpň˛ŐC‹ZĘ ŞäBźše<-ŹŤůt<"ęŇ*L’Ń,·ŞÜ”@Ď$çÓ•"i`c°˝Ô°g揱ćžô¬č hűU2·čbÔĆú€Ŕ>—M/`łRŞŚj’ä٦>µ}QčÔ"^€K·Đ­ źĚ,ĄŞ„T· Ď/0ĂmVÔłi>CgP‰8ž‘…ű…iä¶«&‘B©(µrłđ<ęW€ Ó/ YkŐ`‹§UŞĚ:.ÓiëřźYFŢŔɱ)é"’\¨Ąrż´:Kěv@0'CűĂ/MLŠc,ó—jěł©D¤|0˛żS h"IöéË„L”ć=>>ůl'x 2ŹC'âs‘ęě’4;éČľä‡ZdńGş3Ň)Ć/§''Ů9‰˙35›­ĺŞŔ @ćÍázĹd2Gú‹sĐňµś·ÉçÓs »BÎę>čhcă•ň˛s HŻŃě]q1`‡Ćó•ĂŤVO.Ü\ZĎçSŻAéLĆéG&ýłęg<Ý)YČÄÂß~Ž“élő„D4ýB¬űÔĚdŰ”LĺĐNżă`N˘›˛G`Q #"űďMź´}"‰Ďüpś8¦nÁ´*ę#˘¨şy*™ţ;Y˝yx˘ů `čHÖc"1Qˇň~G"‹Q˛ń#3ÉŢ‚ňŕäŰ/\śËřL?•ë8XŚFĄýÎňź9{źěťYăŁĘµČ 5ÇŘx˝ź nÔwŹôŔK@@áÚ(ö “Ă$Ńô[Ť}"¦~ůčäî±C§Î ńôw>ů·&•ű‰*$ř'ĎZ‚SÇhŽQ dI ó¸)”ź›Üß 0—|ň ăĐČ&OţY­%}ÜsÝ~h2Y§–ˇ°żˇŘI§Té^Ë*¦ţĘ<ö3Ȳ_x(<ľăädŻ_¦I¸čí€äü-ŻËń e,ŽŤĘĆ č°&‚ťFˇ÷9Bńß0úaÎäó:Üp#ŕó„^w°şÝŢŇŘâvĄ‚ž@Ü–Ę"‘DÔĂ"ľZ ęr¸ZŔGĘTŇÇCŐDˇęgjt Ť¦˝ÉGţé\=Öx€ŚK‚Ńl„8LX0ťŽZąx¦Óź}ÎF}´çcváŚuáńT–¸ŞĆ^ŇÝźää<°QBĂYčTć†ŰÓZ~2 ZĄÁÂÍÉxŇC&1A BŘLŔ‘6Lú1ř>@$ë€n«ôKöĐRĐ'=ždrĹ®Çg¤*‰Ärć¤O·D ńÚnłË ŹĚV»3ŕÍ~¦"ž˝|Üě Ú,öš×7]ĺĆk€”ýV‹Í°‡Óç7Žo$ŹúꆄÇ*Ŕád "…P2€5M®‘@:˛9Ľ†ę< ú‘`kř˝ţH(cv:­VßÉ­"ćKBy@PÄđD góq|0Í'mvg>śö  7Ć[ăÉ ß›ažx“ń ™¶ÜÉd4”冫-¶µ…ëÉvYĹňů‹ˇxŔ¤ňJG ăŐ—ăuČi‡úŐB'ě řĂÉÉeôąÝ~ăŞ8Ä<…DtÖ´ą=ľ`°ŮĘ×±–Çăµ9Ŕ®äKŘâ17,x>˝>Ődq/ĐÜ ÁTö‚^CÂâtů˘ńIÝăńŘ\p'Ť{ ޢ>ďń¸z=o:{ Ď.ĐwńćŮVßÓë€4~Ďfł•\&I­S™ĎTűl˛\Áu¸żó©Ů ⍾Tz¬Ôqe„nBË&aÂ>EĂČ64G»śÁşšIpU‚Ŕ'eţ‘”o‚(GÉ•7IăJU29E$"“u˘–‘îŞyű„t™Ŕ<汀ȩa‹Ú<,c¦c‰™ąr!At,Ç‚,*AĂ Ârň!W"$ân$zHdmąŤôđ˝ëv—jŰ+÷]#Űz…ýI˝X ®›q®ßvŃËťĄôr3łĺt™Ë93Đ {p7Jß9JK' uŽ%ÇnȨäę~ÍKţ-sĎvĘRbú<űÇC6ť+ I"Đ,ŁŇ ‰ňť¬#mÇß˙sˇy«+@/!÷”,D’ýťšÇrҰ źŻB,‘{x$P9dYHn•‚ßŃÜm:‹ű÷ĽęŚ{ĚbüC„˙Ş©ŮĽtţ퀜iľ>;nŃFF+cĎ—S°ŮĹ_hľHiđ25ˇ«Rńúzłóđl†ˇ‹”FoŮť]ÜőěpzćX5#/îŤPt:[ťÍ»„+S?P˛Ů˝ŹŠ˙x^#˘'?;Îţ’ČKŐJwä$qOÉ4™‹ľýD31ÝTň3†OŐčQ6 \Ň–^ŹŮőL;{â| <Á.‚ô—ůtë}‹čâYÇýë2]1]·{ÚžÉNQxBÓĹĚJu™ÓĹ^‹ţŔ© 3'Óůl–QĂ3x†¤x†ÍňŔ KjóÓé Čâu©98źäĂ7椯2ŠŔ­@ŰWĚ·+p)«¦ŇX/ÓM—s]=™%ëţ*6i÷‘X!›Ě§ĂDVH72©Á,žČä‰&*ĺt˘T» ë˛yÚ Wń{Ô2h'Wî"™L®ÜHË™t6™J H 4_Φ[P*•MůĆ`ąťŻćĄD.XÉ’7‰rÎŰÎ7«XÓ98&#‚4‘)2ďÔđ܆§0źÁBú ŽóóŮ|Š­.ńöŐ0* 3t–¦í\©0Çő8˙jÇžU@šN-„_ĹŇâ<4v‘QVĐÁC )4™Np6-ˇ îµúçóédLGüŃtń[ b­ékMŠxókTIJŮ&cˇŃąH6R¦SÁŃ MĄ LÄ:‘T ’N…2Vj¦2…b8“nŕU’l$šÉ;WÝöµĺT«4mBW€€Âë‹n.p:ćőh‚7Ŕű[ŕMź „'ŕt6¦ÓŢöG`^iËŽ­ERΠŮ˙ŠçqxŚ'6™' ťĚăĐÄ8ŔÜ'؉•ŔK@ząÖÁK÷ËßaYęť{M@}ĽšŇ0d°Rn‚g–&áĹšŢńô čőž¬‹yĎׂ_äŢ p×™]öăߨb9Ř"‘•›ŰNIŢŹÔ–¦ŤČąňéŃ?» L&ŃžŘĚŁ/¶ă"L•˛ż÷Oů8•DúVŮnťśR9§\>{ďPŠiöż>ŘŹřWÓ"WŃ0_>81÷”¤;ůÇ0ɡĐţQŐ.Sú_ŘRrŕü!;&OGţ÷R¦öűé„pdüţř÷D ŻrYĂÂ!I(“jŚî<=>UŞ—&üVą¨— ŻĂŇ9Żł ďe„V ^źHŽÎű¨ ~0HŚÝą}ď“áŁaĄ“”żtQElĎńéç&6Ą˛)ĽŹ‘ ⶇB|𠽚řqßbváޱ@âťhN?>“‰u;6Ň…ń›€`ÚĂĂ#ĂťůEů_ä&čÂĐ'?E¨«‰ßR\j”ĆqOsÜGŃF€ô-Ńv)>-ĺZ¬ĺ Á©¨Ű™îucA¤ę(@>w.ÚďT»é/žIÇ\1{Žť‘°ßŹ´˘áP<ĆĘólÉž‹ônĚ׹dbýNG(Uęcđ7§F'5ëNúyŁ:ž ývg6&˝ölG“aŘď·fhŚ›ą9`žŹqF˝Ń°?H°§]  Uh¨ňŢb¤Gýn`2kNĆ=ü}ČöýĄ&# ěL;łn§ŰĚwş`k† ©Ťî°Óu›—uő€\¬Ł» ȴѲby3:śÁL1îgcŃhÔqĺ"Q$‘v3Ą°/ęb‰pÔăňB˝‘7Ž:€±?ŽF#Ń Ë“B‹?žŽ[îV@Ş<©ĂhËćŇ~oo FüŽŮ,’KÄsŃšµ kx rpÖ–Ž–§•Z3cYWŘçŠĂą< 'Ró~0SGB-o J»k€ôâáj«7š÷ő®iÔbH§×7»üřo]MG6űhÔ çÇŁasŘďŤÚÝިŤGŁYŻ×µBČ őë{é\á‘­řÜQw&čVó‘@ ŁéT¤®ZˡP0Ž&ËAŹ7št‡s…rŁ ;Š»C®–+ć‰$bBZ#ť.äâ±X(Çą0E}őTĽ ZĚ…S‰Ŕ^ńČž·Őť~¨Bîn* ö2xĆvZ 8’ÉÁÎlĐăI'7ä\ĂČ żŞbŤ÷ú§D®‘W†ÓŞÁífë8,­(ŇČt.O˘‰"5âŘ<˛Ś@„äÂHMG<±Ń(L ŹOSr!O˘ýŔăĘ9΀ Ä“S ë`ŕ:d)yĄ’lĄ~qÂGfG,–A"Ń‚#şŽč뫏yZ"őŘE9ąś…şe/ţ„W“Ĺŕçšň˙śfHâćÄďeÁyÝGe2őLm@tŕrŘřË(4‰0…a!†šÎËÝ—Ç’)\ÓSŽx\1k_Ç.[+€B`őN·ô,ÝžL“fďzţř.Ž‘N;–ĆîŃÍ^¬vńm¸fj^¬‹'\­ô_ö/˛R8ٶşš íÚ˘Ú´éúŚ—+@†ß·©¬}%Ë6ˇ&NÂÎţ‰F˙=‹F*î·@GřŃv|pJ†“CJ8ý$ÂÔt*QMúôűĆ gÍéZĎtŕ#‰$íÇ˝ďéß}pŃí7ákč§rßAۧJr&Ú)ĺűCŐG=p˘˘ĎIŞŽLcđ¨d"EL¨WOű:1ë‹M˝Ü’bk@°éyîXPÖN€•Ö2ťşőem¸ať| î¤tÖ;ŚWuÚ«)|~ëiµŤ üîÍ:6m.6çéwĐÖÝŻ»óRń W@HWłľ˙Á#ý{.ÁOccĽqcĄ Oه5 •|ĎůHńQt¦FS‹§Ü‹Ç˘N0ťđhϱç8ɰĚ×Á×ö+ĽĘ´puĺ9Ńë‰Ň˝Ľäńçhźč˝;Bĺ‹§jźýŢŁeŁ÷ön^ôâ2žâĺÂ-ťÝČp‘áő7šâ 9lfkŐV/¶Ö°^6CŮRnk5űíöXĄm6Ę…f-SbTË˝Zż{ZĹf™Z5nă©í–§ëŮ|=fŠŻŻąĽdžž•ĘąZcŚ–üäi§ÔL4ŇĄb1m…‘rą\‰ă!ŠĺR1ĎkÍ~*_Á&ńbˇÖ*ç»`ĎJ˝Ň| äĘĄNą]i¤«©|¦ŇŚ÷n6Ň›µişRžöđú\™% 9ĽŔN·ŕB˝µŇýRŻT* Ţ„ĂňéNi:ÎŞĹËwÁ9 Çtt]}Ţ @ĐVłšůXŘ 4¨Ô2”p§çSŁbżÔĘTÚč¬Ü«4Ó ŚV:Ír.÷ŞŐBrŽ$±VÜ˄őěrµęń§ËŮa~µÍ{ ÚŞ6łpÎĂ6 őF6’0GË©~3ŹÄ˘í*–V‘ĘŁą™‰~źŤ¨±j=DďGj«Ą†X»Wo4ł…f§‚ŐŠőN­^îXŘB,Šđ Ăx=Ó(äKŮFˇoóžH®—žv+IK?ŕŹe‹Uül·ÚîÔJŐR®R)傊tśjóĹ~ŁY­baŽč ór·Ü)CüŘ [¬Őr•â´o˘9‡Ą!ś/Ô«‹@Ěěf«Z¬˘­šI ö:ńj¶P©Ö<,KłŢH kÍf­^m6ćHî¤]jKíBÎlěÔşŐ™IÜŻŤ¶Şĺl¦ZleňKSë˝Z3ŐO™RŻÓ®çŞőn˝Rid ő^%‘qŹ7dʉS¸ ĂÎTśjI{ÇlDU3E‡§JŃ wŞˇ¤5©ěPIgîŃŤű&[w,&Iɬ|äŁ~áłeŕň™d*—ń%ÇYë@:dÂŞ6š­fkˇfŁéÎ']řA{qŘÄ›Ťĺ—Ö™šá(~­‰6šççšÍĄ7üsÄE@KOÍfB´Ľ‘ćL­ĺőĄÓ« ZË V=Ąćň¸yüąg»e\#_“D$× ňÉš đř⾊%cĺ§Ç¤/"[|*PÂş@yzÄĆČ,Ëbď“S~.ý„+(\1÷HEa‰ßXD)ŹtĘ#®Î=ż¤)â~V˘ sěźXe|*Ź~(dS WʨXXG§LvËŞ`p4˘=6Ř; ’”B:ŹE–ę &÷Đłxrú …zÂCöý“€śA1M1©ňčÄí]Â.4›ĺĆU’¶ZĄlˇŐn'ťÍv(VĎĺęíĺŮćyš.S3źs…/¶ŠgŕŘŐđGćšES<Óš}ÝńQÄä+HD!óÉHÖ(LÂ>+0: ˛x\"N%ýš,ŢO´„ĽĎnLhâ˛Čd*•##‘m$Ú …M3… Z/©8b)脏NÂ)“%c“š^D͕ѥŰc°‡‡s,·Oł™Ç_ÔT.™j6xtšŚ+c0ńđ L>µ­KlČ\šĄFF&ýX&&2BčPL9 3XŤÉ8ŽđÍĄ~*”‡©¬ŤDbkH‡_NN HkýxČ; Î7‹É čJl}‡3@ Bµ"µJ­ľ:P«®IţLÍú˛vňÉÎöU É6öq~_Í­§%ç˝Č ťŐśW™(ěßŃyÁ&ýř!ČNźöçÇŇC1‡ëäű_nýü™É„é?HÉö#笖:¦ž‹~•®Ąĺ ±L.!śđ„Lü;\­d_Ňa«ALĆ•c]-ƶ Ů ‹GT .ĽÖ Rä=íLGeRí´ß×úůg.“BĐ’©:ĆcŞaŐ!ON ÷}H‡JŹÓŤŞ\Ç*fţ`žť‚Úä7SAŁřárÍĹŹóAx[ú]ű‰dáËČ÷®ŁÄ3­\(SňI +fhNh ŻÓe2řżeŐG:˙Ĺâ) Ć?ą×c©Oü™Gg ytę1ÎuŹÄ9"šŘ Ö1Ź™IO¨·â°OŘ<éÁ§:W¤ŹÂłK@ś\žěcŚ!Y§ä’`’)_¨śßS(ô=q©TéN‚zń„-ä>;ĺ5ż±A¤\ŤŞ“ád8u'˝^ĆęµQż×éu{ fd8ŤĆµQoŚs ŰéL;“VŻSOáÎĆ­nżY ¸K¸Ë^ŻŰí×—Ęoż`Ş/FÖŇřMéZ«Ýnw{µÖkšőq/čĆ&Őie\vńć´6n6;]´ßéW&ŤŇAŤĂ×ÚťŢhĐî÷F Ű¶;Ý®–¶Účąjôűý ޢčcč w†î5KQŚ~€ÍńĐ»5t^ívĂng uúÍN§ŰŻŹ«#¤Óë Ńţ¬;ęw»ÝFClUt­ZŔoŘj ÷-ŞJuWM\H´•H´Ú)ŚgZ :kO»xnjňxůf·3-Ch§Ýí–çp§3jôşx”;@ŁŰ±Aż?ƛ ÇÝ^Ď‚xŮŐTG˝á´ŢŔH˝=ę sěáÉÖôZjŻ?É W%ČlĐt'ÝiuÜívâ®H§»H±~ه‚çăޤŃÎşť~ż×ĂS!™šLn.ňzťëjáQ\üĂ…˙iŐťóŁn/Ăź¨ł<îśź=żÖ¬7Ď˝4ęíîąZk!÷ěömŇvˇ¤ůÖĹ˙oE÷ŻîM0¸~×(´Ľ×o_Ú=ľvţţ^,¸ńč…ţPkĄéN@z:qiĹÝT´ÝNęU«ş×Îť2íqŃŰ;ĎÄgyt‘EWt•/N4đüÚŤÉש‹ä›JńűSŐŐ‘ňřDą±×ó„OM[“iaÇŢÖŰ×Ň góŁr2Ĺrě˘ŕšnűů|őÄą• xč‚¶}†ŢFť ›Łzńý­ —ŽVfÁa­ŕ•ďĹg»=Ç÷âUąćÝöĘŚşébţ"ŠźÎŹÝgĺó€Ě\üŘ5ŠŇí˛µ{µ0\ďć˝Ôpuŕl̊𥋅ˇ= z^ĺ"‹Hqť].]7qHJ¨Ř2.$ =ÚěęT«ÇEŐ#ÚBxQ¶'-/§Çé §&ážb#ˇĚšňŞl®l0çuó†Dun·fř¤xÄfÔÁľĆ—ôűźLŻ_iR›RŻ:‚8žŮ )ŹÓď1‡fzuĚśłÚőˇěYÖ]bĆü^ŤEëłą5v0Bń3Ű)v¤üŢxĆĹ’q›5ăÓŠňqÁ‹|\ZÇëKĂ>ŻWe˘¦Ý o?ťÓ›śYWÎąr.îs+ P”ď\ë­©·K˘Üę–k€ÔŁesÂRź‡+o¸‰…’n»ˇd“1—©qŰ5Ź9\ĘkľdÜCsą™0ŮÔíŃ›I F-ú¬čş1¦éÖŰł^×#AlęëMîąŕÖź·Tißőí6Bαâ-Kü=©2ÄNÔ,âá^$L ٦Ă2Ćű GA˙DŇíĘ!™BRǢđ€ ýĺđz™|˛(LNŁ)©ć¤“NŠ“SęU$t…Š,_«FčtýďĚ´SŤL`<“*9UQ‡e±dĎBĐĐ}OÄřµHGd0rDúGźGű§ňˇ8gĐIüăă´ĘÄB§ś~Ńý˝ňÇćĹ{ hA¬ż1{?·ĺF;˝Őg Đ!Aş', O( ááXtĘýŚ¬Ç«e_ŽŚT¶Ž~JĄtí`X ˙R•$ Ť‡ٰ„Ja0č4fdÄ!ś~ŁĽÎěë›uëőesüIńŘDÉ密ßj#ä ŚFk‹70łu,ôąĺ"±ŹĆSÉ ‚ –3ű=*Ó-‘V«r‰B`±¤ ť}ĘQěŰ…FłBjĚw"±,y]“•+ug_ôµM`rÄLÎĺŢjKg6č¤AłŘ®W¨U A«˛)ŢŚ0Y5[‘žC|ö„%T*ůF™ZĚs9ăP&U) AĚŔćěbłEÁr9“ F)SXMÝ{ ŇPĘo±Pvc[éÚ–k€ur©ßYôĚĹnQ©t6‘ÂÜiU ŹIĄ—ŠŞ}™Ă‘Î jB!K;jlo»íŐIeZŤTb ő‘śÉľŢ´}@ ˝áF{ł¤zR<6Ѳ ‚ĽŐFČ «=‚!/ťRٞ0ă ŘŇÉâ^t ĂgvjĐE±1T’"¸°_ł°„źĆîfĚŁ ë>‹“Ŕ"„U@đŔťĚđâ/|Öű YŞË@s6@älęĆâw‚‡źÝq) Ź“ ˙ł¸ t~ Z™jrĄľA”˝­ŕvŢľ;çÝR®Ś€Ż˛Ś!˘0. ˇËH÷#±1 CxÚŕ—K˘žJČ"ĆgOĎÓˇúegű2íčzSć5µ–›ý1/_Ç:k‚ĽáFČZ/ţ_ł—Ű$­m »´‹/®šc†®ľ­µ‚š‹ÝŐjľąs-|÷Bë€LüČíŮĚö“-~őý®U~ë1]±„\?Ţ@ŻČ\eżíeňâu¬ţůo¶˛ŢÍ‹˙Đ(6°/şš–_—í„É8[ “…Ń·ŇtŮ)łřWŔ©łËű‹Fˇ)†ŢĚ ›OV\äłĹË›ŁÓ~u±1€ĂçŰäQVü|Ď]Ks•­ľ¦ŘJč g őšŘErˇŘd:(ť]@Ď\ <ÍÎŞ–NđÖÎňy˝7M<~}@ąëV„_ĽŽ•<˙ŃŢl#d 4i7ČS–SÄOz­V{›ő ·•AŚ–ć¤]ëmqÝb‡4źżŰ,’@Ű!ďib>őQťFĽ>Ľş1 hŇfw)!‡G©µ'ld·;Š»ř–5óđŕŢ `„o˝ss!TşŐVŘőď뀌ĽE‹Ó_đ‡‚a»A‚óKřŚTŞŇe‘ĹőŽ@)—Ď۲!Őj÷%=«Ëa‡ÝčqKźg1W˙Xs٤űę€LĄŢŰ‹¸ő•Ď®ó&ČŰm„¬W±Ôö©?Č™¨‡ —ďý@ĘAű×=‡EĺŹĹ§†ýQ‚A9ř(§™e ÁPČ$xĽŻ˙o†ě˙m …$Áp,Đ®—k>ŃÉ'lŢgť,ýá‹ăNIů‰¦3UŻáĽJ Ť÷ž‹€-Óh,şŞv®Ň9$±L‡†CŃ)‹ó+ŠCeźŹ¬D¦’IĄĐNmăp<ţA§3m&˘ ĆxĘŹ¶ŕűLÇ4αîó"Źq˛ľÎík2ďşôÂu¬‹&ČŰm„¬)¸­Z ‚[ŇšÁ«ćÇÜXŢʍ•ňQö“VäҶRű~ťBćř8IW V)4ţąÜdw˛cÜ rŐ.]/kŕ‹Z7$k¬·BÎ:VËš‡Ł.ëç(˛ň}ńwhÚŔ\bĆ„çĺ¬DݸżAÜ‘?ÔšÔWx}ŹÇ…îłĆÚ"ŇýxfľLŠEâĘÂYüńÄ:kŚ€ŮxwáţÂ/‚ži=˝¶ť ł®íâw_…ŹŘss%/«Ĺoµbf©”·I~ţąrU¶ü«+\±vBE]–ŕÍÓ‡|Ţ)…âa7*†ÍJTµ‡ú‹Jş‡Ňä†ě±ËŻ6şęľ8¨Ő+G˛×Őęű|_JĚyÚŰt+@züÔ}—_¶Žeşş÷m„ÍŻ ł‡H÷EoŇ®GÄęúĂÝ©©í+»±+‹ř_%˝šO|koH›{Ż÷‹Ö±`ńU‡É[m„üX%ĹÚĆ&Ł ~˙Ž®«şĺě­×Ö€4ąlGţ˘u¬ŢĘFŰoµňăę[› ĆŮ9[Őŕťýv›Ră>¸!ŕKÖ±’+CWđm„ü4ń ĚÝMë¶Ů/öL×fc˝m H™[{ĐÍKÖ±Ś«Íź7Úy˙8ůŽ-^>ňGl‚'{ľŤóľ†6¤ŔÝ`R(x±:Öő…„IóŁÚéNˇm“Ŕ·ÍZDüß[_ŘŢĎ+jC@˛ĽŤvi˛Ä_*ž«MőŁťžCHU#Š[y™=fŚÁŢŢĎ+j3@RĽÍ6şxą:Öő2c×ynA™,»Évç«zT;a{ígÝJ °é?8Żö§sp ?u݇›2Ĺ' p5lJÎćó <Ű$ϢYX¤©"Řt>źŔćĄBS>ć[™5ÁUUNhü2^@›Ž0€BÓé7µxů>#[ˇž9š°ÔD`ëE—í»¤AěRsLĘ»˙©hŕX–6El:—X$-ně±@&2´’-mo ?ó ˛řk?ô¦Ú I§”ďőě_Ř%ŤRLWYži_ŮKődęoĽÂŐ±ŤFUűößWď- ­ëľóYbŤ=Ë©—jŘŘo‚NţţPćŰî†ŕ·‡‡Şw3Ôű ¨Wşč¸†˘Ń|üá:«ËTÝĺ<đĚŃLZ[Ńh2YÇC„ćpŐ]Ďn[ľďt]PV.M^šß¨'Î\~ó—›Çʤ2óŤÝ/…Rí±\ęÝ †<ęşnŢç!ix/Ň,¸1đˇnů»ětS“€PSYů˝˛Ťíüߎ»‘Ô[Ţĺuő ]µ]FÜdööşŮkµ›ŽőTˇm‹ŔľĹ.Ľ·HýđŘń-zVŻĄűA,Ú-{şG‰­í|l˘›“Żv#!O”WŠ#, ąOŰ/¸]ĘzŚŻ×Ň˝€Ŕö]ăü?ţ“Ř–^6ĐÍň?ęÇŮi©IP¤.>róÔ+AŹ›VőľBîŇ™·NÄĆżý“äóÇň–ąW¦Ô#ÂŮ Ú2 ěÝgËô ©R,SŁfµJŔdGdXKéâŔŞŠĄđ›1µĚ`QYĆqˇÚ^1ËŐ»‘íY×% ¨Ü&)Ć—¶´5xfs0滆ŃÂăŇ^ăą]E±ĺeü<ŔĎä|ŘF&ë9 +€Čľ Aßk'äÚZläÁx:Ď‘Esc±ă Ś€(CËÝ_přݱ~[žŚuĽZ„Ŕsü™Ł0~÷-6YěŮXW%đO&›Ę$¶hŔşŃÇ×k“نÝy”i81MśĘZ%uĆŃ‚Űkł‹ŐľGçOc źX2#„lŢřĹüťŻ HĘúÚ ą….Ď’ąlZ/˛ďZd!`>»čÄľtşÜ“Â_Xç'ćčŮ˙çŃ9:–îĐEÖFÖ{gťž¶äÎ?ě푵Bމ }fŞÔś[-2…ń/=%2ţF*g«uT95}8!Qh?ă‰x1IýI.źSČFZő˘­| dz}9(´:h1]Á@nŠ‚oôľŁx«€őŻť[h `Ż ‚řăć5<Çż€-Olh ‰V;ńw?4Ă&hŽCBµ6ˇÚ"•LPpÜŔňőšAˇ‚;žçł¦2Ž…#ŚdA+i î¶—eĚŮŻň qpöeđ˘ţÓ –“­×§vŁ!ˇVoňą°Ş%˘íδJł^Ôś}˛éőA™Ó—öŔ^UČĐét&ą(-ŤZ—ČĽ¶\ébHdlrlÔŠÎńÂĆó7€ç€ümYUw @‘9âąÂşiĺ"Ó Úř/ €¸łůĽ‚Äě( ágáĹŹÂŕ*É×i*·xć×Ö‹Ĺ7›ąî Ď.g`26Oc•č"©ł¬„×aý—bC¬ŐČŘôl[Îb łL¶Č¨˘ČÝÇ©äéVD!ćazľQy-pÎ(aH—O# ©Í"Í(x´jé;¶ÄĂDšóŮTÂşŘ(r ŽôÂĂOĄÎv®Zhj™`g–ÎĎţźČĎw§:;D.ÜŁ—±•&ţ5@€ĎýCžÁ ŠCvŁL)ź:hߡS‹¸ą…aU %sŻ]®–1•bP*ýĚ1ň'fľMA‘‘ĺ Ťł7}Ôř†(*§Ée2ą’.7uák€ ß“qĹ "ŤKńX$=IŔA™\ÁLęß‹ţŔÎ5ëU,Ö/OŐLő‘Ź q7ON ĺΦRÉ{ŚDŽJ˘ňöNe“kBHćýAfo_őKŠTĄóD'¶®mF8!+®éÄ@=ö0ĹçżĘ€ăç_Ö / _ţ”AWj03<% î2·Íš—ç›}^Ő’EćÜ6Ýmş¤m±Óç* ÓosGß9šÇÖďťö­c'ś|4p©żŐan]üÁ™ŹN$ѱ Ň(¨ż¶Í\tşčgQ2Kl"ÎüôŢLĘ•ýśĂ!űÁ/µ,x ©čÍX2öú‚oδôA$řÉ€!¤k·úë€řş°®dL~qÍúN·»ßđ{ýn·×ëŃ7ǧĎmá@"y2q ?•—őCÖďí‘®¸\žęĚ_Č››K4uľÚ˙!@B;J¬2ÖUÎ/ü”É×zŁ9ţoÚG°Zm0µ›{łŮt,ß+ڎábCŞÂFhŽ€ţ ›§Gýş°<§Sh0Ç˙Ť¦=pNńl«?µĚu@f:ˇhˇÁ`Ë_ńXg›Ý퉻µ|ËމP+&OĘÂĘ^ŹÇâEšÁHÜ]ňä 9ŹŰ=Şř\vK(‹Q5E}Fźő”cĂ| í÷ĄzEmŃ‹µzÖşŃvkľę­§ŃеeĎ»^¬M… =‚!â0e ć hť UΓ"5›'béżăfa*•Ĺĺq%Ç5lŔ§‘TDŤÔ^Î9ÜR-çĹ&pąr›ý@Ĺ;‘Ş yÁąĄ đ>qŐ8ęľ¶pnDň|Â^\ŹdSÁ›Ěٲ©p@@‡IţÖôŃÎa™D˘0ŘV¶]ٱ„_ČGn%&ťţ¬“ż2™NŠőO4Ť˘ŇwjYzB:eü“@MĚPř'4KĐĺČWîć}Wćyż©(PäÂË‘ô‡Ś‘8Qž!SxÎ'hb݊蘆1´qp¶G,ö®Ö¨=^‹*VZsÚJ§˛ńB1H¤Ňéd3łŘV'“ÉE ‡ęRËĆ‚Eh/ÔöŐ}µtĆO$Ó™\5ßk4Ę‘V¦™JĄ2)_'ŘČ&“™(…‰­V±$0˛‰Öô!X|ő'R˝#ł ßtČbÉ=p– Ş Ř‰M&unŽĘe3HT•”żAîDÉ^Ź ką f ÖÎK©Á nó#ĐL0Ŕ|:“m·gČrHF Ĺ)¤ăoÂg!ţżÚo ŕó‘[Ü͢~y€@ČňË<’Ě!@pČ„*’l ńŃőźŕ({Ŕ‡”â[yĂ;2gö :Ý«łúĚ€ TÉ?‹Ą2,ą÷IudTI©_h˙ ô^s ŕ͸śu~7ÎŹßQ·áS ů"<‹Î$‹ńÉŻËob©řüîB_ąąSxf‰śËJël´&îŕZňÇÉĆţä~?×ĚÖÚ^`UöKiCëî¸*Öd kgRŽ‚­-FÂA{˛ôđ˛ż×ět!h¶.(JXáÉŤó Ď Î6Ő–˝®Ţú‚Ž©E\ß"Wd ÇŻ¬ Ńeđbü~Í}Ľ˛ą,ńźgü7nI^;ޱ::qúÖB:ęÚ6¨MKoŮľ¨$°‡ŕĽ|Â<—6ÄĂ’ôA·‹óζu|CY‰;^˙/HŕÖ©Ts§`ăžÎóĄćů´! ČlúrzOkřß’  /pg?ú‹ŇçÝaRż.6m8v’5˝pĘ<ٶÚ'}§·¤šŘxĎî/ţ΢ /łŃ8Ę{šÎ»äťjnŢ»ëđKŇŢÓ¤ďȵ› ’W_Ä€óËhČűTChż–ÂK˘»wkt8‹?Üçţžć»ďyŹBBĽ‡v(!@‚főŐĘŤ˝ÜŚ–ç×w(@ŻzĐ”Ő â0X$É >Đí2zG Bv€Ľ? Äî‡3ŘË‚J7ŘÜb¬ŔŐDü~„ěywjr71l˙2€ D›ôÉŁ9ľçľŹÚ_ý•´佩Â)oâ,|`݆žÝB[ÚËçĽéäC›łĚ,˘ęb/âB"r Q·¬XxłÚňÎTćl6W<ôżQ>Ďý¦¶%=dÇrĐeEhd„ORň-Ű´Aâ÷cľzČűRťłˇaŰĐ?ĘÄZ÷s÷)Zn€‘Đm0ŰpîRvoéN€%-H}Úň®ÔĺV7tµ! 3~Ď+H4q§° m^C B·5ÇQéű±ďľä=i*ĚnęôEé#1<ß:ČĎKߊŇÍĺÔoV;@Ţ‘Ťoc·/RÚvŁ#×Ü6÷Dţ~ĄďyG Ş7Ďź/HöŻţwK/H”»iĺ lţBÚň~ÔćmѶ} @ş˙ęŹ6±Âq]ŤâF…ęYmŘňn„Č3[¸~‘6Č˙ţo±[šâ®7Ő!IîÝLĆÚňn”Pmá0H·k@ýFÓ˛ţâo,ą172˙Ä i WŚžţů_˝ŕž—Ď« ďEsÁ6-[Űź˙é–sĘF2÷J2n“Łáú÷W“|ĺôßż› ďEˇ­Ö©Ö˙ĹźnÜ#|&@ü<ó?Ŕ[-ďşţř›˙vYĎęýwżyöäy)íy'‚„ŰuŤţ˙ź-{dĎ™7ÚĂÁůĹýeŽî/«sVú÷N`9dd5ŻČĘţ˙ňĎŹ/NYţÓľÍ|—Laě-iČ;QiQci´şÍdřý?6tŞS/{™Îěó…n_ł=î͆Ľ8Đ›¶ŮI`űć} vZý1Цfak ÷¦ťŢ öĆţ  چ“ĺřř9 )A.ł˘l!Ź_Ą3w+ÂxSF*™§GűĚŹ„ŕžoQ÷ş F‚T¦ŰɵÉpţĽň7jbĺ)îˇŇ-¬î ´d§í5]®1ú €@ĂńĽ–iTSÍfˇ”Ne‹•\)äK…Č+ĺŮ\!SĘU˘•"6LćóąLˇQčWSąb%ŃĘ.×É߅٢_ě é©mĎńm”*éaÝG®ň2•^ú4H­\ąÝ˛Ó#Ô•-ţ~@ž¬ŰqŃ™Qě¸*8řžĂčí#Źwx¬ăźśśPl’Š4ńWnw€ěôµ—{ŁÜھѼť_Ll÷F‡đr9ŕĽ>nžŹč]‚6VĆ'şÍ{çÜöĎŐGkS×oÄJ–ëŃ=3OCvÓšźćóS+MÝßiét–ŚDmŻöHďŮéş*AÝAgPÔ›“&ĐĘŕy{Ök–f­q{Ü!ő1¤=ęu;ŐaĄ?nOZˇXaÜę/*ú [ˇUo4Óî 6K6J¦Ó'éŽ~«WVÉs3Ívż_k °a_Ç—c­Ń°5č´»U¨ŤßcŇíÖ:=nŞ]@BŤĆ´ëÔŽ†m ču:íę  Ý Č°ŐĐFc2iŹÚóŚuÝN·ŃiOzŁň¤ÖžŻîŮé:ۤ©·„#Ţń1‹-ń đ‚Ĺ&#PŽ8T¶@Eča1›EÁř@ŕQ…tq ŕÉDŽ,¦ HDşDx4ŔşŚcЎLe™¨{j…>]$Ŕc0L™*E] …H†ÉÔB‘ÁĄJ*ů#ä° ÜI›G=˘ČXÁö nâP>$<Ŕ+‰ŹÉbFq׋µÓk/ŞH8 ĺ'My ‘o%«­J öĄź8šů”¬;én¶đ#‹Č8Ř3ň‹%$šúCŽ— l*Ť% ±čéJďôHfɱąr2™&PŞV«Xa1GĎ<Îśý™Éby˘RÉ&}Uň‰Ł?qPs§•Ô§Ôă}ŞżMeđ źřGůŠ‘8'źĄyéEVoť-"Ťe* čĘŚ-¤±<ă´d§GI»Ţ‡‚ćŰ­V»Z©VKĹAv”öx‹íŁČ$ŐO KĹjLŹí~ąVŞÔË˝rŁÝhÖ2_®†ű¨WŤRĚI!h˝Ö¨ÖZéFka˝6ŞuőZ©–*ŐÎ+ăz±ŇGN“Ô0SŻ[Đ$_¨Të™ŃĽZ­• ůjµXĹť~®ÔěeV«Xw»ŮÍ´šífĽ•k&3­^=ÚÄ>`đÓaŇžĆ\¨PĚĹŃb´íäóŢš+ٲӣZL_żŮHGf3)<0qwáhĺÎ_ﯺ» ­\„üŕ=.uHTݰřląHÍ!č÷÷Osý2ŤŻB0§ňčtÁ`źI?9ePčě•Ĺ•C3t¬IDAT˛d§Ç¨ąhĄżt7/śş\ę7ôů.wűHuć5,¶ě—ý\ ěö0¬«NĂĄo,vŃ™uHÁčglźIŃ h´`#E1đÇťrHaÓ˝ŕ” ŕl°X źĂ<°ČFš€rW‚ěô(A˘ábŞI©řě*KĎa•]Lůý•É8ś˘ŃÚ“TÍ—˛Ć«'‰Q°xö‡­až·”NÇsÔq>îôsM©Ňü⯅‹–¸̦{«€Ŕą>8îMçÓůÎGŁ 0M&OĄ;Ť§O¨ Ćăńh<Ť'a†łZ,”;;@vzśLI ő/ˇĺŔČ2›şGŐSóIÄfvĘÇ"ůፓlb1DĚc"QJ6F9,ćÄÎ?&ŇäBŠÁ~B02LÚo¸¦“čŐdEN,‰DĎ‹ú/ľFCˇptq …˘+ }Ń`żäĄď&+îôe_x[&ĽŠ…*Xr&9ÔaáS!ŹŰnË©B/^űˇdÚtŤ-0 ‰”ě q9cŻLŔcşĺĆFÇăśĘ\ů #®sdĐs@&÷…4ô0„ E`d†@04GQś#3˙!îjq w€0†âź ¸ŘŢxĂ(tsÁZ×Č ×§h+›Ç땵äÝH±©QĹÇ  ‡‰d2‘Ś‹/ř×Drńďě[ňňűňăÜĹĹ—K‰Dřú’[¤¬ć×+M;@vz~^lcçĄĐŐţ˛ĆV‡‹ryńćŇĆ ;=żÚŇwc+ç\p^Ş(ݶňwČNĎ/XüŕćoJpV˘¬ÜÎôť^@ŽĐkÇ` Ai±ŞvW‘·d§PUńÚ1ŘXPR¤©ß]#Ü˛Ó ľ“m5Ŕ¸H׼ŻÁ´d§—ím™ŚşCó¨PßľßÉť^B5ŮkÇŕaÍĂăŰăěŮé%‹ÚŮ´ŽŢÔlĄU!ą‡»P˙Ţae €ń{č¶Ţňžäq -2–^ôFŰ"łßŇÇŰJ’T'ľĎň)ď[Ż’%6ľ©i»whČ{ROâˇ2Áă-íRM}|Ű’\Dű áŢ—űu DŁđLĽďyW’KbťP»ň·źxřŽsCH.[Ëöďs,c´*±zýÍÖW´ä])qň˛Sz«±‹ďÚ˘v€ěôF4á?a°đř”çĚäs™’;Ȥ*ţ´K<×đĹR˘^ŤŤˇl5IÖşó`°ë$ŇBédĘľZeżÓ(Ô Ů|¦…UŽŇéB éžh;@vz+r=ÁţĎE«öK»ŚtČ DÇGʏǬÎ1l đät‘É) x´CŹQ둿Ňx¶ŠůŹXGG"r'*2öŘÖź~lüő>ąt6k@\˙űwíÍ]ż®v€Ľ?Ťů[Í븦 @zűQ±VxŞ“źË›°Ç&ŤŔHkU‚aůÂ`Ei@_M–kŘZM„?‘ďłxd ±‰˛"ťäÄŢ'SXz&‹5á6—nń›?/żv"nŞ ďPNĎŁ˝^á10‚Řp4ĎÇt“Éh‚Ŕ`AFăńŘÁş„áĽu§ÓŮ›ŤĆ“éh€ć¬đdćŘx8á^ĐéŮîQŰ’ýÓ˙řÖJ-µäjôř"ä‡3čÄŔÁş3ťN.ż^»rČ ţ°•˙÷q{wŔk'4¶ä}ĘůčVČ9 M†"čB‹Śľřş8Âć#Á0CPÁĐYqI~ Ĺϡ‹K~y.ľô…ˇňĐŢ\ž_†xČś.W=,5]¨ŢŔë-LŠßň5ć=¶#ëęmŘfóÇL$čŔĽ[ÂíŃ'a–ß4ę™bAŻOg˛»MR«>8‹ʰN+v†`lđť4f×Y-PAéSQźŢ*8kNś2}ÎÝoň/˛źÖ–Úň.ĺµ?Ňă äSîžăXG ĎD_Ŕř·ß†ŘT9 Š2ą±üá2“F qID=6"2¦ĄňĄ|űIƤBńWŇ”Ą´ś0śä)gHś"†Đë"Ţ*čÂĹŔ€) ĎŔŮt2EW.˘ŠĺŤŻťĚŘwŞ˙‘vzĎiéT"i#Řŕą| řU‰>jĆš:‹gšáëůAÁ«Č®VçřĺÉG‡]y9W˛Źy9kHˇš” ˝^o‘˘†łĚ~ DÖ©'sax †‚˙ŹáßŃ9ĽÜJ¤fJ` Ś_ëSc_® ĐŇGşí9‚hĘ Őî ;=^áGV?®¦š,ß×+-íąµż8>;Ťňáą«‘l†˙E±Ë†=rq°rnµ9¨1i& "÷;Í|ąb’3řV±oÄaóMf]'µŹv›‚Á™6°bŚîđŞŹĂ/TMÓ•öSµHkl{|;@vzĽ@áă&ő^ź‹…¬öD]}Ga AVĎŁ÷uzˇK§—€ źÔߨ¨“*VÜ?îĄ÷>«ŘĚ\źĚüV}|` %Ç1›Lűţř´$•Ů)f|đ~śü0ŔxôÔ~řÄţĚöĄ”źíŮé J+5”p1Ő]vTy“ř7¬,vT…Ăč Ô3áMoÜÁ`Š ¸ŰX]\ĹřĚ'„,Â˙,ş¶bEWU,Ł'ÝđU±¤łçË»mÓV,j,jŰSŻ/ d\umńyü.łŮáŐ‚CĚé×,UëČ8Ć"¸›ĽÇ4äŁý´d§'‘ĺăí (ÓhĐ;”AŞ´yQŠÉjäňřĄŠ+ç"z™Űk$’O’׊ń…ź&ŕŃPs)'8uęUŞ©&ę·ęuş~ŽĄ±Ş=˛ G˝ňóÁľ'[·Ű˛ÓT}TŹę Ę/IÚCËľČŐ§“öžáAĹ?qÓč§RĚň»Ł0ťĆd©Ţ(ĺIlßţ2jţżÇÖý~‰.řއ|íŢŃI5Bţő‘iĎ|(Ö,‚\dˇ »Wđ¶Š€éJ¸‘GV;Ŕv€ěô鳟Πi‹NšÔݲěPiAüNWÄëüN«Ó‰öŁÉšÝ®µ‡Ť~×ú˝rµ.Ş´©=†č Ůs¸ç†#mWöË˝,brĄ‘E·Ň.Đ!°[‚fŕĎűđ4?şU€ż&¤'%'0`ýä !;@vzŠĽGîĽ}ÁԦ͙Ecröos ޤ*(•(˘*—Ú"§üć4îGd®L¦tŠiëá'‚Žbn˘Â$Vp8ŤŞ \é{زӓäłlďi4¦nż^«t–§ ŐŠ#Zl^0ť`p˝Rď.HŞm°=h7Ş•eĎďdĐë^.ÚĂĄQ¬[ghűdIJ¦źPż +1Ězplřň”|Ä?­öăĘG‘Źafę'*÷żŠţ@ÓôŇâ•˙ ;=IsA}k? @ <®HGR~ ÁaîŃš`ĆĂN6ŇZŞśiÔ۱‘ń{±:ď"őlRš!d’`§;)¸ą,`ÖśŽńN‹ŮFW·s+ ąH¶hËeň™d,ťŠ†±X:Ž8‰+•+×j»;÷Eَp4‘IŘâÖR6ďZi®ěŮéiĘIśÔ±®Eë¸,úőAQ$bă}‘Ę0úrÄfJÖ!ÝËb¨ †€oZ,¸ŚRgúĂgćw‡ëÓŽíJź9·qTÜßd{ź)26_|Jě^ÂL'皎ŰÓ0泥€Aˇ8\|łŮâ~ ˙ ŚKů°ĽŽ˙› ş—Ţ'ł¸ţµŰňľĄ‰nëýäb‘¸JIv›“Ź´ę1Kú­˘"Z 沚~|`ÜgęXçű.…ö3użţ‡ýÔBô›y&ú˘Ó1é$ŔBŰŁKľË¶Č„ď …ß9’ÄW’%2áá?\J.ľ0%WŽ$ń­’°ß®ľ;@Ţłú[·ÓU¬T¦]/ŐĘ ‚•j…)Zjµ˘ ©ćóąTĄŘĹ›ĎőhŁ‰Î’ÓB!&‘ü¨ĐĚw% ÷' ÜÜΗ+őZw–/µŕ^ăr= ] Fá¶‹/ŹačşUÖ›ŘaČ»–ŰjúKýeĐ[WN ‘»{[ŮÎhĂN_G¨´ť‡{ť‹Ż˝ĺ(řr¦ÖJ+˝ŻĹłX4L•;Ć2w€ěô•Tn·ŁÎ=€¤)éDŃ;É83ľ@$Y-Ąä™x˝/©Ç3Ţ@0•ŠÖÂđNß×AZˇ*}·Ń÷ ;}-Y·[}{ d–üĚâ ëä§nëÁ>źéo™úâ˛Ő!,%Üm™á t”HCwŁ„íŮéëiĆonăü@R|©oÂX"ąĚe7'Ç1µ2âH T˝ĘB rx"©"p§ď @¦I•ĐŮz űyČN_M9é6˝= őť— śsڇDĐĹpĹÂfŕśĐ€ůt~÷śëEĂĂX~xĺťľž ŰěĘÖ`—Šw©´P1–Y~śť}”Î?îö[eĐşM¨No´ŰÔťľž&Ľ-ěx&ý2rq‹“ž«—Č#›ÚZٲÓWTf«JÖKhUČ|ÝÍ—8îŮékJ˙Ş[ß.šĺöĆVŚîŮékjÂŰŞ'ë95ĎëƸĄŻ ;}UĺĹŻ˛94T±µ`{Ź;@vúş2?aËG i:EŠř#5b;@vúÚ_7ˡ]źD}d7Ö˘‘ţN¶ńÜňăŃ\ô({Ö éxŲÇwéަ]/ÖNݎwřô@® íĄďó7’zË•ôݍ ?&ĹeĎŰy:ŠČEÎć¶äG¦´x;CY7–LBmfË™]%Şß·…śžm\ŰXŻ8-eČŹMÎ˙{g÷šČ˛ đÝ˝˙Ŕ>îÓ˛÷aŮ·ĺ>-{vYî={Ď™Ź0“/b?PQŃ ˘¨(ŠbP1(˘"ŠH$  D """"""Ť "ŤMÓ4 MÝÖą“dΙ9“é8\ćR?!¶ŐŐU¨ÝUŐUĺ'Ř?m†ť·ËŻľ®ţ-b_˛ÚlnG@AţŢ ˙Áeye/úëáŔ§±„vűăMŹŮbóńlĐëŽéů}1¶™:2#đÉŻŻ^/ÁüÉvöYţm¦˝ ň÷ů‡ř篞ƄËHÂd˝b;¸ íą„ rŠcűA˝CÁwî'±âjA/ËńbVŇÎ×s}ľ­™®0r<[ĎşU0/Vłĺ,ĺśg6›r‚MVSd´ăÓzF=^mn†PČ.IüËďB_y ň‡üwŰĺýpŕöky4 lgiú`$=>Ű|jĄ¸)‹X(°xO÷ů!‡§V›}˝î©ČbăŘUh÷xćŽ×E™gŇ©đxŇĺQpĺB­ÚťV”ęĐfE#(„Q«ăS¸t˙öĂąă+pę~˙»ú×ősŠň^׹ęgM‹xq%U+ĎuőCj%q—#_cvď«őŇWÇ•ↇ·‡‡nŽ d÷E^ ˙čę?˝bµ\ ÓҰŃ)ä{ŤÜŘIÜeTÄeŁÂB]ë’Ň˙*Ź®\¨?kĽ|+–[T«őË9ń®4ěV۵i"Ęť­vîf•aux—ŻÖFĺ«4X–ËŤ:R2^U:ĺAń¶xwćÚńţő´Ň/·ÚĺJ;?,•ąé aaBźźČ–lúą)üf/VůŁasşň™N˛Eţ—Ăëdi˝ů‚‚@Řđ Ö¬·ź:>}˘—j'‚¬›Řŕ~ĽbÍH0ţto3=ˇĐmćk/Zťz ®czą‰ °áAÄńÖ-®Đ*.FÓ^„,úĂĚť,Čd<óét0^ĐŮn·v«CëŁëô|x_ív"Čr/Ě&#ڇ˝ć Ń_őŢŃăád6Z|–E‡$=^NÄHЦs`S[ýo*/CŻÝ#]Ď×Hµ´ědG&~—^uçł é»/  V-púóoţäüiĹé‚3>Ç%Ó8´­‰Ëdŕ š—'»JciÜP+ R9ꋹ˙X]*e¶i©I“đOZ“»ßň̸ü­ĐÁ+Rj§(É …żąđÝ÷€îFÁë¨A+8y7”(´RµŔ«Öô u‹AˇÇTýđŕô˙xüĄk!:ĆÍ:‡íeéĺ|żlŽ^uä{ĺ0 TŇř_`hg¤/äú—Ş}A  P  2Ż˘őĆ]™^¶şť&Rźä‘zŁŽŽ˝óa{RеjuR@@ŻÖÉ\/jő†Et š˝ĆýK[»-ŁŤŃ 4_7ÍF–o)µÚMŠšÎf%&Ë6B4M+×+ĺ^aHVkD§ŘEËłňş˛Ł&ÚDFÍQهÔkHů†qfRo¶ČM·0‚w+ľŘ‹5i˙2„Ş­7_ýęÇ/4~^,ĽňŚXůŁ·ˇ 6l!óęz·ĽýÔ~Ť˘Ń›/ ‘ďH˛8˝huîéöŁ>ŞâĽż)`;Ůä}©¶űM#äŻT~ýQâP6‚ĄĂŰ|ĺ*×Ȥ’ĆÚm7ß·YéwéL«X»î ýhůîÖUĄúů›Jł| ú7…h%ßÁďB•ňm%ź˝,o%v#ČĘ(2ßĹoóŐBˇ“1Ië Őj]Ő[ŤV¶•«%Ż[vąŃ>ÍZíŚt{˛‚ać±ýĆ’ż.'<őTPő|.‹‹Ľý›âMłqEĚrp$Â’Ť eçÄ)ĺüá ,ÇF™ůxßî …^§U8ń÷…NůęvÁúN­Â€ÄK¦NOxVaXt’(„ç'AÎć1lGm±J,}{r™I,©ůLbUX4NÇ÷†#hÍ[¦#µâŤíZëľX$ć)]&ትvľt”RűżqZ!vHőü Äş¨đRP;6‚ô¤B•·ŕ±Üu&•Ů«9ĺČĽ¸Ţl“ ,i7¸Ň‰Ś~ăŐL­Sq˛ÎŞđmă ! ŘÔaXV-Ů$µ#A˝Ň"·ř<>ąÂ$0™ŚTĘ(şZCvµÖëĐś]u2b‘TĚ—ą—s“ŚkŇh‹ŞáÂăWČMŠł”9ŘLÄ3 “WT¦ &%×()d‚‡‚@رm (ŽŇÔfÓć5¶Ćp›ăôša5öڱZŻ0ŁWë5JP`±Šb] śq°Ŕ–«5Žá]zWm5Ę”‚\R+ÔiľĂĚČŐ#ÖÄG ć 1ĹYâ‹aúr˛`Š´şM–V›čLÉgŘz^«/{€‰C¦ü(ĆüOK@ 8ÂŽG˝X›V.M’ŹŰâÔňs-s’ąlőצýű5ÖwŢ‹µţ8sl–`$ š&)’¸źđ¸m›ż/˝]-ţ3‰CA lx/ąŮŔ7ÔÄîć^Â|IRLőcŞ#E1ő $7ˇ›Ív$ 7áädEŤś›ŁÝ 2[’ﳦIˇ)’«ŁT‹ĂŁ©HĽBćFŇLś@›‰– ¦@čÝQ„)% (ró‡¦t‚@ذ¤çń©—GŤ™/%WôÚ*}Ŕ‘¶şŤŢyžË„^–Ó›ÎóŢPüŽŚ˝ó{c˘ĺv™ÂÖU#*I„‚ň¬yşcALö¨ßd8‹Zmj·ż¶{rˇě8äŮsŢKĎŢč G]h‘@†ç·#č `Ó"#w Ćź9<Őę▴æ{xű aĂVë“#ÉPިs”2i·ŞŘű)pÔú.ŕUj˝?s¸‹Č™BjŘŹ´(ÍK‘÷8†'ŹĽz{Ä I¸ĽS˙®±sě‚Ăŕ™Ü­ ĺdkďĎ×Rţ™R ¤I«d/¶Źľťťěď«íKkŕ ÝQĹů>W<Â3É›ÚT¨ů·Äh4=$°a+Č­Í ř¬HËôűGŐĂ+çe$”şľ.‚b¨˝ńąVĹh(ŕr”í(™KZ ® ¸˛Y Á(ŐME˘—?Äw,H1˝ľČ%Ţą“ˇHÉ57 FÜ‘íó§kÎúůĚ6ÉP vsa;JÉ1 šă‹P6·\_ foŇIwÁ–N=$°áŻš4gŁÓÇ; äKż‘Ň®éO™Űţ«8¶ ›ą~µ®†_B¦ŃţĄ űMf>Εü¸Ň“źťŢ‹ă€Úţ䊠1[ŻVŠA6=E4b§ż8Ů_‚$h‚Â(‚ ťóʧ ’1…9Ë›`ćđá…ŔÝÂ$Š1ůř&ŁuŻşÂ©M9ŤÁ<“E2…b˛%ű@ŕ=ŠĆ™ź›ó$IQÓ!“ę©M+n&&ČIĽ×˛Şô\¬«CA ¬¸„ľ>?Wřü§”G ;Ź[UEŔŕŹ»äö°]ĄNş„‹"ĺŇu‡CvaÍ{4:{Ô}fô?Ü€v"™ŤÇ|&™Óp–2{“^'°ĘM‰$Uđy§yąáÂ3ś9=ő1O{ö»yxÇaŐôł‚ĚňéřZń¸N łe_2 ×ňΛîŰgI aĹ˝ ¤m_¨řA/ˇtňWfńŰAA#ńşąrëőâźd/Lr_¦xAěŕPj4ŘťgĘă#×űO#Ă>;dÎ-ó.~ŠřŇWf‘#§8;ńľ9[x+µŹ' ú·{ÁŁ ˙ZĺśKŚjŽ+ĄłűŻĎ‹Kő±ča!E>® 8Ňă?‡WYaű*ýó@™„m;ţP"ä “WńLşéu˛ą‹J^nl¤óŃXÚŤ†Ž Ë{ĽńL2WL%b™«X%šn~Řw'‚ĐŃT9sŤFbIWŃŮE ©\$šËá­T%LÄŐŔU*ŻŞD<–ňzâ±uŮí*d"Tš—÷ő]Ę”bô]<ń$¨š3Óóµ˝HĽ °ă ¦’Ź·9ŞŰ†ń2ń©µß/Sŕ™|qńęröł§*—_¸ aĄ5Ř~űG?ěöO„~8)É?·,lVwGźşŻ; ŮăgďżńDAĆíőhďô'sd:›ź¸T6ň˝óDAt<­ZÇ5qu.—ěX÷Ó·‹‚@ľwž*ň@ś•˝ăH 'ú_®Ţ“6#‚@ľwž(HżÝ/M:«ůśhŹŰH«9zŇNoPČ÷NͶś?ĹrűY,ćŰćč)WőµPČ÷ÍXgţvüěwöy6PČ.ŘN!üVü ý̇­Db‚%tEXtdate:create2014-02-02T01:59:54-08:00*ZPŐ%tEXtdate:modify2014-02-02T01:59:47-08:00¦EňjIEND®B`‚libtorrent-rasterbar-1.1.13/docs/tuning.html000066400000000000000000000711621351156116000210740ustar00rootroot00000000000000 libtorrent manual

libtorrent manual

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

tuning libtorrent

libtorrent expose most parameters used in the bittorrent engine for customization through the settings_pack. This makes it possible to test and tweak the parameters for certain algorithms to make a client that fits a wide range of needs. From low memory embedded devices to servers seeding thousands of torrents. The default settings in libtorrent are tuned for an end-user bittorrent client running on a normal desktop computer.

This document describes techniques to benchmark libtorrent performance and how parameters are likely to affect it.

profiling

libtorrent is instrumented with a number of counters and gauges you can have access to via the session_stats_alert. First, enable these alerts in the alert mask:

settings_pack p;
p.set_int(settings_mask::alert_mask, alert::stats_notification);
ses.apply_settings(p);

Then print alerts to a file:

std::vector<alert*> alerts;
ses.pop_alerts(&alerts);

for (auto* a : alerts) {
        std::cout << a->message() << "\n";
}

If you want to separate generic alerts from session stats, you can filter on the alert category in the alert, alert::category().

The alerts with data will have the type session_stats_alert and there is one session_log_alert that will be posted on startup containing the column names for all metrics. Logging this line will greatly simplify interpreting the output.

The python scrip in tools/parse_session_stats.py can parse the resulting file and produce graphs of relevant stats. It requires gnuplot.

reducing memory footprint

These are things you can do to reduce the memory footprint of libtorrent. You get some of this by basing your default settings_pack on the min_memory_usage() setting preset function.

Keep in mind that lowering memory usage will affect performance, always profile and benchmark your settings to determine if it's worth the trade-off.

The typical buffer usage of libtorrent, for a single download, with the cache size set to 256 blocks (256 * 16 kiB = 4 MiB) is:

read cache:      128.6 (2058 kiB)
write cache:     103.5 (1656 kiB)
receive buffers: 7.3   (117 kiB)
send buffers:    4.8   (77 kiB)
hash temp:       0.001 (19 Bytes)

The receive buffers is proportional to the number of connections we make, and is limited by the total number of connections in the session (default is 200).

The send buffers is proportional to the number of upload slots that are allowed in the session. The default is auto configured based on the observed upload rate.

The read and write cache can be controlled (see section below).

The "hash temp" entry size depends on whether or not hashing is optimized for speed or memory usage. In this test run it was optimized for memory usage.

disable disk cache

The bulk of the memory libtorrent will use is used for the disk cache. To save the absolute most amount of memory, you can disable the cache by setting settings_pack::cache_size to 0. You might want to consider using the cache but just disable caching read operations. You do this by settings settings_pack::use_read_cache to false. This is the main factor in how much memory will be used by the client. Keep in mind that you will degrade performance by disabling the cache. You should benchmark the disk access in order to make an informed trade-off.

remove torrents

Torrents that have been added to libtorrent will inevitably use up memory, even when it's paused. A paused torrent will not use any peer connection objects or any send or receive buffers though. Any added torrent holds the entire .torrent file in memory, it also remembers the entire list of peers that it's heard about (which can be fairly long unless it's capped). It also retains information about which blocks and pieces we have on disk, which can be significant for torrents with many pieces.

If you need to minimize the memory footprint, consider removing torrents from the session rather than pausing them. This will likely only make a difference when you have a very large number of torrents in a session.

The downside of removing them is that they will no longer be auto-managed. Paused auto managed torrents are scraped periodically, to determine which torrents are in the greatest need of seeding, and libtorrent will prioritize to seed those.

socket buffer sizes

You can make libtorrent explicitly set the kernel buffer sizes of all its peer sockets. If you set this to a low number, you may see reduced throughput, especially for high latency connections. It is however an opportunity to save memory per connection, and might be worth considering if you have a very large number of peer connections. This memory will not be visible in your process, this sets the amount of kernel memory is used for your sockets.

Change this by setting settings_pack::recv_socket_buffer_size and settings_pack::send_socket_buffer_size.

peer list size

The default maximum for the peer list is 4000 peers. For IPv4 peers, each peer entry uses 32 bytes, which ends up using 128 kB per torrent. If seeding 4 popular torrents, the peer lists alone uses about half a megabyte.

The default limit is the same for paused torrents as well, so if you have a large number of paused torrents (that are popular) it will be even more significant.

If you're short of memory, you should consider lowering the limit. 500 is probably enough. You can do this by setting settings_pack::max_peerlist_size to the max number of peers you want in a torrent's peer list. This limit applies per torrent. For 5 torrents, the total number of peers in peerlists will be 5 times the setting.

You should also lower the same limit but for paused torrents. It might even make sense to set that even lower, since you only need a few peers to start up while waiting for the tracker and DHT to give you fresh ones. The max peer list size for paused torrents is set by settings_pack::max_paused_peerlist_size.

The drawback of lowering this number is that if you end up in a position where the tracker is down for an extended period of time, your only hope of finding live peers is to go through your list of all peers you've ever seen. Having a large peer list will also help increase performance when starting up, since the torrent can start connecting to peers in parallel with connecting to the tracker.

send buffer watermark

The send buffer watermark controls when libtorrent will ask the disk I/O thread to read blocks from disk, and append it to a peer's send buffer.

When the send buffer has fewer than or equal number of bytes as settings_pack::send_buffer_watermark, the peer will ask the disk I/O thread for more data to send. The trade-off here is between wasting memory by having too much data in the send buffer, and hurting send rate by starving out the socket, waiting for the disk read operation to complete.

If your main objective is memory usage and you're not concerned about being able to achieve high send rates, you can set the watermark to 9 bytes. This will guarantee that no more than a single (16 kiB) block will be on the send buffer at a time, for all peers. This is the least amount of memory possible for the send buffer.

You should benchmark your max send rate when adjusting this setting. If you have a very fast disk, you are less likely see a performance hit.

reduce executable size

Compilers generally add a significant number of bytes to executables that make use of C++ exceptions. By disabling exceptions (-fno-exceptions on GCC), you can reduce the executable size with up to 45%. In order to build without exception support, you need to patch parts of boost.

Also make sure to optimize for size when compiling.

Another way of reducing the executable size is to disable code that isn't used. There are a number of TORRENT_* macros that control which features are included in libtorrent. If these macros are used to strip down libtorrent, make sure the same macros are defined when building libtorrent as when linking against it. If these are different the structures will look different from the libtorrent side and from the client side and memory corruption will follow.

One, probably, safe macro to define is TORRENT_NO_DEPRECATE which removes all deprecated functions and struct members. As long as no deprecated functions are relied upon, this should be a simple way to eliminate a little bit of code.

For all available options, see the building libtorrent secion.

high performance seeding

In the case of a high volume seed, there are two main concerns. Performance and scalability. This translates into high send rates, and low memory and CPU usage per peer connection.

file pool

libtorrent keeps an LRU file cache. Each file that is opened, is stuck in the cache. The main purpose of this is because of anti-virus software that hooks on file-open and file close to scan the file. Anti-virus software that does that will significantly increase the cost of opening and closing files. However, for a high performance seed, the file open/close might be so frequent that it becomes a significant cost. It might therefore be a good idea to allow a large file descriptor cache. Adjust this though settings_pack::file_pool_size.

Don't forget to set a high rlimit for file descriptors in your process as well. This limit must be high enough to keep all connections and files open.

disk cache

You typically want to set the cache size to as high as possible. The settings_pack::cache_size is specified in 16 kiB blocks. Since you're seeding, the cache would be useless unless you also set settings_pack::use_read_cache to true.

In order to increase the possibility of read cache hits, set the settings_pack::cache_expiry to a large number. This won't degrade anything as long as the client is only seeding, and not downloading any torrents.

There's a guided cache mode. This means the size of the read cache line that's stored in the cache is determined based on the upload rate to the peer that triggered the read operation. The idea being that slow peers don't use up a disproportional amount of space in the cache. This is enabled through settings_pack::guided_read_cache.

In cases where the assumption is that the cache is only used as a read-ahead, and that no other peer will ever request the same block while it's still in the cache, the read cache can be set to be volatile. This means that every block that is requested out of the read cache is removed immediately. This saves a significant amount of cache space which can be used as read-ahead for other peers. To enable volatile read cache, set settings_pack::volatile_read_cache to true.

uTP-TCP mixed mode

libtorrent supports uTP, which has a delay based congestion controller. In order to avoid having a single TCP bittorrent connection completely starve out any uTP connection, there is a mixed mode algorithm. This attempts to detect congestion on the uTP peers and throttle TCP to avoid it taking over all bandwidth. This balances the bandwidth resources between the two protocols. When running on a network where the bandwidth is in such an abundance that it's virtually infinite, this algorithm is no longer necessary, and might even be harmful to throughput. It is adviced to experiment with the session_setting::mixed_mode_algorithm, setting it to settings_pack::prefer_tcp. This setting entirely disables the balancing and unthrottles all connections. On a typical home connection, this would mean that none of the benefits of uTP would be preserved (the modem's send buffer would be full at all times) and uTP connections would for the most part be squashed by the TCP traffic.

send buffer low watermark

libtorrent uses a low watermark for send buffers to determine when a new piece should be requested from the disk I/O subsystem, to be appended to the send buffer. The low watermark is determined based on the send rate of the socket. It needs to be large enough to not draining the socket's send buffer before the disk operation completes.

The watermark is bound to a max value, to avoid buffer sizes growing out of control. The default max send buffer size might not be enough to sustain very high upload rates, and you might have to increase it. It's specified in bytes in settings_pack::send_buffer_watermark.

peers

First of all, in order to allow many connections, set the global connection limit high, settings_pack::connections_limit. Also set the upload rate limit to infinite, settings_pack::upload_rate_limit, 0 means infinite.

When dealing with a large number of peers, it might be a good idea to have slightly stricter timeouts, to get rid of lingering connections as soon as possible.

There are a couple of relevant settings: settings_pack::request_timeout, settings_pack::peer_timeout and settings_pack::inactivity_timeout.

For seeds that are critical for a delivery system, you most likely want to allow multiple connections from the same IP. That way two people from behind the same NAT can use the service simultaneously. This is controlled by settings_pack::allow_multiple_connections_per_ip.

In order to always unchoke peers, turn off automatic unchoke by setting settings_pack::choking_algorithm to fixed_slot_choker and set the number of upload slots to a large number via settings_pack::unchoke_slots_limit, or use -1 (which means infinite).

torrent limits

To seed thousands of torrents, you need to increase the settings_pack::active_limit and settings_pack::active_seeds.

SHA-1 hashing

When downloading at very high rates, it is possible to have the CPU be the bottleneck for passing every downloaded byte through SHA-1. In order to enable calculating SHA-1 hashes in parallel, on multi-core systems, set settings_pack::aio_threads to the number of threads libtorrent should perform I/O and do SHA-1 hashing in. Only if that thread is close to saturating one core does it make sense to increase the number of threads.

scalability

In order to make more efficient use of the libtorrent interface when running a large number of torrents simultaneously, one can use the session::get_torrent_status() call together with session::post_torrent_updates(). Keep in mind that every call into libtorrent that return some value have to block your thread while posting a message to the main network thread and then wait for a response. Calls that don't return any data will simply post the message and then immediately return, performing the work asynchonuously. The time this takes might become significant once you reach a few hundred torrents, depending on how many calls you make to each torrent and how often. session::get_torrent_status() lets you query the status of all torrents in a single call. This will actually loop through all torrents and run a provided predicate function to determine whether or not to include it in the returned vector.

To use session::post_torrent_updates() torrents need to have the flag_update_subscribe flag set. When post_torrent_updates() is called, a state_update_alert alert is posted, with all the torrents that have updated since the last time this function was called. The client have to keep its own state of all torrents, and update it based on this alert.

benchmarking

There is a bunch of built-in instrumentation of libtorrent that can be used to get an insight into what it's doing and how well it performs. This instrumentation is enabled by defining preprocessor symbols when building.

There are also a number of scripts that parses the log files and generates graphs (requires gnuplot and python).

disk metrics

To enable disk I/O instrumentation, define TORRENT_DISK_STATS when building. When built with this configuration libtorrent will create three log files, measuring various aspects of the disk I/O. The following table is an overview of these files and what they measure.

filename description
file_access.log This is a low level log of read and write operations, with timestamps and file offsets. The file offsets are byte offsets in the torrent (not in any particular file, in the case of a multi-file torrent). This can be used as an estimate of the physical drive location. The purpose of this log is to identify the amount of seeking the drive has to do.

file_access.log

The disk access log is a binary file that can be parsed and converted to human readable by the script tools/parse_access_log.py. This tool produces a graphical representation of the disk access and requires gnuplot.

understanding the disk threads

This section is somewhat outdated, there are potentially more than one disk thread

All disk operations are funneled through a separate thread, referred to as the disk thread. The main interface to the disk thread is a queue where disk jobs are posted, and the results of these jobs are then posted back on the main thread's io_service.

A disk job is essentially one of:

  1. write this block to disk, i.e. a write job. For the most part this is just a
    matter of sticking the block in the disk cache, but if we've run out of cache space or completed a whole piece, we'll also flush blocks to disk. This is typically very fast, since the OS just sticks these buffers in its write cache which will be flushed at a later time, presumably when the drive head will pass the place on the platter where the blocks go.
  2. read this block from disk. The first thing that happens is we look in the
    cache to see if the block is already in RAM. If it is, we'll return immediately with this block. If it's a cache miss, we'll have to hit the disk. Here we decide to defer this job. We find the physical offset on the drive for this block and insert the job in an ordered queue, sorted by the physical location. At a later time, once we don't have any more non-read jobs left in the queue, we pick one read job out of the ordered queue and service it. The order we pick jobs out of the queue is according to an elevator cursor moving up and down along the ordered queue of read jobs. If we have enough space in the cache we'll read read_cache_line_size number of blocks and stick those in the cache. This defaults to 32 blocks. If the system supports asynchronous I/O (Windows, Linux, Mac OS X, BSD, Solars for instance), jobs will be issued immediately to the OS. This especially increases read throughput, since the OS has a much greater flexibility to reorder the read jobs.

Other disk job consist of operations that needs to be synchronized with the disk I/O, like renaming files, closing files, flushing the cache, updating the settings etc. These are relatively rare though.

contributions

If you have added instrumentation for some part of libtorrent that is not covered here, or if you have improved any of the parser scrips, please consider contributing it back to the project.

If you have run tests and found that some algorithm or default value in libtorrent are suboptimal, please contribute that knowledge back as well, to allow us to improve the library.

If you have additional suggestions on how to tune libtorrent for any specific use case, please let us know and we'll update this document.

libtorrent-rasterbar-1.1.13/docs/tuning.rst000066400000000000000000000502231351156116000207330ustar00rootroot00000000000000================= libtorrent manual ================= :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none tuning libtorrent ================= libtorrent expose most parameters used in the bittorrent engine for customization through the ``settings_pack``. This makes it possible to test and tweak the parameters for certain algorithms to make a client that fits a wide range of needs. From low memory embedded devices to servers seeding thousands of torrents. The default settings in libtorrent are tuned for an end-user bittorrent client running on a normal desktop computer. This document describes techniques to benchmark libtorrent performance and how parameters are likely to affect it. profiling ========= libtorrent is instrumented with a number of counters and gauges you can have access to via the ``session_stats_alert``. First, enable these alerts in the alert mask:: settings_pack p; p.set_int(settings_mask::alert_mask, alert::stats_notification); ses.apply_settings(p); Then print alerts to a file:: std::vector alerts; ses.pop_alerts(&alerts); for (auto* a : alerts) { std::cout << a->message() << "\n"; } If you want to separate generic alerts from session stats, you can filter on the alert category in the alert, ``alert::category()``. The alerts with data will have the type ``session_stats_alert`` and there is one ``session_log_alert`` that will be posted on startup containing the column names for all metrics. Logging this line will greatly simplify interpreting the output. The python scrip in ``tools/parse_session_stats.py`` can parse the resulting file and produce graphs of relevant stats. It requires gnuplot__. __ http://www.gnuplot.info reducing memory footprint ========================= These are things you can do to reduce the memory footprint of libtorrent. You get some of this by basing your default ``settings_pack`` on the ``min_memory_usage()`` setting preset function. Keep in mind that lowering memory usage will affect performance, always profile and benchmark your settings to determine if it's worth the trade-off. The typical buffer usage of libtorrent, for a single download, with the cache size set to 256 blocks (256 * 16 kiB = 4 MiB) is:: read cache: 128.6 (2058 kiB) write cache: 103.5 (1656 kiB) receive buffers: 7.3 (117 kiB) send buffers: 4.8 (77 kiB) hash temp: 0.001 (19 Bytes) The receive buffers is proportional to the number of connections we make, and is limited by the total number of connections in the session (default is 200). The send buffers is proportional to the number of upload slots that are allowed in the session. The default is auto configured based on the observed upload rate. The read and write cache can be controlled (see section below). The "hash temp" entry size depends on whether or not hashing is optimized for speed or memory usage. In this test run it was optimized for memory usage. disable disk cache ------------------ The bulk of the memory libtorrent will use is used for the disk cache. To save the absolute most amount of memory, you can disable the cache by setting ``settings_pack::cache_size`` to 0. You might want to consider using the cache but just disable caching read operations. You do this by settings ``settings_pack::use_read_cache`` to false. This is the main factor in how much memory will be used by the client. Keep in mind that you will degrade performance by disabling the cache. You should benchmark the disk access in order to make an informed trade-off. remove torrents --------------- Torrents that have been added to libtorrent will inevitably use up memory, even when it's paused. A paused torrent will not use any peer connection objects or any send or receive buffers though. Any added torrent holds the entire .torrent file in memory, it also remembers the entire list of peers that it's heard about (which can be fairly long unless it's capped). It also retains information about which blocks and pieces we have on disk, which can be significant for torrents with many pieces. If you need to minimize the memory footprint, consider removing torrents from the session rather than pausing them. This will likely only make a difference when you have a very large number of torrents in a session. The downside of removing them is that they will no longer be auto-managed. Paused auto managed torrents are scraped periodically, to determine which torrents are in the greatest need of seeding, and libtorrent will prioritize to seed those. socket buffer sizes ------------------- You can make libtorrent explicitly set the kernel buffer sizes of all its peer sockets. If you set this to a low number, you may see reduced throughput, especially for high latency connections. It is however an opportunity to save memory per connection, and might be worth considering if you have a very large number of peer connections. This memory will not be visible in your process, this sets the amount of kernel memory is used for your sockets. Change this by setting ``settings_pack::recv_socket_buffer_size`` and ``settings_pack::send_socket_buffer_size``. peer list size -------------- The default maximum for the peer list is 4000 peers. For IPv4 peers, each peer entry uses 32 bytes, which ends up using 128 kB per torrent. If seeding 4 popular torrents, the peer lists alone uses about half a megabyte. The default limit is the same for paused torrents as well, so if you have a large number of paused torrents (that are popular) it will be even more significant. If you're short of memory, you should consider lowering the limit. 500 is probably enough. You can do this by setting ``settings_pack::max_peerlist_size`` to the max number of peers you want in a torrent's peer list. This limit applies per torrent. For 5 torrents, the total number of peers in peerlists will be 5 times the setting. You should also lower the same limit but for paused torrents. It might even make sense to set that even lower, since you only need a few peers to start up while waiting for the tracker and DHT to give you fresh ones. The max peer list size for paused torrents is set by ``settings_pack::max_paused_peerlist_size``. The drawback of lowering this number is that if you end up in a position where the tracker is down for an extended period of time, your only hope of finding live peers is to go through your list of all peers you've ever seen. Having a large peer list will also help increase performance when starting up, since the torrent can start connecting to peers in parallel with connecting to the tracker. send buffer watermark --------------------- The send buffer watermark controls when libtorrent will ask the disk I/O thread to read blocks from disk, and append it to a peer's send buffer. When the send buffer has fewer than or equal number of bytes as ``settings_pack::send_buffer_watermark``, the peer will ask the disk I/O thread for more data to send. The trade-off here is between wasting memory by having too much data in the send buffer, and hurting send rate by starving out the socket, waiting for the disk read operation to complete. If your main objective is memory usage and you're not concerned about being able to achieve high send rates, you can set the watermark to 9 bytes. This will guarantee that no more than a single (16 kiB) block will be on the send buffer at a time, for all peers. This is the least amount of memory possible for the send buffer. You should benchmark your max send rate when adjusting this setting. If you have a very fast disk, you are less likely see a performance hit. reduce executable size ---------------------- Compilers generally add a significant number of bytes to executables that make use of C++ exceptions. By disabling exceptions (-fno-exceptions on GCC), you can reduce the executable size with up to 45%. In order to build without exception support, you need to patch parts of boost. Also make sure to optimize for size when compiling. Another way of reducing the executable size is to disable code that isn't used. There are a number of ``TORRENT_*`` macros that control which features are included in libtorrent. If these macros are used to strip down libtorrent, make sure the same macros are defined when building libtorrent as when linking against it. If these are different the structures will look different from the libtorrent side and from the client side and memory corruption will follow. One, probably, safe macro to define is ``TORRENT_NO_DEPRECATE`` which removes all deprecated functions and struct members. As long as no deprecated functions are relied upon, this should be a simple way to eliminate a little bit of code. For all available options, see the `building libtorrent`_ secion. .. _`building libtorrent`: building.html high performance seeding ======================== In the case of a high volume seed, there are two main concerns. Performance and scalability. This translates into high send rates, and low memory and CPU usage per peer connection. file pool --------- libtorrent keeps an LRU file cache. Each file that is opened, is stuck in the cache. The main purpose of this is because of anti-virus software that hooks on file-open and file close to scan the file. Anti-virus software that does that will significantly increase the cost of opening and closing files. However, for a high performance seed, the file open/close might be so frequent that it becomes a significant cost. It might therefore be a good idea to allow a large file descriptor cache. Adjust this though ``settings_pack::file_pool_size``. Don't forget to set a high rlimit for file descriptors in your process as well. This limit must be high enough to keep all connections and files open. disk cache ---------- You typically want to set the cache size to as high as possible. The ``settings_pack::cache_size`` is specified in 16 kiB blocks. Since you're seeding, the cache would be useless unless you also set ``settings_pack::use_read_cache`` to true. In order to increase the possibility of read cache hits, set the ``settings_pack::cache_expiry`` to a large number. This won't degrade anything as long as the client is only seeding, and not downloading any torrents. There's a *guided cache* mode. This means the size of the read cache line that's stored in the cache is determined based on the upload rate to the peer that triggered the read operation. The idea being that slow peers don't use up a disproportional amount of space in the cache. This is enabled through ``settings_pack::guided_read_cache``. In cases where the assumption is that the cache is only used as a read-ahead, and that no other peer will ever request the same block while it's still in the cache, the read cache can be set to be *volatile*. This means that every block that is requested out of the read cache is removed immediately. This saves a significant amount of cache space which can be used as read-ahead for other peers. To enable volatile read cache, set ``settings_pack::volatile_read_cache`` to true. uTP-TCP mixed mode ------------------ libtorrent supports uTP_, which has a delay based congestion controller. In order to avoid having a single TCP bittorrent connection completely starve out any uTP connection, there is a mixed mode algorithm. This attempts to detect congestion on the uTP peers and throttle TCP to avoid it taking over all bandwidth. This balances the bandwidth resources between the two protocols. When running on a network where the bandwidth is in such an abundance that it's virtually infinite, this algorithm is no longer necessary, and might even be harmful to throughput. It is adviced to experiment with the ``session_setting::mixed_mode_algorithm``, setting it to ``settings_pack::prefer_tcp``. This setting entirely disables the balancing and unthrottles all connections. On a typical home connection, this would mean that none of the benefits of uTP would be preserved (the modem's send buffer would be full at all times) and uTP connections would for the most part be squashed by the TCP traffic. .. _`uTP`: utp.html send buffer low watermark ------------------------- libtorrent uses a low watermark for send buffers to determine when a new piece should be requested from the disk I/O subsystem, to be appended to the send buffer. The low watermark is determined based on the send rate of the socket. It needs to be large enough to not draining the socket's send buffer before the disk operation completes. The watermark is bound to a max value, to avoid buffer sizes growing out of control. The default max send buffer size might not be enough to sustain very high upload rates, and you might have to increase it. It's specified in bytes in ``settings_pack::send_buffer_watermark``. peers ----- First of all, in order to allow many connections, set the global connection limit high, ``settings_pack::connections_limit``. Also set the upload rate limit to infinite, ``settings_pack::upload_rate_limit``, 0 means infinite. When dealing with a large number of peers, it might be a good idea to have slightly stricter timeouts, to get rid of lingering connections as soon as possible. There are a couple of relevant settings: ``settings_pack::request_timeout``, ``settings_pack::peer_timeout`` and ``settings_pack::inactivity_timeout``. For seeds that are critical for a delivery system, you most likely want to allow multiple connections from the same IP. That way two people from behind the same NAT can use the service simultaneously. This is controlled by ``settings_pack::allow_multiple_connections_per_ip``. In order to always unchoke peers, turn off automatic unchoke by setting ``settings_pack::choking_algorithm`` to ``fixed_slot_choker`` and set the number of upload slots to a large number via ``settings_pack::unchoke_slots_limit``, or use -1 (which means infinite). torrent limits -------------- To seed thousands of torrents, you need to increase the ``settings_pack::active_limit`` and ``settings_pack::active_seeds``. SHA-1 hashing ------------- When downloading at very high rates, it is possible to have the CPU be the bottleneck for passing every downloaded byte through SHA-1. In order to enable calculating SHA-1 hashes in parallel, on multi-core systems, set ``settings_pack::aio_threads`` to the number of threads libtorrent should perform I/O and do SHA-1 hashing in. Only if that thread is close to saturating one core does it make sense to increase the number of threads. scalability =========== In order to make more efficient use of the libtorrent interface when running a large number of torrents simultaneously, one can use the ``session::get_torrent_status()`` call together with ``session::post_torrent_updates()``. Keep in mind that every call into libtorrent that return some value have to block your thread while posting a message to the main network thread and then wait for a response. Calls that don't return any data will simply post the message and then immediately return, performing the work asynchonuously. The time this takes might become significant once you reach a few hundred torrents, depending on how many calls you make to each torrent and how often. ``session::get_torrent_status()`` lets you query the status of all torrents in a single call. This will actually loop through all torrents and run a provided predicate function to determine whether or not to include it in the returned vector. To use ``session::post_torrent_updates()`` torrents need to have the ``flag_update_subscribe`` flag set. When post_torrent_updates() is called, a ``state_update_alert`` alert is posted, with all the torrents that have updated since the last time this function was called. The client have to keep its own state of all torrents, and update it based on this alert. benchmarking ============ There is a bunch of built-in instrumentation of libtorrent that can be used to get an insight into what it's doing and how well it performs. This instrumentation is enabled by defining preprocessor symbols when building. There are also a number of scripts that parses the log files and generates graphs (requires gnuplot and python). disk metrics ------------ To enable disk I/O instrumentation, define ``TORRENT_DISK_STATS`` when building. When built with this configuration libtorrent will create three log files, measuring various aspects of the disk I/O. The following table is an overview of these files and what they measure. +--------------------------+--------------------------------------------------------------+ | filename | description | +==========================+==============================================================+ | ``file_access.log`` | This is a low level log of read and write operations, with | | | timestamps and file offsets. The file offsets are byte | | | offsets in the torrent (not in any particular file, in the | | | case of a multi-file torrent). This can be used as an | | | estimate of the physical drive location. The purpose of | | | this log is to identify the amount of seeking the drive has | | | to do. | | | | +--------------------------+--------------------------------------------------------------+ file_access.log ''''''''''''''' The disk access log is a binary file that can be parsed and converted to human readable by the script ``tools/parse_access_log.py``. This tool produces a graphical representation of the disk access and requires ``gnuplot``. understanding the disk threads ============================== *This section is somewhat outdated, there are potentially more than one disk thread* All disk operations are funneled through a separate thread, referred to as the disk thread. The main interface to the disk thread is a queue where disk jobs are posted, and the results of these jobs are then posted back on the main thread's io_service. A disk job is essentially one of: 1. write this block to disk, i.e. a write job. For the most part this is just a matter of sticking the block in the disk cache, but if we've run out of cache space or completed a whole piece, we'll also flush blocks to disk. This is typically very fast, since the OS just sticks these buffers in its write cache which will be flushed at a later time, presumably when the drive head will pass the place on the platter where the blocks go. 2. read this block from disk. The first thing that happens is we look in the cache to see if the block is already in RAM. If it is, we'll return immediately with this block. If it's a cache miss, we'll have to hit the disk. Here we decide to defer this job. We find the physical offset on the drive for this block and insert the job in an ordered queue, sorted by the physical location. At a later time, once we don't have any more non-read jobs left in the queue, we pick one read job out of the ordered queue and service it. The order we pick jobs out of the queue is according to an elevator cursor moving up and down along the ordered queue of read jobs. If we have enough space in the cache we'll read read_cache_line_size number of blocks and stick those in the cache. This defaults to 32 blocks. If the system supports asynchronous I/O (Windows, Linux, Mac OS X, BSD, Solars for instance), jobs will be issued immediately to the OS. This especially increases read throughput, since the OS has a much greater flexibility to reorder the read jobs. Other disk job consist of operations that needs to be synchronized with the disk I/O, like renaming files, closing files, flushing the cache, updating the settings etc. These are relatively rare though. contributions ============= If you have added instrumentation for some part of libtorrent that is not covered here, or if you have improved any of the parser scrips, please consider contributing it back to the project. If you have run tests and found that some algorithm or default value in libtorrent are suboptimal, please contribute that knowledge back as well, to allow us to improve the library. If you have additional suggestions on how to tune libtorrent for any specific use case, please let us know and we'll update this document. libtorrent-rasterbar-1.1.13/docs/tutorial.html000066400000000000000000001662111351156116000214330ustar00rootroot00000000000000 libtorrent manual

libtorrent manual

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

tutorial

The fundamental feature of starting and downloading torrents in libtorrent is achieved by creating a session, which provides the context and a container for torrents. This is done with via the session class, most of its interface is documented under session_handle though.

To add a torrent to the session, you fill in an add_torrent_params object and pass it either to add_torrent() or async_add_torrent().

add_torrent() is a blocking call which returns a torrent_handle.

For example:

#include <libtorrent/session.hpp>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/magnet_uri.hpp>

namespace lt = libtorrent;
int main(int argc, char const* argv[])
{
        if (argc != 2) {
                fprintf(stderr, "usage: %s <magnet-url>\n");
                return 1;
        }
        lt::session ses;

        lt::add_torrent_params atp;
        lt::error_code ec;
        lt::parse_magnet_uri(argv[1], atp, ec);
        atp.save_path = "."; // save in current dir
        lt::torrent_handle h = ses.add_torrent(atp);

        // ...
}

Once you have a torrent_handle, you can affect it as well as querying status. First, let's extend the example to print out messages from the bittorrent engine about progress and events happening under the hood. libtorrent has a mechanism referred to as alerts to communicate back information to the client application.

Clients can poll a session for new alerts via the pop_alerts() call. This function fills in a vector of alert pointers with all new alerts since the last call to this function. The pointers are owned by the session object at will become invalidated by the next call to pop_alerts().

The alerts form a class hierarchy with alert as the root class. Each specific kind of alert may include additional state, specific to the kind of message. All alerts implement a message() function that prints out pertinent information of the alert message. This can be convenient for simply logging events.

For programatically react to certain events, use alert_cast<> to attempt a down cast of an alert object to a more specific type.

In order to print out events from libtorrent as well as exiting when the torrent completes downloading, we can poll the session for alerts periodically and print them out, as well as listening for the torrent_finished_alert, which is posted when a torrent completes.

#include <iostream>
#include <thread>
#include <chrono>

#include <libtorrent/session.hpp>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/magnet_uri.hpp>
#include <libtorrent/error_code.hpp>

int main(int argc, char const* argv[])
{
  if (argc != 2) {
    std::cerr << "usage: " << argv[0] << " <magnet-url>" << std::endl;
    return 1;
  }
  lt::settings_pack p;
  p.set_int(lt::settings_pack::alert_mask, lt::alert::status_notification
    | lt::alert::error_notification);
  lt::session ses(p);

  lt::add_torrent_params atp;
  lt::error_code ec;
  lt::parse_magnet_uri(argv[1], atp, ec);
  if (ec) {
    std::cerr << "invalid magnet URI: " << ec.message() << std::endl;
    return 1;
  }
  atp.save_path = "."; // save in current dir
  lt::torrent_handle h = ses.add_torrent(atp);

  for (;;) {
    std::vector<lt::alert*> alerts;
    ses.pop_alerts(&alerts);

    for (lt::alert const* a : alerts) {
      std::cout << a->message() << std::endl;
      // if we receive the finished alert or an error, we're done
      if (lt::alert_cast<lt::torrent_finished_alert>(a)) {
        goto done;
      }
      if (lt::alert_cast<lt::torrent_error_alert>(a)) {
        goto done;
      }
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
  }
  done:
  std::cout << "done, shutting down" << std::endl;
}

alert masks

The output from this program will be quite verbose, which is probably a good starting point to get some understanding of what's going on. Alerts are categorized into alert categories. Each category can be enabled and disabled independently via the alert mask.

The alert mask is a configuration option offered by libtorrent. There are many configuration options, see settings_pack. The alert_mask setting is an integer of the category flags ORed together.

For instance, to only see the most pertinent alerts, the session can be constructed like this:

lt::settings_pack pack;
pack.set_int(lt::settings_pack::alert_mask
        , lt::alert::error_notification
        | lt::alert::storage_notification
        | lt::alert::status_notification);

lt::session ses(pack);

Configuration options can be updated after the session is started by calling apply_settings(). Some settings are best set before starting the session though, like listen_interfaces, to avoid race conditions. If you start the session with the default settings and then immediately change them, there will still be a window where the default settings apply.

Changing the settings may trigger listen sockets to close and re-open and NAT-PMP, UPnP updates to be sent. For this reason, it's typically a good idea to batch settings updates into a single call.

session destruction

The session destructor is blocking by default. When shutting down, trackers will need to be contacted to stop torrents and other outstanding operations need to be cancelled. Shutting down can sometimes take several seconds, primarily because of trackers that are unresponsive (and time out) and also DNS servers that are unresponsive. DNS lookups are especially difficult to abort when stalled.

In order to be able to start destruction an wait for it asynchronously, one can call session::abort().

This call returns a session_proxy object, which is a handle keeping the session state alive while destructing it. It deliberately does not provide any of the session operations, since it's shutting down.

After having a session_proxy object, the session destructor does not block. However, the session_proxy destructor will.

This can be used to shut down multiple sessions or other parts of the application in parallel.

asynchronous operations

Essentially any call to a member function of session or torrent_handle that returns a value is a blocking synchronous call. Meaning it will post a message to the main libtorrent thread and wait for a response. Such calls may be expensive, and in applications where stalls should be avoided (such as user interface threads), blocking calls should be avoided.

In the example above, session::add_torrent() returns a torrent_handle and is thus blocking. For higher efficiency, async_add_torrent() will post a message to the main thread to add a torrent, and post the resulting torrent_handle back in an alert (add_torrent_alert). This is especially useful when adding a lot of torrents in quick succession, as there's no stall in between calls.

In the example above, we don't actually use the torrent_handle for anything, so converting it to use async_add_torrent() is just a matter of replacing the add_torrent() call with async_add_torrent().

torrent_status_updates

To get updates to the status of torrents, call post_torrent_updates() on the session object. This will cause libtorrent to post a state_update_alert containing torrent_status objects for all torrents whose status has changed since the last call to post_torrent_updates().

The state_update_alert looks something like this:

struct state_update_alert : alert
{
        virtual std::string message() const;
        std::vector<torrent_status> status;
};

The status field only contains the torrent_status for torrents with updates since the last call. It may be empty if no torrent has updated its state. This feature is critical for scalability.

See the torrent_status object for more information on what is in there. Perhaps the most interesting fields are total_payload_download, total_payload_upload, num_peers and state.

resuming torrents

Since bittorrent downloads pieces of files in random order, it's not trivial to resume a partial download. When resuming a download, the bittorrent engine must restore the state of the downloading torrent, specifically which parts of the file(s) are downloaded. There are two approaches to doing this:

  1. read every piece of the downloaded files from disk and compare it against its expected hash.
  2. save to disk the state of which pieces (and partial pieces) are downloaded, and load it back in again when resuming.

If no resume data is provided with a torrent that's added, libtorrent will employ (1) by default.

To save resume data, call save_resume_data() on the torrent_handle object. This will ask libtorrent to generate the resume data and post it back in a save_resume_data_alert. If generating the resume data fails for any reason, a save_resume_data_failed_alert is posted instead.

Exactly one of those alerts will be posted for every call to save_resume_data(). This is an important property when shutting down a session with multiple torrents, every resume alert must be handled before resuming with shut down. Any torrent may fail to save resume data, so the client would need to keep a count of the outstanding resume files, decremented on either save_resume_data_alert or save_resume_data_failed_alert.

The save_resume_data_alert looks something like this:

struct save_resume_data_alert : torrent_alert
{
        virtual std::string message() const;

        // points to the resume data.
        boost::shared_ptr<entry> resume_data;
};

resume_data points to an entry object. This represents a node or a tree of nodes in a bencoded structure, which is the native encoding scheme in bittorrent. It can be encoded into a byte buffer or file using bencode().

When adding a torrent with resume data, set the add_torrent_params::resume_data to contain the bencoded buffer of the resume data.

example

Here's an updated version of the above example with the following updates:

  1. not using blocking calls
  2. printing torrent status updates rather than the raw log
  3. saving and loading resume files
#include <iostream>
#include <thread>
#include <chrono>
#include <fstream>

#include <libtorrent/session.hpp>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/torrent_status.hpp>
#include <libtorrent/magnet_uri.hpp>
#include <libtorrent/error_code.hpp>

using clk = std::chrono::steady_clock;

// return the name of a torrent status enum
char const* state(lt::torrent_status::state_t s)
{
  switch(s) {
    case lt::torrent_status::checking_files: return "checking";
    case lt::torrent_status::downloading_metadata: return "dl metadata";
    case lt::torrent_status::downloading: return "downloading";
    case lt::torrent_status::finished: return "finished";
    case lt::torrent_status::seeding: return "seeding";
    case lt::torrent_status::allocating: return "allocating";
    case lt::torrent_status::checking_resume_data: return "checking resume";
    default: return "<>";
  }
}

int main(int argc, char const* argv[])
{
  if (argc != 2) {
    std::cerr << "usage: " << argv[0] << " <magnet-url>" << std::endl;
    return 1;
  }

  lt::settings_pack pack;
  pack.set_int(lt::settings_pack::alert_mask
    , lt::alert::error_notification
    | lt::alert::storage_notification
    | lt::alert::status_notification);

  lt::session ses(pack);
  lt::add_torrent_params atp;
  clk::time_point last_save_resume = clk::now();

  // load resume data from disk and pass it in as we add the magnet link
  std::ifstream ifs(".resume_file", std::ios_base::binary);
  ifs.unsetf(std::ios_base::skipws);
  atp.resume_data.assign(std::istream_iterator<char>(ifs)
    , std::istream_iterator<char>());
  lt::error_code ec;
  lt::parse_magnet_uri(argv[1], atp, ec);
  if (ec) {
    std::cerr << "invalid magnet URI: " << ec.message() << std::endl;
    return 1;
  }
  atp.save_path = "."; // save in current dir
  ses.async_add_torrent(atp);

  // this is the handle we'll set once we get the notification of it being
  // added
  lt::torrent_handle h;
  for (;;) {
    std::vector<lt::alert*> alerts;
    ses.pop_alerts(&alerts);

    for (lt::alert const* a : alerts) {
      if (auto at = lt::alert_cast<lt::add_torrent_alert>(a)) {
        h = at->handle;
      }
      // if we receive the finished alert or an error, we're done
      if (lt::alert_cast<lt::torrent_finished_alert>(a)) {
        h.save_resume_data();
        goto done;
      }
      if (lt::alert_cast<lt::torrent_error_alert>(a)) {
        std::cout << a->message() << std::endl;
        goto done;
      }

      // when resume data is ready, save it
      if (auto rd = lt::alert_cast<lt::save_resume_data_alert>(a)) {
        std::ofstream of(".resume_file", std::ios_base::binary);
        of.unsetf(std::ios_base::skipws);
        lt::bencode(std::ostream_iterator<char>(of)
          , *rd->resume_data);
      }

      if (auto st = lt::alert_cast<lt::state_update_alert>(a)) {
        if (st->status.empty()) continue;

        // we only have a single torrent, so we know which one
        // the status is for
        lt::torrent_status const& s = st->status[0];
        std::cout << "\r" << state(s.state) << " "
          << (s.download_payload_rate / 1000) << " kB/s "
          << (s.total_done / 1000) << " kB ("
          << (s.progress_ppm / 10000) << "%) downloaded\x1b[K";
        std::cout.flush();
      }
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(200));

    // ask the session to post a state_update_alert, to update our
    // state output for the torrent
    ses.post_torrent_updates();

    // save resume data once every 30 seconds
    if (clk::now() - last_save_resume > std::chrono::seconds(30)) {
      h.save_resume_data();
      last_save_resume = clk::now();
    }
  }

  // TODO: ideally we should save resume data here

done:
  std::cout << "\ndone, shutting down" << std::endl;
}

torrent files

To add torrent files to a session (as opposed to a magnet link), it must first be loaded into a torrent_info object.

The torrent_info object can be created either by filename a buffer or a bencoded structure. When adding by filename, there's a sanity check limit on the size of the file, for adding arbitrarily large torrents, load the file outside of the constructor.

The torrent_info object provides an opportunity to query information about the .torrent file as well as mutating it before adding it to the session.

bencoding

bencoded structures is the default data storage format used by bittorrent, such as .torrent files, tracker announce and scrape responses and some wire protocol extensions. libtorrent provides an efficient framework for decoding bencoded data through bdecode() function.

There are two separate mechanisms for encoding and decoding. When decoding, use the bdecode() function that returns a bdecode_node. When encoding, use bencode() taking an entry object.

The key property of bdecode() is that it does not copy any data out of the buffer that was parsed. It builds the tree structures of references pointing into the buffer. The buffer must stay alive and valid for as long as the bdecode_node is in use.

For performance details on bdecode(), see the blog post about it.

libtorrent-rasterbar-1.1.13/docs/tutorial.rst000066400000000000000000000303411351156116000212710ustar00rootroot00000000000000================= libtorrent manual ================= :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none tutorial ======== The fundamental feature of starting and downloading torrents in libtorrent is achieved by creating a *session*, which provides the context and a container for torrents. This is done with via the session_ class, most of its interface is documented under session_handle_ though. To add a torrent to the session, you fill in an add_torrent_params_ object and pass it either to `add_torrent()`_ or `async_add_torrent()`_. ``add_torrent()`` is a blocking call which returns a torrent_handle_. For example: .. code:: c++ #include #include #include #include namespace lt = libtorrent; int main(int argc, char const* argv[]) { if (argc != 2) { fprintf(stderr, "usage: %s \n"); return 1; } lt::session ses; lt::add_torrent_params atp; lt::error_code ec; lt::parse_magnet_uri(argv[1], atp, ec); atp.save_path = "."; // save in current dir lt::torrent_handle h = ses.add_torrent(atp); // ... } Once you have a torrent_handle_, you can affect it as well as querying status. First, let's extend the example to print out messages from the bittorrent engine about progress and events happening under the hood. libtorrent has a mechanism referred to as *alerts* to communicate back information to the client application. Clients can poll a session for new alerts via the `pop_alerts()`_ call. This function fills in a vector of alert pointers with all new alerts since the last call to this function. The pointers are owned by the session object at will become invalidated by the next call to `pop_alerts()`_. The alerts form a class hierarchy with alert_ as the root class. Each specific kind of alert may include additional state, specific to the kind of message. All alerts implement a message() function that prints out pertinent information of the alert message. This can be convenient for simply logging events. For programatically react to certain events, use `alert_cast<>`_ to attempt a down cast of an alert object to a more specific type. In order to print out events from libtorrent as well as exiting when the torrent completes downloading, we can poll the session for alerts periodically and print them out, as well as listening for the torrent_finished_alert_, which is posted when a torrent completes. .. include:: ../examples/bt-get.cpp :code: c++ :tab-width: 2 :start-after: */ alert masks ----------- The output from this program will be quite verbose, which is probably a good starting point to get some understanding of what's going on. Alerts are categorized into alert categories. Each category can be enabled and disabled independently via the *alert mask*. The alert mask is a configuration option offered by libtorrent. There are many configuration options, see settings_pack_. The alert_mask_ setting is an integer of the `category flags`_ ORed together. For instance, to only see the most pertinent alerts, the session can be constructed like this: .. code:: c++ lt::settings_pack pack; pack.set_int(lt::settings_pack::alert_mask , lt::alert::error_notification | lt::alert::storage_notification | lt::alert::status_notification); lt::session ses(pack); Configuration options can be updated after the session is started by calling `apply_settings()`_. Some settings are best set before starting the session though, like listen_interfaces_, to avoid race conditions. If you start the session with the default settings and then immediately change them, there will still be a window where the default settings apply. Changing the settings may trigger listen sockets to close and re-open and NAT-PMP, UPnP updates to be sent. For this reason, it's typically a good idea to batch settings updates into a single call. session destruction ------------------- The session destructor is blocking by default. When shutting down, trackers will need to be contacted to stop torrents and other outstanding operations need to be cancelled. Shutting down can sometimes take several seconds, primarily because of trackers that are unresponsive (and time out) and also DNS servers that are unresponsive. DNS lookups are especially difficult to abort when stalled. In order to be able to start destruction an wait for it asynchronously, one can call `session::abort()`_. This call returns a session_proxy_ object, which is a handle keeping the session state alive while destructing it. It deliberately does not provide any of the session operations, since it's shutting down. After having a session_proxy_ object, the session destructor does not block. However, the session_proxy_ destructor *will*. This can be used to shut down multiple sessions or other parts of the application in parallel. asynchronous operations ----------------------- Essentially any call to a member function of session_ or torrent_handle_ that returns a value is a blocking synchronous call. Meaning it will post a message to the main libtorrent thread and wait for a response. Such calls may be expensive, and in applications where stalls should be avoided (such as user interface threads), blocking calls should be avoided. In the example above, session::add_torrent() returns a torrent_handle_ and is thus blocking. For higher efficiency, `async_add_torrent()`_ will post a message to the main thread to add a torrent, and post the resulting torrent_handle_ back in an alert (add_torrent_alert_). This is especially useful when adding a lot of torrents in quick succession, as there's no stall in between calls. In the example above, we don't actually use the torrent_handle_ for anything, so converting it to use `async_add_torrent()`_ is just a matter of replacing the `add_torrent()`_ call with `async_add_torrent()`_. torrent_status_updates ---------------------- To get updates to the status of torrents, call `post_torrent_updates()`_ on the session object. This will cause libtorrent to post a state_update_alert_ containing torrent_status_ objects for all torrents whose status has *changed* since the last call to `post_torrent_updates()`_. The state_update_alert_ looks something like this: .. code:: c++ struct state_update_alert : alert { virtual std::string message() const; std::vector status; }; The ``status`` field only contains the torrent_status_ for torrents with updates since the last call. It may be empty if no torrent has updated its state. This feature is critical for scalability_. See the torrent_status_ object for more information on what is in there. Perhaps the most interesting fields are ``total_payload_download``, ``total_payload_upload``, ``num_peers`` and ``state``. resuming torrents ----------------- Since bittorrent downloads pieces of files in random order, it's not trivial to resume a partial download. When resuming a download, the bittorrent engine must restore the state of the downloading torrent, specifically which parts of the file(s) are downloaded. There are two approaches to doing this: 1. read every piece of the downloaded files from disk and compare it against its expected hash. 2. save to disk the state of which pieces (and partial pieces) are downloaded, and load it back in again when resuming. If no resume data is provided with a torrent that's added, libtorrent will employ (1) by default. To save resume data, call `save_resume_data()`_ on the torrent_handle_ object. This will ask libtorrent to generate the resume data and post it back in a save_resume_data_alert_. If generating the resume data fails for any reason, a save_resume_data_failed_alert_ is posted instead. Exactly one of those alerts will be posted for every call to `save_resume_data()`_. This is an important property when shutting down a session with multiple torrents, every resume alert must be handled before resuming with shut down. Any torrent may fail to save resume data, so the client would need to keep a count of the outstanding resume files, decremented on either save_resume_data_alert_ or save_resume_data_failed_alert_. The save_resume_data_alert_ looks something like this: .. code:: c++ struct save_resume_data_alert : torrent_alert { virtual std::string message() const; // points to the resume data. boost::shared_ptr resume_data; }; ``resume_data`` points to an entry_ object. This represents a node or a tree of nodes in a bencoded_ structure, which is the native encoding scheme in bittorrent. It can be encoded into a byte buffer or file using `bencode()`_. When adding a torrent with resume data, set the `add_torrent_params::resume_data`_ to contain the bencoded buffer of the resume data. example ------- Here's an updated version of the above example with the following updates: 1. not using blocking calls 2. printing torrent status updates rather than the raw log 3. saving and loading resume files .. include:: ../examples/bt-get2.cpp :code: c++ :tab-width: 2 :start-after: */ torrent files ------------- To add torrent files to a session (as opposed to a magnet link), it must first be loaded into a torrent_info_ object. The torrent_info_ object can be created either by filename a buffer or a bencoded structure. When adding by filename, there's a sanity check limit on the size of the file, for adding arbitrarily large torrents, load the file outside of the constructor. The torrent_info_ object provides an opportunity to query information about the .torrent file as well as mutating it before adding it to the session. bencoding --------- bencoded_ structures is the default data storage format used by bittorrent, such as .torrent files, tracker announce and scrape responses and some wire protocol extensions. libtorrent provides an efficient framework for decoding bencoded data through `bdecode()`_ function. There are two separate mechanisms for *encoding* and *decoding*. When decoding, use the `bdecode()`_ function that returns a bdecode_node_. When encoding, use `bencode()`_ taking an entry_ object. The key property of `bdecode()`_ is that it does not copy any data out of the buffer that was parsed. It builds the tree structures of references pointing into the buffer. The buffer must stay alive and valid for as long as the bdecode_node_ is in use. For performance details on `bdecode()`_, see the `blog post about it`__. __ https://blog.libtorrent.org/2015/03/bdecode-parsers/ .. _session: reference-Core.html#session .. _session_handle: reference-Core.html#session_handle .. _add_torrent_params: reference-Core.html#add_torrent_params .. _`add_torrent()`: reference-Core.html#add_torrent() .. _`async_add_torrent()`: reference-Core.html#add_torrent() .. _torrent_handle: reference-Core.html#torrent_handle .. _`pop_alerts()`: reference-Core.html#pop_alerts() .. _`alert`: reference-Alerts.html#alert .. _`alert_cast<>`: reference-Alerts.html#alert_cast() .. _torrent_finished_alert: reference-Alerts.html#torrent-finished-alert .. _listen_interfaces: reference-Settings.html#listen_interfaces .. _`add_torrent_alert`: reference-Alerts.html#add-torrent-alert .. _settings_pack: reference-Settings.html#settings_pack .. _alert_mask: reference-Settings.html#alert_mask .. _`category flags`: reference-Alerts.html#category_t .. _`apply_settings()`: reference-Core.html#apply_settings() .. _`session::abort()`: reference-Core.html#abort() .. _session_proxy: reference-Core.html#session_proxy .. _`post_torrent_updates()`: reference-Core.html#post_torrent_updates() .. _torrent_status: reference-Core.html#torrent_status .. _state_update_alert: reference-Alerts.html#state_update_alert .. _scalability: https://blog.libtorrent.org/2011/11/scalable-interfaces/ .. _`save_resume_data()`: reference-Core.html#save_resume_data() .. _save_resume_data_alert: reference-Alerts.html#save_resume_data_alert .. _save_resume_data_failed_alert: reference-Alerts.html#save_resume_data_failed_alert .. _bencoded: https://en.wikipedia.org/wiki/Bencode .. _entry: reference-Bencoding.html#entry .. _`bencode()`: reference-Bencoding.html#bencode() .. _torrent_info: reference-Core.html#torrent_info .. _`add_torrent_params::resume_data`: reference-Core.html#resume_data .. _`bdecode()`: reference-Bdecoding.html#bdecode() .. _bdecode_node: reference-Bdecoding.html#bdecode-node libtorrent-rasterbar-1.1.13/docs/udp_tracker_protocol.html000066400000000000000000000000001351156116000237730ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/docs/udp_tracker_protocol.rst000066400000000000000000000414151351156116000236560ustar00rootroot00000000000000Bittorrent udp-tracker protocol extension ========================================= :Author: Arvid Norberg, arvid@libtorrent.org .. contents:: Table of contents :depth: 2 :backlinks: none introduction ------------ A tracker with the protocol "udp://" in its URI is supposed to be contacted using this protocol. This protocol is supported by xbt-tracker_. .. _xbt-tracker: http://xbtt.sourceforge.net For additional information and descritptions of the terminology used in this document, see the `protocol specification`__ __ http://wiki.theory.org/index.php/BitTorrentSpecification All values are sent in network byte order (big endian). The sizes are specified with ANSI-C standard types. If no response to a request is received within 15 seconds, resend the request. If no reply has been received after 60 seconds, stop retrying. connecting ---------- Client sends packet: +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int64_t | connection_id | Must be initialized to 0x41727101980 | | | | in network byte order. This will | | | | identify the protocol. | +-------------+---------------------+----------------------------------------+ | int32_t | action | 0 for a connection request | +-------------+---------------------+----------------------------------------+ | int32_t | transaction_id | Randomized by client. | +-------------+---------------------+----------------------------------------+ Server replies with packet: +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int32_t | action | Describes the type of packet, in this | | | | case it should be 0, for connect. | | | | If 3 (for error) see errors_. | +-------------+---------------------+----------------------------------------+ | int32_t | transaction_id | Must match the transaction_id sent | | | | from the client. | +-------------+---------------------+----------------------------------------+ | int64_t | connection_id | A connection id, this is used when | | | | further information is exchanged with | | | | the tracker, to identify you. | | | | This connection id can be reused for | | | | multiple requests, but if it's cached | | | | for too long, it will not be valid | | | | anymore. | +-------------+---------------------+----------------------------------------+ announcing ---------- Client sends packet: +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int64_t | connection_id | The connection id acquired from | | | | establishing the connection. | +-------------+---------------------+----------------------------------------+ | int32_t | action | Action. in this case, 1 for announce. | | | | See actions_. | +-------------+---------------------+----------------------------------------+ | int32_t | transaction_id | Randomized by client. | +-------------+---------------------+----------------------------------------+ | int8_t[20] | info_hash | The info-hash of the torrent you want | | | | announce yourself in. | +-------------+---------------------+----------------------------------------+ | int8_t[20] | peer_id | Your peer id. | +-------------+---------------------+----------------------------------------+ | int64_t | downloaded | The number of byte you've downloaded | | | | in this session. | +-------------+---------------------+----------------------------------------+ | int64_t | left | The number of bytes you have left to | | | | download until you're finished. | +-------------+---------------------+----------------------------------------+ | int64_t | uploaded | The number of bytes you have uploaded | | | | in this session. | +-------------+---------------------+----------------------------------------+ | int32_t | event | The event, one of | | | | | | | | * none = 0 | | | | * completed = 1 | | | | * started = 2 | | | | * stopped = 3 | +-------------+---------------------+----------------------------------------+ | uint32_t | ip | Your ip address. Set to 0 if you want | | | | the tracker to use the ``sender`` of | | | | this udp packet. | +-------------+---------------------+----------------------------------------+ | uint32_t | key | A unique key that is randomized by the | | | | client. | +-------------+---------------------+----------------------------------------+ | int32_t | num_want | The maximum number of peers you want | | | | in the reply. Use -1 for default. | +-------------+---------------------+----------------------------------------+ | uint16_t | port | The port you're listening on. | +-------------+---------------------+----------------------------------------+ | uint16_t | extensions | See extensions_ | +-------------+---------------------+----------------------------------------+ Server replies with packet: +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int32_t | action | The action this is a reply to. Should | | | | in this case be 1 for announce. | | | | If 3 (for error) see errors_. | | | | See actions_. | +-------------+---------------------+----------------------------------------+ | int32_t | transaction_id | Must match the transaction_id sent | | | | in the announce request. | +-------------+---------------------+----------------------------------------+ | int32_t | interval | the number of seconds you should wait | | | | until reannouncing yourself. | +-------------+---------------------+----------------------------------------+ | int32_t | leechers | The number of peers in the swarm that | | | | has not finished downloading. | +-------------+---------------------+----------------------------------------+ | int32_t | seeders | The number of peers in the swarm that | | | | has finished downloading and are | | | | seeding. | +-------------+---------------------+----------------------------------------+ The rest of the server reply is a variable number of the following structure: +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int32_t | ip | The ip of a peer in the swarm. | +-------------+---------------------+----------------------------------------+ | uint16_t | port | The peer's listen port. | +-------------+---------------------+----------------------------------------+ scraping -------- Client sends packet: +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int64_t | connection_id | The connection id retreived from the | | | | establishing of the connection. | +-------------+---------------------+----------------------------------------+ | int32_t | action | The action, in this case, 2 for | | | | scrape. See actions_. | +-------------+---------------------+----------------------------------------+ | int32_t | transaction_id | Randomized by client. | +-------------+---------------------+----------------------------------------+ The following structure is repeated for each info-hash to scrape, but limited by the MTU. +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int8_t[20] | info_hash | The info hash that is to be scraped. | +-------------+---------------------+----------------------------------------+ Server replies with packet: +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int32_t | action | The action, should in this case be | | | | 2 for scrape. | | | | If 3 (for error) see errors_. | +-------------+---------------------+----------------------------------------+ | int32_t | transaction_id | Must match the sent transaction id. | +-------------+---------------------+----------------------------------------+ The rest of the packet contains the following structures once for each info-hash you asked in the scrape request. +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int32_t | complete | The current number of connected seeds. | +-------------+---------------------+----------------------------------------+ | int32_t | downloaded | The number of times this torrent has | | | | been downloaded. | +-------------+---------------------+----------------------------------------+ | int32_t | incomplete | The current number of connected | | | | leechers. | +-------------+---------------------+----------------------------------------+ errors ------ In case of a tracker error, server replies packet: +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int32_t | action | The action, in this case 3, for error. | | | | See actions_. | +-------------+---------------------+----------------------------------------+ | int32_t | transaction_id | Must match the transaction_id sent | | | | from the client. | +-------------+---------------------+----------------------------------------+ | int8_t[] | error_string | The rest of the packet is a string | | | | describing the error. | +-------------+---------------------+----------------------------------------+ actions ------- The action fields has the following encoding: * connect = 0 * announce = 1 * scrape = 2 * error = 3 (only in server replies) extensions ---------- The extensions field is a bitmask. The following bits are assigned: * 1 = authentication_. * 2 = `request string`_. If multiple bits are present in the extension field, the extension bodies are appended to the packet in the order of least significant bit first. For instance, if both bit 1 and 2 are set, the extension represented by bit 1 comes first, followed by the extension represented by bit 2. authentication ~~~~~~~~~~~~~~ The packet will have an authentication part appended to it. It has the following format: +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int8_t | username_length | The number of characters in the | | | | username. | +-------------+---------------------+----------------------------------------+ | int8_t[] | username | The username, the number of characters | | | | as specified in the previous field. | +-------------+---------------------+----------------------------------------+ | uint8_t[8] | passwd_hash | sha1(packet + sha1(password)) | | | | The packet in this case means the | | | | entire packet except these 8 bytes | | | | that are the password hash. These are | | | | the 8 first bytes (most significant) | | | | from the 20 bytes hash calculated. | +-------------+---------------------+----------------------------------------+ request string -------------- The request string extension is meant to allow torrent creators pass along cookies back to the tracker. This can be useful for authenticating that a torrent is allowed to be tracked by a tracker for instance. It could also be used to authenticate users by generating torrents with unique tokens in the tracker URL for each user. The extension body has the following format: +-------------+---------------------+----------------------------------------+ | size | name | description | +=============+=====================+========================================+ | int8_t | request length | The number of bytes in the request | | | | string. | +-------------+---------------------+----------------------------------------+ | int8_t[] | request string | The string that comes after the host- | | | | name and port in the udp tracker URL. | | | | Typically this starts with "/announce" | | | | The bittorrent client is not expected | | | | to append query string arguments for | | | | stats reporting, like "uploaded" and | | | | "downloaded" since this is already | | | | reported in the udp tracker protocol. | | | | However, the client is free to add | | | | arguments as extensions. | +-------------+---------------------+----------------------------------------+ credits ------- Protocol designed by Olaf van der Spek and extended by Arvid Norberg libtorrent-rasterbar-1.1.13/docs/utp.html000066400000000000000000000527401351156116000204010ustar00rootroot00000000000000 libtorrent manual

libtorrent manual

Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.1.13

uTP

uTP (uTorrent transport protocol) is a transport protocol which uses one-way delay measurements for its congestion controller. This article is about uTP in general and specifically about libtorrent's implementation of it.

rationale

One of the most common problems users are experiencing using bittorrent is that their internet "stops working". This can be caused by a number of things, for example:

  1. a home router that crashes or slows down when its NAT pin-hole table overflows, triggered by DHT or simply many TCP connections.
  2. a home router that crashes or slows down by UDP traffic (caused by the DHT)
  3. a home DSL or cable modem having its send buffer filled up by outgoing data, and the buffer fits seconds worth of bytes. This adds seconds of delay on interactive traffic. For a web site that needs 10 round trips to load this may mean 10s of seconds of delay to load compared to without bittorrent. Skype or other delay sensitive applications would be affected even more.

This document will cover (3).

Typically this is solved by asking the user to enter a number of bytes that the client is allowed to send per second (i.e. setting an upload rate limit). The common recommendation is to set this limit to 80% of the uplink's capacity. This is to leave some headroom for things like TCP ACKs as well as the user's interactive use of the connection such as browsing the web or checking email.

There are two major drawbacks with this technique:

  1. The user needs to actively make this setting (very few protocols require the user to provide this sort of information). This also means the user needs to figure out what its up-link capacity is. This is unfortunately a number that many ISPs are not advertizing (because it's often much lower than the download capacity) which might make it hard to find.
  2. The 20% headroom is wasted most of the time. Whenever the user is not using the internet connection for anything, those extra 20% could have been used by bittorrent to upload, but they're already allocated for interactive traffic. On top of that, 20% of the up-link is often not enough to give a good and responsive browsing experience.

The ideal bandwidth allocation would be to use 100% for bittorrent when there is no interactive cross traffic, and 100% for interactive traffic whenever there is any. This would not waste any bandwidth while the user is idling, and it would make for a much better experience when the user is using the internet connection for other things.

This is what uTP does.

TCP

The reason TCP will fill the send buffer, and cause the delay on all traffic, is because its congestion control is only based on packet loss (and timeout).

Since the modem is buffering, packets won't get dropped until the entire queue is full, and no more packets will fit. The packets will be dropped, TCP will detect this within an RTT or so. When TCP notices a packet loss, it will slow down its send rate and the queue will start to drain again. However, TCP will immediately start to ramp up its send rate again until the buffer is full and it detects packet loss again.

TCP is designed to fully utilize the link capacity, without causing congestion. Whenever it sense congestion (through packet loss) it backs off. TCP is not designed to keep delays low. When you get the first packet loss (assuming the kind of queue described above, tail-queue) it is already too late. Your queue is full and you have the maximum amount of delay your modem can provide.

TCP controls its send rate by limiting the number of bytes in-flight at any given time. This limit is called congestion window (cwnd for short). During steady state, the congestion window is constantly increasing linearly. Each packet that is successfully transferred will increase cwnd.

            cwnd
send_rate = ----
            RTT

Send rate is proportional to cwnd divided by RTT. A smaller cwnd will cause the send rate to be lower and a larger cwnd will cause the send rate to be higher.

Using a congestion window instead of controlling the rate directly is simple because it also introduces an upper bound for memory usage for packets that haven't been ACKed yet and needs to be kept around.

The behavior of TCP, where it bumps up against the ceiling, backs off and then starts increasing again until it hits the ceiling again, forms a saw tooth shape. If the modem wouldn't have any send buffer at all, a single TCP stream would not be able to fully utilize the link because of this behavior, since it would only fully utilize the link right before the packet loss and the back-off.

LEDBAT congestion controller

The congestion controller in uTP is called LEDBAT, which also is an IETF working group attempting to standardize it. The congestion controller, on top of reacting to packet loss the same way TCP does, also reacts to changes in delays.

For any uTP (or LEDBAT) implementation, there is a target delay. This is the amount of delay that is acceptable, and is in fact targeted for the connection. The target delay is defined to 25 ms in LEDBAT, uTorrent uses 100 ms and libtorrent uses 75 ms. Whenever a delay measurement is lower than the target, cwnd is increased proportional to (target_delay - delay). Whenever the measurement is higher than the target, cwnd is decreased proportional to (delay - target_delay).

It can simply be expressed as:

cwnd += gain * (target_delay - delay)
cwnd_thumb.png

Similarly to TCP, this is scaled so that the increase is evened out over one RTT.

The linear controller will adjust the cwnd more for delays that are far off the target, and less for delays that are close to the target. This makes it converge at the target delay. Although, due to noise there is almost always some amount of oscillation. This oscillation is typically smaller than the saw tooth TCP forms.

The figure to the right shows how (TCP) cross traffic causese uTP to essentially entirely stop sending anything. Its delay measurements are mostly well above the target during this time. The cross traffic is only a single TCP stream in this test.

As soon as the cross traffic ceases, uTP will pick up its original send rate within a second.

Since uTP constantly measures the delay, with every single packet, the reaction time to cross traffic causing delays is a single RTT (typically a fraction of a second).

one way delays

uTP measures the delay imposed on packets being sent to the other end of the connection. This measurement only includes buffering delay along the link, not propagation delay (the speed of light times distance) nor the routing delay (the time routers spend figuring out where to forward the packet). It does this by always comparing all measurements to a baseline measurement, to cancel out any fixed delay. By focusing on the variable delay along a link, it will specifically detect points where there might be congestion, since those points will have buffers.

delays_thumb.png

Delay on the return link is explicitly not included in the delay measurement. This is because in a peer-to-peer application, the other end is likely to also be connected via a modem, with the same send buffer restrictions as we assume for the sending side. The other end having its send queue full is not an indication of congestion on the path going the other way.

In order to measure one way delays for packets, we cannot rely on clocks being synchronized, especially not at the microsecond level. Instead, the actual time it takes for a packet to arrive at the destination is not measured, only the changes in the transit time is measured.

Each packet that is sent includes a time stamp of the current time, in microseconds, of the sending machine. The receiving machine calculates the difference between its own timestamp and the one in the packet and sends this back in the ACK. This difference, since it is in microseconds, will essentially be a random 32 bit number. However, the difference will stay somewhat similar over time. Any changes in this difference indicates that packets are either going through faster or slower.

In order to measure the one-way buffering delay, a base delay is established. The base delay is the lowest ever seen value of the time stamp difference. Each delay sample we receive back, is compared against the base delay and the delay is the difference.

This is the delay that's fed into the congestion controller.

A histogram of typical delay measurements is shown to the right. This is from a transfer between a cable modem connection and a DSL connection.

The details of the delay measurements are slightly more complicated since the values needs to be able to wrap (cross the 2^32 boundry and start over at 0).

Path MTU discovery

MTU is short for Maximum Transfer Unit and describes the largest packet size that can be sent over a link. Any datagrams which size exceeds this limit will either be fragmented or dropped. A fragmented datagram means that the payload is split up in multiple packets, each with its own individual packet header.

There are several reasons to avoid sending datagrams that get fragmented:

  1. A fragmented datagram is more likely to be lost. If any fragment is lost, the whole datagram is dropped.
  2. Bandwidth is likely to be wasted. If the datagram size is not divisible by the MTU the last packet will not contain as much payload as it could, and the payload over protocol header ratio decreases.
  3. It's expensive to fragment datagrams. Few routers are optimized to handle large numbers of fragmented packets. Datagrams that have to fragment are likely to be delayed significantly, and contribute to more CPU being used on routers. Typically fragmentation (and other advanced IP features) are implemented in software (slow) and not hardware (fast).

The path MTU is the lowest MTU of any link along a path from two endpoints on the internet. The MTU bottleneck isn't necessarily at one of the endpoints, but can be anywhere in between.

The most common MTU is 1500 bytes, which is the largest packet size for ethernet networks. Many home DSL connections, however, tunnel IP through PPPoE (Point to Point Protocol over Ethernet. Yes, that is the old dial-up modem protocol). This protocol uses up 8 bytes per packet for its own header.

If the user happens to be on an internet connection over a VPN, it will add another layer, with its own packet headers.

In short; if you would pick the largest possible packet size on an ethernet network, 1472, and stick with it, you would be quite likely to generate fragments for a lot of connections. The fragments that will be created will be very small and especially inflate the overhead waste.

The other approach of picking a very conservative packet size, that would be very unlikely to get fragmented has the following drawbacks:

  1. People on good, normal, networks will be penalized with a small packet size. Both in terms of router load but also bandwidth waste.
  2. Software routers are typically not limited by the number of bytes they can route, but the number of packets. Small packets means more of them, and more load on software routers.

The solution to the problem of finding the optimal packet size, is to dynamically adjust the packet size and search for the largest size that can make it through without being fragmented along the path.

To help do this, you can set the DF bit (Don't Fragment) in your Datagrams. This asks routers that otherwise would fragment packets to instead drop them, and send back an ICMP message reporting the MTU of the link the packet couldn't fit. With this message, it's very simple to discover the path MTU. You simply mark your packets not to be fragmented, and change your packet size whenever you receive the ICMP packet-too-big message.

Unfortunately it's not quite that simple. There are a significant number of firewalls in the wild blocking all ICMP messages. This means we can't rely on them, we also have to guess that a packet was dropped because of its size. This is done by only marking certain packets with DF, and if all other packets go through, except for the MTU probes, we know that we need to lower our packet sizes.

If we set up bounds for the path MTU (say the minimum internet MTU, 576 and ethernet's 1500), we can do a binary search for the MTU. This would let us find it in just a few round-trips.

On top of this, libtorrent has an optimization where it figures out which interface a uTP connection will be sent over, and initialize the MTU ceiling to that interface's MTU. This means that a VPN tunnel would advertize its MTU as lower, and the uTP connection would immediately know to send smaller packets, no search required. It also has the side-effect of being able to use much larger packet sizes for non-ethernet interfaces or ethernet links with jumbo frames.

clock drift

our_delay_base_thumb.png

Clock drift is clocks progressing at different rates. It's different from clock skew which means clocks set to different values (but which may progress at the same rate).

Any clock drift between the two machines involved in a uTP transfer will result in systematically inflated or deflated delay measurements.

This can be solved by letting the base delay be the lowest seen sample in the last n minutes. This is a trade-off between seeing a single packet go straight through the queue, with no delay, and the amount of clock drift one can assume on normal computers.

It turns out that it's fairly safe to assume that one of your packets will in fact go straight through without any significant delay, once every 20 minutes or so. However, the clock drift between normal computers can be as much as 17 ms in 10 minutes. 17 ms is quite significant, especially if your target delay is 25 ms (as in the LEDBAT spec).

Clocks progresses at different rates depending on temperature. This means computers running hot are likely to have a clock drift compared to computers running cool.

So, by updating the delay base periodically based on the lowest seen sample, you'll either end up changing it upwards (artificaially making the delay samples appear small) without the congestion or delay actually having changed, or you'll end up with a significant clock drift and have artificially low samples because of that.

The solution to this problem is based on the fact that the clock drift is only a problem for one of the sides of the connection. Only when your delay measurements keep increasing is it a problem. If your delay measurements keep decreasing, the samples will simply push down the delay base along with it. With this in mind, we can simply keep track of the other end's delay measurements as well, applying the same logic to it. Whenever the other end's base delay is adjusted downwards, we adjust our base delay upwards by the same amount.

This will accurately keep the base delay updated with the clock drift and improve the delay measurements. The figure on the right shows the absolute timestamp differences along with the base delay. The slope of the measurements is caused by clock drift.

For more information on the clock drift compensation, see the slides from BitTorrent's presentation at IPTPS10.

features

libtorrent's uTP implementation includes the following features:

  • Path MTU discovery, including jumbo frames and detecting restricted MTU tunnels. Binary search packet sizes to find the largest non-fragmented.
  • Selective ACK. The ability to acknowledge individual packets in the event of packet loss
  • Fast resend. The first time a packet is lost, it's resent immediately. Triggered by duplicate ACKs.
  • Nagle's algorithm. Minimize protocol overhead by attempting to lump full packets of payload together before sending a packet.
  • Delayed ACKs to minimize protocol overhead.
  • Microsecond resolution timestamps.
  • Advertised receive window, to support download rate limiting.
  • Correct handling of wrapping sequence numbers.
  • Easy configuration of target-delay, gain-factor, timeouts, delayed-ack and socket buffers.
libtorrent-rasterbar-1.1.13/docs/utp.rst000066400000000000000000000404771351156116000202510ustar00rootroot00000000000000================= libtorrent manual ================= :Author: Arvid Norberg, arvid@libtorrent.org :Version: 1.1.13 .. contents:: Table of contents :depth: 2 :backlinks: none uTP === uTP (uTorrent transport protocol) is a transport protocol which uses one-way delay measurements for its congestion controller. This article is about uTP in general and specifically about libtorrent's implementation of it. rationale --------- One of the most common problems users are experiencing using bittorrent is that their internet "stops working". This can be caused by a number of things, for example: 1. a home router that crashes or slows down when its NAT pin-hole table overflows, triggered by DHT or simply many TCP connections. 2. a home router that crashes or slows down by UDP traffic (caused by the DHT) 3. a home DSL or cable modem having its send buffer filled up by outgoing data, and the buffer fits seconds worth of bytes. This adds seconds of delay on interactive traffic. For a web site that needs 10 round trips to load this may mean 10s of seconds of delay to load compared to without bittorrent. Skype or other delay sensitive applications would be affected even more. This document will cover (3). Typically this is solved by asking the user to enter a number of bytes that the client is allowed to send per second (i.e. setting an upload rate limit). The common recommendation is to set this limit to 80% of the uplink's capacity. This is to leave some headroom for things like TCP ACKs as well as the user's interactive use of the connection such as browsing the web or checking email. There are two major drawbacks with this technique: 1. The user needs to actively make this setting (very few protocols require the user to provide this sort of information). This also means the user needs to figure out what its up-link capacity is. This is unfortunately a number that many ISPs are not advertizing (because it's often much lower than the download capacity) which might make it hard to find. 2. The 20% headroom is wasted most of the time. Whenever the user is not using the internet connection for anything, those extra 20% could have been used by bittorrent to upload, but they're already allocated for interactive traffic. On top of that, 20% of the up-link is often not enough to give a good and responsive browsing experience. The ideal bandwidth allocation would be to use 100% for bittorrent when there is no interactive cross traffic, and 100% for interactive traffic whenever there is any. This would not waste any bandwidth while the user is idling, and it would make for a much better experience when the user is using the internet connection for other things. This is what uTP does. TCP --- The reason TCP will fill the send buffer, and cause the delay on all traffic, is because its congestion control is *only* based on packet loss (and timeout). Since the modem is buffering, packets won't get dropped until the entire queue is full, and no more packets will fit. The packets will be dropped, TCP will detect this within an RTT or so. When TCP notices a packet loss, it will slow down its send rate and the queue will start to drain again. However, TCP will immediately start to ramp up its send rate again until the buffer is full and it detects packet loss again. TCP is designed to fully utilize the link capacity, without causing congestion. Whenever it sense congestion (through packet loss) it backs off. TCP is not designed to keep delays low. When you get the first packet loss (assuming the kind of queue described above, tail-queue) it is already too late. Your queue is full and you have the maximum amount of delay your modem can provide. TCP controls its send rate by limiting the number of bytes in-flight at any given time. This limit is called congestion window (*cwnd* for short). During steady state, the congestion window is constantly increasing linearly. Each packet that is successfully transferred will increase cwnd. :: cwnd send_rate = ---- RTT Send rate is proportional to cwnd divided by RTT. A smaller cwnd will cause the send rate to be lower and a larger cwnd will cause the send rate to be higher. Using a congestion window instead of controlling the rate directly is simple because it also introduces an upper bound for memory usage for packets that haven't been ACKed yet and needs to be kept around. The behavior of TCP, where it bumps up against the ceiling, backs off and then starts increasing again until it hits the ceiling again, forms a saw tooth shape. If the modem wouldn't have any send buffer at all, a single TCP stream would not be able to fully utilize the link because of this behavior, since it would only fully utilize the link right before the packet loss and the back-off. LEDBAT congestion controller ---------------------------- The congestion controller in uTP is called LEDBAT_, which also is an IETF working group attempting to standardize it. The congestion controller, on top of reacting to packet loss the same way TCP does, also reacts to changes in delays. For any uTP (or LEDBAT_) implementation, there is a target delay. This is the amount of delay that is acceptable, and is in fact targeted for the connection. The target delay is defined to 25 ms in LEDBAT_, uTorrent uses 100 ms and libtorrent uses 75 ms. Whenever a delay measurement is lower than the target, cwnd is increased proportional to (target_delay - delay). Whenever the measurement is higher than the target, cwnd is decreased proportional to (delay - target_delay). It can simply be expressed as:: cwnd += gain * (target_delay - delay) .. image:: cwnd_thumb.png :target: cwnd.png :align: right Similarly to TCP, this is scaled so that the increase is evened out over one RTT. The linear controller will adjust the cwnd more for delays that are far off the target, and less for delays that are close to the target. This makes it converge at the target delay. Although, due to noise there is almost always some amount of oscillation. This oscillation is typically smaller than the saw tooth TCP forms. The figure to the right shows how (TCP) cross traffic causese uTP to essentially entirely stop sending anything. Its delay measurements are mostly well above the target during this time. The cross traffic is only a single TCP stream in this test. As soon as the cross traffic ceases, uTP will pick up its original send rate within a second. Since uTP constantly measures the delay, with every single packet, the reaction time to cross traffic causing delays is a single RTT (typically a fraction of a second). one way delays -------------- uTP measures the delay imposed on packets being sent to the other end of the connection. This measurement only includes buffering delay along the link, not propagation delay (the speed of light times distance) nor the routing delay (the time routers spend figuring out where to forward the packet). It does this by always comparing all measurements to a baseline measurement, to cancel out any fixed delay. By focusing on the variable delay along a link, it will specifically detect points where there might be congestion, since those points will have buffers. .. image:: delays_thumb.png :target: delays.png :align: right Delay on the return link is explicitly not included in the delay measurement. This is because in a peer-to-peer application, the other end is likely to also be connected via a modem, with the same send buffer restrictions as we assume for the sending side. The other end having its send queue full is not an indication of congestion on the path going the other way. In order to measure one way delays for packets, we cannot rely on clocks being synchronized, especially not at the microsecond level. Instead, the actual time it takes for a packet to arrive at the destination is not measured, only the changes in the transit time is measured. Each packet that is sent includes a time stamp of the current time, in microseconds, of the sending machine. The receiving machine calculates the difference between its own timestamp and the one in the packet and sends this back in the ACK. This difference, since it is in microseconds, will essentially be a random 32 bit number. However, the difference will stay somewhat similar over time. Any changes in this difference indicates that packets are either going through faster or slower. In order to measure the one-way buffering delay, a base delay is established. The base delay is the lowest ever seen value of the time stamp difference. Each delay sample we receive back, is compared against the base delay and the delay is the difference. This is the delay that's fed into the congestion controller. A histogram of typical delay measurements is shown to the right. This is from a transfer between a cable modem connection and a DSL connection. The details of the delay measurements are slightly more complicated since the values needs to be able to wrap (cross the 2^32 boundry and start over at 0). Path MTU discovery ------------------ MTU is short for *Maximum Transfer Unit* and describes the largest packet size that can be sent over a link. Any datagrams which size exceeds this limit will either be *fragmented* or dropped. A fragmented datagram means that the payload is split up in multiple packets, each with its own individual packet header. There are several reasons to avoid sending datagrams that get fragmented: 1. A fragmented datagram is more likely to be lost. If any fragment is lost, the whole datagram is dropped. 2. Bandwidth is likely to be wasted. If the datagram size is not divisible by the MTU the last packet will not contain as much payload as it could, and the payload over protocol header ratio decreases. 3. It's expensive to fragment datagrams. Few routers are optimized to handle large numbers of fragmented packets. Datagrams that have to fragment are likely to be delayed significantly, and contribute to more CPU being used on routers. Typically fragmentation (and other advanced IP features) are implemented in software (slow) and not hardware (fast). The path MTU is the lowest MTU of any link along a path from two endpoints on the internet. The MTU bottleneck isn't necessarily at one of the endpoints, but can be anywhere in between. The most common MTU is 1500 bytes, which is the largest packet size for ethernet networks. Many home DSL connections, however, tunnel IP through PPPoE (Point to Point Protocol over Ethernet. Yes, that is the old dial-up modem protocol). This protocol uses up 8 bytes per packet for its own header. If the user happens to be on an internet connection over a VPN, it will add another layer, with its own packet headers. In short; if you would pick the largest possible packet size on an ethernet network, 1472, and stick with it, you would be quite likely to generate fragments for a lot of connections. The fragments that will be created will be very small and especially inflate the overhead waste. The other approach of picking a very conservative packet size, that would be very unlikely to get fragmented has the following drawbacks: 1. People on good, normal, networks will be penalized with a small packet size. Both in terms of router load but also bandwidth waste. 2. Software routers are typically not limited by the number of bytes they can route, but the number of packets. Small packets means more of them, and more load on software routers. The solution to the problem of finding the optimal packet size, is to dynamically adjust the packet size and search for the largest size that can make it through without being fragmented along the path. To help do this, you can set the DF bit (Don't Fragment) in your Datagrams. This asks routers that otherwise would fragment packets to instead drop them, and send back an ICMP message reporting the MTU of the link the packet couldn't fit. With this message, it's very simple to discover the path MTU. You simply mark your packets not to be fragmented, and change your packet size whenever you receive the ICMP packet-too-big message. Unfortunately it's not quite that simple. There are a significant number of firewalls in the wild blocking all ICMP messages. This means we can't rely on them, we also have to guess that a packet was dropped because of its size. This is done by only marking certain packets with DF, and if all other packets go through, except for the MTU probes, we know that we need to lower our packet sizes. If we set up bounds for the path MTU (say the minimum internet MTU, 576 and ethernet's 1500), we can do a binary search for the MTU. This would let us find it in just a few round-trips. On top of this, libtorrent has an optimization where it figures out which interface a uTP connection will be sent over, and initialize the MTU ceiling to that interface's MTU. This means that a VPN tunnel would advertize its MTU as lower, and the uTP connection would immediately know to send smaller packets, no search required. It also has the side-effect of being able to use much larger packet sizes for non-ethernet interfaces or ethernet links with jumbo frames. clock drift ----------- .. image:: our_delay_base_thumb.png :target: our_delay_base.png :align: right Clock drift is clocks progressing at different rates. It's different from clock skew which means clocks set to different values (but which may progress at the same rate). Any clock drift between the two machines involved in a uTP transfer will result in systematically inflated or deflated delay measurements. This can be solved by letting the base delay be the lowest seen sample in the last *n* minutes. This is a trade-off between seeing a single packet go straight through the queue, with no delay, and the amount of clock drift one can assume on normal computers. It turns out that it's fairly safe to assume that one of your packets will in fact go straight through without any significant delay, once every 20 minutes or so. However, the clock drift between normal computers can be as much as 17 ms in 10 minutes. 17 ms is quite significant, especially if your target delay is 25 ms (as in the LEDBAT_ spec). Clocks progresses at different rates depending on temperature. This means computers running hot are likely to have a clock drift compared to computers running cool. So, by updating the delay base periodically based on the lowest seen sample, you'll either end up changing it upwards (artificaially making the delay samples appear small) without the congestion or delay actually having changed, or you'll end up with a significant clock drift and have artificially low samples because of that. The solution to this problem is based on the fact that the clock drift is only a problem for one of the sides of the connection. Only when your delay measurements keep increasing is it a problem. If your delay measurements keep decreasing, the samples will simply push down the delay base along with it. With this in mind, we can simply keep track of the other end's delay measurements as well, applying the same logic to it. Whenever the other end's base delay is adjusted downwards, we adjust our base delay upwards by the same amount. This will accurately keep the base delay updated with the clock drift and improve the delay measurements. The figure on the right shows the absolute timestamp differences along with the base delay. The slope of the measurements is caused by clock drift. For more information on the clock drift compensation, see the slides from BitTorrent's presentation at IPTPS10_. .. _IPTPS10: http://www.usenix.org/event/iptps10/tech/slides/cohen.pdf .. _LEDBAT: https://datatracker.ietf.org/doc/draft-ietf-ledbat-congestion/ features -------- libtorrent's uTP implementation includes the following features: * Path MTU discovery, including jumbo frames and detecting restricted MTU tunnels. Binary search packet sizes to find the largest non-fragmented. * Selective ACK. The ability to acknowledge individual packets in the event of packet loss * Fast resend. The first time a packet is lost, it's resent immediately. Triggered by duplicate ACKs. * Nagle's algorithm. Minimize protocol overhead by attempting to lump full packets of payload together before sending a packet. * Delayed ACKs to minimize protocol overhead. * Microsecond resolution timestamps. * Advertised receive window, to support download rate limiting. * Correct handling of wrapping sequence numbers. * Easy configuration of target-delay, gain-factor, timeouts, delayed-ack and socket buffers. libtorrent-rasterbar-1.1.13/docs/utp_stack.diagram000066400000000000000000000003631351156116000222200ustar00rootroot00000000000000+------------------------+ | "BitTorrent protocol" | +------------------------+ | "SSL" | +------------+-----------+ | "TCP" | "uTP" | | +-----------+ | | "UDP" | +------------+-----------+ libtorrent-rasterbar-1.1.13/docs/utp_stack.png000066400000000000000000000037411351156116000214030ustar00rootroot00000000000000‰PNG  IHDR`–¤€Ü¨IDATxśíÝ[H“ođßłfmłŇ;5L]nË©4b^DÁR; ]¸NBŃş*şđ¦î „¤IÝ4Ę +ś¬¬L!DŇjóÝZN-‡čţČţŰ|`Ś1Ć”Jĺ•+W¦ŰOś81ÓOY­VÄjµ.x«ĂˇC‡cçÎť‹÷BÖĽ@›łs8‹%¸\.ŤFÓÓÓ±›(ŠV«5¤±®®®®®nţs-XÄŮ—ŰČ/^Ľ¨®®ŽÉP,Şçs ÎŽ&&&’““‰(??ź16˝'üůó'==˝ˇˇ1vđŕÁ™F¸~ýşR©áęŐ«ĽĄ¸¸řéÓ§jµZ.—‹˘±Ą®®N„m۶ݹs‡\.—Ůl¶X, …‚/`>ł»\®‚‚‚ýű÷oܸńôéÓ3ͲÂ#‡?Š[·nĄ¦¦&&&–””ŚŚŚDěËĹ‚3äp8¦ąxńâää$oűömEEĹt·×Ż_WUU…ülđžđůógťNçt:EQÔét@ ¦¦fßľ}===ăăă|ä–îî<ŹÇăőz úűűEQ”Édmmmż˙NKKóx<3ÍLEą\ŢŮŮéőzŤFă§OźÂ犸‘Ăűttt †_ż~Í҇·cOX"Q=źµ'𳣡ˇˇ±±±Ű·o/lŢŢŢÂÂÂíŰ·§ĄĄ™Íćľľ>"JHH¨®®ÎÎÎްaD" oéííýřńcjjŞR©t8ťťťD´k×.“ɤT*333˙ţý;ĎhµÚÜÜ\AöîÝët:#ÎľÂ9oQ©Tł?RX&bpv” •J˝^oÄ{cn·Űď÷Fě••ŐÚÚęńxDQ|÷î]NNo߼ysHĎŕ–ěěl“Éäőzy KJJhÝşuŃÎND˝˝˝ßľ}óů|íííjµ:|®+ 9ĽĎÎť;[[[ć|¤°,* ÍÍÍü˝Łžžž .4551Ć ?~Ě»ví †?~lÚ´i¦3őÝ»w?~\Ż×ŤĆĘĘJ˝^?ź©u:Ý‘#Gôz}bbbyyąßďŹŘmÎىH­Vź:uJ­VŤĆy®0däđ>Fٱ˘˘"//OˇP …÷illdŚß»wŹ1vóćÍůl·Űă˝˝¨žĎřd €{¬bآ†$! ’@^˝‰RfX›PÇ «ę˘†$! ’0‡©©©šššääääääË—/Gl!˘śś·Űו¢„ľb†ííí]]]}}}~żż¬¬ěë×ŻŁŁŁ!-ąąąń^&,ö„9‚000ĐŮŮąuëV»ÝÎkzBZâ˝F$aŮŮŮ őőőˇ¶¶Öď÷‡·Ä{ŤHÂÜĚfóş»»ÝnwCCCÄXé„9Ü˝{÷Ě™3###˙ţý“H$“““á-ń^#Ä’0‡ĘĘʱ±±ŚŚŚŚŚ ":yňdx žÎżÎ »ÄJ„JXµP©5$€Iŕ"$€CP˝ Ŕˇz3ţđ1ÎAő&@Ô"$€C“„ד}ôčQfffRRŇĄK—(čJµIII(jY áżö…XđÚ¸ŕkş\.A‡×ë---mkk›ľRíŕŕ`iiéýű÷Ł-XŔaü‹Ŕaç˘:°±<;zóćMQQQ~~ľ 6›Íd2Mß555ĺ÷űe2Y §[›Ün÷ô5›ÍýýýłtĆaźżX&app0%%%¤‘_‹M«ŐîŘ±ŁĽĽ<†ÓÁ,pŘŁËoyIIIikk i´X,/_ľŚá,0m–ÂQöhĹrO8pŕ@KK‹Ýn9{öěłgĎb88prąÜétůňĄ««+ŢËY=b™Aęë뫪ŞT*•D"9zôh nË–-fł9--íüůó{öě ŕĎ4buĚń‡ĂľDPÇ 5$€Iŕ"$€Ăő–ÍĆ’°,ŕ]ÔĄ€ęM€¨! DH‡$! +ÎóçĎyˇ,Y­Ö¦¦&^«©P(ňóó[ZZEł ‚$¬‹Ĺçóݸqٞ˛REŢľ˙>::úäÉ“x/p@V ™LV\\|ěŘ1›Í6ÝęÍůCV•J5<Y[a¤RéÄÄ˙÷řřřúőëďíďď/,,$ToF{ ŁŃhěv»Çăůůóçű÷ﳲ˛xűÄÄÄ«WŻl6›Ĺb‰ď W(ě +Ś^Ż/++ÓjµR©´¶¶VŁŃř|ľććfą\n0>|(Âě_ýˇz3ţpŘ—Ş7˘†$! ’@„$p¸ö沀Ăwx˙€gG’@„$pH’Ŕ! DH‡$! ’@„$pH’Ŕý4AW?Ý^IEND®B`‚libtorrent-rasterbar-1.1.13/docs/write_disk_buffers.diagram000066400000000000000000000021761351156116000241070ustar00rootroot00000000000000 "decrypt in place" "move buffer ref." +------------------+ "(no copy)" +--------------+ "(no copy)" +--------------+ | "receive buffer" +--=------------>| "plain text" +--=------------->| "disk cache" | +------------------+ | "buffer" | +------+-------+ ^ +--------------+ | | "read() on socket" "write() to file" | | "(copy)" "(copy)" | ---=----|---------------------------------=---------------------------------|--=---- | "kernel space" | | v +-------+---------+ +---------------------+ | "socket kernel" | | "kernel page cache" | | "buffer" | | | +-----------------+ +---------------------+ libtorrent-rasterbar-1.1.13/docs/write_disk_buffers.png000066400000000000000000000060671351156116000232720ustar00rootroot00000000000000‰PNG  IHDR9™„c= ţIDATxśíÝݲŁ*·PשýţŻśsa}Íź¨Q§qŚ‹®,Ű I¦€ň÷ů|&Búż»3@“X .±@\b5€¸Äjq‰Őâ«Ä%VK¬×wg€÷÷÷7ż8{…Śů@«GÜm5…™e?`¶©¤—;_VQ\|,ř ±çú|>ips»ăµóśB¨·/éĺőŇń+¨qW ľĺĎ)ËyĘ.¨ŐëéĄ&ÍęýVKPŤśŞgu–™ě@«o™ş­ËH?«y"¨v §'gůşUxű)WÓY-×Őc•o/óPݧšNůń!8óŐ8K˙ęy~]nÉ^—•鲥|Wë-Ő˙ZŢŢé'««˙I[ź«ÚćÁ˝Ş'gçşeäî§SŐrËjµĐ?îČ{;$Văj­¨(«:T“öŁ˝2Z…{eARk‡ÖĺÖ5:—OýĘ!ŰGˇăŻĆŐVŻÚËýwL[ľ2ÔkŤď”™yP Ą+OŕoKˇăŻĆ‰úsS:SŇ-ű”» ff|ĘÚřf5ľt)ĚŁ“éżSŁědăý¸5luŔ´?_­Ügp¶\ucçXŠ$‘‰Őx€ęDţkN]ő8÷2_ŤĐţţţZw\^0 ĺĘc@•~5€¸ô«qş8ťRqr·‹Sâäb«q®Öc–nQ˝U^HÁ„ńĚN”Öż­›.GžÇŃ_ s·ië~Ě8­\OÁ„gŃŻĆEŞëŚ< }u €ru˛Ć×@•‚ ńýÓŻ¦šwUŻŞő+© GÁD±}śÁⓏ*uôÝXlzÔŮÖŐ(ůŢDÁdć[}ńbk ”­Ö#Ď /÷é/V¸ş|ˇşŚ—S0áYÜ[ŔąŇulŞËbŽTĐÓ\V7šĽ 3Dż§;».W(W;¸&'đ &/döÇjŮË×énť-Ů%x™Z¶Űŕez5ť¬OľşĄ•C·/ń3ľ{}2Ř‹6Rđ?ĹY[‰¬ةָ0ăąľ~öVKǬlŻłC–ÓńH š¬6w±ľZyËĎęŁ[űŻNhy0c–«ţlÖĺ ĺ–ţ'rĆđćłý/ń•4§-ĎM­üżźČÚ:Ę4P`«Ę ňÝb[¶heéHCĄ­ÚÖH`9Đę>ďô…1Đ%čIżßę>‹Őx«:őĘ_«ó)ÔőŠÍď9űR$-Ôg(Çq^xqĄ`ľÍ5Ŷz ‘¦×H­âżšŽłzöýůj«c­}öĄ|’WUń[ůr~É51M9‘ŕ[iÎZWeűÇp?ůˇ¨şě4nukµݞ}¦Ć4¤rź~řçëůöÓť—ם±íÎ(őÔťŘe¦?'¦“Hç#´ŽŢɡófÓIBd«?t§ÔŹ++ôÎŃ[ÔŞé”»ýä ¬`ľÄ·Ší´˝śv W+ĺV…Đ*žźĆ<őÖžnC5»;Văťś$?`ä˘|k¬ć¬¸—‚ůóľ[l‰`ü÷ŠňĚŕ2gÔć 8•ňőfb5ŕMŔ©ÄjŔ!ăc ťŮ«>€±p…ÖSxDi}b5ŕ Ş7•;[¦Ćíc«7ťü¶ýë,–čŞNĄĎVĚž)˝l)¶ň5Ŕ ĺýjž ÜČ’!p„Fü'ý«© {©…`·ź)>ž”1 |A:R™M>K—\^—[–wU÷ąđŁÄâŢŕţµ#ďra 0éW썱šńŕ)Ţ«<ĹëbµlÎ2@dŻ‹ŐäĄ÷Î«Ö¸Ë :t?DđŇX čs%Ä»Ć@Óľ´tAh€Ţ«<Ë»b5OEžĺ]±Ŕł¸·ŕDË|¸´/ť3ç^ÔjV;{É6đYĺ3^m­uw×reEš&•ÎV…˛‰~µ•Ą1«&âÜß â‚ű|>űâ§kjÝ%,Ë—ŢЦ¦eýj+–â—^0µ.žŞ[˛¤Ş‡čŕň¸é‹ŐwMĹ%]'Ďéöę»–?ő®_1W€ËżËöVPŐ©‹Fęáô ­˙Iyő]˝hăcĽ™~µiAm˝îo9"Ką|Ńy×”\ĆŤäązÄęë4e€/ŞÖZe…SÖE­:mGv¤\ľk¤’üVžůybµQKń›˙ět\Ý.Íäj®:•ŕ’Čwłj-ÓśŐşăI=ËóĚĹŚî”Ý.0Eš|6ŐşĐZĘ.ŔrzÇŕ¶:^sž7g˙š*]-ĘŞ(÷!^iÓÝ—ŐZ`uREą1ľĎLuĆXĘZëĐ­OQMąü«ÝvH§¬McŁý µÓżőŐÖZ·“ňę|µe·‘8o|ZŰŰÄyHBoü:î= b>łŕW©uÇĎ”1_íj#Ă‹|‹Z—§«Ä%VuĆ$Ó8÷"ÜK -bµ!'Ťť‡şuŕ.ęXčđĚŽuťŞ·Fvnú4Ö<0…x-u,ôéWŰlu €ôÖSŁšPqT©c!Ł_m?uŔyÔ±0ÓŻ¶ßČ N 7Ťl˘Ž…™Xm]uąĚl]álˬµĄź>Ŕ«¨cˇĎčtý“©Vň«uÁęF3^&u,t˝ń<>»ôvł{á· đ]ęŘź'ČÎčWű>w$śGËŰŻ—X .±@\˙˝d­´Îl†—|ÖKšé}s+˙3%Ó7ai¦ŤÄ%VK¬—X .ë@Dĺ˘ÔßMyfĆ.@|úŐŕ]>źŹ ŕAô«Ahi[¶@őň_YWYÖ'·oYëÎŤ:)Źl`ýjW¨u^/¦Ű«ˇŢŕq—d—0«źňȶ«AhYwT5âYí˛Ú'Ą˝b­÷v"°lU”p„1Pmé'›íIÜń®Őΰęlö_»ŹŔB¬qÍ‘PĄŁ“Ëë,žkĄ0ˇUënÓVVîźĺ!Ímő裟€˙iVń?¬Ó°Uç=C Ł™ÎŻ¬H;ĎîÍ Ŕ V¸Ŕ¸‘~5€¸Äj@ĺÍOLb5€ČÄj@Ës…Ü š«Ä%VK¬—X bYˇîîŚ"VK¬˘S-#VK¬—X ®ž lí-fć ďˇíă.­íűŢĆSmoŁíăză­­1P€¸Äjq‰Őâ«Ä•ß[pŞeÝę,ÎyĎÖnăéěÎő3Ű€Eżm:žňěMĎ© 1™KűŐľő‹žzf|>gWz\Óó¬Ü>ݡ~µň:  ´[ˇwzu’íłü9rłšÎçóÉň™%ëâ€M®oz˛ç;tŇÉ2ć¤s¬‘滚Ž&őűűŐŇß#;A—×Őłąş˝|ť¦ÜĎC?ťĺőňçH ę–¦'m;é´µţ±FšďV:šÔ ěŹŐŇ ző')Ł®j7Ű‘ltŇś#‚4=ăQŃxD8§ŮIy°…/:4ZFÓ-ĺhćr‘‘&5ňĆr‡‘tútŐ0.HÓSo=r¬ÎřiąĎÁc1._tü+®ŽťŻoW‡Ď«÷żôÇĽGŇiuŇvú„«çĺŰN»M§ŔÓŤWz÷6=ť9â#h%›%>’ňř窋نow¬ĆŻrŻň”JoµCk÷ÎÜbüÄ»ôůjŔKUż×­ő“öÇjă1űŕ}ż@Ő`s©UýI§÷«Uď*śOŽĆjť‰–éĆΖ©1Ą±śĆđ6G×ĘžňWýß©xdKşeđą¸/téz ť ׀׊r¨€  t´_-©Ě&źĄkO-ŻË-Ë»ŞűĚŔŁíďWëOP;ň.}lł(óŐ(‰Őâ«Ä%VK¬—X ®ü™iŔŰhű¬ľŽ'K¬—X .±@\b5€¸Äjq‰Őâ«Ä%VK¬—X .±@\˙ďF‹ÔjtĄIEND®B`‚libtorrent-rasterbar-1.1.13/ed25519/000077500000000000000000000000001351156116000167415ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/ed25519/readme.md000066400000000000000000000131761351156116000205300ustar00rootroot00000000000000Ed25519 ======= This is a portable implementation of [Ed25519](http://ed25519.cr.yp.to/) based on the SUPERCOP "ref10" implementation. Additionally there is key exchanging and scalar addition included to further aid building a PKI using Ed25519. All code is in the public domain. All code is pure ANSI C without any dependencies, except for the random seed generation which uses standard OS cryptography APIs (`CryptGenRandom` on Windows, `/dev/urandom` on nix). If you wish to be entirely portable define `ED25519_NO_SEED`. This disables the `ed25519_create_seed` function, so if your application requires key generation you must supply your own seeding function (which is simply a 256 bit (32 byte) cryptographic random number generator). Performance ----------- On a Windows machine with an Intel Pentium B970 @ 2.3GHz I got the following speeds (running on only one a single core): Seed generation: 64us (15625 per second) Key generation: 88us (11364 per second) Message signing (short message): 87us (11494 per second) Message verifying (short message): 228us (4386 per second) Scalar addition: 100us (10000 per second) Key exchange: 220us (4545 per second) The speeds on other machines may vary. Sign/verify times will be higher with longer messages. The implementation significantly benefits from 64 bit architectures, if possible compile as 64 bit. Usage ----- Simply add all .c and .h files in the `src/` folder to your project and include `ed25519.h` in any file you want to use the API. If you prefer to use a shared library, only copy `ed25519.h` and define `ED25519_DLL` before importing. A windows DLL is pre-built. There are no defined types for seeds, private keys, public keys, shared secrets or signatures. Instead simple `unsigned char` buffers are used with the following sizes: ```c unsigned char seed[32]; unsigned char signature[64]; unsigned char public_key[32]; unsigned char private_key[64]; unsigned char scalar[32]; unsigned char shared_secret[32]; ``` API --- ```c int ed25519_create_seed(unsigned char *seed); ``` Creates a 32 byte random seed in `seed` for key generation. `seed` must be a writable 32 byte buffer. Returns 0 on success, and nonzero on failure. ```c void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); ``` Creates a new key pair from the given seed. `public_key` must be a writable 32 byte buffer, `private_key` must be a writable 64 byte buffer and `seed` must be a 32 byte buffer. ```c void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key); ``` Creates a signature of the given message with the given key pair. `signature` must be a writable 64 byte buffer. `message` must have at least `message_len` bytes to be read. ```c int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key); ``` Verifies the signature on the given message using `public_key`. `signature` must be a readable 64 byte buffer. `message` must have at least `message_len` bytes to be read. Returns 1 if the signature matches, 0 otherwise. ```c void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar); ``` Adds `scalar` to the given key pair where scalar is a 32 byte buffer (possibly generated with `ed25519_create_seed`), generating a new key pair. You can calculate the public key sum without knowing the private key and vice versa by passing in `NULL` for the key you don't know. This is useful for enforcing randomness on a key pair by a third party while only knowing the public key, among other things. Warning: the last bit of the scalar is ignored - if comparing scalars make sure to clear it with `scalar[31] &= 127`. ```c void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); ``` Performs a key exchange on the given public key and private key, producing a shared secret. It is recommended to hash the shared secret before using it. `shared_secret` must be a 32 byte writable buffer where the shared secret will be stored. Example ------- ```c unsigned char seed[32], public_key[32], private_key[64], signature[64]; unsigned char other_public_key[32], other_private_key[64], shared_secret[32]; const unsigned char message[] = "TEST MESSAGE"; /* create a random seed, and a key pair out of that seed */ if (ed25519_create_seed(seed)) { printf("error while generating seed\n"); exit(1); } ed25519_create_keypair(public_key, private_key, seed); /* create signature on the message with the key pair */ ed25519_sign(signature, message, strlen(message), public_key, private_key); /* verify the signature */ if (ed25519_verify(signature, message, strlen(message), public_key)) { printf("valid signature\n"); } else { printf("invalid signature\n"); } /* create a dummy keypair to use for a key exchange, normally you'd only have the public key and receive it through some communication channel */ if (ed25519_create_seed(seed)) { printf("error while generating seed\n"); exit(1); } ed25519_create_keypair(other_public_key, other_private_key, seed); /* do a key exchange with other_public_key */ ed25519_key_exchange(shared_secret, other_public_key, private_key); /* the magic here is that ed25519_key_exchange(shared_secret, public_key, other_private_key); would result in the same shared_secret */ ``` License ------- All code is in the public domain. libtorrent-rasterbar-1.1.13/ed25519/src/000077500000000000000000000000001351156116000175305ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/ed25519/src/add_scalar.cpp000066400000000000000000000035321351156116000223140ustar00rootroot00000000000000// ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" #include "libtorrent/ed25519.hpp" #include "ge.h" #include "sc.h" /* see http://crypto.stackexchange.com/a/6215/4697 */ void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) { const unsigned char SC_1[32] = {1}; /* scalar with value 1 */ unsigned char n[32]; ge_p3 nB; ge_p1p1 A_p1p1; ge_p3 A; ge_p3 public_key_unpacked; ge_cached T; int i; /* copy the scalar and clear highest bit */ for (i = 0; i < 31; ++i) { n[i] = scalar[i]; } n[31] = scalar[31] & 127; /* private key: a = n + t */ if (private_key) { sc_muladd(private_key, SC_1, n, private_key); } /* public key: A = nB + T */ if (public_key) { /* if we know the private key we don't need a point addition, which is faster */ /* using a "timing attack" you could find out wether or not we know the private key, but this information seems rather useless - if this is important pass public_key and private_key seperately in 2 function calls */ if (private_key) { ge_scalarmult_base(&A, private_key); } else { /* unpack public key into T */ ge_frombytes_negate_vartime(&public_key_unpacked, public_key); fe_neg(public_key_unpacked.X, public_key_unpacked.X); // undo negate fe_neg(public_key_unpacked.T, public_key_unpacked.T); // undo negate ge_p3_to_cached(&T, &public_key_unpacked); /* calculate n*B */ ge_scalarmult_base(&nB, n); /* A = n*B + T */ ge_add(&A_p1p1, &nB, &T); ge_p1p1_to_p3(&A, &A_p1p1); } /* pack public key */ ge_p3_tobytes(public_key, &A); } } libtorrent-rasterbar-1.1.13/ed25519/src/fe.cpp000066400000000000000000001074431351156116000206370ustar00rootroot00000000000000// ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" #include "fixedint.h" #include "fe.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4146 ) /* warning C4146: unary minus operator applied to unsigned type, result still unsigned */ #pragma warning(disable : 4244 ) /* warning C4244: '=' : conversion from 'int64_t' to 'int32_t', possible loss of data */ #endif // _MSC_VER /* helper functions */ static u64 load_3(const unsigned char *in) { u64 result; result = (u64) in[0]; result |= ((u64) in[1]) << 8; result |= ((u64) in[2]) << 16; return result; } static u64 load_4(const unsigned char *in) { u64 result; result = (u64) in[0]; result |= ((u64) in[1]) << 8; result |= ((u64) in[2]) << 16; result |= ((u64) in[3]) << 24; return result; } static inline i64 shift_left(i64 v, int s) { return i64(u64(v) << s); } /* h = 0 */ void fe_0(fe h) { h[0] = 0; h[1] = 0; h[2] = 0; h[3] = 0; h[4] = 0; h[5] = 0; h[6] = 0; h[7] = 0; h[8] = 0; h[9] = 0; } /* h = 1 */ void fe_1(fe h) { h[0] = 1; h[1] = 0; h[2] = 0; h[3] = 0; h[4] = 0; h[5] = 0; h[6] = 0; h[7] = 0; h[8] = 0; h[9] = 0; } /* h = f + g Can overlap h with f or g. Preconditions: |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. Postconditions: |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ void fe_add(fe h, const fe f, const fe g) { i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; i32 g0 = g[0]; i32 g1 = g[1]; i32 g2 = g[2]; i32 g3 = g[3]; i32 g4 = g[4]; i32 g5 = g[5]; i32 g6 = g[6]; i32 g7 = g[7]; i32 g8 = g[8]; i32 g9 = g[9]; i32 h0 = f0 + g0; i32 h1 = f1 + g1; i32 h2 = f2 + g2; i32 h3 = f3 + g3; i32 h4 = f4 + g4; i32 h5 = f5 + g5; i32 h6 = f6 + g6; i32 h7 = f7 + g7; i32 h8 = f8 + g8; i32 h9 = f9 + g9; h[0] = h0; h[1] = h1; h[2] = h2; h[3] = h3; h[4] = h4; h[5] = h5; h[6] = h6; h[7] = h7; h[8] = h8; h[9] = h9; } /* Replace (f,g) with (g,g) if b == 1; replace (f,g) with (f,g) if b == 0. Preconditions: b in {0,1}. */ void fe_cmov(fe f, const fe g, unsigned int b) { i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; i32 g0 = g[0]; i32 g1 = g[1]; i32 g2 = g[2]; i32 g3 = g[3]; i32 g4 = g[4]; i32 g5 = g[5]; i32 g6 = g[6]; i32 g7 = g[7]; i32 g8 = g[8]; i32 g9 = g[9]; i32 x0 = f0 ^ g0; i32 x1 = f1 ^ g1; i32 x2 = f2 ^ g2; i32 x3 = f3 ^ g3; i32 x4 = f4 ^ g4; i32 x5 = f5 ^ g5; i32 x6 = f6 ^ g6; i32 x7 = f7 ^ g7; i32 x8 = f8 ^ g8; i32 x9 = f9 ^ g9; b = (unsigned int) (- (int) b); /* silence warning */ x0 &= b; x1 &= b; x2 &= b; x3 &= b; x4 &= b; x5 &= b; x6 &= b; x7 &= b; x8 &= b; x9 &= b; f[0] = f0 ^ x0; f[1] = f1 ^ x1; f[2] = f2 ^ x2; f[3] = f3 ^ x3; f[4] = f4 ^ x4; f[5] = f5 ^ x5; f[6] = f6 ^ x6; f[7] = f7 ^ x7; f[8] = f8 ^ x8; f[9] = f9 ^ x9; } /* Replace (f,g) with (g,f) if b == 1; replace (f,g) with (f,g) if b == 0. Preconditions: b in {0,1}. */ void fe_cswap(fe f,fe g,unsigned int b) { i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; i32 g0 = g[0]; i32 g1 = g[1]; i32 g2 = g[2]; i32 g3 = g[3]; i32 g4 = g[4]; i32 g5 = g[5]; i32 g6 = g[6]; i32 g7 = g[7]; i32 g8 = g[8]; i32 g9 = g[9]; i32 x0 = f0 ^ g0; i32 x1 = f1 ^ g1; i32 x2 = f2 ^ g2; i32 x3 = f3 ^ g3; i32 x4 = f4 ^ g4; i32 x5 = f5 ^ g5; i32 x6 = f6 ^ g6; i32 x7 = f7 ^ g7; i32 x8 = f8 ^ g8; i32 x9 = f9 ^ g9; b = -b; // warning C4146: unary minus operator applied to unsigned type, result still unsigned x0 &= b; x1 &= b; x2 &= b; x3 &= b; x4 &= b; x5 &= b; x6 &= b; x7 &= b; x8 &= b; x9 &= b; f[0] = f0 ^ x0; f[1] = f1 ^ x1; f[2] = f2 ^ x2; f[3] = f3 ^ x3; f[4] = f4 ^ x4; f[5] = f5 ^ x5; f[6] = f6 ^ x6; f[7] = f7 ^ x7; f[8] = f8 ^ x8; f[9] = f9 ^ x9; g[0] = g0 ^ x0; g[1] = g1 ^ x1; g[2] = g2 ^ x2; g[3] = g3 ^ x3; g[4] = g4 ^ x4; g[5] = g5 ^ x5; g[6] = g6 ^ x6; g[7] = g7 ^ x7; g[8] = g8 ^ x8; g[9] = g9 ^ x9; } /* h = f */ void fe_copy(fe h, const fe f) { i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; h[0] = f0; h[1] = f1; h[2] = f2; h[3] = f3; h[4] = f4; h[5] = f5; h[6] = f6; h[7] = f7; h[8] = f8; h[9] = f9; } /* Ignores top bit of h. */ void fe_frombytes(fe h, const unsigned char *s) { i64 h0 = load_4(s); i64 h1 = load_3(s + 4) << 6; i64 h2 = load_3(s + 7) << 5; i64 h3 = load_3(s + 10) << 3; i64 h4 = load_3(s + 13) << 2; i64 h5 = load_4(s + 16); i64 h6 = load_3(s + 20) << 7; i64 h7 = load_3(s + 23) << 5; i64 h8 = load_3(s + 26) << 4; i64 h9 = (load_3(s + 29) & 8388607) << 2; i64 carry0; i64 carry1; i64 carry2; i64 carry3; i64 carry4; i64 carry5; i64 carry6; i64 carry7; i64 carry8; i64 carry9; carry9 = (h9 + (i64) (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= shift_left(carry9, 25); carry1 = (h1 + (i64) (1 << 24)) >> 25; h2 += carry1; h1 -= shift_left(carry1, 25); carry3 = (h3 + (i64) (1 << 24)) >> 25; h4 += carry3; h3 -= shift_left(carry3, 25); carry5 = (h5 + (i64) (1 << 24)) >> 25; h6 += carry5; h5 -= shift_left(carry5, 25); carry7 = (h7 + (i64) (1 << 24)) >> 25; h8 += carry7; h7 -= shift_left(carry7, 25); carry0 = (h0 + (i64) (1 << 25)) >> 26; h1 += carry0; h0 -= shift_left(carry0, 26); carry2 = (h2 + (i64) (1 << 25)) >> 26; h3 += carry2; h2 -= shift_left(carry2, 26); carry4 = (h4 + (i64) (1 << 25)) >> 26; h5 += carry4; h4 -= shift_left(carry4, 26); carry6 = (h6 + (i64) (1 << 25)) >> 26; h7 += carry6; h6 -= shift_left(carry6, 26); carry8 = (h8 + (i64) (1 << 25)) >> 26; h9 += carry8; h8 -= shift_left(carry8, 26); h[0] = (i32) h0; h[1] = (i32) h1; h[2] = (i32) h2; h[3] = (i32) h3; h[4] = (i32) h4; h[5] = (i32) h5; h[6] = (i32) h6; h[7] = (i32) h7; h[8] = (i32) h8; h[9] = (i32) h9; } void fe_invert(fe out, const fe z) { fe t0; fe t1; fe t2; fe t3; int i; fe_sq(t0, z); for (i = 1; i < 1; ++i) { fe_sq(t0, t0); } fe_sq(t1, t0); for (i = 1; i < 2; ++i) { fe_sq(t1, t1); } fe_mul(t1, z, t1); fe_mul(t0, t0, t1); fe_sq(t2, t0); for (i = 1; i < 1; ++i) { fe_sq(t2, t2); } fe_mul(t1, t1, t2); fe_sq(t2, t1); for (i = 1; i < 5; ++i) { fe_sq(t2, t2); } fe_mul(t1, t2, t1); fe_sq(t2, t1); for (i = 1; i < 10; ++i) { fe_sq(t2, t2); } fe_mul(t2, t2, t1); fe_sq(t3, t2); for (i = 1; i < 20; ++i) { fe_sq(t3, t3); } fe_mul(t2, t3, t2); fe_sq(t2, t2); for (i = 1; i < 10; ++i) { fe_sq(t2, t2); } fe_mul(t1, t2, t1); fe_sq(t2, t1); for (i = 1; i < 50; ++i) { fe_sq(t2, t2); } fe_mul(t2, t2, t1); fe_sq(t3, t2); for (i = 1; i < 100; ++i) { fe_sq(t3, t3); } fe_mul(t2, t3, t2); fe_sq(t2, t2); for (i = 1; i < 50; ++i) { fe_sq(t2, t2); } fe_mul(t1, t2, t1); fe_sq(t1, t1); for (i = 1; i < 5; ++i) { fe_sq(t1, t1); } fe_mul(out, t1, t0); } /* return 1 if f is in {1,3,5,...,q-2} return 0 if f is in {0,2,4,...,q-1} Preconditions: |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ int fe_isnegative(const fe f) { unsigned char s[32]; fe_tobytes(s, f); return s[0] & 1; } /* return 1 if f == 0 return 0 if f != 0 Preconditions: |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ int fe_isnonzero(const fe f) { unsigned char s[32]; unsigned char r; fe_tobytes(s, f); r = s[0]; #define F(i) r |= s[i] F(1); F(2); F(3); F(4); F(5); F(6); F(7); F(8); F(9); F(10); F(11); F(12); F(13); F(14); F(15); F(16); F(17); F(18); F(19); F(20); F(21); F(22); F(23); F(24); F(25); F(26); F(27); F(28); F(29); F(30); F(31); #undef F return r != 0; } /* h = f * g Can overlap h with f or g. Preconditions: |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. Postconditions: |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. */ /* Notes on implementation strategy: Using schoolbook multiplication. Karatsuba would save a little in some cost models. Most multiplications by 2 and 19 are 32-bit precomputations; cheaper than 64-bit postcomputations. There is one remaining multiplication by 19 in the carry chain; one *19 precomputation can be merged into this, but the resulting data flow is considerably less clean. There are 12 carries below. 10 of them are 2-way parallelizable and vectorizable. Can get away with 11 carries, but then data flow is much deeper. With tighter constraints on inputs can squeeze carries into int32. */ void fe_mul(fe h, const fe f, const fe g) { i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; i32 g0 = g[0]; i32 g1 = g[1]; i32 g2 = g[2]; i32 g3 = g[3]; i32 g4 = g[4]; i32 g5 = g[5]; i32 g6 = g[6]; i32 g7 = g[7]; i32 g8 = g[8]; i32 g9 = g[9]; i32 g1_19 = 19 * g1; /* 1.959375*2^29 */ i32 g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ i32 g3_19 = 19 * g3; i32 g4_19 = 19 * g4; i32 g5_19 = 19 * g5; i32 g6_19 = 19 * g6; i32 g7_19 = 19 * g7; i32 g8_19 = 19 * g8; i32 g9_19 = 19 * g9; i32 f1_2 = 2 * f1; i32 f3_2 = 2 * f3; i32 f5_2 = 2 * f5; i32 f7_2 = 2 * f7; i32 f9_2 = 2 * f9; i64 f0g0 = f0 * (i64) g0; i64 f0g1 = f0 * (i64) g1; i64 f0g2 = f0 * (i64) g2; i64 f0g3 = f0 * (i64) g3; i64 f0g4 = f0 * (i64) g4; i64 f0g5 = f0 * (i64) g5; i64 f0g6 = f0 * (i64) g6; i64 f0g7 = f0 * (i64) g7; i64 f0g8 = f0 * (i64) g8; i64 f0g9 = f0 * (i64) g9; i64 f1g0 = f1 * (i64) g0; i64 f1g1_2 = f1_2 * (i64) g1; i64 f1g2 = f1 * (i64) g2; i64 f1g3_2 = f1_2 * (i64) g3; i64 f1g4 = f1 * (i64) g4; i64 f1g5_2 = f1_2 * (i64) g5; i64 f1g6 = f1 * (i64) g6; i64 f1g7_2 = f1_2 * (i64) g7; i64 f1g8 = f1 * (i64) g8; i64 f1g9_38 = f1_2 * (i64) g9_19; i64 f2g0 = f2 * (i64) g0; i64 f2g1 = f2 * (i64) g1; i64 f2g2 = f2 * (i64) g2; i64 f2g3 = f2 * (i64) g3; i64 f2g4 = f2 * (i64) g4; i64 f2g5 = f2 * (i64) g5; i64 f2g6 = f2 * (i64) g6; i64 f2g7 = f2 * (i64) g7; i64 f2g8_19 = f2 * (i64) g8_19; i64 f2g9_19 = f2 * (i64) g9_19; i64 f3g0 = f3 * (i64) g0; i64 f3g1_2 = f3_2 * (i64) g1; i64 f3g2 = f3 * (i64) g2; i64 f3g3_2 = f3_2 * (i64) g3; i64 f3g4 = f3 * (i64) g4; i64 f3g5_2 = f3_2 * (i64) g5; i64 f3g6 = f3 * (i64) g6; i64 f3g7_38 = f3_2 * (i64) g7_19; i64 f3g8_19 = f3 * (i64) g8_19; i64 f3g9_38 = f3_2 * (i64) g9_19; i64 f4g0 = f4 * (i64) g0; i64 f4g1 = f4 * (i64) g1; i64 f4g2 = f4 * (i64) g2; i64 f4g3 = f4 * (i64) g3; i64 f4g4 = f4 * (i64) g4; i64 f4g5 = f4 * (i64) g5; i64 f4g6_19 = f4 * (i64) g6_19; i64 f4g7_19 = f4 * (i64) g7_19; i64 f4g8_19 = f4 * (i64) g8_19; i64 f4g9_19 = f4 * (i64) g9_19; i64 f5g0 = f5 * (i64) g0; i64 f5g1_2 = f5_2 * (i64) g1; i64 f5g2 = f5 * (i64) g2; i64 f5g3_2 = f5_2 * (i64) g3; i64 f5g4 = f5 * (i64) g4; i64 f5g5_38 = f5_2 * (i64) g5_19; i64 f5g6_19 = f5 * (i64) g6_19; i64 f5g7_38 = f5_2 * (i64) g7_19; i64 f5g8_19 = f5 * (i64) g8_19; i64 f5g9_38 = f5_2 * (i64) g9_19; i64 f6g0 = f6 * (i64) g0; i64 f6g1 = f6 * (i64) g1; i64 f6g2 = f6 * (i64) g2; i64 f6g3 = f6 * (i64) g3; i64 f6g4_19 = f6 * (i64) g4_19; i64 f6g5_19 = f6 * (i64) g5_19; i64 f6g6_19 = f6 * (i64) g6_19; i64 f6g7_19 = f6 * (i64) g7_19; i64 f6g8_19 = f6 * (i64) g8_19; i64 f6g9_19 = f6 * (i64) g9_19; i64 f7g0 = f7 * (i64) g0; i64 f7g1_2 = f7_2 * (i64) g1; i64 f7g2 = f7 * (i64) g2; i64 f7g3_38 = f7_2 * (i64) g3_19; i64 f7g4_19 = f7 * (i64) g4_19; i64 f7g5_38 = f7_2 * (i64) g5_19; i64 f7g6_19 = f7 * (i64) g6_19; i64 f7g7_38 = f7_2 * (i64) g7_19; i64 f7g8_19 = f7 * (i64) g8_19; i64 f7g9_38 = f7_2 * (i64) g9_19; i64 f8g0 = f8 * (i64) g0; i64 f8g1 = f8 * (i64) g1; i64 f8g2_19 = f8 * (i64) g2_19; i64 f8g3_19 = f8 * (i64) g3_19; i64 f8g4_19 = f8 * (i64) g4_19; i64 f8g5_19 = f8 * (i64) g5_19; i64 f8g6_19 = f8 * (i64) g6_19; i64 f8g7_19 = f8 * (i64) g7_19; i64 f8g8_19 = f8 * (i64) g8_19; i64 f8g9_19 = f8 * (i64) g9_19; i64 f9g0 = f9 * (i64) g0; i64 f9g1_38 = f9_2 * (i64) g1_19; i64 f9g2_19 = f9 * (i64) g2_19; i64 f9g3_38 = f9_2 * (i64) g3_19; i64 f9g4_19 = f9 * (i64) g4_19; i64 f9g5_38 = f9_2 * (i64) g5_19; i64 f9g6_19 = f9 * (i64) g6_19; i64 f9g7_38 = f9_2 * (i64) g7_19; i64 f9g8_19 = f9 * (i64) g8_19; i64 f9g9_38 = f9_2 * (i64) g9_19; i64 h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38; i64 h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19; i64 h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38; i64 h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19; i64 h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38; i64 h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19; i64 h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38; i64 h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19; i64 h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38; i64 h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 ; i64 carry0; i64 carry1; i64 carry2; i64 carry3; i64 carry4; i64 carry5; i64 carry6; i64 carry7; i64 carry8; i64 carry9; carry0 = (h0 + (i64) (1 << 25)) >> 26; h1 += carry0; h0 -= shift_left(carry0, 26); carry4 = (h4 + (i64) (1 << 25)) >> 26; h5 += carry4; h4 -= shift_left(carry4, 26); carry1 = (h1 + (i64) (1 << 24)) >> 25; h2 += carry1; h1 -= shift_left(carry1, 25); carry5 = (h5 + (i64) (1 << 24)) >> 25; h6 += carry5; h5 -= shift_left(carry5, 25); carry2 = (h2 + (i64) (1 << 25)) >> 26; h3 += carry2; h2 -= shift_left(carry2, 26); carry6 = (h6 + (i64) (1 << 25)) >> 26; h7 += carry6; h6 -= shift_left(carry6, 26); carry3 = (h3 + (i64) (1 << 24)) >> 25; h4 += carry3; h3 -= shift_left(carry3, 25); carry7 = (h7 + (i64) (1 << 24)) >> 25; h8 += carry7; h7 -= shift_left(carry7, 25); carry4 = (h4 + (i64) (1 << 25)) >> 26; h5 += carry4; h4 -= shift_left(carry4, 26); carry8 = (h8 + (i64) (1 << 25)) >> 26; h9 += carry8; h8 -= shift_left(carry8, 26); carry9 = (h9 + (i64) (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= shift_left(carry9, 25); carry0 = (h0 + (i64) (1 << 25)) >> 26; h1 += carry0; h0 -= shift_left(carry0, 26); h[0] = (i32) h0; h[1] = (i32) h1; h[2] = (i32) h2; h[3] = (i32) h3; h[4] = (i32) h4; h[5] = (i32) h5; h[6] = (i32) h6; h[7] = (i32) h7; h[8] = (i32) h8; h[9] = (i32) h9; } /* h = f * 121666 Can overlap h with f. Preconditions: |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. Postconditions: |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. */ void fe_mul121666(fe h, fe f) { i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; i64 h0 = f0 * (i64) 121666; i64 h1 = f1 * (i64) 121666; i64 h2 = f2 * (i64) 121666; i64 h3 = f3 * (i64) 121666; i64 h4 = f4 * (i64) 121666; i64 h5 = f5 * (i64) 121666; i64 h6 = f6 * (i64) 121666; i64 h7 = f7 * (i64) 121666; i64 h8 = f8 * (i64) 121666; i64 h9 = f9 * (i64) 121666; i64 carry0; i64 carry1; i64 carry2; i64 carry3; i64 carry4; i64 carry5; i64 carry6; i64 carry7; i64 carry8; i64 carry9; carry9 = (h9 + (i64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= shift_left(carry9, 25); carry1 = (h1 + (i64) (1<<24)) >> 25; h2 += carry1; h1 -= shift_left(carry1, 25); carry3 = (h3 + (i64) (1<<24)) >> 25; h4 += carry3; h3 -= shift_left(carry3, 25); carry5 = (h5 + (i64) (1<<24)) >> 25; h6 += carry5; h5 -= shift_left(carry5, 25); carry7 = (h7 + (i64) (1<<24)) >> 25; h8 += carry7; h7 -= shift_left(carry7, 25); carry0 = (h0 + (i64) (1<<25)) >> 26; h1 += carry0; h0 -= shift_left(carry0, 26); carry2 = (h2 + (i64) (1<<25)) >> 26; h3 += carry2; h2 -= shift_left(carry2, 26); carry4 = (h4 + (i64) (1<<25)) >> 26; h5 += carry4; h4 -= shift_left(carry4, 26); carry6 = (h6 + (i64) (1<<25)) >> 26; h7 += carry6; h6 -= shift_left(carry6, 26); carry8 = (h8 + (i64) (1<<25)) >> 26; h9 += carry8; h8 -= shift_left(carry8, 26); h[0] = h0; h[1] = h1; h[2] = h2; h[3] = h3; h[4] = h4; h[5] = h5; h[6] = h6; h[7] = h7; h[8] = h8; h[9] = h9; } /* h = -f Preconditions: |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. Postconditions: |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. */ void fe_neg(fe h, const fe f) { i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; i32 h0 = -f0; i32 h1 = -f1; i32 h2 = -f2; i32 h3 = -f3; i32 h4 = -f4; i32 h5 = -f5; i32 h6 = -f6; i32 h7 = -f7; i32 h8 = -f8; i32 h9 = -f9; h[0] = h0; h[1] = h1; h[2] = h2; h[3] = h3; h[4] = h4; h[5] = h5; h[6] = h6; h[7] = h7; h[8] = h8; h[9] = h9; } void fe_pow22523(fe out, const fe z) { fe t0; fe t1; fe t2; int i; fe_sq(t0, z); for (i = 1; i < 1; ++i) { fe_sq(t0, t0); } fe_sq(t1, t0); for (i = 1; i < 2; ++i) { fe_sq(t1, t1); } fe_mul(t1, z, t1); fe_mul(t0, t0, t1); fe_sq(t0, t0); for (i = 1; i < 1; ++i) { fe_sq(t0, t0); } fe_mul(t0, t1, t0); fe_sq(t1, t0); for (i = 1; i < 5; ++i) { fe_sq(t1, t1); } fe_mul(t0, t1, t0); fe_sq(t1, t0); for (i = 1; i < 10; ++i) { fe_sq(t1, t1); } fe_mul(t1, t1, t0); fe_sq(t2, t1); for (i = 1; i < 20; ++i) { fe_sq(t2, t2); } fe_mul(t1, t2, t1); fe_sq(t1, t1); for (i = 1; i < 10; ++i) { fe_sq(t1, t1); } fe_mul(t0, t1, t0); fe_sq(t1, t0); for (i = 1; i < 50; ++i) { fe_sq(t1, t1); } fe_mul(t1, t1, t0); fe_sq(t2, t1); for (i = 1; i < 100; ++i) { fe_sq(t2, t2); } fe_mul(t1, t2, t1); fe_sq(t1, t1); for (i = 1; i < 50; ++i) { fe_sq(t1, t1); } fe_mul(t0, t1, t0); fe_sq(t0, t0); for (i = 1; i < 2; ++i) { fe_sq(t0, t0); } fe_mul(out, t0, z); return; } /* h = f * f Can overlap h with f. Preconditions: |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. Postconditions: |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. */ /* See fe_mul.c for discussion of implementation strategy. */ void fe_sq(fe h, const fe f) { i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; i32 f0_2 = 2 * f0; i32 f1_2 = 2 * f1; i32 f2_2 = 2 * f2; i32 f3_2 = 2 * f3; i32 f4_2 = 2 * f4; i32 f5_2 = 2 * f5; i32 f6_2 = 2 * f6; i32 f7_2 = 2 * f7; i32 f5_38 = 38 * f5; /* 1.959375*2^30 */ i32 f6_19 = 19 * f6; /* 1.959375*2^30 */ i32 f7_38 = 38 * f7; /* 1.959375*2^30 */ i32 f8_19 = 19 * f8; /* 1.959375*2^30 */ i32 f9_38 = 38 * f9; /* 1.959375*2^30 */ i64 f0f0 = f0 * (i64) f0; i64 f0f1_2 = f0_2 * (i64) f1; i64 f0f2_2 = f0_2 * (i64) f2; i64 f0f3_2 = f0_2 * (i64) f3; i64 f0f4_2 = f0_2 * (i64) f4; i64 f0f5_2 = f0_2 * (i64) f5; i64 f0f6_2 = f0_2 * (i64) f6; i64 f0f7_2 = f0_2 * (i64) f7; i64 f0f8_2 = f0_2 * (i64) f8; i64 f0f9_2 = f0_2 * (i64) f9; i64 f1f1_2 = f1_2 * (i64) f1; i64 f1f2_2 = f1_2 * (i64) f2; i64 f1f3_4 = f1_2 * (i64) f3_2; i64 f1f4_2 = f1_2 * (i64) f4; i64 f1f5_4 = f1_2 * (i64) f5_2; i64 f1f6_2 = f1_2 * (i64) f6; i64 f1f7_4 = f1_2 * (i64) f7_2; i64 f1f8_2 = f1_2 * (i64) f8; i64 f1f9_76 = f1_2 * (i64) f9_38; i64 f2f2 = f2 * (i64) f2; i64 f2f3_2 = f2_2 * (i64) f3; i64 f2f4_2 = f2_2 * (i64) f4; i64 f2f5_2 = f2_2 * (i64) f5; i64 f2f6_2 = f2_2 * (i64) f6; i64 f2f7_2 = f2_2 * (i64) f7; i64 f2f8_38 = f2_2 * (i64) f8_19; i64 f2f9_38 = f2 * (i64) f9_38; i64 f3f3_2 = f3_2 * (i64) f3; i64 f3f4_2 = f3_2 * (i64) f4; i64 f3f5_4 = f3_2 * (i64) f5_2; i64 f3f6_2 = f3_2 * (i64) f6; i64 f3f7_76 = f3_2 * (i64) f7_38; i64 f3f8_38 = f3_2 * (i64) f8_19; i64 f3f9_76 = f3_2 * (i64) f9_38; i64 f4f4 = f4 * (i64) f4; i64 f4f5_2 = f4_2 * (i64) f5; i64 f4f6_38 = f4_2 * (i64) f6_19; i64 f4f7_38 = f4 * (i64) f7_38; i64 f4f8_38 = f4_2 * (i64) f8_19; i64 f4f9_38 = f4 * (i64) f9_38; i64 f5f5_38 = f5 * (i64) f5_38; i64 f5f6_38 = f5_2 * (i64) f6_19; i64 f5f7_76 = f5_2 * (i64) f7_38; i64 f5f8_38 = f5_2 * (i64) f8_19; i64 f5f9_76 = f5_2 * (i64) f9_38; i64 f6f6_19 = f6 * (i64) f6_19; i64 f6f7_38 = f6 * (i64) f7_38; i64 f6f8_38 = f6_2 * (i64) f8_19; i64 f6f9_38 = f6 * (i64) f9_38; i64 f7f7_38 = f7 * (i64) f7_38; i64 f7f8_38 = f7_2 * (i64) f8_19; i64 f7f9_76 = f7_2 * (i64) f9_38; i64 f8f8_19 = f8 * (i64) f8_19; i64 f8f9_38 = f8 * (i64) f9_38; i64 f9f9_38 = f9 * (i64) f9_38; i64 h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; i64 h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; i64 h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; i64 h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; i64 h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; i64 h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; i64 h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; i64 h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; i64 h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; i64 h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; i64 carry0; i64 carry1; i64 carry2; i64 carry3; i64 carry4; i64 carry5; i64 carry6; i64 carry7; i64 carry8; i64 carry9; carry0 = (h0 + (i64) (1 << 25)) >> 26; h1 += carry0; h0 -= shift_left(carry0, 26); carry4 = (h4 + (i64) (1 << 25)) >> 26; h5 += carry4; h4 -= shift_left(carry4, 26); carry1 = (h1 + (i64) (1 << 24)) >> 25; h2 += carry1; h1 -= shift_left(carry1, 25); carry5 = (h5 + (i64) (1 << 24)) >> 25; h6 += carry5; h5 -= shift_left(carry5, 25); carry2 = (h2 + (i64) (1 << 25)) >> 26; h3 += carry2; h2 -= shift_left(carry2, 26); carry6 = (h6 + (i64) (1 << 25)) >> 26; h7 += carry6; h6 -= shift_left(carry6, 26); carry3 = (h3 + (i64) (1 << 24)) >> 25; h4 += carry3; h3 -= shift_left(carry3, 25); carry7 = (h7 + (i64) (1 << 24)) >> 25; h8 += carry7; h7 -= shift_left(carry7, 25); carry4 = (h4 + (i64) (1 << 25)) >> 26; h5 += carry4; h4 -= shift_left(carry4, 26); carry8 = (h8 + (i64) (1 << 25)) >> 26; h9 += carry8; h8 -= shift_left(carry8, 26); carry9 = (h9 + (i64) (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= shift_left(carry9, 25); carry0 = (h0 + (i64) (1 << 25)) >> 26; h1 += carry0; h0 -= shift_left(carry0, 26); h[0] = (i32) h0; h[1] = (i32) h1; h[2] = (i32) h2; h[3] = (i32) h3; h[4] = (i32) h4; h[5] = (i32) h5; h[6] = (i32) h6; h[7] = (i32) h7; h[8] = (i32) h8; h[9] = (i32) h9; } /* h = 2 * f * f Can overlap h with f. Preconditions: |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. Postconditions: |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. */ /* See fe_mul.c for discussion of implementation strategy. */ void fe_sq2(fe h, const fe f) { i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; i32 f0_2 = 2 * f0; i32 f1_2 = 2 * f1; i32 f2_2 = 2 * f2; i32 f3_2 = 2 * f3; i32 f4_2 = 2 * f4; i32 f5_2 = 2 * f5; i32 f6_2 = 2 * f6; i32 f7_2 = 2 * f7; i32 f5_38 = 38 * f5; /* 1.959375*2^30 */ i32 f6_19 = 19 * f6; /* 1.959375*2^30 */ i32 f7_38 = 38 * f7; /* 1.959375*2^30 */ i32 f8_19 = 19 * f8; /* 1.959375*2^30 */ i32 f9_38 = 38 * f9; /* 1.959375*2^30 */ i64 f0f0 = f0 * (i64) f0; i64 f0f1_2 = f0_2 * (i64) f1; i64 f0f2_2 = f0_2 * (i64) f2; i64 f0f3_2 = f0_2 * (i64) f3; i64 f0f4_2 = f0_2 * (i64) f4; i64 f0f5_2 = f0_2 * (i64) f5; i64 f0f6_2 = f0_2 * (i64) f6; i64 f0f7_2 = f0_2 * (i64) f7; i64 f0f8_2 = f0_2 * (i64) f8; i64 f0f9_2 = f0_2 * (i64) f9; i64 f1f1_2 = f1_2 * (i64) f1; i64 f1f2_2 = f1_2 * (i64) f2; i64 f1f3_4 = f1_2 * (i64) f3_2; i64 f1f4_2 = f1_2 * (i64) f4; i64 f1f5_4 = f1_2 * (i64) f5_2; i64 f1f6_2 = f1_2 * (i64) f6; i64 f1f7_4 = f1_2 * (i64) f7_2; i64 f1f8_2 = f1_2 * (i64) f8; i64 f1f9_76 = f1_2 * (i64) f9_38; i64 f2f2 = f2 * (i64) f2; i64 f2f3_2 = f2_2 * (i64) f3; i64 f2f4_2 = f2_2 * (i64) f4; i64 f2f5_2 = f2_2 * (i64) f5; i64 f2f6_2 = f2_2 * (i64) f6; i64 f2f7_2 = f2_2 * (i64) f7; i64 f2f8_38 = f2_2 * (i64) f8_19; i64 f2f9_38 = f2 * (i64) f9_38; i64 f3f3_2 = f3_2 * (i64) f3; i64 f3f4_2 = f3_2 * (i64) f4; i64 f3f5_4 = f3_2 * (i64) f5_2; i64 f3f6_2 = f3_2 * (i64) f6; i64 f3f7_76 = f3_2 * (i64) f7_38; i64 f3f8_38 = f3_2 * (i64) f8_19; i64 f3f9_76 = f3_2 * (i64) f9_38; i64 f4f4 = f4 * (i64) f4; i64 f4f5_2 = f4_2 * (i64) f5; i64 f4f6_38 = f4_2 * (i64) f6_19; i64 f4f7_38 = f4 * (i64) f7_38; i64 f4f8_38 = f4_2 * (i64) f8_19; i64 f4f9_38 = f4 * (i64) f9_38; i64 f5f5_38 = f5 * (i64) f5_38; i64 f5f6_38 = f5_2 * (i64) f6_19; i64 f5f7_76 = f5_2 * (i64) f7_38; i64 f5f8_38 = f5_2 * (i64) f8_19; i64 f5f9_76 = f5_2 * (i64) f9_38; i64 f6f6_19 = f6 * (i64) f6_19; i64 f6f7_38 = f6 * (i64) f7_38; i64 f6f8_38 = f6_2 * (i64) f8_19; i64 f6f9_38 = f6 * (i64) f9_38; i64 f7f7_38 = f7 * (i64) f7_38; i64 f7f8_38 = f7_2 * (i64) f8_19; i64 f7f9_76 = f7_2 * (i64) f9_38; i64 f8f8_19 = f8 * (i64) f8_19; i64 f8f9_38 = f8 * (i64) f9_38; i64 f9f9_38 = f9 * (i64) f9_38; i64 h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; i64 h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; i64 h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; i64 h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; i64 h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; i64 h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; i64 h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; i64 h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; i64 h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; i64 h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; i64 carry0; i64 carry1; i64 carry2; i64 carry3; i64 carry4; i64 carry5; i64 carry6; i64 carry7; i64 carry8; i64 carry9; h0 += h0; h1 += h1; h2 += h2; h3 += h3; h4 += h4; h5 += h5; h6 += h6; h7 += h7; h8 += h8; h9 += h9; carry0 = (h0 + (i64) (1 << 25)) >> 26; h1 += carry0; h0 -= shift_left(carry0, 26); carry4 = (h4 + (i64) (1 << 25)) >> 26; h5 += carry4; h4 -= shift_left(carry4, 26); carry1 = (h1 + (i64) (1 << 24)) >> 25; h2 += carry1; h1 -= shift_left(carry1, 25); carry5 = (h5 + (i64) (1 << 24)) >> 25; h6 += carry5; h5 -= shift_left(carry5, 25); carry2 = (h2 + (i64) (1 << 25)) >> 26; h3 += carry2; h2 -= shift_left(carry2, 26); carry6 = (h6 + (i64) (1 << 25)) >> 26; h7 += carry6; h6 -= shift_left(carry6, 26); carry3 = (h3 + (i64) (1 << 24)) >> 25; h4 += carry3; h3 -= shift_left(carry3, 25); carry7 = (h7 + (i64) (1 << 24)) >> 25; h8 += carry7; h7 -= shift_left(carry7, 25); carry4 = (h4 + (i64) (1 << 25)) >> 26; h5 += carry4; h4 -= shift_left(carry4, 26); carry8 = (h8 + (i64) (1 << 25)) >> 26; h9 += carry8; h8 -= shift_left(carry8, 26); carry9 = (h9 + (i64) (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= shift_left(carry9, 25); carry0 = (h0 + (i64) (1 << 25)) >> 26; h1 += carry0; h0 -= shift_left(carry0, 26); h[0] = (i32) h0; h[1] = (i32) h1; h[2] = (i32) h2; h[3] = (i32) h3; h[4] = (i32) h4; h[5] = (i32) h5; h[6] = (i32) h6; h[7] = (i32) h7; h[8] = (i32) h8; h[9] = (i32) h9; } /* h = f - g Can overlap h with f or g. Preconditions: |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. Postconditions: |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ void fe_sub(fe h, const fe f, const fe g) { i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; i32 g0 = g[0]; i32 g1 = g[1]; i32 g2 = g[2]; i32 g3 = g[3]; i32 g4 = g[4]; i32 g5 = g[5]; i32 g6 = g[6]; i32 g7 = g[7]; i32 g8 = g[8]; i32 g9 = g[9]; i32 h0 = f0 - g0; i32 h1 = f1 - g1; i32 h2 = f2 - g2; i32 h3 = f3 - g3; i32 h4 = f4 - g4; i32 h5 = f5 - g5; i32 h6 = f6 - g6; i32 h7 = f7 - g7; i32 h8 = f8 - g8; i32 h9 = f9 - g9; h[0] = h0; h[1] = h1; h[2] = h2; h[3] = h3; h[4] = h4; h[5] = h5; h[6] = h6; h[7] = h7; h[8] = h8; h[9] = h9; } /* Preconditions: |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. Write p=2^255-19; q=floor(h/p). Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). Proof: Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). Then 0> 25; q = (h0 + q) >> 26; q = (h1 + q) >> 25; q = (h2 + q) >> 26; q = (h3 + q) >> 25; q = (h4 + q) >> 26; q = (h5 + q) >> 25; q = (h6 + q) >> 26; q = (h7 + q) >> 25; q = (h8 + q) >> 26; q = (h9 + q) >> 25; /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ h0 += 19 * q; /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ carry0 = h0 >> 26; h1 += carry0; h0 -= shift_left(carry0, 26); carry1 = h1 >> 25; h2 += carry1; h1 -= shift_left(carry1, 25); carry2 = h2 >> 26; h3 += carry2; h2 -= shift_left(carry2, 26); carry3 = h3 >> 25; h4 += carry3; h3 -= shift_left(carry3, 25); carry4 = h4 >> 26; h5 += carry4; h4 -= shift_left(carry4, 26); carry5 = h5 >> 25; h6 += carry5; h5 -= shift_left(carry5, 25); carry6 = h6 >> 26; h7 += carry6; h6 -= shift_left(carry6, 26); carry7 = h7 >> 25; h8 += carry7; h7 -= shift_left(carry7, 25); carry8 = h8 >> 26; h9 += carry8; h8 -= shift_left(carry8, 26); carry9 = h9 >> 25; h9 -= shift_left(carry9, 25); /* h10 = carry9 */ /* Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. Have h0+...+2^230 h9 between 0 and 2^255-1; evidently 2^255 h10-2^255 q = 0. Goal: Output h0+...+2^230 h9. */ s[0] = (unsigned char) ((h0 >> 0) & 0xff); s[1] = (unsigned char) ((h0 >> 8) & 0xff); s[2] = (unsigned char) ((h0 >> 16) & 0xff); s[3] = (unsigned char) (((h0 >> 24) | (h1 << 2)) & 0xff); s[4] = (unsigned char) ((h1 >> 6) & 0xff); s[5] = (unsigned char) ((h1 >> 14) & 0xff); s[6] = (unsigned char) (((h1 >> 22) | (h2 << 3)) & 0xff); s[7] = (unsigned char) ((h2 >> 5) & 0xff); s[8] = (unsigned char) ((h2 >> 13) & 0xff); s[9] = (unsigned char) (((h2 >> 21) | (h3 << 5)) & 0xff); s[10] = (unsigned char) ((h3 >> 3) & 0xff); s[11] = (unsigned char) ((h3 >> 11) & 0xff); s[12] = (unsigned char) (((h3 >> 19) | (h4 << 6)) & 0xff); s[13] = (unsigned char) ((h4 >> 2) & 0xff); s[14] = (unsigned char) ((h4 >> 10) & 0xff); s[15] = (unsigned char) ((h4 >> 18) & 0xff); s[16] = (unsigned char) ((h5 >> 0) & 0xff); s[17] = (unsigned char) ((h5 >> 8) & 0xff); s[18] = (unsigned char) ((h5 >> 16) & 0xff); s[19] = (unsigned char) (((h5 >> 24) | (h6 << 1)) & 0xff); s[20] = (unsigned char) ((h6 >> 7) & 0xff); s[21] = (unsigned char) ((h6 >> 15) & 0xff); s[22] = (unsigned char) (((h6 >> 23) | (h7 << 3)) & 0xff); s[23] = (unsigned char) ((h7 >> 5) & 0xff); s[24] = (unsigned char) ((h7 >> 13) & 0xff); s[25] = (unsigned char) (((h7 >> 21) | (h8 << 4)) & 0xff); s[26] = (unsigned char) ((h8 >> 4) & 0xff); s[27] = (unsigned char) ((h8 >> 12) & 0xff); s[28] = (unsigned char) (((h8 >> 20) | (h9 << 6)) & 0xff); s[29] = (unsigned char) ((h9 >> 2) & 0xff); s[30] = (unsigned char) ((h9 >> 10) & 0xff); s[31] = (unsigned char) ((h9 >> 18) & 0xff); } #ifdef _MSC_VER #pragma warning(pop) #endif // _MSC_VER libtorrent-rasterbar-1.1.13/ed25519/src/fe.h000066400000000000000000000017201351156116000202730ustar00rootroot00000000000000#ifndef FE_H #define FE_H #include "fixedint.h" /* fe means field element. Here the field is \Z/(2^255-19). An element t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on context. */ typedef i32 fe[10]; void fe_0(fe h); void fe_1(fe h); void fe_frombytes(fe h, const unsigned char *s); void fe_tobytes(unsigned char *s, const fe h); void fe_copy(fe h, const fe f); int fe_isnegative(const fe f); int fe_isnonzero(const fe f); void fe_cmov(fe f, const fe g, unsigned int b); void fe_cswap(fe f, fe g, unsigned int b); void fe_neg(fe h, const fe f); void fe_add(fe h, const fe f, const fe g); void fe_invert(fe out, const fe z); void fe_sq(fe h, const fe f); void fe_sq2(fe h, const fe f); void fe_mul(fe h, const fe f, const fe g); void fe_mul121666(fe h, fe f); void fe_pow22523(fe out, const fe z); void fe_sub(fe h, const fe f, const fe g); #endif libtorrent-rasterbar-1.1.13/ed25519/src/fixedint.h000066400000000000000000000001641351156116000215140ustar00rootroot00000000000000#include typedef boost::uint64_t u64; typedef boost::int64_t i64; typedef boost::int32_t i32; libtorrent-rasterbar-1.1.13/ed25519/src/ge.cpp000066400000000000000000000244411351156116000206340ustar00rootroot00000000000000// ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" #include "ge.h" #include "precomp_data.h" /* r = p + q */ void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { fe t0; fe_add(r->X, p->Y, p->X); fe_sub(r->Y, p->Y, p->X); fe_mul(r->Z, r->X, q->YplusX); fe_mul(r->Y, r->Y, q->YminusX); fe_mul(r->T, q->T2d, p->T); fe_mul(r->X, p->Z, q->Z); fe_add(t0, r->X, r->X); fe_sub(r->X, r->Z, r->Y); fe_add(r->Y, r->Z, r->Y); fe_add(r->Z, t0, r->T); fe_sub(r->T, t0, r->T); } static void slide(signed char *r, const unsigned char *a) { int i; int b; int k; for (i = 0; i < 256; ++i) { r[i] = 1 & (a[i >> 3] >> (i & 7)); } for (i = 0; i < 256; ++i) if (r[i]) { for (b = 1; b <= 6 && i + b < 256; ++b) { if (r[i + b]) { if (r[i] + (r[i + b] << b) <= 15) { r[i] += r[i + b] << b; r[i + b] = 0; } else if (r[i] - (r[i + b] << b) >= -15) { r[i] -= r[i + b] << b; for (k = i + b; k < 256; ++k) { if (!r[k]) { r[k] = 1; break; } r[k] = 0; } } else { break; } } } } } /* r = a * A + b * B where a = a[0]+256*a[1]+...+256^31 a[31]. and b = b[0]+256*b[1]+...+256^31 b[31]. B is the Ed25519 base point (x,4/5) with x positive. */ void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) { signed char aslide[256]; signed char bslide[256]; ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ ge_p1p1 t; ge_p3 u; ge_p3 A2; int i; slide(aslide, a); slide(bslide, b); ge_p3_to_cached(&Ai[0], A); ge_p3_dbl(&t, A); ge_p1p1_to_p3(&A2, &t); ge_add(&t, &A2, &Ai[0]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[1], &u); ge_add(&t, &A2, &Ai[1]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[2], &u); ge_add(&t, &A2, &Ai[2]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[3], &u); ge_add(&t, &A2, &Ai[3]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[4], &u); ge_add(&t, &A2, &Ai[4]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[5], &u); ge_add(&t, &A2, &Ai[5]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[6], &u); ge_add(&t, &A2, &Ai[6]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[7], &u); ge_p2_0(r); for (i = 255; i >= 0; --i) { if (aslide[i] || bslide[i]) { break; } } for (; i >= 0; --i) { ge_p2_dbl(&t, r); if (aslide[i] > 0) { ge_p1p1_to_p3(&u, &t); ge_add(&t, &u, &Ai[aslide[i] / 2]); } else if (aslide[i] < 0) { ge_p1p1_to_p3(&u, &t); ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); } if (bslide[i] > 0) { ge_p1p1_to_p3(&u, &t); ge_madd(&t, &u, &Bi[bslide[i] / 2]); } else if (bslide[i] < 0) { ge_p1p1_to_p3(&u, &t); ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]); } ge_p1p1_to_p2(r, &t); } } static const fe d = { -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116 }; static const fe sqrtm1 = { -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482 }; int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) { fe u; fe v; fe v3; fe vxx; fe check; fe_frombytes(h->Y, s); fe_1(h->Z); fe_sq(u, h->Y); fe_mul(v, u, d); fe_sub(u, u, h->Z); /* u = y^2-1 */ fe_add(v, v, h->Z); /* v = dy^2+1 */ fe_sq(v3, v); fe_mul(v3, v3, v); /* v3 = v^3 */ fe_sq(h->X, v3); fe_mul(h->X, h->X, v); fe_mul(h->X, h->X, u); /* x = uv^7 */ fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */ fe_mul(h->X, h->X, v3); fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */ fe_sq(vxx, h->X); fe_mul(vxx, vxx, v); fe_sub(check, vxx, u); /* vx^2-u */ if (fe_isnonzero(check)) { fe_add(check, vxx, u); /* vx^2+u */ if (fe_isnonzero(check)) { return -1; } fe_mul(h->X, h->X, sqrtm1); } if (fe_isnegative(h->X) == (s[31] >> 7)) { fe_neg(h->X, h->X); } fe_mul(h->T, h->X, h->Y); return 0; } /* r = p + q */ void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { fe t0; fe_add(r->X, p->Y, p->X); fe_sub(r->Y, p->Y, p->X); fe_mul(r->Z, r->X, q->yplusx); fe_mul(r->Y, r->Y, q->yminusx); fe_mul(r->T, q->xy2d, p->T); fe_add(t0, p->Z, p->Z); fe_sub(r->X, r->Z, r->Y); fe_add(r->Y, r->Z, r->Y); fe_add(r->Z, t0, r->T); fe_sub(r->T, t0, r->T); } /* r = p - q */ void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { fe t0; fe_add(r->X, p->Y, p->X); fe_sub(r->Y, p->Y, p->X); fe_mul(r->Z, r->X, q->yminusx); fe_mul(r->Y, r->Y, q->yplusx); fe_mul(r->T, q->xy2d, p->T); fe_add(t0, p->Z, p->Z); fe_sub(r->X, r->Z, r->Y); fe_add(r->Y, r->Z, r->Y); fe_sub(r->Z, t0, r->T); fe_add(r->T, t0, r->T); } /* r = p */ void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { fe_mul(r->X, p->X, p->T); fe_mul(r->Y, p->Y, p->Z); fe_mul(r->Z, p->Z, p->T); } /* r = p */ void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { fe_mul(r->X, p->X, p->T); fe_mul(r->Y, p->Y, p->Z); fe_mul(r->Z, p->Z, p->T); fe_mul(r->T, p->X, p->Y); } void ge_p2_0(ge_p2 *h) { fe_0(h->X); fe_1(h->Y); fe_1(h->Z); } /* r = 2 * p */ void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) { fe t0; fe_sq(r->X, p->X); fe_sq(r->Z, p->Y); fe_sq2(r->T, p->Z); fe_add(r->Y, p->X, p->Y); fe_sq(t0, r->Y); fe_add(r->Y, r->Z, r->X); fe_sub(r->Z, r->Z, r->X); fe_sub(r->X, t0, r->Y); fe_sub(r->T, r->T, r->Z); } void ge_p3_0(ge_p3 *h) { fe_0(h->X); fe_1(h->Y); fe_1(h->Z); fe_0(h->T); } /* r = 2 * p */ void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) { ge_p2 q; ge_p3_to_p2(&q, p); ge_p2_dbl(r, &q); } /* r = p */ static const fe d2 = { -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199 }; void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { fe_add(r->YplusX, p->Y, p->X); fe_sub(r->YminusX, p->Y, p->X); fe_copy(r->Z, p->Z); fe_mul(r->T2d, p->T, d2); } /* r = p */ void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) { fe_copy(r->X, p->X); fe_copy(r->Y, p->Y); fe_copy(r->Z, p->Z); } void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) { fe recip; fe x; fe y; fe_invert(recip, h->Z); fe_mul(x, h->X, recip); fe_mul(y, h->Y, recip); fe_tobytes(s, y); s[31] ^= fe_isnegative(x) << 7; } static unsigned char equal(signed char b, signed char c) { unsigned char ub = b; unsigned char uc = c; unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ u64 y = x; /* 0: yes; 1..255: no */ y -= 1; /* large: yes; 0..254: no */ y >>= 63; /* 1: yes; 0: no */ return (unsigned char) y; } static unsigned char negative(signed char b) { u64 x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ x >>= 63; /* 1: yes; 0: no */ return (unsigned char) x; } static void cmov(ge_precomp *t, ge_precomp *u, unsigned char b) { fe_cmov(t->yplusx, u->yplusx, b); fe_cmov(t->yminusx, u->yminusx, b); fe_cmov(t->xy2d, u->xy2d, b); } static void select(ge_precomp *t, int pos, signed char b) { typedef signed char schar; typedef unsigned char uchar; ge_precomp minust; unsigned char const bnegative = negative(b); unsigned char const babs = b - schar(uchar((-bnegative) & b) << 1); fe_1(t->yplusx); fe_1(t->yminusx); fe_0(t->xy2d); cmov(t, &base[pos][0], equal(babs, 1)); cmov(t, &base[pos][1], equal(babs, 2)); cmov(t, &base[pos][2], equal(babs, 3)); cmov(t, &base[pos][3], equal(babs, 4)); cmov(t, &base[pos][4], equal(babs, 5)); cmov(t, &base[pos][5], equal(babs, 6)); cmov(t, &base[pos][6], equal(babs, 7)); cmov(t, &base[pos][7], equal(babs, 8)); fe_copy(minust.yplusx, t->yminusx); fe_copy(minust.yminusx, t->yplusx); fe_neg(minust.xy2d, t->xy2d); cmov(t, &minust, bnegative); } /* h = a * B where a = a[0]+256*a[1]+...+256^31 a[31] B is the Ed25519 base point (x,4/5) with x positive. Preconditions: a[31] <= 127 */ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { signed char e[64]; signed char carry; ge_p1p1 r; ge_p2 s; ge_precomp t; int i; for (i = 0; i < 32; ++i) { e[2 * i + 0] = (a[i] >> 0) & 15; e[2 * i + 1] = (a[i] >> 4) & 15; } /* each e[i] is between 0 and 15 */ /* e[63] is between 0 and 7 */ carry = 0; for (i = 0; i < 63; ++i) { e[i] += carry; carry = e[i] + 8; carry >>= 4; e[i] -= carry << 4; } e[63] += carry; /* each e[i] is between -8 and 8 */ ge_p3_0(h); for (i = 1; i < 64; i += 2) { select(&t, i / 2, e[i]); ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); } ge_p3_dbl(&r, h); ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); ge_p1p1_to_p3(h, &r); for (i = 0; i < 64; i += 2) { select(&t, i / 2, e[i]); ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); } } /* r = p - q */ void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { fe t0; fe_add(r->X, p->Y, p->X); fe_sub(r->Y, p->Y, p->X); fe_mul(r->Z, r->X, q->YminusX); fe_mul(r->Y, r->Y, q->YplusX); fe_mul(r->T, q->T2d, p->T); fe_mul(r->X, p->Z, q->Z); fe_add(t0, r->X, r->X); fe_sub(r->X, r->Z, r->Y); fe_add(r->Y, r->Z, r->Y); fe_sub(r->Z, t0, r->T); fe_add(r->T, t0, r->T); } void ge_tobytes(unsigned char *s, const ge_p2 *h) { fe recip; fe x; fe y; fe_invert(recip, h->Z); fe_mul(x, h->X, recip); fe_mul(y, h->Y, recip); fe_tobytes(s, y); s[31] ^= fe_isnegative(x) << 7; } libtorrent-rasterbar-1.1.13/ed25519/src/ge.h000066400000000000000000000032231351156116000202740ustar00rootroot00000000000000#ifndef GE_H #define GE_H #include "fe.h" /* ge means group element. Here the group is the set of pairs (x,y) of field elements (see fe.h) satisfying -x^2 + y^2 = 1 + d x^2y^2 where d = -121665/121666. Representations: ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T ge_precomp (Duif): (y+x,y-x,2dxy) */ typedef struct { fe X; fe Y; fe Z; } ge_p2; typedef struct { fe X; fe Y; fe Z; fe T; } ge_p3; typedef struct { fe X; fe Y; fe Z; fe T; } ge_p1p1; typedef struct { fe yplusx; fe yminusx; fe xy2d; } ge_precomp; typedef struct { fe YplusX; fe YminusX; fe Z; fe T2d; } ge_cached; void ge_p3_tobytes(unsigned char *s, const ge_p3 *h); void ge_tobytes(unsigned char *s, const ge_p2 *h); int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s); void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b); void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); void ge_scalarmult_base(ge_p3 *h, const unsigned char *a); void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p); void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p); void ge_p2_0(ge_p2 *h); void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p); void ge_p3_0(ge_p3 *h); void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p); void ge_p3_to_cached(ge_cached *r, const ge_p3 *p); void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p); #endif libtorrent-rasterbar-1.1.13/ed25519/src/key_exchange.cpp000066400000000000000000000035761351156116000227010ustar00rootroot00000000000000// ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" #include "libtorrent/ed25519.hpp" #include "fe.h" void ed25519_key_exchange(unsigned char *shared_secret , const unsigned char *public_key, const unsigned char *private_key) { unsigned char e[32]; unsigned int i; fe x1; fe x2; fe z2; fe x3; fe z3; fe tmp0; fe tmp1; int pos; unsigned int swap; unsigned int b; /* copy the private key and make sure it's valid */ for (i = 0; i < 32; ++i) { e[i] = private_key[i]; } e[0] &= 248; e[31] &= 63; e[31] |= 64; /* unpack the public key and convert edwards to montgomery */ /* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */ fe_frombytes(x1, public_key); fe_1(tmp1); fe_add(tmp0, x1, tmp1); fe_sub(tmp1, tmp1, x1); fe_invert(tmp1, tmp1); fe_mul(x1, tmp0, tmp1); fe_1(x2); fe_0(z2); fe_copy(x3, x1); fe_1(z3); swap = 0; for (pos = 254; pos >= 0; --pos) { b = e[pos / 8] >> (pos & 7); b &= 1; swap ^= b; fe_cswap(x2, x3, swap); fe_cswap(z2, z3, swap); swap = b; /* from montgomery.h */ fe_sub(tmp0, x3, z3); fe_sub(tmp1, x2, z2); fe_add(x2, x2, z2); fe_add(z2, x3, z3); fe_mul(z3, tmp0, x2); fe_mul(z2, z2, tmp1); fe_sq(tmp0, tmp1); fe_sq(tmp1, x2); fe_add(x3, z3, z2); fe_sub(z2, z3, z2); fe_mul(x2, tmp1, tmp0); fe_sub(tmp1, tmp1, tmp0); fe_sq(z2, z2); fe_mul121666(z3, tmp1); fe_sq(x3, x3); fe_add(tmp0, tmp0, z3); fe_mul(z3, x1, z2); fe_mul(z2, tmp1, tmp0); } fe_cswap(x2, x3, swap); fe_cswap(z2, z3, swap); fe_invert(z2, z2); fe_mul(x2, x2, z2); fe_tobytes(shared_secret, x2); } libtorrent-rasterbar-1.1.13/ed25519/src/keypair.cpp000066400000000000000000000007371351156116000217070ustar00rootroot00000000000000// ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" #include "libtorrent/ed25519.hpp" #include "sha512.h" #include "ge.h" void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) { ge_p3 A; sha512(seed, 32, private_key); private_key[0] &= 248; private_key[31] &= 63; private_key[31] |= 64; ge_scalarmult_base(&A, private_key); ge_p3_tobytes(public_key, &A); } libtorrent-rasterbar-1.1.13/ed25519/src/precomp_data.h000066400000000000000000002767751351156116000223670ustar00rootroot00000000000000static ge_precomp Bi[8] = { { { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, }, { { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, }, { { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, }, { { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, }, { { -22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877 }, { -6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951 }, { 4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784 }, }, { { -25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436 }, { 25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918 }, { 23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877 }, }, { { -33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800 }, { -25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305 }, { -13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300 }, }, { { -3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876 }, { -24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619 }, { -3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683 }, }, }; /* base[i][j] = (j+1)*256^i*B */ static ge_precomp base[32][8] = { { { { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, }, { { -12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303 }, { -21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081 }, { 26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697 }, }, { { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, }, { { -17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540 }, { 23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397 }, { 7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325 }, }, { { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, }, { { -15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777 }, { -8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737 }, { -18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652 }, }, { { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, }, { { 14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726 }, { -7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955 }, { 27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425 }, }, }, { { { -13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171 }, { 27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510 }, { 17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660 }, }, { { -10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639 }, { 29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963 }, { 5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950 }, }, { { -27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568 }, { 12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335 }, { 25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628 }, }, { { -26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007 }, { -2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772 }, { -22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653 }, }, { { 2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567 }, { 13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686 }, { 21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372 }, }, { { -13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887 }, { -23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954 }, { -29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953 }, }, { { 24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833 }, { -16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532 }, { -22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876 }, }, { { 2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268 }, { 33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214 }, { 1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038 }, }, }, { { { 6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800 }, { 4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645 }, { -4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664 }, }, { { 1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933 }, { -25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182 }, { -17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222 }, }, { { -18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991 }, { 20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880 }, { 9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092 }, }, { { -16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295 }, { 19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788 }, { 8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553 }, }, { { -15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026 }, { 11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347 }, { -18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033 }, }, { { -23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395 }, { -27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278 }, { 1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890 }, }, { { 32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995 }, { -30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596 }, { -11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891 }, }, { { 31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060 }, { 11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608 }, { -20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606 }, }, }, { { { 7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389 }, { -19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016 }, { -11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341 }, }, { { -22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505 }, { 14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553 }, { -28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655 }, }, { { 15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220 }, { 12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631 }, { -4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099 }, }, { { 26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556 }, { 14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749 }, { 236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930 }, }, { { 1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391 }, { 5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253 }, { 20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066 }, }, { { 24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958 }, { -11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082 }, { -28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383 }, }, { { -30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521 }, { -11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807 }, { 23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948 }, }, { { 9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134 }, { -32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455 }, { 27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629 }, }, }, { { { -8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069 }, { -32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746 }, { 24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919 }, }, { { 11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837 }, { 8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906 }, { -28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771 }, }, { { -25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817 }, { 10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098 }, { 10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409 }, }, { { -12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504 }, { -26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727 }, { 28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420 }, }, { { -32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003 }, { -1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605 }, { -30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384 }, }, { { -26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701 }, { -23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683 }, { 29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708 }, }, { { -3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563 }, { -19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260 }, { -5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387 }, }, { { -19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672 }, { 23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686 }, { -24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665 }, }, }, { { { 11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182 }, { -31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277 }, { 14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628 }, }, { { -4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474 }, { -26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539 }, { -25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822 }, }, { { -10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970 }, { 19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756 }, { -24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508 }, }, { { -26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683 }, { -10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655 }, { -20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158 }, }, { { -4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125 }, { -15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839 }, { -20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664 }, }, { { 27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294 }, { -18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899 }, { -11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070 }, }, { { 3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294 }, { -15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949 }, { -21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083 }, }, { { 31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420 }, { -5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940 }, { 29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396 }, }, }, { { { -12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567 }, { 20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127 }, { -16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294 }, }, { { -12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887 }, { 22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964 }, { 16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195 }, }, { { 9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244 }, { 24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999 }, { -1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762 }, }, { { -18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274 }, { -33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236 }, { -16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605 }, }, { { -13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761 }, { -22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884 }, { -6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482 }, }, { { -24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638 }, { -11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490 }, { -32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170 }, }, { { 5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736 }, { 10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124 }, { -17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392 }, }, { { 8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029 }, { 6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048 }, { 28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958 }, }, }, { { { 24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593 }, { 26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071 }, { -11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692 }, }, { { 11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687 }, { -160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441 }, { -20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001 }, }, { { -938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460 }, { -19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007 }, { -21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762 }, }, { { 15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005 }, { -9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674 }, { 4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035 }, }, { { 7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590 }, { -2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957 }, { -30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812 }, }, { { 33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740 }, { -18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122 }, { -27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158 }, }, { { 8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885 }, { 26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140 }, { 19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857 }, }, { { 801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155 }, { 19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260 }, { 19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483 }, }, }, { { { -3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677 }, { 32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815 }, { 22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751 }, }, { { -16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203 }, { -11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208 }, { 1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230 }, }, { { 16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850 }, { -21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389 }, { -9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968 }, }, { { -11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689 }, { 14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880 }, { 5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304 }, }, { { 30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632 }, { -3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412 }, { 20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566 }, }, { { -20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038 }, { -26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232 }, { -1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943 }, }, { { 17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856 }, { 23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738 }, { 15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971 }, }, { { -27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718 }, { -13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697 }, { -11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883 }, }, }, { { { 5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912 }, { -26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358 }, { 3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849 }, }, { { 29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307 }, { -14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977 }, { -6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335 }, }, { { -29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644 }, { -22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616 }, { -27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735 }, }, { { -21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099 }, { 29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341 }, { -936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336 }, }, { { -23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646 }, { 31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425 }, { -17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388 }, }, { { -31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743 }, { -16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822 }, { -8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462 }, }, { { 18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985 }, { 9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702 }, { -22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797 }, }, { { 21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293 }, { 27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100 }, { 19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688 }, }, }, { { { 12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186 }, { 2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610 }, { -2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707 }, }, { { 7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220 }, { 915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025 }, { 32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044 }, }, { { 32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992 }, { -4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027 }, { 21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197 }, }, { { 8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901 }, { 31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952 }, { 19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878 }, }, { { -28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390 }, { 32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730 }, { 2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730 }, }, { { -19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180 }, { -30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272 }, { -15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715 }, }, { { -22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970 }, { -31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772 }, { -17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865 }, }, { { 15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750 }, { 20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373 }, { 32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348 }, }, }, { { { 9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144 }, { -22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195 }, { 5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086 }, }, { { -13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684 }, { -8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518 }, { -2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233 }, }, { { -5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793 }, { -2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794 }, { 580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435 }, }, { { 23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921 }, { 13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518 }, { 2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563 }, }, { { 14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278 }, { -27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024 }, { 4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030 }, }, { { 10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783 }, { 27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717 }, { 6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844 }, }, { { 14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333 }, { 16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048 }, { 22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760 }, }, { { -4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760 }, { -15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757 }, { -2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112 }, }, }, { { { -19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468 }, { 3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184 }, { 10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289 }, }, { { 15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066 }, { 24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882 }, { 13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226 }, }, { { 16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101 }, { 29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279 }, { -6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811 }, }, { { 27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709 }, { 20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714 }, { -2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121 }, }, { { 9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464 }, { 12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847 }, { 13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400 }, }, { { 4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414 }, { -15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158 }, { 17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045 }, }, { { -461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415 }, { -5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459 }, { -31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079 }, }, { { 21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412 }, { -20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743 }, { -14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836 }, }, }, { { { 12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022 }, { 18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429 }, { -6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065 }, }, { { 30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861 }, { 10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000 }, { -33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101 }, }, { { 32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815 }, { 29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642 }, { 10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966 }, }, { { 25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574 }, { -21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742 }, { -18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689 }, }, { { 12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020 }, { -10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772 }, { 3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982 }, }, { { -14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953 }, { -16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218 }, { -17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265 }, }, { { 29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073 }, { -3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325 }, { -11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798 }, }, { { -4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870 }, { -7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863 }, { -13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927 }, }, }, { { { -2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267 }, { -9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663 }, { 22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862 }, }, { { -25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673 }, { 15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943 }, { 15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020 }, }, { { -4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238 }, { 11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064 }, { 14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795 }, }, { { 15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052 }, { -10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904 }, { 29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531 }, }, { { -13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979 }, { -5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841 }, { 10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431 }, }, { { 10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324 }, { -31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940 }, { 10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320 }, }, { { -15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184 }, { 14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114 }, { 30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878 }, }, { { 12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784 }, { -2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091 }, { -16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585 }, }, }, { { { -8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208 }, { 10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864 }, { 17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661 }, }, { { 7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233 }, { 26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212 }, { -12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525 }, }, { { -24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068 }, { 9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397 }, { -8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988 }, }, { { 5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889 }, { 32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038 }, { 14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697 }, }, { { 20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875 }, { -25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905 }, { -25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656 }, }, { { 11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818 }, { 27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714 }, { 10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203 }, }, { { 20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931 }, { -30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024 }, { -23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084 }, }, { { -1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204 }, { 20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817 }, { 27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667 }, }, }, { { { 11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504 }, { -12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768 }, { -19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255 }, }, { { 6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790 }, { 1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438 }, { -22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333 }, }, { { 17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971 }, { 31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905 }, { 29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409 }, }, { { 12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409 }, { 6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499 }, { -8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363 }, }, { { 28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664 }, { -11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324 }, { -21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940 }, }, { { 13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990 }, { -17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914 }, { -25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290 }, }, { { 24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257 }, { -6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433 }, { -16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236 }, }, { { -12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045 }, { 11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093 }, { -1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347 }, }, }, { { { -28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191 }, { -15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507 }, { -12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906 }, }, { { 3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018 }, { -16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109 }, { -23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926 }, }, { { -24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528 }, { 8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625 }, { -32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286 }, }, { { 2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033 }, { 27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866 }, { 21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896 }, }, { { 30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075 }, { 26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347 }, { -22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437 }, }, { { -5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165 }, { -18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588 }, { -32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193 }, }, { { -19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017 }, { -28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883 }, { 21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961 }, }, { { 8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043 }, { 29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663 }, { -20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362 }, }, }, { { { -33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860 }, { 2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466 }, { -24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063 }, }, { { -26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997 }, { -1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295 }, { -13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369 }, }, { { 9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385 }, { 18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109 }, { 2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906 }, }, { { 4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424 }, { -19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185 }, { 7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962 }, }, { { -7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325 }, { 10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593 }, { 696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404 }, }, { { -11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644 }, { 17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801 }, { 26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804 }, }, { { -31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884 }, { -586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577 }, { -9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849 }, }, { { 32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473 }, { -8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644 }, { -2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319 }, }, }, { { { -11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599 }, { -9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768 }, { -27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084 }, }, { { -27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328 }, { -15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369 }, { 20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920 }, }, { { 12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815 }, { -32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025 }, { -21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397 }, }, { { -20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448 }, { 6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981 }, { 30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165 }, }, { { 32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501 }, { 17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073 }, { -1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861 }, }, { { 14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845 }, { -1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211 }, { 18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870 }, }, { { 10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096 }, { 33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803 }, { -32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168 }, }, { { 30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965 }, { -14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505 }, { 18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598 }, }, }, { { { 5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782 }, { 5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900 }, { -31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479 }, }, { { -12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208 }, { 8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232 }, { 17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719 }, }, { { 16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271 }, { -4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326 }, { -8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132 }, }, { { 14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300 }, { 8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570 }, { 15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670 }, }, { { -2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994 }, { -12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913 }, { 31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317 }, }, { { -25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730 }, { 842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096 }, { -4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078 }, }, { { -15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411 }, { -19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905 }, { -9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654 }, }, { { -28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870 }, { -23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498 }, { 12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579 }, }, }, { { { 14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677 }, { 10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647 }, { -2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743 }, }, { { -25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468 }, { 21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375 }, { -25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155 }, }, { { 6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725 }, { -12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612 }, { -10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943 }, }, { { -30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944 }, { 30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928 }, { 9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406 }, }, { { 22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139 }, { -8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963 }, { -31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693 }, }, { { 1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734 }, { -448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680 }, { -24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410 }, }, { { -9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931 }, { -16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654 }, { 22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710 }, }, { { 29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180 }, { -26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684 }, { -10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895 }, }, }, { { { 22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501 }, { -11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413 }, { 6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880 }, }, { { -8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874 }, { 22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962 }, { -7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899 }, }, { { 21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152 }, { 9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063 }, { 7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080 }, }, { { -9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146 }, { -17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183 }, { -19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133 }, }, { { -32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421 }, { -3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622 }, { -4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197 }, }, { { 2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663 }, { 31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753 }, { 4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755 }, }, { { -9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862 }, { -26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118 }, { 26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171 }, }, { { 15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380 }, { 16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824 }, { 28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270 }, }, }, { { { -817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438 }, { -31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584 }, { -594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562 }, }, { { 30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471 }, { 18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610 }, { 19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269 }, }, { { -30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650 }, { 14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369 }, { 19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461 }, }, { { 30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462 }, { -5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793 }, { -2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218 }, }, { { -24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226 }, { 18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019 }, { -15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037 }, }, { { 31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171 }, { -17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132 }, { -28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841 }, }, { { 21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181 }, { -33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210 }, { -1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040 }, }, { { 3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935 }, { 24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105 }, { -28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814 }, }, }, { { { 793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852 }, { 5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581 }, { -4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646 }, }, { { 10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844 }, { 10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025 }, { 27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453 }, }, { { -23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068 }, { 4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192 }, { -17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921 }, }, { { -9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259 }, { -12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426 }, { -5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072 }, }, { { -17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305 }, { 13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832 }, { 28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943 }, }, { { -16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011 }, { 24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447 }, { 17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494 }, }, { { -28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245 }, { -20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859 }, { 28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915 }, }, { { 16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707 }, { 10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848 }, { -11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224 }, }, }, { { { -25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391 }, { 15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215 }, { -23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101 }, }, { { 23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713 }, { 21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849 }, { -7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930 }, }, { { -29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940 }, { -21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031 }, { -17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404 }, }, { { -25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243 }, { -23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116 }, { -24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525 }, }, { { -23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509 }, { -10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883 }, { 15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865 }, }, { { -3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660 }, { 4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273 }, { -28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138 }, }, { { -25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560 }, { -10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135 }, { 2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941 }, }, { { -4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739 }, { 18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756 }, { -30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819 }, }, }, { { { -6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347 }, { -27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028 }, { 21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075 }, }, { { 16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799 }, { -2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609 }, { -25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817 }, }, { { -23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989 }, { -30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523 }, { 4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278 }, }, { { 31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045 }, { 19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377 }, { 24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480 }, }, { { 17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016 }, { 510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426 }, { 18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525 }, }, { { 13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396 }, { 9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080 }, { 12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892 }, }, { { 15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275 }, { 11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074 }, { 20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140 }, }, { { -16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717 }, { -1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101 }, { 24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127 }, }, }, { { { -12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632 }, { -26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415 }, { -31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160 }, }, { { 31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876 }, { 22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625 }, { -15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478 }, }, { { 27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164 }, { 26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595 }, { -7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248 }, }, { { -16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858 }, { 15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193 }, { 8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184 }, }, { { -18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942 }, { -1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635 }, { 21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948 }, }, { { 11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935 }, { -25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415 }, { -15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416 }, }, { { -7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018 }, { 4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778 }, { 366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659 }, }, { { -24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385 }, { 18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503 }, { 476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329 }, }, }, { { { 20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056 }, { -13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838 }, { 24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948 }, }, { { -3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691 }, { -15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118 }, { -23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517 }, }, { { -20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269 }, { -6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904 }, { -23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589 }, }, { { -28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193 }, { -7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910 }, { -30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930 }, }, { { -7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667 }, { 25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481 }, { -9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876 }, }, { { 22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640 }, { -8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278 }, { -21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112 }, }, { { 26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272 }, { 17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012 }, { -10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221 }, }, { { 30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046 }, { 13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345 }, { -19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310 }, }, }, { { { 19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937 }, { 31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636 }, { -9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008 }, }, { { -2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429 }, { -15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576 }, { 31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066 }, }, { { -9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490 }, { -12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104 }, { 33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053 }, }, { { 31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275 }, { -20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511 }, { 22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095 }, }, { { -28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439 }, { 23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939 }, { -23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424 }, }, { { 2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310 }, { 3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608 }, { -32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079 }, }, { { -23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101 }, { 21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418 }, { 18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576 }, }, { { 30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356 }, { 9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996 }, { -26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099 }, }, }, { { { -26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728 }, { -13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658 }, { -10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242 }, }, { { -21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001 }, { -4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766 }, { 18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373 }, }, { { 26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458 }, { -17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628 }, { -13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657 }, }, { { -23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062 }, { 25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616 }, { 31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014 }, }, { { 24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383 }, { -25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814 }, { -20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718 }, }, { { 30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417 }, { 2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222 }, { 33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444 }, }, { { -20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597 }, { 23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970 }, { 1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799 }, }, { { -5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647 }, { 13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511 }, { -29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032 }, }, }, { { { 9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834 }, { -23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461 }, { 29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062 }, }, { { -25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516 }, { -20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547 }, { -24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240 }, }, { { -17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038 }, { -33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741 }, { 16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103 }, }, { { -19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747 }, { -1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323 }, { 31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016 }, }, { { -14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373 }, { 15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228 }, { -2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141 }, }, { { 16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399 }, { 11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831 }, { -185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376 }, }, { { -32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313 }, { -18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958 }, { -6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577 }, }, { { -22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743 }, { 29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684 }, { -20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476 }, }, }, }; libtorrent-rasterbar-1.1.13/ed25519/src/sc.cpp000066400000000000000000000572121351156116000206500ustar00rootroot00000000000000// ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" #include "fixedint.h" #include "sc.h" static u64 load_3(const unsigned char *in) { u64 result; result = (u64) in[0]; result |= ((u64) in[1]) << 8; result |= ((u64) in[2]) << 16; return result; } static u64 load_4(const unsigned char *in) { u64 result; result = (u64) in[0]; result |= ((u64) in[1]) << 8; result |= ((u64) in[2]) << 16; result |= ((u64) in[3]) << 24; return result; } static inline i64 shift_left(i64 v, int s) { return i64(u64(v) << s); } /* Input: s[0]+256*s[1]+...+256^63*s[63] = s Output: s[0]+256*s[1]+...+256^31*s[31] = s mod l where l = 2^252 + 27742317777372353535851937790883648493. Overwrites s in place. */ void sc_reduce(unsigned char *s) { i64 s0 = 2097151 & load_3(s); i64 s1 = 2097151 & (load_4(s + 2) >> 5); i64 s2 = 2097151 & (load_3(s + 5) >> 2); i64 s3 = 2097151 & (load_4(s + 7) >> 7); i64 s4 = 2097151 & (load_4(s + 10) >> 4); i64 s5 = 2097151 & (load_3(s + 13) >> 1); i64 s6 = 2097151 & (load_4(s + 15) >> 6); i64 s7 = 2097151 & (load_3(s + 18) >> 3); i64 s8 = 2097151 & load_3(s + 21); i64 s9 = 2097151 & (load_4(s + 23) >> 5); i64 s10 = 2097151 & (load_3(s + 26) >> 2); i64 s11 = 2097151 & (load_4(s + 28) >> 7); i64 s12 = 2097151 & (load_4(s + 31) >> 4); i64 s13 = 2097151 & (load_3(s + 34) >> 1); i64 s14 = 2097151 & (load_4(s + 36) >> 6); i64 s15 = 2097151 & (load_3(s + 39) >> 3); i64 s16 = 2097151 & load_3(s + 42); i64 s17 = 2097151 & (load_4(s + 44) >> 5); i64 s18 = 2097151 & (load_3(s + 47) >> 2); i64 s19 = 2097151 & (load_4(s + 49) >> 7); i64 s20 = 2097151 & (load_4(s + 52) >> 4); i64 s21 = 2097151 & (load_3(s + 55) >> 1); i64 s22 = 2097151 & (load_4(s + 57) >> 6); i64 s23 = (load_4(s + 60) >> 3); i64 carry0; i64 carry1; i64 carry2; i64 carry3; i64 carry4; i64 carry5; i64 carry6; i64 carry7; i64 carry8; i64 carry9; i64 carry10; i64 carry11; i64 carry12; i64 carry13; i64 carry14; i64 carry15; i64 carry16; s11 += s23 * 666643; s12 += s23 * 470296; s13 += s23 * 654183; s14 -= s23 * 997805; s15 += s23 * 136657; s16 -= s23 * 683901; s23 = 0; s10 += s22 * 666643; s11 += s22 * 470296; s12 += s22 * 654183; s13 -= s22 * 997805; s14 += s22 * 136657; s15 -= s22 * 683901; s22 = 0; s9 += s21 * 666643; s10 += s21 * 470296; s11 += s21 * 654183; s12 -= s21 * 997805; s13 += s21 * 136657; s14 -= s21 * 683901; s21 = 0; s8 += s20 * 666643; s9 += s20 * 470296; s10 += s20 * 654183; s11 -= s20 * 997805; s12 += s20 * 136657; s13 -= s20 * 683901; s20 = 0; s7 += s19 * 666643; s8 += s19 * 470296; s9 += s19 * 654183; s10 -= s19 * 997805; s11 += s19 * 136657; s12 -= s19 * 683901; s19 = 0; s6 += s18 * 666643; s7 += s18 * 470296; s8 += s18 * 654183; s9 -= s18 * 997805; s10 += s18 * 136657; s11 -= s18 * 683901; s18 = 0; carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= shift_left(carry6, 21); carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= shift_left(carry8, 21); carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= shift_left(carry10, 21); carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= shift_left(carry12, 21); carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= shift_left(carry14, 21); carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= shift_left(carry16, 21); carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= shift_left(carry7, 21); carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= shift_left(carry9, 21); carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= shift_left(carry11, 21); carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= shift_left(carry13, 21); carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= shift_left(carry15, 21); s5 += s17 * 666643; s6 += s17 * 470296; s7 += s17 * 654183; s8 -= s17 * 997805; s9 += s17 * 136657; s10 -= s17 * 683901; s17 = 0; s4 += s16 * 666643; s5 += s16 * 470296; s6 += s16 * 654183; s7 -= s16 * 997805; s8 += s16 * 136657; s9 -= s16 * 683901; s16 = 0; s3 += s15 * 666643; s4 += s15 * 470296; s5 += s15 * 654183; s6 -= s15 * 997805; s7 += s15 * 136657; s8 -= s15 * 683901; s15 = 0; s2 += s14 * 666643; s3 += s14 * 470296; s4 += s14 * 654183; s5 -= s14 * 997805; s6 += s14 * 136657; s7 -= s14 * 683901; s14 = 0; s1 += s13 * 666643; s2 += s13 * 470296; s3 += s13 * 654183; s4 -= s13 * 997805; s5 += s13 * 136657; s6 -= s13 * 683901; s13 = 0; s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= shift_left(carry0, 21); carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= shift_left(carry2, 21); carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= shift_left(carry4, 21); carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= shift_left(carry6, 21); carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= shift_left(carry8, 21); carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= shift_left(carry10, 21); carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= shift_left(carry1, 21); carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= shift_left(carry3, 21); carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= shift_left(carry5, 21); carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= shift_left(carry7, 21); carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= shift_left(carry9, 21); carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= shift_left(carry11, 21); s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= shift_left(carry0, 21); carry1 = s1 >> 21; s2 += carry1; s1 -= shift_left(carry1, 21); carry2 = s2 >> 21; s3 += carry2; s2 -= shift_left(carry2, 21); carry3 = s3 >> 21; s4 += carry3; s3 -= shift_left(carry3, 21); carry4 = s4 >> 21; s5 += carry4; s4 -= shift_left(carry4, 21); carry5 = s5 >> 21; s6 += carry5; s5 -= shift_left(carry5, 21); carry6 = s6 >> 21; s7 += carry6; s6 -= shift_left(carry6, 21); carry7 = s7 >> 21; s8 += carry7; s7 -= shift_left(carry7, 21); carry8 = s8 >> 21; s9 += carry8; s8 -= shift_left(carry8, 21); carry9 = s9 >> 21; s10 += carry9; s9 -= shift_left(carry9, 21); carry10 = s10 >> 21; s11 += carry10; s10 -= shift_left(carry10, 21); carry11 = s11 >> 21; s12 += carry11; s11 -= shift_left(carry11, 21); s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= shift_left(carry0, 21); carry1 = s1 >> 21; s2 += carry1; s1 -= shift_left(carry1, 21); carry2 = s2 >> 21; s3 += carry2; s2 -= shift_left(carry2, 21); carry3 = s3 >> 21; s4 += carry3; s3 -= shift_left(carry3, 21); carry4 = s4 >> 21; s5 += carry4; s4 -= shift_left(carry4, 21); carry5 = s5 >> 21; s6 += carry5; s5 -= shift_left(carry5, 21); carry6 = s6 >> 21; s7 += carry6; s6 -= shift_left(carry6, 21); carry7 = s7 >> 21; s8 += carry7; s7 -= shift_left(carry7, 21); carry8 = s8 >> 21; s9 += carry8; s8 -= shift_left(carry8, 21); carry9 = s9 >> 21; s10 += carry9; s9 -= shift_left(carry9, 21); carry10 = s10 >> 21; s11 += carry10; s10 -= shift_left(carry10, 21); s[0] = (unsigned char) ((s0 >> 0) & 0xff); s[1] = (unsigned char) ((s0 >> 8) & 0xff); s[2] = (unsigned char) (((s0 >> 16) | (s1 << 5)) & 0xff); s[3] = (unsigned char) ((s1 >> 3) & 0xff); s[4] = (unsigned char) ((s1 >> 11) & 0xff); s[5] = (unsigned char) (((s1 >> 19) | (s2 << 2)) & 0xff); s[6] = (unsigned char) ((s2 >> 6) & 0xff); s[7] = (unsigned char) (((s2 >> 14) | (s3 << 7)) & 0xff); s[8] = (unsigned char) ((s3 >> 1) & 0xff); s[9] = (unsigned char) ((s3 >> 9) & 0xff); s[10] = (unsigned char) (((s3 >> 17) | (s4 << 4)) & 0xff); s[11] = (unsigned char) ((s4 >> 4) & 0xff); s[12] = (unsigned char) ((s4 >> 12) & 0xff); s[13] = (unsigned char) (((s4 >> 20) | (s5 << 1)) & 0xff); s[14] = (unsigned char) ((s5 >> 7) & 0xff); s[15] = (unsigned char) (((s5 >> 15) | (s6 << 6)) & 0xff); s[16] = (unsigned char) ((s6 >> 2) & 0xff); s[17] = (unsigned char) ((s6 >> 10) & 0xff); s[18] = (unsigned char) (((s6 >> 18) | (s7 << 3)) & 0xff); s[19] = (unsigned char) ((s7 >> 5) & 0xff); s[20] = (unsigned char) ((s7 >> 13) & 0xff); s[21] = (unsigned char) ((s8 >> 0) & 0xff); s[22] = (unsigned char) ((s8 >> 8) & 0xff); s[23] = (unsigned char) (((s8 >> 16) | (s9 << 5)) & 0xff); s[24] = (unsigned char) ((s9 >> 3) & 0xff); s[25] = (unsigned char) ((s9 >> 11) & 0xff); s[26] = (unsigned char) (((s9 >> 19) | (s10 << 2)) & 0xff); s[27] = (unsigned char) ((s10 >> 6) & 0xff); s[28] = (unsigned char) (((s10 >> 14) | (s11 << 7)) & 0xff); s[29] = (unsigned char) ((s11 >> 1) & 0xff); s[30] = (unsigned char) ((s11 >> 9) & 0xff); s[31] = (unsigned char) ((s11 >> 17) & 0xff); } /* Input: a[0]+256*a[1]+...+256^31*a[31] = a b[0]+256*b[1]+...+256^31*b[31] = b c[0]+256*c[1]+...+256^31*c[31] = c Output: s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l where l = 2^252 + 27742317777372353535851937790883648493. */ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) { i64 a0 = 2097151 & load_3(a); i64 a1 = 2097151 & (load_4(a + 2) >> 5); i64 a2 = 2097151 & (load_3(a + 5) >> 2); i64 a3 = 2097151 & (load_4(a + 7) >> 7); i64 a4 = 2097151 & (load_4(a + 10) >> 4); i64 a5 = 2097151 & (load_3(a + 13) >> 1); i64 a6 = 2097151 & (load_4(a + 15) >> 6); i64 a7 = 2097151 & (load_3(a + 18) >> 3); i64 a8 = 2097151 & load_3(a + 21); i64 a9 = 2097151 & (load_4(a + 23) >> 5); i64 a10 = 2097151 & (load_3(a + 26) >> 2); i64 a11 = (load_4(a + 28) >> 7); i64 b0 = 2097151 & load_3(b); i64 b1 = 2097151 & (load_4(b + 2) >> 5); i64 b2 = 2097151 & (load_3(b + 5) >> 2); i64 b3 = 2097151 & (load_4(b + 7) >> 7); i64 b4 = 2097151 & (load_4(b + 10) >> 4); i64 b5 = 2097151 & (load_3(b + 13) >> 1); i64 b6 = 2097151 & (load_4(b + 15) >> 6); i64 b7 = 2097151 & (load_3(b + 18) >> 3); i64 b8 = 2097151 & load_3(b + 21); i64 b9 = 2097151 & (load_4(b + 23) >> 5); i64 b10 = 2097151 & (load_3(b + 26) >> 2); i64 b11 = (load_4(b + 28) >> 7); i64 c0 = 2097151 & load_3(c); i64 c1 = 2097151 & (load_4(c + 2) >> 5); i64 c2 = 2097151 & (load_3(c + 5) >> 2); i64 c3 = 2097151 & (load_4(c + 7) >> 7); i64 c4 = 2097151 & (load_4(c + 10) >> 4); i64 c5 = 2097151 & (load_3(c + 13) >> 1); i64 c6 = 2097151 & (load_4(c + 15) >> 6); i64 c7 = 2097151 & (load_3(c + 18) >> 3); i64 c8 = 2097151 & load_3(c + 21); i64 c9 = 2097151 & (load_4(c + 23) >> 5); i64 c10 = 2097151 & (load_3(c + 26) >> 2); i64 c11 = (load_4(c + 28) >> 7); i64 s0; i64 s1; i64 s2; i64 s3; i64 s4; i64 s5; i64 s6; i64 s7; i64 s8; i64 s9; i64 s10; i64 s11; i64 s12; i64 s13; i64 s14; i64 s15; i64 s16; i64 s17; i64 s18; i64 s19; i64 s20; i64 s21; i64 s22; i64 s23; i64 carry0; i64 carry1; i64 carry2; i64 carry3; i64 carry4; i64 carry5; i64 carry6; i64 carry7; i64 carry8; i64 carry9; i64 carry10; i64 carry11; i64 carry12; i64 carry13; i64 carry14; i64 carry15; i64 carry16; i64 carry17; i64 carry18; i64 carry19; i64 carry20; i64 carry21; i64 carry22; s0 = c0 + a0 * b0; s1 = c1 + a0 * b1 + a1 * b0; s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0; s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0; s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0; s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0; s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0; s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0; s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1; s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2; s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3; s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4; s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; s20 = a9 * b11 + a10 * b10 + a11 * b9; s21 = a10 * b11 + a11 * b10; s22 = a11 * b11; s23 = 0; carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= shift_left(carry0, 21); carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= shift_left(carry2, 21); carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= shift_left(carry4, 21); carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= shift_left(carry6, 21); carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= shift_left(carry8, 21); carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= shift_left(carry10, 21); carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= shift_left(carry12, 21); carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= shift_left(carry14, 21); carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= shift_left(carry16, 21); carry18 = (s18 + (1 << 20)) >> 21; s19 += carry18; s18 -= shift_left(carry18, 21); carry20 = (s20 + (1 << 20)) >> 21; s21 += carry20; s20 -= shift_left(carry20, 21); carry22 = (s22 + (1 << 20)) >> 21; s23 += carry22; s22 -= shift_left(carry22, 21); carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= shift_left(carry1, 21); carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= shift_left(carry3, 21); carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= shift_left(carry5, 21); carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= shift_left(carry7, 21); carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= shift_left(carry9, 21); carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= shift_left(carry11, 21); carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= shift_left(carry13, 21); carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= shift_left(carry15, 21); carry17 = (s17 + (1 << 20)) >> 21; s18 += carry17; s17 -= shift_left(carry17, 21); carry19 = (s19 + (1 << 20)) >> 21; s20 += carry19; s19 -= shift_left(carry19, 21); carry21 = (s21 + (1 << 20)) >> 21; s22 += carry21; s21 -= shift_left(carry21, 21); s11 += s23 * 666643; s12 += s23 * 470296; s13 += s23 * 654183; s14 -= s23 * 997805; s15 += s23 * 136657; s16 -= s23 * 683901; s23 = 0; s10 += s22 * 666643; s11 += s22 * 470296; s12 += s22 * 654183; s13 -= s22 * 997805; s14 += s22 * 136657; s15 -= s22 * 683901; s22 = 0; s9 += s21 * 666643; s10 += s21 * 470296; s11 += s21 * 654183; s12 -= s21 * 997805; s13 += s21 * 136657; s14 -= s21 * 683901; s21 = 0; s8 += s20 * 666643; s9 += s20 * 470296; s10 += s20 * 654183; s11 -= s20 * 997805; s12 += s20 * 136657; s13 -= s20 * 683901; s20 = 0; s7 += s19 * 666643; s8 += s19 * 470296; s9 += s19 * 654183; s10 -= s19 * 997805; s11 += s19 * 136657; s12 -= s19 * 683901; s19 = 0; s6 += s18 * 666643; s7 += s18 * 470296; s8 += s18 * 654183; s9 -= s18 * 997805; s10 += s18 * 136657; s11 -= s18 * 683901; s18 = 0; carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= shift_left(carry6, 21); carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= shift_left(carry8, 21); carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= shift_left(carry10, 21); carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= shift_left(carry12, 21); carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= shift_left(carry14, 21); carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= shift_left(carry16, 21); carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= shift_left(carry7, 21); carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= shift_left(carry9, 21); carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= shift_left(carry11, 21); carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= shift_left(carry13, 21); carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= shift_left(carry15, 21); s5 += s17 * 666643; s6 += s17 * 470296; s7 += s17 * 654183; s8 -= s17 * 997805; s9 += s17 * 136657; s10 -= s17 * 683901; s17 = 0; s4 += s16 * 666643; s5 += s16 * 470296; s6 += s16 * 654183; s7 -= s16 * 997805; s8 += s16 * 136657; s9 -= s16 * 683901; s16 = 0; s3 += s15 * 666643; s4 += s15 * 470296; s5 += s15 * 654183; s6 -= s15 * 997805; s7 += s15 * 136657; s8 -= s15 * 683901; s15 = 0; s2 += s14 * 666643; s3 += s14 * 470296; s4 += s14 * 654183; s5 -= s14 * 997805; s6 += s14 * 136657; s7 -= s14 * 683901; s14 = 0; s1 += s13 * 666643; s2 += s13 * 470296; s3 += s13 * 654183; s4 -= s13 * 997805; s5 += s13 * 136657; s6 -= s13 * 683901; s13 = 0; s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= shift_left(carry0, 21); carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= shift_left(carry2, 21); carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= shift_left(carry4, 21); carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= shift_left(carry6, 21); carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= shift_left(carry8, 21); carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= shift_left(carry10, 21); carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= shift_left(carry1, 21); carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= shift_left(carry3, 21); carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= shift_left(carry5, 21); carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= shift_left(carry7, 21); carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= shift_left(carry9, 21); carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= shift_left(carry11, 21); s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= shift_left(carry0, 21); carry1 = s1 >> 21; s2 += carry1; s1 -= shift_left(carry1, 21); carry2 = s2 >> 21; s3 += carry2; s2 -= shift_left(carry2, 21); carry3 = s3 >> 21; s4 += carry3; s3 -= shift_left(carry3, 21); carry4 = s4 >> 21; s5 += carry4; s4 -= shift_left(carry4, 21); carry5 = s5 >> 21; s6 += carry5; s5 -= shift_left(carry5, 21); carry6 = s6 >> 21; s7 += carry6; s6 -= shift_left(carry6, 21); carry7 = s7 >> 21; s8 += carry7; s7 -= shift_left(carry7, 21); carry8 = s8 >> 21; s9 += carry8; s8 -= shift_left(carry8, 21); carry9 = s9 >> 21; s10 += carry9; s9 -= shift_left(carry9, 21); carry10 = s10 >> 21; s11 += carry10; s10 -= shift_left(carry10, 21); carry11 = s11 >> 21; s12 += carry11; s11 -= shift_left(carry11, 21); s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= shift_left(carry0, 21); carry1 = s1 >> 21; s2 += carry1; s1 -= shift_left(carry1, 21); carry2 = s2 >> 21; s3 += carry2; s2 -= shift_left(carry2, 21); carry3 = s3 >> 21; s4 += carry3; s3 -= shift_left(carry3, 21); carry4 = s4 >> 21; s5 += carry4; s4 -= shift_left(carry4, 21); carry5 = s5 >> 21; s6 += carry5; s5 -= shift_left(carry5, 21); carry6 = s6 >> 21; s7 += carry6; s6 -= shift_left(carry6, 21); carry7 = s7 >> 21; s8 += carry7; s7 -= shift_left(carry7, 21); carry8 = s8 >> 21; s9 += carry8; s8 -= shift_left(carry8, 21); carry9 = s9 >> 21; s10 += carry9; s9 -= shift_left(carry9, 21); carry10 = s10 >> 21; s11 += carry10; s10 -= shift_left(carry10, 21); s[0] = (unsigned char) ((s0 >> 0) & 0xff); s[1] = (unsigned char) ((s0 >> 8) & 0xff); s[2] = (unsigned char) (((s0 >> 16) | (s1 << 5)) & 0xff); s[3] = (unsigned char) ((s1 >> 3) & 0xff); s[4] = (unsigned char) ((s1 >> 11) & 0xff); s[5] = (unsigned char) (((s1 >> 19) | (s2 << 2)) & 0xff); s[6] = (unsigned char) ((s2 >> 6) & 0xff); s[7] = (unsigned char) (((s2 >> 14) | (s3 << 7)) & 0xff); s[8] = (unsigned char) ((s3 >> 1) & 0xff); s[9] = (unsigned char) ((s3 >> 9) & 0xff); s[10] = (unsigned char) (((s3 >> 17) | (s4 << 4)) & 0xff); s[11] = (unsigned char) ((s4 >> 4) & 0xff); s[12] = (unsigned char) ((s4 >> 12) & 0xff); s[13] = (unsigned char) (((s4 >> 20) | (s5 << 1)) & 0xff); s[14] = (unsigned char) ((s5 >> 7) & 0xff); s[15] = (unsigned char) (((s5 >> 15) | (s6 << 6)) & 0xff); s[16] = (unsigned char) ((s6 >> 2) & 0xff); s[17] = (unsigned char) ((s6 >> 10) & 0xff); s[18] = (unsigned char) (((s6 >> 18) | (s7 << 3)) & 0xff); s[19] = (unsigned char) ((s7 >> 5) & 0xff); s[20] = (unsigned char) ((s7 >> 13) & 0xff); s[21] = (unsigned char) ((s8 >> 0) & 0xff); s[22] = (unsigned char) ((s8 >> 8) & 0xff); s[23] = (unsigned char) (((s8 >> 16) | (s9 << 5)) & 0xff); s[24] = (unsigned char) ((s9 >> 3) & 0xff); s[25] = (unsigned char) ((s9 >> 11) & 0xff); s[26] = (unsigned char) (((s9 >> 19) | (s10 << 2)) & 0xff); s[27] = (unsigned char) ((s10 >> 6) & 0xff); s[28] = (unsigned char) (((s10 >> 14) | (s11 << 7)) & 0xff); s[29] = (unsigned char) ((s11 >> 1) & 0xff); s[30] = (unsigned char) ((s11 >> 9) & 0xff); s[31] = (unsigned char) ((s11 >> 17) & 0xff); } libtorrent-rasterbar-1.1.13/ed25519/src/sc.h000066400000000000000000000004141351156116000203050ustar00rootroot00000000000000#ifndef SC_H #define SC_H /* The set of scalars is \Z/l where l = 2^252 + 27742317777372353535851937790883648493. */ void sc_reduce(unsigned char *s); void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c); #endif libtorrent-rasterbar-1.1.13/ed25519/src/seed.cpp000066400000000000000000000034641351156116000211630ustar00rootroot00000000000000// ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/config.hpp" #include "libtorrent/ed25519.hpp" #ifndef ED25519_NO_SEED #if TORRENT_USE_CRYPTOGRAPHIC_BUFFER #include #include using namespace Windows::Security::Cryptography; using namespace Windows::Storage::Streams; using namespace Microsoft::WRL; #elif defined _WIN32 #include #include #else #include #endif void ed25519_create_seed(unsigned char *seed) { #if TORRENT_USE_CRYPTOGRAPHIC_BUFFER IBuffer^ seedBuffer = CryptographicBuffer::GenerateRandom(32); ComPtr bufferByteAccess; reinterpret_cast(seedBuffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); bufferByteAccess->Buffer(&seed); #elif defined _WIN32 HCRYPTPROV prov; if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { throw boost::system::system_error(boost::system::error_code(GetLastError() , boost::system::system_category())); } if (!CryptGenRandom(prov, 32, seed)) { CryptReleaseContext(prov, 0); throw boost::system::system_error(boost::system::error_code(GetLastError() , boost::system::system_category())); } CryptReleaseContext(prov, 0); #else FILE *f = fopen("/dev/urandom", "rb"); if (f == NULL) { throw boost::system::system_error(boost::system::error_code(errno, boost::system::system_category())); } int read = fread(seed, 1, 32, f); if (read != 32) { fclose(f); throw boost::system::system_error(boost::system::error_code(errno, boost::system::system_category())); } fclose(f); #endif } #endif libtorrent-rasterbar-1.1.13/ed25519/src/sha512.cpp000066400000000000000000000256321351156116000212470ustar00rootroot00000000000000// ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #include "fixedint.h" #include "sha512.h" #ifndef UINT64_C #define UINT64_C(x) x ## LL #endif /* the K array */ static const u64 K[80] = { UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) }; /* Various logical functions */ #define ROR64c(x, y) \ ( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((u64)(y)&UINT64_C(63))) | \ ((x)<<((u64)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF)) #define STORE64H(x, y) \ { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } #define LOAD64H(x, y) \ { x = (((u64)((y)[0] & 255))<<56)|(((u64)((y)[1] & 255))<<48) | \ (((u64)((y)[2] & 255))<<40)|(((u64)((y)[3] & 255))<<32) | \ (((u64)((y)[4] & 255))<<24)|(((u64)((y)[5] & 255))<<16) | \ (((u64)((y)[6] & 255))<<8)|(((u64)((y)[7] & 255))); } #define Ch(x,y,z) (z ^ (x & (y ^ z))) #define Maj(x,y,z) (((x | y) & z) | (x & y)) #define S(x, n) ROR64c(x, n) #define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((u64)n)) #define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) #define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) #define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) #define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) #ifndef MIN #define MIN(x, y) ( ((x)<(y))?(x):(y) ) #endif /* compress 1024-bits */ static int sha512_compress(sha512_context *md, unsigned char *buf) { u64 S[8], W[80], t0, t1; int i; /* copy state into S */ for (i = 0; i < 8; i++) { S[i] = md->state[i]; } /* copy the state into 1024-bits into W[0..15] */ for (i = 0; i < 16; i++) { LOAD64H(W[i], buf + (8*i)); } /* fill W[16..79] */ for (i = 16; i < 80; i++) { W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; } /* Compress */ #define RND(a,b,c,d,e,f,g,h,i) \ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ t1 = Sigma0(a) + Maj(a, b, c);\ d += t0; \ h = t0 + t1; for (i = 0; i < 80; i += 8) { RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); } #undef RND /* feedback */ for (i = 0; i < 8; i++) { md->state[i] = md->state[i] + S[i]; } return 0; } /** Initialize the hash state @param md The hash state you wish to initialize @return 0 if successful */ int sha512_init(sha512_context * md) { if (md == NULL) return 1; md->curlen = 0; md->length = 0; md->state[0] = UINT64_C(0x6a09e667f3bcc908); md->state[1] = UINT64_C(0xbb67ae8584caa73b); md->state[2] = UINT64_C(0x3c6ef372fe94f82b); md->state[3] = UINT64_C(0xa54ff53a5f1d36f1); md->state[4] = UINT64_C(0x510e527fade682d1); md->state[5] = UINT64_C(0x9b05688c2b3e6c1f); md->state[6] = UINT64_C(0x1f83d9abfb41bd6b); md->state[7] = UINT64_C(0x5be0cd19137e2179); return 0; } /** Process a block of memory though the hash @param md The hash state @param in The data to hash @param inlen The length of the data (octets) @return 0 if successful */ int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen) { size_t n; size_t i; int err; if (md == NULL) return 1; if (in == NULL) return 1; if (md->curlen > sizeof(md->buf)) { return 1; } while (inlen > 0) { if (md->curlen == 0 && inlen >= 128) { if ((err = sha512_compress (md, (unsigned char *)in)) != 0) { return err; } md->length += 128 * 8; in += 128; inlen -= 128; } else { n = MIN(inlen, (128 - md->curlen)); for (i = 0; i < n; i++) { md->buf[i + md->curlen] = in[i]; } md->curlen += n; in += n; inlen -= n; if (md->curlen == 128) { if ((err = sha512_compress (md, md->buf)) != 0) { return err; } md->length += 8*128; md->curlen = 0; } } } return 0; } /** Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (64 bytes) @return 0 if successful */ int sha512_final(sha512_context * md, unsigned char *out) { int i; if (md == NULL) return 1; if (out == NULL) return 1; if (md->curlen >= sizeof(md->buf)) { return 1; } /* increase the length of the message */ md->length += md->curlen * UINT64_C(8); /* append the '1' bit */ md->buf[md->curlen++] = (unsigned char)0x80; /* if the length is currently above 112 bytes we append zeros * then compress. Then we can fall back to padding zeros and length * encoding like normal. */ if (md->curlen > 112) { while (md->curlen < 128) { md->buf[md->curlen++] = (unsigned char)0; } sha512_compress(md, md->buf); md->curlen = 0; } /* pad upto 120 bytes of zeroes * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash * > 2^64 bits of data... :-) */ while (md->curlen < 120) { md->buf[md->curlen++] = (unsigned char)0; } /* store length */ STORE64H(md->length, md->buf+120); sha512_compress(md, md->buf); /* copy output */ for (i = 0; i < 8; i++) { STORE64H(md->state[i], out+(8*i)); } return 0; } int sha512(const unsigned char *message, size_t message_len, unsigned char *out) { sha512_context ctx; int ret; if ((ret = sha512_init(&ctx))) return ret; if ((ret = sha512_update(&ctx, message, message_len))) return ret; if ((ret = sha512_final(&ctx, out))) return ret; return 0; } libtorrent-rasterbar-1.1.13/ed25519/src/sha512.h000066400000000000000000000007451351156116000207120ustar00rootroot00000000000000#ifndef SHA512_H #define SHA512_H #include #include "fixedint.h" /* state */ typedef struct sha512_context_ { u64 length, state[8]; size_t curlen; unsigned char buf[128]; } sha512_context; int sha512_init(sha512_context * md); int sha512_final(sha512_context * md, unsigned char *out); int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen); int sha512(const unsigned char *message, size_t message_len, unsigned char *out); #endif libtorrent-rasterbar-1.1.13/ed25519/src/sign.cpp000066400000000000000000000016361351156116000212020ustar00rootroot00000000000000// ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" #include "libtorrent/ed25519.hpp" #include "sha512.h" #include "ge.h" #include "sc.h" void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) { sha512_context hash; unsigned char hram[64]; unsigned char r[64]; ge_p3 R; sha512_init(&hash); sha512_update(&hash, private_key + 32, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, r); sc_reduce(r); ge_scalarmult_base(&R, r); ge_p3_tobytes(signature, &R); sha512_init(&hash); sha512_update(&hash, signature, 32); sha512_update(&hash, public_key, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, hram); sc_reduce(hram); sc_muladd(signature + 32, hram, private_key, r); } libtorrent-rasterbar-1.1.13/ed25519/src/verify.cpp000066400000000000000000000026721351156116000215470ustar00rootroot00000000000000// ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" #include "libtorrent/ed25519.hpp" #include "sha512.h" #include "ge.h" #include "sc.h" static int consttime_equal(const unsigned char *x, const unsigned char *y) { unsigned char r = 0; r = x[0] ^ y[0]; #define F(i) r |= x[i] ^ y[i] F(1); F(2); F(3); F(4); F(5); F(6); F(7); F(8); F(9); F(10); F(11); F(12); F(13); F(14); F(15); F(16); F(17); F(18); F(19); F(20); F(21); F(22); F(23); F(24); F(25); F(26); F(27); F(28); F(29); F(30); F(31); #undef F return !r; } int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) { unsigned char h[64]; unsigned char checker[32]; sha512_context hash; ge_p3 A; ge_p2 R; if (signature[63] & 224) { return 0; } if (ge_frombytes_negate_vartime(&A, public_key) != 0) { return 0; } sha512_init(&hash); sha512_update(&hash, signature, 32); sha512_update(&hash, public_key, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, h); sc_reduce(h); ge_double_scalarmult_vartime(&R, h, &A, signature + 32); ge_tobytes(checker, &R); if (!consttime_equal(checker, signature)) { return 0; } return 1; } libtorrent-rasterbar-1.1.13/ed25519/test.c000066400000000000000000000110561351156116000200670ustar00rootroot00000000000000#include #include #include #include //#define ED25519_DLL #include "src/ed25519.h" #include "src/ge.h" #include "src/sc.h" const char message[] = "Hello, world!"; int main(int argc, char *argv[]) { unsigned char public_key[32], private_key[64], seed[32], scalar[32]; unsigned char other_public_key[32], other_private_key[64]; unsigned char shared_secret[32], other_shared_secret[32]; unsigned char signature[64]; clock_t start; clock_t end; int i; /* create a random seed, and a keypair out of that seed */ ed25519_create_seed(seed); ed25519_create_keypair(public_key, private_key, seed); /* create signature on the message with the keypair */ ed25519_sign(signature, message, strlen(message), public_key, private_key); /* verify the signature */ if (ed25519_verify(signature, message, strlen(message), public_key)) { printf("valid signature\n"); } else { printf("invalid signature\n"); } /* create scalar and add it to the keypair */ ed25519_create_seed(scalar); ed25519_add_scalar(public_key, private_key, scalar); /* create signature with the new keypair */ ed25519_sign(signature, message, strlen(message), public_key, private_key); /* verify the signature with the new keypair */ if (ed25519_verify(signature, message, strlen(message), public_key)) { printf("valid signature\n"); } else { printf("invalid signature\n"); } /* make a slight adjustment and verify again */ signature[44] ^= 0x10; if (ed25519_verify(signature, message, strlen(message), public_key)) { printf("did not detect signature change\n"); } else { printf("correctly detected signature change\n"); } /* generate two keypairs for testing key exchange */ ed25519_create_seed(seed); ed25519_create_keypair(public_key, private_key, seed); ed25519_create_seed(seed); ed25519_create_keypair(other_public_key, other_private_key, seed); /* create two shared secrets - from both perspectives - and check if they're equal */ ed25519_key_exchange(shared_secret, other_public_key, private_key); ed25519_key_exchange(other_shared_secret, public_key, other_private_key); for (i = 0; i < 32; ++i) { if (shared_secret[i] != other_shared_secret[i]) { printf("key exchange was incorrect\n"); break; } } if (i == 32) { printf("key exchange was correct\n"); } /* test performance */ printf("testing seed generation performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_create_seed(seed); } end = clock(); printf("%fus per seed\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing key generation performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_create_keypair(public_key, private_key, seed); } end = clock(); printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing sign performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_sign(signature, message, strlen(message), public_key, private_key); } end = clock(); printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing verify performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_verify(signature, message, strlen(message), public_key); } end = clock(); printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing keypair scalar addition performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_add_scalar(public_key, private_key, scalar); } end = clock(); printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing public key scalar addition performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_add_scalar(public_key, NULL, scalar); } end = clock(); printf("%fus per key\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing key exchange performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_key_exchange(shared_secret, other_public_key, private_key); } end = clock(); printf("%fus per shared secret\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); return 0; } libtorrent-rasterbar-1.1.13/examples/000077500000000000000000000000001351156116000175615ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/examples/CMakeLists.txt000066400000000000000000000014411351156116000223210ustar00rootroot00000000000000project(libtorrent-examples) cmake_minimum_required(VERSION 3.10) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") if (TARGET torrent-rasterbar) add_library(LibtorrentRasterbar::torrent-rasterbar ALIAS torrent-rasterbar) else() find_package(LibtorrentRasterbar REQUIRED) endif() set(single_file_examples simple_client stats_counters dump_torrent make_torrent connection_tester upnp_test) foreach(example ${single_file_examples}) add_executable(${example} "${example}.cpp") target_link_libraries(${example} LibtorrentRasterbar::torrent-rasterbar) endforeach(example) add_executable(client_test client_test.cpp print.cpp torrent_view.cpp session_view.cpp) target_link_libraries(client_test LibtorrentRasterbar::torrent-rasterbar) libtorrent-rasterbar-1.1.13/examples/Jamfile000066400000000000000000000016471351156116000210630ustar00rootroot00000000000000import modules ; BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; use-project /torrent : .. ; if $(BOOST_ROOT) { use-project /boost : $(BOOST_ROOT) ; } project client_test : requirements multi /torrent//torrent darwin:-Wno-unused-command-line-argument : default-build static ; exe client_test : client_test.cpp print.cpp torrent_view.cpp session_view.cpp ; exe simple_client : simple_client.cpp ; exe bt-get : bt-get.cpp ; exe bt-get2 : bt-get2.cpp ; exe stats_counters : stats_counters.cpp ; exe dump_torrent : dump_torrent.cpp ; exe make_torrent : make_torrent.cpp ; exe connection_tester : connection_tester.cpp ; exe upnp_test : upnp_test.cpp ; explicit stage_client_test ; explicit stage_connection_tester ; explicit bt-get ; explicit bt-get2 ; install stage_client_test : client_test : . ; install stage_connection_tester : connection_tester : . ; libtorrent-rasterbar-1.1.13/examples/Makefile.am000066400000000000000000000021741351156116000216210ustar00rootroot00000000000000example_programs = \ client_test \ stats_counters \ dump_torrent \ make_torrent \ simple_client \ upnp_test \ bt_get \ bt_get2 \ connection_tester if ENABLE_EXAMPLES bin_PROGRAMS = $(example_programs) endif EXTRA_PROGRAMS = $(example_programs) EXTRA_DIST = Jamfile CMakeLists.txt run_cmake.sh.in session_view.hpp torrent_view.hpp print.hpp cmake/FindLibtorrentRasterbar.cmake client_test_SOURCES = client_test.cpp print.cpp session_view.cpp torrent_view.cpp stats_counters_SOURCES = stats_counters.cpp bt_get_SOURCES = bt-get.cpp bt_get2_SOURCES = bt-get2.cpp bt_get_CXXFLAGS = -std=c++11 bt_get2_CXXFLAGS = -std=c++11 dump_torrent_SOURCES = dump_torrent.cpp make_torrent_SOURCES = make_torrent.cpp simple_client_SOURCES = simple_client.cpp connection_tester_SOURCES = connection_tester.cpp upnp_test_SOURCES = upnp_test.cpp LDADD = $(top_builddir)/src/libtorrent-rasterbar.la AM_CPPFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include @DEBUGFLAGS@ @OPENSSL_INCLUDES@ AM_LDFLAGS = @BOOST_SYSTEM_LIB@ @BOOST_CHRONO_LIB@ @BOOST_RANDOM_LIB@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ libtorrent-rasterbar-1.1.13/examples/Makefile.in000066400000000000000000000747721351156116000216470ustar00rootroot00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 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@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd 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@ target_triplet = @target@ @ENABLE_EXAMPLES_TRUE@bin_PROGRAMS = $(am__EXEEXT_1) EXTRA_PROGRAMS = $(am__EXEEXT_1) subdir = examples ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ $(top_srcdir)/m4/ax_boost_chrono.m4 \ $(top_srcdir)/m4/ax_boost_python.m4 \ $(top_srcdir)/m4/ax_boost_random.m4 \ $(top_srcdir)/m4/ax_boost_system.m4 \ $(top_srcdir)/m4/ax_check_openssl.m4 \ $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__EXEEXT_1 = client_test$(EXEEXT) stats_counters$(EXEEXT) \ dump_torrent$(EXEEXT) make_torrent$(EXEEXT) \ simple_client$(EXEEXT) upnp_test$(EXEEXT) bt_get$(EXEEXT) \ bt_get2$(EXEEXT) connection_tester$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) am_bt_get_OBJECTS = bt_get-bt-get.$(OBJEXT) bt_get_OBJECTS = $(am_bt_get_OBJECTS) bt_get_LDADD = $(LDADD) bt_get_DEPENDENCIES = $(top_builddir)/src/libtorrent-rasterbar.la AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = bt_get_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(bt_get_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am_bt_get2_OBJECTS = bt_get2-bt-get2.$(OBJEXT) bt_get2_OBJECTS = $(am_bt_get2_OBJECTS) bt_get2_LDADD = $(LDADD) bt_get2_DEPENDENCIES = $(top_builddir)/src/libtorrent-rasterbar.la bt_get2_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(bt_get2_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am_client_test_OBJECTS = client_test.$(OBJEXT) print.$(OBJEXT) \ session_view.$(OBJEXT) torrent_view.$(OBJEXT) client_test_OBJECTS = $(am_client_test_OBJECTS) client_test_LDADD = $(LDADD) client_test_DEPENDENCIES = \ $(top_builddir)/src/libtorrent-rasterbar.la am_connection_tester_OBJECTS = connection_tester.$(OBJEXT) connection_tester_OBJECTS = $(am_connection_tester_OBJECTS) connection_tester_LDADD = $(LDADD) connection_tester_DEPENDENCIES = \ $(top_builddir)/src/libtorrent-rasterbar.la am_dump_torrent_OBJECTS = dump_torrent.$(OBJEXT) dump_torrent_OBJECTS = $(am_dump_torrent_OBJECTS) dump_torrent_LDADD = $(LDADD) dump_torrent_DEPENDENCIES = \ $(top_builddir)/src/libtorrent-rasterbar.la am_make_torrent_OBJECTS = make_torrent.$(OBJEXT) make_torrent_OBJECTS = $(am_make_torrent_OBJECTS) make_torrent_LDADD = $(LDADD) make_torrent_DEPENDENCIES = \ $(top_builddir)/src/libtorrent-rasterbar.la am_simple_client_OBJECTS = simple_client.$(OBJEXT) simple_client_OBJECTS = $(am_simple_client_OBJECTS) simple_client_LDADD = $(LDADD) simple_client_DEPENDENCIES = \ $(top_builddir)/src/libtorrent-rasterbar.la am_stats_counters_OBJECTS = stats_counters.$(OBJEXT) stats_counters_OBJECTS = $(am_stats_counters_OBJECTS) stats_counters_LDADD = $(LDADD) stats_counters_DEPENDENCIES = \ $(top_builddir)/src/libtorrent-rasterbar.la am_upnp_test_OBJECTS = upnp_test.$(OBJEXT) upnp_test_OBJECTS = $(am_upnp_test_OBJECTS) upnp_test_LDADD = $(LDADD) upnp_test_DEPENDENCIES = $(top_builddir)/src/libtorrent-rasterbar.la AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(bt_get_SOURCES) $(bt_get2_SOURCES) $(client_test_SOURCES) \ $(connection_tester_SOURCES) $(dump_torrent_SOURCES) \ $(make_torrent_SOURCES) $(simple_client_SOURCES) \ $(stats_counters_SOURCES) $(upnp_test_SOURCES) DIST_SOURCES = $(bt_get_SOURCES) $(bt_get2_SOURCES) \ $(client_test_SOURCES) $(connection_tester_SOURCES) \ $(dump_torrent_SOURCES) $(make_torrent_SOURCES) \ $(simple_client_SOURCES) $(stats_counters_SOURCES) \ $(upnp_test_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BOOST_CHRONO_LIB = @BOOST_CHRONO_LIB@ BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ BOOST_LDFLAGS = @BOOST_LDFLAGS@ BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ BOOST_RANDOM_LIB = @BOOST_RANDOM_LIB@ BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUGFLAGS = @DEBUGFLAGS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONV_LIBS = @ICONV_LIBS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ PYTHON_CXXFLAGS = @PYTHON_CXXFLAGS@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ example_programs = \ client_test \ stats_counters \ dump_torrent \ make_torrent \ simple_client \ upnp_test \ bt_get \ bt_get2 \ connection_tester EXTRA_DIST = Jamfile CMakeLists.txt run_cmake.sh.in session_view.hpp torrent_view.hpp print.hpp cmake/FindLibtorrentRasterbar.cmake client_test_SOURCES = client_test.cpp print.cpp session_view.cpp torrent_view.cpp stats_counters_SOURCES = stats_counters.cpp bt_get_SOURCES = bt-get.cpp bt_get2_SOURCES = bt-get2.cpp bt_get_CXXFLAGS = -std=c++11 bt_get2_CXXFLAGS = -std=c++11 dump_torrent_SOURCES = dump_torrent.cpp make_torrent_SOURCES = make_torrent.cpp simple_client_SOURCES = simple_client.cpp connection_tester_SOURCES = connection_tester.cpp upnp_test_SOURCES = upnp_test.cpp LDADD = $(top_builddir)/src/libtorrent-rasterbar.la AM_CPPFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include @DEBUGFLAGS@ @OPENSSL_INCLUDES@ AM_LDFLAGS = @BOOST_SYSTEM_LIB@ @BOOST_CHRONO_LIB@ @BOOST_RANDOM_LIB@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign examples/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign examples/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: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list bt_get$(EXEEXT): $(bt_get_OBJECTS) $(bt_get_DEPENDENCIES) $(EXTRA_bt_get_DEPENDENCIES) @rm -f bt_get$(EXEEXT) $(AM_V_CXXLD)$(bt_get_LINK) $(bt_get_OBJECTS) $(bt_get_LDADD) $(LIBS) bt_get2$(EXEEXT): $(bt_get2_OBJECTS) $(bt_get2_DEPENDENCIES) $(EXTRA_bt_get2_DEPENDENCIES) @rm -f bt_get2$(EXEEXT) $(AM_V_CXXLD)$(bt_get2_LINK) $(bt_get2_OBJECTS) $(bt_get2_LDADD) $(LIBS) client_test$(EXEEXT): $(client_test_OBJECTS) $(client_test_DEPENDENCIES) $(EXTRA_client_test_DEPENDENCIES) @rm -f client_test$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(client_test_OBJECTS) $(client_test_LDADD) $(LIBS) connection_tester$(EXEEXT): $(connection_tester_OBJECTS) $(connection_tester_DEPENDENCIES) $(EXTRA_connection_tester_DEPENDENCIES) @rm -f connection_tester$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(connection_tester_OBJECTS) $(connection_tester_LDADD) $(LIBS) dump_torrent$(EXEEXT): $(dump_torrent_OBJECTS) $(dump_torrent_DEPENDENCIES) $(EXTRA_dump_torrent_DEPENDENCIES) @rm -f dump_torrent$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(dump_torrent_OBJECTS) $(dump_torrent_LDADD) $(LIBS) make_torrent$(EXEEXT): $(make_torrent_OBJECTS) $(make_torrent_DEPENDENCIES) $(EXTRA_make_torrent_DEPENDENCIES) @rm -f make_torrent$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(make_torrent_OBJECTS) $(make_torrent_LDADD) $(LIBS) simple_client$(EXEEXT): $(simple_client_OBJECTS) $(simple_client_DEPENDENCIES) $(EXTRA_simple_client_DEPENDENCIES) @rm -f simple_client$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(simple_client_OBJECTS) $(simple_client_LDADD) $(LIBS) stats_counters$(EXEEXT): $(stats_counters_OBJECTS) $(stats_counters_DEPENDENCIES) $(EXTRA_stats_counters_DEPENDENCIES) @rm -f stats_counters$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(stats_counters_OBJECTS) $(stats_counters_LDADD) $(LIBS) upnp_test$(EXEEXT): $(upnp_test_OBJECTS) $(upnp_test_DEPENDENCIES) $(EXTRA_upnp_test_DEPENDENCIES) @rm -f upnp_test$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(upnp_test_OBJECTS) $(upnp_test_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bt_get-bt-get.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bt_get2-bt-get2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client_test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection_tester.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dump_torrent.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/make_torrent.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_view.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple_client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stats_counters.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_view.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upnp_test.Po@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< bt_get-bt-get.o: bt-get.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bt_get_CXXFLAGS) $(CXXFLAGS) -MT bt_get-bt-get.o -MD -MP -MF $(DEPDIR)/bt_get-bt-get.Tpo -c -o bt_get-bt-get.o `test -f 'bt-get.cpp' || echo '$(srcdir)/'`bt-get.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bt_get-bt-get.Tpo $(DEPDIR)/bt_get-bt-get.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='bt-get.cpp' object='bt_get-bt-get.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bt_get_CXXFLAGS) $(CXXFLAGS) -c -o bt_get-bt-get.o `test -f 'bt-get.cpp' || echo '$(srcdir)/'`bt-get.cpp bt_get-bt-get.obj: bt-get.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bt_get_CXXFLAGS) $(CXXFLAGS) -MT bt_get-bt-get.obj -MD -MP -MF $(DEPDIR)/bt_get-bt-get.Tpo -c -o bt_get-bt-get.obj `if test -f 'bt-get.cpp'; then $(CYGPATH_W) 'bt-get.cpp'; else $(CYGPATH_W) '$(srcdir)/bt-get.cpp'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bt_get-bt-get.Tpo $(DEPDIR)/bt_get-bt-get.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='bt-get.cpp' object='bt_get-bt-get.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bt_get_CXXFLAGS) $(CXXFLAGS) -c -o bt_get-bt-get.obj `if test -f 'bt-get.cpp'; then $(CYGPATH_W) 'bt-get.cpp'; else $(CYGPATH_W) '$(srcdir)/bt-get.cpp'; fi` bt_get2-bt-get2.o: bt-get2.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bt_get2_CXXFLAGS) $(CXXFLAGS) -MT bt_get2-bt-get2.o -MD -MP -MF $(DEPDIR)/bt_get2-bt-get2.Tpo -c -o bt_get2-bt-get2.o `test -f 'bt-get2.cpp' || echo '$(srcdir)/'`bt-get2.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bt_get2-bt-get2.Tpo $(DEPDIR)/bt_get2-bt-get2.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='bt-get2.cpp' object='bt_get2-bt-get2.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bt_get2_CXXFLAGS) $(CXXFLAGS) -c -o bt_get2-bt-get2.o `test -f 'bt-get2.cpp' || echo '$(srcdir)/'`bt-get2.cpp bt_get2-bt-get2.obj: bt-get2.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bt_get2_CXXFLAGS) $(CXXFLAGS) -MT bt_get2-bt-get2.obj -MD -MP -MF $(DEPDIR)/bt_get2-bt-get2.Tpo -c -o bt_get2-bt-get2.obj `if test -f 'bt-get2.cpp'; then $(CYGPATH_W) 'bt-get2.cpp'; else $(CYGPATH_W) '$(srcdir)/bt-get2.cpp'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bt_get2-bt-get2.Tpo $(DEPDIR)/bt_get2-bt-get2.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='bt-get2.cpp' object='bt_get2-bt-get2.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bt_get2_CXXFLAGS) $(CXXFLAGS) -c -o bt_get2-bt-get2.obj `if test -f 'bt-get2.cpp'; then $(CYGPATH_W) 'bt-get2.cpp'; else $(CYGPATH_W) '$(srcdir)/bt-get2.cpp'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$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 $(PROGRAMS) installdirs: for dir in "$(DESTDIR)$(bindir)"; 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: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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-binPROGRAMS clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: 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-binPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-binPROGRAMS clean-generic clean-libtool cscopelist-am \ ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-binPROGRAMS \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am 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 tags-am uninstall uninstall-am uninstall-binPROGRAMS .PRECIOUS: Makefile # 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: libtorrent-rasterbar-1.1.13/examples/bt-get.cpp000066400000000000000000000054761351156116000214630ustar00rootroot00000000000000/* Copyright (c) 2016, 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 #include #include #include #include #include #include #include #include int main(int argc, char const* argv[]) { if (argc != 2) { std::cerr << "usage: " << argv[0] << " " << std::endl; return 1; } lt::settings_pack p; p.set_int(lt::settings_pack::alert_mask, lt::alert::status_notification | lt::alert::error_notification); lt::session ses(p); lt::add_torrent_params atp; lt::error_code ec; lt::parse_magnet_uri(argv[1], atp, ec); if (ec) { std::cerr << "invalid magnet URI: " << ec.message() << std::endl; return 1; } atp.save_path = "."; // save in current dir lt::torrent_handle h = ses.add_torrent(atp); for (;;) { std::vector alerts; ses.pop_alerts(&alerts); for (lt::alert const* a : alerts) { std::cout << a->message() << std::endl; // if we receive the finished alert or an error, we're done if (lt::alert_cast(a)) { goto done; } if (lt::alert_cast(a)) { goto done; } } std::this_thread::sleep_for(std::chrono::milliseconds(200)); } done: std::cout << "done, shutting down" << std::endl; } libtorrent-rasterbar-1.1.13/examples/bt-get2.cpp000066400000000000000000000122111351156116000215260ustar00rootroot00000000000000/* Copyright (c) 2016, 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 #include #include #include #include #include #include #include #include #include #include #include using clk = std::chrono::steady_clock; // return the name of a torrent status enum char const* state(lt::torrent_status::state_t s) { switch(s) { case lt::torrent_status::checking_files: return "checking"; case lt::torrent_status::downloading_metadata: return "dl metadata"; case lt::torrent_status::downloading: return "downloading"; case lt::torrent_status::finished: return "finished"; case lt::torrent_status::seeding: return "seeding"; case lt::torrent_status::allocating: return "allocating"; case lt::torrent_status::checking_resume_data: return "checking resume"; default: return "<>"; } } int main(int argc, char const* argv[]) { if (argc != 2) { std::cerr << "usage: " << argv[0] << " " << std::endl; return 1; } lt::settings_pack pack; pack.set_int(lt::settings_pack::alert_mask , lt::alert::error_notification | lt::alert::storage_notification | lt::alert::status_notification); lt::session ses(pack); lt::add_torrent_params atp; clk::time_point last_save_resume = clk::now(); // load resume data from disk and pass it in as we add the magnet link std::ifstream ifs(".resume_file", std::ios_base::binary); ifs.unsetf(std::ios_base::skipws); atp.resume_data.assign(std::istream_iterator(ifs) , std::istream_iterator()); lt::error_code ec; lt::parse_magnet_uri(argv[1], atp, ec); if (ec) { std::cerr << "invalid magnet URI: " << ec.message() << std::endl; return 1; } atp.save_path = "."; // save in current dir ses.async_add_torrent(atp); // this is the handle we'll set once we get the notification of it being // added lt::torrent_handle h; for (;;) { std::vector alerts; ses.pop_alerts(&alerts); for (lt::alert const* a : alerts) { if (auto at = lt::alert_cast(a)) { h = at->handle; } // if we receive the finished alert or an error, we're done if (lt::alert_cast(a)) { h.save_resume_data(); goto done; } if (lt::alert_cast(a)) { std::cout << a->message() << std::endl; goto done; } // when resume data is ready, save it if (auto rd = lt::alert_cast(a)) { std::ofstream of(".resume_file", std::ios_base::binary); of.unsetf(std::ios_base::skipws); lt::bencode(std::ostream_iterator(of) , *rd->resume_data); } if (auto st = lt::alert_cast(a)) { if (st->status.empty()) continue; // we only have a single torrent, so we know which one // the status is for lt::torrent_status const& s = st->status[0]; std::cout << "\r" << state(s.state) << " " << (s.download_payload_rate / 1000) << " kB/s " << (s.total_done / 1000) << " kB (" << (s.progress_ppm / 10000) << "%) downloaded\x1b[K"; std::cout.flush(); } } std::this_thread::sleep_for(std::chrono::milliseconds(200)); // ask the session to post a state_update_alert, to update our // state output for the torrent ses.post_torrent_updates(); // save resume data once every 30 seconds if (clk::now() - last_save_resume > std::chrono::seconds(30)) { h.save_resume_data(); last_save_resume = clk::now(); } } // TODO: ideally we should save resume data here done: std::cout << "\ndone, shutting down" << std::endl; } libtorrent-rasterbar-1.1.13/examples/client_test.cpp000066400000000000000000002072731351156116000226150ustar00rootroot00000000000000/* 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 #include "libtorrent/config.hpp" #ifdef TORRENT_WINDOWS #include // for _mkdir and _getcwd #include // for _stat #include #endif #ifdef _MSC_VER #pragma warning(push, 1) #endif #include #include #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef TORRENT_UTP_LOG_ENABLE #include "libtorrent/utp_stream.hpp" #endif #include "libtorrent/torrent_info.hpp" #include "libtorrent/announce_entry.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/ip_filter.hpp" #include "libtorrent/magnet_uri.hpp" #include "libtorrent/bitfield.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/bdecode.hpp" #include "libtorrent/add_torrent_params.hpp" #include "libtorrent/time.hpp" #include "libtorrent/create_torrent.hpp" #include "torrent_view.hpp" #include "session_view.hpp" #include "print.hpp" using boost::bind; using libtorrent::total_milliseconds; void sleep_ms(int milliseconds) { #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN Sleep(milliseconds); #elif defined TORRENT_BEOS snooze_until(system_time() + boost::int64_t(milliseconds) * 1000, B_SYSTEM_TIMEBASE); #else usleep(milliseconds * 1000); #endif } #ifdef _WIN32 #include #include bool sleep_and_input(int* c, int sleep) { for (int i = 0; i < 2; ++i) { if (_kbhit()) { *c = _getch(); return true; } Sleep(sleep / 2); } return false; }; #else #include // for snprintf #include // for atoi #include #include #include #include struct set_keypress { set_keypress() { termios new_settings; tcgetattr(0,&stored_settings); new_settings = stored_settings; // Disable canonical mode, and set buffer size to 1 byte // and disable echo new_settings.c_lflag &= ~(ICANON | ECHO); new_settings.c_cc[VTIME] = 0; new_settings.c_cc[VMIN] = 1; tcsetattr(0,TCSANOW,&new_settings); } ~set_keypress() { tcsetattr(0,TCSANOW,&stored_settings); } termios stored_settings; }; bool sleep_and_input(int* c, int sleep) { libtorrent::time_point start = libtorrent::clock_type::now(); int ret = 0; retry: fd_set set; FD_ZERO(&set); FD_SET(0, &set); timeval tv = {sleep/ 1000, (sleep % 1000) * 1000 }; ret = select(1, &set, 0, 0, &tv); if (ret > 0) { *c = getc(stdin); return true; } if (errno == EINTR) { if (total_milliseconds(libtorrent::clock_type::now() - start) < sleep) goto retry; return false; } if (ret < 0 && errno != 0 && errno != ETIMEDOUT) { fprintf(stderr, "select failed: %s\n", strerror(errno)); sleep_ms(500); } return false; } #endif bool print_trackers = false; bool print_peers = false; bool print_log = false; bool print_downloads = false; bool print_matrix = false; bool print_file_progress = false; bool show_pad_files = false; bool show_dht_status = false; bool sequential_download = false; bool print_ip = true; bool print_timers = false; bool print_block = false; bool print_peer_rate = false; bool print_fails = false; bool print_send_bufs = true; bool print_disk_stats = false; // the number of times we've asked to save resume data // without having received a response (successful or failure) int num_outstanding_resume_data = 0; #ifndef TORRENT_DISABLE_DHT std::vector dht_active_requests; std::vector dht_routing_table; #endif int load_file(std::string const& filename, std::vector& v , libtorrent::error_code& ec, int limit = 8000000) { ec.clear(); FILE* f = fopen(filename.c_str(), "rb"); if (f == NULL) { ec.assign(errno, boost::system::system_category()); return -1; } int r = fseek(f, 0, SEEK_END); if (r != 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } long s = ftell(f); if (s < 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } if (s > limit) { fclose(f); return -2; } r = fseek(f, 0, SEEK_SET); if (r != 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } v.resize(s); if (s == 0) { fclose(f); return 0; } r = fread(&v[0], 1, v.size(), f); if (r < 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } fclose(f); if (r != s) return -3; return 0; } bool is_absolute_path(std::string const& f) { if (f.empty()) return false; #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) int i = 0; // match the xx:\ or xx:/ form while (f[i] && strchr("abcdefghijklmnopqrstuvxyz", f[i])) ++i; if (i < int(f.size()-1) && f[i] == ':' && (f[i+1] == '\\' || f[i+1] == '/')) return true; // match the \\ form if (int(f.size()) >= 2 && f[0] == '\\' && f[1] == '\\') return true; return false; #else if (f[0] == '/') return true; return false; #endif } std::string leaf_path(std::string f) { if (f.empty()) return ""; char const* first = f.c_str(); char const* sep = strrchr(first, '/'); #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) char const* altsep = strrchr(first, '\\'); if (sep == 0 || altsep > sep) sep = altsep; #endif if (sep == 0) return f; if (sep - first == int(f.size()) - 1) { // if the last character is a / (or \) // ignore it int len = 0; while (sep > first) { --sep; if (*sep == '/' #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) || *sep == '\\' #endif ) return std::string(sep + 1, len); ++len; } return std::string(first, len); } return std::string(sep + 1); } std::string path_append(std::string const& lhs, std::string const& rhs) { if (lhs.empty() || lhs == ".") return rhs; if (rhs.empty() || rhs == ".") return lhs; #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) #define TORRENT_SEPARATOR "\\" bool need_sep = lhs[lhs.size()-1] != '\\' && lhs[lhs.size()-1] != '/'; #else #define TORRENT_SEPARATOR "/" bool need_sep = lhs[lhs.size()-1] != '/'; #endif return lhs + (need_sep?TORRENT_SEPARATOR:"") + rhs; } bool is_hex(char const *in, int len) { for (char const* end = in + len; in < end; ++in) { if (*in >= '0' && *in <= '9') continue; if (*in >= 'A' && *in <= 'F') continue; if (*in >= 'a' && *in <= 'f') continue; return false; } return true; } std::string print_endpoint(libtorrent::tcp::endpoint const& ep) { using namespace libtorrent; error_code ec; char buf[200]; address const& addr = ep.address(); #if TORRENT_USE_IPV6 if (addr.is_v6()) snprintf(buf, sizeof(buf), "[%s]:%d", addr.to_string(ec).c_str(), ep.port()); else #endif snprintf(buf, sizeof(buf), "%s:%d", addr.to_string(ec).c_str(), ep.port()); return buf; } struct torrent_entry { torrent_entry(libtorrent::torrent_handle h) : handle(h) {} libtorrent::torrent_handle handle; libtorrent::torrent_status status; }; // maps filenames to torrent_handles typedef std::map handles_t; typedef std::map files_t; files_t hash_to_filename; using libtorrent::torrent_status; bool yes(libtorrent::torrent_status const&) { return true; } FILE* g_log_file = 0; std::string const& piece_bar(libtorrent::bitfield const& p, int width) { #ifdef _WIN32 int const table_size = 5; #else int const table_size = 18; width *= 2; // we only print one character for every two "slots" #endif double const piece_per_char = p.size() / double(width); static std::string bar; bar.clear(); bar.reserve(width * 6); bar += "["; if (p.size() == 0) { for (int i = 0; i < width; ++i) bar += ' '; bar += "]"; return bar; } // the [piece, piece + pieces_per_char) range is the pieces that are represented by each character double piece = 0; // we print two blocks at a time, so calculate the color in pair int color[2]; int last_color[2] = { -1, -1}; for (int i = 0; i < width; ++i, piece += piece_per_char) { int num_pieces = 0; int num_have = 0; int end = (std::max)(int(piece + piece_per_char), int(piece) + 1); for (int k = int(piece); k < end; ++k, ++num_pieces) if (p[k]) ++num_have; int const c = int(std::ceil(num_have / float((std::max)(num_pieces, 1)) * (table_size - 1))); char buf[40]; color[i & 1] = c; #ifndef _WIN32 if ((i & 1) == 1) { // now, print color[0] and [1] // bg determines whether we're settings foreground or background color static int const bg[] = { 38, 48}; for (int i = 0; i < 2; ++i) { if (color[i] != last_color[i]) { snprintf(buf, sizeof(buf), "\x1b[%d;5;%dm", bg[i & 1], 232 + color[i]); last_color[i] = color[i]; bar += buf; } } bar += "\u258C"; } #else static char const table[] = {' ', '\xb0', '\xb1', '\xb2', '\xdb'}; bar += table[c]; #endif } bar += esc("0"); bar += "]"; return bar; } int peer_index(libtorrent::tcp::endpoint addr, std::vector const& peers) { using namespace libtorrent; std::vector::const_iterator i = std::find_if(peers.begin() , peers.end(), boost::bind(&peer_info::ip, _1) == addr); if (i == peers.end()) return -1; return i - peers.begin(); } // returns the number of lines printed int print_peer_info(std::string& out , std::vector const& peers, int max_lines) { using namespace libtorrent; int pos = 0; if (print_ip) out += "IP "; out += "progress down (total | peak ) up (total | peak ) sent-req tmo bsy rcv flags dn up source "; if (print_fails) out += "fail hshf "; if (print_send_bufs) out += "rq sndb rcvb q-bytes "; if (print_timers) out += "inactive wait timeout q-time "; out += " v disk ^ rtt "; if (print_block) out += "block-progress "; if (print_peer_rate) out += "peer-rate est.rec.rate "; out += "client \x1b[K\n"; ++pos; char str[500]; for (std::vector::const_iterator i = peers.begin(); i != peers.end(); ++i) { if (i->flags & (peer_info::handshake | peer_info::connecting)) continue; if (print_ip) { snprintf(str, sizeof(str), "%-30s ", (::print_endpoint(i->ip) + (i->flags & peer_info::utp_socket ? " [uTP]" : "") + (i->flags & peer_info::i2p_socket ? " [i2p]" : "") ).c_str()); out += str; } char temp[10]; snprintf(temp, sizeof(temp), "%d/%d" , i->download_queue_length , i->target_dl_queue_length); temp[7] = 0; char peer_progress[10]; snprintf(peer_progress, sizeof(peer_progress), "%.1f%%", i->progress_ppm / 10000.f); snprintf(str, sizeof(str) , "%s %s%s (%s|%s) %s%s (%s|%s) %s%7s %4d%4d%4d %s%s%s%s%s%s%s%s%s%s%s%s%s %s%s%s %s%s%s %s%s%s%s%s%s " , progress_bar(i->progress_ppm / 1000, 15, col_green, '#', '-', peer_progress).c_str() , esc("32"), add_suffix(i->down_speed, "/s").c_str() , add_suffix(i->total_download).c_str(), add_suffix(i->download_rate_peak, "/s").c_str() , esc("31"), add_suffix(i->up_speed, "/s").c_str(), add_suffix(i->total_upload).c_str() , add_suffix(i->upload_rate_peak, "/s").c_str(), esc("0") , temp // sent requests and target number of outstanding reqs. , i->timed_out_requests , i->busy_requests , i->upload_queue_length , color("I", (i->flags & peer_info::interesting)?col_white:col_blue).c_str() , color("C", (i->flags & peer_info::choked)?col_white:col_blue).c_str() , color("i", (i->flags & peer_info::remote_interested)?col_white:col_blue).c_str() , color("c", (i->flags & peer_info::remote_choked)?col_white:col_blue).c_str() , color("x", (i->flags & peer_info::supports_extensions)?col_white:col_blue).c_str() , color("o", (i->flags & peer_info::local_connection)?col_white:col_blue).c_str() , color("p", (i->flags & peer_info::on_parole)?col_white:col_blue).c_str() , color("O", (i->flags & peer_info::optimistic_unchoke)?col_white:col_blue).c_str() , color("S", (i->flags & peer_info::snubbed)?col_white:col_blue).c_str() , color("U", (i->flags & peer_info::upload_only)?col_white:col_blue).c_str() , color("e", (i->flags & peer_info::endgame_mode)?col_white:col_blue).c_str() , color("E", (i->flags & peer_info::rc4_encrypted)?col_white:(i->flags & peer_info::plaintext_encrypted)?col_cyan:col_blue).c_str() , color("h", (i->flags & peer_info::holepunched)?col_white:col_blue).c_str() , color("d", (i->read_state & peer_info::bw_disk)?col_white:col_blue).c_str() , color("l", (i->read_state & peer_info::bw_limit)?col_white:col_blue).c_str() , color("n", (i->read_state & peer_info::bw_network)?col_white:col_blue).c_str() , color("d", (i->write_state & peer_info::bw_disk)?col_white:col_blue).c_str() , color("l", (i->write_state & peer_info::bw_limit)?col_white:col_blue).c_str() , color("n", (i->write_state & peer_info::bw_network)?col_white:col_blue).c_str() , color("t", (i->source & peer_info::tracker)?col_white:col_blue).c_str() , color("p", (i->source & peer_info::pex)?col_white:col_blue).c_str() , color("d", (i->source & peer_info::dht)?col_white:col_blue).c_str() , color("l", (i->source & peer_info::lsd)?col_white:col_blue).c_str() , color("r", (i->source & peer_info::resume_data)?col_white:col_blue).c_str() , color("i", (i->source & peer_info::incoming)?col_white:col_blue).c_str()); out += str; if (print_fails) { snprintf(str, sizeof(str), "%3d %3d " , i->failcount, i->num_hashfails); out += str; } if (print_send_bufs) { snprintf(str, sizeof(str), "%2d %6d %6d%5dkB " , i->requests_in_buffer, i->used_send_buffer , i->used_receive_buffer , i->queue_bytes / 1000); out += str; } if (print_timers) { char req_timeout[20] = "-"; // timeout is only meaningful if there is at least one outstanding // request to the peer if (i->download_queue_length > 0) snprintf(req_timeout, sizeof(req_timeout), "%d", i->request_timeout); snprintf(str, sizeof(str), "%8d %4d %7s %6d " , int(total_seconds(i->last_active)) , int(total_seconds(i->last_request)) , req_timeout , int(total_seconds(i->download_queue_time))); out += str; } snprintf(str, sizeof(str), "%s|%s %5d " , add_suffix(i->pending_disk_bytes).c_str() , add_suffix(i->pending_disk_read_bytes).c_str() , i->rtt); out += str; if (print_block) { if (i->downloading_piece_index >= 0) { char buf[50]; snprintf(buf, sizeof(buf), "%d:%d", i->downloading_piece_index, i->downloading_block_index); out += progress_bar( i->downloading_progress * 1000 / i->downloading_total, 14, col_green, '-', '#', buf); } else { out += progress_bar(0, 14); } } if (print_peer_rate) { bool unchoked = (i->flags & peer_info::choked) == 0; snprintf(str, sizeof(str), " %s %s" , add_suffix(i->remote_dl_rate, "/s").c_str() , unchoked ? add_suffix(i->estimated_reciprocation_rate, "/s").c_str() : " "); out += str; } out += " "; if (i->flags & peer_info::handshake) { out += esc("31"); out += " waiting for handshake"; out += esc("0"); } else if (i->flags & peer_info::connecting) { out += esc("31"); out += " connecting to peer"; out += esc("0"); } else { out += " "; out += i->client; } out += "\x1b[K\n"; ++pos; if (pos >= max_lines) break; } return pos; } int listen_port = 6881; int allocation_mode = libtorrent::storage_mode_sparse; std::string save_path("."); int torrent_upload_limit = 0; int torrent_download_limit = 0; std::string monitor_dir; std::string bind_to_interface = ""; int poll_interval = 5; int max_connections_per_torrent = 50; bool seed_mode = false; bool stats_enabled = false; int cache_size = 1024; bool share_mode = false; bool disable_storage = false; bool quit = false; void signal_handler(int signo) { // make the main loop terminate quit = true; } // if non-empty, a peer that will be added to all torrents std::string peer; using boost::bind; std::string path_to_url(std::string f) { std::string ret = "file://" #ifdef TORRENT_WINDOWS "/" #endif ; static char const hex_chars[] = "0123456789abcdef"; static const char unreserved[] = "/-_!.~*()ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789"; // make sure the path is an absolute path if (!is_absolute_path(f)) { char cwd[TORRENT_MAX_PATH]; #if defined TORRENT_WINDOWS && !defined TORRENT_MINGW _getcwd(cwd, sizeof(cwd)); #else getcwd(cwd, sizeof(cwd)); #endif f = path_append(cwd, f); } for (int i = 0; i < int(f.size()); ++i) { #ifdef TORRENT_WINDOWS if (f[i] == '\\') ret.push_back('/'); else #endif if (std::strchr(unreserved, f[i]) != NULL) ret.push_back(f[i]); else { ret.push_back('%'); ret.push_back(hex_chars[boost::uint8_t(f[i]) >> 4]); ret.push_back(hex_chars[boost::uint8_t(f[i]) & 0xf]); } } return ret; } // monitored_dir is true if this torrent is added because // it was found in the directory that is monitored. If it // is, it should be remembered so that it can be removed // if it's no longer in that directory. void add_torrent(libtorrent::session& ses , handles_t& files , std::set& non_files , std::string torrent , int allocation_mode , std::string const& save_path , bool monitored_dir , int torrent_upload_limit , int torrent_download_limit) { using namespace libtorrent; static int counter = 0; printf("[%d] %s\n", counter++, torrent.c_str()); add_torrent_params p; if (seed_mode) p.flags |= add_torrent_params::flag_seed_mode; if (disable_storage) p.storage = disabled_storage_constructor; if (share_mode) p.flags |= add_torrent_params::flag_share_mode; std::string filename = path_append(save_path, path_append(".resume" , leaf_path(torrent) + ".resume")); error_code ec; load_file(filename, p.resume_data, ec); p.url = path_to_url(torrent); p.save_path = save_path; p.storage_mode = (storage_mode_t)allocation_mode; p.flags |= add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_duplicate_is_error; p.flags |= add_torrent_params::flag_auto_managed; p.userdata = (void*)strdup(torrent.c_str()); ses.async_add_torrent(p); files.insert(std::pair(torrent, torrent_handle())); } std::vector list_dir(std::string path , bool (*filter_fun)(std::string const&) , libtorrent::error_code& ec) { std::vector ret; #ifdef TORRENT_WINDOWS if (!path.empty() && path[path.size()-1] != '\\') path += "\\*"; else path += "*"; WIN32_FIND_DATAA fd; HANDLE handle = FindFirstFileA(path.c_str(), &fd); if (handle == INVALID_HANDLE_VALUE) { ec.assign(GetLastError(), boost::system::system_category()); return ret; } do { std::string p = fd.cFileName; if (filter_fun(p)) ret.push_back(p); } while (FindNextFileA(handle, &fd)); FindClose(handle); #else if (!path.empty() && path[path.size()-1] == '/') path.resize(path.size()-1); DIR* handle = opendir(path.c_str()); if (handle == 0) { ec.assign(errno, boost::system::system_category()); return ret; } struct dirent* de; while ((de = readdir(handle))) { std::string p = de->d_name; if (filter_fun(p)) ret.push_back(p); } closedir(handle); #endif return ret; } bool filter_fun(std::string const& p) { for (int i = p.size() - 1; i >= 0; --i) { if (p[i] == '/') break; #ifdef TORRENT_WINDOWS if (p[i] == '\\') break; #endif if (p[i] != '.') continue; return p.compare(i, 8, ".torrent") == 0; } return false; } void scan_dir(std::string const& dir_path , libtorrent::session& ses , handles_t& files , std::set& non_files , int allocation_mode , std::string const& save_path , int torrent_upload_limit , int torrent_download_limit) { std::set valid; using namespace libtorrent; error_code ec; std::vector ents = list_dir(dir_path, filter_fun, ec); if (ec) { fprintf(stderr, "failed to list directory: (%s : %d) %s\n" , ec.category().name(), ec.value(), ec.message().c_str()); return; } for (std::vector::iterator i = ents.begin() , end(ents.end()); i != end; ++i) { std::string file = path_append(dir_path, *i); handles_t::iterator k = files.find(file); if (k != files.end()) { valid.insert(file); continue; } // the file has been added to the dir, start // downloading it. add_torrent(ses, files, non_files, file, allocation_mode , save_path, true, torrent_upload_limit, torrent_download_limit); valid.insert(file); } // remove the torrents that are no longer in the directory for (handles_t::iterator i = files.begin(); !files.empty() && i != files.end();) { if (i->first.empty() || valid.find(i->first) != valid.end()) { ++i; continue; } torrent_handle& h = i->second; if (!h.is_valid()) { files.erase(i++); continue; } h.auto_managed(false); h.pause(); // the alert handler for save_resume_data_alert // will save it to disk h.save_resume_data(); ++num_outstanding_resume_data; files.erase(i++); } } char const* timestamp() { time_t t = std::time(0); tm* timeinfo = std::localtime(&t); static char str[200]; std::strftime(str, 200, "%b %d %X", timeinfo); return str; } void print_alert(libtorrent::alert const* a, std::string& str) { using namespace libtorrent; if (a->category() & alert::error_notification) { str += esc("31"); } else if (a->category() & (alert::peer_notification | alert::storage_notification)) { str += esc("33"); } str += "["; str += timestamp(); str += "] "; str += a->message(); str += esc("0"); if (g_log_file) fprintf(g_log_file, "[%s] %s\n", timestamp(), a->message().c_str()); } int save_file(std::string const& filename, std::vector& v) { FILE* f = fopen(filename.c_str(), "wb"); if (f == NULL) return -1; int w = fwrite(&v[0], 1, v.size(), f); fclose(f); if (w < 0) return -1; if (w != int(v.size())) return -3; return 0; } // returns true if the alert was handled (and should not be printed to the log) // returns false if the alert was not handled bool handle_alert(torrent_view& view, session_view& ses_view , libtorrent::session& ses, libtorrent::alert* a , handles_t& files, std::set& non_files) { using namespace libtorrent; if (session_stats_alert* s = alert_cast(a)) { ses_view.update_counters(s->values, sizeof(s->values)/sizeof(s->values[0]) , duration_cast(s->timestamp().time_since_epoch()).count()); return !stats_enabled; } #ifndef TORRENT_DISABLE_DHT if (dht_stats_alert* p = alert_cast(a)) { dht_active_requests = p->active_requests; dht_routing_table = p->routing_table; return true; } #endif #ifdef TORRENT_USE_OPENSSL if (torrent_need_cert_alert* p = alert_cast(a)) { torrent_handle h = p->handle; std::string base_name = path_append("certificates", to_hex(h.info_hash().to_string())); std::string cert = base_name + ".pem"; std::string priv = base_name + "_key.pem"; #ifdef TORRENT_WINDOWS struct ::_stat st; int ret = ::_stat(cert.c_str(), &st); if (ret < 0 || (st.st_mode & _S_IFREG) == 0) #else struct ::stat st; int ret = ::stat(cert.c_str(), &st); if (ret < 0 || (st.st_mode & S_IFREG) == 0) #endif { char msg[256]; snprintf(msg, sizeof(msg), "ERROR. could not load certificate %s: %s\n", cert.c_str(), strerror(errno)); if (g_log_file) fprintf(g_log_file, "[%s] %s\n", timestamp(), msg); return true; } #ifdef TORRENT_WINDOWS ret = ::_stat(priv.c_str(), &st); if (ret < 0 || (st.st_mode & _S_IFREG) == 0) #else ret = ::stat(priv.c_str(), &st); if (ret < 0 || (st.st_mode & S_IFREG) == 0) #endif { char msg[256]; snprintf(msg, sizeof(msg), "ERROR. could not load private key %s: %s\n", priv.c_str(), strerror(errno)); if (g_log_file) fprintf(g_log_file, "[%s] %s\n", timestamp(), msg); return true; } char msg[256]; snprintf(msg, sizeof(msg), "loaded certificate %s and key %s\n", cert.c_str(), priv.c_str()); if (g_log_file) fprintf(g_log_file, "[%s] %s\n", timestamp(), msg); h.set_ssl_certificate(cert, priv, "certificates/dhparams.pem", "1234"); h.resume(); } #endif // don't log every peer we try to connect to if (alert_cast(a)) return true; if (peer_disconnected_alert* pd = alert_cast(a)) { // ignore failures to connect and peers not responding with a // handshake. The peers that we successfully connect to and then // disconnect is more interesting. if (pd->operation == op_connect || pd->error == errors::timed_out_no_handshake) return true; } if (metadata_received_alert* p = alert_cast(a)) { // if we have a monitor dir, save the .torrent file we just received in it // also, add it to the files map, and remove it from the non_files list // to keep the scan dir logic in sync so it's not removed, or added twice torrent_handle h = p->handle; if (h.is_valid()) { boost::shared_ptr ti = h.torrent_file(); create_torrent ct(*ti); entry te = ct.generate(); std::vector buffer; bencode(std::back_inserter(buffer), te); sha1_hash hash = ti->info_hash(); std::string filename = ti->name() + "." + to_hex(hash.to_string()) + ".torrent"; filename = path_append(monitor_dir, filename); save_file(filename, buffer); files.insert(std::pair(filename, h)); hash_to_filename[hash] = filename; non_files.erase(h); } } else if (add_torrent_alert* p = alert_cast(a)) { std::string filename; if (p->params.userdata) { filename = (char*)p->params.userdata; free(p->params.userdata); } if (p->error) { fprintf(stderr, "failed to add torrent: %s %s\n", filename.c_str() , p->error.message().c_str()); } else { torrent_handle h = p->handle; if (!filename.empty()) files[filename] = h; else non_files.insert(h); h.set_max_connections(max_connections_per_torrent); h.set_max_uploads(-1); h.set_upload_limit(torrent_upload_limit); h.set_download_limit(torrent_download_limit); // if we have a peer specified, connect to it if (!peer.empty()) { char* port = (char*) strrchr((char*)peer.c_str(), ':'); if (port != NULL) { *port++ = 0; char const* ip = peer.c_str(); int peer_port = atoi(port); error_code ec; if (peer_port > 0) h.connect_peer(tcp::endpoint(address::from_string(ip, ec), peer_port)); } } sha1_hash info_hash; if (p->params.ti) { info_hash = p->params.ti->info_hash(); } else if (!p->params.info_hash.is_all_zeros()) { info_hash = p->params.info_hash; } else { info_hash = h.info_hash(); } hash_to_filename.insert(std::make_pair(info_hash, filename)); } } else if (torrent_finished_alert* p = alert_cast(a)) { p->handle.set_max_connections(max_connections_per_torrent / 2); // write resume data for the finished torrent // the alert handler for save_resume_data_alert // will save it to disk torrent_handle h = p->handle; h.save_resume_data(); ++num_outstanding_resume_data; } else if (save_resume_data_alert* p = alert_cast(a)) { --num_outstanding_resume_data; torrent_handle h = p->handle; TORRENT_ASSERT(p->resume_data); if (p->resume_data) { std::vector out; bencode(std::back_inserter(out), *p->resume_data); torrent_status st = h.status(torrent_handle::query_save_path); save_file(path_append(st.save_path, path_append(".resume", leaf_path( hash_to_filename[st.info_hash]) + ".resume")), out); if (h.is_valid() && non_files.find(h) == non_files.end() && std::find_if(files.begin(), files.end() , boost::bind(&handles_t::value_type::second, _1) == h) == files.end()) ses.remove_torrent(h); } } else if (save_resume_data_failed_alert* p = alert_cast(a)) { --num_outstanding_resume_data; torrent_handle h = p->handle; if (h.is_valid()) { fprintf(stderr, "FAILED TO SAVE RESUME DATA: %s\n" , h.status().name.c_str()); } if (h.is_valid() && non_files.find(h) == non_files.end() && std::find_if(files.begin(), files.end() , boost::bind(&handles_t::value_type::second, _1) == h) == files.end()) ses.remove_torrent(h); } else if (torrent_paused_alert* p = alert_cast(a)) { // write resume data for the finished torrent // the alert handler for save_resume_data_alert // will save it to disk torrent_handle h = p->handle; h.save_resume_data(); ++num_outstanding_resume_data; } else if (state_update_alert* p = alert_cast(a)) { view.update_torrents(p->status); return true; } return false; } void print_piece(libtorrent::partial_piece_info* pp , libtorrent::cached_piece_info* cs , std::vector const& peers , torrent_status const* ts , std::string& out) { using namespace libtorrent; char str[1024]; assert(pp == 0 || cs == 0 || cs->piece == pp->piece_index); int piece = pp ? pp->piece_index : cs->piece; int num_blocks = pp ? pp->blocks_in_piece : cs->blocks.size(); snprintf(str, sizeof(str), "%5d:[", piece); out += str; char const* last_color = 0; for (int j = 0; j < num_blocks; ++j) { int index = pp ? peer_index(pp->blocks[j].peer(), peers) % 36 : -1; char chr = '+'; if (index >= 0) chr = (index < 10)?'0' + index:'A' + index - 10; bool snubbed = index >= 0 ? peers[index].flags & peer_info::snubbed : false; char const* color = ""; if (pp == 0) { color = cs->blocks[j] ? esc("34;7") : esc("0"); chr = ' '; } else { if (cs && cs->blocks[j] && pp->blocks[j].state != block_info::finished) color = esc("36;7"); else if (pp->blocks[j].bytes_progress > 0 && pp->blocks[j].state == block_info::requested) { if (pp->blocks[j].num_peers > 1) color = esc("1;7"); else color = snubbed ? esc("35;7") : esc("33;7"); chr = '0' + (pp->blocks[j].bytes_progress * 10 / pp->blocks[j].block_size); } else if (pp->blocks[j].state == block_info::finished) color = esc("32;7"); else if (pp->blocks[j].state == block_info::writing) color = esc("36;7"); else if (pp->blocks[j].state == block_info::requested) color = snubbed ? esc("35;7") : esc("0"); else { color = esc("0"); chr = ' '; } } if (last_color == 0 || strcmp(last_color, color) != 0) { snprintf(str, sizeof(str), "%s%c", color, chr); out += str; } else out += chr; last_color = color; } out += esc("0"); out += "]"; /* char const* cache_kind_str[] = {"read", "write", "read-volatile"}; snprintf(str, sizeof(str), " %3d cache age: %-5.1f state: %s%s\n" , cs ? cs->next_to_hash : 0 , cs ? (total_milliseconds(clock_type::now() - cs->last_use) / 1000.f) : 0.f , cs ? cache_kind_str[cs->kind] : "N/A" , ts && ts->pieces.size() ? (ts->pieces[piece] ? " have" : " dont-have") : ""); out += str; */ } int main(int argc, char* argv[]) { #ifndef _WIN32 // sets the terminal to single-character mode // and resets when destructed set_keypress s; #endif if (argc == 1) { fprintf(stderr, "usage: client_test [OPTIONS] [TORRENT|MAGNETURL]\n\n" "OPTIONS:\n" "\n CLIENT OPTIONS\n" " -f logs all events to the given file\n" " -s sets the save path for downloads\n" " -m sets the .torrent monitor directory\n" " -t sets the scan interval of the monitor dir\n" " -F sets the UI refresh rate. This is the number of\n" " milliseconds between screen refreshes.\n" " -k enable high performance settings. This overwrites any other\n" " previous command line options, so be sure to specify this first\n" " -G Add torrents in seed-mode (i.e. assume all pieces\n" " are present and check hashes on-demand)\n" " -E specify how many disk I/O threads to use\n" " -O print session stats counters to the log\n" #ifdef TORRENT_UTP_LOG_ENABLE " -q Enable uTP transport-level verbose logging\n" #endif "\n BITTORRENT OPTIONS\n" " -c sets the max number of connections\n" " -T sets the max number of connections per torrent\n" " -U sets per-torrent upload rate\n" " -D sets per-torrent download rate\n" " -d limits the download rate\n" " -u limits the upload rate\n" " -S limits the upload slots\n" " -A allowed pieces set size\n" " -H Don't start DHT\n" " -X Don't start local peer discovery\n" " -n announce to trackers in all tiers\n" " -W Set the max number of peers to keep in the peer list\n" " -B sets the peer timeout\n" " -Q enables share mode. Share mode attempts to maximize\n" " share ratio rather than downloading\n" " -K enable piece suggestions of read cache\n" " -r connect to specified peer\n" #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) " -e force encrypted bittorrent connections\n" #endif "\n QUEING OPTIONS\n" " -v Set the max number of active downloads\n" " -^ Set the max number of active seeds\n" "\n NETWORK OPTIONS\n" " -p sets the listen port\n" " -w sets the retry time for failed web seeds\n" " -x loads an emule IP-filter file\n" " -P Use the specified SOCKS5 proxy\n" " -L Use the specified username and password for the\n" " proxy specified by -P\n" " -h allow multiple connections from the same IP\n" " -M Disable TCP/uTP bandwidth balancing\n" " -N Do not attempt to use UPnP and NAT-PMP to forward ports\n" " -Y Rate limit local peers\n" " -y Disable TCP connections (disable outgoing TCP and reject\n" " incoming TCP connections)\n" " -J Disable uTP connections (disable outgoing uTP and reject\n" " incoming uTP connections)\n" " -b sets IP of the interface to bind the\n" " listen socket to\n" " -I sets the IP of the interface to bind\n" " outgoing peer connections to\n" #if TORRENT_USE_I2P " -i the hostname to an I2P SAM bridge to use\n" #endif " -l sets the listen socket queue size\n" "\n DISK OPTIONS\n" " -a sets the allocation mode. [sparse|allocate]\n" " -R number of blocks per read cache line\n" " -C sets the max cache size. Specified in 16kB blocks\n" " -j disable disk read-ahead\n" " -z disable piece hash checks (used for benchmarking)\n" " -0 disable disk I/O, read garbage and don't flush to disk\n" "\n\n" "TORRENT is a path to a .torrent file\n" "MAGNETURL is a magnet link\n" "URL is a url to a torrent file\n" "\n" "Example for running benchmark:\n\n" " client_test -k -z -N -h -H -M -l 2000 -S 1000 -T 1000 -c 1000 -O test.torrent\n"); ; return 0; } using namespace libtorrent; namespace lt = libtorrent; torrent_view view; session_view ses_view; settings_pack settings; settings.set_int(settings_pack::cache_size, cache_size); settings.set_int(settings_pack::active_loaded_limit, 20); settings.set_int(settings_pack::choking_algorithm, settings_pack::rate_based_choker); int refresh_delay = 500; bool rate_limit_locals = false; std::deque events; time_point next_dir_scan = clock_type::now(); // the string is the filename of the .torrent file, but only if // it was added through the directory monitor. It is used to // be able to remove torrents that were added via the directory // monitor when they're not in the directory anymore. handles_t files; // torrents that were not added via the monitor dir std::set non_files; // load the torrents given on the commandline std::vector magnet_links; std::vector torrents; ip_filter loaded_ip_filter; for (int i = 1; i < argc; ++i) { if (argv[i][0] != '-') { // match it against the @ format if (strlen(argv[i]) > 45 && ::is_hex(argv[i], 40) && (strncmp(argv[i] + 40, "@http://", 8) == 0 || strncmp(argv[i] + 40, "@udp://", 7) == 0)) { sha1_hash info_hash; from_hex(argv[i], 40, (char*)&info_hash[0]); add_torrent_params p; if (seed_mode) p.flags |= add_torrent_params::flag_seed_mode; if (disable_storage) p.storage = disabled_storage_constructor; if (share_mode) p.flags |= add_torrent_params::flag_share_mode; p.trackers.push_back(argv[i] + 41); p.info_hash = info_hash; p.save_path = save_path; p.storage_mode = (storage_mode_t)allocation_mode; p.flags |= add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_duplicate_is_error; p.flags |= add_torrent_params::flag_auto_managed; magnet_links.push_back(p); continue; } torrents.push_back(argv[i]); continue; } // if there's a flag but no argument following, ignore it if (argc == i) continue; char const* arg = argv[i+1]; if (arg == NULL) arg = ""; switch (argv[i][1]) { case 'f': g_log_file = fopen(arg, "w+"); break; case 'h': settings.set_bool(settings_pack::allow_multiple_connections_per_ip, true); --i; break; case 'p': listen_port = atoi(arg); break; case 'k': high_performance_seed(settings); --i; break; case 'j': settings.set_bool(settings_pack::use_disk_read_ahead, false); --i; break; case 'z': settings.set_bool(settings_pack::disable_hash_checks, true); --i; break; case 'K': settings.set_int(settings_pack::suggest_mode, settings_pack::suggest_read_cache); --i; break; case 'B': settings.set_int(settings_pack::peer_timeout, atoi(arg)); break; case 'n': settings.set_bool(settings_pack::announce_to_all_tiers, true); --i; break; case 'G': seed_mode = true; --i; break; case 'O': stats_enabled = true; --i; break; case 'E': settings.set_int(settings_pack::aio_threads, atoi(arg)); break; case 'd': settings.set_int(settings_pack::download_rate_limit, atoi(arg) * 1000); break; case 'u': settings.set_int(settings_pack::upload_rate_limit, atoi(arg) * 1000); break; case 'S': settings.set_int(settings_pack::unchoke_slots_limit, atoi(arg)); settings.set_int(settings_pack::choking_algorithm, settings_pack::fixed_slots_choker); break; case 'a': if (strcmp(arg, "allocate") == 0) allocation_mode = storage_mode_allocate; else if (strcmp(arg, "sparse") == 0) allocation_mode = storage_mode_sparse; break; #ifdef TORRENT_UTP_LOG_ENABLE case 'q': libtorrent::set_utp_stream_logging(true); break; #endif case 's': save_path = arg; break; case 'U': torrent_upload_limit = atoi(arg) * 1000; break; case 'D': torrent_download_limit = atoi(arg) * 1000; break; case 'm': monitor_dir = arg; break; case 'Q': share_mode = true; --i; break; case 'b': bind_to_interface = arg; break; case 'w': settings.set_int(settings_pack::urlseed_wait_retry, atoi(arg)); break; case 't': poll_interval = atoi(arg); break; case 'F': refresh_delay = atoi(arg); break; case 'H': settings.set_bool(settings_pack::enable_dht, false); --i; break; case 'l': settings.set_int(settings_pack::listen_queue_size, atoi(arg)); break; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) case 'e': { settings.set_int(settings_pack::out_enc_policy, settings_pack::pe_forced); settings.set_int(settings_pack::in_enc_policy, settings_pack::pe_forced); settings.set_int(settings_pack::allowed_enc_level, settings_pack::pe_rc4); settings.set_bool(settings_pack::prefer_rc4, true); --i; break; } #endif case 'W': settings.set_int(settings_pack::max_peerlist_size, atoi(arg)); settings.set_int(settings_pack::max_paused_peerlist_size, atoi(arg) / 2); break; case 'x': { FILE* filter = fopen(arg, "r"); if (filter) { unsigned int a,b,c,d,e,f,g,h, flags; while (fscanf(filter, "%u.%u.%u.%u - %u.%u.%u.%u %u\n", &a, &b, &c, &d, &e, &f, &g, &h, &flags) == 9) { address_v4 start((a << 24) + (b << 16) + (c << 8) + d); address_v4 last((e << 24) + (f << 16) + (g << 8) + h); if (flags <= 127) flags = ip_filter::blocked; else flags = 0; loaded_ip_filter.add_rule(start, last, flags); } fclose(filter); } } break; case 'c': settings.set_int(settings_pack::connections_limit, atoi(arg)); break; case 'T': max_connections_per_torrent = atoi(arg); break; #if TORRENT_USE_I2P case 'i': { settings.set_str(settings_pack::i2p_hostname, arg); settings.set_int(settings_pack::i2p_port, 7656); settings.set_int(settings_pack::proxy_type, settings_pack::i2p_proxy); break; } #endif // TORRENT_USE_I2P case 'C': cache_size = atoi(arg); settings.set_int(settings_pack::cache_size, cache_size); settings.set_int(settings_pack::cache_buffer_chunk_size, 0); break; case 'A': settings.set_int(settings_pack::allowed_fast_set_size, atoi(arg)); break; case 'R': settings.set_int(settings_pack::read_cache_line_size, atoi(arg)); break; case 'M': settings.set_int(settings_pack::mixed_mode_algorithm, settings_pack::prefer_tcp); --i; break; case 'y': settings.set_bool(settings_pack::enable_outgoing_tcp, false); settings.set_bool(settings_pack::enable_incoming_tcp, false); --i; break; case 'J': settings.set_bool(settings_pack::enable_outgoing_utp, false); settings.set_bool(settings_pack::enable_incoming_utp, false); --i; break; case 'r': peer = arg; break; case 'P': { char* port = (char*) strrchr(arg, ':'); if (port == 0) { fprintf(stderr, "invalid proxy hostname, no port found\n"); break; } *port++ = 0; settings.set_str(settings_pack::proxy_hostname, arg); settings.set_int(settings_pack::proxy_port, atoi(port)); if (atoi(port) == 0) { fprintf(stderr, "invalid proxy port\n"); break; } if (settings.get_int(settings_pack::proxy_type) == settings_pack::none) settings.set_int(settings_pack::proxy_type, settings_pack::socks5); } break; case 'L': { char* pw = (char*) strchr(arg, ':'); if (pw == 0) { fprintf(stderr, "invalid proxy username and password specified\n"); break; } *pw++ = 0; settings.set_str(settings_pack::proxy_username, arg); settings.set_str(settings_pack::proxy_password, pw); settings.set_int(settings_pack::proxy_type, settings_pack::socks5_pw); } break; case 'I': settings.set_str(settings_pack::outgoing_interfaces, arg); break; case 'N': settings.set_bool(settings_pack::enable_upnp, false); settings.set_bool(settings_pack::enable_natpmp, false); --i; break; case 'Y': { --i; rate_limit_locals = true; break; } case 'X': settings.set_bool(settings_pack::enable_lsd, false); --i; break; case 'v': settings.set_int(settings_pack::active_downloads, atoi(arg)); settings.set_int(settings_pack::active_limit, atoi(arg) * 2); break; case '^': settings.set_int(settings_pack::active_seeds, atoi(arg)); settings.set_int(settings_pack::active_limit, atoi(arg) * 2); break; case '0': disable_storage = true; --i; } ++i; // skip the argument } // create directory for resume files #ifdef TORRENT_WINDOWS int ret = _mkdir(path_append(save_path, ".resume").c_str()); #else int ret = mkdir(path_append(save_path, ".resume").c_str(), 0777); #endif if (ret < 0) fprintf(stderr, "failed to create resume file directory: (%d) %s\n" , errno, strerror(errno)); if (bind_to_interface.empty()) bind_to_interface = "0.0.0.0"; char iface_str[100]; snprintf(iface_str, sizeof(iface_str), "%s:%d", bind_to_interface.c_str() , listen_port); settings.set_str(settings_pack::listen_interfaces, iface_str); settings.set_str(settings_pack::user_agent, "client_test/" LIBTORRENT_VERSION); settings.set_int(settings_pack::alert_mask, alert::all_categories & ~(alert::dht_notification + alert::piece_progress_notification + alert::block_progress_notification + alert::progress_notification + alert::stats_notification + alert::session_log_notification + alert::torrent_log_notification + alert::peer_log_notification + alert::dht_log_notification + alert::picker_log_notification )); libtorrent::session ses(settings); if (rate_limit_locals) { ip_filter pcf; pcf.add_rule(address_v4::from_string("0.0.0.0") , address_v4::from_string("255.255.255.255"), 1 << lt::session::global_peer_class_id); #if TORRENT_USE_IPV6 pcf.add_rule(address_v6::from_string("::") , address_v6::from_string("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), 1); #endif ses.set_peer_class_filter(pcf); } ses.set_ip_filter(loaded_ip_filter); error_code ec; #ifndef TORRENT_DISABLE_DHT dht_settings dht; dht.privacy_lookups = true; ses.set_dht_settings(dht); std::vector in; if (load_file(".ses_state", in, ec) == 0) { bdecode_node e; if (bdecode(&in[0], &in[0] + in.size(), e, ec) == 0) ses.load_state(e, session::save_dht_state); } #endif for (std::vector::iterator i = magnet_links.begin() , end(magnet_links.end()); i != end; ++i) { ses.async_add_torrent(*i); } for (std::vector::iterator i = torrents.begin() , end(torrents.end()); i != end; ++i) { if (std::strstr(i->c_str(), "magnet:") == i->c_str()) { add_torrent_params p; if (seed_mode) p.flags |= add_torrent_params::flag_seed_mode; if (disable_storage) p.storage = disabled_storage_constructor; if (share_mode) p.flags |= add_torrent_params::flag_share_mode; p.save_path = save_path; p.storage_mode = (storage_mode_t)allocation_mode; p.url = *i; std::vector buf; add_torrent_params tmp; ec.clear(); parse_magnet_uri(*i, tmp, ec); if (ec) continue; std::string filename = path_append(save_path, path_append(".resume" , to_hex(tmp.info_hash.to_string()) + ".resume")); load_file(filename, p.resume_data, ec); printf("adding URL: %s\n", i->c_str()); ses.async_add_torrent(p); continue; } // if it's a torrent file, open it as usual add_torrent(ses, files, non_files, i->c_str() , allocation_mode, save_path, false , torrent_upload_limit, torrent_download_limit); } // main loop std::vector peers; std::vector queue; int tick = 0; #ifndef _WIN32 signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); #endif while (!quit) { ++tick; ses.post_torrent_updates(); ses.post_session_stats(); ses.post_dht_stats(); int terminal_width = 80; int terminal_height = 50; terminal_size(&terminal_width, &terminal_height); view.set_size(terminal_width, terminal_height / 3); ses_view.set_pos(terminal_height / 3); int c = 0; if (sleep_and_input(&c, refresh_delay)) { #ifdef _WIN32 #define ESCAPE_SEQ 224 #define LEFT_ARROW 75 #define RIGHT_ARROW 77 #define UP_ARROW 72 #define DOWN_ARROW 80 #else #define ESCAPE_SEQ 27 #define LEFT_ARROW 68 #define RIGHT_ARROW 67 #define UP_ARROW 65 #define DOWN_ARROW 66 #endif torrent_handle h = view.get_active_handle(); if (c == EOF) { break; } do { if (c == ESCAPE_SEQ) { // escape code, read another character #ifdef _WIN32 int c = _getch(); #else int c = getc(stdin); if (c == EOF) { break; } if (c != '[') continue; c = getc(stdin); #endif if (c == EOF) break; if (c == LEFT_ARROW) { // arrow left int filter = view.filter(); if (filter > 0) { --filter; view.set_filter(filter); h = view.get_active_handle(); } } else if (c == RIGHT_ARROW) { // arrow right int filter = view.filter(); if (filter < torrent_view::torrents_max - 1) { ++filter; view.set_filter(filter); h = view.get_active_handle(); } } else if (c == UP_ARROW) { // arrow up view.arrow_up(); h = view.get_active_handle(); } else if (c == DOWN_ARROW) { // arrow down view.arrow_down(); h = view.get_active_handle(); } } if (c == ' ') { if (ses.is_paused()) ses.resume(); else ses.pause(); } // add magnet link if (c == 'm') { char url[4096]; puts("Enter magnet link:\n"); scanf("%4095s", url); add_torrent_params p; if (seed_mode) p.flags |= add_torrent_params::flag_seed_mode; if (disable_storage) p.storage = disabled_storage_constructor; if (share_mode) p.flags |= add_torrent_params::flag_share_mode; p.save_path = save_path; p.storage_mode = (storage_mode_t)allocation_mode; p.url = url; std::vector buf; if (std::strstr(url, "magnet:") == url) { add_torrent_params tmp; parse_magnet_uri(url, tmp, ec); if (ec) continue; std::string filename = path_append(save_path, path_append(".resume" , to_hex(tmp.info_hash.to_string()) + ".resume")); load_file(filename, p.resume_data, ec); } printf("adding URL: %s\n", url); ses.async_add_torrent(p); } if (c == 'q') { quit = true; break; } if (c == 'W' && h.is_valid()) { std::set seeds = h.url_seeds(); for (std::set::iterator i = seeds.begin() , end(seeds.end()); i != end; ++i) { h.remove_url_seed(*i); } seeds = h.http_seeds(); for (std::set::iterator i = seeds.begin() , end(seeds.end()); i != end; ++i) { h.remove_http_seed(*i); } } if (c == 'D' && h.is_valid()) { torrent_status const& st = view.get_active_torrent(); printf("\n\nARE YOU SURE YOU WANT TO DELETE THE FILES FOR '%s'. THIS OPERATION CANNOT BE UNDONE. (y/N)" , st.name.c_str()); char response = 'n'; scanf("%c", &response); if (response == 'y') { // also delete the .torrent file from the torrent directory handles_t::iterator i = std::find_if(files.begin(), files.end() , boost::bind(&handles_t::value_type::second, _1) == st.handle); if (i != files.end()) { error_code ec; std::string path; if (is_absolute_path(i->first)) path = i->first; else path = path_append(monitor_dir, i->first); if (::remove(path.c_str()) < 0) printf("failed to delete .torrent file: %s\n", ec.message().c_str()); files.erase(i); } if (st.handle.is_valid()) ses.remove_torrent(st.handle, lt::session::delete_files); } } if (c == 'j' && h.is_valid()) { h.force_recheck(); } if (c == 'r' && h.is_valid()) { h.force_reannounce(); } if (c == 's' && h.is_valid()) { torrent_status const& ts = view.get_active_torrent(); h.set_sequential_download(!ts.sequential_download); } if (c == 'R') { // save resume data for all torrents std::vector torrents; ses.get_torrent_status(&torrents, &yes, 0); for (std::vector::iterator i = torrents.begin() , end(torrents.end()); i != end; ++i) { if (i->need_save_resume) { i->handle.save_resume_data(); ++num_outstanding_resume_data; } } } if (c == 'o' && h.is_valid()) { torrent_status const& ts = view.get_active_torrent(); int num_pieces = ts.num_pieces; if (num_pieces > 300) num_pieces = 300; for (int i = 0; i < num_pieces; ++i) { h.set_piece_deadline(i, (i+5) * 1000, torrent_handle::alert_when_available); } } if (c == 'v' && h.is_valid()) { h.scrape_tracker(); } if (c == 'p' && h.is_valid()) { torrent_status const& ts = view.get_active_torrent(); if (!ts.auto_managed && ts.paused) { h.auto_managed(true); } else { h.auto_managed(false); h.pause(torrent_handle::graceful_pause); } } // toggle force-start if (c == 'k' && h.is_valid()) { torrent_status const& ts = view.get_active_torrent(); h.auto_managed(!ts.auto_managed); if (ts.auto_managed && ts.paused) h.resume(); } if (c == 'c' && h.is_valid()) { h.clear_error(); } // toggle displays if (c == 't') print_trackers = !print_trackers; if (c == 'i') print_peers = !print_peers; if (c == 'l') print_log = !print_log; if (c == 'd') print_downloads = !print_downloads; if (c == 'y') print_matrix = !print_matrix; if (c == 'f') print_file_progress = !print_file_progress; if (c == 'P') show_pad_files = !show_pad_files; if (c == 'g') show_dht_status = !show_dht_status; if (c == 'u') ses_view.print_utp_stats(!ses_view.print_utp_stats()); if (c == 'x') print_disk_stats = !print_disk_stats; // toggle columns if (c == '1') print_ip = !print_ip; if (c == '3') print_timers = !print_timers; if (c == '4') print_block = !print_block; if (c == '5') print_peer_rate = !print_peer_rate; if (c == '6') print_fails = !print_fails; if (c == '7') print_send_bufs = !print_send_bufs; if (c == 'C') { cache_size = (cache_size == 0) ? -1 : 0; settings_pack p; p.set_int(settings_pack::cache_size, cache_size); ses.apply_settings(p); } if (c == 'h') { clear_screen(); set_cursor_pos(0,0); print( "HELP SCREEN (press any key to dismiss)\n\n" "CLIENT OPTIONS\n" "[q] quit client [m] add magnet link\n" "\n" "TORRENT ACTIONS\n" "[p] pause/resume selected torrent [C] toggle disk cache\n" "[s] toggle sequential download [j] force recheck\n" "[space] toggle session pause [c] clear error\n" "[v] scrape [D] delete torrent and data\n" "[r] force reannounce [R] save resume data for all torrents\n" "[o] set piece deadlines (sequential dl) [P] toggle auto-managed\n" "[k] toggle force-started [W] remove all web seeds\n" "\n" "DISPLAY OPTIONS\n" "left/right arrow keys: select torrent filter\n" "up/down arrow keys: select torrent\n" "[i] toggle show peers [d] toggle show downloading pieces\n" "[u] show uTP stats [f] toggle show files\n" "[g] show DHT [x] toggle disk cache stats\n" "[t] show trackers [l] toggle show log\n" "[P] show pad files (in file list) [y] toggle show piece matrix\n" "\n" "COLUMN OPTIONS\n" "[1] toggle IP column [2]\n" "[3] toggle timers column [4] toggle block progress column\n" "[5] toggle peer rate column [6] toggle failures column\n" "[7] toggle send buffers column\n" ); int tmp; while (sleep_and_input(&tmp, 500) == false); } } while (sleep_and_input(&c, 0)); if (c == 'q') { quit = true; break; } } // loop through the alert queue to see if anything has happened. std::vector alerts; ses.pop_alerts(&alerts); std::string now = timestamp(); for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { TORRENT_TRY { if (!::handle_alert(view, ses_view, ses, *i, files, non_files)) { // if we didn't handle the alert, print it to the log std::string event_string; print_alert(*i, event_string); events.push_back(event_string); if (events.size() >= 20) events.pop_front(); } } TORRENT_CATCH(std::exception& e) {} } alerts.clear(); std::string out; char str[500]; int pos = view.height() + ses_view.height(); set_cursor_pos(0, pos); int cache_flags = print_downloads ? 0 : lt::session::disk_cache_no_pieces; torrent_handle h = view.get_active_handle(); cache_status cs; ses.get_cache_info(&cs, h, cache_flags); #ifndef TORRENT_DISABLE_DHT if (show_dht_status) { // TODO: 3 expose these counters as performance counters /* snprintf(str, sizeof(str), "DHT nodes: %d DHT cached nodes: %d " "total DHT size: %" PRId64 " total observers: %d\n" , sess_stat.dht_nodes, sess_stat.dht_node_cache, sess_stat.dht_global_nodes , sess_stat.dht_total_allocations); out += str; */ int bucket = 0; for (std::vector::iterator i = dht_routing_table.begin() , end(dht_routing_table.end()); i != end; ++i, ++bucket) { char const* progress_bar = "################################" "################################" "################################" "################################"; char const* short_progress_bar = "--------"; snprintf(str, sizeof(str) , "%3d [%3d, %d] %s%s\x1b[K\n" , bucket, i->num_nodes, i->num_replacements , progress_bar + (128 - i->num_nodes) , short_progress_bar + (8 - (std::min)(8, i->num_replacements))); out += str; pos += 1; } for (std::vector::iterator i = dht_active_requests.begin() , end(dht_active_requests.end()); i != end; ++i) { snprintf(str, sizeof(str) , " %10s [limit: %2d] " "in-flight: %-2d " "left: %-3d " "1st-timeout: %-2d " "timeouts: %-2d " "responses: %-2d " "last_sent: %-2d\x1b[K\n" , i->type , i->branch_factor , i->outstanding_requests , i->nodes_left , i->first_timeout , i->timeouts , i->responses , i->last_sent); out += str; pos += 1; } } #endif if (h.is_valid()) { torrent_status const& s = view.get_active_torrent(); print((piece_bar(s.pieces, 126) + "\x1b[K\n").c_str()); pos += 1; if ((print_downloads && s.state != torrent_status::seeding) || print_peers) h.get_peer_info(peers); if (print_peers && !peers.empty()) pos += print_peer_info(out, peers, terminal_height - pos - 2); if (print_trackers) { snprintf(str, sizeof(str), "next_announce: %4" PRId64 " | current tracker: %s\x1b[K\n" , boost::int64_t(duration_cast(s.next_announce).count()) , s.current_tracker.c_str()); out += str; pos += 1; std::vector tr = h.trackers(); time_point now = clock_type::now(); for (std::vector::iterator i = tr.begin() , end(tr.end()); i != end; ++i) { if (pos + 1 >= terminal_height) break; snprintf(str, sizeof(str), "%2d %-55s fails: %-3d (%-3d) %s %s %5d \"%s\" %s\x1b[K\n" , i->tier, i->url.c_str(), i->fails, i->fail_limit, i->verified?"OK ":"- " , i->updating?"updating" :to_string(int(total_seconds(i->next_announce - now)), 8).c_str() , int(i->min_announce > now ? total_seconds(i->min_announce - now) : 0) , i->last_error ? i->last_error.message().c_str() : "" , i->message.c_str()); out += str; pos += 1; } } if (print_matrix) { int height = 0; print(piece_matrix(s.pieces, terminal_width, &height).c_str()); pos += height; } if (print_downloads) { h.get_download_queue(queue); std::sort(queue.begin(), queue.end(), boost::bind(&partial_piece_info::piece_index, _1) < boost::bind(&partial_piece_info::piece_index, _2)); std::sort(cs.pieces.begin(), cs.pieces.end(), boost::bind(&cached_piece_info::piece, _1) > boost::bind(&cached_piece_info::piece, _2)); int p = 0; // this is horizontal position for (std::vector::iterator i = cs.pieces.begin(); i != cs.pieces.end(); ++i) { if (pos + 3 >= terminal_height) break; partial_piece_info* pp = 0; partial_piece_info tmp; tmp.piece_index = i->piece; std::vector::iterator ppi = std::lower_bound(queue.begin(), queue.end(), tmp , boost::bind(&partial_piece_info::piece_index, _1) < boost::bind(&partial_piece_info::piece_index, _2)); if (ppi != queue.end() && ppi->piece_index == i->piece) pp = &*ppi; print_piece(pp, &*i, peers, &s, out); int num_blocks = pp ? pp->blocks_in_piece : i->blocks.size(); p += num_blocks + 8; bool continuous_mode = 8 + num_blocks > terminal_width; if (continuous_mode) { while (p > terminal_width) { p -= terminal_width; ++pos; } } else if (p + num_blocks + 8 > terminal_width) { out += "\x1b[K\n"; pos += 1; p = 0; } if (pp) queue.erase(ppi); } for (std::vector::iterator i = queue.begin() , end(queue.end()); i != end; ++i) { if (pos + 3 >= terminal_height) break; print_piece(&*i, 0, peers, &s, out); int num_blocks = i->blocks_in_piece; p += num_blocks + 8; bool continuous_mode = 8 + num_blocks > terminal_width; if (continuous_mode) { while (p > terminal_width) { p -= terminal_width; ++pos; } } else if (p + num_blocks + 8 > terminal_width) { out += "\x1b[K\n"; pos += 1; p = 0; } } if (p != 0) { out += "\x1b[K\n"; pos += 1; } snprintf(str, sizeof(str), "%s %s read cache | %s %s downloading | %s %s cached | %s %s flushed | %s %s snubbed\x1b[K\n" , esc("34;7"), esc("0") // read cache , esc("33;7"), esc("0") // downloading , esc("36;7"), esc("0") // cached , esc("32;7"), esc("0") // flushed , esc("35;7"), esc("0") // snubbed ); out += str; pos += 1; } if (print_file_progress && s.has_metadata) { std::vector file_progress; h.file_progress(file_progress); std::vector file_status; h.file_status(file_status); std::vector file_prio = h.file_priorities(); std::vector::iterator f = file_status.begin(); boost::shared_ptr ti = h.torrent_file(); int p = 0; // this is horizontal position for (int i = 0; i < ti->num_files(); ++i) { if (pos + 1 >= terminal_height) break; bool pad_file = ti->files().pad_file_at(i); if (pad_file) { if (show_pad_files) { snprintf(str, sizeof(str), "\x1b[34m%-70s %s\x1b[0m\x1b[K\n" , ti->files().file_name(i).c_str() , add_suffix(ti->files().file_size(i)).c_str()); out += str; pos += 1; } continue; } int progress = ti->files().file_size(i) > 0 ?file_progress[i] * 1000 / ti->files().file_size(i):1000; bool complete = file_progress[i] == ti->files().file_size(i); std::string title = ti->files().file_name(i); if (!complete) { snprintf(str, sizeof(str), " (%.1f%%)", progress / 10.f); title += str; } if (f != file_status.end() && f->file_index == i) { title += " [ "; if ((f->open_mode & file::rw_mask) == file::read_write) title += "read/write "; else if ((f->open_mode & file::rw_mask) == file::read_only) title += "read "; else if ((f->open_mode & file::rw_mask) == file::write_only) title += "write "; if (f->open_mode & file::random_access) title += "random_access "; if (f->open_mode & file::lock_file) title += "locked "; if (f->open_mode & file::sparse) title += "sparse "; title += "]"; ++f; } const int file_progress_width = 65; // do we need to line-break? if (p + file_progress_width + 13 > terminal_width) { out += "\x1b[K\n"; pos += 1; p = 0; } snprintf(str, sizeof(str), "%s %7s p: %d ", progress_bar(progress, file_progress_width, complete ? col_green : col_yellow, '-', '#' , title.c_str()).c_str() , add_suffix(file_progress[i]).c_str() , file_prio[i]); p += file_progress_width + 13; out += str; } if (p != 0) { out += "\x1b[K\n"; pos += 1; } } } if (print_log) { for (std::deque::iterator i = events.begin(); i != events.end(); ++i) { if (pos + 1 >= terminal_height) break; out += *i; out += "\x1b[K\n"; pos += 1; } } // clear rest of screen out += "\x1b[J"; print(out.c_str()); fflush(stdout); if (!monitor_dir.empty() && next_dir_scan < clock_type::now()) { scan_dir(monitor_dir, ses, files, non_files , allocation_mode, save_path, torrent_upload_limit , torrent_download_limit); next_dir_scan = clock_type::now() + seconds(poll_interval); } } ses.pause(); printf("saving resume data\n"); std::vector temp; ses.get_torrent_status(&temp, &yes, 0); for (std::vector::iterator i = temp.begin(); i != temp.end(); ++i) { torrent_status& st = *i; if (!st.handle.is_valid()) { printf(" skipping, invalid handle\n"); continue; } if (!st.has_metadata) { printf(" skipping %s, no metadata\n", st.name.c_str()); continue; } if (!st.need_save_resume) { printf(" skipping %s, resume file up-to-date\n", st.name.c_str()); continue; } // save_resume_data will generate an alert when it's done st.handle.save_resume_data(); ++num_outstanding_resume_data; printf("\r%d ", num_outstanding_resume_data); } printf("\nwaiting for resume data [%d]\n", num_outstanding_resume_data); while (num_outstanding_resume_data > 0) { alert const* a = ses.wait_for_alert(seconds(10)); if (a == 0) continue; std::vector alerts; ses.pop_alerts(&alerts); std::string now = timestamp(); for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { if (!::handle_alert(view, ses_view, ses, *i, files, non_files)) { // if we didn't handle the alert, print it to the log std::string event_string; print_alert(*i, event_string); } } } if (g_log_file) fclose(g_log_file); // we're just saving the DHT state #ifndef TORRENT_DISABLE_DHT printf("\nsaving session state\n"); { entry session_state; ses.save_state(session_state, session::save_dht_state); std::vector out; bencode(std::back_inserter(out), session_state); save_file(".ses_state", out); } #endif printf("closing session"); return 0; } libtorrent-rasterbar-1.1.13/examples/cmake/000077500000000000000000000000001351156116000206415ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/examples/cmake/FindLibtorrentRasterbar.cmake000066400000000000000000000154541351156116000264470ustar00rootroot00000000000000# - Try to find libtorrent-rasterbar # # This module tries to locate libtorrent-rasterbar Config.cmake files and uses pkg-config if available # and the config file could not be found. # If that does not work, you can pre-set LibtorrentRasterbar_CUSTOM_DEFINITIONS # for definitions unrelated to Boost's separate compilation (which are already # decided by the LibtorrentRasterbar_USE_STATIC_LIBS variable). # # Once done this will define # LibtorrentRasterbar_FOUND - System has libtorrent-rasterbar # LibtorrentRasterbar_INCLUDE_DIRS - The libtorrent-rasterbar include directories # LibtorrentRasterbar_LIBRARIES - The libraries needed to use libtorrent-rasterbar # LibtorrentRasterbar_DEFINITIONS - Compiler switches required for using libtorrent-rasterbar # LibtorrentRasterbar_OPENSSL_ENABLED - libtorrent-rasterbar uses and links against OpenSSL # LibtorrentRasterbar::torrent-rasterbar imported target will be created # Let's begin with the config mode set(_exactKeyword "") if (${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION_EXACT}) set(_exactKeyword "EXACT") endif() find_package(LibtorrentRasterbar ${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION} ${_exactKeyword} CONFIG) if (LibtorrentRasterbar_FOUND) if (NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) message(STATUS "LibtorrentRasterbar package found in ${LibtorrentRasterbar_DIR}") message(STATUS "LibtorrentRasterbar version: ${LibtorrentRasterbar_VERSION}") endif() # Extract target properties into this module variables get_target_property(LibtorrentRasterbar_INCLUDE_DIRS LibtorrentRasterbar::torrent-rasterbar INTERFACE_INCLUDE_DIRECTORIES) get_target_property(LibtorrentRasterbar_LIBRARIES LibtorrentRasterbar::torrent-rasterbar IMPORTED_LOCATION) get_target_property(_iface_link_libs LibtorrentRasterbar::torrent-rasterbar INTERFACE_LINK_LIBRARIES) list(APPEND LibtorrentRasterbar_LIBRARIES ${_iface_link_libs}) get_target_property(LibtorrentRasterbar_DEFINITIONS LibtorrentRasterbar::torrent-rasterbar INTERFACE_COMPILE_DEFINITIONS) get_target_property(_iface_compile_options LibtorrentRasterbar::torrent-rasterbar INTERFACE_COMPILE_OPTIONS) list(APPEND LibtorrentRasterbar_DEFINITIONS ${_iface_compile_options}) list(FIND _iface_link_libs "OpenSSL::SSL" _openssl_lib_index) if (_openssl_lib_index GREATER -1) set(LibtorrentRasterbar_OPENSSL_ENABLED TRUE) else() set(LibtorrentRasterbar_OPENSSL_ENABLED FALSE) endif() else() find_package(Threads QUIET REQUIRED) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(PC_LIBTORRENT_RASTERBAR QUIET libtorrent-rasterbar) endif() if(LibtorrentRasterbar_USE_STATIC_LIBS) set(LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() if(PC_LIBTORRENT_RASTERBAR_FOUND) set(LibtorrentRasterbar_DEFINITIONS ${PC_LIBTORRENT_RASTERBAR_CFLAGS_OTHER}) else() if(LibtorrentRasterbar_CUSTOM_DEFINITIONS) set(LibtorrentRasterbar_DEFINITIONS ${LibtorrentRasterbar_CUSTOM_DEFINITIONS}) else() # Without pkg-config, we can't possibly figure out the correct build flags. # libtorrent is very picky about those. Let's take a set of defaults and # hope that they apply. If not, you the user are on your own. set(LibtorrentRasterbar_DEFINITIONS -DTORRENT_USE_OPENSSL -DTORRENT_DISABLE_GEO_IP -DBOOST_ASIO_ENABLE_CANCELIO -D_FILE_OFFSET_BITS=64) endif() if(NOT LibtorrentRasterbar_USE_STATIC_LIBS) list(APPEND LibtorrentRasterbar_DEFINITIONS -DTORRENT_LINKING_SHARED -DBOOST_SYSTEM_DYN_LINK) endif() endif() if (NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) message(STATUS "libtorrent definitions: ${LibtorrentRasterbar_DEFINITIONS}") endif() find_path(LibtorrentRasterbar_INCLUDE_DIR libtorrent HINTS ${PC_LIBTORRENT_RASTERBAR_INCLUDEDIR} ${PC_LIBTORRENT_RASTERBAR_INCLUDE_DIRS} PATH_SUFFIXES libtorrent-rasterbar) find_library(LibtorrentRasterbar_LIBRARY NAMES torrent-rasterbar HINTS ${PC_LIBTORRENT_RASTERBAR_LIBDIR} ${PC_LIBTORRENT_RASTERBAR_LIBRARY_DIRS}) if(LibtorrentRasterbar_USE_STATIC_LIBS) set(CMAKE_FIND_LIBRARY_SUFFIXES ${LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) endif() set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) set(LibtorrentRasterbar_INCLUDE_DIRS ${LibtorrentRasterbar_INCLUDE_DIR}) if(NOT Boost_SYSTEM_FOUND) find_package(Boost QUIET REQUIRED COMPONENTS system) set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) set(LibtorrentRasterbar_INCLUDE_DIRS ${LibtorrentRasterbar_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) endif() list(FIND LibtorrentRasterbar_DEFINITIONS -DTORRENT_USE_OPENSSL LibtorrentRasterbar_ENCRYPTION_INDEX) if(LibtorrentRasterbar_ENCRYPTION_INDEX GREATER -1) find_package(OpenSSL QUIET REQUIRED) set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} ${OPENSSL_LIBRARIES}) set(LibtorrentRasterbar_INCLUDE_DIRS ${LibtorrentRasterbar_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR}) set(LibtorrentRasterbar_OPENSSL_ENABLED ON) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LibtorrentRasterbar_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(LibtorrentRasterbar DEFAULT_MSG LibtorrentRasterbar_LIBRARY LibtorrentRasterbar_INCLUDE_DIR Boost_SYSTEM_FOUND ) mark_as_advanced(LibtorrentRasterbar_INCLUDE_DIR LibtorrentRasterbar_LIBRARY LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES LibtorrentRasterbar_ENCRYPTION_INDEX) if (LibtorrentRasterbar_FOUND AND NOT TARGET LibtorrentRasterbar::torrent-rasterbar) add_library(LibtorrentRasterbar::torrent-rasterbar SHARED IMPORTED) # LibtorrentRasterbar_DEFINITIONS var contains a mix of -D, -f, and possible -std options # let's split them into definitions and options (that are not definitions) set(LibtorrentRasterbar_defines "${LibtorrentRasterbar_DEFINITIONS}") set(LibtorrentRasterbar_options "${LibtorrentRasterbar_DEFINITIONS}") list(FILTER LibtorrentRasterbar_defines INCLUDE REGEX "(^|;)-D.+") list(FILTER LibtorrentRasterbar_options EXCLUDE REGEX "(^|;)-D.+") # remove '-D' from LibtorrentRasterbar_defines string(REGEX REPLACE "(^|;)(-D)" "\\1" LibtorrentRasterbar_defines "${LibtorrentRasterbar_defines}") set_target_properties(LibtorrentRasterbar::torrent-rasterbar PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${LibtorrentRasterbar_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}" INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${LibtorrentRasterbar_LIBRARIES}" INTERFACE_COMPILE_DEFINITIONS "${LibtorrentRasterbar_defines}" INTERFACE_COMPILE_OPTIONS "${LibtorrentRasterbar_options}" ) endif() endif() libtorrent-rasterbar-1.1.13/examples/connection_tester.cpp000066400000000000000000000724511351156116000240230ustar00rootroot00000000000000/* Copyright (c) 2008, 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/peer_id.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/address.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/io.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/file_pool.hpp" #include #include #include #include #include #if BOOST_ASIO_DYN_LINK #if BOOST_VERSION >= 104500 #include #elif BOOST_VERSION >= 104400 #include #endif #endif using namespace libtorrent; using namespace libtorrent::detail; // for write_* and read_* void generate_block(boost::uint32_t* buffer, int piece, int start, int length) { boost::uint32_t fill = (piece << 8) | ((start / 0x4000) & 0xff); for (int i = 0; i < length / 4; ++i) { buffer[i] = fill; } } // in order to circumvent the restricton of only // one connection per IP that most clients implement // all sockets created by this tester are bound to // uniqe local IPs in the range (127.0.0.1 - 127.255.255.255) // it's only enabled if the target is also on the loopback int local_if_counter = 0; bool local_bind = false; // when set to true, blocks downloaded are verified to match // the test torrents bool verify_downloads = false; // if this is true, one block in 1000 will be sent corrupt. // this only applies to dual and upload tests bool test_corruption = false; // number of seeds we've spawned. The test is terminated // when this reaches zero, for dual tests static boost::detail::atomic_count num_seeds(0); // the kind of test to run. Upload sends data to a // bittorrent client, download requests data from // a client and dual uploads and downloads from a client // at the same time (this is presumably the most realistic // test) enum test_mode_t{ none, upload_test, download_test, dual_test }; test_mode_t test_mode = none; // the number of suggest messages received (total across all peers) boost::detail::atomic_count num_suggest(0); // the number of requests made from suggested pieces boost::detail::atomic_count num_suggested_requests(0); void sleep_ms(int milliseconds) { #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN Sleep(milliseconds); #elif defined TORRENT_BEOS snooze_until(system_time() + boost::int64_t(milliseconds) * 1000, B_SYSTEM_TIMEBASE); #else usleep(milliseconds * 1000); #endif } std::string leaf_path(std::string f) { if (f.empty()) return ""; char const* first = f.c_str(); char const* sep = strrchr(first, '/'); #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) char const* altsep = strrchr(first, '\\'); if (sep == 0 || altsep > sep) sep = altsep; #endif if (sep == 0) return f; if (sep - first == int(f.size()) - 1) { // if the last character is a / (or \) // ignore it int len = 0; while (sep > first) { --sep; if (*sep == '/' #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) || *sep == '\\' #endif ) return std::string(sep + 1, len); ++len; } return std::string(first, len); } return std::string(sep + 1); } struct peer_conn { peer_conn(io_service& ios, int num_pieces, int blocks_pp, tcp::endpoint const& ep , char const* ih, bool seed_, int churn_, bool corrupt_) : s(ios) , read_pos(0) , state(handshaking) , choked(true) , current_piece(-1) , current_piece_is_allowed(false) , block(0) , blocks_per_piece(blocks_pp) , info_hash(ih) , outstanding_requests(0) , seed(seed_) , fast_extension(false) , blocks_received(0) , blocks_sent(0) , num_pieces(num_pieces) , start_time(clock_type::now()) , churn(churn_) , corrupt(corrupt_) , endpoint(ep) , restarting(false) { corruption_counter = rand() % 1000; if (seed) ++num_seeds; pieces.reserve(num_pieces); start_conn(); } void start_conn() { if (local_bind) { error_code ec; s.open(endpoint.protocol(), ec); if (ec) { close("ERROR OPEN: %s", ec); return; } tcp::endpoint bind_if(address_v4( (127 << 24) + ((local_if_counter / 255) << 16) + ((local_if_counter % 255) + 1)), 0); ++local_if_counter; s.bind(bind_if, ec); if (ec) { close("ERROR BIND: %s", ec); return; } } restarting = false; s.async_connect(endpoint, boost::bind(&peer_conn::on_connect, this, _1)); } tcp::socket s; char write_buf_proto[100]; boost::uint32_t write_buffer[17*1024/4]; boost::uint32_t buffer[17*1024/4]; int read_pos; int corruption_counter; enum state_t { handshaking, sending_request, receiving_message }; int state; std::vector pieces; std::vector suggested_pieces; std::vector allowed_fast; bool choked; int current_piece; // the piece we're currently requesting blocks from bool current_piece_is_allowed; int block; int blocks_per_piece; char const* info_hash; int outstanding_requests; // if this is true, this connection is a seed bool seed; bool fast_extension; int blocks_received; int blocks_sent; int num_pieces; time_point start_time; time_point end_time; int churn; bool corrupt; tcp::endpoint endpoint; bool restarting; void on_connect(error_code const& ec) { if (ec) { close("ERROR CONNECT: %s", ec); return; } char handshake[] = "\x13" "BitTorrent protocol\0\0\0\0\0\0\0\x04" " " // space for info-hash "aaaaaaaaaaaaaaaaaaaa" // peer-id "\0\0\0\x01\x02"; // interested char* h = (char*)malloc(sizeof(handshake)); memcpy(h, handshake, sizeof(handshake)); std::memcpy(h + 28, info_hash, 20); std::generate(h + 48, h + 68, &rand); // for seeds, don't send the interested message boost::asio::async_write(s, boost::asio::buffer(h, (sizeof(handshake) - 1) - (seed ? 5 : 0)) , boost::bind(&peer_conn::on_handshake, this, h, _1, _2)); } void on_handshake(char* h, error_code const& ec, size_t bytes_transferred) { free(h); if (ec) { close("ERROR SEND HANDSHAKE: %s", ec); return; } // read handshake boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 68) , boost::bind(&peer_conn::on_handshake2, this, _1, _2)); } void on_handshake2(error_code const& ec, size_t bytes_transferred) { if (ec) { close("ERROR READ HANDSHAKE: %s", ec); return; } // buffer is the full 68 byte handshake // look at the extension bits fast_extension = ((char*)buffer)[27] & 4; if (seed) { write_have_all(); } else { work_download(); } } void write_have_all() { if (fast_extension) { char* ptr = write_buf_proto; // have_all write_uint32(1, ptr); write_uint8(0xe, ptr); // unchoke write_uint32(1, ptr); write_uint8(1, ptr); error_code ec; boost::asio::async_write(s, boost::asio::buffer(write_buf_proto, ptr - write_buf_proto) , boost::bind(&peer_conn::on_have_all_sent, this, _1, _2)); } else { // bitfield int len = (num_pieces + 7) / 8; char* ptr = (char*)buffer; write_uint32(len + 1, ptr); write_uint8(5, ptr); memset(ptr, 255, len); ptr += len; // unchoke write_uint32(1, ptr); write_uint8(1, ptr); error_code ec; boost::asio::async_write(s, boost::asio::buffer((char*)buffer, len + 10) , boost::bind(&peer_conn::on_have_all_sent, this, _1, _2)); } } void on_have_all_sent(error_code const& ec, size_t bytes_transferred) { if (ec) { close("ERROR SEND HAVE ALL: %s", ec); return; } // read message boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 4) , boost::bind(&peer_conn::on_msg_length, this, _1, _2)); } bool write_request() { // if we're choked (and there are no allowed-fast pieces left) if (choked && allowed_fast.empty() && !current_piece_is_allowed) return false; // if there are no pieces left to request if (pieces.empty() && suggested_pieces.empty() && current_piece == -1) return false; if (current_piece == -1) { // pick a new piece if (choked && allowed_fast.size() > 0) { current_piece = allowed_fast.front(); allowed_fast.erase(allowed_fast.begin()); current_piece_is_allowed = true; } else if (suggested_pieces.size() > 0) { current_piece = suggested_pieces.front(); suggested_pieces.erase(suggested_pieces.begin()); ++num_suggested_requests; current_piece_is_allowed = false; } else if (pieces.size() > 0) { current_piece = pieces.front(); pieces.erase(pieces.begin()); current_piece_is_allowed = false; } else { TORRENT_ASSERT(false); } } char msg[] = "\0\0\0\xd\x06" " " // piece " " // offset " "; // length char* m = (char*)malloc(sizeof(msg)); memcpy(m, msg, sizeof(msg)); char* ptr = m + 5; write_uint32(current_piece, ptr); write_uint32(block * 16 * 1024, ptr); write_uint32(16 * 1024, ptr); error_code ec; boost::asio::async_write(s, boost::asio::buffer(m, sizeof(msg) - 1) , boost::bind(&peer_conn::on_req_sent, this, m, _1, _2)); ++outstanding_requests; ++block; if (block == blocks_per_piece) { block = 0; current_piece = -1; current_piece_is_allowed = false; } return true; } void on_req_sent(char* m, error_code const& ec, size_t bytes_transferred) { free(m); if (ec) { close("ERROR SEND REQUEST: %s", ec); return; } work_download(); } void close(char const* fmt, error_code const& ec) { end_time = clock_type::now(); char tmp[1024]; snprintf(tmp, sizeof(tmp), fmt, ec.message().c_str()); int time = total_milliseconds(end_time - start_time); if (time == 0) time = 1; float up = (boost::int64_t(blocks_sent) * 0x4000) / time / 1000.f; float down = (boost::int64_t(blocks_received) * 0x4000) / time / 1000.f; error_code e; char ep_str[200]; address const& addr = s.local_endpoint(e).address(); #if TORRENT_USE_IPV6 if (addr.is_v6()) snprintf(ep_str, sizeof(ep_str), "[%s]:%d", addr.to_string(e).c_str() , s.local_endpoint(e).port()); else #endif snprintf(ep_str, sizeof(ep_str), "%s:%d", addr.to_string(e).c_str() , s.local_endpoint(e).port()); printf("%s ep: %s sent: %d received: %d duration: %d ms up: %.1fMB/s down: %.1fMB/s\n" , tmp, ep_str, blocks_sent, blocks_received, time, up, down); if (seed) --num_seeds; } void work_download() { if (pieces.empty() && suggested_pieces.empty() && current_piece == -1 && outstanding_requests == 0 && blocks_received >= num_pieces * blocks_per_piece) { close("COMPLETED DOWNLOAD", error_code()); return; } // send requests if (outstanding_requests < 40) { if (write_request()) return; } // read message boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 4) , boost::bind(&peer_conn::on_msg_length, this, _1, _2)); } void on_msg_length(error_code const& ec, size_t bytes_transferred) { if ((ec == boost::asio::error::operation_aborted || ec == boost::asio::error::bad_descriptor) && restarting) { start_conn(); return; } if (ec) { close("ERROR RECEIVE MESSAGE PREFIX: %s", ec); return; } char* ptr = (char*)buffer; unsigned int length = read_uint32(ptr); if (length > sizeof(buffer)) { fprintf(stderr, "len: %d\n", length); close("ERROR RECEIVE MESSAGE PREFIX: packet too big", error_code()); return; } boost::asio::async_read(s, boost::asio::buffer((char*)buffer, length) , boost::bind(&peer_conn::on_message, this, _1, _2)); } void on_message(error_code const& ec, size_t bytes_transferred) { if ((ec == boost::asio::error::operation_aborted || ec == boost::asio::error::bad_descriptor) && restarting) { start_conn(); return; } if (ec) { close("ERROR RECEIVE MESSAGE: %s", ec); return; } char* ptr = (char*)buffer; int msg = read_uint8(ptr); if (test_mode == dual_test && num_seeds == 0) { TORRENT_ASSERT(!seed); close("NO MORE SEEDS, test done", error_code()); return; } //printf("msg: %d len: %d\n", msg, int(bytes_transferred)); if (seed) { if (msg == 6) { if (bytes_transferred != 13) { close("REQUEST packet has invalid size", error_code()); return; } int piece = detail::read_int32(ptr); int start = detail::read_int32(ptr); int length = detail::read_int32(ptr); write_piece(piece, start, length); } else if (msg == 3) // not-interested { close("DONE", error_code()); return; } else { // read another message boost::asio::async_read(s, boost::asio::buffer(buffer, 4) , boost::bind(&peer_conn::on_msg_length, this, _1, _2)); } } else { if (msg == 0xe) // have_all { // build a list of all pieces and request them all! pieces.resize(num_pieces); for (int i = 0; i < int(pieces.size()); ++i) pieces[i] = i; std::random_shuffle(pieces.begin(), pieces.end()); } else if (msg == 4) // have { int piece = detail::read_int32(ptr); if (pieces.empty()) pieces.push_back(piece); else pieces.insert(pieces.begin() + (rand() % pieces.size()), piece); } else if (msg == 5) // bitfield { pieces.reserve(num_pieces); int piece = 0; for (int i = 0; i < int(bytes_transferred); ++i) { int mask = 0x80; for (int k = 0; k < 8; ++k) { if (piece > num_pieces) break; if (*ptr & mask) pieces.push_back(piece); mask >>= 1; ++piece; } ++ptr; } std::random_shuffle(pieces.begin(), pieces.end()); } else if (msg == 7) // piece { if (verify_downloads) { int piece = read_uint32(ptr); int start = read_uint32(ptr); int size = bytes_transferred - 9; verify_piece(piece, start, ptr, size); } ++blocks_received; --outstanding_requests; int piece = detail::read_int32(ptr); int start = detail::read_int32(ptr); if (churn && (blocks_received % churn) == 0) { outstanding_requests = 0; restarting = true; s.close(); return; } if (int((start + bytes_transferred) / 0x4000) == blocks_per_piece) { write_have(piece); return; } } else if (msg == 13) // suggest { int piece = detail::read_int32(ptr); std::vector::iterator i = std::find(pieces.begin(), pieces.end(), piece); if (i != pieces.end()) { pieces.erase(i); suggested_pieces.push_back(piece); ++num_suggest; } } else if (msg == 16) // reject request { int piece = detail::read_int32(ptr); int start = detail::read_int32(ptr); int length = detail::read_int32(ptr); // put it back! if (current_piece != piece) { if (pieces.empty() || pieces.back() != piece) pieces.push_back(piece); } else { block = (std::min)(start / 0x4000, block); if (block == 0) { pieces.push_back(current_piece); current_piece = -1; current_piece_is_allowed = false; } } --outstanding_requests; fprintf(stderr, "REJECT: [ piece: %d start: %d length: %d ]\n", piece, start, length); } else if (msg == 0) // choke { choked = true; } else if (msg == 1) // unchoke { choked = false; } else if (msg == 17) // allowed_fast { int piece = detail::read_int32(ptr); std::vector::iterator i = std::find(pieces.begin(), pieces.end(), piece); if (i != pieces.end()) { pieces.erase(i); allowed_fast.push_back(piece); } } work_download(); } } bool verify_piece(int piece, int start, char const* ptr, int size) { boost::uint32_t* buf = (boost::uint32_t*)ptr; boost::uint32_t fill = (piece << 8) | ((start / 0x4000) & 0xff); for (int i = 0; i < size / 4; ++i) { if (buf[i] != fill) { fprintf(stderr, "received invalid block. piece %d block %d\n", piece, start / 0x4000); exit(1); return false; } } return true; } void write_piece(int piece, int start, int length) { generate_block(write_buffer, piece, start, length); if (corrupt) { --corruption_counter; if (corruption_counter == 0) { corruption_counter = 1000; memset(write_buffer, 0, 10); } } char* ptr = write_buf_proto; write_uint32(9 + length, ptr); assert(length == 0x4000); write_uint8(7, ptr); write_uint32(piece, ptr); write_uint32(start, ptr); boost::array vec; vec[0] = boost::asio::buffer(write_buf_proto, ptr - write_buf_proto); vec[1] = boost::asio::buffer(write_buffer, length); boost::asio::async_write(s, vec, boost::bind(&peer_conn::on_have_all_sent, this, _1, _2)); ++blocks_sent; if (churn && (blocks_sent % churn) == 0 && seed) { outstanding_requests = 0; restarting = true; s.close(); } } void write_have(int piece) { char* ptr = write_buf_proto; write_uint32(5, ptr); write_uint8(4, ptr); write_uint32(piece, ptr); boost::asio::async_write(s, boost::asio::buffer(write_buf_proto, 9), boost::bind(&peer_conn::on_have_all_sent, this, _1, _2)); } }; void print_usage() { fprintf(stderr, "usage: connection_tester command [options]\n\n" "command is one of:\n" " gen-torrent generate a test torrent\n" " options for this command:\n" " -s the size of the torrent in megabytes\n" " -n the number of files in the test torrent\n" " -a introduce a lot of pad-files\n" " (pad files are not supported for gen-data or upload)\n" " -t the file to save the .torrent file to\n" " -T the name of the torrent (and directory\n" " its files are saved in)\n\n" " gen-data generate the data file(s) for the test torrent\n" " options for this command:\n" " -t the torrent file that was previously generated\n" " -P the path to where the data should be stored\n\n" " gen-test-torrents generate many test torrents (cannot be used for up/down tests)\n" " options for this command:\n" " -N number of torrents to generate\n" " -n number of files in each torrent\n" " -t base name of torrent files (index is appended)\n\n" " upload start an uploader test\n" " download start a downloader test\n" " dual start a download and upload test\n" " options for these commands:\n" " -c the number of connections to make to the target\n" " -d the IP address of the target\n" " -p the port the target listens on\n" " -t the torrent file previously generated by gen-torrent\n" " -C send corrupt pieces sometimes (applies to upload and dual)\n" " -r churn - number of reconnects per second\n\n" "examples:\n\n" "connection_tester gen-torrent -s 1024 -n 4 -t test.torrent\n" "connection_tester upload -c 200 -d 127.0.0.1 -p 6881 -t test.torrent\n" "connection_tester download -c 200 -d 127.0.0.1 -p 6881 -t test.torrent\n" "connection_tester dual -c 200 -d 127.0.0.1 -p 6881 -t test.torrent\n"); exit(1); } void hasher_thread(libtorrent::create_torrent* t, int start_piece, int end_piece, int piece_size, bool print) { if (print) fprintf(stderr, "\n"); boost::uint32_t piece[0x4000 / 4]; for (int i = start_piece; i < end_piece; ++i) { hasher ph; for (int j = 0; j < piece_size; j += 0x4000) { generate_block(piece, i, j, 0x4000); ph.update((char*)piece, 0x4000); } t->set_hash(i, ph.final()); if (print && (i & 1)) fprintf(stderr, "\r%.1f %% ", float((i-start_piece) * 100) / float(end_piece-start_piece)); } if (print) fprintf(stderr, "\n"); } // size is in megabytes void generate_torrent(std::vector& buf, int num_pieces, int num_files , char const* torrent_name, bool with_padding) { file_storage fs; // 1 MiB piece size const int piece_size = 1024 * 1024; const boost::int64_t total_size = boost::int64_t(piece_size) * num_pieces; boost::int64_t s = total_size; int i = 0; boost::int64_t file_size = total_size / num_files; while (s > 0) { char b[100]; snprintf(b, sizeof(b), "%s/stress_test%d", torrent_name, i); ++i; fs.add_file(b, (std::min)(s, boost::int64_t(file_size))); s -= file_size; file_size += 200; } libtorrent::create_torrent t(fs, piece_size, with_padding ? 100 : -1); num_pieces = t.num_pieces(); // generate the hashes in 4 threads thread t1(boost::bind(&hasher_thread, &t, 0, 1 * num_pieces / 4, piece_size, false)); thread t2(boost::bind(&hasher_thread, &t, 1 * num_pieces / 4, 2 * num_pieces / 4, piece_size, false)); thread t3(boost::bind(&hasher_thread, &t, 2 * num_pieces / 4, 3 * num_pieces / 4, piece_size, false)); thread t4(boost::bind(&hasher_thread, &t, 3 * num_pieces / 4, 4 * num_pieces / 4, piece_size, true)); t1.join(); t2.join(); t3.join(); t4.join(); std::back_insert_iterator > out(buf); bencode(out, t.generate()); } void generate_data(char const* path, torrent_info const& ti) { file_storage const& fs = ti.files(); file_pool fp; storage_params params; params.files = &const_cast(fs); params.mapped_files = NULL; params.path = path; params.pool = &fp; params.mode = storage_mode_sparse; boost::scoped_ptr st(default_storage_constructor(params)); storage_error error; st->initialize(error); boost::uint32_t piece[0x4000 / 4]; for (int i = 0; i < ti.num_pieces(); ++i) { for (int j = 0; j < ti.piece_size(i); j += 0x4000) { generate_block(piece, i, j, 0x4000); int const left_in_piece = ti.piece_size(i) - j; file::iovec_t const b = { piece, size_t(std::min(left_in_piece, 0x4000))}; storage_error error; st->writev(&b, 1, i, j, 0, error); if (error) fprintf(stderr, "storage error: %s\n", error.ec.message().c_str()); } if (i & 1) fprintf(stderr, "\r%.1f %% ", float(i * 100) / float(ti.num_pieces())); } } void io_thread(io_service* ios) { error_code ec; ios->run(ec); if (ec) fprintf(stderr, "ERROR: %s\n", ec.message().c_str()); } int main(int argc, char* argv[]) { if (argc <= 1) print_usage(); char const* command = argv[1]; int size = 1000; int num_files = 10; int num_torrents = 1; char const* torrent_file = "benchmark.torrent"; char const* data_path = "."; int num_connections = 50; char const* destination_ip = "127.0.0.1"; int destination_port = 6881; int churn = 0; bool gen_pad_files = false; argv += 2; argc -= 2; while (argc > 0) { char const* optname = argv[0]; ++argv; --argc; if (optname[0] != '-' || strlen(optname) != 2) { fprintf(stderr, "unknown option: %s\n", optname); continue; } // options with no arguments switch (optname[1]) { case 'C': test_corruption = true; continue; case 'a': gen_pad_files = true; continue; } if (argc == 0) { fprintf(stderr, "missing argument for option: %s\n", optname); break; } char const* optarg = argv[0]; ++argv; --argc; switch (optname[1]) { case 's': size = atoi(optarg); break; case 'n': num_files = atoi(optarg); break; case 'N': num_torrents = atoi(optarg); break; case 't': torrent_file = optarg; break; case 'P': data_path = optarg; break; case 'c': num_connections = atoi(optarg); break; case 'p': destination_port = atoi(optarg); break; case 'd': destination_ip = optarg; break; case 'r': churn = atoi(optarg); break; default: fprintf(stderr, "unknown option: %s\n", optname); } } if (strcmp(command, "gen-torrent") == 0) { std::vector tmp; std::string name = leaf_path(torrent_file); name = name.substr(0, name.find_last_of('.')); printf("generating torrent: %s\n", name.c_str()); generate_torrent(tmp, size ? size : 1024, num_files ? num_files : 1 , name.c_str(), gen_pad_files); FILE* output = stdout; if (strcmp("-", torrent_file) != 0) { if( (output = fopen(torrent_file, "wb+")) == 0) { fprintf(stderr, "Could not open file '%s' for writing: %s\n", torrent_file, strerror(errno)); exit(2); } } fprintf(stderr, "writing file to: %s\n", torrent_file); fwrite(&tmp[0], 1, tmp.size(), output); if (output != stdout) fclose(output); return 0; } else if (strcmp(command, "gen-data") == 0) { error_code ec; torrent_info ti(torrent_file, ec); if (ec) { fprintf(stderr, "ERROR LOADING .TORRENT: %s\n", ec.message().c_str()); return 1; } generate_data(data_path, ti); return 0; } else if (strcmp(command, "gen-test-torrents") == 0) { std::vector buf; for (int i = 0; i < num_torrents; ++i) { char torrent_name[100]; snprintf(torrent_name, sizeof(torrent_name), "%s-%d.torrent", torrent_file, i); file_storage fs; for (int j = 0; j < num_files; ++j) { char file_name[100]; snprintf(file_name, sizeof(file_name), "%s-%d/file-%d", torrent_file, i, j); fs.add_file(file_name, boost::int64_t(j + i + 1) * 251); } // 1 MiB piece size const int piece_size = 1024 * 1024; libtorrent::create_torrent t(fs, piece_size); sha1_hash zero(0); for (int i = 0; i < fs.num_pieces(); ++i) t.set_hash(i, zero); buf.clear(); std::back_insert_iterator > out(buf); bencode(out, t.generate()); FILE* f = fopen(torrent_name, "w+"); if (f == 0) { fprintf(stderr, "Could not open file '%s' for writing: %s\n", torrent_name, strerror(errno)); return 1; } size_t ret = fwrite(&buf[0], 1, buf.size(), f); if (ret != buf.size()) { fprintf(stderr, "write returned: %d (expected %d)\n", int(ret), int(buf.size())); fclose(f); return 1; } printf("wrote %s\n", torrent_name); fclose(f); } return 0; } else if (strcmp(command, "upload") == 0) { test_mode = upload_test; } else if (strcmp(command, "download") == 0) { test_mode = download_test; } else if (strcmp(command, "dual") == 0) { test_mode = dual_test; } else { fprintf(stderr, "unknown command: %s\n\n", command); print_usage(); } error_code ec; address_v4 addr = address_v4::from_string(destination_ip, ec); if (ec) { fprintf(stderr, "ERROR RESOLVING %s: %s\n", destination_ip, ec.message().c_str()); return 1; } tcp::endpoint ep(addr, destination_port); #if !defined __APPLE__ // apparently darwin doesn't seems to let you bind to // loopback on any other IP than 127.0.0.1 unsigned long ip = addr.to_ulong(); if ((ip & 0xff000000) == 0x7f000000) { local_bind = true; } #endif torrent_info ti(torrent_file, ec); if (ec) { fprintf(stderr, "ERROR LOADING .TORRENT: %s\n", ec.message().c_str()); return 1; } std::vector conns; conns.reserve(num_connections); const int num_threads = 2; io_service ios[num_threads]; for (int i = 0; i < num_connections; ++i) { bool corrupt = test_corruption && (i & 1) == 0; bool seed = false; if (test_mode == upload_test) seed = true; else if (test_mode == dual_test) seed = (i & 1); conns.push_back(new peer_conn(ios[i % num_threads], ti.num_pieces(), ti.piece_length() / 16 / 1024 , ep, (char const*)&ti.info_hash()[0], seed, churn, corrupt)); sleep_ms(1); ios[i % num_threads].poll_one(ec); if (ec) { fprintf(stderr, "ERROR: %s\n", ec.message().c_str()); break; } } thread t1(boost::bind(&io_thread, &ios[0])); thread t2(boost::bind(&io_thread, &ios[1])); t1.join(); t2.join(); float up = 0.f; float down = 0.f; boost::uint64_t total_sent = 0; boost::uint64_t total_received = 0; for (std::vector::iterator i = conns.begin() , end(conns.end()); i != end; ++i) { peer_conn* p = *i; int time = total_milliseconds(p->end_time - p->start_time); if (time == 0) time = 1; total_sent += p->blocks_sent; up += (boost::int64_t(p->blocks_sent) * 0x4000) / time / 1000.f; down += (boost::int64_t(p->blocks_received) * 0x4000) / time / 1000.f; delete p; } printf("=========================\n" "suggests: %d suggested-requests: %d\n" "total sent: %.1f %% received: %.1f %%\n" "rate sent: %.1f MB/s received: %.1f MB/s\n" , int(num_suggest), int(num_suggested_requests) , total_sent * 0x4000 * 100.f / float(ti.total_size()) , total_received * 0x4000 * 100.f / float(ti.total_size()) , up, down); return 0; } libtorrent-rasterbar-1.1.13/examples/dump_torrent.cpp000066400000000000000000000116311351156116000230110ustar00rootroot00000000000000/* Copyright (c) 2003-2017, 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/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/announce_entry.hpp" #include "libtorrent/bdecode.hpp" #include "libtorrent/magnet_uri.hpp" #include std::vector load_file(std::string const& filename) { std::vector ret; std::fstream in; in.exceptions(std::ifstream::failbit); in.open(filename.c_str(), std::ios_base::in | std::ios_base::binary); in.seekg(0, std::ios_base::end); size_t const size = in.tellg(); in.seekg(0, std::ios_base::beg); ret.resize(size); in.read(ret.data(), ret.size()); return ret; } int main(int argc, char* argv[]) try { if (argc < 2 || argc > 4) { fputs("usage: dump_torrent torrent-file [total-items-limit] [recursion-limit]\n", stderr); return 1; } int item_limit = 1000000; int depth_limit = 1000; if (argc > 2) item_limit = atoi(argv[2]); if (argc > 3) depth_limit = atoi(argv[3]); std::vector buf = load_file(argv[1]); lt::bdecode_node e; int pos = -1; lt::error_code ec; std::cout << "decoding. recursion limit: " << depth_limit << " total item count limit: " << item_limit << "\n"; int const ret = lt::bdecode(&buf[0], &buf[0] + buf.size(), e, ec, &pos , depth_limit, item_limit); printf("\n\n----- raw info -----\n\n%s\n", print_entry(e).c_str()); if (ret != 0) { std::cerr << "failed to decode: '" << ec.message() << "' at character: " << pos<< "\n"; return 1; } lt::torrent_info const t(e); e.clear(); std::vector().swap(buf); // print info about torrent printf("\n\n----- torrent file info -----\n\n" "nodes:\n"); typedef std::vector > node_vec; node_vec const& nodes = t.nodes(); for (node_vec::const_iterator i = nodes.begin(), end(nodes.end()); i != end; ++i) { printf("%s: %d\n", i->first.c_str(), i->second); } puts("trackers:\n"); for (std::vector::const_iterator i = t.trackers().begin(); i != t.trackers().end(); ++i) { printf("%2d: %s\n", i->tier, i->url.c_str()); } char ih[41]; lt::to_hex(t.info_hash().data(), 20, ih); printf("number of pieces: %d\n" "piece length: %d\n" "info hash: %s\n" "comment: %s\n" "created by: %s\n" "magnet link: %s\n" "name: %s\n" "number of files: %d\n" "files:\n" , t.num_pieces() , t.piece_length() , ih , t.comment().c_str() , t.creator().c_str() , make_magnet_uri(t).c_str() , t.name().c_str() , t.num_files()); lt::file_storage const& st = t.files(); for (int i = 0; i < st.num_files(); ++i) { int const first = st.map_file(i, 0, 0).piece; int const last = st.map_file(i, (std::max)(boost::int64_t(st.file_size(i))-1, boost::int64_t(0)), 0).piece; int const flags = st.file_flags(i); printf(" %8" PRIx64 " %11" PRId64 " %c%c%c%c [ %5d, %5d ] %7u %s %s %s%s\n" , st.file_offset(i) , st.file_size(i) , ((flags & lt::file_storage::flag_pad_file)?'p':'-') , ((flags & lt::file_storage::flag_executable)?'x':'-') , ((flags & lt::file_storage::flag_hidden)?'h':'-') , ((flags & lt::file_storage::flag_symlink)?'l':'-') , first, last , boost::uint32_t(st.mtime(i)) , st.hash(i) != lt::sha1_hash(0) ? lt::to_hex(st.hash(i).to_string()).c_str() : "" , st.file_path(i).c_str() , (flags & lt::file_storage::flag_symlink) ? "-> " : "" , (flags & lt::file_storage::flag_symlink) ? st.symlink(i).c_str() : ""); } return 0; } catch (std::exception const& e) { std::cerr << "ERROR: " << e.what() << "\n"; } libtorrent-rasterbar-1.1.13/examples/make_torrent.cpp000066400000000000000000000235221351156116000227630ustar00rootroot00000000000000/* Copyright (c) 2006-2017, 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/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/file.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/file.hpp" #include "libtorrent/file_pool.hpp" #include "libtorrent/hex.hpp" // for from_hex #include #include #ifdef TORRENT_WINDOWS #include // for _getcwd #endif std::vector load_file(std::string const& filename) { std::vector ret; std::fstream in; in.exceptions(std::ifstream::failbit); in.open(filename.c_str(), std::ios_base::in | std::ios_base::binary); in.seekg(0, std::ios_base::end); size_t const size = in.tellg(); in.seekg(0, std::ios_base::beg); ret.resize(size); in.read(ret.data(), ret.size()); return ret; } std::string branch_path(std::string const& f) { if (f.empty()) return f; #ifdef TORRENT_WINDOWS if (f == "\\\\") return ""; #endif if (f == "/") return ""; int len = f.size(); // if the last character is / or \ ignore it if (f[len-1] == '/' || f[len-1] == '\\') --len; while (len > 0) { --len; if (f[len] == '/' || f[len] == '\\') break; } if (f[len] == '/' || f[len] == '\\') ++len; return std::string(f.c_str(), len); } // do not include files and folders whose // name starts with a . bool file_filter(std::string const& f) { if (f.empty()) return false; char const* first = f.c_str(); char const* sep = strrchr(first, '/'); #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) char const* altsep = strrchr(first, '\\'); if (sep == NULL || altsep > sep) sep = altsep; #endif // if there is no parent path, just set 'sep' // to point to the filename. // if there is a parent path, skip the '/' character if (sep == NULL) sep = first; else ++sep; // return false if the first character of the filename is a . if (sep[0] == '.') return false; fprintf(stderr, "%s\n", f.c_str()); return true; } void print_progress(int i, int num) { fprintf(stderr, "\r%d/%d", i+1, num); } void print_usage() { fputs("usage: make_torrent FILE [OPTIONS]\n" "\n" "Generates a torrent file from the specified file\n" "or directory and writes it to standard out\n\n" "OPTIONS:\n" "-m file generate a merkle hash tree torrent.\n" " merkle torrents require client support\n" " the resulting full merkle tree is written to\n" " the specified file\n" "-w url adds a web seed to the torrent with\n" " the specified url\n" "-t url adds the specified tracker to the\n" " torrent. For multiple trackers, specify more\n" " -t options\n" "-c comment sets the comment to the specified string\n" "-C creator sets the created-by field to the specified string\n" "-p bytes enables padding files. Files larger\n" " than bytes will be piece-aligned\n" "-s bytes specifies a piece size for the torrent\n" " This has to be a multiple of 16 kiB\n" "-l Don't follow symlinks, instead encode them as\n" " links in the torrent file\n" "-o file specifies the output filename of the torrent file\n" " If this is not specified, the torrent file is\n" " printed to the standard out, except on windows\n" " where the filename defaults to a.torrent\n" "-r file add root certificate to the torrent, to verify\n" " the HTTPS tracker\n" "-S info-hash add a similar torrent by info-hash. The similar\n" " torrent is expected to share some files with this one\n" "-L collection add a collection name to this torrent. Other torrents\n" " in the same collection is expected to share files\n" " with this one.\n" "-M make the torrent compatible with mutable torrents\n" " this means aligning large files and pad them in order\n" " for piece hashes to uniquely indentify a file without\n" " overlap\n" , stderr); } int main(int argc, char* argv[]) try { std::string creator_str = "libtorrent"; std::string comment_str; if (argc < 2) { print_usage(); return 1; } std::vector web_seeds; std::vector trackers; std::vector collections; std::vector similar; int pad_file_limit = -1; int piece_size = 0; int flags = 0; std::string root_cert; std::string outfile; std::string merklefile; #ifdef TORRENT_WINDOWS // don't ever write binary data to the console on windows // it will just be interpreted as text and corrupted outfile = "a.torrent"; #endif std::string full_path = argv[1]; argv += 2; argc -= 2; for (; argc > 0; --argc, ++argv) { if (argv[0][0] != '-') { print_usage(); return 1; } char const flag = argv[0][1]; switch (flag) { case 'M': flags |= lt::create_torrent::mutable_torrent_support; pad_file_limit = 0x4000; continue; case 'l': flags |= lt::create_torrent::symlinks; continue; } if (argc < 2) { print_usage(); return 1; } switch (flag) { case 'w': web_seeds.push_back(argv[1]); break; case 't': trackers.push_back(argv[1]); break; case 's': piece_size = atoi(argv[1]); break; case 'o': outfile = argv[1]; break; case 'C': creator_str = argv[1]; break; case 'c': comment_str = argv[1]; break; case 'r': root_cert = argv[1]; break; case 'L': collections.push_back(argv[1]); break; case 'p': pad_file_limit = atoi(argv[1]); flags |= lt::create_torrent::optimize_alignment; break; case 'm': merklefile = argv[1]; flags |= lt::create_torrent::merkle; break; case 'S': { if (strlen(argv[1]) != 40) { std::cerr << "invalid info-hash for -S. " "Expected 40 hex characters\n"; print_usage(); return 1; } lt::sha1_hash ih; if (!lt::from_hex(argv[1], 40, ih.data())) { std::cerr << "invalid info-hash for -S\n"; print_usage(); return 1; } similar.push_back(ih); } break; default: print_usage(); return 1; } ++argv; --argc; } lt::file_storage fs; #ifdef TORRENT_WINDOWS if (full_path[1] != ':') #else if (full_path[0] != '/') #endif { char cwd[TORRENT_MAX_PATH]; #ifdef TORRENT_WINDOWS _getcwd(cwd, sizeof(cwd)); full_path = cwd + ("\\" + full_path); #else getcwd(cwd, sizeof(cwd)); full_path = cwd + ("/" + full_path); #endif } lt::add_files(fs, full_path, file_filter, flags); if (fs.num_files() == 0) { std::cerr << "no files specified.\n"; return 1; } lt::create_torrent t(fs, piece_size, pad_file_limit, flags); int tier = 0; for (std::vector::iterator i = trackers.begin() , end(trackers.end()); i != end; ++i, ++tier) t.add_tracker(*i, tier); for (std::vector::iterator i = web_seeds.begin() , end(web_seeds.end()); i != end; ++i) t.add_url_seed(*i); for (std::vector::iterator i = collections.begin() , end(collections.end()); i != end; ++i) t.add_collection(*i); for (std::vector::iterator i = similar.begin() , end(similar.end()); i != end; ++i) t.add_similar_torrent(*i); lt::error_code ec; set_piece_hashes(t, branch_path(full_path) , boost::bind(&print_progress, _1, t.num_pieces()), ec); if (ec) { std::cerr << ec.message() << "\n"; return 1; } fprintf(stderr, "\n"); t.set_creator(creator_str.c_str()); if (!comment_str.empty()) { t.set_comment(comment_str.c_str()); } if (!root_cert.empty()) { std::vector const pem = load_file(root_cert); t.set_root_cert(std::string(&pem[0], pem.size())); } // create the torrent and print it to stdout std::vector torrent; lt::bencode(back_inserter(torrent), t.generate()); if (!outfile.empty()) { std::fstream out; out.exceptions(std::ifstream::failbit); out.open(outfile.c_str(), std::ios_base::out | std::ios_base::binary); out.write(&torrent[0], torrent.size()); } else { std::cout.write(&torrent[0], torrent.size()); } if (!merklefile.empty()) { std::fstream merkle; merkle.exceptions(std::ifstream::failbit); merkle.open(merklefile.c_str(), std::ios_base::out | std::ios_base::binary); merkle.write(reinterpret_cast(&t.merkle_tree()[0]), t.merkle_tree().size() * 20); } return 0; } catch (std::exception& e) { std::cerr << "ERROR: " << e.what() << "\n"; return 1; } libtorrent-rasterbar-1.1.13/examples/print.cpp000066400000000000000000000270541351156116000214310ustar00rootroot00000000000000#ifdef _WIN32 #include #include #else #include // for close() #include // for open() #include #endif #include "libtorrent/config.hpp" #include "print.hpp" #include // for atoi #include // for strlen #include #include // for std::min #include // for back_inserter char const* esc(char const* code) { // this is a silly optimization // to avoid copying of strings enum { num_strings = 200 }; static char buf[num_strings][20]; static int round_robin = 0; char* ret = buf[round_robin]; ++round_robin; if (round_robin >= num_strings) round_robin = 0; ret[0] = '\033'; ret[1] = '['; int i = 2; int j = 0; while (code[j]) ret[i++] = code[j++]; ret[i++] = 'm'; ret[i++] = 0; return ret; } std::string to_string(int v, int width) { char buf[100]; snprintf(buf, sizeof(buf), "%*d", width, v); return buf; } std::string add_suffix(float val, char const* suffix) { if (val < 0.001f) { std::string ret; ret.resize(4 + 2, ' '); if (suffix) ret.resize(4 + 2 + strlen(suffix), ' '); return ret; } const char* prefix[] = {"kB", "MB", "GB", "TB", "PB"}; const int num_prefix = sizeof(prefix) / sizeof(const char*); int i = 0; for (; i < num_prefix - 1; ++i) { val /= 1000.f; if (std::fabs(val) < 1000.f) break; } char ret[100]; snprintf(ret, sizeof(ret), "%4.*f%s%s", val < 99 ? 1 : 0, val, prefix[i], suffix ? suffix : ""); return ret; } std::string color(std::string const& s, color_code c) { if (c == col_none) return s; if (std::count(s.begin(), s.end(), ' ') == int(s.size())) return s; char buf[1024]; snprintf(buf, sizeof(buf), "\x1b[3%dm%s\x1b[39m", c, s.c_str()); return buf; } std::string const& progress_bar(int progress, int width, color_code c , char fill, char bg, std::string caption, int flags) { static std::string bar; bar.clear(); bar.reserve(size_t(width + 10)); int const progress_chars = (progress * width + 500) / 1000; if (caption.empty()) { char code[10]; snprintf(code, sizeof(code), "\x1b[3%dm", c); bar = code; std::fill_n(std::back_inserter(bar), progress_chars, fill); std::fill_n(std::back_inserter(bar), width - progress_chars, bg); bar += esc("39"); } else { // foreground color (depends a bit on background color) color_code tc = col_black; if (c == col_black || c == col_blue) tc = col_white; caption.resize(size_t(width), ' '); #ifdef _WIN32 char const* background = "40"; #else char const* background = "48;5;238"; #endif char str[256]; if (flags & progress_invert) snprintf(str, sizeof(str), "\x1b[%sm\x1b[37m%s\x1b[4%d;3%dm%s\x1b[49;39m" , background, caption.substr(0, progress_chars).c_str(), c, tc , caption.substr(progress_chars).c_str()); else snprintf(str, sizeof(str), "\x1b[4%d;3%dm%s\x1b[%sm\x1b[37m%s\x1b[49;39m" , c, tc, caption.substr(0, progress_chars).c_str(), background , caption.substr(progress_chars).c_str()); bar = str; } return bar; } bool get_piece(libtorrent::bitfield const& p, int index) { if (index < 0 || index >= p.size()) return false; return p.get_bit(index); } #ifndef _WIN32 // this function uses the block characters that splits up the glyph in 4 // segments and provide all combinations of a segment lit or not. This allows us // to print 4 pieces per character. std::string piece_matrix(libtorrent::bitfield const& p, int width, int* height) { // print two rows of pieces at a time int piece = 0; ++*height; std::string ret; ret.reserve((p.size() + width * 2 - 1) / width / 2 * 4); while (piece < p.size()) { for (int i = 0; i < width; ++i) { // each character has 4 pieces. store them in a byte to use for lookups int const c = get_piece(p, piece) | (get_piece(p, piece+1) << 1) | (get_piece(p, width*2+piece) << 2) | (get_piece(p, width*2+piece+1) << 3); // we have 4 bits, 16 different combinations static char const* const chars[] = { " ", // no bit is set 0000 "\u2598", // upper left 0001 "\u259d", // upper right 0010 "\u2580", // both top bits 0011 "\u2596", // lower left 0100 "\u258c", // both left bits 0101 "\u259e", // upper right, lower left 0110 "\u259b", // left and upper sides 0111 "\u2597", // lower right 1000 "\u259a", // lower right, upper left 1001 "\u2590", // right side 1010 "\u259c", // lower right, top side 1011 "\u2584", // both lower bits 1100 "\u2599", // both lower, top left 1101 "\u259f", // both lower, top right 1110 "\x1b[7m \x1b[27m" // all bits are set (full block) }; ret += chars[c]; piece += 2; } ret += '\n'; ++*height; piece += width * 2; // skip another row, as we've already printed it } return ret; } #else // on MS-DOS terminals, we only have block characters for upper half and lower // half. This lets us print two pieces per character. std::string piece_matrix(libtorrent::bitfield const& p, int width, int* height) { // print two rows of pieces at a time int piece = 0; ++*height; std::string ret; ret.reserve((p.size() + width * 2 - 1) / width); while (piece < p.size()) { for (int i = 0; i < width; ++i) { // each character has 8 pieces. store them in a byte to use for lookups // the ordering of these bits int const c = get_piece(p, piece) | (get_piece(p, width*2+piece) << 1); static char const* const chars[] = { " ", // no piece 00 "\xdf", // top piece 01 "\xdc", // bottom piece 10 "\xdb" // both pieces 11 }; ret += chars[c]; ++piece; } ret += '\n'; ++*height; piece += width * 2; // skip another row, as we've already printed it } return ret; } #endif void set_cursor_pos(int x, int y) { #ifdef _WIN32 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); COORD c = {x, y}; SetConsoleCursorPosition(out, c); #else printf("\033[%d;%dH", y + 1, x + 1); #endif } void clear_screen() { #ifdef _WIN32 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); COORD c = {0, 0}; CONSOLE_SCREEN_BUFFER_INFO si; GetConsoleScreenBufferInfo(out, &si); DWORD n; FillConsoleOutputCharacter(out, ' ', si.dwSize.X * si.dwSize.Y, c, &n); FillConsoleOutputAttribute(out, 0x7, si.dwSize.X * si.dwSize.Y, c, &n); #else printf("\033[2J"); #endif } void clear_rows(int y1, int y2) { if (y1 > y2) return; #ifdef _WIN32 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); COORD c = {0, y1}; SetConsoleCursorPosition(out, c); CONSOLE_SCREEN_BUFFER_INFO si; GetConsoleScreenBufferInfo(out, &si); DWORD n; int num_chars = si.dwSize.X * (std::min)(si.dwSize.Y - y1, y2 - y1); FillConsoleOutputCharacter(out, ' ', num_chars, c, &n); FillConsoleOutputAttribute(out, 0x7, num_chars, c, &n); #else for (int i = y1; i < y2; ++i) printf("\033[%d;1H\033[2K", i + 1); #endif } void terminal_size(int* terminal_width, int* terminal_height) { #ifdef _WIN32 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO coninfo; if (GetConsoleScreenBufferInfo(out, &coninfo)) { *terminal_width = coninfo.dwSize.X; *terminal_height = coninfo.srWindow.Bottom - coninfo.srWindow.Top; #else int tty = open("/dev/tty", O_RDONLY); if (tty < 0) { *terminal_width = 190; *terminal_height = 100; return; } winsize size; int ret = ioctl(tty, TIOCGWINSZ, reinterpret_cast(&size)); close(tty); if (ret == 0) { *terminal_width = size.ws_col; *terminal_height = size.ws_row; #endif if (*terminal_width < 64) *terminal_width = 64; if (*terminal_height < 25) *terminal_height = 25; } else { *terminal_width = 190; *terminal_height = 100; } } #ifdef _WIN32 void apply_ansi_code(int* attributes, bool* reverse, bool* support_chaining, int code) { static const int color_table[8] = { 0, // black FOREGROUND_RED, // red FOREGROUND_GREEN, // green FOREGROUND_RED | FOREGROUND_GREEN, // yellow FOREGROUND_BLUE, // blue FOREGROUND_RED | FOREGROUND_BLUE, // magenta FOREGROUND_BLUE | FOREGROUND_GREEN, // cyan FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE // white }; enum { foreground_mask = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, background_mask = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY }; static const int fg_mask[2] = {foreground_mask, background_mask}; static const int bg_mask[2] = {background_mask, foreground_mask}; static const int fg_shift[2] = { 0, 4}; static const int bg_shift[2] = { 4, 0}; // default foreground if (code == 39) code = 37; // default background if (code == 49) code = 40; if (code == 0) { // reset *attributes = color_table[7]; *reverse = false; *support_chaining = true; } else if (code == 1) { // intensity *attributes |= *reverse ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY; *support_chaining = true; } else if (code == 7) { // reverse video *support_chaining = true; if (*reverse) return; *reverse = true; int fg_col = *attributes & foreground_mask; int bg_col = (*attributes & background_mask) >> 4; *attributes &= ~(foreground_mask + background_mask); *attributes |= fg_col << 4; *attributes |= bg_col; } else if (code >= 30 && code <= 37) { // foreground color *attributes &= ~fg_mask[*reverse]; *attributes |= color_table[code - 30] << fg_shift[*reverse]; *support_chaining = true; } else if (code >= 40 && code <= 47) { // background color *attributes &= ~bg_mask[*reverse]; *attributes |= color_table[code - 40] << bg_shift[*reverse]; *support_chaining = true; } } #endif void print(char const* buf) { #ifdef _WIN32 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); int current_attributes = 7; bool reverse = false; SetConsoleTextAttribute(out, current_attributes); char const* start = buf; DWORD written; while (*buf != 0) { if (*buf == '\033' && buf[1] == '[') { WriteFile(out, start, buf - start, &written, NULL); buf += 2; // skip escape and '[' start = buf; if (*buf == 0) break; if (*start == 'K') { // this means clear the rest of the line. CONSOLE_SCREEN_BUFFER_INFO sbi; if (GetConsoleScreenBufferInfo(out, &sbi)) { COORD const pos = sbi.dwCursorPosition; int const width = sbi.dwSize.X; int const run = width - pos.X; DWORD n; FillConsoleOutputAttribute(out, 0x7, run, pos, &n); FillConsoleOutputCharacter(out, ' ', run, pos, &n); } ++buf; start = buf; continue; } else if (*start == 'J') { // clear rest of screen CONSOLE_SCREEN_BUFFER_INFO sbi; if (GetConsoleScreenBufferInfo(out, &sbi)) { COORD pos = sbi.dwCursorPosition; int width = sbi.dwSize.X; int run = (width - pos.X) + width * (sbi.dwSize.Y - pos.Y - 1); DWORD n; FillConsoleOutputAttribute(out, 0x7, run, pos, &n); FillConsoleOutputCharacter(out, ' ', run, pos, &n); } ++buf; start = buf; continue; } one_more: while (*buf != 'm' && *buf != ';' && *buf != 0) ++buf; // this is where we handle reset, color and reverse codes if (*buf == 0) break; int code = atoi(start); bool support_chaining = false; apply_ansi_code(¤t_attributes, &reverse, &support_chaining, code); if (support_chaining) { if (*buf == ';') { ++buf; start = buf; goto one_more; } } else { // ignore codes with multiple fields for now while (*buf != 'm' && *buf != 0) ++buf; } SetConsoleTextAttribute(out, current_attributes); ++buf; // skip 'm' start = buf; } else { ++buf; } } WriteFile(out, start, buf - start, &written, NULL); #else fputs(buf, stdout); #endif } libtorrent-rasterbar-1.1.13/examples/print.hpp000066400000000000000000000016421351156116000214310ustar00rootroot00000000000000#ifndef PRINT_HPP_ #define PRINT_HPP_ #include #include "libtorrent/bitfield.hpp" enum color_code { col_none = -1, col_black = 0, col_red = 1, col_green = 2, col_yellow = 3, col_blue = 4, col_magenta = 5, col_cyan = 6, col_white = 7 }; char const* esc(char const* code); std::string to_string(int v, int width); std::string add_suffix(float val, char const* suffix = 0); std::string color(std::string const& s, color_code c); enum { progress_invert = 1}; std::string const& progress_bar(int progress, int width, color_code c = col_green , char fill = '#', char bg = '-', std::string caption = "", int flags = 0); void set_cursor_pos(int x, int y); void clear_screen(); void clear_rows(int y1, int y2); void terminal_size(int* terminal_width, int* terminal_height); std::string piece_matrix(libtorrent::bitfield const& p, int width, int* height); void print(char const* str); #endif // PRINT_HPP_ libtorrent-rasterbar-1.1.13/examples/run_cmake.sh.in000066400000000000000000000002751351156116000224720ustar00rootroot00000000000000#!/bin/sh cd ${libtorrent_BINARY_DIR}/examples cmake \ -D libtorrent_includes_asio_source=${asio_source} \ -G "${CMAKE_GENERATOR}" \ $@ \ ${libtorrent_SOURCE_DIR}/examples libtorrent-rasterbar-1.1.13/examples/session_view.cpp000066400000000000000000000210751351156116000230070ustar00rootroot00000000000000/* Copyright (c) 2003-2017, 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 "session_view.hpp" #include "print.hpp" #include "libtorrent/session_stats.hpp" #include "libtorrent/torrent_handle.hpp" #include // for std::max namespace lt = libtorrent; session_view::session_view() : m_position(0) , m_print_utp_stats(false) { using libtorrent::find_metric_idx; m_width = 128; std::vector metrics = lt::session_stats_metrics(); m_cnt[0].resize(metrics.size(), 0); m_cnt[1].resize(metrics.size(), 0); m_queued_bytes_idx = find_metric_idx("disk.queued_write_bytes"); m_wasted_bytes_idx = find_metric_idx("net.recv_redundant_bytes"); m_failed_bytes_idx = find_metric_idx("net.recv_failed_bytes"); m_num_peers_idx = find_metric_idx("peer.num_peers_connected"); m_recv_idx = find_metric_idx("net.recv_bytes"); m_sent_idx = find_metric_idx("net.sent_bytes"); m_unchoked_idx = find_metric_idx("peer.num_peers_up_unchoked"); m_unchoke_slots_idx = find_metric_idx("ses.num_unchoke_slots"); m_limiter_up_queue_idx = find_metric_idx("net.limiter_up_queue"); m_limiter_down_queue_idx = find_metric_idx("net.limiter_down_queue"); m_queued_writes_idx = find_metric_idx("disk.num_write_jobs"); m_queued_reads_idx = find_metric_idx("disk.num_read_jobs"); m_writes_cache_idx = find_metric_idx("disk.write_cache_blocks"); m_reads_cache_idx = find_metric_idx("disk.read_cache_blocks"); m_pinned_idx = find_metric_idx("disk.pinned_blocks"); m_num_blocks_read_idx = find_metric_idx("disk.num_blocks_read"); m_cache_hit_idx = find_metric_idx("disk.num_blocks_cache_hits"); m_blocks_in_use_idx = find_metric_idx("disk.disk_blocks_in_use"); m_blocks_written_idx = find_metric_idx("disk.num_blocks_written"); m_write_ops_idx = find_metric_idx("disk.num_write_ops"); m_mfu_size_idx = find_metric_idx("disk.arc_mfu_size"); m_mfu_ghost_idx = find_metric_idx("disk.arc_mfu_ghost_size"); m_mru_size_idx = find_metric_idx("disk.arc_mru_size"); m_mru_ghost_idx = find_metric_idx("disk.arc_mru_ghost_size"); m_utp_idle = find_metric_idx("utp.num_utp_idle"); m_utp_syn_sent = find_metric_idx("utp.num_utp_syn_sent"); m_utp_connected = find_metric_idx("utp.num_utp_connected"); m_utp_fin_sent = find_metric_idx("utp.num_utp_fin_sent"); m_utp_close_wait = find_metric_idx("utp.num_utp_close_wait"); } void session_view::set_pos(int pos) { m_position = pos; } int session_view::pos() const { return m_position; } int session_view::height() const { return 3 + m_print_utp_stats; } void session_view::render() { char str[1024]; int pos = 0; int y = m_position; float seconds = (m_timestamp[0] - m_timestamp[1]) / 1000000.f; int download_rate = (m_cnt[0][m_recv_idx] - m_cnt[1][m_recv_idx]) / seconds; int upload_rate = (m_cnt[0][m_sent_idx] - m_cnt[1][m_sent_idx]) / seconds; pos += snprintf(str, sizeof(str), "%s%s fail: %s down: %s (%s) " " bw queue: %s | %s conns: %3d unchoked: %2d / %2d " " %s\x1b[K" , esc("48;5;238") , esc("1") , add_suffix(m_cnt[0][m_failed_bytes_idx]).c_str() , color(add_suffix(download_rate, "/s"), col_green).c_str() , color(add_suffix(m_cnt[0][m_recv_idx]), col_green).c_str() , color(to_string(m_cnt[0][m_limiter_up_queue_idx], 3), col_red).c_str() , color(to_string(m_cnt[0][m_limiter_down_queue_idx], 3), col_green).c_str() , int(m_cnt[0][m_num_peers_idx]) , int(m_cnt[0][m_unchoked_idx]) , int(m_cnt[0][m_unchoke_slots_idx]) , esc("0")); set_cursor_pos(0, y++); print(str); snprintf(str, sizeof(str), "%s%swaste: %s up: %s (%s) " "disk queue: %s | %s cache w: %3d%% r: %3d%% " "size: w: %s r: %s total: %s %s\x1b[K" #ifdef _WIN32 , esc("40") #else , esc("48;5;238") #endif , esc("1") , add_suffix(m_cnt[0][m_wasted_bytes_idx]).c_str() , color(add_suffix(upload_rate, "/s"), col_red).c_str() , color(add_suffix(m_cnt[0][m_sent_idx]), col_red).c_str() , color(to_string(m_cnt[0][m_queued_reads_idx], 3), col_red).c_str() , color(to_string(m_cnt[0][m_queued_writes_idx], 3), col_green).c_str() , int((m_cnt[0][m_blocks_written_idx] - m_cnt[0][m_write_ops_idx]) * 100 / (std::max)(boost::uint64_t(1), m_cnt[0][m_blocks_written_idx])) , int(m_cnt[0][m_cache_hit_idx] * 100 / (std::max)(boost::uint64_t(1), m_cnt[0][m_num_blocks_read_idx])) , add_suffix(m_cnt[0][m_writes_cache_idx] * 16 * 1024).c_str() , add_suffix(m_cnt[0][m_reads_cache_idx] * 16 * 1024).c_str() , add_suffix(m_cnt[0][m_blocks_in_use_idx] * 16 * 1024).c_str() , esc("0")); set_cursor_pos(0, y++); print(str); /* snprintf(str, sizeof(str), "| timing - " " read: %6d ms | write: %6d ms | hash: %6d" , cs.average_read_time / 1000, cs.average_write_time / 1000 , cs.average_hash_time / 1000); set_cursor_pos(0, y++); print(str); snprintf(str, sizeof(str), "| jobs - queued: %4d (%4d) pending: %4d blocked: %4d " "queued-bytes: %5" PRId64 " kB" , cs.queued_jobs, cs.peak_queued, cs.pending_jobs, cs.blocked_jobs , m_cnt[0][m_queued_bytes_idx] / 1000); set_cursor_pos(0, y++); print(str); snprintf(str, sizeof(str), "| cache - total: %4d read: %4d write: %4d pinned: %4d write-queue: %4d" , cs.read_cache_size + cs.write_cache_size, cs.read_cache_size , cs.write_cache_size, cs.pinned_blocks , int(m_cnt[0][m_queued_bytes_idx] / 0x4000)); set_cursor_pos(0, y++); print(str); */ int mru_size = int(m_cnt[0][m_mru_size_idx] + m_cnt[0][m_mru_ghost_idx]); int mfu_size = int(m_cnt[0][m_mfu_size_idx] + m_cnt[0][m_mfu_ghost_idx]); int arc_size = mru_size + mfu_size; char mru_caption[100]; snprintf(mru_caption, sizeof(mru_caption), "MRU: %d (%d)" , int(m_cnt[0][m_mru_size_idx]), int(m_cnt[0][m_mru_ghost_idx])); char mfu_caption[100]; snprintf(mfu_caption, sizeof(mfu_caption), "MFU: %d (%d)" , int(m_cnt[0][m_mfu_size_idx]), int(m_cnt[0][m_mfu_ghost_idx])); pos = snprintf(str, sizeof(str), "cache: "); if (arc_size > 0) { if (mru_size > 0) { pos += snprintf(str + pos, sizeof(str) - pos, "%s" , progress_bar(m_cnt[0][m_mru_ghost_idx] * 1000 / mru_size , mru_size * (m_width-8) / arc_size, col_yellow, '-', '#' , mru_caption, progress_invert).c_str()); } pos += snprintf(str + pos, sizeof(str) - pos, "|"); if (mfu_size) { pos += snprintf(str + pos, sizeof(str) - pos, "%s" , progress_bar(m_cnt[0][m_mfu_size_idx] * 1000 / mfu_size , mfu_size * (m_width-8) / arc_size, col_green, '#', '-' , mfu_caption).c_str()); } } pos += snprintf(str + pos, sizeof(str) - pos, "\x1b[K"); set_cursor_pos(0, y++); print(str); if (m_print_utp_stats) { snprintf(str, sizeof(str), "uTP idle: %d syn: %d est: %d fin: %d wait: %d\x1b[K" , int(m_cnt[0][m_utp_idle]) , int(m_cnt[0][m_utp_syn_sent]) , int(m_cnt[0][m_utp_connected]) , int(m_cnt[0][m_utp_fin_sent]) , int(m_cnt[0][m_utp_close_wait])); set_cursor_pos(0, y++); print(str); } } void session_view::update_counters(boost::uint64_t* stats_counters , int num_cnt, boost::uint64_t t) { // only update the previous counters if there's been enough // time since it was last updated if (t - m_timestamp[1] > 2000000) { m_cnt[1].swap(m_cnt[0]); m_timestamp[1] = m_timestamp[0]; } m_cnt[0].assign(stats_counters, stats_counters + num_cnt); m_timestamp[0] = t; render(); } libtorrent-rasterbar-1.1.13/examples/session_view.hpp000066400000000000000000000057171351156116000230210ustar00rootroot00000000000000/* Copyright (c) 2003-2017, 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 SESSION_VIEW_HPP_ #define SESSION_VIEW_HPP_ #include #include struct session_view { session_view(); void set_pos(int pos); int pos() const; int height() const; void render(); void print_utp_stats(bool p) { m_print_utp_stats = p; } bool print_utp_stats() const { return m_print_utp_stats; } void update_counters(boost::uint64_t* stats_counters, int num_cnt , boost::uint64_t t); private: int m_position; int m_width; // there are two sets of counters. the current one and the last one. This // is used to calculate rates std::vector m_cnt[2]; // the timestamps of the counters in m_cnt[0] and m_cnt[1] // respectively. The timestamps are microseconds since session start boost::uint64_t m_timestamp[2]; bool m_print_utp_stats; int m_queued_bytes_idx; int m_wasted_bytes_idx; int m_failed_bytes_idx; int m_num_peers_idx; int m_recv_idx; int m_sent_idx; int m_unchoked_idx; int m_unchoke_slots_idx; int m_limiter_up_queue_idx; int m_limiter_down_queue_idx; int m_queued_writes_idx; int m_queued_reads_idx; int m_writes_cache_idx; int m_reads_cache_idx; int m_pinned_idx; int m_num_blocks_read_idx; int m_cache_hit_idx; int m_blocks_in_use_idx; int m_blocks_written_idx; int m_write_ops_idx; int m_mfu_size_idx; int m_mfu_ghost_idx; int m_mru_size_idx; int m_mru_ghost_idx; int m_utp_idle; int m_utp_syn_sent; int m_utp_connected; int m_utp_fin_sent; int m_utp_close_wait; }; #endif libtorrent-rasterbar-1.1.13/examples/simple_client.cpp000066400000000000000000000042501351156116000231150ustar00rootroot00000000000000/* Copyright (c) 2003-2017, 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 #include #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/session.hpp" #include "libtorrent/torrent_info.hpp" int main(int argc, char* argv[]) try { if (argc != 2) { std::cerr << "usage: ./simple_client torrent-file\n" "to stop the client, press return.\n"; return 1; } lt::session s; lt::add_torrent_params p; p.save_path = "./"; lt::error_code ec; p.ti = boost::make_shared(std::string(argv[1]), 0); s.add_torrent(p); // wait for the user to end char a; scanf("%c\n", &a); return 0; } catch (std::exception const& e) { std::cerr << "ERROR: " << e.what() << "\n"; } libtorrent-rasterbar-1.1.13/examples/stats_counters.cpp000066400000000000000000000035051351156116000233500ustar00rootroot00000000000000/* Copyright (c) 2008, 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 #include "libtorrent/session_stats.hpp" using namespace libtorrent; int main() { std::vector m = session_stats_metrics(); for (int i = 0; i < int(m.size()); ++i) { printf("%s: %s (%d)\n" , m[i].type == stats_metric::type_counter ? "CNTR" : "GAUG" , m[i].name, m[i].value_index); } return 0; } libtorrent-rasterbar-1.1.13/examples/torrent_view.cpp000066400000000000000000000273511351156116000230240ustar00rootroot00000000000000/* Copyright (c) 2003-2017, 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 "torrent_view.hpp" #include "print.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/torrent_status.hpp" const int header_size = 2; std::string torrent_state(lt::torrent_status const& s) { static char const* state_str[] = {"checking (q)", "checking", "dl metadata" , "downloading", "finished", "seeding", "allocating", "checking (r)"}; if (s.errc) return s.errc.message(); std::string ret; if (s.paused && !s.auto_managed) ret += "paused"; else if (s.paused && s.auto_managed) ret += "queued"; else if (s.upload_mode) ret += "upload mode"; else ret += state_str[s.state]; if (!s.paused && !s.auto_managed) ret += " [F]"; char buf[10]; snprintf(buf, sizeof(buf), " (%.1f%%)", s.progress_ppm / 10000.f); ret += buf; return ret; } bool compare_torrent(lt::torrent_status const* lhs, lt::torrent_status const* rhs) { if (lhs->queue_position != -1 && rhs->queue_position != -1) { // both are downloading, sort by queue pos return lhs->queue_position < rhs->queue_position; } else if (lhs->queue_position == -1 && rhs->queue_position == -1) { // both are seeding, sort by seed-rank if (lhs->seed_rank != rhs->seed_rank) return lhs->seed_rank > rhs->seed_rank; return lhs->info_hash < rhs->info_hash; } return (lhs->queue_position == -1) < (rhs->queue_position == -1); } torrent_view::torrent_view() : m_active_torrent(0) , m_scroll_position(0) , m_torrent_filter(0) , m_width(80) , m_height(30) {} void torrent_view::set_size(int width, int height) { if (m_width == width && m_height == height) return; m_width = width; m_height = height; render(); } int torrent_view::filter() const { return m_torrent_filter; } void torrent_view::set_filter(int filter) { if (filter == m_torrent_filter) return; m_torrent_filter = filter; update_filtered_torrents(); render(); } // returns the lt::torrent_status of the currently selected torrent. lt::torrent_status const& torrent_view::get_active_torrent() const { if (m_active_torrent >= int(m_filtered_handles.size())) m_active_torrent = int(m_filtered_handles.size()) - 1; if (m_active_torrent < 0) m_active_torrent = 0; TORRENT_ASSERT(m_active_torrent >= 0); return *m_filtered_handles[m_active_torrent]; } lt::torrent_handle torrent_view::get_active_handle() const { if (m_active_torrent >= int(m_filtered_handles.size())) m_active_torrent = int(m_filtered_handles.size()) - 1; if (m_active_torrent < 0) m_active_torrent = 0; TORRENT_ASSERT(m_active_torrent >= 0); if (m_filtered_handles.empty()) return lt::torrent_handle(); return m_filtered_handles[m_active_torrent]->handle; } void torrent_view::update_torrents(std::vector const& st) { std::set updates; bool need_filter_update = false; for (std::vector::const_iterator i = st.begin(); i != st.end(); ++i) { boost::unordered_set::iterator j = m_all_handles.find(*i); // add new entries here if (j == m_all_handles.end()) { j = m_all_handles.insert(*i).first; if (show_torrent(*j)) { m_filtered_handles.push_back(&*j); need_filter_update = true; } } else { bool prev_show = show_torrent(*j); ((lt::torrent_status&)*j) = *i; if (prev_show != show_torrent(*j)) need_filter_update = true; else updates.insert(i->handle); } } if (need_filter_update) { update_filtered_torrents(); render(); } else { int torrent_index = 0; for (std::vector::iterator i = m_filtered_handles.begin(); i != m_filtered_handles.end(); ++torrent_index) { if (torrent_index < m_scroll_position || torrent_index >= m_scroll_position + m_height - header_size) { ++i; continue; } lt::torrent_status const& s = **i; ++i; if (!s.handle.is_valid()) continue; if (updates.count(s.handle) == 0) continue; set_cursor_pos(0, header_size + torrent_index - m_scroll_position); print_torrent(s, torrent_index == m_active_torrent); } } } int torrent_view::height() const { return m_height; } void torrent_view::arrow_up() { if (m_filtered_handles.empty()) return; if (m_active_torrent <= 0) return; if (m_active_torrent - 1 < m_scroll_position) { --m_active_torrent; m_scroll_position = m_active_torrent; TORRENT_ASSERT(m_scroll_position >= 0); render(); return; } set_cursor_pos(0, header_size + m_active_torrent - m_scroll_position); print_torrent(*m_filtered_handles[m_active_torrent], false); --m_active_torrent; TORRENT_ASSERT(m_active_torrent >= 0); set_cursor_pos(0, header_size + m_active_torrent - m_scroll_position); print_torrent(*m_filtered_handles[m_active_torrent], true); } void torrent_view::arrow_down() { if (m_filtered_handles.empty()) return; if (m_active_torrent >= int(m_filtered_handles.size()) - 1) return; int bottom_pos = m_height - header_size - 1; if (m_active_torrent - m_scroll_position + 1 > bottom_pos) { ++m_active_torrent; m_scroll_position = m_active_torrent - bottom_pos; TORRENT_ASSERT(m_scroll_position >= 0); render(); return; } set_cursor_pos(0, header_size + m_active_torrent - m_scroll_position); print_torrent(*m_filtered_handles[m_active_torrent], false); TORRENT_ASSERT(m_active_torrent >= 0); ++m_active_torrent; set_cursor_pos(0, header_size + m_active_torrent - m_scroll_position); print_torrent(*m_filtered_handles[m_active_torrent], true); } void torrent_view::render() { print_tabs(); print_headers(); int lines_printed = header_size; int torrent_index = 0; for (std::vector::iterator i = m_filtered_handles.begin(); i != m_filtered_handles.end(); ++torrent_index) { if (torrent_index < m_scroll_position) { ++i; continue; } if (lines_printed >= m_height) break; lt::torrent_status const& s = **i; if (!s.handle.is_valid()) { i = m_filtered_handles.erase(i); continue; } ++i; set_cursor_pos(0, torrent_index + header_size - m_scroll_position); print_torrent(s, torrent_index == m_active_torrent); ++lines_printed; } clear_rows(torrent_index + header_size, m_height); } void torrent_view::print_tabs() { set_cursor_pos(0, 0); char str[400]; int pos = 0; char const* filter_names[] = { "all", "downloading", "non-paused" , "seeding", "queued", "stopped", "checking", "loaded"}; for (int i = 0; i < int(sizeof(filter_names)/sizeof(filter_names[0])); ++i) { pos += snprintf(str+ pos, sizeof(str) - pos, "%s[%s]%s" , m_torrent_filter == i?esc("7"):"" , filter_names[i], m_torrent_filter == i?esc("0"):""); } pos += snprintf(str + pos, sizeof(str) - pos, "\x1b[K"); if (m_width + 1 < int(sizeof(str))) str[m_width + 1] = '\0'; print(str); } void torrent_view::print_headers() { set_cursor_pos(0, 1); char str[400]; // print title bar for torrent list snprintf(str, sizeof(str) , " %-3s %-50s %-35s %-17s %-17s %-11s %-6s %-6s %-4s\x1b[K" , "#", "Name", "Progress", "Download", "Upload", "Peers (D:S)" , "Down", "Up", "Flags"); if (m_width + 1 < int(sizeof(str))) str[m_width + 1] = '\0'; print(str); } void torrent_view::print_torrent(lt::torrent_status const& s, bool selected) { int pos = 0; char str[512]; // the active torrent is highligted in the list // this inverses the forground and background colors char const* selection = ""; if (selected) selection = "\x1b[1m\x1b[44m"; char queue_pos[16] = {0}; if (s.queue_position == -1) snprintf(queue_pos, sizeof(queue_pos), "-"); else snprintf(queue_pos, sizeof(queue_pos), "%d", s.queue_position); std::string name = s.name; if (name.size() > 50) name.resize(50); color_code progress_bar_color = col_yellow; if (s.errc) progress_bar_color = col_red; else if (s.paused) progress_bar_color = col_blue; else if (s.state == lt::torrent_status::downloading_metadata) progress_bar_color = col_magenta; else if (s.current_tracker.empty()) progress_bar_color = col_green; pos += snprintf(str + pos, sizeof(str) - pos, "%s%c%-3s %-50s %s%s %s (%s) " "%s (%s) %5d:%-5d %s %s %c" , selection , s.is_loaded ? 'L' : ' ' , queue_pos , name.c_str() , progress_bar(s.progress_ppm / 1000, 35, progress_bar_color, '-', '#', torrent_state(s)).c_str() , selection , color(add_suffix(s.download_rate, "/s"), col_green).c_str() , color(add_suffix(s.total_download), col_green).c_str() , color(add_suffix(s.upload_rate, "/s"), col_red).c_str() , color(add_suffix(s.total_upload), col_red).c_str() , s.num_peers - s.num_seeds, s.num_seeds , color(add_suffix(s.all_time_download), col_green).c_str() , color(add_suffix(s.all_time_upload), col_red).c_str() , s.need_save_resume?'S':' '); // if this is the selected torrent, restore the background color if (selected) pos += snprintf(str + pos, sizeof(str) - pos, "%s", esc("0")); pos += snprintf(str + pos, sizeof(str) - pos, "\x1b[K"); print(str); } bool torrent_view::show_torrent(lt::torrent_status const& st) { switch (m_torrent_filter) { case torrents_all: return true; case torrents_downloading: return !st.paused && st.state != lt::torrent_status::seeding && st.state != lt::torrent_status::finished; case torrents_not_paused: return !st.paused; case torrents_seeding: return !st.paused && (st.state == lt::torrent_status::seeding || st.state == lt::torrent_status::finished); case torrents_queued: return st.paused && st.auto_managed; case torrents_stopped: return st.paused && !st.auto_managed; case torrents_checking: return st.state == lt::torrent_status::checking_files; case torrents_loaded: return st.is_loaded; } return true; } // refresh all pointers in m_filtered_handles. This must be done when // inserting or removing elements from m_all_handles, since pointers may // be invalidated or when a torrent changes status to either become // visible or filtered void torrent_view::update_filtered_torrents() { m_scroll_position = 0; m_filtered_handles.clear(); for (boost::unordered_set::iterator i = m_all_handles.begin() , end(m_all_handles.end()); i != end; ++i) { if (!show_torrent(*i)) continue; m_filtered_handles.push_back(&*i); } if (m_active_torrent >= int(m_filtered_handles.size())) m_active_torrent = m_filtered_handles.size() - 1; if (m_active_torrent < 0) m_active_torrent = 0; TORRENT_ASSERT(m_active_torrent >= 0); std::sort(m_filtered_handles.begin(), m_filtered_handles.end(), &compare_torrent); } libtorrent-rasterbar-1.1.13/examples/torrent_view.hpp000066400000000000000000000062201351156116000230210ustar00rootroot00000000000000/* Copyright (c) 2003-2017, 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_VIEW_HPP_ #define TORRENT_VIEW_HPP_ #include #include #include #include "libtorrent/fwd.hpp" #include "libtorrent/torrent_status.hpp" namespace lt = libtorrent; struct torrent_view { torrent_view(); void set_size(int width, int height); enum { torrents_all, torrents_downloading, torrents_not_paused, torrents_seeding, torrents_queued, torrents_stopped, torrents_checking, torrents_loaded, torrents_max }; int filter() const; void set_filter(int filter); // returns the lt::torrent_status of the currently selected torrent. lt::torrent_status const& get_active_torrent() const; lt::torrent_handle get_active_handle() const; void update_torrents(std::vector const& st); int height() const; void arrow_up(); void arrow_down(); void render(); private: void print_tabs(); void print_headers(); void print_torrent(lt::torrent_status const& s, bool selected); bool show_torrent(lt::torrent_status const& st); // refresh all pointers in m_filtered_handles. This must be done when // inserting or removing elements from m_all_handles, since pointers may // be invalidated or when a torrent changes status to either become // visible or filtered void update_filtered_torrents(); // all torrents boost::unordered_set m_all_handles; // pointers into m_all_handles of the remaining torrents after filtering std::vector m_filtered_handles; mutable int m_active_torrent; // index into m_filtered_handles int m_scroll_position; int m_torrent_filter; int m_width; int m_height; }; #endif // TORRENT_VIEW_HPP_ libtorrent-rasterbar-1.1.13/examples/upnp_test.cpp000066400000000000000000000062001351156116000223040ustar00rootroot00000000000000/* 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 #include "libtorrent/session.hpp" #include "libtorrent/alert_types.hpp" char const* timestamp() { time_t t = std::time(0); tm* timeinfo = std::localtime(&t); static char str[200]; std::strftime(str, 200, "%b %d %X", timeinfo); return str; } void print_alert(libtorrent::alert const* a) { using namespace libtorrent; if (alert_cast(a)) { printf("%s","\x1b[32m"); } else if (alert_cast(a)) { printf("%s","\x1b[33m"); } printf("[%s] %s\n", timestamp(), a->message().c_str()); printf("%s", "\x1b[0m"); } int main(int argc, char* argv[]) { using namespace libtorrent; namespace lt = libtorrent; if (argc != 1) { fputs("usage: ./upnp_test\n", stderr); return 1; } settings_pack p; p.set_int(settings_pack::alert_mask, alert::port_mapping_notification); lt::session s(p); for (;;) { alert const* a = s.wait_for_alert(seconds(5)); if (a == 0) { settings_pack p; p.set_bool(settings_pack::enable_upnp, false); p.set_bool(settings_pack::enable_natpmp, false); s.apply_settings(p); break; } std::vector alerts; s.pop_alerts(&alerts); for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { print_alert(*i); } } printf("\x1b[1m\n\n===================== done mapping. Now deleting mappings ========================\n\n\n\x1b[0m"); for (;;) { alert const* a = s.wait_for_alert(seconds(5)); if (a == 0) break; std::vector alerts; s.pop_alerts(&alerts); for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { print_alert(*i); } } return 0; } libtorrent-rasterbar-1.1.13/include/000077500000000000000000000000001351156116000173665ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/include/libtorrent/000077500000000000000000000000001351156116000215525ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/include/libtorrent/ConvertUTF.h000066400000000000000000000146441351156116000237330ustar00rootroot00000000000000/* * Copyright 2001-2004 Unicode, Inc. * * Disclaimer * * This source code is provided as is by Unicode, Inc. No claims are * made as to fitness for any particular purpose. No warranties of any * kind are expressed or implied. The recipient agrees to determine * applicability of information provided. If this file has been * purchased on magnetic or optical media from Unicode, Inc., the * sole remedy for any claim will be exchange of defective media * within 90 days of receipt. * * Limitations on Rights to Redistribute This Code * * Unicode, Inc. hereby grants the right to freely use the information * supplied in this file in the creation of products supporting the * Unicode Standard, and to make copies of this file in any form * for internal or external distribution as long as this notice * remains attached. */ /* --------------------------------------------------------------------- Conversions between UTF32, UTF-16, and UTF-8. Header file. Several funtions are included here, forming a complete set of conversions between the three formats. UTF-7 is not included here, but is handled in a separate source file. Each of these routines takes pointers to input buffers and output buffers. The input buffers are const. Each routine converts the text between *sourceStart and sourceEnd, putting the result into the buffer between *targetStart and targetEnd. Note: the end pointers are *after* the last item: e.g. *(sourceEnd - 1) is the last item. The return result indicates whether the conversion was successful, and if not, whether the problem was in the source or target buffers. (Only the first encountered problem is indicated.) After the conversion, *sourceStart and *targetStart are both updated to point to the end of last text successfully converted in the respective buffers. Input parameters: sourceStart - pointer to a pointer to the source buffer. The contents of this are modified on return so that it points at the next thing to be converted. targetStart - similarly, pointer to pointer to the target buffer. sourceEnd, targetEnd - respectively pointers to the ends of the two buffers, for overflow checking only. These conversion functions take a ConversionFlags argument. When this flag is set to strict, both irregular sequences and isolated surrogates will cause an error. When the flag is set to lenient, both irregular sequences and isolated surrogates are converted. Whether the flag is strict or lenient, all illegal sequences will cause an error return. This includes sequences such as: , , or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code must check for illegal sequences. When the flag is set to lenient, characters over 0x10FFFF are converted to the replacement character; otherwise (when the flag is set to strict) they constitute an error. Output parameters: The value "sourceIllegal" is returned from some routines if the input sequence is malformed. When "sourceIllegal" is returned, the source value will point to the illegal value that caused the problem. E.g., in UTF-8 when a sequence is malformed, it points to the start of the malformed sequence. Author: Mark E. Davis, 1994. Rev History: Rick McGowan, fixes & updates May 2001. Fixes & updates, Sept 2001. ------------------------------------------------------------------------ */ /* --------------------------------------------------------------------- The following 4 definitions are compiler-specific. The C standard does not guarantee that wchar_t has at least 16 bits, so wchar_t is no less portable than unsigned short! All should be unsigned values to avoid sign extension during bit mask & shift operations. ------------------------------------------------------------------------ */ #ifdef __cplusplus #include "libtorrent/config.hpp" // these are standard C types, but they might // not be available in c++ #include typedef boost::uint32_t UTF32; typedef boost::uint16_t UTF16; typedef boost::uint8_t UTF8; extern "C" { #else #define TORRENT_EXTRA_EXPORT #ifdef _MSC_VER // msvc doesn't seem to have stdint.h typedef unsigned __int32 UTF32; typedef unsigned __int16 UTF16; typedef unsigned __int8 UTF8; #else #include typedef uint32_t UTF32; typedef uint16_t UTF16; typedef uint8_t UTF8; #endif #endif typedef unsigned char Boolean; /* 0 or 1 */ /* Some fundamental constants */ #define UNI_REPLACEMENT_CHAR UTF32(0x0000FFFD) #define UNI_MAX_BMP UTF32(0x0000FFFF) #define UNI_MAX_UTF16 UTF32(0x0010FFFF) #define UNI_MAX_UTF32 UTF32(0x7FFFFFFF) #define UNI_MAX_LEGAL_UTF32 UTF32(0x0010FFFF) typedef enum { conversionOK, /* conversion successful */ sourceExhausted, /* partial character in source, but hit end */ targetExhausted, /* insuff. room in target for conversion */ sourceIllegal /* source sequence is illegal/malformed */ } ConversionResult; typedef enum { strictConversion = 0, lenientConversion } ConversionFlags; TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF8toUTF16 ( const UTF8** sourceStart, const UTF8* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF16toUTF8 ( const UTF16** sourceStart, const UTF16* sourceEnd, UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF8toUTF32 ( const UTF8** sourceStart, const UTF8* sourceEnd, UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF32toUTF8 ( const UTF32** sourceStart, const UTF32* sourceEnd, UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF16toUTF32 ( const UTF16** sourceStart, const UTF16* sourceEnd, UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF32toUTF16 ( const UTF32** sourceStart, const UTF32* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); TORRENT_EXTRA_EXPORT Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); TORRENT_EXTRA_EXPORT Boolean isLegalUTF8(const UTF8 *source, int length); extern const char trailingBytesForUTF8[256]; extern const UTF32 offsetsFromUTF8[6]; #ifdef __cplusplus } #endif /* --------------------------------------------------------------------- */ libtorrent-rasterbar-1.1.13/include/libtorrent/Makefile.am000066400000000000000000000151601351156116000236110ustar00rootroot00000000000000includedir = @includedir@/libtorrent nobase_include_HEADERS = \ address.hpp \ add_torrent_params.hpp \ alert.hpp \ alert_manager.hpp \ alert_types.hpp \ alloca.hpp \ announce_entry.hpp \ assert.hpp \ bandwidth_limit.hpp \ bandwidth_manager.hpp \ bandwidth_socket.hpp \ bandwidth_queue_entry.hpp \ bencode.hpp \ bdecode.hpp \ bitfield.hpp \ block_cache.hpp \ bloom_filter.hpp \ broadcast_socket.hpp \ bt_peer_connection.hpp \ buffer.hpp \ build_config.hpp \ chained_buffer.hpp \ choker.hpp \ close_reason.hpp \ config.hpp \ ConvertUTF.h \ copy_ptr.hpp \ crc32c.hpp \ create_torrent.hpp \ deadline_timer.hpp \ debug.hpp \ disk_buffer_holder.hpp \ disk_buffer_pool.hpp \ disk_interface.hpp \ disk_io_job.hpp \ disk_io_thread.hpp \ disk_observer.hpp \ disk_job_pool.hpp \ ed25519.hpp \ entry.hpp \ enum_net.hpp \ error.hpp \ error_code.hpp \ export.hpp \ extensions.hpp \ file.hpp \ file_pool.hpp \ file_storage.hpp \ fingerprint.hpp \ fwd.hpp \ gzip.hpp \ hasher.hpp \ hex.hpp \ heterogeneous_queue.hpp \ http_connection.hpp \ http_parser.hpp \ http_seed_connection.hpp \ http_stream.hpp \ http_tracker_connection.hpp \ i2p_stream.hpp \ identify_client.hpp \ instantiate_connection.hpp \ invariant_check.hpp \ io.hpp \ io_service.hpp \ io_service_fwd.hpp \ ip_filter.hpp \ ip_voter.hpp \ lazy_entry.hpp \ link.hpp \ linked_list.hpp \ lsd.hpp \ magnet_uri.hpp \ max.hpp \ natpmp.hpp \ network_thread_pool.hpp \ operations.hpp \ packet_buffer.hpp \ parse_url.hpp \ part_file.hpp \ pe_crypto.hpp \ performance_counters.hpp \ peer_connection.hpp \ peer_connection_handle.hpp \ peer_connection_interface.hpp \ peer.hpp \ peer_class.hpp \ peer_class_set.hpp \ peer_class_type_filter.hpp \ peer_id.hpp \ peer_info.hpp \ peer_request.hpp \ piece_block_progress.hpp \ piece_picker.hpp \ platform_util.hpp \ peer_list.hpp \ proxy_base.hpp \ puff.hpp \ random.hpp \ receive_buffer.hpp \ resolve_links.hpp \ resolver.hpp \ resolver_interface.hpp \ request_blocks.hpp \ rss.hpp \ session.hpp \ session_handle.hpp \ session_settings.hpp \ session_stats.hpp \ session_status.hpp \ settings_pack.hpp \ sha1.hpp \ sha1_hash.hpp \ sliding_average.hpp \ socket.hpp \ socket_io.hpp \ socket_type.hpp \ socket_type_fwd.hpp \ socks5_stream.hpp \ ssl_stream.hpp \ stack_allocator.hpp \ stat.hpp \ stat_cache.hpp \ storage.hpp \ storage_defs.hpp \ tailqueue.hpp \ string_util.hpp \ thread.hpp \ thread_pool.hpp \ time.hpp \ timestamp_history.hpp \ torrent_handle.hpp \ torrent.hpp \ torrent_info.hpp \ torrent_peer.hpp \ torrent_peer_allocator.hpp \ tracker_manager.hpp \ torrent_status.hpp \ udp_socket.hpp \ udp_tracker_connection.hpp \ uncork_interface.hpp \ union_endpoint.hpp \ upnp.hpp \ utp_socket_manager.hpp \ utp_stream.hpp \ utf8.hpp \ vector_utils.hpp \ version.hpp \ web_connection_base.hpp \ web_peer_connection.hpp \ xml_parse.hpp \ \ tommath.h \ tommath_class.h \ tommath_superclass.h \ tommath_private.h \ \ aux_/alert_manager_variadic_emplace.hpp \ aux_/allocating_handler.hpp \ aux_/cpuid.hpp \ aux_/disable_warnings_push.hpp \ aux_/disable_warnings_pop.hpp \ aux_/escape_string.hpp \ aux_/merkle.hpp \ aux_/session_call.hpp \ aux_/session_impl.hpp \ aux_/session_settings.hpp \ aux_/proxy_settings.hpp \ aux_/session_interface.hpp \ aux_/time.hpp \ aux_/file_progress.hpp \ aux_/openssl.hpp \ aux_/byteswap.hpp \ aux_/route.h \ \ extensions/lt_trackers.hpp \ extensions/metadata_transfer.hpp \ extensions/smart_ban.hpp \ extensions/ut_metadata.hpp \ extensions/ut_pex.hpp \ \ kademlia/dht_storage.hpp \ kademlia/dht_tracker.hpp \ kademlia/dht_observer.hpp \ kademlia/direct_request.hpp \ kademlia/dos_blocker.hpp \ kademlia/find_data.hpp \ kademlia/put_data.hpp \ kademlia/msg.hpp \ kademlia/node.hpp \ kademlia/node_entry.hpp \ kademlia/node_id.hpp \ kademlia/observer.hpp \ kademlia/refresh.hpp \ kademlia/routing_table.hpp \ kademlia/rpc_manager.hpp \ kademlia/traversal_algorithm.hpp \ kademlia/item.hpp \ kademlia/get_item.hpp \ kademlia/get_peers.hpp libtorrent-rasterbar-1.1.13/include/libtorrent/Makefile.in000066400000000000000000000612441351156116000236260ustar00rootroot00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 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@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd 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@ target_triplet = @target@ subdir = include/libtorrent ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ $(top_srcdir)/m4/ax_boost_chrono.m4 \ $(top_srcdir)/m4/ax_boost_python.m4 \ $(top_srcdir)/m4/ax_boost_random.m4 \ $(top_srcdir)/m4/ax_boost_system.m4 \ $(top_srcdir)/m4/ax_check_openssl.m4 \ $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(nobase_include_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac 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 = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(includedir)" HEADERS = $(nobase_include_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BOOST_CHRONO_LIB = @BOOST_CHRONO_LIB@ BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ BOOST_LDFLAGS = @BOOST_LDFLAGS@ BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ BOOST_RANDOM_LIB = @BOOST_RANDOM_LIB@ BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUGFLAGS = @DEBUGFLAGS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONV_LIBS = @ICONV_LIBS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ PYTHON_CXXFLAGS = @PYTHON_CXXFLAGS@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@/libtorrent infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ nobase_include_HEADERS = \ address.hpp \ add_torrent_params.hpp \ alert.hpp \ alert_manager.hpp \ alert_types.hpp \ alloca.hpp \ announce_entry.hpp \ assert.hpp \ bandwidth_limit.hpp \ bandwidth_manager.hpp \ bandwidth_socket.hpp \ bandwidth_queue_entry.hpp \ bencode.hpp \ bdecode.hpp \ bitfield.hpp \ block_cache.hpp \ bloom_filter.hpp \ broadcast_socket.hpp \ bt_peer_connection.hpp \ buffer.hpp \ build_config.hpp \ chained_buffer.hpp \ choker.hpp \ close_reason.hpp \ config.hpp \ ConvertUTF.h \ copy_ptr.hpp \ crc32c.hpp \ create_torrent.hpp \ deadline_timer.hpp \ debug.hpp \ disk_buffer_holder.hpp \ disk_buffer_pool.hpp \ disk_interface.hpp \ disk_io_job.hpp \ disk_io_thread.hpp \ disk_observer.hpp \ disk_job_pool.hpp \ ed25519.hpp \ entry.hpp \ enum_net.hpp \ error.hpp \ error_code.hpp \ export.hpp \ extensions.hpp \ file.hpp \ file_pool.hpp \ file_storage.hpp \ fingerprint.hpp \ fwd.hpp \ gzip.hpp \ hasher.hpp \ hex.hpp \ heterogeneous_queue.hpp \ http_connection.hpp \ http_parser.hpp \ http_seed_connection.hpp \ http_stream.hpp \ http_tracker_connection.hpp \ i2p_stream.hpp \ identify_client.hpp \ instantiate_connection.hpp \ invariant_check.hpp \ io.hpp \ io_service.hpp \ io_service_fwd.hpp \ ip_filter.hpp \ ip_voter.hpp \ lazy_entry.hpp \ link.hpp \ linked_list.hpp \ lsd.hpp \ magnet_uri.hpp \ max.hpp \ natpmp.hpp \ network_thread_pool.hpp \ operations.hpp \ packet_buffer.hpp \ parse_url.hpp \ part_file.hpp \ pe_crypto.hpp \ performance_counters.hpp \ peer_connection.hpp \ peer_connection_handle.hpp \ peer_connection_interface.hpp \ peer.hpp \ peer_class.hpp \ peer_class_set.hpp \ peer_class_type_filter.hpp \ peer_id.hpp \ peer_info.hpp \ peer_request.hpp \ piece_block_progress.hpp \ piece_picker.hpp \ platform_util.hpp \ peer_list.hpp \ proxy_base.hpp \ puff.hpp \ random.hpp \ receive_buffer.hpp \ resolve_links.hpp \ resolver.hpp \ resolver_interface.hpp \ request_blocks.hpp \ rss.hpp \ session.hpp \ session_handle.hpp \ session_settings.hpp \ session_stats.hpp \ session_status.hpp \ settings_pack.hpp \ sha1.hpp \ sha1_hash.hpp \ sliding_average.hpp \ socket.hpp \ socket_io.hpp \ socket_type.hpp \ socket_type_fwd.hpp \ socks5_stream.hpp \ ssl_stream.hpp \ stack_allocator.hpp \ stat.hpp \ stat_cache.hpp \ storage.hpp \ storage_defs.hpp \ tailqueue.hpp \ string_util.hpp \ thread.hpp \ thread_pool.hpp \ time.hpp \ timestamp_history.hpp \ torrent_handle.hpp \ torrent.hpp \ torrent_info.hpp \ torrent_peer.hpp \ torrent_peer_allocator.hpp \ tracker_manager.hpp \ torrent_status.hpp \ udp_socket.hpp \ udp_tracker_connection.hpp \ uncork_interface.hpp \ union_endpoint.hpp \ upnp.hpp \ utp_socket_manager.hpp \ utp_stream.hpp \ utf8.hpp \ vector_utils.hpp \ version.hpp \ web_connection_base.hpp \ web_peer_connection.hpp \ xml_parse.hpp \ \ tommath.h \ tommath_class.h \ tommath_superclass.h \ tommath_private.h \ \ aux_/alert_manager_variadic_emplace.hpp \ aux_/allocating_handler.hpp \ aux_/cpuid.hpp \ aux_/disable_warnings_push.hpp \ aux_/disable_warnings_pop.hpp \ aux_/escape_string.hpp \ aux_/merkle.hpp \ aux_/session_call.hpp \ aux_/session_impl.hpp \ aux_/session_settings.hpp \ aux_/proxy_settings.hpp \ aux_/session_interface.hpp \ aux_/time.hpp \ aux_/file_progress.hpp \ aux_/openssl.hpp \ aux_/byteswap.hpp \ aux_/route.h \ \ extensions/lt_trackers.hpp \ extensions/metadata_transfer.hpp \ extensions/smart_ban.hpp \ extensions/ut_metadata.hpp \ extensions/ut_pex.hpp \ \ kademlia/dht_storage.hpp \ kademlia/dht_tracker.hpp \ kademlia/dht_observer.hpp \ kademlia/direct_request.hpp \ kademlia/dos_blocker.hpp \ kademlia/find_data.hpp \ kademlia/put_data.hpp \ kademlia/msg.hpp \ kademlia/node.hpp \ kademlia/node_entry.hpp \ kademlia/node_id.hpp \ kademlia/observer.hpp \ kademlia/refresh.hpp \ kademlia/routing_table.hpp \ kademlia/rpc_manager.hpp \ kademlia/traversal_algorithm.hpp \ kademlia/item.hpp \ kademlia/get_item.hpp \ kademlia/get_peers.hpp all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/libtorrent/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign include/libtorrent/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: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-nobase_includeHEADERS: $(nobase_include_HEADERS) @$(NORMAL_INSTALL) @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ $(am__nobase_list) | while read dir files; do \ xfiles=; for file in $$files; do \ if test -f "$$file"; then xfiles="$$xfiles $$file"; \ else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ test -z "$$xfiles" || { \ test "x$$dir" = x. || { \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ done uninstall-nobase_includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$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 $(HEADERS) installdirs: for dir in "$(DESTDIR)$(includedir)"; 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: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-nobase_includeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-nobase_includeHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ 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-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man \ install-nobase_includeHEADERS install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-nobase_includeHEADERS .PRECIOUS: Makefile # 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: libtorrent-rasterbar-1.1.13/include/libtorrent/add_torrent_params.hpp000066400000000000000000000451531351156116000261430ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_ADD_TORRENT_PARAMS_HPP_INCLUDED #define TORRENT_ADD_TORRENT_PARAMS_HPP_INCLUDED #include #include #include #include "libtorrent/storage_defs.hpp" #include "libtorrent/peer_id.hpp" // sha1_hash #include "libtorrent/version.hpp" namespace libtorrent { class torrent_info; struct torrent_plugin; struct torrent_handle; // The add_torrent_params is a parameter pack for adding torrents to a // session. The key fields when adding a torrent are: // // * ti - when you have a .torrent file // * url - when you have a magnet link // * info_hash - when all you have is an info-hash (this is similar to a // magnet link) // // one of those fields need to be set. Another mandatory field is // ``save_path``. The add_torrent_params object is passed into one of the // ``session::add_torrent()`` overloads or ``session::async_add_torrent()``. // // If you only specify the info-hash, the torrent file will be downloaded // from peers, which requires them to support the metadata extension. For // the metadata extension to work, libtorrent must be built with extensions // enabled (``TORRENT_DISABLE_EXTENSIONS`` must not be defined). It also // takes an optional ``name`` argument. This may be left empty in case no // name should be assigned to the torrent. In case it's not, the name is // used for the torrent as long as it doesn't have metadata. See // ``torrent_handle::name``. // #include "libtorrent/aux_/disable_warnings_push.hpp" struct TORRENT_EXPORT add_torrent_params { // The constructor can be used to initialize the storage constructor, // which determines the storage mechanism for the downloaded or seeding // data for the torrent. For more information, see the ``storage`` field. add_torrent_params(storage_constructor_type sc = default_storage_constructor) : version(LIBTORRENT_VERSION_NUM) #ifndef TORRENT_NO_DEPRECATE , tracker_url(0) #endif , storage_mode(storage_mode_sparse) , storage(sc) , userdata(0) #ifndef TORRENT_NO_DEPRECATE , flags(flag_ignore_flags | default_flags) #else , flags(default_flags) #endif , max_uploads(-1) , max_connections(-1) , upload_limit(-1) , download_limit(-1) #ifndef TORRENT_NO_DEPRECATE , seed_mode(false) , override_resume_data(false) , upload_mode(false) , share_mode(false) , apply_ip_filter(true) , paused(true) , auto_managed(true) , duplicate_is_error(false) , merge_resume_trackers(false) #endif { } #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED void update_flags() const { if (flags != (flag_ignore_flags | default_flags)) return; boost::uint64_t& f = const_cast(flags); f = flag_update_subscribe; if (seed_mode) f |= flag_seed_mode; if (override_resume_data) f |= flag_override_resume_data; if (upload_mode) f |= flag_upload_mode; if (share_mode) f |= flag_share_mode; if (apply_ip_filter) f |= flag_apply_ip_filter; if (paused) f |= flag_paused; if (auto_managed) f |= flag_auto_managed; if (duplicate_is_error) f |= flag_duplicate_is_error; if (merge_resume_trackers) f |= flag_merge_resume_trackers; } #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" // values for the ``flags`` field enum flags_t { // If ``flag_seed_mode`` is set, libtorrent will assume that all files // are present for this torrent and that they all match the hashes in // the torrent file. Each time a peer requests to download a block, // the piece is verified against the hash, unless it has been verified // already. If a hash fails, the torrent will automatically leave the // seed mode and recheck all the files. The use case for this mode is // if a torrent is created and seeded, or if the user already know // that the files are complete, this is a way to avoid the initial // file checks, and significantly reduce the startup time. // // Setting ``flag_seed_mode`` on a torrent without metadata (a // .torrent file) is a no-op and will be ignored. // // If resume data is passed in with this torrent, the seed mode saved // in there will override the seed mode you set here. flag_seed_mode = 0x001, // If ``flag_override_resume_data`` is set, flags set for this torrent // in this ``add_torrent_params`` object will take precedence over // whatever states are saved in the resume data. For instance, the // ``paused``, ``auto_managed``, ``sequential_download``, ``seed_mode``, // ``super_seeding``, ``max_uploads``, ``max_connections``, // ``upload_limit`` and ``download_limit`` are all affected by this // flag. The intention of this flag is to have any field in // add_torrent_params configuring the torrent override the corresponding // configuration from the resume file, with the one exception of save // resume data, which has its own flag (for historic reasons). // "file_priorities" and "save_path" are not affected by this flag. flag_override_resume_data = 0x002, // If ``flag_upload_mode`` is set, the torrent will be initialized in // upload-mode, which means it will not make any piece requests. This // state is typically entered on disk I/O errors, and if the torrent // is also auto managed, it will be taken out of this state // periodically. This mode can be used to avoid race conditions when // adjusting priorities of pieces before allowing the torrent to start // downloading. // // If the torrent is auto-managed (``flag_auto_managed``), the torrent // will eventually be taken out of upload-mode, regardless of how it // got there. If it's important to manually control when the torrent // leaves upload mode, don't make it auto managed. flag_upload_mode = 0x004, // determines if the torrent should be added in *share mode* or not. // Share mode indicates that we are not interested in downloading the // torrent, but merely want to improve our share ratio (i.e. increase // it). A torrent started in share mode will do its best to never // download more than it uploads to the swarm. If the swarm does not // have enough demand for upload capacity, the torrent will not // download anything. This mode is intended to be safe to add any // number of torrents to, without manual screening, without the risk // of downloading more than is uploaded. // // A torrent in share mode sets the priority to all pieces to 0, // except for the pieces that are downloaded, when pieces are decided // to be downloaded. This affects the progress bar, which might be set // to "100% finished" most of the time. Do not change file or piece // priorities for torrents in share mode, it will make it not work. // // The share mode has one setting, the share ratio target, see // ``settings_pack::share_mode_target`` for more info. flag_share_mode = 0x008, // determines if the IP filter should apply to this torrent or not. By // default all torrents are subject to filtering by the IP filter // (i.e. this flag is set by default). This is useful if certain // torrents needs to be exempt for some reason, being an auto-update // torrent for instance. flag_apply_ip_filter = 0x010, // specifies whether or not the torrent is to be started in a paused // state. I.e. it won't connect to the tracker or any of the peers // until it's resumed. This is typically a good way of avoiding race // conditions when setting configuration options on torrents before // starting them. flag_paused = 0x020, // If the torrent is auto-managed (``flag_auto_managed``), the torrent // may be resumed at any point, regardless of how it paused. If it's // important to manually control when the torrent is paused and // resumed, don't make it auto managed. // // If ``flag_auto_managed`` is set, the torrent will be queued, // started and seeded automatically by libtorrent. When this is set, // the torrent should also be started as paused. The default queue // order is the order the torrents were added. They are all downloaded // in that order. For more details, see queuing_. // // If you pass in resume data, the auto_managed state of the torrent // when the resume data was saved will override the auto_managed state // you pass in here. You can override this by setting // ``override_resume_data``. flag_auto_managed = 0x040, flag_duplicate_is_error = 0x080, // defaults to off and specifies whether tracker URLs loaded from // resume data should be added to the trackers in the torrent or // replace the trackers. When replacing trackers (i.e. this flag is not // set), any trackers passed in via add_torrent_params are also // replaced by any trackers in the resume data. The default behavior is // to have the resume data override the .torrent file _and_ the // trackers added in add_torrent_params. flag_merge_resume_trackers = 0x100, // on by default and means that this torrent will be part of state // updates when calling post_torrent_updates(). flag_update_subscribe = 0x200, // sets the torrent into super seeding mode. If the torrent is not a // seed, this flag has no effect. It has the same effect as calling // ``torrent_handle::super_seeding(true)`` on the torrent handle // immediately after adding it. flag_super_seeding = 0x400, // sets the sequential download state for the torrent. It has the same // effect as calling ``torrent_handle::sequential_download(true)`` on // the torrent handle immediately after adding it. flag_sequential_download = 0x800, // if this flag is set, the save path from the resume data file, if // present, is honored. This defaults to not being set, in which // case the save_path specified in add_torrent_params is always used. flag_use_resume_save_path = 0x1000, // indicates that this torrent should never be unloaded from RAM, even // if unloading torrents are allowed in general. Setting this makes // the torrent exempt from loading/unloading management. flag_pinned = 0x2000, // defaults to off and specifies whether web seed URLs loaded from // resume data should be added to the ones in the torrent file or // replace them. No distinction is made between the two different kinds // of web seeds (`BEP 17`_ and `BEP 19`_). When replacing web seeds // (i.e. when this flag is not set), any web seeds passed in via // add_torrent_params are also replaced. The default behavior is to // have any web seeds in the resume data take precedence over whatever // is passed in here as well as the .torrent file. flag_merge_resume_http_seeds = 0x8000, // the stop when ready flag. Setting this flag is equivalent to calling // torrent_handle::stop_when_ready() immediately after the torrent is // added. flag_stop_when_ready = 0x4000, // internal default_flags = flag_pinned | flag_update_subscribe | flag_auto_managed | flag_paused | flag_apply_ip_filter #ifndef TORRENT_NO_DEPRECATE , flag_ignore_flags TORRENT_DEPRECATED_ENUM = 0x80000000 #endif }; // filled in by the constructor and should be left untouched. It is used // for forward binary compatibility. int version; // torrent_info object with the torrent to add. Unless the url or // info_hash is set, this is required to be initialized. boost::shared_ptr ti; #ifndef TORRENT_NO_DEPRECATE char const* TORRENT_DEPRECATED_MEMBER tracker_url; #endif // If the torrent doesn't have a tracker, but relies on the DHT to find // peers, the ``trackers`` can specify tracker URLs for the torrent. std::vector trackers; // url seeds to be added to the torrent (`BEP 17`_). std::vector url_seeds; // a list of hostname and port pairs, representing DHT nodes to be added // to the session (if DHT is enabled). The hostname may be an IP address. std::vector > dht_nodes; std::string name; // the path where the torrent is or will be stored. Note that this may // also be stored in resume data. If you want the save path saved in // the resume data to be used, you need to set the // flag_use_resume_save_path flag. // // .. note:: // On windows this path (and other paths) are interpreted as UNC // paths. This means they must use backslashes as directory separators // and may not contain the special directories "." or "..". // // Setting this to an absolute path performs slightly better than a // relative path. std::string save_path; // The optional parameter, ``resume_data`` can be given if up to date // fast-resume data is available. The fast-resume data can be acquired // from a running torrent by calling save_resume_data() on // torrent_handle. See fast-resume_. The ``vector`` that is passed in // will be swapped into the running torrent instance with // ``std::vector::swap()``. std::vector resume_data; // One of the values from storage_mode_t. For more information, see // storage-allocation_. storage_mode_t storage_mode; // can be used to customize how the data is stored. The default storage // will simply write the data to the files it belongs to, but it could be // overridden to save everything to a single file at a specific location // or encrypt the content on disk for instance. For more information // about the storage_interface that needs to be implemented for a custom // storage, see storage_interface. storage_constructor_type storage; // The ``userdata`` parameter is optional and will be passed on to the // extension constructor functions, if any // (see torrent_handle::add_extension()). void* userdata; // can be set to control the initial file priorities when adding a // torrent. The semantics are the same as for // ``torrent_handle::prioritize_files()``. The file priorities specified // in here take precedence over those specified in the resume data, if // any. std::vector file_priorities; // torrent extension construction functions can be added to this vector // to have them be added immediately when the torrent is constructed. // This may be desired over the torrent_handle::add_extension() in order // to avoid race conditions. For instance it may be important to have the // plugin catch events that happen very early on after the torrent is // created. std::vector(torrent_handle const&, void*)> > extensions; // the default tracker id to be used when announcing to trackers. By // default this is empty, and no tracker ID is used, since this is an // optional argument. If a tracker returns a tracker ID, that ID is used // instead of this. std::string trackerid; // If you specify a ``url``, the torrent will be set in // ``downloading_metadata`` state until the .torrent file has been // downloaded. If there's any error while downloading, the torrent will // be stopped and the torrent error state (``torrent_status::error``) // will indicate what went wrong. The ``url`` may be set to a magnet link. std::string url; // if ``uuid`` is specified, it is used to find duplicates. If another // torrent is already running with the same UUID as the one being added, // it will be considered a duplicate. This is mainly useful for RSS feed // items which has UUIDs specified. std::string uuid; // should point to the URL of the RSS feed this torrent comes from, if it // comes from an RSS feed. std::string source_feed_url; // flags controlling aspects of this torrent and how it's added. See // flags_t for details. // // .. note:: // The ``flags`` field is initialized with default flags by the // constructor. In order to preserve default behavior when clearing or // setting other flags, make sure to bitwise OR or in a flag or bitwise // AND the inverse of a flag to clear it. boost::uint64_t flags; // set this to the info hash of the torrent to add in case the info-hash // is the only known property of the torrent. i.e. you don't have a // .torrent file nor a magnet link. sha1_hash info_hash; // ``max_uploads``, ``max_connections``, ``upload_limit``, // ``download_limit`` correspond to the ``set_max_uploads()``, // ``set_max_connections()``, ``set_upload_limit()`` and // ``set_download_limit()`` functions on torrent_handle. These values let // you initialize these settings when the torrent is added, instead of // calling these functions immediately following adding it. // // -1 means unlimited on these settings just like their counterpart // functions on torrent_handle // // For fine grained control over rate limits depending on various // properties of the peer connection, see peer-classes_. int max_uploads; int max_connections; int upload_limit; int download_limit; #ifndef TORRENT_NO_DEPRECATE bool TORRENT_DEPRECATED_MEMBER seed_mode; bool TORRENT_DEPRECATED_MEMBER override_resume_data; bool TORRENT_DEPRECATED_MEMBER upload_mode; bool TORRENT_DEPRECATED_MEMBER share_mode; bool TORRENT_DEPRECATED_MEMBER apply_ip_filter; bool TORRENT_DEPRECATED_MEMBER paused; bool TORRENT_DEPRECATED_MEMBER auto_managed; bool TORRENT_DEPRECATED_MEMBER duplicate_is_error; bool TORRENT_DEPRECATED_MEMBER merge_resume_trackers; #endif }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/address.hpp000066400000000000000000000047721351156116000237220ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_ADDRESS_HPP_INCLUDED #define TORRENT_ADDRESS_HPP_INCLUDED #include #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #ifdef __OBJC__ #define Protocol Protocol_ #endif #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN // asio assumes that the windows error codes are defined already #include #endif #include #if defined TORRENT_BUILD_SIMULATOR #include "simulator/simulator.hpp" #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifdef __OBJC__ #undef Protocol #endif namespace libtorrent { #if defined TORRENT_BUILD_SIMULATOR typedef sim::asio::ip::address address; typedef sim::asio::ip::address_v4 address_v4; #if TORRENT_USE_IPV6 typedef sim::asio::ip::address_v6 address_v6; #endif #else typedef boost::asio::ip::address address; typedef boost::asio::ip::address_v4 address_v4; #if TORRENT_USE_IPV6 typedef boost::asio::ip::address_v6 address_v6; #endif #endif // SIMULATOR } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/alert.hpp000066400000000000000000000274461351156116000234070ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include #include #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" // OVERVIEW // // The pop_alerts() function on session is the main interface for retrieving // alerts (warnings, messages and errors from libtorrent). If no alerts have // been posted by libtorrent pop_alerts() will return an empty list. // // By default, only errors are reported. settings_pack::alert_mask can be // used to specify which kinds of events should be reported. The alert mask is // comprised by bits from the category_t enum. // // Every alert belongs to one or more category. There is a cost associated with // posting alerts. Only alerts that belong to an enabled category are // posted. Setting the alert bitmask to 0 will disable all alerts (except those // that are non-discardable). Alerts that are responses to API calls such as // save_resume_data() and post_session_stats() are non-discardable and will be // posted even if their category is disabled. // // There are other alert base classes that some alerts derive from, all the // alerts that are generated for a specific torrent are derived from // torrent_alert, and tracker events derive from tracker_alert. // // Alerts returned by pop_alerts() are only valid until the next call to // pop_alerts(). You may not copy an alert object to access it after the next // call to pop_alerts(). Internal members of alerts also become invalid once // pop_alerts() is called again. #include "libtorrent/time.hpp" #include "libtorrent/config.hpp" namespace libtorrent { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif // The ``alert`` class is the base class that specific messages are derived from. // alert types are not copyable, and cannot be constructed by the client. The // pointers returned by libtorrent are short lived (the details are described // under session_handle::pop_alerts()) class TORRENT_EXPORT alert { #ifdef __GNUC__ #pragma GCC diagnostic pop #endif public: #ifndef TORRENT_NO_DEPRECATE // only here for backwards compatibility enum TORRENT_DEPRECATED_ENUM severity_t { debug, info, warning, critical, fatal, none }; #endif // these are bits for the alert_mask used by the session. See // settings_pack::alert_mask. enum category_t { // Enables alerts that report an error. This includes: // // * tracker errors // * tracker warnings // * file errors // * resume data failures // * web seed errors // * .torrent files errors // * listen socket errors // * port mapping errors error_notification = 0x1, // Enables alerts when peers send invalid requests, get banned or // snubbed. peer_notification = 0x2, // Enables alerts for port mapping events. For NAT-PMP and UPnP. port_mapping_notification = 0x4, // Enables alerts for events related to the storage. File errors and // synchronization events for moving the storage, renaming files etc. storage_notification = 0x8, // Enables all tracker events. Includes announcing to trackers, // receiving responses, warnings and errors. tracker_notification = 0x10, // Low level alerts for when peers are connected and disconnected. debug_notification = 0x20, // Enables alerts for when a torrent or the session changes state. status_notification = 0x40, // Alerts for when blocks are requested and completed. Also when // pieces are completed. progress_notification = 0x80, // Alerts when a peer is blocked by the ip blocker or port blocker. ip_block_notification = 0x100, // Alerts when some limit is reached that might limit the download // or upload rate. performance_warning = 0x200, // Alerts on events in the DHT node. For incoming searches or // bootstrapping being done etc. dht_notification = 0x400, // If you enable these alerts, you will receive a stats_alert // approximately once every second, for every active torrent. // These alerts contain all statistics counters for the interval since // the lasts stats alert. stats_notification = 0x800, #ifndef TORRENT_NO_DEPRECATE // Alerts on RSS related events, like feeds being updated, feed error // conditions and successful RSS feed updates. Enabling this categoty // will make you receive rss_alert alerts. rss_notification TORRENT_DEPRECATED_ENUM = 0x1000, #endif // Enables debug logging alerts. These are available unless libtorrent // was built with logging disabled (``TORRENT_DISABLE_LOGGING``). The // alerts being posted are log_alert and are session wide. session_log_notification = 0x2000, // Enables debug logging alerts for torrents. These are available // unless libtorrent was built with logging disabled // (``TORRENT_DISABLE_LOGGING``). The alerts being posted are // torrent_log_alert and are torrent wide debug events. torrent_log_notification = 0x4000, // Enables debug logging alerts for peers. These are available unless // libtorrent was built with logging disabled // (``TORRENT_DISABLE_LOGGING``). The alerts being posted are // peer_log_alert and low-level peer events and messages. peer_log_notification = 0x8000, // enables the incoming_request_alert. incoming_request_notification = 0x10000, // enables dht_log_alert, debug logging for the DHT dht_log_notification = 0x20000, // enable events from pure dht operations not related to torrents dht_operation_notification = 0x40000, // enables port mapping log events. This log is useful // for debugging the UPnP or NAT-PMP implementation port_mapping_log_notification = 0x80000, // enables verbose logging from the piece picker. picker_log_notification = 0x100000, // alerts when files complete downloading file_progress_notification = 0x200000, // alerts when pieces complete downloading or fail hash check piece_progress_notification = 0x400000, // alerts on individual blocks being requested, downloading, finished, // rejected, time-out and cancelled. This is likely to post alerts at a // high rate. block_progress_notification = 0x800000, // The full bitmask, representing all available categories. // // since the enum is signed, make sure this isn't // interpreted as -1. For instance, boost.python // does that and fails when assigning it to an // unsigned parameter. all_categories = 0x7fffffff }; // hidden alert(); // hidden virtual ~alert(); // a timestamp is automatically created in the constructor time_point timestamp() const; // returns an integer that is unique to this alert type. It can be // compared against a specific alert by querying a static constant called ``alert_type`` // in the alert. It can be used to determine the run-time type of an alert* in // order to cast to that alert type and access specific members. // // e.g: // // .. code:: c++ // // std::vector alerts; // ses.pop_alerts(&alerts); // for (alert* i : alerts) { // switch (a->type()) { // // case read_piece_alert::alert_type: // { // auto* p = static_cast(a); // if (p->ec) { // // read_piece failed // break; // } // // use p // break; // } // case file_renamed_alert::alert_type: // { // // etc... // } // } // } virtual int type() const = 0; // returns a string literal describing the type of the alert. It does // not include any information that might be bundled with the alert. virtual char const* what() const = 0; // generate a string describing the alert and the information bundled // with it. This is mainly intended for debug and development use. It is not suitable // to use this for applications that may be localized. Instead, handle each alert // type individually and extract and render the information from the alert depending // on the locale. virtual std::string message() const = 0; // returns a bitmask specifying which categories this alert belong to. virtual int category() const = 0; #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/aux_/disable_warnings_push.hpp" // determines whether or not an alert is allowed to be discarded // when the alert queue is full. There are a few alerts which may not be discared, // since they would break the user contract, such as save_resume_data_alert. TORRENT_DEPRECATED bool discardable() const { return discardable_impl(); } TORRENT_DEPRECATED severity_t severity() const { return warning; } // returns a pointer to a copy of the alert. TORRENT_DEPRECATED std::auto_ptr clone() const { return clone_impl(); } protected: virtual bool discardable_impl() const { return true; } virtual std::auto_ptr clone_impl() const = 0; #include "libtorrent/aux_/disable_warnings_pop.hpp" #endif // TORRENT_NO_DEPRECATE protected: // the alert is not copyable (but for backwards compatibility reasons it // retains the ability to clone itself, for now). #if __cplusplus >= 201103L alert(alert const& rhs) = default; #endif private: // explicitly disallow assignment and copy construction alert& operator=(alert const&); time_point m_timestamp; }; // When you get an alert, you can use ``alert_cast<>`` to attempt to cast the // pointer to a specific alert type, in order to query it for more // information. // // .. note:: // ``alert_cast<>`` can only cast to an exact alert type, not a base class template T* alert_cast(alert* a) { if (a == 0) return 0; if (a->type() == T::alert_type) return static_cast(a); return 0; } template T const* alert_cast(alert const* a) { if (a == 0) return 0; if (a->type() == T::alert_type) return static_cast(a); return 0; } } // namespace libtorrent #endif // TORRENT_ALERT_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/alert_manager.hpp000066400000000000000000000153571351156116000250770ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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_MANAGER_HPP_INCLUDED #define TORRENT_ALERT_MANAGER_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/heterogeneous_queue.hpp" #include "libtorrent/stack_allocator.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #ifndef TORRENT_NO_DEPRECATE #include #endif #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include #include // for std::forward #ifdef __GNUC__ // this is to suppress the warnings for using std::auto_ptr #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif // used for emplace_alert() variadic template emulation for c++98 #define TORRENT_ALERT_MANAGER_MAX_ARITY 7 namespace libtorrent { #ifndef TORRENT_DISABLE_EXTENSIONS struct plugin; #endif class TORRENT_EXTRA_EXPORT alert_manager { public: alert_manager(int queue_limit , boost::uint32_t alert_mask = alert::error_notification); ~alert_manager(); #if !defined BOOST_NO_CXX11_VARIADIC_TEMPLATES \ && !defined BOOST_NO_CXX11_RVALUE_REFERENCES template void emplace_alert(Args&&... args) { recursive_mutex::scoped_lock lock(m_mutex); #ifndef TORRENT_NO_DEPRECATE if (m_dispatch) { m_dispatch(std::auto_ptr(new T(m_allocations[m_generation] , std::forward(args)...))); return; } #endif // don't add more than this number of alerts, unless it's a // high priority alert, in which case we try harder to deliver it // for high priority alerts, double the upper limit if (m_alerts[m_generation].size() / (1 + T::priority) >= m_queue_size_limit) return; T alert(m_allocations[m_generation], std::forward(args)...); m_alerts[m_generation].push_back(alert); maybe_notify(&alert); } #else // emulate variadic templates for c++98 #include "libtorrent/aux_/alert_manager_variadic_emplace.hpp" #endif bool pending() const; void get_all(std::vector& alerts, int& num_resume); template bool should_post() const { recursive_mutex::scoped_lock lock(m_mutex); if (m_alerts[m_generation].size() / (1 + T::priority) >= m_queue_size_limit) { return false; } return (m_alert_mask & T::static_category) != 0; } alert* wait_for_alert(time_duration max_wait); void set_alert_mask(boost::uint32_t m) { recursive_mutex::scoped_lock lock(m_mutex); m_alert_mask = m; } boost::uint32_t alert_mask() const { recursive_mutex::scoped_lock lock(m_mutex); return m_alert_mask; } int alert_queue_size_limit() const { return m_queue_size_limit; } int set_alert_queue_size_limit(int queue_size_limit_); void set_notify_function(boost::function const& fun); #ifndef TORRENT_NO_DEPRECATE void set_dispatch_function(boost::function)> const&); #endif int num_queued_resume() const; #ifndef TORRENT_DISABLE_EXTENSIONS void add_extension(boost::shared_ptr ext); #endif private: // non-copyable alert_manager(alert_manager const&); alert_manager& operator=(alert_manager const&); void maybe_notify(alert* a); // this mutex protects everything. Since it's held while executing user // callbacks (the notify function and extension on_alert()) it must be // recursive to support recursively post new alerts. mutable recursive_mutex m_mutex; condition_variable m_condition; boost::uint32_t m_alert_mask; int m_queue_size_limit; #ifndef TORRENT_NO_DEPRECATE bool maybe_dispatch(alert const& a); boost::function)> m_dispatch; #endif // this function (if set) is called whenever the number of alerts in // the alert queue goes from 0 to 1. The client is expected to wake up // its main message loop for it to poll for alerts (using get_alerts()). // That call will drain every alert in one atomic operation and this // notification function will be called again the next time an alert is // posted to the queue boost::function m_notify; // the number of resume data alerts in the alert queue int m_num_queued_resume; // this is either 0 or 1, it indicates which m_alerts and m_allocations // the alert_manager is allowed to use right now. This is swapped when // the client calls get_all(), at which point all of the alert objects // passed to the client will be owned by libtorrent again, and reset. int m_generation; // this is where all alerts are queued up. There are two heterogenous // queues to double buffer the thread access. The mutex in the alert // manager gives exclusive access to m_alerts[m_generation] and // m_allocations[m_generation] whereas the other copy is exclusively // used by the client thread. heterogeneous_queue m_alerts[2]; // this is a stack where alerts can allocate variable length content, // such as strings, to go with the alerts. aux::stack_allocator m_allocations[2]; #ifndef TORRENT_DISABLE_EXTENSIONS typedef std::list > ses_extension_list_t; ses_extension_list_t m_ses_extensions; #endif }; } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #endif libtorrent-rasterbar-1.1.13/include/libtorrent/alert_types.hpp000066400000000000000000002502601351156116000246230ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/identify_client.hpp" #include "libtorrent/address.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/add_torrent_params.hpp" #include "libtorrent/torrent_status.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/peer_request.hpp" #include "libtorrent/performance_counters.hpp" #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/rss.hpp" // for feed_handle #endif #include "libtorrent/operations.hpp" // for operation_t enum #include "libtorrent/close_reason.hpp" #include "libtorrent/aux_/escape_string.hpp" // for convert_from_native #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifdef __GNUC__ #pragma GCC diagnostic push // this is to suppress the warnings for using std::auto_ptr #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif namespace libtorrent { namespace aux { struct stack_allocator; } struct piece_block; // maps an operation id (from peer_error_alert and peer_disconnected_alert) // to its name. See peer_connection for the constants TORRENT_EXPORT char const* operation_name(int op); // user defined alerts should use IDs greater than this static const int user_alert_id = 10000; enum alert_priority { alert_priority_normal = 0, alert_priority_high, alert_priority_critical }; // This is a base class for alerts that are associated with a // specific torrent. It contains a handle to the torrent. struct TORRENT_EXPORT torrent_alert : alert { // internal torrent_alert(aux::stack_allocator& alloc, torrent_handle const& h); // internal static const int alert_type = 0; // returns the message associated with this alert virtual std::string message() const TORRENT_OVERRIDE; // The torrent_handle pointing to the torrent this // alert is associated with. torrent_handle handle; char const* torrent_name() const; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER name; #endif protected: aux::stack_allocator const& m_alloc; private: int m_name_idx; }; // The peer alert is a base class for alerts that refer to a specific peer. It includes all // the information to identify the peer. i.e. ``ip`` and ``peer-id``. struct TORRENT_EXPORT peer_alert : torrent_alert { // internal peer_alert(aux::stack_allocator& alloc, torrent_handle const& h, tcp::endpoint const& i, peer_id const& pi); static const int alert_type = 1; static const int static_category = alert::peer_notification; virtual int category() const TORRENT_OVERRIDE { return static_category; } virtual std::string message() const TORRENT_OVERRIDE; // The peer's IP address and port. tcp::endpoint ip; // the peer ID, if known. peer_id pid; }; // This is a base class used for alerts that are associated with a // specific tracker. It derives from torrent_alert since a tracker // is also associated with a specific torrent. struct TORRENT_EXPORT tracker_alert : torrent_alert { // internal tracker_alert(aux::stack_allocator& alloc, torrent_handle const& h , std::string const& u); static const int alert_type = 2; static const int static_category = alert::tracker_notification; virtual int category() const TORRENT_OVERRIDE { return static_category; } virtual std::string message() const TORRENT_OVERRIDE; // returns a null-terminated string of the tracker's URL char const* tracker_url() const; #ifndef TORRENT_NO_DEPRECATE // The tracker URL std::string TORRENT_DEPRECATED_MEMBER url; #endif private: int m_url_idx; }; #ifndef TORRENT_NO_DEPRECATE #define TORRENT_CLONE(name) \ virtual std::auto_ptr clone_impl() const TORRENT_OVERRIDE \ { return std::auto_ptr(new name(*this)); } #else #define TORRENT_CLONE(name) #endif // we can only use = default in C++11 // the purpose of this is just to make all alert types non-copyable from user // code. The heterogeneous queue does not yet have an emplace_back(), so it // still needs to copy alerts, but the important part is that it's not // copyable for clients. // TODO: Once the backwards compatibility of clone() is removed, and once // C++11 is required, this can be simplified to just say = delete #if __cplusplus >= 201103L #define TORRENT_PROTECTED_CCTOR(name) \ protected: \ template friend struct heterogeneous_queue; \ name(name const&) = default; \ public: #else #define TORRENT_PROTECTED_CCTOR(name) #endif #define TORRENT_DEFINE_ALERT_IMPL(name, seq, prio) \ TORRENT_PROTECTED_CCTOR(name) \ static const int priority = prio; \ static const int alert_type = seq; \ virtual int type() const TORRENT_OVERRIDE { return alert_type; } \ TORRENT_CLONE(name) \ virtual int category() const TORRENT_OVERRIDE { return static_category; } \ virtual char const* what() const TORRENT_OVERRIDE { return #name; } #define TORRENT_DEFINE_ALERT(name, seq) \ TORRENT_DEFINE_ALERT_IMPL(name, seq, alert_priority_normal) #define TORRENT_DEFINE_ALERT_PRIO(name, seq, prio) \ TORRENT_DEFINE_ALERT_IMPL(name, seq, prio) #ifndef TORRENT_NO_DEPRECATE // The ``torrent_added_alert`` is posted once every time a torrent is successfully // added. It doesn't contain any members of its own, but inherits the torrent handle // from its base class. // It's posted when the ``status_notification`` bit is set in the alert_mask. // deprecated in 1.1.3 // use add_torrent_alert instead struct TORRENT_DEPRECATED_EXPORT torrent_added_alert TORRENT_FINAL : torrent_alert { // internal torrent_added_alert(aux::stack_allocator& alloc, torrent_handle const& h); TORRENT_DEFINE_ALERT(torrent_added_alert, 3) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; }; #endif // The ``torrent_removed_alert`` is posted whenever a torrent is removed. Since // the torrent handle in its base class will always be invalid (since the torrent // is already removed) it has the info hash as a member, to identify it. // It's posted when the ``status_notification`` bit is set in the alert_mask. // // Even though the ``handle`` member doesn't point to an existing torrent anymore, // it is still useful for comparing to other handles, which may also no // longer point to existing torrents, but to the same non-existing torrents. // // The ``torrent_handle`` acts as a ``weak_ptr``, even though its object no // longer exists, it can still compare equal to another weak pointer which // points to the same non-existent object. struct TORRENT_EXPORT torrent_removed_alert TORRENT_FINAL : torrent_alert { // internal torrent_removed_alert(aux::stack_allocator& alloc , torrent_handle const& h, sha1_hash const& ih); TORRENT_DEFINE_ALERT_PRIO(torrent_removed_alert, 4, alert_priority_critical) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; sha1_hash info_hash; }; // This alert is posted when the asynchronous read operation initiated by // a call to torrent_handle::read_piece() is completed. If the read failed, the torrent // is paused and an error state is set and the buffer member of the alert // is 0. If successful, ``buffer`` points to a buffer containing all the data // of the piece. ``piece`` is the piece index that was read. ``size`` is the // number of bytes that was read. // // If the operation fails, ec will indicate what went wrong. struct TORRENT_EXPORT read_piece_alert TORRENT_FINAL : torrent_alert { // internal read_piece_alert(aux::stack_allocator& alloc, torrent_handle const& h , int p, boost::shared_array d, int s); read_piece_alert(aux::stack_allocator& alloc, torrent_handle h, int p, error_code e); TORRENT_DEFINE_ALERT_PRIO(read_piece_alert, 5, alert_priority_critical) static const int static_category = alert::storage_notification; virtual std::string message() const TORRENT_OVERRIDE; error_code ec; boost::shared_array buffer; int piece; int size; }; // This is posted whenever an individual file completes its download. i.e. // All pieces overlapping this file have passed their hash check. struct TORRENT_EXPORT file_completed_alert TORRENT_FINAL : torrent_alert { // internal file_completed_alert(aux::stack_allocator& alloc, torrent_handle const& h , int idx); TORRENT_DEFINE_ALERT_PRIO(file_completed_alert, 6, alert_priority_normal) static const int static_category = alert::file_progress_notification | alert::progress_notification ; virtual std::string message() const TORRENT_OVERRIDE; // refers to the index of the file that completed. int index; }; // This is posted as a response to a torrent_handle::rename_file() call, if the rename // operation succeeds. struct TORRENT_EXPORT file_renamed_alert TORRENT_FINAL : torrent_alert { // internal file_renamed_alert(aux::stack_allocator& alloc, torrent_handle const& h , std::string const& n , int idx); TORRENT_DEFINE_ALERT_PRIO(file_renamed_alert, 7, alert_priority_critical) static const int static_category = alert::storage_notification; virtual std::string message() const TORRENT_OVERRIDE; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER name; #endif char const* new_name() const; // refers to the index of the file that was renamed, int index; private: int m_name_idx; }; // This is posted as a response to a torrent_handle::rename_file() call, if the rename // operation failed. struct TORRENT_EXPORT file_rename_failed_alert TORRENT_FINAL : torrent_alert { // internal file_rename_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h, int idx , error_code ec); TORRENT_DEFINE_ALERT_PRIO(file_rename_failed_alert, 8, alert_priority_critical) static const int static_category = alert::storage_notification; virtual std::string message() const TORRENT_OVERRIDE; // refers to the index of the file that was supposed to be renamed, // ``error`` is the error code returned from the filesystem. int index; error_code error; }; // This alert is generated when a limit is reached that might have a negative impact on // upload or download rate performance. struct TORRENT_EXPORT performance_alert TORRENT_FINAL : torrent_alert { enum performance_warning_t { // This warning means that the number of bytes queued to be written to disk // exceeds the max disk byte queue setting (``settings_pack::max_queued_disk_bytes``). // This might restrict the download rate, by not queuing up enough write jobs // to the disk I/O thread. When this alert is posted, peer connections are // temporarily stopped from downloading, until the queued disk bytes have fallen // below the limit again. Unless your ``max_queued_disk_bytes`` setting is already // high, you might want to increase it to get better performance. outstanding_disk_buffer_limit_reached, // This is posted when libtorrent would like to send more requests to a peer, // but it's limited by ``settings_pack::max_out_request_queue``. The queue length // libtorrent is trying to achieve is determined by the download rate and the // assumed round-trip-time (``settings_pack::request_queue_time``). The assumed // round-trip-time is not limited to just the network RTT, but also the remote disk // access time and message handling time. It defaults to 3 seconds. The target number // of outstanding requests is set to fill the bandwidth-delay product (assumed RTT // times download rate divided by number of bytes per request). When this alert // is posted, there is a risk that the number of outstanding requests is too low // and limits the download rate. You might want to increase the ``max_out_request_queue`` // setting. outstanding_request_limit_reached, // This warning is posted when the amount of TCP/IP overhead is greater than the // upload rate limit. When this happens, the TCP/IP overhead is caused by a much // faster download rate, triggering TCP ACK packets. These packets eat into the // rate limit specified to libtorrent. When the overhead traffic is greater than // the rate limit, libtorrent will not be able to send any actual payload, such // as piece requests. This means the download rate will suffer, and new requests // can be sent again. There will be an equilibrium where the download rate, on // average, is about 20 times the upload rate limit. If you want to maximize the // download rate, increase the upload rate limit above 5% of your download capacity. upload_limit_too_low, // This is the same warning as ``upload_limit_too_low`` but referring to the download // limit instead of upload. This suggests that your download rate limit is much lower // than your upload capacity. Your upload rate will suffer. To maximize upload rate, // make sure your download rate limit is above 5% of your upload capacity. download_limit_too_low, // We're stalled on the disk. We want to write to the socket, and we can write // but our send buffer is empty, waiting to be refilled from the disk. // This either means the disk is slower than the network connection // or that our send buffer watermark is too small, because we can // send it all before the disk gets back to us. // The number of bytes that we keep outstanding, requested from the disk, is calculated // as follows:: // // min(512, max(upload_rate * send_buffer_watermark_factor / 100, send_buffer_watermark)) // // If you receive this alert, you might want to either increase your ``send_buffer_watermark`` // or ``send_buffer_watermark_factor``. send_buffer_watermark_too_low, // If the half (or more) of all upload slots are set as optimistic unchoke slots, this // warning is issued. You probably want more regular (rate based) unchoke slots. too_many_optimistic_unchoke_slots, // If the disk write queue ever grows larger than half of the cache size, this warning // is posted. The disk write queue eats into the total disk cache and leaves very little // left for the actual cache. This causes the disk cache to oscillate in evicting large // portions of the cache before allowing peers to download any more, onto the disk write // queue. Either lower ``max_queued_disk_bytes`` or increase ``cache_size``. too_high_disk_queue_limit, aio_limit_reached, bittyrant_with_no_uplimit, // This is generated if outgoing peer connections are failing because of *address in use* // errors, indicating that ``settings_pack::outgoing_ports`` is set and is too small of // a range. Consider not using the ``outgoing_ports`` setting at all, or widen the range to // include more ports. too_few_outgoing_ports, too_few_file_descriptors, num_warnings }; // internal performance_alert(aux::stack_allocator& alloc, torrent_handle const& h , performance_warning_t w); TORRENT_DEFINE_ALERT(performance_alert, 9) static const int static_category = alert::performance_warning; virtual std::string message() const TORRENT_OVERRIDE; performance_warning_t warning_code; }; // Generated whenever a torrent changes its state. struct TORRENT_EXPORT state_changed_alert TORRENT_FINAL : torrent_alert { // internal state_changed_alert(aux::stack_allocator& alloc, torrent_handle const& h , torrent_status::state_t st , torrent_status::state_t prev_st); TORRENT_DEFINE_ALERT_PRIO(state_changed_alert, 10, alert_priority_high) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; // the new state of the torrent. torrent_status::state_t state; // the previous state. torrent_status::state_t prev_state; }; // This alert is generated on tracker time outs, premature disconnects, // invalid response or a HTTP response other than "200 OK". From the alert // you can get the handle to the torrent the tracker belongs to. // // The ``times_in_row`` member says how many times in a row this tracker has // failed. ``status_code`` is the code returned from the HTTP server. 401 // means the tracker needs authentication, 404 means not found etc. If the // tracker timed out, the code will be set to 0. struct TORRENT_EXPORT tracker_error_alert TORRENT_FINAL : tracker_alert { // internal tracker_error_alert(aux::stack_allocator& alloc , torrent_handle const& h , int times , int status , std::string const& u , error_code const& e , std::string const& m); TORRENT_DEFINE_ALERT(tracker_error_alert, 11) static const int static_category = alert::tracker_notification | alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; int times_in_row; int status_code; error_code error; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER msg; #endif // the message associated with this error char const* error_message() const; private: int m_msg_idx; }; // This alert is triggered if the tracker reply contains a warning field. // Usually this means that the tracker announce was successful, but the // tracker has a message to the client. struct TORRENT_EXPORT tracker_warning_alert TORRENT_FINAL : tracker_alert { // internal tracker_warning_alert(aux::stack_allocator& alloc , torrent_handle const& h , std::string const& u , std::string const& m); TORRENT_DEFINE_ALERT(tracker_warning_alert, 12) static const int static_category = alert::tracker_notification | alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; #ifndef TORRENT_NO_DEPRECATE // contains the warning message from the tracker. std::string TORRENT_DEPRECATED_MEMBER msg; #endif // the message associated with this warning char const* warning_message() const; private: int m_msg_idx; }; // This alert is generated when a scrape request succeeds. struct TORRENT_EXPORT scrape_reply_alert TORRENT_FINAL : tracker_alert { // internal scrape_reply_alert(aux::stack_allocator& alloc , torrent_handle const& h , int incomp , int comp , std::string const& u); TORRENT_DEFINE_ALERT(scrape_reply_alert, 13) virtual std::string message() const TORRENT_OVERRIDE; // the data returned in the scrape response. These numbers // may be -1 if the response was malformed. int incomplete; int complete; }; // If a scrape request fails, this alert is generated. This might be due // to the tracker timing out, refusing connection or returning an http response // code indicating an error. struct TORRENT_EXPORT scrape_failed_alert TORRENT_FINAL : tracker_alert { // internal scrape_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h , std::string const& u , error_code const& e); scrape_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h , std::string const& u , std::string const& m); TORRENT_DEFINE_ALERT(scrape_failed_alert, 14) static const int static_category = alert::tracker_notification | alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; #ifndef TORRENT_NO_DEPRECATE // contains a message describing the error. std::string TORRENT_DEPRECATED_MEMBER msg; #endif // the error itself. This may indicate that the tracker sent an error // message (``error::tracker_failure``), in which case it can be // retrieved by calling ``error_message()``. error_code error; // if the error indicates there is an associated message, this returns // that message. Otherwise and empty string. char const* error_message() const; private: int m_msg_idx; }; // This alert is only for informational purpose. It is generated when a tracker announce // succeeds. It is generated regardless what kind of tracker was used, be it UDP, HTTP or // the DHT. struct TORRENT_EXPORT tracker_reply_alert TORRENT_FINAL : tracker_alert { // internal tracker_reply_alert(aux::stack_allocator& alloc , torrent_handle const& h , int np , std::string const& u); TORRENT_DEFINE_ALERT(tracker_reply_alert, 15) virtual std::string message() const TORRENT_OVERRIDE; // tells how many peers the tracker returned in this response. This is // not expected to be greater than the ``num_want`` settings. These are not necessarily // all new peers, some of them may already be connected. int num_peers; }; // This alert is generated each time the DHT receives peers from a node. ``num_peers`` // is the number of peers we received in this packet. Typically these packets are // received from multiple DHT nodes, and so the alerts are typically generated // a few at a time. struct TORRENT_EXPORT dht_reply_alert TORRENT_FINAL : tracker_alert { // internal dht_reply_alert(aux::stack_allocator& alloc , torrent_handle const& h , int np); TORRENT_DEFINE_ALERT(dht_reply_alert, 16) virtual std::string message() const TORRENT_OVERRIDE; int num_peers; }; // This alert is generated each time a tracker announce is sent (or attempted to be sent). // There are no extra data members in this alert. The url can be found in the base class // however. struct TORRENT_EXPORT tracker_announce_alert TORRENT_FINAL : tracker_alert { // internal tracker_announce_alert(aux::stack_allocator& alloc , torrent_handle const& h , std::string const& u, int e); TORRENT_DEFINE_ALERT(tracker_announce_alert, 17) virtual std::string message() const TORRENT_OVERRIDE; // specifies what event was sent to the tracker. It is defined as: // // 0. None // 1. Completed // 2. Started // 3. Stopped int event; }; // This alert is generated when a finished piece fails its hash check. You can get the handle // to the torrent which got the failed piece and the index of the piece itself from the alert. struct TORRENT_EXPORT hash_failed_alert TORRENT_FINAL : torrent_alert { // internal hash_failed_alert(aux::stack_allocator& alloc, torrent_handle const& h , int index); TORRENT_DEFINE_ALERT(hash_failed_alert, 18) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; int piece_index; }; // This alert is generated when a peer is banned because it has sent too many corrupt pieces // to us. ``ip`` is the endpoint to the peer that was banned. struct TORRENT_EXPORT peer_ban_alert TORRENT_FINAL : peer_alert { // internal peer_ban_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id); TORRENT_DEFINE_ALERT(peer_ban_alert, 19) virtual std::string message() const TORRENT_OVERRIDE; }; // This alert is generated when a peer is unsnubbed. Essentially when it was snubbed for stalling // sending data, and now it started sending data again. struct TORRENT_EXPORT peer_unsnubbed_alert TORRENT_FINAL : peer_alert { // internal peer_unsnubbed_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id); TORRENT_DEFINE_ALERT(peer_unsnubbed_alert, 20) virtual std::string message() const TORRENT_OVERRIDE; }; // This alert is generated when a peer is snubbed, when it stops sending data when we request // it. struct TORRENT_EXPORT peer_snubbed_alert TORRENT_FINAL : peer_alert { // internal peer_snubbed_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id); TORRENT_DEFINE_ALERT(peer_snubbed_alert, 21) virtual std::string message() const TORRENT_OVERRIDE; }; // This alert is generated when a peer sends invalid data over the peer-peer protocol. The peer // will be disconnected, but you get its ip address from the alert, to identify it. struct TORRENT_EXPORT peer_error_alert TORRENT_FINAL : peer_alert { // internal peer_error_alert(aux::stack_allocator& alloc, torrent_handle const& h , tcp::endpoint const& ep, peer_id const& peer_id, int op , error_code const& e); TORRENT_DEFINE_ALERT(peer_error_alert, 22) static const int static_category = alert::peer_notification; virtual std::string message() const TORRENT_OVERRIDE; // a NULL-terminated string of the low-level operation that failed, or NULL if // there was no low level disk operation. int operation; // tells you what error caused this alert. error_code error; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER msg; #endif }; // This alert is posted every time an outgoing peer connect attempts succeeds. struct TORRENT_EXPORT peer_connect_alert TORRENT_FINAL : peer_alert { // internal peer_connect_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id, int type); TORRENT_DEFINE_ALERT(peer_connect_alert, 23) static const int static_category = alert::debug_notification; virtual std::string message() const TORRENT_OVERRIDE; int socket_type; }; // This alert is generated when a peer is disconnected for any reason (other than the ones // covered by peer_error_alert ). struct TORRENT_EXPORT peer_disconnected_alert TORRENT_FINAL : peer_alert { // internal peer_disconnected_alert(aux::stack_allocator& alloc , torrent_handle const& h, tcp::endpoint const& ep , peer_id const& peer_id, operation_t op, int type, error_code const& e , close_reason_t r); TORRENT_DEFINE_ALERT(peer_disconnected_alert, 24) static const int static_category = alert::debug_notification; virtual std::string message() const TORRENT_OVERRIDE; // the kind of socket this peer was connected over int socket_type; // the operation or level where the error occurred. Specified as an // value from the operation_t enum. Defined in operations.hpp. operation_t operation; // tells you what error caused peer to disconnect. error_code error; // the reason the peer disconnected (if specified) close_reason_t reason; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER msg; #endif }; // This is a debug alert that is generated by an incoming invalid piece request. // ``ip`` is the address of the peer and the ``request`` is the actual incoming // request from the peer. See peer_request for more info. struct TORRENT_EXPORT invalid_request_alert TORRENT_FINAL : peer_alert { // internal invalid_request_alert(aux::stack_allocator& alloc , torrent_handle const& h, tcp::endpoint const& ep , peer_id const& peer_id, peer_request const& r , bool we_have, bool peer_interested, bool withheld); TORRENT_DEFINE_ALERT(invalid_request_alert, 25) virtual std::string message() const TORRENT_OVERRIDE; // the request we received from the peer peer_request request; // true if we have this piece bool we_have; // true if the peer indicated that it was interested to download before // sending the request bool peer_interested; // if this is true, the peer is not allowed to download this piece because // of super-seeding rules. bool withheld; }; // This alert is generated when a torrent switches from being a downloader to a seed. // It will only be generated once per torrent. It contains a torrent_handle to the // torrent in question. struct TORRENT_EXPORT torrent_finished_alert TORRENT_FINAL : torrent_alert { // internal torrent_finished_alert(aux::stack_allocator& alloc, torrent_handle h); TORRENT_DEFINE_ALERT_PRIO(torrent_finished_alert, 26, alert_priority_high) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; }; // this alert is posted every time a piece completes downloading // and passes the hash check. This alert derives from torrent_alert // which contains the torrent_handle to the torrent the piece belongs to. struct TORRENT_EXPORT piece_finished_alert TORRENT_FINAL : torrent_alert { // internal piece_finished_alert(aux::stack_allocator& alloc, torrent_handle const& h, int piece_num); TORRENT_DEFINE_ALERT(piece_finished_alert, 27) static const int static_category = alert::piece_progress_notification | alert::progress_notification ; virtual std::string message() const TORRENT_OVERRIDE; // the index of the piece that finished int piece_index; }; // This alert is generated when a peer rejects or ignores a piece request. struct TORRENT_EXPORT request_dropped_alert TORRENT_FINAL : peer_alert { // internal request_dropped_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id, int block_num , int piece_num); TORRENT_DEFINE_ALERT(request_dropped_alert, 28) static const int static_category = alert::block_progress_notification | alert::peer_notification | alert::progress_notification ; virtual std::string message() const TORRENT_OVERRIDE; int block_index; int piece_index; }; // This alert is generated when a block request times out. struct TORRENT_EXPORT block_timeout_alert TORRENT_FINAL : peer_alert { // internal block_timeout_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id, int block_num , int piece_num); TORRENT_DEFINE_ALERT(block_timeout_alert, 29) static const int static_category = alert::block_progress_notification | alert::peer_notification | alert::progress_notification ; virtual std::string message() const TORRENT_OVERRIDE; int block_index; int piece_index; }; // This alert is generated when a block request receives a response. struct TORRENT_EXPORT block_finished_alert TORRENT_FINAL : peer_alert { // internal block_finished_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id, int block_num , int piece_num); TORRENT_DEFINE_ALERT(block_finished_alert, 30) static const int static_category = alert::block_progress_notification | alert::progress_notification ; virtual std::string message() const TORRENT_OVERRIDE; int block_index; int piece_index; }; // This alert is generated when a block request is sent to a peer. struct TORRENT_EXPORT block_downloading_alert TORRENT_FINAL : peer_alert { // internal block_downloading_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep , peer_id const& peer_id, int block_num, int piece_num); TORRENT_DEFINE_ALERT(block_downloading_alert, 31) static const int static_category = alert::block_progress_notification | alert::progress_notification ; virtual std::string message() const TORRENT_OVERRIDE; #ifndef TORRENT_NO_DEPRECATE char const* TORRENT_DEPRECATED_MEMBER peer_speedmsg; #endif int block_index; int piece_index; }; // This alert is generated when a block is received that was not requested or // whose request timed out. struct TORRENT_EXPORT unwanted_block_alert TORRENT_FINAL : peer_alert { // internal unwanted_block_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep , peer_id const& peer_id, int block_num, int piece_num); TORRENT_DEFINE_ALERT(unwanted_block_alert, 32) virtual std::string message() const TORRENT_OVERRIDE; int block_index; int piece_index; }; // The ``storage_moved_alert`` is generated when all the disk IO has // completed and the files have been moved, as an effect of a call to // ``torrent_handle::move_storage``. This is useful to synchronize with the // actual disk. The ``storage_path()`` member return the new path of the // storage. struct TORRENT_EXPORT storage_moved_alert TORRENT_FINAL : torrent_alert { // internal storage_moved_alert(aux::stack_allocator& alloc , torrent_handle const& h, std::string const& p); TORRENT_DEFINE_ALERT_PRIO(storage_moved_alert, 33, alert_priority_critical) static const int static_category = alert::storage_notification; virtual std::string message() const TORRENT_OVERRIDE; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER path; #endif // the path the torrent was moved to char const* storage_path() const; private: int m_path_idx; }; // The ``storage_moved_failed_alert`` is generated when an attempt to move the storage, // via torrent_handle::move_storage(), fails. struct TORRENT_EXPORT storage_moved_failed_alert TORRENT_FINAL : torrent_alert { // internal storage_moved_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h , error_code const& e , std::string const& file , char const* op); TORRENT_DEFINE_ALERT_PRIO(storage_moved_failed_alert, 34, alert_priority_critical) static const int static_category = alert::storage_notification; virtual std::string message() const TORRENT_OVERRIDE; error_code error; #ifndef TORRENT_NO_DEPRECATE // If the error happened for a specific file, ``file`` is its path. std::string TORRENT_DEPRECATED_MEMBER file; #endif // If the error happened for a specific file, this returns its path. char const* file_path() const; // If the error happened in a specific disk operation this is a NULL // terminated string naming which one, otherwise it's NULL. char const* operation; private: int m_file_idx; }; // This alert is generated when a request to delete the files of a torrent complete. // // The ``info_hash`` is the info-hash of the torrent that was just deleted. Most of // the time the torrent_handle in the ``torrent_alert`` will be invalid by the time // this alert arrives, since the torrent is being deleted. The ``info_hash`` member // is hence the main way of identifying which torrent just completed the delete. // // This alert is posted in the ``storage_notification`` category, and that bit // needs to be set in the alert_mask. struct TORRENT_EXPORT torrent_deleted_alert TORRENT_FINAL : torrent_alert { // internal torrent_deleted_alert(aux::stack_allocator& alloc , torrent_handle const& h, sha1_hash const& ih); TORRENT_DEFINE_ALERT_PRIO(torrent_deleted_alert, 35, alert_priority_critical) static const int static_category = alert::storage_notification; virtual std::string message() const TORRENT_OVERRIDE; sha1_hash info_hash; }; // This alert is generated when a request to delete the files of a torrent fails. // Just removing a torrent from the session cannot fail struct TORRENT_EXPORT torrent_delete_failed_alert TORRENT_FINAL : torrent_alert { // internal torrent_delete_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h, error_code const& e, sha1_hash const& ih); TORRENT_DEFINE_ALERT_PRIO(torrent_delete_failed_alert, 36, alert_priority_critical) static const int static_category = alert::storage_notification | alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; // tells you why it failed. error_code error; // the info hash of the torrent whose files failed to be deleted sha1_hash info_hash; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER msg; #endif }; // This alert is generated as a response to a ``torrent_handle::save_resume_data`` request. // It is generated once the disk IO thread is done writing the state for this torrent. struct TORRENT_EXPORT save_resume_data_alert TORRENT_FINAL : torrent_alert { // internal save_resume_data_alert(aux::stack_allocator& alloc , boost::shared_ptr const& rd , torrent_handle const& h); TORRENT_DEFINE_ALERT_PRIO(save_resume_data_alert, 37, alert_priority_critical) static const int static_category = alert::storage_notification; virtual std::string message() const TORRENT_OVERRIDE; // points to the resume data. boost::shared_ptr resume_data; }; // This alert is generated instead of ``save_resume_data_alert`` if there was an error // generating the resume data. ``error`` describes what went wrong. struct TORRENT_EXPORT save_resume_data_failed_alert TORRENT_FINAL : torrent_alert { // internal save_resume_data_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h, error_code const& e); TORRENT_DEFINE_ALERT_PRIO(save_resume_data_failed_alert, 38, alert_priority_critical) static const int static_category = alert::storage_notification | alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; // the error code from the resume_data failure error_code error; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER msg; #endif }; // This alert is generated as a response to a ``torrent_handle::pause`` request. It is // generated once all disk IO is complete and the files in the torrent have been closed. // This is useful for synchronizing with the disk. struct TORRENT_EXPORT torrent_paused_alert TORRENT_FINAL : torrent_alert { // internal torrent_paused_alert(aux::stack_allocator& alloc, torrent_handle const& h); TORRENT_DEFINE_ALERT_PRIO(torrent_paused_alert, 39, alert_priority_high) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; }; // This alert is generated as a response to a torrent_handle::resume() request. It is // generated when a torrent goes from a paused state to an active state. struct TORRENT_EXPORT torrent_resumed_alert TORRENT_FINAL : torrent_alert { // internal torrent_resumed_alert(aux::stack_allocator& alloc, torrent_handle const& h); TORRENT_DEFINE_ALERT_PRIO(torrent_resumed_alert, 40, alert_priority_high) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; }; // This alert is posted when a torrent completes checking. i.e. when it transitions // out of the ``checking files`` state into a state where it is ready to start downloading struct TORRENT_EXPORT torrent_checked_alert TORRENT_FINAL : torrent_alert { // internal torrent_checked_alert(aux::stack_allocator& alloc, torrent_handle const& h); TORRENT_DEFINE_ALERT_PRIO(torrent_checked_alert, 41, alert_priority_high) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; }; // This alert is generated when a HTTP seed name lookup fails. struct TORRENT_EXPORT url_seed_alert TORRENT_FINAL : torrent_alert { // internal url_seed_alert(aux::stack_allocator& alloc, torrent_handle const& h , std::string const& u, error_code const& e); url_seed_alert(aux::stack_allocator& alloc, torrent_handle const& h , std::string const& u, std::string const& m); TORRENT_DEFINE_ALERT(url_seed_alert, 42) static const int static_category = alert::peer_notification | alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; #ifndef TORRENT_NO_DEPRECATE // the HTTP seed that failed std::string TORRENT_DEPRECATED_MEMBER url; // the error message, potentially from the server std::string TORRENT_DEPRECATED_MEMBER msg; #endif // the error the web seed encountered. If this is not set, the server // sent an error message, call ``error_message()``. error_code error; // the URL the error is associated with char const* server_url() const; // in case the web server sent an error message, this function returns // it. char const* error_message() const; private: int m_url_idx; int m_msg_idx; }; // If the storage fails to read or write files that it needs access to, this alert is // generated and the torrent is paused. struct TORRENT_EXPORT file_error_alert TORRENT_FINAL : torrent_alert { // internal file_error_alert(aux::stack_allocator& alloc , error_code const& ec , std::string const& file , char const* op , torrent_handle const& h); TORRENT_DEFINE_ALERT_PRIO(file_error_alert, 43, alert_priority_high) static const int static_category = alert::status_notification | alert::error_notification | alert::storage_notification; virtual std::string message() const TORRENT_OVERRIDE; #ifndef TORRENT_NO_DEPRECATE // the path to the file that was accessed when the error occurred. std::string TORRENT_DEPRECATED_MEMBER file; #endif // the error code describing the error. error_code error; char const* operation; // the file that experienced the error char const* filename() const; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER msg; #endif private: int m_file_idx; }; // This alert is generated when the metadata has been completely received and the info-hash // failed to match it. i.e. the metadata that was received was corrupt. libtorrent will // automatically retry to fetch it in this case. This is only relevant when running a // torrent-less download, with the metadata extension provided by libtorrent. struct TORRENT_EXPORT metadata_failed_alert TORRENT_FINAL : torrent_alert { // internal metadata_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h, error_code const& ec); TORRENT_DEFINE_ALERT(metadata_failed_alert, 44) static const int static_category = alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; // indicates what failed when parsing the metadata. This error is // what's returned from lazy_bdecode(). error_code error; }; // This alert is generated when the metadata has been completely received and the torrent // can start downloading. It is not generated on torrents that are started with metadata, but // only those that needs to download it from peers (when utilizing the libtorrent extension). // // There are no additional data members in this alert. // // Typically, when receiving this alert, you would want to save the torrent file in order // to load it back up again when the session is restarted. Here's an example snippet of // code to do that:: // // torrent_handle h = alert->handle(); // if (h.is_valid()) { // boost::shared_ptr ti = h.torrent_file(); // create_torrent ct(*ti); // entry te = ct.generate(); // std::vector buffer; // bencode(std::back_inserter(buffer), te); // FILE* f = fopen((to_hex(ti->info_hash().to_string()) + ".torrent").c_str(), "wb+"); // if (f) { // fwrite(&buffer[0], 1, buffer.size(), f); // fclose(f); // } // } // struct TORRENT_EXPORT metadata_received_alert TORRENT_FINAL : torrent_alert { // internal metadata_received_alert(aux::stack_allocator& alloc , torrent_handle const& h); TORRENT_DEFINE_ALERT(metadata_received_alert, 45) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; }; // This alert is posted when there is an error on the UDP socket. The // UDP socket is used for all uTP, DHT and UDP tracker traffic. It's // global to the session. struct TORRENT_EXPORT udp_error_alert TORRENT_FINAL : alert { // internal udp_error_alert( aux::stack_allocator& alloc , udp::endpoint const& ep , error_code const& ec); TORRENT_DEFINE_ALERT(udp_error_alert, 46) static const int static_category = alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; // the source address associated with the error (if any) udp::endpoint endpoint; // the error code describing the error error_code error; }; // Whenever libtorrent learns about the machines external IP, this alert is // generated. The external IP address can be acquired from the tracker (if it // supports that) or from peers that supports the extension protocol. // The address can be accessed through the ``external_address`` member. struct TORRENT_EXPORT external_ip_alert TORRENT_FINAL : alert { // internal external_ip_alert(aux::stack_allocator& alloc, address const& ip); TORRENT_DEFINE_ALERT(external_ip_alert, 47) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; // the IP address that is believed to be our external IP address external_address; }; // This alert is generated when none of the ports, given in the port range, to // session can be opened for listening. The ``endpoint`` member is the // interface and port that failed, ``error`` is the error code describing // the failure. // // libtorrent may sometimes try to listen on port 0, if all other ports failed. // Port 0 asks the operating system to pick a port that's free). If that fails // you may see a listen_failed_alert with port 0 even if you didn't ask to // listen on it. struct TORRENT_EXPORT listen_failed_alert TORRENT_FINAL : alert { enum socket_type_t { tcp, tcp_ssl, udp, i2p, socks5, utp_ssl }; // internal listen_failed_alert( aux::stack_allocator& alloc , std::string const& iface , int port , int op , error_code const& ec , socket_type_t t); TORRENT_DEFINE_ALERT_PRIO(listen_failed_alert, 48, alert_priority_critical) static const int static_category = alert::status_notification | alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; // the interface libtorrent attempted to listen on that failed. char const* listen_interface() const; // the error the system returned error_code error; enum op_t { parse_addr, open, bind, listen, get_peer_name, accept }; // the specific low level operation that failed. See op_t. int operation; // the type of listen socket this alert refers to. socket_type_t sock_type; // the address and port libtorrent attempted to listen on tcp::endpoint endpoint; private: aux::stack_allocator const& m_alloc; int m_interface_idx; }; // This alert is posted when the listen port succeeds to be opened on a // particular interface. ``endpoint`` is the endpoint that successfully // was opened for listening. struct TORRENT_EXPORT listen_succeeded_alert TORRENT_FINAL : alert { enum socket_type_t { tcp, tcp_ssl, udp, i2p, socks5, utp_ssl }; // internal listen_succeeded_alert(aux::stack_allocator& alloc, tcp::endpoint const& ep , socket_type_t t); TORRENT_DEFINE_ALERT_PRIO(listen_succeeded_alert, 49, alert_priority_critical) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; // the endpoint libtorrent ended up listening on. The address // refers to the local interface and the port is the listen port. tcp::endpoint endpoint; // the type of listen socket this alert refers to. socket_type_t sock_type; }; // This alert is generated when a NAT router was successfully found but some // part of the port mapping request failed. It contains a text message that // may help the user figure out what is wrong. This alert is not generated in // case it appears the client is not running on a NAT:ed network or if it // appears there is no NAT router that can be remote controlled to add port // mappings. struct TORRENT_EXPORT portmap_error_alert TORRENT_FINAL : alert { // internal portmap_error_alert(aux::stack_allocator& alloc, int i, int t , error_code const& e); TORRENT_DEFINE_ALERT(portmap_error_alert, 50) static const int static_category = alert::port_mapping_notification | alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; // refers to the mapping index of the port map that failed, i.e. // the index returned from add_mapping(). int mapping; // is 0 for NAT-PMP and 1 for UPnP. int map_type; // tells you what failed. error_code error; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER msg; #endif }; // This alert is generated when a NAT router was successfully found and // a port was successfully mapped on it. On a NAT:ed network with a NAT-PMP // capable router, this is typically generated once when mapping the TCP // port and, if DHT is enabled, when the UDP port is mapped. struct TORRENT_EXPORT portmap_alert TORRENT_FINAL : alert { // internal portmap_alert(aux::stack_allocator& alloc, int i, int port, int t, int protocol); TORRENT_DEFINE_ALERT(portmap_alert, 51) static const int static_category = alert::port_mapping_notification; virtual std::string message() const TORRENT_OVERRIDE; // refers to the mapping index of the port map that failed, i.e. // the index returned from add_mapping(). int mapping; // the external port allocated for the mapping. int external_port; // 0 for NAT-PMP and 1 for UPnP. int map_type; enum protocol_t { tcp, udp }; // the protocol this mapping was for. one of protocol_t enums int protocol; }; #ifndef TORRENT_DISABLE_LOGGING // This alert is generated to log informational events related to either // UPnP or NAT-PMP. They contain a log line and the type (0 = NAT-PMP // and 1 = UPnP). Displaying these messages to an end user is only useful // for debugging the UPnP or NAT-PMP implementation. This alert is only // posted if the alert::port_mapping_log_notification flag is enabled in // the alert mask. struct TORRENT_EXPORT portmap_log_alert TORRENT_FINAL : alert { // internal portmap_log_alert(aux::stack_allocator& alloc, int t, const char* m); TORRENT_DEFINE_ALERT(portmap_log_alert, 52) static const int static_category = alert::port_mapping_log_notification; virtual std::string message() const TORRENT_OVERRIDE; int map_type; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER msg; #endif // the message associated with this log line char const* log_message() const; private: // TODO: 2 should the alert base class have this object instead? aux::stack_allocator const& m_alloc; int m_log_idx; }; #endif // This alert is generated when a fastresume file has been passed to // add_torrent() but the files on disk did not match the fastresume file. // The error_code explains the reason why the resume file was rejected. struct TORRENT_EXPORT fastresume_rejected_alert TORRENT_FINAL : torrent_alert { // internal fastresume_rejected_alert(aux::stack_allocator& alloc , torrent_handle const& h , error_code const& ec , std::string const& file , char const* op); TORRENT_DEFINE_ALERT_PRIO(fastresume_rejected_alert, 53, alert_priority_critical) static const int static_category = alert::status_notification | alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; error_code error; #ifndef TORRENT_NO_DEPRECATE // If the error happened to a specific file, ``file`` is the path to it. std::string TORRENT_DEPRECATED_MEMBER file; #endif // If the error happened to a specific file, this returns the path to it. char const* file_path() const; // If the error happened in a disk operation. a NULL-terminated string of // the name of that operation. ``operation`` is NULL otherwise. char const* operation; #ifndef TORRENT_NO_DEPRECATE std::string TORRENT_DEPRECATED_MEMBER msg; #endif private: int m_path_idx; }; // This alert is posted when an incoming peer connection, or a peer that's about to be added // to our peer list, is blocked for some reason. This could be any of: // // * the IP filter // * i2p mixed mode restrictions (a normal peer is not allowed on an i2p swarm) // * the port filter // * the peer has a low port and ``no_connect_privileged_ports`` is enabled // * the protocol of the peer is blocked (uTP/TCP blocking) struct TORRENT_EXPORT peer_blocked_alert TORRENT_FINAL : torrent_alert { // internal peer_blocked_alert(aux::stack_allocator& alloc, torrent_handle const& h , address const& i, int r); TORRENT_DEFINE_ALERT(peer_blocked_alert, 54) static const int static_category = alert::ip_block_notification; virtual std::string message() const TORRENT_OVERRIDE; // the address that was blocked. address ip; enum reason_t { ip_filter, port_filter, i2p_mixed, privileged_ports, utp_disabled, tcp_disabled, invalid_local_interface }; int reason; }; // This alert is generated when a DHT node announces to an info-hash on our // DHT node. It belongs to the ``dht_notification`` category. struct TORRENT_EXPORT dht_announce_alert TORRENT_FINAL : alert { // internal dht_announce_alert(aux::stack_allocator& alloc, address const& i, int p , sha1_hash const& ih); TORRENT_DEFINE_ALERT(dht_announce_alert, 55) static const int static_category = alert::dht_notification; virtual std::string message() const TORRENT_OVERRIDE; address ip; int port; sha1_hash info_hash; }; // This alert is generated when a DHT node sends a ``get_peers`` message to // our DHT node. It belongs to the ``dht_notification`` category. struct TORRENT_EXPORT dht_get_peers_alert TORRENT_FINAL : alert { // internal dht_get_peers_alert(aux::stack_allocator& alloc, sha1_hash const& ih); TORRENT_DEFINE_ALERT(dht_get_peers_alert, 56) static const int static_category = alert::dht_notification; virtual std::string message() const TORRENT_OVERRIDE; sha1_hash info_hash; }; // This alert is posted approximately once every second, and it contains // byte counters of most statistics that's tracked for torrents. Each active // torrent posts these alerts regularly. // This alert has been superceded by calling ``post_torrent_updates()`` // regularly on the session object. This alert will be removed struct TORRENT_EXPORT stats_alert TORRENT_FINAL : torrent_alert { // internal stats_alert(aux::stack_allocator& alloc, torrent_handle const& h, int interval , stat const& s); TORRENT_DEFINE_ALERT(stats_alert, 57) static const int static_category = alert::stats_notification; virtual std::string message() const TORRENT_OVERRIDE; enum stats_channel { upload_payload, upload_protocol, download_payload, download_protocol, upload_ip_protocol, #ifndef TORRENT_NO_DEPRECATE upload_dht_protocol TORRENT_DEPRECATED_ENUM, upload_tracker_protocol TORRENT_DEPRECATED_ENUM, #else deprecated1, deprecated2, #endif download_ip_protocol, #ifndef TORRENT_NO_DEPRECATE download_dht_protocol TORRENT_DEPRECATED_ENUM, download_tracker_protocol TORRENT_DEPRECATED_ENUM, #else deprecated3, deprecated4, #endif num_channels }; // an array of samples. The enum describes what each sample is a // measurement of. All of these are raw, and not smoothing is performed. int transferred[num_channels]; // the number of milliseconds during which these stats were collected. // This is typically just above 1000, but if CPU is limited, it may be // higher than that. int interval; }; // This alert is posted when the disk cache has been flushed for a specific // torrent as a result of a call to torrent_handle::flush_cache(). This // alert belongs to the ``storage_notification`` category, which must be // enabled to let this alert through. The alert is also posted when removing // a torrent from the session, once the outstanding cache flush is complete // and the torrent does no longer have any files open. struct TORRENT_EXPORT cache_flushed_alert TORRENT_FINAL : torrent_alert { // internal cache_flushed_alert(aux::stack_allocator& alloc, torrent_handle const& h); TORRENT_DEFINE_ALERT_PRIO(cache_flushed_alert, 58, alert_priority_high) static const int static_category = alert::storage_notification; }; // This alert is posted when a bittorrent feature is blocked because of the // anonymous mode. For instance, if the tracker proxy is not set up, no // trackers will be used, because trackers can only be used through proxies // when in anonymous mode. struct TORRENT_EXPORT anonymous_mode_alert TORRENT_FINAL : torrent_alert { // internal anonymous_mode_alert(aux::stack_allocator& alloc, torrent_handle const& h , int k, std::string const& s); TORRENT_DEFINE_ALERT(anonymous_mode_alert, 59) static const int static_category = alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; enum kind_t { // means that there's no proxy set up for tracker // communication and the tracker will not be contacted. // The tracker which this failed for is specified in the ``str`` member. tracker_not_anonymous = 0 }; // specifies what error this is, see kind_t. int kind; std::string str; }; // This alert is generated when we receive a local service discovery message // from a peer for a torrent we're currently participating in. struct TORRENT_EXPORT lsd_peer_alert TORRENT_FINAL : peer_alert { // internal lsd_peer_alert(aux::stack_allocator& alloc, torrent_handle const& h , tcp::endpoint const& i); TORRENT_DEFINE_ALERT(lsd_peer_alert, 60) static const int static_category = alert::peer_notification; virtual std::string message() const TORRENT_OVERRIDE; }; // This alert is posted whenever a tracker responds with a ``trackerid``. // The tracker ID is like a cookie. The libtorrent will store the tracker ID // for this tracker and repeat it in subsequent announces. struct TORRENT_EXPORT trackerid_alert TORRENT_FINAL : tracker_alert { // internal trackerid_alert(aux::stack_allocator& alloc, torrent_handle const& h , std::string const& u , const std::string& id); TORRENT_DEFINE_ALERT(trackerid_alert, 61) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; #ifndef TORRENT_NO_DEPRECATE // The tracker ID returned by the tracker std::string TORRENT_DEPRECATED_MEMBER trackerid; #endif // The tracker ID returned by the tracker char const* tracker_id() const; private: int m_tracker_idx; }; // This alert is posted when the initial DHT bootstrap is done. struct TORRENT_EXPORT dht_bootstrap_alert TORRENT_FINAL : alert { // internal dht_bootstrap_alert(aux::stack_allocator& alloc); TORRENT_DEFINE_ALERT(dht_bootstrap_alert, 62) static const int static_category = alert::dht_notification; virtual std::string message() const TORRENT_OVERRIDE; }; #ifndef TORRENT_NO_DEPRECATE // This alert is posted on RSS feed events such as start of RSS feed updates, // successful completed updates and errors during updates. // // This alert is only posted if the ``rss_notifications`` category is enabled // in the alert_mask. struct TORRENT_DEPRECATED TORRENT_EXPORT rss_alert TORRENT_FINAL : alert { // internal rss_alert(aux::stack_allocator& alloc, feed_handle h , std::string const& u, int s, error_code const& ec); TORRENT_DEFINE_ALERT(rss_alert, 63) static const int static_category = alert::rss_notification; virtual std::string message() const TORRENT_OVERRIDE; enum state_t { // An update of this feed was just initiated, it will either succeed // or fail soon. state_updating, // The feed just completed a successful update, there may be new items // in it. If you're adding torrents manually, you may want to request // the feed status of the feed and look through the ``items`` vector. state_updated, // An error just occurred. See the ``error`` field for information on // what went wrong. state_error }; // the handle to the feed which generated this alert. feed_handle handle; // a short cut to access the url of the feed, without // having to call feed_handle::get_settings(). std::string url; // one of the values from rss_alert::state_t. int state; // an error code used for when an error occurs on the feed. error_code error; }; #endif // TORRENT_NO_DEPRECATE // This is posted whenever a torrent is transitioned into the error state. struct TORRENT_EXPORT torrent_error_alert TORRENT_FINAL : torrent_alert { // internal torrent_error_alert(aux::stack_allocator& alloc, torrent_handle const& h , error_code const& e, std::string const& f); TORRENT_DEFINE_ALERT_PRIO(torrent_error_alert, 64, alert_priority_high) static const int static_category = alert::error_notification | alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; // specifies which error the torrent encountered. error_code error; #ifndef TORRENT_NO_DEPRECATE // the filename (or object) the error occurred on. std::string TORRENT_DEPRECATED_MEMBER error_file; #endif // the filename (or object) the error occurred on. char const* filename() const; private: int m_file_idx; }; // This is always posted for SSL torrents. This is a reminder to the client that // the torrent won't work unless torrent_handle::set_ssl_certificate() is called with // a valid certificate. Valid certificates MUST be signed by the SSL certificate // in the .torrent file. struct TORRENT_EXPORT torrent_need_cert_alert TORRENT_FINAL : torrent_alert { // internal torrent_need_cert_alert(aux::stack_allocator& alloc , torrent_handle const& h); TORRENT_DEFINE_ALERT_PRIO(torrent_need_cert_alert, 65, alert_priority_critical) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; error_code error; }; // The incoming connection alert is posted every time we successfully accept // an incoming connection, through any mean. The most straight-forward ways // of accepting incoming connections are through the TCP listen socket and // the UDP listen socket for uTP sockets. However, connections may also be // accepted through a Socks5 or i2p listen socket, or via an SSL listen // socket. struct TORRENT_EXPORT incoming_connection_alert TORRENT_FINAL : alert { // internal incoming_connection_alert(aux::stack_allocator& alloc, int t , tcp::endpoint const& i); TORRENT_DEFINE_ALERT(incoming_connection_alert, 66) static const int static_category = alert::peer_notification; virtual std::string message() const TORRENT_OVERRIDE; // tells you what kind of socket the connection was accepted // as: // // 0. none (no socket instantiated) // 1. TCP // 2. Socks5 // 3. HTTP // 4. uTP // 5. i2p // 6. SSL/TCP // 7. SSL/Socks5 // 8. HTTPS (SSL/HTTP) // 9. SSL/uTP // int socket_type; // is the IP address and port the connection came from. tcp::endpoint ip; }; // This alert is always posted when a torrent was attempted to be added // and contains the return status of the add operation. The torrent handle of the new // torrent can be found in the base class' ``handle`` member. If adding // the torrent failed, ``error`` contains the error code. struct TORRENT_EXPORT add_torrent_alert TORRENT_FINAL : torrent_alert { // internal add_torrent_alert(aux::stack_allocator& alloc, torrent_handle h , add_torrent_params const& p, error_code ec); TORRENT_DEFINE_ALERT_PRIO(add_torrent_alert, 67, alert_priority_critical) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; // a copy of the parameters used when adding the torrent, it can be used // to identify which invocation to ``async_add_torrent()`` caused this alert. add_torrent_params params; // set to the error, if one occurred while adding the torrent. error_code error; }; // This alert is only posted when requested by the user, by calling // session::post_torrent_updates() on the session. It contains the torrent // status of all torrents that changed since last time this message was // posted. Its category is ``status_notification``, but it's not subject to // filtering, since it's only manually posted anyway. struct TORRENT_EXPORT state_update_alert TORRENT_FINAL : alert { state_update_alert(aux::stack_allocator& alloc , std::vector st); TORRENT_DEFINE_ALERT_PRIO(state_update_alert, 68, alert_priority_high) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; // contains the torrent status of all torrents that changed since last // time this message was posted. Note that you can map a torrent status // to a specific torrent via its ``handle`` member. The receiving end is // suggested to have all torrents sorted by the torrent_handle or hashed // by it, for efficient updates. std::vector status; }; #ifndef TORRENT_NO_DEPRECATE struct TORRENT_DEPRECATED_EXPORT mmap_cache_alert TORRENT_FINAL : alert { mmap_cache_alert(aux::stack_allocator& alloc , error_code const& ec); TORRENT_DEFINE_ALERT(mmap_cache_alert, 69) static const int static_category = alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; error_code error; }; #endif // The session_stats_alert is posted when the user requests session statistics by // calling post_session_stats() on the session object. Its category is // ``status_notification``, but it is not subject to filtering, since it's only // manually posted anyway. struct TORRENT_EXPORT session_stats_alert TORRENT_FINAL : alert { session_stats_alert(aux::stack_allocator& alloc, counters const& cnt); TORRENT_DEFINE_ALERT_PRIO(session_stats_alert, 70, alert_priority_critical) static const int static_category = alert::stats_notification; virtual std::string message() const TORRENT_OVERRIDE; // An array are a mix of *counters* and *gauges*, which meanings can be // queries via the session_stats_metrics() function on the session. The // mapping from a specific metric to an index into this array is constant // for a specific version of libtorrent, but may differ for other // versions. The intended usage is to request the mapping, i.e. call // session_stats_metrics(), once on startup, and then use that mapping to // interpret these values throughout the process' runtime. // // For more information, see the session-statistics_ section. boost::uint64_t values[counters::num_counters]; }; // hidden // When a torrent changes its info-hash, this alert is posted. This only // happens in very specific cases. For instance, when a torrent is // downloaded from a URL, the true info hash is not known immediately. First // the .torrent file must be downloaded and parsed. // // Once this download completes, the ``torrent_update_alert`` is posted to // notify the client of the info-hash changing. struct TORRENT_EXPORT torrent_update_alert TORRENT_FINAL : torrent_alert { // internal torrent_update_alert(aux::stack_allocator& alloc, torrent_handle h , sha1_hash const& old_hash, sha1_hash const& new_hash); TORRENT_DEFINE_ALERT_PRIO(torrent_update_alert, 71, alert_priority_critical) static const int static_category = alert::status_notification; virtual std::string message() const TORRENT_OVERRIDE; // ``old_ih`` and ``new_ih`` are the previous and new info-hash for the torrent, respectively. sha1_hash old_ih; sha1_hash new_ih; }; #ifndef TORRENT_NO_DEPRECATE // This alert is posted every time a new RSS item (i.e. torrent) is received // from an RSS feed. // // It is only posted if the ``rss_notifications`` category is enabled in the // alert_mask. struct TORRENT_EXPORT rss_item_alert TORRENT_FINAL : alert { // internal rss_item_alert(aux::stack_allocator& alloc, feed_handle h , feed_item const& item); TORRENT_DEFINE_ALERT(rss_item_alert, 72) static const int static_category = alert::rss_notification; virtual std::string message() const TORRENT_OVERRIDE; feed_handle handle; feed_item item; }; #endif // posted when something fails in the DHT. This is not necessarily a fatal // error, but it could prevent proper operation struct TORRENT_EXPORT dht_error_alert TORRENT_FINAL : alert { // internal dht_error_alert(aux::stack_allocator& alloc, int op, error_code const& ec); TORRENT_DEFINE_ALERT(dht_error_alert, 73) static const int static_category = alert::error_notification | alert::dht_notification; virtual std::string message() const TORRENT_OVERRIDE; // the error code error_code error; enum op_t { unknown, hostname_lookup }; // the operation that failed op_t operation; }; // this alert is posted as a response to a call to session::get_item(), // specifically the overload for looking up immutable items in the DHT. struct TORRENT_EXPORT dht_immutable_item_alert TORRENT_FINAL : alert { dht_immutable_item_alert(aux::stack_allocator& alloc, sha1_hash const& t , entry const& i); TORRENT_DEFINE_ALERT_PRIO(dht_immutable_item_alert, 74, alert_priority_critical) static const int static_category = alert::dht_notification; virtual std::string message() const TORRENT_OVERRIDE; // the target hash of the immutable item. This must // match the SHA-1 hash of the bencoded form of ``item``. sha1_hash target; // the data for this item entry item; }; // this alert is posted as a response to a call to session::get_item(), // specifically the overload for looking up mutable items in the DHT. struct TORRENT_EXPORT dht_mutable_item_alert TORRENT_FINAL : alert { dht_mutable_item_alert(aux::stack_allocator& alloc , boost::array k , boost::array sig , boost::uint64_t sequence , std::string const& s , entry const& i , bool a); TORRENT_DEFINE_ALERT_PRIO(dht_mutable_item_alert, 75, alert_priority_critical) static const int static_category = alert::dht_notification; virtual std::string message() const TORRENT_OVERRIDE; // the public key that was looked up boost::array key; // the signature of the data. This is not the signature of the // plain encoded form of the item, but it includes the sequence number // and possibly the hash as well. See the dht_store document for more // information. This is primarily useful for echoing back in a store // request. boost::array signature; // the sequence number of this item boost::uint64_t seq; // the salt, if any, used to lookup and store this item. If no // salt was used, this is an empty string std::string salt; // the data for this item entry item; // the last response for mutable data is authoritative. bool authoritative; }; // this is posted when a DHT put operation completes. This is useful if the // client is waiting for a put to complete before shutting down for instance. struct TORRENT_EXPORT dht_put_alert TORRENT_FINAL : alert { // internal dht_put_alert(aux::stack_allocator& alloc, sha1_hash const& t, int n); dht_put_alert(aux::stack_allocator& alloc, boost::array key , boost::array sig , std::string s , boost::uint64_t sequence_number , int n); TORRENT_DEFINE_ALERT(dht_put_alert, 76) static const int static_category = alert::dht_notification; virtual std::string message() const TORRENT_OVERRIDE; // the target hash the item was stored under if this was an *immutable* // item. sha1_hash target; // if a mutable item was stored, these are the public key, signature, // salt and sequence number the item was stored under. boost::array public_key; boost::array signature; std::string salt; boost::uint64_t seq; // DHT put operation usually writes item to k nodes, maybe the node // is stale so no response, or the node doesn't support 'put', or the // token for write is out of date, etc. num_success is the number of // successful responses we got from the puts. int num_success; }; // this alert is used to report errors in the i2p SAM connection struct TORRENT_EXPORT i2p_alert TORRENT_FINAL : alert { i2p_alert(aux::stack_allocator& alloc, error_code const& ec); TORRENT_DEFINE_ALERT(i2p_alert, 77) static const int static_category = alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; // the error that occurred in the i2p SAM connection error_code error; }; // This alert is generated when we send a get_peers request // It belongs to the ``dht_notification`` category. struct TORRENT_EXPORT dht_outgoing_get_peers_alert TORRENT_FINAL : alert { // internal dht_outgoing_get_peers_alert(aux::stack_allocator& alloc , sha1_hash const& ih, sha1_hash const& obfih , udp::endpoint ep); TORRENT_DEFINE_ALERT(dht_outgoing_get_peers_alert, 78) static const int static_category = alert::dht_notification; virtual std::string message() const TORRENT_OVERRIDE; // the info_hash of the torrent we're looking for peers for. sha1_hash info_hash; // if this was an obfuscated lookup, this is the info-hash target // actually sent to the node. sha1_hash obfuscated_info_hash; // the endpoint we're sending this query to udp::endpoint ip; }; #ifndef TORRENT_DISABLE_LOGGING // This alert is posted by some session wide event. Its main purpose is // trouble shooting and debugging. It's not enabled by the default alert // mask and is enabled by the ``alert::session_log_notification`` bit. // Furthermore, it's by default disabled as a build configuration. struct TORRENT_EXPORT log_alert TORRENT_FINAL : alert { // internal log_alert(aux::stack_allocator& alloc, char const* log); TORRENT_DEFINE_ALERT(log_alert, 79) static const int static_category = alert::session_log_notification; virtual std::string message() const TORRENT_OVERRIDE; // returns the log message char const* msg() const; private: aux::stack_allocator const& m_alloc; int m_str_idx; }; // This alert is posted by torrent wide events. It's meant to be used for // trouble shooting and debugging. It's not enabled by the default alert // mask and is enabled by the ``alert::torrent_log_notification`` bit. By // default it is disabled as a build configuration. struct TORRENT_EXPORT torrent_log_alert TORRENT_FINAL : torrent_alert { // internal torrent_log_alert(aux::stack_allocator& alloc, torrent_handle const& h , char const* log); TORRENT_DEFINE_ALERT(torrent_log_alert, 80) static const int static_category = alert::torrent_log_notification; virtual std::string message() const TORRENT_OVERRIDE; // returns the log message char const* msg() const; private: int m_str_idx; }; // This alert is posted by events specific to a peer. It's meant to be used // for trouble shooting and debugging. It's not enabled by the default alert // mask and is enabled by the ``alert::peer_log_notification`` bit. By // default it is disabled as a build configuration. struct TORRENT_EXPORT peer_log_alert TORRENT_FINAL : peer_alert { // describes whether this log refers to in-flow or out-flow of the // peer. The exception is ``info`` which is neither incoming or outgoing. enum direction_t { incoming_message, outgoing_message, incoming, outgoing, info }; // internal peer_log_alert(aux::stack_allocator& alloc, torrent_handle const& h , tcp::endpoint const& i, peer_id const& pi , peer_log_alert::direction_t dir , char const* event, char const* log); TORRENT_DEFINE_ALERT(peer_log_alert, 81) static const int static_category = alert::peer_log_notification; virtual std::string message() const TORRENT_OVERRIDE; // string literal indicating the kind of event. For messages, this is the // message name. char const* event_type; direction_t direction; // returns the log message char const* msg() const; private: int m_str_idx; }; #endif // posted if the local service discovery socket fails to start properly. // it's categorized as ``error_notification``. struct TORRENT_EXPORT lsd_error_alert TORRENT_FINAL : alert { // internal lsd_error_alert(aux::stack_allocator& alloc, error_code const& ec); TORRENT_DEFINE_ALERT(lsd_error_alert, 82) static const int static_category = alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; // The error code error_code error; }; // holds statistics about a current dht_lookup operation. // a DHT lookup is the traversal of nodes, looking up a // set of target nodes in the DHT for retrieving and possibly // storing information in the DHT struct TORRENT_EXPORT dht_lookup { // string literal indicating which kind of lookup this is char const* type; // the number of outstanding request to individual nodes // this lookup has right now int outstanding_requests; // the total number of requests that have timed out so far // for this lookup int timeouts; // the total number of responses we have received for this // lookup so far for this lookup int responses; // the branch factor for this lookup. This is the number of // nodes we keep outstanding requests to in parallel by default. // when nodes time out we may increase this. int branch_factor; // the number of nodes left that could be queries for this // lookup. Many of these are likely to be part of the trail // while performing the lookup and would never end up actually // being queried. int nodes_left; // the number of seconds ago the // last message was sent that's still // outstanding int last_sent; // the number of outstanding requests // that have exceeded the short timeout // and are considered timed out in the // sense that they increased the branch // factor int first_timeout; }; // struct to hold information about a single DHT routing table bucket struct TORRENT_EXPORT dht_routing_bucket { // the total number of nodes and replacement nodes // in the routing table int num_nodes; int num_replacements; // number of seconds since last activity int last_active; }; // contains current DHT state. Posted in response to session::post_dht_stats(). struct TORRENT_EXPORT dht_stats_alert TORRENT_FINAL : alert { // internal dht_stats_alert(aux::stack_allocator& alloc , std::vector const& table , std::vector const& requests); TORRENT_DEFINE_ALERT(dht_stats_alert, 83) static const int static_category = alert::stats_notification; virtual std::string message() const TORRENT_OVERRIDE; // a vector of the currently running DHT lookups. std::vector active_requests; // contains information about every bucket in the DHT routing // table. std::vector routing_table; }; // posted every time an incoming request from a peer is accepted and queued // up for being serviced. This alert is only posted if // the alert::incoming_request_notification flag is enabled in the alert // mask. struct TORRENT_EXPORT incoming_request_alert TORRENT_FINAL : peer_alert { // internal incoming_request_alert(aux::stack_allocator& alloc , peer_request r, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id); static const int static_category = alert::incoming_request_notification; TORRENT_DEFINE_ALERT(incoming_request_alert, 84) virtual std::string message() const TORRENT_OVERRIDE; // the request this peer sent to us peer_request req; }; struct TORRENT_EXPORT dht_log_alert TORRENT_FINAL : alert { enum dht_module_t { tracker, node, routing_table, rpc_manager, traversal }; dht_log_alert(aux::stack_allocator& alloc , dht_module_t m, char const* msg); static const int static_category = alert::dht_log_notification; TORRENT_DEFINE_ALERT(dht_log_alert, 85) virtual std::string message() const TORRENT_OVERRIDE; // the log message char const* log_message() const; // the module, or part, of the DHT that produced this log message. dht_module_t module; private: aux::stack_allocator& m_alloc; int m_msg_idx; }; // This alert is posted every time a DHT message is sent or received. It is // only posted if the ``alert::dht_log_notification`` alert category is // enabled. It contains a verbatim copy of the message. struct TORRENT_EXPORT dht_pkt_alert TORRENT_FINAL : alert { enum direction_t { incoming, outgoing }; dht_pkt_alert(aux::stack_allocator& alloc, char const* buf, int size , dht_pkt_alert::direction_t d, udp::endpoint ep); static const int static_category = alert::dht_log_notification; TORRENT_DEFINE_ALERT(dht_pkt_alert, 86) virtual std::string message() const TORRENT_OVERRIDE; // returns a pointer to the packet buffer and size of the packet, // respectively. This buffer is only valid for as long as the alert itself // is valid, which is owned by libtorrent and reclaimed whenever // pop_alerts() is called on the session. char const* pkt_buf() const; int pkt_size() const; // whether this is an incoming or outgoing packet. direction_t dir; // the DHT node we received this packet from, or sent this packet to // (depending on ``dir``). udp::endpoint node; private: aux::stack_allocator& m_alloc; int m_msg_idx; int m_size; }; struct TORRENT_EXPORT dht_get_peers_reply_alert TORRENT_FINAL : alert { dht_get_peers_reply_alert(aux::stack_allocator& alloc , sha1_hash const& ih , std::vector const& v); static const int static_category = alert::dht_operation_notification; TORRENT_DEFINE_ALERT(dht_get_peers_reply_alert, 87) virtual std::string message() const TORRENT_OVERRIDE; sha1_hash info_hash; int num_peers() const; #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED void peers(std::vector& v) const; #endif std::vector peers() const; private: aux::stack_allocator& m_alloc; int m_num_peers; int m_peers_idx; }; // This is posted exactly once for every call to session_handle::dht_direct_request. // If the request failed, response() will return a default constructed bdecode_node. struct TORRENT_EXPORT dht_direct_response_alert TORRENT_FINAL : alert { dht_direct_response_alert(aux::stack_allocator& alloc, void* userdata , udp::endpoint const& addr, bdecode_node const& response); // for when there was a timeout so we don't have a response dht_direct_response_alert(aux::stack_allocator& alloc, void* userdata , udp::endpoint const& addr); TORRENT_DEFINE_ALERT_PRIO(dht_direct_response_alert, 88, alert_priority_critical) static const int static_category = alert::dht_notification; virtual std::string message() const TORRENT_OVERRIDE; void* userdata; udp::endpoint addr; bdecode_node response() const; private: aux::stack_allocator& m_alloc; int m_response_idx; int m_response_size; }; // this is posted when one or more blocks are picked by the piece picker, // assuming the verbose piece picker logging is enabled (see // picker_log_notification). struct TORRENT_EXPORT picker_log_alert TORRENT_FINAL : peer_alert { TORRENT_DEFINE_ALERT(picker_log_alert, 89) #ifndef TORRENT_DISABLE_LOGGING // internal picker_log_alert(aux::stack_allocator& alloc, torrent_handle const& h , tcp::endpoint const& ep, peer_id const& peer_id, boost::uint32_t flags , piece_block const* blocks, int num_blocks); static const int static_category = alert::picker_log_notification; virtual std::string message() const TORRENT_OVERRIDE; #endif // TORRENT_DISABLE_LOGGING enum picker_flags_t { // the ratio of partial pieces is too high. This forces a preference // for picking blocks from partial pieces. partial_ratio = 0x1, prioritize_partials = 0x2, rarest_first_partials = 0x4, rarest_first = 0x8, reverse_rarest_first = 0x10, suggested_pieces = 0x20, prio_sequential_pieces = 0x40, sequential_pieces = 0x80, reverse_pieces = 0x100, time_critical = 0x200, random_pieces = 0x400, prefer_contiguous = 0x800, reverse_sequential = 0x1000, backup1 = 0x2000, backup2 = 0x4000, end_game = 0x8000 }; #ifndef TORRENT_DISABLE_LOGGING // this is a bitmask of which features were enabled for this particular // pick. The bits are defined in the picker_flags_t enum. boost::uint32_t picker_flags; std::vector blocks() const; private: int m_array_idx; int m_num_blocks; #endif // TORRENT_DISABLE_LOGGING }; #undef TORRENT_DEFINE_ALERT_IMPL #undef TORRENT_DEFINE_ALERT #undef TORRENT_DEFINE_ALERT_PRIO #undef TORRENT_CLONE enum { num_alert_types = 90 }; // this enum represents "max_alert_index" + 1 } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #endif libtorrent-rasterbar-1.1.13/include/libtorrent/alloca.hpp000066400000000000000000000036471351156116000235300ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_ALLOCA #include "libtorrent/config.hpp" #if defined TORRENT_WINDOWS || defined TORRENT_MINGW #include #define TORRENT_ALLOCA(t, n) static_cast(_alloca(sizeof(t) * (n))) #elif defined TORRENT_BSD #include #define TORRENT_ALLOCA(t, n) static_cast(alloca(sizeof(t) * (n))) #else #include #define TORRENT_ALLOCA(t, n) static_cast(alloca(sizeof(t) * (n))) #endif #endif libtorrent-rasterbar-1.1.13/include/libtorrent/announce_entry.hpp000066400000000000000000000151461351156116000253210ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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_ANNOUNCE_ENTRY_HPP_INCLUDED #define TORRENT_ANNOUNCE_ENTRY_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/time.hpp" // for time_point #include "libtorrent/error_code.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { namespace aux { struct session_settings; } // this class holds information about one bittorrent tracker, as it // relates to a specific torrent. struct TORRENT_EXPORT announce_entry { // constructs a tracker announce entry with ``u`` as the URL. announce_entry(std::string const& u); announce_entry(); ~announce_entry(); #if __cplusplus >= 201103L announce_entry(announce_entry const&) = default; announce_entry& operator=(announce_entry const&) = default; #endif // tracker URL as it appeared in the torrent file std::string url; // the current ``&trackerid=`` argument passed to the tracker. // this is optional and is normally empty (in which case no // trackerid is sent). std::string trackerid; // if this tracker has returned an error or warning message // that message is stored here std::string message; // if this tracker failed the last time it was contacted // this error code specifies what error occurred error_code last_error; // returns the number of seconds to the next announce on this tracker. // ``min_announce_in()`` returns the number of seconds until we are // allowed to force another tracker update with this tracker. // // If the last time this tracker was contacted failed, ``last_error`` is // the error code describing what error occurred. int next_announce_in() const; int min_announce_in() const; // the time of next tracker announce time_point next_announce; // no announces before this time time_point min_announce; // TODO: include the number of peers received from this tracker, at last // announce // these are either -1 or the scrape information this tracker last // responded with. *incomplete* is the current number of downloaders in // the swarm, *complete* is the current number of seeds in the swarm and // *downloaded* is the cumulative number of completed downloads of this // torrent, since the beginning of time (from this tracker's point of // view). // if this tracker has returned scrape data, these fields are filled in // with valid numbers. Otherwise they are set to -1. the number of // current downloaders int scrape_incomplete; int scrape_complete; int scrape_downloaded; // the tier this tracker belongs to boost::uint8_t tier; // the max number of failures to announce to this tracker in // a row, before this tracker is not used anymore. 0 means unlimited boost::uint8_t fail_limit; // the number of times in a row we have failed to announce to this // tracker. boost::uint8_t fails:7; // true while we're waiting for a response from the tracker. bool updating:1; // flags for the source bitmask, each indicating where // we heard about this tracker enum tracker_source { // the tracker was part of the .torrent file source_torrent = 1, // the tracker was added programatically via the add_troacker()_ function source_client = 2, // the tracker was part of a magnet link source_magnet_link = 4, // the tracker was received from the swarm via tracker exchange source_tex = 8 }; // a bitmask specifying which sources we got this tracker from. boost::uint8_t source:4; // set to true the first time we receive a valid response // from this tracker. bool verified:1; // set to true when we get a valid response from an announce // with event=started. If it is set, we won't send start in the subsequent // announces. bool start_sent:1; // set to true when we send a event=completed. bool complete_sent:1; // this is false the stats sent to this tracker will be 0 bool send_stats:1; // internal bool triggered_manually:1; // reset announce counters and clears the started sent flag. // The announce_entry will look like we've never talked to // the tracker. void reset(); // updates the failure counter and time-outs for re-trying. // This is called when the tracker announce fails. void failed(aux::session_settings const& sett, int retry_interval = 0); #ifndef TORRENT_NO_DEPRECATE // deprecated in 1.0 TORRENT_DEPRECATED bool will_announce(time_point now) const { return now <= next_announce && (fails < fail_limit || fail_limit == 0) && !updating; } #endif // returns true if we can announce to this tracker now. // The current time is passed in as ``now``. The ``is_seed`` // argument is necessary because once we become a seed, we // need to announce right away, even if the re-announce timer // hasn't expired yet. bool can_announce(time_point now, bool is_seed) const; // returns true if the last time we tried to announce to this // tracker succeeded, or if we haven't tried yet. bool is_working() const { return fails == 0; } // trims whitespace characters from the beginning of the URL. void trim(); }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/assert.hpp000066400000000000000000000071201351156116000235640ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_ASSERT #include "libtorrent/config.hpp" #if (defined TORRENT_DEBUG && TORRENT_USE_ASSERTS) \ || defined TORRENT_ASIO_DEBUGGING \ || defined TORRENT_PROFILE_CALLS \ || defined TORRENT_RELEASE_ASSERTS \ || defined TORRENT_DEBUG_BUFFERS #include std::string demangle(char const* name); TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth = 0 , void* ctx = NULL); #endif // this is to disable the warning of conditional expressions // being constant in msvc #ifdef _MSC_VER #define TORRENT_WHILE_0 \ __pragma( warning(push) ) \ __pragma( warning(disable:4127) ) \ while (0) \ __pragma( warning(pop) ) #else #define TORRENT_WHILE_0 while (0) #endif // declarations of the two functions // internal TORRENT_EXPORT void assert_print(char const* fmt, ...) TORRENT_FORMAT(1,2); // internal TORRENT_EXPORT void assert_fail(const char* expr, int line , char const* file, char const* function, char const* val, int kind = 0); #if TORRENT_USE_ASSERTS #ifdef TORRENT_PRODUCTION_ASSERTS extern TORRENT_EXPORT char const* libtorrent_assert_log; #endif #if TORRENT_USE_IOSTREAM #include #endif #ifndef TORRENT_USE_SYSTEM_ASSERTS #define TORRENT_ASSERT_PRECOND(x) \ do { if (x) {} else assert_fail(#x, __LINE__, __FILE__, TORRENT_FUNCTION, 0, 1); } TORRENT_WHILE_0 #define TORRENT_ASSERT(x) \ do { if (x) {} else assert_fail(#x, __LINE__, __FILE__, TORRENT_FUNCTION, 0, 0); } TORRENT_WHILE_0 #if TORRENT_USE_IOSTREAM #define TORRENT_ASSERT_VAL(x, y) \ do { if (x) {} else { std::stringstream __s__; __s__ << #y ": " << y; \ assert_fail(#x, __LINE__, __FILE__, TORRENT_FUNCTION, __s__.str().c_str(), 0); } } TORRENT_WHILE_0 #else #define TORRENT_ASSERT_VAL(x, y) TORRENT_ASSERT(x) #endif #else #include #define TORRENT_ASSERT_PRECOND(x) assert(x) #define TORRENT_ASSERT(x) assert(x) #define TORRENT_ASSERT_VAL(x, y) assert(x) #endif #else // TORRENT_USE_ASSERTS #define TORRENT_ASSERT_PRECOND(a) do {} TORRENT_WHILE_0 #define TORRENT_ASSERT(a) do {} TORRENT_WHILE_0 #define TORRENT_ASSERT_VAL(a, b) do {} TORRENT_WHILE_0 #endif // TORRENT_USE_ASSERTS #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/000077500000000000000000000000001351156116000225065ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/alert_manager_variadic_emplace.hpp000066400000000000000000000026431351156116000313550ustar00rootroot00000000000000 #if !defined BOOST_PP_IS_ITERATING || !BOOST_PP_IS_ITERATING // set-up iteration #include #include #include #include #define BOOST_PP_ITERATION_PARAMS_1 \ (3, (0, TORRENT_ALERT_MANAGER_MAX_ARITY, \ "libtorrent/aux_/alert_manager_variadic_emplace.hpp")) #include BOOST_PP_ITERATE() #else // BOOST_PP_IS_ITERATING // loop body #define I BOOST_PP_ITERATION() template void emplace_alert(BOOST_PP_ENUM_BINARY_PARAMS(I, A, const& a) ) { recursive_mutex::scoped_lock lock(m_mutex); #ifndef TORRENT_NO_DEPRECATE if (m_dispatch) { m_dispatch(std::auto_ptr(new T(m_allocations[m_generation] BOOST_PP_COMMA_IF(I) BOOST_PP_ENUM_PARAMS(I, a)))); return; } #endif // don't add more than this number of alerts, unless it's a // high priority alert, in which case we try harder to deliver it // for high priority alerts, double the upper limit if (m_alerts[m_generation].size() / (1 + T::priority) >= m_queue_size_limit) return; T alert(m_allocations[m_generation] BOOST_PP_COMMA_IF(I) BOOST_PP_ENUM_PARAMS(I, a)); m_alerts[m_generation].push_back(alert); maybe_notify(&alert); } #undef I #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/allocating_handler.hpp000066400000000000000000000105111351156116000270270ustar00rootroot00000000000000/* Copyright (c) 2015, 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_ALLOCATING_HANDLER_HPP_INCLUDED #define TORRENT_ALLOCATING_HANDLER_HPP_INCLUDED #include #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { namespace aux { // this is meant to provide the actual storage for the handler allocator. // There's only a single slot, so the allocator is only supposed to be used // for handlers where there's only a single outstanding operation at a time, // per storage object. For instance, peers only ever have one outstanding // read operation at a time, so it can reuse its storage for read handlers. template struct handler_storage { #ifdef TORRENT_DEBUG handler_storage() : used(false) {} bool used; #else handler_storage() {} #endif boost::aligned_storage bytes; private: handler_storage(handler_storage const&); }; // this class is a wrapper for an asio handler object. Its main purpose // is to pass along additional parameters to the asio handler allocator // function, as well as providing a distinct type for the handler // allocator function to overload on template struct allocating_handler { // TODO: 3 make sure the handlers we pass in are potentially movable! #if !defined BOOST_NO_CXX11_RVALUE_REFERENCES allocating_handler( Handler&& h, handler_storage& s) : handler(std::move(h)) , storage(s) {} #endif allocating_handler( Handler const& h, handler_storage& s) : handler(h) , storage(s) {} #if !defined BOOST_NO_CXX11_VARIADIC_TEMPLATES \ && !defined BOOST_NO_CXX11_RVALUE_REFERENCES template void operator()(A&&... a) const { handler(std::forward(a)...); } #else template void operator()(A0 const& a0) const { handler(a0); } template void operator()(A0 const& a0, A1 const& a1) const { handler(a0, a1); } template void operator()(A0 const& a0, A1 const& a1, A2 const& a2) const { handler(a0, a1, a2); } #endif friend void* asio_handler_allocate( std::size_t size, allocating_handler* ctx) { TORRENT_UNUSED(size); TORRENT_ASSERT(size <= Size); #ifdef TORRENT_DEBUG TORRENT_ASSERT(!ctx->storage.used); ctx->storage.used = true; #endif return &ctx->storage.bytes; } friend void asio_handler_deallocate( void* ptr, std::size_t size, allocating_handler* ctx) { TORRENT_UNUSED(ptr); TORRENT_UNUSED(size); TORRENT_UNUSED(ctx); TORRENT_ASSERT(size <= Size); TORRENT_ASSERT(ptr == &ctx->storage.bytes); #ifdef TORRENT_DEBUG ctx->storage.used = false; #endif } Handler handler; handler_storage& storage; }; } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/byteswap.hpp000066400000000000000000000047211351156116000250610ustar00rootroot00000000000000/* Copyright (c) 2013-2015, 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_BYTESWAP_HPP_INCLUDED #define TORRENT_BYTESWAP_HPP_INCLUDED // this header makes sure htonl(), nothl(), htons() and ntohs() // are available #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #ifdef TORRENT_WINDOWS #include #else // posix header // for ntohl and htonl #include #endif namespace libtorrent { namespace aux { // these need to be within the disabled warnings because on OSX // the htonl and ntohl macros cause lots of old-style case warnings inline boost::uint32_t host_to_network(boost::uint32_t x) { return htonl(x); } inline boost::uint32_t network_to_host(boost::uint32_t x) { return ntohl(x); } inline boost::uint16_t host_to_network(boost::uint16_t x) { return htons(x); } inline boost::uint16_t network_to_host(boost::uint16_t x) { return ntohs(x); } } } #include "libtorrent/aux_/disable_warnings_pop.hpp" #endif // TORRENT_BYTESWAP_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/cpuid.hpp000066400000000000000000000035001351156116000243210ustar00rootroot00000000000000/* Copyright (c) 2014, 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_CPUID_HPP_INCLUDED #define TORRENT_CPUID_HPP_INCLUDED #include "libtorrent/config.hpp" namespace libtorrent { namespace aux { // initialized by static initializers (in cpuid.cpp) TORRENT_EXTRA_EXPORT extern bool sse42_support; TORRENT_EXTRA_EXPORT extern bool mmx_support; } } #endif // TORRENT_CPUID_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/disable_warnings_pop.hpp000066400000000000000000000032051351156116000274100ustar00rootroot00000000000000/* Copyright (c) 2015, 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 _MSC_VER #pragma warning(pop) #endif #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef __GNUC__ #pragma GCC diagnostic pop #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/disable_warnings_push.hpp000066400000000000000000000071211351156116000275720ustar00rootroot00000000000000/* Copyright (c) 2015, 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 __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wall" #pragma GCC diagnostic ignored "-Wsign-conversion" #pragma GCC diagnostic ignored "-Wconversion" #pragma GCC diagnostic ignored "-Wswitch-enum" #pragma GCC diagnostic ignored "-Wold-style-cast" #pragma GCC diagnostic ignored "-Wundef" #pragma GCC diagnostic ignored "-Wmissing-noreturn" #pragma GCC diagnostic ignored "-Wdeprecated" #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #pragma GCC diagnostic ignored "-Wshadow" #pragma GCC diagnostic ignored "-Wunused-variable" #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunknown-pragmas" #pragma clang diagnostic ignored "-Wall" #pragma clang diagnostic ignored "-Weverything" #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wswitch-enum" #pragma clang diagnostic ignored "-Wcovered-switch-default" #pragma clang diagnostic ignored "-Wold-style-cast" #pragma clang diagnostic ignored "-Wundef" #pragma clang diagnostic ignored "-Wweak-vtables" #pragma clang diagnostic ignored "-Wmissing-noreturn" #pragma clang diagnostic ignored "-Wdeprecated" #pragma clang diagnostic ignored "-Wdeprecated-declarations" #pragma clang diagnostic ignored "-Wcast-align" #pragma clang diagnostic ignored "-Wweak-vtable" #pragma clang diagnostic ignored "-Wundef" #pragma clang diagnostic ignored "-Wshadow" #pragma clang diagnostic ignored "-Wimplicit-fallthrough" #pragma clang diagnostic ignored "-Wc++11-long-long" #pragma clang diagnostic ignored "-Wc++11-extensions" #pragma clang diagnostic ignored "-Wextra-semi" #pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wreserved-id-macro" #pragma clang diagnostic ignored "-Wunused-local-typedef" #pragma clang diagnostic ignored "-Wdouble-promotion" #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" #pragma clang diagnostic ignored "-Wexit-time-destructors" #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" #endif #ifdef _MSC_VER #pragma warning(push, 1) #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/escape_string.hpp000066400000000000000000000102411351156116000260430ustar00rootroot00000000000000/* Copyright (c) 2003-2014, 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 #include "libtorrent/config.hpp" #include "libtorrent/error_code.hpp" namespace libtorrent { namespace string { enum flags_t { // use lower case alphabet used with i2p lowercase = 0x1, // don't insert padding no_padding = 0x2, // shortcut used for addresses as sha256 hashes i2p = lowercase | no_padding }; } TORRENT_EXTRA_EXPORT std::string unescape_string(std::string const& s, error_code& ec); // replaces all disallowed URL characters by their %-encoding TORRENT_EXTRA_EXPORT std::string escape_string(const char* str, int len); // same as escape_string but does not encode '/' TORRENT_EXTRA_EXPORT std::string escape_path(const char* str, int len); // if the url does not appear to be encoded, and it contains illegal url characters // it will be encoded TORRENT_EXTRA_EXPORT std::string maybe_url_encode(std::string const& url); // convert a file://-URL to a proper path TORRENT_EXTRA_EXPORT std::string resolve_file_url(std::string const& url); // returns true if the given string (not null terminated) contains // characters that would need to be escaped if used in a URL TORRENT_EXTRA_EXPORT bool need_encoding(char const* str, int len); // encodes a string using the base64 scheme TORRENT_EXTRA_EXPORT std::string base64encode(std::string const& s); // encodes a string using the base32 scheme TORRENT_EXTRA_EXPORT std::string base32encode(std::string const& s, int flags=0); TORRENT_EXTRA_EXPORT std::string base32decode(std::string const& s); TORRENT_EXTRA_EXPORT std::string url_has_argument( std::string const& url, std::string argument, std::string::size_type* out_pos = 0); // replaces \ with / TORRENT_EXTRA_EXPORT void convert_path_to_posix(std::string& path); #ifdef TORRENT_WINDOWS TORRENT_EXTRA_EXPORT void convert_path_to_windows(std::string& path); #endif TORRENT_EXTRA_EXPORT std::string read_until(char const*& str, char delim , char const* end); #if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING TORRENT_EXTRA_EXPORT std::wstring convert_to_wstring(std::string const& s); TORRENT_EXTRA_EXPORT std::string convert_from_wstring(std::wstring const& s); #endif #if TORRENT_USE_ICONV || TORRENT_USE_LOCALE || defined TORRENT_WINDOWS TORRENT_EXTRA_EXPORT std::string convert_to_native(std::string const& s); TORRENT_EXTRA_EXPORT std::string convert_from_native(std::string const& s); #else // internal inline std::string const& convert_to_native(std::string const& s) { return s; } // internal inline std::string const& convert_from_native(std::string const& s) { return s; } #endif } #endif // TORRENT_ESCAPE_STRING_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/file_progress.hpp000066400000000000000000000056461351156116000260750ustar00rootroot00000000000000/* Copyright (c) 2015, 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_PROGRESS_HPP_INCLUDE #define TORRENT_FILE_PROGRESS_HPP_INCLUDE #include #include #include "libtorrent/export.hpp" #if TORRENT_USE_INVARIANT_CHECKS #include "libtorrent/invariant_check.hpp" #include "libtorrent/bitfield.hpp" #endif namespace libtorrent { class piece_picker; class file_storage; class alert_manager; struct torrent_handle; namespace aux { struct TORRENT_EXTRA_EXPORT file_progress { file_progress(); void init(piece_picker const& picker , file_storage const& fs); void export_progress(std::vector &fp); bool empty() const { return m_file_progress.empty(); } void clear(); void update(file_storage const& fs, int index , alert_manager* alerts, torrent_handle const& h); private: // this vector contains the number of bytes completely // downloaded (as in passed-hash-check) in each file. // this lets us trigger on individual files completing // the vector is allocated lazily, when file progress // is first queried by the client std::vector m_file_progress; #if TORRENT_USE_INVARIANT_CHECKS friend class libtorrent::invariant_access; void check_invariant() const; // this is used to assert we never add the same piece twice bitfield m_have_pieces; // to make sure we never say we've downloaded more bytes of a file than // its file size std::vector m_file_sizes; #endif }; } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/merkle.hpp000066400000000000000000000035511351156116000245020ustar00rootroot00000000000000/* Copyright (c) 2003-2014, 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_MERKLE_HPP_INCLUDED #define TORRENT_MERKLE_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { TORRENT_EXTRA_EXPORT int merkle_num_leafs(int); TORRENT_EXTRA_EXPORT int merkle_num_nodes(int); TORRENT_EXTRA_EXPORT int merkle_get_parent(int); TORRENT_EXTRA_EXPORT int merkle_get_sibling(int); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/openssl.hpp000066400000000000000000000056531351156116000247130ustar00rootroot00000000000000/* Copyright (c) 2015, 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_OPENSSL_HPP_INCLUDED #define TORRENT_OPENSSL_HPP_INCLUDED #ifdef TORRENT_USE_OPENSSL // all of OpenSSL causes warnings, so we just have to disable them #include "libtorrent/aux_/disable_warnings_push.hpp" #ifdef TORRENT_WINDOWS // because openssl includes winsock.h, we must include winsock2.h first #include #endif #include #include // for sk_GENERAL_NAME_value #include // for GENERAL_NAME namespace libtorrent { namespace aux { inline void openssl_set_tlsext_hostname(SSL* s, char const* name) { #if OPENSSL_VERSION_NUMBER >= 0x90812f SSL_set_tlsext_host_name(s, name); #endif } #if BOOST_VERSION >= 104700 #if OPENSSL_VERSION_NUMBER >= 0x90812f inline void openssl_set_tlsext_servername_callback(SSL_CTX* ctx , int (*servername_callback)(SSL*, int*, void*)) { SSL_CTX_set_tlsext_servername_callback(ctx, servername_callback); } inline void openssl_set_tlsext_servername_arg(SSL_CTX* ctx, void* userdata) { SSL_CTX_set_tlsext_servername_arg(ctx, userdata); } inline int openssl_num_general_names(GENERAL_NAMES* gens) { return sk_GENERAL_NAME_num(gens); } inline GENERAL_NAME* openssl_general_name_value(GENERAL_NAMES* gens, int i) { return sk_GENERAL_NAME_value(gens, i); } #endif // OPENSSL_VERSION_NUMBER #endif // BOOST_VERSION } } #include "libtorrent/aux_/disable_warnings_pop.hpp" #endif // TORRENT_USE_OPENSSL #endif // TORRENT_OPENSSL_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/proxy_settings.hpp000066400000000000000000000116711351156116000263260ustar00rootroot00000000000000/* Copyright (c) 2003-2015, 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_PROXY_SETTINGS_HPP_INCLUDED #define TORRENT_PROXY_SETTINGS_HPP_INCLUDED #include "libtorrent/version.hpp" #include "libtorrent/config.hpp" #include #include namespace libtorrent { struct settings_pack; namespace aux { struct session_settings; // The ``proxy_settings`` structs contains the information needed to // direct certain traffic to a proxy. struct TORRENT_DEPRECATED_EXPORT proxy_settings { // defaults constructs proxy settings, initializing it to the default // settings. proxy_settings(); // construct the proxy_settings object from the settings // this constructor is implemented in session_impl.cpp proxy_settings(settings_pack const& sett); proxy_settings(aux::session_settings const& sett); // the name or IP of the proxy server. ``port`` is the port number the // proxy listens to. If required, ``username`` and ``password`` can be // set to authenticate with the proxy. std::string hostname; // when using a proxy type that requires authentication, the username // and password fields must be set to the credentials for the proxy. std::string username; std::string password; #ifndef TORRENT_NO_DEPRECATE // the type of proxy to use. Assign one of these to the // proxy_settings::type field. enum proxy_type { // This is the default, no proxy server is used, all other fields are // ignored. none, // The server is assumed to be a `SOCKS4 server`_ that requires a // username. // // .. _`SOCKS4 server`: http://www.ufasoft.com/doc/socks4_protocol.htm socks4, // The server is assumed to be a SOCKS5 server (`RFC 1928`_) that does // not require any authentication. The username and password are // ignored. // // .. _`RFC 1928`: http://www.faqs.org/rfcs/rfc1928.html socks5, // The server is assumed to be a SOCKS5 server that supports plain // text username and password authentication (`RFC 1929`_). The // username and password specified may be sent to the proxy if it // requires. // // .. _`RFC 1929`: http://www.faqs.org/rfcs/rfc1929.html socks5_pw, // The server is assumed to be an HTTP proxy. If the transport used // for the connection is non-HTTP, the server is assumed to support // the CONNECT_ method. i.e. for web seeds and HTTP trackers, a plain // proxy will suffice. The proxy is assumed to not require // authorization. The username and password will not be used. // // .. _CONNECT: http://tools.ietf.org/html/draft-luotonen-web-proxy-tunneling-01 http, // The server is assumed to be an HTTP proxy that requires user // authorization. The username and password will be sent to the proxy. http_pw, // route through an i2p SAM proxy i2p_proxy }; #endif // tells libtorrent what kind of proxy server it is. See proxy_type // enum for options boost::uint8_t type; // the port the proxy server is running on boost::uint16_t port; // defaults to true. It means that hostnames should be attempted to be // resolved through the proxy instead of using the local DNS service. // This is only supported by SOCKS5 and HTTP. bool proxy_hostnames; // determines whether or not to exempt peer and web seed connections // from using the proxy. This defaults to true, i.e. peer connections are // proxied by default. bool proxy_peer_connections; // if true, tracker connections are subject to the proxy settings bool proxy_tracker_connections; }; } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/route.h000066400000000000000000000436271351156116000240310ustar00rootroot00000000000000/* * Copyright (c) 2000-2008 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)route.h 8.3 (Berkeley) 4/19/94 * $FreeBSD: src/sys/net/route.h,v 1.36.2.1 2000/08/16 06:14:23 jayanth Exp $ */ #ifndef _NET_ROUTE_H_ #define _NET_ROUTE_H_ #include #include #include #include /* * Kernel resident routing tables. * * The routing tables are initialized when interface addresses * are set by making entries for all directly connected interfaces. */ /* * A route consists of a destination address and a reference * to a routing entry. These are often held by protocols * in their control blocks, e.g. inpcb. */ #ifdef PRIVATE struct rtentry; struct route { /* * N.B: struct route must begin with ro_rt and ro_flags * because the code does some casts of a 'struct route_in6 *' * to a 'struct route *'. */ struct rtentry *ro_rt; uint32_t ro_flags; /* route flags (see below) */ struct sockaddr ro_dst; }; #define ROF_SRCIF_SELECTED 0x1 /* source interface was selected */ #else struct route; #endif /* PRIVATE */ /* * These numbers are used by reliable protocols for determining * retransmission behavior and are included in the routing structure. */ struct rt_metrics { u_int32_t rmx_locks; /* Kernel must leave these values alone */ u_int32_t rmx_mtu; /* MTU for this path */ u_int32_t rmx_hopcount; /* max hops expected */ int32_t rmx_expire; /* lifetime for route, e.g. redirect */ u_int32_t rmx_recvpipe; /* inbound delay-bandwidth product */ u_int32_t rmx_sendpipe; /* outbound delay-bandwidth product */ u_int32_t rmx_ssthresh; /* outbound gateway buffer limit */ u_int32_t rmx_rtt; /* estimated round trip time */ u_int32_t rmx_rttvar; /* estimated rtt variance */ u_int32_t rmx_pksent; /* packets sent using this route */ u_int32_t rmx_filler[4]; /* will be used for T/TCP later */ }; /* * rmx_rtt and rmx_rttvar are stored as microseconds; */ #define RTM_RTTUNIT 1000000 /* units for rtt, rttvar, as units per sec */ /* * We distinguish between routes to hosts and routes to networks, * preferring the former if available. For each route we infer * the interface to use from the gateway address supplied when * the route was entered. Routes that forward packets through * gateways are marked so that the output routines know to address the * gateway rather than the ultimate destination. */ #ifdef KERNEL_PRIVATE #include #ifndef RNF_NORMAL #include #endif /* * Kernel routing entry structure (private). */ struct rtentry { struct radix_node rt_nodes[2]; /* tree glue, and other values */ #define rt_key(r) ((struct sockaddr *)((r)->rt_nodes->rn_key)) #define rt_mask(r) ((struct sockaddr *)((r)->rt_nodes->rn_mask)) struct sockaddr *rt_gateway; /* value */ int32_t rt_refcnt; /* # held references */ uint32_t rt_flags; /* up/down?, host/net */ struct ifnet *rt_ifp; /* the answer: interface to use */ struct ifaddr *rt_ifa; /* the answer: interface addr to use */ struct sockaddr *rt_genmask; /* for generation of cloned routes */ void *rt_llinfo; /* pointer to link level info cache */ void (*rt_llinfo_free)(void *); /* link level info free function */ struct rt_metrics rt_rmx; /* metrics used by rx'ing protocols */ struct rtentry *rt_gwroute; /* implied entry for gatewayed routes */ struct rtentry *rt_parent; /* cloning parent of this route */ uint32_t generation_id; /* route generation id */ /* * See bsd/net/route.c for synchronization notes. */ decl_lck_mtx_data(, rt_lock); /* lock for routing entry */ }; #endif /* KERNEL_PRIVATE */ #ifdef KERNEL_PRIVATE #define rt_use rt_rmx.rmx_pksent #endif /* KERNEL_PRIVATE */ #define RTF_UP 0x1 /* route usable */ #define RTF_GATEWAY 0x2 /* destination is a gateway */ #define RTF_HOST 0x4 /* host entry (net otherwise) */ #define RTF_REJECT 0x8 /* host or net unreachable */ #define RTF_DYNAMIC 0x10 /* created dynamically (by redirect) */ #define RTF_MODIFIED 0x20 /* modified dynamically (by redirect) */ #define RTF_DONE 0x40 /* message confirmed */ #define RTF_DELCLONE 0x80 /* delete cloned route */ #define RTF_CLONING 0x100 /* generate new routes on use */ #define RTF_XRESOLVE 0x200 /* external daemon resolves name */ #define RTF_LLINFO 0x400 /* generated by link layer (e.g. ARP) */ #define RTF_STATIC 0x800 /* manually added */ #define RTF_BLACKHOLE 0x1000 /* just discard pkts (during updates) */ #define RTF_PROTO2 0x4000 /* protocol specific routing flag */ #define RTF_PROTO1 0x8000 /* protocol specific routing flag */ #define RTF_PRCLONING 0x10000 /* protocol requires cloning */ #define RTF_WASCLONED 0x20000 /* route generated through cloning */ #define RTF_PROTO3 0x40000 /* protocol specific routing flag */ /* 0x80000 unused */ #define RTF_PINNED 0x100000 /* future use */ #define RTF_LOCAL 0x200000 /* route represents a local address */ #define RTF_BROADCAST 0x400000 /* route represents a bcast address */ #define RTF_MULTICAST 0x800000 /* route represents a mcast address */ #define RTF_IFSCOPE 0x1000000 /* has valid interface scope */ #define RTF_CONDEMNED 0x2000000 /* defunct; no longer modifiable */ /* 0x4000000 and up unassigned */ /* * Routing statistics. */ struct rtstat { short rts_badredirect; /* bogus redirect calls */ short rts_dynamic; /* routes created by redirects */ short rts_newgateway; /* routes modified by redirects */ short rts_unreach; /* lookups which failed */ short rts_wildcard; /* lookups satisfied by a wildcard */ }; /* * Structures for routing messages. */ struct rt_msghdr { u_short rtm_msglen; /* to skip over non-understood messages */ u_char rtm_version; /* future binary compatibility */ u_char rtm_type; /* message type */ u_short rtm_index; /* index for associated ifp */ int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ int rtm_addrs; /* bitmask identifying sockaddrs in msg */ pid_t rtm_pid; /* identify sender */ int rtm_seq; /* for sender to identify action */ int rtm_errno; /* why failed */ int rtm_use; /* from rtentry */ u_int32_t rtm_inits; /* which metrics we are initializing */ struct rt_metrics rtm_rmx; /* metrics themselves */ }; struct rt_msghdr2 { u_short rtm_msglen; /* to skip over non-understood messages */ u_char rtm_version; /* future binary compatibility */ u_char rtm_type; /* message type */ u_short rtm_index; /* index for associated ifp */ int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ int rtm_addrs; /* bitmask identifying sockaddrs in msg */ int32_t rtm_refcnt; /* reference count */ int rtm_parentflags; /* flags of the parent route */ int rtm_reserved; /* reserved field set to 0 */ int rtm_use; /* from rtentry */ u_int32_t rtm_inits; /* which metrics we are initializing */ struct rt_metrics rtm_rmx; /* metrics themselves */ }; #define RTM_VERSION 5 /* Up the ante and ignore older versions */ /* * Message types. */ #define RTM_ADD 0x1 /* Add Route */ #define RTM_DELETE 0x2 /* Delete Route */ #define RTM_CHANGE 0x3 /* Change Metrics or flags */ #define RTM_GET 0x4 /* Report Metrics */ #define RTM_LOSING 0x5 /* Kernel Suspects Partitioning */ #define RTM_REDIRECT 0x6 /* Told to use different route */ #define RTM_MISS 0x7 /* Lookup failed on this address */ #define RTM_LOCK 0x8 /* fix specified metrics */ #define RTM_OLDADD 0x9 /* caused by SIOCADDRT */ #define RTM_OLDDEL 0xa /* caused by SIOCDELRT */ #define RTM_RESOLVE 0xb /* req to resolve dst to LL addr */ #define RTM_NEWADDR 0xc /* address being added to iface */ #define RTM_DELADDR 0xd /* address being removed from iface */ #define RTM_IFINFO 0xe /* iface going up/down etc. */ #define RTM_NEWMADDR 0xf /* mcast group membership being added to if */ #define RTM_DELMADDR 0x10 /* mcast group membership being deleted */ #ifdef PRIVATE #define RTM_GET_SILENT 0x11 #endif /* PRIVATE */ #define RTM_IFINFO2 0x12 /* */ #define RTM_NEWMADDR2 0x13 /* */ #define RTM_GET2 0x14 /* */ /* * Bitmask values for rtm_inits and rmx_locks. */ #define RTV_MTU 0x1 /* init or lock _mtu */ #define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */ #define RTV_EXPIRE 0x4 /* init or lock _expire */ #define RTV_RPIPE 0x8 /* init or lock _recvpipe */ #define RTV_SPIPE 0x10 /* init or lock _sendpipe */ #define RTV_SSTHRESH 0x20 /* init or lock _ssthresh */ #define RTV_RTT 0x40 /* init or lock _rtt */ #define RTV_RTTVAR 0x80 /* init or lock _rttvar */ /* * Bitmask values for rtm_addrs. */ #define RTA_DST 0x1 /* destination sockaddr present */ #define RTA_GATEWAY 0x2 /* gateway sockaddr present */ #define RTA_NETMASK 0x4 /* netmask sockaddr present */ #define RTA_GENMASK 0x8 /* cloning mask sockaddr present */ #define RTA_IFP 0x10 /* interface name sockaddr present */ #define RTA_IFA 0x20 /* interface addr sockaddr present */ #define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */ #define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */ /* * Index offsets for sockaddr array for alternate internal encoding. */ #define RTAX_DST 0 /* destination sockaddr present */ #define RTAX_GATEWAY 1 /* gateway sockaddr present */ #define RTAX_NETMASK 2 /* netmask sockaddr present */ #define RTAX_GENMASK 3 /* cloning mask sockaddr present */ #define RTAX_IFP 4 /* interface name sockaddr present */ #define RTAX_IFA 5 /* interface addr sockaddr present */ #define RTAX_AUTHOR 6 /* sockaddr for author of redirect */ #define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */ #define RTAX_MAX 8 /* size of array to allocate */ struct rt_addrinfo { int rti_addrs; struct sockaddr *rti_info[RTAX_MAX]; }; struct route_cb { int ip_count; int ip6_count; int ipx_count; int ns_count; int iso_count; int any_count; }; #ifdef PRIVATE /* * For scoped routing; a zero interface scope value means nil/no scope. */ #define IFSCOPE_NONE 0 #endif /* PRIVATE */ #ifdef KERNEL_PRIVATE /* * Generic call trace used by some subsystems (e.g. route, ifaddr) */ #define CTRACE_STACK_SIZE 8 /* depth of stack trace */ #define CTRACE_HIST_SIZE 4 /* refcnt history size */ typedef struct ctrace { void *th; /* thread ptr */ void *pc[CTRACE_STACK_SIZE]; /* PC stack trace */ } ctrace_t; extern void ctrace_record(ctrace_t *); #define RT_LOCK_ASSERT_HELD(_rt) \ lck_mtx_assert(&(_rt)->rt_lock, LCK_MTX_ASSERT_OWNED) #define RT_LOCK_ASSERT_NOTHELD(_rt) \ lck_mtx_assert(&(_rt)->rt_lock, LCK_MTX_ASSERT_NOTOWNED) #define RT_LOCK(_rt) do { \ if (!rte_debug) \ lck_mtx_lock(&(_rt)->rt_lock); \ else \ rt_lock(_rt, FALSE); \ } while (0) #define RT_LOCK_SPIN(_rt) do { \ if (!rte_debug) \ lck_mtx_lock_spin(&(_rt)->rt_lock); \ else \ rt_lock(_rt, TRUE); \ } while (0) #define RT_CONVERT_LOCK(_rt) do { \ RT_LOCK_ASSERT_HELD(_rt); \ lck_mtx_convert_spin(&(_rt)->rt_lock); \ } while (0) #define RT_UNLOCK(_rt) do { \ if (!rte_debug) \ lck_mtx_unlock(&(_rt)->rt_lock); \ else \ rt_unlock(_rt); \ } while (0) #define RT_ADDREF_LOCKED(_rt) do { \ if (!rte_debug) { \ RT_LOCK_ASSERT_HELD(_rt); \ if (++(_rt)->rt_refcnt == 0) \ panic("RT_ADDREF(%p) bad refcnt\n", _rt); \ } else { \ rtref(_rt); \ } \ } while (0) /* * Spin variant mutex is used here; caller is responsible for * converting any previously-held similar lock to full mutex. */ #define RT_ADDREF(_rt) do { \ RT_LOCK_SPIN(_rt); \ RT_ADDREF_LOCKED(_rt); \ RT_UNLOCK(_rt); \ } while (0) #define RT_REMREF_LOCKED(_rt) do { \ if (!rte_debug) { \ RT_LOCK_ASSERT_HELD(_rt); \ if ((_rt)->rt_refcnt == 0) \ panic("RT_REMREF(%p) bad refcnt\n", _rt); \ --(_rt)->rt_refcnt; \ } else { \ (void) rtunref(_rt); \ } \ } while (0) /* * Spin variant mutex is used here; caller is responsible for * converting any previously-held similar lock to full mutex. */ #define RT_REMREF(_rt) do { \ RT_LOCK_SPIN(_rt); \ RT_REMREF_LOCKED(_rt); \ RT_UNLOCK(_rt); \ } while (0) #define RTFREE(_rt) rtfree(_rt) #define RTFREE_LOCKED(_rt) rtfree_locked(_rt) extern struct route_cb route_cb; extern struct radix_node_head *rt_tables[AF_MAX+1]; __private_extern__ lck_mtx_t *rnh_lock; __private_extern__ int use_routegenid; __private_extern__ uint32_t route_generation; __private_extern__ int rttrash; __private_extern__ unsigned int rte_debug; struct ifmultiaddr; struct proc; extern void route_init(void) __attribute__((section("__TEXT, initcode"))); extern void routegenid_update(void); extern void rt_ifmsg(struct ifnet *); extern void rt_missmsg(int, struct rt_addrinfo *, int, int); extern void rt_newaddrmsg(int, struct ifaddr *, int, struct rtentry *); extern void rt_newmaddrmsg(int, struct ifmultiaddr *); extern int rt_setgate(struct rtentry *, struct sockaddr *, struct sockaddr *); extern void set_primary_ifscope(unsigned int); extern unsigned int get_primary_ifscope(void); extern boolean_t rt_inet_default(struct rtentry *, struct sockaddr *); extern struct rtentry *rt_lookup(boolean_t, struct sockaddr *, struct sockaddr *, struct radix_node_head *, unsigned int); extern void rtalloc(struct route *); extern void rtalloc_ign(struct route *, uint32_t); extern void rtalloc_ign_locked(struct route *, uint32_t); extern void rtalloc_scoped_ign(struct route *, uint32_t, unsigned int); extern void rtalloc_scoped_ign_locked(struct route *, uint32_t, unsigned int); extern struct rtentry *rtalloc1(struct sockaddr *, int, uint32_t); extern struct rtentry *rtalloc1_locked(struct sockaddr *, int, uint32_t); extern struct rtentry *rtalloc1_scoped(struct sockaddr *, int, uint32_t, unsigned int); extern struct rtentry *rtalloc1_scoped_locked(struct sockaddr *, int, uint32_t, unsigned int); extern void rtfree(struct rtentry *); extern void rtfree_locked(struct rtentry *); extern void rtref(struct rtentry *); /* * rtunref will decrement the refcount, rtfree will decrement and free if * the refcount has reached zero and the route is not up. * Unless you have good reason to do otherwise, use rtfree. */ extern int rtunref(struct rtentry *); extern void rtsetifa(struct rtentry *, struct ifaddr *); extern int rtinit(struct ifaddr *, int, int); extern int rtinit_locked(struct ifaddr *, int, int); extern int rtioctl(unsigned long, caddr_t, struct proc *); extern void rtredirect(struct ifnet *, struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct sockaddr *, struct rtentry **); extern int rtrequest(int, struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct rtentry **); extern int rtrequest_locked(int, struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct rtentry **); extern int rtrequest_scoped_locked(int, struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct rtentry **, unsigned int); extern unsigned int sa_get_ifscope(struct sockaddr *); extern void rt_lock(struct rtentry *, boolean_t); extern void rt_unlock(struct rtentry *); extern struct sockaddr *rtm_scrub_ifscope(int, struct sockaddr *, struct sockaddr *, struct sockaddr_storage *); #endif /* KERNEL_PRIVATE */ #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/session_call.hpp000066400000000000000000000065431351156116000257050ustar00rootroot00000000000000/* Copyright (c) 2014, Arvid Norberg, Steven Siloti 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_CALL_HPP_INCLUDED #define TORRENT_SESSION_CALL_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/aux_/session_impl.hpp" #include namespace libtorrent { namespace aux { void blocking_call(); void dump_call_profile(); void fun_wrap(bool& done, condition_variable& e, mutex& m, boost::function f); template void fun_ret(R& ret, bool& done, condition_variable& e, mutex& m, boost::function f) { ret = f(); mutex::scoped_lock l(m); done = true; e.notify_all(); } void torrent_wait(bool& done, aux::session_impl& ses); void sync_call(aux::session_impl& ses, boost::function f); template void sync_call_handle(Handle& h, boost::function f) { bool done = false; session_impl& ses = static_cast(h->session()); ses.get_io_service().dispatch(boost::bind(&aux::fun_wrap , boost::ref(done) , boost::ref(ses.cond) , boost::ref(ses.mut), f)); h.reset(); aux::torrent_wait(done, ses); } template Ret sync_call_ret(aux::session_impl& ses, boost::function f) { bool done = false; Ret r; ses.get_io_service().dispatch(boost::bind(&fun_ret , boost::ref(r) , boost::ref(done) , boost::ref(ses.cond) , boost::ref(ses.mut) , f)); torrent_wait(done, ses); return r; } template void sync_call_ret_handle(Handle& h, Ret& r, boost::function f) { bool done = false; session_impl& ses = static_cast(h->session()); ses.get_io_service().dispatch(boost::bind(&aux::fun_ret , boost::ref(r) , boost::ref(done) , boost::ref(ses.cond) , boost::ref(ses.mut) , f)); h.reset(); aux::torrent_wait(done, ses); } } } // namespace aux namespace libtorrent #endif // TORRENT_SESSION_CALL_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/session_impl.hpp000066400000000000000000001316461351156116000257360ustar00rootroot00000000000000/* 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 "libtorrent/config.hpp" #include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/aux_/session_interface.hpp" #include "libtorrent/uncork_interface.hpp" #include "libtorrent/linked_list.hpp" #include "libtorrent/torrent_peer.hpp" #include "libtorrent/torrent_peer_allocator.hpp" #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include // for va_start, va_end #if TORRENT_HAS_BOOST_UNORDERED #include #endif #include #ifdef TORRENT_USE_OPENSSL #include "libtorrent/ssl_stream.hpp" #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/session.hpp" // for user_load_function_t #include "libtorrent/ip_voter.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/tracker_manager.hpp" #include "libtorrent/debug.hpp" #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/session_status.hpp" #include "libtorrent/add_torrent_params.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/file_pool.hpp" #include "libtorrent/bandwidth_manager.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/disk_io_thread.hpp" #include "libtorrent/udp_socket.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/alert_manager.hpp" // for alert_manager #include "libtorrent/deadline_timer.hpp" #include "libtorrent/socket_io.hpp" // for print_address #include "libtorrent/address.hpp" #include "libtorrent/utp_socket_manager.hpp" #include "libtorrent/bloom_filter.hpp" #include "libtorrent/rss.hpp" #include "libtorrent/peer_class.hpp" #include "libtorrent/disk_io_job.hpp" // block_cache_reference #include "libtorrent/network_thread_pool.hpp" #include "libtorrent/peer_class_type_filter.hpp" #include "libtorrent/kademlia/dht_observer.hpp" #include "libtorrent/resolver.hpp" #include "libtorrent/invariant_check.hpp" #if TORRENT_COMPLETE_TYPES_REQUIRED #include "libtorrent/peer_connection.hpp" #endif namespace libtorrent { struct plugin; class upnp; class natpmp; class lsd; class torrent; class alert; struct cache_info; struct torrent_handle; namespace dht { struct dht_tracker; class item; } struct bencode_map_entry; typedef boost::function dht_extension_handler_t; struct listen_socket_t { listen_socket_t(): external_port(0), ssl(false) {} // this is typically empty but can be set // to the WAN IP address of NAT-PMP or UPnP router address external_address; // this is typically set to the same as the local // listen port. In case a NAT port forward was // successfully opened, this will be set to the // port that is open on the external (NAT) interface // on the NAT box itself. This is the port that has // to be published to peers, since this is the port // the client is reachable through. int external_port; // set to true if this is an SSL listen socket bool ssl; // the actual socket boost::shared_ptr sock; }; namespace aux { struct session_impl; struct session_settings; #ifndef TORRENT_DISABLE_LOGGING struct tracker_logger; #endif // this is the link between the main thread and the // thread started to run the main downloader loop struct TORRENT_EXTRA_EXPORT session_impl TORRENT_FINAL : session_interface , dht::dht_observer , boost::noncopyable , udp_socket_observer , uncork_interface , single_threaded { // the size of each allocation that is chained in the send buffer enum { send_buffer_size_impl = 128 }; // maximum length of query names which can be registered by extensions enum { max_dht_query_length = 15 }; #if TORRENT_USE_INVARIANT_CHECKS friend class libtorrent::invariant_access; #endif typedef std::set > connection_map; #if TORRENT_HAS_BOOST_UNORDERED typedef boost::unordered_map > torrent_map; #else typedef std::map > torrent_map; #endif session_impl(io_service& ios, settings_pack const& pack); virtual ~session_impl(); void start_session(); void set_load_function(user_load_function_t fun) { m_user_load_torrent = fun; } void init_peer_class_filter(bool unlimited_local); #ifndef TORRENT_DISABLE_EXTENSIONS void add_extension(boost::function( torrent_handle const&, void*)> ext); void add_ses_extension(boost::shared_ptr ext); #endif #if TORRENT_USE_ASSERTS bool has_peer(peer_connection const* p) const TORRENT_OVERRIDE; bool any_torrent_has_peer(peer_connection const* p) const TORRENT_OVERRIDE; bool is_single_thread() const TORRENT_OVERRIDE { return single_threaded::is_single_thread(); } bool is_posting_torrent_updates() const TORRENT_OVERRIDE { return m_posting_torrent_updates; } // this is set while the session is building the // torrent status update message bool m_posting_torrent_updates; #endif void open_listen_port(); torrent_peer_allocator_interface& get_peer_allocator() TORRENT_OVERRIDE { return m_peer_allocator; } io_service& get_io_service() TORRENT_OVERRIDE { return m_io_service; } resolver_interface& get_resolver() TORRENT_OVERRIDE { return m_host_resolver; } void async_resolve(std::string const& host, int flags , callback_t const& h) TORRENT_OVERRIDE; std::vector& torrent_list(int i) TORRENT_OVERRIDE { TORRENT_ASSERT(i >= 0); TORRENT_ASSERT(i < session_interface::num_torrent_lists); return m_torrent_lists[i]; } // prioritize this torrent to be allocated some connection // attempts, because this torrent needs more peers. // this is typically done when a torrent starts out and // need the initial push to connect peers void prioritize_connections(boost::weak_ptr t) TORRENT_OVERRIDE; // if we are listening on an IPv6 interface // this will return one of the IPv6 addresses on this // machine, otherwise just an empty endpoint boost::optional get_ipv6_interface() const TORRENT_OVERRIDE; boost::optional get_ipv4_interface() const TORRENT_OVERRIDE; void async_accept(boost::shared_ptr const& listener, bool ssl); void on_accept_connection(boost::shared_ptr const& s , boost::weak_ptr listener, error_code const& e, bool ssl); void incoming_connection(boost::shared_ptr const& s); #ifndef TORRENT_NO_DEPRECATE feed_handle add_feed(feed_settings const& feed); void remove_feed(feed_handle h); void get_feeds(std::vector* f) const; #endif boost::weak_ptr find_torrent(sha1_hash const& info_hash) const TORRENT_OVERRIDE; boost::weak_ptr find_torrent(std::string const& uuid) const; #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS std::vector > find_collection( std::string const& collection) const TORRENT_OVERRIDE; #endif boost::weak_ptr find_disconnect_candidate_torrent() const TORRENT_OVERRIDE; int num_torrents() const TORRENT_OVERRIDE { return int(m_torrents.size()); } void insert_torrent(sha1_hash const& ih, boost::shared_ptr const& t , std::string uuid) TORRENT_OVERRIDE; void insert_uuid_torrent(std::string uuid, boost::shared_ptr const& t) TORRENT_OVERRIDE { m_uuids.insert(std::make_pair(uuid, t)); } boost::shared_ptr delay_load_torrent(sha1_hash const& info_hash , peer_connection* pc) TORRENT_OVERRIDE; void set_queue_position(torrent* t, int p) TORRENT_OVERRIDE; void close_connection(peer_connection* p, error_code const& ec) TORRENT_OVERRIDE; #ifndef TORRENT_NO_DEPRECATE void set_settings(libtorrent::session_settings const& s); libtorrent::session_settings deprecated_settings() const; #endif void apply_settings_pack(boost::shared_ptr pack) TORRENT_OVERRIDE; void apply_settings_pack_impl(settings_pack const& pack); session_settings const& settings() const TORRENT_OVERRIDE { return m_settings; } settings_pack get_settings() const; #ifndef TORRENT_DISABLE_DHT dht::dht_tracker* dht() TORRENT_OVERRIDE { return m_dht.get(); } bool announce_dht() const TORRENT_OVERRIDE { return !m_listen_sockets.empty(); } void add_dht_node_name(std::pair const& node); void add_dht_node(udp::endpoint n) TORRENT_OVERRIDE; void add_dht_router(std::pair const& node); void set_dht_settings(dht_settings const& s); dht_settings const& get_dht_settings() const { return m_dht_settings; } void set_dht_storage(dht::dht_storage_constructor_type sc); void start_dht(); void stop_dht(); void start_dht(entry const& startup_state); bool has_dht() const TORRENT_OVERRIDE; // this is called for torrents when they are started // it will prioritize them for announcing to // the DHT, to get the initial peers quickly void prioritize_dht(boost::weak_ptr t) TORRENT_OVERRIDE; void get_immutable_callback(sha1_hash target , dht::item const& i); void get_mutable_callback(dht::item const& i, bool); void dht_get_immutable_item(sha1_hash const& target); void dht_get_mutable_item(boost::array key , std::string salt = std::string()); void dht_put_immutable_item(entry const& data, sha1_hash target); void dht_put_mutable_item(boost::array key , boost::function& , boost::uint64_t&, std::string const&)> cb , std::string salt = std::string()); void dht_get_peers(sha1_hash const& info_hash); void dht_announce(sha1_hash const& info_hash, int port = 0, int flags = 0); void dht_direct_request(udp::endpoint ep, entry& e, void* userdata = 0); #ifndef TORRENT_NO_DEPRECATE entry dht_state() const; void start_dht_deprecated(entry const& startup_state); #endif void on_dht_announce(error_code const& e); void on_dht_name_lookup(error_code const& e , std::vector
const& addresses, int port); void on_dht_router_name_lookup(error_code const& e , std::vector
const& addresses, int port); #endif void maybe_update_udp_mapping(int nat, bool ssl, int local_port, int external_port); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) torrent const* find_encrypted_torrent( sha1_hash const& info_hash, sha1_hash const& xor_mask) TORRENT_OVERRIDE; void add_obfuscated_hash(sha1_hash const& obfuscated, boost::weak_ptr const& t) TORRENT_OVERRIDE; #endif void on_port_map_log(char const* msg, int map_transport); void on_lsd_announce(error_code const& e); #ifndef TORRENT_DISABLE_LOGGING void on_lsd_log(char const* log); #endif // called when a port mapping is successful, or a router returns // a failure to map a port void on_port_mapping(int mapping, address const& ip, int port , int protocol, error_code const& ec, int nat_transport); bool is_aborted() const TORRENT_OVERRIDE { return m_abort; } bool is_paused() const TORRENT_OVERRIDE { return m_paused; } void pause(); void resume(); void set_ip_filter(boost::shared_ptr const& f); ip_filter const& get_ip_filter(); void set_port_filter(port_filter const& f); port_filter const& get_port_filter() const TORRENT_OVERRIDE; void ban_ip(address addr) TORRENT_OVERRIDE; void queue_tracker_request(tracker_request& req , boost::weak_ptr c) TORRENT_OVERRIDE; // ==== peer class operations ==== // implements session_interface void set_peer_classes(peer_class_set* s, address const& a, int st) TORRENT_OVERRIDE; peer_class_pool const& peer_classes() const TORRENT_OVERRIDE { return m_classes; } peer_class_pool& peer_classes() TORRENT_OVERRIDE { return m_classes; } bool ignore_unchoke_slots_set(peer_class_set const& set) const TORRENT_OVERRIDE; int copy_pertinent_channels(peer_class_set const& set , int channel, bandwidth_channel** dst, int max) TORRENT_OVERRIDE; int use_quota_overhead(peer_class_set& set, int amount_down, int amount_up) TORRENT_OVERRIDE; bool use_quota_overhead(bandwidth_channel* ch, int amount); int create_peer_class(char const* name); void delete_peer_class(int cid); void set_peer_class_filter(ip_filter const& f); ip_filter const& get_peer_class_filter() const; void set_peer_class_type_filter(peer_class_type_filter f); peer_class_type_filter get_peer_class_type_filter(); peer_class_info get_peer_class(int cid); void set_peer_class(int cid, peer_class_info const& pci); bool is_listening() const; #ifndef TORRENT_DISABLE_EXTENSIONS void add_extensions_to_torrent( boost::shared_ptr const& torrent_ptr, void* userdata); #endif torrent_handle add_torrent(add_torrent_params const&, error_code& ec); // second return value is true if the torrent was added and false if an // existing one was found. std::pair, bool> add_torrent_impl(add_torrent_params& p, error_code& ec); void async_add_torrent(add_torrent_params* params); void on_async_load_torrent(disk_io_job const* j); void remove_torrent(torrent_handle const& h, int options) TORRENT_OVERRIDE; void remove_torrent_impl(boost::shared_ptr tptr, int options) TORRENT_OVERRIDE; void get_torrent_status(std::vector* ret , boost::function const& pred , boost::uint32_t flags) const; void refresh_torrent_status(std::vector* ret , boost::uint32_t flags) const; void post_torrent_updates(boost::uint32_t flags); void post_session_stats(); void post_dht_stats(); std::vector get_torrents() const; void pop_alerts(std::vector* alerts); alert* wait_for_alert(time_duration max_wait); #ifndef TORRENT_NO_DEPRECATE void pop_alerts(); alert const* pop_alert(); void pop_alerts(std::deque* alerts); size_t set_alert_queue_size_limit(size_t queue_size_limit_); int upload_rate_limit() const; int download_rate_limit() const; int local_upload_rate_limit() const; int local_download_rate_limit() const; void set_local_download_rate_limit(int bytes_per_second); void set_local_upload_rate_limit(int bytes_per_second); void set_download_rate_limit(int bytes_per_second); void set_upload_rate_limit(int bytes_per_second); void set_max_connections(int limit); void set_max_uploads(int limit); int max_connections() const; int max_uploads() const; #endif bandwidth_manager* get_bandwidth_manager(int channel) TORRENT_OVERRIDE; int upload_rate_limit(peer_class_t c) const; int download_rate_limit(peer_class_t c) const; void set_upload_rate_limit(peer_class_t c, int limit); void set_download_rate_limit(peer_class_t c, int limit); void set_rate_limit(peer_class_t c, int channel, int limit); int rate_limit(peer_class_t c, int channel) const; bool preemptive_unchoke() const TORRENT_OVERRIDE; // deprecated, use stats counters ``num_peers_up_unchoked`` instead int num_uploads() const TORRENT_OVERRIDE { return int(m_stats_counters[counters::num_peers_up_unchoked]); } // deprecated, use stats counters ``num_peers_connected`` + // ``num_peers_half_open`` instead. int num_connections() const TORRENT_OVERRIDE { return int(m_connections.size()); } int peak_up_rate() const { return m_peak_up_rate; } void trigger_unchoke() TORRENT_OVERRIDE { TORRENT_ASSERT(is_single_thread()); m_unchoke_time_scaler = 0; } void trigger_optimistic_unchoke() TORRENT_OVERRIDE { TORRENT_ASSERT(is_single_thread()); m_optimistic_unchoke_time_scaler = 0; } #ifndef TORRENT_NO_DEPRECATE session_status status() const; peer_id deprecated_get_peer_id() const; #endif void set_key(int key); address listen_address() const; boost::uint16_t listen_port() const TORRENT_OVERRIDE; boost::uint16_t ssl_listen_port() const TORRENT_OVERRIDE; alert_manager& alerts() TORRENT_OVERRIDE { return m_alerts; } disk_interface& disk_thread() TORRENT_OVERRIDE { return m_disk_thread; } void abort(); void abort_stage2(); torrent_handle find_torrent_handle(sha1_hash const& info_hash); void announce_lsd(sha1_hash const& ih, int port, bool broadcast = false) TORRENT_OVERRIDE; void save_state(entry* e, boost::uint32_t flags) const; void load_state(bdecode_node const* e, boost::uint32_t flags); bool has_connection(peer_connection* p) const TORRENT_OVERRIDE; void insert_peer(boost::shared_ptr const& c) TORRENT_OVERRIDE; proxy_settings proxy() const TORRENT_OVERRIDE; #ifndef TORRENT_DISABLE_DHT bool is_dht_running() const { return (m_dht.get() != NULL); } int external_udp_port() const TORRENT_OVERRIDE { return m_external_udp_port; } #endif #if TORRENT_USE_I2P char const* i2p_session() const TORRENT_OVERRIDE { return m_i2p_conn.session_id(); } proxy_settings i2p_proxy() const TORRENT_OVERRIDE; void on_i2p_open(error_code const& ec); void open_new_incoming_i2p_connection(); void on_i2p_accept(boost::shared_ptr const& s , error_code const& e); #endif void start_lsd(); natpmp* start_natpmp(); upnp* start_upnp(); void stop_lsd(); void stop_natpmp(); void stop_upnp(); int add_port_mapping(int t, int external_port , int local_port); void delete_port_mapping(int handle); int next_port() const; // load the specified torrent, also // pick the least recently used torrent and unload it, unless // t is the least recently used, then the next least recently // used is picked // returns true if the torrent was loaded successfully bool load_torrent(torrent* t) TORRENT_OVERRIDE; // bump t to the top of the list of least recently used. i.e. // make it the most recently used. This is done every time // an action is performed that required the torrent to be // loaded, indicating activity void bump_torrent(torrent* t, bool back = true) TORRENT_OVERRIDE; // evict torrents until there's space for one new torrent, void evict_torrents_except(torrent* ignore); void evict_torrent(torrent* t) TORRENT_OVERRIDE; void deferred_submit_jobs() TORRENT_OVERRIDE; char* allocate_buffer() TORRENT_OVERRIDE; torrent_peer* allocate_peer_entry(int type); void free_peer_entry(torrent_peer* p); void free_buffer(char* buf) TORRENT_OVERRIDE; int send_buffer_size() const TORRENT_OVERRIDE { return send_buffer_size_impl; } // implements buffer_allocator_interface void free_disk_buffer(char* buf) TORRENT_OVERRIDE; char* allocate_disk_buffer(char const* category) TORRENT_OVERRIDE; char* allocate_disk_buffer(bool& exceeded , boost::shared_ptr o , char const* category) TORRENT_OVERRIDE; void reclaim_block(block_cache_reference ref) TORRENT_OVERRIDE; bool exceeded_cache_use() const { return m_disk_thread.exceeded_cache_use(); } // implements dht_observer virtual void set_external_address(address const& ip , address const& source) TORRENT_OVERRIDE; virtual address external_address() TORRENT_OVERRIDE; virtual void get_peers(sha1_hash const& ih) TORRENT_OVERRIDE; virtual void announce(sha1_hash const& ih, address const& addr, int port) TORRENT_OVERRIDE; virtual void outgoing_get_peers(sha1_hash const& target , sha1_hash const& sent_target, udp::endpoint const& ep) TORRENT_OVERRIDE; #ifndef TORRENT_DISABLE_LOGGING virtual void log(libtorrent::dht::dht_logger::module_t m, char const* fmt, ...) TORRENT_OVERRIDE TORRENT_FORMAT(3,4); virtual void log_packet(message_direction_t dir, char const* pkt, int len , udp::endpoint node) TORRENT_OVERRIDE; #endif virtual bool on_dht_request(char const* query, int query_len , dht::msg const& request, entry& response) TORRENT_OVERRIDE; void set_external_address(address const& ip , int source_type, address const& source) TORRENT_OVERRIDE; virtual external_ip const& external_address() const TORRENT_OVERRIDE; // used when posting synchronous function // calls to session_impl and torrent objects mutable libtorrent::mutex mut; mutable libtorrent::condition_variable cond; // cork a peer and schedule a delayed uncork // does nothing if the peer is already corked void cork_burst(peer_connection* p) TORRENT_OVERRIDE; // uncork all peers added to the delayed uncork queue // implements uncork_interface virtual void do_delayed_uncork() TORRENT_OVERRIDE; void post_socket_job(socket_job& j) TORRENT_OVERRIDE; // implements session_interface virtual tcp::endpoint bind_outgoing_socket(socket_type& s, address const& remote_address, error_code& ec) const TORRENT_OVERRIDE; virtual bool verify_bound_address(address const& addr, bool utp , error_code& ec) TORRENT_OVERRIDE; bool has_lsd() const TORRENT_OVERRIDE { return m_lsd.get() != NULL; } std::vector& block_info_storage() TORRENT_OVERRIDE { return m_block_info_storage; } libtorrent::utp_socket_manager* utp_socket_manager() TORRENT_OVERRIDE { return &m_utp_socket_manager; } void inc_boost_connections() TORRENT_OVERRIDE { ++m_boost_connections; } // the settings for the client aux::session_settings m_settings; #ifndef TORRENT_NO_DEPRECATE // the time when the next rss feed needs updating time_point m_next_rss_update; // update any rss feeds that need updating and // recalculate m_next_rss_update void update_rss_feeds(); #endif void update_proxy(); void update_i2p_bridge(); void update_peer_tos(); void update_user_agent(); void update_unchoke_limit(); void update_connection_speed(); void update_queued_disk_bytes(); void update_alert_queue_size(); void update_dht_upload_rate_limit(); void update_disk_threads(); void update_network_threads(); void update_cache_buffer_chunk_size(); void update_report_web_seed_downloads(); void update_outgoing_interfaces(); void update_listen_interfaces(); void update_privileged_ports(); void update_auto_sequential(); void update_max_failcount(); void update_close_file_interval(); void update_upnp(); void update_natpmp(); void update_lsd(); void update_dht(); void update_count_slow(); void update_dht_bootstrap_nodes(); void update_socket_buffer_size(); void update_dht_announce_interval(); void update_anonymous_mode(); void update_force_proxy(); void update_download_rate(); void update_upload_rate(); void update_connections_limit(); #ifndef TORRENT_NO_DEPRECATE void update_local_download_rate(); void update_local_upload_rate(); void update_rate_limit_utp(); void update_ignore_rate_limits_on_local_network(); #endif void update_alert_mask(); void trigger_auto_manage() TORRENT_OVERRIDE; private: // return the settings value for int setting "n", if the value is // negative, return INT_MAX int get_int_setting(int n) const; std::vector m_torrent_lists[num_torrent_lists]; peer_class_pool m_classes; void init(); void submit_disk_jobs(); void on_trigger_auto_manage(); void on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih); void setup_socket_buffers(socket_type& s) TORRENT_OVERRIDE; counters m_stats_counters; // this is a pool allocator for torrent_peer objects torrent_peer_allocator m_peer_allocator; // this vector is used to store the block_info // objects pointed to by partial_piece_info returned // by torrent::get_download_queue. std::vector m_block_info_storage; #ifndef TORRENT_DISABLE_POOL_ALLOCATOR // this pool is used to allocate and recycle send // buffers from. boost::pool<> m_send_buffers; #endif io_service& m_io_service; #ifdef TORRENT_USE_OPENSSL // this is a generic SSL context used when talking to // unauthenticated HTTPS servers ssl::context m_ssl_ctx; #endif // handles delayed alerts mutable alert_manager m_alerts; #ifndef TORRENT_NO_DEPRECATE // the alert pointers stored in m_alerts mutable std::vector m_alert_pointers; // if not all the alerts in m_alert_pointers have been delivered to // the client. This is the offset into m_alert_pointers where the next // alert is. If this is greater than or equal to m_alert_pointers.size() // it means we need to request new alerts from the main thread. mutable int m_alert_pointer_pos; #endif // handles disk io requests asynchronously // peers have pointers into the disk buffer // pool, and must be destructed before this // object. The disk thread relies on the file // pool object, and must be destructed before // m_files. The disk io thread posts completion // events to the io service, and needs to be // constructed after it. disk_io_thread m_disk_thread; // a thread pool used for async_write_some calls, // to distribute its cost to multiple threads std::vector > m_net_thread_pool; // the bandwidth manager is responsible for // handing out bandwidth to connections that // asks for it, it can also throttle the // rate. bandwidth_manager m_download_rate; bandwidth_manager m_upload_rate; // the peer class that all peers belong to by default peer_class_t m_global_class; // the peer class all TCP peers belong to by default // all tcp peer connections are subject to these // bandwidth limits. Local peers are exempted // from this limit. The purpose is to be able to // throttle TCP that passes over the internet // bottleneck (i.e. modem) to avoid starving out // uTP connections. peer_class_t m_tcp_peer_class; // peer class for local peers peer_class_t m_local_peer_class; resolver m_host_resolver; tracker_manager m_tracker_manager; torrent_map m_torrents; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // this maps obfuscated hashes to torrents. It's only // used when encryption is enabled torrent_map m_obfuscated_torrents; #endif // this is an LRU for torrents. It's used to determine // which torrents should be loaded into RAM and which ones // shouldn't. Each torrent that's loaded is part of this // list. linked_list m_torrent_lru; std::map > m_uuids; // when saving resume data for many torrents, torrents are // queued up in this list in order to not have too many of them // outstanding at any given time, since the resume data may use // a lot of memory. std::list > m_save_resume_queue; // the number of save resume data disk jobs that are currently // outstanding int m_num_save_resume; // peer connections are put here when disconnected to avoid // race conditions with the disk thread. It's important that // peer connections are destructed from the network thread, // once a peer is disconnected, it's put in this list and // every second their refcount is checked, and if it's 1, // they are deleted (from the network thread) std::vector > m_undead_peers; // keep the io_service alive until we have posted the job // to clear the undead peers boost::optional m_work; // this maps sockets to their peer_connection // object. It is the complete list of all connected // peers. connection_map m_connections; // this list holds incoming connections while they // are performing SSL handshake. When we shut down // the session, all of these are disconnected, otherwise // they would linger and stall or hang session shutdown std::set > m_incoming_sockets; // maps IP ranges to bitfields representing peer class IDs // to assign peers matching a specific IP range based on its // remote endpoint ip_filter m_peer_class_filter; // maps socket types to peer classes peer_class_type_filter m_peer_class_type_filter; // filters incoming connections boost::shared_ptr m_ip_filter; // filters outgoing connections port_filter m_port_filter; // this is the highest queue position of any torrent // in this session. queue positions are packed (i.e. there // are no gaps). If there are no torrents with queue positions // this is -1. int m_max_queue_pos; // 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 addresses or device names of the interfaces we are supposed to // listen on. if empty, it means that we should let the os decide // which interface to listen on std::vector > m_listen_interfaces; // keep this around until everything uses the list of interfaces // instead. tcp::endpoint m_listen_interface; // the network interfaces outgoing connections are opened through. If // there is more then one, they are used in a round-robin fashion // each element is a device name or IP address (in string form) and // a port number. The port determines which port to bind the listen // socket to, and the device or IP determines which network adapter // to be used. If no adapter with the specified name exists, the listen // socket fails. // TODO: should this be renamed m_outgoing_interfaces? std::vector m_net_interfaces; // if we're listening on an IPv6 interface // this is one of the non local IPv6 interfaces // on this machine boost::optional m_ipv6_interface; boost::optional m_ipv4_interface; // since we might be listening on multiple interfaces // we might need more than one listen socket std::list m_listen_sockets; #if TORRENT_USE_I2P i2p_connection m_i2p_conn; boost::shared_ptr m_i2p_listen_socket; #endif #ifdef TORRENT_USE_OPENSSL ssl::context* ssl_ctx() TORRENT_OVERRIDE { return &m_ssl_ctx; } void on_incoming_utp_ssl(boost::shared_ptr const& s); void ssl_handshake(error_code const& ec, boost::shared_ptr s); #endif // round-robin index into m_net_interfaces mutable boost::uint8_t m_interface_index; enum listen_on_flags_t { open_ssl_socket = 0x10 }; listen_socket_t setup_listener(std::string const& device , boost::asio::ip::tcp const& protocol, int port, int flags , error_code& ec); #ifndef TORRENT_DISABLE_DHT entry m_dht_state; #endif // this is initialized to the unchoke_interval // session_setting and decreased every second. // when it reaches zero, it is reset to the // unchoke_interval and the unchoke set is // recomputed. // TODO: replace this by a proper asio timer int m_unchoke_time_scaler; // this is used to decide when to recalculate which // torrents to keep queued and which to activate // TODO: replace this by a proper asio timer int m_auto_manage_time_scaler; // works like unchoke_time_scaler but it // is only decreased when the unchoke set // is recomputed, and when it reaches zero, // the optimistic unchoke is moved to another peer. // TODO: replace this by a proper asio timer int m_optimistic_unchoke_time_scaler; // works like unchoke_time_scaler. Each time // it reaches 0, and all the connections are // used, the worst connection will be disconnected // from the torrent with the most peers int m_disconnect_time_scaler; // when this scaler reaches zero, it will // scrape one of the auto managed, paused, // torrents. int m_auto_scrape_time_scaler; #ifndef TORRENT_NO_DEPRECATE // the index of the torrent that we'll // refresh the next time int m_next_explicit_cache_torrent; // this is a counter of the number of seconds until // the next time the read cache is rotated, if we're // using an explicit read read cache. int m_cache_rotation_timer; #endif // the index of the torrent that we'll // refresh the next time int m_next_suggest_torrent; // this is a counter of the number of seconds until // the next time the suggest pieces are refreshed int m_suggest_timer; // statistics gathered from all torrents. stat m_stat; // implements session_interface virtual void sent_bytes(int bytes_payload, int bytes_protocol) TORRENT_OVERRIDE; virtual void received_bytes(int bytes_payload, int bytes_protocol) TORRENT_OVERRIDE; virtual void trancieve_ip_packet(int bytes, bool ipv6) TORRENT_OVERRIDE; virtual void sent_syn(bool ipv6) TORRENT_OVERRIDE; virtual void received_synack(bool ipv6) TORRENT_OVERRIDE; int m_peak_up_rate; int m_peak_down_rate; void on_tick(error_code const& e); void on_close_file(error_code const& e); void try_connect_more_peers(); void auto_manage_checking_torrents(std::vector& list , int& limit); void auto_manage_torrents(std::vector& list , int& dht_limit, int& tracker_limit , int& lsd_limit, int& hard_limit, int type_limit); void recalculate_auto_managed_torrents(); void recalculate_unchoke_slots(); void recalculate_optimistic_unchoke_slots(); time_point m_created; boost::uint16_t session_time() const TORRENT_OVERRIDE { // +1 is here to make it possible to distinguish uninitialized (to // 0) timestamps and timestamps of things that happened during the // first second after the session was constructed boost::int64_t const ret = total_seconds(aux::time_now() - m_created) + 1; TORRENT_ASSERT(ret >= 0); TORRENT_ASSERT(ret <= (std::numeric_limits::max)()); return static_cast(ret); } time_point m_last_tick; time_point m_last_second_tick; // the last time we went through the peers // to decide which ones to choke/unchoke time_point m_last_choke; // the last time we recalculated which torrents should be started // and stopped (only the auto managed ones) time_point m_last_auto_manage; // when outgoing_ports is configured, this is the // port we'll bind the next outgoing socket to mutable int m_next_port; #ifndef TORRENT_DISABLE_DHT boost::shared_ptr m_dht; dht_settings m_dht_settings; dht::dht_storage_constructor_type m_dht_storage_constructor; // these are used when starting the DHT // (and bootstrapping it), and then erased std::vector m_dht_router_nodes; // if a DHT node is added when there's no DHT instance, they're stored // here until we start the DHT std::vector m_dht_nodes; // this announce timer is used // by the DHT. deadline_timer m_dht_announce_timer; // the number of torrents there were when the // update_dht_announce_interval() was last called. // if the number of torrents changes significantly // compared to this number, the DHT announce interval // is updated again. This especially matters for // small numbers. int m_dht_interval_update_torrents; // the number of DHT router lookups there are currently outstanding. As // long as this is > 0, we'll postpone starting the DHT int m_outstanding_router_lookups; #endif bool incoming_packet(error_code const& ec , udp::endpoint const&, char const* buf, int size) TORRENT_OVERRIDE; // see m_external_listen_port. This is the same // but for the udp port used by the DHT. int m_external_udp_port; rate_limited_udp_socket m_udp_socket; libtorrent::utp_socket_manager m_utp_socket_manager; #ifdef TORRENT_USE_OPENSSL // used for uTP connections over SSL udp_socket m_ssl_udp_socket; libtorrent::utp_socket_manager m_ssl_utp_socket_manager; #endif // the number of torrent connection boosts // connections that have been made this second // this is deducted from the connect speed int m_boost_connections; boost::shared_ptr m_natpmp; boost::shared_ptr m_upnp; boost::shared_ptr m_lsd; // mask is a bitmask of which protocols to remap on: // 1: NAT-PMP // 2: UPnP void remap_tcp_ports(boost::uint32_t mask, int tcp_port, int ssl_port); // 0 is natpmp 1 is upnp int m_tcp_mapping[2]; int m_udp_mapping[2]; #ifdef TORRENT_USE_OPENSSL int m_ssl_tcp_mapping[2]; int m_ssl_udp_mapping[2]; #endif // the timer used to fire the tick deadline_timer m_timer; aux::handler_storage m_tick_handler_storage; template aux::allocating_handler make_tick_handler(Handler const& handler) { return aux::allocating_handler( handler, m_tick_handler_storage); } // torrents are announced on the local network in a // round-robin fashion. All torrents are cycled through // within the LSD announce interval (which defaults to // 5 minutes) torrent_map::iterator m_next_lsd_torrent; #ifndef TORRENT_DISABLE_DHT // torrents are announced on the DHT in a // round-robin fashion. All torrents are cycled through // within the DHT announce interval (which defaults to // 15 minutes) torrent_map::iterator m_next_dht_torrent; // torrents that don't have any peers // when added should be announced to the DHT // as soon as possible. Such torrents are put // in this queue and get announced the next time // the timer fires, instead of the next one in // the round-robin sequence. std::deque > m_dht_torrents; #endif // torrents prioritized to get connection attempts std::deque, int> > m_prio_torrents; // this announce timer is used // by Local service discovery deadline_timer m_lsd_announce_timer; // this is the timer used to call ``close_oldest`` on the ``file_pool`` // object. This closes the file that's been opened the longest every // time it's called, to force the windows disk cache to be flushed deadline_timer m_close_file_timer; // the index of the torrent that will be offered to // connect to a peer next time on_tick is called. // This implements a round robin peer connections among // torrents that want more peers. The index is into // m_torrent_lists[torrent_want_peers_downloading] // (which is a list of torrent pointers with all // torrents that want peers and are downloading) int m_next_downloading_connect_torrent; int m_next_finished_connect_torrent; // this is the number of attempts of connecting to // peers we have given to downloading torrents. // when this gets high enough, we try to connect // a peer from a finished torrent int m_download_connect_attempts; // index into m_torrent_lists[torrent_want_scrape] referring // to the next torrent to auto-scrape int m_next_scrape_torrent; #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif counters& stats_counters() TORRENT_OVERRIDE { return m_stats_counters; } void received_buffer(int size) TORRENT_OVERRIDE; void sent_buffer(int size) TORRENT_OVERRIDE; // each second tick the timer takes a little // bit longer than one second to trigger. The // extra time it took is accumulated into this // counter. Every time it exceeds 1000, torrents // will tick their timers 2 seconds instead of one. // this keeps the timers more accurate over time // as a kind of "leap second" to adjust for the // accumulated error boost::uint16_t m_tick_residual; #ifndef TORRENT_DISABLE_LOGGING virtual void session_log(char const* fmt, ...) const TORRENT_OVERRIDE TORRENT_FORMAT(2,3); virtual void session_vlog(char const* fmt, va_list& va) const TORRENT_OVERRIDE TORRENT_FORMAT(2,0); // this list of tracker loggers serves as tracker_callbacks when // shutting down. This list is just here to keep them alive during // whe shutting down process std::list > m_tracker_loggers; #endif // TODO: 2 the throttling of saving resume data could probably be // factored out into a separate class virtual void queue_async_resume_data(boost::shared_ptr const& t) TORRENT_OVERRIDE; virtual void done_async_resume() TORRENT_OVERRIDE; void async_resume_dispatched(); // state for keeping track of external IPs external_ip m_external_ip; #ifndef TORRENT_DISABLE_EXTENSIONS // this is a list to allow extensions to potentially remove themselves. typedef std::list > ses_extension_list_t; ses_extension_list_t m_ses_extensions; // the union of all session extensions' implemented_features(). This is // used to exclude callbacks to the session extensions. boost::uint32_t m_session_extension_features; // std::string could be used for the query names if only all common // implementations used SSO *glares at gcc* struct extension_dht_query { boost::uint8_t query_len; boost::array query; dht_extension_handler_t handler; }; typedef std::vector m_extension_dht_queries_t; m_extension_dht_queries_t m_extension_dht_queries; #endif // if this function is set, it indicates that torrents are allowed // to be unloaded. If it isn't, torrents will never be unloaded user_load_function_t m_user_load_torrent; // this is true whenever we have posted a deferred-disk job // it means we don't need to post another one bool m_deferred_submit_disk_jobs; // this is set to true when a torrent auto-manage // event is triggered, and reset whenever the message // is delivered and the auto-manage is executed. // there should never be more than a single pending auto-manage // message in-flight at any given time. bool m_pending_auto_manage; // this is also set to true when triggering an auto-manage // of the torrents. However, if the normal auto-manage // timer comes along and executes the auto-management, // this is set to false, which means the triggered event // no longer needs to execute the auto-management. bool m_need_auto_manage; // set to true when the session object // is being destructed and the thread // should exit bool m_abort; // is true if the session is paused bool m_paused; #ifndef TORRENT_NO_DEPRECATE std::vector > m_feeds; #endif // this is a list of peer connections who have been // corked (i.e. their network socket) and needs to be // uncorked at the end of the burst of events. This is // here to coalesce the effects of bursts of events // into fewer network writes, saving CPU and possibly // ending up sending larger network packets std::vector m_delayed_uncorks; }; #ifndef TORRENT_DISABLE_LOGGING struct tracker_logger : request_callback { tracker_logger(session_interface& ses); void tracker_warning(tracker_request const& req , std::string const& str); void tracker_response(tracker_request const& , libtorrent::address const& tracker_ip , std::list
const& ip_list , struct tracker_response const& resp); void tracker_request_timed_out( tracker_request const&); void tracker_request_error(tracker_request const& r , int response_code, error_code const& ec, const std::string& str , int retry_interval); void debug_log(const char* fmt, ...) const TORRENT_FORMAT(2,3); session_interface& m_ses; private: // explicitly disallow assignment, to silence msvc warning tracker_logger& operator=(tracker_logger const&); }; #endif } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/session_interface.hpp000066400000000000000000000301031351156116000267170ustar00rootroot00000000000000/* Copyright (c) 2012, 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_INTERFACE_HPP_INCLUDED #define TORRENT_SESSION_INTERFACE_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/address.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/disk_buffer_holder.hpp" #ifndef TORRENT_DISABLE_DHT #include "libtorrent/socket.hpp" #endif #include "libtorrent/socket.hpp" // for tcp::endpoint #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #ifndef TORRENT_DISABLE_LOGGING #include #include // for va_list #endif #ifdef TORRENT_USE_OPENSSL // there is no forward declaration header for asio namespace boost { namespace asio { namespace ssl { struct context; } } } #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { class peer_connection; class torrent; struct socket_job; #ifndef TORRENT_NO_DEPRECATE struct pe_settings; #endif struct peer_class_set; struct bandwidth_channel; struct bandwidth_manager; struct peer_class_pool; struct disk_observer; struct torrent_peer; class alert_manager; struct disk_interface; struct tracker_request; struct request_callback; struct utp_socket_manager; struct socket_type; struct block_info; struct external_ip; struct torrent_handle; struct ip_filter; class port_filter; struct settings_pack; struct torrent_peer_allocator_interface; struct counters; struct resolver_interface; #ifndef TORRENT_DISABLE_DHT namespace dht { struct dht_tracker; } #endif } namespace libtorrent { namespace aux { struct proxy_settings; struct session_settings; #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS // This is the basic logging and debug interface offered by the session. // a release build with logging disabled (which is the default) will // not have this class at all struct TORRENT_EXTRA_EXPORT session_logger { #ifndef TORRENT_DISABLE_LOGGING virtual void session_log(char const* fmt, ...) const TORRENT_FORMAT(2,3) = 0; virtual void session_vlog(char const* fmt, va_list& va) const = 0; #endif #if TORRENT_USE_ASSERTS virtual bool is_single_thread() const = 0; virtual bool has_peer(peer_connection const* p) const = 0; virtual bool any_torrent_has_peer(peer_connection const* p) const = 0; virtual bool is_posting_torrent_updates() const = 0; #endif protected: ~session_logger() {} }; #endif // TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS // TODO: 2 make this interface a lot smaller. It could be split up into // several smaller interfaces. Each subsystem could then limit the size // of the mock object to test it. struct TORRENT_EXTRA_EXPORT session_interface : buffer_allocator_interface #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS , session_logger #endif { // TODO: 2 the IP voting mechanism should be factored out // to its own class, not part of the session enum { source_dht = 1, source_peer = 2, source_tracker = 4, source_router = 8 }; virtual void set_external_address(address const& ip , int source_type, address const& source) = 0; virtual external_ip const& external_address() const = 0; virtual disk_interface& disk_thread() = 0; virtual alert_manager& alerts() = 0; virtual torrent_peer_allocator_interface& get_peer_allocator() = 0; virtual io_service& get_io_service() = 0; virtual resolver_interface& get_resolver() = 0; typedef boost::function const&)> callback_t; // TODO: 2 remove this. There's already get_resolver() virtual void async_resolve(std::string const& host, int flags , callback_t const& h) = 0; virtual bool has_connection(peer_connection* p) const = 0; virtual void insert_peer(boost::shared_ptr const& c) = 0; virtual void queue_async_resume_data(boost::shared_ptr const& t) = 0; virtual void done_async_resume() = 0; virtual void evict_torrent(torrent* t) = 0; virtual void remove_torrent(torrent_handle const& h, int options = 0) = 0; virtual void remove_torrent_impl(boost::shared_ptr tptr, int options) = 0; // port filter virtual port_filter const& get_port_filter() const = 0; virtual void ban_ip(address addr) = 0; virtual boost::uint16_t session_time() const = 0; virtual bool is_paused() const = 0; virtual bool is_aborted() const = 0; virtual int num_uploads() const = 0; virtual bool preemptive_unchoke() const = 0; virtual void trigger_optimistic_unchoke() = 0; virtual void trigger_unchoke() = 0; virtual boost::weak_ptr find_torrent(sha1_hash const& info_hash) const = 0; virtual boost::weak_ptr find_disconnect_candidate_torrent() const = 0; virtual boost::shared_ptr delay_load_torrent(sha1_hash const& info_hash , peer_connection* pc) = 0; virtual void insert_torrent(sha1_hash const& ih, boost::shared_ptr const& t , std::string uuid) = 0; virtual void insert_uuid_torrent(std::string uuid, boost::shared_ptr const& t) = 0; virtual void set_queue_position(torrent* t, int p) = 0; virtual int num_torrents() const = 0; // cork a peer and schedule a delayed uncork // does nothing if the peer is already corked virtual void cork_burst(peer_connection* p) = 0; virtual void close_connection(peer_connection* p, error_code const& ec) = 0; virtual int num_connections() const = 0; virtual char* allocate_buffer() = 0; virtual void free_buffer(char* buf) = 0; virtual int send_buffer_size() const = 0; virtual void deferred_submit_jobs() = 0; virtual boost::uint16_t listen_port() const = 0; virtual boost::uint16_t ssl_listen_port() const = 0; // TODO: 2 factor out the thread pool for socket jobs into a separate // class // used to (potentially) issue socket write calls onto multiple threads virtual void post_socket_job(socket_job& j) = 0; // load the specified torrent. also evict one torrent, except // for the one specified, if we are at the limit of loaded torrents virtual bool load_torrent(torrent* t) = 0; // bump the specified torrent to make it the most recently used one // in the torrent LRU (i.e. the least likely to get unloaded) virtual void bump_torrent(torrent* t, bool back = true) = 0; // ask for which interface and port to bind outgoing peer connections on virtual tcp::endpoint bind_outgoing_socket(socket_type& s, address const& remote_address, error_code& ec) const = 0; virtual bool verify_bound_address(address const& addr, bool utp , error_code& ec) = 0; #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS virtual std::vector > find_collection( std::string const& collection) const = 0; #endif // TODO: it would be nice to not have this be part of session_interface virtual proxy_settings proxy() const = 0; #if TORRENT_USE_I2P virtual proxy_settings i2p_proxy() const = 0; virtual char const* i2p_session() const = 0; #endif virtual void prioritize_connections(boost::weak_ptr t) = 0; virtual boost::optional get_ipv6_interface() const = 0; virtual boost::optional get_ipv4_interface() const = 0; virtual void trigger_auto_manage() = 0; virtual void apply_settings_pack(boost::shared_ptr pack) = 0; virtual session_settings const& settings() const = 0; virtual void queue_tracker_request(tracker_request& req , boost::weak_ptr c) = 0; // peer-classes virtual void set_peer_classes(peer_class_set* s, address const& a, int st) = 0; virtual peer_class_pool const& peer_classes() const = 0; virtual peer_class_pool& peer_classes() = 0; virtual bool ignore_unchoke_slots_set(peer_class_set const& set) const = 0; virtual int copy_pertinent_channels(peer_class_set const& set , int channel, bandwidth_channel** dst, int max) = 0; virtual int use_quota_overhead(peer_class_set& set, int amount_down, int amount_up) = 0; virtual bandwidth_manager* get_bandwidth_manager(int channel) = 0; virtual void sent_bytes(int bytes_payload, int bytes_protocol) = 0; virtual void received_bytes(int bytes_payload, int bytes_protocol) = 0; virtual void trancieve_ip_packet(int bytes, bool ipv6) = 0; virtual void sent_syn(bool ipv6) = 0; virtual void received_synack(bool ipv6) = 0; enum torrent_list_index { // this is the set of (subscribed) torrents that have changed // their states since the last time the user requested updates. torrent_state_updates, // all torrents that want to be ticked every second torrent_want_tick, // all torrents that want more peers and are still downloading // these typically have higher priority when connecting peers torrent_want_peers_download, // all torrents that want more peers and are finished downloading torrent_want_peers_finished, // torrents that want auto-scrape (only paused auto-managed ones) torrent_want_scrape, // auto-managed torrents by state. Only these torrents are considered // when recalculating auto-managed torrents. started auto managed // torrents that are inactive are not part of these lists, because they // are not considered for auto managing (they are left started // unconditionallty) torrent_downloading_auto_managed, torrent_seeding_auto_managed, torrent_checking_auto_managed, // all torrents that have resume data to save // torrent_want_save_resume, num_torrent_lists }; virtual std::vector& torrent_list(int i) = 0; virtual bool has_lsd() const = 0; virtual void announce_lsd(sha1_hash const& ih, int port, bool broadcast = false) = 0; virtual libtorrent::utp_socket_manager* utp_socket_manager() = 0; virtual void inc_boost_connections() = 0; virtual void setup_socket_buffers(socket_type& s) = 0; virtual std::vector& block_info_storage() = 0; #ifdef TORRENT_USE_OPENSSL virtual boost::asio::ssl::context* ssl_ctx() = 0 ; #endif #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) virtual torrent const* find_encrypted_torrent( sha1_hash const& info_hash, sha1_hash const& xor_mask) = 0; virtual void add_obfuscated_hash(sha1_hash const& obfuscated , boost::weak_ptr const& t) = 0; #endif #ifndef TORRENT_DISABLE_DHT virtual bool announce_dht() const = 0; virtual void add_dht_node(udp::endpoint n) = 0; virtual bool has_dht() const = 0; virtual int external_udp_port() const = 0; virtual dht::dht_tracker* dht() = 0; virtual void prioritize_dht(boost::weak_ptr t) = 0; #endif virtual counters& stats_counters() = 0; virtual void received_buffer(int size) = 0; virtual void sent_buffer(int size) = 0; protected: ~session_interface() {} }; }} #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/session_settings.hpp000066400000000000000000000064051351156116000266270ustar00rootroot00000000000000/* Copyright (c) 2012, 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_AUX_SESSION_SETTINGS_HPP_INCLUDED #define TORRENT_AUX_SESSION_SETTINGS_HPP_INCLUDED #include "libtorrent/version.hpp" #include "libtorrent/config.hpp" #include "libtorrent/settings_pack.hpp" #include "libtorrent/assert.hpp" #include namespace libtorrent { struct settings_pack; TORRENT_EXTRA_EXPORT void initialize_default_settings(aux::session_settings& s); } namespace libtorrent { namespace aux { #define SET(type) \ TORRENT_ASSERT((name & settings_pack::type_mask) == settings_pack:: type ## _type_base); \ if ((name & settings_pack::type_mask) != settings_pack:: type ## _type_base) return; \ m_ ## type ## s[name - settings_pack:: type ## _type_base] = value #define GET(type, default_val) \ TORRENT_ASSERT((name & settings_pack::type_mask) == settings_pack:: type ## _type_base); \ if ((name & settings_pack::type_mask) != settings_pack:: type ## _type_base) return default_val; \ return m_ ## type ## s[name - settings_pack:: type ## _type_base] struct TORRENT_EXTRA_EXPORT session_settings { friend void libtorrent::save_settings_to_dict( aux::session_settings const& s, entry::dictionary_type& sett); void set_str(int name, std::string const& value) { SET(string); } std::string const& get_str(int name) const { GET(string, m_strings[0]); } void set_int(int name, int value) { SET(int); } int get_int(int name) const { GET(int, 0); } void set_bool(int name, bool value) { SET(bool); } bool get_bool(int name) const { GET(bool, false); } session_settings(); session_settings(settings_pack const&); private: std::string m_strings[settings_pack::num_string_settings]; int m_ints[settings_pack::num_int_settings]; // TODO: make this a bitfield bool m_bools[settings_pack::num_bool_settings]; }; #undef GET #undef SET } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/aux_/time.hpp000066400000000000000000000036131351156116000241600ustar00rootroot00000000000000/* Copyright (c) 2015, 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_AUX_TIME_HPP #define TORRENT_AUX_TIME_HPP #include "libtorrent/config.hpp" #include "libtorrent/time.hpp" #include #include namespace libtorrent { namespace aux { // returns the current time, as represented by time_point. The // resolution of this timer is about 100 ms. time_point const& time_now(); TORRENT_EXTRA_EXPORT void update_time_now(); } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/bandwidth_limit.hpp000066400000000000000000000064541351156116000254360ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_BANDWIDTH_CHANNEL_HPP_INCLUDED #define TORRENT_BANDWIDTH_CHANNEL_HPP_INCLUDED #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { // member of peer_connection struct TORRENT_EXTRA_EXPORT bandwidth_channel { static boost::int32_t const inf = boost::integer_traits::const_max; bandwidth_channel(); // 0 means infinite void throttle(int limit); int throttle() const { TORRENT_ASSERT_VAL(m_limit >= 0, m_limit); TORRENT_ASSERT_VAL(m_limit < inf, m_limit); return m_limit; } int quota_left() const; void update_quota(int dt_milliseconds); // this is used when connections disconnect with // some quota left. It's returned to its bandwidth // channels. void return_quota(int amount); void use_quota(int amount); // this is an optimization. If there is more than one second // of quota built up in this channel, just apply it right away // instead of introducing a delay to split it up evenly. This // should especially help in situations where a single peer // has a capacity under the rate limit, but would otherwise be // held back by the latency of getting bandwidth from the limiter bool need_queueing(int amount) { if (m_quota_left - amount < m_limit) return true; m_quota_left -= amount; return false; } // used as temporary storage while distributing // bandwidth int tmp; // this is the number of bytes to distribute this round int distribute_quota; private: // this is the amount of bandwidth we have // been assigned without using yet. boost::int64_t m_quota_left; // the limit is the number of bytes // per second we are allowed to use. boost::int32_t m_limit; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/bandwidth_manager.hpp000066400000000000000000000063121351156116000257230ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_BANDWIDTH_MANAGER_HPP_INCLUDED #define TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/bandwidth_limit.hpp" #include "libtorrent/bandwidth_queue_entry.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/bandwidth_socket.hpp" #include "libtorrent/time.hpp" namespace libtorrent { struct TORRENT_EXTRA_EXPORT bandwidth_manager { bandwidth_manager(int channel); void close(); #if TORRENT_USE_ASSERTS bool is_queued(bandwidth_socket const* peer) const; #endif int queue_size() const; boost::int64_t queued_bytes() const; // non prioritized means that, if there's a line for bandwidth, // others will cut in front of the non-prioritized peers. // this is used by web seeds // returns the number of bytes to assign to the peer, or 0 // if the peer's 'assign_bandwidth' callback will be called later int request_bandwidth(boost::shared_ptr const& peer , int blk, int priority, bandwidth_channel** chan, int num_channels); #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif void update_quotas(time_duration const& dt); private: // these are the consumers that want bandwidth typedef std::vector queue_t; queue_t m_queue; // the number of bytes all the requests in queue are for boost::int64_t m_queued_bytes; // this is the channel within the consumers // that bandwidth is assigned to (upload or download) int m_channel; bool m_abort; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/bandwidth_queue_entry.hpp000066400000000000000000000052461351156116000266630ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_BANDWIDTH_QUEUE_ENTRY_HPP_INCLUDED #define TORRENT_BANDWIDTH_QUEUE_ENTRY_HPP_INCLUDED #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/bandwidth_limit.hpp" #include "libtorrent/bandwidth_socket.hpp" namespace libtorrent { struct TORRENT_EXTRA_EXPORT bw_request { bw_request(boost::shared_ptr const& pe , int blk, int prio); boost::shared_ptr peer; // 1 is normal prio int priority; // the number of bytes assigned to this request so far int assigned; // once assigned reaches this, we dispatch the request function int request_size; // the max number of rounds for this request to survive // this ensures that requests gets responses at very low // rate limits, when the requested size would take a long // time to satisfy int ttl; // loops over the bandwidth channels and assigns bandwidth // from the most limiting one int assign_bandwidth(); enum { max_bandwidth_channels = 10 }; // we don't actually support more than 10 channels per peer bandwidth_channel* channel[max_bandwidth_channels]; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/bandwidth_socket.hpp000066400000000000000000000035361351156116000256060ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_BANDWIDTH_SOCKET_HPP_INCLUDED #define TORRENT_BANDWIDTH_SOCKET_HPP_INCLUDED namespace libtorrent { struct TORRENT_EXTRA_EXPORT bandwidth_socket { virtual void assign_bandwidth(int channel, int amount) = 0; virtual bool is_disconnecting() const = 0; virtual ~bandwidth_socket() {} }; } #endif // TORRENT_BANDWIDTH_SOCKET_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/bdecode.hpp000066400000000000000000000413131351156116000236520ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/assert.hpp" #ifndef TORRENT_BDECODE_HPP #define TORRENT_BDECODE_HPP /* This is an efficient bdecoder. It decodes into a flat memory buffer of tokens. Each token has an offset into the bencoded buffer where the token came from and a next pointer, which is a relative number of tokens to skip forward to get to the logical next item in a container. strings and ints offset pointers point to the first character of the length prefix or the 'i' character. This is to maintain uniformity with other types and to allow easily calculating the span of a node by subtracting its offset by the offset of the next node. example layout: { "a": { "b": 1, "c": "abcd" }, "d": 3 } /---------------------------------------------------------------------------------------\ | | | | | /--------------------------------------------\ | | | | | | | | | | /-----\ | /----\ /----\ /----\ /----\ | /----\ /----\ | | next | | | | | | | | | | | | | | | | | | pointers | v | | v | v | v | v v | v | v v +-+-----+----+--+----+--+----+--+----+--+----+--+----+--+-------+----+--+----+--+------+ X | dict | str | dict | str | int | str | str | end | str | int | end | | | | | | | | | | | | | | | | | | | | | | | | | +-+-----+-+-----+-+-----+-+-----+-+-----+-+-----+-+-----+-+-----+-+-----+-+-----+-+----+ | offset| | | | | | | | | | | | | | | | | | | | | |/------/ | | | | | | | | | || /-----------/ | | | | | | | | || |/------------------/ | | | | | | | || || /-----------------------/ | | | | | | || || | /----------------------------/ | | | | | || || | | /---------------------------------/ | | | | || || | | | /-----------------------------------/ | | | || || | | | |/------------------------------------------/ | | || || | | | || /-----------------------------------------------/ | || || | | | || | /----------------------------------------------------/ || || | | | || | | vv vv v v v vv v v ``d1:ad1:bi1e1:c4:abcde1:di3ee`` */ namespace libtorrent { TORRENT_EXPORT boost::system::error_category& bdecode_category(); #ifndef TORRENT_NO_DEPRECATED TORRENT_DEPRECATED_EXPORT boost::system::error_category& get_bdecode_category(); #endif namespace bdecode_errors { // libtorrent uses boost.system's ``error_code`` class to represent // errors. libtorrent has its own error category bdecode_category() // with the error codes defined by error_code_enum. enum error_code_enum { // Not an error no_error = 0, // expected digit in bencoded string expected_digit, // expected colon in bencoded string expected_colon, // unexpected end of file in bencoded string unexpected_eof, // expected value (list, dict, int or string) in bencoded string expected_value, // bencoded recursion depth limit exceeded depth_exceeded, // bencoded item count limit exceeded limit_exceeded, // integer overflow overflow, // the number of error codes error_code_max }; // hidden TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); } } // namespace libtorrent namespace boost { namespace system { template<> struct is_error_code_enum { static const bool value = true; }; } } namespace libtorrent { typedef boost::system::error_code error_code; TORRENT_EXTRA_EXPORT char const* parse_int(char const* start , char const* end, char delimiter, boost::int64_t& val , bdecode_errors::error_code_enum& ec); namespace detail { // internal struct bdecode_token { // the node with type 'end' is a logical node, pointing to the end // of the bencoded buffer. enum type_t { none, dict, list, string, integer, end }; enum limits_t { max_offset = (1 << 29) - 1, max_next_item = (1 << 29) - 1, max_header = (1 << 3) - 1 }; bdecode_token(boost::uint32_t off, bdecode_token::type_t t) : offset(off) , type(t) , next_item(0) , header(0) { TORRENT_ASSERT(off <= max_offset); TORRENT_ASSERT(t >= 0 && t <= end); } bdecode_token(boost::uint32_t off, boost::uint32_t next , bdecode_token::type_t t, boost::uint8_t header_size = 0) : offset(off) , type(t) , next_item(next) , header(type == string ? header_size - 2 : 0) { TORRENT_ASSERT(type != string || header_size >= 2); TORRENT_ASSERT(off <= max_offset); TORRENT_ASSERT(next <= max_next_item); // the string has 2 implied header bytes, to allow for longer prefixes TORRENT_ASSERT(header_size < 8 || (type == string && header_size < 10)); TORRENT_ASSERT(t >= 0 && t <= end); } int start_offset() const { TORRENT_ASSERT(type == string); return header + 2; } // offset into the bdecoded buffer where this node is boost::uint32_t offset:29; // one of type_t enums boost::uint32_t type:3; // if this node is a member of a list, 'next_item' is the number of nodes // to jump forward in th node array to get to the next item in the list. // if it's a key in a dictionary, it's the number of step forwards to get // to its corresponding value. If it's a value in a dictionary, it's the // number of steps to the next key, or to the end node. // this is the _relative_ offset to the next node boost::uint32_t next_item:29; // this is the number of bytes to skip forward from the offset to get to the // first character of the string, if this is a string. This field is not // used for other types. Essentially this is the length of the length prefix // and the colon. Since a string always has at least one character of length // prefix and always a colon, those 2 characters are implied. boost::uint32_t header:3; }; } // a ``bdecode_node`` is used to traverse and hold the tree structure defined // by bencoded data after it has been parse by bdecode(). // // There are primarily two kinds of bdecode_nodes. The ones that own the tree // structure, and defines its lifetime, and nodes that are child nodes in the // tree, pointing back into the root's tree. // // The ``bdecode_node`` passed in to ``bdecode()`` becomes the one owning the // tree structure. Make sure not to destruct that object for as long as you // use any of its child nodes. Also, keep in mind that the buffer originally // parsed also must remain valid while using it. (see switch_underlying_buffer()). // // Copying an owning node will create a copy of the whole tree, but will still // point into the same parsed bencoded buffer as the first one. // Sometimes it's important to get a non-owning reference to the root node ( // to be able to copy it as a reference for instance). For that, use the // non_owning() member function. // // There are 5 different types of nodes, see type_t. struct TORRENT_EXPORT bdecode_node { TORRENT_EXPORT friend int bdecode(char const* start, char const* end, bdecode_node& ret , error_code& ec, int* error_pos, int depth_limit , int token_limit); // creates a default constructed node, it will have the type ``none_t``. bdecode_node(); // For owning nodes, the copy will create a copy of the tree, but the // underlying buffer remains the same. bdecode_node(bdecode_node const&); bdecode_node& operator=(bdecode_node const&); // the types of bdecoded nodes enum type_t { // uninitialized or default constructed. This is also used // to indicate that a node was not found in some cases. none_t, // a dictionary node. The ``dict_find_`` functions are valid. dict_t, // a list node. The ``list_`` functions are valid. list_t, // a string node, the ``string_`` functions are valid. string_t, // an integer node. The ``int_`` functions are valid. int_t }; // the type of this node. See type_t. type_t type() const; // returns true if type() != none_t. operator bool() const; // return a non-owning reference to this node. This is useful to refer to // the root node without copying it in assignments. bdecode_node non_owning() const; // returns the buffer and length of the section in the original bencoded // buffer where this node is defined. For a dictionary for instance, this // starts with ``d`` and ends with ``e``, and has all the content of the // dictionary in between. std::pair data_section() const; // functions with the ``list_`` prefix operate on lists. These functions are // only valid if ``type()`` == ``list_t``. ``list_at()`` returns the item // in the list at index ``i``. ``i`` may not be greater than or equal to the // size of the list. ``size()`` returns the size of the list. bdecode_node list_at(int i) const; std::string list_string_value_at(int i , char const* default_val = "") const; boost::int64_t list_int_value_at(int i , boost::int64_t default_val = 0) const; int list_size() const; // Functions with the ``dict_`` prefix operates on dictionaries. They are // only valid if ``type()`` == ``dict_t``. In case a key you're looking up // contains a 0 byte, you cannot use the null-terminated string overloads, // but have to use ``std::string`` instead. ``dict_find_list`` will return a // valid ``bdecode_node`` if the key is found _and_ it is a list. Otherwise // it will return a default-constructed bdecode_node. // // Functions with the ``_value`` suffix return the value of the node // directly, rather than the nodes. In case the node is not found, or it has // a different type, a default value is returned (which can be specified). bdecode_node dict_find(std::string key) const; bdecode_node dict_find(char const* key) const; std::pair dict_at(int i) const; bdecode_node dict_find_dict(std::string key) const; bdecode_node dict_find_dict(char const* key) const; bdecode_node dict_find_list(char const* key) const; bdecode_node dict_find_string(char const* key) const; bdecode_node dict_find_int(char const* key) const; std::string dict_find_string_value(char const* key , char const* default_value = "") const; boost::int64_t dict_find_int_value(char const* key , boost::int64_t default_val = 0) const; int dict_size() const; // this function is only valid if ``type()`` == ``int_t``. It returns the // value of the integer. boost::int64_t int_value() const; // these functions are only valid if ``type()`` == ``string_t``. They return // the string values. Note that ``string_ptr()`` is *not* null-terminated. // ``string_length()`` returns the number of bytes in the string. std::string string_value() const; char const* string_ptr() const; int string_length() const; // resets the ``bdecoded_node`` to a default constructed state. If this is // an owning node, the tree is freed and all child nodes are invalidated. void clear(); // Swap contents. void swap(bdecode_node& n); // preallocate memory for the specified numbers of tokens. This is // useful if you know approximately how many tokens are in the file // you are about to parse. Doing so will save realloc operations // while parsing. You should only call this on the root node, before // passing it in to bdecode(). void reserve(int tokens); // this buffer *MUST* be identical to the one originally parsed. This // operation is only defined on owning root nodes, i.e. the one passed in to // decode(). void switch_underlying_buffer(char const* buf); private: bdecode_node(detail::bdecode_token const* tokens, char const* buf , int len, int idx); // if this is the root node, that owns all the tokens, they live in this // vector. If this is a sub-node, this field is not used, instead the // m_root_tokens pointer points to the root node's token. std::vector m_tokens; // this points to the root nodes token vector // for the root node, this points to its own m_tokens member detail::bdecode_token const* m_root_tokens; // this points to the original buffer that was parsed char const* m_buffer; int m_buffer_size; // this is the index into m_root_tokens that this node refers to // for the root node, it's 0. -1 means uninitialized. int m_token_idx; // this is a cache of the last element index looked up. This only applies // to lists and dictionaries. If the next lookup is at m_last_index or // greater, we can start iterating the tokens at m_last_token. mutable int m_last_index; mutable int m_last_token; // the number of elements in this list or dict (computed on the first // call to dict_size() or list_size()) mutable int m_size; }; // print the bencoded structure in a human-readable format to a string // that's returned. TORRENT_EXPORT std::string print_entry(bdecode_node const& e , bool single_line = false, int indent = 0); // This function decodes/parses bdecoded data (for example a .torrent file). // The data structure is returned in the ``ret`` argument. the buffer to parse // is specified by the ``start`` of the buffer as well as the ``end``, i.e. one // byte past the end. If the buffer fails to parse, the function returns a // non-zero value and fills in ``ec`` with the error code. The optional // argument ``error_pos``, if set to non-null, will be set to the byte offset // into the buffer where the parse failure occurred. // // ``depth_limit`` specifies the max number of nested lists or dictionaries are // allowed in the data structure. (This affects the stack usage of the // function, be careful not to set it too high). // // ``token_limit`` is the max number of tokens allowed to be parsed from the // buffer. This is simply a sanity check to not have unbounded memory usage. // // The resulting ``bdecode_node`` is an *owning* node. That means it will // be holding the whole parsed tree. When iterating lists and dictionaries, // those ``bdecode_node`` objects will simply have references to the root or // owning ``bdecode_node``. If the root node is destructed, all other nodes // that refer to anything in that tree become invalid. // // However, the underlying buffer passed in to this function (``start``, ``end``) // must also remain valid while the bdecoded tree is used. The parsed tree // produced by this function does not copy any data out of the buffer, but // simply produces references back into it. TORRENT_EXPORT int bdecode(char const* start, char const* end, bdecode_node& ret , error_code& ec, int* error_pos = 0, int depth_limit = 100 , int token_limit = 1000000); } #endif // TORRENT_BDECODE_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/bencode.hpp000066400000000000000000000300441351156116000236630ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 // OVERVIEW // // Bencoding is a common representation in bittorrent used for for dictionary, // list, int and string hierarchies. It's used to encode .torrent files and // some messages in the network protocol. libtorrent also uses it to store // settings, resume data and other state between sessions. // // Strings in bencoded structures are not necessarily representing text. // Strings are raw byte buffers of a certain length. If a string is meant to be // interpreted as text, it is required to be UTF-8 encoded. See `BEP 3`_. // // There are two mechanims to *decode* bencoded buffers in libtorrent. // // The most flexible one is `bdecode() bencode()`_, which returns a structure // represented by entry. Oncea buffer has been decoded with this function, it // can be discarded. The entry does not contain any references back to it. This // means that bdecode() copies all the data out of the buffer and into its own // hierarchy. This makes this function expensive, which might matter if you're // parsing large amounts of data. // // Another consideration is that `bdecode() bencode()`_ is a recursive parser. // For this reason, in order to avoid DoS attacks by triggering a stack // overflow, there is a recursion limit. This limit is a sanity check to make // sure it doesn't run the risk of busting the stack. // // The second mechanism is the decode function for bdecode_node. This function // builds a tree that points back into the original buffer. The returned // bdecode_node will not be valid once the buffer it was parsed out of is // discarded. // // Not only is this function more efficient because of less memory allocation // and data copy, the parser is also not recursive, which means it probably // performs a little bit better and can have a higher recursion limit on the // structures it's parsing. #include #include #include #include // for distance #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/io.hpp" // for write_string namespace libtorrent { #ifndef TORRENT_NO_DEPRECATE // thrown by bdecode() if the provided bencoded buffer does not contain // valid encoding. struct TORRENT_DEPRECATED invalid_encoding : std::exception { // hidden virtual const char* what() const TORRENT_EXCEPTION_THROW_SPECIFIER TORRENT_OVERRIDE TORRENT_FINAL { return "invalid bencoding"; } }; #endif namespace detail { template int 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]; int ret = 0; for (char const* str = integer_to_str(buf, 21, val); *str != 0; ++str) { *out = *str; ++out; ++ret; } return ret; } template void write_char(OutIt& out, char c) { *out = c; ++out; } template std::string read_until(InIt& in, InIt end, char end_token, bool& err) { std::string ret; if (in == end) { err = true; return ret; } while (*in != end_token) { ret += *in; ++in; if (in == end) { err = true; return ret; } } return ret; } template void read_string(InIt& in, InIt end, int len, std::string& str, bool& err) { TORRENT_ASSERT(len >= 0); for (int i = 0; i < len; ++i) { if (in == end) { err = true; return; } str += *in; ++in; } } template int bencode_recursive(OutIt& out, const entry& e) { int ret = 0; switch(e.type()) { case entry::int_t: write_char(out, 'i'); ret += write_integer(out, e.integer()); write_char(out, 'e'); ret += 2; break; case entry::string_t: ret += write_integer(out, e.string().length()); write_char(out, ':'); ret += write_string(e.string(), out); ret += 1; break; case entry::list_t: write_char(out, 'l'); for (entry::list_type::const_iterator i = e.list().begin(); i != e.list().end(); ++i) ret += bencode_recursive(out, *i); write_char(out, 'e'); ret += 2; 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 ret += write_integer(out, i->first.length()); write_char(out, ':'); ret += write_string(i->first, out); // write value ret += bencode_recursive(out, i->second); ret += 1; } write_char(out, 'e'); ret += 2; break; case entry::preformatted_t: std::copy(e.preformatted().begin(), e.preformatted().end(), out); ret += static_cast(e.preformatted().size()); break; case entry::undefined_t: // empty string write_char(out, '0'); write_char(out, ':'); ret += 2; break; } return ret; } template void bdecode_recursive(InIt& in, InIt end, entry& ret, bool& err, int depth) { if (depth >= 100) { err = true; return; } if (in == end) { err = true; #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif return; } switch (*in) { // ---------------------------------------------- // integer case 'i': { ++in; // 'i' std::string val = read_until(in, end, 'e', err); if (err) return; TORRENT_ASSERT(*in == 'e'); ++in; // 'e' ret = entry(entry::int_t); char* end_pointer; ret.integer() = strtoll(val.c_str(), &end_pointer, 10); #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif if (end_pointer == val.c_str()) { err = true; return; } } 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, err, depth + 1); if (err) { #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif return; } if (in == end) { err = true; #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif return; } } #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif TORRENT_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, err, depth + 1); if (err || key.type() != entry::string_t) { #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif return; } entry& e = ret[key.string()]; bdecode_recursive(in, end, e, err, depth + 1); if (err) { #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif return; } if (in == end) { err = true; #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif return; } } #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif TORRENT_ASSERT(*in == 'e'); ++in; // 'e' } break; // ---------------------------------------------- // string default: if (is_digit(boost::uint8_t(*in))) { std::string len_s = read_until(in, end, ':', err); if (err) { #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif return; } TORRENT_ASSERT(*in == ':'); ++in; // ':' int len = atoi(len_s.c_str()); ret = entry(entry::string_t); read_string(in, end, len, ret.string(), err); if (err) { #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif return; } } else { err = true; #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif return; } #ifdef TORRENT_DEBUG ret.m_type_queried = false; #endif } } } // These functions will encode data to bencoded or decode bencoded data. // // If possible, ``bdecode()`` producing a bdecode_node should be preferred // over this function. // // The entry_ class is the internal representation of the bencoded data // and it can be used to retrieve information, an entry_ can also be build by // the program and given to ``bencode()`` to encode it into the ``OutIt`` // iterator. // // The ``OutIt`` and ``InIt`` are iterators // (InputIterator_ and OutputIterator_ respectively). They // are templates and are usually instantiated as ostream_iterator_, // back_insert_iterator_ or istream_iterator_. These // functions will assume that the iterator refers to a character // (``char``). So, if you want to encode entry ``e`` into a buffer // in memory, you can do it like this:: // // std::vector buffer; // bencode(std::back_inserter(buf), e); // // .. _InputIterator: http://www.sgi.com/tech/stl/InputIterator.html // .. _OutputIterator: http://www.sgi.com/tech/stl/OutputIterator.html // .. _ostream_iterator: http://www.sgi.com/tech/stl/ostream_iterator.html // .. _back_insert_iterator: http://www.sgi.com/tech/stl/back_insert_iterator.html // .. _istream_iterator: http://www.sgi.com/tech/stl/istream_iterator.html // // If you want to decode a torrent file from a buffer in memory, you can do it like this:: // // std::vector buffer; // // ... // entry e = bdecode(buf.begin(), buf.end()); // // Or, if you have a raw char buffer:: // // const char* buf; // // ... // entry e = bdecode(buf, buf + data_size); // // Now we just need to know how to retrieve information from the entry. // // If ``bdecode()`` encounters invalid encoded data in the range given to it // it will return a default constructed ``entry`` object. template int bencode(OutIt out, const entry& e) { return detail::bencode_recursive(out, e); } template entry bdecode(InIt start, InIt end) { entry e; bool err = false; detail::bdecode_recursive(start, end, e, err, 0); #ifdef TORRENT_DEBUG TORRENT_ASSERT(e.m_type_queried == false); #endif if (err) return entry(); return e; } template entry bdecode(InIt start, InIt end, int& len) { entry e; bool err = false; InIt s = start; detail::bdecode_recursive(start, end, e, err, 0); len = std::distance(s, start); TORRENT_ASSERT(len >= 0); if (err) return entry(); return e; } } #endif // TORRENT_BENCODE_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/bitfield.hpp000066400000000000000000000167001351156116000240510ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_BITFIELD_HPP_INCLUDED #define TORRENT_BITFIELD_HPP_INCLUDED #include "libtorrent/assert.hpp" #include "libtorrent/config.hpp" #include "libtorrent/aux_/byteswap.hpp" #include // for memset and memcpy #include // for malloc, free and realloc #include // uint32_t #include // for min() namespace libtorrent { // The bitfield type stores any number of bits as a bitfield // in a heap allocated array. struct TORRENT_EXPORT bitfield { // constructs a new bitfield. The default constructor creates an empty // bitfield. ``bits`` is the size of the bitfield (specified in bits). // ``val`` is the value to initialize the bits to. If not specified // all bits are initialized to 0. // // The constructor taking a pointer ``b`` and ``bits`` copies a bitfield // from the specified buffer, and ``bits`` number of bits (rounded up to // the nearest byte boundary). bitfield(): m_buf(NULL) {} bitfield(int bits): m_buf(NULL) { resize(bits); } bitfield(int bits, bool val): m_buf(NULL) { resize(bits, val); } bitfield(char const* b, int bits): m_buf(NULL) { assign(b, bits); } bitfield(bitfield const& rhs): m_buf(NULL) { assign(rhs.data(), rhs.size()); } #if __cplusplus > 199711L bitfield(bitfield&& rhs): m_buf(rhs.m_buf) { rhs.m_buf = NULL; } #endif // hidden ~bitfield() { dealloc(); } // copy bitfield from buffer ``b`` of ``bits`` number of bits, rounded up to // the nearest byte boundary. void assign(char const* b, int bits) { resize(bits); if (bits > 0) { std::memcpy(m_buf, b, size_t((bits + 7) / 8)); clear_trailing_bits(); } } // query bit at ``index``. Returns true if bit is 1, otherwise false. bool operator[](int index) const { return get_bit(index); } bool get_bit(int index) const { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < size()); return (m_buf[index / 32] & aux::host_to_network((0x80000000 >> (index & 31)))) != 0; } // set bit at ``index`` to 0 (clear_bit) or 1 (set_bit). void clear_bit(int index) { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < size()); m_buf[index / 32] &= aux::host_to_network(~(0x80000000 >> (index & 31))); } void set_bit(int index) { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < size()); m_buf[index / 32] |= aux::host_to_network((0x80000000 >> (index & 31))); } // returns true if all bits in the bitfield are set bool all_set() const; bool none_set() const { const int words = num_words(); for (int i = 0; i < words; ++i) { if (m_buf[i] != 0) return false; } return true; } // returns the size of the bitfield in bits. int size() const { return m_buf == NULL ? 0 : int(m_buf[-1]); } int num_words() const { return (size() + 31) / 32; } // returns true if the bitfield has zero size. bool empty() const { return m_buf == NULL ? true : m_buf[-1] == 0; } // returns a pointer to the internal buffer of the bitfield. char const* data() const { return reinterpret_cast(m_buf); } #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED char const* bytes() const { return data(); } #endif // copy operator bitfield& operator=(bitfield const& rhs) { assign(rhs.data(), rhs.size()); return *this; } // count the number of bits in the bitfield that are set to 1. int count() const; struct const_iterator { friend struct bitfield; typedef bool value_type; typedef ptrdiff_t difference_type; typedef bool const* pointer; typedef bool& reference; typedef std::forward_iterator_tag iterator_category; bool operator*() { return (*buf & aux::host_to_network(bit)) != 0; } const_iterator& operator++() { inc(); return *this; } const_iterator operator++(int) { const_iterator ret(*this); inc(); return ret; } const_iterator& operator--() { dec(); return *this; } const_iterator operator--(int) { const_iterator ret(*this); dec(); return ret; } const_iterator(): buf(0), bit(0x80000000) {} bool operator==(const_iterator const& rhs) const { return buf == rhs.buf && bit == rhs.bit; } bool operator!=(const_iterator const& rhs) const { return buf != rhs.buf || bit != rhs.bit; } private: void inc() { TORRENT_ASSERT(buf); if (bit == 0x01) { bit = 0x80000000; ++buf; } else { bit >>= 1; } } void dec() { TORRENT_ASSERT(buf); if (bit == 0x80000000) { bit = 0x01; --buf; } else { bit <<= 1; } } const_iterator(boost::uint32_t const* ptr, int offset) : buf(ptr), bit(0x80000000 >> offset) {} boost::uint32_t const* buf; boost::uint32_t bit; }; const_iterator begin() const { return const_iterator(m_buf, 0); } const_iterator end() const { return const_iterator( m_buf + num_words() - (((size() & 31) == 0) ? 0 : 1), size() & 31); } // set the size of the bitfield to ``bits`` length. If the bitfield is extended, // the new bits are initialized to ``val``. void resize(int bits, bool val); void resize(int bits); // set all bits in the bitfield to 1 (set_all) or 0 (clear_all). void set_all() { if (m_buf == NULL) return; std::memset(m_buf, 0xff, size_t(num_words() * 4)); clear_trailing_bits(); } void clear_all() { if (m_buf == NULL) return; std::memset(m_buf, 0x00, size_t(num_words() * 4)); } // make the bitfield empty, of zero size. void clear() { dealloc(); } private: void clear_trailing_bits() { // clear the tail bits in the last byte if (size() & 31) m_buf[num_words() - 1] &= aux::host_to_network(0xffffffff << (32 - (size() & 31))); } void dealloc() { if (m_buf) std::free(m_buf-1); m_buf = NULL; } // the first element is not part of the bitfield, it's the // number of bits. For this purpose, the m_buf actually points // the element 1, not 0. To access the size (in bits), access // m_buf[-1] boost::uint32_t* m_buf; }; } #endif // TORRENT_BITFIELD_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/block_cache.hpp000066400000000000000000000425021351156116000245030ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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_BLOCK_CACHE #define TORRENT_BLOCK_CACHE #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/time.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/io_service_fwd.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/sliding_average.hpp" #include "libtorrent/time.hpp" #include "libtorrent/tailqueue.hpp" #include "libtorrent/linked_list.hpp" #include "libtorrent/disk_buffer_pool.hpp" #include "libtorrent/file.hpp" // for iovec_t #if TORRENT_USE_ASSERTS #include "libtorrent/disk_io_job.hpp" #endif namespace libtorrent { struct disk_io_job; class piece_manager; struct disk_buffer_pool; struct cache_status; struct block_cache_reference; struct counters; namespace aux { struct session_settings; } #if TORRENT_USE_ASSERTS class file_storage; #endif #if TORRENT_USE_ASSERTS struct piece_log_t { piece_log_t(int j, int b= -1): job(j), block(b) {} int job; int block; // these are "jobs" thar cause piece_refcount // to be incremented enum artificial_jobs { flushing = disk_io_job::num_job_ids, // 20 flush_expired, try_flush_write_blocks, try_flush_write_blocks2, flush_range, clear_outstanding_jobs, set_outstanding_jobs, last_job }; static char const* const job_names[7]; }; char const* job_name(int j); void print_piece_log(std::vector const& piece_log); void assert_print_piece(cached_piece_entry const* pe); #endif extern const char* const job_action_name[]; struct TORRENT_EXTRA_EXPORT partial_hash { partial_hash(): offset(0) {} // the number of bytes in the piece that has been hashed int offset; // the SHA-1 context hasher h; }; struct cached_block_entry { cached_block_entry() : buf(0) , refcount(0) , dirty(false) , pending(false) { #if TORRENT_USE_ASSERTS hashing_count = 0; reading_count = 0; flushing_count = 0; #endif } char* buf; enum { max_refcount = (1 << 30) - 1 }; // the number of references to this buffer. These references // might be in outstanding asynchronous requests or in peer // connection send buffers. We can't free the buffer until // all references are gone and refcount reaches 0. The buf // pointer in this struct doesn't count as a reference and // is always the last to be cleared boost::uint32_t refcount:30; // if this is true, this block needs to be written to // disk before it's freed. Typically all blocks in a piece // would either be dirty (write coalesce cache) or not dirty // (read-ahead cache). Once blocks are written to disk, the // dirty flag is cleared and effectively turns the block // into a read cache block boost::uint32_t dirty:1; // pending means that this buffer has not yet been filled in // with valid data. There's an outstanding read job for this. // If the dirty flag is set, it means there's an outstanding // write job to write this block. boost::uint32_t pending:1; #if TORRENT_USE_ASSERTS // this many of the references are held by hashing operations int hashing_count; // this block is being used in this many peer's send buffers currently int reading_count; // the number of references held by flushing operations int flushing_count; #endif }; // list_node is here to be able to link this cache entry // into one of the LRU lists struct TORRENT_EXTRA_EXPORT cached_piece_entry : list_node { cached_piece_entry(); ~cached_piece_entry(); #if __cplusplus >= 201103L cached_piece_entry(cached_piece_entry const&) = default; cached_piece_entry& operator=(cached_piece_entry const&) = default; #endif bool ok_to_evict(bool ignore_hash = false) const { return refcount == 0 && piece_refcount == 0 && !hashing && read_jobs.size() == 0 && outstanding_read == 0 && (ignore_hash || !hash || hash->offset == 0); } // storage this piece belongs to boost::shared_ptr storage; // write jobs hanging off of this piece tailqueue jobs; // read jobs waiting for the read job currently outstanding // on this piece to complete. These are executed at that point. tailqueue read_jobs; int get_piece() const { return piece; } void* get_storage() const { return storage.get(); } bool operator==(cached_piece_entry const& rhs) const { return storage.get() == rhs.storage.get() && piece == rhs.piece; } // if this is set, we'll be calculating the hash // for this piece. This member stores the interim // state while we're calculating the hash. partial_hash* hash; // set to a unique identifier of a peer that last // requested from this piece. void* last_requester; // the pointers to the block data. If this is a ghost // cache entry, there won't be any data here boost::shared_array blocks; // the last time a block was written to this piece // plus the minimum amount of time the block is guaranteed // to stay in the cache //TODO: make this 32 bits and to count seconds since the block cache was created time_point expire; boost::uint64_t piece:22; // the number of dirty blocks in this piece boost::uint64_t num_dirty:14; // the number of blocks in the cache for this piece boost::uint64_t num_blocks:14; // the total number of blocks in this piece (and the number // of elements in the blocks array) boost::uint64_t blocks_in_piece:14; // ---- 64 bit boundary ---- // while we have an outstanding async hash operation // working on this piece, 'hashing' is set to 1 // When the operation returns, this is set to 0. boost::uint32_t hashing:1; // if we've completed at least one hash job on this // piece, and returned it. This is set to one boost::uint32_t hashing_done:1; // if this is true, whenever refcount hits 0, // this piece should be deleted from the cache // (not just demoted) boost::uint32_t marked_for_deletion:1; // this is set to true once we flush blocks past // the hash cursor. Once this happens, there's // no point in keeping cache blocks around for // it in avoid_readback mode boost::uint32_t need_readback:1; // indicates which LRU list this piece is chained into enum cache_state_t { // this is the LRU list for pieces with dirty blocks write_lru, // this is the LRU list for volatile pieces. i.e. // pieces with very low cache priority. These are // always the first ones to be evicted. volatile_read_lru, // this is the LRU list for read blocks that have // been requested once read_lru1, // the is the LRU list for read blocks that have // been requested once recently, but then was evicted. // if these are requested again, they will be moved // to list 2, the frequently requested pieces read_lru1_ghost, // this is the LRU of frequently used pieces. Any // piece that has been requested by a second peer // while pulled in to list 1 by a different peer // is moved into this list read_lru2, // this is the LRU of frequently used pieces but // that has been recently evicted. If a piece in // this list is requested, it's moved back into list 2. read_lru2_ghost, num_lrus }; boost::uint32_t cache_state:3; // this is the number of threads that are currently holding // a reference to this piece. A piece may not be removed from // the cache while this is > 0 boost::uint32_t piece_refcount:7; // if this is set to one, it means there is an outstanding // flush_hashed job for this piece, and there's no need to // issue another one. boost::uint32_t outstanding_flush:1; // as long as there is a read operation outstanding on this // piece, this is set to 1. Otherwise 0. // the purpose is to make sure not two threads are reading // the same blocks at the same time. If a new read job is // added when this is 1, that new job should be hung on the // read job queue (read_jobs). boost::uint32_t outstanding_read:1; // this is set when the piece should be evicted as soon as there // no longer are any references to it. Evicted here means demoted // to a ghost list boost::uint32_t marked_for_eviction:1; // the number of blocks that have >= 1 refcount boost::uint32_t pinned:15; // ---- 32 bit boundary --- // the sum of all refcounts in all blocks boost::uint32_t refcount; #if TORRENT_USE_ASSERTS // the number of times this piece has finished hashing int hash_passes; // this is a debug facility to keep a log // of which operations have been run on this piece std::vector piece_log; bool in_storage; bool in_use; #endif }; // internal inline std::size_t hash_value(cached_piece_entry const& p) { return std::size_t(p.storage.get()) + std::size_t(p.piece); } struct TORRENT_EXTRA_EXPORT block_cache : disk_buffer_pool { block_cache(int block_size, io_service& ios , boost::function const& trigger_trim); private: typedef boost::unordered_set cache_t; public: typedef cache_t::iterator iterator; typedef cache_t::const_iterator const_iterator; // returns the number of blocks this job would cause to be read in int pad_job(disk_io_job const* j, int blocks_in_piece , int read_ahead) const; void reclaim_block(block_cache_reference const& ref); // returns a range of all pieces. This migh be a very // long list, use carefully std::pair all_pieces() const; int num_pieces() const { return int(m_pieces.size()); } list_iterator write_lru_pieces() const { return m_lru[cached_piece_entry::write_lru].iterate(); } int num_write_lru_pieces() const { return int(m_lru[cached_piece_entry::write_lru].size()); } enum eviction_mode { allow_ghost, disallow_ghost }; // mark this piece for deletion. If there are no outstanding // requests to this piece, it's removed immediately, and the // passed in iterator will be invalidated void mark_for_eviction(cached_piece_entry* p, eviction_mode mode); // similar to mark_for_eviction, except for actually marking the // piece for deletion. If the piece was actually deleted, // the function returns true bool evict_piece(cached_piece_entry* p, tailqueue& jobs , eviction_mode mode); // if this piece is in L1 or L2 proper, move it to // its respective ghost list void move_to_ghost(cached_piece_entry* p); // returns the number of bytes read on success (cache hit) // -1 on cache miss int try_read(disk_io_job* j, bool expect_no_fail = false); // called when we're reading and we found the piece we're // reading from in the hash table (not necessarily that we // hit the block we needed) void cache_hit(cached_piece_entry* p, void* requester, bool volatile_read); // free block from piece entry void free_block(cached_piece_entry* pe, int block); // erase a piece (typically from the ghost list). Reclaim all // its blocks and unlink it and free it. void erase_piece(cached_piece_entry* p); // bump the piece 'p' to the back of the LRU list it's // in (back == MRU) // this is only used for the write cache void bump_lru(cached_piece_entry* p); // move p into the correct lru queue void update_cache_state(cached_piece_entry* p); // if the piece is marked for deletion and has a refcount // of 0, this function will post any sync jobs and // delete the piece from the cache bool maybe_free_piece(cached_piece_entry* p); // either returns the piece in the cache, or allocates // a new empty piece and returns it. // cache_state is one of cache_state_t enum cached_piece_entry* allocate_piece(disk_io_job const* j, int cache_state); // looks for this piece in the cache. If it's there, returns a pointer // to it, otherwise 0. cached_piece_entry* find_piece(block_cache_reference const& ref); cached_piece_entry* find_piece(disk_io_job const* j); cached_piece_entry* find_piece(piece_manager* st, int piece); // clear free all buffers marked as dirty with // refcount of 0. void abort_dirty(cached_piece_entry* p); // used to convert dirty blocks into non-dirty ones // i.e. from being part of the write cache to being part // of the read cache. it's used when flushing blocks to disk // returns true if the piece entry was freed bool blocks_flushed(cached_piece_entry* pe, int const* flushed, int num_flushed); // adds a block to the cache, marks it as dirty and // associates the job with it. When the block is // flushed, the callback is posted cached_piece_entry* add_dirty_block(disk_io_job* j); enum { blocks_inc_refcount = 1 }; void insert_blocks(cached_piece_entry* pe, int block, file::iovec_t *iov , int iov_len, disk_io_job* j, int flags = 0); #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif // try to remove num number of read cache blocks from the cache // pick the least recently used ones first // return the number of blocks that was requested to be evicted // that couldn't be int try_evict_blocks(int num, cached_piece_entry* ignore = 0); // try to evict a single volatile piece, if there is one. void try_evict_one_volatile(); // if there are any dirty blocks void clear(tailqueue& jobs); void update_stats_counters(counters& c) const; #ifndef TORRENT_NO_DEPRECATE void get_stats(cache_status* ret) const; #endif void set_settings(aux::session_settings const& sett, error_code& ec); enum reason_t { ref_hashing = 0, ref_reading = 1, ref_flushing = 2 }; bool inc_block_refcount(cached_piece_entry* pe, int block, int reason); void dec_block_refcount(cached_piece_entry* pe, int block, int reason); int pinned_blocks() const { return m_pinned_blocks; } int read_cache_size() const { return m_read_cache_size; } private: // returns number of bytes read on success, -1 on cache miss // (just because the piece is in the cache, doesn't mean all // the blocks are there) int copy_from_piece(cached_piece_entry* p, disk_io_job* j, bool expect_no_fail = false); void free_piece(cached_piece_entry* p); int drain_piece_bufs(cached_piece_entry& p, std::vector& buf); // block container cache_t m_pieces; // linked list of all elements in m_pieces, in usage order // the most recently used are in the tail. iterating from head // to tail gives the least recently used entries first // the read-list is for read blocks and the write-list is for // dirty blocks that needs flushing before being evicted // [0] = write-LRU // [1] = read-LRU1 // [2] = read-LRU1-ghost // [3] = read-LRU2 // [4] = read-LRU2-ghost linked_list m_lru[cached_piece_entry::num_lrus]; // this is used to determine whether to evict blocks from // L1 or L2. enum cache_op_t { cache_miss, ghost_hit_lru1, ghost_hit_lru2 }; int m_last_cache_op; // the number of pieces to keep in the ARC ghost lists // this is determined by being a fraction of the cache size int m_ghost_size; // the is the max number of volatile read cache blocks are allowed in the // cache. Once this is reached, other volatile blocks will start to be // evicted. int m_max_volatile_blocks; // the number of blocks (buffers) allocated by volatile pieces. boost::uint32_t m_volatile_size; // the number of blocks in the cache // that are in the read cache boost::uint32_t m_read_cache_size; // the number of blocks in the cache // that are in the write cache boost::uint32_t m_write_cache_size; // the number of blocks that are currently sitting // in peer's send buffers. If two peers are sending // the same block, it counts as 2, even though there're // no buffer duplication boost::uint32_t m_send_buffer_blocks; // the number of blocks with a refcount > 0, i.e. // they may not be evicted int m_pinned_blocks; }; } #endif // TORRENT_BLOCK_CACHE libtorrent-rasterbar-1.1.13/include/libtorrent/bloom_filter.hpp000066400000000000000000000054201351156116000247410ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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_BLOOM_FILTER_HPP_INCLUDED #define TORRENT_BLOOM_FILTER_HPP_INCLUDED #include "libtorrent/peer_id.hpp" // for sha1_hash #include "libtorrent/config.hpp" // for sha1_hash #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include // for log() #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { TORRENT_EXTRA_EXPORT void set_bits(boost::uint8_t const* b, boost::uint8_t* bits, int len); TORRENT_EXTRA_EXPORT bool has_bits(boost::uint8_t const* b, boost::uint8_t const* bits, int len); TORRENT_EXTRA_EXPORT int count_zero_bits(boost::uint8_t const* bits, int len); template struct bloom_filter { bool find(sha1_hash const& k) const { return has_bits(&k[0], bits, N); } void set(sha1_hash const& k) { set_bits(&k[0], bits, N); } std::string to_string() const { return std::string(reinterpret_cast(&bits[0]), N); } void from_string(char const* str) { memcpy(bits, str, N); } void clear() { memset(bits, 0, N); } float size() const { const int c = (std::min)(count_zero_bits(bits, N), (N * 8) - 1); const int m = N * 8; return ::log(c / float(m)) / (2.f * ::log(1.f - 1.f/m)); } bloom_filter() { clear(); } private: boost::uint8_t bits[N]; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/broadcast_socket.hpp000066400000000000000000000125761351156116000256100ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_BROADCAST_SOCKET_HPP_INCLUDED #define TORRENT_BROADCAST_SOCKET_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/io_service_fwd.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/address.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { // TODO: 2 facto these functions out TORRENT_EXTRA_EXPORT bool is_local(address const& a); TORRENT_EXTRA_EXPORT bool is_loopback(address const& addr); TORRENT_EXTRA_EXPORT bool is_multicast(address const& addr); TORRENT_EXTRA_EXPORT bool is_any(address const& addr); TORRENT_EXTRA_EXPORT bool is_teredo(address const& addr); TORRENT_EXTRA_EXPORT int cidr_distance(address const& a1, address const& a2); bool is_ip_address(char const* host); // determines if the operating system supports IPv6 TORRENT_EXTRA_EXPORT bool supports_ipv6(); TORRENT_EXTRA_EXPORT int common_bits(unsigned char const* b1 , unsigned char const* b2, int n); typedef boost::function receive_handler_t; class TORRENT_EXTRA_EXPORT broadcast_socket { public: broadcast_socket(udp::endpoint const& multicast_endpoint); ~broadcast_socket() { close(); } void open(receive_handler_t const& handler, io_service& ios , error_code& ec, bool loopback = true); enum flags_t { broadcast = 1 }; void send(char const* buffer, int size, error_code& ec, int flags = 0); void close(); int num_send_sockets() const { return int(m_unicast_sockets.size()); } void enable_ip_broadcast(bool e); private: struct socket_entry { socket_entry(boost::shared_ptr const& s) : socket(s), broadcast(false) {} socket_entry(boost::shared_ptr const& s , address_v4 const& mask): socket(s), netmask(mask), broadcast(false) {} boost::shared_ptr socket; char buffer[1500]; udp::endpoint remote; address_v4 netmask; bool broadcast; void close() { if (!socket) return; error_code ec; socket->close(ec); } bool can_broadcast() const { error_code ec; return broadcast && netmask != address_v4() && socket->local_endpoint(ec).address().is_v4(); } address_v4 broadcast_address() const { error_code ec; #if BOOST_VERSION < 104700 return address_v4(socket->local_endpoint(ec).address().to_v4().to_ulong() | ((~netmask.to_ulong()) & 0xffffffff)); #else return address_v4::broadcast(socket->local_endpoint(ec).address().to_v4(), netmask); #endif } }; void on_receive(socket_entry* s, error_code const& ec , std::size_t bytes_transferred); void open_unicast_socket(io_service& ios, address const& addr , address_v4 const& mask); void open_multicast_socket(io_service& ios, address const& addr , bool loopback, error_code& ec); // if we're aborting, destruct the handler and return true bool maybe_abort(); // these sockets are used to // join the multicast group (on each interface) // and receive multicast messages std::list m_sockets; // these sockets are not bound to any // specific port and are used to // send messages to the multicast group // and receive unicast responses std::list m_unicast_sockets; udp::endpoint m_multicast_endpoint; receive_handler_t m_on_receive; // the number of outstanding async operations // we have on these sockets. The m_on_receive // handler may not be destructed until this reaches // 0, since it may be holding references to // the broadcast_socket itself. int m_outstanding_operations; // when set to true, we're trying to shut down // don't initiate new operations and once the // outstanding counter reaches 0, destruct // the handler object bool m_abort; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/bt_peer_connection.hpp000066400000000000000000000324041351156116000261250ustar00rootroot00000000000000/* Copyright (c) 2003-2018, Arvid Norberg Copyright (c) 2007-2018, Arvid Norberg, Un Shyam 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 #include #include #include #include "libtorrent/debug.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/buffer.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/peer_request.hpp" #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/config.hpp" #include "libtorrent/pe_crypto.hpp" namespace libtorrent { class torrent; class TORRENT_EXTRA_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_connection should handshake and verify that the // other end has the correct id bt_peer_connection(peer_connection_args const& pack); virtual void start() TORRENT_OVERRIDE; enum { // pex_msg = 1, // metadata_msg = 2, upload_only_msg = 3, holepunch_msg = 4, // recommend_msg = 5, // comment_msg = 6, dont_have_msg = 7, share_mode_msg = 8 }; ~bt_peer_connection(); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) bool supports_encryption() const { return m_encrypted; } bool rc4_encrypted() const { return m_rc4_encrypted; } void switch_send_crypto(boost::shared_ptr crypto); void switch_recv_crypto(boost::shared_ptr crypto); #endif virtual int type() const TORRENT_OVERRIDE { return peer_connection::bittorrent_connection; } 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, // DHT extension msg_dht_port, // FAST extension msg_suggest_piece = 0xd, msg_have_all, msg_have_none, msg_reject_request, msg_allowed_fast, // extension protocol message msg_extended = 20, num_supported_messages }; enum hp_message_t { // msg_types hp_rendezvous = 0, hp_connect = 1, hp_failed = 2, // error codes hp_no_such_peer = 1, hp_not_connected = 2, hp_no_support = 3, hp_no_self = 4 }; // called from the main loop when this connection has any // work to do. void on_sent(error_code const& error , std::size_t bytes_transferred) TORRENT_OVERRIDE; void on_receive(error_code const& error , std::size_t bytes_transferred) TORRENT_OVERRIDE; void on_receive_impl(std::size_t bytes_transferred); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) virtual int hit_send_barrier(std::vector& iovec) TORRENT_OVERRIDE; #endif virtual void get_specific_peer_info(peer_info& p) const TORRENT_OVERRIDE; virtual bool in_handshake() const TORRENT_OVERRIDE; bool packet_finished() const { return m_recv_buffer.packet_finished(); } #ifndef TORRENT_DISABLE_EXTENSIONS bool supports_holepunch() const { return m_holepunch_id != 0; } #endif bool support_extensions() const { return m_supports_extensions; } // 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); // DHT extension void on_dht_port(int received); // FAST extension void on_suggest_piece(int received); void on_have_all(int received); void on_have_none(int received); void on_reject_request(int received); void on_allowed_fast(int received); #ifndef TORRENT_DISABLE_EXTENSIONS void on_holepunch(); void on_extended(int received); void on_extended_handshake(); #endif typedef void (bt_peer_connection::*message_handler)(int received); // the following functions appends messages // to the send buffer void write_choke() TORRENT_OVERRIDE; void write_unchoke() TORRENT_OVERRIDE; void write_interested() TORRENT_OVERRIDE; void write_not_interested() TORRENT_OVERRIDE; void write_request(peer_request const& r) TORRENT_OVERRIDE; void write_cancel(peer_request const& r) TORRENT_OVERRIDE; void write_bitfield() TORRENT_OVERRIDE; void write_have(int index) TORRENT_OVERRIDE; void write_dont_have(int index) TORRENT_OVERRIDE; void write_piece(peer_request const& r, disk_buffer_holder& buffer) TORRENT_OVERRIDE; void write_keepalive() TORRENT_OVERRIDE; void write_handshake(); #ifndef TORRENT_DISABLE_EXTENSIONS void write_extensions(); void write_upload_only(); void write_share_mode(); void write_holepunch_msg(int type, tcp::endpoint const& ep, int error); #endif void write_metadata(std::pair req); void write_metadata_request(std::pair req); // DHT extension void write_dht_port(int listen_port); // FAST extension void write_have_all(); void write_have_none(); void write_reject_request(peer_request const&) TORRENT_OVERRIDE; void write_allow_fast(int piece) TORRENT_OVERRIDE; void write_suggest(int piece) TORRENT_OVERRIDE; void on_connected() TORRENT_OVERRIDE; void on_metadata() TORRENT_OVERRIDE; #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; time_point 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 downloading_piece_progress() const TORRENT_OVERRIDE; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // if (is_local()), we are 'a' otherwise 'b' // // 1. a -> b dhkey, pad // 2. b -> a dhkey, pad // 3. a -> b sync, payload // 4. b -> a sync, payload // 5. a -> b payload void write_pe1_2_dhkey(); void write_pe3_sync(); void write_pe4_sync(int crypto_select); void write_pe_vc_cryptofield(char* write_buf, int len , int crypto_field, int pad_size); // stream key (info hash of attached torrent) // secret is the DH shared secret // initializes m_enc_handler void init_pe_rc4_handler(char const* secret, sha1_hash const& stream_key); // Returns offset at which bytestream (src, src + src_size) // matches bytestream(target, target + target_size). // If no sync found, return -1 int get_syncoffset(char const* src, int src_size , char const* target, int target_size) const; // helper to cut down on boilerplate void rc4_decrypt(char* pos, int len); #endif public: // these functions encrypt the send buffer if m_rc4_encrypted // is true, otherwise it passes the call to the // peer_connection functions of the same names virtual void append_const_send_buffer(char const* buffer, int size , chained_buffer::free_buffer_fun destructor = &nop , void* userdata = NULL, block_cache_reference ref = block_cache_reference()) TORRENT_OVERRIDE; private: enum state_t { #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) read_pe_dhkey = 0, read_pe_syncvc, read_pe_synchash, read_pe_skey_vc, read_pe_cryptofield, read_pe_pad, read_pe_ia, init_bt_handshake, read_protocol_identifier, #else read_protocol_identifier = 0, #endif read_info_hash, read_peer_id, // handshake complete read_packet_size, read_packet }; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) enum { handshake_len = 68, dh_key_len = 96 }; #endif // state of on_receive. one of the enums in state_t boost::uint8_t m_state; // this is set to true if the handshake from // the peer indicated that it supports the // extension protocol bool m_supports_extensions:1; bool m_supports_dht_port:1; bool m_supports_fast:1; // this is set to true when we send the bitfield message. // for magnet links we can't do that right away, // since we don't know how many pieces there are in // the torrent. bool m_sent_bitfield:1; // true if we're done sending the bittorrent handshake, // and can send bittorrent messages bool m_sent_handshake:1; // set to true once we send the allowed-fast messages. This is // only done once per connection bool m_sent_allowed_fast:1; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // this is set to true after the encryption method has been // successfully negotiated (either plaintext or rc4), to signal // automatic encryption/decryption. bool m_encrypted:1; // true if rc4, false if plaintext bool m_rc4_encrypted:1; crypto_receive_buffer m_recv_buffer; #endif std::string m_client_version; // the peer ID we advertise for ourself peer_id m_our_peer_id; // 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 // separately on payload and protocol data. struct range { range(int s, int l) : start(s) , length(l) { TORRENT_ASSERT(s >= 0); TORRENT_ASSERT(l > 0); } int start; int length; }; std::vector m_payloads; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // initialized during write_pe1_2_dhkey, and destroyed on // creation of m_enc_handler. Cannot reinitialize once // initialized. boost::scoped_ptr m_dh_key_exchange; // used during an encrypted handshake then moved // into m_enc_handler if rc4 encryption is negotiated // otherwise it is destroyed when the handshake completes boost::shared_ptr m_rc4; // if encryption is negotiated, this is used for // encryption/decryption during the entire session. encryption_handler m_enc_handler; // (outgoing only) synchronize verification constant with // remote peer, this will hold rc4_decrypt(vc). Destroyed // after the sync step. boost::scoped_array m_sync_vc; // (incoming only) synchronize hash with remote peer, holds // the sync hash (hash("req1",secret)). Destroyed after the // sync step. boost::scoped_ptr m_sync_hash; #endif // #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) static const message_handler m_message_handler[num_supported_messages]; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // used to disconnect peer if sync points are not found within // the maximum number of bytes int m_sync_bytes_read; #endif #ifndef TORRENT_DISABLE_EXTENSIONS // the message ID for upload only message // 0 if not supported boost::uint8_t m_upload_only_id; // the message ID for holepunch messages boost::uint8_t m_holepunch_id; // the message ID for don't-have message boost::uint8_t m_dont_have_id; // the message ID for share mode message // 0 if not supported boost::uint8_t m_share_mode_id; char m_reserved_bits[8]; #endif #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS bool m_in_constructor; #endif }; } #endif // TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/buffer.hpp000066400000000000000000000136331351156116000235420ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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 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 #include #include // for numeric_limits #include "libtorrent/invariant_check.hpp" #include "libtorrent/assert.hpp" #include // malloc/free/realloc #include #include // for std::swap namespace libtorrent { class buffer { public: struct interval { interval() : begin(0) , end(0) {} interval(char* b, char* e) : begin(b) , end(e) {} char operator[](int index) const { TORRENT_ASSERT(begin + index < end); return begin[index]; } int left() const { TORRENT_ASSERT(end >= begin); TORRENT_ASSERT(end - begin < (std::numeric_limits::max)()); return int(end - begin); } char* begin; char* end; }; struct const_interval { const_interval(interval const& i) : begin(i.begin) , end(i.end) {} const_interval(char const* b, char const* e) : begin(b) , end(e) {} char operator[](int index) const { TORRENT_ASSERT(begin + index < end); return begin[index]; } bool operator==(const const_interval& p_interval) { return begin == p_interval.begin && end == p_interval.end; } int left() const { TORRENT_ASSERT(end >= begin); TORRENT_ASSERT(end - begin < (std::numeric_limits::max)()); return int(end - begin); } char const* begin; char const* end; }; buffer(std::size_t n = 0) : m_begin(0) , m_size(0) , m_capacity(0) { if (n) resize(n); } buffer(buffer const& b) : m_begin(0) , m_size(0) , m_capacity(0) { if (b.size() == 0) return; resize(b.size()); std::memcpy(m_begin, b.begin(), b.size()); } #if __cplusplus > 199711L buffer(buffer&& b) : m_begin(b.m_begin) , m_size(b.m_size) , m_capacity(b.m_capacity) { b.m_begin = NULL; b.m_size = b.m_capacity = 0; } buffer& operator=(buffer&& b) { if (&b == this) return *this; std::free(m_begin); m_begin = b.m_begin; m_size = b.m_size; m_capacity = b.m_capacity; b.m_begin = NULL; b.m_size = b.m_capacity = 0; return *this; } #endif buffer& operator=(buffer const& b) { if (&b == this) return *this; resize(b.size()); if (b.size() == 0) return *this; std::memcpy(m_begin, b.begin(), b.size()); return *this; } ~buffer() { std::free(m_begin); } buffer::interval data() { return interval(m_begin, m_begin + m_size); } buffer::const_interval data() const { return const_interval(m_begin, m_begin + m_size); } void resize(std::size_t n) { TORRENT_ASSERT(n < 0xffffffffu); reserve(n); m_size = boost::uint32_t(n); } void insert(char* point, char const* first, char const* last) { std::size_t p = point - m_begin; if (point == m_begin + m_size) { resize(size() + last - first); std::memcpy(m_begin + p, first, last - first); return; } resize(size() + last - first); std::memmove(m_begin + p + (last - first), m_begin + p, last - first); std::memcpy(m_begin + p, first, last - first); } void erase(char* b, char* e) { TORRENT_ASSERT(e <= m_begin + m_size); TORRENT_ASSERT(b >= m_begin); TORRENT_ASSERT(b <= e); if (e == m_begin + m_size) { resize(b - m_begin); return; } std::memmove(b, e, m_begin + m_size - e); TORRENT_ASSERT(e >= b); TORRENT_ASSERT(e - b <= (std::numeric_limits::max)()); TORRENT_ASSERT(boost::uint32_t(e - b) <= m_size); m_size -= e - b; } void clear() { m_size = 0; } std::size_t size() const { return m_size; } std::size_t capacity() const { return m_capacity; } void reserve(std::size_t n) { if (n <= capacity()) return; TORRENT_ASSERT(n > 0); TORRENT_ASSERT(n < 0xffffffffu); char* tmp = static_cast(std::realloc(m_begin, n)); #ifndef BOOST_NO_EXCEPTIONS if (tmp == NULL) throw std::bad_alloc(); #endif m_begin = tmp; m_capacity = boost::uint32_t(n); } bool empty() const { return m_size == 0; } char& operator[](std::size_t i) { TORRENT_ASSERT(i < size()); return m_begin[i]; } char const& operator[](std::size_t i) const { TORRENT_ASSERT(i < size()); return m_begin[i]; } char* begin() { return m_begin; } char const* begin() const { return m_begin; } char* end() { return m_begin + m_size; } char const* end() const { return m_begin + m_size; } void swap(buffer& b) { using std::swap; swap(m_begin, b.m_begin); swap(m_size, b.m_size); swap(m_capacity, b.m_capacity); } private: char* m_begin; boost::uint32_t m_size; boost::uint32_t m_capacity; }; } #endif // LIBTORRENT_BUFFER_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/build_config.hpp000066400000000000000000000044361351156116000247160ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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_BUILD_CONFIG_HPP_INCLUDED #define TORRENT_BUILD_CONFIG_HPP_INCLUDED #include "libtorrent/config.hpp" #include #include // TODO: 2 instead of using a dummy function to cause link errors when // incompatible build configurations are used, make the namespace name // depend on the configuration, and have a using declaration in the headers // to pull it into libtorrent. #if TORRENT_USE_IPV6 #define TORRENT_CFG_IPV6 ipv6_ #else #define TORRENT_CFG_IPV6 noipv6_ #endif #ifdef TORRENT_NO_DEPRECATE #define TORRENT_CFG_DEPR nodeprecate_ #else #define TORRENT_CFG_DEPR deprecated_ #endif #define TORRENT_CFG \ BOOST_PP_CAT(TORRENT_CFG_IPV6, \ TORRENT_CFG_DEPR) #define TORRENT_CFG_STRING BOOST_PP_STRINGIZE(TORRENT_CFG) #endif libtorrent-rasterbar-1.1.13/include/libtorrent/chained_buffer.hpp000066400000000000000000000105501351156116000252100ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_CHAINED_BUFFER_HPP_INCLUDED #define TORRENT_CHAINED_BUFFER_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/disk_io_job.hpp" // for block_cache_reference #include "libtorrent/debug.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include // for memcpy #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct TORRENT_EXTRA_EXPORT chained_buffer : private single_threaded { chained_buffer(): m_bytes(0), m_capacity(0) { thread_started(); #if TORRENT_USE_ASSERTS m_destructed = false; #endif } // destructs/frees the buffer (1st arg) with // 2nd argument as userdata typedef void (*free_buffer_fun)(char*, void*, block_cache_reference ref); struct buffer_t { free_buffer_fun free_fun; void* userdata; char* buf; // the first byte of the buffer char* start; // the first byte to send/receive in the buffer int size; // the total size of the buffer int used_size; // this is the number of bytes to send/receive block_cache_reference ref; }; bool empty() const { return m_bytes == 0; } int size() const { return m_bytes; } int capacity() const { return m_capacity; } void pop_front(int bytes_to_pop); void append_buffer(char* buffer, int s, int used_size , free_buffer_fun destructor, void* userdata , block_cache_reference ref = block_cache_reference()); void prepend_buffer(char* buffer, int s, int used_size , free_buffer_fun destructor, void* userdata , block_cache_reference ref = block_cache_reference()); // returns the number of bytes available at the // end of the last chained buffer. int space_in_last_buffer(); // tries to copy the given buffer to the end of the // last chained buffer. If there's not enough room // it returns false char* append(char const* buf, int s); // tries to allocate memory from the end // of the last buffer. If there isn't // enough room, returns 0 char* allocate_appendix(int s); std::vector const& build_iovec(int to_send); void clear(); void build_mutable_iovec(int bytes, std::vector& vec); ~chained_buffer(); private: template void build_vec(int bytes, std::vector& vec); // this is the list of all the buffers we want to // send std::deque m_vec; // this is the number of bytes in the send buf. // this will always be equal to the sum of the // size of all buffers in vec int m_bytes; // the total size of all buffers in the chain // including unused space int m_capacity; // this is the vector of buffers used when // invoking the async write call std::vector m_tmp_vec; #if TORRENT_USE_ASSERTS bool m_destructed; #endif }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/choker.hpp000066400000000000000000000044201351156116000235360ustar00rootroot00000000000000/* Copyright (c) 2014-2018, 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/config.hpp" #include "libtorrent/time.hpp" // for time_duration #include namespace libtorrent { namespace aux { struct session_settings; } class peer_connection; // sorts the vector of peers in-place. When returning, the top unchoke slots // elements are the peers we should unchoke. This is similar to a partial // sort. Only the unchoke slots first elements are sorted. // the return value are the number of peers that should be unchoked. This // is also the number of elements that are valid at the beginning of the // peer list. Peers beyond this initial range are not sorted. TORRENT_EXTRA_EXPORT int unchoke_sort(std::vector& peers , int max_upload_rate, time_duration unchoke_interval , aux::session_settings const& sett); } libtorrent-rasterbar-1.1.13/include/libtorrent/close_reason.hpp000066400000000000000000000114651351156116000247460ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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_CLOSE_REASON_HPP #define TORRENT_CLOSE_REASON_HPP #include "libtorrent/error_code.hpp" namespace libtorrent { // internal: these are all the reasons to disconnect a peer // all reasons caused by the peer sending unexpected data // are 256 and up. enum close_reason_t { // no reason specified. Generic close. close_no_reason = 0, // we're already connected to close_duplicate_peer_id, // this torrent has been removed, paused or stopped from this client. close_torrent_removed, // client failed to allocate necessary memory for this peer connection close_no_memory, // the source port of this peer is blocked close_port_blocked, // the source IP has been blocked close_blocked, // both ends of the connection are upload-only. staying connected would // be redundant close_upload_to_upload, // connection was closed because the other end is upload only and does // not have any pieces we're interested in close_not_interested_upload_only, // peer connection timed out (generic timeout) close_timeout, // the peers have not been interested in each other for a very long time. // disconnect close_timed_out_interest, // the peer has not sent any message in a long time. close_timed_out_activity, // the peer did not complete the handshake in too long close_timed_out_handshake, // the peer sent an interested message, but did not send a request // after a very long time after being unchoked. close_timed_out_request, // the encryption mode is blocked close_protocol_blocked, // the peer was disconnected in the hopes of finding a better peer // in the swarm close_peer_churn, // we have too many peers connected close_too_many_connections, // we have too many file-descriptors open close_too_many_files, // the encryption handshake failed close_encryption_error = 256, // the info hash sent as part of the handshake was not what we expected close_invalid_info_hash, close_self_connection, // the metadata received matched the info-hash, but failed to parse. // this is either someone finding a SHA1 collision, or the author of // the magnet link creating it from an invalid torrent close_invalid_metadata, // the advertised metadata size close_metadata_too_big, // invalid bittorrent messages close_message_too_big, close_invalid_message_id, close_invalid_message, close_invalid_piece_message, close_invalid_have_message, close_invalid_bitfield_message, close_invalid_choke_message, close_invalid_unchoke_message, close_invalid_interested_message, close_invalid_not_interested_message, close_invalid_request_message, close_invalid_reject_message, close_invalid_allow_fast_message, close_invalid_extended_message, close_invalid_cancel_message, close_invalid_dht_port_message, close_invalid_suggest_message, close_invalid_have_all_message, close_invalid_dont_have_message, close_invalid_have_none_message, close_invalid_pex_message, close_invalid_metadata_request_message, close_invalid_metadata_message, close_invalid_metadata_offset, // the peer sent a request while being choked close_request_when_choked, // the peer sent corrupt data close_corrupt_pieces, close_pex_message_too_big, close_pex_too_frequent }; close_reason_t error_to_close_reason(error_code const& ec); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/config.hpp000066400000000000000000000434301351156116000235340ustar00rootroot00000000000000/* Copyright (c) 2005-2018, 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 "libtorrent/aux_/disable_warnings_push.hpp" #define _FILE_OFFSET_BITS 64 #if !defined _MSC_VER || _MSC_VER >= 1600 #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS 1 #endif #endif #include #include #include #include // for snprintf #include // for IOV_MAX #include "libtorrent/export.hpp" #ifdef __linux__ #include // for LINUX_VERSION_CODE and KERNEL_VERSION #endif // __linux #if defined TORRENT_DEBUG_BUFFERS && !defined TORRENT_DISABLE_POOL_ALLOCATOR #error TORRENT_DEBUG_BUFFERS only works if you also disable pool allocators with TORRENT_DISABLE_POOL_ALLOCATOR #endif #if !defined BOOST_ASIO_SEPARATE_COMPILATION && !defined BOOST_ASIO_DYN_LINK #define BOOST_ASIO_SEPARATE_COMPILATION #endif #ifndef _MSC_VER #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS 1 #endif #include // for PRId64 et.al. #endif #ifndef PRId64 // MinGW uses microsofts runtime #if defined _MSC_VER || defined __MINGW32__ #define PRId64 "I64d" #define PRIu64 "I64u" #define PRIx64 "I64x" #define PRIu32 "u" #else #define PRId64 "lld" #define PRIu64 "llu" #define PRIx64 "llx" #define PRIu32 "u" #endif #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" // ====== CLANG ======== #if defined __clang__ # if !defined TORRENT_BUILDING_LIBRARY // TODO: figure out which version of clang this is supported in # define TORRENT_DEPRECATED_ENUM __attribute__ ((deprecated)) # define TORRENT_DEPRECATED_MEMBER __attribute__ ((deprecated)) # endif // ======= GCC ========= #elif defined __GNUC__ // deprecation markup is only enabled when libtorrent // headers are included by clients, not while building // libtorrent itself # if __GNUC__ >= 3 && !defined TORRENT_BUILDING_LIBRARY # define TORRENT_DEPRECATED __attribute__ ((deprecated)) # endif # if __GNUC__ >= 6 && !defined TORRENT_BUILDING_LIBRARY # define TORRENT_DEPRECATED_ENUM __attribute__ ((deprecated)) # define TORRENT_DEPRECATED_MEMBER __attribute__ ((deprecated)) # endif // ======= SUNPRO ========= #elif defined __SUNPRO_CC // SunPRO seems to have an overly-strict // definition of POD types and doesn't // seem to allow boost::array in unions #define TORRENT_BROKEN_UNIONS 1 // ======= MSVC ========= #elif defined BOOST_MSVC #pragma warning(disable: 4258) #pragma warning(disable: 4251) // class X needs to have dll-interface to be used by clients of class Y #pragma warning(disable:4251) // '_vsnprintf': This function or variable may be unsafe #pragma warning(disable:4996) // deprecation markup is only enabled when libtorrent // headers are included by clients, not while building // libtorrent itself #if !defined TORRENT_BUILDING_LIBRARY # define TORRENT_DEPRECATED __declspec(deprecated) #endif #endif // ======= PLATFORMS ========= // set up defines for target environments // ==== AMIGA === #if defined __AMIGA__ || defined __amigaos__ || defined __AROS__ #define TORRENT_AMIGA #define TORRENT_USE_IPV6 0 #define TORRENT_USE_BOOST_THREAD 0 #define TORRENT_USE_IOSTREAM 0 // set this to 1 to disable all floating point operations // (disables some float-dependent APIs) #define TORRENT_NO_FPU 1 #define TORRENT_USE_I2P 0 #ifndef TORRENT_USE_ICONV #define TORRENT_USE_ICONV 0 #endif // ==== Darwin/BSD === #elif (defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __NetBSD__ \ || defined __OpenBSD__ || defined __bsdi__ || defined __DragonFly__ \ || defined __FreeBSD_kernel__ #define TORRENT_BSD // we don't need iconv on mac, because // the locale is always utf-8 #if defined __APPLE__ # define TORRENT_USE_OSATOMIC 1 # ifndef TORRENT_USE_ICONV # define TORRENT_USE_ICONV 0 # define TORRENT_USE_LOCALE 0 # endif #include #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 // on OSX, use the built-in common crypto for built-in # if !defined TORRENT_USE_OPENSSL && !defined TORRENT_USE_GCRYPT # define TORRENT_USE_COMMONCRYPTO 1 # endif // TORRENT_USE_OPENSSL #endif // MAC_OS_X_VERSION_MIN_REQUIRED // execinfo.h is available in the MacOS X 10.5 SDK. #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 #define TORRENT_USE_EXECINFO 1 #endif #endif // __APPLE__ #define TORRENT_HAVE_MMAP 1 #define TORRENT_HAS_FALLOCATE 0 #define TORRENT_USE_IFADDRS 1 #define TORRENT_USE_SYSCTL 1 #define TORRENT_USE_IFCONF 1 // ==== LINUX === #elif defined __linux__ #define TORRENT_LINUX #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) # define TORRENT_USE_PREADV 1 # define TORRENT_USE_PREAD 0 #else # define TORRENT_USE_PREADV 0 # define TORRENT_USE_PREAD 1 #endif #define TORRENT_HAVE_MMAP 1 #define TORRENT_USE_NETLINK 1 #define TORRENT_USE_IFCONF 1 #define TORRENT_HAS_SALEN 0 // ===== ANDROID ===== (almost linux, sort of) #if defined __ANDROID__ #define TORRENT_USE_PREADV 0 #define TORRENT_USE_PREAD 1 #define TORRENT_ANDROID #define TORRENT_HAS_FALLOCATE 0 #define TORRENT_USE_ICONV 0 #define TORRENT_USE_IFADDRS 0 #define TORRENT_USE_MEMALIGN 1 #define TORRENT_USE_FDATASYNC 0 #else // ANDROID #define TORRENT_USE_IFADDRS 1 #define TORRENT_USE_POSIX_MEMALIGN 1 #define TORRENT_USE_FDATASYNC 1 // posix_fallocate() is available under this condition #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L #define TORRENT_HAS_FALLOCATE 1 #else #define TORRENT_HAS_FALLOCATE 0 #endif #endif // ANDROID #if defined __GLIBC__ && ( defined __x86_64__ || defined __i386 \ || defined _M_X64 || defined _M_IX86 ) #define TORRENT_USE_EXECINFO 1 #endif // ==== MINGW === #elif defined __MINGW32__ #define TORRENT_MINGW #define TORRENT_WINDOWS #ifndef TORRENT_USE_ICONV # define TORRENT_USE_ICONV 0 # define TORRENT_USE_LOCALE 1 #endif #define TORRENT_USE_RLIMIT 0 #define TORRENT_USE_NETLINK 0 #define TORRENT_USE_GETADAPTERSADDRESSES 1 #define TORRENT_HAS_SALEN 0 #define TORRENT_USE_GETIPFORWARDTABLE 1 #define TORRENT_USE_INTERLOCKED_ATOMIC 1 #ifndef TORRENT_USE_UNC_PATHS # define TORRENT_USE_UNC_PATHS 1 #endif // these are emulated on windows #define TORRENT_USE_PREADV 1 #define TORRENT_USE_PWRITEV 1 // ==== WINDOWS === #elif defined _WIN32 #define TORRENT_WINDOWS #ifndef TORRENT_USE_GETIPFORWARDTABLE # define TORRENT_USE_GETIPFORWARDTABLE 1 #endif #define TORRENT_USE_GETADAPTERSADDRESSES 1 #define TORRENT_HAS_SALEN 0 // windows has its own functions to convert #ifndef TORRENT_USE_ICONV # define TORRENT_USE_ICONV 0 # define TORRENT_USE_LOCALE 1 #endif #define TORRENT_USE_RLIMIT 0 #define TORRENT_HAS_FALLOCATE 0 #define TORRENT_USE_INTERLOCKED_ATOMIC 1 #ifndef TORRENT_USE_UNC_PATHS # define TORRENT_USE_UNC_PATHS 1 #endif // these are emulated on windows #define TORRENT_USE_PREADV 1 #define TORRENT_USE_PWRITEV 1 // ==== WINRT === #if defined(WINAPI_FAMILY_PARTITION) # if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) \ && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) # define TORRENT_WINRT # define TORRENT_USE_CRYPTOGRAPHIC_BUFFER 1 # endif #endif // ==== SOLARIS === #elif defined sun || defined __sun #define TORRENT_SOLARIS #define TORRENT_COMPLETE_TYPES_REQUIRED 1 #define TORRENT_USE_IFCONF 1 #define TORRENT_HAS_SALEN 0 #define TORRENT_HAS_SEM_RELTIMEDWAIT 1 #define TORRENT_HAVE_MMAP 1 #define TORRENT_USE_SOLARIS_ATOMIC 1 // ==== BEOS === #elif defined __BEOS__ || defined __HAIKU__ #define TORRENT_BEOS #include // B_PATH_NAME_LENGTH #define TORRENT_HAS_FALLOCATE 0 #define TORRENT_USE_BEOS_ATOMIC 1 #ifndef TORRENT_USE_ICONV #define TORRENT_USE_ICONV 0 #endif // ==== GNU/Hurd === #elif defined __GNU__ #define TORRENT_HURD #define TORRENT_USE_IFADDRS 1 #define TORRENT_USE_IFCONF 1 // ==== eCS(OS/2) === #elif defined __OS2__ #define TORRENT_OS2 #define TORRENT_HAS_FALLOCATE 0 #define TORRENT_USE_IFCONF 1 #define TORRENT_USE_SYSCTL 1 #define TORRENT_USE_IPV6 0 #define TORRENT_USE_WRITEV 0 #define TORRENT_USE_READV 0 #else #ifdef _MSC_VER #pragma message ( "unknown OS, assuming BSD" ) #else #warning "unknown OS, assuming BSD" #endif #define TORRENT_BSD #endif #if defined __GNUC__ && !(defined TORRENT_USE_OSATOMIC \ || defined TORRENT_USE_INTERLOCKED_ATOMIC \ || defined TORRENT_USE_BEOS_ATOMIC \ || defined TORRENT_USE_SOLARIS_ATOMIC) // atomic operations in GCC were introduced in 4.1.1 # if (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1 && __GNUC_PATCHLEVEL__ >= 1) || __GNUC__ > 4 # define TORRENT_USE_GCC_ATOMIC 1 # endif #endif // on windows, NAME_MAX refers to Unicode characters // on linux it refers to bytes (utf-8 encoded) // TODO: Make this count Unicode characters instead of bytes on windows // windows #if defined FILENAME_MAX #define TORRENT_MAX_PATH FILENAME_MAX // beos #elif defined B_PATH_NAME_LENGTH #define TORRENT_MAX_PATH B_PATH_NAME_LENGTH // solaris #elif defined MAXPATH #define TORRENT_MAX_PATH MAXPATH // none of the above #else // this is the maximum number of characters in a // path element / filename on windows and also on many filesystems commonly used // on linux #define TORRENT_MAX_PATH 255 #ifdef _MSC_VER #pragma message ( "unknown platform, assuming the longest path is 255" ) #else #warning "unknown platform, assuming the longest path is 255" #endif #endif #define TORRENT_UNUSED(x) (void)(x) #if (defined _MSC_VER && _MSC_VER < 1900) && !defined TORRENT_MINGW #include // internal #ifdef __cplusplus inline #else static #endif int snprintf(char* buf, int len, char const* fmt, ...) { va_list lp; int ret; va_start(lp, fmt); ret = _vsnprintf(buf, len, fmt, lp); va_end(lp); if (ret < 0) { buf[len-1] = 0; ret = len-1; } return ret; } #define strtoll _strtoi64 #endif // at the highest warning level, clang actually warns about functions // that could be marked noreturn. #if defined __clang__ || defined __GNUC__ #define TORRENT_NO_RETURN __attribute((noreturn)) #else #define TORRENT_NO_RETURN #endif #ifdef _GLIBCXX_USE_NOEXCEPT #define TORRENT_EXCEPTION_THROW_SPECIFIER _GLIBCXX_USE_NOEXCEPT #else #if __cplusplus <= 199711L || defined BOOST_NO_CXX11_NOEXCEPT #define TORRENT_EXCEPTION_THROW_SPECIFIER throw() #else #define TORRENT_EXCEPTION_THROW_SPECIFIER noexcept #endif #endif // __GLIBC__ #if __cplusplus <= 199711L || defined BOOST_NO_CXX11_FINAL #define TORRENT_FINAL #else #define TORRENT_FINAL final #endif #if __cplusplus <= 199711L || defined BOOST_NO_CXX11_FINAL #define TORRENT_OVERRIDE #else #define TORRENT_OVERRIDE override #endif #if defined __GNUC__ || defined __clang__ #define TORRENT_FORMAT(fmt, ellipsis) __attribute__((__format__(__printf__, fmt, ellipsis))) #else #define TORRENT_FORMAT(fmt, ellipsis) #endif #ifndef TORRENT_USE_INTERLOCKED_ATOMIC #define TORRENT_USE_INTERLOCKED_ATOMIC 0 #endif #ifndef TORRENT_USE_GCC_ATOMIC #define TORRENT_USE_GCC_ATOMIC 0 #endif #ifndef TORRENT_USE_OSATOMIC #define TORRENT_USE_OSATOMIC 0 #endif #ifndef TORRENT_USE_BEOS_ATOMIC #define TORRENT_USE_BEOS_ATOMIC 0 #endif // libiconv presence detection is not implemented yet #ifndef TORRENT_USE_ICONV #define TORRENT_USE_ICONV 1 #endif #ifndef TORRENT_HAS_SALEN #define TORRENT_HAS_SALEN 1 #endif #ifndef TORRENT_USE_GETADAPTERSADDRESSES #define TORRENT_USE_GETADAPTERSADDRESSES 0 #endif #ifndef TORRENT_USE_NETLINK #define TORRENT_USE_NETLINK 0 #endif #ifndef TORRENT_USE_EXECINFO #define TORRENT_USE_EXECINFO 0 #endif #ifndef TORRENT_USE_SYSCTL #define TORRENT_USE_SYSCTL 0 #endif #ifndef TORRENT_USE_GETIPFORWARDTABLE #define TORRENT_USE_GETIPFORWARDTABLE 0 #endif #ifndef TORRENT_HAS_SEM_RELTIMEDWAIT #define TORRENT_HAS_SEM_RELTIMEDWAIT 0 #endif #ifndef TORRENT_USE_MEMALIGN #define TORRENT_USE_MEMALIGN 0 #endif #ifndef TORRENT_USE_POSIX_MEMALIGN #define TORRENT_USE_POSIX_MEMALIGN 0 #endif #ifndef TORRENT_USE_LOCALE #define TORRENT_USE_LOCALE 0 #endif #ifndef TORRENT_BROKEN_UNIONS #define TORRENT_BROKEN_UNIONS 0 #endif #ifndef TORRENT_USE_WSTRING #if !defined BOOST_NO_STD_WSTRING #define TORRENT_USE_WSTRING 1 #else #define TORRENT_USE_WSTRING 0 #endif // BOOST_NO_STD_WSTRING #endif // TORRENT_USE_WSTRING #ifndef TORRENT_HAS_FALLOCATE #define TORRENT_HAS_FALLOCATE 1 #endif #ifndef TORRENT_DEPRECATED #define TORRENT_DEPRECATED #endif #ifndef TORRENT_DEPRECATED_ENUM #define TORRENT_DEPRECATED_ENUM #endif #ifndef TORRENT_DEPRECATED_MEMBER #define TORRENT_DEPRECATED_MEMBER #endif #ifndef TORRENT_USE_COMMONCRYPTO #define TORRENT_USE_COMMONCRYPTO 0 #endif #ifndef TORRENT_HAVE_MMAP #define TORRENT_HAVE_MMAP 0 #endif #ifndef TORRENT_COMPLETE_TYPES_REQUIRED #define TORRENT_COMPLETE_TYPES_REQUIRED 0 #endif #ifndef TORRENT_USE_FDATASYNC #define TORRENT_USE_FDATASYNC 0 #endif #ifndef TORRENT_USE_UNC_PATHS #define TORRENT_USE_UNC_PATHS 0 #endif #ifndef TORRENT_USE_RLIMIT #define TORRENT_USE_RLIMIT 1 #endif #ifndef TORRENT_USE_IFADDRS #define TORRENT_USE_IFADDRS 0 #endif #ifndef TORRENT_USE_IPV6 #define TORRENT_USE_IPV6 1 #endif // if preadv() exists, we assume pwritev() does as well #ifndef TORRENT_USE_PREADV #define TORRENT_USE_PREADV 0 #endif // if pread() exists, we assume pwrite() does as well #ifndef TORRENT_USE_PREAD #define TORRENT_USE_PREAD 1 #endif #ifndef TORRENT_NO_FPU #define TORRENT_NO_FPU 0 #endif #ifndef TORRENT_USE_IOSTREAM #ifndef BOOST_NO_IOSTREAM #define TORRENT_USE_IOSTREAM 1 #else #define TORRENT_USE_IOSTREAM 0 #endif #endif // whether function-local static variables are thread safe. In c++11 and later // they are (except msvc) #ifndef TORRENT_THREADSAFE_STATIC #if __cplusplus < 199711L || (defined _MSC_VER && _MSC_VER <= 1800) #define TORRENT_THREADSAFE_STATIC 0 #else #define TORRENT_THREADSAFE_STATIC 1 #endif #endif #ifndef TORRENT_USE_I2P #define TORRENT_USE_I2P 1 #endif #ifndef TORRENT_HAS_BOOST_UNORDERED #define TORRENT_HAS_BOOST_UNORDERED 1 #endif #if !defined TORRENT_IOV_MAX #ifdef IOV_MAX #define TORRENT_IOV_MAX IOV_MAX #else #define TORRENT_IOV_MAX INT_MAX #endif #endif #if !defined(TORRENT_READ_HANDLER_MAX_SIZE) # if defined _GLIBCXX_DEBUG || !defined NDEBUG # define TORRENT_READ_HANDLER_MAX_SIZE 400 # else // if this is not divisible by 8, we're wasting space # define TORRENT_READ_HANDLER_MAX_SIZE 336 # endif #endif #if !defined(TORRENT_WRITE_HANDLER_MAX_SIZE) # if defined _GLIBCXX_DEBUG || !defined NDEBUG # define TORRENT_WRITE_HANDLER_MAX_SIZE 400 # else // if this is not divisible by 8, we're wasting space # define TORRENT_WRITE_HANDLER_MAX_SIZE 336 # endif #endif #if defined _MSC_VER && _MSC_VER <= 1200 // this is here to provide a standard-conforming for // keyword for old versions of msvc. The pragmas are // there to silence the warning it produces by using // a constant as conditional #define for \ __pragma( warning(push) ) \ __pragma( warning(disable:4127) ) \ if (false) {} else \ __pragma( warning(pop) ) for #endif #if TORRENT_BROKEN_UNIONS #define TORRENT_UNION struct #else #define TORRENT_UNION union #endif #if defined __GNUC__ #define TORRENT_FUNCTION __PRETTY_FUNCTION__ #else #define TORRENT_FUNCTION __FUNCTION__ #endif // debug builds have asserts enabled by default, release // builds have asserts if they are explicitly enabled by // the release_asserts macro. #ifndef TORRENT_USE_ASSERTS #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS #define TORRENT_USE_ASSERTS 1 #else #define TORRENT_USE_ASSERTS 0 #endif #endif // TORRENT_USE_ASSERTS #if defined TORRENT_DEBUG && TORRENT_USE_ASSERTS \ && !defined TORRENT_DISABLE_INVARIANT_CHECKS #define TORRENT_USE_INVARIANT_CHECKS 1 #else #define TORRENT_USE_INVARIANT_CHECKS 0 #endif // for non-exception builds #ifdef BOOST_NO_EXCEPTIONS #define TORRENT_TRY if (true) #define TORRENT_CATCH(x) else if (false) #define TORRENT_CATCH_ALL else if (false) #define TORRENT_DECLARE_DUMMY(x, y) x y #else #define TORRENT_TRY try #define TORRENT_CATCH(x) catch(x) #define TORRENT_CATCH_ALL catch(...) #define TORRENT_DECLARE_DUMMY(x, y) #endif // BOOST_NO_EXCEPTIONS // SSE is x86 / amd64 specific. On top of that, we only // know how to access it on msvc and gcc (and gcc compatibles). // GCC requires the user to enable SSE support in order for // the program to have access to the intrinsics, this is // indicated by the __SSE4_1__ macro #ifndef TORRENT_HAS_SSE #if (defined _M_AMD64 || defined _M_IX86 || defined _M_X64 \ || defined __amd64__ || defined __i386 || defined __i386__ \ || defined __x86_64__ || defined __x86_64) \ && (defined __GNUC__ || (defined _MSC_VER && _MSC_VER >= 1600)) #define TORRENT_HAS_SSE 1 #else #define TORRENT_HAS_SSE 0 #endif #endif // TORRENT_HAS_SSE namespace libtorrent {} namespace lt = libtorrent; #endif // TORRENT_CONFIG_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/copy_ptr.hpp000066400000000000000000000044431351156116000241270ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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_COPY_PTR #define TORRENT_COPY_PTR namespace libtorrent { template struct copy_ptr { copy_ptr(): m_ptr(0) {} copy_ptr(T* t): m_ptr(t) {} copy_ptr(copy_ptr const& p): m_ptr(p.m_ptr ? new T(*p.m_ptr) : 0) {} void reset(T* t = 0) { delete m_ptr; m_ptr = t; } copy_ptr& operator=(copy_ptr const& p) { delete m_ptr; m_ptr = p.m_ptr ? new T(*p.m_ptr) : 0; return *this; } T* operator->() { return m_ptr; } T const* operator->() const { return m_ptr; } T& operator*() { return *m_ptr; } T const& operator*() const { return *m_ptr; } void swap(copy_ptr& p) { T* tmp = m_ptr; m_ptr = p.m_ptr; p.m_ptr = tmp; } operator bool() const { return m_ptr != 0; } ~copy_ptr() { delete m_ptr; } private: T* m_ptr; }; } #endif // TORRENT_COPY_PTR libtorrent-rasterbar-1.1.13/include/libtorrent/crc32c.hpp000066400000000000000000000035531351156116000233500ustar00rootroot00000000000000/* Copyright (c) 2014-2018, 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_CRC32C_HPP_INCLUDE #define TORRENT_CRC32C_HPP_INCLUDE #include #include "libtorrent/export.hpp" namespace libtorrent { // this is the crc32c (Castagnoli) polynomial TORRENT_EXTRA_EXPORT boost::uint32_t crc32c_32(boost::uint32_t v); TORRENT_EXTRA_EXPORT boost::uint32_t crc32c(boost::uint64_t const* v , int num_words); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/create_torrent.hpp000066400000000000000000000457631351156116000253220ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_CREATE_TORRENT_HPP_INCLUDED #define TORRENT_CREATE_TORRENT_HPP_INCLUDED #include "libtorrent/bencode.hpp" #include "libtorrent/file_storage.hpp" #include "libtorrent/config.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/file.hpp" // for combine_path etc. #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" // OVERVIEW // // This section describes the functions and classes that are used // to create torrent files. It is a layered API with low level classes // and higher level convenience functions. A torrent is created in 4 // steps: // // 1. first the files that will be part of the torrent are determined. // 2. the torrent properties are set, such as tracker url, web seeds, // DHT nodes etc. // 3. Read through all the files in the torrent, SHA-1 all the data // and set the piece hashes. // 4. The torrent is bencoded into a file or buffer. // // If there are a lot of files and or deep directory hierarchies to // traverse, step one can be time consuming. // // Typically step 3 is by far the most time consuming step, since it // requires to read all the bytes from all the files in the torrent. // // All of these classes and functions are declared by including // ``libtorrent/create_torrent.hpp``. // // example: // // .. code:: c++ // // file_storage fs; // // // recursively adds files in directories // add_files(fs, "./my_torrent"); // // create_torrent t(fs); // t.add_tracker("http://my.tracker.com/announce"); // t.set_creator("libtorrent example"); // // // reads the files and calculates the hashes // set_piece_hashes(t, "."); // // ofstream out("my_torrent.torrent", std::ios_base::binary); // bencode(std::ostream_iterator(out), t.generate()); // namespace libtorrent { class torrent_info; // This class holds state for creating a torrent. After having added // all information to it, call create_torrent::generate() to generate // the torrent. The entry that's returned can then be bencoded into a // .torrent file using bencode(). struct TORRENT_EXPORT create_torrent { // flags for create_torrent::create_torrent(). enum flags_t { // This will insert pad files to align the files to piece boundaries, for // optimized disk-I/O. This will minimize the number of bytes of pad- // files, to keep the impact down for clients that don't support // them. optimize_alignment = 1, #ifndef TORRENT_NO_DEPRECATE // same as optimize_alignment, for backwards compatibility optimize TORRENT_DEPRECATED_ENUM = 1, #endif // This will create a merkle hash tree torrent. A merkle torrent cannot // be opened in clients that don't specifically support merkle torrents. // The benefit is that the resulting torrent file will be much smaller and // not grow with more pieces. When this option is specified, it is // recommended to have a fairly small piece size, say 64 kiB. // When creating merkle torrents, the full hash tree is also generated // and should be saved off separately. It is accessed through the // create_torrent::merkle_tree() function. merkle = 2, // This will include the file modification time as part of the torrent. // This is not enabled by default, as it might cause problems when you // create a torrent from separate files with the same content, hoping to // yield the same info-hash. If the files have different modification times, // with this option enabled, you would get different info-hashes for the // files. modification_time = 4, // If this flag is set, files that are symlinks get a symlink attribute // set on them and their data will not be included in the torrent. This // is useful if you need to reconstruct a file hierarchy which contains // symlinks. symlinks = 8, // to create a torrent that can be updated via a *mutable torrent* // (see `BEP 38`_). This also needs to be enabled for torrents that update // another torrent. // // .. _`BEP 38`: http://www.bittorrent.org/beps/bep_0038.html mutable_torrent_support = 16 }; // The ``piece_size`` is the size of each piece in bytes. It must // be a multiple of 16 kiB. If a piece size of 0 is specified, a // piece_size will be calculated such that the torrent file is roughly 40 kB. // // If a ``pad_file_limit`` is specified (other than -1), any file larger than // the specified number of bytes will be preceded by a pad file to align it // with the start of a piece. The pad_file_limit is ignored unless the // ``optimize_alignment`` flag is passed. Typically it doesn't make sense // to set this any lower than 4 kiB. // // The overload that takes a ``torrent_info`` object will make a verbatim // copy of its info dictionary (to preserve the info-hash). The copy of // the info dictionary will be used by create_torrent::generate(). This means // that none of the member functions of create_torrent that affects // the content of the info dictionary (such as ``set_hash()``), will // have any affect. // // The ``flags`` arguments specifies options for the torrent creation. It can // be any combination of the flags defined by create_torrent::flags_t. // // ``alignment`` is used when pad files are enabled. This is the size // eligible files are aligned to. The default is -1, which means the // piece size of the torrent. // The ``use_preformatted`` parameter can be set to true to preserve // invalid encoding of the .torrent file. create_torrent(file_storage& fs, int piece_size = 0 , int pad_file_limit = -1, int flags = optimize_alignment , int alignment = -1); create_torrent(torrent_info const& ti); create_torrent(torrent_info const& ti, bool use_preformatted); // internal ~create_torrent(); // This function will generate the .torrent file as a bencode tree. In order to // generate the flat file, use the bencode() function. // // It may be useful to add custom entries to the torrent file before bencoding it // and saving it to disk. // // If anything goes wrong during torrent generation, this function will return // an empty ``entry`` structure. You can test for this condition by querying the // type of the entry: // // .. code:: c++ // // file_storage fs; // // add file ... // create_torrent t(fs); // // add trackers and piece hashes ... // e = t.generate(); // // if (e.type() == entry::undefined_t) // { // // something went wrong // } // // For instance, you cannot generate a torrent with 0 files in it. If you don't add // any files to the ``file_storage``, torrent generation will fail. entry generate() const; // returns an immutable reference to the file_storage used to create // the torrent from. file_storage const& files() const { return m_files; } // Sets the comment for the torrent. The string ``str`` should be utf-8 encoded. // The comment in a torrent file is optional. void set_comment(char const* str); // Sets the creator of the torrent. The string ``str`` should be utf-8 encoded. // This is optional. void set_creator(char const* str); // This sets the SHA-1 hash for the specified piece (``index``). You are required // to set the hash for every piece in the torrent before generating it. If you have // the files on disk, you can use the high level convenience function to do this. // See set_piece_hashes(). void set_hash(int index, sha1_hash const& h); // This sets the sha1 hash for this file. This hash will end up under the key ``sha1`` // associated with this file (for multi-file torrents) or in the root info dictionary // for single-file torrents. void set_file_hash(int index, sha1_hash const& h); // This adds a url seed to the torrent. You can have any number of url seeds. For a // single file torrent, this should be an HTTP url, pointing to a file with identical // content as the file of the torrent. For a multi-file torrent, it should point to // a directory containing a directory with the same name as this torrent, and all the // files of the torrent in it. // // The second function, ``add_http_seed()`` adds an HTTP seed instead. void add_url_seed(std::string const& url); void add_http_seed(std::string const& url); // This adds a DHT node to the torrent. This especially useful if you're creating a // tracker less torrent. It can be used by clients to bootstrap their DHT node from. // The node is a hostname and a port number where there is a DHT node running. // You can have any number of DHT nodes in a torrent. void add_node(std::pair const& node); // Adds a tracker to the torrent. This is not strictly required, but most torrents // use a tracker as their main source of peers. The url should be an http:// or udp:// // url to a machine running a bittorrent tracker that accepts announces for this torrent's // info-hash. The tier is the fallback priority of the tracker. All trackers with tier 0 are // tried first (in any order). If all fail, trackers with tier 1 are tried. If all of those // fail, trackers with tier 2 are tried, and so on. void add_tracker(std::string const& url, int tier = 0); // This function sets an X.509 certificate in PEM format to the torrent. This makes the // torrent an *SSL torrent*. An SSL torrent requires that each peer has a valid certificate // signed by this root certificate. For SSL torrents, all peers are connecting over SSL // connections. For more information, see the section on ssl-torrents_. // // The string is not the path to the cert, it's the actual content of the certificate, // loaded into a std::string. void set_root_cert(std::string const& pem); // Sets and queries the private flag of the torrent. // Torrents with the private flag set ask clients to not use any other // sources than the tracker for peers, and to not advertise itself publicly, // apart from the tracker. void set_priv(bool p) { m_private = p; } bool priv() const { return m_private; } // returns the number of pieces in the associated file_storage object. int num_pieces() const { return m_files.num_pieces(); } // ``piece_length()`` returns the piece size of all pieces but the // last one. ``piece_size()`` returns the size of the specified piece. // these functions are just forwarding to the associated file_storage. int piece_length() const { return m_files.piece_length(); } int piece_size(int i) const { return m_files.piece_size(i); } // This function returns the merkle hash tree, if the torrent was created as a merkle // torrent. The tree is created by ``generate()`` and won't be valid until that function // has been called. When creating a merkle tree torrent, the actual tree itself has to // be saved off separately and fed into libtorrent the first time you start seeding it, // through the ``torrent_info::set_merkle_tree()`` function. From that point onwards, the // tree will be saved in the resume data. std::vector const& merkle_tree() const { return m_merkle_tree; } // Add similar torrents (by info-hash) or collections of similar torrents. // Similar torrents are expected to share some files with this torrent. // Torrents sharing a collection name with this torrent are also expected // to share files with this torrent. A torrent may have more than one // collection and more than one similar torrents. For more information, // see `BEP 38`_. // // .. _`BEP 38`: http://www.bittorrent.org/beps/bep_0038.html void add_similar_torrent(sha1_hash ih); void add_collection(std::string c); private: void load_from_torrent_info(torrent_info const& ti, bool const use_preformatted); file_storage& m_files; // if m_info_dict is initialized, it is // used instead of m_files to generate // the info dictionary entry m_info_dict; // the URLs to the trackers typedef std::pair announce_entry; std::vector m_urls; std::vector m_url_seeds; std::vector m_http_seeds; std::vector m_piece_hash; std::vector m_filehashes; std::vector m_similar; std::vector m_collections; // if we're generating a merkle torrent, this is the // merkle tree we got. This should be saved in fast-resume // in order to start seeding the torrent mutable std::vector m_merkle_tree; // dht nodes to add to the routing table/bootstrap from typedef std::vector > nodes_t; nodes_t m_nodes; // the hash that identifies this torrent // is mutable because it's calculated // lazily mutable sha1_hash m_info_hash; // if a creation date is found in the torrent file // this will be set to that, otherwise it'll be // 1970, Jan 1 time_t 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 the root cert for SSL torrents std::string m_root_cert; // 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 multi-file torrent // or not. e.g. test/test there's one file and one directory // and they have the same name. bool m_multifile:1; // this is true if the torrent is private. i.e., is should not // be announced on the dht bool m_private:1; // if set to one, a merkle torrent will be generated bool m_merkle_torrent:1; // if set, include the 'mtime' modification time in the // torrent file bool m_include_mtime:1; // if set, symbolic links are declared as such in // the torrent file. The full data of the pointed-to // file is still included bool m_include_symlinks:1; }; namespace detail { inline void nop(int) {} } // Adds the file specified by ``path`` to the file_storage object. In case ``path`` // refers to a directory, files will be added recursively from the directory. // // If specified, the predicate ``p`` is called once for every file and directory that // is encountered. Files for which ``p`` returns true are added, and directories for // which ``p`` returns true are traversed. ``p`` must have the following signature:: // // bool Pred(std::string const& p); // // The path that is passed in to the predicate is the full path of the file or // directory. If no predicate is specified, all files are added, and all directories // are traversed. // // The ".." directory is never traversed. // // The ``flags`` argument should be the same as the flags passed to the `create_torrent`_ // constructor. TORRENT_EXPORT void add_files(file_storage& fs, std::string const& file , boost::function p, boost::uint32_t flags = 0); TORRENT_EXPORT void add_files(file_storage& fs, std::string const& file , boost::uint32_t flags = 0); // This function will assume that the files added to the torrent file exists at path // ``p``, read those files and hash the content and set the hashes in the ``create_torrent`` // object. The optional function ``f`` is called in between every hash that is set. ``f`` // must have the following signature:: // // void Fun(int); // // The overloads that don't take an ``error_code&`` may throw an exception in case of a // file error, the other overloads sets the error code to reflect the error, if any. TORRENT_EXPORT void set_piece_hashes(create_torrent& t, std::string const& p , boost::function const& f, error_code& ec); inline void set_piece_hashes(create_torrent& t, std::string const& p, error_code& ec) { set_piece_hashes(t, p, detail::nop, ec); } #ifndef BOOST_NO_EXCEPTIONS inline void set_piece_hashes(create_torrent& t, std::string const& p) { error_code ec; set_piece_hashes(t, p, detail::nop, ec); if (ec) throw libtorrent_exception(ec); } template void set_piece_hashes(create_torrent& t, std::string const& p, Fun f) { error_code ec; set_piece_hashes(t, p, f, ec); if (ec) throw libtorrent_exception(ec); } #endif #if TORRENT_USE_WSTRING // wstring versions // all wstring APIs are deprecated since 0.16.11 // instead, use the wchar -> utf8 conversion functions // and pass in utf8 strings #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED_EXPORT void add_files(file_storage& fs, std::wstring const& wfile , boost::function p, boost::uint32_t flags = 0); TORRENT_DEPRECATED_EXPORT void add_files(file_storage& fs, std::wstring const& wfile , boost::uint32_t flags = 0); TORRENT_DEPRECATED_EXPORT void set_piece_hashes(create_torrent& t, std::wstring const& p , boost::function f, error_code& ec); TORRENT_EXPORT void set_piece_hashes_deprecated(create_torrent& t , std::wstring const& p , boost::function f, error_code& ec); #ifndef BOOST_NO_EXCEPTIONS template TORRENT_DEPRECATED void set_piece_hashes(create_torrent& t , std::wstring const& p, Fun f) { error_code ec; set_piece_hashes_deprecated(t, p, f, ec); if (ec) throw libtorrent_exception(ec); } TORRENT_DEPRECATED inline void set_piece_hashes(create_torrent& t , std::wstring const& p) { error_code ec; set_piece_hashes_deprecated(t, p, detail::nop, ec); if (ec) throw libtorrent_exception(ec); } #endif TORRENT_DEPRECATED inline void set_piece_hashes(create_torrent& t , std::wstring const& p, error_code& ec) { set_piece_hashes_deprecated(t, p, detail::nop, ec); } #endif // TORRENT_NO_DEPRECATE #endif // TORRENT_USE_WSTRING } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/deadline_timer.hpp000066400000000000000000000041121351156116000252260ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_DEADLINE_TIMER_HPP_INCLUDED #define TORRENT_DEADLINE_TIMER_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #if defined TORRENT_BUILD_SIMULATOR #include "simulator/simulator.hpp" #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { #if defined TORRENT_BUILD_SIMULATOR typedef sim::asio::high_resolution_timer deadline_timer; #else typedef boost::asio::high_resolution_timer deadline_timer; #endif } #endif // TORRENT_DEADLINE_TIMER_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/debug.hpp000066400000000000000000000134721351156116000233600ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/config.hpp" #include "libtorrent/assert.hpp" #if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS #include #endif #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/assert.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/time.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #ifdef __MACH__ #include #include #include const mach_msg_type_number_t task_events_info_count = TASK_EVENTS_INFO_COUNT; #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" std::string demangle(char const* name); namespace libtorrent { struct async_t { async_t() : refs(0) {} std::string stack; int refs; }; // defined in session_impl.cpp extern std::map _async_ops; extern int _async_ops_nthreads; extern mutex _async_ops_mutex; // timestamp -> operation struct wakeup_t { time_point timestamp; boost::uint64_t context_switches; char const* operation; }; extern std::deque _wakeups; inline bool has_outstanding_async(char const* name) { mutex::scoped_lock l(_async_ops_mutex); std::map::iterator i = _async_ops.find(name); return i != _async_ops.end(); } inline void add_outstanding_async(char const* name) { mutex::scoped_lock l(_async_ops_mutex); async_t& a = _async_ops[name]; if (a.stack.empty()) { char stack_text[10000]; print_backtrace(stack_text, sizeof(stack_text), 9); // skip the stack frame of 'add_outstanding_async' char* ptr = strchr(stack_text, '\n'); if (ptr != NULL) ++ptr; else ptr = stack_text; a.stack = ptr; } ++a.refs; } inline void complete_async(char const* name) { mutex::scoped_lock l(_async_ops_mutex); async_t& a = _async_ops[name]; TORRENT_ASSERT(a.refs > 0); --a.refs; // don't let this grow indefinitely if (_wakeups.size() < 100000) { _wakeups.push_back(wakeup_t()); wakeup_t& w = _wakeups.back(); w.timestamp = clock_type::now(); #ifdef __MACH__ task_events_info teinfo; mach_msg_type_number_t t_info_count = task_events_info_count; task_info(mach_task_self(), TASK_EVENTS_INFO, reinterpret_cast(&teinfo), &t_info_count); w.context_switches = teinfo.csw; #else w.context_switches = 0; #endif w.operation = name; } } inline void async_inc_threads() { mutex::scoped_lock l(_async_ops_mutex); ++_async_ops_nthreads; } inline void async_dec_threads() { mutex::scoped_lock l(_async_ops_mutex); --_async_ops_nthreads; } inline int log_async() { mutex::scoped_lock l(_async_ops_mutex); int ret = 0; for (std::map::iterator i = _async_ops.begin() , end(_async_ops.end()); i != end; ++i) { if (i->second.refs <= _async_ops_nthreads - 1) continue; ret += i->second.refs; printf("%s: (%d)\n%s\n", i->first.c_str(), i->second.refs, i->second.stack.c_str()); } return ret; } } #endif // TORRENT_ASIO_DEBUGGING namespace libtorrent { #if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS struct single_threaded { single_threaded(): m_single_thread(0) {} ~single_threaded() { m_single_thread = 0; } bool is_single_thread() const { if (m_single_thread == 0) { m_single_thread = pthread_self(); return true; } return m_single_thread == pthread_self(); } bool is_not_thread() const { if (m_single_thread == 0) return true; return m_single_thread != pthread_self(); } void thread_started() { m_single_thread = pthread_self(); } void transfer_ownership() { m_single_thread = 0; } private: mutable pthread_t m_single_thread; }; #else struct single_threaded { bool is_single_thread() const { return true; } void thread_started() {} bool is_not_thread() const {return true; } }; #endif #if TORRENT_USE_ASSERTS struct increment_guard { int& m_cnt; increment_guard(int& c) : m_cnt(c) { TORRENT_ASSERT(m_cnt >= 0); ++m_cnt; } ~increment_guard() { --m_cnt; TORRENT_ASSERT(m_cnt >= 0); } private: increment_guard(increment_guard const&); increment_guard operator=(increment_guard const&); }; #define TORRENT_INCREMENT(x) increment_guard inc_(x) #else #define TORRENT_INCREMENT(x) do {} while (false) #endif } #endif // TORRENT_DEBUG_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/disk_buffer_holder.hpp000066400000000000000000000105561351156116000261120ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_DISK_BUFFER_HOLDER_HPP_INCLUDED #define TORRENT_DISK_BUFFER_HOLDER_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/disk_io_job.hpp" // for block_cache_reference #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct disk_io_thread; struct disk_observer; struct TORRENT_EXTRA_EXPORT buffer_allocator_interface { virtual char* allocate_disk_buffer(char const* category) = 0; virtual void free_disk_buffer(char* b) = 0; virtual void reclaim_block(block_cache_reference ref) = 0; virtual char* allocate_disk_buffer(bool& exceeded , boost::shared_ptr o , char const* category) = 0; protected: ~buffer_allocator_interface() {} }; // The disk buffer holder acts like a ``scoped_ptr`` that frees a disk buffer // when it's destructed, unless it's released. ``release`` returns the disk // buffer and transfers ownership and responsibility to free it to the caller. // // A disk buffer is freed by passing it to ``session_impl::free_disk_buffer()``. // // ``get()`` returns the pointer without transferring responsibility. If // this buffer has been released, ``buffer()`` will return 0. struct TORRENT_EXPORT disk_buffer_holder { // internal disk_buffer_holder(buffer_allocator_interface& alloc, char* buf); // construct a buffer holder that will free the held buffer // using a disk buffer pool directly (there's only one // disk_buffer_pool per session) disk_buffer_holder(buffer_allocator_interface& alloc, disk_io_job const& j); // frees any unreleased disk buffer held by this object ~disk_buffer_holder(); // return the held disk buffer and clear it from the // holder. The responsibility to free it is passed on // to the caller char* release(); // return a pointer to the held buffer char* get() const { return m_buf; } // set the holder object to hold the specified buffer // (or NULL by default). If it's already holding a // disk buffer, it will first be freed. void reset(char* buf = 0); void reset(disk_io_job const& j); // swap pointers of two disk buffer holders. void swap(disk_buffer_holder& h) { TORRENT_ASSERT(&h.m_allocator == &m_allocator); std::swap(h.m_buf, m_buf); std::swap(h.m_ref, m_ref); } block_cache_reference ref() const { return m_ref; } typedef char* (disk_buffer_holder::*unspecified_bool_type)(); // internal operator unspecified_bool_type() const { return m_buf == 0? 0: &disk_buffer_holder::release; } private: // non-copyable disk_buffer_holder& operator=(disk_buffer_holder const&); disk_buffer_holder(disk_buffer_holder const*); buffer_allocator_interface& m_allocator; char* m_buf; block_cache_reference m_ref; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/disk_buffer_pool.hpp000066400000000000000000000136331351156116000256050ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_DISK_BUFFER_POOL_HPP #define TORRENT_DISK_BUFFER_POOL_HPP #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #ifndef TORRENT_DISABLE_POOL_ALLOCATOR #include #endif #if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/io_service_fwd.hpp" #include "libtorrent/file.hpp" // for iovec_t namespace libtorrent { namespace aux { struct session_settings; } class alert; struct disk_observer; struct TORRENT_EXTRA_EXPORT disk_buffer_pool : boost::noncopyable { disk_buffer_pool(int block_size, io_service& ios , boost::function const& trigger_trim); ~disk_buffer_pool(); #if TORRENT_USE_ASSERTS bool is_disk_buffer(char* buffer , mutex::scoped_lock& l) const; bool is_disk_buffer(char* buffer) const; #endif char* allocate_buffer(char const* category); char* allocate_buffer(bool& exceeded, boost::shared_ptr o , char const* category); void free_buffer(char* buf); void free_multiple_buffers(char** bufvec, int numbufs); int allocate_iovec(file::iovec_t* iov, int iov_len); void free_iovec(file::iovec_t* iov, int iov_len); int block_size() const { return m_block_size; } void release_memory(); boost::uint32_t in_use() const { mutex::scoped_lock l(m_pool_mutex); return m_in_use; } boost::uint32_t num_to_evict(int num_needed = 0); bool exceeded_max_size() const { return m_exceeded_max_size; } void set_settings(aux::session_settings const& sett, error_code& ec); protected: void free_buffer_impl(char* buf, mutex::scoped_lock& l); char* allocate_buffer_impl(mutex::scoped_lock& l, char const* category); // number of bytes per block. The BitTorrent // protocol defines the block size to 16 KiB. const int m_block_size; // number of disk buffers currently allocated int m_in_use; // cache size limit int m_max_use; // if we have exceeded the limit, we won't start // allowing allocations again until we drop below // this low watermark int m_low_watermark; // if we exceed the max number of buffers, we start // adding up callbacks to this queue. Once the number // of buffers in use drops below the low watermark, // we start calling these functions back std::vector > m_observers; // callback used to tell the cache it needs to free up some blocks boost::function m_trigger_cache_trim; // set to true to throttle more allocations bool m_exceeded_max_size; // this is the main thread io_service. Callbacks are // posted on this in order to have them execute in // the main thread. io_service& m_ios; private: void check_buffer_level(mutex::scoped_lock& l); mutable mutex m_pool_mutex; int m_cache_buffer_chunk_size; #if TORRENT_HAVE_MMAP && !defined TORRENT_NO_DEPRECATE // the file descriptor of the cache mmap file int m_cache_fd; // the pointer to the block of virtual address space // making up the mmapped cache space char* m_cache_pool; // list of block indices that are not in use. block_index // times 0x4000 + m_cache_pool is the address where the // corresponding memory lives std::vector m_free_list; #endif #ifndef TORRENT_DISABLE_POOL_ALLOCATOR // if this is true, all buffers are allocated // from m_pool. If this is false, all buffers // are allocated using malloc. // if the settings change to prefer the other // allocator, this bool will not switch over // to match the settings until all buffers have // been freed. That way, we never have a mixture // of buffers allocated from different sources. // in essence, this make the setting only take // effect after a restart (which seems fine). // or once the client goes idle for a while. bool m_using_pool_allocator; // this is the actual user setting bool m_want_pool_allocator; // memory pool for read and write operations // and disk cache boost::pool<> m_pool; #endif // this is specifically exempt from release_asserts // since it's a quite costly check. Only for debug // builds. #if defined TORRENT_DEBUG std::set m_buffers_in_use; #endif #if TORRENT_USE_ASSERTS int m_magic; bool m_settings_set; #endif }; } #endif // TORRENT_DISK_BUFFER_POOL_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/disk_interface.hpp000066400000000000000000000120251351156116000252350ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_DISK_INTERFACE_HPP #define TORRENT_DISK_INTERFACE_HPP #include #include #include "libtorrent/bdecode.hpp" #include namespace libtorrent { struct disk_io_job; class piece_manager; struct peer_request; struct disk_observer; struct file_pool; struct add_torrent_params; struct cache_status; struct TORRENT_EXTRA_EXPORT disk_interface { virtual void async_read(piece_manager* storage, peer_request const& r , boost::function const& handler, void* requester , int flags = 0) = 0; virtual void async_write(piece_manager* storage, peer_request const& r , disk_buffer_holder& buffer , boost::function const& handler , int flags = 0) = 0; virtual void async_hash(piece_manager* storage, int piece, int flags , boost::function const& handler, void* requester) = 0; virtual void async_move_storage(piece_manager* storage, std::string const& p, int flags , boost::function const& handler) = 0; virtual void async_release_files(piece_manager* storage , boost::function const& handler = boost::function()) = 0; virtual void async_check_fastresume(piece_manager* storage , bdecode_node const* resume_data , std::vector& links , boost::function const& handler) = 0; #ifndef TORRENT_NO_DEPRECATE virtual void async_cache_piece(piece_manager* storage, int piece , boost::function const& handler) = 0; virtual void async_finalize_file(piece_manager*, int file , boost::function const& handler = boost::function()) = 0; #endif virtual void async_flush_piece(piece_manager* storage, int piece , boost::function const& handler = boost::function()) = 0; virtual void async_stop_torrent(piece_manager* storage , boost::function const& handler)= 0; virtual void async_rename_file(piece_manager* storage, int index, std::string const& name , boost::function const& handler) = 0; virtual void async_delete_files(piece_manager* storage, int options , boost::function const& handler) = 0; virtual void async_save_resume_data(piece_manager* storage , boost::function const& handler) = 0; virtual void async_set_file_priority(piece_manager* storage , std::vector const& prio , boost::function const& handler) = 0; virtual void async_load_torrent(add_torrent_params* params , boost::function const& handler) = 0; virtual void async_tick_torrent(piece_manager* storage , boost::function const& handler) = 0; virtual void clear_read_cache(piece_manager* storage) = 0; virtual void async_clear_piece(piece_manager* storage, int index , boost::function const& handler) = 0; virtual void clear_piece(piece_manager* storage, int index) = 0; virtual void update_stats_counters(counters& c) const = 0; virtual void get_cache_info(cache_status* ret, bool no_pieces = true , piece_manager const* storage = 0) const = 0; virtual file_pool& files() = 0; #if TORRENT_USE_ASSERTS virtual bool is_disk_buffer(char* buffer) const = 0; #endif protected: ~disk_interface() {} }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/disk_io_job.hpp000066400000000000000000000165741351156116000245530ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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_DISK_IO_JOB_HPP #define TORRENT_DISK_IO_JOB_HPP #include "libtorrent/time.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/tailqueue.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { class entry; class piece_manager; struct cached_piece_entry; struct bdecode_node; class torrent_info; struct block_cache_reference { void* storage; int piece; int block; }; // disk_io_jobs are allocated in a pool allocator in disk_io_thread // they are always allocated from the network thread, posted // (as pointers) to the disk I/O thread, and then passed back // to the network thread for completion handling and to be freed. // each disk_io_job can belong to one tailqueue. The job queue // in the disk thread, is one, the jobs waiting on completion // on a cache piece (in block_cache) is another, and a job // waiting for a storage fence to be lowered is another. Jobs // are never in more than one queue at a time. Only passing around // pointers and chaining them back and forth into lists saves // a lot of heap allocation churn of using general purpose // containers. struct TORRENT_EXTRA_EXPORT disk_io_job : tailqueue_node , boost::noncopyable { disk_io_job(); ~disk_io_job(); enum action_t { read , write , hash , move_storage , release_files , delete_files , check_fastresume , save_resume_data , rename_file , stop_torrent #ifndef TORRENT_NO_DEPRECATE , cache_piece , finalize_file #endif , flush_piece , flush_hashed , flush_storage , trim_cache , file_priority , load_torrent , clear_piece , tick_storage , resolve_links , num_job_ids }; enum flags_t { sequential_access = 0x1, // this flag is set on a job when a read operation did // not hit the disk, but found the data in the read cache. cache_hit = 0x2, // force making a copy of the cached block, rather // than getting a reference to the block already in // the cache. force_copy = 0x4, // this is set by the storage object when a fence is raised // for this job. It means that this no other jobs on the same // storage will execute in parallel with this one. It's used // to lower the fence when the job has completed fence = 0x8, // don't keep the read block in cache volatile_read = 0x10, // this job is currently being performed, or it's hanging // on a cache piece that may be flushed soon in_progress = 0x20 }; // for write jobs, returns true if its block // is not dirty anymore bool completed(cached_piece_entry const* pe, int block_size); // unique identifier for the peer when reading void* requester; // for write, this points to the data to write, // for read, the data read is returned here // for other jobs, it may point to other job-specific types // for move_storage and rename_file this is a string allocated // with malloc() // an entry* for save_resume_data // for aiocb_complete this points to the aiocb that completed // for get_cache_info this points to a cache_status object which // is filled in union { char* disk_block; char* string; entry* resume_data; bdecode_node const* check_resume_data; std::vector* priorities; torrent_info* torrent_file; int delete_options; } buffer; // the disk storage this job applies to (if applicable) boost::shared_ptr storage; // this is called when operation completes boost::function callback; // the error code from the file operation // on error, this also contains the path of the // file the disk operation failed on storage_error error; union { // result for hash jobs char piece_hash[20]; // this is used for check_fastresume to pass in a vector of hard-links // to create. Each element corresponds to a file in the file_storage. // The string is the absolute path of the identical file to create // the hard link to. std::vector* links; struct io_args { // if this is set, the read operation is required to // release the block references once it's done sending // the buffer. For aligned block requests (by far the // most common) the buffers are not actually copied // into the send buffer, but simply referenced. When this // is set in a response to a read, the buffer needs to // be de-referenced by sending a reclaim_block message // back to the disk thread block_cache_reference ref; // for read and write, the offset into the piece // the read or write should start // for hash jobs, this is the first block the hash // job is still holding a reference to. The end of // the range of blocks a hash jobs holds references // to is always the last block in the piece. boost::uint32_t offset; // number of bytes 'buffer' points to. Used for read & write boost::uint16_t buffer_size; } io; } d; // arguments used for read and write // the piece this job applies to boost::uint32_t piece:24; // the type of job this is boost::uint32_t action:8; enum { operation_failed = -1 }; // return value of operation boost::int32_t ret; // flags controlling this job boost::uint8_t flags; #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS bool in_use:1; // set to true when the job is added to the completion queue. // to make sure we don't add it twice mutable bool job_posted:1; // set to true when the callback has been called once // used to make sure we don't call it twice mutable bool callback_called:1; // this is true when the job is blocked by a storage_fence mutable bool blocked:1; #endif }; } #endif // TORRENT_DISK_IO_JOB_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/disk_io_thread.hpp000066400000000000000000000533461351156116000252460ustar00rootroot00000000000000/* Copyright (c) 2007-2018, Arvid Norberg, Steven Siloti 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_DISK_IO_THREAD #define TORRENT_DISK_IO_THREAD #include "libtorrent/config.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/sliding_average.hpp" #include "libtorrent/disk_io_job.hpp" #include "libtorrent/disk_job_pool.hpp" #include "libtorrent/block_cache.hpp" #include "libtorrent/file_pool.hpp" #include "libtorrent/disk_interface.hpp" #include "libtorrent/performance_counters.hpp" #include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #ifndef TORRENT_DISABLE_POOL_ALLOCATOR #include #endif #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { class alert; struct add_torrent_params; struct counters; class alert_manager; struct cached_piece_info { piece_manager* storage; // holds one entry for each block in this piece. ``true`` represents // the data for that block being in the disk cache and ``false`` means it's not. std::vector blocks; // the time when a block was last written to this piece. The older // a piece is, the more likely it is to be flushed to disk. time_point last_use; // The index of the next block that needs to be hashed. // Blocks are hashed as they are downloaded in order to not // have to re-read them from disk once the piece is complete, to // compare its hash against the hashes in the .torrent file. int next_to_hash; // the piece index for this cache entry. int piece; enum kind_t { read_cache = 0, write_cache = 1, volatile_read_cache = 2 }; // specifies if this piece is part of the read cache or the write cache. kind_t kind; bool need_readback; }; typedef tailqueue jobqueue_t; // this struct holds a number of statistics counters // relevant for the disk io thread and disk cache. struct TORRENT_EXPORT cache_status { // initializes all counters to 0 cache_status() : pieces() #ifndef TORRENT_NO_DEPRECATE , blocks_written(0) , writes(0) , blocks_read(0) , blocks_read_hit(0) , reads(0) , queued_bytes(0) , cache_size(0) , write_cache_size(0) , read_cache_size(0) , pinned_blocks(0) , total_used_buffers(0) , average_read_time(0) , average_write_time(0) , average_hash_time(0) , average_job_time(0) , cumulative_job_time(0) , cumulative_read_time(0) , cumulative_write_time(0) , cumulative_hash_time(0) , total_read_back(0) , read_queue_size(0) , blocked_jobs(0) , queued_jobs(0) , peak_queued(0) , pending_jobs(0) , num_jobs(0) , num_read_jobs(0) , num_write_jobs(0) , arc_mru_size(0) , arc_mru_ghost_size(0) , arc_mfu_size(0) , arc_mfu_ghost_size(0) , arc_write_size(0) , arc_volatile_size(0) , num_writing_threads(0) #endif { #ifndef TORRENT_NO_DEPRECATE memset(num_fence_jobs, 0, sizeof(num_fence_jobs)); #endif } std::vector pieces; #ifndef TORRENT_NO_DEPRECATE // the total number of 16 KiB blocks written to disk // since this session was started. int blocks_written; // the total number of write operations performed since this // session was started. // // The ratio (``blocks_written`` - ``writes``) / ``blocks_written`` represents // the number of saved write operations per total write operations. i.e. a kind // of cache hit ratio for the write cahe. int writes; // the number of blocks that were requested from the // bittorrent engine (from peers), that were served from disk or cache. int blocks_read; // the number of blocks that was just copied from the read cache // // The ratio ``blocks_read_hit`` / ``blocks_read`` is the cache hit ratio // for the read cache. int blocks_read_hit; // the number of read operations used int reads; // the number of bytes queued for writing, including bytes // submitted to the OS for writing, but not yet complete mutable boost::int64_t queued_bytes; // the number of 16 KiB blocks currently in the disk cache (both read and write). // This includes both read and write cache. int cache_size; // the number of blocks in the cache used for write cache int write_cache_size; // the number of 16KiB blocks in the read cache. int read_cache_size; // the number of blocks with a refcount > 0, i.e. // they may not be evicted int pinned_blocks; // the total number of buffers currently in use. // This includes the read/write disk cache as well as send and receive buffers // used in peer connections. // deprecated, use session_stats_metrics "disk.disk_blocks_in_use" mutable int total_used_buffers; // the number of microseconds an average disk I/O job // has to wait in the job queue before it get processed. // the time read jobs takes on average to complete // (not including the time in the queue), in microseconds. This only measures // read cache misses. int average_read_time; // the time write jobs takes to complete, on average, // in microseconds. This does not include the time the job sits in the disk job // queue or in the write cache, only blocks that are flushed to disk. int average_write_time; // the time hash jobs takes to complete on average, in // microseconds. Hash jobs include running SHA-1 on the data (which for the most // part is done incrementally) and sometimes reading back parts of the piece. It // also includes checking files without valid resume data. int average_hash_time; int average_job_time; // the number of milliseconds spent in all disk jobs, and specific ones // since the start of the session. Times are specified in milliseconds int cumulative_job_time; int cumulative_read_time; int cumulative_write_time; int cumulative_hash_time; // the number of blocks that had to be read back from disk because // they were flushed before the SHA-1 hash got to hash them. If this // is large, a larger cache could significantly improve performance int total_read_back; // number of read jobs in the disk job queue int read_queue_size; // number of jobs blocked because of a fence int blocked_jobs; // number of jobs waiting to be issued (m_to_issue) // average over 30 seconds // deprecated, use session_stats_metrics "disk.queued_disk_jobs" int queued_jobs; // largest ever seen number of queued jobs int peak_queued; // number of jobs waiting to complete (m_pending) // average over 30 seconds int pending_jobs; // total number of disk job objects allocated right now int num_jobs; // total number of disk read job objects allocated right now int num_read_jobs; // total number of disk write job objects allocated right now int num_write_jobs; // ARC cache stats. All of these counters are in number of pieces // not blocks. A piece does not necessarily correspond to a certain // number of blocks. The pieces in the ghost list never have any // blocks in them int arc_mru_size; int arc_mru_ghost_size; int arc_mfu_size; int arc_mfu_ghost_size; int arc_write_size; int arc_volatile_size; // the number of threads currently writing to disk int num_writing_threads; // counts only fence jobs that are currently blocking jobs // not fences that are themself blocked int num_fence_jobs[disk_io_job::num_job_ids]; #endif }; // this is a singleton consisting of the thread and a queue // of disk io jobs struct TORRENT_EXTRA_EXPORT disk_io_thread TORRENT_FINAL : disk_job_pool , disk_interface , buffer_allocator_interface { disk_io_thread(io_service& ios , counters& cnt , void* userdata , int block_size = 16 * 1024); ~disk_io_thread(); void set_settings(settings_pack const* sett, alert_manager& alerts); void set_num_threads(int i, bool wait = true); void abort(bool wait); void async_read(piece_manager* storage, peer_request const& r , boost::function const& handler, void* requester , int flags = 0) TORRENT_OVERRIDE; void async_write(piece_manager* storage, peer_request const& r , disk_buffer_holder& buffer , boost::function const& handler , int flags = 0) TORRENT_OVERRIDE; void async_hash(piece_manager* storage, int piece, int flags , boost::function const& handler, void* requester) TORRENT_OVERRIDE; void async_move_storage(piece_manager* storage, std::string const& p, int flags , boost::function const& handler) TORRENT_OVERRIDE; void async_release_files(piece_manager* storage , boost::function const& handler = boost::function()) TORRENT_OVERRIDE; void async_delete_files(piece_manager* storage, int options , boost::function const& handler) TORRENT_OVERRIDE; void async_check_fastresume(piece_manager* storage , bdecode_node const* resume_data , std::vector& links , boost::function const& handler) TORRENT_OVERRIDE; void async_save_resume_data(piece_manager* storage , boost::function const& handler) TORRENT_OVERRIDE; void async_rename_file(piece_manager* storage, int index, std::string const& name , boost::function const& handler) TORRENT_OVERRIDE; void async_stop_torrent(piece_manager* storage , boost::function const& handler) TORRENT_OVERRIDE; #ifndef TORRENT_NO_DEPRECATE void async_cache_piece(piece_manager* storage, int piece , boost::function const& handler) TORRENT_OVERRIDE; void async_finalize_file(piece_manager* storage, int file , boost::function const& handler = boost::function()) TORRENT_OVERRIDE; #endif void async_flush_piece(piece_manager* storage, int piece , boost::function const& handler = boost::function()) TORRENT_OVERRIDE; void async_set_file_priority(piece_manager* storage , std::vector const& prio , boost::function const& handler) TORRENT_OVERRIDE; void async_load_torrent(add_torrent_params* params , boost::function const& handler) TORRENT_OVERRIDE; void async_tick_torrent(piece_manager* storage , boost::function const& handler) TORRENT_OVERRIDE; void clear_read_cache(piece_manager* storage) TORRENT_OVERRIDE; void async_clear_piece(piece_manager* storage, int index , boost::function const& handler) TORRENT_OVERRIDE; // this is not asynchronous and requires that the piece does not // have any pending buffers. It's meant to be used for pieces that // were just read and hashed and failed the hash check. // there should be no read-operations left, and all buffers should // be discardable void clear_piece(piece_manager* storage, int index) TORRENT_OVERRIDE; // implements buffer_allocator_interface void reclaim_block(block_cache_reference ref) TORRENT_OVERRIDE; void free_disk_buffer(char* buf) TORRENT_OVERRIDE { m_disk_cache.free_buffer(buf); } char* allocate_disk_buffer(char const* category) TORRENT_OVERRIDE { bool exceed = false; return allocate_disk_buffer(exceed, boost::shared_ptr(), category); } void trigger_cache_trim(); char* allocate_disk_buffer(bool& exceeded, boost::shared_ptr o , char const* category) TORRENT_OVERRIDE; bool exceeded_cache_use() const { return m_disk_cache.exceeded_max_size(); } void update_stats_counters(counters& c) const TORRENT_OVERRIDE; void get_cache_info(cache_status* ret, bool no_pieces = true , piece_manager const* storage = 0) const TORRENT_OVERRIDE; // this submits all queued up jobs to the thread void submit_jobs(); block_cache* cache() { return &m_disk_cache; } #if TORRENT_USE_ASSERTS bool is_disk_buffer(char* buffer) const TORRENT_OVERRIDE { return m_disk_cache.is_disk_buffer(buffer); } #endif enum thread_type_t { generic_thread, hasher_thread }; enum { hasher_thread_mask = 3, hasher_thread_divisor }; void thread_fun(int thread_id, thread_type_t type , boost::shared_ptr w); virtual file_pool& files() TORRENT_OVERRIDE { return m_file_pool; } io_service& get_io_service() { return m_ios; } int prep_read_job_impl(disk_io_job* j, bool check_fence = true); #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif void maybe_issue_queued_read_jobs(cached_piece_entry* pe, jobqueue_t& completed_jobs); int do_read(disk_io_job* j, jobqueue_t& completed_jobs); int do_uncached_read(disk_io_job* j); int do_write(disk_io_job* j, jobqueue_t& completed_jobs); int do_uncached_write(disk_io_job* j); int do_hash(disk_io_job* j, jobqueue_t& completed_jobs); int do_uncached_hash(disk_io_job* j); int do_move_storage(disk_io_job* j, jobqueue_t& completed_jobs); int do_release_files(disk_io_job* j, jobqueue_t& completed_jobs); int do_delete_files(disk_io_job* j, jobqueue_t& completed_jobs); int do_check_fastresume(disk_io_job* j, jobqueue_t& completed_jobs); int do_save_resume_data(disk_io_job* j, jobqueue_t& completed_jobs); int do_rename_file(disk_io_job* j, jobqueue_t& completed_jobs); int do_stop_torrent(disk_io_job* j, jobqueue_t& completed_jobs); int do_read_and_hash(disk_io_job* j, jobqueue_t& completed_jobs); #ifndef TORRENT_NO_DEPRECATE int do_cache_piece(disk_io_job* j, jobqueue_t& completed_jobs); int do_finalize_file(disk_io_job* j, jobqueue_t& completed_jobs); #endif int do_flush_piece(disk_io_job* j, jobqueue_t& completed_jobs); int do_flush_hashed(disk_io_job* j, jobqueue_t& completed_jobs); int do_flush_storage(disk_io_job* j, jobqueue_t& completed_jobs); int do_trim_cache(disk_io_job* j, jobqueue_t& completed_jobs); int do_file_priority(disk_io_job* j, jobqueue_t& completed_jobs); int do_load_torrent(disk_io_job* j, jobqueue_t& completed_jobs); int do_clear_piece(disk_io_job* j, jobqueue_t& completed_jobs); int do_tick(disk_io_job* j, jobqueue_t& completed_jobs); int do_resolve_links(disk_io_job* j, jobqueue_t& completed_jobs); void call_job_handlers(void* userdata); private: enum return_value_t { // the do_* functions can return this to indicate the disk // job did not complete immediately, and shouldn't be posted yet defer_handler = -200, // the job cannot be completed right now, put it back in the // queue and try again later retry_job = -201 }; void add_completed_job(disk_io_job* j); void add_completed_jobs(jobqueue_t& jobs); void add_completed_jobs_impl(jobqueue_t& jobs , jobqueue_t& completed_jobs); void fail_jobs(storage_error const& e, jobqueue_t& jobs_); void fail_jobs_impl(storage_error const& e, jobqueue_t& src, jobqueue_t& dst); void check_cache_level(mutex::scoped_lock& l, jobqueue_t& completed_jobs); void perform_job(disk_io_job* j, jobqueue_t& completed_jobs); // this queues up another job to be submitted void add_job(disk_io_job* j, bool user_add = true); void add_fence_job(piece_manager* storage, disk_io_job* j , bool user_add = true); // assumes l is locked (cache mutex). // writes out the blocks [start, end) (releases the lock // during the file operation) int flush_range(cached_piece_entry* p, int start, int end , jobqueue_t& completed_jobs, mutex::scoped_lock& l); // low level flush operations, used by flush_range int build_iovec(cached_piece_entry* pe, int start, int end , file::iovec_t* iov, int* flushing, int block_base_index = 0); void flush_iovec(cached_piece_entry* pe, file::iovec_t const* iov, int const* flushing , int num_blocks, storage_error& error); // returns true if the piece entry was freed bool iovec_flushed(cached_piece_entry* pe , int* flushing, int num_blocks, int block_offset , storage_error const& error , jobqueue_t& completed_jobs); // assumes l is locked (the cache mutex). // assumes pe->hash to be set. // If there are new blocks in piece 'pe' that have not been // hashed by the partial_hash object attached to this piece, // the piece will void kick_hasher(cached_piece_entry* pe, mutex::scoped_lock& l); // flags to pass in to flush_cache() enum flush_flags_t { // only flush read cache (this is cheap) flush_read_cache = 1, // flush read cache, and write cache flush_write_cache = 2, // flush read cache, delete write cache without flushing to disk flush_delete_cache = 4, // expect all pieces for the storage to have been // cleared when flush_cache() returns. This is only // used for asserts and only applies for fence jobs flush_expect_clear = 8 }; void flush_cache(piece_manager* storage, boost::uint32_t flags, jobqueue_t& completed_jobs, mutex::scoped_lock& l); void flush_expired_write_blocks(jobqueue_t& completed_jobs, mutex::scoped_lock& l); void flush_piece(cached_piece_entry* pe, int flags, jobqueue_t& completed_jobs, mutex::scoped_lock& l); int try_flush_hashed(cached_piece_entry* p, int cont_blocks, jobqueue_t& completed_jobs, mutex::scoped_lock& l); void try_flush_write_blocks(int num, jobqueue_t& completed_jobs, mutex::scoped_lock& l); // used to batch reclaiming of blocks to once per cycle void commit_reclaimed_blocks(); void maybe_flush_write_blocks(); void execute_job(disk_io_job* j); void immediate_execute(); void abort_jobs(); // this is a counter which is atomically incremented // by each thread as it's started up, in order to // assign a unique id to each thread boost::atomic m_num_threads; // set to true once we start shutting down boost::atomic m_abort; // this is a counter of how many threads are currently running. // it's used to identify the last thread still running while // shutting down. This last thread is responsible for cleanup boost::atomic m_num_running_threads; // the actual threads running disk jobs std::vector > m_threads; aux::session_settings m_settings; // userdata pointer for the complete_job function, which // is posted to the network thread when jobs complete void* m_userdata; // the last time we expired write blocks from the cache time_point m_last_cache_expiry; time_point m_last_file_check; // LRU cache of open files file_pool m_file_pool; // disk cache mutable mutex m_cache_mutex; block_cache m_disk_cache; enum { cache_check_idle, cache_check_active, cache_check_reinvoke }; int m_cache_check_state; // total number of blocks in use by both the read // and the write cache. This is not supposed to // exceed m_cache_size counters& m_stats_counters; // this is the main thread io_service. Callbacks are // posted on this in order to have them execute in // the main thread. io_service& m_ios; // used to wake up the disk IO thread when there are new // jobs on the job queue (m_queued_jobs) condition_variable m_job_cond; // mutex to protect the m_queued_jobs list mutable mutex m_job_mutex; // jobs queued for servicing jobqueue_t m_queued_jobs; // when using more than 2 threads, this is // used for just hashing jobs, just for threads // dedicated to do hashing condition_variable m_hash_job_cond; jobqueue_t m_queued_hash_jobs; // used to rate limit disk performance warnings time_point m_last_disk_aio_performance_warning; // jobs that are completed are put on this queue // whenever the queue size grows from 0 to 1 // a message is posted to the network thread, which // will then drain the queue and execute the jobs' // handler functions mutex m_completed_jobs_mutex; jobqueue_t m_completed_jobs; // these are blocks that have been returned by the main thread // but they haven't been freed yet. This is used to batch // reclaiming of blocks, to only need one mutex lock per cycle std::vector m_blocks_to_reclaim; // when this is true, there is an outstanding message in the // message queue that will reclaim all blocks in // m_blocks_to_reclaim, there's no need to send another one bool m_outstanding_reclaim_message; #if TORRENT_USE_ASSERTS int m_magic; #endif }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/disk_job_pool.hpp000066400000000000000000000046621351156116000251100ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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_DISK_JOB_POOL #define TORRENT_DISK_JOB_POOL #include "libtorrent/config.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct disk_io_job; struct TORRENT_EXTRA_EXPORT disk_job_pool { disk_job_pool(); ~disk_job_pool(); disk_io_job* allocate_job(int type); void free_job(disk_io_job* j); void free_jobs(disk_io_job** j, int num); int jobs_in_use() const { return m_jobs_in_use; } int read_jobs_in_use() const { return m_read_jobs; } int write_jobs_in_use() const { return m_write_jobs; } private: // total number of in-use jobs int m_jobs_in_use; // total number of in-use read jobs int m_read_jobs; // total number of in-use write jobs int m_write_jobs; mutex m_job_mutex; boost::pool<> m_job_pool; }; } #endif // TORRENT_DISK_JOB_POOL libtorrent-rasterbar-1.1.13/include/libtorrent/disk_observer.hpp000066400000000000000000000035611351156116000251310ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_DISK_OBSERVER_HPP #define TORRENT_DISK_OBSERVER_HPP #include "libtorrent/config.hpp" namespace libtorrent { struct TORRENT_EXTRA_EXPORT disk_observer { // called when the disk cache size has dropped // below the low watermark again and we can // resume downloading from peers virtual void on_disk() = 0; protected: ~disk_observer() {} }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/ed25519.hpp000066400000000000000000000022241351156116000232610ustar00rootroot00000000000000#ifndef ED25519_HPP #define ED25519_HPP #include "libtorrent/export.hpp" // for TORRENT_EXPORT #include // for size_t enum { ed25519_seed_size = 32, ed25519_private_key_size = 64, ed25519_public_key_size = 32, ed25519_signature_size = 64, ed25519_scalar_size = 32, ed25519_shared_secret_size = 32 }; extern "C" { #ifndef ED25519_NO_SEED void TORRENT_EXPORT ed25519_create_seed(unsigned char *seed); #endif void TORRENT_EXPORT ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); void TORRENT_EXPORT ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key); int TORRENT_EXPORT ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *private_key); void TORRENT_EXPORT ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar); void TORRENT_EXPORT ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); } #endif // ED25519_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/entry.hpp000066400000000000000000000242331351156116000234300ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #if TORRENT_USE_IOSTREAM #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/max.hpp" namespace libtorrent { #ifndef TORRENT_NO_DEPRECATE struct lazy_entry; #endif struct bdecode_node; // thrown by any accessor function of entry if the accessor // function requires a type different than the actual type // of the entry object. struct type_error : std::runtime_error { // internal type_error(const char* error): std::runtime_error(error) {} }; // The ``entry`` class represents one node in a bencoded hierarchy. It works as a // variant type, it can be either a list, a dictionary (``std::map``), an integer // or a string. 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 dictionary_type; typedef std::string string_type; typedef std::list list_type; typedef boost::int64_t integer_type; typedef std::vector preformatted_type; // the types an entry can have enum data_type { int_t, string_t, list_t, dictionary_t, undefined_t, preformatted_t }; // returns the concrete type of the entry data_type type() const; // constructors directly from a specific type. // The content of the argument is copied into the // newly constructed entry entry(dictionary_type const&); entry(string_type const&); entry(list_type const&); entry(integer_type const&); entry(preformatted_type const&); // construct an empty entry of the specified type. // see data_type enum. entry(data_type t); // hidden entry(entry const& e); // hidden entry(); // hidden ~entry(); // hidden bool operator==(entry const& e) const; bool operator!=(entry const& e) const { return !(*this == e); } // copies the structure of the right hand side into this // entry. #ifndef TORRENT_NO_DEPRECATE void operator=(lazy_entry const&); #endif void operator=(bdecode_node 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&); void operator=(preformatted_type const&); // The ``integer()``, ``string()``, ``list()`` and ``dict()`` functions // are accessors that return the respective type. If the ``entry`` object // isn't of the type you request, the accessor will throw // libtorrent_exception (which derives from ``std::runtime_error``). You // can ask an ``entry`` for its type through the ``type()`` function. // // If you want to create an ``entry`` you give it the type you want it to // have in its constructor, and then use one of the non-const accessors // to get a reference which you then can assign the value you want it to // have. // // The typical code to get info from a torrent file will then look like // this: // // .. code:: c++ // // entry torrent_file; // // ... // // // throws if this is not a dictionary // entry::dictionary_type const& dict = torrent_file.dict(); // entry::dictionary_type::const_iterator i; // i = dict.find("announce"); // if (i != dict.end()) // { // std::string tracker_url = i->second.string(); // std::cout << tracker_url << "\n"; // } // // // The following code is equivalent, but a little bit shorter: // // .. code:: c++ // // entry torrent_file; // // ... // // // throws if this is not a dictionary // if (entry* i = torrent_file.find_key("announce")) // { // std::string tracker_url = i->string(); // std::cout << tracker_url << "\n"; // } // // // To make it easier to extract information from a torrent file, the // class torrent_info exists. 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; preformatted_type& preformatted(); const preformatted_type& preformatted() const; // swaps the content of *this* with ``e``. void swap(entry& e); // All of these functions requires the entry to be a dictionary, if it // isn't they will throw ``libtorrent::type_error``. // // The non-const versions of the ``operator[]`` will return a reference // to either the existing element at the given key or, if there is no // element with the given key, a reference to a newly inserted element at // that key. // // The const version of ``operator[]`` will only return a reference to an // existing element at the given key. If the key is not found, it will // throw ``libtorrent::type_error``. entry& operator[](char const* key); entry& operator[](std::string const& key); #ifndef BOOST_NO_EXCEPTIONS const entry& operator[](char const* key) const; const entry& operator[](std::string const& key) const; #endif // These functions requires the entry to be a dictionary, if it isn't // they will throw ``libtorrent::type_error``. // // They will look for an element at the given key in the dictionary, if // the element cannot be found, they will return 0. If an element with // the given key is found, the return a pointer to it. entry* find_key(char const* key); entry const* find_key(char const* key) const; entry* find_key(std::string const& key); entry const* find_key(std::string const& key) const; // returns a pretty-printed string representation // of the bencoded structure, with JSON-style syntax std::string to_string() const; std::string to_string(bool single_line) const; protected: void construct(data_type t); void copy(const entry& e); void destruct(); private: void to_string_impl(std::string& out, int indent, bool single_line) const; #if (defined(_MSC_VER) && _MSC_VER < 1310) || TORRENT_COMPLETE_TYPES_REQUIRED // workaround for msvc-bug. // assumes sizeof(map) == sizeof(map) // and sizeof(list) == sizeof(list) enum { union_size = max5) , sizeof(std::map) , sizeof(string_type) , sizeof(integer_type) , sizeof(preformatted_type)>::value }; #else enum { union_size = max5::value }; #endif integer_type data[(union_size + sizeof(integer_type) - 1) / sizeof(integer_type)]; // the bitfield is used so that the m_type_queried field still fits, so // that the ABI is the same for debug builds and release builds. It // appears to be very hard to match debug builds with debug versions of // libtorrent boost::uint8_t m_type:7; public: // in debug mode this is set to false by bdecode to indicate that the // program has not yet queried the type of this entry, and should not // assume that it has a certain type. This is asserted in the accessor // functions. This does not apply if exceptions are used. mutable boost::uint8_t m_type_queried:1; }; namespace detail { TORRENT_EXPORT char const* integer_to_str(char* buf, int size , entry::integer_type val); } #if TORRENT_USE_IOSTREAM // prints the bencoded structure to the ostream as a JSON-style structure. inline std::ostream& operator<<(std::ostream& os, const entry& e) { os << e.to_string(); return os; } #endif #ifndef BOOST_NO_EXCEPTIONS // internal TORRENT_NO_RETURN inline void throw_type_error() { throw libtorrent_exception(errors::invalid_entry_type); } #endif } #endif // TORRENT_ENTRY_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/enum_net.hpp000066400000000000000000000141431351156116000241000ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_ENUM_NET_HPP_INCLUDED #define TORRENT_ENUM_NET_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #if TORRENT_USE_IFCONF || TORRENT_USE_NETLINK || TORRENT_USE_SYSCTL #include // for SO_BINDTODEVICE #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/io_service_fwd.hpp" #include "libtorrent/address.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/socket.hpp" namespace libtorrent { struct socket_type; // the interface should not have a netmask struct ip_interface { address interface_address; address netmask; char name[64]; int mtu; }; struct ip_route { address destination; address netmask; address gateway; char name[64]; int mtu; }; // returns a list of the configured IP interfaces // on the machine TORRENT_EXTRA_EXPORT std::vector enum_net_interfaces(io_service& ios , error_code& ec); TORRENT_EXTRA_EXPORT std::vector enum_routes(io_service& ios , error_code& ec); // return (a1 & mask) == (a2 & mask) TORRENT_EXTRA_EXPORT bool match_addr_mask(address const& a1 , address const& a2, address const& mask); // returns true if the specified address is on the same // local network as us TORRENT_EXTRA_EXPORT bool in_local_network(io_service& ios, address const& addr , error_code& ec); TORRENT_EXTRA_EXPORT bool in_local_network(std::vector const& net , address const& addr); TORRENT_EXTRA_EXPORT address get_default_gateway(io_service& ios, error_code& ec); #ifdef SO_BINDTODEVICE struct bind_to_device_opt { bind_to_device_opt(char const* device): m_value(device) {} template int level(Protocol const&) const { return SOL_SOCKET; } template int name(Protocol const&) const { return SO_BINDTODEVICE; } template const char* data(Protocol const&) const { return m_value; } template size_t size(Protocol const&) const { return IFNAMSIZ; } char const* m_value; }; #endif // attempt to bind socket to the device with the specified name. For systems // that don't support SO_BINDTODEVICE the socket will be bound to one of the // IP addresses of the specified device. In this case it is necessary to // verify the local endpoint of the socket once the connection is established. // the returned address is the ip the socket was bound to (or address_v4::any() // in case SO_BINDTODEVICE succeeded and we don't need to verify it). template address bind_to_device(io_service& ios, Socket& sock , boost::asio::ip::tcp const& protocol , char const* device_name, int port, error_code& ec) { tcp::endpoint bind_ep(address_v4::any(), port); address ip = address::from_string(device_name, ec); if (!ec) { #if TORRENT_USE_IPV6 // this is to cover the case where "0.0.0.0" is considered any IPv4 or // IPv6 address. If we're asking to be bound to an IPv6 address and // providing 0.0.0.0 as the device, turn it into "::" if (ip == address_v4::any() && protocol == boost::asio::ip::tcp::v6()) ip = address_v6::any(); #endif bind_ep.address(ip); // it appears to be an IP. Just bind to that address sock.bind(bind_ep, ec); return bind_ep.address(); } ec.clear(); #ifdef SO_BINDTODEVICE // try to use SO_BINDTODEVICE here, if that exists. If it fails, // fall back to the mechanism we have below sock.set_option(bind_to_device_opt(device_name), ec); if (ec) #endif { ec.clear(); // TODO: 2 this could be done more efficiently by just looking up // the interface with the given name, maybe even with if_nametoindex() std::vector ifs = enum_net_interfaces(ios, ec); if (ec) return bind_ep.address(); bool found = false; for (int i = 0; i < int(ifs.size()); ++i) { // we're looking for a specific interface, and its address // (which must be of the same family as the address we're // connecting to) if (strcmp(ifs[i].name, device_name) != 0) continue; if (ifs[i].interface_address.is_v4() != (protocol == boost::asio::ip::tcp::v4())) continue; bind_ep.address(ifs[i].interface_address); found = true; break; } if (!found) { ec = error_code(boost::system::errc::no_such_device, generic_category()); return bind_ep.address(); } } sock.bind(bind_ep, ec); return bind_ep.address(); } // returns the device name whose local address is ``addr``. If // no such device is found, an empty string is returned. TORRENT_EXTRA_EXPORT std::string device_for_address(address addr , io_service& ios, error_code& ec); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/error.hpp000066400000000000000000000035711351156116000234220ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_ERROR_HPP_INCLUDED #define TORRENT_ERROR_HPP_INCLUDED #include #include "libtorrent/config.hpp" #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN // asio assumes that the windows error codes are defined already #include #endif #include namespace libtorrent { namespace error = boost::asio::error; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/error_code.hpp000066400000000000000000000501311351156116000244060ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_ERROR_CODE_HPP_INCLUDED #define TORRENT_ERROR_CODE_HPP_INCLUDED #include #include "libtorrent/config.hpp" #include "libtorrent/string_util.hpp" // for allocate_string_copy #include // free #include "libtorrent/aux_/disable_warnings_push.hpp" #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN // asio assumes that the windows error codes are defined already #include #endif #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifndef BOOST_SYSTEM_NOEXCEPT #define BOOST_SYSTEM_NOEXCEPT TORRENT_EXCEPTION_THROW_SPECIFIER #endif namespace libtorrent { namespace errors { // libtorrent uses boost.system's ``error_code`` class to represent // errors. libtorrent has its own error category // libtorrent_category() with the error codes defined by // error_code_enum. enum error_code_enum { // Not an error no_error = 0, // Two torrents has files which end up overwriting each other file_collision, // A piece did not match its piece hash failed_hash_check, // The .torrent file does not contain a bencoded dictionary at // its top level torrent_is_no_dict, // The .torrent file does not have an ``info`` dictionary torrent_missing_info, // The .torrent file's ``info`` entry is not a dictionary torrent_info_no_dict, // The .torrent file does not have a ``piece length`` entry torrent_missing_piece_length, // The .torrent file does not have a ``name`` entry torrent_missing_name, // The .torrent file's name entry is invalid torrent_invalid_name, // The length of a file, or of the whole .torrent file is invalid. // Either negative or not an integer torrent_invalid_length, // Failed to parse a file entry in the .torrent torrent_file_parse_failed, // The ``pieces`` field is missing or invalid in the .torrent file torrent_missing_pieces, // The ``pieces`` string has incorrect length torrent_invalid_hashes, // The .torrent file has more pieces than is supported by libtorrent too_many_pieces_in_torrent, // The metadata (.torrent file) that was received from the swarm // matched the info-hash, but failed to be parsed invalid_swarm_metadata, // The file or buffer is not correctly bencoded invalid_bencoding, // The .torrent file does not contain any files no_files_in_torrent, // The string was not properly url-encoded as expected invalid_escaped_string, // Operation is not permitted since the session is shutting down session_is_closing, // There's already a torrent with that info-hash added to the // session duplicate_torrent, // The supplied torrent_handle is not referring to a valid torrent invalid_torrent_handle, // The type requested from the entry did not match its type invalid_entry_type, // The specified URI does not contain a valid info-hash missing_info_hash_in_uri, // One of the files in the torrent was unexpectedly small. This // might be caused by files being changed by an external process file_too_short, // The URL used an unknown protocol. Currently ``http`` and // ``https`` (if built with openssl support) are recognized. For // trackers ``udp`` is recognized as well. unsupported_url_protocol, // The URL did not conform to URL syntax and failed to be parsed url_parse_error, // The peer sent a 'piece' message of length 0 peer_sent_empty_piece, // A bencoded structure was corrupt and failed to be parsed parse_failed, // The fast resume file was missing or had an invalid file version // tag invalid_file_tag, // The fast resume file was missing or had an invalid info-hash missing_info_hash, // The info-hash did not match the torrent mismatching_info_hash, // The URL contained an invalid hostname invalid_hostname, // The URL had an invalid port invalid_port, // The port is blocked by the port-filter, and prevented the // connection port_blocked, // The IPv6 address was expected to end with ']' expected_close_bracket_in_address, // The torrent is being destructed, preventing the operation to // succeed destructing_torrent, // The connection timed out timed_out, // The peer is upload only, and we are upload only. There's no point // in keeping the connection upload_upload_connection, // The peer is upload only, and we're not interested in it. There's // no point in keeping the connection uninteresting_upload_peer, // The peer sent an unknown info-hash invalid_info_hash, // The torrent is paused, preventing the operation from succeeding torrent_paused, // The peer sent an invalid have message, either wrong size or // referring to a piece that doesn't exist in the torrent invalid_have, // The bitfield message had the incorrect size invalid_bitfield_size, // The peer kept requesting pieces after it was choked, possible // abuse attempt. too_many_requests_when_choked, // The peer sent a piece message that does not correspond to a // piece request sent by the client invalid_piece, // memory allocation failed no_memory, // The torrent is aborted, preventing the operation to succeed torrent_aborted, // The peer is a connection to ourself, no point in keeping it self_connection, // The peer sent a piece message with invalid size, either negative // or greater than one block invalid_piece_size, // The peer has not been interesting or interested in us for too // long, no point in keeping it around timed_out_no_interest, // The peer has not said anything in a long time, possibly dead timed_out_inactivity, // The peer did not send a handshake within a reasonable amount of // time, it might not be a bittorrent peer timed_out_no_handshake, // The peer has been unchoked for too long without requesting any // data. It might be lying about its interest in us timed_out_no_request, // The peer sent an invalid choke message invalid_choke, // The peer send an invalid unchoke message invalid_unchoke, // The peer sent an invalid interested message invalid_interested, // The peer sent an invalid not-interested message invalid_not_interested, // The peer sent an invalid piece request message invalid_request, // The peer sent an invalid hash-list message (this is part of the // merkle-torrent extension) invalid_hash_list, // The peer sent an invalid hash-piece message (this is part of the // merkle-torrent extension) invalid_hash_piece, // The peer sent an invalid cancel message invalid_cancel, // The peer sent an invalid DHT port-message invalid_dht_port, // The peer sent an invalid suggest piece-message invalid_suggest, // The peer sent an invalid have all-message invalid_have_all, // The peer sent an invalid have none-message invalid_have_none, // The peer sent an invalid reject message invalid_reject, // The peer sent an invalid allow fast-message invalid_allow_fast, // The peer sent an invalid extension message ID invalid_extended, // The peer sent an invalid message ID invalid_message, // The synchronization hash was not found in the encrypted handshake sync_hash_not_found, // The encryption constant in the handshake is invalid invalid_encryption_constant, // The peer does not support plaintext, which is the selected mode no_plaintext_mode, // The peer does not support rc4, which is the selected mode no_rc4_mode, // The peer does not support any of the encryption modes that the // client supports unsupported_encryption_mode, // The peer selected an encryption mode that the client did not // advertise and does not support unsupported_encryption_mode_selected, // The pad size used in the encryption handshake is of invalid size invalid_pad_size, // The encryption handshake is invalid invalid_encrypt_handshake, // The client is set to not support incoming encrypted connections // and this is an encrypted connection no_incoming_encrypted, // The client is set to not support incoming regular bittorrent // connections, and this is a regular connection no_incoming_regular, // The client is already connected to this peer-ID duplicate_peer_id, // Torrent was removed torrent_removed, // The packet size exceeded the upper sanity check-limit packet_too_large, reserved, // The web server responded with an error http_error, // The web server response is missing a location header missing_location, // The web seed redirected to a path that no longer matches the // .torrent directory structure invalid_redirection, // The connection was closed because it redirected to a different // URL redirecting, // The HTTP range header is invalid invalid_range, // The HTTP response did not have a content length no_content_length, // The IP is blocked by the IP filter banned_by_ip_filter, // At the connection limit too_many_connections, // The peer is marked as banned peer_banned, // The torrent is stopping, causing the operation to fail stopping_torrent, // The peer has sent too many corrupt pieces and is banned too_many_corrupt_pieces, // The torrent is not ready to receive peers torrent_not_ready, // The peer is not completely constructed yet peer_not_constructed, // The session is closing, causing the operation to fail session_closing, // The peer was disconnected in order to leave room for a // potentially better peer optimistic_disconnect, // The torrent is finished torrent_finished, // No UPnP router found no_router, // The metadata message says the metadata exceeds the limit metadata_too_large, // The peer sent an invalid metadata request message invalid_metadata_request, // The peer advertised an invalid metadata size invalid_metadata_size, // The peer sent a message with an invalid metadata offset invalid_metadata_offset, // The peer sent an invalid metadata message invalid_metadata_message, // The peer sent a peer exchange message that was too large pex_message_too_large, // The peer sent an invalid peer exchange message invalid_pex_message, // The peer sent an invalid tracker exchange message invalid_lt_tracker_message, // The peer sent an pex messages too often. This is a possible // attempt of and attack too_frequent_pex, // The operation failed because it requires the torrent to have // the metadata (.torrent file) and it doesn't have it yet. // This happens for magnet links before they have downloaded the // metadata, and also torrents added by URL. no_metadata, // The peer sent an invalid ``dont_have`` message. The don't have // message is an extension to allow peers to advertise that the // no longer has a piece they previously had. invalid_dont_have, // The peer tried to connect to an SSL torrent without connecting // over SSL. requires_ssl_connection, // The peer tried to connect to a torrent with a certificate // for a different torrent. invalid_ssl_cert, // the torrent is not an SSL torrent, and the operation requires // an SSL torrent not_an_ssl_torrent, // peer was banned because its listen port is within a banned port // range, as specified by the port_filter. banned_by_port_filter, // The NAT-PMP router responded with an unsupported protocol version unsupported_protocol_version = 120, // You are not authorized to map ports on this NAT-PMP router natpmp_not_authorized, // The NAT-PMP router failed because of a network failure network_failure, // The NAT-PMP router failed because of lack of resources no_resources, // The NAT-PMP router failed because an unsupported opcode was sent unsupported_opcode, // The resume data file is missing the 'file sizes' entry missing_file_sizes = 130, // The resume data file 'file sizes' entry is empty no_files_in_resume_data, // The resume data file is missing the 'pieces' and 'slots' entry missing_pieces, // The number of files in the resume data does not match the number // of files in the torrent mismatching_number_of_files, // One of the files on disk has a different size than in the fast // resume file mismatching_file_size, // One of the files on disk has a different timestamp than in the // fast resume file mismatching_file_timestamp, // The resume data file is not a dictionary not_a_dictionary, // The 'blocks per piece' entry is invalid in the resume data file invalid_blocks_per_piece, // The resume file is missing the 'slots' entry, which is required // for torrents with compact allocation. *DEPRECATED* missing_slots, // The resume file contains more slots than the torrent too_many_slots, // The 'slot' entry is invalid in the resume data invalid_slot_list, // One index in the 'slot' list is invalid invalid_piece_index, // The pieces on disk needs to be re-ordered for the specified // allocation mode. This happens if you specify sparse allocation // and the files on disk are using compact storage. The pieces needs // to be moved to their right position. *DEPRECATED* pieces_need_reorder, // this error is returned when asking to save resume data and // specifying the flag to only save when there's anything new to save // (torrent_handle::only_if_modified) and there wasn't anything changed. resume_data_not_modified, // The HTTP header was not correctly formatted http_parse_error = 150, // The HTTP response was in the 300-399 range but lacked a location // header http_missing_location, // The HTTP response was encoded with gzip or deflate but // decompressing it failed http_failed_decompress, // The URL specified an i2p address, but no i2p router is configured no_i2p_router = 160, // i2p acceptor is not available yet, can't announce without endpoint no_i2p_endpoint = 161, // The tracker URL doesn't support transforming it into a scrape // URL. i.e. it doesn't contain "announce. scrape_not_available = 170, // invalid tracker response invalid_tracker_response, // invalid peer dictionary entry. Not a dictionary invalid_peer_dict, // tracker sent a failure message tracker_failure, // missing or invalid 'files' entry invalid_files_entry, // missing or invalid 'hash' entry invalid_hash_entry, // missing or invalid 'peers' and 'peers6' entry invalid_peers_entry, // udp tracker response packet has invalid size invalid_tracker_response_length, // invalid transaction id in udp tracker response invalid_tracker_transaction_id, // invalid action field in udp tracker response invalid_tracker_action, #ifndef TORRENT_NO_DEPRECATE // expected string in bencoded string expected_string = 190, // expected colon in bencoded string expected_colon, // unexpected end of file in bencoded string unexpected_eof, // expected value (list, dict, int or string) in bencoded string expected_value, // bencoded recursion depth limit exceeded depth_exceeded, // bencoded item count limit exceeded limit_exceeded, // integer overflow overflow, #endif // the number of error codes error_code_max }; // HTTP errors are reported in the libtorrent::http_category, with error code enums in // the ``libtorrent::errors`` namespace. enum http_errors { cont = 100, ok = 200, created = 201, accepted = 202, no_content = 204, multiple_choices = 300, moved_permanently = 301, moved_temporarily = 302, not_modified = 304, bad_request = 400, unauthorized = 401, forbidden = 403, not_found = 404, internal_server_error = 500, not_implemented = 501, bad_gateway = 502, service_unavailable = 503 }; // hidden TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); } // namespace errors // return the instance of the libtorrent_error_category which // maps libtorrent error codes to human readable error messages. TORRENT_EXPORT boost::system::error_category& libtorrent_category(); // returns the error_category for HTTP errors TORRENT_EXPORT boost::system::error_category& http_category(); using boost::system::error_code; using boost::system::error_condition; // internal using boost::system::generic_category; using boost::system::system_category; #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED TORRENT_EXPORT boost::system::error_category& get_libtorrent_category(); TORRENT_DEPRECATED TORRENT_EXPORT boost::system::error_category& get_http_category(); #endif #ifndef BOOST_NO_EXCEPTIONS struct TORRENT_EXPORT libtorrent_exception : std::exception { libtorrent_exception(error_code const& s): m_error(s), m_msg(0) {} virtual const char* what() const TORRENT_EXCEPTION_THROW_SPECIFIER; virtual ~libtorrent_exception() TORRENT_EXCEPTION_THROW_SPECIFIER; #if __cplusplus >= 201103L libtorrent_exception(libtorrent_exception const&) = default; libtorrent_exception& operator=(libtorrent_exception const&) = default; #endif error_code error() const { return m_error; } private: error_code m_error; mutable char* m_msg; }; #endif // used by storage to return errors // also includes which underlying file the // error happened on struct TORRENT_EXPORT storage_error { storage_error(): file(-1), operation(0) {} storage_error(error_code e): ec(e), file(-1), operation(0) {} operator bool() const { return ec.value() != 0; } // the error that occurred error_code ec; // the file the error occurred on boost::int32_t file:24; // A code from file_operation_t enum, indicating what // kind of operation failed. boost::uint32_t operation:8; enum file_operation_t { none, stat, mkdir, open, rename, remove, copy, read, write, fallocate, alloc_cache_piece, partfile_move, partfile_read, partfile_write, check_resume, hard_link }; // Returns a string literal representing the file operation // that failed. If there were no failure, it returns // an empty string. char const* operation_str() const { static char const* ops[] = { "", "stat", "mkdir", "open", "rename", "remove", "copy" , "read", "write", "fallocate", "allocate cache piece" , "partfile move", "partfile read", "partfile write" , "check resume", "hard_link" }; return ops[operation]; } }; } namespace boost { namespace system { template<> struct is_error_code_enum { static const bool value = true; }; template<> struct is_error_code_enum { static const bool value = true; }; } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/export.hpp000066400000000000000000000056271351156116000236160ustar00rootroot00000000000000/* Copyright (c) 2005-2018, 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_EXPORT_HPP_INCLUDED #define TORRENT_EXPORT_HPP_INCLUDED #include // backwards compatibility with older versions of boost #if !defined BOOST_SYMBOL_EXPORT && !defined BOOST_SYMBOL_IMPORT # if defined _MSC_VER || defined __MINGW32__ # define BOOST_SYMBOL_EXPORT __declspec(dllexport) # define BOOST_SYMBOL_IMPORT __declspec(dllimport) # elif __GNU__ >= 4 # define BOOST_SYMBOL_EXPORT __attribute__((visibility("default"))) # define BOOST_SYMBOL_IMPORT __attribute__((visibility("default"))) # else # define BOOST_SYMBOL_EXPORT # define BOOST_SYMBOL_IMPORT # endif #endif #if defined TORRENT_BUILDING_SHARED # define TORRENT_EXPORT BOOST_SYMBOL_EXPORT #elif defined TORRENT_LINKING_SHARED # define TORRENT_EXPORT BOOST_SYMBOL_IMPORT #endif // when this is specified, export a bunch of extra // symbols, mostly for the unit tests to reach #if defined TORRENT_EXPORT_EXTRA # if defined TORRENT_BUILDING_SHARED # define TORRENT_EXTRA_EXPORT BOOST_SYMBOL_EXPORT # elif defined TORRENT_LINKING_SHARED # define TORRENT_EXTRA_EXPORT BOOST_SYMBOL_IMPORT # endif #endif #ifndef TORRENT_EXPORT # define TORRENT_EXPORT #endif #ifndef TORRENT_EXTRA_EXPORT # define TORRENT_EXTRA_EXPORT #endif // only export this type if deprecated functions are enabled #ifdef TORRENT_NO_DEPRECATE #define TORRENT_DEPRECATED_EXPORT TORRENT_EXTRA_EXPORT #else #define TORRENT_DEPRECATED_EXPORT TORRENT_EXPORT #endif #endif libtorrent-rasterbar-1.1.13/include/libtorrent/extensions.hpp000066400000000000000000000530601351156116000244660ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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_EXTENSIONS_HPP_INCLUDED #define TORRENT_EXTENSIONS_HPP_INCLUDED // OVERVIEW // // libtorrent has a plugin interface for implementing extensions to the protocol. // These can be general extensions for transferring metadata or peer exchange // extensions, or it could be used to provide a way to customize the protocol // to fit a particular (closed) network. // // In short, the plugin interface makes it possible to: // // * register extension messages (sent in the extension handshake), see // extensions_. // * add data and parse data from the extension handshake. // * send extension messages and standard bittorrent messages. // * override or block the handling of standard bittorrent messages. // * save and restore state via the session state // * see all alerts that are posted // // .. _extensions: extension_protocol.html // // a word of caution // ----------------- // // Writing your own plugin is a very easy way to introduce serious bugs such as // dead locks and race conditions. Since a plugin has access to internal // structures it is also quite easy to sabotage libtorrent's operation. // // All the callbacks in this interface are called with the main libtorrent thread // mutex locked. And they are always called from the libtorrent network thread. In // case portions of your plugin are called from other threads, typically the main // thread, you cannot use any of the member functions on the internal structures // in libtorrent, since those require the mutex to be locked. Furthermore, you would // also need to have a mutex on your own shared data within the plugin, to make // sure it is not accessed at the same time from the libtorrent thread (through a // callback). See `boost thread's mutex`_. If you need to send out a message from // another thread, it is advised to use an internal queue, and do the actual // sending in ``tick()``. // // Since the plugin interface gives you easy access to internal structures, it // is not supported as a stable API. Plugins should be considered specific to a // specific version of libtorrent. Although, in practice the internals mostly // don't change that dramatically. // // .. _`boost thread's mutex`: http://www.boost.org/doc/html/mutex.html // // // plugin-interface // ================ // // The plugin interface consists of three base classes that the plugin may // implement. These are called plugin, torrent_plugin and peer_plugin. // They are found in the ```` header. // // These plugins are instantiated for each session, torrent and possibly each peer, // respectively. // // For plugins that only need per torrent state, it is enough to only implement // ``torrent_plugin`` and pass a constructor function or function object to // ``session::add_extension()`` or ``torrent_handle::add_extension()`` (if the // torrent has already been started and you want to hook in the extension at // run-time). // // The signature of the function is:: // // boost::shared_ptr (*)(torrent_handle const&, void*); // // The second argument is the userdata passed to ``session::add_torrent()`` or // ``torrent_handle::add_extension()``. // // The function should return a ``boost::shared_ptr`` which // may or may not be 0. If it is a null pointer, the extension is simply ignored // for this torrent. If it is a valid pointer (to a class inheriting // ``torrent_plugin``), it will be associated with this torrent and callbacks // will be made on torrent events. // // For more elaborate plugins which require session wide state, you would // implement ``plugin``, construct an object (in a ``boost::shared_ptr``) and pass // it in to ``session::add_extension()``. // // custom alerts // ============= // // Since plugins are running within internal libtorrent threads, one convenient // way to communicate with the client is to post custom alerts. // // The expected interface of any alert, apart from deriving from the alert // base class, looks like this: // // .. parsed-literal:: // // static const int alert_type = **; // virtual int type() const { return alert_type; } // // virtual std::string message() const; // // virtual std::auto_ptr clone() const // { return std::auto_ptr(new name(\*this)); } // // static const int static_category = **; // virtual int category() const { return static_category; } // // virtual char const* what() const { return **; } // // The ``alert_type`` is used for the type-checking in ``alert_cast``. It must // not collide with any other alert. The built-in alerts in libtorrent will // not use alert type IDs greater than ``user_alert_id``. When defining your // own alert, make sure it's greater than this constant. // // ``type()`` is the run-time equivalence of the ``alert_type``. // // The ``message()`` virtual function is expected to construct a useful // string representation of the alert and the event or data it represents. // Something convenient to put in a log file for instance. // // ``clone()`` is used internally to copy alerts. The suggested implementation // of simply allocating a new instance as a copy of ``*this`` is all that's // expected. // // The static category is required for checking whether or not the category // for a specific alert is enabled or not, without instantiating the alert. // The ``category`` virtual function is the run-time equivalence. // // The ``what()`` virtual function may simply be a string literal of the class // name of your alert. // // For more information, see the `alert section`_. // // .. _`alert section`: reference-Alerts.html #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include #include "libtorrent/config.hpp" #include "libtorrent/buffer.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/sha1_hash.hpp" // for sha1_hash #include "libtorrent/error_code.hpp" #include "libtorrent/session_handle.hpp" namespace libtorrent { struct peer_plugin; struct peer_request; class entry; struct bdecode_node; struct disk_buffer_holder; struct bitfield; class alert; struct torrent_plugin; struct add_torrent_params; struct peer_connection_handle; struct torrent_handle; // Functions of this type are called to handle incoming DHT requests typedef boost::function dht_extension_handler_t; // Map of query strings to handlers. Note that query strings are limited to 15 bytes. // see max_dht_query_length typedef std::vector > dht_extensions_t; // this is the base class for a session plugin. One primary feature // is that it is notified of all torrents that are added to the session, // and can add its own torrent_plugins. struct TORRENT_EXPORT plugin { // hidden virtual ~plugin() {} // these are flags that can be returned by implemented_features() // indicating which callbacks this plugin is interested in enum feature_flags_t { // include this bit if your plugin needs to alter the order of the // optimistic unchoke of peers. i.e. have the on_optimistic_unchoke() // callback be called. optimistic_unchoke_feature = 1, // include this bit if your plugin needs to have on_tick() called tick_feature = 2 }; // This function is expected to return a bitmask indicating which features // this plugin implements. Some callbacks on this object may not be called // unless the corresponding feature flag is returned here. Note that // callbacks may still be called even if the corresponding feature is not // specified in the return value here. See feature_flags_t for possible // flags to return. virtual boost::uint32_t implemented_features() { return 0; } // this is called by the session every time a new torrent is added. // The ``torrent*`` points to the internal torrent object created // for the new torrent. The ``void*`` is the userdata pointer as // passed in via add_torrent_params. // // If the plugin returns a torrent_plugin instance, it will be added // to the new torrent. Otherwise, return an empty shared_ptr to a // torrent_plugin (the default). virtual boost::shared_ptr new_torrent(torrent_handle const&, void*) { return boost::shared_ptr(); } // called when plugin is added to a session virtual void added(session_handle) {} // called after a plugin is added // allows the plugin to register DHT requests it would like to handle virtual void register_dht_extensions(dht_extensions_t&) {} // called when an alert is posted alerts that are filtered are not posted virtual void on_alert(alert const*) {} // return true if the add_torrent_params should be added virtual bool on_unknown_torrent(sha1_hash const& /* info_hash */ , peer_connection_handle const& /* pc */, add_torrent_params& /* p */) { return false; } // called once per second virtual void on_tick() {} // called when choosing peers to optimistically unchoke. peer's will be // unchoked in the order they appear in the given vector. if // the plugin returns true then the ordering provided will be used and no // other plugin will be allowed to change it. If your plugin expects this // to be called, make sure to include the flag // ``optimistic_unchoke_feature`` in the return value from // implemented_features(). virtual bool on_optimistic_unchoke(std::vector& /* peers */) { return false; } // called when saving settings state virtual void save_state(entry&) const {} // called when loading settings state virtual void load_state(bdecode_node const&) {} }; // Torrent plugins are associated with a single torrent and have a number // of functions called at certain events. Many of its functions have the // ability to change or override the default libtorrent behavior. struct TORRENT_EXPORT torrent_plugin { // hidden virtual ~torrent_plugin() {} // This function is called each time a new peer is connected to the torrent. You // may choose to ignore this by just returning a default constructed // ``shared_ptr`` (in which case you don't need to override this member // function). // // If you need an extension to the peer connection (which most plugins do) you // are supposed to return an instance of your peer_plugin class. Which in // turn will have its hook functions called on event specific to that peer. // // The ``peer_connection_handle`` will be valid as long as the ``shared_ptr`` // is being held by the torrent object. So, it is generally a good idea to not // keep a ``shared_ptr`` to your own peer_plugin. If you want to keep references // to it, use ``weak_ptr``. // // If this function throws an exception, the connection will be closed. virtual boost::shared_ptr new_connection(peer_connection_handle const&) { return boost::shared_ptr(); } // These hooks are called when a piece passes the hash check or fails the hash // check, respectively. The ``index`` is the piece index that was downloaded. // It is possible to access the list of peers that participated in sending the // piece through the ``torrent`` and the ``piece_picker``. virtual void on_piece_pass(int /*index*/) {} virtual void on_piece_failed(int /*index*/) {} // This hook is called approximately once per second. It is a way of making it // easy for plugins to do timed events, for sending messages or whatever. virtual void tick() {} // These hooks are called when the torrent is paused and resumed respectively. // The return value indicates if the event was handled. A return value of // ``true`` indicates that it was handled, and no other plugin after this one // will have this hook function called, and the standard handler will also not be // invoked. So, returning true effectively overrides the standard behavior of // pause or resume. // // Note that if you call ``pause()`` or ``resume()`` on the torrent from your // handler it will recurse back into your handler, so in order to invoke the // standard handler, you have to keep your own state on whether you want standard // behavior or overridden behavior. virtual bool on_pause() { return false; } virtual bool on_resume() { return false; } // This function is called when the initial files of the torrent have been // checked. If there are no files to check, this function is called immediately. // // i.e. This function is always called when the torrent is in a state where it // can start downloading. virtual void on_files_checked() {} // called when the torrent changes state // the state is one of torrent_status::state_t // enum members virtual void on_state(int /*s*/) {} // called when the torrent is unloaded from RAM // and loaded again, respectively // unload is called right before the torrent is // unloaded and load is called right after it's // loaded. i.e. the full torrent state is available // when these callbacks are called. virtual void on_unload() {} virtual void on_load() {} // called every time policy::add_peer is called // src is a bitmask of which sources this peer // has been seen from. flags is a bitmask of: enum flags_t { // this is the first time we see this peer first_time = 1, // this peer was not added because it was // filtered by the IP filter filtered = 2 }; // called every time a new peer is added to the peer list. // This is before the peer is connected to. For ``flags``, see // torrent_plugin::flags_t. The ``source`` argument refers to // the source where we learned about this peer from. It's a // bitmask, because many sources may have told us about the same // peer. For peer source flags, see peer_info::peer_source_flags. virtual void on_add_peer(tcp::endpoint const&, int /*src*/, int /*flags*/) {} }; // peer plugins are associated with a specific peer. A peer could be // both a regular bittorrent peer (``bt_peer_connection``) or one of the // web seed connections (``web_peer_connection`` or ``http_seed_connection``). // In order to only attach to certain peers, make your // torrent_plugin::new_connection only return a plugin for certain peer // connection types struct TORRENT_EXPORT peer_plugin { // hidden virtual ~peer_plugin() {} // This function is expected to return the name of // the plugin. virtual char const* type() const { return ""; } // can add entries to the extension handshake // this is not called for web seeds virtual void add_handshake(entry&) {} // called when the peer is being disconnected. virtual void on_disconnect(error_code const& /*ec*/) {} // called when the peer is successfully connected. Note that // incoming connections will have been connected by the time // the peer plugin is attached to it, and won't have this hook // called. virtual void on_connected() {} // throwing an exception from any of the handlers (except add_handshake) // closes the connection // this is called when the initial bittorrent handshake is received. // Returning false means that the other end doesn't support this extension // and will remove it from the list of plugins. this is not called for web // seeds virtual bool on_handshake(char const* /*reserved_bits*/) { return true; } // called when the extension handshake from the other end is received // if this returns false, it means that this extension isn't // supported by this peer. It will result in this peer_plugin // being removed from the peer_connection and destructed. // this is not called for web seeds virtual bool on_extension_handshake(bdecode_node const&) { return true; } // returning true from any of the message handlers // indicates that the plugin has handled the message. // it will break the plugin chain traversing and not let // anyone else handle the message, including the default // handler. virtual bool on_choke() { return false; } virtual bool on_unchoke() { return false; } virtual bool on_interested() { return false; } virtual bool on_not_interested() { return false; } virtual bool on_have(int /*index*/) { return false; } virtual bool on_dont_have(int /*index*/) { return false; } virtual bool on_bitfield(bitfield const& /*bitfield*/) { return false; } virtual bool on_have_all() { return false; } virtual bool on_have_none() { return false; } virtual bool on_allowed_fast(int /*index*/) { return false; } virtual bool on_request(peer_request const&) { return false; } virtual bool on_piece(peer_request const& /*piece*/ , disk_buffer_holder& /*data*/) { return false; } virtual bool on_cancel(peer_request const&) { return false; } virtual bool on_reject(peer_request const&) { return false; } virtual bool on_suggest(int /*index*/) { return false; } // called after a choke message has been sent to the peer virtual void sent_unchoke() {} // called after piece data has been sent to the peer // this can be used for stats book keeping virtual void sent_payload(int /* bytes */) {} // called when libtorrent think this peer should be disconnected. // if the plugin returns false, the peer will not be disconnected. virtual bool can_disconnect(error_code const& /*ec*/) { return true; } // called when an extended message is received. If returning true, // the message is not processed by any other plugin and if false // is returned the next plugin in the chain will receive it to // be able to handle it. This is not called for web seeds. // thus function may be called more than once per incoming message, but // only the last of the calls will the ``body`` size equal the ``length``. // i.e. Every time another fragment of the message is received, this // function will be called, until finally the whole message has been // received. The purpose of this is to allow early disconnects for invalid // messages and for reporting progress of receiving large messages. virtual bool on_extended(int /*length*/, int /*msg*/, buffer::const_interval /*body*/) { return false; } // this is not called for web seeds virtual bool on_unknown_message(int /*length*/, int /*msg*/, buffer::const_interval /*body*/) { return false; } // called when a piece that this peer participated in either // fails or passes the hash_check virtual void on_piece_pass(int /*index*/) {} virtual void on_piece_failed(int /*index*/) {} // called approximately once every second virtual void tick() {} // called each time a request message is to be sent. If true // is returned, the original request message won't be sent and // no other plugin will have this function called. virtual bool write_request(peer_request const&) { return false; } }; struct TORRENT_EXPORT crypto_plugin { // hidden virtual ~crypto_plugin() {} virtual void set_incoming_key(unsigned char const* key, int len) = 0; virtual void set_outgoing_key(unsigned char const* key, int len) = 0; // encrypted the provided buffers and returns the number of bytes which // are now ready to be sent to the lower layer. This must be at least // as large as the number of bytes passed in and may be larger if there // is additional data to be inserted at the head of the send buffer. // The additional data is retrieved from the passed in vector. The // vector must be cleared if no additional data is to be inserted. virtual int encrypt(std::vector& /*send_vec*/) = 0; // decrypt the provided buffers. // consume is set to the number of bytes which should be trimmed from the // head of the buffers, default is 0 // // produce is set to the number of bytes of payload which are now ready to // be sent to the upper layer. default is the number of bytes passed in receive_vec // // packet_size is set to the minimum number of bytes which must be read to // advance the next step of decryption. default is 0 virtual void decrypt(std::vector& /*receive_vec*/ , int& /* consume */, int& /*produce*/, int& /*packet_size*/) = 0; }; } #endif #endif // TORRENT_EXTENSIONS_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/extensions/000077500000000000000000000000001351156116000237515ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/include/libtorrent/extensions/lt_trackers.hpp000066400000000000000000000044461351156116000270070ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_LT_TRACKERS_HPP_INCLUDED #define TORRENT_LT_TRACKERS_HPP_INCLUDED #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct torrent_plugin; struct torrent_handle; // constructor function for the trackers exchange extension. This can // either be passed in the add_torrent_params::extensions field, or // via torrent_handle::add_extension(). TORRENT_DEPRECATED boost::shared_ptr TORRENT_EXPORT create_lt_trackers_plugin(torrent_handle const&, void*); } #endif // TORRENT_DISABLE_EXTENSIONS #endif // TORRENT_NO_DEPRECATE #endif // TORRENT_LT_TRACKERS_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/extensions/metadata_transfer.hpp000066400000000000000000000046371351156116000301600ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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_METADATA_TRANSFER_HPP_INCLUDED #define TORRENT_METADATA_TRANSFER_HPP_INCLUDED #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifndef TORRENT_NO_DEPRECATE namespace libtorrent { struct torrent_plugin; struct torrent_handle; #ifndef TORRENT_NO_DEPRECATE // constructor function for the metadata transfer extension. This // extension has been superseded by the ut_metadata extension and // is deprecated. It can be either be passed in the // add_torrent_params::extensions field, or // via torrent_handle::add_extension(). TORRENT_DEPRECATED TORRENT_EXPORT boost::shared_ptr create_metadata_plugin(torrent_handle const&, void*); #endif } #endif #endif // TORRENT_DISABLE_EXTENSIONS #endif // TORRENT_METADATA_TRANSFER_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/extensions/smart_ban.hpp000066400000000000000000000046271351156116000264410ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_SMART_BAN_HPP_INCLUDED #define TORRENT_SMART_BAN_HPP_INCLUDED #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct torrent_plugin; struct torrent_handle; // constructor function for the smart ban extension. The extension keeps // track of the data peers have sent us for failing pieces and once the // piece completes and passes the hash check bans the peers that turned // out to have sent corrupt data. // This function can either be passed in the add_torrent_params::extensions // field, or via torrent_handle::add_extension(). TORRENT_EXPORT boost::shared_ptr create_smart_ban_plugin(torrent_handle const&, void*); } #endif // TORRENT_DISABLE_EXTENSIONS #endif // TORRENT_SMART_BAN_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/extensions/ut_metadata.hpp000066400000000000000000000050301351156116000267500ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_UT_METADATA_HPP_INCLUDED #define TORRENT_UT_METADATA_HPP_INCLUDED #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct torrent_plugin; struct torrent_handle; // constructor function for the ut_metadata extension. The ut_metadata // extension allows peers to request the .torrent file (or more // specifically the 'info'-dictionary of the .torrent file) from each // other. This is the main building block in making magnet links work. // This extension is enabled by default unless explicitly disabled in // the session constructor. // // This can either be passed in the add_torrent_params::extensions field, or // via torrent_handle::add_extension(). TORRENT_EXPORT boost::shared_ptr create_ut_metadata_plugin(torrent_handle const&, void*); } #endif // TORRENT_DISABLE_EXTENSIONS #endif // TORRENT_UT_METADATA_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/extensions/ut_pex.hpp000066400000000000000000000051471351156116000257750ustar00rootroot00000000000000/* Copyright (c) 2006, MassaRoddel 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_UT_PEX_EXTENSION_HPP_INCLUDED #define TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED #include "libtorrent/config.hpp" #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/socket.hpp" // for endpoint #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct torrent_plugin; struct peer_plugin; struct torrent_handle; // constructor function for the ut_pex extension. The ut_pex // extension allows peers to gossip about their connections, allowing // the swarm stay well connected and peers aware of more peers in the // swarm. This extension is enabled by default unless explicitly disabled in // the session constructor. // // This can either be passed in the add_torrent_params::extensions field, or // via torrent_handle::add_extension(). TORRENT_EXPORT boost::shared_ptr create_ut_pex_plugin(torrent_handle const&, void*); bool was_introduced_by(peer_plugin const* pp, tcp::endpoint const& ep); } #endif // TORRENT_DISABLE_EXTENSIONS #endif // TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/file.hpp000066400000000000000000000244651351156116000232150ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #ifdef TORRENT_WINDOWS // windows part #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #else // posix part #define _FILE_OFFSET_BITS 64 #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif #include #include #include #include #include // for DIR #undef _FILE_OFFSET_BITS #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/time.hpp" namespace libtorrent { #ifdef TORRENT_WINDOWS typedef HANDLE handle_type; #else typedef int handle_type; #endif struct file_status { boost::int64_t file_size; boost::uint64_t atime; boost::uint64_t mtime; boost::uint64_t ctime; enum { #if defined TORRENT_WINDOWS fifo = 0x1000, // named pipe (fifo) character_special = 0x2000, // character special directory = 0x4000, // directory regular_file = 0x8000 // regular #else fifo = 0010000, // named pipe (fifo) character_special = 0020000, // character special directory = 0040000, // directory block_special = 0060000, // block special regular_file = 0100000, // regular link = 0120000, // symbolic link socket = 0140000 // socket #endif } modes_t; int mode; }; // internal flags for stat_file enum { dont_follow_links = 1 }; TORRENT_EXTRA_EXPORT void stat_file(std::string const& f, file_status* s , error_code& ec, int flags = 0); TORRENT_EXTRA_EXPORT void rename(std::string const& f , std::string const& newf, error_code& ec); TORRENT_EXTRA_EXPORT void create_directories(std::string const& f , error_code& ec); TORRENT_EXTRA_EXPORT void create_directory(std::string const& f , error_code& ec); TORRENT_EXTRA_EXPORT void remove_all(std::string const& f , error_code& ec); TORRENT_EXTRA_EXPORT void remove(std::string const& f, error_code& ec); TORRENT_EXTRA_EXPORT bool exists(std::string const& f, error_code& ec); TORRENT_EXTRA_EXPORT bool exists(std::string const& f); TORRENT_EXTRA_EXPORT boost::int64_t file_size(std::string const& f); TORRENT_EXTRA_EXPORT bool is_directory(std::string const& f , error_code& ec); TORRENT_EXTRA_EXPORT void recursive_copy(std::string const& old_path , std::string const& new_path, error_code& ec); TORRENT_EXTRA_EXPORT void copy_file(std::string const& f , std::string const& newf, error_code& ec); TORRENT_EXTRA_EXPORT void move_file(std::string const& f , std::string const& newf, error_code& ec); // file is expected to exist, link will be created to point to it. If hard // links are not supported by the filesystem or OS, the file will be copied. TORRENT_EXTRA_EXPORT void hard_link(std::string const& file , std::string const& link, error_code& ec); TORRENT_EXTRA_EXPORT std::string split_path(std::string const& f); TORRENT_EXTRA_EXPORT char const* next_path_element(char const* p); TORRENT_EXTRA_EXPORT std::string extension(std::string const& f); TORRENT_EXTRA_EXPORT std::string remove_extension(std::string const& f); TORRENT_EXTRA_EXPORT void replace_extension(std::string& f, std::string const& ext); TORRENT_EXTRA_EXPORT bool is_root_path(std::string const& f); TORRENT_EXTRA_EXPORT bool compare_path(std::string const& lhs, std::string const& rhs); // internal used by create_torrent.hpp TORRENT_EXTRA_EXPORT std::string parent_path(std::string const& f); TORRENT_EXTRA_EXPORT bool has_parent_path(std::string const& f); TORRENT_EXTRA_EXPORT char const* filename_cstr(char const* f); // internal used by create_torrent.hpp TORRENT_EXTRA_EXPORT std::string filename(std::string const& f); TORRENT_EXTRA_EXPORT std::string combine_path(std::string const& lhs , std::string const& rhs); TORRENT_EXTRA_EXPORT void append_path(std::string& branch , std::string const& leaf); TORRENT_EXTRA_EXPORT void append_path(std::string& branch , char const* str, int len); // internal used by create_torrent.hpp TORRENT_EXTRA_EXPORT std::string complete(std::string const& f); TORRENT_EXTRA_EXPORT bool is_complete(std::string const& f); TORRENT_EXTRA_EXPORT std::string current_working_directory(); #if TORRENT_USE_UNC_PATHS TORRENT_EXTRA_EXPORT std::string canonicalize_path(std::string const& f); #endif // TODO: move this into a separate header file, TU pair class TORRENT_EXTRA_EXPORT directory : public boost::noncopyable { public: directory(std::string const& path, error_code& ec); ~directory(); void next(error_code& ec); std::string file() const; boost::uint64_t inode() const; bool done() const { return m_done; } private: #ifdef TORRENT_WINDOWS HANDLE m_handle; int m_inode; #if TORRENT_USE_WSTRING WIN32_FIND_DATAW m_fd; #else WIN32_FIND_DATAA m_fd; #endif #else DIR* m_handle; ino_t m_inode; std::string m_name; #endif bool m_done; }; struct file; #ifdef TORRENT_DEBUG_FILE_LEAKS struct file_handle { file_handle(); file_handle(file* f); file_handle(file_handle const& fh); ~file_handle(); file* operator->(); file const* operator->() const; file& operator*(); file const& operator*() const; file* get(); file const* get() const; operator bool() const; file_handle& reset(file* f = NULL); char stack[2048]; private: boost::shared_ptr m_file; }; void TORRENT_EXTRA_EXPORT print_open_files(char const* event, char const* name); #else typedef boost::shared_ptr file_handle; #endif struct TORRENT_EXTRA_EXPORT file: boost::noncopyable { // the open mode for files. Used for the file constructor or // file::open(). enum open_mode_t { // open the file for reading only read_only = 0, // open the file for writing only write_only = 1, // open the file for reading and writing read_write = 2, // the mask for the bits determining read or write mode rw_mask = read_only | write_only | read_write, // open the file in sparse mode (if supported by the // filesystem). sparse = 0x4, // don't update the access timestamps on the file (if // supported by the operating system and filesystem). // this generally improves disk performance. no_atime = 0x8, // open the file for random access. This disables read-ahead // logic random_access = 0x10, // prevent the file from being opened by another process // while it's still being held open by this handle lock_file = 0x20, // don't put any pressure on the OS disk cache // because of access to this file. We expect our // files to be fairly large, and there is already // a cache at the bittorrent block level. This // may improve overall system performance by // leaving running applications in the page cache no_cache = 0x40, // this is only used for readv/writev flags coalesce_buffers = 0x100, // when creating a file, set the hidden attribute (windows only) attribute_hidden = 0x200, // when creating a file, set the executable attribute attribute_executable = 0x400, // the mask of all attribute bits attribute_mask = attribute_hidden | attribute_executable }; #ifdef TORRENT_WINDOWS struct iovec_t { void* iov_base; size_t iov_len; }; #else typedef iovec iovec_t; #endif // use a typedef for the type of iovec_t::iov_base // since it may differ #ifdef TORRENT_SOLARIS typedef char* iovec_base_t; #else typedef void* iovec_base_t; #endif file(); file(std::string const& p, int m, error_code& ec); ~file(); bool open(std::string const& p, int m, error_code& ec); bool is_open() const; void close(); bool set_size(boost::int64_t size, error_code& ec); int open_mode() const { return m_open_mode; } boost::int64_t writev(boost::int64_t file_offset, iovec_t const* bufs, int num_bufs , error_code& ec, int flags = 0); boost::int64_t readv(boost::int64_t file_offset, iovec_t const* bufs, int num_bufs , error_code& ec, int flags = 0); boost::int64_t get_size(error_code& ec) const; // return the offset of the first byte that // belongs to a data-region boost::int64_t sparse_end(boost::int64_t start) const; handle_type native_handle() const { return m_file_handle; } #ifdef TORRENT_DISK_STATS boost::uint32_t file_id() const { return m_file_id; } #endif #ifdef TORRENT_DEBUG_FILE_LEAKS void print_info(FILE* out) const; #endif private: handle_type m_file_handle; #ifdef TORRENT_DISK_STATS boost::uint32_t m_file_id; #endif int m_open_mode; #ifdef TORRENT_DEBUG_FILE_LEAKS std::string m_file_path; #endif }; TORRENT_EXTRA_EXPORT int bufs_size(file::iovec_t const* bufs, int num_bufs); } #endif // TORRENT_FILE_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/file_pool.hpp000066400000000000000000000124361351156116000242410ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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_POOL_HPP #define TORRENT_FILE_POOL_HPP #include #include "libtorrent/file.hpp" #include "libtorrent/time.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/file_storage.hpp" #include "libtorrent/aux_/time.hpp" namespace libtorrent { struct pool_file_status { // the index of the file this entry refers to into the ``file_storage`` // file list of this torrent. This starts indexing at 0. int file_index; // a (high precision) timestamp of when the file was last used. time_point last_use; // ``open_mode`` is a bitmask of the file flags this file is currently opened with. These // are the flags used in the ``file::open()`` function. This enum is defined as a member // of the ``file`` class. // // :: // // enum // { // read_only = 0, // write_only = 1, // read_write = 2, // rw_mask = 3, // no_buffer = 4, // sparse = 8, // no_atime = 16, // random_access = 32, // lock_file = 64, // }; // // Note that the read/write mode is not a bitmask. The two least significant bits are used // to represent the read/write mode. Those bits can be masked out using the ``rw_mask`` constant. int open_mode; }; // this is an internal cache of open file handles. It's primarily used by // storage_interface implementations. It provides semi weak guarantees of // not opening more file handles than specified. Given multiple threads, // each with the ability to lock a file handle (via smart pointer), there // may be windows where more file handles are open. struct TORRENT_EXPORT file_pool : boost::noncopyable { // ``size`` specifies the number of allowed files handles // to hold open at any given time. file_pool(int size = 40); ~file_pool(); // return an open file handle to file at ``file_index`` in the // file_storage ``fs`` opened at save path ``p``. ``m`` is the // file open mode (see file::open_mode_t). file_handle open_file(void* st, std::string const& p , int file_index, file_storage const& fs, int m, error_code& ec); // release all files belonging to the specified storage_interface (``st``) // the overload that takes ``file_index`` releases only the file with // that index in storage ``st``. void release(void* st = NULL); void release(void* st, int file_index); // update the allowed number of open file handles to ``size``. void resize(int size); // returns the current limit of number of allowed open file handles held // by the file_pool. int size_limit() const { return m_size; } // internal void set_low_prio_io(bool b) { m_low_prio_io = b; } void get_status(std::vector* files, void* st) const; // close the file that was opened least recently (i.e. not *accessed* // least recently). The purpose is to make the OS (really just windows) // clear and flush its disk cache associated with this file. We don't want // any file to stay open for too long, allowing the disk cache to accrue. void close_oldest(); #if TORRENT_USE_ASSERTS bool assert_idle_files(void* st) const; // remember that this storage has had // its files deleted. We may not open any // files from it again void mark_deleted(file_storage const& fs); #endif private: file_handle remove_oldest(mutex::scoped_lock&); int m_size; bool m_low_prio_io; struct lru_file_entry { lru_file_entry() : opened(aux::time_now()) , last_use(opened) , mode(0) {} file_handle file_ptr; time_point const opened; time_point last_use; int mode; }; // maps storage pointer, file index pairs to the // LRU entry for the file typedef std::map, lru_file_entry> file_set; file_set m_files; #if TORRENT_USE_ASSERTS std::vector > m_deleted_storages; #endif mutable mutex m_mutex; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/file_storage.hpp000066400000000000000000000600531351156116000247320ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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_STORAGE_HPP_INCLUDED #define TORRENT_FILE_STORAGE_HPP_INCLUDED #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/peer_request.hpp" #include "libtorrent/peer_id.hpp" namespace libtorrent { struct file; #ifndef TORRENT_NO_DEPRECATE // information about a file in a file_storage struct TORRENT_DEPRECATED_EXPORT file_entry { // hidden file_entry(); // hidden ~file_entry(); #if __cplusplus >= 201103L file_entry(file_entry const&) = default; file_entry& operator=(file_entry const&) = default; #endif // the full path of this file. The paths are unicode strings // encoded in UTF-8. std::string path; // the path which this is a symlink to, or empty if this is // not a symlink. This field is only used if the ``symlink_attribute`` is set. std::string symlink_path; // the offset of this file inside the torrent boost::int64_t offset; // the size of the file (in bytes) and ``offset`` is the byte offset // of the file within the torrent. i.e. the sum of all the sizes of the files // before it in the list. boost::int64_t size; // the offset in the file where the storage should start. The normal // case is to have this set to 0, so that the storage starts saving data at the start // if the file. In cases where multiple files are mapped into the same file though, // the ``file_base`` should be set to an offset so that the different regions do // not overlap. This is used when mapping "unselected" files into a so-called part // file. boost::int64_t file_base; // the modification time of this file specified in posix time. std::time_t mtime; // a SHA-1 hash of the content of the file, or zeros, if no // file hash was present in the torrent file. It can be used to potentially // find alternative sources for the file. sha1_hash filehash; // set to true for files that are not part of the data of the torrent. // They are just there to make sure the next file is aligned to a particular byte offset // or piece boundry. These files should typically be hidden from an end user. They are // not written to disk. bool pad_file:1; // true if the file was marked as hidden (on windows). bool hidden_attribute:1; // true if the file was marked as executable (posix) bool executable_attribute:1; // true if the file was a symlink. If this is the case // the ``symlink_index`` refers to a string which specifies the original location // where the data for this file was found. bool symlink_attribute:1; }; #endif // TORRENT_NO_DEPRECATE // internal struct TORRENT_DEPRECATED_EXPORT internal_file_entry { friend class file_storage; #ifdef TORRENT_DEBUG // for torrent_info::invariant_check friend class torrent_info; #endif internal_file_entry() : offset(0) , symlink_index(not_a_symlink) , no_root_dir(false) , size(0) , name_len(name_is_owned) , pad_file(false) , hidden_attribute(false) , executable_attribute(false) , symlink_attribute(false) , name(NULL) , path_index(-1) {} internal_file_entry(internal_file_entry const& fe); internal_file_entry& operator=(internal_file_entry const& fe); ~internal_file_entry(); void set_name(char const* n, bool borrow_string = false, int string_len = 0); std::string filename() const; char const* filename_ptr() const { return name; } int filename_len() const { return name_len == name_is_owned?int(strlen(name)):int(name_len); } enum { name_is_owned = (1<<12)-1, not_a_symlink = (1<<15)-1 }; // the offset of this file inside the torrent boost::uint64_t offset:48; // index into file_storage::m_symlinks or not_a_symlink // if this is not a symlink boost::uint64_t symlink_index:15; // if this is true, don't include m_name as part of the // path to this file boost::uint64_t no_root_dir:1; // the size of this file boost::uint64_t size:48; // the number of characters in the name. If this is // name_is_owned, name is null terminated and owned by this object // (i.e. it should be freed in the destructor). If // the len is not name_is_owned, the name pointer doesn not belong // to this object, and it's not null terminated boost::uint64_t name_len:12; boost::uint64_t pad_file:1; boost::uint64_t hidden_attribute:1; boost::uint64_t executable_attribute:1; boost::uint64_t symlink_attribute:1; // make it available for logging private: // This string is not necessarily null terminated! // that's why it's private, to keep people away from it char const* name; public: // the index into file_storage::m_paths. To get // the full path to this file, concatenate the path // from that array with the 'name' field in // this struct // values for path_index include: // -1 means no path (i.e. single file torrent) // -2, it means the filename // in this field contains the full, absolute path // to the file int path_index; }; // represents a window of a file in a torrent. // // The ``file_index`` refers to the index of the file (in the torrent_info). // To get the path and filename, use ``file_path()`` and give the ``file_index`` // as argument. The ``offset`` is the byte offset in the file where the range // starts, and ``size`` is the number of bytes this range is. The size + offset // will never be greater than the file size. struct TORRENT_EXPORT file_slice { // the index of the file int file_index; // the offset from the start of the file, in bytes boost::int64_t offset; // the size of the window, in bytes boost::int64_t size; }; // The ``file_storage`` class represents a file list and the piece // size. Everything necessary to interpret a regular bittorrent storage // file structure. class TORRENT_EXPORT file_storage { friend class torrent_info; public: // hidden file_storage(); // hidden ~file_storage(); file_storage(file_storage const& f); file_storage& operator=(file_storage const&); // returns true if the piece length has been initialized // on the file_storage. This is typically taken as a proxy // of whether the file_storage as a whole is initialized or // not. bool is_valid() const { return m_piece_length > 0; } // file attribute flags enum flags_t { // the file is a pad file. It's required to contain zeros // at it will not be saved to disk. Its purpose is to make // the following file start on a piece boundary. pad_file = 1, // this file has the hidden attribute set. This is primarily // a windows attribute attribute_hidden = 2, // this file has the executable attribute set. attribute_executable = 4, // this file is a symbolic link. It should have a link // target string associated with it. attribute_symlink = 8 }; // allocates space for ``num_files`` in the internal file list. This can // be used to avoid reallocating the internal file list when the number // of files to be added is known up-front. void reserve(int num_files); // Adds a file to the file storage. The ``add_file_borrow`` version // expects that ``filename`` points to a string of ``filename_len`` // bytes that is the file name (without a path) of the file that's // being added. This memory is *borrowed*, i.e. it is the caller's // responsibility to make sure it stays valid throughout the lifetime // of this file_storage object or any copy of it. The same thing applies // to ``filehash``, which is an optional pointer to a 20 byte binary // SHA-1 hash of the file. // // if ``filename`` is NULL, the filename from ``path`` is used and not // borrowed. In this case ``filename_len`` is ignored. // // The ``path`` argument is the full path (in the torrent file) to // the file to add. Note that this is not supposed to be an absolute // path, but it is expected to include the name of the torrent as the // first path element. // // ``file_size`` is the size of the file in bytes. // // The ``file_flags`` argument sets attributes on the file. The file // attributes is an extension and may not work in all bittorrent clients. // // For possible file attributes, see file_storage::flags_t. // // The ``mtime`` argument is optional and can be set to 0. If non-zero, // it is the posix time of the last modification time of this file. // // ``symlink_path`` is the path the file is a symlink to. To make this a // symlink you also need to set the file_storage::flag_symlink file flag. // // If more files than one are added, certain restrictions to their paths // apply. In a multi-file file storage (torrent), all files must share // the same root directory. // // That is, the first path element of all files must be the same. // This shared path element is also set to the name of the torrent. It // can be changed by calling ``set_name``. void add_file_borrow(char const* filename, int filename_len , std::string const& path, boost::int64_t file_size , boost::uint32_t file_flags = 0, char const* filehash = 0 , boost::int64_t mtime = 0, std::string const& symlink_path = ""); void add_file(std::string const& path, boost::int64_t file_size, int file_flags = 0 , std::time_t mtime = 0, std::string const& symlink_path = ""); // renames the file at ``index`` to ``new_filename``. Keep in mind // that filenames are expected to be UTF-8 encoded. void rename_file(int index, std::string const& new_filename); #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED void add_file(file_entry const& fe, char const* filehash = NULL); #if TORRENT_USE_WSTRING // all wstring APIs are deprecated since 0.16.11 // instead, use the wchar -> utf8 conversion functions // and pass in utf8 strings TORRENT_DEPRECATED void add_file(std::wstring const& p, boost::int64_t size, int flags = 0 , std::time_t mtime = 0, std::string const& s_p = ""); TORRENT_DEPRECATED void rename_file(int index, std::wstring const& new_filename); TORRENT_DEPRECATED void set_name(std::wstring const& n); void rename_file_deprecated(int index, std::wstring const& new_filename); #endif // TORRENT_USE_WSTRING #endif // TORRENT_NO_DEPRECATE // returns a list of file_slice objects representing the portions of // files the specified piece index, byte offset and size range overlaps. // this is the inverse mapping of map_file(). // // Preconditions of this function is that the input range is within the // torrents address space. ``piece`` may not be negative and // // ``piece`` * piece_size + ``offset`` + ``size`` // // may not exceed the total size of the torrent. std::vector map_block(int piece, boost::int64_t offset , int size) const; // returns a peer_request representing the piece index, byte offset // and size the specified file range overlaps. This is the inverse // mapping over map_block(). Note that the ``peer_request`` return type // is meant to hold bittorrent block requests, which may not be larger // than 16 kiB. Mapping a range larger than that may return an overflown // integer. peer_request map_file(int file, boost::int64_t offset, int size) const; #ifndef TORRENT_NO_DEPRECATE // all functions depending on internal_file_entry // were deprecated in 1.0. Use the variants that take an // index instead typedef std::vector::const_iterator iterator; typedef std::vector::const_reverse_iterator reverse_iterator; TORRENT_DEPRECATED iterator file_at_offset(boost::int64_t offset) const; TORRENT_DEPRECATED iterator begin() const { return m_files.begin(); } TORRENT_DEPRECATED iterator end() const { return m_files.end(); } TORRENT_DEPRECATED reverse_iterator rbegin() const { return m_files.rbegin(); } TORRENT_DEPRECATED reverse_iterator rend() const { return m_files.rend(); } TORRENT_DEPRECATED internal_file_entry const& internal_at(int index) const { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_files.size())); return m_files[index]; } TORRENT_DEPRECATED file_entry at(iterator i) const; // returns a file_entry with information about the file // at ``index``. Index must be in the range [0, ``num_files()`` ). TORRENT_DEPRECATED file_entry at(int index) const; iterator begin_deprecated() const { return m_files.begin(); } iterator end_deprecated() const { return m_files.end(); } reverse_iterator rbegin_deprecated() const { return m_files.rbegin(); } reverse_iterator rend_deprecated() const { return m_files.rend(); } iterator file_at_offset_deprecated(boost::int64_t offset) const; file_entry at_deprecated(int index) const; #endif // TORRENT_NO_DEPRECATE // returns the number of files in the file_storage int num_files() const { return int(m_files.size()); } // returns the total number of bytes all the files in this torrent spans boost::int64_t total_size() const { return m_total_size; } // set and get the number of pieces in the torrent void set_num_pieces(int n) { m_num_pieces = n; } int num_pieces() const { TORRENT_ASSERT(m_piece_length > 0); return m_num_pieces; } // set and get the size of each piece in this torrent. This size is typically an even power // of 2. It doesn't have to be though. It should be divisible by 16 kiB however. void set_piece_length(int l) { m_piece_length = l; } int piece_length() const { TORRENT_ASSERT(m_piece_length > 0); return m_piece_length; } // returns the piece size of ``index``. This will be the same as piece_length(), except // for the last piece, which may be shorter. int piece_size(int index) const; // set and get the name of this torrent. For multi-file torrents, this is also // the name of the root directory all the files are stored in. void set_name(std::string const& n) { m_name = n; } std::string const& name() const { return m_name; } // swap all content of *this* with *ti*. void swap(file_storage& ti) { using std::swap; swap(ti.m_files, m_files); swap(ti.m_num_files, m_num_files); swap(ti.m_file_hashes, m_file_hashes); swap(ti.m_symlinks, m_symlinks); swap(ti.m_mtime, m_mtime); #ifndef TORRENT_NO_DEPRECATE swap(ti.m_file_base, m_file_base); #endif swap(ti.m_paths, m_paths); swap(ti.m_name, m_name); swap(ti.m_total_size, m_total_size); swap(ti.m_num_pieces, m_num_pieces); swap(ti.m_piece_length, m_piece_length); } // deallocates most of the memory used by this // instance, leaving it only partially usable void unload(); // returns true when populated with at least one file bool is_loaded() const { return !m_files.empty(); } // if pad_file_limit >= 0, files larger than that limit will be padded, // default is to not add any padding (-1). The alignment specifies the // alignment files should be padded to. This defaults to the piece size // (-1) but it may also make sense to set it to 16 kiB, or something // divisible by 16 kiB. // If pad_file_limit is 0, every file will be padded (except empty ones). // ``tail_padding`` indicates whether aligned files also are padded at // the end to make them end aligned. This is required for mutable // torrents, since piece hashes are compared void optimize(int pad_file_limit = -1, int alignment = -1 , bool tail_padding = false); // These functions are used to query attributes of files at // a given index. // // The ``hash()`` is a SHA-1 hash of the file, or 0 if none was // provided in the torrent file. This can potentially be used to // join a bittorrent network with other file sharing networks. // // The ``mtime()`` is the modification time is the posix // time when a file was last modified when the torrent // was created, or 0 if it was not included in the torrent file. // // ``file_path()`` returns the full path to a file. // // ``file_size()`` returns the size of a file. // // ``pad_file_at()`` returns true if the file at the given // index is a pad-file. // // ``file_name()`` returns *just* the name of the file, whereas // ``file_path()`` returns the path (inside the torrent file) with // the filename appended. // // ``file_offset()`` returns the byte offset within the torrent file // where this file starts. It can be used to map the file to a piece // index (given the piece size). sha1_hash hash(int index) const; std::string const& symlink(int index) const; time_t mtime(int index) const; std::string file_path(int index, std::string const& save_path = "") const; std::string file_name(int index) const; boost::int64_t file_size(int index) const; bool pad_file_at(int index) const; boost::int64_t file_offset(int index) const; // returns the crc32 hash of file_path(index) boost::uint32_t file_path_hash(int index, std::string const& save_path) const; // this will add the CRC32 hash of all directory entries to the table. No // filename will be included, just directories. Every depth of directories // are added separately to allow test for collisions with files at all // levels. i.e. if one path in the torrent is ``foo/bar/baz``, the CRC32 // hashes for ``foo``, ``foo/bar`` and ``foo/bar/baz`` will be added to // the set. void all_path_hashes(boost::unordered_set& table) const; // flags indicating various attributes for files in // a file_storage. enum file_flags_t { // this file is a pad file. The creator of the // torrent promises the file is entirely filled with // zeros and does not need to be downloaded. The // purpose is just to align the next file to either // a block or piece boundary. flag_pad_file = 1, // this file is hidden (sets the hidden attribute // on windows) flag_hidden = 2, // this file is executable (sets the executable bit // on posix like systems) flag_executable = 4, // this file is a symlink. The symlink target is // specified in a separate field flag_symlink = 8 }; std::vector const& paths() const { return m_paths; } // returns a bitmask of flags from file_flags_t that apply // to file at ``index``. int file_flags(int index) const; // returns true if the file at the specified index has been renamed to // have an absolute path, i.e. is not anchored in the save path of the // torrent. bool file_absolute_path(int index) const; // returns the index of the file at the given offset in the torrent int file_index_at_offset(boost::int64_t offset) const; // low-level function. returns a pointer to the internal storage for // the filename. This string may not be null terminated! // the ``file_name_len()`` function returns the length of the filename. char const* file_name_ptr(int index) const; int file_name_len(int index) const; #ifndef TORRENT_NO_DEPRECATE // deprecated in 1.1 boost::int64_t file_base_deprecated(int index) const; TORRENT_DEPRECATED boost::int64_t file_base(int index) const; TORRENT_DEPRECATED void set_file_base(int index, boost::int64_t off); // these were deprecated in 1.0. Use the versions that take an index instead TORRENT_DEPRECATED sha1_hash hash(internal_file_entry const& fe) const; TORRENT_DEPRECATED std::string const& symlink(internal_file_entry const& fe) const; TORRENT_DEPRECATED time_t mtime(internal_file_entry const& fe) const; TORRENT_DEPRECATED int file_index(internal_file_entry const& fe) const; TORRENT_DEPRECATED boost::int64_t file_base(internal_file_entry const& fe) const; TORRENT_DEPRECATED void set_file_base(internal_file_entry const& fe, boost::int64_t off); TORRENT_DEPRECATED std::string file_path(internal_file_entry const& fe, std::string const& save_path = "") const; TORRENT_DEPRECATED std::string file_name(internal_file_entry const& fe) const; TORRENT_DEPRECATED boost::int64_t file_size(internal_file_entry const& fe) const; TORRENT_DEPRECATED bool pad_file_at(internal_file_entry const& fe) const; TORRENT_DEPRECATED boost::int64_t file_offset(internal_file_entry const& fe) const; #endif // if the backing buffer changed for this storage, this is the pointer // offset to add to any pointers to make them point into the new buffer void apply_pointer_offset(ptrdiff_t off); private: int get_or_add_path(char const* branch_path, int branch_len); void add_pad_file(int size , std::vector::iterator& i , boost::int64_t& offset , int& pad_file_counter); // the number of bytes in a regular piece // (i.e. not the potentially truncated last piece) int m_piece_length; // the number of pieces in the torrent int m_num_pieces; void update_path_index(internal_file_entry& e, std::string const& path , bool set_name = true); void reorder_file(int index, int dst); // the list of files that this torrent consists of std::vector m_files; // if there are sha1 hashes for each individual file there are as many // entries in this array as the m_files array. Each entry in m_files has // a corresponding hash pointer in this array. The reason to split it up // in separate arrays is to save memory in case the torrent doesn't have // file hashes // the pointers in this vector are pointing into the .torrent file in // memory which is _not_ owned by this file_storage object. It's simply // a non-owning pointer. It is the user's responsibility that the hash // stays valid throughout the lifetime of this file_storage object. std::vector m_file_hashes; // for files that are symlinks, the symlink // path_index in the internal_file_entry indexes // this vector of strings std::vector m_symlinks; // the modification times of each file. This vector // is empty if no file have a modification time. // each element corresponds to the file with the same // index in m_files std::vector m_mtime; #ifndef TORRENT_NO_DEPRECATE // if any file has a non-zero file base (i.e. multiple // files residing in the same physical file at different // offsets) std::vector m_file_base; #endif // all unique paths files have. The internal_file_entry::path_index // points into this array. The paths don't include the root directory // name for multi-file torrents. The m_name field need to be // prepended to these paths, and the filename of a specific file // entry appended, to form full file paths std::vector m_paths; // name of torrent. For multi-file torrents // this is always the root directory std::string m_name; // the sum of all file sizes boost::int64_t m_total_size; // the number of files. This is used when // the torrent is unloaded int m_num_files; }; } #endif // TORRENT_FILE_STORAGE_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/fingerprint.hpp000066400000000000000000000072731351156116000246230ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include #include "libtorrent/config.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/export.hpp" namespace libtorrent { // This is a utility function to produce a client ID fingerprint formatted to // the most common convention. // // The name string should contain exactly two characters. These are the // characters unique to your client, used to identify it. Make sure not to // clash with anybody else. Here are some taken id's: // // +----------+-----------------------+ // | id chars | client | // +==========+=======================+ // | 'AZ' | Azureus | // +----------+-----------------------+ // | 'LT' | libtorrent (default) | // +----------+-----------------------+ // | 'BX' | BittorrentX | // +----------+-----------------------+ // | 'MT' | Moonlight Torrent | // +----------+-----------------------+ // | 'TS' | Torrent Storm | // +----------+-----------------------+ // | 'SS' | Swarm Scope | // +----------+-----------------------+ // | 'XT' | Xan Torrent | // +----------+-----------------------+ // // There's an informal directory of client id's here_. // // .. _here: http://wiki.theory.org/BitTorrentSpecification#peer_id // // The ``major``, ``minor``, ``revision`` and ``tag`` parameters are used to // identify the version of your client. TORRENT_EXPORT std::string generate_fingerprint(std::string name , int major, int minor = 0, int revision = 0, int tag = 0); // The fingerprint class represents information about a client and its version. It is used // to encode this information into the client's peer id. struct TORRENT_DEPRECATED TORRENT_DEPRECATED_EXPORT fingerprint { fingerprint(const char* id_string, int major, int minor, int revision, int tag); #ifndef TORRENT_NO_DEPRECATE // generates the actual string put in the peer-id, and return it. std::string to_string() const; #endif char name[2]; int major_version; int minor_version; int revision_version; int tag_version; }; } #endif // TORRENT_FINGERPRINT_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/fwd.hpp000066400000000000000000000155561351156116000230570ustar00rootroot00000000000000/* Copyright (c) 2017-2018, 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_FWD_HPP #define TORRENT_FWD_HPP namespace libtorrent { // include/libtorrent/add_torrent_params.hpp struct add_torrent_params; // include/libtorrent/alert.hpp class alert; // include/libtorrent/alert_types.hpp struct torrent_alert; struct peer_alert; struct tracker_alert; struct torrent_removed_alert; struct read_piece_alert; struct file_completed_alert; struct file_renamed_alert; struct file_rename_failed_alert; struct performance_alert; struct state_changed_alert; struct tracker_error_alert; struct tracker_warning_alert; struct scrape_reply_alert; struct scrape_failed_alert; struct tracker_reply_alert; struct dht_reply_alert; struct tracker_announce_alert; struct hash_failed_alert; struct peer_ban_alert; struct peer_unsnubbed_alert; struct peer_snubbed_alert; struct peer_error_alert; struct peer_connect_alert; struct peer_disconnected_alert; struct invalid_request_alert; struct torrent_finished_alert; struct piece_finished_alert; struct request_dropped_alert; struct block_timeout_alert; struct block_finished_alert; struct block_downloading_alert; struct unwanted_block_alert; struct storage_moved_alert; struct storage_moved_failed_alert; struct torrent_deleted_alert; struct torrent_delete_failed_alert; struct save_resume_data_alert; struct save_resume_data_failed_alert; struct torrent_paused_alert; struct torrent_resumed_alert; struct torrent_checked_alert; struct url_seed_alert; struct file_error_alert; struct metadata_failed_alert; struct metadata_received_alert; struct udp_error_alert; struct external_ip_alert; struct listen_failed_alert; struct listen_succeeded_alert; struct portmap_error_alert; struct portmap_alert; struct portmap_log_alert; struct fastresume_rejected_alert; struct peer_blocked_alert; struct dht_announce_alert; struct dht_get_peers_alert; struct stats_alert; struct cache_flushed_alert; struct anonymous_mode_alert; struct lsd_peer_alert; struct trackerid_alert; struct dht_bootstrap_alert; struct torrent_error_alert; struct torrent_need_cert_alert; struct incoming_connection_alert; struct add_torrent_alert; struct state_update_alert; struct session_stats_alert; struct torrent_update_alert; struct rss_item_alert; struct dht_error_alert; struct dht_immutable_item_alert; struct dht_mutable_item_alert; struct dht_put_alert; struct i2p_alert; struct dht_outgoing_get_peers_alert; struct log_alert; struct torrent_log_alert; struct peer_log_alert; struct lsd_error_alert; struct dht_lookup; struct dht_routing_bucket; struct dht_stats_alert; struct incoming_request_alert; struct dht_log_alert; struct dht_pkt_alert; struct dht_get_peers_reply_alert; struct dht_direct_response_alert; struct picker_log_alert; // include/libtorrent/announce_entry.hpp struct announce_entry; // include/libtorrent/bdecode.hpp struct bdecode_node; // include/libtorrent/bitfield.hpp struct bitfield; // include/libtorrent/create_torrent.hpp struct create_torrent; // include/libtorrent/disk_buffer_holder.hpp struct disk_buffer_holder; // include/libtorrent/disk_io_thread.hpp struct cache_status; // include/libtorrent/entry.hpp class entry; // include/libtorrent/error_code.hpp struct libtorrent_exception; struct storage_error; // include/libtorrent/extensions.hpp struct plugin; struct torrent_plugin; struct peer_plugin; struct crypto_plugin; // include/libtorrent/file_pool.hpp struct file_pool; // include/libtorrent/file_storage.hpp struct file_slice; class file_storage; // include/libtorrent/hasher.hpp class hasher; // include/libtorrent/ip_filter.hpp struct ip_filter; class port_filter; // include/libtorrent/peer_class.hpp struct peer_class_info; // include/libtorrent/peer_class_type_filter.hpp struct peer_class_type_filter; // include/libtorrent/peer_connection_handle.hpp struct peer_connection_handle; struct bt_peer_connection_handle; // include/libtorrent/peer_info.hpp struct peer_info; struct peer_list_entry; // include/libtorrent/peer_request.hpp struct peer_request; // include/libtorrent/rss.hpp struct feed_item; struct feed_settings; struct feed_status; struct feed_handle; // include/libtorrent/session.hpp class session_proxy; class session; // include/libtorrent/session_handle.hpp struct session_handle; // include/libtorrent/session_settings.hpp struct session_settings; struct dht_settings; struct pe_settings; // include/libtorrent/session_stats.hpp struct stats_metric; // include/libtorrent/session_status.hpp struct utp_status; struct session_status; // include/libtorrent/settings_pack.hpp struct settings_pack; // include/libtorrent/sha1_hash.hpp class sha1_hash; // include/libtorrent/storage.hpp struct storage_interface; class default_storage; // include/libtorrent/storage_defs.hpp struct storage_params; // include/libtorrent/torrent_handle.hpp struct block_info; struct partial_piece_info; struct torrent_handle; // include/libtorrent/torrent_info.hpp struct web_seed_entry; class torrent_info; // include/libtorrent/torrent_status.hpp struct torrent_status; namespace dht { // include/libtorrent/kademlia/dht_storage.hpp struct dht_storage_counters; struct dht_storage_interface; } #ifndef TORRENT_NO_DEPRECATE // include/libtorrent/alert_types.hpp struct torrent_added_alert; struct mmap_cache_alert; // include/libtorrent/file_storage.hpp struct file_entry; struct internal_file_entry; // include/libtorrent/lazy_entry.hpp struct pascal_string; struct lazy_entry; #endif // TORRENT_NO_DEPRECATE } namespace lt = libtorrent; #endif // TORRENT_FWD_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/gzip.hpp000066400000000000000000000102461351156116000232370ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_GZIP_HPP_INCLUDED #define TORRENT_GZIP_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/error_code.hpp" #include namespace libtorrent { TORRENT_EXTRA_EXPORT void inflate_gzip( char const* in, int size , std::vector& buffer , int maximum_size , error_code& error); // get the ``error_category`` for zip errors TORRENT_EXPORT boost::system::error_category& gzip_category(); #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED TORRENT_EXPORT boost::system::error_category& get_gzip_category(); #endif namespace gzip_errors { // libtorrent uses boost.system's ``error_code`` class to represent errors. libtorrent has // its own error category get_gzip_category() with the error codes defined by error_code_enum. enum error_code_enum { // Not an error no_error = 0, // the supplied gzip buffer has invalid header invalid_gzip_header, // the gzip buffer would inflate to more bytes than the specified // maximum size, and was rejected. inflated_data_too_large, // available inflate data did not terminate data_did_not_terminate, // output space exhausted before completing inflate space_exhausted, // invalid block type (type == 3) invalid_block_type, // stored block length did not match one's complement invalid_stored_block_length, // dynamic block code description: too many length or distance codes too_many_length_or_distance_codes, // dynamic block code description: code lengths codes incomplete code_lengths_codes_incomplete, // dynamic block code description: repeat lengths with no first length repeat_lengths_with_no_first_length, // dynamic block code description: repeat more than specified lengths repeat_more_than_specified_lengths, // dynamic block code description: invalid literal/length code lengths invalid_literal_length_code_lengths, // dynamic block code description: invalid distance code lengths invalid_distance_code_lengths, // invalid literal/length or distance code in fixed or dynamic block invalid_literal_code_in_block, // distance is too far back in fixed or dynamic block distance_too_far_back_in_block, // an unknown error occurred during gzip inflation unknown_gzip_error, // the number of error codes error_code_max }; // hidden TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); } } namespace boost { namespace system { template<> struct is_error_code_enum { static const bool value = true; }; template<> struct is_error_condition_enum { static const bool value = true; }; } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/hasher.hpp000066400000000000000000000074671351156116000235530ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/peer_id.hpp" #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifdef TORRENT_USE_GCRYPT #include #elif TORRENT_USE_COMMONCRYPTO #include #elif defined TORRENT_USE_OPENSSL extern "C" { #include } #else #include "libtorrent/sha1.hpp" #endif namespace libtorrent { // this is a SHA-1 hash class. // // You use it by first instantiating it, then call ``update()`` to feed it // with data. i.e. you don't have to keep the entire buffer of which you want to // create the hash in memory. You can feed the hasher parts of it at a time. When // You have fed the hasher with all the data, you call ``final()`` and it // will return the sha1-hash of the data. // // The constructor that takes a ``char const*`` and an integer will construct the // sha1 context and feed it the data passed in. // // If you want to reuse the hasher object once you have created a hash, you have to // call ``reset()`` to reinitialize it. // // The sha1-algorithm used was implemented by Steve Reid and released as public domain. // For more info, see ``src/sha1.cpp``. class TORRENT_EXPORT hasher { public: hasher(); // this is the same as default constructing followed by a call to // ``update(data, len)``. hasher(const char* data, int len); #ifdef TORRENT_USE_GCRYPT hasher(hasher const& h); hasher& operator=(hasher const& h); #endif // append the following bytes to what is being hashed hasher& update(std::string const& data) { update(data.c_str(), int(data.size())); return *this; } hasher& update(const char* data, int len); // returns the SHA-1 digest of the buffers previously passed to // update() and the hasher constructor. sha1_hash final(); // restore the hasher state to be as if the hasher has just been // default constructed. void reset(); ~hasher(); private: #ifdef TORRENT_USE_GCRYPT gcry_md_hd_t m_context; #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_CTX m_context; #elif defined TORRENT_USE_OPENSSL SHA_CTX m_context; #else sha_ctx m_context; #endif }; } #endif // TORRENT_HASHER_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/heterogeneous_queue.hpp000066400000000000000000000135641351156116000263540ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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_HETEROGENEOUS_QUEUE_HPP_INCLUDED #define TORRENT_HETEROGENEOUS_QUEUE_HPP_INCLUDED #include #include #include #include #include "libtorrent/assert.hpp" namespace libtorrent { template struct heterogeneous_queue { heterogeneous_queue() : m_storage(NULL) , m_capacity(0) , m_size(0) , m_num_items(0) {} // TODO: 2 add emplace_back() version template typename boost::enable_if >::type push_back(U const& a) { // the size of the type rounded up to pointer alignment const int object_size = (sizeof(U) + sizeof(*m_storage) - 1) / sizeof(*m_storage); // +1 for the length prefix if (m_size + object_size + header_size > m_capacity) grow_capacity(object_size); uintptr_t* ptr = m_storage + m_size; // length prefix header_t* hdr = reinterpret_cast(ptr); hdr->len = object_size; hdr->move = &move; ptr += header_size; // construct in-place new (ptr) U(a); // if we constructed the object without throwing any exception // update counters to indicate the new item is in there ++m_num_items; m_size += header_size + object_size; } void get_pointers(std::vector& out) { out.clear(); uintptr_t* ptr = m_storage; uintptr_t const* const end = m_storage + m_size; while (ptr < end) { header_t* hdr = reinterpret_cast(ptr); ptr += header_size; TORRENT_ASSERT(ptr + hdr->len <= end); out.push_back(reinterpret_cast(ptr)); ptr += hdr->len; } } void swap(heterogeneous_queue& rhs) { std::swap(m_storage, rhs.m_storage); std::swap(m_capacity, rhs.m_capacity); std::swap(m_size, rhs.m_size); std::swap(m_num_items, rhs.m_num_items); } int size() const { return m_num_items; } bool empty() const { return m_num_items == 0; } void clear() { uintptr_t* ptr = m_storage; uintptr_t const* const end = m_storage + m_size; while (ptr < end) { header_t* hdr = reinterpret_cast(ptr); ptr += header_size; TORRENT_ASSERT(ptr + hdr->len <= end); T* a = reinterpret_cast(ptr); a->~T(); ptr += hdr->len; } m_size = 0; m_num_items = 0; } T* front() { if (m_size == 0) return NULL; TORRENT_ASSERT(m_size > 1); uintptr_t* ptr = m_storage; TORRENT_ASSERT(reinterpret_cast(ptr)->len <= m_size); ptr += header_size; return reinterpret_cast(ptr); } ~heterogeneous_queue() { clear(); delete[] m_storage; } private: // non-copyable heterogeneous_queue(heterogeneous_queue const&); heterogeneous_queue& operator=(heterogeneous_queue const&); // this header is put in front of every element. It tells us // how many uintptr_t's it's using for its allocation, and it // also tells us how to move this type if we need to grow our // allocation. struct header_t { int len; void (*move)(uintptr_t* dst, uintptr_t* src); }; const static int header_size = (sizeof(header_t) + sizeof(uintptr_t) - 1) / sizeof(uintptr_t); void grow_capacity(int const size) { int const amount_to_grow = (std::max)(size + header_size , (std::max)(m_capacity * 3 / 2, 128)); uintptr_t* new_storage = new uintptr_t[m_capacity + amount_to_grow]; uintptr_t* src = m_storage; uintptr_t* dst = new_storage; uintptr_t const* const end = m_storage + m_size; while (src < end) { header_t* src_hdr = reinterpret_cast(src); header_t* dst_hdr = reinterpret_cast(dst); *dst_hdr = *src_hdr; src += header_size; dst += header_size; TORRENT_ASSERT(src + src_hdr->len <= end); // TODO: if this throws, should we do anything? src_hdr->move(dst, src); src += src_hdr->len; dst += src_hdr->len; } delete[] m_storage; m_storage = new_storage; m_capacity += amount_to_grow; } template static void move(uintptr_t* dst, uintptr_t* src) { U* rhs = reinterpret_cast(src); #if __cplusplus >= 201103L new (dst) U(std::move(*rhs)); #else new (dst) U(*rhs); #endif rhs->~U(); } uintptr_t* m_storage; // number of uintptr_t's allocated under m_storage int m_capacity; // the number of uintptr_t's used under m_storage int m_size; // the number of objects allocated under m_storage int m_num_items; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/hex.hpp000066400000000000000000000056151351156116000230560ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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_HEX_HPP_INCLUDED #define TORRENT_HEX_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { namespace detail { TORRENT_EXTRA_EXPORT int hex_to_int(char in); TORRENT_EXTRA_EXPORT bool is_hex(char const *in, int len); } // The overload taking a ``std::string`` converts (binary) the string ``s`` // to hexadecimal representation and returns it. // The overload taking a ``char const*`` and a length converts the binary // buffer [``in``, ``in`` + len) to hexadecimal and prints it to the buffer // ``out``. The caller is responsible for making sure the buffer pointed to // by ``out`` is large enough, i.e. has at least len * 2 bytes of space. TORRENT_EXPORT std::string to_hex(std::string const& s); TORRENT_EXPORT void to_hex(char const *in, int len, char* out); // converts the buffer [``in``, ``in`` + len) from hexadecimal to // binary. The binary output is written to the buffer pointed to // by ``out``. The caller is responsible for making sure the buffer // at ``out`` has enough space for the result to be written to, i.e. // (len + 1) / 2 bytes. TORRENT_EXPORT bool from_hex(char const *in, int len, char* out); } #endif // TORRENT_HEX_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/http_connection.hpp000066400000000000000000000164041351156116000254660ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_CONNECTION #define TORRENT_HTTP_CONNECTION #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #include #include #ifdef TORRENT_USE_OPENSSL // there is no forward declaration header for asio namespace boost { namespace asio { namespace ssl { struct context; } } } #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/http_parser.hpp" #include "libtorrent/deadline_timer.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/i2p_stream.hpp" namespace libtorrent { struct http_connection; struct resolver_interface; const int default_max_bottled_buffer_size = 2*1024*1024; typedef boost::function http_handler; typedef boost::function http_connect_handler; typedef boost::function&)> http_filter_handler; // when bottled, the last two arguments to the handler // will always be 0 struct TORRENT_EXTRA_EXPORT http_connection : boost::enable_shared_from_this , boost::noncopyable { http_connection(io_service& ios , resolver_interface& resolver , http_handler const& handler , bool bottled = true , int max_bottled_buffer_size = default_max_bottled_buffer_size , http_connect_handler const& ch = http_connect_handler() , http_filter_handler const& fh = http_filter_handler() #ifdef TORRENT_USE_OPENSSL , ssl::context* ssl_ctx = 0 #endif ); virtual ~http_connection(); void rate_limit(int limit); int rate_limit() const { return m_rate_limit; } std::string m_sendbuffer; void get(std::string const& url, time_duration timeout = seconds(30) , int prio = 0, aux::proxy_settings const* ps = NULL, int handle_redirects = 5 , std::string const& user_agent = std::string() , boost::optional
bind_addr = boost::none , int resolve_flags = 0, std::string const& auth_ = std::string() #if TORRENT_USE_I2P , i2p_connection* i2p_conn = 0 #endif ); void start(std::string const& hostname, int port , time_duration timeout, int prio = 0, aux::proxy_settings const* ps = NULL , bool ssl = false, int handle_redirect = 5 , boost::optional
bind_addr = boost::none , int resolve_flags = 0 #if TORRENT_USE_I2P , i2p_connection* i2p_conn = 0 #endif ); void close(bool force = false); socket_type const& socket() const { return m_sock; } std::vector const& endpoints() const { return m_endpoints; } private: #if TORRENT_USE_I2P void connect_i2p_tracker(char const* destination); void on_i2p_resolve(error_code const& e , char const* destination); #endif void on_resolve(error_code const& e , std::vector
const& addresses); void connect(); void on_connect(error_code const& e); void on_write(error_code const& e); void on_read(error_code const& e, std::size_t bytes_transferred); static void on_timeout(boost::weak_ptr p , error_code const& e); void on_assign_bandwidth(error_code const& e); void callback(error_code e, char* data = NULL, int size = 0); std::vector m_recvbuffer; std::string m_hostname; std::string m_url; std::string m_user_agent; std::vector m_endpoints; // if the current connection attempt fails, we'll connect to the // endpoint with this index (in m_endpoints) next int m_next_ep; socket_type m_sock; #ifdef TORRENT_USE_OPENSSL ssl::context* m_ssl_ctx; bool m_own_ssl_context; #endif #if TORRENT_USE_I2P i2p_connection* m_i2p_conn; #endif resolver_interface& m_resolver; http_parser m_parser; http_handler m_handler; http_connect_handler m_connect_handler; http_filter_handler m_filter_handler; deadline_timer m_timer; time_duration m_read_timeout; time_duration m_completion_timeout; // the timer fires every 250 millisecond as long // as all the quota was used. deadline_timer m_limiter_timer; time_point m_last_receive; time_point m_start_time; // specifies whether or not the connection is // configured to use a proxy aux::proxy_settings m_proxy; // the address to bind to. unset means do not bind boost::optional
m_bind_addr; // if username password was passed in, remember it in case we need to // re-issue the request for a redirect std::string m_auth; int m_read_pos; // the number of redirects to follow (in sequence) int m_redirects; // maximum size of bottled buffer int m_max_bottled_buffer_size; // the current download limit, in bytes per second // 0 is unlimited. int m_rate_limit; // the number of bytes we are allowed to receive int m_download_quota; // the priority we have in the connection queue. // 0 is normal, 1 is high int m_priority; // used for DNS lookups int m_resolve_flags; boost::uint16_t m_port; // bottled means that the handler is called once, when // everything is received (and buffered in memory). // non bottled means that once the headers have been // received, data is streamed to the handler bool m_bottled; // set to true the first time the handler is called bool m_called; // only hand out new quota 4 times a second if the // quota is 0. If it isn't 0 wait for it to reach // 0 and continue to hand out quota at that time. bool m_limiter_timer_active; // true if the connection is using ssl bool m_ssl; bool m_abort; // true while waiting for an async_connect bool m_connecting; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/http_parser.hpp000066400000000000000000000143421351156116000246220ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_PARSER_HPP_INCLUDED #define TORRENT_HTTP_PARSER_HPP_INCLUDED #include #include #include #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/buffer.hpp" namespace libtorrent { // return true if the status code is 200, 206, or in the 300-400 range TORRENT_EXTRA_EXPORT bool is_ok_status(int http_status); // return true if the status code is a redirect TORRENT_EXTRA_EXPORT bool is_redirect(int http_status); TORRENT_EXTRA_EXPORT std::string resolve_redirect_location(std::string referrer , std::string location); class TORRENT_EXTRA_EXPORT http_parser { public: enum flags_t { dont_parse_chunks = 1 }; http_parser(int flags = 0); ~http_parser(); std::string const& header(char const* key) const { static std::string empty; std::multimap::const_iterator i = m_header.find(key); if (i == m_header.end()) return empty; return i->second; } std::string const& protocol() const { return m_protocol; } int status_code() const { return m_status_code; } std::string const& method() const { return m_method; } std::string const& path() const { return m_path; } std::string const& message() const { return m_server_message; } buffer::const_interval get_body() const; bool header_finished() const { return m_state == read_body; } bool finished() const { return m_finished; } boost::tuple incoming(buffer::const_interval recv_buffer , bool& error); int body_start() const { return m_body_start_pos; } boost::int64_t content_length() const { return m_content_length; } std::pair content_range() const { return std::make_pair(m_range_start, m_range_end); } // returns true if this response is using chunked encoding. // in this case the body is split up into chunks. You need // to call parse_chunk_header() for each chunk, starting with // the start of the body. bool chunked_encoding() const { return m_chunked_encoding; } // removes the chunk headers from the supplied buffer. The buffer // must be the stream received from the http server this parser // instanced parsed. It will use the internal chunk list to determine // where the chunks are in the buffer. It returns the new length of // the buffer int collapse_chunk_headers(char* buffer, int size) const; // returns false if the buffer doesn't contain a complete // chunk header. In this case, call the function again with // a bigger buffer once more bytes have been received. // chunk_size is filled in with the number of bytes in the // chunk that follows. 0 means the response terminated. In // this case there might be additional headers in the parser // object. // header_size is filled in with the number of bytes the header // itself was. Skip this number of bytes to get to the actual // chunk data. // if the function returns false, the chunk size and header // size may still have been modified, but their values are // undefined bool parse_chunk_header(buffer::const_interval buf , boost::int64_t* chunk_size, int* header_size); // reset the whole state and start over void reset(); bool connection_close() const { return m_connection_close; } std::multimap const& headers() const { return m_header; } std::vector > const& chunks() const { return m_chunked_ranges; } private: boost::int64_t m_recv_pos; std::string m_method; std::string m_path; std::string m_protocol; std::string m_server_message; boost::int64_t m_content_length; boost::int64_t m_range_start; boost::int64_t m_range_end; std::multimap m_header; buffer::const_interval m_recv_buffer; // contains offsets of the first and one-past-end of // each chunked range in the response std::vector > m_chunked_ranges; // while reading a chunk, this is the offset where the // current chunk will end (it refers to the first character // in the chunk tail header or the next chunk header) boost::int64_t m_cur_chunk_end; int m_status_code; // the sum of all chunk headers read so far int m_chunk_header_size; int m_partial_chunk_header; // controls some behaviors of the parser int m_flags; int m_body_start_pos; enum { read_status, read_header, read_body, error_state } m_state; // this is true if the server is HTTP/1.0 or // if it sent "connection: close" bool m_connection_close; bool m_chunked_encoding; bool m_finished; }; } #endif // TORRENT_HTTP_PARSER_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/http_seed_connection.hpp000066400000000000000000000106101351156116000264570ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_SEED_CONNECTION_HPP_INCLUDED #define TORRENT_HTTP_SEED_CONNECTION_HPP_INCLUDED #include #include #include #include #include "libtorrent/debug.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/web_connection_base.hpp" #include "libtorrent/disk_buffer_holder.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/http_parser.hpp" namespace libtorrent { class torrent; struct peer_request; namespace detail { struct session_impl; } class TORRENT_EXTRA_EXPORT http_seed_connection : public web_connection_base { 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 http_seed_connection(peer_connection_args const& pack , web_seed_t& web); virtual int type() const TORRENT_OVERRIDE { return peer_connection::http_seed_connection; } // called from the main loop when this connection has any // work to do. virtual void on_receive(error_code const& error , std::size_t bytes_transferred) TORRENT_OVERRIDE; std::string const& url() const TORRENT_OVERRIDE { return m_url; } virtual void get_specific_peer_info(peer_info& p) const TORRENT_OVERRIDE; virtual void disconnect(error_code const& ec, operation_t op, int error = 0) TORRENT_OVERRIDE; virtual void write_request(peer_request const& r) TORRENT_OVERRIDE; 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 downloading_piece_progress() const TORRENT_OVERRIDE; // this is const since it's used as a key in the web seed list in the torrent // if it's changed referencing back into that list will fail const std::string m_url; web_seed_t* m_web; // the number of bytes left to receive of the response we're // currently parsing boost::int64_t m_response_left; // this is the offset inside the current receive // buffer where the next chunk header will be. // this is updated for each chunk header that's // parsed. It does not necessarily point to a valid // offset in the receive buffer, if we haven't received // it yet. This offset never includes the HTTP header boost::int64_t m_chunk_pos; // this is the number of bytes we've already received // from the next chunk header we're waiting for int m_partial_chunk_header; }; } #endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/http_stream.hpp000066400000000000000000000072731351156116000246260ustar00rootroot00000000000000/* Copyright (c) 2007-2018, Arvid Norberg All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TORRENT_HTTP_STREAM_HPP_INCLUDED #define TORRENT_HTTP_STREAM_HPP_INCLUDED #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/proxy_base.hpp" #include "libtorrent/string_util.hpp" namespace libtorrent { class http_stream : public proxy_base { public: explicit http_stream(io_service& io_service) : proxy_base(io_service) , m_no_connect(false) {} void set_no_connect(bool c) { m_no_connect = c; } void set_username(std::string const& user , std::string const& password) { m_user = user; m_password = password; } void set_dst_name(std::string const& host) { m_dst_name = host; } void close(error_code& ec) { m_dst_name.clear(); proxy_base::close(ec); } #ifndef BOOST_NO_EXCEPTIONS void close() { m_dst_name.clear(); proxy_base::close(); } #endif template void async_connect(endpoint_type const& endpoint, Handler const& handler) { m_remote_endpoint = endpoint; // the connect is split up in the following steps: // 1. resolve name of proxy server // 2. connect to proxy server // 3. send HTTP CONNECT method and possibly username+password // 4. read CONNECT response // to avoid unnecessary copying of the handler, // store it in a shared_ptr boost::shared_ptr h(new handler_type(handler)); tcp::resolver::query q(m_hostname, to_string(m_port).elems); m_resolver.async_resolve(q, boost::bind( &http_stream::name_lookup, this, _1, _2, h)); } private: void name_lookup(error_code const& e, tcp::resolver::iterator i , boost::shared_ptr h); void connected(error_code const& e, boost::shared_ptr h); void handshake1(error_code const& e, boost::shared_ptr h); void handshake2(error_code const& e, boost::shared_ptr h); // send and receive buffer std::vector m_buffer; // proxy authentication std::string m_user; std::string m_password; std::string m_dst_name; // this is true if the connection is HTTP based and // want to talk directly to the proxy bool m_no_connect; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/http_tracker_connection.hpp000066400000000000000000000065021351156116000271770ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/lazy_entry.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/tracker_manager.hpp" #include "libtorrent/i2p_stream.hpp" #include "libtorrent/error_code.hpp" namespace libtorrent { struct http_connection; class entry; class http_parser; struct bdecode_node; struct peer_entry; namespace aux { struct session_settings; } class TORRENT_EXTRA_EXPORT http_tracker_connection : public tracker_connection { friend class tracker_manager; public: http_tracker_connection( io_service& ios , tracker_manager& man , tracker_request const& req , boost::weak_ptr c); void start(); void close(); private: boost::shared_ptr shared_from_this() { return boost::static_pointer_cast( tracker_connection::shared_from_this()); } void on_filter(http_connection& c, std::vector& endpoints); void on_connect(http_connection& c); void on_response(error_code const& ec, http_parser const& parser , char const* data, int size); virtual void on_timeout(error_code const&) {} tracker_manager& m_man; boost::shared_ptr m_tracker_connection; address m_tracker_ip; }; TORRENT_EXTRA_EXPORT tracker_response parse_tracker_response( char const* data, int size, error_code& ec , int flags, sha1_hash scrape_ih); TORRENT_EXTRA_EXPORT bool extract_peer_info(bdecode_node const& info , peer_entry& ret, error_code& ec); } #endif // TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/i2p_stream.hpp000066400000000000000000000151341351156116000243340ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_I2P_STREAM_HPP_INCLUDED #define TORRENT_I2P_STREAM_HPP_INCLUDED #include "libtorrent/config.hpp" #if TORRENT_USE_I2P #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/proxy_base.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/string_util.hpp" namespace libtorrent { namespace i2p_error { // error values for the i2p_category error_category. enum i2p_error_code { no_error = 0, parse_failed, cant_reach_peer, i2p_error, invalid_key, invalid_id, timeout, key_not_found, duplicated_id, num_errors }; // hidden TORRENT_EXPORT boost::system::error_code make_error_code(i2p_error_code e); } // returns the error category for I2P errors TORRENT_EXPORT boost::system::error_category& i2p_category(); #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED TORRENT_EXPORT boost::system::error_category& get_i2p_category(); #endif class i2p_stream : public proxy_base { public: explicit i2p_stream(io_service& io_service); ~i2p_stream(); enum command_t { cmd_none, cmd_create_session, cmd_connect, cmd_accept, cmd_name_lookup, cmd_incoming }; void set_command(command_t c) { m_command = c; } void set_session_id(char const* id) { m_id = id; } void set_destination(std::string const& d) { m_dest = d; } std::string const& destination() { return m_dest; } template void async_connect(endpoint_type const&, Handler const& handler) { // since we don't support regular endpoints, just ignore the one // provided and use m_dest. // the connect is split up in the following steps: // 1. resolve name of proxy server // 2. connect to SAM bridge // 4 send command message (CONNECT/ACCEPT) // to avoid unnecessary copying of the handler, // store it in a shaed_ptr boost::shared_ptr h(new handler_type(handler)); tcp::resolver::query q(m_hostname, to_string(m_port).elems); m_resolver.async_resolve(q, boost::bind( &i2p_stream::do_connect, this, _1, _2, h)); } std::string name_lookup() const { return m_name_lookup; } void set_name_lookup(char const* name) { m_name_lookup = name; } void send_name_lookup(boost::shared_ptr h); private: // explicitly disallow assignment, to silence msvc warning i2p_stream& operator=(i2p_stream const&); void do_connect(error_code const& e, tcp::resolver::iterator i , boost::shared_ptr h); void connected(error_code const& e, boost::shared_ptr h); void start_read_line(error_code const& e, boost::shared_ptr h); void read_line(error_code const& e, boost::shared_ptr h); void send_connect(boost::shared_ptr h); void send_accept(boost::shared_ptr h); void send_session_create(boost::shared_ptr h); // send and receive buffer std::vector m_buffer; char const* m_id; command_t m_command; std::string m_dest; std::string m_name_lookup; enum state_t { read_hello_response, read_connect_response, read_accept_response, read_session_create_response, read_name_lookup_response }; state_t m_state; #if TORRENT_USE_ASSERTS int m_magic; #endif }; class i2p_connection { public: i2p_connection(io_service& ios); ~i2p_connection(); aux::proxy_settings proxy() const; bool is_open() const { return m_sam_socket && m_sam_socket->is_open() && m_state != sam_connecting; } void open(std::string const& hostname, int port, i2p_stream::handler_type const& h); void close(error_code&); char const* session_id() const { return m_session_id.c_str(); } std::string const& local_endpoint() const { return m_i2p_local_endpoint; } typedef boost::function name_lookup_handler; void async_name_lookup(char const* name, name_lookup_handler handler); private: // explicitly disallow assignment, to silence msvc warning i2p_connection& operator=(i2p_connection const&); void on_sam_connect(error_code const& ec, i2p_stream::handler_type const& h , boost::shared_ptr); void do_name_lookup(std::string const& name , name_lookup_handler const& h); void on_name_lookup(error_code const& ec , name_lookup_handler handler , boost::shared_ptr); void set_local_endpoint(error_code const& ec, char const* dest , i2p_stream::handler_type const& h); // to talk to i2p SAM bridge boost::shared_ptr m_sam_socket; std::string m_hostname; int m_port; // our i2p endpoint key std::string m_i2p_local_endpoint; std::string m_session_id; std::list > m_name_lookup; enum state_t { sam_connecting, sam_name_lookup, sam_idle }; state_t m_state; io_service& m_io_service; }; } namespace boost { namespace system { template<> struct is_error_code_enum { static const bool value = true; }; } } #endif // TORRENT_USE_I2P #endif libtorrent-rasterbar-1.1.13/include/libtorrent/identify_client.hpp000066400000000000000000000063121351156116000254360ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include "libtorrent/config.hpp" #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #endif #include "libtorrent/peer_id.hpp" #include "libtorrent/fingerprint.hpp" // TODO: hide this declaration when deprecated functions are disabled, and // remove its internal use namespace libtorrent { // these functions don't really need to be public. This mechanism of // advertising client software and version is also out-dated. // This function can can be used to extract a string describing a client // version from its peer-id. It will recognize most clients that have this // kind of identification in the peer-id. TORRENT_DEPRECATED_EXPORT TORRENT_DEPRECATED std::string identify_client(const peer_id& p); #ifndef TORRENT_NO_DEPRECATE #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif // Returns an optional fingerprint if any can be identified from the peer // id. This can be used to automate the identification of clients. It will // not be able to identify peers with non- standard encodings. Only Azureus // style, Shadow's style and Mainline style. TORRENT_DEPRECATED_EXPORT TORRENT_DEPRECATED boost::optional client_fingerprint(peer_id const& p); #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #ifdef __clang__ #pragma clang diagnostic pop #endif #endif // TORRENT_NO_DEPRECATE } #endif // TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/instantiate_connection.hpp000066400000000000000000000040161351156116000270260ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_INSTANTIATE_CONNECTION #define TORRENT_INSTANTIATE_CONNECTION #include "libtorrent/socket_type.hpp" namespace libtorrent { namespace aux { struct proxy_settings; } struct utp_socket_manager; struct socket_type; // instantiate a socket_type (s) according to the specified criteria TORRENT_EXTRA_EXPORT bool instantiate_connection(io_service& ios , aux::proxy_settings const& ps, socket_type& s , void* ssl_context , utp_socket_manager* sm , bool peer_connection , bool tracker_connection); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/invariant_check.hpp000066400000000000000000000032301351156116000254110ustar00rootroot00000000000000// 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 "libtorrent/config.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/config.hpp" #if TORRENT_USE_INVARIANT_CHECKS namespace libtorrent { class invariant_access { public: template static void check_invariant(T const& self) { self.check_invariant(); } }; template void check_invariant(T const& x) { invariant_access::check_invariant(x); } struct invariant_checker {}; template struct invariant_checker_impl : invariant_checker { invariant_checker_impl(T const& self_) : self(self_) { TORRENT_TRY { check_invariant(self); } TORRENT_CATCH_ALL { TORRENT_ASSERT(false); } } invariant_checker_impl(invariant_checker_impl const& rhs) : self(rhs.self) {} ~invariant_checker_impl() { TORRENT_TRY { check_invariant(self); } TORRENT_CATCH_ALL { TORRENT_ASSERT(false); } } T const& self; private: invariant_checker_impl& operator=(invariant_checker_impl const&); }; template invariant_checker_impl make_invariant_checker(T const& x) { return invariant_checker_impl(x); } } #define INVARIANT_CHECK \ invariant_checker const& _invariant_check = make_invariant_checker(*this); \ (void)_invariant_check #else #define INVARIANT_CHECK do {} TORRENT_WHILE_0 #endif #endif // TORRENT_INVARIANT_ACCESS_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/io.hpp000066400000000000000000000117361351156116000227020ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include #include // for copy #include // for memcpy namespace libtorrent { namespace detail { template struct type {}; // reads an integer from a byte stream // in big endian byte order and converts // it to native endianess template inline T read_impl(InIt& start, type) { T ret = 0; for (int i = 0; i < int(sizeof(T)); ++i) { ret <<= 8; ret |= static_cast(*start); ++start; } return ret; } template boost::uint8_t read_impl(InIt& start, type) { return static_cast(*start++); } template boost::int8_t read_impl(InIt& start, type) { return static_cast(*start++); } template inline void write_impl(T val, OutIt& start) { for (int i = int(sizeof(T))-1; i >= 0; --i) { *start = static_cast((val >> (i * 8)) & 0xff); ++start; } } // -- adaptors template boost::int64_t read_int64(InIt& start) { return read_impl(start, type()); } template boost::uint64_t read_uint64(InIt& start) { return read_impl(start, type()); } template boost::uint32_t read_uint32(InIt& start) { return read_impl(start, type()); } template boost::int32_t read_int32(InIt& start) { return read_impl(start, type()); } template boost::int16_t read_int16(InIt& start) { return read_impl(start, type()); } template boost::uint16_t read_uint16(InIt& start) { return read_impl(start, type()); } template boost::int8_t read_int8(InIt& start) { return read_impl(start, type()); } template boost::uint8_t read_uint8(InIt& start) { return read_impl(start, type()); } template void write_uint64(boost::uint64_t val, OutIt& start) { write_impl(val, start); } template void write_int64(boost::int64_t val, OutIt& start) { write_impl(val, start); } template void write_uint32(boost::uint32_t val, OutIt& start) { write_impl(val, start); } template void write_int32(boost::int32_t val, OutIt& start) { write_impl(val, start); } template void write_uint16(boost::uint16_t val, OutIt& start) { write_impl(val, start); } template void write_int16(boost::int16_t val, OutIt& start) { write_impl(val, start); } template void write_uint8(boost::uint8_t val, OutIt& start) { write_impl(val, start); } template void write_int8(boost::int8_t val, OutIt& start) { write_impl(val, start); } inline int write_string(std::string const& str, char*& start) { std::memcpy(reinterpret_cast(start), str.c_str(), str.size()); start += str.size(); return int(str.size()); } template int write_string(std::string const& val, OutIt& out) { for (std::string::const_iterator i = val.begin() , end(val.end()); i != end; ++i) *out++ = *i; return int(val.length()); } } } #endif // TORRENT_IO_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/io_service.hpp000066400000000000000000000044461351156116000244220ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_SERVICE_HPP_INCLUDED #define TORRENT_IO_SERVICE_HPP_INCLUDED #ifdef __OBJC__ #define Protocol Protocol_ #endif #include "libtorrent/aux_/disable_warnings_push.hpp" #include #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN // asio assumes that the windows error codes are defined already #include #endif #include #include #if defined TORRENT_BUILD_SIMULATOR #include "simulator/simulator.hpp" #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifdef __OBJC__ #undef Protocol #endif #include "libtorrent/io_service_fwd.hpp" namespace libtorrent { #if defined TORRENT_BUILD_SIMULATOR typedef sim::asio::io_service io_service; #else typedef boost::asio::io_service io_service; #endif } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/io_service_fwd.hpp000066400000000000000000000047151351156116000252610ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_SERVICE_FWD_HPP_INCLUDED #define TORRENT_IO_SERVICE_FWD_HPP_INCLUDED #ifdef __OBJC__ #define Protocol Protocol_ #endif #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifdef __OBJC__ #undef Protocol #endif #if defined TORRENT_BUILD_SIMULATOR namespace sim { namespace asio { struct io_service; }} #endif namespace boost { namespace asio { #if BOOST_VERSION < 106600 class io_service; #else class io_context; typedef io_context io_service; #endif }} namespace libtorrent { #if defined TORRENT_BUILD_SIMULATOR typedef sim::asio::io_service io_service; #else typedef boost::asio::io_service io_service; #endif #if BOOST_VERSION >= 107000 template io_service& get_io_service(T& o) { return static_cast(o.get_executor().context()); } #else template io_service& get_io_service(T& o) { return o.get_io_service(); } #endif } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/ip_filter.hpp000066400000000000000000000242751351156116000242520ustar00rootroot00000000000000/* Copyright (c) 2005-2018, 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 #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #if BOOST_VERSION >= 106700 #include #else #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/address.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { // hidden inline bool operator<=(address const& lhs , address const& rhs) { return lhs < rhs || lhs == rhs; } template struct ip_range { Addr first; Addr last; boost::uint32_t flags; friend bool operator==(ip_range const& lhs, ip_range const& rhs) { return lhs.first == rhs.first && lhs.last == rhs.last && lhs.flags == rhs.flags; } }; namespace detail { template Addr zero() { Addr zero; std::fill(zero.begin(), zero.end(), 0); return zero; } template<> inline boost::uint16_t zero() { return 0; } template Addr plus_one(Addr const& a) { Addr tmp(a); for (int i = int(tmp.size()) - 1; i >= 0; --i) { if (tmp[i] < (std::numeric_limits::max)()) { tmp[i] += 1; break; } tmp[i] = 0; } return tmp; } inline boost::uint16_t plus_one(boost::uint16_t val) { return val + 1; } template Addr minus_one(Addr const& a) { Addr tmp(a); for (int i = int(tmp.size()) - 1; i >= 0; --i) { if (tmp[i] > 0) { tmp[i] -= 1; break; } tmp[i] = (std::numeric_limits::max)(); } return tmp; } inline boost::uint16_t minus_one(boost::uint16_t val) { return val - 1; } template Addr max_addr() { Addr tmp; std::fill(tmp.begin(), tmp.end() , (std::numeric_limits::max)()); return Addr(tmp); } template<> inline boost::uint16_t max_addr() { return (std::numeric_limits::max)(); } // this is the generic implementation of // a filter for a specific address type. // it works with IPv4 and IPv6 template class filter_impl { public: filter_impl() { // make the entire ip-range non-blocked m_access_list.insert(range(zero(), 0)); } void add_rule(Addr first, Addr last, int flags) { TORRENT_ASSERT(!m_access_list.empty()); TORRENT_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; TORRENT_ASSERT(j != m_access_list.begin()); TORRENT_ASSERT(j != i); boost::uint32_t first_access = i->access; boost::uint32_t last_access = boost::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() && boost::prior(i)->access == flags) { --i; first_access = i->access; } TORRENT_ASSERT(!m_access_list.empty()); TORRENT_ASSERT(i != m_access_list.end()); if (i != j) m_access_list.erase(boost::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(i->start) = first; const_cast(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())) { TORRENT_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); TORRENT_ASSERT(!m_access_list.empty()); } boost::uint32_t access(Addr const& addr) const { TORRENT_ASSERT(!m_access_list.empty()); typename range_t::const_iterator i = m_access_list.upper_bound(addr); if (i != m_access_list.begin()) --i; TORRENT_ASSERT(i != m_access_list.end()); TORRENT_ASSERT(i->start <= addr && (boost::next(i) == m_access_list.end() || addr < boost::next(i)->start)); return i->access; } template std::vector > export_filter() const { std::vector > 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 r; r.first = ExternalAddressType(i->start); r.flags = i->access; ++i; if (i == end) r.last = ExternalAddressType(max_addr()); else r.last = ExternalAddressType(minus_one(i->start)); ret.push_back(r); } return ret; } private: struct range { range(Addr addr, int a = 0): start(addr), access(a) {} 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 boost::uint32_t access; }; typedef std::set range_t; range_t m_access_list; }; } // The ``ip_filter`` class is a set of rules that uniquely categorizes all // ip addresses as allowed or disallowed. The default constructor creates // a single rule that allows all addresses (0.0.0.0 - 255.255.255.255 for // the IPv4 range, and the equivalent range covering all addresses for the // IPv6 range). // // A default constructed ip_filter does not filter any address. struct TORRENT_EXPORT ip_filter { // the flags defined for an IP range enum access_flags { // indicates that IPs in this range should not be connected // to nor accepted as incoming connections blocked = 1 }; // Adds a rule to the filter. ``first`` and ``last`` defines a range of // ip addresses that will be marked with the given flags. The ``flags`` // can currently be 0, which means allowed, or ``ip_filter::blocked``, which // means disallowed. // // precondition: // ``first.is_v4() == last.is_v4() && first.is_v6() == last.is_v6()`` // // postcondition: // ``access(x) == flags`` for every ``x`` in the range [``first``, ``last``] // // This means that in a case of overlapping ranges, the last one applied takes // precedence. void add_rule(address first, address last, boost::uint32_t flags); // Returns the access permissions for the given address (``addr``). The permission // can currently be 0 or ``ip_filter::blocked``. The complexity of this operation // is O(``log`` n), where n is the minimum number of non-overlapping ranges to describe // the current filter. int access(address const& addr) const; #if TORRENT_USE_IPV6 typedef boost::tuple > , std::vector > > filter_tuple_t; #else typedef std::vector > filter_tuple_t; #endif // This function will return the current state of the filter in the minimum number of // ranges possible. They are sorted from ranges in low addresses to high addresses. Each // entry in the returned vector is a range with the access control specified in its // ``flags`` field. // // The return value is a tuple containing two range-lists. One for IPv4 addresses // and one for IPv6 addresses. filter_tuple_t export_filter() const; // void print() const; private: detail::filter_impl m_filter4; #if TORRENT_USE_IPV6 detail::filter_impl m_filter6; #endif }; // the port filter maps non-overlapping port ranges to flags. This // is primarily used to indicate whether a range of ports should // be connected to or not. The default is to have the full port // range (0-65535) set to flag 0. class TORRENT_EXPORT port_filter { public: // the defined flags for a port range enum access_flags { // this flag indicates that destination ports in the // range should not be connected to blocked = 1 }; // set the flags for the specified port range (``first``, ``last``) to // ``flags`` overwriting any existing rule for those ports. The range // is inclusive, i.e. the port ``last`` also has the flag set on it. void add_rule(boost::uint16_t first, boost::uint16_t last, boost::uint32_t flags); // test the specified port (``port``) for whether it is blocked // or not. The returned value is the flags set for this port. // see access_flags. int access(boost::uint16_t port) const; private: detail::filter_impl m_filter; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/ip_voter.hpp000066400000000000000000000106631351156116000241200ustar00rootroot00000000000000/* Copyright (c) 2013-2018, 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_VOTER_HPP_INCLUDED #define TORRENT_IP_VOTER_HPP_INCLUDED #include #include "libtorrent/address.hpp" #include "libtorrent/bloom_filter.hpp" #include "libtorrent/time.hpp" // for time_point namespace libtorrent { // this is an object that keeps the state for a single external IP // based on peoples votes struct TORRENT_EXTRA_EXPORT ip_voter { ip_voter(); // returns true if a different IP is the top vote now // i.e. we changed our idea of what our external IP is bool cast_vote(address const& ip, int source_type, address const& source); address external_address() const { return m_external_address; } private: bool maybe_rotate(); struct external_ip_t { external_ip_t(): sources(0), num_votes(0) {} bool add_vote(sha1_hash const& k, int type); // we want to sort decending bool operator<(external_ip_t const& rhs) const { if (num_votes > rhs.num_votes) return true; if (num_votes < rhs.num_votes) return false; return sources > rhs.sources; } // this is a bloom filter of the IPs that have // reported this address bloom_filter<16> voters; // this is the actual external address address addr; // a bitmask of sources the reporters have come from boost::uint16_t sources; // the total number of votes for this IP boost::uint16_t num_votes; }; // this is a bloom filter of all the IPs that have // been the first to report an external address. Each // IP only gets to add a new item once. bloom_filter<32> m_external_address_voters; std::vector m_external_addresses; address m_external_address; // the total number of unique IPs that have voted int m_total_votes; // this is true from the first time we rotate. Before // we rotate for the first time, we keep updating the // external address as we go, since we don't have any // stable setting to fall back on. Once this is true, // we stop updating it on the fly, and just use the // address from when we rotated. bool m_valid_external; // the last time we rotated this ip_voter. i.e. threw // away all the votes and started from scratch, in case // our IP has changed time_point m_last_rotate; }; // this keeps track of multiple external IPs (for now, just IPv6 and IPv4, but // it could be extended to deal with loopback and local network addresses as well) struct TORRENT_EXTRA_EXPORT external_ip { // returns true if a different IP is the top vote now // i.e. we changed our idea of what our external IP is bool cast_vote(address const& ip, int source_type, address const& source); // the external IP as it would be observed from `ip` address external_address(address const& ip) const; private: // for now, assume one external IPv4 and one external IPv6 address // 0 = IPv4 1 = IPv6 // TODO: 1 instead, have one instance per possible subnet, global IPv4, global IPv6, loopback, 192.168.x.x, 10.x.x.x, etc. ip_voter m_vote_group[2]; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/000077500000000000000000000000001351156116000233215ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/dht_observer.hpp000066400000000000000000000053651351156116000265310ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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 DHT_OBSERVER_HPP #define DHT_OBSERVER_HPP #include "libtorrent/config.hpp" #include "libtorrent/address.hpp" #include "libtorrent/kademlia/msg.hpp" namespace libtorrent { namespace dht { struct TORRENT_EXTRA_EXPORT dht_logger { #ifndef TORRENT_DISABLE_LOGGING enum module_t { tracker, node, routing_table, rpc_manager, traversal }; enum message_direction_t { incoming_message, outgoing_message }; virtual void log(module_t m, char const* fmt, ...) TORRENT_FORMAT(3,4) = 0; virtual void log_packet(message_direction_t dir, char const* pkt, int len , udp::endpoint node) = 0; protected: ~dht_logger() {} #endif }; struct TORRENT_EXTRA_EXPORT dht_observer : dht_logger { virtual void set_external_address(address const& addr , address const& source) = 0; virtual address external_address() = 0; virtual void get_peers(sha1_hash const& ih) = 0; virtual void outgoing_get_peers(sha1_hash const& target , sha1_hash const& sent_target, udp::endpoint const& ep) = 0; virtual void announce(sha1_hash const& ih, address const& addr, int port) = 0; virtual bool on_dht_request(char const* query, int query_len , dht::msg const& request, entry& response) = 0; protected: ~dht_observer() {} }; }} #endif libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/dht_storage.hpp000066400000000000000000000175011351156116000263410ustar00rootroot00000000000000/* Copyright (c) 2012-2018, Arvid Norberg, Alden Torres 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_DHT_STORAGE_HPP #define TORRENT_DHT_STORAGE_HPP #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include #include #include namespace libtorrent { struct dht_settings; class entry; } namespace libtorrent { namespace dht { // This structure hold the relevant counters for the storage struct TORRENT_EXPORT dht_storage_counters { boost::int32_t torrents; boost::int32_t peers; boost::int32_t immutable_data; boost::int32_t mutable_data; }; // The DHT storage interface is a pure virtual class that can // be implemented to customize how the data for the DHT is stored. // // The default storage implementation uses three maps in RAM to save // the peers, mutable and immutable items and it's designed to // provide a fast and fully compliant behavior of the BEPs. // // libtorrent comes with one built-in storage implementation: // ``dht_default_storage`` (private non-accessible class). Its // constructor function is called dht_default_storage_constructor(). // struct TORRENT_EXPORT dht_storage_interface { #ifndef TORRENT_NO_DEPRECATE // This function returns the number of torrents tracked by // the DHT at the moment. It's used to fill session_status. // It's deprecated. // virtual size_t num_torrents() const = 0; // This function returns the sum of all of peers per torrent // tracker byt the DHT at the moment. // It's deprecated. // virtual size_t num_peers() const = 0; #endif // This function retrieve the peers tracked by the DHT // corresponding to the given info_hash. You can specify if // you want only seeds and/or you are scraping the data. // // For future implementers: // If the torrent tracked contains a name, such a name // must be stored as a string in peers["n"] // // If the scrape parameter is true, you should fill these keys:: // // peers["BFpe"] - with the standard bit representation of a // 256 bloom filter containing the downloaders // peers["BFsd"] - with the standard bit representation of a // 256 bloom filter containing the seeders // // If the scrape parameter is false, you should fill the // key peers["values"] with a list containing a subset of // peers tracked by the given info_hash. Such a list should // consider the value of dht_settings::max_peers_reply. // If noseed is true only peers marked as no seed should be included. // // returns true if an entry with the info_hash is found and // the data is returned inside the (entry) out parameter peers. // virtual bool get_peers(sha1_hash const& info_hash , bool noseed, bool scrape , entry& peers) const = 0; // This function is named announce_peer for consistency with the // upper layers, but has nothing to do with networking. Its only // responsibility is store the peer in such a way that it's returned // in the entry with the lookup_peers. // // The ``name`` parameter is the name of the torrent if provided in // the announce_peer DHT message. The length of this value should // have a maximum length in the final storage. The default // implementation truncate the value for a maximum of 50 characters. // virtual void announce_peer(sha1_hash const& info_hash , tcp::endpoint const& endp , std::string const& name, bool seed) = 0; // This function retrieves the immutable item given its target hash. // // For future implementers: // The value should be returned as an entry in the key item["v"]. // // returns true if the item is found and the data is returned // inside the (entry) out parameter item. // virtual bool get_immutable_item(sha1_hash const& target , entry& item) const = 0; // Store the item's data. This layer is only for storage. // The authentication of the item is performed by the upper layer. // // For implementers: // This data can be stored only if the target is not already // present. The implementation should consider the value of // dht_settings::max_dht_items. // virtual void put_immutable_item(sha1_hash const& target , char const* buf, int size , address const& addr) = 0; // This function retrieves the sequence number of a mutable item. // // returns true if the item is found and the data is returned // inside the out parameter seq. // virtual bool get_mutable_item_seq(sha1_hash const& target , boost::int64_t& seq) const = 0; // This function retrieves the mutable stored in the DHT. // // For implementers: // The item sequence should be stored in the key item["seq"]. // if force_fill is true or (0 <= seq and seq < item["seq"]) // the following keys should be filled // item["v"] - with the value no encoded. // item["sig"] - with a string representation of the signature. // item["k"] - with a string representation of the public key. // // returns true if the item is found and the data is returned // inside the (entry) out parameter item. // virtual bool get_mutable_item(sha1_hash const& target , boost::int64_t seq, bool force_fill , entry& item) const = 0; // Store the item's data. This layer is only for storage. // The authentication of the item is performed by the upper layer. // // For implementers: // The sequence number should be checked if the item is already // present. The implementation should consider the value of // dht_settings::max_dht_items. // virtual void put_mutable_item(sha1_hash const& target , char const* buf, int size , char const* sig , boost::int64_t seq , char const* pk , char const* salt, int salt_size , address const& addr) = 0; // This function is called periodically (non-constant frequency). // // For implementers: // Use this functions for expire peers or items or any other // storage cleanup. // virtual void tick() = 0; virtual dht_storage_counters counters() const = 0; virtual ~dht_storage_interface() {} }; typedef boost::function dht_storage_constructor_type; TORRENT_EXPORT dht_storage_interface* dht_default_storage_constructor(sha1_hash const& id , dht_settings const& settings); } } // namespace libtorrent::dht #endif //TORRENT_DHT_STORAGE_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/dht_tracker.hpp000066400000000000000000000131731351156116000263310ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include #include #include #include #include "libtorrent/kademlia/node.hpp" #include "libtorrent/kademlia/node_id.hpp" #include "libtorrent/kademlia/traversal_algorithm.hpp" #include "libtorrent/kademlia/dos_blocker.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/udp_socket.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/deadline_timer.hpp" namespace libtorrent { namespace aux { struct session_impl; } struct counters; #ifndef TORRENT_NO_DEPRECATE struct session_status; #endif } namespace libtorrent { namespace dht { struct dht_tracker; struct dht_tracker TORRENT_FINAL : udp_socket_interface , udp_socket_observer , boost::enable_shared_from_this { dht_tracker(dht_observer* observer, rate_limited_udp_socket& sock , dht_settings const& settings, counters& cnt , dht_storage_constructor_type storage_constructor , entry const& state); virtual ~dht_tracker(); void start(entry const& bootstrap , find_data::nodes_callback const& f); void stop(); // tell the node to recalculate its node id based on the current // understanding of its external address (which may have changed) void update_node_id(); void add_node(udp::endpoint node); void add_router_node(udp::endpoint const& node); entry state() const; enum flags_t { flag_seed = 1, flag_implied_port = 2 }; void get_peers(sha1_hash const& ih , boost::function const&)> f); void announce(sha1_hash const& ih, int listen_port, int flags , boost::function const&)> f); void get_item(sha1_hash const& target , boost::function cb); // key is a 32-byte binary string, the public key to look up. // the salt is optional void get_item(char const* key , boost::function cb , std::string salt = std::string()); // for immutable_item. // the callback function will be called when put operation is done. // the int parameter indicates the success numbers of put operation. void put_item(entry const& data , boost::function cb); // for mutable_item. // the data_cb will be called when we get authoritative mutable_item, // the cb is same as put immutable_item. void put_item(char const* key , boost::function cb , boost::function data_cb, std::string salt = std::string()); // send an arbitrary DHT request directly to a node void direct_request(udp::endpoint ep, entry& e , boost::function f); #ifndef TORRENT_NO_DEPRECATE void dht_status(session_status& s); #endif void dht_status(std::vector& table , std::vector& requests); void update_stats_counters(counters& c) const; // translate bittorrent kademlia message into the generic kademlia message // used by the library virtual bool incoming_packet(error_code const& ec , udp::endpoint const&, char const* buf, int size); private: boost::shared_ptr self() { return shared_from_this(); } void connection_timeout(error_code const& e); void refresh_timeout(error_code const& e); void refresh_key(error_code const& e); // implements udp_socket_interface virtual bool has_quota(); virtual bool send_packet(libtorrent::entry& e, udp::endpoint const& addr , int send_flags); // this is the bdecode_node DHT messages are parsed into. It's a member // in order to avoid having to deallocate and re-allocate it for every // message. bdecode_node m_msg; counters& m_counters; node m_dht; rate_limited_udp_socket& m_sock; dht_logger* m_log; std::vector m_send_buf; dos_blocker m_blocker; deadline_timer m_key_refresh_timer; deadline_timer m_connection_timer; deadline_timer m_refresh_timer; dht_settings const& m_settings; bool m_abort; // used to resolve hostnames for nodes udp::resolver m_host_resolver; }; }} #endif #endif libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/direct_request.hpp000066400000000000000000000053511351156116000270600ustar00rootroot00000000000000/* Copyright (c) 2014, Steven Siloti 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_DIRECT_REQUEST_HPP #define TORRENT_DIRECT_REQUEST_HPP #include #include #include namespace libtorrent { namespace dht { struct direct_traversal : traversal_algorithm { typedef boost::function message_callback; direct_traversal(node& node , node_id target , message_callback cb) : traversal_algorithm(node, target) , m_cb(cb) {} virtual char const* name() const { return "direct_traversal"; } void invoke_cb(msg const& m) { if (!m_cb.empty()) { m_cb(m); m_cb.clear(); done(); } } protected: message_callback m_cb; }; struct direct_observer : observer { direct_observer(boost::intrusive_ptr const& algo , udp::endpoint const& ep, node_id const& id) : observer(algo, ep, id) {} virtual void reply(msg const& m) { flags |= flag_done; static_cast(algorithm())->invoke_cb(m); } virtual void timeout() { if (flags & flag_done) return; flags |= flag_done; bdecode_node e; msg m(e, target_ep()); static_cast(algorithm())->invoke_cb(m); } }; }} // namespace libtorrent::dht #endif //TORRENT_DIRECT_REQUEST_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/dos_blocker.hpp000066400000000000000000000054241351156116000263250ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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_DHT_DOS_BLOCKER #define TORRENT_DHT_DOS_BLOCKER #include "libtorrent/config.hpp" #include "libtorrent/time.hpp" #include "libtorrent/address.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { namespace dht { struct dht_logger; // this is a class that maintains a list of abusive DHT nodes, // blocking their access to our DHT node. struct TORRENT_EXTRA_EXPORT dos_blocker { dos_blocker(); // called every time we receive an incoming packet. Returns // true if we should let the packet through, and false if // it's blocked bool incoming(address addr, time_point now, dht_logger* logger); void set_rate_limit(int l) { TORRENT_ASSERT(l > 0); m_message_rate_limit = l; } void set_block_timer(int t) { TORRENT_ASSERT(t > 0); m_block_timeout = t; } private: // used to ignore abusive dht nodes struct node_ban_entry { node_ban_entry(): count(0) {} address src; time_point limit; int count; }; enum { num_ban_nodes = 20 }; // the max number of packets we can receive per second from a node before // we block it. int m_message_rate_limit; // the number of seconds a node gets blocked for when it exceeds the rate // limit int m_block_timeout; node_ban_entry m_ban_nodes[num_ban_nodes]; }; }} #endif libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/find_data.hpp000066400000000000000000000062511351156116000257470ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { namespace dht { typedef std::vector packet_t; class rpc_manager; class node; // -------- find data ----------- struct find_data : traversal_algorithm { typedef boost::function > const&)> nodes_callback; find_data(node & node, node_id target , nodes_callback const& ncallback); void got_write_token(node_id const& n, std::string const& write_token); virtual void start(); virtual char const* name() const; node_id const target() const { return m_target; } protected: virtual void done(); virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep , node_id const& id); nodes_callback m_nodes_callback; std::map m_write_tokens; bool m_done; }; struct find_data_observer : traversal_observer { find_data_observer( boost::intrusive_ptr const& algorithm , udp::endpoint const& ep, node_id const& id) : traversal_observer(algorithm, ep, id) {} virtual void reply(msg const&); }; } } // namespace libtorrent::dht #endif // FIND_DATA_050323_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/get_item.hpp000066400000000000000000000054521351156116000256350ustar00rootroot00000000000000/* Copyright (c) 2013, Steven Siloti 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 LIBTORRENT_GET_ITEM_HPP #define LIBTORRENT_GET_ITEM_HPP #include #include #include namespace libtorrent { namespace dht { class get_item : public find_data { public: typedef boost::function data_callback; void got_data(bdecode_node const& v, char const* pk, boost::uint64_t seq, char const* sig); // for immutable itms get_item(node& dht_node , node_id target , data_callback const& dcallback , nodes_callback const& ncallback); // for mutable items get_item(node& dht_node , char const* pk , std::string const& salt , data_callback const& dcallback , nodes_callback const& ncallback); virtual char const* name() const; protected: virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id); virtual bool invoke(observer_ptr o); virtual void done(); data_callback m_data_callback; item m_data; bool m_immutable; }; class get_item_observer : public find_data_observer { public: get_item_observer( boost::intrusive_ptr const& algorithm , udp::endpoint const& ep, node_id const& id) : find_data_observer(algorithm, ep, id) {} virtual void reply(msg const&); }; } } // namespace libtorrent::dht #endif // LIBTORRENT_GET_ITEM_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/get_peers.hpp000066400000000000000000000065211351156116000260130ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 LIBTORRENT_GET_PEERS_HPP #define LIBTORRENT_GET_PEERS_HPP #include namespace libtorrent { namespace dht { struct get_peers : find_data { typedef boost::function const&)> data_callback; void got_peers(std::vector const& peers); get_peers(node& dht_node, node_id target , data_callback const& dcallback , nodes_callback const& ncallback , bool noseeds); virtual char const* name() const; protected: virtual bool invoke(observer_ptr o); virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id); data_callback m_data_callback; bool m_noseeds; }; struct obfuscated_get_peers : get_peers { typedef get_peers::nodes_callback done_callback; obfuscated_get_peers(node& dht_node, node_id target , data_callback const& dcallback , nodes_callback const& ncallback , bool noseeds); virtual char const* name() const; protected: virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id); virtual bool invoke(observer_ptr o); virtual void done(); private: // when set to false, we no longer obfuscate // the target hash, and send regular get_peers bool m_obfuscated; }; struct get_peers_observer : find_data_observer { get_peers_observer( boost::intrusive_ptr const& algorithm , udp::endpoint const& ep, node_id const& id) : find_data_observer(algorithm, ep, id) {} virtual void reply(msg const&); }; struct obfuscated_get_peers_observer : traversal_observer { obfuscated_get_peers_observer( boost::intrusive_ptr const& algorithm , udp::endpoint const& ep, node_id const& id) : traversal_observer(algorithm, ep, id) {} virtual void reply(msg const&); }; } } // namespace libtorrent::dht #endif // LIBTORRENT_GET_PEERS_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/item.hpp000066400000000000000000000111201351156116000247630ustar00rootroot00000000000000/* Copyright (c) 2013, Steven Siloti 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 LIBTORRENT_ITEM_HPP #define LIBTORRENT_ITEM_HPP #include #include #include #include #include #include namespace libtorrent { namespace dht { // calculate the target hash for an immutable item. sha1_hash TORRENT_EXTRA_EXPORT item_target_id( std::pair v); // calculate the target hash for a mutable item. sha1_hash TORRENT_EXTRA_EXPORT item_target_id(std::pair salt , char const* pk); bool TORRENT_EXTRA_EXPORT verify_mutable_item( std::pair v , std::pair salt , boost::uint64_t seq , char const* pk , char const* sig); // TODO: since this is a public function, it should probably be moved // out of this header and into one with other public functions. // given a byte range ``v`` and an optional byte range ``salt``, a // sequence number, public key ``pk`` (must be 32 bytes) and a secret key // ``sk`` (must be 64 bytes), this function produces a signature which // is written into a 64 byte buffer pointed to by ``sig``. The caller // is responsible for allocating the destination buffer that's passed in // as the ``sig`` argument. Typically it would be allocated on the stack. void TORRENT_EXPORT sign_mutable_item( std::pair v , std::pair salt , boost::uint64_t seq , char const* pk , char const* sk , char* sig); enum { item_pk_len = 32, item_sk_len = 64, item_sig_len = 64 }; class TORRENT_EXTRA_EXPORT item { public: item() : m_seq(0), m_mutable(false) {} item(char const* pk, std::string const& salt); item(entry const& v) { assign(v); } item(entry const& v , std::pair salt , boost::uint64_t seq, char const* pk, char const* sk); item(bdecode_node const& v) { assign(v); } void assign(entry const& v) { assign(v, std::pair(static_cast(NULL) , 0), 0, NULL, NULL); } void assign(entry const& v, std::pair salt , boost::uint64_t seq, char const* pk, char const* sk); void assign(bdecode_node const& v) { assign(v, std::pair(static_cast(NULL) , 0), 0, NULL, NULL); } bool assign(bdecode_node const& v, std::pair salt , boost::uint64_t seq, char const* pk, char const* sig); void assign(entry const& v, std::string salt, boost::uint64_t seq , char const* pk, char const* sig); void clear() { m_value = entry(); } bool empty() const { return m_value.type() == entry::undefined_t; } bool is_mutable() const { return m_mutable; } entry const& value() const { return m_value; } boost::array const& pk() const { return m_pk; } boost::array const& sig() const { return m_sig; } boost::uint64_t seq() const { return m_seq; } std::string const& salt() const { return m_salt; } private: entry m_value; std::string m_salt; boost::array m_pk; boost::array m_sig; boost::uint64_t m_seq; bool m_mutable; }; } } // namespace libtorrent::dht #endif // LIBTORRENT_ITEM_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/msg.hpp000066400000000000000000000066661351156116000246360ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_KADEMLIA_MSG_HPP #define TORRENT_KADEMLIA_MSG_HPP #include #include "libtorrent/socket.hpp" #include "libtorrent/kademlia/node_id.hpp" namespace libtorrent { struct bdecode_node; class entry; namespace dht { typedef std::vector packet_t; typedef std::vector nodes_t; typedef std::vector peers_t; struct msg { msg(bdecode_node const& m, udp::endpoint const& ep): message(m), addr(ep) {} // the message bdecode_node const& message; // the address of the process sending or receiving // the message. udp::endpoint addr; private: // explicitly disallow assignment, to silence msvc warning msg& operator=(msg const&); }; struct key_desc_t { char const* name; int type; int size; int flags; enum { // this argument is optional, parsing will not // fail if it's not present optional = 1, // for dictionaries, the following entries refer // to child nodes to this node, up until and including // the next item that has the last_child flag set. // these flags are nestable parse_children = 2, // this is the last item in a child dictionary last_child = 4, // the size argument refers to that the size // has to be divisible by the number, instead // of having that exact size size_divisible = 8 }; }; // generate an error response message void incoming_error(entry& e, char const* msg, int error_code = 203); // given a redundant name to avoid clashing with libtorrent::detail namespace dht_detail { TORRENT_EXPORT bool verify_message(bdecode_node const& msg, key_desc_t const desc[] , bdecode_node ret[], int size, char* error, int error_size); } // verifies that a message has all the required // entries and returns them in ret template bool verify_message(bdecode_node const& msg, key_desc_t const (&desc)[Size] , bdecode_node (&ret)[Size], char* error, int error_size) { return dht_detail::verify_message(msg, desc, ret, Size, error, error_size); } } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/node.hpp000066400000000000000000000166341351156116000247710ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include #include #include #include #include #include #include #include #include #include #include #include // for udp::endpoint #include #include #include #include #include #include #include "libtorrent/socket.hpp" namespace libtorrent { class alert_manager; struct alert_dispatcher; class alert; struct counters; struct dht_routing_bucket; } namespace libtorrent { namespace dht { struct traversal_algorithm; struct dht_observer; void TORRENT_EXTRA_EXPORT write_nodes_entry(entry& r, nodes_t const& nodes); struct null_type {}; class announce_observer : public observer { public: announce_observer(boost::intrusive_ptr const& algo , udp::endpoint const& ep, node_id const& id) : observer(algo, ep, id) {} void reply(msg const&) { flags |= flag_done; } }; struct udp_socket_interface { virtual bool has_quota() = 0; virtual bool send_packet(entry& e, udp::endpoint const& addr, int flags) = 0; protected: ~udp_socket_interface() {} }; class TORRENT_EXTRA_EXPORT node : boost::noncopyable { public: node(udp_socket_interface* sock , libtorrent::dht_settings const& settings, node_id nid , dht_observer* observer, counters& cnt , dht_storage_constructor_type storage_constructor = dht_default_storage_constructor); ~node(); void update_node_id(); void tick(); void bootstrap(std::vector const& nodes , find_data::nodes_callback const& f); void add_router_node(udp::endpoint router); void unreachable(udp::endpoint const& ep); void incoming(msg const& m); #ifndef TORRENT_NO_DEPRECATE int num_torrents() const { return m_storage->num_torrents(); } int num_peers() const { return m_storage->num_peers(); } #endif int bucket_size(int bucket); node_id const& nid() const { return m_id; } boost::tuple size() const { return m_table.size(); } boost::int64_t num_global_nodes() const { return m_table.num_global_nodes(); } #ifndef TORRENT_NO_DEPRECATE int data_size() const { return int(m_storage->num_torrents()); } #endif #if defined TORRENT_DEBUG void print_state(std::ostream& os) const { m_table.print_state(os); } #endif enum flags_t { flag_seed = 1, flag_implied_port = 2 }; void get_peers(sha1_hash const& info_hash , boost::function const&)> dcallback , boost::function > const&)> ncallback , bool noseeds); void announce(sha1_hash const& info_hash, int listen_port, int flags , boost::function const&)> f); void direct_request(udp::endpoint ep, entry& e , boost::function f); void get_item(sha1_hash const& target, boost::function f); void get_item(char const* pk, std::string const& salt, boost::function f); void put_item(sha1_hash const& target, entry const& data, boost::function f); void put_item(char const* pk, std::string const& salt , boost::function f , boost::function data_cb); bool verify_token(std::string const& token, char const* info_hash , udp::endpoint const& addr) const; std::string generate_token(udp::endpoint const& addr, char const* info_hash); // the returned time is the delay until connection_timeout() // should be called again the next time time_duration connection_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); int branch_factor() const { return m_settings.search_branching; } void add_traversal_algorithm(traversal_algorithm* a) { mutex_t::scoped_lock l(m_mutex); m_running_requests.insert(a); } void remove_traversal_algorithm(traversal_algorithm* a) { mutex_t::scoped_lock l(m_mutex); m_running_requests.erase(a); } void status(std::vector& table , std::vector& requests); void update_stats_counters(counters& c) const; #ifndef TORRENT_NO_DEPRECATE void status(libtorrent::session_status& s); #endif libtorrent::dht_settings const& settings() const { return m_settings; } counters& stats_counters() const { return m_counters; } dht_observer* observer() const { return m_observer; } private: void send_single_refresh(udp::endpoint const& ep, int bucket , node_id const& id = node_id()); void lookup_peers(sha1_hash const& info_hash, entry& reply , bool noseed, bool scrape) const; bool lookup_torrents(sha1_hash const& target, entry& reply , char* tags) const; libtorrent::dht_settings const& m_settings; typedef libtorrent::mutex mutex_t; mutex_t m_mutex; // this list must be destructed after the rpc manager // since it might have references to it std::set m_running_requests; void incoming_request(msg const& h, entry& e); node_id m_id; public: routing_table m_table; rpc_manager m_rpc; private: dht_observer* m_observer; time_point m_last_tracker_tick; // the last time we issued a bootstrap or a refresh on our own ID, to expand // the routing table buckets close to us. time_point m_last_self_refresh; // secret random numbers used to create write tokens int m_secret[2]; udp_socket_interface* m_sock; counters& m_counters; boost::scoped_ptr m_storage; }; } } // namespace libtorrent::dht #endif // NODE_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/node_entry.hpp000066400000000000000000000057011351156116000262030ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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" #include "libtorrent/address.hpp" #include "libtorrent/union_endpoint.hpp" #include "libtorrent/time.hpp" // for time_point namespace libtorrent { namespace dht { struct TORRENT_EXTRA_EXPORT node_entry { node_entry(node_id const& id_, udp::endpoint ep, int roundtriptime = 0xffff , bool pinged = false); node_entry(udp::endpoint ep); node_entry(); void update_rtt(int new_rtt); bool pinged() const { return timeout_count != 0xff; } void set_pinged() { if (timeout_count == 0xff) timeout_count = 0; } void timed_out() { if (pinged() && timeout_count < 0xfe) ++timeout_count; } int fail_count() const { return pinged() ? timeout_count : 0; } void reset_fail_count() { if (pinged()) timeout_count = 0; } udp::endpoint ep() const { return udp::endpoint(address_v4(a), p); } bool confirmed() const { return timeout_count == 0; } address addr() const { return address_v4(a); } int port() const { return p; } #ifndef TORRENT_DISABLE_LOGGING time_point first_seen; #endif // the time we last received a response for a request to this peer time_point last_queried; node_id id; address_v4::bytes_type a; boost::uint16_t p; // the average RTT of this node boost::uint16_t rtt; // the number of times this node has failed to // respond in a row boost::uint8_t timeout_count; }; } } // namespace libtorrent::dht #endif libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/node_id.hpp000066400000000000000000000063361351156116000254430ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/address.hpp" namespace libtorrent { namespace dht { struct node_entry; typedef libtorrent::sha1_hash node_id; // returns the distance between the two nodes // using the kademlia XOR-metric node_id TORRENT_EXTRA_EXPORT distance(node_id const& n1, node_id const& n2); // returns true if: distance(n1, ref) < distance(n2, ref) bool TORRENT_EXTRA_EXPORT 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) // useful for finding out which bucket a node belongs to // the value that's returned is the number of trailing bits // after the shared bit prefix of ``n1`` and ``n2``. // if the first bits are different, that's 160. int TORRENT_EXTRA_EXPORT distance_exp(node_id const& n1, node_id const& n2); node_id TORRENT_EXTRA_EXPORT generate_id(address const& external_ip); node_id TORRENT_EXTRA_EXPORT generate_random_id(); void TORRENT_EXTRA_EXPORT make_id_secret(node_id& in); node_id TORRENT_EXTRA_EXPORT generate_secret_id(); bool TORRENT_EXTRA_EXPORT verify_secret_id(node_id const& nid); node_id TORRENT_EXTRA_EXPORT generate_id_impl(address const& ip_, boost::uint32_t r); bool TORRENT_EXTRA_EXPORT verify_id(node_id const& nid, address const& source_ip); bool TORRENT_EXTRA_EXPORT matching_prefix(node_entry const& n, int mask, int prefix, int offset); node_id TORRENT_EXTRA_EXPORT generate_prefix_mask(int bits); } } // namespace libtorrent::dht #endif // NODE_ID_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/observer.hpp000066400000000000000000000113551351156116000256660ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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 OBSERVER_HPP #define OBSERVER_HPP #include #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { namespace dht { struct dht_observer; struct observer; struct msg; struct traversal_algorithm; // defined in rpc_manager.cpp TORRENT_EXTRA_EXPORT void intrusive_ptr_add_ref(observer const*); TORRENT_EXTRA_EXPORT void intrusive_ptr_release(observer const*); struct TORRENT_EXTRA_EXPORT observer : boost::noncopyable { friend TORRENT_EXTRA_EXPORT void intrusive_ptr_add_ref(observer const*); friend TORRENT_EXTRA_EXPORT void intrusive_ptr_release(observer const*); observer(boost::intrusive_ptr const& a , udp::endpoint const& ep, node_id const& id) : m_sent() , m_algorithm(a) , m_id(id) , m_refs(0) , m_port(0) , m_transaction_id() , flags(0) { TORRENT_ASSERT(a); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS m_in_constructor = true; m_was_sent = false; m_was_abandoned = false; m_in_use = true; #endif set_target(ep); } // defined in rpc_manager.cpp virtual ~observer(); // this is called when a reply is received virtual void reply(msg const& m) = 0; // this is called if no response has been received after // a few seconds, before the request has timed out void short_timeout(); bool has_short_timeout() const { return (flags & flag_short_timeout) != 0; } // this is called when no reply has been received within // some timeout, or a reply with incorrect format. virtual void timeout(); // if this is called the destructor should // not invoke any new messages, and should // only clean up. It means the rpc-manager // is being destructed void abort(); dht_observer* get_observer() const; traversal_algorithm* algorithm() const { return m_algorithm.get(); } time_point sent() const { return m_sent; } void set_target(udp::endpoint const& ep); address target_addr() const; udp::endpoint target_ep() const; void set_id(node_id const& id); node_id const& id() const { return m_id; } void set_transaction_id(boost::uint16_t tid) { m_transaction_id = tid; } boost::uint16_t transaction_id() const { return m_transaction_id; } enum { flag_queried = 1, flag_initial = 2, flag_no_id = 4, flag_short_timeout = 8, flag_failed = 16, flag_ipv6_address = 32, flag_alive = 64, flag_done = 128 }; protected: void done(); private: time_point m_sent; const boost::intrusive_ptr m_algorithm; node_id m_id; TORRENT_UNION addr_t { #if TORRENT_USE_IPV6 address_v6::bytes_type v6; #endif address_v4::bytes_type v4; } m_addr; // reference counter for intrusive_ptr mutable boost::uint16_t m_refs; boost::uint16_t m_port; // the transaction ID for this call boost::uint16_t m_transaction_id; public: unsigned char flags; #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS bool m_in_constructor:1; bool m_was_sent:1; bool m_was_abandoned:1; bool m_in_use:1; #endif }; typedef boost::intrusive_ptr observer_ptr; } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/put_data.hpp000066400000000000000000000057061351156116000256430ustar00rootroot00000000000000/* Copyright (c) 2006-2018, Arvid Norberg, Thomas Yuan 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_PUT_DATA_HPP #define TORRENT_PUT_DATA_HPP #include #include #include #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { namespace dht { struct msg; class node; struct put_data: traversal_algorithm { typedef boost::function put_callback; put_data(node& node, put_callback const& callback); virtual char const* name() const TORRENT_OVERRIDE; virtual void start() TORRENT_OVERRIDE; void set_data(item const& data) { m_data = data; } void set_targets(std::vector > const& targets); protected: virtual void done() TORRENT_OVERRIDE; virtual bool invoke(observer_ptr o) TORRENT_OVERRIDE; put_callback m_put_callback; item m_data; bool m_done; }; struct put_data_observer : traversal_observer { put_data_observer( boost::intrusive_ptr const& algorithm , udp::endpoint const& ep, node_id const& id, std::string const& token) : traversal_observer(algorithm, ep, id) , m_token(token) { } virtual void reply(msg const&) { done(); } std::string m_token; }; } } // namespace libtorrent::dht #endif // TORRENT_PUT_DATA_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/refresh.hpp000066400000000000000000000043231351156116000254720ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include namespace libtorrent { namespace dht { class routing_table; class rpc_manager; class bootstrap : public get_peers { public: typedef get_peers::nodes_callback done_callback; bootstrap(node& dht_node, node_id target , done_callback const& callback); virtual char const* name() const; observer_ptr new_observer(void* ptr, udp::endpoint const& ep , node_id const& id); protected: virtual bool invoke(observer_ptr o); virtual void done(); }; } } // namespace libtorrent::dht #endif // REFRESH_050324_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/routing_table.hpp000066400000000000000000000177061351156116000267030ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include #include #include #include #include namespace libtorrent { #ifndef TORRENT_NO_DEPRECATE struct session_status; #endif struct dht_routing_bucket; } namespace libtorrent { namespace dht { struct dht_logger; typedef std::vector bucket_t; struct routing_table_node { bucket_t replacements; bucket_t live_nodes; }; // differences in the implementation from the description in // the paper: // // * 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). namespace impl { template inline void forwarder(void* userdata, node_entry const& node) { F* f = reinterpret_cast(userdata); (*f)(node); } } class TORRENT_EXTRA_EXPORT routing_table : boost::noncopyable { public: // TODO: 3 to improve memory locality and scanning performance, turn the // routing table into a single vector with boundaries for the nodes instead. // Perhaps replacement nodes should be in a separate vector. typedef std::vector table_t; routing_table(node_id const& id, int bucket_size , dht_settings const& settings , dht_logger* log); #ifndef TORRENT_NO_DEPRECATE void status(session_status& s) const; #endif void status(std::vector& s) const; void node_failed(node_id const& id, udp::endpoint const& ep); // 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::const_iterator router_iterator; router_iterator router_begin() const { return m_router_nodes.begin(); } router_iterator router_end() const { return m_router_nodes.end(); } enum add_node_status_t { failed_to_add = 0, node_added, need_bucket_split }; add_node_status_t add_node_impl(node_entry e); bool add_node(node_entry e); // 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 ep, int rtt); // this may add a node to the routing table and mark it as // not pinged. If the bucket the node falls into is full, // the node will be ignored. void heard_about(node_id const& id, udp::endpoint const& ep); // change our node ID. This can be expensive since nodes must be moved around // and potentially dropped void update_node_id(node_id id); node_entry const* next_refresh(); enum { // nodes that have not been pinged are considered failed by this flag include_failed = 1 }; // 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& l , int options, int count = 0); void remove_node(node_entry* n , table_t::iterator bucket) ; int bucket_size(int bucket) const { int num_buckets = m_buckets.size(); if (num_buckets == 0) return 0; if (bucket >= num_buckets) bucket = num_buckets - 1; table_t::const_iterator i = m_buckets.begin(); std::advance(i, bucket); return int(i->live_nodes.size()); } template void for_each_node(F f) { for_each_node(&impl::forwarder, &impl::forwarder, reinterpret_cast(&f)); } void for_each_node(void (*)(void*, node_entry const&) , void (*)(void*, node_entry const&), void* userdata) const; int bucket_size() const { return m_bucket_size; } // returns the number of nodes in the main buckets, number of nodes in the // replacement buckets and the number of nodes in the main buckets that have // been pinged and confirmed up boost::tuple size() const; boost::int64_t num_global_nodes() const; // the number of bits down we have full buckets // i.e. essentially the number of full buckets // we have int depth() const; int num_active_buckets() const { return m_buckets.size(); } #if defined TORRENT_DEBUG // 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; #endif int bucket_limit(int bucket) const; #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif bool is_full(int bucket) const; private: #ifndef TORRENT_DISABLE_LOGGING dht_logger* m_log; #endif table_t::iterator find_bucket(node_id const& id); void split_bucket(); // return a pointer the node_entry with the given endpoint // or 0 if we don't have such a node. Both the address and the // port has to match node_entry* find_node(udp::endpoint const& ep , routing_table::table_t::iterator* bucket); dht_settings const& m_settings; // (k-bucket, replacement cache) pairs // the first entry is the bucket the furthest // away from our own ID. Each time the bucket // closest to us (m_buckets.back()) has more than // bucket size nodes in it, another bucket is // added to the end and it's split up between them table_t m_buckets; node_id m_id; // our own node id // the last seen depth (i.e. levels in the routing table) // it's mutable because it's updated by depth(), which is const mutable int m_depth; // the last time we refreshed our own bucket // refreshed every 15 minutes mutable time_point m_last_self_refresh; // 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 m_router_nodes; // these are all the IPs that are in the routing // table. It's used to only allow a single entry // per IP in the whole table. Currently only for // IPv4 boost::unordered_multiset m_ips; // constant called k in paper int m_bucket_size; }; } } // namespace libtorrent::dht #endif // ROUTING_TABLE_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/rpc_manager.hpp000066400000000000000000000076771351156116000263310ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #if TORRENT_HAS_BOOST_UNORDERED #include #else #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include #include #include #include #include "libtorrent/time.hpp" namespace libtorrent { namespace aux { struct session_impl; } } namespace libtorrent { struct dht_settings; } namespace libtorrent { namespace dht { struct dht_logger; struct udp_socket_interface; struct TORRENT_EXTRA_EXPORT null_observer : public observer { null_observer(boost::intrusive_ptr const& a , udp::endpoint const& ep, node_id const& id): observer(a, ep, id) {} virtual void reply(msg const&) { flags |= flag_done; } }; class routing_table; class TORRENT_EXTRA_EXPORT rpc_manager { public: rpc_manager(node_id const& our_id , dht_settings const& settings , routing_table& table , udp_socket_interface* sock , dht_logger* log); ~rpc_manager(); void unreachable(udp::endpoint const& ep); // returns true if the node needs a refresh // if so, id is assigned the node id to refresh bool incoming(msg const&, node_id* id); time_duration tick(); bool invoke(entry& e, udp::endpoint target , observer_ptr o); void add_our_id(entry& e); #if TORRENT_USE_ASSERTS size_t allocation_size() const; #endif #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif void* allocate_observer(); void free_observer(void* ptr); int num_allocated_observers() const { return m_allocated_observers; } void update_node_id(node_id const& id) { m_our_id = id; } private: boost::uint32_t calc_connection_id(udp::endpoint addr); mutable boost::pool<> m_pool_allocator; #if TORRENT_HAS_BOOST_UNORDERED typedef boost::unordered_multimap transactions_t; #else typedef std::multimap transactions_t; #endif transactions_t m_transactions; udp_socket_interface* m_sock; dht_logger* m_log; dht_settings const& m_settings; routing_table& m_table; time_point m_timer; node_id m_our_id; boost::uint32_t m_allocated_observers:31; boost::uint32_t m_destructing:1; }; } } // namespace libtorrent::dht #endif libtorrent-rasterbar-1.1.13/include/libtorrent/kademlia/traversal_algorithm.hpp000066400000000000000000000104371351156116000301100ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct dht_lookup; } namespace libtorrent { namespace dht { class rpc_manager; class node; // this class may not be instantiated as a stack object struct TORRENT_EXTRA_EXPORT traversal_algorithm : boost::noncopyable { void traverse(node_id const& id, udp::endpoint addr); void finished(observer_ptr o); enum flags_t { prevent_request = 1, short_timeout = 2 }; void failed(observer_ptr o, int flags = 0); virtual ~traversal_algorithm(); void status(dht_lookup& l); void* allocate_observer(); void free_observer(void* ptr); virtual char const* name() const; virtual void start(); node_id const& target() const { return m_target; } void resort_results(); void add_entry(node_id const& id, udp::endpoint addr, unsigned char flags); traversal_algorithm(node & node, node_id target); int invoke_count() const { return m_invoke_count; } int branch_factor() const { return m_branch_factor; } node& get_node() const { return m_node; } protected: // returns true if we're done bool add_requests(); void add_router_entries(); void init(); virtual void done(); // should construct an algorithm dependent // observer in ptr. virtual observer_ptr new_observer(void* ptr , udp::endpoint const& ep, node_id const& id); virtual bool invoke(observer_ptr) { return false; } friend void intrusive_ptr_add_ref(traversal_algorithm* p) { TORRENT_ASSERT(p->m_ref_count < 0xffff); p->m_ref_count++; } friend void intrusive_ptr_release(traversal_algorithm* p) { if (--p->m_ref_count == 0) delete p; } node & m_node; std::vector m_results; node_id const m_target; boost::uint16_t m_ref_count; boost::int16_t m_invoke_count; boost::int16_t m_branch_factor; boost::int16_t m_responses; boost::int16_t m_timeouts; // the IP addresses of the nodes in m_results std::set m_peer4_prefixes; // no IPv6 support yet anyway // std::set m_peer6_prefixes; }; struct traversal_observer : observer { traversal_observer( boost::intrusive_ptr const& algorithm , udp::endpoint const& ep, node_id const& id) : observer(algorithm, ep, id) {} // parses out "nodes" and keeps traversing virtual void reply(msg const&); }; } } // namespace libtorrent::dht #endif // TRAVERSAL_ALGORITHM_050324_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/lazy_entry.hpp000066400000000000000000000326751351156116000245000ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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_LAZY_ENTRY_HPP_INCLUDED #define TORRENT_LAZY_ENTRY_HPP_INCLUDED #ifndef TORRENT_NO_DEPRECATE #include #include #include #include #include #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/bdecode.hpp" // for error codes namespace libtorrent { struct lazy_entry; // This function decodes bencoded_ data. // // .. _bencoded: http://wiki.theory.org/index.php/BitTorrentSpecification // // The lazy bdecoder and lazy_entry has been deprecated in favour of // bdecode_node and its corresponding bdecode() function. // // *lazy* refers to the fact that it doesn't copy any actual data out of the // bencoded buffer. It builds a tree of ``lazy_entry`` which has pointers into // the bencoded buffer. This makes it very fast and efficient. On top of that, // it is not recursive, which saves a lot of stack space when parsing deeply // nested trees. However, in order to protect against potential attacks, the // ``depth_limit`` and ``item_limit`` control how many levels deep the tree is // allowed to get. With recursive parser, a few thousand levels would be enough // to exhaust the threads stack and terminate the process. The ``item_limit`` // protects against very large structures, not necessarily deep. Each bencoded // item in the structure causes the parser to allocate some amount of memory, // this memory is constant regardless of how much data actually is stored in // the item. One potential attack is to create a bencoded list of hundreds of // thousands empty strings, which would cause the parser to allocate a significant // amount of memory, perhaps more than is available on the machine, and effectively // provide a denial of service. The default item limit is set as a reasonable // upper limit for desktop computers. Very few torrents have more items in them. // The limit corresponds to about 25 MB, which might be a bit much for embedded // systems. // // ``start`` and ``end`` defines the bencoded buffer to be decoded. ``ret`` is // the ``lazy_entry`` which is filled in with the whole decoded tree. ``ec`` // is a reference to an ``error_code`` which is set to describe the error encountered // in case the function fails. ``error_pos`` is an optional pointer to an int, // which will be set to the byte offset into the buffer where an error occurred, // in case the function fails. TORRENT_DEPRECATED_EXPORT int lazy_bdecode(char const* start, char const* end , lazy_entry& ret, error_code& ec, int* error_pos = 0 , int depth_limit = 1000, int item_limit = 1000000); // for backwards compatibility, does not report error code // deprecated in 0.16 TORRENT_DEPRECATED_EXPORT int lazy_bdecode(char const* start, char const* end , lazy_entry& ret, int depth_limit = 1000, int item_limit = 1000000); // this is a string that is not NULL-terminated. Instead it // comes with a length, specified in bytes. This is particularly // useful when parsing bencoded structures, because strings are // not NULL-terminated internally, and requiring NULL termination // would require copying the string. // // see lazy_entry::string_pstr(). struct TORRENT_DEPRECATED_EXPORT pascal_string { // construct a string pointing to the characters at ``p`` // of length ``l`` characters. No NULL termination is required. pascal_string(char const* p, int l): len(l), ptr(p) {} // the number of characters in the string. int len; // the pointer to the first character in the string. This is // not NULL terminated, but instead consult the ``len`` field // to know how many characters follow. char const* ptr; // lexicographical comparison of strings. Order is consisten // with memcmp. bool operator<(pascal_string const& rhs) const { return std::memcmp(ptr, rhs.ptr, (std::min)(len, rhs.len)) < 0 || len < rhs.len; } }; struct lazy_dict_entry; // this object represent a node in a bencoded structure. It is a variant // type whose concrete type is one of: // // 1. dictionary (maps strings -> lazy_entry) // 2. list (sequence of lazy_entry, i.e. heterogenous) // 3. integer // 4. string // // There is also a ``none`` type, which is used for uninitialized // lazy_entries. struct TORRENT_DEPRECATED_EXPORT lazy_entry { // The different types a lazy_entry can have enum entry_type_t { none_t, dict_t, list_t, string_t, int_t }; // internal lazy_entry() : m_begin(0), m_len(0), m_size(0), m_type(none_t) { m_data.start = NULL; } // tells you which specific type this lazy entry has. // See entry_type_t. The type determines which subset of // member functions are valid to use. entry_type_t type() const { return entry_type_t(m_type); } // start points to the first decimal digit // length is the number of digits void construct_int(char const* start, int length) { TORRENT_ASSERT(m_type == none_t); m_type = int_t; m_data.start = start; m_size = length; m_begin = start - 1; // include 'i' m_len = length + 2; // include 'e' } // requires the type to be an integer. return the integer value boost::int64_t int_value() const; // internal void construct_string(char const* start, int length); // the string is not null-terminated! // use string_length() to determine how many bytes // are part of the string. char const* string_ptr() const { TORRENT_ASSERT(m_type == string_t); return m_data.start; } // this will return a null terminated string // it will write to the source buffer! char const* string_cstr() const { TORRENT_ASSERT(m_type == string_t); const_cast(m_data.start)[m_size] = 0; return m_data.start; } // if this is a string, returns a pascal_string // representing the string value. pascal_string string_pstr() const { TORRENT_ASSERT(m_type == string_t); return pascal_string(m_data.start, m_size); } // if this is a string, returns the string as a std::string. // (which requires a copy) std::string string_value() const { TORRENT_ASSERT(m_type == string_t); return std::string(m_data.start, m_size); } // if the lazy_entry is a string, returns the // length of the string, in bytes. int string_length() const { return m_size; } // internal void construct_dict(char const* begin) { TORRENT_ASSERT(m_type == none_t); m_type = dict_t; m_size = 0; m_begin = begin; } // internal lazy_entry* dict_append(char const* name); // internal void pop(); // if this is a dictionary, look for a key ``name``, and return // a pointer to its value, or NULL if there is none. lazy_entry* dict_find(char const* name); lazy_entry const* dict_find(char const* name) const { return const_cast(this)->dict_find(name); } lazy_entry* dict_find(std::string const& name); lazy_entry const* dict_find(std::string const& name) const { return const_cast(this)->dict_find(name); } lazy_entry const* dict_find_string(char const* name) const; // if this is a dictionary, look for a key ``name`` whose value // is a string. If such key exist, return a pointer to // its value, otherwise NULL. std::string dict_find_string_value(char const* name) const; pascal_string dict_find_pstr(char const* name) const; // if this is a dictionary, look for a key ``name`` whose value // is an int. If such key exist, return a pointer to its value, // otherwise NULL. boost::int64_t dict_find_int_value(char const* name , boost::int64_t default_val = 0) const; lazy_entry const* dict_find_int(char const* name) const; // these functions require that ``this`` is a dictionary. // (this->type() == dict_t). They look for an element with the // specified name in the dictionary. ``dict_find_dict`` only // finds dictionaries and ``dict_find_list`` only finds lists. // if no key with the corresponding value of the right type is // found, NULL is returned. lazy_entry const* dict_find_dict(char const* name) const; lazy_entry const* dict_find_dict(std::string const& name) const; lazy_entry const* dict_find_list(char const* name) const; // if this is a dictionary, return the key value pair at // position ``i`` from the dictionary. std::pair dict_at(int i) const; // requires that ``this`` is a dictionary. return the // number of items in it int dict_size() const { TORRENT_ASSERT(m_type == dict_t); return m_size; } // internal void construct_list(char const* begin) { TORRENT_ASSERT(m_type == none_t); m_type = list_t; m_size = 0; m_begin = begin; } // internal lazy_entry* list_append(); // requires that ``this`` is a list. return // the item at index ``i``. lazy_entry* list_at(int i) { TORRENT_ASSERT(m_type == list_t); TORRENT_ASSERT(i < int(m_size)); return &m_data.list[i+1]; } lazy_entry const* list_at(int i) const { return const_cast(this)->list_at(i); } // these functions require ``this`` to have the type list. // (this->type() == list_t). ``list_string_value_at`` returns // the string at index ``i``. ``list_pstr_at`` // returns a pascal_string of the string value at index ``i``. // if the element at ``i`` is not a string, an empty string // is returned. std::string list_string_value_at(int i) const; pascal_string list_pstr_at(int i) const; // this function require ``this`` to have the type list. // (this->type() == list_t). returns the integer value at // index ``i``. If the element at ``i`` is not an integer // ``default_val`` is returned, which defaults to 0. boost::int64_t list_int_value_at(int i, boost::int64_t default_val = 0) const; // if this is a list, return the number of items in it. int list_size() const { TORRENT_ASSERT(m_type == list_t); return int(m_size); } // internal: end points one byte passed last byte in the source // buffer backing the bencoded structure. void set_end(char const* end) { TORRENT_ASSERT(end > m_begin); TORRENT_ASSERT(end - m_begin < INT_MAX); m_len = int(end - m_begin); } // internal void clear(); // internal: releases ownership of any memory allocated void release() { m_data.start = NULL; m_size = 0; m_type = none_t; } // internal ~lazy_entry() { clear(); } // returns pointers into the source buffer where // this entry has its bencoded data std::pair data_section() const; // swap values of ``this`` and ``e``. void swap(lazy_entry& e) { using std::swap; boost::uint32_t tmp = e.m_type; e.m_type = m_type; m_type = tmp; tmp = e.m_size; e.m_size = m_size; m_size = tmp; swap(m_data.start, e.m_data.start); swap(m_begin, e.m_begin); swap(m_len, e.m_len); } private: int capacity() const; union data_t { // for the dict and list arrays, the first item is not part // of the array. Instead its m_len member indicates the capacity // of the allocation lazy_dict_entry* dict; lazy_entry* list; char const* start; } m_data; // used for dictionaries and lists to record the range // in the original buffer they are based on char const* m_begin; // the number of bytes this entry extends in the // bencoded buffer boost::uint32_t m_len; // if list or dictionary, the number of items boost::uint32_t m_size:29; // element type (dict, list, int, string) boost::uint32_t m_type:3; // non-copyable lazy_entry(lazy_entry const&); lazy_entry const& operator=(lazy_entry const&); }; struct TORRENT_DEPRECATED lazy_dict_entry { char const* name; lazy_entry val; }; // print the bencoded structure in a human-readable format to a string // that's returned. TORRENT_DEPRECATED_EXPORT std::string print_entry(lazy_entry const& e , bool single_line = false, int indent = 0); // defined in bdecode.cpp TORRENT_DEPRECATED TORRENT_EXTRA_EXPORT char const* parse_int(char const* start , char const* end, char delimiter, boost::int64_t& val , bdecode_errors::error_code_enum& ec); } #endif // TORRENT_NO_DEPRECATE #endif libtorrent-rasterbar-1.1.13/include/libtorrent/link.hpp000066400000000000000000000045561351156116000232320ustar00rootroot00000000000000/* Copyright (c) 2011-2018, 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_LINK_HPP_INCLUDED #define TORRENT_LINK_HPP_INCLUDED namespace libtorrent { struct link { link() : index(-1) {} // this is either -1 (not in the list) // or the index of where in the list this // element is found int index; bool in_list() const { return index >= 0; } void clear() { index = -1; } template void unlink(std::vector& list, int link_index) { if (index == -1) return; TORRENT_ASSERT(index >= 0 && index < int(list.size())); int last = int(list.size()) - 1; if (index < last) { list[last]->m_links[link_index].index = index; list[index] = list[last]; } list.resize(last); index = -1; } template void insert(std::vector& list, T* self) { if (index >= 0) return; TORRENT_ASSERT(index == -1); index = int(list.size()); list.push_back(self); } }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/linked_list.hpp000066400000000000000000000100531351156116000245630ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_LINKED_LIST_HPP #define TORRENT_LINKED_LIST_HPP #include "libtorrent/assert.hpp" namespace libtorrent { template struct list_node { list_node() : prev(0), next(0) {} T* prev; T* next; }; template struct list_iterator { template friend struct linked_list; T const* get() const { return m_current; } T* get() { return m_current; } void next() { m_current = m_current->next; } void prev() { m_current = m_current->prev; } private: list_iterator(T* cur) : m_current(cur) {} // the current element T* m_current; }; // T must derive from list_node. Having an enable_if here would require T // to be a complete type, which is a bit too restrictive. template struct linked_list { linked_list(): m_first(NULL), m_last(NULL), m_size(0) {} list_iterator iterate() const { return list_iterator(m_first); } void erase(T* e) { #if TORRENT_USE_ASSERTS T* tmp = m_first; bool found = false; while (tmp) { if (tmp == e) { found = true; break; } tmp = tmp->next; } TORRENT_ASSERT(found); #endif if (e == m_first) { TORRENT_ASSERT(e->prev == 0); m_first = e->next; } if (e == m_last) { TORRENT_ASSERT(e->next == 0); m_last = e->prev; } if (e->prev) e->prev->next = e->next; if (e->next) e->next->prev = e->prev; e->next = 0; e->prev = 0; TORRENT_ASSERT(m_size > 0); --m_size; TORRENT_ASSERT(m_last == 0 || m_last->next == 0); } void push_front(T* e) { TORRENT_ASSERT(e->next == 0); TORRENT_ASSERT(e->prev== 0); TORRENT_ASSERT(m_last == 0 || m_last->next == 0); e->prev = 0; e->next = m_first; if (m_first) m_first->prev = e; else m_last = e; m_first = e; ++m_size; } void push_back(T* e) { TORRENT_ASSERT(e->next == 0); TORRENT_ASSERT(e->prev== 0); TORRENT_ASSERT(m_last == 0 || m_last->next == 0); e->prev = m_last; e->next = 0; if (m_last) m_last->next = e; else m_first = e; m_last = e; ++m_size; } T* get_all() { TORRENT_ASSERT(m_last == 0 || m_last->next == 0); TORRENT_ASSERT(m_first == 0 || m_first->prev == 0); T* e = m_first; m_first = 0; m_last = 0; m_size = 0; return e; } T* back() { return m_last; } T* front() { return m_first; } T const* back() const { return m_last; } T const* front() const { return m_first; } int size() const { return m_size; } bool empty() const { return m_size == 0; } private: T* m_first; T* m_last; int m_size; }; } #endif // LINKED_LIST_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/lsd.hpp000066400000000000000000000070521351156116000230510ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_LSD_HPP #define TORRENT_LSD_HPP #include "libtorrent/socket.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/deadline_timer.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #ifndef TORRENT_DISABLE_LOGGING #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { typedef boost::function peer_callback_t; #ifndef TORRENT_DISABLE_LOGGING typedef boost::function log_callback_t; #endif class lsd : public boost::enable_shared_from_this { public: lsd(io_service& ios, peer_callback_t const& cb #ifndef TORRENT_DISABLE_LOGGING , log_callback_t const& log #endif ); ~lsd(); void start(error_code& ec); void announce(sha1_hash const& ih, int listen_port, bool broadcast = false); void close(); private: boost::shared_ptr self() { return shared_from_this(); } void announce_impl(sha1_hash const& ih, int listen_port , bool broadcast, int retry_count); void resend_announce(error_code const& e, sha1_hash const& ih , int listen_port, int retry_count); void on_announce(udp::endpoint const& from, char* buffer , std::size_t bytes_transferred); peer_callback_t m_callback; // the udp socket used to send and receive // multicast messages on broadcast_socket m_socket; #if TORRENT_USE_IPV6 broadcast_socket m_socket6; #endif #ifndef TORRENT_DISABLE_LOGGING log_callback_t m_log_cb; void debug_log(char const* fmt, ...) const TORRENT_FORMAT(2,3); #endif // used to resend udp packets in case // they time out deadline_timer m_broadcast_timer; // this is a random (presumably unique) // ID for this LSD node. It is used to // ignore our own broadcast messages. // There's no point in adding ourselves // as a peer int m_cookie; bool m_disabled; #if TORRENT_USE_IPV6 bool m_disabled6; #endif }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/magnet_uri.hpp000066400000000000000000000064441351156116000244250ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_MAGNET_URI_HPP_INCLUDED #define TORRENT_MAGNET_URI_HPP_INCLUDED #include #include "libtorrent/config.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/add_torrent_params.hpp" namespace libtorrent { struct torrent_handle; class session; // Generates a magnet URI from the specified torrent. If the torrent // handle is invalid, an empty string is returned. // // For more information about magnet links, see magnet-links_. // TORRENT_EXPORT std::string make_magnet_uri(torrent_handle const& handle); TORRENT_EXPORT std::string make_magnet_uri(torrent_info const& info); #ifndef TORRENT_NO_DEPRECATE #ifndef BOOST_NO_EXCEPTIONS // deprecated in 0.14 TORRENT_DEPRECATED_EXPORT torrent_handle add_magnet_uri(session& ses, std::string const& uri , std::string const& save_path , storage_mode_t storage_mode = storage_mode_sparse , bool paused = false , storage_constructor_type sc = default_storage_constructor , void* userdata = 0); // deprecated in 0.16. Instead, pass in the magnet link as add_torrent_params::url TORRENT_DEPRECATED_EXPORT torrent_handle add_magnet_uri(session& ses, std::string const& uri , add_torrent_params const& p); #endif // deprecated in 0.16. Instead, pass in the magnet link as add_torrent_params::url TORRENT_DEPRECATED_EXPORT torrent_handle add_magnet_uri(session& ses, std::string const& uri , add_torrent_params const& p, error_code& ec); #endif // This function parses out information from the magnet link and populates the // add_torrent_params object. TORRENT_EXPORT void parse_magnet_uri(std::string const& uri , add_torrent_params& p, error_code& ec); // internal, delete when merge in master TORRENT_EXTRA_EXPORT void parse_magnet_uri_peers(std::string const& uri, std::vector& peers); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/max.hpp000066400000000000000000000062121351156116000230510ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_MAX_TYPE #define TORRENT_MAX_TYPE namespace libtorrent { template struct min { enum { value = v1 struct max { enum { value = v1>v2?v1:v2 }; }; template struct max3 { enum { temp = max::value, value = max::value }; }; template struct max4 { enum { temp1 = max::value, temp2 = max::value, value = max::value }; }; template struct max5 { enum { temp = max4::value, value = max::value }; }; template struct max6 { enum { temp1 = max::value, temp2 = max::value, temp3 = max::value, value = max3::value }; }; template struct max7 { enum { temp1 = max::value, temp2 = max::value, temp3 = max3::value, value = max3::value }; }; template struct max8 { enum { temp1 = max::value, temp2 = max3::value, temp3 = max3::value, value = max3::value }; }; template struct max9 { enum { temp1 = max3::value, temp2 = max3::value, temp3 = max3::value, value = max3::value }; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/natpmp.hpp000066400000000000000000000121561351156116000235670ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_NATPMP_HPP #define TORRENT_NATPMP_HPP #include "libtorrent/io_service_fwd.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/address.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/deadline_timer.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { // int: port mapping index // int: external port // std::string: error message typedef boost::function portmap_callback_t; typedef boost::function log_callback_t; class natpmp : public boost::enable_shared_from_this { public: natpmp(io_service& ios, portmap_callback_t const& cb , log_callback_t const& lcb); void start(); // maps the ports, if a port is set to 0 // it will not be mapped enum protocol_type { none = 0, udp = 1, tcp = 2 }; int add_mapping(protocol_type p, int external_port, int local_port); void delete_mapping(int mapping_index); bool get_mapping(int mapping_index, int& local_port, int& external_port, int& protocol) const; void close(); private: boost::shared_ptr self() { return shared_from_this(); } void update_mapping(int i, mutex::scoped_lock& l); void send_map_request(int i, mutex::scoped_lock& l); void send_get_ip_address_request(mutex::scoped_lock& l); void resend_request(int i, error_code const& e); void on_reply(error_code const& e , std::size_t bytes_transferred); void try_next_mapping(int i, mutex::scoped_lock& l); void update_expiration_timer(mutex::scoped_lock& l); void mapping_expired(error_code const& e, int i); void close_impl(mutex::scoped_lock& l); void log(char const* msg, mutex::scoped_lock& l); void disable(error_code const& ec, mutex::scoped_lock& l); struct mapping_t { enum action_t { action_none, action_add, action_delete }; mapping_t() : action(action_none) , local_port(0) , external_port(0) , protocol(none) , map_sent(false) , outstanding_request(false) {} // indicates that the mapping has changed // and needs an update int action; // the time the port mapping will expire time_point expires; // the local port for this mapping. If this is set // to 0, the mapping is not in use int local_port; // the external (on the NAT router) port // for the mapping. This is the port we // should announce to others int external_port; int protocol; // set to true when the first map request is sent bool map_sent; // set to true while we're waiting for a response bool outstanding_request; }; portmap_callback_t m_callback; log_callback_t m_log_callback; std::vector m_mappings; // the endpoint to the nat router udp::endpoint m_nat_endpoint; // this is the mapping that is currently // being updated. It is -1 in case no // mapping is being updated at the moment int m_currently_mapping; // current retry count int m_retry_count; // used to receive responses in char m_response_buffer[16]; // router external IP address address m_external_ip; // the endpoint we received the message from udp::endpoint m_remote; // the udp socket used to communicate // with the NAT router udp::socket m_socket; // used to resend udp packets in case // they time out deadline_timer m_send_timer; // timer used to refresh mappings deadline_timer m_refresh_timer; // the mapping index that will expire next int m_next_refresh; bool m_disabled; bool m_abort; mutable mutex m_mutex; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/network_thread_pool.hpp000066400000000000000000000050241351156116000263350ustar00rootroot00000000000000/* Copyright (c) 2011-2018, 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_NETWORK_THREAD_POOL_HPP_INCLUDED #define TORRENT_NETWORK_THREAD_POOL_HPP_INCLUDED #include "libtorrent/thread_pool.hpp" #include #include namespace libtorrent { class peer_connection; class buffer; struct socket_job { socket_job() : type(none), vec(NULL), recv_buf(NULL), buf_size(0) {} #if __cplusplus >= 201103L socket_job(socket_job const&) = default; socket_job& operator=(socket_job const&) = default; #endif enum job_type_t { read_job = 0, write_job, none }; job_type_t type; // used for write jobs std::vector const* vec; // used for read jobs char* recv_buf; int buf_size; boost::array read_vec; boost::shared_ptr peer; // defined in session_impl.cpp ~socket_job(); }; // defined in session_impl.cpp struct network_thread_pool : thread_pool { void process_job(socket_job const& j, bool post); }; } #endif // TORRENT_NETWORK_THREAD_POOL_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/operations.hpp000066400000000000000000000057731351156116000244620ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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_OPERATIONS_HPP_INCLUDED #define TORRENT_OPERATIONS_HPP_INCLUDED namespace libtorrent { // these constants are used to identify the operation that failed, causing a // peer to disconnect enum operation_t { // this is used when the bittorrent logic // determines to disconnect op_bittorrent = 0, // a call to iocontrol failed op_iocontrol, // a call to getpeername failed (querying the remote IP of a // connection) op_getpeername, // a call to getname failed (querying the local IP of a // connection) op_getname, // an attempt to allocate a receive buffer failed op_alloc_recvbuf, // an attempt to allocate a send buffer failed op_alloc_sndbuf, // writing to a file failed op_file_write, // reading from a file failed op_file_read, // a non-read and non-write file operation failed op_file, // a socket write operation failed op_sock_write, // a socket read operation failed op_sock_read, // a call to open(), to create a socket socket failed op_sock_open, // a call to bind() on a socket failed op_sock_bind, // an attempt to query the number of bytes available to read from a socket // failed op_available, // a call related to bittorrent protocol encryption failed op_encryption, // an attempt to connect a socket failed op_connect, // establishing an SSL connection failed op_ssl_handshake, // a connection failed to satisfy the bind interface setting op_get_interface }; } #endif // TORRENT_OPERATIONS_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/packet_buffer.hpp000066400000000000000000000103601351156116000250630ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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_PACKET_BUFFER_HPP_INCLUDED #define TORRENT_PACKET_BUFFER_HPP_INCLUDED #include "libtorrent/config.hpp" #include "boost/cstdint.hpp" #include namespace libtorrent { // this is a circular buffer that automatically resizes // itself as elements are inserted. Elements are indexed // by integers and are assumed to be sequential. Unless the // old elements are removed when new elements are inserted, // the buffer will be resized. // m_capacity is the number of elements in m_array // and must be an even 2^x. // m_first is the lowest index that has an element // it also determines which indices the other slots // refers to. Since it's a circular buffer, it wraps // around. For example // m_first = 9 // | refers to index 14 // | | // V V // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | | | | | | | | | | | | | | | | | mask = (m_capacity-1) // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // ^ // | // refers to index 15 // whenever the element at the cursor is removed, the // cursor is bumped to the next occupied element class TORRENT_EXTRA_EXPORT packet_buffer_impl { public: typedef boost::uint32_t index_type; packet_buffer_impl(); ~packet_buffer_impl(); void* insert(index_type idx, void* value); std::size_t size() const { return m_size; } bool empty() const { return m_size == 0; } std::size_t capacity() const { return m_capacity; } void* at(index_type idx) const; void* remove(index_type idx); void reserve(std::size_t size); index_type cursor() const { return m_first; } index_type span() const { return (m_last - m_first) & 0xffff; } #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif private: void** m_storage; std::size_t m_capacity; // this is the total number of elements that are occupied // in the array std::size_t m_size; // This defines the first index that is part of the m_storage. // last is one passed the last used slot index_type m_first; index_type m_last; }; template class packet_buffer : packet_buffer_impl { public: using packet_buffer_impl::index_type; using packet_buffer_impl::size; using packet_buffer_impl::empty; using packet_buffer_impl::capacity; using packet_buffer_impl::reserve; using packet_buffer_impl::cursor; using packet_buffer_impl::span; T* insert(index_type i, T* p) { return static_cast(packet_buffer_impl::insert(i, p)); } T* at(index_type idx) const { return static_cast(packet_buffer_impl::at(idx)); } T* remove(index_type idx) { return static_cast(packet_buffer_impl::remove(idx)); } }; } #endif // TORRENT_PACKET_BUFFER_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/parse_url.hpp000066400000000000000000000040261351156116000242610ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_PARSE_URL_HPP_INCLUDED #define TORRENT_PARSE_URL_HPP_INCLUDED #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include #include "libtorrent/config.hpp" #include "libtorrent/error_code.hpp" namespace libtorrent { // returns protocol, auth, hostname, port, path TORRENT_EXTRA_EXPORT boost::tuple parse_url_components(std::string url, error_code& ec); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/part_file.hpp000066400000000000000000000105031351156116000242270ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/file.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/thread.hpp" // for mutex namespace libtorrent { struct TORRENT_EXTRA_EXPORT part_file { // create a part file at 'path', that can hold 'num_pieces' pieces. // each piece being 'piece_size' number of bytes part_file(std::string const& path, std::string const& name, int num_pieces, int piece_size); ~part_file(); int writev(file::iovec_t const* bufs, int num_bufs, int piece, int offset, error_code& ec); int readv(file::iovec_t const* bufs, int num_bufs, int piece, int offset, error_code& ec); // free the slot the given piece is stored in. We no longer need to store this // piece in the part file void free_piece(int piece); void move_partfile(std::string const& path, error_code& ec); void import_file(file& f, boost::int64_t offset, boost::int64_t size, error_code& ec); void export_file(file& f, boost::int64_t offset, boost::int64_t size, error_code& ec); // flush the metadata void flush_metadata(error_code& ec); private: void open_file(int mode, error_code& ec); void flush_metadata_impl(error_code& ec); boost::int64_t slot_offset(boost::int64_t const slot) const { return m_header_size + slot * m_piece_size; } std::string m_path; std::string const m_name; // allocate a slot and return the slot index int allocate_slot(int piece); // this mutex must be held while accessing the data // structure. Not while reading or writing from the file though! // it's important to support multithreading mutex m_mutex; // this is a list of unallocated slots in the part file // within the m_num_allocated range std::vector m_free_slots; // this is the number of slots allocated int m_num_allocated; // the max number of pieces in the torrent this part file is // backing int const m_max_pieces; // number of bytes each piece contains int const m_piece_size; // this is the size of the part_file header, it is added // to offsets when calculating the offset to read and write // payload data from int const m_header_size; // if this is true, the metadata in memory has changed since // we last saved or read it from disk. It means that we // need to flush the metadata before closing the file bool m_dirty_metadata; // maps a piece index to the part-file slot it is stored in boost::unordered_map m_piece_map; // this is the file handle to the part file // it's allocated on the heap and reference counted, to allow it to be // closed and re-opened while other threads are still using it boost::shared_ptr m_file; }; } libtorrent-rasterbar-1.1.13/include/libtorrent/pe_crypto.hpp000066400000000000000000000111201351156116000242620ustar00rootroot00000000000000/* Copyright (c) 2007-2018, Un Shyam & 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. */ #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) #ifndef TORRENT_PE_CRYPTO_HPP_INCLUDED #define TORRENT_PE_CRYPTO_HPP_INCLUDED #include "libtorrent/config.hpp" // RC4 state from libtomcrypt struct rc4 { int x, y; unsigned char buf[256]; }; void TORRENT_EXTRA_EXPORT rc4_init(const unsigned char* in, unsigned long len, rc4 *state); unsigned long TORRENT_EXTRA_EXPORT rc4_encrypt(unsigned char *out, unsigned long outlen, rc4 *state); #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/receive_buffer.hpp" #include "libtorrent/peer_id.hpp" // For sha1_hash #include "libtorrent/extensions.hpp" #include "libtorrent/assert.hpp" #include namespace libtorrent { class TORRENT_EXTRA_EXPORT dh_key_exchange { public: dh_key_exchange(); bool good() const { return true; } // Get local public key, always 96 bytes char const* get_local_key() const; // read remote_pubkey, generate and store shared secret in // m_dh_shared_secret. int compute_secret(const char* remote_pubkey); char const* get_secret() const { return m_dh_shared_secret; } sha1_hash const& get_hash_xor_mask() const { return m_xor_mask; } private: int get_local_key_size() const { return sizeof(m_dh_local_key); } char m_dh_local_key[96]; char m_dh_local_secret[96]; char m_dh_shared_secret[96]; sha1_hash m_xor_mask; }; struct encryption_handler { int encrypt(std::vector& iovec); int decrypt(crypto_receive_buffer& recv_buffer, std::size_t& bytes_transferred); bool switch_send_crypto(boost::shared_ptr crypto , int pending_encryption); void switch_recv_crypto(boost::shared_ptr crypto , crypto_receive_buffer& recv_buffer); bool is_send_plaintext() const { return m_send_barriers.empty() || m_send_barriers.back().next != INT_MAX; } bool is_recv_plaintext() const { return m_dec_handler.get() == NULL; } private: struct barrier { barrier(boost::shared_ptr plugin, int n) : enc_handler(plugin), next(n) {} boost::shared_ptr enc_handler; // number of bytes to next barrier int next; }; std::list m_send_barriers; boost::shared_ptr m_dec_handler; }; struct TORRENT_EXTRA_EXPORT rc4_handler : crypto_plugin { public: rc4_handler(); // Input keys must be 20 bytes void set_incoming_key(unsigned char const* key, int len) TORRENT_OVERRIDE; void set_outgoing_key(unsigned char const* key, int len) TORRENT_OVERRIDE; int encrypt(std::vector& buf) TORRENT_OVERRIDE; void decrypt(std::vector& buf , int& consume , int& produce , int& packet_size) TORRENT_OVERRIDE; private: rc4 m_rc4_incoming; rc4 m_rc4_outgoing; // determines whether or not encryption and decryption is enabled bool m_encrypt; bool m_decrypt; }; } // namespace libtorrent #endif // TORRENT_PE_CRYPTO_HPP_INCLUDED #endif // TORRENT_DISABLE_ENCRYPTION libtorrent-rasterbar-1.1.13/include/libtorrent/peer.hpp000066400000000000000000000042261351156116000232220ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include "libtorrent/peer_id.hpp" #include "libtorrent/address.hpp" namespace libtorrent { struct TORRENT_EXTRA_EXPORT peer_entry { std::string hostname; peer_id pid; boost::uint16_t port; bool operator==(const peer_entry& p) const { return pid == p.pid; } bool operator<(const peer_entry& p) const { return pid < p.pid; } }; struct ipv4_peer_entry { address_v4::bytes_type ip; boost::uint16_t port; }; #if TORRENT_USE_IPV6 struct ipv6_peer_entry { address_v6::bytes_type ip; boost::uint16_t port; }; #endif } #endif // TORRENT_PEER_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/peer_class.hpp000066400000000000000000000121111351156116000243770ustar00rootroot00000000000000/* Copyright (c) 2011-2018, 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_CLASS_HPP_INCLUDED #define TORRENT_PEER_CLASS_HPP_INCLUDED #include "libtorrent/bandwidth_limit.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { typedef int peer_class_t; // holds settings for a peer class. Used in set_peer_class() and // get_peer_class() calls. struct TORRENT_EXPORT peer_class_info { // ``ignore_unchoke_slots`` determines whether peers should always // unchoke a peer, regardless of the choking algorithm, or if it should // honor the unchoke slot limits. It's used for local peers by default. // If *any* of the peer classes a peer belongs to has this set to true, // that peer will be unchoked at all times. bool ignore_unchoke_slots; // adjusts the connection limit (global and per torrent) that applies to // this peer class. By default, local peers are allowed to exceed the // normal connection limit for instance. This is specified as a percent // factor. 100 makes the peer class apply normally to the limit. 200 // means as long as there are fewer connections than twice the limit, we // accept this peer. This factor applies both to the global connection // limit and the per-torrent limit. Note that if not used carefully one // peer class can potentially completely starve out all other over time. int connection_limit_factor; // not used by libtorrent. It's intended as a potentially user-facing // identifier of this peer class. std::string label; // transfer rates limits for the whole peer class. They are specified in // bytes per second and apply to the sum of all peers that are members of // this class. int upload_limit; int download_limit; // relative priorities used by the bandwidth allocator in the rate // limiter. If no rate limits are in use, the priority is not used // either. Priorities start at 1 (0 is not a valid priority) and may not // exceed 255. int upload_priority; int download_priority; }; struct TORRENT_EXTRA_EXPORT peer_class { friend struct peer_class_pool; peer_class(std::string const& l) : in_use(true) , ignore_unchoke_slots(false) , connection_limit_factor(100) , label(l) , references(1) { priority[0] = 1; priority[1] = 1; } void clear() { in_use = false; label.clear(); } void set_info(peer_class_info const* pci); void get_info(peer_class_info* pci) const; void set_upload_limit(int limit); void set_download_limit(int limit); // the bandwidth channels, upload and download // keeps track of the current quotas bandwidth_channel channel[2]; // this is set to false when this slot is not in use for a peer_class bool in_use; bool ignore_unchoke_slots; int connection_limit_factor; // priority for bandwidth allocation // in rate limiter. One for upload and one // for download int priority[2]; // the name of this peer class std::string label; private: int references; }; struct TORRENT_EXTRA_EXPORT peer_class_pool { peer_class_t new_peer_class(std::string const& label); void decref(peer_class_t c); void incref(peer_class_t c); peer_class* at(peer_class_t c); peer_class const* at(peer_class_t c) const; private: // state for peer classes (a peer can belong to multiple classes) // this can control std::deque m_peer_classes; // indices in m_peer_classes that are no longer used std::vector m_free_list; }; } #endif // TORRENT_PEER_CLASS_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/peer_class_set.hpp000066400000000000000000000053331351156116000252620ustar00rootroot00000000000000/* Copyright (c) 2011-2018, 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_CLASS_SET_HPP_INCLUDED #define TORRENT_PEER_CLASS_SET_HPP_INCLUDED #include "libtorrent/peer_class.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { // this represents an object that can have many peer classes applied // to it. Most notably, peer connections and torrents derive from this. struct TORRENT_EXTRA_EXPORT peer_class_set { peer_class_set() : m_size(0) {} void add_class(peer_class_pool& pool, peer_class_t c); bool has_class(peer_class_t c) const; void remove_class(peer_class_pool& pool, peer_class_t c); int num_classes() const { return m_size; } peer_class_t class_at(int i) const { TORRENT_ASSERT(i >= 0 && i < int(m_size)); return m_class[i]; } private: // the number of elements used in the m_class array boost::uint8_t m_size; // if this object belongs to any peer-class, this vector contains all // class IDs. Each ID refers to a an entry in m_ses.m_peer_classes which // holds the metadata about the class. Classes affect bandwidth limits // among other things boost::array m_class; }; } #endif // TORRENT_PEER_CLASS_SET_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/peer_class_type_filter.hpp000066400000000000000000000120671351156116000270170ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_CLASS_TYPE_FILTER_HPP_INCLUDED #define TORRENT_PEER_CLASS_TYPE_FILTER_HPP_INCLUDED #include #include namespace libtorrent { // ``peer_class_type_filter`` is a simple container for rules for adding and subtracting // peer-classes from peers. It is applied *after* the peer class filter is applied (which // is based on the peer's IP address). struct TORRENT_EXPORT peer_class_type_filter { peer_class_type_filter() { m_peer_class_type_mask.fill(0xffffffff); m_peer_class_type.fill(0); } enum socket_type_t { // these match the socket types from socket_type.hpp // shifted one down tcp_socket = 0, utp_socket, ssl_tcp_socket, ssl_utp_socket, i2p_socket, num_socket_types }; // ``add()`` and ``remove()`` adds and removes a peer class to be added // to new peers based on socket type. void add(socket_type_t st, peer_class_t peer_class) { TORRENT_ASSERT(peer_class >= 0); TORRENT_ASSERT(peer_class < 32); if (peer_class < 0 || peer_class > 31) return; TORRENT_ASSERT(st < num_socket_types && st >= 0); if (st < 0 || st >= num_socket_types) return; m_peer_class_type[st] |= 1 << peer_class; } void remove(socket_type_t st, peer_class_t peer_class) { TORRENT_ASSERT(peer_class >= 0); TORRENT_ASSERT(peer_class < 32); if (peer_class < 0 || peer_class > 31) return; TORRENT_ASSERT(st < num_socket_types && st >= 0); if (st < 0 || st >= num_socket_types) return; m_peer_class_type[st] &= ~(1 << peer_class); } // ``disallow()`` and ``allow()`` adds and removes a peer class to be // removed from new peers based on socket type. // // The ``peer_class`` argument cannot be greater than 31. The bitmasks representing // peer classes in the ``peer_class_type_filter`` are 32 bits. void disallow(socket_type_t st, peer_class_t peer_class) { TORRENT_ASSERT(peer_class >= 0); TORRENT_ASSERT(peer_class < 32); if (peer_class < 0 || peer_class > 31) return; TORRENT_ASSERT(st < num_socket_types && st >= 0); if (st < 0 || st >= num_socket_types) return; m_peer_class_type_mask[st] &= ~(1 << peer_class); } void allow(socket_type_t st, peer_class_t peer_class) { TORRENT_ASSERT(peer_class >= 0); TORRENT_ASSERT(peer_class < 32); if (peer_class < 0 || peer_class > 31) return; TORRENT_ASSERT(st < num_socket_types && st >= 0); if (st < 0 || st >= num_socket_types) return; m_peer_class_type_mask[st] |= 1 << peer_class; } // takes a bitmask of peer classes and returns a new bitmask of // peer classes after the rules have been applied, based on the socket type argument // (``st``). boost::uint32_t apply(int st, boost::uint32_t peer_class_mask) { TORRENT_ASSERT(st < num_socket_types && st >= 0); if (st < 0 || st >= num_socket_types) return peer_class_mask; // filter peer classes based on type peer_class_mask &= m_peer_class_type_mask[st]; // add peer classes based on type peer_class_mask |= m_peer_class_type[st]; return peer_class_mask; } friend bool operator==(peer_class_type_filter const& lhs , peer_class_type_filter const& rhs) { return lhs.m_peer_class_type_mask == rhs.m_peer_class_type_mask && lhs.m_peer_class_type == rhs.m_peer_class_type; } private: // maps socket type to a bitmask that's used to filter out // (mask) bits from the m_peer_class_filter. boost::array m_peer_class_type_mask; // peer class bitfield added based on socket type boost::array m_peer_class_type; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/peer_connection.hpp000066400000000000000000001200431351156116000254350ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/config.hpp" #include "libtorrent/buffer.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/peer_request.hpp" #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/bandwidth_limit.hpp" #include "libtorrent/socket_type_fwd.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/chained_buffer.hpp" #include "libtorrent/disk_buffer_holder.hpp" #include "libtorrent/bitfield.hpp" #include "libtorrent/bandwidth_socket.hpp" #include "libtorrent/socket_type_fwd.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/sliding_average.hpp" #include "libtorrent/peer_class.hpp" #include "libtorrent/peer_class_set.hpp" #include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/disk_observer.hpp" #include "libtorrent/peer_connection_interface.hpp" #include "libtorrent/piece_picker.hpp" // for piece_block #include "libtorrent/socket.hpp" // for tcp::endpoint #include "libtorrent/io_service_fwd.hpp" #include "libtorrent/receive_buffer.hpp" #include "libtorrent/aux_/allocating_handler.hpp" #ifndef TORRENT_DISABLE_LOGGING #include "libtorrent/debug.hpp" #endif #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include // for std::forward #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { class torrent; struct peer_info; struct disk_io_job; struct disk_interface; struct torrent_peer; #ifndef TORRENT_DISABLE_EXTENSIONS struct peer_plugin; #endif namespace aux { struct session_interface; } struct pending_block { pending_block(piece_block const& b) : block(b), send_buffer_offset(not_in_buffer), not_wanted(false) , timed_out(false), busy(false) {} piece_block block; enum { not_in_buffer = 0x1fffffff }; // the number of bytes into the send buffer this request is. Every time // some portion of the send buffer is transmitted, this offset is // decremented by the number of bytes sent. once this drops below 0, the // request_time field is set to the current time. // if the request has not been written to the send buffer, this field // remains not_in_buffer. boost::uint32_t send_buffer_offset:29; // if any of these are set to true, this block // is not allocated // in the piece picker anymore, and open for // other peers to pick. This may be caused by // it either timing out or being received // unexpectedly from the peer boost::uint32_t not_wanted:1; boost::uint32_t timed_out:1; // the busy flag is set if the block was // requested from another peer when this // request was queued. We only allow a single // busy request at a time in each peer's queue boost::uint32_t busy:1; bool operator==(pending_block const& b) { return b.block == block && b.not_wanted == not_wanted && b.timed_out == timed_out; } }; struct has_block { #if __cplusplus >= 201103L has_block(has_block const&) = default; #endif has_block(piece_block const& b): block(b) {} piece_block const& block; bool operator()(pending_block const& pb) const { return pb.block == block; } private: // explicitly disallow assignment, to silence msvc warning has_block& operator=(has_block const&); }; // argument pack passed to peer_connection constructor struct peer_connection_args { aux::session_interface* ses; aux::session_settings const* sett; counters* stats_counters; buffer_allocator_interface* allocator; disk_interface* disk_thread; io_service* ios; boost::weak_ptr tor; boost::shared_ptr s; tcp::endpoint endp; torrent_peer* peerinfo; }; // internal inline void nop(char*, void*, block_cache_reference) {} struct TORRENT_EXTRA_EXPORT peer_connection_hot_members { // if tor is set, this is an outgoing connection peer_connection_hot_members( boost::weak_ptr t , aux::session_interface& ses , aux::session_settings const& sett) : m_torrent(t) , m_ses(ses) , m_settings(sett) , m_disconnecting(false) , m_connecting(!t.expired()) , m_endgame_mode(false) , m_snubbed(false) , m_interesting(false) , m_choked(true) , m_corked(false) , m_ignore_stats(false) {} protected: // the pieces the other end have bitfield m_have_piece; // this is the torrent this connection is // associated with. If the connection is an // incoming connection, this is set to zero // until the info_hash is received. Then it's // set to the torrent it belongs to. // TODO: make this a raw pointer (to save size in // the first cache line) and make the constructor // take a raw pointer. torrent objects should always // outlive their peers boost::weak_ptr m_torrent; public: // a back reference to the session // the peer belongs to. aux::session_interface& m_ses; // settings that apply to this peer aux::session_settings const& m_settings; protected: // this is true if this connection has been added // to the list of connections that will be closed. bool m_disconnecting:1; // 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:1; // this is set to true if the last time we tried to // pick a piece to download, we could only find // blocks that were already requested from other // peers. In this case, we should not try to pick // another piece until the last one we requested is done bool m_endgame_mode:1; // set to true when a piece request times out. The // result is that the desired pending queue size // is set to 1 bool m_snubbed:1; // the peer has pieces we are interested in bool m_interesting:1; // we have choked the upload to the peer bool m_choked:1; // when this is set, the peer_connection socket is // corked, similar to the linux TCP feature TCP_CORK. // we won't send anything to the actual socket, just // buffer messages up in the application layer send // buffer, and send it once we're uncorked. bool m_corked:1; // when this is set, the transfer stats for this connection // is not included in the torrent or session stats bool m_ignore_stats:1; private: // explicitly disallow assignment, to silence msvc warning peer_connection_hot_members& operator=(peer_connection_hot_members const&); }; class TORRENT_EXTRA_EXPORT peer_connection : public peer_connection_hot_members , public bandwidth_socket , public peer_class_set , public disk_observer , public peer_connection_interface , public boost::enable_shared_from_this { friend class invariant_access; friend struct network_thread_pool; friend class torrent; public: enum connection_type { bittorrent_connection = 0, url_seed_connection = 1, http_seed_connection = 2 }; virtual int type() const = 0; enum channels { upload_channel, download_channel, num_channels }; peer_connection(peer_connection_args const& pack); // this function is called after it has been constructed and properly // reference counted. It is safe to call self() in this function // and schedule events with references to itself (that is not safe to // do in the constructor). virtual void start(); virtual ~peer_connection(); void set_peer_info(torrent_peer* pi) { TORRENT_ASSERT(m_peer_info == 0 || pi == 0 ); TORRENT_ASSERT(pi != NULL || m_disconnect_started); m_peer_info = pi; } torrent_peer* peer_info_struct() const { return m_peer_info; } // this is called when the peer object is created, in case // it was let in by the connections limit slack. This means // the peer needs to, as soon as the handshake is done, either // disconnect itself or another peer. void peer_exceeds_limit() { m_exceeded_limit = true; } // this is called if this peer causes another peer // to be disconnected, in which case it has fulfilled // its requirement. void peer_disconnected_other() { m_exceeded_limit = false; } void send_allowed_set(); #ifndef TORRENT_DISABLE_EXTENSIONS void add_extension(boost::shared_ptr); peer_plugin const* find_plugin(char const* type); #endif // 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(); // this is called when the metadata is retrieved // and the files has been checked virtual void on_metadata() {} void on_metadata_impl(); void picker_options(int o) { m_picker_options = o; } int prefer_contiguous_blocks() const { if (on_parole()) return 1; return m_prefer_contiguous_blocks; } bool on_parole() const; int picker_options() const; void prefer_contiguous_blocks(int num) { m_prefer_contiguous_blocks = num; } bool request_large_blocks() const { return m_request_large_blocks; } void request_large_blocks(bool b) { m_request_large_blocks = b; } void set_endgame(bool b); bool endgame() const { return m_endgame_mode; } bool no_download() const { return m_no_download; } void no_download(bool b) { m_no_download = b; } bool ignore_stats() const { return m_ignore_stats; } void ignore_stats(bool b) { m_ignore_stats = b; } boost::uint32_t peer_rank() const; void fast_reconnect(bool r); bool fast_reconnect() const { return m_fast_reconnect; } // this is called when we receive a new piece // (and it has passed the hash check) void received_piece(int index); // 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); // this will tell the peer to announce the given piece // and only allow it to request that piece void superseed_piece(int replace_piece, int new_piece); bool super_seeded_piece(int index) const { return m_superseed_piece[0] == index || m_superseed_piece[1] == 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(); bool is_seed() const; int num_have_pieces() const { return m_num_pieces; } void set_share_mode(bool m); bool share_mode() const { return m_share_mode; } void set_upload_only(bool u); bool upload_only() const { return m_upload_only; } void set_holepunch_mode(); // 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& peer_id) { m_peer_id = peer_id; } bool has_piece(int i) const; std::vector const& download_queue() const; std::vector const& request_queue() const; std::vector const& upload_queue() const; void clear_request_queue(); // estimate of how long it will take until we have // received all piece requests that we have sent // if extra_bytes is specified, it will include those // bytes as if they've been requested time_duration download_queue_time(int extra_bytes = 0) 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; } void choke_this_peer(); void maybe_unchoke_this_peer(); void update_interest(); virtual void get_peer_info(peer_info& p) const; // 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 associated_torrent() const { return m_torrent; } stat const& statistics() const { return m_statistics; } void add_stat(boost::int64_t downloaded, boost::int64_t uploaded); void sent_bytes(int bytes_payload, int bytes_protocol); void received_bytes(int bytes_payload, int bytes_protocol); void trancieve_ip_packet(int bytes, bool ipv6); void sent_syn(bool ipv6); void received_synack(bool ipv6); // is called once every second by the main loop void second_tick(int tick_interval_ms); void timeout_requests(); boost::shared_ptr get_socket() const { return m_socket; } tcp::endpoint const& remote() const { return m_remote; } tcp::endpoint local_endpoint() const { return m_local; } bitfield const& get_bitfield() const; std::vector const& allowed_fast(); std::vector const& suggested_pieces() const { return m_suggested_pieces; } time_point connected_time() const { return m_connect; } time_point last_received() const { return m_last_receive; } // this will cause this peer_connection to be disconnected. virtual void disconnect(error_code const& ec , operation_t op, int error = 0); // called when a connect attempt fails (not when an // established connection fails) void connect_failed(error_code const& e); 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(error_code const& e); // returns true if this connection is still waiting to // finish the connection attempt bool is_connecting() const { return m_connecting; } // trust management. virtual void received_valid_data(int index); // returns false if the peer should not be // disconnected virtual bool received_invalid_data(int index, bool single_peer); // a connection is local if it was initiated by us. // if it was an incoming connection, it is remote bool is_outgoing() const { return m_outgoing; } bool received_listen_port() const { return m_received_listen_port; } void received_listen_port() { m_received_listen_port = true; } bool on_local_network() const; bool ignore_unchoke_slots() const; bool failed() const { return m_failed; } int desired_queue_size() const { // this peer is in end-game mode we only want // one outstanding request return (m_endgame_mode || m_snubbed) ? 1 : m_desired_queue_size; } // compares this connection against the given connection // for which one is more eligible for an unchoke. // returns true if this is more eligible int download_payload_rate() const { return m_statistics.download_payload_rate(); } // resets the byte counters that are used to measure // the number of bytes transferred within unchoke cycles void reset_choke_counters(); // if this peer connection is useless (neither party is // interested in the other), disconnect it // returns true if the connection was disconnected bool disconnect_if_redundant(); void increase_est_reciprocation_rate(); void decrease_est_reciprocation_rate(); int est_reciprocation_rate() const { return m_est_reciprocation_rate; } #ifndef TORRENT_DISABLE_LOGGING void peer_log(peer_log_alert::direction_t direction , char const* event, char const* fmt, ...) const TORRENT_FORMAT(4,5); void peer_log(peer_log_alert::direction_t direction , char const* event) const; #endif #ifndef TORRENT_DISABLE_LOGGING time_point m_connect_time; time_point m_bitfield_time; time_point m_unchoke_time; #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_dont_have(int piece_index); void incoming_bitfield(bitfield const& bits); void incoming_request(peer_request const& r); void incoming_piece(peer_request const& p, disk_buffer_holder& data); void incoming_piece(peer_request const& p, char const* data); void incoming_piece_fragment(int bytes); void start_receive_piece(peer_request const& r); void incoming_cancel(peer_request const& r); bool can_disconnect(error_code const& ec) const; void incoming_dht_port(int listen_port); void incoming_reject_request(peer_request const& r); void incoming_have_all(); void incoming_have_none(); void incoming_allowed_fast(int index); void incoming_suggest(int index); void set_has_metadata(bool m) { m_has_metadata = m; } bool has_metadata() const { return m_has_metadata; } // the following functions appends messages // to the send buffer bool send_choke(); bool send_unchoke(); void send_interested(); void send_not_interested(); void send_suggest(int piece); void snub_peer(); // reject any request in the request // queue from this piece void reject_piece(int index); bool can_request_time_critical() const; // returns true if the specified block was actually made time-critical. // if the block was already time-critical, it returns false. bool make_time_critical(piece_block const& block); // adds a block to the request queue // returns true if successful, false otherwise enum flags_t { req_time_critical = 1, req_busy = 2 }; bool add_request(piece_block const& b, int flags = 0); // clears the request queue and sends cancels for all messages // in the download queue void cancel_all_requests(); // removes a block from the request queue or download queue // sends a cancel message if appropriate // refills the request queue, and possibly ignoring pieces requested // by peers in the ignore list (to avoid recursion) // if force is true, the blocks is also freed from the piece // picker, allowing another peer to request it immediately void cancel_request(piece_block const& b, bool force = false); void send_block_requests(); void assign_bandwidth(int channel, int amount); #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif // is true until we can be sure that the other end // speaks our protocol (be it bittorrent or http). virtual bool in_handshake() 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 downloading_piece_progress() const; enum message_type_flags { message_type_request = 1 }; void send_buffer(char const* begin, int size, int flags = 0); void setup_send(); void cork_socket() { TORRENT_ASSERT(!m_corked); m_corked = true; } bool is_corked() const { return m_corked; } void uncork_socket(); void append_send_buffer(char* buffer, int size , chained_buffer::free_buffer_fun destructor = &nop , void* userdata = NULL, block_cache_reference ref = block_cache_reference()); virtual void append_const_send_buffer(char const* buffer, int size , chained_buffer::free_buffer_fun destructor = &nop , void* userdata = NULL, block_cache_reference ref = block_cache_reference()); #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES void set_country(char const* c) { TORRENT_ASSERT(strlen(c) == 2); m_country[0] = c[0]; m_country[1] = c[1]; } bool has_country() const { return m_country[0] != 0; } #endif #endif // TORRENT_NO_DEPRECATE int outstanding_bytes() const { return m_outstanding_bytes; } int send_buffer_size() const { return m_send_buffer.size(); } int send_buffer_capacity() const { return m_send_buffer.capacity(); } void max_out_request_queue(int s); int max_out_request_queue() const; #ifdef TORRENT_DEBUG bool piece_failed; #endif time_t last_seen_complete() const { return m_last_seen_complete; } void set_last_seen_complete(int ago) { m_last_seen_complete = time(0) - ago; } boost::int64_t uploaded_in_last_round() const { return m_statistics.total_payload_upload() - m_uploaded_at_last_round; } boost::int64_t downloaded_in_last_round() const { return m_statistics.total_payload_download() - m_downloaded_at_last_round; } boost::int64_t uploaded_since_unchoked() const { return m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; } // the time we last unchoked this peer time_point time_of_last_unchoke() const { return m_last_unchoke; } // called when the disk write buffer is drained again, and we can // start downloading payload again void on_disk(); int num_reading_bytes() const { return m_reading_bytes; } enum sync_t { read_async, read_sync }; void setup_receive(); boost::shared_ptr self() { TORRENT_ASSERT(!m_in_constructor); return shared_from_this(); } counters& stats_counters() const { return m_counters; } int get_priority(int channel) const; protected: size_t try_read(sync_t s, error_code& ec); virtual void get_specific_peer_info(peer_info& p) const = 0; 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_dont_have(int index) = 0; virtual void write_keepalive() = 0; virtual void write_piece(peer_request const& r, disk_buffer_holder& buffer) = 0; virtual void write_suggest(int piece) = 0; virtual void write_bitfield() = 0; virtual void write_reject_request(peer_request const& r) = 0; virtual void write_allow_fast(int piece) = 0; virtual void on_connected() = 0; virtual void on_tick() {} virtual void on_receive(error_code const& error , std::size_t bytes_transferred) = 0; virtual void on_sent(error_code const& error , std::size_t bytes_transferred) = 0; virtual int hit_send_barrier(std::vector&) { return INT_MAX; } bool allocate_disk_receive_buffer(int disk_buffer_size); void attach_to_torrent(sha1_hash const& ih); bool verify_piece(peer_request const& p) const; void update_desired_queue_size(); // called from the main loop when this connection has any // work to do. void on_send_data(error_code const& error , std::size_t bytes_transferred); void on_receive_data(error_code const& error , std::size_t bytes_transferred); // _nb means null_buffers. i.e. we just know the socket is // readable at this point, we don't know how much has been received void on_receive_data_nb(error_code const& error , std::size_t bytes_transferred); void receive_data_impl(error_code const& error , std::size_t bytes_transferred, int read_loops); void set_send_barrier(int bytes) { TORRENT_ASSERT(bytes == INT_MAX || bytes <= send_buffer_size()); m_send_barrier = bytes; } int get_send_barrier() const { return m_send_barrier; } virtual int timeout() const; io_service& get_io_service() { return m_ios; } private: // explicitly disallow assignment, to silence msvc warning peer_connection& operator=(peer_connection const&); void do_update_interest(); void fill_send_buffer(); void on_disk_read_complete(disk_io_job const* j, peer_request r , time_point issue_time); void on_disk_write_complete(disk_io_job const* j , peer_request r, boost::shared_ptr t); void on_seed_mode_hashed(disk_io_job const* j); int request_timeout() const; void check_graceful_pause(); int wanted_transfer(int channel); int request_bandwidth(int channel, int bytes = 0); boost::shared_ptr m_socket; // the queue of blocks we have requested // from this peer std::vector m_download_queue; // the queue of requests we have got // from this peer that haven't been issued // to the disk thread yet std::vector m_requests; // this peer's peer info struct. This may // be 0, in case the connection is incoming // and hasn't been added to a torrent yet. torrent_peer* m_peer_info; // stats counters counters& m_counters; // 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; public: // upload and download channel state // enum from peer_info::bw_state char m_channel_state[2]; protected: receive_buffer m_recv_buffer; // number of bytes this peer can send and receive int m_quota[2]; // the blocks we have reserved in the piece // picker and will request from this peer. std::vector m_request_queue; // this is the limit on the number of outstanding requests // we have to this peer. This is initialized to the settings // in the settings_pack. 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; // this is the peer we're actually talking to // it may not necessarily be the peer we're // connected to, in case we use a proxy tcp::endpoint m_remote; public: chained_buffer m_send_buffer; private: // the disk thread to use to issue disk jobs to disk_interface& m_disk_thread; public: buffer_allocator_interface& m_allocator; private: // io service io_service& m_ios; public: #ifndef TORRENT_DISABLE_EXTENSIONS typedef std::list > extension_list_t; extension_list_t m_extensions; #endif private: // the average rate of receiving complete piece messages sliding_average<20> m_piece_rate; sliding_average<20> m_send_rate; // the average time between incoming pieces. Or, if there is no // outstanding request, the time since the piece was requested. It // is essentially an estimate of the time it will take to completely // receive a payload message after it has been requested. sliding_average<20> m_request_time; // keep the io_service running as long as we // have peer connections io_service::work m_work; // the time when we last got a part of a // piece packet from this peer time_point m_last_piece; // the time we sent a request to // this peer the last time time_point m_last_request; // the time we received the last // piece request from the peer time_point m_last_incoming_request; // the time when we unchoked this peer time_point m_last_unchoke; // if we're unchoked by this peer, this // was the time time_point m_last_unchoked; // the time we last choked this peer. min_time() in // case we never unchoked it time_point m_last_choke; // timeouts time_point m_last_receive; time_point m_last_sent; // the last time we filled our send buffer with payload // this is used for timeouts time_point m_last_sent_payload; // the time when the first entry in the request queue was requested. Used // for request timeout. it doesn't necessarily represent the time when a // specific request was made. Since requests can be handled out-of-order, // it represents whichever request the other end decided to respond to. // Once we get that response, we set it to the current time. // for more information, see the blog post at: // http://blog.libtorrent.org/2011/11/block-request-time-outs/ time_point m_requested; // a timestamp when the remote download rate // was last updated time_point m_remote_dl_update; // the time when async_connect was called // or when the incoming connection was established time_point m_connect; // the time when this peer sent us a not_interested message // the last time. time_point m_became_uninterested; // the time when we sent a not_interested message to // this peer the last time. time_point m_became_uninteresting; // the total payload download bytes // at the last unchoke round. This is used to // measure the number of bytes transferred during // an unchoke cycle, to unchoke peers the more bytes // they sent us boost::int64_t m_downloaded_at_last_round; boost::int64_t m_uploaded_at_last_round; // this is the number of bytes we had uploaded the // last time this peer was unchoked. This does not // reset each unchoke interval/round. This is used to // track upload across rounds, for the full duration of // the peer being unchoked. Specifically, it's used // for the round-robin unchoke algorithm. boost::int64_t m_uploaded_at_last_unchoke; // the number of payload bytes downloaded last second tick boost::int32_t m_downloaded_last_second; // the number of payload bytes uploaded last second tick boost::int32_t m_uploaded_last_second; // the number of bytes that the other // end has to send us in order to respond // to all outstanding piece requests we // have sent to it int m_outstanding_bytes; aux::handler_storage m_read_handler_storage; aux::handler_storage m_write_handler_storage; // we have suggested these pieces to the peer // don't suggest it again bitfield m_sent_suggested_pieces; // the pieces we will send to the peer // if requested (regardless of choke state) std::vector m_accept_fast; // a sent-piece counter for the allowed fast set // to avoid exploitation. Each slot is a counter // for one of the pieces from the allowed-fast set std::vector m_accept_fast_piece_cnt; // the pieces the peer will send us if // requested (regardless of choke state) std::vector m_allowed_fast; // pieces that has been suggested to be // downloaded from this peer std::vector m_suggested_pieces; // the time when this peer last saw a complete copy // of this torrent time_t m_last_seen_complete; // the block we're currently receiving. Or // (-1, -1) if we're not receiving one piece_block m_receiving_block; // the local endpoint for this peer, i.e. our address // and our port. If this is set for outgoing connections // before the connection completes, it means we want to // force the connection to be bound to the specified interface. // if it ends up being bound to a different local IP, the connection // is closed. tcp::endpoint m_local; // remote peer's id peer_id m_peer_id; // the bandwidth channels, upload and download // keeps track of the current quotas bandwidth_channel m_bandwidth_channel[num_channels]; protected: // statistics about upload and download speeds // and total amount of uploads and downloads for // this peer // TODO: factor this out into its own class with a virtual interface // torrent and session should implement this interface stat m_statistics; // the number of outstanding bytes expected // to be received by extensions int m_extension_outstanding_bytes; // the number of time critical requests // queued up in the m_request_queue that // soon will be committed to the download // queue. This is included in download_queue_time() // so that it can be used while adding more // requests and take the previous requests // into account without submitting it all // immediately int m_queued_time_critical; // the number of bytes we are currently reading // from disk, that will be added to the send // buffer as soon as they complete int m_reading_bytes; // options used for the piece picker. These flags will // be augmented with flags controlled by other settings // like sequential download etc. These are here to // let plugins control flags that should always be set int m_picker_options; // 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; // if [0] is -1, super-seeding is not active. If it is >= 0 // this is the piece that is available to this peer. Only // these two pieces can be downloaded from us by this peer. // This will remain the current piece for this peer until // another peer sends us a have message for this piece int m_superseed_piece[2]; // pieces downloaded since last second // timer timeout; used for determining // approx download rate int m_remote_pieces_dled; // approximate peer download rate int m_remote_dl_rate; // the number of bytes send to the disk-io // thread that hasn't yet been completely written. int m_outstanding_writing_bytes; // max transfer rates seen on this peer int m_download_rate_peak; int m_upload_rate_peak; // when using the BitTyrant choker, this is our // estimated reciprocation rate. i.e. the rate // we need to send to this peer for it to unchoke // us int m_est_reciprocation_rate; // stop sending data after this many bytes, INT_MAX = inf int m_send_barrier; // the number of request we should queue up // at the remote end. // TODO: 2 rename this target queue size boost::uint16_t m_desired_queue_size; #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES // in case the session settings is set // to resolve countries, this is set to // the two character country code this // peer resides in. char m_country[2]; #endif #endif // TORRENT_NO_DEPRECATE // if set to non-zero, this peer will always prefer // to request entire n pieces, rather than blocks. // where n is the value of this variable. // if it is 0, the download rate limit setting // will be used to determine if whole pieces // are preferred. int m_prefer_contiguous_blocks; // this is the number of times this peer has had // a request rejected because of a disk I/O failure. // once this reaches a certain threshold, the // peer is disconnected in order to avoid infinite // loops of consistent failures boost::uint8_t m_disk_read_failures; // this is used in seed mode whenever we trigger a hash check // for a piece, before we read it. It's used to throttle // the hash checks to just a few per peer at a time. boost::uint8_t m_outstanding_piece_verification:3; // is true if it was we that connected to the peer // and false if we got an incoming connection // could be considered: true = local, false = remote bool m_outgoing:1; // is true if we learn the incoming connections listening // during the extended handshake bool m_received_listen_port:1; // if this is true, the disconnection // timestamp is not updated when the connection // is closed. This means the time until we can // reconnect to this peer is shorter, and likely // immediate. bool m_fast_reconnect:1; // 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:1; // this is set to true if the connection attempt // succeeded. i.e. the TCP 3-way handshake bool m_connected:1; // if this is true, the blocks picked by the piece // picker will be merged before passed to the // request function. i.e. subsequent blocks are // merged into larger blocks. This is used by // the http-downloader, to request whole pieces // at a time. bool m_request_large_blocks:1; // set to true if this peer is in share mode bool m_share_mode:1; // set to true when this peer is only uploading bool m_upload_only:1; // this is set to true once the bitfield is received bool m_bitfield_received:1; // if this is set to true, the client will not // pick any pieces from this peer bool m_no_download:1; // set to true when we've sent the first round of suggests bool m_sent_suggests:1; // set to true while we're trying to holepunch bool m_holepunch_mode:1; // the other side has told us that it won't send anymore // data to us for a while bool m_peer_choked:1; // this is set to true when a have_all // message is received. This information // is used to fill the bitmask in init() bool m_have_all:1; // other side says that it's interested in downloading // from us. bool m_peer_interested:1; // set to true when we should recalculate interest // for this peer. Since this is a fairly expensive // operation, it's delayed until the second_tick is // fired, so that multiple events that wants to recalc // interest are coalesced into only triggering it once // the actual computation is done in do_update_interest(). bool m_need_interest_update:1; // set to true if this peer has metadata, and false // otherwise. bool m_has_metadata:1; // this is set to true if this peer was accepted exceeding // the connection limit. It means it has to disconnect // itself, or some other peer, as soon as it's completed // the handshake. We need to wait for the handshake in // order to know which torrent it belongs to, to know which // other peers to compare it to. bool m_exceeded_limit:1; // this is slow-start at the bittorrent layer. It affects how we increase // desired queue size (i.e. the number of outstanding requests we keep). // While the underlying transport protocol is in slow-start, the number of // outstanding requests need to increase at the same pace to keep up. bool m_slow_start:1; template aux::allocating_handler make_read_handler(Handler const& handler) { return aux::allocating_handler( handler, m_read_handler_storage ); } template aux::allocating_handler make_write_handler(Handler const& handler) { return aux::allocating_handler( handler, m_write_handler_storage ); } #if TORRENT_USE_ASSERTS public: bool m_in_constructor; bool m_disconnect_started; bool m_initialized; int m_in_use; int m_received_in_piece; bool m_destructed; // this is true while there is an outstanding // async write job on the socket bool m_socket_is_writing; bool is_single_thread() const; #endif }; struct cork { cork(peer_connection& p): m_pc(p), m_need_uncork(false) { if (m_pc.is_corked()) return; m_pc.cork_socket(); m_need_uncork = true; } ~cork() { if (m_need_uncork) m_pc.uncork_socket(); } private: peer_connection& m_pc; bool m_need_uncork; cork& operator=(cork const&); }; } #endif // TORRENT_PEER_CONNECTION_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/peer_connection_handle.hpp000066400000000000000000000106431351156116000267540ustar00rootroot00000000000000/* Copyright (c) 2015-2018, Arvid Norberg, Steven Siloti 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_HANDLE_HPP_INCLUDED #define TORRENT_PEER_CONNECTION_HANDLE_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/operations.hpp" #include "libtorrent/alert_types.hpp" namespace libtorrent { class peer_connection; class bt_peer_connection; struct torrent_handle; struct peer_plugin; struct peer_info; struct crypto_plugin; typedef boost::system::error_code error_code; // hidden struct TORRENT_EXPORT peer_connection_handle { peer_connection_handle(boost::weak_ptr impl) : m_connection(impl) {} int type() const; void add_extension(boost::shared_ptr); peer_plugin const* find_plugin(char const* type); bool is_seed() const; bool upload_only() const; peer_id const& pid() const; bool has_piece(int i) const; bool is_interesting() const; bool is_choked() const; bool is_peer_interested() const; bool has_peer_choked() const; void choke_this_peer(); void maybe_unchoke_this_peer(); void get_peer_info(peer_info& p) const; torrent_handle associated_torrent() const; tcp::endpoint const& remote() const; tcp::endpoint local_endpoint() const; void disconnect(error_code const& ec, operation_t op, int error = 0); bool is_disconnecting() const; bool is_connecting() const; bool is_outgoing() const; bool on_local_network() const; bool ignore_unchoke_slots() const; bool failed() const; #ifndef TORRENT_DISABLE_LOGGING void peer_log(peer_log_alert::direction_t direction , char const* event, char const* fmt = "", ...) const TORRENT_FORMAT(4,5); #endif // TORRENT_DISABLE_LOGGING bool can_disconnect(error_code const& ec) const; bool has_metadata() const; bool in_handshake() const; void send_buffer(char const* begin, int size, int flags = 0); time_t last_seen_complete() const; time_point time_of_last_unchoke() const; bool operator==(peer_connection_handle const& o) const { return !(m_connection < o.m_connection) && !(o.m_connection < m_connection); } bool operator!=(peer_connection_handle const& o) const { return m_connection < o.m_connection || o.m_connection < m_connection; } bool operator<(peer_connection_handle const& o) const { return m_connection < o.m_connection; } boost::shared_ptr native_handle() const { return m_connection.lock(); } private: boost::weak_ptr m_connection; }; struct TORRENT_EXPORT bt_peer_connection_handle : public peer_connection_handle { explicit bt_peer_connection_handle(peer_connection_handle pc) : peer_connection_handle(pc) {} bool packet_finished() const; bool support_extensions() const; bool supports_encryption() const; void switch_send_crypto(boost::shared_ptr crypto); void switch_recv_crypto(boost::shared_ptr crypto); boost::shared_ptr native_handle() const; }; } // namespace libtorrent #endif // TORRENT_PEER_CONNECTION_HANDLE_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/peer_connection_interface.hpp000066400000000000000000000055531351156116000274650ustar00rootroot00000000000000/* Copyright (c) 2013-2018, 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_INTERFACE_HPP #define TORRENT_PEER_CONNECTION_INTERFACE_HPP #include "libtorrent/socket.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/operations.hpp" // for operation_t enum namespace libtorrent { struct torrent_peer; class stat; struct peer_info; // TODO: make this interface smaller! struct TORRENT_EXTRA_EXPORT peer_connection_interface { virtual tcp::endpoint const& remote() const = 0; virtual tcp::endpoint local_endpoint() const = 0; virtual void disconnect(error_code const& ec , operation_t op, int error = 0) = 0; virtual peer_id const& pid() const = 0; virtual void set_holepunch_mode() = 0; virtual torrent_peer* peer_info_struct() const = 0; virtual void set_peer_info(torrent_peer* pi) = 0; virtual bool is_outgoing() const = 0; virtual void add_stat(boost::int64_t downloaded, boost::int64_t uploaded) = 0; virtual bool fast_reconnect() const = 0; virtual bool is_choked() const = 0; virtual bool failed() const = 0; virtual stat const& statistics() const = 0; virtual void get_peer_info(peer_info& p) const = 0; #ifndef TORRENT_DISABLE_LOGGING virtual void peer_log(peer_log_alert::direction_t direction , char const* event, char const* fmt = "", ...) const TORRENT_FORMAT(4,5) = 0; #endif protected: ~peer_connection_interface() {} }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/peer_id.hpp000066400000000000000000000034461351156116000237010ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/config.hpp" #include "libtorrent/sha1_hash.hpp" namespace libtorrent { typedef sha1_hash peer_id; #ifndef TORRENT_NO_DEPRECATE typedef sha1_hash big_number; #endif } #endif // TORRENT_PEER_ID_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/peer_info.hpp000066400000000000000000000362311351156116000242360ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/socket.hpp" #include "libtorrent/deadline_timer.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/config.hpp" #include "libtorrent/bitfield.hpp" #include "libtorrent/time.hpp" namespace libtorrent { // holds information and statistics about one peer // that libtorrent is connected to struct TORRENT_EXPORT peer_info { // a string describing the software at the other end of the connection. // In some cases this information is not available, then it will contain // a string that may give away something about which software is running // in the other end. In the case of a web seed, the server type and // version will be a part of this string. std::string client; // a bitfield, with one bit per piece in the torrent. Each bit tells you // if the peer has that piece (if it's set to 1) or if the peer miss that // piece (set to 0). bitfield pieces; // the total number of bytes downloaded from and uploaded to this peer. // These numbers do not include the protocol chatter, but only the // payload data. boost::int64_t total_download; boost::int64_t total_upload; // the time since we last sent a request to this peer and since any // transfer occurred with this peer time_duration last_request; time_duration last_active; // the time until all blocks in the request queue will be downloaded time_duration download_queue_time; // flags for the peer_info::flags field. Indicates various states // the peer may be in. These flags are not mutually exclusive, but // not every combination of them makes sense either. enum peer_flags_t { // **we** are interested in pieces from this peer. interesting = 0x1, // **we** have choked this peer. choked = 0x2, // the peer is interested in **us** remote_interested = 0x4, // the peer has choked **us**. remote_choked = 0x8, // means that this peer supports the // `extension protocol`__. // // __ extension_protocol.html supports_extensions = 0x10, // The connection was initiated by us, the peer has a // listen port open, and that port is the same as in the // address of this peer. If this flag is not set, this // peer connection was opened by this peer connecting to // us. local_connection = 0x20, // The connection is opened, and waiting for the // handshake. Until the handshake is done, the peer // cannot be identified. handshake = 0x40, // The connection is in a half-open state (i.e. it is // being connected). connecting = 0x80, #ifndef TORRENT_NO_DEPRECATE // The connection is currently queued for a connection // attempt. This may happen if there is a limit set on // the number of half-open TCP connections. queued = 0x100, #else // hidden deprecated__ = 0x100, #endif // The peer has participated in a piece that failed the // hash check, and is now "on parole", which means we're // only requesting whole pieces from this peer until // it either fails that piece or proves that it doesn't // send bad data. on_parole = 0x200, // This peer is a seed (it has all the pieces). seed = 0x400, // This peer is subject to an optimistic unchoke. It has // been unchoked for a while to see if it might unchoke // us in return an earn an upload/unchoke slot. If it // doesn't within some period of time, it will be choked // and another peer will be optimistically unchoked. optimistic_unchoke = 0x800, // This peer has recently failed to send a block within // the request timeout from when the request was sent. // We're currently picking one block at a time from this // peer. snubbed = 0x1000, // This peer has either explicitly (with an extension) // or implicitly (by becoming a seed) told us that it // will not downloading anything more, regardless of // which pieces we have. upload_only = 0x2000, // This means the last time this peer picket a piece, // it could not pick as many as it wanted because there // were not enough free ones. i.e. all pieces this peer // has were already requested from other peers. endgame_mode = 0x4000, // This flag is set if the peer was in holepunch mode // when the connection succeeded. This typically only // happens if both peers are behind a NAT and the peers // connect via the NAT holepunch mechanism. holepunched = 0x8000, // indicates that this socket is runnin on top of the // I2P transport. i2p_socket = 0x10000, // indicates that this socket is a uTP socket utp_socket = 0x20000, // indicates that this socket is running on top of an SSL // (TLS) channel ssl_socket = 0x40000, // this connection is obfuscated with RC4 rc4_encrypted = 0x100000, // the handshake of this connection was obfuscated // with a diffie-hellman exchange plaintext_encrypted = 0x200000 }; // tells you in which state the peer is in. It is set to // any combination of the peer_flags_t enum. boost::uint32_t flags; // the flags indicating which sources a peer can // have come from. A peer may have been seen from // multiple sources enum peer_source_flags { // The peer was received from the tracker. tracker = 0x1, // The peer was received from the kademlia DHT. dht = 0x2, // The peer was received from the peer exchange // extension. pex = 0x4, // The peer was received from the local service // discovery (The peer is on the local network). lsd = 0x8, // The peer was added from the fast resume data. resume_data = 0x10, // we received an incoming connection from this peer incoming = 0x20 }; // a combination of flags describing from which sources this peer // was received. See peer_source_flags. boost::uint32_t source; // the current upload and download speed we have to and from this peer // (including any protocol messages). updated about once per second int up_speed; int down_speed; // The transfer rates of payload data only updated about once per second int payload_up_speed; int payload_down_speed; // the peer's id as used in the bit torrent protocol. This id can be used // to extract 'fingerprints' from the peer. Sometimes it can tell you // which client the peer is using. See identify_client()_ peer_id pid; int queue_bytes; // the number of seconds until the current front piece request will time // out. This timeout can be adjusted through // ``settings_pack::request_timeout``. // -1 means that there is not outstanding request. int request_timeout; // the number of bytes allocated // and used for the peer's send buffer, respectively. int send_buffer_size; int used_send_buffer; // the number of bytes // allocated and used as receive buffer, respectively. int receive_buffer_size; int used_receive_buffer; // the number of pieces this peer has participated in sending us that // turned out to fail the hash check. int num_hashfails; // 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; // the number of block requests that have timed out, and are still in the // download queue int timed_out_requests; // the number of busy requests in the download queue. A busy request is a // request for a block we've also requested from a different peer int busy_requests; // the number of requests messages that are currently in the send buffer // waiting to be sent. int requests_in_buffer; // the number of requests that is tried to be maintained (this is // typically a function of download speed) int target_dl_queue_length; // the number of piece-requests we have received from this peer // that we haven't answered with a piece yet. int upload_queue_length; // the number of times this peer has "failed". i.e. failed to connect or // disconnected us. The failcount is decremented when we see this peer in // a tracker response or peer exchange message. int failcount; // You can know which piece, and which part of that piece, that is // currently being downloaded from a specific peer by looking at these // four members. ``downloading_piece_index`` is the index of the piece // that is currently being downloaded. This may be set to -1 if there's // currently no piece downloading from this peer. If it is >= 0, the // other three members are valid. ``downloading_block_index`` is the // index of the block (or sub-piece) that is being downloaded. // ``downloading_progress`` is the number of bytes of this block we have // received from the peer, and ``downloading_total`` is the total number // of bytes in this block. int downloading_piece_index; int downloading_block_index; int downloading_progress; int downloading_total; // the kind of connection this is. Used for the connection_type field. enum connection_type_t { // Regular bittorrent connection standard_bittorrent = 0, // HTTP connection using the `BEP 19`_ protocol web_seed = 1, // HTTP connection using the `BEP 17`_ protocol http_seed = 2 }; // the kind of connection this peer uses. See connection_type_t. int connection_type; // an estimate of the rate this peer is downloading at, in // bytes per second. int remote_dl_rate; // the number of bytes this peer has pending in the disk-io thread. // Downloaded and waiting to be written to disk. This is what is capped // by ``settings_pack::max_queued_disk_bytes``. int pending_disk_bytes; // number of outstanding bytes to read // from disk int pending_disk_read_bytes; // the number of bytes this peer has been assigned to be allowed to send // and receive until it has to request more quota from the bandwidth // manager. int send_quota; int receive_quota; // an estimated round trip time to this peer, in milliseconds. It is // estimated by timing the the TCP ``connect()``. It may be 0 for // incoming connections. int rtt; // the number of pieces this peer has. int num_pieces; // the highest download and upload rates seen on this connection. They // are given in bytes per second. This number is reset to 0 on reconnect. int download_rate_peak; int upload_rate_peak; // the progress of the peer in the range [0, 1]. This is always 0 when // floating point operations are disabled, instead use ``progress_ppm``. float progress; // [0, 1] // indicates the download progress of the peer in the range [0, 1000000] // (parts per million). int progress_ppm; // this is an estimation of the upload rate, to this peer, where it will // unchoke us. This is a coarse estimation based on the rate at which // we sent right before we were choked. This is primarily used for the // bittyrant choking algorithm. int estimated_reciprocation_rate; // the IP-address to this peer. The type is an asio endpoint. For // more info, see the asio_ documentation. // // .. _asio: http://asio.sourceforge.net/asio-0.3.8/doc/asio/reference.html tcp::endpoint ip; // the IP and port pair the socket is bound to locally. i.e. the IP // address of the interface it's going out over. This may be useful for // multi-homed clients with multiple interfaces to the internet. tcp::endpoint local_endpoint; // bits for the read_state and write_state enum bw_state { // The peer is not waiting for any external events to // send or receive data. bw_idle = 0, // The peer is waiting for the rate limiter. bw_limit = 1, // The peer has quota and is currently waiting for a // network read or write operation to complete. This is // the state all peers are in if there are no bandwidth // limits. bw_network = 2, // The peer is waiting for the disk I/O thread to catch // up writing buffers to disk before downloading more. bw_disk = 4 }; #ifndef TORRENT_NO_DEPRECATE enum bw_state_deprecated { bw_torrent = bw_limit, bw_global = bw_limit }; #endif // bitmasks indicating what state this peer // is in with regards to sending and receiving data. The states are declared in the // bw_state enum. char read_state; char write_state; #ifndef TORRENT_NO_DEPRECATE // country code deprecated in 1.1 // the two letter `ISO 3166 country code`__ for the country the peer is // connected from. If the country hasn't been resolved yet, both chars // are set to 0. If the resolution failed for some reason, the field is // set to "--". If the resolution service returns an invalid country // code, it is set to "!!". The ``countries.nerd.dk`` service is used to // look up countries. This field will remain set to 0 unless the torrent // is set to resolve countries, see `resolve_countries()`_. // // __ http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html char country[2]; // the number of bytes per second we are allowed to send to or receive // from this peer. It may be -1 if there's no local limit on the peer. // The global limit and the torrent limit may also be enforced. int upload_limit; int download_limit; // a measurement of the balancing of free download (that we get) and free // upload that we give. Every peer gets a certain amount of free upload, // but this member says how much *extra* free upload this peer has got. // If it is a negative number it means that this was a peer from which we // have got this amount of free download. boost::int64_t load_balancing; #endif }; // internal struct TORRENT_EXPORT peer_list_entry { // internal enum flags_t { banned = 1 }; // internal tcp::endpoint ip; // internal int flags; // internal boost::uint8_t failcount; // internal boost::uint8_t source; }; } #endif // TORRENT_PEER_INFO_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/peer_list.hpp000066400000000000000000000225511351156116000242560ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include #include "libtorrent/string_util.hpp" // for allocate_string_copy #include "libtorrent/request_blocks.hpp" // for source_rank #include "libtorrent/torrent_peer.hpp" #include "libtorrent/piece_picker.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/address.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/config.hpp" #include "libtorrent/debug.hpp" #include "libtorrent/peer_connection_interface.hpp" namespace libtorrent { struct external_ip; struct ip_filter; class port_filter; struct torrent_peer_allocator_interface; // this object is used to communicate torrent state and // some configuration to the peer_list object. This make // the peer_list type not depend on the torrent type directly. struct torrent_state { torrent_state() : is_paused(false) , is_finished(false) , allow_multiple_connections_per_ip(false) , first_time_seen(false) , max_peerlist_size(1000) , min_reconnect_time(60) , loop_counter(0) , ip(NULL), port(0) , max_failcount(3) {} bool is_paused; bool is_finished; bool allow_multiple_connections_per_ip; // this is set by peer_list::add_peer to either true or false // true means the peer we just added was new, false means // we already knew about the peer bool first_time_seen; int max_peerlist_size; int min_reconnect_time; // the number of iterations over the peer list for this operation int loop_counter; // these are used only by find_connect_candidates in order // to implement peer ranking. See: // http://blog.libtorrent.org/2012/12/swarm-connectivity/ external_ip const* ip; int port; // the number of times a peer must fail before it's no longer considered // a connect candidate int max_failcount; // if any peer were removed during this call, they are returned in // this vector. The caller would want to make sure there are no // references to these torrent_peers anywhere std::vector erased; }; class TORRENT_EXTRA_EXPORT peer_list : single_threaded { public: peer_list(torrent_peer_allocator_interface& alloc); ~peer_list(); #if TORRENT_USE_I2P torrent_peer* add_i2p_peer(char const* destination, int src, char flags , torrent_state* state); #endif enum { // these flags match the flags passed in ut_pex // messages flag_encryption = 0x1, flag_seed = 0x2, flag_utp = 0x4, flag_holepunch = 0x8 }; // this is called once for every torrent_peer we get from // the tracker, pex, lsd or dht. torrent_peer* add_peer(const tcp::endpoint& remote , int source, char flags, torrent_state* state); // false means duplicate connection bool update_peer_port(int port, torrent_peer* p, int src, torrent_state* state); // called when an incoming connection is accepted // false means the connection was refused or failed bool new_connection(peer_connection_interface& c, int session_time, torrent_state* state); // the given connection was just closed void connection_closed(const peer_connection_interface& c , int session_time, torrent_state* state); bool ban_peer(torrent_peer* p); void set_connection(torrent_peer* p, peer_connection_interface* c); void set_failcount(torrent_peer* p, int f); void inc_failcount(torrent_peer* p); void apply_ip_filter(ip_filter const& filter, torrent_state* state , std::vector
& banned); void apply_port_filter(port_filter const& filter, torrent_state* state , std::vector
& banned); void set_seed(torrent_peer* p, bool s); // this clears all cached peer priorities. It's called when // our external IP changes void clear_peer_prio(); #if TORRENT_USE_ASSERTS bool has_connection(const peer_connection_interface* p); #endif #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif int num_peers() const { return int(m_peers.size()); } #ifdef TORRENT_OPTIMIZE_MEMORY_USAGE typedef std::vector peers_t; #else typedef std::deque peers_t; #endif typedef peers_t::iterator iterator; typedef peers_t::const_iterator const_iterator; iterator begin_peer() { return m_peers.begin(); } iterator end_peer() { return m_peers.end(); } const_iterator begin_peer() const { return m_peers.begin(); } const_iterator end_peer() const { return m_peers.end(); } std::pair find_peers(address const& a) { #if TORRENT_USE_I2P if (a == address()) return std::pair(m_peers.end(), m_peers.end()); #endif return std::equal_range( m_peers.begin(), m_peers.end(), a, peer_address_compare()); } std::pair find_peers(address const& a) const { return std::equal_range( m_peers.begin(), m_peers.end(), a, peer_address_compare()); } torrent_peer* connect_one_peer(int session_time, torrent_state* state); bool has_peer(torrent_peer const* p) const; int num_seeds() const { return m_num_seeds; } int num_connect_candidates() const { return m_num_connect_candidates; } void erase_peer(torrent_peer* p, torrent_state* state); void erase_peer(iterator i, torrent_state* state); void set_max_failcount(torrent_state* st); private: // not copyable peer_list(peer_list const&); peer_list& operator=(peer_list const&); void recalculate_connect_candidates(torrent_state* state); void update_connect_candidates(int delta); void update_peer(torrent_peer* p, int src, int flags , tcp::endpoint const& remote, char const* destination); bool insert_peer(torrent_peer* p, iterator iter, int flags, torrent_state* state); bool compare_peer_erase(torrent_peer const& lhs, torrent_peer const& rhs) const; bool compare_peer(torrent_peer const* lhs, torrent_peer const* rhs , external_ip const& external, int source_port) const; void find_connect_candidates(std::vector& peers , int session_time, torrent_state* state); bool is_connect_candidate(torrent_peer const& p) const; bool is_erase_candidate(torrent_peer const& p) const; bool is_force_erase_candidate(torrent_peer const& pe) const; bool should_erase_immediately(torrent_peer const& p) const; enum flags_t { force_erase = 1 }; void erase_peers(torrent_state* state, int flags = 0); peers_t m_peers; // this should be NULL for the most part. It's set // to point to a valid torrent_peer object if that // object needs to be kept alive. If we ever feel // like removing a torrent_peer from m_peers, we // first check if the peer matches this one, and // if so, don't delete it. torrent_peer* m_locked_peer; // the peer allocator, as stored from the constructor // this must be available in the destructor to free all peers torrent_peer_allocator_interface& m_peer_allocator; // the number of seeds in the torrent_peer list boost::uint32_t m_num_seeds:31; // this was the state of the torrent the // last time we recalculated the number of // connect candidates. Since seeds (or upload // only) peers are not connect candidates // when we're finished, the set depends on // this state. Every time m_torrent->is_finished() // is different from this state, we need to // recalculate the connect candidates. boost::uint32_t m_finished:1; // since the torrent_peer list can grow too large // to scan all of it, start at this index int m_round_robin; // a list of good connect candidates std::vector m_candidate_cache; // The number of peers in our torrent_peer list // that are connect candidates. i.e. they're // not already connected and they have not // yet reached their max try count and they // have the connectable state (we have a listen // port for them). int m_num_connect_candidates; // if a peer has failed this many times or more, we don't consider // it a connect candidate anymore. int m_max_failcount; }; } #endif // TORRENT_POLICY_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/peer_request.hpp000066400000000000000000000042761351156116000247770ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 { // represents a byte range within a piece. Internally this is // is used for incoming piece requests. struct TORRENT_EXPORT peer_request { // the index of the piece in which the range starts. int piece; // the offset within that piece where the range starts. int start; // the size of the range, in bytes. int length; // returns true if the right hand side peer_request refers to the same // range as this does. bool operator==(peer_request const& r) const { return piece == r.piece && start == r.start && length == r.length; } }; } #endif // TORRENT_PEER_REQUEST_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/performance_counters.hpp000066400000000000000000000313261351156116000265130ustar00rootroot00000000000000/* Copyright (c) 2013-2018, 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_PERFORMANCE_COUNTERS_HPP_INCLUDED #define TORRENT_PERFORMANCE_COUNTERS_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct TORRENT_EXTRA_EXPORT counters { enum stats_counter_t { // the number of peers that were disconnected this // tick due to protocol error error_peers, disconnected_peers, eof_peers, connreset_peers, connrefused_peers, connaborted_peers, notconnected_peers, perm_peers, buffer_peers, unreachable_peers, broken_pipe_peers, addrinuse_peers, no_access_peers, invalid_arg_peers, aborted_peers, piece_requests, max_piece_requests, invalid_piece_requests, choked_piece_requests, cancelled_piece_requests, piece_rejects, error_incoming_peers, error_outgoing_peers, error_rc4_peers, error_encrypted_peers, error_tcp_peers, error_utp_peers, // the number of times the piece picker was // successfully invoked, split by the reason // it was invoked reject_piece_picks, unchoke_piece_picks, incoming_redundant_piece_picks, incoming_piece_picks, end_game_piece_picks, snubbed_piece_picks, interesting_piece_picks, hash_fail_piece_picks, // these counters indicate which parts // of the piece picker CPU is spent in piece_picker_partial_loops, piece_picker_suggest_loops, piece_picker_sequential_loops, piece_picker_reverse_rare_loops, piece_picker_rare_loops, piece_picker_rand_start_loops, piece_picker_rand_loops, piece_picker_busy_loops, // reasons to disconnect peers connect_timeouts, uninteresting_peers, timeout_peers, no_memory_peers, too_many_peers, transport_timeout_peers, num_banned_peers, banned_for_hash_failure, // connection attempts (not necessarily successful) connection_attempts, // the number of iterations over the peer list when finding // a connect candidate connection_attempt_loops, // successful incoming connections (not rejected for any reason) incoming_connections, // counts events where the network // thread wakes up on_read_counter, on_write_counter, on_tick_counter, on_lsd_counter, on_lsd_peer_counter, on_udp_counter, on_accept_counter, on_disk_queue_counter, on_disk_counter, torrent_evicted_counter, // bittorrent message counters // TODO: should keepalives be in here too? // how about dont-have, share-mode, upload-only num_incoming_choke, num_incoming_unchoke, num_incoming_interested, num_incoming_not_interested, num_incoming_have, num_incoming_bitfield, num_incoming_request, num_incoming_piece, num_incoming_cancel, num_incoming_dht_port, num_incoming_suggest, num_incoming_have_all, num_incoming_have_none, num_incoming_reject, num_incoming_allowed_fast, num_incoming_ext_handshake, num_incoming_pex, num_incoming_metadata, num_incoming_extended, num_outgoing_choke, num_outgoing_unchoke, num_outgoing_interested, num_outgoing_not_interested, num_outgoing_have, num_outgoing_bitfield, num_outgoing_request, num_outgoing_piece, num_outgoing_cancel, num_outgoing_dht_port, num_outgoing_suggest, num_outgoing_have_all, num_outgoing_have_none, num_outgoing_reject, num_outgoing_allowed_fast, num_outgoing_ext_handshake, num_outgoing_pex, num_outgoing_metadata, num_outgoing_extended, num_piece_passed, num_piece_failed, num_have_pieces, num_total_pieces_added, num_blocks_written, num_blocks_read, num_blocks_hashed, num_blocks_cache_hits, num_write_ops, num_read_ops, num_read_back, disk_read_time, disk_write_time, disk_hash_time, disk_job_time, waste_piece_timed_out, waste_piece_cancelled, waste_piece_unknown, waste_piece_seed, waste_piece_end_game, waste_piece_closing, sent_payload_bytes, sent_bytes, sent_ip_overhead_bytes, sent_tracker_bytes, recv_payload_bytes, recv_bytes, recv_ip_overhead_bytes, recv_tracker_bytes, recv_failed_bytes, recv_redundant_bytes, dht_messages_in, dht_messages_out, dht_messages_out_dropped, dht_bytes_in, dht_bytes_out, dht_ping_in, dht_ping_out, dht_find_node_in, dht_find_node_out, dht_get_peers_in, dht_get_peers_out, dht_announce_peer_in, dht_announce_peer_out, dht_get_in, dht_get_out, dht_put_in, dht_put_out, dht_invalid_announce, dht_invalid_get_peers, dht_invalid_put, dht_invalid_get, // uTP counters. utp_packet_loss, utp_timeout, utp_packets_in, utp_packets_out, utp_fast_retransmit, utp_packet_resend, utp_samples_above_target, utp_samples_below_target, utp_payload_pkts_in, utp_payload_pkts_out, utp_invalid_pkts_in, utp_redundant_pkts_in, // the buffer sizes accepted by // socket send calls. The larger // the more efficient. The size is // 1 << n, where n is the number // at the end of the counter name // 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, // 16384, 32768, 65536, 131072, 262144, 524288, 1048576 socket_send_size3, socket_send_size4, socket_send_size5, socket_send_size6, socket_send_size7, socket_send_size8, socket_send_size9, socket_send_size10, socket_send_size11, socket_send_size12, socket_send_size13, socket_send_size14, socket_send_size15, socket_send_size16, socket_send_size17, socket_send_size18, socket_send_size19, socket_send_size20, // the buffer sizes returned by // socket recv calls. The larger // the more efficient. The size is // 1 << n, where n is the number // at the end of the counter name // 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, // 16384, 32768, 65536, 131072, 262144, 524288, 1048576 socket_recv_size3, socket_recv_size4, socket_recv_size5, socket_recv_size6, socket_recv_size7, socket_recv_size8, socket_recv_size9, socket_recv_size10, socket_recv_size11, socket_recv_size12, socket_recv_size13, socket_recv_size14, socket_recv_size15, socket_recv_size16, socket_recv_size17, socket_recv_size18, socket_recv_size19, socket_recv_size20, num_stats_counters }; // == ALL FOLLOWING ARE GAUGES == // it is important that all gauges have a higher index than counters. // This assumption is relied upon in other parts of the code enum stats_gauge_t { num_checking_torrents = num_stats_counters, num_stopped_torrents, num_upload_only_torrents, // upload_only means finished num_downloading_torrents, num_seeding_torrents, num_queued_seeding_torrents, num_queued_download_torrents, num_error_torrents, // the number of torrents that don't have the // IP filter applied to them. non_filter_torrents, // counters related to evicting torrents num_loaded_torrents, num_pinned_torrents, // these counter indices deliberately // match the order of socket type IDs // defined in socket_type.hpp. num_tcp_peers, num_socks5_peers, num_http_proxy_peers, num_utp_peers, num_i2p_peers, num_ssl_peers, num_ssl_socks5_peers, num_ssl_http_proxy_peers, num_ssl_utp_peers, // the number of peer connections that are half-open (i.e. in the // process of completing a connection attempt) and fully connected. // These states are mutually exclusive (a connection cannot be in both // states simultaneously). num_peers_half_open, num_peers_connected, // the number of peers interested in us (``up_interested``) and peers // we are interested in (``down_interested``). num_peers_up_interested, num_peers_down_interested, // the total number of unchoked peers (``up_unchoked_all``), the number // of peers unchoked via the optimistic unchoke // (``up_unchoked_optimistic``) and peers unchoked via the // reciprocation (regular) unchoke mechanism (``up_unchoked``). // and the number of peers that have unchoked us (``down_unchoked). num_peers_up_unchoked_all, num_peers_up_unchoked_optimistic, num_peers_up_unchoked, num_peers_down_unchoked, // the number of peers with at least one piece request pending, // downloading (``down_requests``) or uploading (``up_requests``) num_peers_up_requests, num_peers_down_requests, // the number of peers that have at least one outstanding disk request, // either reading (``up_disk``) or writing (``down_disk``). num_peers_up_disk, num_peers_down_disk, // the number of peers in end-game mode. End game mode is where there // are no blocks that we have not sent any requests to download. In ths // mode, blocks are allowed to be requested from more than one peer at // at time. num_peers_end_game, write_cache_blocks, read_cache_blocks, request_latency, pinned_blocks, disk_blocks_in_use, queued_disk_jobs, num_running_disk_jobs, num_read_jobs, num_write_jobs, num_jobs, num_writing_threads, num_running_threads, blocked_disk_jobs, queued_write_bytes, num_unchoke_slots, num_fenced_read, num_fenced_write, num_fenced_hash, num_fenced_move_storage, num_fenced_release_files, num_fenced_delete_files, num_fenced_check_fastresume, num_fenced_save_resume_data, num_fenced_rename_file, num_fenced_stop_torrent, num_fenced_cache_piece, num_fenced_flush_piece, num_fenced_flush_hashed, num_fenced_flush_storage, num_fenced_trim_cache, num_fenced_file_priority, num_fenced_load_torrent, num_fenced_clear_piece, num_fenced_tick_storage, arc_mru_size, arc_mru_ghost_size, arc_mfu_size, arc_mfu_ghost_size, arc_write_size, arc_volatile_size, dht_nodes, dht_node_cache, dht_torrents, dht_peers, dht_immutable_data, dht_mutable_data, dht_allocated_observers, has_incoming_connections, limiter_up_queue, limiter_down_queue, limiter_up_bytes, limiter_down_bytes, // the number of uTP connections in each respective state // these must be defined in the same order as the state_t enum // in utp_stream num_utp_idle, num_utp_syn_sent, num_utp_connected, num_utp_fin_sent, num_utp_close_wait, num_utp_deleted, num_counters, num_gauges_counters = num_counters - num_stats_counters }; counters(); counters(counters const&); counters& operator=(counters const&); // returns the new value boost::int64_t inc_stats_counter(int c, boost::int64_t value = 1); boost::int64_t operator[](int i) const; void set_value(int c, boost::int64_t value); void blend_stats_counter(int c, boost::int64_t value, int ratio); private: // TODO: some space could be saved here by making gauges 32 bits // TODO: restore these to regular integers. Instead have one copy // of the counters per thread and collect them at convenient // synchronization points #if BOOST_ATOMIC_LLONG_LOCK_FREE == 2 boost::atomic m_stats_counter[num_counters]; #else // if the atomic type is't lock-free, use a single lock instead, for // the whole array mutable mutex m_mutex; boost::int64_t m_stats_counter[num_counters]; #endif }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/piece_block_progress.hpp000066400000000000000000000040451351156116000264510ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 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 libtorrent-rasterbar-1.1.13/include/libtorrent/piece_picker.hpp000066400000000000000000000702721351156116000247150ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 // heavy weight reference counting invariant checks //#define TORRENT_DEBUG_REFCOUNTS #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/time.hpp" namespace libtorrent { class torrent; class peer_connection; struct bitfield; struct counters; struct torrent_peer; struct TORRENT_EXTRA_EXPORT piece_block { static const piece_block invalid; 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_EXTRA_EXPORT piece_picker { public: enum { // the number of priority levels priority_levels = 8, // priority factor prio_factor = 3 }; struct block_info { block_info(): peer(0), num_peers(0), state(state_none) {} // the peer this block was requested or // downloaded from. torrent_peer* peer; // the number of peers that has this block in their // download or request queues unsigned num_peers:14; // the state of this block enum { state_none, state_requested, state_writing, state_finished }; unsigned state:2; #if TORRENT_USE_ASSERTS // to allow verifying the invariant of blocks belonging to the right piece int piece_index; std::set peers; #endif }; enum options_t { // pick rarest first rarest_first = 1, // pick the most common first, or the last pieces if sequential reverse = 2, // only pick pieces exclusively requested from this peer on_parole = 4, // always pick partial pieces before any other piece prioritize_partials = 8, // pick pieces in sequential order sequential = 16, // treat pieces with priority 6 and below as filtered // to trigger end-game mode until all prio 7 pieces are // completed time_critical_mode = 32, // only expands pieces (when prefer contiguous blocks is set) // within properly aligned ranges, not the largest possible // range of pieces. align_expanded_pieces = 64 }; struct downloading_piece { downloading_piece() : index((std::numeric_limits::max)()) , info_idx((std::numeric_limits::max)()) , finished(0) , passed_hash_check(0) , writing(0) , locked(0) , requested(0) , outstanding_hash_check(0) {} bool operator<(downloading_piece const& rhs) const { return index < rhs.index; } // the index of the piece boost::uint32_t index; // info about each block in this piece. this is an index into the // m_block_info array, when multiplied by m_blocks_per_piece. // The m_blocks_per_piece following entries contain information about // all blocks in this piece. boost::uint16_t info_idx; // the number of blocks in the finished state boost::uint16_t finished:15; // set to true when the hash check job // returns with a valid hash for this piece. // we might not 'have' the piece yet though, // since it might not have been written to // disk. This is not set of locked is // set. boost::uint16_t passed_hash_check:1; // the number of blocks in the writing state boost::uint16_t writing:15; // when this is set, blocks from this piece may // not be picked. This is used when the hash check // fails or writing to the disk fails, while waiting // to synchronize the disk thread and clear out any // remaining state. Once this synchronization is // done, restore_piece() is called to clear the // locked flag. boost::uint16_t locked:1; // the number of blocks in the requested state boost::uint16_t requested:15; // set to true while there is an outstanding // hash check for this piece boost::uint16_t outstanding_hash_check:1; }; piece_picker(); void get_availability(std::vector& avail) const; int get_availability(int piece) const; // increases the peer count for the given piece // (is used when a HAVE message is received) void inc_refcount(int index, const torrent_peer* peer); void dec_refcount(int index, const torrent_peer* peer); // increases the peer count for the given piece // (is used when a BITFIELD message is received) void inc_refcount(bitfield const& bitmask, const torrent_peer* peer); // decreases the peer count for the given piece // (used when a peer disconnects) void dec_refcount(bitfield const& bitmask, const torrent_peer* peer); // these will increase and decrease the peer count // of all pieces. They are used when seeds join // or leave the swarm. void inc_refcount_all(const torrent_peer* peer); void dec_refcount_all(const torrent_peer* peer); // 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); void we_dont_have(int index); // the lowest piece index we do not have int cursor() const { return m_cursor; } // one past the last piece we do not have. int reverse_cursor() const { return m_reverse_cursor; } // sets all pieces to dont-have void init(int blocks_per_piece, int blocks_in_last_piece, int total_num_pieces); int num_pieces() const { return int(m_piece_map.size()); } bool have_piece(int index) const; bool is_downloading(int index) const { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); piece_pos const& p = m_piece_map[index]; return p.downloading(); } // sets the priority of a piece. // returns true if the priority was changed from 0 to non-0 // or vice versa bool set_piece_priority(int index, int prio); // returns the priority for the piece at 'index' int piece_priority(int index) const; // returns the current piece priorities for all pieces void piece_priorities(std::vector& pieces) const; // ========== start deprecation ============== // fills the bitmask with 1's for pieces that are filtered void filtered_pieces(std::vector& mask) const; // ========== end deprecation ============== // 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 torrent_peer pointer for the peer that // we'll download from. // prefer_contiguous_blocks indicates how many blocks we would like // to request contiguously. The blocks are not merged by the piece // picker, but may be coalesced later by the peer_connection. // this feature is used by web_peer_connection to request larger blocks // at a time to mitigate limited pipelining and lack of keep-alive // (i.e. higher overhead per request). boost::uint32_t pick_pieces(bitfield const& pieces , std::vector& interesting_blocks, int num_blocks , int prefer_contiguous_blocks, torrent_peer* peer , int options, std::vector const& suggested_pieces , int num_peers , counters& pc ) const; // picks blocks from each of the pieces in the piece_list // vector that is also in the piece bitmask. The blocks // are added to interesting_blocks, and busy blocks are // added to backup_blocks. num blocks is the number of // blocks to be picked. Blocks are not picked from pieces // that are being downloaded int add_blocks(int piece, bitfield const& pieces , std::vector& interesting_blocks , std::vector& backup_blocks , std::vector& backup_blocks2 , int num_blocks, int prefer_contiguous_blocks , torrent_peer* peer, std::vector const& ignore , int options) const; // picks blocks only from downloading pieces int add_blocks_downloading(downloading_piece const& dp , bitfield const& pieces , std::vector& interesting_blocks , std::vector& backup_blocks , std::vector& backup_blocks2 , int num_blocks, int prefer_contiguous_blocks , torrent_peer* peer , int options) const; // clears the peer pointer in all downloading pieces with this // peer pointer void clear_peer(torrent_peer* peer); #if TORRENT_USE_INVARIANT_CHECKS // this is an invariant check void check_peers(); #endif // int get_block_state(piece_block block) 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_requested(piece_block block) const; // returns true if the block has been downloaded bool is_downloaded(piece_block block) const; // returns true if the block has been downloaded and written to disk bool is_finished(piece_block block) const; // marks this piece-block as queued for downloading // options are flags from options_t. bool mark_as_downloading(piece_block block, torrent_peer* peer , int options = 0); // returns true if the block was marked as writing, // and false if the block is already finished or writing bool mark_as_writing(piece_block block, torrent_peer* peer); void mark_as_canceled(piece_block block, torrent_peer* peer); void mark_as_finished(piece_block block, torrent_peer* peer); void mark_as_pad(piece_block block); // prevent blocks from being picked from this piece. // to unlock the piece, call restore_piece() on it void lock_piece(int piece); void write_failed(piece_block block); int num_peers(piece_block block) const; void piece_passed(int index); // void mark_as_checking(int index); // void mark_as_done_checking(int index); // returns information about the given piece void piece_info(int index, piece_picker::downloading_piece& st) const; struct piece_stats_t { int peer_count; int priority; bool have; bool downloading; }; piece_stats_t piece_stats(int index) const; // 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, torrent_peer* peer = 0); // returns true if all blocks in this piece are finished // or if we have the piece bool is_piece_finished(int index) const; // returns true if we have the piece or if the piece // has passed the hash check bool has_piece_passed(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; // return the peer pointers to all peers that participated in // this piece void get_downloaders(std::vector& d, int index) const; std::vector get_download_queue() const; int get_download_queue_size() const; void get_download_queue_sizes(int* partial , int* full, int* finished, int* zero_prio) const; torrent_peer* 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; } // number of pieces whose hash has passed _and_ they have // been successfully flushed to disk. Including pieces we have // also filtered with priority 0 but have anyway. int num_have() const { return m_num_have; } // number of pieces whose hash has passed (but haven't necessarily // been flushed to disk yet) int num_passed() const { return m_num_passed; } // return true if we have all the pieces we wanted bool is_finished() const { return m_num_have - m_num_have_filtered == int(m_piece_map.size()) - m_num_filtered; } bool is_seeding() const { return m_num_have == int(m_piece_map.size()); } // the number of pieces we want and don't have int num_want_left() const { return num_pieces() - m_num_have - m_num_filtered + m_num_have_filtered; } #if TORRENT_USE_INVARIANT_CHECKS void check_piece_state() const; // used in debug mode void verify_priority(int start, int end, int prio) const; void verify_pick(std::vector const& picked , bitfield const& bits) const; void check_peer_invariant(bitfield const& have, torrent_peer const* p) const; void check_invariant(const torrent* t = 0) const; #endif // functor that compares indices on downloading_pieces struct has_index { has_index(int i): index(boost::uint32_t(i)) { TORRENT_ASSERT(i >= 0); } bool operator()(const downloading_piece& p) const { return p.index == index; } boost::uint32_t index; }; int blocks_in_last_piece() const { return m_blocks_in_last_piece; } std::pair distributed_copies() const; void set_num_pad_files(int n) { m_num_pad_files = n; } // return the array of block_info objects for a given downloading_piece. // this array has m_blocks_per_piece elements in it block_info* blocks_for_piece(downloading_piece const& dp); block_info const* blocks_for_piece(downloading_piece const& dp) const; private: friend struct piece_pos; boost::tuple requested_from( piece_picker::downloading_piece const& p , int num_blocks_in_piece, torrent_peer* peer) const; bool can_pick(int piece, bitfield const& bitmask) const; bool is_piece_free(int piece, bitfield const& bitmask) const; std::pair expand_piece(int piece, int whole_pieces , bitfield const& have, int options) const; // only defined when TORRENT_PICKER_LOG is defined, used for debugging // unit tests void print_pieces() const; struct piece_pos { piece_pos() {} piece_pos(int peer_count_, int index_) : peer_count(unsigned(peer_count_)) , download_state(piece_pos::piece_open) , piece_priority(4) , index(unsigned(index_)) { TORRENT_ASSERT(peer_count_ >= 0); TORRENT_ASSERT(index_ >= 0); } // download_state of this piece. enum state_t { // the piece is partially downloaded or requested piece_downloading, // partial pieces where all blocks in the piece have been requested piece_full, // partial pieces where all blocks in the piece have been received // and are either finished or writing piece_finished, // partial pieces whose priority is 0 piece_zero_prio, // the states up to this point indicate the piece is being // downloaded (or at least has a partially downloaded piece // in one of the m_downloads buckets). num_download_categories, // the piece is open to be picked piece_open = num_download_categories, // this is not a new download category/download list bucket. // it still goes into the piece_downloading bucket. However, // it indicates that this piece only has outstanding requests // from reverse peers. This is to de-prioritize it somewhat piece_downloading_reverse, piece_full_reverse }; // returns one of the valid download categories of state_t or // piece_open if this piece is not being downloaded int download_queue() const { if (download_state == piece_downloading_reverse) return piece_downloading; if (download_state == piece_full_reverse) return piece_full; return download_state; } bool reverse() const { return download_state == piece_downloading_reverse || download_state == piece_full_reverse; } void unreverse() { switch (download_state) { case piece_downloading_reverse: download_state = piece_downloading; break; case piece_full_reverse: download_state = piece_full; break; } } void make_reverse() { switch (download_state) { case piece_downloading: download_state = piece_downloading_reverse; break; case piece_full: download_state = piece_full_reverse; break; } } // the number of peers that has this piece // (availability) #ifdef TORRENT_OPTIMIZE_MEMORY_USAGE boost::uint32_t peer_count : 9; #else boost::uint32_t peer_count : 16; #endif // one of the enums from state_t. This indicates whether this piece // is currently being downloaded or not, and what state it's in if // it is. Specifically, as an optimization, pieces that have all blocks // requested from them are separated out into separate lists to make // lookups quicker. The main oddity is that whether a downloading piece // has only been requested from peers that are reverse, that's // recorded as piece_downloading_reverse, which really means the same // as piece_downloading, it just saves space to also indicate that it // has a bit lower priority. The reverse bit is only relevant if the // state is piece_downloading. boost::uint32_t download_state : 3; // TODO: 2 having 8 priority levels is probably excessive. It should // probably be changed to 3 levels + dont-download // is 0 if the piece is filtered (not to be downloaded) // 1 is low priority // 2 is low priority // 3 is mid priority // 4 is default priority // 5 is mid priority // 6 is high priority // 7 is high priority boost::uint32_t piece_priority : 3; // index in to the piece_info vector #ifdef TORRENT_OPTIMIZE_MEMORY_USAGE boost::uint32_t index : 17; #else boost::uint32_t index; #endif #ifdef TORRENT_DEBUG_REFCOUNTS // all the peers that have this piece std::set have_peers; #endif enum { // index is set to this to indicate that we have the // piece. There is no entry for the piece in the // buckets if this is the case. #ifdef TORRENT_OPTIMIZE_MEMORY_USAGE we_have_index = 0x3ffff, #else we_have_index = 0xffffffff, #endif // the priority value that means the piece is filtered filter_priority = 0, // the max number the peer count can hold #ifdef TORRENT_OPTIMIZE_MEMORY_USAGE max_peer_count = 0x1ff #else max_peer_count = 0xffff #endif }; bool have() const { return index == we_have_index; } void set_have() { index = we_have_index; TORRENT_ASSERT(have()); } void set_not_have() { index = 0; TORRENT_ASSERT(!have()); } bool downloading() const { return download_state != piece_open; } bool filtered() const { return piece_priority == filter_priority; } // this function returns the effective priority of the piece. It's // actually the sort order of this piece compared to other pieces. A // lower index means it will be picked before a piece with a higher // index. // The availability of the piece (the number of peers that have this // piece) is fundamentally controlling the priority. It's multiplied // by 3 to form 3 levels of priority for each availability. // // downloading pieces (not reverse) // | open pieces (not downloading) // | | downloading pieces (reverse peers) // | | | // +---+---+---+ // | 0 | 1 | 2 | // +---+---+---+ // this '3' is called prio_factor // // the manually set priority takes precedence over the availability // by multiplying availability by priority. int priority(piece_picker const* picker) const { // filtered pieces (prio = 0), pieces we have or pieces with // availability = 0 should not be present in the piece list // returning -1 indicates that they shouldn't. if (filtered() || have() || peer_count + picker->m_seeds == 0 || download_state == piece_full || download_state == piece_finished) return -1; TORRENT_ASSERT(piece_priority > 0); // this is to keep downloading pieces at higher priority than // pieces that are not being downloaded, and to make reverse // downloading pieces to be lower priority int adjustment = -2; if (reverse()) adjustment = -1; else if (download_state != piece_open) adjustment = -3; // the + 1 here is because peer_count count be 0, it m_seeds // is > 0. We don't actually care about seeds (except for the // first one) since the order of the pieces is unaffected. int availability = int(peer_count) + 1; TORRENT_ASSERT(availability > 0); TORRENT_ASSERT(int(priority_levels - piece_priority) > 0); return availability * int(priority_levels - piece_priority) * prio_factor + adjustment; } 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; } }; #ifndef TORRENT_DEBUG_REFCOUNTS #ifdef TORRENT_OPTIMIZE_MEMORY_USAGE BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 4); #else BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 8); #endif #endif bool partial_compare_rarest_first(downloading_piece const* lhs , downloading_piece const* rhs) const; void break_one_seed(); void update_pieces() const; // fills in the range [start, end) of pieces in // m_pieces that have priority 'prio' void priority_range(int prio, int* start, int* end); // adds the piece 'index' to m_pieces void add(int index); // removes the piece with the given priority and the // elem_index in the m_pieces vector void remove(int priority, int elem_index); // updates the position of the piece with the given // priority and the elem_index in the m_pieces vector void update(int priority, int elem_index); // shuffles the given piece inside it's priority range void shuffle(int priority, int elem_index); typedef std::vector::iterator dlpiece_iter; dlpiece_iter add_download_piece(int index); void erase_download_piece(dlpiece_iter i); std::vector::const_iterator find_dl_piece(int queue, int index) const; std::vector::iterator find_dl_piece(int queue, int index); // returns an iterator to the downloading piece, whichever // download list it may live in now std::vector::iterator update_piece_state( std::vector::iterator dp); private: // the following vectors are mutable because they sometimes may // be updated lazily, triggered by const functions // 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 // TODO: should this be allocated lazily? mutable std::vector m_piece_map; // this maps pieces to a range of blocks that are pad files and should not // be picked // TOOD: this could be a much more efficient data structure std::set m_pad_blocks; // the number of seeds. These are not added to // the availability counters of the pieces int m_seeds; // the number of pieces that have passed the hash check int m_num_passed; // this vector contains all piece indices that are pickable // sorted by priority. Pieces are in random random order // among pieces with the same priority mutable std::vector m_pieces; // these are indices to the priority boundries inside // the m_pieces vector. priority 0 always start at // 0, priority 1 starts at m_priority_boundries[0] etc. mutable std::vector m_priority_boundries; // each piece that's currently being downloaded has an entry in this list // with block allocations. i.e. it says which parts of the piece that is // being downloaded. This list is ordered by piece index to make lookups // efficient there are as many buckets as there are piece states. See // piece_pos::state_t. The only download state that does not have a // corresponding downloading_piece vector is piece_open and // piece_downloading_reverse (the latter uses the same as // piece_downloading). std::vector m_downloads[piece_pos::num_download_categories]; // this holds the information of the blocks in partially downloaded // pieces. the downloading_piece::info index point into this vector for // its storage std::vector m_block_info; // these are block ranges in m_block_info that are free. The numbers // in here, when multiplied by m_blocks_per_piece is the index to the // first block in the range that's free to use by a new downloading_piece. // this is a free-list. std::vector m_free_block_infos; boost::uint16_t m_blocks_per_piece; boost::uint16_t 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; // we have all pieces in the range [0, m_cursor) // m_cursor is the first piece we don't have int m_cursor; // we have all pieces in the range [m_reverse_cursor, end) // m_reverse_cursor is the first piece where we also have // all the subsequent pieces int m_reverse_cursor; // the number of pieces we have (i.e. passed + flushed). // This includes pieces that we have filtered but still have int m_num_have; // this is the number of partial download pieces // that may be caused by pad files. We raise the limit // of number of partial pieces by this amount, to not // prioritize pieces that intersect pad files for no // apparent reason int m_num_pad_files; // if this is set to true, it means update_pieces() // has to be called before accessing m_pieces. mutable bool m_dirty; public: #ifdef TORRENT_OPTIMIZE_MEMORY_USAGE enum { max_pieces = piece_pos::we_have_index - 1 }; #else enum { max_pieces = INT_MAX - 1 }; #endif }; } #endif // TORRENT_PIECE_PICKER_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/platform_util.hpp000066400000000000000000000003411351156116000251420ustar00rootroot00000000000000#ifndef TORRENT_PLATFORM_UTIL_HPP #define TORRENT_PLATFORM_UTIL_HPP #include namespace libtorrent { int max_open_files(); boost::uint64_t total_physical_ram(); } #endif // TORRENT_PLATFORM_UTIL_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/proxy_base.hpp000066400000000000000000000157011351156116000244420ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_PROXY_BASE_HPP_INCLUDED #define TORRENT_PROXY_BASE_HPP_INCLUDED #include "libtorrent/io.hpp" #include "libtorrent/io_service_fwd.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/address.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { class proxy_base : boost::noncopyable { public: typedef boost::function handler_type; typedef tcp::socket next_layer_type; typedef tcp::socket::lowest_layer_type lowest_layer_type; typedef tcp::socket::endpoint_type endpoint_type; typedef tcp::socket::protocol_type protocol_type; explicit proxy_base(io_service& io_service); ~proxy_base(); void set_proxy(std::string hostname, int port) { m_hostname = hostname; m_port = port; } #if BOOST_VERSION >= 106600 typedef tcp::socket::executor_type executor_type; executor_type get_executor() { return m_sock.get_executor(); } #endif template void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) { m_sock.async_read_some(buffers, handler); } template std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) { return m_sock.read_some(buffers, ec); } template std::size_t write_some(Const_Buffers const& buffers, error_code& ec) { return m_sock.write_some(buffers, ec); } std::size_t available(error_code& ec) const { return m_sock.available(ec); } #ifndef BOOST_NO_EXCEPTIONS std::size_t available() const { return m_sock.available(); } template std::size_t read_some(Mutable_Buffers const& buffers) { return m_sock.read_some(buffers); } template std::size_t write_some(Const_Buffers const& buffers) { return m_sock.write_some(buffers); } template void io_control(IO_Control_Command& ioc) { m_sock.io_control(ioc); } #endif template void io_control(IO_Control_Command& ioc, error_code& ec) { m_sock.io_control(ioc, ec); } template void async_write_some(Const_Buffers const& buffers, Handler const& handler) { m_sock.async_write_some(buffers, handler); } #ifndef BOOST_NO_EXCEPTIONS void non_blocking(bool b) { m_sock.non_blocking(b); } #endif error_code non_blocking(bool b, error_code& ec) { return m_sock.non_blocking(b, ec); } #ifndef BOOST_NO_EXCEPTIONS template void set_option(SettableSocketOption const& opt) { m_sock.set_option(opt); } #endif template error_code set_option(SettableSocketOption const& opt, error_code& ec) { return m_sock.set_option(opt, ec); } #ifndef BOOST_NO_EXCEPTIONS template void get_option(GettableSocketOption& opt) { m_sock.get_option(opt); } #endif template error_code get_option(GettableSocketOption& opt, error_code& ec) { return m_sock.get_option(opt, ec); } #ifndef BOOST_NO_EXCEPTIONS void bind(endpoint_type const& /* endpoint */) { // m_sock.bind(endpoint); } #endif error_code cancel(error_code& ec) { return m_sock.cancel(ec); } void bind(endpoint_type const& /* endpoint */, error_code& /* ec */) { // the reason why we ignore binds here is because we don't // (necessarily) yet know what address family the proxy // will resolve to, and binding to the wrong one would // break our connection attempt later. The caller here // doesn't necessarily know that we're proxying, so this // bind address is based on the final endpoint, not the // proxy. // TODO: it would be nice to remember the bind port and bind once we know where the proxy is // m_sock.bind(endpoint, ec); } #ifndef BOOST_NO_EXCEPTIONS void open(protocol_type const&) { // m_sock.open(p); } #endif void open(protocol_type const&, error_code&) { // we need to ignore this for the same reason as stated // for ignoring bind() // m_sock.open(p, ec); } #ifndef BOOST_NO_EXCEPTIONS void close() { m_remote_endpoint = endpoint_type(); m_sock.close(); m_resolver.cancel(); } #endif void close(error_code& ec) { m_remote_endpoint = endpoint_type(); m_sock.close(ec); m_resolver.cancel(); } #ifndef BOOST_NO_EXCEPTIONS endpoint_type remote_endpoint() const { return m_remote_endpoint; } #endif endpoint_type remote_endpoint(error_code& ec) const { if (!m_sock.is_open()) ec = boost::asio::error::not_connected; return m_remote_endpoint; } #ifndef BOOST_NO_EXCEPTIONS endpoint_type local_endpoint() const { return m_sock.local_endpoint(); } #endif endpoint_type local_endpoint(error_code& ec) const { return m_sock.local_endpoint(ec); } io_service& get_io_service() { return lt::get_io_service(m_sock); } lowest_layer_type& lowest_layer() { return m_sock.lowest_layer(); } next_layer_type& next_layer() { return m_sock; } bool is_open() const { return m_sock.is_open(); } protected: bool handle_error(error_code const& e, boost::shared_ptr const& h); tcp::socket m_sock; std::string m_hostname; // proxy host int m_port; // proxy port endpoint_type m_remote_endpoint; // TODO: 2 use the resolver interface that has a built-in cache tcp::resolver m_resolver; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/puff.hpp000066400000000000000000000024601351156116000232250ustar00rootroot00000000000000/* puff.h Copyright (C) 2002, 2003 Mark Adler, all rights reserved version 1.7, 3 Mar 2002 This software is provided 'as-is', without any express or implied warranty. In no event will the author 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. Mark Adler madler@alumni.caltech.edu */ /* * See puff.c for purpose and usage. */ int puff(unsigned char *dest, /* pointer to destination pointer */ unsigned long *destlen, /* amount of output space */ const unsigned char *source, /* pointer to source data pointer */ unsigned long *sourcelen); /* amount of input available */ libtorrent-rasterbar-1.1.13/include/libtorrent/random.hpp000066400000000000000000000032621351156116000235460ustar00rootroot00000000000000/* Copyright (c) 2011-2018, 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/config.hpp" #include namespace libtorrent { boost::uint32_t TORRENT_EXTRA_EXPORT random(); boost::uint32_t TORRENT_EXTRA_EXPORT randint(int i); } libtorrent-rasterbar-1.1.13/include/libtorrent/receive_buffer.hpp000066400000000000000000000224601351156116000252420ustar00rootroot00000000000000/* Copyright (c) 2014-2018, Arvid Norberg, Steven Siloti 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_RECEIVE_BUFFER_HPP_INCLUDED #define TORRENT_RECEIVE_BUFFER_HPP_INCLUDED #include #include #include #include namespace libtorrent { struct TORRENT_EXTRA_EXPORT receive_buffer { friend struct crypto_receive_buffer; receive_buffer(buffer_allocator_interface& allocator) : m_recv_start(0) , m_recv_end(0) , m_recv_pos(0) , m_packet_size(0) , m_soft_packet_size(0) , m_disk_recv_buffer_size(0) , m_disk_recv_buffer(allocator, 0) {} int packet_size() const { return m_packet_size; } int packet_bytes_remaining() const { TORRENT_ASSERT(m_recv_start == 0); TORRENT_ASSERT(m_packet_size > 0); return m_packet_size - m_recv_pos; } int max_receive(); bool packet_finished() const { return m_packet_size <= m_recv_pos; } int pos() const { return m_recv_pos; } int capacity() const { return m_recv_buffer.capacity() + m_disk_recv_buffer_size; } int regular_buffer_size() const { TORRENT_ASSERT(m_packet_size > 0); return m_packet_size - m_disk_recv_buffer_size; } // regular buffer only boost::asio::mutable_buffer reserve(int size); // with possible disk buffer usage int reserve(boost::array& vec, int size); // tell the buffer we just receved more bytes at the end of it. This will // advance the end cursor void received(int bytes_transferred) { TORRENT_ASSERT(m_packet_size > 0); m_recv_end += bytes_transferred; TORRENT_ASSERT(m_recv_pos <= int(m_recv_buffer.size() + m_disk_recv_buffer_size)); } // tell the buffer we consumed some bytes of it. This will advance the read // cursor int advance_pos(int bytes); // has the read cursor reached the end cursor? bool pos_at_end() { return m_recv_pos == m_recv_end; } // make the buffer size dividible by 8 bytes (RC4 block size) void clamp_size(); void set_soft_packet_size(int size) { m_soft_packet_size = size; } // size = the packet size to remove from the receive buffer // packet_size = the next packet size to receive in the buffer // offset = the offset into the receive buffer where to remove `size` bytes void cut(int size, int packet_size, int offset = 0); // return the interval between the start of the buffer to the read cursor. // This is the "current" packet. buffer::const_interval get() const; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // returns the entire regular buffer // should only be used during the handshake buffer::interval mutable_buffer(); // returns the last 'bytes' from the receive buffer void mutable_buffers(std::vector& vec, int bytes); #endif void free_disk_buffer() { m_disk_recv_buffer.reset(); m_disk_recv_buffer_size = 0; } bool has_disk_buffer() const { return m_disk_recv_buffer; } void assert_no_disk_buffer() const { TORRENT_ASSERT(!m_disk_recv_buffer); TORRENT_ASSERT(m_disk_recv_buffer_size == 0); } void assign_disk_buffer(char* buffer, int size); char* release_disk_buffer(); // the purpose of this function is to free up and cut off all messages // in the receive buffer that have been parsed and processed. void normalize(); bool normalized() const { return m_recv_start == 0; } void reset(int packet_size); bool can_recv_contiguous(int /*size*/) const { return true; } #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const { TORRENT_ASSERT(m_recv_end >= m_recv_start); TORRENT_ASSERT(bool(m_disk_recv_buffer) == (m_disk_recv_buffer_size > 0)); } #endif private: // explicitly disallow assignment, to silence msvc warning receive_buffer& operator=(receive_buffer const&); // recv_buf.begin (start of actual receive buffer) // | // | m_recv_start (logical start of current // | | receive buffer, as perceived by upper layers) // | | // | | m_recv_pos (number of bytes consumed // | | | by upper layer, from logical receive buffer) // | | | // | x---------x // | | | recv_buf.end (end of actual receive buffer) // | | | | // v v v v // *------==========--------- // ^ // | // | // ------------------->x m_recv_end (end of received data, // beyond this point is garbage) // m_recv_buffer // when not using contiguous receive buffers, there // may be a disk_recv_buffer in the mix as well. Whenever // m_disk_recv_buffer_size > 0 (and presumably also // m_disk_recv_buffer != NULL) the disk buffer is imagined // to be appended to the receive buffer right after m_recv_end. // the start of the logical receive buffer int m_recv_start; // the number of valid, received bytes in m_recv_buffer int m_recv_end; // the byte offset in m_recv_buffer that we have // are passing on to the upper layer. This is // always <= m_recv_end int m_recv_pos; // the size (in bytes) of the bittorrent message // we're currently receiving int m_packet_size; // the number of bytes that the other // end has to send us in order to respond // to all outstanding piece requests we // have sent to it int m_soft_packet_size; int m_disk_recv_buffer_size; buffer m_recv_buffer; // if this peer is receiving a piece, this // points to a disk buffer that the data is // read into. This eliminates a memcopy from // the receive buffer into the disk buffer disk_buffer_holder m_disk_recv_buffer; }; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // Wraps a receive_buffer to provide the ability to inject // possibly authenticated crypto beneath the bittorrent protocol. // When authenticated crypto is in use the wrapped receive_buffer // holds the receive state of the crpyto layer while this class // tracks the state of the bittorrent protocol. struct crypto_receive_buffer { crypto_receive_buffer(receive_buffer& next) : m_recv_pos(INT_MAX) , m_packet_size(0) , m_soft_packet_size(0) , m_connection_buffer(next) {} buffer::interval mutable_buffer() { return m_connection_buffer.mutable_buffer(); } char* release_disk_buffer() { return m_connection_buffer.release_disk_buffer(); } bool has_disk_buffer() const { return m_connection_buffer.has_disk_buffer(); } void assert_no_disk_buffer() const { m_connection_buffer.assert_no_disk_buffer(); } bool packet_finished() const; bool crypto_packet_finished() const { return m_recv_pos == INT_MAX || m_connection_buffer.packet_finished(); } int packet_size() const; int crypto_packet_size() const { TORRENT_ASSERT(m_recv_pos != INT_MAX); return m_connection_buffer.packet_size() - m_recv_pos; } int pos() const; void cut(int size, int packet_size, int offset = 0); void crypto_cut(int size, int packet_size) { TORRENT_ASSERT(m_recv_pos != INT_MAX); m_connection_buffer.cut(size, m_recv_pos + packet_size, m_recv_pos); } void reset(int packet_size); void crypto_reset(int packet_size); void set_soft_packet_size(int size); int advance_pos(int bytes); buffer::const_interval get() const; bool can_recv_contiguous(int /*size*/) const { // TODO: Detect when the start of the next crpyto packet is aligned // with the start of piece data and the crpyto packet is at least // as large as the piece data. With a little extra work // we could receive directly into a disk buffer in that case. return m_recv_pos == INT_MAX; } void mutable_buffers(std::vector& vec , std::size_t bytes_transfered); private: // explicitly disallow assignment, to silence msvc warning crypto_receive_buffer& operator=(crypto_receive_buffer const&); int m_recv_pos; int m_packet_size; int m_soft_packet_size; receive_buffer& m_connection_buffer; }; #endif // TORRENT_DISABLE_ENCRYPTION } // namespace libtorrent #endif // #ifndef TORRENT_RECEIVE_BUFFER_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/request_blocks.hpp000066400000000000000000000042431351156116000253130ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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_REQUEST_BLOCKS_HPP_INCLUDED #define TORRENT_REQUEST_BLOCKS_HPP_INCLUDED namespace libtorrent { class torrent; class peer_connection; // returns false if the piece picker was not invoked, because // of an early exit condition. In this case, the stats counter // shouldn't be incremented, since it won't use any significant // amount of CPU bool request_a_block(torrent& t, peer_connection& c); // returns the rank of a peer's source. We have an affinity // to connecting to peers with higher rank. This is to avoid // problems when our peer list is diluted by stale peers from // the resume data for instance int source_rank(int source_bitmask); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/resolve_links.hpp000066400000000000000000000056621351156116000251530ustar00rootroot00000000000000/* Copyright (c) 2014-2018, 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_RESOLVE_LINKS_HPP #define TORRENT_RESOLVE_LINKS_HPP #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/export.hpp" namespace libtorrent { class torrent_info; #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS // this class is used for mutable torrents, to discover identical files // in other torrents. struct TORRENT_EXTRA_EXPORT resolve_links { struct TORRENT_EXTRA_EXPORT link_t { boost::shared_ptr ti; std::string save_path; int file_idx; }; resolve_links(boost::shared_ptr ti); // check to see if any files are shared with this torrent void match(boost::shared_ptr const& ti , std::string const& save_path); std::vector const& get_links() const { return m_links; } private: // this is the torrent we're trying to find files for. boost::shared_ptr m_torrent_file; // each file in m_torrent_file has an entry in this vector. Any file // that also exists somewhere else, is filled in with the corresponding // torrent_info object and file index std::vector m_links; // maps file size to file index, in m_torrent_file boost::unordered_multimap m_file_sizes; }; #endif // TORRENT_DISABLE_MUTABLE_TORRENTS } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/resolver.hpp000066400000000000000000000056071351156116000241340ustar00rootroot00000000000000/* Copyright (c) 2013-2018, 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_RESOLVER_HPP_INCLUDE #define TORRENT_RESOLVER_HPP_INCLUDE #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/resolver_interface.hpp" #include "libtorrent/address.hpp" #include "libtorrent/time.hpp" namespace libtorrent { struct TORRENT_EXTRA_EXPORT resolver TORRENT_FINAL : resolver_interface { resolver(io_service& ios); virtual void async_resolve(std::string const& host, int flags , callback_t const& h) TORRENT_OVERRIDE; virtual void abort() TORRENT_OVERRIDE; private: void on_lookup(error_code const& ec, tcp::resolver::iterator i , resolver_interface::callback_t h, std::string hostname); struct dns_cache_entry { time_point last_seen; std::vector
addresses; }; typedef boost::unordered_map cache_t; cache_t m_cache; io_service& m_ios; // all lookups in this resolver are aborted on shutdown. tcp::resolver m_resolver; // lookups in this resolver are not aborted on shutdown tcp::resolver m_critical_resolver; // max number of cached entries int m_max_size; // timeout of cache entries time_duration m_timeout; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/resolver_interface.hpp000066400000000000000000000051211351156116000261430ustar00rootroot00000000000000/* Copyright (c) 2013-2018, 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_RESOLVER_INTERFACE_HPP_INCLUDE #define TORRENT_RESOLVER_INTERFACE_HPP_INCLUDE #include #include "libtorrent/error_code.hpp" #include "libtorrent/address.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct TORRENT_EXTRA_EXPORT resolver_interface { typedef boost::function const&)> callback_t; enum flags_t { // this flag will make async_resolve() only use the cache and fail if we // don't have a cache entry, regardless of how old it is. This is usefull // when completing the lookup quickly is more important than accuracy, // like on shutdown cache_only = 1, // set this flag for lookups that are not critical during shutdown. i.e. // for looking up tracker names _except_ when stopping a tracker. abort_on_shutdown = 2 }; virtual void async_resolve(std::string const& host, int flags , callback_t const& h) = 0; virtual void abort() = 0; protected: ~resolver_interface() {} }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/rss.hpp000066400000000000000000000231401351156116000230720ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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_RSS_HPP_INCLUDED #define TORRENT_RSS_HPP_INCLUDED #include "libtorrent/torrent_handle.hpp" #include "libtorrent/add_torrent_params.hpp" #include #include #ifndef TORRENT_NO_DEPRECATE namespace libtorrent { namespace aux { struct session_impl; } class session; struct bdecode_node; // represents one item from an RSS feed. Specifically // a feed of torrents. // struct TORRENT_EXPORT feed_item { feed_item(); #if __cplusplus >= 201103L feed_item(feed_item const&) = default; feed_item & operator=(feed_item const&) = default; #endif ~feed_item(); // these are self explanatory and may be empty if the feed does not specify // those fields. std::string url; std::string uuid; std::string title; std::string description; std::string comment; std::string category; // the total size of the content the torrent refers to, or -1 // if no size was specified by the feed. boost::int64_t size; // the handle to the torrent, if the session is already downloading // this torrent. torrent_handle handle; // the info-hash of the torrent, or cleared (i.e. all zeroes) if // the feed does not specify the info-hash. sha1_hash info_hash; }; // given a feed_item ``f``, add the torrent it refers to to session ``s``. #ifndef BOOST_NO_EXCEPTIONS torrent_handle TORRENT_EXPORT add_feed_item(session& s, feed_item const& fi , add_torrent_params const& p); #endif torrent_handle TORRENT_EXPORT add_feed_item(session& s, feed_item const& fi , add_torrent_params const& p, error_code& ec); // the feed_settings object is all the information // and configuration for a specific feed. All of // these settings can be changed by the user // after adding the feed struct TORRENT_EXPORT feed_settings { feed_settings() : auto_download(true) , auto_map_handles(true) , default_ttl(30) {} std::string url; // By default ``auto_download`` is true, which means all torrents in // the feed will be downloaded. Set this to false in order to manually // add torrents to the session. You may react to the rss_alert when // a feed has been updated to poll it for the new items in the feed // when adding torrents manually. When torrents are added automatically, // an add_torrent_alert is posted which includes the torrent handle // as well as the error code if it failed to be added. You may also call // ``session::get_torrents()`` to get the handles to the new torrents. bool auto_download; // ``auto_map_handles`` defaults to true and determines whether or // not to set the ``handle`` field in the feed_item, returned // as the feed status. If auto-download is enabled, this setting // is ignored. If auto-download is not set, setting this to false // will save one pass through all the feed items trying to find // corresponding torrents in the session. bool auto_map_handles; // The ``default_ttl`` is the default interval for refreshing a feed. // This may be overridden by the feed itself (by specifying the ```` // tag) and defaults to 30 minutes. The field specifies the number of // minutes between refreshes. int default_ttl; // If torrents are added automatically, you may want to set the // ``add_args`` to appropriate values for download directory etc. // This object is used as a template for adding torrents from feeds, // but some torrent specific fields will be overridden by the // individual torrent being added. For more information on the // add_torrent_params, see async_add_torrent() and add_torrent(). add_torrent_params add_args; }; // holds information about the status of an RSS feed. Retrieved by // calling get_feed_status() on feed_handle. struct TORRENT_EXPORT feed_status { feed_status(): last_update(0), next_update(0) , updating(false), ttl(0) {} // the URL of the feed. std::string url; // the name of the feed (as specified by the feed itself). This // may be empty if we have not recevied a response from the RSS server yet, // or if the feed does not specify a title. std::string title; // the feed description (as specified by the feed itself). // This may be empty if we have not received a response from the RSS server // yet, or if the feed does not specify a description. std::string description; // the posix time of the last successful response from the feed. time_t last_update; // the number of seconds, from now, when the feed will be // updated again. int next_update; // true if the feed is currently being updated (i.e. waiting for // DNS resolution, connecting to the server or waiting for the response to the // HTTP request, or receiving the response). bool updating; // a vector of all items that we have received from the feed. See // feed_item for more information. std::vector items; // set to the appropriate error code if the feed encountered an // error. See error_code for more info. error_code error; // the current refresh time (in minutes). It's either the configured // default ttl, or the ttl specified by the feed. int ttl; }; struct feed; // The ``feed_handle`` refers to a specific RSS feed that is watched by the session. struct TORRENT_EXPORT feed_handle { feed_handle() {} // Forces an update/refresh of the feed. Regular updates of the feed is managed // by libtorrent, be careful to not call this too frequently since it may // overload the RSS server. void update_feed(); // Queries the RSS feed for information, including all the items in the feed. // see feed_status. feed_status get_feed_status() const; // Sets and gets settings for this feed. For more information on the // available settings, see add_feed(). void set_settings(feed_settings const& s); feed_settings settings() const; private: friend struct aux::session_impl; friend struct feed; feed_handle(boost::weak_ptr const& p); boost::weak_ptr m_feed_ptr; }; struct feed_state; class http_parser; boost::shared_ptr TORRENT_EXPORT new_feed(aux::session_impl& ses, feed_settings const& sett); // this is the internal object holding all state about an // RSS feed. All user interaction with this object // goes through the feed_handle, which makes sure all calls // are posted to the network thread struct TORRENT_EXTRA_EXPORT feed : boost::enable_shared_from_this { friend void parse_feed(feed_state& f, int token, char const* name, int len , char const* val, int val_len); feed(aux::session_impl& ses, feed_settings const& feed); void on_feed(error_code const& ec, http_parser const& parser , char const* data, int size); int update_feed(); aux::session_impl& session() const { return m_ses; } void set_settings(feed_settings const& s); void get_settings(feed_settings* s) const; void get_feed_status(feed_status* ret) const; int next_update(time_t now) const; void load_state(bdecode_node const& rd); void save_state(entry& rd) const; private: friend struct aux::session_impl; // explicitly disallow assignment, to silence msvc warning feed& operator=(feed const&); void add_item(feed_item const& item); feed_handle my_handle(); error_code m_error; std::vector m_items; // these are all the URLs we've seen in the items list. // it's used to avoid adding duplicate entries to the actual // item vector std::set m_urls; // these are URLs that have been added to the session // once. If we see them again, and they're not in the // session, don't add them again, since it means they // were removed from the session. It maps URLs to the // posix time when they were added. The timestamp is // used to prune this list by removing the oldest ones // when the size gets too big std::map m_added; std::string m_title; std::string m_description; time_t m_last_attempt; time_t m_last_update; // refresh rate of this feed in minutes int m_ttl; // the number of update failures in a row int m_failures; // true while waiting for the server to respond bool m_updating; feed_settings m_settings; aux::session_impl& m_ses; }; } #endif // TORRENT_NO_DEPRECATE #endif libtorrent-rasterbar-1.1.13/include/libtorrent/session.hpp000066400000000000000000000270601351156116000237530ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/version.hpp" #include "libtorrent/build_config.hpp" #include "libtorrent/settings_pack.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/session_handle.hpp" #include "libtorrent/thread.hpp" #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/rss.hpp" #include "libtorrent/fingerprint.hpp" #endif #ifdef TORRENT_USE_OPENSSL // this is a nasty openssl macro #ifdef set_key #undef set_key #endif #endif namespace libtorrent { // The default values of the session settings are set for a regular // bittorrent client running on a desktop system. There are functions that // can set the session settings to pre set settings for other environments. // These can be used for the basis, and should be tweaked to fit your needs // better. // // ``min_memory_usage`` returns settings that will use the minimal amount of // RAM, at the potential expense of upload and download performance. It // adjusts the socket buffer sizes, disables the disk cache, lowers the send // buffer watermarks so that each connection only has at most one block in // use at any one time. It lowers the outstanding blocks send to the disk // I/O thread so that connections only have one block waiting to be flushed // to disk at any given time. It lowers the max number of peers in the peer // list for torrents. It performs multiple smaller reads when it hashes // pieces, instead of reading it all into memory before hashing. // // This configuration is inteded to be the starting point for embedded // devices. It will significantly reduce memory usage. // // ``high_performance_seed`` returns settings optimized for a seed box, // serving many peers and that doesn't do any downloading. It has a 128 MB // disk cache and has a limit of 400 files in its file pool. It support fast // upload rates by allowing large send buffers. TORRENT_EXPORT void min_memory_usage(settings_pack& set); TORRENT_EXPORT void high_performance_seed(settings_pack& set); #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED TORRENT_EXPORT session_settings min_memory_usage(); TORRENT_DEPRECATED TORRENT_EXPORT session_settings high_performance_seed(); #endif #ifndef TORRENT_CFG #error TORRENT_CFG is not defined! #endif void TORRENT_EXPORT TORRENT_CFG(); namespace aux { struct session_impl; } // this is a holder for the internal session implementation object. Once the // session destruction is explicitly initiated, this holder is used to // synchronize the completion of the shutdown. The lifetime of this object // may outlive session, causing the session destructor to not block. The // session_proxy destructor will block however, until the underlying session // is done shutting down. class TORRENT_EXPORT session_proxy { friend class session; public: // default constructor, does not refer to any session // implementation object. session_proxy() {} ~session_proxy(); #if __cplusplus >= 201103L session_proxy(session_proxy const&) = default; session_proxy& operator=(session_proxy const&) = default; #endif private: session_proxy( boost::shared_ptr ios , boost::shared_ptr t , boost::shared_ptr impl) : m_io_service(ios) , m_thread(t) , m_impl(impl) {} boost::shared_ptr m_io_service; boost::shared_ptr m_thread; boost::shared_ptr m_impl; }; // The session holds all state that spans multiple torrents. Among other // things it runs the network loop and manages all torrents. Once it's // created, the session object will spawn the main thread that will do all // the work. The main thread will be idle as long it doesn't have any // torrents to participate in. // // You have some control over session configuration through the // ``session::apply_settings()`` member function. To change one or more // configuration options, create a settings_pack. object and fill it with // the settings to be set and pass it in to ``session::apply_settings()``. // // see apply_settings(). class TORRENT_EXPORT session : public boost::noncopyable, public session_handle { public: // Constructs the session objects which acts as the container of torrents. // It provides configuration options across torrents (such as rate limits, // disk cache, ip filter etc.). In order to avoid a race condition between // starting the session and configuring it, you can pass in a // settings_pack object. Its settings will take effect before the session // starts up. // // The ``flags`` parameter can be used to start default features (UPnP & // NAT-PMP) and default plugins (ut_metadata, ut_pex and smart_ban). The // default is to start those features. If you do not want them to start, // pass 0 as the flags parameter. session(settings_pack const& pack = settings_pack() , int flags = start_default_features | add_default_plugins) : session_handle(NULL) { TORRENT_CFG(); start(flags, pack, NULL); } // overload of the constructor that takes an external io_service to run // the session object on. This is primarily useful for tests that may want // to run multiple sessions on a single io_service, or low resource // systems where additional threads are expensive and sharing an // io_service with other events is fine. // // .. warning:: // The session object does not cleanly terminate with an external // ``io_service``. The ``io_service::run()`` call _must_ have returned // before it's safe to destruct the session. Which means you *MUST* // call session::abort() and save the session_proxy first, then // destruct the session object, then sync with the io_service, then // destruct the session_proxy object. session(settings_pack const& pack , io_service& ios , int flags = start_default_features | add_default_plugins) : session_handle(NULL) { TORRENT_CFG(); start(flags, pack, &ios); } #ifndef TORRENT_NO_DEPRECATE #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif TORRENT_DEPRECATED session(fingerprint const& print , int flags = start_default_features | add_default_plugins , boost::uint32_t alert_mask = alert::error_notification) : session_handle(NULL) { TORRENT_CFG(); settings_pack pack; pack.set_int(settings_pack::alert_mask, alert_mask); pack.set_str(settings_pack::peer_fingerprint, print.to_string()); if ((flags & start_default_features) == 0) { pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_dht, false); } start(flags, pack, NULL); } TORRENT_DEPRECATED session(fingerprint const& print , std::pair listen_port_range , char const* listen_interface = "0.0.0.0" , int flags = start_default_features | add_default_plugins , int alert_mask = alert::error_notification) : session_handle(NULL) { TORRENT_CFG(); TORRENT_ASSERT(listen_port_range.first > 0); TORRENT_ASSERT(listen_port_range.first <= listen_port_range.second); settings_pack pack; pack.set_int(settings_pack::alert_mask, alert_mask); pack.set_int(settings_pack::max_retry_port_bind, listen_port_range.second - listen_port_range.first); pack.set_str(settings_pack::peer_fingerprint, print.to_string()); char if_string[100]; if (listen_interface == NULL) listen_interface = "0.0.0.0"; snprintf(if_string, sizeof(if_string), "%s:%d", listen_interface, listen_port_range.first); pack.set_str(settings_pack::listen_interfaces, if_string); if ((flags & start_default_features) == 0) { pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_dht, false); } start(flags, pack, NULL); } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #ifdef __clang__ #pragma clang diagnostic pop #endif #endif // TORRENT_NO_DEPRECATE // The destructor of session will notify all trackers that our torrents // have been shut down. If some trackers are down, they will time out. // All this before the destructor of session returns. So, it's advised // that any kind of interface (such as windows) are closed before // destructing the session object. Because it can take a few second for // it to finish. The timeout can be set with apply_settings(). ~session(); // In case you want to destruct the session asynchronously, you can // request a session destruction proxy. If you don't do this, the // destructor of the session object will block while the trackers are // contacted. If you keep one ``session_proxy`` to the session when // destructing it, the destructor will not block, but start to close down // the session, the destructor of the proxy will then synchronize the // threads. So, the destruction of the session is performed from the // ``session`` destructor call until the ``session_proxy`` destructor // call. The ``session_proxy`` does not have any operations on it (since // the session is being closed down, no operations are allowed on it). // The only valid operation is calling the destructor:: // // class session_proxy // { // public: // session_proxy(); // ~session_proxy() // }; session_proxy abort(); private: void start(int flags, settings_pack const& pack, io_service* ios); // data shared between the main thread // and the working thread boost::shared_ptr m_io_service; boost::shared_ptr m_thread; boost::shared_ptr m_impl; }; } #endif // TORRENT_SESSION_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/session_handle.hpp000066400000000000000000001262431351156116000252710ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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_HANDLE_HPP_INCLUDED #define TORRENT_SESSION_HANDLE_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/add_torrent_params.hpp" #include "libtorrent/disk_io_thread.hpp" // for cached_piece_info #include "libtorrent/alert.hpp" // alert::error_notification #include "libtorrent/peer_class.hpp" #include "libtorrent/peer_class_type_filter.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/kademlia/dht_storage.hpp" #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/rss.hpp" #endif namespace libtorrent { struct plugin; struct torrent_plugin; class torrent; struct ip_filter; class port_filter; class alert; #ifndef TORRENT_NO_DEPRECATE struct session_status; #endif typedef boost::function& , error_code&)> user_load_function_t; struct TORRENT_EXPORT session_handle { session_handle() : m_impl(NULL) {} session_handle(aux::session_impl* impl) : m_impl(impl) {} bool is_valid() const { return m_impl != NULL; } // TODO: 2 the ip filter should probably be saved here too // flags that determines which aspects of the session should be // saved when calling save_state(). enum save_state_flags_t { // saves settings (i.e. the settings_pack) save_settings = 0x001, // saves dht_settings save_dht_settings = 0x002, // saves dht state such as nodes and node-id, possibly accelerating // joining the DHT if provided at next session startup. save_dht_state = 0x004 #ifndef TORRENT_NO_DEPRECATE , // save pe_settings save_encryption_settings TORRENT_DEPRECATED_ENUM = 0x020, save_as_map TORRENT_DEPRECATED_ENUM = 0x040, // saves RSS feeds save_feeds TORRENT_DEPRECATED_ENUM = 0x080, save_proxy TORRENT_DEPRECATED_ENUM = 0x008, save_i2p_proxy TORRENT_DEPRECATED_ENUM = 0x010, save_dht_proxy TORRENT_DEPRECATED_ENUM = 0x008, // save_proxy save_peer_proxy TORRENT_DEPRECATED_ENUM = 0x008, // save_proxy save_web_proxy TORRENT_DEPRECATED_ENUM = 0x008, // save_proxy save_tracker_proxy TORRENT_DEPRECATED_ENUM = 0x008 // save_proxy #endif }; // loads and saves all session settings, including dht_settings, // encryption settings and proxy settings. ``save_state`` writes all keys // to the ``entry`` that's passed in, which needs to either not be // initialized, or initialized as a dictionary. // // ``load_state`` expects a bdecode_node which can be built from a bencoded // buffer with bdecode(). // // The ``flags`` argument is used to filter which parts of the session // state to save or load. By default, all state is saved/restored (except // for the individual torrents). see save_state_flags_t // // When saving settings, there are two fields that are *not* loaded. // ``peer_fingerprint`` and ``user_agent``. Those are left as configured // by the ``session_settings`` passed to the session constructor or // subsequently set via apply_settings(). void save_state(entry& e, boost::uint32_t flags = 0xffffffff) const; void load_state(bdecode_node const& e, boost::uint32_t flags = 0xffffffff); // .. note:: // these calls are potentially expensive and won't scale well with // lots of torrents. If you're concerned about performance, consider // using ``post_torrent_updates()`` instead. // // ``get_torrent_status`` returns a vector of the torrent_status for // every torrent which satisfies ``pred``, which is a predicate function // which determines if a torrent should be included in the returned set // or not. Returning true means it should be included and false means // excluded. The ``flags`` argument is the same as to // ``torrent_handle::status()``. Since ``pred`` is guaranteed to be // called for every torrent, it may be used to count the number of // torrents of different categories as well. // // ``refresh_torrent_status`` takes a vector of torrent_status structs // (for instance the same vector that was returned by // get_torrent_status() ) and refreshes the status based on the // ``handle`` member. It is possible to use this function by first // setting up a vector of default constructed ``torrent_status`` objects, // only initializing the ``handle`` member, in order to request the // torrent status for multiple torrents in a single call. This can save a // significant amount of time if you have a lot of torrents. // // Any torrent_status object whose ``handle`` member is not referring to // a valid torrent are ignored. // // The intended use of these functions is to start off by calling // ``get_torrent_status()`` to get a list of all torrents that match your // criteria. Then call ``refresh_torrent_status()`` on that list. This // will only refresh the status for the torrents in your list, and thus // ignore all other torrents you might be running. This may save a // significant amount of time, especially if the number of torrents you're // interested in is small. In order to keep your list of interested // torrents up to date, you can either call ``get_torrent_status()`` from // time to time, to include torrents you might have become interested in // since the last time. In order to stop refreshing a certain torrent, // simply remove it from the list. void get_torrent_status(std::vector* ret , boost::function const& pred , boost::uint32_t flags = 0) const; void refresh_torrent_status(std::vector* ret , boost::uint32_t flags = 0) const; // This functions instructs the session to post the state_update_alert, // containing the status of all torrents whose state changed since the // last time this function was called. // // Only torrents who has the state subscription flag set will be // included. This flag is on by default. See add_torrent_params. // the ``flags`` argument is the same as for torrent_handle::status(). // see torrent_handle::status_flags_t. void post_torrent_updates(boost::uint32_t flags = 0xffffffff); // This function will post a session_stats_alert object, containing a // snapshot of the performance counters from the internals of libtorrent. // To interpret these counters, query the session via // session_stats_metrics(). // // For more information, see the session-statistics_ section. void post_session_stats(); // This will cause a dht_stats_alert to be posted. void post_dht_stats(); // internal io_service& get_io_service(); // ``find_torrent()`` looks for a torrent with the given info-hash. In // case there is such a torrent in the session, a torrent_handle to that // torrent is returned. In case the torrent cannot be found, an invalid // torrent_handle is returned. // // See ``torrent_handle::is_valid()`` to know if the torrent was found or // not. // // ``get_torrents()`` returns a vector of torrent_handles to all the // torrents currently in the session. torrent_handle find_torrent(sha1_hash const& info_hash) const; std::vector get_torrents() const; // You add torrents through the add_torrent() function where you give an // object with all the parameters. The add_torrent() overloads will block // until the torrent has been added (or failed to be added) and returns // an error code and a torrent_handle. In order to add torrents more // efficiently, consider using async_add_torrent() which returns // immediately, without waiting for the torrent to add. Notification of // the torrent being added is sent as add_torrent_alert. // // The overload that does not take an error_code throws an exception on // error and is not available when building without exception support. // The torrent_handle returned by add_torrent() can be used to retrieve // information about the torrent's progress, its peers etc. It is also // used to abort a torrent. // // If the torrent you are trying to add already exists in the session (is // either queued for checking, being checked or downloading) // ``add_torrent()`` will throw libtorrent_exception which derives from // ``std::exception`` unless duplicate_is_error is set to false. In that // case, add_torrent() will return the handle to the existing torrent. // // all torrent_handles must be destructed before the session is destructed! #ifndef BOOST_NO_EXCEPTIONS torrent_handle add_torrent(add_torrent_params const& params); #endif torrent_handle add_torrent(add_torrent_params const& params, error_code& ec); void async_add_torrent(add_torrent_params const& params); #ifndef BOOST_NO_EXCEPTIONS #ifndef TORRENT_NO_DEPRECATE // deprecated in 0.14 TORRENT_DEPRECATED torrent_handle add_torrent( torrent_info const& ti , std::string const& save_path , entry const& resume_data = entry() , storage_mode_t storage_mode = storage_mode_sparse , bool paused = false , storage_constructor_type sc = default_storage_constructor); // deprecated in 0.14 TORRENT_DEPRECATED torrent_handle add_torrent( char const* tracker_url , sha1_hash const& info_hash , char const* name , std::string const& save_path , entry const& resume_data = entry() , storage_mode_t storage_mode = storage_mode_sparse , bool paused = false , storage_constructor_type sc = default_storage_constructor , void* userdata = 0); #endif #endif // Pausing the session has the same effect as pausing every torrent in // it, except that torrents will not be resumed by the auto-manage // mechanism. Resuming will restore the torrents to their previous paused // state. i.e. the session pause state is separate from the torrent pause // state. A torrent is inactive if it is paused or if the session is // paused. void pause(); void resume(); bool is_paused() const; // *the feature of dynamically loading/unloading torrents is deprecated // and discouraged* // // This function enables dynamic-loading-of-torrent-files_. When a // torrent is unloaded but needs to be available in memory, this function // is called **from within the libtorrent network thread**. From within // this thread, you can **not** use any of the public APIs of libtorrent // itself. The the info-hash of the torrent is passed in to the function // and it is expected to fill in the passed in ``vector`` with the // .torrent file corresponding to it. // // If there is an error loading the torrent file, the ``error_code`` // (``ec``) should be set to reflect the error. In such case, the torrent // itself is stopped and set to an error state with the corresponding // error code. // // Given that the function is called from the internal network thread of // libtorrent, it's important to not stall. libtorrent will not be able // to send nor receive any data until the function call returns. // // The signature of the function to pass in is:: // // void fun(sha1_hash const& info_hash, std::vector& buf, error_code& ec); void set_load_function(user_load_function_t fun); #ifndef TORRENT_NO_DEPRECATE // deprecated in libtorrent 1.1, use performance_counters instead // returns session wide-statistics and status. For more information, see // the ``session_status`` struct. TORRENT_DEPRECATED session_status status() const; // deprecated in libtorrent 1.1 // fills out the supplied vector with information for each piece that is // currently in the disk cache for the torrent with the specified // info-hash (``ih``). TORRENT_DEPRECATED void get_cache_info(sha1_hash const& ih , std::vector& ret) const; // Returns status of the disk cache for this session. // For more information, see the cache_status type. TORRENT_DEPRECATED cache_status get_cache_status() const; #endif enum { disk_cache_no_pieces = 1 }; // Fills in the cache_status struct with information about the given torrent. // If ``flags`` is ``session::disk_cache_no_pieces`` the ``cache_status::pieces`` field // will not be set. This may significantly reduce the cost of this call. void get_cache_info(cache_status* ret, torrent_handle h = torrent_handle(), int flags = 0) const; #ifndef TORRENT_NO_DEPRECATE // This adds an RSS feed to the session. The feed will be refreshed // regularly and optionally add all torrents from the feed, as they // appear. // // Before adding the feed, you must set the ``url`` field to the feed's // url. It may point to an RSS or an atom feed. The returned feed_handle // is a handle which is used to interact with the feed, things like // forcing a refresh or querying for information about the items in the // feed. For more information, see feed_handle. TORRENT_DEPRECATED feed_handle add_feed(feed_settings const& feed); // Removes a feed from being watched by the session. When this // call returns, the feed handle is invalid and won't refer // to any feed. TORRENT_DEPRECATED void remove_feed(feed_handle h); // Returns a list of all RSS feeds that are being watched by the session. TORRENT_DEPRECATED void get_feeds(std::vector& f) const; // ``start_dht`` starts the dht node and makes the trackerless service // available to torrents. // // ``stop_dht`` stops the dht node. // deprecated. use settings_pack::enable_dht instead TORRENT_DEPRECATED void start_dht(); TORRENT_DEPRECATED void stop_dht(); #endif // ``set_dht_settings`` sets some parameters available to the dht node. // See dht_settings for more information. // // ``is_dht_running()`` returns true if the DHT support has been started // and false // otherwise. // // ``get_dht_settings()`` returns the current settings void set_dht_settings(dht_settings const& settings); bool is_dht_running() const; dht_settings get_dht_settings() const; // ``set_dht_storage`` set a dht custom storage constructor function // to be used internally when the dht is created. // // Since the dht storage is a critical component for the dht behavior, // this function will only be effective the next time the dht is started. // If you never touch this feature, a default map-memory based storage // is used. // // If you want to make sure the dht is initially created with your // custom storage, create a session with the setting // ``settings_pack::enable_dht`` to false, set your constructor function // and call ``apply_settings`` with ``settings_pack::enable_dht`` to true. void set_dht_storage(dht::dht_storage_constructor_type sc); // ``add_dht_node`` takes a host name and port pair. That endpoint will be // pinged, and if a valid DHT reply is received, the node will be added to // the routing table. void add_dht_node(std::pair const& node); #ifndef TORRENT_NO_DEPRECATE // deprecated, use settings_pack::dht_bootstrap_nodes instead // // ``add_dht_router`` adds the given endpoint to a list of DHT router // nodes. If a search is ever made while the routing table is empty, // those nodes will be used as backups. Nodes in the router node list // will also never be added to the regular routing table, which // effectively means they are only used for bootstrapping, to keep the // load off them. // // An example routing node that you could typically add is // ``router.bittorrent.com``. TORRENT_DEPRECATED void add_dht_router(std::pair const& node); #endif // query the DHT for an immutable item at the ``target`` hash. // the result is posted as a dht_immutable_item_alert. void dht_get_item(sha1_hash const& target); // query the DHT for a mutable item under the public key ``key``. // this is an ed25519 key. ``salt`` is optional and may be left // as an empty string if no salt is to be used. // if the item is found in the DHT, a dht_mutable_item_alert is // posted. void dht_get_item(boost::array key , std::string salt = std::string()); // store the given bencoded data as an immutable item in the DHT. // the returned hash is the key that is to be used to look the item // up again. It's just the SHA-1 hash of the bencoded form of the // structure. sha1_hash dht_put_item(entry data); // store a mutable item. The ``key`` is the public key the blob is // to be stored under. The optional ``salt`` argument is a string that // is to be mixed in with the key when determining where in the DHT // the value is to be stored. The callback function is called from within // the libtorrent network thread once we've found where to store the blob, // possibly with the current value stored under the key. // The values passed to the callback functions are: // // entry& value // the current value stored under the key (may be empty). Also expected // to be set to the value to be stored by the function. // // boost::array& signature // the signature authenticating the current value. This may be zeros // if there is currently no value stored. The function is expected to // fill in this buffer with the signature of the new value to store. // To generate the signature, you may want to use the // ``sign_mutable_item`` function. // // boost::uint64_t& seq // current sequence number. May be zero if there is no current value. // The function is expected to set this to the new sequence number of // the value that is to be stored. Sequence numbers must be monotonically // increasing. Attempting to overwrite a value with a lower or equal // sequence number will fail, even if the signature is correct. // // std::string const& salt // this is the salt that was used for this put call. // // Since the callback function ``cb`` is called from within libtorrent, // it is critical to not perform any blocking operations. Ideally not // even locking a mutex. Pass any data required for this function along // with the function object's context and make the function entirely // self-contained. The only reason data blob's value is computed // via a function instead of just passing in the new value is to avoid // race conditions. If you want to *update* the value in the DHT, you // must first retrieve it, then modify it, then write it back. The way // the DHT works, it is natural to always do a lookup before storing and // calling the callback in between is convenient. void dht_put_item(boost::array key , boost::function& , boost::uint64_t&, std::string const&)> cb , std::string salt = std::string()); void dht_get_peers(sha1_hash const& info_hash); void dht_announce(sha1_hash const& info_hash, int port = 0, int flags = 0); // Send an arbitrary DHT request directly to the specified endpoint. This // function is intended for use by plugins. When a response is received // or the request times out, a dht_direct_response_alert will be posted // with the response (if any) and the userdata pointer passed in here. // Since this alert is a response to an explicit call, it will always be // posted, regardless of the alert mask. void dht_direct_request(udp::endpoint ep, entry const& e, void* userdata = 0); #ifndef TORRENT_NO_DEPRECATE // deprecated in 0.15 // use save_state and load_state instead TORRENT_DEPRECATED entry dht_state() const; TORRENT_DEPRECATED void start_dht(entry const& startup_state); #endif // This function adds an extension to this session. The argument is a // function object that is called with a ``torrent_handle`` and which should // return a ``boost::shared_ptr``. To write custom // plugins, see `libtorrent plugins`_. For the typical bittorrent client // all of these extensions should be added. The main plugins implemented // in libtorrent are: // // metadata extension // Allows peers to download the metadata (.torrent files) from the swarm // directly. Makes it possible to join a swarm with just a tracker and // info-hash. // // .. code:: c++ // // #include // ses.add_extension(&libtorrent::create_metadata_plugin); // // uTorrent metadata // Same as ``metadata extension`` but compatible with uTorrent. // // .. code:: c++ // // #include // ses.add_extension(&libtorrent::create_ut_metadata_plugin); // // uTorrent peer exchange // Exchanges peers between clients. // // .. code:: c++ // // #include // ses.add_extension(&libtorrent::create_ut_pex_plugin); // // smart ban plugin // A plugin that, with a small overhead, can ban peers // that sends bad data with very high accuracy. Should // eliminate most problems on poisoned torrents. // // .. code:: c++ // // #include // ses.add_extension(&libtorrent::create_smart_ban_plugin); // // // .. _`libtorrent plugins`: libtorrent_plugins.html void add_extension(boost::function( torrent_handle const&, void*)> ext); void add_extension(boost::shared_ptr ext); #ifndef TORRENT_NO_DEPRECATE // GeoIP support has been removed from libtorrent internals. If you // still need to resolve peers, please do so on the client side, using // libgeoip directly. This was removed in libtorrent 1.1 // These functions expects a path to the `MaxMind ASN database`_ and // `MaxMind GeoIP database`_ respectively. This will be used to look up // which AS and country peers belong to. // // ``as_for_ip`` returns the AS number for the IP address specified. If // the IP is not in the database or the ASN database is not loaded, 0 is // returned. // // .. _`MaxMind ASN database`: http://www.maxmind.com/app/asnum // .. _`MaxMind GeoIP database`: http://www.maxmind.com/app/geolitecountry TORRENT_DEPRECATED void load_asnum_db(char const* file); TORRENT_DEPRECATED void load_country_db(char const* file); TORRENT_DEPRECATED int as_for_ip(address const& addr); #if TORRENT_USE_WSTRING // all wstring APIs are deprecated since 0.16.11 // instead, use the wchar -> utf8 conversion functions // and pass in utf8 strings TORRENT_DEPRECATED void load_country_db(wchar_t const* file); TORRENT_DEPRECATED void load_asnum_db(wchar_t const* file); #endif // TORRENT_USE_WSTRING // deprecated in 0.15 // use load_state and save_state instead TORRENT_DEPRECATED void load_state(entry const& ses_state , boost::uint32_t flags = 0xffffffff); TORRENT_DEPRECATED entry state() const; // deprecated in 1.1 TORRENT_DEPRECATED void load_state(lazy_entry const& ses_state , boost::uint32_t flags = 0xffffffff); #endif // TORRENT_NO_DEPRECATE // Sets a filter that will be used to reject and accept incoming as well // as outgoing connections based on their originating ip address. The // default filter will allow connections to any ip address. To build a // set of rules for which addresses are accepted and not, see ip_filter. // // Each time a peer is blocked because of the IP filter, a // peer_blocked_alert is generated. ``get_ip_filter()`` Returns the // ip_filter currently in the session. See ip_filter. void set_ip_filter(ip_filter const& f); ip_filter get_ip_filter() const; // apply port_filter ``f`` to incoming and outgoing peers. a port filter // will reject making outgoing peer connections to certain remote ports. // The main intention is to be able to avoid triggering certain // anti-virus software by connecting to SMTP, FTP ports. void set_port_filter(port_filter const& f); #ifndef TORRENT_NO_DEPRECATE // deprecated in 1.1, use settings_pack::peer_fingerprint instead TORRENT_DEPRECATED void set_peer_id(peer_id const& pid); // deprecated in 1.1.7. read settings_pack::peer_fingerprint instead TORRENT_DEPRECATED peer_id id() const; #endif // sets the key sent to trackers. If it's not set, it is initialized // by libtorrent. The key may be used by the tracker to identify the // peer potentially across you changing your IP. void set_key(int key); // built-in peer classes enum { global_peer_class_id, tcp_peer_class_id, local_peer_class_id }; // ``is_listening()`` will tell you whether or not the session has // successfully opened a listening port. If it hasn't, this function will // return false, and then you can set a new // settings_pack::listen_interfaces to try another interface and port to // bind to. // // ``listen_port()`` returns the port we ended up listening on. unsigned short listen_port() const; unsigned short ssl_listen_port() const; bool is_listening() const; // Sets the peer class filter for this session. All new peer connections // will take this into account and be added to the peer classes specified // by this filter, based on the peer's IP address. // // The ip-filter essentially maps an IP -> uint32. Each bit in that 32 // bit integer represents a peer class. The least significant bit // represents class 0, the next bit class 1 and so on. // // For more info, see ip_filter. // // For example, to make all peers in the range 200.1.1.0 - 200.1.255.255 // belong to their own peer class, apply the following filter: // // .. code:: c++ // // ip_filter f = ses.get_peer_class_filter(); // peer_class_t const my_class = ses.create_peer_class("200.1.x.x IP range"); // f.add_rule(make_address("200.1.1.0"), make_address("200.1.255.255") // , 1 << my_class); // ses.set_peer_class_filter(f); // // This setting only applies to new connections, it won't affect existing // peer connections. // // This function is limited to only peer class 0-31, since there are only // 32 bits in the IP range mapping. Only the set bits matter; no peer // class will be removed from a peer as a result of this call, peer // classes are only added. // // The ``peer_class`` argument cannot be greater than 31. The bitmasks // representing peer classes in the ``peer_class_filter`` are 32 bits. // // The ``get_peer_class_filter()`` function returns the current filter. // // For more information, see peer-classes_. void set_peer_class_filter(ip_filter const& f); ip_filter get_peer_class_filter() const; // Sets and gets the *peer class type filter*. This is controls automatic // peer class assignments to peers based on what kind of socket it is. // // It does not only support assigning peer classes, it also supports // removing peer classes based on socket type. // // The order of these rules being applied are: // // 1. peer-class IP filter // 2. peer-class type filter, removing classes // 3. peer-class type filter, adding classes // // For more information, see peer-classes_. void set_peer_class_type_filter(peer_class_type_filter const& f); peer_class_type_filter get_peer_class_type_filter() const; // Creates a new peer class (see peer-classes_) with the given name. The // returned integer is the new peer class identifier. Peer classes may // have the same name, so each invocation of this function creates a new // class and returns a unique identifier. // // Identifiers are assigned from low numbers to higher. So if you plan on // using certain peer classes in a call to set_peer_class_filter(), // make sure to create those early on, to get low identifiers. // // For more information on peer classes, see peer-classes_. peer_class_t create_peer_class(char const* name); // This call dereferences the reference count of the specified peer // class. When creating a peer class it's automatically referenced by 1. // If you want to recycle a peer class, you may call this function. You // may only call this function **once** per peer class you create. // Calling it more than once for the same class will lead to memory // corruption. // // Since peer classes are reference counted, this function will not // remove the peer class if it's still assigned to torrents or peers. It // will however remove it once the last peer and torrent drops their // references to it. // // There is no need to call this function for custom peer classes. All // peer classes will be properly destructed when the session object // destructs. // // For more information on peer classes, see peer-classes_. void delete_peer_class(peer_class_t cid); // These functions queries information from a peer class and updates the // configuration of a peer class, respectively. // // ``cid`` must refer to an existing peer class. If it does not, the // return value of ``get_peer_class()`` is undefined. // // ``set_peer_class()`` sets all the information in the // peer_class_info object in the specified peer class. There is no // option to only update a single property. // // A peer or torrent belonging to more than one class, the highest // priority among any of its classes is the one that is taken into // account. // // For more information, see peer-classes_. peer_class_info get_peer_class(peer_class_t cid); void set_peer_class(peer_class_t cid, peer_class_info const& pci); #ifndef TORRENT_NO_DEPRECATE // 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. enum listen_on_flags_t { // this is always on starting with 0.16.2 listen_reuse_address TORRENT_DEPRECATED_ENUM = 0x01, listen_no_system_port TORRENT_DEPRECATED_ENUM = 0x02 }; // deprecated in 0.16 // specify which interfaces to bind outgoing connections to // This has been moved to a session setting TORRENT_DEPRECATED void use_interfaces(char const* interfaces); // instead of using this, specify listen interface and port in // the settings_pack::listen_interfaces setting TORRENT_DEPRECATED void listen_on( std::pair const& port_range , error_code& ec , const char* net_interface = 0 , int flags = 0); #endif // flags to be passed in to remove_torrent(). enum options_t { // delete the files belonging to the torrent from disk. // including the part-file, if there is one delete_files = 1, // delete just the part-file associated with this torrent delete_partfile = 2 }; // flags to be passed in to the session constructor enum session_flags_t { // this will add common extensions like ut_pex, ut_metadata, lt_tex // smart_ban and possibly others. add_default_plugins = 1, // this will start features like DHT, local service discovery, UPnP // and NAT-PMP. start_default_features = 2 }; // ``remove_torrent()`` will close all peer connections associated with // the torrent and tell the tracker that we've stopped participating in // the swarm. This operation cannot fail. When it completes, you will // receive a torrent_removed_alert. // // The optional second argument ``options`` can be used to delete all the // files downloaded by this torrent. To do so, pass in the value // ``session::delete_files``. The removal of the torrent is asynchronous, // there is no guarantee that adding the same torrent immediately after // it was removed will not throw a libtorrent_exception exception. Once // the torrent is deleted, a torrent_deleted_alert is posted. void remove_torrent(const torrent_handle& h, int options = 0); #ifndef TORRENT_NO_DEPRECATE // deprecated in aio-branch // Sets the session settings and the packet encryption settings // respectively. See session_settings and pe_settings for more // information on available options. TORRENT_DEPRECATED void set_settings(session_settings const& s); TORRENT_DEPRECATED session_settings settings() const; // deprecated in libtorrent 1.1. use settings_pack instead TORRENT_DEPRECATED void set_pe_settings(pe_settings const& settings); TORRENT_DEPRECATED pe_settings get_pe_settings() const; #endif // Applies the settings specified by the settings_pack ``s``. This is an // asynchronous operation that will return immediately and actually apply // the settings to the main thread of libtorrent some time later. void apply_settings(settings_pack const& s); settings_pack get_settings() const; #ifndef TORRENT_NO_DEPRECATE // ``set_i2p_proxy`` sets the i2p_ proxy, and tries to open a persistant // connection to it. The only used fields in the proxy settings structs // are ``hostname`` and ``port``. // // ``i2p_proxy`` returns the current i2p proxy in use. // // .. _i2p: http://www.i2p2.de TORRENT_DEPRECATED void set_i2p_proxy(proxy_settings const& s); TORRENT_DEPRECATED proxy_settings i2p_proxy() const; // These functions sets and queries the proxy settings to be used for the // session. // // For more information on what settings are available for proxies, see // proxy_settings. If the session is not in anonymous mode, proxies that // aren't working or fail, will automatically be disabled and packets // will flow without using any proxy. If you want to enforce using a // proxy, even when the proxy doesn't work, enable anonymous_mode in // settings_pack. TORRENT_DEPRECATED void set_proxy(proxy_settings const& s); TORRENT_DEPRECATED proxy_settings proxy() const; // deprecated in 0.16 // Get the number of uploads. TORRENT_DEPRECATED int num_uploads() const; // Get the number of connections. This number also contains the // number of half open connections. TORRENT_DEPRECATED int num_connections() const; // deprecated in 0.15. TORRENT_DEPRECATED void set_peer_proxy(proxy_settings const& s); TORRENT_DEPRECATED void set_web_seed_proxy(proxy_settings const& s); TORRENT_DEPRECATED void set_tracker_proxy(proxy_settings const& s); TORRENT_DEPRECATED proxy_settings peer_proxy() const; TORRENT_DEPRECATED proxy_settings web_seed_proxy() const; TORRENT_DEPRECATED proxy_settings tracker_proxy() const; TORRENT_DEPRECATED void set_dht_proxy(proxy_settings const& s); TORRENT_DEPRECATED proxy_settings dht_proxy() const; // deprecated in 0.16 TORRENT_DEPRECATED int upload_rate_limit() const; TORRENT_DEPRECATED int download_rate_limit() const; TORRENT_DEPRECATED int local_upload_rate_limit() const; TORRENT_DEPRECATED int local_download_rate_limit() const; TORRENT_DEPRECATED int max_half_open_connections() const; TORRENT_DEPRECATED void set_local_upload_rate_limit(int bytes_per_second); TORRENT_DEPRECATED void set_local_download_rate_limit(int bytes_per_second); TORRENT_DEPRECATED void set_upload_rate_limit(int bytes_per_second); TORRENT_DEPRECATED void set_download_rate_limit(int bytes_per_second); TORRENT_DEPRECATED void set_max_uploads(int limit); TORRENT_DEPRECATED void set_max_connections(int limit); TORRENT_DEPRECATED void set_max_half_open_connections(int limit); TORRENT_DEPRECATED int max_connections() const; TORRENT_DEPRECATED int max_uploads() const; TORRENT_DEPRECATED void pop_alerts(std::deque* alerts); #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif TORRENT_DEPRECATED std::auto_ptr pop_alert(); #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #endif // Alerts is the main mechanism for libtorrent to report errors and // events. ``pop_alerts`` fills in the vector passed to it with pointers // to new alerts. The session still owns these alerts and they will stay // valid until the next time ``pop_alerts`` is called. You may not delete // the alert objects. // // It is safe to call ``pop_alerts`` from multiple different threads, as // long as the alerts themselves are not accessed once another thread // calls ``pop_alerts``. Doing this requires manual synchronization // between the popping threads. // // ``wait_for_alert`` will block the current thread for ``max_wait`` time // duration, or until another alert is posted. If an alert is available // at the time of the call, it returns immediately. The returned alert // pointer is the head of the alert queue. ``wait_for_alert`` does not // pop alerts from the queue, it merely peeks at it. The returned alert // will stay valid until ``pop_alerts`` is called twice. The first time // will pop it and the second will free it. // // If there is no alert in the queue and no alert arrives within the // specified timeout, ``wait_for_alert`` returns NULL. // // In the python binding, ``wait_for_alert`` takes the number of // milliseconds to wait as an integer. // // The alert queue in the session will not grow indefinitely. Make sure // to pop periodically to not miss notifications. To control the max // number of alerts that's queued by the session, see // ``settings_pack::alert_queue_size``. // // Some alerts are considered so important that they are posted even when // the alert queue is full. Some alerts are considered mandatory and cannot // be disabled by the ``alert_mask``. For instance, // save_resume_data_alert and save_resume_data_failed_alert are always // posted, regardless of the alert mask. // // To control which alerts are posted, set the alert_mask // (settings_pack::alert_mask). // // the ``set_alert_notify`` function lets the client set a function object // to be invoked every time the alert queue goes from having 0 alerts to // 1 alert. This function is called from within libtorrent, it may be the // main thread, or it may be from within a user call. The intention of // of the function is that the client wakes up its main thread, to poll // for more alerts using ``pop_alerts()``. If the notify function fails // to do so, it won't be called again, until ``pop_alerts`` is called for // some other reason. For instance, it could signal an eventfd, post a // message to an HWND or some other main message pump. The actual // retrieval of alerts should not be done in the callback. In fact, the // callback should not block. It should not perform any expensive work. // It really should just notify the main application thread. void pop_alerts(std::vector* alerts); alert* wait_for_alert(time_duration max_wait); void set_alert_notify(boost::function const& fun); #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/aux_/disable_warnings_push.hpp" TORRENT_DEPRECATED void set_severity_level(alert::severity_t s); // use the setting instead TORRENT_DEPRECATED size_t set_alert_queue_size_limit(size_t queue_size_limit_); // Changes the mask of which alerts to receive. By default only errors // are reported. ``m`` is a bitmask where each bit represents a category // of alerts. // // ``get_alert_mask()`` returns the current mask; // // See category_t enum for options. TORRENT_DEPRECATED void set_alert_mask(boost::uint32_t m); TORRENT_DEPRECATED boost::uint32_t get_alert_mask() const; // This sets a function to be called (from within libtorrent's netowrk // thread) every time an alert is posted. Since the function (``fun``) is // run in libtorrent's internal thread, it may not block. // // The main intention with this function is to support integration with // platform-dependent message queues or signalling systems. For instance, // on windows, one could post a message to an HNWD or on linux, write to // a pipe or an eventfd. TORRENT_DEPRECATED void set_alert_dispatch( boost::function)> const& fun); #include "libtorrent/aux_/disable_warnings_pop.hpp" // Starts and stops Local Service Discovery. This service will broadcast // the infohashes of all the non-private torrents on the local network to // look for peers on the same swarm within multicast reach. // // deprecated. use settings_pack::enable_lsd instead TORRENT_DEPRECATED void start_lsd(); TORRENT_DEPRECATED void stop_lsd(); // Starts and stops the UPnP service. When started, the listen port and // the DHT port are attempted to be forwarded on local UPnP router // devices. // // The upnp object returned by ``start_upnp()`` can be used to add and // remove arbitrary port mappings. Mapping status is returned through the // portmap_alert and the portmap_error_alert. The object will be valid // until ``stop_upnp()`` is called. See upnp-and-nat-pmp_. // // deprecated. use settings_pack::enable_upnp instead TORRENT_DEPRECATED void start_upnp(); TORRENT_DEPRECATED void stop_upnp(); // Starts and stops the NAT-PMP service. When started, the listen port // and the DHT port are attempted to be forwarded on the router through // NAT-PMP. // // The natpmp object returned by ``start_natpmp()`` can be used to add // and remove arbitrary port mappings. Mapping status is returned through // the portmap_alert and the portmap_error_alert. The object will be // valid until ``stop_natpmp()`` is called. See upnp-and-nat-pmp_. // // deprecated. use settings_pack::enable_natpmp instead TORRENT_DEPRECATED void start_natpmp(); TORRENT_DEPRECATED void stop_natpmp(); #endif // protocols used by add_port_mapping() enum protocol_type { udp = 1, tcp = 2 }; // add_port_mapping adds a port forwarding on UPnP and/or NAT-PMP, // whichever is enabled. The return value is a handle referring to the // port mapping that was just created. Pass it to delete_port_mapping() // to remove it. int add_port_mapping(protocol_type t, int external_port, int local_port); void delete_port_mapping(int handle); // This function is intended only for use by plugins. This type does // not have a stable API and should be relied on as little as possible. aux::session_impl* native_handle() const { return m_impl; } private: aux::session_impl* m_impl; }; } // namespace libtorrent #endif // TORRENT_SESSION_HANDLE_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/session_settings.hpp000066400000000000000000002124221351156116000256710ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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" #include "libtorrent/config.hpp" #include "libtorrent/settings_pack.hpp" #include "libtorrent/aux_/proxy_settings.hpp" #include #include #include #include namespace libtorrent { #ifndef TORRENT_NO_DEPRECATE typedef aux::proxy_settings proxy_settings; // This holds most of the session-wide settings in libtorrent. Pass this // to session::set_settings() to change the settings, initialize it from // session::get_settings() to get the current settings. struct TORRENT_EXPORT session_settings { // initializes the session_settings to the default settings. session_settings(std::string const& user_agent = "libtorrent/" LIBTORRENT_VERSION); ~session_settings(); #if __cplusplus >= 201103L session_settings(session_settings const&) = default; session_settings& operator=(session_settings const&) = default; #endif // automatically set to the libtorrent version you're using in order to // be forward binary compatible. This field should not be changed. int version; // the client identification to the tracker. The recommended format of // this string is: "ClientName/ClientVersion // libtorrent/libtorrentVersion". This name will not only be used when // making HTTP requests, but also when sending extended headers to peers // that support that extension. std::string user_agent; // the number of seconds the tracker connection will wait from when it // sent the request until it considers the tracker to have timed-out. // Default value is 60 seconds. int tracker_completion_timeout; // the number of seconds to wait to receive any data from the tracker. If // no data is received for this number of seconds, the tracker will be // considered as having timed out. If a tracker is down, this is the kind // of timeout that will occur. The default value is 20 seconds. 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 // // This is given in seconds. Default is 10 seconds. int stop_tracker_timeout; // the maximum number of bytes in a tracker response. If a response size // passes this number it will be rejected and the connection will be // closed. On gzipped responses this size is measured on the uncompressed // data. So, if you get 20 bytes of gzip response that'll expand to 2 // megs, it will be interrupted before the entire response has been // uncompressed (given your limit is lower than 2 megs). Default limit is // 1 megabyte. int tracker_maximum_response_length; // controls the number of seconds from a request is sent until it times // out if no piece response is returned. int piece_timeout; // the number of seconds one block (16kB) is expected to be received // within. If it's not, the block is requested from a different peer int request_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. int 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. i.e. no matter the // download speed, the number of outstanding requests will never exceed // this limit. 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 connection due to time out. This defaults to 120 seconds, // since that's what's specified in the protocol specification. After // half the time out, a keep alive message is sent. 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. This // value defaults to 20 seconds. int urlseed_timeout; // controls the pipelining with the web server. When using persistent // connections to HTTP 1.1 servers, the client is allowed to send more // requests before the first response is received. This number controls // the number of outstanding requests to use with url-seeds. Default is // 5. int urlseed_pipeline_size; // time to wait until a new retry takes place int urlseed_wait_retry; // sets the upper limit on the total number of files this session will // keep open. The reason why files are left open at all is that some anti // virus software hooks on every file close, and scans the file for // viruses. deferring the closing of the files will be the difference // between a usable system and a completely hogged down system. Most // operating systems also has a limit on the total number of file // descriptors a process may have open. It is usually a good idea to find // this limit and set the number of connections and the number of files // limits so their sum is slightly below it. int file_pool_size; // determines if connections from the same IP address as existing // connections should be rejected or not. Multiple connections from the // same IP address is not allowed by default, to prevent abusive behavior // by peers. It may be useful to allow such connections in cases where // simulations are run on the same machine, and all peers in a swarm has // the same IP address. bool allow_multiple_connections_per_ip; // the maximum times we try to connect to a peer before stop connecting // again. If a peer succeeds, its failcounter is reset. If a peer is // retrieved from a peer source (other than DHT) the failcount is // decremented by one, allowing another try. int max_failcount; // the number of seconds to wait to reconnect to a peer. this time is // multiplied with the failcount. int min_reconnect_time; // the number of seconds to wait after a connection attempt is initiated // to a peer until it is considered as having timed out. The default is // 10 seconds. This setting is especially important in case the number of // half-open connections are limited, since stale half-open connection // may delay the connection of other peers considerably. int peer_connect_timeout; #ifndef TORRENT_NO_DEPRECATE // deprecated, use set_peer_class_filter() instead // if set to true, upload, download and unchoke limits // are ignored for peers on the local network. bool ignore_limits_on_local_network; #endif // the number of connection attempts that are made per second. If a // number < 0 is specified, it will default to 200 connections per // second. If 0 is specified, it means don't make outgoing connections at // all. int connection_speed; // if this is set to true, have messages will be sent to peers that // already have the piece. This is typically not necessary, but it might // be necessary for collecting statistics in some cases. Default is // false. bool send_redundant_have; // prevents outgoing bitfields from being full. If the client is seed, a // few bits will be set to 0, and later filled in with have-messages. // This is an old attempt to prevent certain ISPs from stopping people // from seeding. bool lazy_bitfields; // if a peer is uninteresting and uninterested for longer than this // number of seconds, it will be disconnected. default is 10 minutes int inactivity_timeout; // the number of seconds between chokes/unchokes. On this interval, peers // are re-evaluated for being choked/unchoked. This is defined as 30 // seconds in the protocol, and it should be significantly longer than // what it takes for TCP to ramp up to it's max rate. int unchoke_interval; // the number of seconds between each *optimistic* unchoke. On this // timer, the currently optimistically unchoked peer will change. int optimistic_unchoke_interval; // the ip address passed along to trackers as the ``&ip=`` parameter. If // left as the default (an empty string), that parameter is omitted. Most // trackers ignore this argument. This is here for completeness for // edge-cases where it may be useful. std::string announce_ip; // the number of peers we want from each tracker request. It defines what // is sent as the ``&num_want=`` parameter to the tracker. Stopped // messages always send num_want=0. This setting control what to say in // the case where we actually want peers. int num_want; // specifies the number of pieces we need before we switch to rarest // first picking. This defaults to 4, which means the 4 first pieces in // any torrent are picked at random, the following pieces are picked in // rarest first order. int initial_picker_threshold; // the number of allowed pieces to send to choked peers that supports the // fast extensions int allowed_fast_set_size; // options for session_settings::suggest_mode. enum suggest_mode_t { // the default. will not send out suggest messages. no_piece_suggestions = 0, // send out suggest messages for the most recent pieces that are in // the read cache. suggest_read_cache = 1 }; // this determines which pieces will be suggested to peers suggest read // cache will make libtorrent suggest pieces that are fresh in the disk // read cache, to potentially lower disk access and increase the cache // hit ratio // // for options, see suggest_mode_t. int suggest_mode; // the maximum number of bytes a connection may have pending in the disk // write queue before its download rate is being throttled. This prevents // fast downloads to slow medias to allocate more memory indefinitely. // This should be set to at least 16 kB to not completely disrupt normal // downloads. If it's set to 0, you will be starving the disk thread and // nothing will be written to disk. this is a per session setting. // // When this limit is reached, the peer connections will stop reading // data from their sockets, until the disk thread catches up. Setting // this too low will severely limit your download rate. int max_queued_disk_bytes; #ifndef TORRENT_NO_DEPRECATE // not used anymore int max_queued_disk_bytes_low_watermark; #endif // the number of seconds to wait for a handshake response from a peer. If // no response is received within this time, the peer is disconnected. int handshake_timeout; // determines how the DHT is used. If this is true, the DHT will only be // used for torrents where all trackers in its tracker list has failed. // Either by an explicit error message or a time out. This is false by // default, which means the DHT is used by default regardless of if the // trackers fail or not. bool use_dht_as_fallback; // determines whether or not the torrent's piece hashes are kept in // memory after the torrent becomes a seed or not. If it is set to // ``true`` the hashes are freed once the torrent is a seed (they're not // needed anymore since the torrent won't download anything more). If // it's set to false they are not freed. If they are freed, the // torrent_info returned by get_torrent_info() will return an object that // may be incomplete, that cannot be passed back to async_add_torrent() // and add_torrent() for instance. bool free_torrent_hashes; // indicates whether or not the UPnP implementation should ignore any // broadcast response from a device whose address is not the configured // router for this machine. i.e. it's a way to not talk to other people's // routers by mistake. bool upnp_ignore_nonrouters; // This is the minimum send buffer target size (send buffer includes // bytes pending being read from disk). For good and snappy seeding // performance, set this fairly high, to at least fit a few blocks. This // is essentially the initial window size which will determine how fast // we can ramp up the send rate int send_buffer_low_watermark; // the upper limit of the send buffer low-watermark. // // if the send buffer has fewer bytes than this, we'll read another 16kB // block onto it. If set too small, upload rate capacity will suffer. If // set too high, memory will be wasted. The actual watermark may be lower // than this in case the upload rate is low, this is the upper limit. int send_buffer_watermark; // the current upload rate to a peer is multiplied by this factor to get // the send buffer watermark. The factor is specified as a percentage. // i.e. 50 indicates a factor of 0.5. // // This product is clamped to the send_buffer_watermark setting to not // exceed the max. For high speed upload, this should be set to a greater // value than 100. The default is 50. // // For high capacity connections, setting this higher can improve upload // performance and disk throughput. Setting it too high may waste RAM and // create a bias towards read jobs over write jobs. int send_buffer_watermark_factor; // the different choking algorithms available. Set // session_settings::choking_algorithm to one of these enum choking_algorithm_t { // the traditional choker with a fixed number of unchoke slots, as // specified by session::set_max_uploads().. fixed_slots_choker = 0, #ifndef TORRENT_NO_DEPRECATE // opens at least the number of slots as specified by // session::set_max_uploads() but opens up more slots if the upload // capacity is not saturated. This unchoker will work just like the // ``fixed_slot_choker`` if there's no global upload rate limit set. auto_expand_choker = 1, #endif // opens up unchoke slots based on the upload rate achieved to peers. // The more slots that are opened, the marginal upload rate required // to open up another slot increases. rate_based_choker = 1, // attempts to optimize download rate by finding the reciprocation // rate of each peer individually and prefers peers that gives the // highest *return on investment*. It still allocates all upload // capacity, but shuffles it around to the best peers first. For this // choker to be efficient, you need to set a global upload rate limit // session_settings::upload_rate_limit. For more information about // this choker, see the paper_. // // .. _paper: http://bittyrant.cs.washington.edu/#papers bittyrant_choker = 2 }; // specifies which algorithm to use to determine which peers to unchoke. // This setting replaces the deprecated settings ``auto_upload_slots`` // and ``auto_upload_slots_rate_based``. For options, see // choking_algorithm_t. int choking_algorithm; // the different choking algorithms available when seeding. Set // session_settings::seed_choking_algorithm to one of these enum seed_choking_algorithm_t { // round-robins the peers that are unchoked when seeding. This // distributes the upload bandwidht uniformly and fairly. It minimizes // the ability for a peer to download everything without // redistributing it. round_robin, // unchokes the peers we can send to the fastest. This might be a bit // more reliable in utilizing all available capacity. fastest_upload, // prioritizes peers who have just started or are just about to finish // the download. The intention is to force peers in the middle of the // download to trade with each other. anti_leech }; // controls the seeding unchoke behavior. For options, see // seed_choking_algorithm_t. int seed_choking_algorithm; // specifies if parole mode should be used. Parole mode means that peers // that participate in pieces that fail the hash check are put in a mode // where they are only allowed to download whole pieces. If the whole // piece a peer in parole mode fails the hash check, it is banned. If a // peer participates in a piece that passes the hash check, it is taken // out of parole mode. bool use_parole_mode; // the disk write and read cache. It is specified in units of 16 KiB // blocks. Buffers that are part of a peer's send or receive buffer also // count against this limit. Send and receive buffers will never be // denied to be allocated, but they will cause the actual cached blocks // to be flushed or evicted. If this is set to -1, the cache size is // automatically set to the amount of physical RAM available in the // machine divided by 8. If the amount of physical RAM cannot be // determined, it's set to 1024 (= 16 MiB). // // Disk buffers are allocated using a pool allocator, the number of // blocks that are allocated at a time when the pool needs to grow can be // specified in ``cache_buffer_chunk_size``. This defaults to 16 blocks. // Lower numbers saves memory at the expense of more heap allocations. It // must be at least 1. int cache_size; // this is the number of disk buffer blocks (16 kiB) that should be // allocated at a time. It must be at least 1. Lower number saves memory // at the expense of more heap allocations // setting this to zero means 'automatic', i.e. proportional // to the total disk cache size int cache_buffer_chunk_size; // the number of seconds a write cache entry sits idle in the cache // before it's forcefully flushed to disk. int cache_expiry; // when set to true (default), the disk cache is also used to cache // pieces read from disk. Blocks for writing pieces takes presedence. bool use_read_cache; bool use_write_cache; // this will make the disk cache never flush a write // piece if it would cause is to have to re-read it // once we want to calculate the piece hash bool dont_flush_write_cache; #ifndef TORRENT_NO_DEPRECATE // defaults to 0. If set to something greater than 0, the disk read cache // will not be evicted by cache misses and will explicitly be controlled // based on the rarity of pieces. Rare pieces are more likely to be // cached. This would typically be used together with ``suggest_mode`` // set to ``suggest_read_cache``. The value is the number of pieces to // keep in the read cache. If the actual read cache can't fit as many, it // will essentially be clamped. bool explicit_read_cache; // the number of seconds in between each refresh of a part of the // explicit read cache. Torrents take turns in refreshing and this is the // time in between each torrent refresh. Refreshing a torrent's explicit // read cache means scanning all pieces and picking a random set of the // rarest ones. There is an affinity to pick pieces that are already in // the cache, so that subsequent refreshes only swaps in pieces that are // rarer than whatever is in the cache at the time. int explicit_cache_interval; #endif // the buffer modes to use for reading and writing. Set // session_settings::disk_io_read_mode and disk_io_write_mode to one of // these. enum io_buffer_mode_t { // This is the default and files are opened normally, with the OS // caching reads and writes. enable_os_cache = 0, // This will open files in unbuffered mode for files where every read // and write would be sector aligned. Using aligned disk offsets is a // requirement on some operating systems. disable_os_cache_for_aligned_files = 1, // This opens all files in unbuffered mode (if allowed by the // operating system). Linux and Windows, for instance, require disk // offsets to be sector aligned, and in those cases, this option is // the same as ``disable_os_caches_for_aligned_files``. disable_os_cache = 2 }; // determines how files are opened when they're in read only mode versus // read and write mode. For options, see io_buffer_mode_t. // // One reason to disable caching is that it may help the operating system // from growing its file cache indefinitely. Since some OSes only allow // aligned files to be opened in unbuffered mode, It is recommended to // make the largest file in a torrent the first file (with offset 0) or // use pad files to align all files to piece boundaries. int disk_io_write_mode; int disk_io_read_mode; // allocate separate, contiguous, buffers for read and write calls. Only // used where writev/readv cannot be used will use more RAM but may // improve performance bool coalesce_reads; bool coalesce_writes; // if set to something other than (0, 0) is a range of ports used to bind // outgoing sockets to. This may be useful for users whose router allows // them to assign QoS classes to traffic based on its local port. It is a // range instead of a single port because of the problems with failing to // reconnect to peers if a previous socket to that peer and port is in // ``TIME_WAIT`` state. // //.. warning:: // setting outgoing ports will limit the ability to keep multiple // connections to the same client, even for different torrents. It is not // recommended to change this setting. Its main purpose is to use as an // escape hatch for cheap routers with QoS capability but can only // classify flows based on port numbers. int outgoing_port; int num_outgoing_ports; // determines the TOS byte set in the IP header of every packet sent to // peers (including web seeds). The default value for this is ``0x0`` (no // marking). One potentially useful TOS mark is ``0x20``, this represents // the *QBone scavenger service*. For more details, see QBSS_. // // .. _`QBSS`: http://qbone.internet2.edu/qbss/ char peer_tos; // for auto managed torrents, these are the limits they are subject to. // If there are too many torrents some of the auto managed ones will be // paused until some slots free up. // // ``active_dht_limit`` and ``active_tracker_limit`` limits the number of // torrents that will be active on the DHT and their tracker. If the // active limit is set higher than these numbers, some torrents will be // "active" in the sense that they will accept incoming connections, but // not announce on the DHT or their trackers. // // ``active_lsd_limit`` is the max number of torrents to announce to the // local network over the local service discovery protocol. By default // this is 80, which is no more than one announce every 5 seconds // (assuming the default announce interval of 5 minutes). // // ``active_limit`` is a hard limit on the number of active torrents. // This applies even to slow torrents. // // You can have more torrents *active*, even though they are not // announced to the DHT, lsd or their tracker. If some peer knows about // you for any reason and tries to connect, it will still be accepted, // unless the torrent is paused, which means it won't accept any // connections. // // ``active_downloads`` and ``active_seeds`` controls how many active // seeding and downloading torrents the queuing mechanism allows. The // target number of active torrents is ``min(active_downloads + // active_seeds, active_limit)``. ``active_downloads`` and // ``active_seeds`` are upper limits on the number of downloading // torrents and seeding torrents respectively. Setting the value to -1 // means unlimited. // // For example if there are 10 seeding torrents and 10 downloading // torrents, and ``active_downloads`` is 4 and ``active_seeds`` is 4, // there will be 4 seeds active and 4 downloading torrents. If the // settings are ``active_downloads`` = 2 and ``active_seeds`` = 4, then // there will be 2 downloading torrents and 4 seeding torrents active. // Torrents that are not auto managed are also counted against these // limits. If there are non-auto managed torrents that use up all the // slots, no auto managed torrent will be activated. int active_downloads; int active_seeds; int active_dht_limit; int active_tracker_limit; int active_lsd_limit; int active_limit; // prefer seeding torrents when determining which torrents to give active // slots to, the default is false which gives preference to downloading // torrents bool auto_manage_prefer_seeds; // if true, torrents without any payload transfers are not subject to the // ``active_seeds`` and ``active_downloads`` limits. This is intended to // make it more likely to utilize all available bandwidth, and avoid // having torrents that don't transfer anything block the active slots. bool dont_count_slow_torrents; // the number of seconds in between recalculating which torrents to // activate and which ones to queue int auto_manage_interval; // when a seeding torrent reaches either the share ratio (bytes up / // bytes down) or the seed time ratio (seconds as seed / seconds as // downloader) or the seed time limit (seconds as seed) it is considered // done, and it will leave room for other torrents the default value for // share ratio is 2 the default seed time ratio is 7, because that's a // common asymmetry ratio on connections. these are specified as // percentages // //.. note:: // This is an out-dated option that doesn't make much sense. It will be // removed in future versions of libtorrent float share_ratio_limit; // the seeding time / downloading time ratio limit for considering a // seeding torrent to have met the seed limit criteria. See queuing_. float seed_time_ratio_limit; // seed time limit is specified in seconds // // the limit on the time a torrent has been an active seed (specified in // seconds) before it is considered having met the seed limit criteria. // See queuing_. int seed_time_limit; // controls a feature where libtorrent periodically can disconnect the // least useful peers in the hope of connecting to better ones. // ``peer_turnover_interval`` controls the interval of this optimistic // disconnect. It defaults to every 5 minutes, and is specified in // seconds. // // ``peer_turnover`` Is the fraction of the peers that are disconnected. // This is a float where 1.f represents all peers an 0 represents no // peers. It defaults to 4% (i.e. 0.04f) // // ``peer_turnover_cutoff`` is the cut off trigger for optimistic // unchokes. If a torrent has more than this fraction of its connection // limit, the optimistic unchoke is triggered. This defaults to 90% (i.e. // 0.9f). int peer_turnover_interval; // the percentage of peers to disconnect every // turnover interval (if we're at the peer limit) // defaults to 4% // this is specified in percent float peer_turnover; // when we are connected to more than // limit * peer_turnover_cutoff peers // disconnect peer_turnover fraction // of the peers. It is specified in percent float peer_turnover_cutoff; // specifies whether libtorrent should close connections where both ends // have no utility in keeping the connection open. For instance if both // ends have completed their downloads, there's no point in keeping it // open. This defaults to ``true``. bool close_redundant_connections; // the number of seconds between scrapes of queued torrents (auto managed // and paused torrents). Auto managed torrents that are paused, are // scraped regularly in order to keep track of their downloader/seed // ratio. This ratio is used to determine which torrents to seed and // which to pause. int auto_scrape_interval; // the minimum number of seconds between any automatic scrape (regardless // of torrent). In case there are a large number of paused auto managed // torrents, this puts a limit on how often a scrape request is sent. int auto_scrape_min_interval; // the maximum number of peers in the list of known peers. These peers // are not necessarily connected, so this number should be much greater // than the maximum number of connected peers. Peers are evicted from the // cache when the list grows passed 90% of this limit, and once the size // hits the limit, peers are no longer added to the list. If this limit // is set to 0, there is no limit on how many peers we'll keep in the // peer list. int max_peerlist_size; // the max peer list size used for torrents that are paused. This default // to the same as ``max_peerlist_size``, but can be used to save memory // for paused torrents, since it's not as important for them to keep a // large peer list. int max_paused_peerlist_size; // the minimum allowed announce interval for a tracker. This is specified // in seconds, defaults to 5 minutes and is used as a sanity check on // what is returned from a tracker. It mitigates hammering misconfigured // trackers. int min_announce_interval; // If true, partial pieces are picked before pieces that are more rare. // If false, rare pieces are always prioritized, unless the number of // partial pieces is growing out of proportion. bool prioritize_partial_pieces; // the number of seconds a torrent is considered active after it was // started, regardless of upload and download speed. This is so that // newly started torrents are not considered inactive until they have a // fair chance to start downloading. int auto_manage_startup; // if set to true, the estimated TCP/IP overhead is drained from the rate // limiters, to avoid exceeding the limits with the total traffic bool rate_limit_ip_overhead; // controls how multi tracker torrents are treated. If this is set to // true, all trackers in the same tier are announced to in parallel. If // all trackers in tier 0 fails, all trackers in tier 1 are announced as // well. If it's set to false, the behavior is as defined by the multi // tracker specification. It defaults to false, which is the same // behavior previous versions of libtorrent has had as well. bool announce_to_all_trackers; // controls how multi tracker torrents are treated. When this is set to // true, one tracker from each tier is announced to. This is the uTorrent // behavior. This is false by default in order to comply with the // multi-tracker specification. bool announce_to_all_tiers; // true by default. It means that trackers may be rearranged in a way // that udp trackers are always tried before http trackers for the same // hostname. Setting this to fails means that the trackers' tier is // respected and there's no preference of one protocol over another. bool prefer_udp_trackers; // when this is set to true, a piece has to have been forwarded to a // third peer before another one is handed out. This is the traditional // definition of super seeding. bool strict_super_seeding; // the number of pieces to send to a peer, when seeding, before rotating // in another peer to the unchoke set. It defaults to 3 pieces, which // means that when seeding, any peer we've sent more than this number of // pieces to will be unchoked in favour of a choked peer. int seeding_piece_quota; // is a limit of the number of *sparse regions* in a torrent. A sparse // region is defined as a hole of pieces we have not yet downloaded, in // between pieces that have been downloaded. This is used as a hack for // windows vista which has a bug where you cannot write files with more // than a certain number of sparse regions. This limit is not hard, it // will be exceeded. Once it's exceeded, pieces that will maintain or // decrease the number of sparse regions are prioritized. To disable this // functionality, set this to 0. It defaults to 0 on all platforms except // windows. int max_sparse_regions; // if lock disk cache is set to true the disk cache that's in use, will // be locked in physical memory, preventing it from being swapped out. bool lock_disk_cache; // the number of piece requests we will reject in a row while a peer is // choked before the peer is considered abusive and is disconnected. int max_rejects; // specifies the buffer sizes set on peer sockets. 0 (which is the // default) means the OS default (i.e. don't change the buffer sizes). // The socket buffer sizes are changed using setsockopt() with // SOL_SOCKET/SO_RCVBUF and SO_SNDBUFFER. int recv_socket_buffer_size; int send_socket_buffer_size; // chooses between two ways of reading back piece data from disk when its // complete and needs to be verified against the piece hash. This happens // if some blocks were flushed to the disk out of order. Everything that // is flushed in order is hashed as it goes along. Optimizing for speed // will allocate space to fit all the remaining, unhashed, part of // the piece, reads the data into it in a single call and hashes it. This // is the default. If ``optimizing_hashing_for_speed`` is false, a single // block will be allocated (16 kB), and the unhashed parts of the piece // are read, one at a time, and hashed in this single block. This is // appropriate on systems that are memory constrained. bool optimize_hashing_for_speed; // the number of milliseconds to sleep // in between disk read operations when checking torrents. This defaults // to 0, but can be set to higher numbers to slow down the rate at which // data is read from the disk while checking. This may be useful for // background tasks that doesn't matter if they take a bit longer, as long // as they leave disk I/O time for other processes. int file_checks_delay_per_block; // the disk cache algorithms available. Set // session_settings::disk_cache_algorithm to one of these. enum disk_cache_algo_t { // This flushes the entire piece, in the write cache, that was least // recently written to. lru, // will flush the largest sequences of contiguous blocks from the // write cache, regardless of the piece's last use time. largest_contiguous, // will prioritize flushing blocks that will avoid having to read them // back in to verify the hash of the piece once it's done. This is // especially useful for high throughput setups, where reading from // the disk is especially expensive. avoid_readback }; // tells the disk I/O thread which cache flush algorithm to use. // This is specified by the disk_cache_algo_t enum. disk_cache_algo_t disk_cache_algorithm; // the number of blocks to read into the read cache when a read cache // miss occurs. Setting this to 0 is essentially the same thing as // disabling read cache. The number of blocks read into the read cache is // always capped by the piece boundary. // // When a piece in the write cache has ``write_cache_line_size`` // contiguous blocks in it, they will be flushed. Setting this to 1 // effectively disables the write cache. int read_cache_line_size; // whenever a contiguous range of this many blocks is found in the write // cache, it is flushed immediately int write_cache_line_size; // the number of seconds from a disk write errors occur on a torrent // until libtorrent will take it out of the upload mode, to test if the // error condition has been fixed. // // libtorrent will only do this automatically for auto managed torrents. // // You can explicitly take a torrent out of upload only mode using // set_upload_mode(). int optimistic_disk_retry; // controls if downloaded pieces are verified against the piece hashes in // the torrent file or not. The default is false, i.e. to verify all // downloaded data. It may be useful to turn this off for performance // profiling and simulation scenarios. Do not disable the hash check for // regular bittorrent clients. bool disable_hash_checks; // if this is true, disk read operations are sorted by their physical // offset on disk before issued to the operating system. This is useful // if async I/O is not supported. It defaults to true if async I/O is not // supported and fals otherwise. disk I/O operations are likely to be // reordered regardless of this setting when async I/O is supported by // the OS. bool allow_reordered_disk_operations; // if this is true, i2p torrents are allowed to also get peers from other // sources than the tracker, and connect to regular IPs, not providing // any anonymization. This may be useful if the user is not interested in // the anonymization of i2p, but still wants to be able to connect to i2p // peers. bool allow_i2p_mixed; // the max number of suggested piece indices received from a peer that's // remembered. If a peer floods suggest messages, this limit prevents // libtorrent from using too much RAM. It defaults to 10. int max_suggest_pieces; // If set to true (it defaults to false), piece requests that have been // skipped enough times when piece messages are received, will be // considered lost. Requests are considered skipped when the returned // piece messages are re-ordered compared to the order of the requests. // This was an attempt to get out of dead-locks caused by BitComet peers // silently ignoring some requests. It may cause problems at high rates, // and high level of reordering in the uploading peer, that's why it's // disabled by default. bool drop_skipped_requests; // determines if the disk I/O should use a normal // or low priority policy. This defaults to true, which means that // it's low priority by default. Other processes doing disk I/O will // normally take priority in this mode. This is meant to improve the // overall responsiveness of the system while downloading in the // background. For high-performance server setups, this might not // be desirable. bool low_prio_disk; // the time between local // network announces for a torrent. By default, when local service // discovery is enabled a torrent announces itself every 5 minutes. // This interval is specified in seconds. int local_service_announce_interval; // the number of seconds between announcing // torrents to the distributed hash table (DHT). This is specified to // be 15 minutes which is its default. int dht_announce_interval; // the number of seconds libtorrent // will keep UDP tracker connection tokens around for. This is specified // to be 60 seconds, and defaults to that. The higher this value is, the // fewer packets have to be sent to the UDP tracker. In order for higher // values to work, the tracker needs to be configured to match the // expiration time for tokens. int udp_tracker_token_expiry; // if this is set to true, read cache blocks // that are hit by peer read requests are removed from the disk cache // to free up more space. This is useful if you don't expect the disk // cache to create any cache hits from other peers than the one who // triggered the cache line to be read into the cache in the first place. bool volatile_read_cache; // enables the disk cache to adjust the size // of a cache line generated by peers to depend on the upload rate // you are sending to that peer. The intention is to optimize the RAM // usage of the cache, to read ahead further for peers that you're // sending faster to. bool guided_read_cache; // the minimum number of seconds any read cache line is kept in the // cache. This defaults to one second but may be greater if // ``guided_read_cache`` is enabled. Having a lower bound on the time a // cache line stays in the cache is an attempt to avoid swapping the same // pieces in and out of the cache in case there is a shortage of spare // cache space. int default_cache_min_age; // the number of optimistic unchoke slots to use. It defaults to 0, which // means automatic. Having a higher number of optimistic unchoke slots // mean you will find the good peers faster but with the trade-off to use // up more bandwidth. When this is set to 0, libtorrent opens up 20% of // your allowed upload slots as optimistic unchoke slots. int num_optimistic_unchoke_slots; // this is a linux-only option and passes in the ``O_NOATIME`` to // ``open()`` when opening files. This may lead to some disk performance // improvements. bool no_atime_storage; // the assumed reciprocation rate from peers when using the BitTyrant // choker. This defaults to 14 kiB/s. If set too high, you will // over-estimate your peers and be more altruistic while finding the true // reciprocation rate, if it's set too low, you'll be too stingy and // waste finding the true reciprocation rate. int default_est_reciprocation_rate; // specifies how many percent the estimated reciprocation rate should be // increased by each unchoke interval a peer is still choking us back. // This defaults to 20%. This only applies to the BitTyrant choker. int increase_est_reciprocation_rate; // specifies how many percent the estimated reciprocation rate should be // decreased by each unchoke interval a peer unchokes us. This default to // 3%. This only applies to the BitTyrant choker. int decrease_est_reciprocation_rate; // defaults to false. If a torrent has been paused by the auto managed // feature in libtorrent, i.e. the torrent is paused and auto managed, // this feature affects whether or not it is automatically started on an // incoming connection. The main reason to queue torrents, is not to make // them unavailable, but to save on the overhead of announcing to the // trackers, the DHT and to avoid spreading one's unchoke slots too thin. // If a peer managed to find us, even though we're no in the torrent // anymore, this setting can make us start the torrent and serve it. bool incoming_starts_queued_torrents; // when set to true, the downloaded counter sent to trackers will include // the actual number of payload bytes downloaded including redundant // bytes. If set to false, it will not include any redundancy bytes bool report_true_downloaded; // defaults to true, and controls when a block may be requested twice. If // this is ``true``, a block may only be requested twice when there's ay // least one request to every piece that's left to download in the // torrent. This may slow down progress on some pieces sometimes, but it // may also avoid downloading a lot of redundant bytes. If this is // ``false``, libtorrent attempts to use each peer connection to its max, // by always requesting something, even if it means requesting something // that has been requested from another peer already. bool strict_end_game_mode; // if set to true, the local peer discovery (or Local Service Discovery) // will not only use IP multicast, but also broadcast its messages. This // can be useful when running on networks that don't support multicast. // Since broadcast messages might be expensive and disruptive on // networks, only every 8th announce uses broadcast. bool broadcast_lsd; // these all determines if libtorrent should attempt to make outgoing // connections of the specific type, or allow incoming connection. By // default all of them are enabled. bool enable_outgoing_utp; bool enable_incoming_utp; bool enable_outgoing_tcp; bool enable_incoming_tcp; // the max number of peers we accept from pex messages from a single peer. // this limits the number of concurrent peers any of our peers claims to // be connected to. If they claim to be connected to more than this, we'll // ignore any peer that exceeds this limit int max_pex_peers; // determines if the storage, when loading resume data files, should // verify that the file modification time with the timestamps in the // resume data. This defaults to false, which means timestamps are taken // into account, and resume data is less likely to accepted (torrents are // more likely to be fully checked when loaded). It might be useful to // set this to true if your network is faster than your disk, and it // would be faster to redownload potentially missed pieces than to go // through the whole storage to look for them. bool ignore_resume_timestamps; // determines if the storage should check the whole files when resume // data is incomplete or missing or whether it should simply assume we // don't have any of the data. By default, this is determined by the // existence of any of the files. By setting this setting to true, the // files won't be checked, but will go straight to download mode. bool no_recheck_incomplete_resume; // defaults to false. When set to true, the client tries to hide its // identity to a certain degree. The peer-ID will no longer include the // client's fingerprint. The user-agent will be reset to an empty string. // It will also try to not leak other identifying information, such as // your local listen port, your IP etc. // // If you're using I2P, a VPN or a proxy, it might make sense to enable // anonymous mode. bool anonymous_mode; // disables any communication that's not going over a proxy. Enabling // this requires a proxy to be configured as well, see // ``set_proxy_settings``. The listen sockets are closed, and incoming // connections will only be accepted through a SOCKS5 or I2P proxy (if a // peer proxy is set up and is run on the same machine as the tracker // proxy). This setting also disabled peer country lookups, since those // are done via DNS lookups that aren't supported by proxies. bool force_proxy; // specifies the number of milliseconds between internal ticks. This is // the frequency with which bandwidth quota is distributed to peers. It // should not be more than one second (i.e. 1000 ms). Setting this to a // low value (around 100) means higher resolution bandwidth quota // distribution, setting it to a higher value saves CPU cycles. int tick_interval; // specifies whether downloads from web seeds is reported to the // tracker or not. Defaults to on bool report_web_seed_downloads; // specifies the target share ratio for share mode torrents. This // defaults to 3, meaning we'll try to upload 3 times as much as we // download. Setting this very high, will make it very conservative and // you might end up not downloading anything ever (and not affecting your // share ratio). It does not make any sense to set this any lower than 2. // For instance, if only 3 peers need to download the rarest piece, it's // impossible to download a single piece and upload it more than 3 times. // If the share_mode_target is set to more than 3, nothing is downloaded. int share_mode_target; // sets the session-global limits of upload and download rate limits, in // bytes per second. The local rates refer to peers on the local network. // By default peers on the local network are not rate limited. // // These rate limits are only used for local peers (peers within the same // subnet as the client itself) and it is only used when // ``session_settings::ignore_limits_on_local_network`` is set to true // (which it is by default). These rate limits default to unthrottled, // but can be useful in case you want to treat local peers // preferentially, but not quite unthrottled. // // A value of 0 means unlimited. int upload_rate_limit; int download_rate_limit; int local_upload_rate_limit; int local_download_rate_limit; // sets the rate limit on the DHT. This is specified in bytes per second // and defaults to 4000. For busy boxes with lots of torrents that // requires more DHT traffic, this should be raised. int dht_upload_rate_limit; // the max number of unchoked peers in the session. The number of unchoke // slots may be ignored depending on what ``choking_algorithm`` is set // to. A value of -1 means infinite. int unchoke_slots_limit; // sets the maximum number of half-open connections libtorrent will have // when connecting to peers. A half-open connection is one where // connect() has been called, but the connection still hasn't been // established (nor failed). Windows XP Service Pack 2 sets a default, // system wide, limit of the number of half-open connections to 10. So, // this limit can be used to work nicer together with other network // applications on that system. The default is to have no limit, and // passing -1 as the limit, means to have no limit. When limiting the // number of simultaneous connection attempts, peers will be put in a // queue waiting for their turn to get connected. int half_open_limit; // sets a global limit on the number of connections opened. The number of // connections is set to a hard minimum of at least two per torrent, so // if you set a too low connections limit, and open too many torrents, // the limit will not be met. int connections_limit; // the number of extra incoming connections allowed temporarily, in order // to support replacing peers int connections_slack; // the target delay for uTP sockets in milliseconds. A high value will // make uTP connections more aggressive and cause longer queues in the // upload bottleneck. It cannot be too low, since the noise in the // measurements would cause it to send too slow. The default is 50 // milliseconds. int utp_target_delay; // the number of bytes the uTP congestion window can increase at the most // in one RTT. This defaults to 300 bytes. If this is set too high, the // congestion controller reacts too hard to noise and will not be stable, // if it's set too low, it will react slow to congestion and not back off // as fast. int utp_gain_factor; // the shortest allowed uTP socket timeout, specified in milliseconds. // This defaults to 500 milliseconds. The timeout depends on the RTT of // the connection, but is never smaller than this value. A connection // times out when every packet in a window is lost, or when a packet is // lost twice in a row (i.e. the resent packet is lost as well). // // The shorter the timeout is, the faster the connection will recover // from this situation, assuming the RTT is low enough. int utp_min_timeout; // the number of SYN packets that are sent (and timed out) before // giving up and closing the socket. int utp_syn_resends; // the number of resent packets sent on a closed socket before giving up int utp_fin_resends; // the number of times a packet is sent (and lossed or timed out) // before giving up and closing the connection. int utp_num_resends; // the number of milliseconds of timeout for the initial SYN packet for // uTP connections. For each timed out packet (in a row), the timeout is // doubled. int utp_connect_timeout; #ifndef TORRENT_NO_DEPRECATE // number of milliseconds of delaying ACKing packets the most int utp_delayed_ack; #endif // controls if the uTP socket manager is allowed to increase the socket // buffer if a network interface with a large MTU is used (such as // loopback or ethernet jumbo frames). This defaults to true and might // improve uTP throughput. For RAM constrained systems, disabling this // typically saves around 30kB in user space and probably around 400kB in // kernel socket buffers (it adjusts the send and receive buffer size on // the kernel socket, both for IPv4 and IPv6). bool utp_dynamic_sock_buf; // controls how the congestion window is changed when a packet loss is // experienced. It's specified as a percentage multiplier for ``cwnd``. // By default it's set to 50 (i.e. cut in half). Do not change this value // unless you know what you're doing. Never set it higher than 100. int utp_loss_multiplier; // the options for session_settings::mixed_mode_algorithm. enum bandwidth_mixed_algo_t { // disables the mixed mode bandwidth balancing prefer_tcp = 0, // does not throttle uTP, throttles TCP to the same proportion // of throughput as there are TCP connections peer_proportional = 1 }; // determines how to treat TCP connections when there are uTP // connections. Since uTP is designed to yield to TCP, there's an // inherent problem when using swarms that have both TCP and uTP // connections. If nothing is done, uTP connections would often be // starved out for bandwidth by the TCP connections. This mode is // ``prefer_tcp``. The ``peer_proportional`` mode simply looks at the // current throughput and rate limits all TCP connections to their // proportional share based on how many of the connections are TCP. This // works best if uTP connections are not rate limited by the global rate // limiter, see rate_limit_utp. // // see bandwidth_mixed_algo_t for options. int mixed_mode_algorithm; #ifndef TORRENT_NO_DEPRECATE // deprecated, use set_peer_class_filter() instead // set to true if uTP connections should be rate limited // defaults to false bool rate_limit_utp; #endif // the value passed in to listen() for the listen socket. It is the // number of outstanding incoming connections to queue up while we're not // actively waiting for a connection to be accepted. The default is 5 // which should be sufficient for any normal client. If this is a high // performance server which expects to receive a lot of connections, or // used in a simulator or test, it might make sense to raise this number. // It will not take affect until listen_on() is called again (or for the // first time). int listen_queue_size; // if true, the ``&ip=`` argument in tracker requests (unless otherwise // specified) will be set to the intermediate IP address, if the user is // double NATed. If ther user is not double NATed, this option has no // affect. bool announce_double_nat; // the number of peers to try to connect to immediately when the first // tracker response is received for a torrent. This is a boost to given // to new torrents to accelerate them starting up. The normal connect // scheduler is run once every second, this allows peers to be connected // immediately instead of waiting for the session tick to trigger // connections. int torrent_connect_boost; // determines if seeding (and finished) torrents should attempt to make // outgoing connections or not. By default this is true. It may be set to // false in very specific applications where the cost of making outgoing // connections is high, and there are no or small benefits of doing so. // For instance, if no nodes are behind a firewall or a NAT, seeds don't // need to make outgoing connections. bool seeding_outgoing_connections; // if true (which is the default), libtorrent will not connect to any // peers on privileged ports (<= 1023). This can mitigate using // bittorrent swarms for certain DDoS attacks. bool no_connect_privileged_ports; // the maximum number of alerts queued up internally. If alerts are not // popped, the queue will eventually fill up to this level. This defaults // to 1000. int alert_queue_size; // the maximum allowed size (in bytes) to be received // by the metadata extension, i.e. magnet links. It defaults to 1 MiB. int max_metadata_size; // true by default, which means the number of connection attempts per // second may be limited to below the ``connection_speed``, in case we're // close to bump up against the limit of number of connections. The // intention of this setting is to more evenly distribute our connection // attempts over time, instead of attempting to connect in batches, and // timing them out in batches. bool smooth_connects; // defaults to false. When set to true, web connections will include a // user-agent with every request, as opposed to just the first request in // a connection. bool always_send_user_agent; // defaults to true. It determines whether the IP filter applies to // trackers as well as peers. If this is set to false, trackers are // exempt from the IP filter (if there is one). If no IP filter is set, // this setting is irrelevant. bool apply_ip_filter_to_trackers; // used to avoid starvation of read jobs in the disk I/O thread. By // default, read jobs are deferred, sorted by physical disk location and // serviced once all write jobs have been issued. In scenarios where the // download rate is enough to saturate the disk, there's a risk the read // jobs will never be serviced. With this setting, every *x* write job, // issued in a row, will instead pick one read job off of the sorted // queue, where *x* is ``read_job_every``. int read_job_every; // defaults to true and will attempt to optimize disk reads by giving the // operating system heads up of disk read requests as they are queued in // the disk job queue. This gives a significant performance boost for // seeding. bool use_disk_read_ahead; // determines whether or not to lock files which libtorrent is // downloading to or seeding from. This is implemented using // ``fcntl(F_SETLK)`` on unix systems and by not passing in // ``SHARE_READ`` and ``SHARE_WRITE`` on windows. This might prevent 3rd // party processes from corrupting the files under libtorrent's feet. bool lock_files; // the number of threads to use for hash checking of pieces // defaults to 1. If set to 0, the disk thread is used for hashing int hashing_threads; // the number of blocks to keep outstanding at any given time when // checking torrents. Higher numbers give faster re-checks but uses // more memory. Specified in number of 16 kiB blocks int checking_mem_usage; // if set to > 0, pieces will be announced to other peers before they are // fully downloaded (and before they are hash checked). The intention is // to gain 1.5 potential round trip times per downloaded piece. When // non-zero, this indicates how many milliseconds in advance pieces // should be announced, before they are expected to be completed. int predictive_piece_announce; // when false, bytes off the socket is received directly into the disk // buffer. This requires many more calls to recv(). When using a // contiguous recv buffer, the download rate can be much higher bool contiguous_recv_buffer; //#error this should not be an option, it should depend on whether or not we're seeding or downloading // for some aio back-ends, the number of io-threads to use int aio_threads; // for some aio back-ends, the max number of outstanding jobs int aio_max; // the number of threads to use to call async_write_some on peer sockets. // When seeding at extremely high speeds, using 2 or more threads here // may make sense. Also when using SSL peer connections int network_threads; // if this is set, it is interpreted as a file path to where to create an // mmaped file to back the disk cache. this is mostly useful to introduce // another caching layer between RAM and hard drives. Typically you would // point this to an SSD drive. std::string mmap_cache; // sets the listen port for SSL connections. If this is set to 0, no SSL // listen port is opened. Otherwise a socket is opened on this port. This // setting is only taken into account when opening the regular listen // port, and won't re-open the listen socket simply by changing this // setting. // // if this is 0, outgoing SSL connections are disabled // // It defaults to port 4433. int ssl_listen; // ``tracker_backoff`` determines how aggressively to back off from // retrying failing trackers. This value determines *x* in the following // formula, determining the number of seconds to wait until the next // retry: // // delay = 5 + 5 * x / 100 * fails^2 // // It defaults to 250. // // This setting may be useful to make libtorrent more or less aggressive // in hitting trackers. // int tracker_backoff; // enables banning web seeds. By default, web seeds that send corrupt // data are banned. bool ban_web_seeds; // specifies the max number of bytes to receive into RAM buffers when // downloading stuff over HTTP. Specifically when specifying a URL to a // .torrent file when adding a torrent or when announcing to an HTTP // tracker. The default is 2 MiB. int max_http_recv_buffer_size; // enables or disables the share mode extension. This is enabled by // default. bool support_share_mode; // enables or disables the merkle tree torrent support. This is enabled // by default. bool support_merkle_torrents; // enables or disables reporting redundant bytes to the tracker. This is // enabled by default. bool report_redundant_bytes; // the version string to advertise for this client in the peer protocol // handshake. If this is empty the user_agent is used std::string handshake_client_version; // if this is true, the disk cache uses a pool allocator for disk cache // blocks. Enabling this improves performance of the disk cache with the // side effect that the disk cache is less likely and slower at returning // memory to the kernel when cache pressure is low. bool use_disk_cache_pool; // the download and upload rate limits for a torrent to be considered // active by the queuing mechanism. A torrent whose download rate is less // than ``inactive_down_rate`` and whose upload rate is less than // ``inactive_up_rate`` for ``auto_manage_startup`` seconds, is // considered inactive, and another queued torrent may be startert. // This logic is disabled if ``dont_count_slow_torrents`` is false. int inactive_down_rate; int inactive_up_rate; }; #endif // structure used to hold configuration options for the DHT // // The ``dht_settings`` struct used to contain a ``service_port`` member to // control which port the DHT would listen on and send messages from. This // field is deprecated and ignored. libtorrent always tries to open the UDP // socket on the same port as the TCP socket. struct TORRENT_EXPORT dht_settings { // initialized dht_settings to the default values dht_settings() : max_peers_reply(100) , search_branching(5) #ifndef TORRENT_NO_DEPRECATE , service_port(0) #endif , max_fail_count(20) , max_torrents(2000) , max_dht_items(700) , max_peers(5000) , max_torrent_search_reply(20) , restrict_routing_ips(true) , restrict_search_ips(true) , extended_routing_table(true) , aggressive_lookups(true) , privacy_lookups(false) , enforce_node_id(false) , ignore_dark_internet(true) , block_timeout(5 * 60) , block_ratelimit(5) , read_only(false) , item_lifetime(0) {} // the maximum number of peers to send in a reply to ``get_peers`` int max_peers_reply; // the number of concurrent search request the node will send when // announcing and refreshing the routing table. This parameter is called // alpha in the kademlia paper int search_branching; #ifndef TORRENT_NO_DEPRECATE // the listen port for the dht. This is a UDP port. zero means use the // same as the tcp interface int service_port; #endif // the maximum number of failed tries to contact a node before it is // removed from the routing table. If there are known working nodes that // are ready to replace a failing node, it will be replaced immediately, // this limit is only used to clear out nodes that don't have any node // that can replace them. int max_fail_count; // the total number of torrents to track from the DHT. This is simply an // upper limit to make sure malicious DHT nodes cannot make us allocate // an unbounded amount of memory. int max_torrents; // max number of items the DHT will store int max_dht_items; // the max number of peers to store per torrent (for the DHT) int max_peers; // the max number of torrents to return in a torrent search query to the // DHT int max_torrent_search_reply; // determines if the routing table entries should restrict entries to one // per IP. This defaults to true, which helps mitigate some attacks on // the DHT. It prevents adding multiple nodes with IPs with a very close // CIDR distance. // // when set, nodes whose IP address that's in the same /24 (or /64 for // IPv6) range in the same routing table bucket. This is an attempt to // mitigate node ID spoofing attacks also restrict any IP to only have a // single entry in the whole routing table bool restrict_routing_ips; // determines if DHT searches should prevent adding nodes with IPs with // very close CIDR distance. This also defaults to true and helps // mitigate certain attacks on the DHT. bool restrict_search_ips; // makes the first buckets in the DHT routing table fit 128, 64, 32 and // 16 nodes respectively, as opposed to the standard size of 8. All other // buckets have size 8 still. bool extended_routing_table; // slightly changes the lookup behavior in terms of how many outstanding // requests we keep. Instead of having branch factor be a hard limit, we // always keep *branch factor* outstanding requests to the closest nodes. // i.e. every time we get results back with closer nodes, we query them // right away. It lowers the lookup times at the cost of more outstanding // queries. bool aggressive_lookups; // when set, perform lookups in a way that is slightly more expensive, // but which minimizes the amount of information leaked about you. bool privacy_lookups; // when set, node's whose IDs that are not correctly generated based on // its external IP are ignored. When a query arrives from such node, an // error message is returned with a message saying "invalid node ID". bool enforce_node_id; // ignore DHT messages from parts of the internet we wouldn't expect to // see any traffic from bool ignore_dark_internet; // the number of seconds a DHT node is banned if it exceeds the rate // limit. The rate limit is averaged over 10 seconds to allow for bursts // above the limit. int block_timeout; // the max number of packets per second a DHT node is allowed to send // without getting banned. int block_ratelimit; // when set, the other nodes won't keep this node in their routing // tables, it's meant for low-power and/or ephemeral devices that // cannot support the DHT, it is also useful for mobile devices which // are sensitive to network traffic and battery life. // this node no longer responds to 'query' messages, and will place a // 'ro' key (value = 1) in the top-level message dictionary of outgoing // query messages. bool read_only; // the number of seconds a immutable/mutable item will be expired. // default is 0, means never expires. int item_lifetime; }; #ifndef TORRENT_NO_DEPRECATE // The ``pe_settings`` structure is used to control the settings related // to peer protocol encryption. struct TORRENT_EXPORT pe_settings { // initializes the encryption settings with the default vaues pe_settings() : out_enc_policy(enabled) , in_enc_policy(enabled) , allowed_enc_level(both) , prefer_rc4(false) {} // the encoding policy options for use with pe_settings::out_enc_policy // and pe_settings::in_enc_policy. enum enc_policy { // Only encrypted connections are allowed. Incoming connections that // are not encrypted are closed and if the encrypted outgoing // connection fails, a non-encrypted retry will not be made. forced, // encrypted connections are enabled, but non-encrypted connections // are allowed. An incoming non-encrypted connection will be accepted, // and if an outgoing encrypted connection fails, a non- encrypted // connection will be tried. enabled, // only non-encrypted connections are allowed. disabled }; // the encryption levels, to be used with pe_settings::allowed_enc_level. enum enc_level { // use only plaintext encryption plaintext = 1, // use only rc4 encryption rc4 = 2, // allow both both = 3 }; // control the settings for incoming // and outgoing connections respectively. // see enc_policy enum for the available options. boost::uint8_t out_enc_policy; boost::uint8_t in_enc_policy; // determines the encryption level of the // connections. This setting will adjust which encryption scheme is // offered to the other peer, as well as which encryption scheme is // selected by the client. See enc_level enum for options. boost::uint8_t allowed_enc_level; // if the allowed encryption level is both, setting this to // true will prefer rc4 if both methods are offered, plaintext // otherwise bool prefer_rc4; }; #endif // TORRENT_NO_DEPRECATE } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/session_stats.hpp000066400000000000000000000051131351156116000251640ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_STATS_HPP_INCLUDED #define TORRENT_SESSION_STATS_HPP_INCLUDED #include "libtorrent/config.hpp" #include namespace libtorrent { // describes one statistics metric from the session. For more information, // see the session-statistics_ section. struct TORRENT_EXPORT stats_metric { char const* name; int value_index; enum metric_type_t { type_counter, type_gauge }; metric_type_t type; }; // This free function returns the list of available metrics exposed by // libtorrent's statistics API. Each metric has a name and a *value index*. // The value index is the index into the array in session_stats_alert where // this metric's value can be found when the session stats is sampled (by // calling post_session_stats()). TORRENT_EXPORT std::vector session_stats_metrics(); // given a name of a metric, this function returns the counter index of it, // or -1 if it could not be found. The counter index is the index into the // values array returned by session_stats_alert. TORRENT_EXPORT int find_metric_idx(char const* name); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/session_status.hpp000066400000000000000000000211271351156116000253540ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include "libtorrent/config.hpp" #include #ifndef TORRENT_NO_DEPRECATE // for dht_lookup and dht_routing_bucket #include "libtorrent/alert_types.hpp" #endif namespace libtorrent { #ifndef TORRENT_NO_DEPRECATE // holds counters and gauges for the uTP sockets // deprecated in 1.1 in favor of session_stats counters, which is a more // flexible, extensible and perfromant mechanism for stats. struct TORRENT_EXPORT utp_status { // gauges. These are snapshots of the number of // uTP sockets in each respective state int num_idle; int num_syn_sent; int num_connected; int num_fin_sent; int num_close_wait; // These are monotonically increasing // and cumulative counters for their respective event. boost::uint64_t packet_loss; boost::uint64_t timeout; boost::uint64_t packets_in; boost::uint64_t packets_out; boost::uint64_t fast_retransmit; boost::uint64_t packet_resend; boost::uint64_t samples_above_target; boost::uint64_t samples_below_target; boost::uint64_t payload_pkts_in; boost::uint64_t payload_pkts_out; boost::uint64_t invalid_pkts_in; boost::uint64_t redundant_pkts_in; }; // contains session wide state and counters // deprecated in 1.1 in favor of session_stats counters, which is a more // flexible, extensible and perfromant mechanism for stats. struct TORRENT_EXPORT session_status { // false as long as no incoming connections have been // established on the listening socket. Every time you change the listen port, this will // be reset to false. bool has_incoming_connections; // the total download and upload rates accumulated // from all torrents. This includes bittorrent protocol, DHT and an estimated TCP/IP // protocol overhead. // deprecated, use session_stats_metrics "net.recv_bytes" + "net.recv_ip_overhead_bytes" // they does include payload + protocol + ip overhead bytes int upload_rate; int download_rate; // the total number of bytes downloaded and // uploaded to and from all torrents. This also includes all the protocol overhead. // deprecated, use session_stats_metrics "net.recv_bytes" + "net.recv_ip_overhead_bytes" // they does include payload + protocol + ip overhead bytes boost::int64_t total_download; boost::int64_t total_upload; // the rate of the payload // down- and upload only. // deprecated, use session_stats_metrics "net.recv_payload_bytes" int payload_upload_rate; // deprecated, use session_stats_metrics "net.sent_payload_bytes" int payload_download_rate; // the total transfers of payload // only. The payload does not include the bittorrent protocol overhead, but only parts of the // actual files to be downloaded. // deprecated, use session_stats_metrics "net.recv_payload_bytes" boost::int64_t total_payload_download; // deprecated, use session_stats_metrics "net.sent_payload_bytes" boost::int64_t total_payload_upload; // the estimated TCP/IP overhead in each direction. int ip_overhead_upload_rate; int ip_overhead_download_rate; boost::int64_t total_ip_overhead_download; boost::int64_t total_ip_overhead_upload; // the upload and download rate used by DHT traffic. Also the total number // of bytes sent and received to and from the DHT. int dht_upload_rate; int dht_download_rate; boost::int64_t total_dht_download; boost::int64_t total_dht_upload; // the upload and download rate used by tracker traffic. Also the total number // of bytes sent and received to and from trackers. int tracker_upload_rate; int tracker_download_rate; boost::int64_t total_tracker_download; boost::int64_t total_tracker_upload; // the number of bytes that has been received more than once. // This can happen if a request from a peer times out and is requested from a different // peer, and then received again from the first one. To make this lower, increase the // ``request_timeout`` and the ``piece_timeout`` in the session settings. boost::int64_t total_redundant_bytes; // the number of bytes that was downloaded which later failed // the hash-check. boost::int64_t total_failed_bytes; // the total number of peer connections this session has. This includes // incoming connections that still hasn't sent their handshake or outgoing connections // that still hasn't completed the TCP connection. This number may be slightly higher // than the sum of all peers of all torrents because the incoming connections may not // be assigned a torrent yet. int num_peers; int num_dead_peers; // the current number of unchoked peers. int num_unchoked; // the current allowed number of unchoked peers. int allowed_upload_slots; // the number of peers that are // waiting for more bandwidth quota from the torrent rate limiter. int up_bandwidth_queue; int down_bandwidth_queue; // count the number of // bytes the connections are waiting for to be able to send and receive. int up_bandwidth_bytes_queue; int down_bandwidth_bytes_queue; // tells the number of // seconds until the next optimistic unchoke change and the start of the next // unchoke interval. These numbers may be reset prematurely if a peer that is // unchoked disconnects or becomes notinterested. int optimistic_unchoke_counter; int unchoke_counter; // the number of peers currently // waiting on a disk write or disk read to complete before it receives or sends // any more data on the socket. It'a a metric of how disk bound you are. int disk_write_queue; int disk_read_queue; // only available when // built with DHT support. They are all set to 0 if the DHT isn't running. When // the DHT is running, ``dht_nodes`` is set to the number of nodes in the routing // table. This number only includes *active* nodes, not cache nodes. The // ``dht_node_cache`` is set to the number of nodes in the node cache. These nodes // are used to replace the regular nodes in the routing table in case any of them // becomes unresponsive. // deprecated, use session_stats_metrics "dht.dht_nodes" and "dht.dht_nodes_cache" int dht_nodes; int dht_node_cache; // the number of torrents tracked by the DHT at the moment. int dht_torrents; // an estimation of the total number of nodes in the DHT // network. boost::int64_t dht_global_nodes; // a vector of the currently running DHT lookups. std::vector active_requests; // contains information about every bucket in the DHT routing // table. std::vector dht_routing_table; // the number of nodes allocated dynamically for a // particular DHT lookup. This represents roughly the amount of memory used // by the DHT. int dht_total_allocations; // statistics on the uTP sockets. utp_status utp_stats; // the number of known peers across all torrents. These are not necessarily // connected peers, just peers we know of. int peerlist_size; // the number of torrents in the // session and the number of them that are currently paused, respectively. int num_torrents; int num_paused_torrents; }; #endif // TORRENT_NO_DEPRECATE } #endif // TORRENT_SESSION_STATUS_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/settings_pack.hpp000066400000000000000000002311061351156116000251240ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_SETTINGS_PACK_HPP_INCLUDED #define TORRENT_SETTINGS_PACK_HPP_INCLUDED #include "libtorrent/entry.hpp" #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" // OVERVIEW // // You have some control over session configuration through the session::apply_settings() // member function. To change one or more configuration options, create a settings_pack // object and fill it with the settings to be set and pass it in to session::apply_settings(). // // The settings_pack object is a collection of settings updates that are applied // to the session when passed to session::apply_settings(). It's empty when // constructed. // // You have control over proxy and authorization settings and also the user-agent // that will be sent to the tracker. The user-agent will also be used to identify the // client with other peers. // namespace libtorrent { namespace aux { struct session_impl; struct session_settings; } struct settings_pack; struct bdecode_node; TORRENT_EXTRA_EXPORT boost::shared_ptr load_pack_from_dict(bdecode_node const& settings); TORRENT_EXTRA_EXPORT void save_settings_to_dict(aux::session_settings const& s, entry::dictionary_type& sett); TORRENT_EXTRA_EXPORT void apply_pack(settings_pack const* pack, aux::session_settings& sett, aux::session_impl* ses = 0); TORRENT_EXTRA_EXPORT void run_all_updates(aux::session_impl& ses); TORRENT_EXPORT int setting_by_name(std::string const& name); TORRENT_EXPORT char const* name_for_setting(int s); // returns a settings_pack with every setting set to its default value TORRENT_EXPORT settings_pack default_settings(); #ifndef TORRENT_NO_DEPRECATE struct session_settings; TORRENT_DEPRECATED settings_pack load_pack_from_struct(aux::session_settings const& current, session_settings const& s); TORRENT_DEPRECATED void load_struct_from_settings(aux::session_settings const& current, session_settings& ret); #endif // The ``settings_pack`` struct, contains the names of all settings as // enum values. These values are passed in to the ``set_str()``, // ``set_int()``, ``set_bool()`` functions, to specify the setting to // change. // // These are the available settings: // // .. include:: settings-ref.rst // struct TORRENT_EXPORT settings_pack { friend TORRENT_EXTRA_EXPORT void apply_pack(settings_pack const* pack, aux::session_settings& sett, aux::session_impl* ses); void set_str(int name, std::string val); void set_int(int name, int val); void set_bool(int name, bool val); bool has_val(int name) const; // clear the settings pack from all settings void clear(); // clear a specific setting from the pack void clear(int name); std::string get_str(int name) const; int get_int(int name) const; bool get_bool(int name) const; // setting names (indices) are 16 bits. The two most significant // bits indicate what type the setting has. (string, int, bool) enum type_bases { string_type_base = 0x0000, int_type_base = 0x4000, bool_type_base = 0x8000, type_mask = 0xc000, index_mask = 0x3fff }; enum string_types { // this is the client identification to the tracker. The recommended // format of this string is: "ClientName/ClientVersion // libtorrent/libtorrentVersion". This name will not only be used when // making HTTP requests, but also when sending extended headers to // peers that support that extension. It may not contain \r or \n user_agent = string_type_base, // ``announce_ip`` is the ip address passed along to trackers as the // ``&ip=`` parameter. If left as the default, that parameter is // omitted. announce_ip, #ifndef TORRENT_NO_DEPRECATE // ``mmap_cache`` may be set to a filename where the disk cache will // be mmapped to. This could be useful, for instance, to map the disk // cache from regular rotating hard drives onto an SSD drive. Doing // that effectively introduces a second layer of caching, allowing the // disk cache to be as big as can fit on an SSD drive (probably about // one order of magnitude more than the available RAM). The intention // of this setting is to set it up once at the start up and not change // it while running. The setting may not be changed as long as there // are any disk buffers in use. This default to the empty string, // which means use regular RAM allocations for the disk cache. The // file specified will be created and truncated to the disk cache size // (``cache_size``). Any existing file with the same name will be // replaced. // // Since this setting sets a hard upper limit on cache usage, it // cannot be combined with // ``settings_pack::contiguous_recv_buffer``, since that feature // treats the ``cache_size`` setting as a soft (but still pretty hard) // limit. The result of combining the two is peers being disconnected // after failing to allocate more disk buffers. // // This feature requires the ``mmap`` system call, on systems that // don't have ``mmap`` this setting is ignored. mmap_cache TORRENT_DEPRECATED_ENUM, #else deprecated12, #endif // this is the client name and version identifier sent to peers in the // handshake message. If this is an empty string, the user_agent is // used instead handshake_client_version, // sets the network interface this session will use when it opens // outgoing connections. By default, it binds outgoing connections to // INADDR_ANY and port 0 (i.e. let the OS decide). Ths parameter must // be a string containing one or more, comma separated, adapter names. // Adapter names on unix systems are of the form "eth0", "eth1", // "tun0", etc. When specifying multiple interfaces, they will be // assigned in round-robin order. This may be useful for clients that // are multi-homed. Binding an outgoing connection to a local IP does // not necessarily make the connection via the associated NIC/Adapter. // Setting this to an empty string will disable binding of outgoing // connections. outgoing_interfaces, // a comma-separated list of IP port-pairs. These // are the listen ports that will be opened for accepting incoming uTP // and TCP connections. It is possible to listen on multiple // IPs and multiple ports. Binding to port 0 will make the // operating system pick the port. The default is "0.0.0.0:6881", which // binds to all interfaces on port 6881. // // If binding fails because the port is busy, the port number will be // incremented by one, ``settings_pack::max_retry_port_bind`` times. // // if all retry attempts fail, the socket will be bound to port 0, // meaning the operating system will pick a port. This behavior can be // disabled by disabling ``settings_pack::listen_system_port_fallback``. // // if binding fails, the listen_failed_alert is posted, potentially // more than once. Once/if binding the listen socket(s) succeed, // listen_succeeded_alert is posted. // // Each port will attempt to open both a UDP and a TCP listen socket, // to allow accepting uTP connections as well as TCP. If using the DHT, // this will also make the DHT use the same UDP ports. // // .. note:: // The current support for opening arbitrary UDP sockets is limited. // In this version of libtorrent, there will only ever be two UDP // sockets, one for IPv4 and one for IPv6. listen_interfaces, // when using a poxy, this is the hostname where the proxy is running // see proxy_type. proxy_hostname, // when using a proxy, these are the credentials (if any) to use whne // connecting to it. see proxy_type proxy_username, proxy_password, // sets the i2p_ SAM bridge to connect to. set the port with the // ``i2p_port`` setting. // // .. _i2p: http://www.i2p2.de i2p_hostname, // this is the fingerprint for the client. It will be used as the // prefix to the peer_id. If this is 20 bytes (or longer) it will be // truncated to 20 bytes and used as the entire peer-id // // There is a utility function, generate_fingerprint() that can be used // to generate a standard client peer ID fingerprint prefix. peer_fingerprint, // This is a comma-separated list of IP port-pairs. They will be added // to the DHT node (if it's enabled) as back-up nodes in case we don't // know of any. This setting will contain one or more bootstrap nodes // by default. // // Changing these after the DHT has been started may not have any // effect until the DHT is restarted. dht_bootstrap_nodes, max_string_setting_internal }; enum bool_types { // determines if connections from the same IP address as existing // connections should be rejected or not. Multiple connections from // the same IP address is not allowed by default, to prevent abusive // behavior by peers. It may be useful to allow such connections in // cases where simulations are run on the same machine, and all peers // in a swarm has the same IP address. allow_multiple_connections_per_ip = bool_type_base, #ifndef TORRENT_NO_DEPRECATE // if set to true, upload, download and unchoke limits are ignored for // peers on the local network. This option is *DEPRECATED*, please use // set_peer_class_filter() instead. ignore_limits_on_local_network TORRENT_DEPRECATED_ENUM, #else deprecated1, #endif // ``send_redundant_have`` controls if have messages will be sent to // peers that already have the piece. This is typically not necessary, // but it might be necessary for collecting statistics in some cases. // Default is false. send_redundant_have, // if this is true, outgoing bitfields will never be fuil. If the // client is seed, a few bits will be set to 0, and later filled in // with have messages. This is to prevent certain ISPs from stopping // people from seeding. lazy_bitfields, // ``use_dht_as_fallback`` determines how the DHT is used. If this is // true, the DHT will only be used for torrents where all trackers in // its tracker list has failed. Either by an explicit error message or // a time out. This is false by default, which means the DHT is used // by default regardless of if the trackers fail or not. use_dht_as_fallback, // ``upnp_ignore_nonrouters`` indicates whether or not the UPnP // implementation should ignore any broadcast response from a device // whose address is not the configured router for this machine. i.e. // it's a way to not talk to other people's routers by mistake. upnp_ignore_nonrouters, // ``use_parole_mode`` specifies if parole mode should be used. Parole // mode means that peers that participate in pieces that fail the hash // check are put in a mode where they are only allowed to download // whole pieces. If the whole piece a peer in parole mode fails the // hash check, it is banned. If a peer participates in a piece that // passes the hash check, it is taken out of parole mode. use_parole_mode, // enable and disable caching of blocks read from disk. the purpose of // the read cache is partly read-ahead of requests but also to avoid // reading blocks back from the disk multiple times for popular // pieces. use_read_cache, #ifndef TORRENT_NO_DEPRECATE use_write_cache TORRENT_DEPRECATED_ENUM, // this will make the disk cache never flush a write piece if it would // cause is to have to re-read it once we want to calculate the piece // hash dont_flush_write_cache TORRENT_DEPRECATED_ENUM, // ``explicit_read_cache`` defaults to 0. If set to something greater // than 0, the disk read cache will not be evicted by cache misses and // will explicitly be controlled based on the rarity of pieces. Rare // pieces are more likely to be cached. This would typically be used // together with ``suggest_mode`` set to ``suggest_read_cache``. The // value is the number of pieces to keep in the read cache. If the // actual read cache can't fit as many, it will essentially be // clamped. explicit_read_cache TORRENT_DEPRECATED_ENUM, #else deprecated7, deprecated10, deprecated13, #endif // allocate separate, contiguous, buffers for read and write calls. // Only used where writev/readv cannot be used will use more RAM but // may improve performance coalesce_reads, coalesce_writes, // prefer seeding torrents when determining which torrents to give // active slots to, the default is false which gives preference to // downloading torrents auto_manage_prefer_seeds, // if ``dont_count_slow_torrents`` is true, torrents without any // payload transfers are not subject to the ``active_seeds`` and // ``active_downloads`` limits. This is intended to make it more // likely to utilize all available bandwidth, and avoid having // torrents that don't transfer anything block the active slots. dont_count_slow_torrents, // ``close_redundant_connections`` specifies whether libtorrent should // close connections where both ends have no utility in keeping the // connection open. For instance if both ends have completed their // downloads, there's no point in keeping it open. close_redundant_connections, // If ``prioritize_partial_pieces`` is true, partial pieces are picked // before pieces that are more rare. If false, rare pieces are always // prioritized, unless the number of partial pieces is growing out of // proportion. prioritize_partial_pieces, // if set to true, the estimated TCP/IP overhead is drained from the // rate limiters, to avoid exceeding the limits with the total traffic rate_limit_ip_overhead, // ``announce_to_all_trackers`` controls how multi tracker torrents // are treated. If this is set to true, all trackers in the same tier // are announced to in parallel. If all trackers in tier 0 fails, all // trackers in tier 1 are announced as well. If it's set to false, the // behavior is as defined by the multi tracker specification. It // defaults to false, which is the same behavior previous versions of // libtorrent has had as well. // // ``announce_to_all_tiers`` also controls how multi tracker torrents // are treated. When this is set to true, one tracker from each tier // is announced to. This is the uTorrent behavior. This is false by // default in order to comply with the multi-tracker specification. announce_to_all_tiers, announce_to_all_trackers, // ``prefer_udp_trackers`` is true by default. It means that trackers // may be rearranged in a way that udp trackers are always tried // before http trackers for the same hostname. Setting this to false // means that the trackers' tier is respected and there's no // preference of one protocol over another. prefer_udp_trackers, // ``strict_super_seeding`` when this is set to true, a piece has to // have been forwarded to a third peer before another one is handed // out. This is the traditional definition of super seeding. strict_super_seeding, #ifndef TORRENT_NO_DEPRECATE // if this is set to true, the memory allocated for the disk cache // will be locked in physical RAM, never to be swapped out. Every time // a disk buffer is allocated and freed, there will be the extra // overhead of a system call. lock_disk_cache TORRENT_DEPRECATED_ENUM, #else deprecated8, #endif // when set to true, all data downloaded from peers will be assumed to // be correct, and not tested to match the hashes in the torrent this // is only useful for simulation and testing purposes (typically // combined with disabled_storage) disable_hash_checks, // if this is true, i2p torrents are allowed to also get peers from // other sources than the tracker, and connect to regular IPs, not // providing any anonymization. This may be useful if the user is not // interested in the anonymization of i2p, but still wants to be able // to connect to i2p peers. allow_i2p_mixed, // ``low_prio_disk`` determines if the disk I/O should use a normal or // low priority policy. This defaults to true, which means that it's // low priority by default. Other processes doing disk I/O will // normally take priority in this mode. This is meant to improve the // overall responsiveness of the system while downloading in the // background. For high-performance server setups, this might not be // desirable. low_prio_disk, // ``volatile_read_cache``, if this is set to true, read cache blocks // that are hit by peer read requests are removed from the disk cache // to free up more space. This is useful if you don't expect the disk // cache to create any cache hits from other peers than the one who // triggered the cache line to be read into the cache in the first // place. volatile_read_cache, // ``guided_read_cache`` enables the disk cache to adjust the size of // a cache line generated by peers to depend on the upload rate you // are sending to that peer. The intention is to optimize the RAM // usage of the cache, to read ahead further for peers that you're // sending faster to. guided_read_cache, // ``no_atime_storage`` this is a linux-only option and passes in the // ``O_NOATIME`` to ``open()`` when opening files. This may lead to // some disk performance improvements. no_atime_storage, // ``incoming_starts_queued_torrents`` defaults to false. If a torrent // has been paused by the auto managed feature in libtorrent, i.e. the // torrent is paused and auto managed, this feature affects whether or // not it is automatically started on an incoming connection. The main // reason to queue torrents, is not to make them unavailable, but to // save on the overhead of announcing to the trackers, the DHT and to // avoid spreading one's unchoke slots too thin. If a peer managed to // find us, even though we're no in the torrent anymore, this setting // can make us start the torrent and serve it. incoming_starts_queued_torrents, // when set to true, the downloaded counter sent to trackers will // include the actual number of payload bytes downloaded including // redundant bytes. If set to false, it will not include any redundancy // bytes report_true_downloaded, // ``strict_end_game_mode`` defaults to true, and controls when a // block may be requested twice. If this is ``true``, a block may only // be requested twice when there's ay least one request to every piece // that's left to download in the torrent. This may slow down progress // on some pieces sometimes, but it may also avoid downloading a lot // of redundant bytes. If this is ``false``, libtorrent attempts to // use each peer connection to its max, by always requesting // something, even if it means requesting something that has been // requested from another peer already. strict_end_game_mode, // if ``broadcast_lsd`` is set to true, the local peer discovery (or // Local Service Discovery) will not only use IP multicast, but also // broadcast its messages. This can be useful when running on networks // that don't support multicast. Since broadcast messages might be // expensive and disruptive on networks, only every 8th announce uses // broadcast. broadcast_lsd, // when set to true, libtorrent will try to make outgoing utp // connections controls whether libtorrent will accept incoming // connections or make outgoing connections of specific type. enable_outgoing_utp, enable_incoming_utp, enable_outgoing_tcp, enable_incoming_tcp, // ``ignore_resume_timestamps`` determines if the storage, when // loading resume data files, should verify that the file modification // time with the timestamps in the resume data. This defaults to // false, which means timestamps are taken into account, and resume // data is less likely to accepted (torrents are more likely to be // fully checked when loaded). It might be useful to set this to true // if your network is faster than your disk, and it would be faster to // redownload potentially missed pieces than to go through the whole // storage to look for them. ignore_resume_timestamps, // ``no_recheck_incomplete_resume`` determines if the storage should // check the whole files when resume data is incomplete or missing or // whether it should simply assume we don't have any of the data. By // default, this is determined by the existence of any of the files. // By setting this setting to true, the files won't be checked, but // will go straight to download mode. no_recheck_incomplete_resume, // ``anonymous_mode`` defaults to false. When set to true, the client // tries to hide its identity to a certain degree. The user-agent will be // reset to an empty string (except for private torrents). Trackers // will only be used if they are using a proxy server. // The listen sockets are closed, and incoming // connections will only be accepted through a SOCKS5 or I2P proxy (if // a peer proxy is set up and is run on the same machine as the // tracker proxy). Since no incoming connections are accepted, // NAT-PMP, UPnP, DHT and local peer discovery are all turned off when // this setting is enabled. // // If you're using I2P, it might make sense to enable anonymous mode // as well. anonymous_mode, // specifies whether downloads from web seeds is reported to the // tracker or not. Defaults to on. Turning it off also excludes web // seed traffic from other stats and download rate reporting via the // libtorrent API. report_web_seed_downloads, #ifndef TORRENT_NO_DEPRECATE // set to true if uTP connections should be rate limited This option // is *DEPRECATED*, please use set_peer_class_filter() instead. rate_limit_utp TORRENT_DEPRECATED_ENUM, #else deprecated2, #endif // if this is true, the ``&ip=`` argument in tracker requests (unless // otherwise specified) will be set to the intermediate IP address if // the user is double NATed. If the user is not double NATed, this // option does not have an affect announce_double_nat, // ``seeding_outgoing_connections`` determines if seeding (and // finished) torrents should attempt to make outgoing connections or // not. By default this is true. It may be set to false in very // specific applications where the cost of making outgoing connections // is high, and there are no or small benefits of doing so. For // instance, if no nodes are behind a firewall or a NAT, seeds don't // need to make outgoing connections. seeding_outgoing_connections, // when this is true, libtorrent will not attempt to make outgoing // connections to peers whose port is < 1024. This is a safety // precaution to avoid being part of a DDoS attack no_connect_privileged_ports, // ``smooth_connects`` is true by default, which means the number of // connection attempts per second may be limited to below the // ``connection_speed``, in case we're close to bump up against the // limit of number of connections. The intention of this setting is to // more evenly distribute our connection attempts over time, instead // of attempting to connect in batches, and timing them out in // batches. smooth_connects, // always send user-agent in every web seed request. If false, only // the first request per http connection will include the user agent always_send_user_agent, // ``apply_ip_filter_to_trackers`` defaults to true. It determines // whether the IP filter applies to trackers as well as peers. If this // is set to false, trackers are exempt from the IP filter (if there // is one). If no IP filter is set, this setting is irrelevant. apply_ip_filter_to_trackers, // ``use_disk_read_ahead`` defaults to true and will attempt to // optimize disk reads by giving the operating system heads up of disk // read requests as they are queued in the disk job queue. use_disk_read_ahead, // ``lock_files`` determines whether or not to lock files which // libtorrent is downloading to or seeding from. This is implemented // using ``fcntl(F_SETLK)`` on unix systems and by not passing in // ``SHARE_READ`` and ``SHARE_WRITE`` on windows. This might prevent // 3rd party processes from corrupting the files under libtorrent's // feet. lock_files, // ``contiguous_recv_buffer`` determines whether or not libtorrent // should receive data from peers into a contiguous intermediate // buffer, to then copy blocks into disk buffers from, or to make many // smaller calls to ``read()``, each time passing in the specific // buffer the data belongs in. When downloading at high rates, the // latter may save some time copying data. When seeding at high rates, // all incoming traffic consists of a very large number of tiny // packets, and enabling ``contiguous_recv_buffer`` will provide // higher performance. When this is enabled, it will only be used when // seeding to peers, since that's when it provides performance // improvements. contiguous_recv_buffer, // when true, web seeds sending bad data will be banned ban_web_seeds, // when set to false, the ``write_cache_line_size`` will apply across // piece boundaries. this is a bad idea unless the piece picker also // is configured to have an affinity to pick pieces belonging to the // same write cache line as is configured in the disk cache. allow_partial_disk_writes, // If true, disables any communication that's not going over a proxy. // Enabling this requires a proxy to be configured as well, see // proxy_type and proxy_hostname settings. The listen sockets are // closed, and incoming connections will only be accepted through a // SOCKS5 or I2P proxy (if a peer proxy is set up and is run on the // same machine as the tracker proxy). force_proxy, // if false, prevents libtorrent to advertise share-mode support support_share_mode, // if this is false, don't advertise support for the Tribler merkle // tree piece message support_merkle_torrents, // if this is true, the number of redundant bytes is sent to the // tracker report_redundant_bytes, // if this is true, libtorrent will fall back to listening on a port // chosen by the operating system (i.e. binding to port 0). If a // failure is preferred, set this to false. listen_system_port_fallback, // ``use_disk_cache_pool`` enables using a pool allocator for disk // cache blocks. Enabling it makes the cache perform better at high // throughput. It also makes the cache less likely and slower at // returning memory back to the system, once allocated. use_disk_cache_pool, // when this is true, and incoming encrypted connections are enabled, // &supportcrypt=1 is included in http tracker announces announce_crypto_support, // Starts and stops the UPnP service. When started, the listen port // and the DHT port are attempted to be forwarded on local UPnP router // devices. // // The upnp object returned by ``start_upnp()`` can be used to add and // remove arbitrary port mappings. Mapping status is returned through // the portmap_alert and the portmap_error_alert. The object will be // valid until ``stop_upnp()`` is called. See upnp-and-nat-pmp_. enable_upnp, // Starts and stops the NAT-PMP service. When started, the listen port // and the DHT port are attempted to be forwarded on the router // through NAT-PMP. // // The natpmp object returned by ``start_natpmp()`` can be used to add // and remove arbitrary port mappings. Mapping status is returned // through the portmap_alert and the portmap_error_alert. The object // will be valid until ``stop_natpmp()`` is called. See // upnp-and-nat-pmp_. enable_natpmp, // Starts and stops Local Service Discovery. This service will // broadcast the infohashes of all the non-private torrents on the // local network to look for peers on the same swarm within multicast // reach. enable_lsd, // starts the dht node and makes the trackerless service available to // torrents. enable_dht, // if the allowed encryption level is both, setting this to true will // prefer rc4 if both methods are offered, plaintext otherwise prefer_rc4, // if true, hostname lookups are done via the configured proxy (if // any). This is only supported by SOCKS5 and HTTP. proxy_hostnames, // if true, peer connections are made (and accepted) over the // configured proxy, if any. Web seeds as well as regular bittorrent // peer connections are considered "peer connections". Anything // transporting actual torrent payload (trackers and DHT traffic are // not considered peer connections). proxy_peer_connections, // if this setting is true, torrents with a very high availability of // pieces (and seeds) are downloaded sequentially. This is more // efficient for the disk I/O. With many seeds, the download order is // unlikely to matter anyway auto_sequential, // if true, tracker connections are made over the configured proxy, if // any. proxy_tracker_connections, max_bool_setting_internal }; enum int_types { // ``tracker_completion_timeout`` is the number of seconds the tracker // connection will wait from when it sent the request until it // considers the tracker to have timed-out. Default value is 60 // seconds. tracker_completion_timeout = int_type_base, // ``tracker_receive_timeout`` is the number of seconds to wait to // receive any data from the tracker. If no data is received for this // number of seconds, the tracker will be considered as having timed // out. If a tracker is down, this is the kind of timeout that will // occur. 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 stop_tracker_timeout, // this is the maximum number of bytes in a tracker response. If a // response size passes this number of bytes it will be rejected and // the connection will be closed. On gzipped responses this size is // measured on the uncompressed data. So, if you get 20 bytes of gzip // response that'll expand to 2 megabytes, it will be interrupted // before the entire response has been uncompressed (assuming the // limit is lower than 2 megs). tracker_maximum_response_length, // the number of seconds from a request is sent until it times out if // no piece response is returned. piece_timeout, // the number of seconds one block (16kB) is expected to be received // within. If it's not, the block is requested from a different peer request_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. 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. max_allowed_in_request_queue, // ``max_out_request_queue`` is the maximum number of outstanding // requests to send to a peer. This limit takes precedence over // ``request_queue_time``. i.e. no matter the download speed, the // number of outstanding requests will never exceed this limit. 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. whole_pieces_threshold, // ``peer_timeout`` is the number of seconds the peer connection // should wait (for any activity on the peer connection) before // closing it due to time out. This defaults to 120 seconds, since // that's what's specified in the protocol specification. After half // the time out, a keep alive message is sent. 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. urlseed_timeout, // controls the pipelining size of url-seeds. i.e. the number of HTTP // request to keep outstanding before waiting for the first one to // complete. It's common for web servers to limit this to a relatively // low number, like 5 urlseed_pipeline_size, // time to wait until a new retry of a web seed takes place urlseed_wait_retry, // sets the upper limit on the total number of files this session will // keep open. The reason why files are left open at all is that some // anti virus software hooks on every file close, and scans the file // for viruses. deferring the closing of the files will be the // difference between a usable system and a completely hogged down // system. Most operating systems also has a limit on the total number // of file descriptors a process may have open. file_pool_size, // ``max_failcount`` is the maximum times we try to connect to a peer // before stop connecting again. If a peer succeeds, the failcounter // is reset. If a peer is retrieved from a peer source (other than // DHT) the failcount is decremented by one, allowing another try. max_failcount, // the number of seconds to wait to reconnect to a peer. this time is // multiplied with the failcount. min_reconnect_time, // ``peer_connect_timeout`` the number of seconds to wait after a // connection attempt is initiated to a peer until it is considered as // having timed out. This setting is especially important in case the // number of half-open connections are limited, since stale half-open // connection may delay the connection of other peers considerably. peer_connect_timeout, // ``connection_speed`` is the number of connection attempts that are // made per second. If a number < 0 is specified, it will default to // 200 connections per second. If 0 is specified, it means don't make // outgoing connections at all. connection_speed, // if a peer is uninteresting and uninterested for longer than this // number of seconds, it will be disconnected. default is 10 minutes inactivity_timeout, // ``unchoke_interval`` is the number of seconds between // chokes/unchokes. On this interval, peers are re-evaluated for being // choked/unchoked. This is defined as 30 seconds in the protocol, and // it should be significantly longer than what it takes for TCP to // ramp up to it's max rate. unchoke_interval, // ``optimistic_unchoke_interval`` is the number of seconds between // each *optimistic* unchoke. On this timer, the currently // optimistically unchoked peer will change. optimistic_unchoke_interval, // ``num_want`` is the number of peers we want from each tracker // request. It defines what is sent as the ``&num_want=`` parameter to // the tracker. num_want, // ``initial_picker_threshold`` specifies the number of pieces we need // before we switch to rarest first picking. This defaults to 4, which // means the 4 first pieces in any torrent are picked at random, the // following pieces are picked in rarest first order. initial_picker_threshold, // the number of allowed pieces to send to peers that supports the // fast extensions allowed_fast_set_size, // ``suggest_mode`` controls whether or not libtorrent will send out // suggest messages to create a bias of its peers to request certain // pieces. The modes are: // // * ``no_piece_suggestions`` which is the default and will not send // out suggest messages. // * ``suggest_read_cache`` which will send out suggest messages for // the most recent pieces that are in the read cache. suggest_mode, // ``max_queued_disk_bytes`` is the maximum number of bytes, to // be written to disk, that can wait in the disk I/O thread queue. // This queue is only for waiting for the disk I/O thread to receive // the job and either write it to disk or insert it in the write // cache. When this limit is reached, the peer connections will stop // reading data from their sockets, until the disk thread catches up. // Setting this too low will severely limit your download rate. max_queued_disk_bytes, // the number of seconds to wait for a handshake response from a peer. // If no response is received within this time, the peer is // disconnected. handshake_timeout, // ``send_buffer_low_watermark`` the minimum send buffer target size // (send buffer includes bytes pending being read from disk). For good // and snappy seeding performance, set this fairly high, to at least // fit a few blocks. This is essentially the initial window size which // will determine how fast we can ramp up the send rate // // if the send buffer has fewer bytes than ``send_buffer_watermark``, // we'll read another 16kB block onto it. If set too small, upload // rate capacity will suffer. If set too high, memory will be wasted. // The actual watermark may be lower than this in case the upload rate // is low, this is the upper limit. // // the current upload rate to a peer is multiplied by this factor to // get the send buffer watermark. The factor is specified as a // percentage. i.e. 50 -> 0.5 This product is clamped to the // ``send_buffer_watermark`` setting to not exceed the max. For high // speed upload, this should be set to a greater value than 100. For // high capacity connections, setting this higher can improve upload // performance and disk throughput. Setting it too high may waste RAM // and create a bias towards read jobs over write jobs. send_buffer_low_watermark, send_buffer_watermark, send_buffer_watermark_factor, // ``choking_algorithm`` specifies which algorithm to use to determine // which peers to unchoke. // // The options for choking algorithms are: // // * ``fixed_slots_choker`` is the traditional choker with a fixed // number of unchoke slots (as specified by // ``settings_pack::unchoke_slots_limit``). // // * ``rate_based_choker`` opens up unchoke slots based on the upload // rate achieved to peers. The more slots that are opened, the // marginal upload rate required to open up another slot increases. // // * ``bittyrant_choker`` attempts to optimize download rate by // finding the reciprocation rate of each peer individually and // prefers peers that gives the highest *return on investment*. It // still allocates all upload capacity, but shuffles it around to // the best peers first. For this choker to be efficient, you need // to set a global upload rate limit // (``settings_pack::upload_rate_limit``). For more information // about this choker, see the paper_. This choker is not fully // implemented nor tested. // // .. _paper: http://bittyrant.cs.washington.edu/#papers // // ``seed_choking_algorithm`` controls the seeding unchoke behavior. // The available options are: // // * ``round_robin`` which round-robins the peers that are unchoked // when seeding. This distributes the upload bandwidht uniformly and // fairly. It minimizes the ability for a peer to download everything // without redistributing it. // // * ``fastest_upload`` unchokes the peers we can send to the fastest. // This might be a bit more reliable in utilizing all available // capacity. // // * ``anti_leech`` prioritizes peers who have just started or are // just about to finish the download. The intention is to force // peers in the middle of the download to trade with each other. choking_algorithm, seed_choking_algorithm, // ``cache_size`` is the disk write and read cache. It is specified // in units of 16 KiB blocks. Buffers that are part of a peer's send // or receive buffer also count against this limit. Send and receive // buffers will never be denied to be allocated, but they will cause // the actual cached blocks to be flushed or evicted. If this is set // to -1, the cache size is automatically set to the amount of // physical RAM available in the machine divided by 8. If the amount // of physical RAM cannot be determined, it's set to 1024 (= 16 MiB). // // Disk buffers are allocated using a pool allocator, the number of // blocks that are allocated at a time when the pool needs to grow can // be specified in ``cache_buffer_chunk_size``. Lower numbers saves // memory at the expense of more heap allocations. If it is set to 0, // the effective chunk size is proportional to the total cache size, // attempting to strike a good balance between performance and memory // usage. It defaults to 0. ``cache_expiry`` is the number of seconds // from the last cached write to a piece in the write cache, to when // it's forcefully flushed to disk. Default is 60 second. // // On 32 bit builds, the effective cache size will be limited to 3/4 of // 2 GiB to avoid exceeding the virtual address space limit. cache_size, cache_buffer_chunk_size, cache_expiry, #ifndef TORRENT_NO_DEPRECATE // ``explicit_cache_interval`` is the number of seconds in between // each refresh of a part of the explicit read cache. Torrents take // turns in refreshing and this is the time in between each torrent // refresh. Refreshing a torrent's explicit read cache means scanning // all pieces and picking a random set of the rarest ones. There is an // affinity to pick pieces that are already in the cache, so that // subsequent refreshes only swaps in pieces that are rarer than // whatever is in the cache at the time. explicit_cache_interval TORRENT_DEPRECATED_ENUM, #else deprecated11, #endif // determines how files are opened when they're in read only mode // versus read and write mode. The options are: // // enable_os_cache // This is the default and files are opened normally, with the OS // caching reads and writes. // disable_os_cache // This opens all files in no-cache mode. This corresponds to the // OS not letting blocks for the files linger in the cache. This // makes sense in order to avoid the bittorrent client to // potentially evict all other processes' cache by simply handling // high throughput and large files. If libtorrent's read cache is // disabled, enabling this may reduce performance. // // One reason to disable caching is that it may help the operating // system from growing its file cache indefinitely. disk_io_write_mode, disk_io_read_mode, // this is the first port to use for binding outgoing connections to. // This is useful for users that have routers that allow QoS settings // based on local port. when binding outgoing connections to specific // ports, ``num_outgoing_ports`` is the size of the range. It should // be more than a few // // .. warning:: setting outgoing ports will limit the ability to keep // multiple connections to the same client, even for different // torrents. It is not recommended to change this setting. Its main // purpose is to use as an escape hatch for cheap routers with QoS // capability but can only classify flows based on port numbers. // // It is a range instead of a single port because of the problems with // failing to reconnect to peers if a previous socket to that peer and // port is in ``TIME_WAIT`` state. outgoing_port, num_outgoing_ports, // ``peer_tos`` determines the TOS byte set in the IP header of every // packet sent to peers (including web seeds). The default value for // this is ``0x0`` (no marking). One potentially useful TOS mark is // ``0x20``, this represents the *QBone scavenger service*. For more // details, see QBSS_. // // .. _`QBSS`: http://qbone.internet2.edu/qbss/ peer_tos, // for auto managed torrents, these are the limits they are subject // to. If there are too many torrents some of the auto managed ones // will be paused until some slots free up. ``active_downloads`` and // ``active_seeds`` controls how many active seeding and downloading // torrents the queuing mechanism allows. The target number of active // torrents is ``min(active_downloads + active_seeds, active_limit)``. // ``active_downloads`` and ``active_seeds`` are upper limits on the // number of downloading torrents and seeding torrents respectively. // Setting the value to -1 means unlimited. // // For example if there are 10 seeding torrents and 10 downloading // torrents, and ``active_downloads`` is 4 and ``active_seeds`` is 4, // there will be 4 seeds active and 4 downloading torrents. If the // settings are ``active_downloads`` = 2 and ``active_seeds`` = 4, // then there will be 2 downloading torrents and 4 seeding torrents // active. Torrents that are not auto managed are not counted against // these limits. // // ``active_checking`` is the limit of number of simultaneous checking // torrents. // // ``active_limit`` is a hard limit on the number of active (auto // managed) torrents. This limit also applies to slow torrents. // // ``active_dht_limit`` is the max number of torrents to announce to // the DHT. By default this is set to 88, which is no more than one // DHT announce every 10 seconds. // // ``active_tracker_limit`` is the max number of torrents to announce // to their trackers. By default this is 360, which is no more than // one announce every 5 seconds. // // ``active_lsd_limit`` is the max number of torrents to announce to // the local network over the local service discovery protocol. By // default this is 80, which is no more than one announce every 5 // seconds (assuming the default announce interval of 5 minutes). // // You can have more torrents *active*, even though they are not // announced to the DHT, lsd or their tracker. If some peer knows // about you for any reason and tries to connect, it will still be // accepted, unless the torrent is paused, which means it won't accept // any connections. // // ``active_loaded_limit`` is the number of torrents that are allowed // to be *loaded* at any given time. Note that a torrent can be active // even though it's not loaded. If an unloaded torrents finds a peer // that wants to access it, the torrent will be loaded on demand, // using a user-supplied callback function. If the feature of // unloading torrents is not enabled, this setting have no effect. If // this limit is set to 0, it means unlimited. For more information, // see dynamic-loading-of-torrent-files_. active_downloads, active_seeds, active_checking, active_dht_limit, active_tracker_limit, active_lsd_limit, active_limit, active_loaded_limit, // ``auto_manage_interval`` is the number of seconds between the // torrent queue is updated, and rotated. auto_manage_interval, // this is the limit on the time a torrent has been an active seed // (specified in seconds) before it is considered having met the seed // limit criteria. See queuing_. seed_time_limit, // ``auto_scrape_interval`` is the number of seconds between scrapes // of queued torrents (auto managed and paused torrents). Auto managed // torrents that are paused, are scraped regularly in order to keep // track of their downloader/seed ratio. This ratio is used to // determine which torrents to seed and which to pause. // // ``auto_scrape_min_interval`` is the minimum number of seconds // between any automatic scrape (regardless of torrent). In case there // are a large number of paused auto managed torrents, this puts a // limit on how often a scrape request is sent. auto_scrape_interval, auto_scrape_min_interval, // ``max_peerlist_size`` is the maximum number of peers in the list of // known peers. These peers are not necessarily connected, so this // number should be much greater than the maximum number of connected // peers. Peers are evicted from the cache when the list grows passed // 90% of this limit, and once the size hits the limit, peers are no // longer added to the list. If this limit is set to 0, there is no // limit on how many peers we'll keep in the peer list. // // ``max_paused_peerlist_size`` is the max peer list size used for // torrents that are paused. This default to the same as // ``max_peerlist_size``, but can be used to save memory for paused // torrents, since it's not as important for them to keep a large peer // list. max_peerlist_size, max_paused_peerlist_size, // this is the minimum allowed announce interval for a tracker. This // is specified in seconds and is used as a sanity check on what is // returned from a tracker. It mitigates hammering misconfigured // trackers. min_announce_interval, // this is the number of seconds a torrent is considered active after // it was started, regardless of upload and download speed. This is so // that newly started torrents are not considered inactive until they // have a fair chance to start downloading. auto_manage_startup, // ``seeding_piece_quota`` is the number of pieces to send to a peer, // when seeding, before rotating in another peer to the unchoke set. // It defaults to 3 pieces, which means that when seeding, any peer // we've sent more than this number of pieces to will be unchoked in // favour of a choked peer. seeding_piece_quota, // TODO: deprecate this // ``max_rejects`` is the number of piece requests we will reject in a // row while a peer is choked before the peer is considered abusive // and is disconnected. max_rejects, // ``recv_socket_buffer_size`` and ``send_socket_buffer_size`` // specifies the buffer sizes set on peer sockets. 0 (which is the // default) means the OS default (i.e. don't change the buffer sizes). // The socket buffer sizes are changed using setsockopt() with // SOL_SOCKET/SO_RCVBUF and SO_SNDBUFFER. recv_socket_buffer_size, send_socket_buffer_size, #ifndef TORRENT_NO_DEPRECATE // ``file_checks_delay_per_block`` is the number of milliseconds to // sleep in between disk read operations when checking torrents. This // defaults to 0, but can be set to higher numbers to slow down the // rate at which data is read from the disk while checking. This may // be useful for background tasks that doesn't matter if they take a // bit longer, as long as they leave disk I/O time for other // processes. file_checks_delay_per_block TORRENT_DEPRECATED_ENUM, #else deprecated14, #endif // ``read_cache_line_size`` is the number of blocks to read into the // read cache when a read cache miss occurs. Setting this to 0 is // essentially the same thing as disabling read cache. The number of // blocks read into the read cache is always capped by the piece // boundary. // // When a piece in the write cache has ``write_cache_line_size`` // contiguous blocks in it, they will be flushed. Setting this to 1 // effectively disables the write cache. read_cache_line_size, write_cache_line_size, // ``optimistic_disk_retry`` is the number of seconds from a disk // write errors occur on a torrent until libtorrent will take it out // of the upload mode, to test if the error condition has been fixed. // // libtorrent will only do this automatically for auto managed // torrents. // // You can explicitly take a torrent out of upload only mode using // set_upload_mode(). optimistic_disk_retry, // ``max_suggest_pieces`` is the max number of suggested piece indices // received from a peer that's remembered. If a peer floods suggest // messages, this limit prevents libtorrent from using too much RAM. // It defaults to 10. max_suggest_pieces, // ``local_service_announce_interval`` is the time between local // network announces for a torrent. By default, when local service // discovery is enabled a torrent announces itself every 5 minutes. // This interval is specified in seconds. local_service_announce_interval, // ``dht_announce_interval`` is the number of seconds between // announcing torrents to the distributed hash table (DHT). dht_announce_interval, // ``udp_tracker_token_expiry`` is the number of seconds libtorrent // will keep UDP tracker connection tokens around for. This is // specified to be 60 seconds, and defaults to that. The higher this // value is, the fewer packets have to be sent to the UDP tracker. In // order for higher values to work, the tracker needs to be configured // to match the expiration time for tokens. udp_tracker_token_expiry, // ``default_cache_min_age`` is the minimum number of seconds any read // cache line is kept in the cache. This defaults to one second but // may be greater if ``guided_read_cache`` is enabled. Having a lower // bound on the time a cache line stays in the cache is an attempt // to avoid swapping the same pieces in and out of the cache in case // there is a shortage of spare cache space. default_cache_min_age, // ``num_optimistic_unchoke_slots`` is the number of optimistic // unchoke slots to use. It defaults to 0, which means automatic. // Having a higher number of optimistic unchoke slots mean you will // find the good peers faster but with the trade-off to use up more // bandwidth. When this is set to 0, libtorrent opens up 20% of your // allowed upload slots as optimistic unchoke slots. num_optimistic_unchoke_slots, // ``default_est_reciprocation_rate`` is the assumed reciprocation // rate from peers when using the BitTyrant choker. This defaults to // 14 kiB/s. If set too high, you will over-estimate your peers and be // more altruistic while finding the true reciprocation rate, if it's // set too low, you'll be too stingy and waste finding the true // reciprocation rate. // // ``increase_est_reciprocation_rate`` specifies how many percent the // estimated reciprocation rate should be increased by each unchoke // interval a peer is still choking us back. This defaults to 20%. // This only applies to the BitTyrant choker. // // ``decrease_est_reciprocation_rate`` specifies how many percent the // estimated reciprocation rate should be decreased by each unchoke // interval a peer unchokes us. This default to 3%. This only applies // to the BitTyrant choker. default_est_reciprocation_rate, increase_est_reciprocation_rate, decrease_est_reciprocation_rate, // the max number of peers we accept from pex messages from a single // peer. this limits the number of concurrent peers any of our peers // claims to be connected to. If they claim to be connected to more // than this, we'll ignore any peer that exceeds this limit max_pex_peers, // ``tick_interval`` specifies the number of milliseconds between // internal ticks. This is the frequency with which bandwidth quota is // distributed to peers. It should not be more than one second (i.e. // 1000 ms). Setting this to a low value (around 100) means higher // resolution bandwidth quota distribution, setting it to a higher // value saves CPU cycles. tick_interval, // ``share_mode_target`` specifies the target share ratio for share // mode torrents. This defaults to 3, meaning we'll try to upload 3 // times as much as we download. Setting this very high, will make it // very conservative and you might end up not downloading anything // ever (and not affecting your share ratio). It does not make any // sense to set this any lower than 2. For instance, if only 3 peers // need to download the rarest piece, it's impossible to download a // single piece and upload it more than 3 times. If the // share_mode_target is set to more than 3, nothing is downloaded. share_mode_target, // ``upload_rate_limit`` and ``download_rate_limit`` sets // the session-global limits of upload and download rate limits, in // bytes per second. By default peers on the local network are not rate // limited. // // A value of 0 means unlimited. // // For fine grained control over rate limits, including making them apply // to local peers, see peer-classes_. upload_rate_limit, download_rate_limit, #ifndef TORRENT_NO_DEPRECATE local_upload_rate_limit TORRENT_DEPRECATED_ENUM, local_download_rate_limit TORRENT_DEPRECATED_ENUM, #else deprecated3, deprecated4, #endif // ``dht_upload_rate_limit`` sets the rate limit on the DHT. This is // specified in bytes per second and defaults to 4000. For busy boxes // with lots of torrents that requires more DHT traffic, this should // be raised. dht_upload_rate_limit, // ``unchoke_slots_limit`` is the max number of unchoked peers in the // session. The number of unchoke slots may be ignored depending on // what ``choking_algorithm`` is set to. unchoke_slots_limit, #ifndef TORRENT_NO_DEPRECATE // ``half_open_limit`` sets the maximum number of half-open // connections libtorrent will have when connecting to peers. A // half-open connection is one where connect() has been called, but // the connection still hasn't been established (nor failed). Windows // XP Service Pack 2 sets a default, system wide, limit of the number // of half-open connections to 10. So, this limit can be used to work // nicer together with other network applications on that system. The // default is to have no limit, and passing -1 as the limit, means to // have no limit. When limiting the number of simultaneous connection // attempts, peers will be put in a queue waiting for their turn to // get connected. half_open_limit TORRENT_DEPRECATED_ENUM, #else deprecated5, #endif // ``connections_limit`` sets a global limit on the number of // connections opened. The number of connections is set to a hard // minimum of at least two per torrent, so if you set a too low // connections limit, and open too many torrents, the limit will not // be met. connections_limit, // ``connections_slack`` is the the number of incoming connections // exceeding the connection limit to accept in order to potentially // replace existing ones. connections_slack, // ``utp_target_delay`` is the target delay for uTP sockets in // milliseconds. A high value will make uTP connections more // aggressive and cause longer queues in the upload bottleneck. It // cannot be too low, since the noise in the measurements would cause // it to send too slow. The default is 50 milliseconds. // ``utp_gain_factor`` is the number of bytes the uTP congestion // window can increase at the most in one RTT. This defaults to 300 // bytes. If this is set too high, the congestion controller reacts // too hard to noise and will not be stable, if it's set too low, it // will react slow to congestion and not back off as fast. // // ``utp_min_timeout`` is the shortest allowed uTP socket timeout, // specified in milliseconds. This defaults to 500 milliseconds. The // timeout depends on the RTT of the connection, but is never smaller // than this value. A connection times out when every packet in a // window is lost, or when a packet is lost twice in a row (i.e. the // resent packet is lost as well). // // The shorter the timeout is, the faster the connection will recover // from this situation, assuming the RTT is low enough. // ``utp_syn_resends`` is the number of SYN packets that are sent (and // timed out) before giving up and closing the socket. // ``utp_num_resends`` is the number of times a packet is sent (and // lossed or timed out) before giving up and closing the connection. // ``utp_connect_timeout`` is the number of milliseconds of timeout // for the initial SYN packet for uTP connections. For each timed out // packet (in a row), the timeout is doubled. ``utp_loss_multiplier`` // controls how the congestion window is changed when a packet loss is // experienced. It's specified as a percentage multiplier for // ``cwnd``. By default it's set to 50 (i.e. cut in half). Do not // change this value unless you know what you're doing. Never set it // higher than 100. utp_target_delay, utp_gain_factor, utp_min_timeout, utp_syn_resends, utp_fin_resends, utp_num_resends, utp_connect_timeout, #ifndef TORRENT_NO_DEPRECATE utp_delayed_ack TORRENT_DEPRECATED_ENUM, #else deprecated6, #endif utp_loss_multiplier, // The ``mixed_mode_algorithm`` determines how to treat TCP // connections when there are uTP connections. Since uTP is designed // to yield to TCP, there's an inherent problem when using swarms that // have both TCP and uTP connections. If nothing is done, uTP // connections would often be starved out for bandwidth by the TCP // connections. This mode is ``prefer_tcp``. The ``peer_proportional`` // mode simply looks at the current throughput and rate limits all TCP // connections to their proportional share based on how many of the // connections are TCP. This works best if uTP connections are not // rate limited by the global rate limiter (which they aren't by // default). mixed_mode_algorithm, // ``listen_queue_size`` is the value passed in to listen() for the // listen socket. It is the number of outstanding incoming connections // to queue up while we're not actively waiting for a connection to be // accepted. The default is 5 which should be sufficient for any // normal client. If this is a high performance server which expects // to receive a lot of connections, or used in a simulator or test, it // might make sense to raise this number. It will not take affect // until the ``listen_interfaces`` settings is updated. listen_queue_size, // ``torrent_connect_boost`` is the number of peers to try to connect // to immediately when the first tracker response is received for a // torrent. This is a boost to given to new torrents to accelerate // them starting up. The normal connect scheduler is run once every // second, this allows peers to be connected immediately instead of // waiting for the session tick to trigger connections. // This may not be set higher than 255. torrent_connect_boost, // ``alert_queue_size`` is the maximum number of alerts queued up // internally. If alerts are not popped, the queue will eventually // fill up to this level. alert_queue_size, // ``max_metadata_size`` is the maximum allowed size (in bytes) to be // received by the metadata extension, i.e. magnet links. max_metadata_size, #ifndef TORRENT_NO_DEPRECATE // DEPRECTED: use aio_threads instead // ``hashing_threads`` is the number of threads to use for piece hash // verification. It defaults to 1. For very high download rates, on // machines with multiple cores, this could be incremented. Setting it // higher than the number of CPU cores would presumably not provide // any benefit of setting it to the number of cores. If it's set to 0, // hashing is done in the disk thread. hashing_threads TORRENT_DEPRECATED_ENUM, #else deprecated9, #endif // the number of blocks to keep outstanding at any given time when // checking torrents. Higher numbers give faster re-checks but uses // more memory. Specified in number of 16 kiB blocks checking_mem_usage, // if set to > 0, pieces will be announced to other peers before they // are fully downloaded (and before they are hash checked). The // intention is to gain 1.5 potential round trip times per downloaded // piece. When non-zero, this indicates how many milliseconds in // advance pieces should be announced, before they are expected to be // completed. predictive_piece_announce, // for some aio back-ends, ``aio_threads`` specifies the number of // io-threads to use, and ``aio_max`` the max number of outstanding // jobs. aio_threads, aio_max, // ``network_threads`` is the number of threads to use to call // ``async_write_some`` (i.e. send) on peer connection sockets. When // seeding at extremely high rates, this may become a bottleneck, and // setting this to 2 or more may parallelize that cost. When using SSL // torrents, all encryption for outgoing traffic is done within the // socket send functions, and this will help parallelizing the cost of // SSL encryption as well. network_threads, // ``ssl_listen`` sets the listen port for SSL connections. If this is // set to 0, no SSL listen port is opened. Otherwise a socket is // opened on this port. This setting is only taken into account when // opening the regular listen port, and won't re-open the listen // socket simply by changing this setting. ssl_listen, // ``tracker_backoff`` determines how aggressively to back off from // retrying failing trackers. This value determines *x* in the // following formula, determining the number of seconds to wait until // the next retry: // // delay = 5 + 5 * x / 100 * fails^2 // // This setting may be useful to make libtorrent more or less // aggressive in hitting trackers. tracker_backoff, // when a seeding torrent reaches either the share ratio (bytes up / // bytes down) or the seed time ratio (seconds as seed / seconds as // downloader) or the seed time limit (seconds as seed) it is // considered done, and it will leave room for other torrents. These // are specified as percentages. Torrents that are considered done will // still be allowed to be seeded, they just won't have priority anymore. // For more, see queuing_. share_ratio_limit, seed_time_ratio_limit, // peer_turnover is the percentage of peers to disconnect every // turnover peer_turnover_interval (if we're at the peer limit), this // is specified in percent when we are connected to more than limit * // peer_turnover_cutoff peers disconnect peer_turnover fraction of the // peers. It is specified in percent peer_turnover_interval is the // interval (in seconds) between optimistic disconnects if the // disconnects happen and how many peers are disconnected is // controlled by peer_turnover and peer_turnover_cutoff peer_turnover, peer_turnover_cutoff, peer_turnover_interval, // this setting controls the priority of downloading torrents over // seeding or finished torrents when it comes to making peer // connections. Peer connections are throttled by the connection_speed // and the half-open connection limit. This makes peer connections a // limited resource. Torrents that still have pieces to download are // prioritized by default, to avoid having many seeding torrents use // most of the connection attempts and only give one peer every now // and then to the downloading torrent. libtorrent will loop over the // downloading torrents to connect a peer each, and every n:th // connection attempt, a finished torrent is picked to be allowed to // connect to a peer. This setting controls n. connect_seed_every_n_download, // the max number of bytes to allow an HTTP response to be when // announcing to trackers or downloading .torrent files via the // ``url`` provided in ``add_torrent_params``. max_http_recv_buffer_size, // if binding to a specific port fails, should the port be incremented // by one and tried again? This setting specifies how many times to // retry a failed port bind max_retry_port_bind, // a bitmask combining flags from alert::category_t defining which // kinds of alerts to receive alert_mask, // control the settings for incoming and outgoing connections // respectively. see enc_policy enum for the available options. // Keep in mind that protocol encryption degrades performance in // several respects: // // 1. It prevents "zero copy" disk buffers being sent to peers, since // each peer needs to mutate the data (i.e. encrypt it) the data // must be copied per peer connection rather than sending the same // buffer to multiple peers. // 2. The encryption itself requires more CPU than plain bittorrent // protocol. The highest cost is the Diffie Hellman exchange on // connection setup. // 3. The encryption handshake adds several round-trips to the // connection setup, and delays transferring data. out_enc_policy, in_enc_policy, // determines the encryption level of the connections. This setting // will adjust which encryption scheme is offered to the other peer, // as well as which encryption scheme is selected by the client. See // enc_level enum for options. allowed_enc_level, // the download and upload rate limits for a torrent to be considered // active by the queuing mechanism. A torrent whose download rate is // less than ``inactive_down_rate`` and whose upload rate is less than // ``inactive_up_rate`` for ``auto_manage_startup`` seconds, is // considered inactive, and another queued torrent may be started. // This logic is disabled if ``dont_count_slow_torrents`` is false. inactive_down_rate, inactive_up_rate, // proxy to use, defaults to none. see proxy_type_t. proxy_type, // the port of the proxy server proxy_port, // sets the i2p_ SAM bridge port to connect to. set the hostname with // the ``i2p_hostname`` setting. // // .. _i2p: http://www.i2p2.de i2p_port, // this determines the max number of volatile disk cache blocks. If the // number of volatile blocks exceed this limit, other volatile blocks // will start to be evicted. A disk cache block is volatile if it has // low priority, and should be one of the first blocks to be evicted // under pressure. For instance, blocks pulled into the cache as the // result of calculating a piece hash are volatile. These blocks don't // represent potential interest among peers, so the value of keeping // them in the cache is limited. cache_size_volatile, // The maximum request range of an url seed in bytes. This value // defines the largest possible sequential web seed request. Default // is 16 * 1024 * 1024. Lower values are possible but will be ignored // if they are lower then piece size. // This value should be related to your download speed to prevent // libtorrent from creating too many expensive http requests per // second. You can select a value as high as you want but keep in mind // that libtorrent can't create parallel requests if the first request // did already select the whole file. // If you combine bittorrent seeds with web seeds and pick strategies // like rarest first you may find your web seed requests split into // smaller parts because we don't download already picked pieces // twice. urlseed_max_request_bytes, // time to wait until a new retry of a web seed name lookup web_seed_name_lookup_retry, // the number of seconds between closing the file opened the longest // ago. 0 means to disable the feature. The purpose of this is to // periodically close files to trigger the operating system flushing // disk cache. Specifically it has been observed to be required on // windows to not have the disk cache grow indefinitely. // This defaults to 120 seconds on windows, and disabled on other // systems. close_file_interval, // When uTP experiences packet loss, it will reduce the congestion // window, and not reduce it again for this many milliseconds, even if // experiencing another lost packet. utp_cwnd_reduce_timer, max_int_setting_internal }; enum settings_counts_t { num_string_settings = max_string_setting_internal - string_type_base, num_bool_settings = max_bool_setting_internal - bool_type_base, num_int_settings = max_int_setting_internal - int_type_base }; enum suggest_mode_t { no_piece_suggestions = 0, suggest_read_cache = 1 }; enum choking_algorithm_t { fixed_slots_choker = 0, rate_based_choker = 2, bittyrant_choker = 3 }; enum seed_choking_algorithm_t { round_robin, fastest_upload, anti_leech }; enum io_buffer_mode_t { enable_os_cache = 0, #ifndef TORRENT_NO_DEPRECATE disable_os_cache_for_aligned_files TORRENT_DEPRECATED_ENUM = 2, #else deprecated = 1, #endif disable_os_cache = 2 }; enum bandwidth_mixed_algo_t { // disables the mixed mode bandwidth balancing prefer_tcp = 0, // does not throttle uTP, throttles TCP to the same proportion // of throughput as there are TCP connections peer_proportional = 1 }; // the encoding policy options for use with // settings_pack::out_enc_policy and settings_pack::in_enc_policy. enum enc_policy { // Only encrypted connections are allowed. Incoming connections that // are not encrypted are closed and if the encrypted outgoing // connection fails, a non-encrypted retry will not be made. pe_forced, // encrypted connections are enabled, but non-encrypted connections // are allowed. An incoming non-encrypted connection will be accepted, // and if an outgoing encrypted connection fails, a non- encrypted // connection will be tried. pe_enabled, // only non-encrypted connections are allowed. pe_disabled }; // the encryption levels, to be used with // settings_pack::allowed_enc_level. enum enc_level { // use only plaintext encryption pe_plaintext = 1, // use only rc4 encryption pe_rc4 = 2, // allow both pe_both = 3 }; enum proxy_type_t { // This is the default, no proxy server is used, all other fields are // ignored. none, // The server is assumed to be a `SOCKS4 server`_ that requires a // username. // // .. _`SOCKS4 server`: http://www.ufasoft.com/doc/socks4_protocol.htm socks4, // The server is assumed to be a SOCKS5 server (`RFC 1928`_) that does // not require any authentication. The username and password are // ignored. // // .. _`RFC 1928`: http://www.faqs.org/rfcs/rfc1928.html socks5, // The server is assumed to be a SOCKS5 server that supports plain // text username and password authentication (`RFC 1929`_). The // username and password specified may be sent to the proxy if it // requires. // // .. _`RFC 1929`: http://www.faqs.org/rfcs/rfc1929.html socks5_pw, // The server is assumed to be an HTTP proxy. If the transport used // for the connection is non-HTTP, the server is assumed to support // the CONNECT_ method. i.e. for web seeds and HTTP trackers, a plain // proxy will suffice. The proxy is assumed to not require // authorization. The username and password will not be used. // // .. _CONNECT: http://tools.ietf.org/html/draft-luotonen-web-proxy-tunneling-01 http, // The server is assumed to be an HTTP proxy that requires user // authorization. The username and password will be sent to the proxy. http_pw, // route through a i2p SAM proxy i2p_proxy }; private: std::vector > m_strings; std::vector > m_ints; std::vector > m_bools; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/sha1.hpp000066400000000000000000000013621351156116000231210ustar00rootroot00000000000000/* SHA-1 C++ conversion original version: SHA-1 in C By Steve Reid 100% Public Domain changelog at the end of sha1.cpp */ #ifndef TORRENT_SHA1_HPP_INCLUDED #define TORRENT_SHA1_HPP_INCLUDED #include "libtorrent/config.hpp" #include namespace libtorrent { struct TORRENT_EXTRA_EXPORT sha_ctx { boost::uint32_t state[5]; boost::uint32_t count[2]; boost::uint8_t buffer[64]; }; // we don't want these to clash with openssl's libcrypto TORRENT_EXTRA_EXPORT void SHA1_init(sha_ctx* context); TORRENT_EXTRA_EXPORT void SHA1_update(sha_ctx* context , boost::uint8_t const* data , boost::uint32_t len); TORRENT_EXTRA_EXPORT void SHA1_final(boost::uint8_t* digest, sha_ctx* context); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/sha1_hash.hpp000066400000000000000000000237051351156116000241310ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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_SHA1_HASH_HPP_INCLUDED #define TORRENT_SHA1_HASH_HPP_INCLUDED #include #include #include #include #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/aux_/byteswap.hpp" #include "libtorrent/hex.hpp" // to_hex, from_hex #if TORRENT_USE_IOSTREAM #include #include #endif #ifdef max #undef max #endif #ifdef min #undef min #endif namespace libtorrent { // This type holds a SHA-1 digest or any other kind of 20 byte // sequence. It implements a number of convenience functions, such // as bit operations, comparison operators etc. // // In libtorrent it is primarily used to hold info-hashes, piece-hashes, // peer IDs, node IDs etc. class TORRENT_EXPORT sha1_hash { enum { number_size = 5 }; public: // internal // the number of bytes of the number static const int size = number_size * sizeof(boost::uint32_t); // constructs an all-zero sha1-hash sha1_hash() { clear(); } // returns an all-F sha1-hash. i.e. the maximum value // representable by a 160 bit number (20 bytes). This is // a static member function. static sha1_hash max() { sha1_hash ret; memset(ret.m_number, 0xff, size); return ret; } // returns an all-zero sha1-hash. i.e. the minimum value // representable by a 160 bit number (20 bytes). This is // a static member function. static sha1_hash min() { sha1_hash ret; memset(ret.m_number, 0, size); return ret; } // copies 20 bytes from the pointer provided, into the sha1-hash. // The passed in string MUST be at least 20 bytes. NULL terminators // are ignored, ``s`` is treated like a raw memory buffer. explicit sha1_hash(char const* s) { if (s == 0) clear(); else std::memcpy(m_number, s, size); } explicit sha1_hash(std::string const& s) { TORRENT_ASSERT(s.size() >= 20); size_t sl = s.size() < size_t(size) ? s.size() : size_t(size); std::memcpy(m_number, s.c_str(), sl); } void assign(std::string const& s) { TORRENT_ASSERT(s.size() >= 20); size_t sl = s.size() < size_t(size) ? s.size() : size_t(size); std::memcpy(m_number, s.c_str(), sl); } void assign(char const* str) { std::memcpy(m_number, str, size); } char const* data() const { return reinterpret_cast(&m_number[0]); } char* data() { return reinterpret_cast(&m_number[0]); } // set the sha1-hash to all zeros. void clear() { std::memset(m_number, 0, size); } // return true if the sha1-hash is all zero. bool is_all_zeros() const { for (int i = 0; i < number_size; ++i) if (m_number[i] != 0) return false; return true; } // shift left ``n`` bits. sha1_hash& operator<<=(int n) { TORRENT_ASSERT(n >= 0); const int num_words = n / 32; if (num_words >= number_size) { std::memset(m_number, 0, size); return *this; } if (num_words > 0) { std::memmove(m_number, m_number + num_words , (number_size - num_words) * sizeof(boost::uint32_t)); std::memset(m_number + (number_size - num_words) , 0, num_words * sizeof(boost::uint32_t)); n -= num_words * 32; } if (n > 0) { // keep in mind that the uint32_t are stored in network // byte order, so they have to be byteswapped before // applying the shift operations, and then byteswapped // back again. m_number[0] = aux::network_to_host(m_number[0]); for (int i = 0; i < number_size - 1; ++i) { m_number[i] <<= n; m_number[i+1] = aux::network_to_host(m_number[i+1]); m_number[i] |= m_number[i+1] >> (32 - n); m_number[i] = aux::host_to_network(m_number[i]); } m_number[number_size-1] <<= n; m_number[number_size-1] = aux::host_to_network(m_number[number_size-1]); } return *this; } // shift r ``n`` bits. sha1_hash& operator>>=(int n) { TORRENT_ASSERT(n >= 0); const int num_words = n / 32; if (num_words >= number_size) { std::memset(m_number, 0, size_t(size)); return *this; } if (num_words > 0) { std::memmove(m_number + num_words , m_number, (number_size - num_words) * sizeof(boost::uint32_t)); std::memset(m_number, 0, num_words * sizeof(boost::uint32_t)); n -= num_words * 32; } if (n > 0) { // keep in mind that the uint32_t are stored in network // byte order, so they have to be byteswapped before // applying the shift operations, and then byteswapped // back again. m_number[number_size-1] = aux::network_to_host(m_number[number_size-1]); for (int i = number_size - 1; i > 0; --i) { m_number[i] >>= n; m_number[i-1] = aux::network_to_host(m_number[i-1]); m_number[i] |= (m_number[i-1] << (32 - n)) & 0xffffffff; m_number[i] = aux::host_to_network(m_number[i]); } m_number[0] >>= n; m_number[0] = aux::host_to_network(m_number[0]); } return *this; } // standard comparison operators bool operator==(sha1_hash const& n) const { return std::equal(n.m_number, n.m_number+number_size, m_number); } bool operator!=(sha1_hash const& n) const { return !std::equal(n.m_number, n.m_number+number_size, m_number); } bool operator<(sha1_hash const& n) const { for (int i = 0; i < number_size; ++i) { boost::uint32_t lhs = aux::network_to_host(m_number[i]); boost::uint32_t rhs = aux::network_to_host(n.m_number[i]); if (lhs < rhs) return true; if (lhs > rhs) return false; } return false; } // returns a bit-wise negated copy of the sha1-hash sha1_hash operator~() const { sha1_hash ret; for (int i = 0; i < number_size; ++i) ret.m_number[i] = ~m_number[i]; return ret; } // returns the bit-wise XOR of the two sha1-hashes. sha1_hash operator^(sha1_hash const& n) const { sha1_hash ret = *this; ret ^= n; return ret; } // in-place bit-wise XOR with the passed in sha1_hash. sha1_hash& operator^=(sha1_hash const& n) { for (int i = 0; i < number_size; ++i) m_number[i] ^= n.m_number[i]; return *this; } // returns the bit-wise AND of the two sha1-hashes. sha1_hash operator&(sha1_hash const& n) const { sha1_hash ret = *this; ret &= n; return ret; } // in-place bit-wise AND of the passed in sha1_hash sha1_hash& operator&=(sha1_hash const& n) { for (int i = 0; i < number_size; ++i) m_number[i] &= n.m_number[i]; return *this; } // in-place bit-wise OR of the two sha1-hash. sha1_hash& operator|=(sha1_hash const& n) { for (int i = 0; i < number_size; ++i) m_number[i] |= n.m_number[i]; return *this; } // accessors for specific bytes boost::uint8_t& operator[](int i) { TORRENT_ASSERT(i >= 0 && i < size); return reinterpret_cast(m_number)[i]; } boost::uint8_t const& operator[](int i) const { TORRENT_ASSERT(i >= 0 && i < size); return reinterpret_cast(m_number)[i]; } typedef const boost::uint8_t* const_iterator; typedef boost::uint8_t* iterator; // start and end iterators for the hash. The value type // of these iterators is ``boost::uint8_t``. const_iterator begin() const { return reinterpret_cast(m_number); } const_iterator end() const { return reinterpret_cast(m_number) + size; } iterator begin() { return reinterpret_cast(m_number); } iterator end() { return reinterpret_cast(m_number) + size; } // return a copy of the 20 bytes representing the sha1-hash as a std::string. // It's still a binary string with 20 binary characters. std::string to_string() const { return std::string(reinterpret_cast(&m_number[0]) , size_t(size)); } private: boost::uint32_t m_number[number_size]; }; typedef sha1_hash peer_id; inline std::size_t hash_value(sha1_hash const& b) { std::size_t ret; std::memcpy(&ret, &b[0], sizeof(ret)); return ret; } #if TORRENT_USE_IOSTREAM // print a sha1_hash object to an ostream as 40 hexadecimal digits inline std::ostream& operator<<(std::ostream& os, sha1_hash const& peer) { char out[41]; to_hex(reinterpret_cast(&peer[0]), sha1_hash::size, out); return os << out; } // read 40 hexadecimal digits from an istream into a sha1_hash inline std::istream& operator>>(std::istream& is, sha1_hash& peer) { char hex[40]; is.read(hex, 40); if (!from_hex(hex, 40, reinterpret_cast(&peer[0]))) is.setstate(std::ios_base::failbit); return is; } #endif // TORRENT_USE_IOSTREAM } #endif // TORRENT_PEER_ID_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/sliding_average.hpp000066400000000000000000000067251351156116000254200ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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_SLIDING_AVERAGE_HPP_INCLUDED #define TORRENT_SLIDING_AVERAGE_HPP_INCLUDED #include #include // for std::abs namespace libtorrent { // an exponential moving average accumulator. Add samples to it and it keeps // track of a moving mean value and an average deviation template struct sliding_average { sliding_average(): m_mean(0), m_average_deviation(0), m_num_samples(0) {} void add_sample(int s) { // fixed point s *= 64; int deviation = 0; if (m_num_samples > 0) deviation = std::abs(m_mean - s); if (m_num_samples < inverted_gain) ++m_num_samples; m_mean += (s - m_mean) / m_num_samples; if (m_num_samples > 1) { // the the exact same thing for deviation off the mean except -1 on // the samples, because the number of deviation samples always lags // behind by 1 (you need to actual samples to have a single deviation // sample). m_average_deviation += (deviation - m_average_deviation) / (m_num_samples - 1); } } int mean() const { return m_num_samples > 0 ? (m_mean + 32) / 64 : 0; } int avg_deviation() const { return m_num_samples > 1 ? (m_average_deviation + 32) / 64 : 0; } int num_samples() const { return m_num_samples; } private: // both of these are fixed point values (* 64) int m_mean; int m_average_deviation; // the number of samples we have received, but no more than inverted_gain // this is the effective inverted_gain int m_num_samples; }; struct average_accumulator { average_accumulator() : m_num_samples(0) , m_sample_sum(0) {} void add_sample(int s) { ++m_num_samples; m_sample_sum += s; } int mean() { int ret; if (m_num_samples == 0) ret = 0; else ret = int(m_sample_sum / m_num_samples); // in case we don't get any more samples, at least // let the average roll over, but only be worth a // single sample m_num_samples = 1; m_sample_sum = ret; return ret; } int m_num_samples; boost::uint64_t m_sample_sum; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/socket.hpp000066400000000000000000000150271351156116000235600ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" // 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 #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN // asio assumes that the windows error codes are defined already #include #endif #include #include #include #include #include #ifdef __OBJC__ #undef Protocol #endif #if defined TORRENT_BUILD_SIMULATOR #include "simulator/simulator.hpp" #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { #if defined TORRENT_BUILD_SIMULATOR using sim::asio::ip::udp; using sim::asio::ip::tcp; using sim::asio::async_write; using sim::asio::async_read; using sim::asio::null_buffers; #else using boost::asio::ip::tcp; using boost::asio::ip::udp; using boost::asio::async_write; using boost::asio::async_read; using boost::asio::null_buffers; #endif #ifdef TORRENT_WINDOWS #ifndef PROTECTION_LEVEL_UNRESTRICTED #define PROTECTION_LEVEL_UNRESTRICTED 10 #endif #ifndef IPV6_PROTECTION_LEVEL #define IPV6_PROTECTION_LEVEL 30 #endif struct v6_protection_level { v6_protection_level(int level): m_value(level) {} template int level(Protocol const&) const { return IPPROTO_IPV6; } template int name(Protocol const&) const { return IPV6_PROTECTION_LEVEL; } template int const* data(Protocol const&) const { return &m_value; } template size_t size(Protocol const&) const { return sizeof(m_value); } int m_value; }; struct exclusive_address_use { exclusive_address_use(int enable): m_value(enable) {} template int level(Protocol const&) const { return SOL_SOCKET; } template int name(Protocol const&) const { return SO_EXCLUSIVEADDRUSE; } template int const* data(Protocol const&) const { return &m_value; } template size_t size(Protocol const&) const { return sizeof(m_value); } int m_value; }; #endif // TORRENT_WINDOWS #ifdef IPV6_TCLASS struct traffic_class { traffic_class(char val): m_value(val) {} template int level(Protocol const&) const { return IPPROTO_IPV6; } template int name(Protocol const&) const { return IPV6_TCLASS; } template int const* data(Protocol const&) const { return &m_value; } template size_t size(Protocol const&) const { return sizeof(m_value); } int m_value; }; #endif struct type_of_service { #ifdef _WIN32 typedef DWORD tos_t; #else typedef int tos_t; #endif type_of_service(char val): m_value(val) {} template int level(Protocol const&) const { return IPPROTO_IP; } template int name(Protocol const&) const { return IP_TOS; } template tos_t const* data(Protocol const&) const { return &m_value; } template size_t size(Protocol const&) const { return sizeof(m_value); } tos_t m_value; }; #if defined IP_DONTFRAG || defined IP_MTU_DISCOVER || defined IP_DONTFRAGMENT #define TORRENT_HAS_DONT_FRAGMENT #endif #ifdef TORRENT_HAS_DONT_FRAGMENT // the order of these preprocessor tests matters. Windows defines both // IP_DONTFRAGMENT and IP_MTU_DISCOVER, but the latter is not supported // in general, the simple option of just setting the DF bit is preferred, if // it's available #if defined IP_DONTFRAG || defined IP_DONTFRAGMENT struct dont_fragment { dont_fragment(bool val) : m_value(val) {} template int level(Protocol const&) const { return IPPROTO_IP; } template int name(Protocol const&) const #if defined IP_DONTFRAG { return IP_DONTFRAG; } #else // defined IP_DONTFRAGMENT { return IP_DONTFRAGMENT; } #endif template int const* data(Protocol const&) const { return &m_value; } template size_t size(Protocol const&) const { return sizeof(m_value); } int m_value; }; #else // this is the fallback mechanism using the IP_MTU_DISCOVER option, which // does a little bit more than we want, it makes the kernel track an estimate // of the MTU and rejects packets immediately if they are believed to exceed // it. struct dont_fragment { dont_fragment(bool val) : m_value(val ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT) {} template int level(Protocol const&) const { return IPPROTO_IP; } template int name(Protocol const&) const { return IP_MTU_DISCOVER; } template int const* data(Protocol const&) const { return &m_value; } template size_t size(Protocol const&) const { return sizeof(m_value); } int m_value; }; #endif // IP_DONTFRAG vs. IP_MTU_DISCOVER #endif // TORRENT_HAS_DONT_FRAGMENT } #endif // TORRENT_SOCKET_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/socket_io.hpp000066400000000000000000000122361351156116000242460ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_IO_HPP_INCLUDED #define TORRENT_SOCKET_IO_HPP_INCLUDED #include "libtorrent/socket.hpp" #include "libtorrent/address.hpp" #include "libtorrent/io.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/bdecode.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/peer_id.hpp" // for sha1_hash #include namespace libtorrent { TORRENT_EXTRA_EXPORT std::string print_address(address const& addr); TORRENT_EXTRA_EXPORT std::string print_endpoint(tcp::endpoint const& ep); TORRENT_EXTRA_EXPORT std::string print_endpoint(udp::endpoint const& ep); TORRENT_EXTRA_EXPORT tcp::endpoint parse_endpoint(std::string str, error_code& ec); TORRENT_EXTRA_EXPORT std::string address_to_bytes(address const& a); // internal TORRENT_EXPORT std::string endpoint_to_bytes(udp::endpoint const& ep); TORRENT_EXTRA_EXPORT void hash_address(address const& ip, sha1_hash& h); namespace detail { template void write_address(address const& a, OutIt& out) { #if TORRENT_USE_IPV6 if (a.is_v4()) { #endif write_uint32(a.to_v4().to_ulong(), out); #if TORRENT_USE_IPV6 } else if (a.is_v6()) { typedef address_v6::bytes_type bytes_t; bytes_t bytes = a.to_v6().to_bytes(); for (bytes_t::iterator i = bytes.begin() , end(bytes.end()); i != end; ++i) write_uint8(*i, out); } #endif } template address read_v4_address(InIt& in) { unsigned long ip = read_uint32(in); return address_v4(ip); } #if TORRENT_USE_IPV6 template address read_v6_address(InIt& in) { typedef 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 address_v6(bytes); } #endif template void write_endpoint(Endpoint const& e, OutIt& out) { write_address(e.address(), out); write_uint16(e.port(), out); } template Endpoint read_v4_endpoint(InIt& in) { address addr = read_v4_address(in); int port = read_uint16(in); return Endpoint(addr, port); } #if TORRENT_USE_IPV6 template Endpoint read_v6_endpoint(InIt& in) { address addr = read_v6_address(in); int port = read_uint16(in); return Endpoint(addr, port); } #endif template void read_endpoint_list(libtorrent::bdecode_node const& n , std::vector& epl) { using namespace libtorrent; if (n.type() != bdecode_node::list_t) return; for (int i = 0; i < n.list_size(); ++i) { bdecode_node e = n.list_at(i); if (e.type() != bdecode_node::string_t) return; if (e.string_length() < 6) continue; char const* in = e.string_ptr(); if (e.string_length() == 6) epl.push_back(read_v4_endpoint(in)); #if TORRENT_USE_IPV6 else if (e.string_length() == 18) epl.push_back(read_v6_endpoint(in)); #endif } } } template void read_endpoint_list(libtorrent::entry const* n, std::vector& epl) { using namespace libtorrent; if (n->type() != entry::list_t) return; entry::list_type const& contacts = n->list(); for (entry::list_type::const_iterator i = contacts.begin() , end(contacts.end()); i != end; ++i) { if (i->type() != entry::string_t) return; 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(detail::read_v4_endpoint(in)); #if TORRENT_USE_IPV6 else if (p.size() == 18) epl.push_back(detail::read_v6_endpoint(in)); #endif } } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/socket_type.hpp000066400000000000000000000254461351156116000246270ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_TYPE #define TORRENT_SOCKET_TYPE #include "libtorrent/config.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/socks5_stream.hpp" #include "libtorrent/http_stream.hpp" #include "libtorrent/i2p_stream.hpp" #include "libtorrent/utp_stream.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/max.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifdef TORRENT_USE_OPENSSL #include "libtorrent/ssl_stream.hpp" #endif #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/debug.hpp" #endif #if defined TORRENT_OS2 && defined ioc #undef ioc #endif #if TORRENT_USE_I2P #define TORRENT_SOCKTYPE_I2P_FORWARD(x) \ case socket_type_int_impl::value: \ get()->x; break; #define TORRENT_SOCKTYPE_I2P_FORWARD_RET(x, def) \ case socket_type_int_impl::value: \ return get()->x; #else // TORRENT_USE_I2P #define TORRENT_SOCKTYPE_I2P_FORWARD(x) #define TORRENT_SOCKTYPE_I2P_FORWARD_RET(x, def) #endif #ifdef TORRENT_USE_OPENSSL #define TORRENT_SOCKTYPE_SSL_FORWARD(x) \ case socket_type_int_impl >::value: \ get >()->x; break; \ case socket_type_int_impl >::value: \ get >()->x; break; \ case socket_type_int_impl >::value: \ get >()->x; break; \ case socket_type_int_impl >::value: \ get >()->x; break; #define TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) \ case socket_type_int_impl >::value: \ return get >()->x; \ case socket_type_int_impl >::value: \ return get >()->x; \ case socket_type_int_impl >::value: \ return get >()->x; \ case socket_type_int_impl >::value: \ return get >()->x; #else #define TORRENT_SOCKTYPE_SSL_FORWARD(x) #define TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) #endif #define TORRENT_SOCKTYPE_FORWARD(x) \ switch (m_type) { \ case socket_type_int_impl::value: \ get()->x; break; \ case socket_type_int_impl::value: \ get()->x; break; \ case socket_type_int_impl::value: \ get()->x; break; \ case socket_type_int_impl::value: \ get()->x; break; \ TORRENT_SOCKTYPE_I2P_FORWARD(x) \ TORRENT_SOCKTYPE_SSL_FORWARD(x) \ default: TORRENT_ASSERT(false); \ } #define TORRENT_SOCKTYPE_FORWARD_RET(x, def) \ switch (m_type) { \ case socket_type_int_impl::value: \ return get()->x; \ case socket_type_int_impl::value: \ return get()->x; \ case socket_type_int_impl::value: \ return get()->x; \ case socket_type_int_impl::value: \ return get()->x; \ TORRENT_SOCKTYPE_I2P_FORWARD_RET(x, def) \ TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) \ default: TORRENT_ASSERT(false); return def; \ } namespace libtorrent { template struct socket_type_int_impl { enum { value = 0 }; }; template <> struct socket_type_int_impl { enum { value = 1 }; }; template <> struct socket_type_int_impl { enum { value = 2 }; }; template <> struct socket_type_int_impl { enum { value = 3 }; }; template <> struct socket_type_int_impl { enum { value = 4 }; }; #if TORRENT_USE_I2P template <> struct socket_type_int_impl { enum { value = 5 }; }; #endif #ifdef TORRENT_USE_OPENSSL template <> struct socket_type_int_impl > { enum { value = 6 }; }; template <> struct socket_type_int_impl > { enum { value = 7 }; }; template <> struct socket_type_int_impl > { enum { value = 8 }; }; template <> struct socket_type_int_impl > { enum { value = 9 }; }; #endif struct TORRENT_EXTRA_EXPORT socket_type { typedef tcp::socket::endpoint_type endpoint_type; typedef tcp::socket::protocol_type protocol_type; typedef tcp::socket::receive_buffer_size receive_buffer_size; typedef tcp::socket::send_buffer_size send_buffer_size; explicit socket_type(io_service& ios): m_io_service(ios), m_type(0) {} ~socket_type(); io_service& get_io_service() const; bool is_open() const; char const* type_name() const; #ifndef BOOST_NO_EXCEPTIONS void open(protocol_type const& p); void close(); endpoint_type local_endpoint() const; endpoint_type remote_endpoint() const; void bind(endpoint_type const& endpoint); std::size_t available() const; #endif void open(protocol_type const& p, error_code& ec); void close(error_code& ec); // this is only relevant for uTP connections void set_close_reason(boost::uint16_t code); boost::uint16_t get_close_reason(); endpoint_type local_endpoint(error_code& ec) const; endpoint_type remote_endpoint(error_code& ec) const; void bind(endpoint_type const& endpoint, error_code& ec); std::size_t available(error_code& ec) const; int type() const; template std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) { TORRENT_SOCKTYPE_FORWARD_RET(read_some(buffers, ec), 0) } template void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) { TORRENT_SOCKTYPE_FORWARD(async_read_some(buffers, handler)) } template std::size_t write_some(Const_Buffers const& buffers, error_code& ec) { TORRENT_SOCKTYPE_FORWARD_RET(write_some(buffers, ec), 0) } template void async_write_some(Const_Buffers const& buffers, Handler const& handler) { TORRENT_SOCKTYPE_FORWARD(async_write_some(buffers, handler)) } template void async_connect(endpoint_type const& endpoint, Handler const& handler) { TORRENT_SOCKTYPE_FORWARD(async_connect(endpoint, handler)) } #ifndef BOOST_NO_EXCEPTIONS template void io_control(IO_Control_Command& ioc) { TORRENT_SOCKTYPE_FORWARD(io_control(ioc)) } template std::size_t read_some(Mutable_Buffers const& buffers) { TORRENT_SOCKTYPE_FORWARD_RET(read_some(buffers), 0) } #endif template void io_control(IO_Control_Command& ioc, error_code& ec) { TORRENT_SOCKTYPE_FORWARD(io_control(ioc, ec)) } #ifndef BOOST_NO_EXCEPTIONS template void set_option(SettableSocketOption const& opt) { TORRENT_SOCKTYPE_FORWARD(set_option(opt)) } #endif template error_code set_option(SettableSocketOption const& opt, error_code& ec) { TORRENT_SOCKTYPE_FORWARD_RET(set_option(opt, ec), ec) } void non_blocking(bool b, error_code& ec) { TORRENT_SOCKTYPE_FORWARD(non_blocking(b, ec)) } #ifndef BOOST_NO_EXCEPTIONS void non_blocking(bool b) { TORRENT_SOCKTYPE_FORWARD(non_blocking(b)) } #endif #ifndef BOOST_NO_EXCEPTIONS template void get_option(GettableSocketOption& opt) { TORRENT_SOCKTYPE_FORWARD(get_option(opt)) } #endif template error_code get_option(GettableSocketOption& opt, error_code& ec) { TORRENT_SOCKTYPE_FORWARD_RET(get_option(opt, ec), ec) } template void instantiate(io_service& ios, void* userdata = 0) { TORRENT_UNUSED(ios); TORRENT_ASSERT(&ios == &m_io_service); construct(socket_type_int_impl::value, userdata); } template S* get() { if (m_type != socket_type_int_impl::value) return 0; return reinterpret_cast(&m_data); } template S const* get() const { if (m_type != socket_type_int_impl::value) return 0; return reinterpret_cast(&m_data); } private: // explicitly disallow assignment, to silence msvc warning socket_type& operator=(socket_type const&); void destruct(); void construct(int type, void* userdata); io_service& m_io_service; int m_type; enum { storage_size = max9< sizeof(tcp::socket) , sizeof(socks5_stream) , sizeof(http_stream) , sizeof(utp_stream) #if TORRENT_USE_I2P , sizeof(i2p_stream) #else , 0 #endif #ifdef TORRENT_USE_OPENSSL , sizeof(ssl_stream) , sizeof(ssl_stream) , sizeof(ssl_stream) , sizeof(ssl_stream) #else , 0, 0, 0, 0 #endif >::value }; boost::aligned_storage::type m_data; }; // returns true if this socket is an SSL socket bool is_ssl(socket_type const& s); // returns true if this is a uTP socket bool is_utp(socket_type const& s); #if TORRENT_USE_I2P // returns true if this is an i2p socket bool is_i2p(socket_type const& s); #endif // assuming the socket_type s is an ssl socket, make sure it // verifies the hostname in its SSL handshake void setup_ssl_hostname(socket_type& s, std::string const& hostname, error_code& ec); // properly shuts down SSL sockets. holder keeps s alive void async_shutdown(socket_type& s, boost::shared_ptr holder); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/socket_type_fwd.hpp000066400000000000000000000031641351156116000254600ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_TYPE_FWD_HPP #define TORRENT_SOCKET_TYPE_FWD_HPP namespace libtorrent { struct socket_type; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/socks5_stream.hpp000066400000000000000000000141701351156116000250500ustar00rootroot00000000000000/* Copyright (c) 2007-2018, Arvid Norberg All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TORRENT_SOCKS5_STREAM_HPP_INCLUDED #define TORRENT_SOCKS5_STREAM_HPP_INCLUDED #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/proxy_base.hpp" #include "libtorrent/broadcast_socket.hpp" // for is_ip_address #include "libtorrent/assert.hpp" #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/debug.hpp" #endif namespace libtorrent { namespace socks_error { // SOCKS5 error values. If an error_code has the // socks error category (get_socks_category()), these // are the error values. enum socks_error_code { no_error = 0, unsupported_version, unsupported_authentication_method, unsupported_authentication_version, authentication_error, username_required, general_failure, command_not_supported, no_identd, identd_error, num_errors }; // internal TORRENT_EXPORT boost::system::error_code make_error_code(socks_error_code e); } // namespace socks_error // returns the error_category for SOCKS5 errors TORRENT_EXPORT boost::system::error_category& socks_category(); #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED TORRENT_EXPORT boost::system::error_category& get_socks_category(); #endif class socks5_stream : public proxy_base { public: // commands enum { socks5_connect = 1, socks5_udp_associate = 3 }; explicit socks5_stream(io_service& io_service) : proxy_base(io_service) , m_version(5) , m_command(socks5_connect) {} void set_version(int v) { m_version = v; } void set_command(int c) { TORRENT_ASSERT(c == socks5_connect || c == socks5_udp_associate); m_command = c; } void set_username(std::string const& user , std::string const& password) { m_user = user; m_password = password; } void set_dst_name(std::string const& host) { // if this assert trips, set_dst_name() is called wth an IP address rather // than a hostname. Instead, resolve the IP into an address and pass it to // async_connect instead TORRENT_ASSERT(!is_ip_address(host.c_str())); m_dst_name = host; if (m_dst_name.size() > 255) m_dst_name.resize(255); } void close(error_code& ec) { m_dst_name.clear(); proxy_base::close(ec); } #ifndef BOOST_NO_EXCEPTIONS void close() { m_dst_name.clear(); proxy_base::close(); } #endif // TODO: 2 add async_connect() that takes a hostname and port as well template void async_connect(endpoint_type const& endpoint, Handler const& handler) { // make sure we don't try to connect to INADDR_ANY. binding is fine, // and using a hostname is fine on SOCKS version 5. TORRENT_ASSERT(endpoint.address() != address() || (!m_dst_name.empty() && m_version == 5)); m_remote_endpoint = endpoint; // the connect is split up in the following steps: // 1. resolve name of proxy server // 2. connect to proxy server // 3. if version == 5: // 3.1 send SOCKS5 authentication method message // 3.2 read SOCKS5 authentication response // 3.3 send username+password // 4. send SOCKS command message // to avoid unnecessary copying of the handler, // store it in a shaed_ptr boost::shared_ptr h(new handler_type(handler)); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("socks5_stream::name_lookup"); #endif tcp::resolver::query q(m_hostname, to_string(m_port).elems); m_resolver.async_resolve(q, boost::bind( &socks5_stream::name_lookup, this, _1, _2, h)); } private: void name_lookup(error_code const& e, tcp::resolver::iterator i , boost::shared_ptr h); void connected(error_code const& e, boost::shared_ptr h); void handshake1(error_code const& e, boost::shared_ptr h); void handshake2(error_code const& e, boost::shared_ptr h); void handshake3(error_code const& e, boost::shared_ptr h); void handshake4(error_code const& e, boost::shared_ptr h); void socks_connect(boost::shared_ptr h); void connect1(error_code const& e, boost::shared_ptr h); void connect2(error_code const& e, boost::shared_ptr h); void connect3(error_code const& e, boost::shared_ptr h); // send and receive buffer std::vector m_buffer; // proxy authentication std::string m_user; std::string m_password; std::string m_dst_name; int m_version; // the socks command to send for this connection (connect or udp associate) int m_command; }; } namespace boost { namespace system { template<> struct is_error_code_enum { static const bool value = true; }; } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/ssl_stream.hpp000066400000000000000000000213201351156116000244350ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_SSL_STREAM_HPP_INCLUDED #define TORRENT_SSL_STREAM_HPP_INCLUDED #ifdef TORRENT_USE_OPENSSL #include "libtorrent/socket.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/aux_/openssl.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include // openssl seems to believe it owns // this name in every single scope #undef set_key #if defined TORRENT_BUILD_SIMULATOR #include "simulator/simulator.hpp" #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { namespace ssl { #if defined TORRENT_BUILD_SIMULATOR using sim::asio::ssl::context; using sim::asio::ssl::stream_base; using sim::asio::ssl::stream; #else using boost::asio::ssl::context; using boost::asio::ssl::stream_base; using boost::asio::ssl::stream; #endif } template class ssl_stream { public: explicit ssl_stream(io_service& io_service, ssl::context& ctx) : m_sock(io_service, ctx) { } typedef typename boost::asio::ssl::stream sock_type; typedef typename sock_type::next_layer_type next_layer_type; typedef typename Stream::lowest_layer_type lowest_layer_type; typedef typename Stream::endpoint_type endpoint_type; typedef typename Stream::protocol_type protocol_type; #if BOOST_VERSION >= 106600 typedef typename sock_type::executor_type executor_type; executor_type get_executor() { return m_sock.get_executor(); } #endif void set_host_name(std::string name) { aux::openssl_set_tlsext_hostname(m_sock.native_handle(), name.c_str()); } template void set_verify_callback(T const& fun, error_code& ec) { m_sock.set_verify_callback(fun, ec); } #if BOOST_VERSION >= 104700 SSL* native_handle() { return m_sock.native_handle(); } #endif typedef boost::function handler_type; template void async_connect(endpoint_type const& endpoint, Handler const& handler) { // the connect is split up in the following steps: // 1. connect to peer // 2. perform SSL client handshake // to avoid unnecessary copying of the handler, // store it in a shared_ptr boost::shared_ptr h(new handler_type(handler)); m_sock.next_layer().async_connect(endpoint , boost::bind(&ssl_stream::connected, this, _1, h)); } template void async_accept_handshake(Handler const& handler) { // this is used for accepting SSL connections boost::shared_ptr h(new handler_type(handler)); m_sock.async_handshake(ssl::stream_base::server , boost::bind(&ssl_stream::handshake, this, _1, h)); } void accept_handshake(error_code& ec) { // this is used for accepting SSL connections m_sock.handshake(ssl::stream_base::server, ec); } template void async_shutdown(Handler const& handler) { error_code ec; m_sock.next_layer().cancel(ec); m_sock.async_shutdown(handler); } void shutdown(error_code& ec) { m_sock.shutdown(ec); } template void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) { m_sock.async_read_some(buffers, handler); } template std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) { return m_sock.read_some(buffers, ec); } #ifndef BOOST_NO_EXCEPTIONS template void set_option(SettableSocketOption const& opt) { m_sock.next_layer().set_option(opt); } #endif template error_code set_option(SettableSocketOption const& opt, error_code& ec) { return m_sock.next_layer().set_option(opt, ec); } #ifndef BOOST_NO_EXCEPTIONS template void get_option(GettableSocketOption& opt) { m_sock.next_layer().get_option(opt); } #endif template error_code get_option(GettableSocketOption& opt, error_code& ec) { return m_sock.next_layer().get_option(opt, ec); } #ifndef BOOST_NO_EXCEPTIONS template std::size_t read_some(Mutable_Buffers const& buffers) { return m_sock.read_some(buffers); } template void io_control(IO_Control_Command& ioc) { m_sock.next_layer().io_control(ioc); } #endif template void io_control(IO_Control_Command& ioc, error_code& ec) { m_sock.next_layer().io_control(ioc, ec); } #ifndef BOOST_NO_EXCEPTIONS void non_blocking(bool b) { m_sock.next_layer().non_blocking(b); } #endif error_code non_blocking(bool b, error_code& ec) { return m_sock.next_layer().non_blocking(b, ec); } template void async_write_some(Const_Buffers const& buffers, Handler const& handler) { m_sock.async_write_some(buffers, handler); } template std::size_t write_some(Const_Buffers const& buffers, error_code& ec) { return m_sock.write_some(buffers, ec); } // the SSL stream may cache 17 kiB internally, and there's no way of // asking how large its buffer is. 17 kiB isn't very much though, so it // seems fine to potentially over-estimate the number of bytes available. #ifndef BOOST_NO_EXCEPTIONS std::size_t available() const { return 17 * 1024 + const_cast(m_sock).next_layer().available(); } #endif std::size_t available(error_code& ec) const { return 17 * 1024 + const_cast(m_sock).next_layer().available(ec); } #ifndef BOOST_NO_EXCEPTIONS void bind(endpoint_type const& endpoint) { m_sock.next_layer().bind(endpoint); } #endif void bind(endpoint_type const& endpoint, error_code& ec) { m_sock.next_layer().bind(endpoint, ec); } #ifndef BOOST_NO_EXCEPTIONS void open(protocol_type const& p) { m_sock.next_layer().open(p); } #endif void open(protocol_type const& p, error_code& ec) { m_sock.next_layer().open(p, ec); } bool is_open() const { return const_cast(m_sock).next_layer().is_open(); } #ifndef BOOST_NO_EXCEPTIONS void close() { m_sock.next_layer().close(); } #endif void close(error_code& ec) { m_sock.next_layer().close(ec); } #ifndef BOOST_NO_EXCEPTIONS endpoint_type remote_endpoint() const { return const_cast(m_sock).next_layer().remote_endpoint(); } #endif endpoint_type remote_endpoint(error_code& ec) const { return const_cast(m_sock).next_layer().remote_endpoint(ec); } #ifndef BOOST_NO_EXCEPTIONS endpoint_type local_endpoint() const { return const_cast(m_sock).next_layer().local_endpoint(); } #endif endpoint_type local_endpoint(error_code& ec) const { return const_cast(m_sock).next_layer().local_endpoint(ec); } io_service& get_io_service() { return m_sock.get_io_service(); } lowest_layer_type& lowest_layer() { return m_sock.lowest_layer(); } next_layer_type& next_layer() { return m_sock.next_layer(); } private: void connected(error_code const& e, boost::shared_ptr h) { if (e) { (*h)(e); return; } m_sock.async_handshake(ssl::stream_base::client , boost::bind(&ssl_stream::handshake, this, _1, h)); } void handshake(error_code const& e, boost::shared_ptr h) { (*h)(e); } ssl::stream m_sock; }; } #endif // TORRENT_USE_OPENSSL #endif libtorrent-rasterbar-1.1.13/include/libtorrent/stack_allocator.hpp000066400000000000000000000057051351156116000254370ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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_STACK_ALLOCATOR #include "libtorrent/assert.hpp" #include "libtorrent/buffer.hpp" namespace libtorrent { namespace aux { struct stack_allocator { stack_allocator() {} int copy_string(std::string const& str) { int ret = int(m_storage.size()); m_storage.resize(ret + str.length() + 1); strcpy(&m_storage[ret], str.c_str()); return ret; } int copy_string(char const* str) { int ret = int(m_storage.size()); int len = strlen(str); m_storage.resize(ret + len + 1); strcpy(&m_storage[ret], str); return ret; } int copy_buffer(char const* buf, int size) { int ret = int(m_storage.size()); if (size < 1) return -1; m_storage.resize(ret + size); memcpy(&m_storage[ret], buf, size); return ret; } int allocate(int bytes) { if (bytes < 1) return -1; int ret = int(m_storage.size()); m_storage.resize(ret + bytes); return ret; } char* ptr(int idx) { if(idx < 0) return NULL; TORRENT_ASSERT(idx < int(m_storage.size())); return &m_storage[idx]; } char const* ptr(int idx) const { if(idx < 0) return NULL; TORRENT_ASSERT(idx < int(m_storage.size())); return &m_storage[idx]; } void swap(stack_allocator& rhs) { m_storage.swap(rhs.m_storage); } void reset() { m_storage.clear(); } private: // non-copyable stack_allocator(stack_allocator const&); stack_allocator& operator=(stack_allocator const&); buffer m_storage; }; } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/stat.hpp000066400000000000000000000202631351156116000232410ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include #include #include #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { class TORRENT_EXTRA_EXPORT stat_channel { public: stat_channel() : m_total_counter(0) , m_counter(0) , m_5_sec_average(0) {} void operator+=(stat_channel const& s) { TORRENT_ASSERT(m_counter < (std::numeric_limits::max)() - s.m_counter); m_counter += s.m_counter; TORRENT_ASSERT(m_total_counter < (std::numeric_limits::max)() - s.m_counter); m_total_counter += s.m_counter; } void add(int count) { TORRENT_ASSERT(count >= 0); TORRENT_ASSERT(m_counter < (std::numeric_limits::max)() - count); m_counter += count; TORRENT_ASSERT(m_total_counter < (std::numeric_limits::max)() - count); m_total_counter += count; } // should be called once every second void second_tick(int tick_interval_ms); int rate() const { return m_5_sec_average; } int low_pass_rate() const { return m_5_sec_average; } boost::int64_t total() const { return m_total_counter; } void offset(boost::int64_t c) { TORRENT_ASSERT(m_total_counter < (std::numeric_limits::max)() - c); m_total_counter += c; } int counter() const { return m_counter; } void clear() { m_counter = 0; m_5_sec_average = 0; m_total_counter = 0; } private: // total counters boost::uint64_t m_total_counter; // the accumulator for this second. boost::uint32_t m_counter; // sliding average boost::uint32_t m_5_sec_average; }; class TORRENT_EXTRA_EXPORT stat { friend class invariant_access; public: void operator+=(const stat& s) { for (int i = 0; i < num_channels; ++i) m_stat[i] += s.m_stat[i]; } void sent_syn(bool ipv6) { m_stat[upload_ip_protocol].add(ipv6 ? 60 : 40); } void received_synack(bool ipv6) { // we received SYN-ACK and also sent ACK back m_stat[download_ip_protocol].add(ipv6 ? 60 : 40); m_stat[upload_ip_protocol].add(ipv6 ? 60 : 40); } void received_bytes(int bytes_payload, int bytes_protocol) { TORRENT_ASSERT(bytes_payload >= 0); TORRENT_ASSERT(bytes_protocol >= 0); m_stat[download_payload].add(bytes_payload); m_stat[download_protocol].add(bytes_protocol); } void sent_bytes(int bytes_payload, int bytes_protocol) { TORRENT_ASSERT(bytes_payload >= 0); TORRENT_ASSERT(bytes_protocol >= 0); m_stat[upload_payload].add(bytes_payload); m_stat[upload_protocol].add(bytes_protocol); } // and IP packet was received or sent // account for the overhead caused by it void trancieve_ip_packet(int bytes_transferred, bool ipv6) { // one TCP/IP packet header for the packet // sent or received, and one for the ACK // The IPv4 header is 20 bytes // and IPv6 header is 40 bytes const int header = (ipv6 ? 40 : 20) + 20; const int mtu = 1500; const int packet_size = mtu - header; const int overhead = (std::max)(1, (bytes_transferred + packet_size - 1) / packet_size) * header; m_stat[download_ip_protocol].add(overhead); m_stat[upload_ip_protocol].add(overhead); } int upload_ip_overhead() const { return m_stat[upload_ip_protocol].counter(); } int download_ip_overhead() const { return m_stat[download_ip_protocol].counter(); } // should be called once every second void second_tick(int tick_interval_ms) { for (int i = 0; i < num_channels; ++i) m_stat[i].second_tick(tick_interval_ms); } int low_pass_upload_rate() const { return m_stat[upload_payload].low_pass_rate() + m_stat[upload_protocol].low_pass_rate() + m_stat[upload_ip_protocol].low_pass_rate(); } int low_pass_download_rate() const { return m_stat[download_payload].low_pass_rate() + m_stat[download_protocol].low_pass_rate() + m_stat[download_ip_protocol].low_pass_rate(); } int upload_rate() const { return m_stat[upload_payload].rate() + m_stat[upload_protocol].rate() + m_stat[upload_ip_protocol].rate(); } int download_rate() const { return m_stat[download_payload].rate() + m_stat[download_protocol].rate() + m_stat[download_ip_protocol].rate(); } boost::int64_t total_upload() const { return m_stat[upload_payload].total() + m_stat[upload_protocol].total() + m_stat[upload_ip_protocol].total(); } boost::int64_t total_download() const { return m_stat[download_payload].total() + m_stat[download_protocol].total() + m_stat[download_ip_protocol].total(); } int upload_payload_rate() const { return m_stat[upload_payload].rate(); } int download_payload_rate() const { return m_stat[download_payload].rate(); } boost::int64_t total_payload_upload() const { return m_stat[upload_payload].total(); } boost::int64_t total_payload_download() const { return m_stat[download_payload].total(); } boost::int64_t total_protocol_upload() const { return m_stat[upload_protocol].total(); } boost::int64_t total_protocol_download() const { return m_stat[download_protocol].total(); } boost::int64_t total_transfer(int channel) const { return m_stat[channel].total(); } int transfer_rate(int channel) const { return m_stat[channel].rate(); } // this is used to offset the statistics when a // peer_connection is opened and have some previous // transfers from earlier connections. void add_stat(boost::int64_t downloaded, boost::int64_t uploaded) { m_stat[download_payload].offset(downloaded); m_stat[upload_payload].offset(uploaded); } int last_payload_downloaded() const { return m_stat[download_payload].counter(); } int last_payload_uploaded() const { return m_stat[upload_payload].counter(); } int last_protocol_downloaded() const { return m_stat[download_protocol].counter(); } int last_protocol_uploaded() const { return m_stat[upload_protocol].counter(); } // these are the channels we keep stats for enum { // TODO: 3 everything but payload counters and rates could probably be // removed from here upload_payload, upload_protocol, download_payload, download_protocol, upload_ip_protocol, download_ip_protocol, num_channels }; void clear() { for (int i = 0; i < num_channels; ++i) m_stat[i].clear(); } stat_channel const& operator[](int i) const { TORRENT_ASSERT(i >= 0 && i < num_channels); return m_stat[i]; } private: stat_channel m_stat[num_channels]; }; } #endif // TORRENT_STAT_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/stat_cache.hpp000066400000000000000000000051241351156116000243630ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_STAT_CACHE_HPP #define TORRENT_STAT_CACHE_HPP #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/thread.hpp" namespace libtorrent { struct TORRENT_EXTRA_EXPORT stat_cache { stat_cache(); ~stat_cache(); void init(int num_files); enum { cache_error = -1, not_in_cache = -2, no_exist = -3 }; // returns the size of the file or one // of the enums, noent or not_in_cache boost::int64_t get_filesize(int i) const; time_t get_filetime(int i) const; void set_cache(int i, boost::int64_t size, time_t time); void set_noexist(int i); void set_error(int i); void set_dirty(int i); void clear(); private: struct stat_cache_t { stat_cache_t(boost::int64_t s, time_t t = 0): file_size(s), file_time(t) {} boost::int64_t file_size; time_t file_time; }; mutable mutex m_mutex; std::vector m_stat_cache; }; } #endif // TORRENT_STAT_CACHE_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/storage.hpp000066400000000000000000000736131351156116000237410ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/piece_picker.hpp" #include "libtorrent/peer_request.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/file.hpp" #include "libtorrent/disk_buffer_holder.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/storage_defs.hpp" #include "libtorrent/file_pool.hpp" // pool_file_status #include "libtorrent/part_file.hpp" #include "libtorrent/stat_cache.hpp" #include "libtorrent/bdecode.hpp" #include "libtorrent/bitfield.hpp" #include "libtorrent/performance_counters.hpp" // OVERVIEW // // libtorrent provides a customization point for storage of data. By default, // (``default_storage``) downloaded files are saved to disk according with the // general conventions of bittorrent clients, mimicing the original file layout // when the torrent was created. The libtorrent user may define a custom // storage to store piece data in a different way. // // A custom storage implementation must derive from and implement the // storage_interface. You must also provide a function that constructs the // custom storage object and provide this function to the add_torrent() call // via add_torrent_params. Either passed in to the constructor or by setting // the add_torrent_params::storage field. // // This is an example storage implementation that stores all pieces in a // ``std::map``, i.e. in RAM. It's not necessarily very useful in practice, but // illustrates the basics of implementing a custom storage. // // .. code:: c++ // // struct temp_storage : storage_interface // { // temp_storage(file_storage const& fs) : m_files(fs) {} // virtual bool initialize(storage_error& se) { return false; } // virtual bool has_any_file() { return false; } // virtual int read(char* buf, int piece, int offset, int size) // { // std::map >::const_iterator i = m_file_data.find(piece); // if (i == m_file_data.end()) return 0; // int available = i->second.size() - offset; // if (available <= 0) return 0; // if (available > size) available = size; // memcpy(buf, &i->second[offset], available); // return available; // } // virtual int write(const char* buf, int piece, int offset, int size) // { // std::vector& data = m_file_data[piece]; // if (data.size() < offset + size) data.resize(offset + size); // std::memcpy(&data[offset], buf, size); // return size; // } // virtual bool rename_file(int file, std::string const& new_name) // { assert(false); return false; } // virtual bool move_storage(std::string const& save_path) { return false; } // virtual bool verify_resume_data(bdecode_node const& rd // , std::vector const* links // , storage_error& error) { return false; } // virtual bool write_resume_data(entry& rd) const { return false; } // virtual boost::int64_t physical_offset(int piece, int offset) // { return piece * m_files.piece_length() + offset; }; // virtual sha1_hash hash_for_slot(int piece, partial_hash& ph, int piece_size) // { // int left = piece_size - ph.offset; // assert(left >= 0); // if (left > 0) // { // std::vector& data = m_file_data[piece]; // // if there are padding files, those blocks will be considered // // completed even though they haven't been written to the storage. // // in this case, just extend the piece buffer to its full size // // and fill it with zeros. // if (data.size() < piece_size) data.resize(piece_size, 0); // ph.h.update(&data[ph.offset], left); // } // return ph.h.final(); // } // virtual bool release_files() { return false; } // virtual bool delete_files() { return false; } // // std::map > m_file_data; // file_storage m_files; // }; // // storage_interface* temp_storage_constructor(storage_params const& params) // { // return new temp_storage(*params.files); // } namespace libtorrent { class session; struct file_pool; struct disk_io_job; struct disk_buffer_pool; struct cache_status; namespace aux { struct session_settings; } struct cached_piece_entry; TORRENT_EXTRA_EXPORT int copy_bufs(file::iovec_t const* bufs, int bytes, file::iovec_t* target); TORRENT_EXTRA_EXPORT void advance_bufs(file::iovec_t*& bufs, int bytes); TORRENT_EXTRA_EXPORT void clear_bufs(file::iovec_t const* bufs, int num_bufs); // flags for async_move_storage enum move_flags_t { // replace any files in the destination when copying // or moving the storage always_replace_files, // if any files that we want to copy exist in the destination // exist, fail the whole operation and don't perform // any copy or move. There is an inherent race condition // in this mode. The files are checked for existence before // the operation starts. In between the check and performing // the copy, the destination files may be created, in which // case they are replaced. fail_if_exist, // if any file exist in the target, take those files instead // of the ones we may have in the source. dont_replace }; // The storage interface is a pure virtual class that can be implemented to // customize how and where data for a torrent is stored. The default storage // implementation uses regular files in the filesystem, mapping the files in // the torrent in the way one would assume a torrent is saved to disk. // Implementing your own storage interface makes it possible to store all // data in RAM, or in some optimized order on disk (the order the pieces are // received for instance), or saving multi file torrents in a single file in // order to be able to take advantage of optimized disk-I/O. // // It is also possible to write a thin class that uses the default storage // but modifies some particular behavior, for instance encrypting the data // before it's written to disk, and decrypting it when it's read again. // // The storage interface is based on pieces. Avery read and write operation // happens in the piece-space. Each piece fits 'piece_size' number // of bytes. All access is done by writing and reading whole or partial // pieces. // // libtorrent comes with two built-in storage implementations; // ``default_storage`` and ``disabled_storage``. Their constructor functions // are called default_storage_constructor() and // ``disabled_storage_constructor`` respectively. The disabled storage does // just what it sounds like. It throws away data that's written, and it // reads garbage. It's useful mostly for benchmarking and profiling purpose. // struct TORRENT_EXPORT storage_interface { // hidden storage_interface(): m_settings(0) {} // This function is called when the *storage* on disk is to be // initialized. The default storage will create directories and empty // files at this point. If ``allocate_files`` is true, it will also // ``ftruncate`` all files to their target size. // // This function may be called multiple time on a single instance. When a // torrent is force-rechecked, the storage is re-initialized to trigger // the re-check from scratch. // // The function is not necessarily called before other member functions. // For instance has_any_files() and verify_resume_data() are // called early to determine whether we may have to check all files or // not. If we're doing a full check of the files every piece will be // hashed, causing readv() to be called as well. // // Any required internals that need initialization should be done in the // constructor. This function is called before the torrent starts to // download. // // If an error occurs, ``storage_error`` should be set to reflect it. virtual void initialize(storage_error& ec) = 0; // These functions should read and write the data in or to the given // ``piece`` at the given ``offset``. It should read or write // ``num_bufs`` buffers sequentially, where the size of each buffer is // specified in the buffer array ``bufs``. The file::iovec_t type has the // following members:: // // struct iovec_t { void* iov_base; size_t iov_len; }; // // These functions may be called simultaneously from multiple threads. // Make sure they are thread safe. The ``file`` in libtorrent is thread // safe when it can fall back to ``pread``, ``preadv`` or the windows // equivalents. On targets where read operations cannot be thread safe // (i.e one has to seek first and then read), only one disk thread is // used. // // Every buffer in ``bufs`` can be assumed to be page aligned and be of a // page aligned size, except for the last buffer of the torrent. The // allocated buffer can be assumed to fit a fully page aligned number of // bytes though. This is useful when reading and writing the last piece // of a file in unbuffered mode. // // The ``offset`` is aligned to 16 kiB boundaries *most of the time*, but // there are rare exceptions when it's not. Specifically if the read // cache is disabled/or full and a peer requests unaligned data. Most // clients request aligned data. // // The number of bytes read or written should be returned, or -1 on // error. If there's an error, the ``storage_error`` must be filled out // to represent the error that occurred. virtual int readv(file::iovec_t const* bufs, int num_bufs , int piece, int offset, int flags, storage_error& ec) = 0; virtual int writev(file::iovec_t const* bufs, int num_bufs , int piece, int offset, int flags, storage_error& ec) = 0; // This function is called when first checking (or re-checking) the // storage for a torrent. It should return true if any of the files that // is used in this storage exists on disk. If so, the storage will be // checked for existing pieces before starting the download. // // If an error occurs, ``storage_error`` should be set to reflect it. virtual bool has_any_file(storage_error& ec) = 0; // change the priorities of files. This is a fenced job and is // guaranteed to be the only running function on this storage // when called virtual void set_file_priority(std::vector& prio , storage_error& ec) = 0; // This function should move all the files belonging to the storage to // the new save_path. The default storage moves the single file or the // directory of the torrent. // // Before moving the files, any open file handles may have to be closed, // like ``release_files()``. // //If an error occurs, ``storage_error`` should be set to reflect it. // // returns one of: // | no_error = 0 // | fatal_disk_error = -1 // | need_full_check = -2 // | file_exist = -4 virtual int move_storage(std::string const& save_path, int flags , storage_error& ec) = 0; // This function should verify the resume data ``rd`` with the files // on disk. If the resume data seems to be up-to-date, return true. If // not, set ``error`` to a description of what mismatched and return false. // // The default storage may compare file sizes and time stamps of the files. // // If an error occurs, ``storage_error`` should be set to reflect it. // // This function should verify the resume data ``rd`` with the files // on disk. If the resume data seems to be up-to-date, return true. If // not, set ``error`` to a description of what mismatched and return false. // // If the ``links`` pointer is non-null, it has the same number // of elements as there are files. Each element is either empty or contains // the absolute path to a file identical to the corresponding file in this // torrent. The storage must create hard links (or copy) those files. If // any file does not exist or is inaccessible, the disk job must fail. virtual bool verify_resume_data(bdecode_node const& rd , std::vector const* links , storage_error& ec) = 0; // This function should fill in resume data, the current state of the // storage, in ``rd``. The default storage adds file timestamps and // sizes. // // Returning ``true`` indicates an error occurred. // // If an error occurs, ``storage_error`` should be set to reflect it. // virtual void write_resume_data(entry& rd, storage_error& ec) const = 0; // This function should release all the file handles that it keeps open // to files belonging to this storage. The default implementation just // calls file_pool::release_files(). // // If an error occurs, ``storage_error`` should be set to reflect it. // virtual void release_files(storage_error& ec) = 0; // Rename the file with index ``file`` to name ``new_name``. // // If an error occurs, ``storage_error`` should be set to reflect it. // virtual void rename_file(int index, std::string const& new_filename , storage_error& ec) = 0; // This function should delete some or all of the storage for this torrent. // The ``options`` parameter specifies whether to delete all files or just // the partfile. ``options`` are set to the same value as the options // passed to session::remove_torrent(). // // If an error occurs, ``storage_error`` should be set to reflect it. // // The ``disk_buffer_pool`` is used to allocate and free disk buffers. It // has the following members: // // .. code:: c++ // // struct disk_buffer_pool : boost::noncopyable // { // char* allocate_buffer(char const* category); // void free_buffer(char* buf); // // char* allocate_buffers(int blocks, char const* category); // void free_buffers(char* buf, int blocks); // // int block_size() const { return m_block_size; } // // void release_memory(); // }; virtual void delete_files(int options, storage_error& ec) = 0; #ifndef TORRENT_NO_DEPRECATE // This function is called each time a file is completely downloaded. The // storage implementation can perform last operations on a file. The file // will not be opened for writing after this. // // ``index`` is the index of the file that completed. // // On windows the default storage implementation clears the sparse file // flag on the specified file. // // If an error occurs, ``storage_error`` should be set to reflect it. // virtual void finalize_file(int, storage_error&) {} #endif // called periodically (useful for deferred flushing). When returning // false, it means no more ticks are necessary. Any disk job submitted // will re-enable ticking. The default will always turn ticking back // off again. virtual bool tick() { return false; } // access global session_settings aux::session_settings const& settings() const { return *m_settings; } // hidden virtual ~storage_interface() {} // initialized in disk_io_thread::perform_async_job aux::session_settings* m_settings; }; // The default implementation of storage_interface. Behaves as a normal // bittorrent client. It is possible to derive from this class in order to // override some of its behavior, when implementing a custom storage. class TORRENT_EXPORT default_storage : public storage_interface, boost::noncopyable { friend struct write_fileop; friend struct read_fileop; public: // constructs the default_storage based on the give file_storage (fs). // ``mapped`` is an optional argument (it may be NULL). If non-NULL it // represents the file mapping that have been made to the torrent before // adding it. That's where files are supposed to be saved and looked for // on disk. ``save_path`` is the root save folder for this torrent. // ``file_pool`` is the cache of file handles that the storage will use. // All files it opens will ask the file_pool to open them. ``file_prio`` // is a vector indicating the priority of files on startup. It may be // an empty vector. Any file whose index is not represented by the vector // (because the vector is too short) are assumed to have priority 1. // this is used to treat files with priority 0 slightly differently. default_storage(storage_params const& params); // hidden ~default_storage(); #ifndef TORRENT_NO_DEPRECATE void finalize_file(int file, storage_error& ec) TORRENT_OVERRIDE; #endif virtual bool has_any_file(storage_error& ec) TORRENT_OVERRIDE; virtual void set_file_priority(std::vector& prio , storage_error& ec) TORRENT_OVERRIDE; virtual void rename_file(int index, std::string const& new_filename , storage_error& ec) TORRENT_OVERRIDE; virtual void release_files(storage_error& ec) TORRENT_OVERRIDE; virtual void delete_files(int options, storage_error& ec) TORRENT_OVERRIDE; virtual void initialize(storage_error& ec) TORRENT_OVERRIDE; virtual int move_storage(std::string const& save_path, int flags , storage_error& ec) TORRENT_OVERRIDE; virtual bool verify_resume_data(bdecode_node const& rd , std::vector const* links , storage_error& error) TORRENT_OVERRIDE; virtual void write_resume_data(entry& rd, storage_error& ec) const TORRENT_OVERRIDE; virtual bool tick() TORRENT_OVERRIDE; int readv(file::iovec_t const* bufs, int num_bufs , int piece, int offset, int flags, storage_error& ec) TORRENT_OVERRIDE; int writev(file::iovec_t const* bufs, int num_bufs , int piece, int offset, int flags, storage_error& ec) TORRENT_OVERRIDE; // if the files in this storage are mapped, returns the mapped // file_storage, otherwise returns the original file_storage object. file_storage const& files() const { return m_mapped_files?*m_mapped_files:m_files; } #ifdef TORRENT_DISK_STATS static bool disk_write_access_log(); static void disk_write_access_log(bool enable); #endif private: void delete_one_file(std::string const& p, error_code& ec); void need_partfile(); boost::scoped_ptr m_mapped_files; file_storage const& m_files; // in order to avoid calling stat() on each file multiple times // during startup, cache the results in here, and clear it all // out once the torrent starts (to avoid getting stale results) // each entry represents the size and timestamp of the file mutable stat_cache m_stat_cache; // helper function to open a file in the file pool with the right mode file_handle open_file(int file, int mode, storage_error& ec) const; file_handle open_file_impl(int file, int mode, error_code& ec) const; bool use_partfile(int index); void use_partfile(int index, bool b); std::vector m_file_priority; std::string m_save_path; std::string m_part_file_name; // this this is an array indexed by file-index. Each slot represents // whether this file has the part-file enabled for it. This is used for // backwards compatibility with pre-partfile versions of libtorrent. If // this vector is empty, the default is that files *do* use the partfile. // on startup, any 0-priority file that's found in it's original location // is expected to be an old-style (pre-partfile) torrent storage, and // those files have their slot set to false in this vector. // note that the vector is *sparse*, it's only allocated if a file has its // entry set to false, and only indices up to that entry. std::vector m_use_partfile; // the file pool is typically stored in // the session, to make all storage // instances use the same pool file_pool& m_pool; // used for skipped files boost::scoped_ptr m_part_file; // this is a bitfield with one bit per file. A bit being set means // we've written to that file previously. If we do write to a file // whose bit is 0, we set the file size, to make the file allocated // on disk (in full allocation mode) and just sparsely allocated in // case of sparse allocation mode mutable mutex m_file_created_mutex; mutable bitfield m_file_created; bool m_allocate_files; }; // this storage implementation does not write anything to disk // and it pretends to read, and just leaves garbage in the buffers // this is useful when simulating many clients on the same machine // or when running stress tests and want to take the cost of the // disk I/O out of the picture. This cannot be used for any kind // of normal bittorrent operation, since it will just send garbage // to peers and throw away all the data it downloads. It would end // up being banned immediately class disabled_storage TORRENT_FINAL : public storage_interface, boost::noncopyable { public: virtual bool has_any_file(storage_error&) TORRENT_OVERRIDE { return false; } virtual void set_file_priority(std::vector& , storage_error&) TORRENT_OVERRIDE {} virtual void rename_file(int, std::string const&, storage_error&) TORRENT_OVERRIDE {} virtual void release_files(storage_error&) TORRENT_OVERRIDE {} virtual void delete_files(int, storage_error&) TORRENT_OVERRIDE {} virtual void initialize(storage_error&) TORRENT_OVERRIDE {} virtual int move_storage(std::string const&, int, storage_error&) TORRENT_OVERRIDE { return 0; } virtual int readv(file::iovec_t const* bufs, int num_bufs, int piece , int offset, int flags, storage_error& ec) TORRENT_OVERRIDE; virtual int writev(file::iovec_t const* bufs, int num_bufs, int piece , int offset, int flags, storage_error& ec) TORRENT_OVERRIDE; virtual bool verify_resume_data(bdecode_node const& , std::vector const* , storage_error&) TORRENT_OVERRIDE { return false; } virtual void write_resume_data(entry&, storage_error&) const TORRENT_OVERRIDE {} }; // this storage implementation always reads zeros, and always discards // anything written to it struct zero_storage TORRENT_FINAL : storage_interface { virtual void initialize(storage_error&) TORRENT_OVERRIDE {} virtual int readv(file::iovec_t const* bufs, int num_bufs , int piece, int offset, int flags, storage_error& ec) TORRENT_OVERRIDE; virtual int writev(file::iovec_t const* bufs, int num_bufs , int piece, int offset, int flags, storage_error& ec) TORRENT_OVERRIDE; virtual bool has_any_file(storage_error&) TORRENT_OVERRIDE { return false; } virtual void set_file_priority(std::vector& /* prio */ , storage_error&) TORRENT_OVERRIDE {} virtual int move_storage(std::string const& /* save_path */ , int /* flags */, storage_error&) TORRENT_OVERRIDE { return 0; } virtual bool verify_resume_data(bdecode_node const& /* rd */ , std::vector const* /* links */ , storage_error&) TORRENT_OVERRIDE { return false; } virtual void write_resume_data(entry&, storage_error&) const TORRENT_OVERRIDE {} virtual void release_files(storage_error&) TORRENT_OVERRIDE {} virtual void rename_file(int /* index */ , std::string const& /* new_filenamem */, storage_error&) TORRENT_OVERRIDE {} virtual void delete_files(int, storage_error&) TORRENT_OVERRIDE {} }; struct disk_io_thread; // implements the disk I/O job fence used by the piece_manager // to provide to the disk thread. Whenever a disk job needs // exclusive access to the storage for that torrent, it raises // the fence, blocking all new jobs, until there are no longer // any outstanding jobs on the torrent, then the fence is lowered // and it can be performed, along with the backlog of jobs that // accrued while the fence was up struct TORRENT_EXTRA_EXPORT disk_job_fence { disk_job_fence(); ~disk_job_fence() { TORRENT_ASSERT(int(m_outstanding_jobs) == 0); TORRENT_ASSERT(m_blocked_jobs.size() == 0); } // returns one of the fence_* enums. // if there are no outstanding jobs on the // storage, fence_post_fence is returned, the flush job is expected // to be discarded by the caller. // fence_post_flush is returned if the fence job was blocked and queued, // but the flush job should be posted (i.e. put on the job queue) // fence_post_none if both the fence and the flush jobs were queued. enum { fence_post_fence = 0, fence_post_flush = 1, fence_post_none = 2 }; int raise_fence(disk_io_job* fence_job, disk_io_job* flush_job , counters& cnt); bool has_fence() const; // called whenever a job completes and is posted back to the // main network thread. the tailqueue of jobs will have the // backed-up jobs prepended to it in case this resulted in the // fence being lowered. int job_complete(disk_io_job* j, tailqueue& job_queue); int num_outstanding_jobs() const { return m_outstanding_jobs; } // if there is a fence up, returns true and adds the job // to the queue of blocked jobs bool is_blocked(disk_io_job* j); // the number of blocked jobs int num_blocked() const; private: // when > 0, this storage is blocked for new async // operations until all outstanding jobs have completed. // at that point, the m_blocked_jobs are issued // the count is the number of fence job currently in the queue int m_has_fence; // when there's a fence up, jobs are queued up in here // until the fence is lowered tailqueue m_blocked_jobs; // the number of disk_io_job objects there are, belonging // to this torrent, currently pending, hanging off of // cached_piece_entry objects. This is used to determine // when the fence can be lowered boost::atomic m_outstanding_jobs; // must be held when accessing m_has_fence and // m_blocked_jobs mutable mutex m_mutex; }; // this class keeps track of which pieces, belonging to // a specific storage, are in the cache right now. It's // used for quickly being able to evict all pieces for a // specific torrent struct TORRENT_EXTRA_EXPORT storage_piece_set { void add_piece(cached_piece_entry* p); void remove_piece(cached_piece_entry* p); bool has_piece(cached_piece_entry const* p) const; int num_pieces() const { return int(m_cached_pieces.size()); } boost::unordered_set const& cached_pieces() const { return m_cached_pieces; } private: // these are cached pieces belonging to this storage boost::unordered_set m_cached_pieces; }; class TORRENT_EXTRA_EXPORT piece_manager : public boost::enable_shared_from_this , public disk_job_fence , public storage_piece_set , boost::noncopyable { friend struct disk_io_thread; public: piece_manager( storage_interface* storage_impl , boost::shared_ptr const& torrent , file_storage* files); ~piece_manager(); file_storage const* files() const { return &m_files; } enum return_t { // return values from check_fastresume, and move_storage no_error = 0, fatal_disk_error = -1, need_full_check = -2, disk_check_aborted = -3, file_exist = -4 }; storage_interface* get_storage_impl() { return m_storage.get(); } void write_resume_data(entry& rd, storage_error& ec) const; #ifdef TORRENT_DEBUG void assert_torrent_refcount() const; #endif private: // if error is set and return value is 'no_error' or 'need_full_check' // the error message indicates that the fast resume data was rejected // if 'fatal_disk_error' is returned, the error message indicates what // when wrong in the disk access int check_fastresume(bdecode_node const& rd , std::vector const* links , storage_error& error); // helper functions for check_fastresume int check_no_fastresume(storage_error& error); int check_init_storage(storage_error& error); #ifdef TORRENT_DEBUG std::string name() const { return m_files.name(); } #endif #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif file_storage const& m_files; boost::scoped_ptr m_storage; // the reason for this to be a void pointer // is to avoid creating a dependency on the // torrent. This shared_ptr is here only // to keep the torrent object alive until // the piece_manager destructs. This is because // the torrent_info object is owned by the torrent. boost::shared_ptr m_torrent; }; // this identifies a read or write operation so that readwritev() knows // what to do when it's actually touching the file struct fileop { virtual int file_op(int file_index, boost::int64_t file_offset, int size , file::iovec_t const* bufs, storage_error& ec) = 0; }; // this function is responsible for turning read and write operations in the // torrent space (pieces) into read and write operations in the filesystem // space (files on disk). TORRENT_EXTRA_EXPORT int readwritev(file_storage const& files , file::iovec_t const* bufs, int piece, int offset, int num_bufs , fileop& op, storage_error& ec); } #endif // TORRENT_STORAGE_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/storage_defs.hpp000066400000000000000000000066421351156116000247400ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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_DEFS_HPP_INCLUDE #define TORRENT_STORAGE_DEFS_HPP_INCLUDE #include "libtorrent/config.hpp" #include #include #include namespace libtorrent { struct storage_interface; class file_storage; struct file_pool; class torrent_info; // types of storage allocation used for add_torrent_params::storage_mode. enum storage_mode_t { // All pieces will be written to their final position, all files will be // allocated in full when the torrent is first started. This is done with // ``fallocate()`` and similar calls. This mode minimizes fragmentation. storage_mode_allocate, // All pieces will be written to the place where they belong and sparse files // will be used. This is the recommended, and default mode. storage_mode_sparse }; // see default_storage::default_storage() struct TORRENT_EXPORT storage_params { storage_params(): files(NULL), mapped_files(NULL), pool(NULL) , mode(storage_mode_sparse), priorities(NULL), info(NULL) {} file_storage const* files; file_storage const* mapped_files; // optional std::string path; file_pool* pool; storage_mode_t mode; std::vector const* priorities; // optional torrent_info const* info; // optional }; typedef boost::function storage_constructor_type; // the constructor function for the regular file storage. This is the // default value for add_torrent_params::storage. TORRENT_EXPORT storage_interface* default_storage_constructor(storage_params const&); // the constructor function for the disabled storage. This can be used for // testing and benchmarking. It will throw away any data written to // it and return garbage for anything read from it. TORRENT_EXPORT storage_interface* disabled_storage_constructor(storage_params const&); TORRENT_EXPORT storage_interface* zero_storage_constructor(storage_params const&); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/string_util.hpp000066400000000000000000000111661351156116000246330ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_STRING_UTIL_HPP_INCLUDED #define TORRENT_STRING_UTIL_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include // for boost::array #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { TORRENT_EXTRA_EXPORT bool is_alpha(char c); TORRENT_EXTRA_EXPORT boost::array::digits10> to_string(boost::int64_t n); // internal inline bool is_digit(char c) { return c >= '0' && c <= '9'; } TORRENT_EXTRA_EXPORT bool is_print(char c); TORRENT_EXTRA_EXPORT bool is_space(char c); TORRENT_EXTRA_EXPORT char to_lower(char c); TORRENT_EXTRA_EXPORT int split_string(char const** tags, int buf_size, char* in); TORRENT_EXTRA_EXPORT bool string_begins_no_case(char const* s1, char const* s2); TORRENT_EXTRA_EXPORT bool string_equal_no_case(char const* s1, char const* s2); TORRENT_EXTRA_EXPORT void url_random(char* begin, char* end); // this parses the string that's used as the liste_interfaces setting. // it is a comma-separated list of IP or device names with ports. For // example: "eth0:6881,eth1:6881" or "127.0.0.1:6881" TORRENT_EXTRA_EXPORT void parse_comma_separated_string_port( std::string const& in, std::vector >& out); // this parses the string that's used as the outgoing_interfaces setting. // it is a comma separated list of IPs and device names. For example: // "eth0, eth1, 127.0.0.1" TORRENT_EXTRA_EXPORT void parse_comma_separated_string( std::string const& in, std::vector& out); // strdup is not part of the C standard. Some systems // don't have it and it won't be available when building // in strict ansi mode char* allocate_string_copy(char const* str); // returns p + x such that the pointer is 8 bytes aligned // x cannot be greater than 7 void* align_pointer(void* p); // searches for separator in the string 'last'. the pointer last points to // is set to point to the first character following the separator. // returns a pointer to a null terminated string starting at last, ending // at the separator (the string is mutated to replace the separator with // a '\0' character). If there is no separator, but the end of the string, // the pointer next points to is set to the last null terminator, which will // make the following invocation return NULL, to indicate the end of the // string. TORRENT_EXTRA_EXPORT char* string_tokenize(char* last, char sep, char** next); #if TORRENT_USE_I2P bool is_i2p_url(std::string const& url); #endif // this can be used as the hash function in std::unordered_* struct TORRENT_EXTRA_EXPORT string_hash_no_case { size_t operator()(std::string const& s) const; }; // these can be used as the comparison functions in std::map and std::set struct TORRENT_EXTRA_EXPORT string_eq_no_case { bool operator()(std::string const& lhs, std::string const& rhs) const; }; struct TORRENT_EXTRA_EXPORT string_less_no_case { bool operator()(std::string const& lhs, std::string const& rhs) const; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/tailqueue.hpp000066400000000000000000000105471351156116000242700ustar00rootroot00000000000000/* Copyright (c) 2011-2018, 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_TAILQUEUE_HPP #define TORRENT_TAILQUEUE_HPP #include "libtorrent/assert.hpp" namespace libtorrent { template struct tailqueue_node { tailqueue_node() : next(0) {} T* next; }; template inline N* postinc(N*& e) { N* ret = e; e = static_cast(ret->next); return ret; } template struct tailqueue_iterator { template friend struct tailqueue; T* get() const { return m_current; } void next() { m_current = m_current->next; } private: tailqueue_iterator(T* cur) : m_current(cur) {} // the current element T* m_current; }; template struct tailqueue { tailqueue(): m_first(NULL), m_last(NULL), m_size(0) {} tailqueue_iterator iterate() const { return tailqueue_iterator(m_first); } tailqueue_iterator iterate() { return tailqueue_iterator(m_first); } void append(tailqueue& rhs) { TORRENT_ASSERT(m_last == 0 || m_last->next == 0); TORRENT_ASSERT(rhs.m_last == 0 || rhs.m_last->next == 0); if (rhs.m_first == 0) return; if (m_first == 0) { swap(rhs); return; } m_last->next = rhs.m_first; m_last = rhs.m_last; m_size += rhs.m_size; rhs.m_first = 0; rhs.m_last = 0; rhs.m_size = 0; TORRENT_ASSERT(m_last == 0 || m_last->next == 0); } void prepend(tailqueue& rhs) { TORRENT_ASSERT(m_last == 0 || m_last->next == 0); TORRENT_ASSERT(rhs.m_last == 0 || rhs.m_last->next == 0); if (rhs.m_first == 0) return; if (m_first == 0) { swap(rhs); return; } swap(rhs); append(rhs); } T* pop_front() { TORRENT_ASSERT(m_last == 0 || m_last->next == 0); T* e = m_first; m_first = m_first->next; if (e == m_last) m_last = 0; e->next = 0; --m_size; return e; } void push_front(T* e) { TORRENT_ASSERT(e->next == 0); TORRENT_ASSERT(m_last == 0 || m_last->next == 0); e->next = m_first; m_first = e; if (!m_last) m_last = e; ++m_size; } void push_back(T* e) { TORRENT_ASSERT(e->next == 0); TORRENT_ASSERT(m_last == 0 || m_last->next == 0); if (m_last) m_last->next = e; else m_first = e; m_last = e; e->next = 0; ++m_size; } T* get_all() { TORRENT_ASSERT(m_last == 0 || m_last->next == 0); T* e = m_first; m_first = 0; m_last = 0; m_size = 0; return e; } void swap(tailqueue& rhs) { T* tmp = m_first; m_first = rhs.m_first; rhs.m_first = tmp; tmp = m_last; m_last = rhs.m_last; rhs.m_last = tmp; int tmp2 = m_size; m_size = rhs.m_size; rhs.m_size = tmp2; } int size() const { return m_size; } bool empty() const { return m_size == 0; } T* first() const { TORRENT_ASSERT(m_size > 0); return m_first; } T* last() const { TORRENT_ASSERT(m_size > 0); return m_last; } private: T* m_first; T* m_last; int m_size; }; } #endif // TAILQUEUE_HPP libtorrent-rasterbar-1.1.13/include/libtorrent/thread.hpp000066400000000000000000000075761351156116000235510ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_THREAD_HPP_INCLUDED #define TORRENT_THREAD_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/time.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN // asio assumes that the windows error codes are defined already #include #endif #if defined TORRENT_BEOS #include #endif #include // for auto_ptr required by asio #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { typedef boost::asio::detail::thread thread; typedef boost::asio::detail::mutex mutex; typedef boost::asio::detail::event event; // internal void sleep(int milliseconds); struct TORRENT_EXTRA_EXPORT recursive_mutex; struct TORRENT_EXTRA_EXPORT condition_variable { condition_variable(); ~condition_variable(); void wait(mutex::scoped_lock& l); void wait_for(mutex::scoped_lock& l, time_duration); void wait(boost::asio::detail::scoped_lock& l); void wait_for(boost::asio::detail::scoped_lock&, time_duration); void notify_all(); void notify(); private: template void wait_impl(LockGuard& l); template void wait_for_impl(LockGuard& l, time_duration rel_time); #ifdef BOOST_HAS_PTHREADS pthread_cond_t m_cond; #elif defined TORRENT_WINDOWS || defined TORRENT_CYGWIN HANDLE m_sem; mutex m_mutex; int m_num_waiters; #elif defined TORRENT_BEOS sem_id m_sem; mutex m_mutex; int m_num_waiters; #else #error not implemented #endif }; #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN typedef DWORD thread_id_t; #elif defined TORRENT_BEOS typedef thread_id thread_id_t; #endif // internal struct TORRENT_EXTRA_EXPORT recursive_mutex { typedef boost::asio::detail::scoped_lock scoped_lock; recursive_mutex(); ~recursive_mutex(); void lock(); void unlock(); private: recursive_mutex(recursive_mutex const&); recursive_mutex& operator=(recursive_mutex const&); #ifdef BOOST_HAS_PTHREADS ::pthread_mutex_t m_mutex; #else mutex m_mutex; condition_variable m_cond; thread_id_t m_owner; int m_count; #endif }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/thread_pool.hpp000066400000000000000000000111061351156116000245620ustar00rootroot00000000000000/* Copyright (c) 2011-2018, 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_THREAD_POOL #define TORRENT_THREAD_POOL #include "libtorrent/config.hpp" #include "libtorrent/thread.hpp" #include #include #include #include #include namespace libtorrent { template struct thread_pool { thread_pool() : m_num_threads(0) {} virtual ~thread_pool() {} void stop() { set_num_threads(0, true); } void set_num_threads(int n, bool wait = true) { if (n == m_num_threads) return; if (n > m_num_threads) { while (m_num_threads < n) { ++m_num_threads; m_threads.push_back(boost::shared_ptr( new thread(boost::bind(&thread_pool::thread_fun, this, int(m_num_threads)-1)))); } } else { while (m_num_threads > n) { --m_num_threads; } mutex::scoped_lock l(m_mutex); m_cond.notify_all(); l.unlock(); if (wait) { for (int i = m_num_threads; i < int(m_threads.size()); ++i) m_threads[i]->join(); } // this will detach the threads m_threads.resize(m_num_threads); } } // returns true if the job was posted, and false if it was // processed immediately bool post_job(T& e) { if (m_num_threads == 0) { // if we don't have any worker threads // just do the work immediately process_job(e, false); return false; } else { retain_job(e); mutex::scoped_lock l(m_mutex); m_queue.push_back(e); // we only need to signal if the threads // may have been put to sleep. If the size // previous to adding the new job was > 0 // they don't need waking up. if (m_queue.size() == 1) m_cond.notify(); return true; } } protected: virtual void process_job(T const& j, bool post) = 0; virtual void retain_job(T&) {} private: void thread_fun(int thread_id) { for (;;) { mutex::scoped_lock l(m_mutex); while (m_queue.empty() && thread_id < m_num_threads) m_cond.wait(l); // if the number of wanted thread is decreased, // we may stop this thread // when we're terminating the last hasher thread (id=0), make sure // we finish up all queud jobs first if ((thread_id != 0 || m_queue.empty()) && thread_id >= m_num_threads) break; TORRENT_ASSERT(!m_queue.empty()); T e = m_queue.front(); m_queue.pop_front(); l.unlock(); process_job(e, true); } #ifdef TORRENT_DEBUG if (thread_id == 0) { // when we're terminating the last hasher thread, make sure // there are no more scheduled jobs mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(m_queue.empty()); } #endif } // the mutex only protects m_cond and m_queue // all other members are only used from a single // thread (the user of this class, i.e. the disk // thread). mutex m_mutex; condition_variable m_cond; std::deque m_queue; std::vector > m_threads; // this is a counter which is atomically incremented // by each thread as it's started up, in order to // assign a unique id to each thread boost::atomic m_num_threads; }; } #endif // TORRENT_THREAD_POOL libtorrent-rasterbar-1.1.13/include/libtorrent/time.hpp000066400000000000000000000067631351156116000232350ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_TIME_HPP_INCLUDED #define TORRENT_TIME_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #if defined BOOST_ASIO_HAS_STD_CHRONO #include #else #include #endif #if defined TORRENT_BUILD_SIMULATOR #include "simulator/simulator.hpp" #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { #if defined TORRENT_BUILD_SIMULATOR typedef sim::chrono::high_resolution_clock clock_type; #elif defined BOOST_ASIO_HAS_STD_CHRONO typedef std::chrono::high_resolution_clock clock_type; #else typedef boost::chrono::high_resolution_clock clock_type; #endif typedef clock_type::time_point time_point; typedef clock_type::duration time_duration; #if defined BOOST_ASIO_HAS_STD_CHRONO using std::chrono::seconds; using std::chrono::milliseconds; using std::chrono::microseconds; using std::chrono::minutes; using std::chrono::hours; using std::chrono::duration_cast; #else using boost::chrono::seconds; using boost::chrono::milliseconds; using boost::chrono::microseconds; using boost::chrono::minutes; using boost::chrono::hours; using boost::chrono::duration_cast; #endif // internal inline time_point min_time() { return (time_point::min)(); } // internal inline time_point max_time() { return (time_point::max)(); } template boost::int64_t total_seconds(T td) { return duration_cast(td).count(); } template boost::int64_t total_milliseconds(T td) { return duration_cast(td).count(); } template boost::int64_t total_microseconds(T td) { return duration_cast(td).count(); } #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED time_point time_now(); TORRENT_DEPRECATED time_point time_now_hires(); inline time_point time_now() { return clock_type::now(); } inline time_point time_now_hires() { return clock_type::now(); } typedef time_point ptime; #endif } #endif // TORRENT_TIME_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/timestamp_history.hpp000066400000000000000000000057371351156116000260630ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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 TIMESTAMP_HISTORY_HPP #define TIMESTAMP_HISTORY_HPP #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { // timestamp history keeps a history of the lowest timestamps we've // seen in the last 20 minutes struct TORRENT_EXTRA_EXPORT timestamp_history { enum { history_size = 20 }; timestamp_history() : m_base(0), m_index(0), m_num_samples(not_initialized) {} bool initialized() const { return m_num_samples != not_initialized; } // add a sample to the timestamp history. If step is true, it's been // a minute since the last step boost::uint32_t add_sample(boost::uint32_t sample, bool step); boost::uint32_t base() const { TORRENT_ASSERT(initialized()); return m_base; } void adjust_base(int change); private: // this is a circular buffer boost::uint32_t m_history[history_size]; // this is the lowest sample seen in the // last 'history_size' minutes boost::uint32_t m_base; // and this is the index we're currently at // in the circular buffer boost::uint16_t m_index; enum { not_initialized = 0xffff }; // this is the number of samples since the // last time we stepped one minute. If we // don't have enough samples, we won't step // if this is set to 'not_initialized' we // have bit seen any samples at all yet // and m_base is not initialized yet boost::uint16_t m_num_samples; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/tommath.h000066400000000000000000000413371351156116000234040ustar00rootroot00000000000000/* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://math.libtomcrypt.com */ #ifndef BN_H_ #define BN_H_ #include #include #include #include "libtorrent/export.hpp" #ifdef _MSC_VER typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned __int64 uint64_t; #else #include #endif #include "libtorrent/tommath_class.h" #ifdef __cplusplus extern "C" { #endif /* detect 64-bit mode if possible */ #if defined(__x86_64__) #if !(defined(MP_32BIT) || defined(MP_16BIT) || defined(MP_8BIT)) #define MP_64BIT #endif #endif /* some default configurations. * * A "mp_digit" must be able to hold DIGIT_BIT + 1 bits * A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits * * At the very least a mp_digit must be able to hold 7 bits * [any size beyond that is ok provided it doesn't overflow the data type] */ #ifdef MP_8BIT typedef uint8_t mp_digit; typedef uint16_t mp_word; #define MP_SIZEOF_MP_DIGIT 1 #ifdef DIGIT_BIT #error You must not define DIGIT_BIT when using MP_8BIT #endif #elif defined(MP_16BIT) typedef uint16_t mp_digit; typedef uint32_t mp_word; #define MP_SIZEOF_MP_DIGIT 2 #ifdef DIGIT_BIT #error You must not define DIGIT_BIT when using MP_16BIT #endif #elif defined(MP_64BIT) /* for GCC only on supported platforms */ typedef uint64_t mp_digit; #if defined(_WIN32) typedef unsigned __int128 mp_word; #elif defined(__GNUC__) typedef unsigned long mp_word __attribute__ ((mode(TI))); #else /* it seems you have a problem * but we assume you can somewhere define your own uint128_t */ typedef uint128_t mp_word; #endif #define DIGIT_BIT 60 #else /* this is the default case, 28-bit digits */ /* this is to make porting into LibTomCrypt easier :-) */ typedef uint32_t mp_digit; typedef uint64_t mp_word; #ifdef MP_31BIT /* this is an extension that uses 31-bit digits */ #define DIGIT_BIT 31 #else /* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */ #define DIGIT_BIT 28 #define MP_28BIT #endif #endif /* otherwise the bits per digit is calculated automatically from the size of a mp_digit */ #ifndef DIGIT_BIT #define DIGIT_BIT (((CHAR_BIT * MP_SIZEOF_MP_DIGIT) - 1)) /* bits per digit */ typedef uint_least32_t mp_min_u32; #else typedef mp_digit mp_min_u32; #endif /* platforms that can use a better rand function */ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) #define MP_USE_ALT_RAND 1 #endif /* use arc4random on platforms that support it */ #ifdef MP_USE_ALT_RAND #define MP_GEN_RANDOM() arc4random() #else #define MP_GEN_RANDOM() rand() #endif #define MP_DIGIT_BIT DIGIT_BIT #define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) #define MP_DIGIT_MAX MP_MASK /* equalities */ #define MP_LT -1 /* less than */ #define MP_EQ 0 /* equal to */ #define MP_GT 1 /* greater than */ #define MP_ZPOS 0 /* positive integer */ #define MP_NEG 1 /* negative */ #define MP_OKAY 0 /* ok result */ #define MP_MEM -2 /* out of mem */ #define MP_VAL -3 /* invalid input */ #define MP_RANGE MP_VAL #define MP_YES 1 /* yes response */ #define MP_NO 0 /* no response */ /* Primality generation flags */ #define LTM_PRIME_BBS 0x0001 /* BBS style prime */ #define LTM_PRIME_SAFE 0x0002 /* Safe prime (p-1)/2 == prime */ #define LTM_PRIME_2MSB_ON 0x0008 /* force 2nd MSB to 1 */ typedef int mp_err; /* you'll have to tune these... */ extern int KARATSUBA_MUL_CUTOFF, KARATSUBA_SQR_CUTOFF, TOOM_MUL_CUTOFF, TOOM_SQR_CUTOFF; /* define this to use lower memory usage routines (exptmods mostly) */ /* #define MP_LOW_MEM */ /* default precision */ #ifndef MP_PREC #ifndef MP_LOW_MEM #define MP_PREC 32 /* default digits of precision */ #else #define MP_PREC 8 /* default digits of precision */ #endif #endif /* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ #define MP_WARRAY (1 << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) + 1)) /* the infamous mp_int structure */ typedef struct { int used, alloc, sign; mp_digit *dp; } mp_int; /* callback for mp_prime_random, should fill dst with random bytes and return how many read [upto len] */ typedef int ltm_prime_callback(unsigned char *dst, int len, void *dat); #define USED(m) ((m)->used) #define DIGIT(m,k) ((m)->dp[(k)]) #define SIGN(m) ((m)->sign) /* error code to char* string */ const char *mp_error_to_string(int code); /* ---> init and deinit bignum functions <--- */ /* init a bignum */ TORRENT_EXTRA_EXPORT int mp_init(mp_int *a); /* free a bignum */ TORRENT_EXTRA_EXPORT void mp_clear(mp_int *a); /* init a null terminated series of arguments */ int mp_init_multi(mp_int *mp, ...); /* clear a null terminated series of arguments */ void mp_clear_multi(mp_int *mp, ...); /* exchange two ints */ void mp_exch(mp_int *a, mp_int *b); /* shrink ram required for a bignum */ int mp_shrink(mp_int *a); /* grow an int to a given size */ int mp_grow(mp_int *a, int size); /* init to a given number of digits */ int mp_init_size(mp_int *a, int size); /* ---> Basic Manipulations <--- */ #define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) #define mp_iseven(a) ((((a)->used > 0) && (((a)->dp[0] & 1u) == 0u)) ? MP_YES : MP_NO) #define mp_isodd(a) ((((a)->used > 0) && (((a)->dp[0] & 1u) == 1u)) ? MP_YES : MP_NO) #define mp_isneg(a) (((a)->sign != MP_ZPOS) ? MP_YES : MP_NO) /* set to zero */ void mp_zero(mp_int *a); /* set to a digit */ void mp_set(mp_int *a, mp_digit b); /* set a 32-bit const */ int mp_set_int(mp_int *a, unsigned long b); /* set a platform dependent unsigned long value */ int mp_set_long(mp_int *a, unsigned long b); /* set a platform dependent unsigned long long value */ int mp_set_long_long(mp_int *a, unsigned long long b); /* get a 32-bit value */ unsigned long mp_get_int(mp_int * a); /* get a platform dependent unsigned long value */ unsigned long mp_get_long(mp_int * a); /* get a platform dependent unsigned long long value */ unsigned long long mp_get_long_long(mp_int * a); /* initialize and set a digit */ int mp_init_set (mp_int * a, mp_digit b); /* initialize and set 32-bit value */ int mp_init_set_int (mp_int * a, unsigned long b); /* copy, b = a */ int mp_copy(mp_int *a, mp_int *b); /* inits and copies, a = b */ int mp_init_copy(mp_int *a, mp_int *b); /* trim unused digits */ void mp_clamp(mp_int *a); /* import binary data */ int mp_import(mp_int* rop, size_t count, int order, size_t size, int endian, size_t nails, const void* op); /* export binary data */ int mp_export(void* rop, size_t* countp, int order, size_t size, int endian, size_t nails, mp_int* op); /* ---> digit manipulation <--- */ /* right shift by "b" digits */ void mp_rshd(mp_int *a, int b); /* left shift by "b" digits */ int mp_lshd(mp_int *a, int b); /* c = a / 2**b, implemented as c = a >> b */ int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d); /* b = a/2 */ int mp_div_2(mp_int *a, mp_int *b); /* c = a * 2**b, implemented as c = a << b */ int mp_mul_2d(mp_int *a, int b, mp_int *c); /* b = a*2 */ int mp_mul_2(mp_int *a, mp_int *b); /* c = a mod 2**b */ int mp_mod_2d(mp_int *a, int b, mp_int *c); /* computes a = 2**b */ int mp_2expt(mp_int *a, int b); /* Counts the number of lsbs which are zero before the first zero bit */ int mp_cnt_lsb(mp_int *a); /* I Love Earth! */ /* makes a pseudo-random int of a given size */ int mp_rand(mp_int *a, int digits); /* ---> binary operations <--- */ /* c = a XOR b */ int mp_xor(mp_int *a, mp_int *b, mp_int *c); /* c = a OR b */ int mp_or(mp_int *a, mp_int *b, mp_int *c); /* c = a AND b */ int mp_and(mp_int *a, mp_int *b, mp_int *c); /* ---> Basic arithmetic <--- */ /* b = -a */ int mp_neg(mp_int *a, mp_int *b); /* b = |a| */ int mp_abs(mp_int *a, mp_int *b); /* compare a to b */ int mp_cmp(mp_int *a, mp_int *b); /* compare |a| to |b| */ int mp_cmp_mag(mp_int *a, mp_int *b); /* c = a + b */ int mp_add(mp_int *a, mp_int *b, mp_int *c); /* c = a - b */ int mp_sub(mp_int *a, mp_int *b, mp_int *c); /* c = a * b */ int mp_mul(mp_int *a, mp_int *b, mp_int *c); /* b = a*a */ int mp_sqr(mp_int *a, mp_int *b); /* a/b => cb + d == a */ int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d); /* c = a mod b, 0 <= c < b */ int mp_mod(mp_int *a, mp_int *b, mp_int *c); /* ---> single digit functions <--- */ /* compare against a single digit */ int mp_cmp_d(mp_int *a, mp_digit b); /* c = a + b */ int mp_add_d(mp_int *a, mp_digit b, mp_int *c); /* c = a - b */ int mp_sub_d(mp_int *a, mp_digit b, mp_int *c); /* c = a * b */ int mp_mul_d(mp_int *a, mp_digit b, mp_int *c); /* a/b => cb + d == a */ int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d); /* a/3 => 3c + d == a */ int mp_div_3(mp_int *a, mp_int *c, mp_digit *d); /* c = a**b */ int mp_expt_d(mp_int *a, mp_digit b, mp_int *c); int mp_expt_d_ex (mp_int * a, mp_digit b, mp_int * c, int fast); /* c = a mod b, 0 <= c < b */ int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c); /* ---> number theory <--- */ /* d = a + b (mod c) */ int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); /* d = a - b (mod c) */ int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); /* d = a * b (mod c) */ int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); /* c = a * a (mod b) */ int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c); /* c = 1/a (mod b) */ int mp_invmod(mp_int *a, mp_int *b, mp_int *c); /* c = (a, b) */ int mp_gcd(mp_int *a, mp_int *b, mp_int *c); /* produces value such that U1*a + U2*b = U3 */ int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3); /* c = [a, b] or (a*b)/(a, b) */ int mp_lcm(mp_int *a, mp_int *b, mp_int *c); /* finds one of the b'th root of a, such that |c|**b <= |a| * * returns error if a < 0 and b is even */ int mp_n_root(mp_int *a, mp_digit b, mp_int *c); int mp_n_root_ex (mp_int * a, mp_digit b, mp_int * c, int fast); /* special sqrt algo */ int mp_sqrt(mp_int *arg, mp_int *ret); /* special sqrt (mod prime) */ int mp_sqrtmod_prime(mp_int *arg, mp_int *prime, mp_int *ret); /* is number a square? */ int mp_is_square(mp_int *arg, int *ret); /* computes the jacobi c = (a | n) (or Legendre if b is prime) */ int mp_jacobi(mp_int *a, mp_int *n, int *c); /* used to setup the Barrett reduction for a given modulus b */ int mp_reduce_setup(mp_int *a, mp_int *b); /* Barrett Reduction, computes a (mod b) with a precomputed value c * * Assumes that 0 < a <= b*b, note if 0 > a > -(b*b) then you can merely * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code]. */ int mp_reduce(mp_int *a, mp_int *b, mp_int *c); /* setups the montgomery reduction */ int mp_montgomery_setup(mp_int *a, mp_digit *mp); /* computes a = B**n mod b without division or multiplication useful for * normalizing numbers in a Montgomery system. */ int mp_montgomery_calc_normalization(mp_int *a, mp_int *b); /* computes x/R == x (mod N) via Montgomery Reduction */ int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); /* returns 1 if a is a valid DR modulus */ int mp_dr_is_modulus(mp_int *a); /* sets the value of "d" required for mp_dr_reduce */ void mp_dr_setup(mp_int *a, mp_digit *d); /* reduces a modulo b using the Diminished Radix method */ int mp_dr_reduce(mp_int *a, mp_int *b, mp_digit mp); /* returns true if a can be reduced with mp_reduce_2k */ int mp_reduce_is_2k(mp_int *a); /* determines k value for 2k reduction */ int mp_reduce_2k_setup(mp_int *a, mp_digit *d); /* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d); /* returns true if a can be reduced with mp_reduce_2k_l */ int mp_reduce_is_2k_l(mp_int *a); /* determines k value for 2k reduction */ int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); /* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); /* d = a**b (mod c) */ int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); /* ---> Primes <--- */ /* number of primes */ #ifdef MP_8BIT #define PRIME_SIZE 31 #else #define PRIME_SIZE 256 #endif /* table of first PRIME_SIZE primes */ extern const mp_digit ltm_prime_tab[PRIME_SIZE]; /* result=1 if a is divisible by one of the first PRIME_SIZE primes */ int mp_prime_is_divisible(mp_int *a, int *result); /* performs one Fermat test of "a" using base "b". * Sets result to 0 if composite or 1 if probable prime */ int mp_prime_fermat(mp_int *a, mp_int *b, int *result); /* performs one Miller-Rabin test of "a" using base "b". * Sets result to 0 if composite or 1 if probable prime */ int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result); /* This gives [for a given bit size] the number of trials required * such that Miller-Rabin gives a prob of failure lower than 2^-96 */ int mp_prime_rabin_miller_trials(int size); /* performs t rounds of Miller-Rabin on "a" using the first * t prime bases. Also performs an initial sieve of trial * division. Determines if "a" is prime with probability * of error no more than (1/4)**t. * * Sets result to 1 if probably prime, 0 otherwise */ int mp_prime_is_prime(mp_int *a, int t, int *result); /* finds the next prime after the number "a" using "t" trials * of Miller-Rabin. * * bbs_style = 1 means the prime must be congruent to 3 mod 4 */ int mp_prime_next_prime(mp_int *a, int t, int bbs_style); /* makes a truly random prime of a given size (bytes), * call with bbs = 1 if you want it to be congruent to 3 mod 4 * * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself * so it can be NULL * * The prime generated will be larger than 2^(8*size). */ #define mp_prime_random(a, t, size, bbs, cb, dat) mp_prime_random_ex(a, t, ((size) * 8) + 1, (bbs==1)?LTM_PRIME_BBS:0, cb, dat) /* makes a truly random prime of a given size (bits), * * Flags are as follows: * * LTM_PRIME_BBS - make prime congruent to 3 mod 4 * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) * LTM_PRIME_2MSB_ON - make the 2nd highest bit one * * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself * so it can be NULL * */ int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat); /* ---> radix conversion <--- */ int mp_count_bits(mp_int *a); TORRENT_EXTRA_EXPORT int mp_unsigned_bin_size(mp_int *a); TORRENT_EXTRA_EXPORT int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c); int mp_to_unsigned_bin(mp_int *a, unsigned char *b); int mp_to_unsigned_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen); int mp_signed_bin_size(mp_int *a); int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c); int mp_to_signed_bin(mp_int *a, unsigned char *b); int mp_to_signed_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen); int mp_read_radix(mp_int *a, const char *str, int radix); int mp_toradix(mp_int *a, char *str, int radix); int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen); int mp_radix_size(mp_int *a, int radix, int *size); #ifndef LTM_NO_FILE int mp_fread(mp_int *a, int radix, FILE *stream); int mp_fwrite(mp_int *a, int radix, FILE *stream); #endif #define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len)) #define mp_raw_size(mp) mp_signed_bin_size(mp) #define mp_toraw(mp, str) mp_to_signed_bin((mp), (str)) #define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len)) #define mp_mag_size(mp) mp_unsigned_bin_size(mp) #define mp_tomag(mp, str) mp_to_unsigned_bin((mp), (str)) #define mp_tobinary(M, S) mp_toradix((M), (S), 2) #define mp_tooctal(M, S) mp_toradix((M), (S), 8) #define mp_todecimal(M, S) mp_toradix((M), (S), 10) #define mp_tohex(M, S) mp_toradix((M), (S), 16) #ifdef __cplusplus } #endif #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ libtorrent-rasterbar-1.1.13/include/libtorrent/tommath_class.h000066400000000000000000000533101351156116000245630ustar00rootroot00000000000000#if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) #if defined(LTM2) #define LTM3 #endif #if defined(LTM1) #define LTM2 #endif #define LTM1 #if defined(LTM_ALL) #define BN_ERROR_C #define BN_FAST_MP_INVMOD_C #define BN_FAST_MP_MONTGOMERY_REDUCE_C #define BN_FAST_S_MP_MUL_DIGS_C #define BN_FAST_S_MP_MUL_HIGH_DIGS_C #define BN_FAST_S_MP_SQR_C #define BN_MP_2EXPT_C #define BN_MP_ABS_C #define BN_MP_ADD_C #define BN_MP_ADD_D_C #define BN_MP_ADDMOD_C #define BN_MP_AND_C #define BN_MP_CLAMP_C #define BN_MP_CLEAR_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_CMP_C #define BN_MP_CMP_D_C #define BN_MP_CMP_MAG_C #define BN_MP_CNT_LSB_C #define BN_MP_COPY_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_C #define BN_MP_DIV_2_C #define BN_MP_DIV_2D_C #define BN_MP_DIV_3_C #define BN_MP_DIV_D_C #define BN_MP_DR_IS_MODULUS_C #define BN_MP_DR_REDUCE_C #define BN_MP_DR_SETUP_C #define BN_MP_EXCH_C #define BN_MP_EXPORT_C #define BN_MP_EXPT_D_C #define BN_MP_EXPT_D_EX_C #define BN_MP_EXPTMOD_C #define BN_MP_EXPTMOD_FAST_C #define BN_MP_EXTEUCLID_C #define BN_MP_FREAD_C #define BN_MP_FWRITE_C #define BN_MP_GCD_C #define BN_MP_GET_INT_C #define BN_MP_GET_LONG_C #define BN_MP_GET_LONG_LONG_C #define BN_MP_GROW_C #define BN_MP_IMPORT_C #define BN_MP_INIT_C #define BN_MP_INIT_COPY_C #define BN_MP_INIT_MULTI_C #define BN_MP_INIT_SET_C #define BN_MP_INIT_SET_INT_C #define BN_MP_INIT_SIZE_C #define BN_MP_INVMOD_C #define BN_MP_INVMOD_SLOW_C #define BN_MP_IS_SQUARE_C #define BN_MP_JACOBI_C #define BN_MP_KARATSUBA_MUL_C #define BN_MP_KARATSUBA_SQR_C #define BN_MP_LCM_C #define BN_MP_LSHD_C #define BN_MP_MOD_C #define BN_MP_MOD_2D_C #define BN_MP_MOD_D_C #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C #define BN_MP_MONTGOMERY_REDUCE_C #define BN_MP_MONTGOMERY_SETUP_C #define BN_MP_MUL_C #define BN_MP_MUL_2_C #define BN_MP_MUL_2D_C #define BN_MP_MUL_D_C #define BN_MP_MULMOD_C #define BN_MP_N_ROOT_C #define BN_MP_N_ROOT_EX_C #define BN_MP_NEG_C #define BN_MP_OR_C #define BN_MP_PRIME_FERMAT_C #define BN_MP_PRIME_IS_DIVISIBLE_C #define BN_MP_PRIME_IS_PRIME_C #define BN_MP_PRIME_MILLER_RABIN_C #define BN_MP_PRIME_NEXT_PRIME_C #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C #define BN_MP_PRIME_RANDOM_EX_C #define BN_MP_RADIX_SIZE_C #define BN_MP_RADIX_SMAP_C #define BN_MP_RAND_C #define BN_MP_READ_RADIX_C #define BN_MP_READ_SIGNED_BIN_C #define BN_MP_READ_UNSIGNED_BIN_C #define BN_MP_REDUCE_C #define BN_MP_REDUCE_2K_C #define BN_MP_REDUCE_2K_L_C #define BN_MP_REDUCE_2K_SETUP_C #define BN_MP_REDUCE_2K_SETUP_L_C #define BN_MP_REDUCE_IS_2K_C #define BN_MP_REDUCE_IS_2K_L_C #define BN_MP_REDUCE_SETUP_C #define BN_MP_RSHD_C #define BN_MP_SET_C #define BN_MP_SET_INT_C #define BN_MP_SET_LONG_C #define BN_MP_SET_LONG_LONG_C #define BN_MP_SHRINK_C #define BN_MP_SIGNED_BIN_SIZE_C #define BN_MP_SQR_C #define BN_MP_SQRMOD_C #define BN_MP_SQRT_C #define BN_MP_SQRTMOD_PRIME_C #define BN_MP_SUB_C #define BN_MP_SUB_D_C #define BN_MP_SUBMOD_C #define BN_MP_TO_SIGNED_BIN_C #define BN_MP_TO_SIGNED_BIN_N_C #define BN_MP_TO_UNSIGNED_BIN_C #define BN_MP_TO_UNSIGNED_BIN_N_C #define BN_MP_TOOM_MUL_C #define BN_MP_TOOM_SQR_C #define BN_MP_TORADIX_C #define BN_MP_TORADIX_N_C #define BN_MP_UNSIGNED_BIN_SIZE_C #define BN_MP_XOR_C #define BN_MP_ZERO_C #define BN_PRIME_TAB_C #define BN_REVERSE_C #define BN_S_MP_ADD_C #define BN_S_MP_EXPTMOD_C #define BN_S_MP_MUL_DIGS_C #define BN_S_MP_MUL_HIGH_DIGS_C #define BN_S_MP_SQR_C #define BN_S_MP_SUB_C #define BNCORE_C #endif #if defined(BN_ERROR_C) #define BN_MP_ERROR_TO_STRING_C #endif #if defined(BN_FAST_MP_INVMOD_C) #define BN_MP_ISEVEN_C #define BN_MP_INIT_MULTI_C #define BN_MP_COPY_C #define BN_MP_MOD_C #define BN_MP_SET_C #define BN_MP_DIV_2_C #define BN_MP_ISODD_C #define BN_MP_SUB_C #define BN_MP_CMP_C #define BN_MP_ISZERO_C #define BN_MP_CMP_D_C #define BN_MP_ADD_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C) #define BN_MP_GROW_C #define BN_MP_RSHD_C #define BN_MP_CLAMP_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_FAST_S_MP_MUL_DIGS_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_FAST_S_MP_SQR_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_2EXPT_C) #define BN_MP_ZERO_C #define BN_MP_GROW_C #endif #if defined(BN_MP_ABS_C) #define BN_MP_COPY_C #endif #if defined(BN_MP_ADD_C) #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_ADD_D_C) #define BN_MP_GROW_C #define BN_MP_SUB_D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_ADDMOD_C) #define BN_MP_INIT_C #define BN_MP_ADD_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_AND_C) #define BN_MP_INIT_COPY_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_CLAMP_C) #endif #if defined(BN_MP_CLEAR_C) #endif #if defined(BN_MP_CLEAR_MULTI_C) #define BN_MP_CLEAR_C #endif #if defined(BN_MP_CMP_C) #define BN_MP_CMP_MAG_C #endif #if defined(BN_MP_CMP_D_C) #endif #if defined(BN_MP_CMP_MAG_C) #endif #if defined(BN_MP_CNT_LSB_C) #define BN_MP_ISZERO_C #endif #if defined(BN_MP_COPY_C) #define BN_MP_GROW_C #endif #if defined(BN_MP_COUNT_BITS_C) #endif #if defined(BN_MP_DIV_C) #define BN_MP_ISZERO_C #define BN_MP_CMP_MAG_C #define BN_MP_COPY_C #define BN_MP_ZERO_C #define BN_MP_INIT_MULTI_C #define BN_MP_SET_C #define BN_MP_COUNT_BITS_C #define BN_MP_ABS_C #define BN_MP_MUL_2D_C #define BN_MP_CMP_C #define BN_MP_SUB_C #define BN_MP_ADD_C #define BN_MP_DIV_2D_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_INIT_SIZE_C #define BN_MP_INIT_C #define BN_MP_INIT_COPY_C #define BN_MP_LSHD_C #define BN_MP_RSHD_C #define BN_MP_MUL_D_C #define BN_MP_CLAMP_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_DIV_2_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_DIV_2D_C) #define BN_MP_COPY_C #define BN_MP_ZERO_C #define BN_MP_INIT_C #define BN_MP_MOD_2D_C #define BN_MP_CLEAR_C #define BN_MP_RSHD_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #endif #if defined(BN_MP_DIV_3_C) #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_DIV_D_C) #define BN_MP_ISZERO_C #define BN_MP_COPY_C #define BN_MP_DIV_2D_C #define BN_MP_DIV_3_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_DR_IS_MODULUS_C) #endif #if defined(BN_MP_DR_REDUCE_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_DR_SETUP_C) #endif #if defined(BN_MP_EXCH_C) #endif #if defined(BN_MP_EXPORT_C) #define BN_MP_INIT_COPY_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_2D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_EXPT_D_C) #define BN_MP_EXPT_D_EX_C #endif #if defined(BN_MP_EXPT_D_EX_C) #define BN_MP_INIT_COPY_C #define BN_MP_SET_C #define BN_MP_MUL_C #define BN_MP_CLEAR_C #define BN_MP_SQR_C #endif #if defined(BN_MP_EXPTMOD_C) #define BN_MP_INIT_C #define BN_MP_INVMOD_C #define BN_MP_CLEAR_C #define BN_MP_ABS_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_REDUCE_IS_2K_L_C #define BN_S_MP_EXPTMOD_C #define BN_MP_DR_IS_MODULUS_C #define BN_MP_REDUCE_IS_2K_C #define BN_MP_ISODD_C #define BN_MP_EXPTMOD_FAST_C #endif #if defined(BN_MP_EXPTMOD_FAST_C) #define BN_MP_COUNT_BITS_C #define BN_MP_INIT_C #define BN_MP_CLEAR_C #define BN_MP_MONTGOMERY_SETUP_C #define BN_FAST_MP_MONTGOMERY_REDUCE_C #define BN_MP_MONTGOMERY_REDUCE_C #define BN_MP_DR_SETUP_C #define BN_MP_DR_REDUCE_C #define BN_MP_REDUCE_2K_SETUP_C #define BN_MP_REDUCE_2K_C #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C #define BN_MP_MULMOD_C #define BN_MP_SET_C #define BN_MP_MOD_C #define BN_MP_COPY_C #define BN_MP_SQR_C #define BN_MP_MUL_C #define BN_MP_EXCH_C #endif #if defined(BN_MP_EXTEUCLID_C) #define BN_MP_INIT_MULTI_C #define BN_MP_SET_C #define BN_MP_COPY_C #define BN_MP_ISZERO_C #define BN_MP_DIV_C #define BN_MP_MUL_C #define BN_MP_SUB_C #define BN_MP_NEG_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_FREAD_C) #define BN_MP_ZERO_C #define BN_MP_S_RMAP_C #define BN_MP_MUL_D_C #define BN_MP_ADD_D_C #define BN_MP_CMP_D_C #endif #if defined(BN_MP_FWRITE_C) #define BN_MP_RADIX_SIZE_C #define BN_MP_TORADIX_C #endif #if defined(BN_MP_GCD_C) #define BN_MP_ISZERO_C #define BN_MP_ABS_C #define BN_MP_INIT_COPY_C #define BN_MP_CNT_LSB_C #define BN_MP_DIV_2D_C #define BN_MP_CMP_MAG_C #define BN_MP_EXCH_C #define BN_S_MP_SUB_C #define BN_MP_MUL_2D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_GET_INT_C) #endif #if defined(BN_MP_GET_LONG_C) #endif #if defined(BN_MP_GET_LONG_LONG_C) #endif #if defined(BN_MP_GROW_C) #endif #if defined(BN_MP_IMPORT_C) #define BN_MP_ZERO_C #define BN_MP_MUL_2D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_INIT_C) #endif #if defined(BN_MP_INIT_COPY_C) #define BN_MP_INIT_SIZE_C #define BN_MP_COPY_C #endif #if defined(BN_MP_INIT_MULTI_C) #define BN_MP_ERR_C #define BN_MP_INIT_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_INIT_SET_C) #define BN_MP_INIT_C #define BN_MP_SET_C #endif #if defined(BN_MP_INIT_SET_INT_C) #define BN_MP_INIT_C #define BN_MP_SET_INT_C #endif #if defined(BN_MP_INIT_SIZE_C) #define BN_MP_INIT_C #endif #if defined(BN_MP_INVMOD_C) #define BN_MP_ISZERO_C #define BN_MP_ISODD_C #define BN_FAST_MP_INVMOD_C #define BN_MP_INVMOD_SLOW_C #endif #if defined(BN_MP_INVMOD_SLOW_C) #define BN_MP_ISZERO_C #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_C #define BN_MP_COPY_C #define BN_MP_ISEVEN_C #define BN_MP_SET_C #define BN_MP_DIV_2_C #define BN_MP_ISODD_C #define BN_MP_ADD_C #define BN_MP_SUB_C #define BN_MP_CMP_C #define BN_MP_CMP_D_C #define BN_MP_CMP_MAG_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_IS_SQUARE_C) #define BN_MP_MOD_D_C #define BN_MP_INIT_SET_INT_C #define BN_MP_MOD_C #define BN_MP_GET_INT_C #define BN_MP_SQRT_C #define BN_MP_SQR_C #define BN_MP_CMP_MAG_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_JACOBI_C) #define BN_MP_CMP_D_C #define BN_MP_ISZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_CNT_LSB_C #define BN_MP_DIV_2D_C #define BN_MP_MOD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_KARATSUBA_MUL_C) #define BN_MP_MUL_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_S_MP_ADD_C #define BN_MP_ADD_C #define BN_S_MP_SUB_C #define BN_MP_LSHD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_KARATSUBA_SQR_C) #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_SQR_C #define BN_S_MP_ADD_C #define BN_S_MP_SUB_C #define BN_MP_LSHD_C #define BN_MP_ADD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_LCM_C) #define BN_MP_INIT_MULTI_C #define BN_MP_GCD_C #define BN_MP_CMP_MAG_C #define BN_MP_DIV_C #define BN_MP_MUL_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_LSHD_C) #define BN_MP_GROW_C #define BN_MP_RSHD_C #endif #if defined(BN_MP_MOD_C) #define BN_MP_INIT_C #define BN_MP_DIV_C #define BN_MP_CLEAR_C #define BN_MP_ISZERO_C #define BN_MP_EXCH_C #define BN_MP_ADD_C #endif #if defined(BN_MP_MOD_2D_C) #define BN_MP_ZERO_C #define BN_MP_COPY_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_MOD_D_C) #define BN_MP_DIV_D_C #endif #if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C) #define BN_MP_COUNT_BITS_C #define BN_MP_2EXPT_C #define BN_MP_SET_C #define BN_MP_MUL_2_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_MONTGOMERY_REDUCE_C) #define BN_FAST_MP_MONTGOMERY_REDUCE_C #define BN_MP_GROW_C #define BN_MP_CLAMP_C #define BN_MP_RSHD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_MONTGOMERY_SETUP_C) #endif #if defined(BN_MP_MUL_C) #define BN_MP_TOOM_MUL_C #define BN_MP_KARATSUBA_MUL_C #define BN_FAST_S_MP_MUL_DIGS_C #define BN_S_MP_MUL_C #define BN_S_MP_MUL_DIGS_C #endif #if defined(BN_MP_MUL_2_C) #define BN_MP_GROW_C #endif #if defined(BN_MP_MUL_2D_C) #define BN_MP_COPY_C #define BN_MP_GROW_C #define BN_MP_LSHD_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_MUL_D_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_MULMOD_C) #define BN_MP_INIT_C #define BN_MP_MUL_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_N_ROOT_C) #define BN_MP_N_ROOT_EX_C #endif #if defined(BN_MP_N_ROOT_EX_C) #define BN_MP_INIT_C #define BN_MP_SET_C #define BN_MP_COPY_C #define BN_MP_EXPT_D_EX_C #define BN_MP_MUL_C #define BN_MP_SUB_C #define BN_MP_MUL_D_C #define BN_MP_DIV_C #define BN_MP_CMP_C #define BN_MP_SUB_D_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_NEG_C) #define BN_MP_COPY_C #define BN_MP_ISZERO_C #endif #if defined(BN_MP_OR_C) #define BN_MP_INIT_COPY_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_FERMAT_C) #define BN_MP_CMP_D_C #define BN_MP_INIT_C #define BN_MP_EXPTMOD_C #define BN_MP_CMP_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_IS_DIVISIBLE_C) #define BN_MP_MOD_D_C #endif #if defined(BN_MP_PRIME_IS_PRIME_C) #define BN_MP_CMP_D_C #define BN_MP_PRIME_IS_DIVISIBLE_C #define BN_MP_INIT_C #define BN_MP_SET_C #define BN_MP_PRIME_MILLER_RABIN_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_MILLER_RABIN_C) #define BN_MP_CMP_D_C #define BN_MP_INIT_COPY_C #define BN_MP_SUB_D_C #define BN_MP_CNT_LSB_C #define BN_MP_DIV_2D_C #define BN_MP_EXPTMOD_C #define BN_MP_CMP_C #define BN_MP_SQRMOD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_NEXT_PRIME_C) #define BN_MP_CMP_D_C #define BN_MP_SET_C #define BN_MP_SUB_D_C #define BN_MP_ISEVEN_C #define BN_MP_MOD_D_C #define BN_MP_INIT_C #define BN_MP_ADD_D_C #define BN_MP_PRIME_MILLER_RABIN_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C) #endif #if defined(BN_MP_PRIME_RANDOM_EX_C) #define BN_MP_READ_UNSIGNED_BIN_C #define BN_MP_PRIME_IS_PRIME_C #define BN_MP_SUB_D_C #define BN_MP_DIV_2_C #define BN_MP_MUL_2_C #define BN_MP_ADD_D_C #endif #if defined(BN_MP_RADIX_SIZE_C) #define BN_MP_ISZERO_C #define BN_MP_COUNT_BITS_C #define BN_MP_INIT_COPY_C #define BN_MP_DIV_D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_RADIX_SMAP_C) #define BN_MP_S_RMAP_C #endif #if defined(BN_MP_RAND_C) #define BN_MP_ZERO_C #define BN_MP_ADD_D_C #define BN_MP_LSHD_C #endif #if defined(BN_MP_READ_RADIX_C) #define BN_MP_ZERO_C #define BN_MP_S_RMAP_C #define BN_MP_MUL_D_C #define BN_MP_ADD_D_C #define BN_MP_ISZERO_C #endif #if defined(BN_MP_READ_SIGNED_BIN_C) #define BN_MP_READ_UNSIGNED_BIN_C #endif #if defined(BN_MP_READ_UNSIGNED_BIN_C) #define BN_MP_GROW_C #define BN_MP_ZERO_C #define BN_MP_MUL_2D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_REDUCE_C) #define BN_MP_REDUCE_SETUP_C #define BN_MP_INIT_COPY_C #define BN_MP_RSHD_C #define BN_MP_MUL_C #define BN_S_MP_MUL_HIGH_DIGS_C #define BN_FAST_S_MP_MUL_HIGH_DIGS_C #define BN_MP_MOD_2D_C #define BN_S_MP_MUL_DIGS_C #define BN_MP_SUB_C #define BN_MP_CMP_D_C #define BN_MP_SET_C #define BN_MP_LSHD_C #define BN_MP_ADD_C #define BN_MP_CMP_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_2K_C) #define BN_MP_INIT_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_2D_C #define BN_MP_MUL_D_C #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_2K_L_C) #define BN_MP_INIT_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_2D_C #define BN_MP_MUL_C #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_2K_SETUP_C) #define BN_MP_INIT_C #define BN_MP_COUNT_BITS_C #define BN_MP_2EXPT_C #define BN_MP_CLEAR_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_REDUCE_2K_SETUP_L_C) #define BN_MP_INIT_C #define BN_MP_2EXPT_C #define BN_MP_COUNT_BITS_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_IS_2K_C) #define BN_MP_REDUCE_2K_C #define BN_MP_COUNT_BITS_C #endif #if defined(BN_MP_REDUCE_IS_2K_L_C) #endif #if defined(BN_MP_REDUCE_SETUP_C) #define BN_MP_2EXPT_C #define BN_MP_DIV_C #endif #if defined(BN_MP_RSHD_C) #define BN_MP_ZERO_C #endif #if defined(BN_MP_SET_C) #define BN_MP_ZERO_C #endif #if defined(BN_MP_SET_INT_C) #define BN_MP_ZERO_C #define BN_MP_MUL_2D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_SET_LONG_C) #endif #if defined(BN_MP_SET_LONG_LONG_C) #endif #if defined(BN_MP_SHRINK_C) #endif #if defined(BN_MP_SIGNED_BIN_SIZE_C) #define BN_MP_UNSIGNED_BIN_SIZE_C #endif #if defined(BN_MP_SQR_C) #define BN_MP_TOOM_SQR_C #define BN_MP_KARATSUBA_SQR_C #define BN_FAST_S_MP_SQR_C #define BN_S_MP_SQR_C #endif #if defined(BN_MP_SQRMOD_C) #define BN_MP_INIT_C #define BN_MP_SQR_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_SQRT_C) #define BN_MP_N_ROOT_C #define BN_MP_ISZERO_C #define BN_MP_ZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_RSHD_C #define BN_MP_DIV_C #define BN_MP_ADD_C #define BN_MP_DIV_2_C #define BN_MP_CMP_MAG_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_SQRTMOD_PRIME_C) #define BN_MP_CMP_D_C #define BN_MP_ZERO_C #define BN_MP_JACOBI_C #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_D_C #define BN_MP_ADD_D_C #define BN_MP_DIV_2_C #define BN_MP_EXPTMOD_C #define BN_MP_COPY_C #define BN_MP_SUB_D_C #define BN_MP_ISEVEN_C #define BN_MP_SET_INT_C #define BN_MP_SQRMOD_C #define BN_MP_MULMOD_C #define BN_MP_SET_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_SUB_C) #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_SUB_D_C) #define BN_MP_GROW_C #define BN_MP_ADD_D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_SUBMOD_C) #define BN_MP_INIT_C #define BN_MP_SUB_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_TO_SIGNED_BIN_C) #define BN_MP_TO_UNSIGNED_BIN_C #endif #if defined(BN_MP_TO_SIGNED_BIN_N_C) #define BN_MP_SIGNED_BIN_SIZE_C #define BN_MP_TO_SIGNED_BIN_C #endif #if defined(BN_MP_TO_UNSIGNED_BIN_C) #define BN_MP_INIT_COPY_C #define BN_MP_ISZERO_C #define BN_MP_DIV_2D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_TO_UNSIGNED_BIN_N_C) #define BN_MP_UNSIGNED_BIN_SIZE_C #define BN_MP_TO_UNSIGNED_BIN_C #endif #if defined(BN_MP_TOOM_MUL_C) #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_2D_C #define BN_MP_COPY_C #define BN_MP_RSHD_C #define BN_MP_MUL_C #define BN_MP_MUL_2_C #define BN_MP_ADD_C #define BN_MP_SUB_C #define BN_MP_DIV_2_C #define BN_MP_MUL_2D_C #define BN_MP_MUL_D_C #define BN_MP_DIV_3_C #define BN_MP_LSHD_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_TOOM_SQR_C) #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_2D_C #define BN_MP_COPY_C #define BN_MP_RSHD_C #define BN_MP_SQR_C #define BN_MP_MUL_2_C #define BN_MP_ADD_C #define BN_MP_SUB_C #define BN_MP_DIV_2_C #define BN_MP_MUL_2D_C #define BN_MP_MUL_D_C #define BN_MP_DIV_3_C #define BN_MP_LSHD_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_TORADIX_C) #define BN_MP_ISZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_DIV_D_C #define BN_MP_CLEAR_C #define BN_MP_S_RMAP_C #endif #if defined(BN_MP_TORADIX_N_C) #define BN_MP_ISZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_DIV_D_C #define BN_MP_CLEAR_C #define BN_MP_S_RMAP_C #endif #if defined(BN_MP_UNSIGNED_BIN_SIZE_C) #define BN_MP_COUNT_BITS_C #endif #if defined(BN_MP_XOR_C) #define BN_MP_INIT_COPY_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_ZERO_C) #endif #if defined(BN_PRIME_TAB_C) #endif #if defined(BN_REVERSE_C) #endif #if defined(BN_S_MP_ADD_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_S_MP_EXPTMOD_C) #define BN_MP_COUNT_BITS_C #define BN_MP_INIT_C #define BN_MP_CLEAR_C #define BN_MP_REDUCE_SETUP_C #define BN_MP_REDUCE_C #define BN_MP_REDUCE_2K_SETUP_L_C #define BN_MP_REDUCE_2K_L_C #define BN_MP_MOD_C #define BN_MP_COPY_C #define BN_MP_SQR_C #define BN_MP_MUL_C #define BN_MP_SET_C #define BN_MP_EXCH_C #endif #if defined(BN_S_MP_MUL_DIGS_C) #define BN_FAST_S_MP_MUL_DIGS_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_S_MP_MUL_HIGH_DIGS_C) #define BN_FAST_S_MP_MUL_HIGH_DIGS_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_S_MP_SQR_C) #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_S_MP_SUB_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BNCORE_C) #endif #ifdef LTM3 #define LTM_LAST #endif #include "libtorrent/tommath_superclass.h" #include "libtorrent/tommath_class.h" #else #define LTM_LAST #endif libtorrent-rasterbar-1.1.13/include/libtorrent/tommath_private.h000077500000000000000000000103011351156116000251240ustar00rootroot00000000000000/* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://math.libtomcrypt.com */ #ifndef TOMMATH_PRIV_H_ #define TOMMATH_PRIV_H_ #include "libtorrent/tommath.h" #include #define MIN(x,y) (((x) < (y)) ? (x) : (y)) #define MAX(x,y) (((x) > (y)) ? (x) : (y)) #ifdef __cplusplus extern "C" { /* C++ compilers don't like assigning void * to mp_digit * */ #define OPT_CAST(x) (x *) #else /* C on the other hand doesn't care */ #define OPT_CAST(x) #endif /* define heap macros */ #ifndef XMALLOC /* default to libc stuff */ #define XMALLOC malloc #define XFREE free #define XREALLOC realloc #define XCALLOC calloc #else /* prototypes for our heap functions */ extern void *XMALLOC(size_t n); extern void *XREALLOC(void *p, size_t n); extern void *XCALLOC(size_t n, size_t s); extern void XFREE(void *p); #endif /* lowlevel functions, do not call! */ int s_mp_add(mp_int *a, mp_int *b, mp_int *c); int s_mp_sub(mp_int *a, mp_int *b, mp_int *c); #define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); int fast_s_mp_sqr(mp_int *a, mp_int *b); int s_mp_sqr(mp_int *a, mp_int *b); int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c); int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c); int mp_karatsuba_sqr(mp_int *a, mp_int *b); int mp_toom_sqr(mp_int *a, mp_int *b); int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c); int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c); int fast_mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho); int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode); int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); void bn_reverse(unsigned char *s, int len); extern const char *mp_s_rmap; /* Fancy macro to set an MPI from another type. * There are several things assumed: * x is the counter and unsigned * a is the pointer to the MPI * b is the original value that should be set in the MPI. */ #define MP_SET_XLONG(func_name, type) \ int func_name (mp_int * a, type b) \ { \ unsigned int x; \ int res; \ \ mp_zero (a); \ \ /* set four bits at a time */ \ for (x = 0; x < (sizeof(type) * 2u); x++) { \ /* shift the number up four bits */ \ if ((res = mp_mul_2d (a, 4, a)) != MP_OKAY) { \ return res; \ } \ \ /* OR in the top four bits of the source */ \ a->dp[0] |= (b >> ((sizeof(type) * 8u) - 4u)) & 15u; \ \ /* shift the source up to the next four bits */ \ b <<= 4; \ \ /* ensure that digits are not clamped off */ \ a->used += 1; \ } \ mp_clamp (a); \ return MP_OKAY; \ } #ifdef __cplusplus } #endif #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ libtorrent-rasterbar-1.1.13/include/libtorrent/tommath_superclass.h000066400000000000000000000042721351156116000256450ustar00rootroot00000000000000/* super class file for PK algos */ /* default ... include all MPI */ #define LTM_ALL /* RSA only (does not support DH/DSA/ECC) */ /* #define SC_RSA_1 */ /* For reference.... On an Athlon64 optimizing for speed... LTM's mpi.o with all functions [striped] is 142KiB in size. */ /* Works for RSA only, mpi.o is 68KiB */ #ifdef SC_RSA_1 #define BN_MP_SHRINK_C #define BN_MP_LCM_C #define BN_MP_PRIME_RANDOM_EX_C #define BN_MP_INVMOD_C #define BN_MP_GCD_C #define BN_MP_MOD_C #define BN_MP_MULMOD_C #define BN_MP_ADDMOD_C #define BN_MP_EXPTMOD_C #define BN_MP_SET_INT_C #define BN_MP_INIT_MULTI_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_UNSIGNED_BIN_SIZE_C #define BN_MP_TO_UNSIGNED_BIN_C #define BN_MP_MOD_D_C #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C #define BN_REVERSE_C #define BN_PRIME_TAB_C /* other modifiers */ #define BN_MP_DIV_SMALL /* Slower division, not critical */ /* here we are on the last pass so we turn things off. The functions classes are still there * but we remove them specifically from the build. This also invokes tweaks in functions * like removing support for even moduli, etc... */ #ifdef LTM_LAST #undef BN_MP_TOOM_MUL_C #undef BN_MP_TOOM_SQR_C #undef BN_MP_KARATSUBA_MUL_C #undef BN_MP_KARATSUBA_SQR_C #undef BN_MP_REDUCE_C #undef BN_MP_REDUCE_SETUP_C #undef BN_MP_DR_IS_MODULUS_C #undef BN_MP_DR_SETUP_C #undef BN_MP_DR_REDUCE_C #undef BN_MP_REDUCE_IS_2K_C #undef BN_MP_REDUCE_2K_SETUP_C #undef BN_MP_REDUCE_2K_C #undef BN_S_MP_EXPTMOD_C #undef BN_MP_DIV_3_C #undef BN_S_MP_MUL_HIGH_DIGS_C #undef BN_FAST_S_MP_MUL_HIGH_DIGS_C #undef BN_FAST_MP_INVMOD_C /* To safely undefine these you have to make sure your RSA key won't exceed the Comba threshold * which is roughly 255 digits [7140 bits for 32-bit machines, 15300 bits for 64-bit machines] * which means roughly speaking you can handle upto 2536-bit RSA keys with these defined without * trouble. */ #undef BN_S_MP_MUL_DIGS_C #undef BN_S_MP_SQR_C #undef BN_MP_MONTGOMERY_REDUCE_C #endif #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ libtorrent-rasterbar-1.1.13/include/libtorrent/torrent.hpp000066400000000000000000001614021351156116000237640ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include #include #include #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/address.hpp" #include "libtorrent/peer_list.hpp" #include "libtorrent/tracker_manager.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/piece_picker.hpp" #include "libtorrent/config.hpp" #include "libtorrent/bandwidth_limit.hpp" #include "libtorrent/bandwidth_queue_entry.hpp" #include "libtorrent/storage_defs.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/bitfield.hpp" #include "libtorrent/aux_/session_interface.hpp" #include "libtorrent/deadline_timer.hpp" #include "libtorrent/peer_class_set.hpp" #include "libtorrent/link.hpp" #include "libtorrent/vector_utils.hpp" #include "libtorrent/linked_list.hpp" #include "libtorrent/debug.hpp" #include "libtorrent/aux_/file_progress.hpp" #ifdef TORRENT_USE_OPENSSL // there is no forward declaration header for asio namespace boost { namespace asio { namespace ssl { struct context; class verify_context; } } } #endif #if TORRENT_COMPLETE_TYPES_REQUIRED #include "libtorrent/peer_connection.hpp" #endif // define as 0 to disable. 1 enables debug output of the pieces and requested // blocks. 2 also enables trace output of the time critical piece picking // logic #define TORRENT_DEBUG_STREAMING 0 namespace libtorrent { class http_parser; class piece_manager; struct torrent_plugin; struct bitfield; struct announce_entry; struct tracker_request; struct add_torrent_params; struct storage_interface; class bt_peer_connection; struct listen_socket_t; peer_id generate_peer_id(aux::session_settings const& sett); namespace aux { struct piece_checker_data; } struct resume_data_t { std::vector buf; bdecode_node node; }; struct time_critical_piece { // when this piece was first requested time_point first_requested; // when this piece was last requested time_point last_requested; // by what time we want this piece time_point deadline; // 1 = send alert with piece data when available int flags; // how many peers it's been requested from int peers; // the piece index int piece; #if TORRENT_DEBUG_STREAMING > 0 // the number of multiple requests are allowed // to blocks still not downloaded (debugging only) int timed_out; #endif bool operator<(time_critical_piece const& rhs) const { return deadline < rhs.deadline; } }; // this is the internal representation of web seeds struct web_seed_t : web_seed_entry { web_seed_t(web_seed_entry const& wse); web_seed_t(std::string const& url_, web_seed_entry::type_t type_ , std::string const& auth_ = std::string() , web_seed_entry::headers_t const& extra_headers_ = web_seed_entry::headers_t()); // if this is > now, we can't reconnect yet time_point retry; // if the hostname of the web seed has been resolved, // these are its IP addresses std::vector endpoints; // this is the peer_info field used for the // connection, just to count hash failures // it's also used to hold the peer_connection // pointer, when the web seed is connected ipv4_peer peer_info; // this is initialized to true, but if we discover the // server not to support it, it's set to false, and we // make larger requests. bool supports_keepalive; // this indicates whether or not we're resolving the // hostname of this URL bool resolving; // if the user wanted to remove this while // we were resolving it. In this case, we set // the removed flag to true, to make the resolver // callback remove it bool removed; // if the web server doesn't support keepalive or a block request was // interrupted, the block received so far is kept here for the next // connection to pick up peer_request restart_request; std::vector restart_piece; }; struct TORRENT_EXTRA_EXPORT torrent_hot_members { torrent_hot_members(aux::session_interface& ses , add_torrent_params const& p, int block_size); protected: // the piece picker. This is allocated lazily. When we don't // have anything in the torrent (for instance, if it hasn't // been started yet) or if we have everything, there is no // picker. It's allocated on-demand the first time we need // it in torrent::need_picker(). In order to tell the // difference between having everything and nothing in // the case there is no piece picker, see m_have_all. boost::scoped_ptr m_picker; // TODO: make this a raw pointer. perhaps keep the shared_ptr // around further down the object to maintain an owner boost::shared_ptr m_torrent_file; // a back reference to the session // this torrent belongs to. aux::session_interface& m_ses; // this vector is sorted at all times, by the pointer value. // use sorted_insert() and sorted_find() on it. The GNU STL // implementation on Darwin uses significantly less memory to // represent a vector than a set, and this set is typically // relatively small, and it's cheap to copy pointers. std::vector m_connections; // the scrape data from the tracker response, this // is optional and may be 0xffffff boost::uint32_t m_complete:24; // set to true when this torrent may not download anything bool m_upload_mode:1; // 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:1; // is set to true when the torrent has // been aborted. bool m_abort:1; // is true if this torrent has allows having peers bool m_allow_peers:1; // this is set when the torrent is in share-mode bool m_share_mode:1; // this is true if we have all pieces. If it's false, // it means we either don't have any pieces, or, if // there is a piece_picker object present, it contans // the state of how many pieces we have bool m_have_all:1; // set to true when this torrent has been paused but // is waiting to finish all current download requests // before actually closing all connections, When in graceful pause mode, // m_allow_peers is also false. bool m_graceful_pause_mode:1; // state subscription. If set, a pointer to this torrent will be added // to the session_impl::m_torrent_lists[torrent_state_updates] // whenever this torrent's state changes (any state). bool m_state_subscription:1; // the maximum number of connections for this torrent boost::uint32_t m_max_connections:24; // the size of a request block // each piece is divided into these // blocks when requested. The block size is // 1 << m_block_size_shift boost::uint32_t m_block_size_shift:5; // the state of this torrent (queued, checking, downloading, etc.) boost::uint32_t m_state:3; boost::scoped_ptr m_peer_list; }; // a torrent is a class that holds information // for a specific download. It updates itself against // the tracker class TORRENT_EXTRA_EXPORT torrent : private single_threaded , private torrent_hot_members , public request_callback , public peer_class_set , public boost::enable_shared_from_this , public list_node // used for torrent activity LRU { public: torrent(aux::session_interface& ses, int block_size , int seq, add_torrent_params const& p , sha1_hash const& info_hash); ~torrent(); // This may be called from multiple threads sha1_hash const& info_hash() const { return m_info_hash; } bool is_deleted() const { return m_deleted; } // starts the announce timer void start(add_torrent_params const& p); void start_download_url(); // returns which stats gauge this torrent currently // has incremented. int current_stats_state() const; #ifndef TORRENT_DISABLE_EXTENSIONS void add_extension(boost::shared_ptr); void remove_extension(boost::shared_ptr); void add_extension(boost::function(torrent_handle const&, void*)> const& ext , void* userdata); void notify_extension_add_peer(tcp::endpoint const& ip, int src, int flags); #endif peer_connection* find_lowest_ranking_peer() const; #if TORRENT_USE_ASSERTS bool has_peer(peer_connection const* p) const { return sorted_find(m_connections, p) != m_connections.end(); } bool is_single_thread() const { return single_threaded::is_single_thread(); } #endif // this is called when the torrent has metadata. // it will initialize the storage and the piece-picker void init(); // called every time we actually need the torrent_info // object to be fully loaded. If it isn't, this triggers // loading it from disk // the return value indicates success. If it failed to // load, the torrent will be set to an error state and // return false bool need_loaded(); // unload the torrent file to save memory void unload(); // returns true if parsed successfully bool load(std::vector& buffer); // pinned torrents may not be unloaded bool is_pinned() const { return m_pinned; } void set_pinned(bool p); bool is_loaded() const { return m_torrent_file->is_loaded(); } bool should_be_loaded() const { return m_should_be_loaded; } // find the peer that introduced us to the given endpoint. This is // used when trying to holepunch. We need the introducer so that we // can send a rendezvous connect message bt_peer_connection* find_introducer(tcp::endpoint const& ep) const; // if we're connected to a peer at ep, return its peer connection // only count BitTorrent peers bt_peer_connection* find_peer(tcp::endpoint const& ep) const; peer_connection* find_peer(peer_id const& pid); void on_resume_data_checked(disk_io_job const* j); void on_force_recheck(disk_io_job const* j); void on_piece_hashed(disk_io_job const* j); void files_checked(); void start_checking(); void start_announcing(); void stop_announcing(); void send_share_mode(); void send_upload_only(); void set_share_mode(bool s); bool share_mode() const { return m_share_mode; } // TOOD: make graceful pause also finish all sending blocks // before disconnecting bool graceful_pause() const { return m_graceful_pause_mode; } void set_upload_mode(bool b); bool upload_mode() const { return m_upload_mode || m_graceful_pause_mode; } bool is_upload_only() const { return is_finished() || upload_mode(); } int seed_rank(aux::session_settings const& s) const; enum flags_t { overwrite_existing = 1 }; void add_piece(int piece, char const* data, int flags = 0); void on_disk_write_complete(disk_io_job const* j , peer_request p); void on_disk_cache_complete(disk_io_job const* j); void on_disk_tick_done(disk_io_job const* j); void schedule_storage_tick(); void set_progress_ppm(int p) { m_progress_ppm = p; } struct read_piece_struct { boost::shared_array piece_data; int blocks_left; bool fail; error_code error; }; void read_piece(int piece); void on_disk_read_complete(disk_io_job const* j, peer_request r , boost::shared_ptr rp); storage_mode_t storage_mode() const; storage_interface* get_storage(); // 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; } void new_external_ip(); torrent_status::state_t state() const { return torrent_status::state_t(m_state); } void set_state(torrent_status::state_t s); aux::session_settings const& settings() const; aux::session_interface& session() { return m_ses; } void set_sequential_download(bool sd); bool is_sequential_download() const { return m_sequential_download || m_auto_sequential; } void queue_up(); void queue_down(); void set_queue_position(int p); int queue_position() const { return m_sequence_number; } // used internally void set_queue_position_impl(int p) { m_sequence_number = p; } void second_tick(int tick_interval_ms); // see if we need to connect to web seeds, and if so, // connect to them void maybe_connect_web_seeds(); std::string name() const; stat statistics() const { return m_stat; } boost::int64_t bytes_left() const; int block_bytes_wanted(piece_block const& p) const; void bytes_done(torrent_status& st, bool accurate) const; boost::int64_t quantized_bytes_done() const; void sent_bytes(int bytes_payload, int bytes_protocol); void received_bytes(int bytes_payload, int bytes_protocol); void trancieve_ip_packet(int bytes, bool ipv6); void sent_syn(bool ipv6); void received_synack(bool ipv6); void set_ip_filter(boost::shared_ptr ipf); void port_filter_updated(); ip_filter const* get_ip_filter() { return m_ip_filter.get(); } std::string resolve_filename(int file) const; void handle_disk_error(disk_io_job const* j, peer_connection* c = 0); void clear_error(); void set_error(error_code const& ec, int file); bool has_error() const { return !!m_error; } error_code error() const { return m_error; } void flush_cache(); void pause(bool graceful = false); void resume(); enum pause_flags_t { flag_graceful_pause = 1, flag_clear_disk_cache = 2 }; void set_allow_peers(bool b, int flags = flag_clear_disk_cache); void set_announce_to_dht(bool b) { m_announce_to_dht = b; } void set_announce_to_trackers(bool b) { m_announce_to_trackers = b; } void set_announce_to_lsd(bool b) { m_announce_to_lsd = b; } void stop_when_ready(bool b); int started() const { return m_started; } void step_session_time(int seconds); void do_pause(bool clear_disk_cache = true); void do_resume(); int finished_time() const; int active_time() const; int seeding_time() const; bool is_paused() const; bool allows_peers() const { return m_allow_peers; } bool is_torrent_paused() const { return !m_allow_peers || m_graceful_pause_mode; } void force_recheck(); void save_resume_data(int flags); bool do_async_save_resume_data(); bool need_save_resume_data() const { // save resume data every 15 minutes regardless, just to // keep stats up to date return m_need_save_resume_data || m_ses.session_time() - m_last_saved_resume > 15 * 60; } void set_need_save_resume() { m_need_save_resume_data = true; } bool is_auto_managed() const { return m_auto_managed; } void auto_managed(bool a); bool should_check_files() const; bool delete_files(int options); void peers_erased(std::vector const& peers); // ============ start deprecation ============= void filter_piece(int index, bool filter); void filter_pieces(std::vector const& bitmask); bool is_piece_filtered(int index) const; void filtered_pieces(std::vector& bitmask) const; void filter_files(std::vector const& files); #if !TORRENT_NO_FPU void file_progress(std::vector& fp); #endif // ============ end deprecation ============= void piece_availability(std::vector& avail) const; void set_piece_priority(int index, int priority); int piece_priority(int index) const; void prioritize_pieces(std::vector const& pieces); void prioritize_piece_list(std::vector > const& pieces); void piece_priorities(std::vector*) const; void set_file_priority(int index, int priority); int file_priority(int index) const; void on_file_priority(disk_io_job const* j); void prioritize_files(std::vector const& files); void file_priorities(std::vector*) const; void cancel_non_critical(); void set_piece_deadline(int piece, int t, int flags); void reset_piece_deadline(int piece); void clear_time_critical(); void update_piece_priorities(std::vector const& file_prio); void status(torrent_status* st, boost::uint32_t flags); // this torrent changed state, if the user is subscribing to // it, add it to the m_state_updates list in session_impl void state_updated(); void file_progress(std::vector& fp, int flags = 0); #ifndef TORRENT_NO_DEPRECATE void use_interface(std::string net_interface); #endif void connect_to_url_seed(std::list::iterator url); bool connect_to_peer(torrent_peer* peerinfo, bool ignore_limit = false); int priority() const; void set_priority(int const prio); #ifndef TORRENT_NO_DEPRECATE // deprecated in 1.1 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES void resolve_countries(bool r); bool resolving_countries() const; void resolve_peer_country(boost::shared_ptr const& p) const; void on_country_lookup(error_code const& error , std::vector
const& host_list , boost::shared_ptr p) const; #endif #endif // TORRENT_NO_DEPRECATE // -------------------------------------------- // BANDWIDTH MANAGEMENT void set_upload_limit(int limit); int upload_limit() const; void set_download_limit(int limit); int download_limit() const; peer_class_t peer_class() const { return m_peer_class; } void set_max_uploads(int limit, bool state_update = true); int max_uploads() const { return m_max_uploads; } void set_max_connections(int limit, bool state_update = true); int max_connections() const { return m_max_connections; } // -------------------------------------------- // PEER MANAGEMENT // add or remove a url that will be attempted for // finding the file(s) in this torrent. void add_web_seed(std::string const& url, web_seed_t::type_t type); void add_web_seed(std::string const& url, web_seed_t::type_t type , std::string const& auth, web_seed_t::headers_t const& extra_headers); void remove_web_seed(std::string const& url, web_seed_t::type_t type); void disconnect_web_seed(peer_connection* p); void retry_web_seed(peer_connection* p, int retry = 0); void remove_web_seed(peer_connection* p, error_code const& ec , operation_t op, int error = 0); std::set web_seeds(web_seed_entry::type_t type) const; bool free_upload_slots() const { return m_num_uploads < m_max_uploads; } bool choke_peer(peer_connection& c); bool unchoke_peer(peer_connection& c, bool optimistic = false); void trigger_unchoke(); void trigger_optimistic_unchoke(); // 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. // false means attach failed bool 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); // cancel requests to this block from any peer we're // connected to on this torrent void cancel_block(piece_block block); bool want_tick() const; void update_want_tick(); void update_state_list(); bool want_peers() const; bool want_peers_download() const; bool want_peers_finished() const; void update_want_peers(); void update_want_scrape(); void update_gauge(); bool try_connect_peer(); torrent_peer* add_peer(tcp::endpoint const& adr, int source, int flags = 0); bool ban_peer(torrent_peer* tp); void update_peer_port(int port, torrent_peer* p, int src); void set_seed(torrent_peer* p, bool s); void clear_failcount(torrent_peer* p); std::pair find_peers(address const& a); // the number of peers that belong to this torrent int num_peers() const { return int(m_connections.size()); } int num_seeds() const; int num_downloaders() const; typedef std::vector::iterator peer_iterator; typedef std::vector::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(); } void get_full_peer_list(std::vector& v) const; void get_peer_info(std::vector& v); void get_download_queue(std::vector* queue) const; #ifndef TORRENT_NO_DEPRECATE void refresh_explicit_cache(int cache_size); #endif void add_suggest_piece(int piece); void update_suggest_piece(int index, int change); void update_auto_sequential(); void refresh_suggest_pieces(); void do_refresh_suggest_pieces(); // -------------------------------------------- // 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 , address const& tracker_ip , std::list
const& ip_list , struct tracker_response const& resp) TORRENT_OVERRIDE; virtual void tracker_request_error(tracker_request const& r , int response_code, error_code const& ec, const std::string& msg , int retry_interval) TORRENT_OVERRIDE; virtual void tracker_warning(tracker_request const& req , std::string const& msg) TORRENT_OVERRIDE; virtual void tracker_scrape_response(tracker_request const& req , int complete, int incomplete, int downloaded, int downloaders) TORRENT_OVERRIDE; void update_scrape_state(); // 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; // generate the tracker key for this torrent. // The key is passed to http trackers as ``&key=``. boost::uint32_t tracker_key() const; // if we need a connect boost, connect some peers // immediately void do_connect_boost(); // forcefully sets next_announce to the current time void force_tracker_request(time_point, int tracker_idx, int flags); void scrape_tracker(int idx, bool user_triggered); void announce_with_tracker(boost::uint8_t e = tracker_request::none); int seconds_since_last_scrape() const; #ifndef TORRENT_DISABLE_DHT void dht_announce(); #endif #ifndef TORRENT_NO_DEPRECATE // sets the username and password that will be sent to // the tracker void set_tracker_login(std::string const& name, std::string const& pw); #endif announce_entry* find_tracker(tracker_request const& r); // -------------------------------------------- // PIECE MANAGEMENT void recalc_share_mode(); struct suggest_piece_t { int piece_index; int num_peers; bool operator<(suggest_piece_t const& p) const { return num_peers < p.num_peers; } }; std::vector const& get_suggested_pieces() const { return m_suggested_pieces; } bool super_seeding() const { // we're not super seeding if we're not a seed return m_super_seeding; } void super_seeding(bool on); int get_piece_to_super_seed(bitfield const&); // returns true if we have downloaded the given piece bool have_piece(int index) const { if (!valid_metadata()) return false; if (!has_picker()) return m_have_all; return m_picker->have_piece(index); } // returns true if we have downloaded the given piece bool has_piece_passed(int index) const { if (!valid_metadata()) return false; if (index < 0 || index >= torrent_file().num_pieces()) return false; if (!has_picker()) return m_have_all; return m_picker->has_piece_passed(index); } // a predictive piece is a piece that we might // not have yet, but still announced to peers, anticipating that // we'll have it very soon bool is_predictive_piece(int index) const { return std::binary_search(m_predictive_pieces.begin(), m_predictive_pieces.end(), index); } private: // called when we learn that we have a piece // only once per piece void we_have(int index); public: int num_have() const { // pretend we have every piece when in seed mode if (m_seed_mode) { return m_torrent_file->num_pieces(); } return has_picker() ? m_picker->num_have() : m_have_all ? m_torrent_file->num_pieces() : 0; } // the number of pieces that have passed // hash check, but aren't necessarily // flushed to disk yet int num_passed() const { return has_picker() ? m_picker->num_passed() : m_have_all ? m_torrent_file->num_pieces() : 0; } // when we get a have message, this is called for that piece void peer_has(int index, peer_connection const* peer); // when we get a bitfield message, this is called for that piece void peer_has(bitfield const& bits, peer_connection const* peer); void peer_has_all(peer_connection const* peer); void peer_lost(int index, peer_connection const* peer); void peer_lost(bitfield const& bits, peer_connection const* peer); int block_size() const { TORRENT_ASSERT(m_block_size_shift > 0); return 1 << m_block_size_shift; } peer_request to_req(piece_block const& p) const; void disconnect_all(error_code const& ec, operation_t op); int disconnect_peers(int num, error_code const& ec); // called every time a block is marked as finished in the // piece picker. We might have completed the torrent and // we can delete the piece picker void maybe_done_flushing(); // 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(); #if TORRENT_USE_I2P void on_i2p_resolve(error_code const& ec, char const* dest); bool is_i2p() const { return m_torrent_file && m_torrent_file->is_i2p(); } #endif // this is the asio callback that is called when a name // lookup for a PEER is completed. void on_peer_name_lookup(error_code const& e , std::vector
const& addrs , int port); // this is the asio callback that is called when a name // lookup for a WEB SEED is completed. void on_name_lookup(error_code const& e , std::vector
const& addrs , int port , std::list::iterator web); void connect_web_seed(std::list::iterator web, tcp::endpoint a); // this is the asio callback that is called when a name // lookup for a proxy for a web seed is completed. void on_proxy_name_lookup(error_code const& e , std::vector
const& addrs , std::list::iterator web, int port); // re-evaluates whether this torrent should be considered inactive or not void on_inactivity_tick(error_code const& ec); // calculate the instantaneous inactive state (the externally facing // inactive state is not instantaneous, but low-pass filtered) bool is_inactive_internal() const; // remove a web seed, or schedule it for removal in case there // are outstanding operations on it void remove_web_seed(std::list::iterator web); // 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(); // This is the opposite of finished. It is called if we used // to be finished but enabled some files for download so that // we wasn't finished anymore. void resume_download(); void verify_piece(int piece); void on_piece_verified(disk_io_job const* j); // this is called whenever a peer in this swarm becomes interesting // it is responsible for issuing a block request, if appropriate void peer_is_interesting(peer_connection& c); // piece_passed is called when a piece passes the hash check // 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 piece_passed(int index); // piece_failed is called when a piece fails the hash check void piece_failed(int index); // this is the handler for hash failure piece synchronization // i.e. resetting the piece void on_piece_sync(disk_io_job const* j); // this is the handler for write failure piece synchronization void on_piece_fail_sync(disk_io_job const* j, piece_block b); enum wasted_reason_t { piece_timed_out, piece_cancelled, piece_unknown, piece_seed , piece_end_game, piece_closing , waste_reason_max }; void add_redundant_bytes(int b, wasted_reason_t reason); void add_failed_bytes(int b); // this is true if we have all the pieces, but not necessarily flushed them to disk bool is_seed() const; // this is true if we have all the pieces that we want // the pieces don't necessarily need to be flushed to disk bool is_finished() const; bool is_inactive() const; std::string save_path() const; alert_manager& alerts() const; piece_picker& picker() { TORRENT_ASSERT(m_picker.get()); return *m_picker; } piece_picker const& picker() const { TORRENT_ASSERT(m_picker.get()); return *m_picker; } void need_picker(); bool has_picker() const { return m_picker.get() != 0; } void update_max_failcount() { if (!m_peer_list) return; torrent_state st = get_peer_list_state(); m_peer_list->set_max_failcount(&st); } int num_known_peers() const { return m_peer_list ? m_peer_list->num_peers() : 0; } int num_connect_candidates() const { return m_peer_list ? m_peer_list->num_connect_candidates() : 0; } piece_manager& storage(); bool has_storage() const { return m_storage.get() != NULL; } torrent_info const& torrent_file() const { return *m_torrent_file; } boost::shared_ptr get_torrent_copy(); std::string const& uuid() const { return m_uuid; } void set_uuid(std::string const& s) { m_uuid = s; } std::string const& url() const { return m_url; } void set_url(std::string const& s) { m_url = s; } std::string const& source_feed_url() const { return m_source_feed_url; } void set_source_feed_url(std::string const& s) { m_source_feed_url = s; } std::vector const& trackers() const { return m_trackers; } void replace_trackers(std::vector const& urls); // returns true if the tracker was added, and false if it was already // in the tracker list (in which case the source was added to the // entry in the list) bool add_tracker(announce_entry const& url); torrent_handle get_handle(); void write_resume_data(entry& rd) const; void read_resume_data(bdecode_node const& rd); void seen_complete() { m_last_seen_complete = time(0); } int time_since_complete() const { return int(time(0) - m_last_seen_complete); } time_t last_seen_complete() const { return m_last_seen_complete; } // LOGGING #ifndef TORRENT_DISABLE_LOGGING virtual void debug_log(const char* fmt, ...) const TORRENT_OVERRIDE TORRENT_FORMAT(2,3); void log_to_all_peers(char const* message); time_point m_dht_start_time; #endif // DEBUG #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif // -------------------------------------------- // RESOURCE MANAGEMENT // flags are defined in storage.hpp void move_storage(std::string const& save_path, int flags); // renames the file with the given index to the new name // the name may include a directory path // posts alert to indicate success or failure void rename_file(int index, std::string const& name); // 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_torrent_file->is_valid(); } bool are_files_checked() const { return m_files_checked; } bool valid_storage() const { return m_storage.get() != NULL; } // parses the info section from the given // bencoded tree and moves the torrent // to the checker thread for initial checking // of the storage. // a return value of false indicates an error bool set_metadata(char const* metadata_buf, int metadata_size); void on_torrent_download(error_code const& ec, http_parser const& parser , char const* data, int size); int sequence_number() const { return m_sequence_number; } bool seed_mode() const { return m_seed_mode; } void leave_seed_mode(bool skip_checking); bool all_verified() const { return int(m_num_verified) == m_torrent_file->num_pieces(); } bool verifying_piece(int piece) const { TORRENT_ASSERT(piece < int(m_verifying.size())); TORRENT_ASSERT(piece >= 0); return m_verifying.get_bit(piece); } void verifying(int piece) { TORRENT_ASSERT(piece < int(m_verifying.size())); TORRENT_ASSERT(piece >= 0); TORRENT_ASSERT(m_verifying.get_bit(piece) == false); m_verifying.set_bit(piece); } bool verified_piece(int piece) const { TORRENT_ASSERT(piece < int(m_verified.size())); TORRENT_ASSERT(piece >= 0); return m_verified.get_bit(piece); } void verified(int piece); bool add_merkle_nodes(std::map const& n, int piece); // this is called once periodically for torrents // that are not private void lsd_announce(); void update_last_upload() { m_last_upload = static_cast(total_seconds(clock_type::now().time_since_epoch())); } void set_apply_ip_filter(bool b); bool apply_ip_filter() const { return m_apply_ip_filter; } std::vector const& predictive_pieces() const { return m_predictive_pieces; } // this is called whenever we predict to have this piece // within one second void predicted_have_piece(int index, int milliseconds); void clear_in_state_update() { TORRENT_ASSERT(m_links[aux::session_interface::torrent_state_updates].in_list()); m_links[aux::session_interface::torrent_state_updates].clear(); } void dec_refcount(char const* purpose); void inc_refcount(char const* purpose); int refcount() const { return m_refcount; } void inc_num_connecting(torrent_peer* pp) { ++m_num_connecting; TORRENT_ASSERT(m_num_connecting <= int(m_connections.size())); if (pp->seed) { ++m_num_connecting_seeds; TORRENT_ASSERT(m_num_connecting_seeds <= int(m_connections.size())); } } void dec_num_connecting(torrent_peer* pp) { TORRENT_ASSERT(m_num_connecting > 0); --m_num_connecting; if (pp->seed) { TORRENT_ASSERT(m_num_connecting_seeds > 0); --m_num_connecting_seeds; } TORRENT_ASSERT(m_num_connecting <= int(m_connections.size())); } bool is_ssl_torrent() const { return m_ssl_torrent; } #ifdef TORRENT_USE_OPENSSL void set_ssl_cert(std::string const& certificate , std::string const& private_key , std::string const& dh_params , std::string const& passphrase); void set_ssl_cert_buffer(std::string const& certificate , std::string const& private_key , std::string const& dh_params); boost::asio::ssl::context* ssl_ctx() const { return m_ssl_ctx.get(); } #endif int num_time_critical_pieces() const { return int(m_time_critical_pieces.size()); } private: void ip_filter_updated(); void inc_stats_counter(int c, int value = 1); // initialize the torrent_state structure passed to peer_list // member functions. Don't forget to also call peers_erased() // on the erased member after the peer_list call torrent_state get_peer_list_state(); void construct_storage(); void update_list(int list, bool in); void on_files_deleted(disk_io_job const* j); void on_torrent_paused(disk_io_job const* j); void on_storage_moved(disk_io_job const* j); void on_save_resume_data(disk_io_job const* j); void on_file_renamed(disk_io_job const* j); void on_cache_flushed(disk_io_job const* j, bool manually_triggered); // this is used when a torrent is being removed.It synchronizes with the // disk thread void on_torrent_aborted(); // upload and download rate limits for the torrent void set_limit_impl(int limit, int channel, bool state_update = true); int limit_impl(int channel) const; void refresh_explicit_cache_impl(disk_io_job const* j, int cache_size); int prioritize_tracker(int tracker_index); int deprioritize_tracker(int tracker_index); bool request_bandwidth_from_session(int channel) const; void update_peer_interest(bool was_finished); void prioritize_udp_trackers(); void update_tracker_timer(time_point now); static void on_tracker_announce_disp(boost::weak_ptr p , error_code const& e); void on_tracker_announce(); #ifndef TORRENT_DISABLE_DHT static void on_dht_announce_response_disp(boost::weak_ptr t , std::vector const& peers); void on_dht_announce_response(std::vector const& peers); bool should_announce_dht() const; #endif void remove_time_critical_piece(int piece, bool finished = false); void remove_time_critical_pieces(std::vector const& priority); void request_time_critical_pieces(); void need_peer_list(); boost::shared_ptr m_ip_filter; // all time totals of uploaded and downloaded payload // stored in resume data boost::int64_t m_total_uploaded; boost::int64_t m_total_downloaded; // if this pointer is 0, the torrent is in // a state where the metadata hasn't been // received yet, or during shutdown. // the piece_manager keeps the torrent object // alive by holding a shared_ptr to it and // the torrent keeps the piece manager alive // with this shared_ptr. This cycle is // broken when torrent::abort() is called // Then the torrent releases the piece_manager // and when the piece_manager is complete with all // outstanding disk io jobs (that keeps // the piece_manager alive) it will destruct // and release the torrent file. The reason for // this is that the torrent_info is used by // the piece_manager, and stored in the // torrent, so the torrent cannot destruct // before the piece_manager. boost::shared_ptr m_storage; #ifdef TORRENT_USE_OPENSSL boost::shared_ptr m_ssl_ctx; #if BOOST_VERSION >= 104700 bool verify_peer_cert(bool preverified, boost::asio::ssl::verify_context& ctx); #endif void init_ssl(std::string const& cert); #endif void setup_peer_class(); // The list of web seeds in this torrent. Seeds with fatal errors are // removed from the set. It's important that iteratores are not // invalidated as entries are added and removed from this list, hence the // std::list std::list m_web_seeds; #ifndef TORRENT_DISABLE_EXTENSIONS typedef std::list > extension_list_t; extension_list_t m_extensions; #endif // used for tracker announces deadline_timer m_tracker_timer; // used to detect when we are active or inactive for long enough // to trigger the auto-manage logic deadline_timer m_inactivity_timer; // 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; // ----------------------------- // this vector is allocated lazily. If no file priorities are // ever changed, this remains empty. Any unallocated slot // implicitly means the file has priority 1. // TODO: this wastes 5 bits per file std::vector m_file_priority; // this object is used to track download progress of individual files aux::file_progress m_file_progress; // these are the pieces we're currently // suggesting to peers. std::vector m_suggested_pieces; std::vector m_trackers; // this is an index into m_trackers // this list is sorted by time_critical_piece::deadline std::vector m_time_critical_pieces; std::string m_trackerid; #ifndef TORRENT_NO_DEPRECATE // deprecated in 1.1 std::string m_username; std::string m_password; #endif std::string m_save_path; // if we don't have the metadata, this is a url to // the torrent file std::string m_url; // if this was added from an RSS feed, this is the unique // identifier in the feed. std::string m_uuid; // if this torrent was added by an RSS feed, this is the // URL to that feed std::string m_source_feed_url; // this is used as temporary storage while downloading // the .torrent file from m_url // std::vector m_torrent_file_buf; // this is a list of all pieces that we have announced // as having, without actually having yet. If we receive // a request for a piece in this list, we need to hold off // on responding until we have completed the piece and // verified its hash. If the hash fails, send reject to // peers with outstanding requests, and dont_have to other // peers. This vector is ordered, to make lookups fast. std::vector m_predictive_pieces; // the performance counters of this session counters& m_stats_counters; // each bit represents a piece. a set bit means // the piece has had its hash verified. This // is only used in seed mode (when m_seed_mode // is true) // TODO: These two bitfields should probably be coalesced into one bitfield m_verified; // this means there is an outstanding, async, operation // to verify each piece that has a 1 bitfield m_verifying; // set if there's an error on this torrent error_code m_error; // used if there is any resume data boost::scoped_ptr m_resume_data; // if the torrent is started without metadata, it may // still be given a name until the metadata is received // once the metadata is received this field will no // longer be used and will be reset boost::scoped_ptr m_name; storage_constructor_type m_storage_constructor; // the posix time this torrent was added and when // it was completed. If the torrent isn't yet // completed, m_completed_time is 0 time_t m_added_time; time_t m_completed_time; // this was the last time _we_ saw a seed in this swarm time_t m_last_seen_complete; // this is the time last any of our peers saw a seed // in this swarm time_t m_swarm_last_seen_complete; // keep a copy if the info-hash here, so it can be accessed from multiple // threads, and be cheap to access from the client sha1_hash m_info_hash; public: // these are the lists this torrent belongs to. For more // details about each list, see session_impl.hpp. Each list // represents a group this torrent belongs to and makes it // efficient to enumerate only torrents belonging to a specific // group. Such as torrents that want peer connections or want // to be ticked etc. // TODO: 3 factor out the links (as well as update_list() to a separate // class that torrent can inherit) link m_links[aux::session_interface::num_torrent_lists]; private: // m_num_verified = m_verified.count() boost::uint32_t m_num_verified; // this timestamp is kept in session-time, to // make it fit in 16 bits boost::uint16_t m_last_saved_resume; // if this torrent is running, this was the time // when it was started. This is used to have a // bias towards keeping seeding torrents that // recently was started, to avoid oscillation // this is specified at a second granularity // in session-time. see session_impl for details. // the reference point is stepped forward every 4 // hours to keep the timestamps fit in 16 bits boost::uint16_t m_started; // if we're a seed, this is the session time // timestamp of when we became one boost::uint16_t m_became_seed; // if we're finished, this is the session time // timestamp of when we finished boost::uint16_t m_became_finished; // when checking, this is the first piece we have not // issued a hash job for int m_checking_piece; // the number of pieces we completed the check of int m_num_checked_pieces; // the number of async. operations that need this torrent // loaded in RAM. having a refcount > 0 prevents it from // being unloaded. int m_refcount; // if the error ocurred on a file, this is the index of that file // there are a few special cases, when this is negative. See // set_error() int m_error_file; // the average time it takes to download one time critical piece boost::uint32_t m_average_piece_time; // the average piece download time deviation boost::uint32_t m_piece_time_deviation; // the number of bytes that has been // downloaded that failed the hash-test boost::uint32_t m_total_failed_bytes; boost::uint64_t m_total_redundant_bytes; // the sequence number for this torrent, this is a // monotonically increasing number for each added torrent int m_sequence_number; // for torrents who have a bandwidth limit, this is != 0 // and refers to a peer_class in the session. peer_class_t m_peer_class; // of all peers in m_connections, this is the number // of peers that are outgoing and still waiting to // complete the connection. This is used to possibly // kick out these connections when we get incoming // connections (if we've reached the connection limit) boost::uint16_t m_num_connecting; // this is the peer id we generate when we add the torrent. Peers won't // use this (they generate their own peer ids) but this is used in case // the tracker returns peer IDs, to identify ourself in the peer list to // avoid connecting back to it. peer_id m_peer_id; // ============================== // The following members are specifically // ordered to make the 24 bit members // properly 32 bit aligned by inserting // 8 bits after each one // ============================== // the session time timestamp of when we entered upload mode // if we're currently in upload-mode boost::uint16_t m_upload_mode_time; // true when this torrent should anncounce to // trackers bool m_announce_to_trackers:1; // true when this torrent should anncounce to // the local network bool m_announce_to_lsd:1; // is set to true every time there is an incoming // connection to this torrent bool m_has_incoming:1; // this is set to true when the files are checked // before the files are checked, we don't try to // connect to peers bool m_files_checked:1; // determines the storage state for this torrent. unsigned int m_storage_mode:2; // this is true while tracker announcing is enabled // is is disabled while paused and checking files bool m_announcing:1; // this is > 0 while the tracker deadline timer // is in use. i.e. one or more trackers are waiting // for a reannounce boost::int8_t m_waiting_tracker; // ---- // total time we've been active on this torrent. i.e. either (trying to) // download or seed. does not count time when the torrent is stopped or // paused. specified in seconds. This only track time _before_ we started // the torrent this last time. When the torrent is paused, this counter is // incremented to include this current session. unsigned int m_active_time:24; // the index to the last tracker that worked boost::int8_t m_last_working_tracker; // ---- // total time we've been finished with this torrent. // does not count when the torrent is stopped or paused. unsigned int m_finished_time:24; // in case the piece picker hasn't been constructed // when this settings is set, this variable will keep // its value until the piece picker is created bool m_sequential_download:1; // this is set if the auto_sequential setting is true and this swarm // satisfies the criteria to be considered high-availability. i.e. if // there's mostly seeds in the swarm, download the files sequentially // for improved disk I/O performance. bool m_auto_sequential:1; // this means we haven't verified the file content // of the files we're seeding. the m_verified bitfield // indicates which pieces have been verified and which // haven't bool m_seed_mode:1; // if this is true, we're currently super seeding this // torrent. bool m_super_seeding:1; // this is set when we don't want to load seed_mode, // paused or auto_managed from the resume data const bool m_override_resume_data:1; #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES // this is true while there is a country // resolution in progress. To avoid flodding // the DNS request queue, only one ip is resolved // at a time. mutable bool m_resolving_country:1; // this is true if the user has enabled // country resolution in this torrent bool m_resolve_countries:1; #endif #endif // set to false when saving resume data. Set to true // whenever something is downloaded bool m_need_save_resume_data:1; // ---- // total time we've been available as a seed on this torrent. // does not count when the torrent is stopped or paused. This value only // accounts for the time prior to the current start of the torrent. When // the torrent is paused, this counter is incremented to account for the // additional seeding time. unsigned int m_seeding_time:24; // ---- // the maximum number of uploads for this torrent unsigned int m_max_uploads:24; // these are the flags sent in on a call to save_resume_data // we need to save them to check them in write_resume_data boost::uint8_t m_save_resume_flags; // ---- // the number of unchoked peers in this torrent unsigned int m_num_uploads:24; // when this is set, second_tick will perform the actual // work of refreshing the suggest pieces bool m_need_suggest_pieces_refresh:1; // rotating sequence number for LSD announces sent out. // used to only use IP broadcast for every 8th lsd announce boost::uint8_t m_lsd_seq:3; // this is set to true if the torrent was started without // metadata. It is used to save metadata in the resume file // by default for such torrents. It does not necessarily // have to be a magnet link. bool m_magnet_link:1; // set to true if the session IP filter applies to this // torrent or not. Defaults to true. bool m_apply_ip_filter:1; // if set to true, add tracker URLs loaded from resume // data into this torrent instead of replacing them bool m_merge_resume_trackers:1; // ---- // the number of bytes of padding files boost::uint32_t m_padding:24; // this is set to the connect boost quota for this torrent. // After having received this many priority peer connection attempts, it // falls back onto the steady state peer connection logic, driven by the // session tick. Each tracker response, as long as this is non-zero, will // attempt to connect to peers immediately and decrement the counter. // We give torrents a connect boost when they are first added and then // every time they resume from being paused. boost::uint8_t m_connect_boost_counter; // ---- // the scrape data from the tracker response, this // is optional and may be 0xffffff boost::uint32_t m_incomplete:24; // true when the torrent should announce to // the DHT bool m_announce_to_dht:1; // even if we're not built to support SSL torrents, // remember that this is an SSL torrent, so that we don't // accidentally start seeding it without any authentication. bool m_ssl_torrent:1; // this is set to true if we're trying to delete the // files belonging to it. When set, don't write any // more blocks to disk! bool m_deleted:1; // pinned torrents are locked in RAM and won't be unloaded // in favor of more active torrents. When the torrent is added, // the user may choose to initialize this to 1, in which case // it will never be unloaded from RAM bool m_pinned:1; // when this is false, we should unload the torrent as soon // as the no other async. job needs the torrent loaded bool m_should_be_loaded:1; // ---- // the timestamp of the last piece passed for this torrent specified in // seconds since epoch. boost::uint32_t m_last_download; // the number of peer connections to seeds. This should be the same as // counting the peer connections that say true for is_seed() boost::uint16_t m_num_seeds; // this is the number of peers that are seeds, and count against // m_num_seeds, but have not yet been connected boost::uint16_t m_num_connecting_seeds; // the timestamp of the last byte uploaded from this torrent specified in // seconds since epoch. boost::uint32_t m_last_upload; // this is a second count-down to when we should tick the // storage for this torrent. Ticking the storage is used // to periodically flush the partfile metadata and possibly // other deferred flushing. Any disk operation starts this // counter (unless it's already counting down). 0 means no // ticking is needed. boost::uint8_t m_storage_tick; // ---- // if this is true, libtorrent may pause and resume // this torrent depending on queuing rules. Torrents // started with auto_managed flag set may be added in // a paused state in case there are no available // slots. bool m_auto_managed:1; enum { no_gauge_state = 0xf }; // the current stats gauge this torrent counts against boost::uint32_t m_current_gauge_state:4; // set to true while moving the storage bool m_moving_storage:1; // this is true if this torrent is considered inactive from the // queuing mechanism's point of view. If a torrent doesn't transfer // at high enough rates, it's inactive. bool m_inactive:1; // ---- // the scrape data from the tracker response, this // is optional and may be 0xffffff unsigned int m_downloaded:24; // the timestamp of the last scrape request to one of the trackers in // this torrent specified in session_time. This is signed because it must // be able to represent time before the session started boost::uint32_t m_last_scrape; // ---- // progress parts per million (the number of // millionths of completeness) unsigned int m_progress_ppm:20; // this is true when our effective inactive state is different from our // actual inactive state. Whenever this state changes, there is a // quarantine period until we change the effective state. This is to avoid // flapping. If the state changes back during this period, we cancel the // quarantine bool m_pending_active_change:1; // if this is set, accept the save path saved in the resume data, if // present bool m_use_resume_save_path:1; // if set to true, add web seed URLs loaded from resume // data into this torrent instead of replacing the ones from the .torrent // file bool m_merge_resume_http_seeds:1; // if this is set, whenever transitioning into a downloading/seeding state // from a non-downloading/seeding state, the torrent is paused. bool m_stop_when_ready:1; #if TORRENT_USE_ASSERTS public: // set to false until we've loaded resume data bool m_resume_data_loaded; // set to true when torrent is start()ed. It may only be started once bool m_was_started; // this is set to true while we're looping over m_connections. We may not // mutate the list while doing this mutable int m_iterating_connections; #endif }; struct torrent_ref_holder { torrent_ref_holder(torrent* t, char const* p) : m_torrent(t) , m_purpose(p) { if (m_torrent) m_torrent->inc_refcount(m_purpose); } ~torrent_ref_holder() { if (m_torrent) m_torrent->dec_refcount(m_purpose); } torrent* m_torrent; char const* m_purpose; }; } #endif // TORRENT_TORRENT_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/torrent_handle.hpp000066400000000000000000001626701351156116000253070ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #ifndef TORRENT_NO_DEPRECATE // for deprecated force_reannounce #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/address.hpp" #include "libtorrent/socket.hpp" // tcp::endpoint namespace libtorrent { namespace aux { struct session_impl; } class entry; struct pool_file_status; struct announce_entry; class torrent_info; struct torrent_plugin; struct peer_info; struct peer_list_entry; struct torrent_status; struct torrent_handle; class sha1_hash; struct storage_interface; class torrent; // allows torrent_handle to be used in unordered_map and unordered_set. TORRENT_EXPORT std::size_t hash_value(torrent_status const& ts); #ifndef BOOST_NO_EXCEPTIONS void throw_invalid_handle() TORRENT_NO_RETURN; #endif using boost::shared_ptr; using boost::make_shared; // holds the state of a block in a piece. Who we requested // it from and how far along we are at downloading it. struct TORRENT_EXPORT block_info { // this is the enum used for the block_info::state field. enum block_state_t { // This block has not been downloaded or requested form any peer. none, // The block has been requested, but not completely downloaded yet. requested, // The block has been downloaded and is currently queued for being // written to disk. writing, // The block has been written to disk. finished }; private: TORRENT_UNION addr_t { address_v4::bytes_type v4; #if TORRENT_USE_IPV6 address_v6::bytes_type v6; #endif } addr; boost::uint16_t port; public: // The peer is the ip address of the peer this block was downloaded from. void set_peer(tcp::endpoint const& ep) { #if TORRENT_USE_IPV6 is_v6_addr = ep.address().is_v6(); if (is_v6_addr) addr.v6 = ep.address().to_v6().to_bytes(); else #endif addr.v4 = ep.address().to_v4().to_bytes(); port = ep.port(); } tcp::endpoint peer() const { #if TORRENT_USE_IPV6 if (is_v6_addr) return tcp::endpoint(address_v6(addr.v6), port); else #endif return tcp::endpoint(address_v4(addr.v4), port); } // the number of bytes that have been received for this block unsigned bytes_progress:15; // the total number of bytes in this block. unsigned block_size:15; // the state this block is in (see block_state_t) unsigned state:2; // the number of peers that is currently requesting this block. Typically // this is 0 or 1, but at the end of the torrent blocks may be requested // by more peers in parallel to speed things up. unsigned num_peers:14; private: #if TORRENT_USE_IPV6 // the type of the addr union unsigned is_v6_addr:1; #endif }; // This class holds information about pieces that have outstanding requests // or outstanding writes struct TORRENT_EXPORT partial_piece_info { // the index of the piece in question. ``blocks_in_piece`` is the number // of blocks in this particular piece. This number will be the same for // most pieces, but // the last piece may have fewer blocks than the standard pieces. int piece_index; // the number of blocks in this piece int blocks_in_piece; // the number of blocks that are in the finished state int finished; // the number of blocks that are in the writing state int writing; // the number of blocks that are in the requested state int requested; // this is an array of ``blocks_in_piece`` number of // items. One for each block in the piece. // // .. warning:: This is a pointer that points to an array // that's owned by the session object. The next time // get_download_queue() is called, it will be invalidated. block_info* blocks; #ifndef TORRENT_NO_DEPRECATE // the speed classes. These may be used by the piece picker to // coalesce requests of similar download rates enum state_t { none, slow, medium, fast }; // the download speed class this piece falls into. // this is used internally to cluster peers of the same // speed class together when requesting blocks. // // set to either ``fast``, ``medium``, ``slow`` or ``none``. It tells // which download rate category the peers downloading this piece falls // into. ``none`` means that no peer is currently downloading any part of // the piece. Peers prefer picking pieces from the same category as // themselves. The reason for this is to keep the number of partially // downloaded pieces down. Pieces set to ``none`` can be converted into // any of ``fast``, ``medium`` or ``slow`` as soon as a peer want to // download from it. state_t TORRENT_DEPRECATED_MEMBER piece_state; #else // hidden enum deprecated_state_t { none, slow, medium, fast }; deprecated_state_t deprecated_piece_state; #endif }; // for boost::hash (and to support using this type in unordered_map etc.) TORRENT_EXPORT std::size_t hash_value(torrent_handle const& h); // You will usually have to store your torrent handles somewhere, since it's // the object through which you retrieve information about the torrent and // aborts the torrent. // // .. warning:: // Any member function that returns a value or fills in a value has to be // made synchronously. This means it has to wait for the main thread to // complete the query before it can return. This might potentially be // expensive if done from within a GUI thread that needs to stay // responsive. Try to avoid querying for information you don't need, and // try to do it in as few calls as possible. You can get most of the // interesting information about a torrent from the // torrent_handle::status() call. // // The default constructor will initialize the handle to an invalid state. // Which means you cannot perform any operation on it, unless you first // assign it a valid handle. If you try to perform any operation on an // uninitialized handle, it will throw ``invalid_handle``. // // .. warning:: // All operations on a torrent_handle may throw libtorrent_exception // exception, in case the handle is no longer referring to a torrent. // There is one exception is_valid() will never throw. Since the torrents // are processed by a background thread, there is no guarantee that a // handle will remain valid between two calls. // struct TORRENT_EXPORT torrent_handle { // TODO: 3 consider replacing all the setters and getters for pause, // resume, stop-when-ready, share-mode, upload-mode, super-seeding, // apply-ip-filter, resolve-countries, pinned, sequential-download, // seed-mode // with just set_flags() and clear_flags() using the flags from // add_torrent_params. Perhaps those flags should have a more generic // name. friend class invariant_access; friend struct aux::session_impl; friend class session; friend struct session_handle; friend struct feed; friend class torrent; friend TORRENT_EXPORT std::size_t hash_value(torrent_handle const& th); // constructs a torrent handle that does not refer to a torrent. // i.e. is_valid() will return false. torrent_handle() {} torrent_handle(torrent_handle const& t) { if (!t.m_torrent.expired()) m_torrent = t.m_torrent; } #if __cplusplus >= 201103L torrent_handle& operator=(torrent_handle const&) = default; #endif // flags for add_piece(). enum flags_t { overwrite_existing = 1 }; // This function will write ``data`` to the storage as piece ``piece``, // as if it had been downloaded from a peer. ``data`` is expected to // point to a buffer of as many bytes as the size of the specified piece. // The data in the buffer is copied and passed on to the disk IO thread // to be written at a later point. // // By default, data that's already been downloaded is not overwritten by // this buffer. If you trust this data to be correct (and pass the piece // hash check) you may pass the overwrite_existing flag. This will // instruct libtorrent to overwrite any data that may already have been // downloaded with this data. // // Since the data is written asynchronously, you may know that is passed // or failed the hash check by waiting for piece_finished_alert or // hash_failed_alert. void add_piece(int piece, char const* data, int flags = 0) const; // This function starts an asynchronous read operation of the specified // piece from this torrent. You must have completed the download of the // specified piece before calling this function. // // When the read operation is completed, it is passed back through an // alert, read_piece_alert. Since this alert is a response to an explicit // call, it will always be posted, regardless of the alert mask. // // Note that if you read multiple pieces, the read operations are not // guaranteed to finish in the same order as you initiated them. void read_piece(int piece) const; // Returns true if this piece has been completely downloaded, and false // otherwise. bool have_piece(int piece) const; // internal void get_full_peer_list(std::vector& v) const; // takes a reference to a vector that will be cleared and filled with one // entry for each peer connected to this torrent, given the handle is // valid. If the torrent_handle is invalid, it will throw // libtorrent_exception exception. Each entry in the vector contains // information about that particular peer. See peer_info. void get_peer_info(std::vector& v) const; // flags to pass in to status() to specify which properties of the // torrent to query for. By default all flags are set. enum status_flags_t { // calculates ``distributed_copies``, ``distributed_full_copies`` and // ``distributed_fraction``. query_distributed_copies = 1, // includes partial downloaded blocks in ``total_done`` and // ``total_wanted_done``. query_accurate_download_counters = 2, // includes ``last_seen_complete``. query_last_seen_complete = 4, // includes ``pieces``. query_pieces = 8, // includes ``verified_pieces`` (only applies to torrents in *seed // mode*). query_verified_pieces = 16, // includes ``torrent_file``, which is all the static information from // the .torrent file. query_torrent_file = 32, // includes ``name``, the name of the torrent. This is either derived // from the .torrent file, or from the ``&dn=`` magnet link argument // or possibly some other source. If the name of the torrent is not // known, this is an empty string. query_name = 64, // includes ``save_path``, the path to the directory the files of the // torrent are saved to. query_save_path = 128 }; // ``status()`` will return a structure with information about the status // of this torrent. If the torrent_handle is invalid, it will throw // libtorrent_exception exception. See torrent_status. The ``flags`` // argument filters what information is returned in the torrent_status. // Some information in there is relatively expensive to calculate, and if // you're not interested in it (and see performance issues), you can // filter them out. // // By default everything is included. The flags you can use to decide // what to *include* are defined in the status_flags_t enum. torrent_status status(boost::uint32_t flags = 0xffffffff) const; // ``get_download_queue()`` takes a non-const reference to a vector which // it will fill with information about pieces that are partially // downloaded or not downloaded at all but partially requested. See // partial_piece_info for the fields in the returned vector. void get_download_queue(std::vector& queue) const; // flags for set_piece_deadline(). enum deadline_flags { alert_when_available = 1 }; // This function sets or resets the deadline associated with a specific // piece index (``index``). libtorrent will attempt to download this // entire piece before the deadline expires. This is not necessarily // possible, but pieces with a more recent deadline will always be // prioritized over pieces with a deadline further ahead in time. The // deadline (and flags) of a piece can be changed by calling this // function again. // // The ``flags`` parameter can be used to ask libtorrent to post an alert // once the piece has been downloaded, by passing alert_when_available. // When set, the read_piece_alert alert will be delivered, with the piece // data, when it's downloaded. // // If the piece is already downloaded when this call is made, nothing // happens, unless the alert_when_available flag is set, in which case it // will have the same effect as calling read_piece() for ``index``. // // ``deadline`` is the number of milliseconds until this piece should be // completed. // // ``reset_piece_deadline`` removes the deadline from the piece. If it // hasn't already been downloaded, it will no longer be considered a // priority. // // ``clear_piece_deadlines()`` removes deadlines on all pieces in // the torrent. As if reset_piece_deadline() was called on all pieces. void set_piece_deadline(int index, int deadline, int flags = 0) const; void reset_piece_deadline(int index) const; void clear_piece_deadlines() const; // This sets the bandwidth priority of this torrent. The priority of a // torrent determines how much bandwidth its peers are assigned when // distributing upload and download rate quotas. A high number gives more // bandwidth. The priority must be within the range [0, 255]. // // The default priority is 0, which is the lowest priority. // // To query the priority of a torrent, use the // ``torrent_handle::status()`` call. // // Torrents with higher priority will not necessarily get as much // bandwidth as they can consume, even if there's is more quota. Other // peers will still be weighed in when bandwidth is being distributed. // With other words, bandwidth is not distributed strictly in order of // priority, but the priority is used as a weight. // // Peers whose Torrent has a higher priority will take precedence when // distributing unchoke slots. This is a strict prioritisation where // every interested peer on a high priority torrent will be unchoked // before any other, lower priority, torrents have any peers unchoked. void set_priority(int prio) const; #ifndef TORRENT_NO_DEPRECATE #if !TORRENT_NO_FPU // 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. TORRENT_DEPRECATED void file_progress(std::vector& progress) const; #endif #endif // flags to be passed in file_progress(). enum file_progress_flags_t { // only calculate file progress at piece granularity. This makes // the file_progress() call cheaper and also only takes bytes that // have passed the hash check into account, so progress cannot // regress in this mode. piece_granularity = 1 }; // This function fills in the supplied vector with the the number of // bytes downloaded of each file in this torrent. The progress values are // ordered the same as the files in the torrent_info. This operation is // not very cheap. Its complexity is *O(n + mj)*. Where *n* is the number // of files, *m* is the number of downloading pieces and *j* is the // number of blocks in a piece. // // The ``flags`` parameter can be used to specify the granularity of the // file progress. If left at the default value of 0, the progress will be // as accurate as possible, but also more expensive to calculate. If // ``torrent_handle::piece_granularity`` is specified, the progress will // be specified in piece granularity. i.e. only pieces that have been // fully downloaded and passed the hash check count. When specifying // piece granularity, the operation is a lot cheaper, since libtorrent // already keeps track of this internally and no calculation is required. void file_progress(std::vector& progress, int flags = 0) const; // This function fills in the passed in vector with status about files // that are open for this torrent. Any file that is not open in this // torrent, will not be reported in the vector, i.e. it's possible that // the vector is empty when returning, if none of the files in the // torrent are currently open. // // see pool_file_status. void file_status(std::vector& status) const; // If the torrent is in an error state (i.e. ``torrent_status::error`` is // non-empty), this will clear the error and start the torrent again. void clear_error() const; // ``trackers()`` will return the list of trackers for this torrent. The // announce entry contains both a string ``url`` which specify the // announce url for the tracker as well as an int ``tier``, which is // specifies the order in which this tracker is tried. If you want // libtorrent to use another list of trackers for this torrent, you can // use ``replace_trackers()`` which takes a list of the same form as the // one returned from ``trackers()`` and will replace it. If you want an // immediate effect, you have to call force_reannounce(). See // announce_entry. // // ``add_tracker()`` will look if the specified tracker is already in the // set. If it is, it doesn't do anything. If it's not in the current set // of trackers, it will insert it in the tier specified in the // announce_entry. // // The updated set of trackers will be saved in the resume data, and when // a torrent is started with resume data, the trackers from the resume // data will replace the original ones. std::vector trackers() const; void replace_trackers(std::vector const&) const; void add_tracker(announce_entry const&) const; // TODO: 3 unify url_seed and http_seed with just web_seed, using the // web_seed_entry. // ``add_url_seed()`` adds another url to the torrent's list of url // seeds. If the given url already exists in that list, the call has no // effect. The torrent will connect to the server and try to download // pieces from it, unless it's paused, queued, checking or seeding. // ``remove_url_seed()`` removes the given url if it exists already. // ``url_seeds()`` return a set of the url seeds currently in this // torrent. Note that URLs that fails may be removed automatically from // the list. // // See http-seeding_ for more information. void add_url_seed(std::string const& url) const; void remove_url_seed(std::string const& url) const; std::set url_seeds() const; // These functions are identical as the ``*_url_seed()`` variants, but // they operate on `BEP 17`_ web seeds instead of `BEP 19`_. // // See http-seeding_ for more information. void add_http_seed(std::string const& url) const; void remove_http_seed(std::string const& url) const; std::set http_seeds() const; // add the specified extension to this torrent. The ``ext`` argument is // a function that will be called from within libtorrent's context // passing in the internal torrent object and the specified userdata // pointer. The function is expected to return a shared pointer to // a torrent_plugin instance. void add_extension( boost::function(torrent_handle const&, void*)> const& ext , void* userdata = 0); // ``set_metadata`` expects the *info* section of metadata. i.e. The // buffer passed in will be hashed and verified against the info-hash. If // it fails, a ``metadata_failed_alert`` will be generated. If it passes, // a ``metadata_received_alert`` is generated. The function returns true // if the metadata is successfully set on the torrent, and false // otherwise. If the torrent already has metadata, this function will not // affect the torrent, and false will be returned. bool set_metadata(char const* metadata, int size) const; // Returns true if this handle refers to a valid torrent and false if it // hasn't been initialized or if the torrent it refers to has been // aborted. Note that a handle may become invalid after it has been added // to the session. Usually this is because the storage for the torrent is // somehow invalid or if the filenames are not allowed (and hence cannot // be opened/created) on your filesystem. If such an error occurs, a // file_error_alert is generated and all handles that refers to that // torrent will become invalid. bool is_valid() const; // flags for torrent_session::pause() enum pause_flags_t { graceful_pause = 1 }; // ``pause()``, and ``resume()`` will disconnect all peers and reconnect // all peers respectively. When a torrent is paused, it will however // remember all share ratios to all peers and remember all potential (not // connected) peers. Torrents may be paused automatically if there is a // file error (e.g. disk full) or something similar. See // file_error_alert. // // To know if a torrent is paused or not, call // ``torrent_handle::status()`` and inspect ``torrent_status::paused``. // // The ``flags`` argument to pause can be set to // ``torrent_handle::graceful_pause`` which will delay the disconnect of // peers that we're still downloading outstanding requests from. The // torrent will not accept any more requests and will disconnect all idle // peers. As soon as a peer is done transferring the blocks that were // requested from it, it is disconnected. This is a graceful shut down of // the torrent in the sense that no downloaded bytes are wasted. // // .. note:: // Torrents that are auto-managed may be automatically resumed again. It // does not make sense to pause an auto-managed torrent without making it // not auto-managed first. Torrents are auto-managed by default when added // to the session. For more information, see queuing_. // void pause(int flags = 0) const; void resume() const; // set or clear the stop-when-ready flag. When this flag is set, the // torrent will *force stop* whenever it transitions from a // non-data-transferring state into a data-transferring state (referred to // as being ready to download or seed). This is useful for torrents that // should not start downloading or seeding yet, but want to be made ready // to do so. A torrent may need to have its files checked for instance, so // it needs to be started and possibly queued for checking (auto-managed // and started) but as soon as it's done, it should be stopped. // // *Force stopped* means auto-managed is set to false and it's paused. As // if auto_manage(false) and pause() were called on the torrent. // // Note that the torrent may transition into a downloading state while // calling this function, and since the logic is edge triggered you may // miss the edge. To avoid this race, if the torrent already is in a // downloading state when this call is made, it will trigger the // stop-when-ready immediately. // // When the stop-when-ready logic fires, the flag is cleared. Any // subsequent transitions between downloading and non-downloading states // will not be affected, until this function is used to set it again. // // The behavior is more robust when setting this flag as part of adding // the torrent. See add_torrent_params. // // The stop-when-ready flag fixes the inherent race condition of waiting // for the state_changed_alert and then call pause(). The download/seeding // will most likely start in between posting the alert and receiving the // call to pause. // // A downloading state is one where peers are being connected. Which means // just downloading the metadata via the ``ut_metadata`` extension counts // as a downloading state. In order to stop a torrent once the metadata // has been downloaded, instead set all file priorities to dont_download void stop_when_ready(bool b) const; // Explicitly sets the upload mode of the torrent. In upload mode, the // torrent will not request any pieces. If the torrent is auto managed, // it will automatically be taken out of upload mode periodically (see // ``settings_pack::optimistic_disk_retry``). Torrents are // automatically put in upload mode whenever they encounter a disk write // error. // // ``m`` should be true to enter upload mode, and false to leave it. // // To test if a torrent is in upload mode, call // ``torrent_handle::status()`` and inspect // ``torrent_status::upload_mode``. void set_upload_mode(bool b) const; // Enable or disable share mode for this torrent. When in share mode, the // torrent will not necessarily be downloaded, especially not the whole // of it. Only parts that are likely to be distributed to more than 2 // other peers are downloaded, and only if the previous prediction was // correct. void set_share_mode(bool b) const; // Instructs libtorrent to flush all the disk caches for this torrent and // close all file handles. This is done asynchronously and you will be // notified that it's complete through cache_flushed_alert. // // Note that by the time you get the alert, libtorrent may have cached // more data for the torrent, but you are guaranteed that whatever cached // data libtorrent had by the time you called // ``torrent_handle::flush_cache()`` has been written to disk. void flush_cache() const; // Set to true to apply the session global IP filter to this torrent // (which is the default). Set to false to make this torrent ignore the // IP filter. void apply_ip_filter(bool b) const; // ``force_recheck`` puts the torrent back in a state where it assumes to // have no resume data. All peers will be disconnected and the torrent // will stop announcing to the tracker. The torrent will be added to the // checking queue, and will be checked (all the files will be read and // compared to the piece hashes). Once the check is complete, the torrent // will start connecting to peers again, as normal. void force_recheck() const; // flags used in the save_resume_data call to control additional // actions or fields to save. enum save_resume_flags_t { // the disk cache will be flushed before creating the resume data. // This avoids a problem with file timestamps in the resume data in // case the cache hasn't been flushed yet. flush_disk_cache = 1, // the resume data will contain the metadata from the torrent file as // well. This is default for any torrent that's added without a // torrent file (such as a magnet link or a URL). save_info_dict = 2, // if nothing significant has changed in the torrent since the last // time resume data was saved, fail this attempt. Significant changes // primarily include more data having been downloaded, file or piece // priorities having changed etc. If the resume data doesn't need // saving, a save_resume_data_failed_alert is posted with the error // resume_data_not_modified. only_if_modified = 4 }; // ``save_resume_data()`` asks libtorrent to generate fast-resume data for // this torrent. // // The ``flags`` argument is a bitmask of flags ORed together. see // save_resume_flags_t // // This operation is asynchronous, ``save_resume_data`` will return // immediately. The resume data is delivered when it's done through an // save_resume_data_alert. // // The fast resume data will be empty in the following cases: // // 1. The torrent handle is invalid. // 2. The torrent hasn't received valid metadata and was started without // metadata (see libtorrent's metadata-from-peers_ extension) // // Note that by the time you receive the fast resume data, it may already // be invalid if the torrent is still downloading! The recommended // practice is to first pause the session, then generate the fast resume // data, and then close it down. Make sure to not remove_torrent() before // you receive the save_resume_data_alert though. There's no need to // pause when saving intermittent resume data. // //.. warning:: // If you pause every torrent individually instead of pausing the // session, every torrent will have its paused state saved in the // resume data! // //.. warning:: // The resume data contains the modification timestamps for all files. // If one file has been modified when the torrent is added again, the // will be rechecked. When shutting down, make sure to flush the disk // cache before saving the resume data. This will make sure that the // file timestamps are up to date and won't be modified after saving // the resume data. The recommended way to do this is to pause the // torrent, which will flush the cache and disconnect all peers. // //.. note:: // It is typically a good idea to save resume data whenever a torrent // is completed or paused. In those cases you don't need to pause the // torrent or the session, since the torrent will do no more writing to // its files. If you save resume data for torrents when they are // paused, you can accelerate the shutdown process by not saving resume // data again for paused torrents. Completed torrents should have their // resume data saved when they complete and on exit, since their // statistics might be updated. // // In full allocation mode the resume data is never invalidated by // subsequent writes to the files, since pieces won't move around. This // means that you don't need to pause before writing resume data in full // or sparse mode. If you don't, however, any data written to disk after // you saved resume data and before the session closed is lost. // // It also means that if the resume data is out dated, libtorrent will // not re-check the files, but assume that it is fairly recent. The // assumption is that it's better to loose a little bit than to re-check // the entire file. // // It is still a good idea to save resume data periodically during // download as well as when closing down. // // Example code to pause and save resume data for all torrents and wait // for the alerts: // // .. code:: c++ // // extern int outstanding_resume_data; // global counter of outstanding resume data // std::vector handles = ses.get_torrents(); // ses.pause(); // for (torrent_handle const& h : handles) // { // if (!h.is_valid()) continue; // torrent_status s = h.status(); // if (!s.has_metadata) continue; // if (!s.need_save_resume_data()) continue; // // h.save_resume_data(); // ++outstanding_resume_data; // } // // while (outstanding_resume_data > 0) // { // alert const* a = ses.wait_for_alert(seconds(10)); // // // if we don't get an alert within 10 seconds, abort // if (a == nullptr) break; // // std::vector alerts; // ses.pop_alerts(&alerts); // // for (alert* i : alerts) // { // if (alert_cast(a)) // { // process_alert(a); // --outstanding_resume_data; // continue; // } // // save_resume_data_alert const* rd = alert_cast(a); // if (rd == nullptr) // { // process_alert(a); // continue; // } // // torrent_handle h = rd->handle; // torrent_status st = h.status(torrent_handle::query_save_path // | torrent_handle::query_name); // std::ofstream out((st.save_path // + "/" + st.name + ".fastresume").c_str() // , std::ios_base::binary); // out.unsetf(std::ios_base::skipws); // bencode(std::ostream_iterator(out), *rd->resume_data); // --outstanding_resume_data; // } // } // //.. note:: // Note how ``outstanding_resume_data`` is a global counter in this // example. This is deliberate, otherwise there is a race condition for // torrents that was just asked to save their resume data, they posted // the alert, but it has not been received yet. Those torrents would // report that they don't need to save resume data again, and skipped by // the initial loop, and thwart the counter otherwise. void save_resume_data(int flags = 0) const; // This function returns true if any whole chunk has been downloaded // since the torrent was first loaded or since the last time the resume // data was saved. When saving resume data periodically, it makes sense // to skip any torrent which hasn't downloaded anything since the last // time. // //.. note:: // A torrent's resume data is considered saved as soon as the // save_resume_data_alert is posted. It is important to make sure this // alert is received and handled in order for this function to be // meaningful. bool need_save_resume_data() const; // changes whether the torrent is auto managed or not. For more info, // see queuing_. void auto_managed(bool m) const; // Every torrent that is added is assigned a queue position exactly one // greater than the greatest queue position of all existing torrents. // Torrents that are being seeded have -1 as their queue position, since // they're no longer in line to be downloaded. // // When a torrent is removed or turns into a seed, all torrents with // greater queue positions have their positions decreased to fill in the // space in the sequence. // // ``queue_position()`` returns the torrent's position in the download // queue. The torrents with the smallest numbers are the ones that are // being downloaded. The smaller number, the closer the torrent is to the // front of the line to be started. // // The queue position is also available in the torrent_status. // // The ``queue_position_*()`` functions adjust the torrents position in // the queue. Up means closer to the front and down means closer to the // back of the queue. Top and bottom refers to the front and the back of // the queue respectively. int queue_position() const; void queue_position_up() const; void queue_position_down() const; void queue_position_top() const; void queue_position_bottom() const; // updates the position in the queue for this torrent. The relative order // of all other torrents remain intact but their numerical queue position // shifts to make space for this torrent's new position void queue_position_set(int p) const; #ifndef TORRENT_NO_DEPRECATE // deprecated in 1.1 // Sets or gets the flag that determines if countries should be resolved // for the peers of this torrent. It defaults to false. If it is set to // true, the peer_info structure for the peers in this torrent will have // their ``country`` member set. See peer_info for more information on // how to interpret this field. TORRENT_DEPRECATED void resolve_countries(bool r); TORRENT_DEPRECATED bool resolve_countries() const; #endif // For SSL torrents, use this to specify a path to a .pem file to use as // this client's certificate. The certificate must be signed by the // certificate in the .torrent file to be valid. // // The set_ssl_certificate_buffer() overload takes the actual certificate, // private key and DH params as strings, rather than paths to files. This // overload is only available when libtorrent is built against boost // 1.54 or later. // // ``cert`` is a path to the (signed) certificate in .pem format // corresponding to this torrent. // // ``private_key`` is a path to the private key for the specified // certificate. This must be in .pem format. // // ``dh_params`` is a path to the Diffie-Hellman parameter file, which // needs to be in .pem format. You can generate this file using the // openssl command like this: ``openssl dhparam -outform PEM -out // dhparams.pem 512``. // // ``passphrase`` may be specified if the private key is encrypted and // requires a passphrase to be decrypted. // // Note that when a torrent first starts up, and it needs a certificate, // it will suspend connecting to any peers until it has one. It's // typically desirable to resume the torrent after setting the SSL // certificate. // // If you receive a torrent_need_cert_alert, you need to call this to // provide a valid cert. If you don't have a cert you won't be allowed to // connect to any peers. void set_ssl_certificate(std::string const& certificate , std::string const& private_key , std::string const& dh_params , std::string const& passphrase = ""); void set_ssl_certificate_buffer(std::string const& certificate , std::string const& private_key , std::string const& dh_params); // Returns the storage implementation for this torrent. This depends on the // storage constructor function that was passed to add_torrent. storage_interface* get_storage_impl() const; // Returns a pointer to the torrent_info object associated with this // torrent. The torrent_info object may be a copy of the internal object. // If the torrent doesn't have metadata, the pointer will not be // initialized (i.e. a NULL pointer). The torrent may be in a state // without metadata only if it was started without a .torrent file, e.g. // by using the libtorrent extension of just supplying a tracker and // info-hash. shared_ptr torrent_file() const; #ifndef TORRENT_NO_DEPRECATE // ================ start deprecation ============ // deprecated in 1.0 // use status() instead (with query_save_path) TORRENT_DEPRECATED std::string save_path() const; // deprecated in 1.0 // use status() instead (with query_name) // returns the name of this torrent, in case it doesn't // have metadata it returns the name assigned to it // when it was added. TORRENT_DEPRECATED std::string name() const; // use torrent_file() instead TORRENT_DEPRECATED const torrent_info& get_torrent_info() const; // deprecated in 0.16, feature will be removed TORRENT_DEPRECATED int get_peer_upload_limit(tcp::endpoint ip) const; TORRENT_DEPRECATED int get_peer_download_limit(tcp::endpoint ip) const; TORRENT_DEPRECATED void set_peer_upload_limit(tcp::endpoint ip, int limit) const; TORRENT_DEPRECATED void set_peer_download_limit(tcp::endpoint ip, int limit) const; // deprecated in 0.16, feature will be removed TORRENT_DEPRECATED void set_ratio(float up_down_ratio) const; // deprecated in 0.16. use status() instead TORRENT_DEPRECATED bool is_seed() const; TORRENT_DEPRECATED bool is_finished() const; TORRENT_DEPRECATED bool is_paused() const; TORRENT_DEPRECATED bool is_auto_managed() const; TORRENT_DEPRECATED bool is_sequential_download() const; TORRENT_DEPRECATED bool has_metadata() const; TORRENT_DEPRECATED bool super_seeding() const; // deprecated in 0.13 // all these are deprecated, use piece // priority functions instead // marks the piece with the given index as filtered // it will not be downloaded TORRENT_DEPRECATED void filter_piece(int index, bool filter) const; TORRENT_DEPRECATED void filter_pieces(std::vector const& pieces) const; TORRENT_DEPRECATED bool is_piece_filtered(int index) const; TORRENT_DEPRECATED std::vector filtered_pieces() const; // marks the file with the given index as filtered // it will not be downloaded TORRENT_DEPRECATED void filter_files(std::vector const& files) const; // deprecated in 0.14 // use save_resume_data() instead. It is async. and // will return the resume data in an alert TORRENT_DEPRECATED entry write_resume_data() const; // ``use_interface()`` sets the network interface this torrent will use // when it opens outgoing connections. By default, it uses the same // interface as the session uses to listen on. The parameter must be a // string containing one or more, comma separated, ip-address (either an // IPv4 or IPv6 address). When specifying multiple interfaces, the // torrent will round-robin which interface to use for each outgoing // connection. This is useful for clients that are multi-homed. TORRENT_DEPRECATED void use_interface(const char* net_interface) const; // ================ end deprecation ============ #endif // Fills the specified ``std::vector`` with the availability for // each piece in this torrent. libtorrent does not keep track of // availability for seeds, so if the torrent is seeding the availability // for all pieces is reported as 0. // // The piece availability is the number of peers that we are connected // that has advertised having a particular piece. This is the information // that libtorrent uses in order to prefer picking rare pieces. void piece_availability(std::vector& avail) const; // These functions are used to set and get the priority of individual // pieces. By default all pieces have priority 4. That means that the // random rarest first algorithm is effectively active for all pieces. // You may however change the priority of individual pieces. There are 8 // priority levels. 0 means not to download the piece at all. Otherwise, // lower priority values means less likely to be picked. Piece priority // takes precedence over piece availability. Every piece with priority 7 // will be attempted to be picked before a priority 6 piece and so on. // // The default priority of pieces is 4. // // Piece priorities can not be changed for torrents that have not // downloaded the metadata yet. Magnet links won't have metadata // immediately. see the metadata_received_alert. // // ``piece_priority`` sets or gets the priority for an individual piece, // specified by ``index``. // // ``prioritize_pieces`` takes a vector of integers, one integer per // piece in the torrent. All the piece priorities will be updated with // the priorities in the vector. // The second overload of ``prioritize_pieces`` that takes a vector of pairs // will update the priorities of only select pieces, and leave all other // unaffected. Each pair is (piece, priority). That is, the first item is // the piece index and the second item is the priority of that piece. // Invalid entries, where the piece index or priority is out of range, are // not allowed. // // ``piece_priorities`` returns a vector with one element for each piece // in the torrent. Each element is the current priority of that piece. // // It's possible to cancel the effect of *file* priorities by setting the // priorities for the affected pieces. Care has to be taken when mixing // usage of file- and piece priorities. void piece_priority(int index, int priority) const; int piece_priority(int index) const; void prioritize_pieces(std::vector const& pieces) const; void prioritize_pieces(std::vector > const& pieces) const; std::vector piece_priorities() const; // ``index`` must be in the range [0, number_of_files). // // ``file_priority()`` queries or sets the priority of file ``index``. // // ``prioritize_files()`` takes a vector that has at as many elements as // there are files in the torrent. Each entry is the priority of that // file. The function sets the priorities of all the pieces in the // torrent based on the vector. // // ``file_priorities()`` returns a vector with the priorities of all // files. // // The priority values are the same as for piece_priority(). // // Whenever a file priority is changed, all other piece priorities are // reset to match the file priorities. In order to maintain special // priorities for particular pieces, piece_priority() has to be called // again for those pieces. // // You cannot set the file priorities on a torrent that does not yet have // metadata or a torrent that is a seed. ``file_priority(int, int)`` and // prioritize_files() are both no-ops for such torrents. // // Since changing file priorities may involve disk operations (of moving // files in- and out of the part file), the internal accounting of file // priorities happen asynchronously. i.e. setting file priorities and then // immediately querying them may not yield the same priorities just set. // However, the *piece* priorities are updated immediately. // // when combining file- and piece priorities, the resume file will record // both. When loading the resume data, the file priorities will be applied // first, then the piece priorities. void file_priority(int index, int priority) const; int file_priority(int index) const; void prioritize_files(std::vector const& files) const; std::vector file_priorities() const; // flags to be used with force_reannounce enum reannounce_flags_t { // by default, force-reannounce will still honor the min-interval // published by the tracker. If this flag is set, it will be ignored // and the tracker is announced immediately. ignore_min_interval = 1 }; // ``force_reannounce()`` will force this torrent to do another tracker // request, to receive new peers. The ``seconds`` argument specifies how // many seconds from now to issue the tracker announces. // // If the tracker's ``min_interval`` has not passed since the last // announce, the forced announce will be scheduled to happen immediately // as the ``min_interval`` expires. This is to honor trackers minimum // re-announce interval settings. // // The ``tracker_index`` argument specifies which tracker to re-announce. // If set to -1 (which is the default), all trackers are re-announce. // // The ``flags`` argument can be used to affect the re-announce. See // reannounce_flags_t. // // ``force_dht_announce`` will announce the torrent to the DHT // immediately. void force_reannounce(int seconds = 0, int tracker_index = -1) const; void force_reannounce(int seconds, int tracker_index, int flags) const; void force_dht_announce() const; #ifndef TORRENT_NO_DEPRECATE // 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. TORRENT_DEPRECATED void force_reannounce(boost::posix_time::time_duration) const; #endif // ``scrape_tracker()`` will send a scrape request to a tracker. By // default (``idx`` = -1) it will scrape the last working tracker. If // ``idx`` is >= 0, the tracker with the specified index will scraped. // // A scrape request queries the tracker for statistics such as total // number of incomplete peers, complete peers, number of downloads etc. // // This request will specifically update the ``num_complete`` and // ``num_incomplete`` fields in the torrent_status struct once it // completes. When it completes, it will generate a scrape_reply_alert. // If it fails, it will generate a scrape_failed_alert. void scrape_tracker(int idx = -1) const; // ``set_upload_limit`` will limit the upload bandwidth used by this // particular torrent to the limit you set. It is given as the number of // bytes per second the torrent is allowed to upload. // ``set_download_limit`` works the same way but for download bandwidth // instead of upload bandwidth. Note that setting a higher limit on a // torrent then the global limit // (``settings_pack::upload_rate_limit``) will not override the global // rate limit. The torrent can never upload more than the global rate // limit. // // ``upload_limit`` and ``download_limit`` will return the current limit // setting, for upload and download, respectively. // // Local peers are not rate limited by default. see peer-classes_. void set_upload_limit(int limit) const; int upload_limit() const; void set_download_limit(int limit) const; int download_limit() const; // A pinned torrent may not be unloaded by libtorrent. When the dynamic // loading and unloading of torrents is enabled (by setting a load // function on the session), this can be used to exempt certain torrents // from the unloading logic. // // Magnet links, and other torrents that start out without having // metadata are pinned automatically. This is to give the client a chance // to get the metadata and save it before it's unloaded. In this case, it // may be useful to unpin the torrent once its metadata has been saved // to disk. // // For more information about dynamically loading and unloading torrents, // see dynamic-loading-of-torrent-files_. // void set_pinned(bool p) const; // ``set_sequential_download()`` enables or disables *sequential // download*. When enabled, the piece picker will pick pieces in sequence // instead of rarest first. In this mode, piece priorities are ignored, // with the exception of priority 7, which are still preferred over the // sequential piece order. // // Enabling sequential download will affect the piece distribution // negatively in the swarm. It should be used sparingly. void set_sequential_download(bool sd) const; // ``connect_peer()`` is a way to manually connect to peers that one // believe is a part of the torrent. If the peer does not respond, or is // not a member of this torrent, it will simply be disconnected. No harm // can be done by using this other than an unnecessary connection attempt // is made. If the torrent is uninitialized or in queued or checking // mode, this will throw libtorrent_exception. The second (optional) // argument will be bitwise ORed into the source mask of this peer. // Typically this is one of the source flags in peer_info. i.e. // ``tracker``, ``pex``, ``dht`` etc. // // ``flags`` are the same flags that are passed along with the ``ut_pex`` extension. // // ==== ========================================== // 0x01 peer supports encryption. // // 0x02 peer is a seed // // 0x04 supports uTP. If this is not set, the peer will only be contacted // over TCP. // // 0x08 supports hole punching protocol. If this // flag is received from a peer, it can be // used as a rendezvous point in case direct // connections to the peer fail // ==== ========================================== void connect_peer(tcp::endpoint const& adr, int source = 0 , int flags = 0x1 + 0x4 + 0x8) const; // ``set_max_uploads()`` sets the maximum number of peers that's unchoked // at the same time on this torrent. If you set this to -1, there will be // no limit. This defaults to infinite. The primary setting controlling // this is the global unchoke slots limit, set by unchoke_slots_limit in // settings_pack. // // ``max_uploads()`` returns the current settings. void set_max_uploads(int max_uploads) const; int max_uploads() const; // ``set_max_connections()`` sets the maximum number of connection this // torrent will open. If all connections are used up, incoming // connections may be refused or poor connections may be closed. This // must be at least 2. The default is unlimited number of connections. If // -1 is given to the function, it means unlimited. There is also a // global limit of the number of connections, set by // ``connections_limit`` in settings_pack. // // ``max_connections()`` returns the current settings. void set_max_connections(int max_connections) const; int max_connections() const; #ifndef TORRENT_NO_DEPRECATE // sets a username and password that will be sent along in the HTTP-request // of the tracker announce. Set this if the tracker requires authorization. TORRENT_DEPRECATED void set_tracker_login(std::string const& name , std::string const& password) const; #endif // Moves the file(s) that this torrent are currently seeding from or // downloading to. If the given ``save_path`` is not located on the same // drive as the original save path, the files will be copied to the new // drive and removed from their original location. This will block all // other disk IO, and other torrents download and upload rates may drop // while copying the file. // // Since disk IO is performed in a separate thread, this operation is // also asynchronous. Once the operation completes, the // ``storage_moved_alert`` is generated, with the new path as the // message. If the move fails for some reason, // ``storage_moved_failed_alert`` is generated instead, containing the // error message. // // The ``flags`` argument determines the behavior of the copying/moving // of the files in the torrent. see move_flags_t. // // * always_replace_files = 0 // * fail_if_exist = 1 // * dont_replace = 2 // // ``always_replace_files`` is the default and replaces any file that // exist in both the source directory and the target directory. // // ``fail_if_exist`` first check to see that none of the copy operations // would cause an overwrite. If it would, it will fail. Otherwise it will // proceed as if it was in ``always_replace_files`` mode. Note that there // is an inherent race condition here. If the files in the target // directory appear after the check but before the copy or move // completes, they will be overwritten. When failing because of files // already existing in the target path, the ``error`` of // ``move_storage_failed_alert`` is set to // ``boost::system::errc::file_exists``. // // The intention is that a client may use this as a probe, and if it // fails, ask the user which mode to use. The client may then re-issue // the ``move_storage`` call with one of the other modes. // // ``dont_replace`` always keeps the existing file in the target // directory, if there is one. The source files will still be removed in // that case. Note that it won't automatically re-check files. If an // incomplete torrent is moved into a directory with the complete files, // pause, move, force-recheck and resume. Without the re-checking, the // torrent will keep downloading and files in the new download directory // will be overwritten. // // Files that have been renamed to have absolute paths are not moved by // this function. Keep in mind that files that don't belong to the // torrent but are stored in the torrent's directory may be moved as // well. This goes for files that have been renamed to absolute paths // that still end up inside the save path. void move_storage(std::string const& save_path, int flags = 0) const; // Renames the file with the given index asynchronously. The rename // operation is complete when either a file_renamed_alert or // file_rename_failed_alert is posted. void rename_file(int index, std::string const& new_name) const; #ifndef TORRENT_NO_DEPRECATE #if TORRENT_USE_WSTRING // all wstring APIs are deprecated since 0.16.11 // instead, use the wchar -> utf8 conversion functions // and pass in utf8 strings TORRENT_DEPRECATED void move_storage(std::wstring const& save_path, int flags = 0) const; TORRENT_DEPRECATED void rename_file(int index, std::wstring const& new_name) const; #endif // TORRENT_USE_WSTRING #endif // TORRENT_NO_DEPRECATE // Enables or disabled super seeding/initial seeding for this torrent. // The torrent needs to be a seed for this to take effect. void super_seeding(bool on) const; // ``info_hash()`` returns the info-hash of the torrent. If this handle // is to a torrent that hasn't loaded yet (for instance by being added) // by a URL, the returned value is undefined. sha1_hash info_hash() const; // comparison operators. The order of the torrents is unspecified // but stable. bool operator==(const torrent_handle& h) const { return m_torrent.lock() == h.m_torrent.lock(); } bool operator!=(const torrent_handle& h) const { return m_torrent.lock() != h.m_torrent.lock(); } bool operator<(const torrent_handle& h) const { return m_torrent.lock() < h.m_torrent.lock(); } boost::uint32_t id() const { uintptr_t ret = reinterpret_cast(m_torrent.lock().get()); // a torrent object is about 1024 (2^10) bytes, so // it's safe to shift 10 bits return boost::uint32_t(ret >> 10); } // This function is intended only for use by plugins and the alert // dispatch function. This type does not have a stable API and should // be relied on as little as possible. boost::shared_ptr native_handle() const; private: torrent_handle(boost::weak_ptr const& t) { if (!t.expired()) m_torrent = t; } boost::weak_ptr m_torrent; }; } #endif // TORRENT_TORRENT_HANDLE_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/torrent_info.hpp000066400000000000000000000635051351156116000250040ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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_INCLUDED #define TORRENT_TORRENT_INFO_HPP_INCLUDED #include #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/bdecode.hpp" #include "libtorrent/time.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/copy_ptr.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/sha1_hash.hpp" #include "libtorrent/file_storage.hpp" namespace libtorrent { class peer_connection; class entry; struct announce_entry; struct lazy_entry; namespace aux { struct session_settings; } // internal, exposed for the unit test TORRENT_EXTRA_EXPORT void sanitize_append_path_element(std::string& path , char const* element, int element_len); TORRENT_EXTRA_EXPORT bool verify_encoding(std::string& target); // the web_seed_entry holds information about a web seed (also known // as URL seed or HTTP seed). It is essentially a URL with some state // associated with it. For more information, see `BEP 17`_ and `BEP 19`_. struct TORRENT_EXPORT web_seed_entry { // http seeds are different from url seeds in the // protocol they use. http seeds follows the original // http seed spec. by John Hoffman enum type_t { url_seed, http_seed }; typedef std::vector > headers_t; web_seed_entry(std::string const& url_, type_t type_ , std::string const& auth_ = std::string() , headers_t const& extra_headers_ = headers_t()); // URL and type comparison bool operator==(web_seed_entry const& e) const { return url == e.url && type == e.type; } // URL and type less-than comparison bool operator<(web_seed_entry const& e) const { if (url < e.url) return true; if (url > e.url) return false; return type < e.type; } // The URL of the web seed std::string url; // Optional authentication. If this is set, it's passed // in as HTTP basic auth to the web seed. The format is: // username:password. std::string auth; // Any extra HTTP headers that need to be passed to the web seed headers_t extra_headers; // The type of web seed (see type_t) boost::uint8_t type; }; #ifndef BOOST_NO_EXCEPTIONS // for backwards compatibility with 0.14 typedef libtorrent_exception invalid_torrent_file; #endif // TODO: there may be some opportunities to optimize the size if torrent_info. // specifically to turn some std::string and std::vector into pointers class TORRENT_EXPORT torrent_info { public: // The constructor that takes an info-hash will initialize the info-hash // to the given value, but leave all other fields empty. This is used // internally when downloading torrents without the metadata. The // metadata will be created by libtorrent as soon as it has been // downloaded from the swarm. // // The constructor that takes a bdecode_node will create a torrent_info // object from the information found in the given torrent_file. The // bdecode_node represents a tree node in an bencoded file. To load an // ordinary .torrent file into a bdecode_node, use bdecode(). // // The version that takes a buffer pointer and a size will decode it as a // .torrent file and initialize the torrent_info object for you. // // The version that takes a filename will simply load the torrent file // and decode it inside the constructor, for convenience. This might not // be the most suitable for applications that want to be able to report // detailed errors on what might go wrong. // // There is an upper limit on the size of the torrent file that will be // loaded by the overload taking a filename. If it's important that even // very large torrent files are loaded, use one of the other overloads. // // The overloads that takes an ``error_code const&`` never throws if an // error occur, they will simply set the error code to describe what went // wrong and not fully initialize the torrent_info object. The overloads // that do not take the extra error_code parameter will always throw if // an error occurs. These overloads are not available when building // without exception support. // // The ``flags`` argument is currently unused. #ifndef BOOST_NO_EXCEPTIONS torrent_info(bdecode_node const& torrent_file, int flags = 0); torrent_info(char const* buffer, int size, int flags = 0); torrent_info(std::string const& filename, int flags = 0); #endif // BOOST_NO_EXCEPTIONS torrent_info(torrent_info const& t); torrent_info(sha1_hash const& info_hash, int flags = 0); torrent_info(bdecode_node const& torrent_file, error_code& ec, int flags = 0); torrent_info(char const* buffer, int size, error_code& ec, int flags = 0); torrent_info(std::string const& filename, error_code& ec, int flags = 0); #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED torrent_info(lazy_entry const& torrent_file, int flags = 0); TORRENT_DEPRECATED torrent_info(lazy_entry const& torrent_file, error_code& ec , int flags = 0); #if TORRENT_USE_WSTRING // all wstring APIs are deprecated since 0.16.11 instead, use the wchar // -> utf8 conversion functions and pass in utf8 strings TORRENT_DEPRECATED torrent_info(std::wstring const& filename, error_code& ec , int flags = 0); TORRENT_DEPRECATED torrent_info(std::wstring const& filename, int flags = 0); #endif // TORRENT_USE_WSTRING #endif // TORRENT_NO_DEPRECATE // frees all storage associated with this torrent_info object ~torrent_info(); // The file_storage object contains the information on how to map the // pieces to files. It is separated from the torrent_info object because // when creating torrents a storage object needs to be created without // having a torrent file. When renaming files in a storage, the storage // needs to make its own copy of the file_storage in order to make its // mapping differ from the one in the torrent file. // // ``orig_files()`` returns the original (unmodified) file storage for // this torrent. This is used by the web server connection, which needs // to request files with the original names. Filename may be changed using // ``torrent_info::rename_file()``. // // For more information on the file_storage object, see the separate // document on how to create torrents. file_storage const& files() const { return m_files; } file_storage const& orig_files() const { TORRENT_ASSERT(is_loaded()); return m_orig_files ? *m_orig_files : m_files; } // Renames a the file with the specified index to the new name. The new // filename is reflected by the ``file_storage`` returned by ``files()`` // but not by the one returned by ``orig_files()``. // // If you want to rename the base name of the torrent (for a multi file // torrent), you can copy the ``file_storage`` (see files() and // orig_files() ), change the name, and then use `remap_files()`_. // // The ``new_filename`` can both be a relative path, in which case the // file name is relative to the ``save_path`` of the torrent. If the // ``new_filename`` is an absolute path (i.e. ``is_complete(new_filename) // == true``), then the file is detached from the ``save_path`` of the // torrent. In this case the file is not moved when move_storage() is // invoked. void rename_file(int index, std::string const& new_filename) { TORRENT_ASSERT(is_loaded()); copy_on_write(); m_files.rename_file(index, new_filename); } #ifndef TORRENT_NO_DEPRECATE #if TORRENT_USE_WSTRING // all wstring APIs are deprecated since 0.16.11 // instead, use the wchar -> utf8 conversion functions // and pass in utf8 strings TORRENT_DEPRECATED void rename_file(int index, std::wstring const& new_filename); #endif // TORRENT_USE_WSTRING #endif // TORRENT_NO_DEPRECATE // Remaps the file storage to a new file layout. This can be used to, for // instance, download all data in a torrent to a single file, or to a // number of fixed size sector aligned files, regardless of the number // and sizes of the files in the torrent. // // The new specified ``file_storage`` must have the exact same size as // the current one. void remap_files(file_storage const& f); // ``add_tracker()`` adds a tracker to the announce-list. The ``tier`` // determines the order in which the trackers are to be tried. // // The ``trackers()`` function will return a sorted vector of // ``announce_entry``. Each announce entry contains a string, which is // the tracker url, and a tier index. The tier index is the high-level // priority. No matter which trackers that works or not, the ones with // lower tier will always be tried before the one with higher tier // number. For more information, see announce_entry_. void add_tracker(std::string const& url, int tier = 0); std::vector const& trackers() const { return m_urls; } // These two functions are related to `BEP 38`_ (mutable torrents). The // vectors returned from these correspond to the "similar" and // "collections" keys in the .torrent file. Both info-hashes and // collections from within the info-dict and from outside of it are // included. // // .. _`BEP 38`: http://www.bittorrent.org/beps/bep_0038.html std::vector similar_torrents() const; std::vector collections() const; #ifndef TORRENT_NO_DEPRECATE // deprecated in 0.16. Use web_seeds() instead TORRENT_DEPRECATED std::vector url_seeds() const; TORRENT_DEPRECATED std::vector http_seeds() const; // deprecated in 1.1 TORRENT_DEPRECATED bool parse_info_section(lazy_entry const& e, error_code& ec , int flags); #endif // TORRENT_NO_DEPRECATE // ``web_seeds()`` returns all url seeds and http seeds in the torrent. // Each entry is a ``web_seed_entry`` and may refer to either a url seed // or http seed. // // ``add_url_seed()`` and ``add_http_seed()`` adds one url to the list of // url/http seeds. Currently, the only transport protocol supported for // the url is http. // // ``set_web_seeds()`` replaces all web seeds with the ones specified in // the ``seeds`` vector. // // The ``extern_auth`` argument can be used for other authorization // schemes than basic HTTP authorization. If set, it will override any // username and password found in the URL itself. The string will be sent // as the HTTP authorization header's value (without specifying "Basic"). // // The ``extra_headers`` argument defaults to an empty list, but can be // used to insert custom HTTP headers in the requests to a specific web // seed. // // See http-seeding_ for more information. void add_url_seed(std::string const& url , std::string const& extern_auth = std::string() , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t()); void add_http_seed(std::string const& url , std::string const& extern_auth = std::string() , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t()); std::vector const& web_seeds() const { return m_web_seeds; } void set_web_seeds(std::vector seeds); // ``total_size()``, ``piece_length()`` and ``num_pieces()`` returns the // total number of bytes the torrent-file represents (all the files in // it), the number of byte for each piece and the total number of pieces, // respectively. The difference between ``piece_size()`` and // ``piece_length()`` is that ``piece_size()`` takes the piece index as // argument and gives you the exact size of that piece. It will always be // the same as ``piece_length()`` except in the case of the last piece, // which may be smaller. boost::int64_t total_size() const { return m_files.total_size(); } int piece_length() const { return m_files.piece_length(); } int num_pieces() const { return m_files.num_pieces(); } // returns the info-hash of the torrent const sha1_hash& info_hash() const { return m_info_hash; } #ifndef TORRENT_NO_DEPRECATE // deprecated in 1.0. Use the variants that take an index instead // internal_file_entry is no longer exposed in the API typedef file_storage::iterator file_iterator; typedef file_storage::reverse_iterator reverse_file_iterator; // This class will need some explanation. First of all, to get a list of // all files in the torrent, you can use ``begin_files()``, // ``end_files()``, ``rbegin_files()`` and ``rend_files()``. These will // give you standard vector iterators with the type // ``internal_file_entry``, which is an internal type. // // You can resolve it into the public representation of a file // (``file_entry``) using the ``file_storage::at`` function, which takes // an index and an iterator. TORRENT_DEPRECATED file_iterator begin_files() const { return m_files.begin_deprecated(); } TORRENT_DEPRECATED file_iterator end_files() const { return m_files.end_deprecated(); } reverse_file_iterator rbegin_files() const { return m_files.rbegin_deprecated(); } TORRENT_DEPRECATED reverse_file_iterator rend_files() const { return m_files.rend_deprecated(); } TORRENT_DEPRECATED file_iterator file_at_offset(boost::int64_t offset) const { return m_files.file_at_offset_deprecated(offset); } TORRENT_DEPRECATED file_entry file_at(int index) const { return m_files.at_deprecated(index); } #endif // TORRENT_NO_DEPRECATE // If you need index-access to files you can use the ``num_files()`` along // with the ``file_path()``, ``file_size()``-family of functions to access // files using indices. int num_files() const { return m_files.num_files(); } // This function will map a piece index, a byte offset within that piece // and a size (in bytes) into the corresponding files with offsets where // that data for that piece is supposed to be stored. See file_slice. std::vector map_block(int piece, boost::int64_t offset, int size) const { TORRENT_ASSERT(is_loaded()); return m_files.map_block(piece, offset, size); } // This function will map a range in a specific file into a range in the // torrent. The ``file_offset`` parameter is the offset in the file, // given in bytes, where 0 is the start of the file. See peer_request. // // The input range is assumed to be valid within the torrent. // ``file_offset`` + ``size`` is not allowed to be greater than the file // size. ``file_index`` must refer to a valid file, i.e. it cannot be >= // ``num_files()``. peer_request map_file(int file, boost::int64_t offset, int size) const { TORRENT_ASSERT(is_loaded()); return m_files.map_file(file, offset, size); } // load and unload this torrent info void load(char const* buffer, int size, error_code& ec); void unload(); #ifndef TORRENT_NO_DEPRECATE // ------- start deprecation ------- // these functions will be removed in a future version TORRENT_DEPRECATED torrent_info(entry const& torrent_file); TORRENT_DEPRECATED void print(std::ostream& os) const; // ------- end deprecation ------- #endif // Returns the SSL root certificate for the torrent, if it is an SSL // torrent. Otherwise returns an empty string. The certificate is // the the public certificate in x509 format. std::string ssl_cert() const; // returns true if this torrent_info object has a torrent loaded. // This is primarily used to determine if a magnet link has had its // metadata resolved yet or not. bool is_valid() const { return m_files.is_valid(); } // returns true if this torrent is private. i.e., it should not be // distributed on the trackerless network (the kademlia DHT). bool priv() const { return m_private; } // returns true if this is an i2p torrent. This is determined by whether // or not it has a tracker whose URL domain name ends with ".i2p". i2p // torrents disable the DHT and local peer discovery as well as talking // to peers over anything other than the i2p network. bool is_i2p() const { return m_i2p; } // ``hash_for_piece()`` takes a piece-index and returns the 20-bytes // sha1-hash for that piece and ``info_hash()`` returns the 20-bytes // sha1-hash for the info-section of the torrent file. // ``hash_for_piece_ptr()`` returns a pointer to the 20 byte sha1 digest // for the piece. Note that the string is not null-terminated. int piece_size(int index) const { return m_files.piece_size(index); } sha1_hash hash_for_piece(int index) const; char const* hash_for_piece_ptr(int index) const { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < m_files.num_pieces()); TORRENT_ASSERT(is_loaded()); if (is_merkle_torrent()) { TORRENT_ASSERT(index < int(m_merkle_tree.size() - m_merkle_first_leaf)); return m_merkle_tree[m_merkle_first_leaf + index].data(); } else { TORRENT_ASSERT(m_piece_hashes); TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); TORRENT_ASSERT(index < int(m_info_section_size / 20)); return &m_piece_hashes[index*20]; } } bool is_loaded() const { return m_piece_hashes || !m_merkle_tree.empty(); } // ``merkle_tree()`` returns a reference to the merkle tree for this // torrent, if any. // // ``set_merkle_tree()`` moves the passed in merkle tree into the // torrent_info object. i.e. ``h`` will not be identical after the call. // You need to set the merkle tree for a torrent that you've just created // (as a merkle torrent). The merkle tree is retrieved from the // ``create_torrent::merkle_tree()`` function, and need to be saved // separately from the torrent file itself. Once it's added to // libtorrent, the merkle tree will be persisted in the resume data. std::vector const& merkle_tree() const { return m_merkle_tree; } void set_merkle_tree(std::vector& h) { TORRENT_ASSERT(h.size() == m_merkle_tree.size() ); m_merkle_tree.swap(h); } // ``name()`` returns the name of the torrent. // // ``comment()`` returns the comment associated with the torrent. If // there's no comment, it will return an empty string. // ``creation_date()`` returns the creation date of the torrent as time_t // (`posix time`_). If there's no time stamp in the torrent file, the // optional object will be uninitialized. // // Both the name and the comment is UTF-8 encoded strings. // // ``creator()`` returns the creator string in the torrent. If there is // no creator string it will return an empty string. // // .. _`posix time`: http://www.opengroup.org/onlinepubs/009695399/functions/time.html const std::string& name() const { return m_files.name(); } boost::optional 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 > nodes_t; // If this torrent contains any DHT nodes, they are put in this vector in // their original form (host name and port number). nodes_t const& nodes() const { return m_nodes; } // This is used when creating torrent. Use this to add a known DHT node. // It may be used, by the client, to bootstrap into the DHT network. void add_node(std::pair const& node) { m_nodes.push_back(node); } // populates the torrent_info by providing just the info-dict buffer. // This is used when loading a torrent from a magnet link for instance, // where we only have the info-dict. The bdecode_node ``e`` points to a // parsed info-dictionary. ``ec`` returns an error code if something // fails (typically if the info dictionary is malformed). ``flags`` are // currently unused. bool parse_info_section(bdecode_node const& e, error_code& ec, int flags); // This function looks up keys from the info-dictionary of the loaded // torrent file. It can be used to access extension values put in the // .torrent file. If the specified key cannot be found, it returns NULL. bdecode_node info(char const* key) const; // swap the content of this and ``ti``. void swap(torrent_info& ti); // ``metadata()`` returns a the raw info section of the torrent file. The size // of the metadata is returned by ``metadata_size()``. int metadata_size() const { return m_info_section_size; } boost::shared_array metadata() const { return m_info_section; } // internal bool add_merkle_nodes(std::map const& subtree , int piece); std::map build_merkle_list(int piece) const; // returns whether or not this is a merkle torrent. // see `BEP 30`__. // // __ http://bittorrent.org/beps/bep_0030.html bool is_merkle_torrent() const { return !m_merkle_tree.empty(); } bool parse_torrent_file(bdecode_node const& libtorrent, error_code& ec, int flags); // if we're logging member offsets, we need access to them private: void resolve_duplicate_filenames(); // the slow path, in case we detect/suspect a name collision void resolve_duplicate_filenames_slow(); #if TORRENT_USE_INVARIANT_CHECKS friend class invariant_access; void check_invariant() const; #endif // not assignable torrent_info const& operator=(torrent_info const&); void copy_on_write(); file_storage m_files; // if m_files is modified, it is first copied into // m_orig_files so that the original name and // filenames are preserved. copy_ptr m_orig_files; // the URLs to the trackers std::vector m_urls; std::vector m_web_seeds; nodes_t m_nodes; // the info-hashes (20 bytes each) in the "similar" key. The pointers // point directly into the info_section. When copied, these pointers must // be corrected to point into the copied-to buffer std::vector m_similar_torrents; // these are similar torrents from outside of the info-dict. We can't // have non-owning pointers to those, as we only keep the info-dict // around. std::vector m_owned_similar_torrents; // these or strings of the "collections" key from the torrent file. The // pointers point directly into the info_section buffer and when copied, // these pointers must be corrected to point into the new buffer. The // int is the length of the string. Strings are not NULL-terminated. std::vector > m_collections; // these are the collections from outside of the info-dict. These are // owning strings, since we only keep the info-section around, these // cannot be pointers into that buffer. std::vector m_owned_collections; // if this is a merkle torrent, this is the merkle // tree. It has space for merkle_num_nodes(merkle_num_leafs(num_pieces)) // hashes std::vector m_merkle_tree; // this is a copy of the info section from the torrent. // it use maintained in this flat format in order to // make it available through the metadata extension boost::shared_array m_info_section; // this is a pointer into the m_info_section buffer // pointing to the first byte of the first SHA-1 hash char const* m_piece_hashes; // 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; // the info section parsed. points into m_info_section // parsed lazily mutable bdecode_node m_info_dict; // if a creation date is found in the torrent file // this will be set to that, otherwise it'll be // 1970, Jan 1 time_t m_creation_date; // the hash that identifies this torrent sha1_hash m_info_hash; // the number of bytes in m_info_section boost::uint32_t m_info_section_size; // the index to the first leaf. This is where the hash for the // first piece is stored boost::uint32_t m_merkle_first_leaf:24; // 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 multi file torrent // or not. e.g. test/test there's one file and one directory // and they have the same name. bool m_multifile:1; // this is true if the torrent is private. i.e., is should not // be announced on the dht bool m_private:1; // this is true if one of the trackers has an .i2p top // domain in its hostname. This means the DHT and LSD // features are disabled for this torrent (unless the // settings allows mixing i2p peers with regular peers) bool m_i2p:1; }; } #endif // TORRENT_TORRENT_INFO_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/torrent_peer.hpp000066400000000000000000000211661351156116000250010ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_PEER_HPP_INCLUDED #define TORRENT_TORRENT_PEER_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/address.hpp" #include "libtorrent/socket.hpp" namespace libtorrent { struct peer_connection_interface; struct external_ip; // calculate the priority of a peer based on its address. One of the // endpoint should be our own. The priority is symmetric, so it doesn't // matter which is which TORRENT_EXTRA_EXPORT boost::uint32_t peer_priority( tcp::endpoint e1, tcp::endpoint e2); struct TORRENT_EXTRA_EXPORT torrent_peer { torrent_peer(boost::uint16_t port, bool connectable, int src); boost::uint64_t total_download() const; boost::uint64_t total_upload() const; boost::uint32_t rank(external_ip const& external, int external_port) const; libtorrent::address address() const; char const* dest() const; tcp::endpoint ip() const { return tcp::endpoint(address(), port); } #ifndef TORRENT_DISABLE_LOGGING std::string to_string() const; #endif // this is the accumulated amount of // uploaded and downloaded data to this // torrent_peer. It only accounts for what was // shared during the last connection to // this torrent_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 these figures with the // statistics from the peer_connection. // since these values don't need to be stored // with byte-precision, they specify the number // of kiB. i.e. shift left 10 bits to compare to // byte counters. boost::uint32_t prev_amount_upload; boost::uint32_t prev_amount_download; // if the torrent_peer is connected now, this // will refer to a valid peer_connection peer_connection_interface* connection; // as computed by hashing our IP with the remote // IP of this peer // calculated lazily mutable boost::uint32_t peer_rank; // the time when this torrent_peer was optimistically unchoked // the last time. in seconds since session was created // 16 bits is enough to last for 18.2 hours // when the session time reaches 18 hours, it jumps back by // 9 hours, and all peers' times are updated to be // relative to that new time offset boost::uint16_t last_optimistically_unchoked; // the time when the torrent_peer connected to us // or disconnected if it isn't connected right now // in number of seconds since session was created boost::uint16_t last_connected; // the port this torrent_peer is or was connected on boost::uint16_t port; // the number of times this torrent_peer has been // part of a piece that failed the hash check boost::uint8_t hashfails; // the number of failed connection attempts // this torrent_peer has unsigned failcount:5; // [0, 31] // incoming peers (that don't advertise their listen port) // will not be considered connectable. Peers that // we have a listen port for will be assumed to be. bool connectable:1; // true if this torrent_peer currently is unchoked // because of an optimistic unchoke. // when the optimistic unchoke is moved to // another torrent_peer, this torrent_peer will be choked // if this is true bool optimistically_unchoked:1; // this is true if the torrent_peer is a seed bool seed:1; // the number of times we have allowed a fast // reconnect for this torrent_peer. unsigned fast_reconnects:4; // for every valid piece we receive where this // torrent_peer was one of the participants, we increase // this value. For every invalid piece we receive // where this torrent_peer was a participant, we decrease // this value. If it sinks below a threshold, its // considered a bad torrent_peer and will be banned. signed trust_points:4; // [-7, 8] // a bitmap combining the peer_source flags // from peer_info. unsigned source:6; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // Hints encryption support of torrent_peer. Only effective // for and when the outgoing encryption policy // allows both encrypted and non encrypted // connections (pe_settings::out_enc_policy // == enabled). The initial state of this flag // determines the initial connection attempt // type (true = encrypted, false = standard). // This will be toggled everytime either an // encrypted or non-encrypted handshake fails. bool pe_support:1; #endif #if TORRENT_USE_IPV6 // this is true if the v6 union member in addr is // the one to use, false if it's the v4 one bool is_v6_addr:1; #endif #if TORRENT_USE_I2P // set if the i2p_destination is in use in the addr union bool is_i2p_addr:1; #endif // if this is true, the torrent_peer has previously // participated in a piece that failed the piece // hash check. This will put the torrent_peer on parole // and only request entire pieces. If a piece pass // that was partially requested from this torrent_peer it // will leave parole mode and continue download // pieces as normal peers. bool on_parole:1; // is set to true if this torrent_peer has been banned bool banned:1; // we think this torrent_peer supports uTP bool supports_utp:1; // we have been connected via uTP at least once bool confirmed_supports_utp:1; bool supports_holepunch:1; // this is set to one for web seeds. Web seeds // are not stored in the policy m_peers list, // and are exempt from connect candidate bookkeeping // so, any torrent_peer with the web_seed bit set, is // never considered a connect candidate bool web_seed:1; #if TORRENT_USE_ASSERTS bool in_use:1; #endif }; struct TORRENT_EXTRA_EXPORT ipv4_peer : torrent_peer { ipv4_peer(tcp::endpoint const& ip, bool connectable, int src); ipv4_peer(ipv4_peer const& p); address_v4 addr; private: ipv4_peer& operator=(ipv4_peer const&); }; #if TORRENT_USE_I2P struct TORRENT_EXTRA_EXPORT i2p_peer : torrent_peer { i2p_peer(char const* destination, bool connectable, int src); ~i2p_peer(); char* destination; private: i2p_peer(const i2p_peer&); i2p_peer& operator=(i2p_peer const&); }; #endif #if TORRENT_USE_IPV6 struct TORRENT_EXTRA_EXPORT ipv6_peer : torrent_peer { ipv6_peer(tcp::endpoint const& ip, bool connectable, int src); const address_v6::bytes_type addr; private: // explicitly disallow assignment, to silence msvc warning ipv6_peer& operator=(ipv6_peer const&); ipv6_peer(ipv6_peer const&); }; #endif struct peer_address_compare { bool operator()( torrent_peer const* lhs, libtorrent::address const& rhs) const { return lhs->address() < rhs; } bool operator()( libtorrent::address const& lhs, torrent_peer const* rhs) const { return lhs < rhs->address(); } #if TORRENT_USE_I2P bool operator()( torrent_peer const* lhs, char const* rhs) const { return strcmp(lhs->dest(), rhs) < 0; } bool operator()( char const* lhs, torrent_peer const* rhs) const { return strcmp(lhs, rhs->dest()) < 0; } #endif bool operator()( torrent_peer const* lhs, torrent_peer const* rhs) const { #if TORRENT_USE_I2P if (rhs->is_i2p_addr == lhs->is_i2p_addr) return strcmp(lhs->dest(), rhs->dest()) < 0; #endif return lhs->address() < rhs->address(); } }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/torrent_peer_allocator.hpp000066400000000000000000000063771351156116000270500ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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_ALLOCATOR_HPP_INCLUDED #define TORRENT_PEER_ALLOCATOR_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/torrent_peer.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { struct TORRENT_EXTRA_EXPORT torrent_peer_allocator_interface { enum peer_type_t { ipv4_peer_type, ipv6_peer_type, i2p_peer_type }; virtual torrent_peer* allocate_peer_entry(int type) = 0; virtual void free_peer_entry(torrent_peer* p) = 0; protected: ~torrent_peer_allocator_interface() {} }; struct TORRENT_EXTRA_EXPORT torrent_peer_allocator TORRENT_FINAL : torrent_peer_allocator_interface { torrent_peer_allocator(); torrent_peer* allocate_peer_entry(int type); void free_peer_entry(torrent_peer* p); boost::uint64_t total_bytes() const { return m_total_bytes; } boost::uint64_t total_allocations() const { return m_total_allocations; } int live_bytes() const { return m_live_bytes; } int live_allocations() const { return m_live_allocations; } private: // this is a shared pool where torrent_peer objects // are allocated. It's a pool since we're likely // to have tens of thousands of peers, and a pool // saves significant overhead boost::pool<> m_ipv4_peer_pool; #if TORRENT_USE_IPV6 boost::pool<> m_ipv6_peer_pool; #endif #if TORRENT_USE_I2P boost::pool<> m_i2p_peer_pool; #endif // the total number of bytes allocated (cumulative) boost::uint64_t m_total_bytes; // the total number of allocations (cumulative) boost::uint64_t m_total_allocations; // the number of currently live bytes int m_live_bytes; // the number of currently live allocations int m_live_allocations; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/torrent_status.hpp000066400000000000000000000514451351156116000253740ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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_STATUS_HPP_INCLUDED #define TORRENT_TORRENT_STATUS_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/bitfield.hpp" #include "libtorrent/sha1_hash.hpp" #include "libtorrent/time.hpp" // for time_duration #include "libtorrent/storage_defs.hpp" // for storage_mode_t #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { // holds a snapshot of the status of a torrent, as queried by // torrent_handle::status(). struct TORRENT_EXPORT torrent_status { // hidden torrent_status(); ~torrent_status(); #if __cplusplus >= 201103L torrent_status(torrent_status const&) = default; torrent_status& operator=(torrent_status const&) = default; #endif // compares if the torrent status objects come from the same torrent. i.e. // only the torrent_handle field is compared. bool operator==(torrent_status const& st) const { return handle == st.handle; } // a handle to the torrent whose status the object represents. torrent_handle handle; // the different overall states a torrent can be in enum state_t { #ifndef TORRENT_NO_DEPRECATE // The torrent is in the queue for being checked. But there // currently is another torrent that are being checked. // This torrent will wait for its turn. queued_for_checking, #else // internal unused_enum_for_backwards_compatibility, #endif // The torrent has not started its download yet, and is // currently checking existing files. checking_files, // The torrent is trying to download metadata from peers. // This assumes the metadata_transfer extension is in use. downloading_metadata, // The torrent is being downloaded. This is the state // most torrents will be in most of the time. The progress // meter will tell how much of the files that has been // downloaded. downloading, // In this state the torrent has finished downloading but // still doesn't have the entire torrent. i.e. some pieces // are filtered and won't get downloaded. finished, // In this state the torrent has finished downloading and // is a pure seeder. seeding, // If the torrent was started in full allocation mode, this // indicates that the (disk) storage for the torrent is // allocated. allocating, // The torrent is currently checking the fastresume data and // comparing it to the files on disk. This is typically // completed in a fraction of a second, but if you add a // large number of torrents at once, they will queue up. checking_resume_data }; #ifndef TORRENT_NO_DEPRECATE // may be set to an error code describing why the torrent was paused, in // case it was paused by an error. If the torrent is not paused or if it's // paused but not because of an error, this error_code is not set. // if the error is attributed specifically to a file, error_file is set to // the index of that file in the .torrent file. std::string error; #else // internal std::string _dummy_string_; #endif error_code errc; int error_file; // special values for error_file to describe which file or component // encountered the error (``errc``). enum error_file_t { // the error did not occur on a file error_file_none = -1, // the error occurred on m_url error_file_url = -2, // the error occurred setting up the SSL context error_file_ssl_ctx = -3, // the error occurred while loading the .torrent file via the user // supplied load function error_file_metadata = -4, // the error occurred with the partfile error_file_partfile = -5 }; // the path to the directory where this torrent's files are stored. // It's typically the path as was given to async_add_torrent() or // add_torrent() when this torrent was started. This field is only // included if the torrent status is queried with // ``torrent_handle::query_save_path``. std::string save_path; // the name of the torrent. Typically this is derived from the // .torrent file. In case the torrent was started without metadata, // and hasn't completely received it yet, it returns the name given // to it when added to the session. See ``session::add_torrent``. // This field is only included if the torrent status is queried // with ``torrent_handle::query_name``. std::string name; // set to point to the ``torrent_info`` object for this torrent. It's // only included if the torrent status is queried with // ``torrent_handle::query_torrent_file``. boost::weak_ptr torrent_file; // the time until the torrent will announce itself to the tracker. time_duration next_announce; #ifndef TORRENT_NO_DEPRECATE // the time the tracker want us to wait until we announce ourself // again the next time. time_duration announce_interval; #else // hidden time_duration deprecated_announce_interval_; #endif // the URL of the last working tracker. If no tracker request has // been successful yet, it's set to an empty string. std::string current_tracker; // the number of bytes downloaded and uploaded to all peers, accumulated, // *this session* only. The session is considered to restart when a // torrent is paused and restarted again. When a torrent is paused, these // counters are reset to 0. If you want complete, persistent, stats, see // ``all_time_upload`` and ``all_time_download``. boost::int64_t total_download; boost::int64_t total_upload; // counts the amount of bytes send and received this session, but only // the actual payload data (i.e the interesting data), these counters // ignore any protocol overhead. The session is considered to restart // when a torrent is paused and restarted again. When a torrent is // paused, these counters are reset to 0. boost::int64_t total_payload_download; boost::int64_t total_payload_upload; // the number of bytes that has been downloaded and that has failed the // piece hash test. In other words, this is just how much crap that has // been downloaded since the torrent was last started. If a torrent is // paused and then restarted again, this counter will be reset. boost::int64_t total_failed_bytes; // the number of bytes that has been downloaded even though that data // already was downloaded. The reason for this is that in some situations // the same data can be downloaded by mistake. When libtorrent sends // requests to a peer, and the peer doesn't send a response within a // certain timeout, libtorrent will re-request that block. Another // situation when libtorrent may re-request blocks is when the requests // it sends out are not replied in FIFO-order (it will re-request blocks // that are skipped by an out of order block). This is supposed to be as // low as possible. This only counts bytes since the torrent was last // started. If a torrent is paused and then restarted again, this counter // will be reset. boost::int64_t total_redundant_bytes; // a bitmask that represents which pieces we have (set to true) and the // pieces we don't have. It's a pointer and may be set to 0 if the // torrent isn't downloading or seeding. bitfield pieces; // a bitmask representing which pieces has had their hash checked. This // only applies to torrents in *seed mode*. If the torrent is not in seed // mode, this bitmask may be empty. bitfield verified_pieces; // the total number of bytes of the file(s) that we have. All this does // not necessarily has to be downloaded during this session (that's // ``total_payload_download``). boost::int64_t total_done; // the number of bytes we have downloaded, only counting the pieces that // we actually want to download. i.e. excluding any pieces that we have // but have priority 0 (i.e. not wanted). boost::int64_t 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 prioritized to 0, // i.e. not wanted boost::int64_t total_wanted; // are accumulated upload and download payload byte counters. They are // saved in and restored from resume data to keep totals across sessions. boost::int64_t all_time_upload; boost::int64_t all_time_download; // the posix-time when this torrent was added. i.e. what ``time(NULL)`` // returned at the time. time_t added_time; // the posix-time when this torrent was finished. If the torrent is not // yet finished, this is 0. time_t completed_time; // the time when we, or one of our peers, last saw a complete copy of // this torrent. time_t last_seen_complete; // The allocation mode for the torrent. See storage_mode_t for the // options. For more information, see storage-allocation_. storage_mode_t storage_mode; // a value in the range [0, 1], that represents the progress of the // torrent's current task. It may be checking files or downloading. float progress; // progress parts per million (progress * 1000000) when disabling // floating point operations, this is the only option to query progress // // reflects the same value as ``progress``, but instead in a range [0, // 1000000] (ppm = parts per million). When floating point operations are // disabled, this is the only alternative to the floating point value in // progress. int progress_ppm; // the position this torrent has in the download // queue. If the torrent is a seed or finished, this is -1. int queue_position; // the total rates for all peers for this torrent. These will usually // have better precision than summing the rates from all peers. The rates // are given as the number of bytes per second. int download_rate; int upload_rate; // the total transfer rate of payload only, not counting protocol // chatter. This might be slightly smaller than the other rates, but if // projected over a long time (e.g. when calculating ETA:s) the // difference may be noticeable. int download_payload_rate; int upload_payload_rate; // the number of peers that are seeding that this client is // currently connected to. int num_seeds; // the number of peers this torrent currently is connected to. Peer // connections that are in the half-open state (is attempting to connect) // or are queued for later connection attempt do not count. Although they // are visible in the peer list when you call get_peer_info(). 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. set to -1 if the // tracker did not send any scrape data in its announce reply. int num_complete; int num_incomplete; // the number of seeds in our peer list and the total number of peers // (including seeds). We are not necessarily connected to all the peers // in our peer list. This is the number of peers we know of in total, // including banned peers and peers that we have failed to connect to. int list_seeds; int list_peers; // the number of peers in this torrent's peer list that is a candidate to // be connected to. i.e. It has fewer connect attempts than the max fail // count, it is not a seed if we are a seed, it is not banned etc. If // this is 0, it means we don't know of any more peers that we can try. int connect_candidates; // the number of pieces that has been downloaded. It is equivalent to: // ``std::accumulate(pieces->begin(), pieces->end())``. So you don't have // to count yourself. This can be used to see if anything has updated // since last time if you want to keep a graph of the pieces up to date. int num_pieces; // the number of distributed copies of the torrent. Note that one copy // may be spread out among many peers. It tells how many copies there are // currently of the rarest piece(s) among the peers this client is // connected to. int distributed_full_copies; // tells the share of pieces that have more copies than the rarest // piece(s). Divide this number by 1000 to get the fraction. // // For example, if ``distributed_full_copies`` is 2 and // ``distributed_fraction`` is 500, it means that the rarest pieces have // only 2 copies among the peers this torrent is connected to, and that // 50% of all the pieces have more than two copies. // // If we are a seed, the piece picker is deallocated as an optimization, // and piece availability is no longer tracked. In this case the // distributed copies members are set to -1. int distributed_fraction; // the number of distributed copies of the file. note that one copy may // be spread out among many peers. This is a floating point // representation of the distributed copies. // // the integer 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 size of a block, in bytes. A block is a sub piece, it is the // number of bytes that each piece request asks for and the number of // bytes that each bit in the ``partial_piece_info``'s bitset represents, // see get_download_queue(). This is typically 16 kB, but it may be // larger if the pieces are larger. int block_size; // the number of unchoked peers in this torrent. int num_uploads; // the number of peer connections this torrent has, including half-open // connections that hasn't completed the bittorrent handshake yet. This // is always >= ``num_peers``. int num_connections; // the set limit of upload slots (unchoked peers) for this torrent. int uploads_limit; // the set limit of number of connections for this torrent. int connections_limit; // the number of peers in this torrent that are waiting for more // bandwidth quota from the torrent rate limiter. This can determine if // the rate you get from this torrent is bound by the torrents limit or // not. If there is no limit set on this torrent, the peers might still // be waiting for bandwidth quota from the global limiter, but then they // are counted in the ``session_status`` object. int up_bandwidth_queue; int down_bandwidth_queue; // the number of seconds since any peer last uploaded from this torrent // and the last time a downloaded piece passed the hash check, // respectively. Note, when starting up a torrent that needs its files // checked, piece may pass and that will be considered downloading for the // purpose of this counter. -1 means there either hasn't been any // uploading/downloading, or it was too long ago for libtorrent to // remember (currently forgetting happens after about 18 hours) int time_since_upload; int time_since_download; // These keep track of the number of seconds this torrent has been active // (not paused) and the number of seconds it has been active while being // finished and active while being a seed. ``seeding_time`` should be <= // ``finished_time`` which should be <= ``active_time``. They are all // saved in and restored from resume data, to keep totals across // sessions. int active_time; int finished_time; int seeding_time; // A rank of how important it is to seed the torrent, it is used to // determine which torrents to seed and which to queue. It is based on // the peer to seed ratio from the tracker scrape. For more information, // see queuing_. Higher value means more important to seed int seed_rank; // the number of seconds since this torrent acquired scrape data. // If it has never done that, this value is -1. int last_scrape; // the priority of this torrent int priority; // the main state the torrent is in. See torrent_status::state_t. state_t state; // true if this torrent has unsaved changes // to its download state and statistics since the last resume data // was saved. bool need_save_resume; // true if the session global IP filter applies // to this torrent. This defaults to true. bool ip_filter_applies; // true if the torrent is blocked from downloading. This typically // happens when a disk write operation fails. If the torrent is // auto-managed, it will periodically be taken out of this state, in the // hope that the disk condition (be it disk full or permission errors) // has been resolved. If the torrent is not auto-managed, you have to // explicitly take it out of the upload mode by calling set_upload_mode() // on the torrent_handle. bool upload_mode; // true if the torrent is currently in share-mode, i.e. not downloading // the torrent, but just helping the swarm out. bool share_mode; // true if the torrent is in super seeding mode bool super_seeding; // set to true if the torrent is paused and false otherwise. It's only // true if the torrent itself is paused. If the torrent is not running // because the session is paused, this is still false. To know if a // torrent is active or not, you need to inspect both // ``torrent_status::paused`` and ``session::is_paused()``. bool paused; // set to true if the torrent is auto managed, i.e. libtorrent is // responsible for determining whether it should be started or queued. // For more info see queuing_ bool auto_managed; // true when the torrent is in sequential download mode. In this mode // pieces are downloaded in order rather than rarest first. bool sequential_download; // true if all pieces have been downloaded. bool is_seeding; // true if all pieces that have a priority > 0 are downloaded. There is // only a distinction between finished and seeding if some pieces or // files have been set to priority 0, i.e. are not downloaded. bool is_finished; // true if this torrent has metadata (either it was started from a // .torrent file or the metadata has been downloaded). The only scenario // where this can be false is when the torrent was started torrent-less // (i.e. with just an info-hash and tracker ip, a magnet link for // instance). bool has_metadata; // true if there has ever been an incoming connection attempt to this // torrent. bool has_incoming; // true if the torrent is in seed_mode. If the torrent was started in // seed mode, it will leave seed mode once all pieces have been checked // or as soon as one piece fails the hash check. bool seed_mode; // this is true if this torrent's storage is currently being moved from // one location to another. This may potentially be a long operation // if a large file ends up being copied from one drive to another. bool moving_storage; // true if this torrent is loaded into RAM. A torrent can be started // and still not loaded into RAM, in case it has not had any peers interested in it // yet. Torrents are loaded on demand. bool is_loaded; // these are set to true if this torrent is allowed to announce to the // respective peer source. Whether they are true or false is determined by // the queue logic/auto manager. Torrents that are not auto managed will // always be allowed to announce to all peer sources. bool announcing_to_trackers; bool announcing_to_lsd; bool announcing_to_dht; // this reflects whether the ``stop_when_ready`` flag is currently enabled // on this torrent. For more information, see // torrent_handle::stop_when_ready(). bool stop_when_ready; // the info-hash for this torrent sha1_hash info_hash; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/tracker_manager.hpp000066400000000000000000000270261351156116000254170ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TORRENT_USE_OPENSSL // there is no forward declaration header for asio namespace boost { namespace asio { namespace ssl { struct context; } } } #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/address.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/peer.hpp" // peer_entry #include "libtorrent/deadline_timer.hpp" #include "libtorrent/union_endpoint.hpp" #include "libtorrent/udp_socket.hpp" // for udp_socket_observer #include "libtorrent/io_service.hpp" namespace libtorrent { struct request_callback; class tracker_manager; struct timeout_handler; struct tracker_connection; class udp_tracker_connection; class http_tracker_connection; class udp_socket; struct resolver_interface; struct counters; struct ip_filter; #if TORRENT_USE_I2P class i2p_connection; #endif namespace aux { struct session_logger; struct session_settings; } // returns -1 if gzip header is invalid or the header size in bytes TORRENT_EXTRA_EXPORT int gzip_header(const char* buf, int size); struct TORRENT_EXTRA_EXPORT tracker_request { tracker_request() : downloaded(-1) , uploaded(-1) , left(-1) , corrupt(0) , redundant(0) , listen_port(0) , event(none) , kind(announce_request) , key(0) , num_want(0) , send_stats(true) , private_torrent(false) , triggered_manually(false) , second_announce(false) #ifdef TORRENT_USE_OPENSSL , ssl_ctx(0) #endif #if TORRENT_USE_I2P , i2pconn(0) #endif {} enum event_t { none, completed, started, stopped, paused }; enum kind_t { // do not compare against announce_request ! check if not scrape instead announce_request = 0, scrape_request = 1, // affects interpretation of peers string in HTTP response // see parse_tracker_response() i2p = 2 }; std::string url; std::string trackerid; #ifndef TORRENT_NO_DEPRECATE std::string auth; #endif boost::shared_ptr filter; boost::int64_t downloaded; boost::int64_t uploaded; boost::int64_t left; boost::int64_t corrupt; boost::int64_t redundant; boost::uint16_t listen_port; // values from event_t boost::uint8_t event; // values from kind_t boost::uint8_t kind; boost::uint32_t key; int num_want; address_v4 ipv4; #if TORRENT_USE_IPV6 address_v6 ipv6; #endif sha1_hash info_hash; peer_id pid; boost::optional
bind_ip; bool send_stats; // set to true if the .torrent file this tracker announce is for is marked // as private (i.e. has the "priv": 1 key) bool private_torrent; // this is set to true if this request was triggered by a "manual" call to // scrape_tracker() or force_reannounce() bool triggered_manually; // this is set when announcing to the next address family. There are only // two address families now, so when this is set, we won't trigger another // automatic announce bool second_announce; #ifdef TORRENT_USE_OPENSSL boost::asio::ssl::context* ssl_ctx; #endif #if TORRENT_USE_I2P i2p_connection* i2pconn; #endif }; struct tracker_response { tracker_response() : interval(1800) , min_interval(1) , complete(-1) , incomplete(-1) , downloaders(-1) , downloaded(-1) {} // peers from the tracker, in various forms std::vector peers; std::vector peers4; #if TORRENT_USE_IPV6 std::vector peers6; #endif // our external IP address (if the tracker responded with ti, otherwise // INADDR_ANY) address external_ip; // the tracker id, if it was included in the response, otherwise // an empty string std::string trackerid; // if the tracker returned an error, this is set to that error std::string failure_reason; // contains a warning message from the tracker, if included in // the response std::string warning_message; // re-announce interval, in seconds int interval; // the lowest force-announce interval int min_interval; // the number of seeds in the swarm int complete; // the number of downloaders in the swarm int incomplete; // if supported by the tracker, the number of actively downloading peers. // i.e. partial seeds. If not suppored, -1 int downloaders; // the number of times the torrent has been downloaded int downloaded; }; struct TORRENT_EXTRA_EXPORT request_callback { friend class tracker_manager; request_callback() {} virtual ~request_callback() {} virtual void tracker_warning(tracker_request const& req , std::string const& msg) = 0; virtual void tracker_scrape_response(tracker_request const& /*req*/ , int /*complete*/, int /*incomplete*/, int /*downloads*/ , int /*downloaders*/) {} virtual void tracker_response( tracker_request const& req , address const& tracker_ip , std::list
const& ip_list , struct tracker_response const& response) = 0; virtual void tracker_request_error( tracker_request const& req , int response_code , error_code const& ec , const std::string& msg , int retry_interval) = 0; #ifndef TORRENT_DISABLE_LOGGING virtual void debug_log(const char* fmt, ...) const TORRENT_FORMAT(2,3) = 0; #endif }; struct TORRENT_EXTRA_EXPORT timeout_handler : boost::enable_shared_from_this , boost::noncopyable { timeout_handler(io_service& str); void set_timeout(int completion_timeout, int read_timeout); void restart_read_timeout(); void cancel(); bool cancelled() const { return m_abort; } virtual void on_timeout(error_code const& ec) = 0; virtual ~timeout_handler() {} io_service& get_io_service() { return lt::get_io_service(m_timeout); } private: void timeout_callback(error_code const&); int m_completion_timeout; typedef mutex mutex_t; mutable mutex_t m_mutex; // used for timeouts // this is set when the request has been sent time_point m_start_time; // this is set every time something is received time_point m_read_time; // the asio async operation deadline_timer m_timeout; int m_read_timeout; bool m_abort; }; // TODO: 2 this class probably doesn't need to have virtual functions. struct TORRENT_EXTRA_EXPORT tracker_connection : timeout_handler { tracker_connection(tracker_manager& man , tracker_request const& req , io_service& ios , boost::weak_ptr r); void update_transaction_id(boost::shared_ptr c , boost::uint64_t tid); boost::shared_ptr requester() const; virtual ~tracker_connection() {} tracker_request const& tracker_req() const { return m_req; } void fail(error_code const& ec, int code = -1, char const* msg = "" , int interval = 0, int min_interval = 0); virtual void start() = 0; virtual void close(); void sent_bytes(int bytes); void received_bytes(int bytes); virtual bool on_receive(error_code const&, udp::endpoint const& , char const* /* buf */, int /* size */) { return false; } virtual bool on_receive_hostname(error_code const& , char const* /* hostname */ , char const* /* buf */, int /* size */) { return false; } boost::shared_ptr shared_from_this() { return boost::static_pointer_cast( timeout_handler::shared_from_this()); } private: const tracker_request m_req; protected: void fail_impl(error_code const& ec, int code = -1, std::string msg = std::string() , int interval = 0, int min_interval = 0); boost::weak_ptr m_requester; tracker_manager& m_man; }; class TORRENT_EXTRA_EXPORT tracker_manager TORRENT_FINAL : public udp_socket_observer , boost::noncopyable { public: tracker_manager(udp_socket& sock , counters& stats_counters , resolver_interface& resolver , aux::session_settings const& sett #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS , aux::session_logger& ses #endif ); virtual ~tracker_manager(); void queue_request( io_service& ios , tracker_request r , boost::weak_ptr c = boost::weak_ptr()); void abort_all_requests(bool all = false); void remove_request(tracker_connection const*); bool empty() const; int num_requests() const; void sent_bytes(int bytes); void received_bytes(int bytes); virtual bool incoming_packet(error_code const& e, udp::endpoint const& ep , char const* buf, int size) TORRENT_OVERRIDE; // this is only used for SOCKS packets, since // they may be addressed to hostname virtual bool incoming_packet(error_code const& e, char const* hostname , char const* buf, int size) TORRENT_OVERRIDE; void update_transaction_id( boost::shared_ptr c , boost::uint64_t tid); aux::session_settings const& settings() const { return m_settings; } udp_socket& get_udp_socket() { return m_udp_socket; } resolver_interface& host_resolver() { return m_host_resolver; } private: typedef mutex mutex_t; mutable mutex_t m_mutex; // maps transactionid to the udp_tracker_connection // TODO: this should be unique_ptr in the future typedef boost::unordered_map > udp_conns_t; udp_conns_t m_udp_conns; typedef std::vector > http_conns_t; http_conns_t m_http_conns; class udp_socket& m_udp_socket; resolver_interface& m_host_resolver; aux::session_settings const& m_settings; counters& m_stats_counters; #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS aux::session_logger& m_ses; #endif bool m_abort; }; } #endif // TORRENT_TRACKER_MANAGER_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/udp_socket.hpp000066400000000000000000000233031351156116000244240ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_SOCKET_HPP_INCLUDED #define TORRENT_UDP_SOCKET_HPP_INCLUDED #include "libtorrent/socket.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/buffer.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/deadline_timer.hpp" #include "libtorrent/debug.hpp" #include "libtorrent/aux_/allocating_handler.hpp" #include namespace libtorrent { struct TORRENT_EXTRA_EXPORT udp_socket_observer { // return true if the packet was handled (it won't be // propagated to the next observer) virtual bool incoming_packet(error_code const& ec , udp::endpoint const&, char const* buf, int size) = 0; virtual bool incoming_packet(error_code const& /* ec */ , char const* /* hostname */, char const* /* buf */, int /* size */) { return false; } // called when the socket becomes writeable, after having // failed with EWOULDBLOCK virtual void writable() {} // called every time the socket is drained of packets virtual void socket_drained() {} protected: ~udp_socket_observer() {} }; class TORRENT_EXTRA_EXPORT udp_socket : single_threaded { public: udp_socket(io_service& ios); ~udp_socket(); enum flags_t { dont_drop = 1 , peer_connection = 2 , tracker_connection = 4 , dont_queue = 8 }; bool is_open() const { return m_abort == false; } io_service& get_io_service() { return lt::get_io_service(m_ipv4_sock); } void subscribe(udp_socket_observer* o); void unsubscribe(udp_socket_observer* o); // this is only valid when using a socks5 proxy void send_hostname(char const* hostname, int port, char const* p , int len, error_code& ec, int flags = 0); void send(udp::endpoint const& ep, char const* p, int len , error_code& ec, int flags = 0); void bind(udp::endpoint const& ep, error_code& ec); void close(); int local_port() const { return m_bind_port; } void set_proxy_settings(aux::proxy_settings const& ps); aux::proxy_settings const& get_proxy_settings() { return m_proxy_settings; } void set_force_proxy(bool f) { m_force_proxy = f; } bool is_closed() const { return m_abort; } tcp::endpoint local_endpoint(error_code& ec) const { udp::endpoint ep = m_ipv4_sock.local_endpoint(ec); return tcp::endpoint(ep.address(), ep.port()); } void set_buf_size(int s); typedef udp::socket::receive_buffer_size receive_buffer_size; typedef udp::socket::send_buffer_size send_buffer_size; template void get_option(SocketOption const& opt, error_code& ec) { #if TORRENT_USE_IPV6 if (opt.level(udp::v6()) == IPPROTO_IPV6) m_ipv6_sock.get_option(opt, ec); else #endif m_ipv4_sock.get_option(opt, ec); } template void set_option(SocketOption const& opt, error_code& ec) { if (opt.level(udp::v4()) != IPPROTO_IPV6) m_ipv4_sock.set_option(opt, ec); #if TORRENT_USE_IPV6 if (opt.level(udp::v6()) != IPPROTO_IP) m_ipv6_sock.set_option(opt, ec); #endif } template void get_option(SocketOption& opt, error_code& ec) { #if TORRENT_USE_IPV6 if (opt.level(udp::v6()) == IPPROTO_IPV6) m_ipv6_sock.get_option(opt, ec); else #endif m_ipv4_sock.get_option(opt, ec); } udp::endpoint proxy_addr() const { return m_proxy_addr; } private: struct queued_packet { queued_packet() : hostname(NULL) , flags(0) {} udp::endpoint ep; char* hostname; buffer buf; int flags; }; // number of outstanding UDP socket operations // using the UDP socket buffer int num_outstanding() const { return m_v4_outstanding #if TORRENT_USE_IPV6 + m_v6_outstanding #endif ; } // non-copyable udp_socket(udp_socket const&); udp_socket& operator=(udp_socket const&); void close_impl(); // observers on this udp socket std::vector m_observers; std::vector m_added_observers; template aux::allocating_handler make_read_handler4(Handler const& handler) { return aux::allocating_handler( handler, m_v4_read_handler_storage ); } #if TORRENT_USE_IPV6 template aux::allocating_handler make_read_handler6(Handler const& handler) { return aux::allocating_handler( handler, m_v6_read_handler_storage ); } #endif // this is true while iterating over the observers // vector, invoking observer hooks. We may not // add new observers during this time, since it // may invalidate the iterator. If this is true, // instead add new observers to m_added_observers // and they will be added later bool m_observers_locked; void call_handler(error_code const& ec, udp::endpoint const& ep , char const* buf, int size); void call_handler(error_code const& ec, const char* host , char const* buf, int size); void call_drained_handler(); void call_writable_handler(); void on_writable(error_code const& ec, udp::socket* s); void setup_read(udp::socket* s); void on_read(error_code const& ec, udp::socket* s); void on_read_impl(udp::endpoint const& ep , error_code const& e, std::size_t bytes_transferred); void on_name_lookup(error_code const& e, tcp::resolver::iterator i); void on_connect_timeout(error_code const& ec); void on_connected(error_code const& ec); void handshake1(error_code const& e); void handshake2(error_code const& e); void handshake3(error_code const& e); void handshake4(error_code const& e); void socks_forward_udp(); void connect1(error_code const& e); void connect2(error_code const& e); void hung_up(error_code const& e); void retry_socks_connect(error_code const& ec); void drain_queue(); void wrap(udp::endpoint const& ep, char const* p, int len, error_code& ec); void wrap(char const* hostname, int port, char const* p, int len, error_code& ec); void unwrap(error_code const& e, char const* buf, int size); udp::socket m_ipv4_sock; aux::handler_storage m_v4_read_handler_storage; deadline_timer m_timer; int m_buf_size; // if the buffer size is attempted // to be changed while the buffer is // being used, this member is set to // the desired size, and it's resized // later int m_new_buf_size; char* m_buf; #if TORRENT_USE_IPV6 udp::socket m_ipv6_sock; aux::handler_storage m_v6_read_handler_storage; #endif boost::uint16_t m_bind_port; boost::uint8_t m_v4_outstanding; boost::uint8_t m_restart_v4; #if TORRENT_USE_IPV6 boost::uint8_t m_v6_outstanding; boost::uint8_t m_restart_v6; #endif tcp::socket m_socks5_sock; deadline_timer m_retry_timer; aux::proxy_settings m_proxy_settings; tcp::resolver m_resolver; char m_tmp_buf[270]; bool m_queue_packets; bool m_tunnel_packets; bool m_force_proxy; bool m_abort; // this is the endpoint the proxy server lives at. // when performing a UDP associate, we get another // endpoint (presumably on the same IP) where we're // supposed to send UDP packets. udp::endpoint m_proxy_addr; // this is where UDP packets that are to be forwarded // are sent. The result from UDP ASSOCIATE is stored // in here. udp::endpoint m_udp_proxy_addr; // while we're connecting to the proxy // we have to queue the packets, we'll flush // them once we're connected std::deque m_queue; // counts the number of outstanding async // operations hanging on this socket int m_outstanding_ops; #if TORRENT_USE_IPV6 bool m_v6_write_subscribed:1; #endif bool m_v4_write_subscribed:1; #if TORRENT_USE_ASSERTS bool m_started; int m_magic; int m_outstanding_when_aborted; int m_outstanding_connect; int m_outstanding_timeout; int m_outstanding_resolve; int m_outstanding_socks; #endif }; struct rate_limited_udp_socket : public udp_socket { rate_limited_udp_socket(io_service& ios); void set_rate_limit(int limit) { m_rate_limit = limit; } bool send(udp::endpoint const& ep, char const* p, int len , error_code& ec, int flags = 0); bool has_quota(); private: int m_rate_limit; int m_quota; time_point m_last_tick; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/udp_tracker_connection.hpp000066400000000000000000000101161351156116000270040ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/udp_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_EXTRA_EXPORT udp_tracker_connection: public tracker_connection { friend class tracker_manager; public: udp_tracker_connection( io_service& ios , tracker_manager& man , tracker_request const& req , boost::weak_ptr c); void start(); void close(); boost::uint32_t transaction_id() const { return m_transaction_id; } private: enum action_t { action_connect, action_announce, action_scrape, action_error }; boost::shared_ptr shared_from_this() { return boost::static_pointer_cast( tracker_connection::shared_from_this()); } void update_transaction_id(); void name_lookup(error_code const& error , std::vector
const& addresses, int port); void timeout(error_code const& error); void start_announce(); bool on_receive(error_code const& e, udp::endpoint const& ep , char const* buf, int size); bool on_receive_hostname(error_code const& e, char const* hostname , char const* buf, int size); bool on_connect_response(char const* buf, int size); bool on_announce_response(char const* buf, int size); bool on_scrape_response(char const* buf, int size); // wraps tracker_connection::fail void fail(error_code const& ec, int code = -1 , char const* msg = "", int interval = 0, int min_interval = 0); void send_udp_connect(); void send_udp_announce(); void send_udp_scrape(); virtual void on_timeout(error_code const& ec); udp::endpoint pick_target_endpoint() const; std::string m_hostname; std::vector m_endpoints; struct connection_cache_entry { boost::int64_t connection_id; time_point expires; }; static std::map m_connection_cache; static mutex m_cache_mutex; udp::endpoint m_target; boost::uint32_t m_transaction_id; int m_attempts; // action_t boost::uint8_t m_state; bool m_abort; }; } #endif // TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/uncork_interface.hpp000066400000000000000000000044201351156116000256040ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_UNCORK_INTERFACE_HPP #define TORRENT_UNCORK_INTERFACE_HPP #include "libtorrent/export.hpp" namespace libtorrent { // the uncork interface is used by the disk_io_thread // to indicate that it has called all the disk job handlers // in the current batch. The intention is for the peer // connections to be able to not issue any sends on their // sockets until they have recevied all the disk jobs // that are ready first. This makes the networking more // efficient since it can send larger buffers down to the // kernel per system call. // uncorking refers to releasing the "cork" in the peers // preventing them to issue sends struct TORRENT_EXTRA_EXPORT uncork_interface { virtual void do_delayed_uncork() = 0; protected: ~uncork_interface() {} }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/union_endpoint.hpp000066400000000000000000000065671351156116000253310ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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_UNION_ENDPOINT_HPP_INCLUDED #define TORRENT_UNION_ENDPOINT_HPP_INCLUDED #include "libtorrent/socket.hpp" #include "libtorrent/address.hpp" namespace libtorrent { struct union_endpoint { union_endpoint(tcp::endpoint const& ep) { *this = ep; } union_endpoint(udp::endpoint const& ep) { *this = ep; } union_endpoint() { *this = tcp::endpoint(); } union_endpoint& operator=(udp::endpoint const& ep) { #if TORRENT_USE_IPV6 v4 = ep.address().is_v4(); if (v4) addr.v4 = ep.address().to_v4().to_bytes(); else addr.v6 = ep.address().to_v6().to_bytes(); #else addr.v4 = ep.address().to_v4().to_bytes(); #endif port = ep.port(); return *this; } operator udp::endpoint() const { #if TORRENT_USE_IPV6 if (v4) return udp::endpoint(address_v4(addr.v4), port); else return udp::endpoint(address_v6(addr.v6), port); #else return udp::endpoint(address_v4(addr.v4), port); #endif } union_endpoint& operator=(tcp::endpoint const& ep) { #if TORRENT_USE_IPV6 v4 = ep.address().is_v4(); if (v4) addr.v4 = ep.address().to_v4().to_bytes(); else addr.v6 = ep.address().to_v6().to_bytes(); #else addr.v4 = ep.address().to_v4().to_bytes(); #endif port = ep.port(); return *this; } libtorrent::address address() const { #if TORRENT_USE_IPV6 if (v4) return address_v4(addr.v4); else return address_v6(addr.v6); #else return address_v4(addr.v4); #endif } operator tcp::endpoint() const { #if TORRENT_USE_IPV6 if (v4) return tcp::endpoint(address_v4(addr.v4), port); else return tcp::endpoint(address_v6(addr.v6), port); #else return tcp::endpoint(address_v4(addr.v4), port); #endif } TORRENT_UNION addr_t { address_v4::bytes_type v4; #if TORRENT_USE_IPV6 address_v6::bytes_type v6; #endif } addr; boost::uint16_t port; #if TORRENT_USE_IPV6 bool v4:1; #endif }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/upnp.hpp000066400000000000000000000310361351156116000232500ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_UPNP_HPP #define TORRENT_UPNP_HPP #include "libtorrent/socket.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/deadline_timer.hpp" #include "libtorrent/enum_net.hpp" #include "libtorrent/resolver.hpp" #include #include #include #include #include #include namespace libtorrent { struct http_connection; class http_parser; namespace upnp_errors { // error codes for the upnp_error_category. They hold error codes // returned by UPnP routers when mapping ports enum error_code_enum { // No error no_error = 0, // One of the arguments in the request is invalid invalid_argument = 402, // The request failed action_failed = 501, // The specified value does not exist in the array value_not_in_array = 714, // The source IP address cannot be wild-carded, but // must be fully specified source_ip_cannot_be_wildcarded = 715, // The external port cannot be wildcarded, but must // be specified external_port_cannot_be_wildcarded = 716, // The port mapping entry specified conflicts with a // mapping assigned previously to another client port_mapping_conflict = 718, // Internal and external port value must be the same internal_port_must_match_external = 724, // The NAT implementation only supports permanent // lease times on port mappings only_permanent_leases_supported = 725, // RemoteHost must be a wildcard and cannot be a // specific IP addres or DNS name remote_host_must_be_wildcard = 726, // ExternalPort must be a wildcard and cannot be a // specific port external_port_must_be_wildcard = 727 }; // hidden TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); } // the boost.system error category for UPnP errors TORRENT_EXPORT boost::system::error_category& upnp_category(); #ifndef TORRENT_NO_DEPRECATED TORRENT_DEPRECATED_EXPORT boost::system::error_category& get_upnp_category(); #endif // int: port-mapping index // address: external address as queried from router // int: external port // int: protocol (UDP, TCP) // std::string: error message // an empty string as error means success typedef boost::function portmap_callback_t; typedef boost::function log_callback_t; struct parse_state { parse_state(): in_service(false) {} bool in_service; std::list tag_stack; std::string control_url; std::string service_type; std::string model; std::string url_base; bool top_tags(const char* str1, const char* str2) { std::list::reverse_iterator i = tag_stack.rbegin(); if (i == tag_stack.rend()) return false; if (!string_equal_no_case(i->c_str(), str2)) return false; ++i; if (i == tag_stack.rend()) return false; if (!string_equal_no_case(i->c_str(), str1)) return false; return true; } }; struct error_code_parse_state { error_code_parse_state(): in_error_code(false), exit(false), error_code(-1) {} bool in_error_code; bool exit; int error_code; }; struct ip_address_parse_state: error_code_parse_state { ip_address_parse_state(): in_ip_address(false) {} bool in_ip_address; std::string ip_address; }; TORRENT_EXTRA_EXPORT void find_control_url(int type, char const* string , int str_len, parse_state& state); TORRENT_EXTRA_EXPORT void find_error_code(int type, char const* string , int str_len, error_code_parse_state& state); TORRENT_EXTRA_EXPORT void find_ip_address(int type, char const* string , int str_len, ip_address_parse_state& state); // TODO: support using the windows API for UPnP operations as well class TORRENT_EXTRA_EXPORT upnp : public boost::enable_shared_from_this { public: upnp(io_service& ios , address const& listen_interface, std::string const& user_agent , portmap_callback_t const& cb, log_callback_t const& lcb , bool ignore_nonrouters); ~upnp(); void set_user_agent(std::string const& v) { m_user_agent = v; } void start(); enum protocol_type { none = 0, udp = 1, tcp = 2 }; // Attempts to add a port mapping for the specified protocol. Valid protocols are // ``upnp::tcp`` and ``upnp::udp`` for the UPnP class and ``natpmp::tcp`` and // ``natpmp::udp`` for the NAT-PMP class. // // ``external_port`` is the port on the external address that will be mapped. This // is a hint, you are not guaranteed that this port will be available, and it may // end up being something else. In the portmap_alert_ notification, the actual // external port is reported. // // ``local_port`` is the port in the local machine that the mapping should forward // to. // // The return value is an index that identifies this port mapping. This is used // to refer to mappings that fails or succeeds in the portmap_error_alert_ and // portmap_alert_ respectively. If The mapping fails immediately, the return value // is -1, which means failure. There will not be any error alert notification for // mappings that fail with a -1 return value. int add_mapping(protocol_type p, int external_port, tcp::endpoint local_ep); // This function removes a port mapping. ``mapping_index`` is the index that refers // to the mapping you want to remove, which was returned from add_mapping(). void delete_mapping(int mapping_index); bool get_mapping(int mapping_index, tcp::endpoint& local_ep, int& external_port, int& protocol) const; void discover_device(); void close(); // This is only available for UPnP routers. If the model is advertized by // the router, it can be queried through this function. std::string router_model() { mutex::scoped_lock l(m_mutex); return m_model; } private: boost::shared_ptr self() { return shared_from_this(); } void map_timer(error_code const& ec); void try_map_upnp(mutex::scoped_lock& l, bool timer = false); void discover_device_impl(mutex::scoped_lock& l); static address_v4 upnp_multicast_address; static udp::endpoint upnp_multicast_endpoint; // there are routers that's don't support timed // port maps, without returning error 725. It seems // safer to always assume that we have to ask for // permanent leases enum { default_lease_time = 0 }; void resend_request(error_code const& e); void on_reply(udp::endpoint const& from, char* buffer , std::size_t bytes_transferred); struct rootdevice; void next(rootdevice& d, int i, mutex::scoped_lock& l); void update_map(rootdevice& d, int i, mutex::scoped_lock& l); void on_upnp_xml(error_code const& e , libtorrent::http_parser const& p, rootdevice& d , http_connection& c); void on_upnp_get_ip_address_response(error_code const& e , libtorrent::http_parser const& p, rootdevice& d , http_connection& c); void on_upnp_map_response(error_code const& e , libtorrent::http_parser const& p, rootdevice& d , int mapping, http_connection& c); void on_upnp_unmap_response(error_code const& e , libtorrent::http_parser const& p, rootdevice& d , int mapping, http_connection& c); void on_expire(error_code const& e); void disable(error_code const& ec, mutex::scoped_lock& l); void return_error(int mapping, int code, mutex::scoped_lock& l); void log(char const* msg, mutex::scoped_lock& l); void get_ip_address(rootdevice& d); void delete_port_mapping(rootdevice& d, int i); void create_port_mapping(http_connection& c, rootdevice& d, int i); void post(upnp::rootdevice const& d, char const* soap , char const* soap_action, mutex::scoped_lock& l); int num_mappings() const { return int(m_mappings.size()); } struct global_mapping_t { global_mapping_t() : protocol(none) , external_port(0) {} int protocol; int external_port; tcp::endpoint local_ep; }; struct mapping_t { enum action_t { action_none, action_add, action_delete }; mapping_t() : action(action_none) , external_port(0) , protocol(none) , failcount(0) {} // the time the port mapping will expire time_point expires; // the local port for this mapping. The port is set // to 0, the mapping is not in use tcp::endpoint local_ep; int action; // the external (on the NAT router) port // for the mapping. This is the port we // should announce to others int external_port; // 2 = udp, 1 = tcp int protocol; // the number of times this mapping has failed int failcount; }; struct rootdevice { rootdevice(); #if TORRENT_USE_ASSERTS ~rootdevice(); #if __cplusplus >= 201103L rootdevice(rootdevice const&); rootdevice& operator=(rootdevice const&); #endif #endif // the interface url, through which the list of // supported interfaces are fetched std::string url; // the url to the WANIP or WANPPP interface std::string control_url; // either the WANIP namespace or the WANPPP namespace std::string service_namespace; std::vector mapping; // this is the hostname, port and path // component of the url or the control_url // if it has been found std::string hostname; int port; std::string path; address external_ip; int lease_duration; // true if the device supports specifying a // specific external port, false if it doesn't bool supports_specific_external; bool disabled; // this is true if the IP of this device is not // one of our default routes. i.e. it may be someone // else's router, we just happen to have multicast // enabled across networks // this is only relevant if ignore_non_routers is set. bool non_router; mutable boost::shared_ptr upnp_connection; #if TORRENT_USE_ASSERTS int magic; #endif void close() const; bool operator<(rootdevice const& rhs) const { return url < rhs.url; } }; struct upnp_state_t { std::vector mappings; std::set devices; }; std::vector m_mappings; std::string m_user_agent; // the set of devices we've found std::set m_devices; portmap_callback_t m_callback; log_callback_t m_log_callback; // current retry count int m_retry_count; io_service& m_io_service; resolver m_resolver; // the udp socket used to send and receive // multicast messages on the network broadcast_socket m_socket; // used to resend udp packets in case // they time out deadline_timer m_broadcast_timer; // timer used to refresh mappings deadline_timer m_refresh_timer; // this timer fires one second after the last UPnP response. This is the // point where we assume we have received most or all SSDP reponses. If we // are ignoring non-routers and at this point we still haven't received a // response from a router UPnP device, we override the ignoring behavior and // map them anyway. deadline_timer m_map_timer; bool m_disabled; bool m_closing; bool m_ignore_non_routers; mutex m_mutex; std::string m_model; // cache of interfaces mutable std::vector m_interfaces; mutable time_point m_last_if_update; }; } namespace boost { namespace system { template<> struct is_error_code_enum { static const bool value = true; }; } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/utf8.hpp000066400000000000000000000054151351156116000231560ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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_UTF8_HPP_INCLUDED #define TORRENT_UTF8_HPP_INCLUDED #include "libtorrent/export.hpp" // on windows we need these functions for // convert_to_native and convert_from_native #if TORRENT_USE_WSTRING || defined TORRENT_WINDOWS #include #include #include namespace libtorrent { // internal // results from UTF-8 conversion functions utf8_wchar and // wchar_utf8 enum utf8_conv_result_t { // conversion successful conversion_ok, // partial character in source, but hit end source_exhausted, // insuff. room in target for conversion target_exhausted, // source sequence is illegal/malformed source_illegal }; // ``utf8_wchar`` converts a UTF-8 string (``utf8``) to a wide character // string (``wide``). ``wchar_utf8`` converts a wide character string // (``wide``) to a UTF-8 string (``utf8``). The return value is one of // the enumeration values from utf8_conv_result_t. TORRENT_EXTRA_EXPORT utf8_conv_result_t utf8_wchar( const std::string &utf8, std::wstring &wide); TORRENT_EXTRA_EXPORT utf8_conv_result_t wchar_utf8( const std::wstring &wide, std::string &utf8); TORRENT_EXTRA_EXPORT std::pair parse_utf8_codepoint(char const* str, int len); } #endif // !BOOST_NO_STD_WSTRING #endif libtorrent-rasterbar-1.1.13/include/libtorrent/utp_socket_manager.hpp000066400000000000000000000144731351156116000261460ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_UTP_SOCKET_MANAGER_HPP_INCLUDED #define TORRENT_UTP_SOCKET_MANAGER_HPP_INCLUDED #include #include "libtorrent/socket_type.hpp" #include "libtorrent/session_status.hpp" #include "libtorrent/enum_net.hpp" #include "libtorrent/aux_/session_settings.hpp" namespace libtorrent { class udp_socket; class utp_stream; struct utp_socket_impl; struct counters; typedef boost::function const&)> incoming_utp_callback_t; struct utp_socket_manager TORRENT_FINAL : udp_socket_observer { utp_socket_manager(aux::session_settings const& sett, udp_socket& s , counters& cnt, void* ssl_context, incoming_utp_callback_t cb); ~utp_socket_manager(); // return false if this is not a uTP packet virtual bool incoming_packet(error_code const& ec, udp::endpoint const& ep , char const* p, int size) TORRENT_OVERRIDE; virtual bool incoming_packet(error_code const&, char const*, char const*, int) TORRENT_OVERRIDE { return false; } virtual void writable() TORRENT_OVERRIDE; virtual void socket_drained() TORRENT_OVERRIDE; void tick(time_point now); tcp::endpoint local_endpoint(address const& remote, error_code& ec) const; int local_port(error_code& ec) const; // flags for send_packet enum { dont_fragment = 1 }; void send_packet(udp::endpoint const& ep, char const* p, int len , error_code& ec, int flags = 0); void subscribe_writable(utp_socket_impl* s); // internal, used by utp_stream void remove_socket(boost::uint16_t id); utp_socket_impl* new_utp_socket(utp_stream* str); int gain_factor() const { return m_sett.get_int(settings_pack::utp_gain_factor); } int target_delay() const { return m_sett.get_int(settings_pack::utp_target_delay) * 1000; } int syn_resends() const { return m_sett.get_int(settings_pack::utp_syn_resends); } int fin_resends() const { return m_sett.get_int(settings_pack::utp_fin_resends); } int num_resends() const { return m_sett.get_int(settings_pack::utp_num_resends); } int connect_timeout() const { return m_sett.get_int(settings_pack::utp_connect_timeout); } int min_timeout() const { return m_sett.get_int(settings_pack::utp_min_timeout); } int loss_multiplier() const { return m_sett.get_int(settings_pack::utp_loss_multiplier); } int cwnd_reduce_timer() const { return m_sett.get_int(settings_pack::utp_cwnd_reduce_timer); } void mtu_for_dest(address const& addr, int& link_mtu, int& utp_mtu); void set_sock_buf(int size); int num_sockets() const { return m_utp_sockets.size(); } void defer_ack(utp_socket_impl* s); void subscribe_drained(utp_socket_impl* s); void restrict_mtu(int mtu) { m_restrict_mtu[m_mtu_idx] = mtu; m_mtu_idx = (m_mtu_idx + 1) % m_restrict_mtu.size(); } int restrict_mtu() const { return *std::max_element(m_restrict_mtu.begin(), m_restrict_mtu.end()); } // used to keep stats of uTP events // the counter is the enum from ``counters``. void inc_stats_counter(int counter, int delta = 1); private: // explicitly disallow assignment, to silence msvc warning utp_socket_manager& operator=(utp_socket_manager const&); udp_socket& m_sock; incoming_utp_callback_t m_cb; // replace with a hash-map typedef std::multimap socket_map_t; socket_map_t m_utp_sockets; // if this is set, it means this socket still needs to send an ACK. Once // we exit the loop processing packets, or switch to processing packets // for a different socket, issue the ACK packet and clear this. utp_socket_impl* m_deferred_ack; // sockets that have received or sent packets this // round, may subscribe to the event of draining the // UDP socket. At that point they may call the // user callback function to indicate bytes have been // sent or received. std::vector m_drained_event; // list of sockets that received EWOULDBLOCK from the // underlying socket. They are notified when the socket // becomes writable again std::vector m_stalled_sockets; // the last socket we received a packet on utp_socket_impl* m_last_socket; int m_new_connection; aux::session_settings const& m_sett; // this is a copy of the routing table, used // to initialize MTU sizes of uTP sockets mutable std::vector m_routes; // the timestamp for the last time we updated // the routing table mutable time_point m_last_route_update; // cache of interfaces mutable std::vector m_interfaces; mutable time_point m_last_if_update; // the buffer size of the socket. This is used // to now lower the buffer size int m_sock_buf_size; // stats counters counters& m_counters; boost::array m_restrict_mtu; int m_mtu_idx; // this is passed on to the instantiate connection // if this is non-null it will create SSL connections over uTP void* m_ssl_context; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/utp_stream.hpp000066400000000000000000000370061351156116000244540ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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_UTP_STREAM_HPP_INCLUDED #define TORRENT_UTP_STREAM_HPP_INCLUDED #include "libtorrent/proxy_base.hpp" #include "libtorrent/udp_socket.hpp" #include "libtorrent/io.hpp" #include "libtorrent/packet_buffer.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #ifndef BOOST_NO_EXCEPTIONS #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #define CCONTROL_TARGET 100 namespace libtorrent { #ifndef TORRENT_UTP_LOG_ENABLE #define TORRENT_UTP_LOG 0 #define TORRENT_VERBOSE_UTP_LOG 0 #else #define TORRENT_UTP_LOG 1 #define TORRENT_VERBOSE_UTP_LOG 1 #endif #if TORRENT_UTP_LOG TORRENT_EXPORT bool is_utp_stream_logging(); // This function should be used at the very beginning and very end of your program. TORRENT_EXPORT void set_utp_stream_logging(bool enable); #endif TORRENT_EXTRA_EXPORT bool compare_less_wrap(boost::uint32_t lhs , boost::uint32_t rhs, boost::uint32_t mask); struct utp_socket_manager; // internal: some MTU and protocol header sizes constants enum { TORRENT_IPV4_HEADER = 20, TORRENT_IPV6_HEADER = 40, TORRENT_UDP_HEADER = 8, TORRENT_SOCKS5_HEADER = 6, // plus the size of the destination address TORRENT_ETHERNET_MTU = 1500, TORRENT_TEREDO_MTU = 1280, TORRENT_INET_MIN_MTU = 576, TORRENT_INET_MAX_MTU = 0xffff }; // internal: the point of the bif_endian_int is two-fold // one purpose is to not have any alignment requirements // so that any buffer received from the network can be cast // to it and read as an integer of various sizes without // triggering a bus error. The other purpose is to convert // from network byte order to host byte order when read and // written, to offer a convenient interface to both interpreting // and writing network packets template struct big_endian_int { big_endian_int& operator=(T v) { char* p = m_storage; detail::write_impl(v, p); return *this; } operator T() const { const char* p = m_storage; return detail::read_impl(p, detail::type()); } private: char m_storage[sizeof(T)]; }; typedef big_endian_int be_uint64; typedef big_endian_int be_uint32; typedef big_endian_int be_uint16; typedef big_endian_int be_int64; typedef big_endian_int be_int32; typedef big_endian_int be_int16; /* uTP header from BEP 29 0 4 8 16 24 32 +-------+-------+---------------+---------------+---------------+ | type | ver | extension | connection_id | +-------+-------+---------------+---------------+---------------+ | timestamp_microseconds | +---------------+---------------+---------------+---------------+ | timestamp_difference_microseconds | +---------------+---------------+---------------+---------------+ | wnd_size | +---------------+---------------+---------------+---------------+ | seq_nr | ack_nr | +---------------+---------------+---------------+---------------+ */ // internal: the different kinds of uTP packets enum utp_socket_state_t { ST_DATA, ST_FIN, ST_STATE, ST_RESET, ST_SYN, NUM_TYPES }; // internal: extension headers. 2 is skipped because there is a deprecated // extension with that number in the wild enum utp_extensions_t { utp_no_extension = 0, utp_sack = 1, utp_close_reason = 3 }; struct utp_header { unsigned char type_ver; unsigned char extension; be_uint16 connection_id; be_uint32 timestamp_microseconds; be_uint32 timestamp_difference_microseconds; be_uint32 wnd_size; be_uint16 seq_nr; be_uint16 ack_nr; int get_type() const { return type_ver >> 4; } int get_version() const { return type_ver & 0xf; } }; struct utp_socket_impl; utp_socket_impl* construct_utp_impl(boost::uint16_t recv_id , boost::uint16_t send_id, void* userdata , utp_socket_manager* sm); void detach_utp_impl(utp_socket_impl* s); void delete_utp_impl(utp_socket_impl* s); bool should_delete(utp_socket_impl* s); void tick_utp_impl(utp_socket_impl* s, time_point now); void utp_init_mtu(utp_socket_impl* s, int link_mtu, int utp_mtu); bool utp_incoming_packet(utp_socket_impl* s, char const* p , int size, udp::endpoint const& ep, time_point receive_time); bool utp_match(utp_socket_impl* s, udp::endpoint const& ep, boost::uint16_t id); udp::endpoint utp_remote_endpoint(utp_socket_impl* s); boost::uint16_t utp_receive_id(utp_socket_impl* s); int utp_socket_state(utp_socket_impl const* s); void utp_send_ack(utp_socket_impl* s); void utp_socket_drained(utp_socket_impl* s); void utp_writable(utp_socket_impl* s); // this is the user-level stream interface to utp sockets. // the reason why it's split up in a utp_stream class and // an implementation class is because the socket state has // to be able to out-live the user level socket. For instance // when sending data on a stream and then closing it, the // state holding the send buffer has to be kept around until // it has been flushed, which may be longer than the client // will keep the utp_stream object around for. // for more details, see utp_socket_impl, which is analogous // to the kernel state for a socket. It's defined in utp_stream.cpp class TORRENT_EXTRA_EXPORT utp_stream { public: typedef utp_stream lowest_layer_type; typedef tcp::socket::endpoint_type endpoint_type; typedef tcp::socket::protocol_type protocol_type; #if BOOST_VERSION >= 106600 typedef tcp::socket::executor_type executor_type; executor_type get_executor() { return m_io_service.get_executor(); } #endif explicit utp_stream(io_service& io_service); ~utp_stream(); lowest_layer_type& lowest_layer() { return *this; } // used for incoming connections void set_impl(utp_socket_impl* s); utp_socket_impl* get_impl(); #ifndef BOOST_NO_EXCEPTIONS template void io_control(IO_Control_Command&) {} #endif template void io_control(IO_Control_Command&, error_code&) {} #ifndef BOOST_NO_EXCEPTIONS void non_blocking(bool) {} #endif error_code non_blocking(bool, error_code&) { return error_code(); } #ifndef BOOST_NO_EXCEPTIONS void bind(endpoint_type const& /*endpoint*/) {} #endif void bind(endpoint_type const&, error_code&); #ifndef BOOST_NO_EXCEPTIONS template void set_option(SettableSocketOption const&) {} #endif template error_code set_option(SettableSocketOption const&, error_code& ec) { return ec; } #ifndef BOOST_NO_EXCEPTIONS template void get_option(GettableSocketOption&) {} #endif template error_code get_option(GettableSocketOption&, error_code& ec) { return ec; } error_code cancel(error_code&) { cancel_handlers(boost::asio::error::operation_aborted); return error_code(); } void close(); void close(error_code const& /*ec*/) { close(); } void set_close_reason(boost::uint16_t code); boost::uint16_t get_close_reason(); bool is_open() const { return m_open; } int read_buffer_size() const; static void on_read(void* self, size_t bytes_transferred , error_code const& ec, bool kill); static void on_write(void* self, size_t bytes_transferred , error_code const& ec, bool kill); static void on_connect(void* self, error_code const& ec, bool kill); static void on_close_reason(void* self, boost::uint16_t reason); void add_read_buffer(void* buf, size_t len); void issue_read(); void add_write_buffer(void const* buf, size_t len); void issue_write(); size_t read_some(bool clear_buffers); int send_delay() const; int recv_delay() const; void do_connect(tcp::endpoint const& ep); endpoint_type local_endpoint() const { error_code ec; return local_endpoint(ec); } endpoint_type local_endpoint(error_code& ec) const; endpoint_type remote_endpoint() const { error_code ec; return remote_endpoint(ec); } endpoint_type remote_endpoint(error_code& ec) const; std::size_t available() const; std::size_t available(error_code& /*ec*/) const { return available(); } io_service& get_io_service() { return m_io_service; } template void async_connect(endpoint_type const& endpoint, Handler const& handler) { if (!endpoint.address().is_v4()) { m_io_service.post(boost::bind(handler, boost::asio::error::operation_not_supported, 0)); return; } if (m_impl == 0) { m_io_service.post(boost::bind(handler, boost::asio::error::not_connected, 0)); return; } m_connect_handler = handler; do_connect(endpoint); } template void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) { if (m_impl == 0) { m_io_service.post(boost::bind(handler, boost::asio::error::not_connected, 0)); return; } TORRENT_ASSERT(!m_read_handler); if (m_read_handler) { m_io_service.post(boost::bind(handler, boost::asio::error::operation_not_supported, 0)); return; } std::size_t bytes_added = 0; #if BOOST_VERSION >= 106600 for (auto i = buffer_sequence_begin(buffers) , end(buffer_sequence_end(buffers)); i != end; ++i) #else for (typename Mutable_Buffers::const_iterator i = buffers.begin() , end(buffers.end()); i != end; ++i) #endif { if (buffer_size(*i) == 0) continue; using boost::asio::buffer_cast; using boost::asio::buffer_size; add_read_buffer(buffer_cast(*i), buffer_size(*i)); bytes_added += buffer_size(*i); } if (bytes_added == 0) { // if we're reading 0 bytes, post handler immediately // asio's SSL layer depends on this behavior m_io_service.post(boost::bind(handler, error_code(), 0)); return; } m_read_handler = handler; issue_read(); } template void async_read_some(null_buffers const&, Handler const& handler) { if (m_impl == 0) { m_io_service.post(boost::bind(handler, boost::asio::error::not_connected, 0)); return; } TORRENT_ASSERT(!m_read_handler); if (m_read_handler) { TORRENT_ASSERT(false); // we should never do this! m_io_service.post(boost::bind(handler, boost::asio::error::operation_not_supported, 0)); return; } m_read_handler = handler; issue_read(); } void do_async_connect(endpoint_type const& ep , boost::function const& handler); template void open(Protocol const&, error_code&) { m_open = true; } template void open(Protocol const&) { m_open = true; } template std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) { TORRENT_ASSERT(!m_read_handler); if (m_impl == 0) { ec = boost::asio::error::not_connected; return 0; } if (read_buffer_size() == 0) { ec = boost::asio::error::would_block; return 0; } #if TORRENT_USE_ASSERTS size_t buf_size = 0; #endif #if BOOST_VERSION >= 106600 for (auto i = buffer_sequence_begin(buffers) , end(buffer_sequence_end(buffers)); i != end; ++i) #else for (typename Mutable_Buffers::const_iterator i = buffers.begin() , end(buffers.end()); i != end; ++i) #endif { using boost::asio::buffer_cast; using boost::asio::buffer_size; add_read_buffer(buffer_cast(*i), buffer_size(*i)); #if TORRENT_USE_ASSERTS buf_size += buffer_size(*i); #endif } std::size_t ret = read_some(true); TORRENT_ASSERT(ret <= buf_size); TORRENT_ASSERT(ret > 0); return ret; } template std::size_t write_some(Const_Buffers const& /* buffers */, error_code& /* ec */) { TORRENT_ASSERT(false && "not implemented!"); // TODO: implement blocking write. Low priority since it's not used (yet) return 0; } #ifndef BOOST_NO_EXCEPTIONS template std::size_t read_some(Mutable_Buffers const& buffers) { error_code ec; std::size_t ret = read_some(buffers, ec); if (ec) boost::throw_exception(boost::system::system_error(ec)); return ret; } template std::size_t write_some(Const_Buffers const& buffers) { error_code ec; std::size_t ret = write_some(buffers, ec); if (ec) boost::throw_exception(boost::system::system_error(ec)); return ret; } #endif template void async_write_some(Const_Buffers const& buffers, Handler const& handler) { if (m_impl == 0) { m_io_service.post(boost::bind(handler , boost::asio::error::not_connected, 0)); return; } TORRENT_ASSERT(!m_write_handler); if (m_write_handler) { m_io_service.post(boost::bind(handler , boost::asio::error::operation_not_supported, 0)); return; } std::size_t bytes_added = 0; #if BOOST_VERSION >= 106600 for (auto i = buffer_sequence_begin(buffers) , end(buffer_sequence_end(buffers)); i != end; ++i) #else for (typename Const_Buffers::const_iterator i = buffers.begin() , end(buffers.end()); i != end; ++i) #endif { if (buffer_size(*i) == 0) continue; using boost::asio::buffer_cast; using boost::asio::buffer_size; add_write_buffer(buffer_cast(*i), buffer_size(*i)); bytes_added += buffer_size(*i); } if (bytes_added == 0) { // if we're writing 0 bytes, post handler immediately // asio's SSL layer depends on this behavior m_io_service.post(boost::bind(handler, error_code(), 0)); return; } m_write_handler = handler; issue_write(); } private: // explicitly disallow assignment, to silence msvc warning utp_stream& operator=(utp_stream const&); void cancel_handlers(error_code const&); boost::function1 m_connect_handler; boost::function2 m_read_handler; boost::function2 m_write_handler; io_service& m_io_service; utp_socket_impl* m_impl; boost::uint16_t m_incoming_close_reason; // this field requires another 8 bytes (including padding) bool m_open; }; } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/vector_utils.hpp000066400000000000000000000045571351156116000250200ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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_VECTOR_UTILS_HPP_INCLUDE #define TORRENT_VECTOR_UTILS_HPP_INCLUDE #include #include namespace libtorrent { template typename std::vector::iterator sorted_find(std::vector& container , T v) { typename std::vector::iterator i = std::lower_bound(container.begin() , container.end(), v); if (i == container.end()) return container.end(); if (*i != v) return container.end(); return i; } template typename std::vector::const_iterator sorted_find(std::vector const& container , T const* v) { return sorted_find(const_cast&>(container) , const_cast(v)); } template void sorted_insert(std::vector& container, T v) { typename std::vector::iterator i = std::lower_bound(container.begin() , container.end(), v); container.insert(i, v); } } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/version.hpp000066400000000000000000000042561351156116000237570ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include "libtorrent/export.hpp" #define LIBTORRENT_VERSION_MAJOR 1 #define LIBTORRENT_VERSION_MINOR 1 #define LIBTORRENT_VERSION_TINY 13 // the format of this version is: MMmmtt // M = Major version, m = minor version, t = tiny version #define LIBTORRENT_VERSION_NUM ((LIBTORRENT_VERSION_MAJOR * 10000) + (LIBTORRENT_VERSION_MINOR * 100) + LIBTORRENT_VERSION_TINY) #define LIBTORRENT_VERSION "1.1.13.0" #define LIBTORRENT_REVISION "9b0ebc207" namespace libtorrent { // returns the libtorrent version as string form in this format: // "..." TORRENT_EXPORT char const* version(); } #endif libtorrent-rasterbar-1.1.13/include/libtorrent/web_connection_base.hpp000066400000000000000000000113261351156116000262540ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 WEB_CONNECTION_BASE_HPP_INCLUDED #define WEB_CONNECTION_BASE_HPP_INCLUDED #include "libtorrent/debug.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #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/peer_request.hpp" #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/config.hpp" // parse_url #include "libtorrent/tracker_manager.hpp" #include "libtorrent/http_parser.hpp" namespace libtorrent { class torrent; class TORRENT_EXTRA_EXPORT web_connection_base : 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_connection_base(peer_connection_args const& pack , web_seed_t& web); virtual int timeout() const; void start(); ~web_connection_base(); // called from the main loop when this connection has any // work to do. void on_sent(error_code const& error , std::size_t bytes_transferred); virtual std::string const& url() const = 0; bool in_handshake() const; // the following functions appends messages // to the send buffer void write_choke() {} void write_unchoke() {} void write_interested() {} void write_not_interested() {} virtual void write_request(peer_request const&) = 0; void write_cancel(peer_request const&) {} void write_have(int) {} void write_dont_have(int) {} void write_piece(peer_request const&, disk_buffer_holder&) { TORRENT_ASSERT(false); } void write_keepalive() {} void on_connected(); void write_reject_request(peer_request const&) {} void write_allow_fast(int) {} void write_suggest(int) {} void write_bitfield() {} #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif virtual void get_specific_peer_info(peer_info& p) const; protected: virtual void add_headers(std::string& request , aux::session_settings const& sett, bool using_proxy) const; // 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; // true if we're using ssl bool m_ssl; // this has one entry per bittorrent request std::deque m_requests; std::string m_server_string; std::string m_basic_auth; std::string m_host; std::string m_path; std::string m_external_auth; web_seed_entry::headers_t m_extra_headers; http_parser m_parser; int m_port; // the number of bytes into the receive buffer where // current read cursor is. int m_body_start; }; } #endif // TORRENT_WEB_CONNECTION_BASE_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/web_peer_connection.hpp000066400000000000000000000123701351156116000262750ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/web_connection_base.hpp" #include "libtorrent/disk_buffer_holder.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/http_parser.hpp" #include "libtorrent/operations.hpp" // for operation_t enum namespace libtorrent { class torrent; class TORRENT_EXTRA_EXPORT web_peer_connection : public web_connection_base { 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(peer_connection_args const& pack , web_seed_t& web); virtual void on_connected() TORRENT_OVERRIDE; virtual int type() const TORRENT_OVERRIDE { return peer_connection::url_seed_connection; } // called from the main loop when this connection has any // work to do. virtual void on_receive(error_code const& error , std::size_t bytes_transferred) TORRENT_OVERRIDE; std::string const& url() const TORRENT_OVERRIDE { return m_url; } virtual void get_specific_peer_info(peer_info& p) const TORRENT_OVERRIDE; virtual void disconnect(error_code const& ec , operation_t op, int error = 0) TORRENT_OVERRIDE; virtual void write_request(peer_request const& r) TORRENT_OVERRIDE; virtual bool received_invalid_data(int index, bool single_peer) TORRENT_OVERRIDE; private: void on_receive_padfile(); void incoming_payload(char const* buf, int len); void incoming_zeroes(int len); void handle_redirect(int bytes_left); void handle_error(int bytes_left); void maybe_harvest_piece(); // 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 downloading_piece_progress() const TORRENT_OVERRIDE; void handle_padfile(); // this has one entry per http-request // (might be more than the bt requests) struct file_request_t { int file_index; int length; boost::int64_t start; }; std::deque m_file_requests; std::string m_url; web_seed_t* m_web; // this is used for intermediate storage of pieces to be delivered to the // bittorrent engine // TODO: 3 if we make this be a disk_buffer_holder instead // we would save a copy // use allocate_disk_receive_buffer and release_disk_receive_buffer std::vector m_piece; // the number of bytes we've forwarded to the incoming_payload() function // in the current HTTP response. used to know where in the buffer the // next response starts int m_received_body; // this is the offset inside the current receive // buffer where the next chunk header will be. // this is updated for each chunk header that's // parsed. It does not necessarily point to a valid // offset in the receive buffer, if we haven't received // it yet. This offset never includes the HTTP header int m_chunk_pos; // this is the number of bytes we've already received // from the next chunk header we're waiting for int m_partial_chunk_header; // the number of responses we've received so far on // this connection int m_num_responses; }; } #endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED libtorrent-rasterbar-1.1.13/include/libtorrent/xml_parse.hpp000066400000000000000000000047771351156116000242740ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_XML_PARSE_HPP #define TORRENT_XML_PARSE_HPP #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { enum { xml_start_tag, xml_end_tag, xml_empty_tag, xml_declaration_tag, xml_string, xml_attribute, xml_comment, xml_parse_error, // used for tags that don't follow the convention of // key-value pairs inside the tag brackets. Like !DOCTYPE xml_tag_content }; // callback(int type, char const* name, int name_len // , char const* val, int val_len) // name is element or attribute name // val is attribute value // neither string is null terminated, but their lengths are specified via // name_len and val_len respectively TORRENT_EXTRA_EXPORT void xml_parse(char const* p, char const* end , boost::function callback); } #endif libtorrent-rasterbar-1.1.13/libtorrent-rasterbar.pc.in000066400000000000000000000006761351156116000230540ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ bindir=@bindir@ libdir=@libdir@ datarootdir=@datarootdir@ datadir=@datadir@ sysconfdir=@sysconfdir@ includedir=@includedir@ package=@PACKAGE@ Name: libtorrent-rasterbar Description: Bittorrent library. Version: @VERSION@ Libs: -L${libdir} -ltorrent-rasterbar @BOOST_SYSTEM_LIB@ Libs.private: @LIBS@ @PTHREAD_LIBS@ @OPENSSL_LIBS@ Cflags: -I${includedir} -I${includedir}/libtorrent @COMPILETIME_OPTIONS@ libtorrent-rasterbar-1.1.13/m4/000077500000000000000000000000001351156116000162635ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/m4/ax_boost_base.m4000066400000000000000000000257701351156116000213500ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_boost_base.html # =========================================================================== # # SYNOPSIS # # AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # DESCRIPTION # # Test for the Boost C++ libraries of a particular version (or newer) # # If no path to the installed boost library is given the macro searchs # under /usr, /usr/local, /opt and /opt/local and evaluates the # $BOOST_ROOT environment variable. Further documentation is available at # . # # This macro calls: # # AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) # # And sets: # # HAVE_BOOST # # LICENSE # # Copyright (c) 2008 Thomas Porschberg # Copyright (c) 2009 Peter Adolphs # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 26 AC_DEFUN([AX_BOOST_BASE], [ AC_ARG_WITH([boost], [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], [use Boost library from a standard location (ARG=yes), from the specified location (ARG=), or disable it (ARG=no) @<:@ARG=yes@:>@ ])], [ if test "$withval" = "no"; then want_boost="no" elif test "$withval" = "yes"; then want_boost="yes" ac_boost_path="" else want_boost="yes" ac_boost_path="$withval" fi ], [want_boost="yes"]) AC_ARG_WITH([boost-libdir], AS_HELP_STRING([--with-boost-libdir=LIB_DIR], [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), [ if test -d "$withval" then ac_boost_lib_path="$withval" else AC_MSG_ERROR(--with-boost-libdir expected directory name) fi ], [ac_boost_lib_path=""] ) if test "x$want_boost" = "xyes"; then boost_lib_version_req=ifelse([$1], ,1.20.0,$1) boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` if test "x$boost_lib_version_req_sub_minor" = "x" ; then boost_lib_version_req_sub_minor="0" fi WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) succeeded=no dnl On 64-bit systems check for system libraries in both lib64 and lib. dnl The former is specified by FHS, but e.g. Debian does not adhere to dnl this (as it rises problems for generic multi-arch support). dnl The last entry in the list is chosen by default when no libraries dnl are found, e.g. when only header-only libraries are installed! libsubdirs="lib" ax_arch=`uname -m` case $ax_arch in x86_64) libsubdirs="lib64 libx32 lib lib64" ;; ppc64|s390x|sparc64|aarch64|ppc64le) libsubdirs="lib64 lib lib64 ppc64le" ;; esac dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give dnl them priority over the other paths since, if libs are found there, they dnl are almost assuredly the ones desired. AC_REQUIRE([AC_CANONICAL_HOST]) libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" case ${host_cpu} in i?86) libsubdirs="lib/i386-${host_os} $libsubdirs" ;; esac dnl first we check the system location for boost libraries dnl this location ist chosen if boost libraries are installed with the --layout=system option dnl or if you install boost with RPM if test "$ac_boost_path" != ""; then BOOST_CPPFLAGS="-I$ac_boost_path/include" for ac_boost_path_tmp in $libsubdirs; do if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" break fi done elif test "$cross_compiling" != yes; then for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then for libsubdir in $libsubdirs ; do if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" break; fi done fi dnl overwrite ld flags if we have required special directory with dnl --with-boost-libdir parameter if test "$ac_boost_lib_path" != ""; then BOOST_LDFLAGS="-L$ac_boost_lib_path" fi CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS AC_REQUIRE([AC_PROG_CXX]) AC_LANG_PUSH(C++) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ #if BOOST_VERSION >= $WANT_BOOST_VERSION // Everything is okay #else # error Boost version is too old #endif ]])],[ AC_MSG_RESULT(yes) succeeded=yes found_system=yes ],[ ]) AC_LANG_POP([C++]) dnl if we found no boost with system layout we search for boost libraries dnl built and installed without the --layout=system option or for a staged(not installed) version if test "x$succeeded" != "xyes"; then CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" BOOST_CPPFLAGS= BOOST_LDFLAGS= _version=0 if test "$ac_boost_path" != ""; then if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` V_CHECK=`expr $_version_tmp \> $_version` if test "$V_CHECK" = "1" ; then _version=$_version_tmp fi VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" done dnl if nothing found search for layout used in Windows distributions if test -z "$BOOST_CPPFLAGS"; then if test -d "$ac_boost_path/boost" && test -r "$ac_boost_path/boost"; then BOOST_CPPFLAGS="-I$ac_boost_path" fi fi fi else if test "$cross_compiling" != yes; then for ac_boost_path in /usr /usr/local /opt /opt/local ; do if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` V_CHECK=`expr $_version_tmp \> $_version` if test "$V_CHECK" = "1" ; then _version=$_version_tmp best_path=$ac_boost_path fi done fi done VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" if test "$ac_boost_lib_path" = ""; then for libsubdir in $libsubdirs ; do if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done BOOST_LDFLAGS="-L$best_path/$libsubdir" fi fi if test "x$BOOST_ROOT" != "x"; then for libsubdir in $libsubdirs ; do if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` V_CHECK=`expr $stage_version_shorten \>\= $_version` if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) BOOST_CPPFLAGS="-I$BOOST_ROOT" BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" fi fi fi fi CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS AC_LANG_PUSH(C++) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ #if BOOST_VERSION >= $WANT_BOOST_VERSION // Everything is okay #else # error Boost version is too old #endif ]])],[ AC_MSG_RESULT(yes) succeeded=yes found_system=yes ],[ ]) AC_LANG_POP([C++]) fi if test "$succeeded" != "yes" ; then if test "$_version" = "0" ; then AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) else AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) fi # execute ACTION-IF-NOT-FOUND (if present): ifelse([$3], , :, [$3]) else AC_SUBST(BOOST_CPPFLAGS) AC_SUBST(BOOST_LDFLAGS) AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) # execute ACTION-IF-FOUND (if present): ifelse([$2], , :, [$2]) fi CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" fi ]) libtorrent-rasterbar-1.1.13/m4/ax_boost_chrono.m4000066400000000000000000000104021351156116000217100ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_boost_chrono.html # =========================================================================== # # SYNOPSIS # # AX_BOOST_CHRONO # # DESCRIPTION # # Test for System library from the Boost C++ libraries. The macro requires # a preceding call to AX_BOOST_BASE. Further documentation is available at # . # # This macro calls: # # AC_SUBST(BOOST_CHRONO_LIB) # # And sets: # # HAVE_BOOST_CHRONO # # LICENSE # # Copyright (c) 2012 Xiyue Deng # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 1 AC_DEFUN([AX_BOOST_CHRONO], [ AC_ARG_WITH([boost-chrono], AS_HELP_STRING([--with-boost-chrono@<:@=special-lib@:>@], [use the Chrono library from boost - it is possible to specify a certain library for the linker e.g. --with-boost-chrono=boost_chrono-gcc-mt ]), [ if test "$withval" = "no"; then want_boost="no" elif test "$withval" = "yes"; then want_boost="yes" ax_boost_user_chrono_lib="" else want_boost="yes" ax_boost_user_chrono_lib="$withval" fi ], [want_boost="yes"] ) if test "x$want_boost" = "xyes"; then AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_BUILD]) CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS AC_CACHE_CHECK(whether the Boost::Chrono library is available, ax_cv_boost_chrono, [AC_LANG_PUSH([C++]) CXXFLAGS_SAVE=$CXXFLAGS AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], [[boost::chrono::system_clock::time_point time;]])], ax_cv_boost_chrono=yes, ax_cv_boost_chrono=no) CXXFLAGS=$CXXFLAGS_SAVE AC_LANG_POP([C++]) ]) if test "x$ax_cv_boost_chrono" = "xyes"; then AC_SUBST(BOOST_CPPFLAGS) AC_DEFINE(HAVE_BOOST_CHRONO,,[define if the Boost::Chrono library is available]) BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` LDFLAGS_SAVE=$LDFLAGS if test "x$ax_boost_user_chrono_lib" = "x"; then for libextension in `ls $BOOSTLIBDIR/libboost_chrono*.so* $BOOSTLIBDIR/libboost_chrono*.dylib* $BOOSTLIBDIR/libboost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_chrono.*\)\.so.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break], [link_chrono="no"]) done if test "x$link_chrono" != "xyes"; then for libextension in `ls $BOOSTLIBDIR/boost_chrono*.dll* $BOOSTLIBDIR/boost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_chrono.*\)\.dll.*$;\1;' -e 's;^\(boost_chrono.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break], [link_chrono="no"]) done fi else for ax_lib in $ax_boost_user_chrono_lib boost_chrono-$ax_boost_user_chrono_lib; do AC_CHECK_LIB($ax_lib, exit, [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break], [link_chrono="no"]) done fi if test "x$ax_lib" = "x"; then AC_MSG_ERROR(Could not find a version of the library!) fi if test "x$link_chrono" = "xno"; then AC_MSG_ERROR(Could not link against $ax_lib !) fi fi CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" fi ]) libtorrent-rasterbar-1.1.13/m4/ax_boost_python.m4000066400000000000000000000120201351156116000217370ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_boost_python.html # =========================================================================== # # SYNOPSIS # # AX_BOOST_PYTHON # # DESCRIPTION # # This macro checks to see if the Boost.Python library is installed. It # also attempts to guess the correct library name using several attempts. # It tries to build the library name using a user supplied name or suffix # and then just the raw library. # # If the library is found, HAVE_BOOST_PYTHON is defined and # BOOST_PYTHON_LIB is set to the name of the library. # # This macro calls AC_SUBST(BOOST_PYTHON_LIB). # # In order to ensure that the Python headers and the Boost libraries are # specified on the include path, this macro requires AX_PYTHON_DEVEL and # AX_BOOST_BASE to be called. # # LICENSE # # Copyright (c) 2008 Michael Tindal # Copyright (c) 2013 Daniel M"ullner # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 21 AC_DEFUN([AX_BOOST_PYTHON], [AC_REQUIRE([AX_PYTHON_DEVEL])dnl AC_REQUIRE([AX_BOOST_BASE])dnl AC_LANG_PUSH([C++]) ax_boost_python_save_CPPFLAGS="$CPPFLAGS" ax_boost_python_save_LDFLAGS="$LDFLAGS" ax_boost_python_save_LIBS="$LIBS" if test "x$PYTHON_CPPFLAGS" != "x"; then CPPFLAGS="$PYTHON_CPPFLAGS $CPPFLAGS" fi # Versions of AX_PYTHON_DEVEL() before serial 18 provided PYTHON_LDFLAGS # instead of PYTHON_LIBS, so this is just here for compatibility. if test "x$PYTHON_LDFLAGS" != "x"; then LDFLAGS="$PYTHON_LDFLAGS $LDFLAGS" fi # Note: Only versions of AX_PYTHON_DEVEL() since serial 18 provide PYTHON_LIBS # instead of PYTHON_LDFLAGS. if test "x$PYTHON_LIBS" != "x"; then LIBS="$PYTHON_LIBS $LIBS" fi if test "x$BOOST_CPPFLAGS" != "x"; then CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS" fi if test "x$BOOST_LDFLAGS" != "x"; then LDFLAGS="$BOOST_LDFLAGS $LDFLAGS" fi AC_CACHE_CHECK(whether the Boost::Python library is available, ac_cv_boost_python, [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include BOOST_PYTHON_MODULE(test) { throw "Boost::Python test."; }]], [])], ac_cv_boost_python=yes, ac_cv_boost_python=no) ]) if test "$ac_cv_boost_python" = "yes"; then AC_DEFINE(HAVE_BOOST_PYTHON,,[define if the Boost::Python library is available]) ax_python_lib=boost_python AC_ARG_WITH([boost-python],AS_HELP_STRING([--with-boost-python],[specify yes/no or the boost python library or suffix to use]), [if test "x$with_boost_python" != "xno" -a "x$with_boost_python" != "xyes"; then ax_python_lib=$with_boost_python ax_boost_python_lib=boost_python-$with_boost_python fi]) BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` for ax_lib in $ax_python_lib $ax_boost_python_lib `ls $BOOSTLIBDIR/libboost_python*.so* $BOOSTLIBDIR/libboost_python*.dylib* $BOOSTLIBDIR/libboost_python*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_python.*\)\.so.*$;\1;' -e 's;^lib\(boost_python.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_python.*\)\.a.*$;\1;' ` boost_python boost_python3; do AS_VAR_PUSHDEF([ax_Lib], [ax_cv_lib_$ax_lib''_main])dnl AC_CACHE_CHECK([whether $ax_lib is the correct library], [ax_Lib], [LIBS="-l$ax_lib $ax_boost_python_save_LIBS $PYTHON_LIBS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ ]], [])], [AS_VAR_SET([ax_Lib], [yes])], [AS_VAR_SET([ax_Lib], [no])])]) AS_VAR_IF([ax_Lib], [yes], [BOOST_PYTHON_LIB=$ax_lib break], []) AS_VAR_POPDEF([ax_Lib])dnl done AC_SUBST(BOOST_PYTHON_LIB) fi CPPFLAGS="$ax_boost_python_save_CPPFLAGS" LDFLAGS="$ax_boost_python_save_LDFLAGS" LIBS="$ax_boost_python_save_LIBS" AC_LANG_POP([C++]) ])dnl libtorrent-rasterbar-1.1.13/m4/ax_boost_random.m4000066400000000000000000000075321351156116000217120ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_boost_random.html # =========================================================================== # # SYNOPSIS # # AX_BOOST_RANDOM # # DESCRIPTION # # Test for Random library from the Boost C++ libraries. The macro requires a # preceding call to AX_BOOST_BASE. Further documentation is available at # . # # This macro calls: # # AC_SUBST(BOOST_RANDOM_LIB) # # And sets: # # HAVE_BOOST_RANDOM # # LICENSE # # Copyright (c) 2008 Thomas Porschberg # Copyright (c) 2008 Michael Tindal # Copyright (c) 2013 Daniel Casimiro # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 1 AC_DEFUN([AX_BOOST_RANDOM], [ AC_ARG_WITH([boost-random], AS_HELP_STRING([--with-boost-random@<:@=special-lib@:>@], [use the Random library from boost - it is possible to specify a certain library for the linker e.g. --with-boost-random=boost_random-gcc-mt ]), [ if test "$withval" = "no"; then want_boost="no" elif test "$withval" = "yes"; then want_boost="yes" ax_boost_user_random_lib="" else want_boost="yes" ax_boost_user_random_lib="$withval" fi ], [want_boost="yes"] ) if test "x$want_boost" = "xyes"; then AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_BUILD]) CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS AC_CACHE_CHECK(whether the Boost::Random library is available, ax_cv_boost_random, [AC_LANG_PUSH([C++]) CXXFLAGS_SAVE=$CXXFLAGS AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[@%:@include ]], [[boost::random::random_device()();]])], ax_cv_boost_random=yes, ax_cv_boost_random=no) CXXFLAGS=$CXXFLAGS_SAVE AC_LANG_POP([C++]) ]) if test "x$ax_cv_boost_random" = "xyes"; then AC_SUBST(BOOST_CPPFLAGS) AC_DEFINE(HAVE_BOOST_RANDOM,,[define if the Boost::Random library is available]) BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` if test "x$ax_boost_user_random_lib" = "x"; then for libextension in `ls $BOOSTLIBDIR/libboost_random*.so* $BOOSTLIBDIR/libboost_random*.dylib* $BOOSTLIBDIR/libboost_random*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_random.*\)\.so.*$;\1;' -e 's;^lib\(boost_random.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_random.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_RANDOM_LIB="-l$ax_lib"; AC_SUBST(BOOST_RANDOM_LIB) link_random="yes"; break], [link_random="no"]) done if test "x$link_random" != "xyes"; then for libextension in `ls $BOOSTLIBDIR/boost_random*.dll* $BOOSTLIBDIR/boost_random*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_random.*\)\.dll.*$;\1;' -e 's;^\(boost_random.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_RANDOM_LIB="-l$ax_lib"; AC_SUBST(BOOST_RANDOM_LIB) link_random="yes"; break], [link_random="no"]) done fi else for ax_lib in $ax_boost_user_random_lib boost_random-$ax_boost_user_random_lib; do AC_CHECK_LIB($ax_lib, exit, [BOOST_RANDOM_LIB="-l$ax_lib"; AC_SUBST(BOOST_RANDOM_LIB) link_random="yes"; break], [link_random="no"]) done fi if test "x$ax_lib" = "x"; then AC_MSG_ERROR(Could not find a version of the library!) fi if test "x$link_random" = "xno"; then AC_MSG_ERROR(Could not link against $ax_lib !) fi fi CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" fi ]) libtorrent-rasterbar-1.1.13/m4/ax_boost_system.m4000066400000000000000000000101261351156116000217470ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_boost_system.html # =========================================================================== # # SYNOPSIS # # AX_BOOST_SYSTEM # # DESCRIPTION # # Test for System library from the Boost C++ libraries. The macro requires # a preceding call to AX_BOOST_BASE. Further documentation is available at # . # # This macro calls: # # AC_SUBST(BOOST_SYSTEM_LIB) # # And sets: # # HAVE_BOOST_SYSTEM # # LICENSE # # Copyright (c) 2008 Thomas Porschberg # Copyright (c) 2008 Michael Tindal # Copyright (c) 2008 Daniel Casimiro # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 18 AC_DEFUN([AX_BOOST_SYSTEM], [ AC_ARG_WITH([boost-system], AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@], [use the System library from boost - it is possible to specify a certain library for the linker e.g. --with-boost-system=boost_system-gcc-mt ]), [ if test "$withval" = "no"; then want_boost="no" elif test "$withval" = "yes"; then want_boost="yes" ax_boost_user_system_lib="" else want_boost="yes" ax_boost_user_system_lib="$withval" fi ], [want_boost="yes"] ) if test "x$want_boost" = "xyes"; then AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_BUILD]) CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS AC_CACHE_CHECK(whether the Boost::System library is available, ax_cv_boost_system, [AC_LANG_PUSH([C++]) CXXFLAGS_SAVE=$CXXFLAGS CXXFLAGS= AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], [[boost::system::error_category *a = 0;]])], ax_cv_boost_system=yes, ax_cv_boost_system=no) CXXFLAGS=$CXXFLAGS_SAVE AC_LANG_POP([C++]) ]) if test "x$ax_cv_boost_system" = "xyes"; then AC_SUBST(BOOST_CPPFLAGS) AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available]) BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` LDFLAGS_SAVE=$LDFLAGS if test "x$ax_boost_user_system_lib" = "x"; then for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], [link_system="no"]) done if test "x$link_system" != "xyes"; then for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], [link_system="no"]) done fi else for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do AC_CHECK_LIB($ax_lib, exit, [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], [link_system="no"]) done fi if test "x$ax_lib" = "x"; then AC_MSG_ERROR(Could not find a version of the library!) fi if test "x$link_system" = "xno"; then AC_MSG_ERROR(Could not link against $ax_lib !) fi fi CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" fi ]) libtorrent-rasterbar-1.1.13/m4/ax_check_openssl.m4000066400000000000000000000101241351156116000220330ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_openssl.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]]) # # DESCRIPTION # # Look for OpenSSL in a number of default spots, or in a user-selected # spot (via --with-openssl). Sets # # OPENSSL_INCLUDES to the include directives required # OPENSSL_LIBS to the -l directives required # OPENSSL_LDFLAGS to the -L or -R flags required # # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately # # This macro sets OPENSSL_INCLUDES such that source files should use the # openssl/ directory in include directives: # # #include # # LICENSE # # Copyright (c) 2009, 2010 Zmanda Inc. # Copyright (c) 2009, 2010 Dustin J. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 6 AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL]) AC_DEFUN([AX_CHECK_OPENSSL], [ found=false AC_ARG_WITH(openssl, AS_HELP_STRING([--with-openssl=DIR], [root of the OpenSSL directory]), [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-openssl value]) ;; *) ssldirs="$withval" ;; esac ], [ # if pkg-config is installed and openssl has installed a .pc file, # then use that information and don't search ssldirs AC_PATH_PROG(PKG_CONFIG, pkg-config) if test x"$PKG_CONFIG" != x""; then OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null` if test $? = 0; then OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null` OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default ssldirs if ! $found; then ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" fi ] ) # note that we #include , so the OpenSSL headers have to be in # an 'openssl' subdirectory if ! $found; then OPENSSL_INCLUDES= for ssldir in $ssldirs; do AC_MSG_CHECKING([for openssl/ssl.h in $ssldir]) if test -f "$ssldir/include/openssl/ssl.h"; then OPENSSL_INCLUDES="-I$ssldir/include" OPENSSL_LDFLAGS="-L$ssldir/lib" OPENSSL_LIBS="-lssl -lcrypto" found=true AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done # if the file wasn't found, well, go ahead and try the link anyway -- maybe # it will just work! fi # try the preprocessor and linker with our new flags, # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS AC_MSG_CHECKING([whether compiling and linking against OpenSSL works]) echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \ "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&AS_MESSAGE_LOG_FD save_LIBS="$LIBS" save_LDFLAGS="$LDFLAGS" save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" LIBS="$OPENSSL_LIBS $LIBS" CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include ], [SSL_new(NULL)])], [ AC_MSG_RESULT([yes]) $1 ], [ AC_MSG_RESULT([no]) $2 ]) CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" AC_SUBST([OPENSSL_INCLUDES]) AC_SUBST([OPENSSL_LIBS]) AC_SUBST([OPENSSL_LDFLAGS]) ]) libtorrent-rasterbar-1.1.13/m4/ax_pthread.m4000066400000000000000000000353051351156116000206520ustar00rootroot00000000000000# =========================================================================== # http://www.nongnu.org/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also link it with them as well. e.g. you should link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threads programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # # 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 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_SAVE AC_LANG_C ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_LIBS$PTHREAD_CFLAGS" != "x"; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) AC_MSG_RESULT($ax_pthread_ok) if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -kthread lthread -lpthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; esac if test "x$ax_pthread_ok" = "xno"; then for flag in $ax_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) if test "x$ax_pthread_config" = "xno"; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [ax_pthread_ok=yes]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($ax_pthread_ok) if test "x$ax_pthread_ok" = "xyes"; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_TRY_LINK([#include ], [int attr=$attr; return attr;], [attr_name=$attr; break]) done AC_MSG_RESULT($attr_name) if test "x$attr_name" != "xPTHREAD_CREATE_JOINABLE"; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != "xno"; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test "x$GCC" != "xyes"; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi # The next part tries to detect GCC inconsistency with -shared on some # architectures and systems. The problem is that in certain # configurations, when -shared is specified, GCC "forgets" to # internally use various flags which are still necessary. # # Prepare the flags # save_LDFLAGS="$LDFLAGS" save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" save_CC="$CC" # Try with the flags determined by the earlier checks. # # -Wl,-z,defs forces link-time symbol resolution, so that the # linking checks with -shared actually have any value # # FIXME: -fPIC is required for -shared on many architectures, # so we specify it here, but the right way would probably be to # properly detect whether it is actually required. CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CC="$PTHREAD_CC" # In order not to create several levels of indentation, we test # the value of "$done" until we find the cure or run out of ideas. done="no" # First, make sure the CFLAGS we added are actually accepted by our # compiler. If not (and OS X's ld, for instance, does not accept -z), # then we can't do this test. if test x"$done" = xno; then AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies]) AC_TRY_LINK(,, , [done=yes]) if test "x$done" = xyes ; then AC_MSG_RESULT([no]) else AC_MSG_RESULT([yes]) fi fi if test x"$done" = xyes; then done="no" AC_MSG_CHECKING([whether -pthread is sufficient with -shared]) AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [done=yes]) if test "x$done" = xyes; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi fi # # Linux gcc on some architectures such as mips/mipsel forgets # about -lpthread # if test x"$done" = xno; then AC_MSG_CHECKING([whether -lpthread fixes that]) LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [done=yes]) if test "x$done" = xyes; then AC_MSG_RESULT([yes]) PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" else AC_MSG_RESULT([no]) fi fi # # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc # if test x"$done" = xno; then AC_MSG_CHECKING([whether -lc_r fixes that]) LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [done=yes]) if test "x$done" = xyes; then AC_MSG_RESULT([yes]) PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" else AC_MSG_RESULT([no]) fi fi if test x"$done" = xno; then # OK, we have run out of ideas AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries]) # so it's not safe to assume that we may use pthreads acx_pthread_ok=no fi CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" CC="$save_CC" else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,[1],[Define if you have POSIX threads libraries and header files.]),[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_RESTORE ])dnl AX_PTHREAD libtorrent-rasterbar-1.1.13/m4/ax_python_devel.m4000066400000000000000000000257741351156116000217340ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_python_devel.html # =========================================================================== # # SYNOPSIS # # AX_PYTHON_DEVEL([version]) # # DESCRIPTION # # Note: Defines as a precious variable "PYTHON_VERSION". Don't override it # in your configure.ac. # # This macro checks for Python and tries to get the include path to # 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LIBS) output # variables. It also exports $(PYTHON_EXTRA_LIBS) and # $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. # # You can search for some particular version of Python by passing a # parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please # note that you *have* to pass also an operator along with the version to # match, and pay special attention to the single quotes surrounding the # version number. Don't use "PYTHON_VERSION" for this: that environment # variable is declared as precious and thus reserved for the end-user. # # This macro should work for all versions of Python >= 2.1.0. As an end # user, you can disable the check for the python version by setting the # PYTHON_NOVERSIONCHECK environment variable to something else than the # empty string. # # If you need to use this macro for an older Python version, please # contact the authors. We're always open for feedback. # # LICENSE # # Copyright (c) 2009 Sebastian Huber # Copyright (c) 2009 Alan W. Irwin # Copyright (c) 2009 Rafael Laboissiere # Copyright (c) 2009 Andrew Collier # Copyright (c) 2009 Matteo Settenvini # Copyright (c) 2009 Horst Knorr # Copyright (c) 2013 Daniel Mullner # # 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 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 18 AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) AC_DEFUN([AX_PYTHON_DEVEL],[ # # Allow the use of a (user set) custom python version # AC_ARG_VAR([PYTHON_VERSION],[The installed Python version to use, for example '2.3'. This string will be appended to the Python interpreter canonical name.]) AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) if test -z "$PYTHON"; then AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path]) PYTHON_VERSION="" fi # # Check for a version of Python >= 2.1.0 # AC_MSG_CHECKING([for a version of Python >= '2.1.0']) ac_supports_python_ver=`$PYTHON -c "import sys; \ ver = sys.version.split ()[[0]]; \ print (ver >= '2.1.0')"` if test "$ac_supports_python_ver" != "True"; then if test -z "$PYTHON_NOVERSIONCHECK"; then AC_MSG_RESULT([no]) AC_MSG_FAILURE([ This version of the AC@&t@_PYTHON_DEVEL macro doesn't work properly with versions of Python before 2.1.0. You may need to re-run configure, setting the variables PYTHON_CPPFLAGS, PYTHON_LIBS, PYTHON_SITE_PKG, PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. Moreover, to disable this check, set PYTHON_NOVERSIONCHECK to something else than an empty string. ]) else AC_MSG_RESULT([skip at user request]) fi else AC_MSG_RESULT([yes]) fi # # if the macro parameter ``version'' is set, honour it # if test -n "$1"; then AC_MSG_CHECKING([for a version of Python $1]) ac_supports_python_ver=`$PYTHON -c "import sys; \ ver = sys.version.split ()[[0]]; \ print (ver $1)"` if test "$ac_supports_python_ver" = "True"; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) AC_MSG_ERROR([this package requires Python $1. If you have it installed, but it isn't the default Python interpreter in your system path, please pass the PYTHON_VERSION variable to configure. See ``configure --help'' for reference. ]) PYTHON_VERSION="" fi fi # # Check if you have distutils, else fail # AC_MSG_CHECKING([for the distutils Python package]) ac_distutils_result=`$PYTHON -c "import distutils" 2>&1 | grep -v '^\[[0-9]\{1,\} refs\]'` if test -z "$ac_distutils_result"; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) AC_MSG_ERROR([cannot import Python module "distutils". Please check your Python installation. The error was: $ac_distutils_result]) PYTHON_VERSION="" fi # # Check for Python include path # AC_MSG_CHECKING([for Python include path]) if test -z "$PYTHON_CPPFLAGS"; then python_path=`$PYTHON -c "import distutils.sysconfig; \ print (distutils.sysconfig.get_python_inc ());"` plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ print (distutils.sysconfig.get_python_inc (plat_specific=1));"` if test -n "${python_path}"; then if test "${plat_python_path}" != "${python_path}"; then python_path="-I$python_path -I$plat_python_path" else python_path="-I$python_path" fi fi PYTHON_CPPFLAGS=$python_path fi AC_MSG_RESULT([$PYTHON_CPPFLAGS]) AC_SUBST([PYTHON_CPPFLAGS]) # # Check for Python library path # AC_MSG_CHECKING([for Python library path]) if test -z "$PYTHON_LIBS"; then # (makes two attempts to ensure we've got a version number # from the interpreter) ac_python_version=`cat<]], [[Py_Initialize();]]) ],[pythonexists=yes],[pythonexists=no]) AC_LANG_POP([C]) # turn back to default flags CPPFLAGS="$ac_save_CPPFLAGS" LIBS="$ac_save_LIBS" LDFLAGS="$ac_save_LDFLAGS" AC_MSG_RESULT([$pythonexists]) if test ! "x$pythonexists" = "xyes"; then AC_MSG_FAILURE([ Could not link test program to Python. Maybe the main Python library has been installed in some non-standard library path. If so, pass it to configure, via the LIBS environment variable. Example: ./configure LIBS="-L/usr/non-standard-path/python/lib" ============================================================================ ERROR! You probably have to install the development version of the Python package for your distribution. The exact name of this package varies among them. ============================================================================ ]) PYTHON_VERSION="" fi # # all done! # ]) libtorrent-rasterbar-1.1.13/m4/gettext-lib.m4000066400000000000000000000766431351156116000207750ustar00rootroot00000000000000## concatentation of files in gettext-0.14.5/autoconf-lib-link/m4 # lib-ld.m4 serial 3 (gettext-0.13) dnl Copyright (C) 1996-2003 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Subroutines of libtool.m4, dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision dnl with libtool.m4. dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no. AC_DEFUN([AC_LIB_PROG_LD_GNU], [AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld, [# I'd rather use --version here, but apparently some GNU ld's only accept -v. case `$LD -v 2>&1 @])], test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no) AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl # Prepare PATH_SEPARATOR. # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by GCC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]* | [A-Za-z]:[\\/]*)] [re_direlt='/[^/][^/]*/\.\./'] # Canonicalize the path of ld ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(acl_cv_path_LD, [if test -z "$LD"; then IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" for ac_dir in $PATH; do test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then acl_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some GNU ld's only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in *GNU* | *'with BFD'*) test "$with_gnu_ld" != no && break ;; *) test "$with_gnu_ld" != yes && break ;; esac fi done IFS="$ac_save_ifs" else acl_cv_path_LD="$LD" # Let the user override the test with a path. fi]) LD="$acl_cv_path_LD" if test -n "$LD"; then AC_MSG_RESULT($LD) else AC_MSG_RESULT(no) fi test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) AC_LIB_PROG_LD_GNU ]) # lib-link.m4 serial 6 (gettext-0.14.3) dnl Copyright (C) 2001-2005 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. AC_PREREQ(2.50) dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and dnl augments the CPPFLAGS variable. AC_DEFUN([AC_LIB_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) dnl AC_REQUIRE([AC_LIB_RPATH]) define([Name],[translit([$1],[./-], [___])]) define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ AC_LIB_LINKFLAGS_BODY([$1], [$2]) ac_cv_lib[]Name[]_libs="$LIB[]NAME" ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" ac_cv_lib[]Name[]_cppflags="$INC[]NAME" ]) LIB[]NAME="$ac_cv_lib[]Name[]_libs" LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" INC[]NAME="$ac_cv_lib[]Name[]_cppflags" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the dnl results of this search when this library appears as a dependency. HAVE_LIB[]NAME=yes undefine([Name]) undefine([NAME]) ]) dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode) dnl searches for libname and the libraries corresponding to explicit and dnl implicit dependencies, together with the specified include files and dnl the ability to compile and link the specified testcode. If found, it dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) dnl AC_REQUIRE([AC_LIB_RPATH]) define([Name],[translit([$1],[./-], [___])]) define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME dnl accordingly. AC_LIB_LINKFLAGS_BODY([$1], [$2]) dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, dnl because if the user has installed lib[]Name and not disabled its use dnl via --without-lib[]Name-prefix, he wants to use it. ac_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ ac_save_LIBS="$LIBS" LIBS="$LIBS $LIB[]NAME" AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no]) LIBS="$ac_save_LIBS" ]) if test "$ac_cv_lib[]Name" = yes; then HAVE_LIB[]NAME=yes AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.]) AC_MSG_CHECKING([how to link with lib[]$1]) AC_MSG_RESULT([$LIB[]NAME]) else HAVE_LIB[]NAME=no dnl If $LIB[]NAME didn't lead to a usable library, we don't need dnl $INC[]NAME either. CPPFLAGS="$ac_save_CPPFLAGS" LIB[]NAME= LTLIB[]NAME= fi AC_SUBST([HAVE_LIB]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) undefine([Name]) undefine([NAME]) ]) dnl Determine the platform dependent parameters needed to use rpath: dnl libext, shlibext, hardcode_libdir_flag_spec, hardcode_libdir_separator, dnl hardcode_direct, hardcode_minus_L. AC_DEFUN([AC_LIB_RPATH], [ dnl Tell automake >= 1.10 to complain if config.rpath is missing. m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])]) AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS dnl AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh . ./conftest.sh rm -f ./conftest.sh acl_cv_rpath=done ]) wl="$acl_cv_wl" libext="$acl_cv_libext" shlibext="$acl_cv_shlibext" hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" hardcode_direct="$acl_cv_hardcode_direct" hardcode_minus_L="$acl_cv_hardcode_minus_L" dnl Determine whether the user wants rpath handling at all. dnl AC_ARG_ENABLE(rpath, dnl [ --disable-rpath do not hardcode runtime library paths], dnl :, enable_rpath=yes) ]) dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. AC_DEFUN([AC_LIB_LINKFLAGS_BODY], [ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_LIB_ARG_WITH([lib$1-prefix], [ --with-lib$1-prefix[=DIR] search for lib$1 in DIR/include and DIR/lib --without-lib$1-prefix don't search for lib$1 in includedir and libdir], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/lib" fi fi ]) dnl Search the library and its dependencies in $additional_libdir and dnl $LDFLAGS. Using breadth-first-seach. LIB[]NAME= LTLIB[]NAME= INC[]NAME= rpathdirs= ltrpathdirs= names_already_handled= names_next_round='$1 $2' while test -n "$names_next_round"; do names_this_round="$names_next_round" names_next_round= for name in $names_this_round; do already_handled= for n in $names_already_handled; do if test "$n" = "$name"; then already_handled=yes break fi done if test -z "$already_handled"; then names_already_handled="$names_already_handled $name" dnl See if it was already located by an earlier AC_LIB_LINKFLAGS dnl or AC_LIB_HAVE_LINKFLAGS call. uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` eval value=\"\$HAVE_LIB$uppername\" if test -n "$value"; then if test "$value" = yes; then eval value=\"\$LIB$uppername\" test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" eval value=\"\$LTLIB$uppername\" test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" else dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined dnl that this library doesn't exist. So just drop it. : fi else dnl Search the library lib$name in $additional_libdir and $LDFLAGS dnl and the already constructed $LIBNAME/$LTLIBNAME. found_dir= found_la= found_so= found_a= if test $use_additional = yes; then if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then found_dir="$additional_libdir" found_so="$additional_libdir/lib$name.$shlibext" if test -f "$additional_libdir/lib$name.la"; then found_la="$additional_libdir/lib$name.la" fi else if test -f "$additional_libdir/lib$name.$libext"; then found_dir="$additional_libdir" found_a="$additional_libdir/lib$name.$libext" if test -f "$additional_libdir/lib$name.la"; then found_la="$additional_libdir/lib$name.la" fi fi fi fi if test "X$found_dir" = "X"; then for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) case "$x" in -L*) dir=`echo "X$x" | sed -e 's/^X-L//'` if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then found_dir="$dir" found_so="$dir/lib$name.$shlibext" if test -f "$dir/lib$name.la"; then found_la="$dir/lib$name.la" fi else if test -f "$dir/lib$name.$libext"; then found_dir="$dir" found_a="$dir/lib$name.$libext" if test -f "$dir/lib$name.la"; then found_la="$dir/lib$name.la" fi fi fi ;; esac if test "X$found_dir" != "X"; then break fi done fi if test "X$found_dir" != "X"; then dnl Found the library. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" if test "X$found_so" != "X"; then dnl Linking with a shared library. We attempt to hardcode its dnl directory into the executable's runpath, unless it's the dnl standard /usr/lib. if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then dnl No hardcoding is needed. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl Use an explicit option to hardcode DIR into the resulting dnl binary. dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $found_dir" fi dnl The hardcoding into $LIBNAME is system dependent. if test "$hardcode_direct" = yes; then dnl Using DIR/libNAME.so during linking hardcodes DIR into the dnl resulting binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then dnl Use an explicit option to hardcode DIR into the resulting dnl binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $found_dir" fi else dnl Rely on "-L$found_dir". dnl But don't add it if it's already contained in the LDFLAGS dnl or the already constructed $LIBNAME haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" fi if test "$hardcode_minus_L" != no; then dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl We cannot use $hardcode_runpath_var and LD_RUN_PATH dnl here, because this doesn't fit in flags passed to the dnl compiler. So give up. No hardcoding. This affects only dnl very old systems. dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" fi fi fi fi else if test "X$found_a" != "X"; then dnl Linking with a static library. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" else dnl We shouldn't come here, but anyway it's good to have a dnl fallback. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" fi fi dnl Assume the include files are nearby. additional_includedir= case "$found_dir" in */lib | */lib/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'` additional_includedir="$basedir/include" ;; esac if test "X$additional_includedir" != "X"; then dnl Potentially add $additional_includedir to $INCNAME. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's /usr/local/include and we are using GCC on Linux, dnl 3. if it's already present in $CPPFLAGS or the already dnl constructed $INCNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then for x in $CPPFLAGS $INC[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $INCNAME. INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" fi fi fi fi fi dnl Look for dependencies. if test -n "$found_la"; then dnl Read the .la file. It defines the variables dnl dlname, library_names, old_library, dependency_libs, current, dnl age, revision, installed, dlopen, dlpreopen, libdir. save_libdir="$libdir" case "$found_la" in */* | *\\*) . "$found_la" ;; *) . "./$found_la" ;; esac libdir="$save_libdir" dnl We use only dependency_libs. for dep in $dependency_libs; do case "$dep" in -L*) additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's /usr/local/lib and we are using GCC on Linux, dnl 3. if it's already present in $LDFLAGS or the already dnl constructed $LIBNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/lib"; then haveit= if test "X$additional_libdir" = "X/usr/local/lib"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LIBNAME. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" fi fi haveit= for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LTLIBNAME. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" fi fi fi fi ;; -R*) dir=`echo "X$dep" | sed -e 's/^X-R//'` if test "$enable_rpath" != no; then dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $dir" fi dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $dir" fi fi ;; -l*) dnl Handle this in the next round. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` ;; *.la) dnl Handle this in the next round. Throw away the .la's dnl directory; it is already contained in a preceding -L dnl option. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` ;; *) dnl Most likely an immediate library name. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" ;; esac done fi else dnl Didn't find the library; assume it is in the system directories dnl known to the linker and runtime loader. (All the system dnl directories known to the linker should also be known to the dnl runtime loader, otherwise the system is severely misconfigured.) LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" fi fi fi done done if test "X$rpathdirs" != "X"; then if test -n "$hardcode_libdir_separator"; then dnl Weird platform: only the last -rpath option counts, the user must dnl pass all path elements in one option. We can arrange that for a dnl single library, but not when more than one $LIBNAMEs are used. alldirs= for found_dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir" done dnl Note: hardcode_libdir_flag_spec uses $libdir and $wl. acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" else dnl The -rpath options are cumulative. for found_dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$found_dir" eval flag=\"$hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" done fi fi if test "X$ltrpathdirs" != "X"; then dnl When using libtool, the option that works for both libraries and dnl executables is -R. The -R options are cumulative. for found_dir in $ltrpathdirs; do LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" done fi ]) dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, dnl unless already present in VAR. dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes dnl contains two or three consecutive elements that belong together. AC_DEFUN([AC_LIB_APPENDTOVAR], [ for element in [$2]; do haveit= for x in $[$1]; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X$element"; then haveit=yes break fi done if test -z "$haveit"; then [$1]="${[$1]}${[$1]:+ }$element" fi done ]) # lib-prefix.m4 serial 4 (gettext-0.14.2) dnl Copyright (C) 2001-2005 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't dnl require excessive bracketing. ifdef([AC_HELP_STRING], [AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], [AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed dnl to access previously installed libraries. The basic assumption is that dnl a user will want packages to use other packages he previously installed dnl with the same --prefix option. dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate dnl libraries, but is otherwise very convenient. AC_DEFUN([AC_LIB_PREFIX], [ AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_LIB_ARG_WITH([lib-prefix], [ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib --without-lib-prefix don't search for libraries in includedir and libdir], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/lib" fi fi ]) if test $use_additional = yes; then dnl Potentially add $additional_includedir to $CPPFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's already present in $CPPFLAGS, dnl 3. if it's /usr/local/include and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= for x in $CPPFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $CPPFLAGS. CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" fi fi fi fi dnl Potentially add $additional_libdir to $LDFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's already present in $LDFLAGS, dnl 3. if it's /usr/local/lib and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/lib"; then haveit= for x in $LDFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_libdir" = "X/usr/local/lib"; then if test -n "$GCC"; then case $host_os in linux*) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LDFLAGS. LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" fi fi fi fi fi ]) dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, dnl acl_final_exec_prefix, containing the values to which $prefix and dnl $exec_prefix will expand at the end of the configure script. AC_DEFUN([AC_LIB_PREPARE_PREFIX], [ dnl Unfortunately, prefix and exec_prefix get only finally determined dnl at the end of configure. if test "X$prefix" = "XNONE"; then acl_final_prefix="$ac_default_prefix" else acl_final_prefix="$prefix" fi if test "X$exec_prefix" = "XNONE"; then acl_final_exec_prefix='${prefix}' else acl_final_exec_prefix="$exec_prefix" fi acl_save_prefix="$prefix" prefix="$acl_final_prefix" eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" prefix="$acl_save_prefix" ]) dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the dnl variables prefix and exec_prefix bound to the values they will have dnl at the end of the configure script. AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], [ acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" $1 exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" ]) libtorrent-rasterbar-1.1.13/m4/iconv.m4000066400000000000000000000066531351156116000176550ustar00rootroot00000000000000# iconv.m4 serial AM4 (gettext-0.11.3) dnl Copyright (C) 2000-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], [ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV dnl accordingly. AC_LIB_LINKFLAGS_BODY([iconv]) ]) AC_DEFUN([AM_ICONV_LINK], [ dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and dnl those with the standalone portable GNU libiconv installed). dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV dnl accordingly. AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) dnl Add $INCICONV to CPPFLAGS before performing the following checks, dnl because if the user has installed libiconv and not disabled its use dnl via --without-libiconv-prefix, he wants to use it. The first dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed. am_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) AC_CACHE_CHECK(for iconv, am_cv_func_iconv, [ am_cv_func_iconv="no, consider installing GNU libiconv" am_cv_lib_iconv=no AC_TRY_LINK([#include #include ], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);], am_cv_func_iconv=yes) if test "$am_cv_func_iconv" != yes; then am_save_LIBS="$LIBS" LIBS="$LIBS $LIBICONV" AC_TRY_LINK([#include #include ], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);], am_cv_lib_iconv=yes am_cv_func_iconv=yes) LIBS="$am_save_LIBS" fi ]) if test "$am_cv_func_iconv" = yes; then AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.]) fi if test "$am_cv_lib_iconv" = yes; then AC_MSG_CHECKING([how to link with libiconv]) AC_MSG_RESULT([$LIBICONV]) else dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV dnl either. CPPFLAGS="$am_save_CPPFLAGS" LIBICONV= LTLIBICONV= fi AC_SUBST(LIBICONV) AC_SUBST(LTLIBICONV) ]) AC_DEFUN([AM_ICONV], [ AM_ICONV_LINK if test "$am_cv_func_iconv" = yes; then AC_MSG_CHECKING([for iconv declaration]) AC_CACHE_VAL(am_cv_proto_iconv, [ AC_TRY_COMPILE([ #include #include extern #ifdef __cplusplus "C" #endif #if defined(__STDC__) || defined(__cplusplus) size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); #else size_t iconv(); #endif ], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const") am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` AC_MSG_RESULT([$]{ac_t:- }[$]am_cv_proto_iconv) AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1, [Define as const if the declaration of iconv() needs const.]) fi ]) libtorrent-rasterbar-1.1.13/m4/libtool.m4000066400000000000000000011261711351156116000202020ustar00rootroot00000000000000# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file 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. m4_define([_LT_COPYING], [dnl # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool 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, see . ]) # serial 58 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_PREPARE_CC_BASENAME # ----------------------- m4_defun([_LT_PREPARE_CC_BASENAME], [ # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in @S|@*""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } ])# _LT_PREPARE_CC_BASENAME # _LT_CC_BASENAME(CC) # ------------------- # It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, # but that macro is also expanded into generated libtool script, which # arranges for $SED and $ECHO to be set by different means. m4_defun([_LT_CC_BASENAME], [m4_require([_LT_PREPARE_CC_BASENAME])dnl AC_REQUIRE([_LT_DECL_SED])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl func_cc_basename $1 cc_basename=$func_cc_basename_result ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl m4_require([_LT_CMD_TRUNCATE])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from 'configure', and 'config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # 'config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain=$ac_aux_dir/ltmain.sh ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the 'libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to 'config.status' so that its # declaration there will have the same value as in 'configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags='_LT_TAGS'dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into 'config.status', and then the shell code to quote escape them in # for loops in 'config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # '#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test 0 = "$lt_write_fail" && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ '$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test 0 != $[#] do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try '$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try '$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test yes = "$silent" && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 _LT_COPYING _LT_LIBTOOL_TAGS # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE _LT_PREPARE_MUNGE_PATH_LIST _LT_PREPARE_CC_BASENAME # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS=$save_LDFLAGS ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[[012]][[,.]]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test yes = "$lt_cv_ld_force_load"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" m4_if([$1], [CXX], [ if test yes != "$lt_cv_apple_cc_single_mod"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script that will find a shell with a builtin # printf (that we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case $ECHO in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], [Search for dependent libraries within DIR (or the compiler's sysroot if not specified).])], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([$with_sysroot]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and where our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} : ${AR_FLAGS=cru} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test yes = "[$]$2"; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS ]) if test yes = "[$]$2"; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n "$lt_cv_sys_max_cmd_len"; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes = "$cross_compiling"; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen=shl_load], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen=dlopen], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) ]) ]) ]) ]) ]) ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links=nottested if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test no = "$hard_links"; then AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", [Define to the sub-directory where libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then # We can hardcode non-existent directories. if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_PREPARE_MUNGE_PATH_LIST # --------------------------- # Make sure func_munge_path_list() is defined correctly. m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], [[# func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x@S|@2 in x) ;; *:) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" ;; x:*) eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; *::*) eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" ;; *) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; esac } ]])# _LT_PREPARE_PATH_LIST # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown AC_ARG_VAR([LT_SYS_LIBRARY_PATH], [User-defined run-time library search path.]) case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a[(]lib.so.V[)]' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], [Detected run-time system search path for libraries]) _LT_DECL([], [configure_time_lt_sys_library_path], [2], [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program that can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$1"; then lt_cv_path_MAGIC_CMD=$ac_dir/"$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac]) MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program that can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test no = "$withval" || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], [if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi]) rm -f conftest.i conftest2.i conftest.out]) ])# _LT_PATH_DD # _LT_CMD_TRUNCATE # ---------------- # find command to truncate a binary pipe m4_defun([_LT_CMD_TRUNCATE], [m4_require([_LT_PATH_DD]) AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], [printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) _LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], [Command to truncate a binary pipe]) ])# _LT_CMD_TRUNCATE # _LT_CHECK_MAGIC_METHOD # ---------------------- # how to check for library dependencies # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_MAGIC_METHOD], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) AC_CACHE_CHECK([how to recognize dependent libraries], lt_cv_deplibs_check_method, [lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[[4-9]]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[[45]]*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi]) if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # _LT_DLL_DEF_P([FILE]) # --------------------- # True iff FILE is a Windows DLL '.def' file. # Keep in sync with func_dll_def_p in the libtool script AC_DEFUN([_LT_DLL_DEF_P], [dnl test DEF = "`$SED -n dnl -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl -e q dnl Only consider the first "real" line $1`" dnl ])# _LT_DLL_DEF_P # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM=-lm) ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test yes = "$GCC"; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], [Transform the output of nm into a list of symbols to manually relocate]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([nm_interface], [lt_cv_nm_interface], [1], [The name lister interface]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test yes = "$GCC"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS=$save_LDFLAGS]) if test yes = "$lt_cv_irix_exported_symbol"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi _LT_TAGVAR(link_all_deplibs, $1)=no else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(ld_shlibs, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; osf3*) if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test yes = "$GCC"; then wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test yes,yes = "$GCC,$enable_shared"; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting $shlibpath_var if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC=$CC AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report what library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC=$lt_save_CC ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(GCC, $1)=$GXX _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case @S|@2 in .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)=$prev$p else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)=$p else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)=$p else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test no = "$F77"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_F77"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$G77 _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_F77" AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test no = "$FC"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_FC"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_FC" AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code=$lt_simple_compile_test_code # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f "$lt_ac_sed" && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test 10 -lt "$lt_ac_count" && break lt_ac_count=`expr $lt_ac_count + 1` if test "$lt_ac_count" -gt "$lt_ac_max"; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine what file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS libtorrent-rasterbar-1.1.13/m4/ltoptions.m4000066400000000000000000000342621351156116000205670ustar00rootroot00000000000000# Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file 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. # serial 8 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option '$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl 'shared' nor 'disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], [_LT_WITH_AIX_SONAME([aix])]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the 'shared' and # 'disable-shared' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the 'static' and # 'disable-static' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the 'fast-install' # and 'disable-fast-install' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_AIX_SONAME([DEFAULT]) # ---------------------------------- # implement the --with-aix-soname flag, and support the `aix-soname=aix' # and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT # is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. m4_define([_LT_WITH_AIX_SONAME], [m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[[5-9]]*,yes) AC_MSG_CHECKING([which variant of shared library versioning to provide]) AC_ARG_WITH([aix-soname], [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], [case $withval in aix|svr4|both) ;; *) AC_MSG_ERROR([Unknown argument to --with-aix-soname]) ;; esac lt_cv_with_aix_soname=$with_aix_soname], [AC_CACHE_VAL([lt_cv_with_aix_soname], [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) with_aix_soname=$lt_cv_with_aix_soname]) AC_MSG_RESULT([$with_aix_soname]) if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac _LT_DECL([], [shared_archive_member_spec], [0], [Shared archive member basename, for filename based shared library versioning on AIX])dnl ])# _LT_WITH_AIX_SONAME LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the 'pic-only' and 'no-pic' # LT_INIT options. # MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac], [pic_mode=m4_default([$1], [default])]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) libtorrent-rasterbar-1.1.13/m4/ltsugar.m4000066400000000000000000000104401351156116000202050ustar00rootroot00000000000000# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file 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. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59, which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) libtorrent-rasterbar-1.1.13/m4/ltversion.m4000066400000000000000000000012731351156116000205550ustar00rootroot00000000000000# ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file 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. # @configure_input@ # serial 4179 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.6]) m4_define([LT_PACKAGE_REVISION], [2.4.6]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.6' macro_revision='2.4.6' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) libtorrent-rasterbar-1.1.13/m4/lt~obsolete.m4000066400000000000000000000137741351156116000211130ustar00rootroot00000000000000# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software # Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file 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. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) libtorrent-rasterbar-1.1.13/m4/pkgconfig.m4000066400000000000000000000120631351156116000204760ustar00rootroot00000000000000# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # # Copyright © 2004 Scott James Remnant . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # # Similar to PKG_CHECK_MODULES, make sure that the first instance of # this or PKG_CHECK_MODULES is called, or make sure to call # PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_ifval([$2], [$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD ifelse([$4], , [AC_MSG_ERROR(dnl [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT ])], [AC_MSG_RESULT([no]) $4]) elif test $pkg_failed = untried; then ifelse([$4], , [AC_MSG_FAILURE(dnl [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])], [$4]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) ifelse([$3], , :, [$3]) fi[]dnl ])# PKG_CHECK_MODULES libtorrent-rasterbar-1.1.13/setup.py000066400000000000000000000001331351156116000174520ustar00rootroot00000000000000#!/usr/bin/env python import os os.chdir('bindings/python') exec(open('setup.py').read()) libtorrent-rasterbar-1.1.13/src/000077500000000000000000000000001351156116000165325ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/src/ConvertUTF.cpp000066400000000000000000000440301351156116000212360ustar00rootroot00000000000000/* * Copyright 2001-2004 Unicode, Inc. * * Disclaimer * * This source code is provided as is by Unicode, Inc. No claims are * made as to fitness for any particular purpose. No warranties of any * kind are expressed or implied. The recipient agrees to determine * applicability of information provided. If this file has been * purchased on magnetic or optical media from Unicode, Inc., the * sole remedy for any claim will be exchange of defective media * within 90 days of receipt. * * Limitations on Rights to Redistribute This Code * * Unicode, Inc. hereby grants the right to freely use the information * supplied in this file in the creation of products supporting the * Unicode Standard, and to make copies of this file in any form * for internal or external distribution as long as this notice * remains attached. */ /* --------------------------------------------------------------------- Conversions between UTF32, UTF-16, and UTF-8. Source code file. Author: Mark E. Davis, 1994. Rev History: Rick McGowan, fixes & updates May 2001. Sept 2001: fixed const & error conditions per mods suggested by S. Parent & A. Lillich. June 2002: Tim Dodd added detection and handling of incomplete source sequences, enhanced error detection, added casts to eliminate compiler warnings. July 2003: slight mods to back out aggressive FFFE detection. Jan 2004: updated switches in from-UTF8 conversions. Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. See the header file "ConvertUTF.h" for complete documentation. ------------------------------------------------------------------------ */ // ignore warnings in this file #include "libtorrent/aux_/disable_warnings_push.hpp" #include "libtorrent/ConvertUTF.h" #ifdef CVTUTF_DEBUG #include #endif static const int halfShift = 10; /* used for shifting by 10 bits */ static const UTF32 halfBase = 0x0010000UL; static const UTF32 halfMask = 0x3FFUL; #define UNI_SUR_HIGH_START (UTF32)0xD800 #define UNI_SUR_HIGH_END (UTF32)0xDBFF #define UNI_SUR_LOW_START (UTF32)0xDC00 #define UNI_SUR_LOW_END (UTF32)0xDFFF /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF32toUTF16 ( const UTF32** sourceStart, const UTF32* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF32* source = *sourceStart; UTF16* target = *targetStart; while (source < sourceEnd) { UTF32 ch; if (target >= targetEnd) { result = targetExhausted; break; } ch = *source++; if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { if (flags == strictConversion) { --source; /* return to the illegal value itself */ result = sourceIllegal; break; } else { *target++ = UNI_REPLACEMENT_CHAR; } } else { *target++ = (UTF16)ch; /* normal case */ } } else if (ch > UNI_MAX_LEGAL_UTF32) { if (flags == strictConversion) { result = sourceIllegal; } else { *target++ = UNI_REPLACEMENT_CHAR; } } else { /* target is a character in range 0xFFFF - 0x10FFFF. */ if (target + 1 >= targetEnd) { --source; /* Back up source pointer! */ result = targetExhausted; break; } ch -= halfBase; *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); } } *sourceStart = source; *targetStart = target; return result; } /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF16toUTF32 ( const UTF16** sourceStart, const UTF16* sourceEnd, UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF16* source = *sourceStart; UTF32* target = *targetStart; UTF32 ch, ch2; while (source < sourceEnd) { const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ ch = *source++; /* If we have a surrogate pair, convert to UTF32 first. */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { /* If the 16 bits following the high surrogate are in the source buffer... */ if (source < sourceEnd) { ch2 = *source; /* If it's a low surrogate, convert to UTF32. */ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + halfBase; ++source; } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ --source; /* return to the illegal value itself */ result = sourceIllegal; break; } } else { /* We don't have the 16 bits following the high surrogate. */ --source; /* return to the high surrogate */ result = sourceExhausted; break; } } else if (flags == strictConversion) { /* UTF-16 surrogate values are illegal in UTF-32 */ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { --source; /* return to the illegal value itself */ result = sourceIllegal; break; } } if (target >= targetEnd) { source = oldSource; /* Back up source pointer! */ result = targetExhausted; break; } *target++ = ch; } *sourceStart = source; *targetStart = target; #ifdef CVTUTF_DEBUG if (result == sourceIllegal) { fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); fflush(stderr); } #endif return result; } /* --------------------------------------------------------------------- */ /* * Index into the table below with the first byte of a UTF-8 sequence to * get the number of trailing bytes that are supposed to follow it. * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is * left as-is for anyone who may want to do such conversion, which was * allowed in earlier algorithms. */ const char trailingBytesForUTF8[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; /* * Magic values subtracted from a buffer value during UTF8 conversion. * This table contains as many values as there might be trailing bytes * in a UTF-8 sequence. */ const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; /* * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed * into the first byte, depending on how many bytes follow. There are * as many entries in this table as there are UTF-8 sequence types. * (I.e., one byte sequence, two byte... etc.). Remember that sequencs * for *legal* UTF-8 will be 4 or fewer bytes total. */ static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; /* --------------------------------------------------------------------- */ /* The interface converts a whole buffer to avoid function-call overhead. * Constants have been gathered. Loops & conditionals have been removed as * much as possible for efficiency, in favor of drop-through switches. * (See "Note A" at the bottom of the file for equivalent code.) * If your compiler supports it, the "isLegalUTF8" call can be turned * into an inline function. */ /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF16toUTF8 ( const UTF16** sourceStart, const UTF16* sourceEnd, UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF16* source = *sourceStart; UTF8* target = *targetStart; while (source < sourceEnd) { UTF32 ch; unsigned short bytesToWrite = 0; const UTF32 byteMask = 0xBF; const UTF32 byteMark = 0x80; const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ ch = *source++; /* If we have a surrogate pair, convert to UTF32 first. */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { /* If the 16 bits following the high surrogate are in the source buffer... */ if (source < sourceEnd) { UTF32 ch2 = *source; /* If it's a low surrogate, convert to UTF32. */ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + halfBase; ++source; } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ --source; /* return to the illegal value itself */ result = sourceIllegal; break; } } else { /* We don't have the 16 bits following the high surrogate. */ --source; /* return to the high surrogate */ result = sourceExhausted; break; } } else if (flags == strictConversion) { /* UTF-16 surrogate values are illegal in UTF-32 */ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { --source; /* return to the illegal value itself */ result = sourceIllegal; break; } } /* Figure out how many bytes the result will require */ if (ch < (UTF32)0x80) { bytesToWrite = 1; } else if (ch < (UTF32)0x800) { bytesToWrite = 2; } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; } else { bytesToWrite = 3; ch = UNI_REPLACEMENT_CHAR; } target += bytesToWrite; if (target > targetEnd) { source = oldSource; /* Back up source pointer! */ target -= bytesToWrite; result = targetExhausted; break; } switch (bytesToWrite) { /* note: everything falls through. */ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); } target += bytesToWrite; } *sourceStart = source; *targetStart = target; return result; } /* --------------------------------------------------------------------- */ /* * Utility routine to tell whether a sequence of bytes is legal UTF-8. * This must be called with the length pre-determined by the first byte. * If not calling this from ConvertUTF8to*, then the length can be set by: * length = trailingBytesForUTF8[*source]+1; * and the sequence is illegal right away if there aren't that many bytes * available. * If presented with a length > 4, this returns false. The Unicode * definition of UTF-8 goes up to 4-byte sequences. */ Boolean isLegalUTF8(const UTF8 *source, int length) { UTF8 a; const UTF8 *srcptr = source+length; switch (length) { default: return false; /* Everything else falls through when "true"... */ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; case 2: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; switch (*source) { /* no fall-through in this inner switch */ case 0xE0: if (a < 0xA0) return false; break; case 0xED: if (a > 0x9F) return false; break; case 0xF0: if (a < 0x90) return false; break; case 0xF4: if (a > 0x8F) return false; break; default: if (a < 0x80) return false; } case 1: if (*source >= 0x80 && *source < 0xC2) return false; } if (*source > 0xF4) return false; return true; } /* --------------------------------------------------------------------- */ /* * Exported function to return whether a UTF-8 sequence is legal or not. * This is not used here; it's just exported. */ Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { int length = trailingBytesForUTF8[*source]+1; if (source+length > sourceEnd) { return false; } return isLegalUTF8(source, length); } /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF8toUTF16 ( const UTF8** sourceStart, const UTF8* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF8* source = *sourceStart; UTF16* target = *targetStart; while (source < sourceEnd) { UTF32 ch = 0; unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; if (source + extraBytesToRead >= sourceEnd) { result = sourceExhausted; break; } /* Do this check whether lenient or strict */ if (! isLegalUTF8(source, extraBytesToRead+1)) { result = sourceIllegal; break; } /* * The cases all fall through. See "Note A" below. */ switch (extraBytesToRead) { case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ case 3: ch += *source++; ch <<= 6; case 2: ch += *source++; ch <<= 6; case 1: ch += *source++; ch <<= 6; case 0: ch += *source++; } ch -= offsetsFromUTF8[extraBytesToRead]; if (target >= targetEnd) { source -= (extraBytesToRead+1); /* Back up source pointer! */ result = targetExhausted; break; } if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ /* UTF-16 surrogate values are illegal in UTF-32 */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { if (flags == strictConversion) { source -= (extraBytesToRead+1); /* return to the illegal value itself */ result = sourceIllegal; break; } else { *target++ = UNI_REPLACEMENT_CHAR; } } else { *target++ = (UTF16)ch; /* normal case */ } } else if (ch > UNI_MAX_UTF16) { if (flags == strictConversion) { result = sourceIllegal; source -= (extraBytesToRead+1); /* return to the start */ break; /* Bail out; shouldn't continue */ } else { *target++ = UNI_REPLACEMENT_CHAR; } } else { /* target is a character in range 0xFFFF - 0x10FFFF. */ if (target + 1 >= targetEnd) { source -= (extraBytesToRead+1); /* Back up source pointer! */ result = targetExhausted; break; } ch -= halfBase; *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); } } *sourceStart = source; *targetStart = target; return result; } /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF32toUTF8 ( const UTF32** sourceStart, const UTF32* sourceEnd, UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF32* source = *sourceStart; UTF8* target = *targetStart; while (source < sourceEnd) { UTF32 ch; unsigned short bytesToWrite = 0; const UTF32 byteMask = 0xBF; const UTF32 byteMark = 0x80; ch = *source++; if (flags == strictConversion ) { /* UTF-16 surrogate values are illegal in UTF-32 */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { --source; /* return to the illegal value itself */ result = sourceIllegal; break; } } /* * Figure out how many bytes the result will require. Turn any * illegally large UTF32 things (> Plane 17) into replacement chars. */ if (ch < (UTF32)0x80) { bytesToWrite = 1; } else if (ch < (UTF32)0x800) { bytesToWrite = 2; } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; } else { bytesToWrite = 3; ch = UNI_REPLACEMENT_CHAR; result = sourceIllegal; } target += bytesToWrite; if (target > targetEnd) { --source; /* Back up source pointer! */ target -= bytesToWrite; result = targetExhausted; break; } switch (bytesToWrite) { /* note: everything falls through. */ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); } target += bytesToWrite; } *sourceStart = source; *targetStart = target; return result; } /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF8toUTF32 ( const UTF8** sourceStart, const UTF8* sourceEnd, UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF8* source = *sourceStart; UTF32* target = *targetStart; while (source < sourceEnd) { UTF32 ch = 0; unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; if (source + extraBytesToRead >= sourceEnd) { result = sourceExhausted; break; } /* Do this check whether lenient or strict */ if (! isLegalUTF8(source, extraBytesToRead+1)) { result = sourceIllegal; break; } /* * The cases all fall through. See "Note A" below. */ switch (extraBytesToRead) { case 5: ch += *source++; ch <<= 6; case 4: ch += *source++; ch <<= 6; case 3: ch += *source++; ch <<= 6; case 2: ch += *source++; ch <<= 6; case 1: ch += *source++; ch <<= 6; case 0: ch += *source++; } ch -= offsetsFromUTF8[extraBytesToRead]; if (target >= targetEnd) { source -= (extraBytesToRead+1); /* Back up the source pointer! */ result = targetExhausted; break; } if (ch <= UNI_MAX_LEGAL_UTF32) { /* * UTF-16 surrogate values are illegal in UTF-32, and anything * over Plane 17 (> 0x10FFFF) is illegal. */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { if (flags == strictConversion) { source -= (extraBytesToRead+1); /* return to the illegal value itself */ result = sourceIllegal; break; } else { *target++ = UNI_REPLACEMENT_CHAR; } } else { *target++ = ch; } } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ result = sourceIllegal; *target++ = UNI_REPLACEMENT_CHAR; } } *sourceStart = source; *targetStart = target; return result; } /* --------------------------------------------------------------------- Note A. The fall-through switches in UTF-8 reading code save a temp variable, some decrements & conditionals. The switches are equivalent to the following loop: { int tmpBytesToRead = extraBytesToRead+1; do { ch += *source++; --tmpBytesToRead; if (tmpBytesToRead) ch <<= 6; } while (tmpBytesToRead > 0); } In UTF-8 writing code, the switches on "bytesToWrite" are similarly unrolled loops. --------------------------------------------------------------------- */ libtorrent-rasterbar-1.1.13/src/Makefile.am000066400000000000000000000127141351156116000205730ustar00rootroot00000000000000AUTOMAKE_OPTIONS = subdir-objects lib_LTLIBRARIES = libtorrent-rasterbar.la if ENABLE_DHT KADEMLIA_SOURCES = \ kademlia/dht_storage.cpp \ kademlia/dht_tracker.cpp \ kademlia/find_data.cpp \ kademlia/put_data.cpp \ kademlia/msg.cpp \ kademlia/node.cpp \ kademlia/node_entry.cpp \ kademlia/node_id.cpp \ kademlia/refresh.cpp \ kademlia/routing_table.cpp \ kademlia/rpc_manager.cpp \ kademlia/traversal_algorithm.cpp \ kademlia/dos_blocker.cpp \ kademlia/get_peers.cpp \ kademlia/get_item.cpp \ kademlia/item.cpp \ ../ed25519/src/add_scalar.cpp \ ../ed25519/src/fe.cpp \ ../ed25519/src/ge.cpp \ ../ed25519/src/key_exchange.cpp \ ../ed25519/src/keypair.cpp \ ../ed25519/src/sc.cpp \ ../ed25519/src/seed.cpp \ ../ed25519/src/sha512.cpp \ ../ed25519/src/sign.cpp \ ../ed25519/src/verify.cpp endif if ! WITH_OPENSSL BUILTIN_CRYPTO_SOURCES = \ sha1.cpp endif libtorrent_rasterbar_la_SOURCES = \ web_connection_base.cpp \ alert.cpp \ alert_manager.cpp \ announce_entry.cpp \ assert.cpp \ bandwidth_limit.cpp \ bandwidth_manager.cpp \ bandwidth_queue_entry.cpp \ bdecode.cpp \ bitfield.cpp \ bloom_filter.cpp \ broadcast_socket.cpp \ block_cache.cpp \ bt_peer_connection.cpp \ chained_buffer.cpp \ choker.cpp \ close_reason.cpp \ ConvertUTF.cpp \ cpuid.cpp \ crc32c.cpp \ create_torrent.cpp \ disk_buffer_holder.cpp \ disk_buffer_pool.cpp \ disk_io_job.cpp \ disk_io_thread.cpp \ disk_job_pool.cpp \ entry.cpp \ enum_net.cpp \ error_code.cpp \ escape_string.cpp \ file.cpp \ file_pool.cpp \ file_storage.cpp \ fingerprint.cpp \ gzip.cpp \ hasher.cpp \ hex.cpp \ http_connection.cpp \ http_parser.cpp \ http_seed_connection.cpp \ http_stream.cpp \ http_tracker_connection.cpp \ i2p_stream.cpp \ identify_client.cpp \ instantiate_connection.cpp \ ip_filter.cpp \ ip_voter.cpp \ lazy_bdecode.cpp \ lsd.cpp \ lt_trackers.cpp \ magnet_uri.cpp \ merkle.cpp \ metadata_transfer.cpp \ mpi.cpp \ natpmp.cpp \ parse_url.cpp \ part_file.cpp \ pe_crypto.cpp \ performance_counters.cpp \ peer_connection.cpp \ peer_connection_handle.cpp \ peer_class.cpp \ peer_class_set.cpp \ piece_picker.cpp \ platform_util.cpp \ packet_buffer.cpp \ proxy_base.cpp \ peer_list.cpp \ puff.cpp \ random.cpp \ receive_buffer.cpp \ request_blocks.cpp \ resolve_links.cpp \ resolver.cpp \ rss.cpp \ session.cpp \ session_call.cpp \ session_handle.cpp \ session_impl.cpp \ session_settings.cpp \ proxy_settings.cpp \ settings_pack.cpp \ smart_ban.cpp \ socket_io.cpp \ socket_type.cpp \ socks5_stream.cpp \ stat.cpp \ stat_cache.cpp \ storage.cpp \ session_stats.cpp \ string_util.cpp \ thread.cpp \ torrent.cpp \ torrent_handle.cpp \ torrent_info.cpp \ torrent_peer.cpp \ torrent_peer_allocator.cpp \ torrent_status.cpp \ time.cpp \ timestamp_history.cpp \ tracker_manager.cpp \ udp_socket.cpp \ udp_tracker_connection.cpp \ upnp.cpp \ ut_metadata.cpp \ ut_pex.cpp \ utf8.cpp \ utp_socket_manager.cpp \ utp_stream.cpp \ web_peer_connection.cpp \ xml_parse.cpp \ version.cpp \ file_progress.cpp \ \ $(KADEMLIA_SOURCES) \ \ $(BUILTIN_CRYPTO_SOURCES) libtorrent_rasterbar_la_LDFLAGS = -version-info $(INTERFACE_VERSION_INFO) libtorrent_rasterbar_la_LIBADD = @BOOST_SYSTEM_LIB@ @OPENSSL_LIBS@ AM_CPPFLAGS = -DTORRENT_BUILDING_LIBRARY -I$(top_srcdir)/include -I$(top_srcdir)/ed25519/src @DEBUGFLAGS@ @OPENSSL_INCLUDES@ AM_CFLAGS = -I$(top_srcdir)/ed25519/src -std=c99 AM_LDFLAGS = @OPENSSL_LDFLAGS@ libtorrent-rasterbar-1.1.13/src/Makefile.in000066400000000000000000001354561351156116000206150ustar00rootroot00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 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@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd 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@ target_triplet = @target@ subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ $(top_srcdir)/m4/ax_boost_chrono.m4 \ $(top_srcdir)/m4/ax_boost_python.m4 \ $(top_srcdir)/m4/ax_boost_random.m4 \ $(top_srcdir)/m4/ax_boost_system.m4 \ $(top_srcdir)/m4/ax_check_openssl.m4 \ $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_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 = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) libtorrent_rasterbar_la_DEPENDENCIES = am__libtorrent_rasterbar_la_SOURCES_DIST = web_connection_base.cpp \ alert.cpp alert_manager.cpp announce_entry.cpp assert.cpp \ bandwidth_limit.cpp bandwidth_manager.cpp \ bandwidth_queue_entry.cpp bdecode.cpp bitfield.cpp \ bloom_filter.cpp broadcast_socket.cpp block_cache.cpp \ bt_peer_connection.cpp chained_buffer.cpp choker.cpp \ close_reason.cpp ConvertUTF.cpp cpuid.cpp crc32c.cpp \ create_torrent.cpp disk_buffer_holder.cpp disk_buffer_pool.cpp \ disk_io_job.cpp disk_io_thread.cpp disk_job_pool.cpp entry.cpp \ enum_net.cpp error_code.cpp escape_string.cpp file.cpp \ file_pool.cpp file_storage.cpp fingerprint.cpp gzip.cpp \ hasher.cpp hex.cpp http_connection.cpp http_parser.cpp \ http_seed_connection.cpp http_stream.cpp \ http_tracker_connection.cpp i2p_stream.cpp identify_client.cpp \ instantiate_connection.cpp ip_filter.cpp ip_voter.cpp \ lazy_bdecode.cpp lsd.cpp lt_trackers.cpp magnet_uri.cpp \ merkle.cpp metadata_transfer.cpp mpi.cpp natpmp.cpp \ parse_url.cpp part_file.cpp pe_crypto.cpp \ performance_counters.cpp peer_connection.cpp \ peer_connection_handle.cpp peer_class.cpp peer_class_set.cpp \ piece_picker.cpp platform_util.cpp packet_buffer.cpp \ proxy_base.cpp peer_list.cpp puff.cpp random.cpp \ receive_buffer.cpp request_blocks.cpp resolve_links.cpp \ resolver.cpp rss.cpp session.cpp session_call.cpp \ session_handle.cpp session_impl.cpp session_settings.cpp \ proxy_settings.cpp settings_pack.cpp smart_ban.cpp \ socket_io.cpp socket_type.cpp socks5_stream.cpp stat.cpp \ stat_cache.cpp storage.cpp session_stats.cpp string_util.cpp \ thread.cpp torrent.cpp torrent_handle.cpp torrent_info.cpp \ torrent_peer.cpp torrent_peer_allocator.cpp torrent_status.cpp \ time.cpp timestamp_history.cpp tracker_manager.cpp \ udp_socket.cpp udp_tracker_connection.cpp upnp.cpp \ ut_metadata.cpp ut_pex.cpp utf8.cpp utp_socket_manager.cpp \ utp_stream.cpp web_peer_connection.cpp xml_parse.cpp \ version.cpp file_progress.cpp kademlia/dht_storage.cpp \ kademlia/dht_tracker.cpp kademlia/find_data.cpp \ kademlia/put_data.cpp kademlia/msg.cpp kademlia/node.cpp \ kademlia/node_entry.cpp kademlia/node_id.cpp \ kademlia/refresh.cpp kademlia/routing_table.cpp \ kademlia/rpc_manager.cpp kademlia/traversal_algorithm.cpp \ kademlia/dos_blocker.cpp kademlia/get_peers.cpp \ kademlia/get_item.cpp kademlia/item.cpp \ ../ed25519/src/add_scalar.cpp ../ed25519/src/fe.cpp \ ../ed25519/src/ge.cpp ../ed25519/src/key_exchange.cpp \ ../ed25519/src/keypair.cpp ../ed25519/src/sc.cpp \ ../ed25519/src/seed.cpp ../ed25519/src/sha512.cpp \ ../ed25519/src/sign.cpp ../ed25519/src/verify.cpp sha1.cpp am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_DHT_TRUE@am__objects_1 = kademlia/dht_storage.lo \ @ENABLE_DHT_TRUE@ kademlia/dht_tracker.lo kademlia/find_data.lo \ @ENABLE_DHT_TRUE@ kademlia/put_data.lo kademlia/msg.lo \ @ENABLE_DHT_TRUE@ kademlia/node.lo kademlia/node_entry.lo \ @ENABLE_DHT_TRUE@ kademlia/node_id.lo kademlia/refresh.lo \ @ENABLE_DHT_TRUE@ kademlia/routing_table.lo \ @ENABLE_DHT_TRUE@ kademlia/rpc_manager.lo \ @ENABLE_DHT_TRUE@ kademlia/traversal_algorithm.lo \ @ENABLE_DHT_TRUE@ kademlia/dos_blocker.lo kademlia/get_peers.lo \ @ENABLE_DHT_TRUE@ kademlia/get_item.lo kademlia/item.lo \ @ENABLE_DHT_TRUE@ ../ed25519/src/add_scalar.lo \ @ENABLE_DHT_TRUE@ ../ed25519/src/fe.lo ../ed25519/src/ge.lo \ @ENABLE_DHT_TRUE@ ../ed25519/src/key_exchange.lo \ @ENABLE_DHT_TRUE@ ../ed25519/src/keypair.lo \ @ENABLE_DHT_TRUE@ ../ed25519/src/sc.lo ../ed25519/src/seed.lo \ @ENABLE_DHT_TRUE@ ../ed25519/src/sha512.lo \ @ENABLE_DHT_TRUE@ ../ed25519/src/sign.lo \ @ENABLE_DHT_TRUE@ ../ed25519/src/verify.lo @WITH_OPENSSL_FALSE@am__objects_2 = sha1.lo am_libtorrent_rasterbar_la_OBJECTS = web_connection_base.lo alert.lo \ alert_manager.lo announce_entry.lo assert.lo \ bandwidth_limit.lo bandwidth_manager.lo \ bandwidth_queue_entry.lo bdecode.lo bitfield.lo \ bloom_filter.lo broadcast_socket.lo block_cache.lo \ bt_peer_connection.lo chained_buffer.lo choker.lo \ close_reason.lo ConvertUTF.lo cpuid.lo crc32c.lo \ create_torrent.lo disk_buffer_holder.lo disk_buffer_pool.lo \ disk_io_job.lo disk_io_thread.lo disk_job_pool.lo entry.lo \ enum_net.lo error_code.lo escape_string.lo file.lo \ file_pool.lo file_storage.lo fingerprint.lo gzip.lo hasher.lo \ hex.lo http_connection.lo http_parser.lo \ http_seed_connection.lo http_stream.lo \ http_tracker_connection.lo i2p_stream.lo identify_client.lo \ instantiate_connection.lo ip_filter.lo ip_voter.lo \ lazy_bdecode.lo lsd.lo lt_trackers.lo magnet_uri.lo merkle.lo \ metadata_transfer.lo mpi.lo natpmp.lo parse_url.lo \ part_file.lo pe_crypto.lo performance_counters.lo \ peer_connection.lo peer_connection_handle.lo peer_class.lo \ peer_class_set.lo piece_picker.lo platform_util.lo \ packet_buffer.lo proxy_base.lo peer_list.lo puff.lo random.lo \ receive_buffer.lo request_blocks.lo resolve_links.lo \ resolver.lo rss.lo session.lo session_call.lo \ session_handle.lo session_impl.lo session_settings.lo \ proxy_settings.lo settings_pack.lo smart_ban.lo socket_io.lo \ socket_type.lo socks5_stream.lo stat.lo stat_cache.lo \ storage.lo session_stats.lo string_util.lo thread.lo \ torrent.lo torrent_handle.lo torrent_info.lo torrent_peer.lo \ torrent_peer_allocator.lo torrent_status.lo time.lo \ timestamp_history.lo tracker_manager.lo udp_socket.lo \ udp_tracker_connection.lo upnp.lo ut_metadata.lo ut_pex.lo \ utf8.lo utp_socket_manager.lo utp_stream.lo \ web_peer_connection.lo xml_parse.lo version.lo \ file_progress.lo $(am__objects_1) $(am__objects_2) libtorrent_rasterbar_la_OBJECTS = \ $(am_libtorrent_rasterbar_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libtorrent_rasterbar_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libtorrent_rasterbar_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtorrent_rasterbar_la_SOURCES) DIST_SOURCES = $(am__libtorrent_rasterbar_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BOOST_CHRONO_LIB = @BOOST_CHRONO_LIB@ BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ BOOST_LDFLAGS = @BOOST_LDFLAGS@ BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ BOOST_RANDOM_LIB = @BOOST_RANDOM_LIB@ BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUGFLAGS = @DEBUGFLAGS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONV_LIBS = @ICONV_LIBS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ PYTHON_CXXFLAGS = @PYTHON_CXXFLAGS@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = subdir-objects lib_LTLIBRARIES = libtorrent-rasterbar.la @ENABLE_DHT_TRUE@KADEMLIA_SOURCES = \ @ENABLE_DHT_TRUE@ kademlia/dht_storage.cpp \ @ENABLE_DHT_TRUE@ kademlia/dht_tracker.cpp \ @ENABLE_DHT_TRUE@ kademlia/find_data.cpp \ @ENABLE_DHT_TRUE@ kademlia/put_data.cpp \ @ENABLE_DHT_TRUE@ kademlia/msg.cpp \ @ENABLE_DHT_TRUE@ kademlia/node.cpp \ @ENABLE_DHT_TRUE@ kademlia/node_entry.cpp \ @ENABLE_DHT_TRUE@ kademlia/node_id.cpp \ @ENABLE_DHT_TRUE@ kademlia/refresh.cpp \ @ENABLE_DHT_TRUE@ kademlia/routing_table.cpp \ @ENABLE_DHT_TRUE@ kademlia/rpc_manager.cpp \ @ENABLE_DHT_TRUE@ kademlia/traversal_algorithm.cpp \ @ENABLE_DHT_TRUE@ kademlia/dos_blocker.cpp \ @ENABLE_DHT_TRUE@ kademlia/get_peers.cpp \ @ENABLE_DHT_TRUE@ kademlia/get_item.cpp \ @ENABLE_DHT_TRUE@ kademlia/item.cpp \ @ENABLE_DHT_TRUE@ ../ed25519/src/add_scalar.cpp \ @ENABLE_DHT_TRUE@ ../ed25519/src/fe.cpp \ @ENABLE_DHT_TRUE@ ../ed25519/src/ge.cpp \ @ENABLE_DHT_TRUE@ ../ed25519/src/key_exchange.cpp \ @ENABLE_DHT_TRUE@ ../ed25519/src/keypair.cpp \ @ENABLE_DHT_TRUE@ ../ed25519/src/sc.cpp \ @ENABLE_DHT_TRUE@ ../ed25519/src/seed.cpp \ @ENABLE_DHT_TRUE@ ../ed25519/src/sha512.cpp \ @ENABLE_DHT_TRUE@ ../ed25519/src/sign.cpp \ @ENABLE_DHT_TRUE@ ../ed25519/src/verify.cpp @WITH_OPENSSL_FALSE@BUILTIN_CRYPTO_SOURCES = \ @WITH_OPENSSL_FALSE@ sha1.cpp libtorrent_rasterbar_la_SOURCES = \ web_connection_base.cpp \ alert.cpp \ alert_manager.cpp \ announce_entry.cpp \ assert.cpp \ bandwidth_limit.cpp \ bandwidth_manager.cpp \ bandwidth_queue_entry.cpp \ bdecode.cpp \ bitfield.cpp \ bloom_filter.cpp \ broadcast_socket.cpp \ block_cache.cpp \ bt_peer_connection.cpp \ chained_buffer.cpp \ choker.cpp \ close_reason.cpp \ ConvertUTF.cpp \ cpuid.cpp \ crc32c.cpp \ create_torrent.cpp \ disk_buffer_holder.cpp \ disk_buffer_pool.cpp \ disk_io_job.cpp \ disk_io_thread.cpp \ disk_job_pool.cpp \ entry.cpp \ enum_net.cpp \ error_code.cpp \ escape_string.cpp \ file.cpp \ file_pool.cpp \ file_storage.cpp \ fingerprint.cpp \ gzip.cpp \ hasher.cpp \ hex.cpp \ http_connection.cpp \ http_parser.cpp \ http_seed_connection.cpp \ http_stream.cpp \ http_tracker_connection.cpp \ i2p_stream.cpp \ identify_client.cpp \ instantiate_connection.cpp \ ip_filter.cpp \ ip_voter.cpp \ lazy_bdecode.cpp \ lsd.cpp \ lt_trackers.cpp \ magnet_uri.cpp \ merkle.cpp \ metadata_transfer.cpp \ mpi.cpp \ natpmp.cpp \ parse_url.cpp \ part_file.cpp \ pe_crypto.cpp \ performance_counters.cpp \ peer_connection.cpp \ peer_connection_handle.cpp \ peer_class.cpp \ peer_class_set.cpp \ piece_picker.cpp \ platform_util.cpp \ packet_buffer.cpp \ proxy_base.cpp \ peer_list.cpp \ puff.cpp \ random.cpp \ receive_buffer.cpp \ request_blocks.cpp \ resolve_links.cpp \ resolver.cpp \ rss.cpp \ session.cpp \ session_call.cpp \ session_handle.cpp \ session_impl.cpp \ session_settings.cpp \ proxy_settings.cpp \ settings_pack.cpp \ smart_ban.cpp \ socket_io.cpp \ socket_type.cpp \ socks5_stream.cpp \ stat.cpp \ stat_cache.cpp \ storage.cpp \ session_stats.cpp \ string_util.cpp \ thread.cpp \ torrent.cpp \ torrent_handle.cpp \ torrent_info.cpp \ torrent_peer.cpp \ torrent_peer_allocator.cpp \ torrent_status.cpp \ time.cpp \ timestamp_history.cpp \ tracker_manager.cpp \ udp_socket.cpp \ udp_tracker_connection.cpp \ upnp.cpp \ ut_metadata.cpp \ ut_pex.cpp \ utf8.cpp \ utp_socket_manager.cpp \ utp_stream.cpp \ web_peer_connection.cpp \ xml_parse.cpp \ version.cpp \ file_progress.cpp \ \ $(KADEMLIA_SOURCES) \ \ $(BUILTIN_CRYPTO_SOURCES) libtorrent_rasterbar_la_LDFLAGS = -version-info $(INTERFACE_VERSION_INFO) libtorrent_rasterbar_la_LIBADD = @BOOST_SYSTEM_LIB@ @OPENSSL_LIBS@ AM_CPPFLAGS = -DTORRENT_BUILDING_LIBRARY -I$(top_srcdir)/include -I$(top_srcdir)/ed25519/src @DEBUGFLAGS@ @OPENSSL_INCLUDES@ AM_CFLAGS = -I$(top_srcdir)/ed25519/src -std=c99 AM_LDFLAGS = @OPENSSL_LDFLAGS@ all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/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: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } kademlia/$(am__dirstamp): @$(MKDIR_P) kademlia @: > kademlia/$(am__dirstamp) kademlia/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) kademlia/$(DEPDIR) @: > kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/dht_storage.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/dht_tracker.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/find_data.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/put_data.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/msg.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/node.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/node_entry.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/node_id.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/refresh.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/routing_table.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/rpc_manager.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/traversal_algorithm.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/dos_blocker.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/get_peers.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/get_item.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) kademlia/item.lo: kademlia/$(am__dirstamp) \ kademlia/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/$(am__dirstamp): @$(MKDIR_P) ../ed25519/src @: > ../ed25519/src/$(am__dirstamp) ../ed25519/src/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ../ed25519/src/$(DEPDIR) @: > ../ed25519/src/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/add_scalar.lo: ../ed25519/src/$(am__dirstamp) \ ../ed25519/src/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/fe.lo: ../ed25519/src/$(am__dirstamp) \ ../ed25519/src/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/ge.lo: ../ed25519/src/$(am__dirstamp) \ ../ed25519/src/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/key_exchange.lo: ../ed25519/src/$(am__dirstamp) \ ../ed25519/src/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/keypair.lo: ../ed25519/src/$(am__dirstamp) \ ../ed25519/src/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/sc.lo: ../ed25519/src/$(am__dirstamp) \ ../ed25519/src/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/seed.lo: ../ed25519/src/$(am__dirstamp) \ ../ed25519/src/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/sha512.lo: ../ed25519/src/$(am__dirstamp) \ ../ed25519/src/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/sign.lo: ../ed25519/src/$(am__dirstamp) \ ../ed25519/src/$(DEPDIR)/$(am__dirstamp) ../ed25519/src/verify.lo: ../ed25519/src/$(am__dirstamp) \ ../ed25519/src/$(DEPDIR)/$(am__dirstamp) libtorrent-rasterbar.la: $(libtorrent_rasterbar_la_OBJECTS) $(libtorrent_rasterbar_la_DEPENDENCIES) $(EXTRA_libtorrent_rasterbar_la_DEPENDENCIES) $(AM_V_CXXLD)$(libtorrent_rasterbar_la_LINK) -rpath $(libdir) $(libtorrent_rasterbar_la_OBJECTS) $(libtorrent_rasterbar_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f ../ed25519/src/*.$(OBJEXT) -rm -f ../ed25519/src/*.lo -rm -f kademlia/*.$(OBJEXT) -rm -f kademlia/*.lo distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/add_scalar.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/fe.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/ge.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/key_exchange.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/keypair.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/sc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/seed.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/sha512.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/sign.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/verify.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConvertUTF.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert_manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/announce_entry.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assert.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth_limit.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth_manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth_queue_entry.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bdecode.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitfield.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/block_cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bloom_filter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/broadcast_socket.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bt_peer_connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chained_buffer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/choker.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/close_reason.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpuid.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc32c.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/create_torrent.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_buffer_holder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_buffer_pool.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_io_job.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_io_thread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_job_pool.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/enum_net.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error_code.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)/file_pool.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_progress.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fingerprint.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gzip.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hasher.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_seed_connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_tracker_connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i2p_stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/identify_client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/instantiate_connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_filter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_voter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lazy_bdecode.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lt_trackers.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/magnet_uri.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/merkle.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata_transfer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/natpmp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_buffer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse_url.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/part_file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pe_crypto.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_class.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_class_set.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_connection_handle.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_list.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/performance_counters.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/piece_picker.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/platform_util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proxy_base.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proxy_settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/puff.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/receive_buffer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/request_blocks.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve_links.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolver.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rss.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_call.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_handle.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_impl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_stats.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings_pack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smart_ban.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_io.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_type.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socks5_stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat_cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string_util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timestamp_history.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)/torrent_peer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_peer_allocator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_status.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tracker_manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_socket.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_tracker_connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upnp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ut_metadata.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ut_pex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf8.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utp_socket_manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utp_stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_connection_base.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_peer_connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xml_parse.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/dht_storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/dht_tracker.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/dos_blocker.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/find_data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/get_item.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/get_peers.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/item.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/msg.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/node.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/node_entry.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/node_id.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/put_data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/refresh.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/routing_table.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/rpc_manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/traversal_algorithm.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf ../ed25519/src/.libs ../ed25519/src/_libs -rm -rf kademlia/.libs kademlia/_libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$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) 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: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f ../ed25519/src/$(DEPDIR)/$(am__dirstamp) -rm -f ../ed25519/src/$(am__dirstamp) -rm -f kademlia/$(DEPDIR)/$(am__dirstamp) -rm -f kademlia/$(am__dirstamp) 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 ../ed25519/src/$(DEPDIR) ./$(DEPDIR) kademlia/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ../ed25519/src/$(DEPDIR) ./$(DEPDIR) kademlia/$(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-libLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ ctags-am 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-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-libLTLIBRARIES install-man install-pdf \ install-pdf-am install-ps install-ps-am 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 tags-am uninstall uninstall-am uninstall-libLTLIBRARIES .PRECIOUS: Makefile # 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: libtorrent-rasterbar-1.1.13/src/alert.cpp000066400000000000000000001451001351156116000203460ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include "libtorrent/config.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/time.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/aux_/time.hpp" #include "libtorrent/performance_counters.hpp" #include "libtorrent/stack_allocator.hpp" #include "libtorrent/piece_picker.hpp" // for piece_block #include "libtorrent/aux_/escape_string.hpp" // for convert_from_native #include namespace libtorrent { alert::alert() : m_timestamp(clock_type::now()) {} alert::~alert() {} time_point alert::timestamp() const { return m_timestamp; } torrent_alert::torrent_alert(aux::stack_allocator& alloc , torrent_handle const& h) : handle(h) , m_alloc(alloc) { boost::shared_ptr t = h.native_handle(); if (t) { std::string name_str = t->name(); if (!name_str.empty()) { m_name_idx = alloc.copy_string(name_str); } else { char msg[41]; to_hex(t->info_hash().data(), 20, msg); m_name_idx = alloc.copy_string(msg); } } else { m_name_idx = alloc.copy_string(""); } #ifndef TORRENT_NO_DEPRECATE name = m_alloc.ptr(m_name_idx); #endif } char const* torrent_alert::torrent_name() const { return m_alloc.ptr(m_name_idx); } std::string torrent_alert::message() const { if (!handle.is_valid()) return " - "; return torrent_name(); } peer_alert::peer_alert(aux::stack_allocator& alloc , torrent_handle const& h , tcp::endpoint const& i , peer_id const& pi) : torrent_alert(alloc, h) , ip(i) , pid(pi) {} std::string peer_alert::message() const { error_code ec; return torrent_alert::message() + " peer (" + print_endpoint(ip) + ", " + identify_client(pid) + ")"; } tracker_alert::tracker_alert(aux::stack_allocator& alloc , torrent_handle const& h , std::string const& u) : torrent_alert(alloc, h) #ifndef TORRENT_NO_DEPRECATE , url(u) #endif , m_url_idx(alloc.copy_string(u)) {} char const* tracker_alert::tracker_url() const { return m_alloc.ptr(m_url_idx); } std::string tracker_alert::message() const { return torrent_alert::message() + " (" + tracker_url() + ")"; } read_piece_alert::read_piece_alert(aux::stack_allocator& alloc , torrent_handle const& h , int p, boost::shared_array d, int s) : torrent_alert(alloc, h) , buffer(d) , piece(p) , size(s) {} read_piece_alert::read_piece_alert(aux::stack_allocator& alloc , torrent_handle h, int p, error_code e) : torrent_alert(alloc, h) , ec(e) , piece(p) , size(0) {} std::string read_piece_alert::message() const { char msg[200]; if (ec) { snprintf(msg, sizeof(msg), "%s: read_piece %u failed: %s" , torrent_alert::message().c_str() , piece , convert_from_native(ec.message()).c_str()); } else { snprintf(msg, sizeof(msg), "%s: read_piece %u successful" , torrent_alert::message().c_str() , piece); } return msg; } file_completed_alert::file_completed_alert(aux::stack_allocator& alloc , torrent_handle const& h , int idx) : torrent_alert(alloc, h) , index(idx) {} std::string file_completed_alert::message() const { char msg[200 + TORRENT_MAX_PATH]; snprintf(msg, sizeof(msg), "%s: file %d finished downloading" , torrent_alert::message().c_str(), index); return msg; } file_renamed_alert::file_renamed_alert(aux::stack_allocator& alloc , torrent_handle const& h , std::string const& n , int idx) : torrent_alert(alloc, h) #ifndef TORRENT_NO_DEPRECATE , name(n) #endif , index(idx) , m_name_idx(alloc.copy_string(n)) {} char const* file_renamed_alert::new_name() const { return m_alloc.ptr(m_name_idx); } std::string file_renamed_alert::message() const { char msg[200 + TORRENT_MAX_PATH * 2]; snprintf(msg, sizeof(msg), "%s: file %d renamed to %s" , torrent_alert::message().c_str(), index, new_name()); return msg; } file_rename_failed_alert::file_rename_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h , int idx , error_code ec) : torrent_alert(alloc, h) , index(idx) , error(ec) {} std::string file_rename_failed_alert::message() const { char ret[200 + TORRENT_MAX_PATH * 2]; snprintf(ret, sizeof(ret), "%s: failed to rename file %d: %s" , torrent_alert::message().c_str(), index, convert_from_native(error.message()).c_str()); return ret; } performance_alert::performance_alert(aux::stack_allocator& alloc , torrent_handle const& h , performance_warning_t w) : torrent_alert(alloc, h) , warning_code(w) {} std::string performance_alert::message() const { static char const* warning_str[] = { "max outstanding disk writes reached", "max outstanding piece requests reached", "upload limit too low (download rate will suffer)", "download limit too low (upload rate will suffer)", "send buffer watermark too low (upload rate will suffer)", "too many optimistic unchoke slots", "using bittyrant unchoker with no upload rate limit set", "the disk queue limit is too high compared to the cache size. The disk queue eats into the cache size", "outstanding AIO operations limit reached", "too few ports allowed for outgoing connections", "too few file descriptors are allowed for this process. connection limit lowered" }; return torrent_alert::message() + ": performance warning: " + warning_str[warning_code]; } state_changed_alert::state_changed_alert(aux::stack_allocator& alloc , torrent_handle const& h , torrent_status::state_t st , torrent_status::state_t prev_st) : torrent_alert(alloc, h) , state(st) , prev_state(prev_st) {} std::string state_changed_alert::message() const { static char const* state_str[] = {"checking (q)", "checking", "dl metadata" , "downloading", "finished", "seeding", "allocating" , "checking (r)"}; return torrent_alert::message() + ": state changed to: " + state_str[state]; } tracker_error_alert::tracker_error_alert(aux::stack_allocator& alloc , torrent_handle const& h , int times , int status , std::string const& u , error_code const& e , std::string const& m) : tracker_alert(alloc, h, u) , times_in_row(times) , status_code(status) , error(e) #ifndef TORRENT_NO_DEPRECATE , msg(m) #endif , m_msg_idx(alloc.copy_string(m)) { TORRENT_ASSERT(!u.empty()); } char const* tracker_error_alert::error_message() const { return m_alloc.ptr(m_msg_idx); } std::string tracker_error_alert::message() const { char ret[400]; snprintf(ret, sizeof(ret), "%s (%d) %s \"%s\" (%d)" , tracker_alert::message().c_str(), status_code , convert_from_native(error.message()).c_str(), error_message() , times_in_row); return ret; } tracker_warning_alert::tracker_warning_alert(aux::stack_allocator& alloc , torrent_handle const& h , std::string const& u , std::string const& m) : tracker_alert(alloc, h, u) #ifndef TORRENT_NO_DEPRECATE , msg(m) #endif , m_msg_idx(alloc.copy_string(m)) { TORRENT_ASSERT(!u.empty()); } char const* tracker_warning_alert::warning_message() const { return m_alloc.ptr(m_msg_idx); } std::string tracker_warning_alert::message() const { return tracker_alert::message() + " warning: " + warning_message(); } scrape_reply_alert::scrape_reply_alert(aux::stack_allocator& alloc , torrent_handle const& h , int incomp , int comp , std::string const& u) : tracker_alert(alloc, h, u) , incomplete(incomp) , complete(comp) { TORRENT_ASSERT(!u.empty()); } std::string scrape_reply_alert::message() const { char ret[400]; snprintf(ret, sizeof(ret), "%s scrape reply: %u %u" , tracker_alert::message().c_str(), incomplete, complete); return ret; } scrape_failed_alert::scrape_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h , std::string const& u , error_code const& e) : tracker_alert(alloc, h, u) #ifndef TORRENT_NO_DEPRECATE , msg(convert_from_native(e.message())) #endif , error(e) , m_msg_idx(-1) { TORRENT_ASSERT(!u.empty()); } scrape_failed_alert::scrape_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h , std::string const& u , std::string const& m) : tracker_alert(alloc, h, u) #ifndef TORRENT_NO_DEPRECATE , msg(m) #endif , error(errors::tracker_failure) , m_msg_idx(alloc.copy_string(m)) { TORRENT_ASSERT(!u.empty()); } char const* scrape_failed_alert::error_message() const { if (m_msg_idx == -1) return ""; else return m_alloc.ptr(m_msg_idx); } std::string scrape_failed_alert::message() const { return tracker_alert::message() + " scrape failed: " + error_message(); } tracker_reply_alert::tracker_reply_alert(aux::stack_allocator& alloc , torrent_handle const& h , int np , std::string const& u) : tracker_alert(alloc, h, u) , num_peers(np) { TORRENT_ASSERT(!u.empty()); } std::string tracker_reply_alert::message() const { char ret[400]; snprintf(ret, sizeof(ret), "%s received peers: %u" , tracker_alert::message().c_str(), num_peers); return ret; } dht_reply_alert::dht_reply_alert(aux::stack_allocator& alloc , torrent_handle const& h , int np) : tracker_alert(alloc, h, "") , num_peers(np) {} std::string dht_reply_alert::message() const { char ret[400]; snprintf(ret, sizeof(ret), "%s received DHT peers: %u" , tracker_alert::message().c_str(), num_peers); return ret; } tracker_announce_alert::tracker_announce_alert(aux::stack_allocator& alloc , torrent_handle const& h , std::string const& u, int e) : tracker_alert(alloc, h, u) , event(e) { TORRENT_ASSERT(!u.empty()); } std::string tracker_announce_alert::message() const { static const char* event_str[] = {"none", "completed", "started", "stopped", "paused"}; TORRENT_ASSERT_VAL(event < int(sizeof(event_str)/sizeof(event_str[0])), event); return tracker_alert::message() + " sending announce (" + event_str[event] + ")"; } hash_failed_alert::hash_failed_alert( aux::stack_allocator& alloc , torrent_handle const& h , int index) : torrent_alert(alloc, h) , piece_index(index) { TORRENT_ASSERT(index >= 0); } std::string hash_failed_alert::message() const { char ret[400]; snprintf(ret, sizeof(ret), "%s hash for piece %u failed" , torrent_alert::message().c_str(), piece_index); return ret; } peer_ban_alert::peer_ban_alert(aux::stack_allocator& alloc , torrent_handle h, tcp::endpoint const& ep , peer_id const& peer_id) : peer_alert(alloc, h, ep, peer_id) {} std::string peer_ban_alert::message() const { return peer_alert::message() + " banned peer"; } peer_unsnubbed_alert::peer_unsnubbed_alert(aux::stack_allocator& alloc , torrent_handle h, tcp::endpoint const& ep , peer_id const& peer_id) : peer_alert(alloc, h, ep, peer_id) {} std::string peer_unsnubbed_alert::message() const { return peer_alert::message() + " peer unsnubbed"; } peer_snubbed_alert::peer_snubbed_alert(aux::stack_allocator& alloc , torrent_handle h, tcp::endpoint const& ep , peer_id const& peer_id) : peer_alert(alloc, h, ep, peer_id) {} std::string peer_snubbed_alert::message() const { return peer_alert::message() + " peer snubbed"; } invalid_request_alert::invalid_request_alert(aux::stack_allocator& alloc , torrent_handle const& h, tcp::endpoint const& ep , peer_id const& peer_id, peer_request const& r , bool _have, bool _peer_interested, bool _withheld) : peer_alert(alloc, h, ep, peer_id) , request(r) , we_have(_have) , peer_interested(_peer_interested) , withheld(_withheld) {} std::string invalid_request_alert::message() const { char ret[200]; snprintf(ret, sizeof(ret), "%s peer sent an invalid piece request " "(piece: %u start: %u len: %u)%s" , peer_alert::message().c_str(), request.piece, request.start , request.length , withheld ? ": super seeding withheld piece" : !we_have ? ": we don't have piece" : !peer_interested ? ": peer is not interested" : ""); return ret; } torrent_finished_alert::torrent_finished_alert(aux::stack_allocator& alloc , torrent_handle h) : torrent_alert(alloc, h) {} std::string torrent_finished_alert::message() const { return torrent_alert::message() + " torrent finished downloading"; } piece_finished_alert::piece_finished_alert(aux::stack_allocator& alloc , torrent_handle const& h, int piece_num) : torrent_alert(alloc, h) , piece_index(piece_num) { TORRENT_ASSERT(piece_index >= 0); } std::string piece_finished_alert::message() const { char ret[200]; snprintf(ret, sizeof(ret), "%s piece: %u finished downloading" , torrent_alert::message().c_str(), piece_index); return ret; } request_dropped_alert::request_dropped_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id, int block_num , int piece_num) : peer_alert(alloc, h, ep, peer_id) , block_index(block_num) , piece_index(piece_num) { TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); } std::string request_dropped_alert::message() const { char ret[200]; snprintf(ret, sizeof(ret), "%s peer dropped block ( piece: %u block: %u)" , torrent_alert::message().c_str(), piece_index, block_index); return ret; } block_timeout_alert::block_timeout_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id, int block_num , int piece_num) : peer_alert(alloc, h, ep, peer_id) , block_index(block_num) , piece_index(piece_num) { TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); } std::string block_timeout_alert::message() const { char ret[200]; snprintf(ret, sizeof(ret), "%s peer timed out request ( piece: %u block: %u)" , torrent_alert::message().c_str(), piece_index, block_index); return ret; } block_finished_alert::block_finished_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id, int block_num , int piece_num) : peer_alert(alloc, h, ep, peer_id) , block_index(block_num) , piece_index(piece_num) { TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); } std::string block_finished_alert::message() const { char ret[200]; snprintf(ret, sizeof(ret), "%s block finished downloading (piece: %u block: %u)" , torrent_alert::message().c_str(), piece_index, block_index); return ret; } block_downloading_alert::block_downloading_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep , peer_id const& peer_id, int block_num, int piece_num) : peer_alert(alloc, h, ep, peer_id) #ifndef TORRENT_NO_DEPRECATE , peer_speedmsg("") #endif , block_index(block_num) , piece_index(piece_num) { TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); } std::string block_downloading_alert::message() const { char ret[200]; snprintf(ret, sizeof(ret), "%s requested block (piece: %u block: %u)" , torrent_alert::message().c_str(), piece_index, block_index); return ret; } unwanted_block_alert::unwanted_block_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep , peer_id const& peer_id, int block_num, int piece_num) : peer_alert(alloc, h, ep, peer_id) , block_index(block_num) , piece_index(piece_num) { TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); } std::string unwanted_block_alert::message() const { char ret[200]; snprintf(ret, sizeof(ret), "%s received block not in download queue (piece: %u block: %u)" , torrent_alert::message().c_str(), piece_index, block_index); return ret; } storage_moved_alert::storage_moved_alert(aux::stack_allocator& alloc , torrent_handle const& h, std::string const& p) : torrent_alert(alloc, h) #ifndef TORRENT_NO_DEPRECATE , path(p) #endif , m_path_idx(alloc.copy_string(p)) {} std::string storage_moved_alert::message() const { return torrent_alert::message() + " moved storage to: " + storage_path(); } char const* storage_moved_alert::storage_path() const { return m_alloc.ptr(m_path_idx); } storage_moved_failed_alert::storage_moved_failed_alert( aux::stack_allocator& alloc , torrent_handle const& h , error_code const& e , std::string const& f , char const* op) : torrent_alert(alloc, h) , error(e) #ifndef TORRENT_NO_DEPRECATE , file(f) #endif , operation(op) , m_file_idx(alloc.copy_string(f)) {} char const* storage_moved_failed_alert::file_path() const { return m_alloc.ptr(m_file_idx); } std::string storage_moved_failed_alert::message() const { return torrent_alert::message() + " storage move failed. " + (operation?operation:"") + " (" + file_path() + "): " + convert_from_native(error.message()); } torrent_deleted_alert::torrent_deleted_alert(aux::stack_allocator& alloc , torrent_handle const& h, sha1_hash const& ih) : torrent_alert(alloc, h) , info_hash(ih) {} std::string torrent_deleted_alert::message() const { return torrent_alert::message() + " deleted"; } torrent_delete_failed_alert::torrent_delete_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h, error_code const& e, sha1_hash const& ih) : torrent_alert(alloc, h) , error(e) , info_hash(ih) { #ifndef TORRENT_NO_DEPRECATE msg = convert_from_native(error.message()); #endif } std::string torrent_delete_failed_alert::message() const { return torrent_alert::message() + " torrent deletion failed: " +convert_from_native(error.message()); } save_resume_data_alert::save_resume_data_alert(aux::stack_allocator& alloc , boost::shared_ptr const& rd , torrent_handle const& h) : torrent_alert(alloc, h) , resume_data(rd) {} std::string save_resume_data_alert::message() const { return torrent_alert::message() + " resume data generated"; } save_resume_data_failed_alert::save_resume_data_failed_alert(aux::stack_allocator& alloc , torrent_handle const& h, error_code const& e) : torrent_alert(alloc, h) , error(e) { #ifndef TORRENT_NO_DEPRECATE msg = convert_from_native(error.message()); #endif } std::string save_resume_data_failed_alert::message() const { return torrent_alert::message() + " resume data was not generated: " + convert_from_native(error.message()); } torrent_paused_alert::torrent_paused_alert(aux::stack_allocator& alloc , torrent_handle const& h) : torrent_alert(alloc, h) {} std::string torrent_paused_alert::message() const { return torrent_alert::message() + " paused"; } torrent_resumed_alert::torrent_resumed_alert(aux::stack_allocator& alloc , torrent_handle const& h) : torrent_alert(alloc, h) {} std::string torrent_resumed_alert::message() const { return torrent_alert::message() + " resumed"; } torrent_checked_alert::torrent_checked_alert(aux::stack_allocator& alloc , torrent_handle const& h) : torrent_alert(alloc, h) {} std::string torrent_checked_alert::message() const { return torrent_alert::message() + " checked"; } namespace { static char const* const sock_type_str[] = { "TCP", "TCP/SSL", "UDP", "I2P", "Socks5", "uTP/SSL" }; static char const* const nat_type_str[] = {"NAT-PMP", "UPnP"}; static char const* const protocol_str[] = {"TCP", "UDP"}; static char const* const socket_type_str[] = { "null", "TCP", "Socks5/TCP", "HTTP", "uTP", "i2p", "SSL/TCP", "SSL/Socks5", "HTTPS", "SSL/uTP" }; tcp::endpoint parse_interface(std::string const& iface, int port) { // ignore errors error_code ec; return tcp::endpoint(address::from_string(iface, ec), port); } } listen_failed_alert::listen_failed_alert( aux::stack_allocator& alloc , std::string const& iface , int prt , int op , error_code const& ec , socket_type_t t) : error(ec) , operation(op) , sock_type(t) , endpoint(parse_interface(iface, prt)) , m_alloc(alloc) , m_interface_idx(alloc.copy_string(iface)) {} char const* listen_failed_alert::listen_interface() const { return m_alloc.ptr(m_interface_idx); } std::string listen_failed_alert::message() const { static char const* op_str[] = { "parse_addr", "open", "bind", "listen", "get_peer_name", "accept" }; char ret[300]; snprintf(ret, sizeof(ret), "listening on %s : %s failed: [%s] [%s] %s" , listen_interface() , print_endpoint(endpoint).c_str() , op_str[operation] , sock_type_str[sock_type] , convert_from_native(error.message()).c_str()); return ret; } metadata_failed_alert::metadata_failed_alert(aux::stack_allocator& alloc , const torrent_handle& h, error_code const& e) : torrent_alert(alloc, h) , error(e) {} std::string metadata_failed_alert::message() const { return torrent_alert::message() + " invalid metadata received"; } metadata_received_alert::metadata_received_alert(aux::stack_allocator& alloc , const torrent_handle& h) : torrent_alert(alloc, h) {} std::string metadata_received_alert::message() const { return torrent_alert::message() + " metadata successfully received"; } udp_error_alert::udp_error_alert( aux::stack_allocator& , udp::endpoint const& ep , error_code const& ec) : endpoint(ep) , error(ec) {} std::string udp_error_alert::message() const { error_code ec; return "UDP error: " + convert_from_native(error.message()) + " from: " + endpoint.address().to_string(ec); } external_ip_alert::external_ip_alert(aux::stack_allocator& , address const& ip) : external_address(ip) {} std::string external_ip_alert::message() const { error_code ec; return "external IP received: " + external_address.to_string(ec); } listen_succeeded_alert::listen_succeeded_alert(aux::stack_allocator& , tcp::endpoint const& ep, socket_type_t t) : endpoint(ep) , sock_type(t) {} std::string listen_succeeded_alert::message() const { char const* type_str[] = { "TCP", "SSL/TCP", "UDP", "i2p", "socks5", "SSL/uTP" }; char ret[200]; snprintf(ret, sizeof(ret), "successfully listening on [%s] %s" , type_str[sock_type], print_endpoint(endpoint).c_str()); return ret; } portmap_error_alert::portmap_error_alert(aux::stack_allocator& , int i, int t, error_code const& e) : mapping(i), map_type(t), error(e) { #ifndef TORRENT_NO_DEPRECATE msg = convert_from_native(error.message()); #endif } std::string portmap_error_alert::message() const { return std::string("could not map port using ") + nat_type_str[map_type] + ": " + convert_from_native(error.message()); } portmap_alert::portmap_alert(aux::stack_allocator&, int i, int port, int t , int proto) : mapping(i), external_port(port), map_type(t), protocol(proto) {} std::string portmap_alert::message() const { char ret[200]; snprintf(ret, sizeof(ret), "successfully mapped port using %s. external port: %s/%u" , nat_type_str[map_type], protocol_str[protocol], external_port); return ret; } #ifndef TORRENT_DISABLE_LOGGING portmap_log_alert::portmap_log_alert(aux::stack_allocator& alloc, int t, const char* m) : map_type(t) #ifndef TORRENT_NO_DEPRECATE , msg(m) #endif , m_alloc(alloc) , m_log_idx(alloc.copy_string(m)) {} char const* portmap_log_alert::log_message() const { return m_alloc.ptr(m_log_idx); } std::string portmap_log_alert::message() const { char ret[600]; snprintf(ret, sizeof(ret), "%s: %s", nat_type_str[map_type] , log_message()); return ret; } #endif fastresume_rejected_alert::fastresume_rejected_alert( aux::stack_allocator& alloc , torrent_handle const& h , error_code const& ec , std::string const& f , char const* op) : torrent_alert(alloc, h) , error(ec) #ifndef TORRENT_NO_DEPRECATE , file(f) #endif , operation(op) , m_path_idx(alloc.copy_string(f)) { #ifndef TORRENT_NO_DEPRECATE msg = convert_from_native(error.message()); #endif } std::string fastresume_rejected_alert::message() const { return torrent_alert::message() + " fast resume rejected. " + (operation?operation:"") + "(" + file_path() + "): " + convert_from_native(error.message()); } char const* fastresume_rejected_alert::file_path() const { return m_alloc.ptr(m_path_idx); } peer_blocked_alert::peer_blocked_alert(aux::stack_allocator& alloc , torrent_handle const& h, address const& i , int r) : torrent_alert(alloc, h) , ip(i) , reason(r) {} std::string peer_blocked_alert::message() const { error_code ec; char ret[600]; char const* reason_str[] = { "ip_filter", "port_filter", "i2p_mixed", "privileged_ports", "utp_disabled", "tcp_disabled", "invalid_local_interface" }; snprintf(ret, sizeof(ret), "%s: blocked peer: %s [%s]" , torrent_alert::message().c_str(), ip.to_string(ec).c_str() , reason_str[reason]); return ret; } dht_announce_alert::dht_announce_alert(aux::stack_allocator& , address const& i, int p , sha1_hash const& ih) : ip(i) , port(p) , info_hash(ih) {} std::string dht_announce_alert::message() const { error_code ec; char ih_hex[41]; to_hex(info_hash.data(), 20, ih_hex); char msg[200]; snprintf(msg, sizeof(msg), "incoming dht announce: %s:%u (%s)" , ip.to_string(ec).c_str(), port, ih_hex); return msg; } dht_get_peers_alert::dht_get_peers_alert(aux::stack_allocator& , sha1_hash const& ih) : info_hash(ih) {} std::string dht_get_peers_alert::message() const { char ih_hex[41]; to_hex(info_hash.data(), 20, ih_hex); char msg[200]; snprintf(msg, sizeof(msg), "incoming dht get_peers: %s", ih_hex); return msg; } stats_alert::stats_alert(aux::stack_allocator& alloc , torrent_handle const& h, int in, stat const& s) : torrent_alert(alloc, h) , interval(in) { transferred[upload_payload] = s[stat::upload_payload].counter(); transferred[upload_protocol] = s[stat::upload_protocol].counter(); transferred[download_payload] = s[stat::download_payload].counter(); transferred[download_protocol] = s[stat::download_protocol].counter(); transferred[upload_ip_protocol] = s[stat::upload_ip_protocol].counter(); transferred[download_ip_protocol] = s[stat::download_ip_protocol].counter(); #ifndef TORRENT_NO_DEPRECATE transferred[upload_dht_protocol] = 0; transferred[upload_tracker_protocol] = 0; transferred[download_dht_protocol] = 0; transferred[download_tracker_protocol] = 0; #else transferred[deprecated1] = 0; transferred[deprecated2] = 0; transferred[deprecated3] = 0; transferred[deprecated4] = 0; #endif } std::string stats_alert::message() const { char msg[200]; snprintf(msg, sizeof(msg), "%s: [%d] %d %d %d %d %d %d" #ifndef TORRENT_NO_DEPRECATE " %d %d %d %d" #endif , torrent_alert::message().c_str() , interval , transferred[0] , transferred[1] , transferred[2] , transferred[3] , transferred[4] , transferred[5] #ifndef TORRENT_NO_DEPRECATE , transferred[6] , transferred[7] , transferred[8] , transferred[9] #endif ); return msg; } cache_flushed_alert::cache_flushed_alert(aux::stack_allocator& alloc , torrent_handle const& h) : torrent_alert(alloc, h) {} anonymous_mode_alert::anonymous_mode_alert(aux::stack_allocator& alloc , torrent_handle const& h, int k, std::string const& s) : torrent_alert(alloc, h) , kind(k) , str(s) {} std::string anonymous_mode_alert::message() const { char msg[200]; static char const* msgs[] = { "tracker is not anonymous, set a proxy" }; snprintf(msg, sizeof(msg), "%s: %s: %s" , torrent_alert::message().c_str() , msgs[kind], str.c_str()); return msg; } lsd_peer_alert::lsd_peer_alert(aux::stack_allocator& alloc, torrent_handle const& h , tcp::endpoint const& i) : peer_alert(alloc, h, i, peer_id(0)) {} std::string lsd_peer_alert::message() const { char msg[200]; snprintf(msg, sizeof(msg), "%s: received peer from local service discovery" , peer_alert::message().c_str()); return msg; } trackerid_alert::trackerid_alert( aux::stack_allocator& alloc , torrent_handle const& h , std::string const& u , const std::string& id) : tracker_alert(alloc, h, u) #ifndef TORRENT_NO_DEPRECATE , trackerid(id) #endif , m_tracker_idx(alloc.copy_string(id)) {} char const* trackerid_alert::tracker_id() const { return m_alloc.ptr(m_tracker_idx); } std::string trackerid_alert::message() const { return std::string("trackerid received: ") + tracker_id(); } dht_bootstrap_alert::dht_bootstrap_alert(aux::stack_allocator&) {} std::string dht_bootstrap_alert::message() const { return "DHT bootstrap complete"; } #ifndef TORRENT_NO_DEPRECATE rss_alert::rss_alert(aux::stack_allocator&, feed_handle h , std::string const& u, int s, error_code const& ec) : handle(h), url(u), state(s), error(ec) {} std::string rss_alert::message() const { char msg[600]; char const* state_msg[] = {"updating", "updated", "error"}; snprintf(msg, sizeof(msg), "RSS feed %s: %s (%s)" , url.c_str(), state_msg[state], convert_from_native(error.message()).c_str()); return msg; } #endif torrent_error_alert::torrent_error_alert( aux::stack_allocator& alloc , torrent_handle const& h , error_code const& e, std::string const& f) : torrent_alert(alloc, h) , error(e) #ifndef TORRENT_NO_DEPRECATE , error_file(f) #endif , m_file_idx(alloc.copy_string(f)) {} std::string torrent_error_alert::message() const { char msg[200]; snprintf(msg, sizeof(msg), " ERROR: %s", convert_from_native(error.message()).c_str()); return torrent_alert::message() + msg; } char const* torrent_error_alert::filename() const { return m_alloc.ptr(m_file_idx); } #ifndef TORRENT_NO_DEPRECATE torrent_added_alert::torrent_added_alert(aux::stack_allocator& alloc , torrent_handle const& h) : torrent_alert(alloc, h) {} std::string torrent_added_alert::message() const { return torrent_alert::message() + " added"; } #endif torrent_removed_alert::torrent_removed_alert(aux::stack_allocator& alloc , torrent_handle const& h, sha1_hash const& ih) : torrent_alert(alloc, h) , info_hash(ih) {} std::string torrent_removed_alert::message() const { return torrent_alert::message() + " removed"; } torrent_need_cert_alert::torrent_need_cert_alert(aux::stack_allocator& alloc , torrent_handle const& h) : torrent_alert(alloc, h) {} std::string torrent_need_cert_alert::message() const { return torrent_alert::message() + " needs SSL certificate"; } incoming_connection_alert::incoming_connection_alert(aux::stack_allocator&, int t , tcp::endpoint const& i) : socket_type(t) , ip(i) {} std::string incoming_connection_alert::message() const { char msg[600]; error_code ec; snprintf(msg, sizeof(msg), "incoming connection from %s (%s)" , print_endpoint(ip).c_str(), socket_type_str[socket_type]); return msg; } peer_connect_alert::peer_connect_alert(aux::stack_allocator& alloc, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id, int type) : peer_alert(alloc, h, ep, peer_id) , socket_type(type) {} std::string peer_connect_alert::message() const { char msg[600]; error_code ec; snprintf(msg, sizeof(msg), "%s connecting to peer (%s)" , peer_alert::message().c_str(), socket_type_str[socket_type]); return msg; } add_torrent_alert::add_torrent_alert(aux::stack_allocator& alloc, torrent_handle h , add_torrent_params const& p, error_code ec) : torrent_alert(alloc, h) , params(p) , error(ec) {} std::string add_torrent_alert::message() const { char msg[600]; char info_hash[41]; char const* torrent_name = info_hash; if (params.ti) torrent_name = params.ti->name().c_str(); else if (!params.name.empty()) torrent_name = params.name.c_str(); else if (!params.url.empty()) torrent_name = params.url.c_str(); else to_hex(params.info_hash.data(), 20, info_hash); if (error) { snprintf(msg, sizeof(msg), "failed to add torrent \"%s\": [%s] %s" , torrent_name, error.category().name() , convert_from_native(error.message()).c_str()); } else { snprintf(msg, sizeof(msg), "added torrent: %s", torrent_name); } return msg; } state_update_alert::state_update_alert(aux::stack_allocator& , std::vector st) : status(st) {} std::string state_update_alert::message() const { char msg[600]; snprintf(msg, sizeof(msg), "state updates for %d torrents", int(status.size())); return msg; } #ifndef TORRENT_NO_DEPRECATE mmap_cache_alert::mmap_cache_alert(aux::stack_allocator& , error_code const& ec): error(ec) {} std::string mmap_cache_alert::message() const { char msg[600]; snprintf(msg, sizeof(msg), "mmap cache failed: (%d) %s", error.value() , convert_from_native(error.message()).c_str()); return msg; } #endif peer_error_alert::peer_error_alert(aux::stack_allocator& alloc, torrent_handle const& h , tcp::endpoint const& ep, peer_id const& peer_id, int op , error_code const& e) : peer_alert(alloc, h, ep, peer_id) , operation(op) , error(e) { #ifndef TORRENT_NO_DEPRECATE msg = convert_from_native(error.message()); #endif } std::string peer_error_alert::message() const { char buf[200]; snprintf(buf, sizeof(buf), "%s peer error [%s] [%s]: %s" , peer_alert::message().c_str() , operation_name(operation), error.category().name() , convert_from_native(error.message()).c_str()); return buf; } char const* operation_name(int op) { static char const* names[] = { "bittorrent", "iocontrol", "getpeername", "getname", "alloc_recvbuf", "alloc_sndbuf", "file_write", "file_read", "file", "sock_write", "sock_read", "sock_open", "sock_bind", "available", "encryption", "connect", "ssl_handshake", "get_interface", }; if (op < 0 || op >= int(sizeof(names)/sizeof(names[0]))) return "unknown operation"; return names[op]; } torrent_update_alert::torrent_update_alert(aux::stack_allocator& alloc, torrent_handle h , sha1_hash const& old_hash, sha1_hash const& new_hash) : torrent_alert(alloc, h) , old_ih(old_hash) , new_ih(new_hash) {} std::string torrent_update_alert::message() const { char msg[200]; snprintf(msg, sizeof(msg), " torrent changed info-hash from: %s to %s" , to_hex(old_ih.to_string()).c_str() , to_hex(new_ih.to_string()).c_str()); return torrent_alert::message() + msg; } #ifndef TORRENT_NO_DEPRECATE rss_item_alert::rss_item_alert(aux::stack_allocator&, feed_handle h , feed_item const& i) : handle(h) , item(i) {} std::string rss_item_alert::message() const { char msg[500]; snprintf(msg, sizeof(msg), "feed [%s] has new RSS item %s" , handle.get_feed_status().title.c_str() , item.title.empty() ? item.url.c_str() : item.title.c_str()); return msg; } #endif peer_disconnected_alert::peer_disconnected_alert(aux::stack_allocator& alloc , torrent_handle const& h, tcp::endpoint const& ep , peer_id const& peer_id, operation_t op, int type, error_code const& e , close_reason_t r) : peer_alert(alloc, h, ep, peer_id) , socket_type(type) , operation(op) , error(e) , reason(r) { #ifndef TORRENT_NO_DEPRECATE msg = convert_from_native(error.message()); #endif } std::string peer_disconnected_alert::message() const { char buf[600]; snprintf(buf, sizeof(buf), "%s disconnecting (%s) [%s] [%s]: %s (reason: %d)" , peer_alert::message().c_str() , socket_type_str[socket_type] , operation_name(operation), error.category().name() , convert_from_native(error.message()).c_str() , int(reason)); return buf; } dht_error_alert::dht_error_alert(aux::stack_allocator&, int op , error_code const& ec) : error(ec), operation(op_t(op)) {} std::string dht_error_alert::message() const { static const char* const operation_names[] = { "unknown", "hostname lookup" }; int op = operation; if (op < 0 || op >= int(sizeof(operation_names)/sizeof(operation_names[0]))) op = 0; char msg[600]; snprintf(msg, sizeof(msg), "DHT error [%s] (%d) %s" , operation_names[op] , error.value() , convert_from_native(error.message()).c_str()); return msg; } dht_immutable_item_alert::dht_immutable_item_alert(aux::stack_allocator& , sha1_hash const& t, entry const& i) : target(t), item(i) {} std::string dht_immutable_item_alert::message() const { char msg[1050]; snprintf(msg, sizeof(msg), "DHT immutable item %s [ %s ]" , to_hex(target.to_string()).c_str() , item.to_string().c_str()); return msg; } // TODO: 2 the salt here is allocated on the heap. It would be nice to // allocate in in the stack_allocator dht_mutable_item_alert::dht_mutable_item_alert(aux::stack_allocator& , boost::array k , boost::array sig , boost::uint64_t sequence , std::string const& s , entry const& i , bool a) : key(k), signature(sig), seq(sequence), salt(s), item(i), authoritative(a) {} std::string dht_mutable_item_alert::message() const { char msg[1050]; snprintf(msg, sizeof(msg), "DHT mutable item (key=%s salt=%s seq=%" PRId64 " %s) [ %s ]" , to_hex(std::string(&key[0], 32)).c_str() , salt.c_str() , seq , authoritative ? "auth" : "non-auth" , item.to_string().c_str()); return msg; } dht_put_alert::dht_put_alert(aux::stack_allocator&, sha1_hash const& t, int n) : target(t) , seq(0) , num_success(n) {} dht_put_alert::dht_put_alert(aux::stack_allocator& , boost::array key , boost::array sig , std::string s , boost::uint64_t sequence_number , int n) : target(0) , public_key(key) , signature(sig) , salt(s) , seq(sequence_number) , num_success(n) {} std::string dht_put_alert::message() const { char msg[1050]; if (target.is_all_zeros()) { snprintf(msg, sizeof(msg), "DHT put complete (success=%d key=%s sig=%s salt=%s seq=%" PRId64 ")" , num_success , to_hex(std::string(&public_key[0], 32)).c_str() , to_hex(std::string(&signature[0], 64)).c_str() , salt.c_str() , seq); return msg; } snprintf(msg, sizeof(msg), "DHT put commplete (success=%d hash=%s)" , num_success , to_hex(target.to_string()).c_str()); return msg; } i2p_alert::i2p_alert(aux::stack_allocator&, error_code const& ec) : error(ec) {} std::string i2p_alert::message() const { char msg[600]; snprintf(msg, sizeof(msg), "i2p_error: [%s] %s" , error.category().name(), convert_from_native(error.message()).c_str()); return msg; } dht_outgoing_get_peers_alert::dht_outgoing_get_peers_alert(aux::stack_allocator& , sha1_hash const& ih, sha1_hash const& obfih , udp::endpoint ep) : info_hash(ih) , obfuscated_info_hash(obfih) , ip(ep) {} std::string dht_outgoing_get_peers_alert::message() const { char msg[600]; char obf[70]; obf[0] = '\0'; if (obfuscated_info_hash != info_hash) { snprintf(obf, sizeof(obf), " [obfuscated: %s]" , to_hex(obfuscated_info_hash.to_string()).c_str()); } snprintf(msg, sizeof(msg), "outgoing dht get_peers : %s%s -> %s" , to_hex(info_hash.to_string()).c_str() , obf , print_endpoint(ip).c_str()); return msg; } #ifndef TORRENT_DISABLE_LOGGING log_alert::log_alert(aux::stack_allocator& alloc, char const* log) : m_alloc(alloc) , m_str_idx(alloc.copy_string(log)) {} char const* log_alert::msg() const { return m_alloc.ptr(m_str_idx); } std::string log_alert::message() const { return msg(); } torrent_log_alert::torrent_log_alert(aux::stack_allocator& alloc, torrent_handle const& h , char const* log) : torrent_alert(alloc, h) , m_str_idx(alloc.copy_string(log)) {} char const* torrent_log_alert::msg() const { return m_alloc.ptr(m_str_idx); } std::string torrent_log_alert::message() const { return torrent_alert::message() + ": " + msg(); } peer_log_alert::peer_log_alert(aux::stack_allocator& alloc , torrent_handle const& h , tcp::endpoint const& i , peer_id const& pi , direction_t dir , char const* event , char const* log) : peer_alert(alloc, h, i, pi) , event_type(event) , direction(dir) , m_str_idx(alloc.copy_string(log)) {} char const* peer_log_alert::msg() const { return m_alloc.ptr(m_str_idx); } std::string peer_log_alert::message() const { static char const* mode[] = { "<==", "==>", "<<<", ">>>", "***" }; return torrent_alert::message() + " [" + print_endpoint(ip) + "] " + mode[direction] + " " + event_type + " [ " + msg() + " ]"; } #endif lsd_error_alert::lsd_error_alert(aux::stack_allocator&, error_code const& ec) : alert() , error(ec) {} std::string lsd_error_alert::message() const { return "Local Service Discovery error: " + error.message(); } session_stats_alert::session_stats_alert(aux::stack_allocator&, counters const& cnt) { for (int i = 0; i < counters::num_counters; ++i) values[i] = cnt[i]; } std::string session_stats_alert::message() const { // this specific output is parsed by tools/parse_session_stats.py // if this is changed, that parser should also be changed char msg[100]; snprintf(msg, sizeof(msg), "session stats (%d values): " , int(sizeof(values)/sizeof(values[0]))); std::string ret = msg; bool first = true; for (int i = 0; i < sizeof(values)/sizeof(values[0]); ++i) { snprintf(msg, sizeof(msg), first ? "%" PRIu64 : ", %" PRIu64, values[i]); first = false; ret += msg; } return ret; } dht_stats_alert::dht_stats_alert(aux::stack_allocator& , std::vector const& table , std::vector const& requests) : alert() , active_requests(requests) , routing_table(table) {} std::string dht_stats_alert::message() const { char buf[2048]; snprintf(buf, sizeof(buf), "DHT stats: reqs: %d buckets: %d" , int(active_requests.size()) , int(routing_table.size())); return buf; } url_seed_alert::url_seed_alert(aux::stack_allocator& alloc, torrent_handle const& h , std::string const& u, error_code const& e) : torrent_alert(alloc, h) #ifndef TORRENT_NO_DEPRECATE , url(u) , msg(convert_from_native(e.message())) #endif , error(e) , m_url_idx(alloc.copy_string(u)) , m_msg_idx(-1) {} url_seed_alert::url_seed_alert(aux::stack_allocator& alloc, torrent_handle const& h , std::string const& u, std::string const& m) : torrent_alert(alloc, h) #ifndef TORRENT_NO_DEPRECATE , url(u) , msg(m) #endif , m_url_idx(alloc.copy_string(u)) , m_msg_idx(alloc.copy_string(m)) {} std::string url_seed_alert::message() const { return torrent_alert::message() + " url seed (" + server_url() + ") failed: " + convert_from_native(error.message()); } char const* url_seed_alert::server_url() const { return m_alloc.ptr(m_url_idx); } char const* url_seed_alert::error_message() const { if (m_msg_idx == -1) return ""; return m_alloc.ptr(m_msg_idx); } file_error_alert::file_error_alert(aux::stack_allocator& alloc , error_code const& ec , std::string const& f , char const* op , torrent_handle const& h) : torrent_alert(alloc, h) #ifndef TORRENT_NO_DEPRECATE , file(f) #endif , error(ec) , operation(op) , m_file_idx(alloc.copy_string(f)) { #ifndef TORRENT_NO_DEPRECATE msg = convert_from_native(error.message()); #endif } char const* file_error_alert::filename() const { return m_alloc.ptr(m_file_idx); } std::string file_error_alert::message() const { return torrent_alert::message() + " " + (operation?operation:"") + " (" + filename() + ") error: " + convert_from_native(error.message()); } incoming_request_alert::incoming_request_alert(aux::stack_allocator& alloc , peer_request r, torrent_handle h , tcp::endpoint const& ep, peer_id const& peer_id) : peer_alert(alloc, h, ep, peer_id) , req(r) {} std::string incoming_request_alert::message() const { char msg[1024]; snprintf(msg, sizeof(msg), "%s: incoming request [ piece: %d start: %d length: %d ]" , peer_alert::message().c_str(), req.piece, req.start, req.length); return msg; } dht_log_alert::dht_log_alert(aux::stack_allocator& alloc , dht_log_alert::dht_module_t m, const char* msg) : module(m) , m_alloc(alloc) , m_msg_idx(alloc.copy_string(msg)) {} char const* dht_log_alert::log_message() const { return m_alloc.ptr(m_msg_idx); } std::string dht_log_alert::message() const { static char const* const dht_modules[] = { "tracker", "node", "routing_table", "rpc_manager", "traversal" }; char ret[900]; snprintf(ret, sizeof(ret), "DHT %s: %s", dht_modules[module] , log_message()); return ret; } dht_pkt_alert::dht_pkt_alert(aux::stack_allocator& alloc , char const* buf, int size, dht_pkt_alert::direction_t d, udp::endpoint ep) : dir(d) , node(ep) , m_alloc(alloc) , m_msg_idx(alloc.copy_buffer(buf, size)) , m_size(size) {} char const* dht_pkt_alert::pkt_buf() const { return m_alloc.ptr(m_msg_idx); } int dht_pkt_alert::pkt_size() const { return m_size; } std::string dht_pkt_alert::message() const { bdecode_node print; error_code ec; // ignore errors here. This is best-effort. It may be a broken encoding // but at least we'll print the valid parts bdecode(pkt_buf(), pkt_buf() + pkt_size(), print, ec, NULL, 100, 100); std::string msg = print_entry(print, true); char const* prefix[2] = { "<==", "==>"}; char buf[1024]; snprintf(buf, sizeof(buf), "%s [%s] %s", prefix[dir] , print_endpoint(node).c_str(), msg.c_str()); return buf; } dht_get_peers_reply_alert::dht_get_peers_reply_alert(aux::stack_allocator& alloc , sha1_hash const& ih , std::vector const& peers) : info_hash(ih) , m_alloc(alloc) , m_num_peers(peers.size()) { std::size_t total_size = m_num_peers; // num bytes for sizes for (int i = 0; i < m_num_peers; i++) { total_size += peers[i].size(); } m_peers_idx = alloc.allocate(total_size); char *ptr = alloc.ptr(m_peers_idx); for (int i = 0; i < m_num_peers; i++) { tcp::endpoint endp = peers[i]; std::size_t size = endp.size(); detail::write_uint8(size, ptr); memcpy(ptr, endp.data(), size); ptr += size; } } std::string dht_get_peers_reply_alert::message() const { char ih_hex[41]; to_hex(info_hash.data(), 20, ih_hex); char msg[200]; snprintf(msg, sizeof(msg), "incoming dht get_peers reply: %s, peers %d", ih_hex, m_num_peers); return msg; } int dht_get_peers_reply_alert::num_peers() const { return m_num_peers; } #ifndef TORRENT_NO_DEPRECATE void dht_get_peers_reply_alert::peers(std::vector &v) const { std::vector p(peers()); v.reserve(p.size()); std::copy(p.begin(), p.end(), std::back_inserter(v)); } #endif std::vector dht_get_peers_reply_alert::peers() const { std::vector peers(m_num_peers); const char *ptr = m_alloc.ptr(m_peers_idx); for (int i = 0; i < m_num_peers; i++) { std::size_t size = detail::read_uint8(ptr); memcpy(peers[i].data(), ptr, size); ptr += size; } return peers; } dht_direct_response_alert::dht_direct_response_alert( aux::stack_allocator& alloc, void* userdata_ , udp::endpoint const& addr_, bdecode_node const& response) : userdata(userdata_), addr(addr_), m_alloc(alloc) , m_response_idx(alloc.copy_buffer(response.data_section().first, response.data_section().second)) , m_response_size(response.data_section().second) {} dht_direct_response_alert::dht_direct_response_alert( aux::stack_allocator& alloc , void* userdata_ , udp::endpoint const& addr_) : userdata(userdata_), addr(addr_), m_alloc(alloc) , m_response_idx(-1), m_response_size(0) {} std::string dht_direct_response_alert::message() const { char msg[1050]; snprintf(msg, sizeof(msg), "DHT direct response (address=%s) [ %s ]" , addr.address().to_string().c_str() , m_response_size ? std::string(m_alloc.ptr(m_response_idx), m_response_size).c_str() : ""); return msg; } bdecode_node dht_direct_response_alert::response() const { if (m_response_size == 0) return bdecode_node(); char const* start = m_alloc.ptr(m_response_idx); char const* end = start + m_response_size; error_code ec; bdecode_node ret; bdecode(start, end, ret, ec); TORRENT_ASSERT(!ec); return ret; } #ifndef TORRENT_DISABLE_LOGGING picker_log_alert::picker_log_alert(aux::stack_allocator& alloc, torrent_handle const& h , tcp::endpoint const& ep, peer_id const& peer_id, boost::uint32_t flags , piece_block const* blocks, int num_blocks) : peer_alert(alloc, h, ep, peer_id) , picker_flags(flags) , m_array_idx(alloc.copy_buffer(reinterpret_cast(blocks) , num_blocks * sizeof(piece_block))) , m_num_blocks(num_blocks) {} std::vector picker_log_alert::blocks() const { // we need to copy this array to make sure the structures are properly // aigned, not just to have a nice API std::vector ret; ret.resize(m_num_blocks); char const* start = m_alloc.ptr(m_array_idx); memcpy(&ret[0], start, m_num_blocks * sizeof(piece_block)); return ret; } std::string picker_log_alert::message() const { static char const* const flag_names[] = { "partial_ratio ", "prioritize_partials ", "rarest_first_partials ", "rarest_first ", "reverse_rarest_first ", "suggested_pieces ", "prio_sequential_pieces ", "sequential_pieces ", "reverse_pieces ", "time_critical ", "random_pieces ", "prefer_contiguous ", "reverse_sequential ", "backup1 ", "backup2 ", "end_game " }; std::string ret = peer_alert::message(); boost::uint32_t flags = picker_flags; int idx = 0; ret += " picker_log [ "; for (; flags != 0; flags >>= 1, ++idx) { if ((flags & 1) == 0) continue; ret += flag_names[idx]; } ret += "] "; std::vector b = blocks(); for (int i = 0; i < int(b.size()); ++i) { char buf[50]; snprintf(buf, sizeof(buf), "(%d,%d) " , b[i].piece_index, b[i].block_index); ret += buf; } return ret; } #endif // TORRENT_DISABLE_LOGGING } // namespace libtorrent libtorrent-rasterbar-1.1.13/src/alert_manager.cpp000066400000000000000000000124541351156116000220450ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/alert_manager.hpp" #include "libtorrent/alert_types.hpp" #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/extensions.hpp" #endif namespace libtorrent { alert_manager::alert_manager(int queue_limit, boost::uint32_t alert_mask) : m_alert_mask(alert_mask) , m_queue_size_limit(queue_limit) , m_num_queued_resume(0) , m_generation(0) {} alert_manager::~alert_manager() {} int alert_manager::num_queued_resume() const { recursive_mutex::scoped_lock lock(m_mutex); return m_num_queued_resume; } alert* alert_manager::wait_for_alert(time_duration max_wait) { recursive_mutex::scoped_lock lock(m_mutex); if (!m_alerts[m_generation].empty()) return m_alerts[m_generation].front(); // this call can be interrupted prematurely by other signals m_condition.wait_for(lock, max_wait); if (!m_alerts[m_generation].empty()) return m_alerts[m_generation].front(); return NULL; } void alert_manager::maybe_notify(alert* a) { if (a->type() == save_resume_data_failed_alert::alert_type || a->type() == save_resume_data_alert::alert_type) ++m_num_queued_resume; if (m_alerts[m_generation].size() == 1) { // we just posted to an empty queue. If anyone is waiting for // alerts, we need to notify them. Also (potentially) call the // user supplied m_notify callback to let the client wake up its // message loop to poll for alerts. if (m_notify) m_notify(); // TODO: 2 keep a count of the number of threads waiting. Only if it's // > 0 notify them m_condition.notify_all(); } #ifndef TORRENT_DISABLE_EXTENSIONS for (ses_extension_list_t::iterator i = m_ses_extensions.begin() , end(m_ses_extensions.end()); i != end; ++i) { (*i)->on_alert(a); } #endif } #ifndef TORRENT_NO_DEPRECATE bool alert_manager::maybe_dispatch(alert const& a) { if (m_dispatch) { m_dispatch(a.clone()); return true; } return false; } #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif void alert_manager::set_dispatch_function( boost::function)> const& fun) { recursive_mutex::scoped_lock lock(m_mutex); m_dispatch = fun; heterogeneous_queue storage; m_alerts[m_generation].swap(storage); lock.unlock(); std::vector alerts; storage.get_pointers(alerts); for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { m_dispatch((*i)->clone()); } } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #endif void alert_manager::set_notify_function(boost::function const& fun) { recursive_mutex::scoped_lock lock(m_mutex); m_notify = fun; if (!m_alerts[m_generation].empty()) { if (m_notify) m_notify(); } } #ifndef TORRENT_DISABLE_EXTENSIONS void alert_manager::add_extension(boost::shared_ptr ext) { m_ses_extensions.push_back(ext); } #endif void alert_manager::get_all(std::vector& alerts, int& num_resume) { alerts.clear(); recursive_mutex::scoped_lock lock(m_mutex); TORRENT_ASSERT(m_num_queued_resume <= m_alerts[m_generation].size()); if (m_alerts[m_generation].empty()) return; m_alerts[m_generation].get_pointers(alerts); num_resume = m_num_queued_resume; m_num_queued_resume = 0; // swap buffers m_generation = (m_generation + 1) & 1; // clear the one we will start writing to now m_alerts[m_generation].clear(); m_allocations[m_generation].reset(); } bool alert_manager::pending() const { recursive_mutex::scoped_lock lock(m_mutex); return !m_alerts[m_generation].empty(); } int alert_manager::set_alert_queue_size_limit(int queue_size_limit_) { recursive_mutex::scoped_lock lock(m_mutex); std::swap(m_queue_size_limit, queue_size_limit_); return queue_size_limit_; } } libtorrent-rasterbar-1.1.13/src/announce_entry.cpp000066400000000000000000000101101351156116000222560ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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/config.hpp" #include "libtorrent/announce_entry.hpp" #include "libtorrent/aux_/time.hpp" #include "libtorrent/aux_/session_settings.hpp" namespace libtorrent { enum { // wait at least 5 seconds before retrying a failed tracker tracker_retry_delay_min = 5 // when tracker_failed_max trackers // has failed, wait 60 minutes instead , tracker_retry_delay_max = 60 * 60 }; announce_entry::announce_entry(std::string const& u) : url(u) , next_announce(min_time()) , min_announce(min_time()) , scrape_incomplete(-1) , scrape_complete(-1) , scrape_downloaded(-1) , tier(0) , fail_limit(0) , fails(0) , updating(false) , source(0) , verified(false) , start_sent(false) , complete_sent(false) , send_stats(true) , triggered_manually(false) {} announce_entry::announce_entry() : next_announce(min_time()) , min_announce(min_time()) , scrape_incomplete(-1) , scrape_complete(-1) , scrape_downloaded(-1) , tier(0) , fail_limit(0) , fails(0) , updating(false) , source(0) , verified(false) , start_sent(false) , complete_sent(false) , send_stats(true) , triggered_manually(false) {} announce_entry::~announce_entry() {} int announce_entry::next_announce_in() const { return total_seconds(next_announce - aux::time_now()); } int announce_entry::min_announce_in() const { return total_seconds(min_announce - aux::time_now()); } void announce_entry::reset() { start_sent = false; next_announce = min_time(); min_announce = min_time(); } void announce_entry::failed(aux::session_settings const& sett, int retry_interval) { ++fails; // the exponential back-off ends up being: // 7, 15, 27, 45, 95, 127, 165, ... seconds // with the default tracker_backoff of 250 int delay = (std::min)(tracker_retry_delay_min + int(fails) * int(fails) * tracker_retry_delay_min * sett.get_int(settings_pack::tracker_backoff) / 100 , int(tracker_retry_delay_max)); delay = (std::max)(delay, retry_interval); next_announce = aux::time_now() + seconds(delay); updating = false; } bool announce_entry::can_announce(time_point now, bool is_seed) const { // if we're a seed and we haven't sent a completed // event, we need to let this announce through bool const need_send_complete = is_seed && !complete_sent; // add some slack here for rounding errors return now + seconds(1) >= next_announce && (now >= min_announce || need_send_complete) && (fails < fail_limit || fail_limit == 0) && !updating; } void announce_entry::trim() { while (!url.empty() && is_space(url[0])) url.erase(url.begin()); } } libtorrent-rasterbar-1.1.13/src/assert.cpp000066400000000000000000000230641351156116000205440ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/config.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #ifdef TORRENT_PRODUCTION_ASSERTS #include #endif #if TORRENT_USE_ASSERTS \ || defined TORRENT_ASIO_DEBUGGING \ || defined TORRENT_PROFILE_CALLS \ || defined TORRENT_DEBUG_BUFFERS #ifdef __APPLE__ #include #endif #include #include #include #include #include // for snprintf #include #include "libtorrent/aux_/disable_warnings_pop.hpp" // uClibc++ doesn't have cxxabi.h #if defined __GNUC__ && __GNUC__ >= 3 \ && !defined __UCLIBCXX_MAJOR__ #include std::string demangle(char const* name) { // in case this string comes // this is needed on linux char const* start = strchr(name, '('); if (start != NULL) { ++start; } else { // this is needed on macos x start = strstr(name, "0x"); if (start != NULL) { start = strchr(start, ' '); if (start != NULL) ++start; else start = name; } else start = name; } char const* end = strchr(start, '+'); if (end) while (*(end-1) == ' ') --end; std::string in; if (end == NULL) in.assign(start); else in.assign(start, end); size_t len; int status; char* unmangled = ::abi::__cxa_demangle(in.c_str(), NULL, &len, &status); if (unmangled == NULL) return in; std::string ret(unmangled); free(unmangled); return ret; } #elif defined _WIN32 #include "windows.h" #include "dbghelp.h" std::string demangle(char const* name) { char demangled_name[256]; if (UnDecorateSymbolName(name, demangled_name, sizeof(demangled_name), UNDNAME_NO_THROW_SIGNATURES) == 0) demangled_name[0] = 0; return demangled_name; } #else std::string demangle(char const* name) { return name; } #endif #include #include #include #include "libtorrent/version.hpp" #if TORRENT_USE_EXECINFO #include TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth, void*) { void* stack[50]; int size = backtrace(stack, 50); char** symbols = backtrace_symbols(stack, size); for (int i = 1; i < size && len > 0; ++i) { int ret = snprintf(out, len, "%d: %s\n", i, demangle(symbols[i]).c_str()); out += ret; len -= ret; if (i - 1 == max_depth && max_depth > 0) break; } free(symbols); } #elif defined _WIN32 #include "windows.h" #include "libtorrent/utf8.hpp" #include "libtorrent/thread.hpp" #include "winbase.h" #include "dbghelp.h" TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth , void* ctx) { // all calls to DbgHlp.dll are thread-unsafe. i.e. they all need to be // synchronized and not called concurrently. This mutex serializes access static libtorrent::mutex dbghlp_mutex; libtorrent::mutex::scoped_lock l(dbghlp_mutex); CONTEXT context_record; if (ctx) { context_record = *static_cast(ctx); } else { // use the current thread's context RtlCaptureContext(&context_record); } int size = 0; boost::array stack; STACKFRAME64 stack_frame; memset(&stack_frame, 0, sizeof(stack_frame)); #if defined(_WIN64) int const machine_type = IMAGE_FILE_MACHINE_AMD64; stack_frame.AddrPC.Offset = context_record.Rip; stack_frame.AddrFrame.Offset = context_record.Rbp; stack_frame.AddrStack.Offset = context_record.Rsp; #else int const machine_type = IMAGE_FILE_MACHINE_I386; stack_frame.AddrPC.Offset = context_record.Eip; stack_frame.AddrFrame.Offset = context_record.Ebp; stack_frame.AddrStack.Offset = context_record.Esp; #endif stack_frame.AddrPC.Mode = AddrModeFlat; stack_frame.AddrFrame.Mode = AddrModeFlat; stack_frame.AddrStack.Mode = AddrModeFlat; while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(), &stack_frame, &context_record, NULL, &SymFunctionTableAccess64, &SymGetModuleBase64, NULL) && size < stack.size()) { stack[size++] = reinterpret_cast(stack_frame.AddrPC.Offset); } struct symbol_bundle : SYMBOL_INFO { wchar_t name[MAX_SYM_NAME]; }; HANDLE p = GetCurrentProcess(); static bool sym_initialized = false; if (!sym_initialized) { sym_initialized = true; SymInitialize(p, NULL, true); } SymRefreshModuleList(p); for (int i = 0; i < size && len > 0; ++i) { DWORD_PTR frame_ptr = reinterpret_cast(stack[i]); DWORD64 displacement = 0; symbol_bundle symbol; symbol.MaxNameLen = MAX_SYM_NAME; symbol.SizeOfStruct = sizeof(SYMBOL_INFO); BOOL const has_symbol = SymFromAddr(p, frame_ptr, &displacement, &symbol); DWORD line_displacement = 0; IMAGEHLP_LINE64 line = {}; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); BOOL const has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame_ptr, &line_displacement, &line); int ret = snprintf(out, len, "%2d: %p", i, stack[i]); out += ret; len -= ret; if (len <= 0) break; if (has_symbol) { ret = snprintf(out, len, " %s +%-4" PRId64 , demangle(symbol.Name).c_str(), displacement); out += ret; len -= ret; if (len <= 0) break; } if (has_line) { ret = snprintf(out, len, " %s:%d" , line.FileName, line.LineNumber); out += ret; len -= ret; if (len <= 0) break; } ret = snprintf(out, len, "\n"); out += ret; len -= ret; if (i == max_depth && max_depth > 0) break; } } #else TORRENT_EXPORT void print_backtrace(char* out, int len, int /*max_depth*/, void* /* ctx */) { out[0] = 0; strncat(out, "", len); } #endif #endif #if TORRENT_USE_ASSERTS || defined TORRENT_ASIO_DEBUGGING #ifdef TORRENT_PRODUCTION_ASSERTS char const* libtorrent_assert_log = "asserts.log"; namespace { // the number of asserts we've printed to the log boost::atomic assert_counter(0); } #endif TORRENT_FORMAT(1,2) TORRENT_EXPORT void assert_print(char const* fmt, ...) { #ifdef TORRENT_PRODUCTION_ASSERTS if (assert_counter > 500) return; FILE* out = fopen(libtorrent_assert_log, "a+"); if (out == 0) out = stderr; #else FILE* out = stderr; #endif va_list va; va_start(va, fmt); vfprintf(out, fmt, va); va_end(va); #ifdef TORRENT_PRODUCTION_ASSERTS if (out != stderr) fclose(out); #endif } // we deliberately don't want asserts to be marked as no-return, since that // would trigger warnings in debug builds of any code coming after the assert #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-noreturn" #endif TORRENT_EXPORT void assert_fail(char const* expr, int line , char const* file, char const* function, char const* value, int kind) { #ifdef TORRENT_PRODUCTION_ASSERTS // no need to flood the assert log with infinite number of asserts if (assert_counter.fetch_add(1) + 1 > 500) return; #endif char stack[8192]; stack[0] = '\0'; print_backtrace(stack, sizeof(stack), 0); char const* message = "assertion failed. Please file a bugreport at " "https://github.com/arvidn/libtorrent/issues\n" "Please include the following information:\n\n" "version: " LIBTORRENT_VERSION "-" LIBTORRENT_REVISION "\n"; switch (kind) { case 1: message = "A precondition of a libtorrent function has been violated.\n" "This indicates a bug in the client application using libtorrent\n"; } assert_print("%s\n" #ifdef TORRENT_PRODUCTION_ASSERTS "#: %d\n" #endif "file: '%s'\n" "line: %d\n" "function: %s\n" "expression: %s\n" "%s%s\n" "stack:\n" "%s\n" , message #ifdef TORRENT_PRODUCTION_ASSERTS , assert_counter.load() #endif , file, line, function, expr , value ? value : "", value ? "\n" : "" , stack); // if production asserts are defined, don't abort, just print the error #ifndef TORRENT_PRODUCTION_ASSERTS #ifdef _MSC_VER __debugbreak(); #else // send SIGINT to the current process // to break into the debugger raise(SIGABRT); abort(); #endif #endif } #ifdef __clang__ #pragma clang diagnostic pop #endif #elif !TORRENT_USE_ASSERTS // these are just here to make it possible for a client that built with debug // enable to be able to link against a release build (just possible, not // necessarily supported) TORRENT_FORMAT(1,2) TORRENT_EXPORT void assert_print(char const*, ...) {} TORRENT_EXPORT void assert_fail(char const*, int, char const* , char const*, char const*, int) {} #endif libtorrent-rasterbar-1.1.13/src/bandwidth_limit.cpp000066400000000000000000000064011351156116000224010ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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/bandwidth_limit.hpp" #include namespace libtorrent { bandwidth_channel::bandwidth_channel() : tmp(0) , distribute_quota(0) , m_quota_left(0) , m_limit(0) {} // 0 means infinite void bandwidth_channel::throttle(int limit) { TORRENT_ASSERT_VAL(limit >= 0, limit); // if the throttle is more than this, we might overflow TORRENT_ASSERT_VAL(limit < inf, limit); m_limit = limit; } int bandwidth_channel::quota_left() const { if (m_limit == 0) return inf; return (std::max)(int(m_quota_left), 0); } void bandwidth_channel::update_quota(int dt_milliseconds) { TORRENT_ASSERT_VAL(m_limit >= 0, m_limit); TORRENT_ASSERT_VAL(m_limit < inf, m_limit); if (m_limit == 0) return; // "to_add" should never have int64 overflow: "m_limit" contains < "::max" boost::int64_t to_add = (boost::int64_t(m_limit) * dt_milliseconds + 500) / 1000; if (to_add > inf - m_quota_left) { m_quota_left = inf; } else { m_quota_left += to_add; if (m_quota_left / 3 > m_limit) m_quota_left = boost::int64_t(m_limit) * 3; // "m_quota_left" will never have int64 overflow but may exceed "::max" m_quota_left = std::min(m_quota_left, boost::int64_t(inf)); } distribute_quota = int(std::max(m_quota_left, boost::int64_t(0))); } // this is used when connections disconnect with // some quota left. It's returned to its bandwidth // channels. void bandwidth_channel::return_quota(int amount) { TORRENT_ASSERT(amount >= 0); if (m_limit == 0) return; TORRENT_ASSERT(m_quota_left <= m_quota_left + amount); m_quota_left += amount; } void bandwidth_channel::use_quota(int amount) { TORRENT_ASSERT(amount >= 0); TORRENT_ASSERT(m_limit >= 0); if (m_limit == 0) return; m_quota_left -= amount; } } libtorrent-rasterbar-1.1.13/src/bandwidth_manager.cpp000066400000000000000000000135511351156116000227010ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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/bandwidth_manager.hpp" #include "libtorrent/time.hpp" namespace libtorrent { bandwidth_manager::bandwidth_manager(int channel) : m_queued_bytes(0) , m_channel(channel) , m_abort(false) { } void bandwidth_manager::close() { m_abort = true; queue_t tm; tm.swap(m_queue); m_queued_bytes = 0; while (!tm.empty()) { bw_request& bwr = tm.back(); bwr.peer->assign_bandwidth(m_channel, bwr.assigned); tm.pop_back(); } } #if TORRENT_USE_ASSERTS bool bandwidth_manager::is_queued(bandwidth_socket const* peer) const { for (queue_t::const_iterator i = m_queue.begin() , end(m_queue.end()); i != end; ++i) { if (i->peer.get() == peer) return true; } return false; } #endif int bandwidth_manager::queue_size() const { return m_queue.size(); } boost::int64_t bandwidth_manager::queued_bytes() const { return m_queued_bytes; } // non prioritized means that, if there's a line for bandwidth, // others will cut in front of the non-prioritized peers. // this is used by web seeds int bandwidth_manager::request_bandwidth(boost::shared_ptr const& peer , int blk, int priority, bandwidth_channel** chan, int num_channels) { INVARIANT_CHECK; if (m_abort) return 0; TORRENT_ASSERT(blk > 0); TORRENT_ASSERT(priority > 0); // if this assert is hit, the peer is requesting more bandwidth before // being assigned bandwidth for an already outstanding request TORRENT_ASSERT(!is_queued(peer.get())); if (num_channels == 0) { // the connection is not rate limited by any of its // bandwidth channels, or it doesn't belong to any // channels. There's no point in adding it to // the queue, just satisfy the request immediately return blk; } int k = 0; bw_request bwr(peer, blk, priority); for (int i = 0; i < num_channels; ++i) { if (chan[i]->need_queueing(blk)) bwr.channel[k++] = chan[i]; } if (k == 0) return blk; m_queued_bytes += blk; m_queue.push_back(bwr); return 0; } #if TORRENT_USE_INVARIANT_CHECKS void bandwidth_manager::check_invariant() const { boost::int64_t queued = 0; for (queue_t::const_iterator i = m_queue.begin() , end(m_queue.end()); i != end; ++i) { queued += i->request_size - i->assigned; } TORRENT_ASSERT(queued == m_queued_bytes); } #endif void bandwidth_manager::update_quotas(time_duration const& dt) { if (m_abort) return; if (m_queue.empty()) return; INVARIANT_CHECK; boost::int64_t dt_milliseconds = total_milliseconds(dt); if (dt_milliseconds > 3000) dt_milliseconds = 3000; // for each bandwidth channel, call update_quota(dt) std::vector channels; queue_t tm; for (queue_t::iterator i = m_queue.begin(); i != m_queue.end();) { if (i->peer->is_disconnecting()) { m_queued_bytes -= i->request_size - i->assigned; // return all assigned quota to all the // bandwidth channels this peer belongs to for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) { bandwidth_channel* bwc = i->channel[j]; bwc->return_quota(i->assigned); } i->assigned = 0; tm.push_back(*i); i = m_queue.erase(i); continue; } for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) { bandwidth_channel* bwc = i->channel[j]; bwc->tmp = 0; } ++i; } for (queue_t::iterator i = m_queue.begin() , end(m_queue.end()); i != end; ++i) { for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) { bandwidth_channel* bwc = i->channel[j]; if (bwc->tmp == 0) channels.push_back(bwc); TORRENT_ASSERT(INT_MAX - bwc->tmp > i->priority); bwc->tmp += i->priority; } } for (std::vector::iterator i = channels.begin() , end(channels.end()); i != end; ++i) { (*i)->update_quota(int(dt_milliseconds)); } for (queue_t::iterator i = m_queue.begin(); i != m_queue.end();) { int a = i->assign_bandwidth(); if (i->assigned == i->request_size || (i->ttl <= 0 && i->assigned > 0)) { a += i->request_size - i->assigned; TORRENT_ASSERT(i->assigned <= i->request_size); tm.push_back(*i); i = m_queue.erase(i); } else { ++i; } m_queued_bytes -= a; } while (!tm.empty()) { bw_request& bwr = tm.back(); bwr.peer->assign_bandwidth(m_channel, bwr.assigned); tm.pop_back(); } } } libtorrent-rasterbar-1.1.13/src/bandwidth_queue_entry.cpp000066400000000000000000000051171351156116000236330ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/bandwidth_queue_entry.hpp" #include #include namespace libtorrent { bw_request::bw_request(boost::shared_ptr const& pe , int blk, int prio) : peer(pe) , priority(prio) , assigned(0) , request_size(blk) , ttl(20) { TORRENT_ASSERT(priority > 0); std::memset(channel, 0, sizeof(channel)); } int bw_request::assign_bandwidth() { TORRENT_ASSERT(assigned < request_size); int quota = request_size - assigned; TORRENT_ASSERT(quota >= 0); --ttl; if (quota == 0) return quota; for (int j = 0; j < 5 && channel[j]; ++j) { if (channel[j]->throttle() == 0) continue; if (channel[j]->tmp == 0) continue; quota = (std::min)(int(boost::int64_t(channel[j]->distribute_quota) * priority / channel[j]->tmp), quota); } assigned += quota; for (int j = 0; j < 5 && channel[j]; ++j) channel[j]->use_quota(quota); TORRENT_ASSERT(assigned <= request_size); return quota; } } libtorrent-rasterbar-1.1.13/src/bdecode.cpp000066400000000000000000000702401351156116000206260ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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/bdecode.hpp" #include "libtorrent/alloca.hpp" #include #include #include // for memset #ifndef BOOST_SYSTEM_NOEXCEPT #define BOOST_SYSTEM_NOEXCEPT throw() #endif namespace libtorrent { using detail::bdecode_token; namespace { bool numeric(char c) { return c >= '0' && c <= '9'; } // finds the end of an integer and verifies that it looks valid this does // not detect all overflows, just the ones that are an order of magnitued // beyond. Exact overflow checking is done when the integer value is queried // from a bdecode_node. char const* check_integer(char const* start, char const* end , bdecode_errors::error_code_enum& e) { if (start == end) { e = bdecode_errors::unexpected_eof; return start; } if (*start == '-') { ++start; if (start == end) { e = bdecode_errors::unexpected_eof; return start; } } int digits = 0; do { if (!numeric(*start)) { e = bdecode_errors::expected_digit; break; } ++start; ++digits; if (start == end) { e = bdecode_errors::unexpected_eof; break; } } while (*start != 'e'); if (digits > 20) { e = bdecode_errors::overflow; } return start; } struct stack_frame { stack_frame(int t): token(t), state(0) {} // this is an index into m_tokens boost::uint32_t token:31; // this is used for dictionaries to indicate whether we're // reading a key or a vale. 0 means key 1 is value boost::uint32_t state:1; }; // str1 is null-terminated // str2 is not, str2 is len2 chars bool string_equal(char const* str1, char const* str2, int len2) { while (len2 > 0) { if (*str1 != *str2) return false; if (*str1 == 0) return false; ++str1; ++str2; --len2; } return *str1 == 0; } } // anonymous namespace // reads the string between start and end, or up to the first occurrance of // 'delimiter', whichever comes first. This string is interpreted as an // integer which is assigned to 'val'. If there's a non-delimiter and // non-digit in the range, a parse error is reported in 'ec'. If the value // cannot be represented by the variable 'val' and overflow error is reported // by 'ec'. char const* parse_int(char const* start, char const* end, char delimiter , boost::int64_t& val, bdecode_errors::error_code_enum& ec) { while (start < end && *start != delimiter) { if (!numeric(*start)) { ec = bdecode_errors::expected_digit; return start; } if (val > (std::numeric_limits::max)() / 10) { ec = bdecode_errors::overflow; return start; } val *= 10; int digit = *start - '0'; if (val > (std::numeric_limits::max)() - digit) { ec = bdecode_errors::overflow; return start; } val += digit; ++start; } return start; } struct bdecode_error_category : boost::system::error_category { virtual const char* name() const BOOST_SYSTEM_NOEXCEPT; virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT; virtual boost::system::error_condition default_error_condition( int ev) const BOOST_SYSTEM_NOEXCEPT { return boost::system::error_condition(ev, *this); } }; const char* bdecode_error_category::name() const BOOST_SYSTEM_NOEXCEPT { return "bdecode"; } std::string bdecode_error_category::message(int ev) const BOOST_SYSTEM_NOEXCEPT { static char const* msgs[] = { "no error", "expected digit in bencoded string", "expected colon in bencoded string", "unexpected end of file in bencoded string", "expected value (list, dict, int or string) in bencoded string", "bencoded nesting depth exceeded", "bencoded item count limit exceeded", "integer overflow", }; if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0]))) return "Unknown error"; return msgs[ev]; } boost::system::error_category& bdecode_category() { static bdecode_error_category bdecode_category; return bdecode_category; } #ifndef TORRENT_NO_DEPRECATED boost::system::error_category& get_bdecode_category() { return bdecode_category(); } #endif namespace bdecode_errors { boost::system::error_code make_error_code(error_code_enum e) { return boost::system::error_code(e, bdecode_category()); } } bdecode_node::bdecode_node() : m_root_tokens(0) , m_buffer(NULL) , m_buffer_size(0) , m_token_idx(-1) , m_last_index(-1) , m_last_token(-1) , m_size(-1) {} bdecode_node::bdecode_node(bdecode_node const& n) : m_tokens(n.m_tokens) , m_root_tokens(n.m_root_tokens) , m_buffer(n.m_buffer) , m_buffer_size(n.m_buffer_size) , m_token_idx(n.m_token_idx) , m_last_index(n.m_last_index) , m_last_token(n.m_last_token) , m_size(n.m_size) { (*this) = n; } bdecode_node& bdecode_node::operator=(bdecode_node const& n) { m_tokens = n.m_tokens; m_root_tokens = n.m_root_tokens; m_buffer = n.m_buffer; m_buffer_size = n.m_buffer_size; m_token_idx = n.m_token_idx; m_last_index = n.m_last_index; m_last_token = n.m_last_token; m_size = n.m_size; if (!m_tokens.empty()) { // if this is a root, make the token pointer // point to our storage m_root_tokens = &m_tokens[0]; } return *this; } bdecode_node::bdecode_node(bdecode_token const* tokens, char const* buf , int len, int idx) : m_root_tokens(tokens) , m_buffer(buf) , m_buffer_size(len) , m_token_idx(idx) , m_last_index(-1) , m_last_token(-1) , m_size(-1) { TORRENT_ASSERT(tokens != NULL); TORRENT_ASSERT(idx >= 0); } bdecode_node bdecode_node::non_owning() const { // if we're not a root, just return a copy of ourself if (m_tokens.empty()) return *this; // otherwise, return a reference to this node, but without // being an owning root node return bdecode_node(&m_tokens[0], m_buffer, m_buffer_size, m_token_idx); } void bdecode_node::clear() { m_tokens.clear(); m_root_tokens = NULL; m_token_idx = -1; m_size = -1; m_last_index = -1; m_last_token = -1; } void bdecode_node::switch_underlying_buffer(char const* buf) { TORRENT_ASSERT(!m_tokens.empty()); if (m_tokens.empty()) return; m_buffer = buf; } bdecode_node::type_t bdecode_node::type() const { if (m_token_idx == -1) return none_t; return static_cast(m_root_tokens[m_token_idx].type); } bdecode_node::operator bool() const { return m_token_idx != -1; } std::pair bdecode_node::data_section() const { if (m_token_idx == -1) return std::make_pair(m_buffer, 0); TORRENT_ASSERT(m_token_idx != -1); bdecode_token const& t = m_root_tokens[m_token_idx]; bdecode_token const& next = m_root_tokens[m_token_idx + t.next_item]; return std::make_pair(m_buffer + t.offset, next.offset - t.offset); } bdecode_node bdecode_node::list_at(int i) const { TORRENT_ASSERT(type() == list_t); TORRENT_ASSERT(i >= 0); // make sure this is a list. bdecode_token const* tokens = m_root_tokens; // this is the first item int token = m_token_idx + 1; int item = 0; // do we have a lookup cached? if (m_last_index <= i && m_last_index != -1) { token = m_last_token; item = m_last_index; } while (item < i) { token += tokens[token].next_item; ++item; // index 'i' out of range TORRENT_ASSERT(tokens[token].type != bdecode_token::end); } m_last_token = token; m_last_index = i; return bdecode_node(tokens, m_buffer, m_buffer_size, token); } std::string bdecode_node::list_string_value_at(int i , char const* default_val) const { bdecode_node n = list_at(i); if (n.type() != bdecode_node::string_t) return default_val; return n.string_value(); } boost::int64_t bdecode_node::list_int_value_at(int i , boost::int64_t default_val) const { bdecode_node n = list_at(i); if (n.type() != bdecode_node::int_t) return default_val; return n.int_value(); } int bdecode_node::list_size() const { TORRENT_ASSERT(type() == list_t); if (m_size != -1) return m_size; // make sure this is a list. bdecode_token const* tokens = m_root_tokens; TORRENT_ASSERT(tokens[m_token_idx].type == bdecode_token::list); // this is the first item int token = m_token_idx + 1; int ret = 0; // do we have a lookup cached? if (m_last_index != -1) { token = m_last_token; ret = m_last_index; } while (tokens[token].type != bdecode_token::end) { token += tokens[token].next_item; ++ret; } m_size = ret; return ret; } std::pair bdecode_node::dict_at(int i) const { TORRENT_ASSERT(type() == dict_t); TORRENT_ASSERT(m_token_idx != -1); bdecode_token const* tokens = m_root_tokens; TORRENT_ASSERT(tokens[m_token_idx].type == bdecode_token::dict); int token = m_token_idx + 1; int item = 0; // do we have a lookup cached? if (m_last_index <= i && m_last_index != -1) { token = m_last_token; item = m_last_index; } while (item < i) { TORRENT_ASSERT(tokens[token].type == bdecode_token::string); // skip the key token += tokens[token].next_item; TORRENT_ASSERT(tokens[token].type != bdecode_token::end); // skip the value token += tokens[token].next_item; ++item; // index 'i' out of range TORRENT_ASSERT(tokens[token].type != bdecode_token::end); } // there's no point in caching the first item if (i > 0) { m_last_token = token; m_last_index = i; } int value_token = token + tokens[token].next_item; TORRENT_ASSERT(tokens[token].type != bdecode_token::end); return std::make_pair( bdecode_node(tokens, m_buffer, m_buffer_size, token).string_value() , bdecode_node(tokens, m_buffer, m_buffer_size, value_token)); } int bdecode_node::dict_size() const { TORRENT_ASSERT(type() == dict_t); TORRENT_ASSERT(m_token_idx != -1); if (m_size != -1) return m_size; bdecode_token const* tokens = m_root_tokens; TORRENT_ASSERT(tokens[m_token_idx].type == bdecode_token::dict); // this is the first item int token = m_token_idx + 1; int ret = 0; if (m_last_index != -1) { ret = m_last_index * 2; token = m_last_token; } while (tokens[token].type != bdecode_token::end) { token += tokens[token].next_item; ++ret; } // a dictionary must contain full key-value pairs. which means // the number of entries is divisible by 2 TORRENT_ASSERT((ret % 2) == 0); // each item is one key and one value, so divide by 2 ret /= 2; m_size = ret; return ret; } bdecode_node bdecode_node::dict_find(std::string key) const { TORRENT_ASSERT(type() == dict_t); bdecode_token const* tokens = m_root_tokens; // this is the first item int token = m_token_idx + 1; while (tokens[token].type != bdecode_token::end) { bdecode_token const& t = tokens[token]; TORRENT_ASSERT(t.type == bdecode_token::string); int size = m_root_tokens[token + 1].offset - t.offset - t.start_offset(); if (int(key.size()) == size && std::equal(key.c_str(), key.c_str() + size, m_buffer + t.offset + t.start_offset())) { // skip key token += t.next_item; TORRENT_ASSERT(tokens[token].type != bdecode_token::end); return bdecode_node(tokens, m_buffer, m_buffer_size, token); } // skip key token += t.next_item; TORRENT_ASSERT(tokens[token].type != bdecode_token::end); // skip value token += tokens[token].next_item; } return bdecode_node(); } bdecode_node bdecode_node::dict_find_list(char const* key) const { bdecode_node ret = dict_find(key); if (ret.type() == bdecode_node::list_t) return ret; return bdecode_node(); } bdecode_node bdecode_node::dict_find_dict(std::string key) const { bdecode_node ret = dict_find(key); if (ret.type() == bdecode_node::dict_t) return ret; return bdecode_node(); } bdecode_node bdecode_node::dict_find_dict(char const* key) const { bdecode_node ret = dict_find(key); if (ret.type() == bdecode_node::dict_t) return ret; return bdecode_node(); } bdecode_node bdecode_node::dict_find_string(char const* key) const { bdecode_node ret = dict_find(key); if (ret.type() == bdecode_node::string_t) return ret; return bdecode_node(); } bdecode_node bdecode_node::dict_find_int(char const* key) const { bdecode_node ret = dict_find(key); if (ret.type() == bdecode_node::int_t) return ret; return bdecode_node(); } bdecode_node bdecode_node::dict_find(char const* key) const { TORRENT_ASSERT(type() == dict_t); bdecode_token const* tokens = m_root_tokens; // this is the first item int token = m_token_idx + 1; while (tokens[token].type != bdecode_token::end) { bdecode_token const& t = tokens[token]; TORRENT_ASSERT(t.type == bdecode_token::string); int size = m_root_tokens[token + 1].offset - t.offset - t.start_offset(); if (string_equal(key, m_buffer + t.offset + t.start_offset(), size)) { // skip key token += t.next_item; TORRENT_ASSERT(tokens[token].type != bdecode_token::end); return bdecode_node(tokens, m_buffer, m_buffer_size, token); } // skip key token += t.next_item; TORRENT_ASSERT(tokens[token].type != bdecode_token::end); // skip value token += tokens[token].next_item; } return bdecode_node(); } std::string bdecode_node::dict_find_string_value(char const* key , char const* default_value) const { bdecode_node n = dict_find(key); if (n.type() != bdecode_node::string_t) return default_value; return n.string_value(); } boost::int64_t bdecode_node::dict_find_int_value(char const* key , boost::int64_t default_val) const { bdecode_node n = dict_find(key); if (n.type() != bdecode_node::int_t) return default_val; return n.int_value(); } boost::int64_t bdecode_node::int_value() const { TORRENT_ASSERT(type() == int_t); bdecode_token const& t = m_root_tokens[m_token_idx]; int size = m_root_tokens[m_token_idx + 1].offset - t.offset; TORRENT_ASSERT(t.type == bdecode_token::integer); // +1 is to skip the 'i' char const* ptr = m_buffer + t.offset + 1; boost::int64_t val = 0; bool negative = false; if (*ptr == '-') negative = true; bdecode_errors::error_code_enum ec = bdecode_errors::no_error; char const* end = parse_int(ptr + negative , ptr + size, 'e', val, ec); if (ec) return 0; TORRENT_UNUSED(end); TORRENT_ASSERT(end < ptr + size); if (negative) val = -val; return val; } std::string bdecode_node::string_value() const { TORRENT_ASSERT(type() == string_t); bdecode_token const& t = m_root_tokens[m_token_idx]; int size = m_root_tokens[m_token_idx + 1].offset - t.offset - t.start_offset(); TORRENT_ASSERT(t.type == bdecode_token::string); return std::string(m_buffer + t.offset + t.start_offset(), size); } char const* bdecode_node::string_ptr() const { TORRENT_ASSERT(type() == string_t); bdecode_token const& t = m_root_tokens[m_token_idx]; TORRENT_ASSERT(t.type == bdecode_token::string); return m_buffer + t.offset + t.start_offset(); } int bdecode_node::string_length() const { TORRENT_ASSERT(type() == string_t); bdecode_token const& t = m_root_tokens[m_token_idx]; TORRENT_ASSERT(t.type == bdecode_token::string); return m_root_tokens[m_token_idx + 1].offset - t.offset - t.start_offset(); } void bdecode_node::reserve(int tokens) { m_tokens.reserve(tokens); } void bdecode_node::swap(bdecode_node& n) { /* bool lhs_is_root = (m_root_tokens == &m_tokens); bool rhs_is_root = (n.m_root_tokens == &n.m_tokens); // swap is only defined between non-root nodes // and between root-nodes. They may not be mixed! // note that when swapping root nodes, all bdecode_node // entries that exist in those subtrees are invalidated! TORRENT_ASSERT(lhs_is_root == rhs_is_root); // if both are roots, m_root_tokens always point to // its own vector, and should not get swapped (the // underlying vectors are swapped already) if (!lhs_is_root && !rhs_is_root) { // if neither is a root, we just swap the pointers // to the token vectors, switching their roots std::swap(m_root_tokens, n.m_root_tokens); } */ m_tokens.swap(n.m_tokens); std::swap(m_root_tokens, n.m_root_tokens); std::swap(m_buffer, n.m_buffer); std::swap(m_buffer_size, n.m_buffer_size); std::swap(m_token_idx, n.m_token_idx); std::swap(m_last_index, n.m_last_index); std::swap(m_last_token, n.m_last_token); std::swap(m_size, n.m_size); } #define TORRENT_FAIL_BDECODE(code) do { \ ec = code; \ if (error_pos) *error_pos = start - orig_start; \ goto done; \ } TORRENT_WHILE_0 int bdecode(char const* start, char const* end, bdecode_node& ret , error_code& ec, int* error_pos, int depth_limit, int token_limit) { ec.clear(); ret.clear(); if (end - start > bdecode_token::max_offset) { if (error_pos) *error_pos = 0; ec = bdecode_errors::limit_exceeded; return -1; } // this is the stack of bdecode_token indices, into m_tokens. // sp is the stack pointer, as index into the array, stack int sp = 0; stack_frame* stack = TORRENT_ALLOCA(stack_frame, depth_limit); char const* const orig_start = start; if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); while (start <= end) { if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); if (sp >= depth_limit) TORRENT_FAIL_BDECODE(bdecode_errors::depth_exceeded); --token_limit; if (token_limit < 0) TORRENT_FAIL_BDECODE(bdecode_errors::limit_exceeded); // look for a new token const char t = *start; const int current_frame = sp; // if we're currently parsing a dictionary, assert that // every other node is a string. if (current_frame > 0 && ret.m_tokens[stack[current_frame-1].token].type == bdecode_token::dict) { if (stack[current_frame-1].state == 0) { // the current parent is a dict and we are parsing a key. // only allow a digit (for a string) or 'e' to terminate if (!numeric(t) && t != 'e') TORRENT_FAIL_BDECODE(bdecode_errors::expected_digit); } } switch (t) { case 'd': stack[sp++] = ret.m_tokens.size(); // we push it into the stack so that we know where to fill // in the next_node field once we pop this node off the stack. // i.e. get to the node following the dictionary in the buffer ret.m_tokens.push_back(bdecode_token(start - orig_start , bdecode_token::dict)); ++start; break; case 'l': stack[sp++] = ret.m_tokens.size(); // we push it into the stack so that we know where to fill // in the next_node field once we pop this node off the stack. // i.e. get to the node following the list in the buffer ret.m_tokens.push_back(bdecode_token(start - orig_start , bdecode_token::list)); ++start; break; case 'i': { char const* int_start = start; bdecode_errors::error_code_enum e = bdecode_errors::no_error; // +1 here to point to the first digit, rather than 'i' start = check_integer(start + 1, end, e); if (e) { // in order to gracefully terminate the tree, // make sure the end of the previous token is set correctly if (error_pos) *error_pos = start - orig_start; error_pos = NULL; start = int_start; TORRENT_FAIL_BDECODE(e); } ret.m_tokens.push_back(bdecode_token(int_start - orig_start , 1, bdecode_token::integer, 1)); TORRENT_ASSERT(*start == 'e'); // skip 'e' ++start; break; } case 'e': { // this is the end of a list or dict if (sp == 0) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); if (sp > 0 && ret.m_tokens[stack[sp-1].token].type == bdecode_token::dict && stack[sp-1].state == 1) { // this means we're parsing a dictionary and about to parse a // value associated with a key. Instead, we got a termination TORRENT_FAIL_BDECODE(bdecode_errors::expected_value); } // insert the end-of-sequence token ret.m_tokens.push_back(bdecode_token(start - orig_start, 1 , bdecode_token::end)); // and back-patch the start of this sequence with the offset // to the next token we'll insert int top = stack[sp-1].token; // subtract the token's own index, since this is a relative // offset if (ret.m_tokens.size() - top > bdecode_token::max_next_item) TORRENT_FAIL_BDECODE(bdecode_errors::limit_exceeded); ret.m_tokens[top].next_item = ret.m_tokens.size() - top; // and pop it from the stack. --sp; ++start; break; } default: { // this is the case for strings. The start character is any // numeric digit if (!numeric(t)) TORRENT_FAIL_BDECODE(bdecode_errors::expected_value); boost::int64_t len = t - '0'; char const* str_start = start; ++start; if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); bdecode_errors::error_code_enum e = bdecode_errors::no_error; start = parse_int(start, end, ':', len, e); if (e) TORRENT_FAIL_BDECODE(e); if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon); // remaining buffer size excluding ':' const ptrdiff_t buff_size = end - start - 1; if (len > buff_size) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); if (len < 0) TORRENT_FAIL_BDECODE(bdecode_errors::overflow); // skip ':' ++start; if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); // the bdecode_token only has 8 bits to keep the header size // in. If it overflows, fail! if (start - str_start - 2 > detail::bdecode_token::max_header) TORRENT_FAIL_BDECODE(bdecode_errors::limit_exceeded); ret.m_tokens.push_back(bdecode_token(str_start - orig_start , 1, bdecode_token::string, start - str_start)); start += len; break; } } if (current_frame > 0 && ret.m_tokens[stack[current_frame-1].token].type == bdecode_token::dict) { // the next item we parse is the opposite stack[current_frame-1].state = ~stack[current_frame-1].state; } // this terminates the top level node, we're done! if (sp == 0) break; } done: // if parse failed, sp will be greater than 1 // unwind the stack by inserting terminator to make whatever we have // so far valid while (sp > 0) { TORRENT_ASSERT(ec); --sp; // we may need to insert a dummy token to properly terminate the tree, // in case we just parsed a key to a dict and failed in the value if (ret.m_tokens[stack[sp].token].type == bdecode_token::dict && stack[sp].state == 1) { // insert an empty dictionary as the value ret.m_tokens.push_back(bdecode_token(start - orig_start , 2, bdecode_token::dict)); ret.m_tokens.push_back(bdecode_token(start - orig_start , bdecode_token::end)); } int top = stack[sp].token; TORRENT_ASSERT(ret.m_tokens.size() - top <= bdecode_token::max_next_item); ret.m_tokens[top].next_item = ret.m_tokens.size() - top; ret.m_tokens.push_back(bdecode_token(start - orig_start, 1, bdecode_token::end)); } ret.m_tokens.push_back(bdecode_token(start - orig_start, 0 , bdecode_token::end)); ret.m_token_idx = 0; ret.m_buffer = orig_start; ret.m_buffer_size = start - orig_start; ret.m_root_tokens = &ret.m_tokens[0]; return ec ? -1 : 0; } namespace { int line_longer_than(bdecode_node const& e, int limit) { int line_len = 0; switch (e.type()) { case bdecode_node::list_t: line_len += 4; if (line_len > limit) return -1; for (int i = 0; i < e.list_size(); ++i) { int ret = line_longer_than(e.list_at(i), limit - line_len); if (ret == -1) return -1; line_len += ret + 2; } break; case bdecode_node::dict_t: line_len += 4; if (line_len > limit) return -1; for (int i = 0; i < e.dict_size(); ++i) { line_len += 4 + e.dict_at(i).first.size(); if (line_len > limit) return -1; int ret = line_longer_than(e.dict_at(i).second, limit - line_len); if (ret == -1) return -1; line_len += ret + 1; } break; case bdecode_node::string_t: line_len += 3 + e.string_length(); break; case bdecode_node::int_t: { boost::int64_t val = e.int_value(); while (val > 0) { ++line_len; val /= 10; } line_len += 2; } break; case bdecode_node::none_t: line_len += 4; break; } if (line_len > limit) return -1; return line_len; } void escape_string(std::string& ret, char const* str, int len) { for (int i = 0; i < len; ++i) { if (str[i] >= 32 && str[i] < 127) { ret += str[i]; } else { char tmp[5]; snprintf(tmp, sizeof(tmp), "\\x%02x", boost::uint8_t(str[i])); ret += tmp; } } } void print_string(std::string& ret, char const* str, int len, bool single_line) { bool printable = true; for (int i = 0; i < len; ++i) { char c = str[i]; if (c >= 32 && c < 127) continue; printable = false; break; } ret += "'"; if (printable) { if (single_line && len > 30) { ret.append(str, 14); ret += "..."; ret.append(str + len-14, 14); } else ret.append(str, len); ret += "'"; return; } if (single_line && len > 20) { escape_string(ret, str, 9); ret += "..."; escape_string(ret, str + len - 9, 9); } else { escape_string(ret, str, len); } ret += "'"; } } std::string print_entry(bdecode_node const& e , bool single_line, int indent) { char indent_str[200]; using std::memset; memset(indent_str, ' ', 200); indent_str[0] = ','; indent_str[1] = '\n'; indent_str[199] = 0; if (indent < 197 && indent >= 0) indent_str[indent+2] = 0; std::string ret; switch (e.type()) { case bdecode_node::none_t: return "none"; case bdecode_node::int_t: { char str[100]; snprintf(str, sizeof(str), "%" PRId64, e.int_value()); return str; } case bdecode_node::string_t: { print_string(ret, e.string_ptr(), e.string_length(), single_line); return ret; } case bdecode_node::list_t: { ret += '['; bool one_liner = line_longer_than(e, 200) != -1 || single_line; if (!one_liner) ret += indent_str + 1; for (int i = 0; i < e.list_size(); ++i) { if (i == 0 && one_liner) ret += " "; ret += print_entry(e.list_at(i), single_line, indent + 2); if (i < e.list_size() - 1) ret += (one_liner?", ":indent_str); else ret += (one_liner?" ":indent_str+1); } ret += "]"; return ret; } case bdecode_node::dict_t: { ret += "{"; bool one_liner = line_longer_than(e, 200) != -1 || single_line; if (!one_liner) ret += indent_str+1; for (int i = 0; i < e.dict_size(); ++i) { if (i == 0 && one_liner) ret += " "; std::pair ent = e.dict_at(i); print_string(ret, ent.first.c_str(), ent.first.size(), true); ret += ": "; ret += print_entry(ent.second, single_line, indent + 2); if (i < e.dict_size() - 1) ret += (one_liner?", ":indent_str); else ret += (one_liner?" ":indent_str+1); } ret += "}"; return ret; } } return ret; } } libtorrent-rasterbar-1.1.13/src/bitfield.cpp000066400000000000000000000106351351156116000210250ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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/bitfield.hpp" #include "libtorrent/aux_/cpuid.hpp" #ifdef _MSC_VER #include #endif namespace libtorrent { bool bitfield::all_set() const { const int words = size() / 32; for (int i = 0; i < words; ++i) { if (m_buf[i] != 0xffffffff) return false; } int rest = size() & 31; if (rest > 0) { boost::uint32_t mask = aux::host_to_network(0xffffffff << (32-rest)); if ((m_buf[words] & mask) != mask) return false; } return true; } int bitfield::count() const { int ret = 0; const int words = num_words(); #if TORRENT_HAS_SSE if (aux::mmx_support) { for (int i = 0; i < words; ++i) { #ifdef __GNUC__ boost::uint32_t cnt = 0; __asm__("popcnt %1, %0" : "=r"(cnt) : "r"(m_buf[i])); ret += cnt; #else ret += _mm_popcnt_u32(m_buf[i]); #endif } return ret; } #endif // TORRENT_HAS_SSE for (int i = 0; i < words; ++i) { boost::uint32_t v = m_buf[i]; // from: // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel static const int S[] = {1, 2, 4, 8, 16}; // Magic Binary Numbers static const int B[] = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF}; boost::uint32_t c = v - ((v >> 1) & B[0]); c = ((c >> S[1]) & B[1]) + (c & B[1]); c = ((c >> S[2]) + c) & B[2]; c = ((c >> S[3]) + c) & B[3]; c = ((c >> S[4]) + c) & B[4]; ret += c; } TORRENT_ASSERT(ret <= size()); TORRENT_ASSERT(ret >= 0); return ret; } void bitfield::resize(int bits, bool val) { if (bits == size()) return; int s = size(); int b = size() & 31; resize(bits); if (s >= size()) return; int old_size_words = (s + 31) / 32; int new_size_words = num_words(); if (val) { if (old_size_words && b) m_buf[old_size_words - 1] |= aux::host_to_network((0xffffffff >> b)); if (old_size_words < new_size_words) std::memset(m_buf + old_size_words, 0xff , size_t((new_size_words - old_size_words) * 4)); clear_trailing_bits(); } else { if (old_size_words < new_size_words) std::memset(m_buf + old_size_words, 0x00 , size_t((new_size_words - old_size_words) * 4)); } TORRENT_ASSERT(size() == bits); } void bitfield::resize(int bits) { if (bits == size()) return; TORRENT_ASSERT(bits >= 0); // +1 because the first word is the size (in bits) const int b = (bits + 31) / 32; if (m_buf) { boost::uint32_t* tmp = static_cast(std::realloc(m_buf-1, (b+1) * 4)); #ifndef BOOST_NO_EXCEPTIONS if (tmp == NULL) throw std::bad_alloc(); #endif m_buf = tmp + 1; m_buf[-1] = bits; } else if (bits > 0) { boost::uint32_t* tmp = static_cast(std::malloc((b+1) * 4)); #ifndef BOOST_NO_EXCEPTIONS if (tmp == NULL) throw std::bad_alloc(); #endif m_buf = tmp + 1; m_buf[-1] = bits; } else if (m_buf != NULL) { std::free(m_buf-1); m_buf = NULL; } clear_trailing_bits(); TORRENT_ASSERT(size() == bits); } } libtorrent-rasterbar-1.1.13/src/block_cache.cpp000066400000000000000000001601471351156116000214640ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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/config.hpp" #include "libtorrent/block_cache.hpp" #include "libtorrent/disk_buffer_pool.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/time.hpp" #include "libtorrent/disk_io_job.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/io_service_fwd.hpp" #include "libtorrent/error.hpp" #include "libtorrent/disk_io_thread.hpp" // disk_operation_failed #include "libtorrent/invariant_check.hpp" #include "libtorrent/alloca.hpp" #include "libtorrent/performance_counters.hpp" #include "libtorrent/aux_/time.hpp" #ifdef TORRENT_DEBUG #include "libtorrent/random.hpp" #endif /* The disk cache mimics ARC (adaptive replacement cache). See paper: http://dbs.uni-leipzig.de/file/ARC.pdf See slides: http://www-vlsi.stanford.edu/smart_memories/protected/meetings/spring2004/arc-fast.pdf This cache has a few modifications to make it fit the bittorrent use case better. It has a few more lists and it defers the eviction of pieces. read_lru1 This is a plain LRU for items that have been requested once. If a piece in this list gets accessed again, by someone other than the first accessor, the piece is promoted into LRU2. which holds pieces that are more frequently used, and more important to keep around as this LRU list takes churn. read_lru1_ghost This is a list of pieces that were least recently evicted from read_lru1. These pieces don't hold any actual blocks in the cache, they are just here to extend the reach and probability for pieces to be promoted into read_lru2. Any piece in this list that get one more access is promoted to read_lru2. This is technically a cache-miss, since there's no cached blocks here, but for the purposes of promoting the piece from infrequently used to frequently used), it's considered a cache-hit. read_lru2 TODO read_lru2_ghost TODO volatile_read_lru TODO write_lru TODO Cache hits .......... When a piece get a cache hit, it's promoted, either to the beginning of the lru2 or into lru2. Since this ARC implementation operates on pieces instead of blocks, any one peer requesting blocks from one piece would essentially always produce a "cache hit" the second block it requests. In order to make the promotions make more sense, and be more in the spirit of the ARC algorithm, each access contains a token, unique to each peer. If any access has a different token than the last one, it's considered a cache hit. This is because at least two peers requested blocks from the same piece. Deferred evictions .................. Since pieces and blocks can be pinned in the cache, and it's not always practical, or possible, to evict a piece at the point where a new block is allocated (because it's not known what the block will be used for), evictions are not done at the time of allocating blocks. Instead, whenever an operation requires to add a new piece to the cache, it also records the cache event leading to it, in m_last_cache_op. This is one of cache_miss (piece did not exist in cache), lru1_ghost_hit (the piece was found in lru1_ghost and it was promoted) or lru2_ghost_hit (the piece was found in lru2_ghost and it was promoted). This cache operation then guides the cache eviction algorithm to know which list to evict from. The volatile list is always the first one to be evicted however. Write jobs .......... When the write cache is enabled, write jobs are not issued via the normal job queue. They are just hung on its corresponding cached piece entry, and a flush_hashed job is issued. This job will inspect the current state of the cached piece and determine if any of the blocks should be flushed. It also kicks the hasher, i.e. progresses the SHA1 context, which calculates the SHA-1 hash of the piece. This job flushed blocks that have been hashed and also form a contiguous block run of at least the write cache line size. Read jobs ......... The data blocks pulled in from disk by read jobs, are hung on the corresponding cache piece (cached_piece_entry) once the operation completes. Read operations typically pulls in an entire read cache stripe, and not just the one block that was requested. When adjacent blocks are requested to be read in quick succession, there is a risk that each block would pull in more blocks (read ahead) and potentially read the same blocks several times, if the original requests were serviced by different disk thread. This is because all the read operation may start before any of them has completed, hanging the resulting blocks in the cache. i.e. they would all be cache misses, even though all but the first should be cache hits in the first's read ahead. In order to solve this problem, there is only a single outstanding read job at any given time per piece. When there is an outstanding read job on a piece, the *outstanding_read* member is set to 1. This indicates that the job should be hung on the piece for later processing, instead of being issued into the main job queue. There is a tailqueue on each piece entry called read_jobs where these jobs are added. At the end of every read job, this job list is inspected, any job in it is tried against the cache to see if it's a cache hit now. If it is, complete it right away. If it isn't, put it back in the read_jobs list except for one, which is issued into the regular job queue. */ #define DEBUG_CACHE 0 #if __cplusplus >= 201103L || defined __clang__ #if DEBUG_CACHE #define DLOG(...) fprintf(__VA_ARGS__) #else #define DLOG(...) do {} while (false) #endif #else // cplusplus #if DEBUG_CACHE #define DLOG fprintf #else #define DLOG TORRENT_WHILE_0 fprintf #endif #endif // cplusplus namespace libtorrent { #if DEBUG_CACHE void log_refcounts(cached_piece_entry const* pe) { char out[4096]; char* ptr = out; char* end = ptr + sizeof(out); ptr += snprintf(ptr, end - ptr, "piece: %d [ ", int(pe->piece)); for (int i = 0; i < pe->blocks_in_piece; ++i) { ptr += snprintf(ptr, end - ptr, "%d ", int(pe->blocks[i].refcount)); } strncpy(ptr, "]\n", end - ptr); DLOG(stderr, out); } #endif const char* const job_action_name[] = { "read", "write", "hash", "move_storage", "release_files", "delete_files", "check_fastresume", "save_resume_data", "rename_file", "stop_torrent", #ifndef TORRENT_NO_DEPRECATE "cache_piece", "finalize_file", #endif "flush_piece", "flush_hashed", "flush_storage", "trim_cache", "set_file_priority", "load_torrent", "clear_piece", "tick_storage", "resolve_links" }; #if __cplusplus >= 201103L // make sure the job names array covers all the job IDs static_assert(sizeof(job_action_name)/sizeof(job_action_name[0]) == disk_io_job::num_job_ids, "disk-job-action and action-name-array mismatch"); #endif #if TORRENT_USE_ASSERTS char const* const piece_log_t::job_names[7] = { "flushing", "flush_expired", "try_flush_write_blocks", "try_flush_write_blocks2", "flush_range", "clear_outstanding_jobs", "set_outstanding_jobs", }; char const* job_name(int j) { if (j < 0 || j >= piece_log_t::last_job) return "unknown"; if (j < piece_log_t::flushing) return job_action_name[j]; return piece_log_t::job_names[j - piece_log_t::flushing]; } void print_piece_log(std::vector const& piece_log) { for (int i = 0; i < int(piece_log.size()); ++i) { if (piece_log[i].block == -1) { printf("%d: %s\n", i, job_name(piece_log[i].job)); } else { printf("%d: %s %d\n", i, job_name(piece_log[i].job), piece_log[i].block); } } } void assert_print_piece(cached_piece_entry const* pe) { static const char* const cache_state[] = { "write", "volatile-read", "read-lru", "read-lru-ghost", "read-lfu", "read-lfu-ghost" }; if (pe == NULL) { assert_print("piece: NULL\n"); } else { assert_print("piece: %d\nrefcount: %d\npiece_refcount: %d\n" "num_blocks: %d\nhashing: %d\n\nhash: %p\nhash_offset: %d\n" "cache_state: (%d) %s\noutstanding_flush: %d\npiece: %d\n" "num_dirty: %d\nnum_blocks: %d\nblocks_in_piece: %d\n" "hashing_done: %d\nmarked_for_deletion: %d\nneed_readback: %d\n" "hash_passed: %d\nread_jobs: %d\njobs: %d\n" "piece_log:\n" , int(pe->piece), pe->refcount, pe->piece_refcount, int(pe->num_blocks) , int(pe->hashing), static_cast(pe->hash), pe->hash ? pe->hash->offset : -1 , int(pe->cache_state) , pe->cache_state < cached_piece_entry::num_lrus ? cache_state[pe->cache_state] : "" , int(pe->outstanding_flush), int(pe->piece), int(pe->num_dirty) , int(pe->num_blocks), int(pe->blocks_in_piece), int(pe->hashing_done) , int(pe->marked_for_eviction), int(pe->need_readback), pe->hash_passes , int(pe->read_jobs.size()), int(pe->jobs.size())); for (int i = 0; i < pe->piece_log.size(); ++i) { assert_print("%s %s (%d)", (i==0?"":",") , job_name(pe->piece_log[i].job), pe->piece_log[i].block); } } assert_print("\n"); } #define TORRENT_PIECE_ASSERT(cond, piece) \ do { if (!(cond)) { assert_print_piece(piece); assert_fail(#cond, __LINE__, __FILE__, TORRENT_FUNCTION, 0); } } TORRENT_WHILE_0 #else #define TORRENT_PIECE_ASSERT(cond, piece) do {} TORRENT_WHILE_0 #endif cached_piece_entry::cached_piece_entry() : storage() , hash(0) , last_requester(NULL) , blocks() , expire(min_time()) , piece(0) , num_dirty(0) , num_blocks(0) , blocks_in_piece(0) , hashing(0) , hashing_done(0) , marked_for_deletion(false) , need_readback(false) , cache_state(read_lru1) , piece_refcount(0) , outstanding_flush(0) , outstanding_read(0) , marked_for_eviction(false) , pinned(0) , refcount(0) #if TORRENT_USE_ASSERTS , hash_passes(0) , in_storage(false) , in_use(true) #endif {} cached_piece_entry::~cached_piece_entry() { TORRENT_ASSERT(piece_refcount == 0); TORRENT_ASSERT(jobs.size() == 0); TORRENT_ASSERT(read_jobs.size() == 0); #if TORRENT_USE_ASSERTS for (int i = 0; i < blocks_in_piece; ++i) { TORRENT_ASSERT(blocks[i].buf == 0); TORRENT_ASSERT(!blocks[i].pending); TORRENT_ASSERT(blocks[i].refcount == 0); TORRENT_ASSERT(blocks[i].hashing_count == 0); TORRENT_ASSERT(blocks[i].flushing_count == 0); } in_use = false; #endif delete hash; } block_cache::block_cache(int block_size, io_service& ios , boost::function const& trigger_trim) : disk_buffer_pool(block_size, ios, trigger_trim) , m_last_cache_op(cache_miss) , m_ghost_size(8) , m_max_volatile_blocks(100) , m_volatile_size(0) , m_read_cache_size(0) , m_write_cache_size(0) , m_send_buffer_blocks(0) , m_pinned_blocks(0) { } // returns: // -1: not in cache // -2: no memory int block_cache::try_read(disk_io_job* j, bool expect_no_fail) { INVARIANT_CHECK; TORRENT_ASSERT(j->buffer.disk_block == 0); cached_piece_entry* p = find_piece(j); int ret = 0; // if the piece cannot be found in the cache, // it's a cache miss TORRENT_ASSERT(!expect_no_fail || p != NULL); if (p == 0) return -1; #if TORRENT_USE_ASSERTS p->piece_log.push_back(piece_log_t(j->action, j->d.io.offset / 0x4000)); #endif cache_hit(p, j->requester, j->flags & disk_io_job::volatile_read); ret = copy_from_piece(p, j, expect_no_fail); if (ret < 0) return ret; ret = j->d.io.buffer_size; return ret; } void block_cache::bump_lru(cached_piece_entry* p) { // move to the top of the LRU list TORRENT_PIECE_ASSERT(p->cache_state == cached_piece_entry::write_lru, p); linked_list* lru_list = &m_lru[p->cache_state]; // move to the back (MRU) of the list lru_list->erase(p); lru_list->push_back(p); p->expire = aux::time_now(); } // this is called for pieces that we're reading from, when they // are in the cache (including the ghost lists) void block_cache::cache_hit(cached_piece_entry* p, void* requester, bool volatile_read) { // this can be pretty expensive // INVARIANT_CHECK; TORRENT_ASSERT(p); TORRENT_ASSERT(p->in_use); // move the piece into this queue. Whenever we have a cache // hit, we move the piece into the lru2 queue (i.e. the most // frequently used piece). However, we only do that if the // requester is different than the last one. This is to // avoid a single requester making it look like a piece is // frequently requested, when in fact it's only a single peer int target_queue = cached_piece_entry::read_lru2; if (p->last_requester == requester || requester == NULL) { // if it's the same requester and the piece isn't in // any of the ghost lists, ignore it if (p->cache_state == cached_piece_entry::read_lru1 || p->cache_state == cached_piece_entry::read_lru2 || p->cache_state == cached_piece_entry::write_lru || p->cache_state == cached_piece_entry::volatile_read_lru) return; if (p->cache_state == cached_piece_entry::read_lru1_ghost) target_queue = cached_piece_entry::read_lru1; } if (p->cache_state == cached_piece_entry::volatile_read_lru) { // a volatile read hit on a volatile piece doesn't do anything if (volatile_read) return; // however, if this is a proper read on a volatile piece // we need to promote it to lru1 target_queue = cached_piece_entry::read_lru1; } if (requester != NULL) p->last_requester = requester; // if we have this piece anywhere in L1 or L2, it's a "hit" // and it should be bumped to the highest priority in L2 // i.e. "frequently used" if (p->cache_state < cached_piece_entry::read_lru1 || p->cache_state > cached_piece_entry::read_lru2_ghost) return; // if we got a cache hit in a ghost list, that indicates the proper // list is too small. Record which ghost list we got the hit in and // it will be used to determine which end of the cache we'll evict // from, next time we need to reclaim blocks if (p->cache_state == cached_piece_entry::read_lru1_ghost) { m_last_cache_op = ghost_hit_lru1; } else if (p->cache_state == cached_piece_entry::read_lru2_ghost) { m_last_cache_op = ghost_hit_lru2; } // move into L2 (frequently used) m_lru[p->cache_state].erase(p); m_lru[target_queue].push_back(p); p->cache_state = target_queue; p->expire = aux::time_now(); #if TORRENT_USE_ASSERTS switch (p->cache_state) { case cached_piece_entry::write_lru: case cached_piece_entry::volatile_read_lru: case cached_piece_entry::read_lru1: case cached_piece_entry::read_lru2: TORRENT_ASSERT(p->in_storage == true); break; default: TORRENT_ASSERT(p->in_storage == false); break; } #endif } // this is used to move pieces primarily from the write cache // to the read cache. Technically it can move from read to write // cache as well, it's unclear if that ever happens though void block_cache::update_cache_state(cached_piece_entry* p) { int state = p->cache_state; int desired_state = p->cache_state; if (p->num_dirty > 0 || p->hash != 0) desired_state = cached_piece_entry::write_lru; else if (p->cache_state == cached_piece_entry::write_lru) desired_state = cached_piece_entry::read_lru1; if (desired_state == state) return; TORRENT_PIECE_ASSERT(state < cached_piece_entry::num_lrus, p); TORRENT_PIECE_ASSERT(desired_state < cached_piece_entry::num_lrus, p); linked_list* src = &m_lru[state]; linked_list* dst = &m_lru[desired_state]; src->erase(p); dst->push_back(p); p->expire = aux::time_now(); p->cache_state = desired_state; #if TORRENT_USE_ASSERTS switch (p->cache_state) { case cached_piece_entry::write_lru: case cached_piece_entry::volatile_read_lru: case cached_piece_entry::read_lru1: case cached_piece_entry::read_lru2: TORRENT_ASSERT(p->in_storage == true); break; default: TORRENT_ASSERT(p->in_storage == false); break; } #endif } void block_cache::try_evict_one_volatile() { INVARIANT_CHECK; DLOG(stderr, "[%p] try_evict_one_volatile\n", static_cast(this)); if (m_volatile_size < m_max_volatile_blocks) return; linked_list* piece_list = &m_lru[cached_piece_entry::volatile_read_lru]; for (list_iterator i = piece_list->iterate(); i.get();) { cached_piece_entry* pe = i.get(); TORRENT_PIECE_ASSERT(pe->in_use, pe); i.next(); if (pe->ok_to_evict() && pe->num_blocks == 0) { #ifdef TORRENT_DEBUG for (int j = 0; j < pe->blocks_in_piece; ++j) TORRENT_PIECE_ASSERT(pe->blocks[j].buf == 0, pe); #endif TORRENT_PIECE_ASSERT(pe->refcount == 0, pe); move_to_ghost(pe); continue; } TORRENT_PIECE_ASSERT(pe->num_dirty == 0, pe); // someone else is using this piece if (pe->refcount > 0) continue; // some blocks are pinned in this piece, skip it if (pe->pinned > 0) continue; char** to_delete = TORRENT_ALLOCA(char*, pe->blocks_in_piece); int num_to_delete = 0; // go through the blocks and evict the ones that are not dirty and not // referenced for (int j = 0; j < pe->blocks_in_piece; ++j) { cached_block_entry& b = pe->blocks[j]; TORRENT_PIECE_ASSERT(b.dirty == false, pe); TORRENT_PIECE_ASSERT(b.pending == false, pe); if (b.buf == 0 || b.refcount > 0 || b.dirty || b.pending) continue; to_delete[num_to_delete++] = b.buf; b.buf = NULL; TORRENT_PIECE_ASSERT(pe->num_blocks > 0, pe); --pe->num_blocks; TORRENT_PIECE_ASSERT(m_read_cache_size > 0, pe); --m_read_cache_size; TORRENT_PIECE_ASSERT(m_volatile_size > 0, pe); --m_volatile_size; } if (pe->ok_to_evict() && pe->num_blocks == 0) { #ifdef TORRENT_DEBUG for (int j = 0; j < pe->blocks_in_piece; ++j) TORRENT_PIECE_ASSERT(pe->blocks[j].buf == 0, pe); #endif move_to_ghost(pe); } if (num_to_delete == 0) return; DLOG(stderr, "[%p] removed %d blocks\n", static_cast(this) , num_to_delete); free_multiple_buffers(to_delete, num_to_delete); return; } } cached_piece_entry* block_cache::allocate_piece(disk_io_job const* j, int cache_state) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; #endif TORRENT_ASSERT(cache_state < cached_piece_entry::num_lrus); // we're assuming we're not allocating a ghost piece // a bit further down TORRENT_ASSERT(cache_state != cached_piece_entry::read_lru1_ghost && cache_state != cached_piece_entry::read_lru2_ghost); cached_piece_entry* p = find_piece(j); if (p == 0) { int const piece_size = j->storage->files()->piece_size(j->piece); int const blocks_in_piece = (piece_size + block_size() - 1) / block_size(); cached_piece_entry pe; pe.piece = j->piece; pe.storage = j->storage; pe.expire = aux::time_now(); pe.blocks_in_piece = blocks_in_piece; pe.blocks.reset(new (std::nothrow) cached_block_entry[blocks_in_piece]); pe.cache_state = cache_state; pe.last_requester = j->requester; TORRENT_PIECE_ASSERT(pe.blocks, &pe); if (!pe.blocks) return 0; p = const_cast(&*m_pieces.insert(pe).first); j->storage->add_piece(p); TORRENT_PIECE_ASSERT(p->cache_state < cached_piece_entry::num_lrus, p); linked_list* lru_list = &m_lru[p->cache_state]; lru_list->push_back(p); // this piece is part of the ARC cache (as opposed to // the write cache). Allocating a new read piece indicates // that we just got a cache miss. Record this to determine // which end to evict blocks from next time we need to // evict blocks if (cache_state == cached_piece_entry::read_lru1) m_last_cache_op = cache_miss; #if TORRENT_USE_ASSERTS switch (p->cache_state) { case cached_piece_entry::write_lru: case cached_piece_entry::volatile_read_lru: case cached_piece_entry::read_lru1: case cached_piece_entry::read_lru2: TORRENT_ASSERT(p->in_storage == true); break; default: TORRENT_ASSERT(p->in_storage == false); break; } #endif } else { TORRENT_PIECE_ASSERT(p->in_use, p); // we want to retain the piece now p->marked_for_eviction = false; // only allow changing the cache state downwards. i.e. turn a ghost // piece into a non-ghost, or a read piece into a write piece if (p->cache_state > cache_state) { // this can happen for instance if a piece fails the hash check // first it's in the write cache, then it completes and is moved // into the read cache, but fails and is cleared (into the ghost list) // then we want to add new dirty blocks to it and we need to move // it back into the write cache m_lru[p->cache_state].erase(p); p->cache_state = cache_state; m_lru[p->cache_state].push_back(p); p->expire = aux::time_now(); #if TORRENT_USE_ASSERTS switch (p->cache_state) { case cached_piece_entry::write_lru: case cached_piece_entry::volatile_read_lru: case cached_piece_entry::read_lru1: case cached_piece_entry::read_lru2: TORRENT_ASSERT(p->in_storage == true); break; default: TORRENT_ASSERT(p->in_storage == false); break; } #endif } } return p; } cached_piece_entry* block_cache::add_dirty_block(disk_io_job* j) { #if !defined TORRENT_DISABLE_POOL_ALLOCATOR TORRENT_ASSERT(is_disk_buffer(j->buffer.disk_block)); #endif #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; #endif TORRENT_ASSERT(j->buffer.disk_block); TORRENT_ASSERT(m_write_cache_size + m_read_cache_size + 1 <= in_use()); cached_piece_entry* pe = allocate_piece(j, cached_piece_entry::write_lru); TORRENT_ASSERT(pe); if (pe == 0) return pe; TORRENT_PIECE_ASSERT(pe->in_use, pe); int block = j->d.io.offset / block_size(); TORRENT_ASSERT((j->d.io.offset % block_size()) == 0); // we should never add a new dirty block on a piece // that has checked the hash. Before we add it, the // piece need to be cleared (with async_clear_piece) TORRENT_PIECE_ASSERT(pe->hashing_done == 0, pe); // this only evicts read blocks int evict = num_to_evict(1); if (evict > 0) try_evict_blocks(evict, pe); TORRENT_PIECE_ASSERT(block < pe->blocks_in_piece, pe); TORRENT_PIECE_ASSERT(j->piece == pe->piece, pe); TORRENT_PIECE_ASSERT(!pe->marked_for_eviction, pe); TORRENT_PIECE_ASSERT(pe->blocks[block].refcount == 0, pe); cached_block_entry& b = pe->blocks[block]; TORRENT_PIECE_ASSERT(b.buf != j->buffer.disk_block, pe); // we might have a left-over read block from // hash checking // we might also have a previous dirty block which // we're still waiting for to be written if (b.buf != 0 && b.buf != j->buffer.disk_block) { TORRENT_PIECE_ASSERT(b.refcount == 0 && !b.pending, pe); free_block(pe, block); TORRENT_PIECE_ASSERT(b.dirty == 0, pe); } b.buf = j->buffer.disk_block; b.dirty = true; ++pe->num_blocks; ++pe->num_dirty; ++m_write_cache_size; j->buffer.disk_block = 0; TORRENT_PIECE_ASSERT(j->piece == pe->piece, pe); TORRENT_PIECE_ASSERT(j->flags & disk_io_job::in_progress, pe); TORRENT_PIECE_ASSERT(j->piece == pe->piece, pe); pe->jobs.push_back(j); if (block == 0 && pe->hash == NULL && pe->hashing_done == false) pe->hash = new partial_hash; update_cache_state(pe); bump_lru(pe); return pe; } // flushed is an array of num_flushed integers. Each integer is the block index // that was flushed. This function marks those blocks as not pending and not // dirty. It also adjusts its understanding of the read vs. write cache size // (since these blocks now are part of the read cache) the refcounts of the // blocks are also decremented by this function. They are expected to have been // incremented by the caller. bool block_cache::blocks_flushed(cached_piece_entry* pe, int const* flushed, int num_flushed) { TORRENT_PIECE_ASSERT(pe->in_use, pe); for (int i = 0; i < num_flushed; ++i) { int block = flushed[i]; TORRENT_PIECE_ASSERT(block >= 0, pe); TORRENT_PIECE_ASSERT(block < pe->blocks_in_piece, pe); TORRENT_PIECE_ASSERT(pe->blocks[block].dirty, pe); TORRENT_PIECE_ASSERT(pe->blocks[block].pending, pe); pe->blocks[block].pending = false; // it's important to mark it as non-dirty before decrementing the // refcount because the buffer may be marked as discardable/volatile it // this is the last reference to it pe->blocks[block].dirty = false; dec_block_refcount(pe, block, block_cache::ref_flushing); } m_write_cache_size -= num_flushed; m_read_cache_size += num_flushed; pe->num_dirty -= num_flushed; update_cache_state(pe); return maybe_free_piece(pe); } std::pair block_cache::all_pieces() const { return std::make_pair(m_pieces.begin(), m_pieces.end()); } void block_cache::free_block(cached_piece_entry* pe, int block) { TORRENT_ASSERT(pe != 0); TORRENT_PIECE_ASSERT(pe->in_use, pe); TORRENT_PIECE_ASSERT(block < pe->blocks_in_piece, pe); TORRENT_PIECE_ASSERT(block >= 0, pe); cached_block_entry& b = pe->blocks[block]; TORRENT_PIECE_ASSERT(b.refcount == 0, pe); TORRENT_PIECE_ASSERT(!b.pending, pe); TORRENT_PIECE_ASSERT(b.buf, pe); if (b.dirty) { --pe->num_dirty; b.dirty = false; TORRENT_PIECE_ASSERT(m_write_cache_size > 0, pe); --m_write_cache_size; } else { TORRENT_PIECE_ASSERT(m_read_cache_size > 0, pe); --m_read_cache_size; if (pe->cache_state == cached_piece_entry::volatile_read_lru) { --m_volatile_size; } } TORRENT_PIECE_ASSERT(pe->num_blocks > 0, pe); --pe->num_blocks; free_buffer(b.buf); b.buf = NULL; } bool block_cache::evict_piece(cached_piece_entry* pe, tailqueue& jobs , eviction_mode const mode) { INVARIANT_CHECK; TORRENT_PIECE_ASSERT(pe->in_use, pe); char** to_delete = TORRENT_ALLOCA(char*, pe->blocks_in_piece); int num_to_delete = 0; for (int i = 0; i < pe->blocks_in_piece; ++i) { if (pe->blocks[i].buf == 0 || pe->blocks[i].refcount > 0) continue; TORRENT_PIECE_ASSERT(!pe->blocks[i].pending, pe); TORRENT_PIECE_ASSERT(pe->blocks[i].buf != 0, pe); TORRENT_PIECE_ASSERT(num_to_delete < pe->blocks_in_piece, pe); to_delete[num_to_delete++] = pe->blocks[i].buf; pe->blocks[i].buf = NULL; TORRENT_PIECE_ASSERT(pe->num_blocks > 0, pe); --pe->num_blocks; if (!pe->blocks[i].dirty) { TORRENT_PIECE_ASSERT(m_read_cache_size > 0, pe); --m_read_cache_size; } else { TORRENT_PIECE_ASSERT(pe->num_dirty > 0, pe); --pe->num_dirty; pe->blocks[i].dirty = false; TORRENT_PIECE_ASSERT(m_write_cache_size > 0, pe); --m_write_cache_size; } if (pe->num_blocks == 0) break; } if (pe->cache_state == cached_piece_entry::volatile_read_lru) { m_volatile_size -= num_to_delete; } if (num_to_delete) free_multiple_buffers(to_delete, num_to_delete); if (pe->ok_to_evict(true) && pe->num_blocks == 0) { delete pe->hash; pe->hash = NULL; // append will move the items from pe->jobs onto the end of jobs jobs.append(pe->jobs); TORRENT_ASSERT(pe->jobs.size() == 0); if (mode == allow_ghost && (pe->cache_state == cached_piece_entry::read_lru1_ghost || pe->cache_state == cached_piece_entry::read_lru2_ghost)) return true; if (mode == disallow_ghost || pe->cache_state == cached_piece_entry::write_lru || pe->cache_state == cached_piece_entry::volatile_read_lru) erase_piece(pe); else move_to_ghost(pe); return true; } return false; } void block_cache::mark_for_eviction(cached_piece_entry* p , eviction_mode const mode) { INVARIANT_CHECK; DLOG(stderr, "[%p] block_cache mark-for-deletion " "piece: %d\n", static_cast(this), int(p->piece)); TORRENT_PIECE_ASSERT(p->jobs.empty(), p); tailqueue jobs; if (!evict_piece(p, jobs, mode)) { p->marked_for_eviction = true; p->marked_for_deletion = mode == disallow_ghost; } } void block_cache::erase_piece(cached_piece_entry* pe) { INVARIANT_CHECK; TORRENT_PIECE_ASSERT(pe->ok_to_evict(), pe); TORRENT_PIECE_ASSERT(pe->cache_state < cached_piece_entry::num_lrus, pe); TORRENT_PIECE_ASSERT(pe->jobs.empty(), pe); linked_list* lru_list = &m_lru[pe->cache_state]; if (pe->hash) { TORRENT_PIECE_ASSERT(pe->hash->offset == 0, pe); delete pe->hash; pe->hash = NULL; } pe->storage->remove_piece(pe); lru_list->erase(pe); m_pieces.erase(*pe); } // this only evicts read blocks. For write blocks, see // try_flush_write_blocks in disk_io_thread.cpp int block_cache::try_evict_blocks(int num, cached_piece_entry* ignore) { INVARIANT_CHECK; if (num <= 0) return 0; DLOG(stderr, "[%p] try_evict_blocks: %d\n", static_cast(this), num); char** to_delete = TORRENT_ALLOCA(char*, num); int num_to_delete = 0; // There are two ends of the ARC cache we can evict from. There's L1 and L2. // The last cache operation determines which end we'll evict from. If we go // through the entire list from the preferred end, and still need to evict // more blocks, we'll go to the other end and start evicting from there. The // lru_list is an array of two lists, these are the two ends to evict from, // ordered by preference. linked_list* lru_list[3]; // however, before we consider any of the proper LRU lists, we evict pieces // from the volatile list. These are low priority pieces that were // specifically marked as to not survive long in the cache. These are the // first pieces to go when evicting lru_list[0] = &m_lru[cached_piece_entry::volatile_read_lru]; if (m_last_cache_op == cache_miss) { // when there was a cache miss, evict from the largest list, to tend to // keep the lists of equal size when we don't know which one is // performing better if (m_lru[cached_piece_entry::read_lru2].size() > m_lru[cached_piece_entry::read_lru1].size()) { lru_list[1] = &m_lru[cached_piece_entry::read_lru2]; lru_list[2] = &m_lru[cached_piece_entry::read_lru1]; } else { lru_list[1] = &m_lru[cached_piece_entry::read_lru1]; lru_list[2] = &m_lru[cached_piece_entry::read_lru2]; } } else if (m_last_cache_op == ghost_hit_lru1) { // when we insert new items or move things from L1 to L2 // evict blocks from L2 lru_list[1] = &m_lru[cached_piece_entry::read_lru2]; lru_list[2] = &m_lru[cached_piece_entry::read_lru1]; } else { // when we get cache hits in L2 evict from L1 lru_list[1] = &m_lru[cached_piece_entry::read_lru1]; lru_list[2] = &m_lru[cached_piece_entry::read_lru2]; } // end refers to which end of the ARC cache we're evicting // from. The LFU or the LRU end for (int end = 0; num > 0 && end < 3; ++end) { // iterate over all blocks in order of last being used (oldest first) and // as long as we still have blocks to evict TODO: it's somewhat expensive // to iterate over this linked list. Presumably because of the random // access of memory. It would be nice if pieces with no evictable blocks // weren't in this list for (list_iterator i = lru_list[end]->iterate(); i.get() && num > 0;) { cached_piece_entry* pe = i.get(); TORRENT_PIECE_ASSERT(pe->in_use, pe); i.next(); if (pe == ignore) continue; if (pe->ok_to_evict() && pe->num_blocks == 0) { #ifdef TORRENT_DEBUG for (int j = 0; j < pe->blocks_in_piece; ++j) TORRENT_PIECE_ASSERT(pe->blocks[j].buf == 0, pe); #endif TORRENT_PIECE_ASSERT(pe->refcount == 0, pe); move_to_ghost(pe); continue; } TORRENT_PIECE_ASSERT(pe->num_dirty == 0, pe); // all blocks are pinned in this piece, skip it if (pe->num_blocks <= pe->pinned) continue; // go through the blocks and evict the ones that are not dirty and not // referenced int removed = 0; for (int j = 0; j < pe->blocks_in_piece && num > 0; ++j) { cached_block_entry& b = pe->blocks[j]; if (b.buf == 0 || b.refcount > 0 || b.dirty || b.pending) continue; to_delete[num_to_delete++] = b.buf; b.buf = NULL; TORRENT_PIECE_ASSERT(pe->num_blocks > 0, pe); --pe->num_blocks; ++removed; --num; } TORRENT_PIECE_ASSERT(m_read_cache_size >= removed, pe); m_read_cache_size -= removed; if (pe->cache_state == cached_piece_entry::volatile_read_lru) { m_volatile_size -= removed; } if (pe->ok_to_evict() && pe->num_blocks == 0) { #ifdef TORRENT_DEBUG for (int j = 0; j < pe->blocks_in_piece; ++j) TORRENT_PIECE_ASSERT(pe->blocks[j].buf == 0, pe); #endif move_to_ghost(pe); } } } // if we can't evict enough blocks from the read cache, also look at write // cache pieces for blocks that have already been written to disk and can be // evicted the first pass, we only evict blocks that have been hashed, the // second pass we flush anything this is potentially a very expensive // operation, since we're likely to have iterate every single block in the // cache, and we might not get to evict anything. // TODO: this should probably only be done every n:th time if (num > 0 && m_read_cache_size > m_pinned_blocks) { for (int pass = 0; pass < 2 && num > 0; ++pass) { for (list_iterator i = m_lru[cached_piece_entry::write_lru].iterate(); i.get() && num > 0;) { cached_piece_entry* pe = i.get(); TORRENT_PIECE_ASSERT(pe->in_use, pe); i.next(); if (pe == ignore) continue; if (pe->ok_to_evict() && pe->num_blocks == 0) { #ifdef TORRENT_DEBUG for (int j = 0; j < pe->blocks_in_piece; ++j) TORRENT_PIECE_ASSERT(pe->blocks[j].buf == 0, pe); #endif TORRENT_PIECE_ASSERT(pe->refcount == 0, pe); erase_piece(pe); continue; } // all blocks in this piece are dirty if (pe->num_dirty == pe->num_blocks) continue; int end = pe->blocks_in_piece; // the first pass, only evict blocks that have been // hashed if (pass == 0 && pe->hash) end = pe->hash->offset / block_size(); // go through the blocks and evict the ones // that are not dirty and not referenced int removed = 0; for (int j = 0; j < end && num > 0; ++j) { cached_block_entry& b = pe->blocks[j]; if (b.buf == 0 || b.refcount > 0 || b.dirty || b.pending) continue; to_delete[num_to_delete++] = b.buf; b.buf = NULL; TORRENT_PIECE_ASSERT(pe->num_blocks > 0, pe); --pe->num_blocks; ++removed; --num; } TORRENT_PIECE_ASSERT(m_read_cache_size >= removed, pe); m_read_cache_size -= removed; if (pe->cache_state == cached_piece_entry::volatile_read_lru) { m_volatile_size -= removed; } if (pe->ok_to_evict() && pe->num_blocks == 0) { #ifdef TORRENT_DEBUG for (int j = 0; j < pe->blocks_in_piece; ++j) TORRENT_PIECE_ASSERT(pe->blocks[j].buf == 0, pe); #endif erase_piece(pe); } } } } if (num_to_delete == 0) return num; DLOG(stderr, "[%p] removed %d blocks\n", static_cast(this) , num_to_delete); free_multiple_buffers(to_delete, num_to_delete); return num; } void block_cache::clear(tailqueue& jobs) { INVARIANT_CHECK; // this holds all the block buffers we want to free // at the end std::vector bufs; for (iterator p = m_pieces.begin() , end(m_pieces.end()); p != end; ++p) { cached_piece_entry& pe = const_cast(*p); #if TORRENT_USE_ASSERTS for (tailqueue_iterator i = pe.jobs.iterate(); i.get(); i.next()) TORRENT_PIECE_ASSERT((static_cast(i.get()))->piece == pe.piece, &pe); for (tailqueue_iterator i = pe.read_jobs.iterate(); i.get(); i.next()) TORRENT_PIECE_ASSERT((static_cast(i.get()))->piece == pe.piece, &pe); #endif // this also removes the jobs from the piece jobs.append(pe.jobs); jobs.append(pe.read_jobs); drain_piece_bufs(pe, bufs); } if (!bufs.empty()) free_multiple_buffers(&bufs[0], bufs.size()); // clear lru lists for (int i = 0; i < cached_piece_entry::num_lrus; ++i) m_lru[i].get_all(); // it's not ok to erase pieces with a refcount > 0 // since we're cancelling all jobs though, it shouldn't be too bad // to let the jobs already running complete. for (cache_t::iterator i = m_pieces.begin(); i != m_pieces.end();) { if (i->refcount == 0 && i->piece_refcount == 0) { i = m_pieces.erase(i); } else { ++i; } } } void block_cache::move_to_ghost(cached_piece_entry* pe) { TORRENT_PIECE_ASSERT(pe->refcount == 0, pe); TORRENT_PIECE_ASSERT(pe->piece_refcount == 0, pe); TORRENT_PIECE_ASSERT(pe->num_blocks == 0, pe); TORRENT_PIECE_ASSERT(pe->in_use, pe); if (pe->cache_state == cached_piece_entry::volatile_read_lru) { erase_piece(pe); return; } TORRENT_PIECE_ASSERT(pe->cache_state == cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); // if the piece is in L1 or L2, move it into the ghost list // i.e. recently evicted if (pe->cache_state != cached_piece_entry::read_lru1 && pe->cache_state != cached_piece_entry::read_lru2) return; // if the ghost list is growing too big, remove the oldest entry linked_list* ghost_list = &m_lru[pe->cache_state + 1]; while (ghost_list->size() >= m_ghost_size) { cached_piece_entry* p = static_cast(ghost_list->front()); TORRENT_PIECE_ASSERT(p != pe, p); TORRENT_PIECE_ASSERT(p->num_blocks == 0, p); TORRENT_PIECE_ASSERT(p->refcount == 0, p); TORRENT_PIECE_ASSERT(p->piece_refcount == 0, p); erase_piece(p); } m_lru[pe->cache_state].erase(pe); pe->cache_state += 1; ghost_list->push_back(pe); } int block_cache::pad_job(disk_io_job const* j, int blocks_in_piece , int read_ahead) const { int block_offset = j->d.io.offset & (block_size()-1); int start = j->d.io.offset / block_size(); int end = block_offset > 0 && (read_ahead > block_size() - block_offset) ? start + 2 : start + 1; // take the read-ahead into account // make sure to not overflow in this case if (read_ahead == INT_MAX) end = blocks_in_piece; else end = (std::min)(blocks_in_piece, (std::max)(start + read_ahead, end)); return end - start; } void block_cache::insert_blocks(cached_piece_entry* pe, int block, file::iovec_t *iov , int iov_len, disk_io_job* j, int flags) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; #endif TORRENT_ASSERT(pe); TORRENT_ASSERT(pe->in_use); TORRENT_PIECE_ASSERT(iov_len > 0, pe); cache_hit(pe, j->requester, (j->flags & disk_io_job::volatile_read) != 0); TORRENT_ASSERT(pe->in_use); for (int i = 0; i < iov_len; ++i, ++block) { // each iovec buffer has to be the size of a block (or the size of the last block) TORRENT_PIECE_ASSERT(iov[i].iov_len == (std::min)(block_size() , pe->storage->files()->piece_size(pe->piece) - block * block_size()), pe); // no NULL pointers allowed TORRENT_ASSERT(iov[i].iov_base); #ifdef TORRENT_DEBUG_BUFFERS TORRENT_PIECE_ASSERT(is_disk_buffer(static_cast(iov[i].iov_base)), pe); #endif if (pe->blocks[block].buf && (flags & blocks_inc_refcount)) { inc_block_refcount(pe, block, ref_reading); } // either free the block or insert it. Never replace a block if (pe->blocks[block].buf) { free_buffer(static_cast(iov[i].iov_base)); } else { pe->blocks[block].buf = static_cast(iov[i].iov_base); TORRENT_PIECE_ASSERT(iov[i].iov_base != NULL, pe); TORRENT_PIECE_ASSERT(pe->blocks[block].dirty == false, pe); ++pe->num_blocks; ++m_read_cache_size; if (j->flags & disk_io_job::volatile_read) ++m_volatile_size; if (flags & blocks_inc_refcount) { bool ret = inc_block_refcount(pe, block, ref_reading); TORRENT_UNUSED(ret); // suppress warning TORRENT_ASSERT(ret); } } TORRENT_ASSERT(pe->blocks[block].buf != NULL); } TORRENT_PIECE_ASSERT(pe->cache_state != cached_piece_entry::read_lru1_ghost, pe); TORRENT_PIECE_ASSERT(pe->cache_state != cached_piece_entry::read_lru2_ghost, pe); } // return false if the memory was purged bool block_cache::inc_block_refcount(cached_piece_entry* pe, int block, int reason) { TORRENT_PIECE_ASSERT(pe->in_use, pe); TORRENT_PIECE_ASSERT(block < pe->blocks_in_piece, pe); TORRENT_PIECE_ASSERT(block >= 0, pe); if (pe->blocks[block].buf == NULL) return false; TORRENT_PIECE_ASSERT(pe->blocks[block].refcount < cached_block_entry::max_refcount, pe); if (pe->blocks[block].refcount == 0) { ++pe->pinned; ++m_pinned_blocks; } ++pe->blocks[block].refcount; ++pe->refcount; #if TORRENT_USE_ASSERTS switch (reason) { case ref_hashing: ++pe->blocks[block].hashing_count; break; case ref_reading: ++pe->blocks[block].reading_count; break; case ref_flushing: ++pe->blocks[block].flushing_count; break; }; TORRENT_ASSERT(pe->blocks[block].refcount >= pe->blocks[block].hashing_count + pe->blocks[block].reading_count + pe->blocks[block].flushing_count); #else TORRENT_UNUSED(reason); #endif return true; } void block_cache::dec_block_refcount(cached_piece_entry* pe, int block, int reason) { TORRENT_PIECE_ASSERT(pe->in_use, pe); TORRENT_PIECE_ASSERT(block < pe->blocks_in_piece, pe); TORRENT_PIECE_ASSERT(block >= 0, pe); TORRENT_PIECE_ASSERT(pe->blocks[block].buf != NULL, pe); TORRENT_PIECE_ASSERT(pe->blocks[block].refcount > 0, pe); --pe->blocks[block].refcount; TORRENT_PIECE_ASSERT(pe->refcount > 0, pe); --pe->refcount; if (pe->blocks[block].refcount == 0) { TORRENT_PIECE_ASSERT(pe->pinned > 0, pe); --pe->pinned; TORRENT_PIECE_ASSERT(m_pinned_blocks > 0, pe); --m_pinned_blocks; } #if TORRENT_USE_ASSERTS switch (reason) { case ref_hashing: --pe->blocks[block].hashing_count; break; case ref_reading: --pe->blocks[block].reading_count; break; case ref_flushing: --pe->blocks[block].flushing_count; break; }; TORRENT_PIECE_ASSERT(pe->blocks[block].refcount >= pe->blocks[block].hashing_count + pe->blocks[block].reading_count + pe->blocks[block].flushing_count, pe); #else TORRENT_UNUSED(reason); #endif } void block_cache::abort_dirty(cached_piece_entry* pe) { INVARIANT_CHECK; TORRENT_PIECE_ASSERT(pe->in_use, pe); char** to_delete = TORRENT_ALLOCA(char*, pe->blocks_in_piece); int num_to_delete = 0; for (int i = 0; i < pe->blocks_in_piece; ++i) { if (!pe->blocks[i].dirty || pe->blocks[i].refcount > 0 || pe->blocks[i].buf == NULL) continue; TORRENT_PIECE_ASSERT(!pe->blocks[i].pending, pe); TORRENT_PIECE_ASSERT(pe->blocks[i].dirty, pe); to_delete[num_to_delete++] = pe->blocks[i].buf; pe->blocks[i].buf = NULL; pe->blocks[i].dirty = false; TORRENT_PIECE_ASSERT(pe->num_blocks > 0, pe); --pe->num_blocks; TORRENT_PIECE_ASSERT(m_write_cache_size > 0, pe); --m_write_cache_size; TORRENT_PIECE_ASSERT(pe->num_dirty > 0, pe); --pe->num_dirty; } if (num_to_delete) free_multiple_buffers(to_delete, num_to_delete); update_cache_state(pe); } // frees all buffers associated with this piece. May only // be called for pieces with a refcount of 0 void block_cache::free_piece(cached_piece_entry* pe) { INVARIANT_CHECK; TORRENT_PIECE_ASSERT(pe->in_use, pe); TORRENT_PIECE_ASSERT(pe->refcount == 0, pe); TORRENT_PIECE_ASSERT(pe->piece_refcount == 0, pe); TORRENT_PIECE_ASSERT(pe->outstanding_read == 0, pe); // build a vector of all the buffers we need to free // and free them all in one go char** to_delete = TORRENT_ALLOCA(char*, pe->blocks_in_piece); int num_to_delete = 0; int removed_clean = 0; for (int i = 0; i < pe->blocks_in_piece; ++i) { if (pe->blocks[i].buf == 0) continue; TORRENT_PIECE_ASSERT(pe->blocks[i].pending == false, pe); TORRENT_PIECE_ASSERT(pe->blocks[i].refcount == 0, pe); TORRENT_PIECE_ASSERT(num_to_delete < pe->blocks_in_piece, pe); to_delete[num_to_delete++] = pe->blocks[i].buf; pe->blocks[i].buf = NULL; TORRENT_PIECE_ASSERT(pe->num_blocks > 0, pe); --pe->num_blocks; if (pe->blocks[i].dirty) { TORRENT_PIECE_ASSERT(m_write_cache_size > 0, pe); --m_write_cache_size; TORRENT_PIECE_ASSERT(pe->num_dirty > 0, pe); --pe->num_dirty; } else { ++removed_clean; } } TORRENT_PIECE_ASSERT(m_read_cache_size >= removed_clean, pe); m_read_cache_size -= removed_clean; if (pe->cache_state == cached_piece_entry::volatile_read_lru) { m_volatile_size -= num_to_delete; } if (num_to_delete) free_multiple_buffers(to_delete, num_to_delete); update_cache_state(pe); } int block_cache::drain_piece_bufs(cached_piece_entry& p, std::vector& buf) { int const piece_size = p.storage->files()->piece_size(p.piece); int const blocks_in_piece = (piece_size + block_size() - 1) / block_size(); int ret = 0; TORRENT_PIECE_ASSERT(p.in_use, &p); int removed_clean = 0; for (int i = 0; i < blocks_in_piece; ++i) { if (p.blocks[i].buf == 0) continue; TORRENT_PIECE_ASSERT(p.blocks[i].refcount == 0, &p); buf.push_back(p.blocks[i].buf); ++ret; p.blocks[i].buf = NULL; TORRENT_PIECE_ASSERT(p.num_blocks > 0, &p); --p.num_blocks; if (p.blocks[i].dirty) { TORRENT_ASSERT(m_write_cache_size > 0); --m_write_cache_size; TORRENT_PIECE_ASSERT(p.num_dirty > 0, &p); --p.num_dirty; } else { ++removed_clean; } } TORRENT_ASSERT(m_read_cache_size >= removed_clean); m_read_cache_size -= removed_clean; if (p.cache_state == cached_piece_entry::volatile_read_lru) { m_volatile_size -= removed_clean; } update_cache_state(&p); return ret; } void block_cache::update_stats_counters(counters& c) const { c.set_value(counters::write_cache_blocks, m_write_cache_size); c.set_value(counters::read_cache_blocks, m_read_cache_size); c.set_value(counters::pinned_blocks, m_pinned_blocks); c.set_value(counters::arc_mru_size, m_lru[cached_piece_entry::read_lru1].size()); c.set_value(counters::arc_mru_ghost_size, m_lru[cached_piece_entry::read_lru1_ghost].size()); c.set_value(counters::arc_mfu_size, m_lru[cached_piece_entry::read_lru2].size()); c.set_value(counters::arc_mfu_ghost_size, m_lru[cached_piece_entry::read_lru2_ghost].size()); c.set_value(counters::arc_write_size, m_lru[cached_piece_entry::write_lru].size()); c.set_value(counters::arc_volatile_size, m_lru[cached_piece_entry::volatile_read_lru].size()); } #ifndef TORRENT_NO_DEPRECATE void block_cache::get_stats(cache_status* ret) const { ret->write_cache_size = m_write_cache_size; ret->read_cache_size = m_read_cache_size; ret->pinned_blocks = m_pinned_blocks; ret->cache_size = m_read_cache_size + m_write_cache_size; ret->arc_mru_size = m_lru[cached_piece_entry::read_lru1].size(); ret->arc_mru_ghost_size = m_lru[cached_piece_entry::read_lru1_ghost].size(); ret->arc_mfu_size = m_lru[cached_piece_entry::read_lru2].size(); ret->arc_mfu_ghost_size = m_lru[cached_piece_entry::read_lru2_ghost].size(); ret->arc_write_size = m_lru[cached_piece_entry::write_lru].size(); ret->arc_volatile_size = m_lru[cached_piece_entry::volatile_read_lru].size(); } #endif void block_cache::set_settings(aux::session_settings const& sett, error_code& ec) { // the ghost size is the number of pieces to keep track of // after they are evicted. Since cache_size is blocks, the // assumption is that there are about 128 blocks per piece, // and there are two ghost lists, so divide by 2. m_ghost_size = (std::max)(8, sett.get_int(settings_pack::cache_size) / (std::max)(sett.get_int(settings_pack::read_cache_line_size), 4) / 2); m_max_volatile_blocks = sett.get_int(settings_pack::cache_size_volatile); disk_buffer_pool::set_settings(sett, ec); } #if TORRENT_USE_INVARIANT_CHECKS void block_cache::check_invariant() const { int cached_write_blocks = 0; int cached_read_blocks = 0; int num_pinned = 0; std::set storages; for (int i = 0; i < cached_piece_entry::num_lrus; ++i) { time_point timeout = min_time(); for (list_iterator p = m_lru[i].iterate(); p.get(); p.next()) { cached_piece_entry* pe = p.get(); TORRENT_PIECE_ASSERT(pe->cache_state == i, pe); if (pe->num_dirty > 0) TORRENT_PIECE_ASSERT(i == cached_piece_entry::write_lru, pe); // if (i == cached_piece_entry::write_lru) // TORRENT_ASSERT(pe->num_dirty > 0); for (tailqueue_iterator j = pe->jobs.iterate(); j.get(); j.next()) { disk_io_job const* job = static_cast(j.get()); TORRENT_PIECE_ASSERT(job->piece == pe->piece, pe); TORRENT_PIECE_ASSERT(job->in_use, pe); TORRENT_PIECE_ASSERT(!job->callback_called, pe); } if (i != cached_piece_entry::read_lru1_ghost && i != cached_piece_entry::read_lru2_ghost) { TORRENT_PIECE_ASSERT(pe->expire >= timeout, pe); timeout = pe->expire; TORRENT_PIECE_ASSERT(pe->in_storage, pe); } else { // pieces in the ghost lists should never have any blocks TORRENT_PIECE_ASSERT(pe->num_blocks == 0, pe); } // pieces in the ghost list are still in the storage's list of pieces, // because we need to be able to evict them when stopping a torrent TORRENT_PIECE_ASSERT(pe->storage->has_piece(pe), pe); storages.insert(pe->storage.get()); } } for (std::set::iterator i = storages.begin() , end(storages.end()); i != end; ++i) { for (boost::unordered_set::iterator j = (*i)->cached_pieces().begin() , end2((*i)->cached_pieces().end()); j != end2; ++j) { cached_piece_entry* pe = *j; TORRENT_PIECE_ASSERT(pe->storage.get() == *i, pe); } } boost::unordered_set buffers; for (iterator i = m_pieces.begin(), end(m_pieces.end()); i != end; ++i) { cached_piece_entry const& p = *i; TORRENT_PIECE_ASSERT(p.blocks, &p); TORRENT_PIECE_ASSERT(p.storage, &p); int num_blocks = 0; int num_dirty = 0; int num_pending = 0; int num_refcount = 0; TORRENT_ASSERT(p.storage->has_piece(&p)); for (int k = 0; k < p.blocks_in_piece; ++k) { if (p.blocks[k].buf) { #if !defined TORRENT_DISABLE_POOL_ALLOCATOR && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_ASSERT(is_disk_buffer(p.blocks[k].buf), &p); // make sure we don't have the same buffer // in the cache twice TORRENT_PIECE_ASSERT(buffers.count(p.blocks[k].buf) == 0, &p); buffers.insert(p.blocks[k].buf); #endif ++num_blocks; if (p.blocks[k].dirty) { ++num_dirty; ++cached_write_blocks; } else { ++cached_read_blocks; } if (p.blocks[k].pending) ++num_pending; if (p.blocks[k].refcount > 0) ++num_pinned; TORRENT_PIECE_ASSERT(p.blocks[k].refcount >= p.blocks[k].hashing_count + p.blocks[k].reading_count + p.blocks[k].flushing_count, &p); } else { TORRENT_PIECE_ASSERT(!p.blocks[k].dirty, &p); TORRENT_PIECE_ASSERT(!p.blocks[k].pending, &p); TORRENT_PIECE_ASSERT(p.blocks[k].refcount == 0, &p); } num_refcount += p.blocks[k].refcount; } TORRENT_PIECE_ASSERT(num_blocks == p.num_blocks, &p); TORRENT_PIECE_ASSERT(num_pending <= p.refcount, &p); TORRENT_PIECE_ASSERT(num_refcount == p.refcount, &p); TORRENT_PIECE_ASSERT(num_dirty == p.num_dirty, &p); } TORRENT_ASSERT(m_read_cache_size == cached_read_blocks); TORRENT_ASSERT(m_write_cache_size == cached_write_blocks); TORRENT_ASSERT(m_pinned_blocks == num_pinned); TORRENT_ASSERT(m_write_cache_size + m_read_cache_size <= in_use()); } #endif // TODO: 2 turn these return values into enums // returns // -1: block not in cache // -2: out of memory int block_cache::copy_from_piece(cached_piece_entry* const pe , disk_io_job* const j , bool const expect_no_fail) { INVARIANT_CHECK; TORRENT_UNUSED(expect_no_fail); TORRENT_PIECE_ASSERT(j->buffer.disk_block == 0, pe); TORRENT_PIECE_ASSERT(pe->in_use, pe); // copy from the cache and update the last use timestamp int block = j->d.io.offset / block_size(); int block_offset = j->d.io.offset & (block_size()-1); int buffer_offset = 0; int size = j->d.io.buffer_size; int const blocks_to_read = block_offset > 0 && (size > block_size() - block_offset) ? 2 : 1; TORRENT_PIECE_ASSERT(size <= block_size(), pe); int const start_block = block; #if TORRENT_USE_ASSERTS int const piece_size = j->storage->files()->piece_size(j->piece); int const blocks_in_piece = (piece_size + block_size() - 1) / block_size(); TORRENT_PIECE_ASSERT(start_block < blocks_in_piece, pe); #endif // if there's no buffer, we don't have this block in // the cache, and we're not currently reading it in either // since it's not pending if (inc_block_refcount(pe, start_block, ref_reading) == false) { TORRENT_ASSERT(!expect_no_fail); return -1; } // if block_offset > 0, we need to read two blocks, and then // copy parts of both, because it's not aligned to the block // boundaries if (blocks_to_read == 1 && (j->flags & disk_io_job::force_copy) == 0) { // special case for block aligned request // don't actually copy the buffer, just reference // the existing block. Which means we don't want to decrement the // refcount, we're handing the ownership of the reference to the calling // thread. cached_block_entry& bl = pe->blocks[start_block]; // make sure it didn't wrap TORRENT_PIECE_ASSERT(pe->refcount > 0, pe); j->d.io.ref.storage = j->storage.get(); j->d.io.ref.piece = pe->piece; j->d.io.ref.block = start_block; j->buffer.disk_block = bl.buf + (j->d.io.offset & (block_size()-1)); ++m_send_buffer_blocks; return j->d.io.buffer_size; } // if we don't have the second block, it's a cache miss if (blocks_to_read == 2 && inc_block_refcount(pe, start_block + 1, ref_reading) == false) { TORRENT_ASSERT(!expect_no_fail); dec_block_refcount(pe, start_block, ref_reading); maybe_free_piece(pe); return -1; } j->buffer.disk_block = allocate_buffer("send buffer"); if (j->buffer.disk_block == 0) return -2; while (size > 0) { TORRENT_PIECE_ASSERT(pe->blocks[block].buf, pe); int to_copy = (std::min)(block_size() - block_offset, size); std::memcpy(j->buffer.disk_block + buffer_offset , pe->blocks[block].buf + block_offset , to_copy); size -= to_copy; block_offset = 0; buffer_offset += to_copy; ++block; } // we incremented the refcount for both of these blocks. // now decrement it. // TODO: create a holder for refcounts that automatically decrement dec_block_refcount(pe, start_block, ref_reading); if (blocks_to_read == 2) dec_block_refcount(pe, start_block + 1, ref_reading); maybe_free_piece(pe); return j->d.io.buffer_size; } void block_cache::reclaim_block(block_cache_reference const& ref) { cached_piece_entry* pe = find_piece(ref); TORRENT_ASSERT(pe); if (pe == NULL) return; TORRENT_PIECE_ASSERT(pe->in_use, pe); TORRENT_PIECE_ASSERT(pe->blocks[ref.block].buf, pe); dec_block_refcount(pe, ref.block, block_cache::ref_reading); TORRENT_PIECE_ASSERT(m_send_buffer_blocks > 0, pe); --m_send_buffer_blocks; maybe_free_piece(pe); } bool block_cache::maybe_free_piece(cached_piece_entry* pe) { if (!pe->ok_to_evict() || !pe->marked_for_eviction || !pe->jobs.empty()) return false; DLOG(stderr, "[%p] block_cache maybe_free_piece " "piece: %d refcount: %d marked_for_eviction: %d\n" , static_cast(this) , int(pe->piece), int(pe->refcount), int(pe->marked_for_eviction)); tailqueue jobs; bool removed = evict_piece(pe, jobs , pe->marked_for_deletion ? disallow_ghost : allow_ghost); TORRENT_UNUSED(removed); // suppress warning TORRENT_PIECE_ASSERT(removed, pe); TORRENT_PIECE_ASSERT(jobs.empty(), pe); return true; } cached_piece_entry* block_cache::find_piece(block_cache_reference const& ref) { return find_piece(static_cast(ref.storage), ref.piece); } cached_piece_entry* block_cache::find_piece(disk_io_job const* j) { return find_piece(j->storage.get(), j->piece); } cached_piece_entry* block_cache::find_piece(piece_manager* st, int piece) { cached_piece_entry model; model.storage = st->shared_from_this(); model.piece = piece; iterator i = m_pieces.find(model); TORRENT_ASSERT(i == m_pieces.end() || (i->storage.get() == st && i->piece == piece)); if (i == m_pieces.end()) return 0; TORRENT_PIECE_ASSERT(i->in_use, &*i); #if TORRENT_USE_ASSERTS for (tailqueue_iterator j = i->jobs.iterate(); j.get(); j.next()) { disk_io_job const* job = static_cast(j.get()); TORRENT_PIECE_ASSERT(job->piece == piece, &*i); } #endif return const_cast(&*i); } } libtorrent-rasterbar-1.1.13/src/bloom_filter.cpp000066400000000000000000000053171351156116000217210ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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/bloom_filter.hpp" namespace libtorrent { bool has_bits(boost::uint8_t const* k, boost::uint8_t const* bits, int len) { boost::uint32_t idx1 = boost::uint32_t(k[0]) | (boost::uint32_t(k[1]) << 8); boost::uint32_t idx2 = boost::uint32_t(k[2]) | (boost::uint32_t(k[3]) << 8); idx1 %= len * 8; idx2 %= len * 8; return (bits[idx1/8] & (1 << (idx1 & 7))) != 0 && (bits[idx2/8] & (1 << (idx2 & 7))) != 0; } void set_bits(boost::uint8_t const* k, boost::uint8_t* bits, int len) { boost::uint32_t idx1 = boost::uint32_t(k[0]) | (boost::uint32_t(k[1]) << 8); boost::uint32_t idx2 = boost::uint32_t(k[2]) | (boost::uint32_t(k[3]) << 8); idx1 %= len * 8; idx2 %= len * 8; bits[idx1/8] |= (1 << (idx1 & 7)); bits[idx2/8] |= (1 << (idx2 & 7)); } int count_zero_bits(boost::uint8_t const* bits, int len) { // number of bits _not_ set in a nibble boost::uint8_t bitcount[16] = { // 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, // 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0 }; int ret = 0; for (int i = 0; i < len; ++i) { ret += bitcount[bits[i] & 0xf]; ret += bitcount[(bits[i] >> 4) & 0xf]; } return ret; } } libtorrent-rasterbar-1.1.13/src/broadcast_socket.cpp000066400000000000000000000302511351156116000225510ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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 #include "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #if defined TORRENT_OS2 #include #endif #include #include #include #ifdef TORRENT_WINDOWS #include // for if_nametoindex #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/enum_net.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/assert.hpp" #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/debug.hpp" #endif #ifdef TORRENT_DEBUG #include "libtorrent/socket_io.hpp" #endif namespace libtorrent { bool is_ip_address(char const* host) { error_code ec; address::from_string(host, ec); return !ec; } bool is_local(address const& a) { TORRENT_TRY { #if TORRENT_USE_IPV6 if (a.is_v6()) { return a.to_v6().is_loopback() || a.to_v6().is_link_local() || a.to_v6().is_multicast_link_local(); } #endif address_v4 a4 = a.to_v4(); unsigned long ip = a4.to_ulong(); return ((ip & 0xff000000) == 0x0a000000 // 10.x.x.x || (ip & 0xfff00000) == 0xac100000 // 172.16.x.x || (ip & 0xffff0000) == 0xc0a80000 // 192.168.x.x || (ip & 0xffff0000) == 0xa9fe0000 // 169.254.x.x || (ip & 0xff000000) == 0x7f000000); // 127.x.x.x } TORRENT_CATCH(std::exception&) { return false; } } bool is_loopback(address const& addr) { #if TORRENT_USE_IPV6 TORRENT_TRY { if (addr.is_v4()) return addr.to_v4() == address_v4::loopback(); else return addr.to_v6() == address_v6::loopback(); } TORRENT_CATCH(std::exception&) { return false; } #else return addr.to_v4() == address_v4::loopback(); #endif } bool is_multicast(address const& addr) { #if TORRENT_USE_IPV6 TORRENT_TRY { if (addr.is_v4()) return addr.to_v4().is_multicast(); else return addr.to_v6().is_multicast(); } TORRENT_CATCH(std::exception&) { return false; } #else return addr.to_v4().is_multicast(); #endif } bool is_any(address const& addr) { TORRENT_TRY { #if TORRENT_USE_IPV6 if (addr.is_v4()) return addr.to_v4() == address_v4::any(); else if (addr.to_v6().is_v4_mapped()) return (addr.to_v6().to_v4() == address_v4::any()); else return addr.to_v6() == address_v6::any(); #else return addr.to_v4() == address_v4::any(); #endif } TORRENT_CATCH(std::exception&) { return false; } } bool is_teredo(address const& addr) { #if TORRENT_USE_IPV6 TORRENT_TRY { if (!addr.is_v6()) return false; boost::uint8_t teredo_prefix[] = {0x20, 0x01, 0, 0}; address_v6::bytes_type b = addr.to_v6().to_bytes(); return memcmp(&b[0], teredo_prefix, 4) == 0; } TORRENT_CATCH(std::exception&) { return false; } #else TORRENT_UNUSED(addr); return false; #endif } bool supports_ipv6() { #if !TORRENT_USE_IPV6 return false; #elif defined TORRENT_BUILD_SIMULATOR return true; #elif defined TORRENT_WINDOWS TORRENT_TRY { error_code ec; address::from_string("::1", ec); return !ec; } TORRENT_CATCH(std::exception&) { return false; } #else io_service ios; tcp::socket test(ios); error_code ec; test.open(tcp::v6(), ec); if (ec) return false; test.bind(tcp::endpoint(address_v6::from_string("::1"), 0), ec); return !bool(ec); #endif } // count the length of the common bit prefix int common_bits(unsigned char const* b1 , unsigned char const* b2, int n) { for (int i = 0; i < n; ++i, ++b1, ++b2) { unsigned char a = *b1 ^ *b2; if (a == 0) continue; int ret = i * 8 + 8; for (; a > 0; a >>= 1) --ret; return ret; } return n * 8; } // returns the number of bits in that differ from the right // between the addresses. The larger number, the further apart // the IPs are int cidr_distance(address const& a1, address const& a2) { #if TORRENT_USE_IPV6 if (a1.is_v4() && a2.is_v4()) { #endif // both are v4 address_v4::bytes_type b1 = a1.to_v4().to_bytes(); address_v4::bytes_type b2 = a2.to_v4().to_bytes(); return address_v4::bytes_type().size() * 8 - common_bits(b1.data(), b2.data(), b1.size()); #if TORRENT_USE_IPV6 } address_v6::bytes_type b1; address_v6::bytes_type b2; if (a1.is_v4()) b1 = address_v6::v4_mapped(a1.to_v4()).to_bytes(); else b1 = a1.to_v6().to_bytes(); if (a2.is_v4()) b2 = address_v6::v4_mapped(a2.to_v4()).to_bytes(); else b2 = a2.to_v6().to_bytes(); return address_v6::bytes_type().size() * 8 - common_bits(b1.data(), b2.data(), b1.size()); #endif } broadcast_socket::broadcast_socket( udp::endpoint const& multicast_endpoint) : m_multicast_endpoint(multicast_endpoint) , m_outstanding_operations(0) , m_abort(false) { TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address())); using namespace boost::asio::ip::multicast; } void broadcast_socket::open(receive_handler_t const& handler , io_service& ios, error_code& ec, bool loopback) { m_on_receive = handler; std::vector interfaces = enum_net_interfaces(ios, ec); #if TORRENT_USE_IPV6 if (m_multicast_endpoint.address().is_v6()) open_multicast_socket(ios, address_v6::any(), loopback, ec); else #endif open_multicast_socket(ios, address_v4::any(), loopback, ec); for (std::vector::const_iterator i = interfaces.begin() , end(interfaces.end()); i != end; ++i) { // only multicast on compatible networks if (i->interface_address.is_v4() != m_multicast_endpoint.address().is_v4()) continue; // ignore any loopback interface if (!loopback && is_loopback(i->interface_address)) continue; ec = error_code(); // if_nametoindex was introduced in vista #if TORRENT_USE_IPV6 \ && (!defined TORRENT_WINDOWS || _WIN32_WINNT >= 0x0600) \ && !defined TORRENT_MINGW if (i->interface_address.is_v6() && i->interface_address.to_v6().is_link_local()) { address_v6 addr6 = i->interface_address.to_v6(); addr6.scope_id(if_nametoindex(i->name)); open_multicast_socket(ios, addr6, loopback, ec); address_v4 const& mask = i->netmask.is_v4() ? i->netmask.to_v4() : address_v4(); open_unicast_socket(ios, addr6, mask); continue; } #endif open_multicast_socket(ios, i->interface_address, loopback, ec); open_unicast_socket(ios, i->interface_address , i->netmask.is_v4() ? i->netmask.to_v4() : address_v4()); } } void broadcast_socket::open_multicast_socket(io_service& ios , address const& addr, bool loopback, error_code& ec) { using namespace boost::asio::ip::multicast; boost::shared_ptr s(new udp::socket(ios)); s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec); if (ec) return; s->set_option(udp::socket::reuse_address(true), ec); if (ec) return; s->bind(udp::endpoint(addr, m_multicast_endpoint.port()), ec); if (ec) return; s->set_option(join_group(m_multicast_endpoint.address()), ec); if (ec) return; s->set_option(hops(255), ec); if (ec) return; s->set_option(enable_loopback(loopback), ec); if (ec) return; m_sockets.push_back(socket_entry(s)); socket_entry& se = m_sockets.back(); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("broadcast_socket::on_receive"); #endif s->async_receive_from(boost::asio::buffer(se.buffer, sizeof(se.buffer)) , se.remote, boost::bind(&broadcast_socket::on_receive, this, &se, _1, _2)); ++m_outstanding_operations; } void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr , address_v4 const& mask) { using namespace boost::asio::ip::multicast; error_code ec; boost::shared_ptr s(new udp::socket(ios)); s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec); if (ec) return; s->bind(udp::endpoint(addr, 0), ec); if (ec) return; m_unicast_sockets.push_back(socket_entry(s, mask)); socket_entry& se = m_unicast_sockets.back(); // allow sending broadcast messages boost::asio::socket_base::broadcast option(true); s->set_option(option, ec); if (!ec) se.broadcast = true; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("broadcast_socket::on_receive"); #endif s->async_receive_from(boost::asio::buffer(se.buffer, sizeof(se.buffer)) , se.remote, boost::bind(&broadcast_socket::on_receive, this, &se, _1, _2)); ++m_outstanding_operations; } void broadcast_socket::send(char const* buffer, int size, error_code& ec, int flags) { bool all_fail = true; error_code e; for (std::list::iterator i = m_unicast_sockets.begin() , end(m_unicast_sockets.end()); i != end; ++i) { if (!i->socket) continue; i->socket->send_to(boost::asio::buffer(buffer, size), m_multicast_endpoint, 0, e); // if the user specified the broadcast flag, send one to the broadcast // address as well if ((flags & broadcast_socket::broadcast) && i->can_broadcast()) i->socket->send_to(boost::asio::buffer(buffer, size) , udp::endpoint(i->broadcast_address(), m_multicast_endpoint.port()), 0, e); if (e) { i->socket->close(e); i->socket.reset(); } else { all_fail = false; } } for (std::list::iterator i = m_sockets.begin() , end(m_sockets.end()); i != end; ++i) { if (!i->socket) continue; i->socket->send_to(boost::asio::buffer(buffer, size), m_multicast_endpoint, 0, e); if (e) { i->socket->close(e); i->socket.reset(); } else { all_fail = false; } } if (all_fail) ec = e; } void broadcast_socket::on_receive(socket_entry* s, error_code const& ec , std::size_t bytes_transferred) { #if defined TORRENT_ASIO_DEBUGGING complete_async("broadcast_socket::on_receive"); #endif TORRENT_ASSERT(m_outstanding_operations > 0); --m_outstanding_operations; if (ec || bytes_transferred == 0 || !m_on_receive) { maybe_abort(); return; } m_on_receive(s->remote, s->buffer, bytes_transferred); if (maybe_abort()) return; if (!s->socket) return; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("broadcast_socket::on_receive"); #endif s->socket->async_receive_from(boost::asio::buffer(s->buffer, sizeof(s->buffer)) , s->remote, boost::bind(&broadcast_socket::on_receive, this, s, _1, _2)); ++m_outstanding_operations; } bool broadcast_socket::maybe_abort() { bool ret = m_abort; if (m_abort && m_outstanding_operations == 0) { // it's important that m_on_receive is cleared // before the object is destructed, since it may // hold a reference to ourself, which would otherwise // cause an infinite recursion destructing the objects receive_handler_t().swap(m_on_receive); } return ret; } void broadcast_socket::close() { std::for_each(m_sockets.begin(), m_sockets.end(), boost::bind(&socket_entry::close, _1)); std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), boost::bind(&socket_entry::close, _1)); m_abort = true; maybe_abort(); } } libtorrent-rasterbar-1.1.13/src/bt_peer_connection.cpp000066400000000000000000003101701351156116000230770ustar00rootroot00000000000000/* Copyright (c) 2003-2018, Arvid Norberg Copyright (c) 2007-2018, Arvid Norberg, Un Shyam 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/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #ifdef TORRENT_USE_OPENSSL #include // autp_ptr #endif #include "libtorrent/aux_/disable_warnings_pop.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/socket_io.hpp" #include "libtorrent/version.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/aux_/session_interface.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/random.hpp" #include "libtorrent/alloca.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/performance_counters.hpp" // for counters #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) #include "libtorrent/pe_crypto.hpp" #include "libtorrent/hasher.hpp" #endif using boost::shared_ptr; namespace libtorrent { 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, // FAST extension messages &bt_peer_connection::on_suggest_piece, &bt_peer_connection::on_have_all, &bt_peer_connection::on_have_none, &bt_peer_connection::on_reject_request, &bt_peer_connection::on_allowed_fast, #ifndef TORRENT_DISABLE_EXTENSIONS 0, 0, &bt_peer_connection::on_extended #endif }; bt_peer_connection::bt_peer_connection(peer_connection_args const& pack) : peer_connection(pack) , m_state(read_protocol_identifier) , m_supports_extensions(false) , m_supports_dht_port(false) , m_supports_fast(false) , m_sent_bitfield(false) , m_sent_handshake(false) , m_sent_allowed_fast(false) #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) , m_encrypted(false) , m_rc4_encrypted(false) , m_recv_buffer(peer_connection::m_recv_buffer) #endif , m_our_peer_id(generate_peer_id(*pack.sett)) #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) , m_sync_bytes_read(0) #endif #ifndef TORRENT_DISABLE_EXTENSIONS , m_upload_only_id(0) , m_holepunch_id(0) #endif #ifndef TORRENT_DISABLE_EXTENSIONS , m_dont_have_id(0) , m_share_mode_id(0) #endif #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS , m_in_constructor(true) #endif { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CONSTRUCT", "bt_peer_connection"); #endif #if TORRENT_USE_ASSERTS m_in_constructor = false; #endif #ifndef TORRENT_DISABLE_EXTENSIONS memset(m_reserved_bits, 0, sizeof(m_reserved_bits)); #endif } void bt_peer_connection::start() { peer_connection::start(); // start in the state where we are trying to read the // handshake from the other side m_recv_buffer.reset(20); setup_receive(); } bt_peer_connection::~bt_peer_connection() { } #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) void bt_peer_connection::switch_send_crypto(boost::shared_ptr crypto) { if (m_enc_handler.switch_send_crypto(crypto, send_buffer_size() - get_send_barrier())) set_send_barrier(send_buffer_size()); } void bt_peer_connection::switch_recv_crypto(boost::shared_ptr crypto) { m_enc_handler.switch_recv_crypto(crypto, m_recv_buffer); } #endif void bt_peer_connection::on_connected() { if (is_disconnecting()) return; boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); if (t->graceful_pause()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ON_CONNECTED", "graceful-paused"); #endif disconnect(error_code(errors::torrent_paused), op_bittorrent); return; } // make sure are much as possible of the response ends up in the same // packet, or at least back-to-back packets cork c_(*this); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) boost::uint8_t out_policy = m_settings.get_int(settings_pack::out_enc_policy); #ifdef TORRENT_USE_OPENSSL // never try an encrypted connection when already using SSL if (is_ssl(*get_socket())) out_policy = settings_pack::pe_disabled; #endif #ifndef TORRENT_DISABLE_LOGGING static char const* policy_name[] = {"forced", "enabled", "disabled"}; TORRENT_ASSERT(out_policy < sizeof(policy_name)/sizeof(policy_name[0])); peer_log(peer_log_alert::info, "ENCRYPTION" , "outgoing encryption policy: %s", policy_name[out_policy]); #endif if (out_policy == settings_pack::pe_forced) { write_pe1_2_dhkey(); if (is_disconnecting()) return; m_state = read_pe_dhkey; m_recv_buffer.reset(dh_key_len); setup_receive(); } else if (out_policy == settings_pack::pe_enabled) { TORRENT_ASSERT(peer_info_struct()); torrent_peer* pi = peer_info_struct(); if (pi->pe_support == true) { // toggle encryption support flag, toggled back to // true if encrypted portion of the handshake // completes correctly pi->pe_support = false; // if this fails, we need to reconnect // fast. fast_reconnect(true); write_pe1_2_dhkey(); if (is_disconnecting()) return; m_state = read_pe_dhkey; m_recv_buffer.reset(dh_key_len); setup_receive(); } else // pi->pe_support == false { // toggled back to false if standard handshake // completes correctly (without encryption) pi->pe_support = true; write_handshake(); m_recv_buffer.reset(20); setup_receive(); } } else if (out_policy == settings_pack::pe_disabled) #endif { write_handshake(); // start in the state where we are trying to read the // handshake from the other side m_recv_buffer.reset(20); setup_receive(); } } void bt_peer_connection::on_metadata() { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ON_METADATA"); #endif disconnect_if_redundant(); if (m_disconnecting) return; if (!m_sent_handshake) return; // we haven't gotten far enough on the incoming handshake to be able to // send the bitfield yet if (m_state < read_packet_size) return; // connections that are still in the handshake // will send their bitfield when the handshake // is done #ifndef TORRENT_DISABLE_EXTENSIONS write_upload_only(); #endif if (m_sent_bitfield) return; boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); write_bitfield(); TORRENT_ASSERT(m_sent_bitfield); #ifndef TORRENT_DISABLE_DHT if (m_supports_dht_port && m_ses.has_dht()) write_dht_port(m_ses.external_udp_port()); #endif } void bt_peer_connection::write_dht_port(int listen_port) { INVARIANT_CHECK; TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "DHT_PORT", "%d", listen_port); #endif char msg[] = {0,0,0,3, msg_dht_port, 0, 0}; char* ptr = msg + 5; detail::write_uint16(listen_port, ptr); send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_dht_port); } void bt_peer_connection::write_have_all() { INVARIANT_CHECK; TORRENT_ASSERT(m_sent_handshake); m_sent_bitfield = true; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "HAVE_ALL"); #endif char msg[] = {0,0,0,1, msg_have_all}; send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_have_all); } void bt_peer_connection::write_have_none() { INVARIANT_CHECK; TORRENT_ASSERT(m_sent_handshake); m_sent_bitfield = true; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "HAVE_NONE"); #endif char msg[] = {0,0,0,1, msg_have_none}; send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_have_none); } void bt_peer_connection::write_reject_request(peer_request const& r) { INVARIANT_CHECK; stats_counters().inc_stats_counter(counters::piece_rejects); if (!m_supports_fast) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "REJECT_PIECE" , "piece: %d | s: %d | l: %d", r.piece, r.start, r.length); #endif TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); char msg[] = {0,0,0,13, msg_reject_request,0,0,0,0, 0,0,0,0, 0,0,0,0}; char* ptr = msg + 5; detail::write_int32(r.piece, ptr); // index detail::write_int32(r.start, ptr); // begin detail::write_int32(r.length, ptr); // length send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_reject); } void bt_peer_connection::write_allow_fast(int piece) { INVARIANT_CHECK; if (!m_supports_fast) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "ALLOWED_FAST", "%d", piece); #endif TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); char msg[] = {0,0,0,5, msg_allowed_fast, 0, 0, 0, 0}; char* ptr = msg + 5; detail::write_int32(piece, ptr); send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_allowed_fast); } void bt_peer_connection::write_suggest(int piece) { INVARIANT_CHECK; if (!m_supports_fast) return; TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(t->valid_metadata()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "SUGGEST" , "piece: %d num_peers: %d", piece , t->has_picker() ? t->picker().get_availability(piece) : -1); #endif char msg[] = {0,0,0,5, msg_suggest_piece, 0, 0, 0, 0}; char* ptr = msg + 5; detail::write_int32(piece, ptr); send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_suggest); } void bt_peer_connection::get_specific_peer_info(peer_info& p) const { TORRENT_ASSERT(!associated_torrent().expired()); 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_outgoing()) p.flags |= peer_info::local_connection; #if TORRENT_USE_I2P if (is_i2p(*get_socket())) p.flags |= peer_info::i2p_socket; #endif if (is_utp(*get_socket())) p.flags |= peer_info::utp_socket; if (is_ssl(*get_socket())) p.flags |= peer_info::ssl_socket; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) if (m_encrypted) { p.flags |= m_rc4_encrypted ? peer_info::rc4_encrypted : peer_info::plaintext_encrypted; } #endif if (!is_connecting() && in_handshake()) p.flags |= peer_info::handshake; if (is_connecting()) p.flags |= peer_info::connecting; p.client = m_client_version; p.connection_type = peer_info::standard_bittorrent; } bool bt_peer_connection::in_handshake() const { return m_state < read_packet_size; } #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) namespace { char random_byte() { return random() & 0xff; } } void bt_peer_connection::write_pe1_2_dhkey() { INVARIANT_CHECK; TORRENT_ASSERT(!m_encrypted); TORRENT_ASSERT(!m_rc4_encrypted); TORRENT_ASSERT(!m_dh_key_exchange.get()); TORRENT_ASSERT(!m_sent_handshake); #ifndef TORRENT_DISABLE_LOGGING if (is_outgoing()) peer_log(peer_log_alert::info, "ENCRYPTION", "initiating encrypted handshake"); #endif m_dh_key_exchange.reset(new (std::nothrow) dh_key_exchange); if (!m_dh_key_exchange || !m_dh_key_exchange->good()) { disconnect(errors::no_memory, op_encryption); return; } int pad_size = random() % 512; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION", "pad size: %d", pad_size); #endif char msg[dh_key_len + 512]; char* ptr = msg; int buf_size = dh_key_len + pad_size; memcpy(ptr, m_dh_key_exchange->get_local_key(), dh_key_len); ptr += dh_key_len; std::generate(ptr, ptr + pad_size, random_byte); send_buffer(msg, buf_size); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION", "sent DH key"); #endif } void bt_peer_connection::write_pe3_sync() { INVARIANT_CHECK; TORRENT_ASSERT(!m_encrypted); TORRENT_ASSERT(!m_rc4_encrypted); TORRENT_ASSERT(is_outgoing()); TORRENT_ASSERT(!m_sent_handshake); boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); hasher h; sha1_hash const& info_hash = t->torrent_file().info_hash(); char const* const secret = m_dh_key_exchange->get_secret(); int pad_size = random() % 512; // synchash,skeyhash,vc,crypto_provide,len(pad),pad,len(ia) char msg[20 + 20 + 8 + 4 + 2 + 512 + 2]; char* ptr = msg; // sync hash (hash('req1',S)) h.reset(); h.update("req1",4); h.update(secret, dh_key_len); sha1_hash sync_hash = h.final(); memcpy(ptr, &sync_hash[0], 20); ptr += 20; // stream key obfuscated hash [ hash('req2',SKEY) xor hash('req3',S) ] h.reset(); h.update("req2",4); h.update(info_hash.data(), 20); sha1_hash streamkey_hash = h.final(); h.reset(); h.update("req3",4); h.update(secret, dh_key_len); sha1_hash obfsc_hash = h.final(); obfsc_hash ^= streamkey_hash; memcpy(ptr, &obfsc_hash[0], 20); ptr += 20; // Discard DH key exchange data, setup RC4 keys init_pe_rc4_handler(secret, info_hash); m_dh_key_exchange.reset(); // secret should be invalid at this point // write the verification constant and crypto field int encrypt_size = sizeof(msg) - 512 + pad_size - 40; boost::uint8_t crypto_provide = m_settings.get_int(settings_pack::allowed_enc_level); // this is an invalid setting, but let's just make the best of the situation if ((crypto_provide & settings_pack::pe_both) == 0) crypto_provide = settings_pack::pe_both; #ifndef TORRENT_DISABLE_LOGGING static char const* level[] = {"plaintext", "rc4", "plaintext rc4"}; peer_log(peer_log_alert::info, "ENCRYPTION" , "%s", level[crypto_provide-1]); #endif write_pe_vc_cryptofield(ptr, encrypt_size, crypto_provide, pad_size); std::vector vec; vec.push_back(boost::asio::mutable_buffer(ptr, encrypt_size)); m_rc4->encrypt(vec); send_buffer(msg, sizeof(msg) - 512 + pad_size); } void bt_peer_connection::write_pe4_sync(int crypto_select) { INVARIANT_CHECK; TORRENT_ASSERT(!is_outgoing()); TORRENT_ASSERT(!m_encrypted); TORRENT_ASSERT(!m_rc4_encrypted); TORRENT_ASSERT(crypto_select == 0x02 || crypto_select == 0x01); TORRENT_ASSERT(!m_sent_handshake); int pad_size = random() % 512; const int buf_size = 8 + 4 + 2 + pad_size; char msg[512 + 8 + 4 + 2]; write_pe_vc_cryptofield(msg, sizeof(msg), crypto_select, pad_size); std::vector vec; vec.push_back(boost::asio::mutable_buffer(msg, buf_size)); m_rc4->encrypt(vec); send_buffer(msg, buf_size); // encryption method has been negotiated if (crypto_select == 0x02) m_rc4_encrypted = true; else // 0x01 m_rc4_encrypted = false; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION", " crypto select: %s" , (crypto_select == 0x01) ? "plaintext" : "rc4"); #endif } void bt_peer_connection::write_pe_vc_cryptofield(char* write_buf, int len , int crypto_field, int pad_size) { INVARIANT_CHECK; #if !TORRENT_USE_ASSERTS TORRENT_UNUSED(len); #endif TORRENT_ASSERT(crypto_field <= 0x03 && crypto_field > 0); // vc,crypto_field,len(pad),pad, (len(ia)) TORRENT_ASSERT((len >= 8+4+2+pad_size+2 && is_outgoing()) || (len >= 8+4+2+pad_size && !is_outgoing())); TORRENT_ASSERT(!m_sent_handshake); // encrypt(vc, crypto_provide/select, len(Pad), len(IA)) // len(pad) is zero for now, len(IA) only for outgoing connections // vc memset(write_buf, 0, 8); write_buf += 8; detail::write_uint32(crypto_field, write_buf); detail::write_uint16(pad_size, write_buf); // len (pad) // fill pad with zeroes std::generate(write_buf, write_buf + pad_size, random_byte); write_buf += pad_size; // append len(ia) if we are initiating if (is_outgoing()) detail::write_uint16(handshake_len, write_buf); // len(IA) } void bt_peer_connection::init_pe_rc4_handler(char const* secret , sha1_hash const& stream_key) { INVARIANT_CHECK; TORRENT_ASSERT(secret); hasher h; static const char keyA[] = "keyA"; static const char keyB[] = "keyB"; // encryption rc4 longkeys // outgoing connection : hash ('keyA',S,SKEY) // incoming connection : hash ('keyB',S,SKEY) if (is_outgoing()) h.update(keyA, 4); else h.update(keyB, 4); h.update(secret, dh_key_len); h.update(stream_key.data(), 20); const sha1_hash local_key = h.final(); h.reset(); // decryption rc4 longkeys // outgoing connection : hash ('keyB',S,SKEY) // incoming connection : hash ('keyA',S,SKEY) if (is_outgoing()) h.update(keyB, 4); else h.update(keyA, 4); h.update(secret, dh_key_len); h.update(stream_key.data(), 20); const sha1_hash remote_key = h.final(); TORRENT_ASSERT(!m_rc4.get()); m_rc4 = boost::make_shared(); if (!m_rc4) { disconnect(errors::no_memory, op_encryption); return; } m_rc4->set_incoming_key(&remote_key[0], 20); m_rc4->set_outgoing_key(&local_key[0], 20); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION", "computed RC4 keys"); #endif } int bt_peer_connection::get_syncoffset(char const* src, int src_size, char const* target, int target_size) const { TORRENT_ASSERT(target_size >= src_size); TORRENT_ASSERT(src_size > 0); TORRENT_ASSERT(src); TORRENT_ASSERT(target); int traverse_limit = target_size - src_size; // TODO: this could be optimized using knuth morris pratt for (int i = 0; i < traverse_limit; ++i) { char const* target_ptr = target + i; if (std::equal(src, src+src_size, target_ptr)) return i; } // Partial sync // for (int i = 0; i < target_size; ++i) // { // // first is iterator in src[] at which mismatch occurs // // second is iterator in target[] at which mismatch occurs // std::pair ret; // int src_sync_size; // if (i > traverse_limit) // partial sync test // { // ret = std::mismatch(src, src + src_size - (i - traverse_limit), &target[i]); // src_sync_size = ret.first - src; // if (src_sync_size == (src_size - (i - traverse_limit))) // return i; // } // else // complete sync test // { // ret = std::mismatch(src, src + src_size, &target[i]); // src_sync_size = ret.first - src; // if (src_sync_size == src_size) // return i; // } // } // no complete sync return -1; } void bt_peer_connection::rc4_decrypt(char* pos, int len) { std::vector vec; vec.push_back(boost::asio::mutable_buffer(pos, len)); int consume = 0; int produce = len; int packet_size = 0; m_rc4->decrypt(vec, consume, produce, packet_size); } namespace { void regular_c_free(char* buf, void* /* userdata */ , block_cache_reference /* ref */) { ::free(buf); } } #endif // #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) void bt_peer_connection::append_const_send_buffer(char const* buffer, int size , chained_buffer::free_buffer_fun destructor, void* userdata , block_cache_reference ref) { #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) if (!m_enc_handler.is_send_plaintext()) { // if we're encrypting this buffer, we need to make a copy // since we'll mutate it char* buf = static_cast(malloc(size)); memcpy(buf, buffer, size); append_send_buffer(buf, size, ®ular_c_free, NULL); destructor(const_cast(buffer), userdata, ref); } else #endif { peer_connection::append_const_send_buffer(buffer, size, destructor , userdata, ref); } } void bt_peer_connection::write_handshake() { INVARIANT_CHECK; TORRENT_ASSERT(!m_sent_handshake); m_sent_handshake = true; boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); // add handshake to the send buffer static const char version_string[] = "BitTorrent protocol"; const int string_len = sizeof(version_string)-1; char handshake[1 + string_len + 8 + 20 + 20]; char* ptr = handshake; // length of version string detail::write_uint8(string_len, ptr); // protocol identifier memcpy(ptr, version_string, string_len); ptr += string_len; // 8 zeroes memset(ptr, 0, 8); #ifndef TORRENT_DISABLE_DHT // indicate that we support the DHT messages *(ptr + 7) |= 0x01; #endif #ifndef TORRENT_DISABLE_EXTENSIONS // we support extensions *(ptr + 5) |= 0x10; #endif if (m_settings.get_bool(settings_pack::support_merkle_torrents)) { // we support merkle torrents *(ptr + 5) |= 0x08; } // we support FAST extension *(ptr + 7) |= 0x04; #ifndef TORRENT_DISABLE_LOGGING std::string bitmask; for (int k = 0; k < 8; ++k) { for (int j = 0; j < 8; ++j) { if (ptr[k] & (0x80 >> j)) bitmask += '1'; else bitmask += '0'; } } peer_log(peer_log_alert::outgoing_message, "EXTENSIONS" , "%s", bitmask.c_str()); #endif ptr += 8; // info hash sha1_hash const& ih = t->torrent_file().info_hash(); memcpy(ptr, &ih[0], 20); ptr += 20; memcpy(ptr, &m_our_peer_id[0], 20); #ifndef TORRENT_DISABLE_LOGGING { char hex_pid[41]; to_hex(m_our_peer_id.data(), 20, hex_pid); hex_pid[40] = 0; peer_log(peer_log_alert::outgoing, "HANDSHAKE" , "sent peer_id: %s client: %s" , hex_pid, identify_client(m_our_peer_id).c_str()); } peer_log(peer_log_alert::outgoing_message, "HANDSHAKE" , "ih: %s", to_hex(ih.to_string()).c_str()); #endif send_buffer(handshake, sizeof(handshake)); } boost::optional bt_peer_connection::downloading_piece_progress() const { boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); buffer::const_interval recv_buffer = m_recv_buffer.get(); // are we currently receiving a 'piece' message? if (m_state != read_packet || recv_buffer.left() <= 9 || recv_buffer[0] != msg_piece) return boost::optional(); const char* ptr = recv_buffer.begin + 1; peer_request r; r.piece = detail::read_int32(ptr); r.start = detail::read_int32(ptr); r.length = m_recv_buffer.packet_size() - 9; // is any of the piece message header data invalid? if (!verify_piece(r)) return boost::optional(); piece_block_progress p; p.piece_index = r.piece; p.block_index = r.start / t->block_size(); p.bytes_downloaded = recv_buffer.left() - 9; p.full_block_bytes = r.length; return boost::optional(p); } // message handlers // ----------------------------- // --------- KEEPALIVE --------- // ----------------------------- void bt_peer_connection::on_keepalive() { INVARIANT_CHECK; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "KEEPALIVE"); #endif incoming_keepalive(); } // ----------------------------- // ----------- CHOKE ----------- // ----------------------------- void bt_peer_connection::on_choke(int received) { INVARIANT_CHECK; TORRENT_ASSERT(received >= 0); received_bytes(0, received); if (m_recv_buffer.packet_size() != 1) { disconnect(errors::invalid_choke, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; incoming_choke(); if (is_disconnecting()) return; if (!m_supports_fast) { // we just got choked, and the peer that choked use // doesn't support fast extensions, so we have to // assume that the choke message implies that all // of our requests are rejected. Go through them and // pretend that we received reject request messages boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); while (!download_queue().empty()) { piece_block const& b = download_queue().front().block; peer_request r; r.piece = b.piece_index; r.start = b.block_index * t->block_size(); r.length = t->block_size(); // if it's the last piece, make sure to // set the length of the request to not // exceed the end of the torrent. This is // necessary in order to maintain a correct // m_outsanding_bytes if (r.piece == t->torrent_file().num_pieces() - 1) { r.length = (std::min)(t->torrent_file().piece_size( r.piece) - r.start, r.length); } incoming_reject_request(r); } } } // ----------------------------- // ---------- UNCHOKE ---------- // ----------------------------- void bt_peer_connection::on_unchoke(int received) { INVARIANT_CHECK; TORRENT_ASSERT(received >= 0); received_bytes(0, received); if (m_recv_buffer.packet_size() != 1) { disconnect(errors::invalid_unchoke, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; incoming_unchoke(); } // ----------------------------- // -------- INTERESTED --------- // ----------------------------- void bt_peer_connection::on_interested(int received) { INVARIANT_CHECK; TORRENT_ASSERT(received >= 0); received_bytes(0, received); if (m_recv_buffer.packet_size() != 1) { disconnect(errors::invalid_interested, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; // we defer sending the allowed set until the peer says it's interested in // us. This saves some bandwidth and allows us to omit messages for pieces // that the peer already has if (!m_sent_allowed_fast && m_supports_fast) { m_sent_allowed_fast = true; send_allowed_set(); } incoming_interested(); } // ----------------------------- // ------ NOT INTERESTED ------- // ----------------------------- void bt_peer_connection::on_not_interested(int received) { INVARIANT_CHECK; TORRENT_ASSERT(received >= 0); received_bytes(0, received); if (m_recv_buffer.packet_size() != 1) { disconnect(errors::invalid_not_interested, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; incoming_not_interested(); } // ----------------------------- // ----------- HAVE ------------ // ----------------------------- void bt_peer_connection::on_have(int received) { INVARIANT_CHECK; TORRENT_ASSERT(received >= 0); received_bytes(0, received); if (m_recv_buffer.packet_size() != 5) { disconnect(errors::invalid_have, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; buffer::const_interval recv_buffer = m_recv_buffer.get(); 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; TORRENT_ASSERT(received >= 0); boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); received_bytes(0, received); // if we don't have the metedata, we cannot // verify the bitfield size if (t->valid_metadata() && m_recv_buffer.packet_size() - 1 != (t->torrent_file().num_pieces() + 7) / 8) { disconnect(errors::invalid_bitfield_size, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; buffer::const_interval recv_buffer = m_recv_buffer.get(); bitfield bits; bits.assign(recv_buffer.begin + 1 , t->valid_metadata()?get_bitfield().size():(m_recv_buffer.packet_size()-1)*8); incoming_bitfield(bits); } // ----------------------------- // ---------- REQUEST ---------- // ----------------------------- void bt_peer_connection::on_request(int received) { INVARIANT_CHECK; TORRENT_ASSERT(received >= 0); received_bytes(0, received); if (m_recv_buffer.packet_size() != 13) { disconnect(errors::invalid_request, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; buffer::const_interval recv_buffer = m_recv_buffer.get(); 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; TORRENT_ASSERT(received >= 0); buffer::const_interval recv_buffer = m_recv_buffer.get(); int recv_pos = m_recv_buffer.pos(); // recv_buffer.end - recv_buffer.begin; boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); bool merkle = static_cast(recv_buffer.begin[0]) == 250; if (merkle) { if (recv_pos == 1) { m_recv_buffer.set_soft_packet_size(13); received_bytes(0, received); return; } if (recv_pos < 13) { received_bytes(0, received); return; } if (recv_pos == 13) { const char* ptr = recv_buffer.begin + 9; int list_size = detail::read_int32(ptr); // now we know how long the bencoded hash list is // and we can allocate the disk buffer and receive // into it if (list_size > m_recv_buffer.packet_size() - 13 || list_size < 0) { received_bytes(0, received); disconnect(errors::invalid_hash_list, op_bittorrent, 2); return; } if (m_recv_buffer.packet_size() - 13 - list_size > t->block_size()) { received_bytes(0, received); disconnect(errors::packet_too_large, op_bittorrent, 2); return; } m_recv_buffer.assert_no_disk_buffer(); if (!m_settings.get_bool(settings_pack::contiguous_recv_buffer) && m_recv_buffer.can_recv_contiguous(m_recv_buffer.packet_size() - 13 - list_size)) { if (!allocate_disk_receive_buffer(m_recv_buffer.packet_size() - 13 - list_size)) { received_bytes(0, received); return; } } } } else { if (recv_pos == 1) { m_recv_buffer.assert_no_disk_buffer(); if (m_recv_buffer.packet_size() - 9 > t->block_size()) { received_bytes(0, received); disconnect(errors::packet_too_large, op_bittorrent, 2); return; } if (!m_settings.get_bool(settings_pack::contiguous_recv_buffer) && m_recv_buffer.can_recv_contiguous(m_recv_buffer.packet_size() - 9)) { if (!allocate_disk_receive_buffer(m_recv_buffer.packet_size() - 9)) { received_bytes(0, received); return; } } } } TORRENT_ASSERT(m_settings.get_bool(settings_pack::contiguous_recv_buffer) || m_recv_buffer.has_disk_buffer() || m_recv_buffer.packet_size() == 9); // classify the received data as protocol chatter // or data payload for the statistics int piece_bytes = 0; int header_size = merkle?13:9; peer_request p; int list_size = 0; if (recv_pos >= header_size) { const char* ptr = recv_buffer.begin + 1; p.piece = detail::read_int32(ptr); p.start = detail::read_int32(ptr); if (merkle) { list_size = detail::read_int32(ptr); if (list_size < 0) { received_bytes(0, received); disconnect(errors::invalid_hash_list, op_bittorrent, 2); return; } p.length = m_recv_buffer.packet_size() - list_size - header_size; header_size += list_size; } else { p.length = m_recv_buffer.packet_size() - header_size; } } if (recv_pos <= header_size) { // only received protocol data received_bytes(0, received); } else if (recv_pos - received >= header_size) { // only received payload data received_bytes(received, 0); piece_bytes = received; } else { // received a bit of both TORRENT_ASSERT(recv_pos - received < header_size); TORRENT_ASSERT(recv_pos > header_size); TORRENT_ASSERT(header_size - (recv_pos - received) <= header_size); received_bytes( recv_pos - header_size , header_size - (recv_pos - received)); piece_bytes = recv_pos - header_size; } if (recv_pos < header_size) return; #ifndef TORRENT_DISABLE_LOGGING // peer_log(peer_log_alert::incoming_message, "PIECE_FRAGMENT", "p: %d start: %d length: %d" // , p.piece, p.start, p.length); #endif if (recv_pos - received < header_size && recv_pos >= header_size) { // call this once, the first time the entire header // has been received start_receive_piece(p); if (is_disconnecting()) return; } TORRENT_ASSERT(m_settings.get_bool(settings_pack::contiguous_recv_buffer) || m_recv_buffer.has_disk_buffer() || m_recv_buffer.packet_size() == header_size); incoming_piece_fragment(piece_bytes); if (!m_recv_buffer.packet_finished()) return; if (merkle && list_size > 0) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HASHPIECE" , "piece: %d list: %d", p.piece, list_size); #endif bdecode_node hash_list; error_code ec; if (bdecode(recv_buffer.begin + 13, recv_buffer.begin+ 13 + list_size , hash_list, ec) != 0) { disconnect(errors::invalid_hash_piece, op_bittorrent, 2); return; } // the list has this format: // [ [node-index, hash], [node-index, hash], ... ] if (hash_list.type() != bdecode_node::list_t) { disconnect(errors::invalid_hash_list, op_bittorrent, 2); return; } std::map nodes; for (int i = 0; i < hash_list.list_size(); ++i) { bdecode_node e = hash_list.list_at(i); if (e.type() != bdecode_node::list_t || e.list_size() != 2 || e.list_at(0).type() != bdecode_node::int_t || e.list_at(1).type() != bdecode_node::string_t || e.list_at(1).string_length() != 20) continue; nodes.insert(std::make_pair(int(e.list_int_value_at(0)) , sha1_hash(e.list_at(1).string_ptr()))); } if (!nodes.empty() && !t->add_merkle_nodes(nodes, p.piece)) { disconnect(errors::invalid_hash_piece, op_bittorrent, 2); return; } } char* disk_buffer = m_recv_buffer.release_disk_buffer(); if (disk_buffer) { disk_buffer_holder holder(m_allocator, disk_buffer); incoming_piece(p, holder); } else { incoming_piece(p, recv_buffer.begin + header_size); } } // ----------------------------- // ---------- CANCEL ----------- // ----------------------------- void bt_peer_connection::on_cancel(int received) { INVARIANT_CHECK; TORRENT_ASSERT(received >= 0); received_bytes(0, received); if (m_recv_buffer.packet_size() != 13) { disconnect(errors::invalid_cancel, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; buffer::const_interval recv_buffer = m_recv_buffer.get(); 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; TORRENT_ASSERT(received >= 0); received_bytes(0, received); if (m_recv_buffer.packet_size() != 3) { disconnect(errors::invalid_dht_port, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; buffer::const_interval recv_buffer = m_recv_buffer.get(); const char* ptr = recv_buffer.begin + 1; int listen_port = detail::read_uint16(ptr); incoming_dht_port(listen_port); if (!m_supports_dht_port) { m_supports_dht_port = true; #ifndef TORRENT_DISABLE_DHT // if we're done with the handshake, respond right away, otherwise // we'll send the DHT port later if (m_sent_handshake && m_ses.has_dht()) write_dht_port(m_ses.external_udp_port()); #endif } } void bt_peer_connection::on_suggest_piece(int received) { INVARIANT_CHECK; received_bytes(0, received); if (!m_supports_fast) { disconnect(errors::invalid_suggest, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; buffer::const_interval recv_buffer = m_recv_buffer.get(); const char* ptr = recv_buffer.begin + 1; int piece = detail::read_uint32(ptr); incoming_suggest(piece); } void bt_peer_connection::on_have_all(int received) { INVARIANT_CHECK; received_bytes(0, received); if (!m_supports_fast) { disconnect(errors::invalid_have_all, op_bittorrent, 2); return; } incoming_have_all(); } void bt_peer_connection::on_have_none(int received) { INVARIANT_CHECK; received_bytes(0, received); if (!m_supports_fast) { disconnect(errors::invalid_have_none, op_bittorrent, 2); return; } incoming_have_none(); } void bt_peer_connection::on_reject_request(int received) { INVARIANT_CHECK; received_bytes(0, received); if (!m_supports_fast) { disconnect(errors::invalid_reject, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; buffer::const_interval recv_buffer = m_recv_buffer.get(); 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_reject_request(r); } void bt_peer_connection::on_allowed_fast(int received) { INVARIANT_CHECK; received_bytes(0, received); if (!m_supports_fast) { disconnect(errors::invalid_allow_fast, op_bittorrent, 2); return; } if (!m_recv_buffer.packet_finished()) return; buffer::const_interval recv_buffer = m_recv_buffer.get(); const char* ptr = recv_buffer.begin + 1; int index = detail::read_int32(ptr); incoming_allowed_fast(index); } // ----------------------------- // -------- RENDEZVOUS --------- // ----------------------------- #ifndef TORRENT_DISABLE_EXTENSIONS void bt_peer_connection::on_holepunch() { INVARIANT_CHECK; if (!m_recv_buffer.packet_finished()) return; // we can't accept holepunch messages from peers // that don't support the holepunch extension // because we wouldn't be able to respond if (m_holepunch_id == 0) return; buffer::const_interval recv_buffer = m_recv_buffer.get(); TORRENT_ASSERT(*recv_buffer.begin == msg_extended); ++recv_buffer.begin; TORRENT_ASSERT(*recv_buffer.begin == holepunch_msg); ++recv_buffer.begin; const char* ptr = recv_buffer.begin; // ignore invalid messages if (recv_buffer.left() < 2) return; int msg_type = detail::read_uint8(ptr); int addr_type = detail::read_uint8(ptr); tcp::endpoint ep; if (addr_type == 0) { if (recv_buffer.left() < 2 + 4 + 2) return; // IPv4 address ep = detail::read_v4_endpoint(ptr); } #if TORRENT_USE_IPV6 else if (addr_type == 1) { // IPv6 address if (recv_buffer.left() < 2 + 18 + 2) return; ep = detail::read_v6_endpoint(ptr); } #endif else { #ifndef TORRENT_DISABLE_LOGGING error_code ec; static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"}; peer_log(peer_log_alert::incoming_message, "HOLEPUNCH" , "msg: %s from %s to: unknown address type" , (msg_type >= 0 && msg_type < 3 ? hp_msg_name[msg_type] : "unknown message type") , print_address(remote().address()).c_str()); #endif return; // unknown address type } boost::shared_ptr t = associated_torrent().lock(); if (!t) return; switch (msg_type) { case hp_rendezvous: // rendezvous { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HOLEPUNCH" , "msg: rendezvous to: %s", print_address(ep.address()).c_str()); #endif // this peer is asking us to introduce it to // the peer at 'ep'. We need to find which of // our connections points to that endpoint bt_peer_connection* p = t->find_peer(ep); if (p == 0) { // we're not connected to this peer write_holepunch_msg(hp_failed, ep, hp_not_connected); break; } if (!p->supports_holepunch()) { write_holepunch_msg(hp_failed, ep, hp_no_support); break; } if (p == this) { write_holepunch_msg(hp_failed, ep, hp_no_self); break; } write_holepunch_msg(hp_connect, ep, 0); p->write_holepunch_msg(hp_connect, remote(), 0); } break; case hp_connect: { // add or find the peer with this endpoint torrent_peer* p = t->add_peer(ep, peer_info::pex); if (p == 0 || p->connection) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HOLEPUNCH" , "msg:connect to: %s error: failed to add peer" , print_address(ep.address()).c_str()); #endif // we either couldn't add this peer, or it's // already connected. Just ignore the connect message break; } if (p->banned) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HOLEPUNCH" , "msg:connect to: %s error: peer banned", print_address(ep.address()).c_str()); #endif // this peer is banned, don't connect to it break; } // to make sure we use the uTP protocol p->supports_utp = true; // #error make sure we make this a connection candidate // in case it has too many failures for instance t->connect_to_peer(p, true); // mark this connection to be in holepunch mode // so that it will retry faster and stick to uTP while it's // retrying t->update_want_peers(); if (p->connection) p->connection->set_holepunch_mode(); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HOLEPUNCH" , "msg:connect to: %s" , print_address(ep.address()).c_str()); #endif } break; case hp_failed: { boost::uint32_t error = detail::read_uint32(ptr); #ifndef TORRENT_DISABLE_LOGGING error_code ec; static char const* err_msg[] = {"no such peer", "not connected", "no support", "no self"}; peer_log(peer_log_alert::incoming_message, "HOLEPUNCH" , "msg:failed error: %d msg: %s", error , ((error > 0 && error < 5)?err_msg[error-1]:"unknown message id")); #endif // #error deal with holepunch errors (void)error; } break; #ifndef TORRENT_DISABLE_LOGGING default: { error_code ec; peer_log(peer_log_alert::incoming_message, "HOLEPUNCH" , "msg: unknown message type (%d) to: %s" , msg_type, print_address(ep.address()).c_str()); } #endif } } void bt_peer_connection::write_holepunch_msg(int type, tcp::endpoint const& ep, int error) { char buf[35]; char* ptr = buf + 6; detail::write_uint8(type, ptr); if (ep.address().is_v4()) detail::write_uint8(0, ptr); else detail::write_uint8(1, ptr); detail::write_endpoint(ep, ptr); #ifndef TORRENT_DISABLE_LOGGING error_code ec; static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"}; static const char* hp_error_string[] = {"", "no such peer", "not connected", "no support", "no self"}; peer_log(peer_log_alert::outgoing_message, "HOLEPUNCH" , "msg: %s to: %s error: %s" , (type >= 0 && type < 3 ? hp_msg_name[type] : "unknown message type") , print_address(ep.address()).c_str() , hp_error_string[error]); #endif if (type == hp_failed) { detail::write_uint32(error, ptr); } // write the packet length and type char* hdr = buf; detail::write_uint32(ptr - buf - 4, hdr); detail::write_uint8(msg_extended, hdr); detail::write_uint8(m_holepunch_id, hdr); TORRENT_ASSERT(ptr <= buf + sizeof(buf)); send_buffer(buf, ptr - buf); stats_counters().inc_stats_counter(counters::num_outgoing_extended); } #endif // TORRENT_DISABLE_EXTENSIONS // ----------------------------- // --------- EXTENDED ---------- // ----------------------------- #ifndef TORRENT_DISABLE_EXTENSIONS void bt_peer_connection::on_extended(int received) { INVARIANT_CHECK; TORRENT_ASSERT(received >= 0); received_bytes(0, received); if (m_recv_buffer.packet_size() < 2) { disconnect(errors::invalid_extended, op_bittorrent, 2); return; } if (associated_torrent().expired()) { disconnect(errors::invalid_extended, op_bittorrent, 2); return; } buffer::const_interval recv_buffer = m_recv_buffer.get(); if (recv_buffer.left() < 2) return; TORRENT_ASSERT(*recv_buffer.begin == msg_extended); ++recv_buffer.begin; int extended_id = detail::read_uint8(recv_buffer.begin); if (extended_id == 0) { on_extended_handshake(); disconnect_if_redundant(); return; } if (extended_id == upload_only_msg) { if (!m_recv_buffer.packet_finished()) return; if (m_recv_buffer.packet_size() != 3) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "UPLOAD_ONLY" , "ERROR: unexpected packet size: %d", m_recv_buffer.packet_size()); #endif return; } bool ul = detail::read_uint8(recv_buffer.begin) != 0; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "UPLOAD_ONLY" , "%s", (ul?"true":"false")); #endif set_upload_only(ul); return; } if (extended_id == share_mode_msg) { if (!m_recv_buffer.packet_finished()) return; if (m_recv_buffer.packet_size() != 3) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "SHARE_MODE" , "ERROR: unexpected packet size: %d", m_recv_buffer.packet_size()); #endif return; } bool sm = detail::read_uint8(recv_buffer.begin) != 0; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "SHARE_MODE" , "%s", (sm?"true":"false")); #endif set_share_mode(sm); return; } if (extended_id == holepunch_msg) { if (!m_recv_buffer.packet_finished()) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HOLEPUNCH"); #endif on_holepunch(); return; } if (extended_id == dont_have_msg) { if (!m_recv_buffer.packet_finished()) return; if (m_recv_buffer.packet_size() != 6) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "DONT_HAVE" , "ERROR: unexpected packet size: %d", m_recv_buffer.packet_size()); #endif return; } int piece = detail::read_uint32(recv_buffer.begin); incoming_dont_have(piece); return; } #ifndef TORRENT_DISABLE_LOGGING if (m_recv_buffer.packet_finished()) peer_log(peer_log_alert::incoming_message, "EXTENSION_MESSAGE" , "msg: %d size: %d", extended_id, m_recv_buffer.packet_size()); #endif for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_extended(m_recv_buffer.packet_size() - 2, extended_id , recv_buffer)) return; } disconnect(errors::invalid_message, op_bittorrent, 2); return; } void bt_peer_connection::on_extended_handshake() { if (!m_recv_buffer.packet_finished()) return; boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); buffer::const_interval recv_buffer = m_recv_buffer.get(); bdecode_node root; error_code ec; int pos; int ret = bdecode(recv_buffer.begin + 2, recv_buffer.end, root, ec, &pos); if (ret != 0 || ec || root.type() != bdecode_node::dict_t) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "EXTENSION_MESSAGE" , "invalid extended handshake: %s pos: %d" , ec.message().c_str(), pos); #endif return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "EXTENDED_HANDSHAKE" , "%s", print_entry(root, true).c_str()); #endif for (extension_list_t::iterator i = m_extensions.begin(); !m_extensions.empty() && i != m_extensions.end();) { // a false return value means that the extension // isn't supported by the other end. So, it is removed. if (!(*i)->on_extension_handshake(root)) i = m_extensions.erase(i); else ++i; } if (is_disconnecting()) return; // upload_only if (bdecode_node m = root.dict_find_dict("m")) { m_upload_only_id = boost::uint8_t(m.dict_find_int_value("upload_only", 0)); m_holepunch_id = boost::uint8_t(m.dict_find_int_value("ut_holepunch", 0)); m_dont_have_id = boost::uint8_t(m.dict_find_int_value("lt_donthave", 0)); } // there is supposed to be a remote listen port int listen_port = int(root.dict_find_int_value("p")); if (listen_port > 0 && peer_info_struct() != 0) { t->update_peer_port(listen_port, peer_info_struct(), peer_info::incoming); received_listen_port(); if (is_disconnecting()) return; } // there should be a version too // but where do we put that info? int const last_seen_complete = root.dict_find_int_value("complete_ago", -1); if (last_seen_complete >= 0) set_last_seen_complete(last_seen_complete); std::string client_info = root.dict_find_string_value("v"); if (!client_info.empty()) m_client_version = client_info; int reqq = int(root.dict_find_int_value("reqq")); if (reqq > 0) max_out_request_queue(reqq); if (root.dict_find_int_value("upload_only", 0)) set_upload_only(true); if (m_settings.get_bool(settings_pack::support_share_mode) && root.dict_find_int_value("share_mode", 0)) set_share_mode(true); std::string myip = root.dict_find_string_value("yourip"); if (!myip.empty()) { if (myip.size() == address_v4::bytes_type().size()) { address_v4::bytes_type bytes; std::copy(myip.begin(), myip.end(), bytes.begin()); m_ses.set_external_address(address_v4(bytes) , aux::session_interface::source_peer, remote().address()); } #if TORRENT_USE_IPV6 else if (myip.size() == address_v6::bytes_type().size()) { address_v6::bytes_type bytes; std::copy(myip.begin(), myip.end(), bytes.begin()); address_v6 ipv6_address(bytes); if (ipv6_address.is_v4_mapped()) m_ses.set_external_address(ipv6_address.to_v4() , aux::session_interface::source_peer, remote().address()); else m_ses.set_external_address(ipv6_address , aux::session_interface::source_peer, remote().address()); } #endif } // if we're finished and this peer is uploading only // disconnect it if (t->is_finished() && upload_only() && m_settings.get_bool(settings_pack::close_redundant_connections) && !t->share_mode()) disconnect(errors::upload_upload_connection, op_bittorrent); stats_counters().inc_stats_counter(counters::num_incoming_ext_handshake); } #endif // TORRENT_DISABLE_EXTENSIONS bool bt_peer_connection::dispatch_message(int received) { INVARIANT_CHECK; TORRENT_ASSERT(received >= 0); // this means the connection has been closed already if (associated_torrent().expired()) { received_bytes(0, received); return false; } buffer::const_interval recv_buffer = m_recv_buffer.get(); TORRENT_ASSERT(recv_buffer.left() >= 1); int packet_type = static_cast(recv_buffer[0]); if (m_settings.get_bool(settings_pack::support_merkle_torrents) && packet_type == 250) packet_type = msg_piece; if (packet_type < 0 || packet_type >= num_supported_messages || m_message_handler[packet_type] == 0) { #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_unknown_message(m_recv_buffer.packet_size(), packet_type , buffer::const_interval(recv_buffer.begin+1 , recv_buffer.end))) return m_recv_buffer.packet_finished(); } #endif received_bytes(0, received); disconnect(errors::invalid_message, op_bittorrent); return m_recv_buffer.packet_finished(); } TORRENT_ASSERT(m_message_handler[packet_type] != 0); #if TORRENT_USE_ASSERTS boost::int64_t cur_payload_dl = statistics().last_payload_downloaded(); boost::int64_t cur_protocol_dl = statistics().last_protocol_downloaded(); #endif // call the correct handler for this packet type (this->*m_message_handler[packet_type])(received); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(statistics().last_payload_downloaded() - cur_payload_dl >= 0); TORRENT_ASSERT(statistics().last_protocol_downloaded() - cur_protocol_dl >= 0); boost::int64_t stats_diff = statistics().last_payload_downloaded() - cur_payload_dl + statistics().last_protocol_downloaded() - cur_protocol_dl; TORRENT_ASSERT(stats_diff == received); #endif bool finished = m_recv_buffer.packet_finished(); if (finished) { // count this packet in the session stats counters int counter = counters::num_incoming_extended; if (packet_type <= msg_dht_port) counter = counters::num_incoming_choke + packet_type; else if (packet_type <= msg_allowed_fast) counter = counters::num_incoming_suggest + packet_type; else if (packet_type <= msg_extended) counter = counters::num_incoming_extended; else TORRENT_ASSERT(false); stats_counters().inc_stats_counter(counter); } return finished; } #ifndef TORRENT_DISABLE_EXTENSIONS void bt_peer_connection::write_upload_only() { INVARIANT_CHECK; boost::shared_ptr t = associated_torrent().lock(); if (m_upload_only_id == 0) return; if (t->share_mode()) return; // if we send upload-only, the other end is very likely to disconnect // us, at least if it's a seed. If we don't want to close redundant // connections, don't sent upload-only if (!m_settings.get_bool(settings_pack::close_redundant_connections)) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "UPLOAD_ONLY", "%d" , int(t->is_upload_only() && !t->super_seeding())); #endif char msg[7] = {0, 0, 0, 3, msg_extended}; char* ptr = msg + 5; detail::write_uint8(m_upload_only_id, ptr); // if we're super seeding, we don't want to make peers // think that we only have a single piece and is upload // only, since they might disconnect immediately when // they have downloaded a single piece, although we'll // make another piece available detail::write_uint8(t->is_upload_only() && !t->super_seeding(), ptr); send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_extended); } void bt_peer_connection::write_share_mode() { INVARIANT_CHECK; boost::shared_ptr t = associated_torrent().lock(); if (m_share_mode_id == 0) return; char msg[7] = {0, 0, 0, 3, msg_extended}; char* ptr = msg + 5; detail::write_uint8(m_share_mode_id, ptr); detail::write_uint8(t->share_mode(), ptr); send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_extended); } #endif void bt_peer_connection::write_keepalive() { INVARIANT_CHECK; // Don't require the bitfield to have been sent at this point // the case where m_sent_bitfield may not be true is if the // torrent doesn't have any metadata, and a peer is timimg out. // then the keep-alive message will be sent before the bitfield // this is a violation to the original protocol, but necessary // for the metadata extension. TORRENT_ASSERT(m_sent_handshake); char msg[] = {0,0,0,0}; send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_cancel(peer_request const& r) { INVARIANT_CHECK; TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); char msg[17] = {0,0,0,13, msg_cancel}; char* ptr = msg + 5; detail::write_int32(r.piece, ptr); // index detail::write_int32(r.start, ptr); // begin detail::write_int32(r.length, ptr); // length send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_cancel); if (!m_supports_fast) incoming_reject_request(r); } void bt_peer_connection::write_request(peer_request const& r) { INVARIANT_CHECK; TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); char msg[17] = {0,0,0,13, msg_request}; char* ptr = msg + 5; detail::write_int32(r.piece, ptr); // index detail::write_int32(r.start, ptr); // begin detail::write_int32(r.length, ptr); // length send_buffer(msg, sizeof(msg), message_type_request); stats_counters().inc_stats_counter(counters::num_outgoing_request); } void bt_peer_connection::write_bitfield() { INVARIANT_CHECK; // if we have not received the other peer's extension bits yet, how do we // know whether to send a have-all or have-none? TORRENT_ASSERT(m_state >= read_peer_id); boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(t->valid_metadata()); if (t->super_seeding()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "BITFIELD", "not sending bitfield, super seeding"); #endif if (m_supports_fast) write_have_none(); // if we are super seeding, pretend to not have any piece // and don't send a bitfield m_sent_bitfield = true; // bootstrap super-seeding by sending two have message int piece = t->get_piece_to_super_seed(get_bitfield()); if (piece >= 0) superseed_piece(-1, piece); piece = t->get_piece_to_super_seed(get_bitfield()); if (piece >= 0) superseed_piece(-1, piece); return; } else if (m_supports_fast && t->is_seed() && !m_settings.get_bool(settings_pack::lazy_bitfields)) { write_have_all(); return; } else if (m_supports_fast && t->num_have() == 0) { write_have_none(); return; } else if (t->num_have() == 0) { // don't send a bitfield if we don't have any pieces #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "BITFIELD", "not sending bitfield, have none"); #endif m_sent_bitfield = true; return; } const int num_pieces = t->torrent_file().num_pieces(); TORRENT_ASSERT(num_pieces > 0); if (num_pieces <= 0) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "BITFIELD", "not sending bitfield, num_pieces == 0"); #endif return; } int lazy_pieces[50]; int num_lazy_pieces = 0; int lazy_piece = 0; if (t->is_seed() && m_settings.get_bool(settings_pack::lazy_bitfields) #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) && !m_encrypted #endif ) { num_lazy_pieces = (std::min)(50, num_pieces / 10); if (num_lazy_pieces < 1) num_lazy_pieces = 1; for (int i = 0; i < num_pieces; ++i) { if (int(random() % (num_pieces - i)) >= num_lazy_pieces - lazy_piece) continue; lazy_pieces[lazy_piece++] = i; } TORRENT_ASSERT(lazy_piece == num_lazy_pieces); } const int packet_size = (num_pieces + 7) / 8 + 5; boost::uint8_t* msg = TORRENT_ALLOCA(boost::uint8_t, packet_size); if (msg == 0) return; // out of memory unsigned char* ptr = msg; detail::write_int32(packet_size - 4, ptr); detail::write_uint8(msg_bitfield, ptr); if (t->is_seed()) { memset(ptr, 0xff, packet_size - 5); // Clear trailing bits unsigned char *p = msg + packet_size - 1; *p = (0xff << ((8 - (num_pieces & 7)) & 7)) & 0xff; } else { memset(ptr, 0, packet_size - 5); piece_picker const& p = t->picker(); int mask = 0x80; for (int i = 0; i < num_pieces; ++i) { if (p.have_piece(i)) *ptr |= mask; mask >>= 1; if (mask == 0) { mask = 0x80; ++ptr; } } } for (int c = 0; c < num_lazy_pieces; ++c) msg[5 + lazy_pieces[c] / 8] &= ~(0x80 >> (lazy_pieces[c] & 7)); // add predictive pieces to the bitfield as well, since we won't // announce them again for (std::vector::const_iterator i = t->predictive_pieces().begin() , end(t->predictive_pieces().end()); i != end; ++i) msg[5 + *i / 8] |= (0x80 >> (*i & 7)); #ifndef TORRENT_DISABLE_LOGGING std::string bitfield_string; bitfield_string.resize(num_pieces); for (int k = 0; k < num_pieces; ++k) { if (msg[5 + k / 8] & (0x80 >> (k % 8))) bitfield_string[k] = '1'; else bitfield_string[k] = '0'; } peer_log(peer_log_alert::outgoing_message, "BITFIELD" , "%s", bitfield_string.c_str()); #endif m_sent_bitfield = true; send_buffer(reinterpret_cast(msg), packet_size); stats_counters().inc_stats_counter(counters::num_outgoing_bitfield); if (num_lazy_pieces > 0) { for (int i = 0; i < num_lazy_pieces; ++i) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "HAVE" , "piece: %d", lazy_pieces[i]); #endif write_have(lazy_pieces[i]); } // TODO: if we're finished, send upload_only message } } #ifndef TORRENT_DISABLE_EXTENSIONS void bt_peer_connection::write_extensions() { INVARIANT_CHECK; TORRENT_ASSERT(m_supports_extensions); TORRENT_ASSERT(m_sent_handshake); entry handshake; entry::dictionary_type& m = handshake["m"].dict(); // if we're using a proxy, our listen port won't be useful // anyway. if (!m_settings.get_bool(settings_pack::force_proxy) && is_outgoing()) handshake["p"] = m_ses.listen_port(); // only send the port in case we bade the connection // on incoming connections the other end already knows // our listen port if (!m_settings.get_bool(settings_pack::anonymous_mode)) { handshake["v"] = m_settings.get_str(settings_pack::handshake_client_version).empty() ? m_settings.get_str(settings_pack::user_agent) : m_settings.get_str(settings_pack::handshake_client_version); } std::string remote_address; std::back_insert_iterator out(remote_address); detail::write_address(remote().address(), out); #if TORRENT_USE_I2P if (!is_i2p(*get_socket())) #endif handshake["yourip"] = remote_address; handshake["reqq"] = m_settings.get_int(settings_pack::max_allowed_in_request_queue); boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); m["upload_only"] = upload_only_msg; m["ut_holepunch"] = holepunch_msg; if (m_settings.get_bool(settings_pack::support_share_mode)) m["share_mode"] = share_mode_msg; m["lt_donthave"] = dont_have_msg; int complete_ago = -1; if (t->last_seen_complete() > 0) complete_ago = t->time_since_complete(); handshake["complete_ago"] = complete_ago; // if we're using lazy bitfields or if we're super seeding, don't say // we're upload only, since it might make peers disconnect. don't tell // anyone we're upload only when in share mode, we want to stay connected // to seeds. if we're super seeding, we don't want to make peers think // that we only have a single piece and is upload only, since they might // disconnect immediately when they have downloaded a single piece, // although we'll make another piece available. If we don't have // metadata, we also need to suppress saying we're upload-only. If we do, // we may be disconnected before we receive the metadata. if (t->is_upload_only() && !t->share_mode() && t->valid_metadata() && !t->super_seeding() && (!m_settings.get_bool(settings_pack::lazy_bitfields) #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) || m_encrypted #endif )) { handshake["upload_only"] = 1; } if (m_settings.get_bool(settings_pack::support_share_mode) && t->share_mode()) handshake["share_mode"] = 1; // loop backwards, to make the first extension be the last // to fill in the handshake (i.e. give the first extensions priority) for (extension_list_t::reverse_iterator i = m_extensions.rbegin() , end(m_extensions.rend()); i != end; ++i) { (*i)->add_handshake(handshake); } #ifndef NDEBUG // make sure there are not conflicting extensions std::set ext; for (entry::dictionary_type::const_iterator i = m.begin() , end(m.end()); i != end; ++i) { if (i->second.type() != entry::int_t) continue; int val = int(i->second.integer()); TORRENT_ASSERT(ext.find(val) == ext.end()); ext.insert(val); } #endif std::vector dict_msg; bencode(std::back_inserter(dict_msg), handshake); char msg[6]; char* ptr = msg; // write the length of the message detail::write_int32(int(dict_msg.size()) + 2, ptr); detail::write_uint8(msg_extended, ptr); // signal handshake message detail::write_uint8(0, ptr); send_buffer(msg, sizeof(msg)); send_buffer(&dict_msg[0], dict_msg.size()); stats_counters().inc_stats_counter(counters::num_outgoing_ext_handshake); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "EXTENDED_HANDSHAKE" , "%s", handshake.to_string(true).c_str()); #endif } #endif void bt_peer_connection::write_choke() { INVARIANT_CHECK; TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); if (is_choked()) return; char msg[] = {0,0,0,1,msg_choke}; send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_choke); } void bt_peer_connection::write_unchoke() { INVARIANT_CHECK; TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); char msg[] = {0,0,0,1,msg_unchoke}; send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_unchoke); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { (*i)->sent_unchoke(); } #endif } void bt_peer_connection::write_interested() { INVARIANT_CHECK; TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); char msg[] = {0,0,0,1,msg_interested}; send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_interested); } void bt_peer_connection::write_not_interested() { INVARIANT_CHECK; TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); char msg[] = {0,0,0,1,msg_not_interested}; send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_not_interested); } void bt_peer_connection::write_have(int index) { INVARIANT_CHECK; TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < associated_torrent().lock()->torrent_file().num_pieces()); TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); char msg[] = {0,0,0,5,msg_have,0,0,0,0}; char* ptr = msg + 5; detail::write_int32(index, ptr); send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_have); } void bt_peer_connection::write_dont_have(int index) { #ifndef TORRENT_DISABLE_EXTENSIONS INVARIANT_CHECK; TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < associated_torrent().lock()->torrent_file().num_pieces()); if (in_handshake()) return; TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); if (!m_supports_extensions || m_dont_have_id == 0) return; char msg[] = {0,0,0,6,msg_extended,char(m_dont_have_id),0,0,0,0}; char* ptr = msg + 6; detail::write_int32(index, ptr); send_buffer(msg, sizeof(msg)); stats_counters().inc_stats_counter(counters::num_outgoing_extended); #else TORRENT_UNUSED(index); #endif } namespace { void buffer_reclaim_block(char* /* buffer */, void* userdata , block_cache_reference ref) { buffer_allocator_interface* buf = static_cast(userdata); buf->reclaim_block(ref); } void buffer_free_disk_buf(char* buffer, void* userdata , block_cache_reference /* ref */) { buffer_allocator_interface* buf = static_cast(userdata); buf->free_disk_buffer(buffer); } } // anonymous namespace void bt_peer_connection::write_piece(peer_request const& r, disk_buffer_holder& buffer) { INVARIANT_CHECK; TORRENT_ASSERT(m_sent_handshake); TORRENT_ASSERT(m_sent_bitfield); boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); bool merkle = t->torrent_file().is_merkle_torrent() && r.start == 0; // the hash piece looks like this: // uint8_t msg // uint32_t piece index // uint32_t start // uint32_t list len // var bencoded list // var piece data char msg[4 + 1 + 4 + 4 + 4]; char* ptr = msg; TORRENT_ASSERT(r.length <= 16 * 1024); detail::write_int32(r.length + 1 + 4 + 4, ptr); if (m_settings.get_bool(settings_pack::support_merkle_torrents) && merkle) detail::write_uint8(250, ptr); else detail::write_uint8(msg_piece, ptr); detail::write_int32(r.piece, ptr); detail::write_int32(r.start, ptr); // if this is a merkle torrent and the start offset // is 0, we need to include the merkle node hashes if (merkle) { std::vector piece_list_buf; entry piece_list; entry::list_type& l = piece_list.list(); std::map merkle_node_list = t->torrent_file().build_merkle_list(r.piece); for (std::map::iterator i = merkle_node_list.begin() , end(merkle_node_list.end()); i != end; ++i) { l.push_back(entry(entry::list_t)); l.back().list().push_back(i->first); l.back().list().push_back(i->second.to_string()); } bencode(std::back_inserter(piece_list_buf), piece_list); detail::write_int32(piece_list_buf.size(), ptr); // back-patch the length field char* ptr2 = msg; detail::write_int32(r.length + 1 + 4 + 4 + 4 + piece_list_buf.size() , ptr2); send_buffer(msg, 17); send_buffer(&piece_list_buf[0], piece_list_buf.size()); } else { send_buffer(msg, 13); } if (buffer.ref().storage == 0) { append_send_buffer(buffer.get(), r.length , &buffer_free_disk_buf, &m_allocator); } else { append_const_send_buffer(buffer.get(), r.length , &buffer_reclaim_block, &m_allocator, buffer.ref()); } buffer.release(); m_payloads.push_back(range(send_buffer_size() - r.length, r.length)); setup_send(); stats_counters().inc_stats_counter(counters::num_outgoing_piece); } // -------------------------- // RECEIVE DATA // -------------------------- void bt_peer_connection::on_receive(error_code const& error , std::size_t bytes_transferred) { INVARIANT_CHECK; if (error) { received_bytes(0, bytes_transferred); return; } // make sure are much as possible of the response ends up in the same // packet, or at least back-to-back packets cork c_(*this); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) if (!m_enc_handler.is_recv_plaintext()) { int consumed = m_enc_handler.decrypt(m_recv_buffer, bytes_transferred); #ifndef TORRENT_DISABLE_LOGGING if (consumed + bytes_transferred > 0) peer_log(peer_log_alert::incoming_message, "ENCRYPTION" , "decrypted block s = %d", int(consumed + bytes_transferred)); #endif if (bytes_transferred == SIZE_MAX) { disconnect(errors::parse_failed, op_encryption); return; } received_bytes(0, consumed); int sub_transferred = 0; while (bytes_transferred > 0 && ((sub_transferred = m_recv_buffer.advance_pos(bytes_transferred)) > 0)) { #if TORRENT_USE_ASSERTS boost::int64_t cur_payload_dl = m_statistics.last_payload_downloaded(); boost::int64_t cur_protocol_dl = m_statistics.last_protocol_downloaded(); #endif on_receive_impl(sub_transferred); bytes_transferred -= sub_transferred; TORRENT_ASSERT(sub_transferred > 0); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_statistics.last_payload_downloaded() - cur_payload_dl >= 0); TORRENT_ASSERT(m_statistics.last_protocol_downloaded() - cur_protocol_dl >= 0); boost::int64_t stats_diff = m_statistics.last_payload_downloaded() - cur_payload_dl + m_statistics.last_protocol_downloaded() - cur_protocol_dl; TORRENT_ASSERT(stats_diff == int(sub_transferred)); #endif if (m_disconnecting) return; } } else #endif on_receive_impl(bytes_transferred); } void bt_peer_connection::on_receive_impl(std::size_t bytes_transferred) { boost::shared_ptr t = associated_torrent().lock(); buffer::const_interval recv_buffer = m_recv_buffer.get(); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // m_state is set to read_pe_dhkey in initial state // (read_protocol_identifier) for incoming, or in constructor // for outgoing if (m_state == read_pe_dhkey) { received_bytes(0, bytes_transferred); TORRENT_ASSERT(!m_encrypted); TORRENT_ASSERT(!m_rc4_encrypted); TORRENT_ASSERT(m_recv_buffer.packet_size() == dh_key_len); TORRENT_ASSERT(recv_buffer == m_recv_buffer.get()); if (!m_recv_buffer.packet_finished()) return; // write our dh public key. m_dh_key_exchange is // initialized in write_pe1_2_dhkey() if (!is_outgoing()) write_pe1_2_dhkey(); if (is_disconnecting()) return; // read dh key, generate shared secret if (m_dh_key_exchange->compute_secret(recv_buffer.begin) != 0) { disconnect(errors::no_memory, op_encryption); return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION", "received DH key"); #endif // PadA/B can be a max of 512 bytes, and 20 bytes more for // the sync hash (if incoming), or 8 bytes more for the // encrypted verification constant (if outgoing). Instead // of requesting the maximum possible, request the maximum // possible to ensure we do not overshoot the standard // handshake. if (is_outgoing()) { m_state = read_pe_syncvc; write_pe3_sync(); // initial payload is the standard handshake, this is // always rc4 if sent here. m_rc4_encrypted is flagged // again according to peer selection. switch_send_crypto(m_rc4); write_handshake(); switch_send_crypto(boost::shared_ptr()); // vc,crypto_select,len(pad),pad, encrypt(handshake) // 8+4+2+0+handshake_len m_recv_buffer.reset(8+4+2+0+handshake_len); } else { // already written dh key m_state = read_pe_synchash; // synchash,skeyhash,vc,crypto_provide,len(pad),pad,encrypt(handshake) m_recv_buffer.reset(20+20+8+4+2+0+handshake_len); } TORRENT_ASSERT(!m_recv_buffer.packet_finished()); return; } // cannot fall through into if (m_state == read_pe_synchash) { TORRENT_ASSERT(!m_encrypted); TORRENT_ASSERT(!m_rc4_encrypted); TORRENT_ASSERT(!is_outgoing()); TORRENT_ASSERT(recv_buffer == m_recv_buffer.get()); if (recv_buffer.left() < 20) { received_bytes(0, bytes_transferred); if (m_recv_buffer.packet_finished()) disconnect(errors::sync_hash_not_found, op_bittorrent, 1); return; } if (!m_sync_hash.get()) { TORRENT_ASSERT(m_sync_bytes_read == 0); hasher h; // compute synchash (hash('req1',S)) h.update("req1", 4); h.update(m_dh_key_exchange->get_secret(), dh_key_len); m_sync_hash.reset(new (std::nothrow) sha1_hash(h.final())); if (!m_sync_hash) { received_bytes(0, bytes_transferred); disconnect(errors::no_memory, op_encryption); return; } } int syncoffset = get_syncoffset(m_sync_hash->data(), 20 , recv_buffer.begin, recv_buffer.left()); // No sync if (syncoffset == -1) { received_bytes(0, bytes_transferred); std::size_t bytes_processed = recv_buffer.left() - 20; m_sync_bytes_read += bytes_processed; if (m_sync_bytes_read >= 512) { disconnect(errors::sync_hash_not_found, op_encryption, 1); return; } m_recv_buffer.cut(bytes_processed, (std::min)(m_recv_buffer.packet_size() , (512+20) - m_sync_bytes_read)); TORRENT_ASSERT(!m_recv_buffer.packet_finished()); return; } // found complete sync else { std::size_t bytes_processed = syncoffset + 20; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION" , "sync point (hash) found at offset %d" , int(m_sync_bytes_read + bytes_processed - 20)); #endif m_state = read_pe_skey_vc; // skey,vc - 28 bytes m_sync_hash.reset(); int transferred_used = bytes_processed - recv_buffer.left() + bytes_transferred; TORRENT_ASSERT(transferred_used <= int(bytes_transferred)); received_bytes(0, transferred_used); bytes_transferred -= transferred_used; m_recv_buffer.cut(bytes_processed, 28); } } if (m_state == read_pe_skey_vc) { received_bytes(0, bytes_transferred); bytes_transferred = 0; TORRENT_ASSERT(!m_encrypted); TORRENT_ASSERT(!m_rc4_encrypted); TORRENT_ASSERT(!is_outgoing()); TORRENT_ASSERT(m_recv_buffer.packet_size() == 28); if (!m_recv_buffer.packet_finished()) return; if (is_disconnecting()) return; TORRENT_ASSERT(!is_disconnecting()); recv_buffer = m_recv_buffer.get(); TORRENT_ASSERT(!is_disconnecting()); sha1_hash ih(recv_buffer.begin); torrent const* ti = m_ses.find_encrypted_torrent(ih, m_dh_key_exchange->get_hash_xor_mask()); if (ti) { if (!t) { attach_to_torrent(ti->info_hash()); if (is_disconnecting()) return; TORRENT_ASSERT(!is_disconnecting()); t = associated_torrent().lock(); TORRENT_ASSERT(t); } init_pe_rc4_handler(m_dh_key_exchange->get_secret(), ti->info_hash()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION", "stream key found, torrent located"); #endif } if (!m_rc4.get()) { disconnect(errors::invalid_info_hash, op_bittorrent, 1); return; } // verify constant buffer::interval wr_recv_buf = m_recv_buffer.mutable_buffer(); rc4_decrypt(wr_recv_buf.begin + 20, 8); wr_recv_buf.begin += 28; static const char sh_vc[] = {0,0,0,0, 0,0,0,0}; if (!std::equal(sh_vc, sh_vc+8, recv_buffer.begin + 20)) { disconnect(errors::invalid_encryption_constant, op_encryption, 2); return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION", "verification constant found"); #endif m_state = read_pe_cryptofield; m_recv_buffer.reset(4 + 2); } // cannot fall through into if (m_state == read_pe_syncvc) { TORRENT_ASSERT(is_outgoing()); TORRENT_ASSERT(!m_encrypted); TORRENT_ASSERT(!m_rc4_encrypted); TORRENT_ASSERT(recv_buffer == m_recv_buffer.get()); if (recv_buffer.left() < 8) { received_bytes(0, bytes_transferred); if (m_recv_buffer.packet_finished()) disconnect(errors::invalid_encryption_constant, op_encryption, 2); return; } // generate the verification constant if (!m_sync_vc.get()) { TORRENT_ASSERT(m_sync_bytes_read == 0); m_sync_vc.reset(new (std::nothrow) char[8]); if (!m_sync_vc) { disconnect(errors::no_memory, op_encryption); return; } std::fill(m_sync_vc.get(), m_sync_vc.get() + 8, 0); rc4_decrypt(m_sync_vc.get(), 8); } TORRENT_ASSERT(m_sync_vc.get()); int syncoffset = get_syncoffset(m_sync_vc.get(), 8 , recv_buffer.begin, recv_buffer.left()); // No sync if (syncoffset == -1) { std::size_t bytes_processed = recv_buffer.left() - 8; m_sync_bytes_read += bytes_processed; received_bytes(0, bytes_transferred); if (m_sync_bytes_read >= 512) { disconnect(errors::invalid_encryption_constant, op_encryption, 2); return; } m_recv_buffer.cut(bytes_processed, (std::min)(m_recv_buffer.packet_size() , (512+8) - m_sync_bytes_read)); TORRENT_ASSERT(!m_recv_buffer.packet_finished()); } // found complete sync else { std::size_t bytes_processed = syncoffset + 8; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION" , "sync point (verification constant) found at offset %d" , int(m_sync_bytes_read + bytes_processed - 8)); #endif int transferred_used = bytes_processed - recv_buffer.left() + bytes_transferred; TORRENT_ASSERT(transferred_used <= int(bytes_transferred)); received_bytes(0, transferred_used); bytes_transferred -= transferred_used; m_recv_buffer.cut(bytes_processed, 4 + 2); // delete verification constant m_sync_vc.reset(); m_state = read_pe_cryptofield; // fall through } } if (m_state == read_pe_cryptofield) // local/remote { TORRENT_ASSERT(!m_encrypted); TORRENT_ASSERT(!m_rc4_encrypted); TORRENT_ASSERT(m_recv_buffer.packet_size() == 4+2); received_bytes(0, bytes_transferred); bytes_transferred = 0; if (!m_recv_buffer.packet_finished()) return; buffer::interval wr_buf = m_recv_buffer.mutable_buffer(); rc4_decrypt(wr_buf.begin, m_recv_buffer.packet_size()); recv_buffer = m_recv_buffer.get(); boost::uint32_t crypto_field = detail::read_uint32(recv_buffer.begin); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION", "crypto %s : [%s%s ]" , is_outgoing() ? "select" : "provide" , (crypto_field & 1) ? " plaintext" : "" , (crypto_field & 2) ? " rc4" : ""); #endif if (!is_outgoing()) { // select a crypto method int allowed_encryption = m_settings.get_int(settings_pack::allowed_enc_level); boost::uint32_t crypto_select = crypto_field & allowed_encryption; // when prefer_rc4 is set, keep the most significant bit // otherwise keep the least significant one if (m_settings.get_bool(settings_pack::prefer_rc4)) { boost::uint32_t mask = (std::numeric_limits::max)(); while (crypto_select & (mask << 1)) { mask <<= 1; crypto_select = crypto_select & mask; } } else { boost::uint32_t mask = (std::numeric_limits::max)(); while (crypto_select & (mask >> 1)) { mask >>= 1; crypto_select = crypto_select & mask; } } if (crypto_select == 0) { disconnect(errors::unsupported_encryption_mode, op_encryption, 1); return; } // write the pe4 step write_pe4_sync(crypto_select); } else // is_outgoing() { // check if crypto select is valid int allowed_encryption = m_settings.get_int(settings_pack::allowed_enc_level); crypto_field &= allowed_encryption; if (crypto_field == 0) { // we don't allow any of the offered encryption levels disconnect(errors::unsupported_encryption_mode_selected, op_encryption, 2); return; } if (crypto_field == settings_pack::pe_plaintext) m_rc4_encrypted = false; else if (crypto_field == settings_pack::pe_rc4) m_rc4_encrypted = true; } int len_pad = detail::read_int16(recv_buffer.begin); if (len_pad < 0 || len_pad > 512) { disconnect(errors::invalid_pad_size, op_encryption, 2); return; } m_state = read_pe_pad; if (!is_outgoing()) m_recv_buffer.reset(len_pad + 2); // len(IA) at the end of pad else { if (len_pad == 0) { m_encrypted = true; if (m_rc4_encrypted) { switch_send_crypto(m_rc4); switch_recv_crypto(m_rc4); } m_state = init_bt_handshake; } else m_recv_buffer.reset(len_pad); } } if (m_state == read_pe_pad) { TORRENT_ASSERT(!m_encrypted); received_bytes(0, bytes_transferred); bytes_transferred = 0; if (!m_recv_buffer.packet_finished()) return; int pad_size = is_outgoing() ? m_recv_buffer.packet_size() : m_recv_buffer.packet_size() - 2; buffer::interval wr_buf = m_recv_buffer.mutable_buffer(); rc4_decrypt(wr_buf.begin, m_recv_buffer.packet_size()); recv_buffer = m_recv_buffer.get(); if (!is_outgoing()) { recv_buffer.begin += pad_size; int len_ia = detail::read_int16(recv_buffer.begin); if (len_ia < 0) { disconnect(errors::invalid_encrypt_handshake, op_encryption, 2); return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION", "len(IA) : %d", len_ia); #endif if (len_ia == 0) { // everything after this is Encrypt2 m_encrypted = true; if (m_rc4_encrypted) { switch_send_crypto(m_rc4); switch_recv_crypto(m_rc4); } m_state = init_bt_handshake; } else { m_state = read_pe_ia; m_recv_buffer.reset(len_ia); } } else // is_outgoing() { // everything that arrives after this is Encrypt2 m_encrypted = true; if (m_rc4_encrypted) { switch_send_crypto(m_rc4); switch_recv_crypto(m_rc4); } m_state = init_bt_handshake; } } if (m_state == read_pe_ia) { received_bytes(0, bytes_transferred); bytes_transferred = 0; TORRENT_ASSERT(!is_outgoing()); TORRENT_ASSERT(!m_encrypted); if (!m_recv_buffer.packet_finished()) return; // ia is always rc4, so decrypt it buffer::interval wr_buf = m_recv_buffer.mutable_buffer(); rc4_decrypt(wr_buf.begin, m_recv_buffer.packet_size()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION" , "decrypted ia : %d bytes", m_recv_buffer.packet_size()); #endif // everything that arrives after this is encrypted m_encrypted = true; if (m_rc4_encrypted) { switch_send_crypto(m_rc4); switch_recv_crypto(m_rc4); } m_rc4.reset(); m_state = read_protocol_identifier; m_recv_buffer.cut(0, 20); } if (m_state == init_bt_handshake) { received_bytes(0, bytes_transferred); bytes_transferred = 0; TORRENT_ASSERT(m_encrypted); // decrypt remaining received bytes if (m_rc4_encrypted) { buffer::interval wr_buf = m_recv_buffer.mutable_buffer(); wr_buf.begin += m_recv_buffer.packet_size(); rc4_decrypt(wr_buf.begin, wr_buf.left()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION" , "decrypted remaining %d bytes", wr_buf.left()); #endif } m_rc4.reset(); // payload stream, start with 20 handshake bytes m_state = read_protocol_identifier; m_recv_buffer.reset(20); // encrypted portion of handshake completed, toggle // peer_info pe_support flag back to true if (is_outgoing() && m_settings.get_int(settings_pack::out_enc_policy) == settings_pack::pe_enabled) { torrent_peer* pi = peer_info_struct(); TORRENT_ASSERT(pi); pi->pe_support = true; } } #endif // #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) if (m_state == read_protocol_identifier) { received_bytes(0, bytes_transferred); bytes_transferred = 0; TORRENT_ASSERT(m_recv_buffer.packet_size() == 20); if (!m_recv_buffer.packet_finished()) return; recv_buffer = m_recv_buffer.get(); int packet_size = recv_buffer[0]; static const char protocol_string[] = "\x13" "BitTorrent protocol"; if (packet_size != 19 || memcmp(recv_buffer.begin, protocol_string, 20) != 0) { #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION" , "unrecognized protocol header"); #endif #ifdef TORRENT_USE_OPENSSL if (is_ssl(*get_socket())) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION" , "SSL peers are not allowed to use any other encryption"); #endif disconnect(errors::invalid_info_hash, op_bittorrent, 1); return; } #endif // TORRENT_USE_OPENSSL if (!is_outgoing() && m_settings.get_int(settings_pack::in_enc_policy) == settings_pack::pe_disabled) { disconnect(errors::no_incoming_encrypted, op_bittorrent); return; } // Don't attempt to perform an encrypted handshake // within an encrypted connection. For local connections, // we're expected to already have passed the encrypted // handshake by this point if (m_encrypted || is_outgoing()) { disconnect(errors::invalid_info_hash, op_bittorrent, 1); return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ENCRYPTION", "attempting encrypted connection"); #endif m_state = read_pe_dhkey; m_recv_buffer.cut(0, dh_key_len); TORRENT_ASSERT(!m_recv_buffer.packet_finished()); return; #else disconnect(errors::invalid_info_hash, op_bittorrent, 1); return; #endif // TORRENT_DISABLE_ENCRYPTION } else { #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) TORRENT_ASSERT(m_state != read_pe_dhkey); if (!is_outgoing() && m_settings.get_int(settings_pack::in_enc_policy) == settings_pack::pe_forced && !m_encrypted && !is_ssl(*get_socket())) { disconnect(errors::no_incoming_regular, op_bittorrent); return; } #endif #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HANDSHAKE", "BitTorrent protocol"); #endif } m_state = read_info_hash; m_recv_buffer.reset(28); } // fall through if (m_state == read_info_hash) { received_bytes(0, bytes_transferred); bytes_transferred = 0; TORRENT_ASSERT(m_recv_buffer.packet_size() == 28); if (!m_recv_buffer.packet_finished()) return; recv_buffer = m_recv_buffer.get(); #ifndef TORRENT_DISABLE_LOGGING std::string extensions; extensions.resize(8 * 8); for (int i=0; i < 8; ++i) { for (int j=0; j < 8; ++j) { if (recv_buffer[i] & (0x80 >> j)) extensions[i*8+j] = '1'; else extensions[i*8+j] = '0'; } } peer_log(peer_log_alert::incoming_message, "EXTENSIONS", "%s ext: %s%s%s" , extensions.c_str() , (recv_buffer[7] & 0x01) ? "DHT " : "" , (recv_buffer[7] & 0x04) ? "FAST " : "" , (recv_buffer[5] & 0x10) ? "extension " : ""); #endif #ifndef TORRENT_DISABLE_EXTENSIONS std::memcpy(m_reserved_bits, recv_buffer.begin, 8); if ((recv_buffer[5] & 0x10)) m_supports_extensions = true; #endif if (recv_buffer[7] & 0x01) m_supports_dht_port = true; if (recv_buffer[7] & 0x04) m_supports_fast = true; t = associated_torrent().lock(); // 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 , info_hash.data()); attach_to_torrent(info_hash); if (is_disconnecting()) return; } else { // verify info hash if (!std::equal(recv_buffer.begin + 8, recv_buffer.begin + 28 , t->torrent_file().info_hash().data())) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ERROR", "received invalid info_hash"); #endif disconnect(errors::invalid_info_hash, op_bittorrent, 1); return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "HANDSHAKE", "info_hash received"); #endif } t = associated_torrent().lock(); TORRENT_ASSERT(t); // if this is a local connection, we have already // sent the handshake if (!is_outgoing()) write_handshake(); TORRENT_ASSERT(m_sent_handshake); if (is_disconnecting()) return; m_state = read_peer_id; m_recv_buffer.reset(20); } // fall through if (m_state == read_peer_id) { TORRENT_ASSERT(m_sent_handshake); received_bytes(0, bytes_transferred); t = associated_torrent().lock(); if (!t) { TORRENT_ASSERT(!m_recv_buffer.packet_finished()); // TODO return; } TORRENT_ASSERT(m_recv_buffer.packet_size() == 20); if (!m_recv_buffer.packet_finished()) return; recv_buffer = m_recv_buffer.get(); #ifndef TORRENT_DISABLE_LOGGING { char hex_pid[41]; to_hex(recv_buffer.begin, 20, hex_pid); hex_pid[40] = 0; char ascii_pid[21]; ascii_pid[20] = 0; for (int i = 0; i != 20; ++i) { if (is_print(recv_buffer.begin[i])) ascii_pid[i] = recv_buffer.begin[i]; else ascii_pid[i] = '.'; } peer_log(peer_log_alert::incoming, "HANDSHAKE", "received peer_id: %s client: %s ascii: \"%s\"" , hex_pid, identify_client(peer_id(recv_buffer.begin)).c_str(), ascii_pid); } #endif peer_id pid; std::copy(recv_buffer.begin, recv_buffer.begin + 20, pid.data()); if (t->settings().get_bool(settings_pack::allow_multiple_connections_per_ip)) { // now, let's see if this connection should be closed peer_connection* p = t->find_peer(pid); if (p) { TORRENT_ASSERT(p->pid() == pid); // we found another connection with the same peer-id // which connection should be closed in order to be // sure that the other end closes the same connection? // the peer with greatest peer-id is the one allowed to // initiate connections. So, if our peer-id is greater than // the others, we should close the incoming connection, // if not, we should close the outgoing one. if ((pid < m_our_peer_id) == is_outgoing()) { p->disconnect(errors::duplicate_peer_id, op_bittorrent); } else { disconnect(errors::duplicate_peer_id, op_bittorrent); return; } } } set_pid(pid); m_client_version = identify_client(pid); if (pid[0] == '-' && pid[1] == 'B' && pid[2] == 'C' && pid[7] == '-') { // if this is a bitcomet client, lower the request queue size limit if (max_out_request_queue() > 50) max_out_request_queue(50); } #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end;) { if (!(*i)->on_handshake(m_reserved_bits)) { i = m_extensions.erase(i); } else { ++i; } } if (is_disconnecting()) return; if (m_supports_extensions) write_extensions(); #endif #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HANDSHAKE", "connection ready"); #endif // consider this a successful connection, reset the failcount if (peer_info_struct()) t->clear_failcount(peer_info_struct()); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // Toggle pe_support back to false if this is a // standard successful connection if (is_outgoing() && !m_encrypted && m_settings.get_int(settings_pack::out_enc_policy) == settings_pack::pe_enabled) { torrent_peer* pi = peer_info_struct(); TORRENT_ASSERT(pi); pi->pe_support = false; } #endif // complete the handshake // we don't know how many pieces there are until we // have the metadata if (t->ready_for_connections()) { write_bitfield(); #ifndef TORRENT_DISABLE_DHT if (m_supports_dht_port && m_ses.has_dht()) write_dht_port(m_ses.external_udp_port()); #endif // if we don't have any pieces, don't do any preemptive // unchoking at all. if (t->num_have() > 0) { // if the peer is ignoring unchoke slots, or if we have enough // unused slots, unchoke this peer right away, to save a round-trip // in case it's interested. maybe_unchoke_this_peer(); } } m_state = read_packet_size; m_recv_buffer.reset(5); TORRENT_ASSERT(!m_recv_buffer.packet_finished()); return; } // cannot fall through into if (m_state == read_packet_size) { // Make sure this is not fallen though into TORRENT_ASSERT(recv_buffer == m_recv_buffer.get()); TORRENT_ASSERT(m_recv_buffer.packet_size() == 5); if (!t) return; // the 5th byte (if one) should not count as protocol // byte here, instead it's counted in the message // handler itself, for the specific message TORRENT_ASSERT(bytes_transferred <= 5); int used_bytes = recv_buffer.left() > 4 ? bytes_transferred - 1: bytes_transferred; received_bytes(0, used_bytes); bytes_transferred -= used_bytes; if (recv_buffer.left() < 4) return; TORRENT_ASSERT(bytes_transferred <= 1); 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 received_bytes(0, bytes_transferred); disconnect(errors::packet_too_large, op_bittorrent, 2); return; } if (packet_size == 0) { TORRENT_ASSERT(bytes_transferred <= 1); received_bytes(0, bytes_transferred); incoming_keepalive(); if (is_disconnecting()) return; // keepalive message m_state = read_packet_size; m_recv_buffer.cut(4, 5); return; } if (recv_buffer.left() < 5) return; m_state = read_packet; m_recv_buffer.cut(4, packet_size); recv_buffer = m_recv_buffer.get(); TORRENT_ASSERT(recv_buffer.left() == 1); TORRENT_ASSERT(bytes_transferred == 1); } if (m_state == read_packet) { TORRENT_ASSERT(recv_buffer == m_recv_buffer.get()); if (!t) { received_bytes(0, bytes_transferred); disconnect(errors::torrent_removed, op_bittorrent, 1); return; } #if TORRENT_USE_ASSERTS boost::int64_t cur_payload_dl = statistics().last_payload_downloaded(); boost::int64_t cur_protocol_dl = statistics().last_protocol_downloaded(); #endif if (dispatch_message(bytes_transferred)) { m_state = read_packet_size; m_recv_buffer.reset(5); } #if TORRENT_USE_ASSERTS TORRENT_ASSERT(statistics().last_payload_downloaded() - cur_payload_dl >= 0); TORRENT_ASSERT(statistics().last_protocol_downloaded() - cur_protocol_dl >= 0); boost::int64_t stats_diff = statistics().last_payload_downloaded() - cur_payload_dl + statistics().last_protocol_downloaded() - cur_protocol_dl; TORRENT_ASSERT(stats_diff == boost::int64_t(bytes_transferred)); TORRENT_ASSERT(!m_recv_buffer.packet_finished()); #endif return; } TORRENT_ASSERT(!m_recv_buffer.packet_finished()); } #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) int bt_peer_connection::hit_send_barrier(std::vector& iovec) { int next_barrier = m_enc_handler.encrypt(iovec); #ifndef TORRENT_DISABLE_LOGGING if (next_barrier != 0) peer_log(peer_log_alert::outgoing, "SEND_BARRIER" , "encrypted block s = %d", next_barrier); #endif return next_barrier; } #endif // -------------------------- // SEND DATA // -------------------------- void bt_peer_connection::on_sent(error_code const& error , std::size_t bytes_transferred) { INVARIANT_CHECK; if (error) { sent_bytes(0, bytes_transferred); return; } // manage the payload markers int amount_payload = 0; if (!m_payloads.empty()) { // this points to the first entry to not erase. i.e. // [begin, first_to_keep) will be erased because // the payload ranges they represent have been sent std::vector::iterator first_to_keep = m_payloads.begin(); for (std::vector::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; TORRENT_ASSERT(first_to_keep == i); ++first_to_keep; } else { amount_payload += -i->start; i->length -= -i->start; i->start = 0; } } } // remove all payload ranges that have been sent m_payloads.erase(m_payloads.begin(), first_to_keep); } TORRENT_ASSERT(amount_payload <= int(bytes_transferred)); sent_bytes(amount_payload, bytes_transferred - amount_payload); if (amount_payload > 0) { boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); if (t) t->update_last_upload(); } } #if TORRENT_USE_INVARIANT_CHECKS void bt_peer_connection::check_invariant() const { boost::shared_ptr t = associated_torrent().lock(); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) TORRENT_ASSERT( (bool(m_state != read_pe_dhkey) || m_dh_key_exchange.get()) || !is_outgoing()); TORRENT_ASSERT(!m_rc4_encrypted || (!m_encrypted && m_rc4) || (m_encrypted && !m_enc_handler.is_send_plaintext())); #endif if (!in_handshake()) { TORRENT_ASSERT(m_sent_handshake); } if (!m_payloads.empty()) { for (std::vector::const_iterator i = m_payloads.begin(); i != m_payloads.end() - 1; ++i) { TORRENT_ASSERT(i->start + i->length <= (i+1)->start); } } } #endif } libtorrent-rasterbar-1.1.13/src/chained_buffer.cpp000066400000000000000000000130341351156116000221630ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/chained_buffer.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { void chained_buffer::pop_front(int bytes_to_pop) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(bytes_to_pop <= m_bytes); while (bytes_to_pop > 0 && !m_vec.empty()) { buffer_t& b = m_vec.front(); if (b.used_size > bytes_to_pop) { b.start += bytes_to_pop; b.used_size -= bytes_to_pop; m_bytes -= bytes_to_pop; TORRENT_ASSERT(m_bytes <= m_capacity); TORRENT_ASSERT(m_bytes >= 0); TORRENT_ASSERT(m_capacity >= 0); break; } b.free_fun(b.buf, b.userdata, b.ref); m_bytes -= b.used_size; m_capacity -= b.size; bytes_to_pop -= b.used_size; TORRENT_ASSERT(m_bytes >= 0); TORRENT_ASSERT(m_capacity >= 0); TORRENT_ASSERT(m_bytes <= m_capacity); m_vec.pop_front(); } } void chained_buffer::append_buffer(char* buffer, int s, int used_size , free_buffer_fun destructor, void* userdata , block_cache_reference ref) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(s >= used_size); buffer_t b; b.buf = buffer; b.size = s; b.start = buffer; b.used_size = used_size; b.free_fun = destructor; b.userdata = userdata; b.ref = ref; m_vec.push_back(b); m_bytes += used_size; m_capacity += s; TORRENT_ASSERT(m_bytes <= m_capacity); } void chained_buffer::prepend_buffer(char* buffer, int s, int used_size , free_buffer_fun destructor, void* userdata , block_cache_reference ref) { TORRENT_ASSERT(s >= used_size); buffer_t b; b.buf = buffer; b.size = s; b.start = buffer; b.used_size = used_size; b.free_fun = destructor; b.userdata = userdata; b.ref = ref; m_vec.push_front(b); m_bytes += used_size; m_capacity += s; TORRENT_ASSERT(m_bytes <= m_capacity); } // returns the number of bytes available at the // end of the last chained buffer. int chained_buffer::space_in_last_buffer() { TORRENT_ASSERT(is_single_thread()); if (m_vec.empty()) return 0; buffer_t& b = m_vec.back(); return b.size - b.used_size - (b.start - b.buf); } // tries to copy the given buffer to the end of the // last chained buffer. If there's not enough room // it returns false char* chained_buffer::append(char const* buf, int s) { TORRENT_ASSERT(is_single_thread()); char* insert = allocate_appendix(s); if (insert == 0) return 0; memcpy(insert, buf, s); return insert; } // tries to allocate memory from the end // of the last buffer. If there isn't // enough room, returns 0 char* chained_buffer::allocate_appendix(int s) { TORRENT_ASSERT(is_single_thread()); if (m_vec.empty()) return 0; buffer_t& b = m_vec.back(); char* insert = b.start + b.used_size; if (insert + s > b.buf + b.size) return 0; b.used_size += s; m_bytes += s; TORRENT_ASSERT(m_bytes <= m_capacity); return insert; } std::vector const& chained_buffer::build_iovec(int to_send) { TORRENT_ASSERT(is_single_thread()); m_tmp_vec.clear(); build_vec(to_send, m_tmp_vec); return m_tmp_vec; } void chained_buffer::build_mutable_iovec(int bytes, std::vector &vec) { build_vec(bytes, vec); } template void chained_buffer::build_vec(int bytes, std::vector &vec) { for (std::deque::iterator i = m_vec.begin() , end(m_vec.end()); bytes > 0 && i != end; ++i) { if (i->used_size > bytes) { TORRENT_ASSERT(bytes > 0); vec.push_back(Buffer(i->start, bytes)); break; } TORRENT_ASSERT(i->used_size > 0); vec.push_back(Buffer(i->start, i->used_size)); bytes -= i->used_size; } } void chained_buffer::clear() { for (std::deque::iterator i = m_vec.begin() , end(m_vec.end()); i != end; ++i) { i->free_fun(i->buf, i->userdata, i->ref); } m_bytes = 0; m_capacity = 0; m_vec.clear(); } chained_buffer::~chained_buffer() { #if TORRENT_USE_ASSERTS TORRENT_ASSERT(!m_destructed); m_destructed = true; #endif TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_bytes >= 0); TORRENT_ASSERT(m_capacity >= 0); clear(); } } libtorrent-rasterbar-1.1.13/src/choker.cpp000066400000000000000000000362261351156116000205220ustar00rootroot00000000000000/* Copyright (c) 2014-2018, 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/choker.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/aux_/time.hpp" #include "libtorrent/torrent.hpp" #include namespace libtorrent { namespace { // return true if 'lhs' peer should be preferred to be unchoke over 'rhs' bool unchoke_compare_rr(peer_connection const* lhs , peer_connection const* rhs, int pieces) { // if one peer belongs to a higher priority torrent than the other one // that one should be unchoked. boost::shared_ptr t1 = lhs->associated_torrent().lock(); TORRENT_ASSERT(t1); boost::shared_ptr t2 = rhs->associated_torrent().lock(); TORRENT_ASSERT(t2); int prio1 = lhs->get_priority(peer_connection::upload_channel); int prio2 = rhs->get_priority(peer_connection::upload_channel); if (prio1 != prio2) return prio1 > prio2; // compare how many bytes they've sent us boost::int64_t c1; boost::int64_t c2; c1 = lhs->downloaded_in_last_round(); c2 = rhs->downloaded_in_last_round(); if (c1 != c2) return c1 > c2; // when seeding, rotate which peer is unchoked in a round-robin fasion // the amount uploaded since unchoked (not just in the last round) c1 = lhs->uploaded_since_unchoked(); c2 = rhs->uploaded_since_unchoked(); // the way the round-robin unchoker works is that it, // by default, prioritizes any peer that is already unchoked. // this maintain the status quo across unchoke rounds. However, // peers that are unchoked, but have sent more than one quota // since they were unchoked, they get de-prioritized. // if a peer is already unchoked, the number of bytes sent since it was unchoked // is greater than the send quanta, and it has been unchoked for at least one minute // then it's done with its upload slot, and we can de-prioritize it bool c1_quota_complete = !lhs->is_choked() && c1 > boost::int64_t(t1->torrent_file().piece_length()) * pieces && aux::time_now() - lhs->time_of_last_unchoke() > minutes(1); bool c2_quota_complete = !rhs->is_choked() && c2 > boost::int64_t(t2->torrent_file().piece_length()) * pieces && aux::time_now() - rhs->time_of_last_unchoke() > minutes(1); // if c2 has completed a quanta, it should be de-prioritized // and vice versa if (c1_quota_complete < c2_quota_complete) return true; if (c1_quota_complete > c2_quota_complete) return false; // when seeding, prefer the peer we're uploading the fastest to // force the upload rate to zero for choked peers because // if the peers just got choked the previous round // there may have been a residual transfer which was already // in-flight at the time and we don't want that to cause the peer // to be ranked at the top of the choked peers c1 = lhs->is_choked() ? 0 : lhs->uploaded_in_last_round(); c2 = rhs->is_choked() ? 0 : rhs->uploaded_in_last_round(); if (c1 > c2) return true; if (c2 > c1) return false; // if the peers are still identical (say, they're both waiting to be unchoked) // prioritize the one that has waited the longest to be unchoked // the round-robin unchoker relies on this logic. Don't change it // without moving this into that unchoker logic return lhs->time_of_last_unchoke() < rhs->time_of_last_unchoke(); } // return true if 'lhs' peer should be preferred to be unchoke over 'rhs' bool unchoke_compare_fastest_upload(peer_connection const* lhs , peer_connection const* rhs) { // if one peer belongs to a higher priority torrent than the other one // that one should be unchoked. boost::shared_ptr t1 = lhs->associated_torrent().lock(); TORRENT_ASSERT(t1); boost::shared_ptr t2 = rhs->associated_torrent().lock(); TORRENT_ASSERT(t2); int prio1 = lhs->get_priority(peer_connection::upload_channel); int prio2 = rhs->get_priority(peer_connection::upload_channel); if (prio1 != prio2) return prio1 > prio2; // compare how many bytes they've sent us boost::int64_t c1; boost::int64_t c2; c1 = lhs->downloaded_in_last_round(); c2 = rhs->downloaded_in_last_round(); if (c1 != c2) return c1 > c2; // when seeding, prefer the peer we're uploading the fastest to c1 = lhs->uploaded_in_last_round(); c2 = rhs->uploaded_in_last_round(); if (c1 > c2) return true; if (c2 > c1) return false; // prioritize the one that has waited the longest to be unchoked // the round-robin unchoker relies on this logic. Don't change it // without moving this into that unchoker logic return lhs->time_of_last_unchoke() < rhs->time_of_last_unchoke(); } // return true if 'lhs' peer should be preferred to be unchoke over 'rhs' bool unchoke_compare_anti_leech(peer_connection const* lhs , peer_connection const* rhs) { // if one peer belongs to a higher priority torrent than the other one // that one should be unchoked. boost::shared_ptr t1 = lhs->associated_torrent().lock(); TORRENT_ASSERT(t1); boost::shared_ptr t2 = rhs->associated_torrent().lock(); TORRENT_ASSERT(t2); int prio1 = lhs->get_priority(peer_connection::upload_channel); int prio2 = rhs->get_priority(peer_connection::upload_channel); if (prio1 != prio2) return prio1 > prio2; // compare how many bytes they've sent us boost::int64_t c1; boost::int64_t c2; c1 = lhs->downloaded_in_last_round(); c2 = rhs->downloaded_in_last_round(); if (c1 != c2) return c1 > c2; // the anti-leech seeding algorithm is based on the paper "Improving // BitTorrent: A Simple Approach" from Chow et. al. and ranks peers based // on how many pieces they have, preferring to unchoke peers that just // started and peers that are close to completing. Like this: // ^ // | \ / | // | \ / | // | \ / | // s | \ / | // c | \ / | // o | \ / | // r | \ / | // e | \ / | // | \ / | // | \ / | // | \ / | // | \ / | // | V | // +---------------------------+ // 0% num have pieces 100% int t1_total = t1->torrent_file().num_pieces(); int t2_total = t2->torrent_file().num_pieces(); int score1 = (lhs->num_have_pieces() < t1_total / 2 ? t1_total - lhs->num_have_pieces() : lhs->num_have_pieces()) * 1000 / t1_total; int score2 = (rhs->num_have_pieces() < t2_total / 2 ? t2_total - rhs->num_have_pieces() : rhs->num_have_pieces()) * 1000 / t2_total; if (score1 > score2) return true; if (score2 > score1) return false; // prioritize the one that has waited the longest to be unchoked // the round-robin unchoker relies on this logic. Don't change it // without moving this into that unchoker logic return lhs->time_of_last_unchoke() < rhs->time_of_last_unchoke(); } bool upload_rate_compare(peer_connection const* lhs , peer_connection const* rhs) { boost::int64_t c1; boost::int64_t c2; c1 = lhs->uploaded_in_last_round(); c2 = rhs->uploaded_in_last_round(); // take torrent priority into account c1 *= lhs->get_priority(peer_connection::upload_channel); c2 *= rhs->get_priority(peer_connection::upload_channel); return c1 > c2; } bool bittyrant_unchoke_compare(peer_connection const* lhs , peer_connection const* rhs) { boost::int64_t d1, d2, u1, u2; // first compare how many bytes they've sent us d1 = lhs->downloaded_in_last_round(); d2 = rhs->downloaded_in_last_round(); // divided by the number of bytes we've sent them u1 = lhs->uploaded_in_last_round(); u2 = rhs->uploaded_in_last_round(); // take torrent priority into account d1 *= lhs->get_priority(peer_connection::upload_channel); d2 *= rhs->get_priority(peer_connection::upload_channel); d1 = d1 * 1000 / (std::max)(boost::int64_t(1), u1); d2 = d2 * 1000 / (std::max)(boost::int64_t(1), u2); if (d1 > d2) return true; if (d1 < d2) return false; // if both peers are still in their send quota or not in their send quota // prioritize the one that has waited the longest to be unchoked return lhs->time_of_last_unchoke() < rhs->time_of_last_unchoke(); } } // anonymous namespace int unchoke_sort(std::vector& peers , int max_upload_rate , time_duration unchoke_interval , aux::session_settings const& sett) { #if TORRENT_USE_ASSERTS for (std::vector::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { TORRENT_ASSERT((*i)->self()); TORRENT_ASSERT((*i)->associated_torrent().lock()); } #endif int upload_slots = sett.get_int(settings_pack::unchoke_slots_limit); if (upload_slots < 0) upload_slots = (std::numeric_limits::max)(); // ==== BitTyrant ==== // // if we're using the bittyrant unchoker, go through all peers that // we have unchoked already, and adjust our estimated reciprocation // rate. If the peer has reciprocated, lower the estimate, if it hasn't, // increase the estimate (this attempts to optimize "ROI" of upload // capacity, by sending just enough to be reciprocated). // For more information, see: http://bittyrant.cs.washington.edu/ if (sett.get_int(settings_pack::choking_algorithm) == settings_pack::bittyrant_choker) { for (std::vector::const_iterator i = peers.begin() , end(peers.end()); i != end; ++i) { peer_connection* p = *i; if (p->is_choked() || !p->is_interesting()) continue; if (!p->has_peer_choked()) { // we're unchoked, we may want to lower our estimated // reciprocation rate p->decrease_est_reciprocation_rate(); } else { // we've unchoked this peer, and it hasn't reciprocated // we may want to increase our estimated reciprocation rate p->increase_est_reciprocation_rate(); } } // if we're using the bittyrant choker, sort peers by their return // on investment. i.e. download rate / upload rate std::sort(peers.begin(), peers.end() , boost::bind(&bittyrant_unchoke_compare, _1, _2)); int upload_capacity_left = max_upload_rate; // now, figure out how many peers should be unchoked. We deduct the // estimated reciprocation rate from our upload_capacity estimate // until there none left upload_slots = 0; for (std::vector::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { peer_connection* p = *i; TORRENT_ASSERT(p); if (p->est_reciprocation_rate() > upload_capacity_left) break; ++upload_slots; upload_capacity_left -= p->est_reciprocation_rate(); } return upload_slots; } // ==== rate-based ==== // // The rate based unchoker looks at our upload rate to peers, and find // a balance between number of upload slots and the rate we achieve. The // intention is to not spread upload bandwidth too thin, but also to not // unchoke few enough peers to not be able to saturate the up-link. // this is done by traversing the peers sorted by our upload rate to // them in decreasing rates. For each peer we increase our threshold // by 1 kB/s. The first peer we get to to whom we upload slower than // the threshold, we stop and that's the number of unchoke slots we have. if (sett.get_int(settings_pack::choking_algorithm) == settings_pack::rate_based_choker) { // first reset the number of unchoke slots, because we'll calculate // it purely based on the current state of our peers. upload_slots = 0; // TODO: optimize this using partial_sort or something. We don't need // to sort the entire list // TODO: make the comparison function a free function and move it // into this cpp file std::sort(peers.begin(), peers.end() , boost::bind(&upload_rate_compare, _1, _2)); // TODO: make configurable int rate_threshold = 1024; for (std::vector::const_iterator i = peers.begin() , end(peers.end()); i != end; ++i) { peer_connection const& p = **i; int const rate = int(p.uploaded_in_last_round() * 1000 / total_milliseconds(unchoke_interval)); if (rate < rate_threshold) break; ++upload_slots; // TODO: make configurable rate_threshold += 1024; } ++upload_slots; } // sorts the peers that are eligible for unchoke by download rate and // secondary by total upload. The reason for this is, if all torrents are // being seeded, the download rate will be 0, and the peers we have sent // the least to should be unchoked // we use partial sort here, because we only care about the top // upload_slots peers. if (sett.get_int(settings_pack::seed_choking_algorithm) == settings_pack::round_robin) { int const pieces = sett.get_int(settings_pack::seeding_piece_quota); std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_rr, _1, _2, pieces)); } else if (sett.get_int(settings_pack::seed_choking_algorithm) == settings_pack::fastest_upload) { std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_fastest_upload, _1, _2)); } else if (sett.get_int(settings_pack::seed_choking_algorithm) == settings_pack::anti_leech) { std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_anti_leech, _1, _2)); } else { int const pieces = sett.get_int(settings_pack::seeding_piece_quota); std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_rr, _1, _2, pieces)); TORRENT_ASSERT(false); } return upload_slots; } } libtorrent-rasterbar-1.1.13/src/close_reason.cpp000066400000000000000000000167761351156116000217330ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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/close_reason.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { close_reason_t error_to_close_reason(error_code const& ec) { if (ec.category() == libtorrent_category()) { #define TORRENT_MAP(error, close_reason) \ case errors:: error : \ return close_reason; switch (ec.value()) { TORRENT_MAP(invalid_swarm_metadata, close_invalid_metadata) TORRENT_MAP(session_is_closing, close_torrent_removed) TORRENT_MAP(peer_sent_empty_piece, close_invalid_piece_message) TORRENT_MAP(mismatching_info_hash, close_invalid_info_hash) TORRENT_MAP(port_blocked, close_port_blocked) TORRENT_MAP(destructing_torrent, close_torrent_removed) TORRENT_MAP(timed_out, close_timeout) TORRENT_MAP(upload_upload_connection, close_upload_to_upload) TORRENT_MAP(uninteresting_upload_peer, close_not_interested_upload_only) TORRENT_MAP(invalid_info_hash, close_invalid_info_hash) TORRENT_MAP(torrent_paused, close_torrent_removed) TORRENT_MAP(invalid_have, close_invalid_have_message) TORRENT_MAP(invalid_bitfield_size, close_invalid_bitfield_message) TORRENT_MAP(too_many_requests_when_choked, close_request_when_choked) TORRENT_MAP(invalid_piece, close_invalid_piece_message) TORRENT_MAP(invalid_piece_size, close_invalid_piece_message) TORRENT_MAP(no_memory, close_no_memory) TORRENT_MAP(torrent_aborted, close_torrent_removed) TORRENT_MAP(self_connection, close_self_connection) TORRENT_MAP(timed_out_no_interest, close_timed_out_interest) TORRENT_MAP(timed_out_inactivity, close_timed_out_activity) TORRENT_MAP(timed_out_no_handshake, close_timed_out_handshake) TORRENT_MAP(timed_out_no_request, close_timed_out_request) TORRENT_MAP(invalid_choke, close_invalid_choke_message) TORRENT_MAP(invalid_unchoke, close_invalid_unchoke_message) TORRENT_MAP(invalid_interested, close_invalid_interested_message) TORRENT_MAP(invalid_not_interested, close_invalid_not_interested_message) TORRENT_MAP(invalid_request, close_invalid_request_message) TORRENT_MAP(invalid_hash_list, close_invalid_message) TORRENT_MAP(invalid_hash_piece, close_invalid_message) TORRENT_MAP(invalid_cancel, close_invalid_cancel_message) TORRENT_MAP(invalid_dht_port, close_invalid_dht_port_message) TORRENT_MAP(invalid_suggest, close_invalid_suggest_message) TORRENT_MAP(invalid_have_all, close_invalid_have_all_message) TORRENT_MAP(invalid_have_none, close_invalid_have_none_message) TORRENT_MAP(invalid_reject, close_invalid_reject_message) TORRENT_MAP(invalid_allow_fast, close_invalid_allow_fast_message) TORRENT_MAP(invalid_extended, close_invalid_extended_message) TORRENT_MAP(invalid_message, close_invalid_message_id) TORRENT_MAP(sync_hash_not_found, close_encryption_error) TORRENT_MAP(invalid_encryption_constant, close_encryption_error) TORRENT_MAP(no_plaintext_mode, close_protocol_blocked) TORRENT_MAP(no_rc4_mode, close_protocol_blocked) TORRENT_MAP(unsupported_encryption_mode_selected, close_protocol_blocked) TORRENT_MAP(invalid_pad_size, close_encryption_error) TORRENT_MAP(invalid_encrypt_handshake, close_encryption_error) TORRENT_MAP(no_incoming_encrypted, close_protocol_blocked) TORRENT_MAP(no_incoming_regular, close_protocol_blocked) TORRENT_MAP(duplicate_peer_id, close_duplicate_peer_id) TORRENT_MAP(torrent_removed, close_torrent_removed) TORRENT_MAP(packet_too_large, close_message_too_big) TORRENT_MAP(torrent_not_ready, close_torrent_removed) TORRENT_MAP(session_closing, close_torrent_removed) TORRENT_MAP(optimistic_disconnect, close_peer_churn) TORRENT_MAP(torrent_finished, close_upload_to_upload) TORRENT_MAP(too_many_corrupt_pieces, close_corrupt_pieces) TORRENT_MAP(too_many_connections, close_too_many_connections) TORRENT_MAP(peer_banned, close_blocked) TORRENT_MAP(stopping_torrent, close_torrent_removed) TORRENT_MAP(metadata_too_large, close_metadata_too_big) TORRENT_MAP(invalid_metadata_size, close_metadata_too_big) TORRENT_MAP(invalid_metadata_request, close_invalid_metadata_request_message) TORRENT_MAP(invalid_metadata_offset, close_invalid_metadata_offset) TORRENT_MAP(invalid_metadata_message, close_invalid_metadata_message) TORRENT_MAP(pex_message_too_large, close_pex_message_too_big) TORRENT_MAP(invalid_pex_message, close_invalid_pex_message) TORRENT_MAP(invalid_lt_tracker_message, close_invalid_message) TORRENT_MAP(too_frequent_pex, close_pex_too_frequent) TORRENT_MAP(invalid_dont_have, close_invalid_dont_have_message) TORRENT_MAP(requires_ssl_connection, close_protocol_blocked) TORRENT_MAP(invalid_ssl_cert, close_blocked) TORRENT_MAP(not_an_ssl_torrent, close_blocked) TORRENT_MAP(banned_by_port_filter, close_port_blocked) #ifdef TORRENT_USE_ASSERTS case errors::redirecting: return close_no_reason; #endif default: return close_no_reason; } } else if (ec.category() == boost::asio::error::get_misc_category()) { switch (ec.value()) { case boost::asio::error::eof: return close_no_reason; } } else if (ec.category() == boost::system::system_category()) { switch (ec.value()) { #ifdef TORRENT_USE_ASSERTS case boost::system::errc::connection_reset: case boost::system::errc::broken_pipe: return close_no_reason; #endif case boost::system::errc::timed_out: return close_timeout; case boost::system::errc::too_many_files_open: case boost::system::errc::too_many_files_open_in_system: return close_too_many_files; case boost::system::errc::not_enough_memory: case boost::system::errc::no_buffer_space: return close_no_memory; } } else if (ec.category() == http_category()) { return close_no_memory; } return close_no_reason; } } libtorrent-rasterbar-1.1.13/src/cpuid.cpp000066400000000000000000000050361351156116000203460ustar00rootroot00000000000000/* Copyright (c) 2014-2018, 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/config.hpp" #include #include "libtorrent/aux_/cpuid.hpp" #if defined _MSC_VER && TORRENT_HAS_SSE #include #include #endif #if TORRENT_HAS_SSE && defined __GNUC__ #include #endif namespace libtorrent { namespace aux { namespace { // internal void cpuid(unsigned int info[4], int type) { #if TORRENT_HAS_SSE && defined _MSC_VER __cpuid((int*)info, type); #elif TORRENT_HAS_SSE && defined __GNUC__ __get_cpuid(type, &info[0], &info[1], &info[2], &info[3]); #else // for non-x86 and non-amd64, just return zeroes std::memset(&info[0], 0, sizeof(unsigned int) * 4); #endif } bool supports_sse42() { #if TORRENT_HAS_SSE unsigned int cpui[4]; cpuid(cpui, 1); return cpui[2] & (1 << 20); #else return false; #endif } bool supports_mmx() { #if TORRENT_HAS_SSE unsigned int cpui[4]; cpuid(cpui, 1); return cpui[2] & (1 << 23); #else return false; #endif } } // anonymous namespace bool sse42_support = supports_sse42(); bool mmx_support = supports_mmx(); } } libtorrent-rasterbar-1.1.13/src/crc32c.cpp000066400000000000000000000076701351156116000203270ustar00rootroot00000000000000/* Copyright (c) 2014-2018, 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/config.hpp" #include "libtorrent/crc32c.hpp" #include "libtorrent/aux_/cpuid.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #if (defined _MSC_VER && _MSC_VER >= 1600) #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { boost::uint32_t crc32c_32(boost::uint32_t v) { #if TORRENT_HAS_SSE if (aux::sse42_support) { boost::uint32_t ret = 0xffffffff; #ifdef __GNUC__ // we can't use these because then we'd have to tell // -msse4.2 to gcc on the command line // return __builtin_ia32_crc32si(ret, v) ^ 0xffffffff; asm ("crc32l\t" "(%1), %0" : "=r"(ret) : "r"(&v), "0"(ret)); return ret ^ 0xffffffff; #else return _mm_crc32_u32(ret, v) ^ 0xffffffff; #endif } #endif boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; crc.process_bytes(&v, 4); return crc.checksum(); } boost::uint32_t crc32c(boost::uint64_t const* buf, int num_words) { #if TORRENT_HAS_SSE if (aux::sse42_support) { #if defined _M_AMD64 || defined __x86_64__ \ || defined __x86_64 || defined _M_X64 || defined __amd64__ boost::uint64_t ret = 0xffffffff; for (int i = 0; i < num_words; ++i) { #ifdef __GNUC__ // we can't use these because then we'd have to tell // -msse4.2 to gcc on the command line // ret = __builtin_ia32_crc32di(ret, buf[i]); __asm__("crc32q\t" "(%1), %0" : "=r"(ret) : "r"(buf+i), "0"(ret)); #else ret = _mm_crc32_u64(ret, buf[i]); #endif } return boost::uint32_t(ret) ^ 0xffffffff; #else boost::uint32_t ret = 0xffffffff; boost::uint32_t const* buf0 = reinterpret_cast(buf); for (int i = 0; i < num_words; ++i) { #ifdef __GNUC__ // we can't use these because then we'd have to tell // -msse4.2 to gcc on the command line // ret = __builtin_ia32_crc32si(ret, buf0[i*2]); // ret = __builtin_ia32_crc32si(ret, buf0[i*2+1]); asm ("crc32l\t" "(%1), %0" : "=r"(ret) : "r"(buf0+i*2), "0"(ret)); asm ("crc32l\t" "(%1), %0" : "=r"(ret) : "r"(buf0+i*2+1), "0"(ret)); #else ret = _mm_crc32_u32(ret, buf0[i*2]); ret = _mm_crc32_u32(ret, buf0[i*2+1]); #endif } return ret ^ 0xffffffff; #endif // amd64 or x86 } #endif // x86 or amd64 and gcc or msvc boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; crc.process_bytes(buf, num_words * 8); return crc.checksum(); } } libtorrent-rasterbar-1.1.13/src/create_torrent.cpp000066400000000000000000000545101351156116000222630ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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/create_torrent.hpp" #include "libtorrent/utf8.hpp" #include "libtorrent/file_pool.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/aux_/escape_string.hpp" // for convert_to_wstring #include "libtorrent/disk_io_thread.hpp" #include "libtorrent/aux_/merkle.hpp" // for merkle_*() #include "libtorrent/torrent_info.hpp" #include "libtorrent/announce_entry.hpp" #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/alert_manager.hpp" #include #include #include #include #include #include #define MAX_SYMLINK_PATH 200 namespace libtorrent { class alert; namespace { inline bool default_pred(std::string const&) { return true; } inline bool ignore_subdir(std::string const& leaf) { return leaf == ".." || leaf == "."; } int get_file_attributes(std::string const& p) { #ifdef TORRENT_WINDOWS WIN32_FILE_ATTRIBUTE_DATA attr; #if TORRENT_USE_WSTRING std::wstring path = convert_to_wstring(p); GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attr); #else std::string path = convert_to_native(p); GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attr); #endif // TORRENT_USE_WSTRING if (attr.dwFileAttributes == INVALID_FILE_ATTRIBUTES) return 0; if (attr.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) return file_storage::attribute_hidden; return 0; #else struct stat s; if (lstat(convert_to_native(p).c_str(), &s) < 0) return 0; int file_attr = 0; if (s.st_mode & S_IXUSR) file_attr += file_storage::attribute_executable; if (S_ISLNK(s.st_mode)) file_attr += file_storage::attribute_symlink; return file_attr; #endif } #ifndef TORRENT_WINDOWS std::string get_symlink_path_impl(char const* path) { char buf[MAX_SYMLINK_PATH]; std::string f = convert_to_native(path); int char_read = readlink(f.c_str(),buf,MAX_SYMLINK_PATH); if (char_read < 0) return ""; if (char_read < MAX_SYMLINK_PATH) buf[char_read] = 0; else buf[0] = 0; return convert_from_native(buf); } #endif std::string get_symlink_path(std::string const& p) { #if defined TORRENT_WINDOWS TORRENT_UNUSED(p); return ""; #else std::string path = convert_to_native(p); return get_symlink_path_impl(p.c_str()); #endif } void add_files_impl(file_storage& fs, std::string const& p , std::string const& l, boost::function pred, boost::uint32_t flags) { std::string f = combine_path(p, l); if (!pred(f)) return; error_code ec; file_status s; stat_file(f, &s, ec, (flags & create_torrent::symlinks) ? dont_follow_links : 0); if (ec) return; // recurse into directories bool recurse = (s.mode & file_status::directory) != 0; // if the file is not a link or we're following links, and it's a directory // only then should we recurse #ifndef TORRENT_WINDOWS if ((s.mode & file_status::link) && (flags & create_torrent::symlinks)) recurse = false; #endif if (recurse) { for (directory i(f, ec); !i.done(); i.next(ec)) { std::string leaf = i.file(); if (ignore_subdir(leaf)) continue; add_files_impl(fs, p, combine_path(l, leaf), pred, flags); } } else { // #error use the fields from s int file_flags = get_file_attributes(f); // mask all bits to check if the file is a symlink if ((file_flags & file_storage::attribute_symlink) && (flags & create_torrent::symlinks)) { std::string sym_path = get_symlink_path(f); fs.add_file(l, 0, file_flags, s.mtime, sym_path); } else { fs.add_file(l, s.file_size, file_flags, s.mtime); } } } void on_hash(disk_io_job const* j, create_torrent* t , boost::shared_ptr storage, disk_io_thread* iothread , int* piece_counter, int* completed_piece , boost::function const* f, error_code* ec) { if (j->ret != 0) { // on error *ec = j->error.ec; iothread->set_num_threads(0); return; } t->set_hash(j->piece, sha1_hash(j->d.piece_hash)); (*f)(*completed_piece); ++(*completed_piece); if (*piece_counter < t->num_pieces()) { iothread->async_hash(storage.get(), *piece_counter , disk_io_job::sequential_access , boost::bind(&on_hash, _1, t, storage, iothread , piece_counter, completed_piece, f, ec), NULL); ++(*piece_counter); } else { iothread->abort(true); } iothread->submit_jobs(); } } // anonymous namespace #if TORRENT_USE_WSTRING #ifndef TORRENT_NO_DEPRECATE void add_files(file_storage& fs, std::wstring const& wfile , boost::function p, boost::uint32_t flags) { std::string utf8; wchar_utf8(wfile, utf8); add_files_impl(fs, parent_path(complete(utf8)) , filename(utf8), p, flags); } void add_files(file_storage& fs , std::wstring const& wfile, boost::uint32_t flags) { std::string utf8; wchar_utf8(wfile, utf8); add_files_impl(fs, parent_path(complete(utf8)) , filename(utf8), default_pred, flags); } void set_piece_hashes(create_torrent& t, std::wstring const& p , boost::function f, error_code& ec) { std::string utf8; wchar_utf8(p, utf8); set_piece_hashes(t, utf8, f, ec); } void set_piece_hashes_deprecated(create_torrent& t, std::wstring const& p , boost::function f, error_code& ec) { std::string utf8; wchar_utf8(p, utf8); set_piece_hashes(t, utf8, f, ec); } #endif #endif void add_files(file_storage& fs, std::string const& file , boost::function p, boost::uint32_t flags) { add_files_impl(fs, parent_path(complete(file)), filename(file), p, flags); } void add_files(file_storage& fs, std::string const& file, boost::uint32_t flags) { add_files_impl(fs, parent_path(complete(file)), filename(file) , default_pred, flags); } namespace { struct disk_aborter { disk_aborter(disk_io_thread& dio) : m_dio(dio) {} ~disk_aborter() { m_dio.abort(true); } private: disk_aborter(disk_aborter const&); disk_aborter& operator=(disk_aborter const); disk_io_thread& m_dio; }; } void set_piece_hashes(create_torrent& t, std::string const& p , boost::function const& f, error_code& ec) { // optimized path #ifdef TORRENT_BUILD_SIMULATOR sim::default_config conf; sim::simulation sim{conf}; io_service ios{sim}; #else io_service ios; #endif #if TORRENT_USE_UNC_PATHS std::string path = canonicalize_path(p); #else std::string const& path = p; #endif if (t.files().num_files() == 0) { ec = errors::no_files_in_torrent; return; } if (t.files().total_size() == 0) { ec = errors::torrent_invalid_length; return; } // dummy torrent object pointer boost::shared_ptr dummy; counters cnt; disk_io_thread disk_thread(ios, cnt, 0); #ifdef TORRENT_BUILD_SIMULATOR disk_thread.set_num_threads(0); #else disk_thread.set_num_threads(1); #endif disk_aborter da(disk_thread); storage_params params; params.files = &t.files(); params.mapped_files = NULL; params.path = path; params.pool = &disk_thread.files(); params.mode = storage_mode_sparse; storage_interface* storage_impl = default_storage_constructor(params); boost::shared_ptr storage = boost::make_shared( storage_impl, dummy, const_cast(&t.files())); settings_pack sett; sett.set_int(settings_pack::cache_size, 0); sett.set_int(settings_pack::aio_threads, 3); // TODO: this should probably be optional alert_manager dummy2(0, 0); disk_thread.set_settings(&sett, dummy2); int piece_counter = 0; int completed_piece = 0; int piece_read_ahead = 16 * 1024 * 1024 / t.piece_length(); // at least 4 jobs at a time per thread if (piece_read_ahead < 12) piece_read_ahead = 12; for (int i = 0; i < piece_read_ahead; ++i) { disk_thread.async_hash(storage.get(), i, disk_io_job::sequential_access , boost::bind(&on_hash, _1, &t, storage, &disk_thread , &piece_counter, &completed_piece, &f, &ec), NULL); ++piece_counter; if (piece_counter >= t.num_pieces()) break; } disk_thread.submit_jobs(); #ifdef TORRENT_BUILD_SIMULATOR sim.run(); #else ios.run(ec); #endif } create_torrent::~create_torrent() {} create_torrent::create_torrent(file_storage& fs, int piece_size , int pad_file_limit, int flags, int alignment) : m_files(fs) , m_creation_date(time(0)) , m_multifile(fs.num_files() > 1) , m_private(false) , m_merkle_torrent((flags & merkle) != 0) , m_include_mtime((flags & modification_time) != 0) , m_include_symlinks((flags & symlinks) != 0) { // return instead of crash in release mode if (fs.num_files() == 0 || fs.total_size() == 0) return; if (!m_multifile && has_parent_path(m_files.file_path(0))) m_multifile = true; // a piece_size of 0 means automatic if (piece_size == 0 && !m_merkle_torrent) { // size_table is computed from the following: // target_list_size = sqrt(total_size) * 2; // target_piece_size = total_size / (target_list_size / hash_size); // Given hash_size = 20 bytes, target_piece_size = (16*1024 * pow(2, i)) // we can determine size_table = (total_size = pow(2 * target_piece_size / hash_size, 2)) boost::int64_t const size_table[] = {2684355, 10737418, 42949673, 171798692, 687194767, 2748779069LL, 10995116278LL, 43980465111LL, 175921860444LL, 703687441777LL}; int i = 0; for (int max = sizeof(size_table) / sizeof(size_table[0]); i < max; ++i) { if (size_table[i] >= fs.total_size()) break; } piece_size = 0x4000 << i; } else if (piece_size == 0 && m_merkle_torrent) { piece_size = 64*1024; } // to support mutable torrents, alignment always has to be the piece size, // because piece hashes are compared to determine whether files are // identical if (flags & mutable_torrent_support) alignment = piece_size; // make sure the size is an even power of 2 #ifndef NDEBUG for (int i = 0; i < 32; ++i) { if (piece_size & (1 << i)) { TORRENT_ASSERT((piece_size & ~(1 << i)) == 0); break; } } #endif m_files.set_piece_length(piece_size); if (flags & (optimize_alignment | mutable_torrent_support)) m_files.optimize(pad_file_limit, alignment, (flags & mutable_torrent_support) != 0); m_files.set_num_pieces(static_cast( (m_files.total_size() + m_files.piece_length() - 1) / m_files.piece_length())); m_piece_hash.resize(m_files.num_pieces()); } create_torrent::create_torrent(torrent_info const& ti) : m_files(const_cast(ti.files())) , m_creation_date(time(0)) , m_multifile(ti.num_files() > 1) , m_private(ti.priv()) , m_merkle_torrent(ti.is_merkle_torrent()) , m_include_mtime(false) , m_include_symlinks(false) { load_from_torrent_info(ti, false); } create_torrent::create_torrent(torrent_info const& ti, bool const use_preformatted) : m_files(const_cast(ti.files())) , m_creation_date(time(0)) , m_multifile(ti.num_files() > 1) , m_private(ti.priv()) , m_merkle_torrent(ti.is_merkle_torrent()) , m_include_mtime(false) , m_include_symlinks(false) { load_from_torrent_info(ti, use_preformatted); } void create_torrent::load_from_torrent_info(torrent_info const& ti, bool const use_preformatted) { TORRENT_ASSERT(ti.is_valid()); TORRENT_ASSERT(ti.num_pieces() > 0); TORRENT_ASSERT(ti.num_files() > 0); TORRENT_ASSERT(ti.total_size() > 0); if (!ti.is_valid()) return; if (ti.creation_date()) m_creation_date = *ti.creation_date(); if (!ti.creator().empty()) set_creator(ti.creator().c_str()); if (!ti.comment().empty()) set_comment(ti.comment().c_str()); torrent_info::nodes_t const& nodes = ti.nodes(); for (torrent_info::nodes_t::const_iterator i = nodes.begin() , end(nodes.end()); i != end; ++i) add_node(*i); std::vector const& trackers = ti.trackers(); for (std::vector::const_iterator i = trackers.begin() , end(trackers.end()); i != end; ++i) add_tracker(i->url, i->tier); std::vector const& web_seeds = ti.web_seeds(); for (std::vector::const_iterator i = web_seeds.begin() , end(web_seeds.end()); i != end; ++i) { if (i->type == web_seed_entry::url_seed) add_url_seed(i->url); else if (i->type == web_seed_entry::http_seed) add_http_seed(i->url); } m_piece_hash.resize(m_files.num_pieces()); for (int i = 0; i < num_pieces(); ++i) set_hash(i, ti.hash_for_piece(i)); if (use_preformatted) { boost::shared_array const info = ti.metadata(); int const size = ti.metadata_size(); m_info_dict.preformatted().assign(&info[0], &info[0] + size); } else { m_info_dict = bdecode(&ti.metadata()[0], &ti.metadata()[0] + ti.metadata_size()); } m_info_hash = ti.info_hash(); } entry create_torrent::generate() const { TORRENT_ASSERT(m_files.piece_length() > 0); entry dict; if (m_files.num_files() == 0) return dict; if (!m_urls.empty()) dict["announce"] = m_urls.front().first; if (!m_nodes.empty()) { entry& nodes = dict["nodes"]; 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().second; for (std::vector::const_iterator i = m_urls.begin(); i != m_urls.end(); ++i) { if (i->second != current_tier) { current_tier = i->second; trackers.list().push_back(tier); tier.list().clear(); } tier.list().push_back(entry(i->first)); } trackers.list().push_back(tier); dict["announce-list"] = trackers; } if (!m_comment.empty()) dict["comment"] = m_comment; dict["creation date"] = m_creation_date; 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"]; for (std::vector::const_iterator i = m_url_seeds.begin(); i != m_url_seeds.end(); ++i) { list.list().push_back(entry(*i)); } } } if (!m_http_seeds.empty()) { if (m_http_seeds.size() == 1) { dict["httpseeds"] = m_http_seeds.front(); } else { entry& list = dict["httpseeds"]; for (std::vector::const_iterator i = m_http_seeds.begin(); i != m_http_seeds.end(); ++i) { list.list().push_back(entry(*i)); } } } entry& info = dict["info"]; if (m_info_dict.type() == entry::dictionary_t || m_info_dict.type() == entry::preformatted_t) { info = m_info_dict; return dict; } if (!m_collections.empty()) { entry& list = info["collections"]; for (std::vector::const_iterator i = m_collections.begin(); i != m_collections.end(); ++i) { list.list().push_back(entry(*i)); } } if (!m_similar.empty()) { entry& list = info["similar"]; for (std::vector::const_iterator i = m_similar.begin(); i != m_similar.end(); ++i) { list.list().push_back(entry(i->to_string())); } } info["name"] = m_files.name(); if (!m_root_cert.empty()) info["ssl-cert"] = m_root_cert; if (m_private) info["private"] = 1; if (!m_multifile) { if (m_include_mtime) info["mtime"] = m_files.mtime(0); info["length"] = m_files.file_size(0); int const flags = m_files.file_flags(0); if (flags & (file_storage::flag_pad_file | file_storage::flag_hidden | file_storage::flag_executable | file_storage::flag_symlink)) { std::string& attr = info["attr"].string(); if (flags & file_storage::flag_pad_file) attr += 'p'; if (flags & file_storage::flag_hidden) attr += 'h'; if (flags & file_storage::flag_executable) attr += 'x'; if (m_include_symlinks && (flags & file_storage::flag_symlink)) attr += 'l'; } if (m_include_symlinks && (flags & file_storage::flag_symlink)) { entry& sympath_e = info["symlink path"]; std::string split = split_path(m_files.symlink(0)); for (char const* e = split.c_str(); e != 0; e = next_path_element(e)) sympath_e.list().push_back(entry(e)); } if (!m_filehashes.empty()) { info["sha1"] = m_filehashes[0].to_string(); } } else { if (!info.find_key("files")) { entry& files = info["files"]; for (int i = 0; i < m_files.num_files(); ++i) { files.list().push_back(entry()); entry& file_e = files.list().back(); if (m_include_mtime && m_files.mtime(i)) file_e["mtime"] = m_files.mtime(i); file_e["length"] = m_files.file_size(i); entry& path_e = file_e["path"]; TORRENT_ASSERT(has_parent_path(m_files.file_path(i))); { std::string split = split_path(m_files.file_path(i)); TORRENT_ASSERT(split.c_str() == m_files.name()); for (char const* e = next_path_element(split.c_str()); e != 0; e = next_path_element(e)) path_e.list().push_back(entry(e)); } int flags = m_files.file_flags(i); if (flags != 0) { std::string& attr = file_e["attr"].string(); if (flags & file_storage::flag_pad_file) attr += 'p'; if (flags & file_storage::flag_hidden) attr += 'h'; if (flags & file_storage::flag_executable) attr += 'x'; if (m_include_symlinks && (flags & file_storage::flag_symlink)) attr += 'l'; } if (m_include_symlinks && (flags & file_storage::flag_symlink)) { entry& sympath_e = file_e["symlink path"]; std::string split = split_path(m_files.symlink(i)); for (char const* e = split.c_str(); e != 0; e = next_path_element(e)) sympath_e.list().push_back(entry(e)); } if (!m_filehashes.empty() && m_filehashes[i] != sha1_hash()) { file_e["sha1"] = m_filehashes[i].to_string(); } } } } info["piece length"] = m_files.piece_length(); if (m_merkle_torrent) { int num_leafs = merkle_num_leafs(m_files.num_pieces()); int num_nodes = merkle_num_nodes(num_leafs); int first_leaf = num_nodes - num_leafs; m_merkle_tree.resize(num_nodes); int num_pieces = m_piece_hash.size(); for (int i = 0; i < num_pieces; ++i) m_merkle_tree[first_leaf + i] = m_piece_hash[i]; sha1_hash filler(0); for (int i = num_pieces; i < num_leafs; ++i) m_merkle_tree[first_leaf + i] = filler; // now that we have initialized all leaves, build // each level bottom-up int level_start = first_leaf; int level_size = num_leafs; while (level_start > 0) { int parent = merkle_get_parent(level_start); for (int i = level_start; i < level_start + level_size; i += 2, ++parent) { hasher h; h.update(m_merkle_tree[i].data(), 20); h.update(m_merkle_tree[i+1].data(), 20); m_merkle_tree[parent] = h.final(); } level_start = merkle_get_parent(level_start); level_size /= 2; } TORRENT_ASSERT(level_size == 1); std::string& p = info["root hash"].string(); p.assign(m_merkle_tree[0].data(), 20); } else { std::string& p = info["pieces"].string(); for (std::vector::const_iterator i = m_piece_hash.begin(); i != m_piece_hash.end(); ++i) { p.append(i->data(), sha1_hash::size); } } std::vector buf; bencode(std::back_inserter(buf), info); m_info_hash = hasher(&buf[0], buf.size()).final(); return dict; } void create_torrent::add_tracker(std::string const& url, int tier) { m_urls.push_back(announce_entry(url, tier)); std::sort(m_urls.begin(), m_urls.end() , boost::bind(&announce_entry::second, _1) < boost::bind(&announce_entry::second, _2)); } void create_torrent::set_root_cert(std::string const& cert) { m_root_cert = cert; } void create_torrent::add_similar_torrent(sha1_hash ih) { m_similar.push_back(ih); } void create_torrent::add_collection(std::string c) { m_collections.push_back(c); } void create_torrent::set_hash(int index, sha1_hash const& h) { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_hash.size())); m_piece_hash[index] = h; } void create_torrent::set_file_hash(int index, sha1_hash const& h) { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_files.num_files())); if (m_filehashes.empty()) m_filehashes.resize(m_files.num_files()); m_filehashes[index] = h; } void create_torrent::add_node(std::pair const& node) { m_nodes.push_back(node); } void create_torrent::add_url_seed(std::string const& url) { m_url_seeds.push_back(url); } void create_torrent::add_http_seed(std::string const& url) { m_http_seeds.push_back(url); } void create_torrent::set_comment(char const* str) { if (str == 0) m_comment.clear(); else m_comment = str; } void create_torrent::set_creator(char const* str) { if (str == 0) m_created_by.clear(); else m_created_by = str; } } libtorrent-rasterbar-1.1.13/src/disk_buffer_holder.cpp000066400000000000000000000071311351156116000230600ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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/disk_buffer_holder.hpp" #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/disk_io_thread.hpp" namespace libtorrent { disk_buffer_holder::disk_buffer_holder(buffer_allocator_interface& alloc, char* buf) : m_allocator(alloc), m_buf(buf) { m_ref.storage = 0; m_ref.piece = -1; m_ref.block = -1; } disk_buffer_holder::disk_buffer_holder(buffer_allocator_interface& alloc, disk_io_job const& j) : m_allocator(alloc), m_buf(j.buffer.disk_block), m_ref(j.d.io.ref) { TORRENT_ASSERT(m_ref.storage == 0 || m_ref.piece >= 0); TORRENT_ASSERT(m_ref.storage == 0 || m_ref.block >= 0); TORRENT_ASSERT(m_ref.storage == 0 || m_ref.piece < static_cast(m_ref.storage)->files()->num_pieces()); TORRENT_ASSERT(m_ref.storage == 0 || m_ref.block <= static_cast(m_ref.storage)->files()->piece_length() / 0x4000); TORRENT_ASSERT(j.action != disk_io_job::save_resume_data); TORRENT_ASSERT(j.action != disk_io_job::rename_file); TORRENT_ASSERT(j.action != disk_io_job::move_storage); } void disk_buffer_holder::reset(disk_io_job const& j) { if (m_ref.storage) m_allocator.reclaim_block(m_ref); else if (m_buf) m_allocator.free_disk_buffer(m_buf); m_buf = j.buffer.disk_block; m_ref = j.d.io.ref; TORRENT_ASSERT(m_ref.piece >= 0); TORRENT_ASSERT(m_ref.storage != 0); TORRENT_ASSERT(m_ref.block >= 0); TORRENT_ASSERT(m_ref.piece < static_cast(m_ref.storage)->files()->num_pieces()); TORRENT_ASSERT(m_ref.block <= static_cast(m_ref.storage)->files()->piece_length() / 0x4000); TORRENT_ASSERT(j.action != disk_io_job::save_resume_data); TORRENT_ASSERT(j.action != disk_io_job::rename_file); TORRENT_ASSERT(j.action != disk_io_job::move_storage); } void disk_buffer_holder::reset(char* buf) { if (m_ref.storage) m_allocator.reclaim_block(m_ref); else if (m_buf) m_allocator.free_disk_buffer(m_buf); m_buf = buf; m_ref.storage = 0; } char* disk_buffer_holder::release() { char* ret = m_buf; m_buf = 0; m_ref.storage = 0; return ret; } disk_buffer_holder::~disk_buffer_holder() { reset(); } } libtorrent-rasterbar-1.1.13/src/disk_buffer_pool.cpp000066400000000000000000000407151351156116000225610ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/config.hpp" #include "libtorrent/disk_buffer_pool.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/disk_observer.hpp" #include "libtorrent/platform_util.hpp" // for total_physical_ram #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #if TORRENT_HAVE_MMAP #include #endif #ifdef TORRENT_BSD #include #endif #ifdef TORRENT_LINUX #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { namespace { // this is posted to the network thread void watermark_callback(std::vector >* cbs) { if (cbs != NULL) { for (std::vector >::iterator i = cbs->begin() , end(cbs->end()); i != end; ++i) { boost::shared_ptr o = i->lock(); if (o) o->on_disk(); } delete cbs; } } } // anonymous namespace disk_buffer_pool::disk_buffer_pool(int block_size, io_service& ios , boost::function const& trigger_trim) : m_block_size(block_size) , m_in_use(0) , m_max_use(64) , m_low_watermark((std::max)(m_max_use - 32, 0)) , m_trigger_cache_trim(trigger_trim) , m_exceeded_max_size(false) , m_ios(ios) , m_cache_buffer_chunk_size(0) #if TORRENT_HAVE_MMAP && !defined TORRENT_NO_DEPRECATE , m_cache_fd(-1) , m_cache_pool(0) #endif #ifndef TORRENT_DISABLE_POOL_ALLOCATOR , m_using_pool_allocator(false) , m_want_pool_allocator(false) , m_pool(block_size, 32) #endif { #if TORRENT_USE_ASSERTS m_magic = 0x1337; m_settings_set = false; #endif } disk_buffer_pool::~disk_buffer_pool() { TORRENT_ASSERT(m_magic == 0x1337); #if TORRENT_USE_ASSERTS m_magic = 0; #endif #if TORRENT_HAVE_MMAP && !defined TORRENT_NO_DEPRECATE if (m_cache_pool) { munmap(m_cache_pool, boost::uint64_t(m_max_use) * 0x4000); m_cache_pool = 0; // attempt to make MacOS not flush this to disk, making close() // block for a long time int const best_effort = ftruncate(m_cache_fd, 0); TORRENT_UNUSED(best_effort); close(m_cache_fd); m_cache_fd = -1; } #endif } boost::uint32_t disk_buffer_pool::num_to_evict(int num_needed) { int ret = 0; mutex::scoped_lock l(m_pool_mutex); if (m_exceeded_max_size) ret = m_in_use - (std::min)(m_low_watermark, int(m_max_use - m_observers.size()*2)); if (m_in_use + num_needed > m_max_use) ret = (std::max)(ret, int(m_in_use + num_needed - m_max_use)); if (ret < 0) ret = 0; else if (ret > m_in_use) ret = m_in_use; return ret; } // checks to see if we're no longer exceeding the high watermark, // and if we're in fact below the low watermark. If so, we need to // post the notification messages to the peers that are waiting for // more buffers to received data into void disk_buffer_pool::check_buffer_level(mutex::scoped_lock& l) { TORRENT_ASSERT(l.locked()); if (!m_exceeded_max_size || m_in_use > m_low_watermark) return; m_exceeded_max_size = false; std::vector >* cbs = new std::vector >(); m_observers.swap(*cbs); l.unlock(); m_ios.post(boost::bind(&watermark_callback, cbs)); } #if TORRENT_USE_ASSERTS bool disk_buffer_pool::is_disk_buffer(char* buffer , mutex::scoped_lock& l) const { TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(l.locked()); TORRENT_UNUSED(l); #if TORRENT_HAVE_MMAP && !defined TORRENT_NO_DEPRECATE if (m_cache_pool) { return buffer >= m_cache_pool && buffer < m_cache_pool + boost::uint64_t(m_max_use) * 0x4000; } #endif #if defined TORRENT_DEBUG return m_buffers_in_use.count(buffer) == 1; #elif defined TORRENT_DISABLE_POOL_ALLOCATOR return true; #else if (m_using_pool_allocator) return m_pool.is_from(buffer); else return true; #endif } bool disk_buffer_pool::is_disk_buffer(char* buffer) const { mutex::scoped_lock l(m_pool_mutex); return is_disk_buffer(buffer, l); } #endif char* disk_buffer_pool::allocate_buffer(char const* category) { mutex::scoped_lock l(m_pool_mutex); return allocate_buffer_impl(l, category); } // we allow allocating more blocks even after we exceed the max size, // but communicate back to the allocator (typically the peer_connection) // that we have exceeded the limit via the out-parameter "exceeded". The // caller is expected to honor this by not allocating any more buffers // until the disk_observer object (passed in as "o") is invoked, indicating // that there's more room in the pool now. This caps the amount of over- // allocation to one block per peer connection. char* disk_buffer_pool::allocate_buffer(bool& exceeded , boost::shared_ptr o, char const* category) { mutex::scoped_lock l(m_pool_mutex); char* ret = allocate_buffer_impl(l, category); if (m_exceeded_max_size) { exceeded = true; if (o) m_observers.push_back(o); } return ret; } // this function allocates buffers and // fills in the iovec array with the buffers int disk_buffer_pool::allocate_iovec(file::iovec_t* iov, int iov_len) { mutex::scoped_lock l(m_pool_mutex); for (int i = 0; i < iov_len; ++i) { iov[i].iov_base = allocate_buffer_impl(l, "pending read"); iov[i].iov_len = block_size(); if (iov[i].iov_base == NULL) { // uh oh. We failed to allocate the buffer! // we need to roll back and free all the buffers // we've already allocated for (int j = 0; j < i; ++j) free_buffer_impl(static_cast(iov[j].iov_base), l); return -1; } } return 0; } void disk_buffer_pool::free_iovec(file::iovec_t* iov, int iov_len) { // TODO: perhaps we should sort the buffers here? mutex::scoped_lock l(m_pool_mutex); for (int i = 0; i < iov_len; ++i) free_buffer_impl(static_cast(iov[i].iov_base), l); check_buffer_level(l); } char* disk_buffer_pool::allocate_buffer_impl(mutex::scoped_lock& l , char const*) { TORRENT_ASSERT(m_settings_set); TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(l.locked()); TORRENT_UNUSED(l); char* ret; #if TORRENT_HAVE_MMAP && !defined TORRENT_NO_DEPRECATE if (m_cache_pool) { if (m_free_list.size() <= (m_max_use - m_low_watermark) / 2 && !m_exceeded_max_size) { m_exceeded_max_size = true; m_trigger_cache_trim(); } if (m_free_list.empty()) return 0; boost::uint64_t slot_index = m_free_list.back(); m_free_list.pop_back(); ret = m_cache_pool + (slot_index * 0x4000); TORRENT_ASSERT(is_disk_buffer(ret, l)); } else #endif { #if defined TORRENT_DISABLE_POOL_ALLOCATOR ret = static_cast(std::malloc(m_block_size)); #else if (m_using_pool_allocator) { int const effective_block_size = m_in_use >= m_max_use ? 20 // use small increments once we've exceeded the cache size : m_cache_buffer_chunk_size ? m_cache_buffer_chunk_size : (std::max)(m_max_use / 10, 1); m_pool.set_next_size(effective_block_size); ret = static_cast(m_pool.malloc()); } else { ret = static_cast(std::malloc(m_block_size)); } #endif if (ret == NULL) { m_exceeded_max_size = true; m_trigger_cache_trim(); return 0; } } #if defined TORRENT_DEBUG TORRENT_ASSERT(m_buffers_in_use.count(ret) == 0); m_buffers_in_use.insert(ret); #endif ++m_in_use; if (m_in_use >= m_low_watermark + (m_max_use - m_low_watermark) / 2 && !m_exceeded_max_size) { m_exceeded_max_size = true; m_trigger_cache_trim(); } TORRENT_ASSERT(is_disk_buffer(ret, l)); return ret; } void disk_buffer_pool::free_multiple_buffers(char** bufvec, int numbufs) { char** end = bufvec + numbufs; // sort the pointers in order to maximize cache hits std::sort(bufvec, end); mutex::scoped_lock l(m_pool_mutex); for (; bufvec != end; ++bufvec) { char* buf = *bufvec; TORRENT_ASSERT(buf); free_buffer_impl(buf, l); } check_buffer_level(l); } void disk_buffer_pool::free_buffer(char* buf) { mutex::scoped_lock l(m_pool_mutex); free_buffer_impl(buf, l); check_buffer_level(l); } void disk_buffer_pool::set_settings(aux::session_settings const& sett , error_code& ec) { TORRENT_UNUSED(ec); mutex::scoped_lock l(m_pool_mutex); // 0 cache_buffer_chunk_size means 'automatic' (i.e. // proportional to the total disk cache size) m_cache_buffer_chunk_size = sett.get_int(settings_pack::cache_buffer_chunk_size); #ifndef TORRENT_DISABLE_POOL_ALLOCATOR // if the chunk size is set to 1, there's no point in creating a pool m_want_pool_allocator = sett.get_bool(settings_pack::use_disk_cache_pool) && (m_cache_buffer_chunk_size != 1); // if there are no allocated blocks, it's OK to switch allocator if (m_in_use == 0) m_using_pool_allocator = m_want_pool_allocator; #endif #if TORRENT_HAVE_MMAP && !defined TORRENT_NO_DEPRECATE // if we've already allocated an mmap, we can't change // anything unless there are no allocations in use if (m_cache_pool && m_in_use > 0) return; #endif // only allow changing size if we're not using mmapped // cache, or if we're just about to turn it off if ( #if TORRENT_HAVE_MMAP && !defined TORRENT_NO_DEPRECATE m_cache_pool == 0 || sett.get_str(settings_pack::mmap_cache).empty() #else true #endif ) { int const cache_size = sett.get_int(settings_pack::cache_size); if (cache_size < 0) { boost::uint64_t phys_ram = total_physical_ram(); if (phys_ram == 0) m_max_use = 1024; else { // this is the logic to calculate the automatic disk cache size // based on the amount of physical RAM. // The more physical RAM, the smaller portion of it is allocated // for the cache. // we take a 30th of everything exceeding 4 GiB // a 20th of everything exceeding 1 GiB // and a 10th of everything below a GiB boost::int64_t const gb = 1024 * 1024 * 1024; boost::int64_t result = 0; if (phys_ram > 4 * gb) { result += (phys_ram - 4 * gb) / 30; phys_ram = 4 * gb; } if (phys_ram > 1 * gb) { result += (phys_ram - 1 * gb) / 20; phys_ram = 1 * gb; } result += phys_ram / 10; m_max_use = result / m_block_size; } if (sizeof(void*) == 4) { // 32 bit builds should capped below 2 GB of memory, even // when more actual ram is available, because we're still // constrained by the 32 bit virtual address space. m_max_use = (std::min)(2 * 1024 * 1024 * 3 / 4 * 1024 / m_block_size, m_max_use); } } else { m_max_use = cache_size; } m_low_watermark = m_max_use - (std::max)(16, sett.get_int(settings_pack::max_queued_disk_bytes) / 0x4000); if (m_low_watermark < 0) m_low_watermark = 0; if (m_in_use >= m_max_use && !m_exceeded_max_size) { m_exceeded_max_size = true; m_trigger_cache_trim(); } if (m_cache_buffer_chunk_size > m_max_use) m_cache_buffer_chunk_size = m_max_use; } #if TORRENT_USE_ASSERTS m_settings_set = true; #endif #if TORRENT_HAVE_MMAP && !defined TORRENT_NO_DEPRECATE // #error support resizing the map if (m_cache_pool && sett.get_str(settings_pack::mmap_cache).empty()) { TORRENT_ASSERT(m_in_use == 0); munmap(m_cache_pool, boost::uint64_t(m_max_use) * 0x4000); m_cache_pool = 0; // attempt to make MacOS not flush this to disk, making close() // block for a long time int const best_effort = ftruncate(m_cache_fd, 0); TORRENT_UNUSED(best_effort); close(m_cache_fd); m_cache_fd = -1; std::vector().swap(m_free_list); } else if (m_cache_pool == 0 && !sett.get_str(settings_pack::mmap_cache).empty()) { // O_TRUNC here is because we don't actually care about what's // in the file now, there's no need to ever read that into RAM #ifndef O_EXLOCK #define O_EXLOCK 0 #endif m_cache_fd = open(sett.get_str(settings_pack::mmap_cache).c_str(), O_RDWR | O_CREAT | O_EXLOCK | O_TRUNC, 0700); if (m_cache_fd < 0) { ec.assign(errno, boost::system::system_category()); } else { #ifndef MAP_NOCACHE #define MAP_NOCACHE 0 #endif int const best_effort = ftruncate(m_cache_fd, boost::uint64_t(m_max_use) * 0x4000); TORRENT_UNUSED(best_effort); m_cache_pool = static_cast(mmap(0, boost::uint64_t(m_max_use) * 0x4000, PROT_READ | PROT_WRITE , MAP_SHARED | MAP_NOCACHE, m_cache_fd, 0)); if (intptr_t(m_cache_pool) == -1) { ec.assign(errno, boost::system::system_category()); m_cache_pool = 0; // attempt to make MacOS not flush this to disk, making close() // block for a long time int const best_effort2 = ftruncate(m_cache_fd, 0); TORRENT_UNUSED(best_effort2); close(m_cache_fd); m_cache_fd = -1; } else { TORRENT_ASSERT((size_t(m_cache_pool) & 0xfff) == 0); m_free_list.reserve(m_max_use); for (int i = 0; i < m_max_use; ++i) m_free_list.push_back(i); } } } #endif // TORRENT_HAVE_MMAP } void disk_buffer_pool::free_buffer_impl(char* buf, mutex::scoped_lock& l) { TORRENT_ASSERT(buf); TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(m_settings_set); TORRENT_ASSERT(is_disk_buffer(buf, l)); TORRENT_ASSERT(l.locked()); TORRENT_UNUSED(l); #if TORRENT_HAVE_MMAP && !defined TORRENT_NO_DEPRECATE if (m_cache_pool) { TORRENT_ASSERT(buf >= m_cache_pool); TORRENT_ASSERT(buf < m_cache_pool + boost::uint64_t(m_max_use) * 0x4000); int slot_index = (buf - m_cache_pool) / 0x4000; m_free_list.push_back(slot_index); #if defined MADV_FREE // tell the virtual memory system that we don't actually care // about the data in these pages anymore. If this block was // swapped out to the SSD, it (hopefully) means it won't have // to be read back in once we start writing our new data to it madvise(buf, 0x4000, MADV_FREE); #elif defined MADV_DONTNEED && defined TORRENT_LINUX // rumor has it that MADV_DONTNEED is in fact destructive // on linux (i.e. it won't flush it to disk or re-read from disk) // http://kerneltrap.org/mailarchive/linux-kernel/2007/5/1/84410 madvise(buf, 0x4000, MADV_DONTNEED); #endif } else #endif { #if defined TORRENT_DISABLE_POOL_ALLOCATOR std::free(buf); #else if (m_using_pool_allocator) m_pool.free(buf); else std::free(buf); #endif // TORRENT_DISABLE_POOL_ALLOCATOR } #if defined TORRENT_DEBUG std::set::iterator i = m_buffers_in_use.find(buf); TORRENT_ASSERT(i != m_buffers_in_use.end()); m_buffers_in_use.erase(i); #endif --m_in_use; #ifndef TORRENT_DISABLE_POOL_ALLOCATOR // should we switch which allocator to use? if (m_in_use == 0 && m_want_pool_allocator != m_using_pool_allocator) { m_pool.release_memory(); m_using_pool_allocator = m_want_pool_allocator; } #endif } void disk_buffer_pool::release_memory() { TORRENT_ASSERT(m_magic == 0x1337); #ifndef TORRENT_DISABLE_POOL_ALLOCATOR mutex::scoped_lock l(m_pool_mutex); if (m_using_pool_allocator) m_pool.release_memory(); #endif } } libtorrent-rasterbar-1.1.13/src/disk_io_job.cpp000066400000000000000000000055261351156116000215210ustar00rootroot00000000000000/* Copyright (c) 2011-2018, 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/disk_io_job.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/block_cache.hpp" // for cached_piece_entry #include "libtorrent/entry.hpp" namespace libtorrent { disk_io_job::disk_io_job() : requester(0) , piece(0) , action(read) , ret(0) , flags(0) #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS , in_use(false) , job_posted(false) , callback_called(false) , blocked(false) #endif { buffer.disk_block = 0; d.io.offset = 0; d.io.buffer_size = 0; d.io.ref.storage = 0; d.io.ref.piece = 0; d.io.ref.block = 0; } disk_io_job::~disk_io_job() { if (action == rename_file || action == move_storage) free(buffer.string); else if (action == save_resume_data) delete static_cast(buffer.resume_data); } bool disk_io_job::completed(cached_piece_entry const* pe, int block_size) { if (action != write) return false; int block_offset = d.io.offset & (block_size-1); int size = d.io.buffer_size; int start = d.io.offset / block_size; int end = block_offset > 0 && (size > block_size - block_offset) ? start + 2 : start + 1; for (int i = start; i < end; ++i) if (pe->blocks[i].dirty || pe->blocks[i].pending) return false; // if all our blocks are not pending and not dirty, it means they // were successfully written to disk. This job is complete return true; } } libtorrent-rasterbar-1.1.13/src/disk_io_thread.cpp000066400000000000000000003367321351156116000222240ustar00rootroot00000000000000/* Copyright (c) 2007-2018, Arvid Norberg, Steven Siloti 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/config.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/disk_io_thread.hpp" #include "libtorrent/disk_buffer_holder.hpp" #include "libtorrent/alloca.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/error.hpp" #include "libtorrent/file_pool.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/platform_util.hpp" #include #include #include #include #include #include "libtorrent/time.hpp" #include "libtorrent/disk_buffer_pool.hpp" #include "libtorrent/disk_io_job.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/uncork_interface.hpp" #include "libtorrent/performance_counters.hpp" #include "libtorrent/alert_manager.hpp" #include "libtorrent/debug.hpp" #define DEBUG_DISK_THREAD 0 #if __cplusplus >= 201103L || defined __clang__ #if DEBUG_DISK_THREAD #include // for va_list #define DLOG(...) debug_log(__VA_ARGS__) #else #define DLOG(...) do {} while(false) #endif #else #if DEBUG_DISK_THREAD #define DLOG debug_log #else inline void dummy(char const*, ...) {} #define DLOG TORRENT_WHILE_0 dummy #endif #endif // cplusplus namespace libtorrent { #if TORRENT_USE_ASSERTS #define TORRENT_PIECE_ASSERT(cond, piece) \ do { if (!(cond)) { assert_print_piece(piece); assert_fail(#cond, __LINE__, __FILE__, TORRENT_FUNCTION, 0); } } TORRENT_WHILE_0 #else #define TORRENT_PIECE_ASSERT(cond, piece) do {} TORRENT_WHILE_0 #endif // TORRENT_USE_ASSERTS namespace { #if DEBUG_DISK_THREAD void debug_log(char const* fmt, ...) { static mutex log_mutex; static const time_point start = clock_type::now(); va_list v; va_start(v, fmt); char usr[2048]; int len = vsnprintf(usr, sizeof(usr), fmt, v); static bool prepend_time = true; if (!prepend_time) { prepend_time = (usr[len-1] == '\n'); mutex::scoped_lock l(log_mutex); fputs(usr, stderr); return; } va_end(v); char buf[2300]; int t = total_milliseconds(clock_type::now() - start); snprintf(buf, sizeof(buf), "%05d: [%p] %s", t, pthread_self(), usr); prepend_time = (usr[len-1] == '\n'); mutex::scoped_lock l(log_mutex); fputs(buf, stderr); } #endif // DEBUG_DISK_THREAD int file_flags_for_job(disk_io_job* j , bool const coalesce_buffers) { int ret = 0; if (!(j->flags & disk_io_job::sequential_access)) ret |= file::random_access; if (coalesce_buffers) ret |= file::coalesce_buffers; return ret; } } // anonymous namespace // ------- disk_io_thread ------ disk_io_thread::disk_io_thread(io_service& ios , counters& cnt , void* userdata , int block_size) : m_num_threads(0) , m_abort(false) , m_num_running_threads(0) , m_userdata(userdata) , m_last_cache_expiry(min_time()) , m_last_file_check(clock_type::now()) , m_file_pool(40) , m_disk_cache(block_size, ios, boost::bind(&disk_io_thread::trigger_cache_trim, this)) , m_cache_check_state(cache_check_idle) , m_stats_counters(cnt) , m_ios(ios) , m_last_disk_aio_performance_warning(min_time()) , m_outstanding_reclaim_message(false) #if TORRENT_USE_ASSERTS , m_magic(0x1337) #endif { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("disk_io_thread::work"); #endif error_code ec; m_disk_cache.set_settings(m_settings, ec); TORRENT_ASSERT(!ec); } disk_io_thread::~disk_io_thread() { DLOG("destructing disk_io_thread\n"); #if TORRENT_USE_ASSERTS // by now, all pieces should have been evicted std::pair pieces = m_disk_cache.all_pieces(); TORRENT_ASSERT(pieces.first == pieces.second); #endif TORRENT_ASSERT(m_magic == 0x1337); #if TORRENT_USE_ASSERTS m_magic = 0xdead; #endif } void disk_io_thread::abort(bool wait) { m_abort = true; if (m_num_threads == 0) { abort_jobs(); } else { set_num_threads(0, wait); } } // TODO: 1 it would be nice to have the number of threads be set dynamically void disk_io_thread::set_num_threads(int i, bool wait) { TORRENT_ASSERT(m_magic == 0x1337); if (i == m_num_threads) return; if (i > m_num_threads) { while (m_num_threads < i) { int thread_id = (++m_num_threads) - 1; thread_type_t type = generic_thread; // this keeps the io_service::run() call blocked from returning. // When shutting down, it's possible that the event queue is drained // before the disk_io_thread has posted its last callback. When this // happens, the io_service will have a pending callback from the // disk_io_thread, but the event loop is not running. this means // that the event is destructed after the disk_io_thread. If the // event refers to a disk buffer it will try to free it, but the // buffer pool won't exist anymore, and crash. This prevents that. boost::shared_ptr work = boost::make_shared(boost::ref(m_ios)); // every 4:th thread is a hasher thread if ((thread_id & hasher_thread_mask) == hasher_thread_mask) type = hasher_thread; m_threads.push_back(boost::shared_ptr( new thread(boost::bind(&disk_io_thread::thread_fun, this , thread_id, type, work)))); } } else { while (m_num_threads > i) { --m_num_threads; } mutex::scoped_lock l(m_job_mutex); m_job_cond.notify_all(); m_hash_job_cond.notify_all(); l.unlock(); if (wait) for (int j = m_num_threads; j < m_threads.size(); ++j) m_threads[j]->join(); // this will detach the threads m_threads.resize(m_num_threads); } } void disk_io_thread::reclaim_block(block_cache_reference ref) { TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(ref.storage); m_blocks_to_reclaim.push_back(ref); if (m_outstanding_reclaim_message) return; m_ios.post(boost::bind(&disk_io_thread::commit_reclaimed_blocks, this)); m_outstanding_reclaim_message = true; } void disk_io_thread::commit_reclaimed_blocks() { TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(m_outstanding_reclaim_message); m_outstanding_reclaim_message = false; mutex::scoped_lock l(m_cache_mutex); for (int i = 0; i < m_blocks_to_reclaim.size(); ++i) m_disk_cache.reclaim_block(m_blocks_to_reclaim[i]); m_blocks_to_reclaim.clear(); } void disk_io_thread::set_settings(settings_pack const* pack, alert_manager& alerts) { TORRENT_ASSERT(m_magic == 0x1337); mutex::scoped_lock l(m_cache_mutex); apply_pack(pack, m_settings); error_code ec; m_disk_cache.set_settings(m_settings, ec); m_file_pool.resize(m_settings.get_int(settings_pack::file_pool_size)); #ifndef TORRENT_NO_DEPRECATE if (ec && alerts.should_post()) { alerts.emplace_alert(ec); } #else TORRENT_UNUSED(alerts); #endif } // flush all blocks that are below p->hash.offset, since we've // already hashed those blocks, they won't cause any read-back int disk_io_thread::try_flush_hashed(cached_piece_entry* p, int cont_block , jobqueue_t& completed_jobs, mutex::scoped_lock& l) { TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(l.locked()); TORRENT_ASSERT(cont_block > 0); if (p->hash == 0 && !p->hashing_done) { DLOG("try_flush_hashed: (%d) no hash\n", int(p->piece)); return 0; } if (p->num_dirty == 0) { DLOG("try_flush_hashed: no dirty blocks\n"); return 0; } // end is one past the end // round offset up to include the last block, which might // have an odd size int block_size = m_disk_cache.block_size(); int end = p->hashing_done ? p->blocks_in_piece : (p->hash->offset + block_size - 1) / block_size; // nothing has been hashed yet, don't flush anything if (end == 0 && !p->need_readback) return 0; // the number of contiguous blocks we need to be allowed to flush int block_limit = (std::min)(cont_block, int(p->blocks_in_piece)); // if everything has been hashed, we might as well flush everything // regardless of the contiguous block restriction if (end == int(p->blocks_in_piece)) block_limit = 1; if (p->need_readback) { // if this piece needs a read-back already, don't // try to keep it from being flushed, since we'll // need to read it back regardless. Flushing will // save blocks that can be used to "save" other // pieces from being fllushed prematurely end = int(p->blocks_in_piece); } TORRENT_ASSERT(end <= p->blocks_in_piece); // count number of blocks that would be flushed int num_blocks = 0; for (int i = end-1; i >= 0; --i) num_blocks += (p->blocks[i].dirty && !p->blocks[i].pending); // we did not satisfy the block_limit requirement // i.e. too few blocks would be flushed at this point, put it off if (block_limit > num_blocks) return 0; // if the cache line size is larger than a whole piece, hold // off flushing this piece until enough adjacent pieces are // full as well. int cont_pieces = cont_block / p->blocks_in_piece; // at this point, we may enforce flushing full cache stripes even when // they span multiple pieces. This won't necessarily work in the general // case, because it assumes that the piece picker will have an affinity // to download whole stripes at a time. This is why this setting is turned // off by default, flushing only one piece at a time if (cont_pieces <= 1 || m_settings.get_bool(settings_pack::allow_partial_disk_writes)) { DLOG("try_flush_hashed: (%d) blocks_in_piece: %d end: %d\n" , int(p->piece), int(p->blocks_in_piece), end); return flush_range(p, 0, end, completed_jobs, l); } // piece range int range_start = (p->piece / cont_pieces) * cont_pieces; int range_end = (std::min)(range_start + cont_pieces, p->storage->files()->num_pieces()); // look through all the pieces in this range to see if // they are ready to be flushed. If so, flush them all, // otherwise, hold off bool range_full = true; cached_piece_entry* first_piece = NULL; DLOG("try_flush_hashed: multi-piece: "); for (int i = range_start; i < range_end; ++i) { if (i == p->piece) { if (i == range_start) first_piece = p; DLOG("[%d self] ", i); continue; } cached_piece_entry* pe = m_disk_cache.find_piece(p->storage.get(), i); if (pe == NULL) { DLOG("[%d NULL] ", i); range_full = false; break; } if (i == range_start) first_piece = pe; // if this is a read-cache piece, it has already been flushed if (pe->cache_state != cached_piece_entry::write_lru) { DLOG("[%d read-cache] ", i); continue; } int hash_cursor = pe->hash ? pe->hash->offset / block_size : 0; // if the piece has all blocks, and they're all dirty, and they've // all been hashed, then this piece is eligible for flushing if (pe->num_dirty == pe->blocks_in_piece && (pe->hashing_done || hash_cursor == pe->blocks_in_piece || m_settings.get_bool(settings_pack::disable_hash_checks))) { DLOG("[%d hash-done] ", i); continue; } if (pe->num_dirty < pe->blocks_in_piece) { DLOG("[%d dirty:%d] ", i, int(pe->num_dirty)); } else if (pe->hashing_done == 0 && hash_cursor < pe->blocks_in_piece) { DLOG("[%d cursor:%d] ", i, hash_cursor); } else { DLOG("[%d xx] ", i); } // TOOD: in this case, the piece should probably not be flushed yet. are there // any more cases where it should? range_full = false; break; } if (!range_full) { DLOG("not flushing\n"); return 0; } DLOG("\n"); // now, build a iovec for all pieces that we want to flush, so that they // can be flushed in a single atomic operation. This is especially important // when there are more than 1 disk thread, to make sure they don't // interleave in undesired places. // in order to remember where each piece boundary ended up in the iovec, // we keep the indices in the iovec_offset array cont_pieces = range_end - range_start; file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, p->blocks_in_piece * cont_pieces); int* flushing = TORRENT_ALLOCA(int, p->blocks_in_piece * cont_pieces); // this is the offset into iov and flushing for each piece int* iovec_offset = TORRENT_ALLOCA(int, cont_pieces + 1); int iov_len = 0; // this is the block index each piece starts at int block_start = 0; // keep track of the pieces that have had their refcount incremented // so we know to decrement them later int* refcount_pieces = TORRENT_ALLOCA(int, cont_pieces); for (int i = 0; i < cont_pieces; ++i) { cached_piece_entry* pe; if (i == p->piece) pe = p; else pe = m_disk_cache.find_piece(p->storage.get(), range_start + i); if (pe == NULL || pe->cache_state != cached_piece_entry::write_lru) { refcount_pieces[i] = 0; iovec_offset[i] = iov_len; block_start += p->blocks_in_piece; continue; } iovec_offset[i] = iov_len; refcount_pieces[i] = 1; TORRENT_ASSERT_VAL(pe->cache_state <= cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(piece_log_t::flushing, -1)); #endif ++pe->piece_refcount; iov_len += build_iovec(pe, 0, p->blocks_in_piece , iov + iov_len, flushing + iov_len, block_start); block_start += p->blocks_in_piece; } iovec_offset[cont_pieces] = iov_len; // ok, now we have one (or more, but hopefully one) contiguous // iovec array. Now, flush it to disk TORRENT_ASSERT(first_piece != NULL); if (iov_len == 0) { // we may not exit here if we incremented any piece refcounters TORRENT_ASSERT(cont_pieces == 0); DLOG(" iov_len: 0 cont_pieces: %d range_start: %d range_end: %d\n" , cont_pieces, range_start, range_end); return 0; } l.unlock(); storage_error error; flush_iovec(first_piece, iov, flushing, iov_len, error); l.lock(); block_start = 0; for (int i = 0; i < cont_pieces; ++i) { cached_piece_entry* pe; if (i == p->piece) pe = p; else pe = m_disk_cache.find_piece(p->storage.get(), range_start + i); if (pe == NULL) { DLOG("iovec_flushed: piece %d gone!\n", range_start + i); TORRENT_PIECE_ASSERT(refcount_pieces[i] == 0, pe); block_start += p->blocks_in_piece; continue; } if (refcount_pieces[i]) { TORRENT_PIECE_ASSERT(pe->piece_refcount > 0, pe); --pe->piece_refcount; m_disk_cache.maybe_free_piece(pe); } const int block_diff = iovec_offset[i+1] - iovec_offset[i]; iovec_flushed(pe, flushing + iovec_offset[i], block_diff , block_start, error, completed_jobs); block_start += p->blocks_in_piece; } // if the cache is under high pressure, we need to evict // the blocks we just flushed to make room for more write pieces int evict = m_disk_cache.num_to_evict(0); if (evict > 0) m_disk_cache.try_evict_blocks(evict); return iov_len; } // iov and flushing are expected to be arrays to at least pe->blocks_in_piece // items in them. Returns the numner of iovecs written to the iov array. // The same number of block indices are written to the flushing array. These // are block indices that the respecivec iovec structure refers to, since // we might not be able to flush everything as a single contiguous block, // the block indices indicates where the block run is broken // the cache needs to be locked when calling this function // block_base_index is the offset added to every block index written to // the flushing array. This can be used when building iovecs spanning // multiple pieces, the subsequent pieces after the first one, must have // their block indices start where the previous one left off int disk_io_thread::build_iovec(cached_piece_entry* pe, int start, int end , file::iovec_t* iov, int* flushing, int block_base_index) { INVARIANT_CHECK; DLOG("build_iovec: piece=%d [%d, %d)\n" , int(pe->piece), start, end); TORRENT_PIECE_ASSERT(start >= 0, pe); TORRENT_PIECE_ASSERT(start < end, pe); end = (std::min)(end, int(pe->blocks_in_piece)); int piece_size = pe->storage->files()->piece_size(pe->piece); TORRENT_PIECE_ASSERT(piece_size > 0, pe); int iov_len = 0; // the blocks we're flushing int num_flushing = 0; #if DEBUG_DISK_THREAD DLOG("build_iov: piece: %d [", int(pe->piece)); for (int i = 0; i < start; ++i) DLOG("."); #endif int block_size = m_disk_cache.block_size(); int size_left = piece_size; for (int i = start; i < end; ++i, size_left -= block_size) { TORRENT_PIECE_ASSERT(size_left > 0, pe); // don't flush blocks that are empty (buf == 0), not dirty // (read cache blocks), or pending (already being written) if (pe->blocks[i].buf == NULL || pe->blocks[i].pending || !pe->blocks[i].dirty) { DLOG("-"); continue; } // if we fail to lock the block, it' no longer in the cache bool locked = m_disk_cache.inc_block_refcount(pe, i, block_cache::ref_flushing); // it should always suceed, since it's a dirty block, and // should never have been marked as volatile TORRENT_ASSERT(locked); TORRENT_ASSERT(pe->cache_state != cached_piece_entry::volatile_read_lru); TORRENT_UNUSED(locked); flushing[num_flushing++] = i + block_base_index; iov[iov_len].iov_base = pe->blocks[i].buf; iov[iov_len].iov_len = (std::min)(block_size, size_left); ++iov_len; pe->blocks[i].pending = true; DLOG("x"); } DLOG("]\n"); TORRENT_PIECE_ASSERT(iov_len == num_flushing, pe); return iov_len; } // does the actual writing to disk // the cached_piece_entry is supposed to point to the // first piece, if the iovec spans multiple pieces void disk_io_thread::flush_iovec(cached_piece_entry* pe , file::iovec_t const* iov, int const* flushing , int num_blocks, storage_error& error) { TORRENT_PIECE_ASSERT(!error, pe); TORRENT_PIECE_ASSERT(num_blocks > 0, pe); m_stats_counters.inc_stats_counter(counters::num_writing_threads, 1); time_point const start_time = clock_type::now(); int const block_size = m_disk_cache.block_size(); #if DEBUG_DISK_THREAD DLOG("flush_iovec: piece: %d [ ", int(pe->piece)); for (int i = 0; i < num_blocks; ++i) DLOG("%d ", flushing[i]); DLOG("]\n"); #endif int const file_flags = m_settings.get_bool(settings_pack::coalesce_writes) ? file::coalesce_buffers : 0; // issue the actual write operation file::iovec_t const* iov_start = iov; int flushing_start = 0; int piece = pe->piece; int blocks_in_piece = pe->blocks_in_piece; bool failed = false; for (int i = 1; i <= num_blocks; ++i) { if (i < num_blocks && flushing[i] == flushing[i-1]+1) continue; int ret = pe->storage->get_storage_impl()->writev(iov_start , i - flushing_start , piece + flushing[flushing_start] / blocks_in_piece , (flushing[flushing_start] % blocks_in_piece) * block_size , file_flags, error); if (ret < 0 || error) failed = true; iov_start = &iov[i]; flushing_start = i; } m_stats_counters.inc_stats_counter(counters::num_writing_threads, -1); if (!failed) { TORRENT_PIECE_ASSERT(!error, pe); boost::uint32_t const write_time = total_microseconds(clock_type::now() - start_time); m_stats_counters.inc_stats_counter(counters::num_blocks_written, num_blocks); m_stats_counters.inc_stats_counter(counters::num_write_ops); m_stats_counters.inc_stats_counter(counters::disk_write_time, write_time); m_stats_counters.inc_stats_counter(counters::disk_job_time, write_time); #if DEBUG_DISK_THREAD DLOG("flush_iovec: %d\n", num_blocks); #endif } #if DEBUG_DISK_THREAD else { DLOG("flush_iovec: error: (%d) %s\n" , error.ec.value(), error.ec.message().c_str()); } #endif } // It is necessary to call this function with the blocks produced by // build_iovec, to reset their state to not being flushed anymore // the cache needs to be locked when calling this function bool disk_io_thread::iovec_flushed(cached_piece_entry* pe , int* flushing, int num_blocks, int block_offset , storage_error const& error , jobqueue_t& completed_jobs) { for (int i = 0; i < num_blocks; ++i) flushing[i] -= block_offset; #if DEBUG_DISK_THREAD DLOG("iovec_flushed: piece: %d block_offset: %d [ " , int(pe->piece), block_offset); for (int i = 0; i < num_blocks; ++i) DLOG("%d ", flushing[i]); DLOG("]\n"); #endif if (m_disk_cache.blocks_flushed(pe, flushing, num_blocks)) return true; int block_size = m_disk_cache.block_size(); if (error) { fail_jobs_impl(error, pe->jobs, completed_jobs); } else { disk_io_job* j = pe->jobs.get_all(); while (j) { disk_io_job* next = j->next; j->next = NULL; TORRENT_PIECE_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage, pe); TORRENT_PIECE_ASSERT(j->piece == pe->piece, pe); if (j->completed(pe, block_size)) { j->ret = j->d.io.buffer_size; j->error = error; completed_jobs.push_back(j); } else { pe->jobs.push_back(j); } j = next; } } return false; } // issues write operations for blocks in the given // range on the given piece. int disk_io_thread::flush_range(cached_piece_entry* pe, int start, int end , jobqueue_t& completed_jobs, mutex::scoped_lock& l) { TORRENT_ASSERT(l.locked()); INVARIANT_CHECK; DLOG("flush_range: piece=%d [%d, %d)\n" , int(pe->piece), start, end); TORRENT_PIECE_ASSERT(start >= 0, pe); TORRENT_PIECE_ASSERT(start < end, pe); file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, pe->blocks_in_piece); int* flushing = TORRENT_ALLOCA(int, pe->blocks_in_piece); int iov_len = build_iovec(pe, start, end, iov, flushing, 0); if (iov_len == 0) return 0; TORRENT_PIECE_ASSERT(pe->cache_state <= cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(piece_log_t::flush_range, -1)); #endif ++pe->piece_refcount; l.unlock(); storage_error error; flush_iovec(pe, iov, flushing, iov_len, error); l.lock(); TORRENT_PIECE_ASSERT(pe->piece_refcount > 0, pe); --pe->piece_refcount; if (!iovec_flushed(pe, flushing, iov_len, 0, error, completed_jobs)) m_disk_cache.maybe_free_piece(pe); // if the cache is under high pressure, we need to evict // the blocks we just flushed to make room for more write pieces int evict = m_disk_cache.num_to_evict(0); if (evict > 0) m_disk_cache.try_evict_blocks(evict); return iov_len; } void disk_io_thread::fail_jobs(storage_error const& e, jobqueue_t& jobs_) { jobqueue_t jobs; fail_jobs_impl(e, jobs_, jobs); if (jobs.size()) add_completed_jobs(jobs); } void disk_io_thread::fail_jobs_impl(storage_error const& e, jobqueue_t& src, jobqueue_t& dst) { while (src.size()) { disk_io_job* j = src.pop_front(); TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage); j->ret = -1; j->error = e; dst.push_back(j); } } void disk_io_thread::flush_piece(cached_piece_entry* pe, int flags , jobqueue_t& completed_jobs, mutex::scoped_lock& l) { TORRENT_ASSERT(l.locked()); if (flags & flush_delete_cache) { // delete dirty blocks and post handlers with // operation_aborted error code fail_jobs_impl(storage_error(boost::asio::error::operation_aborted) , pe->jobs, completed_jobs); fail_jobs_impl(storage_error(boost::asio::error::operation_aborted) , pe->read_jobs, completed_jobs); m_disk_cache.abort_dirty(pe); } else if ((flags & flush_write_cache) && pe->num_dirty > 0) { // issue write commands flush_range(pe, 0, INT_MAX, completed_jobs, l); // if we're also flushing the read cache, this piece // should be removed as soon as all write jobs finishes // otherwise it will turn into a read piece } // mark_for_eviction may erase the piece from the cache, that's // why we don't have the 'i' iterator referencing it at this point if (flags & (flush_read_cache | flush_delete_cache)) { fail_jobs_impl(storage_error(boost::asio::error::operation_aborted), pe->jobs, completed_jobs); // we're removing the torrent, don't keep any entries around in the // ghost list m_disk_cache.mark_for_eviction(pe, block_cache::disallow_ghost); } } void disk_io_thread::flush_cache(piece_manager* storage, boost::uint32_t flags , jobqueue_t& completed_jobs, mutex::scoped_lock& l) { if (storage) { boost::unordered_set const& pieces = storage->cached_pieces(); std::vector piece_index; piece_index.reserve(pieces.size()); for (boost::unordered_set::const_iterator i = pieces.begin() , end(pieces.end()); i != end; ++i) { TORRENT_ASSERT((*i)->get_storage() == storage); if ((*i)->get_storage() != storage) continue; piece_index.push_back((*i)->piece); } for (std::vector::iterator i = piece_index.begin() , end(piece_index.end()); i != end; ++i) { cached_piece_entry* pe = m_disk_cache.find_piece(storage, *i); if (pe == NULL) continue; TORRENT_PIECE_ASSERT(pe->storage.get() == storage, pe); flush_piece(pe, flags, completed_jobs, l); } #if TORRENT_USE_ASSERTS TORRENT_ASSERT(l.locked()); // if the user asked to delete the cache for this storage // we really should not have any pieces left. This is only called // from disk_io_thread::do_delete, which is a fence job and should // have any other jobs active, i.e. there should not be any references // keeping pieces or blocks alive if ((flags & flush_delete_cache) && (flags & flush_expect_clear)) { boost::unordered_set const& storage_pieces = storage->cached_pieces(); for (boost::unordered_set::const_iterator i = storage_pieces.begin() , end(storage_pieces.end()); i != end; ++i) { cached_piece_entry* pe = m_disk_cache.find_piece(storage, (*i)->piece); TORRENT_PIECE_ASSERT(pe->num_dirty == 0, pe); } } #endif } else { std::pair range = m_disk_cache.all_pieces(); while (range.first != range.second) { // TODO: it would be nice to optimize this by having the cache // pieces also ordered by if ((flags & (flush_read_cache | flush_delete_cache)) == 0) { // if we're not flushing the read cache, and not deleting the // cache, skip pieces with no dirty blocks, i.e. read cache // pieces while (range.first->num_dirty == 0) { ++range.first; if (range.first == range.second) return; } } cached_piece_entry* pe = const_cast(&*range.first); flush_piece(pe, flags, completed_jobs, l); range = m_disk_cache.all_pieces(); } } } // this is called if we're exceeding (or about to exceed) the cache // size limit. This means we should not restrict ourselves to contiguous // blocks of write cache line size, but try to flush all old blocks // this is why we pass in 1 as cont_block to the flushing functions void disk_io_thread::try_flush_write_blocks(int num, jobqueue_t& completed_jobs , mutex::scoped_lock& l) { DLOG("try_flush_write_blocks: %d\n", num); list_iterator range = m_disk_cache.write_lru_pieces(); std::vector, int> > pieces; pieces.reserve(m_disk_cache.num_write_lru_pieces()); for (list_iterator p = range; p.get() && num > 0; p.next()) { cached_piece_entry* e = p.get(); if (e->num_dirty == 0) continue; pieces.push_back(std::make_pair(e->storage, int(e->piece))); } for (std::vector, int> >::iterator i = pieces.begin() , end(pieces.end()); i != end; ++i) { // TODO: instead of doing a lookup each time through the loop, save // cached_piece_entry pointers with piece_refcount incremented to pin them cached_piece_entry* pe = m_disk_cache.find_piece(i->first.get(), i->second); if (pe == NULL) continue; // another thread may flush this piece while we're looping and // evict it into a read piece and then also evict it to ghost if (pe->cache_state != cached_piece_entry::write_lru) continue; #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(piece_log_t::try_flush_write_blocks, -1)); #endif ++pe->piece_refcount; kick_hasher(pe, l); num -= try_flush_hashed(pe, 1, completed_jobs, l); --pe->piece_refcount; m_disk_cache.maybe_free_piece(pe); } // when the write cache is under high pressure, it is likely // counter productive to actually do this, since a piece may // not have had its flush_hashed job run on it // so only do it if no other thread is currently flushing if (num == 0 || m_stats_counters[counters::num_writing_threads] > 0) return; // if we still need to flush blocks, start over and flush // everything in LRU order (degrade to lru cache eviction) for (std::vector, int> >::iterator i = pieces.begin() , end(pieces.end()); i != end; ++i) { cached_piece_entry* pe = m_disk_cache.find_piece(i->first.get(), i->second); if (pe == NULL) continue; if (pe->num_dirty == 0) continue; // another thread may flush this piece while we're looping and // evict it into a read piece and then also evict it to ghost if (pe->cache_state != cached_piece_entry::write_lru) continue; // don't flush blocks that are being hashed by another thread if (pe->num_dirty == 0 || pe->hashing) continue; #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(piece_log_t::try_flush_write_blocks2, -1)); #endif ++pe->piece_refcount; num -= flush_range(pe, 0, INT_MAX, completed_jobs, l); --pe->piece_refcount; m_disk_cache.maybe_free_piece(pe); } } void disk_io_thread::flush_expired_write_blocks(jobqueue_t& completed_jobs , mutex::scoped_lock& l) { DLOG("flush_expired_write_blocks\n"); time_point now = aux::time_now(); time_duration expiration_limit = seconds(m_settings.get_int(settings_pack::cache_expiry)); #if TORRENT_USE_ASSERTS time_point timeout = min_time(); #endif cached_piece_entry** to_flush = TORRENT_ALLOCA(cached_piece_entry*, 200); int num_flush = 0; for (list_iterator p = m_disk_cache.write_lru_pieces(); p.get(); p.next()) { cached_piece_entry* e = p.get(); #if TORRENT_USE_ASSERTS TORRENT_PIECE_ASSERT(e->expire >= timeout, e); timeout = e->expire; #endif // since we're iterating in order of last use, if this piece // shouldn't be evicted, none of the following ones will either if (now - e->expire < expiration_limit) break; if (e->num_dirty == 0) continue; TORRENT_PIECE_ASSERT(e->cache_state <= cached_piece_entry::read_lru1 || e->cache_state == cached_piece_entry::read_lru2, e); #if TORRENT_USE_ASSERTS e->piece_log.push_back(piece_log_t(piece_log_t::flush_expired, -1)); #endif ++e->piece_refcount; // We can rely on the piece entry not being removed by // incrementing the piece_refcount to_flush[num_flush++] = e; if (num_flush == 200) break; } for (int i = 0; i < num_flush; ++i) { flush_range(to_flush[i], 0, INT_MAX, completed_jobs, l); TORRENT_ASSERT(to_flush[i]->piece_refcount > 0); --to_flush[i]->piece_refcount; m_disk_cache.maybe_free_piece(to_flush[i]); } } namespace { typedef int (disk_io_thread::*disk_io_fun_t)(disk_io_job* j, jobqueue_t& completed_jobs); // this is a jump-table for disk I/O jobs const disk_io_fun_t job_functions[] = { &disk_io_thread::do_read, &disk_io_thread::do_write, &disk_io_thread::do_hash, &disk_io_thread::do_move_storage, &disk_io_thread::do_release_files, &disk_io_thread::do_delete_files, &disk_io_thread::do_check_fastresume, &disk_io_thread::do_save_resume_data, &disk_io_thread::do_rename_file, &disk_io_thread::do_stop_torrent, #ifndef TORRENT_NO_DEPRECATE &disk_io_thread::do_cache_piece, &disk_io_thread::do_finalize_file, #endif &disk_io_thread::do_flush_piece, &disk_io_thread::do_flush_hashed, &disk_io_thread::do_flush_storage, &disk_io_thread::do_trim_cache, &disk_io_thread::do_file_priority, &disk_io_thread::do_load_torrent, &disk_io_thread::do_clear_piece, &disk_io_thread::do_tick, }; } // anonymous namespace // evict and/or flush blocks if we're exceeding the cache size // or used to exceed it and haven't dropped below the low watermark yet // the low watermark is dynamic, based on the number of peers waiting // on buffers to free up. The more waiters, the lower the low watermark // is. Because of this, the target for flushing jobs may have dropped // below the number of blocks we flushed by the time we're done flushing // that's why we need to call this fairly often. Both before and after // a disk job is executed void disk_io_thread::check_cache_level(mutex::scoped_lock& l, jobqueue_t& completed_jobs) { // when the read cache is disabled, always try to evict all read cache // blocks if (!m_settings.get_bool(settings_pack::use_read_cache)) { int const evict = m_disk_cache.read_cache_size(); m_disk_cache.try_evict_blocks(evict); } int evict = m_disk_cache.num_to_evict(0); if (evict > 0) { evict = m_disk_cache.try_evict_blocks(evict); // don't evict write jobs if at least one other thread // is flushing right now. Doing so could result in // unnecessary flushing of the wrong pieces if (evict > 0 && m_stats_counters[counters::num_writing_threads] == 0) { try_flush_write_blocks(evict, completed_jobs, l); } } } void disk_io_thread::perform_job(disk_io_job* j, jobqueue_t& completed_jobs) { INVARIANT_CHECK; TORRENT_ASSERT(j->next == 0); TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage); #if DEBUG_DISK_THREAD { mutex::scoped_lock l(m_cache_mutex); DLOG("perform_job job: %s ( %s%s) piece: %d offset: %d outstanding: %d\n" , job_action_name[j->action] , (j->flags & disk_io_job::fence) ? "fence ": "" , (j->flags & disk_io_job::force_copy) ? "force_copy ": "" , j->piece, j->d.io.offset , j->storage ? j->storage->num_outstanding_jobs() : -1); } #endif boost::shared_ptr storage = j->storage; #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS if (storage) { mutex::scoped_lock l(m_cache_mutex); boost::unordered_set const& pieces = storage->cached_pieces(); for (boost::unordered_set::const_iterator i = pieces.begin() , end(pieces.end()); i != end; ++i) { TORRENT_ASSERT((*i)->storage == storage); } } #endif // TODO: instead of doing this. pass in the settings to each storage_interface // call. Each disk thread could hold its most recent understanding of the settings // in a shared_ptr, and update it every time it wakes up from a job. That way // each access to the settings won't require a mutex to be held. if (storage && storage->get_storage_impl()->m_settings == 0) storage->get_storage_impl()->m_settings = &m_settings; TORRENT_ASSERT(j->action < sizeof(job_functions)/sizeof(job_functions[0])); m_stats_counters.inc_stats_counter(counters::num_running_disk_jobs, 1); // call disk function int ret = (this->*(job_functions[j->action]))(j, completed_jobs); // note that -2 erros are OK TORRENT_ASSERT(ret != -1 || (j->error.ec && j->error.operation != 0)); m_stats_counters.inc_stats_counter(counters::num_running_disk_jobs, -1); mutex::scoped_lock l(m_cache_mutex); if (m_cache_check_state == cache_check_idle) { m_cache_check_state = cache_check_active; while (m_cache_check_state != cache_check_idle) { check_cache_level(l, completed_jobs); TORRENT_ASSERT(l.locked()); --m_cache_check_state; } } else { m_cache_check_state = cache_check_reinvoke; } l.unlock(); if (ret == retry_job) { mutex::scoped_lock l2(m_job_mutex); // to avoid busy looping here, give up // our quanta in case there aren't any other // jobs to run in between // TODO: a potentially more efficient solution would be to have a special // queue for retry jobs, that's only ever run when a job completes, in // any thread. It would only work if counters::num_running_disk_jobs > 0 TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage); bool need_sleep = m_queued_jobs.empty(); m_queued_jobs.push_back(j); l2.unlock(); if (need_sleep) sleep(0); return; } if (ret == defer_handler) return; j->ret = ret; completed_jobs.push_back(j); } int disk_io_thread::do_uncached_read(disk_io_job* j) { j->buffer.disk_block = m_disk_cache.allocate_buffer("send buffer"); if (j->buffer.disk_block == 0) { j->error.ec = error::no_memory; j->error.operation = storage_error::alloc_cache_piece; return -1; } time_point const start_time = clock_type::now(); int const file_flags = file_flags_for_job(j , m_settings.get_bool(settings_pack::coalesce_reads)); file::iovec_t b = { j->buffer.disk_block, size_t(j->d.io.buffer_size) }; int ret = j->storage->get_storage_impl()->readv(&b, 1 , j->piece, j->d.io.offset, file_flags, j->error); TORRENT_ASSERT(ret >= 0 || (j->error.ec && j->error.operation != 0)); if (!j->error.ec) { boost::uint32_t const read_time = total_microseconds(clock_type::now() - start_time); m_stats_counters.inc_stats_counter(counters::num_read_back); m_stats_counters.inc_stats_counter(counters::num_blocks_read); m_stats_counters.inc_stats_counter(counters::num_read_ops); m_stats_counters.inc_stats_counter(counters::disk_read_time, read_time); m_stats_counters.inc_stats_counter(counters::disk_job_time, read_time); } return ret; } int disk_io_thread::do_read(disk_io_job* j, jobqueue_t& completed_jobs) { int block_size = m_disk_cache.block_size(); int piece_size = j->storage->files()->piece_size(j->piece); int blocks_in_piece = (piece_size + block_size - 1) / block_size; int iov_len = m_disk_cache.pad_job(j, blocks_in_piece , m_settings.get_int(settings_pack::read_cache_line_size)); file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, iov_len); mutex::scoped_lock l(m_cache_mutex); int evict = m_disk_cache.num_to_evict(iov_len); if (evict > 0) m_disk_cache.try_evict_blocks(evict); cached_piece_entry* pe = m_disk_cache.find_piece(j); if (pe == NULL) { l.unlock(); return do_uncached_read(j); } TORRENT_PIECE_ASSERT(pe->outstanding_read == 1, pe); l.unlock(); // then we'll actually allocate the buffers int ret = m_disk_cache.allocate_iovec(iov, iov_len); if (ret < 0) { ret = do_uncached_read(j); mutex::scoped_lock l2(m_cache_mutex); pe = m_disk_cache.find_piece(j); if (pe) maybe_issue_queued_read_jobs(pe, completed_jobs); return ret; } // this is the offset that's aligned to block boundaries boost::int64_t adjusted_offset = j->d.io.offset & ~(block_size-1); // if this is the last piece, adjust the size of the // last buffer to match up iov[iov_len-1].iov_len = (std::min)(int(piece_size - adjusted_offset) - (iov_len-1) * block_size, block_size); TORRENT_ASSERT(iov[iov_len-1].iov_len > 0); // at this point, all the buffers are allocated and iov is initizalied // and the blocks have their refcounters incremented, so no other thread // can remove them. We can now release the cache mutex and dive into the // disk operations. int const file_flags = file_flags_for_job(j , m_settings.get_bool(settings_pack::coalesce_reads)); time_point const start_time = clock_type::now(); ret = j->storage->get_storage_impl()->readv(iov, iov_len , j->piece, adjusted_offset, file_flags, j->error); TORRENT_ASSERT(ret >= 0 || (j->error.ec && j->error.operation != 0)); if (!j->error.ec) { boost::uint32_t const read_time = total_microseconds(clock_type::now() - start_time); m_stats_counters.inc_stats_counter(counters::num_blocks_read, iov_len); m_stats_counters.inc_stats_counter(counters::num_read_ops); m_stats_counters.inc_stats_counter(counters::disk_read_time, read_time); m_stats_counters.inc_stats_counter(counters::disk_job_time, read_time); } l.lock(); if (ret < 0) { // read failed. free buffers and return error m_disk_cache.free_iovec(iov, iov_len); pe = m_disk_cache.find_piece(j); if (pe == NULL) { // the piece is supposed to be allocated when the // disk job is allocated TORRENT_ASSERT(false); return ret; } TORRENT_PIECE_ASSERT(pe->outstanding_read == 1, pe); if (pe->read_jobs.size() > 0) fail_jobs_impl(j->error, pe->read_jobs, completed_jobs); TORRENT_PIECE_ASSERT(pe->read_jobs.size() == 0, pe); pe->outstanding_read = 0; #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(piece_log_t::clear_outstanding_jobs)); #endif m_disk_cache.maybe_free_piece(pe); return ret; } int block = j->d.io.offset / block_size; #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(j->action, block)); #endif // as soon we insert the blocks they may be evicted // (if using purgeable memory). In order to prevent that // until we can read from them, increment the refcounts m_disk_cache.insert_blocks(pe, block, iov, iov_len, j, block_cache::blocks_inc_refcount); TORRENT_ASSERT(pe->blocks[block].buf); int tmp = m_disk_cache.try_read(j, true); // This should always succeed because we just checked to see there is a // buffer for this block TORRENT_ASSERT(tmp >= 0); TORRENT_UNUSED(tmp); maybe_issue_queued_read_jobs(pe, completed_jobs); for (int i = 0; i < iov_len; ++i, ++block) m_disk_cache.dec_block_refcount(pe, block, block_cache::ref_reading); return j->d.io.buffer_size; } void disk_io_thread::maybe_issue_queued_read_jobs(cached_piece_entry* pe , jobqueue_t& completed_jobs) { TORRENT_PIECE_ASSERT(pe->outstanding_read == 1, pe); // if we're shutting down, just cancel the jobs if (m_abort) { fail_jobs_impl(storage_error(boost::asio::error::operation_aborted) , pe->read_jobs, completed_jobs); TORRENT_PIECE_ASSERT(pe->read_jobs.size() == 0, pe); pe->outstanding_read = 0; #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(piece_log_t::clear_outstanding_jobs)); #endif m_disk_cache.maybe_free_piece(pe); return; } // while we were reading, there may have been a few jobs // that got queued up also wanting to read from this piece. // Any job that is a cache hit now, complete it immediately. // Then, issue the first non-cache-hit job. Once it complete // it will keep working off this list jobqueue_t stalled_jobs; pe->read_jobs.swap(stalled_jobs); // the next job to issue (i.e. this is a cache-miss) disk_io_job* next_job = NULL; while (stalled_jobs.size() > 0) { disk_io_job* j = stalled_jobs.pop_front(); TORRENT_ASSERT(j->flags & disk_io_job::in_progress); int ret = m_disk_cache.try_read(j); if (ret >= 0) { // cache-hit m_stats_counters.inc_stats_counter(counters::num_blocks_cache_hits); DLOG("do_read: cache hit\n"); j->flags |= disk_io_job::cache_hit; j->ret = ret; completed_jobs.push_back(j); } else if (ret == -2) { // error j->ret = disk_io_job::operation_failed; completed_jobs.push_back(j); } else { // cache-miss, issue the first one // put back the rest if (next_job == NULL) { next_job = j; } else { TORRENT_PIECE_ASSERT(j->piece == pe->piece, pe); pe->read_jobs.push_back(j); } } } if (next_job) { add_job(next_job, false); } else { TORRENT_PIECE_ASSERT(pe->read_jobs.size() == 0, pe); pe->outstanding_read = 0; #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(piece_log_t::clear_outstanding_jobs)); #endif m_disk_cache.maybe_free_piece(pe); } } int disk_io_thread::do_uncached_write(disk_io_job* j) { time_point const start_time = clock_type::now(); file::iovec_t const b = { j->buffer.disk_block, size_t(j->d.io.buffer_size) }; int const file_flags = file_flags_for_job(j , m_settings.get_bool(settings_pack::coalesce_writes)); m_stats_counters.inc_stats_counter(counters::num_writing_threads, 1); // the actual write operation int ret = j->storage->get_storage_impl()->writev(&b, 1 , j->piece, j->d.io.offset, file_flags, j->error); TORRENT_ASSERT(ret >= 0 || (j->error.ec && j->error.operation != 0)); m_stats_counters.inc_stats_counter(counters::num_writing_threads, -1); if (!j->error.ec) { boost::uint32_t const write_time = total_microseconds(clock_type::now() - start_time); m_stats_counters.inc_stats_counter(counters::num_blocks_written); m_stats_counters.inc_stats_counter(counters::num_write_ops); m_stats_counters.inc_stats_counter(counters::disk_write_time, write_time); m_stats_counters.inc_stats_counter(counters::disk_job_time, write_time); } m_disk_cache.free_buffer(j->buffer.disk_block); j->buffer.disk_block = NULL; return ret; } int disk_io_thread::do_write(disk_io_job* j, jobqueue_t& completed_jobs) { INVARIANT_CHECK; TORRENT_ASSERT(j->d.io.buffer_size <= m_disk_cache.block_size()); mutex::scoped_lock l(m_cache_mutex); cached_piece_entry* pe = m_disk_cache.find_piece(j); if (pe && pe->hashing_done) { #if TORRENT_USE_ASSERTS print_piece_log(pe->piece_log); #endif TORRENT_ASSERT(pe->blocks[j->d.io.offset / 16 / 1024].buf != j->buffer.disk_block); TORRENT_ASSERT(pe->blocks[j->d.io.offset / 16 / 1024].buf != NULL); j->error.ec = error::operation_aborted; j->error.operation = storage_error::write; return -1; } pe = m_disk_cache.add_dirty_block(j); if (pe) { #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(j->action, j->d.io.offset / 0x4000)); #endif if (!pe->hashing_done && pe->hash == 0 && !m_settings.get_bool(settings_pack::disable_hash_checks)) { pe->hash = new partial_hash; m_disk_cache.update_cache_state(pe); } TORRENT_PIECE_ASSERT(pe->cache_state <= cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); ++pe->piece_refcount; // see if we can progress the hash cursor with this new block kick_hasher(pe, l); TORRENT_PIECE_ASSERT(pe->cache_state <= cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); // flushes the piece to disk in case // it satisfies the condition for a write // piece to be flushed try_flush_hashed(pe, m_settings.get_int( settings_pack::write_cache_line_size), completed_jobs, l); --pe->piece_refcount; m_disk_cache.maybe_free_piece(pe); return defer_handler; } // ok, we should just perform this job right now. return do_uncached_write(j); } void disk_io_thread::async_read(piece_manager* storage, peer_request const& r , boost::function const& handler, void* requester , int flags) { INVARIANT_CHECK; #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif TORRENT_ASSERT(r.length <= m_disk_cache.block_size()); TORRENT_ASSERT(r.length <= 16 * 1024); DLOG("do_read piece: %d block: %d\n", r.piece, r.start / m_disk_cache.block_size()); disk_io_job* j = allocate_job(disk_io_job::read); j->storage = storage->shared_from_this(); j->piece = r.piece; j->d.io.offset = r.start; j->d.io.buffer_size = r.length; j->buffer.disk_block = 0; j->flags = flags; j->requester = requester; j->callback = handler; mutex::scoped_lock l(m_cache_mutex); int ret = prep_read_job_impl(j); l.unlock(); switch (ret) { case 0: if (handler) handler(j); free_job(j); break; case 1: add_job(j); break; } } // this function checks to see if a read job is a cache hit, // and if it doesn't have a picece allocated, it allocates // one and it sets outstanding_read flag and possibly queues // up the job in the piece read job list // the cache mutex must be held when calling this // // returns 0 if the job succeeded immediately // 1 if it needs to be added to the job queue // 2 if it was deferred and will be performed later (no need to // add it to the queue) int disk_io_thread::prep_read_job_impl(disk_io_job* j, bool check_fence) { TORRENT_ASSERT(j->action == disk_io_job::read); int ret = m_disk_cache.try_read(j); if (ret >= 0) { m_stats_counters.inc_stats_counter(counters::num_blocks_cache_hits); DLOG("do_read: cache hit\n"); j->flags |= disk_io_job::cache_hit; j->ret = ret; return 0; } else if (ret == -2) { j->error.ec = error::no_memory; j->error.operation = storage_error::alloc_cache_piece; j->ret = disk_io_job::operation_failed; return 0; } if (check_fence && j->storage->is_blocked(j)) { // this means the job was queued up inside storage m_stats_counters.inc_stats_counter(counters::blocked_disk_jobs); DLOG("blocked job: %s (torrent: %d total: %d)\n" , job_action_name[j->action], j->storage ? j->storage->num_blocked() : 0 , int(m_stats_counters[counters::blocked_disk_jobs])); return 2; } if (!m_settings.get_bool(settings_pack::use_read_cache) || m_settings.get_int(settings_pack::cache_size) == 0) { // if the read cache is disabled then we can skip going through the cache // but only if there is no existing piece entry. Otherwise there may be a // partial hit on one-or-more dirty buffers so we must use the cache // to avoid reading bogus data from storage if (m_disk_cache.find_piece(j) == NULL) return 1; } cached_piece_entry* pe = m_disk_cache.allocate_piece(j, cached_piece_entry::read_lru1); if (pe == NULL) { j->ret = -1; j->error.ec = error::no_memory; j->error.operation = storage_error::read; return 0; } if (pe->outstanding_read) { TORRENT_PIECE_ASSERT(j->piece == pe->piece, pe); pe->read_jobs.push_back(j); return 2; } #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(piece_log_t::set_outstanding_jobs)); #endif pe->outstanding_read = 1; return 1; } void disk_io_thread::async_write(piece_manager* storage, peer_request const& r , disk_buffer_holder& buffer , boost::function const& handler , int flags) { INVARIANT_CHECK; #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif TORRENT_ASSERT(r.length <= m_disk_cache.block_size()); TORRENT_ASSERT(r.length <= 16 * 1024); disk_io_job* j = allocate_job(disk_io_job::write); j->storage = storage->shared_from_this(); j->piece = r.piece; j->d.io.offset = r.start; j->d.io.buffer_size = r.length; j->buffer.disk_block = buffer.get(); j->callback = handler; j->flags = flags; #if TORRENT_USE_ASSERTS mutex::scoped_lock l3_(m_cache_mutex); cached_piece_entry* pe = m_disk_cache.find_piece(j); if (pe) { // we should never add a new dirty block to a piece // whose hash we have calculated. The piece needs // to be cleared first, (async_clear_piece). TORRENT_ASSERT(pe->hashing_done == 0); TORRENT_ASSERT(pe->blocks[r.start / 0x4000].refcount == 0 || pe->blocks[r.start / 0x4000].buf == NULL); } l3_.unlock(); #endif #if TORRENT_USE_ASSERTS && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS mutex::scoped_lock l2_(m_cache_mutex); std::pair range = m_disk_cache.all_pieces(); for (block_cache::iterator i = range.first; i != range.second; ++i) { cached_piece_entry const& p = *i; int bs = m_disk_cache.block_size(); int piece_size = p.storage->files()->piece_size(p.piece); int blocks_in_piece = (piece_size + bs - 1) / bs; for (int k = 0; k < blocks_in_piece; ++k) TORRENT_PIECE_ASSERT(p.blocks[k].buf != j->buffer.disk_block, &p); } l2_.unlock(); #endif #if !defined TORRENT_DISABLE_POOL_ALLOCATOR && TORRENT_USE_ASSERTS mutex::scoped_lock l_(m_cache_mutex); TORRENT_ASSERT(m_disk_cache.is_disk_buffer(j->buffer.disk_block)); l_.unlock(); #endif TORRENT_ASSERT((r.start % m_disk_cache.block_size()) == 0); if (storage->is_blocked(j)) { // this means the job was queued up inside storage m_stats_counters.inc_stats_counter(counters::blocked_disk_jobs); DLOG("blocked job: %s (torrent: %d total: %d)\n" , job_action_name[j->action], j->storage ? j->storage->num_blocked() : 0 , int(m_stats_counters[counters::blocked_disk_jobs])); // make the holder give up ownership of the buffer // since the job was successfully queued up buffer.release(); return; } mutex::scoped_lock l(m_cache_mutex); // if we succeed in adding the block to the cache, the job will // be added along with it. we may not free j if so cached_piece_entry* dpe = m_disk_cache.add_dirty_block(j); // if the buffer was successfully added to the cache // our holder should no longer own it if (dpe) buffer.release(); if (dpe && dpe->outstanding_flush == 0) { dpe->outstanding_flush = 1; l.unlock(); // the block and write job were successfully inserted // into the cache. Now, see if we should trigger a flush j = allocate_job(disk_io_job::flush_hashed); j->storage = storage->shared_from_this(); j->piece = r.piece; j->flags = flags; add_job(j); } // if we added the block (regardless of whether we also // issued a flush job or not), we're done. if (dpe) return; l.unlock(); add_job(j); buffer.release(); } void disk_io_thread::async_hash(piece_manager* storage, int piece, int flags , boost::function const& handler, void* requester) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif disk_io_job* j = allocate_job(disk_io_job::hash); j->storage = storage->shared_from_this(); j->piece = piece; j->callback = handler; j->flags = flags; j->requester = requester; int piece_size = storage->files()->piece_size(piece); // first check to see if the hashing is already done mutex::scoped_lock l(m_cache_mutex); cached_piece_entry* pe = m_disk_cache.find_piece(j); if (pe && !pe->hashing && pe->hash && pe->hash->offset == piece_size) { sha1_hash result = pe->hash->h.final(); memcpy(j->d.piece_hash, &result[0], 20); delete pe->hash; pe->hash = NULL; if (pe->cache_state != cached_piece_entry::volatile_read_lru) pe->hashing_done = 1; #if TORRENT_USE_ASSERTS ++pe->hash_passes; #endif l.unlock(); if (handler) handler(j); free_job(j); return; } l.unlock(); add_job(j); } void disk_io_thread::async_move_storage(piece_manager* storage, std::string const& p, int flags , boost::function const& handler) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif disk_io_job* j = allocate_job(disk_io_job::move_storage); j->storage = storage->shared_from_this(); j->buffer.string = strdup(p.c_str()); j->callback = handler; j->flags = flags; add_fence_job(storage, j); } void disk_io_thread::async_release_files(piece_manager* storage , boost::function const& handler) { disk_io_job* j = allocate_job(disk_io_job::release_files); j->storage = storage->shared_from_this(); j->callback = handler; #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS { mutex::scoped_lock l(m_cache_mutex); boost::unordered_set const& pieces = storage->cached_pieces(); for (boost::unordered_set::const_iterator i = pieces.begin() , end(pieces.end()); i != end; ++i) { TORRENT_ASSERT((*i)->storage.get() == storage); } } #endif add_fence_job(storage, j); } void disk_io_thread::async_delete_files(piece_manager* storage , int const options , boost::function const& handler) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif // remove cache blocks belonging to this torrent jobqueue_t completed_jobs; // remove outstanding jobs belonging to this torrent mutex::scoped_lock l2(m_job_mutex); // TODO: maybe the tailqueue_iterator should contain a pointer-pointer // instead and have an unlink function disk_io_job* qj = m_queued_jobs.get_all(); jobqueue_t to_abort; // if we encounter any read jobs in the queue, we need to clear the // "outstanding_read" flag on its piece, as we abort the job std::vector > pieces; while (qj) { disk_io_job* next = qj->next; #if TORRENT_USE_ASSERTS qj->next = NULL; #endif if (qj->action == disk_io_job::read) { pieces.push_back(std::make_pair(qj->storage.get(), int(qj->piece))); } if (qj->storage.get() == storage) to_abort.push_back(qj); else m_queued_jobs.push_back(qj); qj = next; } l2.unlock(); mutex::scoped_lock l(m_cache_mutex); for (std::vector >::iterator i = pieces.begin() , end(pieces.end()); i != end; ++i) { cached_piece_entry* pe = m_disk_cache.find_piece(i->first, i->second); if (pe == NULL) continue; TORRENT_ASSERT(pe->outstanding_read == 1); pe->outstanding_read = 0; } flush_cache(storage, flush_delete_cache, completed_jobs, l); l.unlock(); disk_io_job* j = allocate_job(disk_io_job::delete_files); j->storage = storage->shared_from_this(); j->callback = handler; j->buffer.delete_options = options; add_fence_job(storage, j); fail_jobs_impl(storage_error(boost::asio::error::operation_aborted) , to_abort, completed_jobs); if (completed_jobs.size()) add_completed_jobs(completed_jobs); } void disk_io_thread::async_check_fastresume(piece_manager* storage , bdecode_node const* resume_data , std::vector& links , boost::function const& handler) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif std::vector* links_vector = new std::vector(); links_vector->swap(links); disk_io_job* j = allocate_job(disk_io_job::check_fastresume); j->storage = storage->shared_from_this(); j->buffer.check_resume_data = resume_data; j->d.links = links_vector; j->callback = handler; add_fence_job(storage, j); } void disk_io_thread::async_save_resume_data(piece_manager* storage , boost::function const& handler) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif disk_io_job* j = allocate_job(disk_io_job::save_resume_data); j->storage = storage->shared_from_this(); j->buffer.resume_data = NULL; j->callback = handler; add_fence_job(storage, j); } void disk_io_thread::async_rename_file(piece_manager* storage, int index, std::string const& name , boost::function const& handler) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif disk_io_job* j = allocate_job(disk_io_job::rename_file); j->storage = storage->shared_from_this(); j->piece = index; j->buffer.string = strdup(name.c_str()); j->callback = handler; add_fence_job(storage, j); } void disk_io_thread::async_stop_torrent(piece_manager* storage , boost::function const& handler) { // remove outstanding hash jobs belonging to this torrent mutex::scoped_lock l2(m_job_mutex); disk_io_job* qj = m_queued_hash_jobs.get_all(); jobqueue_t to_abort; while (qj) { disk_io_job* next = qj->next; #if TORRENT_USE_ASSERTS qj->next = NULL; #endif if (qj->storage.get() == storage) to_abort.push_back(qj); else m_queued_hash_jobs.push_back(qj); qj = next; } l2.unlock(); disk_io_job* j = allocate_job(disk_io_job::stop_torrent); j->storage = storage->shared_from_this(); j->callback = handler; add_fence_job(storage, j); jobqueue_t completed_jobs; fail_jobs_impl(storage_error(boost::asio::error::operation_aborted) , to_abort, completed_jobs); if (completed_jobs.size()) add_completed_jobs(completed_jobs); } #ifndef TORRENT_NO_DEPRECATE void disk_io_thread::async_cache_piece(piece_manager* storage, int piece , boost::function const& handler) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif disk_io_job* j = allocate_job(disk_io_job::cache_piece); j->storage = storage->shared_from_this(); j->piece = piece; j->callback = handler; add_job(j); } void disk_io_thread::async_finalize_file(piece_manager* storage, int file , boost::function const& handler) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif disk_io_job* j = allocate_job(disk_io_job::finalize_file); j->storage = storage->shared_from_this(); j->piece = file; j->callback = handler; add_job(j); } #endif // TORRENT_NO_DEPRECATE void disk_io_thread::async_flush_piece(piece_manager* storage, int piece , boost::function const& handler) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif disk_io_job* j = allocate_job(disk_io_job::flush_piece); j->storage = storage->shared_from_this(); j->piece = piece; j->callback = handler; if (m_abort) { j->error.ec = boost::asio::error::operation_aborted; if (handler) handler(j); free_job(j); return; } add_job(j); } // TODO 3: take `prios` by value and move it into the disk job instead of // copying it into a heap allocation void disk_io_thread::async_set_file_priority(piece_manager* storage , std::vector const& prios , boost::function const& handler) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif std::vector* p = new std::vector(prios); disk_io_job* j = allocate_job(disk_io_job::file_priority); j->storage = storage->shared_from_this(); j->buffer.priorities = p; j->callback = handler; add_fence_job(storage, j); } void disk_io_thread::async_load_torrent(add_torrent_params* params , boost::function const& handler) { disk_io_job* j = allocate_job(disk_io_job::load_torrent); j->requester = reinterpret_cast(params); j->callback = handler; add_job(j); } void disk_io_thread::async_tick_torrent(piece_manager* storage , boost::function const& handler) { disk_io_job* j = allocate_job(disk_io_job::tick_storage); j->storage = storage->shared_from_this(); j->callback = handler; add_job(j); } void disk_io_thread::clear_read_cache(piece_manager* storage) { mutex::scoped_lock l(m_cache_mutex); jobqueue_t jobs; boost::unordered_set const& cache = storage->cached_pieces(); // note that i is incremented in the body! for (boost::unordered_set::const_iterator i = cache.begin() , end(cache.end()); i != end; ) { jobqueue_t temp; if (m_disk_cache.evict_piece(*(i++), temp, block_cache::disallow_ghost)) jobs.append(temp); } fail_jobs(storage_error(boost::asio::error::operation_aborted), jobs); } void disk_io_thread::async_clear_piece(piece_manager* storage, int index , boost::function const& handler) { #ifdef TORRENT_DEBUG // the caller must increment the torrent refcount before // issuing an async disk request storage->assert_torrent_refcount(); #endif disk_io_job* j = allocate_job(disk_io_job::clear_piece); j->storage = storage->shared_from_this(); j->piece = index; j->callback = handler; // regular jobs are not guaranteed to be executed in-order // since clear piece must guarantee that all write jobs that // have been issued finish before the clear piece job completes // TODO: this is potentially very expensive. One way to solve // it would be to have a fence for just this one piece. add_fence_job(storage, j); } void disk_io_thread::clear_piece(piece_manager* storage, int index) { mutex::scoped_lock l(m_cache_mutex); cached_piece_entry* pe = m_disk_cache.find_piece(storage, index); if (pe == 0) return; TORRENT_PIECE_ASSERT(pe->hashing == false, pe); pe->hashing_done = 0; delete pe->hash; pe->hash = NULL; // evict_piece returns true if the piece was in fact // evicted. A piece may fail to be evicted if there // are still outstanding operations on it, which should // never be the case when this function is used // in fact, no jobs should really be hung on this piece // at this point jobqueue_t jobs; bool ok = m_disk_cache.evict_piece(pe, jobs, block_cache::allow_ghost); TORRENT_PIECE_ASSERT(ok, pe); TORRENT_UNUSED(ok); fail_jobs(storage_error(boost::asio::error::operation_aborted), jobs); } void disk_io_thread::kick_hasher(cached_piece_entry* pe, mutex::scoped_lock& l) { if (!pe->hash) return; if (pe->hashing) return; int piece_size = pe->storage.get()->files()->piece_size(pe->piece); partial_hash* ph = pe->hash; // are we already done? if (ph->offset >= piece_size) return; int block_size = m_disk_cache.block_size(); int cursor = ph->offset / block_size; int end = cursor; TORRENT_PIECE_ASSERT(ph->offset % block_size == 0, pe); for (int i = cursor; i < pe->blocks_in_piece; ++i) { cached_block_entry& bl = pe->blocks[i]; if (bl.buf == 0) break; // if we fail to lock the block, it' no longer in the cache if (m_disk_cache.inc_block_refcount(pe, i, block_cache::ref_hashing) == false) break; ++end; } // no blocks to hash? if (end == cursor) return; pe->hashing = 1; DLOG("kick_hasher: %d - %d (piece: %d offset: %d)\n" , cursor, end, int(pe->piece), ph->offset); // save a local copy of offset to avoid concurrent access int offset = ph->offset; #if TORRENT_USE_ASSERTS int old_offset = offset; #endif l.unlock(); time_point const start_time = clock_type::now(); for (int i = cursor; i < end; ++i) { cached_block_entry& bl = pe->blocks[i]; int size = (std::min)(block_size, piece_size - offset); ph->h.update(bl.buf, size); offset += size; } boost::uint64_t const hash_time = total_microseconds(clock_type::now() - start_time); l.lock(); TORRENT_ASSERT(old_offset == ph->offset); ph->offset = offset; TORRENT_PIECE_ASSERT(pe->hashing, pe); TORRENT_PIECE_ASSERT(pe->hash, pe); m_stats_counters.inc_stats_counter(counters::num_blocks_hashed, end - cursor); m_stats_counters.inc_stats_counter(counters::disk_hash_time, hash_time); m_stats_counters.inc_stats_counter(counters::disk_job_time, hash_time); pe->hashing = 0; // decrement the block refcounters for (int i = cursor; i < end; ++i) m_disk_cache.dec_block_refcount(pe, i, block_cache::ref_hashing); // did we complete the hash? if (pe->hash->offset != piece_size) return; // if there are any hash-jobs hanging off of this piece // we should post them now disk_io_job* j = pe->jobs.get_all(); jobqueue_t hash_jobs; while (j) { TORRENT_PIECE_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage, pe); disk_io_job* next = j->next; j->next = NULL; TORRENT_PIECE_ASSERT(j->piece == pe->piece, pe); if (j->action == disk_io_job::hash) hash_jobs.push_back(j); else pe->jobs.push_back(j); j = next; } if (hash_jobs.size()) { sha1_hash result = pe->hash->h.final(); for (tailqueue_iterator i = hash_jobs.iterate(); i.get(); i.next()) { disk_io_job* hj = const_cast(i.get()); memcpy(hj->d.piece_hash, result.data(), 20); hj->ret = 0; } delete pe->hash; pe->hash = NULL; if (pe->cache_state != cached_piece_entry::volatile_read_lru) pe->hashing_done = 1; #if TORRENT_USE_ASSERTS ++pe->hash_passes; #endif add_completed_jobs(hash_jobs); } } int disk_io_thread::do_uncached_hash(disk_io_job* j) { // we're not using a cache. This is the simple path // just read straight from the file TORRENT_ASSERT(m_magic == 0x1337); int const piece_size = j->storage->files()->piece_size(j->piece); int const block_size = m_disk_cache.block_size(); int const blocks_in_piece = (piece_size + block_size - 1) / block_size; int const file_flags = file_flags_for_job(j , m_settings.get_bool(settings_pack::coalesce_reads)); file::iovec_t iov; iov.iov_base = m_disk_cache.allocate_buffer("hashing"); hasher h; int ret = 0; int offset = 0; for (int i = 0; i < blocks_in_piece; ++i) { DLOG("do_hash: (uncached) reading (piece: %d block: %d)\n" , int(j->piece), i); time_point const start_time = clock_type::now(); iov.iov_len = (std::min)(block_size, piece_size - offset); ret = j->storage->get_storage_impl()->readv(&iov, 1, j->piece , offset, file_flags, j->error); if (ret < 0) break; if (!j->error.ec) { boost::uint32_t const read_time = total_microseconds(clock_type::now() - start_time); m_stats_counters.inc_stats_counter(counters::num_blocks_read); m_stats_counters.inc_stats_counter(counters::num_read_ops); m_stats_counters.inc_stats_counter(counters::disk_read_time, read_time); m_stats_counters.inc_stats_counter(counters::disk_job_time, read_time); } offset += block_size; h.update(static_cast(iov.iov_base), iov.iov_len); } m_disk_cache.free_buffer(static_cast(iov.iov_base)); sha1_hash piece_hash = h.final(); memcpy(j->d.piece_hash, &piece_hash[0], 20); return ret >= 0 ? 0 : -1; } int disk_io_thread::do_hash(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) { INVARIANT_CHECK; int const piece_size = j->storage->files()->piece_size(j->piece); int const file_flags = file_flags_for_job(j , m_settings.get_bool(settings_pack::coalesce_reads)); mutex::scoped_lock l(m_cache_mutex); cached_piece_entry* pe = m_disk_cache.find_piece(j); if (pe) { TORRENT_ASSERT(pe->in_use); #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(j->action)); #endif m_disk_cache.cache_hit(pe, j->requester, j->flags & disk_io_job::volatile_read); TORRENT_PIECE_ASSERT(pe->cache_state <= cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); ++pe->piece_refcount; kick_hasher(pe, l); --pe->piece_refcount; TORRENT_PIECE_ASSERT(pe->cache_state <= cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); // are we already done hashing? if (pe->hash && !pe->hashing && pe->hash->offset == piece_size) { DLOG("do_hash: (%d) (already done)\n", int(pe->piece)); sha1_hash piece_hash = pe->hash->h.final(); memcpy(j->d.piece_hash, &piece_hash[0], 20); delete pe->hash; pe->hash = NULL; if (pe->cache_state != cached_piece_entry::volatile_read_lru) pe->hashing_done = 1; #if TORRENT_USE_ASSERTS ++pe->hash_passes; #endif m_disk_cache.update_cache_state(pe); m_disk_cache.maybe_free_piece(pe); return 0; } } else if (m_settings.get_bool(settings_pack::use_read_cache) == false) { return do_uncached_hash(j); } if (pe == NULL) { int cache_state = (j->flags & disk_io_job::volatile_read) ? cached_piece_entry::volatile_read_lru : cached_piece_entry::read_lru1; pe = m_disk_cache.allocate_piece(j, cache_state); } if (pe == NULL) { j->error.ec = error::no_memory; j->error.operation = storage_error::alloc_cache_piece; return -1; } if (pe->hashing) { TORRENT_PIECE_ASSERT(pe->hash, pe); // another thread is hashing this piece right now // try again in a little bit DLOG("do_hash: retry\n"); // TODO: we should probably just hang the job on the piece and make sure the hasher gets kicked return retry_job; } pe->hashing = 1; TORRENT_PIECE_ASSERT(pe->cache_state <= cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); ++pe->piece_refcount; if (pe->hash == NULL) { pe->hashing_done = 0; pe->hash = new partial_hash; } partial_hash* ph = pe->hash; int const block_size = m_disk_cache.block_size(); int const blocks_in_piece = (piece_size + block_size - 1) / block_size; // we don't care about anything to the left of ph->offset // since those blocks have already been hashed. // we just care about [firs_block, first_block + blocks_left] int const first_block = ph->offset / block_size; int const blocks_left = blocks_in_piece - first_block; // ph->offset // | first_block // | | // v v // +---+---+---+---+---+---+ // | | | | | | | // +---+---+---+---+---+---+ // // \-----------/ // blocks_left // // \-----------------------/ // blocks_in_piece // keep track of which blocks we have locked by incrementing // their refcounts. This is used to decrement only these blocks // later. int* locked_blocks = TORRENT_ALLOCA(int, blocks_in_piece); memset(locked_blocks, 0, blocks_in_piece * sizeof(int)); int num_locked_blocks = 0; // increment the refcounts of all // blocks up front, and then hash them without holding the lock TORRENT_PIECE_ASSERT(ph->offset % block_size == 0, pe); for (int i = 0; i < blocks_left; ++i) { // is the block not in the cache? if (pe->blocks[first_block + i].buf == NULL) continue; // if we fail to lock the block, it' no longer in the cache if (m_disk_cache.inc_block_refcount(pe, first_block + i, block_cache::ref_hashing) == false) continue; locked_blocks[num_locked_blocks++] = i; } // to keep the cache footprint low, try to evict a volatile piece m_disk_cache.try_evict_one_volatile(); // save a local copy of offset to avoid concurrent access int offset = ph->offset; #if TORRENT_USE_ASSERTS int old_offset = offset; #endif l.unlock(); bool slow_path = true; int ret = 0; if (num_locked_blocks == 0) { // this is the fast path where we don't have any blocks in the cache. // We'll need to read all (remaining blocks) from disk file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, blocks_left); ret = m_disk_cache.allocate_iovec(iov, blocks_left); if (ret >= 0) { // if this is the last piece, adjust the size of the // last buffer to match up iov[blocks_left-1].iov_len = int(piece_size) - (blocks_in_piece - 1) * block_size; TORRENT_ASSERT(iov[blocks_left-1].iov_len > 0); TORRENT_ASSERT(iov[blocks_left-1].iov_len <= block_size); time_point const start_time = clock_type::now(); ret = j->storage->get_storage_impl()->readv(iov, blocks_left , j->piece, offset, file_flags, j->error); if (ret == piece_size - offset) { boost::uint32_t const read_time = total_microseconds(clock_type::now() - start_time); m_stats_counters.inc_stats_counter(counters::num_read_back, blocks_left); m_stats_counters.inc_stats_counter(counters::num_blocks_read, blocks_left); m_stats_counters.inc_stats_counter(counters::num_read_ops); m_stats_counters.inc_stats_counter(counters::disk_read_time, read_time); m_stats_counters.inc_stats_counter(counters::disk_job_time, read_time); for (int i = 0; i < blocks_left; ++i) { offset += iov[i].iov_len; ph->h.update(static_cast(iov[i].iov_base), iov[i].iov_len); } TORRENT_ASSERT(offset == piece_size); slow_path = false; l.lock(); m_disk_cache.insert_blocks(pe, first_block, iov, blocks_left, j); l.unlock(); } else { m_disk_cache.free_iovec(iov, blocks_left); } } } if (slow_path) { ret = 0; int next_locked_block = 0; for (int i = 0; i < blocks_left; ++i) { file::iovec_t iov; iov.iov_len = (std::min)(block_size, piece_size - offset); if (next_locked_block < num_locked_blocks && locked_blocks[next_locked_block] == i) { ++next_locked_block; TORRENT_PIECE_ASSERT(pe->blocks[first_block + i].buf, pe); TORRENT_PIECE_ASSERT(offset == (first_block + i) * block_size, pe); offset += iov.iov_len; ph->h.update(pe->blocks[first_block + i].buf, iov.iov_len); } else { iov.iov_base = m_disk_cache.allocate_buffer("hashing"); if (iov.iov_base == NULL) { l.lock(); // TODO: introduce a holder class that automatically increments // and decrements the piece_refcount // decrement the refcounts of the blocks we just hashed for (int k = 0; k < num_locked_blocks; ++k) m_disk_cache.dec_block_refcount(pe, first_block + locked_blocks[k], block_cache::ref_hashing); --pe->piece_refcount; pe->hashing = false; delete pe->hash; pe->hash = NULL; m_disk_cache.maybe_free_piece(pe); j->error.ec = errors::no_memory; j->error.operation = storage_error::alloc_cache_piece; return -1; } DLOG("do_hash: reading (piece: %d block: %d)\n", int(pe->piece), first_block + i); time_point const start_time = clock_type::now(); TORRENT_PIECE_ASSERT(offset == (first_block + i) * block_size, pe); ret = j->storage->get_storage_impl()->readv(&iov, 1, j->piece , offset, file_flags, j->error); if (ret < 0) { TORRENT_ASSERT(j->error.ec && j->error.operation != 0); m_disk_cache.free_buffer(static_cast(iov.iov_base)); l.lock(); break; } // treat a short read as an error. The hash will be invalid, the // block cannot be cached and the main thread should skip the rest // of this file if (ret != iov.iov_len) { ret = -1; j->error.ec.assign(boost::asio::error::eof , boost::asio::error::get_misc_category()); j->error.operation = storage_error::read; m_disk_cache.free_buffer(static_cast(iov.iov_base)); l.lock(); break; } if (!j->error.ec) { boost::uint32_t const read_time = total_microseconds(clock_type::now() - start_time); m_stats_counters.inc_stats_counter(counters::num_read_back); m_stats_counters.inc_stats_counter(counters::num_blocks_read); m_stats_counters.inc_stats_counter(counters::num_read_ops); m_stats_counters.inc_stats_counter(counters::disk_read_time, read_time); m_stats_counters.inc_stats_counter(counters::disk_job_time, read_time); } TORRENT_PIECE_ASSERT(offset == (first_block + i) * block_size, pe); offset += iov.iov_len; ph->h.update(static_cast(iov.iov_base), iov.iov_len); l.lock(); m_disk_cache.insert_blocks(pe, (first_block + i), &iov, 1, j); l.unlock(); } } } l.lock(); TORRENT_ASSERT(old_offset == ph->offset); ph->offset = offset; // decrement the refcounts of the blocks we just hashed for (int i = 0; i < num_locked_blocks; ++i) m_disk_cache.dec_block_refcount(pe, first_block + locked_blocks[i], block_cache::ref_hashing); --pe->piece_refcount; pe->hashing = 0; if (ret >= 0) { sha1_hash piece_hash = ph->h.final(); memcpy(j->d.piece_hash, &piece_hash[0], 20); delete pe->hash; pe->hash = NULL; if (pe->cache_state != cached_piece_entry::volatile_read_lru) pe->hashing_done = 1; #if TORRENT_USE_ASSERTS ++pe->hash_passes; #endif m_disk_cache.update_cache_state(pe); } m_disk_cache.maybe_free_piece(pe); TORRENT_ASSERT(ret >= 0 || (j->error.ec && j->error.operation != 0)); return ret < 0 ? ret : 0; } int disk_io_thread::do_move_storage(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) { // if this assert fails, something's wrong with the fence logic TORRENT_ASSERT(j->storage->num_outstanding_jobs() == 1); // if files have to be closed, that's the storage's responsibility return j->storage->get_storage_impl()->move_storage(j->buffer.string , j->flags, j->error); } int disk_io_thread::do_release_files(disk_io_job* j, jobqueue_t& completed_jobs) { INVARIANT_CHECK; // if this assert fails, something's wrong with the fence logic TORRENT_ASSERT(j->storage->num_outstanding_jobs() == 1); mutex::scoped_lock l(m_cache_mutex); flush_cache(j->storage.get(), flush_write_cache, completed_jobs, l); l.unlock(); j->storage->get_storage_impl()->release_files(j->error); return j->error ? -1 : 0; } int disk_io_thread::do_delete_files(disk_io_job* j, jobqueue_t& completed_jobs) { TORRENT_ASSERT(j->buffer.delete_options != 0); INVARIANT_CHECK; // if this assert fails, something's wrong with the fence logic TORRENT_ASSERT(j->storage->num_outstanding_jobs() == 1); mutex::scoped_lock l(m_cache_mutex); flush_cache(j->storage.get() , flush_read_cache | flush_delete_cache | flush_expect_clear , completed_jobs, l); l.unlock(); j->storage->get_storage_impl()->delete_files(j->buffer.delete_options, j->error); return j->error ? -1 : 0; } int disk_io_thread::do_check_fastresume(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) { // if this assert fails, something's wrong with the fence logic TORRENT_ASSERT(j->storage->num_outstanding_jobs() == 1); bdecode_node const* rd = j->buffer.check_resume_data; bdecode_node tmp; if (rd == NULL) rd = &tmp; boost::scoped_ptr > links(j->d.links); return j->storage->check_fastresume(*rd, links.get(), j->error); } int disk_io_thread::do_save_resume_data(disk_io_job* j, jobqueue_t& completed_jobs) { // if this assert fails, something's wrong with the fence logic TORRENT_ASSERT(j->storage->num_outstanding_jobs() == 1); mutex::scoped_lock l(m_cache_mutex); flush_cache(j->storage.get(), flush_write_cache, completed_jobs, l); l.unlock(); entry* resume_data = new entry(entry::dictionary_t); j->storage->get_storage_impl()->write_resume_data(*resume_data, j->error); TORRENT_ASSERT(j->buffer.resume_data == 0); j->buffer.resume_data = resume_data; return j->error ? -1 : 0; } int disk_io_thread::do_rename_file(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) { // if this assert fails, something's wrong with the fence logic TORRENT_ASSERT(j->storage->num_outstanding_jobs() == 1); // if files need to be closed, that's the storage's responsibility j->storage->get_storage_impl()->rename_file(j->piece, j->buffer.string , j->error); return j->error ? -1 : 0; } int disk_io_thread::do_stop_torrent(disk_io_job* j, jobqueue_t& completed_jobs) { // if this assert fails, something's wrong with the fence logic TORRENT_ASSERT(j->storage->num_outstanding_jobs() == 1); // issue write commands for all dirty blocks // and clear all read jobs mutex::scoped_lock l(m_cache_mutex); flush_cache(j->storage.get(), flush_read_cache | flush_write_cache , completed_jobs, l); l.unlock(); m_disk_cache.release_memory(); j->storage->get_storage_impl()->release_files(j->error); return j->error ? -1 : 0; } #ifndef TORRENT_NO_DEPRECATE int disk_io_thread::do_cache_piece(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) { INVARIANT_CHECK; TORRENT_ASSERT(j->buffer.disk_block == 0); if (m_settings.get_int(settings_pack::cache_size) == 0 || m_settings.get_bool(settings_pack::use_read_cache) == false) return 0; int const file_flags = file_flags_for_job(j , m_settings.get_bool(settings_pack::coalesce_reads)); mutex::scoped_lock l(m_cache_mutex); cached_piece_entry* pe = m_disk_cache.find_piece(j); if (pe == NULL) { int cache_state = (j->flags & disk_io_job::volatile_read) ? cached_piece_entry::volatile_read_lru : cached_piece_entry::read_lru1; pe = m_disk_cache.allocate_piece(j, cache_state); } if (pe == NULL) { j->error.ec = error::no_memory; j->error.operation = storage_error::alloc_cache_piece; return -1; } #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(j->action)); #endif ++pe->piece_refcount; int block_size = m_disk_cache.block_size(); int piece_size = j->storage->files()->piece_size(j->piece); int blocks_in_piece = (piece_size + block_size - 1) / block_size; file::iovec_t iov; int ret = 0; int offset = 0; // TODO: it would be nice to not have to lock the mutex every // turn through this loop for (int i = 0; i < blocks_in_piece; ++i) { iov.iov_len = (std::min)(block_size, piece_size - offset); // is the block already in the cache? if (pe->blocks[i].buf) continue; l.unlock(); iov.iov_base = m_disk_cache.allocate_buffer("read cache"); if (iov.iov_base == NULL) { //#error introduce a holder class that automatically increments and decrements the piece_refcount --pe->piece_refcount; m_disk_cache.maybe_free_piece(pe); j->error.ec = errors::no_memory; j->error.operation = storage_error::alloc_cache_piece; return -1; } DLOG("do_cache_piece: reading (piece: %d block: %d)\n" , int(pe->piece), i); time_point const start_time = clock_type::now(); ret = j->storage->get_storage_impl()->readv(&iov, 1, j->piece , offset, file_flags, j->error); if (ret < 0) { l.lock(); break; } if (!j->error.ec) { boost::uint32_t const read_time = total_microseconds(clock_type::now() - start_time); m_stats_counters.inc_stats_counter(counters::num_blocks_read); m_stats_counters.inc_stats_counter(counters::num_read_ops); m_stats_counters.inc_stats_counter(counters::disk_read_time, read_time); m_stats_counters.inc_stats_counter(counters::disk_job_time, read_time); } offset += block_size; l.lock(); m_disk_cache.insert_blocks(pe, i, &iov, 1, j); } --pe->piece_refcount; m_disk_cache.maybe_free_piece(pe); return 0; } int disk_io_thread::do_finalize_file(disk_io_job* j, jobqueue_t& /* completed_jobs */) { j->storage->get_storage_impl()->finalize_file(j->piece, j->error); return j->error ? -1 : 0; } #endif // TORRENT_NO_DEPRECATE namespace { void get_cache_info_impl(cached_piece_info& info, cached_piece_entry const* i , int block_size) { info.piece = i->piece; info.storage = i->storage.get(); info.last_use = i->expire; info.need_readback = i->need_readback; info.next_to_hash = i->hash == 0 ? -1 : (i->hash->offset + block_size - 1) / block_size; info.kind = i->cache_state == cached_piece_entry::write_lru ? cached_piece_info::write_cache : i->cache_state == cached_piece_entry::volatile_read_lru ? cached_piece_info::volatile_read_cache : cached_piece_info::read_cache; int blocks_in_piece = i->blocks_in_piece; info.blocks.resize(blocks_in_piece); for (int b = 0; b < blocks_in_piece; ++b) info.blocks[b] = i->blocks[b].buf != 0; } } // anonymous namespace void disk_io_thread::update_stats_counters(counters& c) const { // These are atomic_counts, so it's safe to access them from // a different thread mutex::scoped_lock jl(m_job_mutex); c.set_value(counters::num_read_jobs, read_jobs_in_use()); c.set_value(counters::num_write_jobs, write_jobs_in_use()); c.set_value(counters::num_jobs, jobs_in_use()); c.set_value(counters::queued_disk_jobs, m_queued_jobs.size() + m_queued_hash_jobs.size()); jl.unlock(); mutex::scoped_lock l(m_cache_mutex); // gauges c.set_value(counters::disk_blocks_in_use, m_disk_cache.in_use()); m_disk_cache.update_stats_counters(c); } void disk_io_thread::get_cache_info(cache_status* ret, bool no_pieces , piece_manager const* storage) const { mutex::scoped_lock l(m_cache_mutex); #ifndef TORRENT_NO_DEPRECATE ret->total_used_buffers = m_disk_cache.in_use(); ret->blocks_read_hit = m_stats_counters[counters::num_blocks_cache_hits]; ret->blocks_read = m_stats_counters[counters::num_blocks_read]; ret->blocks_written = m_stats_counters[counters::num_blocks_written]; ret->writes = m_stats_counters[counters::num_write_ops]; ret->reads = m_stats_counters[counters::num_read_ops]; int num_read_jobs = (std::max)(boost::int64_t(1) , m_stats_counters[counters::num_read_ops]); int num_write_jobs = (std::max)(boost::int64_t(1) , m_stats_counters[counters::num_write_ops]); int num_hash_jobs = (std::max)(boost::int64_t(1) , m_stats_counters[counters::num_blocks_hashed]); ret->average_read_time = m_stats_counters[counters::disk_read_time] / num_read_jobs; ret->average_write_time = m_stats_counters[counters::disk_write_time] / num_write_jobs; ret->average_hash_time = m_stats_counters[counters::disk_hash_time] / num_hash_jobs; ret->average_job_time = m_stats_counters[counters::disk_job_time] / (num_read_jobs + num_write_jobs + num_hash_jobs); ret->cumulative_job_time = m_stats_counters[counters::disk_job_time]; ret->cumulative_read_time = m_stats_counters[counters::disk_read_time]; ret->cumulative_write_time = m_stats_counters[counters::disk_write_time]; ret->cumulative_hash_time = m_stats_counters[counters::disk_hash_time]; ret->total_read_back = m_stats_counters[counters::num_read_back]; ret->blocked_jobs = m_stats_counters[counters::blocked_disk_jobs]; ret->num_jobs = jobs_in_use(); ret->num_read_jobs = read_jobs_in_use(); ret->read_queue_size = read_jobs_in_use(); ret->num_write_jobs = write_jobs_in_use(); ret->pending_jobs = m_stats_counters[counters::num_running_disk_jobs]; ret->num_writing_threads = m_stats_counters[counters::num_writing_threads]; for (int i = 0; i < disk_io_job::num_job_ids; ++i) ret->num_fence_jobs[i] = m_stats_counters[counters::num_fenced_read + i]; m_disk_cache.get_stats(ret); #endif ret->pieces.clear(); if (no_pieces == false) { int block_size = m_disk_cache.block_size(); if (storage) { ret->pieces.reserve(storage->num_pieces()); for (boost::unordered_set::iterator i = storage->cached_pieces().begin(), end(storage->cached_pieces().end()); i != end; ++i) { TORRENT_ASSERT((*i)->storage.get() == storage); if ((*i)->cache_state == cached_piece_entry::read_lru2_ghost || (*i)->cache_state == cached_piece_entry::read_lru1_ghost) continue; ret->pieces.push_back(cached_piece_info()); get_cache_info_impl(ret->pieces.back(), *i, block_size); } } else { ret->pieces.reserve(m_disk_cache.num_pieces()); std::pair range = m_disk_cache.all_pieces(); for (block_cache::iterator i = range.first; i != range.second; ++i) { if (i->cache_state == cached_piece_entry::read_lru2_ghost || i->cache_state == cached_piece_entry::read_lru1_ghost) continue; ret->pieces.push_back(cached_piece_info()); get_cache_info_impl(ret->pieces.back(), &*i, block_size); } } } l.unlock(); #ifndef TORRENT_NO_DEPRECATE mutex::scoped_lock jl(m_job_mutex); ret->queued_jobs = m_queued_jobs.size() + m_queued_hash_jobs.size(); jl.unlock(); #endif } int disk_io_thread::do_flush_piece(disk_io_job* j, jobqueue_t& completed_jobs) { mutex::scoped_lock l(m_cache_mutex); cached_piece_entry* pe = m_disk_cache.find_piece(j); if (pe == NULL) return 0; #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(j->action)); #endif try_flush_hashed(pe, m_settings.get_int( settings_pack::write_cache_line_size), completed_jobs, l); return 0; } // this is triggered every time we insert a new dirty block in a piece // by the time this gets executed, the block may already have been flushed // triggered by another mechanism. int disk_io_thread::do_flush_hashed(disk_io_job* j, jobqueue_t& completed_jobs) { mutex::scoped_lock l(m_cache_mutex); cached_piece_entry* pe = m_disk_cache.find_piece(j); if (pe == NULL) return 0; pe->outstanding_flush = 0; if (pe->num_dirty == 0) return 0; // if multiple threads are flushing this piece, this assert may fire // this happens if the cache is running full and pieces are started to // get flushed // TORRENT_PIECE_ASSERT(pe->outstanding_flush == 1, pe); #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(j->action)); #endif TORRENT_PIECE_ASSERT(pe->cache_state <= cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); ++pe->piece_refcount; if (!pe->hashing_done) { if (pe->hash == 0 && !m_settings.get_bool(settings_pack::disable_hash_checks)) { pe->hash = new partial_hash; m_disk_cache.update_cache_state(pe); } // see if we can progress the hash cursor with this new block kick_hasher(pe, l); TORRENT_PIECE_ASSERT(pe->cache_state <= cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); } // flushes the piece to disk in case // it satisfies the condition for a write // piece to be flushed // #error if hash checks are disabled, always just flush try_flush_hashed(pe, m_settings.get_int( settings_pack::write_cache_line_size), completed_jobs, l); TORRENT_ASSERT(l.locked()); --pe->piece_refcount; m_disk_cache.maybe_free_piece(pe); return 0; } int disk_io_thread::do_flush_storage(disk_io_job* j, jobqueue_t& completed_jobs) { mutex::scoped_lock l(m_cache_mutex); flush_cache(j->storage.get(), flush_write_cache, completed_jobs, l); return 0; } int disk_io_thread::do_trim_cache(disk_io_job*, jobqueue_t& /* completed_jobs */) { //#error implement return 0; } int disk_io_thread::do_file_priority(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) { j->storage->get_storage_impl()->set_file_priority(*j->buffer.priorities, j->error); return 0; } int disk_io_thread::do_load_torrent(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) { add_torrent_params* params = reinterpret_cast(j->requester); std::string filename = resolve_file_url(params->url); torrent_info* t = new torrent_info(filename, j->error.ec); if (j->error.ec) { j->buffer.torrent_file = NULL; delete t; } else { // do this to trigger parsing of the info-dict here. It's better // than to have it be done in the network thread. It has enough to // do as it is. std::string cert = t->ssl_cert(); j->buffer.torrent_file = t; } return 0; } // this job won't return until all outstanding jobs on this // piece are completed or cancelled and the buffers for it // have been evicted int disk_io_thread::do_clear_piece(disk_io_job* j, jobqueue_t& completed_jobs) { mutex::scoped_lock l(m_cache_mutex); cached_piece_entry* pe = m_disk_cache.find_piece(j); if (pe == 0) return 0; TORRENT_PIECE_ASSERT(pe->hashing == false, pe); pe->hashing_done = 0; delete pe->hash; pe->hash = NULL; pe->hashing_done = false; #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(j->action)); #endif // evict_piece returns true if the piece was in fact // evicted. A piece may fail to be evicted if there // are still outstanding operations on it, in which case // try again later jobqueue_t jobs; if (m_disk_cache.evict_piece(pe, jobs, block_cache::allow_ghost)) { fail_jobs_impl(storage_error(boost::asio::error::operation_aborted) , jobs, completed_jobs); return 0; } m_disk_cache.mark_for_eviction(pe, block_cache::allow_ghost); if (pe->num_blocks == 0) return 0; // we should always be able to evict the piece, since // this is a fence job TORRENT_PIECE_ASSERT(false, pe); return retry_job; } int disk_io_thread::do_tick(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) { // true means this storage wants more ticks, false // disables ticking (until it's enabled again) return j->storage->get_storage_impl()->tick(); } void disk_io_thread::add_fence_job(piece_manager* storage, disk_io_job* j , bool user_add) { // if this happens, it means we started to shut down // the disk threads too early. We have to post all jobs // before the disk threads are shut down TORRENT_ASSERT(!m_abort); DLOG("add_fence:job: %s (outstanding: %d)\n" , job_action_name[j->action] , j->storage->num_outstanding_jobs()); m_stats_counters.inc_stats_counter(counters::num_fenced_read + j->action); disk_io_job* fj = allocate_job(disk_io_job::flush_storage); fj->storage = j->storage; int ret = storage->raise_fence(j, fj, m_stats_counters); if (ret == disk_job_fence::fence_post_fence) { mutex::scoped_lock l(m_job_mutex); TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage); m_queued_jobs.push_back(j); l.unlock(); // discard the flush job free_job(fj); if (m_num_threads == 0 && user_add) immediate_execute(); return; } // in this case, we can't run the fence job right now, because there // are other jobs outstanding on this storage. We need to trigger a // flush of all those jobs now. Only write jobs linger, those are the // jobs that needs to be kicked TORRENT_ASSERT(j->blocked); if (ret == disk_job_fence::fence_post_flush) { // now, we have to make sure that all outstanding jobs on this // storage actually get flushed, in order for the fence job to // be executed mutex::scoped_lock l(m_job_mutex); TORRENT_ASSERT((fj->flags & disk_io_job::in_progress) || !fj->storage); m_queued_jobs.push_front(fj); } else { TORRENT_ASSERT((fj->flags & disk_io_job::in_progress) == 0); TORRENT_ASSERT(fj->blocked); } if (m_num_threads == 0 && user_add) immediate_execute(); } void disk_io_thread::add_job(disk_io_job* j, bool user_add) { TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(!j->storage || j->storage->files()->is_valid()); TORRENT_ASSERT(j->next == NULL); // if this happens, it means we started to shut down // the disk threads too early. We have to post all jobs // before the disk threads are shut down TORRENT_ASSERT(!m_abort || j->action == disk_io_job::flush_piece || j->action == disk_io_job::trim_cache); // this happens for read jobs that get hung on pieces in the // block cache, and then get issued if (j->flags & disk_io_job::in_progress) { mutex::scoped_lock l(m_job_mutex); TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage); m_queued_jobs.push_back(j); // if we literally have 0 disk threads, we have to execute the jobs // immediately. If add job is called internally by the disk_io_thread, // we need to defer executing it. We only want the top level to loop // over the job queue (as is done below) if (m_num_threads == 0 && user_add) { l.unlock(); immediate_execute(); } return; } DLOG("add_job: %s (outstanding: %d)\n" , job_action_name[j->action] , j->storage ? j->storage->num_outstanding_jobs() : 0); // is the fence up for this storage? // jobs that are instantaneous are not affected by the fence, is_blocked() // will take ownership of the job and queue it up, in case the fence is up // if the fence flag is set, this job just raised the fence on the storage // and should be scheduled if (j->storage && j->storage->is_blocked(j)) { m_stats_counters.inc_stats_counter(counters::blocked_disk_jobs); DLOG("blocked job: %s (torrent: %d total: %d)\n" , job_action_name[j->action], j->storage ? j->storage->num_blocked() : 0 , int(m_stats_counters[counters::blocked_disk_jobs])); return; } mutex::scoped_lock l(m_job_mutex); TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage); // if there are at least 3 threads, there's a hasher thread // and the hash jobs go into a separate queue // see set_num_threads() if (m_num_threads > hasher_thread_mask && j->action == disk_io_job::hash) { m_queued_hash_jobs.push_back(j); } else { m_queued_jobs.push_back(j); // if we literally have 0 disk threads, we have to execute the jobs // immediately. If add job is called internally by the disk_io_thread, // we need to defer executing it. We only want the top level to loop // over the job queue (as is done below) if (m_num_threads == 0 && user_add) { l.unlock(); immediate_execute(); } } } void disk_io_thread::immediate_execute() { while (!m_queued_jobs.empty()) { disk_io_job* j = m_queued_jobs.pop_front(); maybe_flush_write_blocks(); execute_job(j); } } void disk_io_thread::submit_jobs() { mutex::scoped_lock l(m_job_mutex); if (!m_queued_jobs.empty()) m_job_cond.notify_all(); if (!m_queued_hash_jobs.empty()) m_hash_job_cond.notify_all(); } void disk_io_thread::maybe_flush_write_blocks() { time_point now = clock_type::now(); if (now <= m_last_cache_expiry + seconds(5)) return; mutex::scoped_lock l(m_cache_mutex); DLOG("blocked_jobs: %d queued_jobs: %d num_threads %d\n" , int(m_stats_counters[counters::blocked_disk_jobs]) , m_queued_jobs.size(), int(m_num_threads)); m_last_cache_expiry = now; jobqueue_t completed_jobs; flush_expired_write_blocks(completed_jobs, l); l.unlock(); if (completed_jobs.size()) add_completed_jobs(completed_jobs); } void disk_io_thread::execute_job(disk_io_job* j) { jobqueue_t completed_jobs; perform_job(j, completed_jobs); if (completed_jobs.size()) add_completed_jobs(completed_jobs); } void disk_io_thread::thread_fun(int thread_id, thread_type_t type , boost::shared_ptr w) { DLOG("started disk thread %d\n", int(thread_id)); ++m_num_running_threads; m_stats_counters.inc_stats_counter(counters::num_running_threads, 1); mutex::scoped_lock l(m_job_mutex); for (;;) { disk_io_job* j = 0; if (type == generic_thread) { TORRENT_ASSERT(l.locked()); while (m_queued_jobs.empty() && thread_id < m_num_threads) m_job_cond.wait(l); // if the number of wanted threads is decreased, // we may stop this thread // when we're terminating the last thread (id=0), make sure // we finish up all queued jobs first if (thread_id >= m_num_threads && !(thread_id == 0 && m_queued_jobs.size() > 0)) { // time to exit this thread. break; } j = m_queued_jobs.pop_front(); } else if (type == hasher_thread) { TORRENT_ASSERT(l.locked()); while (m_queued_hash_jobs.empty() && thread_id < m_num_threads) m_hash_job_cond.wait(l); if (m_queued_hash_jobs.empty() && thread_id >= m_num_threads) break; j = m_queued_hash_jobs.pop_front(); } l.unlock(); TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage); if (thread_id == 0) { // there's no need for all threads to be doing this maybe_flush_write_blocks(); } execute_job(j); l.lock(); } l.unlock(); // do cleanup in the last running thread // if we're not aborting, that means we just configured the thread pool to // not have any threads (i.e. perform all disk operations in the network // thread). In this case, the cleanup will happen in abort(). m_stats_counters.inc_stats_counter(counters::num_running_threads, -1); if (--m_num_running_threads > 0 || !m_abort) { DLOG("exiting disk thread %d. num_threads: %d aborting: %d\n" , thread_id, int(m_num_threads), int(m_abort)); TORRENT_ASSERT(m_magic == 0x1337); return; } // at this point, there are no queued jobs left. However, main // thread is still running and may still have peer_connections // that haven't fully destructed yet, reclaiming their references // to read blocks in the disk cache. We need to wait until all // references are removed from other threads before we can go // ahead with the cleanup. // This is not supposed to happen because the disk thread is now scheduled // for shut down after all peers have shut down (see // session_impl::abort_stage2()). mutex::scoped_lock l2(m_cache_mutex); TORRENT_ASSERT_VAL(m_disk_cache.pinned_blocks() == 0 , m_disk_cache.pinned_blocks()); while (m_disk_cache.pinned_blocks() > 0) { l2.unlock(); sleep(100); l2.lock(); } l2.unlock(); DLOG("disk thread %d is the last one alive. cleaning up\n", thread_id); abort_jobs(); // release the io_service to allow the run() call to return // we do this once we stop posting new callbacks to it. #if defined TORRENT_ASIO_DEBUGGING complete_async("disk_io_thread::work"); #endif w.reset(); TORRENT_ASSERT(m_magic == 0x1337); } void disk_io_thread::abort_jobs() { TORRENT_ASSERT(m_magic == 0x1337); jobqueue_t jobs; m_disk_cache.clear(jobs); fail_jobs(storage_error(boost::asio::error::operation_aborted), jobs); // close all files. This may take a long // time on certain OSes (i.e. Mac OS) // that's why it's important to do this in // the disk thread in parallel with stopping // trackers. m_file_pool.release(); #if TORRENT_USE_ASSERTS // by now, all pieces should have been evicted std::pair pieces = m_disk_cache.all_pieces(); TORRENT_ASSERT(pieces.first == pieces.second); #endif TORRENT_ASSERT(m_magic == 0x1337); } // this is a callback called by the block_cache when // it's exceeding the disk cache size. void disk_io_thread::trigger_cache_trim() { // we just exceeded the cache size limit. Trigger a trim job disk_io_job* j = allocate_job(disk_io_job::trim_cache); add_job(j, false); submit_jobs(); } char* disk_io_thread::allocate_disk_buffer(bool& exceeded , boost::shared_ptr o , char const* category) { char* ret = m_disk_cache.allocate_buffer(exceeded, o, category); return ret; } void disk_io_thread::add_completed_job(disk_io_job* j) { jobqueue_t tmp; tmp.push_back(j); add_completed_jobs(tmp); } void disk_io_thread::add_completed_jobs(jobqueue_t& jobs) { jobqueue_t new_completed_jobs; do { // when a job completes, it's possible for it to cause // a fence to be lowered, issuing the jobs queued up // behind the fence. It's also possible for some of these // jobs to be cache-hits, completing immediately. Those // jobs are added to the new_completed_jobs queue and // we need to re-issue those add_completed_jobs_impl(jobs, new_completed_jobs); TORRENT_ASSERT(jobs.size() == 0); jobs.swap(new_completed_jobs); } while (jobs.size() > 0); } void disk_io_thread::add_completed_jobs_impl(jobqueue_t& jobs , jobqueue_t& completed_jobs) { jobqueue_t new_jobs; int ret = 0; for (tailqueue_iterator i = jobs.iterate(); i.get(); i.next()) { disk_io_job* j = i.get(); TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage); // DLOG("job_complete %s outstanding: %d\n" // , job_action_name[j->action], j->storage ? j->storage->num_outstanding_jobs() : 0); if (j->storage) { if (j->flags & disk_io_job::fence) { m_stats_counters.inc_stats_counter( counters::num_fenced_read + j->action, -1); } ret += j->storage->job_complete(j, new_jobs); } TORRENT_ASSERT(ret == new_jobs.size()); TORRENT_ASSERT((j->flags & disk_io_job::in_progress) == 0); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(j->job_posted == false); j->job_posted = true; #endif } #if DEBUG_DISK_THREAD if (ret) DLOG("unblocked %d jobs (%d left)\n", ret , int(m_stats_counters[counters::blocked_disk_jobs]) - ret); #endif m_stats_counters.inc_stats_counter(counters::blocked_disk_jobs, -ret); TORRENT_ASSERT(int(m_stats_counters[counters::blocked_disk_jobs]) >= 0); if (new_jobs.size() > 0) { #if TORRENT_USE_ASSERTS for (tailqueue_iterator i = new_jobs.iterate(); i.get(); i.next()) { disk_io_job const* j = static_cast(i.get()); TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage); if (j->action == disk_io_job::write) { mutex::scoped_lock l(m_cache_mutex); cached_piece_entry* pe = m_disk_cache.find_piece(j); if (pe) { TORRENT_ASSERT(pe->blocks[j->d.io.offset / 16 / 1024].buf != j->buffer.disk_block); TORRENT_ASSERT(pe->blocks[j->d.io.offset / 16 / 1024].buf == NULL); TORRENT_ASSERT(!pe->hashing_done); } } } #endif jobqueue_t other_jobs; jobqueue_t flush_jobs; mutex::scoped_lock l_(m_cache_mutex); while (new_jobs.size() > 0) { disk_io_job* j = new_jobs.pop_front(); if (j->action == disk_io_job::read) { int state = prep_read_job_impl(j, false); switch (state) { case 0: completed_jobs.push_back(j); break; case 1: other_jobs.push_back(j); break; } continue; } // write jobs should be put straight into the cache if (j->action != disk_io_job::write) { other_jobs.push_back(j); continue; } cached_piece_entry* pe = m_disk_cache.add_dirty_block(j); if (pe == NULL) { // this isn't correct, since jobs in the jobs // queue aren't ordered other_jobs.push_back(j); continue; } #if TORRENT_USE_ASSERTS pe->piece_log.push_back(piece_log_t(j->action, j->d.io.offset / 0x4000)); #endif if (!pe->hashing_done && pe->hash == 0 && !m_settings.get_bool(settings_pack::disable_hash_checks)) { pe->hash = new partial_hash; m_disk_cache.update_cache_state(pe); } TORRENT_PIECE_ASSERT(pe->cache_state <= cached_piece_entry::read_lru1 || pe->cache_state == cached_piece_entry::read_lru2, pe); if (pe->outstanding_flush == 0) { pe->outstanding_flush = 1; // the block and write job were successfully inserted // into the cache. Now, see if we should trigger a flush disk_io_job* fj = allocate_job(disk_io_job::flush_hashed); fj->storage = j->storage; fj->piece = j->piece; flush_jobs.push_back(fj); } } l_.unlock(); mutex::scoped_lock l(m_job_mutex); m_queued_jobs.append(other_jobs); l.unlock(); while (flush_jobs.size() > 0) { disk_io_job* j = flush_jobs.pop_front(); add_job(j, false); } m_job_cond.notify_all(); } mutex::scoped_lock l(m_completed_jobs_mutex); bool need_post = m_completed_jobs.size() == 0; m_completed_jobs.append(jobs); l.unlock(); if (need_post) { #if DEBUG_DISK_THREAD // we take this lock just to make the logging prettier (non-interleaved) DLOG("posting job handlers (%d)\n", m_completed_jobs.size()); #endif m_ios.post(boost::bind(&disk_io_thread::call_job_handlers, this, m_userdata)); } } // This is run in the network thread void disk_io_thread::call_job_handlers(void* userdata) { mutex::scoped_lock l(m_completed_jobs_mutex); #if DEBUG_DISK_THREAD DLOG("call_job_handlers (%d)\n", m_completed_jobs.size()); #endif int num_jobs = m_completed_jobs.size(); disk_io_job* j = m_completed_jobs.get_all(); l.unlock(); uncork_interface* uncork = static_cast(userdata); std::vector to_delete; to_delete.reserve(num_jobs); while (j) { TORRENT_ASSERT(j->job_posted == true); TORRENT_ASSERT(j->callback_called == false); // DLOG(" callback: %s\n", job_action_name[j->action]); disk_io_job* next = j->next; #if TORRENT_USE_ASSERTS j->callback_called = true; #endif if (j->callback) j->callback(j); to_delete.push_back(j); j = next; } if (!to_delete.empty()) free_jobs(&to_delete[0], to_delete.size()); // uncork all peers who received a disk event. This is // to coalesce all the socket writes caused by the events. if (uncork) uncork->do_delayed_uncork(); } #if TORRENT_USE_INVARIANT_CHECKS void disk_io_thread::check_invariant() const { } #endif } libtorrent-rasterbar-1.1.13/src/disk_job_pool.cpp000066400000000000000000000064771351156116000220710ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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/disk_job_pool.hpp" #include "libtorrent/disk_io_job.hpp" namespace libtorrent { disk_job_pool::disk_job_pool() : m_jobs_in_use(0) , m_read_jobs(0) , m_write_jobs(0) , m_job_pool(sizeof(disk_io_job)) {} disk_job_pool::~disk_job_pool() { // #error this should be fixed! // TORRENT_ASSERT(m_jobs_in_use == 0); } disk_io_job* disk_job_pool::allocate_job(int type) { mutex::scoped_lock l(m_job_mutex); disk_io_job* ptr = static_cast(m_job_pool.malloc()); m_job_pool.set_next_size(100); if (ptr == 0) return 0; ++m_jobs_in_use; if (type == disk_io_job::read) ++m_read_jobs; else if (type == disk_io_job::write) ++m_write_jobs; l.unlock(); TORRENT_ASSERT(ptr); new (ptr) disk_io_job; ptr->action = static_cast(type); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS ptr->in_use = true; #endif return ptr; } void disk_job_pool::free_job(disk_io_job* j) { TORRENT_ASSERT(j); if (j == 0) return; #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS TORRENT_ASSERT(j->in_use); j->in_use = false; #endif int type = j->action; j->~disk_io_job(); mutex::scoped_lock l(m_job_mutex); if (type == disk_io_job::read) --m_read_jobs; else if (type == disk_io_job::write) --m_write_jobs; --m_jobs_in_use; m_job_pool.free(j); } void disk_job_pool::free_jobs(disk_io_job** j, int num) { if (num == 0) return; int read_jobs = 0; int write_jobs = 0; for (int i = 0; i < num; ++i) { int type = j[i]->action; j[i]->~disk_io_job(); if (type == disk_io_job::read) ++read_jobs; else if (type == disk_io_job::write) ++write_jobs; } mutex::scoped_lock l(m_job_mutex); m_read_jobs -= read_jobs; m_write_jobs -= write_jobs; m_jobs_in_use -= num; for (int i = 0; i < num; ++i) m_job_pool.free(j[i]); } } libtorrent-rasterbar-1.1.13/src/entry.cpp000066400000000000000000000403071351156116000204030ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #if TORRENT_USE_IOSTREAM #include #endif #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/entry.hpp" #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/lazy_entry.hpp" #endif #include "libtorrent/bdecode.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/hex.hpp" namespace { template void call_destructor(T* o) { TORRENT_ASSERT(o); o->~T(); } } 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( std::pair(key, entry())).first; return ret->second; } entry& entry::operator[](std::string const& key) { dictionary_type::iterator i = dict().find(key); if (i != dict().end()) return i->second; dictionary_type::iterator ret = dict().insert( std::make_pair(key, entry())).first; return ret->second; } entry* entry::find_key(char const* key) { dictionary_type::iterator i = dict().find(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; } entry* entry::find_key(std::string const& key) { dictionary_type::iterator i = dict().find(key); if (i == dict().end()) return 0; return &i->second; } entry const* entry::find_key(std::string const& key) const { dictionary_type::const_iterator i = dict().find(key); if (i == dict().end()) return 0; return &i->second; } #ifndef BOOST_NO_EXCEPTIONS const entry& entry::operator[](char const* key) const { return (*this)[std::string(key)]; } const entry& entry::operator[](std::string 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; } #endif entry::data_type entry::type() const { #ifdef TORRENT_DEBUG m_type_queried = true; #endif return entry::data_type(m_type); } entry::~entry() { destruct(); } void entry::operator=(const entry& e) { if (&e == this) return; destruct(); copy(e); } entry::integer_type& entry::integer() { if (m_type == undefined_t) construct(int_t); #ifndef BOOST_NO_EXCEPTIONS if (m_type != int_t) throw_type_error(); #elif defined TORRENT_DEBUG TORRENT_ASSERT(m_type_queried); #endif TORRENT_ASSERT(m_type == int_t); return *reinterpret_cast(data); } entry::integer_type const& entry::integer() const { #ifndef BOOST_NO_EXCEPTIONS if (m_type != int_t) throw_type_error(); #elif defined TORRENT_DEBUG TORRENT_ASSERT(m_type_queried); #endif TORRENT_ASSERT(m_type == int_t); return *reinterpret_cast(data); } entry::string_type& entry::string() { if (m_type == undefined_t) construct(string_t); #ifndef BOOST_NO_EXCEPTIONS if (m_type != string_t) throw_type_error(); #elif defined TORRENT_DEBUG TORRENT_ASSERT(m_type_queried); #endif TORRENT_ASSERT(m_type == string_t); return *reinterpret_cast(data); } entry::string_type const& entry::string() const { #ifndef BOOST_NO_EXCEPTIONS if (m_type != string_t) throw_type_error(); #elif defined TORRENT_DEBUG TORRENT_ASSERT(m_type_queried); #endif TORRENT_ASSERT(m_type == string_t); return *reinterpret_cast(data); } entry::list_type& entry::list() { if (m_type == undefined_t) construct(list_t); #ifndef BOOST_NO_EXCEPTIONS if (m_type != list_t) throw_type_error(); #elif defined TORRENT_DEBUG TORRENT_ASSERT(m_type_queried); #endif TORRENT_ASSERT(m_type == list_t); return *reinterpret_cast(data); } entry::list_type const& entry::list() const { #ifndef BOOST_NO_EXCEPTIONS if (m_type != list_t) throw_type_error(); #elif defined TORRENT_DEBUG TORRENT_ASSERT(m_type_queried); #endif TORRENT_ASSERT(m_type == list_t); return *reinterpret_cast(data); } entry::dictionary_type& entry::dict() { if (m_type == undefined_t) construct(dictionary_t); #ifndef BOOST_NO_EXCEPTIONS if (m_type != dictionary_t) throw_type_error(); #elif defined TORRENT_DEBUG TORRENT_ASSERT(m_type_queried); #endif TORRENT_ASSERT(m_type == dictionary_t); return *reinterpret_cast(data); } entry::dictionary_type const& entry::dict() const { #ifndef BOOST_NO_EXCEPTIONS if (m_type != dictionary_t) throw_type_error(); #elif defined TORRENT_DEBUG TORRENT_ASSERT(m_type_queried); #endif TORRENT_ASSERT(m_type == dictionary_t); return *reinterpret_cast(data); } entry::preformatted_type& entry::preformatted() { if (m_type == undefined_t) construct(preformatted_t); #ifndef BOOST_NO_EXCEPTIONS if (m_type != preformatted_t) throw_type_error(); #elif defined TORRENT_DEBUG TORRENT_ASSERT(m_type_queried); #endif TORRENT_ASSERT(m_type == preformatted_t); return *reinterpret_cast(data); } entry::preformatted_type const& entry::preformatted() const { #ifndef BOOST_NO_EXCEPTIONS if (m_type != preformatted_t) throw_type_error(); #elif defined TORRENT_DEBUG TORRENT_ASSERT(m_type_queried); #endif TORRENT_ASSERT(m_type == preformatted_t); return *reinterpret_cast(data); } entry::entry() : m_type(undefined_t) { #ifdef TORRENT_DEBUG m_type_queried = true; #endif } entry::entry(data_type t) : m_type(undefined_t) { construct(t); #ifdef TORRENT_DEBUG m_type_queried = true; #endif } entry::entry(const entry& e) : m_type(undefined_t) { copy(e); #ifdef TORRENT_DEBUG m_type_queried = e.m_type_queried; #endif } entry::entry(dictionary_type const& v) : m_type(undefined_t) { #ifdef TORRENT_DEBUG m_type_queried = true; #endif new(data) dictionary_type(v); m_type = dictionary_t; } entry::entry(string_type const& v) : m_type(undefined_t) { #ifdef TORRENT_DEBUG m_type_queried = true; #endif new(data) string_type(v); m_type = string_t; } entry::entry(list_type const& v) : m_type(undefined_t) { #ifdef TORRENT_DEBUG m_type_queried = true; #endif new(data) list_type(v); m_type = list_t; } entry::entry(integer_type const& v) : m_type(undefined_t) { #ifdef TORRENT_DEBUG m_type_queried = true; #endif new(data) integer_type(v); m_type = int_t; } entry::entry(preformatted_type const& v) : m_type(undefined_t) { #ifdef TORRENT_DEBUG m_type_queried = true; #endif new(data) preformatted_type(v); m_type = preformatted_t; } // convert a bdecode_node into an old skool entry void entry::operator=(bdecode_node const& e) { switch (e.type()) { case bdecode_node::string_t: this->string() = e.string_value(); break; case bdecode_node::int_t: this->integer() = e.int_value(); break; case bdecode_node::dict_t: { dictionary_type& d = this->dict(); for (int i = 0; i < e.dict_size(); ++i) { std::pair elem = e.dict_at(i); d[elem.first] = elem.second; } break; } case bdecode_node::list_t: { list_type& l = this->list(); for (int i = 0; i < e.list_size(); ++i) { l.push_back(entry()); l.back() = e.list_at(i); } break; } case bdecode_node::none_t: destruct(); break; } } #ifndef TORRENT_NO_DEPRECATE // convert a lazy_entry into an old skool entry void entry::operator=(lazy_entry const& e) { switch (e.type()) { case lazy_entry::string_t: this->string() = e.string_value(); break; case lazy_entry::int_t: this->integer() = e.int_value(); break; case lazy_entry::dict_t: { dictionary_type& d = this->dict(); for (int i = 0; i < e.dict_size(); ++i) { std::pair elem = e.dict_at(i); d[elem.first] = *elem.second; } break; } case lazy_entry::list_t: { list_type& l = this->list(); for (int i = 0; i < e.list_size(); ++i) { l.push_back(entry()); l.back() = *e.list_at(i); } break; } case lazy_entry::none_t: destruct(); break; } } #endif void entry::operator=(preformatted_type const& v) { destruct(); new(data) preformatted_type(v); m_type = preformatted_t; #ifdef TORRENT_DEBUG m_type_queried = true; #endif } void entry::operator=(dictionary_type const& v) { destruct(); new(data) dictionary_type(v); m_type = dictionary_t; #ifdef TORRENT_DEBUG m_type_queried = true; #endif } void entry::operator=(string_type const& v) { destruct(); new(data) string_type(v); m_type = string_t; #ifdef TORRENT_DEBUG m_type_queried = true; #endif } void entry::operator=(list_type const& v) { destruct(); new(data) list_type(v); m_type = list_t; #ifdef TORRENT_DEBUG m_type_queried = true; #endif } void entry::operator=(integer_type const& v) { destruct(); new(data) integer_type(v); m_type = int_t; #ifdef TORRENT_DEBUG m_type_queried = true; #endif } 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(); case preformatted_t: return preformatted() == e.preformatted(); default: TORRENT_ASSERT(m_type == undefined_t); return true; } } void entry::construct(data_type t) { switch (t) { 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; case undefined_t: break; case preformatted_t: new (data) preformatted_type; break; } m_type = t; #ifdef TORRENT_DEBUG m_type_queried = true; #endif } void entry::copy(entry const& e) { switch (e.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; case undefined_t: TORRENT_ASSERT(e.type() == undefined_t); break; case preformatted_t: new (data) preformatted_type(e.preformatted()); break; } m_type = e.type(); #ifdef TORRENT_DEBUG m_type_queried = true; #endif } void entry::destruct() { switch(m_type) { case int_t: call_destructor(reinterpret_cast(data)); break; case string_t: call_destructor(reinterpret_cast(data)); break; case list_t: call_destructor(reinterpret_cast(data)); break; case dictionary_t: call_destructor(reinterpret_cast(data)); break; case preformatted_t: call_destructor(reinterpret_cast(data)); break; default: TORRENT_ASSERT(m_type == undefined_t); break; } m_type = undefined_t; #ifdef TORRENT_DEBUG m_type_queried = false; #endif } void entry::swap(entry& e) { bool clear_this = false; bool clear_that = false; if (m_type == undefined_t && e.m_type == undefined_t) return; if (m_type == undefined_t) { construct(data_type(e.m_type)); clear_that = true; } if (e.m_type == undefined_t) { e.construct(data_type(m_type)); clear_this = true; } if (m_type == e.m_type) { switch (m_type) { case int_t: std::swap(*reinterpret_cast(data) , *reinterpret_cast(e.data)); break; case string_t: std::swap(*reinterpret_cast(data) , *reinterpret_cast(e.data)); break; case list_t: std::swap(*reinterpret_cast(data) , *reinterpret_cast(e.data)); break; case dictionary_t: std::swap(*reinterpret_cast(data) , *reinterpret_cast(e.data)); break; case preformatted_t: std::swap(*reinterpret_cast(data) , *reinterpret_cast(e.data)); break; default: break; } if (clear_this) destruct(); if (clear_that) e.destruct(); } else { // currently, only swapping entries of the same type or where one // of the entries is uninitialized is supported. TORRENT_ASSERT(false); } } std::string entry::to_string() const { std::string ret; to_string_impl(ret, 0, false); return ret; } std::string entry::to_string(bool single_line) const { std::string ret; to_string_impl(ret, 0, single_line); return ret; } namespace { bool is_binary(std::string const& str) { for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) { if (!is_print(static_cast(*i))) return true; } return false; } void add_indent(std::string& out, int const indent) { out.resize(out.size() + indent, ' '); } } void entry::to_string_impl(std::string& out, int const indent , bool const single_line) const { TORRENT_ASSERT(indent >= 0); switch (m_type) { case int_t: out += libtorrent::to_string(integer()).elems; break; case string_t: { out += "'"; if (is_binary(string())) out += to_hex(string()); else out += string(); out += "'"; } break; case list_t: { out += single_line ? "[ " : "[\n"; bool first = true; for (list_type::const_iterator i = list().begin(); i != list().end(); ++i) { if (!first) out += single_line ? ", " : ",\n"; first = false; if (!single_line) add_indent(out, indent+1); i->to_string_impl(out, indent+1, single_line); } out += " ]"; } break; case dictionary_t: { out += single_line ? "{ " : "{\n"; bool first = true; for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i) { if (!first) out += single_line ? ", " : ",\n"; first = false; if (!single_line) add_indent(out, indent+1); out += "'"; if (is_binary(i->first)) out += to_hex(i->first); else out += i->first; out += "': "; i->second.to_string_impl(out, indent+2, single_line); } out += " }"; } break; case preformatted_t: out += ""; break; case undefined_t: default: out += ""; } } } libtorrent-rasterbar-1.1.13/src/enum_net.cpp000066400000000000000000000741361351156116000210630ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/config.hpp" #include "libtorrent/enum_net.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include // for wcstombscstombs #if TORRENT_USE_IFCONF #include #include #include #include #include #endif #if TORRENT_USE_SYSCTL #include #ifdef __APPLE__ #include "TargetConditionals.h" #endif #if defined TARGET_IPHONE_SIMULATOR || defined TARGET_OS_IPHONE // net/route.h is not included in the iphone sdk. #include "libtorrent/aux_/route.h" #else #include #endif #include #endif #if TORRENT_USE_GETIPFORWARDTABLE || TORRENT_USE_GETADAPTERSADDRESSES #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #endif #if TORRENT_USE_NETLINK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #if TORRENT_USE_IFADDRS #include #endif #if TORRENT_USE_IFADDRS || TORRENT_USE_IFCONF || TORRENT_USE_NETLINK || TORRENT_USE_SYSCTL // capture this here where warnings are disabled (the macro generates warnings) const unsigned long siocgifmtu = SIOCGIFMTU; #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #if defined(TORRENT_OS2) && !defined(IF_NAMESIZE) #define IF_NAMESIZE IFNAMSIZ #endif namespace libtorrent { namespace { #if !defined TORRENT_BUILD_SIMULATOR address inaddr_to_address(in_addr const* ina, int len = 4) { typedef boost::asio::ip::address_v4::bytes_type bytes_t; bytes_t b; std::memset(&b[0], 0, b.size()); if (len > 0) std::memcpy(&b[0], ina, (std::min)(len, int(b.size()))); return address_v4(b); } #if TORRENT_USE_IPV6 address inaddr6_to_address(in6_addr const* ina6, int len = 16) { typedef boost::asio::ip::address_v6::bytes_type bytes_t; bytes_t b; std::memset(&b[0], 0, b.size()); if (len > 0) std::memcpy(&b[0], ina6, (std::min)(len, int(b.size()))); return address_v6(b); } #endif int sockaddr_len(sockaddr const* sin) { #if TORRENT_HAS_SALEN return sin->sa_len; #else return sin->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); #endif } address sockaddr_to_address(sockaddr const* sin, int assume_family = -1) { if (sin->sa_family == AF_INET || assume_family == AF_INET) return inaddr_to_address(&reinterpret_cast(sin)->sin_addr , sockaddr_len(sin) - offsetof(sockaddr, sa_data)); #if TORRENT_USE_IPV6 else if (sin->sa_family == AF_INET6 || assume_family == AF_INET6) return inaddr6_to_address(&reinterpret_cast(sin)->sin6_addr , sockaddr_len(sin) - offsetof(sockaddr, sa_data)); #endif return address(); } #if TORRENT_USE_NETLINK int read_nl_sock(int sock, char *buf, int bufsize, int seq, int pid) { nlmsghdr* nl_hdr; int msg_len = 0; do { int read_len = recv(sock, buf, bufsize - msg_len, 0); if (read_len < 0) return -1; nl_hdr = reinterpret_cast(buf); if ((NLMSG_OK(nl_hdr, read_len) == 0) || (nl_hdr->nlmsg_type == NLMSG_ERROR)) return -1; if (nl_hdr->nlmsg_type == NLMSG_DONE) break; buf += read_len; msg_len += read_len; if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0) break; } while((nl_hdr->nlmsg_seq != seq) || (nl_hdr->nlmsg_pid != pid)); return msg_len; } bool parse_route(int s, nlmsghdr* nl_hdr, ip_route* rt_info) { rtmsg* rt_msg = reinterpret_cast(NLMSG_DATA(nl_hdr)); if((rt_msg->rtm_family != AF_INET && rt_msg->rtm_family != AF_INET6) || (rt_msg->rtm_table != RT_TABLE_MAIN && rt_msg->rtm_table != RT_TABLE_LOCAL)) return false; int if_index = 0; int rt_len = RTM_PAYLOAD(nl_hdr); #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-align" #endif for (rtattr* rt_attr = reinterpret_cast(RTM_RTA(rt_msg)); RTA_OK(rt_attr,rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len)) { switch(rt_attr->rta_type) { case RTA_OIF: if_index = *reinterpret_cast(RTA_DATA(rt_attr)); break; case RTA_GATEWAY: #if TORRENT_USE_IPV6 if (rt_msg->rtm_family == AF_INET6) { rt_info->gateway = inaddr6_to_address(reinterpret_cast(RTA_DATA(rt_attr))); } else #endif { rt_info->gateway = inaddr_to_address(reinterpret_cast(RTA_DATA(rt_attr))); } break; case RTA_DST: #if TORRENT_USE_IPV6 if (rt_msg->rtm_family == AF_INET6) { rt_info->destination = inaddr6_to_address(reinterpret_cast(RTA_DATA(rt_attr))); } else #endif { rt_info->destination = inaddr_to_address(reinterpret_cast(RTA_DATA(rt_attr))); } break; } } #ifdef __clang__ #pragma clang diagnostic pop #endif if_indextoname(if_index, rt_info->name); ifreq req; memset(&req, 0, sizeof(req)); if_indextoname(if_index, req.ifr_name); ioctl(s, siocgifmtu, &req); rt_info->mtu = req.ifr_mtu; // obviously this doesn't work correctly. How do you get the netmask for a route? // if (ioctl(s, SIOCGIFNETMASK, &req) == 0) { // rt_info->netmask = sockaddr_to_address(&req.ifr_addr, req.ifr_addr.sa_family); // } return true; } #endif // TORRENT_USE_NETLINK #endif // !BUILD_SIMULATOR #if TORRENT_USE_SYSCTL && !defined TORRENT_BUILD_SIMULATOR #ifdef TORRENT_OS2 int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen); #endif bool parse_route(int s, rt_msghdr* rtm, ip_route* rt_info) { sockaddr* rti_info[RTAX_MAX]; sockaddr* sa = reinterpret_cast(rtm + 1); for (int i = 0; i < RTAX_MAX; ++i) { if ((rtm->rtm_addrs & (1 << i)) == 0) { rti_info[i] = 0; continue; } rti_info[i] = sa; #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) sa = reinterpret_cast(reinterpret_cast(sa) + ROUNDUP(sa->sa_len)); #undef ROUNDUP } sa = rti_info[RTAX_GATEWAY]; if (sa == 0 || rti_info[RTAX_DST] == 0 || rti_info[RTAX_NETMASK] == 0 || (sa->sa_family != AF_INET #if TORRENT_USE_IPV6 && sa->sa_family != AF_INET6 #endif )) return false; rt_info->gateway = sockaddr_to_address(rti_info[RTAX_GATEWAY]); rt_info->destination = sockaddr_to_address(rti_info[RTAX_DST]); rt_info->netmask = sockaddr_to_address(rti_info[RTAX_NETMASK] , rt_info->destination.is_v4() ? AF_INET : AF_INET6); if_indextoname(rtm->rtm_index, rt_info->name); // TODO: get the MTU (and other interesting metrics) from the rt_msghdr instead ifreq req; memset(&req, 0, sizeof(req)); if_indextoname(rtm->rtm_index, req.ifr_name); // ignore errors here. This is best-effort ioctl(s, siocgifmtu, &req); rt_info->mtu = req.ifr_mtu; return true; } #endif #if TORRENT_USE_IFADDRS && !defined TORRENT_BUILD_SIMULATOR bool iface_from_ifaddrs(ifaddrs *ifa, ip_interface &rv) { int family = ifa->ifa_addr->sa_family; if (family != AF_INET #if TORRENT_USE_IPV6 && family != AF_INET6 #endif ) { return false; } strncpy(rv.name, ifa->ifa_name, sizeof(rv.name)); rv.name[sizeof(rv.name)-1] = 0; // determine address rv.interface_address = sockaddr_to_address(ifa->ifa_addr); // determine netmask if (ifa->ifa_netmask != NULL) { rv.netmask = sockaddr_to_address(ifa->ifa_netmask); } return true; } #endif }} // namespace libtorrent { // return (a1 & mask) == (a2 & mask) bool match_addr_mask(address const& a1, address const& a2, address const& mask) { // all 3 addresses needs to belong to the same family if (a1.is_v4() != a2.is_v4()) return false; if (a1.is_v4() != mask.is_v4()) return false; #if TORRENT_USE_IPV6 if (a1.is_v6()) { address_v6::bytes_type b1; address_v6::bytes_type b2; address_v6::bytes_type m; b1 = a1.to_v6().to_bytes(); b2 = a2.to_v6().to_bytes(); m = mask.to_v6().to_bytes(); for (int i = 0; i < int(b1.size()); ++i) { b1[i] &= m[i]; b2[i] &= m[i]; } return memcmp(&b1[0], &b2[0], b1.size()) == 0; } #endif return (a1.to_v4().to_ulong() & mask.to_v4().to_ulong()) == (a2.to_v4().to_ulong() & mask.to_v4().to_ulong()); } bool in_local_network(io_service& ios, address const& addr, error_code& ec) { std::vector net = enum_net_interfaces(ios, ec); if (ec) return false; return in_local_network(net, addr); } bool in_local_network(std::vector const& net, address const& addr) { for (std::vector::const_iterator i = net.begin() , end(net.end()); i != end; ++i) { if (match_addr_mask(addr, i->interface_address, i->netmask)) return true; } return false; } #if TORRENT_USE_GETIPFORWARDTABLE address build_netmask(int bits, int family) { if (family == AF_INET) { typedef boost::asio::ip::address_v4::bytes_type bytes_t; bytes_t b; std::memset(&b[0], 0xff, b.size()); for (int i = int(sizeof(bytes_t)) / 8 - 1; i > 0; --i) { if (bits < 8) { b[i] <<= bits; break; } b[i] = 0; bits -= 8; } return address_v4(b); } #if TORRENT_USE_IPV6 else if (family == AF_INET6) { typedef boost::asio::ip::address_v6::bytes_type bytes_t; bytes_t b; std::memset(&b[0], 0xff, b.size()); for (int i = int(sizeof(bytes_t)) / 8 - 1; i > 0; --i) { if (bits < 8) { b[i] <<= bits; break; } b[i] = 0; bits -= 8; } return address_v6(b); } #endif else { return address(); } } #endif std::vector enum_net_interfaces(io_service& ios, error_code& ec) { TORRENT_UNUSED(ios); // this may be unused depending on configuration std::vector ret; ec.clear(); #if defined TORRENT_BUILD_SIMULATOR std::vector
ips = ios.get_ips(); for (int i = 0; i < int(ips.size()); ++i) { ip_interface wan; wan.interface_address = ips[i]; wan.netmask = address_v4::from_string("255.255.255.255"); strcpy(wan.name, "eth0"); wan.mtu = ios.sim().config().path_mtu(ips[i], ips[i]); ret.push_back(wan); } #elif TORRENT_USE_IFADDRS int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { ec = error_code(errno, system_category()); return ret; } ifaddrs *ifaddr; if (getifaddrs(&ifaddr) == -1) { ec = error_code(errno, system_category()); close(s); return ret; } for (ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr == 0) continue; if ((ifa->ifa_flags & IFF_UP) == 0) continue; int family = ifa->ifa_addr->sa_family; if (family == AF_INET #if TORRENT_USE_IPV6 || family == AF_INET6 #endif ) { ip_interface iface; if (iface_from_ifaddrs(ifa, iface)) { ifreq req; std::memset(&req, 0, sizeof(req)); // -1 to leave a 0-terminator std::strncpy(req.ifr_name, iface.name, IF_NAMESIZE - 1); // ignore errors here. This is best-effort ioctl(s, siocgifmtu, &req); iface.mtu = req.ifr_mtu; ret.push_back(iface); } } } close(s); freeifaddrs(ifaddr); // MacOS X, BSD and solaris #elif TORRENT_USE_IFCONF int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { ec = error_code(errno, system_category()); return ret; } ifconf ifc; // make sure the buffer is aligned to hold ifreq structs ifreq buf[40]; ifc.ifc_len = sizeof(buf); ifc.ifc_buf = reinterpret_cast(buf); if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { ec = error_code(errno, system_category()); close(s); return ret; } char *ifr = reinterpret_cast(ifc.ifc_req); int remaining = ifc.ifc_len; while (remaining > 0) { ifreq const& item = *reinterpret_cast(ifr); #ifdef _SIZEOF_ADDR_IFREQ int current_size = _SIZEOF_ADDR_IFREQ(item); #elif defined TORRENT_BSD int current_size = item.ifr_addr.sa_len + IFNAMSIZ; #else int current_size = sizeof(ifreq); #endif if (remaining < current_size) break; if (item.ifr_addr.sa_family == AF_INET #if TORRENT_USE_IPV6 || item.ifr_addr.sa_family == AF_INET6 #endif ) { ip_interface iface; iface.interface_address = sockaddr_to_address(&item.ifr_addr); strcpy(iface.name, item.ifr_name); ifreq req; memset(&req, 0, sizeof(req)); // -1 to leave a 0-terminator strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE - 1); if (ioctl(s, siocgifmtu, &req) < 0) { ec = error_code(errno, system_category()); close(s); return ret; } #ifndef TORRENT_OS2 iface.mtu = req.ifr_mtu; #else iface.mtu = req.ifr_metric; // according to tcp/ip reference #endif memset(&req, 0, sizeof(req)); strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE - 1); if (ioctl(s, SIOCGIFNETMASK, &req) < 0) { #if TORRENT_USE_IPV6 if (iface.interface_address.is_v6()) { // this is expected to fail (at least on MacOS X) iface.netmask = address_v6::any(); } else #endif { ec = error_code(errno, system_category()); close(s); return ret; } } else { iface.netmask = sockaddr_to_address(&req.ifr_addr, item.ifr_addr.sa_family); } ret.push_back(iface); } ifr += current_size; remaining -= current_size; } close(s); #elif TORRENT_USE_GETADAPTERSADDRESSES #if _WIN32_WINNT >= 0x0501 // Load Iphlpapi library HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); if (iphlp) { // Get GetAdaptersAddresses() pointer typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG,ULONG,PVOID,PIP_ADAPTER_ADDRESSES,PULONG); GetAdaptersAddresses_t GetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress( iphlp, "GetAdaptersAddresses"); if (GetAdaptersAddresses == NULL) { FreeLibrary(iphlp); ec = error_code(boost::system::errc::not_supported, generic_category()); return std::vector(); } ULONG buf_size = 10000; std::vector buffer(buf_size); PIP_ADAPTER_ADDRESSES adapter_addresses = reinterpret_cast(&buffer[0]); DWORD r = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &buf_size); if (r == ERROR_BUFFER_OVERFLOW) { buffer.resize(buf_size); adapter_addresses = reinterpret_cast(&buffer[0]); r = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &buf_size); } if (r != NO_ERROR) { FreeLibrary(iphlp); ec = error_code(WSAGetLastError(), system_category()); return std::vector(); } for (PIP_ADAPTER_ADDRESSES adapter = adapter_addresses; adapter != 0; adapter = adapter->Next) { ip_interface r; strncpy(r.name, adapter->AdapterName, sizeof(r.name)); r.name[sizeof(r.name)-1] = 0; r.mtu = adapter->Mtu; for (IP_ADAPTER_UNICAST_ADDRESS* unicast = adapter->FirstUnicastAddress; unicast; unicast = unicast->Next) { if (unicast->Address.lpSockaddr->sa_family != AF_INET #if TORRENT_USE_IPV6 && unicast->Address.lpSockaddr->sa_family != AF_INET6 #endif ) continue; r.interface_address = sockaddr_to_address(unicast->Address.lpSockaddr); ret.push_back(r); } } // Free memory FreeLibrary(iphlp); return ret; } #endif SOCKET s = socket(AF_INET, SOCK_DGRAM, 0); if (s == SOCKET_ERROR) { ec = error_code(WSAGetLastError(), system_category()); return ret; } INTERFACE_INFO buffer[30]; DWORD size; if (WSAIoctl(s, SIO_GET_INTERFACE_LIST, 0, 0, buffer, sizeof(buffer), &size, 0, 0) != 0) { ec = error_code(WSAGetLastError(), system_category()); closesocket(s); return ret; } closesocket(s); int n = size / sizeof(INTERFACE_INFO); ip_interface iface; for (int i = 0; i < n; ++i) { iface.interface_address = sockaddr_to_address(&buffer[i].iiAddress.Address); if (iface.interface_address == address_v4::any()) continue; iface.netmask = sockaddr_to_address(&buffer[i].iiNetmask.Address , iface.interface_address.is_v4() ? AF_INET : AF_INET6); iface.name[0] = 0; iface.mtu = 1500; // how to get the MTU? ret.push_back(iface); } #else #ifdef _MSC_VER #pragma message ( "THIS OS IS NOT RECOGNIZED, enum_net_interfaces WILL PROBABLY NOT WORK" ) #else #warning "THIS OS IS NOT RECOGNIZED, enum_net_interfaces WILL PROBABLY NOT WORK" #endif // make a best guess of the interface we're using and its IP udp::resolver r(ios); udp::resolver::iterator i = r.resolve(udp::resolver::query(boost::asio::ip::host_name(ec), "0"), ec); if (ec) return ret; ip_interface iface; for (;i != udp::resolver_iterator(); ++i) { iface.interface_address = i->endpoint().address(); iface.name[0] = '\0'; iface.mtu = 1500; if (iface.interface_address.is_v4()) iface.netmask = address_v4::netmask(iface.interface_address.to_v4()); ret.push_back(iface); } #endif return ret; } address get_default_gateway(io_service& ios, error_code& ec) { std::vector ret = enum_routes(ios, ec); std::vector::iterator i = std::find_if(ret.begin(), ret.end() , boost::bind(&ip_route::destination, _1) == address()); if (i == ret.end()) return address(); return i->gateway; } std::vector enum_routes(io_service& ios, error_code& ec) { std::vector ret; TORRENT_UNUSED(ios); #ifdef TORRENT_BUILD_SIMULATOR TORRENT_UNUSED(ec); std::vector
ips = ios.get_ips(); for (int i = 0; i < int(ips.size()); ++i) { ip_route r; if (ips[i].is_v4()) { r.destination = address_v4(); r.netmask = address_v4::from_string("255.255.255.0"); address_v4::bytes_type b = ips[i].to_v4().to_bytes(); b[3] = 1; r.gateway = address_v4(b); } else { r.destination = address_v6(); r.netmask = address_v6::from_string("FFFF:FFFF:FFFF:FFFF::0"); address_v6::bytes_type b = ips[i].to_v6().to_bytes(); b[14] = 1; r.gateway = address_v6(b); } strcpy(r.name, "eth0"); r.mtu = ios.sim().config().path_mtu(ips[i], ips[i]); ret.push_back(r); } #elif TORRENT_USE_SYSCTL /* struct rt_msg { rt_msghdr m_rtm; char buf[512]; }; rt_msg m; int len = sizeof(rt_msg); bzero(&m, len); m.m_rtm.rtm_type = RTM_GET; m.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY; m.m_rtm.rtm_version = RTM_VERSION; m.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; m.m_rtm.rtm_seq = 0; m.m_rtm.rtm_msglen = len; int s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); if (s == -1) { ec = error_code(errno, system_category()); return std::vector(); } int n = write(s, &m, len); if (n == -1) { ec = error_code(errno, system_category()); close(s); return std::vector(); } else if (n != len) { ec = boost::asio::error::operation_not_supported; close(s); return std::vector(); } bzero(&m, len); n = read(s, &m, len); if (n == -1) { ec = error_code(errno, system_category()); close(s); return std::vector(); } for (rt_msghdr* ptr = &m.m_rtm; (char*)ptr < ((char*)&m.m_rtm) + n; ptr = (rt_msghdr*)(((char*)ptr) + ptr->rtm_msglen)) { std::cout << " rtm_msglen: " << ptr->rtm_msglen << std::endl; std::cout << " rtm_type: " << ptr->rtm_type << std::endl; if (ptr->rtm_errno) { ec = error_code(ptr->rtm_errno, system_category()); return std::vector(); } if (m.m_rtm.rtm_flags & RTF_UP == 0 || m.m_rtm.rtm_flags & RTF_GATEWAY == 0) { ec = boost::asio::error::operation_not_supported; return address_v4::any(); } if (ptr->rtm_addrs & RTA_DST == 0 || ptr->rtm_addrs & RTA_GATEWAY == 0 || ptr->rtm_addrs & RTA_NETMASK == 0) { ec = boost::asio::error::operation_not_supported; return std::vector(); } if (ptr->rtm_msglen > len - ((char*)ptr - ((char*)&m.m_rtm))) { ec = boost::asio::error::operation_not_supported; return std::vector(); } int min_len = sizeof(rt_msghdr) + 2 * sizeof(sockaddr_in); if (m.m_rtm.rtm_msglen < min_len) { ec = boost::asio::error::operation_not_supported; return std::vector(); } ip_route r; // destination char* p = m.buf; sockaddr_in* sin = (sockaddr_in*)p; r.destination = sockaddr_to_address((sockaddr*)p); // gateway p += sin->sin_len; sin = (sockaddr_in*)p; r.gateway = sockaddr_to_address((sockaddr*)p); // netmask p += sin->sin_len; sin = (sockaddr_in*)p; r.netmask = sockaddr_to_address((sockaddr*)p); ret.push_back(r); } close(s); */ int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_UNSPEC, NET_RT_DUMP, 0}; size_t needed = 0; #ifdef TORRENT_OS2 if (__libsocket_sysctl(mib, 6, 0, &needed, 0, 0) < 0) #else if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) #endif { ec = error_code(errno, system_category()); return std::vector(); } if (needed <= 0) { return std::vector(); } boost::scoped_array buf(new (std::nothrow) char[needed]); if (buf.get() == 0) { ec = boost::asio::error::no_memory; return std::vector(); } #ifdef TORRENT_OS2 if (__libsocket_sysctl(mib, 6, buf.get(), &needed, 0, 0) < 0) #else if (sysctl(mib, 6, buf.get(), &needed, 0, 0) < 0) #endif { ec = error_code(errno, system_category()); return std::vector(); } char* end = buf.get() + needed; int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { ec = error_code(errno, system_category()); return std::vector(); } rt_msghdr* rtm; for (char* next = buf.get(); next < end; next += rtm->rtm_msglen) { rtm = reinterpret_cast(next); if (rtm->rtm_version != RTM_VERSION || (rtm->rtm_type != RTM_ADD && rtm->rtm_type != RTM_GET)) { continue; } ip_route r; if (parse_route(s, rtm, &r)) ret.push_back(r); } close(s); #elif TORRENT_USE_GETIPFORWARDTABLE /* move this to enum_net_interfaces // Load Iphlpapi library HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); if (!iphlp) { ec = boost::asio::error::operation_not_supported; return std::vector(); } // Get GetAdaptersInfo() pointer typedef DWORD (WINAPI *GetAdaptersInfo_t)(PIP_ADAPTER_INFO, PULONG); GetAdaptersInfo_t GetAdaptersInfo = (GetAdaptersInfo_t)GetProcAddress(iphlp, "GetAdaptersInfo"); if (!GetAdaptersInfo) { FreeLibrary(iphlp); ec = boost::asio::error::operation_not_supported; return std::vector(); } PIP_ADAPTER_INFO adapter_info = 0; ULONG out_buf_size = 0; if (GetAdaptersInfo(adapter_info, &out_buf_size) != ERROR_BUFFER_OVERFLOW) { FreeLibrary(iphlp); ec = boost::asio::error::operation_not_supported; return std::vector(); } adapter_info = (IP_ADAPTER_INFO*)malloc(out_buf_size); if (!adapter_info) { FreeLibrary(iphlp); ec = boost::asio::error::no_memory; return std::vector(); } if (GetAdaptersInfo(adapter_info, &out_buf_size) == NO_ERROR) { for (PIP_ADAPTER_INFO adapter = adapter_info; adapter != 0; adapter = adapter->Next) { ip_route r; r.destination = address::from_string(adapter->IpAddressList.IpAddress.String, ec); r.gateway = address::from_string(adapter->GatewayList.IpAddress.String, ec); r.netmask = address::from_string(adapter->IpAddressList.IpMask.String, ec); strncpy(r.name, adapter->AdapterName, sizeof(r.name)); if (ec) { ec = error_code(); continue; } ret.push_back(r); } } // Free memory free(adapter_info); FreeLibrary(iphlp); */ // Load Iphlpapi library HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); if (!iphlp) { ec = boost::asio::error::operation_not_supported; return std::vector(); } typedef DWORD (WINAPI *GetIfEntry_t)(PMIB_IFROW pIfRow); GetIfEntry_t GetIfEntry = (GetIfEntry_t)GetProcAddress(iphlp, "GetIfEntry"); if (!GetIfEntry) { ec = boost::asio::error::operation_not_supported; return std::vector(); } #if _WIN32_WINNT >= 0x0600 typedef DWORD (WINAPI *GetIpForwardTable2_t)( ADDRESS_FAMILY, PMIB_IPFORWARD_TABLE2*); typedef void (WINAPI *FreeMibTable_t)(PVOID Memory); GetIpForwardTable2_t GetIpForwardTable2 = (GetIpForwardTable2_t)GetProcAddress( iphlp, "GetIpForwardTable2"); FreeMibTable_t FreeMibTable = (FreeMibTable_t)GetProcAddress( iphlp, "FreeMibTable"); if (GetIpForwardTable2 && FreeMibTable) { MIB_IPFORWARD_TABLE2* routes = NULL; int res = GetIpForwardTable2(AF_UNSPEC, &routes); if (res == NO_ERROR) { for (int i = 0; i < routes->NumEntries; ++i) { ip_route r; r.gateway = sockaddr_to_address((const sockaddr*)&routes->Table[i].NextHop); r.destination = sockaddr_to_address( (const sockaddr*)&routes->Table[i].DestinationPrefix.Prefix); r.netmask = build_netmask(routes->Table[i].SitePrefixLength , routes->Table[i].DestinationPrefix.Prefix.si_family); MIB_IFROW ifentry; ifentry.dwIndex = routes->Table[i].InterfaceIndex; if (GetIfEntry(&ifentry) == NO_ERROR) { wcstombs(r.name, ifentry.wszName, sizeof(r.name)); r.mtu = ifentry.dwMtu; ret.push_back(r); } } } if (routes) FreeMibTable(routes); FreeLibrary(iphlp); return ret; } #endif // Get GetIpForwardTable() pointer typedef DWORD (WINAPI *GetIpForwardTable_t)(PMIB_IPFORWARDTABLE pIpForwardTable,PULONG pdwSize,BOOL bOrder); GetIpForwardTable_t GetIpForwardTable = (GetIpForwardTable_t)GetProcAddress( iphlp, "GetIpForwardTable"); if (!GetIpForwardTable) { FreeLibrary(iphlp); ec = boost::asio::error::operation_not_supported; return std::vector(); } MIB_IPFORWARDTABLE* routes = NULL; ULONG out_buf_size = 0; if (GetIpForwardTable(routes, &out_buf_size, FALSE) != ERROR_INSUFFICIENT_BUFFER) { FreeLibrary(iphlp); ec = boost::asio::error::operation_not_supported; return std::vector(); } routes = (MIB_IPFORWARDTABLE*)malloc(out_buf_size); if (!routes) { FreeLibrary(iphlp); ec = boost::asio::error::no_memory; return std::vector(); } if (GetIpForwardTable(routes, &out_buf_size, FALSE) == NO_ERROR) { for (int i = 0; i < routes->dwNumEntries; ++i) { ip_route r; r.destination = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardDest); r.netmask = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardMask); r.gateway = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardNextHop); MIB_IFROW ifentry; ifentry.dwIndex = routes->table[i].dwForwardIfIndex; if (GetIfEntry(&ifentry) == NO_ERROR) { wcstombs(r.name, ifentry.wszName, sizeof(r.name)); r.name[sizeof(r.name)-1] = 0; r.mtu = ifentry.dwMtu; ret.push_back(r); } } } // Free memory free(routes); FreeLibrary(iphlp); #elif TORRENT_USE_NETLINK enum { BUFSIZE = 8192 }; int sock = socket(PF_ROUTE, SOCK_DGRAM, NETLINK_ROUTE); if (sock < 0) { ec = error_code(errno, system_category()); return std::vector(); } int seq = 0; char msg[BUFSIZE]; memset(msg, 0, BUFSIZE); nlmsghdr* nl_msg = reinterpret_cast(msg); nl_msg->nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg)); nl_msg->nlmsg_type = RTM_GETROUTE; nl_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; nl_msg->nlmsg_seq = seq++; nl_msg->nlmsg_pid = getpid(); if (send(sock, nl_msg, nl_msg->nlmsg_len, 0) < 0) { ec = error_code(errno, system_category()); close(sock); return std::vector(); } int len = read_nl_sock(sock, msg, BUFSIZE, seq, getpid()); if (len < 0) { ec = error_code(errno, system_category()); close(sock); return std::vector(); } close(sock); sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { ec = error_code(errno, system_category()); return std::vector(); } #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-align" #endif for (; NLMSG_OK(nl_msg, len); nl_msg = NLMSG_NEXT(nl_msg, len)) { ip_route r; if (parse_route(sock, nl_msg, &r)) ret.push_back(r); } #ifdef __clang__ #pragma clang diagnostic pop #endif close(sock); #endif return ret; } // returns the device name whose local address is ``addr``. If // no such device is found, an empty string is returned. std::string device_for_address(address addr, io_service& ios, error_code& ec) { std::vector ifs = enum_net_interfaces(ios, ec); if (ec) return std::string(); for (int i = 0; i < int(ifs.size()); ++i) if (ifs[i].interface_address == addr) return ifs[i].name; return std::string(); } } libtorrent-rasterbar-1.1.13/src/error_code.cpp000066400000000000000000000246611351156116000213720ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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 #include "libtorrent/config.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/string_util.hpp" // for to_string() #include "libtorrent/aux_/escape_string.hpp" // for convert_to_native namespace libtorrent { struct libtorrent_error_category : boost::system::error_category { virtual const char* name() const BOOST_SYSTEM_NOEXCEPT; virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT; virtual boost::system::error_condition default_error_condition(int ev) const BOOST_SYSTEM_NOEXCEPT { return boost::system::error_condition(ev, *this); } }; const char* libtorrent_error_category::name() const BOOST_SYSTEM_NOEXCEPT { return "libtorrent"; } std::string libtorrent_error_category::message(int ev) const BOOST_SYSTEM_NOEXCEPT { static char const* msgs[] = { "no error", "torrent file collides with file from another torrent", "hash check failed", "torrent file is not a dictionary", "missing or invalid 'info' section in torrent file", "'info' entry is not a dictionary", "invalid or missing 'piece length' entry in torrent file", "missing name in torrent file", "invalid 'name' of torrent (possible exploit attempt)", "invalid length of torrent", "failed to parse files from torrent file", "invalid or missing 'pieces' entry in torrent file", "incorrect number of piece hashes in torrent file", "too many pieces in torrent", "invalid metadata received from swarm", "invalid bencoding", "no files in torrent", "invalid escaped string", "session is closing", "torrent already exists in session", "invalid torrent handle used", "invalid type requested from entry", "missing info-hash from URI", "file too short", "unsupported URL protocol", "failed to parse URL", "peer sent 0 length piece", "parse failed", "invalid file format tag", "missing info-hash", "mismatching info-hash", "invalid hostname", "invalid port", "port blocked by port-filter", "expected closing ] for address", "destructing torrent", "timed out", "upload to upload connection", "uninteresting upload-only peer", "invalid info-hash", "torrent paused", "'have'-message with higher index than the number of pieces", "bitfield of invalid size", "too many piece requests while choked", "invalid piece packet", "out of memory", "torrent aborted", "connected to ourselves", "invalid piece size", "timed out: no interest", "timed out: inactivity", "timed out: no handshake", "timed out: no request", "invalid choke message", "invalid unchoke message", "invalid interested message", "invalid not-interested message", "invalid request message", "invalid hash list", "invalid hash piece message", "invalid cancel message", "invalid dht-port message", "invalid suggest piece message", "invalid have-all message", "invalid have-none message", "invalid reject message", "invalid allow-fast message", "invalid extended message", "invalid message", "sync hash not found", "unable to verify encryption constant", "plaintext mode not provided", "rc4 mode not provided", "unsupported encryption mode", "peer selected unsupported encryption mode", "invalid encryption pad size", "invalid encryption handshake", "incoming encrypted connections disabled", "incoming regular connections disabled", "duplicate peer-id", "torrent removed", "packet too large", "", "HTTP error", "missing location header", "invalid redirection", "redirecting", "invalid HTTP range", "missing content-length", "banned by IP filter", "too many connections", "peer banned", "stopping torrent", "too many corrupt pieces", "torrent is not ready to accept peers", "peer is not properly constructed", "session is closing", "optimistic disconnect", "torrent finished", "no router found", "metadata too large", "invalid metadata request", "invalid metadata size", "invalid metadata offset", "invalid metadata message", "pex message too large", "invalid pex message", "invalid lt_tracker message", "pex messages sent too frequent (possible attack)", "torrent has no metadata", "invalid dont-have message", "SSL connection required", "invalid SSL certificate", "not an SSL torrent", "banned by port filter", "", "", "", "", "", // natpmp errors "unsupported protocol version", "not authorized to create port map (enable NAT-PMP on your router)", "network failure", "out of resources", "unsupported opcode", "", "", "", "", "", // fastresume errors "missing or invalid 'file sizes' entry", "no files in resume data", "missing 'slots' and 'pieces' entry", "mismatching number of files", "mismatching file size", "mismatching file timestamp", "not a dictionary", "invalid 'blocks per piece' entry", "missing slots list", "file has more slots than torrent", "invalid entry type in slot list", "invalid piece index in slot list", "pieces needs to be reordered", "fastresume not modified since last save", "", "", "", "", "", "", // HTTP errors "Invalid HTTP header", "missing Location header in HTTP redirect", "failed to decompress HTTP response", "", "", "", "", "", "", "", // i2p errors "no i2p router is set up", "", "", "", "", "", "", "", "", "", // tracker errors "scrape not available on tracker", "invalid tracker response", "invalid peer dictionary entry", "tracker sent a failure message", "missing or invalid 'files' entry", "missing or invalid 'hash' entry", "missing or invalid 'peers' and 'peers6' entry", "udp tracker response packet has invalid size", "invalid transaction id in udp tracker response", "invalid action field in udp tracker response", #ifndef TORRENT_NO_DEPRECATE "", "", "", "", "", "", "", "", "", "", // bdecode errors "expected string in bencoded string", "expected colon in bencoded string", "unexpected end of file in bencoded string", "expected value (list, dict, int or string) in bencoded string", "bencoded nesting depth exceeded", "bencoded item count limit exceeded", "integer overflow", #endif }; if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0]))) return "Unknown error"; return msgs[ev]; } boost::system::error_category& libtorrent_category() { static libtorrent_error_category libtorrent_category; return libtorrent_category; } #ifndef TORRENT_NO_DEPRECATE boost::system::error_category& get_libtorrent_category() { return libtorrent_category(); } boost::system::error_category& get_http_category() { return http_category(); } #endif struct TORRENT_EXPORT http_error_category : boost::system::error_category { virtual const char* name() const BOOST_SYSTEM_NOEXCEPT { return "http"; } virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT { std::string ret; ret += to_string(ev).elems; ret += " "; switch (ev) { case errors::cont: ret += "Continue"; break; case errors::ok: ret += "OK"; break; case errors::created: ret += "Created"; break; case errors::accepted: ret += "Accepted"; break; case errors::no_content: ret += "No Content"; break; case errors::multiple_choices: ret += "Multiple Choices"; break; case errors::moved_permanently: ret += "Moved Permanently"; break; case errors::moved_temporarily: ret += "Moved Temporarily"; break; case errors::not_modified: ret += "Not Modified"; break; case errors::bad_request: ret += "Bad Request"; break; case errors::unauthorized: ret += "Unauthorized"; break; case errors::forbidden: ret += "Forbidden"; break; case errors::not_found: ret += "Not Found"; break; case errors::internal_server_error: ret += "Internal Server Error"; break; case errors::not_implemented: ret += "Not Implemented"; break; case errors::bad_gateway: ret += "Bad Gateway"; break; case errors::service_unavailable: ret += "Service Unavailable"; break; default: ret += "(unknown HTTP error)"; break; } return ret; } virtual boost::system::error_condition default_error_condition( int ev) const BOOST_SYSTEM_NOEXCEPT { return boost::system::error_condition(ev, *this); } }; boost::system::error_category& http_category() { static http_error_category http_category; return http_category; } #ifndef BOOST_NO_EXCEPTIONS const char* libtorrent_exception::what() const TORRENT_EXCEPTION_THROW_SPECIFIER { if (!m_msg) { std::string msg = convert_from_native(m_error.message()); m_msg = allocate_string_copy(msg.c_str()); } return m_msg; } libtorrent_exception::~libtorrent_exception() TORRENT_EXCEPTION_THROW_SPECIFIER { free(m_msg); } #endif namespace errors { // hidden boost::system::error_code make_error_code(error_code_enum e) { return boost::system::error_code(e, libtorrent_category()); } } } libtorrent-rasterbar-1.1.13/src/escape_string.cpp000066400000000000000000000405501351156116000220700ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #ifdef TORRENT_WINDOWS #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #endif #if TORRENT_USE_ICONV #include #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/parse_url.hpp" #include "libtorrent/random.hpp" #include "libtorrent/utf8.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/aux_/escape_string.hpp" #include "libtorrent/string_util.hpp" // for to_string namespace libtorrent { // defined in hex.cpp extern const char hex_chars[]; std::string unescape_string(std::string const& s, error_code& ec) { 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()) { ec = errors::invalid_escaped_string; return ret; } 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 { ec = errors::invalid_escaped_string; return ret; } ++i; if (i == s.end()) { ec = errors::invalid_escaped_string; return ret; } 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 { ec = errors::invalid_escaped_string; return ret; } ret += char(high * 16 + low); } } return ret; } // http://www.ietf.org/rfc/rfc2396.txt // section 2.3 static const char unreserved_chars[] = // when determining if a url needs encoding // % should be ok "%+" // reserved ";?:@=&,$/" // unreserved (special characters) ' excluded, // since some buggy trackers fail with those "-_!.~*()" // unreserved (alphanumerics) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789"; // the offset is used to ignore the first characters in the unreserved_chars table. static std::string escape_string_impl(const char* str, int len, int offset) { TORRENT_ASSERT(str != 0); TORRENT_ASSERT(len >= 0); TORRENT_ASSERT(offset >= 0); TORRENT_ASSERT(offset < int(sizeof(unreserved_chars))-1); std::string ret; for (int i = 0; i < len; ++i) { if (std::strchr(unreserved_chars+offset, *str) && *str != 0) { ret += *str; } else { ret += '%'; ret += hex_chars[boost::uint8_t(*str) >> 4]; ret += hex_chars[boost::uint8_t(*str) & 15]; } ++str; } return ret; } std::string escape_string(const char* str, int len) { return escape_string_impl(str, len, 11); } std::string escape_path(const char* str, int len) { return escape_string_impl(str, len, 10); } bool need_encoding(char const* str, int len) { for (int i = 0; i < len; ++i) { if (std::strchr(unreserved_chars, *str) == 0 || *str == 0) return true; ++str; } return false; } void convert_path_to_posix(std::string& path) { for (std::string::iterator i = path.begin() , end(path.end()); i != end; ++i) if (*i == '\\') *i = '/'; } #ifdef TORRENT_WINDOWS void convert_path_to_windows(std::string& path) { for (std::string::iterator i = path.begin() , end(path.end()); i != end; ++i) if (*i == '/') *i = '\\'; } #endif // TODO: 2 this should probably be moved into string_util.cpp std::string read_until(char const*& str, char delim, char const* end) { TORRENT_ASSERT(str <= end); std::string ret; while (str != end && *str != delim) { ret += *str; ++str; } // skip the delimiter as well while (str != end && *str == delim) ++str; return ret; } std::string maybe_url_encode(std::string const& url) { std::string protocol, host, auth, path; int port; error_code ec; boost::tie(protocol, auth, host, port, path) = parse_url_components(url, ec); if (ec) return url; // first figure out if this url contains unencoded characters if (!need_encoding(path.c_str(), path.size())) return url; char msg[TORRENT_MAX_PATH*4]; snprintf(msg, sizeof(msg), "%s://%s%s%s%s%s%s", protocol.c_str(), auth.c_str() , auth.empty()?"":"@", host.c_str() , port == -1 ? "" : ":" , port == -1 ? "" : to_string(port).elems , escape_path(path.c_str(), path.size()).c_str()); return msg; } std::string resolve_file_url(std::string const& url) { TORRENT_ASSERT(url.substr(0, 7) == "file://"); // first, strip the file:// part. // On windows, we have // to strip the first / as well int num_to_strip = 7; #ifdef TORRENT_WINDOWS if (url[7] == '/' || url[7] == '\\') ++num_to_strip; #endif std::string ret = url.substr(num_to_strip); // we also need to URL-decode it error_code ec; std::string unescaped = unescape_string(ret, ec); if (ec) unescaped = ret; // on windows, we need to convert forward slashes // to backslashes #ifdef TORRENT_WINDOWS convert_path_to_windows(unescaped); #endif return unescaped; } 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(s.end()-i)); // clear input buffer std::fill(inbuf, inbuf+3, 0); // read a chunk of input into inbuf std::copy(i, i + available_input, inbuf); i += available_input; // 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; } std::string base32encode(std::string const& s, int flags) { static const char base32_table_canonical[] = { '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', '2', '3', '4', '5', '6', '7' }; static const char base32_table_lowercase[] = { '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', '2', '3', '4', '5', '6', '7' }; const char *base32_table = 0 != (flags & string::lowercase) ? base32_table_lowercase : base32_table_canonical; int input_output_mapping[] = {0, 2, 4, 5, 7, 8}; unsigned char inbuf[5]; unsigned char outbuf[8]; std::string ret; for (std::string::const_iterator i = s.begin(); i != s.end();) { int available_input = (std::min)(5, int(s.end()-i)); // clear input buffer std::fill(inbuf, inbuf+5, 0); // read a chunk of input into inbuf std::copy(i, i + available_input, inbuf); i += available_input; // encode inbuf to outbuf outbuf[0] = (inbuf[0] & 0xf8) >> 3; outbuf[1] = ((inbuf[0] & 0x07) << 2) | ((inbuf[1] & 0xc0) >> 6); outbuf[2] = ((inbuf[1] & 0x3e) >> 1); outbuf[3] = ((inbuf[1] & 0x01) << 4) | ((inbuf[2] & 0xf0) >> 4); outbuf[4] = ((inbuf[2] & 0x0f) << 1) | ((inbuf[3] & 0x80) >> 7); outbuf[5] = ((inbuf[3] & 0x7c) >> 2); outbuf[6] = ((inbuf[3] & 0x03) << 3) | ((inbuf[4] & 0xe0) >> 5); outbuf[7] = inbuf[4] & 0x1f; // write output int num_out = input_output_mapping[available_input]; for (int j = 0; j < num_out; ++j) { ret += base32_table[outbuf[j]]; } if (0 == (flags & string::no_padding)) { // write pad for (int j = 0; j < 8 - num_out; ++j) { ret += '='; } } } return ret; } std::string base32decode(std::string const& s) { unsigned char inbuf[8]; unsigned char outbuf[5]; std::string ret; for (std::string::const_iterator i = s.begin(); i != s.end();) { int available_input = (std::min)(8, int(s.end()-i)); int pad_start = 0; if (available_input < 8) pad_start = available_input; // clear input buffer std::fill(inbuf, inbuf+8, 0); for (int j = 0; j < available_input; ++j) { char in = std::toupper(*i++); if (in >= 'A' && in <= 'Z') inbuf[j] = in - 'A'; else if (in >= '2' && in <= '7') inbuf[j] = in - '2' + ('Z' - 'A') + 1; else if (in == '=') { inbuf[j] = 0; if (pad_start == 0) pad_start = j; } else if (in == '1') inbuf[j] = 'I' - 'A'; else return std::string(); TORRENT_ASSERT(inbuf[j] == (inbuf[j] & 0x1f)); } // decode inbuf to outbuf outbuf[0] = inbuf[0] << 3; outbuf[0] |= inbuf[1] >> 2; outbuf[1] = (inbuf[1] & 0x3) << 6; outbuf[1] |= inbuf[2] << 1; outbuf[1] |= (inbuf[3] & 0x10) >> 4; outbuf[2] = (inbuf[3] & 0x0f) << 4; outbuf[2] |= (inbuf[4] & 0x1e) >> 1; outbuf[3] = (inbuf[4] & 0x01) << 7; outbuf[3] |= (inbuf[5] & 0x1f) << 2; outbuf[3] |= (inbuf[6] & 0x18) >> 3; outbuf[4] = (inbuf[6] & 0x07) << 5; outbuf[4] |= inbuf[7]; int input_output_mapping[] = {5, 1, 1, 2, 2, 3, 4, 4, 5}; int num_out = input_output_mapping[pad_start]; // write output std::copy(outbuf, outbuf + num_out, std::back_inserter(ret)); } return ret; } std::string url_has_argument( std::string const& url, std::string argument, std::string::size_type* out_pos) { size_t i = url.find('?'); if (i == std::string::npos) return std::string(); ++i; argument += '='; if (url.compare(i, argument.size(), argument) == 0) { size_t pos = i + argument.size(); if (out_pos) *out_pos = pos; return url.substr(pos, url.find('&', pos) - pos); } argument.insert(0, "&"); i = url.find(argument, i); if (i == std::string::npos) return std::string(); size_t pos = i + argument.size(); if (out_pos) *out_pos = pos; return url.substr(pos, url.find('&', pos) - pos); } #if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING std::wstring convert_to_wstring(std::string const& s) { std::wstring ret; int result = libtorrent::utf8_wchar(s, ret); if (result == 0) return ret; ret.clear(); const char* end = &s[0] + s.size(); for (const char* i = &s[0]; i < end;) { wchar_t c = '.'; result = std::mbtowc(&c, i, end - i); if (result > 0) i += result; else ++i; ret += c; } return ret; } std::string convert_from_wstring(std::wstring const& s) { std::string ret; int result = libtorrent::wchar_utf8(s, ret); if (result == 0) return ret; ret.clear(); const wchar_t* end = &s[0] + s.size(); for (const wchar_t* i = &s[0]; i < end;) { char c[10]; TORRENT_ASSERT(sizeof(c) >= MB_CUR_MAX); result = std::wctomb(c, *i); if (result > 0) { i += result; ret.append(c, result); } else { ++i; ret += "."; } } return ret; } #endif #if TORRENT_USE_ICONV namespace { // this is a helper function to deduce the type of the second argument to // the iconv() function. template size_t call_iconv(size_t (&fun)(iconv_t, Input**, size_t*, char**, size_t*) , iconv_t cd, char const** in, size_t* insize, char** out, size_t* outsize) { return fun(cd, const_cast(in), insize, out, outsize); } std::string iconv_convert_impl(std::string const& s, iconv_t h) { std::string ret; size_t insize = s.size(); size_t outsize = insize * 4; ret.resize(outsize); char const* in = s.c_str(); char* out = &ret[0]; // posix has a weird iconv() signature. implementations // differ on the type of the second parameter. We use a helper template // to deduce what we need to cast to. size_t const retval = call_iconv(::iconv, h, &in, &insize, &out, &outsize); if (retval == size_t(-1)) return s; // if this string has an invalid utf-8 sequence in it, don't touch it if (insize != 0) return s; // not sure why this would happen, but it seems to be possible if (outsize > s.size() * 4) return s; // outsize is the number of bytes unused of the out-buffer TORRENT_ASSERT(ret.size() >= outsize); ret.resize(ret.size() - outsize); return ret; } } // anonymous namespace std::string convert_to_native(std::string const& s) { static mutex iconv_mutex; // only one thread can use this handle at a time mutex::scoped_lock l(iconv_mutex); // the empty string represents the local dependent encoding static iconv_t iconv_handle = iconv_open("", "UTF-8"); if (iconv_handle == iconv_t(-1)) return s; return iconv_convert_impl(s, iconv_handle); } std::string convert_from_native(std::string const& s) { static mutex iconv_mutex; // only one thread can use this handle at a time mutex::scoped_lock l(iconv_mutex); // the empty string represents the local dependent encoding static iconv_t iconv_handle = iconv_open("UTF-8", ""); if (iconv_handle == iconv_t(-1)) return s; return iconv_convert_impl(s, iconv_handle); } #elif defined TORRENT_WINDOWS std::string convert_to_native(std::string const& s) { std::wstring ws; libtorrent::utf8_wchar(s, ws); std::string ret; ret.resize(ws.size() * 4 + 1); std::size_t size = WideCharToMultiByte(CP_ACP, 0, ws.c_str(), -1, &ret[0], ret.size(), NULL, NULL); if (size == std::size_t(-1)) return s; if (size != 0 && ret[size - 1] == '\0') --size; ret.resize(size); return ret; } std::string convert_from_native(std::string const& s) { std::wstring ws; ws.resize(s.size() + 1); std::size_t size = MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, &ws[0], ws.size()); if (size == std::size_t(-1)) return s; if (size != 0 && ws[size - 1] == '\0') --size; ws.resize(size); std::string ret; libtorrent::wchar_utf8(ws, ret); return ret; } #elif TORRENT_USE_LOCALE std::string convert_to_native(std::string const& s) { 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 == std::size_t(-1)) return s; ret.resize(size); return ret; } std::string convert_from_native(std::string const& s) { std::wstring ws; ws.resize(s.size()); std::size_t size = mbstowcs(&ws[0], s.c_str(), s.size()); if (size == std::size_t(-1)) return s; std::string ret; libtorrent::wchar_utf8(ws, ret); return ret; } #endif } libtorrent-rasterbar-1.1.13/src/file.cpp000066400000000000000000001602641351156116000201660ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-macros" #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunknown-pragmas" #pragma clang diagnostic ignored "-Wunused-macros" #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif // these defines are just in case the system we're on needs them for 64 bit file // support #define _FILE_OFFSET_BITS 64 #define _LARGE_FILES 1 // on mingw this is necessary to enable 64-bit time_t, specifically used for // the stat struct. Without this, modification times returned by stat may be // incorrect and consistently fail resume data #ifndef __MINGW_USE_VC2005_COMPAT # define __MINGW_USE_VC2005_COMPAT #endif #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #include "libtorrent/config.hpp" #include "libtorrent/alloca.hpp" #include "libtorrent/file.hpp" #include #include #ifdef TORRENT_DEBUG_FILE_LEAKS #include #include "libtorrent/thread.hpp" #endif // for convert_to_wstring and convert_to_native #include "libtorrent/aux_/escape_string.hpp" #include #include "libtorrent/assert.hpp" #include #include #include #ifdef TORRENT_DISK_STATS #include "libtorrent/io.hpp" #endif #include #ifdef TORRENT_WINDOWS // windows part #ifndef PtrToPtr64 #define PtrToPtr64(x) (x) #endif #include "libtorrent/utf8.hpp" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #ifndef TORRENT_MINGW #include // for _getcwd, _mkdir #else #include #endif #include #else // posix part #include #include #include #include #ifdef TORRENT_LINUX // linux specifics #ifdef TORRENT_ANDROID #include #define statvfs statfs #define fstatvfs fstatfs #else #include #endif #include #ifdef TORRENT_ANDROID #include #define lseek lseek64 #define pread pread64 #define pwrite pwrite64 #define ftruncate ftruncate64 #endif #elif defined __APPLE__ && defined __MACH__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 // mac specifics #include #endif // make sure the _FILE_OFFSET_BITS define worked // on this platform. It's supposed to make file // related functions support 64-bit offsets. // this test makes sure lseek() returns a type // at least 64 bits wide BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8); #endif // posix part #if TORRENT_USE_PREADV # if defined TORRENT_WINDOWS namespace { // wrap the windows function in something that looks // like preadv() and pwritev() // windows only lets us wait for 64 handles at a time, so this function makes // sure we wait for all of them, partially in sequence int wait_for_multiple_objects(int num_handles, HANDLE* h) { int batch_size = (std::min)(num_handles, MAXIMUM_WAIT_OBJECTS); while (WaitForMultipleObjects(batch_size, h, TRUE, INFINITE) != WAIT_FAILED) { h += batch_size; num_handles -= batch_size; batch_size = (std::min)(num_handles, MAXIMUM_WAIT_OBJECTS); if (batch_size <= 0) return WAIT_OBJECT_0; } return WAIT_FAILED; } int preadv(HANDLE fd, libtorrent::file::iovec_t const* bufs, int num_bufs, boost::int64_t file_offset) { OVERLAPPED* ol = TORRENT_ALLOCA(OVERLAPPED, num_bufs); memset(ol, 0, sizeof(OVERLAPPED) * num_bufs); HANDLE* h = TORRENT_ALLOCA(HANDLE, num_bufs); for (int i = 0; i < num_bufs; ++i) { ol[i].OffsetHigh = file_offset >> 32; ol[i].Offset = file_offset & 0xffffffff; ol[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); h[i] = ol[i].hEvent; if (h[i] == NULL) { // we failed to create the event, roll-back and return an error for (int j = 0; j < i; ++j) CloseHandle(h[i]); return -1; } file_offset += bufs[i].iov_len; } int ret = 0; int num_waits = num_bufs; for (int i = 0; i < num_bufs; ++i) { DWORD num_read; if (ReadFile(fd, bufs[i].iov_base, bufs[i].iov_len, &num_read, &ol[i]) == FALSE) { DWORD const last_error = GetLastError(); if (last_error == ERROR_HANDLE_EOF) { num_waits = i; break; } else if (last_error != ERROR_IO_PENDING #ifdef ERROR_CANT_WAIT && last_error != ERROR_CANT_WAIT #endif ) { ret = -1; goto done; } } } if (num_waits == 0) { goto done; } if (wait_for_multiple_objects(num_waits, h) == WAIT_FAILED) { ret = -1; goto done; } for (int i = 0; i < num_waits; ++i) { if (WaitForSingleObject(ol[i].hEvent, INFINITE) == WAIT_FAILED) { ret = -1; break; } DWORD num_read; if (GetOverlappedResult(fd, &ol[i], &num_read, FALSE) == FALSE) { DWORD const last_error = GetLastError(); if (last_error != ERROR_HANDLE_EOF) { #ifdef ERROR_CANT_WAIT TORRENT_ASSERT(last_error != ERROR_CANT_WAIT); #endif ret = -1; break; } } ret += num_read; } done: for (int i = 0; i < num_bufs; ++i) CloseHandle(h[i]); return ret; } int pwritev(HANDLE fd, libtorrent::file::iovec_t const* bufs, int num_bufs, boost::int64_t file_offset) { OVERLAPPED* ol = TORRENT_ALLOCA(OVERLAPPED, num_bufs); memset(ol, 0, sizeof(OVERLAPPED) * num_bufs); HANDLE* h = TORRENT_ALLOCA(HANDLE, num_bufs); for (int i = 0; i < num_bufs; ++i) { ol[i].OffsetHigh = file_offset >> 32; ol[i].Offset = file_offset & 0xffffffff; ol[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); h[i] = ol[i].hEvent; if (h[i] == NULL) { // we failed to create the event, roll-back and return an error for (int j = 0; j < i; ++j) CloseHandle(h[i]); return -1; } file_offset += bufs[i].iov_len; } int ret = 0; for (int i = 0; i < num_bufs; ++i) { DWORD num_written; if (WriteFile(fd, bufs[i].iov_base, bufs[i].iov_len, &num_written, &ol[i]) == FALSE && GetLastError() != ERROR_IO_PENDING #ifdef ERROR_CANT_WAIT && GetLastError() != ERROR_CANT_WAIT #endif ) { ret = -1; goto done; } } if (wait_for_multiple_objects(num_bufs, h) == WAIT_FAILED) { ret = -1; goto done; } for (int i = 0; i < num_bufs; ++i) { if (WaitForSingleObject(ol[i].hEvent, INFINITE) == WAIT_FAILED) { ret = -1; break; } DWORD num_written; if (GetOverlappedResult(fd, &ol[i], &num_written, FALSE) == FALSE) { #ifdef ERROR_CANT_WAIT TORRENT_ASSERT(GetLastError() != ERROR_CANT_WAIT); #endif ret = -1; break; } ret += num_written; } done: for (int i = 0; i < num_bufs; ++i) CloseHandle(h[i]); return ret; } } // namespace # else # undef _BSD_SOURCE # define _BSD_SOURCE // deprecated since glibc 2.20 # undef _DEFAULT_SOURCE # define _DEFAULT_SOURCE # include # endif #endif #ifdef TORRENT_DEBUG BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::sparse) == 0); BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::attribute_mask) == 0); BOOST_STATIC_ASSERT((libtorrent::file::sparse & libtorrent::file::attribute_mask) == 0); #endif #if defined TORRENT_WINDOWS && defined UNICODE && !TORRENT_USE_WSTRING #ifdef _MSC_VER #pragma message ( "wide character support not available. Files will be saved using narrow string names" ) #else #warning "wide character support not available. Files will be saved using narrow string names" #endif #endif // TORRENT_WINDOWS namespace libtorrent { int bufs_size(file::iovec_t const* bufs, int num_bufs) { std::size_t size = 0; for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) size += i->iov_len; return int(size); } #ifdef TORRENT_WINDOWS std::string convert_separators(std::string p) { for (int i = 0; i < int(p.size()); ++i) if (p[i] == '/') p[i] = '\\'; return p; } time_t file_time_to_posix(FILETIME f) { const boost::uint64_t posix_time_offset = 11644473600LL; boost::uint64_t ft = (boost::uint64_t(f.dwHighDateTime) << 32) | f.dwLowDateTime; // windows filetime is specified in 100 nanoseconds resolution. // convert to seconds return time_t(ft / 10000000 - posix_time_offset); } #endif void stat_file(std::string const& inf, file_status* s , error_code& ec, int flags) { ec.clear(); #ifdef TORRENT_WINDOWS TORRENT_UNUSED(flags); std::string p = convert_separators(inf); #if TORRENT_USE_UNC_PATHS // UNC paths must be absolute // network paths are already UNC paths if (inf.substr(0,2) == "\\\\") p = inf; else p = "\\\\?\\" + (is_complete(p) ? p : combine_path(current_working_directory(), p)); #endif #if TORRENT_USE_WSTRING #define CreateFile_ CreateFileW std::wstring f = convert_to_wstring(p); #else #define CreateFile_ CreateFileA std::string f = convert_to_native(p); #endif // in order to open a directory, we need the FILE_FLAG_BACKUP_SEMANTICS HANDLE h = CreateFile_(f.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { ec.assign(GetLastError(), system_category()); TORRENT_ASSERT(ec); return; } BY_HANDLE_FILE_INFORMATION data; if (!GetFileInformationByHandle(h, &data)) { ec.assign(GetLastError(), system_category()); TORRENT_ASSERT(ec); CloseHandle(h); return; } s->file_size = (boost::uint64_t(data.nFileSizeHigh) << 32) | data.nFileSizeLow; s->ctime = file_time_to_posix(data.ftCreationTime); s->atime = file_time_to_posix(data.ftLastAccessTime); s->mtime = file_time_to_posix(data.ftLastWriteTime); s->mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? file_status::directory : (data.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) ? file_status::character_special : file_status::regular_file; CloseHandle(h); #else // posix version std::string const& f = convert_to_native(inf); struct stat ret; int retval; if (flags & dont_follow_links) retval = ::lstat(f.c_str(), &ret); else retval = ::stat(f.c_str(), &ret); if (retval < 0) { ec.assign(errno, system_category()); return; } s->file_size = ret.st_size; s->atime = ret.st_atime; s->mtime = ret.st_mtime; s->ctime = ret.st_ctime; s->mode = (S_ISREG(ret.st_mode) ? file_status::regular_file : 0) | (S_ISDIR(ret.st_mode) ? file_status::directory : 0) | (S_ISLNK(ret.st_mode) ? file_status::link : 0) | (S_ISFIFO(ret.st_mode) ? file_status::fifo : 0) | (S_ISCHR(ret.st_mode) ? file_status::character_special : 0) | (S_ISBLK(ret.st_mode) ? file_status::block_special : 0) | (S_ISSOCK(ret.st_mode) ? file_status::socket : 0); #endif // TORRENT_WINDOWS } void rename(std::string const& inf, std::string const& newf, error_code& ec) { ec.clear(); #if TORRENT_USE_WSTRING && defined TORRENT_WINDOWS std::wstring f1 = convert_to_wstring(inf); std::wstring f2 = convert_to_wstring(newf); if (_wrename(f1.c_str(), f2.c_str()) < 0) #else std::string const& f1 = convert_to_native(inf); std::string const& f2 = convert_to_native(newf); if (::rename(f1.c_str(), f2.c_str()) < 0) #endif { ec.assign(errno, generic_category()); return; } } void create_directories(std::string const& f, error_code& ec) { ec.clear(); if (is_directory(f, ec)) return; if (ec != boost::system::errc::no_such_file_or_directory) return; ec.clear(); if (is_root_path(f)) return; if (has_parent_path(f)) { create_directories(parent_path(f), ec); if (ec) return; } create_directory(f, ec); } void create_directory(std::string const& f, error_code& ec) { ec.clear(); #ifdef TORRENT_WINDOWS #if TORRENT_USE_WSTRING #define CreateDirectory_ CreateDirectoryW std::wstring n = convert_to_wstring(f); #else #define CreateDirectory_ CreateDirectoryA std::string const& n = convert_to_native(f); #endif // TORRENT_USE_WSTRING if (CreateDirectory_(n.c_str(), 0) == 0 && GetLastError() != ERROR_ALREADY_EXISTS) ec.assign(GetLastError(), system_category()); #else std::string n = convert_to_native(f); int ret = mkdir(n.c_str(), 0777); if (ret < 0 && errno != EEXIST) ec.assign(errno, system_category()); #endif } void hard_link(std::string const& file, std::string const& link , error_code& ec) { #ifdef TORRENT_WINDOWS #if TORRENT_USE_WSTRING #define CreateHardLink_ CreateHardLinkW std::wstring n_exist = convert_to_wstring(file); std::wstring n_link = convert_to_wstring(link); #else #define CreateHardLink_ CreateHardLinkA std::string n_exist = convert_to_native(file); std::string n_link = convert_to_native(link); #endif BOOL ret = CreateHardLink_(n_link.c_str(), n_exist.c_str(), NULL); if (ret) { ec.clear(); return; } // something failed. Does the filesystem not support hard links? // TODO: 3 find out what error code is reported when the filesystem // does not support hard links. DWORD const error = GetLastError(); if (error != ERROR_NOT_SUPPORTED && error != ERROR_ACCESS_DENIED) { // it's possible CreateHardLink will copy the file internally too, // if the filesystem does not support it. ec.assign(GetLastError(), system_category()); return; } // fall back to making a copy #else std::string n_exist = convert_to_native(file); std::string n_link = convert_to_native(link); // assume posix's link() function exists int ret = ::link(n_exist.c_str(), n_link.c_str()); if (ret == 0) { ec.clear(); return; } // most errors are passed through, except for the ones that indicate that // hard links are not supported and require a copy. // TODO: 2 test this on a FAT volume to see what error we get! if (errno != EMLINK && errno != EXDEV) { // some error happened, report up to the caller ec.assign(errno, system_category()); return; } // fall back to making a copy #endif // if we get here, we should copy the file copy_file(file, link, ec); } bool is_directory(std::string const& f, error_code& ec) { ec.clear(); error_code e; file_status s; stat_file(f, &s, e); if (!e && s.mode & file_status::directory) return true; ec = e; return false; } void recursive_copy(std::string const& old_path, std::string const& new_path, error_code& ec) { TORRENT_ASSERT(!ec); if (is_directory(old_path, ec)) { create_directory(new_path, ec); if (ec) return; for (directory i(old_path, ec); !i.done(); i.next(ec)) { std::string f = i.file(); if (f == ".." || f == ".") continue; recursive_copy(combine_path(old_path, f), combine_path(new_path, f), ec); if (ec) return; } } else if (!ec) { copy_file(old_path, new_path, ec); } } void copy_file(std::string const& inf, std::string const& newf, error_code& ec) { ec.clear(); #ifdef TORRENT_WINDOWS #if TORRENT_USE_WSTRING #define CopyFile_ CopyFileW std::wstring f1 = convert_to_wstring(inf); std::wstring f2 = convert_to_wstring(newf); #else #define CopyFile_ CopyFileA std::string const& f1 = convert_to_native(inf); std::string const& f2 = convert_to_native(newf); #endif if (CopyFile_(f1.c_str(), f2.c_str(), false) == 0) ec.assign(GetLastError(), system_category()); #elif defined __APPLE__ && defined __MACH__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 std::string f1 = convert_to_native(inf); std::string f2 = convert_to_native(newf); // this only works on 10.5 copyfile_state_t state = copyfile_state_alloc(); if (copyfile(f1.c_str(), f2.c_str(), state, COPYFILE_ALL) < 0) ec.assign(errno, system_category()); copyfile_state_free(state); #else std::string f1 = convert_to_native(inf); std::string f2 = convert_to_native(newf); int infd = ::open(f1.c_str(), O_RDONLY); if (infd < 0) { ec.assign(errno, system_category()); return; } // rely on default umask to filter x and w permissions // for group and others int permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; int outfd = ::open(f2.c_str(), O_WRONLY | O_CREAT, permissions); if (outfd < 0) { close(infd); ec.assign(errno, system_category()); return; } char buffer[4096]; for (;;) { int num_read = read(infd, buffer, sizeof(buffer)); if (num_read == 0) break; if (num_read < 0) { ec.assign(errno, system_category()); break; } int num_written = write(outfd, buffer, num_read); if (num_written < num_read) { ec.assign(errno, system_category()); break; } if (num_read < int(sizeof(buffer))) break; } close(infd); close(outfd); #endif // TORRENT_WINDOWS } void move_file(std::string const& inf, std::string const& newf, error_code& ec) { ec.clear(); file_status s; stat_file(inf, &s, ec); if (ec) return; if (has_parent_path(newf)) { create_directories(parent_path(newf), ec); if (ec) return; } rename(inf, newf, ec); } std::string split_path(std::string const& f) { if (f.empty()) return f; std::string ret; char const* start = f.c_str(); char const* p = start; while (*start != 0) { while (*p != '/' && *p != '\0' #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) && *p != '\\' #endif ) ++p; if (p - start > 0) { ret.append(start, p - start); ret.append(1, '\0'); } if (*p != 0) ++p; start = p; } ret.append(1, '\0'); return ret; } char const* next_path_element(char const* p) { p += strlen(p) + 1; if (*p == 0) return 0; return p; } std::string extension(std::string const& f) { for (int i = f.size() - 1; i >= 0; --i) { if (f[i] == '/') break; #ifdef TORRENT_WINDOWS if (f[i] == '\\') break; #endif if (f[i] != '.') continue; return f.substr(i); } return ""; } std::string remove_extension(std::string const& f) { char const* slash = strrchr(f.c_str(), '/'); #ifdef TORRENT_WINDOWS slash = (std::max)((char const*)strrchr(f.c_str(), '\\'), slash); #endif char const* ext = strrchr(f.c_str(), '.'); // if we don't have an extension, just return f if (ext == 0 || ext == &f[0] || (slash != NULL && ext < slash)) return f; return f.substr(0, ext - &f[0]); } void replace_extension(std::string& f, std::string const& ext) { for (int i = f.size() - 1; i >= 0; --i) { if (f[i] == '/') break; #ifdef TORRENT_WINDOWS if (f[i] == '\\') break; #endif if (f[i] != '.') continue; f.resize(i); break; } f += '.'; f += ext; } bool is_root_path(std::string const& f) { if (f.empty()) return false; #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) // match \\ form if (f == "\\\\") return true; int i = 0; // match the xx:\ or xx:/ form while (f[i] && is_alpha(f[i])) ++i; if (i == int(f.size()-2) && f[i] == ':' && (f[i+1] == '\\' || f[i+1] == '/')) return true; // match network paths \\computer_name\ form if (f.size() > 2 && f[0] == '\\' && f[1] == '\\') { // we don't care about the last character, since it's OK for it // to be a slash or a back slash bool found = false; for (int i = 2; i < int(f.size()) - 1; ++i) { if (f[i] != '\\' && f[i] != '/') continue; // there is a directory separator in here, // i.e. this is not the root found = true; break; } if (!found) return true; } #else // as well as parent_path("/") should be "/". if (f == "/") return true; #endif return false; } bool compare_path(std::string const& lhs, std::string const& rhs) { std::string::size_type const lhs_size = !lhs.empty() && (lhs[lhs.size()-1] == '/' #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) || lhs[lhs.size()-1] == '\\' #endif ) ? lhs.size() - 1 : lhs.size(); std::string::size_type const rhs_size = !rhs.empty() && (rhs[rhs.size()-1] == '/' #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) || rhs[rhs.size()-1] == '\\' #endif ) ? rhs.size() - 1 : rhs.size(); return lhs.compare(0, lhs_size, rhs, 0, rhs_size) == 0; } bool has_parent_path(std::string const& f) { if (f.empty()) return false; if (is_root_path(f)) return false; int len = f.size() - 1; // if the last character is / or \ ignore it if (f[len] == '/' || f[len] == '\\') --len; while (len >= 0) { if (f[len] == '/' || f[len] == '\\') break; --len; } return len >= 0; } std::string parent_path(std::string const& f) { if (f.empty()) return f; #ifdef TORRENT_WINDOWS if (f == "\\\\") return ""; #endif if (f == "/") return ""; int len = f.size(); // if the last character is / or \ ignore it if (f[len-1] == '/' || f[len-1] == '\\') --len; while (len > 0) { --len; if (f[len] == '/' || f[len] == '\\') break; } if (f[len] == '/' || f[len] == '\\') ++len; return std::string(f.c_str(), len); } char const* filename_cstr(char const* f) { if (f == 0) return f; char const* sep = strrchr(f, '/'); #ifdef TORRENT_WINDOWS char const* altsep = strrchr(f, '\\'); if (sep == 0 || altsep > sep) sep = altsep; #endif if (sep == 0) return f; return sep+1; } std::string filename(std::string const& f) { if (f.empty()) return ""; char const* first = f.c_str(); char const* sep = strrchr(first, '/'); #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) char const* altsep = strrchr(first, '\\'); if (sep == 0 || altsep > sep) sep = altsep; #endif if (sep == 0) return f; if (sep - first == int(f.size()) - 1) { // if the last character is a / (or \) // ignore it int len = 0; while (sep > first) { --sep; if (*sep == '/' #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) || *sep == '\\' #endif ) return std::string(sep + 1, len); ++len; } return std::string(first, len); } return std::string(sep + 1); } void append_path(std::string& branch, std::string const& leaf) { append_path(branch, leaf.c_str(), leaf.size()); } void append_path(std::string& branch , char const* str, int len) { TORRENT_ASSERT(!is_complete(std::string(str, len))); if (branch.empty() || branch == ".") { branch.assign(str, len); return; } if (len == 0) return; #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) #define TORRENT_SEPARATOR_CHAR '\\' bool need_sep = branch[branch.size()-1] != '\\' && branch[branch.size()-1] != '/'; #else #define TORRENT_SEPARATOR_CHAR '/' bool need_sep = branch[branch.size()-1] != '/'; #endif if (need_sep) branch += TORRENT_SEPARATOR_CHAR; branch.append(str, len); } std::string combine_path(std::string const& lhs, std::string const& rhs) { TORRENT_ASSERT(!is_complete(rhs)); if (lhs.empty() || lhs == ".") return rhs; if (rhs.empty() || rhs == ".") return lhs; #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) #define TORRENT_SEPARATOR "\\" bool need_sep = lhs[lhs.size()-1] != '\\' && lhs[lhs.size()-1] != '/'; #else #define TORRENT_SEPARATOR "/" bool need_sep = lhs[lhs.size()-1] != '/'; #endif std::string ret; int target_size = lhs.size() + rhs.size() + 2; ret.resize(target_size); target_size = snprintf(&ret[0], target_size, "%s%s%s", lhs.c_str() , (need_sep?TORRENT_SEPARATOR:""), rhs.c_str()); ret.resize(target_size); return ret; } std::string current_working_directory() { #if defined TORRENT_WINDOWS && !defined TORRENT_MINGW #if TORRENT_USE_WSTRING wchar_t cwd[TORRENT_MAX_PATH]; _wgetcwd(cwd, sizeof(cwd) / sizeof(wchar_t)); #else char cwd[TORRENT_MAX_PATH]; _getcwd(cwd, sizeof(cwd)); #endif // TORRENT_USE_WSTRING #else char cwd[TORRENT_MAX_PATH]; if (getcwd(cwd, sizeof(cwd)) == 0) return "/"; #endif #if defined TORRENT_WINDOWS && !defined TORRENT_MINGW && TORRENT_USE_WSTRING return convert_from_wstring(cwd); #else return convert_from_native(cwd); #endif } #if TORRENT_USE_UNC_PATHS std::string canonicalize_path(std::string const& f) { std::string ret; ret.resize(f.size()); char* write_cur = &ret[0]; char* last_write_sep = write_cur; char const* read_cur = f.c_str(); char const* last_read_sep = read_cur; // the last_*_sep pointers point to one past // the last path separator encountered and is // initializes to the first character in the path while (*read_cur) { if (*read_cur != '\\') { *write_cur++ = *read_cur++; continue; } int element_len = read_cur - last_read_sep; if (element_len == 1 && memcmp(last_read_sep, ".", 1) == 0) { --write_cur; ++read_cur; last_read_sep = read_cur; continue; } if (element_len == 2 && memcmp(last_read_sep, "..", 2) == 0) { // find the previous path separator if (last_write_sep > &ret[0]) { --last_write_sep; while (last_write_sep > &ret[0] && last_write_sep[-1] != '\\') --last_write_sep; } write_cur = last_write_sep; // find the previous path separator if (last_write_sep > &ret[0]) { --last_write_sep; while (last_write_sep > &ret[0] && last_write_sep[-1] != '\\') --last_write_sep; } ++read_cur; last_read_sep = read_cur; continue; } *write_cur++ = *read_cur++; last_write_sep = write_cur; last_read_sep = read_cur; } // terminate destination string *write_cur = 0; ret.resize(write_cur - &ret[0]); return ret; } #endif boost::int64_t file_size(std::string const& f) { error_code ec; file_status s; stat_file(f, &s, ec); if (ec) return 0; return s.file_size; } bool exists(std::string const& f, error_code& ec) { file_status s; stat_file(f, &s, ec); if (ec) { if (ec == boost::system::errc::no_such_file_or_directory) ec.clear(); return false; } return true; } bool exists(std::string const& f) { error_code ec; return exists(f, ec); } void remove(std::string const& inf, error_code& ec) { ec.clear(); #ifdef TORRENT_WINDOWS // windows does not allow trailing / or \ in // the path when removing files std::string pruned; if (inf[inf.size() - 1] == '/' || inf[inf.size() - 1] == '\\') pruned = inf.substr(0, inf.size() - 1); else pruned = inf; #if TORRENT_USE_WSTRING #define DeleteFile_ DeleteFileW #define RemoveDirectory_ RemoveDirectoryW std::wstring f = convert_to_wstring(pruned); #else #define DeleteFile_ DeleteFileA #define RemoveDirectory_ RemoveDirectoryA std::string f = convert_to_native(pruned); #endif if (DeleteFile_(f.c_str()) == 0) { if (GetLastError() == ERROR_ACCESS_DENIED) { if (RemoveDirectory_(f.c_str()) != 0) return; } ec.assign(GetLastError(), system_category()); return; } #else // TORRENT_WINDOWS std::string const& f = convert_to_native(inf); if (::remove(f.c_str()) < 0) { ec.assign(errno, system_category()); return; } #endif // TORRENT_WINDOWS } void remove_all(std::string const& f, error_code& ec) { ec.clear(); file_status s; stat_file(f, &s, ec); if (ec) return; if (s.mode & file_status::directory) { for (directory i(f, ec); !i.done(); i.next(ec)) { if (ec) return; std::string p = i.file(); if (p == "." || p == "..") continue; remove_all(combine_path(f, p), ec); if (ec) return; } } remove(f, ec); } std::string complete(std::string const& f) { if (is_complete(f)) return f; if (f == ".") return current_working_directory(); return combine_path(current_working_directory(), f); } bool is_complete(std::string const& f) { if (f.empty()) return false; #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) int i = 0; // match the xx:\ or xx:/ form while (f[i] && is_alpha(f[i])) ++i; if (i < int(f.size()-1) && f[i] == ':' && (f[i+1] == '\\' || f[i+1] == '/')) return true; // match the \\ form if (int(f.size()) >= 2 && f[0] == '\\' && f[1] == '\\') return true; return false; #else if (f[0] == '/') return true; return false; #endif } directory::directory(std::string const& path, error_code& ec) : m_done(false) { ec.clear(); #ifdef TORRENT_WINDOWS m_inode = 0; // the path passed to FindFirstFile() must be // a pattern std::string f = convert_separators(path); if (!f.empty() && f[f.size()-1] != '\\') f += "\\*"; else f += "*"; #if TORRENT_USE_WSTRING #define FindFirstFile_ FindFirstFileW std::wstring p = convert_to_wstring(f); #else #define FindFirstFile_ FindFirstFileA std::string p = convert_to_native(f); #endif m_handle = FindFirstFile_(p.c_str(), &m_fd); if (m_handle == INVALID_HANDLE_VALUE) { ec.assign(GetLastError(), system_category()); m_done = true; return; } #else // the path passed to opendir() may not // end with a / std::string p = path; if (!path.empty() && path[path.size()-1] == '/') p.resize(path.size()-1); p = convert_to_native(p); m_handle = opendir(p.c_str()); if (m_handle == 0) { ec.assign(errno, system_category()); m_done = true; return; } // read the first entry next(ec); #endif } directory::~directory() { #ifdef TORRENT_WINDOWS if (m_handle != INVALID_HANDLE_VALUE) FindClose(m_handle); #else if (m_handle) closedir(m_handle); #endif } boost::uint64_t directory::inode() const { return m_inode; } std::string directory::file() const { #ifdef TORRENT_WINDOWS #if TORRENT_USE_WSTRING return convert_from_wstring(m_fd.cFileName); #else return convert_from_native(m_fd.cFileName); #endif #else return convert_from_native(m_name); #endif } void directory::next(error_code& ec) { ec.clear(); #ifdef TORRENT_WINDOWS #if TORRENT_USE_WSTRING #define FindNextFile_ FindNextFileW #else #define FindNextFile_ FindNextFileA #endif if (FindNextFile_(m_handle, &m_fd) == 0) { m_done = true; int err = GetLastError(); if (err != ERROR_NO_MORE_FILES) ec.assign(err, system_category()); } ++m_inode; #else struct dirent* de; errno = 0; if ((de = ::readdir(m_handle))) { m_inode = de->d_ino; m_name = de->d_name; } else { if (errno) ec.assign(errno, system_category()); m_done = true; } #endif } #ifndef INVALID_HANDLE_VALUE #define INVALID_HANDLE_VALUE -1 #endif #ifdef TORRENT_WINDOWS struct overlapped_t { overlapped_t() { memset(&ol, 0, sizeof(ol)); ol.hEvent = CreateEvent(0, true, false, 0); } ~overlapped_t() { if (ol.hEvent != INVALID_HANDLE_VALUE) CloseHandle(ol.hEvent); } int wait(HANDLE file, error_code& ec) { if (ol.hEvent != INVALID_HANDLE_VALUE && WaitForSingleObject(ol.hEvent, INFINITE) == WAIT_FAILED) { ec.assign(GetLastError(), system_category()); return -1; } DWORD ret = -1; if (GetOverlappedResult(file, &ol, &ret, false) == 0) { DWORD last_error = GetLastError(); if (last_error != ERROR_HANDLE_EOF) { #ifdef ERROR_CANT_WAIT TORRENT_ASSERT(last_error != ERROR_CANT_WAIT); #endif ec.assign(last_error, system_category()); return -1; } } return ret; } OVERLAPPED ol; }; #endif // TORRENT_WINDOWS #ifdef TORRENT_WINDOWS void acquire_manage_volume_privs(); #endif file::file() : m_file_handle(INVALID_HANDLE_VALUE) , m_open_mode(0) { #ifdef TORRENT_DISK_STATS m_file_id = 0; #endif } file::file(std::string const& path, int mode, error_code& ec) : m_file_handle(INVALID_HANDLE_VALUE) , m_open_mode(0) { #ifdef TORRENT_DISK_STATS m_file_id = 0; #endif // the return value is not important, since the // error code contains the same information open(path, mode, ec); } file::~file() { close(); } #ifdef TORRENT_DISK_STATS namespace { boost::uint32_t silly_hash(std::string const& str) { boost::uint32_t ret = 1; for (int i = 0; i < str.size(); ++i) { if (str[i] == 0) continue; ret *= int(str[i]); } return ret; } } #endif bool file::open(std::string const& path, int mode, error_code& ec) { close(); #ifdef TORRENT_DEBUG_FILE_LEAKS m_file_path = path; #endif #ifdef TORRENT_DISK_STATS m_file_id = silly_hash(path); #endif #ifdef TORRENT_WINDOWS struct open_mode_t { DWORD rw_mode; DWORD create_mode; }; static const open_mode_t mode_array[] = { // read_only {GENERIC_READ, OPEN_EXISTING}, // write_only {GENERIC_WRITE, OPEN_ALWAYS}, // read_write {GENERIC_WRITE | GENERIC_READ, OPEN_ALWAYS}, }; static const DWORD attrib_array[] = { FILE_ATTRIBUTE_NORMAL, // no attrib FILE_ATTRIBUTE_HIDDEN, // hidden FILE_ATTRIBUTE_NORMAL, // executable FILE_ATTRIBUTE_HIDDEN, // hidden + executable }; std::string p = convert_separators(path); #if TORRENT_USE_UNC_PATHS // UNC paths must be absolute // network paths are already UNC paths if (path.substr(0,2) == "\\\\") p = path; else p = "\\\\?\\" + (is_complete(p) ? p : combine_path(current_working_directory(), p)); #endif #if TORRENT_USE_WSTRING #define CreateFile_ CreateFileW std::wstring file_path = convert_to_wstring(p); #else #define CreateFile_ CreateFileA std::string file_path = convert_to_native(p); #endif TORRENT_ASSERT((mode & rw_mask) < sizeof(mode_array)/sizeof(mode_array[0])); open_mode_t const& m = mode_array[mode & rw_mask]; DWORD const a = attrib_array[(mode & attribute_mask) >> 9]; // one might think it's a good idea to pass in FILE_FLAG_RANDOM_ACCESS. It // turns out that it isn't. That flag will break your operating system: // http://support.microsoft.com/kb/2549369 DWORD flags = ((mode & random_access) ? 0 : FILE_FLAG_SEQUENTIAL_SCAN) | a | FILE_FLAG_OVERLAPPED | ((mode & no_cache) ? FILE_FLAG_WRITE_THROUGH : 0); if ((mode & sparse) == 0) { // Enable privilege required by SetFileValidData() // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilevaliddata acquire_manage_volume_privs(); } handle_type handle = CreateFile_(file_path.c_str(), m.rw_mode , (mode & lock_file) ? FILE_SHARE_READ : FILE_SHARE_READ | FILE_SHARE_WRITE , 0, m.create_mode, flags, 0); if (handle == INVALID_HANDLE_VALUE) { ec.assign(GetLastError(), system_category()); TORRENT_ASSERT(ec); return false; } m_file_handle = handle; // try to make the file sparse if supported // only set this flag if the file is opened for writing if ((mode & file::sparse) && (mode & rw_mask) != read_only) { DWORD temp; overlapped_t ol; BOOL ret = ::DeviceIoControl(native_handle(), FSCTL_SET_SPARSE, 0, 0 , 0, 0, &temp, &ol.ol); error_code error; if (ret == FALSE && GetLastError() == ERROR_IO_PENDING) ol.wait(native_handle(), error); } #else // TORRENT_WINDOWS // rely on default umask to filter x and w permissions // for group and others int permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; if (mode & attribute_executable) permissions |= S_IXGRP | S_IXOTH | S_IXUSR; #ifdef O_BINARY static const int mode_array[] = {O_RDONLY | O_BINARY, O_WRONLY | O_CREAT | O_BINARY, O_RDWR | O_CREAT | O_BINARY}; #else static const int mode_array[] = {O_RDONLY, O_WRONLY | O_CREAT, O_RDWR | O_CREAT}; #endif int open_mode = 0 #ifdef O_NOATIME | ((mode & no_atime) ? O_NOATIME : 0) #endif #ifdef O_SYNC | ((mode & no_cache) ? O_SYNC: 0) #endif ; handle_type handle = ::open(convert_to_native(path).c_str() , mode_array[mode & rw_mask] | open_mode , permissions); #ifdef O_NOATIME // O_NOATIME is not allowed for files we don't own // so, if we get EPERM when we try to open with it // try again without O_NOATIME if (handle == -1 && (mode & no_atime) && errno == EPERM) { mode &= ~no_atime; open_mode &= ~O_NOATIME; handle = ::open(path.c_str(), mode_array[mode & rw_mask] | open_mode , permissions); } #endif if (handle == -1) { ec.assign(errno, system_category()); TORRENT_ASSERT(ec); return false; } m_file_handle = handle; // The purpose of the lock_file flag is primarily to prevent other // processes from corrupting files that are being used by libtorrent. // the posix file locking mechanism does not prevent others from // accessing files, unless they also attempt to lock the file. That's // why the SETLK mechanism is not used here. #ifdef DIRECTIO_ON // for solaris if (mode & no_cache) { int yes = 1; directio(native_handle(), DIRECTIO_ON); } #endif #ifdef F_NOCACHE // for BSD/Mac if (mode & no_cache) { int yes = 1; fcntl(native_handle(), F_NOCACHE, &yes); #ifdef F_NODIRECT // it's OK to temporarily cache written pages fcntl(native_handle(), F_NODIRECT, &yes); #endif } #endif #ifdef POSIX_FADV_RANDOM if (mode & random_access) { // disable read-ahead posix_fadvise(native_handle(), 0, 0, POSIX_FADV_RANDOM); } #endif #endif m_open_mode = mode; TORRENT_ASSERT(is_open()); return true; } #ifdef TORRENT_DEBUG_FILE_LEAKS void file::print_info(FILE* out) const { if (!is_open()) return; fprintf(out, "\n===> FILE: %s\n", m_file_path.c_str()); } #endif bool file::is_open() const { return m_file_handle != INVALID_HANDLE_VALUE; } #ifdef TORRENT_WINDOWS // returns true if the given file has any regions that are // sparse, i.e. not allocated. bool is_sparse(HANDLE file) { LARGE_INTEGER file_size; if (!GetFileSizeEx(file, &file_size)) return false; overlapped_t ol; if (ol.ol.hEvent == NULL) return false; #ifdef TORRENT_MINGW typedef struct _FILE_ALLOCATED_RANGE_BUFFER { LARGE_INTEGER FileOffset; LARGE_INTEGER Length; } FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER; #define FSCTL_QUERY_ALLOCATED_RANGES ((0x9 << 16) | (1 << 14) | (51 << 2) | 3) #endif FILE_ALLOCATED_RANGE_BUFFER in; in.FileOffset.QuadPart = 0; in.Length.QuadPart = file_size.QuadPart; FILE_ALLOCATED_RANGE_BUFFER out[2]; DWORD returned_bytes = 0; BOOL ret = DeviceIoControl(file, FSCTL_QUERY_ALLOCATED_RANGES, (void*)&in, sizeof(in) , out, sizeof(out), &returned_bytes, &ol.ol); if (ret == FALSE && GetLastError() == ERROR_IO_PENDING) { error_code ec; returned_bytes = ol.wait(file, ec); if (ec) return true; } else if (ret == FALSE) { // int error = GetLastError(); return true; } // if we have more than one range in the file, we're sparse if (returned_bytes != sizeof(FILE_ALLOCATED_RANGE_BUFFER)) { return true; } return (in.Length.QuadPart != out[0].Length.QuadPart); } #endif void file::close() { #ifdef TORRENT_DISK_STATS m_file_id = 0; #endif if (!is_open()) return; #ifdef TORRENT_WINDOWS // if this file is open for writing, has the sparse // flag set, but there are no sparse regions, unset // the flag int rw_mode = m_open_mode & rw_mask; if ((rw_mode != read_only) && (m_open_mode & sparse) && !is_sparse(native_handle())) { overlapped_t ol; // according to MSDN, clearing the sparse flag of a file only // works on windows vista and later #ifdef TORRENT_MINGW typedef struct _FILE_SET_SPARSE_BUFFER { BOOLEAN SetSparse; } FILE_SET_SPARSE_BUFFER, *PFILE_SET_SPARSE_BUFFER; #endif DWORD temp; FILE_SET_SPARSE_BUFFER b; b.SetSparse = FALSE; BOOL ret = ::DeviceIoControl(native_handle(), FSCTL_SET_SPARSE, &b, sizeof(b) , 0, 0, &temp, &ol.ol); error_code ec; if (ret == FALSE && GetLastError() == ERROR_IO_PENDING) { ol.wait(native_handle(), ec); } } CloseHandle(native_handle()); #else if (m_file_handle != INVALID_HANDLE_VALUE) ::close(m_file_handle); #endif m_file_handle = INVALID_HANDLE_VALUE; m_open_mode = 0; } namespace { void gather_copy(file::iovec_t const* bufs, int num_bufs, char* dst) { std::size_t offset = 0; for (int i = 0; i < num_bufs; ++i) { memcpy(dst + offset, bufs[i].iov_base, bufs[i].iov_len); offset += bufs[i].iov_len; } } void scatter_copy(file::iovec_t const* bufs, int num_bufs, char const* src) { std::size_t offset = 0; for (int i = 0; i < num_bufs; ++i) { memcpy(bufs[i].iov_base, src + offset, bufs[i].iov_len); offset += bufs[i].iov_len; } } bool coalesce_read_buffers(file::iovec_t const*& bufs, int& num_bufs , file::iovec_t* tmp) { int const buf_size = bufs_size(bufs, num_bufs); char* buf = static_cast(malloc(buf_size)); if (!buf) return false; tmp->iov_base = buf; tmp->iov_len = buf_size; bufs = tmp; num_bufs = 1; return true; } void coalesce_read_buffers_end(file::iovec_t const* bufs, int const num_bufs , char* const buf, bool const copy) { if (copy) scatter_copy(bufs, num_bufs, buf); free(buf); } bool coalesce_write_buffers(file::iovec_t const*& bufs, int& num_bufs , file::iovec_t* tmp) { int const buf_size = bufs_size(bufs, num_bufs); char* buf = static_cast(malloc(buf_size)); if (!buf) return false; gather_copy(bufs, num_bufs, buf); tmp->iov_base = buf; tmp->iov_len = buf_size; bufs = tmp; num_bufs = 1; return true; } template boost::int64_t iov(Fun f, handle_type fd, boost::int64_t file_offset, file::iovec_t const* bufs_in , int num_bufs_in, error_code& ec) { file::iovec_t const* bufs = bufs_in; int num_bufs = num_bufs_in; #if TORRENT_USE_PREADV int ret = 0; while (num_bufs > 0) { int nbufs = (std::min)(num_bufs, TORRENT_IOV_MAX); int tmp_ret = 0; tmp_ret = f(fd, bufs, nbufs, file_offset); if (tmp_ret < 0) { #ifdef TORRENT_WINDOWS ec.assign(GetLastError(), system_category()); #else ec.assign(errno, system_category()); #endif return -1; } file_offset += tmp_ret; ret += tmp_ret; // we got a short read/write. It's either 0, and we're at EOF, or we // just need to issue the read/write operation again. In either case, // punt that to the upper layer, as reissuing the operations is // complicated here const int expected_len = bufs_size(bufs, nbufs); if (tmp_ret < expected_len) break; num_bufs -= nbufs; bufs += nbufs; } return ret; #elif TORRENT_USE_PREAD int ret = 0; for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) { int tmp_ret = f(fd, i->iov_base, i->iov_len, file_offset); if (tmp_ret < 0) { #ifdef TORRENT_WINDOWS ec.assign(GetLastError(), system_category()); #else ec.assign(errno, system_category()); #endif return -1; } file_offset += tmp_ret; ret += tmp_ret; if (tmp_ret < int(i->iov_len)) break; } return ret; #else // not PREADV nor PREAD int ret = 0; #ifdef TORRENT_WINDOWS if (SetFilePointerEx(fd, offs, &offs, FILE_BEGIN) == FALSE) { ec.assign(GetLastError(), system_category()); return -1; } #else if (lseek(fd, file_offset, SEEK_SET) < 0) { ec.assign(errno, system_category()); return -1; } #endif for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) { int tmp_ret = f(fd, i->iov_base, i->iov_len); if (tmp_ret < 0) { #ifdef TORRENT_WINDOWS ec.assign(GetLastError(), system_category()); #else ec.assign(errno, system_category()); #endif return -1; } file_offset += tmp_ret; ret += tmp_ret; if (tmp_ret < int(i->iov_len)) break; } return ret; #endif } } // anonymous namespace // this has to be thread safe and atomic. i.e. on posix systems it has to be // turned into a series of pread() calls boost::int64_t file::readv(boost::int64_t file_offset, iovec_t const* bufs, int num_bufs , error_code& ec, int flags) { if (m_file_handle == INVALID_HANDLE_VALUE) { #ifdef TORRENT_WINDOWS ec = error_code(ERROR_INVALID_HANDLE, system_category()); #else ec = error_code(boost::system::errc::bad_file_descriptor, generic_category()); #endif return -1; } TORRENT_ASSERT((m_open_mode & rw_mask) == read_only || (m_open_mode & rw_mask) == read_write); TORRENT_ASSERT(bufs); TORRENT_ASSERT(num_bufs > 0); TORRENT_ASSERT(is_open()); // there's no point in coalescing single buffer writes if (num_bufs == 1) { flags &= ~file::coalesce_buffers; } file::iovec_t tmp; file::iovec_t const* const orig_bufs = bufs; int const orig_num_bufs = num_bufs; if ((flags & file::coalesce_buffers)) { if (!coalesce_read_buffers(bufs, num_bufs, &tmp)) // ok, that failed, don't coalesce this read flags &= ~file::coalesce_buffers; } #if TORRENT_USE_PREADV int ret = iov(&::preadv, native_handle(), file_offset, bufs, num_bufs, ec); #elif TORRENT_USE_PREAD int ret = iov(&::pread, native_handle(), file_offset, bufs, num_bufs, ec); #else int ret = iov(&::read, native_handle(), file_offset, bufs, num_bufs, ec); #endif if ((flags & file::coalesce_buffers)) coalesce_read_buffers_end(orig_bufs, orig_num_bufs , static_cast(tmp.iov_base), !ec); return ret; } // This has to be thread safe, i.e. atomic. // that means, on posix this has to be turned into a series of // pwrite() calls boost::int64_t file::writev(boost::int64_t file_offset, iovec_t const* bufs, int num_bufs , error_code& ec, int flags) { if (m_file_handle == INVALID_HANDLE_VALUE) { #ifdef TORRENT_WINDOWS ec = error_code(ERROR_INVALID_HANDLE, system_category()); #else ec = error_code(boost::system::errc::bad_file_descriptor, generic_category()); #endif return -1; } TORRENT_ASSERT((m_open_mode & rw_mask) == write_only || (m_open_mode & rw_mask) == read_write); TORRENT_ASSERT(bufs); TORRENT_ASSERT(num_bufs > 0); TORRENT_ASSERT(is_open()); ec.clear(); // there's no point in coalescing single buffer writes if (num_bufs == 1) { flags &= ~file::coalesce_buffers; } file::iovec_t tmp; if (flags & file::coalesce_buffers) { if (!coalesce_write_buffers(bufs, num_bufs, &tmp)) // ok, that failed, don't coalesce writes flags &= ~file::coalesce_buffers; } #if TORRENT_USE_PREADV int ret = iov(&::pwritev, native_handle(), file_offset, bufs, num_bufs, ec); #elif TORRENT_USE_PREAD int ret = iov(&::pwrite, native_handle(), file_offset, bufs, num_bufs, ec); #else int ret = iov(&::write, native_handle(), file_offset, bufs, num_bufs, ec); #endif if (flags & file::coalesce_buffers) free(tmp.iov_base); #if TORRENT_USE_FDATASYNC \ && !defined F_NOCACHE && \ !defined DIRECTIO_ON if (m_open_mode & no_cache) { if (fdatasync(native_handle()) != 0 && errno != EINVAL && errno != ENOSYS) { ec.assign(errno, system_category()); } } #endif return ret; } #ifdef TORRENT_WINDOWS void acquire_manage_volume_privs() { static bool called_once = false; if (called_once) return; called_once = true; typedef BOOL (WINAPI *OpenProcessToken_t)( HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle); typedef BOOL (WINAPI *LookupPrivilegeValue_t)( LPCSTR lpSystemName, LPCSTR lpName, PLUID lpLuid); typedef BOOL (WINAPI *AdjustTokenPrivileges_t)( HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength); HMODULE advapi = LoadLibraryA("advapi32"); if (advapi == NULL) return; BOOST_SCOPE_EXIT(&advapi) { FreeLibrary(advapi); } BOOST_SCOPE_EXIT_END OpenProcessToken_t const pOpenProcessToken = (OpenProcessToken_t)GetProcAddress(advapi, "OpenProcessToken"); LookupPrivilegeValue_t const pLookupPrivilegeValue = (LookupPrivilegeValue_t)GetProcAddress(advapi, "LookupPrivilegeValueA"); AdjustTokenPrivileges_t const pAdjustTokenPrivileges = (AdjustTokenPrivileges_t)GetProcAddress(advapi, "AdjustTokenPrivileges"); if (pOpenProcessToken == NULL || pLookupPrivilegeValue == NULL || pAdjustTokenPrivileges == NULL) { return; } HANDLE token = NULL; if (!pOpenProcessToken(GetCurrentProcess() , TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) return; BOOST_SCOPE_EXIT(&token) { CloseHandle(token); } BOOST_SCOPE_EXIT_END TOKEN_PRIVILEGES privs = { 0 }; if (!pLookupPrivilegeValue(NULL, "SeManageVolumePrivilege" , &privs.Privileges[0].Luid)) { return; } privs.PrivilegeCount = 1; privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; pAdjustTokenPrivileges(token, FALSE, &privs, 0, NULL, NULL); } void set_file_valid_data(HANDLE f, boost::int64_t size) { typedef BOOL (WINAPI *SetFileValidData_t)(HANDLE, LONGLONG); static SetFileValidData_t const pSetFileValidData = (SetFileValidData_t) GetProcAddress(GetModuleHandleA("kernel32"), "SetFileValidData"); if (pSetFileValidData) { // we don't necessarily expect to have enough // privilege to do this, so ignore errors. pSetFileValidData(f, size); } } #endif bool file::set_size(boost::int64_t s, error_code& ec) { TORRENT_ASSERT(is_open()); TORRENT_ASSERT(s >= 0); #ifdef TORRENT_WINDOWS LARGE_INTEGER offs; LARGE_INTEGER cur_size; if (GetFileSizeEx(native_handle(), &cur_size) == FALSE) { ec.assign(GetLastError(), system_category()); return false; } offs.QuadPart = s; // only set the file size if it's not already at // the right size. We don't want to update the // modification time if we don't have to if (cur_size.QuadPart != s) { if (SetFilePointerEx(native_handle(), offs, &offs, FILE_BEGIN) == FALSE) { ec.assign(GetLastError(), system_category()); return false; } if (::SetEndOfFile(native_handle()) == FALSE) { ec.assign(GetLastError(), system_category()); return false; } if ((m_open_mode & sparse) == 0) { // if the user has permissions, avoid filling // the file with zeroes, but just fill it with // garbage instead set_file_valid_data(m_file_handle, s); } } #else // NON-WINDOWS struct stat st; if (fstat(native_handle(), &st) != 0) { ec.assign(errno, system_category()); return false; } // only truncate the file if it doesn't already // have the right size. We don't want to update if (st.st_size != s && ftruncate(native_handle(), s) < 0) { ec.assign(errno, system_category()); return false; } // if we're not in sparse mode, allocate the storage // but only if the number of allocated blocks for the file // is less than the file size. Otherwise we would just // update the modification time of the file for no good // reason. if ((m_open_mode & sparse) == 0 && st.st_blocks < (s + st.st_blksize - 1) / st.st_blksize) { // How do we know that the file is already allocated? // if we always try to allocate the space, we'll update // the modification time without actually changing the file // but if we don't do anything if the file size is #ifdef F_PREALLOCATE fstore_t f = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, s, 0}; if (fcntl(native_handle(), F_PREALLOCATE, &f) < 0) { // It appears Apple's new filesystem (APFS) does not // support this control message and fails with EINVAL // if so, just skip it if (errno != EINVAL) { if (errno != ENOSPC) { ec.assign(errno, system_category()); return false; } // ok, let's try to allocate non contiguous space then f.fst_flags = F_ALLOCATEALL; if (fcntl(native_handle(), F_PREALLOCATE, &f) < 0) { ec.assign(errno, system_category()); return false; } } } #endif // F_PREALLOCATE #ifdef F_ALLOCSP64 flock64 fl64; fl64.l_whence = SEEK_SET; fl64.l_start = 0; fl64.l_len = s; if (fcntl(native_handle(), F_ALLOCSP64, &fl64) < 0) { ec.assign(errno, system_category()); return false; } #endif // F_ALLOCSP64 #if defined TORRENT_LINUX || TORRENT_HAS_FALLOCATE int ret; #endif #if TORRENT_HAS_FALLOCATE // if fallocate failed, we have to use posix_fallocate // which can be painfully slow // if you get a compile error here, you might want to // define TORRENT_HAS_FALLOCATE to 0. ret = posix_fallocate(native_handle(), 0, s); // posix_allocate fails with EINVAL or ENOTSUP in case the underlying // filesystem does not support this operation if (ret != 0 && ret != EINVAL && ret != ENOTSUP) { ec.assign(ret, system_category()); return false; } #endif // TORRENT_HAS_FALLOCATE } #endif // TORRENT_WINDOWS return true; } boost::int64_t file::get_size(error_code& ec) const { #ifdef TORRENT_WINDOWS LARGE_INTEGER file_size; if (!GetFileSizeEx(native_handle(), &file_size)) { ec.assign(GetLastError(), system_category()); return -1; } return file_size.QuadPart; #else struct stat fs; if (fstat(native_handle(), &fs) != 0) { ec.assign(errno, system_category()); return -1; } return fs.st_size; #endif } boost::int64_t file::sparse_end(boost::int64_t start) const { #ifdef TORRENT_WINDOWS #ifdef TORRENT_MINGW typedef struct _FILE_ALLOCATED_RANGE_BUFFER { LARGE_INTEGER FileOffset; LARGE_INTEGER Length; } FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER; #define FSCTL_QUERY_ALLOCATED_RANGES ((0x9 << 16) | (1 << 14) | (51 << 2) | 3) #endif // TORRENT_MINGW FILE_ALLOCATED_RANGE_BUFFER buffer; DWORD bytes_returned = 0; FILE_ALLOCATED_RANGE_BUFFER in; error_code ec; boost::int64_t file_size = get_size(ec); if (ec) return start; in.FileOffset.QuadPart = start; in.Length.QuadPart = file_size - start; if (!DeviceIoControl(native_handle(), FSCTL_QUERY_ALLOCATED_RANGES , &in, sizeof(FILE_ALLOCATED_RANGE_BUFFER) , &buffer, sizeof(FILE_ALLOCATED_RANGE_BUFFER), &bytes_returned, 0)) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return start; } // if there are no allocated regions within the rest // of the file, return the end of the file if (bytes_returned == 0) return file_size; // assume that this range overlaps the start of the // region we were interested in, and that start actually // resides in an allocated region. if (buffer.FileOffset.QuadPart < start) return start; // return the offset to the next allocated region return buffer.FileOffset.QuadPart; #elif defined SEEK_DATA // this is supported on solaris boost::int64_t ret = lseek(native_handle(), start, SEEK_DATA); if (ret < 0) return start; return start; #else return start; #endif } #ifdef TORRENT_DEBUG_FILE_LEAKS std::set global_file_handles; mutex file_handle_mutex; file_handle::file_handle() { mutex::scoped_lock l(file_handle_mutex); global_file_handles.insert(this); stack[0] = 0; } file_handle::file_handle(file* f): m_file(f) { mutex::scoped_lock l(file_handle_mutex); global_file_handles.insert(this); if (f) print_backtrace(stack, sizeof(stack), 10); else stack[0] = 0; } file_handle::file_handle(file_handle const& fh) { mutex::scoped_lock l(file_handle_mutex); global_file_handles.insert(this); m_file = fh.m_file; if (m_file) print_backtrace(stack, sizeof(stack), 10); else stack[0] = 0; } file_handle::~file_handle() { mutex::scoped_lock l(file_handle_mutex); global_file_handles.erase(this); stack[0] = 0; } file* file_handle::operator->() { return m_file.get(); } file const* file_handle::operator->() const { return m_file.get(); } file& file_handle::operator*() { return *m_file.get(); } file const& file_handle::operator*() const { return *m_file.get(); } file* file_handle::get() { return m_file.get(); } file const* file_handle::get() const { return m_file.get(); } file_handle::operator bool() const { return m_file.get(); } file_handle& file_handle::reset(file* f) { mutex::scoped_lock l(file_handle_mutex); if (f) print_backtrace(stack, sizeof(stack), 10); else stack[0] = 0; l.unlock(); m_file.reset(f); return *this; } void print_open_files(char const* event, char const* name) { FILE* out = fopen("open_files.log", "a+"); mutex::scoped_lock l(file_handle_mutex); fprintf(out, "\n\nEVENT: %s TORRENT: %s\n\n", event, name); for (std::set::iterator i = global_file_handles.begin() , end(global_file_handles.end()); i != end; ++i) { TORRENT_ASSERT(*i != NULL); if (!*i) continue; file_handle const& h = **i; if (!h) continue; if (!h->is_open()) continue; h->print_info(out); fprintf(out, "\n%s\n\n", h.stack); } fclose(out); } #endif } libtorrent-rasterbar-1.1.13/src/file_pool.cpp000066400000000000000000000245461351156116000212210ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/file_pool.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/file_storage.hpp" // for file_entry #include "libtorrent/aux_/time.hpp" namespace libtorrent { file_pool::file_pool(int size) : m_size(size) , m_low_prio_io(true) { } file_pool::~file_pool() { } #ifdef TORRENT_WINDOWS void set_low_priority(file_handle const& f) { // file prio is only supported on vista and up // so load the functions dynamically typedef enum _FILE_INFO_BY_HANDLE_CLASS { FileBasicInfo, FileStandardInfo, FileNameInfo, FileRenameInfo, FileDispositionInfo, FileAllocationInfo, FileEndOfFileInfo, FileStreamInfo, FileCompressionInfo, FileAttributeTagInfo, FileIdBothDirectoryInfo, FileIdBothDirectoryRestartInfo, FileIoPriorityHintInfo, FileRemoteProtocolInfo, MaximumFileInfoByHandleClass } FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS; typedef enum _PRIORITY_HINT { IoPriorityHintVeryLow = 0, IoPriorityHintLow, IoPriorityHintNormal, MaximumIoPriorityHintType } PRIORITY_HINT; typedef struct _FILE_IO_PRIORITY_HINT_INFO { PRIORITY_HINT PriorityHint; } FILE_IO_PRIORITY_HINT_INFO, *PFILE_IO_PRIORITY_HINT_INFO; typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize); static SetFileInformationByHandle_t SetFileInformationByHandle = NULL; static bool failed_kernel_load = false; if (failed_kernel_load) return; if (SetFileInformationByHandle == NULL) { HMODULE kernel32 = LoadLibraryA("kernel32.dll"); if (kernel32 == NULL) { failed_kernel_load = true; return; } SetFileInformationByHandle = (SetFileInformationByHandle_t)GetProcAddress(kernel32, "SetFileInformationByHandle"); if (SetFileInformationByHandle == NULL) { failed_kernel_load = true; return; } } TORRENT_ASSERT(SetFileInformationByHandle); FILE_IO_PRIORITY_HINT_INFO io_hint; io_hint.PriorityHint = IoPriorityHintLow; SetFileInformationByHandle(f->native_handle(), FileIoPriorityHintInfo, &io_hint, sizeof(io_hint)); } #endif // TORRENT_WINDOWS file_handle file_pool::open_file(void* st, std::string const& p , int file_index, file_storage const& fs, int m, error_code& ec) { // potentially used to hold a reference to a file object that's // about to be destructed. If we have such object we assign it to // this member to be destructed after we release the mutex. On some // operating systems (such as OSX) closing a file may take a long // time. We don't want to hold the mutex for that. file_handle defer_destruction; mutex::scoped_lock l(m_mutex); #if TORRENT_USE_ASSERTS // we're not allowed to open a file // from a deleted storage! TORRENT_ASSERT(std::find(m_deleted_storages.begin(), m_deleted_storages.end() , std::make_pair(fs.name(), static_cast(&fs))) == m_deleted_storages.end()); #endif TORRENT_ASSERT(st != 0); TORRENT_ASSERT(is_complete(p)); TORRENT_ASSERT((m & file::rw_mask) == file::read_only || (m & file::rw_mask) == file::read_write); file_set::iterator i = m_files.find(std::make_pair(st, file_index)); if (i != m_files.end()) { lru_file_entry& e = i->second; e.last_use = aux::time_now(); // if we asked for a file in write mode, // and the cached file is is not opened in // write mode, re-open it if ((((e.mode & file::rw_mask) != file::read_write) && ((m & file::rw_mask) == file::read_write)) || (e.mode & file::random_access) != (m & file::random_access)) { // close the file before we open it with // the new read/write privileges, since windows may // file opening a file twice. However, since there may // be outstanding operations on it, we can't close the // file, we can only delete our reference to it. // if this is the only reference to the file, it will be closed defer_destruction = e.file_ptr; e.file_ptr = boost::make_shared(); std::string full_path = fs.file_path(file_index, p); if (!e.file_ptr->open(full_path, m, ec)) { m_files.erase(i); return file_handle(); } #ifdef TORRENT_WINDOWS if (m_low_prio_io) set_low_priority(e.file_ptr); #endif TORRENT_ASSERT(e.file_ptr->is_open()); e.mode = m; } return e.file_ptr; } lru_file_entry e; e.file_ptr = boost::make_shared(); if (!e.file_ptr) { ec = error_code(boost::system::errc::not_enough_memory, generic_category()); return e.file_ptr; } std::string full_path = fs.file_path(file_index, p); if (!e.file_ptr->open(full_path, m, ec)) return file_handle(); #ifdef TORRENT_WINDOWS if (m_low_prio_io) set_low_priority(e.file_ptr); #endif e.mode = m; file_handle file_ptr = e.file_ptr; m_files.insert(std::make_pair(std::make_pair(st, file_index), e)); TORRENT_ASSERT(file_ptr->is_open()); if (int(m_files.size()) >= m_size) { // the file cache is at its maximum size, close // the least recently used (lru) file from it defer_destruction = remove_oldest(l); } return file_ptr; } void file_pool::get_status(std::vector* files, void* st) const { mutex::scoped_lock l(m_mutex); file_set::const_iterator start = m_files.lower_bound(std::make_pair(st, 0)); file_set::const_iterator end = m_files.upper_bound(std::make_pair(st, INT_MAX)); for (file_set::const_iterator i = start; i != end; ++i) { pool_file_status s; s.file_index = i->first.second; s.open_mode = i->second.mode; s.last_use = i->second.last_use; files->push_back(s); } } file_handle file_pool::remove_oldest(mutex::scoped_lock&) { file_set::iterator i = std::min_element(m_files.begin(), m_files.end() , boost::bind(&lru_file_entry::last_use, boost::bind(&file_set::value_type::second, _1)) < boost::bind(&lru_file_entry::last_use, boost::bind(&file_set::value_type::second, _2))); if (i == m_files.end()) return file_handle(); file_handle file_ptr = i->second.file_ptr; m_files.erase(i); // closing a file may be long running operation (mac os x) // let the calling function destruct it after releasing the mutex return file_ptr; } void file_pool::release(void* st, int file_index) { mutex::scoped_lock l(m_mutex); file_set::iterator i = m_files.find(std::make_pair(st, file_index)); if (i == m_files.end()) return; file_handle file_ptr = i->second.file_ptr; m_files.erase(i); // closing a file may take a long time (mac os x), so make sure // we're not holding the mutex l.unlock(); file_ptr.reset(); } // closes files belonging to the specified // storage. If 0 is passed, all files are closed void file_pool::release(void* st) { mutex::scoped_lock l(m_mutex); if (st == 0) { m_files.clear(); l.unlock(); return; } file_set::iterator begin = m_files.lower_bound(std::make_pair(st, 0)); file_set::iterator end = m_files.upper_bound(std::make_pair(st, std::numeric_limits::max())); std::vector to_close; while (begin != end) { to_close.push_back(begin->second.file_ptr); m_files.erase(begin++); } l.unlock(); // the files are closed here while the lock is not held } #if TORRENT_USE_ASSERTS void file_pool::mark_deleted(file_storage const& fs) { mutex::scoped_lock l(m_mutex); m_deleted_storages.push_back(std::make_pair(fs.name() , static_cast(&fs))); if(m_deleted_storages.size() > 100) m_deleted_storages.erase(m_deleted_storages.begin()); } bool file_pool::assert_idle_files(void* st) const { mutex::scoped_lock l(m_mutex); for (file_set::const_iterator i = m_files.begin(); i != m_files.end(); ++i) { if (i->first.first == st && !i->second.file_ptr.unique()) return false; } return true; } #endif void file_pool::resize(int size) { // these are destructed _after_ the mutex is released std::vector defer_destruction; mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(size > 0); if (size == m_size) return; m_size = size; if (int(m_files.size()) <= m_size) return; // close the least recently used files while (int(m_files.size()) > m_size) defer_destruction.push_back(remove_oldest(l)); } void file_pool::close_oldest() { mutex::scoped_lock l(m_mutex); file_set::iterator i = std::min_element(m_files.begin(), m_files.end() , boost::bind(&lru_file_entry::opened, boost::bind(&file_set::value_type::second, _1)) < boost::bind(&lru_file_entry::opened, boost::bind(&file_set::value_type::second, _2))); if (i == m_files.end()) return; file_handle file_ptr = i->second.file_ptr; m_files.erase(i); // closing a file may be long running operation (mac os x) l.unlock(); file_ptr.reset(); l.lock(); } } libtorrent-rasterbar-1.1.13/src/file_progress.cpp000066400000000000000000000133161351156116000221050ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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/piece_picker.hpp" #include "libtorrent/file_storage.hpp" #include "libtorrent/alert_manager.hpp" #include "libtorrent/aux_/file_progress.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/invariant_check.hpp" namespace libtorrent { namespace aux { file_progress::file_progress() { } void file_progress::init(piece_picker const& picker, file_storage const& fs) { if (!m_file_progress.empty()) return; int const num_pieces = fs.num_pieces(); int const num_files = fs.num_files(); #if TORRENT_USE_INVARIANT_CHECKS m_have_pieces.clear(); m_have_pieces.resize(num_pieces, false); m_file_sizes.clear(); m_file_sizes.reserve(num_files); for (int i = 0; i < int(fs.num_files()); ++i) m_file_sizes.push_back(fs.file_size(i)); #endif m_file_progress.resize(num_files, 0); std::fill(m_file_progress.begin(), m_file_progress.end(), 0); // initialize the progress of each file const int piece_size = fs.piece_length(); boost::uint64_t off = 0; boost::uint64_t total_size = fs.total_size(); int file_index = 0; for (int piece = 0; piece < num_pieces; ++piece, off += piece_size) { TORRENT_ASSERT(file_index < fs.num_files()); boost::int64_t file_offset = off - fs.file_offset(file_index); TORRENT_ASSERT(file_offset >= 0); while (file_offset >= fs.file_size(file_index)) { ++file_index; TORRENT_ASSERT(file_index < fs.num_files()); file_offset = off - fs.file_offset(file_index); TORRENT_ASSERT(file_offset >= 0); } TORRENT_ASSERT(file_offset <= fs.file_size(file_index)); if (!picker.have_piece(piece)) continue; #if TORRENT_USE_INVARIANT_CHECKS m_have_pieces.set_bit(piece); #endif int size = (std::min)(boost::uint64_t(piece_size), total_size - off); TORRENT_ASSERT(size >= 0); while (size) { int const add = (std::min)(boost::int64_t(size), fs.file_size(file_index) - file_offset); TORRENT_ASSERT(add >= 0); m_file_progress[file_index] += add; TORRENT_ASSERT(m_file_progress[file_index] <= fs.file_size(file_index)); size -= add; TORRENT_ASSERT(size >= 0); if (size > 0) { ++file_index; TORRENT_ASSERT(file_index < fs.num_files()); file_offset = 0; } } } } void file_progress::export_progress(std::vector &fp) { fp.resize(m_file_progress.size(), 0); std::copy(m_file_progress.begin(), m_file_progress.end(), fp.begin()); } void file_progress::clear() { std::vector().swap(m_file_progress); #if TORRENT_USE_INVARIANT_CHECKS m_have_pieces.clear(); #endif } // update the file progress now that we just completed downloading piece // 'index' void file_progress::update(file_storage const& fs, int index , alert_manager* alerts, torrent_handle const& h) { INVARIANT_CHECK; #if TORRENT_USE_INVARIANT_CHECKS TORRENT_ASSERT(m_have_pieces.get_bit(index) == false); m_have_pieces.set_bit(index); #endif if (m_file_progress.empty()) return; const int piece_size = fs.piece_length(); boost::int64_t off = boost::int64_t(index) * piece_size; int file_index = fs.file_index_at_offset(off); int size = fs.piece_size(index); for (; size > 0; ++file_index) { boost::int64_t file_offset = off - fs.file_offset(file_index); TORRENT_ASSERT(file_index != fs.num_files()); TORRENT_ASSERT(file_offset <= fs.file_size(file_index)); int add = (std::min)(fs.file_size(file_index) - file_offset, boost::int64_t(size)); m_file_progress[file_index] += add; TORRENT_ASSERT(m_file_progress[file_index] <= fs.file_size(file_index)); // TODO: it would be nice to not depend on alert_manager here if (m_file_progress[file_index] >= fs.file_size(file_index) && alerts) { if (!fs.pad_file_at(file_index)) { if (alerts->should_post()) { // this file just completed, post alert alerts->emplace_alert(h, file_index); } } } size -= add; off += add; TORRENT_ASSERT(size >= 0); } } #if TORRENT_USE_INVARIANT_CHECKS void file_progress::check_invariant() const { if (m_file_progress.empty()) return; for (int i = 0; i < int(m_file_progress.size()); ++i) { TORRENT_ASSERT(m_file_progress[i] <= m_file_sizes[i]); } } #endif } } libtorrent-rasterbar-1.1.13/src/file_storage.cpp000066400000000000000000000771461351156116000217200ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/file_storage.hpp" #include "libtorrent/string_util.hpp" // for allocate_string_copy #include "libtorrent/file.hpp" #include "libtorrent/utf8.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include #include #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) #define TORRENT_SEPARATOR '\\' #else #define TORRENT_SEPARATOR '/' #endif namespace libtorrent { file_storage::file_storage() : m_piece_length(0) , m_num_pieces(0) , m_total_size(0) , m_num_files(0) {} file_storage::~file_storage() {} // even though this copy constructor and the copy assignment // operator are identical to what the compiler would have // generated, they are put here to explicitly make them part // of libtorrent and properly exported by the .dll. file_storage::file_storage(file_storage const& f) : m_piece_length(f.m_piece_length) , m_num_pieces(f.m_num_pieces) , m_files(f.m_files) , m_file_hashes(f.m_file_hashes) , m_symlinks(f.m_symlinks) , m_mtime(f.m_mtime) #ifndef TORRENT_NO_DEPRECATE , m_file_base(f.m_file_base) #endif , m_paths(f.m_paths) , m_name(f.m_name) , m_total_size(f.m_total_size) , m_num_files(f.m_num_files) { } file_storage& file_storage::operator=(file_storage const& f) { m_piece_length = f.m_piece_length; m_num_pieces = f.m_num_pieces; m_files = f.m_files; m_file_hashes = f.m_file_hashes; m_symlinks = f.m_symlinks; m_mtime = f.m_mtime; #ifndef TORRENT_NO_DEPRECATE m_file_base = f.m_file_base; #endif m_paths = f.m_paths; m_name = f.m_name; m_total_size = f.m_total_size; m_num_files = f.m_num_files; return *this; } void file_storage::reserve(int num_files) { m_files.reserve(num_files); } int file_storage::piece_size(int index) const { TORRENT_ASSERT_PRECOND(index >= 0 && index < num_pieces()); if (index == num_pieces()-1) { boost::int64_t size_except_last = num_pieces() - 1; size_except_last *= boost::int64_t(piece_length()); boost::int64_t size = total_size() - size_except_last; TORRENT_ASSERT(size > 0); TORRENT_ASSERT(size <= piece_length()); return int(size); } else return piece_length(); } namespace { bool compare_string(char const* str, int len, std::string const& str2) { if (str2.size() != len) return false; return memcmp(str2.c_str(), str, len) == 0; } bool compare_file_entry_size(internal_file_entry const& fe1 , internal_file_entry const& fe2) { return fe1.size < fe2.size; } bool compare_file_offset(internal_file_entry const& lhs , internal_file_entry const& rhs) { return lhs.offset < rhs.offset; } } // path is not supposed to include the name of the torrent itself. void file_storage::update_path_index(internal_file_entry& e , std::string const& path, bool set_name) { if (is_complete(path)) { TORRENT_ASSERT(set_name); e.set_name(path.c_str()); e.path_index = -2; return; } TORRENT_ASSERT(path[0] != '/'); // sorry about this messy string handling, but I did // profile it, and it was expensive char const* leaf = filename_cstr(path.c_str()); char const* branch_path = ""; int branch_len = 0; if (leaf > path.c_str()) { // split the string into the leaf filename // and the branch path branch_path = path.c_str(); branch_len = leaf - path.c_str(); } if (branch_len <= 0) { if (set_name) e.set_name(leaf); e.path_index = -1; return; } if (branch_len >= m_name.size() && std::memcmp(branch_path, m_name.c_str(), m_name.size()) == 0 && branch_path[m_name.size()] == TORRENT_SEPARATOR) { int const offset = m_name.size(); branch_path += offset; branch_len -= offset; while (branch_len > 0 && branch_path[0] == TORRENT_SEPARATOR) { --branch_len; ++branch_path; } e.no_root_dir = false; } else { e.no_root_dir = true; } e.path_index = get_or_add_path(branch_path, branch_len); if (set_name) e.set_name(leaf); } int file_storage::get_or_add_path(char const* branch_path, int branch_len) { // trim trailing slashes while (branch_len > 0 && branch_path[branch_len-1] == TORRENT_SEPARATOR) --branch_len; // do we already have this path in the path list? std::vector::reverse_iterator p = std::find_if(m_paths.rbegin(), m_paths.rend() , boost::bind(&compare_string, branch_path, branch_len, _1)); if (p == m_paths.rend()) { // no, we don't. add it int const ret = int(m_paths.size()); TORRENT_ASSERT(branch_len == 0 || branch_path[0] != TORRENT_SEPARATOR); m_paths.push_back(std::string(branch_path, branch_len)); return ret; } else { // yes we do. use it return int(p.base() - m_paths.begin() - 1); } } #ifndef TORRENT_NO_DEPRECATE file_entry::file_entry(): offset(0), size(0), file_base(0) , mtime(0), pad_file(false), hidden_attribute(false) , executable_attribute(false) , symlink_attribute(false) {} file_entry::~file_entry() {} #endif // TORRENT_NO_DEPRECATE internal_file_entry::~internal_file_entry() { if (name_len == name_is_owned) free(const_cast(name)); } internal_file_entry::internal_file_entry(internal_file_entry const& fe) : offset(fe.offset) , symlink_index(fe.symlink_index) , no_root_dir(fe.no_root_dir) , size(fe.size) , name_len(fe.name_len) , pad_file(fe.pad_file) , hidden_attribute(fe.hidden_attribute) , executable_attribute(fe.executable_attribute) , symlink_attribute(fe.symlink_attribute) , name(0) , path_index(fe.path_index) { if (fe.name_len == name_is_owned) name = allocate_string_copy(fe.name); else name = fe.name; } internal_file_entry& internal_file_entry::operator=(internal_file_entry const& fe) { offset = fe.offset; size = fe.size; path_index = fe.path_index; symlink_index = fe.symlink_index; pad_file = fe.pad_file; hidden_attribute = fe.hidden_attribute; executable_attribute = fe.executable_attribute; symlink_attribute = fe.symlink_attribute; no_root_dir = fe.no_root_dir; set_name(fe.filename().c_str()); return *this; } // if borrow_chars >= 0, don't take ownership over n, just // point to it. It points to borrow_chars number of characters. // if borrow_chars == -1, n is a null terminated string that // should be copied void internal_file_entry::set_name(char const* n, bool borrow_string, int string_len) { TORRENT_ASSERT(string_len >= 0); // we have limited space in the length field. truncate string // if it's too long if (string_len >= name_is_owned) string_len = name_is_owned - 1; // free the current string, before assigning the new one if (name_len == name_is_owned) free(const_cast(name)); if (n == NULL) { TORRENT_ASSERT(borrow_string == false); name = NULL; } else if (borrow_string) { name = n; name_len = string_len; } else { name = allocate_string_copy(n); name_len = name_is_owned; } } std::string internal_file_entry::filename() const { if (name_len != name_is_owned) return std::string(name, name_len); return name ? name : ""; } void file_storage::apply_pointer_offset(ptrdiff_t off) { for (int i = 0; i < m_files.size(); ++i) { if (m_files[i].name_len == internal_file_entry::name_is_owned) continue; m_files[i].name += off; } for (int i = 0; i < m_file_hashes.size(); ++i) { if (m_file_hashes[i] == NULL) continue; m_file_hashes[i] += off; } } #ifndef TORRENT_NO_DEPRECATE void file_storage::add_file(file_entry const& fe, char const* filehash) { int flags = 0; if (fe.pad_file) flags |= file_storage::flag_pad_file; if (fe.hidden_attribute) flags |= file_storage::flag_hidden; if (fe.executable_attribute) flags |= file_storage::flag_executable; if (fe.symlink_attribute) flags |= file_storage::flag_symlink; add_file_borrow(NULL, 0, fe.path, fe.size, flags, filehash, fe.mtime , fe.symlink_path); } #if TORRENT_USE_WSTRING void file_storage::set_name(std::wstring const& n) { std::string utf8; wchar_utf8(n, utf8); m_name = utf8; } void file_storage::rename_file_deprecated(int index, std::wstring const& new_filename) { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); std::string utf8; wchar_utf8(new_filename, utf8); update_path_index(m_files[index], utf8); } void file_storage::add_file(std::wstring const& file, boost::int64_t file_size , int file_flags, std::time_t mtime, std::string const& symlink_path) { std::string utf8; wchar_utf8(file, utf8); add_file(utf8, file_size, file_flags, mtime, symlink_path); } void file_storage::rename_file(int index, std::wstring const& new_filename) { rename_file_deprecated(index, new_filename); } #endif // TORRENT_USE_WSTRING #endif // TORRENT_NO_DEPRECATE void file_storage::rename_file(int index, std::string const& new_filename) { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); update_path_index(m_files[index], new_filename); } #ifndef TORRENT_NO_DEPRECATE file_storage::iterator file_storage::file_at_offset_deprecated(boost::int64_t offset) const { // find the file iterator and file offset internal_file_entry target; target.offset = offset; TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); std::vector::const_iterator file_iter = std::upper_bound( begin_deprecated(), end_deprecated(), target, compare_file_offset); TORRENT_ASSERT(file_iter != begin_deprecated()); --file_iter; return file_iter; } file_storage::iterator file_storage::file_at_offset(boost::int64_t offset) const { return file_at_offset_deprecated(offset); } #endif int file_storage::file_index_at_offset(boost::int64_t offset) const { TORRENT_ASSERT_PRECOND(offset >= 0); TORRENT_ASSERT_PRECOND(offset < m_total_size); // find the file iterator and file offset internal_file_entry target; target.offset = offset; TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); std::vector::const_iterator file_iter = std::upper_bound( m_files.begin(), m_files.end(), target, compare_file_offset); TORRENT_ASSERT(file_iter != m_files.begin()); --file_iter; return file_iter - m_files.begin(); } char const* file_storage::file_name_ptr(int index) const { return m_files[index].name; } int file_storage::file_name_len(int index) const { if (m_files[index].name_len == internal_file_entry::name_is_owned) return -1; return m_files[index].name_len; } std::vector file_storage::map_block(int const piece , boost::int64_t const offset , int size) const { TORRENT_ASSERT_PRECOND(piece >= 0); TORRENT_ASSERT_PRECOND(piece < num_pieces()); TORRENT_ASSERT_PRECOND(num_files() > 0); std::vector ret; if (m_files.empty()) return ret; // find the file iterator and file offset internal_file_entry target; target.offset = piece * boost::int64_t(m_piece_length) + offset; TORRENT_ASSERT_PRECOND(boost::int64_t(target.offset + size) <= m_total_size); TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); // in case the size is past the end, fix it up if (boost::int64_t(target.offset + size) > m_total_size) size = m_total_size - target.offset; std::vector::const_iterator file_iter = std::upper_bound( m_files.begin(), m_files.end(), target, compare_file_offset); TORRENT_ASSERT(file_iter != m_files.begin()); --file_iter; boost::int64_t file_offset = target.offset - file_iter->offset; for (; size > 0; file_offset -= file_iter->size, ++file_iter) { TORRENT_ASSERT(file_iter != m_files.end()); if (file_offset < boost::int64_t(file_iter->size)) { file_slice f; f.file_index = file_iter - m_files.begin(); f.offset = file_offset #ifndef TORRENT_NO_DEPRECATE + file_base_deprecated(f.file_index) #endif ; f.size = (std::min)(boost::uint64_t(file_iter->size) - file_offset, boost::uint64_t(size)); TORRENT_ASSERT(f.size <= size); size -= int(f.size); file_offset += f.size; ret.push_back(f); } TORRENT_ASSERT(size >= 0); } return ret; } #ifndef TORRENT_NO_DEPRECATE file_entry file_storage::at(int index) const { return at_deprecated(index); } file_entry file_storage::at_deprecated(int index) const { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); file_entry ret; internal_file_entry const& ife = m_files[index]; ret.path = file_path(index); ret.offset = ife.offset; ret.size = ife.size; ret.file_base = file_base(index); ret.mtime = mtime(index); ret.pad_file = ife.pad_file; ret.hidden_attribute = ife.hidden_attribute; ret.executable_attribute = ife.executable_attribute; ret.symlink_attribute = ife.symlink_attribute; if (ife.symlink_index != internal_file_entry::not_a_symlink) ret.symlink_path = symlink(index); ret.filehash = hash(index); return ret; } #endif // TORRENT_NO_DEPRECATE peer_request file_storage::map_file(int file_index, boost::int64_t file_offset , int size) const { TORRENT_ASSERT_PRECOND(file_index < num_files()); TORRENT_ASSERT_PRECOND(file_index >= 0); TORRENT_ASSERT(m_num_pieces >= 0); peer_request ret; if (file_index < 0 || file_index >= num_files()) { ret.piece = m_num_pieces; ret.start = 0; ret.length = 0; return ret; } boost::int64_t offset = file_offset + this->file_offset(file_index); if (offset >= total_size()) { ret.piece = m_num_pieces; ret.start = 0; ret.length = 0; } else { ret.piece = int(offset / piece_length()); ret.start = int(offset % piece_length()); ret.length = size; if (offset + size > total_size()) ret.length = int(total_size() - offset); } return ret; } void file_storage::add_file(std::string const& path, boost::int64_t file_size , int file_flags, std::time_t mtime, std::string const& symlink_path) { add_file_borrow(NULL, 0, path, file_size, file_flags, NULL, mtime , symlink_path); } void file_storage::add_file_borrow(char const* filename, int const filename_len , std::string const& path, boost::int64_t const file_size , boost::uint32_t const file_flags, char const* filehash , boost::int64_t const mtime, std::string const& symlink_path) { TORRENT_ASSERT_PRECOND(file_size >= 0); if (!has_parent_path(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. TORRENT_ASSERT_PRECOND(m_files.empty()); m_name = path; } else { if (m_files.empty()) m_name = split_path(path).c_str(); } // this is poor-man's emplace_back() m_files.resize(m_files.size() + 1); internal_file_entry& e = m_files.back(); // the last argument specified whether the function should also set // the filename. If it does, it will copy the leaf filename from path. // if filename is NULL, we should copy it. If it isn't, we're borrowing // it and we can save the copy by setting it after this call to // update_path_index(). update_path_index(e, path, filename == NULL); // filename is allowed to be NULL, in which case we just use path if (filename) e.set_name(filename, true, filename_len); e.size = file_size; e.offset = m_total_size; e.pad_file = (file_flags & file_storage::flag_pad_file) != 0; e.hidden_attribute = (file_flags & file_storage::flag_hidden) != 0; e.executable_attribute = (file_flags & file_storage::flag_executable) != 0; e.symlink_attribute = (file_flags & file_storage::flag_symlink) != 0; if (filehash) { if (m_file_hashes.size() < m_files.size()) m_file_hashes.resize(m_files.size()); m_file_hashes[m_files.size() - 1] = filehash; } if (!symlink_path.empty() && m_symlinks.size() < internal_file_entry::not_a_symlink - 1) { e.symlink_index = m_symlinks.size(); m_symlinks.push_back(symlink_path); } else { e.symlink_attribute = false; } if (mtime) { if (m_mtime.size() < m_files.size()) m_mtime.resize(m_files.size()); m_mtime[m_files.size() - 1] = mtime; } ++m_num_files; m_total_size += e.size; } sha1_hash file_storage::hash(int index) const { if (index >= int(m_file_hashes.size())) return sha1_hash(0); return sha1_hash(m_file_hashes[index]); } std::string const& file_storage::symlink(int index) const { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); internal_file_entry const& fe = m_files[index]; TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size())); return m_symlinks[fe.symlink_index]; } time_t file_storage::mtime(int index) const { if (index >= int(m_mtime.size())) return 0; return m_mtime[index]; } namespace { template void process_string_lowercase(CRC& crc, char const* str, int len) { for (int i = 0; i < len; ++i, ++str) crc.process_byte(to_lower(*str)); } template void process_path_lowercase( boost::unordered_set& table , CRC crc , char const* str, int len) { if (len == 0) return; for (int i = 0; i < len; ++i, ++str) { if (*str == TORRENT_SEPARATOR) table.insert(crc.checksum()); crc.process_byte(to_lower(*str)); } table.insert(crc.checksum()); } } void file_storage::all_path_hashes( boost::unordered_set& table) const { boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; if (!m_name.empty()) { process_string_lowercase(crc, m_name.c_str(), m_name.size()); TORRENT_ASSERT(m_name[m_name.size()-1] != TORRENT_SEPARATOR); crc.process_byte(TORRENT_SEPARATOR); } for (int i = 0; i != int(m_paths.size()); ++i) { std::string const& p = m_paths[i]; process_path_lowercase(table, crc, p.c_str(), p.size()); } } boost::uint32_t file_storage::file_path_hash(int index , std::string const& save_path) const { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); internal_file_entry const& fe = m_files[index]; boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; if (fe.path_index == -2) { // -2 means this is an absolute path filename process_string_lowercase(crc, fe.filename_ptr(), fe.filename_len()); } else if (fe.path_index == -1) { // -1 means no path if (!save_path.empty()) { process_string_lowercase(crc, save_path.c_str(), save_path.size()); TORRENT_ASSERT(save_path[save_path.size()-1] != TORRENT_SEPARATOR); crc.process_byte(TORRENT_SEPARATOR); } process_string_lowercase(crc, fe.filename_ptr(), fe.filename_len()); } else if (fe.no_root_dir) { if (!save_path.empty()) { process_string_lowercase(crc, save_path.c_str(), save_path.size()); TORRENT_ASSERT(save_path[save_path.size()-1] != TORRENT_SEPARATOR); crc.process_byte(TORRENT_SEPARATOR); } std::string const& p = m_paths[fe.path_index]; if (!p.empty()) { process_string_lowercase(crc, p.c_str(), p.size()); TORRENT_ASSERT(p[p.size()-1] != TORRENT_SEPARATOR); crc.process_byte(TORRENT_SEPARATOR); } process_string_lowercase(crc, fe.filename_ptr(), fe.filename_len()); } else { if (!save_path.empty()) { process_string_lowercase(crc, save_path.c_str(), save_path.size()); TORRENT_ASSERT(save_path[save_path.size()-1] != TORRENT_SEPARATOR); crc.process_byte(TORRENT_SEPARATOR); } process_string_lowercase(crc, m_name.c_str(), m_name.size()); TORRENT_ASSERT(m_name.size() > 0); TORRENT_ASSERT(m_name[m_name.size()-1] != TORRENT_SEPARATOR); crc.process_byte(TORRENT_SEPARATOR); std::string const& p = m_paths[fe.path_index]; if (!p.empty()) { process_string_lowercase(crc, p.c_str(), p.size()); TORRENT_ASSERT(p.size() > 0); TORRENT_ASSERT(p[p.size()-1] != TORRENT_SEPARATOR); crc.process_byte(TORRENT_SEPARATOR); } process_string_lowercase(crc, fe.filename_ptr(), fe.filename_len()); } return crc.checksum(); } std::string file_storage::file_path(int index, std::string const& save_path) const { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); internal_file_entry const& fe = m_files[index]; std::string ret; // -2 means this is an absolute path filename if (fe.path_index == -2) { ret.assign(fe.filename_ptr(), fe.filename_len()); } else if (fe.path_index == -1) { // -1 means no path ret.reserve(save_path.size() + fe.filename_len() + 1); ret.assign(save_path); append_path(ret, fe.filename_ptr(), fe.filename_len()); } else if (fe.no_root_dir) { std::string const& p = m_paths[fe.path_index]; ret.reserve(save_path.size() + p.size() + fe.filename_len() + 2); ret.assign(save_path); append_path(ret, p); append_path(ret, fe.filename_ptr(), fe.filename_len()); } else { std::string const& p = m_paths[fe.path_index]; ret.reserve(save_path.size() + m_name.size() + p.size() + fe.filename_len() + 3); ret.assign(save_path); append_path(ret, m_name); append_path(ret, p); append_path(ret, fe.filename_ptr(), fe.filename_len()); } // a single return statement, just to make NRVO more likely to kick in return ret; } std::string file_storage::file_name(int index) const { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); internal_file_entry const& fe = m_files[index]; return fe.filename(); } boost::int64_t file_storage::file_size(int index) const { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); return m_files[index].size; } bool file_storage::pad_file_at(int index) const { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); return m_files[index].pad_file; } boost::int64_t file_storage::file_offset(int index) const { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); return m_files[index].offset; } int file_storage::file_flags(int index) const { internal_file_entry const& fe = m_files[index]; return (fe.pad_file ? flag_pad_file : 0) | (fe.hidden_attribute ? flag_hidden : 0) | (fe.executable_attribute ? flag_executable : 0) | (fe.symlink_attribute ? flag_symlink : 0); } bool file_storage::file_absolute_path(int index) const { internal_file_entry const& fe = m_files[index]; return fe.path_index == -2; } #ifndef TORRENT_NO_DEPRECATE void file_storage::set_file_base(int index, boost::int64_t off) { TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); if (int(m_file_base.size()) <= index) m_file_base.resize(index + 1, 0); m_file_base[index] = off; } boost::int64_t file_storage::file_base_deprecated(int index) const { if (index >= int(m_file_base.size())) return 0; return m_file_base[index]; } boost::int64_t file_storage::file_base(int index) const { if (index >= int(m_file_base.size())) return 0; return m_file_base[index]; } sha1_hash file_storage::hash(internal_file_entry const& fe) const { int index = &fe - &m_files[0]; if (index >= int(m_file_hashes.size())) return sha1_hash(0); return sha1_hash(m_file_hashes[index]); } std::string const& file_storage::symlink(internal_file_entry const& fe) const { TORRENT_ASSERT_PRECOND(fe.symlink_index < int(m_symlinks.size())); return m_symlinks[fe.symlink_index]; } time_t file_storage::mtime(internal_file_entry const& fe) const { int index = &fe - &m_files[0]; if (index >= int(m_mtime.size())) return 0; return m_mtime[index]; } int file_storage::file_index(internal_file_entry const& fe) const { int index = &fe - &m_files[0]; TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); return index; } void file_storage::set_file_base(internal_file_entry const& fe, boost::int64_t off) { int index = &fe - &m_files[0]; TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); if (int(m_file_base.size()) <= index) m_file_base.resize(index + 1, 0); m_file_base[index] = off; } boost::int64_t file_storage::file_base(internal_file_entry const& fe) const { int index = &fe - &m_files[0]; if (index >= int(m_file_base.size())) return 0; return m_file_base[index]; } std::string file_storage::file_path(internal_file_entry const& fe , std::string const& save_path) const { int index = &fe - &m_files[0]; return file_path(index, save_path); } std::string file_storage::file_name(internal_file_entry const& fe) const { return fe.filename(); } boost::int64_t file_storage::file_size(internal_file_entry const& fe) const { return fe.size; } bool file_storage::pad_file_at(internal_file_entry const& fe) const { return fe.pad_file; } boost::int64_t file_storage::file_offset(internal_file_entry const& fe) const { return fe.offset; } file_entry file_storage::at(file_storage::iterator i) const { return at_deprecated(i - m_files.begin()); } #endif // TORRENT_NO_DEPRECATE void file_storage::reorder_file(int index, int dst) { TORRENT_ASSERT(index < int(m_files.size())); TORRENT_ASSERT(dst < int(m_files.size())); TORRENT_ASSERT(dst < index); std::iter_swap(m_files.begin() + index, m_files.begin() + dst); if (!m_mtime.empty()) { TORRENT_ASSERT(m_mtime.size() == m_files.size()); if (int(m_mtime.size()) < index) m_mtime.resize(index+1, 0); std::iter_swap(m_mtime.begin() + dst, m_mtime.begin() + index); } if (!m_file_hashes.empty()) { TORRENT_ASSERT(m_file_hashes.size() == m_files.size()); if (int(m_file_hashes.size()) < index) m_file_hashes.resize(index + 1, NULL); std::iter_swap(m_file_hashes.begin() + dst, m_file_hashes.begin() + index); } #ifndef TORRENT_NO_DEPRECATE if (!m_file_base.empty()) { TORRENT_ASSERT(m_file_base.size() == m_files.size()); if (int(m_file_base.size()) < index) m_file_base.resize(index + 1, 0); std::iter_swap(m_file_base.begin() + dst, m_file_base.begin() + index); } #endif // TORRENT_DEPRECATED } void file_storage::optimize(int pad_file_limit, int alignment , bool tail_padding) { if (alignment == -1) alignment = m_piece_length; boost::int64_t off = 0; int padding_file = 0; for (std::vector::iterator i = m_files.begin(); i != m_files.end(); ++i) { if ((off % alignment) == 0) { // this file position is aligned, pick the largest // available file to put here std::vector::iterator best_match = std::max_element(i, m_files.end() , &compare_file_entry_size); if (best_match != i) { int const index = best_match - m_files.begin(); int const cur_index = i - m_files.begin(); reorder_file(index, cur_index); i = m_files.begin() + cur_index; } } else if (pad_file_limit >= 0 && i->size > boost::uint32_t(pad_file_limit) && i->pad_file == false) { // if we have pad files enabled, and this file is // not piece-aligned and the file size exceeds the // limit, and it's not a padding file itself. // so add a padding file in front of it int const pad_size = alignment - (off % alignment); // find the largest file that fits in pad_size std::vector::iterator best_match = m_files.end(); // if pad_file_limit is 0, it means all files are padded, there's // no point in trying to find smaller files to use as filling if (pad_file_limit > 0) { for (std::vector::iterator j = i+1; j < m_files.end(); ++j) { if (j->size > boost::uint32_t(pad_size)) continue; if (best_match == m_files.end() || j->size > best_match->size) best_match = j; } if (best_match != m_files.end()) { // we found one // We cannot have found i, because i->size > pad_file_limit // which is forced to be no less than alignment. We only // look for files <= pad_size, which never is greater than // alignment TORRENT_ASSERT(best_match != i); int index = best_match - m_files.begin(); int cur_index = i - m_files.begin(); reorder_file(index, cur_index); i = m_files.begin() + cur_index; i->offset = off; off += i->size; continue; } } // we could not find a file that fits in pad_size // add a padding file // note that i will be set to point to the // new pad file. Once we're done adding it, we need // to increment i to point to the current file again // first add the pad file to the end of the file list // then swap it in place. This minimizes the amount // of copying of internal_file_entry, which is somewhat // expensive (until we have move semantics) add_pad_file(pad_size, i, off, padding_file); TORRENT_ASSERT((off % alignment) == 0); continue; } i->offset = off; off += i->size; if (tail_padding && i->size > boost::uint32_t(pad_file_limit) && (off % alignment) != 0) { // skip the file we just put in place, so we put the pad // file after it ++i; // tail-padding is enabled, and the offset after this file is not // aligned. The last file must be padded too, in order to match an // equivalent tail-padded file. add_pad_file(alignment - (off % alignment), i, off, padding_file); TORRENT_ASSERT((off % alignment) == 0); if (i == m_files.end()) break; } } m_total_size = off; } void file_storage::add_pad_file(int size , std::vector::iterator& i , boost::int64_t& offset , int& pad_file_counter) { int const cur_index = i - m_files.begin(); int const index = m_files.size(); m_files.push_back(internal_file_entry()); ++m_num_files; internal_file_entry& e = m_files.back(); // i may have been invalidated, refresh it i = m_files.begin() + cur_index; e.size = size; e.offset = offset; e.path_index = get_or_add_path(".pad", 4); char name[15]; snprintf(name, sizeof(name), "%d", pad_file_counter); e.set_name(name); e.pad_file = true; offset += size; ++pad_file_counter; if (!m_mtime.empty()) m_mtime.resize(index + 1, 0); if (!m_file_hashes.empty()) m_file_hashes.resize(index + 1, NULL); #ifndef TORRENT_NO_DEPRECATE if (!m_file_base.empty()) m_file_base.resize(index + 1, 0); #endif if (index != cur_index) reorder_file(index, cur_index); } void file_storage::unload() { std::vector().swap(m_files); std::vector().swap(m_file_hashes); std::vector().swap(m_symlinks); std::vector().swap(m_mtime); #ifndef TORRENT_NO_DEPRECATE std::vector().swap(m_file_base); #endif std::vector().swap(m_paths); } } libtorrent-rasterbar-1.1.13/src/fingerprint.cpp000066400000000000000000000061251351156116000215710ustar00rootroot00000000000000/* Copyright (c) 2016-2018, 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/fingerprint.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { namespace { char version_to_char(int const v) { if (v >= 0 && v < 10) return char('0' + v); else if (v >= 10) return char('A' + (v - 10)); TORRENT_ASSERT(false); return '0'; } } // anonymous namespace std::string generate_fingerprint(std::string name, int const major , int const minor , int const revision , int const tag) { TORRENT_ASSERT_PRECOND(major >= 0); TORRENT_ASSERT_PRECOND(minor >= 0); TORRENT_ASSERT_PRECOND(revision >= 0); TORRENT_ASSERT_PRECOND(tag >= 0); TORRENT_ASSERT_PRECOND(name.size() == 2); if (name.size() < 2) name = "--"; std::string ret; ret.resize(8); ret[0] = '-'; ret[1] = name[0]; ret[2] = name[1]; ret[3] = version_to_char(major); ret[4] = version_to_char(minor); ret[5] = version_to_char(revision); ret[6] = version_to_char(tag); ret[7] = '-'; return ret; } 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) { TORRENT_ASSERT(id_string); TORRENT_ASSERT(major >= 0); TORRENT_ASSERT(minor >= 0); TORRENT_ASSERT(revision >= 0); TORRENT_ASSERT(tag >= 0); TORRENT_ASSERT(std::strlen(id_string) == 2); name[0] = id_string[0]; name[1] = id_string[1]; } #ifndef TORRENT_NO_DEPRECATE std::string fingerprint::to_string() const { return generate_fingerprint(std::string(name, 2), major_version, minor_version , revision_version, tag_version); } #endif // TORRENT_NO_DEPRECATE } libtorrent-rasterbar-1.1.13/src/gzip.cpp000066400000000000000000000171321351156116000202130ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/assert.hpp" #include "libtorrent/puff.hpp" #include "libtorrent/gzip.hpp" #include #include namespace { enum { FTEXT = 0x01, FHCRC = 0x02, FEXTRA = 0x04, FNAME = 0x08, FCOMMENT = 0x10, FRESERVED = 0xe0, GZIP_MAGIC0 = 0x1f, GZIP_MAGIC1 = 0x8b }; } namespace libtorrent { struct gzip_error_category : boost::system::error_category { virtual const char* name() const BOOST_SYSTEM_NOEXCEPT; virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT; virtual boost::system::error_condition default_error_condition(int ev) const BOOST_SYSTEM_NOEXCEPT { return boost::system::error_condition(ev, *this); } }; const char* gzip_error_category::name() const BOOST_SYSTEM_NOEXCEPT { return "gzip error"; } std::string gzip_error_category::message(int ev) const BOOST_SYSTEM_NOEXCEPT { static char const* msgs[] = { "no error", "invalid gzip header", "inflated data too large", "available inflate data did not terminate", "output space exhausted before completing inflate", "invalid block type (type == 3)", "stored block length did not match one's complement", "dynamic block code description: too many length or distance codes", "dynamic block code description: code lengths codes incomplete", "dynamic block code description: repeat lengths with no first length", "dynamic block code description: repeat more than specified lengths", "dynamic block code description: invalid literal/length code lengths", "dynamic block code description: invalid distance code lengths", "invalid literal/length or distance code in fixed or dynamic block", "distance is too far back in fixed or dynamic block", "unknown gzip error", }; if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0]))) return "Unknown error"; return msgs[ev]; } boost::system::error_category& gzip_category() { static gzip_error_category category; return category; } #ifndef TORRENT_NO_DEPRECATE boost::system::error_category& get_gzip_category() { return gzip_category(); } #endif namespace gzip_errors { boost::system::error_code make_error_code(error_code_enum e) { return boost::system::error_code(e, gzip_category()); } } namespace { // returns -1 if gzip header is invalid or the header size in bytes int gzip_header(const char* buf, int size) { TORRENT_ASSERT(buf != 0); const unsigned char* buffer = reinterpret_cast(buf); const int total_size = size; // gzip is defined in https://tools.ietf.org/html/rfc1952 // The zip header cannot be shorter than 10 bytes if (size < 10 || buf == 0) 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 // we only support deflate if (method != 8 || (flags & FRESERVED) != 0) return -1; // skip time, xflags, OS code. The first 10 bytes of the header: // +---+---+---+---+---+---+---+---+---+---+ // |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->) // +---+---+---+---+---+---+---+---+---+---+ 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; } } // anonymous namespace TORRENT_EXTRA_EXPORT void inflate_gzip( char const* in , int size , std::vector& buffer , int maximum_size , error_code& ec) { ec.clear(); TORRENT_ASSERT(maximum_size > 0); int header_len = gzip_header(in, size); if (header_len < 0) { ec = gzip_errors::invalid_gzip_header; return; } // start off with 4 kilobytes and grow // if needed unsigned long destlen = 4096; int ret = 0; unsigned long srclen = size - header_len; in += header_len; do { TORRENT_TRY { buffer.resize(destlen); } TORRENT_CATCH(std::exception&) { ec = errors::no_memory; return; } ret = puff(reinterpret_cast(&buffer[0]), &destlen , reinterpret_cast(in), &srclen); // if the destination buffer wasn't large enough, double its // size and try again. Unless it's already at its max, in which // case we fail if (ret == 1) // 1: output space exhausted before completing inflate { if (destlen == boost::uint32_t(maximum_size)) { ec = gzip_errors::inflated_data_too_large; return; } destlen *= 2; if (destlen > boost::uint32_t(maximum_size)) destlen = maximum_size; } } while (ret == 1); if (ret != 0) { switch (ret) { case 2: ec = gzip_errors::data_did_not_terminate; return; case 1: ec = gzip_errors::space_exhausted; return; case -1: ec = gzip_errors::invalid_block_type; return; case -2: ec = gzip_errors::invalid_stored_block_length; return; case -3: ec = gzip_errors::too_many_length_or_distance_codes; return; case -4: ec = gzip_errors::code_lengths_codes_incomplete; return; case -5: ec = gzip_errors::repeat_lengths_with_no_first_length; return; case -6: ec = gzip_errors::repeat_more_than_specified_lengths; return; case -7: ec = gzip_errors::invalid_literal_length_code_lengths; return; case -8: ec = gzip_errors::invalid_distance_code_lengths; return; case -9: ec = gzip_errors::invalid_literal_code_in_block; return; case -10: ec = gzip_errors::distance_too_far_back_in_block; return; default: ec = gzip_errors::unknown_gzip_error; return; } } if (destlen > buffer.size()) { ec = gzip_errors::unknown_gzip_error; return; } buffer.resize(destlen); } } libtorrent-rasterbar-1.1.13/src/hasher.cpp000066400000000000000000000075601351156116000205200ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/hasher.hpp" #include "libtorrent/sha1.hpp" namespace libtorrent { hasher::hasher() { #ifdef TORRENT_USE_GCRYPT gcry_md_open(&m_context, GCRY_MD_SHA1, 0); #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_Init(&m_context); #elif defined TORRENT_USE_OPENSSL SHA1_Init(&m_context); #else SHA1_init(&m_context); #endif } hasher::hasher(const char* data, int len) { TORRENT_ASSERT(data != 0); TORRENT_ASSERT(len > 0); #ifdef TORRENT_USE_GCRYPT gcry_md_open(&m_context, GCRY_MD_SHA1, 0); gcry_md_write(m_context, data, len); #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_Init(&m_context); CC_SHA1_Update(&m_context, reinterpret_cast(data), len); #elif defined TORRENT_USE_OPENSSL SHA1_Init(&m_context); SHA1_Update(&m_context, reinterpret_cast(data), len); #else SHA1_init(&m_context); SHA1_update(&m_context, reinterpret_cast(data), len); #endif } #ifdef TORRENT_USE_GCRYPT hasher::hasher(hasher const& h) { gcry_md_copy(&m_context, h.m_context); } hasher& hasher::operator=(hasher const& h) { gcry_md_close(m_context); gcry_md_copy(&m_context, h.m_context); return *this; } #endif hasher& hasher::update(const char* data, int len) { TORRENT_ASSERT(data != 0); TORRENT_ASSERT(len > 0); #ifdef TORRENT_USE_GCRYPT gcry_md_write(m_context, data, len); #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_Update(&m_context, reinterpret_cast(data), len); #elif defined TORRENT_USE_OPENSSL SHA1_Update(&m_context, reinterpret_cast(data), len); #else SHA1_update(&m_context, reinterpret_cast(data), len); #endif return *this; } sha1_hash hasher::final() { sha1_hash digest; #ifdef TORRENT_USE_GCRYPT gcry_md_final(m_context); digest.assign((const char*)gcry_md_read(m_context, 0)); #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_Final(digest.begin(), &m_context); #elif defined TORRENT_USE_OPENSSL SHA1_Final(digest.begin(), &m_context); #else SHA1_final(digest.begin(), &m_context); #endif return digest; } void hasher::reset() { #ifdef TORRENT_USE_GCRYPT gcry_md_reset(m_context); #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_Init(&m_context); #elif defined TORRENT_USE_OPENSSL SHA1_Init(&m_context); #else SHA1_init(&m_context); #endif } hasher::~hasher() { #ifdef TORRENT_USE_GCRYPT gcry_md_close(m_context); #endif } } libtorrent-rasterbar-1.1.13/src/hex.cpp000066400000000000000000000056401351156116000200270ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/hex.hpp" namespace libtorrent { namespace detail { TORRENT_EXTRA_EXPORT int hex_to_int(char in) { if (in >= '0' && in <= '9') return int(in) - '0'; if (in >= 'A' && in <= 'F') return int(in) - 'A' + 10; if (in >= 'a' && in <= 'f') return int(in) - 'a' + 10; return -1; } TORRENT_EXTRA_EXPORT bool is_hex(char const *in, int len) { for (char const* end = in + len; in < end; ++in) { int t = hex_to_int(*in); if (t == -1) return false; } return true; } } // detail namespace TORRENT_EXPORT bool from_hex(char const *in, int len, char* out) { for (char const* end = in + len; in < end; ++in, ++out) { int t = detail::hex_to_int(*in); if (t == -1) return false; *out = t << 4; ++in; t = detail::hex_to_int(*in); if (t == -1) return false; *out |= t & 15; } return true; } extern const char hex_chars[]; const char hex_chars[] = "0123456789abcdef"; TORRENT_EXPORT std::string to_hex(std::string const& s) { std::string ret; for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { ret += hex_chars[boost::uint8_t(*i) >> 4]; ret += hex_chars[boost::uint8_t(*i) & 0xf]; } return ret; } TORRENT_EXPORT void to_hex(char const *in, int len, char* out) { for (char const* end = in + len; in < end; ++in) { *out++ = hex_chars[boost::uint8_t(*in) >> 4]; *out++ = hex_chars[boost::uint8_t(*in) & 0xf]; } *out = '\0'; } } libtorrent-rasterbar-1.1.13/src/http_connection.cpp000066400000000000000000000606451351156116000224470ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/http_connection.hpp" #include "libtorrent/aux_/escape_string.hpp" #include "libtorrent/instantiate_connection.hpp" #include "libtorrent/gzip.hpp" #include "libtorrent/parse_url.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/socket_type.hpp" // for async_shutdown #include "libtorrent/resolver_interface.hpp" #include "libtorrent/settings_pack.hpp" #include "libtorrent/aux_/time.hpp" #include "libtorrent/random.hpp" #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/debug.hpp" #endif #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #ifdef TORRENT_USE_OPENSSL #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { http_connection::http_connection(io_service& ios , resolver_interface& resolver , http_handler const& handler , bool bottled , int max_bottled_buffer_size , http_connect_handler const& ch , http_filter_handler const& fh #ifdef TORRENT_USE_OPENSSL , ssl::context* ssl_ctx #endif ) : m_next_ep(0) , m_sock(ios) #ifdef TORRENT_USE_OPENSSL , m_ssl_ctx(ssl_ctx) , m_own_ssl_context(false) #endif #if TORRENT_USE_I2P , m_i2p_conn(0) #endif , m_resolver(resolver) , m_handler(handler) , m_connect_handler(ch) , m_filter_handler(fh) , m_timer(ios) , m_read_timeout(seconds(5)) , m_completion_timeout(seconds(5)) , m_limiter_timer(ios) , m_last_receive(aux::time_now()) , m_start_time(aux::time_now()) , m_read_pos(0) , m_redirects(5) , m_max_bottled_buffer_size(max_bottled_buffer_size) , m_rate_limit(0) , m_download_quota(0) , m_priority(0) , m_resolve_flags(0) , m_port(0) , m_bottled(bottled) , m_called(false) , m_limiter_timer_active(false) , m_ssl(false) , m_abort(false) , m_connecting(false) { TORRENT_ASSERT(!m_handler.empty()); } http_connection::~http_connection() { #ifdef TORRENT_USE_OPENSSL if (m_own_ssl_context) delete m_ssl_ctx; #endif } void http_connection::get(std::string const& url, time_duration timeout, int prio , aux::proxy_settings const* ps, int handle_redirects, std::string const& user_agent , boost::optional
bind_addr, int resolve_flags, std::string const& auth_ #if TORRENT_USE_I2P , i2p_connection* i2p_conn #endif ) { m_user_agent = user_agent; m_resolve_flags = resolve_flags; std::string protocol; std::string auth; std::string hostname; std::string path; error_code ec; int port; boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url, ec); if (auth.empty()) auth = auth_; m_auth = auth; int default_port = protocol == "https" ? 443 : 80; if (port == -1) port = default_port; // keep ourselves alive even if the callback function // deletes this object boost::shared_ptr me(shared_from_this()); if (ec) { lt::get_io_service(m_timer).post(boost::bind(&http_connection::callback , me, ec, static_cast(NULL), 0)); return; } if (protocol != "http" #ifdef TORRENT_USE_OPENSSL && protocol != "https" #endif ) { error_code err(errors::unsupported_url_protocol); lt::get_io_service(m_timer).post(boost::bind(&http_connection::callback , me, err, static_cast(NULL), 0)); return; } TORRENT_ASSERT(prio >= 0 && prio < 3); bool ssl = false; if (protocol == "https") ssl = true; std::stringstream request; // exclude ssl here, because SSL assumes CONNECT support in the // proxy and is handled at the lower layer if (ps && (ps->type == settings_pack::http || ps->type == settings_pack::http_pw) && !ssl) { // if we're using an http proxy and not an ssl // connection, just do a regular http proxy request request << "GET " << url << " HTTP/1.1\r\n"; if (ps->type == settings_pack::http_pw) request << "Proxy-Authorization: Basic " << base64encode( ps->username + ":" + ps->password) << "\r\n"; hostname = ps->hostname; port = ps->port; request << "Host: " << hostname; if (port != default_port) request << ":" << port << "\r\n"; else request << "\r\n"; } else { request << "GET " << path << " HTTP/1.1\r\nHost: " << hostname; if (port != default_port) request << ":" << port << "\r\n"; else request << "\r\n"; } // request << "Accept: */*\r\n"; if (!m_user_agent.empty()) request << "User-Agent: " << m_user_agent << "\r\n"; if (m_bottled) request << "Accept-Encoding: gzip\r\n"; if (!auth.empty()) request << "Authorization: Basic " << base64encode(auth) << "\r\n"; request << "Connection: close\r\n\r\n"; m_sendbuffer.assign(request.str()); m_url = url; start(hostname, port, timeout, prio , ps, ssl, handle_redirects, bind_addr, m_resolve_flags #if TORRENT_USE_I2P , i2p_conn #endif ); } void http_connection::start(std::string const& hostname, int port , time_duration timeout, int prio, aux::proxy_settings const* ps, bool ssl , int handle_redirects , boost::optional
bind_addr , int resolve_flags #if TORRENT_USE_I2P , i2p_connection* i2p_conn #endif ) { TORRENT_ASSERT(prio >= 0 && prio < 3); m_redirects = handle_redirects; m_resolve_flags = resolve_flags; if (ps) m_proxy = *ps; // keep ourselves alive even if the callback function // deletes this object boost::shared_ptr me(shared_from_this()); m_completion_timeout = timeout; m_read_timeout = seconds(5); if (m_read_timeout < timeout / 5) m_read_timeout = timeout / 5; error_code ec; m_timer.expires_from_now((std::min)( m_read_timeout, m_completion_timeout), ec); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_timeout"); #endif m_timer.async_wait(boost::bind(&http_connection::on_timeout , boost::weak_ptr(me), _1)); m_called = false; m_parser.reset(); m_recvbuffer.clear(); m_read_pos = 0; m_priority = prio; if (ec) { lt::get_io_service(m_timer).post(boost::bind(&http_connection::callback , me, ec, static_cast(NULL), 0)); return; } if (m_sock.is_open() && m_hostname == hostname && m_port == port && m_ssl == ssl && m_bind_addr == bind_addr) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_write"); #endif async_write(m_sock, boost::asio::buffer(m_sendbuffer) , boost::bind(&http_connection::on_write, me, _1)); } else { m_ssl = ssl; m_bind_addr = bind_addr; error_code err; if (m_sock.is_open()) m_sock.close(err); aux::proxy_settings const* proxy = ps; #if TORRENT_USE_I2P bool is_i2p = false; char const* top_domain = strrchr(hostname.c_str(), '.'); aux::proxy_settings i2p_proxy; if (top_domain && strcmp(top_domain, ".i2p") == 0 && i2p_conn) { // this is an i2p name, we need to use the sam connection // to do the name lookup is_i2p = true; m_i2p_conn = i2p_conn; // quadruple the timeout for i2p destinations // because i2p is sloooooow m_completion_timeout *= 4; m_read_timeout *= 4; if (i2p_conn->proxy().type != settings_pack::i2p_proxy) { lt::get_io_service(m_timer).post(boost::bind(&http_connection::callback , me, error_code(errors::no_i2p_router), static_cast(NULL), 0)); return; } i2p_proxy = i2p_conn->proxy(); proxy = &i2p_proxy; } #endif // in this case, the upper layer is assumed to have taken // care of the proxying already. Don't instantiate the socket // with this proxy if (proxy && (proxy->type == settings_pack::http || proxy->type == settings_pack::http_pw) && !ssl) { proxy = 0; } aux::proxy_settings null_proxy; void* userdata = 0; #ifdef TORRENT_USE_OPENSSL if (m_ssl) { if (m_ssl_ctx == 0) { m_ssl_ctx = new (std::nothrow) ssl::context(ssl::context::sslv23_client); if (m_ssl_ctx) { m_own_ssl_context = true; m_ssl_ctx->set_verify_mode(ssl::context::verify_none, ec); if (ec) { lt::get_io_service(m_timer).post(boost::bind(&http_connection::callback , me, ec, static_cast(NULL), 0)); return; } } } userdata = m_ssl_ctx; } #endif // assume this is not a tracker connection. Tracker connections that // shouldn't be subject to the proxy should pass in NULL as the proxy // pointer. instantiate_connection(lt::get_io_service(m_timer) , proxy ? *proxy : null_proxy, m_sock, userdata, NULL, false, false); if (m_bind_addr) { m_sock.open(m_bind_addr->is_v4()?tcp::v4():tcp::v6(), ec); m_sock.bind(tcp::endpoint(*m_bind_addr, 0), ec); if (ec) { lt::get_io_service(m_timer).post(boost::bind(&http_connection::callback , me, ec, static_cast(NULL), 0)); return; } } setup_ssl_hostname(m_sock, hostname, ec); if (ec) { lt::get_io_service(m_timer).post(boost::bind(&http_connection::callback , me, ec, static_cast(NULL), 0)); return; } m_endpoints.clear(); m_next_ep = 0; #if TORRENT_USE_I2P if (is_i2p) { if (hostname.length() < 516) // Base64 encoded destination with optional .i2p { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_i2p_resolve"); #endif i2p_conn->async_name_lookup(hostname.c_str(), boost::bind(&http_connection::on_i2p_resolve , me, _1, _2)); } else connect_i2p_tracker(hostname.c_str()); } else #endif if (ps && ps->proxy_hostnames && (ps->type == settings_pack::socks5 || ps->type == settings_pack::socks5_pw)) { m_hostname = hostname; m_port = port; m_endpoints.push_back(tcp::endpoint(address(), port)); connect(); } else { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_resolve"); #endif m_resolver.async_resolve(hostname, m_resolve_flags , boost::bind(&http_connection::on_resolve , me, _1, _2)); } m_hostname = hostname; m_port = port; } } void http_connection::on_timeout(boost::weak_ptr p , error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("http_connection::on_timeout"); #endif boost::shared_ptr c = p.lock(); if (!c) return; if (e == boost::asio::error::operation_aborted) return; if (c->m_abort) return; time_point now = clock_type::now(); if (c->m_start_time + c->m_completion_timeout <= now || c->m_last_receive + c->m_read_timeout <= now) { // the connection timed out. If we have more endpoints to try, just // close this connection. The on_connect handler will try the next // endpoint in the list. if (c->m_next_ep < c->m_endpoints.size()) { error_code ec; c->m_sock.close(ec); if (!c->m_connecting) c->connect(); c->m_last_receive = now; c->m_start_time = c->m_last_receive; } else { c->callback(boost::asio::error::timed_out); return; } } else { if (!c->m_sock.is_open()) return; } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_timeout"); #endif error_code ec; c->m_timer.expires_at((std::min)( c->m_last_receive + c->m_read_timeout , c->m_start_time + c->m_completion_timeout), ec); c->m_timer.async_wait(boost::bind(&http_connection::on_timeout, p, _1)); } void http_connection::close(bool force) { if (m_abort) return; error_code ec; if (force) m_sock.close(ec); else async_shutdown(m_sock, shared_from_this()); m_timer.cancel(ec); m_limiter_timer.cancel(ec); m_hostname.clear(); m_port = 0; m_handler.clear(); m_abort = true; } #if TORRENT_USE_I2P void http_connection::connect_i2p_tracker(char const* destination) { TORRENT_ASSERT(m_sock.get()); #ifdef TORRENT_USE_OPENSSL TORRENT_ASSERT(m_ssl == false); #endif m_sock.get()->set_destination(destination); m_sock.get()->set_command(i2p_stream::cmd_connect); m_sock.get()->set_session_id(m_i2p_conn->session_id()); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_connect"); #endif TORRENT_ASSERT(!m_connecting); m_connecting = true; m_sock.async_connect(tcp::endpoint(), boost::bind(&http_connection::on_connect , shared_from_this(), _1)); } void http_connection::on_i2p_resolve(error_code const& e, char const* destination) { #if defined TORRENT_ASIO_DEBUGGING complete_async("http_connection::on_i2p_resolve"); #endif if (e) { callback(e); return; } connect_i2p_tracker(destination); } #endif void http_connection::on_resolve(error_code const& e , std::vector
const& addresses) { #if defined TORRENT_ASIO_DEBUGGING complete_async("http_connection::on_resolve"); #endif if (e) { callback(e); return; } TORRENT_ASSERT(!addresses.empty()); for (std::vector
::const_iterator i = addresses.begin() , end(addresses.end()); i != end; ++i) m_endpoints.push_back(tcp::endpoint(*i, m_port)); if (m_filter_handler) m_filter_handler(*this, m_endpoints); if (m_endpoints.empty()) { close(); return; } std::random_shuffle(m_endpoints.begin(), m_endpoints.end(), randint); // The following statement causes msvc to crash (ICE). Since it's not // necessary in the vast majority of cases, just ignore the endpoint // order for windows #if !defined _MSC_VER || _MSC_VER > 1310 // sort the endpoints so that the ones with the same IP version as our // bound listen socket are first. So that when contacting a tracker, // we'll talk to it from the same IP that we're listening on if (m_bind_addr) std::partition(m_endpoints.begin(), m_endpoints.end() , boost::bind(&address::is_v4, boost::bind(&tcp::endpoint::address, _1)) == m_bind_addr->is_v4()); #endif connect(); } void http_connection::connect() { TORRENT_ASSERT(m_next_ep < m_endpoints.size()); boost::shared_ptr me(shared_from_this()); if (m_proxy.proxy_hostnames && (m_proxy.type == settings_pack::socks5 || m_proxy.type == settings_pack::socks5_pw)) { // test to see if m_hostname really just is an IP (and not a hostname). If it // is, ec will be represent "success". If so, don't set it as the socks5 // hostname, just connect to the IP error_code ec; address adr = address::from_string(m_hostname, ec); if (ec) { // we're using a socks proxy and we're resolving // hostnames through it #ifdef TORRENT_USE_OPENSSL if (m_ssl) { TORRENT_ASSERT(m_sock.get >()); m_sock.get >()->next_layer().set_dst_name(m_hostname); } else #endif { TORRENT_ASSERT(m_sock.get()); m_sock.get()->set_dst_name(m_hostname); } } else { m_endpoints[0].address(adr); } } TORRENT_ASSERT(m_next_ep < m_endpoints.size()); if (m_next_ep >= m_endpoints.size()) return; tcp::endpoint target_address = m_endpoints[m_next_ep]; ++m_next_ep; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_connect"); #endif TORRENT_ASSERT(!m_connecting); m_connecting = true; m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect , shared_from_this(), _1)); } void http_connection::on_connect(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("http_connection::on_connect"); #endif TORRENT_ASSERT(m_connecting); m_connecting = false; m_last_receive = clock_type::now(); m_start_time = m_last_receive; if (!e) { if (m_connect_handler) m_connect_handler(*this); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_write"); #endif async_write(m_sock, boost::asio::buffer(m_sendbuffer) , boost::bind(&http_connection::on_write, shared_from_this(), _1)); } else if (m_next_ep < m_endpoints.size() && !m_abort) { // The connection failed. Try the next endpoint in the list. error_code ec; m_sock.close(ec); connect(); } else { callback(e); } } void http_connection::callback(error_code e, char* data, int size) { if (m_bottled && m_called) return; std::vector buf; if (data && m_bottled && m_parser.header_finished()) { size = m_parser.collapse_chunk_headers(data, size); std::string const& encoding = m_parser.header("content-encoding"); if ((encoding == "gzip" || encoding == "x-gzip") && size > 0 && data) { error_code ec; inflate_gzip(data, size, buf, m_max_bottled_buffer_size, ec); if (ec) { if (m_handler) m_handler(ec, m_parser, data, size, *this); return; } size = int(buf.size()); data = size == 0 ? 0 : &buf[0]; } // if we completed the whole response, no need // to tell the user that the connection was closed by // the server or by us. Just clear any error if (m_parser.finished()) e.clear(); } m_called = true; error_code ec; m_timer.cancel(ec); if (m_handler) m_handler(e, m_parser, data, size, *this); } void http_connection::on_write(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("http_connection::on_write"); #endif if (e == boost::asio::error::operation_aborted) return; if (e) { callback(e); return; } if (m_abort) return; std::string().swap(m_sendbuffer); m_recvbuffer.resize(4096); int amount_to_read = m_recvbuffer.size() - m_read_pos; if (m_rate_limit > 0 && amount_to_read > m_download_quota) { amount_to_read = m_download_quota; if (m_download_quota == 0) { if (!m_limiter_timer_active) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_assign_bandwidth"); #endif on_assign_bandwidth(error_code()); } return; } } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_read"); #endif m_sock.async_read_some(boost::asio::buffer(&m_recvbuffer[0] + m_read_pos , amount_to_read) , boost::bind(&http_connection::on_read , shared_from_this(), _1, _2)); } void http_connection::on_read(error_code const& e , std::size_t bytes_transferred) { #if defined TORRENT_ASIO_DEBUGGING complete_async("http_connection::on_read"); #endif if (m_rate_limit) { m_download_quota -= bytes_transferred; TORRENT_ASSERT(m_download_quota >= 0); } if (e == boost::asio::error::operation_aborted) return; if (m_abort) return; // keep ourselves alive even if the callback function // deletes this object boost::shared_ptr me(shared_from_this()); // when using the asio SSL wrapper, it seems like // we get the shut_down error instead of EOF if (e == boost::asio::error::eof || e == boost::asio::error::shut_down) { error_code ec = boost::asio::error::eof; TORRENT_ASSERT(bytes_transferred == 0); char* data = 0; std::size_t size = 0; if (m_bottled && m_parser.header_finished()) { data = &m_recvbuffer[0] + m_parser.body_start(); size = m_parser.get_body().left(); } callback(ec, data, size); return; } if (e) { TORRENT_ASSERT(bytes_transferred == 0); callback(e); return; } m_read_pos += bytes_transferred; TORRENT_ASSERT(m_read_pos <= int(m_recvbuffer.size())); if (m_bottled || !m_parser.header_finished()) { libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0] , &m_recvbuffer[0] + m_read_pos); bool error = false; m_parser.incoming(rcv_buf, error); if (error) { // HTTP parse error error_code ec = errors::http_parse_error; callback(ec, 0, 0); return; } // having a nonempty path means we should handle redirects if (m_redirects && m_parser.header_finished()) { int code = m_parser.status_code(); if (is_redirect(code)) { // attempt a redirect std::string const& location = m_parser.header("location"); if (location.empty()) { // missing location header callback(error_code(errors::http_missing_location)); return; } error_code ec; // it would be nice to gracefully shut down SSL here // but then we'd have to do all the reconnect logic // in its handler. For now, just kill the connection. // async_shutdown(m_sock, shared_from_this()); m_sock.close(ec); std::string url = resolve_redirect_location(m_url, location); get(url, m_completion_timeout, m_priority, &m_proxy, m_redirects - 1 , m_user_agent, m_bind_addr, m_resolve_flags, m_auth #if TORRENT_USE_I2P , m_i2p_conn #endif ); return; } m_redirects = 0; } if (!m_bottled && m_parser.header_finished()) { if (m_read_pos > m_parser.body_start()) callback(e, &m_recvbuffer[0] + m_parser.body_start() , m_read_pos - m_parser.body_start()); m_read_pos = 0; m_last_receive = clock_type::now(); } else if (m_bottled && m_parser.finished()) { error_code ec; m_timer.cancel(ec); callback(e, &m_recvbuffer[0] + m_parser.body_start(), m_parser.get_body().left()); } } else { TORRENT_ASSERT(!m_bottled); callback(e, &m_recvbuffer[0], m_read_pos); m_read_pos = 0; m_last_receive = clock_type::now(); } // if we've hit the limit, double the buffer size if (int(m_recvbuffer.size()) == m_read_pos) m_recvbuffer.resize((std::min)(m_read_pos * 2, m_max_bottled_buffer_size)); if (m_read_pos == m_max_bottled_buffer_size) { // if we've reached the size limit, terminate the connection and // report the error callback(error_code(boost::system::errc::file_too_large, generic_category())); return; } int amount_to_read = m_recvbuffer.size() - m_read_pos; if (m_rate_limit > 0 && amount_to_read > m_download_quota) { amount_to_read = m_download_quota; if (m_download_quota == 0) { if (!m_limiter_timer_active) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_assign_bandwidth"); #endif on_assign_bandwidth(error_code()); } return; } } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_read"); #endif m_sock.async_read_some(boost::asio::buffer(&m_recvbuffer[0] + m_read_pos , amount_to_read) , boost::bind(&http_connection::on_read , me, _1, _2)); } void http_connection::on_assign_bandwidth(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("http_connection::on_assign_bandwidth"); #endif if ((e == boost::asio::error::operation_aborted && m_limiter_timer_active) || !m_sock.is_open()) { callback(boost::asio::error::eof); return; } m_limiter_timer_active = false; if (e) return; if (m_download_quota > 0) return; m_download_quota = m_rate_limit / 4; int amount_to_read = m_recvbuffer.size() - m_read_pos; if (amount_to_read > m_download_quota) amount_to_read = m_download_quota; if (!m_sock.is_open()) return; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_read"); #endif m_sock.async_read_some(boost::asio::buffer(&m_recvbuffer[0] + m_read_pos , amount_to_read) , boost::bind(&http_connection::on_read , shared_from_this(), _1, _2)); error_code ec; m_limiter_timer_active = true; m_limiter_timer.expires_from_now(milliseconds(250), ec); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_assign_bandwidth"); #endif m_limiter_timer.async_wait(boost::bind(&http_connection::on_assign_bandwidth , shared_from_this(), _1)); } void http_connection::rate_limit(int limit) { if (!m_sock.is_open()) return; if (!m_limiter_timer_active) { error_code ec; m_limiter_timer_active = true; m_limiter_timer.expires_from_now(milliseconds(250), ec); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("http_connection::on_assign_bandwidth"); #endif m_limiter_timer.async_wait(boost::bind(&http_connection::on_assign_bandwidth , shared_from_this(), _1)); } m_rate_limit = limit; } } libtorrent-rasterbar-1.1.13/src/http_parser.cpp000066400000000000000000000442361351156116000216020ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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 #include #include #include "libtorrent/config.hpp" #include "libtorrent/http_parser.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/parse_url.hpp" // for parse_url_components #include "libtorrent/aux_/escape_string.hpp" // for read_until #include "libtorrent/hex.hpp" using namespace libtorrent; namespace libtorrent { bool is_ok_status(int http_status) { return http_status == 206 // partial content || http_status == 200 // OK || (http_status >= 300 // redirect && http_status < 400); } bool is_redirect(int http_status) { return http_status >= 300 && http_status < 400; } std::string resolve_redirect_location(std::string referrer , std::string location) { if (location.empty()) return referrer; error_code ec; using boost::tuples::ignore; boost::tie(ignore, ignore, ignore, ignore, ignore) = parse_url_components(location, ec); // if location is a full URL, just return it if (!ec) return location; // otherwise it's likely to be just the path, or a relative path std::string url = referrer; if (location[0] == '/') { // it's an absolute path. replace the path component of // referrer with location. // first skip the url scheme of the referer std::size_t i = url.find("://"); // if the referrer doesn't appear to have a proper URL scheme // just return the location verbatim (and probably fail) if (i == std::string::npos) return location; // then skip the hostname and port, it's fine for this to fail, in // case the referrer doesn't have a path component, it's just the // url-scheme and hostname, in which case we just append the location i = url.find_first_of('/', i + 3); if (i != std::string::npos) url.resize(i); url += location; } else { // some web servers send out relative paths // in the location header. // remove the leaf filename // first skip the url scheme of the referer std::size_t start = url.find("://"); // the referrer is not a valid full URL if (start == std::string::npos) return location; std::size_t end = url.find_last_of('/'); // if the / we find is part of the scheme, there is no / in the path // component or hostname. if (end <= start + 2) end = std::string::npos; // if this fails, the referrer is just url-scheme and hostname. We can // just append the location to it. if (end != std::string::npos) url.resize(end); // however, we may still need to insert a '/' in case neither side // has one. We know the location doesn't start with a / already. // so, if the referrer doesn't end with one, add it. if ((url.empty() || url[url.size()-1] != '/')) url += '/'; url += location; } return url; } http_parser::~http_parser() {} http_parser::http_parser(int flags) : m_recv_pos(0) , m_content_length(-1) , m_range_start(-1) , m_range_end(-1) , m_recv_buffer(0, 0) , m_cur_chunk_end(-1) , m_status_code(-1) , m_chunk_header_size(0) , m_partial_chunk_header(0) , m_flags(flags) , m_body_start_pos(0) , m_state(read_status) , m_connection_close(false) , m_chunked_encoding(false) , m_finished(false) {} boost::tuple http_parser::incoming( buffer::const_interval recv_buffer, bool& error) { TORRENT_ASSERT(recv_buffer.left() >= m_recv_buffer.left()); boost::tuple ret(0, 0); int start_pos = m_recv_buffer.left(); // early exit if there's nothing new in the receive buffer if (start_pos == recv_buffer.left()) return ret; m_recv_buffer = recv_buffer; if (m_state == error_state) { error = true; return ret; } char const* pos = recv_buffer.begin + m_recv_pos; restart_response: if (m_state == read_status) { TORRENT_ASSERT(!m_finished); TORRENT_ASSERT(pos <= recv_buffer.end); 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) { boost::get<1>(ret) += m_recv_buffer.left() - start_pos; return ret; } if (newline == pos) { m_state = error_state; error = true; return ret; } char const* line_end = newline; if (pos != line_end && *(line_end - 1) == '\r') --line_end; char const* line = pos; ++newline; TORRENT_ASSERT(newline >= pos); int incoming = int(newline - pos); m_recv_pos += incoming; boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos); pos = newline; m_protocol = read_until(line, ' ', line_end); if (m_protocol.substr(0, 5) == "HTTP/") { m_status_code = atoi(read_until(line, ' ', line_end).c_str()); m_server_message = read_until(line, '\r', line_end); // HTTP 1.0 always closes the connection after // each request if (m_protocol == "HTTP/1.0") m_connection_close = true; } else { m_method = m_protocol; std::transform(m_method.begin(), m_method.end(), m_method.begin(), &to_lower); // the content length is assumed to be 0 for requests m_content_length = 0; m_protocol.clear(); m_path = read_until(line, ' ', line_end); m_protocol = read_until(line, ' ', line_end); m_status_code = 0; } m_state = read_header; start_pos = pos - recv_buffer.begin; } if (m_state == read_header) { TORRENT_ASSERT(!m_finished); TORRENT_ASSERT(pos <= recv_buffer.end); char const* newline = std::find(pos, recv_buffer.end, '\n'); std::string line; while (newline != recv_buffer.end && m_state == read_header) { // if the LF character is preceeded by a CR // charachter, don't copy it into the line string. char const* line_end = newline; if (pos != line_end && *(line_end - 1) == '\r') --line_end; line.assign(pos, line_end); ++newline; m_recv_pos += newline - pos; pos = newline; std::string::size_type separator = line.find(':'); if (separator == std::string::npos) { if (m_status_code == 100) { // for 100 Continue, we need to read another response header // before reading the body m_state = read_status; goto restart_response; } // this means we got a blank line, // the header is finished and the body // starts. m_state = read_body; // if this is a request (not a response) // we're done once we reach the end of the headers // if (!m_method.empty()) m_finished = true; // the HTTP header should always be < 2 GB TORRENT_ASSERT(m_recv_pos < INT_MAX); m_body_start_pos = int(m_recv_pos); break; } std::string name = line.substr(0, separator); std::transform(name.begin(), name.end(), name.begin(), &to_lower); ++separator; // skip whitespace while (separator < line.size() && (line[separator] == ' ' || line[separator] == '\t')) ++separator; std::string value = line.substr(separator, std::string::npos); m_header.insert(std::make_pair(name, value)); if (name == "content-length") { m_content_length = strtoll(value.c_str(), 0, 10); if (m_content_length < 0 || m_content_length == std::numeric_limits::max()) { m_state = error_state; error = true; return ret; } } else if (name == "connection") { m_connection_close = string_begins_no_case("close", value.c_str()); } else if (name == "content-range") { bool success = true; char const* ptr = value.c_str(); // apparently some web servers do not send the "bytes" // in their content-range. Don't treat it as an error // if we can't find it, just assume the byte counters // start immediately if (string_begins_no_case("bytes ", ptr)) ptr += 6; char* end; m_range_start = strtoll(ptr, &end, 10); if (m_range_start < 0 || m_range_start == std::numeric_limits::max()) { m_state = error_state; error = true; return ret; } if (end == ptr) success = false; else if (*end != '-') success = false; else { ptr = end + 1; m_range_end = strtoll(ptr, &end, 10); if (m_range_end < 0 || m_range_end == std::numeric_limits::max()) { m_state = error_state; error = true; return ret; } if (end == ptr) success = false; } if (!success || m_range_end < m_range_start) { m_state = error_state; error = true; return ret; } // the http range is inclusive m_content_length = m_range_end - m_range_start + 1; } else if (name == "transfer-encoding") { m_chunked_encoding = string_begins_no_case("chunked", value.c_str()); } TORRENT_ASSERT(m_recv_pos <= recv_buffer.left()); TORRENT_ASSERT(pos <= recv_buffer.end); newline = std::find(pos, recv_buffer.end, '\n'); } boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos); } if (m_state == read_body) { int incoming = recv_buffer.end - pos; if (m_chunked_encoding && (m_flags & dont_parse_chunks) == 0) { if (m_cur_chunk_end == -1) m_cur_chunk_end = m_body_start_pos; while (m_cur_chunk_end <= m_recv_pos + incoming && !m_finished && incoming > 0) { boost::int64_t payload = m_cur_chunk_end - m_recv_pos; if (payload > 0) { TORRENT_ASSERT(payload < INT_MAX); m_recv_pos += payload; boost::get<0>(ret) += int(payload); incoming -= int(payload); } buffer::const_interval buf(recv_buffer.begin + m_cur_chunk_end, recv_buffer.end); boost::int64_t chunk_size; int header_size; if (parse_chunk_header(buf, &chunk_size, &header_size)) { if (chunk_size < 0) { m_state = error_state; error = true; return ret; } if (chunk_size > 0) { std::pair chunk_range(m_cur_chunk_end + header_size , m_cur_chunk_end + header_size + chunk_size); m_chunked_ranges.push_back(chunk_range); } m_cur_chunk_end += header_size + chunk_size; if (chunk_size == 0) { m_finished = true; } header_size -= m_partial_chunk_header; m_partial_chunk_header = 0; // fprintf(stderr, "parse_chunk_header(%d, -> %d, -> %d) -> %d\n" // " incoming = %d\n m_recv_pos = %d\n m_cur_chunk_end = %d\n" // " content-length = %d\n" // , buf.left(), int(chunk_size), header_size, 1, incoming, int(m_recv_pos) // , m_cur_chunk_end, int(m_content_length)); } else { m_partial_chunk_header += incoming; header_size = incoming; // fprintf(stderr, "parse_chunk_header(%d, -> %d, -> %d) -> %d\n" // " incoming = %d\n m_recv_pos = %d\n m_cur_chunk_end = %d\n" // " content-length = %d\n" // , buf.left(), int(chunk_size), header_size, 0, incoming, int(m_recv_pos) // , m_cur_chunk_end, int(m_content_length)); } m_chunk_header_size += header_size; m_recv_pos += header_size; boost::get<1>(ret) += header_size; incoming -= header_size; } if (incoming > 0) { m_recv_pos += incoming; boost::get<0>(ret) += incoming; // incoming = 0; } } else { boost::int64_t payload_received = m_recv_pos - m_body_start_pos + incoming; if (payload_received > m_content_length && m_content_length >= 0) { TORRENT_ASSERT(m_content_length - m_recv_pos + m_body_start_pos < INT_MAX); incoming = int(m_content_length - m_recv_pos + m_body_start_pos); } TORRENT_ASSERT(incoming >= 0); m_recv_pos += incoming; boost::get<0>(ret) += incoming; } if (m_content_length >= 0 && !m_chunked_encoding && m_recv_pos - m_body_start_pos >= m_content_length) { m_finished = true; } } return ret; } bool http_parser::parse_chunk_header(buffer::const_interval buf , boost::int64_t* chunk_size, int* header_size) { TORRENT_ASSERT(buf.begin <= buf.end); char const* pos = buf.begin; // ignore one optional new-line. This is since each chunk // is terminated by a newline. we're likely to see one // before the actual header. if (pos < buf.end && pos[0] == '\r') ++pos; if (pos < buf.end && pos[0] == '\n') ++pos; if (pos == buf.end) return false; TORRENT_ASSERT(pos <= buf.end); char const* newline = std::find(pos, buf.end, '\n'); if (newline == buf.end) return false; ++newline; // the chunk header is a single line, a hex length of the // chunk followed by an optional semi-colon with a comment // in case the length is 0, the stream is terminated and // there are extra tail headers, which is terminated by an // empty line // first, read the chunk length boost::int64_t size = 0; for (char const* i = pos; i != newline; ++i) { if (*i == '\r') continue; if (*i == '\n') continue; if (*i == ';') break; int const digit = detail::hex_to_int(*i); if (digit < 0) { *chunk_size = -1; return true; } if (size >= std::numeric_limits::max() / 16) { *chunk_size = -1; return true; } size *= 16; size += digit; } *chunk_size = size; if (*chunk_size != 0) { *header_size = newline - buf.begin; // the newline is at least 1 byte, and the length-prefix is at least 1 // byte TORRENT_ASSERT(newline - buf.begin >= 2); return true; } // this is the terminator of the stream. Also read headers std::map tail_headers; pos = newline; newline = std::find(pos, buf.end, '\n'); std::string line; while (newline != buf.end) { // if the LF character is preceeded by a CR // charachter, don't copy it into the line string. char const* line_end = newline; if (pos != line_end && *(line_end - 1) == '\r') --line_end; line.assign(pos, line_end); ++newline; pos = newline; std::string::size_type separator = line.find(':'); if (separator == std::string::npos) { // this means we got a blank line, // the header is finished and the body // starts. *header_size = newline - buf.begin; // the newline alone is two bytes TORRENT_ASSERT(newline - buf.begin > 2); // we were successfull in parsing the headers. // add them to the headers in the parser for (std::map::const_iterator i = tail_headers.begin(); i != tail_headers.end(); ++i) m_header.insert(std::make_pair(i->first, i->second)); return true; } std::string name = line.substr(0, separator); std::transform(name.begin(), name.end(), name.begin(), &to_lower); ++separator; // skip whitespace while (separator < line.size() && (line[separator] == ' ' || line[separator] == '\t')) ++separator; std::string value = line.substr(separator, std::string::npos); tail_headers.insert(std::make_pair(name, value)); // fprintf(stderr, "tail_header: %s: %s\n", name.c_str(), value.c_str()); newline = std::find(pos, buf.end, '\n'); } return false; } buffer::const_interval http_parser::get_body() const { TORRENT_ASSERT(m_state == read_body); boost::int64_t last_byte = m_chunked_encoding && !m_chunked_ranges.empty() ? (std::min)(m_chunked_ranges.back().second, m_recv_pos) : m_content_length < 0 ? m_recv_pos : (std::min)(m_body_start_pos + m_content_length, m_recv_pos); TORRENT_ASSERT(last_byte >= m_body_start_pos); return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos , m_recv_buffer.begin + last_byte); } void http_parser::reset() { m_method.clear(); m_recv_pos = 0; m_body_start_pos = 0; m_status_code = -1; m_content_length = -1; m_range_start = -1; m_range_end = -1; m_finished = false; m_state = read_status; m_recv_buffer.begin = 0; m_recv_buffer.end = 0; m_header.clear(); m_chunked_encoding = false; m_chunked_ranges.clear(); m_cur_chunk_end = -1; m_chunk_header_size = 0; m_partial_chunk_header = 0; } int http_parser::collapse_chunk_headers(char* buffer, int size) const { if (!chunked_encoding()) return size; // go through all chunks and compact them // since we're bottled, and the buffer is our after all // it's OK to mutate it char* write_ptr = buffer; // the offsets in the array are from the start of the // buffer, not start of the body, so subtract the size // of the HTTP header from them int offset = body_start(); std::vector > const& c = chunks(); for (std::vector >::const_iterator i = c.begin() , end(c.end()); i != end; ++i) { TORRENT_ASSERT(i->second - i->first < INT_MAX); TORRENT_ASSERT(i->second - offset <= size); int len = int(i->second - i->first); if (i->first - offset + len > size) len = size - int(i->first) + offset; memmove(write_ptr, buffer + i->first - offset, len); write_ptr += len; } size = write_ptr - buffer; return size; } } libtorrent-rasterbar-1.1.13/src/http_seed_connection.cpp000066400000000000000000000364101351156116000234400ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/http_seed_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" #include "libtorrent/parse_url.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/hex.hpp" // for is_hex using boost::shared_ptr; using libtorrent::aux::session_impl; namespace libtorrent { http_seed_connection::http_seed_connection(peer_connection_args const& pack , web_seed_t& web) : web_connection_base(pack, web) , m_url(web.url) , m_web(&web) , m_response_left(0) , m_chunk_pos(0) , m_partial_chunk_header(0) { INVARIANT_CHECK; if (!m_settings.get_bool(settings_pack::report_web_seed_downloads)) ignore_stats(true); shared_ptr tor = pack.tor.lock(); TORRENT_ASSERT(tor); int blocks_per_piece = tor->torrent_file().piece_length() / tor->block_size(); // multiply with the blocks per piece since that many requests are // merged into one http request max_out_request_queue(m_settings.get_int(settings_pack::urlseed_pipeline_size) * blocks_per_piece); prefer_contiguous_blocks(blocks_per_piece); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CONNECT", "http_seed_connection"); #endif } void http_seed_connection::disconnect(error_code const& ec , operation_t op, int error) { if (is_disconnecting()) return; if (op == op_connect && m_web && !m_web->endpoints.empty()) { // we failed to connect to this IP. remove it so that the next attempt // uses the next IP in the list. m_web->endpoints.erase(m_web->endpoints.begin()); } boost::shared_ptr t = associated_torrent().lock(); peer_connection::disconnect(ec, op, error); if (t) t->disconnect_web_seed(this); } boost::optional http_seed_connection::downloading_piece_progress() const { if (m_requests.empty()) return boost::optional(); boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); piece_block_progress ret; peer_request const& pr = m_requests.front(); ret.piece_index = pr.piece; if (!m_parser.header_finished()) { ret.bytes_downloaded = 0; } else { int receive_buffer_size = m_recv_buffer.get().left() - m_parser.body_start(); // TODO: 1 in chunked encoding mode, this assert won't hold. // the chunk headers should be subtracted from the receive_buffer_size TORRENT_ASSERT_VAL(receive_buffer_size <= t->block_size(), receive_buffer_size); ret.bytes_downloaded = t->block_size() - receive_buffer_size; } // this is used to make sure that the block_index stays within // bounds. If the entire piece is downloaded, the block_index // would otherwise point to one past the end int correction = ret.bytes_downloaded ? -1 : 0; ret.block_index = (pr.start + ret.bytes_downloaded + correction) / t->block_size(); ret.full_block_bytes = t->block_size(); const int last_piece = t->torrent_file().num_pieces() - 1; if (ret.piece_index == last_piece && ret.block_index == t->torrent_file().piece_size(last_piece) / t->block_size()) ret.full_block_bytes = t->torrent_file().piece_size(last_piece) % t->block_size(); return ret; } void http_seed_connection::write_request(peer_request const& r) { INVARIANT_CHECK; boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(t->valid_metadata()); // http_seeds don't support requesting more than one piece // at a time TORRENT_ASSERT(r.length <= t->torrent_file().piece_size(r.piece)); std::string request; request.reserve(400); int size = r.length; const int block_size = t->block_size(); const int piece_size = t->torrent_file().piece_length(); peer_request pr; while (size > 0) { int request_offset = r.start + r.length - size; pr.start = request_offset % piece_size; pr.length = (std::min)(block_size, size); pr.piece = r.piece + request_offset / piece_size; m_requests.push_back(pr); size -= pr.length; } int proxy_type = m_settings.get_int(settings_pack::proxy_type); bool using_proxy = (proxy_type == settings_pack::http || proxy_type == settings_pack::http_pw) && !m_ssl; request += "GET "; request += using_proxy ? m_url : m_path; request += "?info_hash="; request += escape_string(reinterpret_cast(&t->torrent_file().info_hash()[0]), 20); request += "&piece="; request += to_string(r.piece).elems; // if we're requesting less than an entire piece we need to // add ranges if (r.start > 0 || r.length != t->torrent_file().piece_size(r.piece)) { request += "&ranges="; request += to_string(r.start).elems; request += "-"; // ranges are inclusive, just like HTTP request += to_string(r.start + r.length - 1).elems; } request += " HTTP/1.1\r\n"; add_headers(request, m_settings, using_proxy); request += "\r\n\r\n"; m_first_request = false; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "REQUEST", "%s", request.c_str()); #endif send_buffer(request.c_str(), request.size(), message_type_request); } // -------------------------- // RECEIVE DATA // -------------------------- void http_seed_connection::on_receive(error_code const& error , std::size_t bytes_transferred) { INVARIANT_CHECK; if (error) { received_bytes(0, bytes_transferred); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ERROR" , "http_seed_connection error: %s", error.message().c_str()); #endif return; } boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); for (;;) { buffer::const_interval recv_buffer = m_recv_buffer.get(); if (bytes_transferred == 0) break; TORRENT_ASSERT(recv_buffer.left() > 0); TORRENT_ASSERT(!m_requests.empty()); if (m_requests.empty()) { received_bytes(0, bytes_transferred); disconnect(errors::http_error, op_bittorrent, 2); return; } peer_request front_request = m_requests.front(); bool header_finished = m_parser.header_finished(); if (!header_finished) { bool parse_error = false; int protocol = 0; int payload = 0; boost::tie(payload, protocol) = m_parser.incoming(recv_buffer, parse_error); received_bytes(0, protocol); bytes_transferred -= protocol; #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS if (payload > front_request.length) payload = front_request.length; #endif if (parse_error) { received_bytes(0, bytes_transferred); disconnect(errors::http_parse_error, op_bittorrent, 2); return; } TORRENT_ASSERT(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); TORRENT_ASSERT(recv_buffer.left() <= m_recv_buffer.packet_size()); // this means the entire status line hasn't been received yet if (m_parser.status_code() == -1) { TORRENT_ASSERT(payload == 0); TORRENT_ASSERT(bytes_transferred == 0); break; } // if the status code is not one of the accepted ones, abort if (!is_ok_status(m_parser.status_code())) { int retry_time = atoi(m_parser.header("retry-after").c_str()); if (retry_time <= 0) retry_time = 5 * 60; // temporarily unavailable, retry later t->retry_web_seed(this, retry_time); std::string error_msg = to_string(m_parser.status_code()).elems + (" " + m_parser.message()); if (t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle(), url() , error_msg); } received_bytes(0, bytes_transferred); disconnect(error_code(m_parser.status_code(), http_category()), op_bittorrent, 1); return; } if (!m_parser.header_finished()) { TORRENT_ASSERT(payload == 0); TORRENT_ASSERT(bytes_transferred == 0); break; } } // we just completed reading the header if (!header_finished) { if (is_redirect(m_parser.status_code())) { // this means we got a redirection request // look for the location header std::string location = m_parser.header("location"); received_bytes(0, bytes_transferred); if (location.empty()) { // we should not try this server again. t->remove_web_seed(this, errors::missing_location, op_bittorrent, 2); return; } // add the redirected url and remove the current one t->add_web_seed(location, web_seed_entry::http_seed); t->remove_web_seed(this, errors::redirecting, op_bittorrent, 2); return; } std::string const& server_version = m_parser.header("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 += ")"; } m_response_left = atol(m_parser.header("content-length").c_str()); if (m_response_left == -1) { received_bytes(0, bytes_transferred); // we should not try this server again. t->remove_web_seed(this, errors::no_content_length, op_bittorrent, 2); return; } if (m_response_left != front_request.length) { received_bytes(0, bytes_transferred); // we should not try this server again. t->remove_web_seed(this, errors::invalid_range, op_bittorrent, 2); return; } m_body_start = m_parser.body_start(); } recv_buffer.begin += m_body_start; // ========================= // === CHUNKED ENCODING === // ========================= while (m_parser.chunked_encoding() && m_chunk_pos >= 0 && m_chunk_pos < recv_buffer.left()) { int header_size = 0; boost::int64_t chunk_size = 0; buffer::const_interval chunk_start = recv_buffer; chunk_start.begin += m_chunk_pos; TORRENT_ASSERT(chunk_start.begin[0] == '\r' || detail::is_hex(chunk_start.begin, 1)); bool ret = m_parser.parse_chunk_header(chunk_start, &chunk_size, &header_size); if (!ret) { TORRENT_ASSERT(bytes_transferred >= size_t(chunk_start.left() - m_partial_chunk_header)); bytes_transferred -= chunk_start.left() - m_partial_chunk_header; received_bytes(0, chunk_start.left() - m_partial_chunk_header); m_partial_chunk_header = chunk_start.left(); if (bytes_transferred == 0) return; break; } else { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CHUNKED_ENCODING" , "parsed chunk: %" PRId64 " header_size: %d" , chunk_size, header_size); #endif TORRENT_ASSERT(bytes_transferred >= size_t(header_size - m_partial_chunk_header)); bytes_transferred -= header_size - m_partial_chunk_header; received_bytes(0, header_size - m_partial_chunk_header); m_partial_chunk_header = 0; TORRENT_ASSERT(chunk_size != 0 || chunk_start.left() <= header_size || chunk_start.begin[header_size] == 'H'); // cut out the chunk header from the receive buffer TORRENT_ASSERT(m_chunk_pos + m_body_start < INT_MAX); m_recv_buffer.cut(header_size, t->block_size() + 1024, int(m_chunk_pos + m_body_start)); recv_buffer = m_recv_buffer.get(); recv_buffer.begin += m_body_start; m_chunk_pos += chunk_size; if (chunk_size == 0) { TORRENT_ASSERT(m_recv_buffer.get().left() < m_chunk_pos + m_body_start + 1 || m_recv_buffer.get()[int(m_chunk_pos + m_body_start)] == 'H' || (m_parser.chunked_encoding() && m_recv_buffer.get()[int(m_chunk_pos + m_body_start)] == '\r')); m_chunk_pos = -1; } } } int payload = bytes_transferred; if (payload > m_response_left) payload = int(m_response_left); if (payload > front_request.length) payload = front_request.length; received_bytes(payload, 0); incoming_piece_fragment(payload); m_response_left -= payload; if (m_parser.status_code() == 503) { if (!m_parser.finished()) return; int retry_time = atol(std::string(recv_buffer.begin, recv_buffer.end).c_str()); if (retry_time <= 0) retry_time = 60; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CONNECT", "retrying in %d seconds", retry_time); #endif received_bytes(0, bytes_transferred); // temporarily unavailable, retry later t->retry_web_seed(this, retry_time); disconnect(error_code(m_parser.status_code(), http_category()), op_bittorrent, 1); return; } // we only received the header, no data if (recv_buffer.left() == 0) break; if (recv_buffer.left() < front_request.length) break; // if the response is chunked, we need to receive the last // terminating chunk and the tail headers before we can proceed if (m_parser.chunked_encoding() && m_chunk_pos >= 0) break; m_requests.pop_front(); incoming_piece(front_request, recv_buffer.begin); if (associated_torrent().expired()) return; int size_to_cut = m_body_start + front_request.length; TORRENT_ASSERT(m_recv_buffer.get().left() < size_to_cut + 1 || m_recv_buffer.get()[size_to_cut] == 'H' || (m_parser.chunked_encoding() && m_recv_buffer.get()[size_to_cut] == '\r')); m_recv_buffer.cut(size_to_cut, t->block_size() + 1024); if (m_response_left == 0) m_chunk_pos = 0; else m_chunk_pos -= front_request.length; bytes_transferred -= payload; m_body_start = 0; if (m_response_left > 0) continue; TORRENT_ASSERT(m_response_left == 0); m_parser.reset(); } } void http_seed_connection::get_specific_peer_info(peer_info& p) const { web_connection_base::get_specific_peer_info(p); p.flags |= peer_info::local_connection; p.connection_type = peer_info::http_seed; } } libtorrent-rasterbar-1.1.13/src/http_stream.cpp000066400000000000000000000101751351156116000215740ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/http_stream.hpp" #include "libtorrent/aux_/escape_string.hpp" // for base64encode #include "libtorrent/socket_io.hpp" namespace libtorrent { void http_stream::name_lookup(error_code const& e, tcp::resolver::iterator i , boost::shared_ptr h) { if (handle_error(e, h)) return; m_sock.async_connect(i->endpoint(), boost::bind( &http_stream::connected, this, _1, h)); } void http_stream::connected(error_code const& e, boost::shared_ptr h) { if (handle_error(e, h)) return; using namespace libtorrent::detail; if (m_no_connect) { std::vector().swap(m_buffer); (*h)(e); return; } // send CONNECT std::back_insert_iterator > p(m_buffer); std::string endpoint = print_endpoint(m_remote_endpoint); write_string("CONNECT " + endpoint + " HTTP/1.0\r\n", p); if (!m_user.empty()) { write_string("Proxy-Authorization: Basic " + base64encode( m_user + ":" + m_password) + "\r\n", p); } write_string("\r\n", p); async_write(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&http_stream::handshake1, this, _1, h)); } void http_stream::handshake1(error_code const& e, boost::shared_ptr h) { if (handle_error(e, h)) return; // read one byte from the socket m_buffer.resize(1); async_read(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&http_stream::handshake2, this, _1, h)); } void http_stream::handshake2(error_code const& e, boost::shared_ptr h) { if (handle_error(e, h)) return; int read_pos = m_buffer.size(); // look for \n\n and \r\n\r\n // both of which means end of http response header bool found_end = false; if (m_buffer[read_pos - 1] == '\n' && read_pos > 2) { if (m_buffer[read_pos - 2] == '\n') { found_end = true; } else if (read_pos > 4 && m_buffer[read_pos - 2] == '\r' && m_buffer[read_pos - 3] == '\n' && m_buffer[read_pos - 4] == '\r') { found_end = true; } } if (found_end) { m_buffer.push_back(0); char* status = std::strchr(&m_buffer[0], ' '); if (status == 0) { (*h)(boost::asio::error::operation_not_supported); error_code ec; close(ec); return; } status++; int code = std::atoi(status); if (code != 200) { (*h)(boost::asio::error::operation_not_supported); error_code ec; close(ec); return; } (*h)(e); std::vector().swap(m_buffer); return; } // read another byte from the socket m_buffer.resize(read_pos + 1); async_read(m_sock, boost::asio::buffer(&m_buffer[0] + read_pos, 1) , boost::bind(&http_stream::handshake2, this, _1, h)); } } libtorrent-rasterbar-1.1.13/src/http_tracker_connection.cpp000066400000000000000000000411351351156116000241530ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/gzip.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/tracker_manager.hpp" #include "libtorrent/http_tracker_connection.hpp" #include "libtorrent/http_connection.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/io.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/broadcast_socket.hpp" // for is_local #include "libtorrent/string_util.hpp" // for is_i2p_url #include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/resolver_interface.hpp" #include "libtorrent/ip_filter.hpp" using namespace libtorrent; namespace libtorrent { http_tracker_connection::http_tracker_connection( io_service& ios , tracker_manager& man , tracker_request const& req , boost::weak_ptr c) : tracker_connection(man, req, ios, c) , m_man(man) {} void http_tracker_connection::start() { std::string url = tracker_req().url; if (0 != (tracker_req().kind & tracker_request::scrape_request)) { // find and replace "announce" with "scrape" // in request std::size_t pos = url.find("announce"); if (pos == std::string::npos) { tracker_connection::fail(errors::scrape_not_available); return; } url.replace(pos, 8, "scrape"); } #if TORRENT_USE_I2P bool const i2p = is_i2p_url(url); #else static const bool i2p = false; #endif aux::session_settings const& settings = m_man.settings(); // if request-string already contains // some parameters, append an ampersand instead // of a question mark size_t arguments_start = url.find('?'); if (arguments_start != std::string::npos) url += "&"; else url += "?"; url += "info_hash="; url += escape_string(tracker_req().info_hash.data(), 20); if (0 == (tracker_req().kind & tracker_request::scrape_request)) { static const char* event_string[] = {"completed", "started", "stopped", "paused"}; char str[1024]; const bool stats = tracker_req().send_stats; snprintf(str, sizeof(str) , "&peer_id=%s" "&port=%d" "&uploaded=%" PRId64 "&downloaded=%" PRId64 "&left=%" PRId64 "&corrupt=%" PRId64 "&key=%08X" "%s%s" // event "&numwant=%d" "&compact=1" "&no_peer_id=1" , escape_string(tracker_req().pid.data(), 20).c_str() // the i2p tracker seems to verify that the port is not 0, // even though it ignores it otherwise , i2p ? 1 : tracker_req().listen_port , stats ? tracker_req().uploaded : 0 , stats ? tracker_req().downloaded : 0 , stats ? tracker_req().left : 0 , stats ? tracker_req().corrupt : 0 , tracker_req().key , (tracker_req().event != tracker_request::none) ? "&event=" : "" , (tracker_req().event != tracker_request::none) ? event_string[tracker_req().event - 1] : "" , tracker_req().num_want); url += str; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) if (settings.get_int(settings_pack::in_enc_policy) != settings_pack::pe_disabled && settings.get_bool(settings_pack::announce_crypto_support)) url += "&supportcrypto=1"; #endif if (stats && settings.get_bool(settings_pack::report_redundant_bytes)) { url += "&redundant="; url += to_string(tracker_req().redundant).elems; } if (!tracker_req().trackerid.empty()) { std::string id = tracker_req().trackerid; url += "&trackerid="; url += escape_string(id.c_str(), id.length()); } #if TORRENT_USE_I2P if (i2p && tracker_req().i2pconn) { if (tracker_req().i2pconn->local_endpoint().empty()) { fail(errors::no_i2p_endpoint, -1, "Waiting for i2p acceptor from SAM bridge", 5); return; } else { url += "&ip=" + tracker_req ().i2pconn->local_endpoint () + ".i2p"; } } else #endif if (!settings.get_bool(settings_pack::anonymous_mode)) { std::string announce_ip = settings.get_str(settings_pack::announce_ip); if (!announce_ip.empty()) { url += "&ip=" + escape_string(announce_ip.c_str(), announce_ip.size()); } } } if (tracker_req().ipv4 != address_v4() && !i2p) { error_code err; std::string const ip = tracker_req().ipv4.to_string(err); if (!err) { url += "&ipv4="; url += escape_string(ip.c_str(), ip.size()); } } #if TORRENT_USE_IPV6 if (tracker_req().ipv6 != address_v6() && !i2p) { error_code err; std::string const ip = tracker_req().ipv6.to_string(err); if (!err) { url += "&ipv6="; url += escape_string(ip.c_str(), ip.size()); } } #endif m_tracker_connection.reset(new http_connection(get_io_service(), m_man.host_resolver() , boost::bind(&http_tracker_connection::on_response, shared_from_this(), _1, _2, _3, _4) , true, settings.get_int(settings_pack::max_http_recv_buffer_size) , boost::bind(&http_tracker_connection::on_connect, shared_from_this(), _1) , boost::bind(&http_tracker_connection::on_filter, shared_from_this(), _1, _2) #ifdef TORRENT_USE_OPENSSL , tracker_req().ssl_ctx #endif )); int const timeout = tracker_req().event==tracker_request::stopped ?settings.get_int(settings_pack::stop_tracker_timeout) :settings.get_int(settings_pack::tracker_completion_timeout); // in anonymous mode we omit the user agent to mitigate fingerprinting of // the client. Private torrents is an exception because some private // trackers may requre the user agent std::string const user_agent = settings.get_bool(settings_pack::anonymous_mode) && !tracker_req().private_torrent ? "" : settings.get_str(settings_pack::user_agent); // when sending stopped requests, prefer the cached DNS entry // to avoid being blocked for slow or failing responses. Chances // are that we're shutting down, and this should be a best-effort // attempt. It's not worth stalling shutdown. aux::proxy_settings ps(settings); m_tracker_connection->get(url, seconds(timeout) , tracker_req().event == tracker_request::stopped ? 2 : 1 , ps.proxy_tracker_connections ? &ps : NULL , 5, user_agent, tracker_req().bind_ip , tracker_req().event == tracker_request::stopped ? resolver_interface::cache_only : 0 | resolver_interface::abort_on_shutdown #ifndef TORRENT_NO_DEPRECATE , tracker_req().auth #else , "" #endif #if TORRENT_USE_I2P , tracker_req().i2pconn #endif ); // the url + 100 estimated header size sent_bytes(url.size() + 100); #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr cb = requester(); if (cb) { cb->debug_log("==> TRACKER_REQUEST [ url: %s ]", url.c_str()); } #endif } void http_tracker_connection::close() { if (m_tracker_connection) { m_tracker_connection->close(); m_tracker_connection.reset(); } tracker_connection::close(); } // endpoints is an in-out parameter void http_tracker_connection::on_filter(http_connection& c , std::vector& endpoints) { TORRENT_UNUSED(c); if (!tracker_req().filter) return; // remove endpoints that are filtered by the IP filter for (std::vector::iterator i = endpoints.begin(); i != endpoints.end();) { if (tracker_req().filter->access(i->address()) == ip_filter::blocked) i = endpoints.erase(i); else ++i; } #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr cb = requester(); if (cb) { cb->debug_log("*** TRACKER_FILTER"); } #endif if (endpoints.empty()) fail(errors::banned_by_ip_filter); } void http_tracker_connection::on_connect(http_connection& c) { error_code ec; tcp::endpoint ep = c.socket().remote_endpoint(ec); m_tracker_ip = ep.address(); boost::shared_ptr cb = requester(); } void http_tracker_connection::on_response(error_code const& ec , http_parser const& parser, char const* data, int size) { // keep this alive boost::shared_ptr me(shared_from_this()); if (ec && ec != boost::asio::error::eof) { fail(ec); return; } if (!parser.header_finished()) { fail(boost::asio::error::eof); return; } if (parser.status_code() != 200) { fail(error_code(parser.status_code(), http_category()) , parser.status_code(), parser.message().c_str()); return; } if (ec && ec != boost::asio::error::eof) { fail(ec, parser.status_code()); return; } received_bytes(size + parser.body_start()); // handle tracker response error_code ecode; boost::shared_ptr cb = requester(); if (!cb) { close(); return; } tracker_response resp = parse_tracker_response(data, size, ecode , tracker_req().kind, tracker_req().info_hash); if (!resp.warning_message.empty()) cb->tracker_warning(tracker_req(), resp.warning_message); if (ecode) { fail(ecode, parser.status_code(), resp.failure_reason.c_str() , resp.interval, resp.min_interval); close(); return; } // do slightly different things for scrape requests if (0 != (tracker_req().kind & tracker_request::scrape_request)) { cb->tracker_scrape_response(tracker_req(), resp.complete , resp.incomplete, resp.downloaded, resp.downloaders); } else { std::list
ip_list; if (m_tracker_connection) { error_code ignore; std::vector const& epts = m_tracker_connection->endpoints(); for (std::vector::const_iterator i = epts.begin() , end(epts.end()); i != end; ++i) { ip_list.push_back(i->address()); } } cb->tracker_response(tracker_req(), m_tracker_ip, ip_list, resp); } close(); } // TODO: 2 returning a bool here is redundant. Instead this function should // return the peer_entry bool extract_peer_info(bdecode_node const& info, peer_entry& ret, error_code& ec) { // extract peer id (if any) if (info.type() != bdecode_node::dict_t) { ec = errors::invalid_peer_dict; return false; } bdecode_node i = info.dict_find_string("peer id"); if (i && i.string_length() == 20) { std::copy(i.string_ptr(), i.string_ptr()+20, 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.dict_find_string("ip"); if (i == 0) { ec = errors::invalid_tracker_response; return false; } ret.hostname = i.string_value(); // extract port i = info.dict_find_int("port"); if (i == 0) { ec = errors::invalid_tracker_response; return false; } ret.port = boost::uint16_t(i.int_value()); return true; } tracker_response parse_tracker_response(char const* data, int size, error_code& ec , int const flags, sha1_hash scrape_ih) { tracker_response resp; bdecode_node e; int res = bdecode(data, data + size, e, ec); if (ec) return resp; if (res != 0 || e.type() != bdecode_node::dict_t) { ec = errors::invalid_tracker_response; return resp; } int interval = int(e.dict_find_int_value("interval", 0)); // if no interval is specified, default to 30 minutes if (interval == 0) interval = 1800; int const min_interval = int(e.dict_find_int_value("min interval", 30)); resp.interval = interval; resp.min_interval = min_interval; bdecode_node tracker_id = e.dict_find_string("tracker id"); if (tracker_id) resp.trackerid = tracker_id.string_value(); // parse the response bdecode_node failure = e.dict_find_string("failure reason"); if (failure) { resp.failure_reason = failure.string_value(); ec = errors::tracker_failure; return resp; } bdecode_node warning = e.dict_find_string("warning message"); if (warning) resp.warning_message = warning.string_value(); if (0 != (flags & tracker_request::scrape_request)) { bdecode_node files = e.dict_find_dict("files"); if (!files) { ec = errors::invalid_files_entry; return resp; } bdecode_node scrape_data = files.dict_find_dict( scrape_ih.to_string()); if (!scrape_data) { ec = errors::invalid_hash_entry; return resp; } resp.complete = int(scrape_data.dict_find_int_value("complete", -1)); resp.incomplete = int(scrape_data.dict_find_int_value("incomplete", -1)); resp.downloaded = int(scrape_data.dict_find_int_value("downloaded", -1)); resp.downloaders = int(scrape_data.dict_find_int_value("downloaders", -1)); return resp; } // look for optional scrape info resp.complete = int(e.dict_find_int_value("complete", -1)); resp.incomplete = int(e.dict_find_int_value("incomplete", -1)); resp.downloaded = int(e.dict_find_int_value("downloaded", -1)); bdecode_node peers_ent = e.dict_find("peers"); if (peers_ent && peers_ent.type() == bdecode_node::string_t) { char const* peers = peers_ent.string_ptr(); int len = peers_ent.string_length(); #if TORRENT_USE_I2P if (0 != (flags & tracker_request::i2p)) { error_code parse_error; for (int i = 0; i < len; i += 32) { if (len - i < 32) break; peer_entry p; p.hostname = base32encode(std::string(peers + i, 32), string::i2p); p.hostname += ".b32.i2p"; p.port = 6881; resp.peers.push_back(p); } } else #endif { resp.peers4.reserve(len / 6); for (int i = 0; i < len; i += 6) { if (len - i < 6) break; ipv4_peer_entry p; p.ip = detail::read_v4_address(peers).to_v4().to_bytes(); p.port = detail::read_uint16(peers); resp.peers4.push_back(p); } } } else if (peers_ent && peers_ent.type() == bdecode_node::list_t) { int len = peers_ent.list_size(); resp.peers.reserve(len); error_code parse_error; for (int i = 0; i < len; ++i) { peer_entry p; if (!extract_peer_info(peers_ent.list_at(i), p, parse_error)) continue; resp.peers.push_back(p); } // only report an error if all peer entries are invalid if (resp.peers.empty() && parse_error) { ec = parse_error; return resp; } } else { peers_ent.clear(); } #if TORRENT_USE_IPV6 bdecode_node ipv6_peers = e.dict_find_string("peers6"); if (ipv6_peers) { char const* peers = ipv6_peers.string_ptr(); int len = ipv6_peers.string_length(); resp.peers6.reserve(len / 18); for (int i = 0; i < len; i += 18) { if (len - i < 18) break; ipv6_peer_entry p; p.ip = detail::read_v6_address(peers).to_v6().to_bytes(); p.port = detail::read_uint16(peers); resp.peers6.push_back(p); } } else { ipv6_peers.clear(); } #else bdecode_node ipv6_peers; #endif /* // if we didn't receive any peers. We don't care if we're stopping anyway if (peers_ent == 0 && ipv6_peers == 0 && tracker_req().event != tracker_request::stopped) { ec = errors::invalid_peers_entry; return resp; } */ bdecode_node ip_ent = e.dict_find_string("external ip"); if (ip_ent) { char const* p = ip_ent.string_ptr(); if (ip_ent.string_length() == int(address_v4::bytes_type().size())) resp.external_ip = detail::read_v4_address(p); #if TORRENT_USE_IPV6 else if (ip_ent.string_length() == int(address_v6::bytes_type().size())) resp.external_ip = detail::read_v6_address(p); #endif } return resp; } } libtorrent-rasterbar-1.1.13/src/i2p_stream.cpp000066400000000000000000000346611351156116000213150ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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/config.hpp" #include "libtorrent/i2p_stream.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/string_util.hpp" #include "libtorrent/settings_pack.hpp" #include "libtorrent/hex.hpp" #if TORRENT_USE_I2P #include #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/debug.hpp" #endif namespace libtorrent { struct i2p_error_category : boost::system::error_category { virtual const char* name() const BOOST_SYSTEM_NOEXCEPT { return "i2p error"; } virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT { static char const* messages[] = { "no error", "parse failed", "cannot reach peer", "i2p error", "invalid key", "invalid id", "timeout", "key not found", "duplicated id" }; if (ev < 0 || ev >= i2p_error::num_errors) return "unknown error"; return messages[ev]; } virtual boost::system::error_condition default_error_condition( int ev) const BOOST_SYSTEM_NOEXCEPT { return boost::system::error_condition(ev, *this); } }; boost::system::error_category& i2p_category() { static i2p_error_category i2p_category; return i2p_category; } #ifndef TORRENT_NO_DEPRECATE boost::system::error_category& get_i2p_category() { return i2p_category(); } #endif namespace i2p_error { boost::system::error_code make_error_code(i2p_error_code e) { return error_code(e, i2p_category()); } } i2p_connection::i2p_connection(io_service& ios) : m_port(0) , m_state(sam_idle) , m_io_service(ios) {} i2p_connection::~i2p_connection() {} void i2p_connection::close(error_code& e) { if (m_sam_socket) m_sam_socket->close(e); } aux::proxy_settings i2p_connection::proxy() const { aux::proxy_settings ret; ret.hostname = m_hostname; ret.port = m_port; ret.type = settings_pack::i2p_proxy; return ret; } void i2p_connection::open(std::string const& s, int port , i2p_stream::handler_type const& handler) { // we already seem to have a session to this SAM router if (m_hostname == s && m_port == port && m_sam_socket && (is_open() || m_state == sam_connecting)) return; m_hostname = s; m_port = port; if (m_hostname.empty()) return; m_state = sam_connecting; char tmp[20]; std::generate(tmp, tmp + sizeof(tmp), &std::rand); m_session_id.resize(sizeof(tmp)*2); to_hex(tmp, 20, &m_session_id[0]); m_sam_socket.reset(new i2p_stream(m_io_service)); m_sam_socket->set_proxy(m_hostname, m_port); m_sam_socket->set_command(i2p_stream::cmd_create_session); m_sam_socket->set_session_id(m_session_id.c_str()); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::on_sam_connect"); #endif m_sam_socket->async_connect(tcp::endpoint() , boost::bind(&i2p_connection::on_sam_connect, this, _1, handler, m_sam_socket)); } void i2p_connection::on_sam_connect(error_code const& ec, i2p_stream::handler_type const& h, boost::shared_ptr) { #if defined TORRENT_ASIO_DEBUGGING complete_async("i2p_stream::on_sam_connect"); #endif m_state = sam_idle; if (ec) { h(ec); return; } do_name_lookup("ME", boost::bind(&i2p_connection::set_local_endpoint, this, _1, _2, h)); } void i2p_connection::set_local_endpoint(error_code const& ec, char const* dest , i2p_stream::handler_type const& h) { if (!ec && dest != 0) m_i2p_local_endpoint = dest; else m_i2p_local_endpoint.clear(); h(ec); } void i2p_connection::async_name_lookup(char const* name , i2p_connection::name_lookup_handler handler) { if (m_state == sam_idle && m_name_lookup.empty() && is_open()) do_name_lookup(name, handler); else m_name_lookup.push_back(std::make_pair(std::string(name), handler)); } void i2p_connection::do_name_lookup(std::string const& name , name_lookup_handler const& handler) { TORRENT_ASSERT(m_state == sam_idle); m_state = sam_name_lookup; m_sam_socket->set_name_lookup(name.c_str()); boost::shared_ptr h(new i2p_stream::handler_type( boost::bind(&i2p_connection::on_name_lookup, this, _1, handler, m_sam_socket))); m_sam_socket->send_name_lookup(h); } void i2p_connection::on_name_lookup(error_code const& ec , name_lookup_handler handler, boost::shared_ptr) { m_state = sam_idle; std::string name = m_sam_socket->name_lookup(); if (!m_name_lookup.empty()) { std::pair& nl = m_name_lookup.front(); do_name_lookup(nl.first, nl.second); m_name_lookup.pop_front(); } if (ec) { handler(ec, 0); return; } handler(ec, name.c_str()); } i2p_stream::i2p_stream(io_service& io_service) : proxy_base(io_service) , m_id(0) , m_command(cmd_create_session) , m_state(read_hello_response) { #if TORRENT_USE_ASSERTS m_magic = 0x1337; #endif } i2p_stream::~i2p_stream() { #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_magic == 0x1337); m_magic = 0; #endif } void i2p_stream::do_connect(error_code const& e, tcp::resolver::iterator i , boost::shared_ptr h) { TORRENT_ASSERT(m_magic == 0x1337); if (e || i == tcp::resolver::iterator()) { (*h)(e); error_code ec; close(ec); return; } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::connected"); #endif m_sock.async_connect(i->endpoint(), boost::bind( &i2p_stream::connected, this, _1, h)); } void i2p_stream::connected(error_code const& e, boost::shared_ptr h) { TORRENT_ASSERT(m_magic == 0x1337); #if defined TORRENT_ASIO_DEBUGGING complete_async("i2p_stream::connected"); #endif if (handle_error(e, h)) return; // send hello command m_state = read_hello_response; static const char cmd[] = "HELLO VERSION MIN=3.0 MAX=3.0\n"; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::start_read_line"); #endif async_write(m_sock, boost::asio::buffer(cmd, sizeof(cmd) - 1) , boost::bind(&i2p_stream::start_read_line, this, _1, h)); // fprintf(stderr, ">>> %s", cmd); } void i2p_stream::start_read_line(error_code const& e, boost::shared_ptr h) { TORRENT_ASSERT(m_magic == 0x1337); #if defined TORRENT_ASIO_DEBUGGING complete_async("i2p_stream::start_read_line"); #endif if (handle_error(e, h)) return; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::read_line"); #endif m_buffer.resize(1); async_read(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&i2p_stream::read_line, this, _1, h)); } void i2p_stream::read_line(error_code const& e, boost::shared_ptr h) { TORRENT_ASSERT(m_magic == 0x1337); #if defined TORRENT_ASIO_DEBUGGING complete_async("i2p_stream::read_line"); #endif if (handle_error(e, h)) return; int read_pos = m_buffer.size(); // look for \n which means end of the response if (m_buffer[read_pos - 1] != '\n') { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::read_line"); #endif // read another byte from the socket m_buffer.resize(read_pos + 1); async_read(m_sock, boost::asio::buffer(&m_buffer[read_pos], 1) , boost::bind(&i2p_stream::read_line, this, _1, h)); return; } m_buffer[read_pos - 1] = 0; if (m_command == cmd_incoming) { // this is the line containing the destination // of the incoming connection in an accept call m_dest = &m_buffer[0]; (*h)(e); std::vector().swap(m_buffer); return; } error_code invalid_response(i2p_error::parse_failed , i2p_category()); // null-terminate the string and parse it m_buffer.push_back(0); char* ptr = &m_buffer[0]; char* next = ptr; char const* expect1 = 0; char const* expect2 = 0; switch (m_state) { case read_hello_response: expect1 = "HELLO"; expect2 = "REPLY"; break; case read_connect_response: case read_accept_response: expect1 = "STREAM"; expect2 = "STATUS"; break; case read_session_create_response: expect1 = "SESSION"; expect2 = "STATUS"; break; case read_name_lookup_response: expect1 = "NAMING"; expect2 = "REPLY"; break; } // fprintf(stderr, "<<< %s\n", &m_buffer[0]); ptr = string_tokenize(next, ' ', &next); if (ptr == 0 || expect1 == 0 || strcmp(expect1, ptr)) { handle_error(invalid_response, h); return; } ptr = string_tokenize(next, ' ', &next); if (ptr == 0 || expect2 == 0 || strcmp(expect2, ptr)) { handle_error(invalid_response, h); return; } int result = 0; // char const* message = 0; // float version = 3.0f; for(;;) { char* name = string_tokenize(next, '=', &next); if (name == 0) break; // fprintf(stderr, "name=\"%s\"\n", name); char* ptr2 = string_tokenize(next, ' ', &next); if (ptr2 == 0) { handle_error(invalid_response, h); return; } // fprintf(stderr, "value=\"%s\"\n", ptr2); if (strcmp("RESULT", name) == 0) { if (strcmp("OK", ptr2) == 0) result = i2p_error::no_error; else if (strcmp("CANT_REACH_PEER", ptr2) == 0) result = i2p_error::cant_reach_peer; else if (strcmp("I2P_ERROR", ptr2) == 0) result = i2p_error::i2p_error; else if (strcmp("INVALID_KEY", ptr2) == 0) result = i2p_error::invalid_key; else if (strcmp("INVALID_ID", ptr2) == 0) result = i2p_error::invalid_id; else if (strcmp("TIMEOUT", ptr2) == 0) result = i2p_error::timeout; else if (strcmp("KEY_NOT_FOUND", ptr2) == 0) result = i2p_error::key_not_found; else if (strcmp("DUPLICATED_ID", ptr2) == 0) result = i2p_error::duplicated_id; else result = i2p_error::num_errors; // unknown error } else if (strcmp("MESSAGE", name) == 0) { // message = ptr2; } else if (strcmp("VERSION", name) == 0) { // version = float(atof(ptr2)); } else if (strcmp("VALUE", name) == 0) { m_name_lookup = ptr2; } else if (strcmp("DESTINATION", name) == 0) { m_dest = ptr2; } } error_code ec(result, i2p_category()); switch (result) { case i2p_error::no_error: case i2p_error::invalid_key: break; default: { handle_error (ec, h); return; } } switch (m_state) { case read_hello_response: switch (m_command) { case cmd_create_session: send_session_create(h); break; case cmd_accept: send_accept(h); break; case cmd_connect: send_connect(h); break; case cmd_none: case cmd_name_lookup: case cmd_incoming: (*h)(e); std::vector().swap(m_buffer); } break; case read_connect_response: case read_session_create_response: case read_name_lookup_response: (*h)(ec); std::vector().swap(m_buffer); break; case read_accept_response: // the SAM bridge is waiting for an incoming // connection. // wait for one more line containing // the destination of the remote peer m_command = cmd_incoming; m_buffer.resize(1); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::read_line"); #endif async_read(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&i2p_stream::read_line, this, _1, h)); break; } return; } void i2p_stream::send_connect(boost::shared_ptr h) { TORRENT_ASSERT(m_magic == 0x1337); m_state = read_connect_response; char cmd[1024]; int size = snprintf(cmd, sizeof(cmd), "STREAM CONNECT ID=%s DESTINATION=%s\n" , m_id, m_dest.c_str()); // fprintf(stderr, ">>> %s", cmd); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::start_read_line"); #endif async_write(m_sock, boost::asio::buffer(cmd, size) , boost::bind(&i2p_stream::start_read_line, this, _1, h)); } void i2p_stream::send_accept(boost::shared_ptr h) { TORRENT_ASSERT(m_magic == 0x1337); m_state = read_accept_response; char cmd[400]; int size = snprintf(cmd, sizeof(cmd), "STREAM ACCEPT ID=%s\n", m_id); // fprintf(stderr, ">>> %s", cmd); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::start_read_line"); #endif async_write(m_sock, boost::asio::buffer(cmd, size) , boost::bind(&i2p_stream::start_read_line, this, _1, h)); } void i2p_stream::send_session_create(boost::shared_ptr h) { TORRENT_ASSERT(m_magic == 0x1337); m_state = read_session_create_response; char cmd[400]; int size = snprintf(cmd, sizeof(cmd), "SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT\n" , m_id); // fprintf(stderr, ">>> %s", cmd); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::start_read_line"); #endif async_write(m_sock, boost::asio::buffer(cmd, size) , boost::bind(&i2p_stream::start_read_line, this, _1, h)); } void i2p_stream::send_name_lookup(boost::shared_ptr h) { TORRENT_ASSERT(m_magic == 0x1337); m_state = read_name_lookup_response; char cmd[1024]; int size = snprintf(cmd, sizeof(cmd), "NAMING LOOKUP NAME=%s\n", m_name_lookup.c_str()); // fprintf(stderr, ">>> %s", cmd); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::start_read_line"); #endif async_write(m_sock, boost::asio::buffer(cmd, size) , boost::bind(&i2p_stream::start_read_line, this, _1, h)); } } #endif libtorrent-rasterbar-1.1.13/src/identify_client.cpp000066400000000000000000000254261351156116000224200ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include #include #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/identify_client.hpp" #include "libtorrent/fingerprint.hpp" #include "libtorrent/string_util.hpp" namespace { using namespace libtorrent; int decode_digit(char c) { if (is_digit(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 parse_az_style(const peer_id& id) { fingerprint ret("..", 0, 0, 0, 0); if (id[0] != '-' || !is_print(id[1]) || (id[2] < '0') || (id[3] < '0') || (id[4] < '0') || (id[5] < '0') || (id[6] < '0') || id[7] != '-') return boost::optional(); 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(ret); } // checks if a peer id can possibly contain a shadow-style // identification boost::optional parse_shadow_style(const peer_id& id) { fingerprint ret("..", 0, 0, 0, 0); if (!is_alpha(id[0]) && !is_digit(id[0])) return boost::optional(); if (std::equal(id.begin()+4, id.begin()+6, "--")) { if ((id[1] < '0') || (id[2] < '0') || (id[3] < '0')) return boost::optional(); 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(); 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(ret); } // checks if a peer id can possibly contain a mainline-style // identification boost::optional 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 || !is_print(ret.name[0])) return boost::optional(); return boost::optional(ret); } struct map_entry { char const* id; char const* name; }; // only support BitTorrentSpecification // must be ordered alphabetically map_entry name_map[] = { {"7T", "aTorrent for android"} , {"A", "ABC"} , {"AB", "AnyEvent BitTorrent"} , {"AG", "Ares"} , {"AR", "Arctic Torrent"} , {"AT", "Artemis"} , {"AV", "Avicora"} , {"AX", "BitPump"} , {"AZ", "Azureus"} , {"A~", "Ares"} , {"BB", "BitBuddy"} , {"BC", "BitComet"} , {"BE", "baretorrent"} , {"BF", "Bitflu"} , {"BG", "BTG"} , {"BL", "BitBlinder"} , {"BP", "BitTorrent Pro"} , {"BR", "BitRocket"} , {"BS", "BTSlave"} , {"BT", "BitTorrent"} , {"BU", "BigUp"} , {"BW", "BitWombat"} , {"BX", "BittorrentX"} , {"CD", "Enhanced CTorrent"} , {"CT", "CTorrent"} , {"DE", "Deluge"} , {"DP", "Propagate Data Client"} , {"EB", "EBit"} , {"ES", "electric sheep"} , {"FC", "FileCroc"} , {"FT", "FoxTorrent"} , {"FX", "Freebox BitTorrent"} , {"GS", "GSTorrent"} , {"HK", "Hekate"} , {"HL", "Halite"} , {"HN", "Hydranode"} , {"IL", "iLivid"} , {"KG", "KGet"} , {"KT", "KTorrent"} , {"LC", "LeechCraft"} , {"LH", "LH-ABC"} , {"LK", "Linkage"} , {"LP", "lphant"} , {"LT", "libtorrent"} , {"LW", "Limewire"} , {"M", "Mainline"} , {"ML", "MLDonkey"} , {"MO", "Mono Torrent"} , {"MP", "MooPolice"} , {"MR", "Miro"} , {"MT", "Moonlight Torrent"} , {"NX", "Net Transport"} , {"O", "Osprey Permaseed"} , {"OS", "OneSwarm"} , {"OT", "OmegaTorrent"} , {"PD", "Pando"} , {"Q", "BTQueue"} , {"QD", "QQDownload"} , {"QT", "Qt 4"} , {"R", "Tribler"} , {"RT", "Retriever"} , {"RZ", "RezTorrent"} , {"S", "Shadow"} , {"SB", "Swiftbit"} , {"SD", "Xunlei"} , {"SK", "spark"} , {"SN", "ShareNet"} , {"SS", "SwarmScope"} , {"ST", "SymTorrent"} , {"SZ", "Shareaza"} , {"S~", "Shareaza (beta)"} , {"T", "BitTornado"} , {"TB", "Torch"} , {"TL", "Tribler"} , {"TN", "Torrent.NET"} , {"TR", "Transmission"} , {"TS", "TorrentStorm"} , {"TT", "TuoTu"} , {"U", "UPnP"} , {"UL", "uLeecher"} , {"UM", "uTorrent Mac"} , {"UT", "uTorrent"} , {"VG", "Vagaa"} , {"WT", "BitLet"} , {"WY", "FireTorrent"} , {"XF", "Xfplay"} , {"XL", "Xunlei"} , {"XS", "XSwifter"} , {"XT", "XanTorrent"} , {"XX", "Xtorrent"} , {"ZT", "ZipTorrent"} , {"lt", "rTorrent"} , {"pX", "pHoeniX"} , {"qB", "qBittorrent"} , {"st", "SharkTorrent"} }; struct generic_map_entry { int offset; char const* id; char const* name; }; // non-standard names generic_map_entry generic_mappings[] = { {0, "Deadman Walking-", "Deadman"} , {5, "Azureus", "Azureus 2.0.3.2"} , {0, "DansClient", "XanTorrent"} , {4, "btfans", "SimpleBT"} , {0, "PRC.P---", "Bittorrent Plus! II"} , {0, "P87.P---", "Bittorrent Plus!"} , {0, "S587Plus", "Bittorrent Plus!"} , {0, "martini", "Martini Man"} , {0, "Plus---", "Bittorrent Plus"} , {0, "turbobt", "TurboBT"} , {0, "a00---0", "Swarmy"} , {0, "a02---0", "Swarmy"} , {0, "T00---0", "Teeweety"} , {0, "BTDWV-", "Deadman Walking"} , {2, "BS", "BitSpirit"} , {0, "Pando-", "Pando"} , {0, "LIME", "LimeWire"} , {0, "btuga", "BTugaXP"} , {0, "oernu", "BTugaXP"} , {0, "Mbrst", "Burst!"} , {0, "PEERAPP", "PeerApp"} , {0, "Plus", "Plus!"} , {0, "-Qt-", "Qt"} , {0, "exbc", "BitComet"} , {0, "DNA", "BitTorrent DNA"} , {0, "-G3", "G3 Torrent"} , {0, "-FG", "FlashGet"} , {0, "-ML", "MLdonkey"} , {0, "-MG", "Media Get"} , {0, "XBT", "XBT"} , {0, "OP", "Opera"} , {2, "RS", "Rufus"} , {0, "AZ2500BT", "BitTyrant"} , {0, "btpd/", "BitTorrent Protocol Daemon"} , {0, "TIX", "Tixati"} , {0, "QVOD", "Qvod"} }; bool compare_id(map_entry const& lhs, map_entry const& rhs) { return lhs.id[0] < rhs.id[0] || ((lhs.id[0] == rhs.id[0]) && (lhs.id[1] < rhs.id[1])); } std::string lookup(fingerprint const& f) { char identity[200]; const int size = sizeof(name_map)/sizeof(name_map[0]); map_entry tmp = {f.name, ""}; map_entry* i = std::lower_bound(name_map, name_map + size , tmp, &compare_id); #ifndef NDEBUG for (int j = 1; j < size; ++j) { TORRENT_ASSERT(compare_id(name_map[j-1] , name_map[j])); } #endif char temp[3]; char const* name = 0; if (i < name_map + size && std::equal(f.name, f.name + 2, i->id)) { name = i->name; } else { // if we don't have this client in the list // just use the one or two letter code memcpy(temp, f.name, 2); temp[2] = 0; name = temp; } int num_chars = snprintf(identity, sizeof(identity), "%s %u.%u.%u", name , f.major_version, f.minor_version, f.revision_version); if (f.tag_version != 0) { snprintf(identity + num_chars, sizeof(identity) - num_chars , ".%u", f.tag_version); } return identity; } bool find_string(char const* id, char const* search) { return std::equal(search, search + std::strlen(search), id); } } namespace libtorrent { #ifndef TORRENT_NO_DEPRECATE boost::optional client_fingerprint(peer_id const& p) { // look for azureus style id boost::optional 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; } #endif std::string identify_client(peer_id const& p) { char const* PID = p.data(); boost::optional f; if (p.is_all_zeros()) return "Unknown"; // ---------------------- // non standard encodings // ---------------------- int num_generic_mappings = sizeof(generic_mappings) / sizeof(generic_mappings[0]); for (int i = 0; i < num_generic_mappings; ++i) { generic_map_entry const& e = generic_mappings[i]; if (find_string(PID + e.offset, e.id)) return e.name; } 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 += is_print(char(*i))?*i:'.'; } unknown += "]"; return unknown; } } libtorrent-rasterbar-1.1.13/src/instantiate_connection.cpp000066400000000000000000000105141351156116000240010ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/socket.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/utp_socket_manager.hpp" #include "libtorrent/instantiate_connection.hpp" #include #include namespace libtorrent { // TODO: 2 peer_connection and tracker_connection should probably be flags // TODO: 2 move this function into libtorrent::aux namespace bool instantiate_connection(io_service& ios , aux::proxy_settings const& ps, socket_type& s , void* ssl_context , utp_socket_manager* sm , bool peer_connection , bool tracker_connection) { #ifndef TORRENT_USE_OPENSSL TORRENT_UNUSED(ssl_context); #endif if (sm) { utp_stream* str; #ifdef TORRENT_USE_OPENSSL if (ssl_context) { s.instantiate >(ios, ssl_context); str = &s.get >()->next_layer(); } else #endif { s.instantiate(ios); str = s.get(); } str->set_impl(sm->new_utp_socket(str)); } #if TORRENT_USE_I2P else if (ps.type == settings_pack::i2p_proxy) { // it doesn't make any sense to try ssl over i2p TORRENT_ASSERT(ssl_context == 0); s.instantiate(ios); s.get()->set_proxy(ps.hostname, ps.port); } #endif else if (ps.type == settings_pack::none || (peer_connection && !ps.proxy_peer_connections) || (tracker_connection && !ps.proxy_tracker_connections)) { #ifdef TORRENT_USE_OPENSSL if (ssl_context) { s.instantiate >(ios, ssl_context); } else #endif { s.instantiate(ios); } } else if (ps.type == settings_pack::http || ps.type == settings_pack::http_pw) { http_stream* str; #ifdef TORRENT_USE_OPENSSL if (ssl_context) { s.instantiate >(ios, ssl_context); str = &s.get >()->next_layer(); } else #endif { s.instantiate(ios); str = s.get(); } str->set_proxy(ps.hostname, ps.port); if (ps.type == settings_pack::http_pw) str->set_username(ps.username, ps.password); } else if (ps.type == settings_pack::socks5 || ps.type == settings_pack::socks5_pw || ps.type == settings_pack::socks4) { socks5_stream* str; #ifdef TORRENT_USE_OPENSSL if (ssl_context) { s.instantiate >(ios, ssl_context); str = &s.get >()->next_layer(); } else #endif { s.instantiate(ios); str = s.get(); } str->set_proxy(ps.hostname, ps.port); if (ps.type == settings_pack::socks5_pw) str->set_username(ps.username, ps.password); if (ps.type == settings_pack::socks4) str->set_version(4); } else { TORRENT_ASSERT_VAL(false, ps.type); return false; } return true; } } libtorrent-rasterbar-1.1.13/src/ip_filter.cpp000066400000000000000000000057021351156116000212170ustar00rootroot00000000000000/* Copyright (c) 2005-2018, 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 namespace libtorrent { void ip_filter::add_rule(address first, address last, boost::uint32_t flags) { if (first.is_v4()) { TORRENT_ASSERT(last.is_v4()); m_filter4.add_rule(first.to_v4().to_bytes(), last.to_v4().to_bytes(), flags); } #if TORRENT_USE_IPV6 else if (first.is_v6()) { TORRENT_ASSERT(last.is_v6()); m_filter6.add_rule(first.to_v6().to_bytes(), last.to_v6().to_bytes(), flags); } #endif else TORRENT_ASSERT(false); } int ip_filter::access(address const& addr) const { if (addr.is_v4()) return m_filter4.access(addr.to_v4().to_bytes()); #if TORRENT_USE_IPV6 TORRENT_ASSERT(addr.is_v6()); return m_filter6.access(addr.to_v6().to_bytes()); #else return 0; #endif } ip_filter::filter_tuple_t ip_filter::export_filter() const { #if TORRENT_USE_IPV6 return boost::make_tuple(m_filter4.export_filter() , m_filter6.export_filter()); #else return m_filter4.export_filter(); #endif } void port_filter::add_rule(boost::uint16_t first, boost::uint16_t last, boost::uint32_t flags) { m_filter.add_rule(first, last, flags); } int port_filter::access(boost::uint16_t port) const { return m_filter.access(port); } /* void ip_filter::print() const { for (range_t::iterator i = m_access_list.begin(); i != m_access_list.end(); ++i) { std::cout << i->start.as_string() << " " << i->access << "\n"; } } */ } libtorrent-rasterbar-1.1.13/src/ip_voter.cpp000066400000000000000000000143061351156116000210710ustar00rootroot00000000000000/* Copyright (c) 2013-2018, 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_voter.hpp" #include "libtorrent/broadcast_socket.hpp" // for is_any() etc. #include "libtorrent/socket_io.hpp" // for hash_address #include "libtorrent/random.hpp" // for random() #include "libtorrent/aux_/time.hpp" // for aux::time_now() #include namespace libtorrent { ip_voter::ip_voter() : m_total_votes(0) , m_valid_external(false) , m_last_rotate(aux::time_now()) { } // returns true if our external IP changed bool ip_voter::maybe_rotate() { time_point now = aux::time_now(); // if we have more than or equal to 50 votes, // we rotate. Also, if it's been more than 5 minutes // and we have at least one vote, we also rotate. // this is the inverse condition, since this is the case // were we exit, without rotating if (m_total_votes < 50 && (now - m_last_rotate < minutes(5) || m_total_votes == 0) && m_valid_external) return false; // this shouldn't really happen if we have at least one // vote. if (m_external_addresses.empty()) return false; // if there's just one vote, go with that std::vector::iterator i; if (m_external_addresses.size() == 1) { // avoid flapping. We need more votes to change our mind on the // external IP if (m_external_addresses[0].num_votes < 2) return false; } else { // find the top two votes. std::partial_sort(m_external_addresses.begin() , m_external_addresses.begin() + 2, m_external_addresses.end()); // if we don't have enough of a majority voting for the winning // IP, don't rotate. This avoids flapping if (m_external_addresses[0].num_votes * 2 / 3 <= m_external_addresses[1].num_votes) return false; } i = m_external_addresses.begin(); bool ret = m_external_address != i->addr; m_external_address = i->addr; m_external_address_voters.clear(); m_total_votes = 0; m_external_addresses.clear(); m_last_rotate = now; m_valid_external = true; return ret; } bool ip_voter::cast_vote(address const& ip , int source_type, address const& source) { if (is_any(ip)) return false; if (is_local(ip)) return false; if (is_loopback(ip)) return false; // don't trust source that aren't connected to us // on a different address family than the external // IP they claim we have if (ip.is_v4() != source.is_v4()) return false; // this is the key to use for the bloom filters // it represents the identity of the voter sha1_hash k; hash_address(source, k); // do we already have an entry for this external IP? std::vector::iterator i = std::find_if(m_external_addresses.begin() , m_external_addresses.end(), boost::bind(&external_ip_t::addr, _1) == ip); if (i == m_external_addresses.end()) { // each IP only gets to add a new IP once if (m_external_address_voters.find(k)) return maybe_rotate(); if (m_external_addresses.size() > 40) { if (random() % 100 < 50) return maybe_rotate(); // use stable sort here to maintain the fifo-order // of the entries with the same number of votes // this will sort in ascending order, i.e. the lowest // votes first. Also, the oldest are first, so this // is a sort of weighted LRU. std::stable_sort(m_external_addresses.begin(), m_external_addresses.end()); // erase the last element, since it is one of the // ones with the fewest votes m_external_addresses.erase(m_external_addresses.end() - 1); } m_external_addresses.push_back(external_ip_t()); i = m_external_addresses.end() - 1; i->addr = ip; } // add one more vote to this external IP if (!i->add_vote(k, source_type)) return maybe_rotate(); ++m_total_votes; if (m_valid_external) return maybe_rotate(); i = std::min_element(m_external_addresses.begin(), m_external_addresses.end()); TORRENT_ASSERT(i != m_external_addresses.end()); if (i->addr == m_external_address) return maybe_rotate(); if (m_external_address != address_v4()) { // we have a temporary external address. As soon as we have // more than 25 votes, consider deciding which one to settle for return (m_total_votes >= 25) ? maybe_rotate() : false; } m_external_address = i->addr; return true; } bool ip_voter::external_ip_t::add_vote(sha1_hash const& k, int type) { sources |= type; if (voters.find(k)) return false; voters.set(k); ++num_votes; return true; } bool external_ip::cast_vote(address const& ip, int source_type, address const& source) { return m_vote_group[ip.is_v6()].cast_vote(ip, source_type, source); } address external_ip::external_address(address const& ip) const { address ext = m_vote_group[ip.is_v6()].external_address(); #if TORRENT_USE_IPV6 if (ip.is_v6() && ext == address_v4()) return address_v6(); #endif return ext; } } libtorrent-rasterbar-1.1.13/src/kademlia/000077500000000000000000000000001351156116000203015ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/src/kademlia/dht_storage.cpp000066400000000000000000000416241351156116000233170ustar00rootroot00000000000000/* Copyright (c) 2012-2018, Arvid Norberg, Alden Torres 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/dht_storage.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for memset namespace libtorrent { namespace dht { namespace { using detail::write_endpoint; // this is the entry for every peer // the timestamp is there to make it possible // to remove stale peers struct peer_entry { time_point added; tcp::endpoint addr; bool seed; }; // internal 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(); } // this is a group. It contains a set of group members struct torrent_entry { std::string name; std::set peers; }; #ifndef TORRENT_NO_DEPRECATE struct count_peers { int* count; count_peers(int* c): count(c) {} void operator()(std::pair const& t) { *count += t.second.peers.size(); } }; #endif // TODO: 2 make this configurable in dht_settings enum { announce_interval = 30 }; struct dht_immutable_item { dht_immutable_item() : value(0), num_announcers(0), size(0) {} // malloced space for the actual value char* value; // this counts the number of IPs we have seen // announcing this item, this is used to determine // popularity if we reach the limit of items to store bloom_filter<128> ips; // the last time we heard about this time_point last_seen; // number of IPs in the bloom filter int num_announcers; // size of malloced space pointed to by value int size; }; struct ed25519_public_key { char bytes[item_pk_len]; }; struct dht_mutable_item : dht_immutable_item { char sig[item_sig_len]; boost::int64_t seq; ed25519_public_key key; char* salt; int salt_size; }; void touch_item(dht_immutable_item* f, address const& address) { f->last_seen = aux::time_now(); // maybe increase num_announcers if we haven't seen this IP before sha1_hash iphash; hash_address(address, iphash); if (!f->ips.find(iphash)) { f->ips.set(iphash); ++f->num_announcers; } } // return true of the first argument is a better candidate for removal, i.e. // less important to keep struct immutable_item_comparator { immutable_item_comparator(node_id const& our_id) : m_our_id(our_id) {} immutable_item_comparator(immutable_item_comparator const& c) : m_our_id(c.m_our_id) {} bool operator() (std::pair const& lhs , std::pair const& rhs) const { int l_distance = distance_exp(lhs.first, m_our_id); int r_distance = distance_exp(rhs.first, m_our_id); // this is a score taking the popularity (number of announcers) and the // fit, in terms of distance from ideal storing node, into account. // each additional 5 announcers is worth one extra bit in the distance. // that is, an item with 10 announcers is allowed to be twice as far // from another item with 5 announcers, from our node ID. Twice as far // because it gets one more bit. return lhs.second.num_announcers / 5 - l_distance < rhs.second.num_announcers / 5 - r_distance; } private: // explicitly disallow assignment, to silence msvc warning immutable_item_comparator& operator=(immutable_item_comparator const&); node_id const& m_our_id; }; class dht_default_storage TORRENT_FINAL : public dht_storage_interface, boost::noncopyable { typedef std::map table_t; typedef std::map dht_immutable_table_t; typedef std::map dht_mutable_table_t; public: dht_default_storage(sha1_hash const& id, dht_settings const& settings) : m_id(id) , m_settings(settings) { memset(&m_counters, 0, sizeof(m_counters)); } ~dht_default_storage() { for (dht_mutable_table_t::iterator i = m_mutable_table.begin(); i != m_mutable_table.end(); ++i) { std::free(i->second.value); std::free(i->second.salt); } m_counters.mutable_data -= m_mutable_table.size(); for (dht_immutable_table_t::iterator i = m_immutable_table.begin(); i != m_immutable_table.end(); ++i) { std::free(i->second.value); } } #ifndef TORRENT_NO_DEPRECATE size_t num_torrents() const TORRENT_OVERRIDE { return m_map.size(); } size_t num_peers() const TORRENT_OVERRIDE { int ret = 0; std::for_each(m_map.begin(), m_map.end(), count_peers(&ret)); return ret; } #endif bool get_peers(sha1_hash const& info_hash , bool noseed, bool scrape , entry& peers) const TORRENT_OVERRIDE { table_t::const_iterator i = m_map.lower_bound(info_hash); if (i == m_map.end()) return false; if (i->first != info_hash) return false; torrent_entry const& v = i->second; if (!v.name.empty()) peers["n"] = v.name; if (scrape) { bloom_filter<256> downloaders; bloom_filter<256> seeds; for (std::set::const_iterator peer_it = v.peers.begin() , end(v.peers.end()); peer_it != end; ++peer_it) { sha1_hash iphash; hash_address(peer_it->addr.address(), iphash); if (peer_it->seed) seeds.set(iphash); else downloaders.set(iphash); } peers["BFpe"] = downloaders.to_string(); peers["BFsd"] = seeds.to_string(); } else { int to_pick = m_settings.max_peers_reply; int candidates = int(v.peers.size()); std::set::const_iterator iter = v.peers.begin(); entry::list_type& pe = peers["values"].list(); std::string endpoint; for (; to_pick > 0 && iter != v.peers.end(); ++iter, --candidates) { if (noseed && iter->seed) continue; // pick this peer with probability // / if (random() % candidates > to_pick) continue; endpoint.resize(18); std::string::iterator out = endpoint.begin(); write_endpoint(iter->addr, out); endpoint.resize(out - endpoint.begin()); pe.push_back(entry(endpoint)); --to_pick; } } return true; } void announce_peer(sha1_hash const& info_hash , tcp::endpoint const& endp , std::string const& name, bool seed) TORRENT_OVERRIDE { table_t::iterator ti = m_map.find(info_hash); torrent_entry* v; if (ti == m_map.end()) { // we don't have this torrent, add it // do we need to remove another one first? if (!m_map.empty() && int(m_map.size()) >= m_settings.max_torrents) { // we need to remove some. Remove the ones with the // fewest peers int num_peers = m_map.begin()->second.peers.size(); table_t::iterator candidate = m_map.begin(); for (table_t::iterator i = m_map.begin() , end(m_map.end()); i != end; ++i) { if (int(i->second.peers.size()) > num_peers) continue; if (i->first == info_hash) continue; num_peers = i->second.peers.size(); candidate = i; } m_map.erase(candidate); m_counters.peers -= num_peers; m_counters.torrents -= 1; } m_counters.torrents += 1; v = &m_map[info_hash]; } else { v = &ti->second; } // the peer announces a torrent name, and we don't have a name // for this torrent. Store it. if (!name.empty() && v->name.empty()) { std::string tname = name; if (tname.size() > 100) tname.resize(100); v->name = tname; } peer_entry peer; peer.addr = endp; peer.added = aux::time_now(); peer.seed = seed; std::set::iterator i = v->peers.find(peer); if (i != v->peers.end()) { v->peers.erase(i++); m_counters.peers -= 1; } else if (v->peers.size() >= m_settings.max_peers) { // when we're at capacity, there's a 50/50 chance of dropping the // announcing peer or an existing peer if (random() & 1) return; i = v->peers.lower_bound(peer); if (i == v->peers.end()) --i; v->peers.erase(i++); m_counters.peers -= 1; } v->peers.insert(i, peer); m_counters.peers += 1; } bool get_immutable_item(sha1_hash const& target , entry& item) const TORRENT_OVERRIDE { dht_immutable_table_t::const_iterator i = m_immutable_table.find(target); if (i == m_immutable_table.end()) return false; item["v"] = bdecode(i->second.value, i->second.value + i->second.size); return true; } void put_immutable_item(sha1_hash const& target , char const* buf, int size , address const& addr) TORRENT_OVERRIDE { dht_immutable_table_t::iterator i = m_immutable_table.find(target); if (i == m_immutable_table.end()) { // make sure we don't add too many items if (int(m_immutable_table.size()) >= m_settings.max_dht_items) { // delete the least important one (i.e. the one // the fewest peers are announcing, and farthest // from our node ID) dht_immutable_table_t::iterator j = std::min_element(m_immutable_table.begin() , m_immutable_table.end() , immutable_item_comparator(m_id)); TORRENT_ASSERT(j != m_immutable_table.end()); std::free(j->second.value); m_immutable_table.erase(j); m_counters.immutable_data -= 1; } dht_immutable_item to_add; to_add.value = static_cast(std::malloc(size)); to_add.size = size; memcpy(to_add.value, buf, size); boost::tie(i, boost::tuples::ignore) = m_immutable_table.insert( std::make_pair(target, to_add)); m_counters.immutable_data += 1; } // fprintf(stderr, "added immutable item (%d)\n", int(m_immutable_table.size())); touch_item(&i->second, addr); } bool get_mutable_item_seq(sha1_hash const& target , boost::int64_t& seq) const TORRENT_OVERRIDE { dht_mutable_table_t::const_iterator i = m_mutable_table.find(target); if (i == m_mutable_table.end()) return false; seq = i->second.seq; return true; } bool get_mutable_item(sha1_hash const& target , boost::int64_t seq, bool force_fill , entry& item) const TORRENT_OVERRIDE { dht_mutable_table_t::const_iterator i = m_mutable_table.find(target); if (i == m_mutable_table.end()) return false; dht_mutable_item const& f = i->second; item["seq"] = f.seq; if (force_fill || (0 <= seq && seq < f.seq)) { item["v"] = bdecode(f.value, f.value + f.size); item["sig"] = std::string(f.sig, f.sig + sizeof(f.sig)); item["k"] = std::string(f.key.bytes, f.key.bytes + sizeof(f.key.bytes)); } return true; } void put_mutable_item(sha1_hash const& target , char const* buf, int size , char const* sig , boost::int64_t seq , char const* pk , char const* salt, int salt_size , address const& addr) TORRENT_OVERRIDE { dht_mutable_table_t::iterator i = m_mutable_table.find(target); if (i == m_mutable_table.end()) { // this is the case where we don't have an item in this slot // make sure we don't add too many items if (int(m_mutable_table.size()) >= m_settings.max_dht_items) { // delete the least important one (i.e. the one // the fewest peers are announcing) // TODO: c++11 use a lambda here instead dht_mutable_table_t::iterator j = std::min_element(m_mutable_table.begin() , m_mutable_table.end() , boost::bind(&dht_immutable_item::num_announcers , boost::bind(&dht_mutable_table_t::value_type::second, _1)) < boost::bind(&dht_immutable_item::num_announcers , boost::bind(&dht_mutable_table_t::value_type::second, _2))); TORRENT_ASSERT(j != m_mutable_table.end()); std::free(j->second.value); std::free(j->second.salt); m_mutable_table.erase(j); m_counters.mutable_data -= 1; } dht_mutable_item to_add; to_add.value = static_cast(std::malloc(size)); to_add.size = size; to_add.seq = seq; to_add.salt = NULL; to_add.salt_size = 0; if (salt_size > 0) { to_add.salt = static_cast(std::malloc(salt_size)); to_add.salt_size = salt_size; memcpy(to_add.salt, salt, salt_size); } memcpy(to_add.sig, sig, sizeof(to_add.sig)); memcpy(to_add.value, buf, size); memcpy(&to_add.key, pk, sizeof(to_add.key)); boost::tie(i, boost::tuples::ignore) = m_mutable_table.insert( std::make_pair(target, to_add)); m_counters.mutable_data += 1; // fprintf(stderr, "added mutable item (%d)\n", int(m_mutable_table.size())); } else { // this is the case where we already dht_mutable_item* item = &i->second; if (item->seq < seq) { if (item->size != size) { std::free(item->value); item->value = static_cast(std::malloc(size)); item->size = size; } item->seq = seq; memcpy(item->sig, sig, sizeof(item->sig)); memcpy(item->value, buf, size); } } touch_item(&i->second, addr); } void tick() TORRENT_OVERRIDE { time_point now(aux::time_now()); // look through all peers and see if any have timed out for (table_t::iterator i = m_map.begin(), end(m_map.end()); i != end;) { torrent_entry& t = i->second; purge_peers(t.peers); if (!t.peers.empty()) { ++i; continue; } // if there are no more peers, remove the entry altogether m_map.erase(i++); m_counters.torrents -= 1;// peers is decreased by purge_peers } if (0 == m_settings.item_lifetime) return; time_duration lifetime = seconds(m_settings.item_lifetime); // item lifetime must >= 120 minutes. if (lifetime < minutes(120)) lifetime = minutes(120); for (dht_immutable_table_t::iterator i = m_immutable_table.begin(); i != m_immutable_table.end();) { if (i->second.last_seen + lifetime > now) { ++i; continue; } std::free(i->second.value); m_immutable_table.erase(i++); m_counters.immutable_data -= 1; } for (dht_mutable_table_t::iterator i = m_mutable_table.begin(); i != m_mutable_table.end();) { if (i->second.last_seen + lifetime > now) { ++i; continue; } std::free(i->second.value); std::free(i->second.salt); m_mutable_table.erase(i++); m_counters.mutable_data -= 1; } } virtual dht_storage_counters counters() const TORRENT_OVERRIDE { return m_counters; } private: sha1_hash m_id; dht_settings const& m_settings; dht_storage_counters m_counters; table_t m_map; dht_immutable_table_t m_immutable_table; dht_mutable_table_t m_mutable_table; void purge_peers(std::set& peers) { for (std::set::iterator i = peers.begin() , end(peers.end()); i != end;) { // the peer has timed out if (i->added + minutes(int(announce_interval * 1.5f)) < aux::time_now()) { peers.erase(i++); m_counters.peers -= 1; } else ++i; } } }; } dht_storage_interface* dht_default_storage_constructor(sha1_hash const& id , dht_settings const& settings) { return new dht_default_storage(id, settings); } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/dht_tracker.cpp000066400000000000000000000310771351156116000233070ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include "libtorrent/config.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/kademlia/msg.hpp" #include "libtorrent/kademlia/dht_observer.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/io.hpp" #include "libtorrent/version.hpp" #include "libtorrent/time.hpp" #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" using boost::ref; using libtorrent::dht::node; using libtorrent::dht::node_id; using libtorrent::dht::packet_t; using libtorrent::dht::msg; using libtorrent::detail::write_endpoint; namespace libtorrent { namespace dht { void incoming_error(entry& e, char const* msg); namespace { // generate a new write token key every 5 minutes time_duration const key_refresh = duration_cast(minutes(5)); node_id extract_node_id(entry const& e) { if (e.type() != entry::dictionary_t) return (node_id::min)(); entry const* nid = e.find_key("node-id"); if (nid == NULL || nid->type() != entry::string_t || nid->string().length() != 20) return (node_id::min)(); return node_id(nid->string().c_str()); } } // anonymous namespace // class that puts the networking and the kademlia node in a single // unit and connecting them together. dht_tracker::dht_tracker(dht_observer* observer , rate_limited_udp_socket& sock , dht_settings const& settings , counters& cnt , dht_storage_constructor_type storage_constructor , entry const& state) : m_counters(cnt) , m_dht(this, settings, extract_node_id(state), observer, cnt, storage_constructor) , m_sock(sock) , m_log(observer) , m_key_refresh_timer(sock.get_io_service()) , m_connection_timer(sock.get_io_service()) , m_refresh_timer(sock.get_io_service()) , m_settings(settings) , m_abort(false) , m_host_resolver(sock.get_io_service()) { #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::tracker, "starting DHT tracker with node id: %s" , to_hex(m_dht.nid().to_string()).c_str()); #endif } dht_tracker::~dht_tracker() {} void dht_tracker::update_node_id() { m_dht.update_node_id(); } // defined in node.cpp void nop(); void dht_tracker::start(entry const& bootstrap , find_data::nodes_callback const& f) { std::vector initial_nodes; if (bootstrap.type() == entry::dictionary_t) { TORRENT_TRY { if (entry const* nodes = bootstrap.find_key("nodes")) read_endpoint_list(nodes, initial_nodes); } TORRENT_CATCH(std::exception&) {} } error_code ec; refresh_key(ec); m_connection_timer.expires_from_now(seconds(1), ec); m_connection_timer.async_wait( boost::bind(&dht_tracker::connection_timeout, self(), _1)); m_refresh_timer.expires_from_now(seconds(5), ec); m_refresh_timer.async_wait(boost::bind(&dht_tracker::refresh_timeout, self(), _1)); m_dht.bootstrap(initial_nodes, f); } void dht_tracker::stop() { m_abort = true; error_code ec; m_key_refresh_timer.cancel(ec); m_connection_timer.cancel(ec); m_refresh_timer.cancel(ec); m_host_resolver.cancel(); } #ifndef TORRENT_NO_DEPRECATE void dht_tracker::dht_status(session_status& s) { m_dht.status(s); } #endif void dht_tracker::dht_status(std::vector& table , std::vector& requests) { m_dht.status(table, requests); } void dht_tracker::update_stats_counters(counters& c) const { m_dht.update_stats_counters(c); } void dht_tracker::connection_timeout(error_code const& e) { if (e || m_abort) return; time_duration d = m_dht.connection_timeout(); error_code ec; m_connection_timer.expires_from_now(d, ec); m_connection_timer.async_wait(boost::bind(&dht_tracker::connection_timeout, self(), _1)); } void dht_tracker::refresh_timeout(error_code const& e) { if (e || m_abort) return; m_dht.tick(); // periodically update the DOS blocker's settings from the dht_settings m_blocker.set_block_timer(m_settings.block_timeout); m_blocker.set_rate_limit(m_settings.block_ratelimit); error_code ec; m_refresh_timer.expires_from_now(seconds(5), ec); m_refresh_timer.async_wait( boost::bind(&dht_tracker::refresh_timeout, self(), _1)); } void dht_tracker::refresh_key(error_code const& e) { if (e || m_abort) return; error_code ec; m_key_refresh_timer.expires_from_now(key_refresh, ec); m_key_refresh_timer.async_wait(boost::bind(&dht_tracker::refresh_key, self(), _1)); m_dht.new_write_key(); #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::tracker, "*** new write key***"); #endif } /* #if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM std::ofstream st("dht_routing_table_state.txt", std::ios_base::trunc); m_dht.print_state(st); #endif */ void dht_tracker::get_peers(sha1_hash const& ih , boost::function const&)> f) { // TODO: Use `{}` instead of spelling out the whole function type when this is merged to master m_dht.get_peers(ih, f , boost::function > const&)>() , false); } void dht_tracker::announce(sha1_hash const& ih, int listen_port, int flags , boost::function const&)> f) { m_dht.announce(ih, listen_port, flags, f); } void dht_tracker::get_item(sha1_hash const& target , boost::function cb) { m_dht.get_item(target, cb); } // key is a 32-byte binary string, the public key to look up. // the salt is optional void dht_tracker::get_item(char const* key , boost::function cb , std::string salt) { m_dht.get_item(key, salt, cb); } void dht_tracker::put_item(entry const& data , boost::function cb) { std::string flat_data; bencode(std::back_inserter(flat_data), data); sha1_hash target = item_target_id( std::pair(flat_data.c_str(), flat_data.size())); m_dht.put_item(target, data, cb); } void dht_tracker::put_item(char const* key , boost::function cb , boost::function data_cb, std::string salt) { m_dht.put_item(key, salt, cb, data_cb); } void dht_tracker::direct_request(udp::endpoint ep, entry& e , boost::function f) { m_dht.direct_request(ep, e, f); } // translate bittorrent kademlia message into the generice kademlia message // used by the library bool dht_tracker::incoming_packet(error_code const& ec , udp::endpoint const& ep, char const* buf, int size) { if (ec) { if (ec == boost::asio::error::connection_refused || ec == boost::asio::error::connection_reset || ec == boost::asio::error::connection_aborted #ifdef _WIN32 || ec == error_code(ERROR_HOST_UNREACHABLE, system_category()) || ec == error_code(ERROR_PORT_UNREACHABLE, system_category()) || ec == error_code(ERROR_CONNECTION_REFUSED, system_category()) || ec == error_code(ERROR_CONNECTION_ABORTED, system_category()) #endif ) { m_dht.unreachable(ep); } return false; } if (size <= 20 || *buf != 'd' || buf[size-1] != 'e') return false; // remove this line/check once the DHT supports IPv6 if (!ep.address().is_v4()) return false; m_counters.inc_stats_counter(counters::dht_bytes_in, size); // account for IP and UDP overhead m_counters.inc_stats_counter(counters::recv_ip_overhead_bytes , ep.address().is_v6() ? 48 : 28); m_counters.inc_stats_counter(counters::dht_messages_in); if (m_settings.ignore_dark_internet && ep.address().is_v4()) { address_v4::bytes_type b = ep.address().to_v4().to_bytes(); // these are class A networks not available to the public // if we receive messages from here, that seems suspicious boost::uint8_t class_a[] = { 3, 6, 7, 9, 11, 19, 21, 22, 25 , 26, 28, 29, 30, 33, 34, 48, 51, 56 }; int num = sizeof(class_a)/sizeof(class_a[0]); if (std::find(class_a, class_a + num, b[0]) != class_a + num) return true; } if (!m_blocker.incoming(ep.address(), clock_type::now(), m_log)) return true; using libtorrent::entry; using libtorrent::bdecode; TORRENT_ASSERT(size > 0); int pos; error_code err; int ret = bdecode(buf, buf + size, m_msg, err, &pos, 10, 500); if (ret != 0) { #ifndef TORRENT_DISABLE_LOGGING m_log->log_packet(dht_logger::incoming_message, buf, size, ep); #endif return false; } if (m_msg.type() != bdecode_node::dict_t) { #ifndef TORRENT_DISABLE_LOGGING m_log->log_packet(dht_logger::incoming_message, buf, size, ep); #endif // it's not a good idea to send a response to an invalid messages return false; } #ifndef TORRENT_DISABLE_LOGGING m_log->log_packet(dht_logger::incoming_message, buf , size, ep); #endif libtorrent::dht::msg m(m_msg, ep); m_dht.incoming(m); return true; } namespace { void add_node_fun(void* userdata, node_entry const& e) { entry* n = static_cast(userdata); std::string node; std::back_insert_iterator out(node); write_endpoint(e.ep(), out); n->list().push_back(entry(node)); } } // anonymous namespace entry dht_tracker::state() const { entry ret(entry::dictionary_t); { entry nodes(entry::list_t); m_dht.m_table.for_each_node(&add_node_fun, &add_node_fun, &nodes); if (!nodes.list().empty()) ret["nodes"] = nodes; } ret["node-id"] = m_dht.nid().to_string(); return ret; } void dht_tracker::add_node(udp::endpoint node) { #if !TORRENT_USE_IPV6 TORRENT_ASSERT(node.address().is_v4()); #endif m_dht.add_node(node); } void dht_tracker::add_router_node(udp::endpoint const& node) { #if !TORRENT_USE_IPV6 TORRENT_ASSERT(node.address().is_v4()); #endif m_dht.add_router_node(node); } bool dht_tracker::has_quota() { return m_sock.has_quota(); } bool dht_tracker::send_packet(libtorrent::entry& e, udp::endpoint const& addr, int send_flags) { using libtorrent::bencode; using libtorrent::entry; static char const version_str[] = {'L', 'T' , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR}; e["v"] = std::string(version_str, version_str + 4); m_send_buf.clear(); bencode(std::back_inserter(m_send_buf), e); error_code ec; bool ret = m_sock.send(addr, &m_send_buf[0], int(m_send_buf.size()), ec, send_flags); if (!ret || ec) { m_counters.inc_stats_counter(counters::dht_messages_out_dropped); #ifndef TORRENT_DISABLE_LOGGING m_log->log_packet(dht_logger::outgoing_message, &m_send_buf[0] , m_send_buf.size(), addr); #endif return false; } m_counters.inc_stats_counter(counters::dht_bytes_out, m_send_buf.size()); // account for IP and UDP overhead m_counters.inc_stats_counter(counters::sent_ip_overhead_bytes , addr.address().is_v6() ? 48 : 28); m_counters.inc_stats_counter(counters::dht_messages_out); #ifndef TORRENT_DISABLE_LOGGING m_log->log_packet(dht_logger::outgoing_message, &m_send_buf[0] , m_send_buf.size(), addr); #endif return true; } }} libtorrent-rasterbar-1.1.13/src/kademlia/dos_blocker.cpp000066400000000000000000000064561351156116000233060ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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/kademlia/dos_blocker.hpp" #ifndef TORRENT_DISABLE_LOGGING #include "libtorrent/socket_io.hpp" // for print_address #include "libtorrent/kademlia/dht_observer.hpp" // for dht_logger #endif namespace libtorrent { namespace dht { dos_blocker::dos_blocker() : m_message_rate_limit(5) , m_block_timeout(5 * 60) { for (int i = 0; i < num_ban_nodes; ++i) { m_ban_nodes[i].count = 0; m_ban_nodes[i].limit = min_time(); } } bool dos_blocker::incoming(address addr, time_point now, dht_logger* logger) { TORRENT_UNUSED(logger); node_ban_entry* match = 0; node_ban_entry* min = m_ban_nodes; for (node_ban_entry* i = m_ban_nodes; i < m_ban_nodes + num_ban_nodes; ++i) { if (i->src == addr) { match = i; break; } if (i->count < min->count) min = i; else if (i->count == min->count && i->limit < min->limit) min = i; } if (match) { ++match->count; if (match->count >= m_message_rate_limit * 10) { if (now < match->limit) { if (match->count == m_message_rate_limit * 10) { #ifndef TORRENT_DISABLE_LOGGING logger->log(dht_logger::tracker, "BANNING PEER [ ip: %s time: %d ms count: %d ]" , print_address(addr).c_str() , int(total_milliseconds((now - match->limit) + seconds(10))) , int(match->count)); #endif // we've received too many messages in less than 10 seconds // from this node. Ignore it until it's silent for 5 minutes match->limit = now + seconds(m_block_timeout); } return false; } // the messages we received from this peer took more than 10 // seconds. Reset the counter and the timer match->count = 0; match->limit = now + seconds(10); } } else { min->count = 1; min->limit = now + seconds(10); min->src = addr; } return true; } }} libtorrent-rasterbar-1.1.13/src/kademlia/find_data.cpp000066400000000000000000000125551351156116000227260ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include #include #include #include #include namespace libtorrent { namespace dht { using detail::read_endpoint_list; using detail::read_v4_endpoint; #if TORRENT_USE_IPV6 using detail::read_v6_endpoint; #endif void find_data_observer::reply(msg const& m) { bdecode_node r = m.message.dict_find_dict("r"); if (!r) { #ifndef TORRENT_DISABLE_LOGGING get_observer()->log(dht_logger::traversal, "[%p] missing response dict" , static_cast(algorithm())); #endif timeout(); return; } bdecode_node id = r.dict_find_string("id"); if (!id || id.string_length() != 20) { #ifndef TORRENT_DISABLE_LOGGING get_observer()->log(dht_logger::traversal, "[%p] invalid id in response" , static_cast(algorithm())); #endif timeout(); return; } bdecode_node token = r.dict_find_string("token"); if (token) { static_cast(algorithm())->got_write_token( node_id(id.string_ptr()), token.string_value()); } traversal_observer::reply(m); done(); } find_data::find_data( node& dht_node , node_id target , nodes_callback const& ncallback) : traversal_algorithm(dht_node, target) , m_nodes_callback(ncallback) , m_done(false) { } void find_data::start() { // if the user didn't add seed-nodes manually, grab k (bucket size) // nodes from routing table. if (m_results.empty()) { std::vector nodes; m_node.m_table.find_node(m_target, nodes, routing_table::include_failed); for (std::vector::iterator i = nodes.begin() , end(nodes.end()); i != end; ++i) { add_entry(i->id, i->ep(), observer::flag_initial); } } traversal_algorithm::start(); } void find_data::got_write_token(node_id const& n, std::string const& write_token) { #ifndef TORRENT_DISABLE_LOGGING get_node().observer()->log(dht_logger::traversal , "[%p] adding write token '%s' under id '%s'" , static_cast(this), to_hex(write_token).c_str() , to_hex(n.to_string()).c_str()); #endif m_write_tokens[n] = write_token; } observer_ptr find_data::new_observer(void* ptr , udp::endpoint const& ep, node_id const& id) { observer_ptr o(new (ptr) find_data_observer(this, ep, id)); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS o->m_in_constructor = false; #endif return o; } char const* find_data::name() const { return "find_data"; } void find_data::done() { m_done = true; #ifndef TORRENT_DISABLE_LOGGING get_node().observer()->log(dht_logger::traversal, "[%p] %s DONE" , static_cast(this), name()); #endif std::vector > results; int num_results = m_node.m_table.bucket_size(); for (std::vector::iterator i = m_results.begin() , end(m_results.end()); i != end && num_results > 0; ++i) { observer_ptr const& o = *i; if ((o->flags & observer::flag_alive) == 0) { #ifndef TORRENT_DISABLE_LOGGING get_node().observer()->log(dht_logger::traversal, "[%p] not alive: %s" , static_cast(this), print_endpoint(o->target_ep()).c_str()); #endif continue; } std::map::iterator j = m_write_tokens.find(o->id()); if (j == m_write_tokens.end()) { #ifndef TORRENT_DISABLE_LOGGING get_node().observer()->log(dht_logger::traversal, "[%p] no write token: %s" , static_cast(this), print_endpoint(o->target_ep()).c_str()); #endif continue; } results.push_back(std::make_pair(node_entry(o->id(), o->target_ep()), j->second)); #ifndef TORRENT_DISABLE_LOGGING get_node().observer()->log(dht_logger::traversal, "[%p] %s" , static_cast(this), print_endpoint(o->target_ep()).c_str()); #endif --num_results; } if (m_nodes_callback) m_nodes_callback(results); traversal_algorithm::done(); } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/get_item.cpp000066400000000000000000000140661351156116000226110ustar00rootroot00000000000000/* Copyright (c) 2013, Steven Siloti 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 #include #include #include #include #include #if TORRENT_USE_ASSERTS #include #endif namespace libtorrent { namespace dht { void get_item::got_data(bdecode_node const& v, char const* pk, boost::uint64_t seq, char const* sig) { // we received data! // if no data_callback, we needn't care about the data we get. // only put_immutable_item no data_callback if (!m_data_callback) return; // for get_immutable_item if (m_immutable) { // If m_data isn't empty, we should have post alert. if (!m_data.empty()) return; sha1_hash incoming_target = item_target_id(v.data_section()); if (incoming_target != m_target) return; m_data.assign(v); // There can only be one true immutable item with a given id // Now that we've got it and the user doesn't want to do a put // there's no point in continuing to query other nodes m_data_callback(m_data, true); done(); return; } // immutalbe data should has been handled before this line, only mutable // data can reach here, which means pk and sig must be valid. if (!pk || !sig) return; std::string temp_copy(m_data.salt()); std::pair salt(temp_copy.c_str(), int(temp_copy.size())); sha1_hash incoming_target = item_target_id(salt, pk); if (incoming_target != m_target) return; // this is mutable data. If it passes the signature // check, remember it. Just keep the version with // the highest sequence number. if (m_data.empty() || m_data.seq() < seq) { if (!m_data.assign(v, salt, seq, pk, sig)) return; // for get_item, we should call callback when we get data, // even if the date is not authoritative, we can update later. // so caller can get response ASAP without waitting transaction // time-out (15 seconds). // for put_item, the callback function will do nothing // if the data is non-authoritative. m_data_callback(m_data, false); } } get_item::get_item( node& dht_node , node_id target , data_callback const& dcallback , nodes_callback const& ncallback) : find_data(dht_node, target, ncallback) , m_data_callback(dcallback) , m_immutable(true) { } get_item::get_item( node& dht_node , char const* pk , std::string const& salt , data_callback const& dcallback , nodes_callback const& ncallback) : find_data(dht_node, item_target_id( std::make_pair(salt.c_str(), int(salt.size())), pk) , ncallback) , m_data_callback(dcallback) , m_data(pk, salt) , m_immutable(false) { } char const* get_item::name() const { return "get"; } observer_ptr get_item::new_observer(void* ptr , udp::endpoint const& ep, node_id const& id) { observer_ptr o(new (ptr) get_item_observer(this, ep, id)); #if TORRENT_USE_ASSERTS o->m_in_constructor = false; #endif return o; } bool get_item::invoke(observer_ptr o) { if (m_done) return false; entry e; e["y"] = "q"; entry& a = e["a"]; e["q"] = "get"; a["target"] = m_target.to_string(); return m_node.m_rpc.invoke(e, o->target_ep(), o); } void get_item::done() { // no data_callback for immutable item put if (!m_data_callback) return find_data::done(); if (m_data.is_mutable() || m_data.empty()) { // for mutable data, now we have authoritative data since // we've heard from everyone, to be sure we got the // latest version of the data (i.e. highest sequence number) m_data_callback(m_data, true); #if TORRENT_USE_ASSERTS if (m_data.is_mutable()) { TORRENT_ASSERT(m_target == item_target_id(std::pair(m_data.salt().c_str() , m_data.salt().size()) , m_data.pk().data())); } #endif } find_data::done(); } void get_item_observer::reply(msg const& m) { char const* pk = NULL; char const* sig = NULL; boost::uint64_t seq = 0; bdecode_node r = m.message.dict_find_dict("r"); if (!r) { #ifndef TORRENT_DISABLE_LOGGING get_observer()->log(dht_logger::traversal, "[%p] missing response dict" , static_cast(algorithm())); #endif timeout(); return; } bdecode_node k = r.dict_find_string("k"); if (k && k.string_length() == item_pk_len) pk = k.string_ptr(); bdecode_node s = r.dict_find_string("sig"); if (s && s.string_length() == item_sig_len) sig = s.string_ptr(); bdecode_node q = r.dict_find_int("seq"); if (q) seq = q.int_value(); else if (pk && sig) { timeout(); return; } bdecode_node v = r.dict_find("v"); if (v) { static_cast(algorithm())->got_data(v, pk, seq, sig); } find_data_observer::reply(m); } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/get_peers.cpp000066400000000000000000000226351351156116000227720ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include #include #include namespace libtorrent { namespace dht { using detail::read_endpoint_list; using detail::read_v4_endpoint; #if TORRENT_USE_IPV6 using detail::read_v6_endpoint; #endif void get_peers_observer::reply(msg const& m) { bdecode_node r = m.message.dict_find_dict("r"); if (!r) { #ifndef TORRENT_DISABLE_LOGGING get_observer()->log(dht_logger::traversal, "[%p] missing response dict" , static_cast(algorithm())); #endif timeout(); return; } // look for peers bdecode_node n = r.dict_find_list("values"); if (n) { std::vector peer_list; if (n.list_size() == 1 && n.list_at(0).type() == bdecode_node::string_t) { // assume it's mainline format char const* peers = n.list_at(0).string_ptr(); char const* end = peers + n.list_at(0).string_length(); #ifndef TORRENT_DISABLE_LOGGING bdecode_node id = r.dict_find_string("id"); if (id && id.string_length() == 20) { get_observer()->log(dht_logger::traversal, "[%p] PEERS " "invoke-count: %d branch-factor: %d addr: %s id: %s distance: %d p: %d" , static_cast(algorithm()) , algorithm()->invoke_count() , algorithm()->branch_factor() , print_endpoint(m.addr).c_str() , to_hex(id.string_value()).c_str() , distance_exp(algorithm()->target(), node_id(id.string_ptr())) , int((end - peers) / 6)); } #endif while (end - peers >= 6) peer_list.push_back(read_v4_endpoint(peers)); } else { // assume it's uTorrent/libtorrent format read_endpoint_list(n, peer_list); #ifndef TORRENT_DISABLE_LOGGING bdecode_node id = r.dict_find_string("id"); if (id && id.string_length() == 20) { get_observer()->log(dht_logger::traversal, "[%p] PEERS " "invoke-count: %d branch-factor: %d addr: %s id: %s distance: %d p: %d" , static_cast(algorithm()) , algorithm()->invoke_count() , algorithm()->branch_factor() , print_endpoint(m.addr).c_str() , to_hex(id.string_value()).c_str() , distance_exp(algorithm()->target(), node_id(id.string_ptr())) , int(n.list_size())); } #endif } static_cast(algorithm())->got_peers(peer_list); } find_data_observer::reply(m); } void get_peers::got_peers(std::vector const& peers) { if (m_data_callback) m_data_callback(peers); } get_peers::get_peers( node& dht_node , node_id target , data_callback const& dcallback , nodes_callback const& ncallback , bool noseeds) : find_data(dht_node, target, ncallback) , m_data_callback(dcallback) , m_noseeds(noseeds) { } char const* get_peers::name() const { return "get_peers"; } bool get_peers::invoke(observer_ptr o) { if (m_done) return false; entry e; e["y"] = "q"; entry& a = e["a"]; e["q"] = "get_peers"; a["info_hash"] = m_target.to_string(); if (m_noseeds) a["noseed"] = 1; if (m_node.observer()) { m_node.observer()->outgoing_get_peers(m_target, m_target, o->target_ep()); } m_node.stats_counters().inc_stats_counter(counters::dht_get_peers_out); return m_node.m_rpc.invoke(e, o->target_ep(), o); } observer_ptr get_peers::new_observer(void* ptr , udp::endpoint const& ep, node_id const& id) { observer_ptr o(new (ptr) get_peers_observer(this, ep, id)); #if TORRENT_USE_ASSERTS o->m_in_constructor = false; #endif return o; } obfuscated_get_peers::obfuscated_get_peers( node& dht_node , node_id info_hash , data_callback const& dcallback , nodes_callback const& ncallback , bool noseeds) : get_peers(dht_node, info_hash, dcallback, ncallback, noseeds) , m_obfuscated(true) { } char const* obfuscated_get_peers::name() const { return !m_obfuscated ? get_peers::name() : "get_peers [obfuscated]"; } observer_ptr obfuscated_get_peers::new_observer(void* ptr , udp::endpoint const& ep, node_id const& id) { if (m_obfuscated) { observer_ptr o(new (ptr) obfuscated_get_peers_observer(this, ep, id)); #if TORRENT_USE_ASSERTS o->m_in_constructor = false; #endif return o; } else { observer_ptr o(new (ptr) get_peers_observer(this, ep, id)); #if TORRENT_USE_ASSERTS o->m_in_constructor = false; #endif return o; } } bool obfuscated_get_peers::invoke(observer_ptr o) { if (!m_obfuscated) return get_peers::invoke(o); const node_id id = o->id(); const int shared_prefix = 160 - distance_exp(id, m_target); // when we get close to the target zone in the DHT // start using the correct info-hash, in order to // start receiving peers if (shared_prefix > m_node.m_table.depth() - 4) { m_obfuscated = false; // clear the queried bits on all successful nodes in // our node-list for this traversal algorithm, to // allow the get_peers traversal to regress in case // nodes further down end up being dead for (std::vector::iterator i = m_results.begin() , end(m_results.end()); i != end; ++i) { observer* const node = i->get(); // don't re-request from nodes that didn't respond if (node->flags & observer::flag_failed) continue; // don't interrupt with queries that are already in-flight if ((node->flags & observer::flag_alive) == 0) continue; node->flags &= ~(observer::flag_queried | observer::flag_alive); } return get_peers::invoke(o); } entry e; e["y"] = "q"; e["q"] = "get_peers"; entry& a = e["a"]; // This logic will obfuscate the target info-hash // we're looking up, in order to preserve more privacy // on the DHT. This is done by only including enough // bits in the info-hash for the node we're querying to // give a good answer, but not more. // now, obfuscate the bits past shared_prefix + 3 node_id mask = generate_prefix_mask(shared_prefix + 3); node_id obfuscated_target = generate_random_id() & ~mask; obfuscated_target |= m_target & mask; a["info_hash"] = obfuscated_target.to_string(); if (m_node.observer()) { m_node.observer()->outgoing_get_peers(m_target, obfuscated_target , o->target_ep()); } m_node.stats_counters().inc_stats_counter(counters::dht_get_peers_out); return m_node.m_rpc.invoke(e, o->target_ep(), o); } void obfuscated_get_peers::done() { if (!m_obfuscated) return get_peers::done(); // oops, we failed to switch over to the non-obfuscated // mode early enough. do it now boost::intrusive_ptr ta(new get_peers(m_node, m_target , m_data_callback , m_nodes_callback , m_noseeds)); // don't call these when the obfuscated_get_peers // is done, we're passing them on to be called when // ta completes. m_data_callback.clear(); m_nodes_callback.clear(); #ifndef TORRENT_DISABLE_LOGGING get_node().observer()->log(dht_logger::traversal, "[%p] obfuscated get_peers " "phase 1 done, spawning get_peers [ %p ]" , static_cast(this) , static_cast(ta.get())); #endif int num_added = 0; for (std::vector::iterator i = m_results.begin() , end(m_results.end()); i != end && num_added < 16; ++i) { observer_ptr o = *i; // only add nodes whose node ID we know and that // we know are alive if (o->flags & observer::flag_no_id) continue; if ((o->flags & observer::flag_alive) == 0) continue; ta->add_entry(o->id(), o->target_ep(), observer::flag_initial); ++num_added; } ta->start(); get_peers::done(); } void obfuscated_get_peers_observer::reply(msg const& m) { bdecode_node r = m.message.dict_find_dict("r"); if (!r) { #ifndef TORRENT_DISABLE_LOGGING get_observer()->log(dht_logger::traversal, "[%p] missing response dict" , static_cast(algorithm())); #endif timeout(); return; } bdecode_node id = r.dict_find_string("id"); if (!id || id.string_length() != 20) { #ifndef TORRENT_DISABLE_LOGGING get_observer()->log(dht_logger::traversal, "[%p] invalid id in response" , static_cast(algorithm())); #endif timeout(); return; } traversal_observer::reply(m); done(); } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/item.cpp000066400000000000000000000152241351156116000217470ustar00rootroot00000000000000/* Copyright (c) 2013, Steven Siloti 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 #include #include #include #ifdef TORRENT_DEBUG #include "libtorrent/bdecode.hpp" #endif #ifdef TORRENT_USE_VALGRIND #include #endif namespace libtorrent { namespace dht { namespace { enum { canonical_length = 1200 }; int canonical_string(std::pair v, boost::uint64_t seq , std::pair salt, char out[canonical_length]) { // v must be valid bencoding! #ifdef TORRENT_DEBUG bdecode_node e; error_code ec; TORRENT_ASSERT(bdecode(v.first, v.first + v.second, e, ec) == 0); #endif char* ptr = out; int left = canonical_length - (ptr - out); if (salt.second > 0) { ptr += snprintf(ptr, left, "4:salt%d:", salt.second); left = canonical_length - (ptr - out); memcpy(ptr, salt.first, (std::min)(salt.second, left)); ptr += (std::min)(salt.second, left); left = canonical_length - (ptr - out); } ptr += snprintf(ptr, canonical_length - (ptr - out) , "3:seqi%" PRId64 "e1:v", seq); left = canonical_length - (ptr - out); memcpy(ptr, v.first, (std::min)(v.second, left)); ptr += (std::min)(v.second, left); TORRENT_ASSERT((ptr - out) <= canonical_length); return ptr - out; } } // calculate the target hash for an immutable item. sha1_hash item_target_id(std::pair v) { hasher h; h.update(v.first, v.second); return h.final(); } // calculate the target hash for a mutable item. sha1_hash item_target_id(std::pair salt , char const* pk) { hasher h; h.update(pk, item_pk_len); if (salt.second > 0) h.update(salt.first, salt.second); return h.final(); } bool verify_mutable_item( std::pair v , std::pair salt , boost::uint64_t seq , char const* pk , char const* sig) { #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_MEM_IS_DEFINED(v.first, v.second); VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len); VALGRIND_CHECK_MEM_IS_DEFINED(sig, item_sig_len); #endif char str[canonical_length]; int len = canonical_string(v, seq, salt, str); return ed25519_verify(reinterpret_cast(sig) , reinterpret_cast(str) , len , reinterpret_cast(pk)) == 1; } // given the bencoded buffer ``v``, the salt (which is optional and may have // a length of zero to be omitted), sequence number ``seq``, public key (32 // bytes ed25519 key) ``pk`` and a secret/private key ``sk`` (64 bytes ed25519 // key) a signature ``sig`` is produced. The ``sig`` pointer must point to // at least 64 bytes of available space. This space is where the signature is // written. void sign_mutable_item( std::pair v , std::pair salt , boost::uint64_t seq , char const* pk , char const* sk , char* sig) { #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_MEM_IS_DEFINED(v.first, v.second); VALGRIND_CHECK_MEM_IS_DEFINED(sk, item_sk_len); VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len); #endif char str[canonical_length]; int len = canonical_string(v, seq, salt, str); ed25519_sign(reinterpret_cast(sig) , reinterpret_cast(str) , len , reinterpret_cast(pk) , reinterpret_cast(sk) ); } item::item(char const* pk, std::string const& salt) : m_salt(salt) , m_seq(0) , m_mutable(true) { memcpy(m_pk.data(), pk, item_pk_len); } item::item(entry const& v , std::pair salt , boost::uint64_t seq, char const* pk, char const* sk) { assign(v, salt, seq, pk, sk); } void item::assign(entry const& v, std::pair salt , boost::uint64_t seq, char const* pk, char const* sk) { m_value = v; if (pk && sk) { char buffer[1000]; int bsize = bencode(buffer, v); TORRENT_ASSERT(bsize <= 1000); sign_mutable_item(std::make_pair(buffer, bsize) , salt, seq, pk, sk, m_sig.c_array()); m_salt.assign(salt.first, salt.second); memcpy(m_pk.c_array(), pk, item_pk_len); m_seq = seq; m_mutable = true; } else m_mutable = false; } bool item::assign(bdecode_node const& v , std::pair salt , boost::uint64_t seq, char const* pk, char const* sig) { TORRENT_ASSERT(v.data_section().second <= 1000); if (pk && sig) { if (!verify_mutable_item(v.data_section(), salt, seq, pk, sig)) return false; memcpy(m_pk.c_array(), pk, item_pk_len); memcpy(m_sig.c_array(), sig, item_sig_len); if (salt.second > 0) m_salt.assign(salt.first, salt.second); else m_salt.clear(); m_seq = seq; m_mutable = true; } else m_mutable = false; m_value = v; return true; } void item::assign(entry const& v, std::string salt, boost::uint64_t seq , char const* pk, char const* sig) { #if TORRENT_USE_ASSERTS TORRENT_ASSERT(pk && sig); char buffer[1000]; int bsize = bencode(buffer, v); TORRENT_ASSERT(bsize <= 1000); TORRENT_ASSERT(verify_mutable_item( std::make_pair(buffer, bsize) , std::make_pair(salt.data(), int(salt.size())) , seq, pk, sig)); #endif memcpy(m_pk.c_array(), pk, item_pk_len); memcpy(m_sig.c_array(), sig, item_sig_len); m_salt = salt; m_seq = seq; m_mutable = true; m_value = v; } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/msg.cpp000066400000000000000000000102531351156116000215740ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/kademlia/msg.hpp" #include "libtorrent/bdecode.hpp" #include "libtorrent/entry.hpp" namespace libtorrent { namespace dht { namespace dht_detail { bool verify_message(bdecode_node const& message, key_desc_t const desc[] , bdecode_node ret[], int size, char* error, int error_size) { // get a non-root bdecode_node that still // points to the root. message should not be copied bdecode_node msg = message.non_owning(); // clear the return buffer for (int i = 0; i < size; ++i) ret[i].clear(); // when parsing child nodes, this is the stack // of bdecode_nodes to return to bdecode_node stack[5]; int stack_ptr = -1; if (msg.type() != bdecode_node::dict_t) { snprintf(error, error_size, "not a dictionary"); return false; } ++stack_ptr; stack[stack_ptr] = msg; for (int i = 0; i < size; ++i) { key_desc_t const& k = desc[i]; // fprintf(stderr, "looking for %s in %s\n", k.name, print_entry(*msg).c_str()); ret[i] = msg.dict_find(k.name); // none_t means any type if (ret[i] && ret[i].type() != k.type && k.type != bdecode_node::none_t) ret[i].clear(); if (ret[i] == 0 && (k.flags & key_desc_t::optional) == 0) { // the key was not found, and it's not an optional key snprintf(error, error_size, "missing '%s' key", k.name); return false; } if (k.size > 0 && ret[i] && k.type == bdecode_node::string_t) { bool invalid = false; if (k.flags & key_desc_t::size_divisible) invalid = (ret[i].string_length() % k.size) != 0; else invalid = ret[i].string_length() != k.size; if (invalid) { // the string was not of the required size ret[i].clear(); if ((k.flags & key_desc_t::optional) == 0) { snprintf(error, error_size, "invalid value for '%s'", k.name); return false; } } } if (k.flags & key_desc_t::parse_children) { TORRENT_ASSERT(k.type == bdecode_node::dict_t); if (ret[i]) { ++stack_ptr; TORRENT_ASSERT(stack_ptr < int(sizeof(stack) / sizeof(stack[0]))); msg = ret[i]; stack[stack_ptr] = msg; } else { // skip all children while (i < size && (desc[i].flags & key_desc_t::last_child) == 0) ++i; // if this assert is hit, desc is incorrect TORRENT_ASSERT(i < size); } } else if (k.flags & key_desc_t::last_child) { TORRENT_ASSERT(stack_ptr > 0); // this can happen if the specification passed // in is unbalanced. i.e. contain more last_child // nodes than parse_children if (stack_ptr == 0) return false; --stack_ptr; msg = stack[stack_ptr]; } } return true; } } void incoming_error(entry& e, char const* msg, int error_code) { e["y"] = "e"; entry::list_type& l = e["e"].list(); l.push_back(entry(error_code)); l.push_back(entry(msg)); } } } libtorrent-rasterbar-1.1.13/src/kademlia/node.cpp000066400000000000000000001006741351156116000217420ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #ifdef TORRENT_USE_VALGRIND #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/io.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/random.hpp" #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/alert_types.hpp" // for dht_lookup #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/kademlia/node_id.hpp" #include "libtorrent/kademlia/rpc_manager.hpp" #include "libtorrent/kademlia/routing_table.hpp" #include "libtorrent/kademlia/node.hpp" #include "libtorrent/kademlia/dht_observer.hpp" #include "libtorrent/kademlia/direct_request.hpp" #include "libtorrent/kademlia/refresh.hpp" #include "libtorrent/kademlia/get_peers.hpp" #include "libtorrent/kademlia/get_item.hpp" namespace libtorrent { namespace dht { using detail::write_endpoint; namespace { void nop() {} node_id calculate_node_id(node_id const& nid, dht_observer* observer) { address external_address; if (observer) external_address = observer->external_address(); // if we don't have an observer, don't pretend that external_address is valid // generating an ID based on 0.0.0.0 would be terrible. random is better if (!observer || external_address == address()) { return generate_random_id(); } if (nid == (node_id::min)() || !verify_id(nid, external_address)) return generate_id(external_address); return nid; } } // anonymous namespace node::node(udp_socket_interface* sock , dht_settings const& settings, node_id nid , dht_observer* observer , struct counters& cnt , dht_storage_constructor_type storage_constructor) : m_settings(settings) , m_id(calculate_node_id(nid, observer)) , m_table(m_id, 8, settings, observer) , m_rpc(m_id, m_settings, m_table, sock, observer) , m_observer(observer) , m_last_tracker_tick(aux::time_now()) , m_last_self_refresh(min_time()) , m_sock(sock) , m_counters(cnt) , m_storage(storage_constructor(m_id, m_settings)) { m_secret[0] = random(); m_secret[1] = random(); TORRENT_ASSERT(m_storage.get() != NULL); } node::~node() {} void node::update_node_id() { // if we don't have an observer, we can't ask for the external IP (and our // current node ID is likely not generated from an external address), so we // can just stop here in that case. if (!m_observer) return; // it's possible that our external address hasn't actually changed. If our // current ID is still valid, don't do anything. if (verify_id(m_id, m_observer->external_address())) return; #ifndef TORRENT_DISABLE_LOGGING if (m_observer) m_observer->log(dht_logger::node , "updating node ID (because external IP address changed)"); #endif m_id = generate_id(m_observer->external_address()); m_table.update_node_id(m_id); m_rpc.update_node_id(m_id); } bool node::verify_token(std::string const& token, char const* info_hash , udp::endpoint const& addr) const { if (token.length() != 4) { #ifndef TORRENT_DISABLE_LOGGING if (m_observer) { m_observer->log(dht_logger::node, "token of incorrect length: %d" , int(token.length())); } #endif return false; } hasher h1; error_code ec; std::string address = addr.address().to_string(ec); if (ec) return false; h1.update(&address[0], address.length()); h1.update(reinterpret_cast(&m_secret[0]), sizeof(m_secret[0])); h1.update(reinterpret_cast(info_hash), sha1_hash::size); sha1_hash h = h1.final(); if (std::equal(token.begin(), token.end(), reinterpret_cast(&h[0]))) return true; hasher h2; h2.update(&address[0], address.length()); h2.update(reinterpret_cast(&m_secret[1]), sizeof(m_secret[1])); h2.update(info_hash, sha1_hash::size); h = h2.final(); if (std::equal(token.begin(), token.end(), reinterpret_cast(&h[0]))) return true; return false; } std::string node::generate_token(udp::endpoint const& addr, char const* info_hash) { std::string token; token.resize(4); hasher h; error_code ec; std::string address = addr.address().to_string(ec); TORRENT_ASSERT(!ec); h.update(&address[0], address.length()); h.update(reinterpret_cast(&m_secret[0]), sizeof(m_secret[0])); h.update(info_hash, sha1_hash::size); sha1_hash hash = h.final(); std::copy(hash.begin(), hash.begin() + 4, reinterpret_cast(&token[0])); TORRENT_ASSERT(std::equal(token.begin(), token.end(), reinterpret_cast(&hash[0]))); return token; } void node::bootstrap(std::vector const& nodes , find_data::nodes_callback const& f) { node_id target = m_id; make_id_secret(target); boost::intrusive_ptr r(new dht::bootstrap(*this, target, f)); m_last_self_refresh = aux::time_now(); #ifndef TORRENT_DISABLE_LOGGING int count = 0; #endif for (std::vector::const_iterator i = nodes.begin() , end(nodes.end()); i != end; ++i) { #ifndef TORRENT_DISABLE_LOGGING ++count; #endif r->add_entry(node_id(0), *i, observer::flag_initial); } #ifndef TORRENT_DISABLE_LOGGING if (m_observer) m_observer->log(dht_logger::node, "bootstrapping with %d nodes", count); #endif r->start(); } int node::bucket_size(int bucket) { return m_table.bucket_size(bucket); } void node::new_write_key() { m_secret[1] = m_secret[0]; m_secret[0] = random(); } void node::unreachable(udp::endpoint const& ep) { m_rpc.unreachable(ep); } void node::incoming(msg const& m) { // is this a reply? bdecode_node y_ent = m.message.dict_find_string("y"); if (!y_ent || y_ent.string_length() == 0) { // don't respond to this obviously broken messages. We don't // want to open up a magnification opportunity // entry e; // incoming_error(e, "missing 'y' entry"); // m_sock.send_packet(e, m.addr, 0); return; } char y = *(y_ent.string_ptr()); bdecode_node ext_ip = m.message.dict_find_string("ip"); // backwards compatibility if (!ext_ip) { bdecode_node r = m.message.dict_find_dict("r"); if (r) ext_ip = r.dict_find_string("ip"); } #if TORRENT_USE_IPV6 if (ext_ip && ext_ip.string_length() >= 16) { // this node claims we use the wrong node-ID! address_v6::bytes_type b; memcpy(&b[0], ext_ip.string_ptr(), 16); if (m_observer) m_observer->set_external_address(address_v6(b) , m.addr.address()); } else #endif if (ext_ip && ext_ip.string_length() >= 4) { address_v4::bytes_type b; memcpy(&b[0], ext_ip.string_ptr(), 4); if (m_observer) m_observer->set_external_address(address_v4(b) , m.addr.address()); } switch (y) { case 'r': { node_id id; m_rpc.incoming(m, &id); break; } case 'q': { TORRENT_ASSERT(m.message.dict_find_string_value("y") == "q"); // When a DHT node enters the read-only state, it no longer // responds to 'query' messages that it receives. if (m_settings.read_only) break; entry e; incoming_request(m, e); m_sock->send_packet(e, m.addr, 0); break; } case 'e': { #ifndef TORRENT_DISABLE_LOGGING if (m_observer) { bdecode_node err = m.message.dict_find_list("e"); if (err && err.list_size() >= 2 && err.list_at(0).type() == bdecode_node::int_t && err.list_at(1).type() == bdecode_node::string_t && m_observer) { m_observer->log(dht_logger::node, "INCOMING ERROR: (%" PRId64 ") %s" , err.list_int_value_at(0) , err.list_string_value_at(1).c_str()); } else { m_observer->log(dht_logger::node, "INCOMING ERROR (malformed)"); } } #endif node_id id; m_rpc.incoming(m, &id); break; } } } namespace { void announce_fun(std::vector > const& v , node& node, int listen_port, sha1_hash const& ih, int flags) { #ifndef TORRENT_DISABLE_LOGGING if (node.observer()) { char hex_ih[41]; to_hex(reinterpret_cast(&ih[0]), 20, hex_ih); node.observer()->log(dht_logger::node, "sending announce_peer [ ih: %s " " p: %d nodes: %d ]", hex_ih, listen_port, int(v.size())); } #endif // create a dummy traversal_algorithm boost::intrusive_ptr algo( new traversal_algorithm(node, (node_id::min)())); // store on the first k nodes for (std::vector >::const_iterator i = v.begin() , end(v.end()); i != end; ++i) { #ifndef TORRENT_DISABLE_LOGGING if (node.observer()) { node.observer()->log(dht_logger::node, "announce-distance: %d" , (160 - distance_exp(ih, i->first.id))); } #endif void* ptr = node.m_rpc.allocate_observer(); if (ptr == 0) return; observer_ptr o(new (ptr) announce_observer(algo, i->first.ep(), i->first.id)); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS o->m_in_constructor = false; #endif entry e; e["y"] = "q"; e["q"] = "announce_peer"; entry& a = e["a"]; a["info_hash"] = ih.to_string(); a["port"] = listen_port; a["token"] = i->second; a["seed"] = (flags & node::flag_seed) ? 1 : 0; if (flags & node::flag_implied_port) a["implied_port"] = 1; node.stats_counters().inc_stats_counter(counters::dht_announce_peer_out); node.m_rpc.invoke(e, i->first.ep(), o); } } } void node::add_router_node(udp::endpoint router) { #ifndef TORRENT_DISABLE_LOGGING if (m_observer) { m_observer->log(dht_logger::node, "adding router node: %s" , print_endpoint(router).c_str()); } #endif m_table.add_router_node(router); } void node::add_node(udp::endpoint node) { // ping the node, and if we get a reply, it // will be added to the routing table send_single_refresh(node, m_table.num_active_buckets()); } void node::get_peers(sha1_hash const& info_hash , boost::function const&)> dcallback , boost::function > const&)> ncallback , bool noseeds) { // search for nodes with ids close to id or with peers // for info-hash id. then send announce_peer to them. boost::intrusive_ptr ta; if (m_settings.privacy_lookups) { ta.reset(new dht::obfuscated_get_peers(*this, info_hash, dcallback, ncallback, noseeds)); } else { ta.reset(new dht::get_peers(*this, info_hash, dcallback, ncallback, noseeds)); } ta->start(); } void node::announce(sha1_hash const& info_hash, int listen_port, int flags , boost::function const&)> f) { #ifndef TORRENT_DISABLE_LOGGING if (m_observer) { char hex_ih[41]; to_hex(reinterpret_cast(&info_hash[0]), 20, hex_ih); m_observer->log(dht_logger::node, "announcing [ ih: %s p: %d ]" , hex_ih, listen_port); } #endif get_peers(info_hash, f , boost::bind(&announce_fun, _1, boost::ref(*this) , listen_port, info_hash, flags), flags & node::flag_seed); } void node::direct_request(udp::endpoint ep, entry& e , boost::function f) { // not really a traversal boost::intrusive_ptr algo( new direct_traversal(*this, (node_id::min)(), f)); void* ptr = m_rpc.allocate_observer(); if (ptr == 0) return; observer_ptr o(new (ptr) direct_observer(algo, ep, (node_id::min)())); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS o->m_in_constructor = false; #endif m_rpc.invoke(e, ep, o); } void node::get_item(sha1_hash const& target , boost::function f) { #ifndef TORRENT_DISABLE_LOGGING if (m_observer) { char hex_target[41]; to_hex(reinterpret_cast(&target[0]), 20, hex_target); m_observer->log(dht_logger::node, "starting get for [ hash: %s ]" , hex_target); } #endif boost::intrusive_ptr ta; ta.reset(new dht::get_item(*this, target, boost::bind(f, _1), find_data::nodes_callback())); ta->start(); } void node::get_item(char const* pk, std::string const& salt , boost::function f) { #ifndef TORRENT_DISABLE_LOGGING if (m_observer) { char hex_key[65]; to_hex(pk, 32, hex_key); m_observer->log(dht_logger::node, "starting get for [ key: %s ]", hex_key); } #endif boost::intrusive_ptr ta; ta.reset(new dht::get_item(*this, pk, salt, f, find_data::nodes_callback())); ta->start(); } namespace { void put(std::vector > const& nodes , boost::intrusive_ptr ta) { ta->set_targets(nodes); ta->start(); } void put_data_cb(item i, bool auth , boost::intrusive_ptr ta , boost::function f) { // call data_callback only when we got authoritative data. if (auth) { f(i); ta->set_data(i); } } } // namespace void node::put_item(sha1_hash const& target, entry const& data, boost::function f) { #ifndef TORRENT_DISABLE_LOGGING if (m_observer) { char hex_target[41]; to_hex(target.data(), 20, hex_target); m_observer->log(dht_logger::node, "starting get for [ hash: %s ]" , hex_target); } #endif item i; i.assign(data); boost::intrusive_ptr put_ta; put_ta.reset(new dht::put_data(*this, boost::bind(f, _2))); put_ta->set_data(i); boost::intrusive_ptr ta; ta.reset(new dht::get_item(*this, target, get_item::data_callback(), boost::bind(&put, _1, put_ta))); ta->start(); } void node::put_item(char const* pk, std::string const& salt , boost::function f , boost::function data_cb) { #ifndef TORRENT_DISABLE_LOGGING if (m_observer) { char hex_key[65]; to_hex(pk, 32, hex_key); m_observer->log(dht_logger::node, "starting get for [ key: %s ]", hex_key); } #endif boost::intrusive_ptr put_ta; put_ta.reset(new dht::put_data(*this, f)); boost::intrusive_ptr ta; ta.reset(new dht::get_item(*this, pk, salt , boost::bind(&put_data_cb, _1, _2, put_ta, data_cb) , boost::bind(&put, _1, put_ta))); ta->start(); } struct ping_observer : observer { ping_observer( boost::intrusive_ptr const& algorithm , udp::endpoint const& ep, node_id const& id) : observer(algorithm, ep, id) {} // parses out "nodes" virtual void reply(msg const& m) { flags |= flag_done; bdecode_node r = m.message.dict_find_dict("r"); if (!r) { #ifndef TORRENT_DISABLE_LOGGING if (get_observer()) { get_observer()->log(dht_logger::node , "[%p] missing response dict" , static_cast(algorithm())); } #endif return; } // look for nodes bdecode_node n = r.dict_find_string("nodes"); if (n) { char const* nodes = n.string_ptr(); char const* end = nodes + n.string_length(); while (end - nodes >= 26) { node_id id; std::copy(nodes, nodes + 20, id.begin()); nodes += 20; algorithm()->get_node().m_table.heard_about(id , detail::read_v4_endpoint(nodes)); } } } }; void node::tick() { // every now and then we refresh our own ID, just to keep // expanding the routing table buckets closer to us. // if m_table.depth() < 4, means routing_table doesn't // have enough nodes. time_point now = aux::time_now(); if (m_last_self_refresh + minutes(10) < now && m_table.depth() < 4) { node_id target = m_id; make_id_secret(target); boost::intrusive_ptr r(new dht::bootstrap(*this, target , boost::bind(&nop))); r->start(); m_last_self_refresh = now; return; } node_entry const* ne = m_table.next_refresh(); if (ne == NULL) return; // this shouldn't happen TORRENT_ASSERT(m_id != ne->id); if (ne->id == m_id) return; int bucket = 159 - distance_exp(m_id, ne->id); TORRENT_ASSERT(bucket < 160); send_single_refresh(ne->ep(), bucket, ne->id); } void node::send_single_refresh(udp::endpoint const& ep, int bucket , node_id const& id) { TORRENT_ASSERT(id != m_id); void* ptr = m_rpc.allocate_observer(); if (ptr == 0) return; TORRENT_ASSERT(bucket >= 0); TORRENT_ASSERT(bucket <= 159); // generate a random node_id within the given bucket // TODO: 2 it would be nice to have a bias towards node-id prefixes that // are missing in the bucket node_id mask = generate_prefix_mask(bucket + 1); node_id target = generate_secret_id() & ~mask; target |= m_id & mask; // create a dummy traversal_algorithm // this is unfortunately necessary for the observer // to free itself from the pool when it's being released boost::intrusive_ptr algo( new traversal_algorithm(*this, (node_id::min)())); observer_ptr o(new (ptr) ping_observer(algo, ep, id)); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS o->m_in_constructor = false; #endif entry e; e["y"] = "q"; entry& a = e["a"]; if (m_table.is_full(bucket)) { // current bucket is full, just ping it. e["q"] = "ping"; m_counters.inc_stats_counter(counters::dht_ping_out); } else { // use get_peers instead of find_node. We'll get nodes in the response // either way. e["q"] = "get_peers"; a["info_hash"] = target.to_string(); m_counters.inc_stats_counter(counters::dht_get_peers_out); } m_rpc.invoke(e, ep, o); } time_duration node::connection_timeout() { time_duration d = m_rpc.tick(); time_point now(aux::time_now()); if (now - minutes(2) < m_last_tracker_tick) return d; m_last_tracker_tick = now; m_storage->tick(); return d; } void node::status(std::vector& table , std::vector& requests) { mutex_t::scoped_lock l(m_mutex); m_table.status(table); for (std::set::iterator i = m_running_requests.begin() , end(m_running_requests.end()); i != end; ++i) { requests.push_back(dht_lookup()); dht_lookup& lookup = requests.back(); (*i)->status(lookup); } } // TODO: in the future, this function should update all the // dht related counter. For now, it just update the storage // related ones. void node::update_stats_counters(counters& c) const { const dht_storage_counters& dht_cnt = m_storage->counters(); c.set_value(counters::dht_torrents, dht_cnt.torrents); c.set_value(counters::dht_peers, dht_cnt.peers); c.set_value(counters::dht_immutable_data, dht_cnt.immutable_data); c.set_value(counters::dht_mutable_data, dht_cnt.mutable_data); int nodes, replacements; boost::tie(nodes, replacements, boost::tuples::ignore) = size(); c.set_value(counters::dht_nodes, nodes); c.set_value(counters::dht_node_cache, replacements); c.set_value(counters::dht_allocated_observers, m_rpc.num_allocated_observers()); } #ifndef TORRENT_NO_DEPRECATE // TODO: 2 use the non deprecated function instead of this one void node::status(session_status& s) { mutex_t::scoped_lock l(m_mutex); m_table.status(s); s.dht_torrents = int(m_storage->num_torrents()); s.active_requests.clear(); s.dht_total_allocations = m_rpc.num_allocated_observers(); for (std::set::iterator i = m_running_requests.begin() , end(m_running_requests.end()); i != end; ++i) { s.active_requests.push_back(dht_lookup()); dht_lookup& lookup = s.active_requests.back(); (*i)->status(lookup); } } #endif void node::lookup_peers(sha1_hash const& info_hash, entry& reply , bool noseed, bool scrape) const { if (m_observer) m_observer->get_peers(info_hash); m_storage->get_peers(info_hash, noseed, scrape, reply); } void TORRENT_EXTRA_EXPORT write_nodes_entry(entry& r, nodes_t const& nodes) { entry& n = r["nodes"]; std::back_insert_iterator out(n.string()); for (nodes_t::const_iterator i = nodes.begin() , end(nodes.end()); i != end; ++i) { if (!i->addr().is_v4()) continue; std::copy(i->id.begin(), i->id.end(), out); write_endpoint(udp::endpoint(i->addr(), i->port()), out); } } // build response void node::incoming_request(msg const& m, entry& e) { if (!m_sock->has_quota()) return; e = entry(entry::dictionary_t); e["y"] = "r"; e["t"] = m.message.dict_find_string_value("t"); key_desc_t top_desc[] = { {"q", bdecode_node::string_t, 0, 0}, {"ro", bdecode_node::int_t, 0, key_desc_t::optional}, {"a", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"id", bdecode_node::string_t, 20, key_desc_t::last_child}, }; bdecode_node top_level[4]; char error_string[200]; if (!verify_message(m.message, top_desc, top_level, error_string , sizeof(error_string))) { incoming_error(e, error_string); return; } e["ip"] = endpoint_to_bytes(m.addr); bdecode_node arg_ent = top_level[2]; bool read_only = top_level[1] && top_level[1].int_value() != 0; node_id id(top_level[3].string_ptr()); // if this nodes ID doesn't match its IP, tell it what // its IP is with an error // don't enforce this yet if (m_settings.enforce_node_id && !verify_id(id, m.addr.address())) { incoming_error(e, "invalid node ID"); return; } if (!read_only) m_table.heard_about(id, m.addr); entry& reply = e["r"]; m_rpc.add_our_id(reply); // mirror back the other node's external port reply["p"] = m.addr.port(); char const* query = top_level[0].string_ptr(); int query_len = top_level[0].string_length(); if (m_observer && m_observer->on_dht_request(query, query_len, m, e)) return; if (query_len == 4 && memcmp(query, "ping", 4) == 0) { m_counters.inc_stats_counter(counters::dht_ping_in); // we already have 't' and 'id' in the response // no more left to add } else if (query_len == 9 && memcmp(query, "get_peers", 9) == 0) { key_desc_t msg_desc[] = { {"info_hash", bdecode_node::string_t, 20, 0}, {"noseed", bdecode_node::int_t, 0, key_desc_t::optional}, {"scrape", bdecode_node::int_t, 0, key_desc_t::optional}, }; bdecode_node msg_keys[3]; if (!verify_message(arg_ent, msg_desc, msg_keys, error_string , sizeof(error_string))) { m_counters.inc_stats_counter(counters::dht_invalid_get_peers); incoming_error(e, error_string); return; } reply["token"] = generate_token(m.addr, msg_keys[0].string_ptr()); m_counters.inc_stats_counter(counters::dht_get_peers_in); sha1_hash info_hash(msg_keys[0].string_ptr()); nodes_t n; // always return nodes as well as peers m_table.find_node(info_hash, n, 0); write_nodes_entry(reply, n); bool noseed = false; bool scrape = false; if (msg_keys[1] && msg_keys[1].int_value() != 0) noseed = true; if (msg_keys[2] && msg_keys[2].int_value() != 0) scrape = true; lookup_peers(info_hash, reply, noseed, scrape); #ifndef TORRENT_DISABLE_LOGGING if (reply.find_key("values") && m_observer) { m_observer->log(dht_logger::node, "values: %d" , int(reply["values"].list().size())); } #endif } else if (query_len == 9 && memcmp(query, "find_node", 9) == 0) { key_desc_t msg_desc[] = { {"target", bdecode_node::string_t, 20, 0}, }; bdecode_node msg_keys[1]; if (!verify_message(arg_ent, msg_desc, msg_keys, error_string, sizeof(error_string))) { incoming_error(e, error_string); return; } m_counters.inc_stats_counter(counters::dht_find_node_in); sha1_hash target(msg_keys[0].string_ptr()); // TODO: 2 find_node should write directly to the response entry nodes_t n; m_table.find_node(target, n, 0); write_nodes_entry(reply, n); } else if (query_len == 13 && memcmp(query, "announce_peer", 13) == 0) { key_desc_t msg_desc[] = { {"info_hash", bdecode_node::string_t, 20, 0}, {"port", bdecode_node::int_t, 0, 0}, {"token", bdecode_node::string_t, 0, 0}, {"n", bdecode_node::string_t, 0, key_desc_t::optional}, {"seed", bdecode_node::int_t, 0, key_desc_t::optional}, {"implied_port", bdecode_node::int_t, 0, key_desc_t::optional}, }; bdecode_node msg_keys[6]; if (!verify_message(arg_ent, msg_desc, msg_keys, error_string, sizeof(error_string))) { m_counters.inc_stats_counter(counters::dht_invalid_announce); incoming_error(e, error_string); return; } int port = int(msg_keys[1].int_value()); // is the announcer asking to ignore the explicit // listen port and instead use the source port of the packet? if (msg_keys[5] && msg_keys[5].int_value() != 0) port = m.addr.port(); if (port < 0 || port >= 65536) { m_counters.inc_stats_counter(counters::dht_invalid_announce); incoming_error(e, "invalid port"); return; } sha1_hash info_hash(msg_keys[0].string_ptr()); if (m_observer) m_observer->announce(info_hash, m.addr.address(), port); if (!verify_token(msg_keys[2].string_value(), msg_keys[0].string_ptr(), m.addr)) { m_counters.inc_stats_counter(counters::dht_invalid_announce); incoming_error(e, "invalid token"); return; } m_counters.inc_stats_counter(counters::dht_announce_peer_in); // 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(id, m.addr, 0xffff); tcp::endpoint addr = tcp::endpoint(m.addr.address(), port); std::string name = msg_keys[3] ? msg_keys[3].string_value() : std::string(); bool seed = msg_keys[4] && msg_keys[4].int_value(); m_storage->announce_peer(info_hash, addr, name, seed); } else if (query_len == 3 && memcmp(query, "put", 3) == 0) { // the first 2 entries are for both mutable and // immutable puts static const key_desc_t msg_desc[] = { {"token", bdecode_node::string_t, 0, 0}, {"v", bdecode_node::none_t, 0, 0}, {"seq", bdecode_node::int_t, 0, key_desc_t::optional}, // public key {"k", bdecode_node::string_t, item_pk_len, key_desc_t::optional}, {"sig", bdecode_node::string_t, item_sig_len, key_desc_t::optional}, {"cas", bdecode_node::int_t, 0, key_desc_t::optional}, {"salt", bdecode_node::string_t, 0, key_desc_t::optional}, }; // attempt to parse the message bdecode_node msg_keys[7]; if (!verify_message(arg_ent, msg_desc, msg_keys, error_string, sizeof(error_string))) { m_counters.inc_stats_counter(counters::dht_invalid_put); incoming_error(e, error_string); return; } m_counters.inc_stats_counter(counters::dht_put_in); // is this a mutable put? bool mutable_put = (msg_keys[2] && msg_keys[3] && msg_keys[4]); // public key (only set if it's a mutable put) char const* pk = NULL; if (msg_keys[3]) pk = msg_keys[3].string_ptr(); // signature (only set if it's a mutable put) char const* sig = NULL; if (msg_keys[4]) sig = msg_keys[4].string_ptr(); // pointer and length to the whole entry std::pair buf = msg_keys[1].data_section(); if (buf.second > 1000 || buf.second <= 0) { m_counters.inc_stats_counter(counters::dht_invalid_put); incoming_error(e, "message too big", 205); return; } std::pair salt(static_cast(NULL), 0); if (msg_keys[6]) salt = std::pair( msg_keys[6].string_ptr(), msg_keys[6].string_length()); if (salt.second > 64) { m_counters.inc_stats_counter(counters::dht_invalid_put); incoming_error(e, "salt too big", 207); return; } sha1_hash target; if (pk) target = item_target_id(salt, pk); else target = item_target_id(buf); // fprintf(stderr, "%s PUT target: %s salt: %s key: %s\n" // , mutable_put ? "mutable":"immutable" // , to_hex(target.to_string()).c_str() // , salt.second > 0 ? std::string(salt.first, salt.second).c_str() : "" // , pk ? to_hex(std::string(pk, 32)).c_str() : ""); // verify the write-token. tokens are only valid to write to // specific target hashes. it must match the one we got a "get" for if (!verify_token(msg_keys[0].string_value() , reinterpret_cast(&target[0]), m.addr)) { m_counters.inc_stats_counter(counters::dht_invalid_put); incoming_error(e, "invalid token"); return; } if (!mutable_put) { m_storage->put_immutable_item(target, buf.first, buf.second, m.addr.address()); } else { // mutable put, we must verify the signature #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[4].string_ptr(), item_sig_len); VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len); #endif boost::int64_t seq = msg_keys[2].int_value(); if (seq < 0) { m_counters.inc_stats_counter(counters::dht_invalid_put); incoming_error(e, "invalid (negative) sequence number"); return; } // msg_keys[4] is the signature, msg_keys[3] is the public key if (!verify_mutable_item(buf, salt , seq, pk, sig)) { m_counters.inc_stats_counter(counters::dht_invalid_put); incoming_error(e, "invalid signature", 206); return; } TORRENT_ASSERT(item_sig_len == msg_keys[4].string_length()); boost::int64_t item_seq; if (!m_storage->get_mutable_item_seq(target, item_seq)) { m_storage->put_mutable_item(target , buf.first, buf.second , sig, seq, pk , salt.first, salt.second , m.addr.address()); } else { // this is the "cas" field in the put message // if it was specified, we MUST make sure the current sequence // number matches the expected value before replacing it // this is critical for avoiding race conditions when multiple // writers are accessing the same slot if (msg_keys[5] && item_seq != msg_keys[5].int_value()) { m_counters.inc_stats_counter(counters::dht_invalid_put); incoming_error(e, "CAS mismatch", 301); return; } if (item_seq > seq) { m_counters.inc_stats_counter(counters::dht_invalid_put); incoming_error(e, "old sequence number", 302); return; } m_storage->put_mutable_item(target , buf.first, buf.second , sig, seq, pk , salt.first, salt.second , m.addr.address()); } } m_table.node_seen(id, m.addr, 0xffff); } else if (query_len == 3 && memcmp(query, "get", 3) == 0) { key_desc_t msg_desc[] = { {"seq", bdecode_node::int_t, 0, key_desc_t::optional}, {"target", bdecode_node::string_t, 20, 0}, }; // k is not used for now // attempt to parse the message bdecode_node msg_keys[2]; if (!verify_message(arg_ent, msg_desc, msg_keys, error_string , sizeof(error_string))) { m_counters.inc_stats_counter(counters::dht_invalid_get); incoming_error(e, error_string); return; } m_counters.inc_stats_counter(counters::dht_get_in); sha1_hash target(msg_keys[1].string_ptr()); // fprintf(stderr, "%s GET target: %s\n" // , msg_keys[1] ? "mutable":"immutable" // , to_hex(target.to_string()).c_str()); reply["token"] = generate_token(m.addr, msg_keys[1].string_ptr()); nodes_t n; // always return nodes as well as peers m_table.find_node(target, n, 0); write_nodes_entry(reply, n); // if the get has a sequence number it must be for a mutable item // so don't bother searching the immutable table if (!msg_keys[0]) { if (!m_storage->get_immutable_item(target, reply)) // ok, check for a mutable one { m_storage->get_mutable_item(target, 0, true, reply); } } else { m_storage->get_mutable_item(target , msg_keys[0].int_value(), false , reply); } } else { // if we don't recognize the message but there's a // 'target' or 'info_hash' in the arguments, treat it // as find_node to be future compatible bdecode_node target_ent = arg_ent.dict_find_string("target"); if (!target_ent || target_ent.string_length() != 20) { target_ent = arg_ent.dict_find_string("info_hash"); if (!target_ent || target_ent.string_length() != 20) { incoming_error(e, "unknown message"); return; } } sha1_hash target(target_ent.string_ptr()); nodes_t n; // always return nodes as well as peers m_table.find_node(target, n, 0); write_nodes_entry(reply, n); return; } } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/node_entry.cpp000066400000000000000000000052521351156116000231570ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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/kademlia/node_entry.hpp" #include "libtorrent/aux_/time.hpp" // for aux::time_now() namespace libtorrent { namespace dht { node_entry::node_entry(node_id const& id_, udp::endpoint ep , int roundtriptime , bool pinged) : last_queried(pinged ? aux::time_now() : min_time()) , id(id_) , a(ep.address().to_v4().to_bytes()) , p(ep.port()) , rtt(roundtriptime & 0xffff) , timeout_count(pinged ? 0 : 0xff) { #ifndef TORRENT_DISABLE_LOGGING first_seen = aux::time_now(); #endif } node_entry::node_entry(udp::endpoint ep) : last_queried(min_time()) , id(0) , a(ep.address().to_v4().to_bytes()) , p(ep.port()) , rtt(0xffff) , timeout_count(0xff) { #ifndef TORRENT_DISABLE_LOGGING first_seen = aux::time_now(); #endif } node_entry::node_entry() : last_queried(min_time()) , id(0) , p(0) , rtt(0xffff) , timeout_count(0xff) { #ifndef TORRENT_DISABLE_LOGGING first_seen = aux::time_now(); #endif } void node_entry::update_rtt(int new_rtt) { TORRENT_ASSERT(new_rtt <= 0xffff); TORRENT_ASSERT(new_rtt >= 0); if (new_rtt == 0xffff) return; if (rtt == 0xffff) rtt = new_rtt; else rtt = int(rtt) * 2 / 3 + int(new_rtt) / 3; } }} libtorrent-rasterbar-1.1.13/src/kademlia/node_id.cpp000066400000000000000000000152021351156116000224060ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include "libtorrent/kademlia/node_id.hpp" #include "libtorrent/kademlia/node_entry.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/broadcast_socket.hpp" // for is_local et.al #include "libtorrent/socket_io.hpp" // for hash_address #include "libtorrent/random.hpp" // for random #include "libtorrent/hasher.hpp" // for hasher #include "libtorrent/crc32c.hpp" // for crc32c 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(); // TODO: 3 the XORing should be done at full words instead of bytes 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) { // TODO: 3 the XORing should be done at full words instead of bytes 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) { // TODO: 3 the xoring should be done at full words and _builtin_clz() could // be used as the last step 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) { TORRENT_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 const bit = byte * 8; for (int b = 7; b >= 0; --b) if (t >= (1 << b)) return bit + b; return bit; } return 0; } node_id generate_id_impl(address const& ip_, boost::uint32_t r) { boost::uint8_t* ip = 0; static const boost::uint8_t v4mask[] = { 0x03, 0x0f, 0x3f, 0xff }; #if TORRENT_USE_IPV6 static const boost::uint8_t v6mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; #endif boost::uint8_t const* mask = 0; int num_octets = 0; address_v4::bytes_type b4; #if TORRENT_USE_IPV6 address_v6::bytes_type b6; if (ip_.is_v6()) { b6 = ip_.to_v6().to_bytes(); ip = &b6[0]; num_octets = 8; mask = v6mask; } else #endif { b4 = ip_.to_v4().to_bytes(); ip = &b4[0]; num_octets = 4; mask = v4mask; } for (int i = 0; i < num_octets; ++i) ip[i] &= mask[i]; ip[0] |= (r & 0x7) << 5; // this is the crc32c (Castagnoli) polynomial boost::uint32_t c; if (num_octets == 4) { c = crc32c_32(*reinterpret_cast(ip)); } else { TORRENT_ASSERT(num_octets == 8); c = crc32c(reinterpret_cast(ip), 1); } node_id id; id[0] = (c >> 24) & 0xff; id[1] = (c >> 16) & 0xff; id[2] = ((c >> 8) & 0xf8) | (random() & 0x7); for (int i = 3; i < 19; ++i) id[i] = random() & 0xff; id[19] = r & 0xff; return id; } static boost::uint32_t secret = 0; void make_id_secret(node_id& in) { if (secret == 0) secret = (random() % 0xfffffffe) + 1; boost::uint32_t rand = random(); // generate the last 4 bytes as a "signature" of the previous 4 bytes. This // lets us verify whether a hash came from this function or not in the future. hasher h(reinterpret_cast(&secret), 4); h.update(reinterpret_cast(&rand), 4); sha1_hash secret_hash = h.final(); memcpy(&in[20-4], &secret_hash[0], 4); memcpy(&in[20-8], &rand, 4); } node_id generate_random_id() { char r[20]; for (int i = 0; i < 20; ++i) r[i] = random() & 0xff; return hasher(r, 20).final(); } node_id generate_secret_id() { node_id ret = generate_random_id(); make_id_secret(ret); return ret; } bool verify_secret_id(node_id const& nid) { if (secret == 0) return false; hasher h(reinterpret_cast(&secret), 4); h.update(reinterpret_cast(&nid[20-8]), 4); sha1_hash secret_hash = h.final(); return memcmp(&nid[20-4], &secret_hash[0], 4) == 0; } // verifies whether a node-id matches the IP it's used from // returns true if the node-id is OK coming from this source // and false otherwise. bool verify_id(node_id const& nid, address const& source_ip) { // no need to verify local IPs, they would be incorrect anyway if (is_local(source_ip)) return true; node_id h = generate_id_impl(source_ip, nid[19]); return nid[0] == h[0] && nid[1] == h[1] && (nid[2] & 0xf8) == (h[2] & 0xf8); } node_id generate_id(address const& ip) { return generate_id_impl(ip, random()); } bool matching_prefix(node_entry const& n, int mask, int prefix, int offset) { node_id id = n.id; id <<= offset; return (id[0] & mask) == prefix; } node_id generate_prefix_mask(int bits) { TORRENT_ASSERT(bits >= 0); TORRENT_ASSERT(bits <= 160); node_id mask(0); int b = 0; for (; b < bits - 7; b += 8) mask[b/8] |= 0xff; if (bits < 160) mask[b/8] |= (0xff << (8 - (bits&7))) & 0xff; return mask; } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/put_data.cpp000066400000000000000000000070671351156116000226200ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include #include namespace libtorrent { namespace dht { put_data::put_data(node& dht_node, put_callback const& callback) : traversal_algorithm(dht_node, (node_id::min)()) , m_put_callback(callback) , m_done(false) { } char const* put_data::name() const { return "put_data"; } void put_data::start() { // router nodes must not be added to puts init(); bool is_done = add_requests(); if (is_done) done(); } void put_data::set_targets(std::vector > const& targets) { for (std::vector >::const_iterator i = targets.begin() , end(targets.end()); i != end; ++i) { void* ptr = m_node.m_rpc.allocate_observer(); if (ptr == 0) return; observer_ptr o(new (ptr) put_data_observer(this, i->first.ep() , i->first.id, i->second)); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS o->m_in_constructor = false; #endif m_results.push_back(o); } } void put_data::done() { m_done = true; #ifndef TORRENT_DISABLE_LOGGING get_node().observer()->log(dht_logger::traversal, "[%p] %s DONE, response %d, timeout %d" , static_cast(this), name(), m_responses, m_timeouts); #endif m_put_callback(m_data, m_responses); traversal_algorithm::done(); } bool put_data::invoke(observer_ptr o) { if (m_done) return false; // TODO: what if o is not an isntance of put_data_observer? This need to be // redesigned for better type saftey. put_data_observer* po = static_cast(o.get()); entry e; e["y"] = "q"; e["q"] = "put"; entry& a = e["a"]; a["v"] = m_data.value(); a["token"] = po->m_token; if (m_data.is_mutable()) { a["k"] = std::string(m_data.pk().data(), item_pk_len); a["seq"] = m_data.seq(); a["sig"] = std::string(m_data.sig().data(), item_sig_len); if (!m_data.salt().empty()) { a["salt"] = m_data.salt(); } } return m_node.m_rpc.invoke(e, o->target_ep(), o); } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/refresh.cpp000066400000000000000000000070231351156116000224450ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include #include #include #include #include namespace libtorrent { namespace dht { observer_ptr bootstrap::new_observer(void* ptr , udp::endpoint const& ep, node_id const& id) { observer_ptr o(new (ptr) get_peers_observer(this, ep, id)); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS o->m_in_constructor = false; #endif return o; } bool bootstrap::invoke(observer_ptr o) { entry e; e["y"] = "q"; entry& a = e["a"]; e["q"] = "get_peers"; // in case our node id changes during the bootstrap, make sure to always use // the current node id (rather than the target stored in the traversal // algorithm) node_id target = get_node().nid(); make_id_secret(target); a["info_hash"] = target.to_string(); if (o->flags & observer::flag_initial) { // if this packet is being sent to a bootstrap/router node, let it know // that we're actualy bootstrapping (as opposed to being collateral // traffic). a["bs"] = 1; } // e["q"] = "find_node"; // a["target"] = target.to_string(); m_node.stats_counters().inc_stats_counter(counters::dht_get_peers_out); return m_node.m_rpc.invoke(e, o->target_ep(), o); } bootstrap::bootstrap( node& dht_node , node_id target , done_callback const& callback) : get_peers(dht_node, target, get_peers::data_callback(), callback, false) { } char const* bootstrap::name() const { return "bootstrap"; } void bootstrap::done() { #ifndef TORRENT_DISABLE_LOGGING get_node().observer()->log(dht_logger::traversal, "[%p] bootstrap done, pinging remaining nodes" , static_cast(this)); #endif for (std::vector::iterator i = m_results.begin() , end(m_results.end()); i != end; ++i) { if ((*i)->flags & observer::flag_queried) continue; // this will send a ping m_node.add_node((*i)->target_ep()); } get_peers::done(); } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/routing_table.cpp000066400000000000000000001124641351156116000236530ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 #include // std::distance() #include // std::copy, std::remove_copy_if #include #include #include "libtorrent/config.hpp" #include "libtorrent/kademlia/routing_table.hpp" #include "libtorrent/broadcast_socket.hpp" // for cidr_distance #include "libtorrent/session_status.hpp" #include "libtorrent/kademlia/node_id.hpp" #include "libtorrent/kademlia/dht_observer.hpp" #include "libtorrent/aux_/time.hpp" #include "libtorrent/alert_types.hpp" // for dht_routing_bucket #include "libtorrent/socket_io.hpp" // for print_endpoint #include "libtorrent/invariant_check.hpp" #include "libtorrent/address.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #if BOOST_VERSION >= 106700 #include #else #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" using boost::uint8_t; #if BOOST_VERSION <= 104700 namespace boost { size_t hash_value(libtorrent::address_v4::bytes_type ip) { return boost::hash_value(*reinterpret_cast(&ip[0])); } } #endif namespace libtorrent { namespace dht { namespace { template void erase_one(T& container, K const& key) { typename T::iterator i = container.find(key); TORRENT_ASSERT(i != container.end()); container.erase(i); } bool verify_node_address(dht_settings const& settings , node_id const& id, address const& addr) { // only when the node_id pass the verification, add it to routing table. if (settings.enforce_node_id && !verify_id(id, addr)) return false; return true; } } routing_table::routing_table(node_id const& id, int bucket_size , dht_settings const& settings , dht_logger* log) : #ifndef TORRENT_DISABLE_LOGGING m_log(log), #endif m_settings(settings) , m_id(id) , m_depth(0) , m_last_self_refresh(min_time()) , m_bucket_size(bucket_size) { TORRENT_UNUSED(log); m_buckets.reserve(30); } int routing_table::bucket_limit(int bucket) const { if (!m_settings.extended_routing_table) return m_bucket_size; static const int size_exceptions[] = {16, 8, 4, 2}; if (bucket < int(sizeof(size_exceptions)/sizeof(size_exceptions[0]))) return m_bucket_size * size_exceptions[bucket]; return m_bucket_size; } void routing_table::status(std::vector& s) const { for (table_t::const_iterator i = m_buckets.begin() , end(m_buckets.end()); i != end; ++i) { dht_routing_bucket b; b.num_nodes = i->live_nodes.size(); b.num_replacements = i->replacements.size(); s.push_back(b); } } #ifndef TORRENT_NO_DEPRECATE // TODO: 2 use the non deprecated function instead of this one void routing_table::status(session_status& s) const { int ignore; boost::tie(s.dht_nodes, s.dht_node_cache, ignore) = size(); s.dht_global_nodes = num_global_nodes(); for (table_t::const_iterator i = m_buckets.begin() , end(m_buckets.end()); i != end; ++i) { dht_routing_bucket b; b.num_nodes = i->live_nodes.size(); b.num_replacements = i->replacements.size(); #ifndef TORRENT_NO_DEPRECATE b.last_active = 0; #endif s.dht_routing_table.push_back(b); } } #endif boost::tuple routing_table::size() const { int nodes = 0; int replacements = 0; int confirmed = 0; for (table_t::const_iterator i = m_buckets.begin() , end(m_buckets.end()); i != end; ++i) { nodes += i->live_nodes.size(); for (bucket_t::const_iterator k = i->live_nodes.begin() , end2(i->live_nodes.end()); k != end2; ++k) { if (k->confirmed()) ++confirmed; } replacements += i->replacements.size(); } return boost::make_tuple(nodes, replacements, confirmed); } boost::int64_t routing_table::num_global_nodes() const { int deepest_bucket = 0; int deepest_size = 0; for (table_t::const_iterator i = m_buckets.begin() , end(m_buckets.end()); i != end; ++i) { deepest_size = i->live_nodes.size(); // + i->replacements.size(); if (deepest_size < m_bucket_size) break; // this bucket is full ++deepest_bucket; } if (deepest_bucket == 0) return 1 + deepest_size; if (deepest_size < m_bucket_size / 2) return (boost::int64_t(1) << deepest_bucket) * m_bucket_size; else return (boost::int64_t(2) << deepest_bucket) * deepest_size; } int routing_table::depth() const { if (m_depth >= int(m_buckets.size())) m_depth = m_buckets.size() - 1; if (m_depth < 0) return m_depth; // maybe the table is deeper now? while (m_depth < int(m_buckets.size())-1 && int(m_buckets[m_depth+1].live_nodes.size()) >= m_bucket_size / 2) { ++m_depth; } // maybe the table is more shallow now? while (m_depth > 0 && int(m_buckets[m_depth-1].live_nodes.size()) < m_bucket_size / 2) { --m_depth; } return m_depth; } #if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM void routing_table::print_state(std::ostream& os) const { std::vector buf(2048); int cursor = 0; cursor += snprintf(&buf[cursor], buf.size() - cursor , "kademlia routing table state\n" "bucket_size: %d\n" "global node count: %" PRId64 "\n" "node_id: %s\n\n" "number of nodes per bucket:\n" , m_bucket_size , num_global_nodes() , to_hex(m_id.to_string()).c_str()); if (cursor > buf.size() - 500) buf.resize(buf.size() * 3 / 2); int idx = 0; for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); i != end; ++i, ++idx) { cursor += snprintf(&buf[cursor], buf.size() - cursor , "%2d: ", idx); for (int k = 0; k < int(i->live_nodes.size()); ++k) cursor += snprintf(&buf[cursor], buf.size() - cursor, "#"); for (int k = 0; k < int(i->replacements.size()); ++k) cursor += snprintf(&buf[cursor], buf.size() - cursor, "-"); cursor += snprintf(&buf[cursor], buf.size() - cursor, "\n"); if (cursor > buf.size() - 500) buf.resize(buf.size() * 3 / 2); } time_point now = aux::time_now(); cursor += snprintf(&buf[cursor], buf.size() - cursor , "\nnodes:"); int bucket_index = 0; for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); i != end; ++i, ++bucket_index) { cursor += snprintf(&buf[cursor], buf.size() - cursor , "\n=== BUCKET == %d == %d|%d ==== \n" , bucket_index, int(i->live_nodes.size()) , int(i->replacements.size())); if (cursor > buf.size() - 500) buf.resize(buf.size() * 3 / 2); int id_shift; // the last bucket is special, since it hasn't been split yet, it // includes that top bit as well if (bucket_index + 1 == m_buckets.size()) id_shift = bucket_index; else id_shift = bucket_index + 1; for (bucket_t::const_iterator j = i->live_nodes.begin() , end2(i->live_nodes.end()); j != end2; ++j) { int bucket_size_limit = bucket_limit(bucket_index); boost::uint32_t top_mask = bucket_size_limit - 1; int mask_shift = 0; TORRENT_ASSERT_VAL(bucket_size_limit > 0, bucket_size_limit); while ((top_mask & 0x80) == 0) { top_mask <<= 1; ++mask_shift; } top_mask = (0xff << mask_shift) & 0xff; node_id id = j->id; id <<= id_shift; cursor += snprintf(&buf[cursor], buf.size() - cursor , " prefix: %2x id: %s" , ((id[0] & top_mask) >> mask_shift) , to_hex(j->id.to_string()).c_str()); if (j->rtt == 0xffff) { cursor += snprintf(&buf[cursor], buf.size() - cursor , " rtt: "); } else { cursor += snprintf(&buf[cursor], buf.size() - cursor , " rtt: %4d", j->rtt); } cursor += snprintf(&buf[cursor], buf.size() - cursor , " fail: %4d ping: %d dist: %3d" , j->fail_count() , j->pinged() , distance_exp(m_id, j->id)); if (j->last_queried == min_time()) { cursor += snprintf(&buf[cursor], buf.size() - cursor , " query: "); } else { cursor += snprintf(&buf[cursor], buf.size() - cursor , " query: %3d", int(total_seconds(now - j->last_queried))); } cursor += snprintf(&buf[cursor], buf.size() - cursor , " ip: %s\n", print_endpoint(j->ep()).c_str()); if (cursor > buf.size() - 500) buf.resize(buf.size() * 3 / 2); } } cursor += snprintf(&buf[cursor], buf.size() - cursor , "\nnode spread per bucket:\n"); bucket_index = 0; for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); i != end; ++i, ++bucket_index) { int bucket_size_limit = bucket_limit(bucket_index); // mask out the first 3 bits, or more depending // on the bucket_size_limit // we have all the lower bits set in (bucket_size_limit-1) // but we want the left-most bits to be set. Shift it // until the MSB is set boost::uint32_t top_mask = bucket_size_limit - 1; int mask_shift = 0; TORRENT_ASSERT_VAL(bucket_size_limit > 0, bucket_size_limit); while ((top_mask & 0x80) == 0) { top_mask <<= 1; ++mask_shift; } top_mask = (0xff << mask_shift) & 0xff; bucket_size_limit = (top_mask >> mask_shift) + 1; TORRENT_ASSERT_VAL(bucket_size_limit <= 256, bucket_size_limit); bool sub_buckets[256]; memset(sub_buckets, 0, sizeof(sub_buckets)); int id_shift; // the last bucket is special, since it hasn't been split yet, it // includes that top bit as well if (bucket_index + 1 == m_buckets.size()) id_shift = bucket_index; else id_shift = bucket_index + 1; for (bucket_t::const_iterator j = i->live_nodes.begin() , end2(i->live_nodes.end()); j != end2; ++j) { node_id id = j->id; id <<= id_shift; int b = (id[0] & top_mask) >> mask_shift; TORRENT_ASSERT(b >= 0 && b < int(sizeof(sub_buckets)/sizeof(sub_buckets[0]))); sub_buckets[b] = true; } cursor += snprintf(&buf[cursor], buf.size() - cursor , "%2d mask: %2x: [", bucket_index, (top_mask >> mask_shift)); for (int j = 0; j < bucket_size_limit; ++j) { cursor += snprintf(&buf[cursor], buf.size() - cursor , (sub_buckets[j] ? "X" : " ")); } cursor += snprintf(&buf[cursor], buf.size() - cursor , "]\n"); if (cursor > buf.size() - 500) buf.resize(buf.size() * 3 / 2); } buf[cursor] = '\0'; os << &buf[0]; } #endif node_entry const* routing_table::next_refresh() { // find the node with the least recent 'last_queried' field. if it's too // recent, return false. Otherwise return a random target ID that's close to // a missing prefix for that bucket node_entry* candidate = NULL; // this will have a bias towards pinging nodes close to us first. int idx = m_buckets.size() - 1; for (table_t::reverse_iterator i = m_buckets.rbegin() , end(m_buckets.rend()); i != end; ++i, --idx) { for (bucket_t::iterator j = i->live_nodes.begin() , end2(i->live_nodes.end()); j != end2; ++j) { // this shouldn't happen TORRENT_ASSERT(m_id != j->id); if (j->id == m_id) continue; if (j->last_queried == min_time()) { candidate = &*j; goto out; } if (candidate == NULL || j->last_queried < candidate->last_queried) { candidate = &*j; } } } out: // make sure we don't pick the same node again next time we want to refresh // the routing table if (candidate) candidate->last_queried = aux::time_now(); return candidate; } routing_table::table_t::iterator routing_table::find_bucket(node_id const& id) { // TORRENT_ASSERT(id != m_id); int num_buckets = m_buckets.size(); if (num_buckets == 0) { m_buckets.push_back(routing_table_node()); ++num_buckets; } int bucket_index = (std::min)(159 - distance_exp(m_id, id), num_buckets - 1); TORRENT_ASSERT(bucket_index < int(m_buckets.size())); TORRENT_ASSERT(bucket_index >= 0); table_t::iterator i = m_buckets.begin(); std::advance(i, bucket_index); return i; } namespace { bool compare_ip_cidr(node_entry const& lhs, node_entry const& rhs) { TORRENT_ASSERT(lhs.addr().is_v4() == rhs.addr().is_v4()); // the number of bits in the IPs that may match. If // more bits that this matches, something suspicious is // going on and we shouldn't add the second one to our // routing table int cutoff = rhs.addr().is_v4() ? 8 : 64; int dist = cidr_distance(lhs.addr(), rhs.addr()); return dist <= cutoff; } } // anonymous namespace node_entry* routing_table::find_node(udp::endpoint const& ep , routing_table::table_t::iterator* bucket) { for (table_t::iterator i = m_buckets.begin() , end(m_buckets.end()); i != end; ++i) { for (bucket_t::iterator j = i->replacements.begin(); j != i->replacements.end(); ++j) { if (j->addr() != ep.address()) continue; if (j->port() != ep.port()) continue; *bucket = i; return &*j; } for (bucket_t::iterator j = i->live_nodes.begin(); j != i->live_nodes.end(); ++j) { if (j->addr() != ep.address()) continue; if (j->port() != ep.port()) continue; *bucket = i; return &*j; } } *bucket = m_buckets.end(); return 0; } void routing_table::remove_node(node_entry* n , routing_table::table_t::iterator bucket) { INVARIANT_CHECK; if (!bucket->replacements.empty() && n >= &bucket->replacements[0] && n < &bucket->replacements[0] + bucket->replacements.size()) { int idx = n - &bucket->replacements[0]; TORRENT_ASSERT(m_ips.count(n->a) > 0); erase_one(m_ips, n->a); bucket->replacements.erase(bucket->replacements.begin() + idx); } if (!bucket->live_nodes.empty() && n >= &bucket->live_nodes[0] && n < &bucket->live_nodes[0] + bucket->live_nodes.size()) { int idx = n - &bucket->live_nodes[0]; TORRENT_ASSERT(m_ips.count(n->a) > 0); erase_one(m_ips, n->a); bucket->live_nodes.erase(bucket->live_nodes.begin() + idx); } } bool routing_table::add_node(node_entry e) { add_node_status_t s = add_node_impl(e); if (s == failed_to_add) return false; if (s == node_added) return true; while (s == need_bucket_split) { split_bucket(); // if this assert triggers a lot in the wild, we should probably // harden our resistence towards this attack. Perhaps by never // splitting a bucket (and discard nodes) if the two buckets above it // are empty or close to empty // TORRENT_ASSERT(m_buckets.size() <= 50); if (m_buckets.size() > 50) { // this is a sanity check. In the wild, we shouldn't see routing // tables deeper than 26 or 27. If we get this deep, there might // be a bug in the bucket splitting logic, or there may be someone // playing a prank on us, spoofing node IDs. s = add_node_impl(e); if (s == node_added) return true; return false; } // if the new bucket still has too many nodes in it, we need to keep // splitting if (m_buckets.back().live_nodes.size() > bucket_limit(m_buckets.size()-1)) continue; s = add_node_impl(e); if (s == failed_to_add) return false; if (s == node_added) return true; } return false; } routing_table::add_node_status_t routing_table::add_node_impl(node_entry e) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS // INVARIANT_CHECK; #endif // if we already have this (IP,port), don't do anything if (m_router_nodes.find(e.ep()) != m_router_nodes.end()) return failed_to_add; // don't add ourself if (e.id == m_id) return failed_to_add; // do we already have this IP in the table? if (m_ips.count(e.addr().to_v4().to_bytes()) > 0) { // this exact IP already exists in the table. It might be the case // that the node changed IP. If pinged is true, and the port also // matches then we assume it's in fact the same node, and just update // the routing table // pinged means that we have sent a message to the IP, port and received // a response with a correct transaction ID, i.e. it is verified to not // be the result of a poisoned routing table table_t::iterator existing_bucket; node_entry* existing = find_node(e.ep(), &existing_bucket); if (existing == 0) { // the node we're trying to add is not a match with an existing node. we // should ignore it, unless we allow duplicate IPs in our routing // table. There could be a node with the same IP, but with a different // port. m_ips just contain IP addresses, whereas the lookup we just // performed was for full endpoints (address, port). if (m_settings.restrict_routing_ips) { #ifndef TORRENT_DISABLE_LOGGING char hex_id[41]; to_hex(reinterpret_cast(&e.id[0]), 20, hex_id); m_log->log(dht_logger::routing_table, "ignoring node (duplicate IP): %s %s" , hex_id, print_address(e.addr()).c_str()); #endif return failed_to_add; } } else if (existing->id == e.id) { // if the node ID is the same, just update the failcount // and be done with it. existing->timeout_count = 0; if (e.pinged()) { existing->update_rtt(e.rtt); existing->last_queried = e.last_queried; } return node_added; } else if (!e.pinged()) { // this may be a routing table poison attack. If we haven't confirmed // that this peer actually exist with this new node ID yet, ignore it. // we definitely don't want to replace the existing entry with this one if (m_settings.restrict_routing_ips) return failed_to_add; } else { TORRENT_ASSERT(existing->id != e.id); // this is the same IP and port, but with // a new node ID. remove the old entry and // replace it with this new ID remove_node(existing, existing_bucket); } } table_t::iterator i = find_bucket(e.id); bucket_t& b = i->live_nodes; bucket_t& rb = i->replacements; int const bucket_index = std::distance(m_buckets.begin(), i); // compare against the max size of the next bucket. Otherwise we may wait too // long to split, and lose nodes (in the case where lower-numbered buckets // are larger) int const bucket_size_limit = bucket_limit(bucket_index); int const next_bucket_size_limit = bucket_limit(bucket_index + 1); bucket_t::iterator j; // if the node already exists, we don't need it j = std::find_if(b.begin(), b.end() , boost::bind(&node_entry::id, _1) == e.id); if (j != b.end()) { // a new IP address just claimed this node-ID // ignore it if (j->addr() != e.addr() || j->port() != e.port()) return failed_to_add; // we already have the node in our bucket TORRENT_ASSERT(j->id == e.id && j->ep() == e.ep()); j->timeout_count = 0; j->update_rtt(e.rtt); return node_added; } // if this node exists in the replacement bucket. update it and // pull it out from there. We may add it back to the replacement // bucket, but we may also replace a node in the main bucket, now // that we have an updated RTT j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::id, _1) == e.id); if (j != rb.end()) { // a new IP address just claimed this node-ID // ignore it if (j->addr() != e.addr() || j->port() != e.port()) return failed_to_add; TORRENT_ASSERT(j->id == e.id && j->ep() == e.ep()); j->timeout_count = 0; j->update_rtt(e.rtt); e = *j; erase_one(m_ips, j->addr().to_v4().to_bytes()); rb.erase(j); } if (m_settings.restrict_routing_ips) { // don't allow multiple entries from IPs very close to each other // TODO: 3 the call to compare_ip_cidr here is expensive. peel off some // layers of abstraction here to make it quicker. Look at xoring and using _builtin_ctz() j = std::find_if(b.begin(), b.end(), boost::bind(&compare_ip_cidr, _1, e)); if (j == b.end()) { j = std::find_if(rb.begin(), rb.end(), boost::bind(&compare_ip_cidr, _1, e)); if (j == rb.end()) goto ip_ok; } // we already have a node in this bucket with an IP very // close to this one. We know that it's not the same, because // it claims a different node-ID. Ignore this to avoid attacks #ifndef TORRENT_DISABLE_LOGGING char hex_id1[41]; to_hex(e.id.data(), 20, hex_id1); char hex_id2[41]; to_hex(j->id.data(), 20, hex_id2); m_log->log(dht_logger::routing_table, "ignoring node: %s %s existing node: %s %s" , hex_id1, print_address(e.addr()).c_str() , hex_id2, print_address(j->addr()).c_str()); #endif return failed_to_add; } ip_ok: // can we split the bucket? // only nodes that haven't failed can split the bucket, and we can only // split the last bucket bool const can_split = (boost::next(i) == m_buckets.end() && m_buckets.size() < 159) && e.fail_count() == 0 && (i == m_buckets.begin() || boost::prior(i)->live_nodes.size() > 1); // if there's room in the main bucket, just insert it // if we can split the bucket (i.e. it's the last bucket) use the next // bucket's size limit. This makes use split the low-numbered buckets split // earlier when we have larger low buckets, to make it less likely that we // lose nodes if (int(b.size()) < (can_split ? next_bucket_size_limit : bucket_size_limit)) { if (b.empty()) b.reserve(bucket_size_limit); b.push_back(e); m_ips.insert(e.addr().to_v4().to_bytes()); return node_added; } // if there is no room, we look for nodes that are not 'pinged', // i.e. we haven't confirmed that they respond to messages. // Then we look for nodes marked as stale // in the k-bucket. If we find one, we can replace it. // then we look for nodes with the same 3 bit prefix (or however // many bits prefix the bucket size warrants). If there is no other // node with this prefix, remove the duplicate with the highest RTT. // as the last replacement strategy, if the node we found matching our // bit prefix has higher RTT than the new node, replace it. if (e.pinged() && e.fail_count() == 0) { // if the node we're trying to insert is considered pinged, // we may replace other nodes that aren't pinged j = std::find_if(b.begin(), b.end(), boost::bind(&node_entry::pinged, _1) == false); if (j != b.end() && !j->pinged()) { // j points to a node that has not been pinged. // Replace it with this new one erase_one(m_ips, j->addr().to_v4().to_bytes()); *j = e; m_ips.insert(e.addr().to_v4().to_bytes()); return node_added; } // 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. j = std::max_element(b.begin(), b.end() , boost::bind(&node_entry::fail_count, _1) < boost::bind(&node_entry::fail_count, _2)); TORRENT_ASSERT(j != b.end()); if (j->fail_count() > 0) { // i points to a node that has been marked // as stale. Replace it with this new one erase_one(m_ips, j->addr().to_v4().to_bytes()); *j = e; m_ips.insert(e.addr().to_v4().to_bytes()); return node_added; } // in order to provide as few lookups as possible before finding // the data someone is looking for, make sure there is an affinity // towards having a good spread of node IDs in each bucket boost::uint32_t mask = bucket_size_limit - 1; int mask_shift = 0; TORRENT_ASSERT_VAL(mask > 0, mask); while ((mask & 0x80) == 0) { mask <<= 1; ++mask_shift; } // in case bucket_size_limit is not an even power of 2 mask = (0xff << mask_shift) & 0xff; // pick out all nodes that have the same prefix as the new node std::vector nodes; bool force_replace = false; // the last bucket is special, since it hasn't been split yet, it // includes that top bit as well int const prefix_offset = bucket_index + 1 == m_buckets.size() ? bucket_index : bucket_index + 1; { node_id id = e.id; id <<= prefix_offset; int const candidate_prefix = id[0] & mask; for (j = b.begin(); j != b.end(); ++j) { if (!matching_prefix(*j, mask, candidate_prefix, prefix_offset)) continue; nodes.push_back(j); } } if (!nodes.empty()) { j = *std::max_element(nodes.begin(), nodes.end() , boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _1)) < boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _2))); } else { // there is no node in this prefix-slot, there may be some // nodes sharing a prefix. Find all nodes that do not // have a unique prefix // find node entries with duplicate prefixes in O(1) std::vector prefix(1 << (8 - mask_shift), b.end()); TORRENT_ASSERT(int(prefix.size()) >= bucket_size_limit); // the begin iterator from this object is used as a placeholder // for an occupied slot whose node has already been added to the // duplicate nodes list. bucket_t placeholder; nodes.reserve(b.size()); for (j = b.begin(); j != b.end(); ++j) { node_id id = j->id; id <<= prefix_offset; int this_prefix = (id[0] & mask) >> mask_shift; TORRENT_ASSERT(this_prefix >= 0); TORRENT_ASSERT(this_prefix < int(prefix.size())); if (prefix[this_prefix] != b.end()) { // there's already a node with this prefix. Remember both // duplicates. nodes.push_back(j); if (prefix[this_prefix] != placeholder.begin()) { nodes.push_back(prefix[this_prefix]); prefix[this_prefix] = placeholder.begin(); } } } if (!nodes.empty()) { // from these nodes, pick the one with the highest RTT // and replace it std::vector::iterator k = std::max_element(nodes.begin(), nodes.end() , boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _1)) < boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _2))); // in this case, we would really rather replace the node even if // the new node has higher RTT, becase it fills a new prefix that we otherwise // don't have. force_replace = true; j = *k; } else { j = std::max_element(b.begin(), b.end() , boost::bind(&node_entry::rtt, _1) < boost::bind(&node_entry::rtt, _2)); } } if (j != b.end() && (force_replace || j->rtt > e.rtt)) { erase_one(m_ips, j->addr().to_v4().to_bytes()); *j = e; m_ips.insert(e.addr().to_v4().to_bytes()); #ifndef TORRENT_DISABLE_LOGGING if (m_log) { char hex_id[41]; to_hex(e.id.data(), sha1_hash::size, hex_id); m_log->log(dht_logger::routing_table, "replacing node with higher RTT: %s %s" , hex_id, print_address(e.addr()).c_str()); } #endif return node_added; } // in order to keep lookup times small, prefer nodes with low RTTs } // if we can't split, try to insert into the replacement bucket if (!can_split) { // 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. j = std::find_if(rb.begin(), rb.end() , boost::bind(&node_entry::id, _1) == e.id); // if the node is already in the replacement bucket // just return. if (j != rb.end()) { // if the IP address matches, it's the same node // make sure it's marked as pinged if (j->ep() == e.ep()) j->set_pinged(); return node_added; } if (int(rb.size()) >= m_bucket_size) { // if the replacement bucket is full, remove the oldest entry // but prefer nodes that haven't been pinged, since they are // less reliable than this one, that has been pinged j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::pinged, _1) == false); if (j == rb.end()) j = rb.begin(); erase_one(m_ips, j->addr().to_v4().to_bytes()); rb.erase(j); } if (rb.empty()) rb.reserve(m_bucket_size); rb.push_back(e); m_ips.insert(e.addr().to_v4().to_bytes()); return node_added; } return need_bucket_split; } void routing_table::split_bucket() { INVARIANT_CHECK; int const bucket_index = m_buckets.size()-1; int const bucket_size_limit = bucket_limit(bucket_index); TORRENT_ASSERT(int(m_buckets.back().live_nodes.size()) >= bucket_limit(bucket_index + 1)); // this is the last bucket, and it's full already. Split // it by adding another bucket m_buckets.push_back(routing_table_node()); bucket_t& new_bucket = m_buckets.back().live_nodes; bucket_t& new_replacement_bucket = m_buckets.back().replacements; bucket_t& b = m_buckets[bucket_index].live_nodes; bucket_t& rb = m_buckets[bucket_index].replacements; // move any node whose (160 - distane_exp(m_id, id)) >= (i - m_buckets.begin()) // to the new bucket int const new_bucket_size = bucket_limit(bucket_index + 1); for (bucket_t::iterator j = b.begin(); j != b.end();) { int const d = distance_exp(m_id, j->id); if (d >= 159 - bucket_index) { ++j; continue; } // this entry belongs in the new bucket new_bucket.push_back(*j); j = b.erase(j); } if (b.size() > bucket_size_limit) { // TODO: 2 move the lowest priority nodes to the replacement bucket for (bucket_t::iterator i = b.begin() + bucket_size_limit , end(b.end()); i != end; ++i) { rb.push_back(*i); } b.resize(bucket_size_limit); } // split the replacement bucket as well. If the live bucket // is not full anymore, also move the replacement entries // into the main bucket for (bucket_t::iterator j = rb.begin(); j != rb.end();) { if (distance_exp(m_id, j->id) >= 159 - bucket_index) { if (int(b.size()) >= bucket_size_limit) { ++j; continue; } b.push_back(*j); } else { // this entry belongs in the new bucket if (int(new_bucket.size()) < new_bucket_size) new_bucket.push_back(*j); else new_replacement_bucket.push_back(*j); } j = rb.erase(j); } } void routing_table::update_node_id(node_id id) { m_id = id; m_ips.clear(); // pull all nodes out of the routing table, effectively emptying it table_t old_buckets; old_buckets.swap(m_buckets); // then add them all back. First add the main nodes, then the replacement // nodes for (int i = 0; i < old_buckets.size(); ++i) { bucket_t const& bucket = old_buckets[i].live_nodes; for (int j = 0; j < bucket.size(); ++j) { add_node(bucket[j]); } } // now add back the replacement nodes for (int i = 0; i < old_buckets.size(); ++i) { bucket_t const& bucket = old_buckets[i].replacements; for (int j = 0; j < bucket.size(); ++j) { add_node(bucket[j]); } } } void routing_table::for_each_node( void (*fun1)(void*, node_entry const&) , void (*fun2)(void*, node_entry const&) , void* userdata) const { for (table_t::const_iterator i = m_buckets.begin() , end(m_buckets.end()); i != end; ++i) { if (fun1) { for (bucket_t::const_iterator j = i->live_nodes.begin() , end2(i->live_nodes.end()); j != end2; ++j) fun1(userdata, *j); } if (fun2) { for (bucket_t::const_iterator j = i->replacements.begin() , end2(i->replacements.end()); j != end2; ++j) fun2(userdata, *j); } } } void routing_table::node_failed(node_id const& nid, udp::endpoint const& ep) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; #endif // if messages to ourself fails, ignore it if (nid == m_id) return; table_t::iterator i = find_bucket(nid); bucket_t& b = i->live_nodes; bucket_t& rb = i->replacements; bucket_t::iterator j = std::find_if(b.begin(), b.end() , boost::bind(&node_entry::id, _1) == nid); if (j == b.end()) { j = std::find_if(rb.begin(), rb.end() , boost::bind(&node_entry::id, _1) == nid); if (j == rb.end() || j->ep() != ep) return; j->timed_out(); #ifndef TORRENT_DISABLE_LOGGING char hex_id[41]; to_hex(nid.data(), 20, hex_id); m_log->log(dht_logger::routing_table, "NODE FAILED id: %s ip: %s fails: %d pinged: %d up-time: %d" , hex_id, print_endpoint(j->ep()).c_str() , int(j->fail_count()) , int(j->pinged()) , int(total_seconds(aux::time_now() - j->first_seen))); #endif return; } // if the endpoint doesn't match, it's a different node // claiming the same ID. The node we have in our routing // table is not necessarily stale if (j->ep() != ep) return; if (rb.empty()) { j->timed_out(); #ifndef TORRENT_DISABLE_LOGGING char hex_id[41]; to_hex(nid.data(), 20, hex_id); m_log->log(dht_logger::routing_table, "NODE FAILED id: %s ip: %s fails: %d pinged: %d up-time: %d" , hex_id, print_endpoint(j->ep()).c_str() , int(j->fail_count()) , int(j->pinged()) , int(total_seconds(aux::time_now() - j->first_seen))); #endif // if this node has failed too many times, or if this node // has never responded at all, remove it if (j->fail_count() >= m_settings.max_fail_count || !j->pinged()) { erase_one(m_ips, j->addr().to_v4().to_bytes()); b.erase(j); } return; } erase_one(m_ips, j->a); b.erase(j); // sort by RTT first, to find the node with the lowest // RTT that is pinged std::sort(rb.begin(), rb.end() , boost::bind(&node_entry::rtt, _1) < boost::bind(&node_entry::rtt, _2)); j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::pinged, _1)); if (j == rb.end()) j = rb.begin(); b.push_back(*j); rb.erase(j); } void routing_table::add_router_node(udp::endpoint router) { #if !TORRENT_USE_IPV6 TORRENT_ASSERT(router.address().is_v4()); #endif m_router_nodes.insert(router); } // we heard from this node, but we don't know if it was spoofed or not (i.e. // pinged == false) void routing_table::heard_about(node_id const& id, udp::endpoint const& ep) { if (!verify_node_address(m_settings, id, ep.address())) return; add_node(node_entry(id, ep)); } // 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 ep, int rtt) { if (!verify_node_address(m_settings, id, ep.address())) return false; return add_node(node_entry(id, ep, rtt, 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& l, int options, int count) { l.clear(); if (count == 0) count = m_bucket_size; table_t::iterator i = find_bucket(target); int bucket_index = std::distance(m_buckets.begin(), i); int bucket_size_limit = bucket_limit(bucket_index); l.reserve(bucket_size_limit); table_t::iterator j = i; int unsorted_start_idx = 0; for (; j != m_buckets.end() && int(l.size()) < count; ++j) { bucket_t& b = j->live_nodes; if (options & include_failed) { copy(b.begin(), b.end() , std::back_inserter(l)); } else { std::remove_copy_if(b.begin(), b.end() , std::back_inserter(l) , !boost::bind(&node_entry::confirmed, _1)); } if (int(l.size()) == count) return; if (int(l.size()) > count) { // sort the nodes by how close they are to the target std::sort(l.begin() + unsorted_start_idx, l.end(), boost::bind(&compare_ref , boost::bind(&node_entry::id, _1) , boost::bind(&node_entry::id, _2), target)); l.resize(count); return; } unsorted_start_idx = int(l.size()); } // if we still don't have enough nodes, copy nodes // further away from us if (i == m_buckets.begin()) return; j = i; unsorted_start_idx = int(l.size()); do { --j; bucket_t& b = j->live_nodes; if (options & include_failed) { std::copy(b.begin(), b.end(), std::back_inserter(l)); } else { std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l) , !boost::bind(&node_entry::confirmed, _1)); } if (int(l.size()) == count) return; if (int(l.size()) > count) { // sort the nodes by how close they are to the target std::sort(l.begin() + unsorted_start_idx, l.end(), boost::bind(&compare_ref , boost::bind(&node_entry::id, _1) , boost::bind(&node_entry::id, _2), target)); l.resize(count); return; } unsorted_start_idx = int(l.size()); } while (j != m_buckets.begin() && int(l.size()) < count); TORRENT_ASSERT(int(l.size()) <= count); } #if TORRENT_USE_INVARIANT_CHECKS void routing_table::check_invariant() const { boost::unordered_multiset all_ips; for (table_t::const_iterator i = m_buckets.begin() , end(m_buckets.end()); i != end; ++i) { for (bucket_t::const_iterator j = i->replacements.begin(); j != i->replacements.end(); ++j) { all_ips.insert(j->addr().to_v4().to_bytes()); } for (bucket_t::const_iterator j = i->live_nodes.begin(); j != i->live_nodes.end(); ++j) { all_ips.insert(j->addr().to_v4().to_bytes()); } } TORRENT_ASSERT(all_ips == m_ips); } #endif bool routing_table::is_full(int bucket) const { int num_buckets = m_buckets.size(); if (num_buckets == 0) return false; if (bucket >= num_buckets) return false; table_t::const_iterator i = m_buckets.begin(); std::advance(i, bucket); return (i->live_nodes.size() >= bucket_limit(bucket) && i->replacements.size() >= m_bucket_size); } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/rpc_manager.cpp000066400000000000000000000330101351156116000232600ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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/config.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include #include #include #include // for generate_random_id #include #include #include #include #include #include #include #include #include #include #include // for print_endpoint #include #include // for dht_settings #include #include // for aux::time_now namespace libtorrent { namespace dht { namespace io = libtorrent::detail; void intrusive_ptr_add_ref(observer const* o) { TORRENT_ASSERT(o != 0); TORRENT_ASSERT(o->m_refs < 0xffff); ++o->m_refs; } void intrusive_ptr_release(observer const* o) { TORRENT_ASSERT(o != 0); TORRENT_ASSERT(o->m_refs > 0); if (--o->m_refs == 0) { boost::intrusive_ptr ta = o->algorithm(); (const_cast(o))->~observer(); ta->free_observer(const_cast(o)); } } // TODO: 3 move this into it's own .cpp file dht_observer* observer::get_observer() const { return m_algorithm->get_node().observer(); } void observer::set_target(udp::endpoint const& ep) { m_sent = clock_type::now(); m_port = ep.port(); #if TORRENT_USE_IPV6 if (ep.address().is_v6()) { flags |= flag_ipv6_address; m_addr.v6 = ep.address().to_v6().to_bytes(); } else #endif { flags &= ~flag_ipv6_address; m_addr.v4 = ep.address().to_v4().to_bytes(); } } address observer::target_addr() const { #if TORRENT_USE_IPV6 if (flags & flag_ipv6_address) return address_v6(m_addr.v6); else #endif return address_v4(m_addr.v4); } udp::endpoint observer::target_ep() const { return udp::endpoint(target_addr(), m_port); } void observer::abort() { if (flags & flag_done) return; flags |= flag_done; m_algorithm->failed(observer_ptr(this), traversal_algorithm::prevent_request); } void observer::done() { if (flags & flag_done) return; flags |= flag_done; m_algorithm->finished(observer_ptr(this)); } void observer::short_timeout() { if (flags & flag_short_timeout) return; m_algorithm->failed(observer_ptr(this), traversal_algorithm::short_timeout); } void observer::timeout() { if (flags & flag_done) return; flags |= flag_done; m_algorithm->failed(observer_ptr(this)); } void observer::set_id(node_id const& id) { if (m_id == id) return; m_id = id; if (m_algorithm) m_algorithm->resort_results(); } enum { observer_size = max9< sizeof(find_data_observer) , sizeof(announce_observer) , sizeof(put_data_observer) , sizeof(direct_observer) , sizeof(get_item_observer) , sizeof(get_peers_observer) , sizeof(obfuscated_get_peers_observer) , sizeof(null_observer) , sizeof(traversal_observer) >::value }; rpc_manager::rpc_manager(node_id const& our_id , dht_settings const& settings , routing_table& table, udp_socket_interface* sock , dht_logger* log) : m_pool_allocator(observer_size, 10) , m_sock(sock) , m_log(log) , m_settings(settings) , m_table(table) , m_timer(aux::time_now()) , m_our_id(our_id) , m_allocated_observers(0) , m_destructing(false) {} rpc_manager::~rpc_manager() { TORRENT_ASSERT(!m_destructing); m_destructing = true; for (transactions_t::iterator i = m_transactions.begin() , end(m_transactions.end()); i != end; ++i) { i->second->abort(); } } void* rpc_manager::allocate_observer() { m_pool_allocator.set_next_size(10); void* ret = m_pool_allocator.malloc(); if (ret) ++m_allocated_observers; return ret; } void rpc_manager::free_observer(void* ptr) { if (!ptr) return; --m_allocated_observers; TORRENT_ASSERT(reinterpret_cast(ptr)->m_in_use == false); m_pool_allocator.free(ptr); } #if TORRENT_USE_ASSERTS size_t rpc_manager::allocation_size() const { return observer_size; } #endif #if TORRENT_USE_INVARIANT_CHECKS void rpc_manager::check_invariant() const { for (transactions_t::const_iterator i = m_transactions.begin() , end(m_transactions.end()); i != end; ++i) { TORRENT_ASSERT(i->second); } } #endif void rpc_manager::unreachable(udp::endpoint const& ep) { #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "PORT_UNREACHABLE [ ip: %s ]" , print_endpoint(ep).c_str()); #endif for (transactions_t::iterator i = m_transactions.begin(); i != m_transactions.end();) { TORRENT_ASSERT(i->second); observer_ptr const& o = i->second; if (o->target_ep() != ep) { ++i; continue; } observer_ptr ptr = i->second; i = m_transactions.erase(i); #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "found transaction [ tid: %d ]" , int(ptr->transaction_id())); #endif ptr->timeout(); break; } } bool rpc_manager::incoming(msg const& m, node_id* id) { INVARIANT_CHECK; if (m_destructing) return false; // we only deal with replies and errors, not queries TORRENT_ASSERT(m.message.dict_find_string_value("y") == "r" || m.message.dict_find_string_value("y") == "e"); // if we don't have the transaction id in our // request list, ignore the packet std::string transaction_id = m.message.dict_find_string_value("t"); if (transaction_id.empty()) return false; std::string::const_iterator ptr = transaction_id.begin(); int tid = transaction_id.size() != 2 ? -1 : io::read_uint16(ptr); observer_ptr o; std::pair range = m_transactions.equal_range(tid); for (transactions_t::iterator i = range.first; i != range.second; ++i) { if (m.addr.address() != i->second->target_addr()) continue; o = i->second; i = m_transactions.erase(i); break; } if (!o) { #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "reply with unknown transaction id size: %d from %s" , int(transaction_id.size()), print_endpoint(m.addr).c_str()); #endif // this isn't necessarily because the other end is doing // something wrong. This can also happen when we restart // the node, and we prematurely abort all outstanding // requests. Also, this opens up a potential magnification // attack. // entry e; // incoming_error(e, "invalid transaction id"); // m_sock->send_packet(e, m.addr, 0); return false; } time_point now = clock_type::now(); #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "round trip time(ms): %" PRId64 " from %s" , total_milliseconds(now - o->sent()), print_endpoint(m.addr).c_str()); #endif if (m.message.dict_find_string_value("y") == "e") { // It's an error. #ifndef TORRENT_DISABLE_LOGGING bdecode_node err = m.message.dict_find_list("e"); if (err && err.list_size() >= 2 && err.list_at(0).type() == bdecode_node::int_t && err.list_at(1).type() == bdecode_node::string_t) { m_log->log(dht_logger::rpc_manager, "reply with error from %s: (%" PRId64 ") %s" , print_endpoint(m.addr).c_str() , err.list_int_value_at(0) , err.list_string_value_at(1).c_str()); } else { m_log->log(dht_logger::rpc_manager, "reply with (malformed) error from %s" , print_endpoint(m.addr).c_str()); } #endif // Logically, we should call o->reply(m) since we get a reply. // a reply could be "response" or "error", here the reply is an "error". // if the reply is an "error", basically the observer could/will // do nothing with it, especially when observer::reply() is intended to // handle a "response", not an "error". // A "response" should somehow call algorithm->finished(), and an error/timeout // should call algorithm->failed(). From this point of view, // we should call o->timeout() instead of o->reply(m) because o->reply() // will call algorithm->finished(). o->timeout(); return false; } bdecode_node ret_ent = m.message.dict_find_dict("r"); if (!ret_ent) { o->timeout(); return false; } bdecode_node node_id_ent = ret_ent.dict_find_string("id"); if (!node_id_ent || node_id_ent.string_length() != 20) { o->timeout(); return false; } node_id nid = node_id(node_id_ent.string_ptr()); if (m_settings.enforce_node_id && !verify_id(nid, m.addr.address())) { o->timeout(); return false; } #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "[%p] reply with transaction id: %d from %s" , static_cast(o->algorithm()), int(transaction_id.size()) , print_endpoint(m.addr).c_str()); #endif o->reply(m); *id = nid; int rtt = int(total_milliseconds(now - o->sent())); // we found an observer for this reply, hence the node is not spoofing // add it to the routing table return m_table.node_seen(*id, m.addr, rtt); } time_duration rpc_manager::tick() { INVARIANT_CHECK; static const int short_timeout = 1; static const int timeout = 15; // look for observers that have timed out if (m_transactions.empty()) return seconds(short_timeout); std::vector timeouts; std::vector short_timeouts; time_duration ret = seconds(short_timeout); time_point now = aux::time_now(); for (transactions_t::iterator i = m_transactions.begin(); i != m_transactions.end();) { observer_ptr o = i->second; time_duration diff = now - o->sent(); if (diff >= seconds(timeout)) { #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "[%p] timing out transaction id: %d from: %s" , static_cast(o->algorithm()), o->transaction_id() , print_endpoint(o->target_ep()).c_str()); #endif m_transactions.erase(i++); timeouts.push_back(o); continue; } // don't call short_timeout() again if we've // already called it once if (diff >= seconds(short_timeout) && !o->has_short_timeout()) { #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "[%p] short-timing out transaction id: %d from: %s" , static_cast(o->algorithm()), o->transaction_id() , print_endpoint(o->target_ep()).c_str()); #endif ++i; short_timeouts.push_back(o); continue; } ret = std::min(seconds(timeout) - diff, ret); ++i; } std::for_each(timeouts.begin(), timeouts.end(), boost::bind(&observer::timeout, _1)); std::for_each(short_timeouts.begin(), short_timeouts.end(), boost::bind(&observer::short_timeout, _1)); return (std::max)(ret, duration_cast(milliseconds(200))); } void rpc_manager::add_our_id(entry& e) { e["id"] = m_our_id.to_string(); } bool rpc_manager::invoke(entry& e, udp::endpoint target_addr , observer_ptr o) { INVARIANT_CHECK; if (m_destructing) return false; e["y"] = "q"; entry& a = e["a"]; add_our_id(a); std::string transaction_id; transaction_id.resize(2); char* out = &transaction_id[0]; int tid = (random() ^ (random() << 5)) & 0xffff; io::write_uint16(tid, out); e["t"] = transaction_id; // When a DHT node enters the read-only state, in each outgoing query message, // places a 'ro' key in the top-level message dictionary and sets its value to 1. if (m_settings.read_only) e["ro"] = 1; o->set_target(target_addr); o->set_transaction_id(tid); #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "[%p] invoking %s -> %s" , static_cast(o->algorithm()), e["q"].string().c_str() , print_endpoint(target_addr).c_str()); #endif if (m_sock->send_packet(e, target_addr, 1)) { m_transactions.insert(std::make_pair(tid, o)); #if TORRENT_USE_ASSERTS o->m_was_sent = true; #endif return true; } return false; } observer::~observer() { // if the message was sent, it must have been // reported back to the traversal_algorithm as // well. If it wasn't sent, it cannot have been // reported back TORRENT_ASSERT(m_was_sent == bool(flags & flag_done) || m_was_abandoned); TORRENT_ASSERT(!m_in_constructor); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_in_use); m_in_use = false; #endif } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/kademlia/traversal_algorithm.cpp000066400000000000000000000446251351156116000250710ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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/time.hpp" // for total_seconds #include #include #include #include #include // for dht_logger #include #include // for read_*_endpoint #include // for dht_lookup #include #include namespace libtorrent { namespace dht { using detail::read_v4_endpoint; #if TORRENT_USE_IPV6 using detail::read_v6_endpoint; #endif #if TORRENT_USE_ASSERTS template bool is_sorted(It b, It e, Cmp cmp) { if (b == e) return true; typename std::iterator_traits::value_type v = *b; ++b; while (b != e) { if (cmp(*b, v)) return false; v = *b; ++b; } return true; } #endif observer_ptr traversal_algorithm::new_observer(void* ptr , udp::endpoint const& ep, node_id const& id) { observer_ptr o(new (ptr) null_observer(boost::intrusive_ptr(this), ep, id)); #if TORRENT_USE_ASSERTS o->m_in_constructor = false; #endif return o; } traversal_algorithm::traversal_algorithm( node& dht_node , node_id target) : m_node(dht_node) , m_target(target) , m_ref_count(0) , m_invoke_count(0) , m_branch_factor(3) , m_responses(0) , m_timeouts(0) { #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer()) { char hex_target[41]; to_hex(reinterpret_cast(&target[0]), 20, hex_target); get_node().observer()->log(dht_logger::traversal, "[%p] NEW target: %s k: %d" , static_cast(this), hex_target, int(m_node.m_table.bucket_size())); } #endif } void traversal_algorithm::resort_results() { std::sort( m_results.begin() , m_results.end() , boost::bind( compare_ref , boost::bind(&observer::id, _1) , boost::bind(&observer::id, _2) , m_target ) ); } void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags) { TORRENT_ASSERT(m_node.m_rpc.allocation_size() >= sizeof(find_data_observer)); void* ptr = m_node.m_rpc.allocate_observer(); if (ptr == 0) { #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer()) { get_node().observer()->log(dht_logger::traversal, "[%p] failed to allocate memory or observer. aborting!" , static_cast(this)); } #endif done(); return; } observer_ptr o = new_observer(ptr, addr, id); if (id.is_all_zeros()) { o->set_id(generate_random_id()); o->flags |= observer::flag_no_id; } o->flags |= flags; TORRENT_ASSERT(libtorrent::dht::is_sorted(m_results.begin(), m_results.end() , boost::bind( compare_ref , boost::bind(&observer::id, _1) , boost::bind(&observer::id, _2) , m_target) )); std::vector::iterator iter = std::lower_bound( m_results.begin() , m_results.end() , o , boost::bind( compare_ref , boost::bind(&observer::id, _1) , boost::bind(&observer::id, _2) , m_target ) ); if (iter == m_results.end() || (*iter)->id() != id) { if (m_node.settings().restrict_search_ips && !(flags & observer::flag_initial)) { // mask the lower octet boost::uint32_t prefix4 = o->target_addr().to_v4().to_ulong(); prefix4 &= 0xffffff00; if (m_peer4_prefixes.count(prefix4) > 0) { // we already have a node in this search with an IP very // close to this one. We know that it's not the same, because // it claims a different node-ID. Ignore this to avoid attacks #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer()) { char hex_id[41]; to_hex(reinterpret_cast(&o->id()[0]), 20, hex_id); get_node().observer()->log(dht_logger::traversal , "[%p] traversal DUPLICATE node. id: %s addr: %s type: %s" , static_cast(this), hex_id, print_address(o->target_addr()).c_str(), name()); } #endif return; } m_peer4_prefixes.insert(prefix4); } TORRENT_ASSERT((o->flags & observer::flag_no_id) || std::find_if(m_results.begin(), m_results.end() , boost::bind(&observer::id, _1) == id) == m_results.end()); #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer()) { char hex_id[41]; to_hex(reinterpret_cast(&id[0]), 20, hex_id); get_node().observer()->log(dht_logger::traversal , "[%p] ADD id: %s addr: %s distance: %d invoke-count: %d type: %s" , static_cast(this), hex_id, print_endpoint(addr).c_str() , distance_exp(m_target, id), m_invoke_count, name()); } #endif iter = m_results.insert(iter, o); TORRENT_ASSERT(libtorrent::dht::is_sorted(m_results.begin(), m_results.end() , boost::bind( compare_ref , boost::bind(&observer::id, _1) , boost::bind(&observer::id, _2) , m_target) )); } if (m_results.size() > 100) { for (int i = 100; i < int(m_results.size()); ++i) { if ((m_results[i]->flags & (observer::flag_queried | observer::flag_failed | observer::flag_alive)) == observer::flag_queried) { // set the done flag on any outstanding queries to prevent them from // calling finished() or failed() m_results[i]->flags |= observer::flag_done; TORRENT_ASSERT(m_invoke_count > 0); --m_invoke_count; } #if TORRENT_USE_ASSERTS m_results[i]->m_was_abandoned = true; #endif } m_results.resize(100); } } void traversal_algorithm::start() { // in case the routing table is empty, use the // router nodes in the table if (m_results.size() < 3) add_router_entries(); init(); bool is_done = add_requests(); if (is_done) done(); } void* traversal_algorithm::allocate_observer() { return m_node.m_rpc.allocate_observer(); } void traversal_algorithm::free_observer(void* ptr) { m_node.m_rpc.free_observer(ptr); } char const* traversal_algorithm::name() const { return "traversal_algorithm"; } void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr) { #ifndef TORRENT_DISABLE_LOGGING if (id.is_all_zeros() && get_node().observer()) { get_node().observer()->log(dht_logger::traversal , "[%p] WARNING node returned a list which included a node with id 0" , static_cast(this)); } #endif // let the routing table know this node may exist m_node.m_table.heard_about(id, addr); add_entry(id, addr, 0); } void traversal_algorithm::finished(observer_ptr o) { #if TORRENT_USE_ASSERTS std::vector::iterator i = std::find( m_results.begin(), m_results.end(), o); TORRENT_ASSERT(i != m_results.end() || m_results.size() == 100); #endif // if this flag is set, it means we increased the // branch factor for it, and we should restore it if (o->flags & observer::flag_short_timeout) { TORRENT_ASSERT(m_branch_factor > 0); --m_branch_factor; } TORRENT_ASSERT(o->flags & observer::flag_queried); o->flags |= observer::flag_alive; ++m_responses; TORRENT_ASSERT(m_invoke_count > 0); --m_invoke_count; bool is_done = add_requests(); if (is_done) done(); } // prevent request means that the total number of requests has // overflown. This query failed because it was the oldest one. // So, if this is true, don't make another request void traversal_algorithm::failed(observer_ptr o, int flags) { // don't tell the routing table about // node ids that we just generated ourself if ((o->flags & observer::flag_no_id) == 0) m_node.m_table.node_failed(o->id(), o->target_ep()); if (m_results.empty()) return; bool decrement_branch_factor = false; TORRENT_ASSERT(o->flags & observer::flag_queried); if (flags & short_timeout) { // short timeout means that it has been more than // two seconds since we sent the request, and that // we'll most likely not get a response. But, in case // we do get a late response, keep the handler // around for some more, but open up the slot // by increasing the branch factor if ((o->flags & observer::flag_short_timeout) == 0) { TORRENT_ASSERT(m_branch_factor < (std::numeric_limits::max)()); ++m_branch_factor; } o->flags |= observer::flag_short_timeout; #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer()) { char hex_id[41]; to_hex(reinterpret_cast(&o->id()[0]), 20, hex_id); get_node().observer()->log(dht_logger::traversal , "[%p] 1ST_TIMEOUT id: %s distance: %d addr: %s branch-factor: %d " "invoke-count: %d type: %s" , static_cast(this), hex_id, distance_exp(m_target, o->id()) , print_address(o->target_addr()).c_str(), m_branch_factor , m_invoke_count, name()); } #endif } else { o->flags |= observer::flag_failed; // if this flag is set, it means we increased the // branch factor for it, and we should restore it decrement_branch_factor = (o->flags & observer::flag_short_timeout) != 0; #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer()) { char hex_id[41]; to_hex(reinterpret_cast(&o->id()[0]), 20, hex_id); get_node().observer()->log(dht_logger::traversal , "[%p] TIMEOUT id: %s distance: %d addr: %s branch-factor: %d " "invoke-count: %d type: %s" , static_cast(this), hex_id, distance_exp(m_target, o->id()) , print_address(o->target_addr()).c_str(), m_branch_factor , m_invoke_count, name()); } #endif ++m_timeouts; TORRENT_ASSERT(m_invoke_count > 0); --m_invoke_count; } // this is another reason to decrement the branch factor, to prevent another // request from filling this slot. Only ever decrement once per response though decrement_branch_factor |= (flags & prevent_request); if (decrement_branch_factor) { TORRENT_ASSERT(m_branch_factor > 0); --m_branch_factor; if (m_branch_factor <= 0) m_branch_factor = 1; } bool const is_done = add_requests(); if (is_done) done(); } void traversal_algorithm::done() { #ifndef TORRENT_DISABLE_LOGGING int results_target = m_node.m_table.bucket_size(); int closest_target = 160; #endif for (std::vector::iterator i = m_results.begin() , end(m_results.end()); i != end; ++i) { boost::intrusive_ptr o = *i; if ((o->flags & (observer::flag_queried | observer::flag_failed)) == observer::flag_queried) { // set the done flag on any outstanding queries to prevent them from // calling finished() or failed() after we've already declared the traversal // done o->flags |= observer::flag_done; } #ifndef TORRENT_DISABLE_LOGGING if (results_target > 0 && (o->flags & observer::flag_alive) && get_node().observer()) { TORRENT_ASSERT(o->flags & observer::flag_queried); char hex_id[41]; to_hex(reinterpret_cast(&o->id()[0]), 20, hex_id); get_node().observer()->log(dht_logger::traversal , "[%p] id: %s distance: %d addr: %s" , static_cast(this), hex_id, closest_target , print_endpoint(o->target_ep()).c_str()); --results_target; int dist = distance_exp(m_target, o->id()); if (dist < closest_target) closest_target = dist; } #endif } #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer()) { get_node().observer()->log(dht_logger::traversal , "[%p] COMPLETED distance: %d type: %s" , static_cast(this), closest_target, name()); } #endif // delete all our references to the observer objects so // they will in turn release the traversal algorithm m_results.clear(); m_invoke_count = 0; } bool traversal_algorithm::add_requests() { int results_target = m_node.m_table.bucket_size(); // this only counts outstanding requests at the top of the // target list. This is <= m_invoke count. m_invoke_count // is the total number of outstanding requests, including // old ones that may be waiting on nodes much farther behind // the current point we've reached in the search. int outstanding = 0; // if we're doing aggressive lookups, we keep branch-factor // outstanding requests _at the tops_ of the result list. Otherwise // we just keep any branch-factor outstanding requests bool agg = m_node.settings().aggressive_lookups; // Find the first node that hasn't already been queried. // and make sure that the 'm_branch_factor' top nodes // stay queried at all times (obviously ignoring failed nodes) // and without surpassing the 'result_target' nodes (i.e. k=8) // this is a slight variation of the original paper which instead // limits the number of outstanding requests, this limits the // number of good outstanding requests. It will use more traffic, // but is intended to speed up lookups for (std::vector::iterator i = m_results.begin() , end(m_results.end()); i != end && results_target > 0 && (agg ? outstanding < m_branch_factor : m_invoke_count < m_branch_factor); ++i) { observer* o = i->get(); if (o->flags & observer::flag_alive) { TORRENT_ASSERT(o->flags & observer::flag_queried); --results_target; continue; } if (o->flags & observer::flag_queried) { // if it's queried, not alive and not failed, it // must be currently in flight if ((o->flags & observer::flag_failed) == 0) ++outstanding; continue; } #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer()) { char hex_id[41]; to_hex(reinterpret_cast(&o->id()[0]), 20, hex_id); get_node().observer()->log(dht_logger::traversal , "[%p] INVOKE nodes-left: %d top-invoke-count: %d " "invoke-count: %d branch-factor: %d " "distance: %d id: %s addr: %s type: %s" , static_cast(this), int(m_results.end() - i), outstanding, int(m_invoke_count) , int(m_branch_factor), distance_exp(m_target, o->id()), hex_id , print_address(o->target_addr()).c_str(), name()); } #endif o->flags |= observer::flag_queried; if (invoke(*i)) { TORRENT_ASSERT(m_invoke_count < (std::numeric_limits::max)()); ++m_invoke_count; ++outstanding; } else { o->flags |= observer::flag_failed; } } // this is the completion condition. If we found m_node.m_table.bucket_size() // (i.e. k=8) completed results, without finding any still // outstanding requests, we're done. // also, if invoke count is 0, it means we didn't even find 'k' // working nodes, we still have to terminate though. return (results_target == 0 && outstanding == 0) || m_invoke_count == 0; } void traversal_algorithm::add_router_entries() { #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer()) { get_node().observer()->log(dht_logger::traversal , "[%p] using router nodes to initiate traversal algorithm %d routers" , static_cast(this), int(std::distance(m_node.m_table.router_begin(), m_node.m_table.router_end()))); } #endif for (routing_table::router_iterator i = m_node.m_table.router_begin() , end(m_node.m_table.router_end()); i != end; ++i) { add_entry(node_id(0), *i, observer::flag_initial); } } void traversal_algorithm::init() { m_branch_factor = m_node.branch_factor(); m_node.add_traversal_algorithm(this); } traversal_algorithm::~traversal_algorithm() { m_node.remove_traversal_algorithm(this); } void traversal_algorithm::status(dht_lookup& l) { l.timeouts = m_timeouts; l.responses = m_responses; l.outstanding_requests = m_invoke_count; l.branch_factor = m_branch_factor; l.type = name(); l.nodes_left = 0; l.first_timeout = 0; int last_sent = INT_MAX; time_point now = aux::time_now(); for (std::vector::iterator i = m_results.begin() , end(m_results.end()); i != end; ++i) { observer& o = **i; if (o.flags & observer::flag_queried) { last_sent = (std::min)(last_sent, int(total_seconds(now - o.sent()))); if (o.has_short_timeout()) ++l.first_timeout; continue; } ++l.nodes_left; } l.last_sent = last_sent; } void traversal_observer::reply(msg const& m) { bdecode_node r = m.message.dict_find_dict("r"); if (!r) { #ifndef TORRENT_DISABLE_LOGGING if (get_observer()) { get_observer()->log(dht_logger::traversal , "[%p] missing response dict" , static_cast(algorithm())); } #endif return; } #ifndef TORRENT_DISABLE_LOGGING if (get_observer()) { bdecode_node nid = r.dict_find_string("id"); char hex_id[41]; to_hex(nid.string_ptr(), 20, hex_id); get_observer()->log(dht_logger::traversal , "[%p] RESPONSE id: %s invoke-count: %d addr: %s type: %s" , static_cast(algorithm()), hex_id, algorithm()->invoke_count() , print_endpoint(target_ep()).c_str(), algorithm()->name()); } #endif // look for nodes bdecode_node n = r.dict_find_string("nodes"); if (n) { char const* nodes = n.string_ptr(); char const* end = nodes + n.string_length(); while (end - nodes >= 26) { node_id id; std::copy(nodes, nodes + 20, id.begin()); nodes += 20; algorithm()->traverse(id, read_v4_endpoint(nodes)); } } bdecode_node id = r.dict_find_string("id"); if (!id || id.string_length() != 20) { #ifndef TORRENT_DISABLE_LOGGING if (get_observer()) { get_observer()->log(dht_logger::traversal, "[%p] invalid id in response" , static_cast(algorithm())); } #endif return; } // in case we didn't know the id of this peer when we sent the message to // it. For instance if it's a bootstrap node. set_id(node_id(id.string_ptr())); } } } // namespace libtorrent::dht libtorrent-rasterbar-1.1.13/src/lazy_bdecode.cpp000066400000000000000000000417071351156116000216730ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_NO_DEPRECATE #include "libtorrent/config.hpp" #include "libtorrent/lazy_entry.hpp" #include "libtorrent/bdecode.hpp" // for error codes #include #include // for numeric_limits namespace { const int lazy_entry_grow_factor = 150; // percent const int lazy_entry_dict_init = 5; const int lazy_entry_list_init = 5; } namespace libtorrent { namespace { int fail(int* error_pos , std::vector& stack , char const* start , char const* orig_start) { while (!stack.empty()) { lazy_entry* top = stack.back(); if (top->type() == lazy_entry::dict_t || top->type() == lazy_entry::list_t) { top->pop(); break; } stack.pop_back(); } if (error_pos) *error_pos = start - orig_start; return -1; } #define TORRENT_FAIL_BDECODE(code) do { ec = make_error_code(code); return fail(error_pos, stack, start, orig_start); } TORRENT_WHILE_0 bool numeric(char c) { return c >= '0' && c <= '9'; } char const* find_char(char const* start, char const* end, char delimiter) { while (start < end && *start != delimiter) ++start; return start; } } // anonymous namespace #ifndef TORRENT_NO_DEPRECATE int lazy_bdecode(char const* start, char const* end , lazy_entry& ret, int depth_limit, int item_limit) { error_code ec; int pos; return lazy_bdecode(start, end, ret, ec, &pos, depth_limit, item_limit); } #endif // return 0 = success int lazy_bdecode(char const* start, char const* end, lazy_entry& ret , error_code& ec, int* error_pos, int depth_limit, int item_limit) { char const* const orig_start = start; ret.clear(); std::vector stack; if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); stack.push_back(&ret); while (start <= end) { if (stack.empty()) break; // done! lazy_entry* top = stack.back(); if (int(stack.size()) > depth_limit) TORRENT_FAIL_BDECODE(bdecode_errors::depth_exceeded); if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); char t = *start; ++start; if (start >= end && t != 'e') TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); switch (top->type()) { case lazy_entry::dict_t: { if (t == 'e') { top->set_end(start); stack.pop_back(); continue; } if (!numeric(t)) TORRENT_FAIL_BDECODE(bdecode_errors::expected_digit); boost::int64_t len = t - '0'; bdecode_errors::error_code_enum e = bdecode_errors::no_error; start = parse_int(start, end, ':', len, e); if (e) TORRENT_FAIL_BDECODE(e); if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon); // remaining buffer size excluding ':' const ptrdiff_t buff_size = end - start - 1; if (len > buff_size) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); if (len < 0) TORRENT_FAIL_BDECODE(bdecode_errors::overflow); ++start; if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); lazy_entry* ent = top->dict_append(start); if (ent == 0) TORRENT_FAIL_BDECODE(boost::system::errc::not_enough_memory); start += len; if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); stack.push_back(ent); t = *start; ++start; break; } case lazy_entry::list_t: { if (t == 'e') { top->set_end(start); stack.pop_back(); continue; } lazy_entry* ent = top->list_append(); if (ent == 0) TORRENT_FAIL_BDECODE(boost::system::errc::not_enough_memory); stack.push_back(ent); break; } case lazy_entry::int_t: case lazy_entry::string_t: case lazy_entry::none_t: break; } --item_limit; if (item_limit <= 0) TORRENT_FAIL_BDECODE(bdecode_errors::limit_exceeded); top = stack.back(); switch (t) { case 'd': top->construct_dict(start - 1); break; case 'l': top->construct_list(start - 1); break; case 'i': { char const* int_start = start; start = find_char(start, end, 'e'); top->construct_int(int_start, start - int_start); if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); TORRENT_ASSERT(*start == 'e'); ++start; stack.pop_back(); break; } default: { if (!numeric(t)) TORRENT_FAIL_BDECODE(bdecode_errors::expected_value); boost::int64_t len = t - '0'; bdecode_errors::error_code_enum e = bdecode_errors::no_error; start = parse_int(start, end, ':', len, e); if (e) TORRENT_FAIL_BDECODE(e); if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon); // remaining buffer size excluding ':' const ptrdiff_t buff_size = end - start - 1; if (len > buff_size) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); if (len < 0) TORRENT_FAIL_BDECODE(bdecode_errors::overflow); ++start; if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); top->construct_string(start, int(len)); start += len; stack.pop_back(); break; } } } return 0; } int lazy_entry::capacity() const { TORRENT_ASSERT(m_type == dict_t || m_type == list_t); if (m_data.list == NULL) return 0; if (m_type == dict_t) return m_data.dict[0].val.m_len; else return m_data.list[0].m_len; } boost::int64_t lazy_entry::int_value() const { TORRENT_ASSERT(m_type == int_t); boost::int64_t val = 0; bool negative = false; if (*m_data.start == '-') negative = true; bdecode_errors::error_code_enum ec = bdecode_errors::no_error; parse_int(m_data.start + negative , m_data.start + m_size, 'e', val, ec); if (ec) return 0; if (negative) val = -val; return val; } lazy_entry* lazy_entry::dict_append(char const* name) { TORRENT_ASSERT(m_type == dict_t); TORRENT_ASSERT(m_size <= this->capacity()); if (m_data.dict == NULL) { int capacity = lazy_entry_dict_init; m_data.dict = new (std::nothrow) lazy_dict_entry[capacity+1]; if (m_data.dict == NULL) return NULL; m_data.dict[0].val.m_len = capacity; } else if (m_size == this->capacity()) { int capacity = this->capacity() * lazy_entry_grow_factor / 100; lazy_dict_entry* tmp = new (std::nothrow) lazy_dict_entry[capacity+1]; if (tmp == NULL) return NULL; std::memcpy(tmp, m_data.dict, sizeof(lazy_dict_entry) * (m_size + 1)); for (int i = 0; i < int(m_size); ++i) m_data.dict[i+1].val.release(); delete[] m_data.dict; m_data.dict = tmp; m_data.dict[0].val.m_len = capacity; } TORRENT_ASSERT(m_size < this->capacity()); lazy_dict_entry& ret = m_data.dict[1+m_size++]; ret.name = name; return &ret.val; } void lazy_entry::pop() { if (m_size > 0) --m_size; } namespace { // the number of decimal digits needed // to represent the given value int num_digits(int val) { int ret = 1; while (val >= 10) { ++ret; val /= 10; } return ret; } } void lazy_entry::construct_string(char const* start, int length) { TORRENT_ASSERT(m_type == none_t); m_type = string_t; m_data.start = start; m_size = length; m_begin = start - 1 - num_digits(length); m_len = start - m_begin + length; } namespace { // str1 is null-terminated // str2 is not, str2 is len2 chars bool string_equal(char const* str1, char const* str2, int len2) { while (len2 > 0) { if (*str1 != *str2) return false; if (*str1 == 0) return false; ++str1; ++str2; --len2; } return *str1 == 0; } } std::pair lazy_entry::dict_at(int i) const { TORRENT_ASSERT(m_type == dict_t); TORRENT_ASSERT(i < int(m_size)); lazy_dict_entry const& e = m_data.dict[i+1]; return std::make_pair(std::string(e.name, e.val.m_begin - e.name), &e.val); } std::string lazy_entry::dict_find_string_value(char const* name) const { lazy_entry const* e = dict_find(name); if (e == 0 || e->type() != lazy_entry::string_t) return std::string(); return e->string_value(); } pascal_string lazy_entry::dict_find_pstr(char const* name) const { lazy_entry const* e = dict_find(name); if (e == 0 || e->type() != lazy_entry::string_t) return pascal_string(0, 0); return e->string_pstr(); } lazy_entry const* lazy_entry::dict_find_string(char const* name) const { lazy_entry const* e = dict_find(name); if (e == 0 || e->type() != lazy_entry::string_t) return 0; return e; } lazy_entry const* lazy_entry::dict_find_int(char const* name) const { lazy_entry const* e = dict_find(name); if (e == 0 || e->type() != lazy_entry::int_t) return 0; return e; } boost::int64_t lazy_entry::dict_find_int_value(char const* name , boost::int64_t default_val) const { lazy_entry const* e = dict_find(name); if (e == 0 || e->type() != lazy_entry::int_t) return default_val; return e->int_value(); } lazy_entry const* lazy_entry::dict_find_dict(char const* name) const { lazy_entry const* e = dict_find(name); if (e == 0 || e->type() != lazy_entry::dict_t) return 0; return e; } lazy_entry const* lazy_entry::dict_find_dict(std::string const& name) const { lazy_entry const* e = dict_find(name); if (e == 0 || e->type() != lazy_entry::dict_t) return 0; return e; } lazy_entry const* lazy_entry::dict_find_list(char const* name) const { lazy_entry const* e = dict_find(name); if (e == 0 || e->type() != lazy_entry::list_t) return 0; return e; } lazy_entry* lazy_entry::dict_find(char const* name) { TORRENT_ASSERT(m_type == dict_t); for (int i = 0; i < int(m_size); ++i) { lazy_dict_entry& e = m_data.dict[i+1]; if (string_equal(name, e.name, e.val.m_begin - e.name)) return &e.val; } return 0; } lazy_entry* lazy_entry::dict_find(std::string const& name) { TORRENT_ASSERT(m_type == dict_t); for (int i = 0; i < int(m_size); ++i) { lazy_dict_entry& e = m_data.dict[i+1]; if (name.size() != e.val.m_begin - e.name) continue; if (std::equal(name.begin(), name.end(), e.name)) return &e.val; } return 0; } lazy_entry* lazy_entry::list_append() { TORRENT_ASSERT(m_type == list_t); TORRENT_ASSERT(m_size <= this->capacity()); if (m_data.start == NULL) { int capacity = lazy_entry_list_init; m_data.list = new (std::nothrow) lazy_entry[capacity+1]; if (m_data.list == 0) return 0; m_data.list[0].m_len = capacity; } else if (m_size == this->capacity()) { int capacity = this->capacity() * lazy_entry_grow_factor / 100; lazy_entry* tmp = new (std::nothrow) lazy_entry[capacity+1]; if (tmp == NULL) return NULL; std::memcpy(tmp, m_data.list, sizeof(lazy_entry) * (m_size+1)); for (int i = 0; i < int(m_size); ++i) m_data.list[i+1].release(); delete[] m_data.list; m_data.list = tmp; m_data.list[0].m_len = capacity; } TORRENT_ASSERT(m_size < this->capacity()); return &m_data.list[1 + (m_size++)]; } std::string lazy_entry::list_string_value_at(int i) const { lazy_entry const* e = list_at(i); if (e == 0 || e->type() != lazy_entry::string_t) return std::string(); return e->string_value(); } pascal_string lazy_entry::list_pstr_at(int i) const { lazy_entry const* e = list_at(i); if (e == 0 || e->type() != lazy_entry::string_t) return pascal_string(0, 0); return e->string_pstr(); } boost::int64_t lazy_entry::list_int_value_at(int i, boost::int64_t default_val) const { lazy_entry const* e = list_at(i); if (e == 0 || e->type() != lazy_entry::int_t) return default_val; return e->int_value(); } void lazy_entry::clear() { switch (m_type) { case list_t: delete[] m_data.list; break; case dict_t: delete[] m_data.dict; break; default: break; } m_data.start = NULL; m_size = 0; m_type = none_t; } std::pair lazy_entry::data_section() const { typedef std::pair return_t; return return_t(m_begin, m_len); } namespace { int line_longer_than(lazy_entry const& e, int limit) { int line_len = 0; switch (e.type()) { case lazy_entry::list_t: line_len += 4; if (line_len > limit) return -1; for (int i = 0; i < e.list_size(); ++i) { int ret = line_longer_than(*e.list_at(i), limit - line_len); if (ret == -1) return -1; line_len += ret + 2; } break; case lazy_entry::dict_t: line_len += 4; if (line_len > limit) return -1; for (int i = 0; i < e.dict_size(); ++i) { line_len += 4 + e.dict_at(i).first.size(); if (line_len > limit) return -1; int ret = line_longer_than(*e.dict_at(i).second, limit - line_len); if (ret == -1) return -1; line_len += ret + 1; } break; case lazy_entry::string_t: line_len += 3 + e.string_length(); break; case lazy_entry::int_t: { boost::int64_t val = e.int_value(); while (val > 0) { ++line_len; val /= 10; } line_len += 2; } break; case lazy_entry::none_t: line_len += 4; break; } if (line_len > limit) return -1; return line_len; } void escape_string(std::string& ret, char const* str, int len) { for (int i = 0; i < len; ++i) { if (str[i] >= 32 && str[i] < 127) { ret += str[i]; } else { char tmp[5]; snprintf(tmp, sizeof(tmp), "\\x%02x", boost::uint8_t(str[i])); ret += tmp; } } } void print_string(std::string& ret, char const* str, int len, bool single_line) { bool printable = true; for (int i = 0; i < len; ++i) { char c = str[i]; if (c >= 32 && c < 127) continue; printable = false; break; } ret += "'"; if (printable) { if (single_line && len > 30) { ret.append(str, 14); ret += "..."; ret.append(str + len-14, 14); } else ret.append(str, len); ret += "'"; return; } if (single_line && len > 20) { escape_string(ret, str, 9); ret += "..."; escape_string(ret, str + len - 9, 9); } else { escape_string(ret, str, len); } ret += "'"; } } // anonymous namespace std::string print_entry(lazy_entry const& e, bool single_line, int indent) { char indent_str[200]; memset(indent_str, ' ', 200); indent_str[0] = ','; indent_str[1] = '\n'; indent_str[199] = 0; if (indent < 197 && indent >= 0) indent_str[indent+2] = 0; std::string ret; switch (e.type()) { case lazy_entry::none_t: return "none"; case lazy_entry::int_t: { char str[100]; snprintf(str, sizeof(str), "%" PRId64, e.int_value()); return str; } case lazy_entry::string_t: { print_string(ret, e.string_ptr(), e.string_length(), single_line); return ret; } case lazy_entry::list_t: { ret += '['; bool one_liner = line_longer_than(e, 200) != -1 || single_line; if (!one_liner) ret += indent_str + 1; for (int i = 0; i < e.list_size(); ++i) { if (i == 0 && one_liner) ret += " "; ret += print_entry(*e.list_at(i), single_line, indent + 2); if (i < e.list_size() - 1) ret += (one_liner?", ":indent_str); else ret += (one_liner?" ":indent_str+1); } ret += "]"; return ret; } case lazy_entry::dict_t: { ret += "{"; bool one_liner = line_longer_than(e, 200) != -1 || single_line; if (!one_liner) ret += indent_str+1; for (int i = 0; i < e.dict_size(); ++i) { if (i == 0 && one_liner) ret += " "; std::pair ent = e.dict_at(i); print_string(ret, ent.first.c_str(), ent.first.size(), true); ret += ": "; ret += print_entry(*ent.second, single_line, indent + 2); if (i < e.dict_size() - 1) ret += (one_liner?", ":indent_str); else ret += (one_liner?" ":indent_str+1); } ret += "}"; return ret; } } return ret; } } #endif // TORRENT_NO_DEPRECATE libtorrent-rasterbar-1.1.13/src/lsd.cpp000066400000000000000000000177111351156116000200270ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/lsd.hpp" #include "libtorrent/io.hpp" #include "libtorrent/http_tracker_connection.hpp" #include "libtorrent/buffer.hpp" #include "libtorrent/random.hpp" #include "libtorrent/http_parser.hpp" #include "libtorrent/socket_io.hpp" // for print_address #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/debug.hpp" #endif #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { namespace { int render_lsd_packet(char* dst, int const len, int const listen_port , char const* info_hash_hex, int const cookie, char const* host) { TORRENT_ASSERT(len > 0); return snprintf(dst, len, "BT-SEARCH * HTTP/1.1\r\n" "Host: %s:6771\r\n" "Port: %d\r\n" "Infohash: %s\r\n" "cookie: %x\r\n" "\r\n\r\n", host, listen_port, info_hash_hex, cookie); } } // anonymous namespace static error_code dummy; lsd::lsd(io_service& ios, peer_callback_t const& cb #ifndef TORRENT_DISABLE_LOGGING , log_callback_t const& log #endif ) : m_callback(cb) , m_socket(udp::endpoint(address_v4::from_string("239.192.152.143", dummy), 6771)) #if TORRENT_USE_IPV6 , m_socket6(udp::endpoint(address_v6::from_string("ff15::efc0:988f", dummy), 6771)) #endif #ifndef TORRENT_DISABLE_LOGGING , m_log_cb(log) #endif , m_broadcast_timer(ios) , m_cookie((random() ^ uintptr_t(this)) & 0x7fffffff) , m_disabled(false) #if TORRENT_USE_IPV6 , m_disabled6(false) #endif { } #ifndef TORRENT_DISABLE_LOGGING TORRENT_FORMAT(2,3) void lsd::debug_log(char const* fmt, ...) const { va_list v; va_start(v, fmt); char buf[1024]; vsnprintf(buf, sizeof(buf), fmt, v); va_end(v); m_log_cb(buf); } #endif void lsd::start(error_code& ec) { m_socket.open(boost::bind(&lsd::on_announce, self(), _1, _2, _3) , lt::get_io_service(m_broadcast_timer), ec); if (ec) return; #if TORRENT_USE_IPV6 m_socket6.open(boost::bind(&lsd::on_announce, self(), _1, _2, _3) , lt::get_io_service(m_broadcast_timer), ec); #endif } lsd::~lsd() {} void lsd::announce(sha1_hash const& ih, int listen_port, bool broadcast) { announce_impl(ih, listen_port, broadcast, 0); } void lsd::announce_impl(sha1_hash const& ih, int const listen_port , bool const broadcast, int retry_count) { #if TORRENT_USE_IPV6 if (m_disabled && m_disabled6) return; #else if (m_disabled) return; #endif char ih_hex[41]; to_hex(ih.data(), 20, ih_hex); char msg[200]; #ifndef TORRENT_DISABLE_LOGGING debug_log("==> LSD: ih: %s port: %u\n", ih_hex, listen_port); #endif error_code ec; if (!m_disabled) { int const msg_len = render_lsd_packet(msg, sizeof(msg), listen_port, ih_hex , m_cookie, "239.192.152.143"); m_socket.send(msg, msg_len, ec, broadcast ? broadcast_socket::broadcast : 0); if (ec) { m_disabled = true; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** LSD: failed to send message: (%d) %s", ec.value() , ec.message().c_str()); #endif } } #if TORRENT_USE_IPV6 if (!m_disabled6) { int const msg_len = render_lsd_packet(msg, sizeof(msg), listen_port, ih_hex , m_cookie, "[ff15::efc0:988f]"); m_socket6.send(msg, msg_len, ec, broadcast ? broadcast_socket::broadcast : 0); if (ec) { m_disabled6 = true; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** LSD: failed to send message6: (%d) %s", ec.value() , ec.message().c_str()); #endif } } #endif ++retry_count; if (retry_count >= 3) return; #if TORRENT_USE_IPV6 if (m_disabled && m_disabled6) return; #else if (m_disabled) return; #endif #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("lsd::resend_announce"); #endif m_broadcast_timer.expires_from_now(seconds(2 * retry_count), ec); m_broadcast_timer.async_wait(boost::bind(&lsd::resend_announce, self(), _1 , ih, listen_port, retry_count)); } void lsd::resend_announce(error_code const& e, sha1_hash const& info_hash , int listen_port, int retry_count) { #if defined TORRENT_ASIO_DEBUGGING complete_async("lsd::resend_announce"); #endif if (e) return; announce_impl(info_hash, listen_port, false, retry_count); } void lsd::on_announce(udp::endpoint const& from, char* buf , std::size_t bytes_transferred) { using namespace libtorrent::detail; http_parser p; bool error = false; p.incoming(buffer::const_interval(buf, buf + bytes_transferred) , error); if (!p.header_finished() || error) { #ifndef TORRENT_DISABLE_LOGGING debug_log("<== LSD: incomplete HTTP message"); #endif return; } if (p.method() != "bt-search") { #ifndef TORRENT_DISABLE_LOGGING debug_log("<== LSD: invalid HTTP method: %s", p.method().c_str()); #endif return; } std::string const& port_str = p.header("port"); if (port_str.empty()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("<== LSD: invalid BT-SEARCH, missing port"); #endif return; } int port = std::atoi(port_str.c_str()); typedef std::multimap headers_t; headers_t const& headers = p.headers(); headers_t::const_iterator cookie_iter = headers.find("cookie"); if (cookie_iter != headers.end()) { // we expect it to be hexadecimal // if it isn't, it's not our cookie anyway boost::int32_t const cookie = strtol(cookie_iter->second.c_str(), NULL, 16); if (cookie == m_cookie) { #ifndef TORRENT_DISABLE_LOGGING debug_log("<== LSD: ignoring packet (cookie matched our own): %x" , cookie); #endif return; } } std::pair ihs = headers.equal_range("infohash"); for (headers_t::const_iterator i = ihs.first; i != ihs.second; ++i) { std::string const& ih_str = i->second; if (ih_str.size() != 40) { #ifndef TORRENT_DISABLE_LOGGING debug_log("<== LSD: invalid BT-SEARCH, invalid infohash: %s" , ih_str.c_str()); #endif continue; } sha1_hash ih(0); from_hex(ih_str.c_str(), 40, ih.data()); if (!ih.is_all_zeros() && port != 0) { #ifndef TORRENT_DISABLE_LOGGING debug_log("<== LSD: %s:%d ih: %s" , print_address(from.address()).c_str() , port, ih_str.c_str()); #endif // we got an announce, pass it on through the callback TORRENT_TRY { m_callback(tcp::endpoint(from.address(), port), ih); } TORRENT_CATCH(std::exception&) {} } } } void lsd::close() { m_socket.close(); #if TORRENT_USE_IPV6 m_socket6.close(); #endif error_code ec; m_broadcast_timer.cancel(ec); m_disabled = true; #if TORRENT_USE_IPV6 m_disabled6 = true; #endif m_callback.clear(); } } // libtorrent namespace libtorrent-rasterbar-1.1.13/src/lt_trackers.cpp000066400000000000000000000257411351156116000215640ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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_NO_DEPRECATE #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/peer_connection_handle.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/extensions/lt_trackers.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/io.hpp" #include "libtorrent/parse_url.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/announce_entry.hpp" namespace libtorrent { namespace { bool send_tracker(announce_entry const& e) { // max_fails == 0 means that it's one // of the trackers from the trackers // from the torrent file return e.fail_limit == 0 || e.verified; } struct lt_tracker_plugin : torrent_plugin { lt_tracker_plugin(torrent& t) : m_torrent(t) , m_updates(0) , m_2_minutes(110) , m_num_trackers(0) { m_old_trackers = t.trackers(); update_list_hash(); } virtual boost::shared_ptr new_connection( peer_connection_handle const& pc); virtual void tick() { if (m_2_minutes++ < 120) return; m_2_minutes = 0; // build tracker diff entry tex; entry::list_type& added = tex["added"].list(); std::vector const& trackers = m_torrent.trackers(); for (std::vector::const_iterator i = trackers.begin() , end(trackers.end()); i != end; ++i) { std::vector::const_iterator k = std::find_if( m_old_trackers.begin(), m_old_trackers.end() , boost::bind(&announce_entry::url, _1) == i->url); if (k != m_old_trackers.end()) continue; if (!send_tracker(*i)) continue; m_old_trackers.push_back(*i); ++m_updates; added.push_back(i->url); } m_lt_trackers_msg.clear(); bencode(std::back_inserter(m_lt_trackers_msg), tex); if (m_updates > 0) update_list_hash(); } void update_list_hash() { std::vector canonical_list; std::transform(m_old_trackers.begin(), m_old_trackers.end(), back_inserter(canonical_list) , boost::bind(&announce_entry::url, _1)); std::sort(canonical_list.begin(), canonical_list.end()); hasher h; std::for_each(canonical_list.begin(), canonical_list.end() , boost::bind(&hasher::update, &h, _1)); m_list_hash = h.final(); } int num_updates() const { return m_updates; } std::vector const& get_lt_tex_msg() const { return m_lt_trackers_msg; } sha1_hash const& list_hash() const { return m_list_hash; } std::vector const& trackers() const { return m_old_trackers; } void increment_tracker_counter() { m_num_trackers++; } int num_tex_trackers() const { return m_num_trackers; } private: torrent& m_torrent; std::vector m_old_trackers; int m_updates; int m_2_minutes; std::vector m_lt_trackers_msg; sha1_hash m_list_hash; int m_num_trackers; }; struct lt_tracker_peer_plugin : peer_plugin { lt_tracker_peer_plugin(torrent& t, bt_peer_connection& pc, lt_tracker_plugin& tp) : m_message_index(0) , m_torrent(t) , m_pc(pc) , m_tp(tp) , m_2_minutes(115) , m_full_list(true) {} // can add entries to the extension handshake virtual void add_handshake(entry& h) { entry& messages = h["m"]; messages["lt_tex"] = 19; h["tr"] = m_tp.list_hash().to_string(); } // called when the extension handshake from the other end is received virtual bool on_extension_handshake(bdecode_node const& h) { m_message_index = 0; if (h.type() != bdecode_node::dict_t) return false; bdecode_node messages = h.dict_find("m"); if (!messages || messages.type() != bdecode_node::dict_t) return false; int index = int(messages.dict_find_int_value("lt_tex", -1)); if (index == -1) return false; m_message_index = index; // if we have the same tracker list, don't bother sending the // full list. Just send deltas std::string tracker_list_hash = h.dict_find_string_value("tr"); if (tracker_list_hash.size() == 20 && sha1_hash(tracker_list_hash) == m_tp.list_hash()) { m_full_list = false; } return true; } virtual bool on_extended(int /* length */ , int extended_msg, buffer::const_interval body) { if (extended_msg != 19) return false; if (m_message_index == 0) return false; if (!m_pc.packet_finished()) return true; bdecode_node msg; error_code ec; int ret = bdecode(body.begin, body.end, msg, ec); if (ret != 0 || msg.type() != bdecode_node::dict_t) { m_pc.disconnect(errors::invalid_lt_tracker_message, op_bittorrent, 2); return true; } bdecode_node added = msg.dict_find_list("added"); // invalid tex message if (added == 0) { #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "LT_TEX" , "NOT A DICTIONARY"); #endif return true; } if (m_tp.num_tex_trackers() >= 50) { #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "LT_TEX" , "we already have %d trackers from tex, don't add any more" , m_tp.num_tex_trackers()); #endif return true; } #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "LT_TEX"); #endif for (int i = 0; i < added.list_size(); ++i) { announce_entry e(added.list_string_value_at(i)); if (e.url.empty()) continue; // ignore urls with binary data in them if (need_encoding(e.url.c_str(), e.url.size())) continue; // ignore invalid URLs error_code err; std::string protocol; std::string auth; std::string hostname; int port; std::string path; boost::tie(protocol, auth, hostname, port, path) = parse_url_components(e.url, err); if (err) continue; // ignore unknown protocols if (protocol != "udp" && protocol != "http" && protocol != "https") continue; // ignore invalid ports if (port == 0) continue; if (m_tp.num_tex_trackers() >= 50) break; #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::info, "LT_TEX", "adding: %s", e.url.c_str()); #endif e.fail_limit = 1; e.send_stats = false; e.source = announce_entry::source_tex; if (m_torrent.add_tracker(e)) m_tp.increment_tracker_counter(); } return true; } virtual void tick() { // no handshake yet if (!m_message_index) return; if (++m_2_minutes <= 120) return; m_2_minutes = 0; if (m_full_list) { if (send_full_tex_list()) m_full_list = false; } else { send_lt_tex_diff(); } } private: void send_lt_tex_diff() { // if there's no change in out tracker set, don't send anything if (m_tp.num_updates() == 0) return; if (!m_torrent.valid_metadata() || m_torrent.torrent_file().priv()) return; std::vector const& tex_msg = m_tp.get_lt_tex_msg(); char msg[6]; char* ptr = msg; detail::write_uint32(1 + 1 + tex_msg.size(), ptr); detail::write_uint8(bt_peer_connection::msg_extended, ptr); detail::write_uint8(m_message_index, ptr); m_pc.send_buffer(msg, sizeof(msg)); m_pc.send_buffer(&tex_msg[0], tex_msg.size()); m_pc.setup_send(); } bool send_full_tex_list() const { if (m_tp.trackers().empty()) return false; if (!m_torrent.valid_metadata() || m_torrent.torrent_file().priv()) return false; #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::outgoing_message, "LT_TEX"); #endif entry tex; entry::list_type& added = tex["added"].list(); for (std::vector::const_iterator i = m_tp.trackers().begin() , end(m_tp.trackers().end()); i != end; ++i) { if (!send_tracker(*i)) continue; added.push_back(i->url); #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::info, "LT_TEX" , "sending: %s", i->url.c_str()); #endif } std::vector tex_msg; bencode(std::back_inserter(tex_msg), tex); char msg[6]; char* ptr = msg; detail::write_uint32(1 + 1 + tex_msg.size(), ptr); detail::write_uint8(bt_peer_connection::msg_extended, ptr); detail::write_uint8(m_message_index, ptr); m_pc.send_buffer(msg, sizeof(msg)); m_pc.send_buffer(&tex_msg[0], tex_msg.size()); m_pc.setup_send(); return true; } // this is the message index the remote peer uses // for metadata extension messages. int m_message_index; torrent& m_torrent; bt_peer_connection& m_pc; lt_tracker_plugin& m_tp; int m_2_minutes; bool m_full_list; }; boost::shared_ptr lt_tracker_plugin::new_connection( peer_connection_handle const& pc) { if (pc.type() != peer_connection::bittorrent_connection) return boost::shared_ptr(); if (m_torrent.valid_metadata() && m_torrent.torrent_file().priv()) return boost::shared_ptr(); bt_peer_connection* c = static_cast(pc.native_handle().get()); return boost::shared_ptr(new lt_tracker_peer_plugin(m_torrent, *c, *this)); } } } namespace libtorrent { boost::shared_ptr TORRENT_EXPORT create_lt_trackers_plugin(torrent_handle const& th, void*) { torrent* t = th.native_handle().get(); if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr(); return boost::shared_ptr(new lt_tracker_plugin(*t)); } } #endif #endif libtorrent-rasterbar-1.1.13/src/magnet_uri.cpp000066400000000000000000000205251351156116000213740ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/magnet_uri.hpp" #include "libtorrent/session.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/aux_/escape_string.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/torrent_status.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/announce_entry.hpp" #include #include "libtorrent/socket_io.hpp" namespace libtorrent { std::string make_magnet_uri(torrent_handle const& handle) { if (!handle.is_valid()) return ""; std::string ret; sha1_hash const& ih = handle.info_hash(); ret += "magnet:?xt=urn:btih:"; ret += to_hex(ih.to_string()); torrent_status st = handle.status(torrent_handle::query_name); if (!st.name.empty()) { ret += "&dn="; ret += escape_string(st.name.c_str(), int(st.name.length())); } std::vector const& tr = handle.trackers(); for (std::vector::const_iterator i = tr.begin(), end(tr.end()); i != end; ++i) { ret += "&tr="; ret += escape_string(i->url.c_str(), int(i->url.length())); } std::set seeds = handle.url_seeds(); for (std::set::iterator i = seeds.begin() , end(seeds.end()); i != end; ++i) { ret += "&ws="; ret += escape_string(i->c_str(), int(i->length())); } return ret; } std::string make_magnet_uri(torrent_info const& info) { std::string ret; sha1_hash const& ih = info.info_hash(); ret += "magnet:?xt=urn:btih:"; ret += to_hex(ih.to_string()); std::string const& name = info.name(); if (!name.empty()) { ret += "&dn="; ret += escape_string(name.c_str(), name.length()); } std::vector const& tr = info.trackers(); for (std::vector::const_iterator i = tr.begin(), end(tr.end()); i != end; ++i) { ret += "&tr="; ret += escape_string(i->url.c_str(), i->url.length()); } std::vector const& seeds = info.web_seeds(); for (std::vector::const_iterator i = seeds.begin() , end(seeds.end()); i != end; ++i) { if (i->type != web_seed_entry::url_seed) continue; ret += "&ws="; ret += escape_string(i->url.c_str(), i->url.length()); } return ret; } #ifndef TORRENT_NO_DEPRECATE namespace { torrent_handle add_magnet_uri_deprecated(session& ses, std::string const& uri , add_torrent_params p, error_code& ec) { parse_magnet_uri(uri, p, ec); if (ec) return torrent_handle(); return ses.add_torrent(p, ec); } } torrent_handle add_magnet_uri(session& ses, std::string const& uri , add_torrent_params const& p, error_code& ec) { return add_magnet_uri_deprecated(ses, uri, p, ec); } #ifndef BOOST_NO_EXCEPTIONS torrent_handle add_magnet_uri(session& ses, std::string const& uri , std::string const& save_path , storage_mode_t storage_mode , bool paused , storage_constructor_type sc , void* userdata) { add_torrent_params params(sc); params.storage_mode = storage_mode; params.userdata = userdata; params.save_path = save_path; if (paused) params.flags |= add_torrent_params::flag_paused; else params.flags &= ~add_torrent_params::flag_paused; error_code ec; std::string display_name = url_has_argument(uri, "dn"); if (!display_name.empty()) params.name = unescape_string(display_name.c_str(), ec); std::string tracker_string = url_has_argument(uri, "tr"); if (!tracker_string.empty()) params.trackers.push_back(unescape_string(tracker_string.c_str(), ec)); std::string btih = url_has_argument(uri, "xt"); if (btih.empty()) return torrent_handle(); if (btih.compare(0, 9, "urn:btih:") != 0) return torrent_handle(); if (btih.size() == 40 + 9) from_hex(&btih[9], 40, params.info_hash.data()); else params.info_hash.assign(base32decode(btih.substr(9))); return ses.add_torrent(params); } torrent_handle add_magnet_uri(session& ses, std::string const& uri , add_torrent_params const& p) { error_code ec; torrent_handle ret = add_magnet_uri_deprecated(ses, uri, p, ec); if (ec) throw libtorrent_exception(ec); return ret; } #endif // BOOST_NO_EXCEPTIONS #endif // TORRENT_NO_DEPRECATE void parse_magnet_uri(std::string const& uri, add_torrent_params& p, error_code& ec) { ec.clear(); std::string name; { error_code e; std::string display_name = url_has_argument(uri, "dn"); if (!display_name.empty()) name = unescape_string(display_name.c_str(), e); } // parse trackers out of the magnet link std::string::size_type pos = std::string::npos; std::string url = url_has_argument(uri, "tr", &pos); while (pos != std::string::npos) { error_code e; url = unescape_string(url, e); if (!e) p.trackers.push_back(url); pos = uri.find("&tr=", pos); if (pos == std::string::npos) break; pos += 4; url = uri.substr(pos, uri.find('&', pos) - pos); } // parse web seeds out of the magnet link pos = std::string::npos; url = url_has_argument(uri, "ws", &pos); while (pos != std::string::npos) { error_code e; url = unescape_string(url, e); if (!e) p.url_seeds.push_back(url); pos = uri.find("&ws=", pos); if (pos == std::string::npos) break; pos += 4; url = uri.substr(pos, uri.find('&', pos) - pos); } std::string btih = url_has_argument(uri, "xt"); if (btih.empty()) { ec = errors::missing_info_hash_in_uri; return; } if (btih.compare(0, 9, "urn:btih:") != 0) { ec = errors::missing_info_hash_in_uri; return; } #ifndef TORRENT_DISABLE_DHT std::string::size_type node_pos = std::string::npos; std::string node = url_has_argument(uri, "dht", &node_pos); while (!node.empty()) { std::string::size_type divider = node.find_last_of(':'); if (divider != std::string::npos) { int port = atoi(node.c_str()+divider+1); if (port != 0) p.dht_nodes.push_back(std::make_pair(node.substr(0, divider), port)); } node_pos = uri.find("&dht=", node_pos); if (node_pos == std::string::npos) break; node_pos += 5; node = uri.substr(node_pos, uri.find('&', node_pos) - node_pos); } #endif sha1_hash info_hash; if (btih.size() == 40 + 9) from_hex(&btih[9], 40, info_hash.data()); else if (btih.size() == 32 + 9) { std::string ih = base32decode(btih.substr(9)); if (ih.size() != 20) { ec = errors::invalid_info_hash; return; } info_hash.assign(ih); } else { ec = errors::invalid_info_hash; return; } p.info_hash = info_hash; if (!name.empty()) p.name = name; } void parse_magnet_uri_peers(std::string const& uri, std::vector& peers) { std::string::size_type peer_pos = std::string::npos; std::string peer = url_has_argument(uri, "x.pe", &peer_pos); while (!peer.empty()) { error_code e; tcp::endpoint endp = parse_endpoint(peer, e); if (!e) peers.push_back(endp); peer_pos = uri.find("&x.pe=", peer_pos); if (peer_pos == std::string::npos) break; peer_pos += 6; peer = uri.substr(peer_pos, uri.find('&', peer_pos) - peer_pos); } } } libtorrent-rasterbar-1.1.13/src/merkle.cpp000066400000000000000000000043141351156116000205170ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/aux_/merkle.hpp" namespace libtorrent { int merkle_get_parent(int tree_node) { // node 0 doesn't have a parent TORRENT_ASSERT(tree_node > 0); return (tree_node - 1) / 2; } int merkle_get_sibling(int tree_node) { // node 0 doesn't have a sibling TORRENT_ASSERT(tree_node > 0); // even numbers have their sibling to the left // odd numbers have their sibling to the right return tree_node + ((tree_node&1)?1:-1); } int merkle_num_nodes(int leafs) { TORRENT_ASSERT(leafs > 0); return (leafs << 1) - 1; } int merkle_num_leafs(int pieces) { TORRENT_ASSERT(pieces > 0); // round up to nearest 2 exponent int ret = 1; while (pieces > ret) ret <<= 1; return ret; } } libtorrent-rasterbar-1.1.13/src/metadata_transfer.cpp000066400000000000000000000432211351156116000227240ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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_NO_DEPRECATE #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include // count #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/peer_connection_handle.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/extensions/metadata_transfer.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/buffer.hpp" #include "libtorrent/io.hpp" namespace libtorrent { namespace { int div_round_up(int numerator, int denominator) { return (numerator + denominator - 1) / denominator; } std::pair req_to_offset(std::pair req, int total_size) { TORRENT_ASSERT(req.first >= 0); TORRENT_ASSERT(req.second > 0); TORRENT_ASSERT(req.second <= 256); TORRENT_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 offset_to_req(std::pair offset, int total_size) { int start = offset.first * 256 / total_size; int size = (offset.first + offset.second) * 256 / total_size - start; std::pair ret(start, size); TORRENT_ASSERT(start >= 0); TORRENT_ASSERT(size > 0); TORRENT_ASSERT(start <= 256); TORRENT_ASSERT(start + size <= 256); // assert the identity of this function #if TORRENT_USE_ASSERTS std::pair identity = req_to_offset(ret, total_size); TORRENT_ASSERT(offset == identity); #endif return ret; } struct metadata_plugin TORRENT_FINAL : torrent_plugin { metadata_plugin(torrent& t) : m_torrent(t) , m_metadata_progress(0) , m_metadata_size(0) { m_requested_metadata.resize(256, 0); } /* bool need_loaded() { return m_torrent.need_loaded(); } */ virtual void on_unload() TORRENT_OVERRIDE { m_metadata.reset(); } virtual void on_load() TORRENT_OVERRIDE { // initialize m_metadata_size TORRENT_ASSERT(m_torrent.is_loaded()); metadata(); } virtual void on_files_checked() TORRENT_OVERRIDE { // if the torrent is a seed, make a reference to // the metadata from the torrent before it is deallocated if (m_torrent.is_seed()) metadata(); } virtual boost::shared_ptr new_connection( peer_connection_handle const& pc) TORRENT_OVERRIDE; buffer::const_interval metadata() const { if (!m_metadata) { m_metadata = m_torrent.torrent_file().metadata(); m_metadata_size = m_torrent.torrent_file().metadata_size(); TORRENT_ASSERT(hasher(m_metadata.get(), m_metadata_size).final() == m_torrent.torrent_file().info_hash()); } return buffer::const_interval(m_metadata.get(), m_metadata.get() + m_metadata_size); } bool received_metadata(char const* buf, int size, int offset, int total_size) { if (m_torrent.valid_metadata()) return false; if (!m_metadata || m_metadata_size < total_size) { m_metadata.reset(new char[total_size]); m_metadata_size = total_size; } std::copy(buf, buf + size, &m_metadata[offset]); if (m_have_metadata.empty()) m_have_metadata.resize(256, false); std::pair req = offset_to_req(std::make_pair(offset, size) , total_size); TORRENT_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; if (!m_torrent.set_metadata(&m_metadata[0], m_metadata_size)) { std::fill( m_have_metadata.begin() , m_have_metadata.begin() + req.first + req.second , false); m_metadata_progress = 0; m_metadata_size = 0; return false; } // clear the storage for the bitfield std::vector().swap(m_have_metadata); std::vector().swap(m_requested_metadata); return true; } // returns a range of the metadata that // we should request. std::pair metadata_request(); void cancel_metadata_request(std::pair req) { for (int i = req.first; i < req.first + req.second; ++i) { TORRENT_ASSERT(m_requested_metadata[i] > 0); if (m_requested_metadata[i] > 0) --m_requested_metadata[i]; } } // this is called from the peer_connection for // each piece of metadata it receives void metadata_progress(int total_size, int received) { m_metadata_progress += received; m_metadata_size = total_size; m_torrent.set_progress_ppm(boost::int64_t(m_metadata_progress) * 1000000 / m_metadata_size); } void on_piece_pass(int) TORRENT_OVERRIDE { // if we became a seed, copy the metadata from // the torrent before it is deallocated if (m_torrent.is_seed()) metadata(); } /* int metadata_size() const { return m_metadata_size; } */ private: torrent& m_torrent; // 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 boost::shared_array m_metadata; int m_metadata_progress; mutable int m_metadata_size; // 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 m_have_metadata; // this vector keeps track of how many times each meatdata // block has been requested std::vector m_requested_metadata; // explicitly disallow assignment, to silence msvc warning metadata_plugin& operator=(metadata_plugin const&); }; struct metadata_peer_plugin TORRENT_FINAL : peer_plugin { metadata_peer_plugin(torrent& t, peer_connection& pc , metadata_plugin& tp) : m_waiting_metadata_request(false) , m_message_index(0) , m_metadata_progress(0) , m_no_metadata(min_time()) , m_metadata_request(min_time()) , m_torrent(t) , m_pc(pc) , m_tp(tp) {} virtual char const* type() const TORRENT_OVERRIDE { return "LT_metadata"; } // can add entries to the extension handshake virtual void add_handshake(entry& h) TORRENT_OVERRIDE { entry& messages = h["m"]; messages["LT_metadata"] = 14; } // called when the extension handshake from the other end is received virtual bool on_extension_handshake(bdecode_node const& h) TORRENT_OVERRIDE { m_message_index = 0; if (h.type() != bdecode_node::dict_t) return false; bdecode_node messages = h.dict_find("m"); if (!messages || messages.type() != bdecode_node::dict_t) return false; int index = int(messages.dict_find_int_value("LT_metadata", -1)); if (index == -1) return false; m_message_index = index; return true; } void write_metadata_request(std::pair req) { TORRENT_ASSERT(req.first >= 0); TORRENT_ASSERT(req.second > 0); TORRENT_ASSERT(req.first + req.second <= 256); TORRENT_ASSERT(!m_pc.associated_torrent().expired()); TORRENT_ASSERT(!m_pc.associated_torrent().lock()->valid_metadata()); int start = req.first; int size = req.second; // abort if the peer doesn't support the metadata extension if (m_message_index == 0) return; #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::outgoing_message, "METADATA_REQUEST" , "start: %d size: %d", start, size); #endif char msg[9]; char* ptr = msg; detail::write_uint32(1 + 1 + 3, ptr); detail::write_uint8(bt_peer_connection::msg_extended, ptr); detail::write_uint8(m_message_index, ptr); // means 'request data' detail::write_uint8(0, ptr); detail::write_uint8(start, ptr); detail::write_uint8(size - 1, ptr); m_pc.send_buffer(msg, sizeof(msg)); m_pc.setup_send(); } void write_metadata(std::pair req) { TORRENT_ASSERT(req.first >= 0); TORRENT_ASSERT(req.second > 0); TORRENT_ASSERT(req.second <= 256); TORRENT_ASSERT(req.first + req.second <= 256); TORRENT_ASSERT(!m_pc.associated_torrent().expired()); // abort if the peer doesn't support the metadata extension if (m_message_index == 0) return; if (m_torrent.valid_metadata()) { std::pair offset = req_to_offset(req, int(m_tp.metadata().left())); char msg[15]; char* ptr = msg; #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::outgoing_message, "METADATA" , "start: %d total_size: %d offset: %d data_size: %d" , req.first, req.second, offset.first, offset.second); #endif // yes, we have metadata, send it detail::write_uint32(11 + offset.second, ptr); detail::write_uint8(bt_peer_connection::msg_extended, ptr); detail::write_uint8(m_message_index, ptr); // means 'data packet' detail::write_uint8(1, ptr); detail::write_uint32(int(m_tp.metadata().left()), ptr); detail::write_uint32(offset.first, ptr); m_pc.send_buffer(msg, sizeof(msg)); // TODO: this is not safe. The torrent could be unloaded while // we're still sending the metadata char const* metadata = m_tp.metadata().begin; m_pc.append_const_send_buffer(metadata + offset.first, offset.second); } else { #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::outgoing_message, "METADATA" , "don't have metadata"); #endif char msg[4+3]; char* ptr = msg; // we don't have the metadata, reply with // don't have-message detail::write_uint32(1 + 2, ptr); detail::write_uint8(bt_peer_connection::msg_extended, ptr); detail::write_uint8(m_message_index, ptr); // means 'have no data' detail::write_uint8(2, ptr); m_pc.send_buffer(msg, sizeof(msg)); } m_pc.setup_send(); } virtual bool on_extended(int length , int msg, buffer::const_interval body) TORRENT_OVERRIDE { if (msg != 14) return false; if (m_message_index == 0) return false; if (length > 500 * 1024) { m_pc.disconnect(errors::metadata_too_large, op_bittorrent, 2); return true; } if (body.left() < 1) return true; int type = detail::read_uint8(body.begin); switch (type) { case 0: // request { if (body.left() < 2) return true; int start = detail::read_uint8(body.begin); int size = detail::read_uint8(body.begin) + 1; #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "METADATA_REQUEST" , "start: %d size: %d", start, size); #endif if (length != 3) { // invalid metadata request m_pc.disconnect(errors::invalid_metadata_request, op_bittorrent, 2); return true; } write_metadata(std::make_pair(start, size)); } break; case 1: // data { if (body.left() < 8) return true; int total_size = detail::read_int32(body.begin); int offset = detail::read_int32(body.begin); int data_size = length - 9; #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "METADATA" , "total_size: %d | offset: %d | data_size: %d" ,total_size, offset, data_size); #endif if (total_size > m_torrent.session().settings().get_int(settings_pack::max_metadata_size)) { m_pc.disconnect(errors::metadata_too_large, op_bittorrent, 2); return true; } if (total_size <= 0) { m_pc.disconnect(errors::invalid_metadata_size, op_bittorrent, 2); return true; } if (offset > total_size || offset < 0) { m_pc.disconnect(errors::invalid_metadata_offset, op_bittorrent, 2); return true; } if (offset + data_size > total_size) { m_pc.disconnect(errors::invalid_metadata_message, op_bittorrent, 2); return true; } m_tp.metadata_progress(total_size , body.left() - m_metadata_progress); m_metadata_progress = body.left(); if (body.left() < data_size) return true; m_waiting_metadata_request = false; m_tp.received_metadata(body.begin, data_size , offset, total_size); m_metadata_progress = 0; } break; case 2: // have no data m_no_metadata = aux::time_now(); if (m_waiting_metadata_request) m_tp.cancel_metadata_request(m_last_metadata_request); m_waiting_metadata_request = false; #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "METADATA" , "don't have metadata"); #endif break; default: { m_pc.disconnect(errors::invalid_metadata_message, op_bittorrent, 2); } } return true; } virtual void tick() TORRENT_OVERRIDE { if (m_pc.is_disconnecting()) 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 (!m_torrent.valid_metadata() && m_message_index != 0 && !m_waiting_metadata_request && has_metadata()) { m_last_metadata_request = m_tp.metadata_request(); write_metadata_request(m_last_metadata_request); m_waiting_metadata_request = true; m_metadata_request = aux::time_now(); } } bool has_metadata() const { return aux::time_now() - minutes(5) > m_no_metadata; } private: // 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; // this is the message index the remote peer uses // for metadata extension messages. int m_message_index; // 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; // this is set to the current time each time we get a // "I don't have metadata" message. time_point m_no_metadata; // this is set to the time when we last sent // a request for metadata to this peer time_point m_metadata_request; // if we're waiting for a metadata request // this was the request we sent std::pair m_last_metadata_request; torrent& m_torrent; peer_connection& m_pc; metadata_plugin& m_tp; // explicitly disallow assignment, to silence msvc warning metadata_peer_plugin& operator=(metadata_peer_plugin const&); }; boost::shared_ptr metadata_plugin::new_connection( peer_connection_handle const& pc) { if (pc.type() != peer_connection::bittorrent_connection) return boost::shared_ptr(); return boost::shared_ptr(new metadata_peer_plugin(m_torrent, *pc.native_handle().get(), *this)); } std::pair metadata_plugin::metadata_request() { // the number of blocks to request int num_blocks = 256 / 4; TORRENT_ASSERT(num_blocks <= 128); int min_element = (std::numeric_limits::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 ret(best_index, num_blocks); for (int i = ret.first; i < ret.first + ret.second; ++i) m_requested_metadata[i]++; TORRENT_ASSERT(ret.first >= 0); TORRENT_ASSERT(ret.second > 0); TORRENT_ASSERT(ret.second <= 256); TORRENT_ASSERT(ret.first + ret.second <= 256); return ret; } } } namespace libtorrent { boost::shared_ptr create_metadata_plugin(torrent_handle const& th, void*) { torrent* t = th.native_handle().get(); // don't add this extension if the torrent is private if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr(); return boost::shared_ptr(new metadata_plugin(*t)); } } #endif #endif libtorrent-rasterbar-1.1.13/src/mpi.cpp000066400000000000000000007413101351156116000200310ustar00rootroot00000000000000#include "libtorrent/aux_/disable_warnings_push.hpp" /* Start: bn_error.c */ #include "libtorrent/tommath_private.h" #ifdef BN_ERROR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ static const struct { int code; const char *msg; } msgs[] = { { MP_OKAY, "Successful" }, { MP_MEM, "Out of heap" }, { MP_VAL, "Value out of range" } }; /* return a char * string for a given code */ const char *mp_error_to_string(int code) { int x; /* scan the lookup table for the given message */ for (x = 0; x < (int)(sizeof(msgs) / sizeof(msgs[0])); x++) { if (msgs[x].code == code) { return msgs[x].msg; } } /* generic reply for invalid code */ return "Invalid error code"; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_error.c */ /* Start: bn_fast_mp_invmod.c */ #include "libtorrent/tommath_private.h" #ifdef BN_FAST_MP_INVMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes the modular inverse via binary extended euclidean algorithm, * that is c = 1/a mod b * * Based on slow invmod except this is optimized for the case where b is * odd as per HAC Note 14.64 on pp. 610 */ int fast_mp_invmod (mp_int * a, mp_int * b, mp_int * c) { mp_int x, y, u, v, B, D; int res, neg; /* 2. [modified] b must be odd */ if (mp_iseven (b) == MP_YES) { return MP_VAL; } /* init all our temps */ if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) { return res; } /* x == modulus, y == value to invert */ if ((res = mp_copy (b, &x)) != MP_OKAY) { goto LBL_ERR; } /* we need y = |a| */ if ((res = mp_mod (a, b, &y)) != MP_OKAY) { goto LBL_ERR; } /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ if ((res = mp_copy (&x, &u)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_copy (&y, &v)) != MP_OKAY) { goto LBL_ERR; } mp_set (&D, 1); top: /* 4. while u is even do */ while (mp_iseven (&u) == MP_YES) { /* 4.1 u = u/2 */ if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { goto LBL_ERR; } /* 4.2 if B is odd then */ if (mp_isodd (&B) == MP_YES) { if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { goto LBL_ERR; } } /* B = B/2 */ if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { goto LBL_ERR; } } /* 5. while v is even do */ while (mp_iseven (&v) == MP_YES) { /* 5.1 v = v/2 */ if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { goto LBL_ERR; } /* 5.2 if D is odd then */ if (mp_isodd (&D) == MP_YES) { /* D = (D-x)/2 */ if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { goto LBL_ERR; } } /* D = D/2 */ if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { goto LBL_ERR; } } /* 6. if u >= v then */ if (mp_cmp (&u, &v) != MP_LT) { /* u = u - v, B = B - D */ if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { goto LBL_ERR; } } else { /* v - v - u, D = D - B */ if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { goto LBL_ERR; } } /* if not zero goto step 4 */ if (mp_iszero (&u) == MP_NO) { goto top; } /* now a = C, b = D, gcd == g*v */ /* if v != 1 then there is no inverse */ if (mp_cmp_d (&v, 1) != MP_EQ) { res = MP_VAL; goto LBL_ERR; } /* b is now the inverse */ neg = a->sign; while (D.sign == MP_NEG) { if ((res = mp_add (&D, b, &D)) != MP_OKAY) { goto LBL_ERR; } } mp_exch (&D, c); c->sign = neg; res = MP_OKAY; LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &B, &D, NULL); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_fast_mp_invmod.c */ /* Start: bn_fast_mp_montgomery_reduce.c */ #include "libtorrent/tommath_private.h" #ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes xR**-1 == x (mod N) via Montgomery Reduction * * This is an optimized implementation of montgomery_reduce * which uses the comba method to quickly calculate the columns of the * reduction. * * Based on Algorithm 14.32 on pp.601 of HAC. */ int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) { int ix, res, olduse; mp_word W[MP_WARRAY]; /* get old used count */ olduse = x->used; /* grow a as required */ if (x->alloc < (n->used + 1)) { if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { return res; } } /* first we have to get the digits of the input into * an array of double precision words W[...] */ { mp_word *_W; mp_digit *tmpx; /* alias for the W[] array */ _W = W; /* alias for the digits of x*/ tmpx = x->dp; /* copy the digits of a into W[0..a->used-1] */ for (ix = 0; ix < x->used; ix++) { *_W++ = *tmpx++; } /* zero the high words of W[a->used..m->used*2] */ for (; ix < ((n->used * 2) + 1); ix++) { *_W++ = 0; } } /* now we proceed to zero successive digits * from the least significant upwards */ for (ix = 0; ix < n->used; ix++) { /* mu = ai * m' mod b * * We avoid a double precision multiplication (which isn't required) * by casting the value down to a mp_digit. Note this requires * that W[ix-1] have the carry cleared (see after the inner loop) */ mp_digit mu; mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); /* a = a + mu * m * b**i * * This is computed in place and on the fly. The multiplication * by b**i is handled by offseting which columns the results * are added to. * * Note the comba method normally doesn't handle carries in the * inner loop In this case we fix the carry from the previous * column since the Montgomery reduction requires digits of the * result (so far) [see above] to work. This is * handled by fixing up one carry after the inner loop. The * carry fixups are done in order so after these loops the * first m->used words of W[] have the carries fixed */ { int iy; mp_digit *tmpn; mp_word *_W; /* alias for the digits of the modulus */ tmpn = n->dp; /* Alias for the columns set by an offset of ix */ _W = W + ix; /* inner loop */ for (iy = 0; iy < n->used; iy++) { *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); } } /* now fix carry for next digit, W[ix+1] */ W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); } /* now we have to propagate the carries and * shift the words downward [all those least * significant digits we zeroed]. */ { mp_digit *tmpx; mp_word *_W, *_W1; /* nox fix rest of carries */ /* alias for current word */ _W1 = W + ix; /* alias for next word, where the carry goes */ _W = W + ++ix; for (; ix <= ((n->used * 2) + 1); ix++) { *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); } /* copy out, A = A/b**n * * The result is A/b**n but instead of converting from an * array of mp_word to mp_digit than calling mp_rshd * we just copy them in the right order */ /* alias for destination word */ tmpx = x->dp; /* alias for shifted double precision result */ _W = W + n->used; for (ix = 0; ix < (n->used + 1); ix++) { *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); } /* zero oldused digits, if the input a was larger than * m->used+1 we'll have to clear the digits */ for (; ix < olduse; ix++) { *tmpx++ = 0; } } /* set the max used and clamp */ x->used = n->used + 1; mp_clamp (x); /* if A >= m then A = A - m */ if (mp_cmp_mag (x, n) != MP_LT) { return s_mp_sub (x, n, x); } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_fast_mp_montgomery_reduce.c */ /* Start: bn_fast_s_mp_mul_digs.c */ #include "libtorrent/tommath_private.h" #ifdef BN_FAST_S_MP_MUL_DIGS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Fast (comba) multiplier * * This is the fast column-array [comba] multiplier. It is * designed to compute the columns of the product first * then handle the carries afterwards. This has the effect * of making the nested loops that compute the columns very * simple and schedulable on super-scalar processors. * * This has been modified to produce a variable number of * digits of output so if say only a half-product is required * you don't have to compute the upper half (a feature * required for fast Barrett reduction). * * Based on Algorithm 14.12 on pp.595 of HAC. * */ int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) { int olduse, res, pa, ix, iz; mp_digit W[MP_WARRAY]; mp_word _W; /* grow the destination as required */ if (c->alloc < digs) { if ((res = mp_grow (c, digs)) != MP_OKAY) { return res; } } /* number of output digits to produce */ pa = MIN(digs, a->used + b->used); /* clear the carry */ _W = 0; for (ix = 0; ix < pa; ix++) { int tx, ty; int iy; mp_digit *tmpx, *tmpy; /* get offsets into the two bignums */ ty = MIN(b->used-1, ix); tx = ix - ty; /* setup temp aliases */ tmpx = a->dp + tx; tmpy = b->dp + ty; /* this is the number of times the loop will iterrate, essentially while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used-tx, ty+1); /* execute loop */ for (iz = 0; iz < iy; ++iz) { _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); } /* store term */ W[ix] = ((mp_digit)_W) & MP_MASK; /* make next carry */ _W = _W >> ((mp_word)DIGIT_BIT); } /* setup dest */ olduse = c->used; c->used = pa; { mp_digit *tmpc; tmpc = c->dp; for (ix = 0; ix < (pa + 1); ix++) { /* now extract the previous digit [below the carry] */ *tmpc++ = W[ix]; } /* clear unused digits [that existed in the old copy of c] */ for (; ix < olduse; ix++) { *tmpc++ = 0; } } mp_clamp (c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_fast_s_mp_mul_digs.c */ /* Start: bn_fast_s_mp_mul_high_digs.c */ #include "libtorrent/tommath_private.h" #ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* this is a modified version of fast_s_mul_digs that only produces * output digits *above* digs. See the comments for fast_s_mul_digs * to see how it works. * * This is used in the Barrett reduction since for one of the multiplications * only the higher digits were needed. This essentially halves the work. * * Based on Algorithm 14.12 on pp.595 of HAC. */ int fast_s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) { int olduse, res, pa, ix, iz; mp_digit W[MP_WARRAY]; mp_word _W; /* grow the destination as required */ pa = a->used + b->used; if (c->alloc < pa) { if ((res = mp_grow (c, pa)) != MP_OKAY) { return res; } } /* number of output digits to produce */ pa = a->used + b->used; _W = 0; for (ix = digs; ix < pa; ix++) { int tx, ty, iy; mp_digit *tmpx, *tmpy; /* get offsets into the two bignums */ ty = MIN(b->used-1, ix); tx = ix - ty; /* setup temp aliases */ tmpx = a->dp + tx; tmpy = b->dp + ty; /* this is the number of times the loop will iterrate, essentially its while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used-tx, ty+1); /* execute loop */ for (iz = 0; iz < iy; iz++) { _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); } /* store term */ W[ix] = ((mp_digit)_W) & MP_MASK; /* make next carry */ _W = _W >> ((mp_word)DIGIT_BIT); } /* setup dest */ olduse = c->used; c->used = pa; { mp_digit *tmpc; tmpc = c->dp + digs; for (ix = digs; ix < pa; ix++) { /* now extract the previous digit [below the carry] */ *tmpc++ = W[ix]; } /* clear unused digits [that existed in the old copy of c] */ for (; ix < olduse; ix++) { *tmpc++ = 0; } } mp_clamp (c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_fast_s_mp_mul_high_digs.c */ /* Start: bn_fast_s_mp_sqr.c */ #include "libtorrent/tommath_private.h" #ifdef BN_FAST_S_MP_SQR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* the jist of squaring... * you do like mult except the offset of the tmpx [one that * starts closer to zero] can't equal the offset of tmpy. * So basically you set up iy like before then you min it with * (ty-tx) so that it never happens. You double all those * you add in the inner loop After that loop you do the squares and add them in. */ int fast_s_mp_sqr (mp_int * a, mp_int * b) { int olduse, res, pa, ix, iz; mp_digit W[MP_WARRAY], *tmpx; mp_word W1; /* grow the destination as required */ pa = a->used + a->used; if (b->alloc < pa) { if ((res = mp_grow (b, pa)) != MP_OKAY) { return res; } } /* number of output digits to produce */ W1 = 0; for (ix = 0; ix < pa; ix++) { int tx, ty, iy; mp_word _W; mp_digit *tmpy; /* clear counter */ _W = 0; /* get offsets into the two bignums */ ty = MIN(a->used-1, ix); tx = ix - ty; /* setup temp aliases */ tmpx = a->dp + tx; tmpy = a->dp + ty; /* this is the number of times the loop will iterrate, essentially while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used-tx, ty+1); /* now for squaring tx can never equal ty * we halve the distance since they approach at a rate of 2x * and we have to round because odd cases need to be executed */ iy = MIN(iy, ((ty-tx)+1)>>1); /* execute loop */ for (iz = 0; iz < iy; iz++) { _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); } /* double the inner product and add carry */ _W = _W + _W + W1; /* even columns have the square term in them */ if ((ix&1) == 0) { _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); } /* store it */ W[ix] = (mp_digit)(_W & MP_MASK); /* make next carry */ W1 = _W >> ((mp_word)DIGIT_BIT); } /* setup dest */ olduse = b->used; b->used = a->used+a->used; { mp_digit *tmpb; tmpb = b->dp; for (ix = 0; ix < pa; ix++) { *tmpb++ = W[ix] & MP_MASK; } /* clear unused digits [that existed in the old copy of c] */ for (; ix < olduse; ix++) { *tmpb++ = 0; } } mp_clamp (b); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_fast_s_mp_sqr.c */ /* Start: bn_mp_2expt.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_2EXPT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes a = 2**b * * Simple algorithm which zeroes the int, grows it then just sets one bit * as required. */ int mp_2expt (mp_int * a, int b) { int res; /* zero a as per default */ mp_zero (a); /* grow a to accomodate the single bit */ if ((res = mp_grow (a, (b / DIGIT_BIT) + 1)) != MP_OKAY) { return res; } /* set the used count of where the bit will go */ a->used = (b / DIGIT_BIT) + 1; /* put the single bit in its place */ a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_2expt.c */ /* Start: bn_mp_abs.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_ABS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* b = |a| * * Simple function copies the input and fixes the sign to positive */ int mp_abs (mp_int * a, mp_int * b) { int res; /* copy a to b */ if (a != b) { if ((res = mp_copy (a, b)) != MP_OKAY) { return res; } } /* force the sign of b to positive */ b->sign = MP_ZPOS; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_abs.c */ /* Start: bn_mp_add.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_ADD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* high level addition (handles signs) */ int mp_add (mp_int * a, mp_int * b, mp_int * c) { int sa, sb, res; /* get sign of both inputs */ sa = a->sign; sb = b->sign; /* handle two cases, not four */ if (sa == sb) { /* both positive or both negative */ /* add their magnitudes, copy the sign */ c->sign = sa; res = s_mp_add (a, b, c); } else { /* one positive, the other negative */ /* subtract the one with the greater magnitude from */ /* the one of the lesser magnitude. The result gets */ /* the sign of the one with the greater magnitude. */ if (mp_cmp_mag (a, b) == MP_LT) { c->sign = sb; res = s_mp_sub (b, a, c); } else { c->sign = sa; res = s_mp_sub (a, b, c); } } return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_add.c */ /* Start: bn_mp_add_d.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_ADD_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* single digit addition */ int mp_add_d (mp_int * a, mp_digit b, mp_int * c) { int res, ix, oldused; mp_digit *tmpa, *tmpc, mu; /* grow c as required */ if (c->alloc < (a->used + 1)) { if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { return res; } } /* if a is negative and |a| >= b, call c = |a| - b */ if ((a->sign == MP_NEG) && ((a->used > 1) || (a->dp[0] >= b))) { /* temporarily fix sign of a */ a->sign = MP_ZPOS; /* c = |a| - b */ res = mp_sub_d(a, b, c); /* fix sign */ a->sign = c->sign = MP_NEG; /* clamp */ mp_clamp(c); return res; } /* old number of used digits in c */ oldused = c->used; /* sign always positive */ c->sign = MP_ZPOS; /* source alias */ tmpa = a->dp; /* destination alias */ tmpc = c->dp; /* if a is positive */ if (a->sign == MP_ZPOS) { /* add digit, after this we're propagating * the carry. */ *tmpc = *tmpa++ + b; mu = *tmpc >> DIGIT_BIT; *tmpc++ &= MP_MASK; /* now handle rest of the digits */ for (ix = 1; ix < a->used; ix++) { *tmpc = *tmpa++ + mu; mu = *tmpc >> DIGIT_BIT; *tmpc++ &= MP_MASK; } /* set final carry */ ix++; *tmpc++ = mu; /* setup size */ c->used = a->used + 1; } else { /* a was negative and |a| < b */ c->used = 1; /* the result is a single digit */ if (a->used == 1) { *tmpc++ = b - a->dp[0]; } else { *tmpc++ = b; } /* setup count so the clearing of oldused * can fall through correctly */ ix = 1; } /* now zero to oldused */ while (ix++ < oldused) { *tmpc++ = 0; } mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_add_d.c */ /* Start: bn_mp_addmod.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_ADDMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* d = a + b (mod c) */ int mp_addmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) { int res; mp_int t; if ((res = mp_init (&t)) != MP_OKAY) { return res; } if ((res = mp_add (a, b, &t)) != MP_OKAY) { mp_clear (&t); return res; } res = mp_mod (&t, c, d); mp_clear (&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_addmod.c */ /* Start: bn_mp_and.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_AND_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* AND two ints together */ int mp_and (mp_int * a, mp_int * b, mp_int * c) { int res, ix, px; mp_int t, *x; if (a->used > b->used) { if ((res = mp_init_copy (&t, a)) != MP_OKAY) { return res; } px = b->used; x = b; } else { if ((res = mp_init_copy (&t, b)) != MP_OKAY) { return res; } px = a->used; x = a; } for (ix = 0; ix < px; ix++) { t.dp[ix] &= x->dp[ix]; } /* zero digits above the last from the smallest mp_int */ for (; ix < t.used; ix++) { t.dp[ix] = 0; } mp_clamp (&t); mp_exch (c, &t); mp_clear (&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_and.c */ /* Start: bn_mp_clamp.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_CLAMP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* trim unused digits * * This is used to ensure that leading zero digits are * trimed and the leading "used" digit will be non-zero * Typically very fast. Also fixes the sign if there * are no more leading digits */ void mp_clamp (mp_int * a) { /* decrease used while the most significant digit is * zero. */ while ((a->used > 0) && (a->dp[a->used - 1] == 0)) { --(a->used); } /* reset the sign flag if used == 0 */ if (a->used == 0) { a->sign = MP_ZPOS; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_clamp.c */ /* Start: bn_mp_clear.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_CLEAR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* clear one (frees) */ void mp_clear (mp_int * a) { int i; /* only do anything if a hasn't been freed previously */ if (a->dp != NULL) { /* first zero the digits */ for (i = 0; i < a->used; i++) { a->dp[i] = 0; } /* free ram */ XFREE(a->dp); /* reset members to make debugging easier */ a->dp = NULL; a->alloc = a->used = 0; a->sign = MP_ZPOS; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_clear.c */ /* Start: bn_mp_clear_multi.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_CLEAR_MULTI_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ #include void mp_clear_multi(mp_int *mp, ...) { mp_int* next_mp = mp; va_list args; va_start(args, mp); while (next_mp != NULL) { mp_clear(next_mp); next_mp = va_arg(args, mp_int*); } va_end(args); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_clear_multi.c */ /* Start: bn_mp_cmp.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_CMP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* compare two ints (signed)*/ int mp_cmp (mp_int * a, mp_int * b) { /* compare based on sign */ if (a->sign != b->sign) { if (a->sign == MP_NEG) { return MP_LT; } else { return MP_GT; } } /* compare digits */ if (a->sign == MP_NEG) { /* if negative compare opposite direction */ return mp_cmp_mag(b, a); } else { return mp_cmp_mag(a, b); } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_cmp.c */ /* Start: bn_mp_cmp_d.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_CMP_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* compare a digit */ int mp_cmp_d(mp_int * a, mp_digit b) { /* compare based on sign */ if (a->sign == MP_NEG) { return MP_LT; } /* compare based on magnitude */ if (a->used > 1) { return MP_GT; } /* compare the only digit of a to b */ if (a->dp[0] > b) { return MP_GT; } else if (a->dp[0] < b) { return MP_LT; } else { return MP_EQ; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_cmp_d.c */ /* Start: bn_mp_cmp_mag.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_CMP_MAG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* compare maginitude of two ints (unsigned) */ int mp_cmp_mag (mp_int * a, mp_int * b) { int n; mp_digit *tmpa, *tmpb; /* compare based on # of non-zero digits */ if (a->used > b->used) { return MP_GT; } if (a->used < b->used) { return MP_LT; } /* alias for a */ tmpa = a->dp + (a->used - 1); /* alias for b */ tmpb = b->dp + (a->used - 1); /* compare based on digits */ for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { if (*tmpa > *tmpb) { return MP_GT; } if (*tmpa < *tmpb) { return MP_LT; } } return MP_EQ; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_cmp_mag.c */ /* Start: bn_mp_cnt_lsb.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_CNT_LSB_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ static const int lnz[16] = { 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; /* Counts the number of lsbs which are zero before the first zero bit */ int mp_cnt_lsb(mp_int *a) { int x; mp_digit q, qq; /* easy out */ if (mp_iszero(a) == MP_YES) { return 0; } /* scan lower digits until non-zero */ for (x = 0; (x < a->used) && (a->dp[x] == 0); x++) {} q = a->dp[x]; x *= DIGIT_BIT; /* now scan this digit until a 1 is found */ if ((q & 1) == 0) { do { qq = q & 15; x += lnz[qq]; q >>= 4; } while (qq == 0); } return x; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_cnt_lsb.c */ /* Start: bn_mp_copy.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_COPY_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* copy, b = a */ int mp_copy (mp_int * a, mp_int * b) { int res, n; /* if dst == src do nothing */ if (a == b) { return MP_OKAY; } /* grow dest */ if (b->alloc < a->used) { if ((res = mp_grow (b, a->used)) != MP_OKAY) { return res; } } /* zero b and copy the parameters over */ { mp_digit *tmpa, *tmpb; /* pointer aliases */ /* source */ tmpa = a->dp; /* destination */ tmpb = b->dp; /* copy all the digits */ for (n = 0; n < a->used; n++) { *tmpb++ = *tmpa++; } /* clear high digits */ for (; n < b->used; n++) { *tmpb++ = 0; } } /* copy used count and sign */ b->used = a->used; b->sign = a->sign; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_copy.c */ /* Start: bn_mp_count_bits.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_COUNT_BITS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* returns the number of bits in an int */ int mp_count_bits (mp_int * a) { int r; mp_digit q; /* shortcut */ if (a->used == 0) { return 0; } /* get number of digits and add that */ r = (a->used - 1) * DIGIT_BIT; /* take the last digit and count the bits in it */ q = a->dp[a->used - 1]; while (q > ((mp_digit) 0)) { ++r; q >>= ((mp_digit) 1); } return r; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_count_bits.c */ /* Start: bn_mp_div.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_DIV_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ #ifdef BN_MP_DIV_SMALL /* slower bit-bang division... also smaller */ int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) { mp_int ta, tb, tq, q; int res, n, n2; /* is divisor zero ? */ if (mp_iszero (b) == MP_YES) { return MP_VAL; } /* if a < b then q=0, r = a */ if (mp_cmp_mag (a, b) == MP_LT) { if (d != NULL) { res = mp_copy (a, d); } else { res = MP_OKAY; } if (c != NULL) { mp_zero (c); } return res; } /* init our temps */ if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { return res; } mp_set(&tq, 1); n = mp_count_bits(a) - mp_count_bits(b); if (((res = mp_abs(a, &ta)) != MP_OKAY) || ((res = mp_abs(b, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { goto LBL_ERR; } while (n-- >= 0) { if (mp_cmp(&tb, &ta) != MP_GT) { if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { goto LBL_ERR; } } if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { goto LBL_ERR; } } /* now q == quotient and ta == remainder */ n = a->sign; n2 = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; if (c != NULL) { mp_exch(c, &q); c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; } if (d != NULL) { mp_exch(d, &ta); d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; } LBL_ERR: mp_clear_multi(&ta, &tb, &tq, &q, NULL); return res; } #else /* integer signed division. * c*b + d == a [e.g. a/b, c=quotient, d=remainder] * HAC pp.598 Algorithm 14.20 * * Note that the description in HAC is horribly * incomplete. For example, it doesn't consider * the case where digits are removed from 'x' in * the inner loop. It also doesn't consider the * case that y has fewer than three digits, etc.. * * The overall algorithm is as described as * 14.20 from HAC but fixed to treat these cases. */ int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) { mp_int q, x, y, t1, t2; int res, n, t, i, norm, neg; /* is divisor zero ? */ if (mp_iszero (b) == MP_YES) { return MP_VAL; } /* if a < b then q=0, r = a */ if (mp_cmp_mag (a, b) == MP_LT) { if (d != NULL) { res = mp_copy (a, d); } else { res = MP_OKAY; } if (c != NULL) { mp_zero (c); } return res; } if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { return res; } q.used = a->used + 2; if ((res = mp_init (&t1)) != MP_OKAY) { goto LBL_Q; } if ((res = mp_init (&t2)) != MP_OKAY) { goto LBL_T1; } if ((res = mp_init_copy (&x, a)) != MP_OKAY) { goto LBL_T2; } if ((res = mp_init_copy (&y, b)) != MP_OKAY) { goto LBL_X; } /* fix the sign */ neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; x.sign = y.sign = MP_ZPOS; /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ norm = mp_count_bits(&y) % DIGIT_BIT; if (norm < (int)(DIGIT_BIT-1)) { norm = (DIGIT_BIT-1) - norm; if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { goto LBL_Y; } } else { norm = 0; } /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ n = x.used - 1; t = y.used - 1; /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ goto LBL_Y; } while (mp_cmp (&x, &y) != MP_LT) { ++(q.dp[n - t]); if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { goto LBL_Y; } } /* reset y by shifting it back down */ mp_rshd (&y, n - t); /* step 3. for i from n down to (t + 1) */ for (i = n; i >= (t + 1); i--) { if (i > x.used) { continue; } /* step 3.1 if xi == yt then set q{i-t-1} to b-1, * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ if (x.dp[i] == y.dp[t]) { q.dp[(i - t) - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); } else { mp_word tmp; tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); tmp |= ((mp_word) x.dp[i - 1]); tmp /= ((mp_word) y.dp[t]); if (tmp > (mp_word) MP_MASK) { tmp = MP_MASK; } q.dp[(i - t) - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); } /* while (q{i-t-1} * (yt * b + y{t-1})) > xi * b**2 + xi-1 * b + xi-2 do q{i-t-1} -= 1; */ q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] + 1) & MP_MASK; do { q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1) & MP_MASK; /* find left hand */ mp_zero (&t1); t1.dp[0] = ((t - 1) < 0) ? 0 : y.dp[t - 1]; t1.dp[1] = y.dp[t]; t1.used = 2; if ((res = mp_mul_d (&t1, q.dp[(i - t) - 1], &t1)) != MP_OKAY) { goto LBL_Y; } /* find right hand */ t2.dp[0] = ((i - 2) < 0) ? 0 : x.dp[i - 2]; t2.dp[1] = ((i - 1) < 0) ? 0 : x.dp[i - 1]; t2.dp[2] = x.dp[i]; t2.used = 3; } while (mp_cmp_mag(&t1, &t2) == MP_GT); /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ if ((res = mp_mul_d (&y, q.dp[(i - t) - 1], &t1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_lshd (&t1, (i - t) - 1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { goto LBL_Y; } /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ if (x.sign == MP_NEG) { if ((res = mp_copy (&y, &t1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_lshd (&t1, (i - t) - 1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { goto LBL_Y; } q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1UL) & MP_MASK; } } /* now q is the quotient and x is the remainder * [which we have to normalize] */ /* get sign before writing to c */ x.sign = (x.used == 0) ? MP_ZPOS : a->sign; if (c != NULL) { mp_clamp (&q); mp_exch (&q, c); c->sign = neg; } if (d != NULL) { if ((res = mp_div_2d (&x, norm, &x, NULL)) != MP_OKAY) { goto LBL_Y; } mp_exch (&x, d); } res = MP_OKAY; LBL_Y:mp_clear (&y); LBL_X:mp_clear (&x); LBL_T2:mp_clear (&t2); LBL_T1:mp_clear (&t1); LBL_Q:mp_clear (&q); return res; } #endif #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_div.c */ /* Start: bn_mp_div_2.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_DIV_2_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* b = a/2 */ int mp_div_2(mp_int * a, mp_int * b) { int x, res, oldused; /* copy */ if (b->alloc < a->used) { if ((res = mp_grow (b, a->used)) != MP_OKAY) { return res; } } oldused = b->used; b->used = a->used; { mp_digit r, rr, *tmpa, *tmpb; /* source alias */ tmpa = a->dp + b->used - 1; /* dest alias */ tmpb = b->dp + b->used - 1; /* carry */ r = 0; for (x = b->used - 1; x >= 0; x--) { /* get the carry for the next iteration */ rr = *tmpa & 1; /* shift the current digit, add in carry and store */ *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); /* forward carry to next iteration */ r = rr; } /* zero excess digits */ tmpb = b->dp + b->used; for (x = b->used; x < oldused; x++) { *tmpb++ = 0; } } b->sign = a->sign; mp_clamp (b); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_div_2.c */ /* Start: bn_mp_div_2d.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_DIV_2D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* shift right by a certain bit count (store quotient in c, optional remainder in d) */ int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) { mp_digit D, r, rr; int x, res; mp_int t; /* if the shift count is <= 0 then we do no work */ if (b <= 0) { res = mp_copy (a, c); if (d != NULL) { mp_zero (d); } return res; } if ((res = mp_init (&t)) != MP_OKAY) { return res; } /* get the remainder */ if (d != NULL) { if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { mp_clear (&t); return res; } } /* copy */ if ((res = mp_copy (a, c)) != MP_OKAY) { mp_clear (&t); return res; } /* shift by as many digits in the bit count */ if (b >= (int)DIGIT_BIT) { mp_rshd (c, b / DIGIT_BIT); } /* shift any bit count < DIGIT_BIT */ D = (mp_digit) (b % DIGIT_BIT); if (D != 0) { mp_digit *tmpc, mask, shift; /* mask */ mask = (((mp_digit)1) << D) - 1; /* shift for lsb */ shift = DIGIT_BIT - D; /* alias */ tmpc = c->dp + (c->used - 1); /* carry */ r = 0; for (x = c->used - 1; x >= 0; x--) { /* get the lower bits of this word in a temp */ rr = *tmpc & mask; /* shift the current word and mix in the carry bits from the previous word */ *tmpc = (*tmpc >> D) | (r << shift); --tmpc; /* set the carry to the carry bits of the current word found above */ r = rr; } } mp_clamp (c); if (d != NULL) { mp_exch (&t, d); } mp_clear (&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_div_2d.c */ /* Start: bn_mp_div_3.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_DIV_3_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* divide by three (based on routine from MPI and the GMP manual) */ int mp_div_3 (mp_int * a, mp_int *c, mp_digit * d) { mp_int q; mp_word w, t; mp_digit b; int res, ix; /* b = 2**DIGIT_BIT / 3 */ b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3); if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { return res; } q.used = a->used; q.sign = a->sign; w = 0; for (ix = a->used - 1; ix >= 0; ix--) { w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); if (w >= 3) { /* multiply w by [1/3] */ t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT); /* now subtract 3 * [w/3] from w, to get the remainder */ w -= t+t+t; /* fixup the remainder as required since * the optimization is not exact. */ while (w >= 3) { t += 1; w -= 3; } } else { t = 0; } q.dp[ix] = (mp_digit)t; } /* [optional] store the remainder */ if (d != NULL) { *d = (mp_digit)w; } /* [optional] store the quotient */ if (c != NULL) { mp_clamp(&q); mp_exch(&q, c); } mp_clear(&q); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_div_3.c */ /* Start: bn_mp_div_d.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_DIV_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ static int s_is_power_of_two(mp_digit b, int *p) { int x; /* fast return if no power of two */ if ((b == 0) || ((b & (b-1)) != 0)) { return 0; } for (x = 0; x < DIGIT_BIT; x++) { if (b == (((mp_digit)1)<dp[0] & ((((mp_digit)1)<used)) != MP_OKAY) { return res; } q.used = a->used; q.sign = a->sign; w = 0; for (ix = a->used - 1; ix >= 0; ix--) { w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); if (w >= b) { t = (mp_digit)(w / b); w -= ((mp_word)t) * ((mp_word)b); } else { t = 0; } q.dp[ix] = (mp_digit)t; } if (d != NULL) { *d = (mp_digit)w; } if (c != NULL) { mp_clamp(&q); mp_exch(&q, c); } mp_clear(&q); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_div_d.c */ /* Start: bn_mp_dr_is_modulus.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_DR_IS_MODULUS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines if a number is a valid DR modulus */ int mp_dr_is_modulus(mp_int *a) { int ix; /* must be at least two digits */ if (a->used < 2) { return 0; } /* must be of the form b**k - a [a <= b] so all * but the first digit must be equal to -1 (mod b). */ for (ix = 1; ix < a->used; ix++) { if (a->dp[ix] != MP_MASK) { return 0; } } return 1; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_dr_is_modulus.c */ /* Start: bn_mp_dr_reduce.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_DR_REDUCE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reduce "x" in place modulo "n" using the Diminished Radix algorithm. * * Based on algorithm from the paper * * "Generating Efficient Primes for Discrete Log Cryptosystems" * Chae Hoon Lim, Pil Joong Lee, * POSTECH Information Research Laboratories * * The modulus must be of a special format [see manual] * * Has been modified to use algorithm 7.10 from the LTM book instead * * Input x must be in the range 0 <= x <= (n-1)**2 */ int mp_dr_reduce (mp_int * x, mp_int * n, mp_digit k) { int err, i, m; mp_word r; mp_digit mu, *tmpx1, *tmpx2; /* m = digits in modulus */ m = n->used; /* ensure that "x" has at least 2m digits */ if (x->alloc < (m + m)) { if ((err = mp_grow (x, m + m)) != MP_OKAY) { return err; } } /* top of loop, this is where the code resumes if * another reduction pass is required. */ top: /* aliases for digits */ /* alias for lower half of x */ tmpx1 = x->dp; /* alias for upper half of x, or x/B**m */ tmpx2 = x->dp + m; /* set carry to zero */ mu = 0; /* compute (x mod B**m) + k * [x/B**m] inline and inplace */ for (i = 0; i < m; i++) { r = (((mp_word)*tmpx2++) * (mp_word)k) + *tmpx1 + mu; *tmpx1++ = (mp_digit)(r & MP_MASK); mu = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); } /* set final carry */ *tmpx1++ = mu; /* zero words above m */ for (i = m + 1; i < x->used; i++) { *tmpx1++ = 0; } /* clamp, sub and return */ mp_clamp (x); /* if x >= n then subtract and reduce again * Each successive "recursion" makes the input smaller and smaller. */ if (mp_cmp_mag (x, n) != MP_LT) { if ((err = s_mp_sub(x, n, x)) != MP_OKAY) { return err; } goto top; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_dr_reduce.c */ /* Start: bn_mp_dr_setup.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_DR_SETUP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines the setup value */ void mp_dr_setup(mp_int *a, mp_digit *d) { /* the casts are required if DIGIT_BIT is one less than * the number of bits in a mp_digit [e.g. DIGIT_BIT==31] */ *d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) - ((mp_word)a->dp[0])); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_dr_setup.c */ /* Start: bn_mp_exch.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_EXCH_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* swap the elements of two integers, for cases where you can't simply swap the * mp_int pointers around */ void mp_exch (mp_int * a, mp_int * b) { mp_int t; t = *a; *a = *b; *b = t; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_exch.c */ /* Start: bn_mp_export.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_EXPORT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* based on gmp's mpz_export. * see http://gmplib.org/manual/Integer-Import-and-Export.html */ int mp_export(void* rop, size_t* countp, int order, size_t size, int endian, size_t nails, mp_int* op) { int result; size_t odd_nails, nail_bytes, i, j, bits, count; unsigned char odd_nail_mask; mp_int t; if ((result = mp_init_copy(&t, op)) != MP_OKAY) { return result; } if (endian == 0) { union { unsigned int i; char c[4]; } lint; lint.i = 0x01020304; endian = (lint.c[0] == 4) ? -1 : 1; } odd_nails = (nails % 8); odd_nail_mask = 0xff; for (i = 0; i < odd_nails; ++i) { odd_nail_mask ^= (1 << (7 - i)); } nail_bytes = nails / 8; bits = mp_count_bits(&t); count = (bits / ((size * 8) - nails)) + (((bits % ((size * 8) - nails)) != 0) ? 1 : 0); for (i = 0; i < count; ++i) { for (j = 0; j < size; ++j) { unsigned char* byte = ( (unsigned char*)rop + (((order == -1) ? i : ((count - 1) - i)) * size) + ((endian == -1) ? j : ((size - 1) - j)) ); if (j >= (size - nail_bytes)) { *byte = 0; continue; } *byte = (unsigned char)((j == ((size - nail_bytes) - 1)) ? (t.dp[0] & odd_nail_mask) : (t.dp[0] & 0xFF)); if ((result = mp_div_2d(&t, ((j == ((size - nail_bytes) - 1)) ? (8 - odd_nails) : 8), &t, NULL)) != MP_OKAY) { mp_clear(&t); return result; } } } mp_clear(&t); if (countp != NULL) { *countp = count; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_export.c */ /* Start: bn_mp_expt_d.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_EXPT_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* wrapper function for mp_expt_d_ex() */ int mp_expt_d (mp_int * a, mp_digit b, mp_int * c) { return mp_expt_d_ex(a, b, c, 0); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_expt_d.c */ /* Start: bn_mp_expt_d_ex.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_EXPT_D_EX_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* calculate c = a**b using a square-multiply algorithm */ int mp_expt_d_ex (mp_int * a, mp_digit b, mp_int * c, int fast) { int res; unsigned int x; mp_int g; if ((res = mp_init_copy (&g, a)) != MP_OKAY) { return res; } /* set initial result */ mp_set (c, 1); if (fast != 0) { while (b > 0) { /* if the bit is set multiply */ if ((b & 1) != 0) { if ((res = mp_mul (c, &g, c)) != MP_OKAY) { mp_clear (&g); return res; } } /* square */ if (b > 1) { if ((res = mp_sqr (&g, &g)) != MP_OKAY) { mp_clear (&g); return res; } } /* shift to next bit */ b >>= 1; } } else { for (x = 0; x < DIGIT_BIT; x++) { /* square */ if ((res = mp_sqr (c, c)) != MP_OKAY) { mp_clear (&g); return res; } /* if the bit is set multiply */ if ((b & (mp_digit) (((mp_digit)1) << (DIGIT_BIT - 1))) != 0) { if ((res = mp_mul (c, &g, c)) != MP_OKAY) { mp_clear (&g); return res; } } /* shift to next bit */ b <<= 1; } } /* if ... else */ mp_clear (&g); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_expt_d_ex.c */ /* Start: bn_mp_exptmod.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_EXPTMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* this is a shell function that calls either the normal or Montgomery * exptmod functions. Originally the call to the montgomery code was * embedded in the normal function but that wasted alot of stack space * for nothing (since 99% of the time the Montgomery code would be called) */ int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) { int dr; /* modulus P must be positive */ if (P->sign == MP_NEG) { return MP_VAL; } /* if exponent X is negative we have to recurse */ if (X->sign == MP_NEG) { #ifdef BN_MP_INVMOD_C mp_int tmpG, tmpX; int err; /* first compute 1/G mod P */ if ((err = mp_init(&tmpG)) != MP_OKAY) { return err; } if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { mp_clear(&tmpG); return err; } /* now get |X| */ if ((err = mp_init(&tmpX)) != MP_OKAY) { mp_clear(&tmpG); return err; } if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { mp_clear_multi(&tmpG, &tmpX, NULL); return err; } /* and now compute (1/G)**|X| instead of G**X [X < 0] */ err = mp_exptmod(&tmpG, &tmpX, P, Y); mp_clear_multi(&tmpG, &tmpX, NULL); return err; #else /* no invmod */ return MP_VAL; #endif } /* modified diminished radix reduction */ #if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) if (mp_reduce_is_2k_l(P) == MP_YES) { return s_mp_exptmod(G, X, P, Y, 1); } #endif #ifdef BN_MP_DR_IS_MODULUS_C /* is it a DR modulus? */ dr = mp_dr_is_modulus(P); #else /* default to no */ dr = 0; #endif #ifdef BN_MP_REDUCE_IS_2K_C /* if not, is it a unrestricted DR modulus? */ if (dr == 0) { dr = mp_reduce_is_2k(P) << 1; } #endif /* if the modulus is odd or dr != 0 use the montgomery method */ #ifdef BN_MP_EXPTMOD_FAST_C if ((mp_isodd (P) == MP_YES) || (dr != 0)) { return mp_exptmod_fast (G, X, P, Y, dr); } else { #endif #ifdef BN_S_MP_EXPTMOD_C /* otherwise use the generic Barrett reduction technique */ return s_mp_exptmod (G, X, P, Y, 0); #else /* no exptmod for evens */ return MP_VAL; #endif #ifdef BN_MP_EXPTMOD_FAST_C } #endif } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_exptmod.c */ /* Start: bn_mp_exptmod_fast.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_EXPTMOD_FAST_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 * * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. * The value of k changes based on the size of the exponent. * * Uses Montgomery or Diminished Radix reduction [whichever appropriate] */ #ifdef MP_LOW_MEM #define TAB_SIZE 32 #else #define TAB_SIZE 256 #endif int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) { mp_int M[TAB_SIZE], res; mp_digit buf, mp; int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; /* use a pointer to the reduction algorithm. This allows us to use * one of many reduction algorithms without modding the guts of * the code with if statements everywhere. */ int (*redux)(mp_int*,mp_int*,mp_digit); /* find window size */ x = mp_count_bits (X); if (x <= 7) { winsize = 2; } else if (x <= 36) { winsize = 3; } else if (x <= 140) { winsize = 4; } else if (x <= 450) { winsize = 5; } else if (x <= 1303) { winsize = 6; } else if (x <= 3529) { winsize = 7; } else { winsize = 8; } #ifdef MP_LOW_MEM if (winsize > 5) { winsize = 5; } #endif /* init M array */ /* init first cell */ if ((err = mp_init(&M[1])) != MP_OKAY) { return err; } /* now init the second half of the array */ for (x = 1<<(winsize-1); x < (1 << winsize); x++) { if ((err = mp_init(&M[x])) != MP_OKAY) { for (y = 1<<(winsize-1); y < x; y++) { mp_clear (&M[y]); } mp_clear(&M[1]); return err; } } /* determine and setup reduction code */ if (redmode == 0) { #ifdef BN_MP_MONTGOMERY_SETUP_C /* now setup montgomery */ if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { goto LBL_M; } #else err = MP_VAL; goto LBL_M; #endif /* automatically pick the comba one if available (saves quite a few calls/ifs) */ #ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C if ((((P->used * 2) + 1) < MP_WARRAY) && (P->used < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { redux = fast_mp_montgomery_reduce; } else #endif { #ifdef BN_MP_MONTGOMERY_REDUCE_C /* use slower baseline Montgomery method */ redux = mp_montgomery_reduce; #else err = MP_VAL; goto LBL_M; #endif } } else if (redmode == 1) { #if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) /* setup DR reduction for moduli of the form B**k - b */ mp_dr_setup(P, &mp); redux = mp_dr_reduce; #else err = MP_VAL; goto LBL_M; #endif } else { #if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) /* setup DR reduction for moduli of the form 2**k - b */ if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { goto LBL_M; } redux = mp_reduce_2k; #else err = MP_VAL; goto LBL_M; #endif } /* setup result */ if ((err = mp_init (&res)) != MP_OKAY) { goto LBL_M; } /* create M table * * * The first half of the table is not computed though accept for M[0] and M[1] */ if (redmode == 0) { #ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C /* now we need R mod m */ if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { goto LBL_RES; } #else err = MP_VAL; goto LBL_RES; #endif /* now set M[1] to G * R mod m */ if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { goto LBL_RES; } } else { mp_set(&res, 1); if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { goto LBL_RES; } } /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_RES; } for (x = 0; x < (winsize - 1); x++) { if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { goto LBL_RES; } } /* create upper table */ for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&M[x], P, mp)) != MP_OKAY) { goto LBL_RES; } } /* set initial mode and bit cnt */ mode = 0; bitcnt = 1; buf = 0; digidx = X->used - 1; bitcpy = 0; bitbuf = 0; for (;;) { /* grab next digit as required */ if (--bitcnt == 0) { /* if digidx == -1 we are out of digits so break */ if (digidx == -1) { break; } /* read next digit and reset bitcnt */ buf = X->dp[digidx--]; bitcnt = (int)DIGIT_BIT; } /* grab the next msb from the exponent */ y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; buf <<= (mp_digit)1; /* if the bit is zero and mode == 0 then we ignore it * These represent the leading zero bits before the first 1 bit * in the exponent. Technically this opt is not required but it * does lower the # of trivial squaring/reductions used */ if ((mode == 0) && (y == 0)) { continue; } /* if the bit is zero and mode == 1 then we square */ if ((mode == 1) && (y == 0)) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, mp)) != MP_OKAY) { goto LBL_RES; } continue; } /* else we add it to the window */ bitbuf |= (y << (winsize - ++bitcpy)); mode = 2; if (bitcpy == winsize) { /* ok window is filled so square as required and multiply */ /* square first */ for (x = 0; x < winsize; x++) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, mp)) != MP_OKAY) { goto LBL_RES; } } /* then multiply */ if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, mp)) != MP_OKAY) { goto LBL_RES; } /* empty window and reset */ bitcpy = 0; bitbuf = 0; mode = 1; } } /* if bits remain then square/multiply */ if ((mode == 2) && (bitcpy > 0)) { /* square then multiply if the bit is set */ for (x = 0; x < bitcpy; x++) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, mp)) != MP_OKAY) { goto LBL_RES; } /* get next bit of the window */ bitbuf <<= 1; if ((bitbuf & (1 << winsize)) != 0) { /* then multiply */ if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, mp)) != MP_OKAY) { goto LBL_RES; } } } } if (redmode == 0) { /* fixup result if Montgomery reduction is used * recall that any value in a Montgomery system is * actually multiplied by R mod n. So we have * to reduce one more time to cancel out the factor * of R. */ if ((err = redux(&res, P, mp)) != MP_OKAY) { goto LBL_RES; } } /* swap res with Y */ mp_exch (&res, Y); err = MP_OKAY; LBL_RES:mp_clear (&res); LBL_M: mp_clear(&M[1]); for (x = 1<<(winsize-1); x < (1 << winsize); x++) { mp_clear (&M[x]); } return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_exptmod_fast.c */ /* Start: bn_mp_exteuclid.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_EXTEUCLID_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Extended euclidean algorithm of (a, b) produces a*u1 + b*u2 = u3 */ int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) { mp_int u1,u2,u3,v1,v2,v3,t1,t2,t3,q,tmp; int err; if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) { return err; } /* initialize, (u1,u2,u3) = (1,0,a) */ mp_set(&u1, 1); if ((err = mp_copy(a, &u3)) != MP_OKAY) { goto _ERR; } /* initialize, (v1,v2,v3) = (0,1,b) */ mp_set(&v2, 1); if ((err = mp_copy(b, &v3)) != MP_OKAY) { goto _ERR; } /* loop while v3 != 0 */ while (mp_iszero(&v3) == MP_NO) { /* q = u3/v3 */ if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) { goto _ERR; } /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */ if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) { goto _ERR; } if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) { goto _ERR; } if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) { goto _ERR; } if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) { goto _ERR; } if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) { goto _ERR; } if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) { goto _ERR; } /* (u1,u2,u3) = (v1,v2,v3) */ if ((err = mp_copy(&v1, &u1)) != MP_OKAY) { goto _ERR; } if ((err = mp_copy(&v2, &u2)) != MP_OKAY) { goto _ERR; } if ((err = mp_copy(&v3, &u3)) != MP_OKAY) { goto _ERR; } /* (v1,v2,v3) = (t1,t2,t3) */ if ((err = mp_copy(&t1, &v1)) != MP_OKAY) { goto _ERR; } if ((err = mp_copy(&t2, &v2)) != MP_OKAY) { goto _ERR; } if ((err = mp_copy(&t3, &v3)) != MP_OKAY) { goto _ERR; } } /* make sure U3 >= 0 */ if (u3.sign == MP_NEG) { if ((err = mp_neg(&u1, &u1)) != MP_OKAY) { goto _ERR; } if ((err = mp_neg(&u2, &u2)) != MP_OKAY) { goto _ERR; } if ((err = mp_neg(&u3, &u3)) != MP_OKAY) { goto _ERR; } } /* copy result out */ if (U1 != NULL) { mp_exch(U1, &u1); } if (U2 != NULL) { mp_exch(U2, &u2); } if (U3 != NULL) { mp_exch(U3, &u3); } err = MP_OKAY; _ERR: mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_exteuclid.c */ /* Start: bn_mp_fread.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_FREAD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* read a bigint from a file stream in ASCII */ int mp_fread(mp_int *a, int radix, FILE *stream) { int err, ch, neg, y; /* clear a */ mp_zero(a); /* if first digit is - then set negative */ ch = fgetc(stream); if (ch == '-') { neg = MP_NEG; ch = fgetc(stream); } else { neg = MP_ZPOS; } for (;;) { /* find y in the radix map */ for (y = 0; y < radix; y++) { if (mp_s_rmap[y] == ch) { break; } } if (y == radix) { break; } /* shift up and add */ if ((err = mp_mul_d(a, radix, a)) != MP_OKAY) { return err; } if ((err = mp_add_d(a, y, a)) != MP_OKAY) { return err; } ch = fgetc(stream); } if (mp_cmp_d(a, 0) != MP_EQ) { a->sign = neg; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_fread.c */ /* Start: bn_mp_fwrite.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_FWRITE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ int mp_fwrite(mp_int *a, int radix, FILE *stream) { char *buf; int err, len, x; if ((err = mp_radix_size(a, radix, &len)) != MP_OKAY) { return err; } buf = OPT_CAST(char) XMALLOC (len); if (buf == NULL) { return MP_MEM; } if ((err = mp_toradix(a, buf, radix)) != MP_OKAY) { XFREE (buf); return err; } for (x = 0; x < len; x++) { if (fputc(buf[x], stream) == EOF) { XFREE (buf); return MP_VAL; } } XFREE (buf); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_fwrite.c */ /* Start: bn_mp_gcd.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_GCD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Greatest Common Divisor using the binary method */ int mp_gcd (mp_int * a, mp_int * b, mp_int * c) { mp_int u, v; int k, u_lsb, v_lsb, res; /* either zero than gcd is the largest */ if (mp_iszero (a) == MP_YES) { return mp_abs (b, c); } if (mp_iszero (b) == MP_YES) { return mp_abs (a, c); } /* get copies of a and b we can modify */ if ((res = mp_init_copy (&u, a)) != MP_OKAY) { return res; } if ((res = mp_init_copy (&v, b)) != MP_OKAY) { goto LBL_U; } /* must be positive for the remainder of the algorithm */ u.sign = v.sign = MP_ZPOS; /* B1. Find the common power of two for u and v */ u_lsb = mp_cnt_lsb(&u); v_lsb = mp_cnt_lsb(&v); k = MIN(u_lsb, v_lsb); if (k > 0) { /* divide the power of two out */ if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) { goto LBL_V; } if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) { goto LBL_V; } } /* divide any remaining factors of two out */ if (u_lsb != k) { if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) { goto LBL_V; } } if (v_lsb != k) { if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) { goto LBL_V; } } while (mp_iszero(&v) == MP_NO) { /* make sure v is the largest */ if (mp_cmp_mag(&u, &v) == MP_GT) { /* swap u and v to make sure v is >= u */ mp_exch(&u, &v); } /* subtract smallest from largest */ if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) { goto LBL_V; } /* Divide out all factors of two */ if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) { goto LBL_V; } } /* multiply by 2**k which we divided out at the beginning */ if ((res = mp_mul_2d (&u, k, c)) != MP_OKAY) { goto LBL_V; } c->sign = MP_ZPOS; res = MP_OKAY; LBL_V:mp_clear (&u); LBL_U:mp_clear (&v); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_gcd.c */ /* Start: bn_mp_get_int.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_GET_INT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* get the lower 32-bits of an mp_int */ unsigned long mp_get_int(mp_int * a) { int i; mp_min_u32 res; if (a->used == 0) { return 0; } /* get number of digits of the lsb we have to read */ i = MIN(a->used,(int)(((sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; /* get most significant digit of result */ res = DIGIT(a,i); while (--i >= 0) { res = (res << DIGIT_BIT) | DIGIT(a,i); } /* force result to 32-bits always so it is consistent on non 32-bit platforms */ return res & 0xFFFFFFFFUL; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_get_int.c */ /* Start: bn_mp_get_long.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_GET_LONG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* get the lower unsigned long of an mp_int, platform dependent */ unsigned long mp_get_long(mp_int * a) { int i; unsigned long res; if (a->used == 0) { return 0; } /* get number of digits of the lsb we have to read */ i = MIN(a->used,(int)(((sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; /* get most significant digit of result */ res = DIGIT(a,i); #if (ULONG_MAX != 0xffffffffuL) || (DIGIT_BIT < 32) while (--i >= 0) { res = (res << DIGIT_BIT) | DIGIT(a,i); } #endif return res; } #endif /* End: bn_mp_get_long.c */ /* Start: bn_mp_get_long_long.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_GET_LONG_LONG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* get the lower unsigned long long of an mp_int, platform dependent */ unsigned long long mp_get_long_long (mp_int * a) { int i; unsigned long long res; if (a->used == 0) { return 0; } /* get number of digits of the lsb we have to read */ i = MIN(a->used,(int)(((sizeof(unsigned long long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; /* get most significant digit of result */ res = DIGIT(a,i); #if DIGIT_BIT < 64 while (--i >= 0) { res = (res << DIGIT_BIT) | DIGIT(a,i); } #endif return res; } #endif /* End: bn_mp_get_long_long.c */ /* Start: bn_mp_grow.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_GROW_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* grow as required */ int mp_grow (mp_int * a, int size) { int i; mp_digit *tmp; /* if the alloc size is smaller alloc more ram */ if (a->alloc < size) { /* ensure there are always at least MP_PREC digits extra on top */ size += (MP_PREC * 2) - (size % MP_PREC); /* reallocate the array a->dp * * We store the return in a temporary variable * in case the operation failed we don't want * to overwrite the dp member of a. */ tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); if (tmp == NULL) { /* reallocation failed but "a" is still valid [can be freed] */ return MP_MEM; } /* reallocation succeeded so set a->dp */ a->dp = tmp; /* zero excess digits */ i = a->alloc; a->alloc = size; for (; i < a->alloc; i++) { a->dp[i] = 0; } } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_grow.c */ /* Start: bn_mp_import.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_IMPORT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* based on gmp's mpz_import. * see http://gmplib.org/manual/Integer-Import-and-Export.html */ int mp_import(mp_int* rop, size_t count, int order, size_t size, int endian, size_t nails, const void* op) { int result; size_t odd_nails, nail_bytes, i, j; unsigned char odd_nail_mask; mp_zero(rop); if (endian == 0) { union { unsigned int i; char c[4]; } lint; lint.i = 0x01020304; endian = (lint.c[0] == 4) ? -1 : 1; } odd_nails = (nails % 8); odd_nail_mask = 0xff; for (i = 0; i < odd_nails; ++i) { odd_nail_mask ^= (1 << (7 - i)); } nail_bytes = nails / 8; for (i = 0; i < count; ++i) { for (j = 0; j < (size - nail_bytes); ++j) { unsigned char byte = *( (unsigned char*)op + (((order == 1) ? i : ((count - 1) - i)) * size) + ((endian == 1) ? (j + nail_bytes) : (((size - 1) - j) - nail_bytes)) ); if ( (result = mp_mul_2d(rop, ((j == 0) ? (8 - odd_nails) : 8), rop)) != MP_OKAY) { return result; } rop->dp[0] |= (j == 0) ? (byte & odd_nail_mask) : byte; rop->used += 1; } } mp_clamp(rop); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_import.c */ /* Start: bn_mp_init.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_INIT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* init a new mp_int */ int mp_init (mp_int * a) { int i; /* allocate memory required and clear it */ a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); if (a->dp == NULL) { return MP_MEM; } /* set the digits to zero */ for (i = 0; i < MP_PREC; i++) { a->dp[i] = 0; } /* set the used to zero, allocated digits to the default precision * and sign to positive */ a->used = 0; a->alloc = MP_PREC; a->sign = MP_ZPOS; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_init.c */ /* Start: bn_mp_init_copy.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_INIT_COPY_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* creates "a" then copies b into it */ int mp_init_copy (mp_int * a, mp_int * b) { int res; if ((res = mp_init_size (a, b->used)) != MP_OKAY) { return res; } return mp_copy (b, a); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_init_copy.c */ /* Start: bn_mp_init_multi.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_INIT_MULTI_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ #include int mp_init_multi(mp_int *mp, ...) { mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ int n = 0; /* Number of ok inits */ mp_int* cur_arg = mp; va_list args; va_start(args, mp); /* init args to next argument from caller */ while (cur_arg != NULL) { if (mp_init(cur_arg) != MP_OKAY) { /* Oops - error! Back-track and mp_clear what we already succeeded in init-ing, then return error. */ va_list clean_args; /* end the current list */ va_end(args); /* now start cleaning up */ cur_arg = mp; va_start(clean_args, mp); while (n-- != 0) { mp_clear(cur_arg); cur_arg = va_arg(clean_args, mp_int*); } va_end(clean_args); res = MP_MEM; break; } n++; cur_arg = va_arg(args, mp_int*); } va_end(args); return res; /* Assumed ok, if error flagged above. */ } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_init_multi.c */ /* Start: bn_mp_init_set.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_INIT_SET_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* initialize and set a digit */ int mp_init_set (mp_int * a, mp_digit b) { int err; if ((err = mp_init(a)) != MP_OKAY) { return err; } mp_set(a, b); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_init_set.c */ /* Start: bn_mp_init_set_int.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_INIT_SET_INT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* initialize and set a digit */ int mp_init_set_int (mp_int * a, unsigned long b) { int err; if ((err = mp_init(a)) != MP_OKAY) { return err; } return mp_set_int(a, b); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_init_set_int.c */ /* Start: bn_mp_init_size.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_INIT_SIZE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* init an mp_init for a given size */ int mp_init_size (mp_int * a, int size) { int x; /* pad size so there are always extra digits */ size += (MP_PREC * 2) - (size % MP_PREC); /* alloc mem */ a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); if (a->dp == NULL) { return MP_MEM; } /* set the members */ a->used = 0; a->alloc = size; a->sign = MP_ZPOS; /* zero the digits */ for (x = 0; x < size; x++) { a->dp[x] = 0; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_init_size.c */ /* Start: bn_mp_invmod.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_INVMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* hac 14.61, pp608 */ int mp_invmod (mp_int * a, mp_int * b, mp_int * c) { /* b cannot be negative */ if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) { return MP_VAL; } #ifdef BN_FAST_MP_INVMOD_C /* if the modulus is odd we can use a faster routine instead */ if (mp_isodd (b) == MP_YES) { return fast_mp_invmod (a, b, c); } #endif #ifdef BN_MP_INVMOD_SLOW_C return mp_invmod_slow(a, b, c); #else return MP_VAL; #endif } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_invmod.c */ /* Start: bn_mp_invmod_slow.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_INVMOD_SLOW_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* hac 14.61, pp608 */ int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) { mp_int x, y, u, v, A, B, C, D; int res; /* b cannot be negative */ if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) { return MP_VAL; } /* init temps */ if ((res = mp_init_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL)) != MP_OKAY) { return res; } /* x = a, y = b */ if ((res = mp_mod(a, b, &x)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_copy (b, &y)) != MP_OKAY) { goto LBL_ERR; } /* 2. [modified] if x,y are both even then return an error! */ if ((mp_iseven (&x) == MP_YES) && (mp_iseven (&y) == MP_YES)) { res = MP_VAL; goto LBL_ERR; } /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ if ((res = mp_copy (&x, &u)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_copy (&y, &v)) != MP_OKAY) { goto LBL_ERR; } mp_set (&A, 1); mp_set (&D, 1); top: /* 4. while u is even do */ while (mp_iseven (&u) == MP_YES) { /* 4.1 u = u/2 */ if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { goto LBL_ERR; } /* 4.2 if A or B is odd then */ if ((mp_isodd (&A) == MP_YES) || (mp_isodd (&B) == MP_YES)) { /* A = (A+y)/2, B = (B-x)/2 */ if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { goto LBL_ERR; } } /* A = A/2, B = B/2 */ if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { goto LBL_ERR; } } /* 5. while v is even do */ while (mp_iseven (&v) == MP_YES) { /* 5.1 v = v/2 */ if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { goto LBL_ERR; } /* 5.2 if C or D is odd then */ if ((mp_isodd (&C) == MP_YES) || (mp_isodd (&D) == MP_YES)) { /* C = (C+y)/2, D = (D-x)/2 */ if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { goto LBL_ERR; } } /* C = C/2, D = D/2 */ if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { goto LBL_ERR; } } /* 6. if u >= v then */ if (mp_cmp (&u, &v) != MP_LT) { /* u = u - v, A = A - C, B = B - D */ if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { goto LBL_ERR; } } else { /* v - v - u, C = C - A, D = D - B */ if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { goto LBL_ERR; } } /* if not zero goto step 4 */ if (mp_iszero (&u) == MP_NO) goto top; /* now a = C, b = D, gcd == g*v */ /* if v != 1 then there is no inverse */ if (mp_cmp_d (&v, 1) != MP_EQ) { res = MP_VAL; goto LBL_ERR; } /* if its too low */ while (mp_cmp_d(&C, 0) == MP_LT) { if ((res = mp_add(&C, b, &C)) != MP_OKAY) { goto LBL_ERR; } } /* too big */ while (mp_cmp_mag(&C, b) != MP_LT) { if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { goto LBL_ERR; } } /* C is now the inverse */ mp_exch (&C, c); res = MP_OKAY; LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_invmod_slow.c */ /* Start: bn_mp_is_square.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_IS_SQUARE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Check if remainders are possible squares - fast exclude non-squares */ static const char rem_128[128] = { 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 }; static const char rem_105[105] = { 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 }; /* Store non-zero to ret if arg is square, and zero if not */ int mp_is_square(mp_int *arg,int *ret) { int res; mp_digit c; mp_int t; unsigned long r; /* Default to Non-square :) */ *ret = MP_NO; if (arg->sign == MP_NEG) { return MP_VAL; } /* digits used? (TSD) */ if (arg->used == 0) { return MP_OKAY; } /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */ if (rem_128[127 & DIGIT(arg,0)] == 1) { return MP_OKAY; } /* Next check mod 105 (3*5*7) */ if ((res = mp_mod_d(arg,105,&c)) != MP_OKAY) { return res; } if (rem_105[c] == 1) { return MP_OKAY; } if ((res = mp_init_set_int(&t,11L*13L*17L*19L*23L*29L*31L)) != MP_OKAY) { return res; } if ((res = mp_mod(arg,&t,&t)) != MP_OKAY) { goto ERR; } r = mp_get_int(&t); /* Check for other prime modules, note it's not an ERROR but we must * free "t" so the easiest way is to goto ERR. We know that res * is already equal to MP_OKAY from the mp_mod call */ if (((1L<<(r%11)) & 0x5C4L) != 0L) goto ERR; if (((1L<<(r%13)) & 0x9E4L) != 0L) goto ERR; if (((1L<<(r%17)) & 0x5CE8L) != 0L) goto ERR; if (((1L<<(r%19)) & 0x4F50CL) != 0L) goto ERR; if (((1L<<(r%23)) & 0x7ACCA0L) != 0L) goto ERR; if (((1L<<(r%29)) & 0xC2EDD0CL) != 0L) goto ERR; if (((1L<<(r%31)) & 0x6DE2B848L) != 0L) goto ERR; /* Final check - is sqr(sqrt(arg)) == arg ? */ if ((res = mp_sqrt(arg,&t)) != MP_OKAY) { goto ERR; } if ((res = mp_sqr(&t,&t)) != MP_OKAY) { goto ERR; } *ret = (mp_cmp_mag(&t,arg) == MP_EQ) ? MP_YES : MP_NO; ERR:mp_clear(&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_is_square.c */ /* Start: bn_mp_jacobi.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_JACOBI_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes the jacobi c = (a | n) (or Legendre if n is prime) * HAC pp. 73 Algorithm 2.149 * HAC is wrong here, as the special case of (0 | 1) is not * handled correctly. */ int mp_jacobi (mp_int * a, mp_int * n, int *c) { mp_int a1, p1; int k, s, r, res; mp_digit residue; /* if a < 0 return MP_VAL */ if (mp_isneg(a) == MP_YES) { return MP_VAL; } /* if n <= 0 return MP_VAL */ if (mp_cmp_d(n, 0) != MP_GT) { return MP_VAL; } /* step 1. handle case of a == 0 */ if (mp_iszero (a) == MP_YES) { /* special case of a == 0 and n == 1 */ if (mp_cmp_d (n, 1) == MP_EQ) { *c = 1; } else { *c = 0; } return MP_OKAY; } /* step 2. if a == 1, return 1 */ if (mp_cmp_d (a, 1) == MP_EQ) { *c = 1; return MP_OKAY; } /* default */ s = 0; /* step 3. write a = a1 * 2**k */ if ((res = mp_init_copy (&a1, a)) != MP_OKAY) { return res; } if ((res = mp_init (&p1)) != MP_OKAY) { goto LBL_A1; } /* divide out larger power of two */ k = mp_cnt_lsb(&a1); if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) { goto LBL_P1; } /* step 4. if e is even set s=1 */ if ((k & 1) == 0) { s = 1; } else { /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */ residue = n->dp[0] & 7; if ((residue == 1) || (residue == 7)) { s = 1; } else if ((residue == 3) || (residue == 5)) { s = -1; } } /* step 5. if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ if ( ((n->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 3)) { s = -s; } /* if a1 == 1 we're done */ if (mp_cmp_d (&a1, 1) == MP_EQ) { *c = s; } else { /* n1 = n mod a1 */ if ((res = mp_mod (n, &a1, &p1)) != MP_OKAY) { goto LBL_P1; } if ((res = mp_jacobi (&p1, &a1, &r)) != MP_OKAY) { goto LBL_P1; } *c = s * r; } /* done */ res = MP_OKAY; LBL_P1:mp_clear (&p1); LBL_A1:mp_clear (&a1); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_jacobi.c */ /* Start: bn_mp_karatsuba_mul.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_KARATSUBA_MUL_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* c = |a| * |b| using Karatsuba Multiplication using * three half size multiplications * * Let B represent the radix [e.g. 2**DIGIT_BIT] and * let n represent half of the number of digits in * the min(a,b) * * a = a1 * B**n + a0 * b = b1 * B**n + b0 * * Then, a * b => a1b1 * B**2n + ((a1 + a0)(b1 + b0) - (a0b0 + a1b1)) * B + a0b0 * * Note that a1b1 and a0b0 are used twice and only need to be * computed once. So in total three half size (half # of * digit) multiplications are performed, a0b0, a1b1 and * (a1+b1)(a0+b0) * * Note that a multiplication of half the digits requires * 1/4th the number of single precision multiplications so in * total after one call 25% of the single precision multiplications * are saved. Note also that the call to mp_mul can end up back * in this function if the a0, a1, b0, or b1 are above the threshold. * This is known as divide-and-conquer and leads to the famous * O(N**lg(3)) or O(N**1.584) work which is asymptopically lower than * the standard O(N**2) that the baseline/comba methods use. * Generally though the overhead of this method doesn't pay off * until a certain size (N ~ 80) is reached. */ int mp_karatsuba_mul (mp_int * a, mp_int * b, mp_int * c) { mp_int x0, x1, y0, y1, t1, x0y0, x1y1; int B, err; /* default the return code to an error */ err = MP_MEM; /* min # of digits */ B = MIN (a->used, b->used); /* now divide in two */ B = B >> 1; /* init copy all the temps */ if (mp_init_size (&x0, B) != MP_OKAY) goto ERR; if (mp_init_size (&x1, a->used - B) != MP_OKAY) goto X0; if (mp_init_size (&y0, B) != MP_OKAY) goto X1; if (mp_init_size (&y1, b->used - B) != MP_OKAY) goto Y0; /* init temps */ if (mp_init_size (&t1, B * 2) != MP_OKAY) goto Y1; if (mp_init_size (&x0y0, B * 2) != MP_OKAY) goto T1; if (mp_init_size (&x1y1, B * 2) != MP_OKAY) goto X0Y0; /* now shift the digits */ x0.used = y0.used = B; x1.used = a->used - B; y1.used = b->used - B; { int x; mp_digit *tmpa, *tmpb, *tmpx, *tmpy; /* we copy the digits directly instead of using higher level functions * since we also need to shift the digits */ tmpa = a->dp; tmpb = b->dp; tmpx = x0.dp; tmpy = y0.dp; for (x = 0; x < B; x++) { *tmpx++ = *tmpa++; *tmpy++ = *tmpb++; } tmpx = x1.dp; for (x = B; x < a->used; x++) { *tmpx++ = *tmpa++; } tmpy = y1.dp; for (x = B; x < b->used; x++) { *tmpy++ = *tmpb++; } } /* only need to clamp the lower words since by definition the * upper words x1/y1 must have a known number of digits */ mp_clamp (&x0); mp_clamp (&y0); /* now calc the products x0y0 and x1y1 */ /* after this x0 is no longer required, free temp [x0==t2]! */ if (mp_mul (&x0, &y0, &x0y0) != MP_OKAY) goto X1Y1; /* x0y0 = x0*y0 */ if (mp_mul (&x1, &y1, &x1y1) != MP_OKAY) goto X1Y1; /* x1y1 = x1*y1 */ /* now calc x1+x0 and y1+y0 */ if (s_mp_add (&x1, &x0, &t1) != MP_OKAY) goto X1Y1; /* t1 = x1 - x0 */ if (s_mp_add (&y1, &y0, &x0) != MP_OKAY) goto X1Y1; /* t2 = y1 - y0 */ if (mp_mul (&t1, &x0, &t1) != MP_OKAY) goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */ /* add x0y0 */ if (mp_add (&x0y0, &x1y1, &x0) != MP_OKAY) goto X1Y1; /* t2 = x0y0 + x1y1 */ if (s_mp_sub (&t1, &x0, &t1) != MP_OKAY) goto X1Y1; /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */ /* shift by B */ if (mp_lshd (&t1, B) != MP_OKAY) goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<used; /* now divide in two */ B = B >> 1; /* init copy all the temps */ if (mp_init_size (&x0, B) != MP_OKAY) goto ERR; if (mp_init_size (&x1, a->used - B) != MP_OKAY) goto X0; /* init temps */ if (mp_init_size (&t1, a->used * 2) != MP_OKAY) goto X1; if (mp_init_size (&t2, a->used * 2) != MP_OKAY) goto T1; if (mp_init_size (&x0x0, B * 2) != MP_OKAY) goto T2; if (mp_init_size (&x1x1, (a->used - B) * 2) != MP_OKAY) goto X0X0; { int x; mp_digit *dst, *src; src = a->dp; /* now shift the digits */ dst = x0.dp; for (x = 0; x < B; x++) { *dst++ = *src++; } dst = x1.dp; for (x = B; x < a->used; x++) { *dst++ = *src++; } } x0.used = B; x1.used = a->used - B; mp_clamp (&x0); /* now calc the products x0*x0 and x1*x1 */ if (mp_sqr (&x0, &x0x0) != MP_OKAY) goto X1X1; /* x0x0 = x0*x0 */ if (mp_sqr (&x1, &x1x1) != MP_OKAY) goto X1X1; /* x1x1 = x1*x1 */ /* now calc (x1+x0)**2 */ if (s_mp_add (&x1, &x0, &t1) != MP_OKAY) goto X1X1; /* t1 = x1 - x0 */ if (mp_sqr (&t1, &t1) != MP_OKAY) goto X1X1; /* t1 = (x1 - x0) * (x1 - x0) */ /* add x0y0 */ if (s_mp_add (&x0x0, &x1x1, &t2) != MP_OKAY) goto X1X1; /* t2 = x0x0 + x1x1 */ if (s_mp_sub (&t1, &t2, &t1) != MP_OKAY) goto X1X1; /* t1 = (x1+x0)**2 - (x0x0 + x1x1) */ /* shift by B */ if (mp_lshd (&t1, B) != MP_OKAY) goto X1X1; /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))<sign = MP_ZPOS; LBL_T: mp_clear_multi (&t1, &t2, NULL); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_lcm.c */ /* Start: bn_mp_lshd.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_LSHD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* shift left a certain amount of digits */ int mp_lshd (mp_int * a, int b) { int x, res; /* if its less than zero return */ if (b <= 0) { return MP_OKAY; } /* grow to fit the new digits */ if (a->alloc < (a->used + b)) { if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { return res; } } { mp_digit *top, *bottom; /* increment the used by the shift amount then copy upwards */ a->used += b; /* top */ top = a->dp + a->used - 1; /* base */ bottom = (a->dp + a->used - 1) - b; /* much like mp_rshd this is implemented using a sliding window * except the window goes the otherway around. Copying from * the bottom to the top. see bn_mp_rshd.c for more info. */ for (x = a->used - 1; x >= b; x--) { *top-- = *bottom--; } /* zero the lower digits */ top = a->dp; for (x = 0; x < b; x++) { *top++ = 0; } } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_lshd.c */ /* Start: bn_mp_mod.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* c = a mod b, 0 <= c < b if b > 0, b < c <= 0 if b < 0 */ int mp_mod (mp_int * a, mp_int * b, mp_int * c) { mp_int t; int res; if ((res = mp_init (&t)) != MP_OKAY) { return res; } if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { mp_clear (&t); return res; } if ((mp_iszero(&t) != MP_NO) || (t.sign == b->sign)) { res = MP_OKAY; mp_exch (&t, c); } else { res = mp_add (b, &t, c); } mp_clear (&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_mod.c */ /* Start: bn_mp_mod_2d.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MOD_2D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* calc a value mod 2**b */ int mp_mod_2d (mp_int * a, int b, mp_int * c) { int x, res; /* if b is <= 0 then zero the int */ if (b <= 0) { mp_zero (c); return MP_OKAY; } /* if the modulus is larger than the value than return */ if (b >= (int) (a->used * DIGIT_BIT)) { res = mp_copy (a, c); return res; } /* copy */ if ((res = mp_copy (a, c)) != MP_OKAY) { return res; } /* zero digits above the last digit of the modulus */ for (x = (b / DIGIT_BIT) + (((b % DIGIT_BIT) == 0) ? 0 : 1); x < c->used; x++) { c->dp[x] = 0; } /* clear the digit that is not completely outside/inside the modulus */ c->dp[b / DIGIT_BIT] &= (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); mp_clamp (c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_mod_2d.c */ /* Start: bn_mp_mod_d.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MOD_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ int mp_mod_d (mp_int * a, mp_digit b, mp_digit * c) { return mp_div_d(a, b, NULL, c); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_mod_d.c */ /* Start: bn_mp_montgomery_calc_normalization.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* * shifts with subtractions when the result is greater than b. * * The method is slightly modified to shift B unconditionally upto just under * the leading bit of b. This saves alot of multiple precision shifting. */ int mp_montgomery_calc_normalization (mp_int * a, mp_int * b) { int x, bits, res; /* how many bits of last digit does b use */ bits = mp_count_bits (b) % DIGIT_BIT; if (b->used > 1) { if ((res = mp_2expt (a, ((b->used - 1) * DIGIT_BIT) + bits - 1)) != MP_OKAY) { return res; } } else { mp_set(a, 1); bits = 1; } /* now compute C = A * B mod b */ for (x = bits - 1; x < (int)DIGIT_BIT; x++) { if ((res = mp_mul_2 (a, a)) != MP_OKAY) { return res; } if (mp_cmp_mag (a, b) != MP_LT) { if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { return res; } } } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_montgomery_calc_normalization.c */ /* Start: bn_mp_montgomery_reduce.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MONTGOMERY_REDUCE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes xR**-1 == x (mod N) via Montgomery Reduction */ int mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) { int ix, res, digs; mp_digit mu; /* can the fast reduction [comba] method be used? * * Note that unlike in mul you're safely allowed *less* * than the available columns [255 per default] since carries * are fixed up in the inner loop. */ digs = (n->used * 2) + 1; if ((digs < MP_WARRAY) && (n->used < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { return fast_mp_montgomery_reduce (x, n, rho); } /* grow the input as required */ if (x->alloc < digs) { if ((res = mp_grow (x, digs)) != MP_OKAY) { return res; } } x->used = digs; for (ix = 0; ix < n->used; ix++) { /* mu = ai * rho mod b * * The value of rho must be precalculated via * montgomery_setup() such that * it equals -1/n0 mod b this allows the * following inner loop to reduce the * input one digit at a time */ mu = (mp_digit) (((mp_word)x->dp[ix] * (mp_word)rho) & MP_MASK); /* a = a + mu * m * b**i */ { int iy; mp_digit *tmpn, *tmpx, u; mp_word r; /* alias for digits of the modulus */ tmpn = n->dp; /* alias for the digits of x [the input] */ tmpx = x->dp + ix; /* set the carry to zero */ u = 0; /* Multiply and add in place */ for (iy = 0; iy < n->used; iy++) { /* compute product and sum */ r = ((mp_word)mu * (mp_word)*tmpn++) + (mp_word) u + (mp_word) *tmpx; /* get carry */ u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); /* fix digit */ *tmpx++ = (mp_digit)(r & ((mp_word) MP_MASK)); } /* At this point the ix'th digit of x should be zero */ /* propagate carries upwards as required*/ while (u != 0) { *tmpx += u; u = *tmpx >> DIGIT_BIT; *tmpx++ &= MP_MASK; } } } /* at this point the n.used'th least * significant digits of x are all zero * which means we can shift x to the * right by n.used digits and the * residue is unchanged. */ /* x = x/b**n.used */ mp_clamp(x); mp_rshd (x, n->used); /* if x >= n then x = x - n */ if (mp_cmp_mag (x, n) != MP_LT) { return s_mp_sub (x, n, x); } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_montgomery_reduce.c */ /* Start: bn_mp_montgomery_setup.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MONTGOMERY_SETUP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* setups the montgomery reduction stuff */ int mp_montgomery_setup (mp_int * n, mp_digit * rho) { mp_digit x, b; /* fast inversion mod 2**k * * Based on the fact that * * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) * => 2*X*A - X*X*A*A = 1 * => 2*(1) - (1) = 1 */ b = n->dp[0]; if ((b & 1) == 0) { return MP_VAL; } x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ x *= 2 - (b * x); /* here x*a==1 mod 2**8 */ #if !defined(MP_8BIT) x *= 2 - (b * x); /* here x*a==1 mod 2**16 */ #endif #if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) x *= 2 - (b * x); /* here x*a==1 mod 2**32 */ #endif #ifdef MP_64BIT x *= 2 - (b * x); /* here x*a==1 mod 2**64 */ #endif /* rho = -1/m mod b */ *rho = (mp_digit)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_montgomery_setup.c */ /* Start: bn_mp_mul.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MUL_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* high level multiplication (handles sign) */ int mp_mul (mp_int * a, mp_int * b, mp_int * c) { int res, neg; neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; /* use Toom-Cook? */ #ifdef BN_MP_TOOM_MUL_C if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { res = mp_toom_mul(a, b, c); } else #endif #ifdef BN_MP_KARATSUBA_MUL_C /* use Karatsuba? */ if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { res = mp_karatsuba_mul (a, b, c); } else #endif { /* can we use the fast multiplier? * * The fast multiplier can be used if the output will * have less than MP_WARRAY digits and the number of * digits won't affect carry propagation */ int digs = a->used + b->used + 1; #ifdef BN_FAST_S_MP_MUL_DIGS_C if ((digs < MP_WARRAY) && (MIN(a->used, b->used) <= (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { res = fast_s_mp_mul_digs (a, b, c, digs); } else #endif { #ifdef BN_S_MP_MUL_DIGS_C res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ #else res = MP_VAL; #endif } } c->sign = (c->used > 0) ? neg : MP_ZPOS; return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_mul.c */ /* Start: bn_mp_mul_2.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MUL_2_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* b = a*2 */ int mp_mul_2(mp_int * a, mp_int * b) { int x, res, oldused; /* grow to accomodate result */ if (b->alloc < (a->used + 1)) { if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { return res; } } oldused = b->used; b->used = a->used; { mp_digit r, rr, *tmpa, *tmpb; /* alias for source */ tmpa = a->dp; /* alias for dest */ tmpb = b->dp; /* carry */ r = 0; for (x = 0; x < a->used; x++) { /* get what will be the *next* carry bit from the * MSB of the current digit */ rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); /* now shift up this digit, add in the carry [from the previous] */ *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; /* copy the carry that would be from the source * digit into the next iteration */ r = rr; } /* new leading digit? */ if (r != 0) { /* add a MSB which is always 1 at this point */ *tmpb = 1; ++(b->used); } /* now zero any excess digits on the destination * that we didn't write to */ tmpb = b->dp + b->used; for (x = b->used; x < oldused; x++) { *tmpb++ = 0; } } b->sign = a->sign; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_mul_2.c */ /* Start: bn_mp_mul_2d.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MUL_2D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* shift left by a certain bit count */ int mp_mul_2d (mp_int * a, int b, mp_int * c) { mp_digit d; int res; /* copy */ if (a != c) { if ((res = mp_copy (a, c)) != MP_OKAY) { return res; } } if (c->alloc < (int)(c->used + (b / DIGIT_BIT) + 1)) { if ((res = mp_grow (c, c->used + (b / DIGIT_BIT) + 1)) != MP_OKAY) { return res; } } /* shift by as many digits in the bit count */ if (b >= (int)DIGIT_BIT) { if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { return res; } } /* shift any bit count < DIGIT_BIT */ d = (mp_digit) (b % DIGIT_BIT); if (d != 0) { mp_digit *tmpc, shift, mask, r, rr; int x; /* bitmask for carries */ mask = (((mp_digit)1) << d) - 1; /* shift for msbs */ shift = DIGIT_BIT - d; /* alias */ tmpc = c->dp; /* carry */ r = 0; for (x = 0; x < c->used; x++) { /* get the higher bits of the current word */ rr = (*tmpc >> shift) & mask; /* shift the current word and OR in the carry */ *tmpc = ((*tmpc << d) | r) & MP_MASK; ++tmpc; /* set the carry to the carry bits of the current word */ r = rr; } /* set final carry */ if (r != 0) { c->dp[(c->used)++] = r; } } mp_clamp (c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_mul_2d.c */ /* Start: bn_mp_mul_d.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MUL_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* multiply by a digit */ int mp_mul_d (mp_int * a, mp_digit b, mp_int * c) { mp_digit u, *tmpa, *tmpc; mp_word r; int ix, res, olduse; /* make sure c is big enough to hold a*b */ if (c->alloc < (a->used + 1)) { if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { return res; } } /* get the original destinations used count */ olduse = c->used; /* set the sign */ c->sign = a->sign; /* alias for a->dp [source] */ tmpa = a->dp; /* alias for c->dp [dest] */ tmpc = c->dp; /* zero carry */ u = 0; /* compute columns */ for (ix = 0; ix < a->used; ix++) { /* compute product and carry sum for this term */ r = (mp_word)u + ((mp_word)*tmpa++ * (mp_word)b); /* mask off higher bits to get a single digit */ *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); /* send carry into next iteration */ u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); } /* store final carry [if any] and increment ix offset */ *tmpc++ = u; ++ix; /* now zero digits above the top */ while (ix++ < olduse) { *tmpc++ = 0; } /* set used count */ c->used = a->used + 1; mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_mul_d.c */ /* Start: bn_mp_mulmod.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_MULMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* d = a * b (mod c) */ int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) { int res; mp_int t; if ((res = mp_init (&t)) != MP_OKAY) { return res; } if ((res = mp_mul (a, b, &t)) != MP_OKAY) { mp_clear (&t); return res; } res = mp_mod (&t, c, d); mp_clear (&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_mulmod.c */ /* Start: bn_mp_n_root.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_N_ROOT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* wrapper function for mp_n_root_ex() * computes c = (a)**(1/b) such that (c)**b <= a and (c+1)**b > a */ int mp_n_root (mp_int * a, mp_digit b, mp_int * c) { return mp_n_root_ex(a, b, c, 0); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_n_root.c */ /* Start: bn_mp_n_root_ex.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_N_ROOT_EX_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* find the n'th root of an integer * * Result found such that (c)**b <= a and (c+1)**b > a * * This algorithm uses Newton's approximation * x[i+1] = x[i] - f(x[i])/f'(x[i]) * which will find the root in log(N) time where * each step involves a fair bit. This is not meant to * find huge roots [square and cube, etc]. */ int mp_n_root_ex (mp_int * a, mp_digit b, mp_int * c, int fast) { mp_int t1, t2, t3; int res, neg; /* input must be positive if b is even */ if (((b & 1) == 0) && (a->sign == MP_NEG)) { return MP_VAL; } if ((res = mp_init (&t1)) != MP_OKAY) { return res; } if ((res = mp_init (&t2)) != MP_OKAY) { goto LBL_T1; } if ((res = mp_init (&t3)) != MP_OKAY) { goto LBL_T2; } /* if a is negative fudge the sign but keep track */ neg = a->sign; a->sign = MP_ZPOS; /* t2 = 2 */ mp_set (&t2, 2); do { /* t1 = t2 */ if ((res = mp_copy (&t2, &t1)) != MP_OKAY) { goto LBL_T3; } /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ /* t3 = t1**(b-1) */ if ((res = mp_expt_d_ex (&t1, b - 1, &t3, fast)) != MP_OKAY) { goto LBL_T3; } /* numerator */ /* t2 = t1**b */ if ((res = mp_mul (&t3, &t1, &t2)) != MP_OKAY) { goto LBL_T3; } /* t2 = t1**b - a */ if ((res = mp_sub (&t2, a, &t2)) != MP_OKAY) { goto LBL_T3; } /* denominator */ /* t3 = t1**(b-1) * b */ if ((res = mp_mul_d (&t3, b, &t3)) != MP_OKAY) { goto LBL_T3; } /* t3 = (t1**b - a)/(b * t1**(b-1)) */ if ((res = mp_div (&t2, &t3, &t3, NULL)) != MP_OKAY) { goto LBL_T3; } if ((res = mp_sub (&t1, &t3, &t2)) != MP_OKAY) { goto LBL_T3; } } while (mp_cmp (&t1, &t2) != MP_EQ); /* result can be off by a few so check */ for (;;) { if ((res = mp_expt_d_ex (&t1, b, &t2, fast)) != MP_OKAY) { goto LBL_T3; } if (mp_cmp (&t2, a) == MP_GT) { if ((res = mp_sub_d (&t1, 1, &t1)) != MP_OKAY) { goto LBL_T3; } } else { break; } } /* reset the sign of a first */ a->sign = neg; /* set the result */ mp_exch (&t1, c); /* set the sign of the result */ c->sign = neg; res = MP_OKAY; LBL_T3:mp_clear (&t3); LBL_T2:mp_clear (&t2); LBL_T1:mp_clear (&t1); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_n_root_ex.c */ /* Start: bn_mp_neg.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_NEG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* b = -a */ int mp_neg (mp_int * a, mp_int * b) { int res; if (a != b) { if ((res = mp_copy (a, b)) != MP_OKAY) { return res; } } if (mp_iszero(b) != MP_YES) { b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; } else { b->sign = MP_ZPOS; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_neg.c */ /* Start: bn_mp_or.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_OR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* OR two ints together */ int mp_or (mp_int * a, mp_int * b, mp_int * c) { int res, ix, px; mp_int t, *x; if (a->used > b->used) { if ((res = mp_init_copy (&t, a)) != MP_OKAY) { return res; } px = b->used; x = b; } else { if ((res = mp_init_copy (&t, b)) != MP_OKAY) { return res; } px = a->used; x = a; } for (ix = 0; ix < px; ix++) { t.dp[ix] |= x->dp[ix]; } mp_clamp (&t); mp_exch (c, &t); mp_clear (&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_or.c */ /* Start: bn_mp_prime_fermat.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_PRIME_FERMAT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* performs one Fermat test. * * If "a" were prime then b**a == b (mod a) since the order of * the multiplicative sub-group would be phi(a) = a-1. That means * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a). * * Sets result to 1 if the congruence holds, or zero otherwise. */ int mp_prime_fermat (mp_int * a, mp_int * b, int *result) { mp_int t; int err; /* default to composite */ *result = MP_NO; /* ensure b > 1 */ if (mp_cmp_d(b, 1) != MP_GT) { return MP_VAL; } /* init t */ if ((err = mp_init (&t)) != MP_OKAY) { return err; } /* compute t = b**a mod a */ if ((err = mp_exptmod (b, a, a, &t)) != MP_OKAY) { goto LBL_T; } /* is it equal to b? */ if (mp_cmp (&t, b) == MP_EQ) { *result = MP_YES; } err = MP_OKAY; LBL_T:mp_clear (&t); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_prime_fermat.c */ /* Start: bn_mp_prime_is_divisible.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_PRIME_IS_DIVISIBLE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines if an integers is divisible by one * of the first PRIME_SIZE primes or not * * sets result to 0 if not, 1 if yes */ int mp_prime_is_divisible (mp_int * a, int *result) { int err, ix; mp_digit res; /* default to not */ *result = MP_NO; for (ix = 0; ix < PRIME_SIZE; ix++) { /* what is a mod LBL_prime_tab[ix] */ if ((err = mp_mod_d (a, ltm_prime_tab[ix], &res)) != MP_OKAY) { return err; } /* is the residue zero? */ if (res == 0) { *result = MP_YES; return MP_OKAY; } } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_prime_is_divisible.c */ /* Start: bn_mp_prime_is_prime.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_PRIME_IS_PRIME_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* performs a variable number of rounds of Miller-Rabin * * Probability of error after t rounds is no more than * * Sets result to 1 if probably prime, 0 otherwise */ int mp_prime_is_prime (mp_int * a, int t, int *result) { mp_int b; int ix, err, res; /* default to no */ *result = MP_NO; /* valid value of t? */ if ((t <= 0) || (t > PRIME_SIZE)) { return MP_VAL; } /* is the input equal to one of the primes in the table? */ for (ix = 0; ix < PRIME_SIZE; ix++) { if (mp_cmp_d(a, ltm_prime_tab[ix]) == MP_EQ) { *result = 1; return MP_OKAY; } } /* first perform trial division */ if ((err = mp_prime_is_divisible (a, &res)) != MP_OKAY) { return err; } /* return if it was trivially divisible */ if (res == MP_YES) { return MP_OKAY; } /* now perform the miller-rabin rounds */ if ((err = mp_init (&b)) != MP_OKAY) { return err; } for (ix = 0; ix < t; ix++) { /* set the prime */ mp_set (&b, ltm_prime_tab[ix]); if ((err = mp_prime_miller_rabin (a, &b, &res)) != MP_OKAY) { goto LBL_B; } if (res == MP_NO) { goto LBL_B; } } /* passed the test */ *result = MP_YES; LBL_B:mp_clear (&b); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_prime_is_prime.c */ /* Start: bn_mp_prime_miller_rabin.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_PRIME_MILLER_RABIN_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Miller-Rabin test of "a" to the base of "b" as described in * HAC pp. 139 Algorithm 4.24 * * Sets result to 0 if definitely composite or 1 if probably prime. * Randomly the chance of error is no more than 1/4 and often * very much lower. */ int mp_prime_miller_rabin (mp_int * a, mp_int * b, int *result) { mp_int n1, y, r; int s, j, err; /* default */ *result = MP_NO; /* ensure b > 1 */ if (mp_cmp_d(b, 1) != MP_GT) { return MP_VAL; } /* get n1 = a - 1 */ if ((err = mp_init_copy (&n1, a)) != MP_OKAY) { return err; } if ((err = mp_sub_d (&n1, 1, &n1)) != MP_OKAY) { goto LBL_N1; } /* set 2**s * r = n1 */ if ((err = mp_init_copy (&r, &n1)) != MP_OKAY) { goto LBL_N1; } /* count the number of least significant bits * which are zero */ s = mp_cnt_lsb(&r); /* now divide n - 1 by 2**s */ if ((err = mp_div_2d (&r, s, &r, NULL)) != MP_OKAY) { goto LBL_R; } /* compute y = b**r mod a */ if ((err = mp_init (&y)) != MP_OKAY) { goto LBL_R; } if ((err = mp_exptmod (b, &r, a, &y)) != MP_OKAY) { goto LBL_Y; } /* if y != 1 and y != n1 do */ if ((mp_cmp_d (&y, 1) != MP_EQ) && (mp_cmp (&y, &n1) != MP_EQ)) { j = 1; /* while j <= s-1 and y != n1 */ while ((j <= (s - 1)) && (mp_cmp (&y, &n1) != MP_EQ)) { if ((err = mp_sqrmod (&y, a, &y)) != MP_OKAY) { goto LBL_Y; } /* if y == 1 then composite */ if (mp_cmp_d (&y, 1) == MP_EQ) { goto LBL_Y; } ++j; } /* if y != n1 then composite */ if (mp_cmp (&y, &n1) != MP_EQ) { goto LBL_Y; } } /* probably prime now */ *result = MP_YES; LBL_Y:mp_clear (&y); LBL_R:mp_clear (&r); LBL_N1:mp_clear (&n1); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_prime_miller_rabin.c */ /* Start: bn_mp_prime_next_prime.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_PRIME_NEXT_PRIME_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* finds the next prime after the number "a" using "t" trials * of Miller-Rabin. * * bbs_style = 1 means the prime must be congruent to 3 mod 4 */ int mp_prime_next_prime(mp_int *a, int t, int bbs_style) { int err, res = MP_NO, x, y; mp_digit res_tab[PRIME_SIZE], step, kstep; mp_int b; /* ensure t is valid */ if ((t <= 0) || (t > PRIME_SIZE)) { return MP_VAL; } /* force positive */ a->sign = MP_ZPOS; /* simple algo if a is less than the largest prime in the table */ if (mp_cmp_d(a, ltm_prime_tab[PRIME_SIZE-1]) == MP_LT) { /* find which prime it is bigger than */ for (x = PRIME_SIZE - 2; x >= 0; x--) { if (mp_cmp_d(a, ltm_prime_tab[x]) != MP_LT) { if (bbs_style == 1) { /* ok we found a prime smaller or * equal [so the next is larger] * * however, the prime must be * congruent to 3 mod 4 */ if ((ltm_prime_tab[x + 1] & 3) != 3) { /* scan upwards for a prime congruent to 3 mod 4 */ for (y = x + 1; y < PRIME_SIZE; y++) { if ((ltm_prime_tab[y] & 3) == 3) { mp_set(a, ltm_prime_tab[y]); return MP_OKAY; } } } } else { mp_set(a, ltm_prime_tab[x + 1]); return MP_OKAY; } } } /* at this point a maybe 1 */ if (mp_cmp_d(a, 1) == MP_EQ) { mp_set(a, 2); return MP_OKAY; } /* fall through to the sieve */ } /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */ if (bbs_style == 1) { kstep = 4; } else { kstep = 2; } /* at this point we will use a combination of a sieve and Miller-Rabin */ if (bbs_style == 1) { /* if a mod 4 != 3 subtract the correct value to make it so */ if ((a->dp[0] & 3) != 3) { if ((err = mp_sub_d(a, (a->dp[0] & 3) + 1, a)) != MP_OKAY) { return err; }; } } else { if (mp_iseven(a) == MP_YES) { /* force odd */ if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { return err; } } } /* generate the restable */ for (x = 1; x < PRIME_SIZE; x++) { if ((err = mp_mod_d(a, ltm_prime_tab[x], res_tab + x)) != MP_OKAY) { return err; } } /* init temp used for Miller-Rabin Testing */ if ((err = mp_init(&b)) != MP_OKAY) { return err; } for (;;) { /* skip to the next non-trivially divisible candidate */ step = 0; do { /* y == 1 if any residue was zero [e.g. cannot be prime] */ y = 0; /* increase step to next candidate */ step += kstep; /* compute the new residue without using division */ for (x = 1; x < PRIME_SIZE; x++) { /* add the step to each residue */ res_tab[x] += kstep; /* subtract the modulus [instead of using division] */ if (res_tab[x] >= ltm_prime_tab[x]) { res_tab[x] -= ltm_prime_tab[x]; } /* set flag if zero */ if (res_tab[x] == 0) { y = 1; } } } while ((y == 1) && (step < ((((mp_digit)1) << DIGIT_BIT) - kstep))); /* add the step */ if ((err = mp_add_d(a, step, a)) != MP_OKAY) { goto LBL_ERR; } /* if didn't pass sieve and step == MAX then skip test */ if ((y == 1) && (step >= ((((mp_digit)1) << DIGIT_BIT) - kstep))) { continue; } /* is this prime? */ for (x = 0; x < t; x++) { mp_set(&b, ltm_prime_tab[x]); if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { goto LBL_ERR; } if (res == MP_NO) { break; } } if (res == MP_YES) { break; } } err = MP_OKAY; LBL_ERR: mp_clear(&b); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_prime_next_prime.c */ /* Start: bn_mp_prime_rabin_miller_trials.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_PRIME_RABIN_MILLER_TRIALS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ static const struct { int k, t; } sizes[] = { { 128, 28 }, { 256, 16 }, { 384, 10 }, { 512, 7 }, { 640, 6 }, { 768, 5 }, { 896, 4 }, { 1024, 4 } }; /* returns # of RM trials required for a given bit size */ int mp_prime_rabin_miller_trials(int size) { int x; for (x = 0; x < (int)(sizeof(sizes)/(sizeof(sizes[0]))); x++) { if (sizes[x].k == size) { return sizes[x].t; } else if (sizes[x].k > size) { return (x == 0) ? sizes[0].t : sizes[x - 1].t; } } return sizes[x-1].t + 1; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_prime_rabin_miller_trials.c */ /* Start: bn_mp_prime_random_ex.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_PRIME_RANDOM_EX_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* makes a truly random prime of a given size (bits), * * Flags are as follows: * * LTM_PRIME_BBS - make prime congruent to 3 mod 4 * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) * LTM_PRIME_2MSB_ON - make the 2nd highest bit one * * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself * so it can be NULL * */ /* This is possibly the mother of all prime generation functions, muahahahahaha! */ int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat) { unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb; int res, err, bsize, maskOR_msb_offset; /* sanity check the input */ if ((size <= 1) || (t <= 0)) { return MP_VAL; } /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */ if ((flags & LTM_PRIME_SAFE) != 0) { flags |= LTM_PRIME_BBS; } /* calc the byte size */ bsize = (size>>3) + ((size&7)?1:0); /* we need a buffer of bsize bytes */ tmp = OPT_CAST(unsigned char) XMALLOC(bsize); if (tmp == NULL) { return MP_MEM; } /* calc the maskAND value for the MSbyte*/ maskAND = ((size&7) == 0) ? 0xFF : (0xFF >> (8 - (size & 7))); /* calc the maskOR_msb */ maskOR_msb = 0; maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0; if ((flags & LTM_PRIME_2MSB_ON) != 0) { maskOR_msb |= 0x80 >> ((9 - size) & 7); } /* get the maskOR_lsb */ maskOR_lsb = 1; if ((flags & LTM_PRIME_BBS) != 0) { maskOR_lsb |= 3; } do { /* read the bytes */ if (cb(tmp, bsize, dat) != bsize) { err = MP_VAL; goto error; } /* work over the MSbyte */ tmp[0] &= maskAND; tmp[0] |= 1 << ((size - 1) & 7); /* mix in the maskORs */ tmp[maskOR_msb_offset] |= maskOR_msb; tmp[bsize-1] |= maskOR_lsb; /* read it in */ if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY) { goto error; } /* is it prime? */ if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } if (res == MP_NO) { continue; } if ((flags & LTM_PRIME_SAFE) != 0) { /* see if (a-1)/2 is prime */ if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { goto error; } if ((err = mp_div_2(a, a)) != MP_OKAY) { goto error; } /* is it prime? */ if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } } } while (res == MP_NO); if ((flags & LTM_PRIME_SAFE) != 0) { /* restore a to the original value */ if ((err = mp_mul_2(a, a)) != MP_OKAY) { goto error; } if ((err = mp_add_d(a, 1, a)) != MP_OKAY) { goto error; } } err = MP_OKAY; error: XFREE(tmp); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_prime_random_ex.c */ /* Start: bn_mp_radix_size.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_RADIX_SIZE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* returns size of ASCII reprensentation */ int mp_radix_size (mp_int * a, int radix, int *size) { int res, digs; mp_int t; mp_digit d; *size = 0; /* make sure the radix is in range */ if ((radix < 2) || (radix > 64)) { return MP_VAL; } if (mp_iszero(a) == MP_YES) { *size = 2; return MP_OKAY; } /* special case for binary */ if (radix == 2) { *size = mp_count_bits (a) + ((a->sign == MP_NEG) ? 1 : 0) + 1; return MP_OKAY; } /* digs is the digit count */ digs = 0; /* if it's negative add one for the sign */ if (a->sign == MP_NEG) { ++digs; } /* init a copy of the input */ if ((res = mp_init_copy (&t, a)) != MP_OKAY) { return res; } /* force temp to positive */ t.sign = MP_ZPOS; /* fetch out all of the digits */ while (mp_iszero (&t) == MP_NO) { if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { mp_clear (&t); return res; } ++digs; } mp_clear (&t); /* return digs + 1, the 1 is for the NULL byte that would be required. */ *size = digs + 1; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_radix_size.c */ /* Start: bn_mp_radix_smap.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_RADIX_SMAP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* chars used in radix conversions */ const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_radix_smap.c */ /* Start: bn_mp_rand.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_RAND_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* makes a pseudo-random int of a given size */ int mp_rand (mp_int * a, int digits) { int res; mp_digit d; mp_zero (a); if (digits <= 0) { return MP_OKAY; } /* first place a random non-zero digit */ do { d = ((mp_digit) abs (int(MP_GEN_RANDOM()))) & MP_MASK; } while (d == 0); if ((res = mp_add_d (a, d, a)) != MP_OKAY) { return res; } while (--digits > 0) { if ((res = mp_lshd (a, 1)) != MP_OKAY) { return res; } if ((res = mp_add_d (a, ((mp_digit) abs (int(MP_GEN_RANDOM()))), a)) != MP_OKAY) { return res; } } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_rand.c */ /* Start: bn_mp_read_radix.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_READ_RADIX_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* read a string [ASCII] in a given radix */ int mp_read_radix (mp_int * a, const char *str, int radix) { int y, res, neg; char ch; /* zero the digit bignum */ mp_zero(a); /* make sure the radix is ok */ if ((radix < 2) || (radix > 64)) { return MP_VAL; } /* if the leading digit is a * minus set the sign to negative. */ if (*str == '-') { ++str; neg = MP_NEG; } else { neg = MP_ZPOS; } /* set the integer to the default of zero */ mp_zero (a); /* process each digit of the string */ while (*str != '\0') { /* if the radix <= 36 the conversion is case insensitive * this allows numbers like 1AB and 1ab to represent the same value * [e.g. in hex] */ ch = (radix <= 36) ? (char)toupper((int)*str) : *str; for (y = 0; y < 64; y++) { if (ch == mp_s_rmap[y]) { break; } } /* if the char was found in the map * and is less than the given radix add it * to the number, otherwise exit the loop. */ if (y < radix) { if ((res = mp_mul_d (a, (mp_digit) radix, a)) != MP_OKAY) { return res; } if ((res = mp_add_d (a, (mp_digit) y, a)) != MP_OKAY) { return res; } } else { break; } ++str; } /* set the sign only if a != 0 */ if (mp_iszero(a) != MP_YES) { a->sign = neg; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_read_radix.c */ /* Start: bn_mp_read_signed_bin.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_READ_SIGNED_BIN_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* read signed bin, big endian, first byte is 0==positive or 1==negative */ int mp_read_signed_bin (mp_int * a, const unsigned char *b, int c) { int res; /* read magnitude */ if ((res = mp_read_unsigned_bin (a, b + 1, c - 1)) != MP_OKAY) { return res; } /* first byte is 0 for positive, non-zero for negative */ if (b[0] == 0) { a->sign = MP_ZPOS; } else { a->sign = MP_NEG; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_read_signed_bin.c */ /* Start: bn_mp_read_unsigned_bin.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_READ_UNSIGNED_BIN_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reads a unsigned char array, assumes the msb is stored first [big endian] */ int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) { int res; /* make sure there are at least two digits */ if (a->alloc < 2) { if ((res = mp_grow(a, 2)) != MP_OKAY) { return res; } } /* zero the int */ mp_zero (a); /* read the bytes in */ while (c-- > 0) { if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { return res; } #ifndef MP_8BIT a->dp[0] |= *b++; a->used += 1; #else a->dp[0] = (*b & MP_MASK); a->dp[1] |= ((*b++ >> 7U) & 1); a->used += 2; #endif } mp_clamp (a); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_read_unsigned_bin.c */ /* Start: bn_mp_reduce.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_REDUCE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reduces x mod m, assumes 0 < x < m**2, mu is * precomputed via mp_reduce_setup. * From HAC pp.604 Algorithm 14.42 */ int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) { mp_int q; int res, um = m->used; /* q = x */ if ((res = mp_init_copy (&q, x)) != MP_OKAY) { return res; } /* q1 = x / b**(k-1) */ mp_rshd (&q, um - 1); /* according to HAC this optimization is ok */ if (((mp_digit) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { goto CLEANUP; } } else { #ifdef BN_S_MP_MUL_HIGH_DIGS_C if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { goto CLEANUP; } #elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { goto CLEANUP; } #else { res = MP_VAL; goto CLEANUP; } #endif } /* q3 = q2 / b**(k+1) */ mp_rshd (&q, um + 1); /* x = x mod b**(k+1), quick (no division) */ if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { goto CLEANUP; } /* q = q * m mod b**(k+1), quick (no division) */ if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { goto CLEANUP; } /* x = x - q */ if ((res = mp_sub (x, &q, x)) != MP_OKAY) { goto CLEANUP; } /* If x < 0, add b**(k+1) to it */ if (mp_cmp_d (x, 0) == MP_LT) { mp_set (&q, 1); if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) goto CLEANUP; if ((res = mp_add (x, &q, x)) != MP_OKAY) goto CLEANUP; } /* Back off if it's too big */ while (mp_cmp (x, m) != MP_LT) { if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { goto CLEANUP; } } CLEANUP: mp_clear (&q); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_reduce.c */ /* Start: bn_mp_reduce_2k.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_REDUCE_2K_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reduces a modulo n where n is of the form 2**p - d */ int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d) { mp_int q; int p, res; if ((res = mp_init(&q)) != MP_OKAY) { return res; } p = mp_count_bits(n); top: /* q = a/2**p, a = a mod 2**p */ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { goto ERR; } if (d != 1) { /* q = q * d */ if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) { goto ERR; } } /* a = a + q */ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { goto ERR; } if (mp_cmp_mag(a, n) != MP_LT) { if ((res = s_mp_sub(a, n, a)) != MP_OKAY) { goto ERR; } goto top; } ERR: mp_clear(&q); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_reduce_2k.c */ /* Start: bn_mp_reduce_2k_l.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_REDUCE_2K_L_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reduces a modulo n where n is of the form 2**p - d This differs from reduce_2k since "d" can be larger than a single digit. */ int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) { mp_int q; int p, res; if ((res = mp_init(&q)) != MP_OKAY) { return res; } p = mp_count_bits(n); top: /* q = a/2**p, a = a mod 2**p */ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { goto ERR; } /* q = q * d */ if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { goto ERR; } /* a = a + q */ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { goto ERR; } if (mp_cmp_mag(a, n) != MP_LT) { if ((res = s_mp_sub(a, n, a)) != MP_OKAY) { goto ERR; } goto top; } ERR: mp_clear(&q); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_reduce_2k_l.c */ /* Start: bn_mp_reduce_2k_setup.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_REDUCE_2K_SETUP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines the setup value */ int mp_reduce_2k_setup(mp_int *a, mp_digit *d) { int res, p; mp_int tmp; if ((res = mp_init(&tmp)) != MP_OKAY) { return res; } p = mp_count_bits(a); if ((res = mp_2expt(&tmp, p)) != MP_OKAY) { mp_clear(&tmp); return res; } if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) { mp_clear(&tmp); return res; } *d = tmp.dp[0]; mp_clear(&tmp); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_reduce_2k_setup.c */ /* Start: bn_mp_reduce_2k_setup_l.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_REDUCE_2K_SETUP_L_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines the setup value */ int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) { int res; mp_int tmp; if ((res = mp_init(&tmp)) != MP_OKAY) { return res; } if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { goto ERR; } if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { goto ERR; } ERR: mp_clear(&tmp); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_reduce_2k_setup_l.c */ /* Start: bn_mp_reduce_is_2k.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_REDUCE_IS_2K_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines if mp_reduce_2k can be used */ int mp_reduce_is_2k(mp_int *a) { int ix, iy, iw; mp_digit iz; if (a->used == 0) { return MP_NO; } else if (a->used == 1) { return MP_YES; } else if (a->used > 1) { iy = mp_count_bits(a); iz = 1; iw = 1; /* Test every bit from the second digit up, must be 1 */ for (ix = DIGIT_BIT; ix < iy; ix++) { if ((a->dp[iw] & iz) == 0) { return MP_NO; } iz <<= 1; if (iz > (mp_digit)MP_MASK) { ++iw; iz = 1; } } } return MP_YES; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_reduce_is_2k.c */ /* Start: bn_mp_reduce_is_2k_l.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_REDUCE_IS_2K_L_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines if reduce_2k_l can be used */ int mp_reduce_is_2k_l(mp_int *a) { int ix, iy; if (a->used == 0) { return MP_NO; } else if (a->used == 1) { return MP_YES; } else if (a->used > 1) { /* if more than half of the digits are -1 we're sold */ for (iy = ix = 0; ix < a->used; ix++) { if (a->dp[ix] == MP_MASK) { ++iy; } } return (iy >= (a->used/2)) ? MP_YES : MP_NO; } return MP_NO; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_reduce_is_2k_l.c */ /* Start: bn_mp_reduce_setup.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_REDUCE_SETUP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* pre-calculate the value required for Barrett reduction * For a given modulus "b" it calulates the value required in "a" */ int mp_reduce_setup (mp_int * a, mp_int * b) { int res; if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { return res; } return mp_div (a, b, a, NULL); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_reduce_setup.c */ /* Start: bn_mp_rshd.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_RSHD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* shift right a certain amount of digits */ void mp_rshd (mp_int * a, int b) { int x; /* if b <= 0 then ignore it */ if (b <= 0) { return; } /* if b > used then simply zero it and return */ if (a->used <= b) { mp_zero (a); return; } { mp_digit *bottom, *top; /* shift the digits down */ /* bottom */ bottom = a->dp; /* top [offset into digits] */ top = a->dp + b; /* this is implemented as a sliding window where * the window is b-digits long and digits from * the top of the window are copied to the bottom * * e.g. b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> /\ | ----> \-------------------/ ----> */ for (x = 0; x < (a->used - b); x++) { *bottom++ = *top++; } /* zero the top digits */ for (; x < a->used; x++) { *bottom++ = 0; } } /* remove excess digits */ a->used -= b; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_rshd.c */ /* Start: bn_mp_set.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SET_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* set to a digit */ void mp_set (mp_int * a, mp_digit b) { mp_zero (a); a->dp[0] = b & MP_MASK; a->used = (a->dp[0] != 0) ? 1 : 0; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_set.c */ /* Start: bn_mp_set_int.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SET_INT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* set a 32-bit const */ int mp_set_int (mp_int * a, unsigned long b) { int x, res; mp_zero (a); /* set four bits at a time */ for (x = 0; x < 8; x++) { /* shift the number up four bits */ if ((res = mp_mul_2d (a, 4, a)) != MP_OKAY) { return res; } /* OR in the top four bits of the source */ a->dp[0] |= (b >> 28) & 15; /* shift the source up to the next four bits */ b <<= 4; /* ensure that digits are not clamped off */ a->used += 1; } mp_clamp (a); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_set_int.c */ /* Start: bn_mp_set_long.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SET_LONG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* set a platform dependent unsigned long int */ MP_SET_XLONG(mp_set_long, unsigned long) #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_set_long.c */ /* Start: bn_mp_set_long_long.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SET_LONG_LONG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* set a platform dependent unsigned long long int */ MP_SET_XLONG(mp_set_long_long, unsigned long long) #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_set_long_long.c */ /* Start: bn_mp_shrink.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SHRINK_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* shrink a bignum */ int mp_shrink (mp_int * a) { mp_digit *tmp; int used = 1; if(a->used > 0) { used = a->used; } if (a->alloc != used) { if ((tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * used)) == NULL) { return MP_MEM; } a->dp = tmp; a->alloc = used; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_shrink.c */ /* Start: bn_mp_signed_bin_size.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SIGNED_BIN_SIZE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* get the size for an signed equivalent */ int mp_signed_bin_size (mp_int * a) { return 1 + mp_unsigned_bin_size (a); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_signed_bin_size.c */ /* Start: bn_mp_sqr.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SQR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes b = a*a */ int mp_sqr (mp_int * a, mp_int * b) { int res; #ifdef BN_MP_TOOM_SQR_C /* use Toom-Cook? */ if (a->used >= TOOM_SQR_CUTOFF) { res = mp_toom_sqr(a, b); /* Karatsuba? */ } else #endif #ifdef BN_MP_KARATSUBA_SQR_C if (a->used >= KARATSUBA_SQR_CUTOFF) { res = mp_karatsuba_sqr (a, b); } else #endif { #ifdef BN_FAST_S_MP_SQR_C /* can we use the fast comba multiplier? */ if ((((a->used * 2) + 1) < MP_WARRAY) && (a->used < (1 << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) - 1)))) { res = fast_s_mp_sqr (a, b); } else #endif { #ifdef BN_S_MP_SQR_C res = s_mp_sqr (a, b); #else res = MP_VAL; #endif } } b->sign = MP_ZPOS; return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_sqr.c */ /* Start: bn_mp_sqrmod.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SQRMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* c = a * a (mod b) */ int mp_sqrmod (mp_int * a, mp_int * b, mp_int * c) { int res; mp_int t; if ((res = mp_init (&t)) != MP_OKAY) { return res; } if ((res = mp_sqr (a, &t)) != MP_OKAY) { mp_clear (&t); return res; } res = mp_mod (&t, b, c); mp_clear (&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_sqrmod.c */ /* Start: bn_mp_sqrt.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SQRT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* this function is less generic than mp_n_root, simpler and faster */ int mp_sqrt(mp_int *arg, mp_int *ret) { int res; mp_int t1,t2; /* must be positive */ if (arg->sign == MP_NEG) { return MP_VAL; } /* easy out */ if (mp_iszero(arg) == MP_YES) { mp_zero(ret); return MP_OKAY; } if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) { return res; } if ((res = mp_init(&t2)) != MP_OKAY) { goto E2; } /* First approx. (not very bad for large arg) */ mp_rshd (&t1,t1.used/2); /* t1 > 0 */ if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { goto E1; } if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { goto E1; } if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { goto E1; } /* And now t1 > sqrt(arg) */ do { if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { goto E1; } if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { goto E1; } if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { goto E1; } /* t1 >= sqrt(arg) >= t2 at this point */ } while (mp_cmp_mag(&t1,&t2) == MP_GT); mp_exch(&t1,ret); E1: mp_clear(&t2); E2: mp_clear(&t1); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_sqrt.c */ /* Start: bn_mp_sqrtmod_prime.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SQRTMOD_PRIME_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library is free for all purposes without any express * guarantee it works. */ /* Tonelli-Shanks algorithm * https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm * https://gmplib.org/list-archives/gmp-discuss/2013-April/005300.html * */ int mp_sqrtmod_prime(mp_int *n, mp_int *prime, mp_int *ret) { int res, legendre; mp_int t1, C, Q, S, Z, M, T, R, two; mp_digit i; /* first handle the simple cases */ if (mp_cmp_d(n, 0) == MP_EQ) { mp_zero(ret); return MP_OKAY; } if (mp_cmp_d(prime, 2) == MP_EQ) return MP_VAL; /* prime must be odd */ if ((res = mp_jacobi(n, prime, &legendre)) != MP_OKAY) return res; if (legendre == -1) return MP_VAL; /* quadratic non-residue mod prime */ if ((res = mp_init_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL)) != MP_OKAY) { return res; } /* SPECIAL CASE: if prime mod 4 == 3 * compute directly: res = n^(prime+1)/4 mod prime * Handbook of Applied Cryptography algorithm 3.36 */ if ((res = mp_mod_d(prime, 4, &i)) != MP_OKAY) goto cleanup; if (i == 3) { if ((res = mp_add_d(prime, 1, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_exptmod(n, &t1, prime, ret)) != MP_OKAY) goto cleanup; res = MP_OKAY; goto cleanup; } /* NOW: Tonelli-Shanks algorithm */ /* factor out powers of 2 from prime-1, defining Q and S as: prime-1 = Q*2^S */ if ((res = mp_copy(prime, &Q)) != MP_OKAY) goto cleanup; if ((res = mp_sub_d(&Q, 1, &Q)) != MP_OKAY) goto cleanup; /* Q = prime - 1 */ mp_zero(&S); /* S = 0 */ while (mp_iseven(&Q) != MP_NO) { if ((res = mp_div_2(&Q, &Q)) != MP_OKAY) goto cleanup; /* Q = Q / 2 */ if ((res = mp_add_d(&S, 1, &S)) != MP_OKAY) goto cleanup; /* S = S + 1 */ } /* find a Z such that the Legendre symbol (Z|prime) == -1 */ if ((res = mp_set_int(&Z, 2)) != MP_OKAY) goto cleanup; /* Z = 2 */ while(1) { if ((res = mp_jacobi(&Z, prime, &legendre)) != MP_OKAY) goto cleanup; if (legendre == -1) break; if ((res = mp_add_d(&Z, 1, &Z)) != MP_OKAY) goto cleanup; /* Z = Z + 1 */ } if ((res = mp_exptmod(&Z, &Q, prime, &C)) != MP_OKAY) goto cleanup; /* C = Z ^ Q mod prime */ if ((res = mp_add_d(&Q, 1, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; /* t1 = (Q + 1) / 2 */ if ((res = mp_exptmod(n, &t1, prime, &R)) != MP_OKAY) goto cleanup; /* R = n ^ ((Q + 1) / 2) mod prime */ if ((res = mp_exptmod(n, &Q, prime, &T)) != MP_OKAY) goto cleanup; /* T = n ^ Q mod prime */ if ((res = mp_copy(&S, &M)) != MP_OKAY) goto cleanup; /* M = S */ if ((res = mp_set_int(&two, 2)) != MP_OKAY) goto cleanup; res = MP_VAL; while (1) { if ((res = mp_copy(&T, &t1)) != MP_OKAY) goto cleanup; i = 0; while (1) { if (mp_cmp_d(&t1, 1) == MP_EQ) break; if ((res = mp_exptmod(&t1, &two, prime, &t1)) != MP_OKAY) goto cleanup; i++; } if (i == 0) { if ((res = mp_copy(&R, ret)) != MP_OKAY) goto cleanup; res = MP_OKAY; goto cleanup; } if ((res = mp_sub_d(&M, i, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_sub_d(&t1, 1, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_exptmod(&two, &t1, prime, &t1)) != MP_OKAY) goto cleanup; /* t1 = 2 ^ (M - i - 1) */ if ((res = mp_exptmod(&C, &t1, prime, &t1)) != MP_OKAY) goto cleanup; /* t1 = C ^ (2 ^ (M - i - 1)) mod prime */ if ((res = mp_sqrmod(&t1, prime, &C)) != MP_OKAY) goto cleanup; /* C = (t1 * t1) mod prime */ if ((res = mp_mulmod(&R, &t1, prime, &R)) != MP_OKAY) goto cleanup; /* R = (R * t1) mod prime */ if ((res = mp_mulmod(&T, &C, prime, &T)) != MP_OKAY) goto cleanup; /* T = (T * C) mod prime */ mp_set(&M, i); /* M = i */ } cleanup: mp_clear_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL); return res; } #endif /* End: bn_mp_sqrtmod_prime.c */ /* Start: bn_mp_sub.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SUB_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* high level subtraction (handles signs) */ int mp_sub (mp_int * a, mp_int * b, mp_int * c) { int sa, sb, res; sa = a->sign; sb = b->sign; if (sa != sb) { /* subtract a negative from a positive, OR */ /* subtract a positive from a negative. */ /* In either case, ADD their magnitudes, */ /* and use the sign of the first number. */ c->sign = sa; res = s_mp_add (a, b, c); } else { /* subtract a positive from a positive, OR */ /* subtract a negative from a negative. */ /* First, take the difference between their */ /* magnitudes, then... */ if (mp_cmp_mag (a, b) != MP_LT) { /* Copy the sign from the first */ c->sign = sa; /* The first has a larger or equal magnitude */ res = s_mp_sub (a, b, c); } else { /* The result has the *opposite* sign from */ /* the first number. */ c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; /* The second has a larger magnitude */ res = s_mp_sub (b, a, c); } } return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_sub.c */ /* Start: bn_mp_sub_d.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SUB_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* single digit subtraction */ int mp_sub_d (mp_int * a, mp_digit b, mp_int * c) { mp_digit *tmpa, *tmpc, mu; int res, ix, oldused; /* grow c as required */ if (c->alloc < (a->used + 1)) { if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { return res; } } /* if a is negative just do an unsigned * addition [with fudged signs] */ if (a->sign == MP_NEG) { a->sign = MP_ZPOS; res = mp_add_d(a, b, c); a->sign = c->sign = MP_NEG; /* clamp */ mp_clamp(c); return res; } /* setup regs */ oldused = c->used; tmpa = a->dp; tmpc = c->dp; /* if a <= b simply fix the single digit */ if (((a->used == 1) && (a->dp[0] <= b)) || (a->used == 0)) { if (a->used == 1) { *tmpc++ = b - *tmpa; } else { *tmpc++ = b; } ix = 1; /* negative/1digit */ c->sign = MP_NEG; c->used = 1; } else { /* positive/size */ c->sign = MP_ZPOS; c->used = a->used; /* subtract first digit */ *tmpc = *tmpa++ - b; mu = *tmpc >> ((sizeof(mp_digit) * CHAR_BIT) - 1); *tmpc++ &= MP_MASK; /* handle rest of the digits */ for (ix = 1; ix < a->used; ix++) { *tmpc = *tmpa++ - mu; mu = *tmpc >> ((sizeof(mp_digit) * CHAR_BIT) - 1); *tmpc++ &= MP_MASK; } } /* zero excess digits */ while (ix++ < oldused) { *tmpc++ = 0; } mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_sub_d.c */ /* Start: bn_mp_submod.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_SUBMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* d = a - b (mod c) */ int mp_submod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) { int res; mp_int t; if ((res = mp_init (&t)) != MP_OKAY) { return res; } if ((res = mp_sub (a, b, &t)) != MP_OKAY) { mp_clear (&t); return res; } res = mp_mod (&t, c, d); mp_clear (&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_submod.c */ /* Start: bn_mp_to_signed_bin.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_TO_SIGNED_BIN_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* store in signed [big endian] format */ int mp_to_signed_bin (mp_int * a, unsigned char *b) { int res; if ((res = mp_to_unsigned_bin (a, b + 1)) != MP_OKAY) { return res; } b[0] = (a->sign == MP_ZPOS) ? (unsigned char)0 : (unsigned char)1; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_to_signed_bin.c */ /* Start: bn_mp_to_signed_bin_n.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_TO_SIGNED_BIN_N_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* store in signed [big endian] format */ int mp_to_signed_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen) { if (*outlen < (unsigned long)mp_signed_bin_size(a)) { return MP_VAL; } *outlen = mp_signed_bin_size(a); return mp_to_signed_bin(a, b); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_to_signed_bin_n.c */ /* Start: bn_mp_to_unsigned_bin.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_TO_UNSIGNED_BIN_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* store in unsigned [big endian] format */ int mp_to_unsigned_bin (mp_int * a, unsigned char *b) { int x, res; mp_int t; if ((res = mp_init_copy (&t, a)) != MP_OKAY) { return res; } x = 0; while (mp_iszero (&t) == MP_NO) { #ifndef MP_8BIT b[x++] = (unsigned char) (t.dp[0] & 255); #else b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); #endif if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { mp_clear (&t); return res; } } bn_reverse (b, x); mp_clear (&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_to_unsigned_bin.c */ /* Start: bn_mp_to_unsigned_bin_n.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_TO_UNSIGNED_BIN_N_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* store in unsigned [big endian] format */ int mp_to_unsigned_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen) { if (*outlen < (unsigned long)mp_unsigned_bin_size(a)) { return MP_VAL; } *outlen = mp_unsigned_bin_size(a); return mp_to_unsigned_bin(a, b); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_to_unsigned_bin_n.c */ /* Start: bn_mp_toom_mul.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_TOOM_MUL_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* multiplication using the Toom-Cook 3-way algorithm * * Much more complicated than Karatsuba but has a lower * asymptotic running time of O(N**1.464). This algorithm is * only particularly useful on VERY large inputs * (we're talking 1000s of digits here...). */ int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c) { mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2; int res, B; /* init temps */ if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &b0, &b1, &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) { return res; } /* B */ B = MIN(a->used, b->used) / 3; /* a = a2 * B**2 + a1 * B + a0 */ if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { goto ERR; } if ((res = mp_copy(a, &a1)) != MP_OKAY) { goto ERR; } mp_rshd(&a1, B); if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) { goto ERR; } if ((res = mp_copy(a, &a2)) != MP_OKAY) { goto ERR; } mp_rshd(&a2, B*2); /* b = b2 * B**2 + b1 * B + b0 */ if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) { goto ERR; } if ((res = mp_copy(b, &b1)) != MP_OKAY) { goto ERR; } mp_rshd(&b1, B); (void)mp_mod_2d(&b1, DIGIT_BIT * B, &b1); if ((res = mp_copy(b, &b2)) != MP_OKAY) { goto ERR; } mp_rshd(&b2, B*2); /* w0 = a0*b0 */ if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) { goto ERR; } /* w4 = a2 * b2 */ if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) { goto ERR; } /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */ if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) { goto ERR; } /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */ if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) { goto ERR; } /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */ if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) { goto ERR; } /* now solve the matrix 0 0 0 0 1 1 2 4 8 16 1 1 1 1 1 16 8 4 2 1 1 0 0 0 0 using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication */ /* r1 - r4 */ if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r0 */ if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { goto ERR; } /* r1/2 */ if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { goto ERR; } /* r3/2 */ if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { goto ERR; } /* r2 - r0 - r4 */ if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { goto ERR; } /* r1 - r2 */ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r2 */ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { goto ERR; } /* r1 - 8r0 */ if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { goto ERR; } /* r3 - 8r4 */ if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { goto ERR; } /* 3r2 - r1 - r3 */ if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { goto ERR; } /* r1 - r2 */ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r2 */ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { goto ERR; } /* r1/3 */ if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { goto ERR; } /* r3/3 */ if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { goto ERR; } /* at this point shift W[n] by B*n */ if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) { goto ERR; } ERR: mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &b0, &b1, &b2, &tmp1, &tmp2, NULL); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_toom_mul.c */ /* Start: bn_mp_toom_sqr.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_TOOM_SQR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* squaring using Toom-Cook 3-way algorithm */ int mp_toom_sqr(mp_int *a, mp_int *b) { mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2; int res, B; /* init temps */ if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) { return res; } /* B */ B = a->used / 3; /* a = a2 * B**2 + a1 * B + a0 */ if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { goto ERR; } if ((res = mp_copy(a, &a1)) != MP_OKAY) { goto ERR; } mp_rshd(&a1, B); if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) { goto ERR; } if ((res = mp_copy(a, &a2)) != MP_OKAY) { goto ERR; } mp_rshd(&a2, B*2); /* w0 = a0*a0 */ if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) { goto ERR; } /* w4 = a2 * a2 */ if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) { goto ERR; } /* w1 = (a2 + 2(a1 + 2a0))**2 */ if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) { goto ERR; } /* w3 = (a0 + 2(a1 + 2a2))**2 */ if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) { goto ERR; } /* w2 = (a2 + a1 + a0)**2 */ if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) { goto ERR; } /* now solve the matrix 0 0 0 0 1 1 2 4 8 16 1 1 1 1 1 16 8 4 2 1 1 0 0 0 0 using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication. */ /* r1 - r4 */ if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r0 */ if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { goto ERR; } /* r1/2 */ if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { goto ERR; } /* r3/2 */ if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { goto ERR; } /* r2 - r0 - r4 */ if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { goto ERR; } /* r1 - r2 */ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r2 */ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { goto ERR; } /* r1 - 8r0 */ if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { goto ERR; } /* r3 - 8r4 */ if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { goto ERR; } /* 3r2 - r1 - r3 */ if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { goto ERR; } /* r1 - r2 */ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r2 */ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { goto ERR; } /* r1/3 */ if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { goto ERR; } /* r3/3 */ if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { goto ERR; } /* at this point shift W[n] by B*n */ if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) { goto ERR; } ERR: mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_toom_sqr.c */ /* Start: bn_mp_toradix.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_TORADIX_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* stores a bignum as a ASCII string in a given radix (2..64) */ int mp_toradix (mp_int * a, char *str, int radix) { int res, digs; mp_int t; mp_digit d; char *_s = str; /* check range of the radix */ if ((radix < 2) || (radix > 64)) { return MP_VAL; } /* quick out if its zero */ if (mp_iszero(a) == MP_YES) { *str++ = '0'; *str = '\0'; return MP_OKAY; } if ((res = mp_init_copy (&t, a)) != MP_OKAY) { return res; } /* if it is negative output a - */ if (t.sign == MP_NEG) { ++_s; *str++ = '-'; t.sign = MP_ZPOS; } digs = 0; while (mp_iszero (&t) == MP_NO) { if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { mp_clear (&t); return res; } *str++ = mp_s_rmap[d]; ++digs; } /* reverse the digits of the string. In this case _s points * to the first digit [exluding the sign] of the number] */ bn_reverse ((unsigned char *)_s, digs); /* append a NULL so the string is properly terminated */ *str = '\0'; mp_clear (&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_toradix.c */ /* Start: bn_mp_toradix_n.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_TORADIX_N_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* stores a bignum as a ASCII string in a given radix (2..64) * * Stores upto maxlen-1 chars and always a NULL byte */ int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen) { int res, digs; mp_int t; mp_digit d; char *_s = str; /* check range of the maxlen, radix */ if ((maxlen < 2) || (radix < 2) || (radix > 64)) { return MP_VAL; } /* quick out if its zero */ if (mp_iszero(a) == MP_YES) { *str++ = '0'; *str = '\0'; return MP_OKAY; } if ((res = mp_init_copy (&t, a)) != MP_OKAY) { return res; } /* if it is negative output a - */ if (t.sign == MP_NEG) { /* we have to reverse our digits later... but not the - sign!! */ ++_s; /* store the flag and mark the number as positive */ *str++ = '-'; t.sign = MP_ZPOS; /* subtract a char */ --maxlen; } digs = 0; while (mp_iszero (&t) == MP_NO) { if (--maxlen < 1) { /* no more room */ break; } if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { mp_clear (&t); return res; } *str++ = mp_s_rmap[d]; ++digs; } /* reverse the digits of the string. In this case _s points * to the first digit [exluding the sign] of the number */ bn_reverse ((unsigned char *)_s, digs); /* append a NULL so the string is properly terminated */ *str = '\0'; mp_clear (&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_toradix_n.c */ /* Start: bn_mp_unsigned_bin_size.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_UNSIGNED_BIN_SIZE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* get the size for an unsigned equivalent */ int mp_unsigned_bin_size (mp_int * a) { int size = mp_count_bits (a); return (size / 8) + (((size & 7) != 0) ? 1 : 0); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_unsigned_bin_size.c */ /* Start: bn_mp_xor.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_XOR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* XOR two ints together */ int mp_xor (mp_int * a, mp_int * b, mp_int * c) { int res, ix, px; mp_int t, *x; if (a->used > b->used) { if ((res = mp_init_copy (&t, a)) != MP_OKAY) { return res; } px = b->used; x = b; } else { if ((res = mp_init_copy (&t, b)) != MP_OKAY) { return res; } px = a->used; x = a; } for (ix = 0; ix < px; ix++) { t.dp[ix] ^= x->dp[ix]; } mp_clamp (&t); mp_exch (c, &t); mp_clear (&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_xor.c */ /* Start: bn_mp_zero.c */ #include "libtorrent/tommath_private.h" #ifdef BN_MP_ZERO_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* set to zero */ void mp_zero (mp_int * a) { int n; mp_digit *tmp; a->sign = MP_ZPOS; a->used = 0; tmp = a->dp; for (n = 0; n < a->alloc; n++) { *tmp++ = 0; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_mp_zero.c */ /* Start: bn_prime_tab.c */ #include "libtorrent/tommath_private.h" #ifdef BN_PRIME_TAB_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ const mp_digit ltm_prime_tab[] = { 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, #ifndef MP_8BIT 0x0083, 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653 #endif }; #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_prime_tab.c */ /* Start: bn_reverse.c */ #include "libtorrent/tommath_private.h" #ifdef BN_REVERSE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reverse an array, used for radix code */ void bn_reverse (unsigned char *s, int len) { int ix, iy; unsigned char t; ix = 0; iy = len - 1; while (ix < iy) { t = s[ix]; s[ix] = s[iy]; s[iy] = t; ++ix; --iy; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_reverse.c */ /* Start: bn_s_mp_add.c */ #include "libtorrent/tommath_private.h" #ifdef BN_S_MP_ADD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* low level addition, based on HAC pp.594, Algorithm 14.7 */ int s_mp_add (mp_int * a, mp_int * b, mp_int * c) { mp_int *x; int olduse, res, min, max; /* find sizes, we let |a| <= |b| which means we have to sort * them. "x" will point to the input with the most digits */ if (a->used > b->used) { min = b->used; max = a->used; x = a; } else { min = a->used; max = b->used; x = b; } /* init result */ if (c->alloc < (max + 1)) { if ((res = mp_grow (c, max + 1)) != MP_OKAY) { return res; } } /* get old used digit count and set new one */ olduse = c->used; c->used = max + 1; { mp_digit u, *tmpa, *tmpb, *tmpc; int i; /* alias for digit pointers */ /* first input */ tmpa = a->dp; /* second input */ tmpb = b->dp; /* destination */ tmpc = c->dp; /* zero the carry */ u = 0; for (i = 0; i < min; i++) { /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ *tmpc = *tmpa++ + *tmpb++ + u; /* U = carry bit of T[i] */ u = *tmpc >> ((mp_digit)DIGIT_BIT); /* take away carry bit from T[i] */ *tmpc++ &= MP_MASK; } /* now copy higher words if any, that is in A+B * if A or B has more digits add those in */ if (min != max) { for (; i < max; i++) { /* T[i] = X[i] + U */ *tmpc = x->dp[i] + u; /* U = carry bit of T[i] */ u = *tmpc >> ((mp_digit)DIGIT_BIT); /* take away carry bit from T[i] */ *tmpc++ &= MP_MASK; } } /* add carry */ *tmpc++ = u; /* clear digits above oldused */ for (i = c->used; i < olduse; i++) { *tmpc++ = 0; } } mp_clamp (c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_s_mp_add.c */ /* Start: bn_s_mp_exptmod.c */ #include "libtorrent/tommath_private.h" #ifdef BN_S_MP_EXPTMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ #ifdef MP_LOW_MEM #define TAB_SIZE 32 #else #define TAB_SIZE 256 #endif int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) { mp_int M[TAB_SIZE], res, mu; mp_digit buf; int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; int (*redux)(mp_int*,mp_int*,mp_int*); /* find window size */ x = mp_count_bits (X); if (x <= 7) { winsize = 2; } else if (x <= 36) { winsize = 3; } else if (x <= 140) { winsize = 4; } else if (x <= 450) { winsize = 5; } else if (x <= 1303) { winsize = 6; } else if (x <= 3529) { winsize = 7; } else { winsize = 8; } #ifdef MP_LOW_MEM if (winsize > 5) { winsize = 5; } #endif /* init M array */ /* init first cell */ if ((err = mp_init(&M[1])) != MP_OKAY) { return err; } /* now init the second half of the array */ for (x = 1<<(winsize-1); x < (1 << winsize); x++) { if ((err = mp_init(&M[x])) != MP_OKAY) { for (y = 1<<(winsize-1); y < x; y++) { mp_clear (&M[y]); } mp_clear(&M[1]); return err; } } /* create mu, used for Barrett reduction */ if ((err = mp_init (&mu)) != MP_OKAY) { goto LBL_M; } if (redmode == 0) { if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { goto LBL_MU; } redux = mp_reduce; } else { if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { goto LBL_MU; } redux = mp_reduce_2k_l; } /* create M table * * The M table contains powers of the base, * e.g. M[x] = G**x mod P * * The first half of the table is not * computed though accept for M[0] and M[1] */ if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { goto LBL_MU; } /* compute the value at M[1<<(winsize-1)] by squaring * M[1] (winsize-1) times */ if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; } for (x = 0; x < (winsize - 1); x++) { /* square it */ if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; } /* reduce modulo P */ if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { goto LBL_MU; } } /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) */ for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { goto LBL_MU; } if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { goto LBL_MU; } } /* setup result */ if ((err = mp_init (&res)) != MP_OKAY) { goto LBL_MU; } mp_set (&res, 1); /* set initial mode and bit cnt */ mode = 0; bitcnt = 1; buf = 0; digidx = X->used - 1; bitcpy = 0; bitbuf = 0; for (;;) { /* grab next digit as required */ if (--bitcnt == 0) { /* if digidx == -1 we are out of digits */ if (digidx == -1) { break; } /* read next digit and reset the bitcnt */ buf = X->dp[digidx--]; bitcnt = (int) DIGIT_BIT; } /* grab the next msb from the exponent */ y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; buf <<= (mp_digit)1; /* if the bit is zero and mode == 0 then we ignore it * These represent the leading zero bits before the first 1 bit * in the exponent. Technically this opt is not required but it * does lower the # of trivial squaring/reductions used */ if ((mode == 0) && (y == 0)) { continue; } /* if the bit is zero and mode == 1 then we square */ if ((mode == 1) && (y == 0)) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } continue; } /* else we add it to the window */ bitbuf |= (y << (winsize - ++bitcpy)); mode = 2; if (bitcpy == winsize) { /* ok window is filled so square as required and multiply */ /* square first */ for (x = 0; x < winsize; x++) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } } /* then multiply */ if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } /* empty window and reset */ bitcpy = 0; bitbuf = 0; mode = 1; } } /* if bits remain then square/multiply */ if ((mode == 2) && (bitcpy > 0)) { /* square then multiply if the bit is set */ for (x = 0; x < bitcpy; x++) { if ((err = mp_sqr (&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } bitbuf <<= 1; if ((bitbuf & (1 << winsize)) != 0) { /* then multiply */ if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux (&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } } } } mp_exch (&res, Y); err = MP_OKAY; LBL_RES:mp_clear (&res); LBL_MU:mp_clear (&mu); LBL_M: mp_clear(&M[1]); for (x = 1<<(winsize-1); x < (1 << winsize); x++) { mp_clear (&M[x]); } return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_s_mp_exptmod.c */ /* Start: bn_s_mp_mul_digs.c */ #include "libtorrent/tommath_private.h" #ifdef BN_S_MP_MUL_DIGS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* multiplies |a| * |b| and only computes upto digs digits of result * HAC pp. 595, Algorithm 14.12 Modified so you can control how * many digits of output are created. */ int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) { mp_int t; int res, pa, pb, ix, iy; mp_digit u; mp_word r; mp_digit tmpx, *tmpt, *tmpy; /* can we use the fast multiplier? */ if (((digs) < MP_WARRAY) && (MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { return fast_s_mp_mul_digs (a, b, c, digs); } if ((res = mp_init_size (&t, digs)) != MP_OKAY) { return res; } t.used = digs; /* compute the digits of the product directly */ pa = a->used; for (ix = 0; ix < pa; ix++) { /* set the carry to zero */ u = 0; /* limit ourselves to making digs digits of output */ pb = MIN (b->used, digs - ix); /* setup some aliases */ /* copy of the digit from a used within the nested loop */ tmpx = a->dp[ix]; /* an alias for the destination shifted ix places */ tmpt = t.dp + ix; /* an alias for the digits of b */ tmpy = b->dp; /* compute the columns of the output and propagate the carry */ for (iy = 0; iy < pb; iy++) { /* compute the column as a mp_word */ r = (mp_word)*tmpt + ((mp_word)tmpx * (mp_word)*tmpy++) + (mp_word)u; /* the new column is the lower part of the result */ *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); /* get the carry word from the result */ u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); } /* set carry if it is placed below digs */ if ((ix + iy) < digs) { *tmpt = u; } } mp_clamp (&t); mp_exch (&t, c); mp_clear (&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_s_mp_mul_digs.c */ /* Start: bn_s_mp_mul_high_digs.c */ #include "libtorrent/tommath_private.h" #ifdef BN_S_MP_MUL_HIGH_DIGS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* multiplies |a| * |b| and does not compute the lower digs digits * [meant to get the higher part of the product] */ int s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) { mp_int t; int res, pa, pb, ix, iy; mp_digit u; mp_word r; mp_digit tmpx, *tmpt, *tmpy; /* can we use the fast multiplier? */ #ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C if (((a->used + b->used + 1) < MP_WARRAY) && (MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { return fast_s_mp_mul_high_digs (a, b, c, digs); } #endif if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { return res; } t.used = a->used + b->used + 1; pa = a->used; pb = b->used; for (ix = 0; ix < pa; ix++) { /* clear the carry */ u = 0; /* left hand side of A[ix] * B[iy] */ tmpx = a->dp[ix]; /* alias to the address of where the digits will be stored */ tmpt = &(t.dp[digs]); /* alias for where to read the right hand side from */ tmpy = b->dp + (digs - ix); for (iy = digs - ix; iy < pb; iy++) { /* calculate the double precision result */ r = (mp_word)*tmpt + ((mp_word)tmpx * (mp_word)*tmpy++) + (mp_word)u; /* get the lower part */ *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); /* carry the carry */ u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); } *tmpt = u; } mp_clamp (&t); mp_exch (&t, c); mp_clear (&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_s_mp_mul_high_digs.c */ /* Start: bn_s_mp_sqr.c */ #include "libtorrent/tommath_private.h" #ifdef BN_S_MP_SQR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ int s_mp_sqr (mp_int * a, mp_int * b) { mp_int t; int res, ix, iy, pa; mp_word r; mp_digit u, tmpx, *tmpt; pa = a->used; if ((res = mp_init_size (&t, (2 * pa) + 1)) != MP_OKAY) { return res; } /* default used is maximum possible size */ t.used = (2 * pa) + 1; for (ix = 0; ix < pa; ix++) { /* first calculate the digit at 2*ix */ /* calculate double precision result */ r = (mp_word)t.dp[2*ix] + ((mp_word)a->dp[ix] * (mp_word)a->dp[ix]); /* store lower part in result */ t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); /* get the carry */ u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); /* left hand side of A[ix] * A[iy] */ tmpx = a->dp[ix]; /* alias for where to store the results */ tmpt = t.dp + ((2 * ix) + 1); for (iy = ix + 1; iy < pa; iy++) { /* first calculate the product */ r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); /* now calculate the double precision result, note we use * addition instead of *2 since it's easier to optimize */ r = ((mp_word) *tmpt) + r + r + ((mp_word) u); /* store lower part */ *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); /* get carry */ u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); } /* propagate upwards */ while (u != ((mp_digit) 0)) { r = ((mp_word) *tmpt) + ((mp_word) u); *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); } } mp_clamp (&t); mp_exch (&t, b); mp_clear (&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_s_mp_sqr.c */ /* Start: bn_s_mp_sub.c */ #include "libtorrent/tommath_private.h" #ifdef BN_S_MP_SUB_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ int s_mp_sub (mp_int * a, mp_int * b, mp_int * c) { int olduse, res, min, max; /* find sizes */ min = b->used; max = a->used; /* init result */ if (c->alloc < max) { if ((res = mp_grow (c, max)) != MP_OKAY) { return res; } } olduse = c->used; c->used = max; { mp_digit u, *tmpa, *tmpb, *tmpc; int i; /* alias for digit pointers */ tmpa = a->dp; tmpb = b->dp; tmpc = c->dp; /* set carry to zero */ u = 0; for (i = 0; i < min; i++) { /* T[i] = A[i] - B[i] - U */ *tmpc = (*tmpa++ - *tmpb++) - u; /* U = carry bit of T[i] * Note this saves performing an AND operation since * if a carry does occur it will propagate all the way to the * MSB. As a result a single shift is enough to get the carry */ u = *tmpc >> ((mp_digit)((CHAR_BIT * sizeof(mp_digit)) - 1)); /* Clear carry from T[i] */ *tmpc++ &= MP_MASK; } /* now copy higher words if any, e.g. if A has more digits than B */ for (; i < max; i++) { /* T[i] = A[i] - U */ *tmpc = *tmpa++ - u; /* U = carry bit of T[i] */ u = *tmpc >> ((mp_digit)((CHAR_BIT * sizeof(mp_digit)) - 1)); /* Clear carry from T[i] */ *tmpc++ &= MP_MASK; } /* clear digits above used (since we may not have grown result above) */ for (i = c->used; i < olduse; i++) { *tmpc++ = 0; } } mp_clamp (c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bn_s_mp_sub.c */ /* Start: bncore.c */ #include "libtorrent/tommath_private.h" #ifdef BNCORE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Known optimal configurations CPU /Compiler /MUL CUTOFF/SQR CUTOFF ------------------------------------------------------------- Intel P4 Northwood /GCC v3.4.1 / 88/ 128/LTM 0.32 ;-) AMD Athlon64 /GCC v3.4.4 / 80/ 120/LTM 0.35 */ int KARATSUBA_MUL_CUTOFF = 80, /* Min. number of digits before Karatsuba multiplication is used. */ KARATSUBA_SQR_CUTOFF = 120, /* Min. number of digits before Karatsuba squaring is used. */ TOOM_MUL_CUTOFF = 350, /* no optimal values of these are known yet so set em high */ TOOM_SQR_CUTOFF = 400; #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* End: bncore.c */ /* EOF */ libtorrent-rasterbar-1.1.13/src/natpmp.cpp000066400000000000000000000446441351156116000205510ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #if defined TORRENT_OS2 #include #endif #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/natpmp.hpp" #include "libtorrent/io.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/enum_net.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/aux_/time.hpp" #include "libtorrent/aux_/escape_string.hpp" #include //#define NATPMP_LOG #ifdef NATPMP_LOG #include #endif #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/debug.hpp" #endif using namespace libtorrent; natpmp::natpmp(io_service& ios , portmap_callback_t const& cb, log_callback_t const& lcb) : m_callback(cb) , m_log_callback(lcb) , m_currently_mapping(-1) , m_retry_count(0) , m_socket(ios) , m_send_timer(ios) , m_refresh_timer(ios) , m_next_refresh(-1) , m_disabled(false) , m_abort(false) { // unfortunately async operations rely on the storage // for this array not to be reallocated, by passing // around pointers to its elements. so reserve size for now m_mappings.reserve(10); } void natpmp::start() { mutex::scoped_lock l(m_mutex); error_code ec; address gateway = get_default_gateway(lt::get_io_service(m_socket), ec); if (ec) { char msg[200]; snprintf(msg, sizeof(msg), "failed to find default route: %s" , convert_from_native(ec.message()).c_str()); log(msg, l); disable(ec, l); return; } m_disabled = false; udp::endpoint nat_endpoint(gateway, 5351); if (nat_endpoint == m_nat_endpoint) return; m_nat_endpoint = nat_endpoint; char msg[200]; snprintf(msg, sizeof(msg), "found router at: %s" , print_address(m_nat_endpoint.address()).c_str()); log(msg, l); m_socket.open(udp::v4(), ec); if (ec) { disable(ec, l); return; } m_socket.bind(udp::endpoint(address_v4::any(), 0), ec); if (ec) { disable(ec, l); return; } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("natpmp::on_reply"); #endif m_socket.async_receive_from(boost::asio::buffer(&m_response_buffer[0] , sizeof(m_response_buffer)) , m_remote, boost::bind(&natpmp::on_reply, self(), _1, _2)); send_get_ip_address_request(l); for (std::vector::iterator i = m_mappings.begin() , end(m_mappings.end()); i != end; ++i) { if (i->protocol == none || i->action != mapping_t::action_none) continue; i->action = mapping_t::action_add; update_mapping(i - m_mappings.begin(), l); } } void natpmp::send_get_ip_address_request(mutex::scoped_lock& l) { using namespace libtorrent::detail; char buf[2]; char* out = buf; write_uint8(0, out); // NAT-PMP version write_uint8(0, out); // public IP address request opcode log("==> get public IP address", l); error_code ec; m_socket.send_to(boost::asio::buffer(buf, sizeof(buf)), m_nat_endpoint, 0, ec); } bool natpmp::get_mapping(int index, int& local_port, int& external_port, int& protocol) const { mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); if (index >= int(m_mappings.size()) || index < 0) return false; mapping_t const& m = m_mappings[index]; if (m.protocol == none) return false; local_port = m.local_port; external_port = m.external_port; protocol = m.protocol; return true; } void natpmp::log(char const* msg, mutex::scoped_lock& l) { l.unlock(); m_log_callback(msg); l.lock(); } void natpmp::disable(error_code const& ec, mutex::scoped_lock& l) { m_disabled = true; for (std::vector::iterator i = m_mappings.begin() , end(m_mappings.end()); i != end; ++i) { if (i->protocol == none) continue; int const proto = i->protocol; i->protocol = none; int index = i - m_mappings.begin(); l.unlock(); m_callback(index, address(), 0, proto, ec); l.lock(); } close_impl(l); } void natpmp::delete_mapping(int index) { mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); if (index >= int(m_mappings.size()) || index < 0) return; mapping_t& m = m_mappings[index]; if (m.protocol == none) return; if (!m.map_sent) { m.action = mapping_t::action_none; m.protocol = none; return; } m.action = mapping_t::action_delete; update_mapping(index, l); } int natpmp::add_mapping(protocol_type p, int external_port, int local_port) { mutex::scoped_lock l(m_mutex); if (m_disabled) return -1; std::vector::iterator i = std::find_if(m_mappings.begin() , m_mappings.end(), boost::bind(&mapping_t::protocol, _1) == int(none)); if (i == m_mappings.end()) { m_mappings.push_back(mapping_t()); i = m_mappings.end() - 1; } i->protocol = p; i->external_port = external_port; i->local_port = local_port; i->action = mapping_t::action_add; int mapping_index = i - m_mappings.begin(); #ifdef NATPMP_LOG time_point now = aux::time_now(); for (std::vector::iterator m = m_mappings.begin() , end(m_mappings.end()); m != end; ++m) { std::cout << " ADD MAPPING: " << mapping_index << " [ " "proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp") << " port: " << i->external_port << " local-port: " << i->local_port << " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete") << " ttl: " << total_seconds(i->expires - now) << " ]" << std::endl; } #endif update_mapping(mapping_index, l); return mapping_index; } void natpmp::try_next_mapping(int i, mutex::scoped_lock& l) { #ifdef NATPMP_LOG time_point now = aux::time_now(); for (std::vector::iterator m = m_mappings.begin() , end(m_mappings.end()); m != end; ++m) { std::cout << " " << (m - m_mappings.begin()) << " [ " "proto: " << (m->protocol == none ? "none" : m->protocol == tcp ? "tcp" : "udp") << " port: " << m->external_port << " local-port: " << m->local_port << " action: " << (m->action == mapping_t::action_none ? "none" : m->action == mapping_t::action_add ? "add" : "delete") << " ttl: " << total_seconds(m->expires - now) << " ]" << std::endl; } #endif if (i < int(m_mappings.size()) - 1) { update_mapping(i + 1, l); return; } std::vector::iterator m = std::find_if( m_mappings.begin(), m_mappings.end() , boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none) && boost::bind(&mapping_t::protocol, _1) != int(none)); if (m == m_mappings.end()) { if (m_abort) { error_code ec; m_send_timer.cancel(ec); m_socket.close(ec); } #ifdef NATPMP_LOG std::cout << " done" << (m_abort?" shutting down":"") << std::endl; #endif return; } #ifdef NATPMP_LOG std::cout << " updating " << (m - m_mappings.begin()) << std::endl; #endif update_mapping(m - m_mappings.begin(), l); } void natpmp::update_mapping(int i, mutex::scoped_lock& l) { if (i == int(m_mappings.size())) { if (m_abort) { error_code ec; m_send_timer.cancel(ec); m_socket.close(ec); } #ifdef NATPMP_LOG std::cout << " done" << (m_abort?" shutting down":"") << std::endl; #endif return; } natpmp::mapping_t& m = m_mappings[i]; if (m.action == mapping_t::action_none || m.protocol == none) { try_next_mapping(i, l); return; } if (m_currently_mapping == -1) { // the socket is not currently in use // send out a mapping request m_retry_count = 0; send_map_request(i, l); } } void natpmp::send_map_request(int i, mutex::scoped_lock& l) { using namespace libtorrent::detail; TORRENT_ASSERT(m_currently_mapping == -1 || m_currently_mapping == i); m_currently_mapping = i; mapping_t& m = m_mappings[i]; TORRENT_ASSERT(m.action != mapping_t::action_none); char buf[12]; char* out = buf; write_uint8(0, out); // NAT-PMP version write_uint8(m.protocol, out); // map "protocol" write_uint16(0, out); // reserved write_uint16(m.local_port, out); // private port write_uint16(m.external_port, out); // requested public port int ttl = m.action == mapping_t::action_add ? 3600 : 0; write_uint32(ttl, out); // port mapping lifetime char msg[200]; snprintf(msg, sizeof(msg), "==> port map [ mapping: %d action: %s" " proto: %s local: %u external: %u ttl: %u ]" , i, m.action == mapping_t::action_add ? "add" : "delete" , m.protocol == udp ? "udp" : "tcp" , m.local_port, m.external_port, ttl); log(msg, l); error_code ec; m_socket.send_to(boost::asio::buffer(buf, sizeof(buf)), m_nat_endpoint, 0, ec); m.map_sent = true; m.outstanding_request = true; if (m_abort) { // when we're shutting down, ignore the // responses and just remove all mappings // immediately m_currently_mapping = -1; m.action = mapping_t::action_none; try_next_mapping(i, l); } else { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("natpmp::resend_request"); #endif // linear back-off instead of exponential ++m_retry_count; m_send_timer.expires_from_now(milliseconds(250 * m_retry_count), ec); m_send_timer.async_wait(boost::bind(&natpmp::resend_request, self(), i, _1)); } } void natpmp::resend_request(int i, error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("natpmp::resend_request"); #endif if (e) return; mutex::scoped_lock l(m_mutex); if (m_currently_mapping != i) return; // if we're shutting down, don't retry, just move on // to the next mapping if (m_retry_count >= 9 || m_abort) { m_currently_mapping = -1; m_mappings[i].action = mapping_t::action_none; // try again in two hours m_mappings[i].expires = aux::time_now() + hours(2); try_next_mapping(i, l); return; } send_map_request(i, l); } void natpmp::on_reply(error_code const& e , std::size_t bytes_transferred) { mutex::scoped_lock l(m_mutex); #if defined TORRENT_ASIO_DEBUGGING complete_async("natpmp::on_reply"); #endif using namespace libtorrent::detail; if (e) { char msg[200]; snprintf(msg, sizeof(msg), "error on receiving reply: %s" , convert_from_native(e.message()).c_str()); log(msg, l); return; } if (m_abort) return; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("natpmp::on_reply"); #endif // make a copy of the response packet buffer // to avoid overwriting it in the next receive call char msg_buf[sizeof(m_response_buffer)]; memcpy(msg_buf, m_response_buffer, bytes_transferred); m_socket.async_receive_from(boost::asio::buffer(&m_response_buffer[0] , sizeof(m_response_buffer)) , m_remote, boost::bind(&natpmp::on_reply, self(), _1, _2)); // simulate packet loss /* if ((random() % 2) == 0) { log(" simulating drop", l); return; } */ if (m_remote != m_nat_endpoint) { char msg[200]; snprintf(msg, sizeof(msg), "received packet from wrong IP: %s" , print_endpoint(m_remote).c_str()); log(msg, l); return; } error_code ec; m_send_timer.cancel(ec); if (bytes_transferred < 12) { char msg[200]; snprintf(msg, sizeof(msg), "received packet of invalid size: %d", int(bytes_transferred)); log(msg, l); return; } char* in = msg_buf; int version = read_uint8(in); int cmd = read_uint8(in); int result = read_uint16(in); int time = read_uint32(in); if (cmd == 128) { // public IP request response m_external_ip = read_v4_address(in); char msg[200]; snprintf(msg, sizeof(msg), "<== public IP address [ %s ]", print_address(m_external_ip).c_str()); log(msg, l); return; } if (bytes_transferred != 16) { char msg[200]; snprintf(msg, sizeof(msg), "received packet of invalid size: %d", int(bytes_transferred)); log(msg, l); return; } int private_port = read_uint16(in); int public_port = read_uint16(in); int lifetime = read_uint32(in); (void)time; // to remove warning int protocol = (cmd - 128 == 1)?udp:tcp; char msg[200]; int num_chars = snprintf(msg, sizeof(msg), "<== port map [" " protocol: %s local: %u external: %u ttl: %u ]" , (cmd - 128 == 1 ? "udp" : "tcp") , private_port, public_port, lifetime); if (version != 0) { snprintf(msg + num_chars, sizeof(msg) - num_chars, "unexpected version: %u" , version); log(msg, l); } mapping_t* m = 0; int index = -1; for (std::vector::iterator i = m_mappings.begin() , end(m_mappings.end()); i != end; ++i) { if (private_port != i->local_port) continue; if (protocol != i->protocol) continue; if (!i->map_sent) continue; if (!i->outstanding_request) continue; m = &*i; index = i - m_mappings.begin(); break; } if (m == 0) { snprintf(msg + num_chars, sizeof(msg) - num_chars, " not found in map table"); log(msg, l); return; } m->outstanding_request = false; log(msg, l); if (public_port == 0 || lifetime == 0) { // this means the mapping was // successfully closed m->protocol = none; } else { m->expires = aux::time_now() + seconds(int(lifetime * 0.7f)); m->external_port = public_port; } if (result != 0) { // TODO: 3 it would be nice to have a separate NAT-PMP error category errors::error_code_enum errors[] = { errors::unsupported_protocol_version, errors::natpmp_not_authorized, errors::network_failure, errors::no_resources, errors::unsupported_opcode, }; errors::error_code_enum ev = errors::no_error; if (result >= 1 && result <= 5) ev = errors[result - 1]; m->expires = aux::time_now() + hours(2); int const proto = m->protocol; l.unlock(); m_callback(index, address(), 0, proto, ev); l.lock(); } else if (m->action == mapping_t::action_add) { int const proto = m->protocol; l.unlock(); m_callback(index, m_external_ip, m->external_port, proto , errors::no_error); l.lock(); } m_currently_mapping = -1; m->action = mapping_t::action_none; m_send_timer.cancel(ec); update_expiration_timer(l); try_next_mapping(index, l); } void natpmp::update_expiration_timer(mutex::scoped_lock& l) { if (m_abort) return; time_point now = aux::time_now() + milliseconds(100); #ifdef NATPMP_LOG std::cout << time_now_string() << " update_expiration_timer " << std::endl; for (std::vector::iterator i = m_mappings.begin() , end(m_mappings.end()); i != end; ++i) { std::cout << " " << (i - m_mappings.begin()) << " [ " "proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp") << " port: " << i->external_port << " local-port: " << i->local_port << " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete") << " ttl: " << total_seconds(i->expires - now) << " ]" << std::endl; } #endif time_point min_expire = now + seconds(3600); int min_index = -1; for (std::vector::iterator i = m_mappings.begin() , end(m_mappings.end()); i != end; ++i) { if (i->protocol == none || i->action != mapping_t::action_none) continue; int index = i - m_mappings.begin(); if (i->expires < now) { char msg[200]; snprintf(msg, sizeof(msg), "mapping %u expired", index); log(msg, l); i->action = mapping_t::action_add; if (m_next_refresh == index) m_next_refresh = -1; update_mapping(index, l); } else if (i->expires < min_expire) { min_expire = i->expires; min_index = index; } } // this is already the mapping we're waiting for if (m_next_refresh == min_index) return; if (min_index >= 0) { #ifdef NATPMP_LOG std::cout << time_now_string() << " next expiration [" " i: " << min_index << " ttl: " << total_seconds(min_expire - aux::time_now()) << " ]" << std::endl; #endif error_code ec; if (m_next_refresh >= 0) m_refresh_timer.cancel(ec); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("natpmp::mapping_expired"); #endif m_refresh_timer.expires_from_now(min_expire - now, ec); m_refresh_timer.async_wait(boost::bind(&natpmp::mapping_expired, self(), _1, min_index)); m_next_refresh = min_index; } } void natpmp::mapping_expired(error_code const& e, int i) { #if defined TORRENT_ASIO_DEBUGGING complete_async("natpmp::mapping_expired"); #endif if (e) return; mutex::scoped_lock l(m_mutex); char msg[200]; snprintf(msg, sizeof(msg), "mapping %u expired", i); log(msg, l); m_mappings[i].action = mapping_t::action_add; if (m_next_refresh == i) m_next_refresh = -1; update_mapping(i, l); } void natpmp::close() { mutex::scoped_lock l(m_mutex); close_impl(l); } void natpmp::close_impl(mutex::scoped_lock& l) { m_abort = true; log("closing", l); #ifdef NATPMP_LOG std::cout << time_now_string() << " close" << std::endl; time_point now = aux::time_now(); #endif if (m_disabled) return; for (std::vector::iterator i = m_mappings.begin() , end(m_mappings.end()); i != end; ++i) { #ifdef NATPMP_LOG std::cout << " " << (i - m_mappings.begin()) << " [ " "proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp") << " port: " << i->external_port << " local-port: " << i->local_port << " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete") << " ttl: " << total_seconds(i->expires - now) << " ]" << std::endl; #endif if (i->protocol == none) continue; i->action = mapping_t::action_delete; } error_code ec; m_refresh_timer.cancel(ec); m_currently_mapping = -1; update_mapping(0, l); } libtorrent-rasterbar-1.1.13/src/packet_buffer.cpp000066400000000000000000000132021351156116000220340ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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 // free and calloc #include // for bad_alloc #include "libtorrent/packet_buffer.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/invariant_check.hpp" namespace libtorrent { bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs , boost::uint32_t mask); packet_buffer_impl::packet_buffer_impl() : m_storage(0) , m_capacity(0) , m_size(0) , m_first(0) , m_last(0) {} #if TORRENT_USE_INVARIANT_CHECKS void packet_buffer_impl::check_invariant() const { int count = 0; for (int i = 0; i < int(m_capacity); ++i) { count += m_storage[i] ? 1 : 0; } TORRENT_ASSERT(count == int(m_size)); } #endif packet_buffer_impl::~packet_buffer_impl() { free(m_storage); } void* packet_buffer_impl::insert(index_type idx, void* value) { INVARIANT_CHECK; TORRENT_ASSERT_VAL(idx <= 0xffff, idx); // you're not allowed to insert NULLs! TORRENT_ASSERT(value); if (value == 0) return remove(idx); if (m_size != 0) { if (compare_less_wrap(idx, m_first, 0xffff)) { // Index comes before m_first. If we have room, we can simply // adjust m_first backward. std::size_t free_space = 0; for (index_type i = (m_first - 1) & (m_capacity - 1); i != (m_first & (m_capacity - 1)); i = (i - 1) & (m_capacity - 1)) { if (m_storage[i & (m_capacity - 1)]) break; ++free_space; } if (((m_first - idx) & 0xffff) > free_space) reserve(((m_first - idx) & 0xffff) + m_capacity - free_space); m_first = idx; } else if (idx >= m_first + m_capacity) { reserve(idx - m_first + 1); } else if (idx < m_first) { // We have wrapped. if (idx >= ((m_first + m_capacity) & 0xffff) && m_capacity < 0xffff) { reserve(m_capacity + (idx + 1 - ((m_first + m_capacity) & 0xffff))); } } if (compare_less_wrap(m_last, (idx + 1) & 0xffff, 0xffff)) m_last = (idx + 1) & 0xffff; } else { m_first = idx; m_last = (idx + 1) & 0xffff; } if (m_capacity == 0) reserve(16); void* old_value = m_storage[idx & (m_capacity - 1)]; m_storage[idx & (m_capacity - 1)] = value; if (m_size == 0) m_first = idx; // if we're just replacing an old value, the number // of elements in the buffer doesn't actually increase if (old_value == 0) ++m_size; TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first); return old_value; } void* packet_buffer_impl::at(index_type idx) const { INVARIANT_CHECK; if (idx >= m_first + m_capacity) return 0; if (compare_less_wrap(idx, m_first, 0xffff)) { return 0; } const int mask = (m_capacity - 1); return m_storage[idx & mask]; } void packet_buffer_impl::reserve(std::size_t size) { INVARIANT_CHECK; TORRENT_ASSERT_VAL(size <= 0xffff, size); std::size_t new_size = m_capacity == 0 ? 16 : m_capacity; while (new_size < size) new_size <<= 1; void** new_storage = static_cast(malloc(sizeof(void*) * new_size)); #ifndef BOOST_NO_EXCEPTIONS if (new_storage == NULL) throw std::bad_alloc(); #endif for (index_type i = 0; i < new_size; ++i) new_storage[i] = 0; for (index_type i = m_first; i < (m_first + m_capacity); ++i) new_storage[i & (new_size - 1)] = m_storage[i & (m_capacity - 1)]; free(m_storage); m_storage = new_storage; m_capacity = new_size; } void* packet_buffer_impl::remove(index_type idx) { INVARIANT_CHECK; // TODO: use compare_less_wrap for this comparison as well if (idx >= m_first + m_capacity) return 0; if (compare_less_wrap(idx, m_first, 0xffff)) return 0; const int mask = (m_capacity - 1); void* old_value = m_storage[idx & mask]; m_storage[idx & mask] = 0; if (old_value) { --m_size; if (m_size == 0) m_last = m_first; } if (idx == m_first && m_size != 0) { ++m_first; for (boost::uint32_t i = 0; i < m_capacity; ++i, ++m_first) if (m_storage[m_first & mask]) break; m_first &= 0xffff; } if (((idx + 1) & 0xffff) == m_last && m_size != 0) { --m_last; for (boost::uint32_t i = 0; i < m_capacity; ++i, --m_last) if (m_storage[m_last & mask]) break; ++m_last; m_last &= 0xffff; } TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first); return old_value; } } libtorrent-rasterbar-1.1.13/src/parse_url.cpp000066400000000000000000000073151351156116000212400ustar00rootroot00000000000000/* Copyright (c) 2008-2018, 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/parse_url.hpp" #include namespace libtorrent { // returns protocol, auth, hostname, port, path boost::tuple parse_url_components(std::string url, error_code& ec) { std::string hostname; // hostname only std::string auth; // user:pass std::string protocol; // http or https for instance int port = -1; std::string::iterator at; std::string::iterator colon; std::string::iterator port_pos; // PARSE URL std::string::iterator start = url.begin(); // remove white spaces in front of the url while (start != url.end() && is_space(*start)) ++start; std::string::iterator end = std::find(url.begin(), url.end(), ':'); protocol.assign(start, end); if (end == url.end()) { ec = errors::unsupported_url_protocol; goto exit; } ++end; if (end == url.end() || *end != '/') { ec = errors::unsupported_url_protocol; goto exit; } ++end; if (end == url.end() || *end != '/') { ec = errors::unsupported_url_protocol; goto exit; } ++end; start = end; at = std::find(start, url.end(), '@'); colon = std::find(start, url.end(), ':'); end = std::find(start, url.end(), '/'); if (at != url.end() && colon != url.end() && colon < at && at < end) { auth.assign(start, at); start = at; ++start; } // this is for IPv6 addresses if (start != url.end() && *start == '[') { port_pos = std::find(start, url.end(), ']'); if (port_pos == url.end()) { ec = errors::expected_close_bracket_in_address; goto exit; } // strip the brackets hostname.assign(start + 1, port_pos); port_pos = std::find(port_pos, url.end(), ':'); } else { port_pos = std::find(start, url.end(), ':'); if (port_pos < end) hostname.assign(start, port_pos); else hostname.assign(start, end); } if (port_pos < end) { ++port_pos; for (std::string::iterator i = port_pos; i < end; ++i) { if (is_digit(*i)) continue; ec = errors::invalid_port; goto exit; } port = std::atoi(std::string(port_pos, end).c_str()); } start = end; exit: return boost::make_tuple(protocol, auth, hostname, port , std::string(start, url.end())); } } libtorrent-rasterbar-1.1.13/src/part_file.cpp000066400000000000000000000303521351156116000212060ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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 part_file file format is an array of piece sized blocks with a simple header. For a given number of pieces, the header has a fixed size. The header size is rounded up to an even multiple of 1024, in an attempt at improving disk I/O performance by aligning reads and writes to clusters on the drive. This is the file header format. All values are stored big endian on disk. // the size of the torrent (and can be used to calculate the size // of the file header) uint32_t num_pieces; // the number of bytes in each piece. This determines the size of // each slot in the part file. This is typically an even power of 2, // but it is not guaranteed to be. uint32_t piece_size; // this is an array specifying which slots a particular piece resides in, // A value of 0xffffffff (-1 if you will) means the piece is not in the part_file // Any other value means the piece resides in the slot with that index uint32_t piece[num_pieces]; // unused, n is defined as the number to align the size of this // header to an even multiple of 1024 bytes. uint8_t padding[n]; */ #include "libtorrent/part_file.hpp" #include "libtorrent/io.hpp" #include "libtorrent/assert.hpp" #include #ifdef TORRENT_USE_VALGRIND #include #endif namespace { // round up to even kilobyte int round_up(int n) { return (n + 1023) & ~0x3ff; } } namespace libtorrent { part_file::part_file(std::string const& path, std::string const& name , int num_pieces, int piece_size) : m_path(path) , m_name(name) , m_num_allocated(0) , m_max_pieces(num_pieces) , m_piece_size(piece_size) , m_header_size(round_up((2 + num_pieces) * 4)) , m_dirty_metadata(false) { TORRENT_ASSERT(num_pieces > 0); TORRENT_ASSERT(m_piece_size > 0); error_code ec; std::string fn = combine_path(m_path, m_name); boost::shared_ptr const f = boost::make_shared(fn, file::read_only, boost::ref(ec)); if (!ec) { // parse header boost::scoped_array header(new boost::uint32_t[m_header_size / 4]); file::iovec_t b = {header.get(), size_t(m_header_size) }; int const n = f->readv(0, &b, 1, ec); if (ec) return; // we don't have a full header. consider the file empty if (n < m_header_size) return; using namespace libtorrent::detail; char* ptr = reinterpret_cast(header.get()); // we have a header. Parse it int num_pieces_ = read_uint32(ptr); int piece_size_ = read_uint32(ptr); // if there is a mismatch in number of pieces or piece size // consider the file empty and overwrite anything in there if (num_pieces != num_pieces_ || m_piece_size != piece_size_) return; // this is used to determine which slots are free, and how many // slots are allocated std::vector free_slots; free_slots.resize(num_pieces, true); for (int i = 0; i < num_pieces; ++i) { int slot = read_uint32(ptr); if (slot == 0xffffffff) continue; // invalid part-file TORRENT_ASSERT(slot < num_pieces); if (slot >= num_pieces) continue; if (slot >= m_num_allocated) m_num_allocated = slot + 1; free_slots[slot] = false; m_piece_map[i] = slot; } // now, populate the free_list with the "holes" for (int i = 0; i < m_num_allocated; ++i) { if (free_slots[i]) m_free_slots.push_back(i); } m_file = f; } } part_file::~part_file() { error_code ec; flush_metadata_impl(ec); } int part_file::allocate_slot(int piece) { // the mutex is assumed to be held here, since this is a private function TORRENT_ASSERT(m_piece_map.find(piece) == m_piece_map.end()); int slot = -1; if (!m_free_slots.empty()) { slot = m_free_slots.front(); m_free_slots.erase(m_free_slots.begin()); } else { slot = m_num_allocated; ++m_num_allocated; } m_piece_map[piece] = slot; m_dirty_metadata = true; return slot; } int part_file::writev(file::iovec_t const* bufs, int num_bufs, int piece, int offset, error_code& ec) { TORRENT_ASSERT(offset >= 0); mutex::scoped_lock l(m_mutex); open_file(file::read_write | file::attribute_hidden, ec); if (ec) return -1; boost::unordered_map::iterator const i = m_piece_map.find(piece); int const slot = i == m_piece_map.end() ? allocate_slot(piece) : i->second; boost::shared_ptr const f = m_file; l.unlock(); return f->writev(slot_offset(slot) + offset, bufs, num_bufs, ec); } int part_file::readv(file::iovec_t const* bufs, int num_bufs , int piece, int offset, error_code& ec) { TORRENT_ASSERT(offset >= 0); mutex::scoped_lock l(m_mutex); boost::unordered_map::iterator const i = m_piece_map.find(piece); if (i == m_piece_map.end()) { ec = make_error_code(boost::system::errc::no_such_file_or_directory); return -1; } int const slot = i->second; open_file(file::read_only | file::attribute_hidden, ec); if (ec) return -1; boost::shared_ptr const f = m_file; l.unlock(); return f->readv(slot_offset(slot) + offset, bufs, num_bufs, ec); } void part_file::open_file(int mode, error_code& ec) { TORRENT_ASSERT((mode & file::lock_file) == 0); if (m_file && m_file->is_open() && ((mode & file::rw_mask) == file::read_only || (m_file->open_mode() & file::rw_mask) == file::read_write)) return; std::string const fn = combine_path(m_path, m_name); boost::shared_ptr f = boost::make_shared(fn, mode, boost::ref(ec)); if (((mode & file::rw_mask) != file::read_only) && ec == boost::system::errc::no_such_file_or_directory) { // this means the directory the file is in doesn't exist. // so create it ec.clear(); create_directories(m_path, ec); if (ec) return; f = boost::make_shared(fn, mode, boost::ref(ec)); } if (!ec) m_file = f; } void part_file::free_piece(int piece) { mutex::scoped_lock l(m_mutex); boost::unordered_map::iterator i = m_piece_map.find(piece); if (i == m_piece_map.end()) return; // TODO: what do we do if someone is currently reading from the disk // from this piece? does it matter? Since we won't actively erase the // data from disk, but it may be overwritten soon, it's probably not that // big of a deal m_free_slots.push_back(i->second); m_piece_map.erase(i); m_dirty_metadata = true; } void part_file::move_partfile(std::string const& path, error_code& ec) { mutex::scoped_lock l(m_mutex); flush_metadata_impl(ec); if (ec) return; // we're only supposed to move part files from a fence job. i.e. no other // disk jobs are supposed to be in-flight at this point TORRENT_ASSERT(!m_file || m_file.unique()); m_file.reset(); if (!m_piece_map.empty()) { std::string old_path = combine_path(m_path, m_name); std::string new_path = combine_path(path, m_name); rename(old_path, new_path, ec); if (ec == boost::system::errc::no_such_file_or_directory) ec.clear(); if (ec) { copy_file(old_path, new_path, ec); if (ec) return; remove(old_path, ec); } } m_path = path; } void part_file::import_file(file& f, boost::int64_t offset , boost::int64_t size, error_code& ec) { TORRENT_UNUSED(f); TORRENT_UNUSED(offset); TORRENT_UNUSED(size); TORRENT_UNUSED(ec); // not implemented ec.assign(boost::system::errc::operation_not_supported , boost::system::generic_category()); } void part_file::export_file(file& f, boost::int64_t offset, boost::int64_t size, error_code& ec) { mutex::scoped_lock l(m_mutex); int piece = offset / m_piece_size; int const end = ((offset + size) + m_piece_size - 1) / m_piece_size; boost::scoped_array buf; boost::int64_t piece_offset = offset - boost::int64_t(piece) * m_piece_size; boost::int64_t file_offset = 0; for (; piece < end; ++piece) { boost::unordered_map::iterator i = m_piece_map.find(piece); int const block_to_copy = (std::min)(m_piece_size - piece_offset, size); if (i != m_piece_map.end()) { int const slot = i->second; open_file(file::read_only, ec); boost::shared_ptr const local_file = m_file; if (ec) return; if (!buf) buf.reset(new char[m_piece_size]); // don't hold the lock during disk I/O l.unlock(); file::iovec_t v = { buf.get(), size_t(block_to_copy) }; v.iov_len = local_file->readv(slot_offset(slot) + piece_offset, &v, 1, ec); TORRENT_ASSERT(!ec); if (ec || v.iov_len == 0) return; boost::int64_t ret = f.writev(file_offset, &v, 1, ec); TORRENT_ASSERT(ec || ret == v.iov_len); if (ec || ret != v.iov_len) return; // we're done with the disk I/O, grab the lock again to update // the slot map l.lock(); if (block_to_copy == m_piece_size) { // since we released the lock, it's technically possible that // another thread removed this slot map entry, and invalidated // our iterator. Now that we hold the lock again, perform // another lookup to be sure. boost::unordered_map::iterator j = m_piece_map.find(piece); if (j != m_piece_map.end()) { // if the slot moved, that's really suspicious TORRENT_ASSERT(j->second == slot); m_free_slots.push_back(j->second); m_piece_map.erase(j); m_dirty_metadata = true; } } } file_offset += block_to_copy; piece_offset = 0; size -= block_to_copy; } } void part_file::flush_metadata(error_code& ec) { mutex::scoped_lock l(m_mutex); flush_metadata_impl(ec); } // TODO: instead of rebuilding the whole file header // and flushing it, update the slot entries as we go void part_file::flush_metadata_impl(error_code& ec) { // do we need to flush the metadata? if (m_dirty_metadata == false) return; if (m_piece_map.empty()) { m_file.reset(); // if we don't have any pieces left in the // part file, remove it std::string const p = combine_path(m_path, m_name); remove(p, ec); if (ec == boost::system::errc::no_such_file_or_directory) ec.clear(); return; } open_file(file::read_write | file::attribute_hidden, ec); if (ec) return; boost::scoped_array header(new boost::uint32_t[m_header_size / 4]); using namespace libtorrent::detail; char* ptr = reinterpret_cast(header.get()); write_uint32(m_max_pieces, ptr); write_uint32(m_piece_size, ptr); for (int piece = 0; piece < m_max_pieces; ++piece) { boost::unordered_map::iterator i = m_piece_map.find(piece); int slot = 0xffffffff; if (i != m_piece_map.end()) slot = i->second; write_uint32(slot, ptr); } memset(ptr, 0, m_header_size - (ptr - reinterpret_cast(header.get()))); #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_MEM_IS_DEFINED(header.get(), m_header_size); #endif file::iovec_t b = {header.get(), size_t(m_header_size) }; m_file->writev(0, &b, 1, ec); if (ec) return; m_dirty_metadata = false; } } libtorrent-rasterbar-1.1.13/src/pe_crypto.cpp000066400000000000000000000310021351156116000212360ustar00rootroot00000000000000/* Copyright (c) 2007-2018, Un Shyam, Arvid Norberg, Steven Siloti 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. */ #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include extern "C" { #include "libtorrent/tommath.h" } #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/random.hpp" #include "libtorrent/pe_crypto.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { namespace { const unsigned char dh_prime[96] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63 }; } struct mp_bigint { mp_bigint() { mp_init(&v); } mp_int* operator&() { return &v; } ~mp_bigint() { mp_clear(&v); } private: // non-copyable mp_bigint(mp_bigint const&); mp_bigint const& operator=(mp_bigint const&); mp_int v; }; // Set the prime P and the generator, generate local public key dh_key_exchange::dh_key_exchange() { // create local key for (int i = 0; i < int(sizeof(m_dh_local_secret)); ++i) m_dh_local_secret[i] = random() & 0xff; mp_bigint prime; mp_bigint secret; mp_bigint key; // TODO 2: use exceptions for error reporting here if (mp_read_unsigned_bin(&prime, dh_prime, sizeof(dh_prime))) { TORRENT_ASSERT(false); return; } if (mp_read_unsigned_bin(&secret , reinterpret_cast(m_dh_local_secret) , sizeof(m_dh_local_secret))) { TORRENT_ASSERT(false); return; } // generator is 2 mp_set_int(&key, 2); // key = (2 ^ secret) % prime if (mp_exptmod(&key, &secret, &prime, &key)) { TORRENT_ASSERT(false); return; } // key is now our local key int const size = mp_unsigned_bin_size(&key); TORRENT_ASSERT(size >= 0); TORRENT_ASSERT(size <= sizeof(m_dh_local_key)); if (size < 0 || size > sizeof(m_dh_local_key)) return; std::memset(m_dh_local_key, 0, sizeof(m_dh_local_key) - size); mp_to_unsigned_bin(&key , reinterpret_cast(m_dh_local_key) + sizeof(m_dh_local_key) - size); } char const* dh_key_exchange::get_local_key() const { return m_dh_local_key; } // compute shared secret given remote public key int dh_key_exchange::compute_secret(char const* remote_pubkey) { TORRENT_ASSERT(remote_pubkey); mp_bigint prime; mp_bigint secret; mp_bigint remote_key; // TODO 2: use exceptions for error reporting here if (mp_read_unsigned_bin(&prime, dh_prime, sizeof(dh_prime))) { TORRENT_ASSERT(false); return -1; } if (mp_read_unsigned_bin(&secret , reinterpret_cast(m_dh_local_secret) , sizeof(m_dh_local_secret))) { TORRENT_ASSERT(false); return -1; } if (mp_read_unsigned_bin(&remote_key , reinterpret_cast(remote_pubkey), 96)) { TORRENT_ASSERT(false); return -1; } if (mp_exptmod(&remote_key, &secret, &prime, &remote_key)) { TORRENT_ASSERT(false); return -1; } // remote_key is now the shared secret int const size = mp_unsigned_bin_size(&remote_key); TORRENT_ASSERT(size >= 0); TORRENT_ASSERT(size <= sizeof(m_dh_shared_secret)); if (size < 0 || size > sizeof(m_dh_shared_secret)) { return -1; } std::memset(m_dh_shared_secret, 0, sizeof(m_dh_shared_secret) - size); mp_to_unsigned_bin(&remote_key , reinterpret_cast(m_dh_shared_secret) + sizeof(m_dh_shared_secret) - size); // calculate the xor mask for the obfuscated hash hasher h; h.update("req3", 4); h.update(m_dh_shared_secret, sizeof(m_dh_shared_secret)); m_xor_mask = h.final(); return 0; } int encryption_handler::encrypt(std::vector& iovec) { TORRENT_ASSERT(!m_send_barriers.empty()); TORRENT_ASSERT(m_send_barriers.front().enc_handler); int to_process = m_send_barriers.front().next; if (to_process != INT_MAX) { for (std::vector::iterator i = iovec.begin(); to_process >= 0; ++i) { if (to_process == 0) { iovec.erase(i, iovec.end()); break; } else if (to_process < boost::asio::buffer_size(*i)) { *i = boost::asio::mutable_buffer(boost::asio::buffer_cast(*i), to_process); iovec.erase(++i, iovec.end()); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS to_process = 0; #endif break; } to_process -= boost::asio::buffer_size(*i); } TORRENT_ASSERT(to_process == 0); } #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS to_process = 0; for (std::vector::iterator i = iovec.begin(); i != iovec.end(); ++i) to_process += boost::asio::buffer_size(*i); #endif int next_barrier = 0; if (iovec.empty() || (next_barrier = m_send_barriers.front().enc_handler->encrypt(iovec))) { if (m_send_barriers.front().next != INT_MAX) { if (m_send_barriers.size() == 1) // transitioning back to plaintext next_barrier = INT_MAX; m_send_barriers.pop_front(); } #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS if (next_barrier != INT_MAX) { int overhead = 0; for (std::vector::iterator i = iovec.begin(); i != iovec.end(); ++i) overhead += boost::asio::buffer_size(*i); TORRENT_ASSERT(overhead + to_process == next_barrier); } #endif } else { iovec.clear(); } return next_barrier; } int encryption_handler::decrypt(crypto_receive_buffer& recv_buffer, std::size_t& bytes_transferred) { TORRENT_ASSERT(!is_recv_plaintext()); int consume = 0; if (recv_buffer.crypto_packet_finished()) { std::vector wr_buf; recv_buffer.mutable_buffers(wr_buf, bytes_transferred); int packet_size = 0; int produce = bytes_transferred; m_dec_handler->decrypt(wr_buf, consume, produce, packet_size); TORRENT_ASSERT(packet_size || produce); TORRENT_ASSERT(packet_size >= 0); bytes_transferred = produce; if (packet_size) recv_buffer.crypto_cut(consume, packet_size); } else bytes_transferred = 0; return consume; } bool encryption_handler::switch_send_crypto(boost::shared_ptr crypto , int pending_encryption) { bool place_barrier = false; if (!m_send_barriers.empty()) { std::list::iterator end = m_send_barriers.end(); --end; for (std::list::iterator b = m_send_barriers.begin(); b != end; ++b) pending_encryption -= b->next; TORRENT_ASSERT(pending_encryption >= 0); m_send_barriers.back().next = pending_encryption; } else if (crypto) place_barrier = true; if (crypto) m_send_barriers.push_back(barrier(crypto, INT_MAX)); return place_barrier; } void encryption_handler::switch_recv_crypto(boost::shared_ptr crypto , crypto_receive_buffer& recv_buffer) { m_dec_handler = crypto; int packet_size = 0; if (crypto) { int consume = 0; int produce = 0; std::vector wr_buf; crypto->decrypt(wr_buf, consume, produce, packet_size); TORRENT_ASSERT(wr_buf.empty()); TORRENT_ASSERT(consume == 0); TORRENT_ASSERT(produce == 0); } recv_buffer.crypto_reset(packet_size); } rc4_handler::rc4_handler() : m_encrypt(false) , m_decrypt(false) { m_rc4_incoming.x = 0; m_rc4_incoming.y = 0; m_rc4_outgoing.x = 0; m_rc4_outgoing.y = 0; } void rc4_handler::set_incoming_key(unsigned char const* key, int len) { m_decrypt = true; rc4_init(key, len, &m_rc4_incoming); // Discard first 1024 bytes char buf[1024]; std::vector vec(1, boost::asio::mutable_buffer(buf, 1024)); int consume = 0; int produce = 0; int packet_size = 0; decrypt(vec, consume, produce, packet_size); } void rc4_handler::set_outgoing_key(unsigned char const* key, int len) { m_encrypt = true; rc4_init(key, len, &m_rc4_outgoing); // Discard first 1024 bytes char buf[1024]; std::vector vec(1, boost::asio::mutable_buffer(buf, 1024)); encrypt(vec); } int rc4_handler::encrypt(std::vector& buf) { if (!m_encrypt) return 0; if (buf.empty()) return 0; int bytes_processed = 0; for (std::vector::iterator i = buf.begin(); i != buf.end(); ++i) { unsigned char* pos = boost::asio::buffer_cast(*i); int len = boost::asio::buffer_size(*i); TORRENT_ASSERT(len >= 0); TORRENT_ASSERT(pos); bytes_processed += len; rc4_encrypt(pos, len, &m_rc4_outgoing); } buf.clear(); return bytes_processed; } void rc4_handler::decrypt(std::vector& buf , int& consume , int& produce , int& packet_size) { // these are out-parameters that are not set TORRENT_UNUSED(consume); TORRENT_UNUSED(packet_size); if (!m_decrypt) return; int bytes_processed = 0; for (std::vector::iterator i = buf.begin(); i != buf.end(); ++i) { unsigned char* pos = boost::asio::buffer_cast(*i); int len = boost::asio::buffer_size(*i); TORRENT_ASSERT(len >= 0); TORRENT_ASSERT(pos); bytes_processed += len; rc4_encrypt(pos, len, &m_rc4_incoming); } buf.clear(); produce = bytes_processed; } } // namespace libtorrent // All this code is based on libTomCrypt (http://www.libtomcrypt.com/) // this library is public domain and has been specially // tailored for libtorrent by Arvid Norberg void rc4_init(const unsigned char* in, unsigned long len, rc4 *state) { size_t const key_size = sizeof(state->buf); unsigned char key[key_size], tmp, *s; int keylen, x, y, j; TORRENT_ASSERT(state != 0); TORRENT_ASSERT(len <= key_size); if (len > key_size) len = key_size; state->x = 0; while (len--) { state->buf[state->x++] = *in++; } /* extract the key */ s = state->buf; std::memcpy(key, s, key_size); keylen = state->x; /* make RC4 perm and shuffle */ for (x = 0; x < key_size; ++x) { s[x] = x; } for (j = x = y = 0; x < key_size; x++) { y = (y + state->buf[x] + key[j++]) & 255; if (j == keylen) { j = 0; } tmp = s[x]; s[x] = s[y]; s[y] = tmp; } state->x = 0; state->y = 0; } unsigned long rc4_encrypt(unsigned char *out, unsigned long outlen, rc4 *state) { unsigned char x, y, *s, tmp; unsigned long n; TORRENT_ASSERT(out != 0); TORRENT_ASSERT(state != 0); n = outlen; x = state->x; y = state->y; s = state->buf; while (outlen--) { x = (x + 1) & 255; y = (y + s[x]) & 255; tmp = s[x]; s[x] = s[y]; s[y] = tmp; tmp = (s[x] + s[y]) & 255; *out++ ^= s[tmp]; } state->x = x; state->y = y; return n; } #endif // #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) libtorrent-rasterbar-1.1.13/src/peer_class.cpp000066400000000000000000000107241351156116000213620ustar00rootroot00000000000000/* Copyright (c) 2011-2018, 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/peer_class.hpp" #include "libtorrent/peer_connection.hpp" #ifdef TORRENT_USE_VALGRIND #include #endif namespace libtorrent { void peer_class::set_upload_limit(int limit) { TORRENT_ASSERT(limit >= -1); if (limit < 0) limit = 0; if (limit < 10 && limit > 0) limit = 10; channel[peer_connection::upload_channel].throttle(limit); } void peer_class::set_download_limit(int limit) { TORRENT_ASSERT(limit >= -1); if (limit < 0) limit = 0; if (limit < 10 && limit > 0) limit = 10; channel[peer_connection::download_channel].throttle(limit); } void peer_class::get_info(peer_class_info* pci) const { pci->ignore_unchoke_slots = ignore_unchoke_slots; pci->connection_limit_factor = connection_limit_factor; pci->label = label; pci->upload_limit = channel[peer_connection::upload_channel].throttle(); pci->download_limit = channel[peer_connection::download_channel].throttle(); pci->upload_priority = priority[peer_connection::upload_channel]; pci->download_priority = priority[peer_connection::download_channel]; } void peer_class::set_info(peer_class_info const* pci) { ignore_unchoke_slots = pci->ignore_unchoke_slots; connection_limit_factor = pci->connection_limit_factor; label = pci->label; set_upload_limit(pci->upload_limit); set_download_limit(pci->download_limit); priority[peer_connection::upload_channel] = (std::max)(1, (std::min)(255, pci->upload_priority)); priority[peer_connection::download_channel] = (std::max)(1, (std::min)(255, pci->download_priority)); } peer_class_t peer_class_pool::new_peer_class(std::string const& label) { peer_class_t ret = 0; if (!m_free_list.empty()) { ret = m_free_list.back(); m_free_list.pop_back(); m_peer_classes[ret] = peer_class(label); } else { TORRENT_ASSERT(m_peer_classes.size() < 0x100000000); ret = m_peer_classes.size(); m_peer_classes.push_back(peer_class(label)); } return ret; } void peer_class_pool::decref(peer_class_t c) { #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_VALUE_IS_DEFINED(c); #endif TORRENT_ASSERT(c < m_peer_classes.size()); TORRENT_ASSERT(m_peer_classes[c].in_use); TORRENT_ASSERT(m_peer_classes[c].references > 0); --m_peer_classes[c].references; if (m_peer_classes[c].references) return; m_peer_classes[c].clear(); m_free_list.push_back(c); } void peer_class_pool::incref(peer_class_t c) { #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_VALUE_IS_DEFINED(c); #endif TORRENT_ASSERT(c < m_peer_classes.size()); TORRENT_ASSERT(m_peer_classes[c].in_use); ++m_peer_classes[c].references; } peer_class* peer_class_pool::at(peer_class_t c) { #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_VALUE_IS_DEFINED(c); #endif if (c >= m_peer_classes.size() || !m_peer_classes[c].in_use) return NULL; return &m_peer_classes[c]; } peer_class const* peer_class_pool::at(peer_class_t c) const { if (c >= m_peer_classes.size() || !m_peer_classes[c].in_use) return NULL; return &m_peer_classes[c]; } } libtorrent-rasterbar-1.1.13/src/peer_class_set.cpp000066400000000000000000000050251351156116000222330ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/peer_class_set.hpp" #include "libtorrent/peer_class.hpp" #include #include // for find namespace libtorrent { void peer_class_set::add_class(peer_class_pool& pool, peer_class_t c) { if (std::find(m_class.begin(), m_class.begin() + m_size, c) != m_class.begin() + m_size) return; if (m_size >= m_class.size() - 1) { TORRENT_ASSERT(false); return; } m_class[m_size] = c; pool.incref(c); ++m_size; } bool peer_class_set::has_class(peer_class_t c) const { return std::find(m_class.begin(), m_class.begin() + m_size, c) != m_class.begin() + m_size; } void peer_class_set::remove_class(peer_class_pool& pool, peer_class_t c) { boost::array::iterator i = std::find(m_class.begin() , m_class.begin() + m_size, c); int const idx = i - m_class.begin(); if (idx == m_size) return; // not found if (idx < m_size - 1) { // place the last element in the slot of the erased one m_class[idx] = m_class[m_size - 1]; } --m_size; pool.decref(c); } } libtorrent-rasterbar-1.1.13/src/peer_connection.cpp000066400000000000000000006116701351156116000224230ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/aux_/disable_warnings_push.hpp" #include #include #include #include #ifdef TORRENT_DEBUG #include #endif #ifdef TORRENT_USE_OPENSSL #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifndef TORRENT_DISABLE_LOGGING #include // for va_start, va_end #include // for vsnprintf #include "libtorrent/socket_io.hpp" #endif #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/extensions.hpp" #include "libtorrent/aux_/session_interface.hpp" #include "libtorrent/peer_list.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/network_thread_pool.hpp" #include "libtorrent/error.hpp" #include "libtorrent/alloca.hpp" #include "libtorrent/disk_interface.hpp" #include "libtorrent/bandwidth_manager.hpp" #include "libtorrent/request_blocks.hpp" // for request_a_block #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/alert_manager.hpp" // for alert_manageralert_manager #include "libtorrent/ip_filter.hpp" #include "libtorrent/kademlia/node_id.hpp" #include "libtorrent/close_reason.hpp" #include "libtorrent/aux_/time.hpp" //#define TORRENT_CORRUPT_DATA using boost::shared_ptr; namespace libtorrent { enum { // the limits of the download queue size min_request_queue = 2 }; namespace { bool pending_block_in_buffer(pending_block const& pb) { return pb.send_buffer_offset != pending_block::not_in_buffer; } } #if TORRENT_USE_ASSERTS bool peer_connection::is_single_thread() const { boost::shared_ptr t = m_torrent.lock(); if (!t) return true; return t->is_single_thread(); } #endif // outbound connection peer_connection::peer_connection(peer_connection_args const& pack) : peer_connection_hot_members(pack.tor, *pack.ses, *pack.sett) , m_socket(pack.s) , m_peer_info(pack.peerinfo) , m_counters(*pack.stats_counters) , m_num_pieces(0) , m_recv_buffer(*pack.allocator) , m_max_out_request_queue(m_settings.get_int(settings_pack::max_out_request_queue)) , m_remote(pack.endp) , m_disk_thread(*pack.disk_thread) , m_allocator(*pack.allocator) , m_ios(*pack.ios) , m_work(m_ios) , m_last_piece(aux::time_now()) , m_last_request(aux::time_now()) , m_last_incoming_request(min_time()) , m_last_unchoke(aux::time_now()) , m_last_unchoked(aux::time_now()) , m_last_choke(min_time()) , m_last_receive(aux::time_now()) , m_last_sent(aux::time_now()) , m_last_sent_payload(aux::time_now()) , m_requested(min_time()) , m_remote_dl_update(aux::time_now()) , m_connect(aux::time_now()) , m_became_uninterested(aux::time_now()) , m_became_uninteresting(aux::time_now()) , m_downloaded_at_last_round(0) , m_uploaded_at_last_round(0) , m_uploaded_at_last_unchoke(0) , m_downloaded_last_second(0) , m_uploaded_last_second(0) , m_outstanding_bytes(0) , m_last_seen_complete(0) , m_receiving_block(piece_block::invalid) , m_extension_outstanding_bytes(0) , m_queued_time_critical(0) , m_reading_bytes(0) , m_picker_options(0) , m_num_invalid_requests(0) , m_remote_pieces_dled(0) , m_remote_dl_rate(0) , m_outstanding_writing_bytes(0) , m_download_rate_peak(0) , m_upload_rate_peak(0) , m_send_barrier(INT_MAX) , m_desired_queue_size(4) , m_prefer_contiguous_blocks(0) , m_disk_read_failures(0) , m_outstanding_piece_verification(0) , m_outgoing(!pack.tor.expired()) , m_received_listen_port(false) , m_fast_reconnect(false) , m_failed(false) , m_connected(pack.tor.expired()) , m_request_large_blocks(false) , m_share_mode(false) , m_upload_only(false) , m_bitfield_received(false) , m_no_download(false) , m_sent_suggests(false) , m_holepunch_mode(false) , m_peer_choked(true) , m_have_all(false) , m_peer_interested(false) , m_need_interest_update(false) , m_has_metadata(true) , m_exceeded_limit(false) , m_slow_start(true) #if TORRENT_USE_ASSERTS , m_in_constructor(true) , m_disconnect_started(false) , m_initialized(false) , m_in_use(1337) , m_received_in_piece(0) , m_destructed(false) , m_socket_is_writing(false) #endif { m_counters.inc_stats_counter(counters::num_tcp_peers + m_socket->type() - 1); if (m_connected) m_counters.inc_stats_counter(counters::num_peers_connected); else if (m_connecting) m_counters.inc_stats_counter(counters::num_peers_half_open); m_superseed_piece[0] = -1; m_superseed_piece[1] = -1; boost::shared_ptr t = m_torrent.lock(); // if t is NULL, we better not be connecting, since // we can't decrement the connecting counter TORRENT_ASSERT(t || !m_connecting); m_est_reciprocation_rate = m_settings.get_int(settings_pack::default_est_reciprocation_rate); m_channel_state[upload_channel] = peer_info::bw_idle; m_channel_state[download_channel] = peer_info::bw_idle; m_quota[0] = 0; m_quota[1] = 0; TORRENT_ASSERT(pack.peerinfo == 0 || pack.peerinfo->banned == false); #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES std::fill(m_country, m_country + 2, 0); #endif #endif // TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_LOGGING error_code ec; TORRENT_ASSERT(m_socket->remote_endpoint(ec) == m_remote || ec); tcp::endpoint local_ep = m_socket->local_endpoint(ec); peer_log(m_outgoing ? peer_log_alert::outgoing : peer_log_alert::incoming , m_outgoing ? "OUTGOING_CONNECTION" : "INCOMING_CONNECTION" , "ep: %s type: %s seed: %d p: %p local: %s" , print_endpoint(m_remote).c_str() , m_socket->type_name() , m_peer_info ? m_peer_info->seed : 0 , static_cast(m_peer_info) , print_endpoint(local_ep).c_str()); #endif #ifdef TORRENT_DEBUG piece_failed = false; #endif std::fill(m_peer_id.begin(), m_peer_id.end(), 0); } int peer_connection::timeout() const { TORRENT_ASSERT(is_single_thread()); int ret = m_settings.get_int(settings_pack::peer_timeout); #if TORRENT_USE_I2P if (m_peer_info && m_peer_info->is_i2p_addr) { // quadruple the timeout for i2p peers ret *= 4; } #endif return ret; } void peer_connection::increase_est_reciprocation_rate() { TORRENT_ASSERT(is_single_thread()); m_est_reciprocation_rate += m_est_reciprocation_rate * m_settings.get_int(settings_pack::increase_est_reciprocation_rate) / 100; } void peer_connection::decrease_est_reciprocation_rate() { TORRENT_ASSERT(is_single_thread()); m_est_reciprocation_rate -= m_est_reciprocation_rate * m_settings.get_int(settings_pack::decrease_est_reciprocation_rate) / 100; } int peer_connection::get_priority(int channel) const { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(channel >= 0 && channel < 2); int prio = 1; for (int i = 0; i < num_classes(); ++i) { int class_prio = m_ses.peer_classes().at(class_at(i))->priority[channel]; if (prio < class_prio) prio = class_prio; } boost::shared_ptr t = associated_torrent().lock(); if (t) { for (int i = 0; i < t->num_classes(); ++i) { int class_prio = m_ses.peer_classes().at(t->class_at(i))->priority[channel]; if (prio < class_prio) prio = class_prio; } } return prio; } void peer_connection::reset_choke_counters() { TORRENT_ASSERT(is_single_thread()); m_downloaded_at_last_round= m_statistics.total_payload_download(); m_uploaded_at_last_round = m_statistics.total_payload_upload(); } void peer_connection::start() { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_peer_info == 0 || m_peer_info->connection == this); boost::shared_ptr t = m_torrent.lock(); if (!m_outgoing) { error_code ec; m_socket->non_blocking(true, ec); if (ec) { disconnect(ec, op_iocontrol); return; } m_remote = m_socket->remote_endpoint(ec); if (ec) { disconnect(ec, op_getpeername); return; } m_local = m_socket->local_endpoint(ec); if (ec) { disconnect(ec, op_getname); return; } if (m_remote.address().is_v4() && m_settings.get_int(settings_pack::peer_tos) != 0) { m_socket->set_option(type_of_service(m_settings.get_int(settings_pack::peer_tos)), ec); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "SET_TOS", "tos: %d e: %s" , m_settings.get_int(settings_pack::peer_tos), ec.message().c_str()); #endif } #if TORRENT_USE_IPV6 && defined IPV6_TCLASS else if (m_remote.address().is_v6() && m_settings.get_int(settings_pack::peer_tos) != 0) { m_socket->set_option(traffic_class(m_settings.get_int(settings_pack::peer_tos)), ec); } #endif } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SET_PEER_CLASS", "a: %s" , print_address(m_remote.address()).c_str()); #endif m_ses.set_peer_classes(this, m_remote.address(), m_socket->type()); #ifndef TORRENT_DISABLE_LOGGING for (int i = 0; i < num_classes(); ++i) { peer_log(peer_log_alert::info, "CLASS", "%s" , m_ses.peer_classes().at(class_at(i))->label.c_str()); } #endif if (t && t->ready_for_connections()) { init(); } // if this is an incoming connection, we're done here if (!m_connecting) return; if (m_connecting && t) t->inc_num_connecting(m_peer_info); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "OPEN", "protocol: %s" , (m_remote.address().is_v4()?"IPv4":"IPv6")); #endif error_code ec; m_socket->open(m_remote.protocol(), ec); if (ec) { disconnect(ec, op_sock_open); return; } tcp::endpoint const bound_ip = m_ses.bind_outgoing_socket(*m_socket , m_remote.address(), ec); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "BIND", "dst: %s ec: %s" , print_endpoint(bound_ip).c_str() , ec.message().c_str()); #else TORRENT_UNUSED(bound_ip); #endif if (ec) { disconnect(ec, op_sock_bind); return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "ASYNC_CONNECT", "dst: %s" , print_endpoint(m_remote).c_str()); #endif #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("peer_connection::on_connection_complete"); #endif #ifndef TORRENT_DISABLE_LOGGING if (t) t->debug_log("START connect [%p] (%d)", static_cast(this) , int(t->num_peers())); #endif m_socket->async_connect(m_remote , boost::bind(&peer_connection::on_connection_complete, self(), _1)); m_connect = aux::time_now(); sent_syn(m_remote.address().is_v6()); if (t && t->alerts().should_post()) { t->alerts().emplace_alert( t->get_handle(), remote(), pid(), m_socket->type()); } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "LOCAL ENDPOINT", "e: %s" , print_endpoint(m_socket->local_endpoint(ec)).c_str()); #endif } void peer_connection::update_interest() { TORRENT_ASSERT(is_single_thread()); if (!m_need_interest_update) { // we're the first to request an interest update // post a message in order to delay it enough for // any potential other messages already in the queue // to not trigger another one. This effectively defer // the update until the current message queue is // flushed m_ios.post(boost::bind(&peer_connection::do_update_interest, self())); } m_need_interest_update = true; } void peer_connection::do_update_interest() { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_need_interest_update); m_need_interest_update = false; boost::shared_ptr t = m_torrent.lock(); if (!t) return; // if m_have_piece is 0, it means the connections // have not been initialized yet. The interested // flag will be updated once they are. if (m_have_piece.size() == 0) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "UPDATE_INTEREST", "connections not initialized"); #endif return; } if (!t->ready_for_connections()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "UPDATE_INTEREST", "not ready for connections"); #endif return; } bool interested = false; if (!t->is_upload_only()) { t->need_picker(); piece_picker const& p = t->picker(); int num_pieces = p.num_pieces(); for (int j = 0; j != num_pieces; ++j) { if (m_have_piece[j] && t->piece_priority(j) > 0 && !p.has_piece_passed(j)) { interested = true; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "UPDATE_INTEREST", "interesting, piece: %d", j); #endif break; } } } #ifndef TORRENT_DISABLE_LOGGING if (!interested) peer_log(peer_log_alert::info, "UPDATE_INTEREST", "not interesting"); #endif if (!interested) send_not_interested(); else t->peer_is_interesting(*this); TORRENT_ASSERT(in_handshake() || is_interesting() == interested); disconnect_if_redundant(); } #ifndef TORRENT_DISABLE_LOGGING void peer_connection::peer_log(peer_log_alert::direction_t direction , char const* event) const { peer_log(direction, event, ""); } TORRENT_FORMAT(4,5) void peer_connection::peer_log(peer_log_alert::direction_t direction , char const* event, char const* fmt, ...) const { TORRENT_ASSERT(is_single_thread()); if (!m_ses.alerts().should_post()) return; va_list v; va_start(v, fmt); // TODO: it would be neat to be able to print this straight into the // alert's stack allocator char buf[512]; vsnprintf(buf, sizeof(buf), fmt, v); va_end(v); torrent_handle h; boost::shared_ptr t = m_torrent.lock(); if (t) h = t->get_handle(); m_ses.alerts().emplace_alert( h, m_remote, m_peer_id, direction, event, buf); } #endif #ifndef TORRENT_DISABLE_EXTENSIONS void peer_connection::add_extension(boost::shared_ptr ext) { TORRENT_ASSERT(is_single_thread()); m_extensions.push_back(ext); } peer_plugin const* peer_connection::find_plugin(char const* type) { TORRENT_ASSERT(is_single_thread()); for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if (strcmp((*i)->type(), type) == 0) return (*i).get(); } return 0; } #endif void peer_connection::send_allowed_set() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); if (!t->valid_metadata()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ALLOWED", "skipping allowed set because we don't have metadata"); #endif return; } if (t->super_seeding()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ALLOWED", "skipping allowed set because of super seeding"); #endif return; } if (upload_only()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ALLOWED", "skipping allowed set because peer is upload only"); #endif return; } int const num_allowed_pieces = m_settings.get_int(settings_pack::allowed_fast_set_size); if (num_allowed_pieces == 0) return; int const num_pieces = t->torrent_file().num_pieces(); if (num_allowed_pieces >= num_pieces) { // this is a special case where we have more allowed // fast pieces than pieces in the torrent. Just send // an allowed fast message for every single piece for (int i = 0; i < num_pieces; ++i) { // there's no point in offering fast pieces // that the peer already has if (has_piece(i)) continue; write_allow_fast(i); TORRENT_ASSERT(std::find(m_accept_fast.begin() , m_accept_fast.end(), i) == m_accept_fast.end()); if (m_accept_fast.empty()) { m_accept_fast.reserve(10); m_accept_fast_piece_cnt.reserve(10); } m_accept_fast.push_back(i); m_accept_fast_piece_cnt.push_back(0); } return; } std::string x; address const& addr = m_remote.address(); if (addr.is_v4()) { address_v4::bytes_type bytes = addr.to_v4().to_bytes(); x.assign(reinterpret_cast(bytes.data()), bytes.size()); } #if TORRENT_USE_IPV6 else { address_v6::bytes_type bytes = addr.to_v6().to_bytes(); x.assign(reinterpret_cast(bytes.data()), bytes.size()); } #endif x.append(t->torrent_file().info_hash().data(), 20); sha1_hash hash = hasher(x.c_str(), x.size()).final(); int attempts = 0; int loops = 0; for (;;) { char const* p = hash.data(); for (int i = 0; i < hash.size / sizeof(boost::uint32_t); ++i) { ++loops; int const piece = detail::read_uint32(p) % num_pieces; if (std::find(m_accept_fast.begin(), m_accept_fast.end(), piece) != m_accept_fast.end()) { // this is our safety-net to make sure this loop terminates, even // under the worst conditions if (++loops > 500) return; continue; } if (!has_piece(piece)) { write_allow_fast(piece); if (m_accept_fast.empty()) { m_accept_fast.reserve(10); m_accept_fast_piece_cnt.reserve(10); } m_accept_fast.push_back(piece); m_accept_fast_piece_cnt.push_back(0); } if (++attempts >= num_allowed_pieces) return; } hash = hasher(hash.data(), 20).final(); } } void peer_connection::on_metadata_impl() { TORRENT_ASSERT(is_single_thread()); boost::shared_ptr t = associated_torrent().lock(); m_have_piece.resize(t->torrent_file().num_pieces(), m_have_all); m_num_pieces = m_have_piece.count(); // now that we know how many pieces there are // remove any invalid allowed_fast and suggest pieces // now that we know what the number of pieces are for (std::vector::iterator i = m_allowed_fast.begin(); i != m_allowed_fast.end();) { if (*i < m_num_pieces) { ++i; continue; } i = m_allowed_fast.erase(i); } for (std::vector::iterator i = m_suggested_pieces.begin(); i != m_suggested_pieces.end();) { if (*i < m_num_pieces) { ++i; continue; } i = m_suggested_pieces.erase(i); } on_metadata(); if (m_disconnecting) return; } void peer_connection::init() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(t->valid_metadata()); TORRENT_ASSERT(t->ready_for_connections()); m_have_piece.resize(t->torrent_file().num_pieces(), m_have_all); if (m_have_all) m_num_pieces = t->torrent_file().num_pieces(); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(!m_initialized); m_initialized = true; #endif // now that we have a piece_picker, // update it with this peer's pieces TORRENT_ASSERT(m_num_pieces == m_have_piece.count()); if (m_num_pieces == int(m_have_piece.size())) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "INIT", "this is a seed p: %p" , static_cast(m_peer_info)); #endif TORRENT_ASSERT(m_have_piece.all_set()); TORRENT_ASSERT(m_have_piece.count() == m_have_piece.size()); TORRENT_ASSERT(m_have_piece.size() == t->torrent_file().num_pieces()); // if this is a web seed. we don't have a peer_info struct t->set_seed(m_peer_info, true); m_upload_only = true; t->peer_has_all(this); #if TORRENT_USE_INVARIANT_CHECKS if (t && t->has_picker()) t->picker().check_peer_invariant(m_have_piece, peer_info_struct()); #endif if (t->is_upload_only()) send_not_interested(); else t->peer_is_interesting(*this); disconnect_if_redundant(); return; } // if we're a seed, we don't keep track of piece availability if (t->has_picker()) { TORRENT_ASSERT(m_have_piece.size() == t->torrent_file().num_pieces()); t->peer_has(m_have_piece, this); bool interesting = false; for (int i = 0; i < int(m_have_piece.size()); ++i) { if (m_have_piece[i]) { // if the peer has a piece and we don't, the peer is interesting if (!t->have_piece(i) && t->picker().piece_priority(i) != 0) interesting = true; } } if (interesting) t->peer_is_interesting(*this); else send_not_interested(); } else { update_interest(); } } peer_connection::~peer_connection() { m_counters.inc_stats_counter(counters::num_tcp_peers + m_socket->type() - 1, -1); // INVARIANT_CHECK; TORRENT_ASSERT(!m_in_constructor); TORRENT_ASSERT(m_disconnecting); TORRENT_ASSERT(m_disconnect_started); TORRENT_ASSERT(!m_destructed); #if TORRENT_USE_ASSERTS m_destructed = true; #endif #if TORRENT_USE_ASSERTS m_in_use = 0; #endif // decrement the stats counter set_endgame(false); if (m_interesting) m_counters.inc_stats_counter(counters::num_peers_down_interested, -1); if (m_peer_interested) m_counters.inc_stats_counter(counters::num_peers_up_interested, -1); if (!m_choked) { m_counters.inc_stats_counter(counters::num_peers_up_unchoked_all, -1); if (!ignore_unchoke_slots()) m_counters.inc_stats_counter(counters::num_peers_up_unchoked, -1); } if (!m_peer_choked) m_counters.inc_stats_counter(counters::num_peers_down_unchoked, -1); if (m_connected) m_counters.inc_stats_counter(counters::num_peers_connected, -1); m_connected = false; if (!m_download_queue.empty()) m_counters.inc_stats_counter(counters::num_peers_down_requests, -1); // defensive boost::shared_ptr t = m_torrent.lock(); // if t is NULL, we better not be connecting, since // we can't decrement the connecting counter TORRENT_ASSERT(t || !m_connecting); // we should really have dealt with this already TORRENT_ASSERT(!m_connecting); if (m_connecting) { m_counters.inc_stats_counter(counters::num_peers_half_open, -1); if (t) t->dec_num_connecting(m_peer_info); m_connecting = false; } #ifndef TORRENT_DISABLE_EXTENSIONS m_extensions.clear(); #endif #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CONNECTION CLOSED"); #endif TORRENT_ASSERT(m_request_queue.empty()); TORRENT_ASSERT(m_download_queue.empty()); #if TORRENT_USE_ASSERTS if (m_peer_info) TORRENT_ASSERT(m_peer_info->connection == 0); #endif } bool peer_connection::on_parole() const { return peer_info_struct() && peer_info_struct()->on_parole; } int peer_connection::picker_options() const { TORRENT_ASSERT(is_single_thread()); int ret = m_picker_options; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); if (!t) return 0; if (t->num_time_critical_pieces() > 0) { ret |= piece_picker::time_critical_mode; } if (t->is_sequential_download()) { ret |= piece_picker::sequential; } else if (t->num_have() < m_settings.get_int(settings_pack::initial_picker_threshold)) { // if we have fewer pieces than a certain threshold // don't pick rare pieces, just pick random ones, // and prioritize finishing them ret |= piece_picker::prioritize_partials; } else { ret |= piece_picker::rarest_first; if (m_snubbed) { // snubbed peers should request // the common pieces first, just to make // it more likely for all snubbed peers to // request blocks from the same piece ret |= piece_picker::reverse; } } if (m_settings.get_bool(settings_pack::prioritize_partial_pieces)) ret |= piece_picker::prioritize_partials; if (on_parole()) ret |= piece_picker::on_parole | piece_picker::prioritize_partials; // only one of rarest_first and sequential can be set. i.e. the sum of // whether the bit is set or not may only be 0 or 1 (never 2) TORRENT_ASSERT(((ret & piece_picker::rarest_first) ? 1 : 0) + ((ret & piece_picker::sequential) ? 1 : 0) <= 1); return ret; } void peer_connection::fast_reconnect(bool r) { TORRENT_ASSERT(is_single_thread()); if (!peer_info_struct() || peer_info_struct()->fast_reconnects > 1) return; m_fast_reconnect = r; peer_info_struct()->last_connected = boost::uint16_t(m_ses.session_time()); int rewind = m_settings.get_int(settings_pack::min_reconnect_time) * m_settings.get_int(settings_pack::max_failcount); if (peer_info_struct()->last_connected < rewind) peer_info_struct()->last_connected = 0; else peer_info_struct()->last_connected -= rewind; if (peer_info_struct()->fast_reconnects < 15) ++peer_info_struct()->fast_reconnects; } void peer_connection::received_piece(int index) { TORRENT_ASSERT(is_single_thread()); // dont announce during handshake if (in_handshake()) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "RECEIVED", "piece: %d", index); #endif // remove suggested pieces once we have them std::vector::iterator i = std::find( m_suggested_pieces.begin(), m_suggested_pieces.end(), index); if (i != m_suggested_pieces.end()) m_suggested_pieces.erase(i); // remove allowed fast pieces i = std::find(m_allowed_fast.begin(), m_allowed_fast.end(), index); if (i != m_allowed_fast.end()) m_allowed_fast.erase(i); if (has_piece(index)) { // if we got a piece that this peer has // it might have been the last interesting // piece this peer had. We might not be // interested anymore update_interest(); if (is_disconnecting()) return; } if (disconnect_if_redundant()) return; #if TORRENT_USE_ASSERTS boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); #endif } void peer_connection::announce_piece(int index) { TORRENT_ASSERT(is_single_thread()); // dont announce during handshake if (in_handshake()) return; if (has_piece(index)) { // optimization, don't send have messages // to peers that already have the piece if (!m_settings.get_bool(settings_pack::send_redundant_have)) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "HAVE", "piece: %d SUPRESSED", index); #endif return; } } if (disconnect_if_redundant()) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "HAVE", "piece: %d", index); #endif write_have(index); #if TORRENT_USE_ASSERTS boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); #endif } bool peer_connection::has_piece(int i) const { TORRENT_ASSERT(is_single_thread()); boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(t->valid_metadata()); TORRENT_ASSERT(i >= 0); TORRENT_ASSERT(i < t->torrent_file().num_pieces()); return m_have_piece[i]; } std::vector const& peer_connection::request_queue() const { TORRENT_ASSERT(is_single_thread()); return m_request_queue; } std::vector const& peer_connection::download_queue() const { TORRENT_ASSERT(is_single_thread()); return m_download_queue; } std::vector const& peer_connection::upload_queue() const { TORRENT_ASSERT(is_single_thread()); return m_requests; } time_duration peer_connection::download_queue_time(int extra_bytes) const { TORRENT_ASSERT(is_single_thread()); boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); int rate = 0; // if we haven't received any data recently, the current download rate // is not representative if (aux::time_now() - m_last_piece > seconds(30) && m_download_rate_peak > 0) { rate = m_download_rate_peak; } else if (aux::time_now() - m_last_unchoked < seconds(5) && m_statistics.total_payload_upload() < 2 * 0x4000) { // if we're have only been unchoked for a short period of time, // we don't know what rate we can get from this peer. Instead of assuming // the lowest possible rate, assume the average. int peers_with_requests = stats_counters()[counters::num_peers_down_requests]; // avoid division by 0 if (peers_with_requests == 0) peers_with_requests = 1; // TODO: this should be the global download rate rate = t->statistics().transfer_rate(stat::download_payload) / peers_with_requests; } else { // current download rate in bytes per seconds rate = m_statistics.transfer_rate(stat::download_payload); } // avoid division by zero if (rate < 50) rate = 50; // average of current rate and peak // rate = (rate + m_download_rate_peak) / 2; return milliseconds((m_outstanding_bytes + extra_bytes + m_queued_time_critical * t->block_size() * 1000) / rate); } void peer_connection::add_stat(boost::int64_t downloaded, boost::int64_t uploaded) { TORRENT_ASSERT(is_single_thread()); m_statistics.add_stat(downloaded, uploaded); } void peer_connection::received_bytes(int bytes_payload, int bytes_protocol) { TORRENT_ASSERT(is_single_thread()); m_statistics.received_bytes(bytes_payload, bytes_protocol); if (m_ignore_stats) return; boost::shared_ptr t = m_torrent.lock(); if (!t) return; t->received_bytes(bytes_payload, bytes_protocol); } void peer_connection::sent_bytes(int bytes_payload, int bytes_protocol) { TORRENT_ASSERT(is_single_thread()); m_statistics.sent_bytes(bytes_payload, bytes_protocol); #ifndef TORRENT_DISABLE_EXTENSIONS if (bytes_payload) { for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { (*i)->sent_payload(bytes_payload); } } #endif if (m_ignore_stats) return; boost::shared_ptr t = m_torrent.lock(); if (!t) return; t->sent_bytes(bytes_payload, bytes_protocol); } void peer_connection::trancieve_ip_packet(int bytes, bool ipv6) { TORRENT_ASSERT(is_single_thread()); m_statistics.trancieve_ip_packet(bytes, ipv6); if (m_ignore_stats) return; boost::shared_ptr t = m_torrent.lock(); if (!t) return; t->trancieve_ip_packet(bytes, ipv6); } void peer_connection::sent_syn(bool ipv6) { TORRENT_ASSERT(is_single_thread()); m_statistics.sent_syn(ipv6); if (m_ignore_stats) return; boost::shared_ptr t = m_torrent.lock(); if (!t) return; t->sent_syn(ipv6); } void peer_connection::received_synack(bool ipv6) { TORRENT_ASSERT(is_single_thread()); m_statistics.received_synack(ipv6); if (m_ignore_stats) return; boost::shared_ptr t = m_torrent.lock(); if (!t) return; t->received_synack(ipv6); } bitfield const& peer_connection::get_bitfield() const { TORRENT_ASSERT(is_single_thread()); return m_have_piece; } void peer_connection::received_valid_data(int index) { TORRENT_ASSERT(is_single_thread()); // this fails because we haven't had time to disconnect // seeds yet, and we might have just become one // INVARIANT_CHECK; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->on_piece_pass(index); } TORRENT_CATCH(std::exception&) {} } #else TORRENT_UNUSED(index); #endif } // single_peer is true if the entire piece was received by a single // peer bool peer_connection::received_invalid_data(int index, bool single_peer) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_UNUSED(single_peer); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->on_piece_failed(index); } TORRENT_CATCH(std::exception&) {} } #else TORRENT_UNUSED(index); #endif return true; } // 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 { TORRENT_ASSERT(is_single_thread()); boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(t->valid_metadata()); torrent_info const& ti = t->torrent_file(); return p.piece >= 0 && p.piece < ti.num_pieces() && p.start >= 0 && p.start < ti.piece_length() && t->to_req(piece_block(p.piece, p.start / t->block_size())) == p; } void peer_connection::attach_to_torrent(sha1_hash const& ih) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #ifndef TORRENT_DISABLE_LOGGING m_connect_time = clock_type::now(); peer_log(peer_log_alert::info, "ATTACH", "attached to torrent"); #endif TORRENT_ASSERT(!m_disconnecting); TORRENT_ASSERT(m_torrent.expired()); boost::weak_ptr wpt = m_ses.find_torrent(ih); boost::shared_ptr t = wpt.lock(); if (t && t->is_aborted()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ATTACH", "the torrent has been aborted"); #endif t.reset(); } if (!t) { t = m_ses.delay_load_torrent(ih, this); #ifndef TORRENT_DISABLE_LOGGING if (t) peer_log(peer_log_alert::info, "ATTACH" , "Delay loaded torrent: %s:", to_hex(ih.to_string()).c_str()); #endif } if (!t) { // we couldn't find the torrent! #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ATTACH" , "couldn't find a torrent with the given info_hash: %s torrents:" , to_hex(ih.to_string()).c_str()); #endif #ifndef TORRENT_DISABLE_DHT if (dht::verify_secret_id(ih)) { // this means the hash was generated from our generate_secret_id() // as part of DHT traffic. The fact that we got an incoming // connection on this info-hash, means the other end, making this // connection fished it out of the DHT chatter. That's suspicious. m_ses.ban_ip(m_remote.address()); } #endif disconnect(errors::invalid_info_hash, op_bittorrent, 1); return; } if (t->is_paused() && t->is_auto_managed() && m_settings.get_bool(settings_pack::incoming_starts_queued_torrents) && !t->is_aborted()) { t->resume(); } if (t->is_paused() || t->is_aborted() || t->graceful_pause()) { // paused torrents will not accept // incoming connections unless they are auto managed // and inconing_starts_queued_torrents is true // torrents that have errors should always reject // incoming peers #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ATTACH", "rejected connection to paused torrent"); #endif disconnect(errors::torrent_paused, op_bittorrent, 2); return; } #if TORRENT_USE_I2P i2p_stream* i2ps = m_socket->get(); if (!i2ps && t->torrent_file().is_i2p() && !m_settings.get_bool(settings_pack::allow_i2p_mixed)) { // the torrent is an i2p torrent, the peer is a regular peer // and we don't allow mixed mode. Disconnect the peer. #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ATTACH", "rejected regular connection to i2p torrent"); #endif disconnect(errors::peer_banned, op_bittorrent, 2); return; } #endif // TORRENT_USE_I2P TORRENT_ASSERT(m_torrent.expired()); // 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 (m_disconnecting) return; // it's important to assign the torrent after successfully attaching. // if the peer disconnects while attaching, it's not a proper member // of the torrent and peer_connection::disconnect() will fail if it // think it is m_torrent = t; if (m_exceeded_limit) { // find a peer in some torrent (presumably the one with most peers) // and disconnect the lowest ranking peer boost::weak_ptr torr = m_ses.find_disconnect_candidate_torrent(); boost::shared_ptr other_t = torr.lock(); if (other_t) { if (other_t->num_peers() <= t->num_peers()) { disconnect(errors::too_many_connections, op_bittorrent); return; } // find the lowest ranking peer and disconnect that peer_connection* p = other_t->find_lowest_ranking_peer(); p->disconnect(errors::too_many_connections, op_bittorrent); peer_disconnected_other(); } else { disconnect(errors::too_many_connections, op_bittorrent); return; } } TORRENT_ASSERT(!m_torrent.expired()); // if the torrent isn't ready to accept // connections yet, we'll have to wait with // our initialization if (t->ready_for_connections()) init(); TORRENT_ASSERT(!m_torrent.expired()); // assume the other end has no pieces // if we don't have valid metadata yet, // leave the vector unallocated TORRENT_ASSERT(m_num_pieces == 0); m_have_piece.clear_all(); TORRENT_ASSERT(!m_torrent.expired()); } boost::uint32_t peer_connection::peer_rank() const { TORRENT_ASSERT(is_single_thread()); return m_peer_info == NULL ? 0 : m_peer_info->rank(m_ses.external_address(), m_ses.listen_port()); } // message handlers // ----------------------------- // --------- KEEPALIVE --------- // ----------------------------- void peer_connection::incoming_keepalive() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "KEEPALIVE"); #endif } // ----------------------------- // ----------- CHOKE ----------- // ----------------------------- void peer_connection::set_endgame(bool b) { TORRENT_ASSERT(is_single_thread()); if (m_endgame_mode == b) return; m_endgame_mode = b; if (m_endgame_mode) m_counters.inc_stats_counter(counters::num_peers_end_game); else m_counters.inc_stats_counter(counters::num_peers_end_game, -1); } void peer_connection::incoming_choke() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_choke()) return; } #endif if (is_disconnecting()) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "CHOKE"); #endif if (m_peer_choked == false) m_counters.inc_stats_counter(counters::num_peers_down_unchoked, -1); m_peer_choked = true; set_endgame(false); clear_request_queue(); } void peer_connection::clear_request_queue() { TORRENT_ASSERT(is_single_thread()); boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); if (!t->has_picker()) { m_request_queue.clear(); return; } // clear the requests that haven't been sent yet if (peer_info_struct() == 0 || !peer_info_struct()->on_parole) { // if the peer is not in parole mode, clear the queued // up block requests piece_picker& p = t->picker(); for (std::vector::const_iterator i = m_request_queue.begin() , end(m_request_queue.end()); i != end; ++i) { p.abort_download(i->block, peer_info_struct()); } m_request_queue.clear(); m_queued_time_critical = 0; } } namespace { bool match_request(peer_request const& r, piece_block const& b, int block_size) { if (int(b.piece_index) != r.piece) return false; if (int(b.block_index) != r.start / block_size) return false; if (r.start % block_size != 0) return false; return true; } } // ----------------------------- // -------- REJECT PIECE ------- // ----------------------------- void peer_connection::incoming_reject_request(peer_request const& r) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "REJECT_PIECE", "piece: %d s: %x l: %x" , r.piece, r.start, r.length); #endif #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_reject(r)) return; } #endif if (is_disconnecting()) return; std::vector::iterator dlq_iter = std::find_if( m_download_queue.begin(), m_download_queue.end() , boost::bind(match_request, boost::cref(r), boost::bind(&pending_block::block, _1) , t->block_size())); if (dlq_iter != m_download_queue.end()) { pending_block b = *dlq_iter; bool remove_from_picker = !dlq_iter->timed_out && !dlq_iter->not_wanted; m_download_queue.erase(dlq_iter); TORRENT_ASSERT(m_outstanding_bytes >= r.length); m_outstanding_bytes -= r.length; if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; if (m_download_queue.empty()) m_counters.inc_stats_counter(counters::num_peers_down_requests, -1); // if the peer is in parole mode, keep the request if (peer_info_struct() && peer_info_struct()->on_parole) { // we should only add it if the block is marked as // busy in the piece-picker if (remove_from_picker) m_request_queue.insert(m_request_queue.begin(), b); } else if (!t->is_seed() && remove_from_picker) { piece_picker& p = t->picker(); p.abort_download(b.block, peer_info_struct()); } #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif } #ifndef TORRENT_DISABLE_LOGGING else { peer_log(peer_log_alert::info, "REJECT_PIECE", "piece not in request queue"); } #endif if (has_peer_choked()) { // if we're choked and we got a rejection of // a piece in the allowed fast set, remove it // from the allow fast set. std::vector::iterator i = std::find( m_allowed_fast.begin(), m_allowed_fast.end(), r.piece); if (i != m_allowed_fast.end()) m_allowed_fast.erase(i); } else { std::vector::iterator i = std::find(m_suggested_pieces.begin() , m_suggested_pieces.end(), r.piece); if (i != m_suggested_pieces.end()) m_suggested_pieces.erase(i); } check_graceful_pause(); if (is_disconnecting()) return; if (m_request_queue.empty() && m_download_queue.size() < 2) { if (request_a_block(*t, *this)) m_counters.inc_stats_counter(counters::reject_piece_picks); send_block_requests(); } } // ----------------------------- // ------- SUGGEST PIECE ------- // ----------------------------- void peer_connection::incoming_suggest(int index) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "SUGGEST_PIECE" , "piece: %d", index); #endif boost::shared_ptr t = m_torrent.lock(); if (!t) return; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_suggest(index)) return; } #endif if (is_disconnecting()) return; if (index < 0) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "INVALID_SUGGEST_PIECE" , "%d", index); #endif return; } if (t->valid_metadata()) { if (index >= int(m_have_piece.size())) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "INVALID_SUGGEST" , "%d s: %d", index, int(m_have_piece.size())); #endif return; } // if we already have the piece, we can // ignore this message if (t->have_piece(index)) return; } // the piece picker will prioritize the pieces from the beginning to end. // the later the suggestion is received, the higher priority we should // ascribe to it, so we need to insert suggestions at the front of the // queue. if (int(m_suggested_pieces.size()) > m_settings.get_int(settings_pack::max_suggest_pieces)) m_suggested_pieces.resize(m_settings.get_int(settings_pack::max_suggest_pieces) - 1); m_suggested_pieces.insert(m_suggested_pieces.begin(), index); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SUGGEST_PIECE", "piece: %d added to set: %d" , index, int(m_suggested_pieces.size())); #endif } // ----------------------------- // ---------- UNCHOKE ---------- // ----------------------------- void peer_connection::incoming_unchoke() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); #ifndef TORRENT_DISABLE_LOGGING m_unchoke_time = clock_type::now(); t->debug_log("UNCHOKE [%p] (%d ms)", static_cast(this) , int(total_milliseconds(m_unchoke_time - m_bitfield_time))); #endif #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_unchoke()) return; } #endif #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "UNCHOKE"); #endif if (m_peer_choked) m_counters.inc_stats_counter(counters::num_peers_down_unchoked); m_peer_choked = false; m_last_unchoked = aux::time_now(); if (is_disconnecting()) return; if (is_interesting()) { if (request_a_block(*t, *this)) m_counters.inc_stats_counter(counters::unchoke_piece_picks); send_block_requests(); } } // ----------------------------- // -------- INTERESTED --------- // ----------------------------- void peer_connection::incoming_interested() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_interested()) return; } #endif #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "INTERESTED"); #endif if (m_peer_interested == false) m_counters.inc_stats_counter(counters::num_peers_up_interested); m_peer_interested = true; if (is_disconnecting()) return; // if the peer is ready to download stuff, it must have metadata m_has_metadata = true; disconnect_if_redundant(); if (is_disconnecting()) return; if (t->graceful_pause()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "UNCHOKE" , "did not unchoke, graceful pause mode"); #endif return; } if (!is_choked()) { // the reason to send an extra unchoke message here is that // because of the handshake-round-trip optimization, we may // end up sending an unchoke before the other end sends us // an interested message. This may confuse clients, not reacting // to the first unchoke, and then not check whether it's unchoked // when sending the interested message. If the other end's client // has this problem, sending another unchoke here will kick it // to react to the fact that it's unchoked. #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "UNCHOKE", "sending redundant unchoke"); #endif write_unchoke(); return; } maybe_unchoke_this_peer(); } void peer_connection::maybe_unchoke_this_peer() { TORRENT_ASSERT(is_single_thread()); if (ignore_unchoke_slots()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "UNCHOKE", "about to unchoke, peer ignores unchoke slots"); #endif // if this peer is expempted from the choker // just unchoke it immediately send_unchoke(); } else if (m_ses.preemptive_unchoke()) { // if the peer is choked and we have upload slots left, // then unchoke it. boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); t->unchoke_peer(*this); } #ifndef TORRENT_DISABLE_LOGGING else { peer_log(peer_log_alert::info, "UNCHOKE", "did not unchoke, the number of uploads (%d) " "is more than or equal to the available slots (%d), limit (%d)" , int(m_counters[counters::num_peers_up_unchoked]) , int(m_counters[counters::num_unchoke_slots]) , m_settings.get_int(settings_pack::unchoke_slots_limit)); } #endif } // ----------------------------- // ------ NOT INTERESTED ------- // ----------------------------- void peer_connection::incoming_not_interested() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_not_interested()) return; } #endif m_became_uninterested = aux::time_now(); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "NOT_INTERESTED"); #endif if (m_peer_interested) m_counters.inc_stats_counter(counters::num_peers_up_interested, -1); m_peer_interested = false; if (is_disconnecting()) return; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); choke_this_peer(); } void peer_connection::choke_this_peer() { TORRENT_ASSERT(is_single_thread()); if (is_choked()) return; if (ignore_unchoke_slots()) { send_choke(); return; } boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); if (m_peer_info && m_peer_info->optimistically_unchoked) { m_peer_info->optimistically_unchoked = false; m_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1); t->trigger_optimistic_unchoke(); } t->choke_peer(*this); t->trigger_unchoke(); } // ----------------------------- // ----------- HAVE ------------ // ----------------------------- void peer_connection::incoming_have(int index) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_have(index)) return; } #endif if (is_disconnecting()) return; // if we haven't received a bitfield, it was // probably omitted, which is the same as 'have_none' if (!m_bitfield_received) incoming_have_none(); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HAVE", "piece: %d", index); #endif if (is_disconnecting()) return; if (!t->valid_metadata() && index >= int(m_have_piece.size())) { if (index < 131072) { // if we don't have metadata // and we might not have received a bitfield // extend the bitmask to fit the new // have message m_have_piece.resize(index + 1, false); } else { // unless the index > 64k, in which case // we just ignore it return; } } // if we got an invalid message, abort if (index >= int(m_have_piece.size()) || index < 0) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ERROR", "have-metadata have_piece: %d size: %d" , index, int(m_have_piece.size())); #endif disconnect(errors::invalid_have, op_bittorrent, 2); return; } if (t->super_seeding() && !m_settings.get_bool(settings_pack::strict_super_seeding)) { // if we're super-seeding and the peer just told // us that it completed the piece we're super-seeding // to it, change the super-seeding piece for this peer // if the peer optimizes out redundant have messages // this will be handled when the peer sends not-interested // instead. if (super_seeded_piece(index)) { superseed_piece(index, t->get_piece_to_super_seed(m_have_piece)); } } if (m_have_piece[index]) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "HAVE" , "got redundant HAVE message for index: %d", index); #endif return; } m_have_piece.set_bit(index); ++m_num_pieces; // if the peer is downloading stuff, it must have metadata m_has_metadata = true; // only update the piece_picker if // we have the metadata and if // we're not a seed (in which case // we won't have a piece picker) if (!t->valid_metadata()) return; t->peer_has(index, this); // this will disregard all have messages we get within // the first two seconds. Since some clients implements // lazy bitfields, these will not be reliable to use // for an estimated peer download rate. if (!peer_info_struct() || m_ses.session_time() - peer_info_struct()->last_connected > 2) { // update bytes downloaded since last timer ++m_remote_pieces_dled; } // it's important to not disconnect before we have // updated the piece picker, otherwise we will incorrectly // decrement the piece count without first incrementing it if (is_seed()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SEED", "this is a seed. p: %p" , static_cast(m_peer_info)); #endif TORRENT_ASSERT(m_have_piece.all_set()); TORRENT_ASSERT(m_have_piece.count() == m_have_piece.size()); TORRENT_ASSERT(m_have_piece.size() == t->torrent_file().num_pieces()); t->seen_complete(); t->set_seed(m_peer_info, true); m_upload_only = true; #if TORRENT_USE_INVARIANT_CHECKS if (t && t->has_picker()) t->picker().check_peer_invariant(m_have_piece, peer_info_struct()); #endif if (disconnect_if_redundant()) return; } // it's important to update whether we're intersted in this peer before // calling disconnect_if_redundant, otherwise we may disconnect even if // we are interested if (!t->has_piece_passed(index) && !t->is_upload_only() && !is_interesting() && (!t->has_picker() || t->picker().piece_priority(index) != 0)) t->peer_is_interesting(*this); disconnect_if_redundant(); if (is_disconnecting()) return; // if we're super seeding, this might mean that somebody // forwarded this piece. In which case we need to give // a new piece to that peer if (t->super_seeding() && m_settings.get_bool(settings_pack::strict_super_seeding) && (!super_seeded_piece(index) || t->num_peers() == 1)) { for (torrent::peer_iterator i = t->begin() , end(t->end()); i != end; ++i) { peer_connection* p = *i; if (!p->super_seeded_piece(index)) continue; if (!p->has_piece(index)) continue; p->superseed_piece(index, t->get_piece_to_super_seed(p->get_bitfield())); } } } // ----------------------------- // -------- DONT HAVE ---------- // ----------------------------- void peer_connection::incoming_dont_have(int index) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_dont_have(index)) return; } #endif if (is_disconnecting()) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "DONT_HAVE", "piece: %d", index); #endif // if we got an invalid message, abort if (index >= int(m_have_piece.size()) || index < 0) { disconnect(errors::invalid_dont_have, op_bittorrent, 2); return; } if (!m_have_piece[index]) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "DONT_HAVE" , "got redundant DONT_HAVE message for index: %d", index); #endif return; } bool was_seed = is_seed(); m_have_piece.clear_bit(index); TORRENT_ASSERT(m_num_pieces > 0); --m_num_pieces; // only update the piece_picker if // we have the metadata and if // we're not a seed (in which case // we won't have a piece picker) if (!t->valid_metadata()) return; t->peer_lost(index, this); if (was_seed) t->set_seed(m_peer_info, false); } // ----------------------------- // --------- BITFIELD ---------- // ----------------------------- void peer_connection::incoming_bitfield(bitfield const& bits) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_bitfield(bits)) return; } #endif if (is_disconnecting()) return; #ifndef TORRENT_DISABLE_LOGGING std::string bitfield_str; bitfield_str.resize(bits.size()); for (int i = 0; i < int(bits.size()); ++i) bitfield_str[i] = bits[i] ? '1' : '0'; peer_log(peer_log_alert::incoming_message, "BITFIELD" , "%s", bitfield_str.c_str()); #endif // if we don't have the metedata, we cannot // verify the bitfield size if (t->valid_metadata() && bits.size() != int(m_have_piece.size())) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "BITFIELD" , "invalid size: %d expected %d", bits.size() , int(m_have_piece.size())); #endif disconnect(errors::invalid_bitfield_size, op_bittorrent, 2); return; } if (m_bitfield_received) { // if we've already received a bitfield message // we first need to count down all the pieces // we believe the peer has first t->peer_lost(m_have_piece, this); } m_bitfield_received = true; #ifndef TORRENT_DISABLE_LOGGING m_bitfield_time = clock_type::now(); t->debug_log("HANDSHAKE [%p] (%d ms)" , static_cast(this) , int(total_milliseconds(m_bitfield_time - m_connect_time))); #endif // if we don't have metadata yet // just remember the bitmask // don't update the piecepicker // (since it doesn't exist yet) if (!t->ready_for_connections()) { #ifndef TORRENT_DISABLE_LOGGING if (m_num_pieces == int(bits.size())) peer_log(peer_log_alert::info, "SEED", "this is a seed. p: %p" , static_cast(m_peer_info)); #endif m_have_piece = bits; m_num_pieces = bits.count(); t->set_seed(m_peer_info, m_num_pieces == int(bits.size())); #if TORRENT_USE_INVARIANT_CHECKS if (t && t->has_picker()) t->picker().check_peer_invariant(m_have_piece, peer_info_struct()); #endif return; } TORRENT_ASSERT(t->valid_metadata()); int num_pieces = bits.count(); if (num_pieces == int(m_have_piece.size())) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SEED", "this is a seed. p: %p" , static_cast(m_peer_info)); #endif t->set_seed(m_peer_info, true); m_upload_only = true; m_have_piece.set_all(); m_num_pieces = num_pieces; t->peer_has_all(this); TORRENT_ASSERT(m_have_piece.all_set()); TORRENT_ASSERT(m_have_piece.count() == m_have_piece.size()); TORRENT_ASSERT(m_have_piece.size() == t->torrent_file().num_pieces()); #if TORRENT_USE_INVARIANT_CHECKS if (t && t->has_picker()) t->picker().check_peer_invariant(m_have_piece, peer_info_struct()); #endif // this will cause us to send the INTERESTED message if (!t->is_upload_only()) t->peer_is_interesting(*this); disconnect_if_redundant(); return; } // let the torrent know which pieces the peer has if we're a seed, we // don't keep track of piece availability t->peer_has(bits, this); m_have_piece = bits; m_num_pieces = num_pieces; update_interest(); } bool peer_connection::disconnect_if_redundant() { TORRENT_ASSERT(is_single_thread()); if (m_disconnecting) return false; if (m_need_interest_update) return false; // we cannot disconnect in a constructor TORRENT_ASSERT(m_in_constructor == false); if (!m_settings.get_bool(settings_pack::close_redundant_connections)) return false; boost::shared_ptr t = m_torrent.lock(); if (!t) return false; // if we don't have the metadata yet, don't disconnect // also, if the peer doesn't have metadata we shouldn't // disconnect it, since it may want to request the // metadata from us if (!t->valid_metadata() || !has_metadata()) return false; // don't close connections in share mode, we don't know if we need them if (t->share_mode()) return false; if (m_upload_only && t->is_upload_only() && can_disconnect(errors::upload_upload_connection)) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "UPLOAD_ONLY", "the peer is upload-only and our torrent is also upload-only"); #endif disconnect(errors::upload_upload_connection, op_bittorrent); return true; } if (m_upload_only && !m_interesting && m_bitfield_received && t->are_files_checked() && can_disconnect(errors::uninteresting_upload_peer)) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "UPLOAD_ONLY", "the peer is upload-only and we're not interested in it"); #endif disconnect(errors::uninteresting_upload_peer, op_bittorrent); return true; } return false; } bool peer_connection::can_disconnect(error_code const& ec) const { TORRENT_ASSERT(is_single_thread()); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::const_iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if (!(*i)->can_disconnect(ec)) return false; } #else TORRENT_UNUSED(ec); #endif return true; } // ----------------------------- // ---------- REQUEST ---------- // ----------------------------- void peer_connection::incoming_request(peer_request const& r) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); torrent_info const& ti = t->torrent_file(); m_counters.inc_stats_counter(counters::piece_requests); #ifndef TORRENT_DISABLE_LOGGING const bool valid_piece_index = r.piece >= 0 && r.piece < int(ti.num_pieces()); peer_log(peer_log_alert::incoming_message, "REQUEST" , "piece: %d s: %x l: %x", r.piece, r.start, r.length); #endif if (t->super_seeding() && !super_seeded_piece(r.piece)) { m_counters.inc_stats_counter(counters::invalid_piece_requests); ++m_num_invalid_requests; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "INVALID_REQUEST", "piece not superseeded " "i: %d t: %d n: %d h: %d ss1: %d ss2: %d" , m_peer_interested , valid_piece_index ? int(ti.piece_size(r.piece)) : -1 , ti.num_pieces() , valid_piece_index ? t->has_piece_passed(r.piece) : 0 , m_superseed_piece[0] , m_superseed_piece[1]); #endif write_reject_request(r); if (t->alerts().should_post()) { // msvc 12 appears to deduce the rvalue reference template // incorrectly for bool temporaries. So, create a dummy instance bool peer_interested = bool(m_peer_interested); t->alerts().emplace_alert( t->get_handle(), m_remote, m_peer_id, r , t->has_piece_passed(r.piece), peer_interested, true); } return; } // if we haven't received a bitfield, it was // probably omitted, which is the same as 'have_none' if (!m_bitfield_received) incoming_have_none(); if (is_disconnecting()) return; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_request(r)) return; } #endif if (is_disconnecting()) return; if (!t->valid_metadata()) { m_counters.inc_stats_counter(counters::invalid_piece_requests); // if we don't have valid metadata yet, // we shouldn't get a request #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "INVALID_REQUEST", "we don't have metadata yet"); peer_log(peer_log_alert::outgoing_message, "REJECT_PIECE", "piece: %d s: %x l: %x no metadata" , r.piece, r.start, r.length); #endif write_reject_request(r); return; } if (int(m_requests.size()) > m_settings.get_int(settings_pack::max_allowed_in_request_queue)) { m_counters.inc_stats_counter(counters::max_piece_requests); // don't allow clients to abuse our // memory consumption. // ignore requests if the client // is making too many of them. #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "INVALID_REQUEST", "incoming request queue full %d" , int(m_requests.size())); peer_log(peer_log_alert::outgoing_message, "REJECT_PIECE", "piece: %d s: %x l: %x too many requests" , r.piece, r.start, r.length); #endif write_reject_request(r); return; } int fast_idx = -1; std::vector::iterator fast_iter = std::find(m_accept_fast.begin() , m_accept_fast.end(), r.piece); if (fast_iter != m_accept_fast.end()) fast_idx = fast_iter - m_accept_fast.begin(); if (!m_peer_interested) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "INVALID_REQUEST", "peer is not interested " " t: %d n: %d block_limit: %d" , valid_piece_index ? int(ti.piece_size(r.piece)) : -1 , ti.num_pieces() , t->block_size()); peer_log(peer_log_alert::info, "INTERESTED", "artificial incoming INTERESTED message"); #endif if (t->alerts().should_post()) { // msvc 12 appears to deduce the rvalue reference template // incorrectly for bool temporaries. So, create a dummy instance bool peer_interested = bool(m_peer_interested); t->alerts().emplace_alert( t->get_handle(), m_remote, m_peer_id, r , t->has_piece_passed(r.piece) , peer_interested, false); } // be lenient and pretend that the peer said it was interested incoming_interested(); } // make sure this request // is legal and that the peer // is not choked if (r.piece < 0 || r.piece >= ti.num_pieces() || (!t->has_piece_passed(r.piece) && !t->is_predictive_piece(r.piece) && !t->seed_mode()) || r.start < 0 || r.start >= ti.piece_size(r.piece) || r.length <= 0 || r.length + r.start > ti.piece_size(r.piece) || r.length > t->block_size()) { m_counters.inc_stats_counter(counters::invalid_piece_requests); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "INVALID_REQUEST" , "i: %d t: %d n: %d h: %d block_limit: %d" , m_peer_interested , valid_piece_index ? int(ti.piece_size(r.piece)) : -1 , ti.num_pieces() , t->has_piece_passed(r.piece) , t->block_size()); peer_log(peer_log_alert::outgoing_message, "REJECT_PIECE" , "piece: %d s: %d l: %d invalid request" , r.piece , r.start , r.length); #endif write_reject_request(r); ++m_num_invalid_requests; if (t->alerts().should_post()) { // msvc 12 appears to deduce the rvalue reference template // incorrectly for bool temporaries. So, create a dummy instance bool peer_interested = bool(m_peer_interested); t->alerts().emplace_alert( t->get_handle(), m_remote, m_peer_id, r , t->has_piece_passed(r.piece), peer_interested, false); } // every ten invalid request, remind the peer that it's choked if (!m_peer_interested && m_num_invalid_requests % 10 == 0 && m_choked) { // TODO: 2 this should probably be based on time instead of number // of request messages. For a very high throughput connection, 300 // may be a legitimate number of requests to have in flight when // getting choked if (m_num_invalid_requests > 300 && !m_peer_choked && can_disconnect(errors::too_many_requests_when_choked)) { disconnect(errors::too_many_requests_when_choked, op_bittorrent, 2); return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "CHOKE"); #endif write_choke(); } return; } // if we have choked the client // ignore the request const int blocks_per_piece = static_cast( (ti.piece_length() + t->block_size() - 1) / t->block_size()); // disconnect peers that downloads more than foo times an allowed // fast piece if (m_choked && fast_idx != -1 && m_accept_fast_piece_cnt[fast_idx] >= 3 * blocks_per_piece && can_disconnect(errors::too_many_requests_when_choked)) { disconnect(errors::too_many_requests_when_choked, op_bittorrent, 2); return; } if (m_choked && fast_idx == -1) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "REJECTING REQUEST", "peer choked and piece not in allowed fast set"); peer_log(peer_log_alert::outgoing_message, "REJECT_PIECE", "piece: %d s: %d l: %d peer choked" , r.piece, r.start, r.length); #endif m_counters.inc_stats_counter(counters::choked_piece_requests); write_reject_request(r); // allow peers to send request up to 2 seconds after getting choked, // then disconnect them if (aux::time_now() - seconds(2) > m_last_choke && can_disconnect(errors::too_many_requests_when_choked)) { disconnect(errors::too_many_requests_when_choked, op_bittorrent, 2); return; } } else { // increase the allowed fast set counter if (fast_idx != -1) ++m_accept_fast_piece_cnt[fast_idx]; if (m_requests.empty()) m_counters.inc_stats_counter(counters::num_peers_up_requests); TORRENT_ASSERT(t->valid_metadata()); TORRENT_ASSERT(r.piece >= 0); TORRENT_ASSERT(r.piece < ti.num_pieces()); m_requests.push_back(r); if (t->alerts().should_post()) { t->alerts().emplace_alert(r, t->get_handle() , m_remote, m_peer_id); } m_last_incoming_request = aux::time_now(); fill_send_buffer(); } } // reject all requests to this piece void peer_connection::reject_piece(int index) { TORRENT_ASSERT(is_single_thread()); for (std::vector::iterator i = m_requests.begin() , end(m_requests.end()); i != end; ++i) { peer_request const& r = *i; if (r.piece != index) continue; write_reject_request(r); i = m_requests.erase(i); if (m_requests.empty()) m_counters.inc_stats_counter(counters::num_peers_up_requests, -1); } } void peer_connection::incoming_piece_fragment(int bytes) { TORRENT_ASSERT(is_single_thread()); m_last_piece = aux::time_now(); TORRENT_ASSERT(m_outstanding_bytes >= bytes); m_outstanding_bytes -= bytes; if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; boost::shared_ptr t = associated_torrent().lock(); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_received_in_piece + bytes <= t->block_size()); m_received_in_piece += bytes; #endif // progress of this torrent increased t->state_updated(); #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif } void peer_connection::start_receive_piece(peer_request const& r) { TORRENT_ASSERT(is_single_thread()); #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif #if TORRENT_USE_ASSERTS buffer::const_interval recv_buffer = m_recv_buffer.get(); int recv_pos = recv_buffer.end - recv_buffer.begin; TORRENT_ASSERT(recv_pos >= 9); #endif boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); if (!verify_piece(r)) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "INVALID_PIECE", "piece: %d s: %d l: %d" , r.piece, r.start, r.length); #endif disconnect(errors::invalid_piece, op_bittorrent, 2); return; } piece_block b(r.piece, r.start / t->block_size()); m_receiving_block = b; bool in_req_queue = false; for (std::vector::iterator i = m_download_queue.begin() , end(m_download_queue.end()); i != end; ++i) { if (i->block != b) continue; in_req_queue = true; break; } // if this is not in the request queue, we have to // assume our outstanding bytes includes this piece too // if we're disconnecting, we shouldn't add pieces if (!in_req_queue && !m_disconnecting) { for (std::vector::iterator i = m_request_queue.begin() , end(m_request_queue.end()); i != end; ++i) { if (i->block != b) continue; in_req_queue = true; if (i - m_request_queue.begin() < m_queued_time_critical) --m_queued_time_critical; m_request_queue.erase(i); break; } if (m_download_queue.empty()) m_counters.inc_stats_counter(counters::num_peers_down_requests); m_download_queue.insert(m_download_queue.begin(), b); if (!in_req_queue) { if (t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle() , m_remote, m_peer_id, int(b.block_index), int(b.piece_index)); } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "INVALID_REQUEST" , "The block we just got was not in the request queue"); #endif TORRENT_ASSERT(m_download_queue.front().block == b); m_download_queue.front().not_wanted = true; } m_outstanding_bytes += r.length; } } #if TORRENT_USE_INVARIANT_CHECKS struct check_postcondition { check_postcondition(boost::shared_ptr const& t_ , bool init_check = true): t(t_) { if (init_check) check(); } ~check_postcondition() { check(); } void check() { if (!t->is_seed()) { const int blocks_per_piece = static_cast( (t->torrent_file().piece_length() + t->block_size() - 1) / t->block_size()); std::vector const& dl_queue = t->picker().get_download_queue(); for (std::vector::const_iterator i = dl_queue.begin(); i != dl_queue.end(); ++i) { TORRENT_ASSERT(i->finished <= blocks_per_piece); } } } shared_ptr t; }; #endif // ----------------------------- // ----------- PIECE ----------- // ----------------------------- void peer_connection::incoming_piece(peer_request const& p, char const* data) { TORRENT_ASSERT(is_single_thread()); bool exceeded = false; char* buffer = m_allocator.allocate_disk_buffer(exceeded, self(), "receive buffer"); if (buffer == 0) { disconnect(errors::no_memory, op_alloc_recvbuf); return; } // every peer is entitled to have two disk blocks allocated at any given // time, regardless of whether the cache size is exceeded or not. If this // was not the case, when the cache size setting is very small, most peers // would be blocked most of the time, because the disk cache would // continously be in exceeded state. Only rarely would it actually drop // down to 0 and unblock all peers. if (exceeded && m_outstanding_writing_bytes > 0) { if ((m_channel_state[download_channel] & peer_info::bw_disk) == 0) m_counters.inc_stats_counter(counters::num_peers_down_disk); m_channel_state[download_channel] |= peer_info::bw_disk; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "DISK", "exceeded disk buffer watermark"); #endif } disk_buffer_holder holder(m_allocator, buffer); std::memcpy(buffer, data, p.length); incoming_piece(p, holder); } void peer_connection::incoming_piece(peer_request const& p, disk_buffer_holder& data) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); m_recv_buffer.assert_no_disk_buffer(); // we're not receiving any block right now m_receiving_block = piece_block::invalid; #ifdef TORRENT_CORRUPT_DATA // corrupt all pieces from certain peers if (m_remote.address().is_v4() && (m_remote.address().to_v4().to_ulong() & 0xf) == 0) { data.get()[0] = ~data.get()[0]; } #endif // if we haven't received a bitfield, it was // probably omitted, which is the same as 'have_none' if (!m_bitfield_received) incoming_have_none(); if (is_disconnecting()) return; // slow-start if (m_slow_start) m_desired_queue_size += 1; update_desired_queue_size(); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_piece(p, data)) { #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_received_in_piece == p.length); m_received_in_piece = 0; #endif return; } } #endif if (is_disconnecting()) return; #if TORRENT_USE_INVARIANT_CHECKS check_postcondition post_checker_(t); #if defined TORRENT_EXPENSIVE_INVARIANT_CHECKS t->check_invariant(); #endif #endif #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "PIECE", "piece: %d s: %x l: %x ds: %d qs: %d q: %d" , p.piece, p.start, p.length, statistics().download_rate() , int(m_desired_queue_size), int(m_download_queue.size())); #endif if (p.length == 0) { if (t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle(), m_remote , m_peer_id, op_bittorrent, errors::peer_sent_empty_piece); } // This is used as a reject-request by bitcomet incoming_reject_request(p); return; } // if we're already seeding, don't bother, // just ignore it if (t->is_seed()) { #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_received_in_piece == p.length); m_received_in_piece = 0; #endif if (!m_download_queue.empty()) { m_download_queue.erase(m_download_queue.begin()); if (m_download_queue.empty()) m_counters.inc_stats_counter(counters::num_peers_down_requests, -1); } t->add_redundant_bytes(p.length, torrent::piece_seed); return; } time_point now = clock_type::now(); t->need_picker(); piece_picker& picker = t->picker(); piece_block block_finished(p.piece, p.start / t->block_size()); TORRENT_ASSERT(verify_piece(p)); std::vector::iterator b = std::find_if( m_download_queue.begin() , m_download_queue.end() , has_block(block_finished)); if (b == m_download_queue.end()) { if (t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle() , m_remote, m_peer_id, int(block_finished.block_index) , int(block_finished.piece_index)); } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "INVALID_REQUEST", "The block we just got was not in the request queue"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT_VAL(m_received_in_piece == p.length, m_received_in_piece); m_received_in_piece = 0; #endif t->add_redundant_bytes(p.length, torrent::piece_unknown); // the bytes of the piece we just completed have been deducted from // m_outstanding_bytes as we received it, in incoming_piece_fragment. // however, it now turns out the piece we received wasn't in the // download queue, so we still have the same number of pieces in the // download queue, which is why we need to add the bytes back. m_outstanding_bytes += p.length; #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif return; } #if TORRENT_USE_ASSERTS TORRENT_ASSERT_VAL(m_received_in_piece == p.length, m_received_in_piece); m_received_in_piece = 0; #endif // if the block we got is already finished, then ignore it if (picker.is_downloaded(block_finished)) { torrent::wasted_reason_t reason; if (b->timed_out) reason = torrent::piece_timed_out; else if (b->not_wanted) reason = torrent::piece_cancelled; else if (b->busy) reason = torrent::piece_end_game; else reason = torrent::piece_unknown; t->add_redundant_bytes(p.length, reason); m_download_queue.erase(b); if (m_download_queue.empty()) m_counters.inc_stats_counter(counters::num_peers_down_requests, -1); if (m_disconnecting) return; m_request_time.add_sample(total_milliseconds(now - m_requested)); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "REQUEST_TIME", "%d +- %d ms" , m_request_time.mean(), m_request_time.avg_deviation()); #endif // we completed an incoming block, and there are still outstanding // requests. The next block we expect to receive now has another // timeout period until we time out. So, reset the timer. if (!m_download_queue.empty()) m_requested = now; if (request_a_block(*t, *this)) m_counters.inc_stats_counter(counters::incoming_redundant_piece_picks); send_block_requests(); return; } // we received a request within the timeout, make sure this peer is // not snubbed anymore if (total_seconds(now - m_requested) < request_timeout() && m_snubbed) { m_snubbed = false; if (t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle() , m_remote, m_peer_id); } } #ifndef TORRENT_DISABLE_LOGGING t->debug_log("PIECE [%p] (%d ms) (%d)", static_cast(this) , int(total_milliseconds(clock_type::now() - m_unchoke_time)), t->num_have()); peer_log(peer_log_alert::info, "FILE_ASYNC_WRITE", "piece: %d s: %x l: %x" , p.piece, p.start, p.length); #endif m_download_queue.erase(b); if (m_download_queue.empty()) m_counters.inc_stats_counter(counters::num_peers_down_requests, -1); if (t->is_deleted()) return; if (!t->need_loaded()) { t->add_redundant_bytes(p.length, torrent::piece_unknown); return; } t->inc_refcount("async_write"); m_disk_thread.async_write(&t->storage(), p, data , boost::bind(&peer_connection::on_disk_write_complete , self(), _1, p, t)); boost::uint64_t write_queue_size = m_counters.inc_stats_counter( counters::queued_write_bytes, p.length); m_outstanding_writing_bytes += p.length; boost::uint64_t max_queue_size = m_settings.get_int( settings_pack::max_queued_disk_bytes); if (write_queue_size > max_queue_size && write_queue_size - p.length < max_queue_size && m_settings.get_int(settings_pack::cache_size) > 5 && t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle() , performance_alert::too_high_disk_queue_limit); } m_request_time.add_sample(total_milliseconds(now - m_requested)); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "REQUEST_TIME", "%d +- %d ms" , m_request_time.mean(), m_request_time.avg_deviation()); #endif // we completed an incoming block, and there are still outstanding // requests. The next block we expect to receive now has another // timeout period until we time out. So, reset the timer. if (!m_download_queue.empty()) m_requested = now; bool was_finished = picker.is_piece_finished(p.piece); // did we request this block from any other peers? bool multi = picker.num_peers(block_finished) > 1; // fprintf(stderr, "peer_connection mark_as_writing peer: %p piece: %d block: %d\n" // , peer_info_struct(), block_finished.piece_index, block_finished.block_index); picker.mark_as_writing(block_finished, peer_info_struct()); TORRENT_ASSERT(picker.num_peers(block_finished) == 0); // if we requested this block from other peers, cancel it now if (multi) t->cancel_block(block_finished); if (m_settings.get_int(settings_pack::predictive_piece_announce)) { int piece = block_finished.piece_index; piece_picker::downloading_piece st; t->picker().piece_info(piece, st); int num_blocks = t->picker().blocks_in_piece(piece); if (st.requested > 0 && st.writing + st.finished + st.requested == num_blocks) { std::vector d; t->picker().get_downloaders(d, piece); if (d.size() == 1) { // only make predictions if all remaining // blocks are requested from the same peer torrent_peer* peer = d[0]; if (peer->connection) { // we have a connection. now, what is the current // download rate from this peer, and how many blocks // do we have left to download? boost::int64_t rate = peer->connection->statistics().download_payload_rate(); boost::int64_t bytes_left = boost::int64_t(st.requested) * t->block_size(); // the settings unit is milliseconds, so calculate the // number of milliseconds worth of bytes left in the piece if (rate > 1000 && (bytes_left * 1000) / rate < m_settings.get_int(settings_pack::predictive_piece_announce)) { // we predict we will complete this piece very soon. t->predicted_have_piece(piece, (bytes_left * 1000) / rate); } } } } } TORRENT_ASSERT(picker.num_peers(block_finished) == 0); #if TORRENT_USE_INVARIANT_CHECKS \ && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS t->check_invariant(); #endif #if TORRENT_USE_ASSERTS piece_picker::downloading_piece pi; picker.piece_info(p.piece, pi); int num_blocks = picker.blocks_in_piece(p.piece); TORRENT_ASSERT(pi.writing + pi.finished + pi.requested <= num_blocks); TORRENT_ASSERT(picker.is_piece_finished(p.piece) == (pi.writing + pi.finished == num_blocks)); #endif // did we just finish the piece? // this means all blocks are either written // to disk or are in the disk write cache if (picker.is_piece_finished(p.piece) && !was_finished) { #if TORRENT_USE_INVARIANT_CHECKS check_postcondition post_checker2_(t, false); #endif t->verify_piece(p.piece); } check_graceful_pause(); if (is_disconnecting()) return; if (request_a_block(*t, *this)) m_counters.inc_stats_counter(counters::incoming_piece_picks); send_block_requests(); } void peer_connection::check_graceful_pause() { // TODO: 3 instead of having to ask the torrent whether it's in graceful // pause mode or not, the peers should keep that state (and the torrent // should update them when it enters graceful pause). When a peer enters // graceful pause mode, it should cancel all outstanding requests and // clear its request queue. boost::shared_ptr t = m_torrent.lock(); if (!t || !t->graceful_pause()) return; if (m_outstanding_bytes > 0) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "GRACEFUL_PAUSE", "NO MORE DOWNLOAD"); #endif disconnect(errors::torrent_paused, op_bittorrent); } void peer_connection::on_disk_write_complete(disk_io_job const* j , peer_request p, boost::shared_ptr t) { TORRENT_ASSERT(is_single_thread()); torrent_ref_holder h(t.get(), "async_write"); if (t) t->dec_refcount("async_write"); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "FILE_ASYNC_WRITE_COMPLETE", "ret: %d piece: %d s: %x l: %x e: %s" , j->ret, p.piece, p.start, p.length, j->error.ec.message().c_str()); #endif m_counters.inc_stats_counter(counters::queued_write_bytes, -p.length); m_outstanding_writing_bytes -= p.length; TORRENT_ASSERT(m_outstanding_writing_bytes >= 0); // every peer is entitled to allocate a disk buffer if it has no writes outstanding // see the comment in incoming_piece if (m_outstanding_writing_bytes == 0 && m_channel_state[download_channel] & peer_info::bw_disk) { m_counters.inc_stats_counter(counters::num_peers_down_disk, -1); m_channel_state[download_channel] &= ~peer_info::bw_disk; } // flush send buffer at the end of // this burst of disk events // m_ses.cork_burst(this); INVARIANT_CHECK; if (!t) { disconnect(j->error.ec, op_file_write); return; } t->schedule_storage_tick(); // in case the outstanding bytes just dropped down // to allow to receive more data setup_receive(); piece_block block_finished(p.piece, p.start / t->block_size()); if (j->ret < 0) { // handle_disk_error may disconnect us t->handle_disk_error(j, this); return; } TORRENT_ASSERT(j->ret == p.length); if (!t->has_picker()) return; piece_picker& picker = t->picker(); TORRENT_ASSERT(p.piece == j->piece); TORRENT_ASSERT(p.start == j->d.io.offset); TORRENT_ASSERT(picker.num_peers(block_finished) == 0); if (j->ret == -1 && j->error.ec == boost::system::errc::operation_canceled) { picker.mark_as_canceled(block_finished, peer_info_struct()); TORRENT_ASSERT(false); // how do we get here? return; } // fprintf(stderr, "peer_connection mark_as_finished peer: %p piece: %d block: %d\n" // , peer_info_struct(), block_finished.piece_index, block_finished.block_index); picker.mark_as_finished(block_finished, peer_info_struct()); t->maybe_done_flushing(); if (t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle(), remote(), pid(), int(block_finished.block_index) , int(block_finished.piece_index)); } disconnect_if_redundant(); if (m_disconnecting) return; #if TORRENT_USE_ASSERTS if (t->has_picker()) { const std::vector& q = picker.get_download_queue(); for (std::vector::const_iterator i = q.begin(), end(q.end()); i != end; ++i) { if (i->index != block_finished.piece_index) continue; piece_picker::block_info* info = picker.blocks_for_piece(*i); TORRENT_ASSERT(info[block_finished.block_index].state == piece_picker::block_info::state_finished); } } #endif if (t->is_aborted()) return; } // ----------------------------- // ---------- CANCEL ----------- // ----------------------------- void peer_connection::incoming_cancel(peer_request const& r) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_cancel(r)) return; } #endif if (is_disconnecting()) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "CANCEL" , "piece: %d s: %x l: %x", r.piece, r.start, r.length); #endif std::vector::iterator i = std::find(m_requests.begin(), m_requests.end(), r); if (i != m_requests.end()) { m_counters.inc_stats_counter(counters::cancelled_piece_requests); m_requests.erase(i); if (m_requests.empty()) m_counters.inc_stats_counter(counters::num_peers_up_requests, -1); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "REJECT_PIECE", "piece: %d s: %x l: %x cancelled" , r.piece , r.start , r.length); #endif write_reject_request(r); } else { // TODO: 2 since we throw away the queue entry once we issue // the disk job, this may happen. Instead, we should keep the // queue entry around, mark it as having been requested from // disk and once the disk job comes back, discard it if it has // been cancelled. Maybe even be able to cancel disk jobs? #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "INVALID_CANCEL", "got cancel not in the queue"); #endif } } // ----------------------------- // --------- DHT PORT ---------- // ----------------------------- void peer_connection::incoming_dht_port(int listen_port) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "DHT_PORT", "p: %d", listen_port); #endif #ifndef TORRENT_DISABLE_DHT m_ses.add_dht_node(udp::endpoint( m_remote.address(), listen_port)); #else TORRENT_UNUSED(listen_port); #endif } // ----------------------------- // --------- HAVE ALL ---------- // ----------------------------- void peer_connection::incoming_have_all() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); // we cannot disconnect in a constructor, and // this function may end up doing that TORRENT_ASSERT(m_in_constructor == false); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HAVE_ALL"); #endif #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_have_all()) return; } #endif if (is_disconnecting()) return; if (m_bitfield_received) t->peer_lost(m_have_piece, this); m_have_all = true; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SEED", "this is a seed p: %p" , static_cast(m_peer_info)); #endif t->set_seed(m_peer_info, true); m_upload_only = true; m_bitfield_received = true; #ifndef TORRENT_DISABLE_LOGGING m_bitfield_time = clock_type::now(); t->debug_log("HANDSHAKE [%p] (%d ms)" , static_cast(this) , int(total_milliseconds(m_bitfield_time - m_connect_time))); #endif // if we don't have metadata yet // just remember the bitmask // don't update the piecepicker // (since it doesn't exist yet) if (!t->ready_for_connections()) { // assume seeds are interesting when we // don't even have the metadata t->peer_is_interesting(*this); disconnect_if_redundant(); return; } TORRENT_ASSERT(!m_have_piece.empty()); m_have_piece.set_all(); m_num_pieces = m_have_piece.size(); t->peer_has_all(this); #if TORRENT_USE_INVARIANT_CHECKS if (t && t->has_picker()) t->picker().check_peer_invariant(m_have_piece, peer_info_struct()); #endif TORRENT_ASSERT(m_have_piece.all_set()); TORRENT_ASSERT(m_have_piece.count() == m_have_piece.size()); TORRENT_ASSERT(m_have_piece.size() == t->torrent_file().num_pieces()); // if we're finished, we're not interested if (t->is_upload_only()) send_not_interested(); else t->peer_is_interesting(*this); disconnect_if_redundant(); } // ----------------------------- // --------- HAVE NONE --------- // ----------------------------- void peer_connection::incoming_have_none() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "HAVE_NONE"); #endif boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_have_none()) return; } #endif if (is_disconnecting()) return; if (m_bitfield_received) t->peer_lost(m_have_piece, this); t->set_seed(m_peer_info, false); m_bitfield_received = true; #ifndef TORRENT_DISABLE_LOGGING m_bitfield_time = clock_type::now(); t->debug_log("HANDSHAKE [%p] (%d ms)" , static_cast(this) , int(total_milliseconds(m_bitfield_time - m_connect_time))); #endif m_have_piece.clear_all(); m_num_pieces = 0; // if the peer is ready to download stuff, it must have metadata m_has_metadata = true; // we're never interested in a peer that doesn't have anything send_not_interested(); TORRENT_ASSERT(!m_have_piece.empty() || !t->ready_for_connections()); disconnect_if_redundant(); } // ----------------------------- // ------- ALLOWED FAST -------- // ----------------------------- void peer_connection::incoming_allowed_fast(int index) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); #ifndef TORRENT_DISABLE_LOGGING { time_point now = clock_type::now(); t->debug_log("ALLOW FAST [%p] (%d ms)" , static_cast(this) , int(total_milliseconds(now - m_connect_time))); if (m_peer_choked) m_unchoke_time = now; } peer_log(peer_log_alert::incoming_message, "ALLOWED_FAST", "%d", index); #endif #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((*i)->on_allowed_fast(index)) return; } #endif if (is_disconnecting()) return; if (index < 0) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "INVALID_ALLOWED_FAST" , "%d", index); #endif return; } if (t->valid_metadata()) { if (index >= int(m_have_piece.size())) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "INVALID_ALLOWED_FAST" , "%d s: %d", index, int(m_have_piece.size())); #endif return; } // if we already have the piece, we can // ignore this message if (t->have_piece(index)) return; } // if we don't have the metadata, we'll verify // this piece index later m_allowed_fast.push_back(index); // if the peer has the piece and we want // to download it, request it if (int(m_have_piece.size()) > index && m_have_piece[index] && !t->has_piece_passed(index) && t->valid_metadata() && t->has_picker() && t->picker().piece_priority(index) > 0) { t->peer_is_interesting(*this); } } std::vector const& peer_connection::allowed_fast() { TORRENT_ASSERT(is_single_thread()); boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); // TODO: sort the allowed fast set in priority order return m_allowed_fast; } bool peer_connection::can_request_time_critical() const { TORRENT_ASSERT(is_single_thread()); if (has_peer_choked() || !is_interesting()) return false; if (int(m_download_queue.size()) + int(m_request_queue.size()) > m_desired_queue_size * 2) return false; if (on_parole()) return false; if (m_disconnecting) return false; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); if (t->upload_mode()) return false; // ignore snubbed peers, since they're not likely to return pieces in a // timely manner anyway if (m_snubbed) return false; return true; } bool peer_connection::make_time_critical(piece_block const& block) { TORRENT_ASSERT(is_single_thread()); std::vector::iterator rit = std::find_if(m_request_queue.begin() , m_request_queue.end(), has_block(block)); if (rit == m_request_queue.end()) return false; #if TORRENT_USE_ASSERTS boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(t->has_picker()); TORRENT_ASSERT(t->picker().is_requested(block)); #endif // ignore it if it's already time critical if (rit - m_request_queue.begin() < m_queued_time_critical) return false; pending_block b = *rit; m_request_queue.erase(rit); m_request_queue.insert(m_request_queue.begin() + m_queued_time_critical, b); ++m_queued_time_critical; return true; } bool peer_connection::add_request(piece_block const& block, int flags) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(!m_disconnecting); TORRENT_ASSERT(t->valid_metadata()); TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(int(block.piece_index) < t->torrent_file().num_pieces()); TORRENT_ASSERT(int(block.block_index) < t->torrent_file().piece_size(block.piece_index)); TORRENT_ASSERT(!t->picker().is_requested(block) || (t->picker().num_peers(block) > 0)); TORRENT_ASSERT(!t->have_piece(block.piece_index)); TORRENT_ASSERT(std::find_if(m_download_queue.begin(), m_download_queue.end() , has_block(block)) == m_download_queue.end()); TORRENT_ASSERT(std::find(m_request_queue.begin(), m_request_queue.end() , block) == m_request_queue.end()); if (t->upload_mode()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "PIECE_PICKER" , "not_picking: %d,%d upload_mode" , block.piece_index, block.block_index); #endif return false; } if (m_disconnecting) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "PIECE_PICKER" , "not_picking: %d,%d disconnecting" , block.piece_index, block.block_index); #endif return false; } if ((flags & req_busy) && !(flags & req_time_critical)) { // this block is busy (i.e. it has been requested // from another peer already). Only allow one busy // request in the pipeline at the time // this rule does not apply to time critical pieces, // in which case we are allowed to pick more than one // busy blocks for (std::vector::const_iterator i = m_download_queue.begin() , end(m_download_queue.end()); i != end; ++i) { if (i->busy) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "PIECE_PICKER" , "not_picking: %d,%d already in download queue & busy" , block.piece_index, block.block_index); #endif return false; } } for (std::vector::const_iterator i = m_request_queue.begin() , end(m_request_queue.end()); i != end; ++i) { if (i->busy) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "PIECE_PICKER" , "not_picking: %d,%d already in request queue & busy" , block.piece_index, block.block_index); #endif return false; } } } if (!t->picker().mark_as_downloading(block, peer_info_struct() , picker_options())) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "PIECE_PICKER" , "not_picking: %d,%d failed to mark_as_downloading" , block.piece_index, block.block_index); #endif return false; } if (t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle() , remote(), pid(), block.block_index, block.piece_index); } pending_block pb(block); pb.busy = (flags & req_busy) ? true : false; if (flags & req_time_critical) { m_request_queue.insert(m_request_queue.begin() + m_queued_time_critical , pb); ++m_queued_time_critical; } else { m_request_queue.push_back(pb); } return true; } void peer_connection::cancel_all_requests() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); // this peer might be disconnecting if (!t) return; TORRENT_ASSERT(t->valid_metadata()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CANCEL_ALL_REQUESTS"); #endif while (!m_request_queue.empty()) { t->picker().abort_download(m_request_queue.back().block, peer_info_struct()); m_request_queue.pop_back(); } m_queued_time_critical = 0; // make a local temporary copy of the download queue, since it // may be modified when we call write_cancel (for peers that don't // support the FAST extensions). std::vector temp_copy = m_download_queue; for (std::vector::iterator i = temp_copy.begin() , end(temp_copy.end()); i != end; ++i) { piece_block b = i->block; int block_offset = b.block_index * t->block_size(); int block_size = (std::min)(t->torrent_file().piece_size(b.piece_index)-block_offset, t->block_size()); TORRENT_ASSERT(block_size > 0); TORRENT_ASSERT(block_size <= t->block_size()); // we can't cancel the piece if we've started receiving it if (m_receiving_block == b) continue; peer_request r; r.piece = b.piece_index; r.start = block_offset; r.length = block_size; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "CANCEL" , "piece: %d s: %d l: %d b: %d" , b.piece_index, block_offset, block_size, b.block_index); #endif write_cancel(r); } } void peer_connection::cancel_request(piece_block const& block, bool force) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); // this peer might be disconnecting if (!t) return; TORRENT_ASSERT(t->valid_metadata()); TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(int(block.piece_index) < t->torrent_file().num_pieces()); TORRENT_ASSERT(int(block.block_index) < t->torrent_file().piece_size(block.piece_index)); // if all the peers that requested this block has been // cancelled, then just ignore the cancel. if (!t->picker().is_requested(block)) return; std::vector::iterator it = std::find_if(m_download_queue.begin(), m_download_queue.end(), has_block(block)); if (it == m_download_queue.end()) { std::vector::iterator rit = std::find_if(m_request_queue.begin() , m_request_queue.end(), has_block(block)); // when a multi block is received, it is cancelled // from all peers, so if this one hasn't requested // the block, just ignore to cancel it. if (rit == m_request_queue.end()) return; if (rit - m_request_queue.begin() < m_queued_time_critical) --m_queued_time_critical; t->picker().abort_download(block, peer_info_struct()); m_request_queue.erase(rit); // 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; } int block_offset = block.block_index * t->block_size(); int block_size = (std::min)(t->torrent_file().piece_size(block.piece_index)-block_offset, t->block_size()); TORRENT_ASSERT(block_size > 0); TORRENT_ASSERT(block_size <= t->block_size()); it->not_wanted = true; if (force) t->picker().abort_download(block, peer_info_struct()); if (m_outstanding_bytes < block_size) return; peer_request r; r.piece = block.piece_index; r.start = block_offset; r.length = block_size; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "CANCEL" , "piece: %d s: %d l: %d b: %d" , block.piece_index, block_offset, block_size, block.block_index); #endif write_cancel(r); } bool peer_connection::send_choke() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(!is_connecting()); if (m_choked) { TORRENT_ASSERT(m_peer_info == NULL || m_peer_info->optimistically_unchoked == false); return false; } if (m_peer_info && m_peer_info->optimistically_unchoked) { m_peer_info->optimistically_unchoked = false; m_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1); } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "CHOKE"); #endif write_choke(); m_counters.inc_stats_counter(counters::num_peers_up_unchoked_all, -1); if (!ignore_unchoke_slots()) m_counters.inc_stats_counter(counters::num_peers_up_unchoked, -1); m_choked = true; m_last_choke = aux::time_now(); m_num_invalid_requests = 0; // reject the requests we have in the queue // except the allowed fast pieces for (std::vector::iterator i = m_requests.begin(); i != m_requests.end();) { if (std::find(m_accept_fast.begin(), m_accept_fast.end(), i->piece) != m_accept_fast.end()) { ++i; continue; } peer_request const& r = *i; m_counters.inc_stats_counter(counters::choked_piece_requests); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "REJECT_PIECE" , "piece: %d s: %d l: %d choking" , r.piece , r.start , r.length); #endif write_reject_request(r); i = m_requests.erase(i); if (m_requests.empty()) m_counters.inc_stats_counter(counters::num_peers_up_requests, -1); } return true; } bool peer_connection::send_unchoke() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (!m_choked) return false; boost::shared_ptr t = m_torrent.lock(); if (!t->ready_for_connections()) return false; if (!m_sent_suggests) { std::vector const& ret = t->get_suggested_pieces(); for (std::vector::const_iterator i = ret.begin() , end(ret.end()); i != end; ++i) { TORRENT_ASSERT(i->piece_index >= 0); // this can happen if a piece fail to be // flushed to disk for whatever reason if (!t->has_piece_passed(i->piece_index)) continue; send_suggest(i->piece_index); } m_sent_suggests = true; } m_last_unchoke = aux::time_now(); write_unchoke(); m_counters.inc_stats_counter(counters::num_peers_up_unchoked_all); if (!ignore_unchoke_slots()) m_counters.inc_stats_counter(counters::num_peers_up_unchoked); m_choked = false; m_uploaded_at_last_unchoke = m_statistics.total_payload_upload(); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "UNCHOKE"); #endif return true; } void peer_connection::send_interested() { TORRENT_ASSERT(is_single_thread()); if (m_interesting) return; boost::shared_ptr t = m_torrent.lock(); if (!t->ready_for_connections()) return; m_interesting = true; m_counters.inc_stats_counter(counters::num_peers_down_interested); write_interested(); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "INTERESTED"); #endif } void peer_connection::send_not_interested() { TORRENT_ASSERT(is_single_thread()); // we cannot disconnect in a constructor, and // this function may end up doing that TORRENT_ASSERT(m_in_constructor == false); if (!m_interesting) { disconnect_if_redundant(); return; } boost::shared_ptr t = m_torrent.lock(); if (!t->ready_for_connections()) return; m_interesting = false; m_slow_start = false; m_counters.inc_stats_counter(counters::num_peers_down_interested, -1); disconnect_if_redundant(); if (m_disconnecting) return; write_not_interested(); m_became_uninteresting = aux::time_now(); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "NOT_INTERESTED"); #endif } void peer_connection::send_suggest(int piece) { TORRENT_ASSERT(is_single_thread()); if (m_connecting) return; if (in_handshake()) return; // don't suggest a piece that the peer already has // don't suggest anything to a peer that isn't interested if (has_piece(piece) || !m_peer_interested) return; // we cannot suggest a piece we don't have! #if TORRENT_USE_ASSERTS { boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(t->has_piece_passed(piece)); TORRENT_ASSERT(piece >= 0 && piece < t->torrent_file().num_pieces()); } #endif if (m_sent_suggested_pieces.empty()) { boost::shared_ptr t = m_torrent.lock(); m_sent_suggested_pieces.resize(t->torrent_file().num_pieces(), false); } TORRENT_ASSERT(piece >= 0 && piece < m_sent_suggested_pieces.size()); if (m_sent_suggested_pieces[piece]) return; m_sent_suggested_pieces.set_bit(piece); write_suggest(piece); } void peer_connection::send_block_requests() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); if (m_disconnecting) return; // TODO: 3 once peers are properly put in graceful pause mode, they can // cancel all outstanding requests and this test can be removed. if (t->graceful_pause()) return; // we can't download pieces in these states if (t->state() == torrent_status::checking_files || t->state() == torrent_status::checking_resume_data || t->state() == torrent_status::downloading_metadata || t->state() == torrent_status::allocating) return; if (int(m_download_queue.size()) >= m_desired_queue_size || t->upload_mode()) return; bool empty_download_queue = m_download_queue.empty(); while (!m_request_queue.empty() && (int(m_download_queue.size()) < m_desired_queue_size || m_queued_time_critical > 0)) { pending_block block = m_request_queue.front(); m_request_queue.erase(m_request_queue.begin()); if (m_queued_time_critical) --m_queued_time_critical; // if we're a seed, we don't have a piece picker // so we don't have to worry about invariants getting // out of sync with it if (!t->has_picker()) continue; // this can happen if a block times out, is re-requested and // then arrives "unexpectedly" if (t->picker().is_finished(block.block) || t->picker().is_downloaded(block.block)) { t->picker().abort_download(block.block, peer_info_struct()); continue; } int block_offset = block.block.block_index * t->block_size(); int block_size = (std::min)(t->torrent_file().piece_size( block.block.piece_index) - block_offset, t->block_size()); TORRENT_ASSERT(block_size > 0); TORRENT_ASSERT(block_size <= t->block_size()); peer_request r; r.piece = block.block.piece_index; r.start = block_offset; r.length = block_size; if (m_download_queue.empty()) m_counters.inc_stats_counter(counters::num_peers_down_requests); TORRENT_ASSERT(verify_piece(t->to_req(block.block))); block.send_buffer_offset = m_send_buffer.size(); m_download_queue.push_back(block); m_outstanding_bytes += block_size; #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif // if we are requesting large blocks, merge the smaller // blocks that are in the same piece into larger requests if (m_request_large_blocks) { int blocks_per_piece = t->torrent_file().piece_length() / t->block_size(); while (!m_request_queue.empty()) { // check to see if this block is connected to the previous one // if it is, merge them, otherwise, break this merge loop pending_block const& front = m_request_queue.front(); if (front.block.piece_index * blocks_per_piece + front.block.block_index != block.block.piece_index * blocks_per_piece + block.block.block_index + 1) break; block = m_request_queue.front(); m_request_queue.erase(m_request_queue.begin()); TORRENT_ASSERT(verify_piece(t->to_req(block.block))); if (m_download_queue.empty()) m_counters.inc_stats_counter(counters::num_peers_down_requests); block.send_buffer_offset = m_send_buffer.size(); m_download_queue.push_back(block); if (m_queued_time_critical) --m_queued_time_critical; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "MERGING_REQUEST" , "piece: %d block: %d" , block.block.piece_index, block.block.block_index); #endif block_offset = block.block.block_index * t->block_size(); block_size = (std::min)(t->torrent_file().piece_size( block.block.piece_index) - block_offset, t->block_size()); TORRENT_ASSERT(block_size > 0); TORRENT_ASSERT(block_size <= t->block_size()); r.length += block_size; m_outstanding_bytes += block_size; #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif } } // the verification will fail for coalesced blocks TORRENT_ASSERT(verify_piece(r) || m_request_large_blocks); #ifndef TORRENT_DISABLE_EXTENSIONS bool handled = false; for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { if ((handled = (*i)->write_request(r))) break; } if (is_disconnecting()) return; if (!handled) #endif { write_request(r); m_last_request = aux::time_now(); } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "REQUEST" , "piece: %d s: %x l: %x ds: %dB/s dqs: %d rqs: %d blk: %s" , r.piece, r.start, r.length, statistics().download_rate() , int(m_desired_queue_size), int(m_download_queue.size()) , m_request_large_blocks?"large":"single"); #endif } m_last_piece = aux::time_now(); if (!m_download_queue.empty() && empty_download_queue) { // This means we just added a request to this connection that // previously did not have a request. That's when we start the // request timeout. m_requested = aux::time_now(); #ifndef TORRENT_DISABLE_LOGGING t->debug_log("REQUEST [%p] (%d ms)", static_cast(this) , int(total_milliseconds(clock_type::now() - m_unchoke_time))); #endif } } void peer_connection::connect_failed(error_code const& e) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(e); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CONNECTION FAILED" , "%s", print_endpoint(m_remote).c_str()); #endif #ifndef TORRENT_DISABLE_LOGGING m_ses.session_log("CONNECTION FAILED: %s", print_endpoint(m_remote).c_str()); #endif m_counters.inc_stats_counter(counters::connect_timeouts); boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(!m_connecting || t); if (m_connecting) { m_counters.inc_stats_counter(counters::num_peers_half_open, -1); if (t) t->dec_num_connecting(m_peer_info); m_connecting = false; } // a connection attempt using uTP just failed // mark this peer as not supporting uTP // we'll never try it again (unless we're trying holepunch) if (is_utp(*m_socket) && m_peer_info && m_peer_info->supports_utp && !m_holepunch_mode) { m_peer_info->supports_utp = false; // reconnect immediately using TCP torrent_peer* pi = peer_info_struct(); fast_reconnect(true); disconnect(e, op_connect, 0); if (t && pi) t->connect_to_peer(pi, true); return; } if (m_holepunch_mode) fast_reconnect(true); #ifndef TORRENT_DISABLE_EXTENSIONS if ((!is_utp(*m_socket) || !m_settings.get_bool(settings_pack::enable_outgoing_tcp)) && m_peer_info && m_peer_info->supports_holepunch && !m_holepunch_mode) { // see if we can try a holepunch bt_peer_connection* p = t->find_introducer(remote()); if (p) p->write_holepunch_msg(bt_peer_connection::hp_rendezvous, remote(), 0); } #endif disconnect(e, op_connect, 1); return; } // the error argument defaults to 0, which means deliberate disconnect // 1 means unexpected disconnect/error // 2 protocol error (client sent something invalid) void peer_connection::disconnect(error_code const& ec , operation_t op, int error) { TORRENT_ASSERT(is_single_thread()); #if TORRENT_USE_ASSERTS m_disconnect_started = true; #endif if (m_disconnecting) return; m_socket->set_close_reason(error_to_close_reason(ec)); close_reason_t close_reason = close_reason_t(m_socket->get_close_reason()); #ifndef TORRENT_DISABLE_LOGGING if (close_reason != 0) { peer_log(peer_log_alert::info, "CLOSE_REASON", "%d", int(close_reason)); } #endif // while being disconnected, it's possible that our torrent_peer // pointer gets cleared. Make sure we save it to be able to keep // proper books in the piece_picker (when debugging is enabled) torrent_peer* self_peer = peer_info_struct(); #ifndef TORRENT_DISABLE_LOGGING switch (error) { case 0: peer_log(peer_log_alert::info, "CONNECTION_CLOSED", "op: %d error: %s" , op, ec.message().c_str()); break; case 1: peer_log(peer_log_alert::info, "CONNECTION_FAILED", "op: %d error: %s" , op, ec.message().c_str()); break; case 2: peer_log(peer_log_alert::info, "PEER_ERROR" ,"op: %d error: %s" , op, ec.message().c_str()); break; } if (ec == boost::asio::error::eof && !in_handshake() && !is_connecting() && aux::time_now() - connected_time() < seconds(15)) { peer_log(peer_log_alert::info, "SHORT_LIVED_DISCONNECT", ""); } #endif if ((m_channel_state[upload_channel] & peer_info::bw_network) == 0) { // make sure we free up all send buffers that are owned // by the disk thread m_send_buffer.clear(); m_recv_buffer.free_disk_buffer(); } // we cannot do this in a constructor TORRENT_ASSERT(m_in_constructor == false); if (error > 0) { m_failed = true; } if (m_connected) m_counters.inc_stats_counter(counters::num_peers_connected, -1); m_connected = false; // for incoming connections, we get invalid argument errors // when asking for the remote endpoint and the socket already // closed, which is an edge case, but possible to happen when // a peer makes a TCP and uTP connection in parallel. // for outgoing connections however, why would we get this? // TORRENT_ASSERT(ec != error::invalid_argument || !m_outgoing); m_counters.inc_stats_counter(counters::disconnected_peers); if (error == 2) m_counters.inc_stats_counter(counters::error_peers); if (ec == error::connection_reset) m_counters.inc_stats_counter(counters::connreset_peers); else if (ec == error::eof) m_counters.inc_stats_counter(counters::eof_peers); else if (ec == error::connection_refused) m_counters.inc_stats_counter(counters::connrefused_peers); else if (ec == error::connection_aborted) m_counters.inc_stats_counter(counters::connaborted_peers); else if (ec == error::not_connected) m_counters.inc_stats_counter(counters::notconnected_peers); else if (ec == error::no_permission) m_counters.inc_stats_counter(counters::perm_peers); else if (ec == error::no_buffer_space) m_counters.inc_stats_counter(counters::buffer_peers); else if (ec == error::host_unreachable) m_counters.inc_stats_counter(counters::unreachable_peers); else if (ec == error::broken_pipe) m_counters.inc_stats_counter(counters::broken_pipe_peers); else if (ec == error::address_in_use) m_counters.inc_stats_counter(counters::addrinuse_peers); else if (ec == error::access_denied) m_counters.inc_stats_counter(counters::no_access_peers); else if (ec == error::invalid_argument) m_counters.inc_stats_counter(counters::invalid_arg_peers); else if (ec == error::operation_aborted) m_counters.inc_stats_counter(counters::aborted_peers); else if (ec == errors::upload_upload_connection || ec == errors::uninteresting_upload_peer || ec == errors::torrent_aborted || ec == errors::self_connection || ec == errors::torrent_paused) m_counters.inc_stats_counter(counters::uninteresting_peers); if (ec == errors::timed_out || ec == error::timed_out) m_counters.inc_stats_counter(counters::transport_timeout_peers); if (ec == errors::timed_out_inactivity || ec == errors::timed_out_no_request || ec == errors::timed_out_no_interest) m_counters.inc_stats_counter(counters::timeout_peers); if (ec == errors::no_memory) m_counters.inc_stats_counter(counters::no_memory_peers); if (ec == errors::too_many_connections) m_counters.inc_stats_counter(counters::too_many_peers); if (ec == errors::timed_out_no_handshake) m_counters.inc_stats_counter(counters::connect_timeouts); if (error > 0) { if (is_utp(*m_socket)) m_counters.inc_stats_counter(counters::error_utp_peers); else m_counters.inc_stats_counter(counters::error_tcp_peers); if (m_outgoing) m_counters.inc_stats_counter(counters::error_outgoing_peers); else m_counters.inc_stats_counter(counters::error_incoming_peers); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) if (type() == bittorrent_connection && op != op_connect) { bt_peer_connection* bt = static_cast(this); if (bt->supports_encryption()) m_counters.inc_stats_counter( counters::error_encrypted_peers); if (bt->rc4_encrypted() && bt->supports_encryption()) m_counters.inc_stats_counter(counters::error_rc4_peers); } #endif // TORRENT_DISABLE_ENCRYPTION } boost::shared_ptr me(self()); INVARIANT_CHECK; if (m_channel_state[upload_channel] & peer_info::bw_disk) { m_counters.inc_stats_counter(counters::num_peers_up_disk, -1); m_channel_state[upload_channel] &= ~peer_info::bw_disk; } if (m_channel_state[download_channel] & peer_info::bw_disk) { m_counters.inc_stats_counter(counters::num_peers_down_disk, -1); m_channel_state[download_channel] &= ~peer_info::bw_disk; } boost::shared_ptr t = m_torrent.lock(); if (m_connecting) { m_counters.inc_stats_counter(counters::num_peers_half_open, -1); if (t) t->dec_num_connecting(m_peer_info); m_connecting = false; } torrent_handle handle; if (t) handle = t->get_handle(); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { (*i)->on_disconnect(ec); } #endif if (ec == error::address_in_use && m_settings.get_int(settings_pack::outgoing_port) != 0 && t) { if (t->alerts().should_post()) t->alerts().emplace_alert( handle, performance_alert::too_few_outgoing_ports); } if (t) { if (ec) { if ((error > 1 || ec.category() == socks_category()) && t->alerts().should_post()) { t->alerts().emplace_alert(handle, remote() , pid(), op, ec); } if (error <= 1 && t->alerts().should_post()) { t->alerts().emplace_alert(handle , remote(), pid(), op, m_socket->type(), ec, close_reason); } } // make sure we keep all the stats! if (!m_ignore_stats) { // report any partially received payload as redundant boost::optional pbp = downloading_piece_progress(); if (pbp && pbp->bytes_downloaded > 0 && pbp->bytes_downloaded < pbp->full_block_bytes) { t->add_redundant_bytes(pbp->bytes_downloaded, torrent::piece_closing); } } if (t->has_picker()) { piece_picker& picker = t->picker(); while (!m_download_queue.empty()) { pending_block& qe = m_download_queue.back(); if (!qe.timed_out && !qe.not_wanted) picker.abort_download(qe.block, self_peer); m_outstanding_bytes -= t->to_req(qe.block).length; if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; m_download_queue.pop_back(); } while (!m_request_queue.empty()) { pending_block& qe = m_request_queue.back(); if (!qe.timed_out && !qe.not_wanted) picker.abort_download(qe.block, self_peer); m_request_queue.pop_back(); } } else { m_download_queue.clear(); m_request_queue.clear(); m_outstanding_bytes = 0; } m_queued_time_critical = 0; #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif t->remove_peer(this); } else { TORRENT_ASSERT(m_download_queue.empty()); TORRENT_ASSERT(m_request_queue.empty()); } #if defined TORRENT_DEBUG && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS // since this connection doesn't have a torrent reference // no torrent should have a reference to this connection either TORRENT_ASSERT(!m_ses.any_torrent_has_peer(this)); #endif m_disconnecting = true; error_code e; async_shutdown(*m_socket, m_socket); m_ses.close_connection(this, ec); } bool peer_connection::ignore_unchoke_slots() const { TORRENT_ASSERT(is_single_thread()); if (num_classes() == 0) return true; if (m_ses.ignore_unchoke_slots_set(*this)) return true; boost::shared_ptr t = m_torrent.lock(); if (t && m_ses.ignore_unchoke_slots_set(*t)) return true; return false; } // defined in upnp.cpp bool is_local(address const& a); bool peer_connection::on_local_network() const { TORRENT_ASSERT(is_single_thread()); if (libtorrent::is_local(m_remote.address()) || is_loopback(m_remote.address())) return true; return false; } int peer_connection::request_timeout() const { const int deviation = m_request_time.avg_deviation(); const int avg = m_request_time.mean(); int ret; if (m_request_time.num_samples() < 2) { if (m_request_time.num_samples() == 0) return m_settings.get_int(settings_pack::request_timeout); ret = avg + avg / 5; } else { ret = avg + deviation * 4; } // ret is milliseconds, the return value is seconds. Convert to // seconds and round up ret = (std::min)((ret + 999) / 1000 , m_settings.get_int(settings_pack::request_timeout)); // timeouts should never be less than 2 seconds. The granularity is whole // seconds, and only checked once per second. 2 is the minimum to avoid // being considered timed out instantly return (std::max)(2, ret); } void peer_connection::get_peer_info(peer_info& p) const { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(!associated_torrent().expired()); time_point now = aux::time_now(); p.download_rate_peak = m_download_rate_peak; p.upload_rate_peak = m_upload_rate_peak; p.rtt = m_request_time.mean(); 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.pending_disk_bytes = m_outstanding_writing_bytes; p.pending_disk_read_bytes = m_reading_bytes; p.send_quota = m_quota[upload_channel]; p.receive_quota = m_quota[download_channel]; p.num_pieces = m_num_pieces; if (m_download_queue.empty()) p.request_timeout = -1; else p.request_timeout = int(total_seconds(m_requested - now) + request_timeout()); p.download_queue_time = download_queue_time(); p.queue_bytes = m_outstanding_bytes; #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES p.country[0] = m_country[0]; p.country[1] = m_country[1]; #else std::fill(p.country, p.country + 2, 0); #endif #endif // TORRENT_NO_DEPRECATE p.total_download = statistics().total_payload_download(); p.total_upload = statistics().total_payload_upload(); #ifndef TORRENT_NO_DEPRECATE p.upload_limit = -1; p.download_limit = -1; p.load_balancing = 0; #endif p.download_queue_length = int(download_queue().size() + m_request_queue.size()); p.requests_in_buffer = int(std::count_if(m_download_queue.begin() , m_download_queue.end() , &pending_block_in_buffer)); p.target_dl_queue_length = int(desired_queue_size()); p.upload_queue_length = int(upload_queue().size()); p.timed_out_requests = 0; p.busy_requests = 0; for (std::vector::const_iterator i = m_download_queue.begin() , end(m_download_queue.end()); i != end; ++i) { if (i->timed_out) ++p.timed_out_requests; if (i->busy) ++p.busy_requests; } if (boost::optional 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.pieces = get_bitfield(); p.last_request = now - m_last_request; p.last_active = now - (std::max)(m_last_sent, m_last_receive); // this will set the flags so that we can update them later p.flags = 0; get_specific_peer_info(p); p.flags |= is_seed() ? peer_info::seed : 0; p.flags |= m_snubbed ? peer_info::snubbed : 0; p.flags |= m_upload_only ? peer_info::upload_only : 0; p.flags |= m_endgame_mode ? peer_info::endgame_mode : 0; p.flags |= m_holepunch_mode ? peer_info::holepunched : 0; if (peer_info_struct()) { torrent_peer* pi = peer_info_struct(); TORRENT_ASSERT(pi->in_use); p.source = pi->source; p.failcount = pi->failcount; p.num_hashfails = pi->hashfails; p.flags |= pi->on_parole ? peer_info::on_parole : 0; p.flags |= pi->optimistically_unchoked ? peer_info::optimistic_unchoke : 0; } else { p.source = 0; p.failcount = 0; p.num_hashfails = 0; } p.remote_dl_rate = m_remote_dl_rate; p.send_buffer_size = m_send_buffer.capacity(); p.used_send_buffer = m_send_buffer.size(); p.receive_buffer_size = m_recv_buffer.capacity(); p.used_receive_buffer = m_recv_buffer.pos(); p.write_state = m_channel_state[upload_channel]; p.read_state = m_channel_state[download_channel]; // pieces may be empty if we don't have metadata yet if (p.pieces.size() == 0) { p.progress = 0.f; p.progress_ppm = 0; } else { #if TORRENT_NO_FPU p.progress = 0.f; #else p.progress = float(p.pieces.count()) / float(p.pieces.size()); #endif p.progress_ppm = boost::uint64_t(p.pieces.count()) * 1000000 / p.pieces.size(); } p.estimated_reciprocation_rate = m_est_reciprocation_rate; error_code ec; p.local_endpoint = get_socket()->local_endpoint(ec); } // allocates a disk buffer of size 'disk_buffer_size' and replaces the // end of the current receive buffer with it. i.e. the receive pos // must be <= packet_size - disk_buffer_size // the disk buffer can be accessed through release_disk_receive_buffer() // when it is queried, the responsibility to free it is transferred // to the caller bool peer_connection::allocate_disk_receive_buffer(int disk_buffer_size) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; m_recv_buffer.assert_no_disk_buffer(); TORRENT_ASSERT(m_recv_buffer.pos() <= m_recv_buffer.packet_size() - disk_buffer_size); TORRENT_ASSERT(disk_buffer_size <= 16 * 1024); if (disk_buffer_size == 0) return true; if (disk_buffer_size > 16 * 1024) { disconnect(errors::invalid_piece_size, op_bittorrent, 2); return false; } // first free the old buffer m_recv_buffer.free_disk_buffer(); // then allocate a new one bool exceeded = false; m_recv_buffer.assign_disk_buffer( m_allocator.allocate_disk_buffer(exceeded, self(), "receive buffer") , disk_buffer_size); if (!m_recv_buffer.has_disk_buffer()) { disconnect(errors::no_memory, op_alloc_recvbuf); return false; } // to understand why m_outstanding_writing_bytes is here, see comment by // the other call to allocate_disk_buffer() if (exceeded && m_outstanding_writing_bytes > 0) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "DISK", "exceeded disk buffer watermark"); #endif if ((m_channel_state[download_channel] & peer_info::bw_disk) == 0) m_counters.inc_stats_counter(counters::num_peers_down_disk); m_channel_state[download_channel] |= peer_info::bw_disk; } return true; } void peer_connection::superseed_piece(int replace_piece, int new_piece) { TORRENT_ASSERT(is_single_thread()); if (is_connecting()) return; if (in_handshake()) return; if (new_piece == -1) { if (m_superseed_piece[0] == -1) return; m_superseed_piece[0] = -1; m_superseed_piece[1] = -1; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SUPER_SEEDING", "ending"); #endif boost::shared_ptr t = m_torrent.lock(); assert(t); // this will either send a full bitfield or // a have-all message, effectively terminating // super-seeding, since the peer may pick any piece write_bitfield(); return; } assert(!has_piece(new_piece)); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "HAVE", "piece: %d (super seed)" , new_piece); #endif write_have(new_piece); if (replace_piece >= 0) { // move the piece we're replacing to the tail if (m_superseed_piece[0] == replace_piece) std::swap(m_superseed_piece[0], m_superseed_piece[1]); } m_superseed_piece[1] = m_superseed_piece[0]; m_superseed_piece[0] = new_piece; } void peer_connection::max_out_request_queue(int s) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "MAX_OUT_QUEUE_SIZE", "%d -> %d" , m_max_out_request_queue, s); #endif m_max_out_request_queue = s; } int peer_connection::max_out_request_queue() const { return m_max_out_request_queue; } void peer_connection::update_desired_queue_size() { TORRENT_ASSERT(is_single_thread()); if (m_snubbed) { m_desired_queue_size = 1; return; } #ifndef TORRENT_DISABLE_LOGGING int const previous_queue_size = m_desired_queue_size; #endif int const download_rate = statistics().download_payload_rate(); // the desired download queue size int const queue_time = m_settings.get_int(settings_pack::request_queue_time); // when we're in slow-start mode we increase the desired queue size every // time we receive a piece, no need to adjust it here (other than // enforcing the upper limit) if (!m_slow_start) { // (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 boost::shared_ptr t = m_torrent.lock(); int const block_size = t->block_size(); TORRENT_ASSERT(block_size > 0); m_desired_queue_size = queue_time * 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; #ifndef TORRENT_DISABLE_LOGGING if (previous_queue_size != m_desired_queue_size) { peer_log(peer_log_alert::info, "UPDATE_QUEUE_SIZE" , "dqs: %d max: %d dl: %d qt: %d snubbed: %d slow-start: %d" , m_desired_queue_size, m_max_out_request_queue , download_rate, queue_time, int(m_snubbed), int(m_slow_start)); } #endif } void peer_connection::second_tick(int tick_interval_ms) { TORRENT_ASSERT(is_single_thread()); time_point now = aux::time_now(); boost::shared_ptr me(self()); // the invariant check must be run before me is destructed // in case the peer got disconnected INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); int warning = 0; // drain the IP overhead from the bandwidth limiters if (m_settings.get_bool(settings_pack::rate_limit_ip_overhead) && t) { warning |= m_ses.use_quota_overhead(*this, m_statistics.download_ip_overhead() , m_statistics.upload_ip_overhead()); warning |= m_ses.use_quota_overhead(*t, m_statistics.download_ip_overhead() , m_statistics.upload_ip_overhead()); } if (warning && t->alerts().should_post()) { for (int channel = 0; channel < 2; ++channel) { if ((warning & (1 << channel)) == 0) continue; t->alerts().emplace_alert(t->get_handle() , channel == peer_connection::download_channel ? performance_alert::download_limit_too_low : performance_alert::upload_limit_too_low); } } if (!t || m_disconnecting) { TORRENT_ASSERT(t || !m_connecting); if (m_connecting) { m_counters.inc_stats_counter(counters::num_peers_half_open, -1); if (t) t->dec_num_connecting(m_peer_info); m_connecting = false; } disconnect(errors::torrent_aborted, op_bittorrent); return; } if (m_endgame_mode && m_interesting && m_download_queue.empty() && m_request_queue.empty() && now - seconds(5) >= m_last_request) { // this happens when we're in strict end-game // mode and the peer could not request any blocks // because they were all taken but there were still // unrequested blocks. Now, 5 seconds later, there // might not be any unrequested blocks anymore, so // we should try to pick another block to see // if we can pick a busy one m_last_request = now; if (request_a_block(*t, *this)) m_counters.inc_stats_counter(counters::end_game_piece_picks); if (m_disconnecting) return; send_block_requests(); } if (t->super_seeding() && t->ready_for_connections() && !m_peer_interested && m_became_uninterested + seconds(10) < now) { // maybe we need to try another piece, to see if the peer // become interested in us then superseed_piece(-1, t->get_piece_to_super_seed(m_have_piece)); } on_tick(); if (is_disconnecting()) return; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { (*i)->tick(); } if (is_disconnecting()) return; #endif // if the peer hasn't said a thing for a certain // time, it is considered to have timed out time_duration d; d = (std::min)(now - m_last_receive, now - m_last_sent); if (m_connecting) { int connect_timeout = m_settings.get_int(settings_pack::peer_connect_timeout); if (m_peer_info) connect_timeout += 3 * m_peer_info->failcount; // SSL and i2p handshakes are slow if (is_ssl(*m_socket)) connect_timeout += 10; #if TORRENT_USE_I2P if (is_i2p(*m_socket)) connect_timeout += 20; #endif if (d > seconds(connect_timeout) && can_disconnect(errors::timed_out)) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CONNECT_FAILED", "waited %d seconds" , int(total_seconds(d))); #endif connect_failed(errors::timed_out); return; } } // if we can't read, it means we're blocked on the rate-limiter // or the disk, not the peer itself. In this case, don't blame // the peer and disconnect it bool may_timeout = (m_channel_state[download_channel] & peer_info::bw_network) != 0; // TODO: 2 use a deadline_timer for timeouts. Don't rely on second_tick()! // Hook this up to connect timeout as well. This would improve performance // because of less work in second_tick(), and might let use remove ticking // entirely eventually if (may_timeout && d > seconds(timeout()) && !m_connecting && m_reading_bytes == 0 && can_disconnect(errors::timed_out_inactivity)) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "LAST_ACTIVITY", "%d seconds ago" , int(total_seconds(d))); #endif disconnect(errors::timed_out_inactivity, op_bittorrent); return; } // do not stall waiting for a handshake int timeout = m_settings.get_int (settings_pack::handshake_timeout); #if TORRENT_USE_I2P timeout *= is_i2p(*m_socket) ? 4 : 1; #endif if (may_timeout && !m_connecting && in_handshake() && d > seconds(timeout)) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "NO_HANDSHAKE", "waited %d seconds" , int(total_seconds(d))); #endif disconnect(errors::timed_out_no_handshake, op_bittorrent); return; } // disconnect peers that we unchoked, but they didn't send a request in // the last 60 seconds, and we haven't been working on servicing a request // for more than 60 seconds. // but only if we're a seed d = now - (std::max)((std::max)(m_last_unchoke, m_last_incoming_request) , m_last_sent_payload); if (may_timeout && !m_connecting && m_requests.empty() && m_reading_bytes == 0 && !m_choked && m_peer_interested && t && t->is_upload_only() && d > seconds(60) && can_disconnect(errors::timed_out_no_request)) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "NO_REQUEST", "waited %d seconds" , int(total_seconds(d))); #endif disconnect(errors::timed_out_no_request, op_bittorrent); return; } // 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; time_duration time_limit = seconds( m_settings.get_int(settings_pack::inactivity_timeout)); // don't bother disconnect peers we haven't been interested // in (and that hasn't been interested in us) for a while // unless we have used up all our connection slots if (may_timeout && !m_interesting && !m_peer_interested && d1 > time_limit && d2 > time_limit && (m_ses.num_connections() >= m_settings.get_int(settings_pack::connections_limit) || (t && t->num_peers() >= t->max_connections())) && can_disconnect(errors::timed_out_no_interest)) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "MUTUAL_NO_INTEREST", "t1: %d t2: %d" , int(total_seconds(d1)), int(total_seconds(d2))); #endif disconnect(errors::timed_out_no_interest, op_bittorrent); return; } if (may_timeout && !m_download_queue.empty() && m_quota[download_channel] > 0 && now > m_requested + seconds(request_timeout())) { snub_peer(); } // if we haven't sent something in too long, send a keep-alive keep_alive(); // if our download rate isn't increasing significantly anymore, end slow // start. The 10kB is to have some slack here. // we can't do this when we're choked, because we aren't sending any // requests yet, so there hasn't been an opportunity to ramp up the // connection yet. if (m_slow_start && !m_peer_choked && m_downloaded_last_second > 0 && m_downloaded_last_second + 5000 >= m_statistics.last_payload_downloaded()) { m_slow_start = false; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SLOW_START", "exit slow start: " "prev-dl: %d dl: %d" , int(m_downloaded_last_second) , int(m_statistics.last_payload_downloaded())); #endif } m_downloaded_last_second = m_statistics.last_payload_downloaded(); m_uploaded_last_second = m_statistics.last_payload_uploaded(); m_statistics.second_tick(tick_interval_ms); if (m_statistics.upload_payload_rate() > m_upload_rate_peak) { m_upload_rate_peak = m_statistics.upload_payload_rate(); } if (m_statistics.download_payload_rate() > m_download_rate_peak) { m_download_rate_peak = m_statistics.download_payload_rate(); } if (is_disconnecting()) return; if (!t->ready_for_connections()) return; update_desired_queue_size(); if (m_desired_queue_size == m_max_out_request_queue && t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle() , performance_alert::outstanding_request_limit_reached); } int piece_timeout = m_settings.get_int(settings_pack::piece_timeout); if (!m_download_queue.empty() && m_quota[download_channel] > 0 && now - m_last_piece > seconds(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. #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "PIECE_REQUEST_TIMED_OUT" , "%d time: %d to: %d" , int(m_download_queue.size()), int(total_seconds(now - m_last_piece)) , piece_timeout); #endif snub_peer(); } // update once every minute if (now - m_remote_dl_update >= seconds(60)) { boost::int64_t piece_size = t->torrent_file().piece_length(); if (m_remote_dl_rate > 0) m_remote_dl_rate = int((m_remote_dl_rate * 2 / 3) + ((boost::int64_t(m_remote_pieces_dled) * piece_size / 3) / 60)); else m_remote_dl_rate = int(boost::int64_t(m_remote_pieces_dled) * piece_size / 60); m_remote_pieces_dled = 0; m_remote_dl_update = now; } fill_send_buffer(); } void peer_connection::snub_peer() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); if (!m_snubbed) { m_snubbed = true; m_slow_start = false; if (t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle() , m_remote, m_peer_id); } } m_desired_queue_size = 1; if (on_parole()) return; if (!t->has_picker()) return; piece_picker& picker = t->picker(); // first, if we have any unsent requests, just // wipe those out while (!m_request_queue.empty()) { t->picker().abort_download(m_request_queue.back().block, peer_info_struct()); m_request_queue.pop_back(); } m_queued_time_critical = 0; TORRENT_ASSERT(!m_download_queue.empty()); // time out the last request eligible // block in the queue int i = m_download_queue.size() - 1; for (; i >= 0; --i) { if (!m_download_queue[i].timed_out && !m_download_queue[i].not_wanted) break; } if (i >= 0) { pending_block& qe = m_download_queue[i]; piece_block r = qe.block; // only cancel a request if it blocks the piece from being completed // (i.e. no free blocks to request from it) piece_picker::downloading_piece p; picker.piece_info(qe.block.piece_index, p); int free_blocks = picker.blocks_in_piece(qe.block.piece_index) - p.finished - p.writing - p.requested; // if there are still blocks available for other peers to pick, we're // still not holding up the completion of the piece and there's no // need to cancel the requests. For more information, see: // http://blog.libtorrent.org/2011/11/block-request-time-outs/ if (free_blocks > 0) { send_block_requests(); return; } if (t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle() , remote(), pid(), int(qe.block.block_index) , int(qe.block.piece_index)); } // request a new block before removing the previous // one, in order to prevent it from // picking the same block again, stalling the // same piece indefinitely. m_desired_queue_size = 2; if (request_a_block(*t, *this)) m_counters.inc_stats_counter(counters::snubbed_piece_picks); // the block we just picked (potentially) // hasn't been put in m_download_queue yet. // it's in m_request_queue and will be sent // once send_block_requests() is called. m_desired_queue_size = 1; qe.timed_out = true; picker.abort_download(r, peer_info_struct()); } send_block_requests(); } void peer_connection::fill_send_buffer() { TORRENT_ASSERT(is_single_thread()); #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; #endif bool sent_a_piece = false; boost::shared_ptr t = m_torrent.lock(); if (!t || t->is_aborted() || m_requests.empty()) 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! int buffer_size_watermark = int(boost::int64_t(m_uploaded_last_second) * m_settings.get_int(settings_pack::send_buffer_watermark_factor) / 100); if (buffer_size_watermark < m_settings.get_int(settings_pack::send_buffer_low_watermark)) { buffer_size_watermark = m_settings.get_int(settings_pack::send_buffer_low_watermark); } else if (buffer_size_watermark > m_settings.get_int(settings_pack::send_buffer_watermark)) { buffer_size_watermark = m_settings.get_int(settings_pack::send_buffer_watermark); } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "SEND_BUFFER_WATERMARK" , "current watermark: %d max: %d min: %d factor: %d uploaded: %d B/s" , buffer_size_watermark , m_ses.settings().get_int(settings_pack::send_buffer_watermark) , m_ses.settings().get_int(settings_pack::send_buffer_low_watermark) , m_ses.settings().get_int(settings_pack::send_buffer_watermark_factor) , int(m_uploaded_last_second)); #endif // don't just pop the front element here, since in seed mode one request may // be blocked because we have to verify the hash first, so keep going with the // next request. However, only let each peer have one hash verification outstanding // at any given time for (int i = 0; i < m_requests.size() && (send_buffer_size() + m_reading_bytes < buffer_size_watermark); ++i) { TORRENT_ASSERT(t->ready_for_connections()); peer_request& r = m_requests[i]; TORRENT_ASSERT(r.piece >= 0); TORRENT_ASSERT(r.piece < int(m_have_piece.size())); // TORRENT_ASSERT(t->have_piece(r.piece)); TORRENT_ASSERT(r.start + r.length <= t->torrent_file().piece_size(r.piece)); TORRENT_ASSERT(r.length > 0 && r.start >= 0); if (t->is_deleted()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "REJECT_PIECE" , "piece: %d s: %x l: %x torrent deleted" , r.piece , r.start , r.length); #endif write_reject_request(r); continue; } if (t->seed_mode() && !t->verified_piece(r.piece)) { // we're still verifying the hash of this piece // so we can't return it yet. if (t->verifying_piece(r.piece)) continue; // only have three outstanding hash check per peer if (m_outstanding_piece_verification >= 3) continue; ++m_outstanding_piece_verification; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SEED_MODE_FILE_ASYNC_HASH" , "piece: %d", r.piece); #endif // this means we're in seed mode and we haven't yet // verified this piece (r.piece) if (!t->need_loaded()) return; t->inc_refcount("async_seed_hash"); m_disk_thread.async_hash(&t->storage(), r.piece, 0 , boost::bind(&peer_connection::on_seed_mode_hashed, self(), _1) , this); t->verifying(r.piece); continue; } // in seed mode, we might end up accepting a request // which it later turns out we cannot serve, if we ended // up not having that piece if (!t->has_piece_passed(r.piece)) { // we don't have this piece yet, but we anticipate to have // it very soon, so we have told our peers we have it. // hold off on sending it. If the piece fails later // we will reject this request if (t->is_predictive_piece(r.piece)) continue; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "REJECT_PIECE" , "piece: %d s: %x l: %x piece not passed hash check" , r.piece , r.start , r.length); #endif write_reject_request(r); } else { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "FILE_ASYNC_READ" , "piece: %d s: %x l: %x", r.piece, r.start, r.length); #endif m_reading_bytes += r.length; sent_a_piece = true; // the callback function may be called immediately, instead of being posted if (!t->need_loaded()) return; TORRENT_ASSERT(t->valid_metadata()); TORRENT_ASSERT(r.piece >= 0); TORRENT_ASSERT(r.piece < t->torrent_file().num_pieces()); t->inc_refcount("async_read"); m_disk_thread.async_read(&t->storage(), r , boost::bind(&peer_connection::on_disk_read_complete , self(), _1, r, clock_type::now()), this); } m_last_sent_payload = clock_type::now(); m_requests.erase(m_requests.begin() + i); if (m_requests.empty()) m_counters.inc_stats_counter(counters::num_peers_up_requests, -1); --i; } if (t->share_mode() && sent_a_piece) t->recalc_share_mode(); } // this is called when a previously unchecked piece has been // checked, while in seed-mode void peer_connection::on_seed_mode_hashed(disk_io_job const* j) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); torrent_ref_holder h(t.get(), "async_seed_hash"); if (t) t->dec_refcount("async_seed_hash"); TORRENT_ASSERT(m_outstanding_piece_verification > 0); --m_outstanding_piece_verification; if (!t || t->is_aborted()) return; if (j->error) { t->handle_disk_error(j, this); t->leave_seed_mode(false); return; } // we're using the piece hashes here, we need the torrent to be loaded if (!t->need_loaded()) return; if (!m_settings.get_bool(settings_pack::disable_hash_checks) && sha1_hash(j->d.piece_hash) != t->torrent_file().hash_for_piece(j->piece)) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SEED_MODE_FILE_HASH" , "piece: %d failed", j->piece); #endif t->leave_seed_mode(false); } else { if (t->seed_mode()) { TORRENT_ASSERT(t->verifying_piece(j->piece)); t->verified(j->piece); } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SEED_MODE_FILE_HASH" , "piece: %d passed", j->piece); #endif if (t->seed_mode() && t->all_verified()) t->leave_seed_mode(true); } // try to service the requests again, now that the piece // has been verified fill_send_buffer(); } void peer_connection::on_disk_read_complete(disk_io_job const* j , peer_request r, time_point issue_time) { TORRENT_ASSERT(is_single_thread()); // return value: // 0: success, piece passed hash check // -1: disk failure int disk_rtt = int(total_microseconds(clock_type::now() - issue_time)); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "FILE_ASYNC_READ_COMPLETE" , "ret: %d piece: %d s: %x l: %x b: %p c: %s e: %s rtt: %d us" , j->ret, r.piece, r.start, r.length , static_cast(j->buffer.disk_block) , (j->flags & disk_io_job::cache_hit ? "cache hit" : "cache miss") , j->error.ec.message().c_str(), disk_rtt); #endif m_reading_bytes -= r.length; boost::shared_ptr t = m_torrent.lock(); torrent_ref_holder h(t.get(), "async_read"); if (t) t->dec_refcount("async_read"); if (j->ret < 0) { if (!t) { disconnect(j->error.ec, op_file_read); return; } TORRENT_ASSERT(j->buffer.disk_block == 0); write_dont_have(r.piece); write_reject_request(r); if (t->alerts().should_post()) t->alerts().emplace_alert(j->error.ec , t->resolve_filename(j->error.file) , j->error.operation_str(), t->get_handle()); ++m_disk_read_failures; if (m_disk_read_failures > 100) disconnect(j->error.ec, op_file_read); return; } // we're only interested in failures in a row. // if we every now and then successfully send a // block, the peer is still useful m_disk_read_failures = 0; TORRENT_ASSERT(j->ret == r.length); // even if we're disconnecting, we need to free this block // otherwise the disk thread will hang, waiting for the network // thread to be done with it disk_buffer_holder buffer(m_allocator, *j); if (m_disconnecting) return; // flush send buffer at the end of // this burst of disk events // m_ses.cork_burst(this); if (!t) { disconnect(j->error.ec, op_file_read); return; } if (j->ret != r.length) { // handle_disk_error may disconnect us t->handle_disk_error(j, this); return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message , "PIECE", "piece: %d s: %x l: %x", r.piece, r.start, r.length); #endif m_counters.blend_stats_counter(counters::request_latency, disk_rtt, 5); // we probably just pulled this piece into the cache. // if it's rare enough to make it into the suggested piece // push another piece out if (m_settings.get_int(settings_pack::suggest_mode) == settings_pack::suggest_read_cache && (j->flags & disk_io_job::cache_hit) == 0) { t->add_suggest_piece(r.piece); } write_piece(r, buffer); } void peer_connection::assign_bandwidth(int channel, int amount) { TORRENT_ASSERT(is_single_thread()); #ifndef TORRENT_DISABLE_LOGGING peer_log(channel == upload_channel ? peer_log_alert::outgoing : peer_log_alert::incoming , "ASSIGN_BANDWIDHT", "bytes: %d", amount); #endif TORRENT_ASSERT(amount > 0 || is_disconnecting()); m_quota[channel] += amount; TORRENT_ASSERT(m_channel_state[channel] & peer_info::bw_limit); m_channel_state[channel] &= ~peer_info::bw_limit; #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif if (is_disconnecting()) return; if (channel == upload_channel) { setup_send(); } else if (channel == download_channel) { setup_receive(); } } // the number of bytes we expect to receive, or want to send // channel either refer to upload or download. This is used // by the rate limiter to allocate quota for this peer int peer_connection::wanted_transfer(int channel) { TORRENT_ASSERT(is_single_thread()); const int tick_interval = (std::max)(1, m_settings.get_int(settings_pack::tick_interval)); if (channel == download_channel) { boost::int64_t const download_rate = boost::int64_t(m_statistics.download_rate()) * 3 / 2; return std::max(int(download_rate * tick_interval / 1000), (std::max)(m_outstanding_bytes, m_recv_buffer.packet_bytes_remaining()) + 30); } else { boost::int64_t const upload_rate = boost::int64_t(m_statistics.upload_rate()) * 2; return std::max(int(upload_rate * tick_interval / 1000), (std::max)(m_reading_bytes, m_send_buffer.size())); } } int peer_connection::request_bandwidth(int channel, int bytes) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; // we can only have one outstanding bandwidth request at a time if (m_channel_state[channel] & peer_info::bw_limit) return 0; shared_ptr t = m_torrent.lock(); bytes = (std::max)(wanted_transfer(channel), bytes); // we already have enough quota if (m_quota[channel] >= bytes) return 0; // deduct the bytes we already have quota for bytes -= m_quota[channel]; int priority = get_priority(channel); int max_channels = num_classes() + (t ? t->num_classes() : 0) + 2; bandwidth_channel** channels = TORRENT_ALLOCA(bandwidth_channel*, max_channels); // collect the pointers to all bandwidth channels // that apply to this torrent int c = 0; c += m_ses.copy_pertinent_channels(*this, channel , channels + c, max_channels - c); if (t) { c += m_ses.copy_pertinent_channels(*t, channel , channels + c, max_channels - c); } #ifdef TORRENT_DEBUG // make sure we don't have duplicates std::set unique_classes; for (int i = 0; i < c; ++i) { TORRENT_ASSERT(unique_classes.count(channels[i]) == 0); unique_classes.insert(channels[i]); } #endif TORRENT_ASSERT((m_channel_state[channel] & peer_info::bw_limit) == 0); bandwidth_manager* manager = m_ses.get_bandwidth_manager(channel); int ret = manager->request_bandwidth(self() , bytes, priority, channels, c); if (ret == 0) { #ifndef TORRENT_DISABLE_LOGGING peer_log( channel == download_channel ? peer_log_alert::incoming : peer_log_alert::outgoing, "REQUEST_BANDWIDTH", "bytes: %d quota: %d wanted_transfer: %d " "prio: %d num_channels: %d", bytes, m_quota[channel] , wanted_transfer(channel), priority, c); #endif m_channel_state[channel] |= peer_info::bw_limit; } else { m_quota[channel] += ret; } return ret; } void peer_connection::uncork_socket() { TORRENT_ASSERT(is_single_thread()); if (!m_corked) return; m_corked = false; setup_send(); } void peer_connection::setup_send() { TORRENT_ASSERT(is_single_thread()); if (m_disconnecting || m_send_buffer.empty()) return; // we may want to request more quota at this point request_bandwidth(upload_channel); if (m_channel_state[upload_channel] & peer_info::bw_network) return; if (m_send_barrier == 0) { std::vector vec; m_send_buffer.build_mutable_iovec(m_send_buffer.size(), vec); int next_barrier = hit_send_barrier(vec); for (std::vector::reverse_iterator i = vec.rbegin(); i != vec.rend(); ++i) { m_send_buffer.prepend_buffer(boost::asio::buffer_cast(*i) , boost::asio::buffer_size(*i) , boost::asio::buffer_size(*i) , &nop , NULL); } set_send_barrier(next_barrier); } if ((m_quota[upload_channel] == 0 || m_send_barrier == 0) && !m_send_buffer.empty() && !m_connecting) { return; } int quota_left = m_quota[upload_channel]; if (m_send_buffer.empty() && m_reading_bytes > 0 && quota_left > 0) { if ((m_channel_state[upload_channel] & peer_info::bw_disk) == 0) m_counters.inc_stats_counter(counters::num_peers_up_disk); m_channel_state[upload_channel] |= peer_info::bw_disk; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "WAITING_FOR_DISK", "outstanding: %d" , m_reading_bytes); #endif if (!m_connecting && !m_requests.empty() && m_reading_bytes > m_settings.get_int(settings_pack::send_buffer_watermark) - 0x4000) { boost::shared_ptr t = m_torrent.lock(); // we're stalled on the disk. We want to write and we can write // but our send buffer is empty, waiting to be refilled from the disk // this either means the disk is slower than the network connection // or that our send buffer watermark is too small, because we can // send it all before the disk gets back to us. That's why we only // trigger this if we've also filled the allowed send buffer. The // first request would not fill it all the way up because of the // upload rate being virtually 0. If m_requests is empty, it doesn't // matter anyway, because we don't have any more requests from the // peer to hang on to the disk if (t && t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle() , performance_alert::send_buffer_watermark_too_low); } } } else { if (m_channel_state[upload_channel] & peer_info::bw_disk) m_counters.inc_stats_counter(counters::num_peers_up_disk, -1); m_channel_state[upload_channel] &= ~peer_info::bw_disk; } if (!can_write()) { #ifndef TORRENT_DISABLE_LOGGING if (m_send_buffer.empty()) { peer_log(peer_log_alert::outgoing, "SEND_BUFFER_DEPLETED" , "quota: %d buf: %d connecting: %s disconnecting: %s " "pending_disk: %d piece-requests: %d" , m_quota[upload_channel] , int(m_send_buffer.size()), m_connecting?"yes":"no" , m_disconnecting?"yes":"no", m_reading_bytes , int(m_requests.size())); } else { peer_log(peer_log_alert::outgoing, "CANNOT_WRITE" , "quota: %d buf: %d connecting: %s disconnecting: %s " "pending_disk: %d" , m_quota[upload_channel] , int(m_send_buffer.size()), m_connecting?"yes":"no" , m_disconnecting?"yes":"no", m_reading_bytes); } #endif return; } // send the actual buffer int amount_to_send = m_send_buffer.size(); if (amount_to_send > quota_left) amount_to_send = quota_left; if (amount_to_send > m_send_barrier) amount_to_send = m_send_barrier; TORRENT_ASSERT(amount_to_send > 0); if (m_corked) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "CORKED_WRITE", "bytes: %d" , amount_to_send); #endif return; } TORRENT_ASSERT((m_channel_state[upload_channel] & peer_info::bw_network) == 0); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "ASYNC_WRITE", "bytes: %d", amount_to_send); #endif std::vector const& vec = m_send_buffer.build_iovec(amount_to_send); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("peer_connection::on_send_data"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT(!m_socket_is_writing); m_socket_is_writing = true; #endif // uTP sockets aren't thread safe... if (is_utp(*m_socket)) { m_socket->async_write_some(vec, make_write_handler(boost::bind( &peer_connection::on_send_data, self(), _1, _2))); } else { socket_job j; j.type = socket_job::write_job; j.vec = &vec; j.peer = self(); m_ses.post_socket_job(j); } m_channel_state[upload_channel] |= peer_info::bw_network; m_last_sent = aux::time_now(); } void peer_connection::on_disk() { TORRENT_ASSERT(is_single_thread()); if ((m_channel_state[download_channel] & peer_info::bw_disk) == 0) return; boost::shared_ptr me(self()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "DISK", "dropped below disk buffer watermark"); #endif m_counters.inc_stats_counter(counters::num_peers_down_disk, -1); m_channel_state[download_channel] &= ~peer_info::bw_disk; setup_receive(); } void peer_connection::setup_receive() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (m_disconnecting) return; // we may want to request more quota at this point request_bandwidth(download_channel); if (m_channel_state[download_channel] & peer_info::bw_network) return; if (m_quota[download_channel] == 0 && !m_connecting) { return; } if (!can_read()) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "CANNOT_READ", "quota: %d " "can-write-to-disk: %s queue-limit: %d disconnecting: %s " " connecting: %s" , m_quota[download_channel] , ((m_channel_state[download_channel] & peer_info::bw_disk)?"no":"yes") , m_settings.get_int(settings_pack::max_queued_disk_bytes) , (m_disconnecting?"yes":"no") , (m_connecting?"yes":"no")); #endif // if we block reading, waiting for the disk, we will wake up // by the disk_io_thread posting a message every time it drops // from being at or exceeding the limit down to below the limit return; } error_code ec; try_read(read_async, ec); } size_t peer_connection::try_read(sync_t s, error_code& ec) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_connected); if (m_quota[download_channel] == 0) { ec = boost::asio::error::would_block; return 0; } if (!can_read()) { ec = boost::asio::error::would_block; return 0; } int max_receive = m_recv_buffer.max_receive(); boost::array vec; int num_bufs = 0; // only apply the contiguous receive buffer when we don't have any // outstanding requests. When we're likely to receive pieces, we'll // save more time from avoiding copying data from the socket if ((m_settings.get_bool(settings_pack::contiguous_recv_buffer) || m_download_queue.empty()) && !m_recv_buffer.has_disk_buffer()) { if (s == read_sync) { ec = boost::asio::error::would_block; return 0; } TORRENT_ASSERT((m_channel_state[download_channel] & peer_info::bw_network) == 0); m_channel_state[download_channel] |= peer_info::bw_network; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "ASYNC_READ"); #endif #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("peer_connection::on_receive_data_nb"); #endif m_socket->async_read_some(null_buffers(), make_read_handler( boost::bind(&peer_connection::on_receive_data_nb, self(), _1, _2))); return 0; } TORRENT_ASSERT(max_receive >= 0); int quota_left = m_quota[download_channel]; if (max_receive > quota_left) max_receive = quota_left; if (max_receive == 0) { ec = boost::asio::error::would_block; return 0; } num_bufs = m_recv_buffer.reserve(vec, max_receive); if (s == read_async) { TORRENT_ASSERT((m_channel_state[download_channel] & peer_info::bw_network) == 0); m_channel_state[download_channel] |= peer_info::bw_network; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "ASYNC_READ" , "max: %d bytes", max_receive); #endif // utp sockets aren't thread safe... #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("peer_connection::on_receive_data"); #endif if (is_utp(*m_socket)) { if (num_bufs == 1) { TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) > 0); m_socket->async_read_some( boost::asio::mutable_buffers_1(vec[0]), make_read_handler( boost::bind(&peer_connection::on_receive_data, self(), _1, _2))); } else { TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) + boost::asio::buffer_size(vec[1])> 0); m_socket->async_read_some( vec, make_read_handler( boost::bind(&peer_connection::on_receive_data, self(), _1, _2))); } } else { socket_job j; j.type = socket_job::read_job; j.peer = self(); if (num_bufs == 1) { TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) > 0); j.recv_buf = boost::asio::buffer_cast(vec[0]); j.buf_size = boost::asio::buffer_size(vec[0]); } else { TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) + boost::asio::buffer_size(vec[1])> 0); j.read_vec = vec; } m_ses.post_socket_job(j); } return 0; } size_t ret = 0; if (num_bufs == 1) { ret = m_socket->read_some(boost::asio::mutable_buffers_1(vec[0]), ec); } else { ret = m_socket->read_some(vec, ec); } // this is weird. You would imagine read_some() would do this if (ret == 0 && !ec) ec = boost::asio::error::eof; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "SYNC_READ", "max: %d ret: %d e: %s" , max_receive, int(ret), ec ? ec.message().c_str() : ""); #endif return ret; } void peer_connection::append_send_buffer(char* buffer, int size , chained_buffer::free_buffer_fun destructor, void* userdata , block_cache_reference ref) { TORRENT_ASSERT(is_single_thread()); m_send_buffer.append_buffer(buffer, size, size, destructor , userdata, ref); } void peer_connection::append_const_send_buffer(char const* buffer, int size , chained_buffer::free_buffer_fun destructor, void* userdata , block_cache_reference ref) { TORRENT_ASSERT(is_single_thread()); m_send_buffer.append_buffer(const_cast(buffer), size, size, destructor , userdata, ref); } boost::optional peer_connection::downloading_piece_progress() const { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ERROR" , "downloading_piece_progress() dispatched to the base class!"); #endif return boost::optional(); } namespace { void session_free_buffer(char* buffer, void* userdata, block_cache_reference) { aux::session_interface* ses = static_cast(userdata); ses->free_buffer(buffer); } } void peer_connection::send_buffer(char const* buf, int size, int flags) { TORRENT_ASSERT(is_single_thread()); TORRENT_UNUSED(flags); int free_space = m_send_buffer.space_in_last_buffer(); if (free_space > size) free_space = size; if (free_space > 0) { char* dst = m_send_buffer.append(buf, free_space); // this should always succeed, because we checked how much space // there was up-front TORRENT_UNUSED(dst); TORRENT_ASSERT(dst != 0); size -= free_space; buf += free_space; } if (size <= 0) return; int i = 0; while (size > 0) { char* chain_buf = m_ses.allocate_buffer(); if (chain_buf == 0) { disconnect(errors::no_memory, op_alloc_sndbuf); return; } const int alloc_buf_size = m_ses.send_buffer_size(); int buf_size = (std::min)(alloc_buf_size, size); memcpy(chain_buf, buf, buf_size); buf += buf_size; size -= buf_size; m_send_buffer.append_buffer(chain_buf, alloc_buf_size, buf_size , &session_free_buffer, &m_ses); ++i; } setup_send(); } template 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; }; void peer_connection::on_receive_data_nb(const error_code& error , std::size_t bytes_transferred) { TORRENT_ASSERT(is_single_thread()); #if defined TORRENT_ASIO_DEBUGGING complete_async("peer_connection::on_receive_data_nb"); #endif // leave this bit set until we're done looping, reading from the socket. // that way we don't trigger any async read calls until the end of this // function. TORRENT_ASSERT(m_channel_state[download_channel] & peer_info::bw_network); // nb is short for null_buffers. In this mode we don't actually // allocate a receive buffer up-front, but get notified when // we can read from the socket, and then determine how much there // is to read. if (error) { TORRENT_ASSERT_VAL(error.value() != 0, error.value()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ERROR" , "in peer_connection::on_receive_data_nb error: (%s:%d) %s" , error.category().name(), error.value() , error.message().c_str()); #endif on_receive(error, bytes_transferred); disconnect(error, op_sock_read); return; } error_code ec; std::size_t buffer_size = m_socket->available(ec); if (ec) { disconnect(ec, op_available); return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "READ_AVAILABLE" , "bytes: %d", int(buffer_size)); #endif // at this point the ioctl told us the socket doesn't have any // pending bytes. This probably means some error happened. // in order to find out though, we need to initiate a read // operation if (buffer_size == 0) { // try to read one byte. The socket is non-blocking anyway // so worst case, we'll fail with EWOULDBLOCK buffer_size = 1; } else { if (buffer_size > m_quota[download_channel]) { request_bandwidth(download_channel, buffer_size); buffer_size = m_quota[download_channel]; } // we're already waiting to get some more // quota from the bandwidth manager if (buffer_size == 0) { // allow reading from the socket again TORRENT_ASSERT(m_channel_state[download_channel] & peer_info::bw_network); m_channel_state[download_channel] &= ~peer_info::bw_network; return; } } if (buffer_size > 2097152) buffer_size = 2097152; boost::asio::mutable_buffer buffer = m_recv_buffer.reserve(buffer_size); TORRENT_ASSERT(m_recv_buffer.normalized()); // utp sockets aren't thread safe... if (is_utp(*m_socket)) { bytes_transferred = m_socket->read_some(boost::asio::mutable_buffers_1(buffer), ec); if (ec) { if (ec == boost::asio::error::try_again || ec == boost::asio::error::would_block) { // allow reading from the socket again TORRENT_ASSERT(m_channel_state[download_channel] & peer_info::bw_network); m_channel_state[download_channel] &= ~peer_info::bw_network; setup_receive(); return; } disconnect(ec, op_sock_read); return; } } else { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("peer_connection::on_receive_data"); #endif socket_job j; j.type = socket_job::read_job; j.recv_buf = boost::asio::buffer_cast(buffer); j.buf_size = boost::asio::buffer_size(buffer); j.peer = self(); m_ses.post_socket_job(j); return; } receive_data_impl(error, bytes_transferred, 0); } // -------------------------- // RECEIVE DATA // -------------------------- // nb is true if this callback is due to a null_buffers() // invocation of async_read_some(). In that case, we need // to disregard bytes_transferred. // at all exit points of this function, one of the following MUST hold: // 1. the socket is disconnecting // 2. m_channel_state[download_channel] & peer_info::bw_network == 0 void peer_connection::on_receive_data(const error_code& error , std::size_t bytes_transferred) { TORRENT_ASSERT(is_single_thread()); #if defined TORRENT_ASIO_DEBUGGING complete_async("peer_connection::on_receive_data"); #endif // leave this bit set until we're done looping, reading from the socket. // that way we don't trigger any async read calls until the end of this // function. TORRENT_ASSERT(m_channel_state[download_channel] & peer_info::bw_network); TORRENT_ASSERT(bytes_transferred > 0 || error); receive_data_impl(error, bytes_transferred, 10); } void peer_connection::receive_data_impl(const error_code& error , std::size_t bytes_transferred, int read_loops) { TORRENT_ASSERT(is_single_thread()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "ON_RECEIVE_DATA" , "bytes: %d error: (%s:%d) %s" , int(bytes_transferred), error.category().name(), error.value() , error.message().c_str()); #endif // submit all disk jobs later m_ses.deferred_submit_jobs(); // keep ourselves alive in until this function exits in // case we disconnect // this needs to be created before the invariant check, // to keep the object alive through the exit check boost::shared_ptr me(self()); // flush the send buffer at the end of this function cork _c(*this); INVARIANT_CHECK; int bytes_in_loop = bytes_transferred; if (error) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ERROR" , "in peer_connection::on_receive_data_impl error: %s" , error.message().c_str()); #endif trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); on_receive(error, bytes_transferred); disconnect(error, op_sock_read); return; } TORRENT_ASSERT(bytes_transferred > 0); m_counters.inc_stats_counter(counters::on_read_counter); m_ses.received_buffer(bytes_transferred); if (m_extension_outstanding_bytes > 0) m_extension_outstanding_bytes -= (std::min)(m_extension_outstanding_bytes, int(bytes_transferred)); check_graceful_pause(); if (m_disconnecting) return; int num_loops = 0; do { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "READ" , "%d bytes", int(bytes_transferred)); #endif // correct the dl quota usage, if not all of the buffer was actually read TORRENT_ASSERT(int(bytes_transferred) <= m_quota[download_channel]); m_quota[download_channel] -= bytes_transferred; if (m_disconnecting) { trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); return; } TORRENT_ASSERT(bytes_transferred > 0); m_recv_buffer.received(bytes_transferred); int bytes = bytes_transferred; int sub_transferred = 0; do { // TODO: The stats checks can not be honored when authenticated encryption is in use // because we may have encrypted data which we cannot authenticate yet #if 0 boost::int64_t cur_payload_dl = m_statistics.last_payload_downloaded(); boost::int64_t cur_protocol_dl = m_statistics.last_protocol_downloaded(); #endif sub_transferred = m_recv_buffer.advance_pos(bytes); on_receive(error, sub_transferred); bytes -= sub_transferred; TORRENT_ASSERT(sub_transferred > 0); #if 0 TORRENT_ASSERT(m_statistics.last_payload_downloaded() - cur_payload_dl >= 0); TORRENT_ASSERT(m_statistics.last_protocol_downloaded() - cur_protocol_dl >= 0); boost::int64_t stats_diff = m_statistics.last_payload_downloaded() - cur_payload_dl + m_statistics.last_protocol_downloaded() - cur_protocol_dl; TORRENT_ASSERT(stats_diff == int(sub_transferred)); #endif if (m_disconnecting) return; } while (bytes > 0 && sub_transferred > 0); m_recv_buffer.normalize(); TORRENT_ASSERT(m_recv_buffer.pos_at_end()); TORRENT_ASSERT(m_recv_buffer.packet_size() > 0); if (m_peer_choked) { m_recv_buffer.clamp_size(); } if (num_loops > read_loops) break; error_code ec; bytes_transferred = try_read(read_sync, ec); TORRENT_ASSERT(bytes_transferred > 0 || ec); if (ec == boost::asio::error::would_block || ec == boost::asio::error::try_again) break; if (ec) { trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); disconnect(ec, op_sock_read); return; } bytes_in_loop += bytes_transferred; ++num_loops; } while (bytes_transferred > 0); m_last_receive = aux::time_now(); if (is_seed()) { boost::shared_ptr t = m_torrent.lock(); if (t) t->seen_complete(); } trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); // allow reading from the socket again TORRENT_ASSERT(m_channel_state[download_channel] & peer_info::bw_network); m_channel_state[download_channel] &= ~peer_info::bw_network; setup_receive(); } bool peer_connection::can_write() const { TORRENT_ASSERT(is_single_thread()); // if we have requests or pending data to be sent or announcements to be made // we want to send data return !m_send_buffer.empty() && m_quota[upload_channel] > 0 && (m_send_barrier > 0) && !m_connecting; } bool peer_connection::can_read() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = m_torrent.lock(); bool bw_limit = m_quota[download_channel] > 0; if (!bw_limit) return false; if (m_outstanding_bytes > 0) { // if we're expecting to download piece data, we might not // want to read from the socket in case we're out of disk // cache space right now if (m_channel_state[download_channel] & peer_info::bw_disk) return false; } return !m_connecting && !m_disconnecting; } void peer_connection::on_connection_complete(error_code const& e) { TORRENT_ASSERT(is_single_thread()); #if defined TORRENT_ASIO_DEBUGGING complete_async("peer_connection::on_connection_complete"); #endif #if !defined TORRENT_DISABLE_LOGGING || defined TORRENT_USE_OPENSSL time_point completed = clock_type::now(); #endif INVARIANT_CHECK; #ifndef TORRENT_DISABLE_LOGGING { boost::shared_ptr t = m_torrent.lock(); if (t) t->debug_log("END connect [%p]", static_cast(this)); m_connect_time = completed; } #endif #ifdef TORRENT_USE_OPENSSL // add this RTT to the PRNG seed, to add more unpredictability boost::uint64_t now = total_microseconds(completed - m_connect); // assume 12 bits of entropy (i.e. about 8 milliseconds) RAND_add(&now, 8, 1.5); #endif // if t is NULL, we better not be connecting, since // we can't decrement the connecting counter boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t || !m_connecting); if (m_connecting) { m_counters.inc_stats_counter(counters::num_peers_half_open, -1); if (t) t->dec_num_connecting(m_peer_info); m_connecting = false; } if (m_disconnecting) return; if (e) { connect_failed(e); return; } TORRENT_ASSERT(!m_connected); m_connected = true; m_counters.inc_stats_counter(counters::num_peers_connected); if (m_disconnecting) return; m_last_receive = aux::time_now(); error_code ec; m_local = m_socket->local_endpoint(ec); if (ec) { disconnect(ec, op_getname); return; } // if there are outgoing interfaces specified, verify this // peer is correctly bound to one of them if (!m_settings.get_str(settings_pack::outgoing_interfaces).empty()) { if (!m_ses.verify_bound_address(m_local.address() , is_utp(*m_socket), ec)) { if (ec) { disconnect(ec, op_get_interface); return; } disconnect(error_code( boost::system::errc::no_such_device, generic_category()) , op_connect); return; } } if (is_utp(*m_socket) && m_peer_info) { m_peer_info->confirmed_supports_utp = true; m_peer_info->supports_utp = false; } // this means the connection just succeeded received_synack(m_remote.address().is_v6()); TORRENT_ASSERT(m_socket); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "COMPLETED" , "ep: %s", print_endpoint(m_remote).c_str()); #endif // set the socket to non-blocking, so that we can // read the entire buffer on each read event we get #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SET_NON_BLOCKING"); #endif m_socket->non_blocking(true, ec); if (ec) { disconnect(ec, op_iocontrol); return; } if (m_remote == m_socket->local_endpoint(ec)) { // if the remote endpoint is the same as the local endpoint, we're connected // to ourselves if (m_peer_info && t) t->ban_peer(m_peer_info); disconnect(errors::self_connection, op_bittorrent, 1); return; } if (m_remote.address().is_v4() && m_settings.get_int(settings_pack::peer_tos) != 0) { error_code err; m_socket->set_option(type_of_service(m_settings.get_int(settings_pack::peer_tos)), err); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "SET_TOS", "tos: %d e: %s" , m_settings.get_int(settings_pack::peer_tos), err.message().c_str()); #endif } #if TORRENT_USE_IPV6 && defined IPV6_TCLASS else if (m_remote.address().is_v6() && m_settings.get_int(settings_pack::peer_tos) != 0) { error_code err; m_socket->set_option(traffic_class(m_settings.get_int(settings_pack::peer_tos)), err); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "SET_TOS", "tos: %d e: %s" , m_settings.get_int(settings_pack::peer_tos), err.message().c_str()); #endif } #endif #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { (*i)->on_connected(); } #endif on_connected(); setup_send(); setup_receive(); } // -------------------------- // SEND DATA // -------------------------- void peer_connection::on_send_data(error_code const& error , std::size_t bytes_transferred) { TORRENT_ASSERT(is_single_thread()); m_counters.inc_stats_counter(counters::on_write_counter); m_ses.sent_buffer(bytes_transferred); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_socket_is_writing); m_socket_is_writing = false; #endif // submit all disk jobs when we've processed all messages // in the current message queue m_ses.deferred_submit_jobs(); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ON_SEND_DATA", "bytes: %d error: %s" , int(bytes_transferred), error.message().c_str()); #endif INVARIANT_CHECK; #if defined TORRENT_ASIO_DEBUGGING complete_async("peer_connection::on_send_data"); #endif // keep ourselves alive in until this function exits in // case we disconnect boost::shared_ptr me(self()); TORRENT_ASSERT(m_channel_state[upload_channel] & peer_info::bw_network); m_send_buffer.pop_front(bytes_transferred); time_point now = clock_type::now(); for (std::vector::iterator i = m_download_queue.begin() , end(m_download_queue.end()); i != end; ++i) { if (i->send_buffer_offset == pending_block::not_in_buffer) continue; boost::int32_t offset = i->send_buffer_offset; offset -= bytes_transferred; if (offset < 0) i->send_buffer_offset = pending_block::not_in_buffer; else i->send_buffer_offset = offset; } m_channel_state[upload_channel] &= ~peer_info::bw_network; TORRENT_ASSERT(int(bytes_transferred) <= m_quota[upload_channel]); m_quota[upload_channel] -= bytes_transferred; trancieve_ip_packet(bytes_transferred, m_remote.address().is_v6()); if (m_send_barrier != INT_MAX) m_send_barrier -= bytes_transferred; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing, "WROTE" , "%d bytes", int(bytes_transferred)); #endif if (error) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ERROR" , "%s in peer_connection::on_send_data", error.message().c_str()); #endif disconnect(error, op_sock_write); return; } if (m_disconnecting) { // make sure we free up all send buffers that are owned // by the disk thread m_send_buffer.clear(); m_recv_buffer.free_disk_buffer(); return; } TORRENT_ASSERT(!m_connecting); TORRENT_ASSERT(bytes_transferred > 0); m_last_sent = now; #if TORRENT_USE_ASSERTS boost::int64_t cur_payload_ul = m_statistics.last_payload_uploaded(); boost::int64_t cur_protocol_ul = m_statistics.last_protocol_uploaded(); #endif on_sent(error, bytes_transferred); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_statistics.last_payload_uploaded() - cur_payload_ul >= 0); TORRENT_ASSERT(m_statistics.last_protocol_uploaded() - cur_protocol_ul >= 0); boost::int64_t stats_diff = m_statistics.last_payload_uploaded() - cur_payload_ul + m_statistics.last_protocol_uploaded() - cur_protocol_ul; TORRENT_ASSERT(stats_diff == int(bytes_transferred)); #endif fill_send_buffer(); setup_send(); } #if TORRENT_USE_INVARIANT_CHECKS struct peer_count_t { peer_count_t(): num_peers(0), num_peers_with_timeouts(0), num_peers_with_nowant(0), num_not_requested(0) {} int num_peers; int num_peers_with_timeouts; int num_peers_with_nowant; int num_not_requested; // std::vector peers; }; void peer_connection::check_invariant() const { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_in_use == 1337); TORRENT_ASSERT(m_queued_time_critical <= int(m_request_queue.size())); TORRENT_ASSERT(m_accept_fast.size() == m_accept_fast_piece_cnt.size()); m_recv_buffer.check_invariant(); for (int i = 0; i < 2; ++i) { if (m_channel_state[i] & peer_info::bw_limit) { // if we're waiting for bandwidth, we should be in the // bandwidth manager's queue TORRENT_ASSERT(m_ses.get_bandwidth_manager(i)->is_queued(this)); } } boost::shared_ptr t = m_torrent.lock(); #if TORRENT_USE_INVARIANT_CHECKS \ && !defined TORRENT_NO_EXPENSIVE_INVARIANT_CHECK if (t && t->has_picker() && !m_disconnecting) t->picker().check_peer_invariant(m_have_piece, peer_info_struct()); #endif if (!m_disconnect_started && m_initialized) { // none of this matters if we're disconnecting anyway if (t->is_finished()) TORRENT_ASSERT(!is_interesting() || m_need_interest_update); if (is_seed()) TORRENT_ASSERT(upload_only()); } if (m_disconnecting) { TORRENT_ASSERT(m_download_queue.empty()); TORRENT_ASSERT(m_request_queue.empty()); TORRENT_ASSERT(m_disconnect_started); } else if (!m_in_constructor) { TORRENT_ASSERT(m_ses.has_peer(this)); } TORRENT_ASSERT(m_outstanding_bytes >= 0); if (t && t->valid_metadata() && !m_disconnecting) { torrent_info const& ti = t->torrent_file(); // if the piece is fully downloaded, we might have popped it from the // download queue already int outstanding_bytes = 0; // bool in_download_queue = false; int block_size = t->block_size(); piece_block last_block(ti.num_pieces()-1 , (ti.piece_size(ti.num_pieces()-1) + block_size - 1) / block_size); for (std::vector::const_iterator i = m_download_queue.begin() , end(m_download_queue.end()); i != end; ++i) { TORRENT_ASSERT(i->block.piece_index <= last_block.piece_index); TORRENT_ASSERT(i->block.piece_index < last_block.piece_index || i->block.block_index <= last_block.block_index); if (m_received_in_piece && i == m_download_queue.begin()) { // in_download_queue = true; // this assert is not correct since block may have different sizes // and may not be returned in the order they were requested // TORRENT_ASSERT(t->to_req(i->block).length >= m_received_in_piece); outstanding_bytes += t->to_req(i->block).length - m_received_in_piece; } else { outstanding_bytes += t->to_req(i->block).length; } } //if (p && p->bytes_downloaded < p->full_block_bytes) TORRENT_ASSERT(in_download_queue); if (m_outstanding_bytes != outstanding_bytes) { fprintf(stderr, "m_outstanding_bytes = %d\noutstanding_bytes = %d\n" , m_outstanding_bytes, outstanding_bytes); } TORRENT_ASSERT(m_outstanding_bytes == outstanding_bytes); } std::set unique; std::transform(m_download_queue.begin(), m_download_queue.end() , std::inserter(unique, unique.begin()), boost::bind(&pending_block::block, _1)); std::transform(m_request_queue.begin(), m_request_queue.end() , std::inserter(unique, unique.begin()), boost::bind(&pending_block::block, _1)); TORRENT_ASSERT(unique.size() == m_download_queue.size() + m_request_queue.size()); if (m_peer_info) { TORRENT_ASSERT(m_peer_info->prev_amount_upload == 0); TORRENT_ASSERT(m_peer_info->prev_amount_download == 0); TORRENT_ASSERT(m_peer_info->connection == this || m_peer_info->connection == 0); if (m_peer_info->optimistically_unchoked) TORRENT_ASSERT(!is_choked()); } TORRENT_ASSERT(m_have_piece.count() == m_num_pieces); if (!t) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS // since this connection doesn't have a torrent reference // no torrent should have a reference to this connection either TORRENT_ASSERT(!m_ses.any_torrent_has_peer(this)); #endif return; } if (t->ready_for_connections() && m_initialized) TORRENT_ASSERT(t->torrent_file().num_pieces() == int(m_have_piece.size())); // in share mode we don't close redundant connections if (m_settings.get_bool(settings_pack::close_redundant_connections) && !t->share_mode()) { bool const ok_to_disconnect = can_disconnect(errors::upload_upload_connection) || can_disconnect(errors::uninteresting_upload_peer) || can_disconnect(errors::too_many_requests_when_choked) || can_disconnect(errors::timed_out_no_interest) || can_disconnect(errors::timed_out_no_request) || can_disconnect(errors::timed_out_inactivity); // make sure upload only peers are disconnected if (t->is_upload_only() && m_upload_only && !m_need_interest_update && t->valid_metadata() && has_metadata() && ok_to_disconnect) TORRENT_ASSERT(m_disconnect_started || t->graceful_pause() || t->has_error()); if (m_upload_only && !m_interesting && !m_need_interest_update && m_bitfield_received && t->are_files_checked() && t->valid_metadata() && has_metadata() && ok_to_disconnect) TORRENT_ASSERT(m_disconnect_started); } if (!m_disconnect_started && m_initialized && m_settings.get_bool(settings_pack::close_redundant_connections)) { // none of this matters if we're disconnecting anyway if (t->is_upload_only() && !m_need_interest_update) TORRENT_ASSERT(!m_interesting || t->graceful_pause() || t->has_error()); if (is_seed()) TORRENT_ASSERT(m_upload_only); } #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS if (t->has_picker()) { std::map num_requests; for (torrent::const_peer_iterator i = t->begin(); i != t->end(); ++i) { // make sure this peer is not a dangling pointer TORRENT_ASSERT(m_ses.has_peer(*i)); peer_connection const& p = *(*i); for (std::vector::const_iterator j = p.request_queue().begin() , end(p.request_queue().end()); j != end; ++j) { ++num_requests[j->block].num_peers; ++num_requests[j->block].num_peers_with_timeouts; ++num_requests[j->block].num_peers_with_nowant; ++num_requests[j->block].num_not_requested; // num_requests[j->block].peers.push_back(&p); } for (std::vector::const_iterator j = p.download_queue().begin() , end(p.download_queue().end()); j != end; ++j) { if (!j->not_wanted && !j->timed_out) ++num_requests[j->block].num_peers; if (j->timed_out) ++num_requests[j->block].num_peers_with_timeouts; if (j->not_wanted) ++num_requests[j->block].num_peers_with_nowant; // num_requests[j->block].peers.push_back(&p); } } for (std::map::iterator j = num_requests.begin() , end(num_requests.end()); j != end; ++j) { piece_block b = j->first; peer_count_t const& pc = j->second; int count = pc.num_peers; int count_with_timeouts = pc.num_peers_with_timeouts; int count_with_nowant = pc.num_peers_with_nowant; (void)count_with_timeouts; (void)count_with_nowant; int picker_count = t->picker().num_peers(b); if (!t->picker().is_downloaded(b)) TORRENT_ASSERT(picker_count == count); } } #endif /* if (t->has_picker() && !t->is_aborted()) { for (std::vector::const_iterator i = m_download_queue.begin() , end(m_download_queue.end()); i != end; ++i) { pending_block const& pb = *i; if (pb.timed_out || pb.not_wanted) continue; TORRENT_ASSERT(t->picker().get_block_state(pb.block) != piece_picker::block_info::state_none); TORRENT_ASSERT(complete); } } */ // extremely expensive invariant check /* if (!t->is_seed()) { piece_picker& p = t->picker(); const std::vector& dlq = p.get_download_queue(); const int blocks_per_piece = static_cast( t->torrent_file().piece_length() / t->block_size()); for (std::vector::const_iterator i = dlq.begin(); i != dlq.end(); ++i) { for (int j = 0; j < blocks_per_piece; ++j) { if (std::find(m_request_queue.begin(), m_request_queue.end() , piece_block(i->index, j)) != m_request_queue.end() || std::find(m_download_queue.begin(), m_download_queue.end() , piece_block(i->index, j)) != m_download_queue.end()) { TORRENT_ASSERT(i->info[j].peer == m_remote); } else { TORRENT_ASSERT(i->info[j].peer != m_remote || i->info[j].finished); } } } } */ } #endif void peer_connection::set_holepunch_mode() { m_holepunch_mode = true; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "HOLEPUNCH_MODE", "[ on ]"); #endif } void peer_connection::keep_alive() { TORRENT_ASSERT(is_single_thread()); #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; #endif time_duration d; d = aux::time_now() - m_last_sent; if (total_seconds(d) < timeout() / 2) return; if (m_connecting) return; if (in_handshake()) return; // if the last send has not completed yet, do not send a keep // alive if (m_channel_state[upload_channel] & peer_info::bw_network) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "KEEPALIVE"); #endif write_keepalive(); } bool peer_connection::is_seed() const { TORRENT_ASSERT(is_single_thread()); // if m_num_pieces == 0, we probably don't have the // metadata yet. boost::shared_ptr t = m_torrent.lock(); return m_num_pieces == int(m_have_piece.size()) && m_num_pieces > 0 && t && t->valid_metadata(); } void peer_connection::set_share_mode(bool u) { TORRENT_ASSERT(is_single_thread()); // if the peer is a seed, ignore share mode messages if (is_seed()) return; m_share_mode = u; } void peer_connection::set_upload_only(bool u) { TORRENT_ASSERT(is_single_thread()); // if the peer is a seed, don't allow setting // upload_only to false if (m_upload_only || is_seed()) return; m_upload_only = u; boost::shared_ptr t = associated_torrent().lock(); t->set_seed(m_peer_info, u); disconnect_if_redundant(); } } libtorrent-rasterbar-1.1.13/src/peer_connection_handle.cpp000066400000000000000000000220021351156116000237170ustar00rootroot00000000000000/* Copyright (c) 2015-2018, Arvid Norberg, Steven Siloti 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/peer_connection_handle.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/bt_peer_connection.hpp" #ifndef TORRENT_DISABLE_LOGGING #include // for va_start, va_end #endif namespace libtorrent { int peer_connection_handle::type() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->type(); } void peer_connection_handle::add_extension(boost::shared_ptr ext) { #ifndef TORRENT_DISABLE_EXTENSIONS boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); pc->add_extension(ext); #else TORRENT_UNUSED(ext); #endif } peer_plugin const* peer_connection_handle::find_plugin(char const* type) { #ifndef TORRENT_DISABLE_EXTENSIONS boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->find_plugin(type); #else TORRENT_UNUSED(type); return NULL; #endif } bool peer_connection_handle::is_seed() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->is_seed(); } bool peer_connection_handle::upload_only() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->upload_only(); } peer_id const& peer_connection_handle::pid() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->pid(); } bool peer_connection_handle::has_piece(int i) const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->has_piece(i); } bool peer_connection_handle::is_interesting() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->is_interesting(); } bool peer_connection_handle::is_choked() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->is_choked(); } bool peer_connection_handle::is_peer_interested() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->is_peer_interested(); } bool peer_connection_handle::has_peer_choked() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->has_peer_choked(); } void peer_connection_handle::choke_this_peer() { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); pc->choke_this_peer(); } void peer_connection_handle::maybe_unchoke_this_peer() { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); pc->maybe_unchoke_this_peer(); } void peer_connection_handle::get_peer_info(peer_info& p) const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); pc->get_peer_info(p); } torrent_handle peer_connection_handle::associated_torrent() const { boost::shared_ptr pc = native_handle(); if (!pc) return torrent_handle(); boost::shared_ptr t = pc->associated_torrent().lock(); if (!t) return torrent_handle(); return t->get_handle(); } tcp::endpoint const& peer_connection_handle::remote() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->remote(); } tcp::endpoint peer_connection_handle::local_endpoint() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->local_endpoint(); } void peer_connection_handle::disconnect(error_code const& ec, operation_t op, int error) { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); pc->disconnect(ec, op, error); } bool peer_connection_handle::is_disconnecting() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->is_disconnecting(); } bool peer_connection_handle::is_connecting() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->is_connecting(); } bool peer_connection_handle::is_outgoing() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->is_outgoing(); } bool peer_connection_handle::on_local_network() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->on_local_network(); } bool peer_connection_handle::ignore_unchoke_slots() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->ignore_unchoke_slots(); } bool peer_connection_handle::failed() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->failed(); } #ifndef TORRENT_DISABLE_LOGGING TORRENT_FORMAT(4,5) void peer_connection_handle::peer_log(peer_log_alert::direction_t direction , char const* event, char const* fmt, ...) const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); va_list v; va_start(v, fmt); #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat-nonliteral" #endif pc->peer_log(direction, event, fmt, v); #ifdef __clang__ #pragma clang diagnostic pop #endif va_end(v); } #endif // TORRENT_DISABLE_LOGGING bool peer_connection_handle::can_disconnect(error_code const& ec) const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->can_disconnect(ec); } bool peer_connection_handle::has_metadata() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->has_metadata(); } bool peer_connection_handle::in_handshake() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->in_handshake(); } void peer_connection_handle::send_buffer(char const* begin, int size, int flags) { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); pc->send_buffer(begin, size, flags); } time_t peer_connection_handle::last_seen_complete() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->last_seen_complete(); } time_point peer_connection_handle::time_of_last_unchoke() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->time_of_last_unchoke(); } bool bt_peer_connection_handle::packet_finished() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->packet_finished(); } bool bt_peer_connection_handle::support_extensions() const { boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->support_extensions(); } bool bt_peer_connection_handle::supports_encryption() const { #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); return pc->supports_encryption(); #else return false; #endif } void bt_peer_connection_handle::switch_send_crypto(boost::shared_ptr crypto) { #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); pc->switch_send_crypto(crypto); #else TORRENT_UNUSED(crypto); #endif } void bt_peer_connection_handle::switch_recv_crypto(boost::shared_ptr crypto) { #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) boost::shared_ptr pc = native_handle(); TORRENT_ASSERT(pc); pc->switch_recv_crypto(crypto); #else TORRENT_UNUSED(crypto); #endif } boost::shared_ptr bt_peer_connection_handle::native_handle() const { return boost::static_pointer_cast( peer_connection_handle::native_handle()); } } // namespace libtorrent libtorrent-rasterbar-1.1.13/src/peer_list.cpp000066400000000000000000001137271351156116000212370ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/web_peer_connection.hpp" #include "libtorrent/peer_list.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/time.hpp" #include "libtorrent/aux_/session_interface.hpp" #include "libtorrent/piece_picker.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/random.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/torrent_peer_allocator.hpp" #ifdef TORRENT_DEBUG #include "libtorrent/bt_peer_connection.hpp" #endif #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS #include "libtorrent/socket_io.hpp" // for print_endpoint #endif #ifndef TORRENT_DISABLE_LOGGING #include "libtorrent/socket_io.hpp" // for print_endpoint #include "libtorrent/ip_voter.hpp" // for external_ip #endif namespace { using namespace libtorrent; struct match_peer_endpoint { match_peer_endpoint(tcp::endpoint const& ep) : m_ep(ep) {} bool operator()(torrent_peer const* p) const { TORRENT_ASSERT(p->in_use); return p->address() == m_ep.address() && p->port == m_ep.port(); } tcp::endpoint const& m_ep; }; #ifndef TORRENT_DISABLE_INVARIANT_CHECKS struct match_peer_connection { match_peer_connection(peer_connection_interface const& c) : m_conn(c) {} bool operator()(torrent_peer const* p) const { TORRENT_ASSERT(p->in_use); return p->connection == &m_conn; } peer_connection_interface const& m_conn; }; #endif #if TORRENT_USE_ASSERTS struct match_peer_connection_or_endpoint { match_peer_connection_or_endpoint(peer_connection_interface const& c) : m_conn(c) {} bool operator()(torrent_peer const* p) const { TORRENT_ASSERT(p->in_use); return p->connection == &m_conn || (p->ip() == m_conn.remote() && p->connectable); } peer_connection_interface const& m_conn; }; #endif } namespace libtorrent { peer_list::peer_list(torrent_peer_allocator_interface& alloc) : m_locked_peer(NULL) , m_peer_allocator(alloc) , m_num_seeds(0) , m_finished(0) , m_round_robin(0) , m_num_connect_candidates(0) , m_max_failcount(3) { thread_started(); } peer_list::~peer_list() { for (peers_t::iterator i = m_peers.begin() , end(m_peers.end()); i != end; ++i) { m_peer_allocator.free_peer_entry(*i); } } void peer_list::set_max_failcount(torrent_state* state) { if (state->max_failcount == m_max_failcount) return; recalculate_connect_candidates(state); } // disconnects and removes all peers that are now filtered fills in 'erased' // with torrent_peer pointers that were removed from the peer list. Any // references to these peers must be cleared immediately after this call // returns. For instance, in the piece picker. void peer_list::apply_ip_filter(ip_filter const& filter , torrent_state* state, std::vector
& banned) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; for (iterator i = m_peers.begin(); i != m_peers.end();) { if ((filter.access((*i)->address()) & ip_filter::blocked) == 0) { ++i; continue; } if (*i == m_locked_peer) { ++i; continue; } int current = i - m_peers.begin(); TORRENT_ASSERT(current >= 0); TORRENT_ASSERT(m_peers.size() > 0); TORRENT_ASSERT(i != m_peers.end()); if ((*i)->connection) { // disconnecting the peer here may also delete the // peer_info_struct. If that is the case, just continue size_t count = m_peers.size(); peer_connection_interface* p = (*i)->connection; banned.push_back(p->remote().address()); p->disconnect(errors::banned_by_ip_filter , op_bittorrent); // what *i refers to has changed, i.e. cur was deleted if (m_peers.size() < count) { i = m_peers.begin() + current; continue; } TORRENT_ASSERT((*i)->connection == 0 || (*i)->connection->peer_info_struct() == 0); } erase_peer(i, state); i = m_peers.begin() + current; } } void peer_list::clear_peer_prio() { for (peers_t::iterator i = m_peers.begin() , end(m_peers.end()); i != end; ++i) (*i)->peer_rank = 0; } // disconnects and removes all peers that are now filtered // fills in 'erased' with torrent_peer pointers that were removed // from the peer list. Any references to these peers must be cleared // immediately after this call returns. For instance, in the piece picker. void peer_list::apply_port_filter(port_filter const& filter , torrent_state* state, std::vector
& banned) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; for (iterator i = m_peers.begin(); i != m_peers.end();) { if ((filter.access((*i)->port) & port_filter::blocked) == 0) { ++i; continue; } if (*i == m_locked_peer) { ++i; continue; } int current = i - m_peers.begin(); TORRENT_ASSERT(current >= 0); TORRENT_ASSERT(m_peers.size() > 0); TORRENT_ASSERT(i != m_peers.end()); if ((*i)->connection) { // disconnecting the peer here may also delete the // peer_info_struct. If that is the case, just continue int count = m_peers.size(); peer_connection_interface* p = (*i)->connection; banned.push_back(p->remote().address()); p->disconnect(errors::banned_by_port_filter, op_bittorrent); // what *i refers to has changed, i.e. cur was deleted if (int(m_peers.size()) < count) { i = m_peers.begin() + current; continue; } TORRENT_ASSERT((*i)->connection == 0 || (*i)->connection->peer_info_struct() == 0); } erase_peer(i, state); i = m_peers.begin() + current; } } void peer_list::erase_peer(torrent_peer* p, torrent_state* state) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(p->in_use); TORRENT_ASSERT(m_locked_peer != p); std::pair range = find_peers(p->address()); iterator iter = std::find_if(range.first, range.second, match_peer_endpoint(p->ip())); if (iter == range.second) return; erase_peer(iter, state); } // any peer that is erased from m_peers will be // erased through this function. This way we can make // sure that any references to the peer are removed // as well, such as in the piece picker. void peer_list::erase_peer(iterator i, torrent_state* state) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(i != m_peers.end()); TORRENT_ASSERT(m_locked_peer != *i); state->erased.push_back(*i); if ((*i)->seed) { TORRENT_ASSERT(m_num_seeds > 0); --m_num_seeds; } if (is_connect_candidate(**i)) update_connect_candidates(-1); TORRENT_ASSERT(m_num_connect_candidates < int(m_peers.size())); if (m_round_robin > i - m_peers.begin()) --m_round_robin; if (m_round_robin >= int(m_peers.size())) m_round_robin = 0; // if this peer is in the connect candidate // cache, erase it from there as well std::vector::iterator ci = std::find(m_candidate_cache.begin(), m_candidate_cache.end(), *i); if (ci != m_candidate_cache.end()) m_candidate_cache.erase(ci); #if TORRENT_USE_ASSERTS TORRENT_ASSERT((*i)->in_use); (*i)->in_use = false; #endif m_peer_allocator.free_peer_entry(*i); m_peers.erase(i); } bool peer_list::should_erase_immediately(torrent_peer const& p) const { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(p.in_use); if (&p == m_locked_peer) return false; return p.source == peer_info::resume_data; } bool peer_list::is_erase_candidate(torrent_peer const& pe) const { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(pe.in_use); if (&pe == m_locked_peer) return false; if (pe.connection) return false; if (is_connect_candidate(pe)) return false; return (pe.failcount > 0) || (pe.source == peer_info::resume_data); } bool peer_list::is_force_erase_candidate(torrent_peer const& pe) const { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(pe.in_use); if (&pe == m_locked_peer) return false; return pe.connection == 0; } void peer_list::erase_peers(torrent_state* state, int flags) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; int max_peerlist_size = state->max_peerlist_size; if (max_peerlist_size == 0 || m_peers.empty()) return; int erase_candidate = -1; int force_erase_candidate = -1; if (m_finished != state->is_finished) recalculate_connect_candidates(state); int round_robin = random() % m_peers.size(); int low_watermark = max_peerlist_size * 95 / 100; if (low_watermark == max_peerlist_size) --low_watermark; for (int iterations = (std::min)(int(m_peers.size()), 300); iterations > 0; --iterations) { if (int(m_peers.size()) < low_watermark) break; if (round_robin == int(m_peers.size())) round_robin = 0; torrent_peer& pe = *m_peers[round_robin]; TORRENT_ASSERT(pe.in_use); int current = round_robin; if (is_erase_candidate(pe) && (erase_candidate == -1 || !compare_peer_erase(*m_peers[erase_candidate], pe))) { if (should_erase_immediately(pe)) { if (erase_candidate > current) --erase_candidate; if (force_erase_candidate > current) --force_erase_candidate; TORRENT_ASSERT(current >= 0 && current < int(m_peers.size())); erase_peer(m_peers.begin() + current, state); continue; } else { erase_candidate = current; } } if (is_force_erase_candidate(pe) && (force_erase_candidate == -1 || !compare_peer_erase(*m_peers[force_erase_candidate], pe))) { force_erase_candidate = current; } ++round_robin; } if (erase_candidate > -1) { TORRENT_ASSERT(erase_candidate >= 0 && erase_candidate < int(m_peers.size())); erase_peer(m_peers.begin() + erase_candidate, state); } else if ((flags & force_erase) && force_erase_candidate > -1) { TORRENT_ASSERT(force_erase_candidate >= 0 && force_erase_candidate < int(m_peers.size())); erase_peer(m_peers.begin() + force_erase_candidate, state); } } // returns true if the peer was actually banned bool peer_list::ban_peer(torrent_peer* p) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(p->in_use); if (is_connect_candidate(*p)) update_connect_candidates(-1); p->banned = true; TORRENT_ASSERT(!is_connect_candidate(*p)); return true; } void peer_list::set_connection(torrent_peer* p, peer_connection_interface* c) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(p->in_use); TORRENT_ASSERT(c); const bool was_conn_cand = is_connect_candidate(*p); p->connection = c; if (was_conn_cand) update_connect_candidates(-1); } void peer_list::inc_failcount(torrent_peer* p) { // failcount is a 5 bit value if (p->failcount == 31) return; const bool was_conn_cand = is_connect_candidate(*p); ++p->failcount; if (was_conn_cand && !is_connect_candidate(*p)) update_connect_candidates(-1); } void peer_list::set_failcount(torrent_peer* p, int f) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(p->in_use); const bool was_conn_cand = is_connect_candidate(*p); p->failcount = f; if (was_conn_cand != is_connect_candidate(*p)) { update_connect_candidates(was_conn_cand ? -1 : 1); } } bool peer_list::is_connect_candidate(torrent_peer const& p) const { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(p.in_use); if (p.connection || p.banned || p.web_seed || !p.connectable || (p.seed && m_finished) || int(p.failcount) >= m_max_failcount) return false; return true; } void peer_list::find_connect_candidates(std::vector& peers , int session_time, torrent_state* state) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; const int candidate_count = 10; peers.reserve(candidate_count); int erase_candidate = -1; if (m_finished != state->is_finished) recalculate_connect_candidates(state); external_ip const& external = *state->ip; int external_port = state->port; if (m_round_robin >= int(m_peers.size())) m_round_robin = 0; int max_peerlist_size = state->max_peerlist_size; // TODO: 2 it would be nice if there was a way to iterate over these // torrent_peer objects in the order they are allocated in the pool // instead. It would probably be more efficient for (int iterations = (std::min)(int(m_peers.size()), 300); iterations > 0; --iterations) { ++state->loop_counter; if (m_round_robin >= int(m_peers.size())) m_round_robin = 0; torrent_peer& pe = *m_peers[m_round_robin]; TORRENT_ASSERT(pe.in_use); int current = m_round_robin; // if the number of peers is growing large // we need to start weeding. if (int(m_peers.size()) >= max_peerlist_size * 0.95 && max_peerlist_size > 0) { if (is_erase_candidate(pe) && (erase_candidate == -1 || !compare_peer_erase(*m_peers[erase_candidate], pe))) { if (should_erase_immediately(pe)) { if (erase_candidate > current) --erase_candidate; erase_peer(m_peers.begin() + current, state); continue; } else { erase_candidate = current; } } } ++m_round_robin; if (!is_connect_candidate(pe)) continue; if (pe.last_connected && session_time - pe.last_connected < (int(pe.failcount) + 1) * state->min_reconnect_time) continue; // compare peer returns true if lhs is better than rhs. In this // case, it returns true if the current candidate is better than // pe, which is the peer m_round_robin points to. If it is, just // keep looking. if (peers.size() == candidate_count && compare_peer(peers.back(), &pe, external, external_port)) continue; if (peers.size() >= candidate_count) peers.resize(candidate_count - 1); // insert this candidate sorted into peers std::vector::iterator i = std::lower_bound(peers.begin(), peers.end() , &pe, boost::bind(&peer_list::compare_peer, this, _1, _2, boost::cref(external), external_port)); peers.insert(i, &pe); } if (erase_candidate > -1) { erase_peer(m_peers.begin() + erase_candidate, state); } } bool peer_list::new_connection(peer_connection_interface& c, int session_time , torrent_state* state) { TORRENT_ASSERT(is_single_thread()); // TORRENT_ASSERT(!c.is_outgoing()); INVARIANT_CHECK; TORRENT_ASSERT(!state->is_paused); iterator iter; torrent_peer* i = 0; bool found = false; if (state->allow_multiple_connections_per_ip) { tcp::endpoint remote = c.remote(); std::pair range = find_peers(remote.address()); iter = std::find_if(range.first, range.second, match_peer_endpoint(remote)); if (iter != range.second) { TORRENT_ASSERT((*iter)->in_use); found = true; } } else { iter = std::lower_bound( m_peers.begin(), m_peers.end() , c.remote().address(), peer_address_compare() ); if (iter != m_peers.end() && (*iter)->address() == c.remote().address()) { TORRENT_ASSERT((*iter)->in_use); found = true; } } // make sure the iterator we got is properly sorted relative // to the connection's address // TORRENT_ASSERT(m_peers.empty() // || (iter == m_peers.end() && (*(iter-1))->address() < c.remote().address()) // || (iter != m_peers.end() && c.remote().address() < (*iter)->address()) // || (iter != m_peers.end() && iter != m_peers.begin() && (*(iter-1))->address() < c.remote().address())); if (found) { i = *iter; TORRENT_ASSERT(i->in_use); TORRENT_ASSERT(i->connection != &c); TORRENT_ASSERT(i->address() == c.remote().address()); #ifndef TORRENT_DISABLE_LOGGING c.peer_log(peer_log_alert::info, "DUPLICATE PEER", "this: \"%s\" that: \"%s\"" , print_address(c.remote().address()).c_str() , print_address(i->address()).c_str()); #endif if (i->banned) { c.disconnect(errors::peer_banned, op_bittorrent); return false; } if (i->connection != 0) { bool const self_connection = i->connection->remote() == c.local_endpoint() || i->connection->local_endpoint() == c.remote(); if (self_connection) { c.disconnect(errors::self_connection, op_bittorrent, 1); TORRENT_ASSERT(i->connection->peer_info_struct() == i); i->connection->disconnect(errors::self_connection, op_bittorrent, 1); TORRENT_ASSERT(i->connection == 0); return false; } TORRENT_ASSERT(i->connection != &c); // the new connection is a local (outgoing) connection // or the current one is already connected if (i->connection->is_outgoing() == c.is_outgoing()) { // if the other end connected to us both times, just drop // the second one. Or if we made both connections. c.disconnect(errors::duplicate_peer_id, op_bittorrent); return false; } else { // at this point, we need to disconnect either // i->connection or c. In order for both this client // and the client on the other end to decide to // disconnect the same one, we need a consistent rule to // select which one. bool const outgoing1 = c.is_outgoing(); // for this, we compare our ports and whoever has the lower port // should be the one keeping its outgoing connection. Since // outgoing ports are selected at random by the OS, we need to // be careful to only look at the target end of a connection for // the endpoint. int const our_port = outgoing1 ? i->connection->local_endpoint().port() : c.local_endpoint().port(); int const other_port = outgoing1 ? c.remote().port() : i->connection->remote().port(); // decide which peer connection to disconnect // if the ports are equal, pick on at random bool const disconnect1 = ((our_port < other_port) && !outgoing1) || ((our_port > other_port) && outgoing1) || ((our_port == other_port) && randint(2)); #ifndef TORRENT_DISABLE_LOGGING c.peer_log(peer_log_alert::info, "DUPLICATE_PEER_RESOLUTION" , "our: %d other: %d disconnecting: %s" , our_port, other_port, disconnect1 ? "yes" : "no"); i->connection->peer_log(peer_log_alert::info, "DUPLICATE_PEER_RESOLUTION" , "our: %d other: %d disconnecting: %s" , our_port, other_port, disconnect1 ? "no" : "yes"); #endif if (disconnect1) { c.disconnect(errors::duplicate_peer_id, op_bittorrent); return false; } TORRENT_ASSERT(m_locked_peer == NULL); m_locked_peer = i; i->connection->disconnect(errors::duplicate_peer_id, op_bittorrent); m_locked_peer = NULL; } } if (is_connect_candidate(*i)) update_connect_candidates(-1); } else { // we don't have any info about this peer. // add a new entry if (state->max_peerlist_size && int(m_peers.size()) >= state->max_peerlist_size) { // this may invalidate our iterator! erase_peers(state, force_erase); if (int(m_peers.size()) >= state->max_peerlist_size) { c.disconnect(errors::too_many_connections, op_bittorrent); return false; } // restore it iter = std::lower_bound( m_peers.begin(), m_peers.end() , c.remote().address(), peer_address_compare() ); } #if TORRENT_USE_IPV6 bool is_v6 = c.remote().address().is_v6(); #else bool is_v6 = false; #endif torrent_peer* p = m_peer_allocator.allocate_peer_entry( is_v6 ? torrent_peer_allocator_interface::ipv6_peer_type : torrent_peer_allocator_interface::ipv4_peer_type); if (p == 0) return false; #if TORRENT_USE_IPV6 if (is_v6) new (p) ipv6_peer(c.remote(), false, 0); else #endif new (p) ipv4_peer(c.remote(), false, 0); #if TORRENT_USE_ASSERTS p->in_use = true; #endif iter = m_peers.insert(iter, p); if (m_round_robin >= iter - m_peers.begin()) ++m_round_robin; i = *iter; i->source = peer_info::incoming; } TORRENT_ASSERT(i); c.set_peer_info(i); TORRENT_ASSERT(i->connection == 0); c.add_stat(boost::int64_t(i->prev_amount_download) << 10, boost::int64_t(i->prev_amount_upload) << 10); i->prev_amount_download = 0; i->prev_amount_upload = 0; i->connection = &c; TORRENT_ASSERT(i->connection); if (!c.fast_reconnect()) i->last_connected = session_time; // this cannot be a connect candidate anymore, since i->connection is set TORRENT_ASSERT(!is_connect_candidate(*i)); TORRENT_ASSERT(has_connection(&c)); return true; } bool peer_list::update_peer_port(int port, torrent_peer* p, int src, torrent_state* state) { TORRENT_ASSERT(p != 0); TORRENT_ASSERT(p->connection); TORRENT_ASSERT(p->in_use); TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (p->port == port) return true; if (state->allow_multiple_connections_per_ip) { tcp::endpoint remote(p->address(), port); std::pair range = find_peers(remote.address()); iterator i = std::find_if(range.first, range.second , match_peer_endpoint(remote)); if (i != range.second) { torrent_peer& pp = **i; TORRENT_ASSERT(pp.in_use); if (pp.connection) { bool was_conn_cand = is_connect_candidate(pp); // if we already have an entry with this // new endpoint, disconnect this one pp.connectable = true; pp.source |= src; if (!was_conn_cand && is_connect_candidate(pp)) update_connect_candidates(1); // calling disconnect() on a peer, may actually end // up "garbage collecting" its torrent_peer entry // as well, if it's considered useless (which this specific) // case will, since it was an incoming peer that just disconnected // and we allow multiple connections per IP. Because of that, // we need to make sure we don't let it do that, locking i TORRENT_ASSERT(m_locked_peer == NULL); m_locked_peer = p; p->connection->disconnect(errors::duplicate_peer_id, op_bittorrent); m_locked_peer = NULL; erase_peer(p, state); return false; } erase_peer(i, state); } } #if TORRENT_USE_ASSERTS else { #if TORRENT_USE_I2P if (!p->is_i2p_addr) #endif { std::pair range = find_peers(p->address()); TORRENT_ASSERT(std::distance(range.first, range.second) == 1); } } #endif bool was_conn_cand = is_connect_candidate(*p); p->port = port; p->source |= src; p->connectable = true; if (was_conn_cand != is_connect_candidate(*p)) update_connect_candidates(was_conn_cand ? -1 : 1); return true; } // it's important that we don't dereference // p here, since it is allowed to be a dangling // pointer. see smart_ban.cpp bool peer_list::has_peer(torrent_peer const* p) const { TORRENT_ASSERT(is_single_thread()); // find p in m_peers for (const_iterator i = m_peers.begin() , end(m_peers.end()); i != end; ++i) { if (*i == p) return true; } return false; } void peer_list::set_seed(torrent_peer* p, bool s) { TORRENT_ASSERT(is_single_thread()); if (p == 0) return; TORRENT_ASSERT(p->in_use); if (p->seed == s) return; bool was_conn_cand = is_connect_candidate(*p); p->seed = s; if (was_conn_cand && !is_connect_candidate(*p)) update_connect_candidates(-1); if (p->web_seed) return; if (s) { TORRENT_ASSERT(m_num_seeds < int(m_peers.size())); ++m_num_seeds; } else { TORRENT_ASSERT(m_num_seeds > 0); --m_num_seeds; } } // this is an internal function bool peer_list::insert_peer(torrent_peer* p, iterator iter, int flags , torrent_state* state) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(p); TORRENT_ASSERT(p->in_use); int max_peerlist_size = state->max_peerlist_size; if (max_peerlist_size && int(m_peers.size()) >= max_peerlist_size) { if (p->source == peer_info::resume_data) return false; erase_peers(state); if (int(m_peers.size()) >= max_peerlist_size) return false; // since some peers were removed, we need to // update the iterator to make it valid again #if TORRENT_USE_I2P if (p->is_i2p_addr) { iter = std::lower_bound( m_peers.begin(), m_peers.end() , p->dest(), peer_address_compare()); } else #endif iter = std::lower_bound( m_peers.begin(), m_peers.end() , p->address(), peer_address_compare()); } iter = m_peers.insert(iter, p); if (m_round_robin >= iter - m_peers.begin()) ++m_round_robin; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) if (flags & flag_encryption) p->pe_support = true; #endif if (flags & flag_seed) { p->seed = true; TORRENT_ASSERT(m_num_seeds < int(m_peers.size())); ++m_num_seeds; } if (flags & flag_utp) p->supports_utp = true; if (flags & flag_holepunch) p->supports_holepunch = true; if (is_connect_candidate(*p)) update_connect_candidates(1); return true; } void peer_list::update_peer(torrent_peer* p, int src, int flags , tcp::endpoint const& remote, char const* /* destination*/) { TORRENT_ASSERT(is_single_thread()); bool was_conn_cand = is_connect_candidate(*p); TORRENT_ASSERT(p->in_use); p->connectable = true; TORRENT_ASSERT(p->address() == remote.address()); p->port = remote.port(); p->source |= src; // if this peer has failed before, decrease the // counter to allow it another try, since somebody // else is appearantly able to connect to it // only trust this if it comes from the tracker if (p->failcount > 0 && src == peer_info::tracker) --p->failcount; // if we're connected to this peer // we already know if it's a seed or not // so we don't have to trust this source if ((flags & flag_seed) && !p->connection) { if (!p->seed) { TORRENT_ASSERT(m_num_seeds < int(m_peers.size())); ++m_num_seeds; } p->seed = true; } if (flags & flag_utp) p->supports_utp = true; if (flags & flag_holepunch) p->supports_holepunch = true; if (was_conn_cand != is_connect_candidate(*p)) { update_connect_candidates(was_conn_cand ? -1 : 1); } } void peer_list::update_connect_candidates(int delta) { TORRENT_ASSERT(is_single_thread()); if (delta == 0) return; m_num_connect_candidates += delta; if (delta < 0) { TORRENT_ASSERT(m_num_connect_candidates >= 0); if (m_num_connect_candidates < 0) m_num_connect_candidates = 0; } } #if TORRENT_USE_I2P torrent_peer* peer_list::add_i2p_peer(char const* destination, int src, char flags, torrent_state* state) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; bool found = false; iterator iter = std::lower_bound( m_peers.begin(), m_peers.end() , destination, peer_address_compare() ); if (iter != m_peers.end() && strcmp((*iter)->dest(), destination) == 0) found = true; torrent_peer* p = 0; if (!found) { // we don't have any info about this peer. // add a new entry p = m_peer_allocator.allocate_peer_entry(torrent_peer_allocator_interface::i2p_peer_type); if (p == NULL) return NULL; new (p) i2p_peer(destination, true, src); #if TORRENT_USE_ASSERTS p->in_use = true; #endif if (!insert_peer(p, iter, flags, state)) { #if TORRENT_USE_ASSERTS p->in_use = false; #endif m_peer_allocator.free_peer_entry(p); return 0; } } else { p = *iter; update_peer(p, src, flags, tcp::endpoint(), destination); } return p; } #endif // TORRENT_USE_I2P // if this returns non-NULL, the torrent need to post status update torrent_peer* peer_list::add_peer(tcp::endpoint const& remote, int src, char flags , torrent_state* state) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; // just ignore the obviously invalid entries if (remote.address() == address() || remote.port() == 0) return 0; #if TORRENT_USE_IPV6 // don't allow link-local IPv6 addresses since they // can't be used like normal addresses, they require an interface // and will just cause connect() to fail with EINVAL if (remote.address().is_v6() && remote.address().to_v6().is_link_local()) return 0; #endif iterator iter; torrent_peer* p = 0; bool found = false; if (state->allow_multiple_connections_per_ip) { std::pair range = find_peers(remote.address()); iter = std::find_if(range.first, range.second, match_peer_endpoint(remote)); if (iter != range.second) found = true; } else { iter = std::lower_bound( m_peers.begin(), m_peers.end() , remote.address(), peer_address_compare() ); if (iter != m_peers.end() && (*iter)->address() == remote.address()) found = true; } if (!found) { // we don't have any info about this peer. // add a new entry #if TORRENT_USE_IPV6 bool is_v6 = remote.address().is_v6(); #else bool is_v6 = false; #endif p = m_peer_allocator.allocate_peer_entry( is_v6 ? torrent_peer_allocator_interface::ipv6_peer_type : torrent_peer_allocator_interface::ipv4_peer_type); if (p == NULL) return NULL; #if TORRENT_USE_IPV6 if (is_v6) new (p) ipv6_peer(remote, true, src); else #endif new (p) ipv4_peer(remote, true, src); #if TORRENT_USE_ASSERTS p->in_use = true; #endif if (!insert_peer(p, iter, flags, state)) { #if TORRENT_USE_ASSERTS p->in_use = false; #endif // TODO: 3 this is not exception safe! m_peer_allocator.free_peer_entry(p); return 0; } state->first_time_seen = true; } else { p = *iter; TORRENT_ASSERT(p->in_use); update_peer(p, src, flags, remote, 0); state->first_time_seen = false; } return p; } torrent_peer* peer_list::connect_one_peer(int session_time, torrent_state* state) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (m_finished != state->is_finished) recalculate_connect_candidates(state); // clear out any peers from the cache that no longer // are connection candidates for (std::vector::iterator i = m_candidate_cache.begin(); i != m_candidate_cache.end();) { if (!is_connect_candidate(**i)) i = m_candidate_cache.erase(i); else ++i; } if (m_candidate_cache.empty()) { find_connect_candidates(m_candidate_cache, session_time, state); if (m_candidate_cache.empty()) return NULL; } torrent_peer* p = m_candidate_cache.front(); m_candidate_cache.erase(m_candidate_cache.begin()); TORRENT_ASSERT(p->in_use); TORRENT_ASSERT(!p->banned); TORRENT_ASSERT(!p->connection); TORRENT_ASSERT(p->connectable); // this should hold because find_connect_candidates should have done this TORRENT_ASSERT(m_finished == state->is_finished); TORRENT_ASSERT(is_connect_candidate(*p)); return p; } // this is called whenever a peer connection is closed void peer_list::connection_closed(const peer_connection_interface& c , int session_time, torrent_state* state) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; torrent_peer* p = c.peer_info_struct(); // if we couldn't find the connection in our list, just ignore it. if (p == 0) return; TORRENT_ASSERT(p->in_use); #ifndef TORRENT_DISABLE_INVARIANT_CHECKS // web seeds are special, they're not connected via the peer list // so they're not kept in m_peers TORRENT_ASSERT(p->web_seed || std::find_if( m_peers.begin() , m_peers.end() , match_peer_connection(c)) != m_peers.end()); #endif TORRENT_ASSERT(p->connection == &c); TORRENT_ASSERT(!is_connect_candidate(*p)); p->connection = 0; p->optimistically_unchoked = false; // if fast reconnect is true, we won't // update the timestamp, and it will remain // the time when we initiated the connection. if (!c.fast_reconnect()) p->last_connected = session_time; if (c.failed()) { // failcount is a 5 bit value if (p->failcount < 31) ++p->failcount; } if (is_connect_candidate(*p)) update_connect_candidates(1); // if we're already a seed, it's not as important // to keep all the possibly stale peers // if we're not a seed, but we have too many peers // start weeding the ones we only know from resume // data first // at this point it may be tempting to erase peers // from the peer list, but keep in mind that we might // have gotten to this point through new_connection, just // disconnecting an old peer, relying on this torrent_peer // to still exist when we get back there, to assign the new // peer connection pointer to it. The peer list must // be left intact. // if we allow multiple connections per IP, and this peer // was incoming and it never advertised its listen // port, we don't really know which peer it was. In order // to avoid adding one entry for every single connection // the peer makes to us, don't save this entry if (state->allow_multiple_connections_per_ip && !p->connectable && p != m_locked_peer) { erase_peer(p, state); } } void peer_list::recalculate_connect_candidates(torrent_state* state) { TORRENT_ASSERT(is_single_thread()); m_num_connect_candidates = 0; m_finished = state->is_finished; m_max_failcount = state->max_failcount; for (const_iterator i = m_peers.begin(); i != m_peers.end(); ++i) { m_num_connect_candidates += is_connect_candidate(**i); } #if TORRENT_USE_INVARIANT_CHECKS // the invariant is not likely to be upheld at the entry of this function // but it is likely to have been restored by the end of it check_invariant(); #endif } #if TORRENT_USE_ASSERTS bool peer_list::has_connection(const peer_connection_interface* c) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(c); iterator iter = std::lower_bound( m_peers.begin(), m_peers.end() , c->remote().address(), peer_address_compare()); if (iter != m_peers.end() && (*iter)->address() == c->remote().address()) return true; return std::find_if( m_peers.begin() , m_peers.end() , match_peer_connection_or_endpoint(*c)) != m_peers.end(); } #endif #if TORRENT_USE_INVARIANT_CHECKS void peer_list::check_invariant() const { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_num_connect_candidates >= 0); TORRENT_ASSERT(m_num_connect_candidates <= int(m_peers.size())); #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS int total_connections = 0; int nonempty_connections = 0; int connect_candidates = 0; const_iterator prev = m_peers.end(); for (const_iterator i = m_peers.begin(); i != m_peers.end(); ++i) { if (prev != m_peers.end()) ++prev; if (i == m_peers.begin() + 1) prev = m_peers.begin(); if (prev != m_peers.end()) { TORRENT_ASSERT(!((*i)->address() < (*prev)->address())); } torrent_peer const& p = **i; TORRENT_ASSERT(p.in_use); if (is_connect_candidate(p)) ++connect_candidates; ++total_connections; if (!p.connection) { continue; } if (p.optimistically_unchoked) { TORRENT_ASSERT(p.connection); TORRENT_ASSERT(!p.connection->is_choked()); } TORRENT_ASSERT(p.connection->peer_info_struct() == 0 || p.connection->peer_info_struct() == &p); ++nonempty_connections; } TORRENT_ASSERT(m_num_connect_candidates == connect_candidates); #endif // TORRENT_EXPENSIVE_INVARIANT_CHECKS } #endif // TORRENT_DEBUG // this returns true if lhs is a better erase candidate than rhs bool peer_list::compare_peer_erase(torrent_peer const& lhs, torrent_peer const& rhs) const { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(lhs.connection == 0); TORRENT_ASSERT(rhs.connection == 0); // primarily, prefer getting rid of peers we've already tried and failed if (lhs.failcount != rhs.failcount) return lhs.failcount > rhs.failcount; bool lhs_resume_data_source = lhs.source == peer_info::resume_data; bool rhs_resume_data_source = rhs.source == peer_info::resume_data; // prefer to drop peers whose only source is resume data if (lhs_resume_data_source != rhs_resume_data_source) return lhs_resume_data_source > rhs_resume_data_source; if (lhs.connectable != rhs.connectable) return lhs.connectable < rhs.connectable; return lhs.trust_points < rhs.trust_points; } // this returns true if lhs is a better connect candidate than rhs bool peer_list::compare_peer(torrent_peer const* lhs, torrent_peer const* rhs , external_ip const& external, int external_port) const { TORRENT_ASSERT(is_single_thread()); // prefer peers with lower failcount if (lhs->failcount != rhs->failcount) return lhs->failcount < rhs->failcount; // Local peers should always be tried first bool lhs_local = is_local(lhs->address()); bool rhs_local = is_local(rhs->address()); if (lhs_local != rhs_local) return lhs_local > rhs_local; if (lhs->last_connected != rhs->last_connected) return lhs->last_connected < rhs->last_connected; int lhs_rank = source_rank(lhs->source); int rhs_rank = source_rank(rhs->source); if (lhs_rank != rhs_rank) return lhs_rank > rhs_rank; boost::uint32_t lhs_peer_rank = lhs->rank(external, external_port); boost::uint32_t rhs_peer_rank = rhs->rank(external, external_port); if (lhs_peer_rank > rhs_peer_rank) return true; return false; } } libtorrent-rasterbar-1.1.13/src/performance_counters.cpp000066400000000000000000000123551351156116000234670ustar00rootroot00000000000000/* Copyright (c) 2013-2018, 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/performance_counters.hpp" #include "libtorrent/assert.hpp" #include // for memset #ifdef TORRENT_USE_VALGRIND #include #endif namespace libtorrent { counters::counters() { #if BOOST_ATOMIC_LLONG_LOCK_FREE == 2 for (int i = 0; i < sizeof(m_stats_counter) / sizeof(m_stats_counter[0]); ++i) m_stats_counter[i].store(0, boost::memory_order_relaxed); #else memset(m_stats_counter, 0, sizeof(m_stats_counter)); #endif } counters::counters(counters const& c) { #if BOOST_ATOMIC_LLONG_LOCK_FREE == 2 for (int i = 0; i < sizeof(m_stats_counter) / sizeof(m_stats_counter[0]); ++i) m_stats_counter[i].store( c.m_stats_counter[i].load(boost::memory_order_relaxed) , boost::memory_order_relaxed); #else mutex::scoped_lock l(c.m_mutex); memcpy(m_stats_counter, c.m_stats_counter, sizeof(m_stats_counter)); #endif } counters& counters::operator=(counters const& c) { #if BOOST_ATOMIC_LLONG_LOCK_FREE == 2 for (int i = 0; i < sizeof(m_stats_counter) / sizeof(m_stats_counter[0]); ++i) m_stats_counter[i].store( c.m_stats_counter[i].load(boost::memory_order_relaxed) , boost::memory_order_relaxed); #else mutex::scoped_lock l(m_mutex); mutex::scoped_lock l2(c.m_mutex); memcpy(m_stats_counter, c.m_stats_counter, sizeof(m_stats_counter)); #endif return *this; } boost::int64_t counters::operator[](int i) const { TORRENT_ASSERT(i >= 0); TORRENT_ASSERT(i < num_counters); #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_VALUE_IS_DEFINED(m_stats_counter[i]); #endif #if BOOST_ATOMIC_LLONG_LOCK_FREE == 2 return m_stats_counter[i].load(boost::memory_order_relaxed); #else mutex::scoped_lock l(m_mutex); return m_stats_counter[i]; #endif } // the argument specifies which counter to // increment or decrement boost::int64_t counters::inc_stats_counter(int c, boost::int64_t value) { // if c >= num_stats_counters, it means it's not // a monotonically increasing counter, but a gauge // and it's allowed to be decremented TORRENT_ASSERT(value >= 0 || c >= num_stats_counters); TORRENT_ASSERT(c >= 0); TORRENT_ASSERT(c < num_counters); #if BOOST_ATOMIC_LLONG_LOCK_FREE == 2 boost::int64_t pv = m_stats_counter[c].fetch_add(value, boost::memory_order_relaxed); TORRENT_ASSERT(pv + value >= 0); return pv + value; #else mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(m_stats_counter[c] + value >= 0); return m_stats_counter[c] += value; #endif } // ratio is a vaue between 0 and 100 representing the percentage the value // is blended in at. void counters::blend_stats_counter(int c, boost::int64_t value, int ratio) { TORRENT_ASSERT(c >= num_stats_counters); TORRENT_ASSERT(c < num_counters); TORRENT_ASSERT(ratio >= 0); TORRENT_ASSERT(ratio <= 100); #if BOOST_ATOMIC_LLONG_LOCK_FREE == 2 boost::int64_t current = m_stats_counter[c].load(boost::memory_order_relaxed); boost::int64_t new_value = (current * (100-ratio) + value * ratio) / 100; while (!m_stats_counter[c].compare_exchange_weak(current, new_value , boost::memory_order_relaxed)) { new_value = (current * (100-ratio) + value * ratio) / 100; } #else mutex::scoped_lock l(m_mutex); boost::int64_t current = m_stats_counter[c]; m_stats_counter[c] = (current * (100-ratio) + value * ratio) / 100; #endif } void counters::set_value(int c, boost::int64_t value) { TORRENT_ASSERT(c >= 0); TORRENT_ASSERT(c < num_counters); #if BOOST_ATOMIC_LLONG_LOCK_FREE == 2 m_stats_counter[c].store(value); #else mutex::scoped_lock l(m_mutex); // if this assert fires, someone is trying to decrement a counter // which is not allowed. Counters are monotonically increasing TORRENT_ASSERT(value >= m_stats_counter[c] || c >= num_stats_counters); m_stats_counter[c] = value; #endif } } libtorrent-rasterbar-1.1.13/src/piece_picker.cpp000066400000000000000000003445451351156116000216770ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/piece_picker.hpp" #include "libtorrent/bitfield.hpp" #include "libtorrent/random.hpp" #include "libtorrent/alloca.hpp" #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/alert_types.hpp" // for picker_log_alert #if TORRENT_USE_ASSERTS #include "libtorrent/peer_connection.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/torrent_peer.hpp" #endif #ifdef TORRENT_USE_VALGRIND #include #endif #include "libtorrent/invariant_check.hpp" #define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK //#define TORRENT_NO_EXPENSIVE_INVARIANT_CHECK //#define TORRENT_PIECE_PICKER_INVARIANT_CHECK // this is really only useful for debugging unit tests //#define TORRENT_PICKER_LOG namespace libtorrent { const piece_block piece_block::invalid((std::numeric_limits::max)(), (std::numeric_limits::max)()); piece_picker::piece_picker() : m_seeds(0) , m_num_passed(0) , m_priority_boundries(1, int(m_pieces.size())) , m_blocks_per_piece(0) , m_blocks_in_last_piece(0) , m_num_filtered(0) , m_num_have_filtered(0) , m_cursor(0) , m_reverse_cursor(0) , m_num_have(0) , m_num_pad_files(0) , m_dirty(false) { #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "new piece_picker" << std::endl; #endif #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif } void piece_picker::init(int blocks_per_piece, int blocks_in_last_piece, int total_num_pieces) { TORRENT_ASSERT(blocks_per_piece > 0); TORRENT_ASSERT(total_num_pieces > 0); #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "piece_picker::init()" << std::endl; #endif // allocate the piece_map to cover all pieces // and make them invalid (as if we don't have a single piece) m_piece_map.resize(total_num_pieces, piece_pos(0, 0)); m_reverse_cursor = int(m_piece_map.size()); m_cursor = 0; for (int i = 0; i < piece_pos::num_download_categories; ++i) m_downloads[i].clear(); m_block_info.clear(); m_free_block_infos.clear(); m_num_filtered += m_num_have_filtered; m_num_have_filtered = 0; m_num_have = 0; m_num_passed = 0; m_dirty = true; for (std::vector::iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end; ++i) { i->peer_count = 0; i->download_state = piece_pos::piece_open; i->index = 0; #ifdef TORRENT_DEBUG_REFCOUNTS i->have_peers.clear(); #endif } for (std::vector::iterator i = m_piece_map.begin() + m_cursor , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); ++i, ++m_cursor); for (std::vector::reverse_iterator i = m_piece_map.rend() - m_reverse_cursor; m_reverse_cursor > 0 && (i->have() || i->filtered()); ++i, --m_reverse_cursor); // the piece index is stored in 20 bits, which limits the allowed // number of pieces somewhat TORRENT_ASSERT(m_piece_map.size() < piece_pos::we_have_index); m_blocks_per_piece = blocks_per_piece; m_blocks_in_last_piece = blocks_in_last_piece; if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = blocks_per_piece; TORRENT_ASSERT(m_blocks_in_last_piece <= m_blocks_per_piece); } void piece_picker::piece_info(int index, piece_picker::downloading_piece& st) const { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); int state = m_piece_map[index].download_queue(); if (state != piece_pos::piece_open) { std::vector::const_iterator piece = find_dl_piece(state, index); TORRENT_ASSERT(piece != m_downloads[state].end()); st = *piece; return; } st.info_idx = 0; st.index = index; st.writing = 0; st.requested = 0; if (m_piece_map[index].have()) { st.finished = blocks_in_piece(index); return; } st.finished = 0; } piece_picker::piece_stats_t piece_picker::piece_stats(int index) const { TORRENT_ASSERT(index >= 0 && index < int(m_piece_map.size())); piece_pos const& pp = m_piece_map[index]; piece_stats_t ret = { pp.peer_count + m_seeds, pp.priority(this), pp.have(), pp.downloading() }; return ret; } piece_picker::dlpiece_iter piece_picker::add_download_piece(int piece) { TORRENT_ASSERT(piece >= 0); TORRENT_ASSERT(piece < int(m_piece_map.size())); #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif int block_index; if (m_free_block_infos.empty()) { // we need to allocate more space in m_block_info block_index = m_block_info.size() / m_blocks_per_piece; TORRENT_ASSERT((m_block_info.size() % m_blocks_per_piece) == 0); m_block_info.resize(m_block_info.size() + m_blocks_per_piece); } else { // there is already free space in m_block_info, grab one range block_index = m_free_block_infos.back(); m_free_block_infos.pop_back(); } // always insert into bucket 0 (piece_downloading) downloading_piece ret; ret.index = piece; int download_state = piece_pos::piece_downloading; std::vector::iterator downloading_iter = std::lower_bound(m_downloads[download_state].begin() , m_downloads[download_state].end(), ret); TORRENT_ASSERT(downloading_iter == m_downloads[download_state].end() || downloading_iter->index != piece); TORRENT_ASSERT(block_index >= 0); TORRENT_ASSERT(block_index < (std::numeric_limits::max)()); ret.info_idx = block_index; TORRENT_ASSERT(int(ret.info_idx) * m_blocks_per_piece + m_blocks_per_piece <= int(m_block_info.size())); #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_VALUE_IS_DEFINED(piece); VALGRIND_CHECK_VALUE_IS_DEFINED(block_index); #endif block_info* info = blocks_for_piece(ret); for (int i = 0; i < m_blocks_per_piece; ++i) { info[i].num_peers = 0; if (m_pad_blocks.count(piece_block(piece, i))) { info[i].state = block_info::state_finished; ++ret.finished; } else { info[i].state = block_info::state_none; } info[i].peer = 0; #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_VALUE_IS_DEFINED(info[i].peer); #endif #if TORRENT_USE_ASSERTS info[i].piece_index = piece; info[i].peers.clear(); #endif } #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_VALUE_IS_DEFINED(ret.info_idx); VALGRIND_CHECK_VALUE_IS_DEFINED(ret.index); #endif downloading_iter = m_downloads[download_state].insert(downloading_iter, ret); // in case every block was a pad block, we need to make sure the piece // structure is correctly categorised downloading_iter = update_piece_state(downloading_iter); #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif return downloading_iter; } void piece_picker::erase_download_piece(std::vector::iterator i) { #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif int download_state = m_piece_map[i->index].download_queue(); TORRENT_ASSERT(download_state != piece_pos::piece_open); TORRENT_ASSERT(find_dl_piece(download_state, i->index) == i); #if TORRENT_USE_ASSERTS int prev_size = m_downloads[download_state].size(); #endif // since we're removing a downloading_piece, we also need to free its // blocks that are allocated from the m_block_info array. m_free_block_infos.push_back(i->info_idx); TORRENT_ASSERT(find_dl_piece(download_state, i->index) == i); m_piece_map[i->index].download_state = piece_pos::piece_open; m_downloads[download_state].erase(i); TORRENT_ASSERT(prev_size == m_downloads[download_state].size() + 1); #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif } std::vector piece_picker::get_download_queue() const { #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif std::vector ret; for (int k = 0; k < piece_pos::num_download_categories; ++k) ret.insert(ret.end(), m_downloads[k].begin(), m_downloads[k].end()); return ret; } int piece_picker::get_download_queue_size() const { int ret = 0; for (int k = 0; k < piece_pos::num_download_categories; ++k) ret += m_downloads[k].size(); return ret; } void piece_picker::get_download_queue_sizes(int* partial , int* full, int* finished, int* zero_prio) const { *partial = m_downloads[piece_pos::piece_downloading].size(); *full = m_downloads[piece_pos::piece_full].size(); *finished = m_downloads[piece_pos::piece_finished].size(); *zero_prio = m_downloads[piece_pos::piece_zero_prio].size(); } piece_picker::block_info* piece_picker::blocks_for_piece( downloading_piece const& dp) { int idx = int(dp.info_idx) * m_blocks_per_piece; TORRENT_ASSERT(idx + m_blocks_per_piece <= m_block_info.size()); return &m_block_info[idx]; } piece_picker::block_info const* piece_picker::blocks_for_piece( downloading_piece const& dp) const { return const_cast(this)->blocks_for_piece(dp); } #if TORRENT_USE_INVARIANT_CHECKS void piece_picker::check_piece_state() const { #ifndef TORRENT_DISABLE_INVARIANT_CHECKS for (int k = 0; k < piece_pos::num_download_categories; ++k) { if (!m_downloads[k].empty()) { for (std::vector::const_iterator i = m_downloads[k].begin(); i != m_downloads[k].end() - 1; ++i) { downloading_piece const& dp = *i; downloading_piece const& next = *(i + 1); // TORRENT_ASSERT(dp.finished + dp.writing >= next.finished + next.writing); TORRENT_ASSERT(dp.index < next.index); TORRENT_ASSERT(int(dp.info_idx) * m_blocks_per_piece + m_blocks_per_piece <= int(m_block_info.size())); block_info const* info = blocks_for_piece(dp); for (int j = 0; j < m_blocks_per_piece; ++j) { if (info[j].peer) { torrent_peer* p = info[j].peer; TORRENT_ASSERT(p->in_use); TORRENT_ASSERT(p->connection == NULL || static_cast(p->connection)->m_in_use); } } } } } #endif } void piece_picker::verify_pick(std::vector const& picked , bitfield const& bits) const { TORRENT_ASSERT(bits.size() == m_piece_map.size()); for (std::vector::const_iterator i = picked.begin() , end(picked.end()); i != end; ++i) { TORRENT_ASSERT(i->piece_index >= 0); TORRENT_ASSERT(i->piece_index < bits.size()); TORRENT_ASSERT(bits[i->piece_index]); TORRENT_ASSERT(!m_piece_map[i->piece_index].have()); TORRENT_ASSERT(!m_piece_map[i->piece_index].filtered()); } } void piece_picker::verify_priority(int range_start, int range_end, int prio) const { TORRENT_ASSERT(range_start <= range_end); TORRENT_ASSERT(range_end <= int(m_pieces.size())); for (std::vector::const_iterator i = m_pieces.begin() + range_start , end(m_pieces.begin() + range_end); i != end; ++i) { int index = *i; TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); int p = m_piece_map[index].priority(this); TORRENT_ASSERT(p == prio); } } #if defined TORRENT_PICKER_LOG void piece_picker::print_pieces() const { int limit = 20; std::cerr << "[" << this << "] "; if (m_dirty) { std::cerr << " === dirty ===" << std::endl; return; } for (std::vector::const_iterator i = m_priority_boundries.begin() , end(m_priority_boundries.end()); i != end; ++i) { std::cerr << *i << " "; } std::cerr << std::endl; int index = 0; std::cerr << "[" << this << "] "; std::vector::const_iterator j = m_priority_boundries.begin(); for (std::vector::const_iterator i = m_pieces.begin() , end(m_pieces.end()); i != end; ++i, ++index) { if (limit == 0) { std::cerr << " ..."; break; } if (*i == -1) break; while (j != m_priority_boundries.end() && *j <= index) { std::cerr << "| "; ++j; } std::cerr << *i << "(" << m_piece_map[*i].index << ") "; --limit; } std::cerr << std::endl; } #endif // TORRENT_PICKER_LOG #endif // TORRENT_USE_INVARIANT_CHECKS #if TORRENT_USE_INVARIANT_CHECKS void piece_picker::check_peer_invariant(bitfield const& have , torrent_peer const* p) const { #ifdef TORRENT_DEBUG_REFCOUNTS int num_pieces = have.size(); for (int i = 0; i < num_pieces; ++i) { int h = have[i]; TORRENT_ASSERT(m_piece_map[i].have_peers.count(p) == h); } #else TORRENT_UNUSED(have); TORRENT_UNUSED(p); #endif } void piece_picker::check_invariant(torrent const* t) const { #ifndef TORRENT_DEBUG_REFCOUNTS #ifdef TORRENT_OPTIMIZE_MEMORY_USAGE TORRENT_ASSERT(sizeof(piece_pos) == 4); #else TORRENT_ASSERT(sizeof(piece_pos) == 8); #endif #endif TORRENT_ASSERT(m_num_have >= 0); TORRENT_ASSERT(m_num_have_filtered >= 0); TORRENT_ASSERT(m_num_filtered >= 0); TORRENT_ASSERT(m_seeds >= 0); for (int k = 0; k < piece_pos::num_download_categories; ++k) { if (!m_downloads[k].empty()) { for (std::vector::const_iterator i = m_downloads[k].begin(); i != m_downloads[k].end() - 1; ++i) { downloading_piece const& dp = *i; downloading_piece const& next = *(i + 1); // TORRENT_ASSERT(dp.finished + dp.writing >= next.finished + next.writing); TORRENT_ASSERT(dp.index < next.index); TORRENT_ASSERT(int(dp.info_idx) * m_blocks_per_piece + m_blocks_per_piece <= int(m_block_info.size())); #if TORRENT_USE_ASSERTS block_info const* info = blocks_for_piece(dp); for (int j = 0; j < m_blocks_per_piece; ++j) { if (!info[j].peer) continue; torrent_peer* p = info[j].peer; TORRENT_ASSERT(p->in_use); TORRENT_ASSERT(p->connection == NULL || static_cast(p->connection)->m_in_use); } #endif } } } if (t != 0) TORRENT_ASSERT(int(m_piece_map.size()) == t->torrent_file().num_pieces()); for (int j = 0; j < piece_pos::num_download_categories; ++j) { for (std::vector::const_iterator i = m_downloads[j].begin() , end(m_downloads[j].end()); i != end; ++i) { TORRENT_ASSERT(m_piece_map[i->index].download_queue() == j); const int num_blocks = blocks_in_piece(i->index); int num_requested = 0; int num_finished = 0; int num_writing = 0; int num_open = 0; block_info const* info = blocks_for_piece(*i); for (int k = 0; k < num_blocks; ++k) { TORRENT_ASSERT(info[k].piece_index == i->index); TORRENT_ASSERT(info[k].peer == 0 || info[k].peer->in_use); if (info[k].state == block_info::state_finished) { ++num_finished; TORRENT_ASSERT(info[k].num_peers == 0); } else if (info[k].state == block_info::state_requested) { ++num_requested; TORRENT_ASSERT(info[k].num_peers > 0); } else if (info[k].state == block_info::state_writing) { ++num_writing; TORRENT_ASSERT(info[k].num_peers == 0); } else if (info[k].state == block_info::state_none) { ++num_open; TORRENT_ASSERT(info[k].num_peers == 0); } } switch(j) { case piece_pos::piece_downloading: TORRENT_ASSERT(!m_piece_map[i->index].filtered()); TORRENT_ASSERT(num_open > 0); break; case piece_pos::piece_full: TORRENT_ASSERT(!m_piece_map[i->index].filtered()); TORRENT_ASSERT(num_open == 0); // if requested == 0, the piece should be in the finished state TORRENT_ASSERT(num_requested > 0); break; case piece_pos::piece_finished: TORRENT_ASSERT(!m_piece_map[i->index].filtered()); TORRENT_ASSERT(num_open == 0); TORRENT_ASSERT(num_requested == 0); TORRENT_ASSERT(num_finished + num_writing == num_blocks); break; case piece_pos::piece_zero_prio: TORRENT_ASSERT(m_piece_map[i->index].filtered()); break; } TORRENT_ASSERT(num_requested == i->requested); TORRENT_ASSERT(num_writing == i->writing); TORRENT_ASSERT(num_finished == i->finished); if (m_piece_map[i->index].download_queue() == piece_pos::piece_full || m_piece_map[i->index].download_queue() == piece_pos::piece_finished) TORRENT_ASSERT(num_finished + num_writing + num_requested == num_blocks); } } int num_pieces = int(m_piece_map.size()); TORRENT_ASSERT(m_cursor >= 0); TORRENT_ASSERT(m_cursor <= num_pieces); TORRENT_ASSERT(m_reverse_cursor <= num_pieces); TORRENT_ASSERT(m_reverse_cursor >= 0); TORRENT_ASSERT(m_reverse_cursor > m_cursor || (m_cursor == num_pieces && m_reverse_cursor == 0)); if (!m_dirty) { TORRENT_ASSERT(!m_priority_boundries.empty()); int prio = 0; int start = 0; for (std::vector::const_iterator i = m_priority_boundries.begin() , end(m_priority_boundries.end()); i != end; ++i) { verify_priority(start, *i, prio); ++prio; start = *i; } TORRENT_ASSERT(m_priority_boundries.back() == int(m_pieces.size())); } #ifdef TORRENT_NO_EXPENSIVE_INVARIANT_CHECK return; #endif { int index = 0; for (std::vector::const_iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); ++i, ++index); TORRENT_ASSERT(m_cursor == index); index = num_pieces; if (num_pieces > 0) { for (std::vector::reverse_iterator i = m_piece_map.rend() - index; index > 0 && (i->have() || i->filtered()); ++i, --index); TORRENT_ASSERT(index == num_pieces || m_piece_map[index].have() || m_piece_map[index].filtered()); TORRENT_ASSERT(m_reverse_cursor == index); } else { TORRENT_ASSERT(m_reverse_cursor == 0); } } int num_filtered = 0; int num_have_filtered = 0; int num_have = 0; for (std::vector::const_iterator i = m_piece_map.begin(); i != m_piece_map.end(); ++i) { int index = static_cast(i - m_piece_map.begin()); piece_pos const& p = *i; if (p.filtered()) { if (p.index != piece_pos::we_have_index) ++num_filtered; else ++num_have_filtered; } #ifdef TORRENT_DEBUG_REFCOUNTS TORRENT_ASSERT(p.have_peers.size() == p.peer_count + m_seeds); #endif if (p.index == piece_pos::we_have_index) ++num_have; #if 0 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++; } TORRENT_ASSERT((int)i->peer_count == actual_peer_count); /* int num_downloaders = 0; for (std::vector::const_iterator peer = t->begin(); peer != t->end(); ++peer) { const std::vector& queue = (*peer)->download_queue(); if (std::find_if(queue.begin(), queue.end(), has_index(index)) == queue.end()) continue; ++num_downloaders; } if (i->downloading()) { TORRENT_ASSERT(num_downloaders == 1); } else { TORRENT_ASSERT(num_downloaders == 0); } */ } #endif if (p.index == piece_pos::we_have_index) { TORRENT_ASSERT(t == 0 || t->have_piece(index)); TORRENT_ASSERT(p.downloading() == false); } if (t != 0) TORRENT_ASSERT(!t->have_piece(index)); int prio = p.priority(this); #if TORRENT_USE_ASSERTS if (p.downloading()) { if (p.reverse()) TORRENT_ASSERT(prio == -1 || (prio % piece_picker::prio_factor == 2)); else TORRENT_ASSERT(prio == -1 || (prio % piece_picker::prio_factor == 0)); } else { TORRENT_ASSERT(prio == -1 || (prio % piece_picker::prio_factor == 1)); } #endif if (!m_dirty) { TORRENT_ASSERT(prio < int(m_priority_boundries.size()) || m_dirty); if (prio >= 0) { TORRENT_ASSERT(p.index < m_pieces.size()); TORRENT_ASSERT(m_pieces[p.index] == index); } else { TORRENT_ASSERT(prio == -1); // make sure there's no entry // with this index. (there shouldn't // be since the priority is -1) TORRENT_ASSERT(std::find(m_pieces.begin(), m_pieces.end(), index) == m_pieces.end()); } } int count_downloading = std::count_if( m_downloads[piece_pos::piece_downloading].begin() , m_downloads[piece_pos::piece_downloading].end() , has_index(index)); int count_full = std::count_if( m_downloads[piece_pos::piece_full].begin() , m_downloads[piece_pos::piece_full].end() , has_index(index)); int count_finished = std::count_if( m_downloads[piece_pos::piece_finished].begin() , m_downloads[piece_pos::piece_finished].end() , has_index(index)); int count_zero = std::count_if( m_downloads[piece_pos::piece_zero_prio].begin() , m_downloads[piece_pos::piece_zero_prio].end() , has_index(index)); TORRENT_ASSERT(i->download_queue() == piece_pos::piece_open || count_zero + count_downloading + count_full + count_finished == 1); switch(i->download_queue()) { case piece_pos::piece_open: TORRENT_ASSERT(count_downloading + count_full + count_finished + count_zero == 0); break; case piece_pos::piece_downloading: TORRENT_ASSERT(count_downloading == 1); break; case piece_pos::piece_full: TORRENT_ASSERT(count_full == 1); break; case piece_pos::piece_finished: TORRENT_ASSERT(count_finished == 1); break; case piece_pos::piece_zero_prio: TORRENT_ASSERT(count_zero == 1); break; }; } TORRENT_ASSERT(num_have == m_num_have); TORRENT_ASSERT(num_filtered == m_num_filtered); TORRENT_ASSERT(num_have_filtered == m_num_have_filtered); if (!m_dirty) { for (std::vector::const_iterator i = m_pieces.begin() , end(m_pieces.end()); i != end; ++i) { TORRENT_ASSERT(m_piece_map[*i].priority(this) >= 0); } } } #endif std::pair piece_picker::distributed_copies() const { TORRENT_ASSERT(m_seeds >= 0); const int num_pieces = m_piece_map.size(); if (num_pieces == 0) return std::make_pair(1, 0); int min_availability = piece_pos::max_peer_count; // find the lowest availability count // count the number of pieces that have that availability // and also the number of pieces that have more than that. int integer_part = 0; int fraction_part = 0; for (std::vector::const_iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end; ++i) { int peer_count = int(i->peer_count); // take ourself into account if (i->have()) ++peer_count; if (min_availability > peer_count) { min_availability = peer_count; fraction_part += integer_part; integer_part = 1; } else if (peer_count == min_availability) { ++integer_part; } else { TORRENT_ASSERT(peer_count > min_availability); ++fraction_part; } } TORRENT_ASSERT(integer_part + fraction_part == num_pieces); return std::make_pair(min_availability + m_seeds, fraction_part * 1000 / num_pieces); } void piece_picker::priority_range(int prio, int* start, int* end) { TORRENT_ASSERT(prio >= 0); TORRENT_ASSERT(prio < int(m_priority_boundries.size()) || m_dirty); if (prio == 0) *start = 0; else *start = m_priority_boundries[prio - 1]; *end = m_priority_boundries[prio]; TORRENT_ASSERT(*start <= *end); } void piece_picker::add(int index) { TORRENT_ASSERT(!m_dirty); TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); piece_pos& p = m_piece_map[index]; TORRENT_ASSERT(!p.filtered()); TORRENT_ASSERT(!p.have()); int priority = p.priority(this); TORRENT_ASSERT(priority >= 0); if (priority < 0) return; if (int(m_priority_boundries.size()) <= priority) m_priority_boundries.resize(priority + 1, m_pieces.size()); TORRENT_ASSERT(int(m_priority_boundries.size()) >= priority); int range_start, range_end; priority_range(priority, &range_start, &range_end); int new_index; if (range_end == range_start) new_index = range_start; else new_index = random() % (range_end - range_start + 1) + range_start; #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "add " << index << " (" << priority << ")" << std::endl; std::cerr << "[" << this << "] " << " p: state: " << p.download_state << " peer_count: " << p.peer_count << " prio: " << p.piece_priority << " index: " << p.index << std::endl; print_pieces(); #endif m_pieces.push_back(-1); for (;;) { TORRENT_ASSERT(new_index < int(m_pieces.size())); int temp = m_pieces[new_index]; m_pieces[new_index] = index; m_piece_map[index].index = new_index; index = temp; do { temp = m_priority_boundries[priority]++; ++priority; } while (temp == new_index && priority < int(m_priority_boundries.size())); new_index = temp; #ifdef TORRENT_PICKER_LOG print_pieces(); std::cerr << "[" << this << "] " << " index: " << index << " prio: " << priority << " new_index: " << new_index << std::endl; #endif if (priority >= int(m_priority_boundries.size())) break; TORRENT_ASSERT(temp >= 0); } if (index != -1) { TORRENT_ASSERT(new_index == int(m_pieces.size() - 1)); m_pieces[new_index] = index; m_piece_map[index].index = new_index; #ifdef TORRENT_PICKER_LOG print_pieces(); #endif } } void piece_picker::remove(int priority, int elem_index) { TORRENT_ASSERT(!m_dirty); TORRENT_ASSERT(priority >= 0); TORRENT_ASSERT(elem_index < int(m_pieces.size())); TORRENT_ASSERT(elem_index >= 0); #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "remove " << m_pieces[elem_index] << " (" << priority << ")" << std::endl; #endif int next_index = elem_index; TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == -1); for (;;) { #ifdef TORRENT_PICKER_LOG print_pieces(); #endif TORRENT_ASSERT(elem_index < int(m_pieces.size())); int temp; do { temp = --m_priority_boundries[priority]; ++priority; } while (next_index == temp && priority < int(m_priority_boundries.size())); if (next_index == temp) break; next_index = temp; int piece = m_pieces[next_index]; m_pieces[elem_index] = piece; m_piece_map[piece].index = elem_index; TORRENT_ASSERT(m_piece_map[piece].priority(this) == priority - 1); TORRENT_ASSERT(elem_index < int(m_pieces.size() - 1)); elem_index = next_index; if (priority == int(m_priority_boundries.size())) break; } m_pieces.pop_back(); TORRENT_ASSERT(next_index == int(m_pieces.size())); #ifdef TORRENT_PICKER_LOG print_pieces(); #endif } // will update the piece with the given properties (priority, elem_index) // to place it at the correct position void piece_picker::update(int priority, int elem_index) { TORRENT_ASSERT(!m_dirty); TORRENT_ASSERT(elem_index >= 0); TORRENT_ASSERT(elem_index < int(m_piece_map.size())); TORRENT_ASSERT(priority >= 0); TORRENT_ASSERT(int(m_priority_boundries.size()) > priority); // make sure the passed in elem_index actually lives in the specified // priority bucket. If it doesn't, it means this piece changed // state without updating the corresponding entry in the pieces list TORRENT_ASSERT(m_priority_boundries[priority] >= elem_index); TORRENT_ASSERT(priority == 0 || m_priority_boundries[priority-1] <= elem_index); TORRENT_ASSERT(priority + 1 == m_priority_boundries.size() || m_priority_boundries[priority+1] > elem_index); int index = m_pieces[elem_index]; // update the piece_map piece_pos& p = m_piece_map[index]; TORRENT_ASSERT(int(p.index) == elem_index || p.have()); int new_priority = p.priority(this); if (new_priority == priority) return; if (new_priority == -1) { remove(priority, elem_index); return; } if (int(m_priority_boundries.size()) <= new_priority) m_priority_boundries.resize(new_priority + 1, m_pieces.size()); #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "update " << index << " (" << priority << "->" << new_priority << ")" << std::endl; #endif if (priority > new_priority) { int new_index; int temp = index; for (;;) { #ifdef TORRENT_PICKER_LOG print_pieces(); #endif TORRENT_ASSERT(priority > 0); --priority; new_index = m_priority_boundries[priority]++; TORRENT_ASSERT(new_index >= 0); TORRENT_ASSERT(new_index < int(m_pieces.size())); if (temp != m_pieces[new_index]) { temp = m_pieces[new_index]; m_pieces[elem_index] = temp; m_piece_map[temp].index = elem_index; TORRENT_ASSERT(elem_index < int(m_pieces.size())); } elem_index = new_index; if (priority == new_priority) break; } #ifdef TORRENT_PICKER_LOG print_pieces(); #endif m_pieces[elem_index] = index; m_piece_map[index].index = elem_index; TORRENT_ASSERT(elem_index < int(m_pieces.size())); #ifdef TORRENT_PICKER_LOG print_pieces(); #endif shuffle(priority, elem_index); #ifdef TORRENT_PICKER_LOG print_pieces(); #endif TORRENT_ASSERT(m_piece_map[index].priority(this) == priority); } else { int new_index; int temp = index; for (;;) { #ifdef TORRENT_PICKER_LOG print_pieces(); #endif TORRENT_ASSERT(priority >= 0); TORRENT_ASSERT(priority < int(m_priority_boundries.size())); new_index = --m_priority_boundries[priority]; TORRENT_ASSERT(new_index >= 0); TORRENT_ASSERT(new_index < int(m_pieces.size())); if (temp != m_pieces[new_index]) { temp = m_pieces[new_index]; m_pieces[elem_index] = temp; m_piece_map[temp].index = elem_index; TORRENT_ASSERT(elem_index < int(m_pieces.size())); } elem_index = new_index; ++priority; if (priority == new_priority) break; } #ifdef TORRENT_PICKER_LOG print_pieces(); #endif m_pieces[elem_index] = index; m_piece_map[index].index = elem_index; TORRENT_ASSERT(elem_index < int(m_pieces.size())); #ifdef TORRENT_PICKER_LOG print_pieces(); #endif shuffle(priority, elem_index); #ifdef TORRENT_PICKER_LOG print_pieces(); #endif TORRENT_ASSERT(m_piece_map[index].priority(this) == priority); } } void piece_picker::shuffle(int priority, int elem_index) { #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "shuffle()" << std::endl; #endif TORRENT_ASSERT(!m_dirty); TORRENT_ASSERT(priority >= 0); TORRENT_ASSERT(elem_index >= 0); TORRENT_ASSERT(elem_index < int(m_pieces.size())); TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == priority); int range_start, range_end; priority_range(priority, &range_start, &range_end); TORRENT_ASSERT(range_start < range_end); int other_index = random() % (range_end - range_start) + range_start; if (other_index == elem_index) return; // swap other_index with elem_index piece_pos& p1 = m_piece_map[m_pieces[other_index]]; piece_pos& p2 = m_piece_map[m_pieces[elem_index]]; int temp = p1.index; p1.index = p2.index; p2.index = temp; std::swap(m_pieces[other_index], m_pieces[elem_index]); } void piece_picker::restore_piece(int index) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "restore_piece(" << index << ")" << std::endl; #endif TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); int download_state = m_piece_map[index].download_queue(); TORRENT_ASSERT(download_state != piece_pos::piece_open); if (download_state == piece_pos::piece_open) return; std::vector::iterator i = find_dl_piece(download_state, index); TORRENT_ASSERT(i != m_downloads[download_state].end()); TORRENT_ASSERT(int(i->info_idx) * m_blocks_per_piece + m_blocks_per_piece <= int(m_block_info.size())); i->locked = false; piece_pos& p = m_piece_map[index]; int prev_priority = p.priority(this); erase_download_piece(i); int new_priority = p.priority(this); #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif if (new_priority == prev_priority) return; if (m_dirty) return; if (prev_priority == -1) add(index); else update(prev_priority, p.index); #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif } void piece_picker::inc_refcount_all(const torrent_peer* peer) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif ++m_seeds; if (m_seeds == 1) { // when m_seeds is increased from 0 to 1 // we may have to add pieces that previously // didn't have any peers m_dirty = true; } #ifdef TORRENT_DEBUG_REFCOUNTS for (std::vector::iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end; ++i) { TORRENT_ASSERT(i->have_peers.count(peer) == 0); i->have_peers.insert(peer); } #else TORRENT_UNUSED(peer); #endif } void piece_picker::dec_refcount_all(const torrent_peer* peer) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif if (m_seeds > 0) { --m_seeds; if (m_seeds == 0) { // when m_seeds is decreased from 1 to 0 // we may have to remove pieces that previously // didn't have any peers m_dirty = true; } #ifdef TORRENT_DEBUG_REFCOUNTS for (std::vector::iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end; ++i) { TORRENT_ASSERT(i->have_peers.count(peer) == 1); i->have_peers.erase(peer); } #else TORRENT_UNUSED(peer); #endif return; } TORRENT_ASSERT(m_seeds == 0); for (std::vector::iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end; ++i) { #ifdef TORRENT_DEBUG_REFCOUNTS TORRENT_ASSERT(i->have_peers.count(peer) == 1); i->have_peers.erase(peer); #else TORRENT_UNUSED(peer); #endif TORRENT_ASSERT(i->peer_count > 0); --i->peer_count; } m_dirty = true; } void piece_picker::inc_refcount(int index, const torrent_peer* peer) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "inc_refcount(" << index << ")" << std::endl; #endif piece_pos& p = m_piece_map[index]; #ifdef TORRENT_DEBUG_REFCOUNTS TORRENT_ASSERT(p.have_peers.count(peer) == 0); p.have_peers.insert(peer); #else TORRENT_UNUSED(peer); #endif int prev_priority = p.priority(this); ++p.peer_count; if (m_dirty) return; int new_priority = p.priority(this); if (prev_priority == new_priority) return; if (prev_priority == -1) add(index); else update(prev_priority, p.index); } // this function decrements the m_seeds counter // and increments the peer counter on every piece // instead. Sometimes of we connect to a seed that // later sends us a dont-have message, we'll need to // turn that m_seed into counts on the pieces since // they can't be negative void piece_picker::break_one_seed() { TORRENT_PIECE_PICKER_INVARIANT_CHECK; TORRENT_ASSERT(m_seeds > 0); --m_seeds; for (std::vector::iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end; ++i) { ++i->peer_count; } m_dirty = true; } void piece_picker::dec_refcount(int index, const torrent_peer* peer) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "dec_refcount(" << index << ")" << std::endl; #endif piece_pos& p = m_piece_map[index]; if (p.peer_count == 0) { TORRENT_ASSERT(m_seeds > 0); // this is the case where we have one or more // seeds, and one of them saying: I don't have this // piece anymore. we need to break up one of the seed // counters into actual peer counters on the pieces break_one_seed(); } int prev_priority = p.priority(this); #ifdef TORRENT_DEBUG_REFCOUNTS TORRENT_ASSERT(p.have_peers.count(peer) == 1); p.have_peers.erase(peer); #else TORRENT_UNUSED(peer); #endif TORRENT_ASSERT(p.peer_count > 0); --p.peer_count; if (m_dirty) return; if (prev_priority >= 0) update(prev_priority, p.index); } void piece_picker::inc_refcount(bitfield const& bitmask, const torrent_peer* peer) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "inc_refcount(bitfield)" << std::endl; #endif // nothing set, nothing to do here if (bitmask.none_set()) return; if (bitmask.all_set() && bitmask.size() == m_piece_map.size()) { inc_refcount_all(peer); return; } const int size = (std::min)(50, int(bitmask.size()/2)); // this is an optimization where if just a few // pieces end up changing, instead of making // the piece list dirty, just update those pieces // instead int* incremented = TORRENT_ALLOCA(int, size); int num_inc = 0; if (!m_dirty) { // first count how many pieces we're updating. If it's few (less than half) // we'll just update them one at a time. Othewise we'll just update the counters // and mark the picker as dirty, so we'll rebuild it next time we need it. // this only matters if we're not already dirty, in which case the fasted // thing to do is to just update the counters and be done int index = 0; for (bitfield::const_iterator i = bitmask.begin() , end(bitmask.end()); i != end; ++i, ++index) { if (!*i) continue; if (num_inc < size) incremented[num_inc] = index; ++num_inc; if (num_inc >= size) break; } if (num_inc < size) { // not that many pieces were updated // just update those individually instead of // rebuilding the whole piece list for (int i = 0; i < num_inc; ++i) { int piece = incremented[i]; piece_pos& p = m_piece_map[piece]; int prev_priority = p.priority(this); ++p.peer_count; #ifdef TORRENT_DEBUG_REFCOUNTS TORRENT_ASSERT(p.have_peers.count(peer) == 0); p.have_peers.insert(peer); #else TORRENT_UNUSED(peer); #endif int new_priority = p.priority(this); if (prev_priority == new_priority) continue; else if (prev_priority >= 0) update(prev_priority, p.index); else add(piece); } return; } } int index = 0; bool updated = false; for (bitfield::const_iterator i = bitmask.begin() , end(bitmask.end()); i != end; ++i, ++index) { if (*i) { #ifdef TORRENT_DEBUG_REFCOUNTS TORRENT_ASSERT(m_piece_map[index].have_peers.count(peer) == 0); m_piece_map[index].have_peers.insert(peer); #else TORRENT_UNUSED(peer); #endif ++m_piece_map[index].peer_count; updated = true; } } // if we're already dirty, no point in doing anything more if (m_dirty) return; if (updated) m_dirty = true; } void piece_picker::dec_refcount(bitfield const& bitmask, const torrent_peer* peer) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif TORRENT_ASSERT(bitmask.size() <= m_piece_map.size()); #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "dec_refcount(bitfield)" << std::endl; #endif // nothing set, nothing to do here if (bitmask.none_set()) return; if (bitmask.all_set() && bitmask.size() == m_piece_map.size()) { dec_refcount_all(peer); return; } const int size = (std::min)(50, int(bitmask.size()/2)); // this is an optimization where if just a few // pieces end up changing, instead of making // the piece list dirty, just update those pieces // instead int* decremented = TORRENT_ALLOCA(int, size); int num_dec = 0; if (!m_dirty) { // first count how many pieces we're updating. If it's few (less than half) // we'll just update them one at a time. Othewise we'll just update the counters // and mark the picker as dirty, so we'll rebuild it next time we need it. // this only matters if we're not already dirty, in which case the fasted // thing to do is to just update the counters and be done int index = 0; for (bitfield::const_iterator i = bitmask.begin() , end(bitmask.end()); i != end; ++i, ++index) { if (!*i) continue; if (num_dec < size) decremented[num_dec] = index; ++num_dec; if (num_dec >= size) break; } if (num_dec < size) { // not that many pieces were updated // just update those individually instead of // rebuilding the whole piece list for (int i = 0; i < num_dec; ++i) { int piece = decremented[i]; piece_pos& p = m_piece_map[piece]; int prev_priority = p.priority(this); if (p.peer_count == 0) { TORRENT_ASSERT(m_seeds > 0); // this is the case where we have one or more // seeds, and one of them saying: I don't have this // piece anymore. we need to break up one of the seed // counters into actual peer counters on the pieces break_one_seed(); } #ifdef TORRENT_DEBUG_REFCOUNTS TORRENT_ASSERT(p.have_peers.count(peer) == 1); p.have_peers.erase(peer); #else TORRENT_UNUSED(peer); #endif TORRENT_ASSERT(p.peer_count > 0); --p.peer_count; if (!m_dirty && prev_priority >= 0) update(prev_priority, p.index); } return; } } int index = 0; bool updated = false; for (bitfield::const_iterator i = bitmask.begin() , end(bitmask.end()); i != end; ++i, ++index) { if (*i) { piece_pos& p = m_piece_map[index]; if (p.peer_count == 0) { TORRENT_ASSERT(m_seeds > 0); // this is the case where we have one or more // seeds, and one of them saying: I don't have this // piece anymore. we need to break up one of the seed // counters into actual peer counters on the pieces break_one_seed(); } #ifdef TORRENT_DEBUG_REFCOUNTS TORRENT_ASSERT(p.have_peers.count(peer) == 1); p.have_peers.erase(peer); #else TORRENT_UNUSED(peer); #endif TORRENT_ASSERT(p.peer_count > 0); --p.peer_count; updated = true; } } // if we're already dirty, no point in doing anything more if (m_dirty) return; if (updated) m_dirty = true; } void piece_picker::update_pieces() const { TORRENT_ASSERT(m_dirty); if (m_priority_boundries.empty()) m_priority_boundries.resize(1, 0); #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "update_pieces" << std::endl; #endif std::fill(m_priority_boundries.begin(), m_priority_boundries.end(), 0); for (std::vector::iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end; ++i) { int prio = i->priority(this); if (prio == -1) continue; if (prio >= int(m_priority_boundries.size())) m_priority_boundries.resize(prio + 1, 0); i->index = m_priority_boundries[prio]; ++m_priority_boundries[prio]; } #ifdef TORRENT_PICKER_LOG print_pieces(); #endif int index = 0; for (std::vector::iterator i = m_priority_boundries.begin() , end(m_priority_boundries.end()); i != end; ++i) { *i += index; index = *i; } m_pieces.resize(index, 0); #ifdef TORRENT_PICKER_LOG print_pieces(); #endif index = 0; for (std::vector::iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end; ++i, ++index) { piece_pos& p = *i; int prio = p.priority(this); if (prio == -1) continue; int new_index = (prio == 0 ? 0 : m_priority_boundries[prio - 1]) + p.index; m_pieces[new_index] = index; } int start = 0; for (std::vector::iterator i = m_priority_boundries.begin() , end(m_priority_boundries.end()); i != end; ++i) { if (start == *i) continue; std::random_shuffle(&m_pieces[0] + start, &m_pieces[0] + *i, randint); start = *i; } index = 0; for (std::vector::const_iterator i = m_pieces.begin() , end(m_pieces.end()); i != end; ++i, ++index) { TORRENT_ASSERT(*i >= 0 && *i < int(m_piece_map.size())); m_piece_map[*i].index = index; } m_dirty = false; #ifdef TORRENT_PICKER_LOG print_pieces(); #endif } void piece_picker::piece_passed(int index) { piece_pos& p = m_piece_map[index]; int download_state = p.download_queue(); // this is kind of odd. Could this happen? TORRENT_ASSERT(download_state != piece_pos::piece_open); if (download_state == piece_pos::piece_open) return; std::vector::iterator i = find_dl_piece(download_state, index); TORRENT_ASSERT(i != m_downloads[download_state].end()); TORRENT_ASSERT(i->locked == false); if (i->locked) return; TORRENT_ASSERT(!i->passed_hash_check); i->passed_hash_check = true; ++m_num_passed; if (i->finished < blocks_in_piece(index)) return; we_have(index); } void piece_picker::we_dont_have(int index) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); piece_pos& p = m_piece_map[index]; #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "piece_picker::we_dont_have(" << index << ")" << std::endl; #endif if (!p.have()) { // even though we don't have the piece, it // might still have passed hash check int download_state = p.download_queue(); if (download_state == piece_pos::piece_open) return; std::vector::iterator i = find_dl_piece(download_state, index); if (i->passed_hash_check) { i->passed_hash_check = false; TORRENT_ASSERT(m_num_passed > 0); --m_num_passed; } erase_download_piece(i); return; } TORRENT_ASSERT(m_num_passed > 0); --m_num_passed; if (p.filtered()) { ++m_num_filtered; --m_num_have_filtered; } else { // update cursors if (index < m_cursor) m_cursor = index; if (index >= m_reverse_cursor) m_reverse_cursor = index + 1; if (m_reverse_cursor == m_cursor) { m_reverse_cursor = 0; m_cursor = num_pieces(); } } --m_num_have; p.set_not_have(); if (m_dirty) return; if (p.priority(this) >= 0) add(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) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "piece_picker::we_have(" << index << ")" << std::endl; #endif piece_pos& p = m_piece_map[index]; int info_index = p.index; int priority = p.priority(this); TORRENT_ASSERT(priority < int(m_priority_boundries.size()) || m_dirty); if (p.have()) return; int state = p.download_queue(); if (state != piece_pos::piece_open) { std::vector::iterator i = find_dl_piece(state, index); TORRENT_ASSERT(i != m_downloads[state].end()); // decrement num_passed here to compensate // for the unconditional increment further down if (i->passed_hash_check) --m_num_passed; erase_download_piece(i); } if (p.filtered()) { --m_num_filtered; ++m_num_have_filtered; } ++m_num_have; ++m_num_passed; p.set_have(); if (m_cursor == m_reverse_cursor - 1 && m_cursor == index) { m_cursor = int(m_piece_map.size()); m_reverse_cursor = 0; TORRENT_ASSERT(num_pieces() > 0); } else if (m_cursor == index) { ++m_cursor; for (std::vector::const_iterator i = m_piece_map.begin() + m_cursor , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); ++i, ++m_cursor); } else if (m_reverse_cursor - 1 == index) { --m_reverse_cursor; TORRENT_ASSERT(m_piece_map[m_reverse_cursor].have() || m_piece_map[m_reverse_cursor].filtered()); for (std::vector::const_iterator i = m_piece_map.begin() + m_reverse_cursor - 1; m_reverse_cursor > 0 && (i->have() || i->filtered()); --i, --m_reverse_cursor); TORRENT_ASSERT(m_piece_map[m_reverse_cursor].have() || m_piece_map[m_reverse_cursor].filtered()); } TORRENT_ASSERT(m_reverse_cursor > m_cursor || (m_cursor == num_pieces() && m_reverse_cursor == 0)); if (priority == -1) return; if (m_dirty) return; remove(priority, info_index); TORRENT_ASSERT(p.priority(this) == -1); } bool piece_picker::set_piece_priority(int index, int new_piece_priority) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "set_piece_priority(" << index << ", " << new_piece_priority << ")" << std::endl; #endif TORRENT_ASSERT(new_piece_priority >= 0); TORRENT_ASSERT(new_piece_priority < priority_levels); TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); piece_pos& p = m_piece_map[index]; // if the priority isn't changed, don't do anything if (new_piece_priority == int(p.piece_priority)) return false; int prev_priority = p.priority(this); TORRENT_ASSERT(m_dirty || prev_priority < int(m_priority_boundries.size())); bool ret = false; if (new_piece_priority == piece_pos::filter_priority && p.piece_priority != piece_pos::filter_priority) { // the piece just got filtered if (p.have()) { ++m_num_have_filtered; } else { ++m_num_filtered; // update m_cursor if (m_cursor == m_reverse_cursor - 1 && m_cursor == index) { m_cursor = int(m_piece_map.size()); m_reverse_cursor = 0; } else if (m_cursor == index) { ++m_cursor; while (m_cursor < int(m_piece_map.size()) && (m_piece_map[m_cursor].have() || m_piece_map[m_cursor].filtered())) ++m_cursor; } else if (m_reverse_cursor == index + 1) { --m_reverse_cursor; while (m_reverse_cursor > 0 && (m_piece_map[m_reverse_cursor-1].have() || m_piece_map[m_reverse_cursor-1].filtered())) --m_reverse_cursor; } } ret = true; } else if (new_piece_priority != piece_pos::filter_priority && p.piece_priority == piece_pos::filter_priority) { // the piece just got unfiltered if (p.have()) { --m_num_have_filtered; } else { --m_num_filtered; // update cursors if (index < m_cursor) m_cursor = index; if (index >= m_reverse_cursor) m_reverse_cursor = index + 1; if (m_reverse_cursor == m_cursor) { m_reverse_cursor = 0; m_cursor = num_pieces(); } } ret = true; } TORRENT_ASSERT(m_num_filtered >= 0); TORRENT_ASSERT(m_num_have_filtered >= 0); p.piece_priority = new_piece_priority; int new_priority = p.priority(this); if (prev_priority != new_priority && !m_dirty) { if (prev_priority == -1) { add(index); } else { update(prev_priority, p.index); } } if (p.downloading()) { std::vector::iterator i = find_dl_piece( p.download_queue(), index); if (i != m_downloads[p.download_queue()].end()) update_piece_state(i); } return ret; } int piece_picker::piece_priority(int index) const { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); return m_piece_map[index].piece_priority; } void piece_picker::piece_priorities(std::vector& pieces) const { pieces.resize(m_piece_map.size()); std::vector::iterator j = pieces.begin(); for (std::vector::const_iterator i = m_piece_map.begin(), end(m_piece_map.end()); i != end; ++i, ++j) { *j = i->piece_priority; } } // ============ start deprecation ============== void piece_picker::filtered_pieces(std::vector& mask) const { mask.resize(m_piece_map.size()); std::vector::iterator j = mask.begin(); for (std::vector::const_iterator i = m_piece_map.begin(), end(m_piece_map.end()); i != end; ++i, ++j) { *j = i->filtered(); } } // ============ end deprecation ============== namespace { int append_blocks(std::vector& dst, std::vector& src , int const num_blocks) { if (src.empty()) return num_blocks; int const to_copy = (std::min)(int(src.size()), num_blocks); dst.insert(dst.end(), src.begin(), src.begin() + to_copy); src.erase(src.begin(), src.begin() + to_copy); return num_blocks - to_copy; } } // lower availability comes first. This is a less-than comparison, it returns // true if lhs has lower availability than rhs bool piece_picker::partial_compare_rarest_first(downloading_piece const* lhs , downloading_piece const* rhs) const { int lhs_availability = m_piece_map[lhs->index].peer_count; int rhs_availability = m_piece_map[rhs->index].peer_count; if (lhs_availability != rhs_availability) return lhs_availability < rhs_availability; // if the availability is the same, prefer the piece that's closest to // being complete. int lhs_blocks_left = m_blocks_per_piece - lhs->finished - lhs->writing - lhs->requested; TORRENT_ASSERT(lhs_blocks_left > 0); int rhs_blocks_left = m_blocks_per_piece - rhs->finished - rhs->writing - rhs->requested; TORRENT_ASSERT(rhs_blocks_left > 0); return lhs_blocks_left < rhs_blocks_left; } // pieces describes which pieces the peer we're requesting from has. // interesting_blocks is an out parameter, and will be filled with (up to) // num_blocks of interesting blocks that the peer has. // prefer_contiguous_blocks can be set if this peer should download whole // pieces rather than trying to download blocks from the same piece as other // peers. the peer argument is the torrent_peer of the peer we're // picking pieces from. This is used when downloading whole pieces, to only // pick from the same piece the same peer is downloading from. // options are: // * rarest_first // pick the rarest pieces first // * reverse // reverse the piece picking. Pick the most common // pieces first or the last pieces (if picking sequential) // * sequential // download pieces in-order // * on_parole // the peer is on parole, only pick whole pieces which // has only been downloaded and requested from the same // peer // * prioritize_partials // pick blocks from downloading pieces first // only one of rarest_first or sequential can be set // the return value is a combination of picker_log_alert::picker_flags_t, // indicating which path throught the picker we took to arrive at the // returned block picks. boost::uint32_t piece_picker::pick_pieces(bitfield const& pieces , std::vector& interesting_blocks, int num_blocks , int prefer_contiguous_blocks, torrent_peer* peer , int options, std::vector const& suggested_pieces , int num_peers , counters& pc ) const { TORRENT_ASSERT(peer == 0 || peer->in_use); boost::uint32_t ret = 0; // prevent the number of partial pieces to grow indefinitely // make this scale by the number of peers we have. For large // scale clients, we would have more peers, and allow a higher // threshold for the number of partials // deduct pad files because they case partial pieces which are OK // the second condition is to make sure we cap the number of partial // _bytes_. The larger the pieces are, the fewer partial pieces we want. // 2048 corresponds to 32 MiB // TODO: 2 make the 2048 limit configurable const int num_partials = int(m_downloads[piece_pos::piece_downloading].size()) - m_num_pad_files; if (num_partials > num_peers * 3 / 2 || num_partials * m_blocks_per_piece > 2048) { // if we have too many partial pieces, prioritize completing // them. In order for this to have an affect, also disable // prefer whole pieces (otherwise partial pieces would be de-prioritized) options |= prioritize_partials; prefer_contiguous_blocks = 0; ret |= picker_log_alert::partial_ratio; } if (prefer_contiguous_blocks) ret |= picker_log_alert::prefer_contiguous; // only one of rarest_first and sequential can be set. TORRENT_ASSERT(((options & rarest_first) ? 1 : 0) + ((options & sequential) ? 1 : 0) <= 1); #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif TORRENT_ASSERT(num_blocks > 0); TORRENT_ASSERT(pieces.size() == m_piece_map.size()); TORRENT_ASSERT(!m_priority_boundries.empty() || m_dirty); // this will be filled with blocks that we should not request // unless we can't find num_blocks among the other ones. std::vector backup_blocks; std::vector backup_blocks2; const std::vector empty_vector; // When prefer_contiguous_blocks is set (usually set when downloading from // fast peers) the partial pieces will not be prioritized, but actually // ignored as long as possible. All blocks found in downloading // pieces are regarded as backup blocks if (options & prioritize_partials) { // first, allocate a small array on the stack of all the partial // pieces (downloading_piece). We'll then sort this list by // availability or by some other condition. The list of partial pieces // in m_downloads is ordered by piece index, this is to have O(log n) // lookups when finding a downloading_piece for a specific piece index. // this is important and needs to stay sorted that way, that's why // we're copying it here downloading_piece const** ordered_partials = TORRENT_ALLOCA( downloading_piece const*, m_downloads[piece_pos::piece_downloading].size()); int num_ordered_partials = 0; // now, copy over the pointers. We also apply a filter here to not // include ineligible pieces in certain modes. For instance, a piece // that the current peer doesn't have is not included. for (std::vector::const_iterator i = m_downloads[piece_pos::piece_downloading].begin() , end(m_downloads[piece_pos::piece_downloading].end()); i != end; ++i) { pc.inc_stats_counter(counters::piece_picker_partial_loops); // in time critical mode, only pick high priority pieces if ((options & time_critical_mode) && piece_priority(i->index) != priority_levels - 1) continue; if (!is_piece_free(i->index, pieces)) continue; TORRENT_ASSERT(m_piece_map[i->index].download_queue() == piece_pos::piece_downloading); ordered_partials[num_ordered_partials++] = &*i; } // now, sort the list. if (options & rarest_first) { ret |= picker_log_alert::rarest_first_partials; // TODO: this could probably be optimized by incrementally // calling partial_sort to sort one more element in the list. Because // chances are that we'll just need a single piece, and once we've // picked from it we're done. Sorting the rest of the list in that // case is a waste of time. std::sort(ordered_partials, ordered_partials + num_ordered_partials , boost::bind(&piece_picker::partial_compare_rarest_first, this , _1, _2)); } for (int i = 0; i < num_ordered_partials; ++i) { ret |= picker_log_alert::prioritize_partials; num_blocks = add_blocks_downloading(*ordered_partials[i], pieces , interesting_blocks, backup_blocks, backup_blocks2 , num_blocks, prefer_contiguous_blocks, peer, options); if (num_blocks <= 0) return ret; if (int(backup_blocks.size()) >= num_blocks && int(backup_blocks2.size()) >= num_blocks) break; } num_blocks = append_blocks(interesting_blocks, backup_blocks , num_blocks); if (num_blocks <= 0) return ret; num_blocks = append_blocks(interesting_blocks, backup_blocks2 , num_blocks); if (num_blocks <= 0) return ret; } if (!suggested_pieces.empty()) { for (std::vector::const_iterator i = suggested_pieces.begin(); i != suggested_pieces.end(); ++i) { // in time critical mode, only pick high priority pieces if ((options & time_critical_mode) && piece_priority(*i) != priority_levels - 1) continue; pc.inc_stats_counter(counters::piece_picker_suggest_loops); if (!is_piece_free(*i, pieces)) continue; ret |= picker_log_alert::suggested_pieces; num_blocks = add_blocks(*i, pieces , interesting_blocks, backup_blocks , backup_blocks2, num_blocks , prefer_contiguous_blocks, peer, empty_vector , options); if (num_blocks <= 0) return ret; } } if (options & sequential) { if (m_dirty) update_pieces(); TORRENT_ASSERT(!m_dirty); for (std::vector::const_iterator i = m_pieces.begin(); i != m_pieces.end() && piece_priority(*i) == priority_levels - 1; ++i) { if (!is_piece_free(*i, pieces)) continue; ret |= picker_log_alert::prio_sequential_pieces; num_blocks = add_blocks(*i, pieces , interesting_blocks, backup_blocks , backup_blocks2, num_blocks , prefer_contiguous_blocks, peer, suggested_pieces , options); if (num_blocks <= 0) return ret; } // in time critical mode, only pick high priority pieces if ((options & time_critical_mode) == 0) { if (options & reverse) { for (int i = m_reverse_cursor - 1; i >= m_cursor; --i) { pc.inc_stats_counter(counters::piece_picker_sequential_loops); if (!is_piece_free(i, pieces)) continue; // we've already added high priority pieces if (piece_priority(i) == priority_levels - 1) continue; ret |= picker_log_alert::reverse_sequential; num_blocks = add_blocks(i, pieces , interesting_blocks, backup_blocks , backup_blocks2, num_blocks , prefer_contiguous_blocks, peer, suggested_pieces , options); if (num_blocks <= 0) return ret; } } else { for (int i = m_cursor; i < m_reverse_cursor; ++i) { pc.inc_stats_counter(counters::piece_picker_sequential_loops); if (!is_piece_free(i, pieces)) continue; // we've already added high priority pieces if (piece_priority(i) == priority_levels - 1) continue; ret |= picker_log_alert::sequential_pieces; num_blocks = add_blocks(i, pieces , interesting_blocks, backup_blocks , backup_blocks2, num_blocks , prefer_contiguous_blocks, peer, suggested_pieces , options); if (num_blocks <= 0) return ret; } } } } else if (options & rarest_first) { if (m_dirty) update_pieces(); TORRENT_ASSERT(!m_dirty); // in time critical mode, we're only allowed to pick high priority // pieces. This is why reverse mode is disabled when we're in // time-critical mode, because all high priority pieces are at the // front of the list if ((options & reverse) && (options & time_critical_mode) == 0) { for (int i = m_priority_boundries.size() - 1; i >= 0; --i) { int start = (i == 0) ? 0 : m_priority_boundries[i - 1]; int end = m_priority_boundries[i]; for (int p = end - 1; p >= start; --p) { pc.inc_stats_counter(counters::piece_picker_reverse_rare_loops); if (!is_piece_free(m_pieces[p], pieces)) continue; ret |= picker_log_alert::reverse_rarest_first; num_blocks = add_blocks(m_pieces[p], pieces , interesting_blocks, backup_blocks , backup_blocks2, num_blocks , prefer_contiguous_blocks, peer, suggested_pieces , options); if (num_blocks <= 0) return ret; } } } else { for (std::vector::const_iterator i = m_pieces.begin(); i != m_pieces.end(); ++i) { pc.inc_stats_counter(counters::piece_picker_rare_loops); // in time critical mode, only pick high priority pieces // it's safe to break here because in this mode we // pick pieces in priority order. Once we hit a lower priority // piece, we won't encounter any more high priority ones if ((options & time_critical_mode) && piece_priority(*i) != priority_levels - 1) break; if (!is_piece_free(*i, pieces)) continue; ret |= picker_log_alert::rarest_first; num_blocks = add_blocks(*i, pieces , interesting_blocks, backup_blocks , backup_blocks2, num_blocks , prefer_contiguous_blocks, peer, suggested_pieces , options); if (num_blocks <= 0) return ret; } } } else if (options & time_critical_mode) { // if we're in time-critical mode, we are only allowed to pick // high priority pieces. for (std::vector::const_iterator i = m_pieces.begin(); i != m_pieces.end() && piece_priority(*i) == priority_levels - 1; ++i) { if (!is_piece_free(*i, pieces)) continue; ret |= picker_log_alert::time_critical; num_blocks = add_blocks(*i, pieces , interesting_blocks, backup_blocks , backup_blocks2, num_blocks , prefer_contiguous_blocks, peer, suggested_pieces , options); if (num_blocks <= 0) return ret; } } else { // we're not using rarest first (only for the first // bucket, since that's where the currently downloading // pieces are) int start_piece = random() % m_piece_map.size(); int piece = start_piece; while (num_blocks > 0) { // skip pieces we can't pick, and suggested pieces // since we've already picked those while (!is_piece_free(piece, pieces) || std::find(suggested_pieces.begin() , suggested_pieces.end(), piece) != suggested_pieces.end()) { pc.inc_stats_counter(counters::piece_picker_rand_start_loops); ++piece; if (piece == int(m_piece_map.size())) piece = 0; // could not find any more pieces if (piece == start_piece) { goto get_out; } } if (prefer_contiguous_blocks > 1 && !m_piece_map[piece].downloading()) { TORRENT_ASSERT(can_pick(piece, pieces)); TORRENT_ASSERT(m_piece_map[piece].downloading() == false); int start, end; boost::tie(start, end) = expand_piece(piece , prefer_contiguous_blocks, pieces, options); TORRENT_ASSERT(end - start > 0); for (int k = start; k < end; ++k) { TORRENT_ASSERT(m_piece_map[k].downloading() == false); TORRENT_ASSERT(m_piece_map[k].priority(this) >= 0); const int num_blocks_in_piece = blocks_in_piece(k); ret |= picker_log_alert::random_pieces; for (int j = 0; j < num_blocks_in_piece; ++j) { pc.inc_stats_counter(counters::piece_picker_rand_loops); TORRENT_ASSERT(is_piece_free(k, pieces)); interesting_blocks.push_back(piece_block(k, j)); --num_blocks; --prefer_contiguous_blocks; if (prefer_contiguous_blocks <= 0 && num_blocks <= 0) break; } } piece = end; } else { ret |= picker_log_alert::random_pieces; num_blocks = add_blocks(piece, pieces , interesting_blocks, backup_blocks , backup_blocks2, num_blocks , prefer_contiguous_blocks, peer, empty_vector , options); ++piece; } if (piece == int(m_piece_map.size())) piece = 0; // could not find any more pieces if (piece == start_piece) break; } } get_out: if (num_blocks <= 0) return ret; #if TORRENT_USE_INVARIANT_CHECKS verify_pick(interesting_blocks, pieces); verify_pick(backup_blocks, pieces); verify_pick(backup_blocks2, pieces); #endif ret |= picker_log_alert::backup1; num_blocks = append_blocks(interesting_blocks, backup_blocks, num_blocks); if (num_blocks <= 0) return ret; ret |= picker_log_alert::backup2; num_blocks = append_blocks(interesting_blocks, backup_blocks2, num_blocks); if (num_blocks <= 0) return ret; // ===== THIS IS FOR END-GAME MODE ===== // don't double-pick anything if the peer is on parole if (options & on_parole) return ret; // in end game mode we pick a single block // that has already been requested from someone // all pieces that are interesting are in // m_downloads[0] and m_download[1] // (i.e. partial and full pieces) std::vector temp; // pick one random block from one random partial piece. // only pick from non-downloaded blocks. // first, create a temporary array of the partial pieces // this peer has, and can pick from. Cap the stack allocation // at 200 pieces. int partials_size = (std::min)(200, int( m_downloads[piece_pos::piece_downloading].size() + m_downloads[piece_pos::piece_full].size())); if (partials_size == 0) return ret; downloading_piece const** partials = TORRENT_ALLOCA(downloading_piece const*, partials_size); int c = 0; #if TORRENT_USE_ASSERTS && !defined TORRENT_DISABLE_INVARIANT_CHECKS // if we get here, we're about to pick a busy block. First, make sure // we really exhausted the available blocks for (std::vector::const_iterator i = m_downloads[piece_pos::piece_downloading].begin() , end(m_downloads[piece_pos::piece_downloading].end()); i != end; ++i) { downloading_piece const& dp = *i; if ((options & time_critical_mode) && piece_priority(dp.index) != priority_levels - 1) continue; // we either don't have this piece, or we've already requested from it if (!pieces[dp.index]) continue; // if we already have the piece, obviously we should not have // since this is a partial piece in the piece_downloading state, we // should not already have it TORRENT_ASSERT(!m_piece_map[dp.index].have()); // if it was filtered, it would be in the prio_zero queue TORRENT_ASSERT(!m_piece_map[dp.index].filtered()); // we're not allowed to pick from locked pieces if (dp.locked) continue; bool found = false; for (std::vector::const_iterator j = interesting_blocks.begin(), end2(interesting_blocks.end()); j != end2; ++j) { if (j->piece_index != dp.index) continue; found = true; break; } // we expect to find this piece in our interesting_blocks list TORRENT_ASSERT(found); } #endif for (std::vector::const_iterator i = m_downloads[piece_pos::piece_full].begin() , end(m_downloads[piece_pos::piece_full].end()); i != end; ++i) { if (c == partials_size) break; downloading_piece const& dp = *i; TORRENT_ASSERT(dp.requested > 0); // this peer doesn't have this piece, try again if (!pieces[dp.index]) continue; // don't pick pieces with priority 0 TORRENT_ASSERT(piece_priority(dp.index) > 0); if ((options & time_critical_mode) && piece_priority(dp.index) != priority_levels - 1) continue; partials[c++] = &dp; } partials_size = c; while (partials_size > 0) { pc.inc_stats_counter(counters::piece_picker_busy_loops); int piece = random() % partials_size; downloading_piece const* dp = partials[piece]; TORRENT_ASSERT(pieces[dp->index]); TORRENT_ASSERT(piece_priority(dp->index) > 0); // fill in with blocks requested from other peers // as backups const int num_blocks_in_piece = blocks_in_piece(dp->index); TORRENT_ASSERT(dp->requested > 0); block_info const* binfo = blocks_for_piece(*dp); for (int j = 0; j < num_blocks_in_piece; ++j) { block_info const& info = binfo[j]; TORRENT_ASSERT(info.peer == 0 || static_cast(info.peer)->in_use); TORRENT_ASSERT(info.piece_index == dp->index); if (info.state != block_info::state_requested || info.peer == peer) continue; temp.push_back(piece_block(dp->index, j)); } // are we done? if (!temp.empty()) { ret |= picker_log_alert::end_game; interesting_blocks.push_back(temp[random() % temp.size()]); --num_blocks; break; } // the piece we picked only had blocks outstanding requested // by ourself. Remove it and pick another one. partials[piece] = partials[partials_size-1]; --partials_size; } #if defined TORRENT_DEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS // make sure that we at this point have added requests to all unrequested blocks // in all downloading pieces for (std::vector::const_iterator i = m_downloads[piece_pos::piece_downloading].begin() , end(m_downloads[piece_pos::piece_downloading].end()); i != end; ++i) { if (!pieces[i->index]) continue; if (piece_priority(i->index) == 0) continue; if (i->locked) continue; if ((options & time_critical_mode) && piece_priority(i->index) != priority_levels - 1) continue; const int num_blocks_in_piece = blocks_in_piece(i->index); block_info const* binfo = blocks_for_piece(*i); for (int j = 0; j < num_blocks_in_piece; ++j) { block_info const& info = binfo[j]; TORRENT_ASSERT(info.piece_index == i->index); if (info.state != block_info::state_none) continue; std::vector::iterator k = std::find( interesting_blocks.begin(), interesting_blocks.end() , piece_block(i->index, j)); if (k != interesting_blocks.end()) continue; fprintf(stderr, "interesting blocks:\n"); for (k = interesting_blocks.begin(); k != interesting_blocks.end(); ++k) fprintf(stderr, "(%d, %d)", k->piece_index, k->block_index); fprintf(stderr, "\nnum_blocks: %d\n", num_blocks); for (std::vector::const_iterator l = m_downloads[piece_pos::piece_downloading].begin() , end2(m_downloads[piece_pos::piece_downloading].end()); l != end2; ++l) { block_info const* binfo2 = blocks_for_piece(*l); fprintf(stderr, "%d : ", l->index); const int cnt = blocks_in_piece(l->index); for (int m = 0; m < cnt; ++m) fprintf(stderr, "%d", binfo2[m].state); fprintf(stderr, "\n"); } TORRENT_ASSERT(false); } } if (interesting_blocks.empty()) { for (int i = 0; i < num_pieces(); ++i) { if (!pieces[i]) continue; if (m_piece_map[i].priority(this) <= 0) continue; if (have_piece(i)) continue; int download_state = m_piece_map[i].download_queue(); if (download_state == piece_pos::piece_open) continue; std::vector::const_iterator k = find_dl_piece(download_state, i); TORRENT_ASSERT(k != m_downloads[download_state].end()); if (k == m_downloads[download_state].end()) continue; } } #endif // TORRENT_DEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS return ret; } // have piece means that the piece passed hash check // AND has been successfully written to disk bool piece_picker::have_piece(int index) const { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); piece_pos const& p = m_piece_map[index]; return p.index == piece_pos::we_have_index; } int piece_picker::blocks_in_piece(int index) const { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size()) || m_piece_map.empty()); if (index + 1 == int(m_piece_map.size())) return m_blocks_in_last_piece; else return m_blocks_per_piece; } bool piece_picker::is_piece_free(int piece, bitfield const& bitmask) const { TORRENT_ASSERT(piece >= 0 && piece < int(m_piece_map.size())); return bitmask[piece] && !m_piece_map[piece].have() && !m_piece_map[piece].filtered(); } bool piece_picker::can_pick(int piece, bitfield const& bitmask) const { TORRENT_ASSERT(piece >= 0 && piece < int(m_piece_map.size())); return bitmask[piece] && !m_piece_map[piece].have() // TODO: when expanding pieces for cache stripe reasons, // the !downloading condition doesn't make much sense && !m_piece_map[piece].downloading() && !m_piece_map[piece].filtered(); } #if TORRENT_USE_INVARIANT_CHECKS void piece_picker::check_peers() { for (std::vector::iterator i = m_block_info.begin() , end(m_block_info.end()); i != end; ++i) { TORRENT_ASSERT(i->peer == 0 || static_cast(i->peer)->in_use); } } #endif void piece_picker::clear_peer(torrent_peer* peer) { for (std::vector::iterator i = m_block_info.begin() , end(m_block_info.end()); i != end; ++i) { if (i->peer == peer) i->peer = 0; } } // the first bool is true if this is the only peer that has requested and downloaded // blocks from this piece. // the second bool is true if this is the only active peer that is requesting // and downloading blocks from this piece. Active means having a connection. // TODO: 2 the first_block returned here is the largest free range, not // the first-fit range, which would be better boost::tuple piece_picker::requested_from( piece_picker::downloading_piece const& p , int num_blocks_in_piece, torrent_peer* peer) const { bool exclusive = true; bool exclusive_active = true; int contiguous_blocks = 0; int max_contiguous = 0; int first_block = 0; block_info const* binfo = blocks_for_piece(p); for (int j = 0; j < num_blocks_in_piece; ++j) { piece_picker::block_info const& info = binfo[j]; TORRENT_ASSERT(info.peer == 0 || static_cast(info.peer)->in_use); TORRENT_ASSERT(info.piece_index == p.index); if (info.state == piece_picker::block_info::state_none) { ++contiguous_blocks; continue; } if (contiguous_blocks > max_contiguous) { max_contiguous = contiguous_blocks; first_block = j - contiguous_blocks; } contiguous_blocks = 0; if (info.peer != peer) { exclusive = false; if (info.state == piece_picker::block_info::state_requested && info.peer != 0) { exclusive_active = false; } } } if (contiguous_blocks > max_contiguous) { max_contiguous = contiguous_blocks; first_block = num_blocks_in_piece - contiguous_blocks; } return boost::make_tuple(exclusive, exclusive_active, max_contiguous , first_block); } int piece_picker::add_blocks(int piece , bitfield const& pieces , std::vector& interesting_blocks , std::vector& backup_blocks , std::vector& backup_blocks2 , int num_blocks, int prefer_contiguous_blocks , torrent_peer* peer, std::vector const& ignore , int options) const { TORRENT_ASSERT(piece >= 0); TORRENT_ASSERT(piece < int(m_piece_map.size())); TORRENT_ASSERT(is_piece_free(piece, pieces)); // std::cout << "add_blocks(" << piece << ")" << std::endl; // std::cout << " num_blocks " << num_blocks << std::endl; // ignore pieces found in the ignore list if (std::find(ignore.begin(), ignore.end(), piece) != ignore.end()) return num_blocks; if (m_piece_map[piece].download_queue() != piece_pos::piece_open && m_piece_map[piece].download_queue() != piece_pos::piece_downloading) return num_blocks; TORRENT_ASSERT(m_piece_map[piece].priority(this) >= 0); int state = m_piece_map[piece].download_queue(); if (state == piece_pos::piece_downloading) { // if we're prioritizing partials, we've already // looked through the downloading pieces if (options & prioritize_partials) return num_blocks; std::vector::const_iterator i = find_dl_piece( piece_pos::piece_downloading, piece); TORRENT_ASSERT(i != m_downloads[state].end()); // std::cout << "add_blocks_downloading(" << piece << ")" << std::endl; return add_blocks_downloading(*i, pieces , interesting_blocks, backup_blocks, backup_blocks2 , num_blocks, prefer_contiguous_blocks, peer, options); } int num_blocks_in_piece = blocks_in_piece(piece); // pick a new piece if (prefer_contiguous_blocks == 0) { if (num_blocks_in_piece > num_blocks) num_blocks_in_piece = num_blocks; TORRENT_ASSERT(is_piece_free(piece, pieces)); for (int j = 0; j < num_blocks_in_piece; ++j) interesting_blocks.push_back(piece_block(piece, j)); num_blocks -= num_blocks_in_piece; } else { int start, end; boost::tie(start, end) = expand_piece(piece, prefer_contiguous_blocks , pieces, options); for (int k = start; k < end; ++k) { TORRENT_ASSERT(m_piece_map[k].priority(this) > 0); num_blocks_in_piece = blocks_in_piece(k); TORRENT_ASSERT(is_piece_free(k, pieces)); for (int j = 0; j < num_blocks_in_piece; ++j) { interesting_blocks.push_back(piece_block(k, j)); --num_blocks; --prefer_contiguous_blocks; if (prefer_contiguous_blocks == 0 && num_blocks <= 0) break; } } } #if TORRENT_USE_INVARIANT_CHECKS verify_pick(interesting_blocks, pieces); #endif return (std::max)(num_blocks, 0); } int piece_picker::add_blocks_downloading(downloading_piece const& dp , bitfield const& pieces , std::vector& interesting_blocks , std::vector& backup_blocks , std::vector& backup_blocks2 , int num_blocks, int prefer_contiguous_blocks , torrent_peer* peer, int options) const { if (!pieces[dp.index]) return num_blocks; TORRENT_ASSERT(!m_piece_map[dp.index].filtered()); // this piece failed to write. We're currently restoring // it. It's not OK to send more requests to it right now. if (dp.locked) return num_blocks; int num_blocks_in_piece = blocks_in_piece(dp.index); // is true if all the other pieces that are currently // requested from this piece are from the same // peer as 'peer'. bool exclusive; bool exclusive_active; // used to report back the largest contiguous block run int contiguous_blocks; int first_block; boost::tie(exclusive, exclusive_active, contiguous_blocks, first_block) = requested_from(dp, num_blocks_in_piece, peer); // no need in picking from the largest contiguous block run unless // we're interested in it. In fact, we really want the opposite. if (prefer_contiguous_blocks == 0) first_block = 0; // peers on parole are only allowed to pick blocks from // pieces that only they have downloaded/requested from if ((options & on_parole) && !exclusive) return num_blocks; block_info const* binfo = blocks_for_piece(dp); // we prefer whole blocks, but there are other peers // downloading from this piece and there aren't enough contiguous blocks // to pick, add it as backups. // if we're on parole, don't let the contiguous blocks stop us, we want // to primarily request from a piece all by ourselves. if (prefer_contiguous_blocks > contiguous_blocks && !exclusive_active && (options & on_parole) == 0) { if (int(backup_blocks2.size()) >= num_blocks) return num_blocks; for (int j = 0; j < num_blocks_in_piece; ++j) { // ignore completed blocks and already requested blocks int block_idx = (j + first_block) % num_blocks_in_piece; block_info const& info = binfo[block_idx]; TORRENT_ASSERT(info.piece_index == dp.index); if (info.state != block_info::state_none) continue; backup_blocks2.push_back(piece_block(dp.index, block_idx)); } return num_blocks; } for (int j = 0; j < num_blocks_in_piece; ++j) { // ignore completed blocks and already requested blocks int block_idx = (j + first_block) % num_blocks_in_piece; block_info const& info = binfo[block_idx]; TORRENT_ASSERT(info.piece_index == dp.index); if (info.state != block_info::state_none) continue; // this block is interesting (we don't have it yet). interesting_blocks.push_back(piece_block(dp.index, block_idx)); // we have found a block that's free to download --num_blocks; // if we prefer contiguous blocks, continue picking from this // piece even though we have num_blocks if (prefer_contiguous_blocks > 0) { --prefer_contiguous_blocks; continue; } if (num_blocks <= 0) return 0; } if (num_blocks <= 0) return 0; if (options & on_parole) return num_blocks; if (int(backup_blocks.size()) >= num_blocks) return num_blocks; #if TORRENT_USE_INVARIANT_CHECKS verify_pick(backup_blocks, pieces); #endif return num_blocks; } std::pair piece_picker::expand_piece(int piece, int contiguous_blocks , bitfield const& have, int options) const { if (contiguous_blocks == 0) return std::make_pair(piece, piece + 1); // round to even pieces and expand in order to get the number of // contiguous pieces we want int whole_pieces = (contiguous_blocks + m_blocks_per_piece - 1) / m_blocks_per_piece; int start = piece; int lower_limit; if (options & align_expanded_pieces) { lower_limit = piece - (piece % whole_pieces); } else { lower_limit = piece - whole_pieces + 1; if (lower_limit < 0) lower_limit = 0; } while (start - 1 >= lower_limit && can_pick(start - 1, have)) --start; TORRENT_ASSERT(start >= 0); int end = piece + 1; int upper_limit ; if (options & align_expanded_pieces) { upper_limit = lower_limit + whole_pieces; } else { upper_limit = start + whole_pieces; } if (upper_limit > int(m_piece_map.size())) upper_limit = int(m_piece_map.size()); while (end < upper_limit && can_pick(end, have)) ++end; return std::make_pair(start, end); } bool piece_picker::is_piece_finished(int index) const { TORRENT_ASSERT(index < int(m_piece_map.size())); TORRENT_ASSERT(index >= 0); piece_pos const& p = m_piece_map[index]; if (p.index == piece_pos::we_have_index) return true; int state = p.download_queue(); if (state == piece_pos::piece_open) { for (int i = 0; i < piece_pos::num_download_categories; ++i) TORRENT_ASSERT(find_dl_piece(i, index) == m_downloads[i].end()); return false; } std::vector::const_iterator i = find_dl_piece(state, index); TORRENT_ASSERT(i != m_downloads[state].end()); TORRENT_ASSERT(int(i->finished) <= m_blocks_per_piece); int max_blocks = blocks_in_piece(index); if (int(i->finished) + int(i->writing) < max_blocks) return false; TORRENT_ASSERT(int(i->finished) + int(i->writing) == max_blocks); #if TORRENT_USE_ASSERTS && !defined TORRENT_DISABLE_INVARIANT_CHECKS block_info const* info = blocks_for_piece(*i); for (int k = 0; k < max_blocks; ++k) { TORRENT_ASSERT(info[k].piece_index == index); TORRENT_ASSERT(info[k].state == block_info::state_finished || info[k].state == block_info::state_writing); } #endif return true; } bool piece_picker::has_piece_passed(int index) const { TORRENT_ASSERT(index < int(m_piece_map.size())); TORRENT_ASSERT(index >= 0); piece_pos const& p = m_piece_map[index]; if (p.index == piece_pos::we_have_index) return true; int state = p.download_queue(); if (state == piece_pos::piece_open) { for (int i = 0; i < piece_pos::num_download_categories; ++i) TORRENT_ASSERT(find_dl_piece(i, index) == m_downloads[i].end()); return false; } std::vector::const_iterator i = find_dl_piece(state, index); TORRENT_ASSERT(i != m_downloads[state].end()); return i->passed_hash_check; } std::vector::iterator piece_picker::find_dl_piece( int queue, int index) { TORRENT_ASSERT(queue >= 0 && queue < piece_pos::num_download_categories); downloading_piece cmp; cmp.index = index; std::vector::iterator i = std::lower_bound( m_downloads[queue].begin(), m_downloads[queue].end(), cmp); if (i == m_downloads[queue].end()) return i; if (i->index == index) return i; return m_downloads[queue].end(); } std::vector::const_iterator piece_picker::find_dl_piece( int queue, int index) const { return const_cast(this)->find_dl_piece(queue, index); } std::vector::iterator piece_picker::update_piece_state( std::vector::iterator dp) { #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "update_piece_state(" << dp->index << ")" << std::endl; #endif int const num_blocks = blocks_in_piece(dp->index); piece_pos& p = m_piece_map[dp->index]; int const current_state = p.download_state; TORRENT_ASSERT(current_state != piece_pos::piece_open); if (current_state == piece_pos::piece_open) return dp; // this function is not allowed to create new downloading pieces int new_state = 0; if (p.filtered()) { new_state = piece_pos::piece_zero_prio; } else if (dp->requested + dp->finished + dp->writing == 0) { new_state = piece_pos::piece_open; } else if (dp->requested + dp->finished + dp->writing < num_blocks) { new_state = p.reverse() ? piece_pos::piece_downloading_reverse : piece_pos::piece_downloading; } else if (dp->requested > 0) { TORRENT_ASSERT(dp->requested + dp->finished + dp->writing == num_blocks); new_state = p.reverse() ? piece_pos::piece_full_reverse : piece_pos::piece_full; } else { TORRENT_ASSERT(dp->finished + dp->writing == num_blocks); new_state = piece_pos::piece_finished; } #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << " new_state: " << new_state << " current_state: " << current_state << std::endl; #endif if (new_state == current_state) return dp; if (new_state == piece_pos::piece_open) return dp; // assert that the iterator that was passed-in in fact lives in // the correct list TORRENT_ASSERT(find_dl_piece(p.download_queue(), dp->index) == dp); // remove the downloading_piece from the list corresponding // to the old state downloading_piece dp_info = *dp; m_downloads[p.download_queue()].erase(dp); int prio = p.priority(this); p.download_state = new_state; #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << " " << dp_info.index << " state (" << current_state << " -> " << new_state << ")" << std::endl; #endif // insert the downloading_piece in the list corresponding to // the new state downloading_piece cmp; cmp.index = dp_info.index; std::vector::iterator i = std::lower_bound( m_downloads[p.download_queue()].begin() , m_downloads[p.download_queue()].end(), cmp); TORRENT_ASSERT(i == m_downloads[p.download_queue()].end() || i->index != dp_info.index); i = m_downloads[p.download_queue()].insert(i, dp_info); if (!m_dirty) { if (prio == -1 && p.priority(this) != -1) add(dp_info.index); else if (prio != -1) update(prio, p.index); } return i; } /* int piece_picker::get_block_state(piece_block block) const { TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); // if we have the piece, the block state is considered finished if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return block_info::state_finished; int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return block_info::state_none; std::vector::const_iterator i = find_dl_piece(state , block.piece_index); TORRENT_ASSERT(i != m_downloads[state].end()); block_info const* info = blocks_for_piece(*i); TORRENT_ASSERT(info[block.block_index].piece_index == block.piece_index); return info[block.block_index].state; } */ bool piece_picker::is_requested(piece_block block) const { #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_VALUE_IS_DEFINED(block); #endif TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return false; std::vector::const_iterator i = find_dl_piece(state , block.piece_index); TORRENT_ASSERT(i != m_downloads[state].end()); block_info const* info = blocks_for_piece(*i); TORRENT_ASSERT(info[block.block_index].piece_index == block.piece_index); return info[block.block_index].state == block_info::state_requested; } bool piece_picker::is_downloaded(piece_block block) const { #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_VALUE_IS_DEFINED(block); #endif TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true; int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return false; std::vector::const_iterator i = find_dl_piece(state , block.piece_index); TORRENT_ASSERT(i != m_downloads[state].end()); block_info const* info = blocks_for_piece(*i); TORRENT_ASSERT(info[block.block_index].piece_index == block.piece_index); return info[block.block_index].state == block_info::state_finished || info[block.block_index].state == block_info::state_writing; } bool piece_picker::is_finished(piece_block block) const { #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_VALUE_IS_DEFINED(block); #endif TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); piece_pos const& p = m_piece_map[block.piece_index]; if (p.index == piece_pos::we_have_index) return true; if (p.download_queue() == piece_pos::piece_open) return false; std::vector::const_iterator i = find_dl_piece(p.download_queue() , block.piece_index); TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info const* info = blocks_for_piece(*i); TORRENT_ASSERT(info[block.block_index].piece_index == block.piece_index); return info[block.block_index].state == block_info::state_finished; } // options may be 0 or piece_picker::reverse bool piece_picker::mark_as_downloading(piece_block block , torrent_peer* peer, int options) { #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "mark_as_downloading( {" << block.piece_index << ", " << block.block_index << "} )" << std::endl; #endif TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); TORRENT_ASSERT(!m_piece_map[block.piece_index].have()); piece_pos& p = m_piece_map[block.piece_index]; if (p.download_queue() == piece_pos::piece_open) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif int prio = p.priority(this); TORRENT_ASSERT(prio < int(m_priority_boundries.size()) || m_dirty); p.download_state = (options & reverse) ? piece_pos::piece_downloading_reverse : piece_pos::piece_downloading; if (prio >= 0 && !m_dirty) update(prio, p.index); dlpiece_iter dp = add_download_piece(block.piece_index); block_info* binfo = blocks_for_piece(*dp); block_info& info = binfo[block.block_index]; TORRENT_ASSERT(info.piece_index == block.piece_index); if (info.state == block_info::state_finished) return false; info.state = block_info::state_requested; info.peer = peer; info.num_peers = 1; #if TORRENT_USE_ASSERTS TORRENT_ASSERT(info.peers.count(peer) == 0); info.peers.insert(peer); #endif ++dp->requested; // update_full may move the downloading piece to // a different vector, so 'dp' may be invalid after // this call update_piece_state(dp); } else { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif std::vector::iterator i = find_dl_piece(p.download_queue() , block.piece_index); TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info* binfo = blocks_for_piece(*i); block_info& info = binfo[block.block_index]; TORRENT_ASSERT(info.piece_index == block.piece_index); if (info.state == block_info::state_writing || info.state == block_info::state_finished) { return false; } if ((options & reverse) && !p.reverse() && i->requested == 0) { // this piece isn't reverse, but there's no other peer // downloading from it and we just requested a block from a // reverse peer. Make it reverse int prio = p.priority(this); p.make_reverse(); if (prio >= 0 && !m_dirty) update(prio, p.index); } TORRENT_ASSERT(info.state == block_info::state_none || (info.state == block_info::state_requested && (info.num_peers > 0))); info.peer = peer; if (info.state != block_info::state_requested) { info.state = block_info::state_requested; ++i->requested; i = update_piece_state(i); } ++info.num_peers; // if we make a non-reverse request from a reversed piece, // undo the reverse state if ((options & reverse) == 0 && p.reverse()) { int prio = p.priority(this); // make it non-reverse p.unreverse(); if (prio >= 0 && !m_dirty) update(prio, p.index); } #if TORRENT_USE_ASSERTS TORRENT_ASSERT(info.peers.count(peer) == 0); info.peers.insert(peer); #endif } return true; } int piece_picker::num_peers(piece_block block) const { TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); piece_pos const& p = m_piece_map[block.piece_index]; if (!p.downloading()) return 0; std::vector::const_iterator i = find_dl_piece(p.download_queue() , block.piece_index); TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info const* binfo = blocks_for_piece(*i); block_info const& info = binfo[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); TORRENT_ASSERT(info.piece_index == block.piece_index); return info.num_peers; } void piece_picker::get_availability(std::vector& avail) const { TORRENT_ASSERT(m_seeds >= 0); TORRENT_PIECE_PICKER_INVARIANT_CHECK; avail.resize(m_piece_map.size()); std::vector::iterator j = avail.begin(); for (std::vector::const_iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end; ++i, ++j) *j = i->peer_count + m_seeds; } int piece_picker::get_availability(int piece) const { TORRENT_ASSERT(piece >= 0 && piece < int(m_piece_map.size())); return m_piece_map[piece].peer_count + m_seeds; } bool piece_picker::mark_as_writing(piece_block block, torrent_peer* peer) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "mark_as_writing( {" << block.piece_index << ", " << block.block_index << "} )" << std::endl; #endif TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); // this is not valid for web peers // TORRENT_ASSERT(peer != 0); piece_pos& p = m_piece_map[block.piece_index]; if (p.downloading() == 0) { // if we already have this piece, just ignore this if (have_piece(block.piece_index)) return false; int const prio = p.priority(this); TORRENT_ASSERT(prio < int(m_priority_boundries.size()) || m_dirty); p.download_state = piece_pos::piece_downloading; // prio being -1 can happen if a block is requested before // the piece priority was set to 0 if (prio >= 0 && !m_dirty) update(prio, p.index); dlpiece_iter dp = add_download_piece(block.piece_index); block_info* binfo = blocks_for_piece(*dp); block_info& info = binfo[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); TORRENT_ASSERT(info.piece_index == block.piece_index); TORRENT_ASSERT(info.state == block_info::state_none); if (info.state == block_info::state_finished) return false; info.state = block_info::state_writing; info.peer = peer; info.num_peers = 0; #if TORRENT_USE_ASSERTS info.peers.clear(); #endif dp->writing = 1; update_piece_state(dp); } else { std::vector::iterator i = find_dl_piece(p.download_queue() , block.piece_index); TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info* binfo = blocks_for_piece(*i); block_info& info = binfo[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); TORRENT_ASSERT(info.piece_index == block.piece_index); info.peer = peer; if (info.state == block_info::state_requested) --i->requested; if (info.state == block_info::state_writing || info.state == block_info::state_finished) return false; ++i->writing; info.state = block_info::state_writing; TORRENT_ASSERT(info.piece_index == block.piece_index); // all other requests for this block should have been // cancelled now info.num_peers = 0; #if TORRENT_USE_ASSERTS info.peers.clear(); #endif update_piece_state(i); } return true; } // calling this function prevents this piece from being picked // by the piece picker until the pieces is restored. This allow // the disk thread to synchronize and flush any failed state // (used for disk write failures and piece hash failures). void piece_picker::lock_piece(int piece) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "lock_piece(" << piece << ")" << std::endl; #endif int state = m_piece_map[piece].download_queue(); if (state == piece_pos::piece_open) return; std::vector::iterator i = find_dl_piece(state, piece); if (i == m_downloads[state].end()) return; TORRENT_ASSERT(i->passed_hash_check == false); if (i->passed_hash_check) { // it's not clear why this would happen, // but it seems reasonable to not break the // accounting over it. i->passed_hash_check = false; TORRENT_ASSERT(m_num_passed > 0); --m_num_passed; } // prevent this piece from being picked until it's restored i->locked = true; } // TODO: 2 it would be nice if this could be folded into lock_piece() // the main distinction is that this also maintains the m_num_passed // counter and the passed_hash_check member // Is there ever a case where we call write filed without also locking // the piece? Perhaps write_failed() should imply locking it. void piece_picker::write_failed(piece_block block) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "write_failed( {" << block.piece_index << ", " << block.block_index << "} )" << std::endl; #endif int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return; std::vector::iterator i = find_dl_piece(state, block.piece_index); if (i == m_downloads[state].end()) return; block_info* binfo = blocks_for_piece(*i); block_info& info = binfo[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); TORRENT_ASSERT(info.piece_index == block.piece_index); TORRENT_ASSERT(info.state == block_info::state_writing); TORRENT_ASSERT(info.num_peers == 0); TORRENT_ASSERT(i->writing > 0); TORRENT_ASSERT(info.state == block_info::state_writing); if (info.state == block_info::state_finished) return; if (info.state == block_info::state_writing) --i->writing; info.peer = 0; info.state = block_info::state_none; if (i->passed_hash_check) { // the hash was good, but we failed to write // some of the blocks to disk, which means we // can't consider the piece complete i->passed_hash_check = false; TORRENT_ASSERT(m_num_passed > 0); --m_num_passed; } // prevent this hash job from actually completing // this piece, by setting the failure state. // the piece is unlocked in the call to restore_piece() i->locked = true; i = update_piece_state(i); if (i->finished + i->writing + i->requested == 0) { piece_pos& p = m_piece_map[block.piece_index]; int prev_priority = p.priority(this); erase_download_piece(i); int new_priority = p.priority(this); if (m_dirty) return; if (new_priority == prev_priority) return; if (prev_priority == -1) add(block.piece_index); else update(prev_priority, p.index); } } void piece_picker::mark_as_canceled(const piece_block block, torrent_peer* peer) { #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "mark_as_cancelled( {" << block.piece_index << ", " << block.block_index << "} )" << std::endl; #endif #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif TORRENT_ASSERT(block.piece_index >= 0); TORRENT_ASSERT(block.block_index >= 0); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); piece_pos& p = m_piece_map[block.piece_index]; if (p.download_queue() == piece_pos::piece_open) return; std::vector::iterator i = find_dl_piece(p.download_queue() , block.piece_index); TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info* binfo = blocks_for_piece(*i); block_info& info = binfo[block.block_index]; if (info.state == block_info::state_finished) return; TORRENT_ASSERT(info.num_peers == 0); info.peer = peer; TORRENT_ASSERT(info.state == block_info::state_writing || peer == 0); if (info.state == block_info::state_writing) { --i->writing; info.state = block_info::state_none; // i may be invalid after this call i = update_piece_state(i); if (i->finished + i->writing + i->requested == 0) { int prev_priority = p.priority(this); erase_download_piece(i); int new_priority = p.priority(this); if (m_dirty) return; if (new_priority == prev_priority) return; if (prev_priority == -1) add(block.piece_index); else update(prev_priority, p.index); } } else { TORRENT_ASSERT(info.state == block_info::state_none); } #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif } void piece_picker::mark_as_finished(piece_block block, torrent_peer* peer) { #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "mark_as_finished( {" << block.piece_index << ", " << block.block_index << "} )" << std::endl; #endif TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); TORRENT_ASSERT(block.piece_index >= 0); TORRENT_ASSERT(block.block_index >= 0); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); piece_pos& p = m_piece_map[block.piece_index]; if (p.download_queue() == piece_pos::piece_open) { // if we already have this piece, just ignore this if (have_piece(block.piece_index)) return; #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif int const prio = p.priority(this); TORRENT_ASSERT(prio < int(m_priority_boundries.size()) || m_dirty); p.download_state = piece_pos::piece_downloading; if (prio >= 0 && !m_dirty) update(prio, p.index); dlpiece_iter dp = add_download_piece(block.piece_index); block_info* binfo = blocks_for_piece(*dp); block_info& info = binfo[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); TORRENT_ASSERT(info.piece_index == block.piece_index); if (info.state == block_info::state_finished) return; info.peer = peer; TORRENT_ASSERT(info.state == block_info::state_none); TORRENT_ASSERT(info.num_peers == 0); ++dp->finished; info.state = block_info::state_finished; // dp may be invalid after this call update_piece_state(dp); } else { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif std::vector::iterator i = find_dl_piece(p.download_queue() , block.piece_index); TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info* binfo = blocks_for_piece(*i); block_info& info = binfo[block.block_index]; TORRENT_ASSERT(info.piece_index == block.piece_index); if (info.state == block_info::state_finished) return; TORRENT_ASSERT(info.num_peers == 0); // peers may have been disconnected in between mark_as_writing // and mark_as_finished. When a peer disconnects, its m_peer_info // pointer is set to NULL. If so, preserve the previous peer // pointer, instead of forgetting who we downloaded this block from if (info.state != block_info::state_writing || peer != 0) info.peer = peer; ++i->finished; if (info.state == block_info::state_writing) { TORRENT_ASSERT(i->writing > 0); --i->writing; info.state = block_info::state_finished; } else { TORRENT_ASSERT(info.state == block_info::state_none); info.state = block_info::state_finished; } i = update_piece_state(i); if (i->finished < blocks_in_piece(i->index)) return; if (i->passed_hash_check) we_have(i->index); } #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); #endif } void piece_picker::mark_as_pad(piece_block block) { m_pad_blocks.insert(block); // if we mark and entire piece as a pad file, we need to also // consder that piece as "had" and increment some counters typedef std::set::iterator iter; iter begin = m_pad_blocks.lower_bound(piece_block(block.piece_index, 0)); int const blocks = blocks_in_piece(block.piece_index); iter end = m_pad_blocks.upper_bound(piece_block(block.piece_index, blocks)); if (std::distance(begin, end) == blocks) { // the entire piece is a pad file we_have(block.piece_index); } } /* void piece_picker::mark_as_checking(int index) { int state = m_piece_map[index].download_queue(); if (state == piece_pos::piece_open) return; std::vector::iterator i = find_dl_piece(state, index); if (i == m_downloads[state].end()) return; TORRENT_ASSERT(i->outstanding_hash_check == false); i->outstanding_hash_check = true; } void piece_picker::mark_as_done_checking(int index) { int state = m_piece_map[index].download_queue(); if (state == piece_pos::piece_open) return; std::vector::iterator i = find_dl_piece(state, index); if (i == m_downloads[state].end()) return; i->outstanding_hash_check = false; } */ void piece_picker::get_downloaders(std::vector& d, int index) const { TORRENT_ASSERT(index >= 0 && index <= int(m_piece_map.size())); d.clear(); int state = m_piece_map[index].download_queue(); const int num_blocks = blocks_in_piece(index); d.reserve(num_blocks); if (state == piece_pos::piece_open) { for (int i = 0; i < num_blocks; ++i) d.push_back(NULL); return; } std::vector::const_iterator i = find_dl_piece(state, index); TORRENT_ASSERT(i != m_downloads[state].end()); block_info const* binfo = blocks_for_piece(*i); for (int j = 0; j != num_blocks; ++j) { TORRENT_ASSERT(binfo[j].peer == 0 || binfo[j].peer->in_use); d.push_back(binfo[j].peer); } } torrent_peer* piece_picker::get_downloader(piece_block block) const { int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return 0; std::vector::const_iterator i = find_dl_piece(state , block.piece_index); TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); block_info const* binfo = blocks_for_piece(*i); TORRENT_ASSERT(binfo[block.block_index].piece_index == block.piece_index); if (binfo[block.block_index].state == block_info::state_none) return NULL; torrent_peer* peer = binfo[block.block_index].peer; TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); return peer; } // this is called when a request is rejected or when // a peer disconnects. The piece might be in any state void piece_picker::abort_download(piece_block block, torrent_peer* peer) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "abort_download( {" << block.piece_index << ", " << block.block_index << "} )" << std::endl; #endif TORRENT_ASSERT(peer == 0 || peer->in_use); TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return; std::vector::iterator i = find_dl_piece(state , block.piece_index); TORRENT_ASSERT(i != m_downloads[state].end()); block_info* binfo = blocks_for_piece(*i); block_info& info = binfo[block.block_index]; TORRENT_ASSERT(info.peer == 0 || info.peer->in_use); TORRENT_ASSERT(info.piece_index == block.piece_index); TORRENT_ASSERT(info.state != block_info::state_none); if (info.state != block_info::state_requested) return; piece_pos& p = m_piece_map[block.piece_index]; int prev_prio = p.priority(this); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(info.peers.count(peer)); info.peers.erase(peer); #endif TORRENT_ASSERT(info.num_peers > 0); if (info.num_peers > 0) --info.num_peers; if (info.peer == peer) info.peer = 0; TORRENT_ASSERT(info.peers.size() == info.num_peers); TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); // if there are other peers, leave the block requested if (info.num_peers > 0) return; // clear the downloader of this block info.peer = 0; // clear this block as being downloaded info.state = block_info::state_none; TORRENT_ASSERT(i->requested > 0); --i->requested; // if there are no other blocks in this piece // that's being downloaded, remove it from the list if (i->requested + i->finished + i->writing == 0) { TORRENT_ASSERT(prev_prio < int(m_priority_boundries.size()) || m_dirty); erase_download_piece(i); int const prio = p.priority(this); if (!m_dirty) { if (prev_prio == -1 && prio >= 0) add(block.piece_index); else if (prev_prio >= 0) update(prev_prio, p.index); } return; } i = update_piece_state(i); } int piece_picker::unverified_blocks() const { int counter = 0; for (int k = 0; k < piece_pos::num_download_categories; ++k) { for (std::vector::const_iterator i = m_downloads[k].begin(); i != m_downloads[k].end(); ++i) { counter += int(i->finished); } } return counter; } } libtorrent-rasterbar-1.1.13/src/platform_util.cpp000066400000000000000000000077371351156116000221350ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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/config.hpp" #include "libtorrent/platform_util.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #if TORRENT_USE_RLIMIT #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlong-long" #endif // __GNUC__ #include // capture this here where warnings are disabled (the macro generates warnings) const rlim_t rlimit_as = RLIMIT_AS; const rlim_t rlimit_nofile = RLIMIT_NOFILE; const rlim_t rlim_infinity = RLIM_INFINITY; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif // __GNUC__ #endif // TORRENT_USE_RLIMIT #ifdef TORRENT_BSD #include #include #endif #if defined TORRENT_WINDOWS #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { int max_open_files() { #if defined TORRENT_BUILD_SIMULATOR return 256; #elif TORRENT_USE_RLIMIT struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { if (rl.rlim_cur == rlim_infinity) return (std::numeric_limits::max)(); return rl.rlim_cur; } return 1024; #else // this seems like a reasonable limit for windows. // http://blogs.msdn.com/b/oldnewthing/archive/2007/07/18/3926581.aspx return 10000; #endif } boost::uint64_t total_physical_ram() { #if defined TORRENT_BUILD_SIMULATOR return boost::uint64_t(4) * 1024 * 1024 * 1024; #else // figure out how much physical RAM there is in // this machine. This is used for automatically // sizing the disk cache size when it's set to // automatic. boost::uint64_t ret = 0; #ifdef TORRENT_BSD #ifdef HW_MEMSIZE int mib[2] = { CTL_HW, HW_MEMSIZE }; #else // not entirely sure this sysctl supports 64 // bit return values, but it's probably better // than not building int mib[2] = { CTL_HW, HW_PHYSMEM }; #endif size_t len = sizeof(ret); if (sysctl(mib, 2, &ret, &len, NULL, 0) != 0) ret = 0; #elif defined TORRENT_WINDOWS MEMORYSTATUSEX ms; ms.dwLength = sizeof(MEMORYSTATUSEX); if (GlobalMemoryStatusEx(&ms)) ret = ms.ullTotalPhys; else ret = 0; #elif defined TORRENT_LINUX ret = sysconf(_SC_PHYS_PAGES); ret *= sysconf(_SC_PAGESIZE); #elif defined TORRENT_AMIGA ret = AvailMem(MEMF_PUBLIC); #endif #if TORRENT_USE_RLIMIT if (ret > 0) { struct rlimit r; if (getrlimit(rlimit_as, &r) == 0 && r.rlim_cur != rlim_infinity) { if (ret > r.rlim_cur) ret = r.rlim_cur; } } #endif return ret; #endif // TORRENT_BUILD_SIMULATOR } } libtorrent-rasterbar-1.1.13/src/proxy_base.cpp000066400000000000000000000035751351156116000214230ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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/proxy_base.hpp" namespace libtorrent { proxy_base::proxy_base(io_service& io_service) : m_sock(io_service) , m_port(0) , m_resolver(io_service) {} proxy_base::~proxy_base() {} bool proxy_base::handle_error(error_code const& e, boost::shared_ptr const& h) { if (!e) return false; (*h)(e); error_code ec; close(ec); return true; } } libtorrent-rasterbar-1.1.13/src/proxy_settings.cpp000066400000000000000000000057441351156116000223510ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/aux_/proxy_settings.hpp" #include "libtorrent/settings_pack.hpp" #include "libtorrent/aux_/session_settings.hpp" namespace libtorrent { namespace aux { proxy_settings::proxy_settings() : type(0) , port(0) , proxy_hostnames(true) , proxy_peer_connections(true) , proxy_tracker_connections(true) {} proxy_settings::proxy_settings(settings_pack const& sett) { hostname = sett.get_str(settings_pack::proxy_hostname); username = sett.get_str(settings_pack::proxy_username); password = sett.get_str(settings_pack::proxy_password); type = sett.get_int(settings_pack::proxy_type); port = sett.get_int(settings_pack::proxy_port); proxy_hostnames = sett.get_bool(settings_pack::proxy_hostnames); proxy_peer_connections = sett.get_bool( settings_pack::proxy_peer_connections); proxy_tracker_connections = sett.get_bool( settings_pack::proxy_tracker_connections); } proxy_settings::proxy_settings(aux::session_settings const& sett) { hostname = sett.get_str(settings_pack::proxy_hostname); username = sett.get_str(settings_pack::proxy_username); password = sett.get_str(settings_pack::proxy_password); type = sett.get_int(settings_pack::proxy_type); port = sett.get_int(settings_pack::proxy_port); proxy_hostnames = sett.get_bool(settings_pack::proxy_hostnames); proxy_peer_connections = sett.get_bool( settings_pack::proxy_peer_connections); proxy_tracker_connections = sett.get_bool( settings_pack::proxy_tracker_connections); } } // namespace aux } // namespace libtorrent libtorrent-rasterbar-1.1.13/src/puff.cpp000066400000000000000000001122541351156116000202030ustar00rootroot00000000000000/* * puff.c * Copyright (C) 2002-2013 Mark Adler * For conditions of distribution and use, see copyright notice in puff.h * version 2.3, 21 Jan 2013 * * puff.c is a simple inflate written to be an unambiguous way to specify the * deflate format. It is not written for speed but rather simplicity. As a * side benefit, this code might actually be useful when small code is more * important than speed, such as bootstrap applications. For typical deflate * data, zlib's inflate() is about four times as fast as puff(). zlib's * inflate compiles to around 20K on my machine, whereas puff.c compiles to * around 4K on my machine (a PowerPC using GNU cc). If the faster decode() * function here is used, then puff() is only twice as slow as zlib's * inflate(). * * All dynamically allocated memory comes from the stack. The stack required * is less than 2K bytes. This code is compatible with 16-bit int's and * assumes that long's are at least 32 bits. puff.c uses the short data type, * assumed to be 16 bits, for arrays in order to to conserve memory. The code * works whether integers are stored big endian or little endian. * * In the comments below are "Format notes" that describe the inflate process * and document some of the less obvious aspects of the format. This source * code is meant to supplement RFC 1951, which formally describes the deflate * format: * * http://www.zlib.org/rfc-deflate.html */ /* * Change history: * * 1.0 10 Feb 2002 - First version * 1.1 17 Feb 2002 - Clarifications of some comments and notes * - Update puff() dest and source pointers on negative * errors to facilitate debugging deflators * - Remove longest from struct huffman -- not needed * - Simplify offs[] index in construct() * - Add input size and checking, using longjmp() to * maintain easy readability * - Use short data type for large arrays * - Use pointers instead of long to specify source and * destination sizes to avoid arbitrary 4 GB limits * 1.2 17 Mar 2002 - Add faster version of decode(), doubles speed (!), * but leave simple version for readabilty * - Make sure invalid distances detected if pointers * are 16 bits * - Fix fixed codes table error * - Provide a scanning mode for determining size of * uncompressed data * 1.3 20 Mar 2002 - Go back to lengths for puff() parameters [Gailly] * - Add a puff.h file for the interface * - Add braces in puff() for else do [Gailly] * - Use indexes instead of pointers for readability * 1.4 31 Mar 2002 - Simplify construct() code set check * - Fix some comments * - Add FIXLCODES #define * 1.5 6 Apr 2002 - Minor comment fixes * 1.6 7 Aug 2002 - Minor format changes * 1.7 3 Mar 2003 - Added test code for distribution * - Added zlib-like license * 1.8 9 Jan 2004 - Added some comments on no distance codes case * 1.9 21 Feb 2008 - Fix bug on 16-bit integer architectures [Pohland] * - Catch missing end-of-block symbol error * 2.0 25 Jul 2008 - Add #define to permit distance too far back * - Add option in TEST code for puff to write the data * - Add option in TEST code to skip input bytes * - Allow TEST code to read from piped stdin * 2.1 4 Apr 2010 - Avoid variable initialization for happier compilers * - Avoid unsigned comparisons for even happier compilers * 2.2 25 Apr 2010 - Fix bug in variable initializations [Oberhumer] * - Add const where appropriate [Oberhumer] * - Split if's and ?'s for coverage testing * - Break out test code to separate file * - Move NIL to puff.h * - Allow incomplete code only if single code length is 1 * - Add full code coverage test to Makefile * 2.3 21 Jan 2013 - Check for invalid code length codes in dynamic blocks */ // this whole file is just preserved and warnings are suppressed #include "libtorrent/aux_/disable_warnings_push.hpp" #include /* for setjmp(), longjmp(), and jmp_buf */ #include /* for NULL */ #include "libtorrent/puff.hpp" /* prototype for puff() */ #define local static /* for local function definitions */ /* * Maximums for allocations and loops. It is not useful to change these -- * they are fixed by the deflate format. */ #define MAXBITS 15 /* maximum bits in a code */ #define MAXLCODES 286 /* maximum number of literal/length codes */ #define MAXDCODES 30 /* maximum number of distance codes */ #define MAXCODES (MAXLCODES+MAXDCODES) /* maximum codes lengths to read */ #define FIXLCODES 288 /* number of fixed literal/length codes */ /* input and output state */ struct state { /* output state */ unsigned char *out; /* output buffer */ unsigned long outlen; /* available space at out */ unsigned long outcnt; /* bytes written to out so far */ /* input state */ const unsigned char *in; /* input buffer */ unsigned long inlen; /* available input at in */ unsigned long incnt; /* bytes read so far */ int bitbuf; /* bit buffer */ int bitcnt; /* number of bits in bit buffer */ /* input limit error return state for bits() and decode() */ jmp_buf env; }; /* * Return need bits from the input stream. This always leaves less than * eight bits in the buffer. bits() works properly for need == 0. * * Format notes: * * - Bits are stored in bytes from the least significant bit to the most * significant bit. Therefore bits are dropped from the bottom of the bit * buffer, using shift right, and new bytes are appended to the top of the * bit buffer, using shift left. */ local int bits(struct state *s, int need) { long val; /* bit accumulator (can use up to 20 bits) */ /* load at least need bits into val */ val = s->bitbuf; while (s->bitcnt < need) { if (s->incnt == s->inlen) longjmp(s->env, 1); /* out of input */ val |= long(s->in[s->incnt++]) << s->bitcnt; /* load eight bits */ s->bitcnt += 8; } /* drop need bits and update buffer, always zero to seven bits left */ s->bitbuf = int(val >> need); s->bitcnt -= need; /* return need bits, zeroing the bits above that */ return int(val & ((1L << need) - 1)); } /* * Process a stored block. * * Format notes: * * - After the two-bit stored block type (00), the stored block length and * stored bytes are byte-aligned for fast copying. Therefore any leftover * bits in the byte that has the last bit of the type, as many as seven, are * discarded. The value of the discarded bits are not defined and should not * be checked against any expectation. * * - The second inverted copy of the stored block length does not have to be * checked, but it's probably a good idea to do so anyway. * * - A stored block can have zero length. This is sometimes used to byte-align * subsets of the compressed data for random access or partial recovery. */ local int stored(struct state *s) { unsigned len; /* length of stored block */ /* discard leftover bits from current byte (assumes s->bitcnt < 8) */ s->bitbuf = 0; s->bitcnt = 0; /* get length and check against its one's complement */ if (s->incnt + 4 > s->inlen) return 2; /* not enough input */ len = s->in[s->incnt++]; len |= s->in[s->incnt++] << 8; if (s->in[s->incnt++] != (~len & 0xff) || s->in[s->incnt++] != ((~len >> 8) & 0xff)) return -2; /* didn't match complement! */ /* copy len bytes from in to out */ if (s->incnt + len > s->inlen) return 2; /* not enough input */ if (s->out != NULL) { if (s->outcnt + len > s->outlen) return 1; /* not enough output space */ while (len--) s->out[s->outcnt++] = s->in[s->incnt++]; } else { /* just scanning */ s->outcnt += len; s->incnt += len; } /* done with a valid stored block */ return 0; } /* * Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of * each length, which for a canonical code are stepped through in order. * symbol[] are the symbol values in canonical order, where the number of * entries is the sum of the counts in count[]. The decoding process can be * seen in the function decode() below. */ struct huffman { short *count; /* number of symbols of each length */ short *symbol; /* canonically ordered symbols */ }; /* * Decode a code from the stream s using huffman table h. Return the symbol or * a negative value if there is an error. If all of the lengths are zero, i.e. * an empty code, or if the code is incomplete and an invalid code is received, * then -10 is returned after reading MAXBITS bits. * * Format notes: * * - The codes as stored in the compressed data are bit-reversed relative to * a simple integer ordering of codes of the same lengths. Hence below the * bits are pulled from the compressed data one at a time and used to * build the code value reversed from what is in the stream in order to * permit simple integer comparisons for decoding. A table-based decoding * scheme (as used in zlib) does not need to do this reversal. * * - The first code for the shortest length is all zeros. Subsequent codes of * the same length are simply integer increments of the previous code. When * moving up a length, a zero bit is appended to the code. For a complete * code, the last code of the longest length will be all ones. * * - Incomplete codes are handled by this decoder, since they are permitted * in the deflate format. See the format notes for fixed() and dynamic(). */ #ifdef SLOW local int decode(struct state *s, const struct huffman *h) { int len; /* current number of bits in code */ int code; /* len bits being decoded */ int first; /* first code of length len */ int count; /* number of codes of length len */ int index; /* index of first code of length len in symbol table */ code = first = index = 0; for (len = 1; len <= MAXBITS; len++) { code |= bits(s, 1); /* get next bit */ count = h->count[len]; if (code - count < first) /* if length len, return symbol */ return h->symbol[index + (code - first)]; index += count; /* else update for next length */ first += count; first <<= 1; code <<= 1; } return -10; /* ran out of codes */ } /* * A faster version of decode() for real applications of this code. It's not * as readable, but it makes puff() twice as fast. And it only makes the code * a few percent larger. */ #else /* !SLOW */ local int decode(struct state *s, const struct huffman *h) { int len; /* current number of bits in code */ int code; /* len bits being decoded */ int first; /* first code of length len */ int count; /* number of codes of length len */ int index; /* index of first code of length len in symbol table */ int bitbuf; /* bits from stream */ int left; /* bits left in next or left to process */ short *next; /* next number of codes */ bitbuf = s->bitbuf; left = s->bitcnt; code = first = index = 0; len = 1; next = h->count + 1; while (1) { while (left--) { code |= bitbuf & 1; bitbuf >>= 1; count = *next++; if (code - count < first) { /* if length len, return symbol */ s->bitbuf = bitbuf; s->bitcnt = (s->bitcnt - len) & 7; return h->symbol[index + (code - first)]; } index += count; /* else update for next length */ first += count; first <<= 1; code <<= 1; len++; } left = (MAXBITS+1) - len; if (left == 0) break; if (s->incnt == s->inlen) longjmp(s->env, 1); /* out of input */ bitbuf = s->in[s->incnt++]; if (left > 8) left = 8; } return -10; /* ran out of codes */ } #endif /* SLOW */ /* * Given the list of code lengths length[0..n-1] representing a canonical * Huffman code for n symbols, construct the tables required to decode those * codes. Those tables are the number of codes of each length, and the symbols * sorted by length, retaining their original order within each length. The * return value is zero for a complete code set, negative for an over- * subscribed code set, and positive for an incomplete code set. The tables * can be used if the return value is zero or positive, but they cannot be used * if the return value is negative. If the return value is zero, it is not * possible for decode() using that table to return an error--any stream of * enough bits will resolve to a symbol. If the return value is positive, then * it is possible for decode() using that table to return an error for received * codes past the end of the incomplete lengths. * * Not used by decode(), but used for error checking, h->count[0] is the number * of the n symbols not in the code. So n - h->count[0] is the number of * codes. This is useful for checking for incomplete codes that have more than * one symbol, which is an error in a dynamic block. * * Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS * This is assured by the construction of the length arrays in dynamic() and * fixed() and is not verified by construct(). * * Format notes: * * - Permitted and expected examples of incomplete codes are one of the fixed * codes and any code with a single symbol which in deflate is coded as one * bit instead of zero bits. See the format notes for fixed() and dynamic(). * * - Within a given code length, the symbols are kept in ascending order for * the code bits definition. */ local int construct(struct huffman *h, const short *length, int n) { int symbol; /* current symbol when stepping through length[] */ int len; /* current length when stepping through h->count[] */ int left; /* number of possible codes left of current length */ short offs[MAXBITS+1]; /* offsets in symbol table for each length */ /* count number of codes of each length */ for (len = 0; len <= MAXBITS; len++) h->count[len] = 0; for (symbol = 0; symbol < n; symbol++) (h->count[length[symbol]])++; /* assumes lengths are within bounds */ if (h->count[0] == n) /* no codes! */ return 0; /* complete, but decode() will fail */ /* check for an over-subscribed or incomplete set of lengths */ left = 1; /* one possible code of zero length */ for (len = 1; len <= MAXBITS; len++) { left <<= 1; /* one more bit, double codes left */ left -= h->count[len]; /* deduct count from possible codes */ if (left < 0) return left; /* over-subscribed--return negative */ } /* left > 0 means incomplete */ /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h->count[len]; /* * put symbols in table sorted by length, by symbol order within each * length */ for (symbol = 0; symbol < n; symbol++) if (length[symbol] != 0) h->symbol[offs[length[symbol]]++] = symbol; /* return zero for complete set, positive for incomplete set */ return left; } /* * Decode literal/length and distance codes until an end-of-block code. * * Format notes: * * - Compressed data that is after the block type if fixed or after the code * description if dynamic is a combination of literals and length/distance * pairs terminated by and end-of-block code. Literals are simply Huffman * coded bytes. A length/distance pair is a coded length followed by a * coded distance to represent a string that occurs earlier in the * uncompressed data that occurs again at the current location. * * - Literals, lengths, and the end-of-block code are combined into a single * code of up to 286 symbols. They are 256 literals (0..255), 29 length * symbols (257..285), and the end-of-block symbol (256). * * - There are 256 possible lengths (3..258), and so 29 symbols are not enough * to represent all of those. Lengths 3..10 and 258 are in fact represented * by just a length symbol. Lengths 11..257 are represented as a symbol and * some number of extra bits that are added as an integer to the base length * of the length symbol. The number of extra bits is determined by the base * length symbol. These are in the static arrays below, lens[] for the base * lengths and lext[] for the corresponding number of extra bits. * * - The reason that 258 gets its own symbol is that the longest length is used * often in highly redundant files. Note that 258 can also be coded as the * base value 227 plus the maximum extra value of 31. While a good deflate * should never do this, it is not an error, and should be decoded properly. * * - If a length is decoded, including its extra bits if any, then it is * followed a distance code. There are up to 30 distance symbols. Again * there are many more possible distances (1..32768), so extra bits are added * to a base value represented by the symbol. The distances 1..4 get their * own symbol, but the rest require extra bits. The base distances and * corresponding number of extra bits are below in the static arrays dist[] * and dext[]. * * - Literal bytes are simply written to the output. A length/distance pair is * an instruction to copy previously uncompressed bytes to the output. The * copy is from distance bytes back in the output stream, copying for length * bytes. * * - Distances pointing before the beginning of the output data are not * permitted. * * - Overlapped copies, where the length is greater than the distance, are * allowed and common. For example, a distance of one and a length of 258 * simply copies the last byte 258 times. A distance of four and a length of * twelve copies the last four bytes three times. A simple forward copy * ignoring whether the length is greater than the distance or not implements * this correctly. You should not use memcpy() since its behavior is not * defined for overlapped arrays. You should not use memmove() or bcopy() * since though their behavior -is- defined for overlapping arrays, it is * defined to do the wrong thing in this case. */ local int codes(struct state *s, const struct huffman *lencode, const struct huffman *distcode) { int symbol; /* decoded symbol */ int len; /* length for copy */ unsigned dist; /* distance for copy */ static const short lens[29] = { /* Size base for length codes 257..285 */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}; static const short lext[29] = { /* Extra bits for length codes 257..285 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; static const short dists[30] = { /* Offset base for distance codes 0..29 */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; static const short dext[30] = { /* Extra bits for distance codes 0..29 */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; /* decode literals and length/distance pairs */ do { symbol = decode(s, lencode); if (symbol < 0) return symbol; /* invalid symbol */ if (symbol < 256) { /* literal: symbol is the byte */ /* write out the literal */ if (s->out != NULL) { if (s->outcnt == s->outlen) return 1; s->out[s->outcnt] = symbol; } s->outcnt++; } else if (symbol > 256) { /* length */ /* get and compute length */ symbol -= 257; if (symbol >= 29) return -10; /* invalid fixed code */ len = lens[symbol] + bits(s, lext[symbol]); /* get and check distance */ symbol = decode(s, distcode); if (symbol < 0) return symbol; /* invalid symbol */ dist = dists[symbol] + bits(s, dext[symbol]); #ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR if (dist > s->outcnt) return -11; /* distance too far back */ #endif /* copy length bytes from distance bytes back */ if (s->out != NULL) { if (s->outcnt + len > s->outlen) return 1; while (len--) { s->out[s->outcnt] = #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR dist > s->outcnt ? 0 : #endif s->out[s->outcnt - dist]; s->outcnt++; } } else s->outcnt += len; } } while (symbol != 256); /* end of block symbol */ /* done with a valid fixed or dynamic block */ return 0; } /* * Process a fixed codes block. * * Format notes: * * - This block type can be useful for compressing small amounts of data for * which the size of the code descriptions in a dynamic block exceeds the * benefit of custom codes for that block. For fixed codes, no bits are * spent on code descriptions. Instead the code lengths for literal/length * codes and distance codes are fixed. The specific lengths for each symbol * can be seen in the "for" loops below. * * - The literal/length code is complete, but has two symbols that are invalid * and should result in an error if received. This cannot be implemented * simply as an incomplete code since those two symbols are in the "middle" * of the code. They are eight bits long and the longest literal/length\ * code is nine bits. Therefore the code must be constructed with those * symbols, and the invalid symbols must be detected after decoding. * * - The fixed distance codes also have two invalid symbols that should result * in an error if received. Since all of the distance codes are the same * length, this can be implemented as an incomplete code. Then the invalid * codes are detected while decoding. */ local int fixed(struct state *s) { static int virgin = 1; static short lencnt[MAXBITS+1], lensym[FIXLCODES]; static short distcnt[MAXBITS+1], distsym[MAXDCODES]; static struct huffman lencode, distcode; /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { int symbol; short lengths[FIXLCODES]; /* construct lencode and distcode */ lencode.count = lencnt; lencode.symbol = lensym; distcode.count = distcnt; distcode.symbol = distsym; /* literal/length table */ for (symbol = 0; symbol < 144; symbol++) lengths[symbol] = 8; for (; symbol < 256; symbol++) lengths[symbol] = 9; for (; symbol < 280; symbol++) lengths[symbol] = 7; for (; symbol < FIXLCODES; symbol++) lengths[symbol] = 8; construct(&lencode, lengths, FIXLCODES); /* distance table */ for (symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5; construct(&distcode, lengths, MAXDCODES); /* do this just once */ virgin = 0; } /* decode data until end-of-block code */ return codes(s, &lencode, &distcode); } /* * Process a dynamic codes block. * * Format notes: * * - A dynamic block starts with a description of the literal/length and * distance codes for that block. New dynamic blocks allow the compressor to * rapidly adapt to changing data with new codes optimized for that data. * * - The codes used by the deflate format are "canonical", which means that * the actual bits of the codes are generated in an unambiguous way simply * from the number of bits in each code. Therefore the code descriptions * are simply a list of code lengths for each symbol. * * - The code lengths are stored in order for the symbols, so lengths are * provided for each of the literal/length symbols, and for each of the * distance symbols. * * - If a symbol is not used in the block, this is represented by a zero as * as the code length. This does not mean a zero-length code, but rather * that no code should be created for this symbol. There is no way in the * deflate format to represent a zero-length code. * * - The maximum number of bits in a code is 15, so the possible lengths for * any code are 1..15. * * - The fact that a length of zero is not permitted for a code has an * interesting consequence. Normally if only one symbol is used for a given * code, then in fact that code could be represented with zero bits. However * in deflate, that code has to be at least one bit. So for example, if * only a single distance base symbol appears in a block, then it will be * represented by a single code of length one, in particular one 0 bit. This * is an incomplete code, since if a 1 bit is received, it has no meaning, * and should result in an error. So incomplete distance codes of one symbol * should be permitted, and the receipt of invalid codes should be handled. * * - It is also possible to have a single literal/length code, but that code * must be the end-of-block code, since every dynamic block has one. This * is not the most efficient way to create an empty block (an empty fixed * block is fewer bits), but it is allowed by the format. So incomplete * literal/length codes of one symbol should also be permitted. * * - If there are only literal codes and no lengths, then there are no distance * codes. This is represented by one distance code with zero bits. * * - The list of up to 286 length/literal lengths and up to 30 distance lengths * are themselves compressed using Huffman codes and run-length encoding. In * the list of code lengths, a 0 symbol means no code, a 1..15 symbol means * that length, and the symbols 16, 17, and 18 are run-length instructions. * Each of 16, 17, and 18 are follwed by extra bits to define the length of * the run. 16 copies the last length 3 to 6 times. 17 represents 3 to 10 * zero lengths, and 18 represents 11 to 138 zero lengths. Unused symbols * are common, hence the special coding for zero lengths. * * - The symbols for 0..18 are Huffman coded, and so that code must be * described first. This is simply a sequence of up to 19 three-bit values * representing no code (0) or the code length for that symbol (1..7). * * - A dynamic block starts with three fixed-size counts from which is computed * the number of literal/length code lengths, the number of distance code * lengths, and the number of code length code lengths (ok, you come up with * a better name!) in the code descriptions. For the literal/length and * distance codes, lengths after those provided are considered zero, i.e. no * code. The code length code lengths are received in a permuted order (see * the order[] array below) to make a short code length code length list more * likely. As it turns out, very short and very long codes are less likely * to be seen in a dynamic code description, hence what may appear initially * to be a peculiar ordering. * * - Given the number of literal/length code lengths (nlen) and distance code * lengths (ndist), then they are treated as one long list of nlen + ndist * code lengths. Therefore run-length coding can and often does cross the * boundary between the two sets of lengths. * * - So to summarize, the code description at the start of a dynamic block is * three counts for the number of code lengths for the literal/length codes, * the distance codes, and the code length codes. This is followed by the * code length code lengths, three bits each. This is used to construct the * code length code which is used to read the remainder of the lengths. Then * the literal/length code lengths and distance lengths are read as a single * set of lengths using the code length codes. Codes are constructed from * the resulting two sets of lengths, and then finally you can start * decoding actual compressed data in the block. * * - For reference, a "typical" size for the code description in a dynamic * block is around 80 bytes. */ local int dynamic(struct state *s) { int nlen, ndist, ncode; /* number of lengths in descriptor */ int index; /* index of lengths[] */ int err; /* construct() return value */ short lengths[MAXCODES]; /* descriptor code lengths */ short lencnt[MAXBITS+1], lensym[MAXLCODES]; /* lencode memory */ short distcnt[MAXBITS+1], distsym[MAXDCODES]; /* distcode memory */ struct huffman lencode, distcode; /* length and distance codes */ static const short order[19] = /* permutation of code length codes */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* construct lencode and distcode */ lencode.count = lencnt; lencode.symbol = lensym; distcode.count = distcnt; distcode.symbol = distsym; /* get number of lengths in each table, check lengths */ nlen = bits(s, 5) + 257; ndist = bits(s, 5) + 1; ncode = bits(s, 4) + 4; if (nlen > MAXLCODES || ndist > MAXDCODES) return -3; /* bad counts */ /* read code length code lengths (really), missing lengths are zero */ for (index = 0; index < ncode; index++) lengths[order[index]] = bits(s, 3); for (; index < 19; index++) lengths[order[index]] = 0; /* build huffman table for code lengths codes (use lencode temporarily) */ err = construct(&lencode, lengths, 19); if (err != 0) /* require complete code set here */ return -4; /* read length/literal and distance code length tables */ index = 0; while (index < nlen + ndist) { int symbol; /* decoded value */ int len; /* last length to repeat */ symbol = decode(s, &lencode); if (symbol < 0) return symbol; /* invalid symbol */ if (symbol < 16) /* length in 0..15 */ lengths[index++] = symbol; else { /* repeat instruction */ len = 0; /* assume repeating zeros */ if (symbol == 16) { /* repeat last length 3..6 times */ if (index == 0) return -5; /* no last length! */ len = lengths[index - 1]; /* last length */ symbol = 3 + bits(s, 2); } else if (symbol == 17) /* repeat zero 3..10 times */ symbol = 3 + bits(s, 3); else /* == 18, repeat zero 11..138 times */ symbol = 11 + bits(s, 7); if (index + symbol > nlen + ndist) return -6; /* too many lengths! */ while (symbol--) /* repeat last or zero symbol times */ lengths[index++] = len; } } /* check for end-of-block code -- there better be one! */ if (lengths[256] == 0) return -9; /* build huffman table for literal/length codes */ err = construct(&lencode, lengths, nlen); if (err && (err < 0 || nlen != lencode.count[0] + lencode.count[1])) return -7; /* incomplete code ok only for single length 1 code */ /* build huffman table for distance codes */ err = construct(&distcode, lengths + nlen, ndist); if (err && (err < 0 || ndist != distcode.count[0] + distcode.count[1])) return -8; /* incomplete code ok only for single length 1 code */ /* decode data until end-of-block code */ return codes(s, &lencode, &distcode); } /* * Inflate source to dest. On return, destlen and sourcelen are updated to the * size of the uncompressed data and the size of the deflate data respectively. * On success, the return value of puff() is zero. If there is an error in the * source data, i.e. it is not in the deflate format, then a negative value is * returned. If there is not enough input available or there is not enough * output space, then a positive error is returned. In that case, destlen and * sourcelen are not updated to facilitate retrying from the beginning with the * provision of more input data or more output space. In the case of invalid * inflate data (a negative error), the dest and source pointers are updated to * facilitate the debugging of deflators. * * puff() also has a mode to determine the size of the uncompressed output with * no output written. For this dest must be (unsigned char *)0. In this case, * the input value of *destlen is ignored, and on return *destlen is set to the * size of the uncompressed output. * * The return codes are: * * 2: available inflate data did not terminate * 1: output space exhausted before completing inflate * 0: successful inflate * -1: invalid block type (type == 3) * -2: stored block length did not match one's complement * -3: dynamic block code description: too many length or distance codes * -4: dynamic block code description: code lengths codes incomplete * -5: dynamic block code description: repeat lengths with no first length * -6: dynamic block code description: repeat more than specified lengths * -7: dynamic block code description: invalid literal/length code lengths * -8: dynamic block code description: invalid distance code lengths * -9: dynamic block code description: missing end-of-block code * -10: invalid literal/length or distance code in fixed or dynamic block * -11: distance is too far back in fixed or dynamic block * * Format notes: * * - Three bits are read for each block to determine the kind of block and * whether or not it is the last block. Then the block is decoded and the * process repeated if it was not the last block. * * - The leftover bits in the last byte of the deflate data after the last * block (if it was a fixed or dynamic block) are undefined and have no * expected values to check. */ int puff(unsigned char *dest, /* pointer to destination pointer */ unsigned long *destlen, /* amount of output space */ const unsigned char *source, /* pointer to source data pointer */ unsigned long *sourcelen) /* amount of input available */ { struct state s; /* input/output state */ int last, type; /* block information */ int err; /* return value */ /* initialize output state */ s.out = dest; s.outlen = *destlen; /* ignored if dest is NIL */ s.outcnt = 0; /* initialize input state */ s.in = source; s.inlen = *sourcelen; s.incnt = 0; s.bitbuf = 0; s.bitcnt = 0; /* return if bits() or decode() tries to read past available input */ if (setjmp(s.env) != 0) /* if came back here via longjmp() */ err = 2; /* then skip do-loop, return error */ else { /* process blocks until last block or error */ do { last = bits(&s, 1); /* one if last block */ type = bits(&s, 2); /* block type 0..3 */ err = type == 0 ? stored(&s) : (type == 1 ? fixed(&s) : (type == 2 ? dynamic(&s) : -1)); /* type == 3, invalid */ if (err != 0) break; /* return with error */ } while (!last); } /* update the lengths and return */ if (err <= 0) { *destlen = s.outcnt; *sourcelen = s.incnt; } return err; } libtorrent-rasterbar-1.1.13/src/random.cpp000066400000000000000000000064311351156116000205220ustar00rootroot00000000000000/* Copyright (c) 2011-2018, 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/config.hpp" #include "libtorrent/random.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/thread.hpp" #ifdef BOOST_NO_CXX11_HDR_RANDOM #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #else #include #endif namespace libtorrent { #ifdef BOOST_NO_CXX11_HDR_RANDOM using boost::random::random_device; using boost::random::mt19937; using boost::random::uniform_int_distribution; #else using std::random_device; using std::mt19937; using std::uniform_int_distribution; #endif #ifdef TORRENT_BUILD_SIMULATOR boost::uint32_t random() { // make sure random numbers are deterministic. Seed with a fixed number static mt19937 random_engine(4040); return uniform_int_distribution(0, UINT_MAX)(random_engine); } #else namespace { #if !TORRENT_THREADSAFE_STATIC // because local statics are not atomic pre c++11 // do it manually, probably at a higher cost random_device* dev = NULL; mt19937* rnd = NULL; #endif mutex random_device_mutex; } boost::uint32_t random() { // TODO: use a thread local mt19937 instance instead! mutex::scoped_lock l(random_device_mutex); #if TORRENT_THREADSAFE_STATIC static random_device dev; static mt19937 random_engine(dev()); return uniform_int_distribution(0, UINT_MAX)(random_engine); #else if (dev == NULL) { dev = new random_device(); rnd = new mt19937((*dev)()); } return uniform_int_distribution(0, UINT_MAX)(*rnd); #endif } #endif // TORRENT_BUILD_SIMULATOR boost::uint32_t randint(int i) { TORRENT_ASSERT(i > 1); return random() % i; } } libtorrent-rasterbar-1.1.13/src/receive_buffer.cpp000066400000000000000000000322141351156116000222130ustar00rootroot00000000000000/* Copyright (c) 2014-2018, Arvid Norberg, Steven Siloti 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 namespace libtorrent { namespace { int round_up8(int v) { return ((v & 7) == 0) ? v : v + (8 - (v & 7)); } } int receive_buffer::max_receive() { int max = packet_bytes_remaining(); if (m_recv_pos >= m_soft_packet_size) m_soft_packet_size = 0; if (m_soft_packet_size && max > m_soft_packet_size - m_recv_pos) max = m_soft_packet_size - m_recv_pos; return max; } boost::asio::mutable_buffer receive_buffer::reserve(int size) { TORRENT_ASSERT(size > 0); TORRENT_ASSERT(!m_disk_recv_buffer); // this is unintuitive, but we used to use m_recv_pos in this function when // we should have used m_recv_end. perhaps they always happen to be equal TORRENT_ASSERT(m_recv_pos == m_recv_end); m_recv_buffer.resize(m_recv_end + size); return boost::asio::buffer(&m_recv_buffer[0] + m_recv_end, size); } int receive_buffer::reserve(boost::array& vec, int size) { TORRENT_ASSERT(size > 0); TORRENT_ASSERT(m_recv_pos >= 0); TORRENT_ASSERT(m_packet_size > 0); // normalize() must be called before receiving more data TORRENT_ASSERT(m_recv_start == 0); // this is unintuitive, but we used to use m_recv_pos in this function when // we should have used m_recv_end. perhaps they always happen to be equal TORRENT_ASSERT(m_recv_pos == m_recv_end); int num_bufs; int const regular_buf_size = regular_buffer_size(); if (int(m_recv_buffer.size()) < regular_buf_size) m_recv_buffer.resize(round_up8(regular_buf_size)); if (!m_disk_recv_buffer || regular_buf_size >= m_recv_end + size) { // only receive into regular buffer TORRENT_ASSERT(m_recv_end + size <= int(m_recv_buffer.size())); vec[0] = boost::asio::buffer(&m_recv_buffer[0] + m_recv_end, size); TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) > 0); num_bufs = 1; } else if (m_recv_end >= regular_buf_size) { // only receive into disk buffer TORRENT_ASSERT(m_recv_end - regular_buf_size >= 0); TORRENT_ASSERT(m_recv_end - regular_buf_size + size <= m_disk_recv_buffer_size); vec[0] = boost::asio::buffer(m_disk_recv_buffer.get() + m_recv_end - regular_buf_size, size); TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) > 0); num_bufs = 1; } else { // receive into both regular and disk buffer TORRENT_ASSERT(size + m_recv_end > regular_buf_size); TORRENT_ASSERT(m_recv_end < regular_buf_size); TORRENT_ASSERT(size - regular_buf_size + m_recv_end <= m_disk_recv_buffer_size); vec[0] = boost::asio::buffer(&m_recv_buffer[0] + m_recv_end , regular_buf_size - m_recv_end); vec[1] = boost::asio::buffer(m_disk_recv_buffer.get() , size - regular_buf_size + m_recv_end); TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) + boost::asio::buffer_size(vec[1])> 0); num_bufs = 2; } return num_bufs; } int receive_buffer::advance_pos(int bytes) { int const packet_size = m_soft_packet_size ? m_soft_packet_size : m_packet_size; int const limit = packet_size > m_recv_pos ? packet_size - m_recv_pos : packet_size; int const sub_transferred = (std::min)(bytes, limit); m_recv_pos += sub_transferred; if (m_recv_pos >= m_soft_packet_size) m_soft_packet_size = 0; return sub_transferred; } void receive_buffer::clamp_size() { if (m_recv_pos == 0 && (m_recv_buffer.capacity() - m_packet_size) > 128) { // round up to an even 8 bytes since that's the RC4 blocksize buffer(round_up8(m_packet_size)).swap(m_recv_buffer); } } // size = the packet size to remove from the receive buffer // packet_size = the next packet size to receive in the buffer // offset = the offset into the receive buffer where to remove `size` bytes void receive_buffer::cut(int size, int packet_size, int offset) { TORRENT_ASSERT(packet_size > 0); TORRENT_ASSERT(int(m_recv_buffer.size()) >= size); TORRENT_ASSERT(int(m_recv_buffer.size()) >= m_recv_pos); TORRENT_ASSERT(m_recv_pos >= size + offset); TORRENT_ASSERT(offset >= 0); TORRENT_ASSERT(int(m_recv_buffer.size()) >= m_recv_end); TORRENT_ASSERT(m_recv_start <= m_recv_end); TORRENT_ASSERT(size >= 0); if (offset > 0) { TORRENT_ASSERT(m_recv_start - size <= m_recv_end); if (size > 0) std::memmove(&m_recv_buffer[0] + m_recv_start + offset , &m_recv_buffer[0] + m_recv_start + offset + size , m_recv_end - m_recv_start - size - offset); m_recv_pos -= size; m_recv_end -= size; #ifdef TORRENT_DEBUG std::fill(m_recv_buffer.begin() + m_recv_end, m_recv_buffer.end(), 0xcc); #endif } else { TORRENT_ASSERT(m_recv_start + size <= m_recv_end); m_recv_start += size; m_recv_pos -= size; } m_packet_size = packet_size; } buffer::const_interval receive_buffer::get() const { if (m_recv_buffer.empty()) { TORRENT_ASSERT(m_recv_pos == 0); return buffer::interval(0,0); } int rcv_pos = (std::min)(m_recv_pos, int(m_recv_buffer.size()) - m_recv_start); return buffer::const_interval(&m_recv_buffer[0] + m_recv_start , &m_recv_buffer[0] + m_recv_start + rcv_pos); } #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) buffer::interval receive_buffer::mutable_buffer() { if (m_recv_buffer.empty()) { TORRENT_ASSERT(m_recv_pos == 0); return buffer::interval(0,0); } TORRENT_ASSERT(!m_disk_recv_buffer); TORRENT_ASSERT(m_disk_recv_buffer_size == 0); int rcv_pos = (std::min)(m_recv_pos, int(m_recv_buffer.size())); return buffer::interval(&m_recv_buffer[0] + m_recv_start , &m_recv_buffer[0] + m_recv_start + rcv_pos); } // TODO: 2 should this take a boost::array<..., 2> instead? it could return the // number of buffers added, just like reserve. void receive_buffer::mutable_buffers(std::vector& vec, int const bytes) { namespace asio = boost::asio; // bytes is the number of bytes we just received, and m_recv_pos has // already been adjusted for these bytes. The receive pos immediately // before we received these bytes was (m_recv_pos - bytes) int const last_recv_pos = m_recv_pos - bytes; TORRENT_ASSERT(bytes <= m_recv_pos); // the number of bytes in the current packet that are being received into a // regular receive buffer (as opposed to a disk cache buffer) int const regular_buf_size = regular_buffer_size(); TORRENT_ASSERT(regular_buf_size >= 0); if (!m_disk_recv_buffer || regular_buf_size >= m_recv_pos) { // we just received into a regular disk buffer vec.push_back(asio::mutable_buffer(&m_recv_buffer[0] + m_recv_start + last_recv_pos, bytes)); } else if (last_recv_pos >= regular_buf_size) { // we only received into a disk buffer vec.push_back(asio::mutable_buffer(m_disk_recv_buffer.get() + last_recv_pos - regular_buf_size, bytes)); } else { // we received into a regular and a disk buffer TORRENT_ASSERT(last_recv_pos < regular_buf_size); TORRENT_ASSERT(m_recv_pos > regular_buf_size); vec.push_back(asio::mutable_buffer(&m_recv_buffer[0] + m_recv_start + last_recv_pos , regular_buf_size - last_recv_pos)); vec.push_back(asio::mutable_buffer(m_disk_recv_buffer.get() , m_recv_pos - regular_buf_size)); } #if TORRENT_USE_ASSERTS int vec_bytes = 0; for (std::vector::iterator i = vec.begin(); i != vec.end(); ++i) vec_bytes += boost::asio::buffer_size(*i); TORRENT_ASSERT(vec_bytes == bytes); #endif } #endif void receive_buffer::assign_disk_buffer(char* buffer, int size) { TORRENT_ASSERT(m_packet_size > 0); assert_no_disk_buffer(); m_disk_recv_buffer.reset(buffer); if (m_disk_recv_buffer) m_disk_recv_buffer_size = size; } char* receive_buffer::release_disk_buffer() { if (!m_disk_recv_buffer) return 0; TORRENT_ASSERT(m_disk_recv_buffer_size <= m_recv_end); TORRENT_ASSERT(m_recv_start <= m_recv_end - m_disk_recv_buffer_size); m_recv_end -= m_disk_recv_buffer_size; m_disk_recv_buffer_size = 0; return m_disk_recv_buffer.release(); } // the purpose of this function is to free up and cut off all messages // in the receive buffer that have been parsed and processed. void receive_buffer::normalize() { TORRENT_ASSERT(m_recv_end >= m_recv_start); if (m_recv_start == 0) return; if (m_recv_end > m_recv_start) std::memmove(&m_recv_buffer[0], &m_recv_buffer[0] + m_recv_start, m_recv_end - m_recv_start); m_recv_end -= m_recv_start; m_recv_start = 0; #ifdef TORRENT_DEBUG std::fill(m_recv_buffer.begin() + m_recv_end, m_recv_buffer.end(), 0xcc); #endif } void receive_buffer::reset(int packet_size) { TORRENT_ASSERT(m_recv_buffer.size() >= m_recv_end); TORRENT_ASSERT(packet_size > 0); if (m_recv_end > m_packet_size) { cut(m_packet_size, packet_size); return; } m_recv_pos = 0; m_recv_start = 0; m_recv_end = 0; m_packet_size = packet_size; } #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) bool crypto_receive_buffer::packet_finished() const { if (m_recv_pos == INT_MAX) return m_connection_buffer.packet_finished(); else return m_packet_size <= m_recv_pos; } int crypto_receive_buffer::packet_size() const { if (m_recv_pos == INT_MAX) return m_connection_buffer.packet_size(); else return m_packet_size; } int crypto_receive_buffer::pos() const { if (m_recv_pos == INT_MAX) return m_connection_buffer.pos(); else return m_recv_pos; } void crypto_receive_buffer::cut(int size, int packet_size, int offset) { if (m_recv_pos != INT_MAX) { TORRENT_ASSERT(size <= m_recv_pos); m_packet_size = packet_size; packet_size = m_connection_buffer.packet_size() - size; m_recv_pos -= size; } m_connection_buffer.cut(size, packet_size, offset); } void crypto_receive_buffer::reset(int packet_size) { if (m_recv_pos != INT_MAX) { if (m_connection_buffer.m_recv_end > m_packet_size) { cut(m_packet_size, packet_size); return; } m_packet_size = packet_size; packet_size = m_connection_buffer.packet_size() - m_recv_pos; m_recv_pos = 0; } m_connection_buffer.reset(packet_size); } void crypto_receive_buffer::crypto_reset(int packet_size) { TORRENT_ASSERT(packet_finished()); TORRENT_ASSERT(crypto_packet_finished()); TORRENT_ASSERT(m_recv_pos == INT_MAX || m_recv_pos == m_connection_buffer.pos()); TORRENT_ASSERT(m_recv_pos == INT_MAX || m_connection_buffer.pos_at_end()); TORRENT_ASSERT(!m_connection_buffer.has_disk_buffer()); if (packet_size == 0) { if (m_recv_pos != INT_MAX) m_connection_buffer.cut(0, m_packet_size); m_recv_pos = INT_MAX; } else { if (m_recv_pos == INT_MAX) m_packet_size = m_connection_buffer.packet_size(); m_recv_pos = m_connection_buffer.pos(); TORRENT_ASSERT(m_recv_pos >= 0); m_connection_buffer.cut(0, m_recv_pos + packet_size); } } void crypto_receive_buffer::set_soft_packet_size(int size) { if (m_recv_pos == INT_MAX) m_connection_buffer.set_soft_packet_size(size); else m_soft_packet_size = size; } int crypto_receive_buffer::advance_pos(int bytes) { if (m_recv_pos == INT_MAX) return bytes; int packet_size = m_soft_packet_size ? m_soft_packet_size : m_packet_size; int limit = packet_size > m_recv_pos ? packet_size - m_recv_pos : packet_size; int sub_transferred = (std::min)(bytes, limit); m_recv_pos += sub_transferred; m_connection_buffer.cut(0, m_connection_buffer.packet_size() + sub_transferred); if (m_recv_pos >= m_soft_packet_size) m_soft_packet_size = 0; return sub_transferred; } buffer::const_interval crypto_receive_buffer::get() const { buffer::const_interval recv_buffer = m_connection_buffer.get(); if (m_recv_pos < m_connection_buffer.pos()) recv_buffer.end = recv_buffer.begin + m_recv_pos; return recv_buffer; } void crypto_receive_buffer::mutable_buffers( std::vector& vec , std::size_t bytes_transfered) { int pending_decryption = bytes_transfered; if (m_recv_pos != INT_MAX) { pending_decryption = m_connection_buffer.packet_size() - m_recv_pos; } m_connection_buffer.mutable_buffers(vec, pending_decryption); } #endif // TORRENT_DISABLE_ENCRYPTION } // namespace libtorrent libtorrent-rasterbar-1.1.13/src/request_blocks.cpp000066400000000000000000000253171351156116000222730ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/bitfield.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/peer_info.hpp" // for peer_info flags #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/request_blocks.hpp" #include "libtorrent/alert_manager.hpp" #include namespace libtorrent { int source_rank(int source_bitmask) { int ret = 0; if (source_bitmask & peer_info::tracker) ret |= 1 << 5; if (source_bitmask & peer_info::lsd) ret |= 1 << 4; if (source_bitmask & peer_info::dht) ret |= 1 << 3; if (source_bitmask & peer_info::pex) ret |= 1 << 2; return ret; } // 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. // returns false if the function is aborted by an early-exit // condition. bool request_a_block(torrent& t, peer_connection& c) { if (t.is_seed()) return false; if (c.no_download()) return false; if (t.upload_mode()) return false; if (c.is_disconnecting()) return false; // don't request pieces before we have the metadata if (!t.valid_metadata()) return false; // don't request pieces before the peer is properly // initialized after we have the metadata if (!t.are_files_checked()) return false; // we don't want to request more blocks while trying to gracefully pause if (t.graceful_pause()) return false; TORRENT_ASSERT(c.peer_info_struct() != 0 || c.type() != peer_connection::bittorrent_connection); bool time_critical_mode = t.num_time_critical_pieces() > 0; int desired_queue_size = c.desired_queue_size(); // in time critical mode, only have 1 outstanding request at a time // via normal requests if (time_critical_mode) desired_queue_size = (std::min)(1, desired_queue_size); int num_requests = desired_queue_size - int(c.download_queue().size()) - int(c.request_queue().size()); #ifndef TORRENT_DISABLE_LOGGING c.peer_log(peer_log_alert::info, "PIECE_PICKER" , "dlq: %d rqq: %d target: %d req: %d engame: %d" , int(c.download_queue().size()), int(c.request_queue().size()) , c.desired_queue_size(), num_requests, c.endgame()); #endif TORRENT_ASSERT(c.desired_queue_size() > 0); // if our request queue is already full, we // don't have to make any new requests yet if (num_requests <= 0) return false; t.need_picker(); piece_picker& p = t.picker(); std::vector interesting_pieces; interesting_pieces.reserve(100); int prefer_contiguous_blocks = c.prefer_contiguous_blocks(); if (prefer_contiguous_blocks == 0 && !time_critical_mode && t.settings().get_int(settings_pack::whole_pieces_threshold) > 0) { int const blocks_per_piece = t.torrent_file().piece_length() / t.block_size(); prefer_contiguous_blocks = (c.statistics().download_payload_rate() > t.torrent_file().piece_length() / t.settings().get_int(settings_pack::whole_pieces_threshold)) ? blocks_per_piece : 0; } // 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. #ifdef TORRENT_DEBUG error_code ec; TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec); #endif aux::session_interface& ses = t.session(); std::vector const& dq = c.download_queue(); std::vector const& rq = c.request_queue(); std::vector const& suggested = c.suggested_pieces(); bitfield const* bits = &c.get_bitfield(); bitfield fast_mask; if (c.has_peer_choked()) { // if we are choked we can only pick pieces from the // allowed fast set. The allowed fast set is sorted // in ascending priority order std::vector const& allowed_fast = c.allowed_fast(); // build a bitmask with only the allowed pieces in it fast_mask.resize(c.get_bitfield().size(), false); for (std::vector::const_iterator i = allowed_fast.begin() , end(allowed_fast.end()); i != end; ++i) if ((*bits)[*i]) fast_mask.set_bit(*i); bits = &fast_mask; } // 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. boost::uint32_t flags = p.pick_pieces(*bits, interesting_pieces , num_requests, prefer_contiguous_blocks, c.peer_info_struct() , c.picker_options(), suggested, t.num_peers() , ses.stats_counters()); #ifndef TORRENT_DISABLE_LOGGING if (t.alerts().should_post() && !interesting_pieces.empty()) { t.alerts().emplace_alert(t.get_handle(), c.remote() , c.pid(), flags, &interesting_pieces[0], int(interesting_pieces.size())); } c.peer_log(peer_log_alert::info, "PIECE_PICKER" , "prefer_contiguous: %d picked: %d" , prefer_contiguous_blocks, int(interesting_pieces.size())); #else TORRENT_UNUSED(flags); #endif // if the number of pieces we have + the number of pieces // we're requesting from is less than the number of pieces // in the torrent, there are still some unrequested pieces // and we're not strictly speaking in end-game mode yet // also, if we already have at least one outstanding // request, we shouldn't pick any busy pieces either // in time critical mode, it's OK to request busy blocks bool dont_pick_busy_blocks = ((ses.settings().get_bool(settings_pack::strict_end_game_mode) && p.get_download_queue_size() < p.num_want_left()) || dq.size() + rq.size() > 0) && !time_critical_mode; // this is filled with an interesting piece // that some other peer is currently downloading piece_block busy_block = piece_block::invalid; for (std::vector::iterator i = interesting_pieces.begin(); i != interesting_pieces.end(); ++i) { if (prefer_contiguous_blocks == 0 && num_requests <= 0) break; if (time_critical_mode && p.piece_priority(i->piece_index) != 7) { // assume the subsequent pieces are not prio 7 and // be done break; } int num_block_requests = p.num_peers(*i); if (num_block_requests > 0) { // have we picked enough pieces? if (num_requests <= 0) break; // this block is busy. This means all the following blocks // in the interesting_pieces list are busy as well, we might // as well just exit the loop if (dont_pick_busy_blocks) break; TORRENT_ASSERT(p.num_peers(*i) > 0); busy_block = *i; continue; } TORRENT_ASSERT(p.num_peers(*i) == 0); // don't request pieces we already have in our request queue // This happens when pieces time out or the peer sends us // pieces we didn't request. Those aren't marked in the // piece picker, but we still keep track of them in the // download queue if (std::find_if(dq.begin(), dq.end(), has_block(*i)) != dq.end() || std::find_if(rq.begin(), rq.end(), has_block(*i)) != rq.end()) { #ifdef TORRENT_DEBUG std::vector::const_iterator j = std::find_if(dq.begin(), dq.end(), has_block(*i)); if (j != dq.end()) TORRENT_ASSERT(j->timed_out || j->not_wanted); #endif #ifndef TORRENT_DISABLE_LOGGING c.peer_log(peer_log_alert::info, "PIECE_PICKER" , "not_picking: %d,%d already in queue" , i->piece_index, i->block_index); #endif continue; } // ok, we found a piece that's not being downloaded // by somebody else. request it from this peer // and return if (!c.add_request(*i, 0)) continue; TORRENT_ASSERT(p.num_peers(*i) == 1); TORRENT_ASSERT(p.is_requested(*i)); num_requests--; } // we have picked as many blocks as we should // we're done! if (num_requests <= 0) { // since we could pick as many blocks as we // requested without having to resort to picking // busy ones, we're not in end-game mode c.set_endgame(false); return true; } // we did not pick as many pieces as we wanted, because // there aren't enough. This means we're in end-game mode // as long as we have at least one request outstanding, // we shouldn't pick another piece // if we are attempting to download 'allowed' pieces // and can't find any, that doesn't count as end-game if (!c.has_peer_choked()) c.set_endgame(true); // if we don't have any potential busy blocks to request // or if we already have outstanding requests, don't // pick a busy piece if (busy_block == piece_block::invalid || dq.size() + rq.size() > 0) { return true; } #ifdef TORRENT_DEBUG piece_picker::downloading_piece st; p.piece_info(busy_block.piece_index, st); TORRENT_ASSERT(st.requested + st.finished + st.writing == p.blocks_in_piece(busy_block.piece_index)); #endif TORRENT_ASSERT(p.is_requested(busy_block)); TORRENT_ASSERT(!p.is_downloaded(busy_block)); TORRENT_ASSERT(!p.is_finished(busy_block)); TORRENT_ASSERT(p.num_peers(busy_block) > 0); c.add_request(busy_block, peer_connection::req_busy); return true; } } libtorrent-rasterbar-1.1.13/src/resolve_links.cpp000066400000000000000000000112331351156116000221150ustar00rootroot00000000000000/* Copyright (c) 2014-2018, 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/resolve_links.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS resolve_links::resolve_links(boost::shared_ptr ti) : m_torrent_file(ti) { TORRENT_ASSERT(ti); int piece_size = ti->piece_length(); file_storage const& fs = ti->files(); m_file_sizes.reserve(fs.num_files()); for (int i = 0; i < fs.num_files(); ++i) { // don't match pad-files, and don't match files that aren't aligned to // ieces. Files are matched by comparing piece hashes, so pieces must // be aligned and the same size if (fs.pad_file_at(i)) continue; if ((fs.file_offset(i) % piece_size) != 0) continue; m_file_sizes.insert(std::make_pair(fs.file_size(i), i)); } m_links.resize(m_torrent_file->num_files()); } void resolve_links::match(boost::shared_ptr const& ti , std::string const& save_path) { if (!ti) return; // only torrents with the same if (ti->piece_length() != m_torrent_file->piece_length()) return; int piece_size = ti->piece_length(); file_storage const& fs = ti->files(); m_file_sizes.reserve(fs.num_files()); for (int i = 0; i < fs.num_files(); ++i) { // for every file in the other torrent, see if we have one that match // it in m_torrent_file // if the file base is not aligned to pieces, we're not going to match // it anyway (we only compare piece hashes) if ((fs.file_offset(i) % piece_size) != 0) continue; if (fs.pad_file_at(i)) continue; boost::int64_t file_size = fs.file_size(i); typedef boost::unordered_multimap::iterator iterator; typedef std::pair range_iterator; range_iterator range = m_file_sizes.equal_range(file_size); for (iterator iter = range.first; iter != range.second; ++iter) { TORRENT_ASSERT(iter->second < m_torrent_file->files().num_files()); TORRENT_ASSERT(iter->second >= 0); // if we already have found a duplicate for this file, no need // to keep looking if (m_links[iter->second].ti) continue; // files are aligned and have the same size, now start comparing // piece hashes, to see if the files are identical // the pieces of the incoming file int their_piece = fs.map_file(i, 0, 0).piece; // the pieces of "this" file (from m_torrent_file) int our_piece = m_torrent_file->files().map_file( iter->second, 0, 0).piece; int num_pieces = (file_size + piece_size - 1) / piece_size; bool match = true; for (int p = 0; p < num_pieces; ++p, ++their_piece, ++our_piece) { if (m_torrent_file->hash_for_piece(our_piece) != ti->hash_for_piece(their_piece)) { match = false; break; } } if (!match) continue; m_links[iter->second].ti = ti; m_links[iter->second].save_path = save_path; m_links[iter->second].file_idx = i; // since we have a duplicate for this file, we may as well remove // it from the file-size map, so we won't find it again. m_file_sizes.erase(iter); break; } } } #endif // TORRENT_DISABLE_MUTABLE_TORRENTS } // namespace libtorrent libtorrent-rasterbar-1.1.13/src/resolver.cpp000066400000000000000000000102601351156116000210760ustar00rootroot00000000000000/* Copyright (c) 2013-2018, 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/resolver.hpp" #include #include "libtorrent/debug.hpp" #include "libtorrent/aux_/time.hpp" namespace libtorrent { resolver::resolver(io_service& ios) : m_ios(ios) , m_resolver(ios) , m_critical_resolver(ios) , m_max_size(700) , m_timeout(seconds(1200)) {} void resolver::on_lookup(error_code const& ec, tcp::resolver::iterator i , resolver_interface::callback_t h, std::string hostname) { #if defined TORRENT_ASIO_DEBUGGING complete_async("resolver::on_lookup"); #endif if (ec) { std::vector
empty; h(ec, empty); return; } dns_cache_entry& ce = m_cache[hostname]; time_point now = aux::time_now(); ce.last_seen = now; ce.addresses.clear(); while (i != tcp::resolver::iterator()) { ce.addresses.push_back(i->endpoint().address()); ++i; } h(ec, ce.addresses); // if m_cache grows too big, weed out the // oldest entries if (m_cache.size() > m_max_size) { cache_t::iterator oldest = m_cache.begin(); for (cache_t::iterator k = m_cache.begin(); k != m_cache.end(); ++k) { if (k->second.last_seen < oldest->second.last_seen) oldest = k; } // remove the oldest entry m_cache.erase(oldest); } } void resolver::async_resolve(std::string const& host, int const flags , resolver_interface::callback_t const& h) { // special handling for raw IP addresses. There's no need to get in line // behind actual lookups if we can just resolve it immediately. error_code ec; address ip = address::from_string(host.c_str(), ec); if (!ec) { std::vector
addresses; addresses.push_back(ip); m_ios.post(boost::bind(h, ec, addresses)); return; } ec.clear(); cache_t::iterator i = m_cache.find(host); if (i != m_cache.end()) { // keep cache entries valid for m_timeout seconds if ((flags & resolver_interface::cache_only) || i->second.last_seen + m_timeout >= aux::time_now()) { m_ios.post(boost::bind(h, ec, i->second.addresses)); return; } } if (flags & resolver_interface::cache_only) { // we did not find a cache entry, fail the lookup m_ios.post(boost::bind(h, boost::asio::error::host_not_found , std::vector
())); return; } // the port is ignored tcp::resolver::query q(host, "80"); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("resolver::on_lookup"); #endif if (flags & resolver_interface::abort_on_shutdown) { m_resolver.async_resolve(q, boost::bind(&resolver::on_lookup, this, _1, _2 , h, host)); } else { m_critical_resolver.async_resolve(q, boost::bind(&resolver::on_lookup, this, _1, _2 , h, host)); } } void resolver::abort() { m_resolver.cancel(); } } libtorrent-rasterbar-1.1.13/src/rss.cpp000066400000000000000000000446261351156116000200610ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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/rss.hpp" #include "libtorrent/xml_parse.hpp" #include "libtorrent/http_parser.hpp" #include "libtorrent/http_connection.hpp" #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/aux_/session_call.hpp" #include "libtorrent/session.hpp" #include "libtorrent/alert_types.hpp" // for rss_alert #include #include #include #include #ifndef TORRENT_NO_DEPRECATE namespace libtorrent { feed_item::feed_item(): size(-1) {} feed_item::~feed_item() {} struct feed_state { feed_state(feed& r) : in_item(false) , num_items(0) , type(none) , ret(r) {} bool in_item; int num_items; std::string current_tag; enum feed_type { none, atom, rss2 } type; feed_item current_item; feed& ret; bool is_item(char const* tag) const { switch (type) { case atom: return string_equal_no_case(tag, "entry"); case rss2: return string_equal_no_case(tag, "item"); case none: return false; } return false; } bool is_title(char const* tag) const { switch (type) { case atom: case rss2: return string_equal_no_case(tag, "title"); case none: return false; } return false; } bool is_url(char const* tag) const { switch (type) { case atom: case rss2: return string_equal_no_case(tag, "link"); case none: return false; } return false; } bool is_desc(char const* tag) const { switch (type) { case atom: return string_equal_no_case(tag, "summary"); case rss2: return string_equal_no_case(tag, "description") || string_equal_no_case(tag, "media:text"); case none: return false; } return false; } bool is_uuid(char const* tag) const { switch (type) { case atom: return string_equal_no_case(tag, "id"); case rss2: return string_equal_no_case(tag, "guid"); case none: return false; } return false; } bool is_comment(char const* tag) const { switch (type) { case atom: return false; case rss2: return string_equal_no_case(tag, "comments"); case none: return false; } return false; } bool is_category(char const* tag) const { switch (type) { case atom: return false; case rss2: return string_equal_no_case(tag, "category"); case none: return false; } return false; } bool is_size(char const* tag) const { return string_equal_no_case(tag, "size") || string_equal_no_case(tag, "contentlength"); } bool is_hash(char const* tag) const { return string_equal_no_case(tag, "hash") || string_equal_no_case(tag, "media:hash"); } bool is_ttl(char const* tag) const { return string_equal_no_case(tag, "ttl"); } }; void parse_feed(feed_state& f, int token, char const* name, int name_len , char const* val, int val_len) { switch (token) { case xml_parse_error: f.ret.m_error = errors::parse_failed; return; case xml_start_tag: case xml_empty_tag: { f.current_tag.assign(name, name_len); if (f.type == feed_state::none) { if (string_equal_no_case(f.current_tag.c_str(), "feed")) f.type = feed_state::atom; else if (string_equal_no_case(f.current_tag.c_str(), "rss")) f.type = feed_state::rss2; } if (f.is_item(f.current_tag.c_str())) f.in_item = true; return; } case xml_attribute: { if (!f.in_item) return; std::string str(name, name_len); if (f.is_url(f.current_tag.c_str()) && f.type == feed_state::atom) { // atom feeds have items like this: // if (string_equal_no_case(str.c_str(), "href")) f.current_item.url.assign(val, val_len); else if (string_equal_no_case(str.c_str(), "length")) f.current_item.size = strtoll(val, 0, 10); } else if (f.type == feed_state::rss2 && string_equal_no_case(f.current_tag.c_str(), "enclosure")) { // rss feeds have items like this: // if (string_equal_no_case(str.c_str(), "url")) f.current_item.url.assign(val, val_len); else if (string_equal_no_case(str.c_str(), "length")) f.current_item.size = strtoll(val, 0, 10); } else if (f.type == feed_state::rss2 && string_equal_no_case(f.current_tag.c_str(), "media:content")) { // rss feeds sometimes have items like this: // if (string_equal_no_case(str.c_str(), "url")) f.current_item.url.assign(val, val_len); else if (string_equal_no_case(str.c_str(), "filesize")) f.current_item.size = strtoll(val, 0, 10); } return; } case xml_end_tag: { if (f.in_item && f.is_item(std::string(name, name_len).c_str())) { f.in_item = false; if (!f.current_item.title.empty() && !f.current_item.url.empty()) { f.ret.add_item(f.current_item); ++f.num_items; } f.current_item = feed_item(); } f.current_tag = ""; return; } case xml_string: { if (!f.in_item) { if (f.is_title(f.current_tag.c_str())) f.ret.m_title.assign(name, name_len); else if (f.is_desc(f.current_tag.c_str())) f.ret.m_description.assign(name, name_len); else if (f.is_ttl(f.current_tag.c_str())) { int tmp = atoi(name); if (tmp > 0) f.ret.m_ttl = tmp; } return; } if (f.is_title(f.current_tag.c_str())) f.current_item.title.assign(name, name_len); else if (f.is_desc(f.current_tag.c_str())) f.current_item.description.assign(name, name_len); else if (f.is_uuid(f.current_tag.c_str())) f.current_item.uuid.assign(name, name_len); else if (f.is_url(f.current_tag.c_str()) && f.type != feed_state::atom) f.current_item.url.assign(name, name_len); else if (f.is_comment(f.current_tag.c_str())) f.current_item.comment.assign(name, name_len); else if (f.is_category(f.current_tag.c_str())) f.current_item.category.assign(name, name_len); else if (f.is_size(f.current_tag.c_str())) f.current_item.size = strtoll(name, 0, 10); else if (f.is_hash(f.current_tag.c_str()) && name_len == 40) { if (!from_hex(name, 40, f.current_item.info_hash.data())) { // hex parsing failed f.current_item.info_hash.clear(); } } return; } case xml_declaration_tag: return; case xml_comment: return; } } torrent_handle add_feed_item(session& s, feed_item const& fi , add_torrent_params const& tp, error_code& ec) { add_torrent_params p = tp; p.url = fi.url; p.uuid = fi.uuid; // #error figure out how to get the feed url in here // p.source_feed_url = ???; p.ti.reset(); p.info_hash.clear(); p.name = fi.title.c_str(); return s.add_torrent(p, ec); } #ifndef BOOST_NO_EXCEPTIONS torrent_handle add_feed_item(session& s, feed_item const& fi , add_torrent_params const& tp) { error_code ec; torrent_handle ret = add_feed_item(s, fi, tp, ec); if (ec) throw libtorrent_exception(ec); return ret; } #endif boost::shared_ptr new_feed(aux::session_impl& ses, feed_settings const& sett) { return boost::shared_ptr(new feed(ses, sett)); } feed::feed(aux::session_impl& ses, feed_settings const& sett) : m_last_attempt(0) , m_last_update(0) , m_ttl(-1) , m_failures(0) , m_updating(false) , m_settings(sett) , m_ses(ses) { } void feed::set_settings(feed_settings const& s) { m_settings = s; } void feed::get_settings(feed_settings* s) const { *s = m_settings; } feed_handle feed::my_handle() { return feed_handle(boost::weak_ptr(shared_from_this())); } void feed::on_feed(error_code const& ec , http_parser const& parser, char const* data, int size) { // enabling this assert makes the unit test a lot more difficult // TORRENT_ASSERT(m_updating); m_updating = false; // rss_alert is deprecated, and so is all of this code. #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif if (ec && ec != boost::asio::error::eof) { ++m_failures; m_error = ec; if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(my_handle(), m_settings.url , rss_alert::state_error, m_error); } return; } if (parser.status_code() != 200) { ++m_failures; m_error = error_code(parser.status_code(), get_http_category()); if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(my_handle(), m_settings.url , rss_alert::state_error, m_error); } return; } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif m_failures = 0; feed_state s(*this); xml_parse(data, data + size, boost::bind(&parse_feed, boost::ref(s) , _1, _2, _3, _4, _5)); time_t now = time(NULL); // keep history of the typical feed size times 5 int max_history = (std::max)(s.num_items * 5, 100); // this is not very efficient, but that's probably OK for now while (int(m_added.size()) > max_history) { // loop over all elements and find the one with the lowest timestamp // i.e. it was added the longest ago, then remove it std::map::iterator i = std::min_element( m_added.begin(), m_added.end() , boost::bind(&std::pair::second, _1) < boost::bind(&std::pair::second, _2)); m_added.erase(i); } m_last_update = now; // rss_alert is deprecated, and so is all of this code. #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif // report that we successfully updated the feed if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(my_handle(), m_settings.url , rss_alert::state_updated, error_code()); } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif // update m_ses.m_next_rss_update timestamps // now that we have updated our timestamp m_ses.update_rss_feeds(); } void feed::load_state(bdecode_node const& rd) { m_title = rd.dict_find_string_value("m_title"); m_description = rd.dict_find_string_value("m_description"); m_last_attempt = rd.dict_find_int_value("m_last_attempt"); m_last_update = rd.dict_find_int_value("m_last_update"); bdecode_node e = rd.dict_find_list("items"); if (e) { m_items.reserve(e.list_size()); for (int i = 0; i < e.list_size(); ++i) { bdecode_node entry = e.list_at(i); if (entry.type() != bdecode_node::dict_t) continue; m_items.push_back(feed_item()); feed_item& item = m_items.back(); item.url = entry.dict_find_string_value("url"); item.uuid = entry.dict_find_string_value("uuid"); item.title = entry.dict_find_string_value("title"); item.description = entry.dict_find_string_value("description"); item.comment = entry.dict_find_string_value("comment"); item.category = entry.dict_find_string_value("category"); item.size = entry.dict_find_int_value("size"); // don't load duplicates if (m_urls.find(item.url) != m_urls.end()) { m_items.pop_back(); continue; } m_urls.insert(item.url); } } m_settings.url = rd.dict_find_string_value("url"); m_settings.auto_download = rd.dict_find_int_value("auto_download"); m_settings.auto_map_handles = rd.dict_find_int_value("auto_map_handles"); m_settings.default_ttl = rd.dict_find_int_value("default_ttl"); e = rd.dict_find_dict("add_params"); if (e) { m_settings.add_args.save_path = e.dict_find_string_value("save_path"); m_settings.add_args.flags = e.dict_find_int_value("flags"); } e = rd.dict_find_list("history"); if (e) { for (int i = 0; i < e.list_size(); ++i) { if (e.list_at(i).type() != bdecode_node::list_t) continue; bdecode_node item = e.list_at(i); if (item.list_size() != 2 || item.list_at(0).type() != bdecode_node::string_t || item.list_at(1).type() != bdecode_node::int_t) continue; m_added.insert(std::pair( item.list_at(0).string_value() , item.list_at(1).int_value())); } } } void feed::save_state(entry& rd) const { // feed properties rd["m_title"] = m_title; rd["m_description"] = m_description; rd["m_last_attempt"] = m_last_attempt; rd["m_last_update"] = m_last_update; // items entry::list_type& items = rd["items"].list(); for (std::vector::const_iterator i = m_items.begin() , end(m_items.end()); i != end; ++i) { items.push_back(entry()); entry& item = items.back(); item["url"] = i->url; item["uuid"] = i->uuid; item["title"] = i->title; item["description"] = i->description; item["comment"] = i->comment; item["category"] = i->category; item["size"] = i->size; } // settings feed_settings sett_def; #define TORRENT_WRITE_SETTING(name) \ if (m_settings.name != sett_def.name) rd[#name] = m_settings.name TORRENT_WRITE_SETTING(url); TORRENT_WRITE_SETTING(auto_download); TORRENT_WRITE_SETTING(auto_map_handles); TORRENT_WRITE_SETTING(default_ttl); #undef TORRENT_WRITE_SETTING entry& add = rd["add_params"]; add_torrent_params add_def; #define TORRENT_WRITE_SETTING(name) \ if (m_settings.add_args.name != add_def.name) add[#name] = m_settings.add_args.name; TORRENT_WRITE_SETTING(save_path); TORRENT_WRITE_SETTING(flags); #undef TORRENT_WRITE_SETTING entry::list_type& history = rd["history"].list(); for (std::map::const_iterator i = m_added.begin() , end(m_added.end()); i != end; ++i) { history.push_back(entry()); entry::list_type& item = history.back().list(); item.push_back(entry(i->first)); item.push_back(entry(i->second)); } } void feed::add_item(feed_item const& item) { // don't add duplicates if (m_urls.find(item.url) != m_urls.end()) return; m_urls.insert(item.url); m_items.push_back(item); feed_item& i = m_items.back(); if (m_settings.auto_map_handles) i.handle = torrent_handle(m_ses.find_torrent(i.uuid.empty() ? i.url : i.uuid)); if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(my_handle(), i); if (m_settings.auto_download) { if (!m_settings.auto_map_handles) i.handle = torrent_handle(m_ses.find_torrent(i.uuid.empty() ? i.url : i.uuid)); // if we're already downloading this torrent // move along to the next one if (i.handle.is_valid()) return; // has this already been added? if (m_added.find(i.url) != m_added.end()) return; // this means we should add this torrent to the session add_torrent_params p = m_settings.add_args; p.url = i.url; p.uuid = i.uuid; p.source_feed_url = m_settings.url; p.ti.reset(); p.info_hash.clear(); p.name = i.title.c_str(); error_code e; m_ses.add_torrent(p, e); time_t now = time(NULL); m_added.insert(make_pair(i.url, now)); } } // returns the number of seconds until trying again int feed::update_feed() { if (m_updating) return 60; m_last_attempt = time(0); m_last_update = 0; // rss_alert is deprecated, and so is all of this code. #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(my_handle(), m_settings.url , rss_alert::state_updating, error_code()); } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif boost::shared_ptr feed( new http_connection(m_ses.get_io_service() , m_ses.get_resolver() , boost::bind(&feed::on_feed, shared_from_this() , _1, _2, _3, _4))); std::string const user_agent = m_ses.settings().get_bool(settings_pack::anonymous_mode) ? "" : m_ses.settings().get_str(settings_pack::user_agent); m_updating = true; feed->get(m_settings.url, seconds(30), 0, 0, 5, user_agent); return 60 + m_failures * m_failures * 60; } void feed::get_feed_status(feed_status* ret) const { ret->items = m_items; ret->last_update = m_last_update; ret->updating = m_updating; ret->url = m_settings.url; ret->title = m_title; ret->description = m_description; ret->error = m_error; ret->ttl = m_ttl == -1 ? m_settings.default_ttl : m_ttl; ret->next_update = next_update(time(0)); } int feed::next_update(time_t now) const { if (m_last_update == 0) return int(m_last_attempt + 60 * 5 - now); int ttl = m_ttl == -1 ? m_settings.default_ttl : m_ttl; TORRENT_ASSERT((m_last_update + ttl * 60) - now < INT_MAX); return int((m_last_update + ttl * 60) - now); } #define TORRENT_ASYNC_CALL(x) \ boost::shared_ptr f = m_feed_ptr.lock(); \ if (!f) return; \ aux::session_impl& ses = f->session(); \ ses.get_io_service().post(boost::bind(&feed:: x, f)) #define TORRENT_ASYNC_CALL1(x, a1) \ boost::shared_ptr f = m_feed_ptr.lock(); \ if (!f) return; \ aux::session_impl& ses = f->session(); \ ses.get_io_service().post(boost::bind(&feed:: x, f, a1)) #define TORRENT_SYNC_CALL1(x, a1) \ boost::shared_ptr f = m_feed_ptr.lock(); \ if (f) aux::sync_call_handle(f, boost::bind(&feed:: x, f, a1)); feed_handle::feed_handle(boost::weak_ptr const& p) : m_feed_ptr(p) {} void feed_handle::update_feed() { TORRENT_ASYNC_CALL(update_feed); } feed_status feed_handle::get_feed_status() const { feed_status ret; TORRENT_SYNC_CALL1(get_feed_status, &ret); return ret; } void feed_handle::set_settings(feed_settings const& s) { TORRENT_ASYNC_CALL1(set_settings, s); } feed_settings feed_handle::settings() const { feed_settings ret; TORRENT_SYNC_CALL1(get_settings, &ret); return ret; } } #endif // TORRENT_NO_DEPRECATE libtorrent-rasterbar-1.1.13/src/session.cpp000066400000000000000000000341761351156116000207340ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #ifdef TORRENT_PROFILE_CALLS #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/extensions/ut_pex.hpp" #include "libtorrent/extensions/ut_metadata.hpp" #include "libtorrent/extensions/smart_ban.hpp" #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/session_handle.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/bt_peer_connection.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/aux_/session_call.hpp" #include "libtorrent/kademlia/dht_tracker.hpp" #include "libtorrent/natpmp.hpp" #include "libtorrent/upnp.hpp" #include "libtorrent/magnet_uri.hpp" #include "libtorrent/lazy_entry.hpp" using boost::shared_ptr; using boost::weak_ptr; using libtorrent::aux::session_impl; namespace libtorrent { namespace { #if defined TORRENT_ASIO_DEBUGGING void wait_for_asio_handlers() { int counter = 0; while (log_async()) { #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN Sleep(1000); #elif defined TORRENT_BEOS snooze_until(system_time() + 1000000, B_SYSTEM_TIMEBASE); #else usleep(1000000); #endif ++counter; printf("\x1b[2J\x1b[0;0H\x1b[33m==== Waiting to shut down: %d ==== \x1b[0m\n\n" , counter); } async_dec_threads(); fprintf(stderr, "\n\nEXPECTS NO MORE ASYNC OPS\n\n\n"); } #endif } // anonymous namespace TORRENT_EXPORT void min_memory_usage(settings_pack& set) { // receive data directly into disk buffers // this yields more system calls to read() and // kqueue(), but saves RAM. set.set_bool(settings_pack::contiguous_recv_buffer, false); set.set_int(settings_pack::disk_io_write_mode, settings_pack::disable_os_cache); set.set_int(settings_pack::disk_io_read_mode, settings_pack::disable_os_cache); // keep 2 blocks outstanding when hashing set.set_int(settings_pack::checking_mem_usage, 2); // don't use any extra threads to do SHA-1 hashing set.set_int(settings_pack::network_threads, 0); set.set_int(settings_pack::aio_threads, 1); set.set_int(settings_pack::alert_queue_size, 100); set.set_int(settings_pack::max_out_request_queue, 300); set.set_int(settings_pack::max_allowed_in_request_queue, 100); // setting this to a low limit, means more // peers are more likely to request from the // same piece. Which means fewer partial // pieces and fewer entries in the partial // piece list set.set_int(settings_pack::whole_pieces_threshold, 2); set.set_bool(settings_pack::use_parole_mode, false); set.set_bool(settings_pack::prioritize_partial_pieces, true); // connect to 5 peers per second set.set_int(settings_pack::connection_speed, 5); // only have 4 files open at a time set.set_int(settings_pack::file_pool_size, 4); // we want to keep the peer list as small as possible set.set_bool(settings_pack::allow_multiple_connections_per_ip, false); set.set_int(settings_pack::max_failcount, 2); set.set_int(settings_pack::inactivity_timeout, 120); // whenever a peer has downloaded one block, write // it to disk, and don't read anything from the // socket until the disk write is complete set.set_int(settings_pack::max_queued_disk_bytes, 1); // don't keep track of all upnp devices, keep // the device list small set.set_bool(settings_pack::upnp_ignore_nonrouters, true); // never keep more than one 16kB block in // the send buffer set.set_int(settings_pack::send_buffer_watermark, 9); // don't use any disk cache set.set_int(settings_pack::cache_size, 0); set.set_int(settings_pack::cache_buffer_chunk_size, 1); set.set_bool(settings_pack::use_read_cache, false); set.set_bool(settings_pack::use_disk_read_ahead, false); set.set_bool(settings_pack::close_redundant_connections, true); set.set_int(settings_pack::max_peerlist_size, 500); set.set_int(settings_pack::max_paused_peerlist_size, 50); // udp trackers are cheaper to talk to set.set_bool(settings_pack::prefer_udp_trackers, true); set.set_int(settings_pack::max_rejects, 10); set.set_int(settings_pack::recv_socket_buffer_size, 16 * 1024); set.set_int(settings_pack::send_socket_buffer_size, 16 * 1024); // use less memory when reading and writing // whole pieces set.set_bool(settings_pack::coalesce_reads, false); set.set_bool(settings_pack::coalesce_writes, false); } TORRENT_EXPORT void high_performance_seed(settings_pack& set) { // don't throttle TCP, assume there is // plenty of bandwidth set.set_int(settings_pack::mixed_mode_algorithm, settings_pack::prefer_tcp); set.set_int(settings_pack::max_out_request_queue, 1500); set.set_int(settings_pack::max_allowed_in_request_queue, 2000); // we will probably see a high rate of alerts, make it less // likely to loose alerts set.set_int(settings_pack::alert_queue_size, 10000); // allow 500 files open at a time set.set_int(settings_pack::file_pool_size, 500); // don't update access time for each read/write set.set_bool(settings_pack::no_atime_storage, true); // as a seed box, we must accept multiple peers behind // the same NAT // set.set_bool(settings_pack::allow_multiple_connections_per_ip, true); // connect to 50 peers per second set.set_int(settings_pack::connection_speed, 500); // allow 8000 peer connections set.set_int(settings_pack::connections_limit, 8000); // allow lots of peers to try to connect simultaneously set.set_int(settings_pack::listen_queue_size, 3000); // unchoke many peers set.set_int(settings_pack::unchoke_slots_limit, 2000); // we need more DHT capacity to ping more peers // candidates before trying to connect set.set_int(settings_pack::dht_upload_rate_limit, 20000); // use 1 GB of cache set.set_int(settings_pack::cache_size, 32768 * 2); set.set_bool(settings_pack::use_read_cache, true); set.set_int(settings_pack::cache_buffer_chunk_size, 0); set.set_int(settings_pack::read_cache_line_size, 32); set.set_int(settings_pack::write_cache_line_size, 256); set.set_bool(settings_pack::low_prio_disk, false); // 30 seconds expiration to save cache // space for active pieces set.set_int(settings_pack::cache_expiry, 30); // in case the OS we're running on doesn't support // readv/writev, allocate contiguous buffers for // reads and writes // disable, since it uses a lot more RAM and a significant // amount of CPU to copy it around set.set_bool(settings_pack::coalesce_reads, false); set.set_bool(settings_pack::coalesce_writes, false); // the max number of bytes pending write before we throttle // download rate set.set_int(settings_pack::max_queued_disk_bytes, 7 * 1024 * 1024); // prevent fast pieces to interfere with suggested pieces // since we unchoke everyone, we don't need fast pieces anyway set.set_int(settings_pack::allowed_fast_set_size, 0); // suggest pieces in the read cache for higher cache hit rate set.set_int(settings_pack::suggest_mode, settings_pack::suggest_read_cache); set.set_bool(settings_pack::close_redundant_connections, true); set.set_int(settings_pack::max_rejects, 10); set.set_int(settings_pack::recv_socket_buffer_size, 1024 * 1024); set.set_int(settings_pack::send_socket_buffer_size, 1024 * 1024); // don't let connections linger for too long set.set_int(settings_pack::request_timeout, 10); set.set_int(settings_pack::peer_timeout, 20); set.set_int(settings_pack::inactivity_timeout, 20); set.set_int(settings_pack::active_limit, 20000); set.set_int(settings_pack::active_tracker_limit, 2000); set.set_int(settings_pack::active_dht_limit, 600); set.set_int(settings_pack::active_seeds, 2000); set.set_int(settings_pack::choking_algorithm, settings_pack::fixed_slots_choker); // of 500 ms, and a send rate of 4 MB/s, the upper // limit should be 2 MB set.set_int(settings_pack::send_buffer_watermark, 3 * 1024 * 1024); // put 1.5 seconds worth of data in the send buffer // this gives the disk I/O more heads-up on disk // reads, and can maximize throughput set.set_int(settings_pack::send_buffer_watermark_factor, 150); // always stuff at least 1 MiB down each peer // pipe, to quickly ramp up send rates set.set_int(settings_pack::send_buffer_low_watermark, 1 * 1024 * 1024); // don't retry peers if they fail once. Let them // connect to us if they want to set.set_int(settings_pack::max_failcount, 1); // the number of threads to use to call async_write_some // and read_some on peer sockets // this doesn't work. See comment in settings_pack.cpp set.set_int(settings_pack::network_threads, 0); // number of disk threads for low level file operations set.set_int(settings_pack::aio_threads, 8); set.set_int(settings_pack::checking_mem_usage, 2048); // the disk cache performs better with the pool allocator set.set_bool(settings_pack::use_disk_cache_pool, true); } #ifndef TORRENT_NO_DEPRECATE // this function returns a session_settings object // which will optimize libtorrent for minimum memory // usage, with no consideration of performance. TORRENT_EXPORT session_settings min_memory_usage() { aux::session_settings def; settings_pack pack; min_memory_usage(pack); apply_pack(&pack, def, 0); session_settings ret; load_struct_from_settings(def, ret); return ret; } TORRENT_EXPORT session_settings high_performance_seed() { aux::session_settings def; settings_pack pack; high_performance_seed(pack); apply_pack(&pack, def, 0); session_settings ret; load_struct_from_settings(def, ret); return ret; } #endif #define TORRENT_ASYNC_CALL(x) \ m_impl->get_io_service().dispatch(boost::bind(&session_impl:: x, m_impl.get())) #ifndef TORRENT_CFG #error TORRENT_CFG is not defined! #endif // this is a dummy function that's exported and named based // on the configuration. The session.hpp file will reference // it and if the library and the client are built with different // configurations this will give a link error void TORRENT_EXPORT TORRENT_CFG() {} #if defined _MSC_VER && defined TORRENT_DEBUG static void straight_to_debugger(unsigned int, _EXCEPTION_POINTERS*) { throw; } #endif void session::start(int flags, settings_pack const& pack, io_service* ios) { #if defined _MSC_VER && defined TORRENT_DEBUG // workaround for microsoft's // hardware exceptions that makes // it hard to debug stuff ::_set_se_translator(straight_to_debugger); #endif bool const internal_executor = ios == NULL; if (internal_executor) { // the user did not provide an executor, we have to use our own m_io_service = boost::make_shared(); ios = m_io_service.get(); } m_impl = boost::make_shared(boost::ref(*ios), boost::ref(pack)); *static_cast(this) = session_handle(m_impl.get()); #ifndef TORRENT_DISABLE_EXTENSIONS if (flags & add_default_plugins) { add_extension(create_ut_pex_plugin); add_extension(create_ut_metadata_plugin); add_extension(create_smart_ban_plugin); } #else TORRENT_UNUSED(flags); #endif m_impl->start_session(); if (internal_executor) { // start a thread for the message pump m_thread = boost::make_shared(boost::bind(&io_service::run , m_io_service.get())); } } session::~session() { aux::dump_call_profile(); TORRENT_ASSERT(m_impl); TORRENT_ASYNC_CALL(abort); if (m_thread && m_thread.unique()) { #if defined TORRENT_ASIO_DEBUGGING wait_for_asio_handlers(); #endif m_thread->join(); } } session_proxy session::abort() { // stop calling the alert notify function now, to avoid it thinking the // session is still alive m_impl->alerts().set_notify_function(boost::function()); return session_proxy(m_io_service, m_thread, m_impl); } #ifndef TORRENT_NO_DEPRECATE session_settings::session_settings(std::string const& user_agent_) { aux::session_settings def; def.set_str(settings_pack::user_agent, user_agent_); load_struct_from_settings(def, *this); } session_settings::~session_settings() {} #endif // TORRENT_NO_DEPRECATE session_proxy::~session_proxy() { if (m_thread && m_thread.unique()) { #if defined TORRENT_ASIO_DEBUGGING wait_for_asio_handlers(); #endif m_thread->join(); } } } libtorrent-rasterbar-1.1.13/src/session_call.cpp000066400000000000000000000060521351156116000217170ustar00rootroot00000000000000/* Copyright (c) 2014-2018, Arvid Norberg, Steven Siloti 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/aux_/session_call.hpp" namespace libtorrent { namespace aux { #ifdef TORRENT_PROFILE_CALLS static mutex g_calls_mutex; static boost::unordered_map g_blocking_calls; #endif void blocking_call() { #ifdef TORRENT_PROFILE_CALLS char stack[2048]; print_backtrace(stack, sizeof(stack), 20); mutex::scoped_lock l(g_calls_mutex); g_blocking_calls[stack] += 1; #endif } void dump_call_profile() { #ifdef TORRENT_PROFILE_CALLS FILE* out = fopen("blocking_calls.txt", "w+"); std::map profile; mutex::scoped_lock l(g_calls_mutex); for (boost::unordered_map::const_iterator i = g_blocking_calls.begin() , end(g_blocking_calls.end()); i != end; ++i) { profile[i->second] = i->first; } for (std::map::const_reverse_iterator i = profile.rbegin() , end(profile.rend()); i != end; ++i) { fprintf(out, "\n\n%d\n%s\n", i->first, i->second.c_str()); } fclose(out); #endif } void fun_wrap(bool& done, condition_variable& e, mutex& m, boost::function f) { f(); mutex::scoped_lock l(m); done = true; e.notify_all(); } void torrent_wait(bool& done, aux::session_impl& ses) { blocking_call(); mutex::scoped_lock l(ses.mut); while (!done) { ses.cond.wait(l); }; } void sync_call(aux::session_impl& ses, boost::function f) { bool done = false; ses.get_io_service().dispatch(boost::bind(&fun_wrap , boost::ref(done) , boost::ref(ses.cond) , boost::ref(ses.mut) , f)); torrent_wait(done, ses); } } } // namespace aux namespace libtorrent libtorrent-rasterbar-1.1.13/src/session_handle.cpp000066400000000000000000000663621351156116000222510ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/session_handle.hpp" #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/aux_/session_call.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/lazy_entry.hpp" #include "libtorrent/peer_class_type_filter.hpp" #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-macros" #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-macros" #endif #define TORRENT_ASYNC_CALL(x) \ m_impl->get_io_service().dispatch(boost::bind(&session_impl:: x, m_impl)) #define TORRENT_ASYNC_CALL1(x, a1) \ m_impl->get_io_service().dispatch(boost::bind(&session_impl:: x, m_impl, a1)) #define TORRENT_ASYNC_CALL2(x, a1, a2) \ m_impl->get_io_service().dispatch(boost::bind(&session_impl:: x, m_impl, a1, a2)) #define TORRENT_ASYNC_CALL3(x, a1, a2, a3) \ m_impl->get_io_service().dispatch(boost::bind(&session_impl:: x, m_impl, a1, a2, a3)) #define TORRENT_SYNC_CALL(x) \ aux::sync_call(*m_impl, boost::function(boost::bind(&session_impl:: x, m_impl))) #define TORRENT_SYNC_CALL1(x, a1) \ aux::sync_call(*m_impl, boost::function(boost::bind(&session_impl:: x, m_impl, a1))) #define TORRENT_SYNC_CALL2(x, a1, a2) \ aux::sync_call(*m_impl, boost::function(boost::bind(&session_impl:: x, m_impl, a1, a2))) #define TORRENT_SYNC_CALL3(x, a1, a2, a3) \ aux::sync_call(*m_impl, boost::function(boost::bind(&session_impl:: x, m_impl, a1, a2, a3))) #define TORRENT_SYNC_CALL4(x, a1, a2, a3, a4) \ aux::sync_call(*m_impl, boost::function(boost::bind(&session_impl:: x, m_impl, a1, a2, a3, a4))) #define TORRENT_SYNC_CALL_RET(type, x) \ aux::sync_call_ret(*m_impl, boost::function(boost::bind(&session_impl:: x, m_impl))) #define TORRENT_SYNC_CALL_RET1(type, x, a1) \ aux::sync_call_ret(*m_impl, boost::function(boost::bind(&session_impl:: x, m_impl, a1))) #define TORRENT_SYNC_CALL_RET2(type, x, a1, a2) \ aux::sync_call_ret(*m_impl, boost::function(boost::bind(&session_impl:: x, m_impl, a1, a2))) #define TORRENT_SYNC_CALL_RET3(type, x, a1, a2, a3) \ aux::sync_call_ret(*m_impl, boost::function(boost::bind(&session_impl:: x, m_impl, a1, a2, a3))) #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef __GNUC__ #pragma GCC diagnostic pop #endif using libtorrent::aux::session_impl; namespace libtorrent { void session_handle::save_state(entry& e, boost::uint32_t flags) const { TORRENT_SYNC_CALL2(save_state, &e, flags); } void session_handle::load_state(bdecode_node const& e , boost::uint32_t const flags) { // this needs to be synchronized since the lifespan // of e is tied to the caller TORRENT_SYNC_CALL2(load_state, &e, flags); } void session_handle::get_torrent_status(std::vector* ret , boost::function const& pred , boost::uint32_t flags) const { TORRENT_SYNC_CALL3(get_torrent_status, ret, boost::ref(pred), flags); } void session_handle::refresh_torrent_status(std::vector* ret , boost::uint32_t flags) const { TORRENT_SYNC_CALL2(refresh_torrent_status, ret, flags); } void session_handle::post_torrent_updates(boost::uint32_t flags) { TORRENT_ASYNC_CALL1(post_torrent_updates, flags); } void session_handle::post_session_stats() { TORRENT_ASYNC_CALL(post_session_stats); } void session_handle::post_dht_stats() { TORRENT_ASYNC_CALL(post_dht_stats); } io_service& session_handle::get_io_service() { return m_impl->get_io_service(); } torrent_handle session_handle::find_torrent(sha1_hash const& info_hash) const { return TORRENT_SYNC_CALL_RET1(torrent_handle, find_torrent_handle, info_hash); } std::vector session_handle::get_torrents() const { return TORRENT_SYNC_CALL_RET(std::vector, get_torrents); } #ifndef BOOST_NO_EXCEPTIONS torrent_handle session_handle::add_torrent(add_torrent_params const& params) { error_code ec; torrent_handle r = TORRENT_SYNC_CALL_RET2(torrent_handle, add_torrent, params, boost::ref(ec)); if (ec) throw libtorrent_exception(ec); return r; } #endif torrent_handle session_handle::add_torrent(add_torrent_params const& params, error_code& ec) { ec.clear(); return TORRENT_SYNC_CALL_RET2(torrent_handle, add_torrent, params, boost::ref(ec)); } void session_handle::async_add_torrent(add_torrent_params const& params) { add_torrent_params* p = new add_torrent_params(params); p->save_path = complete(p->save_path); #ifndef TORRENT_NO_DEPRECATE if (params.tracker_url) { p->trackers.push_back(params.tracker_url); p->tracker_url = NULL; } #endif TORRENT_ASYNC_CALL1(async_add_torrent, p); } #ifndef BOOST_NO_EXCEPTIONS #ifndef TORRENT_NO_DEPRECATE // if the torrent already exists, this will throw duplicate_torrent torrent_handle session_handle::add_torrent( torrent_info const& ti , std::string const& save_path , entry const& resume_data , storage_mode_t storage_mode , bool paused , storage_constructor_type sc) { boost::shared_ptr tip(boost::make_shared(ti)); add_torrent_params p(sc); p.ti = tip; p.save_path = save_path; if (resume_data.type() != entry::undefined_t) { bencode(std::back_inserter(p.resume_data), resume_data); } p.storage_mode = storage_mode; p.paused = paused; return add_torrent(p); } torrent_handle session_handle::add_torrent( char const* tracker_url , sha1_hash const& info_hash , char const* name , std::string const& save_path , entry const& resume_data , storage_mode_t storage_mode , bool paused , storage_constructor_type sc , void* userdata) { add_torrent_params p(sc); p.tracker_url = tracker_url; p.info_hash = info_hash; p.save_path = save_path; p.storage_mode = storage_mode; p.paused = paused; p.userdata = userdata; p.name = name; if (resume_data.type() != entry::undefined_t) { bencode(std::back_inserter(p.resume_data), resume_data); } return add_torrent(p); } #endif // TORRENT_NO_DEPRECATE #endif // BOOST_NO_EXCEPTIONS void session_handle::pause() { TORRENT_ASYNC_CALL(pause); } void session_handle::resume() { TORRENT_ASYNC_CALL(resume); } bool session_handle::is_paused() const { return TORRENT_SYNC_CALL_RET(bool, is_paused); } void session_handle::set_load_function(user_load_function_t fun) { TORRENT_ASYNC_CALL1(set_load_function, fun); } #ifndef TORRENT_NO_DEPRECATE session_status session_handle::status() const { return TORRENT_SYNC_CALL_RET(session_status, status); } void session_handle::get_cache_info(sha1_hash const& ih , std::vector& ret) const { cache_status st; get_cache_info(&st, find_torrent(ih)); ret.swap(st.pieces); } cache_status session_handle::get_cache_status() const { cache_status st; get_cache_info(&st); return st; } #endif void session_handle::get_cache_info(cache_status* ret , torrent_handle h, int flags) const { piece_manager* st = 0; boost::shared_ptr t = h.m_torrent.lock(); if (t) { if (t->has_storage()) st = &t->storage(); else flags = session::disk_cache_no_pieces; } m_impl->disk_thread().get_cache_info(ret, flags & session::disk_cache_no_pieces, st); } #ifndef TORRENT_NO_DEPRECATE feed_handle session_handle::add_feed(feed_settings const& feed) { // if you have auto-download enabled, you must specify a download directory! TORRENT_ASSERT_PRECOND(!feed.auto_download || !feed.add_args.save_path.empty()); return TORRENT_SYNC_CALL_RET1(feed_handle, add_feed, feed); } void session_handle::remove_feed(feed_handle h) { TORRENT_ASYNC_CALL1(remove_feed, h); } void session_handle::get_feeds(std::vector& f) const { f.clear(); TORRENT_SYNC_CALL1(get_feeds, &f); } void session_handle::start_dht() { settings_pack p; p.set_bool(settings_pack::enable_dht, true); apply_settings(p); } void session_handle::stop_dht() { settings_pack p; p.set_bool(settings_pack::enable_dht, false); apply_settings(p); } #endif // TORRENT_NO_DEPRECATE void session_handle::set_dht_settings(dht_settings const& settings) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL1(set_dht_settings, settings); #else TORRENT_UNUSED(settings); #endif } dht_settings session_handle::get_dht_settings() const { #ifndef TORRENT_DISABLE_DHT return TORRENT_SYNC_CALL_RET(dht_settings, get_dht_settings); #else return dht_settings(); #endif } bool session_handle::is_dht_running() const { #ifndef TORRENT_DISABLE_DHT return TORRENT_SYNC_CALL_RET(bool, is_dht_running); #else return false; #endif } void session_handle::set_dht_storage(dht::dht_storage_constructor_type sc) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL1(set_dht_storage, sc); #else TORRENT_UNUSED(sc); #endif } void session_handle::add_dht_node(std::pair const& node) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL1(add_dht_node_name, node); #else TORRENT_UNUSED(node); #endif } #ifndef TORRENT_NO_DEPRECATE void session_handle::add_dht_router(std::pair const& node) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL1(add_dht_router, node); #else TORRENT_UNUSED(node); #endif } #endif // TORRENT_NO_DEPRECATE void session_handle::dht_get_item(sha1_hash const& target) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL1(dht_get_immutable_item, target); #else TORRENT_UNUSED(target); #endif } void session_handle::dht_get_item(boost::array key , std::string salt) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL2(dht_get_mutable_item, key, salt); #else TORRENT_UNUSED(key); TORRENT_UNUSED(salt); #endif } sha1_hash session_handle::dht_put_item(entry data) { std::vector buf; bencode(std::back_inserter(buf), data); sha1_hash ret = hasher(&buf[0], buf.size()).final(); #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL2(dht_put_immutable_item, data, ret); #endif return ret; } void session_handle::dht_put_item(boost::array key , boost::function& , boost::uint64_t&, std::string const&)> cb , std::string salt) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL3(dht_put_mutable_item, key, cb, salt); #else TORRENT_UNUSED(key); TORRENT_UNUSED(cb); TORRENT_UNUSED(salt); #endif } void session_handle::dht_get_peers(sha1_hash const& info_hash) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL1(dht_get_peers, info_hash); #else TORRENT_UNUSED(info_hash); #endif } void session_handle::dht_announce(sha1_hash const& info_hash, int port, int flags) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL3(dht_announce, info_hash, port, flags); #else TORRENT_UNUSED(info_hash); TORRENT_UNUSED(port); TORRENT_UNUSED(flags); #endif } void session_handle::dht_direct_request(udp::endpoint ep, entry const& e, void* userdata) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL3(dht_direct_request, ep, e, userdata); #else TORRENT_UNUSED(ep); TORRENT_UNUSED(e); TORRENT_UNUSED(userdata); #endif } #ifndef TORRENT_NO_DEPRECATE entry session_handle::dht_state() const { #ifndef TORRENT_DISABLE_DHT return TORRENT_SYNC_CALL_RET(entry, dht_state); #else return entry(); #endif } void session_handle::start_dht(entry const& startup_state) { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL1(start_dht_deprecated, startup_state); #else TORRENT_UNUSED(startup_state); #endif } #endif // TORRENT_NO_DEPRECATE void session_handle::add_extension(boost::function(torrent_handle const&, void*)> ext) { #ifndef TORRENT_DISABLE_EXTENSIONS TORRENT_ASYNC_CALL1(add_extension, ext); #else TORRENT_UNUSED(ext); #endif } void session_handle::add_extension(boost::shared_ptr ext) { #ifndef TORRENT_DISABLE_EXTENSIONS TORRENT_ASYNC_CALL1(add_ses_extension, ext); #else TORRENT_UNUSED(ext); #endif } #ifndef TORRENT_NO_DEPRECATE void session_handle::load_asnum_db(char const*) {} void session_handle::load_country_db(char const*) {} int session_handle::as_for_ip(address const&) { return 0; } #if TORRENT_USE_WSTRING void session_handle::load_asnum_db(wchar_t const*) {} void session_handle::load_country_db(wchar_t const*) {} #endif // TORRENT_USE_WSTRING void session_handle::load_state(entry const& ses_state , boost::uint32_t const flags) { if (ses_state.type() == entry::undefined_t) return; std::vector buf; bencode(std::back_inserter(buf), ses_state); bdecode_node e; error_code ec; #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS || !defined BOOST_NO_EXCEPTIONS int ret = #endif bdecode(&buf[0], &buf[0] + buf.size(), e, ec); TORRENT_ASSERT(ret == 0); #ifndef BOOST_NO_EXCEPTIONS if (ret != 0) throw libtorrent_exception(ec); #endif TORRENT_SYNC_CALL2(load_state, &e, flags); } entry session_handle::state() const { entry ret; TORRENT_SYNC_CALL2(save_state, &ret, 0xffffffff); return ret; } void session_handle::load_state(lazy_entry const& ses_state , boost::uint32_t const flags) { if (ses_state.type() == lazy_entry::none_t) return; std::pair buf = ses_state.data_section(); bdecode_node e; error_code ec; #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS || !defined BOOST_NO_EXCEPTIONS int ret = #endif bdecode(buf.first, buf.first + buf.second, e, ec); TORRENT_ASSERT(ret == 0); #ifndef BOOST_NO_EXCEPTIONS if (ret != 0) throw libtorrent_exception(ec); #endif TORRENT_SYNC_CALL2(load_state, &e, flags); } #endif // TORRENT_NO_DEPRECATE void session_handle::set_ip_filter(ip_filter const& f) { boost::shared_ptr copy = boost::make_shared(f); TORRENT_ASYNC_CALL1(set_ip_filter, copy); } ip_filter session_handle::get_ip_filter() const { return TORRENT_SYNC_CALL_RET(ip_filter, get_ip_filter); } void session_handle::set_port_filter(port_filter const& f) { TORRENT_ASYNC_CALL1(set_port_filter, f); } #ifndef TORRENT_NO_DEPRECATE void session_handle::set_peer_id(peer_id const& id) { settings_pack p; p.set_str(settings_pack::peer_fingerprint, id.to_string()); apply_settings(p); } peer_id session_handle::id() const { return TORRENT_SYNC_CALL_RET(peer_id, deprecated_get_peer_id); } #endif void session_handle::set_key(int key) { TORRENT_ASYNC_CALL1(set_key, key); } unsigned short session_handle::listen_port() const { return TORRENT_SYNC_CALL_RET(unsigned short, listen_port); } unsigned short session_handle::ssl_listen_port() const { return TORRENT_SYNC_CALL_RET(unsigned short, ssl_listen_port); } bool session_handle::is_listening() const { return TORRENT_SYNC_CALL_RET(bool, is_listening); } void session_handle::set_peer_class_filter(ip_filter const& f) { TORRENT_ASYNC_CALL1(set_peer_class_filter, f); } ip_filter session_handle::get_peer_class_filter() const { return TORRENT_SYNC_CALL_RET(ip_filter, get_peer_class_filter); } void session_handle::set_peer_class_type_filter(peer_class_type_filter const& f) { TORRENT_ASYNC_CALL1(set_peer_class_type_filter, f); } peer_class_type_filter session_handle::get_peer_class_type_filter() const { return TORRENT_SYNC_CALL_RET(peer_class_type_filter, get_peer_class_type_filter); } int session_handle::create_peer_class(char const* name) { return TORRENT_SYNC_CALL_RET1(int, create_peer_class, name); } void session_handle::delete_peer_class(int cid) { TORRENT_ASYNC_CALL1(delete_peer_class, cid); } peer_class_info session_handle::get_peer_class(int cid) { return TORRENT_SYNC_CALL_RET1(peer_class_info, get_peer_class, cid); } void session_handle::set_peer_class(int cid, peer_class_info const& pci) { TORRENT_ASYNC_CALL2(set_peer_class, cid, pci); } #ifndef TORRENT_NO_DEPRECATE void session_handle::use_interfaces(char const* interfaces) { settings_pack pack; pack.set_str(settings_pack::outgoing_interfaces, interfaces); apply_settings(pack); } void session_handle::listen_on( std::pair const& port_range , error_code& ec , const char* net_interface, int flags) { settings_pack p; std::string interfaces_str; if (net_interface == NULL || strlen(net_interface) == 0) net_interface = "0.0.0.0"; interfaces_str = print_endpoint(tcp::endpoint(address::from_string(net_interface, ec), port_range.first)); if (ec) return; p.set_str(settings_pack::listen_interfaces, interfaces_str); p.set_int(settings_pack::max_retry_port_bind, port_range.second - port_range.first); p.set_bool(settings_pack::listen_system_port_fallback, (flags & session::listen_no_system_port) == 0); apply_settings(p); } #endif void session_handle::remove_torrent(const torrent_handle& h, int options) { if (!h.is_valid()) #ifdef BOOST_NO_EXCEPTIONS return; #else throw_invalid_handle(); #endif TORRENT_ASYNC_CALL2(remove_torrent, h, options); } #ifndef TORRENT_NO_DEPRECATE void session_handle::set_settings(session_settings const& s) { TORRENT_ASYNC_CALL1(set_settings, s); } session_settings session_handle::settings() const { return TORRENT_SYNC_CALL_RET(session_settings, deprecated_settings); } void session_handle::set_pe_settings(pe_settings const& r) { settings_pack pack; pack.set_bool(settings_pack::prefer_rc4, r.prefer_rc4); pack.set_int(settings_pack::out_enc_policy, r.out_enc_policy); pack.set_int(settings_pack::in_enc_policy, r.in_enc_policy); pack.set_int(settings_pack::allowed_enc_level, r.allowed_enc_level); apply_settings(pack); } pe_settings session_handle::get_pe_settings() const { settings_pack sett = get_settings(); pe_settings r; r.prefer_rc4 = sett.get_bool(settings_pack::prefer_rc4); r.out_enc_policy = sett.get_int(settings_pack::out_enc_policy); r.in_enc_policy = sett.get_int(settings_pack::in_enc_policy); r.allowed_enc_level = sett.get_int(settings_pack::allowed_enc_level); return r; } #endif void session_handle::apply_settings(settings_pack const& s) { TORRENT_ASSERT_PRECOND(!s.has_val(settings_pack::out_enc_policy) || s.get_int(settings_pack::out_enc_policy) <= settings_pack::pe_disabled); TORRENT_ASSERT_PRECOND(!s.has_val(settings_pack::in_enc_policy) || s.get_int(settings_pack::in_enc_policy) <= settings_pack::pe_disabled); TORRENT_ASSERT_PRECOND(!s.has_val(settings_pack::allowed_enc_level) || s.get_int(settings_pack::allowed_enc_level) <= settings_pack::pe_both); boost::shared_ptr copy = boost::make_shared(s); TORRENT_ASYNC_CALL1(apply_settings_pack, copy); } settings_pack session_handle::get_settings() const { return TORRENT_SYNC_CALL_RET(settings_pack, get_settings); } #ifndef TORRENT_NO_DEPRECATE void session_handle::set_i2p_proxy(proxy_settings const& s) { settings_pack pack; pack.set_str(settings_pack::i2p_hostname, s.hostname); pack.set_int(settings_pack::i2p_port, s.port); apply_settings(pack); } proxy_settings session_handle::i2p_proxy() const { proxy_settings ret; settings_pack sett = get_settings(); ret.hostname = sett.get_str(settings_pack::i2p_hostname); ret.port = sett.get_int(settings_pack::i2p_port); return ret; } void session_handle::set_proxy(proxy_settings const& s) { settings_pack pack; pack.set_str(settings_pack::proxy_hostname, s.hostname); pack.set_str(settings_pack::proxy_username, s.username); pack.set_str(settings_pack::proxy_password, s.password); pack.set_int(settings_pack::proxy_type, s.type); pack.set_int(settings_pack::proxy_port, s.port); pack.set_bool(settings_pack::proxy_hostnames,s.proxy_hostnames); pack.set_bool(settings_pack::proxy_peer_connections, s.proxy_peer_connections); apply_settings(pack); } proxy_settings session_handle::proxy() const { settings_pack sett = get_settings(); return proxy_settings(sett); } int session_handle::num_uploads() const { return TORRENT_SYNC_CALL_RET(int, num_uploads); } int session_handle::num_connections() const { return TORRENT_SYNC_CALL_RET(int, num_connections); } void session_handle::set_peer_proxy(proxy_settings const& s) { set_proxy(s); } void session_handle::set_web_seed_proxy(proxy_settings const&) { // NO-OP } void session_handle::set_tracker_proxy(proxy_settings const& s) { // if the tracker proxy is enabled, set the "proxy_tracker_connections" // setting settings_pack pack; pack.set_bool(settings_pack::proxy_tracker_connections , s.type != aux::proxy_settings::none); apply_settings(pack); } proxy_settings session_handle::peer_proxy() const { return proxy(); } proxy_settings session_handle::web_seed_proxy() const { return proxy(); } proxy_settings session_handle::tracker_proxy() const { settings_pack const sett = get_settings(); return sett.get_bool(settings_pack::proxy_tracker_connections) ? proxy_settings(sett) : proxy_settings(); } void session_handle::set_dht_proxy(proxy_settings const&) { // NO-OP } proxy_settings session_handle::dht_proxy() const { return proxy(); } int session_handle::upload_rate_limit() const { return TORRENT_SYNC_CALL_RET(int, upload_rate_limit); } int session_handle::download_rate_limit() const { return TORRENT_SYNC_CALL_RET(int, download_rate_limit); } int session_handle::local_upload_rate_limit() const { return TORRENT_SYNC_CALL_RET(int, local_upload_rate_limit); } int session_handle::local_download_rate_limit() const { return TORRENT_SYNC_CALL_RET(int, local_download_rate_limit); } int session_handle::max_half_open_connections() const { return 8; } void session_handle::set_local_upload_rate_limit(int bytes_per_second) { TORRENT_ASYNC_CALL1(set_local_upload_rate_limit, bytes_per_second); } void session_handle::set_local_download_rate_limit(int bytes_per_second) { TORRENT_ASYNC_CALL1(set_local_download_rate_limit, bytes_per_second); } void session_handle::set_upload_rate_limit(int bytes_per_second) { TORRENT_ASYNC_CALL1(set_upload_rate_limit, bytes_per_second); } void session_handle::set_download_rate_limit(int bytes_per_second) { TORRENT_ASYNC_CALL1(set_download_rate_limit, bytes_per_second); } void session_handle::set_max_connections(int limit) { TORRENT_ASYNC_CALL1(set_max_connections, limit); } void session_handle::set_max_uploads(int limit) { TORRENT_ASYNC_CALL1(set_max_uploads, limit); } void session_handle::set_max_half_open_connections(int) {} int session_handle::max_uploads() const { return TORRENT_SYNC_CALL_RET(int, max_uploads); } int session_handle::max_connections() const { return TORRENT_SYNC_CALL_RET(int, max_connections); } #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif std::auto_ptr session_handle::pop_alert() { alert const* a = m_impl->pop_alert(); if (a == NULL) return std::auto_ptr(); return a->clone(); } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif void session_handle::pop_alerts(std::deque* alerts) { m_impl->pop_alerts(alerts); } #endif // TORRENT_NO_DEPRECATE // the alerts are const, they may not be deleted by the client void session_handle::pop_alerts(std::vector* alerts) { m_impl->pop_alerts(alerts); } alert* session_handle::wait_for_alert(time_duration max_wait) { return m_impl->wait_for_alert(max_wait); } void session_handle::set_alert_notify(boost::function const& fun) { m_impl->alerts().set_notify_function(fun); } #ifndef TORRENT_NO_DEPRECATE void session_handle::set_severity_level(alert::severity_t s) { int m = 0; switch (s) { case alert::debug: m = alert::all_categories; break; case alert::info: m = alert::all_categories & ~(alert::debug_notification | alert::progress_notification | alert::dht_notification); break; case alert::warning: m = alert::all_categories & ~(alert::debug_notification | alert::status_notification | alert::progress_notification | alert::dht_notification); break; case alert::critical: m = alert::error_notification | alert::storage_notification; break; case alert::fatal: m = alert::error_notification; break; case alert::none: m = 0; break; } settings_pack p; p.set_int(settings_pack::alert_mask, m); apply_settings(p); } size_t session_handle::set_alert_queue_size_limit(size_t queue_size_limit_) { return TORRENT_SYNC_CALL_RET1(size_t, set_alert_queue_size_limit, queue_size_limit_); } void session_handle::set_alert_mask(boost::uint32_t m) { settings_pack p; p.set_int(settings_pack::alert_mask, m); apply_settings(p); } boost::uint32_t session_handle::get_alert_mask() const { return get_settings().get_int(settings_pack::alert_mask); } #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif void session_handle::set_alert_dispatch(boost::function)> const& fun) { m_impl->alerts().set_dispatch_function(fun); } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif void session_handle::start_lsd() { settings_pack p; p.set_bool(settings_pack::enable_lsd, true); apply_settings(p); } void session_handle::stop_lsd() { settings_pack p; p.set_bool(settings_pack::enable_lsd, false); apply_settings(p); } void session_handle::start_upnp() { settings_pack p; p.set_bool(settings_pack::enable_upnp, true); apply_settings(p); } void session_handle::stop_upnp() { settings_pack p; p.set_bool(settings_pack::enable_upnp, false); apply_settings(p); } void session_handle::start_natpmp() { settings_pack p; p.set_bool(settings_pack::enable_natpmp, true); apply_settings(p); } void session_handle::stop_natpmp() { settings_pack p; p.set_bool(settings_pack::enable_natpmp, false); apply_settings(p); } #endif // TORRENT_NO_DEPRECATE int session_handle::add_port_mapping(session::protocol_type t, int external_port, int local_port) { return TORRENT_SYNC_CALL_RET3(int, add_port_mapping, int(t), external_port, local_port); } void session_handle::delete_port_mapping(int handle) { TORRENT_ASYNC_CALL1(delete_port_mapping, handle); } } // namespace libtorrent libtorrent-rasterbar-1.1.13/src/session_impl.cpp000066400000000000000000006547551351156116000217700ustar00rootroot00000000000000/* Copyright (c) 2006-2018, 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 "libtorrent/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #if defined TORRENT_DEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS #if TORRENT_HAS_BOOST_UNORDERED #include #else #include #endif #endif // TORRENT_DEBUG && !TORRENT_DISABLE_INVARIANT_CHECKS #include #include #include #include #include #ifdef TORRENT_USE_VALGRIND #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/aux_/openssl.hpp" #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/bt_peer_connection.hpp" #include "libtorrent/peer_connection_handle.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/session_stats.hpp" #include "libtorrent/aux_/session_impl.hpp" #ifndef TORRENT_DISABLE_DHT #include "libtorrent/kademlia/dht_tracker.hpp" #endif #include "libtorrent/enum_net.hpp" #include "libtorrent/config.hpp" #include "libtorrent/utf8.hpp" #include "libtorrent/upnp.hpp" #include "libtorrent/natpmp.hpp" #include "libtorrent/lsd.hpp" #include "libtorrent/instantiate_connection.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/build_config.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/random.hpp" #include "libtorrent/magnet_uri.hpp" #include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/torrent_peer.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/choker.hpp" #include "libtorrent/error.hpp" #include "libtorrent/platform_util.hpp" #ifndef TORRENT_DISABLE_LOGGING #include "libtorrent/socket_io.hpp" // for logging stat layout #include "libtorrent/stat.hpp" #include // for va_list // for logging the size of DHT structures #ifndef TORRENT_DISABLE_DHT #include #include #include #include #include #endif // TORRENT_DISABLE_DHT #include "libtorrent/http_tracker_connection.hpp" #include "libtorrent/udp_tracker_connection.hpp" #endif // TORRENT_DISABLE_LOGGING #ifdef TORRENT_USE_GCRYPT extern "C" { GCRY_THREAD_OPTION_PTHREAD_IMPL; } namespace { // libgcrypt requires this to initialize the library struct gcrypt_setup { gcrypt_setup() { gcry_check_version(0); gcry_error_t e = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); if (e != 0) fprintf(stderr, "libcrypt ERROR: %s\n", gcry_strerror(e)); e = gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); if (e != 0) fprintf(stderr, "initialization finished error: %s\n", gcry_strerror(e)); } } gcrypt_global_constructor; } #endif // TORRENT_USE_GCRYPT #ifdef TORRENT_USE_OPENSSL #include #include // by openssl changelog at https://www.openssl.org/news/changelog.html // Changes between 1.0.2h and 1.1.0 [25 Aug 2016] // - Most global cleanup functions are no longer required because they are handled // via auto-deinit. Affected function CRYPTO_cleanup_all_ex_data() #if !defined(OPENSSL_API_COMPAT) || OPENSSL_API_COMPAT < 0x10100000L namespace { // openssl requires this to clean up internal // structures it allocates struct openssl_cleanup { ~openssl_cleanup() { CRYPTO_cleanup_all_ex_data(); } } openssl_global_destructor; } #endif #endif // TORRENT_USE_OPENSSL #ifdef TORRENT_WINDOWS // for ERROR_SEM_TIMEOUT #include #endif using boost::shared_ptr; using boost::weak_ptr; using libtorrent::aux::session_impl; #ifdef BOOST_NO_EXCEPTIONS namespace boost { void throw_exception(std::exception const& e) { ::abort(); } } #endif namespace libtorrent { #if defined TORRENT_ASIO_DEBUGGING std::map _async_ops; std::deque _wakeups; int _async_ops_nthreads = 0; mutex _async_ops_mutex; #endif socket_job::~socket_job() {} void network_thread_pool::process_job(socket_job const& j, bool post) { TORRENT_UNUSED(post); if (j.type == socket_job::write_job) { TORRENT_ASSERT(j.peer->m_socket_is_writing); j.peer->get_socket()->async_write_some( *j.vec, j.peer->make_write_handler(boost::bind( &peer_connection::on_send_data, j.peer, _1, _2))); } else { if (j.recv_buf) { j.peer->get_socket()->async_read_some(boost::asio::buffer(j.recv_buf, j.buf_size) , j.peer->make_read_handler(boost::bind( &peer_connection::on_receive_data, j.peer, _1, _2))); } else { j.peer->get_socket()->async_read_some(j.read_vec , j.peer->make_read_handler(boost::bind( &peer_connection::on_receive_data, j.peer, _1, _2))); } } } namespace aux { void session_impl::init_peer_class_filter(bool unlimited_local) { // set the default peer_class_filter to use the local peer class // for peers on local networks boost::uint32_t lfilter = 1 << m_local_peer_class; boost::uint32_t gfilter = 1 << m_global_class; struct class_mapping { char const* first; char const* last; boost::uint32_t filter; }; static const class_mapping v4_classes[] = { // everything {"0.0.0.0", "255.255.255.255", gfilter}, // local networks {"10.0.0.0", "10.255.255.255", lfilter}, {"172.16.0.0", "172.31.255.255", lfilter}, {"192.168.0.0", "192.168.255.255", lfilter}, // link-local {"169.254.0.0", "169.254.255.255", lfilter}, // loop-back {"127.0.0.0", "127.255.255.255", lfilter}, }; #if TORRENT_USE_IPV6 static const class_mapping v6_classes[] = { // everything {"::0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", gfilter}, // local networks {"fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", lfilter}, // link-local {"fe80::", "febf::ffff:ffff:ffff:ffff:ffff:ffff:ffff", lfilter}, // loop-back {"::1", "::1", lfilter}, }; #endif class_mapping const* p = v4_classes; int len = sizeof(v4_classes) / sizeof(v4_classes[0]); if (!unlimited_local) len = 1; for (int i = 0; i < len; ++i) { error_code ec; address_v4 begin = address_v4::from_string(p[i].first, ec); address_v4 end = address_v4::from_string(p[i].last, ec); if (ec) continue; m_peer_class_filter.add_rule(begin, end, p[i].filter); } #if TORRENT_USE_IPV6 p = v6_classes; len = sizeof(v6_classes) / sizeof(v6_classes[0]); if (!unlimited_local) len = 1; for (int i = 0; i < len; ++i) { error_code ec; address_v6 begin = address_v6::from_string(p[i].first, ec); address_v6 end = address_v6::from_string(p[i].last, ec); if (ec) continue; m_peer_class_filter.add_rule(begin, end, p[i].filter); } #endif } #if defined TORRENT_USE_OPENSSL && BOOST_VERSION >= 104700 && OPENSSL_VERSION_NUMBER >= 0x90812f namespace { // when running bittorrent over SSL, the SNI (server name indication) // extension is used to know which torrent the incoming connection is // trying to connect to. The 40 first bytes in the name is expected to // be the hex encoded info-hash int servername_callback(SSL* s, int* ad, void* arg) { TORRENT_UNUSED(ad); session_impl* ses = reinterpret_cast(arg); const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); if (!servername || strlen(servername) < 40) return SSL_TLSEXT_ERR_ALERT_FATAL; sha1_hash info_hash; bool valid = from_hex(servername, 40, info_hash.data()); // the server name is not a valid hex-encoded info-hash if (!valid) return SSL_TLSEXT_ERR_ALERT_FATAL; // see if there is a torrent with this info-hash boost::shared_ptr t = ses->find_torrent(info_hash).lock(); // if there isn't, fail if (!t) return SSL_TLSEXT_ERR_ALERT_FATAL; // if the torrent we found isn't an SSL torrent, also fail. if (!t->is_ssl_torrent()) return SSL_TLSEXT_ERR_ALERT_FATAL; // if the torrent doesn't have an SSL context and should not allow // incoming SSL connections if (!t->ssl_ctx()) return SSL_TLSEXT_ERR_ALERT_FATAL; // use this torrent's certificate SSL_CTX *torrent_context = t->ssl_ctx()->native_handle(); SSL_set_SSL_CTX(s, torrent_context); SSL_set_verify(s, SSL_CTX_get_verify_mode(torrent_context), SSL_CTX_get_verify_callback(torrent_context)); return SSL_TLSEXT_ERR_OK; } } // anonymous namesoace #endif session_impl::session_impl(io_service& ios, settings_pack const& pack) : m_settings(pack) #ifndef TORRENT_NO_DEPRECATE , m_next_rss_update(min_time()) #endif #ifndef TORRENT_DISABLE_POOL_ALLOCATOR , m_send_buffers(send_buffer_size()) #endif , m_io_service(ios) #ifdef TORRENT_USE_OPENSSL , m_ssl_ctx(boost::asio::ssl::context::sslv23) #endif , m_alerts(m_settings.get_int(settings_pack::alert_queue_size) , m_settings.get_int(settings_pack::alert_mask)) #ifndef TORRENT_NO_DEPRECATE , m_alert_pointer_pos(0) #endif , m_disk_thread(m_io_service, m_stats_counters , static_cast(this)) , m_download_rate(peer_connection::download_channel) , m_upload_rate(peer_connection::upload_channel) , m_global_class(0) , m_tcp_peer_class(0) , m_local_peer_class(0) , m_host_resolver(m_io_service) , m_tracker_manager(m_udp_socket, m_stats_counters, m_host_resolver , m_settings #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS , *this #endif ) , m_num_save_resume(0) , m_work(io_service::work(m_io_service)) , m_max_queue_pos(-1) , m_key(0) #if TORRENT_USE_I2P , m_i2p_conn(m_io_service) #endif , m_interface_index(0) , m_unchoke_time_scaler(0) , m_auto_manage_time_scaler(0) , m_optimistic_unchoke_time_scaler(0) , m_disconnect_time_scaler(90) , m_auto_scrape_time_scaler(180) #ifndef TORRENT_NO_DEPRECATE , m_next_explicit_cache_torrent(0) , m_cache_rotation_timer(0) #endif , m_next_suggest_torrent(0) , m_suggest_timer(0) , m_peak_up_rate(0) , m_peak_down_rate(0) , m_created(clock_type::now()) , m_last_tick(m_created) , m_last_second_tick(m_created - milliseconds(900)) , m_last_choke(m_created) , m_last_auto_manage(m_created) , m_next_port(0) #ifndef TORRENT_DISABLE_DHT , m_dht_storage_constructor(dht::dht_default_storage_constructor) , m_dht_announce_timer(m_io_service) , m_dht_interval_update_torrents(0) , m_outstanding_router_lookups(0) #endif , m_external_udp_port(0) , m_udp_socket(m_io_service) , m_utp_socket_manager(m_settings, m_udp_socket, m_stats_counters, NULL , boost::bind(&session_impl::incoming_connection, this, _1)) #ifdef TORRENT_USE_OPENSSL , m_ssl_udp_socket(m_io_service) , m_ssl_utp_socket_manager(m_settings, m_ssl_udp_socket, m_stats_counters , &m_ssl_ctx , boost::bind(&session_impl::on_incoming_utp_ssl, this, _1)) #endif , m_boost_connections(0) , m_timer(m_io_service) , m_lsd_announce_timer(m_io_service) , m_close_file_timer(m_io_service) , m_next_downloading_connect_torrent(0) , m_next_finished_connect_torrent(0) , m_download_connect_attempts(0) , m_next_scrape_torrent(0) , m_tick_residual(0) #ifndef TORRENT_DISABLE_EXTENSIONS , m_session_extension_features(0) #endif , m_deferred_submit_disk_jobs(false) , m_pending_auto_manage(false) , m_need_auto_manage(false) , m_abort(false) , m_paused(false) { #if TORRENT_USE_ASSERTS m_posting_torrent_updates = false; #endif m_udp_socket.set_rate_limit(m_settings.get_int(settings_pack::dht_upload_rate_limit)); m_udp_socket.subscribe(&m_utp_socket_manager); m_udp_socket.subscribe(this); m_udp_socket.subscribe(&m_tracker_manager); #ifdef TORRENT_USE_OPENSSL m_ssl_udp_socket.subscribe(&m_ssl_utp_socket_manager); m_ssl_udp_socket.subscribe(this); #endif error_code ec; m_listen_interface = tcp::endpoint(address_v4::any(), 0); TORRENT_ASSERT_VAL(!ec, ec); update_time_now(); m_disk_thread.set_settings(&pack, m_alerts); } // This function is called by the creating thread, not in the message loop's // / io_service thread. // TODO: 2 is there a reason not to move all of this into init()? and just // post it to the io_service? void session_impl::start_session() { #ifndef TORRENT_DISABLE_LOGGING session_log("start session"); #endif error_code ec; #ifdef TORRENT_USE_OPENSSL m_ssl_ctx.set_verify_mode(boost::asio::ssl::context::verify_none, ec); #if BOOST_VERSION >= 104700 #if OPENSSL_VERSION_NUMBER >= 0x90812f aux::openssl_set_tlsext_servername_callback(m_ssl_ctx.native_handle() , servername_callback); aux::openssl_set_tlsext_servername_arg(m_ssl_ctx.native_handle(), this); #endif // OPENSSL_VERSION_NUMBER #endif // BOOST_VERSION #endif #ifndef TORRENT_DISABLE_DHT m_next_dht_torrent = m_torrents.begin(); #endif m_next_lsd_torrent = m_torrents.begin(); m_tcp_mapping[0] = -1; m_tcp_mapping[1] = -1; m_udp_mapping[0] = -1; m_udp_mapping[1] = -1; #ifdef TORRENT_USE_OPENSSL m_ssl_tcp_mapping[0] = -1; m_ssl_tcp_mapping[1] = -1; m_ssl_udp_mapping[0] = -1; m_ssl_udp_mapping[1] = -1; #endif m_global_class = m_classes.new_peer_class("global"); m_tcp_peer_class = m_classes.new_peer_class("tcp"); m_local_peer_class = m_classes.new_peer_class("local"); // local peers are always unchoked m_classes.at(m_local_peer_class)->ignore_unchoke_slots = true; // local peers are allowed to exceed the normal connection // limit by 50% m_classes.at(m_local_peer_class)->connection_limit_factor = 150; TORRENT_ASSERT(m_global_class == session::global_peer_class_id); TORRENT_ASSERT(m_tcp_peer_class == session::tcp_peer_class_id); TORRENT_ASSERT(m_local_peer_class == session::local_peer_class_id); init_peer_class_filter(true); // TCP, SSL/TCP and I2P connections should be assigned the TCP peer class m_peer_class_type_filter.add(peer_class_type_filter::tcp_socket, m_tcp_peer_class); m_peer_class_type_filter.add(peer_class_type_filter::ssl_tcp_socket, m_tcp_peer_class); m_peer_class_type_filter.add(peer_class_type_filter::i2p_socket, m_tcp_peer_class); #ifndef TORRENT_DISABLE_LOGGING session_log("config: %s version: %s revision: %s" , TORRENT_CFG_STRING , LIBTORRENT_VERSION , LIBTORRENT_REVISION); #endif // TORRENT_DISABLE_LOGGING // ---- auto-cap max connections ---- int const max_files = max_open_files(); // deduct some margin for epoll/kqueue, log files, // futexes, shared objects etc. // 80% of the available file descriptors should go to connections m_settings.set_int(settings_pack::connections_limit, std::min( m_settings.get_int(settings_pack::connections_limit) , std::max(5, (max_files - 20) * 8 / 10))); // 20% goes towards regular files (see disk_io_thread) #ifndef TORRENT_DISABLE_LOGGING session_log(" max connections: %d", m_settings.get_int(settings_pack::connections_limit)); session_log(" max files: %d", max_files); #endif m_io_service.post(boost::bind(&session_impl::init, this)); } void session_impl::init() { // this is a debug facility // see single_threaded in debug.hpp thread_started(); TORRENT_ASSERT(is_single_thread()); #ifndef TORRENT_DISABLE_LOGGING if (m_alerts.should_post() || m_alerts.should_post()) { session_log(" *** session thread init"); // this specific output is parsed by tools/parse_session_stats.py // if this is changed, that parser should also be changed std::string stats_header = "session stats header: "; std::vector stats = session_stats_metrics(); std::sort(stats.begin(), stats.end() , boost::bind(&stats_metric::value_index, _1) < boost::bind(&stats_metric::value_index, _2)); for (int i = 0; i < stats.size(); ++i) { if (i > 0) stats_header += ", "; stats_header += stats[i].name; } m_alerts.emplace_alert(stats_header.c_str()); } #endif // this is where we should set up all async operations. This // is called from within the network thread as opposed to the // constructor which is called from the main thread #if defined TORRENT_ASIO_DEBUGGING async_inc_threads(); add_outstanding_async("session_impl::on_tick"); #endif error_code ec; m_io_service.post(boost::bind(&session_impl::on_tick, this, ec)); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::on_lsd_announce"); #endif int delay = (std::max)(m_settings.get_int(settings_pack::local_service_announce_interval) / (std::max)(int(m_torrents.size()), 1), 1); m_lsd_announce_timer.expires_from_now(seconds(delay), ec); m_lsd_announce_timer.async_wait( boost::bind(&session_impl::on_lsd_announce, this, _1)); TORRENT_ASSERT(!ec); #ifndef TORRENT_DISABLE_DHT update_dht_announce_interval(); #endif #ifndef TORRENT_DISABLE_LOGGING session_log(" done starting session"); #endif // apply all m_settings to this session run_all_updates(*this); // this applies unchoke settings from m_settings recalculate_unchoke_slots(); if (m_listen_sockets.empty()) { update_listen_interfaces(); open_listen_port(); } #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif } void session_impl::async_resolve(std::string const& host, int flags , session_interface::callback_t const& h) { m_host_resolver.async_resolve(host, flags, h); } void session_impl::queue_async_resume_data(boost::shared_ptr const& t) { INVARIANT_CHECK; int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit); if (m_num_save_resume + m_alerts.num_queued_resume() >= loaded_limit && m_user_load_torrent && loaded_limit > 0) { TORRENT_ASSERT(t); // do loaded torrents first, otherwise they'll just be // evicted and have to be loaded again if (t->is_loaded()) m_save_resume_queue.push_front(t); else m_save_resume_queue.push_back(t); return; } if (t->do_async_save_resume_data()) ++m_num_save_resume; } // this is called whenever a save_resume_data comes back // from the disk thread void session_impl::done_async_resume() { TORRENT_ASSERT(m_num_save_resume > 0); --m_num_save_resume; } // this is called when one or all save resume alerts are // popped off the alert queue void session_impl::async_resume_dispatched() { INVARIANT_CHECK; int num_queued_resume = m_alerts.num_queued_resume(); int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit); while (!m_save_resume_queue.empty() && (m_num_save_resume + num_queued_resume < loaded_limit || loaded_limit == 0)) { boost::shared_ptr t = m_save_resume_queue.front(); m_save_resume_queue.erase(m_save_resume_queue.begin()); if (t->do_async_save_resume_data()) ++m_num_save_resume; } } void session_impl::save_state(entry* eptr, boost::uint32_t flags) const { TORRENT_ASSERT(is_single_thread()); entry& e = *eptr; // make it a dict e.dict(); if (flags & session::save_settings) { entry::dictionary_type& sett = e["settings"].dict(); save_settings_to_dict(m_settings, sett); } #ifndef TORRENT_DISABLE_DHT if (flags & session::save_dht_settings) { entry::dictionary_type& dht_sett = e["dht"].dict(); dht_sett["max_peers_reply"] = m_dht_settings.max_peers_reply; dht_sett["search_branching"] = m_dht_settings.search_branching; dht_sett["max_fail_count"] = m_dht_settings.max_fail_count; dht_sett["max_torrents"] = m_dht_settings.max_torrents; dht_sett["max_dht_items"] = m_dht_settings.max_dht_items; dht_sett["max_peers"] = m_dht_settings.max_peers; dht_sett["max_torrent_search_reply"] = m_dht_settings.max_torrent_search_reply; dht_sett["restrict_routing_ips"] = m_dht_settings.restrict_routing_ips; dht_sett["restrict_search_ips"] = m_dht_settings.restrict_search_ips; dht_sett["extended_routing_table"] = m_dht_settings.extended_routing_table; dht_sett["aggressive_lookups"] = m_dht_settings.aggressive_lookups; dht_sett["privacy_lookups"] = m_dht_settings.privacy_lookups; dht_sett["enforce_node_id"] = m_dht_settings.enforce_node_id; dht_sett["ignore_dark_internet"] = m_dht_settings.ignore_dark_internet; dht_sett["block_timeout"] = m_dht_settings.block_timeout; dht_sett["block_ratelimit"] = m_dht_settings.block_ratelimit; dht_sett["read_only"] = m_dht_settings.read_only; dht_sett["item_lifetime"] = m_dht_settings.item_lifetime; } if (m_dht && (flags & session::save_dht_state)) { e["dht state"] = m_dht->state(); } #endif #ifndef TORRENT_NO_DEPRECATE if (flags & session::save_feeds) { entry::list_type& feeds = e["feeds"].list(); for (std::vector >::const_iterator i = m_feeds.begin(), end(m_feeds.end()); i != end; ++i) { feeds.push_back(entry()); (*i)->save_state(feeds.back()); } } #endif #ifndef TORRENT_DISABLE_EXTENSIONS for (ses_extension_list_t::const_iterator i = m_ses_extensions.begin() , end(m_ses_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->save_state(*eptr); } TORRENT_CATCH(std::exception&) {} } #endif } proxy_settings session_impl::proxy() const { return proxy_settings(m_settings); } void session_impl::load_state(bdecode_node const* e , boost::uint32_t const flags = 0xffffffff) { TORRENT_ASSERT(is_single_thread()); bdecode_node settings; if (e->type() != bdecode_node::dict_t) return; #ifndef TORRENT_DISABLE_DHT bool need_update_dht = false; // load from the old settings names if (flags & session::save_dht_settings) { settings = e->dict_find_dict("dht"); if (settings) { bdecode_node val; val = settings.dict_find_int("max_peers_reply"); if (val) m_dht_settings.max_peers_reply = val.int_value(); val = settings.dict_find_int("search_branching"); if (val) m_dht_settings.search_branching = val.int_value(); val = settings.dict_find_int("max_fail_count"); if (val) m_dht_settings.max_fail_count = val.int_value(); val = settings.dict_find_int("max_torrents"); if (val) m_dht_settings.max_torrents = val.int_value(); val = settings.dict_find_int("max_dht_items"); if (val) m_dht_settings.max_dht_items = val.int_value(); val = settings.dict_find_int("max_peers"); if (val) m_dht_settings.max_peers = val.int_value(); val = settings.dict_find_int("max_torrent_search_reply"); if (val) m_dht_settings.max_torrent_search_reply = val.int_value(); val = settings.dict_find_int("restrict_routing_ips"); if (val) m_dht_settings.restrict_routing_ips = val.int_value(); val = settings.dict_find_int("restrict_search_ips"); if (val) m_dht_settings.restrict_search_ips = val.int_value(); val = settings.dict_find_int("extended_routing_table"); if (val) m_dht_settings.extended_routing_table = val.int_value(); val = settings.dict_find_int("aggressive_lookups"); if (val) m_dht_settings.aggressive_lookups = val.int_value(); val = settings.dict_find_int("privacy_lookups"); if (val) m_dht_settings.privacy_lookups = val.int_value(); val = settings.dict_find_int("enforce_node_id"); if (val) m_dht_settings.enforce_node_id = val.int_value(); val = settings.dict_find_int("ignore_dark_internet"); if (val) m_dht_settings.ignore_dark_internet = val.int_value(); val = settings.dict_find_int("block_timeout"); if (val) m_dht_settings.block_timeout = val.int_value(); val = settings.dict_find_int("block_ratelimit"); if (val) m_dht_settings.block_ratelimit = val.int_value(); val = settings.dict_find_int("read_only"); if (val) m_dht_settings.read_only = val.int_value(); val = settings.dict_find_int("item_lifetime"); if (val) m_dht_settings.item_lifetime = val.int_value(); } } if (flags & session::save_dht_state) { settings = e->dict_find_dict("dht state"); if (settings) { m_dht_state = settings; need_update_dht = true; } } #endif #ifndef TORRENT_NO_DEPRECATE bool need_update_proxy = false; if (flags & session::save_proxy) { settings = e->dict_find_dict("proxy"); if (settings) { bdecode_node val; val = settings.dict_find_int("port"); if (val) m_settings.set_int(settings_pack::proxy_port, val.int_value()); val = settings.dict_find_int("type"); if (val) m_settings.set_int(settings_pack::proxy_type, val.int_value()); val = settings.dict_find_int("proxy_hostnames"); if (val) m_settings.set_bool(settings_pack::proxy_hostnames, val.int_value()); val = settings.dict_find_int("proxy_peer_connections"); if (val) m_settings.set_bool(settings_pack::proxy_peer_connections, val.int_value()); val = settings.dict_find_string("hostname"); if (val) m_settings.set_str(settings_pack::proxy_hostname, val.string_value()); val = settings.dict_find_string("password"); if (val) m_settings.set_str(settings_pack::proxy_password, val.string_value()); val = settings.dict_find_string("username"); if (val) m_settings.set_str(settings_pack::proxy_username, val.string_value()); need_update_proxy = true; } } settings = e->dict_find_dict("encryption"); if (settings) { bdecode_node val; val = settings.dict_find_int("prefer_rc4"); if (val) m_settings.set_bool(settings_pack::prefer_rc4, val.int_value()); val = settings.dict_find_int("out_enc_policy"); if (val) m_settings.set_int(settings_pack::out_enc_policy, val.int_value()); val = settings.dict_find_int("in_enc_policy"); if (val) m_settings.set_int(settings_pack::in_enc_policy, val.int_value()); val = settings.dict_find_int("allowed_enc_level"); if (val) m_settings.set_int(settings_pack::allowed_enc_level, val.int_value()); } if (flags & session::save_feeds) { settings = e->dict_find_list("feeds"); if (settings) { m_feeds.reserve(settings.list_size()); for (int i = 0; i < settings.list_size(); ++i) { if (settings.list_at(i).type() != bdecode_node::dict_t) continue; boost::shared_ptr f(new_feed(*this, feed_settings())); f->load_state(settings.list_at(i)); f->update_feed(); m_feeds.push_back(f); } update_rss_feeds(); } } #endif if (flags & session::save_settings) { settings = e->dict_find_dict("settings"); if (settings) { // apply_settings_pack will update dht and proxy boost::shared_ptr pack = load_pack_from_dict(settings); // these settings are not loaded from state // they are set by the client software, not configured by users pack->clear(settings_pack::user_agent); pack->clear(settings_pack::peer_fingerprint); apply_settings_pack(pack); #ifndef TORRENT_DISABLE_DHT need_update_dht = false; #endif #ifndef TORRENT_NO_DEPRECATE need_update_proxy = false; #endif } } #ifndef TORRENT_DISABLE_DHT if (need_update_dht) start_dht(); #endif #ifndef TORRENT_NO_DEPRECATE if (need_update_proxy) update_proxy(); #endif #ifndef TORRENT_DISABLE_EXTENSIONS for (ses_extension_list_t::iterator i = m_ses_extensions.begin() , end(m_ses_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->load_state(*e); } TORRENT_CATCH(std::exception&) {} } #endif } #ifndef TORRENT_DISABLE_EXTENSIONS typedef boost::function(torrent_handle const&, void*)> ext_function_t; struct session_plugin_wrapper : plugin { session_plugin_wrapper(ext_function_t const& f) : m_f(f) {} virtual boost::shared_ptr new_torrent(torrent_handle const& t, void* user) { return m_f(t, user); } ext_function_t m_f; }; void session_impl::add_extension(ext_function_t ext) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT_VAL(ext, ext); boost::shared_ptr p(new session_plugin_wrapper(ext)); m_ses_extensions.push_back(p); m_session_extension_features |= p->implemented_features(); } void session_impl::add_ses_extension(boost::shared_ptr ext) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT_VAL(ext, ext); m_ses_extensions.push_back(ext); m_alerts.add_extension(ext); ext->added(session_handle(this)); m_session_extension_features |= ext->implemented_features(); // get any DHT queries the plugin would like to handle // and record them in m_extension_dht_queries for lookup // later dht_extensions_t dht_ext; ext->register_dht_extensions(dht_ext); for (dht_extensions_t::iterator e = dht_ext.begin(); e != dht_ext.end(); ++e) { TORRENT_ASSERT(e->first.size() <= max_dht_query_length); if (e->first.size() > max_dht_query_length) continue; extension_dht_query registration; registration.query_len = e->first.size(); std::copy(e->first.begin(), e->first.end(), registration.query.begin()); registration.handler = e->second; m_extension_dht_queries.push_back(registration); } } #endif // TORRENT_DISABLE_EXTENSIONS #ifndef TORRENT_NO_DEPRECATE feed_handle session_impl::add_feed(feed_settings const& sett) { TORRENT_ASSERT(is_single_thread()); // look for duplicates. If we already have a feed with this // URL, return a handle to the existing one for (std::vector >::const_iterator i = m_feeds.begin(), end(m_feeds.end()); i != end; ++i) { if (sett.url != (*i)->m_settings.url) continue; return feed_handle(*i); } boost::shared_ptr f(new_feed(*this, sett)); m_feeds.push_back(f); update_rss_feeds(); return feed_handle(f); } void session_impl::remove_feed(feed_handle h) { TORRENT_ASSERT(is_single_thread()); boost::shared_ptr f = h.m_feed_ptr.lock(); if (!f) return; std::vector >::iterator i = std::find(m_feeds.begin(), m_feeds.end(), f); if (i == m_feeds.end()) return; m_feeds.erase(i); } void session_impl::get_feeds(std::vector* ret) const { TORRENT_ASSERT(is_single_thread()); ret->clear(); ret->reserve(m_feeds.size()); for (std::vector >::const_iterator i = m_feeds.begin() , end(m_feeds.end()); i != end; ++i) ret->push_back(feed_handle(*i)); } #endif void session_impl::pause() { TORRENT_ASSERT(is_single_thread()); if (m_paused) return; #ifndef TORRENT_DISABLE_LOGGING session_log(" *** session paused ***"); #endif m_paused = true; for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { torrent& t = *i->second; t.do_pause(); } } void session_impl::resume() { TORRENT_ASSERT(is_single_thread()); if (!m_paused) return; m_paused = false; for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { torrent& t = *i->second; t.do_resume(); if (t.should_check_files()) t.start_checking(); } } void session_impl::abort() { TORRENT_ASSERT(is_single_thread()); if (m_abort) return; #ifndef TORRENT_DISABLE_LOGGING session_log(" *** ABORT CALLED ***"); #endif // at this point we cannot call the notify function anymore, since the // session will become invalid. m_alerts.set_notify_function(boost::function()); // this will cancel requests that are not critical for shutting down // cleanly. i.e. essentially tracker hostname lookups that we're not // about to send event=stopped to m_host_resolver.abort(); m_close_file_timer.cancel(); // abort the main thread m_abort = true; error_code ec; #if TORRENT_USE_I2P m_i2p_conn.close(ec); #endif stop_lsd(); stop_upnp(); stop_natpmp(); #ifndef TORRENT_DISABLE_DHT stop_dht(); m_dht_announce_timer.cancel(ec); #endif m_lsd_announce_timer.cancel(ec); for (std::set >::iterator i = m_incoming_sockets.begin() , end(m_incoming_sockets.end()); i != end; ++i) { (*i)->close(ec); TORRENT_ASSERT(!ec); } m_incoming_sockets.clear(); // close the listen sockets for (std::list::iterator i = m_listen_sockets.begin() , end(m_listen_sockets.end()); i != end; ++i) { i->sock->close(ec); TORRENT_ASSERT(!ec); } m_listen_sockets.clear(); #if TORRENT_USE_I2P if (m_i2p_listen_socket && m_i2p_listen_socket->is_open()) { m_i2p_listen_socket->close(ec); TORRENT_ASSERT(!ec); } m_i2p_listen_socket.reset(); #endif #ifndef TORRENT_DISABLE_LOGGING session_log(" aborting all torrents (%d)", int(m_torrents.size())); #endif // abort all torrents for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { i->second->abort(); } m_torrents.clear(); #ifndef TORRENT_DISABLE_LOGGING session_log(" aborting all tracker requests"); #endif m_tracker_manager.abort_all_requests(); #ifndef TORRENT_DISABLE_LOGGING session_log(" aborting all connections (%d)", int(m_connections.size())); #endif // abort all connections while (!m_connections.empty()) { #if TORRENT_USE_ASSERTS int conn = m_connections.size(); #endif (*m_connections.begin())->disconnect(errors::stopping_torrent, op_bittorrent); TORRENT_ASSERT_VAL(conn == int(m_connections.size()) + 1, conn); } // we need to give all the sockets an opportunity to actually have their handlers // called and cancelled before we continue the shutdown. This is a bit // complicated, if there are no "undead" peers, it's safe tor resume the // shutdown, but if there are, we have to wait for them to be cleared out // first. In session_impl::on_tick() we check them periodically. If we're // shutting down and we remove the last one, we'll initiate // shutdown_stage2 from there. if (m_undead_peers.empty()) { m_io_service.post(boost::bind(&session_impl::abort_stage2, this)); } } void session_impl::abort_stage2() { m_download_rate.close(); m_upload_rate.close(); m_udp_socket.close(); m_external_udp_port = 0; #ifdef TORRENT_USE_OPENSSL m_ssl_udp_socket.close(); #endif // it's OK to detach the threads here. The disk_io_thread // has an internal counter and won't release the network // thread until they're all dead (via m_work). m_disk_thread.abort(false); // now it's OK for the network thread to exit m_work.reset(); } bool session_impl::has_connection(peer_connection* p) const { return m_connections.find(p->self()) != m_connections.end(); } void session_impl::insert_peer(boost::shared_ptr const& c) { TORRENT_ASSERT(!c->m_in_constructor); m_connections.insert(c); } void session_impl::set_port_filter(port_filter const& f) { m_port_filter = f; if (m_settings.get_bool(settings_pack::no_connect_privileged_ports)) m_port_filter.add_rule(0, 1024, port_filter::blocked); // Close connections whose endpoint is filtered // by the new ip-filter for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) i->second->port_filter_updated(); } void session_impl::set_ip_filter(boost::shared_ptr const& f) { INVARIANT_CHECK; m_ip_filter = f; // Close connections whose endpoint is filtered // by the new ip-filter for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) i->second->set_ip_filter(m_ip_filter); } void session_impl::ban_ip(address addr) { TORRENT_ASSERT(is_single_thread()); if (!m_ip_filter) m_ip_filter = boost::make_shared(); m_ip_filter->add_rule(addr, addr, ip_filter::blocked); for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) i->second->set_ip_filter(m_ip_filter); } ip_filter const& session_impl::get_ip_filter() { TORRENT_ASSERT(is_single_thread()); if (!m_ip_filter) m_ip_filter = boost::make_shared(); return *m_ip_filter; } port_filter const& session_impl::get_port_filter() const { TORRENT_ASSERT(is_single_thread()); return m_port_filter; } namespace { template void set_socket_buffer_size(Socket& s, session_settings const& sett, error_code& ec) { int snd_size = sett.get_int(settings_pack::send_socket_buffer_size); if (snd_size) { typename Socket::send_buffer_size prev_option; s.get_option(prev_option, ec); if (!ec && prev_option.value() != snd_size) { typename Socket::send_buffer_size option(snd_size); s.set_option(option, ec); if (ec) { // restore previous value s.set_option(prev_option, ec); return; } } } int recv_size = sett.get_int(settings_pack::recv_socket_buffer_size); if (recv_size) { typename Socket::receive_buffer_size prev_option; s.get_option(prev_option, ec); if (!ec && prev_option.value() != recv_size) { typename Socket::receive_buffer_size option(recv_size); s.set_option(option, ec); if (ec) { // restore previous value s.set_option(prev_option, ec); return; } } } } } // anonymous namespace int session_impl::create_peer_class(char const* name) { TORRENT_ASSERT(is_single_thread()); return m_classes.new_peer_class(name); } void session_impl::delete_peer_class(peer_class_t const cid) { TORRENT_ASSERT(is_single_thread()); // if you hit this assert, you're deleting a non-existent peer class TORRENT_ASSERT_PRECOND(m_classes.at(cid)); if (m_classes.at(cid) == 0) return; m_classes.decref(cid); } peer_class_info session_impl::get_peer_class(peer_class_t const cid) { peer_class_info ret; peer_class* pc = m_classes.at(cid); // if you hit this assert, you're passing in an invalid cid TORRENT_ASSERT_PRECOND(pc); if (pc == 0) { #ifdef TORRENT_DEBUG // make it obvious that the return value is undefined ret.upload_limit = 0xf0f0f0f; ret.download_limit = 0xf0f0f0f; ret.label.resize(20); url_random(&ret.label[0], &ret.label[0] + 20); ret.ignore_unchoke_slots = false; ret.connection_limit_factor = 0xf0f0f0f; ret.upload_priority = 0xf0f0f0f; ret.download_priority = 0xf0f0f0f; #endif return ret; } pc->get_info(&ret); return ret; } void session_impl::queue_tracker_request(tracker_request& req , boost::weak_ptr c) { req.listen_port = listen_port(); if (m_key) req.key = m_key; #ifdef TORRENT_USE_OPENSSL // SSL torrents use the SSL listen port if (req.ssl_ctx && req.ssl_ctx != &m_ssl_ctx) req.listen_port = ssl_listen_port(); else req.ssl_ctx = &m_ssl_ctx; #endif #if TORRENT_USE_I2P if (!m_settings.get_str(settings_pack::i2p_hostname).empty()) { req.i2pconn = &m_i2p_conn; } #endif if (!req.bind_ip && m_listen_interface.address() != address_v4::any()) { req.bind_ip = m_listen_interface.address(); } m_tracker_manager.queue_request(get_io_service(), req, c); } void session_impl::set_peer_class(peer_class_t const cid, peer_class_info const& pci) { peer_class* pc = m_classes.at(cid); // if you hit this assert, you're passing in an invalid cid TORRENT_ASSERT_PRECOND(pc); if (pc == 0) return; pc->set_info(&pci); } void session_impl::set_peer_class_filter(ip_filter const& f) { INVARIANT_CHECK; m_peer_class_filter = f; } ip_filter const& session_impl::get_peer_class_filter() const { return m_peer_class_filter; } void session_impl::set_peer_class_type_filter(peer_class_type_filter f) { m_peer_class_type_filter = f; } peer_class_type_filter session_impl::get_peer_class_type_filter() { return m_peer_class_type_filter; } void session_impl::set_peer_classes(peer_class_set* s, address const& a, int st) { boost::uint32_t peer_class_mask = m_peer_class_filter.access(a); // assign peer class based on socket type static const int mapping[] = { 0, 0, 0, 0, 1, 4, 2, 2, 2, 3}; int socket_type = mapping[st]; // filter peer classes based on type peer_class_mask = m_peer_class_type_filter.apply(socket_type, peer_class_mask); for (peer_class_t i = 0; peer_class_mask; peer_class_mask >>= 1, ++i) { if ((peer_class_mask & 1) == 0) continue; // if you hit this assert, your peer class filter contains // a bitmask referencing a non-existent peer class TORRENT_ASSERT_PRECOND(m_classes.at(i)); if (m_classes.at(i) == 0) continue; s->add_class(m_classes, i); } } bool session_impl::ignore_unchoke_slots_set(peer_class_set const& set) const { int num = set.num_classes(); for (int i = 0; i < num; ++i) { peer_class const* pc = m_classes.at(set.class_at(i)); if (pc == 0) continue; if (pc->ignore_unchoke_slots) return true; } return false; } bandwidth_manager* session_impl::get_bandwidth_manager(int channel) { return (channel == peer_connection::download_channel) ? &m_download_rate : &m_upload_rate; } // the back argument determines whether this bump causes the torrent // to be the most recently used or the least recently used. Putting // the torrent at the back of the queue makes it the most recently // used and the least likely to be evicted. This is the default. // if back is false, the torrent is moved to the front of the queue, // and made the most likely to be evicted. This is used for torrents // that are paused, to give up their slot among the loaded torrents void session_impl::bump_torrent(torrent* t, bool back) { if (t->is_aborted()) return; bool new_torrent = false; // if t is the only torrent in the LRU list, both // its prev and next links will be NULL, even though // it's already in the list. Cover this case by also // checking to see if it's the first item if (t->next != NULL || t->prev != NULL || m_torrent_lru.front() == t) { #ifdef TORRENT_DEBUG torrent* i = m_torrent_lru.front(); while (i != NULL && i != t) i = i->next; TORRENT_ASSERT(i == t); #endif // this torrent is in the list already. // first remove it m_torrent_lru.erase(t); } else { new_torrent = true; } // pinned torrents should not be part of the LRU, since // the LRU is only used to evict torrents if (t->is_pinned()) return; if (back) m_torrent_lru.push_back(t); else m_torrent_lru.push_front(t); if (new_torrent) evict_torrents_except(t); } void session_impl::evict_torrent(torrent* t) { TORRENT_ASSERT(!t->is_pinned()); // if there's no user-load function set, we cannot evict // torrents. The feature is not enabled if (!m_user_load_torrent) return; // if it's already evicted, there's nothing to do if (!t->is_loaded() || !t->should_be_loaded()) return; TORRENT_ASSERT(t->next != NULL || t->prev != NULL || m_torrent_lru.front() == t); #if defined TORRENT_DEBUG && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS torrent* i = m_torrent_lru.front(); while (i != NULL && i != t) i = i->next; TORRENT_ASSERT(i == t); #endif int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit); // 0 means unlimited, never evict anything if (loaded_limit == 0) return; if (m_torrent_lru.size() > loaded_limit) { // just evict the torrent m_stats_counters.inc_stats_counter(counters::torrent_evicted_counter); TORRENT_ASSERT(t->is_pinned() == false); t->unload(); m_torrent_lru.erase(t); return; } // move this torrent to be the first to be evicted whenever // another torrent need its slot bump_torrent(t, false); } void session_impl::evict_torrents_except(torrent* ignore) { if (!m_user_load_torrent) return; int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit); // 0 means unlimited, never evict anything if (loaded_limit == 0) return; // if the torrent we're ignoring (i.e. making room for), allow // one more torrent in the list. if (ignore->next != NULL || ignore->prev != NULL || m_torrent_lru.front() == ignore) { #ifdef TORRENT_DEBUG torrent* i = m_torrent_lru.front(); while (i != NULL && i != ignore) i = i->next; TORRENT_ASSERT(i == ignore); #endif ++loaded_limit; } while (m_torrent_lru.size() >= loaded_limit) { // we're at the limit of loaded torrents. Find the least important // torrent and unload it. This is done with an LRU. torrent* i = m_torrent_lru.front(); if (i == ignore) { i = i->next; if (i == NULL) break; } m_stats_counters.inc_stats_counter(counters::torrent_evicted_counter); TORRENT_ASSERT(i->is_pinned() == false); i->unload(); m_torrent_lru.erase(i); } } bool session_impl::load_torrent(torrent* t) { TORRENT_ASSERT(is_single_thread()); evict_torrents_except(t); // we wouldn't be loading the torrent if it was already // in the LRU (and loaded) TORRENT_ASSERT(t->next == NULL && t->prev == NULL && m_torrent_lru.front() != t); TORRENT_ASSERT(m_user_load_torrent); // now, load t into RAM std::vector buffer; error_code ec; m_user_load_torrent(t->info_hash(), buffer, ec); if (ec) { t->set_error(ec, torrent_status::error_file_metadata); t->pause(false); return false; } bool ret = t->load(buffer); if (ret) bump_torrent(t); return ret; } void session_impl::deferred_submit_jobs() { if (m_deferred_submit_disk_jobs) return; m_deferred_submit_disk_jobs = true; m_io_service.post(boost::bind(&session_impl::submit_disk_jobs, this)); } void session_impl::submit_disk_jobs() { TORRENT_ASSERT(m_deferred_submit_disk_jobs); m_deferred_submit_disk_jobs = false; m_disk_thread.submit_jobs(); } // copies pointers to bandwidth channels from the peer classes // into the array. Only bandwidth channels with a bandwidth limit // is considered pertinent and copied // returns the number of pointers copied // channel is upload_channel or download_channel int session_impl::copy_pertinent_channels(peer_class_set const& set , int channel, bandwidth_channel** dst, int max) { int num_channels = set.num_classes(); int num_copied = 0; for (int i = 0; i < num_channels; ++i) { peer_class* pc = m_classes.at(set.class_at(i)); TORRENT_ASSERT(pc); if (pc == 0) continue; bandwidth_channel* chan = &pc->channel[channel]; // no need to include channels that don't have any bandwidth limits if (chan->throttle() == 0) continue; dst[num_copied] = chan; ++num_copied; if (num_copied == max) break; } return num_copied; } bool session_impl::use_quota_overhead(bandwidth_channel* ch, int amount) { ch->use_quota(amount); return (ch->throttle() > 0 && ch->throttle() < amount); } int session_impl::use_quota_overhead(peer_class_set& set, int amount_down, int amount_up) { int ret = 0; int num = set.num_classes(); for (int i = 0; i < num; ++i) { peer_class* p = m_classes.at(set.class_at(i)); if (p == 0) continue; bandwidth_channel* ch = &p->channel[peer_connection::download_channel]; if (use_quota_overhead(ch, amount_down)) ret |= 1 << peer_connection::download_channel; ch = &p->channel[peer_connection::upload_channel]; if (use_quota_overhead(ch, amount_up)) ret |= 1 << peer_connection::upload_channel; } return ret; } // session_impl is responsible for deleting 'pack' void session_impl::apply_settings_pack(boost::shared_ptr pack) { INVARIANT_CHECK; apply_settings_pack_impl(*pack); } settings_pack session_impl::get_settings() const { settings_pack ret; // TODO: it would be nice to reserve() these vectors up front for (int i = settings_pack::string_type_base; i < settings_pack::max_string_setting_internal; ++i) { ret.set_str(i, m_settings.get_str(i)); } for (int i = settings_pack::int_type_base; i < settings_pack::max_int_setting_internal; ++i) { ret.set_int(i, m_settings.get_int(i)); } for (int i = settings_pack::bool_type_base; i < settings_pack::max_bool_setting_internal; ++i) { ret.set_bool(i, m_settings.get_bool(i)); } return ret; } void session_impl::apply_settings_pack_impl(settings_pack const& pack) { bool reopen_listen_port = (pack.has_val(settings_pack::ssl_listen) && pack.get_int(settings_pack::ssl_listen) != m_settings.get_int(settings_pack::ssl_listen)) || (pack.has_val(settings_pack::listen_interfaces) && pack.get_str(settings_pack::listen_interfaces) != m_settings.get_str(settings_pack::listen_interfaces)); apply_pack(&pack, m_settings, this); m_disk_thread.set_settings(&pack, m_alerts); if (reopen_listen_port) { error_code ec; open_listen_port(); } } #ifndef TORRENT_NO_DEPRECATE void session_impl::set_settings(libtorrent::session_settings const& s) { INVARIANT_CHECK; TORRENT_ASSERT(is_single_thread()); settings_pack p = load_pack_from_struct(m_settings, s); apply_settings_pack_impl(p); } libtorrent::session_settings session_impl::deprecated_settings() const { libtorrent::session_settings ret; load_struct_from_settings(m_settings, ret); return ret; } #endif boost::optional session_impl::get_ipv6_interface() const { return m_ipv6_interface; } boost::optional session_impl::get_ipv4_interface() const { return m_ipv4_interface; } enum { listen_no_system_port = 0x02 }; listen_socket_t session_impl::setup_listener(std::string const& device , boost::asio::ip::tcp const& protocol, int port, int flags, error_code& ec) { int retries = m_settings.get_int(settings_pack::max_retry_port_bind); listen_socket_t ret; ret.ssl = flags & open_ssl_socket; int last_op = 0; listen_failed_alert::socket_type_t sock_type = (flags & open_ssl_socket) ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp; ret.sock.reset(new tcp::acceptor(m_io_service)); ret.sock->open(protocol, ec); last_op = listen_failed_alert::open; if (ec) { if (m_alerts.should_post()) m_alerts.emplace_alert(device, port, last_op , ec, sock_type); #ifndef TORRENT_DISABLE_LOGGING session_log("failed to open socket: %s: %s" , device.c_str(), ec.message().c_str()); #endif return ret; } { // this is best-effort. ignore errors error_code err; #ifdef TORRENT_WINDOWS ret.sock->set_option(exclusive_address_use(true), err); #else ret.sock->set_option(tcp::acceptor::reuse_address(true), err); #endif } #if TORRENT_USE_IPV6 if (protocol == boost::asio::ip::tcp::v6()) { error_code err; // ignore errors here ret.sock->set_option(boost::asio::ip::v6_only(true), err); #ifdef TORRENT_WINDOWS // enable Teredo on windows ret.sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err); #endif // TORRENT_WINDOWS } #endif // TORRENT_USE_IPV6 address bind_ip = bind_to_device(m_io_service, *ret.sock, protocol , device.c_str(), port, ec); while (ec == error_code(error::address_in_use) && retries > 0) { TORRENT_ASSERT_VAL(ec, ec); #ifndef TORRENT_DISABLE_LOGGING error_code ignore; session_log("failed to bind to interface [%s %d] \"%s\" : %s (%d) : %s " "(retries: %d)" , device.c_str(), port, bind_ip.to_string(ignore).c_str() , ec.category().name(), ec.value(), ec.message().c_str(), retries); #endif ec.clear(); TORRENT_ASSERT_VAL(!ec, ec); --retries; port += 1; bind_ip = bind_to_device(m_io_service, *ret.sock, protocol , device.c_str(), port, ec); last_op = listen_failed_alert::bind; } if (ec == error_code(error::address_in_use) && !(flags & listen_no_system_port)) { // instead of giving up, try let the OS pick a port port = 0; ec.clear(); bind_ip = bind_to_device(m_io_service, *ret.sock, protocol , device.c_str(), port, ec); last_op = listen_failed_alert::bind; } if (ec) { TORRENT_ASSERT_VAL(ec.value() != 0, ec); // not even that worked, give up if (m_alerts.should_post()) m_alerts.emplace_alert(device, port, last_op, ec, sock_type); #ifndef TORRENT_DISABLE_LOGGING error_code err; session_log("cannot to bind to interface [%s %d] \"%s : %s\": %s" , device.c_str(), port, bind_ip.to_string(err).c_str() , ec.category().name(), ec.message().c_str()); #endif return ret; } ret.external_port = ret.sock->local_endpoint(ec).port(); TORRENT_ASSERT(ret.external_port == port || port == 0); last_op = listen_failed_alert::get_peer_name; if (!ec) { ret.sock->listen(m_settings.get_int(settings_pack::listen_queue_size), ec); last_op = listen_failed_alert::listen; } if (ec) { if (m_alerts.should_post()) m_alerts.emplace_alert(device, port, last_op, ec, sock_type); #ifndef TORRENT_DISABLE_LOGGING session_log("cannot listen on interface \"%s\": %s" , device.c_str(), ec.message().c_str()); #endif return ret; } // if we asked the system to listen on port 0, which // socket did it end up choosing? if (port == 0) { port = ret.sock->local_endpoint(ec).port(); last_op = listen_failed_alert::get_peer_name; if (ec) { if (m_alerts.should_post()) m_alerts.emplace_alert(device, port, last_op, ec, sock_type); #ifndef TORRENT_DISABLE_LOGGING session_log("failed to get peer name \"%s\": %s" , device.c_str(), ec.message().c_str()); #endif return ret; } } #ifndef TORRENT_DISABLE_LOGGING session_log(" listening on: %s external port: %d" , print_endpoint(tcp::endpoint(bind_ip, port)).c_str(), ret.external_port); #endif return ret; } void session_impl::open_listen_port() { #ifndef TORRENT_DISABLE_LOGGING session_log("open listen port"); #endif TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(!m_abort); int flags = m_settings.get_bool(settings_pack::listen_system_port_fallback) ? 0 : listen_no_system_port; error_code ec; int listen_port_retries = m_settings.get_int(settings_pack::max_retry_port_bind); retry: // close the open listen sockets // close the listen sockets for (std::list::iterator i = m_listen_sockets.begin() , end(m_listen_sockets.end()); i != end; ++i) i->sock->close(ec); m_listen_sockets.clear(); m_stats_counters.set_value(counters::has_incoming_connections, 0); ec.clear(); if (m_abort) return; m_ipv6_interface = boost::none; m_ipv4_interface = boost::none; // TODO: instead of having a special case for this, just make the // default listen interfaces be "0.0.0.0:6881,[::]:6881" and use // the generic path. That would even allow for not listening at all. if (m_listen_interfaces.empty()) { // this means we should open two listen sockets // one for IPv4 and one for IPv6 listen_socket_t s = setup_listener("0.0.0.0", boost::asio::ip::tcp::v4() , m_listen_interface.port() , flags, ec); if (!ec && s.sock) { // update the listen_interface member with the // actual port we ended up listening on, so that the other // sockets can be bound to the same one m_listen_interface.port(s.external_port); TORRENT_ASSERT(!m_abort); m_listen_sockets.push_back(s); } #ifdef TORRENT_USE_OPENSSL if (m_settings.get_int(settings_pack::ssl_listen)) { s = setup_listener("0.0.0.0", boost::asio::ip::tcp::v4() , m_settings.get_int(settings_pack::ssl_listen) , flags | open_ssl_socket, ec); if (!ec && s.sock) { TORRENT_ASSERT(!m_abort); m_listen_sockets.push_back(s); } } #endif #if TORRENT_USE_IPV6 // only try to open the IPv6 port if IPv6 is installed if (supports_ipv6()) { s = setup_listener("::", boost::asio::ip::tcp::v6() , m_listen_interface.port() , flags, ec); if (!ec && s.sock) { TORRENT_ASSERT(!m_abort); m_listen_sockets.push_back(s); } #ifdef TORRENT_USE_OPENSSL if (m_settings.get_int(settings_pack::ssl_listen)) { s.ssl = true; s = setup_listener("::", boost::asio::ip::tcp::v6() , m_settings.get_int(settings_pack::ssl_listen) , flags | open_ssl_socket, ec); if (!ec && s.sock) { TORRENT_ASSERT(!m_abort); m_listen_sockets.push_back(s); } } #endif // TORRENT_USE_OPENSSL } #endif // TORRENT_USE_IPV6 } else { // TODO: 2 the udp socket(s) should be using the same generic // mechanism and not be restricted to a single one // we should open a one listen socket for each entry in the // listen_interfaces list for (int i = 0; i < m_listen_interfaces.size(); ++i) { std::string const& device = m_listen_interfaces[i].first; int const port = m_listen_interfaces[i].second; int num_device_fails = 0; #if TORRENT_USE_IPV6 const int first_family = 0; #else const int first_family = 1; #endif boost::asio::ip::tcp protocol[] = { boost::asio::ip::tcp::v6(), boost::asio::ip::tcp::v4() }; for (int address_family = first_family; address_family < 2; ++address_family) { error_code err; address test_family = address::from_string(device.c_str(), err); if (!err && test_family.is_v4() != address_family && !is_any(test_family)) continue; listen_socket_t s = setup_listener(device, protocol[address_family] , port, flags, ec); if (ec == error_code(boost::system::errc::no_such_device, generic_category())) { ++num_device_fails; continue; } if (!ec && s.sock) { // update the listen_interface member with the // actual port we ended up listening on, so that the other // sockets can be bound to the same one m_listen_interface.port(s.external_port); TORRENT_ASSERT(!m_abort); m_listen_sockets.push_back(s); tcp::endpoint bind_ep = s.sock->local_endpoint(ec); #if TORRENT_USE_IPV6 if (bind_ep.address().is_v6()) m_ipv6_interface = bind_ep; else #endif m_ipv4_interface = bind_ep; } #ifdef TORRENT_USE_OPENSSL if (m_settings.get_int(settings_pack::ssl_listen)) { listen_socket_t ssl_s = setup_listener(device , protocol[address_family] , m_settings.get_int(settings_pack::ssl_listen) , flags | open_ssl_socket, ec); if (!ec && ssl_s.sock) { TORRENT_ASSERT(!m_abort); m_listen_sockets.push_back(ssl_s); } } #endif } } } if (m_listen_sockets.empty() && ec) { #ifndef TORRENT_DISABLE_LOGGING session_log("cannot bind TCP listen socket to interface \"%s\": %s" , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); #endif if (m_alerts.should_post()) m_alerts.emplace_alert( m_listen_interface.address().to_string() , m_listen_interface.port() , listen_failed_alert::bind , ec, listen_failed_alert::tcp); if (listen_port_retries > 0) { m_listen_interface.port(m_listen_interface.port() + 1); // update the actual port m_listen_interface was derived from also if (!m_listen_interfaces.empty()) m_listen_interfaces[0].second += 1; --listen_port_retries; goto retry; } return; } #if TORRENT_USE_IPV6 bool want_v6 = (m_ipv6_interface && is_any(m_ipv6_interface->address())) || m_listen_interfaces.empty(); #else bool const want_v6 = false; #endif bool want_v4 = (m_ipv4_interface && is_any(m_ipv4_interface->address())) || m_listen_interfaces.empty(); if (want_v6 || want_v4) { // set our main IPv4 and IPv6 interfaces // used to send to the tracker std::vector ifs = enum_net_interfaces(m_io_service, ec); for (std::vector::const_iterator i = ifs.begin() , end(ifs.end()); i != end && (want_v4 || want_v6); ++i) { address const& addr = i->interface_address; if (want_v4 && addr.is_v4() && !is_local(addr) && !is_loopback(addr)) { m_ipv4_interface = tcp::endpoint(addr, m_listen_interface.port()); want_v4 = false; } #if TORRENT_USE_IPV6 else if (want_v6 && addr.is_v6() && !is_local(addr) && !is_loopback(addr)) { m_ipv6_interface = tcp::endpoint(addr, m_listen_interface.port()); want_v6 = false; } #endif } } #ifdef TORRENT_USE_OPENSSL int const ssl_port = m_settings.get_int(settings_pack::ssl_listen); udp::endpoint ssl_bind_if(m_listen_interface.address(), ssl_port); tcp::endpoint ssl_bind_ep(m_listen_interface.address(), ssl_port); // if ssl port is 0, we don't want to listen on an SSL port if (ssl_port != 0) { // if the socket is already open with the port we want, just leave it error_code err; if (!m_ssl_udp_socket.is_open() || m_ssl_udp_socket.local_endpoint(err) != ssl_bind_ep || err) { m_ssl_udp_socket.bind(ssl_bind_if, ec); if (ec) { #ifndef TORRENT_DISABLE_LOGGING session_log("SSL: cannot bind to UDP interface \"%s\": %s" , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); #endif if (m_alerts.should_post()) { m_alerts.emplace_alert(ssl_bind_if.address().to_string() , ssl_port, listen_failed_alert::bind, ec, listen_failed_alert::utp_ssl); } m_ssl_udp_socket.close(); ec.clear(); } else { maybe_update_udp_mapping(0, true, ssl_port, ssl_port); maybe_update_udp_mapping(1, true, ssl_port, ssl_port); } m_ssl_udp_socket.set_proxy_settings(proxy()); } } else { m_ssl_udp_socket.close(); // if there are mappings for the SSL socket, delete them now if (m_ssl_udp_mapping[0] != -1 && m_natpmp) { m_natpmp->delete_mapping(m_ssl_udp_mapping[0]); m_ssl_udp_mapping[0] = -1; } if (m_ssl_udp_mapping[1] != -1 && m_upnp) { m_upnp->delete_mapping(m_ssl_udp_mapping[1]); m_ssl_udp_mapping[1] = -1; } } #endif // TORRENT_USE_OPENSSL udp::endpoint const udp_bind_ep(m_listen_interface.address() , m_listen_interface.port()); // if the socket is already open with the port we want, just leave it error_code err; if (!m_udp_socket.is_open() || m_udp_socket.local_endpoint(err) != m_listen_interface || err) { m_udp_socket.bind(udp_bind_ep, ec); if (ec) { #ifndef TORRENT_DISABLE_LOGGING session_log("cannot bind to UDP interface \"%s\": %s" , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); #endif if (m_alerts.should_post()) { m_alerts.emplace_alert(m_listen_interface.address().to_string() , m_listen_interface.port() , listen_failed_alert::bind , ec, listen_failed_alert::udp); } m_udp_socket.close(); if (listen_port_retries > 0) { m_listen_interface.port(m_listen_interface.port() + 1); // update the actual port m_listen_interface was derived from also if (!m_listen_interfaces.empty()) m_listen_interfaces[0].second += 1; --listen_port_retries; goto retry; } return; } else { m_external_udp_port = m_udp_socket.local_port(); maybe_update_udp_mapping(0, false, m_listen_interface.port(), m_listen_interface.port()); maybe_update_udp_mapping(1, false, m_listen_interface.port(), m_listen_interface.port()); } m_udp_socket.set_proxy_settings(proxy()); } // we made it! now post all the listen_succeeded_alerts for (std::list::iterator i = m_listen_sockets.begin() , end(m_listen_sockets.end()); i != end; ++i) { listen_succeeded_alert::socket_type_t const socket_type = i->ssl ? listen_succeeded_alert::tcp_ssl : listen_succeeded_alert::tcp; if (!m_alerts.should_post()) continue; error_code error; tcp::endpoint bind_ep = i->sock->local_endpoint(error); if (error) continue; m_alerts.emplace_alert(bind_ep, socket_type); } #ifdef TORRENT_USE_OPENSSL if (m_ssl_udp_socket.is_open()) { if (m_alerts.should_post()) m_alerts.emplace_alert( ssl_bind_ep, listen_succeeded_alert::utp_ssl); } #endif if (m_udp_socket.is_open()) { if (m_alerts.should_post()) m_alerts.emplace_alert(m_listen_interface , listen_succeeded_alert::udp); } if (m_settings.get_int(settings_pack::peer_tos) != 0) { update_peer_tos(); } ec.clear(); set_socket_buffer_size(m_udp_socket, m_settings, ec); if (ec) { if (m_alerts.should_post()) m_alerts.emplace_alert(udp::endpoint(), ec); } // initiate accepting on the listen sockets for (std::list::iterator i = m_listen_sockets.begin() , end(m_listen_sockets.end()); i != end; ++i) async_accept(i->sock, i->ssl); #if TORRENT_USE_I2P open_new_incoming_i2p_connection(); #endif if (!m_listen_sockets.empty()) { tcp::endpoint local = m_listen_sockets.front().sock->local_endpoint(ec); if (!ec) remap_tcp_ports(3, local.port(), ssl_listen_port()); } } void session_impl::remap_tcp_ports(boost::uint32_t mask, int tcp_port, int ssl_port) { #ifndef TORRENT_USE_OPENSSL TORRENT_UNUSED(ssl_port); #endif if ((mask & 1) && m_natpmp) { if (m_tcp_mapping[0] != -1) m_natpmp->delete_mapping(m_tcp_mapping[0]); m_tcp_mapping[0] = m_natpmp->add_mapping(natpmp::tcp, tcp_port, tcp_port); #ifdef TORRENT_USE_OPENSSL if (m_ssl_tcp_mapping[0] != -1) { m_natpmp->delete_mapping(m_ssl_tcp_mapping[0]); m_ssl_tcp_mapping[0] = -1; } if (ssl_port > 0) m_ssl_tcp_mapping[0] = m_natpmp->add_mapping(natpmp::tcp , ssl_port, ssl_port); #endif } if ((mask & 2) && m_upnp) { if (m_tcp_mapping[1] != -1) m_upnp->delete_mapping(m_tcp_mapping[1]); m_tcp_mapping[1] = m_upnp->add_mapping(upnp::tcp, tcp_port , tcp::endpoint(m_listen_interface.address(), tcp_port)); #ifdef TORRENT_USE_OPENSSL if (m_ssl_tcp_mapping[1] != -1) { m_upnp->delete_mapping(m_ssl_tcp_mapping[1]); m_ssl_tcp_mapping[1] = -1; } if (ssl_port > 0) m_ssl_tcp_mapping[1] = m_upnp->add_mapping(upnp::tcp , ssl_port, tcp::endpoint(m_listen_interface.address(), ssl_port)); #endif } } void session_impl::update_i2p_bridge() { // we need this socket to be open before we // can make name lookups for trackers for instance. // pause the session now and resume it once we've // established the i2p SAM connection #if TORRENT_USE_I2P if (m_settings.get_str(settings_pack::i2p_hostname).empty()) { error_code ec; m_i2p_conn.close(ec); return; } m_i2p_conn.open(m_settings.get_str(settings_pack::i2p_hostname) , m_settings.get_int(settings_pack::i2p_port) , boost::bind(&session_impl::on_i2p_open, this, _1)); #endif } #if TORRENT_USE_I2P proxy_settings session_impl::i2p_proxy() const { proxy_settings ret; ret.hostname = m_settings.get_str(settings_pack::i2p_hostname); ret.type = settings_pack::i2p_proxy; ret.port = m_settings.get_int(settings_pack::i2p_port); return ret; } void session_impl::on_i2p_open(error_code const& ec) { if (ec) { if (m_alerts.should_post()) m_alerts.emplace_alert(ec); #ifndef TORRENT_DISABLE_LOGGING session_log("i2p open failed (%d) %s", ec.value(), ec.message().c_str()); #endif } // now that we have our i2p connection established // it's OK to start torrents and use this socket to // do i2p name lookups open_new_incoming_i2p_connection(); } void session_impl::open_new_incoming_i2p_connection() { if (!m_i2p_conn.is_open()) return; if (m_i2p_listen_socket) return; m_i2p_listen_socket = boost::shared_ptr(new socket_type(m_io_service)); bool ret = instantiate_connection(m_io_service, m_i2p_conn.proxy() , *m_i2p_listen_socket, NULL, NULL, true, false); TORRENT_ASSERT_VAL(ret, ret); TORRENT_UNUSED(ret); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::on_i2p_accept"); #endif i2p_stream& s = *m_i2p_listen_socket->get(); s.set_command(i2p_stream::cmd_accept); s.set_session_id(m_i2p_conn.session_id()); s.async_connect(tcp::endpoint(address_v4::any(), m_listen_interface.port()) , boost::bind(&session_impl::on_i2p_accept, this, m_i2p_listen_socket, _1)); } void session_impl::on_i2p_accept(boost::shared_ptr const& s , error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("session_impl::on_i2p_accept"); #endif m_i2p_listen_socket.reset(); if (e == boost::asio::error::operation_aborted) return; if (e) { if (m_alerts.should_post()) m_alerts.emplace_alert("i2p" , m_listen_interface.port() , listen_failed_alert::accept , e, listen_failed_alert::i2p); #ifndef TORRENT_DISABLE_LOGGING session_log("cannot bind to port %d: %s" , m_listen_interface.port(), e.message().c_str()); #endif return; } open_new_incoming_i2p_connection(); incoming_connection(s); } #endif bool session_impl::incoming_packet(error_code const& ec , udp::endpoint const& ep, char const*, int) { m_stats_counters.inc_stats_counter(counters::on_udp_counter); if (ec) { // don't bubble up operation aborted errors to the user if (ec != boost::asio::error::operation_aborted && m_alerts.should_post()) m_alerts.emplace_alert(ep, ec); #ifndef TORRENT_DISABLE_LOGGING session_log("UDP socket error: (%d) %s", ec.value(), ec.message().c_str()); #endif } return false; } void session_impl::async_accept(boost::shared_ptr const& listener, bool ssl) { TORRENT_ASSERT(!m_abort); shared_ptr c(new socket_type(m_io_service)); tcp::socket* str = 0; #ifdef TORRENT_USE_OPENSSL if (ssl) { // accept connections initializing the SSL connection to // use the generic m_ssl_ctx context. However, since it has // the servername callback set on it, we will switch away from // this context into a specific torrent once we start handshaking c->instantiate >(m_io_service, &m_ssl_ctx); str = &c->get >()->next_layer(); } else #endif { c->instantiate(m_io_service); str = c->get(); } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::on_accept_connection"); #endif #ifdef TORRENT_USE_OPENSSL TORRENT_ASSERT(ssl == is_ssl(*c)); #endif listener->async_accept(*str , boost::bind(&session_impl::on_accept_connection, this, c , boost::weak_ptr(listener), _1, ssl)); } void session_impl::on_accept_connection(shared_ptr const& s , weak_ptr listen_socket, error_code const& e, bool ssl) { #if defined TORRENT_ASIO_DEBUGGING complete_async("session_impl::on_accept_connection"); #endif m_stats_counters.inc_stats_counter(counters::on_accept_counter); TORRENT_ASSERT(is_single_thread()); boost::shared_ptr listener = listen_socket.lock(); if (!listener) return; if (e == boost::asio::error::operation_aborted) return; if (m_abort) return; error_code ec; if (e) { tcp::endpoint ep = listener->local_endpoint(ec); #ifndef TORRENT_DISABLE_LOGGING session_log("error accepting connection on '%s': %s" , print_endpoint(ep).c_str(), e.message().c_str()); #endif #ifdef TORRENT_WINDOWS // Windows sometimes generates this error. It seems to be // non-fatal and we have to do another async_accept. if (e.value() == ERROR_SEM_TIMEOUT) { async_accept(listener, ssl); return; } #endif #ifdef TORRENT_BSD // Leopard sometimes generates an "invalid argument" error. It seems to be // non-fatal and we have to do another async_accept. if (e.value() == EINVAL) { async_accept(listener, ssl); return; } #endif if (e == boost::system::errc::too_many_files_open) { // if we failed to accept an incoming connection // because we have too many files open, try again // and lower the number of file descriptors used // elsewere. if (m_settings.get_int(settings_pack::connections_limit) > 10) { // now, disconnect a random peer torrent_map::iterator i = std::max_element(m_torrents.begin() , m_torrents.end() , boost::bind(&torrent::num_peers , boost::bind(&torrent_map::value_type::second, _1)) < boost::bind(&torrent::num_peers , boost::bind(&torrent_map::value_type::second, _2)) ); if (m_alerts.should_post()) m_alerts.emplace_alert( torrent_handle(), performance_alert::too_few_file_descriptors); if (i != m_torrents.end()) { i->second->disconnect_peers(1, e); } m_settings.set_int(settings_pack::connections_limit , std::max(10, int(m_connections.size()))); } // try again, but still alert the user of the problem async_accept(listener, ssl); } if (m_alerts.should_post()) { error_code err; m_alerts.emplace_alert(ep.address().to_string() , ep.port(), listen_failed_alert::accept, e , ssl ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp); } return; } async_accept(listener, ssl); // don't accept any connections from our local sockets if (m_settings.get_bool(settings_pack::force_proxy)) return; #ifdef TORRENT_USE_OPENSSL if (ssl) { TORRENT_ASSERT(is_ssl(*s)); // for SSL connections, incoming_connection() is called // after the handshake is done #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::ssl_handshake"); #endif s->get >()->async_accept_handshake( boost::bind(&session_impl::ssl_handshake, this, _1, s)); m_incoming_sockets.insert(s); } else #endif { incoming_connection(s); } } #ifdef TORRENT_USE_OPENSSL void session_impl::on_incoming_utp_ssl(boost::shared_ptr const& s) { TORRENT_ASSERT(is_ssl(*s)); // for SSL connections, incoming_connection() is called // after the handshake is done #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::ssl_handshake"); #endif s->get >()->async_accept_handshake( boost::bind(&session_impl::ssl_handshake, this, _1, s)); m_incoming_sockets.insert(s); } // to test SSL connections, one can use this openssl command template: // // openssl s_client -cert .pem -key .pem // -CAfile .pem -debug -connect 127.0.0.1:4433 -tls1 // -servername void session_impl::ssl_handshake(error_code const& ec, boost::shared_ptr s) { #if defined TORRENT_ASIO_DEBUGGING complete_async("session_impl::ssl_handshake"); #endif TORRENT_ASSERT(is_ssl(*s)); m_incoming_sockets.erase(s); error_code e; tcp::endpoint endp = s->remote_endpoint(e); if (e) return; #ifndef TORRENT_DISABLE_LOGGING session_log(" *** peer SSL handshake done [ ip: %s ec: %s socket: %s ]" , print_endpoint(endp).c_str(), ec.message().c_str(), s->type_name()); #endif if (ec) { if (m_alerts.should_post()) { m_alerts.emplace_alert(torrent_handle(), endp , peer_id(), op_ssl_handshake, ec); } return; } incoming_connection(s); } #endif // TORRENT_USE_OPENSSL void session_impl::incoming_connection(boost::shared_ptr const& s) { TORRENT_ASSERT(is_single_thread()); #ifdef TORRENT_USE_OPENSSL // add the current time to the PRNG, to add more unpredictability boost::uint64_t now = clock_type::now().time_since_epoch().count(); // assume 12 bits of entropy (i.e. about 8 milliseconds) RAND_add(&now, 8, 1.5); #endif if (m_paused) { #ifndef TORRENT_DISABLE_LOGGING session_log(" <== INCOMING CONNECTION [ ignored, paused ]"); #endif return; } error_code ec; // we got a connection request! tcp::endpoint endp = s->remote_endpoint(ec); if (ec) { #ifndef TORRENT_DISABLE_LOGGING session_log(" <== INCOMING CONNECTION FAILED, could " "not retrieve remote endpoint: %s" , ec.message().c_str()); #endif return; } #ifndef TORRENT_DISABLE_LOGGING session_log(" <== INCOMING CONNECTION %s type: %s" , print_endpoint(endp).c_str(), s->type_name()); #endif if (!m_settings.get_bool(settings_pack::enable_incoming_utp) && is_utp(*s)) { #ifndef TORRENT_DISABLE_LOGGING session_log(" rejected uTP connection"); #endif if (m_alerts.should_post()) m_alerts.emplace_alert(torrent_handle() , endp.address(), peer_blocked_alert::utp_disabled); return; } if (!m_settings.get_bool(settings_pack::enable_incoming_tcp) && s->get()) { #ifndef TORRENT_DISABLE_LOGGING session_log(" rejected TCP connection"); #endif if (m_alerts.should_post()) m_alerts.emplace_alert(torrent_handle() , endp.address(), peer_blocked_alert::tcp_disabled); return; } // if there are outgoing interfaces specified, verify this // peer is correctly bound to on of them if (!m_settings.get_str(settings_pack::outgoing_interfaces).empty()) { tcp::endpoint local = s->local_endpoint(ec); if (ec) { #ifndef TORRENT_DISABLE_LOGGING session_log(" rejected connection: (%d) %s", ec.value() , ec.message().c_str()); #endif return; } if (!verify_bound_address(local.address() , is_utp(*s), ec)) { if (ec) { #ifndef TORRENT_DISABLE_LOGGING session_log(" rejected connection, not allowed local interface: (%d) %s" , ec.value(), ec.message().c_str()); #endif return; } #ifndef TORRENT_DISABLE_LOGGING error_code err; session_log(" rejected connection, not allowed local interface: %s" , local.address().to_string(err).c_str()); #endif if (m_alerts.should_post()) m_alerts.emplace_alert(torrent_handle() , endp.address(), peer_blocked_alert::invalid_local_interface); return; } } // local addresses do not count, since it's likely // coming from our own client through local service discovery // and it does not reflect whether or not a router is open // for incoming connections or not. if (!is_local(endp.address())) m_stats_counters.set_value(counters::has_incoming_connections, 1); // this filter is ignored if a single torrent // is set to ignore the filter, since this peer might be // for that torrent if (m_stats_counters[counters::non_filter_torrents] == 0 && m_ip_filter && (m_ip_filter->access(endp.address()) & ip_filter::blocked)) { #ifndef TORRENT_DISABLE_LOGGING session_log("filtered blocked ip"); #endif if (m_alerts.should_post()) m_alerts.emplace_alert(torrent_handle() , endp.address(), peer_blocked_alert::ip_filter); return; } // check if we have any active torrents // if we don't reject the connection if (m_torrents.empty()) { #ifndef TORRENT_DISABLE_LOGGING session_log(" There are no torrents, disconnect"); #endif return; } // figure out which peer classes this is connections has, // to get connection_limit_factor peer_class_set pcs; set_peer_classes(&pcs, endp.address(), s->type()); int connection_limit_factor = 0; for (int i = 0; i < pcs.num_classes(); ++i) { int pc = pcs.class_at(i); if (m_classes.at(pc) == NULL) continue; int f = m_classes.at(pc)->connection_limit_factor; if (connection_limit_factor < f) connection_limit_factor = f; } if (connection_limit_factor == 0) connection_limit_factor = 100; boost::uint64_t limit = m_settings.get_int(settings_pack::connections_limit); limit = limit * 100 / connection_limit_factor; // don't allow more connections than the max setting // weighed by the peer class' setting bool reject = num_connections() >= limit + m_settings.get_int(settings_pack::connections_slack); if (reject) { if (m_alerts.should_post()) { m_alerts.emplace_alert(torrent_handle(), endp, peer_id() , op_bittorrent, s->type() , error_code(errors::too_many_connections) , close_no_reason); } #ifndef TORRENT_DISABLE_LOGGING session_log("number of connections limit exceeded (conns: %d, limit: %d, slack: %d), connection rejected" , num_connections(), m_settings.get_int(settings_pack::connections_limit) , m_settings.get_int(settings_pack::connections_slack)); #endif return; } // if we don't have any active torrents, there's no // point in accepting this connection. If, however, // the setting to start up queued torrents when they // get an incoming connection is enabled, we cannot // perform this check. if (!m_settings.get_bool(settings_pack::incoming_starts_queued_torrents)) { bool has_active_torrent = false; for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { if (i->second->allows_peers()) { has_active_torrent = true; break; } } if (!has_active_torrent) { #ifndef TORRENT_DISABLE_LOGGING session_log(" There are no _active_ torrents, disconnect"); #endif return; } } m_stats_counters.inc_stats_counter(counters::incoming_connections); if (m_alerts.should_post()) m_alerts.emplace_alert(s->type(), endp); setup_socket_buffers(*s); peer_connection_args pack; pack.ses = this; pack.sett = &m_settings; pack.stats_counters = &m_stats_counters; pack.allocator = this; pack.disk_thread = &m_disk_thread; pack.ios = &m_io_service; pack.tor = boost::weak_ptr(); pack.s = s; pack.endp = endp; pack.peerinfo = 0; boost::shared_ptr c = boost::make_shared(boost::cref(pack)); #if TORRENT_USE_ASSERTS c->m_in_constructor = false; #endif if (!c->is_disconnecting()) { // in case we've exceeded the limit, let this peer know that // as soon as it's received the handshake, it needs to either // disconnect or pick another peer to disconnect if (num_connections() >= limit) c->peer_exceeds_limit(); TORRENT_ASSERT(!c->m_in_constructor); m_connections.insert(c); c->start(); } } void session_impl::setup_socket_buffers(socket_type& s) { error_code ec; set_socket_buffer_size(s, m_settings, ec); } // if cancel_with_cq is set, the peer connection is // currently expected to be scheduled for a connection // with the connection queue, and should be cancelled // TODO: should this function take a shared_ptr instead? void session_impl::close_connection(peer_connection* p , error_code const& ec) { TORRENT_ASSERT(is_single_thread()); boost::shared_ptr sp(p->self()); // someone else is holding a reference, it's important that // it's destructed from the network thread. Make sure the // last reference is held by the network thread. if (!sp.unique()) m_undead_peers.push_back(sp); // too expensive // INVARIANT_CHECK; #ifdef TORRENT_DEBUG // for (aux::session_impl::torrent_map::const_iterator i = m_torrents.begin() // , end(m_torrents.end()); i != end; ++i) // TORRENT_ASSERT(!i->second->has_peer((peer_connection*)p)); #endif #ifndef TORRENT_DISABLE_LOGGING session_log(" CLOSING CONNECTION %s : %s" , print_endpoint(p->remote()).c_str(), ec.message().c_str()); #else TORRENT_UNUSED(ec); #endif TORRENT_ASSERT(p->is_disconnecting()); TORRENT_ASSERT(sp.use_count() > 0); connection_map::iterator i = m_connections.find(sp); // make sure the next disk peer round-robin cursor stays valid if (i != m_connections.end()) m_connections.erase(i); } #ifndef TORRENT_NO_DEPRECATE peer_id session_impl::deprecated_get_peer_id() const { return generate_peer_id(m_settings); } #endif void session_impl::set_key(int key) { m_key = key; } int session_impl::next_port() const { int start = m_settings.get_int(settings_pack::outgoing_port); int num = m_settings.get_int(settings_pack::num_outgoing_ports); std::pair out_ports(start, start + num); if (m_next_port < out_ports.first || m_next_port > out_ports.second) m_next_port = out_ports.first; int port = m_next_port; ++m_next_port; if (m_next_port > out_ports.second) m_next_port = out_ports.first; #ifndef TORRENT_DISABLE_LOGGING session_log(" *** BINDING OUTGOING CONNECTION [ port: %d ]", port); #endif return port; } int session_impl::rate_limit(peer_class_t c, int channel) const { TORRENT_ASSERT(channel >= 0 && channel <= 1); if (channel < 0 || channel > 1) return 0; peer_class const* pc = m_classes.at(c); if (pc == 0) return 0; return pc->channel[channel].throttle(); } int session_impl::upload_rate_limit(peer_class_t c) const { return rate_limit(c, peer_connection::upload_channel); } int session_impl::download_rate_limit(peer_class_t c) const { return rate_limit(c, peer_connection::download_channel); } void session_impl::set_rate_limit(peer_class_t c, int channel, int limit) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(limit >= -1); TORRENT_ASSERT(channel >= 0 && channel <= 1); if (channel < 0 || channel > 1) return; peer_class* pc = m_classes.at(c); if (pc == 0) return; if (limit <= 0) limit = 0; else limit = std::min(limit, std::numeric_limits::max() - 1); pc->channel[channel].throttle(limit); } void session_impl::set_upload_rate_limit(peer_class_t c, int limit) { set_rate_limit(c, peer_connection::upload_channel, limit); } void session_impl::set_download_rate_limit(peer_class_t c, int limit) { set_rate_limit(c, peer_connection::download_channel, limit); } #if TORRENT_USE_ASSERTS bool session_impl::has_peer(peer_connection const* p) const { TORRENT_ASSERT(is_single_thread()); return std::find_if(m_connections.begin(), m_connections.end() , boost::bind(&boost::shared_ptr::get, _1) == p) != m_connections.end(); } bool session_impl::any_torrent_has_peer(peer_connection const* p) const { for (aux::session_impl::torrent_map::const_iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) if (i->second->has_peer(p)) return true; return false; } #endif void session_impl::sent_bytes(int bytes_payload, int bytes_protocol) { m_stats_counters.inc_stats_counter(counters::sent_bytes , bytes_payload + bytes_protocol); m_stats_counters.inc_stats_counter(counters::sent_payload_bytes , bytes_payload); m_stat.sent_bytes(bytes_payload, bytes_protocol); } void session_impl::received_bytes(int bytes_payload, int bytes_protocol) { m_stats_counters.inc_stats_counter(counters::recv_bytes , bytes_payload + bytes_protocol); m_stats_counters.inc_stats_counter(counters::recv_payload_bytes , bytes_payload); m_stat.received_bytes(bytes_payload, bytes_protocol); } void session_impl::trancieve_ip_packet(int bytes, bool ipv6) { // one TCP/IP packet header for the packet // sent or received, and one for the ACK // The IPv4 header is 20 bytes // and IPv6 header is 40 bytes int const header = (ipv6 ? 40 : 20) + 20; int const mtu = 1500; int const packet_size = mtu - header; int const overhead = std::max(1, (bytes + packet_size - 1) / packet_size) * header; m_stats_counters.inc_stats_counter(counters::sent_ip_overhead_bytes , overhead); m_stats_counters.inc_stats_counter(counters::recv_ip_overhead_bytes , overhead); m_stat.trancieve_ip_packet(bytes, ipv6); } void session_impl::sent_syn(bool ipv6) { int const overhead = ipv6 ? 60 : 40; m_stats_counters.inc_stats_counter(counters::sent_ip_overhead_bytes , overhead); m_stat.sent_syn(ipv6); } void session_impl::received_synack(bool ipv6) { int const overhead = ipv6 ? 60 : 40; m_stats_counters.inc_stats_counter(counters::sent_ip_overhead_bytes , overhead); m_stats_counters.inc_stats_counter(counters::recv_ip_overhead_bytes , overhead); m_stat.received_synack(ipv6); } void session_impl::on_tick(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("session_impl::on_tick"); #endif m_stats_counters.inc_stats_counter(counters::on_tick_counter); TORRENT_ASSERT(is_single_thread()); // submit all disk jobs when we leave this function deferred_submit_jobs(); aux::update_time_now(); time_point now = aux::time_now(); // remove undead peers that only have this list as their reference keeping them alive if (!m_undead_peers.empty()) { std::vector >::iterator remove_it = std::remove_if(m_undead_peers.begin(), m_undead_peers.end() , boost::bind(&boost::shared_ptr::unique, _1)); m_undead_peers.erase(remove_it, m_undead_peers.end()); if (m_undead_peers.empty()) { // we just removed our last "undead" peer (i.e. a peer connection // that had some external reference to it). It's now safe to // shut-down if (m_abort) { m_io_service.post(boost::bind(&session_impl::abort_stage2, this)); } } } // too expensive // INVARIANT_CHECK; // we have to keep ticking the utp socket manager // until they're all closed // we also have to keep updating the aux time while // there are outstanding announces if (m_abort) { if (m_utp_socket_manager.num_sockets() == 0 && m_undead_peers.empty() && m_tracker_manager.empty()) return; #if defined TORRENT_ASIO_DEBUGGING fprintf(stderr, "uTP sockets left: %d undead-peers left: %d\n" , m_utp_socket_manager.num_sockets() , int(m_undead_peers.size())); #endif } if (e == boost::asio::error::operation_aborted) return; if (e) { #ifndef TORRENT_DISABLE_LOGGING session_log("*** TICK TIMER FAILED %s", e.message().c_str()); #endif std::abort(); } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::on_tick"); #endif error_code ec; m_timer.expires_at(now + milliseconds(m_settings.get_int(settings_pack::tick_interval)), ec); m_timer.async_wait(make_tick_handler(boost::bind(&session_impl::on_tick, this, _1))); m_download_rate.update_quotas(now - m_last_tick); m_upload_rate.update_quotas(now - m_last_tick); m_last_tick = now; m_utp_socket_manager.tick(now); #ifdef TORRENT_USE_OPENSSL m_ssl_utp_socket_manager.tick(now); #endif // only tick the following once per second if (now - m_last_second_tick < seconds(1)) return; #ifndef TORRENT_DISABLE_DHT if (m_dht && m_dht_interval_update_torrents < 40 && m_dht_interval_update_torrents != int(m_torrents.size())) update_dht_announce_interval(); #endif int tick_interval_ms = int(total_milliseconds(now - m_last_second_tick)); m_last_second_tick = now; m_tick_residual += tick_interval_ms - 1000; boost::int64_t const stime = session_time(); if (stime > 65000) { // we're getting close to the point where our timestamps // in torrent_peer are wrapping. We need to step all counters back // four hours. This means that any timestamp that refers to a time // more than 18.2 - 4 = 14.2 hours ago, will be incremented to refer to // 14.2 hours ago. m_created += hours(4); const int four_hours = 60 * 60 * 4; for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { i->second->step_session_time(four_hours); } } #ifndef TORRENT_DISABLE_EXTENSIONS if (m_session_extension_features & plugin::tick_feature) { for (ses_extension_list_t::const_iterator i = m_ses_extensions.begin() , end(m_ses_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->on_tick(); } TORRENT_CATCH(std::exception&) {} } } #endif // don't do any of the following while we're shutting down if (m_abort) return; #ifndef TORRENT_NO_DEPRECATE // -------------------------------------------------------------- // RSS feeds // -------------------------------------------------------------- if (now > m_next_rss_update) update_rss_feeds(); #endif switch (m_settings.get_int(settings_pack::mixed_mode_algorithm)) { case settings_pack::prefer_tcp: set_upload_rate_limit(m_tcp_peer_class, 0); set_download_rate_limit(m_tcp_peer_class, 0); break; case settings_pack::peer_proportional: { int num_peers[2][2] = {{0, 0}, {0, 0}}; for (connection_map::iterator i = m_connections.begin() , end(m_connections.end());i != end; ++i) { peer_connection& p = *(*i); if (p.in_handshake()) continue; int protocol = 0; if (is_utp(*p.get_socket())) protocol = 1; if (p.download_queue().size() + p.request_queue().size() > 0) ++num_peers[protocol][peer_connection::download_channel]; if (p.upload_queue().size() > 0) ++num_peers[protocol][peer_connection::upload_channel]; } peer_class* pc = m_classes.at(m_tcp_peer_class); bandwidth_channel* tcp_channel = pc->channel; int stat_rate[] = {m_stat.upload_rate(), m_stat.download_rate() }; // never throttle below this int lower_limit[] = {5000, 30000}; for (int i = 0; i < 2; ++i) { // if there are no uploading uTP peers, don't throttle TCP up if (num_peers[1][i] == 0) { tcp_channel[i].throttle(0); } else { if (num_peers[0][i] == 0) num_peers[0][i] = 1; int total_peers = num_peers[0][i] + num_peers[1][i]; // this are 64 bits since it's multiplied by the number // of peers, which otherwise might overflow an int boost::uint64_t rate = stat_rate[i]; tcp_channel[i].throttle((std::max)(int(rate * num_peers[0][i] / total_peers), lower_limit[i])); } } } break; } // -------------------------------------------------------------- // auto managed torrent // -------------------------------------------------------------- if (!m_paused) m_auto_manage_time_scaler--; if (m_auto_manage_time_scaler < 0) { m_auto_manage_time_scaler = settings().get_int(settings_pack::auto_manage_interval); recalculate_auto_managed_torrents(); } // -------------------------------------------------------------- // check for incoming connections that might have timed out // -------------------------------------------------------------- for (connection_map::iterator i = m_connections.begin(); i != m_connections.end();) { peer_connection* p = (*i).get(); ++i; // ignore connections that already have a torrent, since they // are ticked through the torrents' second_tick if (!p->associated_torrent().expired()) continue; // TODO: have a separate list for these connections, instead of having to loop through all of them int timeout = m_settings.get_int(settings_pack::handshake_timeout); #if TORRENT_USE_I2P timeout *= is_i2p(*p->get_socket()) ? 4 : 1; #endif if (m_last_tick - p->connected_time () > seconds(timeout)) p->disconnect(errors::timed_out, op_bittorrent); } // -------------------------------------------------------------- // second_tick every torrent (that wants it) // -------------------------------------------------------------- #if TORRENT_DEBUG_STREAMING > 0 printf("\033[2J\033[0;0H"); #endif std::vector& want_tick = m_torrent_lists[torrent_want_tick]; for (int i = 0; i < int(want_tick.size()); ++i) { torrent& t = *want_tick[i]; TORRENT_ASSERT(t.want_tick()); TORRENT_ASSERT(!t.is_aborted()); t.second_tick(tick_interval_ms); // if the call to second_tick caused the torrent // to no longer want to be ticked (i.e. it was // removed from the list) we need to back up the counter // to not miss the torrent after it if (!t.want_tick()) --i; } // TODO: this should apply to all bandwidth channels if (m_settings.get_bool(settings_pack::rate_limit_ip_overhead)) { int up_limit = upload_rate_limit(m_global_class); int down_limit = download_rate_limit(m_global_class); if (down_limit > 0 && m_stat.download_ip_overhead() >= down_limit && m_alerts.should_post()) { m_alerts.emplace_alert(torrent_handle() , performance_alert::download_limit_too_low); } if (up_limit > 0 && m_stat.upload_ip_overhead() >= up_limit && m_alerts.should_post()) { m_alerts.emplace_alert(torrent_handle() , performance_alert::upload_limit_too_low); } } m_peak_up_rate = (std::max)(m_stat.upload_rate(), m_peak_up_rate); m_peak_down_rate = (std::max)(m_stat.download_rate(), m_peak_down_rate); m_stat.second_tick(tick_interval_ms); // -------------------------------------------------------------- // scrape paused torrents that are auto managed // (unless the session is paused) // -------------------------------------------------------------- if (!is_paused()) { INVARIANT_CHECK; --m_auto_scrape_time_scaler; if (m_auto_scrape_time_scaler <= 0) { std::vector& want_scrape = m_torrent_lists[torrent_want_scrape]; m_auto_scrape_time_scaler = m_settings.get_int(settings_pack::auto_scrape_interval) / (std::max)(1, int(want_scrape.size())); if (m_auto_scrape_time_scaler < m_settings.get_int(settings_pack::auto_scrape_min_interval)) m_auto_scrape_time_scaler = m_settings.get_int(settings_pack::auto_scrape_min_interval); if (!want_scrape.empty() && !m_abort) { if (m_next_scrape_torrent >= int(want_scrape.size())) m_next_scrape_torrent = 0; torrent& t = *want_scrape[m_next_scrape_torrent]; TORRENT_ASSERT(t.is_paused() && t.is_auto_managed()); // false means it's not triggered by the user, but automatically // by libtorrent t.scrape_tracker(-1, false); ++m_next_scrape_torrent; if (m_next_scrape_torrent >= int(want_scrape.size())) m_next_scrape_torrent = 0; } } } // -------------------------------------------------------------- // refresh torrent suggestions // -------------------------------------------------------------- --m_suggest_timer; if (m_settings.get_int(settings_pack::suggest_mode) != settings_pack::no_piece_suggestions && m_suggest_timer <= 0) { INVARIANT_CHECK; m_suggest_timer = 10; torrent_map::iterator least_recently_refreshed = m_torrents.begin(); if (m_next_suggest_torrent >= int(m_torrents.size())) m_next_suggest_torrent = 0; std::advance(least_recently_refreshed, m_next_suggest_torrent); if (least_recently_refreshed != m_torrents.end()) least_recently_refreshed->second->refresh_suggest_pieces(); ++m_next_suggest_torrent; } #ifndef TORRENT_NO_DEPRECATE // -------------------------------------------------------------- // refresh explicit disk read cache // -------------------------------------------------------------- --m_cache_rotation_timer; if (m_settings.get_bool(settings_pack::explicit_read_cache) && m_cache_rotation_timer <= 0) { INVARIANT_CHECK; m_cache_rotation_timer = m_settings.get_int(settings_pack::explicit_cache_interval); torrent_map::iterator least_recently_refreshed = m_torrents.begin(); if (m_next_explicit_cache_torrent >= int(m_torrents.size())) m_next_explicit_cache_torrent = 0; std::advance(least_recently_refreshed, m_next_explicit_cache_torrent); // how many blocks does this torrent get? int cache_size = (std::max)(0, m_settings.get_int(settings_pack::cache_size) * 9 / 10); if (m_connections.empty()) { // if we don't have any connections at all, split the // cache evenly across all torrents cache_size = cache_size / (std::max)(int(m_torrents.size()), 1); } else { cache_size = cache_size * least_recently_refreshed->second->num_peers() / m_connections.size(); } if (least_recently_refreshed != m_torrents.end()) least_recently_refreshed->second->refresh_explicit_cache(cache_size); ++m_next_explicit_cache_torrent; } #endif // -------------------------------------------------------------- // connect new peers // -------------------------------------------------------------- try_connect_more_peers(); // -------------------------------------------------------------- // unchoke set calculations // -------------------------------------------------------------- m_unchoke_time_scaler--; if (m_unchoke_time_scaler <= 0 && !m_connections.empty()) { m_unchoke_time_scaler = settings().get_int(settings_pack::unchoke_interval); recalculate_unchoke_slots(); } // -------------------------------------------------------------- // optimistic unchoke calculation // -------------------------------------------------------------- m_optimistic_unchoke_time_scaler--; if (m_optimistic_unchoke_time_scaler <= 0) { m_optimistic_unchoke_time_scaler = settings().get_int(settings_pack::optimistic_unchoke_interval); recalculate_optimistic_unchoke_slots(); } // -------------------------------------------------------------- // disconnect peers when we have too many // -------------------------------------------------------------- --m_disconnect_time_scaler; if (m_disconnect_time_scaler <= 0) { m_disconnect_time_scaler = m_settings.get_int(settings_pack::peer_turnover_interval); // if the connections_limit is too low, the disconnect // logic is disabled, since it is too disruptive if (m_settings.get_int(settings_pack::connections_limit) > 5) { if (num_connections() >= m_settings.get_int(settings_pack::connections_limit) * m_settings.get_int(settings_pack::peer_turnover_cutoff) / 100 && !m_torrents.empty()) { // every 90 seconds, disconnect the worst peers // if we have reached the connection limit torrent_map::iterator i = std::max_element(m_torrents.begin(), m_torrents.end() , boost::bind(&torrent::num_peers, boost::bind(&torrent_map::value_type::second, _1)) < boost::bind(&torrent::num_peers, boost::bind(&torrent_map::value_type::second, _2))); TORRENT_ASSERT(i != m_torrents.end()); int peers_to_disconnect = (std::min)((std::max)( int(i->second->num_peers() * m_settings.get_int(settings_pack::peer_turnover) / 100), 1) , i->second->num_connect_candidates()); i->second->disconnect_peers(peers_to_disconnect , error_code(errors::optimistic_disconnect)); } else { // if we haven't reached the global max. see if any torrent // has reached its local limit for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { boost::shared_ptr t = i->second; // ths disconnect logic is disabled for torrents with // too low connection limit if (t->num_peers() < t->max_connections() * m_settings.get_int(settings_pack::peer_turnover_cutoff) / 100 || t->max_connections() < 6) continue; int peers_to_disconnect = (std::min)((std::max)(int(t->num_peers() * m_settings.get_int(settings_pack::peer_turnover) / 100), 1) , t->num_connect_candidates()); t->disconnect_peers(peers_to_disconnect , error_code(errors::optimistic_disconnect)); } } } } m_tick_residual = m_tick_residual % 1000; // m_peer_pool.release_memory(); } void session_impl::on_close_file(error_code const& e) { if (e) return; m_disk_thread.files().close_oldest(); // re-issue the timer update_close_file_interval(); } namespace { // returns the index of the first set bit. int log2(boost::uint32_t v) { // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn static const int MultiplyDeBruijnBitPosition[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; v |= v >> 1; // first round down to one less than a power of 2 v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; return MultiplyDeBruijnBitPosition[boost::uint32_t(v * 0x07C4ACDDU) >> 27]; } } // anonymous namespace void session_impl::received_buffer(int s) { int index = (std::min)(log2(s >> 3), 17); m_stats_counters.inc_stats_counter(counters::socket_recv_size3 + index); } void session_impl::sent_buffer(int s) { int index = (std::min)(log2(s >> 3), 17); m_stats_counters.inc_stats_counter(counters::socket_send_size3 + index); } #ifndef TORRENT_NO_DEPRECATE void session_impl::update_rss_feeds() { time_t now_posix = time(0); time_point min_update = max_time(); time_point now = aux::time_now(); for (std::vector >::iterator i = m_feeds.begin(), end(m_feeds.end()); i != end; ++i) { feed& f = **i; int delta = f.next_update(now_posix); if (delta <= 0) delta = f.update_feed(); TORRENT_ASSERT(delta >= 0); time_point next_update = now + seconds(delta); if (next_update < min_update) min_update = next_update; } m_next_rss_update = min_update; } #endif void session_impl::prioritize_connections(boost::weak_ptr t) { m_prio_torrents.push_back(std::make_pair(t, 10)); } #ifndef TORRENT_DISABLE_DHT void session_impl::add_dht_node(udp::endpoint n) { TORRENT_ASSERT(is_single_thread()); #if !TORRENT_USE_IPV6 if (!n.address().is_v4()) return; #endif if (m_dht) m_dht->add_node(n); else m_dht_nodes.push_back(n); } bool session_impl::has_dht() const { return m_dht.get(); } void session_impl::prioritize_dht(boost::weak_ptr t) { TORRENT_ASSERT(!m_abort); if (m_abort) return; TORRENT_ASSERT(m_dht); m_dht_torrents.push_back(t); #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr tor = t.lock(); if (tor) session_log("prioritizing DHT announce: \"%s\"", tor->name().c_str()); #endif // trigger a DHT announce right away if we just added a new torrent and // there's no back-log. in the timer handler, as long as there are more // high priority torrents to be announced to the DHT, it will keep the // timer interval short until all torrents have been announced. if (m_dht_torrents.size() == 1) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::on_dht_announce"); #endif error_code ec; m_dht_announce_timer.expires_from_now(seconds(0), ec); m_dht_announce_timer.async_wait( bind(&session_impl::on_dht_announce, this, _1)); } } void session_impl::on_dht_announce(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("session_impl::on_dht_announce"); #endif TORRENT_ASSERT(is_single_thread()); if (e) { #ifndef TORRENT_DISABLE_LOGGING session_log("aborting DHT announce timer (%d): %s" , e.value(), e.message().c_str()); #endif return; } if (m_abort) { #ifndef TORRENT_DISABLE_LOGGING session_log("aborting DHT announce timer: m_abort set"); #endif return; } if (!m_dht) { m_dht_torrents.clear(); return; } TORRENT_ASSERT(m_dht); // announce to DHT every 15 minutes int delay = (std::max)(m_settings.get_int(settings_pack::dht_announce_interval) / (std::max)(int(m_torrents.size()), 1), 1); if (!m_dht_torrents.empty()) { // we have prioritized torrents that need // an initial DHT announce. Don't wait too long // until we announce those. delay = (std::min)(4, delay); } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::on_dht_announce"); #endif error_code ec; m_dht_announce_timer.expires_from_now(seconds(delay), ec); m_dht_announce_timer.async_wait( bind(&session_impl::on_dht_announce, this, _1)); if (!m_dht_torrents.empty()) { boost::shared_ptr t; do { t = m_dht_torrents.front().lock(); m_dht_torrents.pop_front(); } while (!t && !m_dht_torrents.empty()); if (t) { t->dht_announce(); return; } } if (m_torrents.empty()) return; if (m_next_dht_torrent == m_torrents.end()) m_next_dht_torrent = m_torrents.begin(); m_next_dht_torrent->second->dht_announce(); // TODO: 2 make a list for torrents that want to be announced on the DHT so we // don't have to loop over all torrents, just to find the ones that want to announce ++m_next_dht_torrent; if (m_next_dht_torrent == m_torrents.end()) m_next_dht_torrent = m_torrents.begin(); } #endif void session_impl::on_lsd_announce(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("session_impl::on_lsd_announce"); #endif m_stats_counters.inc_stats_counter(counters::on_lsd_counter); TORRENT_ASSERT(is_single_thread()); if (e) return; if (m_abort) return; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::on_lsd_announce"); #endif // announce on local network every 5 minutes int delay = (std::max)(m_settings.get_int(settings_pack::local_service_announce_interval) / (std::max)(int(m_torrents.size()), 1), 1); error_code ec; m_lsd_announce_timer.expires_from_now(seconds(delay), ec); m_lsd_announce_timer.async_wait( bind(&session_impl::on_lsd_announce, this, _1)); if (m_torrents.empty()) return; if (m_next_lsd_torrent == m_torrents.end()) m_next_lsd_torrent = m_torrents.begin(); m_next_lsd_torrent->second->lsd_announce(); ++m_next_lsd_torrent; if (m_next_lsd_torrent == m_torrents.end()) m_next_lsd_torrent = m_torrents.begin(); } void session_impl::auto_manage_checking_torrents(std::vector& list , int& limit) { for (std::vector::iterator i = list.begin() , end(list.end()); i != end; ++i) { torrent* t = *i; TORRENT_ASSERT(t->state() == torrent_status::checking_files); TORRENT_ASSERT(t->is_auto_managed()); if (limit <= 0) { t->pause(); } else { t->resume(); if (!t->should_check_files()) continue; t->start_checking(); --limit; } } } void session_impl::auto_manage_torrents(std::vector& list , int& dht_limit, int& tracker_limit , int& lsd_limit, int& hard_limit, int type_limit) { for (std::vector::iterator i = list.begin() , end(list.end()); i != end; ++i) { torrent* t = *i; TORRENT_ASSERT(t->state() != torrent_status::checking_files); // inactive torrents don't count (and if you configured them to do so, // the torrent won't say it's inactive) if (hard_limit > 0 && t->is_inactive()) { t->set_announce_to_dht(--dht_limit >= 0); t->set_announce_to_trackers(--tracker_limit >= 0); t->set_announce_to_lsd(--lsd_limit >= 0); --hard_limit; #ifndef TORRENT_DISABLE_LOGGING if (!t->allows_peers()) t->log_to_all_peers("auto manager starting (inactive) torrent"); #endif t->set_allow_peers(true); t->update_gauge(); t->update_want_peers(); continue; } if (type_limit > 0 && hard_limit > 0) { t->set_announce_to_dht(--dht_limit >= 0); t->set_announce_to_trackers(--tracker_limit >= 0); t->set_announce_to_lsd(--lsd_limit >= 0); --hard_limit; --type_limit; #ifndef TORRENT_DISABLE_LOGGING if (!t->allows_peers()) t->log_to_all_peers("auto manager starting torrent"); #endif t->set_allow_peers(true); t->update_gauge(); t->update_want_peers(); continue; } #ifndef TORRENT_DISABLE_LOGGING if (t->allows_peers()) t->log_to_all_peers("auto manager pausing torrent"); #endif // use graceful pause for auto-managed torrents t->set_allow_peers(false, torrent::flag_graceful_pause | torrent::flag_clear_disk_cache); t->set_announce_to_dht(false); t->set_announce_to_trackers(false); t->set_announce_to_lsd(false); t->update_gauge(); t->update_want_peers(); } } int session_impl::get_int_setting(int n) const { int const v = settings().get_int(n); if (v < 0) return (std::numeric_limits::max)(); return v; } void session_impl::recalculate_auto_managed_torrents() { INVARIANT_CHECK; m_last_auto_manage = time_now(); m_need_auto_manage = false; if (is_paused()) return; // make copies of the lists of torrents that we want to consider for auto // management. We need copies because they will be sorted. std::vector checking = torrent_list(session_interface::torrent_checking_auto_managed); std::vector downloaders = torrent_list(session_interface::torrent_downloading_auto_managed); std::vector seeds = torrent_list(session_interface::torrent_seeding_auto_managed); // these counters are set to the number of torrents // of each kind we're allowed to have active int downloading_limit = get_int_setting(settings_pack::active_downloads); int seeding_limit = get_int_setting(settings_pack::active_seeds); int checking_limit = get_int_setting(settings_pack::active_checking); int dht_limit = get_int_setting(settings_pack::active_dht_limit); int tracker_limit = get_int_setting(settings_pack::active_tracker_limit); int lsd_limit = get_int_setting(settings_pack::active_lsd_limit); int hard_limit = get_int_setting(settings_pack::active_limit); // if hard_limit is <= 0, all torrents in these lists should be paused. // The order is not relevant if (hard_limit > 0) { // we only need to sort the first n torrents here, where n is the number // of checking torrents we allow. The rest of the list is still used to // make sure the remaining torrents are paused, but their order is not // relevant std::partial_sort(checking.begin(), checking.begin() + (std::min)(checking_limit, int(checking.size())), checking.end() , boost::bind(&torrent::sequence_number, _1) < boost::bind(&torrent::sequence_number, _2)); std::partial_sort(downloaders.begin(), downloaders.begin() + (std::min)(hard_limit, int(downloaders.size())), downloaders.end() , boost::bind(&torrent::sequence_number, _1) < boost::bind(&torrent::sequence_number, _2)); std::partial_sort(seeds.begin(), seeds.begin() + (std::min)(hard_limit, int(seeds.size())), seeds.end() , boost::bind(&torrent::seed_rank, _1, boost::ref(m_settings)) > boost::bind(&torrent::seed_rank, _2, boost::ref(m_settings))); } auto_manage_checking_torrents(checking, checking_limit); if (settings().get_bool(settings_pack::auto_manage_prefer_seeds)) { auto_manage_torrents(seeds, dht_limit, tracker_limit, lsd_limit , hard_limit, seeding_limit); auto_manage_torrents(downloaders, dht_limit, tracker_limit, lsd_limit , hard_limit, downloading_limit); } else { auto_manage_torrents(downloaders, dht_limit, tracker_limit, lsd_limit , hard_limit, downloading_limit); auto_manage_torrents(seeds, dht_limit, tracker_limit, lsd_limit , hard_limit, seeding_limit); } } namespace { bool last_optimistic_unchoke_cmp(torrent_peer const* const l , torrent_peer const* const r) { return l->last_optimistically_unchoked < r->last_optimistically_unchoked; } } void session_impl::recalculate_optimistic_unchoke_slots() { INVARIANT_CHECK; TORRENT_ASSERT(is_single_thread()); if (m_stats_counters[counters::num_unchoke_slots] == 0) return; std::vector opt_unchoke; // collect the currently optimistically unchoked peers here, so we can // choke them when we've found new optimistic unchoke candidates. std::vector prev_opt_unchoke; // TODO: 3 it would probably make sense to have a separate list of peers // that are eligible for optimistic unchoke, similar to the torrents // perhaps this could even iterate over the pool allocators of // torrent_peer objects. It could probably be done in a single pass and // collect the n best candidates. maybe just a queue of peers would make // even more sense, just pick the next peer in the queue for unchoking. It // would be O(1). for (connection_map::iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { peer_connection* p = i->get(); TORRENT_ASSERT(p); torrent_peer* pi = p->peer_info_struct(); if (!pi) continue; if (pi->web_seed) continue; if (pi->optimistically_unchoked) { prev_opt_unchoke.push_back(pi); } torrent* t = p->associated_torrent().lock().get(); if (!t) continue; // TODO: 3 peers should know whether their torrent is paused or not, // instead of having to ask it over and over again if (t->is_paused()) continue; if (!p->is_connecting() && !p->is_disconnecting() && p->is_peer_interested() && t->free_upload_slots() && (p->is_choked() || pi->optimistically_unchoked) && !p->ignore_unchoke_slots() && t->valid_metadata()) { opt_unchoke.push_back(pi); } } // find the peers that has been waiting the longest to be optimistically // unchoked int num_opt_unchoke = m_settings.get_int(settings_pack::num_optimistic_unchoke_slots); int const allowed_unchoke_slots = m_stats_counters[counters::num_unchoke_slots]; if (num_opt_unchoke == 0) num_opt_unchoke = (std::max)(1, allowed_unchoke_slots / 5); if (num_opt_unchoke > int(opt_unchoke.size())) num_opt_unchoke = int(opt_unchoke.size()); // find the n best optimistic unchoke candidates std::partial_sort(opt_unchoke.begin() , opt_unchoke.begin() + num_opt_unchoke , opt_unchoke.end(), &last_optimistic_unchoke_cmp); #ifndef TORRENT_DISABLE_EXTENSIONS if (m_session_extension_features & plugin::optimistic_unchoke_feature) { // if there is an extension that wants to reorder the optimistic // unchoke peers, first convert the vector into one containing // peer_connection_handles, since that's the exported API std::vector peers; peers.reserve(opt_unchoke.size()); for (std::vector::iterator i = opt_unchoke.begin() , end(opt_unchoke.end()); i != end; ++i) { peers.push_back(peer_connection_handle(static_cast((*i)->connection)->self())); } for (ses_extension_list_t::iterator i = m_ses_extensions.begin() , end(m_ses_extensions.end()); i != end; ++i) { if ((*i)->on_optimistic_unchoke(peers)) break; } // then convert back to the internal torrent_peer pointers opt_unchoke.clear(); for (std::vector::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { opt_unchoke.push_back(i->native_handle()->peer_info_struct()); } } #endif // unchoke the first num_opt_unchoke peers in the candidate set // and make sure that the others are choked std::vector::iterator opt_unchoke_end = opt_unchoke.begin() + num_opt_unchoke; for (std::vector::iterator i = opt_unchoke.begin(); i != opt_unchoke_end; ++i) { torrent_peer* pi = *i; peer_connection* p = static_cast(pi->connection); if (pi->optimistically_unchoked) { #ifndef TORRENT_DISABLE_LOGGING p->peer_log(peer_log_alert::info, "OPTIMISTIC UNCHOKE" , "already unchoked | session-time: %d" , pi->last_optimistically_unchoked); #endif TORRENT_ASSERT(!pi->connection->is_choked()); // remove this peer from prev_opt_unchoke, to prevent us from // choking it later. This peer gets another round of optimistic // unchoke std::vector::iterator existing = std::find(prev_opt_unchoke.begin(), prev_opt_unchoke.end(), pi); TORRENT_ASSERT(existing != prev_opt_unchoke.end()); prev_opt_unchoke.erase(existing); } else { TORRENT_ASSERT(p->is_choked()); boost::shared_ptr t = p->associated_torrent().lock(); bool ret = t->unchoke_peer(*p, true); TORRENT_ASSERT(ret); if (ret) { pi->optimistically_unchoked = true; m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic); pi->last_optimistically_unchoked = boost::uint16_t(session_time()); #ifndef TORRENT_DISABLE_LOGGING p->peer_log(peer_log_alert::info, "OPTIMISTIC UNCHOKE" , "session-time: %d", pi->last_optimistically_unchoked); #endif } } } // now, choke all the previous optimistically unchoked peers for (std::vector::iterator i = prev_opt_unchoke.begin() , end(prev_opt_unchoke.end()); i != end; ++i) { torrent_peer* pi = *i; TORRENT_ASSERT(pi->optimistically_unchoked); peer_connection* p = static_cast(pi->connection); boost::shared_ptr t = p->associated_torrent().lock(); pi->optimistically_unchoked = false; m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1); t->choke_peer(*p); } // if we have too many unchoked peers now, we need to trigger the regular // choking logic to choke some if (m_stats_counters[counters::num_unchoke_slots] < m_stats_counters[counters::num_peers_up_unchoked_all]) { m_unchoke_time_scaler = 0; } } void session_impl::try_connect_more_peers() { if (m_abort) return; if (num_connections() >= m_settings.get_int(settings_pack::connections_limit)) return; // this is the maximum number of connections we will // attempt this tick int max_connections = m_settings.get_int(settings_pack::connection_speed); // this loop will "hand out" connection_speed to the torrents, in a round // robin fashion, so that every torrent is equally likely to connect to a // peer // boost connections are connections made by torrent connection // boost, which are done immediately on a tracker response. These // connections needs to be deducted from the regular connection attempt // quota for this tick if (m_boost_connections > 0) { if (m_boost_connections > max_connections) { m_boost_connections -= max_connections; max_connections = 0; } else { max_connections -= m_boost_connections; m_boost_connections = 0; } } // zero connections speeds are allowed, we just won't make any connections if (max_connections <= 0) return; // TODO: use a lower limit than m_settings.connections_limit // to allocate the to 10% or so of connection slots for incoming // connections // cap this at max - 1, since we may add one below int const limit = std::min(m_settings.get_int(settings_pack::connections_limit) - num_connections(), std::numeric_limits::max() - 1); // this logic is here to smooth out the number of new connection // attempts over time, to prevent connecting a large number of // sockets, wait 10 seconds, and then try again if (m_settings.get_bool(settings_pack::smooth_connects) && max_connections > (limit+1) / 2) max_connections = (limit+1) / 2; std::vector& want_peers_download = m_torrent_lists[torrent_want_peers_download]; std::vector& want_peers_finished = m_torrent_lists[torrent_want_peers_finished]; // if no torrent want any peers, just return if (want_peers_download.empty() && want_peers_finished.empty()) return; // if we don't have any connection attempt quota, return if (max_connections <= 0) return; INVARIANT_CHECK; int steps_since_last_connect = 0; int num_torrents = int(want_peers_finished.size() + want_peers_download.size()); for (;;) { if (m_next_downloading_connect_torrent >= int(want_peers_download.size())) m_next_downloading_connect_torrent = 0; if (m_next_finished_connect_torrent >= int(want_peers_finished.size())) m_next_finished_connect_torrent = 0; torrent* t = NULL; // there are prioritized torrents. Pick one of those while (!m_prio_torrents.empty()) { t = m_prio_torrents.front().first.lock().get(); --m_prio_torrents.front().second; if (m_prio_torrents.front().second > 0 && t != NULL && t->want_peers()) break; m_prio_torrents.pop_front(); t = NULL; } if (t == NULL) { if ((m_download_connect_attempts >= m_settings.get_int( settings_pack::connect_seed_every_n_download) && want_peers_finished.size()) || want_peers_download.empty()) { // pick a finished torrent to give a peer to t = want_peers_finished[m_next_finished_connect_torrent]; TORRENT_ASSERT(t->want_peers_finished()); m_download_connect_attempts = 0; ++m_next_finished_connect_torrent; } else { // pick a downloading torrent to give a peer to t = want_peers_download[m_next_downloading_connect_torrent]; TORRENT_ASSERT(t->want_peers_download()); ++m_download_connect_attempts; ++m_next_downloading_connect_torrent; } } TORRENT_ASSERT(t->want_peers()); TORRENT_ASSERT(t->allows_peers()); TORRENT_TRY { if (t->try_connect_peer()) { --max_connections; steps_since_last_connect = 0; m_stats_counters.inc_stats_counter(counters::connection_attempts); } } TORRENT_CATCH(std::bad_alloc&) { // we ran out of memory trying to connect to a peer // lower the global limit to the number of peers // we already have m_settings.set_int(settings_pack::connections_limit , std::max(2, num_connections())); } ++steps_since_last_connect; // if there are no more free connection slots, abort if (max_connections == 0) return; // there are no more torrents that want peers if (want_peers_download.empty() && want_peers_finished.empty()) break; // if we have gone a whole loop without // handing out a single connection, break if (steps_since_last_connect > num_torrents + 1) break; // maintain the global limit on number of connections if (num_connections() >= m_settings.get_int(settings_pack::connections_limit)) break; } } void session_impl::recalculate_unchoke_slots() { TORRENT_ASSERT(is_single_thread()); time_point const now = aux::time_now(); time_duration const unchoke_interval = now - m_last_choke; m_last_choke = now; // build list of all peers that are // unchokable. // TODO: 3 there should be a pre-calculated list of all peers eligible for // unchoking std::vector peers; for (connection_map::iterator i = m_connections.begin(); i != m_connections.end();) { boost::shared_ptr p = *i; TORRENT_ASSERT(p); ++i; torrent* const t = p->associated_torrent().lock().get(); torrent_peer* const pi = p->peer_info_struct(); if (p->ignore_unchoke_slots() || t == 0 || pi == 0 || pi->web_seed || t->is_paused()) { p->reset_choke_counters(); continue; } if (!p->is_peer_interested() || p->is_disconnecting() || p->is_connecting()) { // this peer is not unchokable. So, if it's unchoked // already, make sure to choke it. if (p->is_choked()) { p->reset_choke_counters(); continue; } if (pi && pi->optimistically_unchoked) { m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1); pi->optimistically_unchoked = false; // force a new optimistic unchoke m_optimistic_unchoke_time_scaler = 0; // TODO: post a message to have this happen // immediately instead of waiting for the next tick } t->choke_peer(*p); p->reset_choke_counters(); continue; } peers.push_back(p.get()); } // the unchoker wants an estimate of our upload rate capacity // (used by bittyrant) int max_upload_rate = upload_rate_limit(m_global_class); if (m_settings.get_int(settings_pack::choking_algorithm) == settings_pack::bittyrant_choker && max_upload_rate == 0) { // we don't know at what rate we can upload. If we have a // measurement of the peak, use that + 10kB/s, otherwise // assume 20 kB/s max_upload_rate = (std::max)(20000, m_peak_up_rate + 10000); if (m_alerts.should_post()) m_alerts.emplace_alert(torrent_handle() , performance_alert::bittyrant_with_no_uplimit); } int const allowed_upload_slots = unchoke_sort(peers, max_upload_rate , unchoke_interval, m_settings); m_stats_counters.set_value(counters::num_unchoke_slots , allowed_upload_slots); #ifndef TORRENT_DISABLE_LOGGING session_log("RECALCULATE UNCHOKE SLOTS: [ peers: %d " "eligible-peers: %d" " max_upload_rate: %d" " allowed-slots: %d ]" , int(m_connections.size()) , int(peers.size()) , max_upload_rate , allowed_upload_slots); #endif int const unchoked_counter_optimistic = m_stats_counters[counters::num_peers_up_unchoked_optimistic]; int const num_opt_unchoke = (unchoked_counter_optimistic == 0) ? (std::max)(1, allowed_upload_slots / 5) : unchoked_counter_optimistic; int unchoke_set_size = allowed_upload_slots - num_opt_unchoke; // go through all the peers and unchoke the first ones and choke // all the other ones. for (std::vector::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { peer_connection* p = *i; TORRENT_ASSERT(p); TORRENT_ASSERT(!p->ignore_unchoke_slots()); // this will update the m_uploaded_at_last_unchoke p->reset_choke_counters(); torrent* t = p->associated_torrent().lock().get(); TORRENT_ASSERT(t); if (unchoke_set_size > 0) { // yes, this peer should be unchoked if (p->is_choked()) { if (!t->unchoke_peer(*p)) continue; } --unchoke_set_size; TORRENT_ASSERT(p->peer_info_struct()); if (p->peer_info_struct()->optimistically_unchoked) { // force a new optimistic unchoke // since this one just got promoted into the // proper unchoke set m_optimistic_unchoke_time_scaler = 0; p->peer_info_struct()->optimistically_unchoked = false; m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1); } } else { // no, this peer should be choked TORRENT_ASSERT(p->peer_info_struct()); if (!p->is_choked() && !p->peer_info_struct()->optimistically_unchoked) t->choke_peer(*p); } } } void session_impl::cork_burst(peer_connection* p) { TORRENT_ASSERT(is_single_thread()); if (p->is_corked()) return; p->cork_socket(); m_delayed_uncorks.push_back(p); } void session_impl::do_delayed_uncork() { m_stats_counters.inc_stats_counter(counters::on_disk_counter); TORRENT_ASSERT(is_single_thread()); for (std::vector::iterator i = m_delayed_uncorks.begin() , end(m_delayed_uncorks.end()); i != end; ++i) { (*i)->uncork_socket(); } m_delayed_uncorks.clear(); } boost::shared_ptr session_impl::delay_load_torrent(sha1_hash const& info_hash , peer_connection* pc) { #ifndef TORRENT_DISABLE_EXTENSIONS for (ses_extension_list_t::iterator i = m_ses_extensions.begin() , end(m_ses_extensions.end()); i != end; ++i) { add_torrent_params p; if ((*i)->on_unknown_torrent(info_hash, peer_connection_handle(pc->self()), p)) { error_code ec; torrent_handle handle = add_torrent(p, ec); return handle.native_handle(); } } #else TORRENT_UNUSED(pc); TORRENT_UNUSED(info_hash); #endif return boost::shared_ptr(); } // the return value from this function is valid only as long as the // session is locked! boost::weak_ptr session_impl::find_torrent(sha1_hash const& info_hash) const { TORRENT_ASSERT(is_single_thread()); torrent_map::const_iterator i = m_torrents.find(info_hash); #if defined TORRENT_DEBUG && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS for (torrent_map::const_iterator j = m_torrents.begin(); j != m_torrents.end(); ++j) { torrent* p = boost::get_pointer(j->second); TORRENT_ASSERT(p); } #endif if (i != m_torrents.end()) return i->second; return boost::weak_ptr(); } void session_impl::insert_torrent(sha1_hash const& ih, boost::shared_ptr const& t , std::string uuid) { m_torrents.insert(std::make_pair(ih, t)); if (!uuid.empty()) m_uuids.insert(std::make_pair(uuid, t)); TORRENT_ASSERT(m_torrents.size() >= m_torrent_lru.size()); } void session_impl::set_queue_position(torrent* me, int p) { if (p >= 0 && me->queue_position() == -1) { for (session_impl::torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { torrent* t = i->second.get(); if (t->queue_position() >= p) { t->set_queue_position_impl(t->queue_position()+1); t->state_updated(); } if (t->queue_position() >= p) t->set_queue_position_impl(t->queue_position()+1); } ++m_max_queue_pos; me->set_queue_position_impl((std::min)(m_max_queue_pos, p)); } else if (p < 0) { TORRENT_ASSERT(me->queue_position() >= 0); TORRENT_ASSERT(p == -1); for (session_impl::torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { torrent* t = i->second.get(); if (t == me) continue; if (t->queue_position() == -1) continue; if (t->queue_position() >= me->queue_position()) { t->set_queue_position_impl(t->queue_position()-1); t->state_updated(); } } --m_max_queue_pos; me->set_queue_position_impl(p); } else if (p < me->queue_position()) { for (session_impl::torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { torrent* t = i->second.get(); if (t == me) continue; if (t->queue_position() == -1) continue; if (t->queue_position() >= p && t->queue_position() < me->queue_position()) { t->set_queue_position_impl(t->queue_position()+1); t->state_updated(); } } me->set_queue_position_impl(p); } else if (p > me->queue_position()) { for (session_impl::torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { torrent* t = i->second.get(); int pos = t->queue_position(); if (t == me) continue; if (pos == -1) continue; if (pos <= p && pos > me->queue_position() && pos != -1) { t->set_queue_position_impl(t->queue_position()-1); t->state_updated(); } } me->set_queue_position_impl((std::min)(m_max_queue_pos, p)); } trigger_auto_manage(); } #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) torrent const* session_impl::find_encrypted_torrent(sha1_hash const& info_hash , sha1_hash const& xor_mask) { sha1_hash obfuscated = info_hash; obfuscated ^= xor_mask; torrent_map::iterator i = m_obfuscated_torrents.find(obfuscated); if (i == m_obfuscated_torrents.end()) return NULL; return i->second.get(); } #endif boost::weak_ptr session_impl::find_torrent(std::string const& uuid) const { TORRENT_ASSERT(is_single_thread()); std::map >::const_iterator i = m_uuids.find(uuid); if (i != m_uuids.end()) return i->second; return boost::weak_ptr(); } #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS std::vector > session_impl::find_collection( std::string const& collection) const { std::vector > ret; for (session_impl::torrent_map::const_iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { boost::shared_ptr t = i->second; if (!t) continue; std::vector const& c = t->torrent_file().collections(); if (std::count(c.begin(), c.end(), collection) == 0) continue; ret.push_back(t); } return ret; } #endif //TORRENT_DISABLE_MUTABLE_TORRENTS namespace { // returns true if lhs is a better disconnect candidate than rhs bool compare_disconnect_torrent(session_impl::torrent_map::value_type const& lhs , session_impl::torrent_map::value_type const& rhs) { // a torrent with 0 peers is never a good disconnect candidate // since there's nothing to disconnect if ((lhs.second->num_peers() == 0) != (rhs.second->num_peers() == 0)) return lhs.second->num_peers() != 0; // other than that, always prefer to disconnect peers from seeding torrents // in order to not harm downloading ones if (lhs.second->is_seed() != rhs.second->is_seed()) return lhs.second->is_seed(); return lhs.second->num_peers() > rhs.second->num_peers(); } } // anonymous namespace boost::weak_ptr session_impl::find_disconnect_candidate_torrent() const { aux::session_impl::torrent_map::const_iterator i = std::min_element(m_torrents.begin(), m_torrents.end() , &compare_disconnect_torrent); TORRENT_ASSERT(i != m_torrents.end()); if (i == m_torrents.end()) return boost::shared_ptr(); return i->second; } #ifndef TORRENT_DISABLE_LOGGING TORRENT_FORMAT(2,3) void session_impl::session_log(char const* fmt, ...) const { if (!m_alerts.should_post()) return; va_list v; va_start(v, fmt); session_vlog(fmt, v); va_end(v); } TORRENT_FORMAT(2, 0) void session_impl::session_vlog(char const* fmt, va_list& v) const { if (!m_alerts.should_post()) return; char buf[1024]; vsnprintf(buf, sizeof(buf), fmt, v); m_alerts.emplace_alert(buf); } #endif void session_impl::get_torrent_status(std::vector* ret , boost::function const& pred , boost::uint32_t flags) const { for (torrent_map::const_iterator i = m_torrents.begin(), end(m_torrents.end()); i != end; ++i) { if (i->second->is_aborted()) continue; torrent_status st; i->second->status(&st, flags); if (!pred(st)) continue; ret->push_back(st); } } void session_impl::refresh_torrent_status(std::vector* ret , boost::uint32_t flags) const { for (std::vector::iterator i = ret->begin(), end(ret->end()); i != end; ++i) { boost::shared_ptr t = i->handle.m_torrent.lock(); if (!t) continue; t->status(&*i, flags); } } void session_impl::post_torrent_updates(boost::uint32_t flags) { INVARIANT_CHECK; TORRENT_ASSERT(is_single_thread()); std::vector& state_updates = m_torrent_lists[aux::session_impl::torrent_state_updates]; #if TORRENT_USE_ASSERTS m_posting_torrent_updates = true; #endif std::vector status; status.reserve(state_updates.size()); // TODO: it might be a nice feature here to limit the number of torrents // to send in a single update. By just posting the first n torrents, they // would nicely be round-robined because the torrent lists are always // pushed back. Perhaps the status_update_alert could even have a fixed // array of n entries rather than a vector, to further improve memory // locality. for (std::vector::iterator i = state_updates.begin() , end(state_updates.end()); i != end; ++i) { torrent* t = *i; TORRENT_ASSERT(t->m_links[aux::session_impl::torrent_state_updates].in_list()); status.push_back(torrent_status()); // querying accurate download counters may require // the torrent to be loaded. Loading a torrent, and evicting another // one will lead to calling state_updated(), which screws with // this list while we're working on it, and break things t->status(&status.back(), flags); t->clear_in_state_update(); } state_updates.clear(); #if TORRENT_USE_ASSERTS m_posting_torrent_updates = false; #endif m_alerts.emplace_alert(status); } void session_impl::post_session_stats() { m_disk_thread.update_stats_counters(m_stats_counters); #ifndef TORRENT_DISABLE_DHT if (m_dht) m_dht->update_stats_counters(m_stats_counters); #endif m_stats_counters.set_value(counters::limiter_up_queue , m_upload_rate.queue_size()); m_stats_counters.set_value(counters::limiter_down_queue , m_download_rate.queue_size()); m_stats_counters.set_value(counters::limiter_up_bytes , m_upload_rate.queued_bytes()); m_stats_counters.set_value(counters::limiter_down_bytes , m_download_rate.queued_bytes()); m_alerts.emplace_alert(m_stats_counters); } void session_impl::post_dht_stats() { std::vector requests; std::vector table; #ifndef TORRENT_DISABLE_DHT if (m_dht) m_dht->dht_status(table, requests); #endif m_alerts.emplace_alert(table, requests); } std::vector session_impl::get_torrents() const { std::vector ret; for (torrent_map::const_iterator i = m_torrents.begin(), end(m_torrents.end()); i != end; ++i) { if (i->second->is_aborted()) continue; ret.push_back(torrent_handle(i->second)); } return ret; } torrent_handle session_impl::find_torrent_handle(sha1_hash const& info_hash) { return torrent_handle(find_torrent(info_hash)); } void session_impl::async_add_torrent(add_torrent_params* params) { if (string_begins_no_case("file://", params->url.c_str()) && !params->ti) { m_disk_thread.async_load_torrent(params , boost::bind(&session_impl::on_async_load_torrent, this, _1)); return; } error_code ec; torrent_handle handle = add_torrent(*params, ec); delete params; } void session_impl::on_async_load_torrent(disk_io_job const* j) { add_torrent_params* params = static_cast(j->requester); error_code ec; torrent_handle handle; if (j->error.ec) { ec = j->error.ec; m_alerts.emplace_alert(handle, *params, ec); } else { params->url.clear(); params->ti = boost::shared_ptr(j->buffer.torrent_file); TORRENT_ASSERT(params->ti->is_valid()); TORRENT_ASSERT(params->ti->num_files() > 0); handle = add_torrent(*params, ec); } delete params; } #ifndef TORRENT_DISABLE_EXTENSIONS void session_impl::add_extensions_to_torrent( boost::shared_ptr const& torrent_ptr, void* userdata) { for (ses_extension_list_t::iterator i = m_ses_extensions.begin() , end(m_ses_extensions.end()); i != end; ++i) { boost::shared_ptr tp((*i)->new_torrent(torrent_ptr->get_handle(), userdata)); if (tp) torrent_ptr->add_extension(tp); } } #endif torrent_handle session_impl::add_torrent(add_torrent_params const& p , error_code& ec) { // params is updated by add_torrent_impl() add_torrent_params params = p; boost::shared_ptr torrent_ptr; bool added; boost::tie(torrent_ptr, added) = add_torrent_impl(params, ec); torrent_handle const handle(torrent_ptr); m_alerts.emplace_alert(handle, params, ec); if (!torrent_ptr) return handle; // params.info_hash should have been initialized by add_torrent_impl() TORRENT_ASSERT(params.info_hash != sha1_hash(0)); // --- PEERS --- (delete when merged to master) std::vector peers; parse_magnet_uri_peers(p.url, peers); for (std::vector::const_iterator i = peers.begin() , end(peers.end()); i != end; ++i) { torrent_ptr->add_peer(*i , peer_info::resume_data); } if (!peers.empty()) torrent_ptr->update_want_peers(); #ifndef TORRENT_DISABLE_DHT if (params.ti) { torrent_info::nodes_t const& nodes = params.ti->nodes(); for (std::vector >::const_iterator i = nodes.begin() , end(nodes.end()); i != end; ++i) { add_dht_node_name(*i); } } #endif #ifndef TORRENT_NO_DEPRECATE if (m_alerts.should_post()) m_alerts.emplace_alert(handle); #endif // if this was an existing torrent, we can't start it again, or add // another set of plugins etc. we're done if (!added) return handle; torrent_ptr->set_ip_filter(m_ip_filter); torrent_ptr->start(params); #ifndef TORRENT_DISABLE_EXTENSIONS typedef std::vector(torrent_handle const&, void*)> > torrent_plugins_t; for (torrent_plugins_t::const_iterator i = params.extensions.begin() , end(params.extensions.end()); i != end; ++i) { torrent_ptr->add_extension((*i)(handle, params.userdata)); } add_extensions_to_torrent(torrent_ptr, params.userdata); #endif #if TORRENT_HAS_BOOST_UNORDERED sha1_hash next_lsd(0); sha1_hash next_dht(0); if (m_next_lsd_torrent != m_torrents.end()) next_lsd = m_next_lsd_torrent->first; #ifndef TORRENT_DISABLE_DHT if (m_next_dht_torrent != m_torrents.end()) next_dht = m_next_dht_torrent->first; #endif float load_factor = m_torrents.load_factor(); #endif // TORRENT_HAS_BOOST_UNORDERED m_torrents.insert(std::make_pair(params.info_hash, torrent_ptr)); TORRENT_ASSERT(m_torrents.size() >= m_torrent_lru.size()); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) hasher h; h.update("req2", 4); h.update(params.info_hash.data(), 20); // this is SHA1("req2" + info-hash), used for // encrypted hand shakes m_obfuscated_torrents.insert(std::make_pair(h.final(), torrent_ptr)); #endif if (torrent_ptr->is_pinned() == false) { evict_torrents_except(torrent_ptr.get()); bump_torrent(torrent_ptr.get()); } #if TORRENT_HAS_BOOST_UNORDERED // if this insert made the hash grow, the iterators became invalid // we need to reset them if (m_torrents.load_factor() < load_factor) { // this indicates the hash table re-hashed if (!next_lsd.is_all_zeros()) m_next_lsd_torrent = m_torrents.find(next_lsd); #ifndef TORRENT_DISABLE_DHT if (!next_dht.is_all_zeros()) m_next_dht_torrent = m_torrents.find(next_dht); #endif } #endif // TORRENT_HAS_BOOST_UNORDERED if (!params.uuid.empty() || !params.url.empty()) m_uuids.insert(std::make_pair(params.uuid.empty() ? params.url : params.uuid, torrent_ptr)); // recalculate auto-managed torrents sooner (or put it off) // if another torrent will be added within one second from now // we want to put it off again anyway. So that while we're adding // a boat load of torrents, we postpone the recalculation until // we're done adding them all (since it's kind of an expensive operation) if (params.flags & add_torrent_params::flag_auto_managed) { const int max_downloading = settings().get_int(settings_pack::active_downloads); const int max_seeds = settings().get_int(settings_pack::active_seeds); const int max_active = settings().get_int(settings_pack::active_limit); const int num_downloading = int(torrent_list(session_interface::torrent_downloading_auto_managed).size()); const int num_seeds = int(torrent_list(session_interface::torrent_seeding_auto_managed).size()); const int num_active = num_downloading + num_seeds; // there's no point in triggering the auto manage logic early if we // don't have a reason to believe anything will change. It's kind of // expensive. if ((num_downloading < max_downloading || num_seeds < max_seeds) && num_active < max_active) { trigger_auto_manage(); } } return handle; } std::pair, bool> session_impl::add_torrent_impl( add_torrent_params& params, error_code& ec) { TORRENT_ASSERT(!params.save_path.empty()); typedef boost::shared_ptr ptr_t; #ifndef TORRENT_NO_DEPRECATE params.update_flags(); #endif if (string_begins_no_case("magnet:", params.url.c_str())) { parse_magnet_uri(params.url, params, ec); if (ec) return std::make_pair(ptr_t(), false); params.url.clear(); } if (string_begins_no_case("file://", params.url.c_str()) && !params.ti) { std::string filename = resolve_file_url(params.url); boost::shared_ptr t = boost::make_shared(filename, boost::ref(ec), 0); if (ec) return std::make_pair(ptr_t(), false); params.url.clear(); params.ti = t; } if (params.ti && !params.ti->is_valid()) { ec = errors::no_metadata; return std::make_pair(ptr_t(), false); } if (params.ti && params.ti->is_valid() && params.ti->num_files() == 0) { ec = errors::no_files_in_torrent; return std::make_pair(ptr_t(), false); } #ifndef TORRENT_DISABLE_DHT // add params.dht_nodes to the DHT, if enabled if (!params.dht_nodes.empty()) { for (std::vector >::const_iterator i = params.dht_nodes.begin() , end(params.dht_nodes.end()); i != end; ++i) { add_dht_node_name(*i); } } #endif INVARIANT_CHECK; if (is_aborted()) { ec = errors::session_is_closing; return std::make_pair(ptr_t(), false); } // figure out the info hash of the torrent and make sure params.info_hash // is set correctly if (params.ti) params.info_hash = params.ti->info_hash(); else if (!params.url.empty()) { // in order to avoid info-hash collisions, for // torrents where we don't have an info-hash, but // just a URL, set the temporary info-hash to the // hash of the URL. This will be changed once we // have the actual .torrent file params.info_hash = hasher(¶ms.url[0], params.url.size()).final(); } // we don't have a torrent file. If the user provided // resume data, there may be some metadata in there // TODO: this logic could probably be less spaghetti looking by being // moved to a function with early exits if ((!params.ti || !params.ti->is_valid()) && !params.resume_data.empty()) { int pos; error_code err; bdecode_node root; bdecode_node info; #ifndef TORRENT_DISABLE_LOGGING session_log("adding magnet link with resume data"); #endif if (bdecode(¶ms.resume_data[0], ¶ms.resume_data[0] + params.resume_data.size(), root, err, &pos) == 0 && root.type() == bdecode_node::dict_t && (info = root.dict_find_dict("info"))) { #ifndef TORRENT_DISABLE_LOGGING session_log("found metadata in resume data"); #endif // verify the info-hash of the metadata stored in the resume file matches // the torrent we're loading std::pair const buf = info.data_section(); sha1_hash const resume_ih = hasher(buf.first, buf.second).final(); // if url is set, the info_hash is not actually the info-hash of the // torrent, but the hash of the URL, until we have the full torrent // only require the info-hash to match if we actually passed in one if (resume_ih == params.info_hash || !params.url.empty() || params.info_hash.is_all_zeros()) { #ifndef TORRENT_DISABLE_LOGGING session_log("info-hash matched"); #endif params.ti = boost::make_shared(resume_ih); if (params.ti->parse_info_section(info, err, 0)) { #ifndef TORRENT_DISABLE_LOGGING session_log("successfully loaded metadata from resume file"); #endif // make the info-hash be the one in the resume file params.info_hash = resume_ih; } else { #ifndef TORRENT_DISABLE_LOGGING session_log("failed to load metadata from resume file: %s" , err.message().c_str()); #endif } } #ifndef TORRENT_DISABLE_LOGGING else { session_log("metadata info-hash failed"); } #endif } #ifndef TORRENT_DISABLE_LOGGING else { session_log("no metadata found (\"%s\")", err.message().c_str()); } #endif } // is the torrent already active? boost::shared_ptr torrent_ptr = find_torrent(params.info_hash).lock(); if (!torrent_ptr && !params.uuid.empty()) torrent_ptr = find_torrent(params.uuid).lock(); // if we still can't find the torrent, look for it by url if (!torrent_ptr && !params.url.empty()) { torrent_map::iterator i = std::find_if(m_torrents.begin() , m_torrents.end(), boost::bind(&torrent::url, boost::bind(&std::pair >::second, _1)) == params.url); if (i != m_torrents.end()) torrent_ptr = i->second; } if (torrent_ptr) { if ((params.flags & add_torrent_params::flag_duplicate_is_error) == 0) { if (!params.uuid.empty() && torrent_ptr->uuid().empty()) torrent_ptr->set_uuid(params.uuid); if (!params.url.empty() && torrent_ptr->url().empty()) torrent_ptr->set_url(params.url); if (!params.source_feed_url.empty() && torrent_ptr->source_feed_url().empty()) torrent_ptr->set_source_feed_url(params.source_feed_url); return std::make_pair(torrent_ptr, false); } ec = errors::duplicate_torrent; return std::make_pair(ptr_t(), false); } int queue_pos = ++m_max_queue_pos; torrent_ptr = boost::make_shared(boost::ref(*this) , 16 * 1024, queue_pos, boost::cref(params), boost::cref(params.info_hash)); return std::make_pair(torrent_ptr, true); } void session_impl::update_outgoing_interfaces() { std::string const net_interfaces = m_settings.get_str(settings_pack::outgoing_interfaces); // declared in string_util.hpp parse_comma_separated_string(net_interfaces, m_net_interfaces); #ifndef TORRENT_DISABLE_LOGGING if (!net_interfaces.empty() && m_net_interfaces.empty()) { session_log("ERROR: failed to parse outgoing interface list: %s" , net_interfaces.c_str()); } #endif } tcp::endpoint session_impl::bind_outgoing_socket(socket_type& s, address const& remote_address, error_code& ec) const { tcp::endpoint bind_ep(address_v4(), 0); if (m_settings.get_int(settings_pack::outgoing_port) > 0) { #ifdef TORRENT_WINDOWS s.set_option(exclusive_address_use(true), ec); #else s.set_option(tcp::acceptor::reuse_address(true), ec); #endif // ignore errors because the underlying socket may not // be opened yet. This happens when we're routing through // a proxy. In that case, we don't yet know the address of // the proxy server, and more importantly, we don't know // the address family of its address. This means we can't // open the socket yet. The socks abstraction layer defers // opening it. ec.clear(); bind_ep.port(next_port()); } if (!m_net_interfaces.empty()) { if (m_interface_index >= m_net_interfaces.size()) m_interface_index = 0; std::string const& ifname = m_net_interfaces[m_interface_index++]; if (ec) return bind_ep; bind_ep.address(bind_to_device(m_io_service, s , remote_address.is_v4() ? boost::asio::ip::tcp::v4() : boost::asio::ip::tcp::v6() , ifname.c_str(), bind_ep.port(), ec)); return bind_ep; } // if we're not binding to a specific interface, bind // to the same protocol family as the target endpoint if (is_any(bind_ep.address())) { #if TORRENT_USE_IPV6 if (remote_address.is_v6()) bind_ep.address(address_v6::any()); else #endif bind_ep.address(address_v4::any()); } s.bind(bind_ep, ec); return bind_ep; } // verify that the given local address satisfies the requirements of // the outgoing interfaces. i.e. that one of the allowed outgoing // interfaces has this address. For uTP sockets, which are all backed // by an unconnected udp socket, we won't be able to tell what local // address is used for this peer's packets, in that case, just make // sure one of the allowed interfaces exists and maybe that it's the // default route. For systems that have SO_BINDTODEVICE, it should be // enough to just know that one of the devices exist bool session_impl::verify_bound_address(address const& addr, bool utp , error_code& ec) { TORRENT_UNUSED(utp); // we have specific outgoing interfaces specified. Make sure the // local endpoint for this socket is bound to one of the allowed // interfaces. the list can be a mixture of interfaces and IP // addresses. first look for the address for (int i = 0; i < int(m_net_interfaces.size()); ++i) { error_code err; address ip = address::from_string(m_net_interfaces[i].c_str(), err); if (err) continue; if (ip == addr) return true; } // we didn't find the address as an IP in the interface list. Now, // resolve which device (if any) has this IP address. std::string device = device_for_address(addr, m_io_service, ec); if (ec) return false; // if no device was found to have this address, we fail if (device.empty()) return false; for (int i = 0; i < int(m_net_interfaces.size()); ++i) { if (m_net_interfaces[i] == device) return true; } return false; } void session_impl::remove_torrent(const torrent_handle& h, int options) { INVARIANT_CHECK; boost::shared_ptr tptr = h.m_torrent.lock(); if (!tptr) return; m_alerts.emplace_alert(tptr->get_handle() , tptr->info_hash()); remove_torrent_impl(tptr, options); tptr->abort(); tptr->set_queue_position(-1); } void session_impl::remove_torrent_impl(boost::shared_ptr tptr , int options) { // remove from uuid list if (!tptr->uuid().empty()) { std::map >::iterator j = m_uuids.find(tptr->uuid()); if (j != m_uuids.end()) m_uuids.erase(j); } torrent_map::iterator i = m_torrents.find(tptr->torrent_file().info_hash()); // this torrent might be filed under the URL-hash if (i == m_torrents.end() && !tptr->url().empty()) { std::string const& url = tptr->url(); sha1_hash urlhash = hasher(&url[0], url.size()).final(); i = m_torrents.find(urlhash); } if (i == m_torrents.end()) return; torrent& t = *i->second; if (options) { if (!t.delete_files(options)) { if (m_alerts.should_post()) m_alerts.emplace_alert(t.get_handle() , error_code(), t.torrent_file().info_hash()); } } if (m_torrent_lru.size() > 0 && (t.prev != NULL || t.next != NULL || m_torrent_lru.front() == &t)) m_torrent_lru.erase(&t); TORRENT_ASSERT(t.prev == NULL && t.next == NULL); tptr->update_gauge(); #if TORRENT_USE_ASSERTS sha1_hash i_hash = t.torrent_file().info_hash(); #endif #ifndef TORRENT_DISABLE_DHT if (i == m_next_dht_torrent) ++m_next_dht_torrent; #endif if (i == m_next_lsd_torrent) ++m_next_lsd_torrent; m_torrents.erase(i); TORRENT_ASSERT(m_torrents.size() >= m_torrent_lru.size()); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) hasher h; h.update("req2", 4); h.update(tptr->info_hash().data(), 20); m_obfuscated_torrents.erase(h.final()); #endif #ifndef TORRENT_DISABLE_DHT if (m_next_dht_torrent == m_torrents.end()) m_next_dht_torrent = m_torrents.begin(); #endif if (m_next_lsd_torrent == m_torrents.end()) m_next_lsd_torrent = m_torrents.begin(); // this torrent may open up a slot for a queued torrent trigger_auto_manage(); TORRENT_ASSERT(m_torrents.find(i_hash) == m_torrents.end()); } void session_impl::update_listen_interfaces() { std::string const net_interfaces = m_settings.get_str(settings_pack::listen_interfaces); std::vector > new_listen_interfaces; // declared in string_util.hpp parse_comma_separated_string_port(net_interfaces, new_listen_interfaces); #ifndef TORRENT_DISABLE_LOGGING if (!net_interfaces.empty() && new_listen_interfaces.empty()) { session_log("ERROR: failed to parse listen_interfaces setting: %s" , net_interfaces.c_str()); } session_log("update listen interfaces: %s", net_interfaces.c_str()); #endif // if the interface is the same and the socket is open // don't do anything if (new_listen_interfaces == m_listen_interfaces && !m_listen_sockets.empty()) return; m_listen_interfaces = new_listen_interfaces; // for backwards compatibility. Some components still only supports // a single listen interface m_listen_interface.address(address_v4::any()); m_listen_interface.port(0); if (m_listen_interfaces.size() > 0) { error_code ec; m_listen_interface.port(m_listen_interfaces[0].second); char const* device_name = m_listen_interfaces[0].first.c_str(); // if the first character is [, skip it since it may be an // IPv6 address m_listen_interface.address(address::from_string( device_name[0] == '[' ? device_name + 1 : device_name, ec)); if (ec) { #ifndef TORRENT_DISABLE_LOGGING session_log("failed to treat %s as an IP address [ %s ]" , device_name, ec.message().c_str()); #endif // it may have been a device name. std::vector ifs = enum_net_interfaces(m_io_service, ec); #ifndef TORRENT_DISABLE_LOGGING if (ec) session_log("failed to enumerate interfaces [ %s ]" , ec.message().c_str()); #endif bool found = false; for (int i = 0; i < int(ifs.size()); ++i) { // we're looking for a specific interface, and its address // (which must be of the same family as the address we're // connecting to) if (strcmp(ifs[i].name, device_name) != 0) continue; m_listen_interface.address(ifs[i].interface_address); #ifndef TORRENT_DISABLE_LOGGING error_code err; session_log("binding to %s" , m_listen_interface.address().to_string(err).c_str()); #endif found = true; break; } if (!found) { #ifndef TORRENT_DISABLE_LOGGING session_log("failed to find device %s", device_name); #endif // effectively disable whatever socket decides to bind to this m_listen_interface.address(address_v4::loopback()); } } } } void session_impl::update_privileged_ports() { if (m_settings.get_bool(settings_pack::no_connect_privileged_ports)) { m_port_filter.add_rule(0, 1024, port_filter::blocked); // Close connections whose endpoint is filtered // by the new ip-filter for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) i->second->port_filter_updated(); } else { m_port_filter.add_rule(0, 1024, 0); } } void session_impl::update_auto_sequential() { for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) i->second->update_auto_sequential(); } void session_impl::update_max_failcount() { for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { i->second->update_max_failcount(); } } void session_impl::update_close_file_interval() { int const interval = m_settings.get_int(settings_pack::close_file_interval); if (interval == 0 || m_abort) { m_close_file_timer.cancel(); return; } error_code ec; m_close_file_timer.expires_from_now(seconds(interval), ec); m_close_file_timer.async_wait(boost::bind(&session_impl::on_close_file, this, _1)); } void session_impl::update_proxy() { m_udp_socket.set_proxy_settings(proxy()); #ifdef TORRENT_USE_OPENSSL m_ssl_udp_socket.set_proxy_settings(proxy()); #endif } void session_impl::update_upnp() { if (m_settings.get_bool(settings_pack::enable_upnp)) start_upnp(); else stop_upnp(); } void session_impl::update_natpmp() { if (m_settings.get_bool(settings_pack::enable_natpmp)) start_natpmp(); else stop_natpmp(); } void session_impl::update_lsd() { if (m_settings.get_bool(settings_pack::enable_lsd)) start_lsd(); else stop_lsd(); } void session_impl::update_dht() { #ifndef TORRENT_DISABLE_DHT if (m_settings.get_bool(settings_pack::enable_dht)) { if (!m_settings.get_str(settings_pack::dht_bootstrap_nodes).empty() && m_dht_router_nodes.empty()) { // if we have bootstrap nodes configured, make sure we initiate host // name lookups. once these complete, the DHT will be started. // they are tracked by m_outstanding_router_lookups update_dht_bootstrap_nodes(); } else { start_dht(); } } else stop_dht(); #endif } void session_impl::update_dht_bootstrap_nodes() { #ifndef TORRENT_DISABLE_DHT if (!m_settings.get_bool(settings_pack::enable_dht)) return; std::string const& node_list = m_settings.get_str(settings_pack::dht_bootstrap_nodes); std::vector > nodes; parse_comma_separated_string_port(node_list, nodes); #ifndef TORRENT_DISABLE_LOGGING if (!node_list.empty() && nodes.empty()) { session_log("ERROR: failed to parse DHT bootstrap list: %s", node_list.c_str()); } #endif for (int i = 0; i < nodes.size(); ++i) { add_dht_router(nodes[i]); } #endif } void session_impl::update_count_slow() { error_code ec; for (torrent_map::const_iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { i->second->on_inactivity_tick(ec); } } address session_impl::listen_address() const { for (std::list::const_iterator i = m_listen_sockets.begin() , end(m_listen_sockets.end()); i != end; ++i) { if (i->external_address != address()) return i->external_address; } return address(); } boost::uint16_t session_impl::listen_port() const { // if not, don't tell the tracker anything if we're in force_proxy // mode. We don't want to leak our listen port since it can // potentially identify us if it is leaked elsewere if (m_settings.get_bool(settings_pack::force_proxy)) return 0; if (m_listen_sockets.empty()) return 0; #ifdef TORRENT_USE_OPENSSL for (std::list::const_iterator i = m_listen_sockets.begin() , end(m_listen_sockets.end()); i != end; ++i) { if (!i->ssl) return i->external_port; } return 0; #else return m_listen_sockets.front().external_port; #endif } boost::uint16_t session_impl::ssl_listen_port() const { #ifdef TORRENT_USE_OPENSSL // if not, don't tell the tracker anything if we're in force_proxy // mode. We don't want to leak our listen port since it can // potentially identify us if it is leaked elsewere if (m_settings.get_bool(settings_pack::force_proxy)) return 0; for (std::list::const_iterator i = m_listen_sockets.begin() , end(m_listen_sockets.end()); i != end; ++i) { if (i->ssl) return i->external_port; } if (m_ssl_udp_socket.is_open()) return m_ssl_udp_socket.local_port(); #endif return 0; } void session_impl::announce_lsd(sha1_hash const& ih, int port, bool broadcast) { // use internal listen port for local peers if (m_lsd) m_lsd->announce(ih, port, broadcast); } void session_impl::on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih) { m_stats_counters.inc_stats_counter(counters::on_lsd_peer_counter); TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; boost::shared_ptr t = find_torrent(ih).lock(); if (!t) return; // don't add peers from lsd to private torrents if (t->torrent_file().priv() || (t->torrent_file().is_i2p() && !m_settings.get_bool(settings_pack::allow_i2p_mixed))) return; #ifndef TORRENT_DISABLE_LOGGING session_log("added peer from local discovery: %s", print_endpoint(peer).c_str()); #endif t->add_peer(peer, peer_info::lsd); t->do_connect_boost(); if (m_alerts.should_post()) m_alerts.emplace_alert(t->get_handle(), peer); } // TODO: perhaps this function should not exist when logging is disabled void session_impl::on_port_map_log( char const* msg, int map_transport) { #ifndef TORRENT_DISABLE_LOGGING TORRENT_ASSERT(map_transport >= 0 && map_transport <= 1); // log message if (m_alerts.should_post()) m_alerts.emplace_alert(map_transport, msg); #else TORRENT_UNUSED(msg); TORRENT_UNUSED(map_transport); #endif } void session_impl::on_port_mapping(int mapping, address const& ip, int port , int protocol, error_code const& ec, int map_transport) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(map_transport >= 0 && map_transport <= 1); if (mapping == m_udp_mapping[map_transport] && port != 0) { m_external_udp_port = port; if (m_alerts.should_post()) m_alerts.emplace_alert(mapping, port , map_transport, protocol == natpmp::udp ? portmap_alert::udp : portmap_alert::tcp); return; } if (mapping == m_tcp_mapping[map_transport] && port != 0) { if (ip != address()) { // TODO: 1 report the proper address of the router as the source IP of // this understanding of our external address, instead of the empty address set_external_address(ip, source_router, address()); } if (!m_listen_sockets.empty()) { m_listen_sockets.front().external_address = ip; m_listen_sockets.front().external_port = port; } if (m_alerts.should_post()) m_alerts.emplace_alert(mapping, port , map_transport, protocol == natpmp::udp ? portmap_alert::udp : portmap_alert::tcp); return; } if (ec) { if (m_alerts.should_post()) m_alerts.emplace_alert(mapping , map_transport, ec); } else { if (m_alerts.should_post()) m_alerts.emplace_alert(mapping, port , map_transport, protocol == natpmp::udp ? portmap_alert::udp : portmap_alert::tcp); } } #ifndef TORRENT_NO_DEPRECATE session_status session_impl::status() const { // INVARIANT_CHECK; TORRENT_ASSERT(is_single_thread()); session_status s; s.optimistic_unchoke_counter = m_optimistic_unchoke_time_scaler; s.unchoke_counter = m_unchoke_time_scaler; s.num_dead_peers = int(m_undead_peers.size()); s.num_peers = m_stats_counters[counters::num_peers_connected]; s.num_unchoked = m_stats_counters[counters::num_peers_up_unchoked_all]; s.allowed_upload_slots = m_stats_counters[counters::num_unchoke_slots]; s.num_torrents = m_stats_counters[counters::num_checking_torrents] + m_stats_counters[counters::num_stopped_torrents] + m_stats_counters[counters::num_queued_seeding_torrents] + m_stats_counters[counters::num_queued_download_torrents] + m_stats_counters[counters::num_upload_only_torrents] + m_stats_counters[counters::num_downloading_torrents] + m_stats_counters[counters::num_seeding_torrents] + m_stats_counters[counters::num_error_torrents]; s.num_paused_torrents = m_stats_counters[counters::num_stopped_torrents] + m_stats_counters[counters::num_error_torrents] + m_stats_counters[counters::num_queued_seeding_torrents] + m_stats_counters[counters::num_queued_download_torrents]; s.total_redundant_bytes = m_stats_counters[counters::recv_redundant_bytes]; s.total_failed_bytes = m_stats_counters[counters::recv_failed_bytes]; s.up_bandwidth_queue = m_stats_counters[counters::limiter_up_queue]; s.down_bandwidth_queue = m_stats_counters[counters::limiter_down_queue]; s.up_bandwidth_bytes_queue = m_stats_counters[counters::limiter_up_bytes]; s.down_bandwidth_bytes_queue = m_stats_counters[counters::limiter_down_bytes]; s.disk_write_queue = m_stats_counters[counters::num_peers_down_disk]; s.disk_read_queue = m_stats_counters[counters::num_peers_up_disk]; s.has_incoming_connections = m_stats_counters[counters::has_incoming_connections]; // total s.download_rate = m_stat.download_rate(); s.total_upload = m_stat.total_upload(); s.upload_rate = m_stat.upload_rate(); s.total_download = m_stat.total_download(); // payload s.payload_download_rate = m_stat.transfer_rate(stat::download_payload); s.total_payload_download = m_stat.total_transfer(stat::download_payload); s.payload_upload_rate = m_stat.transfer_rate(stat::upload_payload); s.total_payload_upload = m_stat.total_transfer(stat::upload_payload); // IP-overhead s.ip_overhead_download_rate = m_stat.transfer_rate(stat::download_ip_protocol); s.total_ip_overhead_download = m_stats_counters[counters::recv_ip_overhead_bytes]; s.ip_overhead_upload_rate = m_stat.transfer_rate(stat::upload_ip_protocol); s.total_ip_overhead_upload = m_stats_counters[counters::sent_ip_overhead_bytes]; // tracker s.total_tracker_download = m_stats_counters[counters::recv_tracker_bytes]; s.total_tracker_upload = m_stats_counters[counters::sent_tracker_bytes]; // dht s.total_dht_download = m_stats_counters[counters::dht_bytes_in]; s.total_dht_upload = m_stats_counters[counters::dht_bytes_out]; // deprecated s.tracker_download_rate = 0; s.tracker_upload_rate = 0; s.dht_download_rate = 0; s.dht_upload_rate = 0; #ifndef TORRENT_DISABLE_DHT if (m_dht) { m_dht->dht_status(s); } else #endif { s.dht_nodes = 0; s.dht_node_cache = 0; s.dht_torrents = 0; s.dht_global_nodes = 0; s.dht_total_allocations = 0; } s.utp_stats.packet_loss = m_stats_counters[counters::utp_packet_loss]; s.utp_stats.timeout = m_stats_counters[counters::utp_timeout]; s.utp_stats.packets_in = m_stats_counters[counters::utp_packets_in]; s.utp_stats.packets_out = m_stats_counters[counters::utp_packets_out]; s.utp_stats.fast_retransmit = m_stats_counters[counters::utp_fast_retransmit]; s.utp_stats.packet_resend = m_stats_counters[counters::utp_packet_resend]; s.utp_stats.samples_above_target = m_stats_counters[counters::utp_samples_above_target]; s.utp_stats.samples_below_target = m_stats_counters[counters::utp_samples_below_target]; s.utp_stats.payload_pkts_in = m_stats_counters[counters::utp_payload_pkts_in]; s.utp_stats.payload_pkts_out = m_stats_counters[counters::utp_payload_pkts_out]; s.utp_stats.invalid_pkts_in = m_stats_counters[counters::utp_invalid_pkts_in]; s.utp_stats.redundant_pkts_in = m_stats_counters[counters::utp_redundant_pkts_in]; s.utp_stats.num_idle = m_stats_counters[counters::num_utp_idle]; s.utp_stats.num_syn_sent = m_stats_counters[counters::num_utp_syn_sent]; s.utp_stats.num_connected = m_stats_counters[counters::num_utp_connected]; s.utp_stats.num_fin_sent = m_stats_counters[counters::num_utp_fin_sent]; s.utp_stats.num_close_wait = m_stats_counters[counters::num_utp_close_wait]; // this loop is potentially expensive. It could be optimized by // simply keeping a global counter int peerlist_size = 0; for (torrent_map::const_iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { peerlist_size += i->second->num_known_peers(); } s.peerlist_size = peerlist_size; return s; } #endif // TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_DHT void session_impl::start_dht() { start_dht(m_dht_state); } namespace { void on_bootstrap(alert_manager& alerts) { if (alerts.should_post()) alerts.emplace_alert(); } } void session_impl::start_dht(entry const& startup_state) { INVARIANT_CHECK; stop_dht(); if (!m_settings.get_bool(settings_pack::enable_dht)) return; // postpone starting the DHT if we're still resolving the DHT router if (m_outstanding_router_lookups > 0) return; if (m_abort) return; m_dht = boost::make_shared(static_cast(this) , boost::ref(m_udp_socket), boost::cref(m_dht_settings) , boost::ref(m_stats_counters) , m_dht_storage_constructor , startup_state); for (std::vector::iterator i = m_dht_router_nodes.begin() , end(m_dht_router_nodes.end()); i != end; ++i) { m_dht->add_router_node(*i); } for (std::vector::iterator i = m_dht_nodes.begin() , end(m_dht_nodes.end()); i != end; ++i) { m_dht->add_node(*i); } m_dht_nodes.clear(); m_dht->start(startup_state, boost::bind(&on_bootstrap, boost::ref(m_alerts))); m_udp_socket.subscribe(m_dht.get()); } void session_impl::stop_dht() { if (!m_dht) return; m_udp_socket.unsubscribe(m_dht.get()); m_dht->stop(); m_dht.reset(); } void session_impl::set_dht_settings(dht_settings const& settings) { m_dht_settings = settings; } void session_impl::set_dht_storage(dht::dht_storage_constructor_type sc) { m_dht_storage_constructor = sc; } #ifndef TORRENT_NO_DEPRECATE entry session_impl::dht_state() const { if (!m_dht) return entry(); return m_dht->state(); } void session_impl::start_dht_deprecated(entry const& startup_state) { m_settings.set_bool(settings_pack::enable_dht, true); start_dht(startup_state); } #endif void session_impl::add_dht_node_name(std::pair const& node) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::on_dht_name_lookup"); #endif m_host_resolver.async_resolve(node.first, resolver_interface::abort_on_shutdown , boost::bind(&session_impl::on_dht_name_lookup , this, _1, _2, node.second)); } void session_impl::on_dht_name_lookup(error_code const& e , std::vector
const& addresses, int port) { #if defined TORRENT_ASIO_DEBUGGING complete_async("session_impl::on_dht_name_lookup"); #endif if (e) { if (m_alerts.should_post()) m_alerts.emplace_alert( dht_error_alert::hostname_lookup, e); return; } for (std::vector
::const_iterator i = addresses.begin() , end(addresses.end()); i != end; ++i) { udp::endpoint ep(*i, port); add_dht_node(ep); } } void session_impl::add_dht_router(std::pair const& node) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::on_dht_router_name_lookup"); #endif ++m_outstanding_router_lookups; m_host_resolver.async_resolve(node.first, resolver_interface::abort_on_shutdown , boost::bind(&session_impl::on_dht_router_name_lookup , this, _1, _2, node.second)); } void session_impl::on_dht_router_name_lookup(error_code const& e , std::vector
const& addresses, int port) { #if defined TORRENT_ASIO_DEBUGGING complete_async("session_impl::on_dht_router_name_lookup"); #endif --m_outstanding_router_lookups; if (e) { if (m_alerts.should_post()) m_alerts.emplace_alert( dht_error_alert::hostname_lookup, e); if (m_outstanding_router_lookups == 0) start_dht(); return; } for (std::vector
::const_iterator i = addresses.begin() , end(addresses.end()); i != end; ++i) { #if !TORRENT_USE_IPV6 if (!i->is_v4()) continue; #endif // router nodes should be added before the DHT is started (and bootstrapped) udp::endpoint ep(*i, port); if (m_dht) m_dht->add_router_node(ep); m_dht_router_nodes.push_back(ep); } if (m_outstanding_router_lookups == 0) start_dht(); } // callback for dht_immutable_get void session_impl::get_immutable_callback(sha1_hash target , dht::item const& i) { TORRENT_ASSERT(!i.is_mutable()); m_alerts.emplace_alert(target, i.value()); } void session_impl::dht_get_immutable_item(sha1_hash const& target) { if (!m_dht) return; m_dht->get_item(target, boost::bind(&session_impl::get_immutable_callback , this, target, _1)); } // callback for dht_mutable_get void session_impl::get_mutable_callback(dht::item const& i, bool authoritative) { TORRENT_ASSERT(i.is_mutable()); m_alerts.emplace_alert(i.pk(), i.sig(), i.seq() , i.salt(), i.value(), authoritative); } // key is a 32-byte binary string, the public key to look up. // the salt is optional void session_impl::dht_get_mutable_item(boost::array key , std::string salt) { if (!m_dht) return; m_dht->get_item(key.data(), boost::bind(&session_impl::get_mutable_callback , this, _1, _2), salt); } namespace { void on_dht_put_immutable_item(alert_manager& alerts, sha1_hash target, int num) { if (alerts.should_post()) alerts.emplace_alert(target, num); } void on_dht_put_mutable_item(alert_manager& alerts, dht::item const& i, int num) { boost::array sig = i.sig(); boost::array pk = i.pk(); boost::uint64_t seq = i.seq(); std::string salt = i.salt(); if (alerts.should_post()) alerts.emplace_alert(pk, sig, salt, seq, num); } void put_mutable_callback(dht::item& i , boost::function& , boost::uint64_t&, std::string const&)> cb) { entry value = i.value(); boost::array sig = i.sig(); boost::array pk = i.pk(); boost::uint64_t seq = i.seq(); std::string salt = i.salt(); cb(value, sig, seq, salt); i.assign(value, salt, seq, pk.data(), sig.data()); } void on_dht_get_peers(alert_manager& alerts, sha1_hash info_hash, std::vector const& peers) { if (alerts.should_post()) alerts.emplace_alert(info_hash, peers); } void on_direct_response(alert_manager& alerts, void* userdata, dht::msg const& msg) { if (msg.message.type() == bdecode_node::none_t) alerts.emplace_alert(userdata, msg.addr); else alerts.emplace_alert(userdata, msg.addr, msg.message); } } // anonymous namespace void session_impl::dht_put_immutable_item(entry const& data, sha1_hash target) { if (!m_dht) return; m_dht->put_item(data, boost::bind(&on_dht_put_immutable_item, boost::ref(m_alerts) , target, _1)); } void session_impl::dht_put_mutable_item(boost::array key , boost::function& , boost::uint64_t&, std::string const&)> cb , std::string salt) { if (!m_dht) return; m_dht->put_item(key.data(), boost::bind(&on_dht_put_mutable_item, boost::ref(m_alerts), _1, _2), boost::bind(&put_mutable_callback, _1, cb), salt); } void session_impl::dht_get_peers(sha1_hash const& info_hash) { if (!m_dht) return; m_dht->get_peers(info_hash, boost::bind(&on_dht_get_peers, boost::ref(m_alerts), info_hash, _1)); } void session_impl::dht_announce(sha1_hash const& info_hash, int port, int flags) { if (!m_dht) return; m_dht->announce(info_hash, port, flags, boost::bind(&on_dht_get_peers, boost::ref(m_alerts), info_hash, _1)); } void session_impl::dht_direct_request(udp::endpoint ep, entry& e, void* userdata) { if (!m_dht) return; m_dht->direct_request(ep, e, boost::bind(&on_direct_response, boost::ref(m_alerts), userdata, _1)); } #endif void session_impl::maybe_update_udp_mapping(int const nat, bool const ssl , int const local_port, int const external_port) { int external, protocol; tcp::endpoint local_ep; #ifdef TORRENT_USE_OPENSSL int* mapping = ssl ? m_ssl_udp_mapping : m_udp_mapping; #else TORRENT_UNUSED(ssl); int* mapping = m_udp_mapping; #endif if (nat == 0 && m_natpmp) { int local = 0; if (mapping[nat] != -1) { if (m_natpmp->get_mapping(mapping[nat], local, external, protocol)) { // we already have a mapping. If it's the same, don't do anything if (local == local_port && external == external_port && protocol == natpmp::udp) return; } m_natpmp->delete_mapping(mapping[nat]); } mapping[nat] = m_natpmp->add_mapping(natpmp::udp , external_port, local_port); return; } else if (nat == 1 && m_upnp) { if (mapping[nat] != -1) { if (m_upnp->get_mapping(mapping[nat], local_ep, external, protocol)) { // we already have a mapping. If it's the same, don't do anything if (local_ep.port() == local_port && external == external_port && protocol == natpmp::udp) return; } m_upnp->delete_mapping(mapping[nat]); } local_ep.port(local_port); mapping[nat] = m_upnp->add_mapping(upnp::udp , external_port, local_ep); return; } } #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) void session_impl::add_obfuscated_hash(sha1_hash const& obfuscated , boost::weak_ptr const& t) { m_obfuscated_torrents.insert(std::make_pair(obfuscated, t.lock())); } #endif // !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) bool session_impl::is_listening() const { return !m_listen_sockets.empty(); } session_impl::~session_impl() { // this is not allowed to be the network thread! // TORRENT_ASSERT(is_not_thread()); m_udp_socket.unsubscribe(this); m_udp_socket.unsubscribe(&m_utp_socket_manager); m_udp_socket.unsubscribe(&m_tracker_manager); #ifdef TORRENT_USE_OPENSSL m_ssl_udp_socket.unsubscribe(this); m_ssl_udp_socket.unsubscribe(&m_ssl_utp_socket_manager); #endif TORRENT_ASSERT(m_torrents.empty()); TORRENT_ASSERT(m_connections.empty()); #if defined TORRENT_ASIO_DEBUGGING FILE* f = fopen("wakeups.log", "w+"); if (f != NULL) { time_point m = min_time(); if (_wakeups.size() > 0) m = _wakeups[0].timestamp; time_point prev = m; boost::uint64_t prev_csw = 0; if (_wakeups.size() > 0) prev_csw = _wakeups[0].context_switches; fprintf(f, "abs. time\trel. time\tctx switch\tidle-wakeup\toperation\n"); for (int i = 0; i < _wakeups.size(); ++i) { wakeup_t const& w = _wakeups[i]; bool idle_wakeup = w.context_switches > prev_csw; fprintf(f, "%" PRId64 "\t%" PRId64 "\t%" PRId64 "\t%c\t%s\n" , total_microseconds(w.timestamp - m) , total_microseconds(w.timestamp - prev) , w.context_switches , idle_wakeup ? '*' : '.' , w.operation); prev = w.timestamp; prev_csw = w.context_switches; } fclose(f); } #endif // clear the torrent LRU. We do this to avoid having the torrent // destructor assert because it's still linked into the lru list #if TORRENT_USE_ASSERTS list_node* i = m_torrent_lru.get_all(); // clear the prev and next pointers in all torrents // to avoid the assert when destructing them while (i) { list_node* tmp = i; i = i->next; tmp->next = NULL; tmp->prev = NULL; } #endif } #ifndef TORRENT_NO_DEPRECATE int session_impl::max_connections() const { return m_settings.get_int(settings_pack::connections_limit); } int session_impl::max_uploads() const { return m_settings.get_int(settings_pack::unchoke_slots_limit); } void session_impl::set_local_download_rate_limit(int bytes_per_second) { INVARIANT_CHECK; settings_pack p; p.set_int(settings_pack::local_download_rate_limit, bytes_per_second); apply_settings_pack_impl(p); } void session_impl::set_local_upload_rate_limit(int bytes_per_second) { INVARIANT_CHECK; settings_pack p; p.set_int(settings_pack::local_upload_rate_limit, bytes_per_second); apply_settings_pack_impl(p); } void session_impl::set_download_rate_limit(int bytes_per_second) { INVARIANT_CHECK; settings_pack p; p.set_int(settings_pack::download_rate_limit, bytes_per_second); apply_settings_pack_impl(p); } void session_impl::set_upload_rate_limit(int bytes_per_second) { INVARIANT_CHECK; settings_pack p; p.set_int(settings_pack::upload_rate_limit, bytes_per_second); apply_settings_pack_impl(p); } void session_impl::set_max_connections(int limit) { INVARIANT_CHECK; settings_pack p; p.set_int(settings_pack::connections_limit, limit); apply_settings_pack_impl(p); } void session_impl::set_max_uploads(int limit) { INVARIANT_CHECK; settings_pack p; p.set_int(settings_pack::unchoke_slots_limit, limit); apply_settings_pack_impl(p); } int session_impl::local_upload_rate_limit() const { return upload_rate_limit(m_local_peer_class); } int session_impl::local_download_rate_limit() const { return download_rate_limit(m_local_peer_class); } int session_impl::upload_rate_limit() const { return upload_rate_limit(m_global_class); } int session_impl::download_rate_limit() const { return download_rate_limit(m_global_class); } #endif // DEPRECATE // TODO: 2 this should be factored into the udp socket, so we only have the // code once void session_impl::update_peer_tos() { error_code ec; #if TORRENT_USE_IPV6 && defined IPV6_TCLASS if (m_udp_socket.local_endpoint(ec).address().is_v6()) m_udp_socket.set_option(traffic_class(m_settings.get_int(settings_pack::peer_tos)), ec); else #endif m_udp_socket.set_option(type_of_service(m_settings.get_int(settings_pack::peer_tos)), ec); #ifdef TORRENT_USE_OPENSSL #if TORRENT_USE_IPV6 && defined IPV6_TCLASS if (m_ssl_udp_socket.local_endpoint(ec).address().is_v6()) m_ssl_udp_socket.set_option(traffic_class(m_settings.get_int(settings_pack::peer_tos)), ec); else #endif m_ssl_udp_socket.set_option(type_of_service(m_settings.get_int(settings_pack::peer_tos)), ec); #endif #ifndef TORRENT_DISABLE_LOGGING session_log(">>> SET_TOS [ udp_socket tos: %x e: %s ]" , m_settings.get_int(settings_pack::peer_tos) , ec.message().c_str()); #endif } void session_impl::update_user_agent() { // replace all occurrences of '\n' with ' '. std::string agent = m_settings.get_str(settings_pack::user_agent); std::string::iterator i = agent.begin(); while ((i = std::find(i, agent.end(), '\n')) != agent.end()) *i = ' '; m_settings.set_str(settings_pack::user_agent, agent); } void session_impl::update_unchoke_limit() { int const allowed_upload_slots = get_int_setting(settings_pack::unchoke_slots_limit); m_stats_counters.set_value(counters::num_unchoke_slots , allowed_upload_slots); if (m_settings.get_int(settings_pack::num_optimistic_unchoke_slots) >= allowed_upload_slots / 2) { if (m_alerts.should_post()) m_alerts.emplace_alert(torrent_handle() , performance_alert::too_many_optimistic_unchoke_slots); } } void session_impl::update_connection_speed() { if (m_settings.get_int(settings_pack::connection_speed) < 0) m_settings.set_int(settings_pack::connection_speed, 200); } void session_impl::update_queued_disk_bytes() { boost::uint64_t const cache_size = m_settings.get_int(settings_pack::cache_size); if (m_settings.get_int(settings_pack::max_queued_disk_bytes) / 16 / 1024 > cache_size / 2 && cache_size > 5 && m_alerts.should_post()) { m_alerts.emplace_alert(torrent_handle() , performance_alert::too_high_disk_queue_limit); } } void session_impl::update_alert_queue_size() { m_alerts.set_alert_queue_size_limit(m_settings.get_int(settings_pack::alert_queue_size)); } bool session_impl::preemptive_unchoke() const { return m_stats_counters[counters::num_peers_up_unchoked] < m_stats_counters[counters::num_unchoke_slots] || m_settings.get_int(settings_pack::unchoke_slots_limit) < 0; } void session_impl::update_dht_upload_rate_limit() { m_udp_socket.set_rate_limit(m_settings.get_int(settings_pack::dht_upload_rate_limit)); } void session_impl::update_disk_threads() { if (m_settings.get_int(settings_pack::aio_threads) < 0) m_settings.set_int(settings_pack::aio_threads, 0); #if !TORRENT_USE_PREAD && !TORRENT_USE_PREADV // if we don't have pread() nor preadv() there's no way // to perform concurrent file operations on the same file // handle, so we must limit the disk thread to a single one if (m_settings.get_int(settings_pack::aio_threads) > 1) m_settings.set_int(settings_pack::aio_threads, 1); #endif m_disk_thread.set_num_threads(m_settings.get_int(settings_pack::aio_threads)); } void session_impl::update_network_threads() { int num_threads = m_settings.get_int(settings_pack::network_threads); int num_pools = num_threads > 0 ? num_threads : 1; while (num_pools > m_net_thread_pool.size()) { m_net_thread_pool.push_back(boost::make_shared()); m_net_thread_pool.back()->set_num_threads(num_threads > 0 ? 1 : 0); } while (num_pools < m_net_thread_pool.size()) { m_net_thread_pool.erase(m_net_thread_pool.end() - 1); } if (num_threads == 0 && m_net_thread_pool.size() > 0) { m_net_thread_pool[0]->set_num_threads(0); } } void session_impl::post_socket_job(socket_job& j) { uintptr_t idx = 0; if (m_net_thread_pool.size() > 1) { // each peer needs to be pinned to a specific thread // since reading and writing simultaneously on the same // socket from different threads is not supported by asio. // as long as a specific socket is consistently used from // the same thread, it's safe idx = uintptr_t(j.peer.get()); idx ^= idx >> 8; idx %= m_net_thread_pool.size(); } m_net_thread_pool[idx]->post_job(j); } void session_impl::update_cache_buffer_chunk_size() { if (m_settings.get_int(settings_pack::cache_buffer_chunk_size) <= 0) m_settings.set_int(settings_pack::cache_buffer_chunk_size, 1); } void session_impl::update_report_web_seed_downloads() { // if this flag changed, update all web seed connections bool report = m_settings.get_bool(settings_pack::report_web_seed_downloads); for (connection_map::iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { int type = (*i)->type(); if (type == peer_connection::url_seed_connection || type == peer_connection::http_seed_connection) (*i)->ignore_stats(!report); } } void session_impl::trigger_auto_manage() { if (m_pending_auto_manage || m_abort) return; // we recalculated auto-managed torrents less than a second ago, // put it off one second. if (time_now() - m_last_auto_manage < seconds(1)) { m_auto_manage_time_scaler = 0; return; } m_pending_auto_manage = true; m_need_auto_manage = true; m_io_service.post(boost::bind(&session_impl::on_trigger_auto_manage, this)); } void session_impl::on_trigger_auto_manage() { TORRENT_ASSERT(m_pending_auto_manage); if (!m_need_auto_manage || m_abort) { m_pending_auto_manage = false; return; } // don't clear m_pending_auto_manage until after we've // recalculated the auto managed torrents. The auto-managed // logic may trigger another auto-managed event otherwise recalculate_auto_managed_torrents(); m_pending_auto_manage = false; } void session_impl::update_socket_buffer_size() { error_code ec; set_socket_buffer_size(m_udp_socket, m_settings, ec); if (ec) { if (m_alerts.should_post()) m_alerts.emplace_alert(udp::endpoint(), ec); } #ifdef TORRENT_USE_OPENSSL set_socket_buffer_size(m_ssl_udp_socket, m_settings, ec); if (ec) { if (m_alerts.should_post()) m_alerts.emplace_alert(udp::endpoint(), ec); } #endif } void session_impl::update_dht_announce_interval() { #ifndef TORRENT_DISABLE_DHT if (!m_dht) { #ifndef TORRENT_DISABLE_LOGGING session_log("not starting DHT announce timer: m_dht == NULL"); #endif return; } m_dht_interval_update_torrents = m_torrents.size(); if (m_abort) { #ifndef TORRENT_DISABLE_LOGGING session_log("not starting DHT announce timer: m_abort set"); #endif return; } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("session_impl::on_dht_announce"); #endif error_code ec; int delay = (std::max)(m_settings.get_int(settings_pack::dht_announce_interval) / (std::max)(int(m_torrents.size()), 1), 1); m_dht_announce_timer.expires_from_now(seconds(delay), ec); m_dht_announce_timer.async_wait( boost::bind(&session_impl::on_dht_announce, this, _1)); #endif } void session_impl::update_anonymous_mode() { if (!m_settings.get_bool(settings_pack::anonymous_mode)) { if (m_upnp) m_upnp->set_user_agent(m_settings.get_str(settings_pack::user_agent)); return; } if (m_upnp) m_upnp->set_user_agent(""); } void session_impl::update_force_proxy() { m_udp_socket.set_force_proxy(m_settings.get_bool(settings_pack::force_proxy)); #ifdef TORRENT_USE_OPENSSL m_ssl_udp_socket.set_force_proxy(m_settings.get_bool(settings_pack::force_proxy)); #endif if (!m_settings.get_bool(settings_pack::force_proxy)) { #ifndef TORRENT_DISABLE_LOGGING session_log("force-proxy disabled"); #endif return; } #ifndef TORRENT_DISABLE_LOGGING session_log("force-proxy enabled"); #endif // enable force_proxy mode. We don't want to accept any incoming // connections, except through a proxy. stop_lsd(); stop_upnp(); stop_natpmp(); } #ifndef TORRENT_NO_DEPRECATE void session_impl::update_local_download_rate() { if (m_settings.get_int(settings_pack::local_download_rate_limit) < 0) m_settings.set_int(settings_pack::local_download_rate_limit, 0); set_download_rate_limit(m_local_peer_class , m_settings.get_int(settings_pack::local_download_rate_limit)); } void session_impl::update_local_upload_rate() { if (m_settings.get_int(settings_pack::local_upload_rate_limit) < 0) m_settings.set_int(settings_pack::local_upload_rate_limit, 0); set_upload_rate_limit(m_local_peer_class , m_settings.get_int(settings_pack::local_upload_rate_limit)); } #endif void session_impl::update_download_rate() { if (m_settings.get_int(settings_pack::download_rate_limit) < 0) m_settings.set_int(settings_pack::download_rate_limit, 0); set_download_rate_limit(m_global_class , m_settings.get_int(settings_pack::download_rate_limit)); } void session_impl::update_upload_rate() { if (m_settings.get_int(settings_pack::upload_rate_limit) < 0) m_settings.set_int(settings_pack::upload_rate_limit, 0); set_upload_rate_limit(m_global_class , m_settings.get_int(settings_pack::upload_rate_limit)); } void session_impl::update_connections_limit() { int limit = m_settings.get_int(settings_pack::connections_limit); if (limit <= 0) limit = max_open_files(); m_settings.set_int(settings_pack::connections_limit, limit); if (num_connections() > m_settings.get_int(settings_pack::connections_limit) && !m_torrents.empty()) { // if we have more connections that we're allowed, disconnect // peers from the torrents so that they are all as even as possible int to_disconnect = num_connections() - m_settings.get_int(settings_pack::connections_limit); int last_average = 0; int average = m_settings.get_int(settings_pack::connections_limit) / m_torrents.size(); // the number of slots that are unused by torrents int extra = m_settings.get_int(settings_pack::connections_limit) % m_torrents.size(); // run 3 iterations of this, then we're probably close enough for (int iter = 0; iter < 4; ++iter) { // the number of torrents that are above average int num_above = 0; for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { int num = i->second->num_peers(); if (num <= last_average) continue; if (num > average) ++num_above; if (num < average) extra += average - num; } // distribute extra among the torrents that are above average if (num_above == 0) num_above = 1; last_average = average; average += extra / num_above; if (extra == 0) break; // save the remainder for the next iteration extra = extra % num_above; } for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { int num = i->second->num_peers(); if (num <= average) continue; // distribute the remainder int my_average = average; if (extra > 0) { ++my_average; --extra; } int disconnect = (std::min)(to_disconnect, num - my_average); to_disconnect -= disconnect; i->second->disconnect_peers(disconnect, errors::too_many_connections); } } } void session_impl::update_alert_mask() { m_alerts.set_alert_mask(m_settings.get_int(settings_pack::alert_mask)); } void session_impl::pop_alerts(std::vector* alerts) { int num_resume = 0; m_alerts.get_all(*alerts, num_resume); if (num_resume > 0) { // we can only issue more resume data jobs from // the network thread m_io_service.post(boost::bind(&session_impl::async_resume_dispatched , this)); } } #ifndef TORRENT_NO_DEPRECATE void session_impl::update_rate_limit_utp() { if (m_settings.get_bool(settings_pack::rate_limit_utp)) { // allow the global or local peer class to limit uTP peers m_peer_class_type_filter.allow(peer_class_type_filter::utp_socket , m_global_class); m_peer_class_type_filter.allow(peer_class_type_filter::ssl_utp_socket , m_global_class); } else { // don't add the global or local peer class to limit uTP peers m_peer_class_type_filter.disallow(peer_class_type_filter::utp_socket , m_global_class); m_peer_class_type_filter.disallow(peer_class_type_filter::ssl_utp_socket , m_global_class); } } void session_impl::update_ignore_rate_limits_on_local_network() { init_peer_class_filter( m_settings.get_bool(settings_pack::ignore_limits_on_local_network)); } // this function is called on the user's thread // not the network thread void session_impl::pop_alerts() { // if we don't have any alerts in our local cache, we have to ask // the alert_manager for more. It will swap our vector with its and // destruct eny left-over alerts in there. if (m_alert_pointer_pos >= m_alert_pointers.size()) { pop_alerts(&m_alert_pointers); m_alert_pointer_pos = 0; } } alert const* session_impl::pop_alert() { if (m_alert_pointer_pos >= m_alert_pointers.size()) { pop_alerts(); if (m_alert_pointers.empty()) return NULL; } if (m_alert_pointers.empty()) return NULL; // clone here to be backwards compatible, to make the client delete the // alert object return m_alert_pointers[m_alert_pointer_pos++]; } void session_impl::pop_alerts(std::deque* alerts) { alerts->clear(); if (m_alert_pointer_pos >= m_alert_pointers.size()) { pop_alerts(); if (m_alert_pointers.empty()) return; } for (std::vector::iterator i = m_alert_pointers.begin() + m_alert_pointer_pos, end(m_alert_pointers.end()); i != end; ++i) { alerts->push_back((*i)->clone().release()); } m_alert_pointer_pos = m_alert_pointers.size(); } #endif alert* session_impl::wait_for_alert(time_duration max_wait) { return m_alerts.wait_for_alert(max_wait); } #ifndef TORRENT_NO_DEPRECATE size_t session_impl::set_alert_queue_size_limit(size_t queue_size_limit_) { m_settings.set_int(settings_pack::alert_queue_size, queue_size_limit_); return m_alerts.set_alert_queue_size_limit(queue_size_limit_); } #endif void session_impl::start_lsd() { INVARIANT_CHECK; if (m_lsd) return; m_lsd = boost::make_shared(boost::ref(m_io_service) , boost::bind(&session_impl::on_lsd_peer, this, _1, _2) #ifndef TORRENT_DISABLE_LOGGING , boost::bind(&session_impl::on_lsd_log, this, _1) #endif ); error_code ec; m_lsd->start(ec); if (ec && m_alerts.should_post()) m_alerts.emplace_alert(ec); } #ifndef TORRENT_DISABLE_LOGGING void session_impl::on_lsd_log(char const* log) { if (!m_alerts.should_post()) return; m_alerts.emplace_alert(log); } #endif natpmp* session_impl::start_natpmp() { INVARIANT_CHECK; if (m_natpmp) return m_natpmp.get(); // the natpmp constructor may fail and call the callbacks // into the session_impl. m_natpmp = boost::make_shared(boost::ref(m_io_service) , boost::bind(&session_impl::on_port_mapping , this, _1, _2, _3, _4, _5, 0) , boost::bind(&session_impl::on_port_map_log , this, _1, 0)); m_natpmp->start(); int const ssl_port = ssl_listen_port(); if (m_listen_interface.port() > 0) { remap_tcp_ports(1, m_listen_interface.port(), ssl_port); } if (m_udp_socket.is_open()) { m_udp_mapping[0] = m_natpmp->add_mapping(natpmp::udp , m_listen_interface.port(), m_listen_interface.port()); } #ifdef TORRENT_USE_OPENSSL if (m_ssl_udp_socket.is_open() && ssl_port > 0) { m_ssl_udp_mapping[0] = m_natpmp->add_mapping(natpmp::udp , ssl_port, ssl_port); } #endif return m_natpmp.get(); } upnp* session_impl::start_upnp() { INVARIANT_CHECK; if (m_upnp) return m_upnp.get(); // the upnp constructor may fail and call the callbacks m_upnp = boost::make_shared(boost::ref(m_io_service) , m_listen_interface.address() , m_settings.get_bool(settings_pack::anonymous_mode) ? "" : m_settings.get_str(settings_pack::user_agent) , boost::bind(&session_impl::on_port_mapping , this, _1, _2, _3, _4, _5, 1) , boost::bind(&session_impl::on_port_map_log , this, _1, 1) , m_settings.get_bool(settings_pack::upnp_ignore_nonrouters)); m_upnp->start(); int ssl_port = ssl_listen_port(); m_upnp->discover_device(); if (m_listen_interface.port() > 0 || ssl_port > 0) { remap_tcp_ports(2, m_listen_interface.port(), ssl_port); } if (m_udp_socket.is_open()) { m_udp_mapping[1] = m_upnp->add_mapping(upnp::udp , m_listen_interface.port(), m_listen_interface); } #ifdef TORRENT_USE_OPENSSL if (m_ssl_udp_socket.is_open() && ssl_port > 0) { m_ssl_udp_mapping[1] = m_upnp->add_mapping(upnp::udp , ssl_port, tcp::endpoint(m_listen_interface.address(), ssl_port)); } #endif return m_upnp.get(); } int session_impl::add_port_mapping(int t, int external_port , int local_port) { int ret = 0; if (m_upnp) ret = m_upnp->add_mapping(static_cast(t), external_port , tcp::endpoint(m_listen_interface.address(), local_port)); if (m_natpmp) ret = m_natpmp->add_mapping(static_cast(t) , external_port, local_port); return ret; } void session_impl::delete_port_mapping(int handle) { if (m_upnp) m_upnp->delete_mapping(handle); if (m_natpmp) m_natpmp->delete_mapping(handle); } void session_impl::stop_lsd() { if (m_lsd) m_lsd->close(); m_lsd.reset(); } void session_impl::stop_natpmp() { if (m_natpmp) { m_natpmp->close(); m_udp_mapping[0] = -1; m_tcp_mapping[0] = -1; #ifdef TORRENT_USE_OPENSSL m_ssl_tcp_mapping[0] = -1; m_ssl_udp_mapping[0] = -1; #endif } m_natpmp.reset(); } void session_impl::stop_upnp() { if (m_upnp) { m_upnp->close(); m_udp_mapping[1] = -1; m_tcp_mapping[1] = -1; #ifdef TORRENT_USE_OPENSSL m_ssl_tcp_mapping[1] = -1; m_ssl_udp_mapping[1] = -1; #endif } m_upnp.reset(); } external_ip const& session_impl::external_address() const { return m_external_ip; } // this is the DHT observer version. DHT is the implied source void session_impl::set_external_address(address const& ip , address const& source) { set_external_address(ip, source_dht, source); } address session_impl::external_address() { return m_external_ip.external_address(address_v4()); } void session_impl::get_peers(sha1_hash const& ih) { if (!m_alerts.should_post()) return; m_alerts.emplace_alert(ih); } void session_impl::announce(sha1_hash const& ih, address const& addr , int port) { if (!m_alerts.should_post()) return; m_alerts.emplace_alert(addr, port, ih); } void session_impl::outgoing_get_peers(sha1_hash const& target , sha1_hash const& sent_target, udp::endpoint const& ep) { if (!m_alerts.should_post()) return; m_alerts.emplace_alert(target, sent_target, ep); } #ifndef TORRENT_DISABLE_LOGGING TORRENT_FORMAT(3,4) void session_impl::log(libtorrent::dht::dht_logger::module_t m, char const* fmt, ...) { if (!m_alerts.should_post()) return; va_list v; va_start(v, fmt); char buf[1024]; vsnprintf(buf, sizeof(buf), fmt, v); va_end(v); m_alerts.emplace_alert(static_cast(m), buf); } void session_impl::log_packet(message_direction_t dir, char const* pkt, int len , udp::endpoint node) { if (!m_alerts.should_post()) return; dht_pkt_alert::direction_t d = dir == dht_logger::incoming_message ? dht_pkt_alert::incoming : dht_pkt_alert::outgoing; m_alerts.emplace_alert(pkt, len, d, node); } #endif bool session_impl::on_dht_request(char const* query, int query_len , dht::msg const& request, entry& response) { #ifndef TORRENT_DISABLE_EXTENSIONS if (query_len > max_dht_query_length) return false; for (m_extension_dht_queries_t::iterator i = m_extension_dht_queries.begin(); i != m_extension_dht_queries.end(); ++i) { if (query_len == i->query_len && memcmp(i->query.data(), query, query_len) == 0 && i->handler(request.addr, request.message, response)) return true; } #else TORRENT_UNUSED(query); TORRENT_UNUSED(query_len); TORRENT_UNUSED(request); TORRENT_UNUSED(response); #endif return false; } void session_impl::set_external_address(address const& ip , int source_type, address const& source) { #ifndef TORRENT_DISABLE_LOGGING session_log(": set_external_address(%s, %d, %s)", print_address(ip).c_str() , source_type, print_address(source).c_str()); #endif if (!m_external_ip.cast_vote(ip, source_type, source)) return; #ifndef TORRENT_DISABLE_LOGGING session_log(" external IP updated"); #endif if (m_alerts.should_post()) m_alerts.emplace_alert(ip); for (torrent_map::iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { i->second->new_external_ip(); } // since we have a new external IP now, we need to // restart the DHT with a new node ID #ifndef TORRENT_DISABLE_DHT if (m_dht) m_dht->update_node_id(); #endif } // decrement the refcount of the block in the disk cache // since the network thread doesn't need it anymore void session_impl::reclaim_block(block_cache_reference ref) { m_disk_thread.reclaim_block(ref); } char* session_impl::allocate_disk_buffer(char const* category) { return m_disk_thread.allocate_disk_buffer(category); } void session_impl::free_disk_buffer(char* buf) { m_disk_thread.free_disk_buffer(buf); } char* session_impl::allocate_disk_buffer(bool& exceeded , boost::shared_ptr o , char const* category) { return m_disk_thread.allocate_disk_buffer(exceeded, o, category); } char* session_impl::allocate_buffer() { TORRENT_ASSERT(is_single_thread()); #ifdef TORRENT_DISABLE_POOL_ALLOCATOR int num_bytes = send_buffer_size(); return static_cast(malloc(num_bytes)); #else return static_cast(m_send_buffers.malloc()); #endif } void session_impl::free_buffer(char* buf) { TORRENT_ASSERT(is_single_thread()); #ifdef TORRENT_DISABLE_POOL_ALLOCATOR free(buf); #else m_send_buffers.free(buf); #endif } #if TORRENT_USE_INVARIANT_CHECKS void session_impl::check_invariant() const { TORRENT_ASSERT(is_single_thread()); int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit); TORRENT_ASSERT(loaded_limit == 0 || !m_user_load_torrent || m_num_save_resume <= loaded_limit); // if (m_num_save_resume < loaded_limit) // TORRENT_ASSERT(m_save_resume_queue.empty()); TORRENT_ASSERT(m_torrents.size() >= m_torrent_lru.size()); if (m_settings.get_int(settings_pack::unchoke_slots_limit) < 0 && m_settings.get_int(settings_pack::choking_algorithm) == settings_pack::fixed_slots_choker) TORRENT_ASSERT(m_stats_counters[counters::num_unchoke_slots] == (std::numeric_limits::max)()); for (int l = 0; l < num_torrent_lists; ++l) { std::vector const& list = m_torrent_lists[l]; for (std::vector::const_iterator i = list.begin() , end(list.end()); i != end; ++i) { TORRENT_ASSERT((*i)->m_links[l].in_list()); } } #if TORRENT_HAS_BOOST_UNORDERED boost::unordered_set unique_torrents; #else std::set unique_torrents; #endif for (list_iterator i = m_torrent_lru.iterate(); i.get(); i.next()) { torrent* t = i.get(); TORRENT_ASSERT(t->is_loaded()); TORRENT_ASSERT(unique_torrents.count(t) == 0); unique_torrents.insert(t); } TORRENT_ASSERT(unique_torrents.size() == m_torrent_lru.size()); int torrent_state_gauges[counters::num_error_torrents - counters::num_checking_torrents + 1]; memset(torrent_state_gauges, 0, sizeof(torrent_state_gauges)); #if defined TORRENT_EXPENSIVE_INVARIANT_CHECKS #if TORRENT_HAS_BOOST_UNORDERED boost::unordered_set unique; #else std::set unique; #endif #endif int num_active_downloading = 0; int num_active_finished = 0; int total_downloaders = 0; for (torrent_map::const_iterator i = m_torrents.begin() , end(m_torrents.end()); i != end; ++i) { boost::shared_ptr t = i->second; if (t->want_peers_download()) ++num_active_downloading; if (t->want_peers_finished()) ++num_active_finished; TORRENT_ASSERT(!(t->want_peers_download() && t->want_peers_finished())); ++torrent_state_gauges[t->current_stats_state() - counters::num_checking_torrents]; int pos = t->queue_position(); if (pos < 0) { TORRENT_ASSERT(pos == -1); continue; } ++total_downloaders; #if defined TORRENT_EXPENSIVE_INVARIANT_CHECKS unique.insert(t->queue_position()); #endif } for (int i = 0, j = counters::num_checking_torrents; j < counters::num_error_torrents + 1; ++i, ++j) { TORRENT_ASSERT(torrent_state_gauges[i] == m_stats_counters[j]); } #if defined TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_ASSERT(int(unique.size()) == total_downloaders); #endif TORRENT_ASSERT(num_active_downloading == m_torrent_lists[torrent_want_peers_download].size()); TORRENT_ASSERT(num_active_finished == m_torrent_lists[torrent_want_peers_finished].size()); #if TORRENT_HAS_BOOST_UNORDERED boost::unordered_set unique_peers; #else std::set unique_peers; #endif int unchokes = 0; int unchokes_all = 0; int num_optimistic = 0; int disk_queue[2] = {0, 0}; for (connection_map::const_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { TORRENT_ASSERT(*i); boost::shared_ptr t = (*i)->associated_torrent().lock(); TORRENT_ASSERT(unique_peers.find(i->get()) == unique_peers.end()); unique_peers.insert(i->get()); if ((*i)->m_channel_state[0] & peer_info::bw_disk) ++disk_queue[0]; if ((*i)->m_channel_state[1] & peer_info::bw_disk) ++disk_queue[1]; peer_connection* p = i->get(); TORRENT_ASSERT(!p->is_disconnecting()); if (p->ignore_unchoke_slots()) { if (!p->is_choked()) ++unchokes_all; continue; } if (!p->is_choked()) { ++unchokes; ++unchokes_all; } if (p->peer_info_struct() && p->peer_info_struct()->optimistically_unchoked) { ++num_optimistic; TORRENT_ASSERT(!p->is_choked()); } } for (std::vector >::const_iterator i = m_undead_peers.begin(); i != m_undead_peers.end(); ++i) { peer_connection* p = i->get(); if (p->ignore_unchoke_slots()) { if (!p->is_choked()) ++unchokes_all; continue; } if (!p->is_choked()) { ++unchokes_all; ++unchokes; } if (p->peer_info_struct() && p->peer_info_struct()->optimistically_unchoked) { ++num_optimistic; TORRENT_ASSERT(!p->is_choked()); } } TORRENT_ASSERT(disk_queue[peer_connection::download_channel] == m_stats_counters[counters::num_peers_down_disk]); TORRENT_ASSERT(disk_queue[peer_connection::upload_channel] == m_stats_counters[counters::num_peers_up_disk]); if (m_settings.get_int(settings_pack::num_optimistic_unchoke_slots)) { TORRENT_ASSERT(num_optimistic <= m_settings.get_int( settings_pack::num_optimistic_unchoke_slots)); } int const unchoked_counter_all = m_stats_counters[counters::num_peers_up_unchoked_all]; int const unchoked_counter = m_stats_counters[counters::num_peers_up_unchoked]; int const unchoked_counter_optimistic = m_stats_counters[counters::num_peers_up_unchoked_optimistic]; TORRENT_ASSERT_VAL(unchoked_counter_all == unchokes_all, unchokes_all); TORRENT_ASSERT_VAL(unchoked_counter == unchokes, unchokes); TORRENT_ASSERT_VAL(unchoked_counter_optimistic == num_optimistic, num_optimistic); for (torrent_map::const_iterator j = m_torrents.begin(); j != m_torrents.end(); ++j) { TORRENT_ASSERT(boost::get_pointer(j->second)); } } #endif // TORRENT_USE_INVARIANT_CHECKS #ifndef TORRENT_DISABLE_LOGGING tracker_logger::tracker_logger(session_interface& ses): m_ses(ses) {} void tracker_logger::tracker_warning(tracker_request const& , std::string const& str) { debug_log("*** tracker warning: %s", str.c_str()); } void tracker_logger::tracker_response(tracker_request const& , libtorrent::address const& tracker_ip , std::list
const& tracker_ips , struct tracker_response const& resp) { TORRENT_UNUSED(tracker_ips); debug_log("TRACKER RESPONSE\n" "interval: %d\n" "external ip: %s\n" "we connected to: %s\n" "peers:" , resp.interval , print_address(resp.external_ip).c_str() , print_address(tracker_ip).c_str()); for (std::vector::const_iterator i = resp.peers.begin(); i != resp.peers.end(); ++i) { debug_log(" %16s %5d %s %s", i->hostname.c_str(), i->port , i->pid.is_all_zeros()?"":to_hex(i->pid.to_string()).c_str() , identify_client(i->pid).c_str()); } for (std::vector::const_iterator i = resp.peers4.begin(); i != resp.peers4.end(); ++i) { debug_log(" %s:%d", print_address(address_v4(i->ip)).c_str(), i->port); } #if TORRENT_USE_IPV6 for (std::vector::const_iterator i = resp.peers6.begin(); i != resp.peers6.end(); ++i) { debug_log(" [%s]:%d", print_address(address_v6(i->ip)).c_str(), i->port); } #endif } void tracker_logger::tracker_request_timed_out( tracker_request const&) { debug_log("*** tracker timed out"); } void tracker_logger::tracker_request_error(tracker_request const& , int response_code, error_code const& ec, const std::string& str , int retry_interval) { TORRENT_UNUSED(retry_interval); debug_log("*** tracker error: %d: %s %s" , response_code, ec.message().c_str(), str.c_str()); } void tracker_logger::debug_log(const char* fmt, ...) const { va_list v; va_start(v, fmt); char usr[1024]; vsnprintf(usr, sizeof(usr), fmt, v); va_end(v); m_ses.session_log("%s", usr); } #endif // TORRENT_DISABLE_LOGGING }} libtorrent-rasterbar-1.1.13/src/session_settings.cpp000066400000000000000000000035151351156116000226450ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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/aux_/session_settings.hpp" #include "libtorrent/settings_pack.hpp" namespace libtorrent { namespace aux { session_settings::session_settings() { initialize_default_settings(*this); } session_settings::session_settings(settings_pack const& p) { initialize_default_settings(*this); apply_pack(&p, *this); } } } libtorrent-rasterbar-1.1.13/src/session_stats.cpp000066400000000000000000000505671351156116000221540ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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/session_stats.hpp" // for stats_metric #include "libtorrent/aux_/session_interface.hpp" // for stats counter names #include "libtorrent/performance_counters.hpp" // for counters #include namespace libtorrent { struct stats_metric_impl { char const* name; int value_index; }; #define METRIC(category, name) { #category "." #name, counters:: name }, static const stats_metric_impl metrics[] = { // ``error_peers`` is the total number of peer disconnects // caused by an error (not initiated by this client) and // disconnected initiated by this client (``disconnected_peers``). METRIC(peer, error_peers) METRIC(peer, disconnected_peers) // these counters break down the peer errors into more specific // categories. These errors are what the underlying transport // reported (i.e. TCP or uTP) METRIC(peer, eof_peers) METRIC(peer, connreset_peers) METRIC(peer, connrefused_peers) METRIC(peer, connaborted_peers) METRIC(peer, notconnected_peers) METRIC(peer, perm_peers) METRIC(peer, buffer_peers) METRIC(peer, unreachable_peers) METRIC(peer, broken_pipe_peers) METRIC(peer, addrinuse_peers) METRIC(peer, no_access_peers) METRIC(peer, invalid_arg_peers) METRIC(peer, aborted_peers) // the total number of incoming piece requests we've received followed // by the number of rejected piece requests for various reasons. // max_piece_requests mean we already had too many outstanding requests // from this peer, so we rejected it. cancelled_piece_requests are ones // where the other end explicitly asked for the piece to be rejected. METRIC(peer, piece_requests) METRIC(peer, max_piece_requests) METRIC(peer, invalid_piece_requests) METRIC(peer, choked_piece_requests) METRIC(peer, cancelled_piece_requests) METRIC(peer, piece_rejects) // these counters break down the peer errors into // whether they happen on incoming or outgoing peers. METRIC(peer, error_incoming_peers) METRIC(peer, error_outgoing_peers) // these counters break down the peer errors into // whether they happen on encrypted peers (just // encrypted handshake) and rc4 peers (full stream // encryption). These can indicate whether encrypted // peers are more or less likely to fail METRIC(peer, error_rc4_peers) METRIC(peer, error_encrypted_peers) // these counters break down the peer errors into // whether they happen on uTP peers or TCP peers. // these may indicate whether one protocol is // more error prone METRIC(peer, error_tcp_peers) METRIC(peer, error_utp_peers) // these counters break down the reasons to // disconnect peers. METRIC(peer, connect_timeouts) METRIC(peer, uninteresting_peers) METRIC(peer, timeout_peers) METRIC(peer, no_memory_peers) METRIC(peer, too_many_peers) METRIC(peer, transport_timeout_peers) METRIC(peer, num_banned_peers) METRIC(peer, banned_for_hash_failure) METRIC(peer, connection_attempts) METRIC(peer, connection_attempt_loops) METRIC(peer, incoming_connections) // the number of peer connections for each kind of socket. // these counts include half-open (connecting) peers. // ``num_peers_up_unchoked_all`` is the total number of unchoked peers, // whereas ``num_peers_up_unchoked`` only are unchoked peers that count // against the limit (i.e. excluding peers that are unchoked because the // limit doesn't apply to them). ``num_peers_up_unchoked_optimistic`` is // the number of optimistically unchoked peers. METRIC(peer, num_tcp_peers) METRIC(peer, num_socks5_peers) METRIC(peer, num_http_proxy_peers) METRIC(peer, num_utp_peers) METRIC(peer, num_i2p_peers) METRIC(peer, num_ssl_peers) METRIC(peer, num_ssl_socks5_peers) METRIC(peer, num_ssl_http_proxy_peers) METRIC(peer, num_ssl_utp_peers) METRIC(peer, num_peers_half_open) METRIC(peer, num_peers_connected) METRIC(peer, num_peers_up_interested) METRIC(peer, num_peers_down_interested) METRIC(peer, num_peers_up_unchoked_all) METRIC(peer, num_peers_up_unchoked_optimistic) METRIC(peer, num_peers_up_unchoked) METRIC(peer, num_peers_down_unchoked) METRIC(peer, num_peers_up_requests) METRIC(peer, num_peers_down_requests) METRIC(peer, num_peers_end_game) METRIC(peer, num_peers_up_disk) METRIC(peer, num_peers_down_disk) // These counters count the number of times the // network thread wakes up for each respective // reason. If these counters are very large, it // may indicate a performance issue, causing the // network thread to wake up too ofte, wasting CPU. // mitigate it by increasing buffers and limits // for the specific trigger that wakes up the // thread. METRIC(net, on_read_counter) METRIC(net, on_write_counter) METRIC(net, on_tick_counter) METRIC(net, on_lsd_counter) METRIC(net, on_lsd_peer_counter) METRIC(net, on_udp_counter) METRIC(net, on_accept_counter) METRIC(net, on_disk_queue_counter) METRIC(net, on_disk_counter) // total number of bytes sent and received by the session METRIC(net, sent_payload_bytes) METRIC(net, sent_bytes) METRIC(net, sent_ip_overhead_bytes) METRIC(net, sent_tracker_bytes) METRIC(net, recv_payload_bytes) METRIC(net, recv_bytes) METRIC(net, recv_ip_overhead_bytes) METRIC(net, recv_tracker_bytes) // the number of sockets currently waiting for upload and download // bandwidht from the rate limiter. METRIC(net, limiter_up_queue) METRIC(net, limiter_down_queue) // the number of upload and download bytes waiting to be handed out from // the rate limiter. METRIC(net, limiter_up_bytes) METRIC(net, limiter_down_bytes) // the number of bytes downloaded that had to be discarded because they // failed the hash check METRIC(net, recv_failed_bytes) // the number of downloaded bytes that were discarded because they // were downloaded multiple times (from different peers) METRIC(net, recv_redundant_bytes) // 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. METRIC(net, has_incoming_connections) // these gauges count the number of torrents in // different states. Each torrent only belongs to // one of these states. For torrents that could // belong to multiple of these, the most prominent // in picked. For instance, a torrent with an error // counts as an error-torrent, regardless of its other // state. METRIC(ses, num_checking_torrents) METRIC(ses, num_stopped_torrents) METRIC(ses, num_upload_only_torrents) METRIC(ses, num_downloading_torrents) METRIC(ses, num_seeding_torrents) METRIC(ses, num_queued_seeding_torrents) METRIC(ses, num_queued_download_torrents) METRIC(ses, num_error_torrents) // the number of torrents that don't have the // IP filter applied to them. METRIC(ses, non_filter_torrents) // the number of torrents that are currently loaded METRIC(ses, num_loaded_torrents) METRIC(ses, num_pinned_torrents) // these count the number of times a piece has passed the // hash check, the number of times a piece was successfully // written to disk and the number of total possible pieces // added by adding torrents. e.g. when adding a torrent with // 1000 piece, num_total_pieces_added is incremented by 1000. METRIC(ses, num_piece_passed) METRIC(ses, num_piece_failed) METRIC(ses, num_have_pieces) METRIC(ses, num_total_pieces_added) // this counts the number of times a torrent has been // evicted (only applies when `dynamic loading of torrent files`_ // is enabled). METRIC(ses, torrent_evicted_counter) // the number of allowed unchoked peers METRIC(ses, num_unchoke_slots) // bittorrent message counters. These counters are incremented // every time a message of the corresponding type is received from // or sent to a bittorrent peer. METRIC(ses, num_incoming_choke) METRIC(ses, num_incoming_unchoke) METRIC(ses, num_incoming_interested) METRIC(ses, num_incoming_not_interested) METRIC(ses, num_incoming_have) METRIC(ses, num_incoming_bitfield) METRIC(ses, num_incoming_request) METRIC(ses, num_incoming_piece) METRIC(ses, num_incoming_cancel) METRIC(ses, num_incoming_dht_port) METRIC(ses, num_incoming_suggest) METRIC(ses, num_incoming_have_all) METRIC(ses, num_incoming_have_none) METRIC(ses, num_incoming_reject) METRIC(ses, num_incoming_allowed_fast) METRIC(ses, num_incoming_ext_handshake) METRIC(ses, num_incoming_pex) METRIC(ses, num_incoming_metadata) METRIC(ses, num_incoming_extended) METRIC(ses, num_outgoing_choke) METRIC(ses, num_outgoing_unchoke) METRIC(ses, num_outgoing_interested) METRIC(ses, num_outgoing_not_interested) METRIC(ses, num_outgoing_have) METRIC(ses, num_outgoing_bitfield) METRIC(ses, num_outgoing_request) METRIC(ses, num_outgoing_piece) METRIC(ses, num_outgoing_cancel) METRIC(ses, num_outgoing_dht_port) METRIC(ses, num_outgoing_suggest) METRIC(ses, num_outgoing_have_all) METRIC(ses, num_outgoing_have_none) METRIC(ses, num_outgoing_reject) METRIC(ses, num_outgoing_allowed_fast) METRIC(ses, num_outgoing_ext_handshake) METRIC(ses, num_outgoing_pex) METRIC(ses, num_outgoing_metadata) METRIC(ses, num_outgoing_extended) // the number of wasted downloaded bytes by reason of the bytes being // wasted. METRIC(ses, waste_piece_timed_out) METRIC(ses, waste_piece_cancelled) METRIC(ses, waste_piece_unknown) METRIC(ses, waste_piece_seed) METRIC(ses, waste_piece_end_game) METRIC(ses, waste_piece_closing) // the number of pieces considered while picking pieces METRIC(picker, piece_picker_partial_loops) METRIC(picker, piece_picker_suggest_loops) METRIC(picker, piece_picker_sequential_loops) METRIC(picker, piece_picker_reverse_rare_loops) METRIC(picker, piece_picker_rare_loops) METRIC(picker, piece_picker_rand_start_loops) METRIC(picker, piece_picker_rand_loops) METRIC(picker, piece_picker_busy_loops) // This breaks down the piece picks into the event that // triggered it METRIC(picker, reject_piece_picks) METRIC(picker, unchoke_piece_picks) METRIC(picker, incoming_redundant_piece_picks) METRIC(picker, incoming_piece_picks) METRIC(picker, end_game_piece_picks) METRIC(picker, snubbed_piece_picks) METRIC(picker, interesting_piece_picks) METRIC(picker, hash_fail_piece_picks) // These gauges indicate how many blocks are currently in use as dirty // disk blocks (``write_cache_blocks``) and read cache blocks, // respectively. deprecates ``cache_status::read_cache_size``. // The sum of these gauges deprecates ``cache_status::cache_size``. METRIC(disk, write_cache_blocks) METRIC(disk, read_cache_blocks) // the number of microseconds it takes from receiving a request from a // peer until we're sending the response back on the socket. METRIC(disk, request_latency) // ``disk_blocks_in_use`` indicates how many disk blocks are currently in // use, either as dirty blocks waiting to be written or blocks kept around // in the hope that a peer will request it or in a peer send buffer. This // gauge deprecates ``cache_status::total_used_buffers``. METRIC(disk, pinned_blocks) METRIC(disk, disk_blocks_in_use) // ``queued_disk_jobs`` is the number of disk jobs currently queued, // waiting to be executed by a disk thread. Deprecates // ``cache_status::job_queue_length``. METRIC(disk, queued_disk_jobs) METRIC(disk, num_running_disk_jobs) METRIC(disk, num_read_jobs) METRIC(disk, num_write_jobs) METRIC(disk, num_jobs) METRIC(disk, blocked_disk_jobs) METRIC(disk, num_writing_threads) METRIC(disk, num_running_threads) // the number of bytes we have sent to the disk I/O // thread for writing. Every time we hear back from // the disk I/O thread with a completed write job, this // is updated to the number of bytes the disk I/O thread // is actually waiting for to be written (as opposed to // bytes just hanging out in the cache) METRIC(disk, queued_write_bytes) METRIC(disk, arc_mru_size) METRIC(disk, arc_mru_ghost_size) METRIC(disk, arc_mfu_size) METRIC(disk, arc_mfu_ghost_size) METRIC(disk, arc_write_size) METRIC(disk, arc_volatile_size) // the number of blocks written and read from disk in total. A block is 16 // kiB. ``num_blocks_written`` and ``num_blocks_read`` deprecates // ``cache_status::blocks_written`` and ``cache_status::blocks_read`` respectively. METRIC(disk, num_blocks_written) METRIC(disk, num_blocks_read) // the total number of blocks run through SHA-1 hashing METRIC(disk, num_blocks_hashed) // the number of blocks read from the disk cache // Deprecates ``cache_info::blocks_read_hit``. METRIC(disk, num_blocks_cache_hits) // the number of disk I/O operation for reads and writes. One disk // operation may transfer more then one block. // These counters deprecates ``cache_status::writes`` and // ``cache_status::reads``. METRIC(disk, num_write_ops) METRIC(disk, num_read_ops) // the number of blocks that had to be read back from disk in order to // hash a piece (when verifying against the piece hash) METRIC(disk, num_read_back) // cumulative time spent in various disk jobs, as well // as total for all disk jobs. Measured in microseconds METRIC(disk, disk_read_time) METRIC(disk, disk_write_time) METRIC(disk, disk_hash_time) METRIC(disk, disk_job_time) // for each kind of disk job, a counter of how many jobs of that kind // are currently blocked by a disk fence METRIC(disk, num_fenced_read) METRIC(disk, num_fenced_write) METRIC(disk, num_fenced_hash) METRIC(disk, num_fenced_move_storage) METRIC(disk, num_fenced_release_files) METRIC(disk, num_fenced_delete_files) METRIC(disk, num_fenced_check_fastresume) METRIC(disk, num_fenced_save_resume_data) METRIC(disk, num_fenced_rename_file) METRIC(disk, num_fenced_stop_torrent) METRIC(disk, num_fenced_cache_piece) METRIC(disk, num_fenced_flush_piece) METRIC(disk, num_fenced_flush_hashed) METRIC(disk, num_fenced_flush_storage) METRIC(disk, num_fenced_trim_cache) METRIC(disk, num_fenced_file_priority) METRIC(disk, num_fenced_load_torrent) METRIC(disk, num_fenced_clear_piece) METRIC(disk, num_fenced_tick_storage) // The number of nodes in the DHT routing table METRIC(dht, dht_nodes) // The number of replacement nodes in the DHT routing table METRIC(dht, dht_node_cache) // the number of torrents currently tracked by our DHT node METRIC(dht, dht_torrents) // the number of peers currently tracked by our DHT node METRIC(dht, dht_peers) // the number of immutable data items tracked by our DHT node METRIC(dht, dht_immutable_data) // the number of mutable data items tracked by our DHT node METRIC(dht, dht_mutable_data) // the number of RPC observers currently allocated METRIC(dht, dht_allocated_observers) // the total number of DHT messages sent and received METRIC(dht, dht_messages_in) METRIC(dht, dht_messages_out) // the number of outgoing messages that failed to be // sent METRIC(dht, dht_messages_out_dropped) // the total number of bytes sent and received by the DHT METRIC(dht, dht_bytes_in) METRIC(dht, dht_bytes_out) // the number of DHT messages we've sent and received // by kind. METRIC(dht, dht_ping_in) METRIC(dht, dht_ping_out) METRIC(dht, dht_find_node_in) METRIC(dht, dht_find_node_out) METRIC(dht, dht_get_peers_in) METRIC(dht, dht_get_peers_out) METRIC(dht, dht_announce_peer_in) METRIC(dht, dht_announce_peer_out) METRIC(dht, dht_get_in) METRIC(dht, dht_get_out) METRIC(dht, dht_put_in) METRIC(dht, dht_put_out) // the number of failed incoming DHT requests by kind of request METRIC(dht, dht_invalid_announce) METRIC(dht, dht_invalid_get_peers) METRIC(dht, dht_invalid_put) METRIC(dht, dht_invalid_get) // uTP counters. Each counter represents the number of time each event // has occurred. METRIC(utp, utp_packet_loss) METRIC(utp, utp_timeout) METRIC(utp, utp_packets_in) METRIC(utp, utp_packets_out) METRIC(utp, utp_fast_retransmit) METRIC(utp, utp_packet_resend) METRIC(utp, utp_samples_above_target) METRIC(utp, utp_samples_below_target) METRIC(utp, utp_payload_pkts_in) METRIC(utp, utp_payload_pkts_out) METRIC(utp, utp_invalid_pkts_in) METRIC(utp, utp_redundant_pkts_in) // the number of uTP sockets in each respective state METRIC(utp, num_utp_idle) METRIC(utp, num_utp_syn_sent) METRIC(utp, num_utp_connected) METRIC(utp, num_utp_fin_sent) METRIC(utp, num_utp_close_wait) METRIC(utp, num_utp_deleted) // the buffer sizes accepted by // socket send and receive calls respectively. // The larger the buffers are, the more efficient, // because it reqire fewer system calls per byte. // The size is 1 << n, where n is the number // at the end of the counter name. i.e. // 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, // 16384, 32768, 65536, 131072, 262144, 524288, 1048576 // bytes METRIC(sock_bufs, socket_send_size3) METRIC(sock_bufs, socket_send_size4) METRIC(sock_bufs, socket_send_size5) METRIC(sock_bufs, socket_send_size6) METRIC(sock_bufs, socket_send_size7) METRIC(sock_bufs, socket_send_size8) METRIC(sock_bufs, socket_send_size9) METRIC(sock_bufs, socket_send_size10) METRIC(sock_bufs, socket_send_size11) METRIC(sock_bufs, socket_send_size12) METRIC(sock_bufs, socket_send_size13) METRIC(sock_bufs, socket_send_size14) METRIC(sock_bufs, socket_send_size15) METRIC(sock_bufs, socket_send_size16) METRIC(sock_bufs, socket_send_size17) METRIC(sock_bufs, socket_send_size18) METRIC(sock_bufs, socket_send_size19) METRIC(sock_bufs, socket_send_size20) METRIC(sock_bufs, socket_recv_size3) METRIC(sock_bufs, socket_recv_size4) METRIC(sock_bufs, socket_recv_size5) METRIC(sock_bufs, socket_recv_size6) METRIC(sock_bufs, socket_recv_size7) METRIC(sock_bufs, socket_recv_size8) METRIC(sock_bufs, socket_recv_size9) METRIC(sock_bufs, socket_recv_size10) METRIC(sock_bufs, socket_recv_size11) METRIC(sock_bufs, socket_recv_size12) METRIC(sock_bufs, socket_recv_size13) METRIC(sock_bufs, socket_recv_size14) METRIC(sock_bufs, socket_recv_size15) METRIC(sock_bufs, socket_recv_size16) METRIC(sock_bufs, socket_recv_size17) METRIC(sock_bufs, socket_recv_size18) METRIC(sock_bufs, socket_recv_size19) METRIC(sock_bufs, socket_recv_size20) // ... more }; #undef METRIC std::vector session_stats_metrics() { std::vector stats; const int num = sizeof(metrics)/sizeof(metrics[0]); stats.resize(num); for (int i = 0; i < num; ++i) { stats[i].name = metrics[i].name; stats[i].value_index = metrics[i].value_index; stats[i].type = metrics[i].value_index >= counters::num_stats_counters ? stats_metric::type_gauge : stats_metric::type_counter; } return stats; } int find_metric_idx(char const* name) { stats_metric_impl const* end = metrics + sizeof(metrics)/sizeof(metrics[0]); stats_metric_impl const* i = std::find_if(metrics, end , boost::bind(&strcmp , boost::bind(&stats_metric_impl::name, _1), name) == 0); if (i == end) return -1; return i->value_index; } } libtorrent-rasterbar-1.1.13/src/settings_pack.cpp000066400000000000000000000777361351156116000221200ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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/config.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/settings_pack.hpp" #include "libtorrent/aux_/session_impl.hpp" #include namespace { template bool compare_first(std::pair const& lhs , std::pair const& rhs) { return lhs.first < rhs.first; } template void insort_replace(std::vector >& c, std::pair const& v) { typedef std::vector > container_t; typename container_t::iterator i = std::lower_bound(c.begin(), c.end(), v , &compare_first); if (i != c.end() && i->first == v.first) i->second = v.second; else c.insert(i, v); } // return the string, unless it's null, in which case the empty string is // returned char const* ensure_string(char const* str) { return str == NULL ? "" : str; } } namespace libtorrent { struct str_setting_entry_t { // the name of this setting. used for serialization and deserialization char const* name; // if present, this function is called when the setting is changed void (aux::session_impl::*fun)(); char const *default_value; #ifndef TORRENT_NO_DEPRECATE // offset into session_settings, used to map // settings to the deprecated settings struct int offset; #endif }; struct int_setting_entry_t { // the name of this setting. used for serialization and deserialization char const* name; // if present, this function is called when the setting is changed void (aux::session_impl::*fun)(); int default_value; #ifndef TORRENT_NO_DEPRECATE // offset into session_settings, used to map // settings to the deprecated settings struct int offset; #endif }; struct bool_setting_entry_t { // the name of this setting. used for serialization and deserialization char const* name; // if present, this function is called when the setting is changed void (aux::session_impl::*fun)(); bool default_value; #ifndef TORRENT_NO_DEPRECATE // offset into session_settings, used to map // settings to the deprecated settings struct int offset; #endif }; // SET_NOPREV - this is used for new settings that don't exist in the // deprecated session_settings. #ifdef TORRENT_NO_DEPRECATE #define SET(name, default_value, fun) { #name, fun, default_value } #define SET_NOPREV(name, default_value, fun) { #name, fun, default_value } #define DEPRECATED_SET(name, default_value, fun) { "", NULL, 0 } #else #define SET(name, default_value, fun) { #name, fun, default_value, offsetof(libtorrent::session_settings, name) } #define SET_NOPREV(name, default_value, fun) { #name, fun, default_value, 0 } #define DEPRECATED_SET(name, default_value, fun) { #name, fun, default_value, offsetof(libtorrent::session_settings, name) } #endif #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Winvalid-offsetof" #endif #ifdef TORRENT_WINDOWS #define CLOSE_FILE_INTERVAL 120 #else #define CLOSE_FILE_INTERVAL 0 #endif namespace { using aux::session_impl; str_setting_entry_t str_settings[settings_pack::num_string_settings] = { SET(user_agent, "libtorrent/" LIBTORRENT_VERSION, &session_impl::update_user_agent), SET(announce_ip, 0, 0), SET(mmap_cache, 0, 0), SET(handshake_client_version, 0, 0), SET_NOPREV(outgoing_interfaces, "", &session_impl::update_outgoing_interfaces), SET_NOPREV(listen_interfaces, "0.0.0.0:6881", &session_impl::update_listen_interfaces), SET_NOPREV(proxy_hostname, "", &session_impl::update_proxy), SET_NOPREV(proxy_username, "", &session_impl::update_proxy), SET_NOPREV(proxy_password, "", &session_impl::update_proxy), SET_NOPREV(i2p_hostname, "", &session_impl::update_i2p_bridge), SET_NOPREV(peer_fingerprint, "-LT11D0-", 0), SET_NOPREV(dht_bootstrap_nodes, "dht.libtorrent.org:25401", &session_impl::update_dht_bootstrap_nodes) }; bool_setting_entry_t bool_settings[settings_pack::num_bool_settings] = { SET(allow_multiple_connections_per_ip, false, 0), DEPRECATED_SET(ignore_limits_on_local_network, true, &session_impl::update_ignore_rate_limits_on_local_network), SET(send_redundant_have, true, 0), SET(lazy_bitfields, false, 0), SET(use_dht_as_fallback, false, 0), SET(upnp_ignore_nonrouters, false, 0), SET(use_parole_mode, true, 0), SET(use_read_cache, true, 0), DEPRECATED_SET(use_write_cache, true, 0), DEPRECATED_SET(dont_flush_write_cache, false, 0), DEPRECATED_SET(explicit_read_cache, false, 0), #ifdef TORRENT_WINDOWS // the emulation of preadv/pwritev uses overlapped reads/writes to be able // to issue them all back to back. However, it appears windows fail to // merge them. At least for people reporting performance issues in // qBittorrent SET(coalesce_reads, true, 0), SET(coalesce_writes, true, 0), #else SET(coalesce_reads, false, 0), SET(coalesce_writes, false, 0), #endif SET(auto_manage_prefer_seeds, false, 0), SET(dont_count_slow_torrents, true, &session_impl::update_count_slow), SET(close_redundant_connections, true, 0), SET(prioritize_partial_pieces, false, 0), SET(rate_limit_ip_overhead, true, 0), SET(announce_to_all_trackers, false, 0), SET(announce_to_all_tiers, false, 0), SET(prefer_udp_trackers, true, 0), SET(strict_super_seeding, false, 0), DEPRECATED_SET(lock_disk_cache, false, 0), SET(disable_hash_checks, false, 0), SET(allow_i2p_mixed, false, 0), SET(low_prio_disk, true, 0), SET(volatile_read_cache, false, 0), SET(guided_read_cache, false, 0), SET(no_atime_storage, true, 0), SET(incoming_starts_queued_torrents, false, 0), SET(report_true_downloaded, false, 0), SET(strict_end_game_mode, true, 0), SET(broadcast_lsd, true, 0), SET(enable_outgoing_utp, true, 0), SET(enable_incoming_utp, true, 0), SET(enable_outgoing_tcp, true, 0), SET(enable_incoming_tcp, true, 0), SET(ignore_resume_timestamps, false, 0), SET(no_recheck_incomplete_resume, false, 0), SET(anonymous_mode, false, &session_impl::update_anonymous_mode), SET(report_web_seed_downloads, true, &session_impl::update_report_web_seed_downloads), DEPRECATED_SET(rate_limit_utp, true, &session_impl::update_rate_limit_utp), SET(announce_double_nat, false, 0), SET(seeding_outgoing_connections, true, 0), SET(no_connect_privileged_ports, false, &session_impl::update_privileged_ports), SET(smooth_connects, true, 0), SET(always_send_user_agent, false, 0), SET(apply_ip_filter_to_trackers, true, 0), SET(use_disk_read_ahead, true, 0), SET(lock_files, false, 0), SET(contiguous_recv_buffer, true, 0), SET(ban_web_seeds, true, 0), SET_NOPREV(allow_partial_disk_writes, true, 0), SET(force_proxy, false, &session_impl::update_force_proxy), SET(support_share_mode, true, 0), SET(support_merkle_torrents, true, 0), SET(report_redundant_bytes, true, 0), SET_NOPREV(listen_system_port_fallback, true, 0), SET(use_disk_cache_pool, false, 0), SET_NOPREV(announce_crypto_support, true, 0), SET_NOPREV(enable_upnp, true, &session_impl::update_upnp), SET_NOPREV(enable_natpmp, true, &session_impl::update_natpmp), SET_NOPREV(enable_lsd, true, &session_impl::update_lsd), SET_NOPREV(enable_dht, true, &session_impl::update_dht), SET_NOPREV(prefer_rc4, false, 0), SET_NOPREV(proxy_hostnames, true, 0), SET_NOPREV(proxy_peer_connections, true, 0), SET_NOPREV(auto_sequential, true, &session_impl::update_auto_sequential), SET_NOPREV(proxy_tracker_connections, true, 0), }; int_setting_entry_t int_settings[settings_pack::num_int_settings] = { SET(tracker_completion_timeout, 30, 0), SET(tracker_receive_timeout, 10, 0), SET(stop_tracker_timeout, 5, 0), SET(tracker_maximum_response_length, 1024*1024, 0), SET(piece_timeout, 20, 0), SET(request_timeout, 60, 0), SET(request_queue_time, 3, 0), SET(max_allowed_in_request_queue, 500, 0), SET(max_out_request_queue, 500, 0), SET(whole_pieces_threshold, 20, 0), SET(peer_timeout, 120, 0), SET(urlseed_timeout, 20, 0), SET(urlseed_pipeline_size, 5, 0), SET(urlseed_wait_retry, 30, 0), SET(file_pool_size, 40, 0), SET(max_failcount, 3, &session_impl::update_max_failcount), SET(min_reconnect_time, 60, 0), SET(peer_connect_timeout, 15, 0), SET(connection_speed, 30, &session_impl::update_connection_speed), SET(inactivity_timeout, 600, 0), SET(unchoke_interval, 15, 0), SET(optimistic_unchoke_interval, 30, 0), SET(num_want, 200, 0), SET(initial_picker_threshold, 4, 0), SET(allowed_fast_set_size, 10, 0), SET(suggest_mode, settings_pack::no_piece_suggestions, 0), SET(max_queued_disk_bytes, 1024 * 1024, &session_impl::update_queued_disk_bytes), SET(handshake_timeout, 10, 0), SET(send_buffer_low_watermark, 10 * 1024, 0), SET(send_buffer_watermark, 500 * 1024, 0), SET(send_buffer_watermark_factor, 50, 0), SET(choking_algorithm, settings_pack::fixed_slots_choker, 0), SET(seed_choking_algorithm, settings_pack::round_robin, 0), SET(cache_size, 1024, 0), SET(cache_buffer_chunk_size, 0, &session_impl::update_cache_buffer_chunk_size), SET(cache_expiry, 300, 0), DEPRECATED_SET(explicit_cache_interval, 30, 0), SET(disk_io_write_mode, settings_pack::enable_os_cache, 0), SET(disk_io_read_mode, settings_pack::enable_os_cache, 0), SET(outgoing_port, 0, 0), SET(num_outgoing_ports, 0, 0), SET(peer_tos, 0x20, &session_impl::update_peer_tos), SET(active_downloads, 3, &session_impl::trigger_auto_manage), SET(active_seeds, 5, &session_impl::trigger_auto_manage), SET_NOPREV(active_checking, 1, &session_impl::trigger_auto_manage), SET(active_dht_limit, 88, 0), SET(active_tracker_limit, 1600, 0), SET(active_lsd_limit, 60, 0), SET(active_limit, 500, &session_impl::trigger_auto_manage), SET_NOPREV(active_loaded_limit, 0, &session_impl::trigger_auto_manage), SET(auto_manage_interval, 30, 0), SET(seed_time_limit, 24 * 60 * 60, 0), SET(auto_scrape_interval, 1800, 0), SET(auto_scrape_min_interval, 300, 0), SET(max_peerlist_size, 3000, 0), SET(max_paused_peerlist_size, 1000, 0), SET(min_announce_interval, 5 * 60, 0), SET(auto_manage_startup, 60, 0), SET(seeding_piece_quota, 20, 0), SET(max_rejects, 50, 0), SET(recv_socket_buffer_size, 0, &session_impl::update_socket_buffer_size), SET(send_socket_buffer_size, 0, &session_impl::update_socket_buffer_size), DEPRECATED_SET(file_checks_delay_per_block, 0, 0), SET(read_cache_line_size, 32, 0), SET(write_cache_line_size, 16, 0), SET(optimistic_disk_retry, 10 * 60, 0), SET(max_suggest_pieces, 10, 0), SET(local_service_announce_interval, 5 * 60, 0), SET(dht_announce_interval, 15 * 60, &session_impl::update_dht_announce_interval), SET(udp_tracker_token_expiry, 60, 0), SET(default_cache_min_age, 1, 0), SET(num_optimistic_unchoke_slots, 0, 0), SET(default_est_reciprocation_rate, 16000, 0), SET(increase_est_reciprocation_rate, 20, 0), SET(decrease_est_reciprocation_rate, 3, 0), SET(max_pex_peers, 50, 0), SET(tick_interval, 500, 0), SET(share_mode_target, 3, 0), SET(upload_rate_limit, 0, &session_impl::update_upload_rate), SET(download_rate_limit, 0, &session_impl::update_download_rate), DEPRECATED_SET(local_upload_rate_limit, 0, &session_impl::update_local_upload_rate), DEPRECATED_SET(local_download_rate_limit, 0, &session_impl::update_local_download_rate), SET(dht_upload_rate_limit, 4000, &session_impl::update_dht_upload_rate_limit), SET(unchoke_slots_limit, 8, &session_impl::update_unchoke_limit), DEPRECATED_SET(half_open_limit, 0, 0), SET(connections_limit, 200, &session_impl::update_connections_limit), SET(connections_slack, 10, 0), SET(utp_target_delay, 100, 0), SET(utp_gain_factor, 3000, 0), SET(utp_min_timeout, 500, 0), SET(utp_syn_resends, 2, 0), SET(utp_fin_resends, 2, 0), SET(utp_num_resends, 3, 0), SET(utp_connect_timeout, 3000, 0), SET(utp_delayed_ack, 0, 0), SET(utp_loss_multiplier, 50, 0), SET(mixed_mode_algorithm, settings_pack::peer_proportional, 0), SET(listen_queue_size, 5, 0), SET(torrent_connect_boost, 30, 0), SET(alert_queue_size, 1000, &session_impl::update_alert_queue_size), SET(max_metadata_size, 3 * 1024 * 10240, 0), DEPRECATED_SET(hashing_threads, 1, 0), SET(checking_mem_usage, 1024, 0), SET(predictive_piece_announce, 0, 0), SET(aio_threads, 4, &session_impl::update_disk_threads), SET(aio_max, 300, 0), SET(network_threads, 0, &session_impl::update_network_threads), SET(ssl_listen, 0, 0), SET(tracker_backoff, 250, 0), SET_NOPREV(share_ratio_limit, 200, 0), SET_NOPREV(seed_time_ratio_limit, 700, 0), SET_NOPREV(peer_turnover, 4, 0), SET_NOPREV(peer_turnover_cutoff, 90, 0), SET(peer_turnover_interval, 300, 0), SET_NOPREV(connect_seed_every_n_download, 10, 0), SET(max_http_recv_buffer_size, 4*1024*204, 0), SET_NOPREV(max_retry_port_bind, 10, 0), SET_NOPREV(alert_mask, alert::error_notification, &session_impl::update_alert_mask), SET_NOPREV(out_enc_policy, settings_pack::pe_enabled, 0), SET_NOPREV(in_enc_policy, settings_pack::pe_enabled, 0), SET_NOPREV(allowed_enc_level, settings_pack::pe_both, 0), SET(inactive_down_rate, 2048, 0), SET(inactive_up_rate, 2048, 0), SET_NOPREV(proxy_type, settings_pack::none, &session_impl::update_proxy), SET_NOPREV(proxy_port, 0, &session_impl::update_proxy), SET_NOPREV(i2p_port, 0, &session_impl::update_i2p_bridge), SET_NOPREV(cache_size_volatile, 256, 0), SET_NOPREV(urlseed_max_request_bytes, 16 * 1024 * 1024, 0), SET_NOPREV(web_seed_name_lookup_retry, 1800, 0), SET_NOPREV(close_file_interval, CLOSE_FILE_INTERVAL, &session_impl::update_close_file_interval), SET_NOPREV(utp_cwnd_reduce_timer, 100, 0), }; #undef SET #ifdef __GNUC__ #pragma GCC diagnostic pop #endif } // anonymous namespace int setting_by_name(std::string const& key) { for (int k = 0; k < sizeof(str_settings)/sizeof(str_settings[0]); ++k) { if (key != str_settings[k].name) continue; return settings_pack::string_type_base + k; } for (int k = 0; k < sizeof(int_settings)/sizeof(int_settings[0]); ++k) { if (key != int_settings[k].name) continue; return settings_pack::int_type_base + k; } for (int k = 0; k < sizeof(bool_settings)/sizeof(bool_settings[0]); ++k) { if (key != bool_settings[k].name) continue; return settings_pack::bool_type_base + k; } return -1; } char const* name_for_setting(int s) { switch (s & settings_pack::type_mask) { case settings_pack::string_type_base: return str_settings[s - settings_pack::string_type_base].name; case settings_pack::int_type_base: return int_settings[s - settings_pack::int_type_base].name; case settings_pack::bool_type_base: return bool_settings[s - settings_pack::bool_type_base].name; }; return ""; } boost::shared_ptr load_pack_from_dict(bdecode_node const& settings) { boost::shared_ptr pack = boost::make_shared(); for (int i = 0; i < settings.dict_size(); ++i) { std::string key; bdecode_node val; boost::tie(key, val) = settings.dict_at(i); switch (val.type()) { case bdecode_node::dict_t: case bdecode_node::list_t: continue; case bdecode_node::int_t: { bool found = false; for (int k = 0; k < sizeof(int_settings)/sizeof(int_settings[0]); ++k) { if (key != int_settings[k].name) continue; pack->set_int(settings_pack::int_type_base + k, val.int_value()); found = true; break; } if (found) continue; for (int k = 0; k < sizeof(bool_settings)/sizeof(bool_settings[0]); ++k) { if (key != bool_settings[k].name) continue; pack->set_bool(settings_pack::bool_type_base + k, val.int_value()); break; } } break; case bdecode_node::string_t: for (int k = 0; k < sizeof(str_settings)/sizeof(str_settings[0]); ++k) { if (key != str_settings[k].name) continue; pack->set_str(settings_pack::string_type_base + k, val.string_value()); break; } break; case bdecode_node::none_t: break; } } return pack; } void save_settings_to_dict(aux::session_settings const& s, entry::dictionary_type& sett) { // loop over all settings that differ from default for (int i = 0; i < settings_pack::num_string_settings; ++i) { if (ensure_string(str_settings[i].default_value) == s.m_strings[i]) continue; sett[str_settings[i].name] = s.m_strings[i]; } for (int i = 0; i < settings_pack::num_int_settings; ++i) { if (int_settings[i].default_value == s.m_ints[i]) continue; sett[int_settings[i].name] = s.m_ints[i]; } for (int i = 0; i < settings_pack::num_bool_settings; ++i) { if (bool_settings[i].default_value == s.m_bools[i]) continue; sett[bool_settings[i].name] = s.m_bools[i]; } } void run_all_updates(aux::session_impl& ses) { typedef void (aux::session_impl::*fun_t)(); for (int i = 0; i < settings_pack::num_string_settings; ++i) { fun_t const& f = str_settings[i].fun; if (f) (ses.*f)(); } for (int i = 0; i < settings_pack::num_int_settings; ++i) { fun_t const& f = int_settings[i].fun; if (f) (ses.*f)(); } for (int i = 0; i < settings_pack::num_bool_settings; ++i) { fun_t const& f = bool_settings[i].fun; if (f) (ses.*f)(); } } #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/aux_/disable_warnings_push.hpp" settings_pack load_pack_from_struct( aux::session_settings const& current, session_settings const& s) { settings_pack p; for (int i = 0; i < settings_pack::num_string_settings; ++i) { if (str_settings[i].offset == 0) continue; std::string& val = *(std::string*)(((char*)&s) + str_settings[i].offset); int setting_name = settings_pack::string_type_base + i; if (val == current.get_str(setting_name)) continue; p.set_str(setting_name, val); } for (int i = 0; i < settings_pack::num_int_settings; ++i) { if (int_settings[i].offset == 0) continue; int& val = *(int*)(((char*)&s) + int_settings[i].offset); int setting_name = settings_pack::int_type_base + i; if (val == current.get_int(setting_name)) continue; p.set_int(setting_name, val); } for (int i = 0; i < settings_pack::num_bool_settings; ++i) { if (bool_settings[i].offset == 0) continue; bool& val = *(bool*)(((char*)&s) + bool_settings[i].offset); int setting_name = settings_pack::bool_type_base + i; if (val == current.get_bool(setting_name)) continue; p.set_bool(setting_name, val); } // special case for deprecated float values int val = current.get_int(settings_pack::share_ratio_limit); if (fabs(s.share_ratio_limit - float(val) / 100.f) > 0.001f) p.set_int(settings_pack::share_ratio_limit, s.share_ratio_limit * 100); val = current.get_int(settings_pack::seed_time_ratio_limit); if (fabs(s.seed_time_ratio_limit - float(val) / 100.f) > 0.001f) p.set_int(settings_pack::seed_time_ratio_limit, s.seed_time_ratio_limit * 100); val = current.get_int(settings_pack::peer_turnover); if (fabs(s.peer_turnover - float(val) / 100.f) > 0.001) p.set_int(settings_pack::peer_turnover, s.peer_turnover * 100); val = current.get_int(settings_pack::peer_turnover_cutoff); if (fabs(s.peer_turnover_cutoff - float(val) / 100.f) > 0.001) p.set_int(settings_pack::peer_turnover_cutoff, s.peer_turnover_cutoff * 100); return p; } void load_struct_from_settings(aux::session_settings const& current, session_settings& ret) { for (int i = 0; i < settings_pack::num_string_settings; ++i) { if (str_settings[i].offset == 0) continue; std::string& val = *(std::string*)(((char*)&ret) + str_settings[i].offset); val = current.get_str(settings_pack::string_type_base + i); } for (int i = 0; i < settings_pack::num_int_settings; ++i) { if (int_settings[i].offset == 0) continue; int& val = *(int*)(((char*)&ret) + int_settings[i].offset); val = current.get_int(settings_pack::int_type_base + i); } for (int i = 0; i < settings_pack::num_bool_settings; ++i) { if (bool_settings[i].offset == 0) continue; bool& val = *(bool*)(((char*)&ret) + bool_settings[i].offset); val = current.get_bool(settings_pack::bool_type_base + i); } // special case for deprecated float values ret.share_ratio_limit = float(current.get_int(settings_pack::share_ratio_limit)) / 100.f; ret.seed_time_ratio_limit = float(current.get_int(settings_pack::seed_time_ratio_limit)) / 100.f; ret.peer_turnover = float(current.get_int(settings_pack::peer_turnover)) / 100.f; ret.peer_turnover_cutoff = float(current.get_int(settings_pack::peer_turnover_cutoff)) / 100.f; } #include "libtorrent/aux_/disable_warnings_pop.hpp" #endif void initialize_default_settings(aux::session_settings& s) { for (int i = 0; i < settings_pack::num_string_settings; ++i) { if (str_settings[i].default_value == NULL) continue; s.set_str(settings_pack::string_type_base + i, str_settings[i].default_value); TORRENT_ASSERT(s.get_str(settings_pack::string_type_base + i) == str_settings[i].default_value); } for (int i = 0; i < settings_pack::num_int_settings; ++i) { s.set_int(settings_pack::int_type_base + i, int_settings[i].default_value); TORRENT_ASSERT(s.get_int(settings_pack::int_type_base + i) == int_settings[i].default_value); } for (int i = 0; i < settings_pack::num_bool_settings; ++i) { s.set_bool(settings_pack::bool_type_base + i, bool_settings[i].default_value); TORRENT_ASSERT(s.get_bool(settings_pack::bool_type_base + i) == bool_settings[i].default_value); } } settings_pack default_settings() { settings_pack ret; // TODO: it would be nice to reserve() these vectors up front for (int i = 0; i < settings_pack::num_string_settings; ++i) { if (str_settings[i].default_value == NULL) continue; ret.set_str(settings_pack::string_type_base + i, str_settings[i].default_value); } for (int i = 0; i < settings_pack::num_int_settings; ++i) { ret.set_int(settings_pack::int_type_base + i, int_settings[i].default_value); } for (int i = 0; i < settings_pack::num_bool_settings; ++i) { ret.set_bool(settings_pack::bool_type_base + i, bool_settings[i].default_value); } return ret; } void apply_pack(settings_pack const* pack, aux::session_settings& sett , aux::session_impl* ses) { typedef void (aux::session_impl::*fun_t)(); std::vector callbacks; for (std::vector >::const_iterator i = pack->m_strings.begin() , end(pack->m_strings.end()); i != end; ++i) { // disregard setting indices that are not string types if ((i->first & settings_pack::type_mask) != settings_pack::string_type_base) continue; // ignore settings that are out of bounds int index = i->first & settings_pack::index_mask; if (index < 0 || index >= settings_pack::num_string_settings) continue; // if the vaue did not change, don't call the update callback if (sett.get_str(i->first) == i->second) continue; sett.set_str(i->first, i->second); str_setting_entry_t const& sa = str_settings[i->first & settings_pack::index_mask]; if (sa.fun && ses && std::find(callbacks.begin(), callbacks.end(), sa.fun) == callbacks.end()) callbacks.push_back(sa.fun); } for (std::vector >::const_iterator i = pack->m_ints.begin() , end(pack->m_ints.end()); i != end; ++i) { // disregard setting indices that are not int types if ((i->first & settings_pack::type_mask) != settings_pack::int_type_base) continue; // ignore settings that are out of bounds int index = i->first & settings_pack::index_mask; if (index < 0 || index >= settings_pack::num_int_settings) continue; // if the vaue did not change, don't call the update callback if (sett.get_int(i->first) == i->second) continue; sett.set_int(i->first, i->second); int_setting_entry_t const& sa = int_settings[i->first & settings_pack::index_mask]; if (sa.fun && ses && std::find(callbacks.begin(), callbacks.end(), sa.fun) == callbacks.end()) callbacks.push_back(sa.fun); } for (std::vector >::const_iterator i = pack->m_bools.begin() , end(pack->m_bools.end()); i != end; ++i) { // disregard setting indices that are not bool types if ((i->first & settings_pack::type_mask) != settings_pack::bool_type_base) continue; // ignore settings that are out of bounds int index = i->first & settings_pack::index_mask; if (index < 0 || index >= settings_pack::num_bool_settings) continue; // if the vaue did not change, don't call the update callback if (sett.get_bool(i->first) == i->second) continue; sett.set_bool(i->first, i->second); bool_setting_entry_t const& sa = bool_settings[i->first & settings_pack::index_mask]; if (sa.fun && ses && std::find(callbacks.begin(), callbacks.end(), sa.fun) == callbacks.end()) callbacks.push_back(sa.fun); } // call the callbacks once all the settings have been applied, and // only once per callback for (std::vector::iterator i = callbacks.begin(), end(callbacks.end()); i != end; ++i) { fun_t const& f = *i; (ses->*f)(); } } void settings_pack::set_str(int name, std::string val) { TORRENT_ASSERT((name & type_mask) == string_type_base); if ((name & type_mask) != string_type_base) return; std::pair v(name, val); insort_replace(m_strings, v); } void settings_pack::set_int(int name, int val) { TORRENT_ASSERT((name & type_mask) == int_type_base); if ((name & type_mask) != int_type_base) return; std::pair v(name, val); insort_replace(m_ints, v); } void settings_pack::set_bool(int name, bool val) { TORRENT_ASSERT((name & type_mask) == bool_type_base); if ((name & type_mask) != bool_type_base) return; std::pair v(name, val); insort_replace(m_bools, v); } bool settings_pack::has_val(int name) const { switch (name & type_mask) { case string_type_base: { // this is an optimization. If the settings pack is complete, // i.e. has every key, we don't need to search, it's just a lookup if (m_strings.size() == settings_pack::num_string_settings) return true; std::pair v(name, std::string()); std::vector >::const_iterator i = std::lower_bound(m_strings.begin(), m_strings.end(), v , &compare_first); return i != m_strings.end() && i->first == name; } case int_type_base: { // this is an optimization. If the settings pack is complete, // i.e. has every key, we don't need to search, it's just a lookup if (m_ints.size() == settings_pack::num_int_settings) return true; std::pair v(name, 0); std::vector >::const_iterator i = std::lower_bound(m_ints.begin(), m_ints.end(), v , &compare_first); return i != m_ints.end() && i->first == name; } case bool_type_base: { // this is an optimization. If the settings pack is complete, // i.e. has every key, we don't need to search, it's just a lookup if (m_bools.size() == settings_pack::num_bool_settings) return true; std::pair v(name, false); std::vector >::const_iterator i = std::lower_bound(m_bools.begin(), m_bools.end(), v , &compare_first); return i != m_bools.end() && i->first == name; } } TORRENT_ASSERT(false); return false; } std::string settings_pack::get_str(int name) const { TORRENT_ASSERT((name & type_mask) == string_type_base); if ((name & type_mask) != string_type_base) return std::string(); // this is an optimization. If the settings pack is complete, // i.e. has every key, we don't need to search, it's just a lookup if (m_strings.size() == settings_pack::num_string_settings) { TORRENT_ASSERT(m_strings[name & index_mask].first == name); return m_strings[name & index_mask].second; } std::pair v(name, std::string()); std::vector >::const_iterator i = std::lower_bound(m_strings.begin(), m_strings.end(), v , &compare_first); if (i != m_strings.end() && i->first == name) return i->second; return std::string(); } int settings_pack::get_int(int name) const { TORRENT_ASSERT((name & type_mask) == int_type_base); if ((name & type_mask) != int_type_base) return 0; // this is an optimization. If the settings pack is complete, // i.e. has every key, we don't need to search, it's just a lookup if (m_ints.size() == settings_pack::num_int_settings) { TORRENT_ASSERT(m_ints[name & index_mask].first == name); return m_ints[name & index_mask].second; } std::pair v(name, 0); std::vector >::const_iterator i = std::lower_bound(m_ints.begin(), m_ints.end(), v , &compare_first); if (i != m_ints.end() && i->first == name) return i->second; return 0; } bool settings_pack::get_bool(int name) const { TORRENT_ASSERT((name & type_mask) == bool_type_base); if ((name & type_mask) != bool_type_base) return false; // this is an optimization. If the settings pack is complete, // i.e. has every key, we don't need to search, it's just a lookup if (m_bools.size() == settings_pack::num_bool_settings) { TORRENT_ASSERT(m_bools[name & index_mask].first == name); return m_bools[name & index_mask].second; } std::pair v(name, false); std::vector >::const_iterator i = std::lower_bound(m_bools.begin(), m_bools.end(), v , &compare_first); if (i != m_bools.end() && i->first == name) return i->second; return false; } void settings_pack::clear() { m_strings.clear(); m_ints.clear(); m_bools.clear(); } void settings_pack::clear(int const name) { switch (name & type_mask) { case string_type_base: { std::pair v(name, std::string()); std::vector >::iterator i = std::lower_bound(m_strings.begin(), m_strings.end(), v , &compare_first); if (i != m_strings.end() && i->first == name) m_strings.erase(i); break; } case int_type_base: { std::pair v(name, 0); std::vector >::iterator i = std::lower_bound(m_ints.begin(), m_ints.end(), v , &compare_first); if (i != m_ints.end() && i->first == name) m_ints.erase(i); break; } case bool_type_base: { std::pair v(name, false); std::vector >::iterator i = std::lower_bound(m_bools.begin(), m_bools.end(), v , &compare_first); if (i != m_bools.end() && i->first == name) m_bools.erase(i); break; } } } } libtorrent-rasterbar-1.1.13/src/sha1.cpp000066400000000000000000000222521351156116000200750ustar00rootroot00000000000000/* SHA-1 C++ conversion original version: SHA-1 in C By Steve Reid 100% Public Domain changelog at the end of the file. */ #include #include #include "libtorrent/sha1.hpp" #include typedef boost::uint32_t u32; typedef boost::uint8_t u8; namespace libtorrent { namespace { union CHAR64LONG16 { u8 c[64]; u32 l[16]; }; #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-member-function" #endif // blk0() and blk() perform the initial expand. // I got the idea of expanding during the round function from SSLeay struct little_endian_blk0 { static u32 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 u32 apply(CHAR64LONG16* block, int i) { return block->l[i]; } }; #ifdef __clang__ #pragma clang diagnostic pop #endif #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 void SHA1transform(u32 state[5], u8 const buffer[64]) { using namespace std; u32 a, b, c, d, e; CHAR64LONG16 workspace; CHAR64LONG16* block = &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; } #ifdef VERBOSE void SHAPrintContext(sha_ctx *context, char *msg) { using namespace std; printf("%s (%d,%d) %x %x %x %x %x\n" , msg, (unsigned int)context->count[0] , (unsigned int)context->count[1] , (unsigned int)context->state[0] , (unsigned int)context->state[1] , (unsigned int)context->state[2] , (unsigned int)context->state[3] , (unsigned int)context->state[4]); } #endif template void internal_update(sha_ctx* context, u8 const* data, u32 len) { using namespace std; u32 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(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { SHA1transform(context->state, &data[i]); } j = 0; } else { i = 0; } memcpy(&context->buffer[j], &data[i], len - i); #ifdef VERBOSE SHAPrintContext(context, "after "); #endif } #if !BOOST_ENDIAN_BIG_BYTE && !BOOST_ENDIAN_LITTLE_BYTE bool is_big_endian() { u32 test = 1; return *reinterpret_cast(&test) == 0; } #endif } // SHA1Init - Initialize new context void SHA1_init(sha_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 SHA1_update(sha_ctx* context, u8 const* data, u32 len) { // GCC standard defines for endianness // test with: cpp -dM /dev/null #if BOOST_ENDIAN_BIG_BYTE internal_update(context, data, len); #elif BOOST_ENDIAN_LITTLE_BYTE internal_update(context, data, len); #else // select different functions depending on endianess // and figure out the endianess runtime if (is_big_endian()) internal_update(context, data, len); else internal_update(context, data, len); #endif } // Add padding and return the message digest. void SHA1_final(u8* digest, sha_ctx* context) { u8 finalcount[8]; for (u32 i = 0; i < 8; ++i) { // Endian independent finalcount[i] = static_cast( (context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); } SHA1_update(context, reinterpret_cast("\200"), 1); while ((context->count[0] & 504) != 448) SHA1_update(context, reinterpret_cast("\0"), 1); SHA1_update(context, finalcount, 8); // Should cause a SHA1transform() for (u32 i = 0; i < 20; ++i) { digest[i] = static_cast( (context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } } } // libtorrent namespace /************************************************************ ----------------- Modified 7/98 By James H. Brown 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 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 Still 100% public domain 1- Removed #include 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 Still 100% PD Modified to run on Compaq Alpha hardware. ----------------- Converted to C++ 6/04 By Arvid Norberg 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 BOOST_BIG_ENDIAN or BOOST_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 6- made API compatible with openssl 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 */ libtorrent-rasterbar-1.1.13/src/smart_ban.cpp000066400000000000000000000306131351156116000212070ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_EXTENSIONS #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/extensions/smart_ban.hpp" #include "libtorrent/disk_io_thread.hpp" #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/random.hpp" #include "libtorrent/operations.hpp" // for operation_t enum //#define TORRENT_LOG_HASH_FAILURES #ifndef TORRENT_DISABLE_LOGGING #include "libtorrent/socket_io.hpp" #endif #ifdef TORRENT_LOG_HASH_FAILURES #include "libtorrent/peer_id.hpp" // sha1_hash #include "libtorrent/hex.hpp" // to_hex #include "libtorrent/socket_io.hpp" void log_hash_block(FILE** f, libtorrent::torrent const& t, int piece, int block , libtorrent::address a, char const* bytes, int len, bool corrupt) { using namespace libtorrent; mkdir("hash_failures", 0755); if (*f == NULL) { char filename[1024]; snprintf(filename, sizeof(filename), "hash_failures/%s.log" , to_hex(t.info_hash().to_string()).c_str()); *f = fopen(filename, "w"); } file_storage const& fs = t.torrent_file().files(); std::vector files = fs.map_block(piece, block * 0x4000, len); std::string fn = fs.file_path(fs.internal_at(files[0].file_index)); char filename[4094]; int offset = 0; for (int i = 0; i < files.size(); ++i) { offset += snprintf(filename+offset, sizeof(filename)-offset , "%s[%" PRId64 ",%d]", libtorrent::filename(fn).c_str(), files[i].offset, int(files[i].size)); if (offset >= sizeof(filename)) break; } fprintf(*f, "%s\t%04d\t%04d\t%s\t%s\t%s\n", to_hex(t.info_hash().to_string()).c_str(), piece , block, corrupt ? " bad" : "good", print_address(a).c_str(), filename); snprintf(filename, sizeof(filename), "hash_failures/%s_%d_%d_%s.block" , to_hex(t.info_hash().to_string()).c_str(), piece, block, corrupt ? "bad" : "good"); FILE* data = fopen(filename, "w+"); fwrite(bytes, 1, len, data); fclose(data); } #endif namespace libtorrent { class torrent; namespace { struct smart_ban_plugin TORRENT_FINAL : torrent_plugin , boost::enable_shared_from_this { smart_ban_plugin(torrent& t) : m_torrent(t) , m_salt(random()) { #ifdef TORRENT_LOG_HASH_FAILURES m_log_file = NULL; #endif } #ifdef TORRENT_LOG_HASH_FAILURES ~smart_ban_plugin() { fclose(m_log_file); } #endif virtual void on_piece_pass(int p) TORRENT_OVERRIDE { #ifndef TORRENT_DISABLE_LOGGING m_torrent.debug_log(" PIECE PASS [ p: %d | block_hash_size: %d ]" , p, int(m_block_hashes.size())); #endif // has this piece failed earlier? If it has, go through the // CRCs from the time it failed and ban the peers that // sent bad blocks std::map::iterator i = m_block_hashes.lower_bound(piece_block(p, 0)); if (i == m_block_hashes.end() || int(i->first.piece_index) != p) return; int size = m_torrent.torrent_file().piece_size(p); peer_request r = {p, 0, (std::min)(16*1024, size)}; piece_block pb(p, 0); while (size > 0) { if (i->first.block_index == pb.block_index) { m_torrent.session().disk_thread().async_read(&m_torrent.storage() , r, boost::bind(&smart_ban_plugin::on_read_ok_block , shared_from_this(), *i, i->second.peer->address(), _1) , reinterpret_cast(1)); m_block_hashes.erase(i++); } else { TORRENT_ASSERT(i->first.block_index > pb.block_index); } if (i == m_block_hashes.end() || int(i->first.piece_index) != p) break; r.start += 16*1024; size -= 16*1024; r.length = (std::min)(16*1024, size); ++pb.block_index; } #ifndef NDEBUG // make sure we actually removed all the entries for piece 'p' i = m_block_hashes.lower_bound(piece_block(p, 0)); TORRENT_ASSERT(i == m_block_hashes.end() || int(i->first.piece_index) != p); #endif if (m_torrent.is_seed()) { std::map().swap(m_block_hashes); return; } } virtual void on_piece_failed(int p) TORRENT_OVERRIDE { // The piece failed the hash check. Record // the CRC and origin peer of every block // if the torrent is aborted, no point in starting // a bunch of read operations on it if (m_torrent.is_aborted()) return; std::vector downloaders; m_torrent.picker().get_downloaders(downloaders, p); int size = m_torrent.torrent_file().piece_size(p); peer_request r = {p, 0, (std::min)(16*1024, size)}; piece_block pb(p, 0); for (std::vector::iterator i = downloaders.begin() , end(downloaders.end()); i != end; ++i) { if (*i != NULL) { // for very sad and involved reasons, this read need to force a copy out of the cache // since the piece has failed, this block is very likely to be replaced with a newly // downloaded one very soon, and to get a block by reference would fail, since the // block read will have been deleted by the time it gets back to the network thread m_torrent.session().disk_thread().async_read(&m_torrent.storage(), r , boost::bind(&smart_ban_plugin::on_read_failed_block , shared_from_this(), pb, (*i)->address(), _1) , reinterpret_cast(1) , disk_io_job::force_copy); } r.start += 16*1024; size -= 16*1024; r.length = (std::min)(16*1024, size); ++pb.block_index; } TORRENT_ASSERT(size <= 0); } private: // this entry ties a specific block CRC to // a peer. struct block_entry { torrent_peer* peer; sha1_hash digest; }; void on_read_failed_block(piece_block b, address a, disk_io_job const* j) { TORRENT_ASSERT(m_torrent.session().is_single_thread()); disk_buffer_holder buffer(m_torrent.session(), *j); // ignore read errors if (j->ret != j->d.io.buffer_size) return; hasher h; h.update(j->buffer.disk_block, j->d.io.buffer_size); h.update(reinterpret_cast(&m_salt), sizeof(m_salt)); std::pair range = m_torrent.find_peers(a); // there is no peer with this address anymore if (range.first == range.second) return; torrent_peer* p = (*range.first); block_entry e = {p, h.final()}; #ifdef TORRENT_LOG_HASH_FAILURES log_hash_block(&m_log_file, m_torrent, b.piece_index , b.block_index, p->address(), j->buffer.disk_block, j->buffer_size, true); #endif std::map::iterator i = m_block_hashes.lower_bound(b); if (i != m_block_hashes.end() && i->first == b && i->second.peer == p) { // this peer has sent us this block before // if the peer is already banned, it doesn't matter if it sent // good or bad data. Nothings going to change it if (!p->banned && i->second.digest != e.digest) { // this time the digest of the block is different // from the first time it sent it // at least one of them must be bad #ifndef TORRENT_DISABLE_LOGGING char const* client = "-"; peer_info info; if (p->connection) { p->connection->get_peer_info(info); client = info.client.c_str(); } m_torrent.debug_log(" BANNING PEER [ p: %d | b: %d | c: %s" " | hash1: %s | hash2: %s | ip: %s ]" , b.piece_index, b.block_index, client , to_hex(i->second.digest.to_string()).c_str() , to_hex(e.digest.to_string()).c_str() , print_endpoint(p->ip()).c_str()); #endif m_torrent.ban_peer(p); if (p->connection) p->connection->disconnect( errors::peer_banned, op_bittorrent); } // we already have this exact entry in the map // we don't have to insert it return; } m_block_hashes.insert(i, std::pair(b, e)); #ifndef TORRENT_DISABLE_LOGGING char const* client = "-"; peer_info info; if (p->connection) { p->connection->get_peer_info(info); client = info.client.c_str(); } m_torrent.debug_log(" STORE BLOCK CRC [ p: %d | b: %d | c: %s" " | digest: %s | ip: %s ]" , b.piece_index, b.block_index, client , to_hex(e.digest.to_string()).c_str() , print_address(p->ip().address()).c_str()); #endif } void on_read_ok_block(std::pair b, address a, disk_io_job const* j) { TORRENT_ASSERT(m_torrent.session().is_single_thread()); disk_buffer_holder buffer(m_torrent.session(), *j); // ignore read errors if (j->ret != j->d.io.buffer_size) return; hasher h; h.update(j->buffer.disk_block, j->d.io.buffer_size); h.update(reinterpret_cast(&m_salt), sizeof(m_salt)); sha1_hash ok_digest = h.final(); if (b.second.digest == ok_digest) return; #ifdef TORRENT_LOG_HASH_FAILURES log_hash_block(&m_log_file, m_torrent, b.first.piece_index , b.first.block_index, a, j->buffer.disk_block, j->buffer_size, false); #endif // find the peer std::pair range = m_torrent.find_peers(a); if (range.first == range.second) return; torrent_peer* p = NULL; for (; range.first != range.second; ++range.first) { if (b.second.peer != *range.first) continue; p = *range.first; } if (p == NULL) return; #ifndef TORRENT_DISABLE_LOGGING char const* client = "-"; peer_info info; if (p->connection) { p->connection->get_peer_info(info); client = info.client.c_str(); } m_torrent.debug_log(" BANNING PEER [ p: %d | b: %d | c: %s" " | ok_digest: %s | bad_digest: %s | ip: %s ]" , b.first.piece_index, b.first.block_index, client , to_hex(ok_digest.to_string()).c_str() , to_hex(b.second.digest.to_string()).c_str() , print_address(p->ip().address()).c_str()); #endif m_torrent.ban_peer(p); if (p->connection) p->connection->disconnect( errors::peer_banned, op_bittorrent); } torrent& m_torrent; // This table maps a piece_block (piece and block index // pair) to a peer and the block CRC. The CRC is calculated // from the data in the block + the salt std::map m_block_hashes; // This salt is a random value used to calculate the block CRCs // Since the CRC function that is used is not a one way function // the salt is required to avoid attacks where bad data is sent // that is forged to match the CRC of the good data. int m_salt; #ifdef TORRENT_LOG_HASH_FAILURES FILE* m_log_file; #endif // explicitly disallow assignment, to silence msvc warning smart_ban_plugin& operator=(smart_ban_plugin const&); }; } } namespace libtorrent { boost::shared_ptr create_smart_ban_plugin(torrent_handle const& th, void*) { torrent* t = th.native_handle().get(); return boost::shared_ptr(new smart_ban_plugin(*t)); } } #endif libtorrent-rasterbar-1.1.13/src/socket_io.cpp000066400000000000000000000103771351156116000212250ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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 #include "libtorrent/error_code.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/address.hpp" #include "libtorrent/io.hpp" // for write_uint16 #include "libtorrent/hasher.hpp" // for hasher namespace libtorrent { std::string print_address(address const& addr) { error_code ec; return addr.to_string(ec); } std::string address_to_bytes(address const& a) { std::string ret; std::back_insert_iterator out(ret); detail::write_address(a, out); return ret; } std::string endpoint_to_bytes(udp::endpoint const& ep) { std::string ret; std::back_insert_iterator out(ret); detail::write_endpoint(ep, out); return ret; } std::string print_endpoint(tcp::endpoint const& ep) { error_code ec; char buf[200]; address const& addr = ep.address(); #if TORRENT_USE_IPV6 if (addr.is_v6()) snprintf(buf, sizeof(buf), "[%s]:%d", addr.to_string(ec).c_str(), ep.port()); else #endif snprintf(buf, sizeof(buf), "%s:%d", addr.to_string(ec).c_str(), ep.port()); return buf; } std::string print_endpoint(udp::endpoint const& ep) { return print_endpoint(tcp::endpoint(ep.address(), ep.port())); } tcp::endpoint parse_endpoint(std::string str, error_code& ec) { tcp::endpoint ret; std::string::iterator start = str.begin(); std::string::iterator port_pos; // remove white spaces in front of the string while (start != str.end() && is_space(*start)) ++start; // this is for IPv6 addresses if (start != str.end() && *start == '[') { ++start; port_pos = std::find(start, str.end(), ']'); if (port_pos == str.end()) { ec = errors::expected_close_bracket_in_address; return ret; } *port_pos = '\0'; ++port_pos; if (port_pos == str.end() || *port_pos != ':') { ec = errors::invalid_port; return ret; } #if TORRENT_USE_IPV6 ret.address(address_v6::from_string(&*start, ec)); #else ec = boost::asio::error::address_family_not_supported; #endif if (ec) return ret; } else { port_pos = std::find(start, str.end(), ':'); if (port_pos == str.end()) { ec = errors::invalid_port; return ret; } *port_pos = '\0'; ret.address(address_v4::from_string(&*start, ec)); if (ec) return ret; } ++port_pos; if (port_pos == str.end()) { ec = errors::invalid_port; return ret; } ret.port(std::atoi(&*port_pos)); return ret; } void hash_address(address const& ip, sha1_hash& h) { #if TORRENT_USE_IPV6 if (ip.is_v6()) { address_v6::bytes_type b = ip.to_v6().to_bytes(); h = hasher(reinterpret_cast(&b[0]), b.size()).final(); } else #endif { address_v4::bytes_type b = ip.to_v4().to_bytes(); h = hasher(reinterpret_cast(&b[0]), b.size()).final(); } } } libtorrent-rasterbar-1.1.13/src/socket_type.cpp000066400000000000000000000262471351156116000216020ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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/config.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/aux_/openssl.hpp" #ifdef TORRENT_USE_OPENSSL #include #if BOOST_VERSION >= 104700 #include #endif #endif #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/debug.hpp" #endif namespace libtorrent { bool is_ssl(socket_type const& s) { #ifdef TORRENT_USE_OPENSSL #define CASE(t) case socket_type_int_impl >::value: switch (s.type()) { CASE(tcp::socket) CASE(socks5_stream) CASE(http_stream) CASE(utp_stream) return true; default: return false; }; #undef CASE #else TORRENT_UNUSED(s); return false; #endif } bool is_utp(socket_type const& s) { return s.get() #ifdef TORRENT_USE_OPENSSL || s.get >() #endif ; } #if TORRENT_USE_I2P bool is_i2p(socket_type const& s) { return s.get() #ifdef TORRENT_USE_OPENSSL || s.get >() #endif ; } #endif void setup_ssl_hostname(socket_type& s, std::string const& hostname, error_code& ec) { #if defined TORRENT_USE_OPENSSL && BOOST_VERSION >= 104700 // for SSL connections, make sure to authenticate the hostname // of the certificate #define CASE(t) case socket_type_int_impl >::value: \ s.get >()->set_verify_callback( \ boost::asio::ssl::rfc2818_verification(hostname), ec); \ ssl = s.get >()->native_handle(); \ ctx = SSL_get_SSL_CTX(ssl); \ break; SSL* ssl = 0; SSL_CTX* ctx = 0; switch(s.type()) { CASE(tcp::socket) CASE(socks5_stream) CASE(http_stream) CASE(utp_stream) } #undef CASE #if OPENSSL_VERSION_NUMBER >= 0x90812f if (ctx) { aux::openssl_set_tlsext_servername_callback(ctx, 0); aux::openssl_set_tlsext_servername_arg(ctx, 0); } #endif // OPENSSL_VERSION_NUMBER #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME if (ssl) { aux::openssl_set_tlsext_hostname(ssl, hostname.c_str()); } #endif #else TORRENT_UNUSED(ec); TORRENT_UNUSED(hostname); TORRENT_UNUSED(s); #endif } #ifdef TORRENT_USE_OPENSSL namespace { void nop(boost::shared_ptr) {} void on_close_socket(socket_type* s, boost::shared_ptr) { #if defined TORRENT_ASIO_DEBUGGING complete_async("on_close_socket"); #endif error_code ec; s->close(ec); } } // anonymous namespace #endif // the second argument is a shared pointer to an object that // will keep the socket (s) alive for the duration of the async operation void async_shutdown(socket_type& s, boost::shared_ptr holder) { error_code e; #ifdef TORRENT_USE_OPENSSL // for SSL connections, first do an async_shutdown, before closing the socket #if defined TORRENT_ASIO_DEBUGGING #define MAYBE_ASIO_DEBUGGING add_outstanding_async("on_close_socket"); #else #define MAYBE_ASIO_DEBUGGING #endif char const buffer[] = ""; // chasing the async_shutdown by a write is a trick to close the socket as // soon as we've sent the close_notify, without having to wait to receive a // response from the other end // https://stackoverflow.com/questions/32046034/what-is-the-proper-way-to-securely-disconnect-an-asio-ssl-socket #define CASE(t) case socket_type_int_impl >::value: \ MAYBE_ASIO_DEBUGGING \ s.get >()->async_shutdown(boost::bind(&nop, holder)); \ s.get >()->async_write_some(boost::asio::buffer(buffer), boost::bind(&on_close_socket, &s, holder)); \ break; switch (s.type()) { CASE(tcp::socket) CASE(socks5_stream) CASE(http_stream) CASE(utp_stream) default: s.close(e); break; } #undef CASE #else TORRENT_UNUSED(holder); s.close(e); #endif // TORRENT_USE_OPENSSL } void socket_type::destruct() { typedef tcp::socket tcp_socket; switch (m_type) { case 0: break; case socket_type_int_impl::value: get()->~tcp_socket(); break; case socket_type_int_impl::value: get()->~socks5_stream(); break; case socket_type_int_impl::value: get()->~http_stream(); break; case socket_type_int_impl::value: get()->~utp_stream(); break; #if TORRENT_USE_I2P case socket_type_int_impl::value: get()->~i2p_stream(); break; #endif #ifdef TORRENT_USE_OPENSSL case socket_type_int_impl >::value: get >()->~ssl_stream(); break; case socket_type_int_impl >::value: get >()->~ssl_stream(); break; case socket_type_int_impl >::value: get >()->~ssl_stream(); break; case socket_type_int_impl >::value: get >()->~ssl_stream(); break; #endif default: TORRENT_ASSERT(false); } m_type = 0; } void socket_type::construct(int type, void* userdata) { #ifndef TORRENT_USE_OPENSSL TORRENT_UNUSED(userdata); #endif destruct(); switch (type) { case 0: break; case socket_type_int_impl::value: new (reinterpret_cast(&m_data)) tcp::socket(m_io_service); break; case socket_type_int_impl::value: new (reinterpret_cast(&m_data)) socks5_stream(m_io_service); break; case socket_type_int_impl::value: new (reinterpret_cast(&m_data)) http_stream(m_io_service); break; case socket_type_int_impl::value: new (reinterpret_cast(&m_data)) utp_stream(m_io_service); break; #if TORRENT_USE_I2P case socket_type_int_impl::value: new (reinterpret_cast(&m_data)) i2p_stream(m_io_service); break; #endif #ifdef TORRENT_USE_OPENSSL case socket_type_int_impl >::value: TORRENT_ASSERT(userdata); new (reinterpret_cast*>(&m_data)) ssl_stream(m_io_service , *static_cast(userdata)); break; case socket_type_int_impl >::value: TORRENT_ASSERT(userdata); new (reinterpret_cast*>(&m_data)) ssl_stream(m_io_service , *static_cast(userdata)); break; case socket_type_int_impl >::value: TORRENT_ASSERT(userdata); new (reinterpret_cast*>(&m_data)) ssl_stream(m_io_service , *static_cast(userdata)); break; case socket_type_int_impl >::value: TORRENT_ASSERT(userdata); new (reinterpret_cast*>(&m_data)) ssl_stream(m_io_service , *static_cast(userdata)); break; #endif default: TORRENT_ASSERT(false); } m_type = type; } char const* socket_type::type_name() const { static char const* const names[] = { "uninitialized", "TCP", "Socks5", "HTTP", "uTP", #if TORRENT_USE_I2P "I2P", #else "", #endif #ifdef TORRENT_USE_OPENSSL "SSL/TCP", "SSL/Socks5", "SSL/HTTP", "SSL/uTP" #else "","","","" #endif }; return names[m_type]; } io_service& socket_type::get_io_service() const { return m_io_service; } socket_type::~socket_type() { destruct(); } bool socket_type::is_open() const { if (m_type == 0) return false; TORRENT_SOCKTYPE_FORWARD_RET(is_open(), false) } void socket_type::open(protocol_type const& p, error_code& ec) { TORRENT_SOCKTYPE_FORWARD(open(p, ec)) } void socket_type::close(error_code& ec) { if (m_type == 0) return; TORRENT_SOCKTYPE_FORWARD(close(ec)) } void socket_type::set_close_reason(boost::uint16_t code) { switch (m_type) { case socket_type_int_impl::value: get()->set_close_reason(code); break; #ifdef TORRENT_USE_OPENSSL case socket_type_int_impl >::value: get >()->lowest_layer().set_close_reason(code); break; #endif default: break; } } boost::uint16_t socket_type::get_close_reason() { switch (m_type) { case socket_type_int_impl::value: return get()->get_close_reason(); #ifdef TORRENT_USE_OPENSSL case socket_type_int_impl >::value: return get >()->lowest_layer().get_close_reason(); #endif default: return 0; } } socket_type::endpoint_type socket_type::local_endpoint(error_code& ec) const { TORRENT_SOCKTYPE_FORWARD_RET(local_endpoint(ec), socket_type::endpoint_type()) } socket_type::endpoint_type socket_type::remote_endpoint(error_code& ec) const { TORRENT_SOCKTYPE_FORWARD_RET(remote_endpoint(ec), socket_type::endpoint_type()) } void socket_type::bind(endpoint_type const& endpoint, error_code& ec) { TORRENT_SOCKTYPE_FORWARD(bind(endpoint, ec)) } std::size_t socket_type::available(error_code& ec) const { TORRENT_SOCKTYPE_FORWARD_RET(available(ec), 0) } int socket_type::type() const { return m_type; } #ifndef BOOST_NO_EXCEPTIONS void socket_type::open(protocol_type const& p) { TORRENT_SOCKTYPE_FORWARD(open(p)) } void socket_type::close() { if (m_type == 0) return; TORRENT_SOCKTYPE_FORWARD(close()) } socket_type::endpoint_type socket_type::local_endpoint() const { TORRENT_SOCKTYPE_FORWARD_RET(local_endpoint(), socket_type::endpoint_type()) } socket_type::endpoint_type socket_type::remote_endpoint() const { TORRENT_SOCKTYPE_FORWARD_RET(remote_endpoint(), socket_type::endpoint_type()) } void socket_type::bind(endpoint_type const& endpoint) { TORRENT_SOCKTYPE_FORWARD(bind(endpoint)) } std::size_t socket_type::available() const { TORRENT_SOCKTYPE_FORWARD_RET(available(), 0) } #endif } libtorrent-rasterbar-1.1.13/src/socks5_stream.cpp000066400000000000000000000302211351156116000220160ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/socks5_stream.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/socket_io.hpp" namespace libtorrent { namespace socks_error { boost::system::error_code make_error_code(socks_error_code e) { return error_code(e, socks_category()); } } struct socks_error_category : boost::system::error_category { virtual const char* name() const BOOST_SYSTEM_NOEXCEPT { return "socks"; } virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT { static char const* messages[] = { "SOCKS no error", "SOCKS unsupported version", "SOCKS unsupported authentication method", "SOCKS unsupported authentication version", "SOCKS authentication error", "SOCKS username required", "SOCKS general failure", "SOCKS command not supported", "SOCKS no identd running", "SOCKS identd could not identify username" }; if (ev < 0 || ev >= socks_error::num_errors) return "unknown error"; return messages[ev]; } virtual boost::system::error_condition default_error_condition( int ev) const BOOST_SYSTEM_NOEXCEPT { return boost::system::error_condition(ev, *this); } }; boost::system::error_category& socks_category() { static socks_error_category cat; return cat; } #ifndef TORRENT_NO_DEPRECATE boost::system::error_category& get_socks_category() { return socks_category(); } #endif void socks5_stream::name_lookup(error_code const& e, tcp::resolver::iterator i , boost::shared_ptr h) { #if defined TORRENT_ASIO_DEBUGGING complete_async("socks5_stream::name_lookup"); #endif if (handle_error(e, h)) return; error_code ec; if (!m_sock.is_open()) { m_sock.open(i->endpoint().protocol(), ec); if (handle_error(ec, h)) return; } // TOOD: we could bind the socket here, since we know what the // target endpoint is of the proxy #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("socks5_stream::connected"); #endif m_sock.async_connect(i->endpoint(), boost::bind( &socks5_stream::connected, this, _1, h)); } void socks5_stream::connected(error_code const& e, boost::shared_ptr h) { #if defined TORRENT_ASIO_DEBUGGING complete_async("socks5_stream::connected"); #endif if (handle_error(e, h)) return; using namespace libtorrent::detail; if (m_version == 5) { // send SOCKS5 authentication methods m_buffer.resize(m_user.empty()?3:4); char* p = &m_buffer[0]; write_uint8(5, p); // SOCKS VERSION 5 if (m_user.empty()) { write_uint8(1, p); // 1 authentication method (no auth) write_uint8(0, p); // no authentication } else { write_uint8(2, p); // 2 authentication methods write_uint8(0, p); // no authentication write_uint8(2, p); // username/password } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("socks5_stream::handshake1"); #endif async_write(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&socks5_stream::handshake1, this, _1, h)); } else if (m_version == 4) { socks_connect(h); } else { (*h)(socks_error::unsupported_version); } } void socks5_stream::handshake1(error_code const& e, boost::shared_ptr h) { #if defined TORRENT_ASIO_DEBUGGING complete_async("socks5_stream::handshake1"); #endif if (handle_error(e, h)) return; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("socks5_stream::handshake2"); #endif m_buffer.resize(2); async_read(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&socks5_stream::handshake2, this, _1, h)); } void socks5_stream::handshake2(error_code const& e, boost::shared_ptr h) { #if defined TORRENT_ASIO_DEBUGGING complete_async("socks5_stream::handshake2"); #endif if (handle_error(e, h)) return; using namespace libtorrent::detail; char* p = &m_buffer[0]; int version = read_uint8(p); int method = read_uint8(p); if (version < m_version) { (*h)(socks_error::unsupported_version); return; } if (method == 0) { socks_connect(h); } else if (method == 2) { if (m_user.empty()) { (*h)(socks_error::username_required); return; } // start sub-negotiation m_buffer.resize(m_user.size() + m_password.size() + 3); p = &m_buffer[0]; write_uint8(1, p); write_uint8(m_user.size(), p); write_string(m_user, p); write_uint8(m_password.size(), p); write_string(m_password, p); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("socks5_stream::handshake3"); #endif async_write(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&socks5_stream::handshake3, this, _1, h)); } else { (*h)(socks_error::unsupported_authentication_method); return; } } void socks5_stream::handshake3(error_code const& e , boost::shared_ptr h) { #if defined TORRENT_ASIO_DEBUGGING complete_async("socks5_stream::handshake3"); #endif if (handle_error(e, h)) return; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("socks5_stream::handshake4"); #endif m_buffer.resize(2); async_read(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&socks5_stream::handshake4, this, _1, h)); } void socks5_stream::handshake4(error_code const& e , boost::shared_ptr h) { #if defined TORRENT_ASIO_DEBUGGING complete_async("socks5_stream::handshake4"); #endif if (handle_error(e, h)) return; using namespace libtorrent::detail; char* p = &m_buffer[0]; int version = read_uint8(p); int status = read_uint8(p); if (version != 1) { (*h)(socks_error::unsupported_authentication_version); return; } if (status != 0) { (*h)(socks_error::authentication_error); return; } std::vector().swap(m_buffer); socks_connect(h); } void socks5_stream::socks_connect(boost::shared_ptr h) { using namespace libtorrent::detail; if (m_version == 5) { // send SOCKS5 connect command m_buffer.resize(6 + (!m_dst_name.empty() ?m_dst_name.size() + 1 :(m_remote_endpoint.address().is_v4()?4:16))); char* p = &m_buffer[0]; write_uint8(5, p); // SOCKS VERSION 5 write_uint8(m_command, p); // CONNECT command write_uint8(0, p); // reserved if (!m_dst_name.empty()) { write_uint8(3, p); // address type TORRENT_ASSERT(m_dst_name.size() <= 255); write_uint8(m_dst_name.size(), p); std::copy(m_dst_name.begin(), m_dst_name.end(), p); p += m_dst_name.size(); } else { // we either need a hostname or a valid endpoint TORRENT_ASSERT(m_remote_endpoint.address() != address()); write_uint8(m_remote_endpoint.address().is_v4()?1:4, p); // address type write_address(m_remote_endpoint.address(), p); } write_uint16(m_remote_endpoint.port(), p); } else if (m_version == 4) { // SOCKS4 only supports IPv4 if (!m_remote_endpoint.address().is_v4()) { (*h)(boost::asio::error::address_family_not_supported); return; } m_buffer.resize(m_user.size() + 9); char* p = &m_buffer[0]; write_uint8(4, p); // SOCKS VERSION 4 write_uint8(m_command, p); // CONNECT command write_uint16(m_remote_endpoint.port(), p); write_uint32(m_remote_endpoint.address().to_v4().to_ulong(), p); std::copy(m_user.begin(), m_user.end(), p); p += m_user.size(); write_uint8(0, p); // NULL terminator } else { (*h)(socks_error::unsupported_version); return; } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("socks5_stream::connect1"); #endif async_write(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&socks5_stream::connect1, this, _1, h)); } void socks5_stream::connect1(error_code const& e, boost::shared_ptr h) { #if defined TORRENT_ASIO_DEBUGGING complete_async("socks5_stream::connect1"); #endif if (handle_error(e, h)) return; if (m_version == 5) m_buffer.resize(6 + 4); // assume an IPv4 address else if (m_version == 4) m_buffer.resize(8); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("socks5_stream::connect2"); #endif async_read(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&socks5_stream::connect2, this, _1, h)); } void socks5_stream::connect2(error_code const& e, boost::shared_ptr h) { #if defined TORRENT_ASIO_DEBUGGING complete_async("socks5_stream::connect2"); #endif if (handle_error(e, h)) return; using namespace libtorrent::detail; char const* p = &m_buffer[0]; int const version = read_uint8(p); int const response = read_uint8(p); if (m_version == 5) { if (version < m_version) { (*h)(socks_error::unsupported_version); return; } if (response != 0) { error_code ec(socks_error::general_failure); switch (response) { case 2: ec = boost::asio::error::no_permission; break; case 3: ec = boost::asio::error::network_unreachable; break; case 4: ec = boost::asio::error::host_unreachable; break; case 5: ec = boost::asio::error::connection_refused; break; case 6: ec = boost::asio::error::timed_out; break; case 7: ec = socks_error::command_not_supported; break; case 8: ec = boost::asio::error::address_family_not_supported; break; } (*h)(ec); return; } p += 1; // reserved int const atyp = read_uint8(p); // read the proxy IP it was bound to (this is variable length depending // on address type) if (atyp == 1) { std::vector().swap(m_buffer); (*h)(e); return; } int extra_bytes = 0; if (atyp == 4) { // IPv6 extra_bytes = 12; } else if (atyp == 3) { // hostname with length prefix extra_bytes = read_uint8(p) - 3; } else { (*h)(boost::asio::error::address_family_not_supported); return; } m_buffer.resize(m_buffer.size() + extra_bytes); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("socks5_stream::connect3"); #endif TORRENT_ASSERT(extra_bytes > 0); async_read(m_sock, boost::asio::buffer(&m_buffer[m_buffer.size() - extra_bytes], extra_bytes) , boost::bind(&socks5_stream::connect3, this, _1, h)); } else if (m_version == 4) { if (version != 0) { (*h)(socks_error::general_failure); return; } // access granted if (response == 90) { std::vector().swap(m_buffer); (*h)(e); return; } error_code ec(socks_error::general_failure); switch (response) { case 91: ec = boost::asio::error::connection_refused; break; case 92: ec = socks_error::no_identd; break; case 93: ec = socks_error::identd_error; break; } (*h)(ec); } } void socks5_stream::connect3(error_code const& e, boost::shared_ptr h) { #if defined TORRENT_ASIO_DEBUGGING complete_async("socks5_stream::connect3"); #endif using namespace libtorrent::detail; if (handle_error(e, h)) return; std::vector().swap(m_buffer); (*h)(e); } } libtorrent-rasterbar-1.1.13/src/stat.cpp000066400000000000000000000035211351156116000202120ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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 #include #include "libtorrent/stat.hpp" namespace libtorrent { void stat_channel::second_tick(int tick_interval_ms) { int sample = int(boost::int64_t(m_counter) * 1000 / tick_interval_ms); TORRENT_ASSERT(sample >= 0); m_5_sec_average = boost::int64_t(m_5_sec_average) * 4 / 5 + sample / 5; m_counter = 0; } } libtorrent-rasterbar-1.1.13/src/stat_cache.cpp000066400000000000000000000063531351156116000213430ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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/stat_cache.hpp" #include "libtorrent/assert.hpp" namespace libtorrent { stat_cache::stat_cache() {} stat_cache::~stat_cache() {} void stat_cache::set_cache(int i, boost::int64_t size, time_t time) { TORRENT_ASSERT(i >= 0); mutex::scoped_lock l(m_mutex); if (i >= int(m_stat_cache.size())) m_stat_cache.resize(i + 1, not_in_cache); m_stat_cache[i].file_size = size; m_stat_cache[i].file_time = time; } void stat_cache::set_dirty(int i) { TORRENT_ASSERT(i >= 0); mutex::scoped_lock l(m_mutex); if (i >= int(m_stat_cache.size())) return; m_stat_cache[i].file_size = not_in_cache; } void stat_cache::set_noexist(int i) { TORRENT_ASSERT(i >= 0); mutex::scoped_lock l(m_mutex); if (i >= int(m_stat_cache.size())) m_stat_cache.resize(i + 1, not_in_cache); m_stat_cache[i].file_size = no_exist; } void stat_cache::set_error(int i) { TORRENT_ASSERT(i >= 0); mutex::scoped_lock l(m_mutex); if (i >= int(m_stat_cache.size())) m_stat_cache.resize(i + 1, not_in_cache); m_stat_cache[i].file_size = cache_error; } boost::int64_t stat_cache::get_filesize(int i) const { mutex::scoped_lock l(m_mutex); if (i >= int(m_stat_cache.size())) return not_in_cache; return m_stat_cache[i].file_size; } time_t stat_cache::get_filetime(int i) const { mutex::scoped_lock l(m_mutex); if (i >= int(m_stat_cache.size())) return not_in_cache; if (m_stat_cache[i].file_size < 0) return m_stat_cache[i].file_size; return m_stat_cache[i].file_time; } void stat_cache::init(int num_files) { mutex::scoped_lock l(m_mutex); m_stat_cache.resize(num_files, not_in_cache); } void stat_cache::clear() { mutex::scoped_lock l(m_mutex); std::vector().swap(m_stat_cache); } } libtorrent-rasterbar-1.1.13/src/storage.cpp000066400000000000000000001555661351156116000207240ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #include #if defined(__APPLE__) // for getattrlist() #include #include // for statfs() #include #include #endif #if defined(__linux__) #include #endif #if defined(__FreeBSD__) // for statfs() #include #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #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/file_pool.hpp" #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/disk_buffer_holder.hpp" #include "libtorrent/alloca.hpp" #include "libtorrent/stat_cache.hpp" #include //#define TORRENT_PARTIAL_HASH_LOG // for convert_to_wstring and convert_to_native #include "libtorrent/aux_/escape_string.hpp" #define DEBUG_STORAGE 0 #define DEBUG_DELETE_FILES 0 #if __cplusplus >= 201103L || defined __clang__ #if DEBUG_STORAGE #define DLOG(...) fprintf(__VA_ARGS__) #else #define DLOG(...) do {} while (false) #endif #if DEBUG_DELETE_FILES #define DFLOG(...) fprintf(__VA_ARGS__) #else #define DFLOG(...) do {} while (false) #endif #else #if DEBUG_STORAGE #define DLOG fprintf #else #define DLOG TORRENT_WHILE_0 fprintf #endif #if DEBUG_DELETE_FILES #define DFLOG fprintf #else #define DFLOG TORRENT_WHILE_0 fprintf #endif #endif // cplusplus namespace libtorrent { int copy_bufs(file::iovec_t const* bufs, int bytes, file::iovec_t* target) { int size = 0; int ret = 1; for (;;) { *target = *bufs; size += bufs->iov_len; if (size >= bytes) { target->iov_len -= size - bytes; return ret; } ++bufs; ++target; ++ret; } } void advance_bufs(file::iovec_t*& bufs, int bytes) { int size = 0; for (;;) { size += bufs->iov_len; if (size >= bytes) { bufs->iov_base = reinterpret_cast(bufs->iov_base) + bufs->iov_len - (size - bytes); bufs->iov_len = size - bytes; return; } ++bufs; } } void clear_bufs(file::iovec_t const* bufs, int num_bufs) { for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) std::memset(i->iov_base, 0, i->iov_len); } namespace { int count_bufs(file::iovec_t const* bufs, int bytes) { int size = 0; int count = 1; if (bytes == 0) return 0; for (file::iovec_t const* i = bufs;; ++i, ++count) { size += i->iov_len; if (size >= bytes) return count; } } #ifdef TORRENT_DISK_STATS static boost::atomic event_id; static mutex disk_access_mutex; // this is opened and closed by the disk_io_thread class FILE* g_access_log = NULL; enum access_log_flags_t { op_read = 0, op_write = 1, op_start = 0, op_end = 2 }; void write_access_log(boost::uint64_t offset, boost::uint32_t fileid, int flags, time_point timestamp) { if (g_access_log == NULL) return; // the event format in the log is: // uint64_t timestamp (microseconds) // uint64_t file offset // uint32_t file-id // uint8_t event (0: start read, 1: start write, 2: complete read, 4: complete write) char event[29]; char* ptr = event; detail::write_uint64(timestamp.time_since_epoch().count(), ptr); detail::write_uint64(offset, ptr); detail::write_uint64(static_cast(event_id++), ptr); detail::write_uint32(fileid, ptr); detail::write_uint8(flags, ptr); mutex::scoped_lock l(disk_access_mutex); int ret = fwrite(event, 1, sizeof(event), g_access_log); l.unlock(); if (ret != sizeof(event)) { fprintf(stderr, "ERROR writing to disk access log: (%d) %s\n" , errno, strerror(errno)); } } #endif } // anonymous namespace struct write_fileop : fileop { write_fileop(default_storage& st, int flags) : m_storage(st) , m_flags(flags) {} int file_op(int file_index, boost::int64_t file_offset, int size , file::iovec_t const* bufs, storage_error& ec) TORRENT_OVERRIDE TORRENT_FINAL { if (m_storage.files().pad_file_at(file_index)) { // writing to a pad-file is a no-op return size; } int num_bufs = count_bufs(bufs, size); if (file_index < int(m_storage.m_file_priority.size()) && m_storage.m_file_priority[file_index] == 0 && m_storage.use_partfile(file_index)) { TORRENT_ASSERT(m_storage.m_part_file); error_code e; peer_request map = m_storage.files().map_file(file_index , file_offset, 0); int ret = m_storage.m_part_file->writev(bufs, num_bufs , map.piece, map.start, e); if (e) { ec.ec = e; ec.file = file_index; ec.operation = storage_error::partfile_write; return -1; } return ret; } // invalidate our stat cache for this file, since // we're writing to it m_storage.m_stat_cache.set_dirty(file_index); file_handle handle = m_storage.open_file(file_index , file::read_write, ec); if (ec) return -1; // please ignore the adjusted_offset. It's just file_offset. boost::int64_t adjusted_offset = #ifndef TORRENT_NO_DEPRECATE m_storage.files().file_base_deprecated(file_index) + #endif file_offset; #ifdef TORRENT_DISK_STATS write_access_log(adjusted_offset, handle->file_id(), op_start | op_write, clock_type::now()); #endif error_code e; int ret = handle->writev(adjusted_offset , bufs, num_bufs, e, m_flags); // set this unconditionally in case the upper layer would like to treat // short reads as errors ec.operation = storage_error::write; // we either get an error or 0 or more bytes read TORRENT_ASSERT(e || ret >= 0); #ifdef TORRENT_DISK_STATS write_access_log(adjusted_offset + ret , handle->file_id(), op_end | op_write, clock_type::now()); #endif TORRENT_ASSERT(ret <= bufs_size(bufs, num_bufs)); if (e) { ec.ec = e; ec.file = file_index; return -1; } return ret; } private: default_storage& m_storage; int m_flags; }; struct read_fileop : fileop { read_fileop(default_storage& st, int const flags) : m_storage(st) , m_flags(flags) {} int file_op(int file_index, boost::int64_t file_offset, int size , file::iovec_t const* bufs, storage_error& ec) TORRENT_OVERRIDE TORRENT_FINAL { int num_bufs = count_bufs(bufs, size); if (m_storage.files().pad_file_at(file_index)) { // reading from a pad file yields zeroes clear_bufs(bufs, num_bufs); return size; } if (file_index < int(m_storage.m_file_priority.size()) && m_storage.m_file_priority[file_index] == 0 && m_storage.use_partfile(file_index)) { TORRENT_ASSERT(m_storage.m_part_file); error_code e; peer_request map = m_storage.files().map_file(file_index , file_offset, 0); int ret = m_storage.m_part_file->readv(bufs, num_bufs , map.piece, map.start, e); if (e) { ec.ec = e; ec.file = file_index; ec.operation = storage_error::partfile_read; return -1; } return ret; } file_handle handle = m_storage.open_file(file_index , file::read_only | m_flags, ec); if (ec) return -1; // please ignore the adjusted_offset. It's just file_offset. boost::int64_t adjusted_offset = #ifndef TORRENT_NO_DEPRECATE m_storage.files().file_base_deprecated(file_index) + #endif file_offset; #ifdef TORRENT_DISK_STATS write_access_log(adjusted_offset, handle->file_id(), op_start | op_read, clock_type::now()); #endif error_code e; int ret = handle->readv(adjusted_offset , bufs, num_bufs, e, m_flags); // set this unconditionally in case the upper layer would like to treat // short reads as errors ec.operation = storage_error::read; // we either get an error or 0 or more bytes read TORRENT_ASSERT(e || ret >= 0); #ifdef TORRENT_DISK_STATS write_access_log(adjusted_offset + ret , handle->file_id(), op_end | op_read, clock_type::now()); #endif TORRENT_ASSERT(ret <= bufs_size(bufs, num_bufs)); if (e) { ec.ec = e; ec.file = file_index; return -1; } return ret; } private: default_storage& m_storage; int const m_flags; }; default_storage::default_storage(storage_params const& params) : m_files(*params.files) , m_pool(*params.pool) , m_allocate_files(params.mode == storage_mode_allocate) { if (params.mapped_files) m_mapped_files.reset(new file_storage(*params.mapped_files)); if (params.priorities) m_file_priority = *params.priorities; TORRENT_ASSERT(m_files.num_files() > 0); m_save_path = complete(params.path); m_part_file_name = "." + (params.info ? to_hex(params.info->info_hash().to_string()) : params.files->name()) + ".parts"; } default_storage::~default_storage() { error_code ec; if (m_part_file) m_part_file->flush_metadata(ec); // this may be called from a different // thread than the disk thread m_pool.release(this); } void default_storage::need_partfile() { if (m_part_file) return; m_part_file.reset(new part_file( m_save_path, m_part_file_name , m_files.num_pieces(), m_files.piece_length())); } void default_storage::set_file_priority(std::vector& prio, storage_error& ec) { // extend our file priorities in case it's truncated // the default assumed priority is 4 (the default) if (prio.size() > m_file_priority.size()) m_file_priority.resize(prio.size(), 4); file_storage const& fs = files(); for (int i = 0; i < int(prio.size()); ++i) { // pad files always have priority 0. if (fs.pad_file_at(i)) continue; int old_prio = m_file_priority[i]; int new_prio = prio[i]; if (old_prio == 0 && new_prio != 0) { // move stuff out of the part file file_handle f = open_file(i, file::read_write, ec); if (ec) { prio = m_file_priority; return; } if (m_part_file && use_partfile(i)) { m_part_file->export_file(*f, fs.file_offset(i), fs.file_size(i), ec.ec); if (ec) { ec.file = i; ec.operation = storage_error::partfile_write; prio = m_file_priority; return; } } } else if (old_prio != 0 && new_prio == 0) { // move stuff into the part file // this is not implemented yet. // so we just don't use a partfile for this file std::string const fp = fs.file_path(i, m_save_path); if (exists(fp)) use_partfile(i, false); /* file_handle f = open_file(i, file::read_only, ec); if (ec.ec != boost::system::errc::no_such_file_or_directory) { if (ec) { prio = m_file_priority; return; } need_partfile(); m_part_file->import_file(*f, fs.file_offset(i), fs.file_size(i), ec.ec); if (ec) { ec.file = i; ec.operation = storage_error::partfile_read; prio = m_file_priority; return; } // remove the file std::string p = fs.file_path(i, m_save_path); delete_one_file(p, ec.ec); if (ec) { ec.file = i; ec.operation = storage_error::remove; prio = m_file_priority; return; } } */ } ec.ec.clear(); m_file_priority[i] = new_prio; if (m_file_priority[i] == 0 && use_partfile(i)) { need_partfile(); } } if (m_part_file) m_part_file->flush_metadata(ec.ec); if (ec) { ec.file = torrent_status::error_file_partfile; ec.operation = storage_error::partfile_write; } } bool default_storage::use_partfile(int const index) { TORRENT_ASSERT_VAL(index >= 0, index); if (index >= int(m_use_partfile.size())) return true; return m_use_partfile[index]; } void default_storage::use_partfile(int const index, bool const b) { if (index >= int(m_use_partfile.size())) m_use_partfile.resize(index + 1, true); m_use_partfile[index] = b; } void default_storage::initialize(storage_error& ec) { m_stat_cache.init(files().num_files()); #ifdef TORRENT_WINDOWS // don't do full file allocations on network drives #if TORRENT_USE_WSTRING std::wstring f = convert_to_wstring(m_save_path); int const drive_type = GetDriveTypeW(f.c_str()); #else int const drive_type = GetDriveTypeA(m_save_path.c_str()); #endif if (drive_type == DRIVE_REMOTE) m_allocate_files = false; #endif { mutex::scoped_lock l(m_file_created_mutex); m_file_created.resize(files().num_files(), false); } file_storage const& fs = files(); // if some files have priority 0, we need to check if they exist on the // filesystem, in which case we won't use a partfile for them. // this is to be backwards compatible with previous versions of // libtorrent, when part files were not supported. for (int i = 0; i < m_file_priority.size(); ++i) { if (m_file_priority[i] != 0 || fs.pad_file_at(i)) continue; file_status s; std::string const file_path = fs.file_path(i, m_save_path); error_code err; stat_file(file_path, &s, err); if (!err) { use_partfile(i, false); } else { need_partfile(); } } // first, create all missing directories std::string last_path; for (int file_index = 0; file_index < fs.num_files(); ++file_index) { // ignore files that have priority 0 if (int(m_file_priority.size()) > file_index && m_file_priority[file_index] == 0) { continue; } // ignore pad files if (fs.pad_file_at(file_index)) continue; boost::int64_t cached_size = m_stat_cache.get_filesize(file_index); if (cached_size == stat_cache::not_in_cache) { file_status s; std::string file_path = fs.file_path(file_index, m_save_path); stat_file(file_path, &s, ec.ec); if (!ec) { m_stat_cache.set_cache(file_index, s.file_size, s.mtime); cached_size = s.file_size; } else if (ec.ec != boost::system::errc::no_such_file_or_directory) { m_stat_cache.set_error(file_index); ec.file = file_index; ec.operation = storage_error::stat; break; } else { cached_size = stat_cache::no_exist; } } // if the file is empty and doesn't already exist, create it // deliberately don't truncate files that already exist // if a file is supposed to have size 0, but already exists, we will // never truncate it to 0. if (fs.file_size(file_index) == 0 && cached_size == stat_cache::no_exist) { std::string file_path = fs.file_path(file_index, m_save_path); std::string dir = parent_path(file_path); if (dir != last_path) { last_path = dir; create_directories(last_path, ec.ec); if (ec.ec) { ec.file = file_index; ec.operation = storage_error::mkdir; break; } } ec.ec.clear(); // just creating the file is enough to make it zero-sized. If // there's a race here and some other process truncates the file, // it's not a problem, we won't access empty files ever again file_handle f = open_file(file_index, file::read_write | file::random_access, ec); if (ec) return; size_t const mtime = m_stat_cache.get_filetime(file_index); m_stat_cache.set_cache(file_index, 0, mtime); } ec.ec.clear(); } // close files that were opened in write mode m_pool.release(this); #if defined TORRENT_DEBUG_FILE_LEAKS print_open_files("release files", m_files.name().c_str()); #endif } #ifndef TORRENT_NO_DEPRECATE void default_storage::finalize_file(int, storage_error&) {} #endif bool default_storage::has_any_file(storage_error& ec) { file_storage const& fs = files(); m_stat_cache.init(fs.num_files()); std::string file_path; for (int i = 0; i < fs.num_files(); ++i) { file_status s; boost::int64_t cache_status = m_stat_cache.get_filesize(i); if (cache_status < 0 && cache_status != stat_cache::no_exist) { file_path = fs.file_path(i, m_save_path); stat_file(file_path, &s, ec.ec); boost::int64_t r = s.file_size; if (ec.ec || !(s.mode & file_status::regular_file)) { r = stat_cache::cache_error; } if (ec && ec.ec == boost::system::errc::no_such_file_or_directory) { ec.ec.clear(); r = stat_cache::no_exist; } m_stat_cache.set_cache(i, r, s.mtime); if (ec) { ec.file = i; ec.operation = storage_error::stat; m_stat_cache.clear(); return false; } } // if we didn't find the file, check the next one if (m_stat_cache.get_filesize(i) == stat_cache::no_exist) continue; if (m_stat_cache.get_filesize(i) > 0) return true; } file_status s; stat_file(combine_path(m_save_path, m_part_file_name), &s, ec.ec); if (!ec) return true; if (ec && ec.ec == boost::system::errc::no_such_file_or_directory) ec.ec.clear(); if (ec) { ec.file = torrent_status::error_file_partfile; ec.operation = storage_error::stat; return false; } return false; } void default_storage::rename_file(int index, std::string const& new_filename , storage_error& ec) { if (index < 0 || index >= files().num_files()) return; std::string old_name = files().file_path(index, m_save_path); m_pool.release(this, index); // if the old file doesn't exist, just succeed and change the filename // that will be created. This shortcut is important because the // destination directory may not exist yet, which would cause a failure // even though we're not moving a file (yet). It's better for it to // fail later when we try to write to the file the first time, because // the user then will have had a chance to make the destination directory // valid. if (exists(old_name, ec.ec)) { #if defined TORRENT_DEBUG_FILE_LEAKS print_open_files("release files", m_files.name().c_str()); #endif std::string new_path; if (is_complete(new_filename)) new_path = new_filename; else new_path = combine_path(m_save_path, new_filename); std::string new_dir = parent_path(new_path); // create any missing directories that the new filename // lands in create_directories(new_dir, ec.ec); if (ec.ec) { ec.file = index; ec.operation = storage_error::rename; return; } rename(old_name, new_path, ec.ec); // if old_name doesn't exist, that's not an error // here. Once we start writing to the file, it will // be written to the new filename if (ec.ec == boost::system::errc::no_such_file_or_directory) ec.ec.clear(); if (ec) { ec.ec.clear(); copy_file(old_name, new_path, ec.ec); if (ec) { ec.file = index; ec.operation = storage_error::rename; return; } error_code ignore; remove(old_name, ignore); } } else if (ec.ec) { // if exists fails, report that error ec.file = index; ec.operation = storage_error::rename; return; } // if old path doesn't exist, just rename the file // in our file_storage, so that when it is created // it will get the new name if (!m_mapped_files) { m_mapped_files.reset(new file_storage(m_files)); } m_mapped_files->rename_file(index, new_filename); } void default_storage::release_files(storage_error&) { if (m_part_file) { error_code ignore; m_part_file->flush_metadata(ignore); } // make sure we don't have the files open m_pool.release(this); m_stat_cache.clear(); #if defined TORRENT_DEBUG_FILE_LEAKS print_open_files("release files", m_files.name().c_str()); #endif } void default_storage::delete_one_file(std::string const& p, error_code& ec) { remove(p, ec); DFLOG(stderr, "[%p] delete_one_file: %s [%s]\n", static_cast(this) , p.c_str(), ec.message().c_str()); if (ec == boost::system::errc::no_such_file_or_directory) ec.clear(); } void default_storage::delete_files(int const options, storage_error& ec) { DFLOG(stderr, "[%p] delete_files [%x]\n", static_cast(this) , options); #if TORRENT_USE_ASSERTS // this is a fence job, we expect no other // threads to hold any references to any files // in this file storage. Assert that that's the // case if (!m_pool.assert_idle_files(this)) { #if defined TORRENT_DEBUG_FILE_LEAKS print_open_files("delete-files idle assert failed", m_files.name().c_str()); #endif TORRENT_ASSERT(false); } #endif // make sure we don't have the files open m_pool.release(this); // if there's a part file open, make sure to destruct it to have it // release the underlying part file. Otherwise we may not be able to // delete it if (m_part_file) m_part_file.reset(); #if defined TORRENT_DEBUG_FILE_LEAKS print_open_files("release files", m_files.name().c_str()); #endif if (options == session::delete_files) { #if TORRENT_USE_ASSERTS m_pool.mark_deleted(m_files); #endif // delete the files from disk std::set directories; typedef std::set::iterator iter_t; for (int i = 0; i < files().num_files(); ++i) { std::string const fp = files().file_path(i); bool const complete = files().file_absolute_path(i); std::string p = complete ? fp : combine_path(m_save_path, fp); if (!complete) { std::string bp = parent_path(fp); std::pair ret; ret.second = true; while (ret.second && !bp.empty()) { ret = directories.insert(combine_path(m_save_path, bp)); bp = parent_path(bp); } } delete_one_file(p, ec.ec); if (ec) { ec.file = i; ec.operation = storage_error::remove; } } // remove the directories. Reverse order to delete // subdirectories first for (std::set::reverse_iterator i = directories.rbegin() , end(directories.rend()); i != end; ++i) { error_code error; delete_one_file(*i, error); if (error && !ec) { ec.file = -1; ec.ec = error; ec.operation = storage_error::remove; } } } if (options == session::delete_files || options == session::delete_partfile) { error_code error; remove(combine_path(m_save_path, m_part_file_name), error); DFLOG(stderr, "[%p] delete partfile %s/%s [%s]\n", static_cast(this) , m_save_path.c_str(), m_part_file_name.c_str(), error.message().c_str()); if (error && error != boost::system::errc::no_such_file_or_directory) { ec.file = torrent_status::error_file_partfile; ec.ec = error; ec.operation = storage_error::remove; } } DFLOG(stderr, "[%p] delete_files result: %s\n", static_cast(this) , ec.ec.message().c_str()); #if defined TORRENT_DEBUG_FILE_LEAKS print_open_files("delete-files done", m_files.name().c_str()); #endif } void default_storage::write_resume_data(entry& rd, storage_error& ec) const { TORRENT_ASSERT(rd.type() == entry::dictionary_t); entry::list_type& fl = rd["file sizes"].list(); if (m_part_file) { error_code ignore; const_cast(*m_part_file).flush_metadata(ignore); } file_storage const& fs = files(); for (int i = 0; i < fs.num_files(); ++i) { boost::int64_t file_size = 0; time_t file_time = 0; boost::int64_t const cache_state = m_stat_cache.get_filesize(i); if (cache_state != stat_cache::not_in_cache) { if (cache_state >= 0) { file_size = cache_state; file_time = m_stat_cache.get_filetime(i); } } else { file_status s; error_code error; stat_file(fs.file_path(i, m_save_path), &s, error); if (!error) { file_size = s.file_size; file_time = s.mtime; m_stat_cache.set_cache(i, file_size, file_time); } else if (error == error_code(boost::system::errc::no_such_file_or_directory , generic_category())) { m_stat_cache.set_noexist(i); } else { ec.ec = error; ec.file = i; ec.operation = storage_error::stat; m_stat_cache.set_error(i); } } #if TORRENT_USE_INVARIANT_CHECKS { file_status s; error_code error; stat_file(fs.file_path(i, m_save_path), &s, error); if (s.file_size >= 0 && !error) { TORRENT_ASSERT_VAL(s.file_size == file_size, file_size); } else { TORRENT_ASSERT_VAL(file_size == 0, file_size); } } #endif fl.push_back(entry(entry::list_t)); entry::list_type& p = fl.back().list(); p.push_back(entry(file_size)); p.push_back(entry(file_time)); } } bool default_storage::verify_resume_data(bdecode_node const& rd , std::vector const* links , storage_error& ec) { // TODO: make this more generic to not just work if files have been // renamed, but also if they have been merged into a single file for instance // maybe use the same format as .torrent files and reuse some code from torrent_info bdecode_node mapped_files = rd.dict_find_list("mapped_files"); if (mapped_files && mapped_files.list_size() == m_files.num_files()) { m_mapped_files.reset(new file_storage(m_files)); for (int i = 0; i < m_files.num_files(); ++i) { std::string new_filename = mapped_files.list_string_value_at(i); if (new_filename.empty()) continue; m_mapped_files->rename_file(i, new_filename); } } bdecode_node file_priority = rd.dict_find_list("file_priority"); if (file_priority && file_priority.list_size() == files().num_files()) { m_file_priority.resize(file_priority.list_size()); for (int i = 0; i < file_priority.list_size(); ++i) m_file_priority[i] = boost::uint8_t(file_priority.list_int_value_at(i, 1)); } bdecode_node file_sizes_ent = rd.dict_find_list("file sizes"); if (file_sizes_ent == 0) { ec.ec = errors::missing_file_sizes; ec.file = -1; ec.operation = storage_error::check_resume; return false; } if (file_sizes_ent.list_size() == 0) { ec.ec = errors::no_files_in_resume_data; return false; } file_storage const& fs = files(); if (file_sizes_ent.list_size() != fs.num_files()) { ec.ec = errors::mismatching_number_of_files; ec.file = -1; ec.operation = storage_error::check_resume; return false; } bool seed = false; bdecode_node slots = rd.dict_find_list("slots"); if (slots) { if (slots.list_size() == m_files.num_pieces()) { seed = true; for (int i = 0; i < slots.list_size(); ++i) { if (slots.list_int_value_at(i, -1) >= 0) continue; seed = false; break; } } } else if (bdecode_node pieces = rd.dict_find_string("pieces")) { if (pieces.string_length() == m_files.num_pieces()) { seed = true; char const* p = pieces.string_ptr(); for (int i = 0; i < pieces.string_length(); ++i) { if ((p[i] & 1) == 1) continue; seed = false; break; } } } else { ec.ec = errors::missing_pieces; ec.file = -1; ec.operation = storage_error::check_resume; return false; } for (int i = 0; i < file_sizes_ent.list_size(); ++i) { if (fs.pad_file_at(i)) continue; // files with priority zero may not have been saved to disk at their // expected location, but is likely to be in a partfile. Just exempt it // from checking // if we don't have a part-file, this may have been downloaded with a // previous version of libtorrent. Then assume the pieces are in the // files they belong in. if (i < int(m_file_priority.size()) && m_file_priority[i] == 0 && use_partfile(i)) continue; bdecode_node e = file_sizes_ent.list_at(i); if (e.type() != bdecode_node::list_t || e.list_size() < 2 || e.list_at(0).type() != bdecode_node::int_t || e.list_at(1).type() != bdecode_node::int_t) { ec.ec = errors::missing_file_sizes; ec.file = i; ec.operation = storage_error::check_resume; return false; } boost::int64_t expected_size = e.list_int_value_at(0); time_t expected_time = e.list_int_value_at(1); // if we're a seed, the expected size should match // the actual full size according to the torrent if (seed && expected_size < fs.file_size(i)) { ec.ec = errors::mismatching_file_size; ec.file = i; ec.operation = storage_error::check_resume; return false; } boost::int64_t file_size = m_stat_cache.get_filesize(i); time_t file_time; if (file_size >= 0) { file_time = m_stat_cache.get_filetime(i); } else { file_status s; error_code error; std::string file_path = fs.file_path(i, m_save_path); stat_file(file_path, &s, error); if (error) { if (error != boost::system::errc::no_such_file_or_directory) { m_stat_cache.set_error(i); ec.ec = error; ec.file = i; ec.operation = storage_error::stat; return false; } m_stat_cache.set_noexist(i); if (expected_size != 0) { ec.ec = errors::mismatching_file_size; ec.file = i; ec.operation = storage_error::none; return false; } file_size = 0; file_time = 0; } else { file_size = s.file_size; file_time = s.mtime; } } if (expected_size > file_size) { ec.ec = errors::mismatching_file_size; ec.file = i; ec.operation = storage_error::none; return false; } if (settings().get_bool(settings_pack::ignore_resume_timestamps)) continue; // allow some slack, because of FAT volumes if (expected_time != 0 && (file_time > expected_time + 5 * 60 || file_time < expected_time - 5)) { ec.ec = errors::mismatching_file_timestamp; ec.file = i; ec.operation = storage_error::stat; return false; } } // TODO: 2 we probably need to do this unconditionally in this function. // Even if the resume data file appears stale, we need to create these // hard links, right? #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS if (links) { // if this is a mutable torrent, and we need to pick up some files // from other torrents, do that now. Note that there is an inherent // race condition here. We checked if the files existed on a different // thread a while ago. These files may no longer exist or may have been // moved. If so, we just fail. The user is responsible to not touch // other torrents until a new mutable torrent has been completely // added. int idx = 0; for (std::vector::const_iterator i = links->begin(); i != links->end(); ++i, ++idx) { if (i->empty()) continue; error_code err; std::string file_path = fs.file_path(idx, m_save_path); hard_link(*i, file_path, err); // if the file already exists, that's not an error // TODO: 2 is this risky? The upper layer will assume we have the // whole file. Perhaps we should verify that at least the size // of the file is correct if (!err || err == boost::system::errc::file_exists) continue; ec.ec = err; ec.file = idx; ec.operation = storage_error::hard_link; return false; } } #endif // TORRENT_DISABLE_MUTABLE_TORRENTS return true; } int default_storage::move_storage(std::string const& sp, int const flags , storage_error& ec) { int ret = piece_manager::no_error; std::string const save_path = complete(sp); // check to see if any of the files exist file_storage const& f = files(); if (flags == fail_if_exist) { file_status s; error_code err; stat_file(save_path, &s, err); if (err != boost::system::errc::no_such_file_or_directory) { // the directory exists, check all the files for (int i = 0; i < f.num_files(); ++i) { // files moved out to absolute paths are ignored if (f.file_absolute_path(i)) continue; stat_file(f.file_path(i, save_path), &s, err); if (err != boost::system::errc::no_such_file_or_directory) { ec.ec = err; ec.file = i; ec.operation = storage_error::stat; return piece_manager::file_exist; } } } } { file_status s; error_code err; stat_file(save_path, &s, err); if (err == boost::system::errc::no_such_file_or_directory) { err.clear(); create_directories(save_path, err); if (err) { ec.ec = err; ec.file = -1; ec.operation = storage_error::mkdir; return piece_manager::fatal_disk_error; } } else if (err) { ec.ec = err; ec.file = -1; ec.operation = storage_error::stat; return piece_manager::fatal_disk_error; } } m_pool.release(this); #if defined TORRENT_DEBUG_FILE_LEAKS print_open_files("release files", m_files.name().c_str()); #endif // indices of all files we ended up copying. These need to be deleted // later std::vector copied_files(f.num_files(), false); int i; error_code e; for (i = 0; i < f.num_files(); ++i) { // files moved out to absolute paths are not moved if (f.file_absolute_path(i)) continue; std::string const old_path = combine_path(m_save_path, f.file_path(i)); std::string const new_path = combine_path(save_path, f.file_path(i)); if (flags == dont_replace && exists(new_path)) { if (ret == piece_manager::no_error) ret = piece_manager::need_full_check; // this is a new file, clear our cached version m_stat_cache.set_dirty(i); continue; } // TODO: ideally, if we end up copying files because of a move across // volumes, the source should not be deleted until they've all been // copied. That would let us rollback with higher confidence. move_file(old_path, new_path, e); // if the source file doesn't exist. That's not a problem // we just ignore that file if (e == boost::system::errc::no_such_file_or_directory) { e.clear(); // the source file doesn't exist, but it may exist at the // destination, we don't know. m_stat_cache.set_dirty(i); } else if (e && e != boost::system::errc::invalid_argument && e != boost::system::errc::permission_denied) { // moving the file failed // on OSX, the error when trying to rename a file across different // volumes is EXDEV, which will make it fall back to copying. e.clear(); copy_file(old_path, new_path, e); if (!e) copied_files[i] = true; } if (e) { ec.ec = e; ec.file = i; ec.operation = storage_error::rename; break; } } if (!e && m_part_file) { m_part_file->move_partfile(save_path, e); if (e) { ec.ec = e; ec.file = torrent_status::error_file_partfile; ec.operation = storage_error::partfile_move; } } if (e) { // rollback while (--i >= 0) { // files moved out to absolute paths are not moved if (f.file_absolute_path(i)) continue; // if we ended up copying the file, don't do anything during // roll-back if (copied_files[i]) continue; std::string const old_path = combine_path(m_save_path, f.file_path(i)); std::string const new_path = combine_path(save_path, f.file_path(i)); // ignore errors when rolling back error_code ignore; move_file(new_path, old_path, ignore); } return piece_manager::fatal_disk_error; } std::string const old_save_path = m_save_path; m_save_path = save_path; std::set subdirs; for (i = 0; i < f.num_files(); ++i) { // files moved out to absolute paths are not moved if (f.file_absolute_path(i)) continue; if (has_parent_path(f.file_path(i))) subdirs.insert(parent_path(f.file_path(i))); // if we ended up renaming the file instead of moving it, there's no // need to delete the source. if (copied_files[i] == false) continue; std::string const old_path = combine_path(old_save_path, f.file_path(i)); // we may still have some files in old old_save_path // eg. if (flags == dont_replace && exists(new_path)) // ignore errors when removing error_code ignore; remove(old_path, ignore); } for (std::set::iterator it(subdirs.begin()) , end(subdirs.end()); it != end; ++it) { error_code err; std::string subdir = combine_path(old_save_path, *it); while (!compare_path(subdir, old_save_path) && !err) { remove(subdir, err); subdir = parent_path(subdir); } } return ret; } int default_storage::readv(file::iovec_t const* bufs, int num_bufs , int piece, int offset, int flags, storage_error& ec) { read_fileop op(*this, flags); #ifdef TORRENT_SIMULATE_SLOW_READ boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(1000)); #endif return readwritev(files(), bufs, piece, offset, num_bufs, op, ec); } int default_storage::writev(file::iovec_t const* bufs, int num_bufs , int piece, int offset, int flags, storage_error& ec) { write_fileop op(*this, flags); return readwritev(files(), bufs, piece, offset, num_bufs, op, ec); } // much of what needs to be done when reading and writing is buffer // management and piece to file mapping. Most of that is the same for reading // and writing. This function is a template, and the fileop decides what to // do with the file and the buffers. int readwritev(file_storage const& files, file::iovec_t const* const bufs , const int piece, const int offset, const int num_bufs, fileop& op , storage_error& ec) { TORRENT_ASSERT(bufs != 0); TORRENT_ASSERT(piece >= 0); TORRENT_ASSERT(piece < files.num_pieces()); TORRENT_ASSERT(offset >= 0); TORRENT_ASSERT(num_bufs > 0); const int size = bufs_size(bufs, num_bufs); TORRENT_ASSERT(size > 0); TORRENT_ASSERT(files.is_loaded()); // find the file iterator and file offset boost::uint64_t torrent_offset = piece * boost::uint64_t(files.piece_length()) + offset; int file_index = files.file_index_at_offset(torrent_offset); TORRENT_ASSERT(torrent_offset >= files.file_offset(file_index)); TORRENT_ASSERT(torrent_offset < files.file_offset(file_index) + files.file_size(file_index)); boost::int64_t file_offset = torrent_offset - files.file_offset(file_index); // the number of bytes left before this read or write operation is // completely satisfied. int bytes_left = size; TORRENT_ASSERT(bytes_left >= 0); // copy the iovec array so we can use it to keep track of our current // location by updating the head base pointer and size. (see // advance_bufs()) file::iovec_t* current_buf = TORRENT_ALLOCA(file::iovec_t, num_bufs); copy_bufs(bufs, size, current_buf); TORRENT_ASSERT(count_bufs(current_buf, size) == num_bufs); file::iovec_t* tmp_buf = TORRENT_ALLOCA(file::iovec_t, num_bufs); // the number of bytes left to read in the current file (specified by // file_index). This is the minimum of (file_size - file_offset) and // bytes_left. int file_bytes_left; while (bytes_left > 0) { file_bytes_left = bytes_left; if (file_offset + file_bytes_left > files.file_size(file_index)) file_bytes_left = (std::max)(static_cast(files.file_size(file_index) - file_offset), 0); // there are no bytes left in this file, move to the next one // this loop skips over empty files while (file_bytes_left == 0) { ++file_index; file_offset = 0; TORRENT_ASSERT(file_index < files.num_files()); // this should not happen. bytes_left should be clamped by the total // size of the torrent, so we should never run off the end of it if (file_index >= files.num_files()) return size; file_bytes_left = bytes_left; if (file_offset + file_bytes_left > files.file_size(file_index)) file_bytes_left = (std::max)(static_cast(files.file_size(file_index) - file_offset), 0); } // make a copy of the iovec array that _just_ covers the next // file_bytes_left bytes, i.e. just this one operation copy_bufs(current_buf, file_bytes_left, tmp_buf); int bytes_transferred = op.file_op(file_index, file_offset, file_bytes_left, tmp_buf, ec); if (ec) return -1; // advance our position in the iovec array and the file offset. advance_bufs(current_buf, bytes_transferred); bytes_left -= bytes_transferred; file_offset += bytes_transferred; TORRENT_ASSERT(count_bufs(current_buf, bytes_left) <= num_bufs); // if the file operation returned 0, we've hit end-of-file. We're done if (bytes_transferred == 0) { if (file_bytes_left > 0 ) { // fill in this information in case the caller wants to treat // a short-read as an error ec.file = file_index; } return size - bytes_left; } } return size; } file_handle default_storage::open_file(int file, int mode , storage_error& ec) const { file_handle h = open_file_impl(file, mode, ec.ec); if (((mode & file::rw_mask) != file::read_only) && ec.ec == boost::system::errc::no_such_file_or_directory) { // this means the directory the file is in doesn't exist. // so create it ec.ec.clear(); std::string path = files().file_path(file, m_save_path); create_directories(parent_path(path), ec.ec); if (ec.ec) { ec.file = file; ec.operation = storage_error::mkdir; return file_handle(); } // if the directory creation failed, don't try to open the file again // but actually just fail h = open_file_impl(file, mode, ec.ec); } if (ec.ec) { ec.file = file; ec.operation = storage_error::open; return file_handle(); } TORRENT_ASSERT(h); if ((mode & file::rw_mask) != file::read_only) { mutex::scoped_lock l(m_file_created_mutex); if (m_file_created.size() != files().num_files()) m_file_created.resize(files().num_files(), false); TORRENT_ASSERT(int(m_file_created.size()) == files().num_files()); TORRENT_ASSERT(file < m_file_created.size()); // if this is the first time we open this file for writing, // and we have m_allocate_files enabled, set the final size of // the file right away, to allocate it on the filesystem. if (m_file_created[file] == false) { m_file_created.set_bit(file); l.unlock(); // if we're allocating files or if the file exists and is greater // than what it's supposed to be, truncate it to its correct size boost::int64_t const size = files().file_size(file); error_code e; bool const need_truncate = h->get_size(e) > size; if (e) { ec.ec = e; ec.file = file; ec.operation = storage_error::stat; return h; } if (m_allocate_files || need_truncate) { h->set_size(size, e); if (e) { ec.ec = e; ec.file = file; ec.operation = storage_error::fallocate; return h; } m_stat_cache.set_dirty(file); } } } return h; } file_handle default_storage::open_file_impl(int file, int mode , error_code& ec) const { bool lock_files = m_settings ? settings().get_bool(settings_pack::lock_files) : false; if (lock_files) mode |= file::lock_file; if (!m_allocate_files) mode |= file::sparse; // files with priority 0 should always be sparse if (int(m_file_priority.size()) > file && m_file_priority[file] == 0) mode |= file::sparse; if (m_settings && settings().get_bool(settings_pack::no_atime_storage)) mode |= file::no_atime; // if we have a cache already, don't store the data twice by leaving it in the OS cache as well if (m_settings && settings().get_int(settings_pack::disk_io_write_mode) == settings_pack::disable_os_cache) { mode |= file::no_cache; } file_handle ret = m_pool.open_file(const_cast(this) , m_save_path, file, files(), mode, ec); if (ec && (mode & file::lock_file)) { // we failed to open the file and we're trying to lock it. It's // possible we're failing because we have another handle to this // file in use (but waiting to be closed). Just retry to open it // without locking. mode &= ~file::lock_file; ret = m_pool.open_file(const_cast(this) , m_save_path, file, files(), mode, ec); } return ret; } bool default_storage::tick() { error_code ec; if (m_part_file) m_part_file->flush_metadata(ec); return false; } #ifdef TORRENT_DISK_STATS bool default_storage::disk_write_access_log() { return g_access_log != NULL; } void default_storage::disk_write_access_log(bool enable) { if (enable) { if (g_access_log == NULL) { g_access_log = fopen("file_access.log", "a+"); } } else { if (g_access_log != NULL) { FILE* f = g_access_log; g_access_log = NULL; fclose(f); } } } #endif storage_interface* default_storage_constructor(storage_params const& params) { return new default_storage(params); } int disabled_storage::readv(file::iovec_t const* bufs , int num_bufs, int, int, int, storage_error&) { return bufs_size(bufs, num_bufs); } int disabled_storage::writev(file::iovec_t const* bufs , int num_bufs, int, int, int, storage_error&) { return bufs_size(bufs, num_bufs); } storage_interface* disabled_storage_constructor(storage_params const& params) { TORRENT_UNUSED(params); return new disabled_storage; } // -- zero_storage ------------------------------------------------------ int zero_storage::readv(file::iovec_t const* bufs, int num_bufs , int /* piece */, int /* offset */, int /* flags */, storage_error&) { int ret = 0; for (int i = 0; i < num_bufs; ++i) { memset(bufs[i].iov_base, 0, bufs[i].iov_len); ret += bufs[i].iov_len; } return 0; } int zero_storage::writev(file::iovec_t const* bufs, int num_bufs , int /* piece */, int /* offset */, int /* flags */, storage_error&) { int ret = 0; for (int i = 0; i < num_bufs; ++i) ret += bufs[i].iov_len; return 0; } storage_interface* zero_storage_constructor(storage_params const&) { return new zero_storage; } void storage_piece_set::add_piece(cached_piece_entry* p) { TORRENT_ASSERT(p->in_storage == false); TORRENT_ASSERT(p->storage.get() == this); TORRENT_ASSERT(m_cached_pieces.count(p) == 0); m_cached_pieces.insert(p); #if TORRENT_USE_ASSERTS p->in_storage = true; #endif } bool storage_piece_set::has_piece(cached_piece_entry const* p) const { return m_cached_pieces.count(const_cast(p)) > 0; } void storage_piece_set::remove_piece(cached_piece_entry* p) { TORRENT_ASSERT(p->storage.get() == this); TORRENT_ASSERT(p->in_storage == true); TORRENT_ASSERT(m_cached_pieces.count(p) == 1); m_cached_pieces.erase(p); #if TORRENT_USE_ASSERTS p->in_storage = false; #endif } // -- piece_manager ----------------------------------------------------- piece_manager::piece_manager( storage_interface* storage_impl , boost::shared_ptr const& torrent , file_storage* files) : m_files(*files) , m_storage(storage_impl) , m_torrent(torrent) { } piece_manager::~piece_manager() {} #ifdef TORRENT_DEBUG void piece_manager::assert_torrent_refcount() const { if (!m_torrent) return; // sorry about this layer violation, but it's // quite convenient to make sure the torrent won't // get unloaded under our feet later TORRENT_ASSERT(static_cast(m_torrent.get())->refcount() > 0); } #endif // used in torrent_handle.cpp void piece_manager::write_resume_data(entry& rd, storage_error& ec) const { m_storage->write_resume_data(rd, ec); } int piece_manager::check_no_fastresume(storage_error& ec) { if (!m_storage->settings().get_bool(settings_pack::no_recheck_incomplete_resume)) { storage_error se; if (m_storage->has_any_file(se)) { // always initialize the storage int ret = check_init_storage(ec); return ret != no_error ? ret : need_full_check; } } return check_init_storage(ec); } int piece_manager::check_init_storage(storage_error& ec) { storage_error se; m_storage->initialize(se); if (se) { ec = se; return fatal_disk_error; } return no_error; } // 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. If the links pointer is non-null, it has the same number // of elements as there are files. Each element is either empty or contains // the absolute path to a file identical to the corresponding file in this // torrent. The storage must create hard links (or copy) those files. If // any file does not exist or is inaccessible, the disk job must fail. int piece_manager::check_fastresume( bdecode_node const& rd , std::vector const* links , storage_error& ec) { TORRENT_ASSERT(m_files.piece_length() > 0); // if we don't have any resume data, return if (rd.type() == bdecode_node::none_t) return check_no_fastresume(ec); if (rd.type() != bdecode_node::dict_t) { ec.ec = errors::not_a_dictionary; return check_no_fastresume(ec); } int block_size = (std::min)(16 * 1024, m_files.piece_length()); int blocks_per_piece = int(rd.dict_find_int_value("blocks per piece", -1)); if (blocks_per_piece != -1 && blocks_per_piece != m_files.piece_length() / block_size) { ec.ec = errors::invalid_blocks_per_piece; return check_no_fastresume(ec); } if (!m_storage->verify_resume_data(rd, links, ec)) return check_no_fastresume(ec); return check_init_storage(ec); } // ====== disk_job_fence implementation ======== disk_job_fence::disk_job_fence() : m_has_fence(0) , m_outstanding_jobs(0) {} int disk_job_fence::job_complete(disk_io_job* j, tailqueue& jobs) { mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(j->flags & disk_io_job::in_progress); j->flags &= ~disk_io_job::in_progress; TORRENT_ASSERT(m_outstanding_jobs > 0); --m_outstanding_jobs; if (j->flags & disk_io_job::fence) { // a fence job just completed. Make sure the fence logic // works by asserting m_outstanding_jobs is in fact 0 now TORRENT_ASSERT(m_outstanding_jobs == 0); // the fence can now be lowered --m_has_fence; // now we need to post all jobs that have been queued up // while this fence was up. However, if there's another fence // in the queue, stop there and raise the fence again int ret = 0; while (m_blocked_jobs.size()) { disk_io_job *bj = static_cast(m_blocked_jobs.pop_front()); if (bj->flags & disk_io_job::fence) { // we encountered another fence. We cannot post anymore // jobs from the blocked jobs queue. We have to go back // into a raised fence mode and wait for all current jobs // to complete. The exception is that if there are no jobs // executing currently, we should add the fence job. if (m_outstanding_jobs == 0 && jobs.empty()) { TORRENT_ASSERT((bj->flags & disk_io_job::in_progress) == 0); bj->flags |= disk_io_job::in_progress; ++m_outstanding_jobs; ++ret; #if TORRENT_USE_ASSERTS TORRENT_ASSERT(bj->blocked); bj->blocked = false; #endif jobs.push_back(bj); } else { // put the fence job back in the blocked queue m_blocked_jobs.push_front(bj); } return ret; } TORRENT_ASSERT((bj->flags & disk_io_job::in_progress) == 0); bj->flags |= disk_io_job::in_progress; ++m_outstanding_jobs; ++ret; #if TORRENT_USE_ASSERTS TORRENT_ASSERT(bj->blocked); bj->blocked = false; #endif jobs.push_back(bj); } return ret; } // there are still outstanding jobs, even if we have a // fence, it's not time to lower it yet // also, if we don't have a fence, we're done if (m_outstanding_jobs > 0 || m_has_fence == 0) return 0; // there's a fence raised, and no outstanding operations. // it means we can execute the fence job right now. TORRENT_ASSERT(m_blocked_jobs.size() > 0); // this is the fence job disk_io_job *bj = static_cast(m_blocked_jobs.pop_front()); TORRENT_ASSERT(bj->flags & disk_io_job::fence); TORRENT_ASSERT((bj->flags & disk_io_job::in_progress) == 0); bj->flags |= disk_io_job::in_progress; ++m_outstanding_jobs; #if TORRENT_USE_ASSERTS TORRENT_ASSERT(bj->blocked); bj->blocked = false; #endif // prioritize fence jobs since they're blocking other jobs jobs.push_front(bj); return 1; } bool disk_job_fence::is_blocked(disk_io_job* j) { mutex::scoped_lock l(m_mutex); DLOG(stderr, "[%p] is_blocked: fence: %d num_outstanding: %d\n" , static_cast(this), m_has_fence, int(m_outstanding_jobs)); // if this is the job that raised the fence, don't block it if (m_has_fence == 0) { TORRENT_ASSERT((j->flags & disk_io_job::in_progress) == 0); j->flags |= disk_io_job::in_progress; ++m_outstanding_jobs; return false; } m_blocked_jobs.push_back(j); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(j->blocked == false); j->blocked = true; #endif return true; } bool disk_job_fence::has_fence() const { mutex::scoped_lock l(m_mutex); return m_has_fence; } int disk_job_fence::num_blocked() const { mutex::scoped_lock l(m_mutex); return m_blocked_jobs.size(); } // j is the fence job. It must have exclusive access to the storage // fj is the flush job. If the job j is queued, we need to issue // this job int disk_job_fence::raise_fence(disk_io_job* j, disk_io_job* fj , counters& cnt) { TORRENT_ASSERT((j->flags & disk_io_job::fence) == 0); j->flags |= disk_io_job::fence; mutex::scoped_lock l(m_mutex); DLOG(stderr, "[%p] raise_fence: fence: %d num_outstanding: %d\n" , static_cast(this), m_has_fence, int(m_outstanding_jobs)); if (m_has_fence == 0 && m_outstanding_jobs == 0) { ++m_has_fence; DLOG(stderr, "[%p] raise_fence: need posting\n" , static_cast(this)); // the job j is expected to be put on the job queue // after this, without being passed through is_blocked() // that's why we're accounting for it here // fj is expected to be discarded by the caller j->flags |= disk_io_job::in_progress; ++m_outstanding_jobs; return fence_post_fence; } ++m_has_fence; if (m_has_fence > 1) { #if TORRENT_USE_ASSERTS TORRENT_ASSERT(fj->blocked == false); fj->blocked = true; #endif m_blocked_jobs.push_back(fj); cnt.inc_stats_counter(counters::blocked_disk_jobs); } else { // in this case, fj is expected to be put on the job queue fj->flags |= disk_io_job::in_progress; ++m_outstanding_jobs; } #if TORRENT_USE_ASSERTS TORRENT_ASSERT(j->blocked == false); j->blocked = true; #endif m_blocked_jobs.push_back(j); cnt.inc_stats_counter(counters::blocked_disk_jobs); return m_has_fence > 1 ? fence_post_none : fence_post_flush; } } // namespace libtorrent libtorrent-rasterbar-1.1.13/src/string_util.cpp000066400000000000000000000202251351156116000216020ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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/config.hpp" #include "libtorrent/string_util.hpp" #include "libtorrent/random.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/parse_url.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include // for malloc #include // for memmov/strcpy/strlen #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { // We need well defined results that don't depend on locale boost::array::digits10> to_string(boost::int64_t n) { boost::array::digits10> ret; char *p = &ret.back(); *p = '\0'; boost::uint64_t un = n; // TODO: warning C4146: unary minus operator applied to unsigned type, // result still unsigned if (n < 0) un = -un; do { *--p = '0' + un % 10; un /= 10; } while (un); if (n < 0) *--p = '-'; std::memmove(&ret[0], p, &ret.back() - p + 1); return ret; } bool is_alpha(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } bool is_print(char c) { return c >= 32 && c < 127; } bool is_space(char c) { static const char* ws = " \t\n\r\f\v"; return strchr(ws, c) != 0; } char to_lower(char c) { return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } int split_string(char const** tags, int buf_size, char* in) { int ret = 0; char* i = in; for (;*i; ++i) { if (!is_print(*i) || is_space(*i)) { *i = 0; if (ret == buf_size) return ret; continue; } if (i == in || i[-1] == 0) { tags[ret++] = i; } } return ret; } bool string_begins_no_case(char const* s1, char const* s2) { while (*s1 != 0) { if (to_lower(*s1) != to_lower(*s2)) return false; ++s1; ++s2; } return true; } bool string_equal_no_case(char const* s1, char const* s2) { while (to_lower(*s1) == to_lower(*s2)) { if (*s1 == 0) return true; ++s1; ++s2; } return false; } // generate a url-safe random string void url_random(char* begin, char* end) { // http-accepted characters: // excluding ', since some buggy trackers don't support that static char const printable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz-_.!~*()"; // the random number while (begin != end) *begin++ = printable[random() % (sizeof(printable)-1)]; } char* allocate_string_copy(char const* str) { if (str == 0) return 0; int const len = std::strlen(str) + 1; char* tmp = static_cast(std::malloc(len)); if (tmp == NULL) return 0; std::memcpy(tmp, str, len); return tmp; } // 8-byte align pointer void* align_pointer(void* p) { int offset = uintptr_t(p) & 0x7; // if we're already aligned, don't do anything if (offset == 0) return p; // offset is how far passed the last aligned address // we are. We need to go forward to the next aligned // one. Since aligned addresses are 8 bytes apart, add // 8 - offset. return static_cast(p) + (8 - offset); } // this parses the string that's used as the listen_interfaces setting. // it is a comma-separated list of IP or device names with ports. For // example: "eth0:6881,eth1:6881" or "127.0.0.1:6881" void parse_comma_separated_string_port(std::string const& in , std::vector >& out) { out.clear(); std::string::size_type start = 0; std::string::size_type end = 0; while (start < in.size()) { // skip leading spaces while (start < in.size() && is_space(in[start])) ++start; end = in.find_first_of(',', start); if (end == std::string::npos) end = in.size(); std::string::size_type colon = in.find_last_of(':', end); if (colon != std::string::npos && colon > start) { int port = atoi(in.substr(colon + 1, end - colon - 1).c_str()); // skip trailing spaces std::string::size_type soft_end = colon; while (soft_end > start && is_space(in[soft_end-1])) --soft_end; // in case this is an IPv6 address, strip off the square brackets // to make it more easily parseable into an ip::address if (in[start] == '[') ++start; if (soft_end > start && in[soft_end-1] == ']') --soft_end; out.push_back(std::make_pair(in.substr(start, soft_end - start), port)); } start = end + 1; } } void parse_comma_separated_string(std::string const& in, std::vector& out) { out.clear(); std::string::size_type start = 0; std::string::size_type end = 0; while (start < in.size()) { // skip leading spaces while (start < in.size() && is_space(in[start])) ++start; end = in.find_first_of(',', start); if (end == std::string::npos) end = in.size(); // skip trailing spaces std::string::size_type soft_end = end; while (soft_end > start && is_space(in[soft_end-1])) --soft_end; out.push_back(in.substr(start, soft_end - start)); start = end + 1; } } char* string_tokenize(char* last, char sep, char** next) { if (last == 0) return 0; if (last[0] == '"') { *next = strchr(last + 1, '"'); // consume the actual separator as well. if (*next != NULL) *next = strchr(*next, sep); } else { *next = strchr(last, sep); } if (*next == 0) return last; **next = 0; ++(*next); while (**next == sep && **next) ++(*next); return last; } #if TORRENT_USE_I2P bool is_i2p_url(std::string const& url) { using boost::tuples::ignore; std::string hostname; error_code ec; boost::tie(ignore, ignore, hostname, ignore, ignore) = parse_url_components(url, ec); char const* top_domain = strrchr(hostname.c_str(), '.'); return top_domain && strcmp(top_domain, ".i2p") == 0; } #endif std::size_t string_hash_no_case::operator()(std::string const& s) const { size_t ret = 5381; for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) ret = (ret * 33) ^ to_lower(*i); return ret; } bool string_eq_no_case::operator()(std::string const& lhs, std::string const& rhs) const { if (lhs.size() != rhs.size()) return false; std::string::const_iterator s1 = lhs.begin(); std::string::const_iterator s2 = rhs.begin(); while (s1 != lhs.end() && s2 != rhs.end()) { if (to_lower(*s1) != to_lower(*s2)) return false; ++s1; ++s2; } return true; } bool string_less_no_case::operator()(std::string const& lhs, std::string const& rhs) const { std::string::const_iterator s1 = lhs.begin(); std::string::const_iterator s2 = rhs.begin(); while (s1 != lhs.end() && s2 != rhs.end()) { char const c1 = to_lower(*s1); char const c2 = to_lower(*s2); if (c1 < c2) return true; if (c1 > c2) return false; ++s1; ++s2; } // this is the tie-breaker return lhs.size() < rhs.size(); } } libtorrent-rasterbar-1.1.13/src/thread.cpp000066400000000000000000000166531351156116000205200ustar00rootroot00000000000000/* Copyright (c) 2010-2018, 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/thread.hpp" #include "libtorrent/assert.hpp" #ifdef TORRENT_BEOS #include #endif #ifdef BOOST_HAS_PTHREADS #include // for gettimeofday() #include #endif #include namespace libtorrent { void sleep(int milliseconds) { #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN Sleep(milliseconds); #elif defined TORRENT_BEOS snooze_until(system_time() + boost::int64_t(milliseconds) * 1000, B_SYSTEM_TIMEBASE); #else usleep(milliseconds * 1000); #endif } #ifdef BOOST_HAS_PTHREADS recursive_mutex::recursive_mutex() { ::pthread_mutexattr_t attr; ::pthread_mutexattr_init(&attr); ::pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); ::pthread_mutex_init(&m_mutex, &attr); ::pthread_mutexattr_destroy(&attr); } recursive_mutex::~recursive_mutex() { ::pthread_mutex_destroy(&m_mutex); } void recursive_mutex::lock() { ::pthread_mutex_lock(&m_mutex); } void recursive_mutex::unlock() { ::pthread_mutex_unlock(&m_mutex); } #else namespace { #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN thread_id_t thread_self() { return GetCurrentThreadId(); } #elif defined TORRENT_BEOS thread_id_t thread_self() { return find_thread(NULL); } #endif } recursive_mutex::recursive_mutex() : m_owner() , m_count(0) {} recursive_mutex::~recursive_mutex() {} void recursive_mutex::lock() { thread_id_t const self = thread_self(); mutex::scoped_lock lock(m_mutex); if (m_owner == self) { ++m_count; } else if (m_count == 0) { TORRENT_ASSERT(m_owner == thread_id_t()); m_owner = self; m_count = 1; } else { while (m_count != 0) m_cond.wait(lock); TORRENT_ASSERT(m_count == 0); TORRENT_ASSERT(m_owner == thread_id_t()); m_owner = self; m_count = 1; } } void recursive_mutex::unlock() { thread_id_t const self = thread_self(); mutex::scoped_lock lock(m_mutex); TORRENT_ASSERT(m_owner == self); TORRENT_ASSERT(m_count > 0); if (--m_count == 0) { m_owner = thread_id_t(); m_cond.notify(); } } #endif #ifdef BOOST_HAS_PTHREADS condition_variable::condition_variable() { pthread_cond_init(&m_cond, 0); } condition_variable::~condition_variable() { pthread_cond_destroy(&m_cond); } template void condition_variable::wait_impl(LockGuard& l) { TORRENT_ASSERT(l.locked()); // wow, this is quite a hack pthread_cond_wait(&m_cond, reinterpret_cast(&l.mutex())); } template void condition_variable::wait_for_impl(LockGuard& l, time_duration rel_time) { TORRENT_ASSERT(l.locked()); struct timeval tv; struct timespec ts; gettimeofday(&tv, NULL); boost::uint64_t microseconds = tv.tv_usec + total_microseconds(rel_time) % 1000000; ts.tv_nsec = (microseconds % 1000000) * 1000; ts.tv_sec = tv.tv_sec + total_seconds(rel_time) + microseconds / 1000000; // wow, this is quite a hack pthread_cond_timedwait(&m_cond, reinterpret_cast(&l.mutex()), &ts); } void condition_variable::notify_all() { pthread_cond_broadcast(&m_cond); } void condition_variable::notify() { pthread_cond_signal(&m_cond); } #elif defined TORRENT_WINDOWS || defined TORRENT_CYGWIN condition_variable::condition_variable() : m_num_waiters(0) { #if _WIN32_WINNT <= 0x0501 m_sem = CreateSemaphore(0, 0, INT_MAX, 0); #else m_sem = CreateSemaphoreEx(0, 0, INT_MAX, 0, 0, SEMAPHORE_ALL_ACCESS); #endif } condition_variable::~condition_variable() { CloseHandle(m_sem); } template void condition_variable::wait_impl(LockGuard& l) { TORRENT_ASSERT(l.locked()); ++m_num_waiters; l.unlock(); WaitForSingleObjectEx(m_sem, INFINITE, FALSE); l.lock(); --m_num_waiters; } template void condition_variable::wait_for_impl(LockGuard& l, time_duration rel_time) { TORRENT_ASSERT(l.locked()); ++m_num_waiters; l.unlock(); WaitForSingleObjectEx(m_sem, total_milliseconds(rel_time), FALSE); l.lock(); --m_num_waiters; } void condition_variable::notify_all() { ReleaseSemaphore(m_sem, m_num_waiters, 0); } void condition_variable::notify() { ReleaseSemaphore(m_sem, (std::min)(m_num_waiters, 1), 0); } #elif defined TORRENT_BEOS condition_variable::condition_variable() : m_num_waiters(0) { m_sem = create_sem(0, 0); } condition_variable::~condition_variable() { delete_sem(m_sem); } template void condition_variable::wait_impl(mutex::scoped_lock& l) { TORRENT_ASSERT(l.locked()); ++m_num_waiters; l.unlock(); acquire_sem(m_sem); l.lock(); --m_num_waiters; } template void condition_variable::wait_for_impl(LockGuard& l, time_duration rel_time) { TORRENT_ASSERT(l.locked()); ++m_num_waiters; l.unlock(); acquire_sem_etc(m_sem, 1, B_RELATIVE_TIMEOUT, total_microseconds(rel_time)); l.lock(); --m_num_waiters; } void condition_variable::notify_all() { release_sem_etc(m_sem, m_num_waiters, 0); } void condition_variable::notify() { release_sem_etc(m_sem, (std::min)(m_num_waiters, 1), 0); } #else #error not implemented #endif void condition_variable::wait(mutex::scoped_lock& l) { wait_impl(l); } void condition_variable::wait_for(mutex::scoped_lock& l, time_duration rel_time) { wait_for_impl(l, rel_time); } void condition_variable::wait(recursive_mutex::scoped_lock& l) { wait_impl(l); } void condition_variable::wait_for(recursive_mutex::scoped_lock& l, time_duration rel_time) { wait_for_impl(l, rel_time); } // explicitly instantiate for these locks template void condition_variable::wait_impl(mutex::scoped_lock&); template void condition_variable::wait_impl(recursive_mutex::scoped_lock&); template void condition_variable::wait_for_impl(mutex::scoped_lock&, time_duration); template void condition_variable::wait_for_impl(recursive_mutex::scoped_lock&, time_duration); } libtorrent-rasterbar-1.1.13/src/time.cpp000066400000000000000000000041241351156116000201750ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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 #include #include #include #include #include "libtorrent/config.hpp" #include "libtorrent/time.hpp" #include "libtorrent/aux_/time.hpp" namespace libtorrent { namespace aux { // used to cache the current time // every 100 ms. This is cheaper // than a system call and can be // used where more accurate time // is not necessary namespace { time_point g_current_time = clock_type::now(); } time_point const& time_now() { return aux::g_current_time; } void update_time_now() { g_current_time = clock_type::now(); } } } libtorrent-rasterbar-1.1.13/src/timestamp_history.cpp000066400000000000000000000063451351156116000230320ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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/timestamp_history.hpp" namespace libtorrent { enum { TIME_MASK = 0xffffffff }; // defined in utp_stream.cpp bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs , boost::uint32_t mask); boost::uint32_t timestamp_history::add_sample(boost::uint32_t sample, bool step) { if (!initialized()) { for (int i = 0; i < history_size; ++i) m_history[i] = sample; m_base = sample; m_num_samples = 0; } // don't let the counter wrap if (m_num_samples < 0xfffe) ++m_num_samples; // if sample is less than base, update the base // and update the history entry (because it will // be less than that too) if (compare_less_wrap(sample, m_base, TIME_MASK)) { m_base = sample; m_history[m_index] = sample; } // if sample is less than our history entry, update it else if (compare_less_wrap(sample, m_history[m_index], TIME_MASK)) { m_history[m_index] = sample; } boost::uint32_t ret = sample - m_base; // don't step base delay history unless we have at least 120 // samples. Anything less would suggest that the connection is // essentially idle and the samples are probably not very reliable if (step && m_num_samples > 120) { m_num_samples = 0; m_index = (m_index + 1) % history_size; m_history[m_index] = sample; // update m_base m_base = sample; for (int i = 0; i < history_size; ++i) { if (compare_less_wrap(m_history[i], m_base, TIME_MASK)) m_base = m_history[i]; } } return ret; } void timestamp_history::adjust_base(int change) { TORRENT_ASSERT(initialized()); m_base += change; // make sure this adjustment sticks by updating all history slots for (int i = 0; i < history_size; ++i) { if (compare_less_wrap(m_history[i], m_base, TIME_MASK)) m_history[i] = m_base; } } } libtorrent-rasterbar-1.1.13/src/torrent.cpp000066400000000000000000013221101351156116000207330ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include // for va_list #include #include #include #include #include #include #include #include // for numeric_limits #include #include #if TORRENT_USE_I2P # include #endif #ifdef TORRENT_USE_OPENSSL #include "libtorrent/ssl_stream.hpp" #include #include #if BOOST_VERSION >= 104700 #include #endif // BOOST_VERSION #endif // TORRENT_USE_OPENSSL #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/announce_entry.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/tracker_manager.hpp" #include "libtorrent/parse_url.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/peer.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/web_peer_connection.hpp" #include "libtorrent/http_seed_connection.hpp" #include "libtorrent/peer_connection_handle.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/identify_client.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/aux_/session_interface.hpp" #include "libtorrent/instantiate_connection.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/kademlia/dht_tracker.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/http_connection.hpp" #include "libtorrent/random.hpp" #include "libtorrent/peer_class.hpp" // for peer_class #include "libtorrent/socket_io.hpp" // for read_*_endpoint #include "libtorrent/ip_filter.hpp" #include "libtorrent/request_blocks.hpp" #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/resolver_interface.hpp" #include "libtorrent/alloca.hpp" #include "libtorrent/resolve_links.hpp" #include "libtorrent/aux_/file_progress.hpp" #include "libtorrent/alert_manager.hpp" #include "libtorrent/disk_interface.hpp" #include "libtorrent/broadcast_socket.hpp" // for is_ip_address // TODO: factor out cache_status to its own header #include "libtorrent/disk_io_thread.hpp" // for cache_status #ifndef TORRENT_DISABLE_LOGGING #include "libtorrent/aux_/session_impl.hpp" // for tracker_logger #endif using namespace libtorrent; using boost::tuples::tuple; using boost::tuples::get; using boost::tuples::make_tuple; namespace libtorrent { namespace { boost::uint32_t const unset = std::numeric_limits::max(); bool is_downloading_state(int const st) { switch (st) { case torrent_status::checking_files: case torrent_status::allocating: case torrent_status::checking_resume_data: return false; case torrent_status::downloading_metadata: case torrent_status::downloading: case torrent_status::finished: case torrent_status::seeding: return true; default: // unexpected state TORRENT_ASSERT_VAL(false, st); return false; } } int root2(int x) { int ret = 0; x >>= 1; while (x > 0) { // if this assert triggers, the block size // is not an even 2 exponent! TORRENT_ASSERT(x == 1 || (x & 1) == 0); ++ret; x >>= 1; } return ret; } } // anonymous namespace web_seed_t::web_seed_t(web_seed_entry const& wse) : web_seed_entry(wse) , retry(aux::time_now()) , peer_info(tcp::endpoint(), true, 0) , supports_keepalive(true) , resolving(false) , removed(false) { peer_info.web_seed = true; restart_request.piece = -1; restart_request.start = -1; restart_request.length = -1; } web_seed_t::web_seed_t(std::string const& url_, web_seed_entry::type_t type_ , std::string const& auth_ , web_seed_entry::headers_t const& extra_headers_) : web_seed_entry(url_, type_, auth_, extra_headers_) , retry(aux::time_now()) , peer_info(tcp::endpoint(), true, 0) , supports_keepalive(true) , resolving(false) , removed(false) { peer_info.web_seed = true; restart_request.piece = -1; restart_request.start = -1; restart_request.length = -1; } peer_id generate_peer_id(aux::session_settings const& sett) { // ---- generate a peer id ---- std::string print = sett.get_str(settings_pack::peer_fingerprint); if (print.size() > 20) print.resize(20); // the client's fingerprint peer_id ret; std::copy(print.begin(), print.begin() + print.length(), ret.begin()); if (print.length() < 20) { url_random(ret.data() + print.length(), ret.data() + 20); } return ret; } #ifndef TORRENT_DISABLE_EXTENSIONS // defined in ut_pex.cpp bool was_introduced_by(peer_plugin const*, tcp::endpoint const&); #endif torrent_hot_members::torrent_hot_members(aux::session_interface& ses , add_torrent_params const& p, int block_size) : m_ses(ses) , m_complete(0xffffff) , m_upload_mode((p.flags & add_torrent_params::flag_upload_mode) != 0) , m_connections_initialized(false) , m_abort(false) , m_allow_peers((p.flags & add_torrent_params::flag_paused) == 0) , m_share_mode((p.flags & add_torrent_params::flag_share_mode) != 0) , m_have_all(false) , m_graceful_pause_mode(false) , m_state_subscription((p.flags & add_torrent_params::flag_update_subscribe) != 0) , m_max_connections(0xffffff) , m_block_size_shift(root2(block_size)) , m_state(torrent_status::checking_resume_data) {} torrent::torrent( aux::session_interface& ses , int block_size , int seq , add_torrent_params const& p , sha1_hash const& info_hash) : torrent_hot_members(ses, p, block_size) , m_total_uploaded(0) , m_total_downloaded(0) , m_tracker_timer(ses.get_io_service()) , m_inactivity_timer(ses.get_io_service()) , m_trackerid(p.trackerid) , m_save_path(complete(p.save_path)) , m_url(p.url) , m_uuid(p.uuid) , m_source_feed_url(p.source_feed_url) , m_stats_counters(ses.stats_counters()) , m_storage_constructor(p.storage) , m_added_time(time(0)) , m_completed_time(0) , m_last_seen_complete(0) , m_swarm_last_seen_complete(0) , m_info_hash(info_hash) , m_num_verified(0) , m_last_saved_resume(ses.session_time()) , m_started(ses.session_time()) , m_became_seed(0) , m_became_finished(0) , m_checking_piece(0) , m_num_checked_pieces(0) , m_refcount(0) , m_error_file(torrent_status::error_file_none) , m_average_piece_time(0) , m_piece_time_deviation(0) , m_total_failed_bytes(0) , m_total_redundant_bytes(0) , m_sequence_number(seq) , m_peer_class(0) , m_num_connecting(0) , m_peer_id(generate_peer_id(settings())) , m_upload_mode_time(0) , m_announce_to_trackers((p.flags & add_torrent_params::flag_paused) == 0) , m_announce_to_lsd((p.flags & add_torrent_params::flag_paused) == 0) , m_has_incoming(false) , m_files_checked(false) , m_storage_mode(p.storage_mode) , m_announcing(false) , m_waiting_tracker(0) , m_active_time(0) , m_last_working_tracker(-1) , m_finished_time(0) , m_sequential_download(false) , m_auto_sequential(false) , m_seed_mode(false) , m_super_seeding(false) , m_override_resume_data((p.flags & add_torrent_params::flag_override_resume_data) != 0) #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES , m_resolving_country(false) , m_resolve_countries(false) #endif #endif , m_need_save_resume_data(true) , m_seeding_time(0) , m_max_uploads((1<<24)-1) , m_save_resume_flags(0) , m_num_uploads(0) , m_need_suggest_pieces_refresh(false) , m_lsd_seq(0) , m_magnet_link(false) , m_apply_ip_filter((p.flags & add_torrent_params::flag_apply_ip_filter) != 0) , m_merge_resume_trackers((p.flags & add_torrent_params::flag_merge_resume_trackers) != 0) , m_padding(0) , m_connect_boost_counter(static_cast(settings().get_int(settings_pack::torrent_connect_boost))) , m_incomplete(0xffffff) , m_announce_to_dht((p.flags & add_torrent_params::flag_paused) == 0) , m_ssl_torrent(false) , m_deleted(false) , m_pinned((p.flags & add_torrent_params::flag_pinned) != 0) , m_should_be_loaded(true) , m_last_download(unset) , m_num_seeds(0) , m_num_connecting_seeds(0) , m_last_upload(unset) , m_storage_tick(0) , m_auto_managed(p.flags & add_torrent_params::flag_auto_managed) , m_current_gauge_state(no_gauge_state) , m_moving_storage(false) , m_inactive(false) , m_downloaded(0xffffff) , m_last_scrape(unset) , m_progress_ppm(0) , m_pending_active_change(false) , m_use_resume_save_path((p.flags & add_torrent_params::flag_use_resume_save_path) != 0) , m_merge_resume_http_seeds((p.flags & add_torrent_params::flag_merge_resume_http_seeds) != 0) , m_stop_when_ready((p.flags & add_torrent_params::flag_stop_when_ready) != 0) #if TORRENT_USE_ASSERTS , m_resume_data_loaded(false) , m_was_started(false) , m_iterating_connections(0) #endif { // we cannot log in the constructor, because it relies on shared_from_this // being initialized, which happens after the constructor returns. if (m_pinned) inc_stats_counter(counters::num_pinned_torrents); inc_stats_counter(counters::num_loaded_torrents); // if there is resume data already, we don't need to trigger the initial save // resume data if (!p.resume_data.empty() && (p.flags & add_torrent_params::flag_override_resume_data) == 0) m_need_save_resume_data = false; #if TORRENT_USE_UNC_PATHS m_save_path = canonicalize_path(m_save_path); #endif if (!m_apply_ip_filter) { inc_stats_counter(counters::non_filter_torrents); } if (!p.ti || !p.ti->is_valid()) { // we don't have metadata for this torrent. We'll download // it either through the URL passed in, or through a metadata // extension. Make sure that when we save resume data for this // torrent, we also save the metadata m_magnet_link = true; } if (!m_torrent_file) m_torrent_file = (p.ti ? p.ti : boost::make_shared(info_hash)); std::vector const& web_seeds = m_torrent_file->web_seeds(); m_web_seeds.insert(m_web_seeds.end(), web_seeds.begin(), web_seeds.end()); // add web seeds from add_torrent_params for (std::vector::const_iterator i = p.url_seeds.begin() , end(p.url_seeds.end()); i != end; ++i) { m_web_seeds.push_back(web_seed_t(*i, web_seed_entry::url_seed)); } m_trackers = m_torrent_file->trackers(); if (m_torrent_file->is_valid()) { m_seed_mode = (p.flags & add_torrent_params::flag_seed_mode) != 0; m_connections_initialized = true; m_block_size_shift = root2((std::min)(block_size, m_torrent_file->piece_length())); } else { if (!p.name.empty()) m_name.reset(new std::string(p.name)); } if (!m_url.empty() && m_uuid.empty()) m_uuid = m_url; TORRENT_ASSERT(is_single_thread()); m_file_priority = p.file_priorities; if (m_seed_mode) { m_verified.resize(m_torrent_file->num_pieces(), false); m_verifying.resize(m_torrent_file->num_pieces(), false); } if (!p.resume_data.empty()) { m_resume_data.reset(new resume_data_t); m_resume_data->buf = p.resume_data; } } void torrent::inc_stats_counter(int c, int value) { m_ses.stats_counters().inc_stats_counter(c, value); } #if 0 // NON BOTTLED VERSION. SUPPORTS PROGRESS REPORTING // since this download is not bottled, this callback will // be called every time we receive another piece of the // .torrent file void torrent::on_torrent_download(error_code const& ec , http_parser const& parser , char const* data, int size) { if (m_abort) return; if (ec && ec != boost::asio::error::eof) { set_error(ec, error_file_url); pause(); return; } if (size > 0) { m_torrent_file_buf.insert(m_torrent_file_buf.end(), data, data + size); if (parser.content_length() > 0) set_progress_ppm(boost::int64_t(m_torrent_file_buf.size()) * 1000000 / parser.content_length()); } if (parser.header_finished() && parser.status_code() != 200) { set_error(error_code(parser.status_code(), http_category()), error_file_url); pause(); return; } if (!ec) return; // if this was received with chunked encoding, we need to strip out // the chunk headers size = parser.collapse_chunk_headers((char*)&m_torrent_file_buf[0], m_torrent_file_buf.size()); m_torrent_file_buf.resize(size); std::string const& encoding = parser.header("content-encoding"); if ((encoding == "gzip" || encoding == "x-gzip") && m_torrent_file_buf.size()) { std::vector buf; error_code ec; inflate_gzip(&m_torrent_file_buf[0], m_torrent_file_buf.size() , buf, 4 * 1024 * 1024, ex); if (ec) { set_error(ec, error_file_url); pause(); std::vector().swap(m_torrent_file_buf); return; } m_torrent_file_buf.swap(buf); } // we're done! error_code e; boost::shared_ptr tf(boost::make_shared( &m_torrent_file_buf[0], m_torrent_file_buf.size(), e)); if (e) { set_error(e, error_file_url); pause(); std::vector().swap(m_torrent_file_buf); return; } std::vector().swap(m_torrent_file_buf); // update our torrent_info object and move the // torrent from the old info-hash to the new one // as we replace the torrent_info object #if TORRENT_USE_ASSERTS int num_torrents = m_ses.m_torrents.size(); #endif // we're about to erase the session's reference to this // torrent, create another reference boost::shared_ptr me(shared_from_this()); m_ses.remove_torrent_impl(me, 0); m_torrent_file = tf; // now, we might already have this torrent in the session. boost::shared_ptr t = m_ses.find_torrent(m_torrent_file->info_hash()).lock(); if (t) { if (!m_uuid.empty() && t->uuid().empty()) t->set_uuid(m_uuid); if (!m_url.empty() && t->url().empty()) t->set_url(m_url); if (!m_source_feed_url.empty() && t->source_feed_url().empty()) t->set_source_feed_url(m_source_feed_url); // insert this torrent in the uuid index if (!m_uuid.empty() || !m_url.empty()) { m_ses.insert_uuid_torrent(m_uuid.empty() ? m_url : m_uuid, t); } // TODO: if the existing torrent doesn't have metadata, insert // the metadata we just downloaded into it. set_error(errors::duplicate_torrent, error_file_url); abort(); return; } m_ses.insert_torrent(m_torrent_file->info_hash(), me, m_uuid); TORRENT_ASSERT(num_torrents == int(m_ses.m_torrents.size())); // if the user added any trackers while downloading the // .torrent file, merge them into the new tracker list std::vector new_trackers = m_torrent_file->trackers(); for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) { // if we already have this tracker, ignore it if (std::find_if(new_trackers.begin(), new_trackers.end() , boost::bind(&announce_entry::url, _1) == i->url) != new_trackers.end()) continue; // insert the tracker ordered by tier new_trackers.insert(std::find_if(new_trackers.begin(), new_trackers.end() , boost::bind(&announce_entry::tier, _1) >= i->tier), *i); } m_trackers.swap(new_trackers); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) hasher h; h.update("req2", 4); h.update((char*)&m_torrent_file->info_hash()[0], 20); // this is SHA1("req2" + info-hash), used for // encrypted hand shakes m_ses.add_obfuscated_hash(h.final(), shared_from_this()); #endif if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert( get_handle()); } state_updated(); set_state(torrent_status::downloading); init(); } #else // if 0 void torrent::on_torrent_download(error_code const& ec , http_parser const& parser, char const* data, int size) { if (m_abort) return; if (ec && ec != boost::asio::error::eof) { set_error(ec, torrent_status::error_file_url); pause(); return; } if (parser.status_code() != 200) { set_error(error_code(parser.status_code(), http_category()), torrent_status::error_file_url); pause(); return; } error_code e; boost::shared_ptr tf(boost::make_shared(data, size, boost::ref(e), 0)); if (e) { set_error(e, torrent_status::error_file_url); pause(); return; } // update our torrent_info object and move the // torrent from the old info-hash to the new one // as we replace the torrent_info object // we're about to erase the session's reference to this // torrent, create another reference boost::shared_ptr me(shared_from_this()); m_ses.remove_torrent_impl(me, 0); if (alerts().should_post()) alerts().emplace_alert(get_handle(), info_hash(), tf->info_hash()); m_torrent_file = tf; m_info_hash = tf->info_hash(); // now, we might already have this torrent in the session. boost::shared_ptr t = m_ses.find_torrent(m_torrent_file->info_hash()).lock(); if (t) { if (!m_uuid.empty() && t->uuid().empty()) t->set_uuid(m_uuid); if (!m_url.empty() && t->url().empty()) t->set_url(m_url); if (!m_source_feed_url.empty() && t->source_feed_url().empty()) t->set_source_feed_url(m_source_feed_url); // insert this torrent in the uuid index if (!m_uuid.empty() || !m_url.empty()) { m_ses.insert_uuid_torrent(m_uuid.empty() ? m_url : m_uuid, t); } // TODO: if the existing torrent doesn't have metadata, insert // the metadata we just downloaded into it. set_error(errors::duplicate_torrent, torrent_status::error_file_url); abort(); return; } m_ses.insert_torrent(m_torrent_file->info_hash(), me, m_uuid); // if the user added any trackers while downloading the // .torrent file, merge them into the new tracker list std::vector new_trackers = m_torrent_file->trackers(); for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) { // if we already have this tracker, ignore it if (std::find_if(new_trackers.begin(), new_trackers.end() , boost::bind(&announce_entry::url, _1) == i->url) != new_trackers.end()) continue; // insert the tracker ordered by tier new_trackers.insert(std::find_if(new_trackers.begin(), new_trackers.end() , boost::bind(&announce_entry::tier, _1) >= i->tier), *i); } m_trackers.swap(new_trackers); // add the web seeds from the .torrent file std::vector const& web_seeds = m_torrent_file->web_seeds(); m_web_seeds.insert(m_web_seeds.end(), web_seeds.begin(), web_seeds.end()); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) hasher h; h.update("req2", 4); h.update(m_torrent_file->info_hash().data(), 20); m_ses.add_obfuscated_hash(h.final(), shared_from_this()); #endif if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert( get_handle()); } state_updated(); set_state(torrent_status::downloading); init(); } #endif // if 0 int torrent::current_stats_state() const { if (m_abort) return counters::num_checking_torrents + no_gauge_state; if (has_error()) return counters::num_error_torrents; if (!m_allow_peers || m_graceful_pause_mode) { if (!is_auto_managed()) return counters::num_stopped_torrents; if (is_seed()) return counters::num_queued_seeding_torrents; return counters::num_queued_download_torrents; } if (state() == torrent_status::checking_files #ifndef TORRENT_NO_DEPRECATE || state() == torrent_status::queued_for_checking #endif ) return counters::num_checking_torrents; else if (is_seed()) return counters::num_seeding_torrents; else if (is_upload_only()) return counters::num_upload_only_torrents; return counters::num_downloading_torrents; } void torrent::update_gauge() { int new_gauge_state = current_stats_state() - counters::num_checking_torrents; TORRENT_ASSERT(new_gauge_state >= 0); TORRENT_ASSERT(new_gauge_state <= no_gauge_state); if (new_gauge_state == m_current_gauge_state) return; if (m_current_gauge_state != no_gauge_state) inc_stats_counter(m_current_gauge_state + counters::num_checking_torrents, -1); if (new_gauge_state != no_gauge_state) inc_stats_counter(new_gauge_state + counters::num_checking_torrents, 1); m_current_gauge_state = new_gauge_state; } void torrent::leave_seed_mode(bool skip_checking) { if (!m_seed_mode) return; if (!skip_checking) { // this means the user promised we had all the // files, but it turned out we didn't. This is // an error. // TODO: 2 post alert #ifndef TORRENT_DISABLE_LOGGING debug_log("*** FAILED SEED MODE, rechecking"); #endif } #ifndef TORRENT_DISABLE_LOGGING debug_log("*** LEAVING SEED MODE (%s)" , skip_checking ? "as seed" : "as non-seed"); #endif m_seed_mode = false; // seed is false if we turned out not // to be a seed after all if (!skip_checking) { m_have_all = false; set_state(torrent_status::downloading); force_recheck(); } m_num_verified = 0; m_verified.clear(); m_verifying.clear(); set_need_save_resume(); } void torrent::verified(int piece) { TORRENT_ASSERT(piece < int(m_verified.size())); TORRENT_ASSERT(piece >= 0); TORRENT_ASSERT(m_verified.get_bit(piece) == false); ++m_num_verified; m_verified.set_bit(piece); } void torrent::start(add_torrent_params const& p) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_was_started == false); #if TORRENT_USE_ASSERTS m_was_started = true; #endif #ifndef TORRENT_DISABLE_LOGGING debug_log("creating torrent: %s max-uploads: %d max-connections: %d " "upload-limit: %d download-limit: %d flags: %s%s%s%s%s%s%s%s%s%s%s%s" "save-path: %s" , torrent_file().name().c_str() , p.max_uploads , p.max_connections , p.upload_limit , p.download_limit , (p.flags & add_torrent_params::flag_seed_mode) ? "seed-mode " : "" , (p.flags & add_torrent_params::flag_override_resume_data) ? "override-resume-data " : "" , (p.flags & add_torrent_params::flag_upload_mode) ? "upload-mode " : "" , (p.flags & add_torrent_params::flag_share_mode) ? "share-mode " : "" , (p.flags & add_torrent_params::flag_apply_ip_filter) ? "apply-ip-filter " : "" , (p.flags & add_torrent_params::flag_paused) ? "paused " : "" , (p.flags & add_torrent_params::flag_auto_managed) ? "auto-managed " : "" , (p.flags & add_torrent_params::flag_merge_resume_trackers) ? "merge-resume-trackers " : "" , (p.flags & add_torrent_params::flag_update_subscribe) ? "update-subscribe " : "" , (p.flags & add_torrent_params::flag_super_seeding) ? "super-seeding " : "" , (p.flags & add_torrent_params::flag_sequential_download) ? "sequential-download " : "" , (p.flags & add_torrent_params::flag_use_resume_save_path) ? "resume-save-path " : "" , p.save_path.c_str() ); #endif if (p.flags & add_torrent_params::flag_sequential_download) m_sequential_download = true; if (p.flags & add_torrent_params::flag_super_seeding) { m_super_seeding = true; set_need_save_resume(); } set_max_uploads(p.max_uploads, false); set_max_connections(p.max_connections, false); set_limit_impl(p.upload_limit, peer_connection::upload_channel, false); set_limit_impl(p.download_limit, peer_connection::download_channel, false); if (!m_name && !m_url.empty()) m_name.reset(new std::string(m_url)); #ifndef TORRENT_NO_DEPRECATE if (p.tracker_url && std::strlen(p.tracker_url) > 0) { m_trackers.push_back(announce_entry(p.tracker_url)); m_trackers.back().fail_limit = 0; m_trackers.back().source = announce_entry::source_magnet_link; m_torrent_file->add_tracker(p.tracker_url); } #endif int tier = 0; for (std::vector::const_iterator i = p.trackers.begin() , end(p.trackers.end()); i != end; ++i) { m_trackers.push_back(announce_entry(*i)); m_trackers.back().fail_limit = 0; m_trackers.back().source = announce_entry::source_magnet_link; m_trackers.back().tier = tier++; m_torrent_file->add_tracker(*i); } if (settings().get_bool(settings_pack::prefer_udp_trackers)) prioritize_udp_trackers(); // if we don't have metadata, make this torrent pinned. The // client may unpin it once we have metadata and it has had // a chance to save it on the metadata_received_alert if (!valid_metadata()) { if (!m_pinned && m_refcount == 0) inc_stats_counter(counters::num_pinned_torrents); m_pinned = true; } else { inc_stats_counter(counters::num_total_pieces_added , m_torrent_file->num_pieces()); } update_gauge(); if (m_resume_data) { int pos; error_code ec; if (bdecode(&m_resume_data->buf[0], &m_resume_data->buf[0] + m_resume_data->buf.size(), m_resume_data->node, ec, &pos) != 0) { m_resume_data.reset(); #ifndef TORRENT_DISABLE_LOGGING debug_log("resume data rejected: %s pos: %d", ec.message().c_str(), pos); #endif if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle() , ec, "", static_cast(0)); } } update_want_peers(); update_want_scrape(); update_want_tick(); update_state_list(); if (!m_torrent_file->is_valid() && !m_url.empty()) { // we need to download the .torrent file from m_url start_download_url(); } else if (m_torrent_file->is_valid()) { init(); } else { // we need to start announcing since we don't have any // metadata. To receive peers to ask for it. set_state(torrent_status::downloading_metadata); start_announcing(); } #if TORRENT_USE_INVARIANT_CHECKS check_invariant(); #endif } void torrent::start_download_url() { TORRENT_ASSERT(!m_url.empty()); TORRENT_ASSERT(!m_torrent_file->is_valid()); boost::shared_ptr conn( new http_connection(m_ses.get_io_service() , m_ses.get_resolver() , boost::bind(&torrent::on_torrent_download, shared_from_this() , _1, _2, _3, _4) , true // bottled //bottled buffer size , settings().get_int(settings_pack::max_http_recv_buffer_size) , http_connect_handler() , http_filter_handler() #ifdef TORRENT_USE_OPENSSL , m_ssl_ctx.get() #endif )); aux::proxy_settings ps = m_ses.proxy(); conn->get(m_url, seconds(30), 0, &ps , 5 , settings().get_bool(settings_pack::anonymous_mode) ? "" : settings().get_str(settings_pack::user_agent)); set_state(torrent_status::downloading_metadata); } void torrent::set_apply_ip_filter(bool b) { if (b == m_apply_ip_filter) return; if (b) { inc_stats_counter(counters::non_filter_torrents, -1); } else { inc_stats_counter(counters::non_filter_torrents); } m_apply_ip_filter = b; ip_filter_updated(); state_updated(); } void torrent::set_ip_filter(boost::shared_ptr ipf) { m_ip_filter = ipf; if (!m_apply_ip_filter) return; ip_filter_updated(); } #ifndef TORRENT_DISABLE_DHT bool torrent::should_announce_dht() const { TORRENT_ASSERT(is_single_thread()); if (!m_ses.announce_dht()) return false; if (!m_ses.dht()) return false; if (m_torrent_file->is_valid() && !m_files_checked) return false; if (!m_announce_to_dht) return false; if (!m_allow_peers) return false; // if we don't have the metadata, and we're waiting // for a web server to serve it to us, no need to announce // because the info-hash is just the URL hash if (!m_torrent_file->is_valid() && !m_url.empty()) return false; // don't announce private torrents if (m_torrent_file->is_valid() && m_torrent_file->priv()) return false; if (m_trackers.empty()) return true; if (!settings().get_bool(settings_pack::use_dht_as_fallback)) return true; int verified_trackers = 0; for (std::vector::const_iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) if (i->verified) ++verified_trackers; return verified_trackers == 0; } #endif torrent::~torrent() { TORRENT_ASSERT(m_abort); TORRENT_ASSERT(prev == NULL && next == NULL); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS for (int i = 0; i < aux::session_interface::num_torrent_lists; ++i) { if (!m_links[i].in_list()) continue; m_links[i].unlink(m_ses.torrent_list(i), i); } #endif TORRENT_ASSERT(m_refcount == 0); if (m_pinned) inc_stats_counter(counters::num_pinned_torrents, -1); if (is_loaded()) inc_stats_counter(counters::num_loaded_torrents, -1); // 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. // however, the torrent object may be destructed from the main // thread when shutting down, if the disk cache has references to it. // this means that the invariant check that this is called from the // network thread cannot be maintained TORRENT_ASSERT(m_peer_class == 0); TORRENT_ASSERT(m_abort); TORRENT_ASSERT(m_connections.empty()); if (!m_connections.empty()) disconnect_all(errors::torrent_aborted, op_bittorrent); } void torrent::read_piece(int piece) { if (m_abort || m_deleted) { // failed m_ses.alerts().emplace_alert( get_handle(), piece, error_code(boost::system::errc::operation_canceled, generic_category())); return; } TORRENT_ASSERT(piece >= 0 && piece < m_torrent_file->num_pieces()); const int piece_size = m_torrent_file->piece_size(piece); const int blocks_in_piece = (piece_size + block_size() - 1) / block_size(); TORRENT_ASSERT(blocks_in_piece > 0); TORRENT_ASSERT(piece_size > 0); if (blocks_in_piece == 0) { // this shouldn't actually happen boost::shared_array buf; m_ses.alerts().emplace_alert( get_handle(), piece, buf, 0); return; } boost::shared_ptr rp = boost::make_shared(); rp->piece_data.reset(new (std::nothrow) char[piece_size]); if (!rp->piece_data) { m_ses.alerts().emplace_alert( get_handle(), piece, error_code(boost::system::errc::not_enough_memory, generic_category())); return; } rp->blocks_left = 0; rp->fail = false; peer_request r; r.piece = piece; r.start = 0; rp->blocks_left = blocks_in_piece; if (!need_loaded()) { rp->piece_data.reset(); m_ses.alerts().emplace_alert( get_handle(), r.piece, rp->piece_data, 0); return; } m_ses.deferred_submit_jobs(); for (int i = 0; i < blocks_in_piece; ++i, r.start += block_size()) { r.length = (std::min)(piece_size - r.start, block_size()); inc_refcount("read_piece"); m_ses.disk_thread().async_read(&storage(), r , boost::bind(&torrent::on_disk_read_complete , shared_from_this(), _1, r, rp), reinterpret_cast(1)); } } void torrent::send_share_mode() { #ifndef TORRENT_DISABLE_EXTENSIONS for (peer_iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { TORRENT_INCREMENT(m_iterating_connections); if ((*i)->type() != peer_connection::bittorrent_connection) continue; bt_peer_connection* p = static_cast(*i); p->write_share_mode(); } #endif } void torrent::send_upload_only() { #ifndef TORRENT_DISABLE_EXTENSIONS if (share_mode()) return; if (super_seeding()) return; // sending "not_interested" to a peer may cause us to disconnect it. Don't // do that while looping over the peer list. std::vector defer; for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { TORRENT_INCREMENT(m_iterating_connections); // since the call to disconnect_if_redundant() may // delete the entry from this container, make sure // to increment the iterator early peer_connection* p = *i; if (p->type() == peer_connection::bittorrent_connection) { bt_peer_connection* btp = static_cast(*i); if (!btp->is_disconnecting()) defer.push_back(btp); } } for (std::vector::iterator i = defer.begin() , end(defer.end()); i != end; ++i) { bt_peer_connection* btp = *i; boost::shared_ptr me(btp->self()); btp->send_not_interested(); if (!btp->is_disconnecting()) btp->write_upload_only(); } #endif } void torrent::set_share_mode(bool s) { if (s == m_share_mode) return; m_share_mode = s; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** set-share-mode: %d", s); #endif if (m_share_mode) { int const num_files = valid_metadata() ? m_torrent_file->num_files() : m_file_priority.size(); // in share mode, all pieces have their priorities initialized to 0 prioritize_files(std::vector(num_files)); } } void torrent::set_upload_mode(bool b) { if (b == m_upload_mode) return; m_upload_mode = b; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** set-upload-mode: %d", b); #endif update_gauge(); state_updated(); send_upload_only(); if (m_upload_mode) { // clear request queues of all peers for (peer_iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = (*i); // we may want to disconnect other upload-only peers if (p->upload_only()) p->update_interest(); p->cancel_all_requests(); } // this is used to try leaving upload only mode periodically m_upload_mode_time = m_ses.session_time(); } else if (m_peer_list) { // reset last_connected, to force fast reconnect after leaving upload mode for (peer_list::iterator i = m_peer_list->begin_peer() , end(m_peer_list->end_peer()); i != end; ++i) { (*i)->last_connected = 0; } // send_block_requests on all peers for (peer_iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = (*i); // we may be interested now, or no longer interested p->update_interest(); p->send_block_requests(); } } } void torrent::need_peer_list() { if (m_peer_list) return; m_peer_list.reset(new peer_list(m_ses.get_peer_allocator())); } void torrent::handle_disk_error(disk_io_job const* j, peer_connection* c) { TORRENT_ASSERT(is_single_thread()); if (!j->error) return; #ifndef TORRENT_DISABLE_LOGGING debug_log("disk error: (%d) %s in file: %s", j->error.ec.value(), j->error.ec.message().c_str() , resolve_filename(j->error.file).c_str()); #endif if (j->action == disk_io_job::write) { piece_block block_finished(j->piece, j->d.io.offset / block_size()); // we failed to write j->piece to disk tell the piece picker // this will block any other peer from issuing requests // to this piece, until we've cleared it. if (j->error.ec == boost::asio::error::operation_aborted) { if (has_picker()) picker().mark_as_canceled(block_finished, NULL); } else { // if any other peer has a busy request to this block, we need // to cancel it too cancel_block(block_finished); if (has_picker()) picker().write_failed(block_finished); if (m_storage) { // when this returns, all outstanding jobs to the // piece are done, and we can restore it, allowing // new requests to it m_ses.disk_thread().async_clear_piece(m_storage.get(), j->piece , boost::bind(&torrent::on_piece_fail_sync, shared_from_this(), _1, block_finished)); } else { // is m_abort true? if so, we should probably just // exit this function early, no need to keep the picker // state up-to-date, right? disk_io_job sj; sj.piece = j->piece; on_piece_fail_sync(&sj, block_finished); } } update_gauge(); } if (j->error.ec == boost::system::errc::not_enough_memory) { if (alerts().should_post()) alerts().emplace_alert(j->error.ec , resolve_filename(j->error.file), j->error.operation_str(), get_handle()); if (c) c->disconnect(errors::no_memory, op_file); return; } if (j->error.ec == boost::asio::error::operation_aborted) return; // notify the user of the error if (alerts().should_post()) alerts().emplace_alert(j->error.ec , resolve_filename(j->error.file), j->error.operation_str(), get_handle()); // if a write operation failed, and future writes are likely to // fail, while reads may succeed, just set the torrent to upload mode // if we make an incorrect assumption here, it's not the end of the // world, if we ever issue a read request and it fails as well, we // won't get in here and we'll actually end up pausing the torrent if (j->action == disk_io_job::write && (j->error.ec == boost::system::errc::read_only_file_system || j->error.ec == boost::system::errc::permission_denied || j->error.ec == boost::system::errc::operation_not_permitted || j->error.ec == boost::system::errc::no_space_on_device || j->error.ec == boost::system::errc::file_too_large)) { // if we failed to write, stop downloading and just // keep seeding. // TODO: 1 make this depend on the error and on the filesystem the // files are being downloaded to. If the error is no_space_left_on_device // and the filesystem doesn't support sparse files, only zero the priorities // of the pieces that are at the tails of all files, leaving everything // up to the highest written piece in each file set_upload_mode(true); return; } // put the torrent in an error-state set_error(j->error.ec, j->error.file); // if the error appears to be more serious than a full disk, just pause the torrent pause(); } void torrent::on_piece_fail_sync(disk_io_job const* j, piece_block b) { TORRENT_UNUSED(j); TORRENT_UNUSED(b); if (m_abort) return; update_gauge(); // some peers that previously was no longer interesting may // now have become interesting, since we lack this one piece now. for (peer_iterator i = begin(); i != end();) { peer_connection* p = *i; // update_interest may disconnect the peer and // invalidate the iterator ++i; // no need to do anything with peers that // already are interested. Gaining a piece may // only make uninteresting peers interesting again. if (p->is_interesting()) continue; p->update_interest(); if (!m_abort) { if (request_a_block(*this, *p)) inc_stats_counter(counters::hash_fail_piece_picks); p->send_block_requests(); } } } void torrent::on_disk_read_complete(disk_io_job const* j, peer_request r , boost::shared_ptr rp) { // hold a reference until this function returns torrent_ref_holder h(this, "read_piece"); dec_refcount("read_piece"); TORRENT_ASSERT(is_single_thread()); disk_buffer_holder buffer(m_ses, *j); --rp->blocks_left; if (j->ret != r.length) { rp->fail = true; rp->error = j->error.ec; handle_disk_error(j); } else { std::memcpy(rp->piece_data.get() + r.start, j->buffer.disk_block, r.length); } if (rp->blocks_left == 0) { int size = m_torrent_file->piece_size(r.piece); if (rp->fail) { m_ses.alerts().emplace_alert( get_handle(), r.piece, rp->error); } else { m_ses.alerts().emplace_alert( get_handle(), r.piece, rp->piece_data, size); } } } storage_mode_t torrent::storage_mode() const { return storage_mode_t(m_storage_mode); } storage_interface* torrent::get_storage() { if (!m_storage) return 0; return m_storage->get_storage_impl(); } void torrent::need_picker() { if (m_picker) return; TORRENT_ASSERT(valid_metadata()); TORRENT_ASSERT(m_connections_initialized); INVARIANT_CHECK; // if we have all pieces we should not have a picker TORRENT_ASSERT(!m_have_all); m_picker.reset(new piece_picker()); int blocks_per_piece = (m_torrent_file->piece_length() + block_size() - 1) / block_size(); int blocks_in_last_piece = ((m_torrent_file->total_size() % m_torrent_file->piece_length()) + block_size() - 1) / block_size(); m_picker->init(blocks_per_piece, blocks_in_last_piece, m_torrent_file->num_pieces()); // initialize the file progress too if (m_file_progress.empty()) { TORRENT_ASSERT(has_picker()); if (!need_loaded()) return; m_file_progress.init(picker(), m_torrent_file->files()); } update_gauge(); for (peer_iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_has((*i)->get_bitfield(), *i); } } void torrent::add_piece(int piece, char const* data, int flags) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(piece >= 0 && piece < m_torrent_file->num_pieces()); int piece_size = m_torrent_file->piece_size(piece); int blocks_in_piece = (piece_size + block_size() - 1) / block_size(); if (m_deleted) return; // avoid crash trying to access the picker when there is none if (m_have_all && !has_picker()) return; need_picker(); if (picker().have_piece(piece) && (flags & torrent::overwrite_existing) == 0) return; peer_request p; p.piece = piece; p.start = 0; picker().inc_refcount(piece, 0); for (int i = 0; i < blocks_in_piece; ++i, p.start += block_size()) { if (picker().is_finished(piece_block(piece, i)) && (flags & torrent::overwrite_existing) == 0) continue; p.length = (std::min)(piece_size - p.start, int(block_size())); char* buffer = m_ses.allocate_disk_buffer("add piece"); // out of memory if (buffer == 0) { picker().dec_refcount(piece, 0); return; } disk_buffer_holder holder(m_ses, buffer); std::memcpy(buffer, data + p.start, p.length); if (!need_loaded()) { // failed to load .torrent file picker().dec_refcount(piece, 0); return; } inc_refcount("add_piece"); m_ses.disk_thread().async_write(&storage(), p, holder , boost::bind(&torrent::on_disk_write_complete , shared_from_this(), _1, p)); piece_block block(piece, i); picker().mark_as_downloading(block, 0); picker().mark_as_writing(block, 0); } verify_piece(piece); picker().dec_refcount(piece, 0); } void torrent::schedule_storage_tick() { // schedule a disk tick in 2 minutes or so if (m_storage_tick != 0) return; m_storage_tick = 120 + (random() % 60); update_want_tick(); } void torrent::on_disk_write_complete(disk_io_job const* j , peer_request p) { // hold a reference until this function returns torrent_ref_holder h(this, "add_piece"); dec_refcount("add_piece"); TORRENT_ASSERT(is_single_thread()); schedule_storage_tick(); // fprintf(stderr, "torrent::on_disk_write_complete ret:%d piece:%d block:%d\n" // , j->ret, j->piece, j->offset/0x4000); INVARIANT_CHECK; if (m_abort) { return; } piece_block block_finished(p.piece, p.start / block_size()); if (j->ret == -1) { handle_disk_error(j); return; } if (!has_picker()) return; // if we already have this block, just ignore it. // this can happen if the same block is passed in through // add_piece() multiple times if (picker().is_finished(block_finished)) return; picker().mark_as_finished(block_finished, 0); maybe_done_flushing(); } void torrent::on_disk_cache_complete(disk_io_job const* j) { TORRENT_ASSERT(have_piece(j->piece)); dec_refcount("cache_piece"); if (j->ret < 0) return; // suggest this piece to all peers for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { TORRENT_INCREMENT(m_iterating_connections); (*i)->send_suggest(j->piece); } } void torrent::on_disk_tick_done(disk_io_job const* j) { if (j->ret && m_storage_tick == 0) { m_storage_tick = 120 + (random() % 20); update_want_tick(); } } bool torrent::add_merkle_nodes(std::map const& nodes, int piece) { return m_torrent_file->add_merkle_nodes(nodes, piece); } peer_request torrent::to_req(piece_block const& p) const { int block_offset = p.block_index * block_size(); int block = (std::min)(torrent_file().piece_size( p.piece_index) - block_offset, int(block_size())); TORRENT_ASSERT(block > 0); TORRENT_ASSERT(block <= block_size()); peer_request r; r.piece = p.piece_index; r.start = block_offset; r.length = block; return r; } std::string torrent::name() const { if (valid_metadata()) return m_torrent_file->name(); if (m_name) return *m_name; return ""; } #ifndef TORRENT_DISABLE_EXTENSIONS void torrent::add_extension(boost::shared_ptr ext) { m_extensions.push_back(ext); } void torrent::remove_extension(boost::shared_ptr ext) { extension_list_t::iterator i = std::find(m_extensions.begin(), m_extensions.end(), ext); if (i == m_extensions.end()) return; m_extensions.erase(i); } void torrent::add_extension(boost::function(torrent_handle const&, void*)> const& ext , void* userdata) { boost::shared_ptr tp(ext(get_handle(), userdata)); if (!tp) return; add_extension(tp); for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = *i; boost::shared_ptr pp(tp->new_connection(peer_connection_handle(p->self()))); if (pp) p->add_extension(pp); } // if files are checked for this torrent, call the extension // to let it initialize itself if (m_connections_initialized) tp->on_files_checked(); } #endif #ifdef TORRENT_USE_OPENSSL #if BOOST_VERSION >= 104700 bool torrent::verify_peer_cert(bool preverified, boost::asio::ssl::verify_context& ctx) { // if the cert wasn't signed by the correct CA, fail the verification if (!preverified) return false; // we're only interested in checking the certificate at the end of the chain. // TODO: is verify_peer_cert called once per certificate in the chain, and // this function just tells us which depth we're at right now? If so, the comment // makes sense. // any certificate that isn't the leaf (i.e. the one presented by the peer) // should be accepted automatically, given preverified is true. The leaf certificate // need to be verified to make sure its DN matches the info-hash int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle()); if (depth > 0) return true; X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); // Go through the alternate names in the certificate looking for matching DNS entries GENERAL_NAMES* gens = static_cast( X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0)); #ifndef TORRENT_DISABLE_LOGGING std::string names; bool match = false; #endif for (int i = 0; i < aux::openssl_num_general_names(gens); ++i) { GENERAL_NAME* gen = aux::openssl_general_name_value(gens, i); if (gen->type != GEN_DNS) continue; ASN1_IA5STRING* domain = gen->d.dNSName; if (domain->type != V_ASN1_IA5STRING || !domain->data || !domain->length) continue; const char* torrent_name = reinterpret_cast(domain->data); std::size_t name_length = domain->length; #ifndef TORRENT_DISABLE_LOGGING if (i > 1) names += " | n: "; names.append(torrent_name, name_length); #endif if (strncmp(torrent_name, "*", name_length) == 0 || strncmp(torrent_name, m_torrent_file->name().c_str(), name_length) == 0) { #ifndef TORRENT_DISABLE_LOGGING match = true; // if we're logging, keep looping over all names, // for completeness of the log continue; #else return true; #endif } } // no match in the alternate names, so try the common names. We should only // use the "most specific" common name, which is the last one in the list. X509_NAME* name = X509_get_subject_name(cert); int i = -1; ASN1_STRING* common_name = 0; while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) { X509_NAME_ENTRY* name_entry = X509_NAME_get_entry(name, i); common_name = X509_NAME_ENTRY_get_data(name_entry); } if (common_name && common_name->data && common_name->length) { const char* torrent_name = reinterpret_cast(common_name->data); std::size_t name_length = common_name->length; #ifndef TORRENT_DISABLE_LOGGING if (!names.empty()) names += " | n: "; names.append(torrent_name, name_length); #endif if (strncmp(torrent_name, "*", name_length) == 0 || strncmp(torrent_name, m_torrent_file->name().c_str(), name_length) == 0) { #ifdef TORRENT_DISABLE_LOGGING return true; #else match = true; #endif } } #ifndef TORRENT_DISABLE_LOGGING debug_log("<== incoming SSL CONNECTION [ n: %s | match: %s ]" , names.c_str(), match?"yes":"no"); return match; #else return false; #endif } #endif // BOOST_VERSION void torrent::init_ssl(std::string const& cert) { using boost::asio::ssl::context; // this is needed for openssl < 1.0 to decrypt keys created by openssl 1.0+ #if !defined(OPENSSL_API_COMPAT) || (OPENSSL_API_COMPAT < 0x10100000L) OpenSSL_add_all_algorithms(); #else OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS, nullptr); #endif boost::uint64_t now = clock_type::now().time_since_epoch().count(); // assume 9 bits of entropy (i.e. about 1 millisecond) RAND_add(&now, 8, 1.125); RAND_add(&info_hash()[0], 20, 3); // entropy is also added on incoming and completed connection attempts TORRENT_ASSERT(RAND_status() == 1); #if BOOST_VERSION >= 104700 // create the SSL context for this torrent. We need to // inject the root certificate, and no other, to // verify other peers against boost::shared_ptr ctx = boost::make_shared(context::sslv23); if (!ctx) { error_code ec(::ERR_get_error(), boost::asio::error::get_ssl_category()); set_error(ec, torrent_status::error_file_ssl_ctx); pause(); return; } ctx->set_options(context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); error_code ec; ctx->set_verify_mode(context::verify_peer | context::verify_fail_if_no_peer_cert | context::verify_client_once, ec); if (ec) { set_error(ec, torrent_status::error_file_ssl_ctx); pause(); return; } // the verification function verifies the distinguished name // of a peer certificate to make sure it matches the info-hash // of the torrent, or that it's a "star-cert" ctx->set_verify_callback(boost::bind(&torrent::verify_peer_cert, this, _1, _2), ec); if (ec) { set_error(ec, torrent_status::error_file_ssl_ctx); pause(); return; } SSL_CTX* ssl_ctx = ctx->native_handle(); // create a new x.509 certificate store X509_STORE* cert_store = X509_STORE_new(); if (!cert_store) { ec.assign(::ERR_get_error(), boost::asio::error::get_ssl_category()); set_error(ec, torrent_status::error_file_ssl_ctx); pause(); return; } // wrap the PEM certificate in a BIO, for openssl to read BIO* bp = BIO_new_mem_buf( const_cast(static_cast(cert.c_str())) , cert.size()); // parse the certificate into OpenSSL's internal // representation X509* certificate = PEM_read_bio_X509_AUX(bp, 0, 0, 0); BIO_free(bp); if (!certificate) { ec.assign(::ERR_get_error(), boost::asio::error::get_ssl_category()); X509_STORE_free(cert_store); set_error(ec, torrent_status::error_file_ssl_ctx); pause(); return; } // add cert to cert_store X509_STORE_add_cert(cert_store, certificate); X509_free(certificate); // and lastly, replace the default cert store with ours SSL_CTX_set_cert_store(ssl_ctx, cert_store); #if 0 char filename[100]; snprintf(filename, sizeof(filename), "/tmp/%u.pem", random()); FILE* f = fopen(filename, "w+"); fwrite(cert.c_str(), cert.size(), 1, f); fclose(f); ctx->load_verify_file(filename); #endif // if all went well, set the torrent ssl context to this one m_ssl_ctx = ctx; // tell the client we need a cert for this torrent alerts().emplace_alert(get_handle()); #else set_error(boost::asio::error::operation_not_supported, torrent_status::error_file_ssl_ctx); pause(); #endif } #endif // TORRENT_OPENSSL void torrent::construct_storage() { storage_params params; if (&m_torrent_file->orig_files() != &m_torrent_file->files()) { params.mapped_files = &m_torrent_file->files(); params.files = &m_torrent_file->orig_files(); } else { params.files = &m_torrent_file->files(); params.mapped_files = 0; } params.path = m_save_path; params.pool = &m_ses.disk_thread().files(); params.mode = static_cast(m_storage_mode); params.priorities = &m_file_priority; params.info = m_torrent_file.get(); TORRENT_ASSERT(m_storage_constructor); storage_interface* storage_impl = m_storage_constructor(params); // the shared_from_this() will create an intentional // cycle of ownership, se the hpp file for description. m_storage = boost::make_shared( storage_impl, shared_from_this() , const_cast(&m_torrent_file->files())); } peer_connection* torrent::find_lowest_ranking_peer() const { const_peer_iterator lowest_rank = end(); for (const_peer_iterator i = begin(); i != end(); ++i) { // disconnecting peers don't count if ((*i)->is_disconnecting()) continue; if (lowest_rank == end() || (*lowest_rank)->peer_rank() > (*i)->peer_rank()) lowest_rank = i; } if (lowest_rank == end()) return NULL; return *lowest_rank; } // this may not be called from a constructor because of the call to // shared_from_this() void torrent::init() { INVARIANT_CHECK; TORRENT_ASSERT(is_single_thread()); #ifndef TORRENT_DISABLE_LOGGING debug_log("init torrent: %s", torrent_file().name().c_str()); #endif if (!need_loaded()) return; TORRENT_ASSERT(valid_metadata()); TORRENT_ASSERT(m_torrent_file->num_files() > 0); TORRENT_ASSERT(m_torrent_file->total_size() >= 0); if (int(m_file_priority.size()) > m_torrent_file->num_files()) m_file_priority.resize(m_torrent_file->num_files()); std::string cert = m_torrent_file->ssl_cert(); if (!cert.empty()) { m_ssl_torrent = true; #ifdef TORRENT_USE_OPENSSL init_ssl(cert); #endif } m_block_size_shift = root2((std::min)(block_size(), m_torrent_file->piece_length())); if (m_torrent_file->num_pieces() > piece_picker::max_pieces) { set_error(errors::too_many_pieces_in_torrent, torrent_status::error_file_none); pause(); return; } if (m_torrent_file->num_pieces() == 0) { set_error(errors::torrent_invalid_length, torrent_status::error_file_none); pause(); return; } if (m_resume_data && m_resume_data->node.type() == bdecode_node::dict_t) { errors::error_code_enum ev = errors::no_error; if (m_resume_data->node.dict_find_string_value("file-format") != "libtorrent resume file") { ev = errors::invalid_file_tag; } std::string info_hash = m_resume_data->node.dict_find_string_value("info-hash"); if (!ev && info_hash.empty()) ev = errors::missing_info_hash; if (!ev && sha1_hash(info_hash) != m_torrent_file->info_hash()) ev = errors::mismatching_info_hash; if (ev && m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle() , error_code(ev), "", static_cast(0)); } if (ev) { #ifndef TORRENT_DISABLE_LOGGING debug_log("fastresume data rejected: %s" , error_code(ev).message().c_str()); #endif m_resume_data.reset(); } else { read_resume_data(m_resume_data->node); } } #if TORRENT_USE_ASSERTS m_resume_data_loaded = true; #endif construct_storage(); if (m_share_mode && valid_metadata()) { // in share mode, all pieces have their priorities initialized to 0 m_file_priority.clear(); m_file_priority.resize(m_torrent_file->num_files(), 0); } // it's important to initialize the peers early, because this is what will // fix up their have-bitmasks to have the correct size // TODO: 2 add a unit test where we don't have metadata, connect to a peer // that sends a bitfield that's too large, then we get the metadata if (!m_connections_initialized) { m_connections_initialized = true; // all peer connections have to initialize themselves now that the metadata // is available // copy the peer list since peers may disconnect and invalidate // m_connections as we initialize them std::vector peers = m_connections; for (torrent::peer_iterator i = peers.begin(); i != peers.end(); ++i) { peer_connection* pc = *i; if (pc->is_disconnecting()) continue; pc->on_metadata_impl(); if (pc->is_disconnecting()) continue; pc->init(); } } // in case file priorities were passed in via the add_torrent_params // and also in the case of share mode, we need to update the priorities // this has to be applied before piece priority if (!m_file_priority.empty()) update_piece_priorities(m_file_priority); if (!m_seed_mode && m_resume_data) { bdecode_node piece_priority = m_resume_data->node .dict_find_string("piece_priority"); if (piece_priority && piece_priority.string_length() == m_torrent_file->num_pieces()) { char const* p = piece_priority.string_ptr(); for (int i = 0; i < piece_priority.string_length(); ++i) { int prio = p[i]; if (!has_picker() && prio == 4) continue; need_picker(); m_picker->set_piece_priority(i, p[i]); } update_gauge(); } } #if TORRENT_USE_ASSERTS m_resume_data_loaded = true; #endif if (m_seed_mode) { m_have_all = true; update_gauge(); update_state_list(); } else { need_picker(); int num_pad_files = 0; TORRENT_ASSERT(block_size() > 0); file_storage const& fs = m_torrent_file->files(); for (int i = 0; i < fs.num_files(); ++i) { if (!fs.pad_file_at(i) || fs.file_size(i) == 0) continue; if (fs.pad_file_at(i)) ++num_pad_files; m_padding += boost::uint32_t(fs.file_size(i)); peer_request pr = m_torrent_file->map_file(i, 0, fs.file_size(i)); int off = pr.start & (block_size()-1); if (off != 0) { pr.length -= block_size() - off; pr.start += block_size() - off; } TORRENT_ASSERT((pr.start & (block_size()-1)) == 0); int block = block_size(); int blocks_per_piece = m_torrent_file->piece_length() / block; piece_block pb(pr.piece, pr.start / block); for (; pr.length >= block; pr.length -= block, ++pb.block_index) { if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; } m_picker->mark_as_pad(pb); } // ugly edge case where padfiles are not used they way they're // supposed to be. i.e. added back-to back or at the end if (pb.block_index == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; } if (pr.length > 0 && ((i+1 != fs.num_files() && fs.pad_file_at(i+1)) || i + 1 == fs.num_files())) { m_picker->mark_as_finished(pb, 0); } } if (m_padding > 0) { // if we marked an entire piece as finished, we actually // need to consider it finished std::vector dq = m_picker->get_download_queue(); std::vector have_pieces; for (std::vector::const_iterator i = dq.begin(); i != dq.end(); ++i) { int num_blocks = m_picker->blocks_in_piece(i->index); if (i->finished < num_blocks) continue; have_pieces.push_back(i->index); } for (std::vector::iterator i = have_pieces.begin(); i != have_pieces.end(); ++i) { picker().piece_passed(*i); TORRENT_ASSERT(picker().have_piece(*i)); we_have(*i); } } if (!need_loaded()) return; if (num_pad_files > 0) m_picker->set_num_pad_files(num_pad_files); } set_state(torrent_status::checking_resume_data); std::vector links; #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS if (!m_torrent_file->similar_torrents().empty() || !m_torrent_file->collections().empty()) { resolve_links res(m_torrent_file); std::vector s = m_torrent_file->similar_torrents(); for (std::vector::iterator i = s.begin(), end(s.end()); i != end; ++i) { boost::shared_ptr t = m_ses.find_torrent(*i).lock(); if (!t) continue; // Only attempt to reuse files from torrents that are seeding. // TODO: this could be optimized by looking up which files are // complete and just look at those if (!t->is_seed()) continue; res.match(t->get_torrent_copy(), t->save_path()); } std::vector c = m_torrent_file->collections(); for (std::vector::iterator i = c.begin(), end(c.end()); i != end; ++i) { std::vector > ts = m_ses.find_collection(*i); for (std::vector >::iterator k = ts.begin() , end2(ts.end()); k != end2; ++k) { // Only attempt to reuse files from torrents that are seeding. // TODO: this could be optimized by looking up which files are // complete and just look at those if (!(*k)->is_seed()) continue; res.match((*k)->get_torrent_copy(), (*k)->save_path()); } } std::vector const& l = res.get_links(); if (!l.empty()) { for (std::vector::const_iterator i = l.begin() , end(l.end()); i != end; ++i) { if (!i->ti) continue; torrent_info const& ti = *i->ti; std::string const& save_path = i->save_path; links.push_back(combine_path(save_path , ti.files().file_path(i->file_idx))); } } } #endif // TORRENT_DISABLE_MUTABLE_TORRENTS inc_refcount("check_fastresume"); // async_check_fastresume will gut links m_ses.disk_thread().async_check_fastresume( m_storage.get(), m_resume_data ? &m_resume_data->node : NULL , links, boost::bind(&torrent::on_resume_data_checked , shared_from_this(), _1)); #ifndef TORRENT_DISABLE_LOGGING debug_log("init, async_check_fastresume"); #endif update_want_peers(); // this will remove the piece picker, if we're done with it maybe_done_flushing(); } bool torrent::need_loaded() { m_should_be_loaded = true; // if we don't have the metadata yet, pretend the file is loaded if (!m_torrent_file->is_valid() || m_torrent_file->is_loaded()) { // bump this torrent to the top of the torrent LRU of // which torrents are most active m_ses.bump_torrent(this); return true; } // load the specified torrent and also evict one torrent, // except for the one specified. if we're not at our limit // yet, no torrent is evicted return m_ses.load_torrent(this); } void torrent::dec_refcount(char const* purpose) { TORRENT_UNUSED(purpose); TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_refcount > 0); --m_refcount; if (m_refcount == 0) { if (!m_pinned) inc_stats_counter(counters::num_pinned_torrents, -1); if (m_should_be_loaded == false) unload(); } } void torrent::inc_refcount(char const* purpose) { TORRENT_UNUSED(purpose); TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(is_loaded()); ++m_refcount; if (!m_pinned && m_refcount == 1) inc_stats_counter(counters::num_pinned_torrents); } void torrent::set_pinned(bool p) { TORRENT_ASSERT(is_single_thread()); if (m_pinned == p) return; m_pinned = p; if (m_refcount == 0) inc_stats_counter(counters::num_pinned_torrents, p ? 1 : -1); // if the torrent was just unpinned, we need to insert // it into the LRU m_ses.bump_torrent(this, true); } bool torrent::load(std::vector& buffer) { error_code ec; m_torrent_file->load(&buffer[0], buffer.size(), ec); if (ec) { set_error(ec, torrent_status::error_file_metadata); return false; } state_updated(); /* #ifndef TORRENT_DISABLE_EXTENSIONS // create the extensions again // TOOD: should we store add_torrent_params::userdata // in torrent just to have it available here? m_ses.add_extensions_to_torrent(shared_from_this(), NULL); // and call on_load() on them for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->on_load(); } TORRENT_CATCH (std::exception&) {} } #endif */ inc_stats_counter(counters::num_loaded_torrents); construct_storage(); return true; } // this is called when this torrent hasn't been active in long enough // to warrant swapping it out, in favor of a more active torrent. void torrent::unload() { TORRENT_ASSERT(is_loaded()); // pinned torrents are not allowed to be swapped out TORRENT_ASSERT(!m_pinned); m_should_be_loaded = false; // make sure it's not unloaded in the middle of some operation that uses it if (m_refcount > 0) return; // call on_unload() on extensions #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->on_unload(); } TORRENT_CATCH (std::exception&) {} } // also remove extensions and re-instantiate them when the torrent is loaded again // they end up using a significant amount of memory // TODO: there may be peer extensions relying on the torrent extension // still being alive. Only do this if there are no peers. And when the last peer // is disconnected, if the torrent is unloaded, clear the extensions // m_extensions.clear(); #endif // someone else holds a reference to the torrent_info // make the torrent release its reference to it, // after making a copy and then unloading that version // as soon as the user is done with its copy of torrent_info // it will be freed, and we'll have the unloaded version left if (!m_torrent_file.unique()) m_torrent_file = boost::make_shared(*m_torrent_file); m_torrent_file->unload(); inc_stats_counter(counters::num_loaded_torrents, -1); m_storage.reset(); state_updated(); } bt_peer_connection* torrent::find_introducer(tcp::endpoint const& ep) const { #ifndef TORRENT_DISABLE_EXTENSIONS for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { TORRENT_INCREMENT(m_iterating_connections); if ((*i)->type() != peer_connection::bittorrent_connection) continue; bt_peer_connection* p = static_cast(*i); if (!p->supports_holepunch()) continue; peer_plugin const* pp = p->find_plugin("ut_pex"); if (!pp) continue; if (was_introduced_by(pp, ep)) return p; } #else TORRENT_UNUSED(ep); #endif return NULL; } bt_peer_connection* torrent::find_peer(tcp::endpoint const& ep) const { for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = *i; if (p->type() != peer_connection::bittorrent_connection) continue; if (p->remote() == ep) return static_cast(p); } return NULL; } peer_connection* torrent::find_peer(peer_id const& pid) { for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = *i; if (p->pid() == pid) return p; } return 0; } void torrent::on_resume_data_checked(disk_io_job const* j) { // hold a reference until this function returns torrent_ref_holder h(this, "check_fastresume"); // when applying some of the resume data to the torrent, we will // trigger calls that set m_need_save_resume_data, even though we're // just applying the state of the resume data we loaded with. We don't // want anything in this function to affect the state of // m_need_save_resume_data, so we save it in a local variable and reset // it at the end of the function. bool const need_save_resume_data = m_need_save_resume_data; dec_refcount("check_fastresume"); TORRENT_ASSERT(is_single_thread()); if (j->ret == piece_manager::fatal_disk_error) { m_resume_data.reset(); handle_disk_error(j); auto_managed(false); pause(); set_state(torrent_status::checking_files); if (should_check_files()) start_checking(); return; } if (m_abort) return; state_updated(); if (m_resume_data && m_resume_data->node.type() == bdecode_node::dict_t) { using namespace libtorrent::detail; // for read_*_endpoint() if (bdecode_node peers_entry = m_resume_data->node.dict_find_string("peers")) { int num_peers = peers_entry.string_length() / (sizeof(address_v4::bytes_type) + 2); char const* ptr = peers_entry.string_ptr(); for (int i = 0; i < num_peers; ++i) { add_peer(read_v4_endpoint(ptr) , peer_info::resume_data); } update_want_peers(); } if (bdecode_node banned_peers_entry = m_resume_data->node.dict_find_string("banned_peers")) { int num_peers = banned_peers_entry.string_length() / (sizeof(address_v4::bytes_type) + 2); char const* ptr = banned_peers_entry.string_ptr(); for (int i = 0; i < num_peers; ++i) { std::vector peers; torrent_peer* p = add_peer(read_v4_endpoint(ptr) , peer_info::resume_data); peers_erased(peers); if (p) ban_peer(p); } update_want_peers(); } #if TORRENT_USE_IPV6 if (bdecode_node peers6_entry = m_resume_data->node.dict_find_string("peers6")) { int num_peers = peers6_entry.string_length() / (sizeof(address_v6::bytes_type) + 2); char const* ptr = peers6_entry.string_ptr(); for (int i = 0; i < num_peers; ++i) { add_peer(read_v6_endpoint(ptr) , peer_info::resume_data); } update_want_peers(); } if (bdecode_node banned_peers6_entry = m_resume_data->node.dict_find_string("banned_peers6")) { int num_peers = banned_peers6_entry.string_length() / (sizeof(address_v6::bytes_type) + 2); char const* ptr = banned_peers6_entry.string_ptr(); for (int i = 0; i < num_peers; ++i) { torrent_peer* p = add_peer(read_v6_endpoint(ptr) , peer_info::resume_data); if (p) ban_peer(p); } update_want_peers(); } #endif // parse out "peers" from the resume data and add them to the peer list if (bdecode_node peers_entry = m_resume_data->node.dict_find_list("peers")) { for (int i = 0; i < peers_entry.list_size(); ++i) { bdecode_node e = peers_entry.list_at(i); if (e.type() != bdecode_node::dict_t) continue; std::string ip = e.dict_find_string_value("ip"); int port = e.dict_find_int_value("port"); if (ip.empty() || port == 0) continue; error_code ec; tcp::endpoint a(address::from_string(ip, ec), boost::uint16_t(port)); if (ec) continue; add_peer(a, peer_info::resume_data); } update_want_peers(); } // parse out "banned_peers" and add them as banned if (bdecode_node banned_peers_entry = m_resume_data->node.dict_find_list("banned_peers")) { for (int i = 0; i < banned_peers_entry.list_size(); ++i) { bdecode_node e = banned_peers_entry.list_at(i); if (e.type() != bdecode_node::dict_t) continue; std::string ip = e.dict_find_string_value("ip"); int port = e.dict_find_int_value("port"); if (ip.empty() || port == 0) continue; error_code ec; tcp::endpoint a(address::from_string(ip, ec) , boost::uint16_t(port)); if (ec) continue; torrent_peer* p = add_peer(a, peer_info::resume_data); if (p) ban_peer(p); } update_want_peers(); } } #ifndef TORRENT_DISABLE_LOGGING if (m_peer_list && m_peer_list->num_peers() > 0) debug_log("resume added peers (%d)", m_peer_list->num_peers()); #endif // only report this error if the user actually provided resume data if ((j->error || j->ret != 0) && m_resume_data && m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle(), j->error.ec , resolve_filename(j->error.file), j->error.operation_str()); } #ifndef TORRENT_DISABLE_LOGGING if (j->ret != 0) { debug_log("fastresume data rejected: ret: %d (%d) %s" , j->ret, j->error.ec.value(), j->error.ec.message().c_str()); } #ifndef TORRENT_DISABLE_LOGGING else debug_log("fastresume data accepted"); #endif #endif // if ret != 0, it means we need a full check. We don't necessarily need // that when the resume data check fails. For instance, if the resume data // is incorrect, but we don't have any files, we skip the check and initialize // the storage to not have anything. if (m_seed_mode) { m_have_all = true; files_checked(); update_gauge(); update_state_list(); } else if (j->ret == 0) { // there are either no files for this torrent // or the resume_data was accepted if (!j->error && m_resume_data && m_resume_data->node.type() == bdecode_node::dict_t) { // parse have bitmask bdecode_node pieces = m_resume_data->node.dict_find("pieces"); if (pieces && pieces.type() == bdecode_node::string_t && int(pieces.string_length()) == m_torrent_file->num_pieces()) { char const* pieces_str = pieces.string_ptr(); for (int i = 0, end(pieces.string_length()); i < end; ++i) { if (pieces_str[i] & 1) { need_picker(); m_picker->we_have(i); inc_stats_counter(counters::num_piece_passed); update_gauge(); we_have(i); } if (m_seed_mode && (pieces_str[i] & 2)) m_verified.set_bit(i); } } else { bdecode_node slots = m_resume_data->node.dict_find("slots"); if (slots && slots.type() == bdecode_node::list_t) { for (int i = 0; i < slots.list_size(); ++i) { int piece = slots.list_int_value_at(i, -1); if (piece >= 0) { need_picker(); m_picker->we_have(piece); update_gauge(); inc_stats_counter(counters::num_piece_passed); we_have(piece); } } } } // parse unfinished pieces int num_blocks_per_piece = torrent_file().piece_length() / block_size(); if (bdecode_node unfinished_ent = m_resume_data->node.dict_find_list("unfinished")) { for (int i = 0; i < unfinished_ent.list_size(); ++i) { bdecode_node e = unfinished_ent.list_at(i); if (e.type() != bdecode_node::dict_t) continue; int piece = e.dict_find_int_value("piece", -1); if (piece < 0 || piece > torrent_file().num_pieces()) continue; // being in seed mode and missing a piece is not compatible. // Leave seed mode if that happens if (m_seed_mode) leave_seed_mode(true); if (has_picker() && m_picker->have_piece(piece)) { m_picker->we_dont_have(piece); update_gauge(); } std::string bitmask = e.dict_find_string_value("bitmask"); if (bitmask.empty()) continue; need_picker(); const int num_bitmask_bytes = (std::max)(num_blocks_per_piece / 8, 1); if (int(bitmask.size()) != num_bitmask_bytes) continue; for (int k = 0; k < num_bitmask_bytes; ++k) { const boost::uint8_t bits = boost::uint8_t(bitmask[k]); int num_bits = (std::min)(num_blocks_per_piece - k*8, 8); for (int b = 0; b < num_bits; ++b) { const int block = k * 8 + b; if (bits & (1 << b)) { m_picker->mark_as_finished(piece_block(piece, block), 0); } } } if (m_picker->is_piece_finished(piece)) { verify_piece(piece); } } } } files_checked(); } else { // either the fastresume data was rejected or there are // some files set_state(torrent_status::checking_files); if (should_check_files()) start_checking(); // start the checking right away (potentially) m_ses.trigger_auto_manage(); } maybe_done_flushing(); m_resume_data.reset(); // restore m_need_save_resume_data to its state when we entered this // function. m_need_save_resume_data = need_save_resume_data; } namespace { void disk_nop(disk_io_job const*) {} } void torrent::force_recheck() { INVARIANT_CHECK; if (!valid_metadata()) return; // if the torrent is already queued to check its files // don't do anything if (should_check_files() || m_state == torrent_status::checking_resume_data) return; clear_error(); if (!need_loaded()) return; disconnect_all(errors::stopping_torrent, op_bittorrent); stop_announcing(); // we're checking everything anyway, no point in assuming we are a seed // now. leave_seed_mode(true); m_ses.disk_thread().async_release_files(m_storage.get() , boost::function()); // forget that we have any pieces m_have_all = false; // removing the piece picker will clear the user priorities // instead, just clear which pieces we have if (m_picker) { int blocks_per_piece = (m_torrent_file->piece_length() + block_size() - 1) / block_size(); int blocks_in_last_piece = ((m_torrent_file->total_size() % m_torrent_file->piece_length()) + block_size() - 1) / block_size(); m_picker->init(blocks_per_piece, blocks_in_last_piece, m_torrent_file->num_pieces()); m_file_progress.clear(); m_file_progress.init(picker(), m_torrent_file->files()); } // assume that we don't have anything m_files_checked = false; update_gauge(); update_want_tick(); set_state(torrent_status::checking_resume_data); if (m_auto_managed && !is_finished()) set_queue_position((std::numeric_limits::max)()); m_resume_data.reset(); // this will clear the stat cache, to make us actually query the // filesystem for files again m_ses.disk_thread().async_release_files(m_storage.get(), &disk_nop); std::vector links; inc_refcount("force_recheck"); m_ses.disk_thread().async_check_fastresume(m_storage.get(), NULL , links, boost::bind(&torrent::on_force_recheck , shared_from_this(), _1)); } void torrent::on_force_recheck(disk_io_job const* j) { TORRENT_ASSERT(is_single_thread()); // hold a reference until this function returns torrent_ref_holder h(this, "force_recheck"); dec_refcount("force_recheck"); state_updated(); if (m_abort) return; if (j->ret == piece_manager::fatal_disk_error) { handle_disk_error(j); return; } if (j->ret == 0) { // if there are no files, just start files_checked(); } else { m_progress_ppm = 0; m_checking_piece = 0; m_num_checked_pieces = 0; set_state(torrent_status::checking_files); if (m_auto_managed) pause(true); if (should_check_files()) start_checking(); else m_ses.trigger_auto_manage(); } } void torrent::start_checking() { TORRENT_ASSERT(should_check_files()); int num_outstanding = settings().get_int(settings_pack::checking_mem_usage) * block_size() / m_torrent_file->piece_length(); // if we only keep a single read operation in-flight at a time, we suffer // significant performance degradation. Always keep at least 4 jobs // outstanding per hasher thread int const min_outstanding = 4 * std::max(1, settings().get_int(settings_pack::aio_threads) / disk_io_thread::hasher_thread_divisor); if (num_outstanding < min_outstanding) num_outstanding = min_outstanding; // we might already have some outstanding jobs, if we were paused and // resumed quickly, before the outstanding jobs completed if (m_checking_piece >= m_torrent_file->num_pieces()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("start_checking, checking_piece >= num_pieces. %d >= %d" , m_checking_piece, m_torrent_file->num_pieces()); #endif return; } // subtract the number of pieces we already have outstanding num_outstanding -= (m_checking_piece - m_num_checked_pieces); if (num_outstanding < 0) num_outstanding = 0; if (!need_loaded()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("start_checking, need_loaded() failed"); #endif return; } for (int i = 0; i < num_outstanding; ++i) { inc_refcount("start_checking"); m_ses.disk_thread().async_hash(m_storage.get(), m_checking_piece++ , disk_io_job::sequential_access | disk_io_job::volatile_read , boost::bind(&torrent::on_piece_hashed , shared_from_this(), _1), reinterpret_cast(1)); if (m_checking_piece >= m_torrent_file->num_pieces()) break; } #ifndef TORRENT_DISABLE_LOGGING debug_log("start_checking, m_checking_piece: %d", m_checking_piece); #endif } // This is only used for checking of torrents. i.e. force-recheck or initial checking // of existing files void torrent::on_piece_hashed(disk_io_job const* j) { // hold a reference until this function returns torrent_ref_holder h(this, "start_checking"); TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; dec_refcount("start_checking"); if (m_abort) return; if (j->ret == piece_manager::disk_check_aborted) { m_checking_piece = 0; m_num_checked_pieces = 0; #ifndef TORRENT_DISABLE_LOGGING debug_log("on_piece_hashed, disk_check_aborted"); #endif pause(); return; } state_updated(); ++m_num_checked_pieces; if (j->ret < 0) { if (j->error.ec == boost::system::errc::no_such_file_or_directory || j->error.ec == boost::asio::error::eof #ifdef TORRENT_WINDOWS || j->error.ec == error_code(ERROR_HANDLE_EOF, system_category()) #endif ) { TORRENT_ASSERT(j->error.file >= 0); // skip this file by updating m_checking_piece to the first piece following it file_storage const& st = m_torrent_file->files(); boost::uint64_t file_size = st.file_size(j->error.file); int last = st.map_file(j->error.file, file_size, 0).piece; if (m_checking_piece < last) { int diff = last - m_checking_piece; m_num_checked_pieces += diff; m_checking_piece += diff; } } else { m_checking_piece = 0; m_num_checked_pieces = 0; if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(j->error.ec, resolve_filename(j->error.file), j->error.operation_str(), get_handle()); #ifndef TORRENT_DISABLE_LOGGING debug_log("on_piece_hashed, fatal disk error: (%d) %s", j->error.ec.value(), j->error.ec.message().c_str()); #endif auto_managed(false); pause(); set_error(j->error.ec, j->error.file); // recalculate auto-managed torrents sooner // in order to start checking the next torrent m_ses.trigger_auto_manage(); return; } } m_progress_ppm = boost::int64_t(m_num_checked_pieces) * 1000000 / torrent_file().num_pieces(); // we're using the piece hashes here, we need the torrent to be loaded if (!need_loaded()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("on_piece_hashed, need_loaded failed"); #endif return; } if (settings().get_bool(settings_pack::disable_hash_checks) || sha1_hash(j->d.piece_hash) == m_torrent_file->hash_for_piece(j->piece)) { if (has_picker() || !m_have_all) { need_picker(); m_picker->we_have(j->piece); update_gauge(); } we_have(j->piece); } else { // if the hash failed, remove it from the cache if (m_storage) m_ses.disk_thread().clear_piece(m_storage.get(), j->piece); } if (m_num_checked_pieces < m_torrent_file->num_pieces()) { // we're not done yet, issue another job if (m_checking_piece >= m_torrent_file->num_pieces()) { // actually, we already have outstanding jobs for // the remaining pieces. We just need to wait for them // to finish return; } // we paused the checking if (!should_check_files()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("on_piece_hashed, checking paused"); #endif if (m_checking_piece == m_num_checked_pieces) { // we are paused, and we just completed the last outstanding job. // now we can be considered paused if (alerts().should_post()) alerts().emplace_alert(get_handle()); } return; } if (!need_loaded()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("on_piece_hashed, need_loaded failed"); #endif return; } inc_refcount("start_checking"); m_ses.disk_thread().async_hash(m_storage.get(), m_checking_piece++ , disk_io_job::sequential_access | disk_io_job::volatile_read , boost::bind(&torrent::on_piece_hashed , shared_from_this(), _1), reinterpret_cast(1)); #ifndef TORRENT_DISABLE_LOGGING debug_log("on_piece_hashed, m_checking_piece: %d", m_checking_piece); #endif return; } #ifndef TORRENT_DISABLE_LOGGING debug_log("on_piece_hashed, completed"); #endif if (m_auto_managed) { // if we're auto managed. assume we need to be paused until the auto // managed logic runs again (which is triggered further down) // setting flags to 0 prevents the disk cache from being evicted as a // result of this set_allow_peers(false, 0); } // we're done checking! (this should cause a call to trigger_auto_manage) files_checked(); // reset the checking state m_checking_piece = 0; m_num_checked_pieces = 0; } #ifndef TORRENT_NO_DEPRECATE void torrent::use_interface(std::string net_interfaces) { boost::shared_ptr p = boost::make_shared(); p->set_str(settings_pack::outgoing_interfaces, net_interfaces); m_ses.apply_settings_pack(p); } #endif void torrent::on_tracker_announce_disp(boost::weak_ptr p , error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("tracker::on_tracker_announce_disp"); #endif boost::shared_ptr t = p.lock(); if (!t) return; TORRENT_ASSERT(t->m_waiting_tracker > 0); --t->m_waiting_tracker; if (e) return; t->on_tracker_announce(); } void torrent::on_tracker_announce() { TORRENT_ASSERT(is_single_thread()); if (m_abort) return; announce_with_tracker(); } void torrent::lsd_announce() { if (m_abort) return; // if the files haven't been checked yet, we're // not ready for peers. Except, if we don't have metadata, // we need peers to download from if (!m_files_checked && valid_metadata()) return; if (!m_announce_to_lsd) return; // private torrents are never announced on LSD if (m_torrent_file->is_valid() && m_torrent_file->priv()) return; // i2p torrents are also never announced on LSD // unless we allow mixed swarms if (m_torrent_file->is_valid() && (torrent_file().is_i2p() && !settings().get_bool(settings_pack::allow_i2p_mixed))) return; if (is_paused()) return; if (!m_ses.has_lsd()) return; // TODO: this pattern is repeated in a few places. Factor this into // a function and generalize the concept of a torrent having a // dedicated listen port #ifdef TORRENT_USE_OPENSSL int port = is_ssl_torrent() ? m_ses.ssl_listen_port() : m_ses.listen_port(); #else int port = m_ses.listen_port(); #endif // announce with the local discovery service m_ses.announce_lsd(m_torrent_file->info_hash(), port , settings().get_bool(settings_pack::broadcast_lsd) && m_lsd_seq == 0); ++m_lsd_seq; } #ifndef TORRENT_DISABLE_DHT void torrent::dht_announce() { TORRENT_ASSERT(is_single_thread()); if (!m_ses.dht()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("DHT: no dht initialized"); #endif return; } if (!should_announce_dht()) { #ifndef TORRENT_DISABLE_LOGGING if (!m_ses.announce_dht()) debug_log("DHT: no listen sockets"); if (m_torrent_file->is_valid() && !m_files_checked) debug_log("DHT: files not checked, skipping DHT announce"); if (!m_announce_to_dht) debug_log("DHT: queueing disabled DHT announce"); if (!m_allow_peers) debug_log("DHT: torrent paused, no DHT announce"); if (!m_torrent_file->is_valid() && !m_url.empty()) debug_log("DHT: no info-hash, waiting for \"%s\"", m_url.c_str()); if (m_torrent_file->is_valid() && m_torrent_file->priv()) debug_log("DHT: private torrent, no DHT announce"); if (settings().get_bool(settings_pack::use_dht_as_fallback)) { int verified_trackers = 0; for (std::vector::const_iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) if (i->verified) ++verified_trackers; if (verified_trackers > 0) debug_log("DHT: only using DHT as fallback, and there are %d working trackers", verified_trackers); } #endif return; } TORRENT_ASSERT(m_allow_peers); #ifdef TORRENT_USE_OPENSSL int port = is_ssl_torrent() ? m_ses.ssl_listen_port() : m_ses.listen_port(); #else int port = m_ses.listen_port(); #endif #ifndef TORRENT_DISABLE_LOGGING debug_log("START DHT announce"); m_dht_start_time = clock_type::now(); #endif // if we're a seed, we tell the DHT for better scrape stats int flags = is_seed() ? dht::dht_tracker::flag_seed : 0; // if we allow incoming uTP connections, set the implied_port // argument in the announce, this will make the DHT node use // our source port in the packet as our listen port, which is // likely more accurate when behind a NAT if (settings().get_bool(settings_pack::enable_incoming_utp)) flags |= dht::dht_tracker::flag_implied_port; boost::weak_ptr self(shared_from_this()); m_ses.dht()->announce(m_torrent_file->info_hash() , port, flags , boost::bind(&torrent::on_dht_announce_response_disp, self, _1)); } void torrent::on_dht_announce_response_disp(boost::weak_ptr t , std::vector const& peers) { boost::shared_ptr tor = t.lock(); if (!tor) return; tor->on_dht_announce_response(peers); } void torrent::on_dht_announce_response(std::vector const& peers) { TORRENT_ASSERT(is_single_thread()); #ifndef TORRENT_DISABLE_LOGGING debug_log("END DHT announce (%d ms) (%d peers)" , int(total_milliseconds(clock_type::now() - m_dht_start_time)) , int(peers.size())); #endif if (m_abort) return; if (peers.empty()) return; if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert( get_handle(), peers.size()); } if (torrent_file().priv() || (torrent_file().is_i2p() && !settings().get_bool(settings_pack::allow_i2p_mixed))) return; std::for_each(peers.begin(), peers.end(), boost::bind( &torrent::add_peer, this, _1, peer_info::dht, 0)); do_connect_boost(); update_want_peers(); } #endif void torrent::announce_with_tracker(boost::uint8_t e) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(e == tracker_request::stopped || state() != torrent_status::checking_files); INVARIANT_CHECK; if (m_trackers.empty()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("*** announce: no trackers"); #endif return; } if (m_abort) e = tracker_request::stopped; // if we're not announcing to trackers, only allow // stopping if (e != tracker_request::stopped && !m_announce_to_trackers) { #ifndef TORRENT_DISABLE_LOGGING debug_log("*** announce: event != stopped && !m_announce_to_trackers"); #endif return; } // if we're not allowing peers, there's no point in announcing if (e != tracker_request::stopped && !m_allow_peers) { #ifndef TORRENT_DISABLE_LOGGING debug_log("*** announce: event != stopped && !m_allow_peers"); #endif return; } TORRENT_ASSERT(m_allow_peers || e == tracker_request::stopped); if (e == tracker_request::none && is_finished() && !is_seed()) e = tracker_request::paused; tracker_request req; if (settings().get_bool(settings_pack::apply_ip_filter_to_trackers) && m_apply_ip_filter) req.filter = m_ip_filter; req.private_torrent = m_torrent_file->priv(); req.info_hash = m_torrent_file->info_hash(); req.pid = m_peer_id; req.downloaded = m_stat.total_payload_download() - m_total_failed_bytes; req.uploaded = m_stat.total_payload_upload(); req.corrupt = m_total_failed_bytes; req.left = bytes_left(); if (req.left == -1) req.left = 16*1024; #ifdef TORRENT_USE_OPENSSL // if this torrent contains an SSL certificate, make sure // any SSL tracker presents a certificate signed by it req.ssl_ctx = m_ssl_ctx.get(); #endif req.redundant = m_total_redundant_bytes; // exclude redundant bytes if we should if (!settings().get_bool(settings_pack::report_true_downloaded)) { req.downloaded -= m_total_redundant_bytes; // if the torrent is complete we know that all incoming pieces will be // marked redundant so add them to the redundant count // this is mainly needed to cover the case where a torrent has just completed // but still has partially downloaded pieces // if the incoming pieces are not accounted for it could cause the downloaded // amount to exceed the total size of the torrent which upsets some trackers if (is_seed()) { for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = *i; boost::optional pbp = p->downloading_piece_progress(); if (pbp && pbp->bytes_downloaded > 0) { req.downloaded -= pbp->bytes_downloaded; req.redundant += pbp->bytes_downloaded; } } } } if (req.downloaded < 0) req.downloaded = 0; req.event = e; // since sending our IPv4/v6 address to the tracker may be sensitive. Only // do that if we're not in anonymous mode and if it's a private torrent if (!settings().get_bool(settings_pack::anonymous_mode) && m_torrent_file && m_torrent_file->priv()) { boost::optional ep4 = m_ses.get_ipv4_interface(); if (ep4 && !is_local(ep4->address()) && !is_loopback(ep4->address())) req.ipv4 = ep4->address().to_v4(); #if TORRENT_USE_IPV6 boost::optional ep6 = m_ses.get_ipv6_interface(); if (ep6 && !is_local(ep6->address()) && !is_loopback(ep6->address())) req.ipv6 = ep6->address().to_v6(); #endif } // if we are aborting. we don't want any new peers req.num_want = (req.event == tracker_request::stopped) ? 0 : settings().get_int(settings_pack::num_want); time_point const now = aux::time_now(); // the tier is kept as INT_MAX until we find the first // tracker that works, then it's set to that tracker's // tier. int tier = INT_MAX; // have we sent an announce in this tier yet? bool sent_announce = false; for (int i = 0; i < int(m_trackers.size()); ++i) { announce_entry& ae = m_trackers[i]; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** tracker: \"%s\" " "[ tiers: %d trackers: %d" " i->tier: %d tier: %d" " working: %d fails: %d limit: %d upd: %d" " can: %d sent: %d ]" , ae.url.c_str(), settings().get_bool(settings_pack::announce_to_all_tiers) , settings().get_bool(settings_pack::announce_to_all_trackers) , ae.tier, tier, ae.is_working(), ae.fails, ae.fail_limit , ae.updating, ae.can_announce(now, is_seed()), sent_announce); #endif if (settings().get_bool(settings_pack::announce_to_all_tiers) && !settings().get_bool(settings_pack::announce_to_all_trackers) && sent_announce && ae.tier <= tier && tier != INT_MAX) continue; // if trackerid is not specified for tracker use default one, probably set explicitly req.trackerid = ae.trackerid.empty() ? m_trackerid : ae.trackerid; if (ae.tier > tier && sent_announce && !settings().get_bool(settings_pack::announce_to_all_tiers)) break; if (ae.is_working()) { tier = ae.tier; sent_announce = false; } if (!ae.can_announce(now, is_seed())) { // this counts if (ae.is_working()) sent_announce = true; continue; } req.url = ae.url; req.event = e; if (req.event == tracker_request::none) { if (!ae.start_sent) req.event = tracker_request::started; else if (!ae.complete_sent && is_seed()) req.event = tracker_request::completed; } req.triggered_manually = ae.triggered_manually; ae.triggered_manually = false; if (settings().get_bool(settings_pack::force_proxy)) { // in force_proxy mode we don't talk directly to trackers // we only allow trackers if there is a proxy and issue // a warning if there isn't one std::string protocol = req.url.substr(0, req.url.find(':')); int proxy_type = settings().get_int(settings_pack::proxy_type); // http can run over any proxy, so as long as one is used // it's OK. If no proxy is configured, skip this tracker if ((protocol == "http" || protocol == "https") && proxy_type == settings_pack::none) { ae.next_announce = now + minutes(10); if (m_ses.alerts().should_post() || req.triggered_manually) { m_ses.alerts().emplace_alert(get_handle() , anonymous_mode_alert::tracker_not_anonymous, req.url); } continue; } // for UDP, only socks5 and i2p proxies will work. // if we're not using one of those proxues with a UDP // tracker, skip it if (protocol == "udp" && proxy_type != settings_pack::socks5 && proxy_type != settings_pack::socks5_pw && proxy_type != settings_pack::i2p_proxy) { ae.next_announce = now + minutes(10); if (m_ses.alerts().should_post() || req.triggered_manually) { m_ses.alerts().emplace_alert(get_handle() , anonymous_mode_alert::tracker_not_anonymous, req.url); } continue; } } #ifndef TORRENT_NO_DEPRECATE req.auth = tracker_login(); #endif req.key = tracker_key(); #if TORRENT_USE_I2P if (is_i2p()) { req.kind |= tracker_request::i2p; } #endif #ifndef TORRENT_DISABLE_LOGGING debug_log("==> TRACKER REQUEST \"%s\" event: %s abort: %d ssl: %p " "port: %d ssl-port: %d force-proxy: %d" , req.url.c_str() , (req.event==tracker_request::stopped?"stopped" :req.event==tracker_request::started?"started":"-") , m_abort #ifdef TORRENT_USE_OPENSSL , static_cast(req.ssl_ctx) #else , static_cast(0) #endif , m_ses.listen_port() , m_ses.ssl_listen_port() , settings().get_bool(settings_pack::force_proxy)); // if we're not logging session logs, don't bother creating an // observer object just for logging if (m_abort && alerts().should_post()) { boost::shared_ptr tl(new aux::tracker_logger(m_ses)); m_ses.queue_tracker_request(req, tl); } else #endif { m_ses.queue_tracker_request(req, shared_from_this()); } ae.updating = true; ae.next_announce = now; ae.min_announce = now; if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert( get_handle(), req.url, req.event); } sent_announce = true; if (ae.is_working() && !settings().get_bool(settings_pack::announce_to_all_trackers) && !settings().get_bool(settings_pack::announce_to_all_tiers)) break; } update_tracker_timer(now); } int torrent::seconds_since_last_scrape() const { return m_last_scrape == unset ? -1 : total_seconds(clock_type::now() - time_point(seconds(m_last_scrape))); } void torrent::scrape_tracker(int idx, bool user_triggered) { TORRENT_ASSERT(is_single_thread()); m_last_scrape = total_seconds(clock_type::now().time_since_epoch()); if (m_trackers.empty()) return; if (idx < 0 || idx >= int(m_trackers.size())) idx = m_last_working_tracker; if (idx < 0) idx = 0; tracker_request req; if (settings().get_bool(settings_pack::apply_ip_filter_to_trackers) && m_apply_ip_filter) req.filter = m_ip_filter; req.info_hash = m_torrent_file->info_hash(); req.kind |= tracker_request::scrape_request; req.url = m_trackers[idx].url; req.private_torrent = m_torrent_file->priv(); #ifndef TORRENT_NO_DEPRECATE req.auth = tracker_login(); #endif req.key = tracker_key(); req.triggered_manually = user_triggered; m_ses.queue_tracker_request(req, shared_from_this()); } void torrent::tracker_warning(tracker_request const& req, std::string const& msg) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; announce_entry* ae = find_tracker(req); if (ae) { ae->message = msg; } if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle(), req.url, msg); } void torrent::tracker_scrape_response(tracker_request const& req , int complete, int incomplete, int downloaded, int /* downloaders */) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(0 != (req.kind & tracker_request::scrape_request)); announce_entry* ae = find_tracker(req); if (ae) { if (incomplete >= 0) ae->scrape_incomplete = incomplete; if (complete >= 0) ae->scrape_complete = complete; if (downloaded >= 0) ae->scrape_downloaded = downloaded; update_scrape_state(); } // if this was triggered manually we need to post this unconditionally, // since the client expects a response from its action, regardless of // whether all tracker events have been enabled by the alert mask if (m_ses.alerts().should_post() || req.triggered_manually) { m_ses.alerts().emplace_alert( get_handle(), incomplete, complete, req.url); } } void torrent::update_scrape_state() { // loop over all trackers and find the largest numbers for each scrape field // then update the torrent-wide understanding of number of downloaders and seeds int complete = -1; int incomplete = -1; int downloaded = -1; for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) { complete = (std::max)(i->scrape_complete, complete); incomplete = (std::max)(i->scrape_incomplete, incomplete); downloaded = (std::max)(i->scrape_downloaded, downloaded); } if ((complete >= 0 && m_complete != complete) || (incomplete >= 0 && m_incomplete != incomplete) || (downloaded >= 0 && m_downloaded != downloaded)) state_updated(); if (m_complete != complete || m_incomplete != incomplete || m_downloaded != downloaded) { m_complete = complete; m_incomplete = incomplete; m_downloaded = downloaded; update_auto_sequential(); // these numbers are cached in the resume data set_need_save_resume(); } } void torrent::tracker_response( tracker_request const& r , address const& tracker_ip // this is the IP we connected to , std::list
const& tracker_ips // these are all the IPs it resolved to , struct tracker_response const& resp) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(0 == (r.kind & tracker_request::scrape_request)); // if the tracker told us what our external IP address is, record it with // out external IP counter (and pass along the IP of the tracker to know // who to attribute this vote to) if (resp.external_ip != address() && !is_any(tracker_ip)) m_ses.set_external_address(resp.external_ip , aux::session_interface::source_tracker, tracker_ip); time_point now = aux::time_now(); int interval = resp.interval; if (interval < settings().get_int(settings_pack::min_announce_interval)) interval = settings().get_int(settings_pack::min_announce_interval); announce_entry* ae = find_tracker(r); if (ae) { if (resp.incomplete >= 0) ae->scrape_incomplete = resp.incomplete; if (resp.complete >= 0) ae->scrape_complete = resp.complete; if (resp.downloaded >= 0) ae->scrape_downloaded = resp.downloaded; if (!ae->start_sent && r.event == tracker_request::started) ae->start_sent = true; if (!ae->complete_sent && r.event == tracker_request::completed) ae->complete_sent = true; ae->verified = true; ae->updating = false; ae->fails = 0; ae->next_announce = now + seconds(interval); ae->min_announce = now + seconds(resp.min_interval); int tracker_index = ae - &m_trackers[0]; m_last_working_tracker = prioritize_tracker(tracker_index); if ((!resp.trackerid.empty()) && (ae->trackerid != resp.trackerid)) { ae->trackerid = resp.trackerid; if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle() , r.url, resp.trackerid); } update_scrape_state(); } update_tracker_timer(now); if (resp.complete >= 0 && resp.incomplete >= 0) m_last_scrape = total_seconds(clock_type::now().time_since_epoch()); #ifndef TORRENT_DISABLE_LOGGING std::string resolved_to; for (std::list
::const_iterator i = tracker_ips.begin() , end(tracker_ips.end()); i != end; ++i) { resolved_to += i->to_string(); resolved_to += ", "; } debug_log("TRACKER RESPONSE\n" "interval: %d\n" "min-interval: %d\n" "external ip: %s\n" "resolved to: %s\n" "we connected to: %s\n" , interval , resp.min_interval , print_address(resp.external_ip).c_str() , resolved_to.c_str() , print_address(tracker_ip).c_str()); #endif // for each of the peers we got from the tracker for (std::vector::const_iterator i = resp.peers.begin(); i != resp.peers.end(); ++i) { // don't make connections to ourself if (i->pid == m_peer_id) continue; #if TORRENT_USE_I2P if (r.i2pconn && boost::algorithm::ends_with(i->hostname, ".i2p")) { // this is an i2p name, we need to use the sam connection // to do the name lookup if (boost::algorithm::ends_with(i->hostname, ".b32.i2p")) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("torrent::on_i2p_resolve"); #endif r.i2pconn->async_name_lookup(i->hostname.c_str() , boost::bind(&torrent::on_i2p_resolve , shared_from_this(), _1, _2)); } else { torrent_state st = get_peer_list_state(); need_peer_list(); if (m_peer_list->add_i2p_peer(i->hostname.c_str (), peer_info::tracker, 0, &st)) state_updated(); peers_erased(st.erased); } } else #endif { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("torrent::on_peer_name_lookup"); #endif m_ses.async_resolve(i->hostname, resolver_interface::abort_on_shutdown , boost::bind(&torrent::on_peer_name_lookup , shared_from_this(), _1, _2, i->port)); } } // there are 2 reasons to allow local IPs to be returned from a // non-local tracker // 1. retrackers are popular in russia, where an ISP runs a tracker within // the AS (but not on the local network) giving out peers only from the // local network // 2. it might make sense to have a tracker extension in the future where // trackers records a peer's internal and external IP, and match up // peers on the same local network bool need_update = false; for (std::vector::const_iterator i = resp.peers4.begin(); i != resp.peers4.end(); ++i) { tcp::endpoint a(address_v4(i->ip), i->port); need_update |= bool(add_peer(a, peer_info::tracker) != NULL); } #if TORRENT_USE_IPV6 for (std::vector::const_iterator i = resp.peers6.begin(); i != resp.peers6.end(); ++i) { tcp::endpoint a(address_v6(i->ip), i->port); need_update |= bool(add_peer(a, peer_info::tracker) != NULL); } #endif if (need_update) state_updated(); update_want_peers(); // post unconditionally if the announce was triggered manually if (m_ses.alerts().should_post() || r.triggered_manually) { m_ses.alerts().emplace_alert( get_handle(), resp.peers.size() + resp.peers4.size() #if TORRENT_USE_IPV6 + resp.peers6.size() #endif , r.url); } // we're listening on an interface type that was not used // when talking to the tracker. If there is a matching interface // type in the tracker IP list, make another tracker request // using that interface // in order to avoid triggering this case over and over, check whether // this announce was itself triggered by this logic (second_announce) if (((m_ses.get_ipv6_interface() && tracker_ip.is_v4()) || (m_ses.get_ipv4_interface() && tracker_ip.is_v6())) && !r.second_announce) { std::list
::const_iterator i = std::find_if(tracker_ips.begin() , tracker_ips.end(), boost::bind(&address::is_v4, _1) != tracker_ip.is_v4()); if (i != tracker_ips.end()) { // the tracker did resolve to a different type of address, so announce // to that as well // TODO 3: there's a bug when removing a torrent or shutting down the session, // where the second announce is skipped (in this case, the one to the IPv6 // name). This should be fixed by generalizing the tracker list structure to // separate the IPv6 and IPv4 addresses as conceptually separate trackers, // and they should be announced to in parallel tracker_request req = r; req.private_torrent = m_torrent_file->priv(); req.second_announce = true; // tell the tracker to bind to the opposite protocol type req.bind_ip = tracker_ip.is_v4() ? m_ses.get_ipv6_interface()->address() : m_ses.get_ipv4_interface()->address(); #ifndef TORRENT_DISABLE_LOGGING debug_log("announce again using %s as the bind interface. port: %d" , print_address(*req.bind_ip).c_str(), req.listen_port); #endif m_ses.queue_tracker_request(req, shared_from_this()); } } do_connect_boost(); state_updated(); } void torrent::update_auto_sequential() { if (!settings().get_bool(settings_pack::auto_sequential)) { m_auto_sequential = false; return; } if (int(m_connections.size() - m_num_connecting) < 10) { // there are too few peers. Be conservative and don't assume it's // well seeded until we can connect to more peers m_auto_sequential = false; return; } // if there are at least 10 seeds, and there are 10 times more // seeds than downloaders, enter sequential download mode // (for performance) int const downloaders = num_downloaders(); int const seeds = num_seeds(); m_auto_sequential = downloaders * 10 <= seeds && seeds > 9; } void torrent::do_connect_boost() { if (m_connect_boost_counter == 0) return; // this is the first tracker response for this torrent // instead of waiting one second for session_impl::on_tick() // to be called, connect to a few peers immediately int conns = (std::min)(int(m_connect_boost_counter) , settings().get_int(settings_pack::connections_limit) - m_ses.num_connections()); if (conns == 0) return; // if we don't know of any peers if (!m_peer_list) return; while (want_peers() && conns > 0) { TORRENT_ASSERT(m_connect_boost_counter > 0); --conns; --m_connect_boost_counter; torrent_state st = get_peer_list_state(); torrent_peer* p = m_peer_list->connect_one_peer(m_ses.session_time(), &st); peers_erased(st.erased); inc_stats_counter(counters::connection_attempt_loops, st.loop_counter); if (p == NULL) { update_want_peers(); continue; } #ifndef TORRENT_DISABLE_LOGGING external_ip const& external = m_ses.external_address(); debug_log(" *** FOUND CONNECTION CANDIDATE [" " ip: %s rank: %u external: %s t: %d ]" , print_endpoint(p->ip()).c_str() , p->rank(external, m_ses.listen_port()) , print_address(external.external_address(p->address())).c_str() , int(m_ses.session_time() - p->last_connected)); #endif if (!connect_to_peer(p)) { m_peer_list->inc_failcount(p); update_want_peers(); } else { // increase m_ses.m_boost_connections for each connection // attempt. This will be deducted from the connect speed // the next time session_impl::on_tick() is triggered m_ses.inc_boost_connections(); update_want_peers(); } } if (want_peers()) m_ses.prioritize_connections(shared_from_this()); } // this is the entry point for the client to force a re-announce. It's // considered a client-initiated announce (as opposed to the regular ones, // issued by libtorrent) void torrent::force_tracker_request(time_point const t, int const tracker_idx , int const flags) { if (is_paused()) return; if (tracker_idx == -1) { for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) { i->next_announce = (flags & torrent_handle::ignore_min_interval) ? t + seconds(1) : (std::max)(t, i->min_announce) + seconds(1); i->min_announce = i->next_announce; i->triggered_manually = true; } } else { TORRENT_ASSERT(tracker_idx >= 0 && tracker_idx < int(m_trackers.size())); if (tracker_idx < 0 || tracker_idx >= int(m_trackers.size())) return; announce_entry& e = m_trackers[tracker_idx]; e.next_announce = (flags & torrent_handle::ignore_min_interval) ? t + seconds(1) : (std::max)(t, e.min_announce) + seconds(1); e.min_announce = e.next_announce; e.triggered_manually = true; } update_tracker_timer(clock_type::now()); } #ifndef TORRENT_NO_DEPRECATE void torrent::set_tracker_login( std::string const& name , std::string const& pw) { m_username = name; m_password = pw; } #endif #if TORRENT_USE_I2P void torrent::on_i2p_resolve(error_code const& ec, char const* dest) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #if defined TORRENT_ASIO_DEBUGGING complete_async("torrent::on_i2p_resolve"); #endif #ifndef TORRENT_DISABLE_LOGGING if (ec) debug_log("i2p_resolve error: %s", ec.message().c_str()); #endif if (ec || m_abort || m_ses.is_aborted()) return; need_peer_list(); torrent_state st = get_peer_list_state(); if (m_peer_list->add_i2p_peer(dest, peer_info::tracker, 0, &st)) state_updated(); peers_erased(st.erased); } #endif void torrent::on_peer_name_lookup(error_code const& e , std::vector
const& host_list, int port) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #if defined TORRENT_ASIO_DEBUGGING complete_async("torrent::on_peer_name_lookup"); #endif #ifndef TORRENT_DISABLE_LOGGING if (e) debug_log("peer name lookup error: %s", e.message().c_str()); #endif if (e || m_abort || host_list.empty() || m_ses.is_aborted()) return; // TODO: add one peer per IP the hostname resolves to tcp::endpoint host(host_list.front(), port); if (m_ip_filter && m_ip_filter->access(host.address()) & ip_filter::blocked) { #ifndef TORRENT_DISABLE_LOGGING error_code ec; debug_log("blocked ip from tracker: %s", host.address().to_string(ec).c_str()); #endif if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle() , host.address(), peer_blocked_alert::ip_filter); return; } if (add_peer(host, peer_info::tracker)) state_updated(); update_want_peers(); } boost::int64_t 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(); } boost::int64_t torrent::quantized_bytes_done() const { // INVARIANT_CHECK; if (!valid_metadata()) return 0; if (m_torrent_file->num_pieces() == 0) return 0; // if any piece hash fails, we'll be taken out of seed mode // and m_seed_mode will be false if (m_seed_mode) return m_torrent_file->total_size(); if (!has_picker()) return m_have_all ? m_torrent_file->total_size() : 0; const int last_piece = m_torrent_file->num_pieces() - 1; boost::int64_t total_done = boost::uint64_t(m_picker->num_passed()) * 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_picker->has_piece_passed(last_piece)) { int corr = m_torrent_file->piece_size(last_piece) - m_torrent_file->piece_length(); total_done += corr; } return total_done; } // returns the number of bytes we are interested // in for the given block. This returns block_size() // for all blocks except the last one (if it's smaller // than block_size()) and blocks that overlap a padding // file int torrent::block_bytes_wanted(piece_block const& p) const { file_storage const& fs = m_torrent_file->files(); int piece_size = m_torrent_file->piece_size(p.piece_index); int offset = p.block_index * block_size(); if (m_padding == 0) return (std::min)(piece_size - offset, block_size()); std::vector files = fs.map_block( p.piece_index, offset, (std::min)(piece_size - offset, block_size())); int ret = 0; for (std::vector::iterator i = files.begin() , end(files.end()); i != end; ++i) { if (fs.pad_file_at(i->file_index)) continue; ret += i->size; } TORRENT_ASSERT(ret <= (std::min)(piece_size - offset, block_size())); return ret; } // fills in total_wanted, total_wanted_done and total_done void torrent::bytes_done(torrent_status& st, bool const accurate) const { INVARIANT_CHECK; st.total_done = 0; st.total_wanted_done = 0; st.total_wanted = m_torrent_file->total_size(); TORRENT_ASSERT(st.total_wanted >= m_padding); TORRENT_ASSERT(st.total_wanted >= 0); if (!valid_metadata() || m_torrent_file->num_pieces() == 0) return; TORRENT_ASSERT(st.total_wanted >= boost::int64_t(m_torrent_file->piece_length()) * (m_torrent_file->num_pieces() - 1)); const int last_piece = m_torrent_file->num_pieces() - 1; const int piece_size = m_torrent_file->piece_length(); // if any piece hash fails, we'll be taken out of seed mode // and m_seed_mode will be false if (m_seed_mode || is_seed()) { st.total_done = m_torrent_file->total_size() - m_padding; st.total_wanted_done = st.total_done; st.total_wanted = st.total_done; return; } else if (!has_picker()) { st.total_done = 0; st.total_wanted_done = 0; st.total_wanted = m_torrent_file->total_size() - m_padding; return; } TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); st.total_wanted_done = boost::int64_t(num_passed() - m_picker->num_have_filtered()) * piece_size; TORRENT_ASSERT(st.total_wanted_done >= 0); st.total_done = boost::int64_t(num_passed()) * piece_size; // if num_passed() == num_pieces(), we should be a seed, and taken the // branch above TORRENT_ASSERT(num_passed() <= m_torrent_file->num_pieces()); int num_filtered_pieces = m_picker->num_filtered() + m_picker->num_have_filtered(); int last_piece_index = m_torrent_file->num_pieces() - 1; if (m_picker->piece_priority(last_piece_index) == 0) { st.total_wanted -= m_torrent_file->piece_size(last_piece_index); TORRENT_ASSERT(st.total_wanted >= 0); --num_filtered_pieces; } st.total_wanted -= boost::int64_t(num_filtered_pieces) * piece_size; TORRENT_ASSERT(st.total_wanted >= 0); // 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_picker->has_piece_passed(last_piece)) { TORRENT_ASSERT(st.total_done >= piece_size); int const corr = m_torrent_file->piece_size(last_piece) - piece_size; TORRENT_ASSERT(corr <= 0); TORRENT_ASSERT(corr > -piece_size); st.total_done += corr; if (m_picker->piece_priority(last_piece) != 0) { TORRENT_ASSERT(st.total_wanted_done >= piece_size); st.total_wanted_done += corr; } } TORRENT_ASSERT(st.total_wanted >= st.total_wanted_done); // this is expensive, we might not want to do it all the time if (!accurate) return; // subtract padding files if (m_padding > 0) { // this is a bit unfortunate // (both the const cast and the requirement to load the torrent) if (!const_cast(this)->need_loaded()) return; file_storage const& files = m_torrent_file->files(); for (int i = 0; i < files.num_files(); ++i) { if (!files.pad_file_at(i)) continue; peer_request p = files.map_file(i, 0, files.file_size(i)); for (int j = p.piece; p.length > 0; ++j) { int deduction = (std::min)(p.length, piece_size - p.start); bool done = m_picker->has_piece_passed(j); bool wanted = m_picker->piece_priority(j) > 0; if (done) st.total_done -= deduction; if (wanted) st.total_wanted -= deduction; if (wanted && done) st.total_wanted_done -= deduction; TORRENT_ASSERT(st.total_done >= 0); TORRENT_ASSERT(st.total_wanted >= 0); TORRENT_ASSERT(st.total_wanted_done >= 0); p.length -= piece_size - p.start; p.start = 0; ++p.piece; } } } TORRENT_ASSERT(!accurate || st.total_done <= m_torrent_file->total_size() - m_padding); TORRENT_ASSERT(st.total_wanted_done >= 0); TORRENT_ASSERT(st.total_done >= st.total_wanted_done); std::vector dl_queue = m_picker->get_download_queue(); const int blocks_per_piece = (piece_size + block_size() - 1) / block_size(); // look at all unfinished pieces and add the completed // blocks to our 'done' counter for (std::vector::const_iterator i = dl_queue.begin(); i != dl_queue.end(); ++i) { int corr = 0; int index = i->index; // completed pieces are already accounted for if (m_picker->has_piece_passed(index)) continue; TORRENT_ASSERT(i->finished <= m_picker->blocks_in_piece(index)); #if TORRENT_USE_ASSERTS for (std::vector::const_iterator j = boost::next(i); j != dl_queue.end(); ++j) { TORRENT_ASSERT(j->index != index); } #endif piece_picker::block_info* info = m_picker->blocks_for_piece(*i); for (int j = 0; j < blocks_per_piece; ++j) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_ASSERT(m_picker->is_finished(piece_block(index, j)) == (info[j].state == piece_picker::block_info::state_finished)); #endif if (info[j].state == piece_picker::block_info::state_finished) { corr += block_bytes_wanted(piece_block(index, j)); } TORRENT_ASSERT(corr >= 0); TORRENT_ASSERT(index != last_piece || j < m_picker->blocks_in_last_piece() || info[j].state != piece_picker::block_info::state_finished); } st.total_done += corr; if (m_picker->piece_priority(index) > 0) st.total_wanted_done += corr; } TORRENT_ASSERT(st.total_wanted <= m_torrent_file->total_size() - m_padding); TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size() - m_padding); TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size() - m_padding); TORRENT_ASSERT(st.total_wanted_done >= 0); TORRENT_ASSERT(st.total_done >= st.total_wanted_done); std::map downloading_piece; for (const_peer_iterator i = begin(); i != end(); ++i) { peer_connection* pc = *i; boost::optional p = pc->downloading_piece_progress(); if (!p) continue; if (m_picker->has_piece_passed(p->piece_index)) continue; piece_block block(p->piece_index, p->block_index); if (m_picker->is_finished(block)) continue; std::map::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; } #ifdef TORRENT_DEBUG TORRENT_ASSERT(p->bytes_downloaded <= p->full_block_bytes); TORRENT_ASSERT(p->full_block_bytes == to_req(piece_block( p->piece_index, p->block_index)).length); #endif } for (std::map::iterator i = downloading_piece.begin(); i != downloading_piece.end(); ++i) { int done = (std::min)(block_bytes_wanted(i->first), i->second); st.total_done += done; if (m_picker->piece_priority(i->first.piece_index) != 0) st.total_wanted_done += done; } TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size() - m_padding); TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size() - m_padding); #ifdef TORRENT_DEBUG if (st.total_done >= m_torrent_file->total_size()) { // Thist happens when a piece has been downloaded completely // but not yet verified against the hash fprintf(stderr, "num_have: %d\nunfinished:\n", num_have()); for (std::vector::const_iterator i = dl_queue.begin(); i != dl_queue.end(); ++i) { fprintf(stderr, " %d ", i->index); piece_picker::block_info* info = m_picker->blocks_for_piece(*i); for (int j = 0; j < blocks_per_piece; ++j) { char const* state = info[j].state == piece_picker::block_info::state_finished ? "1" : "0"; fputs(state, stderr); } fputs("\n", stderr); } fputs("downloading pieces:\n", stderr); for (std::map::iterator i = downloading_piece.begin(); i != downloading_piece.end(); ++i) { fprintf(stderr, " %d:%d %d\n", int(i->first.piece_index), int(i->first.block_index), i->second); } } TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size()); TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size()); #endif TORRENT_ASSERT(st.total_done >= st.total_wanted_done); } void torrent::on_piece_verified(disk_io_job const* j) { TORRENT_ASSERT(is_single_thread()); torrent_ref_holder h(this, "verify_piece"); dec_refcount("verify_piece"); if (m_abort) return; int ret = j->ret; if (settings().get_bool(settings_pack::disable_hash_checks)) { ret = 0; } else if (ret == -1) { handle_disk_error(j); } // we're using the piece hashes here, we need the torrent to be loaded else if (need_loaded()) { if (sha1_hash(j->d.piece_hash) != m_torrent_file->hash_for_piece(j->piece)) ret = -2; } else { // failing to load the .torrent file counts as disk failure ret = -1; } // 0: success, piece passed check // -1: disk failure // -2: piece failed check #ifndef TORRENT_DISABLE_LOGGING debug_log("*** PIECE_FINISHED [ p: %d | chk: %s | size: %d ]" , j->piece, ((ret == 0) ?"passed":ret == -1 ?"disk failed":"failed") , m_torrent_file->piece_size(j->piece)); #endif TORRENT_ASSERT(valid_metadata()); // if we're a seed we don't have a picker // and we also don't have to do anything because // we already have this piece if (!has_picker() && m_have_all) return; need_picker(); TORRENT_ASSERT(!m_picker->have_piece(j->piece)); // picker().mark_as_done_checking(j->piece); state_updated(); // even though the piece passed the hash-check // it might still have failed being written to disk // if so, piece_picker::write_failed() has been // called, and the piece is no longer finished. // in this case, we have to ignore the fact that // it passed the check if (!m_picker->is_piece_finished(j->piece)) return; if (ret == 0) { // the following call may cause picker to become invalid // in case we just became a seed piece_passed(j->piece); // if we're in seed mode, we just acquired this piece // mark it as verified if (m_seed_mode) verified(j->piece); } else if (ret == -2) { // piece_failed() will restore the piece piece_failed(j->piece); } else { TORRENT_ASSERT(ret == -1); update_gauge(); } } // this is called once we have completely downloaded piece // 'index', its hash has been verified. It's also called // during initial file check when we find a piece whose hash // is correct void torrent::we_have(int index) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(!has_picker() || m_picker->has_piece_passed(index)); inc_stats_counter(counters::num_have_pieces); // at this point, we have the piece for sure. It has been // successfully written to disk. We may announce it to peers // (unless it has already been announced through predictive_piece_announce // feature). bool announce_piece = true; std::vector::iterator it = std::lower_bound(m_predictive_pieces.begin() , m_predictive_pieces.end(), index); if (it != m_predictive_pieces.end() && *it == index) { // this means we've already announced the piece announce_piece = false; m_predictive_pieces.erase(it); } // make a copy of the peer list since peers // may disconnect while looping std::vector peers = m_connections; for (peer_iterator i = peers.begin(); i != peers.end(); ++i) { boost::shared_ptr p = (*i)->self(); // received_piece will check to see if we're still interested // in this peer, and if neither of us is interested in the other, // disconnect it. p->received_piece(index); if (p->is_disconnecting()) continue; // if we're not announcing the piece, it means we // already have, and that we might have received // a request for it, and not sending it because // we were waiting to receive the piece, now that // we have received it, try to send stuff (fill_send_buffer) if (announce_piece) p->announce_piece(index); else p->fill_send_buffer(); } #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->on_piece_pass(index); } TORRENT_CATCH (std::exception&) {} } #endif // since this piece just passed, we might have // become uninterested in some peers where this // was the last piece we were interested in for (peer_iterator i = m_connections.begin(); i != m_connections.end();) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = *i; // update_interest may disconnect the peer and // invalidate the iterator ++i; // if we're not interested already, no need to check if (!p->is_interesting()) continue; // if the peer doesn't have the piece we just got, it // shouldn't affect our interest if (!p->has_piece(index)) continue; p->update_interest(); } if (settings().get_int(settings_pack::suggest_mode) == settings_pack::suggest_read_cache) { // we just got a new piece. Chances are that it's actually the // rarest piece (since we're likely to download pieces rarest first) // if it's rarer than any other piece that we currently suggest, insert // it in the suggest set and pop the last one out add_suggest_piece(index); } set_need_save_resume(); state_updated(); if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle(), index); // update m_file_progress (if we have one) m_file_progress.update(m_torrent_file->files(), index , &m_ses.alerts(), get_handle()); remove_time_critical_piece(index, true); if (is_downloading_state(m_state)) { if (is_finished() && m_state != torrent_status::finished && m_state != torrent_status::seeding) { // 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) finished(); // if we just became a seed, picker is now invalid, since it // is deallocated by the torrent once it starts seeding } m_last_download = total_seconds(clock_type::now().time_since_epoch()); if (m_share_mode) recalc_share_mode(); } } // this is called when the piece hash is checked as correct. Note // that the piece picker and the torrent won't necessarily consider // us to have this piece yet, since it might not have been flushed // to disk yet. Only if we have predictive_piece_announce on will // we announce this piece to peers at this point. void torrent::piece_passed(int index) { // INVARIANT_CHECK; TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(!m_picker->has_piece_passed(index)); #ifndef TORRENT_DISABLE_LOGGING debug_log("PIECE_PASSED (%d)", num_passed()); #endif // fprintf(stderr, "torrent::piece_passed piece:%d\n", index); TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < m_torrent_file->num_pieces()); set_need_save_resume(); inc_stats_counter(counters::num_piece_passed); remove_time_critical_piece(index, true); std::vector downloaders; m_picker->get_downloaders(downloaders, index); // increase the trust point of all peers that sent // parts of this piece. std::set peers; // these torrent_peer pointers are owned by m_peer_list and they may be // invalidated if a peer disconnects. We cannot keep them across any // significant operations, but we should use them right away // ignore NULL pointers std::remove_copy(downloaders.begin(), downloaders.end() , std::inserter(peers, peers.begin()), static_cast(0)); for (std::set::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { torrent_peer* p = static_cast(*i); TORRENT_ASSERT(p != 0); if (p == 0) continue; TORRENT_ASSERT(p->in_use); p->on_parole = false; int trust_points = p->trust_points; ++trust_points; if (trust_points > 8) trust_points = 8; p->trust_points = trust_points; if (p->connection) { peer_connection* peer = static_cast(p->connection); TORRENT_ASSERT(peer->m_in_use == 1337); peer->received_valid_data(index); } } // announcing a piece may invalidate the torrent_peer pointers // so we can't use them anymore downloaders.clear(); peers.clear(); // make the disk cache flush the piece to disk if (m_storage) m_ses.disk_thread().async_flush_piece(m_storage.get(), index); m_picker->piece_passed(index); update_gauge(); we_have(index); } // we believe we will complete this piece very soon // announce it to peers ahead of time to eliminate the // round-trip times involved in announcing it, requesting it // and sending it void torrent::predicted_have_piece(int index, int milliseconds) { std::vector::iterator i = std::lower_bound(m_predictive_pieces.begin() , m_predictive_pieces.end(), index); if (i != m_predictive_pieces.end() && *i == index) return; for (peer_iterator p = m_connections.begin() , end(m_connections.end()); p != end; ++p) { TORRENT_INCREMENT(m_iterating_connections); #ifndef TORRENT_DISABLE_LOGGING (*p)->peer_log(peer_log_alert::outgoing, "PREDICTIVE_HAVE", "piece: %d expected in %d ms" , index, milliseconds); #else TORRENT_UNUSED(milliseconds); #endif (*p)->announce_piece(index); } m_predictive_pieces.insert(i, index); } void torrent::piece_failed(int index) { // if the last piece fails the peer connection will still // think that it has received all of it until this function // resets the download queue. So, we cannot do the // invariant check here since it assumes: // (total_done == m_torrent_file->total_size()) => is_seed() INVARIANT_CHECK; TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_picker.get()); TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < m_torrent_file->num_pieces()); inc_stats_counter(counters::num_piece_failed); std::vector::iterator it = std::lower_bound(m_predictive_pieces.begin() , m_predictive_pieces.end(), index); if (it != m_predictive_pieces.end() && *it == index) { for (peer_iterator p = m_connections.begin() , end(m_connections.end()); p != end; ++p) { TORRENT_INCREMENT(m_iterating_connections); // send reject messages for // potential outstanding requests to this piece (*p)->reject_piece(index); // let peers that support the dont-have message // know that we don't actually have this piece (*p)->write_dont_have(index); } m_predictive_pieces.erase(it); } // increase the total amount of failed bytes add_failed_bytes(m_torrent_file->piece_size(index)); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->on_piece_failed(index); } TORRENT_CATCH (std::exception&) {} } #endif std::vector downloaders; if (m_picker) 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 peers; std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); #ifdef TORRENT_DEBUG for (std::vector::iterator i = downloaders.begin() , end(downloaders.end()); i != end; ++i) { torrent_peer* p = static_cast(*i); if (p && p->connection) { peer_connection* peer = static_cast(p->connection); peer->piece_failed = true; } } #endif // did we receive this piece from a single peer? bool single_peer = peers.size() == 1; for (std::set::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { torrent_peer* p = static_cast(*i); if (p == 0) continue; TORRENT_ASSERT(p->in_use); bool allow_disconnect = true; if (p->connection) { peer_connection* peer = static_cast(p->connection); TORRENT_ASSERT(peer->m_in_use == 1337); // the peer implementation can ask not to be disconnected. // this is used for web seeds for instance, to instead of // disconnecting, mark the file as not being haved. allow_disconnect = peer->received_invalid_data(index, single_peer); } if (settings().get_bool(settings_pack::use_parole_mode)) p->on_parole = true; int hashfails = p->hashfails; int trust_points = p->trust_points; // we decrease more than we increase, to keep the // allowed failed/passed ratio low. trust_points -= 2; ++hashfails; if (trust_points < -7) trust_points = -7; p->trust_points = trust_points; if (hashfails > 255) hashfails = 255; p->hashfails = hashfails; // either, we have received too many failed hashes // or this was the only peer that sent us this piece. // if we have failed more than 3 pieces from this peer, // don't trust it regardless. if (p->trust_points <= -7 || (single_peer && allow_disconnect)) { // we don't trust this peer anymore // ban it. if (m_ses.alerts().should_post()) { peer_id pid(0); if (p->connection) pid = p->connection->pid(); m_ses.alerts().emplace_alert( get_handle(), p->ip(), pid); } // mark the peer as banned ban_peer(p); update_want_peers(); inc_stats_counter(counters::banned_for_hash_failure); if (p->connection) { peer_connection* peer = static_cast(p->connection); #ifndef TORRENT_DISABLE_LOGGING debug_log("*** BANNING PEER: \"%s\" Too many corrupt pieces" , print_endpoint(p->ip()).c_str()); #endif #ifndef TORRENT_DISABLE_LOGGING peer->peer_log(peer_log_alert::info, "BANNING_PEER", "Too many corrupt pieces"); #endif peer->disconnect(errors::too_many_corrupt_pieces, op_bittorrent); } } } // If m_storage isn't set here, it means we're shutting down if (m_storage) { // it doesn't make much sense to fail to hash a piece // without having a storage associated with the torrent. // restoring the piece in the piece picker without calling // clear piece on the disk thread will make them out of // sync, and if we try to write more blocks to this piece // the disk thread will barf, because it hasn't been cleared TORRENT_ASSERT(m_storage); // don't allow picking any blocks from this piece // until we're done synchronizing with the disk threads. m_picker->lock_piece(index); // don't do this until after the plugins have had a chance // to read back the blocks that failed, for blame purposes // this way they have a chance to hit the cache m_ses.disk_thread().async_clear_piece(m_storage.get(), index , boost::bind(&torrent::on_piece_sync, shared_from_this(), _1)); } else { TORRENT_ASSERT(m_abort); // it doesn't really matter what we do // here, since we're about to destruct the // torrent anyway. disk_io_job j; j.piece = index; on_piece_sync(&j); } #ifdef TORRENT_DEBUG for (std::vector::iterator i = downloaders.begin() , end(downloaders.end()); i != end; ++i) { torrent_peer* p = *i; if (p && p->connection) { peer_connection* peer = static_cast(p->connection); peer->piece_failed = false; } } #endif } void torrent::peer_is_interesting(peer_connection& c) { INVARIANT_CHECK; // no peer should be interesting if we're finished TORRENT_ASSERT(!is_finished()); if (c.in_handshake()) return; c.send_interested(); if (c.has_peer_choked() && c.allowed_fast().empty()) return; if (request_a_block(*this, c)) inc_stats_counter(counters::interesting_piece_picks); c.send_block_requests(); } void torrent::on_piece_sync(disk_io_job const* j) { // the user may have called force_recheck, which clears // the piece picker if (!has_picker()) return; // unlock the piece and restore it, as if no block was // ever downloaded for it. m_picker->restore_piece(j->piece); if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle(), j->piece); // 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 TORRENT_ASSERT(m_picker->have_piece(j->piece) == false); // loop over all peers and re-request potential duplicate // blocks to this piece for (std::vector::iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = *i; std::vector const& dq = p->download_queue(); std::vector const& rq = p->request_queue(); for (std::vector::const_iterator k = dq.begin() , end2(dq.end()); k != end2; ++k) { if (k->timed_out || k->not_wanted) continue; if (int(k->block.piece_index) != j->piece) continue; m_picker->mark_as_downloading(k->block, p->peer_info_struct() , p->picker_options()); } for (std::vector::const_iterator k = rq.begin() , end2(rq.end()); k != end2; ++k) { if (int(k->block.piece_index) != j->piece) continue; m_picker->mark_as_downloading(k->block, p->peer_info_struct() , p->picker_options()); } } } void torrent::peer_has(int index, peer_connection const* peer) { if (has_picker()) { torrent_peer* pp = peer->peer_info_struct(); m_picker->inc_refcount(index, pp); update_suggest_piece(index, 1); } #ifdef TORRENT_DEBUG else { TORRENT_ASSERT(is_seed() || !m_have_all); } #endif } // when we get a bitfield message, this is called for that piece void torrent::peer_has(bitfield const& bits, peer_connection const* peer) { if (has_picker()) { TORRENT_ASSERT(bits.size() == torrent_file().num_pieces()); torrent_peer* pp = peer->peer_info_struct(); m_picker->inc_refcount(bits, pp); refresh_suggest_pieces(); } #ifdef TORRENT_DEBUG else { TORRENT_ASSERT(is_seed() || !m_have_all); } #endif } void torrent::peer_has_all(peer_connection const* peer) { if (has_picker()) { torrent_peer* pp = peer->peer_info_struct(); m_picker->inc_refcount_all(pp); } #ifdef TORRENT_DEBUG else { TORRENT_ASSERT(is_seed() || !m_have_all); } #endif } void torrent::peer_lost(bitfield const& bits, peer_connection const* peer) { if (has_picker()) { TORRENT_ASSERT(bits.size() == torrent_file().num_pieces()); torrent_peer* pp = peer->peer_info_struct(); m_picker->dec_refcount(bits, pp); // TODO: update suggest_piece? } #ifdef TORRENT_DEBUG else { TORRENT_ASSERT(is_seed() || !m_have_all); } #endif } void torrent::peer_lost(int index, peer_connection const* peer) { if (m_picker.get()) { torrent_peer* pp = peer->peer_info_struct(); m_picker->dec_refcount(index, pp); update_suggest_piece(index, -1); } #ifdef TORRENT_DEBUG else { TORRENT_ASSERT(is_seed() || !m_have_all); } #endif } void torrent::add_suggest_piece(int index) { // it would be nice if we would keep track of piece // availability even when we're a seed, for // the suggest piece feature if (!has_picker()) return; int num_peers = m_picker->get_availability(index); TORRENT_ASSERT(has_piece_passed(index)); // in order to avoid unnecessary churn in the suggested pieces // the new piece has to beat the existing piece by at least one // peer in availability. // m_suggested_pieces is sorted by rarity, the last element // should have the most peers (num_peers). if (m_suggested_pieces.empty() || num_peers < m_suggested_pieces[m_suggested_pieces.size()-1].num_peers - 1) { suggest_piece_t sp; sp.piece_index = index; sp.num_peers = num_peers; typedef std::vector::iterator iter; std::pair range = std::equal_range( m_suggested_pieces.begin(), m_suggested_pieces.end(), sp); // make sure this piece isn't already in the suggested set. // if it is, just ignore it iter i = std::find_if(range.first, range.second , boost::bind(&suggest_piece_t::piece_index, _1) == index); if (i != range.second) return; m_suggested_pieces.insert(range.second, sp); if (m_suggested_pieces.size() > 0) m_suggested_pieces.pop_back(); // tell all peers about this new suggested piece for (peer_iterator p = m_connections.begin() , end(m_connections.end()); p != end; ++p) { TORRENT_INCREMENT(m_iterating_connections); (*p)->send_suggest(index); } refresh_suggest_pieces(); } } void torrent::update_suggest_piece(int index, int change) { for (std::vector::iterator i = m_suggested_pieces.begin() , end(m_suggested_pieces.end()); i != end; ++i) { if (i->piece_index != index) continue; i->num_peers += change; if (change > 0) std::stable_sort(i, end); else if (change < 0) std::stable_sort(m_suggested_pieces.begin(), i + 1); } if (!m_suggested_pieces.empty() && m_suggested_pieces[0].num_peers > m_connections.size() * 2 / 3) { // the rarest piece we have in the suggest set is not very // rare anymore. at least 2/3 of the peers has it now. Refresh refresh_suggest_pieces(); } } void torrent::refresh_suggest_pieces() { m_need_suggest_pieces_refresh = true; } void torrent::do_refresh_suggest_pieces() { m_need_suggest_pieces_refresh = false; if (settings().get_int(settings_pack::suggest_mode) == settings_pack::no_piece_suggestions) return; if (!valid_metadata()) return; boost::shared_ptr t = shared_from_this(); TORRENT_ASSERT(t); cache_status cs; m_ses.disk_thread().get_cache_info(&cs, m_storage.get() == NULL, m_storage.get()); // remove write cache entries cs.pieces.erase(std::remove_if(cs.pieces.begin(), cs.pieces.end() , boost::bind(&cached_piece_info::kind, _1) == cached_piece_info::write_cache) , cs.pieces.end()); std::vector& pieces = m_suggested_pieces; pieces.clear(); pieces.reserve(cs.pieces.size()); // sort in ascending order, to get most recently used first std::sort(cs.pieces.begin(), cs.pieces.end() , boost::bind(&cached_piece_info::last_use, _1) > boost::bind(&cached_piece_info::last_use, _2)); for (std::vector::iterator i = cs.pieces.begin() , end(cs.pieces.end()); i != end; ++i) { TORRENT_ASSERT(i->storage == m_storage.get()); if (!has_piece_passed(i->piece)) continue; suggest_piece_t p; p.piece_index = i->piece; if (has_picker()) { p.num_peers = m_picker->get_availability(i->piece); } else { // TODO: really, we should just keep the picker around // in this case to maintain the availability counters p.num_peers = 0; for (const_peer_iterator j = m_connections.begin() , end2(m_connections.end()); j != end2; ++j) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* peer = *j; if (peer->has_piece(p.piece_index)) ++p.num_peers; } } pieces.push_back(p); } // sort by rarity (stable, to maintain sort // by last use) std::stable_sort(pieces.begin(), pieces.end()); // only suggest half of the pieces pieces.resize(pieces.size() / 2); // send new suggests to peers // the peers will filter out pieces we've // already suggested to them for (std::vector::iterator i = pieces.begin() , end(pieces.end()); i != end; ++i) { for (peer_iterator p = m_connections.begin(); p != m_connections.end(); ++p) { TORRENT_INCREMENT(m_iterating_connections); (*p)->send_suggest(i->piece_index); } } } void torrent::abort() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (m_abort) return; m_abort = true; update_want_peers(); update_want_tick(); update_want_scrape(); update_gauge(); stop_announcing(); if (m_peer_class > 0) { remove_class(m_ses.peer_classes(), m_peer_class); m_ses.peer_classes().decref(m_peer_class); m_peer_class = 0; } error_code ec; m_inactivity_timer.cancel(ec); #ifndef TORRENT_DISABLE_LOGGING log_to_all_peers("aborting"); #endif // disconnect all peers and close all // files belonging to the torrents disconnect_all(errors::torrent_aborted, op_bittorrent); // post a message to the main thread to destruct // the torrent object from there if (m_storage.get()) { m_ses.disk_thread().async_stop_torrent(m_storage.get() , boost::bind(&torrent::on_torrent_aborted, shared_from_this())); } else { TORRENT_ASSERT(m_abort); if (alerts().should_post()) alerts().emplace_alert(get_handle()); } // TODO: 2 abort lookups this torrent has made via the // session host resolver interface if (!m_apply_ip_filter) { inc_stats_counter(counters::non_filter_torrents, -1); m_apply_ip_filter = true; } m_allow_peers = false; m_auto_managed = false; update_state_list(); for (int i = 0; i < aux::session_interface::num_torrent_lists; ++i) { if (!m_links[i].in_list()) continue; m_links[i].unlink(m_ses.torrent_list(i), i); } // don't re-add this torrent to the state-update list m_state_subscription = false; } void torrent::super_seeding(bool on) { if (on == m_super_seeding) return; m_super_seeding = on; set_need_save_resume(); state_updated(); if (m_super_seeding) return; // disable super seeding for all peers for (peer_iterator i = begin(); i != end(); ++i) { (*i)->superseed_piece(-1, -1); } } int torrent::get_piece_to_super_seed(bitfield const& bits) { // return a piece with low availability that is not in // the bitfield and that is not currently being super // seeded by any peer TORRENT_ASSERT(m_super_seeding); if (!need_loaded()) return -1; // do a linear search from the first piece int min_availability = 9999; std::vector avail_vec; for (int i = 0; i < m_torrent_file->num_pieces(); ++i) { if (bits[i]) continue; int availability = 0; for (const_peer_iterator j = begin(); j != end(); ++j) { if ((*j)->super_seeded_piece(i)) { // avoid super-seeding the same piece to more than one // peer if we can avoid it. Do this by artificially // increase the availability availability = 999; break; } if ((*j)->has_piece(i)) ++availability; } if (availability > min_availability) continue; if (availability == min_availability) { avail_vec.push_back(i); continue; } TORRENT_ASSERT(availability < min_availability); min_availability = availability; avail_vec.clear(); avail_vec.push_back(i); } if (avail_vec.empty()) return -1; return avail_vec[random() % avail_vec.size()]; } void torrent::on_files_deleted(disk_io_job const* j) { TORRENT_ASSERT(is_single_thread()); dec_refcount("delete_files"); if (j->ret != 0) { if (alerts().should_post()) alerts().emplace_alert(get_handle() , j->error.ec, m_torrent_file->info_hash()); } else { alerts().emplace_alert(get_handle(), m_torrent_file->info_hash()); } } void torrent::on_save_resume_data(disk_io_job const* j) { TORRENT_ASSERT(is_single_thread()); torrent_ref_holder h(this, "save_resume"); dec_refcount("save_resume"); m_ses.done_async_resume(); if (!j->buffer.resume_data) { alerts().emplace_alert(get_handle(), j->error.ec); return; } if (!need_loaded()) { alerts().emplace_alert(get_handle() , m_error); return; } m_need_save_resume_data = false; m_last_saved_resume = m_ses.session_time(); write_resume_data(*j->buffer.resume_data); alerts().emplace_alert( boost::shared_ptr(j->buffer.resume_data), get_handle()); const_cast(j)->buffer.resume_data = 0; state_updated(); } void torrent::on_file_renamed(disk_io_job const* j) { TORRENT_ASSERT(is_single_thread()); dec_refcount("rename_file"); if (j->ret == 0) { if (alerts().should_post()) alerts().emplace_alert(get_handle() , j->buffer.string, j->piece); m_torrent_file->rename_file(j->piece, j->buffer.string); } else { if (alerts().should_post()) alerts().emplace_alert(get_handle() , j->piece, j->error.ec); } } void torrent::on_torrent_paused(disk_io_job const*) { TORRENT_ASSERT(is_single_thread()); if (alerts().should_post()) alerts().emplace_alert(get_handle()); } #ifndef TORRENT_NO_DEPRECATE std::string torrent::tracker_login() const { if (m_username.empty() && m_password.empty()) return ""; return m_username + ":" + m_password; } #endif boost::uint32_t torrent::tracker_key() const { uintptr_t self = reinterpret_cast(this); uintptr_t ses = reinterpret_cast(&m_ses); uintptr_t storage = reinterpret_cast(m_storage.get()); sha1_hash h = hasher(reinterpret_cast(&self), sizeof(self)) .update(reinterpret_cast(&storage), sizeof(storage)) .update(reinterpret_cast(&ses), sizeof(ses)) .final(); unsigned char const* ptr = &h[0]; return detail::read_uint32(ptr); } void torrent::cancel_non_critical() { std::set time_critical; for (std::vector::iterator i = m_time_critical_pieces.begin() , end(m_time_critical_pieces.end()); i != end; ++i) { time_critical.insert(i->piece); } for (std::vector::iterator i = m_connections.begin(), end(m_connections.end()); i != end; ++i) { TORRENT_INCREMENT(m_iterating_connections); // for each peer, go through its download and request queue // and cancel everything, except pieces that are time critical peer_connection* p = *i; std::vector dq = p->download_queue(); for (std::vector::iterator k = dq.begin() , end2(dq.end()); k != end2; ++k) { if (time_critical.count(k->block.piece_index)) continue; if (k->not_wanted || k->timed_out) continue; p->cancel_request(k->block, true); } std::vector rq = p->request_queue(); for (std::vector::const_iterator k = rq.begin() , end2(rq.end()); k != end2; ++k) { if (time_critical.count(k->block.piece_index)) continue; p->cancel_request(k->block, true); } } } void torrent::set_piece_deadline(int piece, int t, int flags) { INVARIANT_CHECK; TORRENT_ASSERT_PRECOND(piece >= 0); TORRENT_ASSERT_PRECOND(valid_metadata()); TORRENT_ASSERT_PRECOND(valid_metadata() && piece < m_torrent_file->num_pieces()); if (m_abort || !valid_metadata() || piece < 0 || piece >= m_torrent_file->num_pieces()) { // failed if (flags & torrent_handle::alert_when_available) { m_ses.alerts().emplace_alert( get_handle(), piece, error_code(boost::system::errc::operation_canceled, generic_category())); } return; } time_point deadline = aux::time_now() + milliseconds(t); // if we already have the piece, no need to set the deadline. // however, if the user asked to get the piece data back, we still // need to read it and post it back to the user if (is_seed() || (has_picker() && m_picker->has_piece_passed(piece))) { if (flags & torrent_handle::alert_when_available) read_piece(piece); return; } // if this is the first time critical piece we add. in order to make it // react quickly, cancel all the currently outstanding requests if (m_time_critical_pieces.empty()) { // defer this by posting it to the end of the message queue. // this gives the client a chance to specify multiple time critical // pieces before libtorrent cancels requests m_ses.get_io_service().post(boost::bind(&torrent::cancel_non_critical, this)); } for (std::vector::iterator i = m_time_critical_pieces.begin() , end(m_time_critical_pieces.end()); i != end; ++i) { if (i->piece != piece) continue; i->deadline = deadline; i->flags = flags; // resort i since deadline might have changed while (boost::next(i) != m_time_critical_pieces.end() && i->deadline > boost::next(i)->deadline) { std::iter_swap(i, boost::next(i)); ++i; } while (i != m_time_critical_pieces.begin() && i->deadline < boost::prior(i)->deadline) { std::iter_swap(i, boost::prior(i)); --i; } // just in case this piece had priority 0 int prev_prio = m_picker->piece_priority(piece); m_picker->set_piece_priority(piece, 7); if (prev_prio == 0) update_gauge(); return; } need_picker(); time_critical_piece p; p.first_requested = min_time(); p.last_requested = min_time(); p.flags = flags; p.deadline = deadline; p.peers = 0; p.piece = piece; std::vector::iterator critical_piece_it = std::upper_bound(m_time_critical_pieces.begin() , m_time_critical_pieces.end(), p); m_time_critical_pieces.insert(critical_piece_it, p); // just in case this piece had priority 0 int prev_prio = m_picker->piece_priority(piece); m_picker->set_piece_priority(piece, 7); if (prev_prio == 0) update_gauge(); piece_picker::downloading_piece pi; m_picker->piece_info(piece, pi); if (pi.requested == 0) return; // this means we have outstanding requests (or queued // up requests that haven't been sent yet). Promote them // to deadline pieces immediately std::vector downloaders; m_picker->get_downloaders(downloaders, piece); int block = 0; for (std::vector::iterator i = downloaders.begin() , end(downloaders.end()); i != end; ++i, ++block) { torrent_peer* tp = *i; if (tp == 0 || tp->connection == 0) continue; peer_connection* peer = static_cast(tp->connection); peer->make_time_critical(piece_block(piece, block)); } } void torrent::reset_piece_deadline(int piece) { remove_time_critical_piece(piece); } void torrent::remove_time_critical_piece(int piece, bool finished) { for (std::vector::iterator i = m_time_critical_pieces.begin(), end(m_time_critical_pieces.end()); i != end; ++i) { if (i->piece != piece) continue; if (finished) { if (i->flags & torrent_handle::alert_when_available) { read_piece(i->piece); } // if first_requested is min_time(), it wasn't requested as a critical piece // and we shouldn't adjust any average download times if (i->first_requested != min_time()) { // update the average download time and average // download time deviation int dl_time = total_milliseconds(aux::time_now() - i->first_requested); if (m_average_piece_time == 0) { m_average_piece_time = dl_time; } else { int diff = abs(int(dl_time - m_average_piece_time)); if (m_piece_time_deviation == 0) m_piece_time_deviation = diff; else m_piece_time_deviation = (m_piece_time_deviation * 9 + diff) / 10; m_average_piece_time = (m_average_piece_time * 9 + dl_time) / 10; } } } else if (i->flags & torrent_handle::alert_when_available) { // post an empty read_piece_alert to indicate it failed alerts().emplace_alert( get_handle(), piece, error_code(boost::system::errc::operation_canceled, generic_category())); } if (has_picker()) m_picker->set_piece_priority(piece, 1); m_time_critical_pieces.erase(i); return; } } void torrent::clear_time_critical() { for (std::vector::iterator i = m_time_critical_pieces.begin(); i != m_time_critical_pieces.end();) { if (i->flags & torrent_handle::alert_when_available) { // post an empty read_piece_alert to indicate it failed m_ses.alerts().emplace_alert( get_handle(), i->piece, error_code(boost::system::errc::operation_canceled, generic_category())); } if (has_picker()) m_picker->set_piece_priority(i->piece, 1); i = m_time_critical_pieces.erase(i); } } // remove time critical pieces where priority is 0 void torrent::remove_time_critical_pieces(std::vector const& priority) { for (std::vector::iterator i = m_time_critical_pieces.begin(); i != m_time_critical_pieces.end();) { if (priority[i->piece] == 0) { if (i->flags & torrent_handle::alert_when_available) { // post an empty read_piece_alert to indicate it failed alerts().emplace_alert( get_handle(), i->piece, error_code(boost::system::errc::operation_canceled, generic_category())); } i = m_time_critical_pieces.erase(i); continue; } ++i; } } void torrent::piece_availability(std::vector& avail) const { INVARIANT_CHECK; TORRENT_ASSERT(valid_metadata()); if (!has_picker()) { avail.clear(); return; } m_picker->get_availability(avail); } void torrent::set_piece_priority(int index, int priority) { // INVARIANT_CHECK; #ifndef TORRENT_DISABLE_LOGGING if (!valid_metadata()) { debug_log("*** SET_PIECE_PRIORITY [ idx: %d prio: %d ignored. " "no metadata yet ]", index, priority); } #endif if (!valid_metadata() || is_seed()) return; // this call is only valid on torrents with metadata TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < m_torrent_file->num_pieces()); if (index < 0 || index >= m_torrent_file->num_pieces()) return; need_picker(); bool was_finished = is_finished(); bool filter_updated = m_picker->set_piece_priority(index, priority); TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); update_gauge(); if (filter_updated) { update_peer_interest(was_finished); if (priority == 0) remove_time_critical_piece(index); } } int torrent::piece_priority(int index) const { // INVARIANT_CHECK; if (!has_picker()) return 4; // this call is only valid on torrents with metadata TORRENT_ASSERT(valid_metadata()); TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < m_torrent_file->num_pieces()); if (index < 0 || index >= m_torrent_file->num_pieces()) return 0; return m_picker->piece_priority(index); } void torrent::prioritize_piece_list(std::vector > const& pieces) { INVARIANT_CHECK; // this call is only valid on torrents with metadata TORRENT_ASSERT(valid_metadata()); if (is_seed()) return; need_picker(); bool filter_updated = false; bool was_finished = is_finished(); for (std::vector >::const_iterator i = pieces.begin() , end(pieces.end()); i != end; ++i) { TORRENT_ASSERT(i->second >= 0); TORRENT_ASSERT(i->second <= 7); TORRENT_ASSERT(i->first >= 0); TORRENT_ASSERT(i->first < m_torrent_file->num_pieces()); if (i->first < 0 || i->first >= m_torrent_file->num_pieces() || i->second < 0 || i->second > 7) continue; filter_updated |= m_picker->set_piece_priority(i->first, i->second); TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); } update_gauge(); if (filter_updated) { // we need to save this new state set_need_save_resume(); update_peer_interest(was_finished); } state_updated(); } void torrent::prioritize_pieces(std::vector const& pieces) { INVARIANT_CHECK; // this call is only valid on torrents with metadata TORRENT_ASSERT(valid_metadata()); if (is_seed()) return; if (!valid_metadata()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("*** PRIORITIZE_PIECES [ ignored. no metadata yet ]"); #endif return; } need_picker(); int index = 0; bool filter_updated = false; bool was_finished = is_finished(); for (std::vector::const_iterator i = pieces.begin() , end(pieces.end()); i != end; ++i, ++index) { TORRENT_ASSERT(*i >= 0); TORRENT_ASSERT(*i <= 7); filter_updated |= m_picker->set_piece_priority(index, *i); TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); } update_gauge(); update_want_tick(); if (filter_updated) { // we need to save this new state set_need_save_resume(); update_peer_interest(was_finished); remove_time_critical_pieces(pieces); } state_updated(); update_state_list(); } void torrent::piece_priorities(std::vector* pieces) const { INVARIANT_CHECK; // this call is only valid on torrents with metadata TORRENT_ASSERT(valid_metadata()); if (!has_picker()) { pieces->clear(); pieces->resize(m_torrent_file->num_pieces(), 4); return; } TORRENT_ASSERT(m_picker.get()); m_picker->piece_priorities(*pieces); } namespace { std::vector fix_priorities(std::vector const& input , file_storage const* fs) { std::vector files(input.begin(), input.end()); if (fs) files.resize(fs->num_files(), 4); for (int i = 0; i < int(files.size()); ++i) { // initialize pad files to priority 0 if (files[i] > 0 && fs && fs->pad_file_at(i)) files[i] = 0; else if (files[i] > 7) files[i] = 7; } return files; } void set_if_greater(int& piece_prio, int file_prio) { if (file_prio > piece_prio) piece_prio = file_prio; } } void torrent::on_file_priority(disk_io_job const* j) { dec_refcount("file_priority"); boost::scoped_ptr > p(j->buffer.priorities); if (m_file_priority != *p) { m_file_priority = *p; if (m_share_mode) recalc_share_mode(); } if (!j->error) return; // in this case, some file priorities failed to get set if (alerts().should_post()) alerts().emplace_alert(j->error.ec , resolve_filename(j->error.file), j->error.operation_str(), get_handle()); set_error(j->error.ec, j->error.file); pause(); } void torrent::prioritize_files(std::vector const& files) { INVARIANT_CHECK; std::vector const new_priority = fix_priorities(files , valid_metadata() ? &m_torrent_file->files() : NULL); // storage may be NULL during shutdown if (m_storage) { // the update of m_file_priority is deferred until the disk job comes // back, but to preserve sanity and consistency, the piece priorities are // updated immediately. If, on the off-chance, there's a disk failure, the // piece priorities still stay the same, but the file priorities are // possibly not fully updated. update_piece_priorities(new_priority); inc_refcount("file_priority"); m_ses.disk_thread().async_set_file_priority(m_storage.get() , new_priority, boost::bind(&torrent::on_file_priority, shared_from_this(), _1)); } else { m_file_priority = new_priority; } } void torrent::set_file_priority(int index, int prio) { INVARIANT_CHECK; // setting file priority on a torrent that doesn't have metadata yet is // similar to having passed in file priorities through add_torrent_params. // we store the priorities in m_file_priority until we get the metadata if (index < 0 || (valid_metadata() && index >= m_torrent_file->num_files())) { return; } if (prio < 0) prio = 0; else if (prio > 7) prio = 7; std::vector new_priority = m_file_priority; new_priority.resize(std::max(int(new_priority.size()), index + 1), 4); new_priority[index] = prio; // storage may be NULL during shutdown if (m_storage) { // the update of m_file_priority is deferred until the disk job comes // back, but to preserve sanity and consistency, the piece priorities are // updated immediately. If, on the off-chance, there's a disk failure, the // piece priorities still stay the same, but the file priorities are // possibly not fully updated. update_piece_priorities(new_priority); inc_refcount("file_priority"); m_ses.disk_thread().async_set_file_priority(m_storage.get() , new_priority, boost::bind(&torrent::on_file_priority, shared_from_this(), _1)); } else { m_file_priority = new_priority; } } int torrent::file_priority(int index) const { TORRENT_ASSERT_PRECOND(index >= 0); if (index < 0) return 0; // if we have metadata, perform additional checks if (valid_metadata()) { TORRENT_ASSERT_PRECOND(index < m_torrent_file->num_files()); if (index >= m_torrent_file->num_files()) return 0; // pad files always have priority 0 if (m_torrent_file->files().pad_file_at(index)) return 0; } // any unallocated slot is assumed to be 4 (normal priority) if (int(m_file_priority.size()) <= index) return 4; return m_file_priority[index]; } void torrent::file_priorities(std::vector* files) const { INVARIANT_CHECK; files->assign(m_file_priority.begin(), m_file_priority.end()); if (!valid_metadata()) { return; } files->resize(m_torrent_file->num_files(), 4); } void torrent::update_piece_priorities(std::vector const& file_prios) { INVARIANT_CHECK; if (m_torrent_file->num_pieces() == 0) return; bool need_update = false; boost::int64_t position = 0; int const piece_length = m_torrent_file->piece_length(); // initialize the piece priorities to 0, then only allow // setting higher priorities std::vector pieces(m_torrent_file->num_pieces(), 0); file_storage const& fs = m_torrent_file->files(); for (int i = 0; i < fs.num_files(); ++i) { if (i >= fs.num_files()) break; boost::int64_t const start = position; boost::int64_t const size = m_torrent_file->files().file_size(i); if (size == 0) continue; position += size; int file_prio; // pad files always have priority 0 if (fs.pad_file_at(i)) file_prio = 0; else if (file_prios.size() <= i) file_prio = 4; else file_prio = file_prios[i]; if (file_prio == 0) { // the pieces already start out as priority 0, no need to update // the pieces vector in this case need_update = true; continue; } // mark all pieces of the file with this file's priority // but only if the priority is higher than the pieces // already set (to avoid problems with overlapping pieces) int const start_piece = int(start / piece_length); int const last_piece = int((position - 1) / piece_length); TORRENT_ASSERT(last_piece < int(pieces.size())); // if one piece spans several files, we might // come here several times with the same start_piece, end_piece std::for_each(pieces.begin() + start_piece , pieces.begin() + last_piece + 1 , boost::bind(&set_if_greater, _1, file_prio)); need_update = true; } if (need_update) prioritize_pieces(pieces); } // this is called when piece priorities have been updated // updates the interested flag in peers void torrent::update_peer_interest(bool was_finished) { for (peer_iterator i = begin(); i != end();) { peer_connection* p = *i; // update_interest may disconnect the peer and // invalidate the iterator ++i; p->update_interest(); } if (!is_downloading_state(m_state)) { #ifndef TORRENT_DISABLE_LOGGING debug_log("*** UPDATE_PEER_INTEREST [ skipping, state: %d ]" , int(m_state)); #endif return; } #ifndef TORRENT_DISABLE_LOGGING debug_log("*** UPDATE_PEER_INTEREST [ finished: %d was_finished %d ]" , is_finished(), was_finished); #endif // the torrent just became finished if (is_finished() && !was_finished) { finished(); } else if (!is_finished() && was_finished) { // if we used to be finished, but we aren't anymore // we may need to connect to peers again resume_download(); } } void torrent::filter_piece(int index, bool filter) { INVARIANT_CHECK; TORRENT_ASSERT(valid_metadata()); if (is_seed()) return; need_picker(); // this call is only valid on torrents with metadata TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < m_torrent_file->num_pieces()); if (index < 0 || index >= m_torrent_file->num_pieces()) return; bool was_finished = is_finished(); m_picker->set_piece_priority(index, filter ? 1 : 0); update_peer_interest(was_finished); update_gauge(); } void torrent::filter_pieces(std::vector const& bitmask) { INVARIANT_CHECK; // this call is only valid on torrents with metadata TORRENT_ASSERT(valid_metadata()); if (is_seed()) return; need_picker(); bool was_finished = is_finished(); int index = 0; for (std::vector::const_iterator i = bitmask.begin() , end(bitmask.end()); i != end; ++i, ++index) { if ((m_picker->piece_priority(index) == 0) == *i) continue; if (*i) m_picker->set_piece_priority(index, 0); else m_picker->set_piece_priority(index, 1); } update_peer_interest(was_finished); update_gauge(); } bool torrent::is_piece_filtered(int index) const { // this call is only valid on torrents with metadata TORRENT_ASSERT(valid_metadata()); if (!has_picker()) return false; TORRENT_ASSERT(m_picker.get()); TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < m_torrent_file->num_pieces()); if (index < 0 || index >= m_torrent_file->num_pieces()) return true; return m_picker->piece_priority(index) == 0; } void torrent::filtered_pieces(std::vector& bitmask) const { INVARIANT_CHECK; // this call is only valid on torrents with metadata TORRENT_ASSERT(valid_metadata()); if (!has_picker()) { bitmask.clear(); bitmask.resize(m_torrent_file->num_pieces(), false); return; } TORRENT_ASSERT(m_picker.get()); m_picker->filtered_pieces(bitmask); } void torrent::filter_files(std::vector const& bitmask) { INVARIANT_CHECK; // this call is only valid on torrents with metadata if (!valid_metadata() || is_seed()) return; // the bitmask need to have exactly one bit for every file // in the torrent TORRENT_ASSERT(int(bitmask.size()) == m_torrent_file->num_files()); if (int(bitmask.size()) != m_torrent_file->num_files()) return; boost::int64_t 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 piece_filter(m_torrent_file->num_pieces(), true); for (int i = 0; i < int(bitmask.size()); ++i) { boost::int64_t start = position; position += m_torrent_file->files().file_size(i); // 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); } } namespace { bool has_empty_url(announce_entry const& e) { return e.url.empty(); } } void torrent::replace_trackers(std::vector const& urls) { m_trackers.clear(); std::remove_copy_if(urls.begin(), urls.end(), back_inserter(m_trackers) , &has_empty_url); m_last_working_tracker = -1; for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) { if (i->source == 0) i->source = announce_entry::source_client; i->complete_sent = is_seed(); } if (settings().get_bool(settings_pack::prefer_udp_trackers)) prioritize_udp_trackers(); if (!m_trackers.empty()) announce_with_tracker(); set_need_save_resume(); } void torrent::prioritize_udp_trackers() { // look for udp-trackers for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) { if (i->url.substr(0, 6) != "udp://") continue; // now, look for trackers with the same hostname // that is has higher priority than this one // if we find one, swap with the udp-tracker error_code ec; std::string udp_hostname; using boost::tuples::ignore; boost::tie(ignore, ignore, udp_hostname, ignore, ignore) = parse_url_components(i->url, ec); for (std::vector::iterator j = m_trackers.begin(); j != i; ++j) { std::string hostname; boost::tie(ignore, ignore, hostname, ignore, ignore) = parse_url_components(j->url, ec); if (hostname != udp_hostname) continue; if (j->url.substr(0, 6) == "udp://") continue; using std::swap; using std::iter_swap; swap(i->tier, j->tier); iter_swap(i, j); break; } } } bool torrent::add_tracker(announce_entry const& url) { std::vector::iterator k = std::find_if(m_trackers.begin() , m_trackers.end(), boost::bind(&announce_entry::url, _1) == url.url); if (k != m_trackers.end()) { k->source |= url.source; return false; } k = std::upper_bound(m_trackers.begin(), m_trackers.end(), url , boost::bind(&announce_entry::tier, _1) < boost::bind(&announce_entry::tier, _2)); if (k - m_trackers.begin() < m_last_working_tracker) ++m_last_working_tracker; k = m_trackers.insert(k, url); if (k->source == 0) k->source = announce_entry::source_client; if (m_announcing && !m_trackers.empty()) announce_with_tracker(); return true; } bool torrent::choke_peer(peer_connection& c) { INVARIANT_CHECK; TORRENT_ASSERT(!c.is_choked()); TORRENT_ASSERT(!c.ignore_unchoke_slots()); TORRENT_ASSERT(m_num_uploads > 0); if (!c.send_choke()) return false; --m_num_uploads; state_updated(); return true; } bool torrent::unchoke_peer(peer_connection& c, bool optimistic) { INVARIANT_CHECK; TORRENT_ASSERT(!m_graceful_pause_mode); TORRENT_ASSERT(c.is_choked()); TORRENT_ASSERT(!c.ignore_unchoke_slots()); // when we're unchoking the optimistic slots, we might // exceed the limit temporarily while we're iterating // over the peers if (m_num_uploads >= m_max_uploads && !optimistic) return false; if (!c.send_unchoke()) return false; ++m_num_uploads; state_updated(); return true; } void torrent::trigger_unchoke() { m_ses.get_io_service().dispatch(boost::bind( &aux::session_interface::trigger_unchoke, boost::ref(m_ses))); } void torrent::trigger_optimistic_unchoke() { m_ses.get_io_service().dispatch(boost::bind( &aux::session_interface::trigger_optimistic_unchoke, boost::ref(m_ses))); } void torrent::cancel_block(piece_block block) { INVARIANT_CHECK; for (peer_iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { TORRENT_INCREMENT(m_iterating_connections); (*i)->cancel_request(block); } } #ifdef TORRENT_USE_OPENSSL namespace { std::string password_callback(int length, boost::asio::ssl::context::password_purpose p , std::string pw) { TORRENT_UNUSED(length); if (p != boost::asio::ssl::context::for_reading) return ""; return pw; } } // certificate is a filename to a .pem file which is our // certificate. The certificate must be signed by the root // cert of the torrent file. any peer we connect to or that // connect to use must present a valid certificate signed // by the torrent root cert as well void torrent::set_ssl_cert(std::string const& certificate , std::string const& private_key , std::string const& dh_params , std::string const& passphrase) { if (!m_ssl_ctx) { if (alerts().should_post()) alerts().emplace_alert(get_handle() , errors::not_an_ssl_torrent, ""); return; } using boost::asio::ssl::context; error_code ec; m_ssl_ctx->set_password_callback(boost::bind(&password_callback, _1, _2, passphrase), ec); if (ec) { if (alerts().should_post()) alerts().emplace_alert(get_handle(), ec, ""); } m_ssl_ctx->use_certificate_file(certificate, context::pem, ec); if (ec) { if (alerts().should_post()) alerts().emplace_alert(get_handle(), ec, certificate); } #ifndef TORRENT_DISABLE_LOGGING debug_log("*** use certificate file: %s", ec.message().c_str()); #endif m_ssl_ctx->use_private_key_file(private_key, context::pem, ec); if (ec) { if (alerts().should_post()) alerts().emplace_alert(get_handle(), ec, private_key); } #ifndef TORRENT_DISABLE_LOGGING debug_log("*** use private key file: %s", ec.message().c_str()); #endif m_ssl_ctx->use_tmp_dh_file(dh_params, ec); if (ec) { if (alerts().should_post()) alerts().emplace_alert(get_handle(), ec, dh_params); } #ifndef TORRENT_DISABLE_LOGGING debug_log("*** use DH file: %s", ec.message().c_str()); #endif } void torrent::set_ssl_cert_buffer(std::string const& certificate , std::string const& private_key , std::string const& dh_params) { if (!m_ssl_ctx) return; #if BOOST_VERSION < 105400 if (alerts().should_post()) alerts().emplace_alert(get_handle() , error_code(boost::system::errc::not_supported, generic_category()), "[certificate]"); #else boost::asio::const_buffer certificate_buf(certificate.c_str(), certificate.size()); using boost::asio::ssl::context; error_code ec; m_ssl_ctx->use_certificate(certificate_buf, context::pem, ec); if (ec) { if (alerts().should_post()) alerts().emplace_alert(get_handle(), ec, "[certificate]"); } boost::asio::const_buffer private_key_buf(private_key.c_str(), private_key.size()); m_ssl_ctx->use_private_key(private_key_buf, context::pem, ec); if (ec) { if (alerts().should_post()) alerts().emplace_alert(get_handle(), ec, "[private key]"); } boost::asio::const_buffer dh_params_buf(dh_params.c_str(), dh_params.size()); m_ssl_ctx->use_tmp_dh(dh_params_buf, ec); if (ec) { if (alerts().should_post()) alerts().emplace_alert(get_handle(), ec, "[dh params]"); } #endif // BOOST_VERSION } #endif void torrent::remove_peer(peer_connection* p) { TORRENT_ASSERT(p != 0); TORRENT_ASSERT(is_single_thread()); peer_iterator i = sorted_find(m_connections, p); if (i == m_connections.end()) { TORRENT_ASSERT(false); return; } torrent_peer* pp = p->peer_info_struct(); if (ready_for_connections()) { TORRENT_ASSERT(p->associated_torrent().lock().get() == NULL || p->associated_torrent().lock().get() == this); if (p->is_seed()) { if (has_picker()) { m_picker->dec_refcount_all(pp); } } else { if (has_picker()) { bitfield const& pieces = p->get_bitfield(); TORRENT_ASSERT(pieces.count() <= int(pieces.size())); m_picker->dec_refcount(pieces, pp); } } } if (!p->is_choked() && !p->ignore_unchoke_slots()) { --m_num_uploads; trigger_unchoke(); } if (pp) { if (pp->optimistically_unchoked) { pp->optimistically_unchoked = false; m_stats_counters.inc_stats_counter( counters::num_peers_up_unchoked_optimistic, -1); trigger_optimistic_unchoke(); } TORRENT_ASSERT(pp->prev_amount_upload == 0); TORRENT_ASSERT(pp->prev_amount_download == 0); pp->prev_amount_download += p->statistics().total_payload_download() >> 10; pp->prev_amount_upload += p->statistics().total_payload_upload() >> 10; if (pp->seed) { TORRENT_ASSERT(m_num_seeds > 0); --m_num_seeds; } } torrent_state st = get_peer_list_state(); if (m_peer_list) m_peer_list->connection_closed(*p, m_ses.session_time(), &st); peers_erased(st.erased); p->set_peer_info(0); TORRENT_ASSERT(m_iterating_connections == 0); TORRENT_ASSERT(i != m_connections.end()); m_connections.erase(i); if (m_graceful_pause_mode && m_connections.empty()) { // we're in graceful pause mode and this was the last peer we // disconnected. This will clear the graceful_pause_mode and post the // torrent_paused_alert. TORRENT_ASSERT(is_paused()); // this will post torrent_paused alert set_allow_peers(false); } update_want_peers(); update_want_tick(); } void torrent::remove_web_seed(std::list::iterator web) { if (web->resolving) { web->removed = true; } else { #ifndef TORRENT_DISABLE_LOGGING debug_log("removing web seed: \"%s\"", web->url.c_str()); #endif peer_connection* peer = static_cast(web->peer_info.connection); if (peer) { // if we have a connection for this web seed, we also need to // disconnect it and clear its reference to the peer_info object // that's part of the web_seed_t we're about to remove TORRENT_ASSERT(peer->m_in_use == 1337); peer->disconnect(boost::asio::error::operation_aborted, op_bittorrent); peer->set_peer_info(0); } if (has_picker()) picker().clear_peer(&web->peer_info); m_web_seeds.erase(web); } update_want_tick(); } void torrent::connect_to_url_seed(std::list::iterator web) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(!web->resolving); if (web->resolving) return; if (int(m_connections.size()) >= m_max_connections || m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit)) return; std::string protocol; std::string auth; std::string hostname; int port; std::string path; error_code ec; boost::tie(protocol, auth, hostname, port, path) = parse_url_components(web->url, ec); if (port == -1) { port = protocol == "http" ? 80 : 443; } if (ec) { #ifndef TORRENT_DISABLE_LOGGING debug_log("failed to parse web seed url: %s", ec.message().c_str()); #endif if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle() , web->url, ec); } // never try it again remove_web_seed(web); return; } if (web->peer_info.banned) { #ifndef TORRENT_DISABLE_LOGGING debug_log("banned web seed: %s", web->url.c_str()); #endif if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle(), web->url , libtorrent::errors::peer_banned); } // never try it again remove_web_seed(web); return; } #ifdef TORRENT_USE_OPENSSL if (protocol != "http" && protocol != "https") #else if (protocol != "http") #endif { if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle(), web->url, errors::unsupported_url_protocol); } // never try it again remove_web_seed(web); return; } if (hostname.empty()) { if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle(), web->url , errors::invalid_hostname); } // never try it again remove_web_seed(web); return; } if (port == 0) { if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle(), web->url , errors::invalid_port); } // never try it again remove_web_seed(web); return; } if (m_ses.get_port_filter().access(port) & port_filter::blocked) { if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle() , web->url, errors::port_blocked); } // never try it again remove_web_seed(web); return; } if (!web->endpoints.empty()) { connect_web_seed(web, web->endpoints.front()); return; } aux::proxy_settings const& ps = m_ses.proxy(); if ((ps.type == settings_pack::http || ps.type == settings_pack::http_pw) && ps.proxy_peer_connections) { #ifndef TORRENT_DISABLE_LOGGING debug_log("resolving proxy for web seed: %s", web->url.c_str()); #endif // use proxy web->resolving = true; m_ses.async_resolve(ps.hostname, resolver_interface::abort_on_shutdown , boost::bind(&torrent::on_proxy_name_lookup, shared_from_this() , _1, _2, web, ps.port)); } else if (ps.proxy_hostnames && (ps.type == settings_pack::socks5 || ps.type == settings_pack::socks5_pw) && ps.proxy_peer_connections) { connect_web_seed(web, tcp::endpoint(address(), port)); } else { #ifndef TORRENT_DISABLE_LOGGING debug_log("resolving web seed: \"%s\" %s", hostname.c_str(), web->url.c_str()); #endif web->resolving = true; m_ses.async_resolve(hostname, resolver_interface::abort_on_shutdown , boost::bind(&torrent::on_name_lookup, shared_from_this(), _1, _2 , port, web)); } } void torrent::on_proxy_name_lookup(error_code const& e , std::vector
const& addrs , std::list::iterator web, int port) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(web->resolving == true); #ifndef TORRENT_DISABLE_LOGGING debug_log("completed resolve proxy hostname for: %s", web->url.c_str()); if (e) debug_log("proxy name lookup error: %s", e.message().c_str()); #endif web->resolving = false; if (web->removed) { #ifndef TORRENT_DISABLE_LOGGING debug_log("removed web seed"); #endif remove_web_seed(web); return; } if (m_abort) return; if (e || addrs.empty()) { if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle() , web->url, e); } // the name lookup failed for the http host. Don't try // this host again remove_web_seed(web); return; } if (m_ses.is_aborted()) return; if (int(m_connections.size()) >= m_max_connections || m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit)) return; tcp::endpoint a(addrs[0], port); using boost::tuples::ignore; std::string hostname; error_code ec; std::string protocol; boost::tie(protocol, ignore, hostname, port, ignore) = parse_url_components(web->url, ec); if (port == -1) port = protocol == "http" ? 80 : 443; if (ec) { if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle() , web->url, ec); } remove_web_seed(web); return; } if (m_ip_filter && m_ip_filter->access(a.address()) & ip_filter::blocked) { if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle() , a.address(), peer_blocked_alert::ip_filter); return; } web->resolving = true; m_ses.async_resolve(hostname, resolver_interface::abort_on_shutdown , boost::bind(&torrent::on_name_lookup, shared_from_this(), _1, _2 , port, web)); } void torrent::on_name_lookup(error_code const& e , std::vector
const& addrs , int port , std::list::iterator web) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_ASSERT(web->resolving == true); #ifndef TORRENT_DISABLE_LOGGING debug_log("completed resolve: %s", web->url.c_str()); #endif web->resolving = false; if (web->removed) { #ifndef TORRENT_DISABLE_LOGGING debug_log("removed web seed"); #endif remove_web_seed(web); return; } if (m_abort) return; if (e || addrs.empty()) { if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle(), web->url, e); #ifndef TORRENT_DISABLE_LOGGING debug_log("*** HOSTNAME LOOKUP FAILED: %s: (%d) %s" , web->url.c_str(), e.value(), e.message().c_str()); #endif // unavailable, retry in `settings_pack::web_seed_name_lookup_retry` seconds web->retry = aux::time_now() + seconds(settings().get_int(settings_pack::web_seed_name_lookup_retry)); return; } for (std::vector
::const_iterator i = addrs.begin() , end(addrs.end()); i != end; ++i) { // fill in the peer struct's address field web->endpoints.push_back(tcp::endpoint(*i, port)); #ifndef TORRENT_DISABLE_LOGGING debug_log(" -> %s", print_endpoint(tcp::endpoint(*i, port)).c_str()); #endif } if (int(m_connections.size()) >= m_max_connections || m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit)) return; connect_web_seed(web, web->endpoints.front()); } void torrent::connect_web_seed(std::list::iterator web, tcp::endpoint a) { INVARIANT_CHECK; TORRENT_ASSERT(is_single_thread()); if (m_abort) return; if (m_ip_filter && m_ip_filter->access(a.address()) & ip_filter::blocked) { if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle() , a.address(), peer_blocked_alert::ip_filter); return; } TORRENT_ASSERT(web->resolving == false); TORRENT_ASSERT(web->peer_info.connection == 0); if (a.address().is_v4()) { web->peer_info.addr = a.address().to_v4(); web->peer_info.port = a.port(); } if (is_paused()) return; if (m_ses.is_aborted()) return; if (is_upload_only()) return; boost::shared_ptr s = boost::make_shared(boost::ref(m_ses.get_io_service())); if (!s) return; void* userdata = 0; #ifdef TORRENT_USE_OPENSSL const bool ssl = string_begins_no_case("https://", web->url.c_str()); if (ssl) { userdata = m_ssl_ctx.get(); if (!userdata) userdata = m_ses.ssl_ctx(); } #endif bool ret = instantiate_connection(m_ses.get_io_service(), m_ses.proxy() , *s, userdata, 0, true, false); (void)ret; TORRENT_ASSERT(ret); if (s->get()) { // the web seed connection will talk immediately to // the proxy, without requiring CONNECT support s->get()->set_no_connect(true); } using boost::tuples::ignore; std::string hostname; error_code ec; boost::tie(ignore, ignore, hostname, ignore, ignore) = parse_url_components(web->url, ec); if (ec) { if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle(), web->url, ec); return; } bool const is_ip = is_ip_address(hostname.c_str()); if (is_ip) a.address(address::from_string(hostname.c_str(), ec)); bool const proxy_hostnames = settings().get_bool(settings_pack::proxy_hostnames) && !is_ip; if (proxy_hostnames && (s->get() #ifdef TORRENT_USE_OPENSSL || s->get >() #endif )) { // we're using a socks proxy and we're resolving // hostnames through it socks5_stream* str = #ifdef TORRENT_USE_OPENSSL ssl ? &s->get >()->next_layer() : #endif s->get(); TORRENT_ASSERT_VAL(str, s->type_name()); str->set_dst_name(hostname); } setup_ssl_hostname(*s, hostname, ec); if (ec) { if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle(), web->url, ec); return; } boost::shared_ptr c; peer_connection_args pack; pack.ses = &m_ses; pack.sett = &settings(); pack.stats_counters = &m_ses.stats_counters(); pack.allocator = &m_ses; pack.disk_thread = &m_ses.disk_thread(); pack.ios = &m_ses.get_io_service(); pack.tor = shared_from_this(); pack.s = s; pack.endp = a; pack.peerinfo = &web->peer_info; if (web->type == web_seed_entry::url_seed) { c = boost::make_shared( boost::cref(pack), boost::ref(*web)); } else if (web->type == web_seed_entry::http_seed) { c = boost::make_shared( boost::cref(pack), boost::ref(*web)); } if (!c) return; #if TORRENT_USE_ASSERTS c->m_in_constructor = false; #endif #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { boost::shared_ptr pp((*i)->new_connection(peer_connection_handle(c.get()->self()))); if (pp) c->add_extension(pp); } #endif TORRENT_TRY { TORRENT_ASSERT(!c->m_in_constructor); // add the newly connected peer to this torrent's peer list TORRENT_ASSERT(m_iterating_connections == 0); sorted_insert(m_connections, boost::get_pointer(c)); update_want_peers(); update_want_tick(); m_ses.insert_peer(c); if (web->peer_info.seed) { TORRENT_ASSERT(m_num_seeds < 0xffff); ++m_num_seeds; } TORRENT_ASSERT(!web->peer_info.connection); web->peer_info.connection = c.get(); #if TORRENT_USE_ASSERTS web->peer_info.in_use = true; #endif c->add_stat(boost::int64_t(web->peer_info.prev_amount_download) << 10 , boost::int64_t(web->peer_info.prev_amount_upload) << 10); web->peer_info.prev_amount_download = 0; web->peer_info.prev_amount_upload = 0; #ifndef TORRENT_DISABLE_LOGGING debug_log("web seed connection started: [%s] %s" , print_endpoint(a).c_str(), web->url.c_str()); #endif c->start(); if (c->is_disconnecting()) return; #ifndef TORRENT_DISABLE_LOGGING debug_log("START queue peer [%p] (%d)", static_cast(c.get()) , num_peers()); #endif } TORRENT_CATCH (std::exception& e) { TORRENT_DECLARE_DUMMY(std::exception, e); (void)e; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** PEER_ERROR: %s", e.what()); #endif c->disconnect(errors::no_error, op_bittorrent, 1); } } #ifndef TORRENT_NO_DEPRECATE // deprecated in 1.1 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES namespace { unsigned long swap_bytes(unsigned long a) { return (a >> 24) | ((a & 0xff0000) >> 8) | ((a & 0xff00) << 8) | ((a & 0xff) << 24); } } void torrent::resolve_countries(bool r) { m_resolve_countries = r; } bool torrent::resolving_countries() const { return m_resolve_countries && !settings().get_bool(settings_pack::force_proxy); } void torrent::resolve_peer_country(boost::shared_ptr const& p) const { TORRENT_ASSERT(is_single_thread()); if (m_resolving_country || is_local(p->remote().address()) || p->has_country() || p->is_connecting() || p->in_handshake() || p->remote().address().is_v6()) return; boost::asio::ip::address_v4 reversed(swap_bytes(p->remote().address().to_v4().to_ulong())); error_code ec; std::string hostname = reversed.to_string(ec) + ".zz.countries.nerd.dk"; if (ec) { p->set_country("!!"); return; } m_resolving_country = true; m_ses.async_resolve(hostname, resolver_interface::abort_on_shutdown , boost::bind(&torrent::on_country_lookup, shared_from_this(), _1, _2, p)); } namespace { struct country_entry { int code; char const* name; }; } void torrent::on_country_lookup(error_code const& error , std::vector
const& host_list , boost::shared_ptr p) const { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; m_resolving_country = false; if (m_abort) return; // must be ordered in increasing order static const country_entry country_map[] = { { 4, "AF"}, { 8, "AL"}, { 10, "AQ"}, { 12, "DZ"}, { 16, "AS"} , { 20, "AD"}, { 24, "AO"}, { 28, "AG"}, { 31, "AZ"}, { 32, "AR"} , { 36, "AU"}, { 40, "AT"}, { 44, "BS"}, { 48, "BH"}, { 50, "BD"} , { 51, "AM"}, { 52, "BB"}, { 56, "BE"}, { 60, "BM"}, { 64, "BT"} , { 68, "BO"}, { 70, "BA"}, { 72, "BW"}, { 74, "BV"}, { 76, "BR"} , { 84, "BZ"}, { 86, "IO"}, { 90, "SB"}, { 92, "VG"}, { 96, "BN"} , {100, "BG"}, {104, "MM"}, {108, "BI"}, {112, "BY"}, {116, "KH"} , {120, "CM"}, {124, "CA"}, {132, "CV"}, {136, "KY"}, {140, "CF"} , {144, "LK"}, {148, "TD"}, {152, "CL"}, {156, "CN"}, {158, "TW"} , {162, "CX"}, {166, "CC"}, {170, "CO"}, {174, "KM"}, {175, "YT"} , {178, "CG"}, {180, "CD"}, {184, "CK"}, {188, "CR"}, {191, "HR"} , {192, "CU"}, {203, "CZ"}, {204, "BJ"}, {208, "DK"}, {212, "DM"} , {214, "DO"}, {218, "EC"}, {222, "SV"}, {226, "GQ"}, {231, "ET"} , {232, "ER"}, {233, "EE"}, {234, "FO"}, {238, "FK"}, {239, "GS"} , {242, "FJ"}, {246, "FI"}, {248, "AX"}, {250, "FR"}, {254, "GF"} , {258, "PF"}, {260, "TF"}, {262, "DJ"}, {266, "GA"}, {268, "GE"} , {270, "GM"}, {275, "PS"}, {276, "DE"}, {288, "GH"}, {292, "GI"} , {296, "KI"}, {300, "GR"}, {304, "GL"}, {308, "GD"}, {312, "GP"} , {316, "GU"}, {320, "GT"}, {324, "GN"}, {328, "GY"}, {332, "HT"} , {334, "HM"}, {336, "VA"}, {340, "HN"}, {344, "HK"}, {348, "HU"} , {352, "IS"}, {356, "IN"}, {360, "ID"}, {364, "IR"}, {368, "IQ"} , {372, "IE"}, {376, "IL"}, {380, "IT"}, {384, "CI"}, {388, "JM"} , {392, "JP"}, {398, "KZ"}, {400, "JO"}, {404, "KE"}, {408, "KP"} , {410, "KR"}, {414, "KW"}, {417, "KG"}, {418, "LA"}, {422, "LB"} , {426, "LS"}, {428, "LV"}, {430, "LR"}, {434, "LY"}, {438, "LI"} , {440, "LT"}, {442, "LU"}, {446, "MO"}, {450, "MG"}, {454, "MW"} , {458, "MY"}, {462, "MV"}, {466, "ML"}, {470, "MT"}, {474, "MQ"} , {478, "MR"}, {480, "MU"}, {484, "MX"}, {492, "MC"}, {496, "MN"} , {498, "MD"}, {500, "MS"}, {504, "MA"}, {508, "MZ"}, {512, "OM"} , {516, "NA"}, {520, "NR"}, {524, "NP"}, {528, "NL"}, {530, "AN"} , {533, "AW"}, {540, "NC"}, {548, "VU"}, {554, "NZ"}, {558, "NI"} , {562, "NE"}, {566, "NG"}, {570, "NU"}, {574, "NF"}, {578, "NO"} , {580, "MP"}, {581, "UM"}, {583, "FM"}, {584, "MH"}, {585, "PW"} , {586, "PK"}, {591, "PA"}, {598, "PG"}, {600, "PY"}, {604, "PE"} , {608, "PH"}, {612, "PN"}, {616, "PL"}, {620, "PT"}, {624, "GW"} , {626, "TL"}, {630, "PR"}, {634, "QA"}, {634, "QA"}, {638, "RE"} , {642, "RO"}, {643, "RU"}, {646, "RW"}, {654, "SH"}, {659, "KN"} , {660, "AI"}, {662, "LC"}, {666, "PM"}, {670, "VC"}, {674, "SM"} , {678, "ST"}, {682, "SA"}, {686, "SN"}, {690, "SC"}, {694, "SL"} , {702, "SG"}, {703, "SK"}, {704, "VN"}, {705, "SI"}, {706, "SO"} , {710, "ZA"}, {716, "ZW"}, {724, "ES"}, {732, "EH"}, {736, "SD"} , {740, "SR"}, {744, "SJ"}, {748, "SZ"}, {752, "SE"}, {756, "CH"} , {760, "SY"}, {762, "TJ"}, {764, "TH"}, {768, "TG"}, {772, "TK"} , {776, "TO"}, {780, "TT"}, {784, "AE"}, {788, "TN"}, {792, "TR"} , {795, "TM"}, {796, "TC"}, {798, "TV"}, {800, "UG"}, {804, "UA"} , {807, "MK"}, {818, "EG"}, {826, "GB"}, {834, "TZ"}, {840, "US"} , {850, "VI"}, {854, "BF"}, {858, "UY"}, {860, "UZ"}, {862, "VE"} , {876, "WF"}, {882, "WS"}, {887, "YE"}, {891, "CS"}, {894, "ZM"} }; if (error || host_list.empty()) { // this is used to indicate that we shouldn't // try to resolve it again p->set_country("--"); return; } int idx = 0; while (idx < host_list.size() && !host_list[idx].is_v4()) ++idx; if (idx >= host_list.size()) { p->set_country("--"); return; } // country is an ISO 3166 country code int country = host_list[idx].to_v4().to_ulong() & 0xffff; // look up the country code in the map const int size = sizeof(country_map)/sizeof(country_map[0]); country_entry tmp = {country, ""}; country_entry const* j = std::lower_bound(country_map, country_map + size, tmp , boost::bind(&country_entry::code, _1) < boost::bind(&country_entry::code, _2)); if (j == country_map + size || j->code != country) { // unknown country! p->set_country("!!"); #ifndef TORRENT_DISABLE_LOGGING debug_log("IP \"%s\" was mapped to unknown country: %d" , print_address(p->remote().address()).c_str(), country); #endif return; } p->set_country(j->name); } #endif #endif // TORRENT_NO_DEPRECATE void torrent::read_resume_data(bdecode_node const& rd) { m_total_uploaded = rd.dict_find_int_value("total_uploaded"); m_total_downloaded = rd.dict_find_int_value("total_downloaded"); m_active_time = rd.dict_find_int_value("active_time"); m_finished_time = rd.dict_find_int_value("finished_time"); m_seeding_time = rd.dict_find_int_value("seeding_time"); m_last_seen_complete = rd.dict_find_int_value("last_seen_complete"); m_complete = rd.dict_find_int_value("num_complete", 0xffffff); m_incomplete = rd.dict_find_int_value("num_incomplete", 0xffffff); m_downloaded = rd.dict_find_int_value("num_downloaded", 0xffffff); if (!m_override_resume_data) { int up_limit_ = rd.dict_find_int_value("upload_rate_limit", -1); if (up_limit_ != -1) set_upload_limit(up_limit_); int down_limit_ = rd.dict_find_int_value("download_rate_limit", -1); if (down_limit_ != -1) set_download_limit(down_limit_); int max_connections_ = rd.dict_find_int_value("max_connections", -1); if (max_connections_ != -1) set_max_connections(max_connections_); int max_uploads_ = rd.dict_find_int_value("max_uploads", -1); if (max_uploads_ != -1) set_max_uploads(max_uploads_); int seed_mode_ = rd.dict_find_int_value("seed_mode", -1); if (seed_mode_ != -1) m_seed_mode = seed_mode_ && m_torrent_file->is_valid(); int super_seeding_ = rd.dict_find_int_value("super_seeding", -1); if (super_seeding_ != -1) super_seeding(super_seeding_ != 0); int auto_managed_ = rd.dict_find_int_value("auto_managed", -1); if (auto_managed_ != -1) { m_auto_managed = auto_managed_ != 0; update_want_scrape(); update_state_list(); } int sequential_ = rd.dict_find_int_value("sequential_download", -1); if (sequential_ != -1) set_sequential_download(sequential_ != 0); int paused_ = rd.dict_find_int_value("paused", -1); if (paused_ != -1) { set_allow_peers(paused_ == 0); m_announce_to_dht = (paused_ == 0); m_announce_to_trackers = (paused_ == 0); m_announce_to_lsd = (paused_ == 0); update_gauge(); update_want_peers(); update_want_scrape(); update_state_list(); } int dht_ = rd.dict_find_int_value("announce_to_dht", -1); if (dht_ != -1) m_announce_to_dht = (dht_ != 0); int lsd_ = rd.dict_find_int_value("announce_to_lsd", -1); if (lsd_ != -1) m_announce_to_lsd = (lsd_ != 0); int track_ = rd.dict_find_int_value("announce_to_trackers", -1); if (track_ != -1) m_announce_to_trackers = (track_ != 0); #ifndef TORRENT_DISABLE_LOGGING debug_log("loaded resume data: max-uploads: %d max-connections: %d " "upload-limit: %d download-limit: %d paused: %d sequential-download: %d " "super-seeding: %d auto-managed: %d" , max_uploads_, max_connections_, up_limit_, down_limit_ , paused_, sequential_, super_seeding_, auto_managed_); #endif } boost::int64_t tmp = rd.dict_find_int_value("last_scrape", -1); m_last_scrape = tmp == -1 ? unset : tmp; tmp = rd.dict_find_int_value("last_download", -1); m_last_download = tmp == -1 ? unset : tmp; tmp = rd.dict_find_int_value("last_upload", -1); m_last_upload = tmp == -1 ? unset : tmp; if (m_use_resume_save_path) { std::string p = rd.dict_find_string_value("save_path"); if (!p.empty()) { m_save_path = p; #ifndef TORRENT_DISABLE_LOGGING debug_log("loaded resume data: save-path: %s", m_save_path.c_str()); #endif } } m_url = rd.dict_find_string_value("url"); m_uuid = rd.dict_find_string_value("uuid"); m_source_feed_url = rd.dict_find_string_value("feed"); if (!m_uuid.empty() || !m_url.empty()) { boost::shared_ptr me(shared_from_this()); // insert this torrent in the uuid index m_ses.insert_uuid_torrent(m_uuid.empty() ? m_url : m_uuid, me); } // TODO: make this more generic to not just work if files have been // renamed, but also if they have been merged into a single file for instance // maybe use the same format as .torrent files and reuse some code from torrent_info // The mapped_files needs to be read both in the network thread // and in the disk thread, since they both have their own mapped files structures // which are kept in sync bdecode_node mapped_files = rd.dict_find_list("mapped_files"); if (mapped_files && mapped_files.list_size() == m_torrent_file->num_files()) { for (int i = 0; i < m_torrent_file->num_files(); ++i) { std::string new_filename = mapped_files.list_string_value_at(i); if (new_filename.empty()) continue; m_torrent_file->rename_file(i, new_filename); } } m_added_time = rd.dict_find_int_value("added_time", m_added_time); m_completed_time = rd.dict_find_int_value("completed_time", m_completed_time); if (m_completed_time != 0 && m_completed_time < m_added_time) m_completed_time = m_added_time; if (m_file_priority.empty()) { bdecode_node file_priority = rd.dict_find_list("file_priority"); if (file_priority) { const int num_files = (std::min)(file_priority.list_size() , m_torrent_file->num_files()); m_file_priority.resize(num_files, 4); for (int i = 0; i < num_files; ++i) { m_file_priority[i] = file_priority.list_int_value_at(i, 1); // this is suspicious, leave seed mode if (m_file_priority[i] == 0) m_seed_mode = false; } // unallocated slots are assumed to be priority 1, so cut off any // trailing ones int end_range = num_files - 1; for (; end_range >= 0; --end_range) if (m_file_priority[end_range] != 1) break; m_file_priority.resize(end_range + 1, 4); // initialize pad files to priority 0 file_storage const& fs = m_torrent_file->files(); for (int i = 0; i < (std::min)(fs.num_files(), end_range + 1); ++i) { if (!fs.pad_file_at(i)) continue; m_file_priority[i] = 0; } // storage may be NULL during shutdown if (m_storage) { inc_refcount("file_priority"); m_ses.disk_thread().async_set_file_priority(m_storage.get() , m_file_priority, boost::bind(&torrent::on_file_priority, shared_from_this(), _1)); } } } bdecode_node trackers = rd.dict_find_list("trackers"); if (trackers) { if (!m_merge_resume_trackers) m_trackers.clear(); int tier = 0; for (int i = 0; i < trackers.list_size(); ++i) { bdecode_node tier_list = trackers.list_at(i); if (!tier_list || tier_list.type() != bdecode_node::list_t) continue; for (int j = 0; j < tier_list.list_size(); ++j) { announce_entry e(tier_list.list_string_value_at(j)); if (std::find_if(m_trackers.begin(), m_trackers.end() , boost::bind(&announce_entry::url, _1) == e.url) != m_trackers.end()) continue; e.tier = tier; e.fail_limit = 0; m_trackers.push_back(e); } ++tier; } std::sort(m_trackers.begin(), m_trackers.end(), boost::bind(&announce_entry::tier, _1) < boost::bind(&announce_entry::tier, _2)); if (settings().get_bool(settings_pack::prefer_udp_trackers)) prioritize_udp_trackers(); } // if merge resume http seeds is not set, we need to clear whatever web // seeds we loaded from the .torrent file, because we want whatever's in // the resume file to take precedence. If there aren't even any fields in // the resume data though, keep the ones from the torrent bdecode_node url_list = rd.dict_find_list("url-list"); bdecode_node httpseeds = rd.dict_find_list("httpseeds"); if ((url_list || httpseeds) && !m_merge_resume_http_seeds) { m_web_seeds.clear(); } if (url_list) { for (int i = 0; i < url_list.list_size(); ++i) { std::string url = url_list.list_string_value_at(i); if (url.empty()) continue; if (m_torrent_file->num_files() > 1 && url[url.size()-1] != '/') url += '/'; add_web_seed(url, web_seed_entry::url_seed); } } if (httpseeds) { for (int i = 0; i < httpseeds.list_size(); ++i) { std::string url = httpseeds.list_string_value_at(i); if (url.empty()) continue; add_web_seed(url, web_seed_entry::http_seed); } } if (m_torrent_file->is_merkle_torrent()) { bdecode_node mt = rd.dict_find_string("merkle tree"); if (mt && mt.string_length() >= 20) { std::vector tree; tree.resize(m_torrent_file->merkle_tree().size()); std::memcpy(&tree[0], mt.string_ptr() , (std::min)(mt.string_length(), int(tree.size()) * 20)); if (mt.string_length() < int(tree.size()) * 20) std::memset(&tree[0] + mt.string_length() / 20, 0 , tree.size() - mt.string_length() / 20); m_torrent_file->set_merkle_tree(tree); } else { // TODO: 0 if this is a merkle torrent and we can't // restore the tree, we need to wipe all the // bits in the have array, but not necessarily // we might want to do a full check to see if we have // all the pieces. This is low priority since almost // no one uses merkle torrents TORRENT_ASSERT(false); } } // updating some of the torrent state may have set need_save_resume_data. // clear it here since we've just restored the resume data we already // have. Nothing has changed from that state yet. m_need_save_resume_data = false; if (m_seed_mode) { // some sanity checking. Maybe we shouldn't be in seed mode anymore bdecode_node pieces = rd.dict_find("pieces"); if (pieces && pieces.type() == bdecode_node::string_t && pieces.string_length() == m_torrent_file->num_pieces()) { char const* pieces_str = pieces.string_ptr(); for (int i = 0, end(pieces.string_length()); i < end; ++i) { // being in seed mode and missing a piece is not compatible. // Leave seed mode if that happens if ((pieces_str[i] & 1)) continue; m_seed_mode = false; break; } } bdecode_node piece_priority = rd.dict_find_string("piece_priority"); if (piece_priority && piece_priority.string_length() == m_torrent_file->num_pieces()) { char const* p = piece_priority.string_ptr(); for (int i = 0; i < piece_priority.string_length(); ++i) { if (p[i] > 0) continue; m_seed_mode = false; break; } } m_verified.resize(m_torrent_file->num_pieces(), false); m_verifying.resize(m_torrent_file->num_pieces(), false); } } boost::shared_ptr torrent::get_torrent_copy() { if (!m_torrent_file->is_valid()) return boost::shared_ptr(); if (!need_loaded()) return boost::shared_ptr(); return m_torrent_file; } void torrent::write_resume_data(entry& ret) const { using namespace libtorrent::detail; // for write_*_endpoint() ret["file-format"] = "libtorrent resume file"; ret["file-version"] = 1; ret["libtorrent-version"] = LIBTORRENT_VERSION; ret["total_uploaded"] = m_total_uploaded; ret["total_downloaded"] = m_total_downloaded; ret["active_time"] = active_time(); ret["finished_time"] = finished_time(); ret["seeding_time"] = seeding_time(); ret["last_seen_complete"] = m_last_seen_complete; ret["num_complete"] = m_complete; ret["num_incomplete"] = m_incomplete; ret["num_downloaded"] = m_downloaded; ret["last_upload"] = m_last_upload == unset ? -1 : boost::int64_t(m_last_upload); ret["last_download"] = m_last_download == unset ? -1 : boost::int64_t(m_last_download); ret["last_scrape"] = m_last_scrape == unset ? -1 : boost::int64_t(m_last_scrape); ret["sequential_download"] = m_sequential_download; ret["seed_mode"] = m_seed_mode; ret["super_seeding"] = m_super_seeding; ret["added_time"] = m_added_time; ret["completed_time"] = m_completed_time; ret["save_path"] = m_save_path; if (!m_url.empty()) ret["url"] = m_url; if (!m_uuid.empty()) ret["uuid"] = m_uuid; if (!m_source_feed_url.empty()) ret["feed"] = m_source_feed_url; const sha1_hash& info_hash = torrent_file().info_hash(); ret["info-hash"] = info_hash.to_string(); if (valid_metadata()) { if (m_magnet_link || (m_save_resume_flags & torrent_handle::save_info_dict)) { ret["info"] = bdecode(&torrent_file().metadata()[0] , &torrent_file().metadata()[0] + torrent_file().metadata_size()); // TODO: re-enable this code once there's a non-inlined encoder function. Or // perhaps this should not be used until saving resume_data via // add_torrent_params and a free function, similar to read_resume_data // boost::shared_array const info = torrent_file().metadata(); // int const size = torrent_file().metadata_size(); // ret["info"].preformatted().assign(&info[0], &info[0] + size); } } // blocks per piece int num_blocks_per_piece = static_cast(torrent_file().piece_length()) / block_size(); ret["blocks per piece"] = num_blocks_per_piece; if (m_torrent_file->is_merkle_torrent()) { // we need to save the whole merkle hash tree // in order to resume std::string& tree_str = ret["merkle tree"].string(); std::vector const& tree = m_torrent_file->merkle_tree(); tree_str.resize(tree.size() * 20); std::memcpy(&tree_str[0], &tree[0], tree.size() * 20); } // if this torrent is a seed, we won't have a piece picker // if we don't have anything, we may also not have a picker // in either case; there will be no half-finished pieces. if (has_picker()) { std::vector q = m_picker->get_download_queue(); // unfinished pieces ret["unfinished"] = entry::list_type(); entry::list_type& up = ret["unfinished"].list(); // info for each unfinished piece for (std::vector::const_iterator i = q.begin(); i != q.end(); ++i) { if (i->finished == 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); piece_picker::block_info const* info = m_picker->blocks_for_piece(*i); for (int j = 0; j < num_bitmask_bytes; ++j) { unsigned char v = 0; int bits = (std::min)(num_blocks_per_piece - j*8, 8); for (int k = 0; k < bits; ++k) v |= (info[j*8+k].state == piece_picker::block_info::state_finished) ? (1 << k) : 0; bitmask.append(1, v); TORRENT_ASSERT(bits == 8 || j == num_bitmask_bytes - 1); } piece_struct["bitmask"] = bitmask; // push the struct onto the unfinished-piece list up.push_back(piece_struct); } } // save trackers entry::list_type& tr_list = ret["trackers"].list(); tr_list.push_back(entry::list_type()); int tier = 0; for (std::vector::const_iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) { // don't save trackers we can't trust // TODO: 1 save the send_stats state instead of throwing them away // it may pose an issue when downgrading though if (i->send_stats == false) continue; if (i->tier == tier) { tr_list.back().list().push_back(i->url); } else { tr_list.push_back(entry::list_t); tr_list.back().list().push_back(i->url); tier = i->tier; } } // save web seeds if (!m_web_seeds.empty()) { entry::list_type& url_list = ret["url-list"].list(); entry::list_type& httpseed_list = ret["httpseeds"].list(); for (std::list::const_iterator i = m_web_seeds.begin() , end(m_web_seeds.end()); i != end; ++i) { if (i->removed) continue; if (i->type == web_seed_entry::url_seed) url_list.push_back(i->url); else if (i->type == web_seed_entry::http_seed) httpseed_list.push_back(i->url); } } // write have bitmask // the pieces string has one byte per piece. Each // byte is a bitmask representing different properties // for the piece // bit 0: set if we have the piece // bit 1: set if we have verified the piece (in seed mode) bool const is_checking = state() == torrent_status::checking_files; // if we are checking, only save the have_pieces bitfield up to the piece // we have actually checked. This allows us to resume the checking when we // load this torrent up again. If we have not completed checking nor is // currently checking, don't save any pieces from the have_pieces // bitfield. int const max_piece = is_checking ? m_num_checked_pieces : m_files_checked ? m_torrent_file->num_pieces() : 0; if (max_piece > 0) { entry::string_type& pieces = ret["pieces"].string(); pieces.resize(max_piece); if (!has_picker()) { std::memset(&pieces[0], m_have_all, pieces.size()); } else if (has_picker()) { for (int i = 0, end(pieces.size()); i < end; ++i) pieces[i] = m_picker->have_piece(i) ? 1 : 0; } if (m_seed_mode) { TORRENT_ASSERT(m_verified.size() == pieces.size()); TORRENT_ASSERT(m_verifying.size() == pieces.size()); for (int i = 0, end(pieces.size()); i < end; ++i) pieces[i] |= m_verified[i] ? 2 : 0; } } // write renamed files // TODO: 0 make this more generic to not just work if files have been // renamed, but also if they have been merged into a single file for instance. // using file_base if (&m_torrent_file->files() != &m_torrent_file->orig_files() && m_torrent_file->files().num_files() == m_torrent_file->orig_files().num_files()) { entry::list_type& fl = ret["mapped_files"].list(); file_storage const& fs = m_torrent_file->files(); for (int i = 0; i < fs.num_files(); ++i) { fl.push_back(fs.file_path(i)); } } // write local peers std::back_insert_iterator peers(ret["peers"].string()); std::back_insert_iterator banned_peers(ret["banned_peers"].string()); #if TORRENT_USE_IPV6 std::back_insert_iterator peers6(ret["peers6"].string()); std::back_insert_iterator banned_peers6(ret["banned_peers6"].string()); #endif int num_saved_peers = 0; std::vector deferred_peers; if (m_peer_list) { for (peer_list::const_iterator i = m_peer_list->begin_peer() , end(m_peer_list->end_peer()); i != end; ++i) { error_code ec; torrent_peer const* p = *i; address addr = p->address(); #if TORRENT_USE_I2P if (p->is_i2p_addr) continue; #endif if (p->banned) { #if TORRENT_USE_IPV6 if (addr.is_v6()) { write_address(addr, banned_peers6); write_uint16(p->port, banned_peers6); } else #endif { write_address(addr, banned_peers); write_uint16(p->port, banned_peers); } continue; } // 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 (!p->connectable) continue; // don't save peers that don't work if (int(p->failcount) > 0) continue; // don't save peers that appear to send corrupt data if (int(p->trust_points) < 0) continue; if (p->last_connected == 0) { // we haven't connected to this peer. It might still // be useful to save it, but only save it if we // don't have enough peers that we actually did connect to deferred_peers.push_back(p); continue; } #if TORRENT_USE_IPV6 if (addr.is_v6()) { write_address(addr, peers6); write_uint16(p->port, peers6); } else #endif { write_address(addr, peers); write_uint16(p->port, peers); } ++num_saved_peers; } } // if we didn't save 100 peers, fill in with second choice peers if (num_saved_peers < 100) { std::random_shuffle(deferred_peers.begin(), deferred_peers.end(), randint); for (std::vector::const_iterator i = deferred_peers.begin() , end(deferred_peers.end()); i != end && num_saved_peers < 100; ++i) { torrent_peer const* p = *i; address addr = p->address(); #if TORRENT_USE_IPV6 if (addr.is_v6()) { write_address(addr, peers6); write_uint16(p->port, peers6); } else #endif { write_address(addr, peers); write_uint16(p->port, peers); } ++num_saved_peers; } } ret["upload_rate_limit"] = upload_limit(); ret["download_rate_limit"] = download_limit(); ret["max_connections"] = max_connections(); ret["max_uploads"] = max_uploads(); ret["paused"] = is_torrent_paused(); ret["announce_to_dht"] = m_announce_to_dht; ret["announce_to_trackers"] = m_announce_to_trackers; ret["announce_to_lsd"] = m_announce_to_lsd; ret["auto_managed"] = m_auto_managed; // piece priorities and file priorities are mutually exclusive. If there // are file priorities set, don't save piece priorities. // when in seed mode (i.e. the client promises that we have all files) // it does not make sense to save file priorities. if (!m_file_priority.empty() && !m_seed_mode) { // write file priorities entry::list_type& file_priority = ret["file_priority"].list(); file_priority.clear(); for (int i = 0, end(m_file_priority.size()); i < end; ++i) file_priority.push_back(m_file_priority[i]); } if (has_picker()) { // write piece priorities // but only if they are not set to the default bool default_prio = true; for (int i = 0, end(m_torrent_file->num_pieces()); i < end; ++i) { if (m_picker->piece_priority(i) == 4) continue; default_prio = false; break; } if (!default_prio) { entry::string_type& piece_priority = ret["piece_priority"].string(); piece_priority.resize(m_torrent_file->num_pieces()); for (int i = 0, end(piece_priority.size()); i < end; ++i) piece_priority[i] = m_picker->piece_priority(i); } } } void torrent::get_full_peer_list(std::vector& v) const { v.clear(); if (!m_peer_list) return; v.reserve(m_peer_list->num_peers()); for (peer_list::const_iterator i = m_peer_list->begin_peer(); i != m_peer_list->end_peer(); ++i) { peer_list_entry e; e.ip = (*i)->ip(); e.flags = (*i)->banned ? peer_list_entry::banned : 0; e.failcount = (*i)->failcount; e.source = (*i)->source; v.push_back(e); } } void torrent::get_peer_info(std::vector& v) { v.clear(); for (peer_iterator i = begin(); i != end(); ++i) { peer_connection* peer = *i; TORRENT_ASSERT(peer->m_in_use == 1337); // incoming 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); #ifndef TORRENT_NO_DEPRECATE // deprecated in 1.1 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES if (resolving_countries()) resolve_peer_country(peer->self()); #endif #endif // TORRENT_NO_DEPRECATE } } void torrent::get_download_queue(std::vector* queue) const { TORRENT_ASSERT(is_single_thread()); queue->clear(); std::vector& blk = m_ses.block_info_storage(); blk.clear(); if (!valid_metadata() || !has_picker()) return; piece_picker const& p = picker(); std::vector q = p.get_download_queue(); if (q.empty()) return; const int blocks_per_piece = m_picker->blocks_in_piece(0); blk.resize(q.size() * blocks_per_piece); // for some weird reason valgrind claims these are uninitialized // unless it's zeroed out here (block_info has a construct that's // supposed to initialize it) if (!blk.empty()) memset(&blk[0], 0, sizeof(blk[0]) * blk.size()); int counter = 0; for (std::vector::const_iterator i = q.begin(); i != q.end(); ++i, ++counter) { partial_piece_info pi; pi.blocks_in_piece = p.blocks_in_piece(i->index); pi.finished = int(i->finished); pi.writing = int(i->writing); pi.requested = int(i->requested); #ifndef TORRENT_NO_DEPRECATE pi.piece_state = partial_piece_info::none; #else pi.deprecated_piece_state = partial_piece_info::none; #endif TORRENT_ASSERT(counter * blocks_per_piece + pi.blocks_in_piece <= int(blk.size())); pi.blocks = &blk[counter * blocks_per_piece]; int piece_size = int(torrent_file().piece_size(i->index)); piece_picker::block_info const* info = m_picker->blocks_for_piece(*i); for (int j = 0; j < pi.blocks_in_piece; ++j) { block_info& bi = pi.blocks[j]; bi.state = info[j].state; bi.block_size = j < pi.blocks_in_piece - 1 ? block_size() : piece_size - (j * block_size()); bool complete = bi.state == block_info::writing || bi.state == block_info::finished; if (info[j].peer == 0) { bi.set_peer(tcp::endpoint()); bi.bytes_progress = complete ? bi.block_size : 0; } else { torrent_peer* tp = static_cast(info[j].peer); TORRENT_ASSERT(tp->in_use); if (tp->connection) { peer_connection* peer = static_cast(tp->connection); TORRENT_ASSERT(peer->m_in_use); bi.set_peer(peer->remote()); if (bi.state == block_info::requested) { boost::optional pbp = peer->downloading_piece_progress(); if (pbp && pbp->piece_index == i->index && pbp->block_index == j) { bi.bytes_progress = pbp->bytes_downloaded; TORRENT_ASSERT(bi.bytes_progress <= bi.block_size); } else { bi.bytes_progress = 0; } } else { bi.bytes_progress = complete ? bi.block_size : 0; } } else { bi.set_peer(tp->ip()); bi.bytes_progress = complete ? bi.block_size : 0; } } pi.blocks[j].num_peers = info[j].num_peers; } pi.piece_index = i->index; queue->push_back(pi); } } bool torrent::connect_to_peer(torrent_peer* peerinfo, bool ignore_limit) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; TORRENT_UNUSED(ignore_limit); TORRENT_ASSERT(peerinfo); TORRENT_ASSERT(peerinfo->connection == 0); if (m_abort) return false; peerinfo->last_connected = m_ses.session_time(); #if TORRENT_USE_ASSERTS if (!settings().get_bool(settings_pack::allow_multiple_connections_per_ip)) { // this asserts that we don't have duplicates in the peer_list's peer list peer_iterator i_ = std::find_if(m_connections.begin(), m_connections.end() , boost::bind(&peer_connection::remote, _1) == peerinfo->ip()); #if TORRENT_USE_I2P TORRENT_ASSERT(i_ == m_connections.end() || (*i_)->type() != peer_connection::bittorrent_connection || peerinfo->is_i2p_addr); #else TORRENT_ASSERT(i_ == m_connections.end() || (*i_)->type() != peer_connection::bittorrent_connection); #endif } #endif // TORRENT_USE_ASSERTS TORRENT_ASSERT(want_peers() || ignore_limit); TORRENT_ASSERT(m_ses.num_connections() < settings().get_int(settings_pack::connections_limit) || ignore_limit); tcp::endpoint a(peerinfo->ip()); TORRENT_ASSERT(!m_apply_ip_filter || !m_ip_filter || (m_ip_filter->access(peerinfo->address()) & ip_filter::blocked) == 0); boost::shared_ptr s(new socket_type(m_ses.get_io_service())); #if TORRENT_USE_I2P bool i2p = peerinfo->is_i2p_addr; if (i2p) { if (m_ses.i2p_proxy().hostname.empty()) { // we have an i2p torrent, but we're not connected to an i2p // SAM proxy. if (alerts().should_post()) alerts().emplace_alert(errors::no_i2p_router); return false; } // It's not entirely obvious why this peer connection is not marked as // one. The main feature of a peer connection is that whether or not we // proxy it is configurable. When we use i2p, we want to always prox // everything via i2p. bool ret = instantiate_connection(m_ses.get_io_service() , m_ses.i2p_proxy(), *s, NULL, NULL, false, false); (void)ret; TORRENT_ASSERT(ret); s->get()->set_destination(static_cast(peerinfo)->destination); s->get()->set_command(i2p_stream::cmd_connect); s->get()->set_session_id(m_ses.i2p_session()); } else #endif { // this is where we determine if we open a regular TCP connection // or a uTP connection. If the utp_socket_manager pointer is not passed in // we'll instantiate a TCP connection utp_socket_manager* sm = 0; if (settings().get_bool(settings_pack::enable_outgoing_utp) && (!settings().get_bool(settings_pack::enable_outgoing_tcp) || peerinfo->supports_utp || peerinfo->confirmed_supports_utp)) sm = m_ses.utp_socket_manager(); // don't make a TCP connection if it's disabled if (sm == 0 && !settings().get_bool(settings_pack::enable_outgoing_tcp)) { #ifndef TORRENT_DISABLE_LOGGING debug_log("discarding peer \"%s\": TCP connections disabled " "[ supports-utp: %d ]", peerinfo->to_string().c_str() , peerinfo->supports_utp); #endif return false; } void* userdata = 0; #ifdef TORRENT_USE_OPENSSL if (is_ssl_torrent()) { userdata = m_ssl_ctx.get(); } #endif bool ret = instantiate_connection(m_ses.get_io_service() , m_ses.proxy(), *s, userdata, sm, true, false); (void)ret; TORRENT_ASSERT(ret); #if defined TORRENT_USE_OPENSSL && BOOST_VERSION >= 104700 if (is_ssl_torrent()) { // for ssl sockets, set the hostname std::string host_name = to_hex(m_torrent_file->info_hash().to_string()); #define CASE(t) case socket_type_int_impl >::value: \ s->get >()->set_host_name(host_name); break; switch (s->type()) { CASE(tcp::socket) CASE(socks5_stream) CASE(http_stream) CASE(utp_stream) default: break; }; } #undef CASE #endif } m_ses.setup_socket_buffers(*s); peer_connection_args pack; pack.ses = &m_ses; pack.sett = &settings(); pack.stats_counters = &m_ses.stats_counters(); pack.allocator = &m_ses; pack.disk_thread = &m_ses.disk_thread(); pack.ios = &m_ses.get_io_service(); pack.tor = shared_from_this(); pack.s = s; pack.endp = a; pack.peerinfo = peerinfo; boost::shared_ptr c = boost::make_shared( boost::cref(pack)); TORRENT_TRY { #if TORRENT_USE_ASSERTS c->m_in_constructor = false; #endif c->add_stat(boost::int64_t(peerinfo->prev_amount_download) << 10 , boost::int64_t(peerinfo->prev_amount_upload) << 10); peerinfo->prev_amount_download = 0; peerinfo->prev_amount_upload = 0; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { boost::shared_ptr pp((*i)->new_connection( peer_connection_handle(c.get()->self()))); if (pp) c->add_extension(pp); } TORRENT_CATCH (std::exception&) {} } #endif // add the newly connected peer to this torrent's peer list TORRENT_ASSERT(m_iterating_connections == 0); sorted_insert(m_connections, boost::get_pointer(c)); m_ses.insert_peer(c); need_peer_list(); m_peer_list->set_connection(peerinfo, c.get()); if (peerinfo->seed) { TORRENT_ASSERT(m_num_seeds < 0xffff); ++m_num_seeds; } update_want_peers(); update_want_tick(); c->start(); if (c->is_disconnecting()) return false; } TORRENT_CATCH (std::exception&) { TORRENT_ASSERT(m_iterating_connections == 0); peer_iterator i = sorted_find(m_connections, boost::get_pointer(c)); if (i != m_connections.end()) { m_connections.erase(i); update_want_peers(); update_want_tick(); } c->disconnect(errors::no_error, op_bittorrent, 1); return false; } if (m_share_mode) recalc_share_mode(); return peerinfo->connection != NULL; } bool torrent::set_metadata(char const* metadata_buf, int metadata_size) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (m_torrent_file->is_valid()) return false; hasher h; h.update(metadata_buf, metadata_size); sha1_hash info_hash = h.final(); if (info_hash != m_torrent_file->info_hash()) { if (alerts().should_post()) { alerts().emplace_alert(get_handle() , errors::mismatching_info_hash); } return false; } bdecode_node metadata; error_code ec; int ret = bdecode(metadata_buf, metadata_buf + metadata_size, metadata, ec); if (ret != 0 || !m_torrent_file->parse_info_section(metadata, ec, 0)) { update_gauge(); // this means the metadata is correct, since we // verified it against the info-hash, but we // failed to parse it. Pause the torrent if (alerts().should_post()) { alerts().emplace_alert(get_handle(), ec); } set_error(errors::invalid_swarm_metadata, torrent_status::error_file_none); pause(); return false; } update_gauge(); if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert( get_handle()); } // we have to initialize the torrent before we start // disconnecting redundant peers, otherwise we'll think // we're a seed, because we have all 0 pieces init(); inc_stats_counter(counters::num_total_pieces_added , m_torrent_file->num_pieces()); // disconnect redundant peers int idx = 0; for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++idx) { if ((*i)->disconnect_if_redundant()) { i = m_connections.begin() + idx; --idx; } else { ++i; } } set_need_save_resume(); return true; } namespace { bool connecting_time_compare(peer_connection const* lhs, peer_connection const* rhs) { bool lhs_connecting = lhs->is_connecting() && !lhs->is_disconnecting(); bool rhs_connecting = rhs->is_connecting() && !rhs->is_disconnecting(); if (lhs_connecting > rhs_connecting) return false; if (lhs_connecting < rhs_connecting) return true; // a lower value of connected_time means it's been waiting // longer. This is a less-than comparison, so if lhs has // waited longer than rhs, we should return false. return lhs->connected_time() > rhs->connected_time(); } } // anonymous namespaec bool torrent::attach_peer(peer_connection* p) { // INVARIANT_CHECK; #ifdef TORRENT_USE_OPENSSL #if BOOST_VERSION >= 104700 if (is_ssl_torrent()) { // if this is an SSL torrent, don't allow non SSL peers on it boost::shared_ptr s = p->get_socket(); // #define SSL(t) socket_type_int_impl >::value: \ ssl_conn = s->get >()->native_handle(); \ break; SSL* ssl_conn = 0; switch (s->type()) { case SSL(tcp::socket) case SSL(socks5_stream) case SSL(http_stream) case SSL(utp_stream) }; #undef SSL if (ssl_conn == 0) { // don't allow non SSL peers on SSL torrents p->disconnect(errors::requires_ssl_connection, op_bittorrent); return false; } if (!m_ssl_ctx) { // we don't have a valid cert, don't accept any connection! p->disconnect(errors::invalid_ssl_cert, op_ssl_handshake); return false; } if (SSL_get_SSL_CTX(ssl_conn) != m_ssl_ctx->native_handle()) { // if the SSL_CTX associated with this connection is // not the one belonging to this torrent, the SSL handshake // connected to one torrent, and the BitTorrent protocol // to a different one. This is probably an attempt to circumvent // access control. Don't allow it. p->disconnect(errors::invalid_ssl_cert, op_bittorrent); return false; } } #else // BOOST_VERSION if (is_ssl_torrent()) { p->disconnect(boost::asio::error::operation_not_supported, op_bittorrent); return false; } #endif #else // TORRENT_USE_OPENSSL if (is_ssl_torrent()) { // Don't accidentally allow seeding of SSL torrents, just // because libtorrent wasn't built with SSL support p->disconnect(errors::requires_ssl_connection, op_ssl_handshake); return false; } #endif // TORRENT_USE_OPENSSL TORRENT_ASSERT(p != 0); TORRENT_ASSERT(!p->is_outgoing()); m_has_incoming = true; if (m_apply_ip_filter && m_ip_filter && m_ip_filter->access(p->remote().address()) & ip_filter::blocked) { if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle() , p->remote().address(), peer_blocked_alert::ip_filter); p->disconnect(errors::banned_by_ip_filter, op_bittorrent); return false; } if (!is_downloading_state(m_state) && valid_metadata()) { p->disconnect(errors::torrent_not_ready, op_bittorrent); return false; } if (!m_ses.has_connection(p)) { p->disconnect(errors::peer_not_constructed, op_bittorrent); return false; } if (m_ses.is_aborted()) { p->disconnect(errors::session_closing, op_bittorrent); return false; } int connection_limit_factor = 0; for (int i = 0; i < p->num_classes(); ++i) { int pc = p->class_at(i); if (m_ses.peer_classes().at(pc) == NULL) continue; int f = m_ses.peer_classes().at(pc)->connection_limit_factor; if (connection_limit_factor < f) connection_limit_factor = f; } if (connection_limit_factor == 0) connection_limit_factor = 100; boost::uint64_t limit = boost::uint64_t(m_max_connections) * 100 / connection_limit_factor; bool maybe_replace_peer = false; if (m_connections.size() >= limit) { // if more than 10% of the connections are outgoing // connection attempts that haven't completed yet, // disconnect one of them and let this incoming // connection through. if (m_num_connecting > m_max_connections / 10) { // find one of the connecting peers and disconnect it // find any peer that's connecting (i.e. a half-open TCP connection) // that's also not disconnecting // disconnect the peer that's been wating to establish a connection // the longest std::vector::iterator i = std::max_element(begin(), end() , &connecting_time_compare); if (i == end() || !(*i)->is_connecting() || (*i)->is_disconnecting()) { // this seems odd, but we might as well handle it p->disconnect(errors::too_many_connections, op_bittorrent); return false; } (*i)->disconnect(errors::too_many_connections, op_bittorrent); // if this peer was let in via connections slack, // it has done its duty of causing the disconnection // of another peer p->peer_disconnected_other(); } else { maybe_replace_peer = true; } } TORRENT_TRY { #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { boost::shared_ptr pp((*i)->new_connection( peer_connection_handle(p->self()))); if (pp) p->add_extension(pp); } #endif torrent_state st = get_peer_list_state(); need_peer_list(); if (!m_peer_list->new_connection(*p, m_ses.session_time(), &st)) { peers_erased(st.erased); #ifndef TORRENT_DISABLE_LOGGING debug_log("CLOSING CONNECTION \"%s\" peer list full " "connections: %d limit: %d" , print_endpoint(p->remote()).c_str() , int(m_connections.size()) , m_max_connections); #endif p->disconnect(errors::too_many_connections, op_bittorrent); return false; } peers_erased(st.erased); update_want_peers(); } TORRENT_CATCH (std::exception& e) { TORRENT_DECLARE_DUMMY(std::exception, e); (void)e; #ifndef TORRENT_DISABLE_LOGGING debug_log("CLOSING CONNECTION \"%s\" caught exception: %s" , print_endpoint(p->remote()).c_str(), e.what()); #endif p->disconnect(errors::no_error, op_bittorrent); return false; } TORRENT_ASSERT(sorted_find(m_connections, p) == m_connections.end()); TORRENT_ASSERT(m_iterating_connections == 0); sorted_insert(m_connections, p); update_want_peers(); update_want_tick(); if (p->peer_info_struct() && p->peer_info_struct()->seed) { TORRENT_ASSERT(m_num_seeds < 0xffff); ++m_num_seeds; } #ifndef TORRENT_DISABLE_LOGGING debug_log("incoming peer (%d)", int(m_connections.size())); #endif #ifdef TORRENT_DEBUG error_code ec; TORRENT_ASSERT(p->remote() == p->get_socket()->remote_endpoint(ec) || ec); #endif TORRENT_ASSERT(p->peer_info_struct() != NULL); // we need to do this after we've added the peer to the peer_list // since that's when the peer is assigned its peer_info object, // which holds the rank if (maybe_replace_peer) { // now, find the lowest rank peer and disconnect that // if it's lower rank than the incoming connection peer_connection* peer = find_lowest_ranking_peer(); // TODO: 2 if peer is a really good peer, maybe we shouldn't disconnect it // perhaps this logic should be disabled if we have too many idle peers // (with some definition of idle) if (peer && peer->peer_rank() < p->peer_rank()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("CLOSING CONNECTION \"%s\" peer list full (low peer rank) " "connections: %d limit: %d" , print_endpoint(peer->remote()).c_str() , int(m_connections.size()) , m_max_connections); #endif peer->disconnect(errors::too_many_connections, op_bittorrent); p->peer_disconnected_other(); } else { #ifndef TORRENT_DISABLE_LOGGING debug_log("CLOSING CONNECTION \"%s\" peer list full (low peer rank) " "connections: %d limit: %d" , print_endpoint(p->remote()).c_str() , int(m_connections.size()) , m_max_connections); #endif p->disconnect(errors::too_many_connections, op_bittorrent); // we have to do this here because from the peer's point of // it wasn't really attached to the torrent, but we do need // to let peer_list know we're removing it remove_peer(p); return false; } } #if TORRENT_USE_INVARIANT_CHECKS if (m_peer_list) m_peer_list->check_invariant(); #endif if (m_share_mode) recalc_share_mode(); #ifndef TORRENT_DISABLE_LOGGING debug_log("ATTACHED CONNECTION \"%s\" connections: %d limit: %d" , print_endpoint(p->remote()).c_str(), int(m_connections.size()) , m_max_connections); #endif return true; } bool torrent::want_tick() const { if (m_abort) return false; if (!m_connections.empty()) return true; // there's a deferred storage tick waiting // to happen if (m_storage_tick) return true; // we might want to connect web seeds if (!is_finished() && !m_web_seeds.empty() && m_files_checked) return true; if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) return true; // if we don't get ticks we won't become inactive if (m_allow_peers && !m_inactive) return true; return false; } void torrent::update_want_tick() { update_list(aux::session_interface::torrent_want_tick, want_tick()); } // this function adjusts which lists this torrent is part of (checking, // seeding or downloading) void torrent::update_state_list() { bool is_checking = false; bool is_downloading = false; bool is_seeding = false; if (is_auto_managed() && !has_error()) { if (m_state == torrent_status::checking_files || m_state == torrent_status::allocating) { is_checking = true; } else if (m_state == torrent_status::downloading_metadata || m_state == torrent_status::downloading || m_state == torrent_status::finished || m_state == torrent_status::seeding) { // torrents that are started (not paused) and // inactive are not part of any list. They will not be touched because // they are inactive if (is_finished()) is_seeding = true; else is_downloading = true; } } update_list(aux::session_interface::torrent_downloading_auto_managed , is_downloading); update_list(aux::session_interface::torrent_seeding_auto_managed , is_seeding); update_list(aux::session_interface::torrent_checking_auto_managed , is_checking); } // returns true if this torrent is interested in connecting to more peers bool torrent::want_peers() const { if (m_should_be_loaded == false) return false; // if all our connection slots are taken, we can't connect to more if (m_connections.size() >= m_max_connections) return false; // if we're paused, obviously we're not connecting to peers if (is_paused() || m_abort || m_graceful_pause_mode) return false; if ((m_state == torrent_status::checking_files || m_state == torrent_status::checking_resume_data) && valid_metadata()) return false; // if we don't know of any more potential peers to connect to, there's // no point in trying if (!m_peer_list || m_peer_list->num_connect_candidates() == 0) return false; // if the user disabled outgoing connections for seeding torrents, // don't make any if (!settings().get_bool(settings_pack::seeding_outgoing_connections) && (m_state == torrent_status::seeding || m_state == torrent_status::finished)) return false; return true; } bool torrent::want_peers_download() const { return (m_state == torrent_status::downloading || m_state == torrent_status::downloading_metadata) && want_peers(); } bool torrent::want_peers_finished() const { return (m_state == torrent_status::finished || m_state == torrent_status::seeding) && want_peers(); } void torrent::update_want_peers() { update_list(aux::session_interface::torrent_want_peers_download, want_peers_download()); update_list(aux::session_interface::torrent_want_peers_finished, want_peers_finished()); } void torrent::update_want_scrape() { update_list(aux::session_interface::torrent_want_scrape , !m_allow_peers && m_auto_managed && !m_abort); } namespace { #ifndef TORRENT_DISABLE_LOGGING char const* list_name(int const idx) { #define TORRENT_LIST_NAME(n) case aux::session_interface:: n: return #n; switch (idx) { TORRENT_LIST_NAME(torrent_state_updates); TORRENT_LIST_NAME(torrent_want_tick); TORRENT_LIST_NAME(torrent_want_peers_download); TORRENT_LIST_NAME(torrent_want_peers_finished); TORRENT_LIST_NAME(torrent_want_scrape); TORRENT_LIST_NAME(torrent_downloading_auto_managed); TORRENT_LIST_NAME(torrent_seeding_auto_managed); TORRENT_LIST_NAME(torrent_checking_auto_managed); default: TORRENT_ASSERT_VAL(false, idx); } #undef TORRENT_LIST_NAME return ""; } #endif // TORRENT_DISABLE_LOGGING } // anonymous namespace void torrent::update_list(int list, bool in) { link& l = m_links[list]; std::vector& v = m_ses.torrent_list(list); if (in) { if (l.in_list()) return; l.insert(v, this); } else { if (!l.in_list()) return; l.unlink(v, list); } #ifndef TORRENT_DISABLE_LOGGING debug_log("*** UPDATE LIST [ %s : %d ]", list_name(list), int(in)); #endif } void torrent::disconnect_all(error_code const& ec, operation_t op) { // doesn't work with the !m_allow_peers -> m_num_peers == 0 condition // INVARIANT_CHECK; TORRENT_ASSERT(m_iterating_connections == 0); while (!m_connections.empty()) { peer_connection* p = *m_connections.begin(); TORRENT_ASSERT(p->associated_torrent().lock().get() == this); #if TORRENT_USE_ASSERTS std::size_t size = m_connections.size(); #endif if (p->is_disconnecting()) m_connections.erase(m_connections.begin()); else p->disconnect(ec, op); TORRENT_ASSERT(m_connections.size() <= size); } update_want_peers(); update_want_tick(); } namespace { // this returns true if lhs is a better disconnect candidate than rhs bool compare_disconnect_peer(peer_connection const* lhs, peer_connection const* rhs) { // prefer to disconnect peers that are already disconnecting if (lhs->is_disconnecting() != rhs->is_disconnecting()) return lhs->is_disconnecting(); // prefer to disconnect peers we're not interested in if (lhs->is_interesting() != rhs->is_interesting()) return rhs->is_interesting(); // prefer to disconnect peers that are not seeds if (lhs->is_seed() != rhs->is_seed()) return rhs->is_seed(); // prefer to disconnect peers that are on parole if (lhs->on_parole() != rhs->on_parole()) return lhs->on_parole(); // prefer to disconnect peers that send data at a lower rate boost::int64_t lhs_transferred = lhs->statistics().total_payload_download(); boost::int64_t rhs_transferred = rhs->statistics().total_payload_download(); time_point now = aux::time_now(); boost::int64_t lhs_time_connected = total_seconds(now - lhs->connected_time()); boost::int64_t rhs_time_connected = total_seconds(now - rhs->connected_time()); lhs_transferred /= lhs_time_connected + 1; rhs_transferred /= (rhs_time_connected + 1); if (lhs_transferred != rhs_transferred) return lhs_transferred < rhs_transferred; // prefer to disconnect peers that chokes us if (lhs->is_choked() != rhs->is_choked()) return lhs->is_choked(); return lhs->last_received() < rhs->last_received(); } } // anonymous namespace int torrent::disconnect_peers(int num, error_code const& ec) { INVARIANT_CHECK; #ifdef TORRENT_DEBUG for (peer_iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { TORRENT_INCREMENT(m_iterating_connections); // make sure this peer is not a dangling pointer TORRENT_ASSERT(m_ses.has_peer(*i)); } #endif int ret = 0; while (ret < num && !m_connections.empty()) { peer_iterator i = std::min_element( m_connections.begin(), m_connections.end(), compare_disconnect_peer); peer_connection* p = *i; ++ret; TORRENT_ASSERT(p->associated_torrent().lock().get() == this); #if TORRENT_USE_ASSERTS int num_conns = m_connections.size(); #endif p->disconnect(ec, op_bittorrent); TORRENT_ASSERT(int(m_connections.size()) == num_conns - 1); } return ret; } // called when torrent is finished (all interesting // pieces have been downloaded) void torrent::finished() { update_state_list(); INVARIANT_CHECK; TORRENT_ASSERT(is_finished()); set_state(torrent_status::finished); set_queue_position(-1); m_became_finished = m_ses.session_time(); // we have to call completed() before we start // disconnecting peers, since there's an assert // to make sure we're cleared the piece picker if (is_seed()) completed(); send_upload_only(); state_updated(); if (m_completed_time == 0) m_completed_time = time(0); // disconnect all seeds if (settings().get_bool(settings_pack::close_redundant_connections)) { // TODO: 1 should disconnect all peers that have the pieces we have // not just seeds. It would be pretty expensive to check all pieces // for all peers though std::vector seeds; for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = *i; TORRENT_ASSERT(p->associated_torrent().lock().get() == this); if (p->upload_only()) { #ifndef TORRENT_DISABLE_LOGGING p->peer_log(peer_log_alert::info, "SEED", "CLOSING CONNECTION"); #endif seeds.push_back(p); } } std::for_each(seeds.begin(), seeds.end() , boost::bind(&peer_connection::disconnect, _1, errors::torrent_finished , op_bittorrent, 0)); } if (m_abort) return; update_want_peers(); if (m_storage) { // we need to keep the object alive during this operation inc_refcount("release_files"); m_ses.disk_thread().async_release_files(m_storage.get() , boost::bind(&torrent::on_cache_flushed, shared_from_this(), _1, false)); } // this torrent just completed downloads, which means it will fall // under a different limit with the auto-manager. Make sure we // update auto-manage torrents in that case if (m_auto_managed) m_ses.trigger_auto_manage(); } // this is called when we were finished, but some files were // marked for downloading, and we are no longer finished void torrent::resume_download() { // the invariant doesn't hold here, because it expects the torrent // to be in downloading state (which it will be set to shortly) // INVARIANT_CHECK; TORRENT_ASSERT(m_state != torrent_status::checking_resume_data && m_state != torrent_status::checking_files && m_state != torrent_status::allocating); // we're downloading now, which means we're no longer in seed mode if (m_seed_mode) leave_seed_mode(false); TORRENT_ASSERT(!is_finished()); set_state(torrent_status::downloading); set_queue_position((std::numeric_limits::max)()); m_completed_time = 0; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** RESUME_DOWNLOAD"); #endif send_upload_only(); update_want_tick(); update_state_list(); } void torrent::maybe_done_flushing() { if (!has_picker()) return; if (m_picker->is_seeding()) { // no need for the piece picker anymore // when we're suggesting read cache pieces, we // still need the piece picker, to keep track // of availability counts for pieces if (settings().get_int(settings_pack::suggest_mode) != settings_pack::suggest_read_cache) { m_picker.reset(); m_file_progress.clear(); } m_have_all = true; update_gauge(); } } // called when torrent is complete. i.e. all pieces downloaded // not necessarily flushed to disk void torrent::completed() { maybe_done_flushing(); set_state(torrent_status::seeding); m_became_seed = m_ses.session_time(); if (!m_announcing) return; time_point now = aux::time_now(); for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) { if (i->complete_sent) continue; i->next_announce = now; i->min_announce = now; } announce_with_tracker(); } // this will move the tracker with the given index // to a prioritized position in the list (move it towards // the beginning) and return the new index to the tracker. int torrent::prioritize_tracker(int index) { INVARIANT_CHECK; TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_trackers.size())); if (index >= int(m_trackers.size())) return -1; while (index > 0 && m_trackers[index].tier == m_trackers[index-1].tier) { using std::swap; swap(m_trackers[index], m_trackers[index-1]); if (m_last_working_tracker == index) --m_last_working_tracker; else if (m_last_working_tracker == index - 1) ++m_last_working_tracker; --index; } return index; } int torrent::deprioritize_tracker(int index) { INVARIANT_CHECK; TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_trackers.size())); if (index >= int(m_trackers.size())) return -1; while (index < int(m_trackers.size()) - 1 && m_trackers[index].tier == m_trackers[index + 1].tier) { using std::swap; swap(m_trackers[index], m_trackers[index + 1]); if (m_last_working_tracker == index) ++m_last_working_tracker; else if (m_last_working_tracker == index + 1) --m_last_working_tracker; ++index; } return index; } void torrent::files_checked() { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_torrent_file->is_valid()); if (m_abort) { #ifndef TORRENT_DISABLE_LOGGING debug_log("files_checked(), paused"); #endif return; } // calling pause will also trigger the auto managed // recalculation // if we just got here by downloading the metadata, // just keep going, no need to disconnect all peers just // to restart the torrent in a second if (m_auto_managed) { // if this is an auto managed torrent, force a recalculation // of which torrents to have active m_ses.trigger_auto_manage(); } if (!is_seed()) { // turn off super seeding if we're not a seed if (m_super_seeding) { m_super_seeding = false; set_need_save_resume(); state_updated(); } if (is_finished() && m_state != torrent_status::finished) finished(); } else { for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) i->complete_sent = true; if (m_state != torrent_status::finished && m_state != torrent_status::seeding) finished(); } // we might be finished already, in which case we should // not switch to downloading mode. If all files are // filtered, we're finished when we start. if (m_state != torrent_status::finished && m_state != torrent_status::seeding && !m_seed_mode) { set_state(torrent_status::downloading); } INVARIANT_CHECK; if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert( get_handle()); } #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->on_files_checked(); } TORRENT_CATCH (std::exception&) {} } #endif bool const notify_initialized = !m_connections_initialized; m_connections_initialized = true; m_files_checked = true; update_want_tick(); for (torrent::peer_iterator i = m_connections.begin(); i != m_connections.end();) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* pc = *i; ++i; // all peer connections have to initialize themselves now that the metadata // is available if (notify_initialized) { if (pc->is_disconnecting()) continue; pc->on_metadata_impl(); if (pc->is_disconnecting()) continue; pc->init(); } #ifndef TORRENT_DISABLE_LOGGING pc->peer_log(peer_log_alert::info, "ON_FILES_CHECKED"); #endif if (pc->is_interesting() && !pc->has_peer_choked()) { if (request_a_block(*this, *pc)) { inc_stats_counter(counters::unchoke_piece_picks); pc->send_block_requests(); } } } start_announcing(); maybe_connect_web_seeds(); } alert_manager& torrent::alerts() const { TORRENT_ASSERT(is_single_thread()); return m_ses.alerts(); } bool torrent::is_seed() const { if (!valid_metadata()) return false; if (m_seed_mode) return true; if (m_have_all) return true; if (m_picker && m_picker->num_passed() == m_picker->num_pieces()) return true; return m_state == torrent_status::seeding; } bool torrent::is_finished() const { if (is_seed()) return true; // this is slightly different from m_picker->is_finished() // because any piece that has *passed* is considered here, // which may be more than the piece we *have* (i.e. written to disk) // keep in mind that num_filtered() does not include pieces we // have that are filtered return valid_metadata() && has_picker() && m_torrent_file->num_pieces() - m_picker->num_filtered() - m_picker->num_passed() == 0; } bool torrent::is_inactive() const { if (!settings().get_bool(settings_pack::dont_count_slow_torrents)) return false; return m_inactive; } std::string torrent::save_path() const { return m_save_path; } void torrent::rename_file(int index, std::string const& name) { INVARIANT_CHECK; TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < m_torrent_file->num_files()); // storage may be NULL during shutdown if (!m_storage.get()) { if (alerts().should_post()) alerts().emplace_alert(get_handle() , index, errors::session_is_closing); return; } inc_refcount("rename_file"); m_ses.disk_thread().async_rename_file(m_storage.get(), index, name , boost::bind(&torrent::on_file_renamed, shared_from_this(), _1)); return; } void torrent::move_storage(std::string const& save_path, int flags) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (m_abort) { if (alerts().should_post()) alerts().emplace_alert(get_handle(), boost::asio::error::operation_aborted , "", ""); return; } // if we don't have metadata yet, we don't know anything about the file // structure and we have to assume we don't have any file. if (!valid_metadata()) { if (alerts().should_post()) alerts().emplace_alert(get_handle(), save_path); #if TORRENT_USE_UNC_PATHS std::string path = canonicalize_path(save_path); #else std::string const& path = save_path; #endif m_save_path = complete(path); return; } // storage may be NULL during shutdown if (m_storage.get()) { #if TORRENT_USE_UNC_PATHS std::string path = canonicalize_path(save_path); #else std::string const& path = save_path; #endif inc_refcount("move_storage"); m_ses.disk_thread().async_move_storage(m_storage.get(), path, flags , boost::bind(&torrent::on_storage_moved, shared_from_this(), _1)); m_moving_storage = true; } else { #if TORRENT_USE_UNC_PATHS m_save_path = canonicalize_path(save_path); #else m_save_path = save_path; #endif set_need_save_resume(); if (alerts().should_post()) { alerts().emplace_alert(get_handle(), m_save_path); } } } void torrent::on_storage_moved(disk_io_job const* j) { TORRENT_ASSERT(is_single_thread()); m_moving_storage = false; dec_refcount("move_storage"); if (j->ret == piece_manager::no_error || j->ret == piece_manager::need_full_check) { if (alerts().should_post()) alerts().emplace_alert(get_handle(), j->buffer.string); m_save_path = j->buffer.string; set_need_save_resume(); if (j->ret == piece_manager::need_full_check) force_recheck(); } else { if (alerts().should_post()) alerts().emplace_alert(get_handle(), j->error.ec , resolve_filename(j->error.file), j->error.operation_str()); } } piece_manager& torrent::storage() { TORRENT_ASSERT(m_storage.get()); return *m_storage; } torrent_handle torrent::get_handle() { TORRENT_ASSERT(is_single_thread()); return torrent_handle(shared_from_this()); } aux::session_settings const& torrent::settings() const { TORRENT_ASSERT(is_single_thread()); return m_ses.settings(); } #if TORRENT_USE_INVARIANT_CHECKS void torrent::check_invariant() const { // the piece picker and the file progress states are supposed to be // created in sync TORRENT_ASSERT(has_picker() == !m_file_progress.empty()); TORRENT_ASSERT(current_stats_state() == m_current_gauge_state + counters::num_checking_torrents || m_current_gauge_state == no_gauge_state); for (std::vector::const_iterator i = m_time_critical_pieces.begin() , end(m_time_critical_pieces.end()); i != end; ++i) { TORRENT_ASSERT(!is_seed()); TORRENT_ASSERT(!has_picker() || !m_picker->have_piece(i->piece)); } switch (current_stats_state()) { case counters::num_error_torrents: TORRENT_ASSERT(has_error()); break; case counters::num_checking_torrents: #ifdef TORRENT_NO_DEPRECATE TORRENT_ASSERT(state() == torrent_status::checking_files); #else TORRENT_ASSERT(state() == torrent_status::checking_files || state() == torrent_status::queued_for_checking); #endif break; case counters::num_seeding_torrents: TORRENT_ASSERT(is_seed()); break; case counters::num_upload_only_torrents: TORRENT_ASSERT(is_upload_only()); break; case counters::num_stopped_torrents: TORRENT_ASSERT(!is_auto_managed() && (!m_allow_peers || m_graceful_pause_mode)); break; case counters::num_queued_seeding_torrents: TORRENT_ASSERT((!m_allow_peers || m_graceful_pause_mode) && is_seed()); break; } if (m_torrent_file) { TORRENT_ASSERT(m_info_hash == m_torrent_file->info_hash()); } #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS for (int i = 0; i < aux::session_interface::num_torrent_lists; ++i) { if (!m_links[i].in_list()) continue; int index = m_links[i].index; TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_ses.torrent_list(i).size())); } #endif if (!is_loaded()) return; TORRENT_ASSERT(want_peers_download() == m_links[aux::session_interface::torrent_want_peers_download].in_list()); TORRENT_ASSERT(want_peers_finished() == m_links[aux::session_interface::torrent_want_peers_finished].in_list()); TORRENT_ASSERT(want_tick() == m_links[aux::session_interface::torrent_want_tick].in_list()); TORRENT_ASSERT((!m_allow_peers && m_auto_managed && !m_abort) == m_links[aux::session_interface::torrent_want_scrape].in_list()); bool is_checking = false; bool is_downloading = false; bool is_seeding = false; if (is_auto_managed() && !has_error()) { if (m_state == torrent_status::checking_files || m_state == torrent_status::allocating) { is_checking = true; } else if (m_state == torrent_status::downloading_metadata || m_state == torrent_status::downloading || m_state == torrent_status::finished || m_state == torrent_status::seeding || m_state == torrent_status::downloading) { if (is_finished()) is_seeding = true; else is_downloading = true; } } TORRENT_ASSERT(m_links[aux::session_interface::torrent_checking_auto_managed].in_list() == is_checking); TORRENT_ASSERT(m_links[aux::session_interface::torrent_downloading_auto_managed].in_list() == is_downloading); TORRENT_ASSERT(m_links[aux::session_interface::torrent_seeding_auto_managed].in_list() == is_seeding); if (m_seed_mode) { TORRENT_ASSERT(is_seed()); } TORRENT_ASSERT(is_single_thread()); // this fires during disconnecting peers // if (is_paused()) TORRENT_ASSERT(num_peers() == 0 || m_graceful_pause_mode); TORRENT_ASSERT(!m_resume_data || m_resume_data->node.type() == bdecode_node::dict_t || m_resume_data->node.type() == bdecode_node::none_t); int seeds = 0; int num_uploads = 0; int num_connecting = 0; int num_connecting_seeds = 0; std::map num_requests; for (const_peer_iterator i = this->begin(); i != this->end(); ++i) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS // make sure this peer is not a dangling pointer TORRENT_ASSERT(m_ses.has_peer(*i)); #endif peer_connection const& p = *(*i); if (p.is_connecting()) ++num_connecting; if (p.is_connecting() && p.peer_info_struct()->seed) ++num_connecting_seeds; if (p.peer_info_struct() && p.peer_info_struct()->seed) ++seeds; for (std::vector::const_iterator j = p.request_queue().begin() , end(p.request_queue().end()); j != end; ++j) { if (!j->not_wanted && !j->timed_out) ++num_requests[j->block]; } for (std::vector::const_iterator j = p.download_queue().begin() , end(p.download_queue().end()); j != end; ++j) { if (!j->not_wanted && !j->timed_out) ++num_requests[j->block]; } if (!p.is_choked() && !p.ignore_unchoke_slots()) ++num_uploads; torrent* associated_torrent = p.associated_torrent().lock().get(); if (associated_torrent != this && associated_torrent != 0) TORRENT_ASSERT(false); } TORRENT_ASSERT(num_uploads == int(m_num_uploads)); TORRENT_ASSERT(seeds == int(m_num_seeds)); TORRENT_ASSERT(num_connecting == int(m_num_connecting)); TORRENT_ASSERT(num_connecting_seeds == int(m_num_connecting_seeds)); TORRENT_ASSERT(int(m_num_uploads) <= int(m_connections.size())); TORRENT_ASSERT(int(m_num_seeds) <= int(m_connections.size())); TORRENT_ASSERT(int(m_num_connecting) <= int(m_connections.size())); TORRENT_ASSERT(int(m_num_connecting_seeds) <= int(m_connections.size())); TORRENT_ASSERT(int(m_num_connecting) + int(m_num_seeds) >= int(m_num_connecting_seeds)); TORRENT_ASSERT(int(m_num_connecting) + int(m_num_seeds) - int(m_num_connecting_seeds) <= int(m_connections.size())); if (has_picker()) { for (std::map::iterator i = num_requests.begin() , end(num_requests.end()); i != end; ++i) { piece_block b = i->first; int count = i->second; int picker_count = m_picker->num_peers(b); // if we're no longer downloading the piece // (for instance, it may be fully downloaded and waiting // for the hash check to return), the piece picker always // returns 0 requests, regardless of how many peers may still // have the block in their queue if (!m_picker->is_downloaded(b) && m_picker->is_downloading(b.piece_index)) { if (picker_count != count) { fprintf(stderr, "picker count discrepancy: " "picker: %d != peerlist: %d\n", picker_count, count); for (const_peer_iterator j = this->begin(); j != this->end(); ++j) { peer_connection const& p = *(*j); fprintf(stderr, "peer: %s\n", print_endpoint(p.remote()).c_str()); for (std::vector::const_iterator k = p.request_queue().begin() , end2(p.request_queue().end()); k != end2; ++k) { fprintf(stderr, " rq: (%d, %d) %s %s %s\n", k->block.piece_index , k->block.block_index, k->not_wanted ? "not-wanted" : "" , k->timed_out ? "timed-out" : "", k->busy ? "busy": ""); } for (std::vector::const_iterator k = p.download_queue().begin() , end2(p.download_queue().end()); k != end2; ++k) { fprintf(stderr, " dq: (%d, %d) %s %s %s\n", k->block.piece_index , k->block.block_index, k->not_wanted ? "not-wanted" : "" , k->timed_out ? "timed-out" : "", k->busy ? "busy": ""); } } TORRENT_ASSERT(false); } } } TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); } if (valid_metadata()) { TORRENT_ASSERT(m_abort || m_error || !m_picker || m_picker->num_pieces() == m_torrent_file->num_pieces()); } else { TORRENT_ASSERT(m_abort || m_error || !m_picker || m_picker->num_pieces() == 0); } #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS // make sure we haven't modified the peer object // in a way that breaks the sort order if (m_peer_list && m_peer_list->begin_peer() != m_peer_list->end_peer()) { peer_list::const_iterator i = m_peer_list->begin_peer(); peer_list::const_iterator prev = i++; peer_list::const_iterator end(m_peer_list->end_peer()); peer_address_compare cmp; for (; i != end; ++i, ++prev) { TORRENT_ASSERT(!cmp(*i, *prev)); } } #endif boost::int64_t total_done = quantized_bytes_done(); if (m_torrent_file->is_valid()) { if (is_seed()) TORRENT_ASSERT(total_done == m_torrent_file->total_size()); else TORRENT_ASSERT(total_done != m_torrent_file->total_size() || !m_files_checked); TORRENT_ASSERT(block_size() <= m_torrent_file->piece_length()); } else { TORRENT_ASSERT(total_done == 0); } /* if (m_picker && !m_abort) { // make sure that pieces that have completed the download // of all their blocks are in the disk io thread's queue // to be checked. std::vector dl_queue = m_picker->get_download_queue(); for (std::vector::const_iterator i = dl_queue.begin(); i != dl_queue.end(); ++i) { const int blocks_per_piece = m_picker->blocks_in_piece(i->index); bool complete = true; for (int j = 0; j < blocks_per_piece; ++j) { if (i->info[j].state == piece_picker::block_info::state_finished) continue; complete = false; break; } TORRENT_ASSERT(complete); } } */ if (m_files_checked && valid_metadata()) { TORRENT_ASSERT(block_size() > 0); } } #endif void torrent::set_sequential_download(bool sd) { TORRENT_ASSERT(is_single_thread()); if (m_sequential_download == sd) return; m_sequential_download = sd; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** set-sequential-download: %d", sd); #endif set_need_save_resume(); state_updated(); } void torrent::queue_up() { // finished torrents may not change their queue positions, as it's set to // -1 if (m_abort || is_finished()) return; set_queue_position(queue_position() == 0 ? queue_position() : queue_position() - 1); } void torrent::queue_down() { set_queue_position(queue_position() + 1); } void torrent::set_queue_position(int p) { TORRENT_ASSERT(is_single_thread()); // finished torrents may not change their queue positions, as it's set to // -1 if ((m_abort || is_finished()) && p != -1) return; TORRENT_ASSERT((p == -1) == is_finished() || (!m_auto_managed && p == -1) || (m_abort && p == -1)); if (p == m_sequence_number) return; TORRENT_ASSERT(p >= -1); state_updated(); m_ses.set_queue_position(this, p); } void torrent::set_max_uploads(int limit, bool state_update) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(limit >= -1); if (limit <= 0) limit = (1<<24)-1; if (m_max_uploads != limit && state_update) state_updated(); m_max_uploads = limit; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** set-max-uploads: %d", m_max_uploads); #endif if (state_update) set_need_save_resume(); } void torrent::set_max_connections(int limit, bool state_update) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(limit >= -1); if (limit <= 0) limit = (1<<24)-1; if (m_max_connections != limit && state_update) state_updated(); m_max_connections = limit; update_want_peers(); #ifndef TORRENT_DISABLE_LOGGING debug_log("*** set-max-connections: %d", m_max_connections); #endif if (num_peers() > int(m_max_connections)) { disconnect_peers(num_peers() - m_max_connections , errors::too_many_connections); } if (state_update) set_need_save_resume(); } void torrent::set_upload_limit(int limit) { set_limit_impl(limit, peer_connection::upload_channel); set_need_save_resume(); #ifndef TORRENT_DISABLE_LOGGING debug_log("*** set-upload-limit: %d", limit); #endif } void torrent::set_download_limit(int limit) { set_limit_impl(limit, peer_connection::download_channel); set_need_save_resume(); #ifndef TORRENT_DISABLE_LOGGING debug_log("*** set-download-limit: %d", limit); #endif } void torrent::set_limit_impl(int limit, int channel, bool state_update) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(limit >= -1); if (limit <= 0) limit = 0; if (m_peer_class == 0 && limit == 0) return; if (m_peer_class == 0) setup_peer_class(); struct peer_class* tpc = m_ses.peer_classes().at(m_peer_class); TORRENT_ASSERT(tpc); if (tpc->channel[channel].throttle() != limit && state_update) state_updated(); tpc->channel[channel].throttle(limit); } void torrent::setup_peer_class() { TORRENT_ASSERT(m_peer_class == 0); m_peer_class = m_ses.peer_classes().new_peer_class(name()); add_class(m_ses.peer_classes(), m_peer_class); } int torrent::limit_impl(int channel) const { TORRENT_ASSERT(is_single_thread()); if (m_peer_class == 0) return -1; int limit = m_ses.peer_classes().at(m_peer_class)->channel[channel].throttle(); if (limit == (std::numeric_limits::max)()) limit = -1; return limit; } int torrent::upload_limit() const { return limit_impl(peer_connection::upload_channel); } int torrent::download_limit() const { return limit_impl(peer_connection::download_channel); } bool torrent::delete_files(int const options) { TORRENT_ASSERT(is_single_thread()); #ifndef TORRENT_DISABLE_LOGGING log_to_all_peers("deleting files"); #endif disconnect_all(errors::torrent_removed, op_bittorrent); stop_announcing(); // storage may be NULL during shutdown if (m_storage.get()) { TORRENT_ASSERT(m_storage); inc_refcount("delete_files"); m_ses.disk_thread().async_delete_files(m_storage.get(), options , boost::bind(&torrent::on_files_deleted, shared_from_this(), _1)); m_deleted = true; return true; } return false; } void torrent::clear_error() { TORRENT_ASSERT(is_single_thread()); if (!m_error) return; bool checking_files = should_check_files(); m_ses.trigger_auto_manage(); m_error = error_code(); m_error_file = torrent_status::error_file_none; update_gauge(); state_updated(); update_want_peers(); update_state_list(); // if we haven't downloaded the metadata from m_url, try again if (!m_url.empty() && !m_torrent_file->is_valid()) { start_download_url(); return; } // if the error happened during initialization, try again now if (!m_connections_initialized && valid_metadata()) init(); if (!checking_files && should_check_files()) start_checking(); } std::string torrent::resolve_filename(int file) const { switch (file) { case torrent_status::error_file_none: return ""; case torrent_status::error_file_url: return m_url; case torrent_status::error_file_ssl_ctx: return "SSL Context"; case torrent_status::error_file_metadata: return "metadata (from user load function)"; case torrent_status::error_file_partfile: return "partfile"; } if (m_storage && file >= 0) { file_storage const& st = m_torrent_file->files(); return combine_path(m_save_path, st.file_path(file)); } else { return m_save_path; } } void torrent::set_error(error_code const& ec, int error_file) { TORRENT_ASSERT(is_single_thread()); m_error = ec; m_error_file = error_file; update_gauge(); if (alerts().should_post()) alerts().emplace_alert(get_handle(), ec , resolve_filename(error_file)); #ifndef TORRENT_DISABLE_LOGGING if (ec) { char buf[1024]; snprintf(buf, sizeof(buf), "error %s: %s", ec.message().c_str() , resolve_filename(error_file).c_str()); log_to_all_peers(buf); } #endif state_updated(); update_state_list(); } void torrent::auto_managed(bool a) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (m_auto_managed == a) return; bool checking_files = should_check_files(); m_auto_managed = a; update_gauge(); update_want_scrape(); update_state_list(); state_updated(); // we need to save this new state as well set_need_save_resume(); // recalculate which torrents should be // paused m_ses.trigger_auto_manage(); if (!checking_files && should_check_files()) { start_checking(); } } namespace { int clamped_subtract(int a, int b) { if (a < b) return 0; return a - b; } } // anonymous namespace // this is called every time the session timer takes a step back. Since the // session time is meant to fit in 16 bits, it only covers a range of // about 18 hours. This means every few hours the whole epoch of this // clock is shifted forward. All timestamp in this clock must then be // shifted backwards to remain the same. Anything that's shifted back // beyond the new epoch is clamped to 0 (to represent the oldest timestamp // currently representable by the session_time) void torrent::step_session_time(int seconds) { if (m_peer_list) { for (peer_list::iterator j = m_peer_list->begin_peer() , end(m_peer_list->end_peer()); j != end; ++j) { torrent_peer* pe = *j; pe->last_optimistically_unchoked = clamped_subtract(pe->last_optimistically_unchoked, seconds); pe->last_connected = clamped_subtract(pe->last_connected, seconds); } } // m_active_time, m_seeding_time and m_finished_time are absolute cunters // of the historical time we've spent in each state. The current time // we've spent in those states (this session) is calculated by // session_time() - m_started // session_time() - m_became_seed // session_time() - m_became_finished respectively. If any of the // comparison points were pulled back to the oldest representable value (0) // the left-over time must be transferred into the m_*_time counters. if (m_started < seconds && !is_paused()) { int lost_seconds = seconds - m_started; m_active_time += lost_seconds; } m_started = clamped_subtract(m_started, seconds); if (m_became_seed < seconds && is_seed()) { int lost_seconds = seconds - m_became_seed; m_seeding_time += lost_seconds; } m_became_seed = clamped_subtract(m_became_seed, seconds); if (m_became_finished < seconds && is_finished()) { int lost_seconds = seconds - m_became_finished; m_finished_time += lost_seconds; } m_became_finished = clamped_subtract(m_became_finished, seconds); m_last_saved_resume = clamped_subtract(m_last_saved_resume, seconds); m_upload_mode_time = clamped_subtract(m_upload_mode_time, seconds); } // the higher seed rank, the more important to seed int torrent::seed_rank(aux::session_settings const& s) const { TORRENT_ASSERT(is_single_thread()); enum flags { seed_ratio_not_met = 0x40000000, no_seeds = 0x20000000, recently_started = 0x10000000, prio_mask = 0x0fffffff }; if (!is_finished()) return 0; int scale = 1000; if (!is_seed()) scale = 500; int ret = 0; boost::int64_t fin_time = finished_time(); boost::int64_t download_time = int(active_time()) - fin_time; // if we haven't yet met the seed limits, set the seed_ratio_not_met // flag. That will make this seed prioritized // downloaded may be 0 if the torrent is 0-sized boost::int64_t downloaded = (std::max)(m_total_downloaded, m_torrent_file->total_size()); if (fin_time < s.get_int(settings_pack::seed_time_limit) && (download_time > 1 && fin_time * 100 / download_time < s.get_int(settings_pack::seed_time_ratio_limit)) && downloaded > 0 && m_total_uploaded * 100 / downloaded < s.get_int(settings_pack::share_ratio_limit)) ret |= seed_ratio_not_met; // if this torrent is running, and it was started less // than 30 minutes ago, give it priority, to avoid oscillation if (!is_paused() && (m_ses.session_time() - m_started) < 30 * 60) ret |= recently_started; // if we have any scrape data, use it to calculate // seed rank int seeds = 0; int downloaders = 0; if (m_complete != 0xffffff) seeds = m_complete; else seeds = m_peer_list ? m_peer_list->num_seeds() : 0; if (m_incomplete != 0xffffff) downloaders = m_incomplete; else downloaders = m_peer_list ? m_peer_list->num_peers() - m_peer_list->num_seeds() : 0; if (seeds == 0) { ret |= no_seeds; ret |= downloaders & prio_mask; } else { ret |= ((1 + downloaders) * scale / seeds) & prio_mask; } return ret; } // this is an async operation triggered by the client // TODO: add a flag to ignore stats, and only care about resume data for // content. For unchanged files, don't trigger a load of the metadata // just to save an empty resume data file void torrent::save_resume_data(int flags) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (!valid_metadata()) { alerts().emplace_alert(get_handle() , errors::no_metadata); return; } if ((flags & torrent_handle::only_if_modified) && !m_need_save_resume_data) { alerts().emplace_alert(get_handle() , errors::resume_data_not_modified); return; } m_need_save_resume_data = false; m_last_saved_resume = m_ses.session_time(); m_save_resume_flags = boost::uint8_t(flags); state_updated(); if (m_state == torrent_status::checking_files || m_state == torrent_status::checking_resume_data) { if (!need_loaded()) { alerts().emplace_alert(get_handle() , m_error); return; } // storage may be NULL during shutdown if (!m_storage) { TORRENT_ASSERT(m_abort); alerts().emplace_alert(get_handle() , boost::asio::error::operation_aborted); return; } boost::shared_ptr rd(new entry); write_resume_data(*rd); alerts().emplace_alert(rd, get_handle()); return; } // TODO: 3 this really needs to be moved to do_async_save_resume_data. // flags need to be passed on if ((flags & torrent_handle::flush_disk_cache) && m_storage.get()) m_ses.disk_thread().async_release_files(m_storage.get()); m_ses.queue_async_resume_data(shared_from_this()); } bool torrent::do_async_save_resume_data() { if (!need_loaded()) { alerts().emplace_alert(get_handle(), m_error); return false; } // storage may be NULL during shutdown if (!m_storage) { TORRENT_ASSERT(m_abort); alerts().emplace_alert(get_handle() , boost::asio::error::operation_aborted); return false; } inc_refcount("save_resume"); m_ses.disk_thread().async_save_resume_data(m_storage.get() , boost::bind(&torrent::on_save_resume_data, shared_from_this(), _1)); return true; } bool torrent::should_check_files() const { TORRENT_ASSERT(is_single_thread()); // #error should m_allow_peers really affect checking? return m_state == torrent_status::checking_files && m_allow_peers && !has_error() && !m_abort && !m_graceful_pause_mode && !m_ses.is_paused(); } void torrent::flush_cache() { TORRENT_ASSERT(is_single_thread()); // storage may be NULL during shutdown if (!m_storage) { TORRENT_ASSERT(m_abort); return; } inc_refcount("release_files"); m_ses.disk_thread().async_release_files(m_storage.get() , boost::bind(&torrent::on_cache_flushed, shared_from_this(), _1, true)); } void torrent::on_cache_flushed(disk_io_job const*, bool const manually_triggered) { dec_refcount("release_files"); TORRENT_ASSERT(is_single_thread()); if (m_ses.is_aborted()) return; if (manually_triggered || alerts().should_post()) alerts().emplace_alert(get_handle()); } void torrent::on_torrent_aborted() { TORRENT_ASSERT(is_single_thread()); // there should be no more disk activity for this torrent now, we can // release the disk io handle m_storage.reset(); } bool torrent::is_paused() const { return !m_allow_peers || m_ses.is_paused() || m_graceful_pause_mode; } void torrent::pause(bool graceful) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (m_allow_peers) { // we need to save this new state set_need_save_resume(); } int const flags = graceful ? flag_graceful_pause : 0; set_allow_peers(false, flags | flag_clear_disk_cache); } void torrent::do_pause(bool const clear_disk_cache) { TORRENT_ASSERT(is_single_thread()); if (!is_paused()) return; // this torrent may be about to consider itself inactive. If so, we want // to prevent it from doing so, since it's being paused unconditionally // now. An illustrative example of this is a torrent that completes // downloading when active_seeds = 0. It completes, it gets paused and it // should not come back to life again. if (m_pending_active_change) { m_inactivity_timer.cancel(); } #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { if ((*i)->on_pause()) return; } TORRENT_CATCH (std::exception&) {} } #endif m_connect_boost_counter = static_cast(settings().get_int(settings_pack::torrent_connect_boost)); m_inactive = false; update_state_list(); update_want_tick(); m_active_time += m_ses.session_time() - m_started; if (is_seed()) m_seeding_time += m_ses.session_time() - m_became_seed; if (is_finished()) m_finished_time += m_ses.session_time() - m_became_finished; state_updated(); update_want_peers(); update_want_scrape(); #ifndef TORRENT_DISABLE_LOGGING log_to_all_peers("pausing"); #endif // when checking and being paused in graceful pause mode, we // post the paused alert when the last outstanding disk job completes if (m_state == torrent_status::checking_files) { if (m_checking_piece == m_num_checked_pieces) { if (alerts().should_post()) alerts().emplace_alert(get_handle()); } disconnect_all(errors::torrent_paused, op_bittorrent); return; } if (!m_graceful_pause_mode) { // this will make the storage close all // files and flush all cached data if (m_storage.get() && clear_disk_cache) { TORRENT_ASSERT(m_storage); m_ses.disk_thread().async_stop_torrent(m_storage.get() , boost::bind(&torrent::on_torrent_paused, shared_from_this(), _1)); } else { if (alerts().should_post()) alerts().emplace_alert(get_handle()); } disconnect_all(errors::torrent_paused, op_bittorrent); } else { // disconnect all peers with no outstanding data to receive // and choke all remaining peers to prevent responding to new // requests std::vector to_disconnect; for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = *i; TORRENT_ASSERT(p->associated_torrent().lock().get() == this); if (p->is_disconnecting()) continue; if (p->outstanding_bytes() > 0) { #ifndef TORRENT_DISABLE_LOGGING p->peer_log(peer_log_alert::info, "CHOKING_PEER", "torrent graceful paused"); #endif // remove any un-sent requests from the queue p->clear_request_queue(); // don't accept new requests from the peer p->choke_this_peer(); continue; } to_disconnect.push_back(p); } for (peer_iterator i = to_disconnect.begin(); i != to_disconnect.end(); ++i) { peer_connection* p = *i; // since we're currently in graceful pause mode, the last peer to // disconnect (assuming all peers end up begin disconnected here) // will post the torrent_paused_alert #ifndef TORRENT_DISABLE_LOGGING p->peer_log(peer_log_alert::info, "CLOSING_CONNECTION", "torrent_paused"); #endif p->disconnect(errors::torrent_paused, op_bittorrent); } } stop_announcing(); // if the torrent is pinned, we should not unload it if (!is_pinned()) { m_ses.evict_torrent(this); } } #ifndef TORRENT_DISABLE_LOGGING void torrent::log_to_all_peers(char const* message) { TORRENT_ASSERT(is_single_thread()); for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { (*i)->peer_log(peer_log_alert::info, "TORRENT", "%s", message); } debug_log("%s", message); } #endif // add or remove a url that will be attempted for // finding the file(s) in this torrent. void torrent::add_web_seed(std::string const& url, web_seed_entry::type_t type) { web_seed_t ent(url, type); // don't add duplicates if (std::find(m_web_seeds.begin(), m_web_seeds.end(), ent) != m_web_seeds.end()) return; m_web_seeds.push_back(ent); set_need_save_resume(); } void torrent::add_web_seed(std::string const& url, web_seed_entry::type_t type , std::string const& auth, web_seed_entry::headers_t const& extra_headers) { web_seed_t ent(url, type, auth, extra_headers); // don't add duplicates if (std::find(m_web_seeds.begin(), m_web_seeds.end(), ent) != m_web_seeds.end()) return; m_web_seeds.push_back(ent); set_need_save_resume(); } void torrent::set_allow_peers(bool b, int flags) { TORRENT_ASSERT(is_single_thread()); // if there are no peers, there is no point in a graceful pause mode. In // fact, the promise to post the torrent_paused_alert exactly once is // maintained by the last peer to be disconnected in graceful pause mode, // if there are no peers, we must not enter graceful pause mode, and post // the torrent_paused_alert immediately instead. if (m_connections.empty()) flags &= ~flag_graceful_pause; if (m_allow_peers == b) { // there is one special case here. If we are // currently in graceful pause mode, and we just turned into regular // paused mode, we need to actually pause the torrent properly if (m_allow_peers == false && m_graceful_pause_mode == true && (flags & flag_graceful_pause) == 0) { m_graceful_pause_mode = false; update_gauge(); do_pause(); } return; } m_allow_peers = b; if (!m_ses.is_paused()) m_graceful_pause_mode = (flags & flag_graceful_pause) ? true : false; if (!b) { m_announce_to_dht = false; m_announce_to_trackers = false; m_announce_to_lsd = false; } update_gauge(); update_want_scrape(); update_want_peers(); update_state_list(); state_updated(); if (!b) { do_pause(flags & flag_clear_disk_cache); } else { do_resume(); } } void torrent::resume() { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (m_allow_peers && m_announce_to_dht && m_announce_to_trackers && m_announce_to_lsd) return; m_announce_to_dht = true; m_announce_to_trackers = true; m_announce_to_lsd = true; m_allow_peers = true; if (!m_ses.is_paused()) m_graceful_pause_mode = false; update_gauge(); // we need to save this new state set_need_save_resume(); update_want_scrape(); do_resume(); } void torrent::do_resume() { TORRENT_ASSERT(is_single_thread()); if (is_paused()) { update_want_tick(); return; } #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { if ((*i)->on_resume()) return; } TORRENT_CATCH (std::exception&) {} } #endif if (alerts().should_post()) alerts().emplace_alert(get_handle()); m_started = m_ses.session_time(); if (is_seed()) m_became_seed = m_started; if (is_finished()) m_became_finished = m_started; clear_error(); if (m_state == torrent_status::checking_files) { if (m_auto_managed) m_ses.trigger_auto_manage(); if (should_check_files()) start_checking(); } state_updated(); update_want_peers(); update_want_tick(); update_want_scrape(); if (m_state == torrent_status::checking_files) return; start_announcing(); do_connect_boost(); } void torrent::update_tracker_timer(time_point now) { TORRENT_ASSERT(is_single_thread()); if (!m_announcing) { #ifndef TORRENT_DISABLE_LOGGING debug_log("*** update tracker timer: not announcing"); #endif return; } time_point next_announce = max_time(); int tier = INT_MAX; bool found_working = false; for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) { #ifndef TORRENT_DISABLE_LOGGING debug_log("*** tracker: \"%s\" " "[ tiers: %d trackers: %d" " found: %d i->tier: %d tier: %d" " working: %d fails: %d limit: %d upd: %d ]" , i->url.c_str(), settings().get_bool(settings_pack::announce_to_all_tiers) , settings().get_bool(settings_pack::announce_to_all_trackers), found_working , i->tier, tier, i->is_working(), i->fails, i->fail_limit , i->updating); #endif if (settings().get_bool(settings_pack::announce_to_all_tiers) && found_working && i->tier <= tier && tier != INT_MAX) continue; if (i->tier > tier && !settings().get_bool(settings_pack::announce_to_all_tiers)) break; if (i->is_working()) { tier = i->tier; found_working = false; } if (i->fails >= i->fail_limit && i->fail_limit != 0) continue; if (i->updating) { found_working = true; } else { time_point const next_tracker_announce = std::max(i->next_announce, i->min_announce); if (next_tracker_announce < next_announce && (!found_working || i->is_working())) next_announce = next_tracker_announce; } if (i->is_working()) found_working = true; if (found_working && !settings().get_bool(settings_pack::announce_to_all_trackers) && !settings().get_bool(settings_pack::announce_to_all_tiers)) break; } if (next_announce <= now) next_announce = now; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** update tracker timer: next_announce < now %d" " m_waiting_tracker: %d next_announce_in: %d" , next_announce <= now, m_waiting_tracker , int(total_seconds(now - next_announce))); #endif // don't re-issue the timer if it's the same expiration time as last time // if m_waiting_tracker is false, expires_at() is undefined if (m_waiting_tracker && m_tracker_timer.expires_at() == next_announce) return; ++m_waiting_tracker; error_code ec; boost::weak_ptr self(shared_from_this()); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("tracker::on_tracker_announce_disp"); #endif m_tracker_timer.expires_at(next_announce, ec); m_tracker_timer.async_wait(boost::bind(&torrent::on_tracker_announce_disp, self, _1)); } void torrent::start_announcing() { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(state() != torrent_status::checking_files); if (is_paused()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("start_announcing(), paused"); #endif return; } // if we don't have metadata, we need to announce // before checking files, to get peers to // request the metadata from if (!m_files_checked && valid_metadata()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("start_announcing(), files not checked (with valid metadata)"); #endif return; } if (!m_torrent_file->is_valid() && !m_url.empty()) { #ifndef TORRENT_DISABLE_LOGGING debug_log("start_announcing(), downloading URL"); #endif return; } if (m_announcing) return; m_announcing = true; #ifndef TORRENT_DISABLE_DHT if ((!m_peer_list || m_peer_list->num_peers() < 50) && m_ses.dht()) { // we don't have any peers, prioritize // announcing this torrent with the DHT m_ses.prioritize_dht(shared_from_this()); } #endif if (!m_trackers.empty()) { // tell the tracker that we're back std::for_each(m_trackers.begin(), m_trackers.end() , boost::bind(&announce_entry::reset, _1)); } // reset the stats, since from the tracker's // point of view, this is a new session m_total_failed_bytes = 0; m_total_redundant_bytes = 0; m_stat.clear(); update_want_tick(); announce_with_tracker(); lsd_announce(); } void torrent::stop_announcing() { TORRENT_ASSERT(is_single_thread()); if (!m_announcing) return; error_code ec; m_tracker_timer.cancel(ec); m_announcing = false; time_point now = aux::time_now(); for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) { i->next_announce = now; i->min_announce = now; } announce_with_tracker(tracker_request::stopped); } int torrent::finished_time() const { // m_finished_time does not account for the current "session", just the // time before we last started this torrent. To get the current time, we // need to add the time since we started it return m_finished_time + ((!is_finished() || is_paused()) ? 0 : (m_ses.session_time() - m_became_finished)); } int torrent::active_time() const { // m_active_time does not account for the current "session", just the // time before we last started this torrent. To get the current time, we // need to add the time since we started it return m_active_time + (is_paused() ? 0 : m_ses.session_time() - m_started); } int torrent::seeding_time() const { // m_seeding_time does not account for the current "session", just the // time before we last started this torrent. To get the current time, we // need to add the time since we started it return m_seeding_time + ((!is_seed() || is_paused()) ? 0 : m_ses.session_time() - m_became_seed); } void torrent::second_tick(int tick_interval_ms) { TORRENT_ASSERT(want_tick()); TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (m_should_be_loaded == false) return; boost::weak_ptr self(shared_from_this()); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->tick(); } TORRENT_CATCH (std::exception&) {} } if (m_abort) return; #endif // if we're in upload only mode and we're auto-managed // leave upload mode every 10 minutes hoping that the error // condition has been fixed if (m_upload_mode && m_auto_managed && int(m_ses.session_time() - m_upload_mode_time) >= settings().get_int(settings_pack::optimistic_disk_retry)) { set_upload_mode(false); } if (m_storage_tick > 0 && is_loaded()) { --m_storage_tick; if (m_storage_tick == 0) { if (m_storage) { m_ses.disk_thread().async_tick_torrent(&storage() , boost::bind(&torrent::on_disk_tick_done , shared_from_this(), _1)); } update_want_tick(); } } if (is_paused() && !m_graceful_pause_mode) { // let the stats fade out to 0 // check the rate before ticking the stats so that the last update is sent // with the rate equal to zero if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) state_updated(); m_stat.second_tick(tick_interval_ms); // if the rate is 0, there's no update because of network transfers if (!(m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0)) update_want_tick(); return; } if (m_need_suggest_pieces_refresh) do_refresh_suggest_pieces(); if (settings().get_bool(settings_pack::rate_limit_ip_overhead)) { int up_limit = upload_limit(); int down_limit = download_limit(); if (down_limit > 0 && m_stat.download_ip_overhead() >= down_limit && alerts().should_post()) { alerts().emplace_alert(get_handle() , performance_alert::download_limit_too_low); } if (up_limit > 0 && m_stat.upload_ip_overhead() >= up_limit && alerts().should_post()) { alerts().emplace_alert(get_handle() , performance_alert::upload_limit_too_low); } } // ---- TIME CRITICAL PIECES ---- #if TORRENT_DEBUG_STREAMING > 0 std::vector queue; get_download_queue(&queue); std::vector peer_list; get_peer_info(peer_list); std::sort(queue.begin(), queue.end(), boost::bind(&partial_piece_info::piece_index, _1) < boost::bind(&partial_piece_info::piece_index, _2)); printf("average piece download time: %.2f s (+/- %.2f s)\n" , m_average_piece_time / 1000.f , m_piece_time_deviation / 1000.f); for (std::vector::iterator i = queue.begin() , end(queue.end()); i != end; ++i) { extern void print_piece(libtorrent::partial_piece_info* pp , std::vector const& peers , std::vector const& time_critical); print_piece(&*i, peer_list, m_time_critical_pieces); } #endif // TORRENT_DEBUG_STREAMING if (!m_time_critical_pieces.empty() && !upload_mode()) { request_time_critical_pieces(); } // ---- WEB SEEDS ---- maybe_connect_web_seeds(); m_swarm_last_seen_complete = m_last_seen_complete; int idx = 0; for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++idx) { // keep the peer object alive while we're // inspecting it boost::shared_ptr p = (*i)->self(); ++i; // look for the peer that saw a seed most recently m_swarm_last_seen_complete = (std::max)(p->last_seen_complete(), m_swarm_last_seen_complete); // updates the peer connection's ul/dl bandwidth // resource requests TORRENT_TRY { p->second_tick(tick_interval_ms); } TORRENT_CATCH (std::exception& e) { TORRENT_DECLARE_DUMMY(std::exception, e); (void)e; #ifndef TORRENT_DISABLE_LOGGING p->peer_log(peer_log_alert::info, "ERROR", "%s", e.what()); #endif p->disconnect(errors::no_error, op_bittorrent, 1); } if (p->is_disconnecting()) { i = m_connections.begin() + idx; --idx; } } if (m_ses.alerts().should_post()) m_ses.alerts().emplace_alert(get_handle(), tick_interval_ms, m_stat); m_total_uploaded += m_stat.last_payload_uploaded(); m_total_downloaded += m_stat.last_payload_downloaded(); m_stat.second_tick(tick_interval_ms); // these counters are saved in the resume data, since they updated // we need to save the resume data too m_need_save_resume_data = true; // if the rate is 0, there's no update because of network transfers if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) state_updated(); // this section determines whether the torrent is active or not. When it // changes state, it may also trigger the auto-manage logic to reconsider // which torrents should be queued and started. There is a low pass // filter in order to avoid flapping (auto_manage_startup). bool is_inactive = is_inactive_internal(); if (settings().get_bool(settings_pack::dont_count_slow_torrents)) { if (is_inactive != m_inactive && !m_pending_active_change) { int delay = settings().get_int(settings_pack::auto_manage_startup); m_inactivity_timer.expires_from_now(seconds(delay)); m_inactivity_timer.async_wait(boost::bind(&torrent::on_inactivity_tick , shared_from_this(), _1)); m_pending_active_change = true; } else if (is_inactive == m_inactive && m_pending_active_change) { m_inactivity_timer.cancel(); } } update_want_tick(); } bool torrent::is_inactive_internal() const { if (is_finished()) return m_stat.upload_payload_rate() < settings().get_int(settings_pack::inactive_up_rate); else return m_stat.download_payload_rate() < settings().get_int(settings_pack::inactive_down_rate); } void torrent::on_inactivity_tick(error_code const& ec) { m_pending_active_change = false; if (ec) return; bool is_inactive = is_inactive_internal(); if (is_inactive == m_inactive) return; m_inactive = is_inactive; update_state_list(); update_want_tick(); if (settings().get_bool(settings_pack::dont_count_slow_torrents)) m_ses.trigger_auto_manage(); } void torrent::maybe_connect_web_seeds() { if (m_abort) return; // if we have everything we want we don't need to connect to any web-seed if (!is_finished() && !m_web_seeds.empty() && m_files_checked && int(m_connections.size()) < m_max_connections && m_ses.num_connections() < settings().get_int(settings_pack::connections_limit)) { // keep trying web-seeds if there are any // first find out which web seeds we are connected to for (std::list::iterator i = m_web_seeds.begin(); i != m_web_seeds.end();) { std::list::iterator w = i++; if (w->peer_info.connection) continue; if (w->retry > aux::time_now()) continue; if (w->resolving) continue; if (w->removed) continue; connect_to_url_seed(w); } } } void torrent::recalc_share_mode() { TORRENT_ASSERT(share_mode()); if (is_seed()) return; int pieces_in_torrent = m_torrent_file->num_pieces(); int num_seeds = 0; int num_peers = 0; int num_downloaders = 0; int missing_pieces = 0; int num_interested = 0; for (peer_iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { TORRENT_INCREMENT(m_iterating_connections); peer_connection* p = *i; if (p->is_connecting()) continue; if (p->is_disconnecting()) continue; ++num_peers; if (p->is_seed()) { ++num_seeds; continue; } if (p->share_mode()) continue; if (p->upload_only()) continue; if ((*i)->is_peer_interested()) ++num_interested; ++num_downloaders; missing_pieces += pieces_in_torrent - p->num_have_pieces(); } if (num_peers == 0) return; if (num_seeds * 100 / num_peers > 50 && (num_peers * 100 / m_max_connections > 90 || num_peers > 20)) { // we are connected to more than 90% seeds (and we're beyond // 90% of the max number of connections). That will // limit our ability to upload. We need more downloaders. // disconnect some seeds so that we don't have more than 50% int to_disconnect = num_seeds - num_peers / 2; std::vector seeds; seeds.reserve(num_seeds); for (peer_iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) { peer_connection* p = *i; if (p->is_seed()) seeds.push_back(p); } std::random_shuffle(seeds.begin(), seeds.end(), randint); TORRENT_ASSERT(to_disconnect <= int(seeds.size())); for (int i = 0; i < to_disconnect; ++i) seeds[i]->disconnect(errors::upload_upload_connection , op_bittorrent); } if (num_downloaders == 0) return; // assume that the seeds are about as fast as us. During the time // we can download one piece, and upload one piece, each seed // can upload two pieces. missing_pieces -= 2 * num_seeds; if (missing_pieces <= 0) return; // missing_pieces represents our opportunity to download pieces // and share them more than once each // now, download at least one piece, otherwise download one more // piece if our downloaded (and downloading) pieces is less than 50% // of the uploaded bytes int num_downloaded_pieces = (std::max)(m_picker->num_have() , pieces_in_torrent - m_picker->num_filtered()); if (boost::int64_t(num_downloaded_pieces) * m_torrent_file->piece_length() * settings().get_int(settings_pack::share_mode_target) > m_total_uploaded && num_downloaded_pieces > 0) return; // don't have more pieces downloading in parallel than 5% of the total // number of pieces we have downloaded if (m_picker->get_download_queue_size() > num_downloaded_pieces / 20) return; // one more important property is that there are enough pieces // that more than one peer wants to download // make sure that there are enough downloaders for the rarest // piece. Go through all pieces, figure out which one is the rarest // and how many peers that has that piece std::vector rarest_pieces; int num_pieces = m_torrent_file->num_pieces(); int rarest_rarity = INT_MAX; for (int i = 0; i < num_pieces; ++i) { piece_picker::piece_stats_t ps = m_picker->piece_stats(i); if (ps.peer_count == 0) continue; if (ps.priority == 0 && (ps.have || ps.downloading)) { m_picker->set_piece_priority(i, 4); continue; } // don't count pieces we already have or are trying to download if (ps.priority > 0 || ps.have) continue; if (int(ps.peer_count) > rarest_rarity) continue; if (int(ps.peer_count) == rarest_rarity) { rarest_pieces.push_back(i); continue; } rarest_pieces.clear(); rarest_rarity = ps.peer_count; rarest_pieces.push_back(i); } update_gauge(); update_want_peers(); // now, rarest_pieces is a list of all pieces that are the rarest ones. // and rarest_rarity is the number of peers that have the rarest pieces // if there's only a single peer that doesn't have the rarest piece // it's impossible for us to download one piece and upload it // twice. i.e. we cannot get a positive share ratio if (num_peers - rarest_rarity < settings().get_int(settings_pack::share_mode_target)) return; // now, pick one of the rarest pieces to download int pick = random() % rarest_pieces.size(); bool was_finished = is_finished(); m_picker->set_piece_priority(rarest_pieces[pick], 4); update_gauge(); update_peer_interest(was_finished); update_want_peers(); } #ifndef TORRENT_NO_DEPRECATE // TODO: 2 this should probably be removed void torrent::refresh_explicit_cache(int cache_size) { TORRENT_ASSERT(is_single_thread()); if (!ready_for_connections()) return; if (m_abort) return; TORRENT_ASSERT(m_storage); if (!is_loaded()) return; // rotate the cached pieces cache_status status; m_ses.disk_thread().get_cache_info(&status, false, m_storage.get()); // add blocks_per_piece / 2 in order to round to closest whole piece int blocks_per_piece = m_torrent_file->piece_length() / block_size(); int num_cache_pieces = (cache_size + blocks_per_piece / 2) / blocks_per_piece; if (num_cache_pieces > m_torrent_file->num_pieces()) num_cache_pieces = m_torrent_file->num_pieces(); std::vector avail_vec; if (has_picker()) { m_picker->get_availability(avail_vec); } else { // we don't keep track of availability, do it the expensive way // do a linear search from the first piece for (int i = 0; i < m_torrent_file->num_pieces(); ++i) { int availability = 0; if (!have_piece(i)) { avail_vec.push_back(INT_MAX); continue; } for (const_peer_iterator j = this->begin(); j != this->end(); ++j) if ((*j)->has_piece(i)) ++availability; avail_vec.push_back(availability); } } // now pick the num_cache_pieces rarest pieces from avail_vec std::vector > pieces(m_torrent_file->num_pieces()); for (int i = 0; i < m_torrent_file->num_pieces(); ++i) { pieces[i].second = i; if (!have_piece(i)) pieces[i].first = INT_MAX; else pieces[i].first = avail_vec[i]; } // remove write cache entries status.pieces.erase(std::remove_if(status.pieces.begin(), status.pieces.end() , boost::bind(&cached_piece_info::kind, _1) == cached_piece_info::write_cache) , status.pieces.end()); // decrease the availability of the pieces that are // already in the read cache, to move them closer to // the beginning of the pieces list, and more likely // to be included in this round of cache pieces for (std::vector::iterator i = status.pieces.begin() , end(status.pieces.end()); i != end; ++i) { --pieces[i->piece].first; } std::random_shuffle(pieces.begin(), pieces.end(), randint); std::stable_sort(pieces.begin(), pieces.end() , boost::bind(&std::pair::first, _1) < boost::bind(&std::pair::first, _2)); avail_vec.clear(); for (int i = 0; i < num_cache_pieces; ++i) { if (pieces[i].first == INT_MAX) break; avail_vec.push_back(pieces[i].second); } if (!avail_vec.empty()) { // the number of pieces to cache for this torrent is proportional // the number of peers it has, divided by the total number of peers. // Each peer gets an equal share of the cache avail_vec.resize((std::min)(num_cache_pieces, int(avail_vec.size()))); for (std::vector::iterator i = avail_vec.begin() , end(avail_vec.end()); i != end; ++i) { inc_refcount("cache_piece"); m_ses.disk_thread().async_cache_piece(m_storage.get(), *i , boost::bind(&torrent::on_disk_cache_complete , shared_from_this(), _1)); } } } #endif // TORRENT_NO_DEPRECATE void torrent::sent_bytes(int bytes_payload, int bytes_protocol) { m_stat.sent_bytes(bytes_payload, bytes_protocol); m_ses.sent_bytes(bytes_payload, bytes_protocol); } void torrent::received_bytes(int bytes_payload, int bytes_protocol) { m_stat.received_bytes(bytes_payload, bytes_protocol); m_ses.received_bytes(bytes_payload, bytes_protocol); } void torrent::trancieve_ip_packet(int bytes, bool ipv6) { m_stat.trancieve_ip_packet(bytes, ipv6); m_ses.trancieve_ip_packet(bytes, ipv6); } void torrent::sent_syn(bool ipv6) { m_stat.sent_syn(ipv6); m_ses.sent_syn(ipv6); } void torrent::received_synack(bool ipv6) { m_stat.received_synack(ipv6); m_ses.received_synack(ipv6); } #if TORRENT_DEBUG_STREAMING > 0 char const* esc(char const* code) { // this is a silly optimization // to avoid copying of strings enum { num_strings = 200 }; static char buf[num_strings][20]; static int round_robin = 0; char* ret = buf[round_robin]; ++round_robin; if (round_robin >= num_strings) round_robin = 0; ret[0] = '\033'; ret[1] = '['; int i = 2; int j = 0; while (code[j]) ret[i++] = code[j++]; ret[i++] = 'm'; ret[i++] = 0; return ret; } int peer_index(libtorrent::tcp::endpoint addr , std::vector const& peers) { using namespace libtorrent; std::vector::const_iterator i = std::find_if(peers.begin() , peers.end(), boost::bind(&peer_info::ip, _1) == addr); if (i == peers.end()) return -1; return i - peers.begin(); } void print_piece(libtorrent::partial_piece_info* pp , std::vector const& peers , std::vector const& time_critical) { using namespace libtorrent; time_point now = clock_type::now(); float deadline = 0.f; float last_request = 0.f; int timed_out = -1; int piece = pp->piece_index; std::vector::const_iterator i = std::find_if(time_critical.begin(), time_critical.end() , boost::bind(&time_critical_piece::piece, _1) == piece); if (i != time_critical.end()) { deadline = total_milliseconds(i->deadline - now) / 1000.f; if (i->last_requested == min_time()) last_request = -1; else last_request = total_milliseconds(now - i->last_requested) / 1000.f; timed_out = i->timed_out; } int num_blocks = pp->blocks_in_piece; printf("%5d: [", piece); for (int j = 0; j < num_blocks; ++j) { int index = pp ? peer_index(pp->blocks[j].peer(), peers) % 36 : -1; char chr = '+'; if (index >= 0) chr = (index < 10)?'0' + index:'A' + index - 10; char const* color = ""; char const* multi_req = ""; if (pp->blocks[j].num_peers > 1) multi_req = esc("1"); if (pp->blocks[j].bytes_progress > 0 && pp->blocks[j].state == block_info::requested) { color = esc("33;7"); chr = '0' + (pp->blocks[j].bytes_progress * 10 / pp->blocks[j].block_size); } else if (pp->blocks[j].state == block_info::finished) color = esc("32;7"); else if (pp->blocks[j].state == block_info::writing) color = esc("36;7"); else if (pp->blocks[j].state == block_info::requested) color = esc("0"); else { color = esc("0"); chr = ' '; } printf("%s%s%c%s", color, multi_req, chr, esc("0")); } printf("%s]", esc("0")); if (deadline != 0.f) printf(" deadline: %f last-req: %f timed_out: %d\n" , deadline, last_request, timed_out); else printf("\n"); } #endif // TORRENT_DEBUG_STREAMING namespace { struct busy_block_t { int peers; int index; bool operator<(busy_block_t rhs) const { return peers < rhs.peers; } }; void pick_busy_blocks(piece_picker const* picker , int piece , int blocks_in_piece , int timed_out , std::vector& interesting_blocks , piece_picker::downloading_piece const& pi) { // if there aren't any free blocks in the piece, and the piece is // old enough, we may switch into busy mode for this piece. In this // case busy_blocks and busy_count are set to contain the eligible // busy blocks we may pick // first, figure out which blocks are eligible for picking // in "busy-mode" busy_block_t* busy_blocks = TORRENT_ALLOCA(busy_block_t, blocks_in_piece); int busy_count = 0; piece_picker::block_info const* info = picker->blocks_for_piece(pi); // pick busy blocks from the piece for (int k = 0; k < blocks_in_piece; ++k) { // only consider blocks that have been requested // and we're still waiting for them if (info[k].state != piece_picker::block_info::state_requested) continue; piece_block b(piece, k); // only allow a single additional request per block, in order // to spread it out evenly across all stalled blocks if (info[k].num_peers > timed_out) continue; busy_blocks[busy_count].peers = info[k].num_peers; busy_blocks[busy_count].index = k; ++busy_count; #if TORRENT_DEBUG_STREAMING > 1 printf(" [%d (%d)]", b.block_index, info[k].num_peers); #endif } #if TORRENT_DEBUG_STREAMING > 1 printf("\n"); #endif // then sort blocks by the number of peers with requests // to the blocks (request the blocks with the fewest peers // first) std::sort(busy_blocks, busy_blocks + busy_count); // then insert them into the interesting_blocks vector for (int k = 0; k < busy_count; ++k) { interesting_blocks.push_back( piece_block(piece, busy_blocks[k].index)); } } void pick_time_critical_block(std::vector& peers , std::vector& ignore_peers , std::set& peers_with_requests , piece_picker::downloading_piece const& pi , time_critical_piece* i , piece_picker const* picker , int blocks_in_piece , int timed_out) { std::vector interesting_blocks; std::vector backup1; std::vector backup2; std::vector ignore; time_point now = aux::time_now(); // loop until every block has been requested from this piece (i->piece) do { // if this peer's download time exceeds 2 seconds, we're done. // We don't want to build unreasonably long request queues if (!peers.empty() && peers[0]->download_queue_time() > milliseconds(2000)) { #if TORRENT_DEBUG_STREAMING > 1 printf("queue time: %d ms, done\n" , int(total_milliseconds(peers[0]->download_queue_time()))); #endif break; } // pick the peer with the lowest download_queue_time that has i->piece std::vector::iterator p = std::find_if(peers.begin(), peers.end() , boost::bind(&peer_connection::has_piece, _1, i->piece)); // obviously we'll have to skip it if we don't have a peer that has // this piece if (p == peers.end()) { #if TORRENT_DEBUG_STREAMING > 1 printf("out of peers, done\n"); #endif break; } peer_connection& c = **p; interesting_blocks.clear(); backup1.clear(); backup2.clear(); // specifically request blocks with no affinity towards fast or slow // pieces. If we would, the picked block might end up in one of // the backup lists picker->add_blocks(i->piece, c.get_bitfield(), interesting_blocks , backup1, backup2, blocks_in_piece, 0, c.peer_info_struct() , ignore, 0); interesting_blocks.insert(interesting_blocks.end() , backup1.begin(), backup1.end()); interesting_blocks.insert(interesting_blocks.end() , backup2.begin(), backup2.end()); bool busy_mode = false; if (interesting_blocks.empty()) { busy_mode = true; #if TORRENT_DEBUG_STREAMING > 1 printf("interesting_blocks.empty()\n"); #endif // there aren't any free blocks to pick, and the piece isn't // old enough to pick busy blocks yet. break to continue to // the next piece. if (timed_out == 0) { #if TORRENT_DEBUG_STREAMING > 1 printf("not timed out, moving on to next piece\n"); #endif break; } #if TORRENT_DEBUG_STREAMING > 1 printf("pick busy blocks\n"); #endif pick_busy_blocks(picker, i->piece, blocks_in_piece, timed_out , interesting_blocks, pi); } // we can't pick anything from this piece, we're done with it. // move on to the next one if (interesting_blocks.empty()) break; piece_block b = interesting_blocks.front(); // in busy mode we need to make sure we don't do silly // things like requesting the same block twice from the // same peer std::vector const& dq = c.download_queue(); bool already_requested = std::find_if(dq.begin(), dq.end() , has_block(b)) != dq.end(); if (already_requested) { // if the piece is stalled, we may end up picking a block // that we've already requested from this peer. If so, we should // simply disregard this peer from this piece, since this peer // is likely to be causing the stall. We should request it // from the next peer in the list // the peer will be put back in the set for the next piece ignore_peers.push_back(*p); peers.erase(p); #if TORRENT_DEBUG_STREAMING > 1 printf("piece already requested by peer, try next peer\n"); #endif // try next peer continue; } std::vector const& rq = c.request_queue(); bool already_in_queue = std::find_if(rq.begin(), rq.end() , has_block(b)) != rq.end(); if (already_in_queue) { if (!c.make_time_critical(b)) { #if TORRENT_DEBUG_STREAMING > 1 printf("piece already time-critical and in queue for peer, trying next peer\n"); #endif ignore_peers.push_back(*p); peers.erase(p); continue; } i->last_requested = now; #if TORRENT_DEBUG_STREAMING > 1 printf("piece already in queue for peer, making time-critical\n"); #endif // we inserted a new block in the request queue, this // makes us actually send it later peers_with_requests.insert(peers_with_requests.begin(), &c); } else { if (!c.add_request(b, peer_connection::req_time_critical | (busy_mode ? peer_connection::req_busy : 0))) { #if TORRENT_DEBUG_STREAMING > 1 printf("failed to request block [%d, %d]\n" , b.piece_index, b.block_index); #endif ignore_peers.push_back(*p); peers.erase(p); continue; } #if TORRENT_DEBUG_STREAMING > 1 printf("requested block [%d, %d]\n" , b.piece_index, b.block_index); #endif peers_with_requests.insert(peers_with_requests.begin(), &c); } if (!busy_mode) i->last_requested = now; if (i->first_requested == min_time()) i->first_requested = now; if (!c.can_request_time_critical()) { #if TORRENT_DEBUG_STREAMING > 1 printf("peer cannot pick time critical pieces\n"); #endif peers.erase(p); // try next peer continue; } // resort p, since it will have a higher download_queue_time now while (p != peers.end()-1 && (*p)->download_queue_time() > (*(p+1))->download_queue_time()) { std::iter_swap(p, p+1); ++p; } } while (!interesting_blocks.empty()); } } // anonymous namespace void torrent::request_time_critical_pieces() { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(!upload_mode()); // build a list of peers and sort it by download_queue_time // we use this sorted list to determine which peer we should // request a block from. The earlier a peer is in the list, // the sooner we will fully download the block we request. std::vector peers; peers.reserve(m_connections.size()); // some peers are marked as not being able to request time critical // blocks from. For instance, peers that have choked us, peers that are // on parole (i.e. they are believed to have sent us bad data), peers // that are being disconnected, in upload mode etc. std::remove_copy_if(m_connections.begin(), m_connections.end() , std::back_inserter(peers), !boost::bind(&peer_connection::can_request_time_critical, _1)); // sort by the time we believe it will take this peer to send us all // blocks we've requested from it. The shorter time, the better candidate // it is to request a time critical block from. std::sort(peers.begin(), peers.end() , boost::bind(&peer_connection::download_queue_time, _1, 16*1024) < boost::bind(&peer_connection::download_queue_time, _2, 16*1024)); // remove the bottom 10% of peers from the candidate set. // this is just to remove outliers that might stall downloads int new_size = (peers.size() * 9 + 9) / 10; TORRENT_ASSERT(new_size <= int(peers.size())); peers.resize(new_size); // remember all the peers we issued requests to, so we can commit them // at the end of this function. Instead of sending the requests right // away, we batch them up and send them in a single write to the TCP // socket, increasing the chance that they will all be sent in the same // packet. std::set peers_with_requests; // peers that should be temporarily ignored for a specific piece // in order to give priority to other peers. They should be used for // subsequent pieces, so they are stored in this vector until the // piece is done std::vector ignore_peers; time_point now = clock_type::now(); // now, iterate over all time critical pieces, in order of importance, and // request them from the peers, in order of responsiveness. i.e. request // the most time critical pieces from the fastest peers. for (std::vector::iterator i = m_time_critical_pieces.begin() , end(m_time_critical_pieces.end()); i != end; ++i) { #if TORRENT_DEBUG_STREAMING > 1 printf("considering %d\n", i->piece); #endif if (peers.empty()) { #if TORRENT_DEBUG_STREAMING > 1 printf("out of peers, done\n"); #endif break; } // the +1000 is to compensate for the fact that we only call this // function once per second, so if we need to request it 500 ms from // now, we should request it right away if (i != m_time_critical_pieces.begin() && i->deadline > now + milliseconds(m_average_piece_time + m_piece_time_deviation * 4 + 1000)) { // don't request pieces whose deadline is too far in the future // this is one of the termination conditions. We don't want to // send requests for all pieces in the torrent right away #if TORRENT_DEBUG_STREAMING > 0 printf("reached deadline horizon [%f + %f * 4 + 1]\n" , m_average_piece_time / 1000.f , m_piece_time_deviation / 1000.f); #endif break; } piece_picker::downloading_piece pi; m_picker->piece_info(i->piece, pi); // the number of "times" this piece has timed out. int timed_out = 0; int blocks_in_piece = m_picker->blocks_in_piece(i->piece); #if TORRENT_DEBUG_STREAMING > 0 i->timed_out = timed_out; #endif int free_to_request = blocks_in_piece - pi.finished - pi.writing - pi.requested; if (free_to_request == 0) { if (i->last_requested == min_time()) i->last_requested = now; // if it's been more than half of the typical download time // of a piece since we requested the last block, allow // one more request per block if (m_average_piece_time > 0) timed_out = total_milliseconds(now - i->last_requested) / (std::max)(int(m_average_piece_time + m_piece_time_deviation / 2), 1); #if TORRENT_DEBUG_STREAMING > 0 i->timed_out = timed_out; #endif // every block in this piece is already requested // there's no need to consider this piece, unless it // appears to be stalled. if (pi.requested == 0 || timed_out == 0) { #if TORRENT_DEBUG_STREAMING > 1 printf("skipping %d (full) [req: %d timed_out: %d ]\n" , i->piece, pi.requested , timed_out); #endif // if requested is 0, it meants all blocks have been received, and // we're just waiting for it to flush them to disk. // if last_requested is recent enough, we should give it some // more time // skip to the next piece continue; } // it's been too long since we requested the last block from // this piece. Allow re-requesting blocks from this piece #if TORRENT_DEBUG_STREAMING > 1 printf("timed out [average-piece-time: %d ms ]\n" , m_average_piece_time); #endif } // pick all blocks for this piece. the peers list is kept up to date // and sorted. when we issue a request to a peer, its download queue // time will increase and it may need to be bumped in the peers list, // since it's ordered by download queue time pick_time_critical_block(peers, ignore_peers , peers_with_requests , pi, &*i, m_picker.get() , blocks_in_piece, timed_out); // put back the peers we ignored into the peer list for the next piece if (!ignore_peers.empty()) { peers.insert(peers.begin(), ignore_peers.begin(), ignore_peers.end()); ignore_peers.clear(); // TODO: instead of resorting the whole list, insert the peers // directly into the right place std::sort(peers.begin(), peers.end() , boost::bind(&peer_connection::download_queue_time, _1, 16*1024) < boost::bind(&peer_connection::download_queue_time, _2, 16*1024)); } // if this peer's download time exceeds 2 seconds, we're done. // We don't want to build unreasonably long request queues if (!peers.empty() && peers[0]->download_queue_time() > milliseconds(2000)) break; } // commit all the time critical requests for (std::set::iterator i = peers_with_requests.begin() , end(peers_with_requests.end()); i != end; ++i) { (*i)->send_block_requests(); } } std::set torrent::web_seeds(web_seed_entry::type_t type) const { TORRENT_ASSERT(is_single_thread()); std::set ret; for (std::list::const_iterator i = m_web_seeds.begin() , end(m_web_seeds.end()); i != end; ++i) { if (i->peer_info.banned) continue; if (i->removed) continue; if (i->type != type) continue; ret.insert(i->url); } return ret; } void torrent::remove_web_seed(std::string const& url, web_seed_entry::type_t type) { std::list::iterator i = std::find_if(m_web_seeds.begin() , m_web_seeds.end() , (boost::bind(&web_seed_t::url, _1) == url && boost::bind(&web_seed_t::type, _1) == type)); if (i != m_web_seeds.end()) { remove_web_seed(i); set_need_save_resume(); } } void torrent::disconnect_web_seed(peer_connection* p) { std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() , (boost::bind(&torrent_peer::connection , boost::bind(&web_seed_t::peer_info, _1)) == p)); // this happens if the web server responded with a redirect // or with something incorrect, so that we removed the web seed // immediately, before we disconnected if (i == m_web_seeds.end()) return; TORRENT_ASSERT(i->resolving == false); TORRENT_ASSERT(i->peer_info.connection); i->peer_info.connection = 0; } void torrent::remove_web_seed(peer_connection* p, error_code const& ec , operation_t op, int error) { std::list::iterator i = std::find_if(m_web_seeds.begin() , m_web_seeds.end() , boost::bind(&torrent_peer::connection , boost::bind(&web_seed_t::peer_info, _1)) == p); TORRENT_ASSERT(i != m_web_seeds.end()); if (i == m_web_seeds.end()) return; peer_connection* peer = static_cast(i->peer_info.connection); if (peer) { // if we have a connection for this web seed, we also need to // disconnect it and clear its reference to the peer_info object // that's part of the web_seed_t we're about to remove TORRENT_ASSERT(peer->m_in_use == 1337); peer->disconnect(ec, op, error); peer->set_peer_info(0); } remove_web_seed(i); } void torrent::retry_web_seed(peer_connection* p, int retry) { TORRENT_ASSERT(is_single_thread()); std::list::iterator i = std::find_if(m_web_seeds.begin() , m_web_seeds.end() , boost::bind(&torrent_peer::connection , boost::bind(&web_seed_t::peer_info, _1)) == p); TORRENT_ASSERT(i != m_web_seeds.end()); if (i == m_web_seeds.end()) return; if (i->removed) return; if (retry == 0) retry = settings().get_int(settings_pack::urlseed_wait_retry); i->retry = aux::time_now() + seconds(retry); } torrent_state torrent::get_peer_list_state() { torrent_state ret; ret.is_paused = is_paused(); ret.is_finished = is_finished(); ret.allow_multiple_connections_per_ip = settings().get_bool(settings_pack::allow_multiple_connections_per_ip); ret.max_peerlist_size = is_paused() ? settings().get_int(settings_pack::max_paused_peerlist_size) : settings().get_int(settings_pack::max_peerlist_size); ret.min_reconnect_time = settings().get_int(settings_pack::min_reconnect_time); ret.ip = &m_ses.external_address(); ret.port = m_ses.listen_port(); ret.max_failcount = settings().get_int(settings_pack::max_failcount); return ret; } bool torrent::try_connect_peer() { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(want_peers()); if (m_should_be_loaded == false) { update_want_peers(); return false; } torrent_state st = get_peer_list_state(); need_peer_list(); torrent_peer* p = m_peer_list->connect_one_peer(m_ses.session_time(), &st); peers_erased(st.erased); inc_stats_counter(counters::connection_attempt_loops, st.loop_counter); if (p == NULL) { update_want_peers(); return false; } if (!connect_to_peer(p)) { m_peer_list->inc_failcount(p); update_want_peers(); return false; } update_want_peers(); return true; } torrent_peer* torrent::add_peer(tcp::endpoint const& adr, int source, int flags) { TORRENT_ASSERT(is_single_thread()); #if !TORRENT_USE_IPV6 if (!adr.address().is_v4()) { #ifndef TORRENT_DISABLE_LOGGING error_code ec; debug_log("add_peer() %s unsupported address family" , adr.address().to_string(ec).c_str()); #endif return NULL; } #endif #ifndef TORRENT_DISABLE_DHT if (source != peer_info::resume_data) { // try to send a DHT ping to this peer // as well, to figure out if it supports // DHT (uTorrent and BitComet don't // advertise support) udp::endpoint node(adr.address(), adr.port()); session().add_dht_node(node); } #endif if (m_apply_ip_filter && m_ip_filter && m_ip_filter->access(adr.address()) & ip_filter::blocked) { if (alerts().should_post()) alerts().emplace_alert(get_handle() , adr.address(), peer_blocked_alert::ip_filter); #ifndef TORRENT_DISABLE_EXTENSIONS notify_extension_add_peer(adr, source, torrent_plugin::filtered); #endif return NULL; } if (m_ses.get_port_filter().access(adr.port()) & port_filter::blocked) { if (alerts().should_post()) alerts().emplace_alert(get_handle() , adr.address(), peer_blocked_alert::port_filter); #ifndef TORRENT_DISABLE_EXTENSIONS notify_extension_add_peer(adr, source, torrent_plugin::filtered); #endif return NULL; } #if TORRENT_USE_I2P // if this is an i2p torrent, and we don't allow mixed mode // no regular peers should ever be added! if (!settings().get_bool(settings_pack::allow_i2p_mixed) && is_i2p()) { if (alerts().should_post()) alerts().emplace_alert(get_handle() , adr.address(), peer_blocked_alert::i2p_mixed); return NULL; } #endif if (settings().get_bool(settings_pack::no_connect_privileged_ports) && adr.port() < 1024) { if (alerts().should_post()) alerts().emplace_alert(get_handle() , adr.address(), peer_blocked_alert::privileged_ports); #ifndef TORRENT_DISABLE_EXTENSIONS notify_extension_add_peer(adr, source, torrent_plugin::filtered); #endif return NULL; } need_peer_list(); torrent_state st = get_peer_list_state(); torrent_peer* p = m_peer_list->add_peer(adr, source, flags, &st); peers_erased(st.erased); #ifndef TORRENT_DISABLE_LOGGING error_code ec; debug_log("add_peer() %s connect-candidates: %d" , adr.address().to_string(ec).c_str(), m_peer_list->num_connect_candidates()); #endif if (p) { state_updated(); #ifndef TORRENT_DISABLE_EXTENSIONS notify_extension_add_peer(adr, source, st.first_time_seen ? torrent_plugin::first_time : 0); #endif } else { #ifndef TORRENT_DISABLE_EXTENSIONS notify_extension_add_peer(adr, source, torrent_plugin::filtered); #endif } update_want_peers(); state_updated(); return p; } bool torrent::ban_peer(torrent_peer* tp) { if (!settings().get_bool(settings_pack::ban_web_seeds) && tp->web_seed) return false; need_peer_list(); if (!m_peer_list->ban_peer(tp)) return false; update_want_peers(); inc_stats_counter(counters::num_banned_peers); return true; } void torrent::set_seed(torrent_peer* p, bool s) { if (p->seed != s) { if (s) { TORRENT_ASSERT(m_num_seeds < 0xffff); ++m_num_seeds; } else { TORRENT_ASSERT(m_num_seeds > 0); --m_num_seeds; } } need_peer_list(); m_peer_list->set_seed(p, s); update_auto_sequential(); } void torrent::clear_failcount(torrent_peer* p) { need_peer_list(); m_peer_list->set_failcount(p, 0); update_want_peers(); } std::pair torrent::find_peers(address const& a) { need_peer_list(); return m_peer_list->find_peers(a); } void torrent::update_peer_port(int port, torrent_peer* p, int src) { need_peer_list(); torrent_state st = get_peer_list_state(); m_peer_list->update_peer_port(port, p, src, &st); peers_erased(st.erased); update_want_peers(); } // verify piece is used when checking resume data or when the user // adds a piece void torrent::verify_piece(int piece) { // picker().mark_as_checking(piece); TORRENT_ASSERT(m_storage.get()); inc_refcount("verify_piece"); m_ses.disk_thread().async_hash(m_storage.get(), piece, 0 , boost::bind(&torrent::on_piece_verified, shared_from_this(), _1) , reinterpret_cast(1)); } announce_entry* torrent::find_tracker(tracker_request const& r) { std::vector::iterator i = std::find_if( m_trackers.begin(), m_trackers.end() , boost::bind(&announce_entry::url, _1) == r.url); if (i == m_trackers.end()) return 0; return &*i; } void torrent::ip_filter_updated() { if (!m_apply_ip_filter) return; if (!m_peer_list) return; if (!m_ip_filter) return; torrent_state st = get_peer_list_state(); std::vector
banned; m_peer_list->apply_ip_filter(*m_ip_filter, &st, banned); if (alerts().should_post()) { for (std::vector
::iterator i = banned.begin() , end(banned.end()); i != end; ++i) alerts().emplace_alert(get_handle(), *i , peer_blocked_alert::ip_filter); } peers_erased(st.erased); } void torrent::port_filter_updated() { if (!m_apply_ip_filter) return; if (!m_peer_list) return; torrent_state st = get_peer_list_state(); std::vector
banned; m_peer_list->apply_port_filter(m_ses.get_port_filter(), &st, banned); if (alerts().should_post()) { for (std::vector
::iterator i = banned.begin() , end(banned.end()); i != end; ++i) alerts().emplace_alert(get_handle(), *i , peer_blocked_alert::port_filter); } peers_erased(st.erased); } // this is called when torrent_peers are removed from the peer_list // (peer-list). It removes any references we may have to those torrent_peers, // so we don't leave then dangling void torrent::peers_erased(std::vector const& peers) { if (!has_picker()) return; for (std::vector::const_iterator i = peers.begin() , end(peers.end()); i != end; ++i) { m_picker->clear_peer(*i); } #if TORRENT_USE_INVARIANT_CHECKS m_picker->check_peers(); #endif } #if !TORRENT_NO_FPU void torrent::file_progress(std::vector& fp) { TORRENT_ASSERT(is_single_thread()); if (!valid_metadata()) { fp.clear(); return; } if (!need_loaded()) return; fp.resize(m_torrent_file->num_files(), 1.f); if (is_seed()) return; std::vector progress; file_progress(progress); for (int i = 0; i < m_torrent_file->num_files(); ++i) { boost::int64_t file_size = m_torrent_file->files().file_size(i); if (file_size == 0) fp[i] = 1.f; else fp[i] = float(progress[i]) / file_size; } } #endif void torrent::file_progress(std::vector& fp, int flags) { TORRENT_ASSERT(is_single_thread()); if (!valid_metadata()) { fp.clear(); return; } if (!need_loaded()) return; // if we're a seed, we don't have an m_file_progress anyway // since we don't need one. We know we have all files // just fill in the full file sizes as a shortcut if (is_seed()) { fp.resize(m_torrent_file->num_files()); file_storage const& fs = m_torrent_file->files(); for (int i = 0; i < fs.num_files(); ++i) fp[i] = fs.file_size(i); return; } if (num_have() == 0) { // if we don't have any pieces, just return zeroes fp.clear(); fp.resize(m_torrent_file->num_files(), 0); return; } m_file_progress.export_progress(fp); if (flags & torrent_handle::piece_granularity) return; TORRENT_ASSERT(has_picker()); std::vector q = m_picker->get_download_queue(); if (!q.empty()) { if (!const_cast(*this).need_loaded()) return; } file_storage const& fs = m_torrent_file->files(); for (std::vector::const_iterator i = q.begin(), end(q.end()); i != end; ++i) { boost::int64_t offset = boost::int64_t(i->index) * m_torrent_file->piece_length(); int file = fs.file_index_at_offset(offset); int num_blocks = m_picker->blocks_in_piece(i->index); piece_picker::block_info const* info = m_picker->blocks_for_piece(*i); for (int k = 0; k < num_blocks; ++k) { TORRENT_ASSERT(file < fs.num_files()); TORRENT_ASSERT(offset == boost::int64_t(i->index) * m_torrent_file->piece_length() + k * block_size()); TORRENT_ASSERT(offset < m_torrent_file->total_size()); while (offset >= fs.file_offset(file) + fs.file_size(file)) { ++file; } TORRENT_ASSERT(file < fs.num_files()); boost::int64_t block = block_size(); if (info[k].state == piece_picker::block_info::state_none) { offset += block; continue; } if (info[k].state == piece_picker::block_info::state_requested) { block = 0; torrent_peer* p = static_cast(info[k].peer); if (p && p->connection) { peer_connection* peer = static_cast(p->connection); boost::optional pbp = peer->downloading_piece_progress(); if (pbp && pbp->piece_index == i->index && pbp->block_index == k) block = pbp->bytes_downloaded; TORRENT_ASSERT(block <= block_size()); } if (block == 0) { offset += block_size(); continue; } } if (offset + block > fs.file_offset(file) + fs.file_size(file)) { int left_over = int(block_size() - block); // split the block on multiple files while (block > 0) { TORRENT_ASSERT(offset <= fs.file_offset(file) + fs.file_size(file)); boost::int64_t slice = (std::min)(fs.file_offset(file) + fs.file_size(file) - offset , block); fp[file] += slice; offset += slice; block -= slice; TORRENT_ASSERT(offset <= fs.file_offset(file) + fs.file_size(file)); if (offset == fs.file_offset(file) + fs.file_size(file)) { ++file; if (file == fs.num_files()) { offset += block; break; } } } offset += left_over; TORRENT_ASSERT(offset == boost::int64_t(i->index) * m_torrent_file->piece_length() + (k+1) * block_size()); } else { fp[file] += block; offset += block_size(); } TORRENT_ASSERT(file <= m_torrent_file->num_files()); } } } void torrent::new_external_ip() { if (m_peer_list) m_peer_list->clear_peer_prio(); } void torrent::stop_when_ready(bool b) { m_stop_when_ready = b; // to avoid race condition, if we're already in a downloading state, // trigger the stop-when-ready logic immediately. if (m_stop_when_ready && is_downloading_state(m_state)) { #ifndef TORRENT_DISABLE_LOGGING debug_log("stop_when_ready triggered"); #endif auto_managed(false); pause(); m_stop_when_ready = false; } } void torrent::set_state(torrent_status::state_t s) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(s != 0); // this state isn't used anymore #if TORRENT_USE_ASSERTS if (s == torrent_status::seeding) { TORRENT_ASSERT(is_seed()); TORRENT_ASSERT(is_finished()); } if (s == torrent_status::finished) TORRENT_ASSERT(is_finished()); if (s == torrent_status::downloading && m_state == torrent_status::finished) TORRENT_ASSERT(!is_finished()); #endif if (int(m_state) == s) return; if (m_ses.alerts().should_post()) { m_ses.alerts().emplace_alert(get_handle() , s, static_cast(m_state)); } if (s == torrent_status::finished && alerts().should_post()) { alerts().emplace_alert( get_handle()); } if (m_stop_when_ready && !is_downloading_state(m_state) && is_downloading_state(s)) { #ifndef TORRENT_DISABLE_LOGGING debug_log("stop_when_ready triggered"); #endif // stop_when_ready is set, and we're transitioning from a downloading // state to a non-downloading state. pause the torrent. Note that // "downloading" is defined broadly to include any state where we // either upload or download (for the purpose of this flag). auto_managed(false); pause(); m_stop_when_ready = false; } m_state = s; #ifndef TORRENT_DISABLE_LOGGING debug_log("set_state() %d", m_state); #endif update_want_peers(); update_state_list(); update_gauge(); state_updated(); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->on_state(m_state); } TORRENT_CATCH (std::exception&) {} } #endif } #ifndef TORRENT_DISABLE_EXTENSIONS void torrent::notify_extension_add_peer(tcp::endpoint const& ip , int src, int flags) { for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { TORRENT_TRY { (*i)->on_add_peer(ip, src, flags); } TORRENT_CATCH (std::exception&) {} } } #endif void torrent::state_updated() { // if this fails, this function is probably called // from within the torrent constructor, which it // shouldn't be. Whichever function ends up calling // this should probably be moved to torrent::start() TORRENT_ASSERT(shared_from_this()); // we can't call state_updated() while the session // is building the status update alert TORRENT_ASSERT(!m_ses.is_posting_torrent_updates()); // we're not subscribing to this torrent, don't add it if (!m_state_subscription) return; std::vector& list = m_ses.torrent_list(aux::session_interface::torrent_state_updates); // if it has already been updated this round, no need to // add it to the list twice if (m_links[aux::session_interface::torrent_state_updates].in_list()) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_ASSERT(find(list.begin(), list.end(), this) != list.end()); #endif return; } #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_ASSERT(find(list.begin(), list.end(), this) == list.end()); #endif m_links[aux::session_interface::torrent_state_updates].insert(list, this); } void torrent::status(torrent_status* st, boost::uint32_t flags) { INVARIANT_CHECK; time_point now = aux::time_now(); st->handle = get_handle(); st->info_hash = info_hash(); st->is_loaded = is_loaded(); if (flags & torrent_handle::query_name) st->name = name(); if (flags & torrent_handle::query_save_path) st->save_path = save_path(); if (flags & torrent_handle::query_torrent_file) st->torrent_file = m_torrent_file; st->has_incoming = m_has_incoming; st->errc = m_error; st->error_file = m_error_file; #ifndef TORRENT_NO_DEPRECATE if (m_error) st->error = convert_from_native(m_error.message()) + ": " + resolve_filename(m_error_file); #endif st->seed_mode = m_seed_mode; st->moving_storage = m_moving_storage; st->announcing_to_trackers = m_announce_to_trackers; st->announcing_to_lsd = m_announce_to_lsd; st->announcing_to_dht = m_announce_to_dht; st->stop_when_ready = m_stop_when_ready; st->added_time = m_added_time; st->completed_time = m_completed_time; st->last_scrape = m_last_scrape == unset ? -1 : total_seconds(clock_type::now() - time_point(seconds(m_last_scrape))); st->share_mode = m_share_mode; st->upload_mode = m_upload_mode; st->up_bandwidth_queue = 0; st->down_bandwidth_queue = 0; st->priority = priority(); st->num_peers = int(m_connections.size()) - m_num_connecting; st->list_peers = m_peer_list ? m_peer_list->num_peers() : 0; st->list_seeds = m_peer_list ? m_peer_list->num_seeds() : 0; st->connect_candidates = m_peer_list ? m_peer_list->num_connect_candidates() : 0; st->seed_rank = seed_rank(settings()); st->all_time_upload = m_total_uploaded; st->all_time_download = m_total_downloaded; // activity time st->finished_time = finished_time(); st->active_time = active_time(); st->seeding_time = seeding_time(); st->time_since_upload = m_last_upload == unset ? -1 : total_seconds(clock_type::now() - time_point(seconds(m_last_upload))); st->time_since_download = m_last_download == unset ? -1 : total_seconds(clock_type::now() - time_point(seconds(m_last_download))); st->storage_mode = static_cast(m_storage_mode); st->num_complete = (m_complete == 0xffffff) ? -1 : m_complete; st->num_incomplete = (m_incomplete == 0xffffff) ? -1 : m_incomplete; st->paused = is_torrent_paused(); st->auto_managed = m_auto_managed; st->sequential_download = m_sequential_download; st->is_seeding = is_seed(); st->is_finished = is_finished(); st->super_seeding = m_super_seeding; st->has_metadata = valid_metadata(); bytes_done(*st, (flags & torrent_handle::query_accurate_download_counters) != 0); TORRENT_ASSERT(st->total_wanted_done >= 0); TORRENT_ASSERT(st->total_done >= st->total_wanted_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(); if (is_paused() || m_tracker_timer.expires_at() < now) st->next_announce = seconds(0); else st->next_announce = m_tracker_timer.expires_at() - now; if (st->next_announce.count() < 0) st->next_announce = seconds(0); #ifndef TORRENT_NO_DEPRECATE st->announce_interval = seconds(0); #endif st->current_tracker.clear(); if (m_last_working_tracker >= 0) { TORRENT_ASSERT(m_last_working_tracker < int(m_trackers.size())); const int i = m_last_working_tracker; st->current_tracker = m_trackers[i].url; } else { std::vector::const_iterator i; for (i = m_trackers.begin(); i != m_trackers.end(); ++i) { if (i->updating) continue; if (!i->verified) continue; st->current_tracker = i->url; break; } } if ((flags & torrent_handle::query_verified_pieces)) { st->verified_pieces = m_verified; } st->num_uploads = m_num_uploads; st->uploads_limit = m_max_uploads == (1<<24)-1 ? -1 : m_max_uploads; st->num_connections = int(m_connections.size()); st->connections_limit = m_max_connections == (1<<24)-1 ? -1 : m_max_connections; // if we don't have any metadata, stop here st->queue_position = queue_position(); st->need_save_resume = need_save_resume_data(); st->ip_filter_applies = m_apply_ip_filter; st->state = static_cast(m_state); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS if (st->state == torrent_status::finished || st->state == torrent_status::seeding) { TORRENT_ASSERT(st->is_finished); } #endif if (!valid_metadata()) { st->state = torrent_status::downloading_metadata; st->progress_ppm = m_progress_ppm; #if !TORRENT_NO_FPU st->progress = m_progress_ppm / 1000000.f; #endif st->block_size = 0; return; } st->block_size = block_size(); if (m_state == torrent_status::checking_files) { st->progress_ppm = m_progress_ppm; #if !TORRENT_NO_FPU st->progress = m_progress_ppm / 1000000.f; #endif } else if (st->total_wanted == 0) { st->progress_ppm = 1000000; st->progress = 1.f; } else { st->progress_ppm = st->total_wanted_done * 1000000 / st->total_wanted; #if !TORRENT_NO_FPU st->progress = st->progress_ppm / 1000000.f; #endif } int num_pieces = m_torrent_file->num_pieces(); if (has_picker() && (flags & torrent_handle::query_pieces)) { st->pieces.resize(num_pieces, false); for (int i = 0; i < num_pieces; ++i) if (m_picker->has_piece_passed(i)) st->pieces.set_bit(i); } else if (m_have_all) { st->pieces.resize(num_pieces, true); } else { st->pieces.resize(num_pieces, false); } st->num_pieces = num_have(); st->num_seeds = num_seeds(); if ((flags & torrent_handle::query_distributed_copies) && m_picker.get()) { boost::tie(st->distributed_full_copies, st->distributed_fraction) = m_picker->distributed_copies(); #if TORRENT_NO_FPU st->distributed_copies = -1.f; #else st->distributed_copies = st->distributed_full_copies + float(st->distributed_fraction) / 1000; #endif } else { st->distributed_full_copies = -1; st->distributed_fraction = -1; st->distributed_copies = -1.f; } st->last_seen_complete = m_swarm_last_seen_complete; } int torrent::priority() const { int priority = 0; for (int i = 0; i < num_classes(); ++i) { int const* prio = m_ses.peer_classes().at(class_at(i))->priority; priority = std::max(priority, prio[peer_connection::upload_channel]); priority = std::max(priority, prio[peer_connection::download_channel]); } return priority; } void torrent::set_priority(int const prio) { // priority 1 is default if (prio == 1 && m_peer_class == 0) return; if (m_peer_class == 0) setup_peer_class(); struct peer_class* tpc = m_ses.peer_classes().at(m_peer_class); TORRENT_ASSERT(tpc); tpc->priority[peer_connection::download_channel] = prio; tpc->priority[peer_connection::upload_channel] = prio; state_updated(); } void torrent::add_redundant_bytes(int b, torrent::wasted_reason_t reason) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(b > 0); m_total_redundant_bytes += b; TORRENT_ASSERT(b > 0); TORRENT_ASSERT(reason >= 0); TORRENT_ASSERT(reason < waste_reason_max); m_stats_counters.inc_stats_counter(counters::recv_redundant_bytes, b); m_stats_counters.inc_stats_counter(counters::waste_piece_timed_out + reason, b); } void torrent::add_failed_bytes(int b) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(b > 0); m_total_failed_bytes += b; m_stats_counters.inc_stats_counter(counters::recv_failed_bytes, b); } // the number of connected peers that are seeds int torrent::num_seeds() const { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; return int(m_num_seeds) - int(m_num_connecting_seeds); } // the number of connected peers that are not seeds int torrent::num_downloaders() const { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; int const ret = int(m_connections.size()) - m_num_seeds - m_num_connecting + m_num_connecting_seeds; TORRENT_ASSERT(ret >= 0); return ret; } void torrent::tracker_request_error(tracker_request const& r , int response_code, error_code const& ec, std::string const& msg , int retry_interval) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** tracker error: (%d) %s %s", ec.value() , ec.message().c_str(), msg.c_str()); #endif if (0 == (r.kind & tracker_request::scrape_request)) { // announce request announce_entry* ae = find_tracker(r); if (ae) { ae->failed(settings(), retry_interval); ae->last_error = ec; ae->message = msg; int tracker_index = ae - &m_trackers[0]; #ifndef TORRENT_DISABLE_LOGGING debug_log("*** increment tracker fail count [%d]", ae->fails); #endif // never talk to this tracker again if (response_code == 410) ae->fail_limit = 1; deprioritize_tracker(tracker_index); } if (m_ses.alerts().should_post() || r.triggered_manually) { m_ses.alerts().emplace_alert(get_handle() , ae?ae->fails:0, response_code, r.url, ec, msg); } } else { // scrape request if (response_code == 410) { // never talk to this tracker again announce_entry* ae = find_tracker(r); if (ae) ae->fail_limit = 1; } // if this was triggered manually we need to post this unconditionally, // since the client expects a response from its action, regardless of // whether all tracker events have been enabled by the alert mask if (m_ses.alerts().should_post() || r.triggered_manually) { m_ses.alerts().emplace_alert(get_handle(), r.url, ec); } } // announce to the next working tracker if ((!m_abort && !is_paused()) || r.event == tracker_request::stopped) announce_with_tracker(r.event); update_tracker_timer(aux::time_now()); } #ifndef TORRENT_DISABLE_LOGGING TORRENT_FORMAT(2,3) void torrent::debug_log(char const* fmt, ...) const { if (!alerts().should_post()) return; va_list v; va_start(v, fmt); char buf[400]; vsnprintf(buf, sizeof(buf), fmt, v); va_end(v); alerts().emplace_alert( const_cast(this)->get_handle(), buf); } #endif } libtorrent-rasterbar-1.1.13/src/torrent_handle.cpp000066400000000000000000000525451351156116000222610ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #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/aux_/session_call.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/utf8.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/announce_entry.hpp" #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-macros" #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-macros" #endif #define TORRENT_ASYNC_CALL(x) \ boost::shared_ptr t = m_torrent.lock(); \ if (!t) return; \ session_impl& ses = static_cast(t->session()); \ ses.get_io_service().dispatch(boost::bind(&torrent:: x, t)) #define TORRENT_ASYNC_CALL1(x, a1) \ boost::shared_ptr t = m_torrent.lock(); \ if (!t) return; \ session_impl& ses = static_cast(t->session()); \ ses.get_io_service().dispatch(boost::bind(&torrent:: x, t, a1)) #define TORRENT_ASYNC_CALL2(x, a1, a2) \ boost::shared_ptr t = m_torrent.lock(); \ if (!t) return; \ session_impl& ses = static_cast(t->session()); \ ses.get_io_service().dispatch(boost::bind(&torrent:: x, t, a1, a2)) #define TORRENT_ASYNC_CALL3(x, a1, a2, a3) \ boost::shared_ptr t = m_torrent.lock(); \ if (!t) return; \ session_impl& ses = static_cast(t->session()); \ ses.get_io_service().dispatch(boost::bind(&torrent:: x, t, a1, a2, a3)) #define TORRENT_ASYNC_CALL4(x, a1, a2, a3, a4) \ boost::shared_ptr t = m_torrent.lock(); \ if (!t) return; \ session_impl& ses = static_cast(t->session()); \ ses.get_io_service().dispatch(boost::bind(&torrent:: x, t, a1, a2, a3, a4)) #define TORRENT_SYNC_CALL(x) \ boost::shared_ptr t = m_torrent.lock(); \ if (t) aux::sync_call_handle(t, boost::bind(&torrent:: x, t)); #define TORRENT_SYNC_CALL1(x, a1) \ boost::shared_ptr t = m_torrent.lock(); \ if (t) aux::sync_call_handle(t, boost::bind(&torrent:: x, t, a1)); #define TORRENT_SYNC_CALL2(x, a1, a2) \ boost::shared_ptr t = m_torrent.lock(); \ if (t) aux::sync_call_handle(t, boost::bind(&torrent:: x, t, a1, a2)); #define TORRENT_SYNC_CALL3(x, a1, a2, a3) \ boost::shared_ptr t = m_torrent.lock(); \ if (t) aux::sync_call_handle(t, boost::bind(&torrent:: x, t, a1, a2, a3)); #define TORRENT_SYNC_CALL_RET(type, def, x) \ boost::shared_ptr t = m_torrent.lock(); \ type r = def; \ if (t) aux::sync_call_ret_handle(t, r, boost::function(boost::bind(&torrent:: x, t))); #define TORRENT_SYNC_CALL_RET1(type, def, x, a1) \ boost::shared_ptr t = m_torrent.lock(); \ type r = def; \ if (t) aux::sync_call_ret_handle(t, r, boost::function(boost::bind(&torrent:: x, t, a1))); #define TORRENT_SYNC_CALL_RET2(type, def, x, a1, a2) \ boost::shared_ptr t = m_torrent.lock(); \ type r = def; \ if (t) aux::sync_call_ret_handle(t, r, boost::function(boost::bind(&torrent:: x, t, a1, a2))); #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef __GNUC__ #pragma GCC diagnostic pop #endif using libtorrent::aux::session_impl; namespace libtorrent { #ifndef BOOST_NO_EXCEPTIONS void throw_invalid_handle() { throw libtorrent_exception(errors::invalid_torrent_handle); } #endif sha1_hash torrent_handle::info_hash() const { boost::shared_ptr t = m_torrent.lock(); static const sha1_hash empty; if (!t) return empty; return t->info_hash(); } int torrent_handle::max_uploads() const { TORRENT_SYNC_CALL_RET(int, 0, max_uploads); return r; } void torrent_handle::set_max_uploads(int max_uploads) const { TORRENT_ASSERT_PRECOND(max_uploads >= 2 || max_uploads == -1); TORRENT_ASYNC_CALL2(set_max_uploads, max_uploads, true); } int torrent_handle::max_connections() const { TORRENT_SYNC_CALL_RET(int, 0, max_connections); return r; } void torrent_handle::set_max_connections(int max_connections) const { TORRENT_ASSERT_PRECOND(max_connections >= 2 || max_connections == -1); TORRENT_ASYNC_CALL2(set_max_connections, max_connections, true); } void torrent_handle::set_upload_limit(int limit) const { TORRENT_ASSERT_PRECOND(limit >= -1); TORRENT_ASYNC_CALL1(set_upload_limit, limit); } int torrent_handle::upload_limit() const { TORRENT_SYNC_CALL_RET(int, 0, upload_limit); return r; } void torrent_handle::set_download_limit(int limit) const { TORRENT_ASSERT_PRECOND(limit >= -1); TORRENT_ASYNC_CALL1(set_download_limit, limit); } int torrent_handle::download_limit() const { TORRENT_SYNC_CALL_RET(int, 0, download_limit); return r; } void torrent_handle::move_storage( std::string const& save_path, int flags) const { TORRENT_ASYNC_CALL2(move_storage, save_path, flags); } #if TORRENT_USE_WSTRING #ifndef TORRENT_NO_DEPRECATE void torrent_handle::move_storage( std::wstring const& save_path, int flags) const { std::string utf8; wchar_utf8(save_path, utf8); TORRENT_ASYNC_CALL2(move_storage, utf8, flags); } void torrent_handle::rename_file(int index, std::wstring const& new_name) const { std::string utf8; wchar_utf8(new_name, utf8); TORRENT_ASYNC_CALL2(rename_file, index, utf8); } #endif // TORRENT_NO_DEPRECATE #endif // TORRENT_USE_WSTRING void torrent_handle::rename_file(int index, std::string const& new_name) const { TORRENT_ASYNC_CALL2(rename_file, index, new_name); } void torrent_handle::add_extension( boost::function(torrent_handle const&, void*)> const& ext , void* userdata) { #ifndef TORRENT_DISABLE_EXTENSIONS TORRENT_ASYNC_CALL2(add_extension, ext, userdata); #else TORRENT_UNUSED(ext); TORRENT_UNUSED(userdata); #endif } bool torrent_handle::set_metadata(char const* metadata, int size) const { TORRENT_SYNC_CALL_RET2(bool, false, set_metadata, metadata, size); return r; } void torrent_handle::pause(int flags) const { TORRENT_ASYNC_CALL1(pause, bool(flags & graceful_pause)); } void torrent_handle::stop_when_ready(bool b) const { TORRENT_ASYNC_CALL1(stop_when_ready, b); } void torrent_handle::apply_ip_filter(bool b) const { TORRENT_ASYNC_CALL1(set_apply_ip_filter, b); } void torrent_handle::set_share_mode(bool b) const { TORRENT_ASYNC_CALL1(set_share_mode, b); } void torrent_handle::set_upload_mode(bool b) const { TORRENT_ASYNC_CALL1(set_upload_mode, b); } void torrent_handle::flush_cache() const { TORRENT_ASYNC_CALL(flush_cache); } void torrent_handle::set_ssl_certificate( std::string const& certificate , std::string const& private_key , std::string const& dh_params , std::string const& passphrase) { #ifdef TORRENT_USE_OPENSSL TORRENT_ASYNC_CALL4(set_ssl_cert, certificate, private_key, dh_params, passphrase); #else TORRENT_UNUSED(certificate); TORRENT_UNUSED(private_key); TORRENT_UNUSED(dh_params); TORRENT_UNUSED(passphrase); #endif } void torrent_handle::set_ssl_certificate_buffer( std::string const& certificate , std::string const& private_key , std::string const& dh_params) { #ifdef TORRENT_USE_OPENSSL TORRENT_ASYNC_CALL3(set_ssl_cert_buffer, certificate, private_key, dh_params); #else TORRENT_UNUSED(certificate); TORRENT_UNUSED(private_key); TORRENT_UNUSED(dh_params); #endif } void torrent_handle::save_resume_data(int f) const { TORRENT_ASYNC_CALL1(save_resume_data, f); } bool torrent_handle::need_save_resume_data() const { TORRENT_SYNC_CALL_RET(bool, false, need_save_resume_data); return r; } void torrent_handle::force_recheck() const { TORRENT_ASYNC_CALL(force_recheck); } void torrent_handle::resume() const { TORRENT_ASYNC_CALL(resume); } void torrent_handle::auto_managed(bool m) const { TORRENT_ASYNC_CALL1(auto_managed, m); } void torrent_handle::set_priority(int p) const { TORRENT_ASYNC_CALL1(set_priority, p); } int torrent_handle::queue_position() const { TORRENT_SYNC_CALL_RET(int, -1, queue_position); return r; } void torrent_handle::queue_position_up() const { TORRENT_ASYNC_CALL(queue_up); } void torrent_handle::queue_position_down() const { TORRENT_ASYNC_CALL(queue_down); } void torrent_handle::queue_position_set(int p) const { TORRENT_ASSERT_PRECOND(p >= 0); if (p < 0) return; TORRENT_ASYNC_CALL1(set_queue_position, p); } void torrent_handle::queue_position_top() const { TORRENT_ASYNC_CALL1(set_queue_position, 0); } void torrent_handle::queue_position_bottom() const { TORRENT_ASYNC_CALL1(set_queue_position, INT_MAX); } void torrent_handle::clear_error() const { TORRENT_ASYNC_CALL(clear_error); } #ifndef TORRENT_NO_DEPRECATE void torrent_handle::set_tracker_login(std::string const& name , std::string const& password) const { TORRENT_ASYNC_CALL2(set_tracker_login, name, password); } #endif void torrent_handle::file_progress(std::vector& progress, int flags) const { TORRENT_SYNC_CALL2(file_progress, boost::ref(progress), flags); } torrent_status torrent_handle::status(boost::uint32_t flags) const { torrent_status st; TORRENT_SYNC_CALL2(status, &st, flags); return st; } void torrent_handle::set_pinned(bool p) const { TORRENT_ASYNC_CALL1(set_pinned, p); } void torrent_handle::set_sequential_download(bool sd) const { TORRENT_ASYNC_CALL1(set_sequential_download, sd); } void torrent_handle::piece_availability(std::vector& avail) const { TORRENT_SYNC_CALL1(piece_availability, boost::ref(avail)); } void torrent_handle::piece_priority(int index, int priority) const { TORRENT_ASYNC_CALL2(set_piece_priority, index, priority); } int torrent_handle::piece_priority(int index) const { TORRENT_SYNC_CALL_RET1(int, 0, piece_priority, index); return r; } void torrent_handle::prioritize_pieces(std::vector const& pieces) const { TORRENT_ASYNC_CALL1(prioritize_pieces, pieces); } void torrent_handle::prioritize_pieces(std::vector > const& pieces) const { TORRENT_ASYNC_CALL1(prioritize_piece_list, pieces); } std::vector torrent_handle::piece_priorities() const { std::vector ret; TORRENT_SYNC_CALL1(piece_priorities, &ret); return ret; } void torrent_handle::file_priority(int index, int priority) const { TORRENT_ASYNC_CALL2(set_file_priority, index, priority); } int torrent_handle::file_priority(int index) const { TORRENT_SYNC_CALL_RET1(int, 0, file_priority, index); return r; } void torrent_handle::prioritize_files(std::vector const& files) const { TORRENT_ASYNC_CALL1(prioritize_files, files); } std::vector torrent_handle::file_priorities() const { std::vector ret; TORRENT_SYNC_CALL1(file_priorities, &ret); return ret; } #ifndef TORRENT_NO_DEPRECATE // ============ start deprecation =============== int torrent_handle::get_peer_upload_limit(tcp::endpoint) const { return -1; } int torrent_handle::get_peer_download_limit(tcp::endpoint) const { return -1; } void torrent_handle::set_peer_upload_limit(tcp::endpoint, int /* limit */) const {} void torrent_handle::set_peer_download_limit(tcp::endpoint, int /* limit */) const {} void torrent_handle::set_ratio(float) const {} void torrent_handle::use_interface(const char* net_interface) const { TORRENT_ASYNC_CALL1(use_interface, std::string(net_interface)); } #if !TORRENT_NO_FPU void torrent_handle::file_progress(std::vector& progress) const { TORRENT_SYNC_CALL1(file_progress, boost::ref(progress)); } #endif bool torrent_handle::is_seed() const { TORRENT_SYNC_CALL_RET(bool, false, is_seed); return r; } bool torrent_handle::is_finished() const { TORRENT_SYNC_CALL_RET(bool, false, is_finished); return r; } bool torrent_handle::is_paused() const { TORRENT_SYNC_CALL_RET(bool, false, is_torrent_paused); return r; } bool torrent_handle::is_sequential_download() const { TORRENT_SYNC_CALL_RET(bool, false, is_sequential_download); return r; } bool torrent_handle::is_auto_managed() const { TORRENT_SYNC_CALL_RET(bool, false, is_auto_managed); return r; } bool torrent_handle::has_metadata() const { TORRENT_SYNC_CALL_RET(bool, false, valid_metadata); return r; } void torrent_handle::filter_piece(int index, bool filter) const { TORRENT_ASYNC_CALL2(filter_piece, index, filter); } void torrent_handle::filter_pieces(std::vector const& pieces) const { TORRENT_ASYNC_CALL1(filter_pieces, pieces); } bool torrent_handle::is_piece_filtered(int index) const { TORRENT_SYNC_CALL_RET1(bool, false, is_piece_filtered, index); return r; } std::vector torrent_handle::filtered_pieces() const { std::vector ret; TORRENT_SYNC_CALL1(filtered_pieces, ret); return ret; } void torrent_handle::filter_files(std::vector const& files) const { TORRENT_ASYNC_CALL1(filter_files, files); } bool torrent_handle::super_seeding() const { TORRENT_SYNC_CALL_RET(bool, false, super_seeding); return r; } // ============ end deprecation =============== #endif std::vector torrent_handle::trackers() const { static const std::vector empty; TORRENT_SYNC_CALL_RET(std::vector, empty, trackers); return r; } void torrent_handle::add_url_seed(std::string const& url) const { TORRENT_ASYNC_CALL2(add_web_seed, url, web_seed_entry::url_seed); } void torrent_handle::remove_url_seed(std::string const& url) const { TORRENT_ASYNC_CALL2(remove_web_seed, url, web_seed_entry::url_seed); } std::set torrent_handle::url_seeds() const { static const std::set empty; TORRENT_SYNC_CALL_RET1(std::set, empty, web_seeds, web_seed_entry::url_seed); return r; } void torrent_handle::add_http_seed(std::string const& url) const { TORRENT_ASYNC_CALL2(add_web_seed, url, web_seed_entry::http_seed); } void torrent_handle::remove_http_seed(std::string const& url) const { TORRENT_ASYNC_CALL2(remove_web_seed, url, web_seed_entry::http_seed); } std::set torrent_handle::http_seeds() const { static const std::set empty; TORRENT_SYNC_CALL_RET1(std::set, empty, web_seeds, web_seed_entry::http_seed); return r; } void torrent_handle::replace_trackers( std::vector const& urls) const { TORRENT_ASYNC_CALL1(replace_trackers, urls); } void torrent_handle::add_tracker(announce_entry const& url) const { TORRENT_ASYNC_CALL1(add_tracker, url); } void torrent_handle::add_piece(int piece, char const* data, int flags) const { TORRENT_SYNC_CALL3(add_piece, piece, data, flags); } void torrent_handle::read_piece(int piece) const { TORRENT_ASYNC_CALL1(read_piece, piece); } bool torrent_handle::have_piece(int piece) const { TORRENT_SYNC_CALL_RET1(bool, false, have_piece, piece); return r; } storage_interface* torrent_handle::get_storage_impl() const { TORRENT_SYNC_CALL_RET(storage_interface*, 0, get_storage); return r; } bool torrent_handle::is_valid() const { return !m_torrent.expired(); } boost::shared_ptr torrent_handle::torrent_file() const { TORRENT_SYNC_CALL_RET(boost::shared_ptr , boost::shared_ptr(), get_torrent_copy); return r; } #ifndef TORRENT_NO_DEPRECATE // this function should either be removed, or return // reference counted handle to the torrent_info which // forces the torrent to stay loaded while the client holds it torrent_info const& torrent_handle::get_torrent_info() const { static boost::shared_ptr holder[4]; static int cursor = 0; static mutex holder_mutex; boost::shared_ptr r = torrent_file(); mutex::scoped_lock l(holder_mutex); holder[cursor++] = r; cursor = cursor % (sizeof(holder) / sizeof(holder[0])); return *r; } entry torrent_handle::write_resume_data() const { entry ret(entry::dictionary_t); TORRENT_SYNC_CALL1(write_resume_data, boost::ref(ret)); t = m_torrent.lock(); if (t) { bool done = false; session_impl& ses = static_cast(t->session()); storage_error ec; ses.get_io_service().dispatch(boost::bind(&aux::fun_wrap, boost::ref(done), boost::ref(ses.cond) , boost::ref(ses.mut), boost::function(boost::bind( &piece_manager::write_resume_data, &t->storage(), boost::ref(ret), boost::ref(ec))))); t.reset(); aux::torrent_wait(done, ses); } return ret; } std::string torrent_handle::save_path() const { TORRENT_SYNC_CALL_RET(std::string, "", save_path); return r; } std::string torrent_handle::name() const { TORRENT_SYNC_CALL_RET(std::string, "", name); return r; } #endif void torrent_handle::connect_peer(tcp::endpoint const& adr, int source, int flags) const { TORRENT_ASYNC_CALL3(add_peer, adr, source, flags); } #ifndef TORRENT_NO_DEPRECATE void torrent_handle::force_reannounce( boost::posix_time::time_duration duration) const { TORRENT_ASYNC_CALL3(force_tracker_request, aux::time_now() + seconds(duration.total_seconds()), -1, 0); } #endif void torrent_handle::force_dht_announce() const { #ifndef TORRENT_DISABLE_DHT TORRENT_ASYNC_CALL(dht_announce); #endif } void torrent_handle::force_reannounce(int s, int idx) const { TORRENT_ASYNC_CALL3(force_tracker_request, aux::time_now() + seconds(s), idx, 0); } void torrent_handle::force_reannounce(int s, int idx, int flags) const { TORRENT_ASYNC_CALL3(force_tracker_request, aux::time_now() + seconds(s) , idx, flags); } void torrent_handle::file_status(std::vector& status) const { status.clear(); boost::shared_ptr t = m_torrent.lock(); if (!t || !t->has_storage()) return; session_impl& ses = static_cast(t->session()); ses.disk_thread().files().get_status(&status, t->get_storage()); } void torrent_handle::scrape_tracker(int idx) const { TORRENT_ASYNC_CALL2(scrape_tracker, idx, true); } void torrent_handle::super_seeding(bool on) const { TORRENT_ASYNC_CALL1(super_seeding, on); } #ifndef TORRENT_NO_DEPRECATE void torrent_handle::resolve_countries(bool r) { #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES TORRENT_ASYNC_CALL1(resolve_countries, r); #endif } bool torrent_handle::resolve_countries() const { #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES TORRENT_SYNC_CALL_RET(bool, false, resolving_countries); return r; #else return false; #endif } #endif // TORRENT_NO_DEPRECATE void torrent_handle::get_full_peer_list(std::vector& v) const { TORRENT_SYNC_CALL1(get_full_peer_list, boost::ref(v)); } void torrent_handle::get_peer_info(std::vector& v) const { TORRENT_SYNC_CALL1(get_peer_info, boost::ref(v)); } void torrent_handle::get_download_queue(std::vector& queue) const { TORRENT_SYNC_CALL1(get_download_queue, &queue); } void torrent_handle::set_piece_deadline(int index, int deadline, int flags) const { TORRENT_ASYNC_CALL3(set_piece_deadline, index, deadline, flags); } void torrent_handle::reset_piece_deadline(int index) const { TORRENT_ASYNC_CALL1(reset_piece_deadline, index); } void torrent_handle::clear_piece_deadlines() const { TORRENT_ASYNC_CALL(clear_time_critical); } boost::shared_ptr torrent_handle::native_handle() const { return m_torrent.lock(); } std::size_t hash_value(torrent_status const& ts) { return hash_value(ts.handle); } std::size_t hash_value(torrent_handle const& th) { // using the locked shared_ptr value as hash doesn't work // for expired weak_ptrs. So, we're left with a hack return std::size_t(*reinterpret_cast(&th.m_torrent)); } } libtorrent-rasterbar-1.1.13/src/torrent_info.cpp000066400000000000000000001402751351156116000217570ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/ConvertUTF.h" #include "libtorrent/torrent_info.hpp" #include "libtorrent/string_util.hpp" // is_space, is_i2p_url #include "libtorrent/bencode.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/file.hpp" #include "libtorrent/utf8.hpp" #include "libtorrent/time.hpp" #include "libtorrent/random.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/aux_/escape_string.hpp" // maybe_url_encode #include "libtorrent/aux_/merkle.hpp" // for merkle_* #include "libtorrent/aux_/time.hpp" #include "libtorrent/add_torrent_params.hpp" #include "libtorrent/magnet_uri.hpp" #include "libtorrent/announce_entry.hpp" #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/lazy_entry.hpp" #endif #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include #include #if !defined TORRENT_NO_DEPRECATE && TORRENT_USE_IOSTREAM #include #include #endif #include "libtorrent/aux_/disable_warnings_pop.hpp" #if TORRENT_USE_I2P #include "libtorrent/parse_url.hpp" #endif namespace libtorrent { namespace { bool valid_path_character(boost::int32_t const c) { #ifdef TORRENT_WINDOWS static const char invalid_chars[] = "?<>\"|\b*:"; #else static const char invalid_chars[] = ""; #endif if (c < 32) return false; if (c > 127) return true; return std::strchr(invalid_chars, static_cast(c)) == NULL; } bool filter_path_character(boost::int32_t const c) { // these unicode characters change the writing writing direction of the // string and can be used for attacks: // https://security.stackexchange.com/questions/158802/how-can-this-executable-have-an-avi-extension static const boost::array bad_cp = {{0x202a, 0x202b, 0x202c, 0x202d, 0x202e, 0x200e, 0x200f}}; if (std::find(bad_cp.begin(), bad_cp.end(), c) != bad_cp.end()) return true; static const char invalid_chars[] = "/\\"; if (c > 127) return false; return std::strchr(invalid_chars, static_cast(c)) != NULL; } } // anonymous namespace // fixes invalid UTF-8 sequences TORRENT_EXTRA_EXPORT bool verify_encoding(std::string& target) { if (target.empty()) return true; std::string tmp_path; tmp_path.reserve(target.size()+5); bool valid_encoding = true; UTF8 const* ptr = reinterpret_cast(&target[0]); UTF8 const* end = ptr + target.size(); while (ptr < end) { UTF32 codepoint; UTF32* cp = &codepoint; // decode a single utf-8 character ConversionResult res = ConvertUTF8toUTF32(&ptr, end, &cp, cp + 1 , lenientConversion); // this was the last character, and nothing was // written to the destination buffer (i.e. the source character was // truncated) if (res == sourceExhausted || res == sourceIllegal) { if (cp == &codepoint) { if (res == sourceExhausted) ptr = end; else ++ptr; codepoint = '_'; valid_encoding = false; } } else if ((res != conversionOK && res != targetExhausted) || codepoint == UNI_REPLACEMENT_CHAR) { // we expect the conversion to fail with targetExhausted, since we // only pass in a single destination character slot. The last // character will succeed though. Also, if the character was replaced, // use our own replacement symbol (underscore). codepoint = '_'; valid_encoding = false; } // encode codepoint into utf-8 cp = &codepoint; UTF8 sequence[5]; UTF8* start = sequence; res = ConvertUTF32toUTF8(const_cast(&cp), cp + 1, &start, start + 5, lenientConversion); TORRENT_UNUSED(res); TORRENT_ASSERT(res == conversionOK); for (int i = 0; i < std::min(5, int(start - sequence)); ++i) tmp_path += char(sequence[i]); } // 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 = tmp_path; return valid_encoding; } void sanitize_append_path_element(std::string& path , char const* element, int element_len) { if (element_len == 1 && element[0] == '.') return; #ifdef TORRENT_WINDOWS #define TORRENT_SEPARATOR '\\' #else #define TORRENT_SEPARATOR '/' #endif path.reserve(path.size() + element_len + 2); int added_separator = 0; if (!path.empty()) { path += TORRENT_SEPARATOR; added_separator = 1; } if (element_len == 0) { path += "_"; return; } #if !TORRENT_USE_UNC_PATHS && defined TORRENT_WINDOWS // if we're not using UNC paths on windows, there // are certain filenames we're not allowed to use static const char const* reserved_names[] = { "con", "prn", "aux", "clock$", "nul", "com0", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "lpt0", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9" }; int num_names = sizeof(reserved_names)/sizeof(reserved_names[0]); // this is not very efficient, but it only affects some specific // windows builds for now anyway (not even the default windows build) std::string pe(element, element_len); char const* file_end = strrchr(pe.c_str(), '.'); std::string name; if (file_end) name.assign(pe.c_str(), file_end); else name = pe; std::transform(name.begin(), name.end(), name.begin(), &to_lower); char const* str = std::find(reserved_names, reserved_names + num_names, name); if (str != reserved + num_names) { pe = "_" + pe; element = pe.c_str(); element_len = pe.size(); } #endif // this counts the number of unicode characters // we've added (which is different from the number // of bytes) int unicode_chars = 0; int added = 0; // the number of dots we've added char num_dots = 0; bool found_extension = false; int seq_len = 0; for (int i = 0; i < element_len; i += seq_len) { boost::int32_t code_point; boost::tie(code_point, seq_len) = parse_utf8_codepoint(element + i, element_len - i); if (code_point >= 0 && filter_path_character(code_point)) { continue; } if (code_point < 0 || !valid_path_character(code_point)) { // invalid utf8 sequence, replace with "_" path += '_'; ++added; ++unicode_chars; continue; } TORRENT_ASSERT(isLegalUTF8(reinterpret_cast(element + i), seq_len)); // validation passed, add it to the output string for (int k = i; k < i + seq_len; ++k) { TORRENT_ASSERT(element[k] != 0); path.push_back(element[k]); } if (code_point == '.') ++num_dots; added += seq_len; ++unicode_chars; // any given path element should not // be more than 255 characters // if we exceed 240, pick up any potential // file extension and add that too #ifdef TORRENT_WINDOWS if (unicode_chars >= 240 && !found_extension) #else if (added >= 240 && !found_extension) #endif { int dot = -1; for (int j = element_len-1; j > (std::max)(element_len - 10, i); --j) { if (element[j] != '.') continue; dot = j; break; } // there is no extension if (dot == -1) break; found_extension = true; i = dot - 1; } } if (added == num_dots && added <= 2) { // revert everything path.erase(path.end()-added-added_separator, path.end()); return; } #ifdef TORRENT_WINDOWS // remove trailing spaces and dots. These aren't allowed in filenames on windows for (int i = path.size() - 1; i >= 0; --i) { if (path[i] != ' ' && path[i] != '.') break; path.resize(i); --added; TORRENT_ASSERT(added >= 0); } if (added == 0 && added_separator) { // remove the separator added at the beginning path.erase(path.end()-1); return; } #endif if (path.empty()) path = "_"; } namespace { boost::uint32_t get_file_attributes(bdecode_node const& dict) { boost::uint32_t file_flags = 0; bdecode_node attr = dict.dict_find_string("attr"); if (attr) { for (int i = 0; i < attr.string_length(); ++i) { switch (attr.string_ptr()[i]) { case 'l': file_flags |= file_storage::flag_symlink; break; case 'x': file_flags |= file_storage::flag_executable; break; case 'h': file_flags |= file_storage::flag_hidden; break; case 'p': file_flags |= file_storage::flag_pad_file; break; } } } return file_flags; } // iterates an array of strings and returns the sum of the lengths of all // strings + one additional character per entry (to account for the presumed // forward- or backslash to seaprate directory entries) int path_length(bdecode_node const& p, error_code& ec) { int ret = 0; int const len = p.list_size(); for (int i = 0; i < len; ++i) { bdecode_node e = p.list_at(i); if (e.type() != bdecode_node::string_t) { ec = errors::torrent_invalid_name; return -1; } ret += e.string_length(); } return ret + len; } // 'top_level' is extracting the file for a single-file torrent. The // distinction is that the filename is found in "name" rather than // "path" // root_dir is the name of the torrent, unless this is a single file // torrent, in which case it's empty. bool extract_single_file(bdecode_node const& dict, file_storage& files , std::string const& root_dir, ptrdiff_t info_ptr_diff, bool top_level , int& pad_file_cnt, error_code& ec) { if (dict.type() != bdecode_node::dict_t) return false; boost::uint32_t file_flags = get_file_attributes(dict); // symlinks have an implied "size" of zero. i.e. they use up 0 bytes of // the torrent payload space boost::int64_t const file_size = (file_flags & file_storage::flag_symlink) ? 0 : dict.dict_find_int_value("length", -1); if (file_size < 0 ) { ec = errors::torrent_invalid_length; return false; } boost::int64_t const mtime = dict.dict_find_int_value("mtime", 0); std::string path = root_dir; std::string path_element; char const* filename = NULL; int filename_len = 0; if (top_level) { // prefer the name.utf-8 because if it exists, it is more likely to be // correctly encoded bdecode_node p = dict.dict_find_string("name.utf-8"); if (!p) p = dict.dict_find_string("name"); if (!p || p.string_length() == 0) { ec = errors::torrent_missing_name; return false; } filename = p.string_ptr() + info_ptr_diff; filename_len = p.string_length(); while (filename_len > 0 && filename[0] == TORRENT_SEPARATOR) { filename += 1; filename_len -= 1; } sanitize_append_path_element(path, p.string_ptr(), p.string_length()); } else { bdecode_node p = dict.dict_find_list("path.utf-8"); if (!p) p = dict.dict_find_list("path"); if (p && p.list_size() > 0) { std::size_t const orig_path_len = path.size(); int const preallocate = path.size() + path_length(p, ec); if (ec) return false; path.reserve(preallocate); for (int i = 0, end(p.list_size()); i < end; ++i) { bdecode_node e = p.list_at(i); if (i == end - 1) { filename = e.string_ptr() + info_ptr_diff; filename_len = e.string_length(); } while (filename_len > 0 && filename[0] == TORRENT_SEPARATOR) { filename += 1; filename_len -= 1; } sanitize_append_path_element(path, e.string_ptr(), e.string_length()); } // if all path elements were sanitized away, we need to use another // name instead if (path.size() == orig_path_len) { path += TORRENT_SEPARATOR; path += "_"; } } else if (file_flags & file_storage::flag_pad_file) { // pad files don't need a path element, we'll just store them // under the .pad directory char cnt[10]; snprintf(cnt, sizeof(cnt), "%d", pad_file_cnt); path = combine_path(".pad", cnt); ++pad_file_cnt; } else { ec = errors::torrent_missing_name; return false; } } // bitcomet pad file if (path.find("_____padding_file_") != std::string::npos) file_flags = file_storage::flag_pad_file; bdecode_node fh = dict.dict_find_string("sha1"); char const* filehash = NULL; if (fh && fh.string_length() == 20) filehash = fh.string_ptr() + info_ptr_diff; std::string symlink_path; if (file_flags & file_storage::flag_symlink) { if (bdecode_node s_p = dict.dict_find_list("symlink path")) { int const preallocate = path_length(s_p, ec); if (ec) return false; symlink_path.reserve(preallocate); for (int i = 0, end(s_p.list_size()); i < end; ++i) { bdecode_node const& n = s_p.list_at(i); sanitize_append_path_element(symlink_path, n.string_ptr() , n.string_length()); } } } else { file_flags &= ~file_storage::flag_symlink; } if (filename_len > path.length() || path.compare(path.size() - filename_len, filename_len, filename , filename_len) != 0) { // if the filename was sanitized and differ, clear it to just use path filename = NULL; filename_len = 0; } files.add_file_borrow(filename, filename_len, path, file_size, file_flags, filehash , mtime, symlink_path); return true; } // root_dir is the name of the torrent, unless this is a single file // torrent, in which case it's empty. bool extract_files(bdecode_node const& list, file_storage& target , std::string const& root_dir, ptrdiff_t info_ptr_diff, error_code& ec) { if (list.type() != bdecode_node::list_t) { ec = errors::torrent_file_parse_failed; return false; } target.reserve(list.list_size()); // this is the counter used to name pad files int pad_file_cnt = 0; for (int i = 0, end(list.list_size()); i < end; ++i) { if (!extract_single_file(list.list_at(i), target, root_dir , info_ptr_diff, false, pad_file_cnt, ec)) return false; } return true; } int load_file(std::string const& filename, std::vector& v , error_code& ec) { ec.clear(); file f; if (!f.open(filename, file::read_only, ec)) return -1; boost::int64_t s = f.get_size(ec); if (ec) return -1; v.resize(std::size_t(s)); if (s == 0) return 0; file::iovec_t b = {&v[0], size_t(s) }; boost::int64_t read = f.readv(0, &b, 1, ec); if (read != s) return -3; if (ec) return -3; return 0; } } // anonymous namespace web_seed_entry::web_seed_entry(std::string const& url_, type_t type_ , std::string const& auth_ , headers_t const& extra_headers_) : url(url_) , auth(auth_) , extra_headers(extra_headers_) , type(type_) { } torrent_info::torrent_info(torrent_info const& t) : m_files(t.m_files) , m_orig_files(t.m_orig_files) , m_urls(t.m_urls) , m_web_seeds(t.m_web_seeds) , m_nodes(t.m_nodes) , m_merkle_tree(t.m_merkle_tree) , m_piece_hashes(t.m_piece_hashes) , m_comment(t.m_comment) , m_created_by(t.m_created_by) , m_creation_date(t.m_creation_date) , m_info_hash(t.m_info_hash) , m_info_section_size(t.m_info_section_size) , m_merkle_first_leaf(t.m_merkle_first_leaf) , m_multifile(t.m_multifile) , m_private(t.m_private) , m_i2p(t.m_i2p) { #if TORRENT_USE_INVARIANT_CHECKS t.check_invariant(); #endif if (m_info_section_size == 0) return; TORRENT_ASSERT(m_piece_hashes); error_code ec; m_info_section.reset(new char[m_info_section_size]); memcpy(m_info_section.get(), t.m_info_section.get(), m_info_section_size); ptrdiff_t offset = m_info_section.get() - t.m_info_section.get(); m_files.apply_pointer_offset(offset); if (m_orig_files) const_cast(*m_orig_files).apply_pointer_offset(offset); #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS for (int i = 0; i < m_collections.size(); ++i) m_collections[i].first += offset; for (int i = 0; i < m_similar_torrents.size(); ++i) m_similar_torrents[i] += offset; #endif if (m_info_dict) { // make this decoded object point to our copy of the info section // buffer m_info_dict.switch_underlying_buffer(m_info_section.get()); } m_piece_hashes += offset; TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); } void torrent_info::resolve_duplicate_filenames() { INVARIANT_CHECK; boost::unordered_set files; std::string empty_str; // insert all directories first, to make sure no files // are allowed to collied with them m_files.all_path_hashes(files); for (int i = 0; i < m_files.num_files(); ++i) { // as long as this file already exists // increase the counter boost::uint32_t const h = m_files.file_path_hash(i, empty_str); if (!files.insert(h).second) { // This filename appears to already exist! // If this happens, just start over and do it the slow way, // comparing full file names and come up with new names resolve_duplicate_filenames_slow(); return; } } } void torrent_info::resolve_duplicate_filenames_slow() { INVARIANT_CHECK; int cnt = 0; #if TORRENT_HAS_BOOST_UNORDERED boost::unordered_set files; #else std::set files; #endif std::vector const& paths = m_files.paths(); files.reserve(paths.size() + m_files.num_files()); // insert all directories first, to make sure no files // are allowed to collied with them for (std::vector::const_iterator i = paths.begin() , end(paths.end()); i != end; ++i) { std::string p = combine_path(m_files.name(), *i); files.insert(p); while (has_parent_path(p)) { p = parent_path(p); // we don't want trailing slashes here TORRENT_ASSERT(p[p.size() - 1] == TORRENT_SEPARATOR); p.resize(p.size() - 1); files.insert(p); } } for (int i = 0; i < m_files.num_files(); ++i) { // as long as this file already exists // increase the counter std::string filename = m_files.file_path(i); if (!files.insert(filename).second) { std::string base = remove_extension(filename); std::string ext = extension(filename); do { ++cnt; char new_ext[50]; snprintf(new_ext, sizeof(new_ext), ".%d%s", cnt, ext.c_str()); filename = base + new_ext; } while (!files.insert(filename).second); copy_on_write(); m_files.rename_file(i, filename); } cnt = 0; } } void torrent_info::remap_files(file_storage const& f) { INVARIANT_CHECK; TORRENT_ASSERT(is_loaded()); // the new specified file storage must have the exact // same size as the current file storage TORRENT_ASSERT(m_files.total_size() == f.total_size()); if (m_files.total_size() != f.total_size()) return; copy_on_write(); m_files = f; m_files.set_num_pieces(m_orig_files->num_pieces()); m_files.set_piece_length(m_orig_files->piece_length()); } #ifndef TORRENT_NO_DEPRECATE torrent_info::torrent_info(lazy_entry const& torrent_file, error_code& ec, int) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { std::pair buf = torrent_file.data_section(); bdecode_node e; if (bdecode(buf.first, buf.first + buf.second, e, ec) != 0) return; parse_torrent_file(e, ec, 0); } torrent_info::torrent_info(lazy_entry const& torrent_file, int) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { std::pair buf = torrent_file.data_section(); bdecode_node e; error_code ec; if (bdecode(buf.first, buf.first + buf.second, e, ec) != 0) { #ifndef BOOST_NO_EXCEPTIONS throw invalid_torrent_file(ec); #else return; #endif } #ifndef BOOST_NO_EXCEPTIONS if (!parse_torrent_file(e, ec, 0)) throw invalid_torrent_file(ec); #else parse_torrent_file(e, ec, 0); #endif } // standard constructor that parses a torrent file torrent_info::torrent_info(entry const& torrent_file) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { std::vector tmp; std::back_insert_iterator > out(tmp); bencode(out, torrent_file); bdecode_node e; error_code ec; if (tmp.size() == 0 || bdecode(&tmp[0], &tmp[0] + tmp.size(), e, ec) != 0) { #ifndef BOOST_NO_EXCEPTIONS throw invalid_torrent_file(ec); #else return; #endif } #ifndef BOOST_NO_EXCEPTIONS if (!parse_torrent_file(e, ec, 0)) throw invalid_torrent_file(ec); #else parse_torrent_file(e, ec, 0); #endif INVARIANT_CHECK; } #endif #ifndef BOOST_NO_EXCEPTIONS torrent_info::torrent_info(bdecode_node const& torrent_file, int flags) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { error_code ec; if (!parse_torrent_file(torrent_file, ec, flags)) throw invalid_torrent_file(ec); INVARIANT_CHECK; } torrent_info::torrent_info(char const* buffer, int size, int flags) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { error_code ec; bdecode_node e; if (bdecode(buffer, buffer + size, e, ec) != 0) throw invalid_torrent_file(ec); if (!parse_torrent_file(e, ec, flags)) throw invalid_torrent_file(ec); INVARIANT_CHECK; } torrent_info::torrent_info(std::string const& filename, int flags) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { std::vector buf; error_code ec; int ret = load_file(filename, buf, ec); if (ret < 0) throw invalid_torrent_file(ec); bdecode_node e; if (buf.size() == 0 || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) throw invalid_torrent_file(ec); if (!parse_torrent_file(e, ec, flags)) throw invalid_torrent_file(ec); INVARIANT_CHECK; } #if TORRENT_USE_WSTRING #ifndef TORRENT_NO_DEPRECATE torrent_info::torrent_info(std::wstring const& filename, int flags) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { std::vector buf; std::string utf8; wchar_utf8(filename, utf8); error_code ec; int ret = load_file(utf8, buf, ec); if (ret < 0) throw invalid_torrent_file(ec); bdecode_node e; if (buf.size() == 0 || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) throw invalid_torrent_file(ec); if (!parse_torrent_file(e, ec, flags)) throw invalid_torrent_file(ec); INVARIANT_CHECK; } void torrent_info::rename_file(int index, std::wstring const& new_filename) { TORRENT_ASSERT(is_loaded()); copy_on_write(); m_files.rename_file_deprecated(index, new_filename); } #endif // TORRENT_NO_DEPRECATE #endif // TORRENT_USE_WSTRING #endif torrent_info::torrent_info(bdecode_node const& torrent_file, error_code& ec , int flags) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { parse_torrent_file(torrent_file, ec, flags); INVARIANT_CHECK; } torrent_info::torrent_info(char const* buffer, int size, error_code& ec, int flags) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { bdecode_node e; if (bdecode(buffer, buffer + size, e, ec) != 0) return; parse_torrent_file(e, ec, flags); INVARIANT_CHECK; } torrent_info::torrent_info(std::string const& filename, error_code& ec, int flags) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { std::vector buf; int ret = load_file(filename, buf, ec); if (ret < 0) return; bdecode_node e; if (buf.size() == 0 || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) return; parse_torrent_file(e, ec, flags); INVARIANT_CHECK; } #if TORRENT_USE_WSTRING #ifndef TORRENT_NO_DEPRECATE torrent_info::torrent_info(std::wstring const& filename, error_code& ec , int flags) : m_piece_hashes(0) , m_creation_date(0) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { std::vector buf; std::string utf8; wchar_utf8(filename, utf8); int ret = load_file(utf8, buf, ec); if (ret < 0) return; bdecode_node e; if (buf.size() == 0 || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) return; parse_torrent_file(e, ec, flags); INVARIANT_CHECK; } #endif // TORRENT_NO_DEPRECATE #endif // TORRENT_USE_WSTRING // 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, int flags) : m_piece_hashes(0) , m_creation_date(time(0)) , m_info_hash(info_hash) , m_info_section_size(0) , m_merkle_first_leaf(0) , m_multifile(false) , m_private(false) , m_i2p(false) { TORRENT_UNUSED(flags); } torrent_info::~torrent_info() {} void torrent_info::load(char const* buffer, int size, error_code& ec) { bdecode_node e; if (bdecode(buffer, buffer + size, e, ec) != 0) return; if (!parse_torrent_file(e, ec, 0)) return; } void torrent_info::unload() { TORRENT_ASSERT(m_info_section.unique()); m_info_section.reset(); m_info_section_size = 0; // if we have orig_files, we have to keep // m_files around, since it means we have // remapped files, and we won't be able to // restore that from just reloading the // torrent file if (m_orig_files) m_orig_files.reset(); else m_files.unload(); m_piece_hashes = 0; std::vector().swap(m_web_seeds); TORRENT_ASSERT(!is_loaded()); } sha1_hash torrent_info::hash_for_piece(int index) const { return sha1_hash(hash_for_piece_ptr(index)); } void torrent_info::copy_on_write() { TORRENT_ASSERT(is_loaded()); INVARIANT_CHECK; if (m_orig_files) return; m_orig_files.reset(new file_storage(m_files)); } #define SWAP(tmp, a, b) \ tmp = a; \ a = b; \ b = tmp; void torrent_info::swap(torrent_info& ti) { INVARIANT_CHECK; using std::swap; m_urls.swap(ti.m_urls); m_web_seeds.swap(ti.m_web_seeds); m_files.swap(ti.m_files); m_orig_files.swap(ti.m_orig_files); m_nodes.swap(ti.m_nodes); swap(m_info_hash, ti.m_info_hash); swap(m_creation_date, ti.m_creation_date); m_comment.swap(ti.m_comment); m_created_by.swap(ti.m_created_by); swap(m_info_section, ti.m_info_section); swap(m_piece_hashes, ti.m_piece_hashes); m_info_dict.swap(ti.m_info_dict); swap(m_merkle_tree, ti.m_merkle_tree); std::swap(m_info_section_size, ti.m_info_section_size); boost::uint32_t tmp; SWAP(tmp, m_merkle_first_leaf, ti.m_merkle_first_leaf); bool tmp2; SWAP(tmp2, m_private, ti.m_private); SWAP(tmp2, m_i2p, ti.m_i2p); SWAP(tmp2, m_multifile, ti.m_multifile); } #undef SWAP std::string torrent_info::ssl_cert() const { // this is parsed lazily if (!m_info_dict) { error_code ec; bdecode(m_info_section.get(), m_info_section.get() + m_info_section_size, m_info_dict, ec); if (ec) return ""; } if (m_info_dict.type() != bdecode_node::dict_t) return ""; return m_info_dict.dict_find_string_value("ssl-cert"); } bool torrent_info::parse_info_section(bdecode_node const& info , error_code& ec, int flags) { TORRENT_UNUSED(flags); if (info.type() != bdecode_node::dict_t) { ec = errors::torrent_info_no_dict; return false; } // hash the info-field to calculate info-hash hasher h; std::pair section = info.data_section(); h.update(section.first, section.second); m_info_hash = h.final(); if (section.second >= (std::numeric_limits::max)()) { ec = errors::metadata_too_large; return false; } // copy the info section m_info_section_size = section.second; m_info_section.reset(new char[m_info_section_size]); std::memcpy(m_info_section.get(), section.first, m_info_section_size); TORRENT_ASSERT(section.first[0] == 'd'); TORRENT_ASSERT(section.first[m_info_section_size-1] == 'e'); // when translating a pointer that points into the 'info' tree's // backing buffer, into a pointer to our copy of the info section, // this is the pointer offset to use. ptrdiff_t info_ptr_diff = m_info_section.get() - section.first; // extract piece length int piece_length = info.dict_find_int_value("piece length", -1); if (piece_length <= 0) { ec = errors::torrent_missing_piece_length; return false; } file_storage files; files.set_piece_length(piece_length); // extract file name (or the directory name if it's a multi file libtorrent) bdecode_node name_ent = info.dict_find_string("name.utf-8"); if (!name_ent) name_ent = info.dict_find_string("name"); if (!name_ent) { ec = errors::torrent_missing_name; // mark the torrent as invalid m_files.set_piece_length(0); return false; } std::string name; sanitize_append_path_element(name, name_ent.string_ptr() , name_ent.string_length()); if (name.empty()) name = to_hex(m_info_hash.to_string()); // extract file list bdecode_node files_node = info.dict_find_list("files"); if (!files_node) { // if there's no list of files, there has to be a length // field. // this is the counter used to name pad files int pad_file_cnt = 0; if (!extract_single_file(info, files, "", info_ptr_diff, true, pad_file_cnt, ec)) { // mark the torrent as invalid m_files.set_piece_length(0); return false; } m_multifile = false; } else { if (!extract_files(files_node, files, name, info_ptr_diff, ec)) { // mark the torrent as invalid m_files.set_piece_length(0); return false; } m_multifile = true; } if (files.num_files() == 0) { ec = errors::no_files_in_torrent; // mark the torrent as invalid m_files.set_piece_length(0); return false; } if (files.name().empty()) { ec = errors::torrent_missing_name; // mark the torrent as invalid m_files.set_piece_length(0); return false; } // extract SHA-1 hashes for all pieces // we want this division to round upwards, that's why we have the // extra addition if (files.total_size() >= static_cast(std::numeric_limits::max() - files.piece_length()) * files.piece_length()) { ec = errors::too_many_pieces_in_torrent; // mark the torrent as invalid m_files.set_piece_length(0); return false; } files.set_num_pieces(int((files.total_size() + files.piece_length() - 1) / files.piece_length())); bdecode_node pieces = info.dict_find_string("pieces"); bdecode_node root_hash = info.dict_find_string("root hash"); if (!pieces && !root_hash) { ec = errors::torrent_missing_pieces; // mark the torrent as invalid m_files.set_piece_length(0); return false; } // this is an arbitrary limit to avoid malicious torrents causing // unreasaonably large allocations for the merkle hash tree // the size of the tree would be max_pieces * sizeof(int) * 2 // which is about 6.3 MB with this limit const int max_pieces = 0xC0000; // we expect the piece hashes to be < 2 GB in size if (files.num_pieces() >= std::numeric_limits::max() / 20 || files.num_pieces() > max_pieces) { ec = errors::too_many_pieces_in_torrent; // mark the torrent as invalid m_files.set_piece_length(0); return false; } if (pieces) { if (pieces.string_length() != files.num_pieces() * 20) { ec = errors::torrent_invalid_hashes; // mark the torrent as invalid m_files.set_piece_length(0); return false; } m_piece_hashes = pieces.string_ptr() + info_ptr_diff; TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); } else { TORRENT_ASSERT(root_hash); if (root_hash.string_length() != 20) { ec = errors::torrent_invalid_hashes; // mark the torrent as invalid m_files.set_piece_length(0); return false; } if (files.num_pieces() <= 0) { ec = errors::no_files_in_torrent; // mark the torrent as invalid m_files.set_piece_length(0); return false; } int const num_leafs = merkle_num_leafs(files.num_pieces()); int const num_nodes = merkle_num_nodes(num_leafs); if (num_nodes - num_leafs >= (2<<24)) { ec = errors::too_many_pieces_in_torrent; // mark the torrent as invalid m_files.set_piece_length(0); return false; } m_merkle_first_leaf = num_nodes - num_leafs; m_merkle_tree.resize(num_nodes); std::memset(&m_merkle_tree[0], 0, num_nodes * 20); m_merkle_tree[0].assign(root_hash.string_ptr()); } m_private = info.dict_find_int_value("private", 0) != 0; #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS bdecode_node similar = info.dict_find_list("similar"); if (similar) { for (int i = 0; i < similar.list_size(); ++i) { if (similar.list_at(i).type() != bdecode_node::string_t) continue; if (similar.list_at(i).string_length() != 20) continue; m_similar_torrents.push_back(similar.list_at(i).string_ptr() + info_ptr_diff); } } bdecode_node collections = info.dict_find_list("collections"); if (collections) { for (int i = 0; i < collections.list_size(); ++i) { bdecode_node str = collections.list_at(i); if (str.type() != bdecode_node::string_t) continue; m_collections.push_back(std::make_pair(str.string_ptr() + info_ptr_diff, str.string_length())); } } #endif // TORRENT_DISABLE_MUTABLE_TORRENTS // now, commit the files structure we just parsed out // into the torrent_info object. // if we already have an m_files that's populated, it // indicates that we unloaded this torrent_info ones // and we had modifications to the files, so we unloaded // the orig_files. In that case, the orig files is what // needs to be restored if (m_files.is_loaded()) { m_orig_files.reset(new file_storage); const_cast(*m_orig_files).swap(files); } else { m_files.swap(files); } return true; } bdecode_node torrent_info::info(char const* key) const { if (m_info_dict.type() == bdecode_node::none_t) { error_code ec; bdecode(m_info_section.get(), m_info_section.get() + m_info_section_size, m_info_dict, ec); if (ec) return bdecode_node(); } return m_info_dict.dict_find(key); } bool torrent_info::add_merkle_nodes(std::map const& subtree , int piece) { INVARIANT_CHECK; int n = m_merkle_first_leaf + piece; typedef std::map::const_iterator iter; iter it = subtree.find(n); if (it == subtree.end()) return false; sha1_hash h = it->second; // if the verification passes, these are the // nodes to add to our tree std::map to_add; while (n > 0) { int sibling = merkle_get_sibling(n); int parent = merkle_get_parent(n); iter sibling_hash = subtree.find(sibling); if (sibling_hash == subtree.end()) return false; to_add[n] = h; to_add[sibling] = sibling_hash->second; hasher hs; if (sibling < n) { hs.update(sibling_hash->second.data(), 20); hs.update(h.data(), 20); } else { hs.update(h.data(), 20); hs.update(sibling_hash->second.data(), 20); } h = hs.final(); n = parent; } if (h != m_merkle_tree[0]) return false; // the nodes and piece hash matched the root-hash // insert them into our tree for (std::map::iterator i = to_add.begin() , end(to_add.end()); i != end; ++i) { m_merkle_tree[i->first] = i->second; } return true; } // builds a list of nodes that are required to verify // the given piece std::map torrent_info::build_merkle_list(int piece) const { INVARIANT_CHECK; std::map ret; int n = m_merkle_first_leaf + piece; ret[n] = m_merkle_tree[n]; ret[0] = m_merkle_tree[0]; while (n > 0) { int sibling = merkle_get_sibling(n); int parent = merkle_get_parent(n); ret[sibling] = m_merkle_tree[sibling]; // we cannot build the tree path if one // of the nodes in the tree is missing TORRENT_ASSERT(m_merkle_tree[sibling] != sha1_hash(0)); n = parent; } return ret; } bool torrent_info::parse_torrent_file(bdecode_node const& torrent_file , error_code& ec, int flags) { if (torrent_file.type() != bdecode_node::dict_t) { ec = errors::torrent_is_no_dict; return false; } bdecode_node info = torrent_file.dict_find_dict("info"); if (info == 0) { bdecode_node link = torrent_file.dict_find_string("magnet-uri"); if (link) { std::string uri = link.string_value(); add_torrent_params p; parse_magnet_uri(uri, p, ec); if (ec) return false; m_info_hash = p.info_hash; for (std::vector::iterator i = p.trackers.begin() , end(p.trackers.end()); i != end; ++i) m_urls.push_back(*i); return true; } ec = errors::torrent_missing_info; return false; } if (!parse_info_section(info, ec, flags)) return false; resolve_duplicate_filenames(); #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS bdecode_node similar = torrent_file.dict_find_list("similar"); if (similar) { for (int i = 0; i < similar.list_size(); ++i) { if (similar.list_at(i).type() != bdecode_node::string_t) continue; if (similar.list_at(i).string_length() != 20) continue; m_owned_similar_torrents.push_back( sha1_hash(similar.list_at(i).string_ptr())); } } bdecode_node collections = torrent_file.dict_find_list("collections"); if (collections) { for (int i = 0; i < collections.list_size(); ++i) { bdecode_node str = collections.list_at(i); if (str.type() != bdecode_node::string_t) continue; m_owned_collections.push_back(std::string(str.string_ptr() , str.string_length())); } } #endif // TORRENT_DISABLE_MUTABLE_TORRENTS // extract the url of the tracker bdecode_node announce_node = torrent_file.dict_find_list("announce-list"); if (announce_node) { m_urls.reserve(announce_node.list_size()); for (int j = 0, end(announce_node.list_size()); j < end; ++j) { bdecode_node tier = announce_node.list_at(j); if (tier.type() != bdecode_node::list_t) continue; for (int k = 0, end2(tier.list_size()); k < end2; ++k) { announce_entry e(tier.list_string_value_at(k)); e.trim(); if (e.url.empty()) continue; e.tier = j; e.fail_limit = 0; e.source = announce_entry::source_torrent; #if TORRENT_USE_I2P if (is_i2p_url(e.url)) m_i2p = true; #endif m_urls.push_back(e); } } if (!m_urls.empty()) { // shuffle each tier std::vector::iterator start = m_urls.begin(); std::vector::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, randint); start = stop; current_tier = stop->tier; } } std::random_shuffle(start, stop, randint); } } if (m_urls.empty()) { announce_entry e(torrent_file.dict_find_string_value("announce")); e.fail_limit = 0; e.source = announce_entry::source_torrent; e.trim(); #if TORRENT_USE_I2P if (is_i2p_url(e.url)) m_i2p = true; #endif if (!e.url.empty()) m_urls.push_back(e); } bdecode_node nodes = torrent_file.dict_find_list("nodes"); if (nodes) { for (int i = 0, end(nodes.list_size()); i < end; ++i) { bdecode_node n = nodes.list_at(i); if (n.type() != bdecode_node::list_t || n.list_size() < 2 || n.list_at(0).type() != bdecode_node::string_t || n.list_at(1).type() != bdecode_node::int_t) continue; m_nodes.push_back(std::make_pair( n.list_at(0).string_value() , int(n.list_at(1).int_value()))); } } // extract creation date boost::int64_t cd = torrent_file.dict_find_int_value("creation date", -1); if (cd >= 0) { m_creation_date = long(cd); } // if there are any url-seeds, extract them bdecode_node url_seeds = torrent_file.dict_find("url-list"); if (url_seeds && url_seeds.type() == bdecode_node::string_t && url_seeds.string_length() > 0) { web_seed_entry ent(maybe_url_encode(url_seeds.string_value()) , web_seed_entry::url_seed); if (m_multifile && ent.url[ent.url.size()-1] != '/') ent.url += '/'; m_web_seeds.push_back(ent); } else if (url_seeds && url_seeds.type() == bdecode_node::list_t) { // only add a URL once std::set unique; for (int i = 0, end(url_seeds.list_size()); i < end; ++i) { bdecode_node url = url_seeds.list_at(i); if (url.type() != bdecode_node::string_t) continue; if (url.string_length() == 0) continue; web_seed_entry ent(maybe_url_encode(url.string_value()) , web_seed_entry::url_seed); if (m_multifile && ent.url[ent.url.size()-1] != '/') ent.url += '/'; if (unique.count(ent.url)) continue; unique.insert(ent.url); m_web_seeds.push_back(ent); } } // if there are any http-seeds, extract them bdecode_node http_seeds = torrent_file.dict_find("httpseeds"); if (http_seeds && http_seeds.type() == bdecode_node::string_t && http_seeds.string_length() > 0) { m_web_seeds.push_back(web_seed_entry(maybe_url_encode(http_seeds.string_value()) , web_seed_entry::http_seed)); } else if (http_seeds && http_seeds.type() == bdecode_node::list_t) { // only add a URL once std::set unique; for (int i = 0, end(http_seeds.list_size()); i < end; ++i) { bdecode_node url = http_seeds.list_at(i); if (url.type() != bdecode_node::string_t || url.string_length() == 0) continue; std::string u = maybe_url_encode(url.string_value()); if (unique.count(u)) continue; unique.insert(u); m_web_seeds.push_back(web_seed_entry(u, web_seed_entry::http_seed)); } } m_comment = torrent_file.dict_find_string_value("comment.utf-8"); if (m_comment.empty()) m_comment = torrent_file.dict_find_string_value("comment"); verify_encoding(m_comment); m_created_by = torrent_file.dict_find_string_value("created by.utf-8"); if (m_created_by.empty()) m_created_by = torrent_file.dict_find_string_value("created by"); verify_encoding(m_created_by); return true; } boost::optional torrent_info::creation_date() const { if (m_creation_date != 0) { return boost::optional(m_creation_date); } return boost::optional(); } void torrent_info::add_tracker(std::string const& url, int tier) { announce_entry e(url); e.tier = tier; e.source = announce_entry::source_client; m_urls.push_back(e); std::sort(m_urls.begin(), m_urls.end(), boost::bind(&announce_entry::tier, _1) < boost::bind(&announce_entry::tier, _2)); } #ifndef TORRENT_NO_DEPRECATE namespace { struct filter_web_seed_type { filter_web_seed_type(web_seed_entry::type_t t_) : t(t_) {} void operator() (web_seed_entry const& w) { if (w.type == t) urls.push_back(w.url); } std::vector urls; web_seed_entry::type_t t; }; } std::vector torrent_info::url_seeds() const { return std::for_each(m_web_seeds.begin(), m_web_seeds.end() , filter_web_seed_type(web_seed_entry::url_seed)).urls; } std::vector torrent_info::http_seeds() const { return std::for_each(m_web_seeds.begin(), m_web_seeds.end() , filter_web_seed_type(web_seed_entry::http_seed)).urls; } bool torrent_info::parse_info_section(lazy_entry const& le, error_code& ec , int flags) { if (le.type() == lazy_entry::none_t) return false; std::pair buf = le.data_section(); bdecode_node e; if (bdecode(buf.first, buf.first + buf.second, e, ec) != 0) return false; return parse_info_section(e, ec, flags); } #endif // TORRENT_NO_DEPRECATE void torrent_info::add_url_seed(std::string const& url , std::string const& ext_auth , web_seed_entry::headers_t const& ext_headers) { m_web_seeds.push_back(web_seed_entry(url, web_seed_entry::url_seed , ext_auth, ext_headers)); } void torrent_info::add_http_seed(std::string const& url , std::string const& auth , web_seed_entry::headers_t const& extra_headers) { m_web_seeds.push_back(web_seed_entry(url, web_seed_entry::http_seed , auth, extra_headers)); } void torrent_info::set_web_seeds(std::vector seeds) { m_web_seeds = seeds; } std::vector torrent_info::similar_torrents() const { std::vector ret; #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS ret.reserve(m_similar_torrents.size() + m_owned_similar_torrents.size()); for (int i = 0; i < m_similar_torrents.size(); ++i) ret.push_back(sha1_hash(m_similar_torrents[i])); for (int i = 0; i < m_owned_similar_torrents.size(); ++i) ret.push_back(m_owned_similar_torrents[i]); #endif return ret; } std::vector torrent_info::collections() const { std::vector ret; #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS ret.reserve(m_collections.size() + m_owned_collections.size()); for (int i = 0; i < m_collections.size(); ++i) ret.push_back(std::string(m_collections[i].first, m_collections[i].second)); for (int i = 0; i < m_owned_collections.size(); ++i) ret.push_back(m_owned_collections[i]); #endif // TORRENT_DISABLE_MUTABLE_TORRENTS return ret; } #if !defined TORRENT_NO_DEPRECATE && TORRENT_USE_IOSTREAM // ------- start deprecation ------- void torrent_info::print(std::ostream& os) const { INVARIANT_CHECK; os << "trackers:\n"; for (std::vector::const_iterator i = trackers().begin(); i != trackers().end(); ++i) { os << i->tier << ": " << i->url << "\n"; } if (!m_comment.empty()) os << "comment: " << m_comment << "\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 (int i = 0; i < m_files.num_files(); ++i) os << " " << std::setw(11) << m_files.file_size(i) << " " << m_files.file_path(i) << "\n"; } // ------- end deprecation ------- #endif #if TORRENT_USE_INVARIANT_CHECKS void torrent_info::check_invariant() const { for (int i = 0; i < m_files.num_files(); ++i) { TORRENT_ASSERT(m_files.file_name_ptr(i) != 0); if (m_files.file_name_len(i) != -1) { // name needs to point into the allocated info section buffer TORRENT_ASSERT(m_files.file_name_ptr(i) >= m_info_section.get()); TORRENT_ASSERT(m_files.file_name_ptr(i) < m_info_section.get() + m_info_section_size); } else { // name must be a valid string TORRENT_ASSERT(strlen(m_files.file_name_ptr(i)) < 2048); } } if (m_piece_hashes != 0) { TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); } } #endif } libtorrent-rasterbar-1.1.13/src/torrent_peer.cpp000066400000000000000000000201671351156116000217540ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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/torrent_peer.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/string_util.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/crc32c.hpp" #include "libtorrent/ip_voter.hpp" #include namespace libtorrent { namespace { void apply_mask(boost::uint8_t* b, boost::uint8_t const* mask, int size) { for (int i = 0; i < size; ++i) { *b &= *mask; ++b; ++mask; } } } // 1. if the IP addresses are identical, hash the ports in 16 bit network-order // binary representation, ordered lowest first. // 2. if the IPs are in the same /24, hash the IPs ordered, lowest first. // 3. if the IPs are in the ame /16, mask the IPs by 0xffffff55, hash them // ordered, lowest first. // 4. if IPs are not in the same /16, mask the IPs by 0xffff5555, hash them // ordered, lowest first. // // * for IPv6 peers, just use the first 64 bits and widen the masks. // like this: 0xffff5555 -> 0xffffffff55555555 // the lower 64 bits are always unmasked // // * for IPv6 addresses, compare /32 and /48 instead of /16 and /24 // // * the two IP addresses that are used to calculate the rank must // always be of the same address family // // * all IP addresses are in network byte order when hashed boost::uint32_t peer_priority(tcp::endpoint e1, tcp::endpoint e2) { TORRENT_ASSERT(e1.address().is_v4() == e2.address().is_v4()); using std::swap; boost::uint32_t ret; if (e1.address() == e2.address()) { if (e1.port() > e2.port()) swap(e1, e2); boost::uint32_t p; #if BOOST_ENDIAN_BIG_BYTE p = e1.port() << 16; p |= e2.port(); #elif BOOST_ENDIAN_LITTLE_BYTE p = aux::host_to_network(e2.port()) << 16; p |= aux::host_to_network(e1.port()); #else #error unsupported endianness #endif ret = crc32c_32(p); } #if TORRENT_USE_IPV6 else if (e1.address().is_v6()) { static const boost::uint8_t v6mask[][8] = { { 0xff, 0xff, 0xff, 0xff, 0x55, 0x55, 0x55, 0x55 }, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x55, 0x55 }, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; if (e1 > e2) swap(e1, e2); address_v6::bytes_type b1 = e1.address().to_v6().to_bytes(); address_v6::bytes_type b2 = e2.address().to_v6().to_bytes(); int mask = memcmp(&b1[0], &b2[0], 4) ? 0 : memcmp(&b1[0], &b2[0], 6) ? 1 : 2; apply_mask(&b1[0], v6mask[mask], 8); apply_mask(&b2[0], v6mask[mask], 8); boost::uint64_t addrbuf[4]; memcpy(&addrbuf[0], &b1[0], 16); memcpy(&addrbuf[2], &b2[0], 16); ret = crc32c(addrbuf, 4); } #endif else { static const boost::uint8_t v4mask[][4] = { { 0xff, 0xff, 0x55, 0x55 }, { 0xff, 0xff, 0xff, 0x55 }, { 0xff, 0xff, 0xff, 0xff } }; if (e1 > e2) swap(e1, e2); address_v4::bytes_type b1 = e1.address().to_v4().to_bytes(); address_v4::bytes_type b2 = e2.address().to_v4().to_bytes(); int mask = memcmp(&b1[0], &b2[0], 2) ? 0 : memcmp(&b1[0], &b2[0], 3) ? 1 : 2; apply_mask(&b1[0], v4mask[mask], 4); apply_mask(&b2[0], v4mask[mask], 4); boost::uint64_t addrbuf; memcpy(&addrbuf, &b1[0], 4); memcpy(reinterpret_cast(&addrbuf) + 4, &b2[0], 4); ret = crc32c(&addrbuf, 1); } return ret; } torrent_peer::torrent_peer(boost::uint16_t port_, bool conn, int src) : prev_amount_upload(0) , prev_amount_download(0) , connection(0) , peer_rank(0) , last_optimistically_unchoked(0) , last_connected(0) , port(port_) , hashfails(0) , failcount(0) , connectable(conn) , optimistically_unchoked(false) , seed(false) , fast_reconnects(0) , trust_points(0) , source(src) #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) // assume no support in order to // prefer opening non-encrypted // connections. If it fails, we'll // retry with encryption , pe_support(false) #endif #if TORRENT_USE_IPV6 , is_v6_addr(false) #endif #if TORRENT_USE_I2P , is_i2p_addr(false) #endif , on_parole(false) , banned(false) , supports_utp(true) // assume peers support utp , confirmed_supports_utp(false) , supports_holepunch(false) , web_seed(false) #if TORRENT_USE_ASSERTS , in_use(false) #endif { TORRENT_ASSERT((src & 0xff) == src); } boost::uint32_t torrent_peer::rank(external_ip const& external, int external_port) const { //TODO: how do we deal with our external address changing? if (peer_rank == 0) peer_rank = peer_priority( tcp::endpoint(external.external_address(this->address()), external_port) , tcp::endpoint(this->address(), this->port)); return peer_rank; } #ifndef TORRENT_DISABLE_LOGGING std::string torrent_peer::to_string() const { #if TORRENT_USE_I2P if (is_i2p_addr) return dest(); #endif // TORRENT_USE_I2P error_code ec; return address().to_string(ec); } #endif boost::uint64_t torrent_peer::total_download() const { if (connection != 0) { TORRENT_ASSERT(prev_amount_download == 0); return connection->statistics().total_payload_download(); } else { return boost::uint64_t(prev_amount_download) << 10; } } boost::uint64_t torrent_peer::total_upload() const { if (connection != 0) { TORRENT_ASSERT(prev_amount_upload == 0); return connection->statistics().total_payload_upload(); } else { return boost::uint64_t(prev_amount_upload) << 10; } } ipv4_peer::ipv4_peer( tcp::endpoint const& ep, bool c, int src ) : torrent_peer(ep.port(), c, src) , addr(ep.address().to_v4()) { #if TORRENT_USE_IPV6 is_v6_addr = false; #endif #if TORRENT_USE_I2P is_i2p_addr = false; #endif } ipv4_peer::ipv4_peer(ipv4_peer const& p) : torrent_peer(p), addr(p.addr) {} #if TORRENT_USE_I2P i2p_peer::i2p_peer(char const* dest, bool connectable, int src) : torrent_peer(0, connectable, src), destination(allocate_string_copy(dest)) { #if TORRENT_USE_IPV6 is_v6_addr = false; #endif is_i2p_addr = true; } i2p_peer::~i2p_peer() { free(destination); } #endif // TORRENT_USE_I2P #if TORRENT_USE_IPV6 ipv6_peer::ipv6_peer( tcp::endpoint const& ep, bool c, int src ) : torrent_peer(ep.port(), c, src) , addr(ep.address().to_v6().to_bytes()) { is_v6_addr = true; #if TORRENT_USE_I2P is_i2p_addr = false; #endif } #endif // TORRENT_USE_IPV6 #if TORRENT_USE_I2P char const* torrent_peer::dest() const { if (is_i2p_addr) return static_cast(this)->destination; return ""; } #endif libtorrent::address torrent_peer::address() const { #if TORRENT_USE_IPV6 if (is_v6_addr) return libtorrent::address_v6( static_cast(this)->addr); else #endif #if TORRENT_USE_I2P if (is_i2p_addr) return libtorrent::address(); else #endif return static_cast(this)->addr; } } libtorrent-rasterbar-1.1.13/src/torrent_peer_allocator.cpp000066400000000000000000000106221351156116000240070ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/torrent_peer_allocator.hpp" namespace libtorrent { torrent_peer_allocator::torrent_peer_allocator() : m_ipv4_peer_pool(sizeof(libtorrent::ipv4_peer), 500) #if TORRENT_USE_IPV6 , m_ipv6_peer_pool(sizeof(libtorrent::ipv6_peer), 500) #endif #if TORRENT_USE_I2P , m_i2p_peer_pool(sizeof(libtorrent::i2p_peer), 500) #endif , m_total_bytes(0) , m_total_allocations(0) , m_live_bytes(0) , m_live_allocations(0) { } torrent_peer* torrent_peer_allocator::allocate_peer_entry(int type) { torrent_peer* p = NULL; switch(type) { case torrent_peer_allocator_interface::ipv4_peer_type: p = static_cast(m_ipv4_peer_pool.malloc()); if (p == NULL) return NULL; m_ipv4_peer_pool.set_next_size(500); m_total_bytes += sizeof(libtorrent::ipv4_peer); m_live_bytes += sizeof(libtorrent::ipv4_peer); ++m_live_allocations; ++m_total_allocations; break; #if TORRENT_USE_IPV6 case torrent_peer_allocator_interface::ipv6_peer_type: p = static_cast(m_ipv6_peer_pool.malloc()); if (p == NULL) return NULL; m_ipv6_peer_pool.set_next_size(500); m_total_bytes += sizeof(libtorrent::ipv6_peer); m_live_bytes += sizeof(libtorrent::ipv6_peer); ++m_live_allocations; ++m_total_allocations; break; #endif #if TORRENT_USE_I2P case torrent_peer_allocator_interface::i2p_peer_type: p = static_cast(m_i2p_peer_pool.malloc()); if (p == NULL) return NULL; m_i2p_peer_pool.set_next_size(500); m_total_bytes += sizeof(libtorrent::i2p_peer); m_live_bytes += sizeof(libtorrent::i2p_peer); ++m_live_allocations; ++m_total_allocations; break; #endif } return p; } void torrent_peer_allocator::free_peer_entry(torrent_peer* p) { #if TORRENT_USE_IPV6 if (p->is_v6_addr) { TORRENT_ASSERT(m_ipv6_peer_pool.is_from(static_cast(p))); static_cast(p)->~ipv6_peer(); m_ipv6_peer_pool.free(p); TORRENT_ASSERT(m_live_bytes >= sizeof(ipv6_peer)); m_live_bytes -= sizeof(ipv6_peer); TORRENT_ASSERT(m_live_allocations > 0); --m_live_allocations; return; } #endif #if TORRENT_USE_I2P if (p->is_i2p_addr) { TORRENT_ASSERT(m_i2p_peer_pool.is_from(static_cast(p))); static_cast(p)->~i2p_peer(); m_i2p_peer_pool.free(p); TORRENT_ASSERT(m_live_bytes >= sizeof(i2p_peer)); m_live_bytes -= sizeof(i2p_peer); TORRENT_ASSERT(m_live_allocations > 0); --m_live_allocations; return; } #endif TORRENT_ASSERT(m_ipv4_peer_pool.is_from(static_cast(p))); static_cast(p)->~ipv4_peer(); m_ipv4_peer_pool.free(p); TORRENT_ASSERT(m_live_bytes >= sizeof(ipv4_peer)); m_live_bytes -= sizeof(ipv4_peer); TORRENT_ASSERT(m_live_allocations > 0); --m_live_allocations; } } libtorrent-rasterbar-1.1.13/src/torrent_status.cpp000066400000000000000000000064521351156116000223450ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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/torrent_status.hpp" namespace libtorrent { torrent_status::torrent_status() : error_file(torrent_status::error_file_none) , next_announce(seconds(0)) , total_download(0) , total_upload(0) , total_payload_download(0) , total_payload_upload(0) , total_failed_bytes(0) , total_redundant_bytes(0) , total_done(0) , total_wanted_done(0) , total_wanted(0) , all_time_upload(0) , all_time_download(0) , added_time(0) , completed_time(0) , last_seen_complete(0) , storage_mode(storage_mode_sparse) , progress(0.f) , progress_ppm(0) , queue_position(0) , download_rate(0) , upload_rate(0) , download_payload_rate(0) , upload_payload_rate(0) , num_seeds(0) , num_peers(0) , num_complete(-1) , num_incomplete(-1) , list_seeds(0) , list_peers(0) , connect_candidates(0) , num_pieces(0) , distributed_full_copies(0) , distributed_fraction(0) , distributed_copies(0.f) , block_size(0) , num_uploads(0) , num_connections(0) , uploads_limit(0) , connections_limit(0) , up_bandwidth_queue(0) , down_bandwidth_queue(0) , time_since_upload(0) , time_since_download(0) , active_time(0) , finished_time(0) , seeding_time(0) , seed_rank(0) , last_scrape(0) , priority(0) , state(checking_resume_data) , need_save_resume(false) , ip_filter_applies(true) , upload_mode(false) , share_mode(false) , super_seeding(false) , paused(false) , auto_managed(false) , sequential_download(false) , is_seeding(false) , is_finished(false) , has_metadata(false) , has_incoming(false) , seed_mode(false) , moving_storage(false) , is_loaded(true) , announcing_to_trackers(false) , announcing_to_lsd(false) , announcing_to_dht(false) , stop_when_ready(false) , info_hash(0) {} torrent_status::~torrent_status() {} } libtorrent-rasterbar-1.1.13/src/tracker_manager.cpp000066400000000000000000000315261351156116000223720ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/tracker_manager.hpp" #include "libtorrent/http_tracker_connection.hpp" #include "libtorrent/udp_tracker_connection.hpp" #include "libtorrent/aux_/session_impl.hpp" #ifdef TORRENT_USE_OPENSSL #include #endif using boost::tuples::make_tuple; using boost::tuples::tuple; namespace { enum { minimum_tracker_response_length = 3, http_buffer_size = 2048 }; } namespace libtorrent { timeout_handler::timeout_handler(io_service& ios) : m_completion_timeout(0) , m_start_time(clock_type::now()) , m_read_time(m_start_time) , m_timeout(ios) , m_read_timeout(0) , m_abort(false) {} void timeout_handler::set_timeout(int completion_timeout, int read_timeout) { m_completion_timeout = completion_timeout; m_read_timeout = read_timeout; m_start_time = m_read_time = clock_type::now(); TORRENT_ASSERT(completion_timeout > 0 || read_timeout > 0); if (m_abort) return; int timeout = 0; if (m_read_timeout > 0) timeout = m_read_timeout; if (m_completion_timeout > 0) { timeout = timeout == 0 ? m_completion_timeout : (std::min)(m_completion_timeout, timeout); } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("timeout_handler::timeout_callback"); #endif error_code ec; m_timeout.expires_at(m_read_time + seconds(timeout), ec); m_timeout.async_wait(boost::bind( &timeout_handler::timeout_callback, shared_from_this(), _1)); } void timeout_handler::restart_read_timeout() { m_read_time = clock_type::now(); } void timeout_handler::cancel() { m_abort = true; m_completion_timeout = 0; error_code ec; m_timeout.cancel(ec); } void timeout_handler::timeout_callback(error_code const& error) { #if defined TORRENT_ASIO_DEBUGGING complete_async("timeout_handler::timeout_callback"); #endif if (m_abort) return; time_point now = clock_type::now(); time_duration receive_timeout = now - m_read_time; time_duration completion_timeout = now - m_start_time; if ((m_read_timeout && m_read_timeout <= total_seconds(receive_timeout)) || (m_completion_timeout && m_completion_timeout <= total_seconds(completion_timeout)) || error) { on_timeout(error); return; } int timeout = 0; if (m_read_timeout > 0) timeout = m_read_timeout; if (m_completion_timeout > 0) { timeout = timeout == 0 ? int(m_completion_timeout - total_seconds(m_read_time - m_start_time)) : (std::min)(int(m_completion_timeout - total_seconds(m_read_time - m_start_time)), timeout); } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("timeout_handler::timeout_callback"); #endif error_code ec; m_timeout.expires_at(m_read_time + seconds(timeout), ec); m_timeout.async_wait( boost::bind(&timeout_handler::timeout_callback, shared_from_this(), _1)); } tracker_connection::tracker_connection( tracker_manager& man , tracker_request const& req , io_service& ios , boost::weak_ptr r) : timeout_handler(ios) , m_req(req) , m_requester(r) , m_man(man) {} boost::shared_ptr tracker_connection::requester() const { return m_requester.lock(); } void tracker_connection::fail(error_code const& ec, int code , char const* msg, int interval, int min_interval) { // we need to post the error to avoid deadlock get_io_service().post(boost::bind(&tracker_connection::fail_impl , shared_from_this(), ec, code, std::string(msg), interval, min_interval)); } void tracker_connection::fail_impl(error_code const& ec, int code , std::string msg, int interval, int min_interval) { boost::shared_ptr cb = requester(); if (cb) cb->tracker_request_error(m_req, code, ec, msg.c_str() , interval == 0 ? min_interval : interval); close(); } void tracker_connection::sent_bytes(int bytes) { m_man.sent_bytes(bytes); } void tracker_connection::received_bytes(int bytes) { m_man.received_bytes(bytes); } void tracker_connection::close() { cancel(); m_man.remove_request(this); } // TODO: 2 some of these arguments could probably be moved to the // tracker request itself. like the ip_filter and settings tracker_manager::tracker_manager(class udp_socket& sock , counters& stats_counters , resolver_interface& resolver , aux::session_settings const& sett #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS , aux::session_logger& ses #endif ) : m_udp_socket(sock) , m_host_resolver(resolver) , m_settings(sett) , m_stats_counters(stats_counters) #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS , m_ses(ses) #endif , m_abort(false) {} tracker_manager::~tracker_manager() { TORRENT_ASSERT(m_abort); abort_all_requests(true); } void tracker_manager::sent_bytes(int bytes) { TORRENT_ASSERT(m_ses.is_single_thread()); m_stats_counters.inc_stats_counter(counters::sent_tracker_bytes, bytes); } void tracker_manager::received_bytes(int bytes) { TORRENT_ASSERT(m_ses.is_single_thread()); m_stats_counters.inc_stats_counter(counters::recv_tracker_bytes, bytes); } void tracker_manager::remove_request(tracker_connection const* c) { mutex_t::scoped_lock l(m_mutex); http_conns_t::iterator i = std::find_if(m_http_conns.begin() , m_http_conns.end() , boost::bind(&boost::shared_ptr::get, _1) == c); if (i != m_http_conns.end()) { m_http_conns.erase(i); return; } udp_conns_t::iterator j = std::find_if(m_udp_conns.begin() , m_udp_conns.end() , boost::bind(&boost::shared_ptr::get , boost::bind(&udp_conns_t::value_type::second, _1)) == c); if (j != m_udp_conns.end()) { m_udp_conns.erase(j); return; } } void tracker_manager::update_transaction_id( boost::shared_ptr c , boost::uint64_t tid) { m_udp_conns.erase(c->transaction_id()); m_udp_conns[tid] = c; } void tracker_manager::queue_request( io_service& ios , tracker_request req , boost::weak_ptr c) { mutex_t::scoped_lock l(m_mutex); TORRENT_ASSERT(req.num_want >= 0); TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped); if (m_abort && req.event != tracker_request::stopped) return; #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr cb = c.lock(); if (cb) cb->debug_log("*** QUEUE_TRACKER_REQUEST [ listen_port: %d ]" , req.listen_port); #endif std::string const protocol = req.url.substr(0, req.url.find(':')); #ifdef TORRENT_USE_OPENSSL if (protocol == "http" || protocol == "https") #else if (protocol == "http") #endif { boost::shared_ptr con = boost::make_shared( boost::ref(ios), boost::ref(*this), boost::cref(req), c); m_http_conns.push_back(con); con->start(); return; } else if (protocol == "udp") { boost::shared_ptr con = boost::make_shared( boost::ref(ios), boost::ref(*this), boost::cref(req) , c); m_udp_conns[con->transaction_id()] = con; con->start(); return; } // we need to post the error to avoid deadlock if (boost::shared_ptr r = c.lock()) ios.post(boost::bind(&request_callback::tracker_request_error, r, req , -1, error_code(errors::unsupported_url_protocol) , "", 0)); } bool tracker_manager::incoming_packet(error_code const& e , udp::endpoint const& ep, char const* buf, int size) { // ignore packets smaller than 8 bytes if (size < 8) { #ifndef TORRENT_DISABLE_LOGGING m_ses.session_log("incoming packet from %s, not a UDP tracker message " "(%d Bytes)", print_endpoint(ep).c_str(), size); #endif return false; } // the first word is the action, if it's not [0, 3] // it's not a valid udp tracker response const char* ptr = buf; boost::uint32_t action = detail::read_uint32(ptr); if (action > 3) return false; boost::uint32_t transaction = detail::read_uint32(ptr); udp_conns_t::iterator i = m_udp_conns.find(transaction); if (i == m_udp_conns.end()) { #ifndef TORRENT_DISABLE_LOGGING m_ses.session_log("incoming UDP tracker packet from %s has invalid " "transaction ID (%" PRIu32 ")", print_endpoint(ep).c_str() , transaction); #endif return false; } boost::shared_ptr p = i->second; // on_receive() may remove the tracker connection from the list return p->on_receive(e, ep, buf, size); } bool tracker_manager::incoming_packet(error_code const& e , char const* hostname, char const* buf, int size) { // ignore packets smaller than 8 bytes if (size < 16) return false; // the first word is the action, if it's not [0, 3] // it's not a valid udp tracker response const char* ptr = buf; boost::uint32_t action = detail::read_uint32(ptr); if (action > 3) return false; boost::uint32_t transaction = detail::read_uint32(ptr); udp_conns_t::iterator i = m_udp_conns.find(transaction); if (i == m_udp_conns.end()) { #ifndef TORRENT_DISABLE_LOGGING // now, this may not have been meant to be a tracker response, // but chances are pretty good, so it's probably worth logging m_ses.session_log("incoming UDP tracker packet from %s has invalid " "transaction ID (%x)", hostname, int(transaction)); #endif return false; } boost::shared_ptr p = i->second; // on_receive() may remove the tracker connection from the list return p->on_receive_hostname(e, hostname, buf, size); } void tracker_manager::abort_all_requests(bool all) { // removes all connections except 'event=stopped'-requests mutex_t::scoped_lock l(m_mutex); m_abort = true; http_conns_t close_http_connections; std::vector > close_udp_connections; for (http_conns_t::iterator i = m_http_conns.begin() , end(m_http_conns.end()); i != end; ++i) { http_tracker_connection* c = i->get(); tracker_request const& req = c->tracker_req(); if (req.event == tracker_request::stopped && !all) continue; close_http_connections.push_back(*i); #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr rc = c->requester(); if (rc) rc->debug_log("aborting: %s", req.url.c_str()); #endif } for (udp_conns_t::iterator i = m_udp_conns.begin() , end(m_udp_conns.end()); i != end; ++i) { boost::shared_ptr c = i->second; tracker_request const& req = c->tracker_req(); if (req.event == tracker_request::stopped && !all) continue; close_udp_connections.push_back(c); #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr rc = c->requester(); if (rc) rc->debug_log("aborting: %s", req.url.c_str()); #endif } l.unlock(); for (http_conns_t::iterator i = close_http_connections.begin() , end(close_http_connections.end()); i != end; ++i) { (*i)->close(); } for (std::vector >::iterator i = close_udp_connections.begin() , end(close_udp_connections.end()); i != end; ++i) { (*i)->close(); } } bool tracker_manager::empty() const { mutex_t::scoped_lock l(m_mutex); return m_http_conns.empty() && m_udp_conns.empty(); } int tracker_manager::num_requests() const { mutex_t::scoped_lock l(m_mutex); return m_http_conns.size() + m_udp_conns.size(); } } libtorrent-rasterbar-1.1.13/src/udp_socket.cpp000066400000000000000000001071151351156116000214030ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/config.hpp" #include "libtorrent/assert.hpp" // for print_backtrace #include "libtorrent/socket.hpp" #include "libtorrent/udp_socket.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/error.hpp" #include "libtorrent/string_util.hpp" // for allocate_string_copy #include "libtorrent/broadcast_socket.hpp" // for is_any #include "libtorrent/settings_pack.hpp" #include "libtorrent/error.hpp" #include "libtorrent/aux_/time.hpp" // for aux::time_now() #include #include #include #include #include #include #include #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/debug.hpp" #endif using namespace libtorrent; udp_socket::udp_socket(io_service& ios) : m_observers_locked(false) , m_ipv4_sock(ios) , m_timer(ios) , m_buf_size(0) , m_new_buf_size(0) , m_buf(0) #if TORRENT_USE_IPV6 , m_ipv6_sock(ios) #endif , m_bind_port(0) , m_v4_outstanding(0) , m_restart_v4(0) #if TORRENT_USE_IPV6 , m_v6_outstanding(0) , m_restart_v6(false) #endif , m_socks5_sock(ios) , m_retry_timer(ios) , m_resolver(ios) , m_queue_packets(false) , m_tunnel_packets(false) , m_force_proxy(false) , m_abort(true) , m_outstanding_ops(0) #if TORRENT_USE_IPV6 , m_v6_write_subscribed(false) #endif , m_v4_write_subscribed(false) { #if TORRENT_USE_ASSERTS m_magic = 0x1337; m_started = false; m_outstanding_when_aborted = -1; m_outstanding_connect = 0; m_outstanding_timeout = 0; m_outstanding_resolve = 0; m_outstanding_socks = 0; #endif m_buf_size = 2048; m_new_buf_size = m_buf_size; m_buf = static_cast(std::malloc(m_buf_size)); } udp_socket::~udp_socket() { for (std::deque::iterator i = m_queue.begin() , end(m_queue.end()); i != end; ++i) { if (i->hostname) std::free(i->hostname); } std::free(m_buf); #if TORRENT_USE_IPV6 TORRENT_ASSERT_VAL(m_v6_outstanding == 0, m_v6_outstanding); #endif TORRENT_ASSERT_VAL(m_v4_outstanding == 0, m_v4_outstanding); TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(m_observers_locked == false); #if TORRENT_USE_ASSERTS m_magic = 0; #endif TORRENT_ASSERT(m_outstanding_ops == 0); } #if TORRENT_USE_ASSERTS #define CHECK_MAGIC check_magic_ cm_(m_magic) struct check_magic_ { check_magic_(int& m_): m(m_) { TORRENT_ASSERT(m == 0x1337); } ~check_magic_() { TORRENT_ASSERT(m == 0x1337); } int& m; }; #else #define CHECK_MAGIC do {} TORRENT_WHILE_0 #endif void udp_socket::send_hostname(char const* hostname, int port , char const* p, int len, error_code& ec, int flags) { CHECK_MAGIC; TORRENT_ASSERT(is_single_thread()); // if the sockets are closed, the udp_socket is closing too if (!is_open()) { ec = error_code(boost::system::errc::bad_file_descriptor, generic_category()); return; } if (m_tunnel_packets) { // send udp packets through SOCKS5 server wrap(hostname, port, p, len, ec); return; } // this function is only supported when we're using a proxy if (!m_queue_packets && !m_force_proxy) { address target = address::from_string(hostname, ec); if (!ec) send(udp::endpoint(target, port), p, len, ec, 0); return; } if (m_queue.size() > 1000 || (flags & dont_queue)) return; m_queue.push_back(queued_packet()); queued_packet& qp = m_queue.back(); qp.ep.port(port); address target = address::from_string(hostname, ec); if (ec) qp.ep.address(target); else qp.hostname = allocate_string_copy(hostname); qp.buf.insert(qp.buf.begin(), p, p + len); qp.flags = 0; } void udp_socket::send(udp::endpoint const& ep, char const* p, int len , error_code& ec, int flags) { CHECK_MAGIC; TORRENT_ASSERT(is_single_thread()); // if the sockets are closed, the udp_socket is closing too if (!is_open()) { ec = error_code(boost::system::errc::bad_file_descriptor, generic_category()); return; } const bool allow_proxy = ((flags & peer_connection) && m_proxy_settings.proxy_peer_connections) || ((flags & tracker_connection) && m_proxy_settings.proxy_tracker_connections) || (flags & (tracker_connection | peer_connection)) == 0 ; if (allow_proxy) { if (m_tunnel_packets) { // send udp packets through SOCKS5 server wrap(ep, p, len, ec); return; } if (m_queue_packets) { if (m_queue.size() > 1000 || (flags & dont_queue)) return; m_queue.push_back(queued_packet()); queued_packet& qp = m_queue.back(); qp.ep = ep; qp.hostname = 0; qp.flags = flags; qp.buf.insert(qp.buf.begin(), p, p + len); return; } } if (m_force_proxy) return; #if TORRENT_USE_IPV6 if (ep.address().is_v6() && m_ipv6_sock.is_open()) m_ipv6_sock.send_to(boost::asio::buffer(p, len), ep, 0, ec); else #endif m_ipv4_sock.send_to(boost::asio::buffer(p, len), ep, 0, ec); if (ec == error::would_block || ec == error::try_again) { #if TORRENT_USE_IPV6 if (ep.address().is_v6() && m_ipv6_sock.is_open()) { if (!m_v6_write_subscribed) { m_ipv6_sock.async_send(null_buffers() , boost::bind(&udp_socket::on_writable, this, _1, &m_ipv6_sock)); m_v6_write_subscribed = true; } } else #endif { if (!m_v4_write_subscribed) { m_ipv4_sock.async_send(null_buffers() , boost::bind(&udp_socket::on_writable, this, _1, &m_ipv4_sock)); m_v4_write_subscribed = true; } } } } void udp_socket::on_writable(error_code const& ec, udp::socket* s) { #if TORRENT_USE_IPV6 if (s == &m_ipv6_sock) m_v6_write_subscribed = false; else #else TORRENT_UNUSED(s); #endif m_v4_write_subscribed = false; if (ec == boost::asio::error::operation_aborted) return; call_writable_handler(); } // called whenever the socket is readable void udp_socket::on_read(error_code const& ec, udp::socket* s) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::on_read"); #endif TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(is_single_thread()); #if TORRENT_USE_IPV6 if (s == &m_ipv6_sock) { TORRENT_ASSERT(m_v6_outstanding > 0); --m_v6_outstanding; } else #else TORRENT_UNUSED(s); #endif { TORRENT_ASSERT(m_v4_outstanding > 0); --m_v4_outstanding; } if (ec == boost::asio::error::operation_aborted) { #if TORRENT_USE_IPV6 if (s == &m_ipv6_sock) { if (m_restart_v6) { --m_restart_v6; setup_read(s); } } else #endif { if (m_restart_v4) { --m_restart_v4; setup_read(s); } } return; } if (m_abort) { close_impl(); return; } CHECK_MAGIC; for (;;) { error_code err; udp::endpoint ep; size_t bytes_transferred = s->receive_from(boost::asio::buffer(m_buf, m_buf_size), ep, 0, err); // TODO: it would be nice to detect this on posix systems also #ifdef TORRENT_WINDOWS if ((err == error_code(ERROR_MORE_DATA, system_category()) || err == error_code(WSAEMSGSIZE, system_category())) && m_buf_size < 65536) { // if this function fails to allocate memory, m_buf_size // is set to 0. In that case, don't issue the async_read(). set_buf_size(m_buf_size * 2); if (m_buf_size == 0) return; continue; } #endif if (err == boost::asio::error::would_block || err == boost::asio::error::try_again) break; on_read_impl(ep, err, bytes_transferred); // found on iOS, socket will be disconnected when app goes backgroud. try to reopen it. if (err == boost::asio::error::not_connected || err == boost::asio::error::bad_descriptor) { ep = s->local_endpoint(err); if (!err) { bind(ep, err); } return; } } call_drained_handler(); setup_read(s); } void udp_socket::call_handler(error_code const& ec, udp::endpoint const& ep, char const* buf, int size) { m_observers_locked = true; for (std::vector::iterator i = m_observers.begin(); i != m_observers.end();) { bool ret = false; TORRENT_TRY { ret = (*i)->incoming_packet(ec, ep, buf, size); } TORRENT_CATCH (std::exception&) {} if (*i == NULL) i = m_observers.erase(i); else ++i; if (ret) break; } if (!m_added_observers.empty()) { m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); m_added_observers.clear(); } m_observers_locked = false; if (m_new_buf_size != m_buf_size) set_buf_size(m_new_buf_size); } void udp_socket::call_handler(error_code const& ec, const char* host, char const* buf, int size) { m_observers_locked = true; for (std::vector::iterator i = m_observers.begin(); i != m_observers.end();) { bool ret = false; TORRENT_TRY { ret = (*i)->incoming_packet(ec, host, buf, size); } TORRENT_CATCH (std::exception&) {} if (*i == NULL) i = m_observers.erase(i); else ++i; if (ret) break; } if (!m_added_observers.empty()) { m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); m_added_observers.clear(); } m_observers_locked = false; if (m_new_buf_size != m_buf_size) set_buf_size(m_new_buf_size); } void udp_socket::call_drained_handler() { m_observers_locked = true; for (std::vector::iterator i = m_observers.begin(); i != m_observers.end();) { TORRENT_TRY { (*i)->socket_drained(); } TORRENT_CATCH (std::exception&) {} if (*i == NULL) i = m_observers.erase(i); else ++i; } if (!m_added_observers.empty()) { m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); m_added_observers.clear(); } m_observers_locked = false; if (m_new_buf_size != m_buf_size) set_buf_size(m_new_buf_size); } void udp_socket::call_writable_handler() { m_observers_locked = true; for (std::vector::iterator i = m_observers.begin(); i != m_observers.end();) { TORRENT_TRY { (*i)->writable(); } TORRENT_CATCH (std::exception&) {} if (*i == NULL) i = m_observers.erase(i); else ++i; } if (!m_added_observers.empty()) { m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); m_added_observers.clear(); } m_observers_locked = false; if (m_new_buf_size != m_buf_size) set_buf_size(m_new_buf_size); } void udp_socket::subscribe(udp_socket_observer* o) { TORRENT_ASSERT(std::find(m_observers.begin(), m_observers.end(), o) == m_observers.end()); if (m_observers_locked) m_added_observers.push_back(o); else m_observers.push_back(o); } void udp_socket::unsubscribe(udp_socket_observer* o) { std::vector::iterator i = std::find(m_observers.begin(), m_observers.end(), o); if (i == m_observers.end()) return; if (m_observers_locked) *i = NULL; else m_observers.erase(i); } void udp_socket::on_read_impl(udp::endpoint const& ep , error_code const& e, std::size_t bytes_transferred) { TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(is_single_thread()); if (e) { call_handler(e, ep, 0, 0); // don't stop listening on recoverable errors if (e != boost::asio::error::host_unreachable && e != boost::asio::error::fault && e != boost::asio::error::connection_reset && e != boost::asio::error::connection_refused && e != boost::asio::error::connection_aborted && e != boost::asio::error::operation_aborted && e != boost::asio::error::network_reset && e != boost::asio::error::network_unreachable #ifdef _WIN32 // ERROR_MORE_DATA means the same thing as EMSGSIZE && e != error_code(ERROR_MORE_DATA, system_category()) && e != error_code(ERROR_HOST_UNREACHABLE, system_category()) && e != error_code(ERROR_PORT_UNREACHABLE, system_category()) && e != error_code(ERROR_RETRY, system_category()) && e != error_code(ERROR_NETWORK_UNREACHABLE, system_category()) && e != error_code(ERROR_CONNECTION_REFUSED, system_category()) && e != error_code(ERROR_CONNECTION_ABORTED, system_category()) #endif && e != boost::asio::error::message_size) { return; } if (m_abort) { close_impl(); return; } return; } TORRENT_TRY { if (m_tunnel_packets) { // if the source IP doesn't match the proxy's, ignore the packet if (ep == m_udp_proxy_addr) unwrap(e, m_buf, bytes_transferred); } else if (!m_force_proxy) // block incoming packets that aren't coming via the proxy { call_handler(e, ep, m_buf, bytes_transferred); } } TORRENT_CATCH (std::exception&) {} } void udp_socket::setup_read(udp::socket* s) { if (m_abort) { close_impl(); return; } #if TORRENT_USE_IPV6 if (s == &m_ipv6_sock) { if (m_v6_outstanding) { ++m_restart_v6; m_ipv6_sock.cancel(); return; } ++m_v6_outstanding; } else #endif { if (m_v4_outstanding) { ++m_restart_v4; m_ipv4_sock.cancel(); return; } ++m_v4_outstanding; } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::on_read"); #endif udp::endpoint ep; TORRENT_TRY { #if TORRENT_USE_IPV6 if (s == &m_ipv6_sock) { s->async_receive_from(null_buffers() , ep, make_read_handler6(boost::bind(&udp_socket::on_read, this, _1, s))); } else #endif { s->async_receive_from(null_buffers() , ep, make_read_handler4(boost::bind(&udp_socket::on_read, this, _1, s))); } } TORRENT_CATCH(boost::system::system_error& e) { #ifdef BOOST_NO_EXCEPTIONS // dummy error_code ec; boost::system::system_error e(ec); #endif on_read_impl(udp::endpoint(), e.code(), 0); m_abort = true; close(); return; } } void udp_socket::wrap(udp::endpoint const& ep, char const* p, int len, error_code& ec) { CHECK_MAGIC; using namespace libtorrent::detail; char header[25]; char* h = header; write_uint16(0, h); // reserved write_uint8(0, h); // fragment write_uint8(ep.address().is_v4()?1:4, h); // atyp write_endpoint(ep, h); boost::array iovec; iovec[0] = boost::asio::const_buffer(header, h - header); iovec[1] = boost::asio::const_buffer(p, len); #if TORRENT_USE_IPV6 if (m_udp_proxy_addr.address().is_v4() && m_ipv4_sock.is_open()) #endif m_ipv4_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); #if TORRENT_USE_IPV6 else m_ipv6_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); #endif } void udp_socket::wrap(char const* hostname, int port, char const* p, int len, error_code& ec) { CHECK_MAGIC; using namespace libtorrent::detail; char header[270]; char* h = header; write_uint16(0, h); // reserved write_uint8(0, h); // fragment write_uint8(3, h); // atyp int hostlen = (std::min)(strlen(hostname), size_t(255)); write_uint8(hostlen, h); // hostname len memcpy(h, hostname, hostlen); h += hostlen; write_uint16(port, h); boost::array iovec; iovec[0] = boost::asio::const_buffer(header, h - header); iovec[1] = boost::asio::const_buffer(p, len); #if TORRENT_USE_IPV6 if (m_udp_proxy_addr.address().is_v6() && m_ipv6_sock.is_open()) m_ipv6_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); else #endif m_ipv4_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); } // unwrap the UDP packet from the SOCKS5 header void udp_socket::unwrap(error_code const& e, char const* buf, int size) { CHECK_MAGIC; using namespace libtorrent::detail; // the minimum socks5 header size if (size <= 10) return; char const* p = buf; p += 2; // reserved int frag = read_uint8(p); // fragmentation is not supported if (frag != 0) return; udp::endpoint sender; int atyp = read_uint8(p); if (atyp == 1) { // IPv4 sender = read_v4_endpoint(p); } #if TORRENT_USE_IPV6 else if (atyp == 4) { // IPv6 sender = read_v6_endpoint(p); } #endif else { int len = read_uint8(p); if (len > (buf + size) - p) return; std::string hostname(p, p + len); p += len; call_handler(e, hostname.c_str(), p, size - (p - buf)); return; } call_handler(e, sender, p, size - (p - buf)); } #if !defined BOOST_ASIO_ENABLE_CANCELIO && defined TORRENT_WINDOWS #error BOOST_ASIO_ENABLE_CANCELIO needs to be defined when building libtorrent to enable cancel() in asio on windows #endif void udp_socket::close() { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(m_magic == 0x1337); error_code ec; m_ipv4_sock.close(ec); TORRENT_ASSERT_VAL(!ec || ec == error::bad_descriptor, ec); #if TORRENT_USE_IPV6 m_ipv6_sock.close(ec); TORRENT_ASSERT_VAL(!ec || ec == error::bad_descriptor, ec); #endif m_socks5_sock.close(ec); TORRENT_ASSERT_VAL(!ec || ec == error::bad_descriptor, ec); m_resolver.cancel(); m_timer.cancel(); m_abort = true; #if TORRENT_USE_ASSERTS m_outstanding_when_aborted = num_outstanding(); #endif } void udp_socket::set_buf_size(int s) { TORRENT_ASSERT(is_single_thread()); if (m_observers_locked) { // we can't actually reallocate the buffer while // it's being used by the observers, we have to // do that once we're done iterating over them m_new_buf_size = s; return; } if (s == m_buf_size) return; bool no_mem = false; char* tmp = static_cast(realloc(m_buf, s)); if (tmp != 0) { m_buf = tmp; m_buf_size = s; m_new_buf_size = s; } else { no_mem = true; } if (no_mem) { std::free(m_buf); m_buf = 0; m_buf_size = 0; m_new_buf_size = 0; udp::endpoint ep; call_handler(error::no_memory, ep, 0, 0); close(); } int size = m_buf_size; // don't shrink the size of the receive buffer error_code ec; boost::asio::socket_base::receive_buffer_size recv_size; m_ipv4_sock.get_option(recv_size, ec); if (!ec) size = (std::max)(recv_size.value(), size); #if TORRENT_USE_IPV6 m_ipv6_sock.get_option(recv_size, ec); if (!ec) size = (std::max)(recv_size.value(), size); #endif error_code ignore_errors; // set the internal buffer sizes as well m_ipv4_sock.set_option(boost::asio::socket_base::receive_buffer_size(size) , ignore_errors); #if TORRENT_USE_IPV6 m_ipv6_sock.set_option(boost::asio::socket_base::receive_buffer_size(size) , ignore_errors); #endif } void udp_socket::bind(udp::endpoint const& ep, error_code& ec) { CHECK_MAGIC; TORRENT_ASSERT(is_single_thread()); m_abort = false; if (m_ipv4_sock.is_open()) m_ipv4_sock.close(ec); #if TORRENT_USE_IPV6 if (m_ipv6_sock.is_open()) m_ipv6_sock.close(ec); #endif ec.clear(); if (ep.address().is_v4()) { m_ipv4_sock.open(udp::v4(), ec); if (ec) return; // this is best-effort. ignore errors error_code err; #ifdef TORRENT_WINDOWS m_ipv4_sock.set_option(exclusive_address_use(true), err); #else m_ipv4_sock.set_option(boost::asio::socket_base::reuse_address(true), err); #endif m_ipv4_sock.bind(ep, ec); if (ec) return; m_ipv4_sock.non_blocking(true, ec); if (ec) return; setup_read(&m_ipv4_sock); } #if TORRENT_USE_IPV6 // TODO: 2 the udp_socket should really just be a single socket, and the // session should support having more than one, just like with TCP sockets // for now, just make bind failures non-fatal if (supports_ipv6() && (ep.address().is_v6() || is_any(ep.address()))) { udp::endpoint ep6 = ep; if (is_any(ep.address())) ep6.address(address_v6::any()); m_ipv6_sock.open(udp::v6(), ec); if (ec) return; // this is best-effort. ignore errors error_code err; #ifdef TORRENT_WINDOWS m_ipv6_sock.set_option(exclusive_address_use(true), err); #else m_ipv6_sock.set_option(boost::asio::socket_base::reuse_address(true), err); #endif m_ipv6_sock.set_option(boost::asio::ip::v6_only(true), err); m_ipv6_sock.bind(ep6, ec); if (ec != error_code(boost::system::errc::address_not_available , boost::system::generic_category())) { if (ec) return; m_ipv6_sock.non_blocking(true, ec); if (ec) return; setup_read(&m_ipv6_sock); } else { ec.clear(); } } #endif error_code err; m_bind_port = m_ipv4_sock.local_endpoint(err).port(); if (err) m_bind_port = ep.port(); #if TORRENT_USE_ASSERTS m_started = true; #endif } void udp_socket::set_proxy_settings(aux::proxy_settings const& ps) { CHECK_MAGIC; TORRENT_ASSERT(is_single_thread()); error_code ec; m_socks5_sock.close(ec); m_tunnel_packets = false; m_proxy_settings = ps; if (m_abort) { close_impl(); return; } if (ps.type == settings_pack::socks5 || ps.type == settings_pack::socks5_pw) { m_queue_packets = true; // connect to socks5 server and open up the UDP tunnel // TODO: use the system resolver_interface here tcp::resolver::query q(ps.hostname, to_string(ps.port).elems); ++m_outstanding_ops; #if TORRENT_USE_ASSERTS ++m_outstanding_resolve; #endif #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::on_name_lookup"); #endif m_resolver.async_resolve(q, boost::bind( &udp_socket::on_name_lookup, this, _1, _2)); } } void udp_socket::close_impl() { if (m_outstanding_ops == 0) { error_code ec; m_ipv4_sock.close(ec); #if TORRENT_USE_IPV6 m_ipv6_sock.close(ec); #endif m_socks5_sock.close(ec); } } void udp_socket::on_name_lookup(error_code const& e, tcp::resolver::iterator i) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::on_name_lookup"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_outstanding_resolve > 0); --m_outstanding_resolve; #endif TORRENT_ASSERT(m_outstanding_ops > 0); --m_outstanding_ops; TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); if (m_abort) { close_impl(); return; } CHECK_MAGIC; if (e == boost::asio::error::operation_aborted) return; TORRENT_ASSERT(is_single_thread()); if (e) { if (m_force_proxy) { call_handler(e, udp::endpoint(), 0, 0); } else { // if we can't connect to the proxy, and // we're not in privacy mode, try to just // not use a proxy m_proxy_settings = aux::proxy_settings(); m_tunnel_packets = false; } drain_queue(); return; } m_proxy_addr.address(i->endpoint().address()); m_proxy_addr.port(i->endpoint().port()); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::on_connected"); #endif error_code ec; m_socks5_sock.open(m_proxy_addr.address().is_v4()?tcp::v4():tcp::v6(), ec); // enable keepalives m_socks5_sock.set_option(boost::asio::socket_base::keep_alive(true), ec); ++m_outstanding_ops; #if TORRENT_USE_ASSERTS ++m_outstanding_connect; #endif m_socks5_sock.async_connect(tcp::endpoint(m_proxy_addr.address(), m_proxy_addr.port()) , boost::bind(&udp_socket::on_connected, this, _1)); ++m_outstanding_ops; #if TORRENT_USE_ASSERTS ++m_outstanding_timeout; #endif #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::on_connect_timeout"); #endif m_timer.expires_from_now(seconds(10)); m_timer.async_wait(boost::bind(&udp_socket::on_connect_timeout , this, _1)); } void udp_socket::on_connect_timeout(error_code const& ec) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::on_connect_timeout"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_outstanding_timeout > 0); --m_outstanding_timeout; #endif TORRENT_ASSERT(m_outstanding_ops > 0); --m_outstanding_ops; TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); if (ec == boost::asio::error::operation_aborted) return; m_queue_packets = false; if (m_abort) { close_impl(); return; } CHECK_MAGIC; TORRENT_ASSERT(is_single_thread()); error_code ignore; m_socks5_sock.close(ignore); } void udp_socket::on_connected(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::on_connected"); #endif TORRENT_ASSERT(is_single_thread()); #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_outstanding_connect > 0); --m_outstanding_connect; #endif TORRENT_ASSERT(m_outstanding_ops > 0); --m_outstanding_ops; TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); CHECK_MAGIC; m_timer.cancel(); if (e == boost::asio::error::operation_aborted) return; if (m_abort) { close_impl(); return; } if (e) { // we failed to connect to the proxy, if we don't have force_proxy set, // drain the queue over the UDP socket if (!m_force_proxy) { drain_queue(); } call_handler(e, udp::endpoint(), 0, 0); return; } using namespace libtorrent::detail; // send SOCKS5 authentication methods char* p = &m_tmp_buf[0]; write_uint8(5, p); // SOCKS VERSION 5 if (m_proxy_settings.username.empty() || m_proxy_settings.type == settings_pack::socks5) { write_uint8(1, p); // 1 authentication method (no auth) write_uint8(0, p); // no authentication } else { write_uint8(2, p); // 2 authentication methods write_uint8(0, p); // no authentication write_uint8(2, p); // username/password } TORRENT_ASSERT_VAL(p - m_tmp_buf < int(sizeof(m_tmp_buf)), (p - m_tmp_buf)); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::on_handshake1"); #endif ++m_outstanding_ops; #if TORRENT_USE_ASSERTS ++m_outstanding_socks; #endif boost::asio::async_write(m_socks5_sock, boost::asio::buffer(m_tmp_buf, p - m_tmp_buf) , boost::bind(&udp_socket::handshake1, this, _1)); } void udp_socket::handshake1(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::on_handshake1"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_outstanding_socks > 0); --m_outstanding_socks; #endif TORRENT_ASSERT(m_outstanding_ops > 0); --m_outstanding_ops; TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); if (m_abort) { close_impl(); return; } CHECK_MAGIC; if (e) { drain_queue(); return; } TORRENT_ASSERT(is_single_thread()); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::on_handshake2"); #endif ++m_outstanding_ops; #if TORRENT_USE_ASSERTS ++m_outstanding_socks; #endif boost::asio::async_read(m_socks5_sock, boost::asio::buffer(m_tmp_buf, 2) , boost::bind(&udp_socket::handshake2, this, _1)); } void udp_socket::handshake2(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::on_handshake2"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_outstanding_socks > 0); --m_outstanding_socks; #endif TORRENT_ASSERT(m_outstanding_ops > 0); --m_outstanding_ops; TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); if (m_abort) { close_impl(); return; } CHECK_MAGIC; if (e) { drain_queue(); return; } using namespace libtorrent::detail; TORRENT_ASSERT(is_single_thread()); char* p = &m_tmp_buf[0]; int version = read_uint8(p); int method = read_uint8(p); if (version < 5) { error_code ec; m_socks5_sock.close(ec); drain_queue(); return; } if (method == 0) { socks_forward_udp(/*l*/); } else if (method == 2) { if (m_proxy_settings.username.empty()) { error_code ec; m_socks5_sock.close(ec); drain_queue(); return; } // start sub-negotiation p = &m_tmp_buf[0]; write_uint8(1, p); write_uint8(m_proxy_settings.username.size(), p); write_string(m_proxy_settings.username, p); write_uint8(m_proxy_settings.password.size(), p); write_string(m_proxy_settings.password, p); TORRENT_ASSERT_VAL(p - m_tmp_buf < int(sizeof(m_tmp_buf)), (p - m_tmp_buf)); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::on_handshake3"); #endif ++m_outstanding_ops; #if TORRENT_USE_ASSERTS ++m_outstanding_socks; #endif boost::asio::async_write(m_socks5_sock, boost::asio::buffer(m_tmp_buf, p - m_tmp_buf) , boost::bind(&udp_socket::handshake3, this, _1)); } else { drain_queue(); error_code ec; m_socks5_sock.close(ec); return; } } void udp_socket::handshake3(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::on_handshake3"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_outstanding_socks > 0); --m_outstanding_socks; #endif TORRENT_ASSERT(m_outstanding_ops > 0); --m_outstanding_ops; TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); if (m_abort) { close_impl(); return; } CHECK_MAGIC; if (e) { drain_queue(); return; } TORRENT_ASSERT(is_single_thread()); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::on_handshake4"); #endif ++m_outstanding_ops; #if TORRENT_USE_ASSERTS ++m_outstanding_socks; #endif boost::asio::async_read(m_socks5_sock, boost::asio::buffer(m_tmp_buf, 2) , boost::bind(&udp_socket::handshake4, this, _1)); } void udp_socket::handshake4(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::on_handshake4"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_outstanding_socks > 0); --m_outstanding_socks; #endif TORRENT_ASSERT(m_outstanding_ops > 0); --m_outstanding_ops; TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); if (m_abort) { close_impl(); return; } CHECK_MAGIC; if (e) { drain_queue(); return; } TORRENT_ASSERT(is_single_thread()); using namespace libtorrent::detail; char* p = &m_tmp_buf[0]; int version = read_uint8(p); int status = read_uint8(p); if (version != 1 || status != 0) { drain_queue(); return; } socks_forward_udp(/*l*/); } void udp_socket::socks_forward_udp() { CHECK_MAGIC; using namespace libtorrent::detail; // send SOCKS5 UDP command char* p = &m_tmp_buf[0]; write_uint8(5, p); // SOCKS VERSION 5 write_uint8(3, p); // UDP ASSOCIATE command write_uint8(0, p); // reserved error_code ec; write_uint8(1, p); // ATYP = IPv4 write_uint32(0, p); // 0.0.0.0 write_uint16(0, p); // :0 TORRENT_ASSERT_VAL(p - m_tmp_buf < int(sizeof(m_tmp_buf)), (p - m_tmp_buf)); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::connect1"); #endif ++m_outstanding_ops; #if TORRENT_USE_ASSERTS ++m_outstanding_socks; #endif boost::asio::async_write(m_socks5_sock, boost::asio::buffer(m_tmp_buf, p - m_tmp_buf) , boost::bind(&udp_socket::connect1, this, _1)); } void udp_socket::connect1(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::connect1"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_outstanding_socks > 0); --m_outstanding_socks; #endif TORRENT_ASSERT(m_outstanding_ops > 0); --m_outstanding_ops; TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); if (m_abort) { close_impl(); return; } CHECK_MAGIC; if (e) { drain_queue(); return; } TORRENT_ASSERT(is_single_thread()); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::connect2"); #endif ++m_outstanding_ops; #if TORRENT_USE_ASSERTS ++m_outstanding_socks; #endif boost::asio::async_read(m_socks5_sock, boost::asio::buffer(m_tmp_buf, 10) , boost::bind(&udp_socket::connect2, this, _1)); } void udp_socket::connect2(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::connect2"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_outstanding_socks > 0); --m_outstanding_socks; #endif TORRENT_ASSERT(m_outstanding_ops > 0); --m_outstanding_ops; TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); if (m_abort) { m_queue.clear(); return; } CHECK_MAGIC; if (e) { drain_queue(); return; } TORRENT_ASSERT(is_single_thread()); using namespace libtorrent::detail; char* p = &m_tmp_buf[0]; int version = read_uint8(p); // VERSION int status = read_uint8(p); // STATUS ++p; // RESERVED int atyp = read_uint8(p); // address type if (version != 5 || status != 0) { drain_queue(); return; } if (atyp == 1) { m_udp_proxy_addr.address(address_v4(read_uint32(p))); m_udp_proxy_addr.port(read_uint16(p)); } else { // in this case we need to read more data from the socket TORRENT_ASSERT(false); drain_queue(); return; } m_tunnel_packets = true; drain_queue(); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::hung_up"); #endif ++m_outstanding_ops; #if TORRENT_USE_ASSERTS ++m_outstanding_socks; #endif boost::asio::async_read(m_socks5_sock, boost::asio::buffer(m_tmp_buf, 10) , boost::bind(&udp_socket::hung_up, this, _1)); } void udp_socket::hung_up(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_socket::hung_up"); #endif #if TORRENT_USE_ASSERTS TORRENT_ASSERT(m_outstanding_socks > 0); --m_outstanding_socks; #endif TORRENT_ASSERT(m_outstanding_ops > 0); --m_outstanding_ops; TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); if (m_abort) { close_impl(); return; } CHECK_MAGIC; TORRENT_ASSERT(is_single_thread()); if (e == boost::asio::error::operation_aborted || m_abort) return; // the socks connection was closed, re-open it in a bit m_retry_timer.expires_from_now(seconds(5)); m_retry_timer.async_wait(boost::bind(&udp_socket::retry_socks_connect , this, _1)); } void udp_socket::retry_socks_connect(error_code const& ec) { if (ec) return; set_proxy_settings(m_proxy_settings); } void udp_socket::drain_queue() { m_queue_packets = false; // forward all packets that were put in the queue while (!m_queue.empty()) { queued_packet const& p = m_queue.front(); error_code ec; if (p.hostname) { udp_socket::send_hostname(p.hostname, p.ep.port(), &p.buf[0] , p.buf.size(), ec, p.flags | dont_queue); std::free(p.hostname); } else { udp_socket::send(p.ep, &p.buf[0], p.buf.size(), ec, p.flags | dont_queue); } m_queue.pop_front(); } } rate_limited_udp_socket::rate_limited_udp_socket(io_service& ios) : udp_socket(ios) , m_rate_limit(8000) , m_quota(8000) , m_last_tick(aux::time_now()) { } bool rate_limited_udp_socket::has_quota() { time_point now = clock_type::now(); time_duration delta = now - m_last_tick; m_last_tick = now; // add any new quota we've accrued since last time m_quota += boost::uint64_t(m_rate_limit) * total_microseconds(delta) / 1000000; return m_quota > 0; } bool rate_limited_udp_socket::send(udp::endpoint const& ep, char const* p , int len, error_code& ec, int flags) { time_point now = clock_type::now(); time_duration delta = now - m_last_tick; m_last_tick = now; // add any new quota we've accrued since last time m_quota += boost::uint64_t(m_rate_limit) * total_microseconds(delta) / 1000000; // allow 3 seconds worth of burst if (m_quota > 3 * m_rate_limit) m_quota = 3 * m_rate_limit; // if there's no quota, and it's OK to drop, just // drop the packet if (m_quota < 0 && (flags & dont_drop) == 0) return false; m_quota -= len; if (m_quota < 0) m_quota = 0; udp_socket::send(ep, p, len, ec, flags); return true; } libtorrent-rasterbar-1.1.13/src/udp_tracker_connection.cpp000066400000000000000000000541401351156116000237640ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/tracker_manager.hpp" #include "libtorrent/parse_url.hpp" #include "libtorrent/udp_tracker_connection.hpp" #include "libtorrent/io.hpp" #include "libtorrent/hex.hpp" #include "libtorrent/broadcast_socket.hpp" // for is_any #include "libtorrent/random.hpp" #include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/resolver_interface.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/aux_/time.hpp" #ifndef TORRENT_DISABLE_LOGGING #include "libtorrent/socket_io.hpp" #endif namespace libtorrent { std::map udp_tracker_connection::m_connection_cache; mutex udp_tracker_connection::m_cache_mutex; udp_tracker_connection::udp_tracker_connection( io_service& ios , tracker_manager& man , tracker_request const& req , boost::weak_ptr c) : tracker_connection(man, req, ios, c) , m_transaction_id(0) , m_attempts(0) , m_state(action_error) , m_abort(false) { update_transaction_id(); } void udp_tracker_connection::start() { // TODO: 2 support authentication here. tracker_req().auth std::string hostname; std::string protocol; int port; error_code ec; using boost::tuples::ignore; boost::tie(protocol, ignore, hostname, port, ignore) = parse_url_components(tracker_req().url, ec); if (port == -1) port = protocol == "http" ? 80 : 443; if (ec) { tracker_connection::fail(ec); return; } aux::session_settings const& settings = m_man.settings(); if (settings.get_bool(settings_pack::proxy_hostnames) && (settings.get_int(settings_pack::proxy_type) == settings_pack::socks5 || settings.get_int(settings_pack::proxy_type) == settings_pack::socks5_pw)) { m_hostname = hostname; m_target.port(port); start_announce(); } else { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_tracker_connection::name_lookup"); #endif // when stopping, pass in the cache-only flag, because we // don't want to get stuck on DNS lookups when shutting down m_man.host_resolver().async_resolve(hostname , tracker_req().event == tracker_request::stopped ? resolver_interface::cache_only : 0 | resolver_interface::abort_on_shutdown , boost::bind(&udp_tracker_connection::name_lookup , shared_from_this(), _1, _2, port)); #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr cb = requester(); if (cb) cb->debug_log("*** UDP_TRACKER [ initiating name lookup: \"%s\" ]" , hostname.c_str()); #endif } set_timeout(tracker_req().event == tracker_request::stopped ? settings.get_int(settings_pack::stop_tracker_timeout) : settings.get_int(settings_pack::tracker_completion_timeout) , settings.get_int(settings_pack::tracker_receive_timeout)); } void udp_tracker_connection::fail(error_code const& ec, int code , char const* msg, int interval, int min_interval) { // m_target failed. remove it from the endpoint list std::vector::iterator i = std::find(m_endpoints.begin() , m_endpoints.end(), tcp::endpoint(m_target.address(), m_target.port())); if (i != m_endpoints.end()) m_endpoints.erase(i); // if that was the last one, fail the whole announce if (m_endpoints.empty()) { tracker_connection::fail(ec, code, msg, interval, min_interval); return; } #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr cb = requester(); if (cb) cb->debug_log("*** UDP_TRACKER [ host: \"%s\" ip: \"%s\" | error: \"%s\" ]" , m_hostname.c_str(), print_endpoint(m_target).c_str(), ec.message().c_str()); #endif // pick another target endpoint and try again m_target = pick_target_endpoint(); #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("*** UDP_TRACKER trying next IP [ host: \"%s\" ip: \"%s\" ]" , m_hostname.c_str(), print_endpoint(m_target).c_str()); #endif get_io_service().post(boost::bind( &udp_tracker_connection::start_announce, shared_from_this())); aux::session_settings const& settings = m_man.settings(); set_timeout(tracker_req().event == tracker_request::stopped ? settings.get_int(settings_pack::stop_tracker_timeout) : settings.get_int(settings_pack::tracker_completion_timeout) , settings.get_int(settings_pack::tracker_receive_timeout)); } void udp_tracker_connection::name_lookup(error_code const& error , std::vector
const& addresses, int port) { #if defined TORRENT_ASIO_DEBUGGING complete_async("udp_tracker_connection::name_lookup"); #endif if (m_abort) return; if (error == boost::asio::error::operation_aborted) return; if (error || addresses.empty()) { fail(error); return; } boost::shared_ptr cb = requester(); #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("*** UDP_TRACKER [ name lookup successful ]"); #endif if (cancelled()) { fail(error_code(errors::torrent_aborted)); return; } restart_read_timeout(); // look for an address that has the same kind as the one // we're listening on. To make sure the tracker get our // correct listening address. for (std::vector
::const_iterator i = addresses.begin() , end(addresses.end()); i != end; ++i) m_endpoints.push_back(tcp::endpoint(*i, port)); if (tracker_req().filter) { // remove endpoints that are filtered by the IP filter for (std::vector::iterator k = m_endpoints.begin(); k != m_endpoints.end();) { if (tracker_req().filter->access(k->address()) == ip_filter::blocked) { #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("*** UDP_TRACKER [ IP blocked by filter: %s ]" , print_address(k->address()).c_str()); #endif k = m_endpoints.erase(k); } else ++k; } } // if all endpoints were filtered by the IP filter, we can't connect if (m_endpoints.empty()) { fail(error_code(errors::banned_by_ip_filter)); return; } m_target = pick_target_endpoint(); start_announce(); } udp::endpoint udp_tracker_connection::pick_target_endpoint() const { std::vector::const_iterator iter = m_endpoints.begin(); udp::endpoint target = udp::endpoint(iter->address(), iter->port()); if (tracker_req().bind_ip) { // find first endpoint that matches our bind interface type for (; iter != m_endpoints.end() && iter->address().is_v4() != tracker_req().bind_ip->is_v4(); ++iter); if (iter == m_endpoints.end()) { TORRENT_ASSERT(target.address().is_v4() != tracker_req().bind_ip->is_v4()); boost::shared_ptr cb = requester(); if (cb) { char const* tracker_address_type = target.address().is_v4() ? "IPv4" : "IPv6"; char const* bind_address_type = tracker_req().bind_ip->is_v4() ? "IPv4" : "IPv6"; char msg[200]; snprintf(msg, sizeof(msg) , "the tracker only resolves to an %s address, and you're " "listening on an %s socket. This may prevent you from receiving " "incoming connections." , tracker_address_type, bind_address_type); cb->tracker_warning(tracker_req(), msg); } } else { target = udp::endpoint(iter->address(), iter->port()); } } return target; } void udp_tracker_connection::start_announce() { mutex::scoped_lock l(m_cache_mutex); std::map::iterator cc = m_connection_cache.find(m_target.address()); if (cc != m_connection_cache.end()) { // we found a cached entry! Now, we can only // use if if it hasn't expired if (aux::time_now() < cc->second.expires) { if (0 == (tracker_req().kind & tracker_request::scrape_request)) send_udp_announce(); else if (0 != (tracker_req().kind & tracker_request::scrape_request)) send_udp_scrape(); return; } // if it expired, remove it from the cache m_connection_cache.erase(cc); } l.unlock(); send_udp_connect(); } void udp_tracker_connection::on_timeout(error_code const& ec) { if (ec) { fail(ec); return; } #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr cb = requester(); if (cb) cb->debug_log("*** UDP_TRACKER [ timed out url: %s ]", tracker_req().url.c_str()); #endif fail(error_code(errors::timed_out)); } void udp_tracker_connection::close() { error_code ec; tracker_connection::close(); } bool udp_tracker_connection::on_receive_hostname(error_code const& e , char const* hostname, char const* buf, int size) { TORRENT_UNUSED(hostname); // just ignore the hostname this came from, pretend that // it's from the same endpoint we sent it to (i.e. the same // port). We have so many other ways of confirming this packet // comes from the tracker anyway, so it's not a big deal return on_receive(e, m_target, buf, size); } bool udp_tracker_connection::on_receive(error_code const& e , udp::endpoint const& ep, char const* buf, int size) { #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr cb = requester(); #endif // ignore resposes before we've sent any requests if (m_state == action_error) { #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("<== UDP_TRACKER [ m_action == error ]"); #endif return false; } if (m_abort) { #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("<== UDP_TRACKER [ aborted]"); #endif return false; } // ignore packet not sent from the tracker // if m_target is inaddr_any, it suggests that we // sent the packet through a proxy only knowing // the hostname, in which case this packet might be for us if (!is_any(m_target.address()) && m_target != ep) { #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("<== UDP_TRACKER [ unexpected source IP: %s " "expected: %s ]" , print_endpoint(ep).c_str() , print_endpoint(m_target).c_str()); #endif return false; } if (e) fail(e); #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("<== UDP_TRACKER_PACKET [ size: %d ]", size); #endif // ignore packets smaller than 8 bytes if (size < 8) return false; const char* ptr = buf; int action = detail::read_int32(ptr); boost::uint32_t transaction = detail::read_uint32(ptr); #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("*** UDP_TRACKER_PACKET [ action: %d ]", action); #endif // ignore packets with incorrect transaction id if (m_transaction_id != transaction) { #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("*** UDP_TRACKER_PACKET [ tid: %x ]" , int(transaction)); #endif return false; } if (action == action_error) { fail(error_code(errors::tracker_failure), -1, std::string(ptr, size - 8).c_str()); return true; } // ignore packets that's not a response to our message if (action != m_state) { #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("*** UDP_TRACKER_PACKET [ unexpected action: %d " " expected: %d ]", action, m_state); #endif return false; } restart_read_timeout(); #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("*** UDP_TRACKER_RESPONSE [ tid: %x ]" , int(transaction)); #endif switch (m_state) { case action_connect: return on_connect_response(buf, size); case action_announce: return on_announce_response(buf, size); case action_scrape: return on_scrape_response(buf, size); default: break; } return false; } void udp_tracker_connection::update_transaction_id() { boost::uint32_t new_tid; // don't use 0, because that has special meaning (unintialized) do { new_tid = random(); } while (new_tid == 0); if (m_transaction_id != 0) m_man.update_transaction_id(shared_from_this(), new_tid); m_transaction_id = new_tid; } bool udp_tracker_connection::on_connect_response(char const* buf, int size) { // ignore packets smaller than 16 bytes if (size < 16) return false; restart_read_timeout(); buf += 8; // skip header // reset transaction update_transaction_id(); boost::uint64_t connection_id = detail::read_int64(buf); mutex::scoped_lock l(m_cache_mutex); connection_cache_entry& cce = m_connection_cache[m_target.address()]; cce.connection_id = connection_id; cce.expires = aux::time_now() + seconds(m_man.settings().get_int(settings_pack::udp_tracker_token_expiry)); if (0 == (tracker_req().kind & tracker_request::scrape_request)) send_udp_announce(); else if (0 != (tracker_req().kind & tracker_request::scrape_request)) send_udp_scrape(); return true; } void udp_tracker_connection::send_udp_connect() { #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr cb = requester(); #endif if (m_abort) { #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("==> UDP_TRACKER_CONNECT [ skipped, m_abort ]"); #endif return; } char buf[16]; char* ptr = buf; TORRENT_ASSERT(m_transaction_id != 0); detail::write_uint32(0x417, ptr); detail::write_uint32(0x27101980, ptr); // connection_id detail::write_int32(action_connect, ptr); // action (connect) detail::write_int32(m_transaction_id, ptr); // transaction_id TORRENT_ASSERT(ptr - buf == sizeof(buf)); error_code ec; if (!m_hostname.empty()) { m_man.get_udp_socket().send_hostname(m_hostname.c_str() , m_target.port(), buf, 16, ec , udp_socket::tracker_connection); } else { m_man.get_udp_socket().send(m_target, buf, 16, ec , udp_socket::tracker_connection); } ++m_attempts; if (ec) { #ifndef TORRENT_DISABLE_LOGGING if (cb) cb->debug_log("==> UDP_TRACKER_CONNECT [ failed: %s ]" , ec.message().c_str()); #endif fail(ec); return; } #ifndef TORRENT_DISABLE_LOGGING if (cb) { char hex_ih[41]; to_hex(tracker_req().info_hash.data(), 20, hex_ih); cb->debug_log("==> UDP_TRACKER_CONNECT [ to: %s ih: %s]" , m_hostname.empty() ? print_endpoint(m_target).c_str() : (m_hostname + ":" + to_string(m_target.port()).elems).c_str() , hex_ih); } #endif m_state = action_connect; sent_bytes(16 + 28); // assuming UDP/IP header } void udp_tracker_connection::send_udp_scrape() { if (m_abort) return; std::map::iterator i = m_connection_cache.find(m_target.address()); // this isn't really supposed to happen TORRENT_ASSERT(i != m_connection_cache.end()); if (i == m_connection_cache.end()) return; char buf[8 + 4 + 4 + 20]; char* out = buf; detail::write_int64(i->second.connection_id, out); // connection_id detail::write_int32(action_scrape, out); // action (scrape) detail::write_int32(m_transaction_id, out); // transaction_id // info_hash std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS out += 20; TORRENT_ASSERT(out - buf == sizeof(buf)); #endif error_code ec; if (!m_hostname.empty()) { m_man.get_udp_socket().send_hostname(m_hostname.c_str(), m_target.port() , buf, sizeof(buf), ec, udp_socket::tracker_connection); } else { m_man.get_udp_socket().send(m_target, buf, sizeof(buf), ec , udp_socket::tracker_connection); } m_state = action_scrape; sent_bytes(sizeof(buf) + 28); // assuming UDP/IP header ++m_attempts; if (ec) { fail(ec); return; } } bool udp_tracker_connection::on_announce_response(char const* buf, int size) { if (size < 20) return false; buf += 8; // skip header restart_read_timeout(); tracker_response resp; resp.interval = detail::read_int32(buf); resp.min_interval = 60; resp.incomplete = detail::read_int32(buf); resp.complete = detail::read_int32(buf); std::size_t const ip_stride = #if TORRENT_USE_IPV6 m_target.address().is_v6() ? 18 : #endif 6; int const num_peers = (size - 20) / ip_stride; if ((size - 20) % ip_stride != 0) { fail(error_code(errors::invalid_tracker_response_length)); return false; } boost::shared_ptr cb = requester(); #ifndef TORRENT_DISABLE_LOGGING if (cb) { cb->debug_log("<== UDP_TRACKER_RESPONSE [ url: %s ]", tracker_req().url.c_str()); } #endif if (!cb) { close(); return true; } #if TORRENT_USE_IPV6 if (m_target.address().is_v6()) { resp.peers6.reserve(std::size_t(num_peers)); for (int i = 0; i < num_peers; ++i) { ipv6_peer_entry e; std::memcpy(&e.ip[0], buf, 16); buf += 16; e.port = detail::read_uint16(buf); resp.peers6.push_back(e); } } else #endif { resp.peers4.reserve(std::size_t(num_peers)); for (int i = 0; i < num_peers; ++i) { ipv4_peer_entry e; memcpy(&e.ip[0], buf, 4); buf += 4; e.port = detail::read_uint16(buf); resp.peers4.push_back(e); } } std::list
ip_list; for (std::vector::const_iterator i = m_endpoints.begin() , end(m_endpoints.end()); i != end; ++i) { ip_list.push_back(i->address()); } cb->tracker_response(tracker_req(), m_target.address(), ip_list , resp); close(); return true; } bool udp_tracker_connection::on_scrape_response(char const* buf, int size) { restart_read_timeout(); int action = detail::read_int32(buf); boost::uint32_t transaction = detail::read_uint32(buf); if (transaction != m_transaction_id) { fail(error_code(errors::invalid_tracker_transaction_id)); return false; } if (action == action_error) { fail(error_code(errors::tracker_failure), -1, std::string(buf, size - 8).c_str()); return true; } if (action != action_scrape) { fail(error_code(errors::invalid_tracker_action)); return true; } if (size < 20) { fail(error_code(errors::invalid_tracker_response_length)); return true; } int complete = detail::read_int32(buf); int downloaded = detail::read_int32(buf); int incomplete = detail::read_int32(buf); boost::shared_ptr cb = requester(); if (!cb) { close(); return true; } cb->tracker_scrape_response(tracker_req() , complete, incomplete, downloaded, -1); close(); return true; } void udp_tracker_connection::send_udp_announce() { if (m_abort) return; char buf[800]; char* out = buf; tracker_request const& req = tracker_req(); const bool stats = req.send_stats; aux::session_settings const& settings = m_man.settings(); std::map::iterator i = m_connection_cache.find(m_target.address()); // this isn't really supposed to happen TORRENT_ASSERT(i != m_connection_cache.end()); if (i == m_connection_cache.end()) return; detail::write_int64(i->second.connection_id, out); // connection_id detail::write_int32(action_announce, out); // action (announce) detail::write_int32(m_transaction_id, out); // transaction_id std::copy(req.info_hash.begin(), req.info_hash.end(), out); // info_hash out += 20; std::copy(req.pid.begin(), req.pid.end(), out); // peer_id out += 20; detail::write_int64(stats ? req.downloaded : 0, out); // downloaded detail::write_int64(stats ? req.left : 0, out); // left detail::write_int64(stats ? req.uploaded : 0, out); // uploaded detail::write_int32(req.event, out); // event // ip address address_v4 announce_ip; if (!settings.get_bool(settings_pack::anonymous_mode) && !settings.get_str(settings_pack::announce_ip).empty()) { error_code ec; address ip = address::from_string(settings.get_str(settings_pack::announce_ip).c_str(), ec); if (!ec && ip.is_v4()) announce_ip = ip.to_v4(); } detail::write_uint32(announce_ip.to_ulong(), out); detail::write_int32(req.key, out); // key detail::write_int32(req.num_want, out); // num_want detail::write_uint16(req.listen_port, out); // port std::string request_string; error_code ec; using boost::tuples::ignore; boost::tie(ignore, ignore, ignore, ignore, request_string) = parse_url_components(req.url, ec); if (ec) request_string.clear(); if (!request_string.empty()) { int str_len = (std::min)(int(request_string.size()), 255); request_string.resize(str_len); detail::write_uint8(2, out); detail::write_uint8(str_len, out); detail::write_string(request_string, out); } TORRENT_ASSERT(out - buf <= int(sizeof(buf))); #ifndef TORRENT_DISABLE_LOGGING boost::shared_ptr cb = requester(); if (cb) { char hex_ih[41]; to_hex(req.info_hash.data(), 20, hex_ih); cb->debug_log("==> UDP_TRACKER_ANNOUNCE [%s]", hex_ih); } #endif if (!m_hostname.empty()) { m_man.get_udp_socket().send_hostname(m_hostname.c_str() , m_target.port(), buf, out - buf, ec , udp_socket::tracker_connection); } else { m_man.get_udp_socket().send(m_target, buf, out - buf, ec , udp_socket::tracker_connection); } m_state = action_announce; sent_bytes(out - buf + 28); // assuming UDP/IP header ++m_attempts; if (ec) { fail(ec); return; } } } libtorrent-rasterbar-1.1.13/src/upnp.cpp000066400000000000000000001236201351156116000202240ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/socket.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/upnp.hpp" #include "libtorrent/io.hpp" #include "libtorrent/parse_url.hpp" #include "libtorrent/xml_parse.hpp" #include "libtorrent/enum_net.hpp" #include "libtorrent/random.hpp" #include "libtorrent/aux_/time.hpp" // for aux::time_now() #include "libtorrent/aux_/escape_string.hpp" // for convert_from_native #include "libtorrent/http_connection.hpp" #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/debug.hpp" #endif #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include namespace libtorrent { namespace upnp_errors { boost::system::error_code make_error_code(error_code_enum e) { return error_code(e, upnp_category()); } } // upnp_errors namespace static error_code ignore_error; upnp::rootdevice::rootdevice() : port(0) , lease_duration(default_lease_time) , supports_specific_external(true) , disabled(false) , non_router(false) { #if TORRENT_USE_ASSERTS magic = 1337; #endif } void upnp::rootdevice::close() const { TORRENT_ASSERT(magic == 1337); if (!upnp_connection) return; upnp_connection->close(); upnp_connection.reset(); } #if TORRENT_USE_ASSERTS upnp::rootdevice::~rootdevice() { TORRENT_ASSERT(magic == 1337); magic = 0; } #if __cplusplus >= 201103L upnp::rootdevice::rootdevice(rootdevice const&) = default; upnp::rootdevice& upnp::rootdevice::operator=(rootdevice const&) = default; #endif #endif upnp::upnp(io_service& ios , address const& listen_interface, std::string const& user_agent , portmap_callback_t const& cb, log_callback_t const& lcb , bool ignore_nonrouters) : m_user_agent(user_agent) , m_callback(cb) , m_log_callback(lcb) , m_retry_count(0) , m_io_service(ios) , m_resolver(ios) , m_socket(udp::endpoint(address_v4::from_string("239.255.255.250" , ignore_error), 1900)) , m_broadcast_timer(ios) , m_refresh_timer(ios) , m_map_timer(ios) , m_disabled(false) , m_closing(false) , m_ignore_non_routers(ignore_nonrouters) , m_last_if_update(min_time()) { TORRENT_ASSERT(cb); // TODO: 3 listen_interface is not used. It's meant to bind the broadcast // socket. it would probably have to be changed to a vector of interfaces to // bind to though, since the broadcast socket opens one socket per local // interface by default TORRENT_UNUSED(listen_interface); } void upnp::start() { error_code ec; m_socket.open(boost::bind(&upnp::on_reply, self(), _1, _2, _3) , lt::get_io_service(m_refresh_timer), ec); m_mappings.reserve(10); } upnp::~upnp() { } void upnp::discover_device() { mutex::scoped_lock l(m_mutex); if (m_socket.num_send_sockets() == 0) log("No network interfaces to broadcast to", l); discover_device_impl(l); } void upnp::log(char const* msg, mutex::scoped_lock& l) { l.unlock(); m_log_callback(msg); l.lock(); } void upnp::discover_device_impl(mutex::scoped_lock& l) { static const char msearch[] = "M-SEARCH * HTTP/1.1\r\n" "HOST: 239.255.255.250:1900\r\n" "ST:upnp:rootdevice\r\n" "MAN:\"ssdp:discover\"\r\n" "MX:3\r\n" "\r\n\r\n"; error_code ec; #ifdef TORRENT_DEBUG_UPNP // simulate packet loss if (m_retry_count & 1) #endif m_socket.send(msearch, sizeof(msearch) - 1, ec); if (ec) { char msg[500]; snprintf(msg, sizeof(msg), "broadcast failed: %s. Aborting." , convert_from_native(ec.message()).c_str()); log(msg, l); disable(ec, l); return; } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("upnp::resend_request"); #endif ++m_retry_count; m_broadcast_timer.expires_from_now(seconds(2 * m_retry_count), ec); m_broadcast_timer.async_wait(boost::bind(&upnp::resend_request , self(), _1)); log("broadcasting search for rootdevice", l); } // returns a reference to a mapping or -1 on failure int upnp::add_mapping(upnp::protocol_type p, int external_port, tcp::endpoint local_ep) { // external port 0 means _every_ port TORRENT_ASSERT(external_port != 0); mutex::scoped_lock l(m_mutex); char msg[500]; snprintf(msg, sizeof(msg), "adding port map: [ protocol: %s ext_port: %u " "local_ep: %s ] %s", (p == tcp?"tcp":"udp"), external_port , print_endpoint(local_ep).c_str(), m_disabled ? "DISABLED": ""); log(msg, l); if (m_disabled) return -1; std::vector::iterator mapping_it = std::find_if( m_mappings.begin(), m_mappings.end() , boost::bind(&global_mapping_t::protocol, _1) == int(none)); if (mapping_it == m_mappings.end()) { m_mappings.push_back(global_mapping_t()); mapping_it = m_mappings.end() - 1; } mapping_it->protocol = p; mapping_it->external_port = external_port; mapping_it->local_ep = local_ep; int mapping_index = mapping_it - m_mappings.begin(); for (std::set::iterator i = m_devices.begin() , end(m_devices.end()); i != end; ++i) { rootdevice& d = const_cast(*i); TORRENT_ASSERT(d.magic == 1337); if (int(d.mapping.size()) <= mapping_index) d.mapping.resize(mapping_index + 1); mapping_t& m = d.mapping[mapping_index]; m.action = mapping_t::action_add; m.protocol = p; m.external_port = external_port; m.local_ep = local_ep; if (!d.service_namespace.empty()) update_map(d, mapping_index, l); } return mapping_index; } void upnp::delete_mapping(int mapping) { mutex::scoped_lock l(m_mutex); if (mapping >= int(m_mappings.size())) return; global_mapping_t& m = m_mappings[mapping]; char msg[500]; snprintf(msg, sizeof(msg), "deleting port map: [ protocol: %s ext_port: %u " "local_ep: %s ]", (m.protocol == tcp?"tcp":"udp"), m.external_port , print_endpoint(m.local_ep).c_str()); log(msg, l); if (m.protocol == none) return; for (std::set::iterator i = m_devices.begin() , end(m_devices.end()); i != end; ++i) { rootdevice& d = const_cast(*i); TORRENT_ASSERT(d.magic == 1337); TORRENT_ASSERT(mapping < int(d.mapping.size())); d.mapping[mapping].action = mapping_t::action_delete; if (!d.service_namespace.empty()) update_map(d, mapping, l); } } bool upnp::get_mapping(int index, tcp::endpoint& local_ep, int& external_port, int& protocol) const { TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); if (index >= int(m_mappings.size()) || index < 0) return false; global_mapping_t const& m = m_mappings[index]; if (m.protocol == none) return false; local_ep = m.local_ep; external_port = m.external_port; protocol = m.protocol; return true; } void upnp::resend_request(error_code const& ec) { #if defined TORRENT_ASIO_DEBUGGING complete_async("upnp::resend_request"); #endif if (ec) return; boost::shared_ptr me(self()); mutex::scoped_lock l(m_mutex); if (m_closing) return; if (m_retry_count < 12 && (m_devices.empty() || m_retry_count < 4)) { discover_device_impl(l); return; } if (m_devices.empty()) { disable(errors::no_router, l); return; } for (std::set::iterator i = m_devices.begin() , end(m_devices.end()); i != end; ++i) { if (i->control_url.empty() && !i->upnp_connection && !i->disabled) { // we don't have a WANIP or WANPPP url for this device, // ask for it rootdevice& d = const_cast(*i); TORRENT_ASSERT(d.magic == 1337); TORRENT_TRY { char msg[500]; snprintf(msg, sizeof(msg), "connecting to: %s", d.url.c_str()); log(msg, l); if (d.upnp_connection) d.upnp_connection->close(); d.upnp_connection.reset(new http_connection(m_io_service , m_resolver , boost::bind(&upnp::on_upnp_xml, self(), _1, _2 , boost::ref(d), _5))); d.upnp_connection->get(d.url, seconds(30), 1); } TORRENT_CATCH (std::exception& exc) { TORRENT_DECLARE_DUMMY(std::exception, exc); char msg[500]; snprintf(msg, sizeof(msg), "connection failed to: %s %s", d.url.c_str(), exc.what()); log(msg, l); d.disabled = true; } } } } void upnp::on_reply(udp::endpoint const& from, char* buffer , std::size_t bytes_transferred) { boost::shared_ptr me(self()); mutex::scoped_lock l(m_mutex); using namespace libtorrent::detail; // parse out the url for the device /* the response looks like this: HTTP/1.1 200 OK ST:upnp:rootdevice USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice Location: http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc Server: Custom/1.0 UPnP/1.0 Proc/Ver EXT: Cache-Control:max-age=180 DATE: Fri, 02 Jan 1970 08:10:38 GMT a notification looks like this: NOTIFY * HTTP/1.1 Host:239.255.255.250:1900 NT:urn:schemas-upnp-org:device:MediaServer:1 NTS:ssdp:alive Location:http://10.0.3.169:2869/upnphost/udhisapi.dll?content=uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e USN:uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e::urn:schemas-upnp-org:device:MediaServer:1 Cache-Control:max-age=900 Server:Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0 */ error_code ec; if (clock_type::now() - seconds(60) > m_last_if_update) { m_interfaces = enum_net_interfaces(m_io_service, ec); if (ec) { char msg[500]; snprintf(msg, sizeof(msg), "when receiving response from: %s: %s" , print_endpoint(from).c_str(), convert_from_native(ec.message()).c_str()); log(msg, l); } m_last_if_update = aux::time_now(); } if (!ec && !in_local_network(m_interfaces, from.address())) { char msg[400]; int num_chars = snprintf(msg, sizeof(msg) , "ignoring response from: %s. IP is not on local network. " , print_endpoint(from).c_str()); std::vector net = enum_net_interfaces(m_io_service, ec); for (std::vector::const_iterator i = net.begin() , end(net.end()); i != end && num_chars < int(sizeof(msg)); ++i) { num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) " , print_address(i->interface_address).c_str(), print_address(i->netmask).c_str()); } log(msg, l); return; } bool non_router = false; if (m_ignore_non_routers) { std::vector routes = enum_routes(m_io_service, ec); if (std::find_if(routes.begin(), routes.end() , boost::bind(&ip_route::gateway, _1) == from.address()) == routes.end()) { // this upnp device is filtered because it's not in the // list of configured routers if (ec) { char msg[500]; snprintf(msg, sizeof(msg), "failed to enumerate routes when " "receiving response from: %s: %s" , print_endpoint(from).c_str(), convert_from_native(ec.message()).c_str()); log(msg, l); } else { char msg[400]; int num_chars = snprintf(msg, sizeof(msg), "SSDP response from: " "%s: IP is not a router. " , print_endpoint(from).c_str()); for (std::vector::const_iterator i = routes.begin() , end(routes.end()); i != end && num_chars < int(sizeof(msg)); ++i) { num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) " , print_address(i->gateway).c_str(), print_address(i->netmask).c_str()); } log(msg, l); non_router = true; } } } http_parser p; bool error = false; p.incoming(buffer::const_interval(buffer , buffer + bytes_transferred), error); if (error) { char msg[500]; snprintf(msg, sizeof(msg), "received malformed HTTP from: %s" , print_endpoint(from).c_str()); log(msg, l); return; } if (p.status_code() != 200 && p.method() != "notify") { if (p.method().empty()) { char msg[500]; snprintf(msg, sizeof(msg), "HTTP status %u from %s" , p.status_code(), print_endpoint(from).c_str()); log(msg, l); } else { char msg[500]; snprintf(msg, sizeof(msg), "HTTP method %s from %s" , p.method().c_str(), print_endpoint(from).c_str()); log(msg, l); } return; } if (!p.header_finished()) { char msg[500]; snprintf(msg, sizeof(msg), "incomplete HTTP packet from %s" , print_endpoint(from).c_str()); log(msg, l); return; } std::string url = p.header("location"); if (url.empty()) { char msg[500]; snprintf(msg, sizeof(msg), "missing location header from %s" , print_endpoint(from).c_str()); log(msg, l); return; } rootdevice d; d.url = url; std::set::iterator i = m_devices.find(d); if (i == m_devices.end()) { std::string protocol; std::string auth; // we don't have this device in our list. Add it boost::tie(protocol, auth, d.hostname, d.port, d.path) = parse_url_components(d.url, ec); if (d.port == -1) d.port = protocol == "http" ? 80 : 443; if (ec) { char msg[500]; snprintf(msg, sizeof(msg), "invalid URL %s from %s: %s" , d.url.c_str(), print_endpoint(from).c_str(), convert_from_native(ec.message()).c_str()); log(msg, l); return; } // ignore the auth here. It will be re-parsed // by the http connection later if (protocol != "http") { char msg[500]; snprintf(msg, sizeof(msg), "unsupported protocol %s from %s" , protocol.c_str(), print_endpoint(from).c_str()); log(msg, l); return; } if (d.port == 0) { char msg[500]; snprintf(msg, sizeof(msg), "URL with port 0 from %s" , print_endpoint(from).c_str()); log(msg, l); return; } { char msg[500]; snprintf(msg, sizeof(msg), "found rootdevice: %s (%d)" , d.url.c_str(), int(m_devices.size())); log(msg, l); } if (m_devices.size() >= 50) { char msg[500]; snprintf(msg, sizeof(msg), "too many rootdevices: (%d). Ignoring %s" , int(m_devices.size()), d.url.c_str()); log(msg, l); return; } d.non_router = non_router; TORRENT_ASSERT(d.mapping.empty()); for (std::vector::iterator j = m_mappings.begin() , end(m_mappings.end()); j != end; ++j) { mapping_t m; m.action = mapping_t::action_add; m.local_ep = j->local_ep; m.external_port = j->external_port; m.protocol = j->protocol; d.mapping.push_back(m); } boost::tie(i, boost::tuples::ignore) = m_devices.insert(d); } // iterate over the devices we know and connect and issue the mappings try_map_upnp(l); if (m_ignore_non_routers) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("upnp::map_timer"); #endif // check back in in a little bit to see if we have seen any // devices at one of our default routes. If not, we want to override // ignoring them and use them instead (better than not working). m_map_timer.expires_from_now(seconds(1), ec); m_map_timer.async_wait(boost::bind(&upnp::map_timer , self(), _1)); } } void upnp::map_timer(error_code const& ec) { #if defined TORRENT_ASIO_DEBUGGING complete_async("upnp::map_timer"); #endif if (ec) return; if (m_closing) return; mutex::scoped_lock l(m_mutex); try_map_upnp(l, true); } void upnp::try_map_upnp(mutex::scoped_lock& l, bool timer) { if (m_devices.empty()) return; bool override_ignore_non_routers = false; if (m_ignore_non_routers && timer) { // if we don't ave any devices that match our default route, we // should try to map with the ones we did hear from anyway, // regardless of if they are not running at our gateway. override_ignore_non_routers = std::find_if(m_devices.begin() , m_devices.end(), boost::bind(&rootdevice::non_router, _1) == false) == m_devices.end(); if (override_ignore_non_routers) { char msg[500]; snprintf(msg, sizeof(msg), "overriding ignore non-routers"); log(msg, l); } } for (std::set::iterator i = m_devices.begin() , end(m_devices.end()); i != end; ++i) { // if we're ignoring non-routers, skip them. If on_timer is // set, we expect to have received all responses and if we don't // have any devices at our default route, then issue requests // to any device we found. if (m_ignore_non_routers && i->non_router && !override_ignore_non_routers) continue; if (i->control_url.empty() && !i->upnp_connection && !i->disabled) { // we don't have a WANIP or WANPPP url for this device, // ask for it rootdevice& d = const_cast(*i); TORRENT_ASSERT(d.magic == 1337); TORRENT_TRY { char msg[500]; snprintf(msg, sizeof(msg), "connecting to: %s" , d.url.c_str()); log(msg, l); if (d.upnp_connection) d.upnp_connection->close(); d.upnp_connection.reset(new http_connection(m_io_service , m_resolver , boost::bind(&upnp::on_upnp_xml, self(), _1, _2 , boost::ref(d), _5))); d.upnp_connection->get(d.url, seconds(30), 1); } TORRENT_CATCH (std::exception& exc) { TORRENT_DECLARE_DUMMY(std::exception, exc); char msg[500]; snprintf(msg, sizeof(msg), "connection failed to: %s %s" , d.url.c_str(), exc.what()); log(msg, l); d.disabled = true; } } } } void upnp::post(upnp::rootdevice const& d, char const* soap , char const* soap_action, mutex::scoped_lock& l) { TORRENT_ASSERT(d.magic == 1337); TORRENT_ASSERT(d.upnp_connection); char header[2048]; snprintf(header, sizeof(header), "POST %s HTTP/1.1\r\n" "Host: %s:%u\r\n" "Content-Type: text/xml; charset=\"utf-8\"\r\n" "Content-Length: %d\r\n" "Soapaction: \"%s#%s\"\r\n\r\n" "%s" , d.path.c_str(), d.hostname.c_str(), d.port , int(strlen(soap)), d.service_namespace.c_str(), soap_action , soap); d.upnp_connection->m_sendbuffer = header; char msg[1024]; snprintf(msg, sizeof(msg), "sending: %s", header); log(msg, l); } void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i) { mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(d.magic == 1337); if (!d.upnp_connection) { TORRENT_ASSERT(d.disabled); char msg[500]; snprintf(msg, sizeof(msg), "mapping %u aborted", i); log(msg, l); return; } char const* soap_action = "AddPortMapping"; error_code ec; std::string local_endpoint = print_address(c.socket().local_endpoint(ec).address()); char soap[2048]; snprintf(soap, sizeof(soap), "\n" "" "" "" "%u" "%s" "%u" "%s" "1" "%s at %s:%d" "%u" "" , soap_action, d.service_namespace.c_str(), d.mapping[i].external_port , (d.mapping[i].protocol == udp ? "UDP" : "TCP") , d.mapping[i].local_ep.port() , local_endpoint.c_str() , m_user_agent.c_str(), local_endpoint.c_str(), d.mapping[i].local_ep.port() , d.lease_duration, soap_action); post(d, soap, soap_action, l); } void upnp::next(rootdevice& d, int i, mutex::scoped_lock& l) { if (i < num_mappings() - 1) { update_map(d, i + 1, l); } else { std::vector::iterator j = std::find_if(d.mapping.begin(), d.mapping.end() , boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none)); if (j == d.mapping.end()) return; update_map(d, j - d.mapping.begin(), l); } } void upnp::update_map(rootdevice& d, int i, mutex::scoped_lock& l) { TORRENT_ASSERT(d.magic == 1337); TORRENT_ASSERT(i < int(d.mapping.size())); TORRENT_ASSERT(d.mapping.size() == m_mappings.size()); if (d.upnp_connection) return; boost::shared_ptr me(self()); mapping_t& m = d.mapping[i]; if (m.action == mapping_t::action_none || m.protocol == none) { char msg[500]; snprintf(msg, sizeof(msg), "mapping %u does not need updating, skipping", i); log(msg, l); m.action = mapping_t::action_none; next(d, i, l); return; } TORRENT_ASSERT(!d.upnp_connection); TORRENT_ASSERT(!d.service_namespace.empty()); char msg[500]; snprintf(msg, sizeof(msg), "connecting to %s", d.hostname.c_str()); log(msg, l); if (m.action == mapping_t::action_add) { if (m.failcount > 5) { m.action = mapping_t::action_none; // giving up next(d, i, l); return; } if (d.upnp_connection) d.upnp_connection->close(); d.upnp_connection.reset(new http_connection(m_io_service , m_resolver , boost::bind(&upnp::on_upnp_map_response, self(), _1, _2 , boost::ref(d), i, _5), true, default_max_bottled_buffer_size , boost::bind(&upnp::create_port_mapping, self(), _1, boost::ref(d), i))); d.upnp_connection->start(d.hostname, d.port , seconds(10), 1, NULL, false, 5, m.local_ep.address()); } else if (m.action == mapping_t::action_delete) { if (d.upnp_connection) d.upnp_connection->close(); d.upnp_connection.reset(new http_connection(m_io_service , m_resolver , boost::bind(&upnp::on_upnp_unmap_response, self(), _1, _2 , boost::ref(d), i, _5), true, default_max_bottled_buffer_size , boost::bind(&upnp::delete_port_mapping, self(), boost::ref(d), i))); d.upnp_connection->start(d.hostname, d.port , seconds(10), 1, NULL, false, 5, m.local_ep.address()); } m.action = mapping_t::action_none; } void upnp::delete_port_mapping(rootdevice& d, int i) { mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(d.magic == 1337); if (!d.upnp_connection) { TORRENT_ASSERT(d.disabled); char msg[500]; snprintf(msg, sizeof(msg), "unmapping %u aborted", i); log(msg, l); return; } char const* soap_action = "DeletePortMapping"; char soap[2048]; error_code ec; snprintf(soap, sizeof(soap), "\n" "" "" "" "%u" "%s" "" , soap_action, d.service_namespace.c_str() , d.mapping[i].external_port , (d.mapping[i].protocol == udp ? "UDP" : "TCP") , soap_action); post(d, soap, soap_action, l); } namespace { void copy_tolower(std::string& dst, char const* src, int len) { dst.clear(); dst.reserve(len); while (len-- > 0) { dst.push_back(to_lower(*src++)); } } } TORRENT_EXTRA_EXPORT void find_control_url(int type, char const* string , int str_len, parse_state& state) { if (type == xml_start_tag) { std::string tag; copy_tolower(tag, string, str_len); state.tag_stack.push_back(tag); // std::copy(state.tag_stack.begin(), state.tag_stack.end(), std::ostream_iterator(std::cout, " ")); // std::cout << std::endl; } else if (type == xml_end_tag) { if (!state.tag_stack.empty()) { if (state.in_service && state.tag_stack.back() == "service") state.in_service = false; state.tag_stack.pop_back(); } } else if (type == xml_string) { if (state.tag_stack.empty()) return; // std::cout << " " << string << std::endl;} if (!state.in_service && state.top_tags("service", "servicetype")) { std::string name(string, str_len); if (string_equal_no_case(name.c_str(), "urn:schemas-upnp-org:service:WANIPConnection:1") || string_equal_no_case(name.c_str(), "urn:schemas-upnp-org:service:WANIPConnection:2") || string_equal_no_case(name.c_str(), "urn:schemas-upnp-org:service:WANPPPConnection:1")) { state.service_type.assign(string, str_len); state.in_service = true; } } else if (state.control_url.empty() && state.in_service && state.top_tags("service", "controlurl") && strlen(string) > 0) { // default to the first (or only) control url in the router's listing state.control_url.assign(string, str_len); } else if (state.model.empty() && state.top_tags("device", "modelname")) { state.model.assign(string, str_len); } else if (state.tag_stack.back() == "urlbase") { state.url_base.assign(string, str_len); } } } void upnp::on_upnp_xml(error_code const& e , libtorrent::http_parser const& p, rootdevice& d , http_connection& c) { boost::shared_ptr me(self()); mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(d.magic == 1337); if (d.upnp_connection && d.upnp_connection.get() == &c) { d.upnp_connection->close(); d.upnp_connection.reset(); } if (e && e != boost::asio::error::eof) { char msg[500]; snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s" , d.url.c_str(), convert_from_native(e.message()).c_str()); log(msg, l); d.disabled = true; return; } if (!p.header_finished()) { char msg[500]; snprintf(msg, sizeof(msg), "error while fetching control url from: %s: incomplete HTTP message" , d.url.c_str()); log(msg, l); d.disabled = true; return; } if (p.status_code() != 200) { char msg[500]; snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s" , d.url.c_str(), convert_from_native(p.message()).c_str()); log(msg, l); d.disabled = true; return; } parse_state s; xml_parse(p.get_body().begin, p.get_body().end , boost::bind(&find_control_url, _1, _2, _3, boost::ref(s))); if (s.control_url.empty()) { char msg[500]; snprintf(msg, sizeof(msg), "could not find a port mapping interface in response from: %s" , d.url.c_str()); log(msg, l); d.disabled = true; return; } d.service_namespace = s.service_type; TORRENT_ASSERT(!d.service_namespace.empty()); if (!s.model.empty()) m_model = s.model; if (!s.url_base.empty() && s.control_url.substr(0, 7) != "http://") { // avoid double slashes in path if (s.url_base[s.url_base.size()-1] == '/' && !s.control_url.empty() && s.control_url[0] == '/') s.url_base.erase(s.url_base.end()-1); d.control_url = s.url_base + s.control_url; } else d.control_url = s.control_url; std::string protocol; std::string auth; error_code ec; if (!d.control_url.empty() && d.control_url[0] == '/') { boost::tie(protocol, auth, d.hostname, d.port, d.path) = parse_url_components(d.url, ec); if (d.port == -1) d.port = protocol == "http" ? 80 : 443; d.control_url = protocol + "://" + d.hostname + ":" + to_string(d.port).elems + s.control_url; } { char msg[500]; snprintf(msg, sizeof(msg), "found control URL: %s namespace %s " "urlbase: %s in response from %s" , d.control_url.c_str(), d.service_namespace.c_str() , s.url_base.c_str(), d.url.c_str()); log(msg, l); } boost::tie(protocol, auth, d.hostname, d.port, d.path) = parse_url_components(d.control_url, ec); if (d.port == -1) d.port = protocol == "http" ? 80 : 443; if (ec) { char msg[500]; snprintf(msg, sizeof(msg), "failed to parse URL '%s': %s" , d.control_url.c_str(), convert_from_native(ec.message()).c_str()); log(msg, l); d.disabled = true; return; } d.upnp_connection.reset(new http_connection(m_io_service , m_resolver , boost::bind(&upnp::on_upnp_get_ip_address_response, self(), _1, _2 , boost::ref(d), _5), true, default_max_bottled_buffer_size , boost::bind(&upnp::get_ip_address, self(), boost::ref(d)))); d.upnp_connection->start(d.hostname, d.port , seconds(10), 1); } void upnp::get_ip_address(rootdevice& d) { mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(d.magic == 1337); if (!d.upnp_connection) { TORRENT_ASSERT(d.disabled); char msg[500]; snprintf(msg, sizeof(msg), "getting external IP address"); log(msg, l); return; } char const* soap_action = "GetExternalIPAddress"; char soap[2048]; error_code ec; snprintf(soap, sizeof(soap), "\n" "" "" "" , soap_action, d.service_namespace.c_str() , soap_action); post(d, soap, soap_action, l); } void upnp::disable(error_code const& ec, mutex::scoped_lock& l) { m_disabled = true; // kill all mappings for (std::vector::iterator i = m_mappings.begin() , end(m_mappings.end()); i != end; ++i) { if (i->protocol == none) continue; int const proto = i->protocol; i->protocol = none; l.unlock(); m_callback(i - m_mappings.begin(), address(), 0, proto, ec); l.lock(); } // we cannot clear the devices since there // might be outstanding requests relying on // the device entry being present when they // complete error_code e; m_broadcast_timer.cancel(e); m_refresh_timer.cancel(e); m_map_timer.cancel(e); m_socket.close(); } void find_error_code(int type, char const* string, int str_len, error_code_parse_state& state) { if (state.exit) return; if (type == xml_start_tag && !std::strncmp("errorCode", string, size_t(str_len))) { state.in_error_code = true; } else if (type == xml_string && state.in_error_code) { std::string error_code_str(string, str_len); state.error_code = std::atoi(error_code_str.c_str()); state.exit = true; } } void find_ip_address(int type, char const* string, int str_len, ip_address_parse_state& state) { find_error_code(type, string, str_len, state); if (state.exit) return; if (type == xml_start_tag && !std::strncmp("NewExternalIPAddress", string, size_t(str_len))) { state.in_ip_address = true; } else if (type == xml_string && state.in_ip_address) { state.ip_address.assign(string, str_len); state.exit = true; } } namespace { struct error_code_t { int code; char const* msg; }; error_code_t error_codes[] = { {0, "no error"} , {402, "Invalid Arguments"} , {501, "Action Failed"} , {714, "The specified value does not exist in the array"} , {715, "The source IP address cannot be wild-carded"} , {716, "The external port cannot be wild-carded"} , {718, "The port mapping entry specified conflicts with " "a mapping assigned previously to another client"} , {724, "Internal and External port values must be the same"} , {725, "The NAT implementation only supports permanent " "lease times on port mappings"} , {726, "RemoteHost must be a wildcard and cannot be a " "specific IP address or DNS name"} , {727, "ExternalPort must be a wildcard and cannot be a specific port "} }; } struct upnp_error_category : boost::system::error_category { virtual const char* name() const BOOST_SYSTEM_NOEXCEPT { return "upnp"; } virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT { int num_errors = sizeof(error_codes) / sizeof(error_codes[0]); error_code_t* end = error_codes + num_errors; error_code_t tmp = {ev, 0}; error_code_t* e = std::lower_bound(error_codes, end, tmp , boost::bind(&error_code_t::code, _1) < boost::bind(&error_code_t::code, _2)); if (e != end && e->code == ev) { return e->msg; } char msg[500]; snprintf(msg, sizeof(msg), "unknown UPnP error (%d)", ev); return msg; } virtual boost::system::error_condition default_error_condition( int ev) const BOOST_SYSTEM_NOEXCEPT { return boost::system::error_condition(ev, *this); } }; boost::system::error_category& upnp_category() { static upnp_error_category cat; return cat; } #ifndef TORRENT_NO_DEPRECATED boost::system::error_category& get_upnp_category() { return upnp_category(); } #endif void upnp::on_upnp_get_ip_address_response(error_code const& e , libtorrent::http_parser const& p, rootdevice& d , http_connection& c) { boost::shared_ptr me(self()); mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(d.magic == 1337); if (d.upnp_connection && d.upnp_connection.get() == &c) { d.upnp_connection->close(); d.upnp_connection.reset(); } if (m_closing) return; if (e && e != boost::asio::error::eof) { char msg[500]; snprintf(msg, sizeof(msg), "error while getting external IP address: %s" , convert_from_native(e.message()).c_str()); log(msg, l); if (num_mappings() > 0) update_map(d, 0, l); return; } if (!p.header_finished()) { log("error while getting external IP address: incomplete http message", l); if (num_mappings() > 0) update_map(d, 0, l); return; } if (p.status_code() != 200) { char msg[500]; snprintf(msg, sizeof(msg), "error while getting external IP address: %s" , convert_from_native(p.message()).c_str()); log(msg, l); if (num_mappings() > 0) update_map(d, 0, l); return; } // response may look like // // // // 192.168.160.19 // // // { char msg[500]; snprintf(msg, sizeof(msg), "get external IP address response: %s" , std::string(p.get_body().begin, p.get_body().end).c_str()); log(msg, l); } ip_address_parse_state s; xml_parse(const_cast(p.get_body().begin), const_cast(p.get_body().end) , boost::bind(&find_ip_address, _1, _2, _3, boost::ref(s))); if (s.error_code != -1) { char msg[500]; snprintf(msg, sizeof(msg), "error while getting external IP address, code: %u" , s.error_code); log(msg, l); } if (!s.ip_address.empty()) { char msg[500]; snprintf(msg, sizeof(msg), "got router external IP address %s", s.ip_address.c_str()); log(msg, l); d.external_ip = address::from_string(s.ip_address.c_str(), ignore_error); } else { log("failed to find external IP address in response", l); } if (num_mappings() > 0) update_map(d, 0, l); } void upnp::on_upnp_map_response(error_code const& e , libtorrent::http_parser const& p, rootdevice& d, int mapping , http_connection& c) { boost::shared_ptr me(self()); mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(d.magic == 1337); if (d.upnp_connection && d.upnp_connection.get() == &c) { d.upnp_connection->close(); d.upnp_connection.reset(); } if (e && e != boost::asio::error::eof) { char msg[500]; snprintf(msg, sizeof(msg), "error while adding port map: %s" , convert_from_native(e.message()).c_str()); log(msg, l); d.disabled = true; return; } if (m_closing) return; // error code response may look like this: // // // // s:Client // UPnPError // // // 402 // Invalid Args // // // // // if (!p.header_finished()) { log("error while adding port map: incomplete http message", l); next(d, mapping, l); return; } std::string ct = p.header("content-type"); if (!ct.empty() && ct.find_first_of("text/xml") == std::string::npos && ct.find_first_of("text/soap+xml") == std::string::npos && ct.find_first_of("application/xml") == std::string::npos && ct.find_first_of("application/soap+xml") == std::string::npos ) { char msg[300]; snprintf(msg, sizeof(msg), "error while adding port map: invalid content-type, \"%s\". Expected text/xml or application/soap+xml" , ct.c_str()); log(msg, l); next(d, mapping, l); return; } // We don't want to ignore responses with return codes other than 200 // since those might contain valid UPnP error codes error_code_parse_state s; xml_parse(const_cast(p.get_body().begin) , const_cast(p.get_body().end) , boost::bind(&find_error_code, _1, _2, _3, boost::ref(s))); if (s.error_code != -1) { char msg[500]; snprintf(msg, sizeof(msg), "error while adding port map, code: %u" , s.error_code); log(msg, l); } mapping_t& m = d.mapping[mapping]; if (s.error_code == 725) { // only permanent leases supported d.lease_duration = 0; m.action = mapping_t::action_add; ++m.failcount; update_map(d, mapping, l); return; } else if (s.error_code == 727) { return_error(mapping, s.error_code, l); } else if ((s.error_code == 718 || s.error_code == 501) && m.failcount < 4) { // some routers return 501 action failed, instead of 716 // The external port conflicts with another mapping // pick a random port m.external_port = 40000 + (random() % 10000); m.action = mapping_t::action_add; ++m.failcount; update_map(d, mapping, l); return; } else if (s.error_code != -1) { return_error(mapping, s.error_code, l); } char msg[500]; snprintf(msg, sizeof(msg), "map response: %s" , std::string(p.get_body().begin, p.get_body().end).c_str()); log(msg, l); if (s.error_code == -1) { l.unlock(); m_callback(mapping, d.external_ip, m.external_port, m.protocol, error_code()); l.lock(); if (d.lease_duration > 0) { m.expires = aux::time_now() + seconds(int(d.lease_duration * 0.75f)); time_point next_expire = m_refresh_timer.expires_at(); if (next_expire < aux::time_now() || next_expire > m.expires) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("upnp::on_expire"); #endif error_code ec; m_refresh_timer.expires_at(m.expires, ec); m_refresh_timer.async_wait(boost::bind(&upnp::on_expire, self(), _1)); } } else { m.expires = max_time(); } m.failcount = 0; } next(d, mapping, l); } void upnp::return_error(int mapping, int code, mutex::scoped_lock& l) { int num_errors = sizeof(error_codes) / sizeof(error_codes[0]); error_code_t* end = error_codes + num_errors; error_code_t tmp = {code, 0}; error_code_t* e = std::lower_bound(error_codes, end, tmp , boost::bind(&error_code_t::code, _1) < boost::bind(&error_code_t::code, _2)); std::string error_string = "UPnP mapping error "; error_string += to_string(code).elems; if (e != end && e->code == code) { error_string += ": "; error_string += e->msg; } const int proto = m_mappings[mapping].protocol; l.unlock(); m_callback(mapping, address(), 0, proto, error_code(code, upnp_category())); l.lock(); } void upnp::on_upnp_unmap_response(error_code const& e , libtorrent::http_parser const& p, rootdevice& d, int mapping , http_connection& c) { boost::shared_ptr me(self()); mutex::scoped_lock l(m_mutex); TORRENT_ASSERT(d.magic == 1337); if (d.upnp_connection && d.upnp_connection.get() == &c) { d.upnp_connection->close(); d.upnp_connection.reset(); } if (e && e != boost::asio::error::eof) { char msg[500]; snprintf(msg, sizeof(msg), "error while deleting portmap: %s" , convert_from_native(e.message()).c_str()); log(msg, l); } else if (!p.header_finished()) { log("error while deleting portmap: incomplete http message", l); } else if (p.status_code() != 200) { char msg[500]; snprintf(msg, sizeof(msg), "error while deleting portmap: %s" , convert_from_native(p.message()).c_str()); log(msg, l); } else { char msg[500]; snprintf(msg, sizeof(msg), "unmap response: %s" , std::string(p.get_body().begin, p.get_body().end).c_str()); log(msg, l); } error_code_parse_state s; if (p.header_finished()) { xml_parse(const_cast(p.get_body().begin) , const_cast(p.get_body().end) , boost::bind(&find_error_code, _1, _2, _3, boost::ref(s))); } int const proto = m_mappings[mapping].protocol; l.unlock(); m_callback(mapping, address(), 0, proto, p.status_code() != 200 ? error_code(p.status_code(), http_category()) : error_code(s.error_code, upnp_category())); l.lock(); d.mapping[mapping].protocol = none; next(d, mapping, l); } void upnp::on_expire(error_code const& ec) { #if defined TORRENT_ASIO_DEBUGGING complete_async("upnp::on_expire"); #endif if (ec) return; time_point now = aux::time_now(); time_point next_expire = max_time(); mutex::scoped_lock l(m_mutex); for (std::set::iterator i = m_devices.begin() , end(m_devices.end()); i != end; ++i) { rootdevice& d = const_cast(*i); TORRENT_ASSERT(d.magic == 1337); for (int m = 0; m < num_mappings(); ++m) { if (d.mapping[m].expires != max_time()) continue; if (d.mapping[m].expires < now) { d.mapping[m].expires = max_time(); update_map(d, m, l); } else if (d.mapping[m].expires < next_expire) { next_expire = d.mapping[m].expires; } } } if (next_expire != max_time()) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("upnp::on_expire"); #endif error_code e; m_refresh_timer.expires_at(next_expire, e); m_refresh_timer.async_wait(boost::bind(&upnp::on_expire, self(), _1)); } } void upnp::close() { mutex::scoped_lock l(m_mutex); error_code ec; m_refresh_timer.cancel(ec); m_broadcast_timer.cancel(ec); m_map_timer.cancel(ec); m_closing = true; m_socket.close(); for (std::set::iterator i = m_devices.begin() , end(m_devices.end()); i != end; ++i) { rootdevice& d = const_cast(*i); TORRENT_ASSERT(d.magic == 1337); if (d.control_url.empty()) continue; for (std::vector::iterator j = d.mapping.begin() , end2(d.mapping.end()); j != end2; ++j) { if (j->protocol == none) continue; if (j->action == mapping_t::action_add) { j->action = mapping_t::action_none; continue; } j->action = mapping_t::action_delete; m_mappings[j - d.mapping.begin()].protocol = none; } if (num_mappings() > 0) update_map(d, 0, l); } } } libtorrent-rasterbar-1.1.13/src/ut_metadata.cpp000066400000000000000000000507411351156116000215350ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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_EXTENSIONS #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/peer_connection_handle.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/extensions/ut_metadata.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/random.hpp" #include "libtorrent/io.hpp" #include "libtorrent/performance_counters.hpp" // for counters namespace libtorrent { namespace { enum { // this is the max number of bytes we'll // queue up in the send buffer. If we exceed this, // we'll wait another second before checking // the send buffer size again. So, this may limit // the rate at which we can server metadata to // 160 kiB/s send_buffer_limit = 0x4000 * 10, // this is the max number of requests we'll queue // up. If we get more requests tha this, we'll // start rejecting them, claiming we don't have // metadata. If the torrent is greater than 16 MiB, // we may hit this case (and the client requesting // doesn't throttle its requests) max_incoming_requests = 1024, metadata_req = 0, metadata_piece = 1, metadata_dont_have = 2 }; int div_round_up(int numerator, int denominator) { return (numerator + denominator - 1) / denominator; } struct ut_metadata_peer_plugin; struct ut_metadata_plugin TORRENT_FINAL : torrent_plugin { ut_metadata_plugin(torrent& t) : m_torrent(t) // , m_metadata_progress(0) , m_metadata_size(0) { // initialize m_metadata_size if (m_torrent.valid_metadata()) metadata(); } bool need_loaded() { return m_torrent.need_loaded(); } virtual void on_unload() TORRENT_OVERRIDE { m_metadata.reset(); } virtual void on_load() TORRENT_OVERRIDE { // initialize m_metadata_size TORRENT_ASSERT(m_torrent.is_loaded()); metadata(); } virtual void on_files_checked() TORRENT_OVERRIDE { // TODO: 2 if we were to initialize m_metadata_size lazily instead, // we would probably be more efficient // initialize m_metadata_size metadata(); } virtual boost::shared_ptr new_connection( peer_connection_handle const& pc) TORRENT_OVERRIDE; int get_metadata_size() const { TORRENT_ASSERT(m_metadata_size > 0); return m_metadata_size; } buffer::const_interval metadata() const { if (!m_torrent.need_loaded()) return buffer::const_interval(NULL, NULL); TORRENT_ASSERT(m_torrent.valid_metadata()); if (!m_metadata) { m_metadata = m_torrent.torrent_file().metadata(); m_metadata_size = m_torrent.torrent_file().metadata_size(); TORRENT_ASSERT(hasher(m_metadata.get(), m_metadata_size).final() == m_torrent.torrent_file().info_hash()); } return buffer::const_interval(m_metadata.get(), m_metadata.get() + m_metadata_size); } bool received_metadata(ut_metadata_peer_plugin& source , char const* buf, int size, int piece, int total_size); // returns a piece of the metadata that // we should request. // returns -1 if we should hold off the request int metadata_request(bool has_metadata); /* // this is called from the peer_connection for // each piece of metadata it receives void metadata_progress(int total_size, int received) { m_metadata_progress += received; m_metadata_size = total_size; m_torrent.set_progress_ppm(boost::int64_t(m_metadata_progress) * 1000000 / m_metadata_size); } */ void on_piece_pass(int) TORRENT_OVERRIDE { // if we became a seed, copy the metadata from // the torrent before it is deallocated if (m_torrent.is_seed()) metadata(); } void metadata_size(int size) { if (m_metadata_size > 0 || size <= 0 || size > 4 * 1024 * 1024) return; m_metadata_size = size; m_metadata.reset(new char[size]); m_requested_metadata.resize(div_round_up(size, 16 * 1024)); } private: torrent& m_torrent; // 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 boost::shared_array m_metadata; // int m_metadata_progress; mutable int m_metadata_size; struct metadata_piece { metadata_piece(): num_requests(0), last_request(min_time()) {} int num_requests; time_point last_request; boost::weak_ptr source; bool operator<(metadata_piece const& rhs) const { return num_requests < rhs.num_requests; } }; // this vector keeps track of how many times each metadata // block has been requested and who we ended up getting it from // std::numeric_limits::max() means we have the piece std::vector m_requested_metadata; // explicitly disallow assignment, to silence msvc warning ut_metadata_plugin& operator=(ut_metadata_plugin const&); }; struct ut_metadata_peer_plugin TORRENT_FINAL : peer_plugin, boost::enable_shared_from_this { friend struct ut_metadata_plugin; ut_metadata_peer_plugin(torrent& t, bt_peer_connection& pc , ut_metadata_plugin& tp) : m_message_index(0) , m_request_limit(min_time()) , m_torrent(t) , m_pc(pc) , m_tp(tp) {} virtual char const* type() const TORRENT_OVERRIDE { return "ut_metadata"; } // can add entries to the extension handshake virtual void add_handshake(entry& h) TORRENT_OVERRIDE { entry& messages = h["m"]; messages["ut_metadata"] = 2; if (m_torrent.valid_metadata()) h["metadata_size"] = m_tp.get_metadata_size(); } // called when the extension handshake from the other end is received virtual bool on_extension_handshake(bdecode_node const& h) TORRENT_OVERRIDE { m_message_index = 0; if (h.type() != bdecode_node::dict_t) return false; bdecode_node messages = h.dict_find_dict("m"); if (!messages) return false; int index = messages.dict_find_int_value("ut_metadata", -1); if (index == -1) return false; m_message_index = index; int metadata_size = h.dict_find_int_value("metadata_size"); if (metadata_size > 0) m_tp.metadata_size(metadata_size); else m_pc.set_has_metadata(false); maybe_send_request(); return true; } void write_metadata_packet(int type, int piece) { TORRENT_ASSERT(type >= 0 && type <= 2); TORRENT_ASSERT(!m_pc.associated_torrent().expired()); #ifndef TORRENT_DISABLE_LOGGING static char const* names[] = {"request", "data", "dont-have"}; char const* n = ""; if (type >= 0 && type < 3) n = names[type]; m_pc.peer_log(peer_log_alert::outgoing_message, "UT_METADATA" , "type: %d (%s) piece: %d", type, n, piece); #endif // abort if the peer doesn't support the metadata extension if (m_message_index == 0) return; entry e; e["msg_type"] = type; e["piece"] = piece; char const* metadata = 0; int metadata_piece_size = 0; if (m_torrent.valid_metadata()) e["total_size"] = m_tp.get_metadata_size(); if (type == 1) { TORRENT_ASSERT(piece >= 0 && piece < int(m_tp.get_metadata_size() + 16 * 1024 - 1)/(16*1024)); TORRENT_ASSERT(m_pc.associated_torrent().lock()->valid_metadata()); TORRENT_ASSERT(m_torrent.valid_metadata()); int offset = piece * 16 * 1024; // unloaded torrents don't have any metadata. Since we're // about to send the metadata, we need it to be loaded if (!m_tp.need_loaded()) return; metadata = m_tp.metadata().begin + offset; metadata_piece_size = (std::min)( int(m_tp.get_metadata_size() - offset), 16 * 1024); TORRENT_ASSERT(metadata_piece_size > 0); TORRENT_ASSERT(offset >= 0); TORRENT_ASSERT(offset + metadata_piece_size <= int(m_tp.get_metadata_size())); } char msg[200]; char* header = msg; char* p = &msg[6]; int len = bencode(p, e); int total_size = 2 + len + metadata_piece_size; namespace io = detail; io::write_uint32(total_size, header); io::write_uint8(bt_peer_connection::msg_extended, header); io::write_uint8(m_message_index, header); m_pc.send_buffer(msg, len + 6); // TODO: we really need to increment the refcounter on the torrent // while this buffer is still in the peer's send buffer if (metadata_piece_size) m_pc.append_const_send_buffer( metadata, metadata_piece_size); m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_extended); m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_metadata); } virtual bool on_extended(int length , int extended_msg, buffer::const_interval body) TORRENT_OVERRIDE { if (extended_msg != 2) return false; if (m_message_index == 0) return false; if (length > 17 * 1024) { #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "UT_METADATA" , "packet too big %d", length); #endif m_pc.disconnect(errors::invalid_metadata_message, op_bittorrent, 2); return true; } if (!m_pc.packet_finished()) return true; int len; entry msg = bdecode(body.begin, body.end, len); if (msg.type() != entry::dictionary_t) { #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "UT_METADATA" , "not a dictionary"); #endif m_pc.disconnect(errors::invalid_metadata_message, op_bittorrent, 2); return true; } entry const* type_ent = msg.find_key("msg_type"); entry const* piece_ent = msg.find_key("piece"); if (type_ent == 0 || type_ent->type() != entry::int_t || piece_ent == 0 || piece_ent->type() != entry::int_t) { #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "UT_METADATA" , "missing or invalid keys"); #endif m_pc.disconnect(errors::invalid_metadata_message, op_bittorrent, 2); return true; } int type = type_ent->integer(); int piece = piece_ent->integer(); #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "UT_METADATA" , "type: %d piece: %d", type, piece); #endif switch (type) { case metadata_req: { if (!m_torrent.valid_metadata() || piece < 0 || piece >= int(m_tp.get_metadata_size() + 16 * 1024 - 1)/(16*1024)) { #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::info, "UT_METADATA" , "have: %d invalid piece %d metadata size: %d" , int(m_torrent.valid_metadata()), piece , m_torrent.valid_metadata() ? int(m_tp.get_metadata_size()) : 0); #endif write_metadata_packet(metadata_dont_have, piece); return true; } if (m_pc.send_buffer_size() < send_buffer_limit) write_metadata_packet(metadata_piece, piece); else if (m_incoming_requests.size() < max_incoming_requests) m_incoming_requests.push_back(piece); else write_metadata_packet(metadata_dont_have, piece); } break; case metadata_piece: { std::vector::iterator i = std::find(m_sent_requests.begin() , m_sent_requests.end(), piece); // unwanted piece? if (i == m_sent_requests.end()) { #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::info, "UT_METADATA" , "UNWANTED / TIMED OUT"); #endif return true; } m_sent_requests.erase(i); entry const* total_size = msg.find_key("total_size"); m_tp.received_metadata(*this, body.begin + len, body.left() - len, piece , (total_size && total_size->type() == entry::int_t) ? total_size->integer() : 0); maybe_send_request(); } break; case metadata_dont_have: { m_request_limit = (std::max)(aux::time_now() + minutes(1), m_request_limit); std::vector::iterator i = std::find(m_sent_requests.begin() , m_sent_requests.end(), piece); // unwanted piece? if (i == m_sent_requests.end()) return true; m_sent_requests.erase(i); } break; default: // unknown message, ignore break; } m_pc.stats_counters().inc_stats_counter(counters::num_incoming_metadata); return true; } virtual void tick() TORRENT_OVERRIDE { maybe_send_request(); while (!m_incoming_requests.empty() && m_pc.send_buffer_size() < send_buffer_limit) { int piece = m_incoming_requests.front(); m_incoming_requests.erase(m_incoming_requests.begin()); write_metadata_packet(metadata_piece, piece); } } void maybe_send_request() { if (m_pc.is_disconnecting()) 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 (!m_torrent.valid_metadata() && m_message_index != 0 && m_sent_requests.size() < 2 && has_metadata()) { int piece = m_tp.metadata_request(m_pc.has_metadata()); if (piece == -1) return; m_sent_requests.push_back(piece); write_metadata_packet(metadata_req, piece); } } bool has_metadata() const { return m_pc.has_metadata() || (aux::time_now() > m_request_limit); } void failed_hash_check(time_point const& now) { m_request_limit = now + seconds(20 + (boost::int64_t(random()) * 50) / UINT_MAX); } private: // this is the message index the remote peer uses // for metadata extension messages. int m_message_index; // this is set to the next time we can request pieces // again. It is updated every time we get a // "I don't have metadata" message, but also when // we receive metadata that fails the infohash check time_point m_request_limit; // request queues std::vector m_sent_requests; std::vector m_incoming_requests; torrent& m_torrent; bt_peer_connection& m_pc; ut_metadata_plugin& m_tp; // explicitly disallow assignment, to silence msvc warning ut_metadata_peer_plugin& operator=(ut_metadata_peer_plugin const&); }; boost::shared_ptr ut_metadata_plugin::new_connection( peer_connection_handle const& pc) { if (pc.type() != peer_connection::bittorrent_connection) return boost::shared_ptr(); bt_peer_connection* c = static_cast(pc.native_handle().get()); return boost::shared_ptr(new ut_metadata_peer_plugin(m_torrent, *c, *this)); } // has_metadata is false if the peer making the request has not announced // that it has metadata. In this case, it shouldn't prevent other peers // from requesting this block by setting a timeout on it. int ut_metadata_plugin::metadata_request(bool has_metadata) { std::vector::iterator i = std::min_element( m_requested_metadata.begin(), m_requested_metadata.end()); if (m_requested_metadata.empty()) { // if we don't know how many pieces there are // just ask for piece 0 m_requested_metadata.resize(1); i = m_requested_metadata.begin(); } int piece = i - m_requested_metadata.begin(); // don't request the same block more than once every 3 seconds time_point now = aux::time_now(); if (m_requested_metadata[piece].last_request != min_time() && total_seconds(now - m_requested_metadata[piece].last_request) < 3) return -1; ++m_requested_metadata[piece].num_requests; // only set the timeout on this block, only if the peer // has metadata. This is to prevent peers with no metadata // to starve out sending requests to peers with metadata if (has_metadata) m_requested_metadata[piece].last_request = now; return piece; } bool ut_metadata_plugin::received_metadata( ut_metadata_peer_plugin& source , char const* buf, int size, int piece, int total_size) { if (m_torrent.valid_metadata()) { #ifndef TORRENT_DISABLE_LOGGING source.m_pc.peer_log(peer_log_alert::info, "UT_METADATA" , "already have metadata"); #endif m_torrent.add_redundant_bytes(size, torrent::piece_unknown); return false; } if (!m_metadata) { // verify the total_size if (total_size <= 0 || total_size > m_torrent.session().settings().get_int(settings_pack::max_metadata_size)) { #ifndef TORRENT_DISABLE_LOGGING source.m_pc.peer_log(peer_log_alert::info, "UT_METADATA" , "metadata size too big: %d", total_size); #endif // #error post alert return false; } m_metadata.reset(new char[total_size]); m_requested_metadata.resize(div_round_up(total_size, 16 * 1024)); m_metadata_size = total_size; } if (piece < 0 || piece >= int(m_requested_metadata.size())) { #ifndef TORRENT_DISABLE_LOGGING source.m_pc.peer_log(peer_log_alert::info, "UT_METADATA" , "piece: %d INVALID", piece); #endif return false; } if (total_size != m_metadata_size) { #ifndef TORRENT_DISABLE_LOGGING source.m_pc.peer_log(peer_log_alert::info, "UT_METADATA" , "total_size: %d INCONSISTENT WITH: %d" , total_size, m_metadata_size); #endif // they disagree about the size! return false; } if (piece * 16 * 1024 + size > m_metadata_size) { // this piece is invalid return false; } std::memcpy(&m_metadata[piece * 16 * 1024], buf, size); // mark this piece has 'have' m_requested_metadata[piece].num_requests = (std::numeric_limits::max)(); m_requested_metadata[piece].source = source.shared_from_this(); bool have_all = std::count_if(m_requested_metadata.begin() , m_requested_metadata.end(), boost::bind(&metadata_piece::num_requests, _1) == (std::numeric_limits::max)()) == int(m_requested_metadata.size()); if (!have_all) return false; if (!m_torrent.set_metadata(&m_metadata[0], m_metadata_size)) { if (!m_torrent.valid_metadata()) { time_point now = aux::time_now(); // any peer that we downloaded metadata from gets a random time // penalty, from 5 to 30 seconds or so. During this time we don't // make any metadata requests from those peers (to mix it up a bit // of which peers we use) // if we only have one block, and thus requested it from a single // peer, we bump up the retry time a lot more to try other peers bool single_peer = m_requested_metadata.size() == 1; for (int i = 0; i < int(m_requested_metadata.size()); ++i) { m_requested_metadata[i].num_requests = 0; boost::shared_ptr peer = m_requested_metadata[i].source.lock(); if (!peer) continue; peer->failed_hash_check(single_peer ? now + minutes(5) : now); } } return false; } // free our copy of the metadata and get a reference // to the torrent's copy instead. No need to keep two // identical copies around m_metadata.reset(); metadata(); // clear the storage for the bitfield std::vector().swap(m_requested_metadata); return true; } } } namespace libtorrent { boost::shared_ptr create_ut_metadata_plugin(torrent_handle const& th, void*) { torrent* t = th.native_handle().get(); // don't add this extension if the torrent is private if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr(); return boost::shared_ptr(new ut_metadata_plugin(*t)); } } #endif libtorrent-rasterbar-1.1.13/src/ut_pex.cpp000066400000000000000000000540401351156116000205450ustar00rootroot00000000000000/* Copyright (c) 2006-2018, MassaRoddel, 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/config.hpp" #include "libtorrent/peer_connection.hpp" #include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/peer_connection_handle.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/random.hpp" #include "libtorrent/socket_type.hpp" // for is_utp #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/extensions/ut_pex.hpp" #ifndef TORRENT_DISABLE_LOGGING #include "libtorrent/lazy_entry.hpp" #endif #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { namespace { static const char extension_name[] = "ut_pex"; enum { extension_index = 1, max_peer_entries = 100 }; bool send_peer(peer_connection const& p) { // don't send out those peers that we haven't connected to // (that have connected to us) and that aren't sharing their // listening port if (!p.is_outgoing() && !p.received_listen_port()) return false; // don't send out peers that we haven't successfully connected to if (p.is_connecting()) return false; if (p.in_handshake()) return false; return true; } struct ut_pex_plugin TORRENT_FINAL : torrent_plugin { // randomize when we rebuild the pex message // to evenly spread it out across all torrents // the more torrents we have, the longer we can // delay the rebuilding ut_pex_plugin(torrent& t) : m_torrent(t) , m_last_msg(min_time()) , m_peers_in_message(0) {} virtual boost::shared_ptr new_connection( peer_connection_handle const& pc) TORRENT_OVERRIDE; std::vector& get_ut_pex_msg() { return m_ut_pex_msg; } int peers_in_msg() const { return m_peers_in_message; } // the second tick of the torrent // each minute the new lists of "added" + "added.f" and "dropped" // are calculated here and the pex message is created // each peer connection will use this message // max_peer_entries limits the packet size virtual void tick() TORRENT_OVERRIDE { time_point now = aux::time_now(); if (now - seconds(60) < m_last_msg) return; m_last_msg = now; int num_peers = m_torrent.num_peers(); if (num_peers == 0) return; entry pex; std::string& pla = pex["added"].string(); std::string& pld = pex["dropped"].string(); std::string& plf = pex["added.f"].string(); std::back_insert_iterator pla_out(pla); std::back_insert_iterator pld_out(pld); std::back_insert_iterator plf_out(plf); #if TORRENT_USE_IPV6 std::string& pla6 = pex["added6"].string(); std::string& pld6 = pex["dropped6"].string(); std::string& plf6 = pex["added6.f"].string(); std::back_insert_iterator pla6_out(pla6); std::back_insert_iterator pld6_out(pld6); std::back_insert_iterator plf6_out(plf6); #endif std::set dropped; m_old_peers.swap(dropped); m_peers_in_message = 0; int num_added = 0; for (torrent::peer_iterator i = m_torrent.begin() , end(m_torrent.end()); i != end; ++i) { peer_connection* peer = *i; if (!send_peer(*peer)) continue; tcp::endpoint remote = peer->remote(); m_old_peers.insert(remote); std::set::iterator di = dropped.find(remote); if (di == dropped.end()) { // don't write too big of a package if (num_added >= max_peer_entries) break; // only send proper bittorrent peers if (peer->type() != peer_connection::bittorrent_connection) continue; bt_peer_connection* p = static_cast(peer); // if the peer has told us which port its listening on, // use that port. But only if we didn't connect to the peer. // if we connected to it, use the port we know works torrent_peer *pi = 0; if (!p->is_outgoing() && (pi = peer->peer_info_struct()) && pi->port > 0) remote.port(pi->port); // no supported flags to set yet // 0x01 - peer supports encryption // 0x02 - peer is a seed // 0x04 - supports uTP. This is only a positive flags // passing 0 doesn't mean the peer doesn't // support uTP // 0x08 - supports hole punching protocol. If this // flag is received from a peer, it can be // used as a rendezvous point in case direct // connections to the peer fail int flags = p->is_seed() ? 2 : 0; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) flags |= p->supports_encryption() ? 1 : 0; #endif flags |= is_utp(*p->get_socket()) ? 4 : 0; flags |= p->supports_holepunch() ? 8 : 0; // i->first was added since the last time if (remote.address().is_v4()) { detail::write_endpoint(remote, pla_out); detail::write_uint8(flags, plf_out); } #if TORRENT_USE_IPV6 else { detail::write_endpoint(remote, pla6_out); detail::write_uint8(flags, plf6_out); } #endif ++num_added; ++m_peers_in_message; } else { // this was in the previous message // so, it wasn't dropped dropped.erase(di); } } for (std::set::const_iterator i = dropped.begin() , end(dropped.end()); i != end; ++i) { if (i->address().is_v4()) detail::write_endpoint(*i, pld_out); #if TORRENT_USE_IPV6 else detail::write_endpoint(*i, pld6_out); #endif ++m_peers_in_message; } m_ut_pex_msg.clear(); bencode(std::back_inserter(m_ut_pex_msg), pex); } private: torrent& m_torrent; std::set m_old_peers; time_point m_last_msg; std::vector m_ut_pex_msg; int m_peers_in_message; // explicitly disallow assignment, to silence msvc warning ut_pex_plugin& operator=(ut_pex_plugin const&); }; struct ut_pex_peer_plugin TORRENT_FINAL : peer_plugin { ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp) : m_torrent(t) , m_pc(pc) , m_tp(tp) , m_last_msg(min_time()) , m_message_index(0) , m_first_time(true) { const int num_pex_timers = sizeof(m_last_pex)/sizeof(m_last_pex[0]); for (int i = 0; i < num_pex_timers; ++i) { m_last_pex[i]= min_time(); } } virtual char const* type() const TORRENT_OVERRIDE { return "ut_pex"; } virtual void add_handshake(entry& h) TORRENT_OVERRIDE { entry& messages = h["m"]; messages[extension_name] = extension_index; } virtual bool on_extension_handshake(bdecode_node const& h) TORRENT_OVERRIDE { m_message_index = 0; if (h.type() != bdecode_node::dict_t) return false; bdecode_node messages = h.dict_find_dict("m"); if (!messages) return false; int index = int(messages.dict_find_int_value(extension_name, -1)); if (index == -1) return false; m_message_index = index; return true; } virtual bool on_extended(int length, int msg, buffer::const_interval body) TORRENT_OVERRIDE { if (msg != extension_index) return false; if (m_message_index == 0) return false; if (length > 500 * 1024) { m_pc.disconnect(errors::pex_message_too_large, op_bittorrent, 2); return true; } if (body.left() < length) return true; time_point now = aux::time_now(); if (now - seconds(60) < m_last_pex[0]) { // this client appears to be trying to flood us // with pex messages. Don't allow that. m_pc.disconnect(errors::too_frequent_pex, op_bittorrent); return true; } const int num_pex_timers = sizeof(m_last_pex)/sizeof(m_last_pex[0]); for (int i = 0; i < num_pex_timers-1; ++i) m_last_pex[i] = m_last_pex[i+1]; m_last_pex[num_pex_timers-1] = now; bdecode_node pex_msg; error_code ec; int ret = bdecode(body.begin, body.end, pex_msg, ec); if (ret != 0 || pex_msg.type() != bdecode_node::dict_t) { m_pc.disconnect(errors::invalid_pex_message, op_bittorrent, 2); return true; } bdecode_node p = pex_msg.dict_find_string("dropped"); #ifndef TORRENT_DISABLE_LOGGING int num_dropped = 0; int num_added = 0; if (p) num_dropped += p.string_length()/6; #endif if (p) { int num_peers = p.string_length() / 6; char const* in = p.string_ptr(); for (int i = 0; i < num_peers; ++i) { tcp::endpoint adr = detail::read_v4_endpoint(in); peers4_t::value_type v(adr.address().to_v4().to_bytes(), adr.port()); peers4_t::iterator j = std::lower_bound(m_peers.begin(), m_peers.end(), v); if (j != m_peers.end() && *j == v) m_peers.erase(j); } } p = pex_msg.dict_find_string("added"); bdecode_node pf = pex_msg.dict_find_string("added.f"); bool peers_added = false; #ifndef TORRENT_DISABLE_LOGGING if (p) num_added += p.string_length() / 6; #endif if (p && pf && pf.string_length() == p.string_length() / 6) { int num_peers = pf.string_length(); char const* in = p.string_ptr(); char const* fin = pf.string_ptr(); for (int i = 0; i < num_peers; ++i) { tcp::endpoint adr = detail::read_v4_endpoint(in); char flags = *fin++; if (int(m_peers.size()) >= m_torrent.settings().get_int(settings_pack::max_pex_peers)) break; // ignore local addresses unless the peer is local to us if (is_local(adr.address()) && !is_local(m_pc.remote().address())) continue; peers4_t::value_type v(adr.address().to_v4().to_bytes(), adr.port()); peers4_t::iterator j = std::lower_bound(m_peers.begin(), m_peers.end(), v); // do we already know about this peer? if (j != m_peers.end() && *j == v) continue; m_peers.insert(j, v); m_torrent.add_peer(adr, peer_info::pex, flags); peers_added = true; } } #if TORRENT_USE_IPV6 bdecode_node p6 = pex_msg.dict_find("dropped6"); #ifndef TORRENT_DISABLE_LOGGING if (p6) num_dropped += p6.string_length() / 18; #endif if (p6 != 0 && p6.type() == bdecode_node::string_t) { int num_peers = p6.string_length() / 18; char const* in = p6.string_ptr(); for (int i = 0; i < num_peers; ++i) { tcp::endpoint adr = detail::read_v6_endpoint(in); peers6_t::value_type v(adr.address().to_v6().to_bytes(), adr.port()); peers6_t::iterator j = std::lower_bound(m_peers6.begin(), m_peers6.end(), v); if (j != m_peers6.end() && *j == v) m_peers6.erase(j); } } p6 = pex_msg.dict_find("added6"); #ifndef TORRENT_DISABLE_LOGGING if (p6) num_added += p6.string_length() / 18; #endif bdecode_node p6f = pex_msg.dict_find("added6.f"); if (p6 != 0 && p6f != 0 && p6.type() == bdecode_node::string_t && p6f.type() == bdecode_node::string_t && p6f.string_length() == p6.string_length() / 18) { int num_peers = p6f.string_length(); char const* in = p6.string_ptr(); char const* fin = p6f.string_ptr(); for (int i = 0; i < num_peers; ++i) { tcp::endpoint adr = detail::read_v6_endpoint(in); char flags = *fin++; // ignore local addresses unless the peer is local to us if (is_local(adr.address()) && !is_local(m_pc.remote().address())) continue; if (int(m_peers6.size()) >= m_torrent.settings().get_int(settings_pack::max_pex_peers)) break; peers6_t::value_type v(adr.address().to_v6().to_bytes(), adr.port()); peers6_t::iterator j = std::lower_bound(m_peers6.begin(), m_peers6.end(), v); // do we already know about this peer? if (j != m_peers6.end() && *j == v) continue; m_peers6.insert(j, v); m_torrent.add_peer(adr, peer_info::pex, flags); peers_added = true; } } #endif #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::incoming_message, "PEX", "dropped: %d added: %d" , num_dropped, num_added); #endif m_pc.stats_counters().inc_stats_counter(counters::num_incoming_pex); if (peers_added) m_torrent.do_connect_boost(); return true; } // the peers second tick // every minute we send a pex message virtual void tick() TORRENT_OVERRIDE { // no handshake yet if (!m_message_index) return; time_point now = aux::time_now(); if (now - seconds(60) < m_last_msg) { #ifndef TORRENT_DISABLE_LOGGING // m_pc.peer_log(peer_log_alert::info, "PEX", "waiting: %d seconds to next msg" // , int(total_seconds(seconds(60) - (now - m_last_msg)))); #endif return; } static time_point global_last = min_time(); int num_peers = m_torrent.num_peers(); if (num_peers <= 1) return; // don't send pex messages more often than 1 every 100 ms, and // allow pex messages to be sent 5 seconds apart if there isn't // contention int delay = (std::min)((std::max)(60000 / num_peers, 100), 3000); if (now - milliseconds(delay) < global_last) { #ifndef TORRENT_DISABLE_LOGGING // m_pc.peer_log(peer_log_alert::info, "PEX", "global-wait: %d" // , int(total_seconds(milliseconds(delay) - (now - global_last)))); #endif return; } // this will allow us to catch up, even if our timer // has lower resolution than delay if (global_last == min_time()) global_last = now; else global_last += milliseconds(delay); m_last_msg = now; if (m_first_time) { send_ut_peer_list(); m_first_time = false; } else { send_ut_peer_diff(); } } void send_ut_peer_diff() { // if there's no change in out peer set, don't send anything if (m_tp.peers_in_msg() == 0) return; std::vector const& pex_msg = m_tp.get_ut_pex_msg(); char msg[6]; char* ptr = msg; detail::write_uint32(1 + 1 + pex_msg.size(), ptr); detail::write_uint8(bt_peer_connection::msg_extended, ptr); detail::write_uint8(m_message_index, ptr); m_pc.send_buffer(msg, sizeof(msg)); m_pc.send_buffer(&pex_msg[0], pex_msg.size()); m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_extended); m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_pex); #ifndef TORRENT_DISABLE_LOGGING bdecode_node m; error_code ec; int ret = bdecode(&pex_msg[0], &pex_msg[0] + pex_msg.size(), m, ec); TORRENT_ASSERT(ret == 0); TORRENT_ASSERT(!ec); TORRENT_UNUSED(ret); int num_dropped = 0; int num_added = 0; bdecode_node e = m.dict_find_string("added"); if (e) num_added += e.string_length() / 6; e = m.dict_find_string("dropped"); if (e) num_dropped += e.string_length() / 6; e = m.dict_find_string("added6"); if (e) num_added += e.string_length() / 18; e = m.dict_find_string("dropped6"); if (e) num_dropped += e.string_length() / 18; m_pc.peer_log(peer_log_alert::outgoing_message, "PEX_DIFF", "dropped: %d added: %d msg_size: %d" , num_dropped, num_added, int(pex_msg.size())); #endif } void send_ut_peer_list() { entry pex; // leave the dropped string empty pex["dropped"].string(); std::string& pla = pex["added"].string(); std::string& plf = pex["added.f"].string(); std::back_insert_iterator pla_out(pla); std::back_insert_iterator plf_out(plf); #if TORRENT_USE_IPV6 pex["dropped6"].string(); std::string& pla6 = pex["added6"].string(); std::string& plf6 = pex["added6.f"].string(); std::back_insert_iterator pla6_out(pla6); std::back_insert_iterator plf6_out(plf6); #endif int num_added = 0; for (torrent::peer_iterator i = m_torrent.begin() , end(m_torrent.end()); i != end; ++i) { peer_connection* peer = *i; if (!send_peer(*peer)) continue; // don't write too big of a package if (num_added >= max_peer_entries) break; // only send proper bittorrent peers if (peer->type() != peer_connection::bittorrent_connection) continue; bt_peer_connection* p = static_cast(peer); // no supported flags to set yet // 0x01 - peer supports encryption // 0x02 - peer is a seed // 0x04 - supports uTP. This is only a positive flags // passing 0 doesn't mean the peer doesn't // support uTP // 0x08 - supports hole punching protocol. If this // flag is received from a peer, it can be // used as a rendezvous point in case direct // connections to the peer fail int flags = p->is_seed() ? 2 : 0; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) flags |= p->supports_encryption() ? 1 : 0; #endif flags |= is_utp(*p->get_socket()) ? 4 : 0; flags |= p->supports_holepunch() ? 8 : 0; tcp::endpoint remote = peer->remote(); torrent_peer *pi = 0; if (!p->is_outgoing() && (pi = peer->peer_info_struct()) && pi->port > 0) remote.port(pi->port); // i->first was added since the last time if (remote.address().is_v4()) { detail::write_endpoint(remote, pla_out); detail::write_uint8(flags, plf_out); } #if TORRENT_USE_IPV6 else { detail::write_endpoint(remote, pla6_out); detail::write_uint8(flags, plf6_out); } #endif ++num_added; } std::vector pex_msg; bencode(std::back_inserter(pex_msg), pex); char msg[6]; char* ptr = msg; detail::write_uint32(1 + 1 + pex_msg.size(), ptr); detail::write_uint8(bt_peer_connection::msg_extended, ptr); detail::write_uint8(m_message_index, ptr); m_pc.send_buffer(msg, sizeof(msg)); m_pc.send_buffer(&pex_msg[0], pex_msg.size()); m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_extended); m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_pex); #ifndef TORRENT_DISABLE_LOGGING m_pc.peer_log(peer_log_alert::outgoing_message, "PEX_FULL" , "added: %d msg_size: %d", num_added, int(pex_msg.size())); #endif } torrent& m_torrent; peer_connection& m_pc; ut_pex_plugin& m_tp; // stores all peers this this peer is connected to. These lists // are updated with each pex message and are limited in size // to protect against malicious clients. These lists are also // used for looking up which peer a peer that supports holepunch // came from. // these are vectors to save memory and keep the items close // together for performance. Inserting and removing is relatively // cheap since the lists' size is limited typedef std::vector > peers4_t; peers4_t m_peers; #if TORRENT_USE_IPV6 typedef std::vector > peers6_t; peers6_t m_peers6; #endif // the last pex messages we received // [0] is the oldest one. There is a problem with // rate limited connections, because we may sit // for a long time, accumulating pex messages, and // then once we read from the socket it will look like // we received them all back to back. That's why // we look at 6 pex messages back. time_point m_last_pex[6]; time_point m_last_msg; int m_message_index; // this is initialized to true, and set to // false after the first pex message has been sent. // it is used to know if a diff message or a) ful // message should be sent. bool m_first_time; // explicitly disallow assignment, to silence msvc warning ut_pex_peer_plugin& operator=(ut_pex_peer_plugin const&); }; boost::shared_ptr ut_pex_plugin::new_connection(peer_connection_handle const& pc) { if (pc.type() != peer_connection::bittorrent_connection) return boost::shared_ptr(); return boost::shared_ptr(new ut_pex_peer_plugin(m_torrent , *pc.native_handle(), *this)); } } } namespace libtorrent { boost::shared_ptr create_ut_pex_plugin(torrent_handle const& th, void*) { torrent* t = th.native_handle().get(); if (t->torrent_file().priv() || (t->torrent_file().is_i2p() && !t->settings().get_bool(settings_pack::allow_i2p_mixed))) { return boost::shared_ptr(); } return boost::shared_ptr(new ut_pex_plugin(*t)); } bool was_introduced_by(peer_plugin const* pp, tcp::endpoint const& ep) { ut_pex_peer_plugin const* p = static_cast(pp); #if TORRENT_USE_IPV6 if (ep.address().is_v4()) { #endif ut_pex_peer_plugin::peers4_t::value_type v(ep.address().to_v4().to_bytes(), ep.port()); ut_pex_peer_plugin::peers4_t::const_iterator i = std::lower_bound(p->m_peers.begin(), p->m_peers.end(), v); return i != p->m_peers.end() && *i == v; #if TORRENT_USE_IPV6 } else { ut_pex_peer_plugin::peers6_t::value_type v(ep.address().to_v6().to_bytes(), ep.port()); ut_pex_peer_plugin::peers6_t::const_iterator i = std::lower_bound(p->m_peers6.begin(), p->m_peers6.end(), v); return i != p->m_peers6.end() && *i == v; } #endif } } #endif libtorrent-rasterbar-1.1.13/src/utf8.cpp000066400000000000000000000157231351156116000201340ustar00rootroot00000000000000/* Copyright (c) 2012-2018, 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/config.hpp" // on windows we need these functions for // convert_to_native and convert_from_native #if TORRENT_USE_WSTRING || defined TORRENT_WINDOWS #include #include "libtorrent/utf8.hpp" #include "libtorrent/ConvertUTF.h" #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-member-function" #endif namespace libtorrent { namespace { // ==== utf-8 -> wide === template struct convert_to_wide { static utf8_conv_result_t convert(UTF8 const** src_start , UTF8 const* src_end , std::wstring& wide) { TORRENT_UNUSED(src_start); TORRENT_UNUSED(src_end); TORRENT_UNUSED(wide); return source_illegal; } }; // ==== utf-8 -> utf-32 === template<> struct convert_to_wide<4> { static utf8_conv_result_t convert(char const** src_start , char const* src_end , std::wstring& wide) { wchar_t* dst_start = &wide[0]; int ret = ConvertUTF8toUTF32( reinterpret_cast(src_start) , reinterpret_cast(src_end) , reinterpret_cast(&dst_start) , reinterpret_cast(dst_start + wide.size()) , lenientConversion); if (ret == sourceIllegal) { // assume Latin-1 wide.clear(); std::copy(reinterpret_cast(*src_start) ,reinterpret_cast(src_end) , std::back_inserter(wide)); return static_cast(ret); } wide.resize(dst_start - wide.c_str()); return static_cast(ret); } }; // ==== utf-8 -> utf-16 === template<> struct convert_to_wide<2> { static utf8_conv_result_t convert(char const** src_start , char const* src_end , std::wstring& wide) { wchar_t* dst_start = &wide[0]; int ret = ConvertUTF8toUTF16( reinterpret_cast(src_start) , reinterpret_cast(src_end) , reinterpret_cast(&dst_start) , reinterpret_cast(dst_start + wide.size()) , lenientConversion); if (ret == sourceIllegal) { // assume Latin-1 wide.clear(); std::copy(reinterpret_cast(*src_start) , reinterpret_cast(src_end) , std::back_inserter(wide)); return static_cast(ret); } wide.resize(dst_start - wide.c_str()); return static_cast(ret); } }; // ==== wide -> utf-8 === template struct convert_from_wide { static utf8_conv_result_t convert(wchar_t const** src_start , wchar_t const* src_end , std::string& utf8) { TORRENT_UNUSED(src_start); TORRENT_UNUSED(src_end); TORRENT_UNUSED(utf8); return source_illegal; } }; // ==== utf-32 -> utf-8 === template<> struct convert_from_wide<4> { static utf8_conv_result_t convert(wchar_t const** src_start , wchar_t const* src_end , std::string& utf8) { char* dst_start = &utf8[0]; int ret = ConvertUTF32toUTF8( reinterpret_cast(src_start) , reinterpret_cast(src_end) , reinterpret_cast(&dst_start) , reinterpret_cast(dst_start + utf8.size()) , lenientConversion); utf8.resize(dst_start - &utf8[0]); return static_cast(ret); } }; // ==== utf-16 -> utf-8 === template<> struct convert_from_wide<2> { static utf8_conv_result_t convert(wchar_t const** src_start , wchar_t const* src_end , std::string& utf8) { char* dst_start = &utf8[0]; int ret = ConvertUTF16toUTF8( reinterpret_cast(src_start) , reinterpret_cast(src_end) , reinterpret_cast(&dst_start) , reinterpret_cast(dst_start + utf8.size()) , lenientConversion); utf8.resize(dst_start - &utf8[0]); return static_cast(ret); } }; } // anonymous namespace utf8_conv_result_t utf8_wchar(std::string const& utf8, std::wstring &wide) { // allocate space for worst-case wide.resize(utf8.size()); char const* src_start = utf8.c_str(); return convert_to_wide::convert( &src_start, src_start + utf8.size(), wide); } utf8_conv_result_t wchar_utf8(std::wstring const& wide, std::string &utf8) { // allocate space for worst-case utf8.resize(wide.size() * 6); if (wide.empty()) return conversion_ok; wchar_t const* src_start = wide.c_str(); return convert_from_wide::convert( &src_start, src_start + wide.size(), utf8); } // returns the unicode codepoint and the number of bytes of the utf8 sequence // that was parsed. The codepoint is -1 if it's invalid std::pair parse_utf8_codepoint(char const* str, int const len) { int const sequence_len = trailingBytesForUTF8[static_cast(*str)] + 1; if (sequence_len > len) return std::make_pair(-1, len); if (sequence_len > 4) { return std::make_pair(-1, sequence_len); } if (!isLegalUTF8(reinterpret_cast(str), sequence_len)) { return std::make_pair(-1, sequence_len); } boost::uint32_t ch = 0; for (int i = 0; i < sequence_len; ++i) { ch <<= 6; ch += static_cast(str[i]); } ch -= offsetsFromUTF8[sequence_len-1]; if (ch > 0x7fffffff) { return std::make_pair(-1, sequence_len); } return std::make_pair(static_cast(ch), sequence_len); } } #ifdef __clang__ #pragma clang diagnostic pop #endif #endif libtorrent-rasterbar-1.1.13/src/utp_socket_manager.cpp000066400000000000000000000315011351156116000231100ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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/utp_stream.hpp" #include "libtorrent/udp_socket.hpp" #include "libtorrent/utp_socket_manager.hpp" #include "libtorrent/instantiate_connection.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/socket.hpp" // for TORRENT_HAS_DONT_FRAGMENT #include "libtorrent/broadcast_socket.hpp" // for is_teredo #include "libtorrent/random.hpp" #include "libtorrent/performance_counters.hpp" #include "libtorrent/aux_/time.hpp" // for aux::time_now() // #define TORRENT_DEBUG_MTU 1135 namespace libtorrent { utp_socket_manager::utp_socket_manager(aux::session_settings const& sett , udp_socket& s , counters& cnt , void* ssl_context , incoming_utp_callback_t cb) : m_sock(s) , m_cb(cb) , m_deferred_ack(0) , m_last_socket(0) , m_new_connection(-1) , m_sett(sett) , m_last_route_update(min_time()) , m_last_if_update(min_time()) , m_sock_buf_size(0) , m_counters(cnt) , m_mtu_idx(0) , m_ssl_context(ssl_context) { m_restrict_mtu.fill(65536); } utp_socket_manager::~utp_socket_manager() { for (socket_map_t::iterator i = m_utp_sockets.begin() , end(m_utp_sockets.end()); i != end; ++i) { delete_utp_impl(i->second); } } void utp_socket_manager::tick(time_point now) { for (socket_map_t::iterator i = m_utp_sockets.begin() , end(m_utp_sockets.end()); i != end;) { if (should_delete(i->second)) { delete_utp_impl(i->second); if (m_last_socket == i->second) m_last_socket = 0; m_utp_sockets.erase(i++); continue; } tick_utp_impl(i->second, now); ++i; } } void utp_socket_manager::mtu_for_dest(address const& addr, int& link_mtu, int& utp_mtu) { int mtu = 0; if (is_teredo(addr)) mtu = TORRENT_TEREDO_MTU; else mtu = TORRENT_ETHERNET_MTU; #if defined __APPLE__ // apple has a very strange loopback. It appears you can't // send messages of the reported MTU size, and you don't get // EWOULDBLOCK either. if (is_loopback(addr)) { if (is_teredo(addr)) mtu = TORRENT_TEREDO_MTU; else mtu = TORRENT_ETHERNET_MTU; } #endif // clamp the MTU within reasonable bounds if (mtu < TORRENT_INET_MIN_MTU) mtu = TORRENT_INET_MIN_MTU; else if (mtu > TORRENT_INET_MAX_MTU) mtu = TORRENT_INET_MAX_MTU; link_mtu = mtu; mtu -= TORRENT_UDP_HEADER; if (m_sock.get_proxy_settings().type == settings_pack::socks5 || m_sock.get_proxy_settings().type == settings_pack::socks5_pw) { // this is for the IP layer address proxy_addr = m_sock.proxy_addr().address(); if (proxy_addr.is_v4()) mtu -= TORRENT_IPV4_HEADER; else mtu -= TORRENT_IPV6_HEADER; // this is for the SOCKS layer mtu -= TORRENT_SOCKS5_HEADER; // the address field in the SOCKS header if (addr.is_v4()) mtu -= 4; else mtu -= 16; } else { if (addr.is_v4()) mtu -= TORRENT_IPV4_HEADER; else mtu -= TORRENT_IPV6_HEADER; } utp_mtu = (std::min)(mtu, restrict_mtu()); } void utp_socket_manager::send_packet(udp::endpoint const& ep, char const* p , int len, error_code& ec, int flags) { #if !defined TORRENT_HAS_DONT_FRAGMENT && !defined TORRENT_DEBUG_MTU TORRENT_UNUSED(flags); #endif if (!m_sock.is_open()) { ec = boost::asio::error::operation_aborted; return; } #ifdef TORRENT_DEBUG_MTU // drop packets that exceed the debug MTU if ((flags & dont_fragment) && len > TORRENT_DEBUG_MTU) return; #endif #ifdef TORRENT_HAS_DONT_FRAGMENT error_code tmp; if (flags & utp_socket_manager::dont_fragment) { m_sock.set_option(libtorrent::dont_fragment(true), tmp); TORRENT_ASSERT_VAL(!tmp, tmp.message()); } #endif m_sock.send(ep, p, len, ec, udp_socket::peer_connection); #ifdef TORRENT_HAS_DONT_FRAGMENT if (flags & utp_socket_manager::dont_fragment) { m_sock.set_option(libtorrent::dont_fragment(false), tmp); TORRENT_ASSERT_VAL(!tmp, tmp.message()); } #endif } int utp_socket_manager::local_port(error_code& ec) const { return m_sock.local_endpoint(ec).port(); } tcp::endpoint utp_socket_manager::local_endpoint(address const& remote, error_code& ec) const { tcp::endpoint socket_ep = m_sock.local_endpoint(ec); // first enumerate the routes in the routing table if (aux::time_now() - seconds(60) > m_last_route_update) { m_last_route_update = aux::time_now(); error_code err; m_routes = enum_routes(m_sock.get_io_service(), err); if (err) return socket_ep; } if (m_routes.empty()) return socket_ep; // then find the best match ip_route* best = &m_routes[0]; for (std::vector::iterator i = m_routes.begin() , end(m_routes.end()); i != end; ++i) { if (is_any(i->destination) && i->destination.is_v4() == remote.is_v4()) { best = &*i; break; } if (match_addr_mask(remote, i->destination, i->netmask)) { best = &*i; break; } } // best now tells us which interface we would send over // for this target. Now figure out what the local address // is for that interface if (aux::time_now() - seconds(60) > m_last_if_update) { m_last_if_update = aux::time_now(); error_code err; m_interfaces = enum_net_interfaces(m_sock.get_io_service(), err); if (err) return socket_ep; } for (std::vector::iterator i = m_interfaces.begin() , end(m_interfaces.end()); i != end; ++i) { if (i->interface_address.is_v4() != remote.is_v4()) continue; if (strcmp(best->name, i->name) == 0) return tcp::endpoint(i->interface_address, socket_ep.port()); } return socket_ep; } bool utp_socket_manager::incoming_packet(error_code const& ec, udp::endpoint const& ep , char const* p, int size) { // TODO: 2 we may want to take ec into account here. possibly close // connections quicker TORRENT_UNUSED(ec); // UTP_LOGV("incoming packet size:%d\n", size); if (size < int(sizeof(utp_header))) return false; utp_header const* ph = reinterpret_cast(p); // UTP_LOGV("incoming packet version:%d\n", int(ph->get_version())); if (ph->get_version() != 1) return false; const time_point receive_time = clock_type::now(); // parse out connection ID and look for existing // connections. If found, forward to the utp_stream. boost::uint16_t id = ph->connection_id; // first test to see if it's the same socket as last time // in most cases it is if (m_last_socket && utp_match(m_last_socket, ep, id)) { return utp_incoming_packet(m_last_socket, p, size, ep, receive_time); } if (m_deferred_ack) { utp_send_ack(m_deferred_ack); m_deferred_ack = NULL; } std::pair r = m_utp_sockets.equal_range(id); for (; r.first != r.second; ++r.first) { if (!utp_match(r.first->second, ep, id)) continue; bool ret = utp_incoming_packet(r.first->second, p, size, ep, receive_time); if (ret) m_last_socket = r.first->second; return ret; } // UTP_LOGV("incoming packet id:%d source:%s\n", id, print_endpoint(ep).c_str()); if (!m_sett.get_bool(settings_pack::enable_incoming_utp)) return false; // if not found, see if it's a SYN packet, if it is, // create a new utp_stream if (ph->get_type() == ST_SYN) { // possible SYN flood. Just ignore if (int(m_utp_sockets.size()) > m_sett.get_int(settings_pack::connections_limit) * 2) return false; // UTP_LOGV("not found, new connection id:%d\n", m_new_connection); boost::shared_ptr c(new (std::nothrow) socket_type(m_sock.get_io_service())); if (!c) return false; TORRENT_ASSERT(m_new_connection == -1); // create the new socket with this ID m_new_connection = id; instantiate_connection(m_sock.get_io_service(), aux::proxy_settings(), *c , m_ssl_context, this, true, false); utp_stream* str = NULL; #ifdef TORRENT_USE_OPENSSL if (is_ssl(*c)) str = &c->get >()->next_layer(); else #endif str = c->get(); TORRENT_ASSERT(str); int link_mtu, utp_mtu; mtu_for_dest(ep.address(), link_mtu, utp_mtu); utp_init_mtu(str->get_impl(), link_mtu, utp_mtu); bool ret = utp_incoming_packet(str->get_impl(), p, size, ep, receive_time); if (!ret) return false; m_last_socket = str->get_impl(); m_cb(c); // the connection most likely changed its connection ID here // we need to move it to the correct ID return true; } if (ph->get_type() == ST_RESET) return false; // #error send reset return false; } void utp_socket_manager::subscribe_writable(utp_socket_impl* s) { TORRENT_ASSERT(std::find(m_stalled_sockets.begin(), m_stalled_sockets.end() , s) == m_stalled_sockets.end()); m_stalled_sockets.push_back(s); } void utp_socket_manager::writable() { std::vector stalled_sockets; m_stalled_sockets.swap(stalled_sockets); for (std::vector::iterator i = stalled_sockets.begin() , end(stalled_sockets.end()); i != end; ++i) { utp_socket_impl* s = *i; utp_writable(s); } } void utp_socket_manager::socket_drained() { if (m_deferred_ack) { utp_socket_impl* s = m_deferred_ack; m_deferred_ack = NULL; utp_send_ack(s); } std::vector drained_event; m_drained_event.swap(drained_event); for (std::vector::iterator i = drained_event.begin() , end(drained_event.end()); i != end; ++i) { utp_socket_impl* s = *i; utp_socket_drained(s); } } void utp_socket_manager::defer_ack(utp_socket_impl* s) { TORRENT_ASSERT(m_deferred_ack == NULL || m_deferred_ack == s); m_deferred_ack = s; } void utp_socket_manager::subscribe_drained(utp_socket_impl* s) { TORRENT_ASSERT(std::find(m_drained_event.begin(), m_drained_event.end(), s) == m_drained_event.end()); m_drained_event.push_back(s); } void utp_socket_manager::remove_socket(boost::uint16_t id) { socket_map_t::iterator i = m_utp_sockets.find(id); if (i == m_utp_sockets.end()) return; delete_utp_impl(i->second); if (m_last_socket == i->second) m_last_socket = 0; if (m_deferred_ack == i->second) m_deferred_ack = 0; m_utp_sockets.erase(i); } void utp_socket_manager::set_sock_buf(int size) { if (size < m_sock_buf_size) return; m_sock.set_buf_size(size); error_code ec; // add more socket buffer storage on the lower level socket // to avoid dropping packets because of a full receive buffer // while processing a packet // only update the buffer size if it's bigger than // what we already have udp::socket::receive_buffer_size recv_buf_size_opt; m_sock.get_option(recv_buf_size_opt, ec); if (recv_buf_size_opt.value() < size * 10) { m_sock.set_option(udp::socket::receive_buffer_size(size * 10), ec); m_sock.set_option(udp::socket::send_buffer_size(size * 3), ec); } m_sock_buf_size = size; } void utp_socket_manager::inc_stats_counter(int counter, int delta) { TORRENT_ASSERT((counter >= counters::utp_packet_loss && counter <= counters::utp_redundant_pkts_in) || (counter >= counters::num_utp_idle && counter <= counters::num_utp_deleted)); m_counters.inc_stats_counter(counter, delta); } utp_socket_impl* utp_socket_manager::new_utp_socket(utp_stream* str) { boost::uint16_t send_id = 0; boost::uint16_t recv_id = 0; if (m_new_connection != -1) { send_id = m_new_connection; recv_id = m_new_connection + 1; m_new_connection = -1; } else { send_id = random() & 0xffff; recv_id = send_id - 1; } utp_socket_impl* impl = construct_utp_impl(recv_id, send_id, str, this); m_utp_sockets.insert(std::make_pair(recv_id, impl)); return impl; } } libtorrent-rasterbar-1.1.13/src/utp_stream.cpp000066400000000000000000003436021351156116000214310ustar00rootroot00000000000000/* Copyright (c) 2009-2018, 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/config.hpp" #include "libtorrent/utp_stream.hpp" #include "libtorrent/sliding_average.hpp" #include "libtorrent/utp_socket_manager.hpp" #include "libtorrent/alloca.hpp" #include "libtorrent/timestamp_history.hpp" #include "libtorrent/error.hpp" #include "libtorrent/random.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/performance_counters.hpp" #include "libtorrent/io_service.hpp" #include #include #if TORRENT_UTP_LOG #include #include "libtorrent/socket_io.hpp" #endif namespace libtorrent { #if TORRENT_UTP_LOG char const* packet_type_names[] = { "ST_DATA", "ST_FIN", "ST_STATE", "ST_RESET", "ST_SYN" }; char const* socket_state_names[] = { "NONE", "SYN_SENT", "CONNECTED", "FIN_SENT", "ERROR", "DELETE" }; static struct utp_logger { FILE* utp_log_file; mutex utp_log_mutex; utp_logger() : utp_log_file(NULL) {} ~utp_logger() { if (utp_log_file) fclose(utp_log_file); } } log_file_holder; TORRENT_FORMAT(1, 2) void utp_log(char const* fmt, ...) { if (log_file_holder.utp_log_file == NULL) return; mutex::scoped_lock lock(log_file_holder.utp_log_mutex); static time_point start = clock_type::now(); fprintf(log_file_holder.utp_log_file, "[%012" PRId64 "] ", total_microseconds(clock_type::now() - start)); va_list l; va_start(l, fmt); vfprintf(log_file_holder.utp_log_file, fmt, l); va_end(l); } bool is_utp_stream_logging() { return log_file_holder.utp_log_file != NULL; } void set_utp_stream_logging(bool enable) { if (enable) { if (log_file_holder.utp_log_file == NULL) { log_file_holder.utp_log_file = fopen("utp.log", "w+"); } } else { if (log_file_holder.utp_log_file != NULL) { FILE* f = log_file_holder.utp_log_file; log_file_holder.utp_log_file = NULL; fclose(f); } } } #define UTP_LOG utp_log #if TORRENT_VERBOSE_UTP_LOG #define UTP_LOGV utp_log #else #define UTP_LOGV TORRENT_WHILE_0 printf #endif #else #if __cplusplus >= 201103L || defined __clang__ #define UTP_LOG(...) do {} while(false) #define UTP_LOGV(...) do {} while(false) #else #define UTP_LOG TORRENT_WHILE_0 printf #define UTP_LOGV TORRENT_WHILE_0 printf #endif // cplusplus #endif enum { ACK_MASK = 0xffff, // if a packet receives more than this number of // duplicate acks, we'll trigger a fast re-send dup_ack_limit = 3 }; // compare if lhs is less than rhs, taking wrapping // into account. if lhs is close to UINT_MAX and rhs // is close to 0, lhs is assumed to have wrapped and // considered smaller TORRENT_EXTRA_EXPORT bool compare_less_wrap(boost::uint32_t lhs , boost::uint32_t rhs, boost::uint32_t mask) { // distance walking from lhs to rhs, downwards boost::uint32_t dist_down = (lhs - rhs) & mask; // distance walking from lhs to rhs, upwards boost::uint32_t dist_up = (rhs - lhs) & mask; // if the distance walking up is shorter, lhs // is less than rhs. If the distance walking down // is shorter, then rhs is less than lhs return dist_up < dist_down; } // used for out-of-order incoming packets // as well as sent packets that are waiting to be ACKed struct packet { // the last time this packet was sent time_point send_time; // the number of bytes actually allocated in 'buf' boost::uint16_t allocated; // the size of the buffer 'buf' points to boost::uint16_t size; // this is the offset to the payload inside the buffer // this is also used as a cursor to describe where the // next payload that hasn't been consumed yet starts boost::uint16_t header_size; // the number of times this packet has been sent boost::uint8_t num_transmissions:6; // true if we need to send this packet again. All // outstanding packets are marked as needing to be // resent on timeouts bool need_resend:1; // this is set to true for packets that were // sent with the DF bit set (Don't Fragment) bool mtu_probe:1; #ifdef TORRENT_DEBUG int num_fast_resend; #endif // the actual packet buffer boost::uint8_t buf[1]; }; // since the uTP socket state may be needed after the // utp_stream is closed, it's kept in a separate struct // whose lifetime is not tied to the lifetime of utp_stream // the utp socket is closely modelled after the asio async // operations and handler model. For writing to the socket, // the client provides a list of buffers (for gather/writev // style of I/O) and whenever the socket can write another // packet to the stream, it picks up data from these buffers. // When all of the data has been written, or enough time has // passed since we first started writing, the write handler // is called and the write buffer is reset. This means that // we're not writing anything at all while waiting for the // client to re-issue a write request. // reading is a little bit more complicated, since we must // be able to receive data even when the user doesn't have // an outstanding read operation on the socket. When the user // does however, we want to receive data directly into the // user's buffer instead of first copying it into our receive // buffer. This is why the receive case is more complicated. // There are two receive buffers. One provided by the user, // which when present is always used. The other one is used // when the user doesn't have an outstanding read request, // and hence hasn't provided any buffer space to receive into. // the user provided read buffer is called "m_read_buffer" and // its size is "m_read_buffer_size". The buffer we spill over // into when the user provided buffer is full or when there // is none, is "m_receive_buffer" and "m_receive_buffer_size" // respectively. // in order to know when to trigger the read and write handlers // there are two counters, m_read and m_written, which count // the number of bytes we've stuffed into the user provided // read buffer or written to the stream from the write buffer. // These are used to trigger the handlers if we're written a // large number of bytes. It's also triggered if we're filled // the whole read buffer, or written the entire write buffer. // The last way the handlers can be triggered is if we're read // or written some, and enough time has elapsed since then. // when we receive data into m_receive_buffer (i.e. the buffer // used when there's no user provided one) is stored as a // number of heap allocated packets. This is just because it's // simple to reuse the data structured and it provides all the // functionality needed for this buffer. struct utp_socket_impl { utp_socket_impl(boost::uint16_t recv_id, boost::uint16_t send_id , void* userdata, utp_socket_manager* sm) : m_sm(sm) , m_userdata(userdata) , m_nagle_packet(NULL) , m_read_handler(false) , m_write_handler(false) , m_connect_handler(false) , m_remote_address() , m_timeout(clock_type::now() + milliseconds(m_sm->connect_timeout())) , m_last_history_step(clock_type::now()) , m_cwnd(TORRENT_ETHERNET_MTU << 16) , m_ssthres(0) , m_buffered_incoming_bytes(0) , m_reply_micro(0) , m_adv_wnd(TORRENT_ETHERNET_MTU) , m_bytes_in_flight(0) , m_read(0) , m_write_buffer_size(0) , m_written(0) , m_receive_buffer_size(0) , m_read_buffer_size(0) , m_receive_buffer_capacity(1024 * 1024) , m_in_packets(0) , m_out_packets(0) , m_send_delay(0) , m_recv_delay(0) , m_close_reason(0) , m_port(0) , m_send_id(send_id) , m_recv_id(recv_id) , m_ack_nr(0) , m_seq_nr(0) , m_acked_seq_nr(0) , m_fast_resend_seq_nr(0) , m_eof_seq_nr(0) , m_loss_seq_nr(0) , m_mtu(TORRENT_ETHERNET_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER - 8 - 24 - 36) , m_mtu_floor(TORRENT_INET_MIN_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER) , m_mtu_ceiling(TORRENT_ETHERNET_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER) , m_mtu_seq(0) , m_duplicate_acks(0) , m_num_timeouts(0) , m_delay_sample_idx(0) , m_state(UTP_STATE_NONE) , m_eof(false) , m_attached(true) , m_nagle(true) , m_slow_start(true) , m_cwnd_full(false) , m_null_buffers(false) , m_deferred_ack(false) , m_subscribe_drained(false) , m_stalled(false) , m_confirmed(false) { TORRENT_ASSERT((m_recv_id == ((m_send_id + 1) & 0xffff)) || (m_send_id == ((m_recv_id + 1) & 0xffff))); m_sm->inc_stats_counter(counters::num_utp_idle); TORRENT_ASSERT(m_userdata); for (int i = 0; i != num_delay_hist; ++i) m_delay_sample_hist[i] = (std::numeric_limits::max)(); } ~utp_socket_impl(); void tick(time_point now); void init_mtu(int link_mtu, int utp_mtu); bool incoming_packet(boost::uint8_t const* buf, int size , udp::endpoint const& ep, time_point receive_time); void writable(); bool should_delete() const; tcp::endpoint remote_endpoint(error_code& ec) const { if (m_state == UTP_STATE_NONE) ec = boost::asio::error::not_connected; else TORRENT_ASSERT(m_remote_address != address_v4::any()); return tcp::endpoint(m_remote_address, m_port); } std::size_t available() const; // returns true if there were handlers cancelled // if it returns false, we can detach immediately bool destroy(); void set_close_reason(boost::uint16_t code); void detach(); void send_syn(); void send_fin(); void subscribe_drained(); void defer_ack(); void remove_sack_header(packet* p); enum packet_flags_t { pkt_ack = 1, pkt_fin = 2 }; bool send_pkt(int flags = 0); bool resend_packet(packet* p, bool fast_resend = false); void send_reset(utp_header const* ph); void parse_sack(boost::uint16_t packet_ack, boost::uint8_t const* ptr , int size, int* acked_bytes, time_point const now, boost::uint32_t& min_rtt); void parse_close_reason(boost::uint8_t const* ptr, int size); void write_payload(boost::uint8_t* ptr, int size); void maybe_inc_acked_seq_nr(); void ack_packet(packet* p, time_point receive_time , boost::uint32_t& min_rtt, boost::uint16_t seq_nr); void write_sack(boost::uint8_t* buf, int size) const; void incoming(boost::uint8_t const* buf, int size, packet* p, time_point now); void do_ledbat(int acked_bytes, int delay, int in_flight); int packet_timeout() const; bool test_socket_state(); void maybe_trigger_receive_callback(); void maybe_trigger_send_callback(); bool cancel_handlers(error_code const& ec, bool kill); bool consume_incoming_data( utp_header const* ph, boost::uint8_t const* ptr, int payload_size, time_point now); void update_mtu_limits(); void experienced_loss(int seq_nr, time_point now); void set_state(int s); private: // non-copyable utp_socket_impl(utp_socket_impl const&); utp_socket_impl const& operator=(utp_socket_impl const&); // TODO: 2 it would be nice if not everything would have to be public here public: void check_receive_buffers() const; #if TORRENT_USE_INVARIANT_CHECKS void check_invariant() const; #endif utp_socket_manager* m_sm; // userdata pointer passed along // with any callback. This is initialized to 0 // then set to point to the utp_stream when // hooked up, and then reset to 0 once the utp_stream // detaches. This is used to know whether or not // the socket impl is still attached to a utp_stream // object. When it isn't, we'll never be able to // signal anything back to the client, and in case // of errors, we just have to delete ourselves // i.e. transition to the UTP_STATE_DELETED state void* m_userdata; // This is a platform-independent replacement // for the regular iovec type in posix. Since // it's not used in any system call, we might as // well define our own type instead of wrapping // the system's type. struct iovec_t { iovec_t(void* b, size_t l): buf(b), len(l) {} void* buf; size_t len; }; // if there's currently an async read or write // operation in progress, these buffers are initialized // and used, otherwise any bytes received are stuck in // m_receive_buffer until another read is made // as we flush from the write buffer, individual iovecs // are updated to only refer to unflushed portions of the // buffers. Buffers that empty are erased from the vector. std::vector m_write_buffer; // if this is non NULL, it's a packet. This packet was held off because // of NAGLE. We couldn't send it immediately. It's left // here to accrue more bytes before we send it. packet* m_nagle_packet; // the user provided read buffer. If this has a size greater // than 0, we'll always prefer using it over putting received // data in the m_receive_buffer. As data is stored in the // read buffer, the iovec_t elements are adjusted to only // refer to the unwritten portions of the buffers, and the // ones that fill up are erased from the vector std::vector m_read_buffer; // packets we've received without a read operation // active. Store them here until the client triggers // an async_read_some std::vector m_receive_buffer; // this is the error on this socket. If m_state is // set to UTP_STATE_ERROR_WAIT, this error should be // forwarded to the client as soon as we have a new // async operation initiated error_code m_error; // these indicate whether or not there is an outstanding read/write or // connect operation. i.e. is there upper layer subscribed to these events. bool m_read_handler; bool m_write_handler; bool m_connect_handler; // the address of the remote endpoint address m_remote_address; // the local address address m_local_address; // the send and receive buffers // maps packet sequence numbers packet_buffer m_inbuf; packet_buffer m_outbuf; // the time when the last packet we sent times out. Including re-sends. // if we ever end up not having sent anything in one second ( // or one mean rtt + 2 average deviations, whichever is greater) // we set our cwnd to 1 MSS. This condition can happen either because // a packet has timed out and needs to be resent or because our // cwnd is set to less than one MSS during congestion control. // it can also happen if the other end sends an advertised window // size less than one MSS. time_point m_timeout; // the last time we stepped the timestamp history time_point m_last_history_step; // the next time we allow a lost packet to halve cwnd. We only do this once every // 100 ms time_point m_next_loss; // the max number of bytes in-flight. This is a fixed point // value, to get the true number of bytes, shift right 16 bits // the value is always >= 0, but the calculations performed on // it in do_ledbat() are signed. boost::int64_t m_cwnd; timestamp_history m_delay_hist; timestamp_history m_their_delay_hist; // the slow-start threshold. This is the congestion window size (m_cwnd) // in bytes the last time we left slow-start mode. This is used as a // threshold to leave slow-start earlier next time, to avoid packet-loss boost::int32_t m_ssthres; // the number of bytes we have buffered in m_inbuf boost::int32_t m_buffered_incoming_bytes; // the timestamp diff in the last packet received // this is what we'll send back boost::uint32_t m_reply_micro; // this is the advertised receive window the other end sent // we'll never have more un-acked bytes in flight // if this ever gets set to zero, we'll try one packet every // second until the window opens up again boost::uint32_t m_adv_wnd; // the number of un-acked bytes we have sent boost::int32_t m_bytes_in_flight; // the number of bytes read into the user provided // buffer. If this grows too big, we'll trigger the // read handler. boost::int32_t m_read; // the sum of the lengths of all iovec in m_write_buffer boost::int32_t m_write_buffer_size; // the number of bytes already written to packets // from m_write_buffer boost::int32_t m_written; // the sum of all packets stored in m_receive_buffer boost::int32_t m_receive_buffer_size; // the sum of all buffers in m_read_buffer boost::int32_t m_read_buffer_size; // max number of bytes to allocate for receive buffer boost::int32_t m_receive_buffer_capacity; // this holds the 3 last delay measurements, // these are the actual corrected delay measurements. // the lowest of the 3 last ones is used in the congestion // controller. This is to not completely close the cwnd // by a single outlier. enum { num_delay_hist = 3 }; boost::uint32_t m_delay_sample_hist[num_delay_hist]; // counters boost::uint32_t m_in_packets; boost::uint32_t m_out_packets; // the last send delay sample boost::int32_t m_send_delay; // the last receive delay sample boost::int32_t m_recv_delay; // average RTT sliding_average<16> m_rtt; // if this is != 0, it means the upper layer provided a reason for why // the connection is being closed. The reason is indicated by this // non-zero value which is included in a packet header extension boost::uint16_t m_close_reason; // port of destination endpoint boost::uint16_t m_port; boost::uint16_t m_send_id; boost::uint16_t m_recv_id; // this is the ack we're sending back. We have // received all packets up to this sequence number boost::uint16_t m_ack_nr; // the sequence number of the next packet // we'll send boost::uint16_t m_seq_nr; // this is the sequence number of the packet that // everything has been ACKed up to. Everything we've // sent up to this point has been received by the other // end. boost::uint16_t m_acked_seq_nr; // each packet gets one chance of "fast resend". i.e. // if we have multiple duplicate acks, we may send a // packet immediately, if m_fast_resend_seq_nr is set // to that packet's sequence number boost::uint16_t m_fast_resend_seq_nr; // this is the sequence number of the FIN packet // we've received. This sequence number is only // valid if m_eof is true. We should not accept // any packets beyond this sequence number from the // other end boost::uint16_t m_eof_seq_nr; // this is the lowest sequence number that, when lost, // will cause the window size to be cut in half boost::uint16_t m_loss_seq_nr; // the max number of bytes we can send in a packet // including the header boost::uint16_t m_mtu; // the floor is the largest packet that we have // been able to get through without fragmentation boost::uint16_t m_mtu_floor; // the ceiling is the largest packet that we might // be able to get through without fragmentation. // i.e. ceiling +1 is very likely to not get through // or we have in fact experienced a drop or ICMP // message indicating that it is boost::uint16_t m_mtu_ceiling; // the sequence number of the probe in-flight // this is 0 if there is no probe in flight boost::uint16_t m_mtu_seq; // this is a counter of how many times the current m_acked_seq_nr // has been ACKed. If it's ACKed more than 3 times, we assume the // packet with the next sequence number has been lost, and we trigger // a re-send. Obviously an ACK only counts as a duplicate as long as // we have outstanding packets following it. boost::uint8_t m_duplicate_acks; // the number of packet timeouts we've seen in a row // this affects the packet timeout time boost::uint8_t m_num_timeouts; // it's important that these match the enums in performance_counters for // num_utp_idle etc. enum state_t { // not yet connected UTP_STATE_NONE, // sent a syn packet, not received any acks UTP_STATE_SYN_SENT, // syn-ack received and in normal operation // of sending and receiving data UTP_STATE_CONNECTED, // fin sent, but all packets up to the fin packet // have not yet been acked. We might still be waiting // for a FIN from the other end UTP_STATE_FIN_SENT, // ====== states beyond this point ===== // === are considered closing states === // === and will cause the socket to ==== // ============ be deleted ============= // the socket has been gracefully disconnected // and is waiting for the client to make a // socket call so that we can communicate this // fact and actually delete all the state, or // there is an error on this socket and we're // waiting to communicate this to the client in // a callback. The error in either case is stored // in m_error. If the socket has gracefully shut // down, the error is error::eof. UTP_STATE_ERROR_WAIT, // there are no more references to this socket // and we can delete it UTP_STATE_DELETE }; // this is the cursor into m_delay_sample_hist boost::uint8_t m_delay_sample_idx:2; // the state the socket is in boost::uint8_t m_state:3; // this is set to true when we receive a fin bool m_eof:1; // is this socket state attached to a user space socket? bool m_attached:1; // this is true if nagle is enabled (which it is by default) bool m_nagle:1; // this is true while the socket is in slow start mode. It's // only in slow-start during the start-up phase. Slow start // (contrary to what its name suggest) means that we're growing // the congestion window (cwnd) exponentially rather than linearly. // this is done at startup of a socket in order to find its // link capacity faster. This behaves similar to TCP slow start bool m_slow_start:1; // this is true as long as we have as many packets in // flight as allowed by the congestion window (cwnd) bool m_cwnd_full:1; // this is set to one if the current read operation // has a null-buffer. i.e. we're not reading into a user-provided // buffer, we're just signalling when there's something // to read from our internal receive buffer bool m_null_buffers:1; // this is set to true when this socket has added itself to // the utp socket manager's list of deferred acks. Once the // burst of incoming UDP packets is all drained, the utp socket // manager will send acks for all sockets on this list. bool m_deferred_ack:1; // this is true if this socket has subscribed to be notified // when this receive round is done bool m_subscribe_drained:1; // if this socket tries to send a packet via the utp socket // manager, and it fails with EWOULDBLOCK, the socket // is stalled and this is set. It's also added to a list // of sockets in the utp_socket_manager to be notified of // the socket being writable again bool m_stalled:1; // this is false by default and set to true once we've received a non-SYN // packet for this connection with a correct ack_nr, confirming that the // other end is not spoofing its source IP bool m_confirmed:1; }; utp_socket_impl* construct_utp_impl(boost::uint16_t recv_id , boost::uint16_t send_id, void* userdata , utp_socket_manager* sm) { return new utp_socket_impl(recv_id, send_id, userdata, sm); } void detach_utp_impl(utp_socket_impl* s) { s->detach(); } void delete_utp_impl(utp_socket_impl* s) { delete s; } bool should_delete(utp_socket_impl* s) { return s->should_delete(); } void tick_utp_impl(utp_socket_impl* s, time_point now) { s->tick(now); } void utp_init_mtu(utp_socket_impl* s, int link_mtu, int utp_mtu) { s->init_mtu(link_mtu, utp_mtu); } bool utp_incoming_packet(utp_socket_impl* s, char const* p , int size, udp::endpoint const& ep, time_point const receive_time) { return s->incoming_packet(reinterpret_cast(p), size , ep, receive_time); } bool utp_match(utp_socket_impl* s, udp::endpoint const& ep, boost::uint16_t id) { return s->m_remote_address == ep.address() && s->m_port == ep.port() && s->m_recv_id == id; } udp::endpoint utp_remote_endpoint(utp_socket_impl* s) { return udp::endpoint(s->m_remote_address, s->m_port); } boost::uint16_t utp_receive_id(utp_socket_impl* s) { return s->m_recv_id; } void utp_writable(utp_socket_impl* s) { TORRENT_ASSERT(s->m_stalled); s->m_stalled = false; s->writable(); } void utp_send_ack(utp_socket_impl* s) { TORRENT_ASSERT(s->m_deferred_ack); s->m_deferred_ack = false; s->send_pkt(utp_socket_impl::pkt_ack); } void utp_socket_drained(utp_socket_impl* s) { s->m_subscribe_drained = false; // at this point, we know we won't receive any // more packets this round. So, we may want to // call the receive callback function to // let the user consume it s->maybe_trigger_receive_callback(); s->maybe_trigger_send_callback(); } void utp_socket_impl::update_mtu_limits() { INVARIANT_CHECK; if (m_mtu_floor > m_mtu_ceiling) m_mtu_floor = m_mtu_ceiling; m_mtu = (m_mtu_floor + m_mtu_ceiling) / 2; if ((m_cwnd >> 16) < m_mtu) m_cwnd = boost::int64_t(m_mtu) * (1 << 16); UTP_LOGV("%8p: updating MTU to: %d [%d, %d]\n" , static_cast(this), m_mtu, m_mtu_floor, m_mtu_ceiling); // clear the mtu probe sequence number since // it was either dropped or acked m_mtu_seq = 0; } int utp_socket_state(utp_socket_impl const* s) { return s->m_state; } int utp_stream::send_delay() const { return m_impl ? m_impl->m_send_delay : 0; } int utp_stream::recv_delay() const { return m_impl ? m_impl->m_recv_delay : 0; } utp_stream::utp_stream(io_service& io_service) : m_io_service(io_service) , m_impl(0) , m_incoming_close_reason(0) , m_open(false) { } utp_socket_impl* utp_stream::get_impl() { return m_impl; } void utp_stream::set_close_reason(boost::uint16_t code) { if (!m_impl) return; m_impl->set_close_reason(code); } boost::uint16_t utp_stream::get_close_reason() { return m_incoming_close_reason; } void utp_stream::close() { if (!m_impl) return; if (!m_impl->destroy()) { if (!m_impl) return; detach_utp_impl(m_impl); m_impl = 0; } } std::size_t utp_stream::available() const { return m_impl ? m_impl->available() : 0; } utp_stream::endpoint_type utp_stream::remote_endpoint(error_code& ec) const { if (!m_impl) { ec = boost::asio::error::not_connected; return endpoint_type(); } return m_impl->remote_endpoint(ec); } utp_stream::endpoint_type utp_stream::local_endpoint(error_code& ec) const { if (m_impl == 0 || m_impl->m_sm == 0) { ec = boost::asio::error::not_connected; return endpoint_type(); } return tcp::endpoint(m_impl->m_local_address, m_impl->m_sm->local_port(ec)); } utp_stream::~utp_stream() { if (m_impl) { UTP_LOGV("%8p: utp_stream destructed\n", static_cast(m_impl)); m_impl->destroy(); detach_utp_impl(m_impl); } m_impl = 0; } void utp_stream::set_impl(utp_socket_impl* impl) { TORRENT_ASSERT(m_impl == 0); TORRENT_ASSERT(!m_open); m_impl = impl; m_open = true; } int utp_stream::read_buffer_size() const { TORRENT_ASSERT(m_impl); return m_impl->m_receive_buffer_size; } void utp_stream::on_close_reason(void* self, boost::uint16_t close_reason) { utp_stream* s = static_cast(self); // it's possible the socket has been unlinked already, in which case m_impl // will be NULL if (s->m_impl) s->m_incoming_close_reason = close_reason; } void utp_stream::on_read(void* self, size_t bytes_transferred , error_code const& ec, bool kill) { utp_stream* s = static_cast(self); UTP_LOGV("%8p: calling read handler read:%d ec:%s kill:%d\n", static_cast(s->m_impl) , int(bytes_transferred), ec.message().c_str(), kill); TORRENT_ASSERT(s->m_read_handler); TORRENT_ASSERT(bytes_transferred > 0 || ec || s->m_impl->m_null_buffers); s->m_io_service.post(boost::bind(s->m_read_handler, ec, bytes_transferred)); s->m_read_handler.clear(); // boost::function2 tmp; // tmp.swap(s->m_read_handler); if (kill && s->m_impl) { TORRENT_ASSERT(ec); detach_utp_impl(s->m_impl); s->m_impl = 0; } // tmp(ec, bytes_transferred); } void utp_stream::on_write(void* self, size_t bytes_transferred , error_code const& ec, bool kill) { utp_stream* s = static_cast(self); UTP_LOGV("%8p: calling write handler written:%d ec:%s kill:%d\n" , static_cast(s->m_impl) , int(bytes_transferred), ec.message().c_str(), kill); TORRENT_ASSERT(s->m_write_handler); TORRENT_ASSERT(bytes_transferred > 0 || ec); s->m_io_service.post(boost::bind(s->m_write_handler, ec, bytes_transferred)); s->m_write_handler.clear(); // boost::function2 tmp; // tmp.swap(s->m_read_handler); if (kill && s->m_impl) { TORRENT_ASSERT(ec); detach_utp_impl(s->m_impl); s->m_impl = 0; } // tmp(ec, bytes_transferred); } void utp_stream::on_connect(void* self, error_code const& ec, bool kill) { utp_stream* s = static_cast(self); TORRENT_ASSERT(s); UTP_LOGV("%8p: calling connect handler ec:%s kill:%d\n" , static_cast(s->m_impl), ec.message().c_str(), kill); TORRENT_ASSERT(s->m_connect_handler); s->m_io_service.post(boost::bind(s->m_connect_handler, ec)); s->m_connect_handler.clear(); // boost::function1 tmp; // s->m_connect_handler.swap(tmp); if (kill && s->m_impl) { TORRENT_ASSERT(ec); detach_utp_impl(s->m_impl); s->m_impl = 0; } // tmp(ec); } void utp_stream::add_read_buffer(void* buf, size_t len) { TORRENT_ASSERT(m_impl); TORRENT_ASSERT(len < INT_MAX); TORRENT_ASSERT(len > 0); TORRENT_ASSERT(buf); m_impl->m_read_buffer.push_back(utp_socket_impl::iovec_t(buf, len)); m_impl->m_read_buffer_size += len; UTP_LOGV("%8p: add_read_buffer %d bytes\n", static_cast(m_impl), int(len)); } // this is the wrapper to add a user provided write buffer to the // utp_socket_impl. It makes sure the m_write_buffer_size is kept // up to date void utp_stream::add_write_buffer(void const* buf, size_t len) { TORRENT_ASSERT(m_impl); TORRENT_ASSERT(len < INT_MAX); TORRENT_ASSERT(len > 0); TORRENT_ASSERT(buf); #ifdef TORRENT_DEBUG int write_buffer_size = 0; for (std::vector::iterator i = m_impl->m_write_buffer.begin() , end(m_impl->m_write_buffer.end()); i != end; ++i) { write_buffer_size += i->len; } TORRENT_ASSERT(m_impl->m_write_buffer_size == write_buffer_size); #endif m_impl->m_write_buffer.push_back(utp_socket_impl::iovec_t(const_cast(buf), len)); m_impl->m_write_buffer_size += len; #ifdef TORRENT_DEBUG write_buffer_size = 0; for (std::vector::iterator i = m_impl->m_write_buffer.begin() , end(m_impl->m_write_buffer.end()); i != end; ++i) { write_buffer_size += i->len; } TORRENT_ASSERT(m_impl->m_write_buffer_size == write_buffer_size); #endif UTP_LOGV("%8p: add_write_buffer %d bytes\n", static_cast(m_impl), int(len)); } // this is called when all user provided read buffers have been added // and it's time to execute the async operation. The first thing we // do is to copy any data stored in m_receive_buffer into the user // provided buffer. This might be enough to in turn trigger the read // handler immediately. void utp_stream::issue_read() { TORRENT_ASSERT(m_impl->m_userdata); TORRENT_ASSERT(!m_impl->m_read_handler); m_impl->m_null_buffers = m_impl->m_read_buffer_size == 0; m_impl->m_read_handler = true; if (m_impl->test_socket_state()) return; UTP_LOGV("%8p: new read handler. %d bytes in buffer\n" , static_cast(m_impl), m_impl->m_receive_buffer_size); // so, the client wants to read. If we already // have some data in the read buffer, move it into the // client's buffer right away m_impl->m_read += read_some(false); m_impl->maybe_trigger_receive_callback(); } size_t utp_stream::read_some(bool clear_buffers) { if (m_impl->m_receive_buffer_size == 0) { if (clear_buffers) { m_impl->m_read_buffer_size = 0; m_impl->m_read_buffer.clear(); } return 0; } std::vector::iterator target = m_impl->m_read_buffer.begin(); size_t ret = 0; int pop_packets = 0; for (std::vector::iterator i = m_impl->m_receive_buffer.begin() , end(m_impl->m_receive_buffer.end()); i != end;) { if (target == m_impl->m_read_buffer.end()) { UTP_LOGV(" No more target buffers: %d bytes left in buffer\n" , m_impl->m_receive_buffer_size); TORRENT_ASSERT(m_impl->m_read_buffer.empty()); break; } m_impl->check_receive_buffers(); packet* p = *i; int to_copy = (std::min)(p->size - p->header_size, int(target->len)); TORRENT_ASSERT(to_copy >= 0); memcpy(target->buf, p->buf + p->header_size, to_copy); ret += to_copy; target->buf = static_cast(target->buf) + to_copy; TORRENT_ASSERT(int(target->len) >= to_copy); target->len -= to_copy; m_impl->m_receive_buffer_size -= to_copy; TORRENT_ASSERT(m_impl->m_read_buffer_size >= to_copy); m_impl->m_read_buffer_size -= to_copy; p->header_size += to_copy; if (target->len == 0) target = m_impl->m_read_buffer.erase(target); m_impl->check_receive_buffers(); TORRENT_ASSERT(m_impl->m_receive_buffer_size >= 0); // Consumed entire packet if (p->header_size == p->size) { free(p); ++pop_packets; *i = 0; ++i; } if (m_impl->m_receive_buffer_size == 0) { UTP_LOGV("%8p: Didn't fill entire target: %d bytes left in buffer\n" , static_cast(m_impl), m_impl->m_receive_buffer_size); break; } } // remove the packets from the receive_buffer that we already copied over // and freed m_impl->m_receive_buffer.erase(m_impl->m_receive_buffer.begin() , m_impl->m_receive_buffer.begin() + pop_packets); // we exited either because we ran out of bytes to copy // or because we ran out of space to copy the bytes to TORRENT_ASSERT(m_impl->m_receive_buffer_size == 0 || m_impl->m_read_buffer.empty()); UTP_LOGV("%8p: %d packets moved from buffer to user space (%d bytes)\n" , static_cast(m_impl), pop_packets, int(ret)); if (clear_buffers) { m_impl->m_read_buffer_size = 0; m_impl->m_read_buffer.clear(); } TORRENT_ASSERT(ret > 0 || m_impl->m_null_buffers); return ret; } // this is called when all user provided write buffers have been // added. Start trying to send packets with the payload immediately. void utp_stream::issue_write() { UTP_LOGV("%8p: new write handler. %d bytes to write\n" , static_cast(m_impl), m_impl->m_write_buffer_size); TORRENT_ASSERT(m_impl->m_write_buffer_size > 0); TORRENT_ASSERT(m_impl->m_write_handler == false); TORRENT_ASSERT(m_impl->m_userdata); m_impl->m_write_handler = true; m_impl->m_written = 0; if (m_impl->test_socket_state()) return; // try to write. send_pkt returns false if there's // no more payload to send or if the congestion window // is full and we can't send more packets right now while (m_impl->send_pkt()); // if there was an error in send_pkt(), m_impl may be // 0 at this point if (m_impl) m_impl->maybe_trigger_send_callback(); } void utp_stream::do_connect(tcp::endpoint const& ep) { int link_mtu, utp_mtu; m_impl->m_sm->mtu_for_dest(ep.address(), link_mtu, utp_mtu); m_impl->init_mtu(link_mtu, utp_mtu); TORRENT_ASSERT(m_impl->m_connect_handler == false); m_impl->m_remote_address = ep.address(); m_impl->m_port = ep.port(); m_impl->m_connect_handler = true; error_code ec; m_impl->m_local_address = m_impl->m_sm->local_endpoint(m_impl->m_remote_address, ec).address(); if (m_impl->test_socket_state()) return; m_impl->send_syn(); } // =========== utp_socket_impl ============ utp_socket_impl::~utp_socket_impl() { INVARIANT_CHECK; TORRENT_ASSERT(!m_attached); TORRENT_ASSERT(!m_deferred_ack); m_sm->inc_stats_counter(counters::num_utp_idle + m_state, -1); UTP_LOGV("%8p: destroying utp socket state\n", static_cast(this)); // free any buffers we're holding for (boost::uint16_t i = m_inbuf.cursor(), end((m_inbuf.cursor() + m_inbuf.capacity()) & ACK_MASK); i != end; i = (i + 1) & ACK_MASK) { packet* p = m_inbuf.remove(i); free(p); } for (boost::uint16_t i = m_outbuf.cursor(), end((m_outbuf.cursor() + m_outbuf.capacity()) & ACK_MASK); i != end; i = (i + 1) & ACK_MASK) { packet* p = m_outbuf.remove(i); free(p); } for (std::vector::iterator i = m_receive_buffer.begin() , end = m_receive_buffer.end(); i != end; ++i) { free(*i); } free(m_nagle_packet); m_nagle_packet = NULL; } bool utp_socket_impl::should_delete() const { INVARIANT_CHECK; // if the socket state is not attached anymore we're free // to delete it from the client's point of view. The other // endpoint however might still need to be told that we're // closing the socket. Only delete the state if we're not // attached and we're in a state where the other end doesn't // expect the socket to still be alive // when m_stalled is true, it means the socket manager has a // pointer to this socket, waiting for the UDP socket to // become writable again. We have to wait for that, so that // the pointer is removed from that queue. Otherwise we would // leave a dangling pointer in the socket manager bool ret = (m_state >= UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_NONE) && !m_attached && !m_stalled; if (ret) { UTP_LOGV("%8p: should_delete() = true\n", static_cast(this)); } return ret; } void utp_socket_impl::maybe_trigger_receive_callback() { INVARIANT_CHECK; if (m_read_handler == false) return; // nothing has been read or there's no outstanding read operation if (m_null_buffers && m_receive_buffer_size == 0) return; else if (!m_null_buffers && m_read == 0) return; UTP_LOGV("%8p: calling read handler read:%d\n", static_cast(this), m_read); m_read_handler = false; utp_stream::on_read(m_userdata, m_read, m_error, false); m_read = 0; m_read_buffer_size = 0; m_read_buffer.clear(); } void utp_socket_impl::maybe_trigger_send_callback() { INVARIANT_CHECK; // nothing has been written or there's no outstanding write operation if (m_written == 0 || m_write_handler == false) return; UTP_LOGV("%8p: calling write handler written:%d\n", static_cast(this), m_written); m_write_handler = false; utp_stream::on_write(m_userdata, m_written, m_error, false); m_written = 0; m_write_buffer_size = 0; m_write_buffer.clear(); } void utp_socket_impl::set_close_reason(boost::uint16_t code) { #if TORRENT_UTP_LOG UTP_LOGV("%8p: set_close_reason: %d\n" , static_cast(this), int(m_close_reason)); #endif m_close_reason = code; } bool utp_socket_impl::destroy() { INVARIANT_CHECK; #if TORRENT_UTP_LOG UTP_LOGV("%8p: destroy state:%s (close-reason: %d)\n" , static_cast(this), socket_state_names[m_state], int(m_close_reason)); #endif if (m_userdata == 0) return false; if (m_state == UTP_STATE_CONNECTED) send_fin(); bool cancelled = cancel_handlers(boost::asio::error::operation_aborted, true); m_userdata = 0; m_read_buffer.clear(); m_read_buffer_size = 0; m_write_buffer.clear(); m_write_buffer_size = 0; if ((m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_NONE || m_state == UTP_STATE_SYN_SENT) && cancelled) { set_state(UTP_STATE_DELETE); #if TORRENT_UTP_LOG UTP_LOGV("%8p: state:%s\n", static_cast(this), socket_state_names[m_state]); #endif } return cancelled; // #error our end is closing. Wait for everything to be acked } void utp_socket_impl::detach() { INVARIANT_CHECK; UTP_LOGV("%8p: detach()\n", static_cast(this)); m_attached = false; } void utp_socket_impl::send_syn() { INVARIANT_CHECK; m_seq_nr = random() & 0xffff; m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK; m_loss_seq_nr = m_acked_seq_nr; m_ack_nr = 0; m_fast_resend_seq_nr = m_seq_nr; packet* p = static_cast(malloc(sizeof(packet) + sizeof(utp_header))); p->size = sizeof(utp_header); p->header_size = sizeof(utp_header); p->num_transmissions = 0; p->mtu_probe = false; #ifdef TORRENT_DEBUG p->num_fast_resend = 0; #endif p->need_resend = false; utp_header* h = reinterpret_cast(p->buf); h->type_ver = (ST_SYN << 4) | 1; h->extension = utp_no_extension; // using recv_id here is intentional! This is an odd // thing in uTP. The syn packet is sent with the connection // ID that it expects to receive the syn ack on. All // subsequent connection IDs will be this plus one. h->connection_id = m_recv_id; h->timestamp_difference_microseconds = m_reply_micro; h->wnd_size = 0; h->seq_nr = m_seq_nr; h->ack_nr = 0; time_point now = clock_type::now(); p->send_time = now; h->timestamp_microseconds = boost::uint32_t( total_microseconds(now.time_since_epoch()) & 0xffffffff); #if TORRENT_UTP_LOG UTP_LOGV("%8p: send_syn seq_nr:%d id:%d target:%s\n" , static_cast(this), int(m_seq_nr), int(m_recv_id) , print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str()); #endif error_code ec; m_sm->send_packet(udp::endpoint(m_remote_address, m_port) , reinterpret_cast(h) , sizeof(utp_header), ec); if (ec == error::would_block || ec == error::try_again) { #if TORRENT_UTP_LOG UTP_LOGV("%8p: socket stalled\n", static_cast(this)); #endif if (!m_stalled) { m_stalled = true; m_sm->subscribe_writable(this); } } else if (ec) { free(p); m_error = ec; set_state(UTP_STATE_ERROR_WAIT); test_socket_state(); return; } if (!m_stalled) ++p->num_transmissions; TORRENT_ASSERT(!m_outbuf.at(m_seq_nr)); m_outbuf.insert(m_seq_nr, p); TORRENT_ASSERT(h->seq_nr == m_seq_nr); TORRENT_ASSERT(p->buf == reinterpret_cast(h)); m_seq_nr = (m_seq_nr + 1) & ACK_MASK; TORRENT_ASSERT(!m_error); set_state(UTP_STATE_SYN_SENT); #if TORRENT_UTP_LOG UTP_LOGV("%8p: state:%s\n", static_cast(this), socket_state_names[m_state]); #endif } // if a send ever failed with EWOULDBLOCK, we // subscribe to the udp socket and will be // signalled with this function. void utp_socket_impl::writable() { #if TORRENT_UTP_LOG UTP_LOGV("%8p: writable\n", static_cast(this)); #endif if (should_delete()) return; while(send_pkt()); maybe_trigger_send_callback(); } void utp_socket_impl::send_fin() { INVARIANT_CHECK; send_pkt(pkt_fin); // unless there was an error, we're now // in FIN-SENT state if (!m_error) set_state(UTP_STATE_FIN_SENT); #if TORRENT_UTP_LOG UTP_LOGV("%8p: state:%s\n", static_cast(this), socket_state_names[m_state]); #endif } void utp_socket_impl::send_reset(utp_header const* ph) { INVARIANT_CHECK; utp_header h; h.type_ver = (ST_RESET << 4) | 1; h.extension = utp_no_extension; h.connection_id = m_send_id; h.timestamp_difference_microseconds = m_reply_micro; h.wnd_size = 0; h.seq_nr = random() & 0xffff; h.ack_nr = ph->seq_nr; time_point now = clock_type::now(); h.timestamp_microseconds = boost::uint32_t( total_microseconds(now.time_since_epoch()) & 0xffffffff); UTP_LOGV("%8p: send_reset seq_nr:%d id:%d ack_nr:%d\n" , static_cast(this), int(h.seq_nr), int(m_send_id), int(ph->seq_nr)); // ignore errors here error_code ec; m_sm->send_packet(udp::endpoint(m_remote_address, m_port) , reinterpret_cast(&h), sizeof(h), ec); if (ec) { UTP_LOGV("%8p: socket error: %s\n" , static_cast(this) , ec.message().c_str()); } } std::size_t utp_socket_impl::available() const { return m_receive_buffer_size; } void utp_socket_impl::parse_close_reason(boost::uint8_t const* ptr, int size) { if (size != 4) return; // skip reserved bytes ptr += 2; boost::uint16_t incoming_close_reason = detail::read_uint16(ptr); UTP_LOGV("%8p: incoming close_reason: %d\n" , static_cast(this), int(incoming_close_reason)); if (m_userdata == 0 || !m_attached) return; utp_stream::on_close_reason(m_userdata, incoming_close_reason); } void utp_socket_impl::parse_sack(boost::uint16_t const packet_ack, boost::uint8_t const* ptr , int const size, int* acked_bytes, time_point const now, boost::uint32_t& min_rtt) { INVARIANT_CHECK; if (size == 0) return; // this is the sequence number the current bit represents int ack_nr = (packet_ack + 2) & ACK_MASK; #if TORRENT_VERBOSE_UTP_LOG std::string bitmask; bitmask.reserve(size); for (boost::uint8_t const* b = ptr, *end = ptr + size; b != end; ++b) { unsigned char bitfield = unsigned(*b); unsigned char mask = 1; // for each bit for (int i = 0; i < 8; ++i) { bitmask += (mask & bitfield) ? "1" : "0"; mask <<= 1; } } UTP_LOGV("%8p: got SACK first:%d %s our_seq_nr:%u fast_resend_seq_nr:%d\n" , static_cast(this), ack_nr, bitmask.c_str(), m_seq_nr, m_fast_resend_seq_nr); #endif boost::array resend; int num_to_resend = 0; // this was implicitly lost if (!compare_less_wrap((packet_ack + 1) & ACK_MASK, m_fast_resend_seq_nr, ACK_MASK)) { resend[num_to_resend++] = (packet_ack + 1) & ACK_MASK; } // for each byte boost::uint8_t const* const start = ptr; boost::uint8_t const* const end = ptr + size; for (; ptr != end; ++ptr) { unsigned char const bitfield = unsigned(*ptr); unsigned char mask = 1; // for each bit for (int i = 0; i < 8; ++i) { if (mask & bitfield) { // this bit was set, ack_nr was received packet* p = m_outbuf.remove(ack_nr); if (p) { *acked_bytes += p->size - p->header_size; ack_packet(p, now, min_rtt, ack_nr); } else { // this packet might have been acked by a previous // selective ack maybe_inc_acked_seq_nr(); } } else if (!compare_less_wrap(ack_nr, m_fast_resend_seq_nr, ACK_MASK) && num_to_resend < resend.size()) { resend[num_to_resend++] = ack_nr; } mask <<= 1; ack_nr = (ack_nr + 1) & ACK_MASK; // we haven't sent packets past this point. // if there are any more bits set, we have to // ignore them anyway if (ack_nr == m_seq_nr) break; } if (ack_nr == m_seq_nr) break; } if (m_outbuf.empty()) m_duplicate_acks = 0; // now, scan the bits in reverse, and count the number of ACKed packets. Only // lost packets followed by 'dup_ack_limit' packets may be resent // start with the sequence number represented by the last bit in the SACK // bitmask int last_resend = (packet_ack + 1 + size * 8) & ACK_MASK; // the number of acked packets past the fast re-send sequence number // this is used to determine if we should trigger more fast re-sends int dups = 0; for (boost::uint8_t const* i = end; i != start; --i) { unsigned char const bitfield = unsigned(i[-1]); unsigned char mask = 0x80; // for each bit for (int k = 0; k < 8; ++k) { if (mask & bitfield) ++dups; if (dups > dup_ack_limit) break; last_resend = (last_resend - 1) & ACK_MASK; mask >>= 1; } if (dups > dup_ack_limit) break; } // we did not get enough packets acked in this message to warrant a resend if (dups <= dup_ack_limit) { UTP_LOGV("%8p: only %d ACKs in SACK, requires more than %d to trigger fast retransmit\n" , static_cast(this), dups, dup_ack_limit); num_to_resend = 0; } // now we need to (likely) prune the tail of the resend list, since all // "unacked" packets that weren't followed by an acked one, don't count while (num_to_resend > 0 && !compare_less_wrap(resend[num_to_resend - 1], last_resend, ACK_MASK)) { --num_to_resend; } TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); bool cut_cwnd = true; // we received more than dup_ack_limit ACKs in this SACK message. // trigger fast re-send. This is not an equal check because 3 identical ACKS // are only 2 duplicates for (int i = 0; i < num_to_resend; ++i) { boost::uint16_t const pkt_seq = resend[i]; packet* p = m_outbuf.at(pkt_seq); UTP_LOGV("%8p: Packet %d lost. (fast_resend_seq_nr:%d trigger fast-resend)\n" , static_cast(this), pkt_seq, m_fast_resend_seq_nr); if (!p) continue; // don't cut cwnd if the packet we lost was the MTU probe // the logic to handle a lost MTU probe is in resend_packet() if (cut_cwnd && (pkt_seq != m_mtu_seq || m_mtu_seq == 0)) { experienced_loss(pkt_seq, now); cut_cwnd = false; } if (resend_packet(p, true)) { m_duplicate_acks = 0; m_fast_resend_seq_nr = (pkt_seq + 1) & ACK_MASK; } } } // copies data from the write buffer into the packet // pointed to by ptr void utp_socket_impl::write_payload(boost::uint8_t* ptr, int size) { INVARIANT_CHECK; #ifdef TORRENT_DEBUG int write_buffer_size = 0; for (std::vector::iterator i = m_write_buffer.begin() , end(m_write_buffer.end()); i != end; ++i) { write_buffer_size += i->len; } TORRENT_ASSERT(m_write_buffer_size == write_buffer_size); #endif TORRENT_ASSERT(!m_write_buffer.empty() || size == 0); TORRENT_ASSERT(m_write_buffer_size >= size); std::vector::iterator i = m_write_buffer.begin(); if (size == 0) return; int buffers_to_clear = 0; while (size > 0) { // i points to the iovec we'll start copying from int to_copy = (std::min)(size, int(i->len)); TORRENT_ASSERT(to_copy >= 0); TORRENT_ASSERT(to_copy < INT_MAX / 2 && m_written < INT_MAX / 2); memcpy(ptr, static_cast(i->buf), to_copy); size -= to_copy; m_written += to_copy; ptr += to_copy; i->len -= to_copy; TORRENT_ASSERT(m_write_buffer_size >= to_copy); m_write_buffer_size -= to_copy; i->buf = static_cast(i->buf) + to_copy; if (i->len == 0) ++buffers_to_clear; ++i; } if (buffers_to_clear) m_write_buffer.erase(m_write_buffer.begin() , m_write_buffer.begin() + buffers_to_clear); #ifdef TORRENT_DEBUG write_buffer_size = 0; for (std::vector::iterator j = m_write_buffer.begin() , end(m_write_buffer.end()); j != end; ++j) { write_buffer_size += j->len; } TORRENT_ASSERT(m_write_buffer_size == write_buffer_size); #endif } void utp_socket_impl::subscribe_drained() { INVARIANT_CHECK; if (m_subscribe_drained) return; UTP_LOGV("%8p: subscribe drained\n", static_cast(this)); m_subscribe_drained = true; m_sm->subscribe_drained(this); } void utp_socket_impl::defer_ack() { INVARIANT_CHECK; if (m_deferred_ack) return; UTP_LOGV("%8p: defer ack\n", static_cast(this)); m_deferred_ack = true; m_sm->defer_ack(this); } void utp_socket_impl::remove_sack_header(packet* p) { INVARIANT_CHECK; // remove the sack header boost::uint8_t* ptr = p->buf + sizeof(utp_header); utp_header* h = reinterpret_cast(p->buf); TORRENT_ASSERT(h->extension == utp_sack); h->extension = ptr[0]; int sack_size = ptr[1]; TORRENT_ASSERT(h->extension == utp_no_extension || h->extension == utp_close_reason); UTP_LOGV("%8p: removing SACK header, %d bytes\n" , static_cast(this), sack_size + 2); TORRENT_ASSERT(p->size >= p->header_size); TORRENT_ASSERT(p->header_size >= sizeof(utp_header) + sack_size + 2); memmove(ptr, ptr + sack_size + 2, p->size - p->header_size); p->header_size -= sack_size + 2; p->size -= sack_size + 2; } struct holder { holder(char* buf = NULL): m_buf(buf) {} ~holder() { free(m_buf); } void reset(char* buf) { free(m_buf); m_buf = buf; } char* release() { char* ret = m_buf; m_buf = NULL; return ret; } private: // not copyable holder(holder const&); holder& operator=(holder const&); char* m_buf; }; // sends a packet, pulls data from the write buffer (if there's any) // if ack is true, we need to send a packet regardless of if there's // any data. Returns true if we could send more data (i.e. call // send_pkt() again) // returns true if there is more space for payload in our // congestion window, false if there is no more space. bool utp_socket_impl::send_pkt(int const flags) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; #endif bool const force = (flags & pkt_ack) || (flags & pkt_fin); // TORRENT_ASSERT(m_state != UTP_STATE_FIN_SENT || (flags & pkt_ack)); // first see if we need to resend any packets // TODO: this loop is not very efficient. It could be fixed by having // a separate list of sequence numbers that need resending for (int i = (m_acked_seq_nr + 1) & ACK_MASK; i != m_seq_nr; i = (i + 1) & ACK_MASK) { packet* p = m_outbuf.at(i); if (!p) continue; if (!p->need_resend) continue; if (!resend_packet(p)) { // we couldn't resend the packet. It probably doesn't // fit in our cwnd. If force is set, we need to continue // to send our packet anyway, if we don't have force set, // we might as well return if (!force) return false; // resend_packet might have failed if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return false; break; } // don't fast-resend this packet if (m_fast_resend_seq_nr == i) m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; } // MTU DISCOVERY // under these conditions, the next packet we send should be an MTU probe. // MTU probes get to use the mid-point packet size, whereas other packets // use a conservative packet size of the largest known to work. The reason // for the cwnd condition is to make sure the probe is surrounded by non- // probes, to be able to distinguish a loss of the probe vs. just loss in // general. bool const mtu_probe = (m_mtu_seq == 0 && m_write_buffer_size >= m_mtu_floor * 3 && m_seq_nr != 0 && (m_cwnd >> 16) > m_mtu_floor * 3); // for non MTU-probes, use the conservative packet size int const effective_mtu = mtu_probe ? m_mtu : m_mtu_floor; boost::uint32_t const close_reason = m_close_reason; int sack = 0; if (m_inbuf.size()) { const int max_sack_size = effective_mtu - sizeof(utp_header) - 2 // for sack padding/header - (close_reason ? 6 : 0); // the SACK bitfield should ideally fit all // the pieces we have successfully received sack = (m_inbuf.span() + 7) / 8; if (sack > max_sack_size) sack = max_sack_size; } int const header_size = sizeof(utp_header) + (sack ? sack + 2 : 0) + (close_reason ? 6 : 0); int payload_size = (std::min)(m_write_buffer_size , effective_mtu - header_size); TORRENT_ASSERT(payload_size >= 0); // if we have one MSS worth of data, make sure it fits in our // congestion window and the advertised receive window from // the other end. if (m_bytes_in_flight + payload_size > (std::min)(int(m_cwnd >> 16) , int(m_adv_wnd))) { // this means there's not enough room in the send window for // another packet. We have to hold off sending this data. // we still need to send an ACK though // if we're trying to send a FIN, make an exception if ((flags & pkt_fin) == 0) payload_size = 0; // we're constrained by the window size m_cwnd_full = true; UTP_LOGV("%8p: no space in window send_buffer_size:%d cwnd:%d " "adv_wnd:%d in-flight:%d mtu:%d\n" , static_cast(this), m_write_buffer_size, int(m_cwnd >> 16) , m_adv_wnd, m_bytes_in_flight, m_mtu); if (!force) { #if TORRENT_UTP_LOG UTP_LOGV("%8p: skipping send seq_nr:%d ack_nr:%d " "id:%d target:%s header_size:%d error:%s send_buffer_size:%d cwnd:%d " "adv_wnd:%d in-flight:%d mtu:%d effective-mtu:%d\n" , static_cast(this), int(m_seq_nr), int(m_ack_nr) , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() , header_size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) , m_adv_wnd, m_bytes_in_flight, m_mtu, effective_mtu); #endif return false; } } // if we don't have any data to send, or can't send any data // and we don't have any data to force, don't send a packet if (payload_size == 0 && !force && !m_nagle_packet) { #if TORRENT_UTP_LOG UTP_LOGV("%8p: skipping send (no payload and no force) seq_nr:%d ack_nr:%d " "id:%d target:%s header_size:%d error:%s send_buffer_size:%d cwnd:%d " "adv_wnd:%d in-flight:%d mtu:%d\n" , static_cast(this), int(m_seq_nr), int(m_ack_nr) , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() , header_size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) , m_adv_wnd, m_bytes_in_flight, m_mtu); #endif return false; } int packet_size = header_size + payload_size; packet* p = NULL; boost::uint8_t* ptr = NULL; utp_header* h = NULL; #if TORRENT_USE_ASSERTS bool stack_alloced = false; #endif // used to free the packet buffer in case we exit the // function early holder buf_holder; // payload size being zero means we're just sending // an force. We should not pick up the nagle packet if (!m_nagle_packet || (payload_size == 0 && force)) { // we only need a heap allocation if we have payload and // need to keep the packet around (in the outbuf) if (payload_size) { p = static_cast(malloc(sizeof(packet) + effective_mtu)); p->allocated = effective_mtu; buf_holder.reset(reinterpret_cast(p)); m_sm->inc_stats_counter(counters::utp_payload_pkts_out); } else { #if TORRENT_USE_ASSERTS stack_alloced = true; #endif TORRENT_ASSERT(force); // this alloca() statement won't necessarily produce // correctly aligned memory. That's why we ask for 7 more bytes // and adjust our pointer to be aligned later p = reinterpret_cast(TORRENT_ALLOCA(char, sizeof(packet) + packet_size + sizeof(packet*) - 1)); p = reinterpret_cast(align_pointer(p)); p->allocated = packet_size; } p->size = packet_size; p->header_size = packet_size - payload_size; p->num_transmissions = 0; #ifdef TORRENT_DEBUG p->num_fast_resend = 0; #endif p->mtu_probe = false; p->need_resend = false; ptr = p->buf; h = reinterpret_cast(ptr); ptr += sizeof(utp_header); h->extension = sack ? utp_sack : close_reason ? utp_close_reason : utp_no_extension; h->connection_id = m_send_id; // seq_nr is ignored for ST_STATE packets, so it doesn't // matter that we say this is a sequence number we haven't // actually sent yet h->seq_nr = m_seq_nr; h->type_ver = ((payload_size ? ST_DATA : ST_STATE) << 4) | 1; write_payload(p->buf + p->header_size, payload_size); } else { // pick up the nagle packet and keep adding bytes to it p = m_nagle_packet; ptr = p->buf + sizeof(utp_header); h = reinterpret_cast(p->buf); TORRENT_ASSERT(h->seq_nr == m_seq_nr); // if the packet has a selective force header, we'll need // to update it if (h->extension == utp_sack) { sack = ptr[1]; // if we no longer have any out-of-order packets waiting // to be delivered, there's no selective ack to be sent. if (m_inbuf.size() == 0) { // we need to remove the sack header remove_sack_header(p); sack = 0; } } else sack = 0; boost::int32_t const size_left = (std::min)(p->allocated - p->size , m_write_buffer_size); write_payload(p->buf + p->size, size_left); p->size += size_left; if (size_left > 0) { UTP_LOGV("%8p: NAGLE appending %d bytes to nagle packet. new size: %d allocated: %d\n" , static_cast(this), size_left, p->size, p->allocated); } // did we fill up the whole mtu? // if we didn't, we may still send it if there's // no bytes in flight if (m_bytes_in_flight > 0 && p->size < p->allocated && !force && m_nagle) { return false; } // clear the nagle packet pointer and fall through // sending p m_nagle_packet = NULL; packet_size = p->size; payload_size = p->size - p->header_size; } if (sack) { *ptr++ = close_reason ? utp_close_reason : utp_no_extension; *ptr++ = sack; // bytes for SACK bitfield write_sack(ptr, sack); ptr += sack; TORRENT_ASSERT(ptr <= p->buf + p->header_size); } if (close_reason) { *ptr++ = utp_no_extension; *ptr++ = 4; detail::write_uint32(close_reason, ptr); } if (m_bytes_in_flight > 0 && p->size < p->allocated && !force && m_nagle) { // this is nagle. If we don't have a full packet // worth of payload to send AND we have at least // one outstanding packet, hold off. Once the // outstanding packet is acked, we'll send this // payload UTP_LOGV("%8p: NAGLE not enough payload send_buffer_size:%d cwnd:%d " "adv_wnd:%d in-flight:%d mtu:%d effective_mtu:%d\n" , static_cast(this), m_write_buffer_size, int(m_cwnd >> 16) , m_adv_wnd, m_bytes_in_flight, m_mtu, effective_mtu); TORRENT_ASSERT(m_nagle_packet == NULL); TORRENT_ASSERT(h->seq_nr == m_seq_nr); m_nagle_packet = p; buf_holder.release(); return false; } // for ST_DATA packets, payload size is 0. Such packets do not have unique // sequence numbers and should never be used as mtu probes if ((mtu_probe || p->mtu_probe) && payload_size > m_mtu_floor) { p->mtu_probe = true; m_mtu_seq = m_seq_nr; } else { p->mtu_probe = false; } h->timestamp_difference_microseconds = m_reply_micro; h->wnd_size = (std::max)(m_receive_buffer_capacity - m_buffered_incoming_bytes - m_receive_buffer_size, boost::int32_t(0)); h->ack_nr = m_ack_nr; // if this is a FIN packet, override the type if (flags & pkt_fin) h->type_ver = (ST_FIN << 4) | 1; // fill in the timestamp as late as possible time_point now = clock_type::now(); p->send_time = now; h->timestamp_microseconds = boost::uint32_t( total_microseconds(now.time_since_epoch()) & 0xffffffff); #if TORRENT_UTP_LOG UTP_LOG("%8p: sending packet seq_nr:%d ack_nr:%d type:%s " "id:%d target:%s size:%d error:%s send_buffer_size:%d cwnd:%d " "adv_wnd:%d in-flight:%d mtu:%d timestamp:%u time_diff:%u " "mtu_probe:%d extension:%d\n" , static_cast(this), int(h->seq_nr), int(h->ack_nr), packet_type_names[h->get_type()] , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() , p->size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) , m_adv_wnd, m_bytes_in_flight, m_mtu, boost::uint32_t(h->timestamp_microseconds) , boost::uint32_t(h->timestamp_difference_microseconds), int(p->mtu_probe) , h->extension); #endif error_code ec; m_sm->send_packet(udp::endpoint(m_remote_address, m_port) , reinterpret_cast(h), p->size, ec , p->mtu_probe ? utp_socket_manager::dont_fragment : 0); ++m_out_packets; m_sm->inc_stats_counter(counters::utp_packets_out); if (ec == error::message_size) { #if TORRENT_UTP_LOG UTP_LOGV("%8p: error sending packet: %s\n" , static_cast(this) , ec.message().c_str()); #endif // if we fail even though this is not a probe, we're screwed // since we'd have to repacketize TORRENT_ASSERT(p->mtu_probe); m_mtu_ceiling = p->size - 1; if (m_mtu_floor > m_mtu_ceiling) m_mtu_floor = m_mtu_ceiling; update_mtu_limits(); // resend the packet immediately without // it being an MTU probe p->mtu_probe = false; m_mtu_seq = 0; ec.clear(); #if TORRENT_UTP_LOG UTP_LOGV("%8p: re-sending\n", static_cast(this)); #endif m_sm->send_packet(udp::endpoint(m_remote_address, m_port) , reinterpret_cast(h), p->size, ec, 0); } if (ec == error::would_block || ec == error::try_again) { #if TORRENT_UTP_LOG UTP_LOGV("%8p: socket stalled\n", static_cast(this)); #endif if (!m_stalled) { m_stalled = true; m_sm->subscribe_writable(this); } } else if (ec) { TORRENT_ASSERT(stack_alloced != bool(payload_size)); m_error = ec; set_state(UTP_STATE_ERROR_WAIT); test_socket_state(); return false; } if (!m_stalled) ++p->num_transmissions; // if we have payload, we need to save the packet until it's acked // and progress m_seq_nr if (p->size > p->header_size) { // if we're sending a payload packet, there should not // be a nagle packet waiting for more data TORRENT_ASSERT(m_nagle_packet == NULL); TORRENT_ASSERT(h->seq_nr == m_seq_nr); // 0 is a special sequence number, since it's also used as "uninitialized". // we never send an mtu probe for sequence number 0 TORRENT_ASSERT(p->mtu_probe == (m_seq_nr == m_mtu_seq) || m_seq_nr == 0); // release the buffer, we're saving it in the circular // buffer of outgoing packets buf_holder.release(); packet* old = m_outbuf.insert(m_seq_nr, p); if (old) { TORRENT_ASSERT(reinterpret_cast(old->buf)->seq_nr == m_seq_nr); if (!old->need_resend) m_bytes_in_flight -= old->size - old->header_size; free(old); } TORRENT_ASSERT(h->seq_nr == m_seq_nr); m_seq_nr = (m_seq_nr + 1) & ACK_MASK; TORRENT_ASSERT(payload_size >= 0); m_bytes_in_flight += p->size - p->header_size; } else { TORRENT_ASSERT(h->seq_nr == m_seq_nr); } // if the socket is stalled, always return false, don't // try to write more packets. We'll keep writing once // the underlying UDP socket becomes writable return m_write_buffer_size > 0 && !m_cwnd_full && !m_stalled; } // size is in bytes void utp_socket_impl::write_sack(boost::uint8_t* buf, int size) const { INVARIANT_CHECK; TORRENT_ASSERT(m_inbuf.size()); int ack_nr = (m_ack_nr + 2) & ACK_MASK; boost::uint8_t* end = buf + size; for (; buf != end; ++buf) { *buf = 0; int mask = 1; for (int i = 0; i < 8; ++i) { if (m_inbuf.at(ack_nr)) *buf |= mask; mask <<= 1; ack_nr = (ack_nr + 1) & ACK_MASK; } } } bool utp_socket_impl::resend_packet(packet* p, bool fast_resend) { INVARIANT_CHECK; // for fast re-sends the packet hasn't been marked as needing resending TORRENT_ASSERT(p->need_resend || fast_resend); if (m_error) return false; if (((m_acked_seq_nr + 1) & ACK_MASK) == m_mtu_seq && m_mtu_seq != 0) { m_mtu_seq = 0; p->mtu_probe = false; // we got multiple acks for the packet before our probe, assume // it was dropped because it was too big m_mtu_ceiling = p->size - 1; update_mtu_limits(); } // we can only resend the packet if there's // enough space in our congestion window // since we can't re-packetize, some packets that are // larger than the congestion window must be allowed through // but only if we don't have any outstanding bytes int window_size_left = (std::min)(int(m_cwnd >> 16), int(m_adv_wnd)) - m_bytes_in_flight; if (!fast_resend && p->size - p->header_size > window_size_left && m_bytes_in_flight > 0) { m_cwnd_full = true; return false; } // plus one since we have fast-resend as well, which doesn't // necessarily trigger by a timeout TORRENT_ASSERT(p->num_transmissions < m_sm->num_resends() + 1); TORRENT_ASSERT(p->size - p->header_size >= 0); if (p->need_resend) m_bytes_in_flight += p->size - p->header_size; m_sm->inc_stats_counter(counters::utp_packet_resend); if (fast_resend) m_sm->inc_stats_counter(counters::utp_fast_retransmit); #ifdef TORRENT_DEBUG if (fast_resend) ++p->num_fast_resend; #endif p->need_resend = false; utp_header* h = reinterpret_cast(p->buf); // update packet header h->timestamp_difference_microseconds = m_reply_micro; p->send_time = clock_type::now(); h->timestamp_microseconds = boost::uint32_t( total_microseconds(p->send_time.time_since_epoch()) & 0xffffffff); // if the packet has a selective ack header, we'll need // to update it if (h->extension == utp_sack && h->ack_nr != m_ack_nr) { boost::uint8_t* ptr = p->buf + sizeof(utp_header); int sack_size = ptr[1]; if (m_inbuf.size()) { // update the sack header write_sack(ptr + 2, sack_size); TORRENT_ASSERT(ptr + sack_size + 2 <= p->buf + p->header_size); } else { remove_sack_header(p); } } h->ack_nr = m_ack_nr; error_code ec; m_sm->send_packet(udp::endpoint(m_remote_address, m_port) , reinterpret_cast(p->buf), p->size, ec); ++m_out_packets; m_sm->inc_stats_counter(counters::utp_packets_out); #if TORRENT_UTP_LOG UTP_LOGV("%8p: re-sending packet seq_nr:%d ack_nr:%d type:%s " "id:%d target:%s size:%d error:%s send_buffer_size:%d cwnd:%d " "adv_wnd:%d in-flight:%d mtu:%d timestamp:%u time_diff:%u\n" , static_cast(this), int(h->seq_nr), int(h->ack_nr), packet_type_names[h->get_type()] , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() , p->size, ec.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) , m_adv_wnd, m_bytes_in_flight, m_mtu, boost::uint32_t(h->timestamp_microseconds) , boost::uint32_t(h->timestamp_difference_microseconds)); #endif if (ec == error::would_block || ec == error::try_again) { #if TORRENT_UTP_LOG UTP_LOGV("%8p: socket stalled\n", static_cast(this)); #endif if (!m_stalled) { m_stalled = true; m_sm->subscribe_writable(this); } } else if (ec) { m_error = ec; set_state(UTP_STATE_ERROR_WAIT); test_socket_state(); return false; } if (!m_stalled) ++p->num_transmissions; return !m_stalled; } void utp_socket_impl::experienced_loss(int const seq_nr, time_point const now) { INVARIANT_CHECK; // the window size could go below one MMS here, if it does, // we'll get a timeout in about one second m_sm->inc_stats_counter(counters::utp_packet_loss); // since loss often comes in bursts, we only cut the // window in half once per RTT. This is implemented // by limiting which packets can cause us to cut the // window size. The first packet that's lost will // update the limit to the last sequence number we sent. // i.e. only packet sent after this loss can cause another // window size cut. The +1 is to turn the comparison into // less than or equal to. If we experience loss of the // same packet again, ignore it. if (compare_less_wrap(seq_nr, m_loss_seq_nr + 1, ACK_MASK)) return; // don't reduce cwnd more than once every 100ms if (m_next_loss >= now) return; m_next_loss = now + milliseconds(m_sm->cwnd_reduce_timer()); // cut window size in 2 m_cwnd = std::max(m_cwnd * m_sm->loss_multiplier() / 100 , boost::int64_t(m_mtu) * (1 << 16)); m_loss_seq_nr = m_seq_nr; UTP_LOGV("%8p: Lost packet %d caused cwnd cut. m_loss_seq_nr:%d\n" , static_cast(this), seq_nr, m_seq_nr); // if we happen to be in slow-start mode, we need to leave it // note that we set ssthres to the window size _after_ reducing it. Next slow // start should end before we over shoot. if (m_slow_start) { m_ssthres = m_cwnd >> 16; m_slow_start = false; UTP_LOGV("%8p: experienced loss, slow_start -> 0 ssthres:%d\n" , static_cast(this), m_ssthres); } } void utp_socket_impl::set_state(int s) { if (s == m_state) return; m_sm->inc_stats_counter(counters::num_utp_idle + m_state, -1); m_state = s; m_sm->inc_stats_counter(counters::num_utp_idle + m_state, 1); } void utp_socket_impl::maybe_inc_acked_seq_nr() { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; #endif bool incremented = false; // don't pass m_seq_nr, since we move into sequence // numbers that haven't been sent yet, and aren't // supposed to be in m_outbuf // if the slot in m_outbuf is 0, it means the // packet has been ACKed and removed from the send buffer while (((m_acked_seq_nr + 1) & ACK_MASK) != m_seq_nr && m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) == 0) { // increment the fast resend sequence number if (m_fast_resend_seq_nr == m_acked_seq_nr) m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; m_acked_seq_nr = (m_acked_seq_nr + 1) & ACK_MASK; incremented = true; } if (!incremented) return; // update loss seq number if it's less than the packet // that was just acked. If loss seq nr is greater, it suggests // that we're still in a window that has experienced loss if (compare_less_wrap(m_loss_seq_nr, m_acked_seq_nr, ACK_MASK)) m_loss_seq_nr = m_acked_seq_nr; m_duplicate_acks = 0; } void utp_socket_impl::ack_packet(packet* p, time_point const receive_time , boost::uint32_t& min_rtt, boost::uint16_t const seq_nr) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; #endif TORRENT_ASSERT(p); // verify that the packet we're removing was in fact sent // with the sequence number we expect TORRENT_ASSERT(reinterpret_cast(p->buf)->seq_nr == seq_nr); if (!p->need_resend) { TORRENT_ASSERT(m_bytes_in_flight >= p->size - p->header_size); m_bytes_in_flight -= p->size - p->header_size; } if (seq_nr == m_mtu_seq && m_mtu_seq != 0) { TORRENT_ASSERT(p->mtu_probe); // our mtu probe was acked! m_mtu_floor = (std::max)(m_mtu_floor, p->size); update_mtu_limits(); } // increment the acked sequence number counter maybe_inc_acked_seq_nr(); boost::uint32_t rtt = boost::uint32_t(total_microseconds(receive_time - p->send_time)); if (receive_time < p->send_time) { // this means our clock is not monotonic. Just assume the RTT was 100 ms rtt = 100000; // the clock for this platform is not monotonic! TORRENT_ASSERT(false); } UTP_LOGV("%8p: acked packet %d (%d bytes) (rtt:%u)\n" , static_cast(this), seq_nr, p->size - p->header_size, rtt / 1000); m_rtt.add_sample(rtt / 1000); if (rtt < min_rtt) min_rtt = rtt; free(p); } void utp_socket_impl::incoming(boost::uint8_t const* buf, int size, packet* p , time_point /* now */) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; #endif while (!m_read_buffer.empty()) { UTP_LOGV("%8p: incoming: have user buffer (%d)\n", static_cast(this), m_read_buffer_size); if (p) { buf = p->buf + p->header_size; TORRENT_ASSERT(p->size - p->header_size >= size); } iovec_t* target = &m_read_buffer.front(); int to_copy = (std::min)(size, int(target->len)); memcpy(target->buf, buf, to_copy); m_read += to_copy; target->buf = reinterpret_cast(target->buf) + to_copy; target->len -= to_copy; buf += to_copy; UTP_LOGV("%8p: copied %d bytes into user receive buffer\n", static_cast(this), to_copy); TORRENT_ASSERT(m_read_buffer_size >= to_copy); m_read_buffer_size -= to_copy; size -= to_copy; if (target->len == 0) m_read_buffer.erase(m_read_buffer.begin()); if (p) { p->header_size += to_copy; TORRENT_ASSERT(p->header_size <= p->size); } if (size == 0) { TORRENT_ASSERT(p == 0 || p->header_size == p->size); free(p); return; } } TORRENT_ASSERT(m_read_buffer_size == 0); if (!p) { TORRENT_ASSERT(buf); p = static_cast(malloc(sizeof(packet) + size)); p->size = size; p->header_size = 0; memcpy(p->buf, buf, size); } // save this packet until the client issues another read m_receive_buffer.push_back(p); m_receive_buffer_size += p->size - p->header_size; UTP_LOGV("%8p: incoming: saving packet in receive buffer (%d)\n", static_cast(this), m_receive_buffer_size); check_receive_buffers(); } bool utp_socket_impl::cancel_handlers(error_code const& ec, bool kill) { INVARIANT_CHECK; TORRENT_ASSERT(ec); bool ret = m_read_handler || m_write_handler || m_connect_handler; // calling the callbacks with m_userdata being 0 will just crash TORRENT_ASSERT((ret && bool(m_userdata)) || !ret); bool read = m_read_handler; bool write = m_write_handler; bool connect = m_connect_handler; m_read_handler = false; m_write_handler = false; m_connect_handler = false; if (read) utp_stream::on_read(m_userdata, 0, ec, kill); if (write) utp_stream::on_write(m_userdata, 0, ec, kill); if (connect) utp_stream::on_connect(m_userdata, ec, kill); return ret; } bool utp_socket_impl::consume_incoming_data( utp_header const* ph, boost::uint8_t const* ptr, int payload_size , time_point const now) { INVARIANT_CHECK; if (ph->get_type() != ST_DATA) return false; if (m_eof && m_ack_nr == m_eof_seq_nr) { // What?! We've already received a FIN and everything up // to it has been acked. Ignore this packet UTP_LOG("%8p: ERROR: ignoring packet on shut down socket\n" , static_cast(this)); return true; } if (m_read_buffer_size == 0 && m_receive_buffer_size >= m_receive_buffer_capacity - m_buffered_incoming_bytes) { // if we don't have a buffer from the upper layer, and the // number of queued up bytes, waiting for the upper layer, // exceeds the advertised receive window, start ignoring // more data packets UTP_LOG("%8p: ERROR: our advertized window is not honored. " "recv_buf: %d buffered_in: %d max_size: %d\n" , static_cast(this), m_receive_buffer_size, m_buffered_incoming_bytes, m_receive_buffer_capacity); return false; } if (ph->seq_nr == ((m_ack_nr + 1) & ACK_MASK)) { TORRENT_ASSERT(m_inbuf.at(m_ack_nr) == 0); if (m_buffered_incoming_bytes + m_receive_buffer_size + payload_size > m_receive_buffer_capacity) { UTP_LOGV("%8p: other end is not honoring our advertised window, dropping packet\n" , static_cast(this)); return true; } // we received a packet in order incoming(ptr, payload_size, 0, now); m_ack_nr = (m_ack_nr + 1) & ACK_MASK; // If this packet was previously in the reorder buffer // it would have been acked when m_ack_nr-1 was acked. TORRENT_ASSERT(m_inbuf.at(m_ack_nr) == 0); UTP_LOGV("%8p: remove inbuf: %d (%d)\n" , static_cast(this), m_ack_nr, int(m_inbuf.size())); for (;;) { int const next_ack_nr = (m_ack_nr + 1) & ACK_MASK; packet* p = m_inbuf.remove(next_ack_nr); if (!p) break; m_buffered_incoming_bytes -= p->size - p->header_size; incoming(0, p->size - p->header_size, p, now); m_ack_nr = next_ack_nr; UTP_LOGV("%8p: reordered remove inbuf: %d (%d)\n" , static_cast(this), m_ack_nr, int(m_inbuf.size())); } } else { // this packet was received out of order. Stick it in the // reorder buffer until it can be delivered in order // have we already received this packet and passed it on // to the client? if (!compare_less_wrap(m_ack_nr, ph->seq_nr, ACK_MASK)) { UTP_LOGV("%8p: already received seq_nr: %d\n" , static_cast(this), int(ph->seq_nr)); return true; } // do we already have this packet? If so, just ignore it if (m_inbuf.at(ph->seq_nr)) { UTP_LOGV("%8p: already received seq_nr: %d\n" , static_cast(this), int(ph->seq_nr)); return true; } if (m_buffered_incoming_bytes + m_receive_buffer_size + payload_size > m_receive_buffer_capacity) { UTP_LOGV("%8p: other end is not honoring our advertised window, dropping packet %d\n" , static_cast(this), int(ph->seq_nr)); return true; } // we don't need to save the packet header, just the payload packet* p = static_cast(malloc(sizeof(packet) + payload_size)); p->size = payload_size; p->header_size = 0; p->num_transmissions = 0; #ifdef TORRENT_DEBUG p->num_fast_resend = 0; #endif p->need_resend = false; memcpy(p->buf, ptr, payload_size); m_inbuf.insert(ph->seq_nr, p); m_buffered_incoming_bytes += p->size; UTP_LOGV("%8p: out of order. insert inbuf: %d (%d) m_ack_nr: %d\n" , static_cast(this), int(ph->seq_nr), int(m_inbuf.size()), m_ack_nr); } return false; } // returns true of the socket was closed bool utp_socket_impl::test_socket_state() { INVARIANT_CHECK; // if the socket is in a state where it's dead, just waiting to // tell the client that it's closed. Do that and transition into // the deleted state, where it will be deleted // it might be possible to get here twice, in which we need to // cancel any new handlers as well, even though we're already // in the delete state if (!m_error) return false; TORRENT_ASSERT(m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE); #if TORRENT_UTP_LOG UTP_LOGV("%8p: state:%s error:%s\n" , static_cast(this), socket_state_names[m_state], m_error.message().c_str()); #endif if (cancel_handlers(m_error, true)) { set_state(UTP_STATE_DELETE); #if TORRENT_UTP_LOG UTP_LOGV("%8p: state:%s\n", static_cast(this), socket_state_names[m_state]); #endif return true; } return false; } void utp_socket_impl::init_mtu(int link_mtu, int utp_mtu) { INVARIANT_CHECK; if (link_mtu > TORRENT_ETHERNET_MTU) { // we can't use larger packets than this since we're // not allocating any more memory for socket buffers int decrease = link_mtu - TORRENT_ETHERNET_MTU; utp_mtu -= decrease; link_mtu -= decrease; } // set the ceiling to what we found out from the interface m_mtu_ceiling = utp_mtu; // start in the middle of the PMTU search space m_mtu = (m_mtu_ceiling + m_mtu_floor) / 2; if (m_mtu > m_mtu_ceiling) m_mtu = m_mtu_ceiling; if (m_mtu_floor > utp_mtu) m_mtu_floor = utp_mtu; // if the window size is smaller than one packet size // set it to one if ((m_cwnd >> 16) < m_mtu) m_cwnd = boost::int64_t(m_mtu) * (1 << 16); UTP_LOGV("%8p: initializing MTU to: %d [%d, %d]\n" , static_cast(this), m_mtu, m_mtu_floor, m_mtu_ceiling); } // return false if this is an invalid packet bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size , udp::endpoint const& ep, time_point receive_time) { INVARIANT_CHECK; m_sm->inc_stats_counter(counters::utp_packets_in); if (size < sizeof(utp_header)) { UTP_LOG("%8p: ERROR: incoming packet size too small:%d (ignored)\n" , static_cast(this), size); m_sm->inc_stats_counter(counters::utp_invalid_pkts_in); return false; } utp_header const* ph = reinterpret_cast(buf); if (ph->get_version() != 1) { UTP_LOG("%8p: ERROR: incoming packet version:%d (ignored)\n" , static_cast(this), int(ph->get_version())); m_sm->inc_stats_counter(counters::utp_invalid_pkts_in); return false; } // SYN packets have special (reverse) connection ids if (ph->get_type() != ST_SYN && ph->connection_id != m_recv_id) { UTP_LOG("%8p: ERROR: incoming packet id:%d expected:%d (ignored)\n" , static_cast(this), int(ph->connection_id), int(m_recv_id)); m_sm->inc_stats_counter(counters::utp_invalid_pkts_in); return false; } if (ph->get_type() >= NUM_TYPES) { UTP_LOG("%8p: ERROR: incoming packet type:%d (ignored)\n" , static_cast(this), int(ph->get_type())); m_sm->inc_stats_counter(counters::utp_invalid_pkts_in); return false; } if (m_state == UTP_STATE_NONE && ph->get_type() == ST_SYN) { m_remote_address = ep.address(); m_port = ep.port(); } if (m_state != UTP_STATE_NONE && ph->get_type() == ST_SYN) { UTP_LOG("%8p: ERROR: incoming packet type:ST_SYN (ignored)\n" , static_cast(this)); m_sm->inc_stats_counter(counters::utp_invalid_pkts_in); return true; } bool step = false; if (receive_time - m_last_history_step > minutes(1)) { step = true; m_last_history_step = receive_time; } // this is the difference between their send time and our receive time // 0 means no sample yet boost::uint32_t their_delay = 0; if (ph->timestamp_microseconds != 0) { boost::uint32_t timestamp = boost::uint32_t(total_microseconds( receive_time.time_since_epoch()) & 0xffffffff); m_reply_micro = timestamp - ph->timestamp_microseconds; boost::uint32_t prev_base = m_their_delay_hist.initialized() ? m_their_delay_hist.base() : 0; their_delay = m_their_delay_hist.add_sample(m_reply_micro, step); int base_change = m_their_delay_hist.base() - prev_base; UTP_LOGV("%8p: their_delay::add_sample:%u prev_base:%u new_base:%u\n" , static_cast(this), m_reply_micro, prev_base, m_their_delay_hist.base()); if (prev_base && base_change < 0 && base_change > -10000 && m_delay_hist.initialized()) { // their base delay went down. This is caused by clock drift. To compensate, // adjust our base delay upwards // don't adjust more than 10 ms. If the change is that big, something is probably wrong m_delay_hist.adjust_base(-base_change); } UTP_LOGV("%8p: incoming packet reply_micro:%u base_change:%d\n" , static_cast(this), m_reply_micro, prev_base ? base_change : 0); } // is this ACK valid? If the other end is ACKing // a packet that hasn't been sent yet // just ignore it. A 3rd party could easily inject a packet // like this in a stream, don't sever it because of it. // since m_seq_nr is the sequence number of the next packet // we'll send (and m_seq_nr-1 was the last packet we sent), // if the ACK we got is greater than the last packet we sent // something is wrong. // If our state is state_none, this packet must be a syn packet // and the ack_nr should be ignored boost::uint16_t const cmp_seq_nr = (m_state == UTP_STATE_SYN_SENT && ph->get_type() == ST_STATE) ? m_seq_nr : (m_seq_nr - 1) & ACK_MASK; if ((m_state != UTP_STATE_NONE || ph->get_type() != ST_SYN) && (compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK) || compare_less_wrap(ph->ack_nr, m_acked_seq_nr - dup_ack_limit, ACK_MASK))) { UTP_LOG("%8p: ERROR: incoming packet ack_nr:%d our seq_nr:%d our " "acked_seq_nr:%d (ignored)\n" , static_cast(this), int(ph->ack_nr), m_seq_nr, m_acked_seq_nr); m_sm->inc_stats_counter(counters::utp_redundant_pkts_in); return true; } // check to make sure the sequence number of this packet // is reasonable. If it's a data packet and we've already // received it, ignore it. This is either a stray old packet // that finally made it here (after having been re-sent) or // an attempt to interfere with the connection from a 3rd party // in both cases, we can safely ignore the timestamp and ACK // information in this packet /* // even if we've already received this packet, we need to // send another ack to it, since it may be a resend caused by // our ack getting dropped if (m_state != UTP_STATE_SYN_SENT && ph->get_type() == ST_DATA && !compare_less_wrap(m_ack_nr, ph->seq_nr, ACK_MASK)) { // we've already received this packet UTP_LOGV("%8p: incoming packet seq_nr:%d our ack_nr:%d (ignored)\n" , static_cast(this), int(ph->seq_nr), m_ack_nr); m_sm->inc_stats_counter(counters::utp_redundant_pkts_in); return true; } */ // if the socket is closing, always ignore any packet // with a higher sequence number than the FIN sequence number // ST_STATE messages always include the next seqnr. if (m_eof && (compare_less_wrap(m_eof_seq_nr, ph->seq_nr, ACK_MASK) || (m_eof_seq_nr == ph->seq_nr && ph->get_type() != ST_STATE))) { #if TORRENT_UTP_LOG UTP_LOG("%8p: ERROR: incoming packet type: %s seq_nr:%d eof_seq_nr:%d (ignored)\n" , static_cast(this), packet_type_names[ph->get_type()], int(ph->seq_nr), m_eof_seq_nr); #endif return true; } if (ph->get_type() == ST_DATA) m_sm->inc_stats_counter(counters::utp_payload_pkts_in); // the number of packets that'll fit in the reorder buffer const boost::uint32_t max_packets_reorder = std::max(16, m_receive_buffer_capacity / 1100); if (m_state != UTP_STATE_NONE && m_state != UTP_STATE_SYN_SENT && compare_less_wrap((m_ack_nr + max_packets_reorder) & ACK_MASK, ph->seq_nr, ACK_MASK)) { // this is too far out to fit in our reorder buffer. Drop it // This is either an attack to try to break the connection // or a seriously damaged connection that lost a lot of // packets. Neither is very likely, and it should be OK // to drop the timestamp information. UTP_LOG("%8p: ERROR: incoming packet seq_nr:%d our ack_nr:%d (ignored)\n" , static_cast(this), int(ph->seq_nr), m_ack_nr); m_sm->inc_stats_counter(counters::utp_redundant_pkts_in); return true; } if (ph->get_type() == ST_RESET) { if (compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK)) { UTP_LOG("%8p: ERROR: invalid RESET packet, ack_nr:%d our seq_nr:%d (ignored)\n" , static_cast(this), int(ph->ack_nr), m_seq_nr); return true; } UTP_LOGV("%8p: incoming packet type:RESET\n", static_cast(this)); m_error = boost::asio::error::connection_reset; set_state(UTP_STATE_ERROR_WAIT); test_socket_state(); return true; } ++m_in_packets; // this is a valid incoming packet, update the timeout timer m_num_timeouts = 0; m_timeout = receive_time + milliseconds(packet_timeout()); UTP_LOGV("%8p: updating timeout to: now + %d\n" , static_cast(this), packet_timeout()); // the test for INT_MAX here is a work-around for a bug in uTorrent where // it's sometimes sent as INT_MAX when it is in fact uninitialized const boost::uint32_t sample = ph->timestamp_difference_microseconds == INT_MAX ? 0 : ph->timestamp_difference_microseconds; boost::uint32_t delay = 0; if (sample != 0) { delay = m_delay_hist.add_sample(sample, step); m_delay_sample_hist[m_delay_sample_idx++] = delay; if (m_delay_sample_idx >= num_delay_hist) m_delay_sample_idx = 0; } int acked_bytes = 0; TORRENT_ASSERT(m_bytes_in_flight >= 0); int prev_bytes_in_flight = m_bytes_in_flight; m_adv_wnd = ph->wnd_size; // if we get an ack for the same sequence number as // was last ACKed, and we have outstanding packets, // it counts as a duplicate ack. The reason to not count ST_DATA packets as // duplicate ACKs is because we may be receiving a stream of those // regardless of our outgoing traffic, which makes their ACK number not // indicative of a dropped packet if (ph->ack_nr == m_acked_seq_nr && m_outbuf.size() && ph->get_type() == ST_STATE) { ++m_duplicate_acks; } TORRENT_ASSERT_VAL(m_outbuf.size() > 0 || m_duplicate_acks == 0, m_duplicate_acks); boost::uint32_t min_rtt = (std::numeric_limits::max)(); TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); // has this packet already been ACKed? // if the ACK we just got is less than the max ACKed // sequence number, it doesn't tell us anything. // So, only act on it if the ACK is greater than the last acked // sequence number if (m_state != UTP_STATE_NONE && compare_less_wrap(m_acked_seq_nr, ph->ack_nr, ACK_MASK)) { int const next_ack_nr = ph->ack_nr; for (int ack_nr = (m_acked_seq_nr + 1) & ACK_MASK; ack_nr != ((next_ack_nr + 1) & ACK_MASK); ack_nr = (ack_nr + 1) & ACK_MASK) { if (m_fast_resend_seq_nr == ack_nr) m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; packet* p = m_outbuf.remove(ack_nr); if (!p) continue; acked_bytes += p->size - p->header_size; ack_packet(p, receive_time, min_rtt, ack_nr); } maybe_inc_acked_seq_nr(); if (m_outbuf.empty()) m_duplicate_acks = 0; } // look for extended headers boost::uint8_t const* ptr = buf; ptr += sizeof(utp_header); unsigned int extension = ph->extension; while (extension) { // invalid packet. It says it has an extension header // but the packet is too short if (ptr - buf + 2 > size) { UTP_LOG("%8p: ERROR: invalid extension header\n", static_cast(this)); m_sm->inc_stats_counter(counters::utp_invalid_pkts_in); return true; } int next_extension = *ptr++; int const len = *ptr++; if (ptr - buf + len > ptrdiff_t(size)) { UTP_LOG("%8p: ERROR: invalid extension header size:%d packet:%d\n" , static_cast(this), len, int(ptr - buf)); m_sm->inc_stats_counter(counters::utp_invalid_pkts_in); return true; } switch(extension) { case utp_sack: // selective ACKs parse_sack(ph->ack_nr, ptr, len, &acked_bytes, receive_time, min_rtt); break; case utp_close_reason: parse_close_reason(ptr, len); break; } ptr += len; extension = next_extension; } // the send operation in parse_sack() may have set the socket to an error // state, in which case we shouldn't continue if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; if (m_duplicate_acks >= dup_ack_limit && ((m_acked_seq_nr + 1) & ACK_MASK) == m_fast_resend_seq_nr) { // LOSS UTP_LOGV("%8p: Packet %d lost. (%d duplicate acks, trigger fast-resend)\n" , static_cast(this), m_fast_resend_seq_nr, m_duplicate_acks); // resend the lost packet packet* p = m_outbuf.at(m_fast_resend_seq_nr); TORRENT_ASSERT(p); // don't fast-resend this again m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; if (p) { // don't consider a lost probe as proper loss, it doesn't necessarily // signal congestion if (!p->mtu_probe) experienced_loss(m_fast_resend_seq_nr, receive_time); resend_packet(p, true); if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; } } // ptr points to the payload of the packet // size is the packet size, payload is the // number of payload bytes are in this packet const int header_size = ptr - buf; const int payload_size = size - header_size; #if TORRENT_UTP_LOG UTP_LOGV("%8p: incoming packet seq_nr:%d ack_nr:%d type:%s id:%d size:%d timestampdiff:%u timestamp:%u " "our ack_nr:%d our seq_nr:%d our acked_seq_nr:%d our state:%s\n" , static_cast(this), int(ph->seq_nr), int(ph->ack_nr), packet_type_names[ph->get_type()] , int(ph->connection_id), payload_size, boost::uint32_t(ph->timestamp_difference_microseconds) , boost::uint32_t(ph->timestamp_microseconds), m_ack_nr, m_seq_nr, m_acked_seq_nr, socket_state_names[m_state]); #endif if (ph->get_type() == ST_FIN) { // We ignore duplicate FIN packets, but we still need to ACK them. if (ph->seq_nr == ((m_ack_nr + 1) & ACK_MASK) || ph->seq_nr == m_ack_nr) { UTP_LOGV("%8p: FIN received in order\n", static_cast(this)); // The FIN arrived in order, nothing else is in the // reorder buffer. // TORRENT_ASSERT(m_inbuf.size() == 0); m_ack_nr = ph->seq_nr; // Transition to UTP_STATE_FIN_SENT. The sent FIN is also an ack // to the FIN we received. Once we're in UTP_STATE_FIN_SENT we // just need to wait for our FIN to be acked. if (m_state == UTP_STATE_FIN_SENT) { send_pkt(pkt_ack); if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; } else { send_fin(); if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; } } if (m_eof) { UTP_LOGV("%8p: duplicate FIN packet (ignoring)\n", static_cast(this)); return true; } m_eof = true; m_eof_seq_nr = ph->seq_nr; // we will respond with a fin once we have received everything up to m_eof_seq_nr } switch (m_state) { case UTP_STATE_NONE: { if (ph->get_type() == ST_SYN) { // if we're in state_none, the only thing // we accept are SYN packets. set_state(UTP_STATE_CONNECTED); m_remote_address = ep.address(); m_port = ep.port(); error_code ec; m_local_address = m_sm->local_endpoint(m_remote_address, ec).address(); m_ack_nr = ph->seq_nr; m_seq_nr = random() & 0xffff; m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK; m_loss_seq_nr = m_acked_seq_nr; m_fast_resend_seq_nr = m_seq_nr; #if TORRENT_UTP_LOG UTP_LOGV("%8p: received ST_SYN state:%s seq_nr:%d ack_nr:%d\n" , static_cast(this), socket_state_names[m_state], m_seq_nr, m_ack_nr); #endif if (m_send_id != ph->connection_id) { #if TORRENT_UTP_LOG UTP_LOGV("%8p: received invalid connection_id:%d expected: %d\n" , static_cast(this), int(ph->connection_id), int(m_send_id)); #endif return false; } TORRENT_ASSERT(m_recv_id == ((m_send_id + 1) & 0xffff)); defer_ack(); } else { #if TORRENT_UTP_LOG UTP_LOG("%8p: ERROR: type:%s state:%s (ignored)\n" , static_cast(this), packet_type_names[ph->get_type()], socket_state_names[m_state]); #endif } break; } case UTP_STATE_SYN_SENT: { // just wait for an ack to our SYN, ignore everything else if (ph->ack_nr != ((m_seq_nr - 1) & ACK_MASK)) { #if TORRENT_UTP_LOG UTP_LOGV("%8p: incorrect ack_nr (%d) waiting for %d\n" , static_cast(this), int(ph->ack_nr), (m_seq_nr - 1) & ACK_MASK); #endif break; } TORRENT_ASSERT(!m_error); set_state(UTP_STATE_CONNECTED); #if TORRENT_UTP_LOG UTP_LOGV("%8p: state:%s\n", static_cast(this), socket_state_names[m_state]); #endif // only progress our ack_nr on ST_DATA messages // since our m_ack_nr is uninitialized at this point // we still need to set it to something regardless if (ph->get_type() == ST_DATA) m_ack_nr = ph->seq_nr; else m_ack_nr = (ph->seq_nr - 1) & ACK_MASK; // notify the client that the socket connected if (m_connect_handler) { UTP_LOGV("%8p: calling connect handler\n", static_cast(this)); m_connect_handler = false; utp_stream::on_connect(m_userdata, m_error, false); } // fall through } case UTP_STATE_CONNECTED: { // the lowest seen RTT can be used to clamp the delay // within reasonable bounds. The one-way delay is never // higher than the round-trip time. if (sample && acked_bytes && prev_bytes_in_flight) { // only use the minimum from the last 3 delay measurements delay = *std::min_element(m_delay_sample_hist, m_delay_sample_hist + num_delay_hist); // it's impossible for delay to be more than the RTT, so make // sure to clamp it as a sanity check if (delay > min_rtt) delay = min_rtt; do_ledbat(acked_bytes, delay, prev_bytes_in_flight); m_send_delay = delay; } m_recv_delay = (std::min)(their_delay, min_rtt); consume_incoming_data(ph, ptr, payload_size, receive_time); // the parameter to send_pkt tells it if we're acking data // If we are, we'll send an ACK regardless of if we have any // space left in our send window or not. If we just got an ACK // (i.e. ST_STATE) we're not ACKing anything. If we just // received a FIN packet, we need to ack that as well bool has_ack = ph->get_type() == ST_DATA || ph->get_type() == ST_FIN || ph->get_type() == ST_SYN; boost::uint32_t prev_out_packets = m_out_packets; // the connection is connected and this packet made it past all the // checks. We can now assume the other end is not spoofing it's IP. if (ph->get_type() != ST_SYN) m_confirmed = true; // try to send more data as long as we can // if send_pkt returns true while (send_pkt()); if (has_ack && prev_out_packets == m_out_packets) { // we need to ack some data we received, and we didn't // end up sending any payload packets in the loop // above (because m_out_packets would have been incremented // in that case). This means we need to send an ack. // don't do it right away, because we may still receive // more packets. defer the ack to send as few acks as possible defer_ack(); } // we may want to call the user callback function at the end // of this round. Subscribe to that event subscribe_drained(); if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; // Everything up to the FIN has been received, respond with a FIN // from our side. if (m_eof && m_ack_nr == ((m_eof_seq_nr - 1) & ACK_MASK)) { UTP_LOGV("%8p: incoming stream consumed\n", static_cast(this)); // This transitions to the UTP_STATE_FIN_SENT state. send_fin(); if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; } TORRENT_ASSERT(!compare_less_wrap(m_seq_nr, m_acked_seq_nr, ACK_MASK)); #if TORRENT_UTP_LOG if (sample && acked_bytes && prev_bytes_in_flight) { char their_delay_base[20]; if (m_their_delay_hist.initialized()) snprintf(their_delay_base, sizeof(their_delay_base), "%u", m_their_delay_hist.base()); else strcpy(their_delay_base, "-"); char our_delay_base[20]; if (m_delay_hist.initialized()) snprintf(our_delay_base, sizeof(our_delay_base), "%u", m_delay_hist.base()); else strcpy(our_delay_base, "-"); UTP_LOG("%8p: " "actual_delay:%u " "our_delay:%f " "their_delay:%f " "off_target:%f " "max_window:%u " "upload_rate:%d " "delay_base:%s " "delay_sum:%f " "target_delay:%d " "acked_bytes:%d " "cur_window:%d " "scaled_gain:%f " "rtt:%u " "rate:%d " "quota:%d " "wnduser:%u " "rto:%d " "timeout:%d " "get_microseconds:%u " "cur_window_packets:%u " "packet_size:%d " "their_delay_base:%s " "their_actual_delay:%u " "seq_nr:%u " "acked_seq_nr:%u " "reply_micro:%u " "min_rtt:%u " "send_buffer:%d " "recv_buffer:%d " "fast_resend_seq_nr:%d " "ssthres:%d " "\n" , static_cast(this) , sample , float(delay / 1000.f) , float(their_delay / 1000.f) , float(int(m_sm->target_delay() - delay)) / 1000.f , boost::uint32_t(m_cwnd >> 16) , 0 , our_delay_base , float(delay + their_delay) / 1000.f , m_sm->target_delay() / 1000 , acked_bytes , m_bytes_in_flight , 0.f // float(scaled_gain) , m_rtt.mean() , int((m_cwnd * 1000 / (m_rtt.mean()?m_rtt.mean():50)) >> 16) , 0 , m_adv_wnd , packet_timeout() , int(total_milliseconds(m_timeout - receive_time)) , int(total_microseconds(receive_time.time_since_epoch())) , m_outbuf.size() , m_mtu , their_delay_base , boost::uint32_t(m_reply_micro) , m_seq_nr , m_acked_seq_nr , m_reply_micro , min_rtt / 1000 , m_write_buffer_size , m_read_buffer_size , m_fast_resend_seq_nr , m_ssthres); } #endif break; } case UTP_STATE_FIN_SENT: { // There are two ways we can end up in this state: // // 1. If the socket has been explicitly closed on our // side, in which case m_eof is false. // // 2. If we received a FIN from the remote side, in which // case m_eof is true. If this is the case, we don't // come here until everything up to the FIN has been // received. // // // // At this point m_seq_nr - 1 is the FIN sequence number. // We can receive both ST_DATA and ST_STATE here, because after // we have closed our end of the socket, the remote end might // have data in the pipeline. We don't really care about the // data, but we do have to ack it. Or rather, we have to ack // the FIN that will come after the data. // Case 1: // --------------------------------------------------------------- // // If we are here because the local endpoint was closed, we need // to first wait for all of our messages to be acked: // // if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) // // `m_seq_nr - 1` is the ST_FIN message that we sent. // // ---------------------- // // After that has happened we need to wait for the remote side // to send its ST_FIN message. When we receive that we send an // ST_STATE back to ack, and wait for a sufficient period. // During this wait we keep acking incoming ST_FIN's. This is // all handled at the top of this function. // // Note that the user handlers are all cancelled when the initial // close() call happens, so nothing will happen on the user side // after that. // Case 2: // --------------------------------------------------------------- // // If we are here because we received a ST_FIN message, and then // sent our own ST_FIN to ack that, we need to wait for our ST_FIN // to be acked: // // if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) // // `m_seq_nr - 1` is the ST_FIN message that we sent. // // After that has happened we know the remote side has all our // data, and we can gracefully shut down. if (consume_incoming_data(ph, ptr, payload_size, receive_time)) { break; } if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) { // When this happens we know that the remote side has // received all of our packets. UTP_LOGV("%8p: FIN acked\n", static_cast(this)); if (!m_attached) { UTP_LOGV("%8p: close initiated here, delete socket\n" , static_cast(this)); m_error = boost::asio::error::eof; set_state(UTP_STATE_DELETE); test_socket_state(); } else { UTP_LOGV("%8p: closing socket\n", static_cast(this)); m_error = boost::asio::error::eof; set_state(UTP_STATE_ERROR_WAIT); test_socket_state(); } } break; } case UTP_STATE_DELETE: default: { // respond with a reset send_reset(ph); break; } } return true; } void utp_socket_impl::do_ledbat(const int acked_bytes, const int delay , const int in_flight) { INVARIANT_CHECK; // the portion of the in-flight bytes that were acked. This is used to make // the gain factor be scaled by the rtt. The formula is applied once per // rtt, or on every ACK scaled by the number of ACKs per rtt TORRENT_ASSERT(in_flight > 0); TORRENT_ASSERT(acked_bytes > 0); const int target_delay = (std::max)(1, m_sm->target_delay()); // true if the upper layer is pushing enough data down the socket to be // limited by the cwnd. If this is not the case, we should not adjust cwnd. const bool cwnd_saturated = (m_bytes_in_flight + acked_bytes + m_mtu > (m_cwnd >> 16)); // all of these are fixed points with 16 bits fraction portion const boost::int64_t window_factor = (boost::int64_t(acked_bytes) * (1 << 16)) / in_flight; const boost::int64_t delay_factor = (boost::int64_t(target_delay - delay) * (1 << 16)) / target_delay; boost::int64_t scaled_gain; if (delay >= target_delay) { if (m_slow_start) { UTP_LOGV("%8p: off_target: %d slow_start -> 0\n" , static_cast(this), target_delay - delay); m_ssthres = (m_cwnd >> 16) / 2; m_slow_start = false; } m_sm->inc_stats_counter(counters::utp_samples_above_target); } else { m_sm->inc_stats_counter(counters::utp_samples_below_target); } boost::int64_t linear_gain = (window_factor * delay_factor) >> 16; linear_gain *= boost::int64_t(m_sm->gain_factor()); // if the user is not saturating the link (i.e. not filling the // congestion window), don't adjust it at all. if (cwnd_saturated) { boost::int64_t exponential_gain = boost::int64_t(acked_bytes) * (1 << 16); if (m_slow_start) { // mimic TCP slow-start by adding the number of acked // bytes to cwnd if (m_ssthres != 0 && ((m_cwnd + exponential_gain) >> 16) > m_ssthres) { // if we would exceed the slow start threshold by growing the cwnd // exponentially, don't do it, and leave slow-start mode. This // make us avoid causing more delay and/or packet loss by being too // aggressive m_slow_start = false; scaled_gain = linear_gain; UTP_LOGV("%8p: cwnd > ssthres (%d) slow_start -> 0\n" , static_cast(this), m_ssthres); } else { scaled_gain = (std::max)(exponential_gain, linear_gain); } } else { scaled_gain = linear_gain; } } else { scaled_gain = 0; } // make sure we don't wrap the cwnd if (scaled_gain >= (std::numeric_limits::max)() - m_cwnd) scaled_gain = (std::numeric_limits::max)() - m_cwnd - 1; UTP_LOGV("%8p: do_ledbat delay:%d off_target: %d window_factor:%f target_factor:%f " "scaled_gain:%f cwnd:%d slow_start:%d\n" , static_cast(this), delay, target_delay - delay, window_factor / float(1 << 16) , delay_factor / float(1 << 16) , scaled_gain / float(1 << 16), int(m_cwnd >> 16) , int(m_slow_start)); // if scaled_gain + m_cwnd <= 0, set m_cwnd to 0 if (-scaled_gain >= m_cwnd) { m_cwnd = 0; } else { m_cwnd += scaled_gain; TORRENT_ASSERT(m_cwnd > 0); } TORRENT_ASSERT(m_cwnd >= 0); int window_size_left = (std::min)(int(m_cwnd >> 16), int(m_adv_wnd)) - in_flight + acked_bytes; if (window_size_left >= m_mtu) { UTP_LOGV("%8p: mtu:%d in_flight:%d adv_wnd:%d cwnd:%d acked_bytes:%d cwnd_full -> 0\n" , static_cast(this), m_mtu, in_flight, int(m_adv_wnd), int(m_cwnd >> 16), acked_bytes); m_cwnd_full = false; } /* if ((m_cwnd >> 16) >= m_adv_wnd) { m_slow_start = false; m_ssthres = (m_cwnd >> 16); UTP_LOGV("%8p: cwnd > advertized wnd (%d) slow_start -> 0\n" , static_cast(this), m_adv_wnd); } */ } void utp_stream::bind(endpoint_type const&, error_code&) { } void utp_stream::cancel_handlers(error_code const& ec) { if (!m_impl) return; m_impl->cancel_handlers(ec, false); } // returns the number of milliseconds a packet would have before // it would time-out if it was sent right now. Takes the RTT estimate // into account int utp_socket_impl::packet_timeout() const { INVARIANT_CHECK; // SYN packets have a bit longer timeout, since we don't // have an RTT estimate yet, make a conservative guess if (m_state == UTP_STATE_NONE) return 3000; // avoid overflow by simply capping based on number of timeouts as well if (m_num_timeouts >= 7) return 60000; int timeout = (std::max)(m_sm->min_timeout(), m_rtt.mean() + m_rtt.avg_deviation() * 2); if (m_num_timeouts > 0) timeout += (1 << (int(m_num_timeouts) - 1)) * 1000; // timeouts over 1 minute are capped if (timeout > 60000) timeout = 60000; return timeout; } void utp_socket_impl::tick(time_point const now) { INVARIANT_CHECK; #if TORRENT_UTP_LOG UTP_LOGV("%8p: tick:%s r: %d (%s) w: %d (%s)\n" , static_cast(this), socket_state_names[m_state], m_read, m_read_handler ? "handler" : "no handler" , m_written, m_write_handler ? "handler" : "no handler"); #endif TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); // if we're already in an error state, we're just waiting for the // client to perform an operation so that we can communicate the // error. No need to do anything else with this socket if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; if (now > m_timeout) { // TIMEOUT! bool ignore_loss = false; if (((m_acked_seq_nr + 1) & ACK_MASK) == m_mtu_seq && ((m_seq_nr - 1) & ACK_MASK) == m_mtu_seq && m_mtu_seq != 0) { // we timed out, and the only outstanding packet // we had was the probe. Assume it was dropped // because it was too big m_mtu_ceiling = m_mtu - 1; update_mtu_limits(); ignore_loss = true; } // the close_reason here is a bit of a hack. When it's set, it indicates // that the upper layer intends to close the socket. However, it has been // observed that the SSL shutdown sometimes can hang in a state where // there's no outstanding data, and it won't receive any more from the // other end. This catches that case and let the socket time out. if (m_outbuf.size() || m_close_reason != 0) { // m_num_timeouts is used to update the connection timeout, and if we // lose this packet because it's an MTU-probe, don't change the timeout if (!ignore_loss) ++m_num_timeouts; m_sm->inc_stats_counter(counters::utp_timeout); } UTP_LOGV("%8p: timeout num-timeouts: %d max-resends: %d confirmed: %d " " acked-seq-num: %d mtu-seq: %d\n" , static_cast(this) , m_num_timeouts , m_sm->num_resends() , m_confirmed , m_acked_seq_nr , m_mtu_seq); // a socket that has not been confirmed to actually have a live remote end // (the IP may have been spoofed) fail on the first timeout. If we had // heard anything from this peer, it would have been confirmed. if (m_num_timeouts > m_sm->num_resends() || (m_num_timeouts > 0 && !m_confirmed)) { // the connection is dead m_error = boost::asio::error::timed_out; set_state(UTP_STATE_ERROR_WAIT); test_socket_state(); return; } if (!ignore_loss) { // set cwnd to 1 MSS if (m_bytes_in_flight == 0 && (m_cwnd >> 16) >= m_mtu) { // this is just a timeout because this direction of // the stream is idle. Don't reset the cwnd, just decay it m_cwnd = std::max(m_cwnd * 2 / 3, boost::int64_t(m_mtu) * (1 << 16)); } else { // we timed out because a packet was not ACKed or because // the cwnd was made smaller than one packet m_cwnd = boost::int64_t(m_mtu) * (1 << 16); } TORRENT_ASSERT(m_cwnd >= 0); m_timeout = now + milliseconds(packet_timeout()); UTP_LOGV("%8p: resetting cwnd:%d\n" , static_cast(this), int(m_cwnd >> 16)); // since we've already timed out now, don't count // loss that we might detect for packets that just // timed out m_loss_seq_nr = m_seq_nr; // when we time out, the cwnd is reset to 1 MSS, which means we // need to ramp it up quickly again. enter slow start mode. This time // we're very likely to have an ssthres set, which will make us leave // slow start before inducing more delay or loss. m_slow_start = true; UTP_LOGV("%8p: slow_start -> 1\n", static_cast(this)); } // we dropped all packets, that includes the mtu probe m_mtu_seq = 0; // we need to go one past m_seq_nr to cover the case // where we just sent a SYN packet and then adjusted for // the uTorrent sequence number reuse for (int i = m_acked_seq_nr & ACK_MASK; i != ((m_seq_nr + 1) & ACK_MASK); i = (i + 1) & ACK_MASK) { packet* p = m_outbuf.at(i); if (!p) continue; if (p->need_resend) continue; p->need_resend = true; TORRENT_ASSERT(m_bytes_in_flight >= p->size - p->header_size); m_bytes_in_flight -= p->size - p->header_size; UTP_LOGV("%8p: Packet %d lost (timeout).\n", static_cast(this), i); } TORRENT_ASSERT(m_bytes_in_flight == 0); // if we have a packet that needs re-sending, resend it packet* p = m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK); if (p) { if (p->num_transmissions >= m_sm->num_resends() || (m_state == UTP_STATE_SYN_SENT && p->num_transmissions >= m_sm->syn_resends()) || (m_state == UTP_STATE_FIN_SENT && p->num_transmissions >= m_sm->fin_resends())) { #if TORRENT_UTP_LOG UTP_LOGV("%8p: %d failed sends in a row. Socket timed out. state:%s\n" , static_cast(this), p->num_transmissions, socket_state_names[m_state]); #endif if (p->size > m_mtu_floor) { // the packet that caused the connection to fail was an mtu probe // (note that the mtu_probe field won't be set at this point because // it's cleared when the packet is re-sent). This suggests that // perhaps our network throws away oversized packets without // fragmenting them. Tell the socket manager to be more conservative // about mtu ceiling in the future m_sm->restrict_mtu(m_mtu); } // the connection is dead m_error = boost::asio::error::timed_out; set_state(UTP_STATE_ERROR_WAIT); test_socket_state(); return; } // don't fast-resend this packet if (m_fast_resend_seq_nr == ((m_acked_seq_nr + 1) & ACK_MASK)) m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; // the packet timed out, resend it resend_packet(p); if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; } else if (m_state < UTP_STATE_FIN_SENT) { send_pkt(); if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; } else if (m_state == UTP_STATE_FIN_SENT) { // the connection is dead m_error = boost::asio::error::eof; set_state(UTP_STATE_ERROR_WAIT); test_socket_state(); return; } } switch (m_state) { case UTP_STATE_NONE: case UTP_STATE_DELETE: return; // case UTP_STATE_SYN_SENT: // // break; } } void utp_socket_impl::check_receive_buffers() const { INVARIANT_CHECK; std::size_t size = 0; for (std::vector::const_iterator i = m_receive_buffer.begin() , end(m_receive_buffer.end()); i != end; ++i) { if (packet const* p = *i) size += p->size - p->header_size; } TORRENT_ASSERT(int(size) == m_receive_buffer_size); } #if TORRENT_USE_INVARIANT_CHECKS void utp_socket_impl::check_invariant() const { for (int i = m_outbuf.cursor(); i != int((m_outbuf.cursor() + m_outbuf.span()) & ACK_MASK); i = (i + 1) & ACK_MASK) { packet* p = m_outbuf.at(i); if (!p) continue; if (m_mtu_seq == i && m_mtu_seq != 0) { TORRENT_ASSERT(p->mtu_probe); } TORRENT_ASSERT(reinterpret_cast(p->buf)->seq_nr == i); } if (m_nagle_packet) { // if this packet is full, it should have been sent TORRENT_ASSERT(m_nagle_packet->size < m_nagle_packet->allocated); } } #endif } libtorrent-rasterbar-1.1.13/src/version.cpp000066400000000000000000000031511351156116000207230ustar00rootroot00000000000000/* Copyright (c) 2015-2018, 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/version.hpp" namespace libtorrent { char const* version() { return LIBTORRENT_VERSION; } } libtorrent-rasterbar-1.1.13/src/web_connection_base.cpp000066400000000000000000000150011351156116000232210ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/web_connection_base.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/parse_url.hpp" #include "libtorrent/peer_info.hpp" using boost::shared_ptr; namespace libtorrent { web_connection_base::web_connection_base( peer_connection_args const& pack , web_seed_t& web) : peer_connection(pack) , m_first_request(true) , m_ssl(false) , m_external_auth(web.auth) , m_extra_headers(web.extra_headers) , m_parser(http_parser::dont_parse_chunks) , m_body_start(0) { TORRENT_ASSERT(&web.peer_info == pack.peerinfo); // when going through a proxy, we don't necessarily have an endpoint here, // since the proxy might be resolving the hostname, not us TORRENT_ASSERT(web.endpoints.empty() || web.endpoints.front() == pack.endp); INVARIANT_CHECK; TORRENT_ASSERT(is_outgoing()); TORRENT_ASSERT(!m_torrent.lock()->is_upload_only()); // we only want left-over bandwidth // TODO: introduce a web-seed default class which has a low download priority std::string protocol; error_code ec; boost::tie(protocol, m_basic_auth, m_host, m_port, m_path) = parse_url_components(web.url, ec); TORRENT_ASSERT(!ec); if (m_port == -1 && protocol == "http") m_port = 80; #ifdef TORRENT_USE_OPENSSL if (protocol == "https") { m_ssl = true; if (m_port == -1) m_port = 443; } #endif if (!m_basic_auth.empty()) m_basic_auth = base64encode(m_basic_auth); m_server_string = "URL seed @ "; m_server_string += m_host; } int web_connection_base::timeout() const { // since this is a web seed, change the timeout // according to the settings. return m_settings.get_int(settings_pack::urlseed_timeout); } void web_connection_base::start() { // avoid calling torrent::set_seed because it calls torrent::check_invariant // which fails because the m_num_connecting count is not consistent until // after we call peer_connection::start m_upload_only = true; peer_connection::start(); // disconnect_if_redundant must be called after start to keep // m_num_connecting consistent disconnect_if_redundant(); } web_connection_base::~web_connection_base() {} void web_connection_base::on_connected() { boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); // this is always a seed incoming_have_all(); // it is always possible to request pieces incoming_unchoke(); m_recv_buffer.reset(t->block_size() + 1024); } void web_connection_base::add_headers(std::string& request , aux::session_settings const& sett, bool using_proxy) const { request += "Host: "; request += m_host; if ((m_first_request || m_settings.get_bool(settings_pack::always_send_user_agent)) && !m_settings.get_bool(settings_pack::anonymous_mode)) { request += "\r\nUser-Agent: "; request += m_settings.get_str(settings_pack::user_agent); } if (!m_external_auth.empty()) { request += "\r\nAuthorization: "; request += m_external_auth; } else if (!m_basic_auth.empty()) { request += "\r\nAuthorization: Basic "; request += m_basic_auth; } if (sett.get_int(settings_pack::proxy_type) == settings_pack::http_pw) { request += "\r\nProxy-Authorization: Basic "; request += base64encode(sett.get_str(settings_pack::proxy_username) + ":" + sett.get_str(settings_pack::proxy_password)); } for (web_seed_entry::headers_t::const_iterator it = m_extra_headers.begin(); it != m_extra_headers.end(); ++it) { request += "\r\n"; request += it->first; request += ": "; request += it->second; } if (using_proxy) { request += "\r\nProxy-Connection: keep-alive"; } if (m_first_request || using_proxy) { request += "\r\nConnection: keep-alive"; } } // -------------------------- // RECEIVE DATA // -------------------------- void web_connection_base::get_specific_peer_info(peer_info& p) const { if (is_interesting()) p.flags |= peer_info::interesting; if (is_choked()) p.flags |= peer_info::choked; if (!is_connecting() && m_server_string.empty()) p.flags |= peer_info::handshake; if (is_connecting()) p.flags |= peer_info::connecting; p.client = m_server_string; } bool web_connection_base::in_handshake() const { return m_server_string.empty(); } void web_connection_base::on_sent(error_code const& error , std::size_t bytes_transferred) { INVARIANT_CHECK; if (error) return; sent_bytes(0, bytes_transferred); } #if TORRENT_USE_INVARIANT_CHECKS void web_connection_base::check_invariant() const { /* TORRENT_ASSERT(m_num_pieces == std::count( m_have_piece.begin() , m_have_piece.end() , true)); */ } #endif } libtorrent-rasterbar-1.1.13/src/web_peer_connection.cpp000066400000000000000000001017601351156116000232520ustar00rootroot00000000000000/* Copyright (c) 2003-2018, 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/config.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #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/parse_url.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/aux_/session_interface.hpp" #include "libtorrent/alert_manager.hpp" // for alert_manageralert_manager #include "libtorrent/aux_/escape_string.hpp" // for escape_path #include "libtorrent/hex.hpp" // for is_hex using boost::shared_ptr; namespace libtorrent { enum { request_size_overhead = 5000 }; struct disk_interface; web_peer_connection::web_peer_connection(peer_connection_args const& pack , web_seed_t& web) : web_connection_base(pack, web) , m_url(web.url) , m_web(&web) , m_received_body(0) , m_chunk_pos(0) , m_partial_chunk_header(0) , m_num_responses(0) { INVARIANT_CHECK; if (!m_settings.get_bool(settings_pack::report_web_seed_downloads)) ignore_stats(true); shared_ptr tor = pack.tor.lock(); TORRENT_ASSERT(tor); // if the web server is known not to support keep-alive. request 4MiB // but we want to have at least piece size to prevent block based requests int const min_size = std::max((web.supports_keepalive ? 1 : 4) * 1024 * 1024, tor->torrent_file().piece_length()); // we prefer downloading large chunks from web seeds, // but still want to be able to split requests int const preferred_size = std::max(min_size, m_settings.get_int(settings_pack::urlseed_max_request_bytes)); prefer_contiguous_blocks(preferred_size / tor->block_size()); boost::shared_ptr t = associated_torrent().lock(); bool const single_file_request = t->torrent_file().num_files() == 1; if (!single_file_request) { // handle incorrect .torrent files which are multi-file // but have web seeds not ending with a slash if (m_path.empty() || m_path[m_path.size()-1] != '/') m_path += '/'; if (m_url.empty() || m_url[m_url.size()-1] != '/') m_url += '/'; } else { // handle .torrent files that don't include the filename in the url if (m_path.empty()) m_path += '/'; if (m_path[m_path.size()-1] == '/') { std::string const& name = t->torrent_file().name(); m_path += escape_string(name.c_str(), name.size()); } if (!m_url.empty() && m_url[m_url.size() - 1] == '/') { std::string tmp = t->torrent_file().files().file_path(0); #ifdef TORRENT_WINDOWS convert_path_to_posix(tmp); #endif tmp = escape_path(tmp.c_str(), tmp.size()); m_url += tmp; } } // we want large blocks as well, so // we can request more bytes at once // this setting will merge adjacent requests // into single larger ones request_large_blocks(true); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "URL", "web_peer_connection %s", m_url.c_str()); #endif } void web_peer_connection::on_connected() { incoming_have_all(); if (m_web->restart_request.piece != -1) { // increase the chances of requesting the block // we have partial data for already, to finish it incoming_suggest(m_web->restart_request.piece); } web_connection_base::on_connected(); } void web_peer_connection::disconnect(error_code const& ec , operation_t op, int error) { if (is_disconnecting()) return; if (op == op_sock_write && ec == boost::system::errc::broken_pipe) { #ifndef TORRENT_DISABLE_LOGGING // a write operation failed with broken-pipe. This typically happens // with HTTP 1.0 servers that close their incoming channel of the TCP // stream whenever they're done reading one full request. Instead of // us bailing out and failing the entire request just because our // write-end was closed, ignore it and keep reading until the read-end // also is closed. peer_log(peer_log_alert::info, "WRITE_DIRECTION", "CLOSED"); #endif // prevent the peer from trying to send anything more m_send_buffer.clear(); m_recv_buffer.free_disk_buffer(); // when the web server closed our write-end of the socket (i.e. its // read-end), if it's an HTTP 1.0 server. we will stop sending more // requests. We'll close the connection once we receive the last bytes, // and our read end is closed as well. incoming_choke(); return; } if (op == op_connect && m_web && !m_web->endpoints.empty()) { // we failed to connect to this IP. remove it so that the next attempt // uses the next IP in the list. m_web->endpoints.erase(m_web->endpoints.begin()); } boost::shared_ptr t = associated_torrent().lock(); if (!m_requests.empty() && !m_file_requests.empty() && !m_piece.empty() && m_web) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "SAVE_RESTART_DATA" , "data: %d req: %d off: %d" , int(m_piece.size()), int(m_requests.front().piece) , int(m_requests.front().start)); #endif m_web->restart_request = m_requests.front(); if (!m_web->restart_piece.empty()) { // we're about to replace a different restart piece // buffer. So it was wasted download if (t) t->add_redundant_bytes(m_web->restart_piece.size() , torrent::piece_closing); } m_web->restart_piece.swap(m_piece); // we have to do this to not count this data as redundant. The // upper layer will call downloading_piece_progress and assume // it's all wasted download. Since we're saving it here, it isn't. m_requests.clear(); } if (m_web && !m_web->supports_keepalive && error == 0) { // if the web server doesn't support keepalive and we were // disconnected as a graceful EOF, reconnect right away if (t) get_io_service().post( boost::bind(&torrent::maybe_connect_web_seeds, t)); } peer_connection::disconnect(ec, op, error); if (t) t->disconnect_web_seed(this); } boost::optional web_peer_connection::downloading_piece_progress() const { if (m_requests.empty()) return boost::optional(); boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); piece_block_progress ret; ret.piece_index = m_requests.front().piece; ret.bytes_downloaded = m_piece.size(); // this is used to make sure that the block_index stays within // bounds. If the entire piece is downloaded, the block_index // would otherwise point to one past the end int correction = m_piece.size() ? -1 : 0; ret.block_index = (m_requests.front().start + m_piece.size() + correction) / t->block_size(); TORRENT_ASSERT(ret.block_index < int(piece_block::invalid.block_index)); TORRENT_ASSERT(ret.piece_index < int(piece_block::invalid.piece_index)); ret.full_block_bytes = t->block_size(); const int last_piece = t->torrent_file().num_pieces() - 1; if (ret.piece_index == last_piece && ret.block_index == t->torrent_file().piece_size(last_piece) / t->block_size()) ret.full_block_bytes = t->torrent_file().piece_size(last_piece) % t->block_size(); return ret; } void web_peer_connection::write_request(peer_request const& r) { INVARIANT_CHECK; boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(t->valid_metadata()); torrent_info const& info = t->torrent_file(); peer_request req = r; std::string request; request.reserve(400); int size = r.length; const int block_size = t->block_size(); const int piece_size = t->torrent_file().piece_length(); peer_request pr; while (size > 0) { int request_offset = r.start + r.length - size; pr.start = request_offset % piece_size; pr.length = (std::min)(block_size, size); pr.piece = r.piece + request_offset / piece_size; m_requests.push_back(pr); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "REQUESTING", "piece: %d start: %d len: %d" , pr.piece, pr.start, pr.length); #endif if (m_web->restart_request == m_requests.front()) { m_piece.swap(m_web->restart_piece); peer_request& front = m_requests.front(); TORRENT_ASSERT(front.length > int(m_piece.size())); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "RESTART_DATA", "data: %d req: (%d, %d) size: %d" , int(m_piece.size()), int(front.piece), int(front.start) , int (front.start + front.length - 1)); #else TORRENT_UNUSED(front); #endif req.start += m_piece.size(); req.length -= m_piece.size(); // just to keep the accounting straight for the upper layer. // it doesn't know we just re-wrote the request incoming_piece_fragment(m_piece.size()); m_web->restart_request.piece = -1; } #if 0 std::cerr << this << " REQ: p: " << pr.piece << " " << pr.start << std::endl; #endif size -= pr.length; } bool const single_file_request = t->torrent_file().num_files() == 1; int const proxy_type = m_settings.get_int(settings_pack::proxy_type); bool const using_proxy = (proxy_type == settings_pack::http || proxy_type == settings_pack::http_pw) && !m_ssl; // the number of pad files that have been "requested". In case we _only_ // request padfiles, we can't rely on handling them in the on_receive() // callback (because we won't receive anything), instead we have to post a // pretend read callback where we can deliver the zeroes for the partfile int num_pad_files = 0; // TODO: 2 do we really need a special case here? wouldn't the multi-file // case handle single file torrents correctly too? if (single_file_request) { file_request_t file_req; file_req.file_index = 0; file_req.start = boost::int64_t(req.piece) * info.piece_length() + req.start; file_req.length = req.length; request += "GET "; // do not encode single file paths, they are // assumed to be encoded in the torrent file request += using_proxy ? m_url : m_path; request += " HTTP/1.1\r\n"; add_headers(request, m_settings, using_proxy); request += "\r\nRange: bytes="; request += to_string(file_req.start).elems; request += "-"; request += to_string(file_req.start + file_req.length - 1).elems; request += "\r\n\r\n"; m_first_request = false; m_file_requests.push_back(file_req); } else { if (!t->need_loaded()) { disconnect(errors::torrent_aborted, op_bittorrent); return; } std::vector files = info.orig_files().map_block(req.piece, req.start , req.length); for (std::vector::iterator i = files.begin(); i != files.end(); ++i) { file_slice const& f = *i; file_request_t file_req; file_req.file_index = f.file_index; file_req.start = f.offset; file_req.length = f.size; if (info.orig_files().pad_file_at(f.file_index)) { m_file_requests.push_back(file_req); ++num_pad_files; continue; } request += "GET "; if (using_proxy) { // m_url is already a properly escaped URL // with the correct slashes. Don't encode it again request += m_url; std::string path = info.orig_files().file_path(f.file_index); #ifdef TORRENT_WINDOWS convert_path_to_posix(path); #endif request += escape_path(path.c_str(), path.length()); } else { // m_path is already a properly escaped URL // with the correct slashes. Don't encode it again request += m_path; std::string path = info.orig_files().file_path(f.file_index); #ifdef TORRENT_WINDOWS convert_path_to_posix(path); #endif request += escape_path(path.c_str(), path.length()); } request += " HTTP/1.1\r\n"; add_headers(request, m_settings, using_proxy); request += "\r\nRange: bytes="; request += to_string(f.offset).elems; request += "-"; request += to_string(f.offset + f.size - 1).elems; request += "\r\n\r\n"; m_first_request = false; #if 0 std::cerr << this << " SEND-REQUEST: f: " << f.file_index << " s: " << f.offset << " e: " << (f.offset + f.size - 1) << std::endl; #endif TORRENT_ASSERT(f.file_index >= 0); m_file_requests.push_back(file_req); } } if (num_pad_files == int(m_file_requests.size())) { get_io_service().post(boost::bind( &web_peer_connection::on_receive_padfile, boost::static_pointer_cast(self()))); return; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::outgoing_message, "REQUEST", "%s", request.c_str()); #endif send_buffer(request.c_str(), request.size(), message_type_request); } namespace { std::string get_peer_name(http_parser const& p, std::string const& host) { std::string ret = "URL seed @ "; ret += host; std::string const& server_version = p.header("server"); if (!server_version.empty()) { ret += " ("; ret += server_version; ret += ")"; } return ret; } boost::tuple get_range( http_parser const& parser, error_code& ec) { boost::int64_t range_start; boost::int64_t range_end; if (parser.status_code() == 206) { boost::tie(range_start, range_end) = parser.content_range(); if (range_start < 0 || range_end < range_start) { ec = errors::invalid_range; range_start = 0; range_end = 0; } else { // the http range is inclusive range_end++; } } else { range_start = 0; range_end = parser.content_length(); if (range_end < 0) { range_end = 0; ec = errors::no_content_length; } } return boost::tuple(range_start, range_end); } } // -------------------------- // RECEIVE DATA // -------------------------- bool web_peer_connection::received_invalid_data(int index, bool single_peer) { if (!single_peer) return peer_connection::received_invalid_data(index, single_peer); // when a web seed fails a hash check, do the following: // 1. if the whole piece only overlaps a single file, mark that file as not // have for this peer // 2. if the piece overlaps more than one file, mark the piece as not have // for this peer // 3. if it's a single file torrent, just ban it right away // this handles the case where web seeds may have some files updated but not other boost::shared_ptr t = associated_torrent().lock(); file_storage const& fs = t->torrent_file().files(); // single file torrent if (fs.num_files() == 1) return peer_connection::received_invalid_data(index, single_peer); std::vector files = fs.map_block(index, 0, fs.piece_size(index)); if (files.size() == 1) { // assume the web seed has a different copy of this specific file // than what we expect, and pretend not to have it. int fi = files[0].file_index; int first_piece = fs.file_offset(fi) / fs.piece_length(); // one past last piece int end_piece = int((fs.file_offset(fi) + fs.file_size(fi) + 1) / fs.piece_length()); for (int i = first_piece; i < end_piece; ++i) incoming_dont_have(i); } else { incoming_dont_have(index); } peer_connection::received_invalid_data(index, single_peer); // if we don't think we have any of the files, allow banning the web seed if (num_have_pieces() == 0) return true; // don't disconnect, we won't request anything from this file again return false; } void web_peer_connection::on_receive_padfile() { handle_padfile(); } void web_peer_connection::handle_error(int bytes_left) { boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); // TODO: 2 just make this peer not have the pieces // associated with the file we just requested. Only // when it doesn't have any of the file do the following int retry_time = atoi(m_parser.header("retry-after").c_str()); if (retry_time <= 0) retry_time = m_settings.get_int(settings_pack::urlseed_wait_retry); // temporarily unavailable, retry later t->retry_web_seed(this, retry_time); std::string error_msg = to_string(m_parser.status_code()).elems + (" " + m_parser.message()); if (t->alerts().should_post()) { t->alerts().emplace_alert(t->get_handle(), m_url , error_msg); } received_bytes(0, bytes_left); disconnect(error_code(m_parser.status_code(), http_category()), op_bittorrent, 1); return; } void web_peer_connection::handle_redirect(int bytes_left) { // this means we got a redirection request // look for the location header std::string location = m_parser.header("location"); received_bytes(0, bytes_left); boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); if (location.empty()) { // we should not try this server again. t->remove_web_seed(this, errors::missing_location, op_bittorrent, 2); m_web = NULL; TORRENT_ASSERT(is_disconnecting()); return; } bool const single_file_request = !m_path.empty() && m_path[m_path.size() - 1] != '/'; // add the redirected url and remove the current one if (!single_file_request) { TORRENT_ASSERT(!m_file_requests.empty()); int const file_index = m_file_requests.front().file_index; if (!t->need_loaded()) { disconnect(errors::torrent_aborted, op_bittorrent); return; } // TODO: 2 create a mapping of file-index to redirection URLs. Use that to form // URLs instead. Support to reconnect to a new server without destructing this // peer_connection torrent_info const& info = t->torrent_file(); std::string path = info.orig_files().file_path(file_index); #ifdef TORRENT_WINDOWS convert_path_to_posix(path); #endif path = escape_path(path.c_str(), path.length()); size_t i = location.rfind(path); if (i == std::string::npos) { t->remove_web_seed(this, errors::invalid_redirection, op_bittorrent, 2); m_web = NULL; TORRENT_ASSERT(is_disconnecting()); return; } location.resize(i); } else { location = resolve_redirect_location(m_url, location); } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "LOCATION", "%s", location.c_str()); #endif t->add_web_seed(location, web_seed_entry::url_seed, m_external_auth, m_extra_headers); t->remove_web_seed(this, errors::redirecting, op_bittorrent, 2); m_web = NULL; TORRENT_ASSERT(is_disconnecting()); return; } void web_peer_connection::on_receive(error_code const& error , std::size_t bytes_transferred) { INVARIANT_CHECK; if (error) { received_bytes(0, bytes_transferred); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ERROR" , "web_peer_connection error: %s", error.message().c_str()); #endif return; } boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); // in case the first file on this series of requests is a padfile // we need to handle it right now buffer::const_interval recv_buffer = m_recv_buffer.get(); handle_padfile(); if (associated_torrent().expired()) return; for (;;) { int payload; int protocol; bool header_finished = m_parser.header_finished(); if (!header_finished) { bool failed = false; boost::tie(payload, protocol) = m_parser.incoming(recv_buffer, failed); received_bytes(0, protocol); TORRENT_ASSERT(int(recv_buffer.left()) >= protocol); if (failed) { received_bytes(0, recv_buffer.left()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "RECEIVE_BYTES" , "%s", std::string(recv_buffer.begin, recv_buffer.end).c_str()); #endif disconnect(errors::http_parse_error, op_bittorrent, 2); return; } TORRENT_ASSERT(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); TORRENT_ASSERT(recv_buffer.left() <= m_recv_buffer.packet_size()); // this means the entire status line hasn't been received yet if (m_parser.status_code() == -1) { TORRENT_ASSERT(payload == 0); break; } if (!m_parser.header_finished()) { TORRENT_ASSERT(payload == 0); break; } m_body_start = m_parser.body_start(); m_received_body = 0; } // we just completed reading the header if (!header_finished) { ++m_num_responses; if (m_parser.connection_close()) { incoming_choke(); if (m_num_responses == 1) m_web->supports_keepalive = false; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "STATUS" , "%d %s", m_parser.status_code(), m_parser.message().c_str()); std::multimap const& headers = m_parser.headers(); for (std::multimap::const_iterator i = headers.begin() , end(headers.end()); i != end; ++i) peer_log(peer_log_alert::info, "STATUS", " %s: %s", i->first.c_str(), i->second.c_str()); #endif // if the status code is not one of the accepted ones, abort if (!is_ok_status(m_parser.status_code())) { handle_error(recv_buffer.left()); return; } if (is_redirect(m_parser.status_code())) { handle_redirect(recv_buffer.left()); return; } m_server_string = get_peer_name(m_parser, m_host); recv_buffer.begin += m_body_start; m_body_start = m_parser.body_start(); m_received_body = 0; } // we only received the header, no data if (recv_buffer.left() == 0) break; // =================================== // ======= RESPONSE BYTE RANGE ======= // =================================== // despite the HTTP range being inclusive, range_start and range_end are // exclusive to fit better into C++. i.e. range_end points one byte past // the end of the payload boost::int64_t range_start; boost::int64_t range_end; error_code ec; boost::tie(range_start, range_end) = get_range(m_parser, ec); if (ec) { received_bytes(0, recv_buffer.left()); // we should not try this server again. t->remove_web_seed(this, ec, op_bittorrent, 2); m_web = NULL; TORRENT_ASSERT(is_disconnecting()); return; } TORRENT_ASSERT(!m_file_requests.empty()); file_request_t const& file_req = m_file_requests.front(); if (range_start != file_req.start || range_end != file_req.start + file_req.length) { // the byte range in the http response is different what we expected received_bytes(0, recv_buffer.left()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "INVALID HTTP RESPONSE" , "in=(%d, %" PRId64 "-%" PRId64 ") expected=(%d, %" PRId64 "-%" PRId64 ") ]" , file_req.file_index, range_start, range_end , file_req.file_index, file_req.start, file_req.start + file_req.length - 1); #endif disconnect(errors::invalid_range, op_bittorrent, 2); return; } if (m_parser.chunked_encoding()) { // ========================= // === CHUNKED ENCODING === // ========================= while (m_chunk_pos >= 0 && recv_buffer.left() > 0) { // first deliver any payload we have in the buffer so far, ahead of // the next chunk header. if (m_chunk_pos > 0) { int const copy_size = (std::min)(m_chunk_pos, recv_buffer.left()); TORRENT_ASSERT(copy_size > 0); if (m_received_body + copy_size > file_req.length) { // the byte range in the http response is different what we expected received_bytes(0, recv_buffer.left()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "INVALID HTTP RESPONSE" , "received body: %d request size: %d" , m_received_body, file_req.length); #endif disconnect(errors::invalid_range, op_bittorrent, 2); return; } incoming_payload(recv_buffer.begin, copy_size); recv_buffer.begin += copy_size; m_chunk_pos -= copy_size; if (recv_buffer.left() == 0) goto done; } TORRENT_ASSERT(m_chunk_pos == 0); int header_size = 0; boost::int64_t chunk_size = 0; buffer::const_interval chunk_start = recv_buffer; chunk_start.begin += m_chunk_pos; TORRENT_ASSERT(chunk_start.begin[0] == '\r' || detail::is_hex(chunk_start.begin, 1)); bool ret = m_parser.parse_chunk_header(chunk_start, &chunk_size, &header_size); if (!ret) { received_bytes(0, chunk_start.left() - m_partial_chunk_header); m_partial_chunk_header = chunk_start.left(); goto done; } #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CHUNKED_ENCODING" , "parsed chunk: %" PRId64 " header_size: %d" , chunk_size, header_size); #endif received_bytes(0, header_size - m_partial_chunk_header); m_partial_chunk_header = 0; TORRENT_ASSERT(chunk_size != 0 || chunk_start.left() <= header_size || chunk_start.begin[header_size] == 'H'); TORRENT_ASSERT(m_body_start + m_chunk_pos < INT_MAX); m_chunk_pos += chunk_size; recv_buffer.begin += header_size; // a chunk size of zero means the request is complete. Make sure the // number of payload bytes we've received matches the number we // requested. If that's not the case, we got an invalid response. if (chunk_size == 0) { TORRENT_ASSERT_VAL(m_chunk_pos == 0, m_chunk_pos); #ifdef TORRENT_DEBUG chunk_start = recv_buffer; chunk_start.begin += m_chunk_pos; TORRENT_ASSERT(chunk_start.left() == 0 || chunk_start.begin[0] == 'H'); #endif m_chunk_pos = -1; TORRENT_ASSERT(m_received_body <= file_req.length); if (m_received_body != file_req.length) { // the byte range in the http response is different what we expected received_bytes(0, recv_buffer.left()); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming, "INVALID HTTP RESPONSE" , "received body: %d request size: %d" , m_received_body, file_req.length); #endif disconnect(errors::invalid_range, op_bittorrent, 2); return; } // we just completed an HTTP file request. pop it from m_file_requests m_file_requests.pop_front(); m_parser.reset(); m_body_start = 0; m_received_body = 0; m_chunk_pos = 0; m_partial_chunk_header = 0; // in between each file request, there may be an implicit // pad-file request handle_padfile(); break; } // if all of the receive buffer was just consumed as chunk // header, we're done if (recv_buffer.left() == 0) goto done; } } else { // this is the simple case, where we don't have chunked encoding TORRENT_ASSERT(m_received_body <= file_req.length); int const copy_size = (std::min)(file_req.length - m_received_body , recv_buffer.left()); incoming_payload(recv_buffer.begin, copy_size); recv_buffer.begin += copy_size; TORRENT_ASSERT(m_received_body <= file_req.length); if (m_received_body == file_req.length) { // we just completed an HTTP file request. pop it from m_file_requests m_file_requests.pop_front(); m_parser.reset(); m_body_start = 0; m_received_body = 0; m_chunk_pos = 0; m_partial_chunk_header = 0; // in between each file request, there may be an implicit // pad-file request handle_padfile(); } } if (recv_buffer.left() == 0) break; } done: // now, remove all the bytes we've processed from the receive buffer m_recv_buffer.cut(recv_buffer.begin - m_recv_buffer.get().begin , t->block_size() + request_size_overhead); } void web_peer_connection::incoming_payload(char const* buf, int len) { received_bytes(len, 0); m_received_body += len; if (is_disconnecting()) return; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "INCOMING_PAYLOAD", "%d bytes", len); #endif // deliver all complete bittorrent requests to the bittorrent engine while (len > 0) { if (m_requests.empty()) return; TORRENT_ASSERT(!m_requests.empty()); peer_request const& front_request = m_requests.front(); int const piece_size = int(m_piece.size()); int const copy_size = (std::min)(front_request.length - piece_size, len); // m_piece may not hold more than the response to the next BT request TORRENT_ASSERT(front_request.length > piece_size); // copy_size is the number of bytes we need to add to the end of m_piece // to not exceed the size of the next bittorrent request to be delivered. // m_piece can only hold the response for a single BT request at a time m_piece.resize(piece_size + copy_size); std::memcpy(&m_piece[0] + piece_size, buf, copy_size); len -= copy_size; buf += copy_size; // keep peer stats up-to-date incoming_piece_fragment(copy_size); TORRENT_ASSERT(front_request.length >= piece_size); if (int(m_piece.size()) == front_request.length) { boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "POP_REQUEST" , "piece: %d start: %d len: %d" , front_request.piece, front_request.start, front_request.length); #endif // Make a copy of the request and pop it off the queue before calling // incoming_piece because that may lead to a call to disconnect() // which will clear the request queue and invalidate any references // to the request peer_request const front_request_copy = front_request; m_requests.pop_front(); incoming_piece(front_request_copy, &m_piece[0]); m_piece.clear(); } } } void web_peer_connection::incoming_zeroes(int len) { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "INCOMING_ZEROES", "%d bytes", len); #endif // deliver all complete bittorrent requests to the bittorrent engine while (len > 0) { TORRENT_ASSERT(!m_requests.empty()); peer_request const& front_request = m_requests.front(); int const piece_size = int(m_piece.size()); int const copy_size = (std::min)(front_request.length - piece_size, len); // m_piece may not hold more than the response to the next BT request TORRENT_ASSERT(front_request.length > piece_size); // copy_size is the number of bytes we need to add to the end of m_piece // to not exceed the size of the next bittorrent request to be delivered. // m_piece can only hold the response for a single BT request at a time m_piece.resize(piece_size + copy_size, 0); len -= copy_size; // keep peer stats up-to-date incoming_piece_fragment(copy_size); maybe_harvest_piece(); } } void web_peer_connection::maybe_harvest_piece() { peer_request const& front_request = m_requests.front(); TORRENT_ASSERT(front_request.length >= int(m_piece.size())); if (int(m_piece.size()) != front_request.length) return; boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::incoming_message, "POP_REQUEST" , "piece: %d start: %d len: %d" , front_request.piece, front_request.start, front_request.length); #endif m_requests.pop_front(); incoming_piece(front_request, &m_piece[0]); m_piece.clear(); } void web_peer_connection::get_specific_peer_info(peer_info& p) const { web_connection_base::get_specific_peer_info(p); p.flags |= peer_info::local_connection; p.connection_type = peer_info::web_seed; } void web_peer_connection::handle_padfile() { if (m_file_requests.empty()) return; if (m_requests.empty()) return; boost::shared_ptr t = associated_torrent().lock(); TORRENT_ASSERT(t); torrent_info const& info = t->torrent_file(); while (!m_file_requests.empty() && info.orig_files().pad_file_at(m_file_requests.front().file_index)) { // the next file is a pad file. We didn't actually send // a request for this since it most likely doesn't exist on // the web server anyway. Just pretend that we received a // bunch of zeroes here and pop it again boost::int64_t file_size = m_file_requests.front().length; // in theory the pad file can span multiple bocks, hence the loop while (file_size > 0) { peer_request const front_request = m_requests.front(); TORRENT_ASSERT(m_piece.size() < front_request.length); int pad_size = int((std::min)(file_size , boost::int64_t(front_request.length - m_piece.size()))); TORRENT_ASSERT(pad_size > 0); file_size -= pad_size; incoming_zeroes(pad_size); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "HANDLE_PADFILE" , "file: %d start: %" PRId64 " len: %d" , m_file_requests.front().file_index , m_file_requests.front().start , m_file_requests.front().length); #endif } m_file_requests.pop_front(); } } } // libtorrent namespace libtorrent-rasterbar-1.1.13/src/xml_parse.cpp000066400000000000000000000125701351156116000212350ustar00rootroot00000000000000/* Copyright (c) 2007-2018, 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/xml_parse.hpp" #include "libtorrent/string_util.hpp" namespace libtorrent { TORRENT_EXTRA_EXPORT void xml_parse(char const* p, char const* end , boost::function callback) { for (;p != end; ++p) { char const* start = p; int token; // look for tag start for (; p != end && *p != '<'; ++p); if (p != start) { token = xml_string; const int name_len = p - start; callback(token, start, name_len, NULL, 0); } if (p == end) break; // skip '<' ++p; if (p != end && p+8 < end && string_begins_no_case("![CDATA[", p)) { // CDATA. match '![CDATA[' p += 8; start = p; while (p != end && !string_begins_no_case("]]>", p-2)) ++p; // parse error if (p == end) { token = xml_parse_error; start = "unexpected end of file"; callback(token, start, strlen(start), NULL, 0); break; } token = xml_string; const int name_len = p - start - 2; callback(token, start, name_len, NULL, 0); continue; } // parse the name of the tag. for (start = p; p != end && *p != '>' && !is_space(*p); ++p); char const* tag_name_end = p; // skip the attributes for now for (; p != end && *p != '>'; ++p); // parse error if (p == end) { token = xml_parse_error; start = "unexpected end of file"; callback(token, start, strlen(start), NULL, 0); break; } TORRENT_ASSERT(*p == '>'); char const* tag_end = p; if (*start == '/') { ++start; token = xml_end_tag; const int name_len = tag_name_end - start; callback(token, start, name_len, NULL, 0); } else if (*(p-1) == '/') { token = xml_empty_tag; const int name_len = (std::min)(tag_name_end - start, p - start - 1); callback(token, start, name_len, NULL, 0); tag_end = p - 1; } else if (*start == '?' && *(p-1) == '?') { ++start; token = xml_declaration_tag; const int name_len = (std::min)(tag_name_end - start, p - start - 1); callback(token, start, name_len, NULL, 0); tag_end = p - 1; } else if (start + 5 < p && std::memcmp(start, "!--", 3) == 0 && std::memcmp(p-2, "--", 2) == 0) { start += 3; token = xml_comment; const int name_len = tag_name_end - start - 2; callback(token, start, name_len, NULL, 0); continue; } else { token = xml_start_tag; const int name_len = tag_name_end - start; callback(token, start, name_len, NULL, 0); } // parse attributes for (char const* i = tag_name_end; i < tag_end; ++i) { char const* val_start = NULL; // find start of attribute name while (i != tag_end && is_space(*i)) ++i; if (i == tag_end) break; start = i; // find end of attribute name while (i != tag_end && *i != '=' && !is_space(*i)) ++i; const int name_len = i - start; // look for equality sign for (; i != tag_end && *i != '='; ++i); // no equality sign found. Report this as xml_tag_content // instead of a series of key value pairs if (i == tag_end) { token = xml_tag_content; callback(token, start, i - start, NULL, 0); break; } ++i; while (i != tag_end && is_space(*i)) ++i; // check for parse error (values must be quoted) if (i == tag_end || (*i != '\'' && *i != '\"')) { token = xml_parse_error; start = "unquoted attribute value"; callback(token, start, strlen(start), NULL, 0); break; } char quote = *i; ++i; val_start = i; for (; i != tag_end && *i != quote; ++i); // parse error (missing end quote) if (i == tag_end) { token = xml_parse_error; start = "missing end quote on attribute"; callback(token, start, strlen(start), NULL, 0); break; } const int val_len = i - val_start; token = xml_attribute; callback(token, start, name_len, val_start, val_len); } } } } libtorrent-rasterbar-1.1.13/test/000077500000000000000000000000001351156116000167225ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/test/CMakeLists.txt000066400000000000000000000027441351156116000214710ustar00rootroot00000000000000 file(GLOB tests "${CMAKE_CURRENT_SOURCE_DIR}/test_*.cpp") list(REMOVE_ITEM tests "${CMAKE_CURRENT_SOURCE_DIR}/test_natpmp.cpp") # doesn't build at time of writing list(REMOVE_ITEM tests "${CMAKE_CURRENT_SOURCE_DIR}/test_utils.cpp") # helper file, not a test add_library(test_common STATIC main.cpp test.cpp setup_transfer.cpp dht_server.cpp udp_tracker.cpp peer_server.cpp web_seed_suite.cpp swarm_suite.cpp test_utils.cpp make_torrent.cpp settings.cpp ) target_link_libraries(test_common PUBLIC torrent-rasterbar) foreach(TARGET_SRC ${tests}) get_filename_component(TARGET ${TARGET_SRC} NAME_WE) add_executable(${TARGET} ${TARGET_SRC}) target_link_libraries(${TARGET} test_common) add_test(${TARGET} ${TARGET}) endforeach() add_executable(bdecode_benchmark bdecode_benchmark.cpp) target_link_libraries(bdecode_benchmark torrent-rasterbar) file(GLOB GZIP_ASSETS "${CMAKE_CURRENT_SOURCE_DIR}/*.gz") file(COPY ${GZIP_ASSETS} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") file(GLOB PYTHON_ASSETS "${CMAKE_CURRENT_SOURCE_DIR}/*.py") file(COPY ${PYTHON_ASSETS} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") file(GLOB XML_ASSETS "${CMAKE_CURRENT_SOURCE_DIR}/*.xml") file(COPY ${XML_ASSETS} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") file(COPY "utf8_test.txt" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") file(COPY "mutable_test_torrents" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") file(COPY "test_torrents" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") file(COPY "ssl" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") libtorrent-rasterbar-1.1.13/test/Jamfile000066400000000000000000000124431351156116000202200ustar00rootroot00000000000000import testing ; import feature : feature ; use-project /torrent : .. ; exe test_natpmp : test_natpmp.cpp /torrent//torrent : multi on full ; exe enum_if : enum_if.cpp /torrent//torrent : multi on full ; exe bdecode_benchmark : bdecode_benchmark.cpp /torrent//torrent : release ; explicit test_natpmp ; explicit enum_if ; explicit bdecode_benchmark ; lib libtorrent_test : # sources main.cpp test.cpp setup_transfer.cpp dht_server.cpp udp_tracker.cpp peer_server.cpp bittorrent_peer.cpp print_alerts.cpp web_seed_suite.cpp swarm_suite.cpp test_utils.cpp settings.cpp make_torrent.cpp : # requirements # this is used to determine whether # symbols are exported or imported shared:TORRENT_BUILDING_TEST_SHARED shared:ED25519_BUILD_DLL ../ed25519/src windows:advapi32 /torrent//torrent on darwin:-Wno-unused-command-line-argument : # default build shared : # user-requirements shared:TORRENT_LINK_TEST_SHARED . ; explicit libtorrent_test ; lib advapi32 : : Advapi32 ; project : requirements on libtorrent_test /torrent//torrent darwin:-Wno-unused-command-line-argument : default-build multi full shared on on on ; feature launcher : none valgrind : composite ; feature.compose valgrind : "valgrind --tool=memcheck -v --num-callers=20 --read-var-info=yes --track-origins=yes --error-exitcode=222 --suppressions=valgrind_suppressions.txt" on ; test-suite libtorrent : [ run test_primitives.cpp test_create_torrent.cpp test_packet_buffer.cpp test_timestamp_history.cpp test_sha1_hash.cpp test_bloom_filter.cpp test_identify_client.cpp test_merkle.cpp test_resolve_links.cpp test_crc32.cpp test_heterogeneous_queue.cpp test_ip_voter.cpp test_sliding_average.cpp test_socket_io.cpp # test_random.cpp test_utf8.cpp test_bitfield.cpp test_part_file.cpp test_peer_list.cpp test_torrent_info.cpp test_time.cpp test_file_storage.cpp test_peer_priority.cpp test_threads.cpp test_tailqueue.cpp test_bandwidth_limiter.cpp test_buffer.cpp test_piece_picker.cpp test_bencoding.cpp test_bdecode.cpp test_http_parser.cpp test_string.cpp test_xml.cpp test_ip_filter.cpp test_hasher.cpp test_dht_storage.cpp test_dht.cpp test_block_cache.cpp test_peer_classes.cpp test_settings_pack.cpp test_fence.cpp test_dos_blocker.cpp test_stat_cache.cpp test_enum_net.cpp test_linked_list.cpp test_stack_allocator.cpp test_file_progress.cpp ] [ run test_gzip.cpp ] [ run test_receive_buffer.cpp ] [ run test_alert_manager.cpp ] [ run test_direct_dht.cpp ] [ run test_magnet.cpp ] [ run test_storage.cpp ] [ run test_session.cpp ] [ run test_read_piece.cpp ] [ run test_remove_torrent.cpp ] [ run test_file.cpp ] [ run test_fast_extension.cpp ] [ run test_privacy.cpp ] [ run test_recheck.cpp ] [ run test_resume.cpp ] [ run test_ssl.cpp : : : openssl:/torrent//ssl openssl:/torrent//crypto ] [ run test_tracker.cpp ] [ run test_checking.cpp ] [ run test_url_seed.cpp ] [ run test_web_seed.cpp ] [ run test_web_seed_redirect.cpp ] [ run test_web_seed_socks4.cpp ] [ run test_web_seed_socks5.cpp ] [ run test_web_seed_socks5_no_peers.cpp ] [ run test_web_seed_socks5_pw.cpp ] [ run test_web_seed_http.cpp ] [ run test_web_seed_http_pw.cpp ] [ run test_web_seed_chunked.cpp ] [ run test_web_seed_ban.cpp ] [ run test_pe_crypto.cpp ] [ run test_ed25519.cpp ] [ run test_remap_files.cpp ] [ run test_utp.cpp ] [ run test_auto_unchoke.cpp ] [ run test_http_connection.cpp : : : openssl:/torrent//ssl openssl:/torrent//crypto ] [ run test_torrent.cpp ] [ run test_transfer.cpp ] [ run test_time_critical.cpp ] [ run test_pex.cpp ] [ run test_priority.cpp ] # turn these tests into simulations [ run test_upnp.cpp ] [ run test_lsd.cpp ] ; # these are the tests run on appveyor, while the flapping ones are being # transitioned into simulations alias win-tests : test_primitives test_pe_crypto test_remap_files test_auto_unchoke test_torrent test_transfer test_time_critical test_pex test_priority test_storage test_session test_read_piece test_file test_fast_extension test_recheck test_resume test_tracker test_checking test_gzip ; # the openssl test hangs on the travis osx machine. difficult to debug alias osx-tests : test_primitives test_alert_manager test_direct_dht test_magnet test_storage test_session test_read_piece test_file test_fast_extension test_privacy test_recheck test_resume # test_ssl test_tracker test_checking test_url_seed test_web_seed test_web_seed_redirect test_web_seed_socks4 test_web_seed_socks5 test_web_seed_socks5_pw test_web_seed_http test_web_seed_http_pw test_web_seed_chunked test_web_seed_ban test_pe_crypto test_remap_files test_utp test_auto_unchoke test_http_connection test_torrent test_transfer test_time_critical test_pex test_priority test_gzip ; explicit win-tests ; libtorrent-rasterbar-1.1.13/test/Makefile.am000066400000000000000000000203001351156116000207510ustar00rootroot00000000000000AUTOMAKE_OPTIONS = subdir-objects benchmark_programs = \ bdecode_benchmark test_programs = \ test_primitives \ test_recheck \ test_stat_cache \ test_file \ test_privacy \ test_priority \ test_remove_torrent \ test_auto_unchoke \ test_checking \ test_fast_extension \ test_http_connection \ test_lsd \ test_pe_crypto \ test_pex \ test_read_piece \ test_receive_buffer \ test_resume \ test_ssl \ test_stack_allocator \ test_storage \ test_time_critical \ test_torrent \ test_tracker \ test_transfer \ test_create_torrent \ enum_if \ test_utp \ test_session \ test_web_seed \ test_web_seed_ban \ test_web_seed_chunked \ test_web_seed_http \ test_web_seed_http_pw \ test_web_seed_redirect \ test_web_seed_socks4 \ test_web_seed_socks5 \ test_web_seed_socks5_no_peers \ test_web_seed_socks5_pw \ test_url_seed \ test_remap_files \ test_enum_net \ test_file_progress \ test_linked_list \ test_direct_dht \ test_ed25519 if ENABLE_TESTS check_PROGRAMS = $(test_programs) $(benchmark_programs) noinst_LTLIBRARIES = libtest.la endif TESTS = $(test_programs) EXTRA_DIST = Jamfile \ test_torrents/backslash_path.torrent \ test_torrents/base.torrent \ test_torrents/creation_date.torrent \ test_torrents/duplicate_files.torrent \ test_torrents/duplicate_web_seeds.torrent \ test_torrents/empty_httpseed.torrent \ test_torrents/empty_path.torrent \ test_torrents/empty_path_multi.torrent \ test_torrents/hidden_parent_path.torrent \ test_torrents/httpseed.torrent \ test_torrents/invalid_file_size.torrent \ test_torrents/invalid_info.torrent \ test_torrents/invalid_merkle.torrent \ test_torrents/invalid_name.torrent \ test_torrents/invalid_name2.torrent \ test_torrents/invalid_name3.torrent \ test_torrents/invalid_path_list.torrent \ test_torrents/invalid_piece_len.torrent \ test_torrents/invalid_pieces.torrent \ test_torrents/invalid_root_hash.torrent \ test_torrents/invalid_root_hash2.torrent \ test_torrents/invalid_symlink.torrent \ test_torrents/long_name.torrent \ test_torrents/missing_path_list.torrent \ test_torrents/missing_piece_len.torrent \ test_torrents/negative_file_size.torrent \ test_torrents/negative_piece_len.torrent \ test_torrents/negative_size.torrent \ test_torrents/no_creation_date.torrent \ test_torrents/no_name.torrent \ test_torrents/pad_file.torrent \ test_torrents/pad_file_no_path.torrent \ test_torrents/parent_path.torrent \ test_torrents/root_hash.torrent \ test_torrents/sample.torrent \ test_torrents/single_multi_file.torrent \ test_torrents/slash_path.torrent \ test_torrents/slash_path2.torrent \ test_torrents/slash_path3.torrent \ test_torrents/string.torrent \ test_torrents/symlink1.torrent \ test_torrents/symlink_zero_size.torrent \ test_torrents/unaligned_pieces.torrent \ test_torrents/unordered.torrent \ test_torrents/url_list.torrent \ test_torrents/url_list2.torrent \ test_torrents/url_list3.torrent \ test_torrents/url_seed.torrent \ test_torrents/url_seed_multi.torrent \ test_torrents/url_seed_multi_space.torrent \ test_torrents/url_seed_multi_space_nolist.torrent \ test_torrents/whitespace_url.torrent \ test_torrents/invalid_filename.torrent \ test_torrents/invalid_filename2.torrent \ mutable_test_torrents/test1.torrent \ mutable_test_torrents/test1_pad_files.torrent \ mutable_test_torrents/test1_single.torrent\ mutable_test_torrents/test1_single_padded.torrent \ mutable_test_torrents/test2.torrent \ mutable_test_torrents/test2_pad_files.torrent \ mutable_test_torrents/test3.torrent \ mutable_test_torrents/test3_pad_files.torrent \ zeroes.gz \ corrupt.gz \ utf8_test.txt \ web_server.py \ socks.py \ http.py EXTRA_PROGRAMS = $(test_programs) $(benchmark_programs) noinst_HEADERS = test.hpp setup_transfer.hpp dht_server.hpp \ peer_server.hpp udp_tracker.hpp web_seed_suite.hpp swarm_suite.hpp \ test_utils.hpp settings.hpp make_torrent.hpp bittorrent_peer.hpp \ print_alerts.hpp libtest_la_SOURCES = main.cpp \ test.cpp \ setup_transfer.cpp \ dht_server.cpp \ udp_tracker.cpp \ peer_server.cpp \ bittorrent_peer.cpp \ make_torrent.cpp \ web_seed_suite.cpp \ swarm_suite.cpp \ test_utils.cpp \ settings.cpp \ print_alerts.cpp test_primitives_SOURCES = \ test_primitives.cpp \ test_packet_buffer.cpp \ test_timestamp_history.cpp \ test_sha1_hash.cpp \ test_bloom_filter.cpp \ test_identify_client.cpp \ test_merkle.cpp \ test_alert_manager.cpp \ test_resolve_links.cpp \ test_crc32.cpp \ test_heterogeneous_queue.cpp \ test_ip_voter.cpp \ test_sliding_average.cpp \ test_socket_io.cpp \ test_random.cpp \ test_utf8.cpp \ test_gzip.cpp \ test_bitfield.cpp \ test_part_file.cpp \ test_peer_list.cpp \ test_torrent_info.cpp \ test_time.cpp \ test_file_storage.cpp \ test_peer_priority.cpp \ test_threads.cpp \ test_tailqueue.cpp \ test_bandwidth_limiter.cpp \ test_buffer.cpp \ test_piece_picker.cpp \ test_bencoding.cpp \ test_bdecode.cpp \ test_http_parser.cpp \ test_string.cpp \ test_magnet.cpp \ test_xml.cpp \ test_ip_filter.cpp \ test_hasher.cpp \ test_dht_storage.cpp \ test_dht.cpp \ test_block_cache.cpp \ test_peer_classes.cpp \ test_settings_pack.cpp \ test_fence.cpp \ test_dos_blocker.cpp \ test_upnp.cpp bdecode_benchmark_SOURCES = bdecode_benchmark.cpp test_recheck_SOURCES = test_recheck.cpp test_stat_cache_SOURCES = test_stat_cache.cpp test_file_SOURCES = test_file.cpp test_privacy_SOURCES = test_privacy.cpp test_priority_SOURCES = test_priority.cpp test_remove_torrent_SOURCES = test_remove_torrent.cpp test_auto_unchoke_SOURCES = test_auto_unchoke.cpp test_checking_SOURCES = test_checking.cpp test_enum_net_SOURCES = test_enum_net.cpp test_fast_extension_SOURCES = test_fast_extension.cpp test_http_connection_SOURCES = test_http_connection.cpp test_lsd_SOURCES = test_lsd.cpp test_pe_crypto_SOURCES = test_pe_crypto.cpp test_pex_SOURCES = test_pex.cpp test_read_piece_SOURCES = test_read_piece.cpp test_receive_buffer_SOURCES = test_receive_buffer.cpp test_storage_SOURCES = test_storage.cpp test_time_critical_SOURCES = test_time_critical.cpp test_resume_SOURCES = test_resume.cpp test_stack_allocator_SOURCES = test_stack_allocator.cpp test_ssl_SOURCES = test_ssl.cpp test_torrent_SOURCES = test_torrent.cpp test_tracker_SOURCES = test_tracker.cpp test_transfer_SOURCES = test_transfer.cpp test_create_torrent_SOURCES = test_create_torrent.cpp enum_if_SOURCES = enum_if.cpp test_utp_SOURCES = test_utp.cpp test_session_SOURCES = test_session.cpp test_web_seed_SOURCES = test_web_seed.cpp test_web_seed_ban_SOURCES = test_web_seed_ban.cpp test_web_seed_chunked_SOURCES = test_web_seed_chunked.cpp test_web_seed_http_SOURCES = test_web_seed_http.cpp test_web_seed_http_pw_SOURCES = test_web_seed_http_pw.cpp test_web_seed_redirect_SOURCES = test_web_seed_redirect.cpp test_web_seed_socks4_SOURCES = test_web_seed_socks4.cpp test_web_seed_socks5_SOURCES = test_web_seed_socks5.cpp test_web_seed_socks5_no_peers_SOURCES = test_web_seed_socks5_no_peers.cpp test_web_seed_socks5_pw_SOURCES = test_web_seed_socks5_pw.cpp test_url_seed_SOURCES = test_url_seed.cpp test_remap_files_SOURCES = test_remap_files.cpp test_file_progress_SOURCES = test_file_progress.cpp test_linked_list_SOURCES = test_linked_list.cpp test_direct_dht_SOURCES = test_direct_dht.cpp test_ed25519_SOURCES = test_ed25519.cpp LDADD = libtest.la $(top_builddir)/src/libtorrent-rasterbar.la #AM_CXXFLAGS=-ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @DEBUGFLAGS@ @PTHREAD_CFLAGS@ AM_CPPFLAGS=-ftemplate-depth-50 -I$(top_srcdir)/include @DEBUGFLAGS@ @OPENSSL_INCLUDES@ AM_LDFLAGS=@BOOST_SYSTEM_LIB@ @PTHREAD_LIBS@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ \ @BOOST_CHRONO_LIB@ libtorrent-rasterbar-1.1.13/test/Makefile.in000066400000000000000000002632061351156116000210000ustar00rootroot00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 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@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd 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@ target_triplet = @target@ @ENABLE_TESTS_TRUE@check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) TESTS = $(am__EXEEXT_1) EXTRA_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) subdir = test ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ $(top_srcdir)/m4/ax_boost_chrono.m4 \ $(top_srcdir)/m4/ax_boost_python.m4 \ $(top_srcdir)/m4/ax_boost_random.m4 \ $(top_srcdir)/m4/ax_boost_system.m4 \ $(top_srcdir)/m4/ax_check_openssl.m4 \ $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libtest_la_LIBADD = am_libtest_la_OBJECTS = main.lo test.lo setup_transfer.lo \ dht_server.lo udp_tracker.lo peer_server.lo bittorrent_peer.lo \ make_torrent.lo web_seed_suite.lo swarm_suite.lo test_utils.lo \ settings.lo print_alerts.lo libtest_la_OBJECTS = $(am_libtest_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = @ENABLE_TESTS_TRUE@am_libtest_la_rpath = am__EXEEXT_1 = test_primitives$(EXEEXT) test_recheck$(EXEEXT) \ test_stat_cache$(EXEEXT) test_file$(EXEEXT) \ test_privacy$(EXEEXT) test_priority$(EXEEXT) \ test_remove_torrent$(EXEEXT) test_auto_unchoke$(EXEEXT) \ test_checking$(EXEEXT) test_fast_extension$(EXEEXT) \ test_http_connection$(EXEEXT) test_lsd$(EXEEXT) \ test_pe_crypto$(EXEEXT) test_pex$(EXEEXT) \ test_read_piece$(EXEEXT) test_receive_buffer$(EXEEXT) \ test_resume$(EXEEXT) test_ssl$(EXEEXT) \ test_stack_allocator$(EXEEXT) test_storage$(EXEEXT) \ test_time_critical$(EXEEXT) test_torrent$(EXEEXT) \ test_tracker$(EXEEXT) test_transfer$(EXEEXT) \ test_create_torrent$(EXEEXT) enum_if$(EXEEXT) \ test_utp$(EXEEXT) test_session$(EXEEXT) test_web_seed$(EXEEXT) \ test_web_seed_ban$(EXEEXT) test_web_seed_chunked$(EXEEXT) \ test_web_seed_http$(EXEEXT) test_web_seed_http_pw$(EXEEXT) \ test_web_seed_redirect$(EXEEXT) test_web_seed_socks4$(EXEEXT) \ test_web_seed_socks5$(EXEEXT) \ test_web_seed_socks5_no_peers$(EXEEXT) \ test_web_seed_socks5_pw$(EXEEXT) test_url_seed$(EXEEXT) \ test_remap_files$(EXEEXT) test_enum_net$(EXEEXT) \ test_file_progress$(EXEEXT) test_linked_list$(EXEEXT) \ test_direct_dht$(EXEEXT) test_ed25519$(EXEEXT) am__EXEEXT_2 = bdecode_benchmark$(EXEEXT) am_bdecode_benchmark_OBJECTS = bdecode_benchmark.$(OBJEXT) bdecode_benchmark_OBJECTS = $(am_bdecode_benchmark_OBJECTS) bdecode_benchmark_LDADD = $(LDADD) bdecode_benchmark_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_enum_if_OBJECTS = enum_if.$(OBJEXT) enum_if_OBJECTS = $(am_enum_if_OBJECTS) enum_if_LDADD = $(LDADD) enum_if_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_auto_unchoke_OBJECTS = test_auto_unchoke.$(OBJEXT) test_auto_unchoke_OBJECTS = $(am_test_auto_unchoke_OBJECTS) test_auto_unchoke_LDADD = $(LDADD) test_auto_unchoke_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_checking_OBJECTS = test_checking.$(OBJEXT) test_checking_OBJECTS = $(am_test_checking_OBJECTS) test_checking_LDADD = $(LDADD) test_checking_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_create_torrent_OBJECTS = test_create_torrent.$(OBJEXT) test_create_torrent_OBJECTS = $(am_test_create_torrent_OBJECTS) test_create_torrent_LDADD = $(LDADD) test_create_torrent_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_direct_dht_OBJECTS = test_direct_dht.$(OBJEXT) test_direct_dht_OBJECTS = $(am_test_direct_dht_OBJECTS) test_direct_dht_LDADD = $(LDADD) test_direct_dht_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_ed25519_OBJECTS = test_ed25519.$(OBJEXT) test_ed25519_OBJECTS = $(am_test_ed25519_OBJECTS) test_ed25519_LDADD = $(LDADD) test_ed25519_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_enum_net_OBJECTS = test_enum_net.$(OBJEXT) test_enum_net_OBJECTS = $(am_test_enum_net_OBJECTS) test_enum_net_LDADD = $(LDADD) test_enum_net_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_fast_extension_OBJECTS = test_fast_extension.$(OBJEXT) test_fast_extension_OBJECTS = $(am_test_fast_extension_OBJECTS) test_fast_extension_LDADD = $(LDADD) test_fast_extension_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_file_OBJECTS = test_file.$(OBJEXT) test_file_OBJECTS = $(am_test_file_OBJECTS) test_file_LDADD = $(LDADD) test_file_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_file_progress_OBJECTS = test_file_progress.$(OBJEXT) test_file_progress_OBJECTS = $(am_test_file_progress_OBJECTS) test_file_progress_LDADD = $(LDADD) test_file_progress_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_http_connection_OBJECTS = test_http_connection.$(OBJEXT) test_http_connection_OBJECTS = $(am_test_http_connection_OBJECTS) test_http_connection_LDADD = $(LDADD) test_http_connection_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_linked_list_OBJECTS = test_linked_list.$(OBJEXT) test_linked_list_OBJECTS = $(am_test_linked_list_OBJECTS) test_linked_list_LDADD = $(LDADD) test_linked_list_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_lsd_OBJECTS = test_lsd.$(OBJEXT) test_lsd_OBJECTS = $(am_test_lsd_OBJECTS) test_lsd_LDADD = $(LDADD) test_lsd_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_pe_crypto_OBJECTS = test_pe_crypto.$(OBJEXT) test_pe_crypto_OBJECTS = $(am_test_pe_crypto_OBJECTS) test_pe_crypto_LDADD = $(LDADD) test_pe_crypto_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_pex_OBJECTS = test_pex.$(OBJEXT) test_pex_OBJECTS = $(am_test_pex_OBJECTS) test_pex_LDADD = $(LDADD) test_pex_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_primitives_OBJECTS = test_primitives.$(OBJEXT) \ test_packet_buffer.$(OBJEXT) test_timestamp_history.$(OBJEXT) \ test_sha1_hash.$(OBJEXT) test_bloom_filter.$(OBJEXT) \ test_identify_client.$(OBJEXT) test_merkle.$(OBJEXT) \ test_alert_manager.$(OBJEXT) test_resolve_links.$(OBJEXT) \ test_crc32.$(OBJEXT) test_heterogeneous_queue.$(OBJEXT) \ test_ip_voter.$(OBJEXT) test_sliding_average.$(OBJEXT) \ test_socket_io.$(OBJEXT) test_random.$(OBJEXT) \ test_utf8.$(OBJEXT) test_gzip.$(OBJEXT) \ test_bitfield.$(OBJEXT) test_part_file.$(OBJEXT) \ test_peer_list.$(OBJEXT) test_torrent_info.$(OBJEXT) \ test_time.$(OBJEXT) test_file_storage.$(OBJEXT) \ test_peer_priority.$(OBJEXT) test_threads.$(OBJEXT) \ test_tailqueue.$(OBJEXT) test_bandwidth_limiter.$(OBJEXT) \ test_buffer.$(OBJEXT) test_piece_picker.$(OBJEXT) \ test_bencoding.$(OBJEXT) test_bdecode.$(OBJEXT) \ test_http_parser.$(OBJEXT) test_string.$(OBJEXT) \ test_magnet.$(OBJEXT) test_xml.$(OBJEXT) \ test_ip_filter.$(OBJEXT) test_hasher.$(OBJEXT) \ test_dht_storage.$(OBJEXT) test_dht.$(OBJEXT) \ test_block_cache.$(OBJEXT) test_peer_classes.$(OBJEXT) \ test_settings_pack.$(OBJEXT) test_fence.$(OBJEXT) \ test_dos_blocker.$(OBJEXT) test_upnp.$(OBJEXT) test_primitives_OBJECTS = $(am_test_primitives_OBJECTS) test_primitives_LDADD = $(LDADD) test_primitives_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_priority_OBJECTS = test_priority.$(OBJEXT) test_priority_OBJECTS = $(am_test_priority_OBJECTS) test_priority_LDADD = $(LDADD) test_priority_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_privacy_OBJECTS = test_privacy.$(OBJEXT) test_privacy_OBJECTS = $(am_test_privacy_OBJECTS) test_privacy_LDADD = $(LDADD) test_privacy_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_read_piece_OBJECTS = test_read_piece.$(OBJEXT) test_read_piece_OBJECTS = $(am_test_read_piece_OBJECTS) test_read_piece_LDADD = $(LDADD) test_read_piece_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_receive_buffer_OBJECTS = test_receive_buffer.$(OBJEXT) test_receive_buffer_OBJECTS = $(am_test_receive_buffer_OBJECTS) test_receive_buffer_LDADD = $(LDADD) test_receive_buffer_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_recheck_OBJECTS = test_recheck.$(OBJEXT) test_recheck_OBJECTS = $(am_test_recheck_OBJECTS) test_recheck_LDADD = $(LDADD) test_recheck_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_remap_files_OBJECTS = test_remap_files.$(OBJEXT) test_remap_files_OBJECTS = $(am_test_remap_files_OBJECTS) test_remap_files_LDADD = $(LDADD) test_remap_files_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_remove_torrent_OBJECTS = test_remove_torrent.$(OBJEXT) test_remove_torrent_OBJECTS = $(am_test_remove_torrent_OBJECTS) test_remove_torrent_LDADD = $(LDADD) test_remove_torrent_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_resume_OBJECTS = test_resume.$(OBJEXT) test_resume_OBJECTS = $(am_test_resume_OBJECTS) test_resume_LDADD = $(LDADD) test_resume_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_session_OBJECTS = test_session.$(OBJEXT) test_session_OBJECTS = $(am_test_session_OBJECTS) test_session_LDADD = $(LDADD) test_session_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_ssl_OBJECTS = test_ssl.$(OBJEXT) test_ssl_OBJECTS = $(am_test_ssl_OBJECTS) test_ssl_LDADD = $(LDADD) test_ssl_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_stack_allocator_OBJECTS = test_stack_allocator.$(OBJEXT) test_stack_allocator_OBJECTS = $(am_test_stack_allocator_OBJECTS) test_stack_allocator_LDADD = $(LDADD) test_stack_allocator_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_stat_cache_OBJECTS = test_stat_cache.$(OBJEXT) test_stat_cache_OBJECTS = $(am_test_stat_cache_OBJECTS) test_stat_cache_LDADD = $(LDADD) test_stat_cache_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_storage_OBJECTS = test_storage.$(OBJEXT) test_storage_OBJECTS = $(am_test_storage_OBJECTS) test_storage_LDADD = $(LDADD) test_storage_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_time_critical_OBJECTS = test_time_critical.$(OBJEXT) test_time_critical_OBJECTS = $(am_test_time_critical_OBJECTS) test_time_critical_LDADD = $(LDADD) test_time_critical_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_torrent_OBJECTS = test_torrent.$(OBJEXT) test_torrent_OBJECTS = $(am_test_torrent_OBJECTS) test_torrent_LDADD = $(LDADD) test_torrent_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_tracker_OBJECTS = test_tracker.$(OBJEXT) test_tracker_OBJECTS = $(am_test_tracker_OBJECTS) test_tracker_LDADD = $(LDADD) test_tracker_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_transfer_OBJECTS = test_transfer.$(OBJEXT) test_transfer_OBJECTS = $(am_test_transfer_OBJECTS) test_transfer_LDADD = $(LDADD) test_transfer_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_url_seed_OBJECTS = test_url_seed.$(OBJEXT) test_url_seed_OBJECTS = $(am_test_url_seed_OBJECTS) test_url_seed_LDADD = $(LDADD) test_url_seed_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_utp_OBJECTS = test_utp.$(OBJEXT) test_utp_OBJECTS = $(am_test_utp_OBJECTS) test_utp_LDADD = $(LDADD) test_utp_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_web_seed_OBJECTS = test_web_seed.$(OBJEXT) test_web_seed_OBJECTS = $(am_test_web_seed_OBJECTS) test_web_seed_LDADD = $(LDADD) test_web_seed_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_web_seed_ban_OBJECTS = test_web_seed_ban.$(OBJEXT) test_web_seed_ban_OBJECTS = $(am_test_web_seed_ban_OBJECTS) test_web_seed_ban_LDADD = $(LDADD) test_web_seed_ban_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_web_seed_chunked_OBJECTS = test_web_seed_chunked.$(OBJEXT) test_web_seed_chunked_OBJECTS = $(am_test_web_seed_chunked_OBJECTS) test_web_seed_chunked_LDADD = $(LDADD) test_web_seed_chunked_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_web_seed_http_OBJECTS = test_web_seed_http.$(OBJEXT) test_web_seed_http_OBJECTS = $(am_test_web_seed_http_OBJECTS) test_web_seed_http_LDADD = $(LDADD) test_web_seed_http_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_web_seed_http_pw_OBJECTS = test_web_seed_http_pw.$(OBJEXT) test_web_seed_http_pw_OBJECTS = $(am_test_web_seed_http_pw_OBJECTS) test_web_seed_http_pw_LDADD = $(LDADD) test_web_seed_http_pw_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_web_seed_redirect_OBJECTS = test_web_seed_redirect.$(OBJEXT) test_web_seed_redirect_OBJECTS = $(am_test_web_seed_redirect_OBJECTS) test_web_seed_redirect_LDADD = $(LDADD) test_web_seed_redirect_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_web_seed_socks4_OBJECTS = test_web_seed_socks4.$(OBJEXT) test_web_seed_socks4_OBJECTS = $(am_test_web_seed_socks4_OBJECTS) test_web_seed_socks4_LDADD = $(LDADD) test_web_seed_socks4_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_web_seed_socks5_OBJECTS = test_web_seed_socks5.$(OBJEXT) test_web_seed_socks5_OBJECTS = $(am_test_web_seed_socks5_OBJECTS) test_web_seed_socks5_LDADD = $(LDADD) test_web_seed_socks5_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_web_seed_socks5_no_peers_OBJECTS = \ test_web_seed_socks5_no_peers.$(OBJEXT) test_web_seed_socks5_no_peers_OBJECTS = \ $(am_test_web_seed_socks5_no_peers_OBJECTS) test_web_seed_socks5_no_peers_LDADD = $(LDADD) test_web_seed_socks5_no_peers_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la am_test_web_seed_socks5_pw_OBJECTS = \ test_web_seed_socks5_pw.$(OBJEXT) test_web_seed_socks5_pw_OBJECTS = \ $(am_test_web_seed_socks5_pw_OBJECTS) test_web_seed_socks5_pw_LDADD = $(LDADD) test_web_seed_socks5_pw_DEPENDENCIES = libtest.la \ $(top_builddir)/src/libtorrent-rasterbar.la AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtest_la_SOURCES) $(bdecode_benchmark_SOURCES) \ $(enum_if_SOURCES) $(test_auto_unchoke_SOURCES) \ $(test_checking_SOURCES) $(test_create_torrent_SOURCES) \ $(test_direct_dht_SOURCES) $(test_ed25519_SOURCES) \ $(test_enum_net_SOURCES) $(test_fast_extension_SOURCES) \ $(test_file_SOURCES) $(test_file_progress_SOURCES) \ $(test_http_connection_SOURCES) $(test_linked_list_SOURCES) \ $(test_lsd_SOURCES) $(test_pe_crypto_SOURCES) \ $(test_pex_SOURCES) $(test_primitives_SOURCES) \ $(test_priority_SOURCES) $(test_privacy_SOURCES) \ $(test_read_piece_SOURCES) $(test_receive_buffer_SOURCES) \ $(test_recheck_SOURCES) $(test_remap_files_SOURCES) \ $(test_remove_torrent_SOURCES) $(test_resume_SOURCES) \ $(test_session_SOURCES) $(test_ssl_SOURCES) \ $(test_stack_allocator_SOURCES) $(test_stat_cache_SOURCES) \ $(test_storage_SOURCES) $(test_time_critical_SOURCES) \ $(test_torrent_SOURCES) $(test_tracker_SOURCES) \ $(test_transfer_SOURCES) $(test_url_seed_SOURCES) \ $(test_utp_SOURCES) $(test_web_seed_SOURCES) \ $(test_web_seed_ban_SOURCES) $(test_web_seed_chunked_SOURCES) \ $(test_web_seed_http_SOURCES) $(test_web_seed_http_pw_SOURCES) \ $(test_web_seed_redirect_SOURCES) \ $(test_web_seed_socks4_SOURCES) \ $(test_web_seed_socks5_SOURCES) \ $(test_web_seed_socks5_no_peers_SOURCES) \ $(test_web_seed_socks5_pw_SOURCES) DIST_SOURCES = $(libtest_la_SOURCES) $(bdecode_benchmark_SOURCES) \ $(enum_if_SOURCES) $(test_auto_unchoke_SOURCES) \ $(test_checking_SOURCES) $(test_create_torrent_SOURCES) \ $(test_direct_dht_SOURCES) $(test_ed25519_SOURCES) \ $(test_enum_net_SOURCES) $(test_fast_extension_SOURCES) \ $(test_file_SOURCES) $(test_file_progress_SOURCES) \ $(test_http_connection_SOURCES) $(test_linked_list_SOURCES) \ $(test_lsd_SOURCES) $(test_pe_crypto_SOURCES) \ $(test_pex_SOURCES) $(test_primitives_SOURCES) \ $(test_priority_SOURCES) $(test_privacy_SOURCES) \ $(test_read_piece_SOURCES) $(test_receive_buffer_SOURCES) \ $(test_recheck_SOURCES) $(test_remap_files_SOURCES) \ $(test_remove_torrent_SOURCES) $(test_resume_SOURCES) \ $(test_session_SOURCES) $(test_ssl_SOURCES) \ $(test_stack_allocator_SOURCES) $(test_stat_cache_SOURCES) \ $(test_storage_SOURCES) $(test_time_critical_SOURCES) \ $(test_torrent_SOURCES) $(test_tracker_SOURCES) \ $(test_transfer_SOURCES) $(test_url_seed_SOURCES) \ $(test_utp_SOURCES) $(test_web_seed_SOURCES) \ $(test_web_seed_ban_SOURCES) $(test_web_seed_chunked_SOURCES) \ $(test_web_seed_http_SOURCES) $(test_web_seed_http_pw_SOURCES) \ $(test_web_seed_redirect_SOURCES) \ $(test_web_seed_socks4_SOURCES) \ $(test_web_seed_socks5_SOURCES) \ $(test_web_seed_socks5_no_peers_SOURCES) \ $(test_web_seed_socks5_pw_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no am__tty_colors = { \ $(am__tty_colors_dummy); \ if test "X$(AM_COLOR_TESTS)" = Xno; then \ am__color_tests=no; \ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ am__color_tests=yes; \ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ am__color_tests=yes; \ fi; \ if test $$am__color_tests = yes; then \ red=''; \ grn=''; \ lgn=''; \ blu=''; \ mgn=''; \ brg=''; \ std=''; \ fi; \ } 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 = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__recheck_rx = ^[ ]*:recheck:[ ]* am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* # A command that, given a newline-separated list of test names on the # standard input, print the name of the tests that are to be re-run # upon "make recheck". am__list_recheck_tests = $(AWK) '{ \ recheck = 1; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ { \ if ((getline line2 < ($$0 ".log")) < 0) \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ { \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ { \ break; \ } \ }; \ if (recheck) \ print $$0; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # A command that, given a newline-separated list of test names on the # standard input, create the global log from their .trs and .log files. am__create_global_log = $(AWK) ' \ function fatal(msg) \ { \ print "fatal: making $@: " msg | "cat >&2"; \ exit 1; \ } \ function rst_section(header) \ { \ print header; \ len = length(header); \ for (i = 1; i <= len; i = i + 1) \ printf "="; \ printf "\n\n"; \ } \ { \ copy_in_global_log = 1; \ global_test_result = "RUN"; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".trs"); \ if (line ~ /$(am__global_test_result_rx)/) \ { \ sub("$(am__global_test_result_rx)", "", line); \ sub("[ ]*$$", "", line); \ global_test_result = line; \ } \ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ copy_in_global_log = 0; \ }; \ if (copy_in_global_log) \ { \ rst_section(global_test_result ": " $$0); \ while ((rc = (getline line < ($$0 ".log"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".log"); \ print line; \ }; \ printf "\n"; \ }; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # Restructured Text title. am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } # Solaris 10 'make', and several other traditional 'make' implementations, # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it # by disabling -e (using the XSI extension "set +e") if it's set. am__sh_e_setup = case $$- in *e*) set +e;; esac # Default flags passed to test drivers. am__common_driver_flags = \ --color-tests "$$am__color_tests" \ --enable-hard-errors "$$am__enable_hard_errors" \ --expect-failure "$$am__expect_failure" # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). am__check_pre = \ $(am__sh_e_setup); \ $(am__vpath_adj_setup) $(am__vpath_adj) \ $(am__tty_colors); \ srcdir=$(srcdir); export srcdir; \ case "$@" in \ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ *) am__odir=.;; \ esac; \ test "x$$am__odir" = x"." || test -d "$$am__odir" \ || $(MKDIR_P) "$$am__odir" || exit $$?; \ if test -f "./$$f"; then dir=./; \ elif test -f "$$f"; then dir=; \ else dir="$(srcdir)/"; fi; \ tst=$$dir$$f; log='$@'; \ if test -n '$(DISABLE_HARD_ERRORS)'; then \ am__enable_hard_errors=no; \ else \ am__enable_hard_errors=yes; \ fi; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ am__expect_failure=yes;; \ *) \ am__expect_failure=no;; \ esac; \ $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) # A shell command to get the names of the tests scripts with any registered # extension removed (i.e., equivalently, the names of the test logs, with # the '.log' extension removed). The result is saved in the shell variable # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", # since that might cause problem with VPATH rewrites for suffix-less tests. # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` RECHECK_LOGS = $(TEST_LOGS) AM_RECURSIVE_TARGETS = check recheck TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ case '$@' in \ */*) \ case '$*' in \ */*) b='$*';; \ *) b=`echo '$@' | sed 's/\.log$$//'`; \ esac;; \ *) \ b='$*';; \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp \ $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BOOST_CHRONO_LIB = @BOOST_CHRONO_LIB@ BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ BOOST_LDFLAGS = @BOOST_LDFLAGS@ BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ BOOST_RANDOM_LIB = @BOOST_RANDOM_LIB@ BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUGFLAGS = @DEBUGFLAGS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONV_LIBS = @ICONV_LIBS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ PYTHON_CXXFLAGS = @PYTHON_CXXFLAGS@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = subdir-objects benchmark_programs = \ bdecode_benchmark test_programs = \ test_primitives \ test_recheck \ test_stat_cache \ test_file \ test_privacy \ test_priority \ test_remove_torrent \ test_auto_unchoke \ test_checking \ test_fast_extension \ test_http_connection \ test_lsd \ test_pe_crypto \ test_pex \ test_read_piece \ test_receive_buffer \ test_resume \ test_ssl \ test_stack_allocator \ test_storage \ test_time_critical \ test_torrent \ test_tracker \ test_transfer \ test_create_torrent \ enum_if \ test_utp \ test_session \ test_web_seed \ test_web_seed_ban \ test_web_seed_chunked \ test_web_seed_http \ test_web_seed_http_pw \ test_web_seed_redirect \ test_web_seed_socks4 \ test_web_seed_socks5 \ test_web_seed_socks5_no_peers \ test_web_seed_socks5_pw \ test_url_seed \ test_remap_files \ test_enum_net \ test_file_progress \ test_linked_list \ test_direct_dht \ test_ed25519 @ENABLE_TESTS_TRUE@noinst_LTLIBRARIES = libtest.la EXTRA_DIST = Jamfile \ test_torrents/backslash_path.torrent \ test_torrents/base.torrent \ test_torrents/creation_date.torrent \ test_torrents/duplicate_files.torrent \ test_torrents/duplicate_web_seeds.torrent \ test_torrents/empty_httpseed.torrent \ test_torrents/empty_path.torrent \ test_torrents/empty_path_multi.torrent \ test_torrents/hidden_parent_path.torrent \ test_torrents/httpseed.torrent \ test_torrents/invalid_file_size.torrent \ test_torrents/invalid_info.torrent \ test_torrents/invalid_merkle.torrent \ test_torrents/invalid_name.torrent \ test_torrents/invalid_name2.torrent \ test_torrents/invalid_name3.torrent \ test_torrents/invalid_path_list.torrent \ test_torrents/invalid_piece_len.torrent \ test_torrents/invalid_pieces.torrent \ test_torrents/invalid_root_hash.torrent \ test_torrents/invalid_root_hash2.torrent \ test_torrents/invalid_symlink.torrent \ test_torrents/long_name.torrent \ test_torrents/missing_path_list.torrent \ test_torrents/missing_piece_len.torrent \ test_torrents/negative_file_size.torrent \ test_torrents/negative_piece_len.torrent \ test_torrents/negative_size.torrent \ test_torrents/no_creation_date.torrent \ test_torrents/no_name.torrent \ test_torrents/pad_file.torrent \ test_torrents/pad_file_no_path.torrent \ test_torrents/parent_path.torrent \ test_torrents/root_hash.torrent \ test_torrents/sample.torrent \ test_torrents/single_multi_file.torrent \ test_torrents/slash_path.torrent \ test_torrents/slash_path2.torrent \ test_torrents/slash_path3.torrent \ test_torrents/string.torrent \ test_torrents/symlink1.torrent \ test_torrents/symlink_zero_size.torrent \ test_torrents/unaligned_pieces.torrent \ test_torrents/unordered.torrent \ test_torrents/url_list.torrent \ test_torrents/url_list2.torrent \ test_torrents/url_list3.torrent \ test_torrents/url_seed.torrent \ test_torrents/url_seed_multi.torrent \ test_torrents/url_seed_multi_space.torrent \ test_torrents/url_seed_multi_space_nolist.torrent \ test_torrents/whitespace_url.torrent \ test_torrents/invalid_filename.torrent \ test_torrents/invalid_filename2.torrent \ mutable_test_torrents/test1.torrent \ mutable_test_torrents/test1_pad_files.torrent \ mutable_test_torrents/test1_single.torrent\ mutable_test_torrents/test1_single_padded.torrent \ mutable_test_torrents/test2.torrent \ mutable_test_torrents/test2_pad_files.torrent \ mutable_test_torrents/test3.torrent \ mutable_test_torrents/test3_pad_files.torrent \ zeroes.gz \ corrupt.gz \ utf8_test.txt \ web_server.py \ socks.py \ http.py noinst_HEADERS = test.hpp setup_transfer.hpp dht_server.hpp \ peer_server.hpp udp_tracker.hpp web_seed_suite.hpp swarm_suite.hpp \ test_utils.hpp settings.hpp make_torrent.hpp bittorrent_peer.hpp \ print_alerts.hpp libtest_la_SOURCES = main.cpp \ test.cpp \ setup_transfer.cpp \ dht_server.cpp \ udp_tracker.cpp \ peer_server.cpp \ bittorrent_peer.cpp \ make_torrent.cpp \ web_seed_suite.cpp \ swarm_suite.cpp \ test_utils.cpp \ settings.cpp \ print_alerts.cpp test_primitives_SOURCES = \ test_primitives.cpp \ test_packet_buffer.cpp \ test_timestamp_history.cpp \ test_sha1_hash.cpp \ test_bloom_filter.cpp \ test_identify_client.cpp \ test_merkle.cpp \ test_alert_manager.cpp \ test_resolve_links.cpp \ test_crc32.cpp \ test_heterogeneous_queue.cpp \ test_ip_voter.cpp \ test_sliding_average.cpp \ test_socket_io.cpp \ test_random.cpp \ test_utf8.cpp \ test_gzip.cpp \ test_bitfield.cpp \ test_part_file.cpp \ test_peer_list.cpp \ test_torrent_info.cpp \ test_time.cpp \ test_file_storage.cpp \ test_peer_priority.cpp \ test_threads.cpp \ test_tailqueue.cpp \ test_bandwidth_limiter.cpp \ test_buffer.cpp \ test_piece_picker.cpp \ test_bencoding.cpp \ test_bdecode.cpp \ test_http_parser.cpp \ test_string.cpp \ test_magnet.cpp \ test_xml.cpp \ test_ip_filter.cpp \ test_hasher.cpp \ test_dht_storage.cpp \ test_dht.cpp \ test_block_cache.cpp \ test_peer_classes.cpp \ test_settings_pack.cpp \ test_fence.cpp \ test_dos_blocker.cpp \ test_upnp.cpp bdecode_benchmark_SOURCES = bdecode_benchmark.cpp test_recheck_SOURCES = test_recheck.cpp test_stat_cache_SOURCES = test_stat_cache.cpp test_file_SOURCES = test_file.cpp test_privacy_SOURCES = test_privacy.cpp test_priority_SOURCES = test_priority.cpp test_remove_torrent_SOURCES = test_remove_torrent.cpp test_auto_unchoke_SOURCES = test_auto_unchoke.cpp test_checking_SOURCES = test_checking.cpp test_enum_net_SOURCES = test_enum_net.cpp test_fast_extension_SOURCES = test_fast_extension.cpp test_http_connection_SOURCES = test_http_connection.cpp test_lsd_SOURCES = test_lsd.cpp test_pe_crypto_SOURCES = test_pe_crypto.cpp test_pex_SOURCES = test_pex.cpp test_read_piece_SOURCES = test_read_piece.cpp test_receive_buffer_SOURCES = test_receive_buffer.cpp test_storage_SOURCES = test_storage.cpp test_time_critical_SOURCES = test_time_critical.cpp test_resume_SOURCES = test_resume.cpp test_stack_allocator_SOURCES = test_stack_allocator.cpp test_ssl_SOURCES = test_ssl.cpp test_torrent_SOURCES = test_torrent.cpp test_tracker_SOURCES = test_tracker.cpp test_transfer_SOURCES = test_transfer.cpp test_create_torrent_SOURCES = test_create_torrent.cpp enum_if_SOURCES = enum_if.cpp test_utp_SOURCES = test_utp.cpp test_session_SOURCES = test_session.cpp test_web_seed_SOURCES = test_web_seed.cpp test_web_seed_ban_SOURCES = test_web_seed_ban.cpp test_web_seed_chunked_SOURCES = test_web_seed_chunked.cpp test_web_seed_http_SOURCES = test_web_seed_http.cpp test_web_seed_http_pw_SOURCES = test_web_seed_http_pw.cpp test_web_seed_redirect_SOURCES = test_web_seed_redirect.cpp test_web_seed_socks4_SOURCES = test_web_seed_socks4.cpp test_web_seed_socks5_SOURCES = test_web_seed_socks5.cpp test_web_seed_socks5_no_peers_SOURCES = test_web_seed_socks5_no_peers.cpp test_web_seed_socks5_pw_SOURCES = test_web_seed_socks5_pw.cpp test_url_seed_SOURCES = test_url_seed.cpp test_remap_files_SOURCES = test_remap_files.cpp test_file_progress_SOURCES = test_file_progress.cpp test_linked_list_SOURCES = test_linked_list.cpp test_direct_dht_SOURCES = test_direct_dht.cpp test_ed25519_SOURCES = test_ed25519.cpp LDADD = libtest.la $(top_builddir)/src/libtorrent-rasterbar.la #AM_CXXFLAGS=-ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @DEBUGFLAGS@ @PTHREAD_CFLAGS@ AM_CPPFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include @DEBUGFLAGS@ @OPENSSL_INCLUDES@ AM_LDFLAGS = @BOOST_SYSTEM_LIB@ @PTHREAD_LIBS@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ \ @BOOST_CHRONO_LIB@ all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign test/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign test/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: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libtest.la: $(libtest_la_OBJECTS) $(libtest_la_DEPENDENCIES) $(EXTRA_libtest_la_DEPENDENCIES) $(AM_V_CXXLD)$(CXXLINK) $(am_libtest_la_rpath) $(libtest_la_OBJECTS) $(libtest_la_LIBADD) $(LIBS) clean-checkPROGRAMS: @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list bdecode_benchmark$(EXEEXT): $(bdecode_benchmark_OBJECTS) $(bdecode_benchmark_DEPENDENCIES) $(EXTRA_bdecode_benchmark_DEPENDENCIES) @rm -f bdecode_benchmark$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(bdecode_benchmark_OBJECTS) $(bdecode_benchmark_LDADD) $(LIBS) enum_if$(EXEEXT): $(enum_if_OBJECTS) $(enum_if_DEPENDENCIES) $(EXTRA_enum_if_DEPENDENCIES) @rm -f enum_if$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(enum_if_OBJECTS) $(enum_if_LDADD) $(LIBS) test_auto_unchoke$(EXEEXT): $(test_auto_unchoke_OBJECTS) $(test_auto_unchoke_DEPENDENCIES) $(EXTRA_test_auto_unchoke_DEPENDENCIES) @rm -f test_auto_unchoke$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_auto_unchoke_OBJECTS) $(test_auto_unchoke_LDADD) $(LIBS) test_checking$(EXEEXT): $(test_checking_OBJECTS) $(test_checking_DEPENDENCIES) $(EXTRA_test_checking_DEPENDENCIES) @rm -f test_checking$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_checking_OBJECTS) $(test_checking_LDADD) $(LIBS) test_create_torrent$(EXEEXT): $(test_create_torrent_OBJECTS) $(test_create_torrent_DEPENDENCIES) $(EXTRA_test_create_torrent_DEPENDENCIES) @rm -f test_create_torrent$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_create_torrent_OBJECTS) $(test_create_torrent_LDADD) $(LIBS) test_direct_dht$(EXEEXT): $(test_direct_dht_OBJECTS) $(test_direct_dht_DEPENDENCIES) $(EXTRA_test_direct_dht_DEPENDENCIES) @rm -f test_direct_dht$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_direct_dht_OBJECTS) $(test_direct_dht_LDADD) $(LIBS) test_ed25519$(EXEEXT): $(test_ed25519_OBJECTS) $(test_ed25519_DEPENDENCIES) $(EXTRA_test_ed25519_DEPENDENCIES) @rm -f test_ed25519$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_ed25519_OBJECTS) $(test_ed25519_LDADD) $(LIBS) test_enum_net$(EXEEXT): $(test_enum_net_OBJECTS) $(test_enum_net_DEPENDENCIES) $(EXTRA_test_enum_net_DEPENDENCIES) @rm -f test_enum_net$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_enum_net_OBJECTS) $(test_enum_net_LDADD) $(LIBS) test_fast_extension$(EXEEXT): $(test_fast_extension_OBJECTS) $(test_fast_extension_DEPENDENCIES) $(EXTRA_test_fast_extension_DEPENDENCIES) @rm -f test_fast_extension$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_fast_extension_OBJECTS) $(test_fast_extension_LDADD) $(LIBS) test_file$(EXEEXT): $(test_file_OBJECTS) $(test_file_DEPENDENCIES) $(EXTRA_test_file_DEPENDENCIES) @rm -f test_file$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_file_OBJECTS) $(test_file_LDADD) $(LIBS) test_file_progress$(EXEEXT): $(test_file_progress_OBJECTS) $(test_file_progress_DEPENDENCIES) $(EXTRA_test_file_progress_DEPENDENCIES) @rm -f test_file_progress$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_file_progress_OBJECTS) $(test_file_progress_LDADD) $(LIBS) test_http_connection$(EXEEXT): $(test_http_connection_OBJECTS) $(test_http_connection_DEPENDENCIES) $(EXTRA_test_http_connection_DEPENDENCIES) @rm -f test_http_connection$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_http_connection_OBJECTS) $(test_http_connection_LDADD) $(LIBS) test_linked_list$(EXEEXT): $(test_linked_list_OBJECTS) $(test_linked_list_DEPENDENCIES) $(EXTRA_test_linked_list_DEPENDENCIES) @rm -f test_linked_list$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_linked_list_OBJECTS) $(test_linked_list_LDADD) $(LIBS) test_lsd$(EXEEXT): $(test_lsd_OBJECTS) $(test_lsd_DEPENDENCIES) $(EXTRA_test_lsd_DEPENDENCIES) @rm -f test_lsd$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_lsd_OBJECTS) $(test_lsd_LDADD) $(LIBS) test_pe_crypto$(EXEEXT): $(test_pe_crypto_OBJECTS) $(test_pe_crypto_DEPENDENCIES) $(EXTRA_test_pe_crypto_DEPENDENCIES) @rm -f test_pe_crypto$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_pe_crypto_OBJECTS) $(test_pe_crypto_LDADD) $(LIBS) test_pex$(EXEEXT): $(test_pex_OBJECTS) $(test_pex_DEPENDENCIES) $(EXTRA_test_pex_DEPENDENCIES) @rm -f test_pex$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_pex_OBJECTS) $(test_pex_LDADD) $(LIBS) test_primitives$(EXEEXT): $(test_primitives_OBJECTS) $(test_primitives_DEPENDENCIES) $(EXTRA_test_primitives_DEPENDENCIES) @rm -f test_primitives$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_primitives_OBJECTS) $(test_primitives_LDADD) $(LIBS) test_priority$(EXEEXT): $(test_priority_OBJECTS) $(test_priority_DEPENDENCIES) $(EXTRA_test_priority_DEPENDENCIES) @rm -f test_priority$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_priority_OBJECTS) $(test_priority_LDADD) $(LIBS) test_privacy$(EXEEXT): $(test_privacy_OBJECTS) $(test_privacy_DEPENDENCIES) $(EXTRA_test_privacy_DEPENDENCIES) @rm -f test_privacy$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_privacy_OBJECTS) $(test_privacy_LDADD) $(LIBS) test_read_piece$(EXEEXT): $(test_read_piece_OBJECTS) $(test_read_piece_DEPENDENCIES) $(EXTRA_test_read_piece_DEPENDENCIES) @rm -f test_read_piece$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_read_piece_OBJECTS) $(test_read_piece_LDADD) $(LIBS) test_receive_buffer$(EXEEXT): $(test_receive_buffer_OBJECTS) $(test_receive_buffer_DEPENDENCIES) $(EXTRA_test_receive_buffer_DEPENDENCIES) @rm -f test_receive_buffer$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_receive_buffer_OBJECTS) $(test_receive_buffer_LDADD) $(LIBS) test_recheck$(EXEEXT): $(test_recheck_OBJECTS) $(test_recheck_DEPENDENCIES) $(EXTRA_test_recheck_DEPENDENCIES) @rm -f test_recheck$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_recheck_OBJECTS) $(test_recheck_LDADD) $(LIBS) test_remap_files$(EXEEXT): $(test_remap_files_OBJECTS) $(test_remap_files_DEPENDENCIES) $(EXTRA_test_remap_files_DEPENDENCIES) @rm -f test_remap_files$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_remap_files_OBJECTS) $(test_remap_files_LDADD) $(LIBS) test_remove_torrent$(EXEEXT): $(test_remove_torrent_OBJECTS) $(test_remove_torrent_DEPENDENCIES) $(EXTRA_test_remove_torrent_DEPENDENCIES) @rm -f test_remove_torrent$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_remove_torrent_OBJECTS) $(test_remove_torrent_LDADD) $(LIBS) test_resume$(EXEEXT): $(test_resume_OBJECTS) $(test_resume_DEPENDENCIES) $(EXTRA_test_resume_DEPENDENCIES) @rm -f test_resume$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_resume_OBJECTS) $(test_resume_LDADD) $(LIBS) test_session$(EXEEXT): $(test_session_OBJECTS) $(test_session_DEPENDENCIES) $(EXTRA_test_session_DEPENDENCIES) @rm -f test_session$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_session_OBJECTS) $(test_session_LDADD) $(LIBS) test_ssl$(EXEEXT): $(test_ssl_OBJECTS) $(test_ssl_DEPENDENCIES) $(EXTRA_test_ssl_DEPENDENCIES) @rm -f test_ssl$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_ssl_OBJECTS) $(test_ssl_LDADD) $(LIBS) test_stack_allocator$(EXEEXT): $(test_stack_allocator_OBJECTS) $(test_stack_allocator_DEPENDENCIES) $(EXTRA_test_stack_allocator_DEPENDENCIES) @rm -f test_stack_allocator$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_stack_allocator_OBJECTS) $(test_stack_allocator_LDADD) $(LIBS) test_stat_cache$(EXEEXT): $(test_stat_cache_OBJECTS) $(test_stat_cache_DEPENDENCIES) $(EXTRA_test_stat_cache_DEPENDENCIES) @rm -f test_stat_cache$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_stat_cache_OBJECTS) $(test_stat_cache_LDADD) $(LIBS) test_storage$(EXEEXT): $(test_storage_OBJECTS) $(test_storage_DEPENDENCIES) $(EXTRA_test_storage_DEPENDENCIES) @rm -f test_storage$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_storage_OBJECTS) $(test_storage_LDADD) $(LIBS) test_time_critical$(EXEEXT): $(test_time_critical_OBJECTS) $(test_time_critical_DEPENDENCIES) $(EXTRA_test_time_critical_DEPENDENCIES) @rm -f test_time_critical$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_time_critical_OBJECTS) $(test_time_critical_LDADD) $(LIBS) test_torrent$(EXEEXT): $(test_torrent_OBJECTS) $(test_torrent_DEPENDENCIES) $(EXTRA_test_torrent_DEPENDENCIES) @rm -f test_torrent$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_torrent_OBJECTS) $(test_torrent_LDADD) $(LIBS) test_tracker$(EXEEXT): $(test_tracker_OBJECTS) $(test_tracker_DEPENDENCIES) $(EXTRA_test_tracker_DEPENDENCIES) @rm -f test_tracker$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_tracker_OBJECTS) $(test_tracker_LDADD) $(LIBS) test_transfer$(EXEEXT): $(test_transfer_OBJECTS) $(test_transfer_DEPENDENCIES) $(EXTRA_test_transfer_DEPENDENCIES) @rm -f test_transfer$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_transfer_OBJECTS) $(test_transfer_LDADD) $(LIBS) test_url_seed$(EXEEXT): $(test_url_seed_OBJECTS) $(test_url_seed_DEPENDENCIES) $(EXTRA_test_url_seed_DEPENDENCIES) @rm -f test_url_seed$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_url_seed_OBJECTS) $(test_url_seed_LDADD) $(LIBS) test_utp$(EXEEXT): $(test_utp_OBJECTS) $(test_utp_DEPENDENCIES) $(EXTRA_test_utp_DEPENDENCIES) @rm -f test_utp$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_utp_OBJECTS) $(test_utp_LDADD) $(LIBS) test_web_seed$(EXEEXT): $(test_web_seed_OBJECTS) $(test_web_seed_DEPENDENCIES) $(EXTRA_test_web_seed_DEPENDENCIES) @rm -f test_web_seed$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_web_seed_OBJECTS) $(test_web_seed_LDADD) $(LIBS) test_web_seed_ban$(EXEEXT): $(test_web_seed_ban_OBJECTS) $(test_web_seed_ban_DEPENDENCIES) $(EXTRA_test_web_seed_ban_DEPENDENCIES) @rm -f test_web_seed_ban$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_web_seed_ban_OBJECTS) $(test_web_seed_ban_LDADD) $(LIBS) test_web_seed_chunked$(EXEEXT): $(test_web_seed_chunked_OBJECTS) $(test_web_seed_chunked_DEPENDENCIES) $(EXTRA_test_web_seed_chunked_DEPENDENCIES) @rm -f test_web_seed_chunked$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_web_seed_chunked_OBJECTS) $(test_web_seed_chunked_LDADD) $(LIBS) test_web_seed_http$(EXEEXT): $(test_web_seed_http_OBJECTS) $(test_web_seed_http_DEPENDENCIES) $(EXTRA_test_web_seed_http_DEPENDENCIES) @rm -f test_web_seed_http$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_web_seed_http_OBJECTS) $(test_web_seed_http_LDADD) $(LIBS) test_web_seed_http_pw$(EXEEXT): $(test_web_seed_http_pw_OBJECTS) $(test_web_seed_http_pw_DEPENDENCIES) $(EXTRA_test_web_seed_http_pw_DEPENDENCIES) @rm -f test_web_seed_http_pw$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_web_seed_http_pw_OBJECTS) $(test_web_seed_http_pw_LDADD) $(LIBS) test_web_seed_redirect$(EXEEXT): $(test_web_seed_redirect_OBJECTS) $(test_web_seed_redirect_DEPENDENCIES) $(EXTRA_test_web_seed_redirect_DEPENDENCIES) @rm -f test_web_seed_redirect$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_web_seed_redirect_OBJECTS) $(test_web_seed_redirect_LDADD) $(LIBS) test_web_seed_socks4$(EXEEXT): $(test_web_seed_socks4_OBJECTS) $(test_web_seed_socks4_DEPENDENCIES) $(EXTRA_test_web_seed_socks4_DEPENDENCIES) @rm -f test_web_seed_socks4$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_web_seed_socks4_OBJECTS) $(test_web_seed_socks4_LDADD) $(LIBS) test_web_seed_socks5$(EXEEXT): $(test_web_seed_socks5_OBJECTS) $(test_web_seed_socks5_DEPENDENCIES) $(EXTRA_test_web_seed_socks5_DEPENDENCIES) @rm -f test_web_seed_socks5$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_web_seed_socks5_OBJECTS) $(test_web_seed_socks5_LDADD) $(LIBS) test_web_seed_socks5_no_peers$(EXEEXT): $(test_web_seed_socks5_no_peers_OBJECTS) $(test_web_seed_socks5_no_peers_DEPENDENCIES) $(EXTRA_test_web_seed_socks5_no_peers_DEPENDENCIES) @rm -f test_web_seed_socks5_no_peers$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_web_seed_socks5_no_peers_OBJECTS) $(test_web_seed_socks5_no_peers_LDADD) $(LIBS) test_web_seed_socks5_pw$(EXEEXT): $(test_web_seed_socks5_pw_OBJECTS) $(test_web_seed_socks5_pw_DEPENDENCIES) $(EXTRA_test_web_seed_socks5_pw_DEPENDENCIES) @rm -f test_web_seed_socks5_pw$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_web_seed_socks5_pw_OBJECTS) $(test_web_seed_socks5_pw_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bdecode_benchmark.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bittorrent_peer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dht_server.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/enum_if.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/make_torrent.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_server.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print_alerts.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setup_transfer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/swarm_suite.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_alert_manager.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_auto_unchoke.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_bandwidth_limiter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_bdecode.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_bencoding.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_bitfield.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_block_cache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_bloom_filter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_checking.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crc32.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_create_torrent.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dht.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dht_storage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_direct_dht.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dos_blocker.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_ed25519.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_enum_net.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_fast_extension.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_fence.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_file.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_file_progress.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_file_storage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_gzip.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_hasher.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_heterogeneous_queue.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_http_connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_http_parser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_identify_client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_ip_filter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_ip_voter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_linked_list.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lsd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_magnet.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_merkle.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_packet_buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_part_file.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_pe_crypto.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_peer_classes.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_peer_list.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_peer_priority.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_pex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_piece_picker.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_primitives.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_priority.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_privacy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_random.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_read_piece.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_receive_buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_recheck.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_remap_files.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_remove_torrent.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_resolve_links.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_resume.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_session.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_settings_pack.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_sha1_hash.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_sliding_average.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_socket_io.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_ssl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stack_allocator.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stat_cache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_storage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_string.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_tailqueue.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_threads.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_time.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_time_critical.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_timestamp_history.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_torrent.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_torrent_info.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_tracker.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_transfer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_upnp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_url_seed.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_utf8.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_utils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_utp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_web_seed.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_web_seed_ban.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_web_seed_chunked.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_web_seed_http.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_web_seed_http_pw.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_web_seed_redirect.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_web_seed_socks4.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_web_seed_socks5.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_web_seed_socks5_no_peers.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_web_seed_socks5_pw.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_xml.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_tracker.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_seed_suite.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags # Recover from deleted '.trs' file; this should ensure that # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells # to avoid problems with "make -n". .log.trs: rm -f $< $@ $(MAKE) $(AM_MAKEFLAGS) $< # Leading 'am--fnord' is there to ensure the list of targets does not # expand to empty, as could happen e.g. with make check TESTS=''. am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) am--force-recheck: @: $(TEST_SUITE_LOG): $(TEST_LOGS) @$(am__set_TESTS_bases); \ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ redo_bases=`for i in $$bases; do \ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ done`; \ if test -n "$$redo_bases"; then \ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ if $(am__make_dryrun); then :; else \ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ fi; \ if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ elif test -n "$$redo_logs"; then \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ st=0; \ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ for i in $$redo_bases; do \ test -f $$i.trs && test -r $$i.trs \ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ test -f $$i.log && test -r $$i.log \ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ done; \ test $$st -eq 0 || exit 1; \ fi @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ ws='[ ]'; \ results=`for b in $$bases; do echo $$b.trs; done`; \ test -n "$$results" || results=/dev/null; \ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ success=true; \ else \ success=false; \ fi; \ br='==================='; br=$$br$$br$$br$$br; \ result_count () \ { \ if test x"$$1" = x"--maybe-color"; then \ maybe_colorize=yes; \ elif test x"$$1" = x"--no-color"; then \ maybe_colorize=no; \ else \ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ shift; \ desc=$$1 count=$$2; \ if test $$maybe_colorize = yes && test $$count -gt 0; then \ color_start=$$3 color_end=$$std; \ else \ color_start= color_end=; \ fi; \ echo "$${color_start}# $$desc $$count$${color_end}"; \ }; \ create_testsuite_report () \ { \ result_count $$1 "TOTAL:" $$all "$$brg"; \ result_count $$1 "PASS: " $$pass "$$grn"; \ result_count $$1 "SKIP: " $$skip "$$blu"; \ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ result_count $$1 "FAIL: " $$fail "$$red"; \ result_count $$1 "XPASS:" $$xpass "$$red"; \ result_count $$1 "ERROR:" $$error "$$mgn"; \ }; \ { \ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ $(am__rst_title); \ create_testsuite_report --no-color; \ echo; \ echo ".. contents:: :depth: 2"; \ echo; \ for b in $$bases; do echo $$b; done \ | $(am__create_global_log); \ } >$(TEST_SUITE_LOG).tmp || exit 1; \ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ if $$success; then \ col="$$grn"; \ else \ col="$$red"; \ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ if $$success; then :; else \ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ if test -n "$(PACKAGE_BUGREPORT)"; then \ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ fi; \ echo "$$col$$br$$std"; \ fi; \ $$success || exit 1 check-TESTS: @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ log_list=`for i in $$bases; do echo $$i.log; done`; \ trs_list=`for i in $$bases; do echo $$i.trs; done`; \ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; recheck: all $(check_PROGRAMS) @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ | $(am__list_recheck_tests)` || exit 1; \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ am__force_recheck=am--force-recheck \ TEST_LOGS="$$log_list"; \ exit $$? test_primitives.log: test_primitives$(EXEEXT) @p='test_primitives$(EXEEXT)'; \ b='test_primitives'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_recheck.log: test_recheck$(EXEEXT) @p='test_recheck$(EXEEXT)'; \ b='test_recheck'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_stat_cache.log: test_stat_cache$(EXEEXT) @p='test_stat_cache$(EXEEXT)'; \ b='test_stat_cache'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_file.log: test_file$(EXEEXT) @p='test_file$(EXEEXT)'; \ b='test_file'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_privacy.log: test_privacy$(EXEEXT) @p='test_privacy$(EXEEXT)'; \ b='test_privacy'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_priority.log: test_priority$(EXEEXT) @p='test_priority$(EXEEXT)'; \ b='test_priority'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_remove_torrent.log: test_remove_torrent$(EXEEXT) @p='test_remove_torrent$(EXEEXT)'; \ b='test_remove_torrent'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_auto_unchoke.log: test_auto_unchoke$(EXEEXT) @p='test_auto_unchoke$(EXEEXT)'; \ b='test_auto_unchoke'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_checking.log: test_checking$(EXEEXT) @p='test_checking$(EXEEXT)'; \ b='test_checking'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_fast_extension.log: test_fast_extension$(EXEEXT) @p='test_fast_extension$(EXEEXT)'; \ b='test_fast_extension'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_http_connection.log: test_http_connection$(EXEEXT) @p='test_http_connection$(EXEEXT)'; \ b='test_http_connection'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_lsd.log: test_lsd$(EXEEXT) @p='test_lsd$(EXEEXT)'; \ b='test_lsd'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_pe_crypto.log: test_pe_crypto$(EXEEXT) @p='test_pe_crypto$(EXEEXT)'; \ b='test_pe_crypto'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_pex.log: test_pex$(EXEEXT) @p='test_pex$(EXEEXT)'; \ b='test_pex'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_read_piece.log: test_read_piece$(EXEEXT) @p='test_read_piece$(EXEEXT)'; \ b='test_read_piece'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_receive_buffer.log: test_receive_buffer$(EXEEXT) @p='test_receive_buffer$(EXEEXT)'; \ b='test_receive_buffer'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_resume.log: test_resume$(EXEEXT) @p='test_resume$(EXEEXT)'; \ b='test_resume'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_ssl.log: test_ssl$(EXEEXT) @p='test_ssl$(EXEEXT)'; \ b='test_ssl'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_stack_allocator.log: test_stack_allocator$(EXEEXT) @p='test_stack_allocator$(EXEEXT)'; \ b='test_stack_allocator'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_storage.log: test_storage$(EXEEXT) @p='test_storage$(EXEEXT)'; \ b='test_storage'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_time_critical.log: test_time_critical$(EXEEXT) @p='test_time_critical$(EXEEXT)'; \ b='test_time_critical'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_torrent.log: test_torrent$(EXEEXT) @p='test_torrent$(EXEEXT)'; \ b='test_torrent'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_tracker.log: test_tracker$(EXEEXT) @p='test_tracker$(EXEEXT)'; \ b='test_tracker'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_transfer.log: test_transfer$(EXEEXT) @p='test_transfer$(EXEEXT)'; \ b='test_transfer'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_create_torrent.log: test_create_torrent$(EXEEXT) @p='test_create_torrent$(EXEEXT)'; \ b='test_create_torrent'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) enum_if.log: enum_if$(EXEEXT) @p='enum_if$(EXEEXT)'; \ b='enum_if'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_utp.log: test_utp$(EXEEXT) @p='test_utp$(EXEEXT)'; \ b='test_utp'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_session.log: test_session$(EXEEXT) @p='test_session$(EXEEXT)'; \ b='test_session'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_web_seed.log: test_web_seed$(EXEEXT) @p='test_web_seed$(EXEEXT)'; \ b='test_web_seed'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_web_seed_ban.log: test_web_seed_ban$(EXEEXT) @p='test_web_seed_ban$(EXEEXT)'; \ b='test_web_seed_ban'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_web_seed_chunked.log: test_web_seed_chunked$(EXEEXT) @p='test_web_seed_chunked$(EXEEXT)'; \ b='test_web_seed_chunked'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_web_seed_http.log: test_web_seed_http$(EXEEXT) @p='test_web_seed_http$(EXEEXT)'; \ b='test_web_seed_http'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_web_seed_http_pw.log: test_web_seed_http_pw$(EXEEXT) @p='test_web_seed_http_pw$(EXEEXT)'; \ b='test_web_seed_http_pw'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_web_seed_redirect.log: test_web_seed_redirect$(EXEEXT) @p='test_web_seed_redirect$(EXEEXT)'; \ b='test_web_seed_redirect'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_web_seed_socks4.log: test_web_seed_socks4$(EXEEXT) @p='test_web_seed_socks4$(EXEEXT)'; \ b='test_web_seed_socks4'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_web_seed_socks5.log: test_web_seed_socks5$(EXEEXT) @p='test_web_seed_socks5$(EXEEXT)'; \ b='test_web_seed_socks5'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_web_seed_socks5_no_peers.log: test_web_seed_socks5_no_peers$(EXEEXT) @p='test_web_seed_socks5_no_peers$(EXEEXT)'; \ b='test_web_seed_socks5_no_peers'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_web_seed_socks5_pw.log: test_web_seed_socks5_pw$(EXEEXT) @p='test_web_seed_socks5_pw$(EXEEXT)'; \ b='test_web_seed_socks5_pw'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_url_seed.log: test_url_seed$(EXEEXT) @p='test_url_seed$(EXEEXT)'; \ b='test_url_seed'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_remap_files.log: test_remap_files$(EXEEXT) @p='test_remap_files$(EXEEXT)'; \ b='test_remap_files'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_enum_net.log: test_enum_net$(EXEEXT) @p='test_enum_net$(EXEEXT)'; \ b='test_enum_net'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_file_progress.log: test_file_progress$(EXEEXT) @p='test_file_progress$(EXEEXT)'; \ b='test_file_progress'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_linked_list.log: test_linked_list$(EXEEXT) @p='test_linked_list$(EXEEXT)'; \ b='test_linked_list'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_direct_dht.log: test_direct_dht$(EXEEXT) @p='test_direct_dht$(EXEEXT)'; \ b='test_direct_dht'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test_ed25519.log: test_ed25519$(EXEEXT) @p='test_ed25519$(EXEEXT)'; \ b='test_ed25519'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) @am__EXEEXT_TRUE@.test$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$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 $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: 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: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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-checkPROGRAMS clean-generic clean-libtool \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: 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: .MAKE: check-am install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \ clean-checkPROGRAMS clean-generic clean-libtool \ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am 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-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am recheck tags tags-am uninstall \ uninstall-am .PRECIOUS: Makefile # 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: libtorrent-rasterbar-1.1.13/test/bdecode_benchmark.cpp000066400000000000000000000103431351156116000230260ustar00rootroot00000000000000/* Copyright (c) 2009, 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/lazy_entry.hpp" #include "libtorrent/bdecode.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/sha1_hash.hpp" #include #include "test.hpp" #include "libtorrent/time.hpp" using namespace libtorrent; int load_file(std::string const& filename, std::vector& v , libtorrent::error_code& ec, int limit = 8000000) { ec.clear(); FILE* f = fopen(filename.c_str(), "rb"); if (f == NULL) { ec.assign(errno, boost::system::system_category()); return -1; } int r = fseek(f, 0, SEEK_END); if (r != 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } long s = ftell(f); if (s < 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } if (s > limit) { fclose(f); return -2; } r = fseek(f, 0, SEEK_SET); if (r != 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } v.resize(s); if (s == 0) { fclose(f); return 0; } r = fread(&v[0], 1, v.size(), f); if (r < 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } fclose(f); if (r != s) return -3; return 0; } int main(int argc, char* argv[]) { using namespace libtorrent; if (argc != 2) { fputs("usage: bdecode_benchmark torrent-file\n", stderr); return 1; } std::vector buf; error_code ec; int ret = load_file(argv[1], buf, ec, 40 * 1000000); if (ret == -1) { fprintf(stderr, "file too big, aborting\n"); return 1; } if (ret != 0) { fprintf(stderr, "failed to load file: %s\n", ec.message().c_str()); return 1; } { time_point start(clock_type::now()); entry e; for (int i = 0; i < 1000000; ++i) { int len; e = bdecode(&buf[0], &buf[0] + buf.size(), len); // entry& info = e["info"]; } ptime stop(time_now_hires()); fprintf(stderr, "(slow) bdecode done in %5d ns per message\n" , int(total_microseconds(stop - start) / 1000)); } // =============================================== { ptime start(time_now_hires()); lazy_entry e; for (int i = 0; i < 1000000; ++i) { error_code ec; lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec); // lazy_entry* info = e.dict_find("info"); } time_point stop(clock_type::now()); fprintf(stderr, "lazy_bdecode done in %5d ns per message\n" , int(total_microseconds(stop - start) / 1000)); } // =============================================== { ptime start(time_now_hires()); bdecode_node e; e.reserve(100); for (int i = 0; i < 1000000; ++i) { error_code ec; bdecode(&buf[0], &buf[0] + buf.size(), e, ec); // bdecode_node info = e.dict_find("info"); } ptime stop(time_now_hires()); fprintf(stderr, "bdecode done in %5d ns per message\n" , int(total_microseconds(stop - start) / 1000)); } return 0; } libtorrent-rasterbar-1.1.13/test/bittorrent_peer.cpp000066400000000000000000000340711351156116000226420ustar00rootroot00000000000000/* Copyright (c) 2016, 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/socket.hpp" #include "libtorrent/sha1_hash.hpp" #include "libtorrent/address.hpp" #include "libtorrent/assert.hpp" #include "bittorrent_peer.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/io.hpp" #include #include using namespace libtorrent; peer_conn::peer_conn(io_service& ios , boost::function on_msg , torrent_info const& ti , tcp::endpoint const& ep , peer_mode_t mode) : s(ios) , m_mode(mode) , m_ti(ti) , read_pos(0) , m_on_msg(on_msg) , state(handshaking) , choked(true) , current_piece(-1) , m_current_piece_is_allowed(false) , block(0) , m_blocks_per_piece((m_ti.piece_length() + 0x3fff) / 0x4000) , outstanding_requests(0) , fast_extension(false) , blocks_received(0) , blocks_sent(0) , start_time(clock_type::now()) , endpoint(ep) , restarting(false) { pieces.reserve(m_ti.num_pieces()); start_conn(); } void peer_conn::start_conn() { restarting = false; s.async_connect(endpoint, boost::bind(&peer_conn::on_connect, this, _1)); } void peer_conn::on_connect(error_code const& ec) { if (ec) { close("ERROR CONNECT: %s", ec); return; } char handshake[] = "\x13" "BitTorrent protocol\0\0\0\0\0\0\0\x04" " " // space for info-hash "aaaaaaaaaaaaaaaaaaaa" // peer-id "\0\0\0\x01\x02"; // interested char* h = (char*)malloc(sizeof(handshake)); memcpy(h, handshake, sizeof(handshake)); std::memcpy(h + 28, m_ti.info_hash().data(), 20); std::generate(h + 48, h + 68, &rand); // for seeds, don't send the interested message boost::asio::async_write(s, boost::asio::buffer(h, (sizeof(handshake) - 1) - (m_mode == uploader ? 5 : 0)) , boost::bind(&peer_conn::on_handshake, this, h, _1, _2)); } void peer_conn::on_handshake(char* h, error_code const& ec, size_t bytes_transferred) { free(h); if (ec) { close("ERROR SEND HANDSHAKE: %s", ec); return; } // read handshake boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 68) , boost::bind(&peer_conn::on_handshake2, this, _1, _2)); } void peer_conn::on_handshake2(error_code const& ec, size_t bytes_transferred) { if (ec) { close("ERROR READ HANDSHAKE: %s", ec); return; } // buffer is the full 68 byte handshake // look at the extension bits fast_extension = ((char*)buffer)[27] & 4; if (m_mode == uploader) { write_have_all(); } else { work_download(); } } void peer_conn::write_have_all() { using namespace libtorrent::detail; if (fast_extension) { char* ptr = write_buf_proto; // have_all write_uint32(1, ptr); write_uint8(0xe, ptr); // unchoke write_uint32(1, ptr); write_uint8(1, ptr); error_code ec; boost::asio::async_write(s, boost::asio::buffer(write_buf_proto, ptr - write_buf_proto) , boost::bind(&peer_conn::on_have_all_sent, this, _1, _2)); } else { // bitfield int len = (m_ti.num_pieces() + 7) / 8; char* ptr = (char*)buffer; write_uint32(len + 1, ptr); write_uint8(5, ptr); memset(ptr, 255, len); ptr += len; // unchoke write_uint32(1, ptr); write_uint8(1, ptr); error_code ec; boost::asio::async_write(s, boost::asio::buffer((char*)buffer, len + 10) , boost::bind(&peer_conn::on_have_all_sent, this, _1, _2)); } } void peer_conn::on_have_all_sent(error_code const& ec, size_t bytes_transferred) { if (ec) { close("ERROR SEND HAVE ALL: %s", ec); return; } // read message boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 4) , boost::bind(&peer_conn::on_msg_length, this, _1, _2)); } bool peer_conn::write_request() { using namespace libtorrent::detail; // if we're choked (and there are no allowed-fast pieces left) if (choked && allowed_fast.empty() && !m_current_piece_is_allowed) return false; // if there are no pieces left to request if (pieces.empty() && suggested_pieces.empty() && current_piece == -1) return false; if (current_piece == -1) { // pick a new piece if (choked && allowed_fast.size() > 0) { current_piece = allowed_fast.front(); allowed_fast.erase(allowed_fast.begin()); m_current_piece_is_allowed = true; } else if (suggested_pieces.size() > 0) { current_piece = suggested_pieces.front(); suggested_pieces.erase(suggested_pieces.begin()); m_current_piece_is_allowed = false; } else if (pieces.size() > 0) { current_piece = pieces.front(); pieces.erase(pieces.begin()); m_current_piece_is_allowed = false; } else { TORRENT_ASSERT(false); } } char msg[] = "\0\0\0\xd\x06" " " // piece " " // offset " "; // length char* m = (char*)malloc(sizeof(msg)); memcpy(m, msg, sizeof(msg)); char* ptr = m + 5; write_uint32(current_piece, ptr); write_uint32(block * 16 * 1024, ptr); write_uint32(16 * 1024, ptr); error_code ec; boost::asio::async_write(s, boost::asio::buffer(m, sizeof(msg) - 1) , boost::bind(&peer_conn::on_req_sent, this, m, _1, _2)); ++outstanding_requests; ++block; if (block == m_blocks_per_piece) { block = 0; current_piece = -1; m_current_piece_is_allowed = false; } return true; } void peer_conn::on_req_sent(char* m, error_code const& ec, size_t bytes_transferred) { free(m); if (ec) { close("ERROR SEND REQUEST: %s", ec); return; } work_download(); } void peer_conn::close(char const* fmt, error_code const& ec) { end_time = clock_type::now(); char tmp[1024]; snprintf(tmp, sizeof(tmp), fmt, ec.message().c_str()); int time = total_milliseconds(end_time - start_time); if (time == 0) time = 1; float up = (boost::int64_t(blocks_sent) * 0x4000) / time / 1000.f; float down = (boost::int64_t(blocks_received) * 0x4000) / time / 1000.f; error_code e; char ep_str[200]; address const& addr = s.local_endpoint(e).address(); #if TORRENT_USE_IPV6 if (addr.is_v6()) snprintf(ep_str, sizeof(ep_str), "[%s]:%d", addr.to_string(e).c_str() , s.local_endpoint(e).port()); else #endif snprintf(ep_str, sizeof(ep_str), "%s:%d", addr.to_string(e).c_str() , s.local_endpoint(e).port()); printf("%s ep: %s sent: %d received: %d duration: %d ms up: %.1fMB/s down: %.1fMB/s\n" , tmp, ep_str, blocks_sent, blocks_received, time, up, down); } void peer_conn::work_download() { if (pieces.empty() && suggested_pieces.empty() && current_piece == -1 && outstanding_requests == 0 && blocks_received >= m_ti.num_pieces() * m_blocks_per_piece) { close("COMPLETED DOWNLOAD", error_code()); return; } // send requests if (outstanding_requests < 40) { if (write_request()) return; } // read message boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 4) , boost::bind(&peer_conn::on_msg_length, this, _1, _2)); } void peer_conn::on_msg_length(error_code const& ec, size_t bytes_transferred) { using namespace libtorrent::detail; if ((ec == boost::asio::error::operation_aborted || ec == boost::asio::error::bad_descriptor) && restarting) { start_conn(); return; } if (ec) { close("ERROR RECEIVE MESSAGE PREFIX: %s", ec); return; } char* ptr = (char*)buffer; unsigned int length = read_uint32(ptr); if (length > sizeof(buffer)) { fprintf(stderr, "len: %d\n", length); close("ERROR RECEIVE MESSAGE PREFIX: packet too big", error_code()); return; } if (length == 0) { // keep-alive messate. read another length prefix boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 4) , boost::bind(&peer_conn::on_msg_length, this, _1, _2)); } else { boost::asio::async_read(s, boost::asio::buffer((char*)buffer, length) , boost::bind(&peer_conn::on_message, this, _1, _2)); } } void peer_conn::on_message(error_code const& ec, size_t bytes_transferred) { using namespace libtorrent::detail; if ((ec == boost::asio::error::operation_aborted || ec == boost::asio::error::bad_descriptor) && restarting) { start_conn(); return; } if (ec) { close("ERROR RECEIVE MESSAGE: %s", ec); return; } char* ptr = (char*)buffer; int msg = read_uint8(ptr); m_on_msg(msg, ptr, bytes_transferred); switch (m_mode) { case peer_conn::uploader: if (msg == 6) { if (bytes_transferred != 13) { close("REQUEST packet has invalid size", error_code()); return; } int piece = detail::read_int32(ptr); int start = detail::read_int32(ptr); int length = detail::read_int32(ptr); write_piece(piece, start, length); } else if (msg == 3) // not-interested { close("DONE", error_code()); return; } else { // read another message boost::asio::async_read(s, boost::asio::buffer(buffer, 4) , boost::bind(&peer_conn::on_msg_length, this, _1, _2)); } break; case peer_conn::downloader: if (msg == 0xe) // have_all { // build a list of all pieces and request them all! pieces.resize(m_ti.num_pieces()); for (int i = 0; i < int(pieces.size()); ++i) pieces[i] = i; std::random_shuffle(pieces.begin(), pieces.end()); } else if (msg == 4) // have { int piece = detail::read_int32(ptr); if (pieces.empty()) pieces.push_back(piece); else pieces.insert(pieces.begin() + (rand() % pieces.size()), piece); } else if (msg == 5) // bitfield { pieces.reserve(m_ti.num_pieces()); int piece = 0; for (int i = 0; i < int(bytes_transferred); ++i) { int mask = 0x80; for (int k = 0; k < 8; ++k) { if (piece > m_ti.num_pieces()) break; if (*ptr & mask) pieces.push_back(piece); mask >>= 1; ++piece; } ++ptr; } std::random_shuffle(pieces.begin(), pieces.end()); } else if (msg == 7) // piece { /* if (verify_downloads) { int piece = read_uint32(ptr); int start = read_uint32(ptr); int size = bytes_transferred - 9; verify_piece(piece, start, ptr, size); } */ ++blocks_received; --outstanding_requests; int piece = detail::read_int32(ptr); int start = detail::read_int32(ptr); if (int((start + bytes_transferred) / 0x4000) == m_blocks_per_piece) { write_have(piece); return; } } else if (msg == 13) // suggest { int piece = detail::read_int32(ptr); std::vector::iterator i = std::find(pieces.begin(), pieces.end(), piece); if (i != pieces.end()) { pieces.erase(i); suggested_pieces.push_back(piece); } } else if (msg == 16) // reject request { int piece = detail::read_int32(ptr); int start = detail::read_int32(ptr); int length = detail::read_int32(ptr); // put it back! if (current_piece != piece) { if (pieces.empty() || pieces.back() != piece) pieces.push_back(piece); } else { block = (std::min)(start / 0x4000, block); if (block == 0) { pieces.push_back(current_piece); current_piece = -1; m_current_piece_is_allowed = false; } } --outstanding_requests; fprintf(stderr, "REJECT: [ piece: %d start: %d length: %d ]\n", piece, start, length); } else if (msg == 0) // choke { choked = true; } else if (msg == 1) // unchoke { choked = false; } else if (msg == 17) // allowed_fast { int piece = detail::read_int32(ptr); std::vector::iterator i = std::find(pieces.begin(), pieces.end(), piece); if (i != pieces.end()) { pieces.erase(i); allowed_fast.push_back(piece); } } work_download(); break; case peer_conn::idle: // read another message boost::asio::async_read(s, boost::asio::buffer(buffer, 4) , boost::bind(&peer_conn::on_msg_length, this, _1, _2)); break; } } /* bool peer_conn::verify_piece(int piece, int start, char const* ptr, int size) { boost::uint32_t* buf = (boost::uint32_t*)ptr; boost::uint32_t fill = (piece << 8) | ((start / 0x4000) & 0xff); for (int i = 0; i < size / 4; ++i) { if (buf[i] != fill) { fprintf(stderr, "received invalid block. piece %d block %d\n", piece, start / 0x4000); exit(1); return false; } } return true; } */ void peer_conn::write_piece(int piece, int start, int length) { using namespace libtorrent::detail; // generate_block(write_buffer, piece, start, length); char* ptr = write_buf_proto; write_uint32(9 + length, ptr); TORRENT_ASSERT(length == 0x4000); write_uint8(7, ptr); write_uint32(piece, ptr); write_uint32(start, ptr); boost::array vec; vec[0] = boost::asio::buffer(write_buf_proto, ptr - write_buf_proto); vec[1] = boost::asio::buffer(write_buffer, length); boost::asio::async_write(s, vec, boost::bind(&peer_conn::on_have_all_sent, this, _1, _2)); ++blocks_sent; } void peer_conn::write_have(int piece) { using namespace libtorrent::detail; char* ptr = write_buf_proto; write_uint32(5, ptr); write_uint8(4, ptr); write_uint32(piece, ptr); boost::asio::async_write(s, boost::asio::buffer(write_buf_proto, 9), boost::bind(&peer_conn::on_have_all_sent, this, _1, _2)); } void peer_conn::abort() { error_code ec; s.close(ec); } libtorrent-rasterbar-1.1.13/test/bittorrent_peer.hpp000066400000000000000000000072601351156116000226470ustar00rootroot00000000000000/* Copyright (c) 2016, 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 BITTORRENT_PEER_HPP #define BITTORRENT_PEER_HPP #include "libtorrent/socket.hpp" #include "libtorrent/sha1_hash.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/time.hpp" #include "libtorrent/address.hpp" #include "libtorrent/torrent_info.hpp" #include "test.hpp" // for EXPORT #include using namespace libtorrent; struct EXPORT peer_conn { enum peer_mode_t { uploader, downloader, idle }; peer_conn(io_service& ios , boost::function on_msg , libtorrent::torrent_info const& ti , libtorrent::tcp::endpoint const& ep , peer_mode_t mode); void start_conn(); void on_connect(error_code const& ec); void on_handshake(char* h, error_code const& ec, size_t bytes_transferred); void on_handshake2(error_code const& ec, size_t bytes_transferred); void write_have_all(); void on_have_all_sent(error_code const& ec, size_t bytes_transferred); bool write_request(); void on_req_sent(char* m, error_code const& ec, size_t bytes_transferred); void close(char const* fmt, error_code const& ec); void work_download(); void on_msg_length(error_code const& ec, size_t bytes_transferred); void on_message(error_code const& ec, size_t bytes_transferred); bool verify_piece(int piece, int start, char const* ptr, int size); void write_piece(int piece, int start, int length); void write_have(int piece); void abort(); private: tcp::socket s; char write_buf_proto[100]; boost::uint32_t write_buffer[17*1024/4]; boost::uint32_t buffer[17*1024/4]; peer_mode_t m_mode; torrent_info const& m_ti; int read_pos; boost::function m_on_msg; enum state_t { handshaking, sending_request, receiving_message }; int state; std::vector pieces; std::vector suggested_pieces; std::vector allowed_fast; bool choked; int current_piece; // the piece we're currently requesting blocks from bool m_current_piece_is_allowed; int block; int const m_blocks_per_piece; int outstanding_requests; // if this is true, this connection is a seed bool fast_extension; int blocks_received; int blocks_sent; time_point start_time; time_point end_time; tcp::endpoint endpoint; bool restarting; }; #endif libtorrent-rasterbar-1.1.13/test/corrupt.gz000066400000000000000000000004501351156116000207610ustar00rootroot00000000000000‹ă`bbYu€`YYYYYY]řřřřřřřřřřřřřřřřřřřřřřřřřřřřřřřţ  Ë™ Y˙bb˙™ Y˙bb˙˙ĽĽ ———————————————YYYYYYY]řřřřřřřřřřřřřřřřřřřřřřřřřřřřřřřţ  Ë™ Y˙bb˙™ Y˙bb˙˙ĽĽ ———————————————YYYYYYYYú  Ë™ Y˙bb˙YYYYYYYYY€˙˙YYYYYYYú  Ë™ Y˙bb˙YYYYYYYYY€˙˙bbbd@öYYYYY ‹Dlibtorrent-rasterbar-1.1.13/test/dht_server.cpp000066400000000000000000000107041351156116000215750ustar00rootroot00000000000000/* Copyright (c) 2013, 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/thread.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/address.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/aux_/time.hpp" #include "dht_server.hpp" #include "test_utils.hpp" #include #include #include #if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM #include #endif using namespace libtorrent; struct dht_server { libtorrent::io_service m_ios; boost::detail::atomic_count m_dht_requests; udp::socket m_socket; int m_port; boost::shared_ptr m_thread; dht_server() : m_dht_requests(0) , m_socket(m_ios) , m_port(0) { error_code ec; m_socket.open(udp::v4(), ec); if (ec) { fprintf(stderr, "Error opening listen DHT socket: %s\n", ec.message().c_str()); return; } m_socket.bind(udp::endpoint(address_v4::any(), 0), ec); if (ec) { fprintf(stderr, "Error binding DHT socket to port 0: %s\n", ec.message().c_str()); return; } m_port = m_socket.local_endpoint(ec).port(); if (ec) { fprintf(stderr, "Error getting local endpoint of DHT socket: %s\n", ec.message().c_str()); return; } fprintf(stderr, "%s: DHT initialized on port %d\n", time_now_string(), m_port); m_thread.reset(new libtorrent::thread(boost::bind(&dht_server::thread_fun, this))); } ~dht_server() { m_socket.cancel(); m_socket.close(); if (m_thread) m_thread->join(); } int port() const { return m_port; } int num_hits() const { return m_dht_requests; } static void incoming_packet(error_code const& ec, size_t bytes_transferred, size_t *ret, error_code* error, bool* done) { *ret = bytes_transferred; *error = ec; *done = true; } void thread_fun() { char buffer[2000]; for (;;) { error_code ec; udp::endpoint from; size_t bytes_transferred; bool done = false; m_socket.async_receive_from( boost::asio::buffer(buffer, sizeof(buffer)), from, 0 , boost::bind(&incoming_packet, _1, _2, &bytes_transferred, &ec, &done)); while (!done) { m_ios.poll_one(); m_ios.reset(); } if (ec == boost::asio::error::operation_aborted || ec == boost::asio::error::bad_descriptor) return; if (ec) { fprintf(stderr, "Error receiving on DHT socket: %s\n", ec.message().c_str()); return; } try { entry msg = bdecode(buffer, buffer + bytes_transferred); #if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM std::cerr << msg << std::endl; #endif ++m_dht_requests; } catch (std::exception& e) { fprintf(stderr, "failed to decode DHT message: %s\n", e.what()); } } } }; boost::shared_ptr g_dht; int start_dht() { g_dht.reset(new dht_server); return g_dht->port(); } // the number of DHT messages received int num_dht_hits() { if (g_dht) return g_dht->num_hits(); return 0; } void stop_dht() { g_dht.reset(); } libtorrent-rasterbar-1.1.13/test/dht_server.hpp000066400000000000000000000032611351156116000216020ustar00rootroot00000000000000/* Copyright (c) 2013, 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 "test.hpp" // for EXPORT // returns the port the DHT is running on int EXPORT start_dht(); // the number of DHT messages received int EXPORT num_dht_hits(); void EXPORT stop_dht(); libtorrent-rasterbar-1.1.13/test/enum_if.cpp000066400000000000000000000061141351156116000210520ustar00rootroot00000000000000/* Copyright (c) 2008, 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 #include #include #include using namespace libtorrent; int main() { io_service ios; error_code ec; address def_gw = get_default_gateway(ios, ec); if (ec) { fprintf(stderr, "%s\n", ec.message().c_str()); return 1; } printf("Default gateway: %s\n", def_gw.to_string(ec).c_str()); printf("=========== Routes ===========\n"); std::vector routes = enum_routes(ios, ec); if (ec) { printf("%s\n", ec.message().c_str()); return 1; } printf("%-18s%-18s%-35s%-7sinterface\n", "destination", "network", "gateway", "mtu"); for (std::vector::const_iterator i = routes.begin() , end(routes.end()); i != end; ++i) { printf("%-18s%-18s%-35s%-7d%s\n" , i->destination.to_string(ec).c_str() , i->netmask.to_string(ec).c_str() , i->gateway.to_string(ec).c_str() , i->mtu , i->name); } printf("========= Interfaces =========\n"); std::vector const& net = enum_net_interfaces(ios, ec); if (ec) { printf("%s\n", ec.message().c_str()); return 1; } printf("%-30s%-45s%-20s%-8sflags\n", "address", "netmask", "name", "mtu"); for (std::vector::const_iterator i = net.begin() , end(net.end()); i != end; ++i) { printf("%-30s%-45s%-20s%-8d%s%s%s\n" , i->interface_address.to_string(ec).c_str() , i->netmask.to_string(ec).c_str() , i->name , i->mtu , (is_multicast(i->interface_address)?"multicast ":"") , (is_local(i->interface_address)?"local ":"") , (is_loopback(i->interface_address)?"loopback ":"") ); } } libtorrent-rasterbar-1.1.13/test/http.py000077500000000000000000000176751351156116000202760ustar00rootroot00000000000000# -*- coding: cp1252 -*- # # #Copyright (c) <2009> # #Permission is hereby granted, free of charge, to any person #obtaining a copy of this software and associated documentation #files (the "Software"), to deal in the Software without #restriction, including without limitation the rights to use, #copy, modify, merge, publish, distribute, sublicense, and/or sell #copies of the Software, and to permit persons to whom the #Software is furnished to do so, subject to the following #conditions: # #The above copyright notice and this permission notice shall be #included in all copies or substantial portions of the Software. # #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES #OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND #NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT #HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, #WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING #FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #OTHER DEALINGS IN THE SOFTWARE. """\ Copyright (c) <2009> ************************************** *** Python Proxy - A Fast HTTP proxy *** ************************************** Neste momento este proxy Ă© um Elie Proxy. Suporta os mĂ©todos HTTP: - OPTIONS; - GET; - HEAD; - POST; - PUT; - DELETE; - TRACE; - CONENCT. Suporta: - Conexões dos cliente em IPv4 ou IPv6; - Conexões ao alvo em IPv4 e IPv6; - Conexões todo o tipo de transmissĂŁo de dados TCP (CONNECT tunneling), p.e. ligações SSL, como Ă© o caso do HTTPS. A fazer: - Verificar se o input vindo do cliente está correcto; - Enviar os devidos HTTP erros se nĂŁo, ou simplesmente quebrar a ligação; - Criar um gestor de erros; - Criar ficheiro log de erros; - Colocar excepções nos sĂ­tios onde Ă© previsĂ­vel a ocorrĂŞncia de erros, p.e.sockets e ficheiros; - Rever tudo e melhorar a estrutura do programar e colocar nomes adequados nas variáveis e mĂ©todos; - Comentar o programa decentemente; - Doc Strings. Funcionalidades futuras: - Adiconar a funcionalidade de proxy anĂłnimo e transparente; - Suportar FTP?. (!) Atenção o que se segue sĂł tem efeito em conexões nĂŁo CONNECT, para estas o proxy Ă© sempre Elite. Qual a diferença entre um proxy Elite, AnĂłnimo e Transparente? - Um proxy elite Ă© totalmente anĂłnimo, o servidor que o recebe nĂŁo consegue ter conhecimento da existĂŞncia do proxy e nĂŁo recebe o endereço IP do cliente; - Quando Ă© usado um proxy anĂłnimo o servidor sabe que o cliente está a usar um proxy mas nĂŁo sabe o endereço IP do cliente; É enviado o cabeçalho HTTP "Proxy-agent". - Um proxy transparente fornece ao servidor o IP do cliente e um informação que se está a usar um proxy. SĂŁo enviados os cabeçalhos HTTP "Proxy-agent" e "HTTP_X_FORWARDED_FOR". """ import socket, thread, select, sys, base64, time, errno __version__ = '0.1.0 Draft 1' BUFLEN = 8192 VERSION = 'Python Proxy/'+__version__ HTTPVER = 'HTTP/1.1' username = None password = None class ConnectionHandler: def __init__(self, connection, address, timeout): self.client = connection self.client_buffer = '' self.timeout = timeout self.method, self.path, self.protocol = self.get_base_header() global username global password if username != None: auth = base64.b64encode(username + ':' + password) if not 'Proxy-Authorization: Basic ' + auth in self.client_buffer: print 'PROXY - failed authentication: %s' % self.client_buffer self.client.send(HTTPVER+' 401 Authentication Failed\n'+ 'Proxy-agent: %s\n\n'%VERSION) self.client.close() return try: if self.method == 'CONNECT': self.method_CONNECT() elif self.method in ('OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE'): self.method_others() except: try: self.client.send(HTTPVER+' 502 Connection failed\n'+ 'Proxy-agent: %s\n\n'%VERSION) except Exception, e: print 'PROXY - ', e self.client.close() return self.client.close() self.target.close() def get_base_header(self): retries = 0 while 1: try: self.client_buffer += self.client.recv(BUFLEN) except socket.error, e: err = e.args[0] if (err == errno.EAGAIN or err == errno.EWOULDBLOCK) and retries < 20: time.sleep(0.5) retries += 1 continue raise e end = self.client_buffer.find('\r\n\r\n') if end!=-1: break line_end = self.client_buffer.find('\n') print 'PROXY - %s' % self.client_buffer[:line_end]#debug data = (self.client_buffer[:line_end+1]).split() self.client_buffer = self.client_buffer[line_end+1:] return data def method_CONNECT(self): self._connect_target(self.path) self.client.send(HTTPVER+' 200 Connection established\n'+ 'Proxy-agent: %s\n\n'%VERSION) self.client_buffer = '' self._read_write() def method_others(self): self.path = self.path[7:] i = self.path.find('/') host = self.path[:i] path = self.path[i:] self._connect_target(host) self.target.send('%s %s %s\n' % (self.method, path, self.protocol)+ self.client_buffer) self.client_buffer = '' self._read_write() def _connect_target(self, host): i = host.find(':') if i!=-1: port = int(host[i+1:]) host = host[:i] else: port = 80 (soc_family, _, _, _, address) = socket.getaddrinfo(host, port)[0] self.target = socket.socket(soc_family) self.target.connect(address) def _read_write(self): time_out_max = self.timeout/3 socs = [self.client, self.target] count = 0 while 1: count += 1 (recv, _, error) = select.select(socs, [], socs, 3) if error: break if recv: for in_ in recv: data = in_.recv(BUFLEN) if in_ is self.client: out = self.target else: out = self.client if data: out.send(data) count = 0 if count == time_out_max: break def start_server(host='localhost', port=8080, IPv6=False, timeout=100, handler=ConnectionHandler): if IPv6==True: soc_type=socket.AF_INET6 else: soc_type=socket.AF_INET soc = socket.socket(soc_type) soc.settimeout(120) print "PROXY - Serving on %s:%d."%(host, port)#debug soc.bind((host, port)) soc.listen(0) while 1: thread.start_new_thread(handler, soc.accept()+(timeout,)) if __name__ == '__main__': listen_port = 8080 i = 1 while i < len(sys.argv): if sys.argv[i] == '--port': listen_port = int(sys.argv[i+1]) i += 1 elif sys.argv[i] == '--username': username = sys.argv[i+1] i += 1 elif sys.argv[i] == '--password': password = sys.argv[i+1] i += 1 else: if sys.argv[i] != '--help': print('PROXY - unknown option "%s"' % sys.argv[i]) print('usage: http.py [--port ]') sys.exit(1) i += 1 start_server(port=listen_port) libtorrent-rasterbar-1.1.13/test/main.cpp000066400000000000000000000315071351156116000203600ustar00rootroot00000000000000/* Copyright (c) 2008, 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 #include #include #include #include // for exit() #include "libtorrent/address.hpp" #include "libtorrent/socket.hpp" #include "setup_transfer.hpp" // for _g_test_failures #include "test.hpp" #include "dht_server.hpp" // for stop_dht #include "peer_server.hpp" // for stop_peer #include "udp_tracker.hpp" // for stop_udp_tracker #include "libtorrent/assert.hpp" #include "libtorrent/file.hpp" #include "libtorrent/aux_/escape_string.hpp" #include #ifdef WIN32 #include // fot SetErrorMode #include // for _dup and _dup2 #include // for _getpid #include #define dup _dup #define dup2 _dup2 #else #include // for getpid() #endif using namespace libtorrent; // these are global so we can restore them on abnormal exits and print stuff // out, such as the log int old_stdout = -1; int old_stderr = -1; bool redirect_stdout = true; bool redirect_stderr = true; bool keep_files = false; extern int _g_test_idx; // the current tests file descriptor unit_test_t* current_test = NULL; void output_test_log_to_terminal() { if (current_test == NULL || current_test->output == NULL) return; fflush(stdout); fflush(stderr); if (old_stdout != -1) { dup2(old_stdout, fileno(stdout)); old_stdout = -1; } if (old_stderr != -1) { dup2(old_stderr, fileno(stderr)); old_stderr = -1; } fseek(current_test->output, 0, SEEK_SET); fprintf(stdout, "\x1b[1m[%s]\x1b[0m\n\n", current_test->name); char buf[4096]; int size = 0; do { size = fread(buf, 1, sizeof(buf), current_test->output); if (size > 0) fwrite(buf, 1, size, stdout); } while (size > 0); } #ifdef _WIN32 LONG WINAPI seh_exception_handler(LPEXCEPTION_POINTERS p) { char stack_text[10000]; #if TORRENT_USE_ASSERTS \ || defined TORRENT_ASIO_DEBUGGING \ || defined TORRENT_PROFILE_CALLS \ || defined TORRENT_DEBUG_BUFFERS print_backtrace(stack_text, sizeof(stack_text), 30 , p->ContextRecord); #elif defined __FUNCTION__ strcat(stack_text, __FUNCTION__); #else stack_text[0] = 0; strcat(stack_text, ""); #endif int const code = p->ExceptionRecord->ExceptionCode; char const* name = ""; switch (code) { #define EXC(x) case x: name = #x; break EXC(EXCEPTION_ACCESS_VIOLATION); EXC(EXCEPTION_ARRAY_BOUNDS_EXCEEDED); EXC(EXCEPTION_BREAKPOINT); EXC(EXCEPTION_DATATYPE_MISALIGNMENT); EXC(EXCEPTION_FLT_DENORMAL_OPERAND); EXC(EXCEPTION_FLT_DIVIDE_BY_ZERO); EXC(EXCEPTION_FLT_INEXACT_RESULT); EXC(EXCEPTION_FLT_INVALID_OPERATION); EXC(EXCEPTION_FLT_OVERFLOW); EXC(EXCEPTION_FLT_STACK_CHECK); EXC(EXCEPTION_FLT_UNDERFLOW); EXC(EXCEPTION_ILLEGAL_INSTRUCTION); EXC(EXCEPTION_IN_PAGE_ERROR); EXC(EXCEPTION_INT_DIVIDE_BY_ZERO); EXC(EXCEPTION_INT_OVERFLOW); EXC(EXCEPTION_INVALID_DISPOSITION); EXC(EXCEPTION_NONCONTINUABLE_EXCEPTION); EXC(EXCEPTION_PRIV_INSTRUCTION); EXC(EXCEPTION_SINGLE_STEP); EXC(EXCEPTION_STACK_OVERFLOW); #undef EXC }; std::fprintf(stderr, "exception: (0x%x) %s caught:\n%s\n" , code, name, stack_text); output_test_log_to_terminal(); exit(code); } #else void sig_handler(int sig) { char stack_text[10000]; #if (defined TORRENT_DEBUG && TORRENT_USE_ASSERTS) \ || defined TORRENT_ASIO_DEBUGGING \ || defined TORRENT_PROFILE_CALLS \ || defined TORRENT_RELEASE_ASSERTS \ || defined TORRENT_DEBUG_BUFFERS print_backtrace(stack_text, sizeof(stack_text), 30); #elif defined __FUNCTION__ strcat(stack_text, __FUNCTION__); #else stack_text[0] = 0; strcat(stack_text, ""); #endif char const* sig_name = 0; switch (sig) { #define SIG(x) case x: sig_name = #x; break SIG(SIGSEGV); #ifdef SIGBUS SIG(SIGBUS); #endif SIG(SIGINT); SIG(SIGTERM); SIG(SIGILL); SIG(SIGABRT); SIG(SIGFPE); #ifdef SIGSYS SIG(SIGSYS); #endif #undef SIG }; fprintf(stderr, "signal: %s caught:\n%s\n", sig_name, stack_text); output_test_log_to_terminal(); #ifdef WIN32 exit(sig); #else exit(128 + sig); #endif } #endif // _WIN32 void print_usage(char const* executable) { printf("%s [options] [tests...]\n" "\n" "OPTIONS:\n" "-h,--help show this help\n" "-l,--list list the tests available to run\n" "-k,--keep keep files created by the test\n" " regardless of whether it passed or not\n" "-n,--no-redirect don't redirect test output to\n" " temporary file, but let it go straight\n" " to stdout\n" "--no-stderr-redirect don't redirect stderr, but still redirect\n" " stdout. This is useful when building with\n" " sanitizers, which rely on being able to print\n" " to stderr and exit\n" "\n" "for tests, specify one or more test names as printed\n" "by -l. If no test is specified, all tests are run\n", executable); } void change_directory(std::string const& f, error_code& ec) { ec.clear(); #ifdef TORRENT_WINDOWS #if TORRENT_USE_WSTRING #define SetCurrentDirectory_ SetCurrentDirectoryW std::wstring n = convert_to_wstring(f); #else #define SetCurrentDirectory_ SetCurrentDirectoryA std::string const& n = convert_to_native(f); #endif // TORRENT_USE_WSTRING if (SetCurrentDirectory_(n.c_str()) == 0) ec.assign(GetLastError(), system_category()); #else std::string n = convert_to_native(f); int ret = ::chdir(n.c_str()); if (ret != 0) ec.assign(errno, system_category()); #endif } struct unit_directory_guard { std::string dir; unit_directory_guard(std::string const& d) : dir(d) {} ~unit_directory_guard() { error_code ec; std::string parent_dir = parent_path(dir); change_directory(parent_dir, ec); // windows will not allow to remove current dir, so let's change it to root if (ec) { TEST_ERROR("Failed to change directory: " + ec.message()); return; } if (!keep_files) { error_code ec; remove_all(dir, ec); if (ec) TEST_ERROR("Failed to remove unit test directory: " + ec.message()); } } }; EXPORT int main(int argc, char const* argv[]) { char const* executable = argv[0]; // skip executable name ++argv; --argc; // pick up options while (argc > 0 && argv[0][0] == '-') { if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) { print_usage(executable); return 0; } if (strcmp(argv[0], "-l") == 0 || strcmp(argv[0], "--list") == 0) { printf("TESTS:\n"); for (int i = 0; i < _g_num_unit_tests; ++i) { printf(" - %s\n", _g_unit_tests[i].name); } return 0; } if (strcmp(argv[0], "-n") == 0 || strcmp(argv[0], "--no-redirect") == 0) { redirect_stdout = false; redirect_stderr = false; } if (strcmp(argv[0], "--no-stderr-redirect") == 0) { redirect_stderr = false; } if (strcmp(argv[0], "-k") == 0 || strcmp(argv[0], "--keep") == 0) { keep_files = true; } ++argv; --argc; } std::set tests_to_run; bool filter = false; for (int i = 0; i < argc; ++i) { tests_to_run.insert(argv[i]); filter = true; } #ifdef O_NONBLOCK // on darwin, stdout is set to non-blocking mode by default // which sometimes causes tests to fail with EAGAIN just // by printing logs int flags = fcntl(fileno(stdout), F_GETFL, 0); fcntl(fileno(stdout), F_SETFL, flags & ~O_NONBLOCK); flags = fcntl(fileno(stderr), F_GETFL, 0); fcntl(fileno(stderr), F_SETFL, flags & ~O_NONBLOCK); #endif #ifdef WIN32 // try to suppress hanging the process by windows displaying // modal dialogs. SetErrorMode( SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); SetUnhandledExceptionFilter(&seh_exception_handler); #ifdef _DEBUG _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); #endif #else signal(SIGSEGV, &sig_handler); #ifdef SIGBUS signal(SIGBUS, &sig_handler); #endif signal(SIGILL, &sig_handler); signal(SIGINT, &sig_handler); signal(SIGABRT, &sig_handler); signal(SIGFPE, &sig_handler); #ifdef SIGSYS signal(SIGSYS, &sig_handler); #endif #endif // WIN32 int process_id = -1; #ifdef _WIN32 process_id = _getpid(); #else process_id = getpid(); #endif std::string root_dir = current_working_directory(); char dir[40]; snprintf(dir, sizeof(dir), "test_tmp_%u", process_id); std::string unit_dir_prefix = combine_path(root_dir, dir); std::printf("cwd_prefix = \"%s\"\n", unit_dir_prefix.c_str()); if (_g_num_unit_tests == 0) { fprintf(stderr, "\x1b[31mERROR: no unit tests registered\x1b[0m\n"); return 1; } if (redirect_stdout) old_stdout = dup(fileno(stdout)); if (redirect_stderr) old_stderr = dup(fileno(stderr)); int num_run = 0; for (int i = 0; i < _g_num_unit_tests; ++i) { if (filter && tests_to_run.count(_g_unit_tests[i].name) == 0) continue; std::string unit_dir = unit_dir_prefix; char i_str[40]; snprintf(i_str, sizeof(i_str), "%u", i); unit_dir.append(i_str); error_code ec; create_directory(unit_dir, ec); if (ec) { std::printf("Failed to create unit test directory: %s\n", ec.message().c_str()); return 1; } unit_directory_guard unit_dir_guard(unit_dir); change_directory(unit_dir, ec); if (ec) { std::printf("Failed to change unit test directory: %s\n", ec.message().c_str()); return 1; } unit_test_t& t = _g_unit_tests[i]; if (redirect_stdout) { // redirect test output to a temporary file fflush(stdout); fflush(stderr); FILE* f = tmpfile(); if (f != NULL) { int ret1 = dup2(fileno(f), fileno(stdout)); if (redirect_stderr) dup2(fileno(f), fileno(stderr)); if (ret1 >= 0) { t.output = f; } else { fprintf(stderr, "failed to redirect output: (%d) %s\n" , errno, strerror(errno)); } } else { fprintf(stderr, "failed to create temporary file for redirecting " "output: (%d) %s\n", errno, strerror(errno)); } } // get proper interleaving of stderr and stdout setbuf(stdout, NULL); setbuf(stderr, NULL); _g_test_idx = i; current_test = &t; #ifndef BOOST_NO_EXCEPTIONS try { #endif _g_test_failures = 0; (*t.fun)(); #ifndef BOOST_NO_EXCEPTIONS } catch (std::exception const& e) { char buf[200]; snprintf(buf, sizeof(buf), "Terminated with exception: \"%s\"", e.what()); report_failure(buf, __FILE__, __LINE__); } catch (...) { report_failure("Terminated with unknown exception", __FILE__, __LINE__); } #endif if (!tests_to_run.empty()) tests_to_run.erase(t.name); if (_g_test_failures > 0) { output_test_log_to_terminal(); } t.num_failures = _g_test_failures; t.run = true; ++num_run; if (redirect_stdout && t.output) fclose(t.output); } if (redirect_stdout && old_stdout != -1) dup2(old_stdout, fileno(stdout)); if (redirect_stderr && old_stderr != -1) dup2(old_stderr, fileno(stderr)); if (!tests_to_run.empty()) { fprintf(stderr, "\x1b[1mUNKONWN tests:\x1b[0m\n"); for (std::set::iterator i = tests_to_run.begin() , end(tests_to_run.end()); i != end; ++i) { fprintf(stderr, " %s\n", i->c_str()); } } if (num_run == 0) { fprintf(stderr, "\x1b[31mERROR: no unit tests run\x1b[0m\n"); return 1; } // just in case of premature exits // make sure we try to clean up some stop_udp_tracker(); stop_all_proxies(); stop_web_server(); stop_peer(); stop_dht(); if (redirect_stdout) fflush(stdout); if (redirect_stderr) fflush(stderr); int total_num_failures = print_failures(); return total_num_failures ? 333 : 0; } libtorrent-rasterbar-1.1.13/test/make_torrent.cpp000066400000000000000000000127371351156116000221320ustar00rootroot00000000000000/* Copyright (c) 2016, 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 #include #include "make_torrent.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/file_pool.hpp" #include "libtorrent/storage_defs.hpp" using namespace libtorrent; boost::shared_ptr make_test_torrent( torrent_args const& args) { entry e; entry::dictionary_type& info = e["info"].dict(); int total_size = 0; if (args.m_priv) { info["priv"] = 1; } // torrent offset ranges where the pad files are // used when generating hashes std::deque > pad_files; int const piece_length = 32768; info["piece length"] = piece_length; if (args.m_files.size() == 1) { std::string const& ent = args.m_files[0]; std::string name = "test_file-1"; if (ent.find("name=") != std::string::npos) { int pos = ent.find("name=") + 5; name = ent.substr(pos, ent.find(',', pos)); } info["name"] = name; int file_size = atoi(args.m_files[0].c_str()); info["length"] = file_size; total_size = file_size; } else { info["name"] = args.m_name; entry::list_type& files = info["files"].list(); for (int i = 0; i < int(args.m_files.size()); ++i) { int file_size = atoi(args.m_files[i].c_str()); files.push_back(entry()); entry::dictionary_type& file_entry = files.back().dict(); std::string const& ent = args.m_files[i]; if (ent.find("padfile") != std::string::npos) { file_entry["attr"].string() += "p"; pad_files.push_back(std::make_pair(total_size, total_size + file_size)); } if (ent.find("executable") != std::string::npos) file_entry["attr"].string() += "x"; char filename[100]; snprintf(filename, sizeof(filename), "test_file-%d", i); std::string name = filename; if (ent.find("name=") != std::string::npos) { int pos = ent.find("name=") + 5; name = ent.substr(pos, ent.find(',', pos)); } file_entry["path"].list().push_back(name); file_entry["length"] = file_size; total_size += file_size; } } if (!args.m_url_seed.empty()) { e["url-list"] = args.m_url_seed; } if (!args.m_http_seed.empty()) { e["httpseeds"] = args.m_http_seed; } std::string piece_hashes; int num_pieces = (total_size + piece_length - 1) / piece_length; int torrent_offset = 0; for (int i = 0; i < num_pieces; ++i) { hasher h; int const piece_size = (i < num_pieces - 1) ? piece_length : total_size - (num_pieces - 1) * piece_length; char const data = i; char const zero = 0; for (int o = 0; o < piece_size; ++o, ++torrent_offset) { while (!pad_files.empty() && torrent_offset >= pad_files.front().second) pad_files.pop_front(); if (!pad_files.empty() && torrent_offset >= pad_files.front().first) { h.update(&zero, 1); } else { h.update(&data, 1); } } piece_hashes += h.final().to_string(); } info["pieces"] = piece_hashes; std::vector tmp; std::back_insert_iterator > out(tmp); bencode(out, e); FILE* f = fopen("test.torrent", "w+"); fwrite(&tmp[0], 1, tmp.size(), f); fclose(f); return boost::make_shared(&tmp[0], tmp.size()); } void generate_files(libtorrent::torrent_info const& ti, std::string const& path , bool alternate_data) { file_pool fp; storage_params params; params.files = &ti.files(); params.path = path; params.pool = &fp; default_storage st(params); int const num_pieces = ti.num_pieces(); std::vector buffer; for (int i = 0; i < num_pieces; ++i) { int const piece_size = ti.piece_size(i); buffer.resize(ti.piece_length()); boost::uint8_t const data = alternate_data ? 255 - i : i; for (int o = 0; o < piece_size; ++o) { memcpy(&buffer[o], &data, 1); } file::iovec_t b = { &buffer[0], size_t(piece_size) }; storage_error ec; int ret = st.writev(&b, 1, i, 0, 0, ec); if (ret != piece_size || ec) { fprintf(stderr, "ERROR writing files: (%d expected %d) %s\n" , ret, piece_size, ec.ec.message().c_str()); } } } libtorrent-rasterbar-1.1.13/test/make_torrent.hpp000066400000000000000000000046611351156116000221340ustar00rootroot00000000000000/* Copyright (c) 2016, 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 MAKE_TORRENT_HPP #define MAKE_TORRENT_HPP #include "libtorrent/torrent_info.hpp" #include #include #include #include "test.hpp" enum flags_t { private_torrent = 1 }; struct torrent_args { torrent_args() : m_priv(false) {} torrent_args& name(char const* n) { m_name = n; return *this; } torrent_args& file(char const* f) { m_files.push_back(f); return *this; } torrent_args& url_seed(char const* u) { m_url_seed = u; return *this; } torrent_args& http_seed(char const* u) { m_http_seed = u; return *this; } torrent_args& priv() { m_priv = true; return *this; } bool m_priv; std::string m_name; std::vector m_files; std::string m_url_seed; std::string m_http_seed; }; EXPORT boost::shared_ptr make_test_torrent(torrent_args const& args); EXPORT void generate_files(libtorrent::torrent_info const& ti, std::string const& path, bool random = false); #endif libtorrent-rasterbar-1.1.13/test/mutable_test_torrents/000077500000000000000000000000001351156116000233525ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/test/mutable_test_torrents/test1.torrent000066400000000000000000000005561351156116000260370ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1419452992e4:infod5:filesld6:lengthi51200e4:pathl1:aeed6:lengthi18e4:pathl1:beed6:lengthi19e4:pathl1:ceed6:lengthi53248e4:pathl1:deee4:name5:test112:piece lengthi16384e6:pieces140:ČÄÔ}„Ç_ML¤)ŘŻym$„¶ ŢźĎk}@űŃŤąbĆŹ®¸ßňüťÝ ‡=ĺćâvw‰ŤŻQ\Ż\t˙r§źsx Ź^ńzň57¸F¬ëW`žnřryܱt§VČŰŔÔBôo­HQŹÝk¦Ą•ÄĐ‚Ü÷—˝UçôŔ6¤Q ńkŞ•kŘŕ t˘eelibtorrent-rasterbar-1.1.13/test/mutable_test_torrents/test1_pad_files.torrent000066400000000000000000000010101351156116000300270ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1419490165e4:infod5:filesld6:lengthi53248e4:pathl1:deed4:attr1:p6:lengthi12288e4:pathl17:.____padding_file1:0eed6:lengthi51200e4:pathl1:aeed4:attr1:p6:lengthi14336e4:pathl17:.____padding_file1:1eed6:lengthi19e4:pathl1:ceed6:lengthi18e4:pathl1:beee4:name5:test112:piece lengthi16384e6:pieces180:1QěęÄô«5đOÓŰ»ĺĚ@…´îÍÜMsęt0üz€ť«F‹؉9™V{0Š“]yĄĹrŢöv¤c>F†Ú×O˝~zmŔNČÄÔ}„Ç_ML¤)ŘŻym$„¶ ŢźĎk}@űŃŤąbĆŹ®¸ßňüťÝ ‡=ĺćâvw‰ŤŻQ“ âF?ó{¶ôť+,/߼Gć:Ąk†të´Ű‰Ű±geeelibtorrent-rasterbar-1.1.13/test/mutable_test_torrents/test1_single.torrent000066400000000000000000000003111351156116000273650ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1419490700e4:infod6:lengthi51200e4:name1:a12:piece lengthi16384e6:pieces80:ČÄÔ}„Ç_ML¤)ŘŻym$„¶ ŢźĎk}@űŃŤąbĆŹ®¸ßňüťÝ ‡=ĺćâvw‰ŤŻQ¦(R!Ż+O0^+w÷I•Çeelibtorrent-rasterbar-1.1.13/test/mutable_test_torrents/test1_single_padded.torrent000066400000000000000000000003111351156116000306660ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1419490711e4:infod6:lengthi51200e4:name1:a12:piece lengthi16384e6:pieces80:ČÄÔ}„Ç_ML¤)ŘŻym$„¶ ŢźĎk}@űŃŤąbĆŹ®¸ßňüťÝ ‡=ĺćâvw‰ŤŻQ“ âF?ó{¶ôť+,/߼Geelibtorrent-rasterbar-1.1.13/test/mutable_test_torrents/test2.torrent000066400000000000000000000005321351156116000260320ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1419452987e4:infod5:filesld6:lengthi18e4:pathl1:aeed6:lengthi51200e4:pathl1:beed6:lengthi19e4:pathl1:ceed6:lengthi46080e4:pathl1:deee4:name5:test212:piece lengthi16384e6:pieces120:Ή!utŠ Ë%Ëť0˝ĽŁ˝ÍîiŚÇŘ/%ÄáżO¸N´\´<< ú,3J«~ĘŰ,tS’žĘ Äě…@EÜi»›ú˝ÁŻQŹôו…™7GFÖě®¶yę-I›ĺ«ÇżRbÁ0ňđgďŕ’hXąľâĆBVD8ô-..45k×qÝDÄ- fH„óě„”ýŽ†ĺ¸’KyŹűń€Ş A•,ő@¦¤f0kHOć:Ąk†të´Ű‰Ű±geeelibtorrent-rasterbar-1.1.13/test/peer_server.cpp000066400000000000000000000105251351156116000217520ustar00rootroot00000000000000/* Copyright (c) 2013, 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/thread.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/address.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/aux_/time.hpp" #include "libtorrent/io_service.hpp" #include "peer_server.hpp" #include "test_utils.hpp" #include #include #include using namespace libtorrent; struct peer_server { libtorrent::io_service m_ios; boost::detail::atomic_count m_peer_requests; tcp::acceptor m_acceptor; int m_port; boost::shared_ptr m_thread; peer_server() : m_peer_requests(0) , m_acceptor(m_ios) , m_port(0) { error_code ec; m_acceptor.open(tcp::v4(), ec); if (ec) { fprintf(stderr, "PEER Error opening peer listen socket: %s\n", ec.message().c_str()); return; } m_acceptor.bind(tcp::endpoint(address_v4::any(), 0), ec); if (ec) { fprintf(stderr, "PEER Error binding peer socket to port 0: %s\n", ec.message().c_str()); return; } m_port = m_acceptor.local_endpoint(ec).port(); if (ec) { fprintf(stderr, "PEER Error getting local endpoint of peer socket: %s\n", ec.message().c_str()); return; } m_acceptor.listen(10, ec); if (ec) { fprintf(stderr, "PEER Error listening on peer socket: %s\n", ec.message().c_str()); return; } fprintf(stderr, "%s: PEER peer initialized on port %d\n", time_now_string(), m_port); m_thread.reset(new libtorrent::thread(boost::bind(&peer_server::thread_fun, this))); } ~peer_server() { error_code ignore; m_acceptor.cancel(ignore); m_acceptor.close(ignore); if (m_thread) m_thread->join(); } int port() const { return m_port; } int num_hits() const { return m_peer_requests; } static void new_connection(error_code const& ec, error_code* ret, bool* done) { *ret = ec; *done = true; } void thread_fun() { for (;;) { error_code ec; tcp::endpoint from; tcp::socket socket(m_ios); condition_variable cond; bool done = false; m_acceptor.async_accept(socket, from, boost::bind(&new_connection, _1, &ec, &done)); while (!done) { m_ios.poll_one(); m_ios.reset(); } if (ec == boost::asio::error::operation_aborted || ec == boost::asio::error::bad_descriptor) return; if (ec) { fprintf(stderr, "PEER Error accepting connection on peer socket: %s\n", ec.message().c_str()); return; } fprintf(stderr, "%s: PEER incoming peer connection\n", time_now_string()); ++m_peer_requests; socket.close(ec); } } }; boost::shared_ptr g_peer; int start_peer() { g_peer.reset(new peer_server); return g_peer->port(); } // the number of DHT messages received int num_peer_hits() { if (g_peer) return g_peer->num_hits(); return 0; } void stop_peer() { g_peer.reset(); } libtorrent-rasterbar-1.1.13/test/peer_server.hpp000066400000000000000000000033721351156116000217610ustar00rootroot00000000000000/* Copyright (c) 2013, 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 PEER_SERVER_HPP #define PEER_SERVER_HPP #include "test.hpp" // for EXPORT // returns the port the peer is running on EXPORT int start_peer(); // the number of incoming connections to this peer EXPORT int num_peer_hits(); EXPORT void stop_peer(); #endif libtorrent-rasterbar-1.1.13/test/print_alerts.cpp000066400000000000000000000050451351156116000221400ustar00rootroot00000000000000/* Copyright (c) 2016, 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 "print_alerts.hpp" #include "libtorrent/time.hpp" #include "libtorrent/session.hpp" #include "libtorrent/alert_types.hpp" #include "print_alerts.hpp" void print_alerts(libtorrent::session* ses, libtorrent::time_point start_time) { using namespace libtorrent; namespace lt = libtorrent; if (ses == NULL) return; std::vector alerts; ses->pop_alerts(&alerts); for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { alert* a = *i; #ifndef TORRENT_DISABLE_LOGGING if (peer_log_alert* pla = alert_cast(a)) { // in order to keep down the amount of logging, just log actual peer // messages if (pla->direction != peer_log_alert::incoming_message && pla->direction != peer_log_alert::outgoing_message) { continue; } } #endif lt::time_duration d = a->timestamp() - start_time; boost::uint32_t millis = lt::duration_cast(d).count(); printf("%4d.%03d: %-25s %s\n", millis / 1000, millis % 1000 , a->what() , a->message().c_str()); } } libtorrent-rasterbar-1.1.13/test/print_alerts.hpp000066400000000000000000000033461351156116000221470ustar00rootroot00000000000000/* Copyright (c) 2016, 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 PRINT_ALERTS_HPP #define PRINT_ALERTS_HPP #include "libtorrent/time.hpp" #include "libtorrent/session.hpp" #include "test.hpp" // for EXPORT EXPORT void print_alerts(libtorrent::session* ses, libtorrent::time_point start_time); #endif libtorrent-rasterbar-1.1.13/test/settings.cpp000066400000000000000000000063441351156116000212750ustar00rootroot00000000000000/* Copyright (c) 2015, 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/settings_pack.hpp" #include "libtorrent/alert.hpp" #include "settings.hpp" using namespace libtorrent; libtorrent::settings_pack settings() { const int mask = alert::error_notification | alert::peer_notification | alert::port_mapping_notification | alert::storage_notification | alert::tracker_notification | alert::debug_notification | alert::status_notification | alert::ip_block_notification | alert::dht_notification | alert::session_log_notification | alert::torrent_log_notification | alert::peer_log_notification | alert::incoming_request_notification | alert::dht_log_notification | alert::dht_operation_notification | alert::port_mapping_log_notification | alert::file_progress_notification | alert::piece_progress_notification; settings_pack pack; pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_dht, false); pack.set_str(settings_pack::dht_bootstrap_nodes, ""); pack.set_bool(settings_pack::prefer_rc4, false); pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled); pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled); pack.set_int(settings_pack::allowed_enc_level, settings_pack::pe_both); pack.set_int(settings_pack::alert_mask, mask); #ifndef TORRENT_BUILD_SIMULATOR pack.set_bool(settings_pack::allow_multiple_connections_per_ip, true); #else // we use 0 threads (disk I/O operations will be performed in the network // thread) to be simulator friendly. pack.set_int(settings_pack::aio_threads, 0); #endif #ifndef TORRENT_NO_DEPRECATE pack.set_int(settings_pack::half_open_limit, 1); #endif return pack; } libtorrent-rasterbar-1.1.13/test/settings.hpp000066400000000000000000000031321351156116000212720ustar00rootroot00000000000000/* Copyright (c) 2015, 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/settings_pack.hpp" #include "test.hpp" libtorrent::settings_pack EXPORT settings(); libtorrent-rasterbar-1.1.13/test/setup_transfer.cpp000066400000000000000000000606271351156116000225050ustar00rootroot00000000000000/* Copyright (c) 2008, 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 #include #include #include "libtorrent/session.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/http_parser.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/socket_io.hpp" // print_endpoint #include "libtorrent/ip_filter.hpp" #include "libtorrent/session_stats.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/random.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/broadcast_socket.hpp" // for supports_ipv6() #include #include #include #include #include "test.hpp" #include "test_utils.hpp" #include "setup_transfer.hpp" #ifndef _WIN32 #include #include #endif #define DEBUG_WEB_SERVER 0 #define DLOG if (DEBUG_WEB_SERVER) fprintf using namespace libtorrent; namespace lt = libtorrent; #if defined TORRENT_WINDOWS #include #endif boost::shared_ptr generate_torrent() { file_storage fs; fs.add_file("test/tmp1", 128 * 1024 * 8); fs.add_file("test/tmp2", 128 * 1024); fs.add_file("test/tmp3", 128 * 1024); lt::create_torrent t(fs, 128 * 1024, 6); t.add_tracker("http://torrent_file_tracker.com/announce"); t.add_url_seed("http://torrent_file_url_seed.com/"); int num = t.num_pieces(); TEST_CHECK(num > 0); for (int i = 0; i < num; ++i) { sha1_hash ph; for (int k = 0; k < 20; ++k) ph[k] = lt::random(); t.set_hash(i, ph); } std::vector buf; bencode(std::back_inserter(buf), t.generate()); return boost::make_shared(&buf[0], buf.size()); } boost::uint32_t g_addr = 0x92343023; void init_rand_address() { g_addr = 0x92343023; } address rand_v4() { address_v4 ret; do { g_addr += 0x3080ca; ret = address_v4(g_addr); } while (is_any(ret) || is_local(ret) || is_loopback(ret)); return ret; } sha1_hash rand_hash() { sha1_hash ret; for (int i = 0; i < 20; ++i) ret[i] = lt::random(); return ret; } #if TORRENT_USE_IPV6 address rand_v6() { address_v6::bytes_type bytes; for (int i = 0; i < int(bytes.size()); ++i) bytes[i] = rand(); return address_v6(bytes); } #endif static boost::uint16_t g_port = 0; tcp::endpoint rand_tcp_ep() { // make sure we don't procude the same "random" port twice g_port = (g_port + 1) % 14038; return tcp::endpoint(rand_v4(), g_port + 1024); } udp::endpoint rand_udp_ep() { g_port = (g_port + 1) % 14037; return udp::endpoint(rand_v4(), g_port + 1024); } std::map get_counters(libtorrent::session& s) { using namespace libtorrent; s.post_session_stats(); std::map ret; alert const* a = wait_for_alert(s, session_stats_alert::alert_type , "get_counters()"); TEST_CHECK(a); if (!a) return ret; session_stats_alert const* sa = alert_cast(a); if (!sa) return ret; static std::vector metrics = session_stats_metrics(); for (int i = 0; i < int(metrics.size()); ++i) ret[metrics[i].name] = sa->values[metrics[i].value_index]; return ret; } alert const* wait_for_alert(lt::session& ses, int type, char const* name, int num , lt::time_duration timeout) { time_point end = libtorrent::clock_type::now() + timeout; while (true) { time_point now = clock_type::now(); if (now > end) return NULL; alert const* ret = NULL; ses.wait_for_alert(end - now); std::vector alerts; ses.pop_alerts(&alerts); for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { fprintf(stdout, "%s: %s: [%s] %s\n", time_now_string(), name , (*i)->what(), (*i)->message().c_str()); if ((*i)->type() == type) { ret = *i; --num; } } if (num <= 0) return ret; } return NULL; } int load_file(std::string const& filename, std::vector& v, libtorrent::error_code& ec, int limit) { ec.clear(); FILE* f = fopen(filename.c_str(), "rb"); if (f == NULL) { ec.assign(errno, boost::system::system_category()); return -1; } int r = fseek(f, 0, SEEK_END); if (r != 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } long s = ftell(f); if (s < 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } if (s > limit) { fclose(f); return -2; } r = fseek(f, 0, SEEK_SET); if (r != 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } v.resize(s); if (s == 0) { fclose(f); return 0; } r = fread(&v[0], 1, v.size(), f); if (r < 0) { ec.assign(errno, boost::system::system_category()); fclose(f); return -1; } fclose(f); if (r != s) return -3; return 0; } void save_file(char const* filename, char const* data, int size) { error_code ec; file out(filename, file::write_only, ec); TEST_CHECK(!ec); if (ec) { fprintf(stderr, "ERROR opening file '%s': %s\n", filename, ec.message().c_str()); return; } file::iovec_t b = { (void*)data, size_t(size) }; out.writev(0, &b, 1, ec); TEST_CHECK(!ec); if (ec) { fprintf(stderr, "ERROR writing file '%s': %s\n", filename, ec.message().c_str()); return; } } bool print_alerts(lt::session& ses, char const* name , bool allow_disconnects, bool allow_no_torrents, bool allow_failed_fastresume , boost::function const& predicate, bool no_output) { TEST_CHECK(!ses.get_torrents().empty() || allow_no_torrents); std::vector alerts; ses.pop_alerts(&alerts); for (std::vector::iterator i = alerts.begin(); i != alerts.end(); ++i) { if (peer_disconnected_alert const* p = alert_cast(*i)) { fprintf(stdout, "%s: %s: [%s] (%s): %s\n", time_now_string(), name, (*i)->what(), print_endpoint(p->ip).c_str(), p->message().c_str()); } else if ((*i)->type() == invalid_request_alert::alert_type) { fprintf(stdout, "peer error: %s\n", (*i)->message().c_str()); TEST_CHECK(false); } else if ((*i)->type() == fastresume_rejected_alert::alert_type) { fprintf(stdout, "resume data error: %s\n", (*i)->message().c_str()); TEST_CHECK(allow_failed_fastresume); } else if (!no_output && (*i)->type() != block_downloading_alert::alert_type && (*i)->type() != block_finished_alert::alert_type && (*i)->type() != piece_finished_alert::alert_type) { fprintf(stdout, "%s: %s: [%s] %s\n", time_now_string(), name, (*i)->what(), (*i)->message().c_str()); } /* peer_error_alert const* pea = alert_cast(*i); if (pea) { fprintf(stdout, "%s: peer error: %s\n", time_now_string(), pea->error.message().c_str()); TEST_CHECK((!handles.empty() && h.status().is_seeding) || pea->error.message() == "connecting to peer" || pea->error.message() == "closing connection to ourself" || pea->error.message() == "duplicate connection" || pea->error.message() == "duplicate peer-id" || pea->error.message() == "upload to upload connection" || pea->error.message() == "stopping torrent" || (allow_disconnects && pea->error.message() == "Broken pipe") || (allow_disconnects && pea->error.message() == "Connection reset by peer") || (allow_disconnects && pea->error.message() == "no shared cipher") || (allow_disconnects && pea->error.message() == "End of file.")); } */ } return predicate && boost::algorithm::any_of(alerts.begin(), alerts.end(), predicate); } bool listen_alert(libtorrent::alert const* a) { return alert_cast(a) || alert_cast(a); } void wait_for_listen(lt::session& ses, char const* name) { alert const* a = 0; bool listen_done = false; do { listen_done = print_alerts(ses, name, true, true, true, listen_alert, false); if (listen_done) break; a = ses.wait_for_alert(milliseconds(500)); } while (a); // we din't receive a listen alert! TEST_CHECK(listen_done); } bool downloading_alert(libtorrent::alert const* a) { state_changed_alert const* sc = alert_cast(a); return sc && sc->state == torrent_status::downloading; } void wait_for_downloading(lt::session& ses, char const* name) { time_point const start = clock_type::now(); bool downloading_done = false; alert const* a = 0; do { downloading_done = print_alerts(ses, name, true, true, true, downloading_alert, false); if (downloading_done) break; if (total_seconds(clock_type::now() - start) > 10) break; a = ses.wait_for_alert(seconds(2)); } while (a); if (!downloading_done) { fprintf(stdout, "%s: did not receive a state_changed_alert indicating " "the torrent is downloading. waited: %d ms\n" , name, int(total_milliseconds(clock_type::now() - start))); } } void print_ses_rate(float time , libtorrent::torrent_status const* st1 , libtorrent::torrent_status const* st2 , libtorrent::torrent_status const* st3) { if (st1) { fprintf(stdout, "%3.1fs | %dkB/s %dkB/s %d%% %d cc:%d%s", time , int(st1->download_payload_rate / 1000) , int(st1->upload_payload_rate / 1000) , int(st1->progress * 100) , st1->num_peers , st1->connect_candidates , st1->errc ? (" [" + st1->errc.message() + "]").c_str() : ""); } if (st2) fprintf(stdout, " : %3.1fs | %dkB/s %dkB/s %d%% %d cc:%d%s", time , int(st2->download_payload_rate / 1000) , int(st2->upload_payload_rate / 1000) , int(st2->progress * 100) , st2->num_peers , st2->connect_candidates , st2->errc ? (" [" + st1->errc.message() + "]").c_str() : ""); if (st3) fprintf(stdout, " : %3.1fs | %dkB/s %dkB/s %d%% %d cc:%d%s", time , int(st3->download_payload_rate / 1000) , int(st3->upload_payload_rate / 1000) , int(st3->progress * 100) , st3->num_peers , st3->connect_candidates , st3->errc ? (" [" + st1->errc.message() + "]").c_str() : ""); fprintf(stdout, "\n"); } void test_sleep(int milliseconds) { #if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN Sleep(milliseconds); #elif defined TORRENT_BEOS snooze_until(system_time() + boost::int64_t(milliseconds) * 1000, B_SYSTEM_TIMEBASE); #else usleep(milliseconds * 1000); #endif } #ifdef _WIN32 typedef DWORD pid_type; #else typedef pid_t pid_type; #endif struct proxy_t { pid_type pid; int type; }; // maps port to proxy type static std::map running_proxies; // returns 0 on failure, otherwise pid pid_type async_run(char const* cmdline) { #ifdef _WIN32 char buf[2048]; snprintf(buf, sizeof(buf), "%s", cmdline); PROCESS_INFORMATION pi; STARTUPINFOA startup; memset(&startup, 0, sizeof(startup)); startup.cb = sizeof(startup); startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE); startup.hStdOutput= GetStdHandle(STD_OUTPUT_HANDLE); startup.hStdError = GetStdHandle(STD_INPUT_HANDLE); int ret = CreateProcessA(NULL, buf, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &startup, &pi); if (ret == 0) { int error = GetLastError(); fprintf(stdout, "failed (%d) %s\n", error, error_code(error, system_category()).message().c_str()); return 0; } return pi.dwProcessId; #else pid_type p; char arg_storage[4096]; char* argp = arg_storage; std::vector argv; argv.push_back(argp); for (char const* in = cmdline; *in != '\0'; ++in) { if (*in != ' ') { *argp++ = *in; continue; } *argp++ = '\0'; argv.push_back(argp); } *argp = '\0'; argv.push_back(NULL); int ret = posix_spawnp(&p, argv[0], NULL, NULL, &argv[0], NULL); if (ret != 0) { fprintf(stderr, "failed (%d) %s\n", errno, strerror(errno)); return 0; } return p; #endif } void stop_process(pid_type p) { #ifdef _WIN32 HANDLE proc = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, p); if (proc == NULL) return; TerminateProcess(proc, 138); WaitForSingleObject(proc, 5000); CloseHandle(proc); #else printf("killing pid: %d\n", p); kill(p, SIGKILL); #endif } void stop_proxy(int port) { std::map::iterator const it = running_proxies.find(port); if (it == running_proxies.end()) return; fprintf(stdout, "stopping proxy on port %d\n", port); stop_process(it->second.pid); running_proxies.erase(it); } void stop_all_proxies() { std::map proxies = running_proxies; running_proxies.clear(); for (std::map::iterator i = proxies.begin() , end = proxies.end(); i != end; ++i) { stop_process(i->second.pid); } } // returns a port on success and -1 on failure int start_proxy(int proxy_type) { using namespace libtorrent; std::map :: iterator i = running_proxies.begin(); for (; i != running_proxies.end(); ++i) { if (i->second.type == proxy_type) { return i->first; } } int port = 2000 + (lt::random() % 6000); error_code ec; io_service ios; // make sure the port we pick is free do { ++port; tcp::socket s(ios); s.open(tcp::v4(), ec); if (ec) break; s.bind(tcp::endpoint(address::from_string("127.0.0.1"), port), ec); } while (ec); char const* type = ""; char const* auth = ""; char const* cmd = ""; switch (proxy_type) { case settings_pack::socks4: type = "socks4"; auth = " --allow-v4"; cmd = "python ../socks.py"; break; case settings_pack::socks5: type = "socks5"; cmd = "python ../socks.py"; break; case settings_pack::socks5_pw: type = "socks5"; auth = " --username testuser --password testpass"; cmd = "python ../socks.py"; break; case settings_pack::http: type = "http"; cmd = "python ../http.py"; break; case settings_pack::http_pw: type = "http"; auth = " --username testuser --password testpass"; cmd = "python ../http.py"; break; } char buf[512]; snprintf(buf, sizeof(buf), "%s --port %d%s", cmd, port, auth); fprintf(stdout, "%s starting proxy on port %d (%s %s)...\n", time_now_string(), port, type, auth); fprintf(stdout, "%s\n", buf); pid_type r = async_run(buf); if (r == 0) abort(); proxy_t t = { r, proxy_type }; running_proxies.insert(std::make_pair(port, t)); fprintf(stdout, "%s launched\n", time_now_string()); test_sleep(500); return port; } using namespace libtorrent; template boost::shared_ptr clone_ptr(boost::shared_ptr const& ptr) { return boost::make_shared(*ptr); } unsigned char random_byte() { return std::rand() & 0xff; } void create_random_files(std::string const& path, const int file_sizes[], int num_files , file_storage* fs) { error_code ec; char* random_data = (char*)malloc(300000); for (int i = 0; i != num_files; ++i) { std::generate(random_data, random_data + 300000, random_byte); char filename[200]; snprintf(filename, sizeof(filename), "test%d", i); char dirname[200]; snprintf(dirname, sizeof(dirname), "test_dir%d", i / 5); std::string full_path = combine_path(path, dirname); error_code ec; lt::create_directories(full_path, ec); if (ec) fprintf(stderr, "create_directory(%s) failed: (%d) %s\n" , full_path.c_str(), ec.value(), ec.message().c_str()); full_path = combine_path(full_path, filename); int to_write = file_sizes[i]; if (fs) fs->add_file(full_path, to_write); file f(full_path, file::write_only, ec); if (ec) fprintf(stderr, "failed to create file \"%s\": (%d) %s\n" , full_path.c_str(), ec.value(), ec.message().c_str()); boost::int64_t offset = 0; while (to_write > 0) { int s = (std::min)(to_write, 300000); file::iovec_t b = { random_data, size_t(s)}; f.writev(offset, &b, 1, ec); if (ec) fprintf(stderr, "failed to write file \"%s\": (%d) %s\n" , full_path.c_str(), ec.value(), ec.message().c_str()); offset += s; to_write -= s; } } free(random_data); } boost::shared_ptr create_torrent(std::ostream* file , char const* name, int piece_size , int num_pieces, bool add_tracker, std::string ssl_certificate) { // excercise the path when encountering invalid urls char const* invalid_tracker_url = "http:"; char const* invalid_tracker_protocol = "foo://non/existent-name.com/announce"; file_storage fs; int total_size = piece_size * num_pieces; fs.add_file(name, total_size); libtorrent::create_torrent t(fs, piece_size); if (add_tracker) { t.add_tracker(invalid_tracker_url); t.add_tracker(invalid_tracker_protocol); } if (!ssl_certificate.empty()) { std::vector file_buf; error_code ec; int res = load_file(ssl_certificate, file_buf, ec); if (ec || res < 0) { fprintf(stderr, "failed to load SSL certificate: %s\n", ec.message().c_str()); } else { std::string pem; std::copy(file_buf.begin(), file_buf.end(), std::back_inserter(pem)); t.set_root_cert(pem); } } std::vector piece(piece_size); for (int i = 0; i < int(piece.size()); ++i) piece[i] = (i % 26) + 'A'; // calculate the hash for all pieces int num = t.num_pieces(); sha1_hash ph = hasher(&piece[0], piece.size()).final(); for (int i = 0; i < num; ++i) t.set_hash(i, ph); if (file) { while (total_size > 0) { file->write(&piece[0], (std::min)(int(piece.size()), total_size)); total_size -= piece.size(); } } std::vector tmp; std::back_insert_iterator > out(tmp); entry tor = t.generate(); bencode(out, tor); error_code ec; return boost::make_shared( &tmp[0], tmp.size(), boost::ref(ec), 0); } boost::tuple setup_transfer(lt::session* ses1, lt::session* ses2, lt::session* ses3 , bool clear_files, bool use_metadata_transfer, bool connect_peers , std::string suffix, int piece_size , boost::shared_ptr* torrent, bool super_seeding , add_torrent_params const* p, bool stop_lsd, bool use_ssl_ports , boost::shared_ptr* torrent2) { TORRENT_ASSERT(ses1); TORRENT_ASSERT(ses2); if (stop_lsd) { settings_pack pack; pack.set_bool(settings_pack::enable_lsd, false); ses1->apply_settings(pack); ses2->apply_settings(pack); if (ses3) ses3->apply_settings(pack); } // This has the effect of applying the global // rule to all peers, regardless of if they're local or not ip_filter f; f.add_rule(address_v4::from_string("0.0.0.0") , address_v4::from_string("255.255.255.255") , 1 << lt::session::global_peer_class_id); ses1->set_peer_class_filter(f); ses2->set_peer_class_filter(f); if (ses3) ses3->set_peer_class_filter(f); const int mask = alert::all_categories & ~(alert::progress_notification | alert::piece_progress_notification | alert::block_progress_notification | alert::performance_warning | alert::stats_notification | alert::picker_log_notification); settings_pack pack; pack.set_int(settings_pack::alert_mask, mask); if (ses3) pack.set_bool(settings_pack::allow_multiple_connections_per_ip, true); pack.set_int(settings_pack::mixed_mode_algorithm, settings_pack::prefer_tcp); pack.set_int(settings_pack::max_failcount, 1); ses1->apply_settings(pack); ses2->apply_settings(pack); if (ses3) { ses3->apply_settings(pack); } boost::shared_ptr t; if (torrent == 0) { error_code ec; create_directory("tmp1" + suffix, ec); std::ofstream file(combine_path("tmp1" + suffix, "temporary").c_str()); t = ::create_torrent(&file, "temporary", piece_size, 9, false); file.close(); if (clear_files) { remove_all(combine_path("tmp2" + suffix, "temporary"), ec); remove_all(combine_path("tmp3" + suffix, "temporary"), ec); } char ih_hex[41]; to_hex((char const*)&t->info_hash()[0], 20, ih_hex); fprintf(stdout, "generated torrent: %s tmp1%s/temporary\n", ih_hex, suffix.c_str()); } else { t = *torrent; } // they should not use the same save dir, because the // file pool will complain if two torrents are trying to // use the same files add_torrent_params param; param.flags &= ~add_torrent_params::flag_paused; param.flags &= ~add_torrent_params::flag_auto_managed; if (p) param = *p; param.ti = clone_ptr(t); param.save_path = "tmp1" + suffix; param.flags |= add_torrent_params::flag_seed_mode; error_code ec; torrent_handle tor1 = ses1->add_torrent(param, ec); if (ec) { fprintf(stdout, "ses1.add_torrent: %s\n", ec.message().c_str()); return boost::make_tuple(torrent_handle(), torrent_handle(), torrent_handle()); } tor1.super_seeding(super_seeding); // the downloader cannot use seed_mode param.flags &= ~add_torrent_params::flag_seed_mode; TEST_CHECK(!ses1->get_torrents().empty()); torrent_handle tor2; torrent_handle tor3; if (ses3) { param.ti = clone_ptr(t); param.save_path = "tmp3" + suffix; tor3 = ses3->add_torrent(param, ec); TEST_CHECK(!ses3->get_torrents().empty()); } if (use_metadata_transfer) { param.ti.reset(); param.info_hash = t->info_hash(); } else if (torrent2) { param.ti = clone_ptr(*torrent2); } else { param.ti = clone_ptr(t); } param.save_path = "tmp2" + suffix; tor2 = ses2->add_torrent(param, ec); TEST_CHECK(!ses2->get_torrents().empty()); TORRENT_ASSERT(ses1->get_torrents().size() == 1); TORRENT_ASSERT(ses2->get_torrents().size() == 1); // test_sleep(100); if (connect_peers) { wait_for_downloading(*ses2, "ses2"); error_code ec; int port = 0; if (use_ssl_ports) { port = ses2->ssl_listen_port(); fprintf(stdout, "%s: ses2->ssl_listen_port(): %d\n", time_now_string(), port); } if (port == 0) { port = ses2->listen_port(); fprintf(stdout, "%s: ses2->listen_port(): %d\n", time_now_string(), port); } fprintf(stdout, "%s: ses1: connecting peer port: %d\n" , time_now_string(), port); tor1.connect_peer(tcp::endpoint(address::from_string("127.0.0.1", ec) , port)); if (ses3) { // give the other peers some time to get an initial // set of pieces before they start sharing with each-other wait_for_downloading(*ses3, "ses3"); port = 0; int port2 = 0; if (use_ssl_ports) { port = ses2->ssl_listen_port(); port2 = ses1->ssl_listen_port(); } if (port == 0) port = ses2->listen_port(); if (port2 == 0) port2 = ses1->listen_port(); fprintf(stdout, "ses3: connecting peer port: %d\n", port); tor3.connect_peer(tcp::endpoint( address::from_string("127.0.0.1", ec), port)); fprintf(stdout, "ses3: connecting peer port: %d\n", port2); tor3.connect_peer(tcp::endpoint( address::from_string("127.0.0.1", ec) , port2)); } } return boost::make_tuple(tor1, tor2, tor3); } pid_type web_server_pid = 0; int start_web_server(bool ssl, bool chunked_encoding, bool keepalive) { int port = 2000 + (lt::random() % 6000); error_code ec; io_service ios; // make sure the port we pick is free do { ++port; tcp::socket s(ios); s.open(tcp::v4(), ec); if (ec) break; s.bind(tcp::endpoint(address::from_string("127.0.0.1"), port), ec); } while (ec); char buf[200]; snprintf(buf, sizeof(buf), "python ../web_server.py %d %d %d %d" , port, chunked_encoding , ssl, keepalive); fprintf(stdout, "%s starting web_server on port %d...\n", time_now_string(), port); fprintf(stdout, "%s\n", buf); pid_type r = async_run(buf); if (r == 0) abort(); web_server_pid = r; fprintf(stdout, "%s launched\n", time_now_string()); test_sleep(500); return port; } void stop_web_server() { if (web_server_pid == 0) return; fprintf(stdout, "stopping web server\n"); stop_process(web_server_pid); web_server_pid = 0; } tcp::endpoint ep(char const* ip, int port) { error_code ec; return tcp::endpoint(address::from_string(ip, ec), port); } libtorrent-rasterbar-1.1.13/test/setup_transfer.hpp000066400000000000000000000110231351156116000224740ustar00rootroot00000000000000/* Copyright (c) 2008, 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 SETUP_TRANSFER_HPP #define SETUP_TRANSFER_HPP #include "libtorrent/session.hpp" #include #include "test.hpp" namespace libtorrent { class alert; struct add_torrent_params; class file_storage; } EXPORT boost::shared_ptr generate_torrent(); EXPORT int print_failures(); EXPORT unsigned char random_byte(); EXPORT int load_file(std::string const& filename, std::vector& v, libtorrent::error_code& ec, int limit = 8000000); EXPORT void save_file(char const* filename, char const* data, int size); EXPORT void report_failure(char const* err, char const* file, int line); EXPORT void init_rand_address(); EXPORT libtorrent::address rand_v4(); #if TORRENT_USE_IPV6 EXPORT libtorrent::address rand_v6(); #endif EXPORT libtorrent::tcp::endpoint rand_tcp_ep(); EXPORT libtorrent::udp::endpoint rand_udp_ep(); EXPORT libtorrent::sha1_hash rand_hash(); EXPORT std::map get_counters(libtorrent::session& s); EXPORT libtorrent::alert const* wait_for_alert( libtorrent::session& ses, int type, char const* name = "" , int num = 1 , lt::time_duration timeout = lt::seconds(10)); EXPORT void print_ses_rate(float time , libtorrent::torrent_status const* st1 , libtorrent::torrent_status const* st2 , libtorrent::torrent_status const* st3 = NULL); EXPORT bool print_alerts(libtorrent::session& ses, char const* name , bool allow_disconnects = false , bool allow_no_torrents = false , bool allow_failed_fastresume = false , boost::function const& predicate = boost::function() , bool no_output = false); EXPORT void wait_for_listen(libtorrent::session& ses, char const* name); EXPORT void wait_for_downloading(libtorrent::session& ses, char const* name); EXPORT void test_sleep(int millisec); EXPORT void create_random_files(std::string const& path, const int file_sizes[] , int num_files, libtorrent::file_storage* fs = NULL); EXPORT boost::shared_ptr create_torrent(std::ostream* file = 0 , char const* name = "temporary", int piece_size = 16 * 1024, int num_pieces = 13 , bool add_tracker = true, std::string ssl_certificate = ""); EXPORT boost::tuple setup_transfer(libtorrent::session* ses1, libtorrent::session* ses2 , libtorrent::session* ses3, bool clear_files, bool use_metadata_transfer = true , bool connect = true, std::string suffix = "", int piece_size = 16 * 1024 , boost::shared_ptr* torrent = 0, bool super_seeding = false , libtorrent::add_torrent_params const* p = 0, bool stop_lsd = true, bool use_ssl_ports = false , boost::shared_ptr* torrent2 = 0); EXPORT int start_web_server(bool ssl = false, bool chunked = false , bool keepalive = true); EXPORT void stop_web_server(); EXPORT int start_proxy(int type); EXPORT void stop_proxy(int port); EXPORT void stop_all_proxies(); EXPORT libtorrent::tcp::endpoint ep(char const* ip, int port); #endif libtorrent-rasterbar-1.1.13/test/socks.py000077500000000000000000000212141351156116000204210ustar00rootroot00000000000000#!/usr/bin/env python """Minimal non-feature complete socks proxy""" import random import socket from SocketServer import StreamRequestHandler, ThreadingTCPServer from struct import pack, unpack import threading import sys def debug(s): print >>sys.stderr, 'socks.py: ', s def error(s): print >>sys.stderr, 'socks.py, ERROR: ', s class MyTCPServer(ThreadingTCPServer): allow_reuse_address = True def handle_timeout(self): raise Exception('timeout') CLOSE = object() VERSION = '\x05' NOAUTH = '\x00' USERPASS = '\x02' CONNECT = '\x01' UDP_ASSOCIATE = '\x03' IPV4 = '\x01' IPV6 = '\x04' DOMAIN_NAME = '\x03' SUCCESS = '\x00' password = None username = None allow_v4 = False def send(dest, msg): if msg == CLOSE: try: dest.shutdown(socket.SHUT_WR) except: pass dest.close() return 0 else: return dest.sendall(msg) def recv(source, buffer): data = source.recv(buffer) if data == '': return CLOSE else: return data def forward(source, dest, name): while True: data = recv(source, 4000) if data == CLOSE: send(dest, CLOSE) debug('%s hung up' % name) return # debug('Forwarding (%d) %r' % (len(data), data)) send(dest, data) def spawn_forwarder(source, dest, name): t = threading.Thread(target=forward, args=(source, dest, name)) t.daemon = True t.start() class SocksHandler(StreamRequestHandler): """Highly feature incomplete SOCKS 5 implementation""" def close_request(self): self.server.close_request(self.request) def read(self, n): data = '' while len(data) < n: extra = self.rfile.read(n) if extra == '': raise Exception('Connection closed') data += extra return data def handle(self): # IMRPOVEMENT: Report who requests are from in logging # IMPROVEMENT: Timeout on client debug('Connection - authenticating') version = self.read(1) if allow_v4 and version == '\x04': cmd = self.read(1) if cmd != CONNECT and cmd != UDP_ASSOCIATE: error('Only supports connect and udp-associate method not (%r) closing' % cmd) self.close_request() return raw_dest_port = self.read(2) dest_port, = unpack('>H', raw_dest_port) raw_dest_address = self.read(4) dest_address = '.'.join(map(str, unpack('>4B', raw_dest_address))) user_id = '' c = self.read(1) while c != '\0': user_id += c c = self.read(1) outbound_sock = socket.socket(socket.AF_INET) out_address = socket.getaddrinfo(dest_address,dest_port)[0][4] debug("Creating forwarder connection to %s:%d" % (out_address[0], out_address[1])) outbound_sock.connect(out_address) self.send_reply_v4(outbound_sock.getsockname()) spawn_forwarder(outbound_sock, self.request, 'destination') forward(self.request, outbound_sock, 'client') return if version != '\x05': error('Wrong version number (%r) closing...' % version) self.close_request() return nmethods = ord(self.read(1)) method_list = self.read(nmethods) global password global username if password == None and NOAUTH in method_list: self.send_no_auth_method() debug('Authenticated (no-auth)') elif USERPASS in method_list: self.send_user_pass_auth_method() auth_version = self.read(1) if auth_version != '\x01': error('Wrong sub-negotiation version number (%r) closing...' % version) self.close_request() return usr_len = ord(self.read(1)) usr_name = self.read(usr_len) pwd_len = ord(self.read(1)) pwd = self.read(pwd_len) if usr_name != username or pwd != password: error('Invalid username or password') self.close_request() return debug('Authenticated (user/password)') self.send_authenticated() else: error('Server only supports NOAUTH and user/pass') self.send_no_method() return # If we were authenticating it would go here version, cmd, zero, address_type = self.read(4) if version != '\x05': error('Wrong version number (%r) closing...' % version) self.close_request() elif cmd != CONNECT and cmd != UDP_ASSOCIATE: error('Only supports connect and udp-associate method not (%r) closing' % cmd) self.close_request() elif zero != '\x00': error('Mangled request. Reserved field (%r) is not null' % zero) self.close_request() if address_type == IPV4: raw_dest_address = self.read(4) dest_address = '.'.join(map(str, unpack('>4B', raw_dest_address))) elif address_type == IPV6: raw_dest_address = self.read(16) dest_address = ":".join(map(lambda x: hex(x)[2:],unpack('>8H',raw_dest_address))) elif address_type == DOMAIN_NAME: dns_length = ord(self.read(1)) dns_name = self.read(dns_length) dest_address = dns_name else: error('Unknown addressing (%r)' % address_type) self.close_request() raw_dest_port = self.read(2) dest_port, = unpack('>H', raw_dest_port) if address_type == IPV6: outbound_sock = socket.socket(socket.AF_INET6) else: outbound_sock = socket.socket(socket.AF_INET) try: out_address = socket.getaddrinfo(dest_address,dest_port)[0][4] except Exception, e: print e return if cmd == UDP_ASSOCIATE: debug("no UDP support yet, closing") return; debug("Creating forwarder connection to %s:%d" % (out_address[0], out_address[1])) try: outbound_sock.connect(out_address) except Exception, e: print e return if address_type == IPV6: self.send_reply6(outbound_sock.getsockname()) else: self.send_reply(outbound_sock.getsockname()) spawn_forwarder(outbound_sock, self.request, 'destination') try: forward(self.request, outbound_sock, 'client') except Exception,e: print e def send_reply_v4(self, (bind_addr, bind_port)): self.wfile.write('\0\x5a\0\0\0\0\0\0') self.wfile.flush() def send_reply(self, (bind_addr, bind_port)): bind_tuple = tuple(map(int, bind_addr.split('.'))) full_address = bind_tuple + (bind_port,) debug('Setting up forwarding port %r' % (full_address,)) msg = pack('>cccc4BH', VERSION, SUCCESS, '\x00', IPV4, *full_address) self.wfile.write(msg) def send_reply6(self, (bind_addr, bind_port, unused1, unused2)): bind_tuple = tuple(map(lambda x: int(x,16), bind_addr.split(':'))) full_address = bind_tuple + (bind_port,) debug('Setting up forwarding port %r' % (full_address,)) msg = pack('>cccc8HH', VERSION, SUCCESS, '\x00', IPV6, *full_address) self.wfile.write(msg) def send_no_method(self): self.wfile.write('\x05\xff') self.close_request() def send_no_auth_method(self): self.wfile.write('\x05\x00') self.wfile.flush() def send_user_pass_auth_method(self): self.wfile.write('\x05\x02') self.wfile.flush() def send_authenticated(self): self.wfile.write('\x01\x00') self.wfile.flush() if __name__ == '__main__': listen_port = 8002 i = 1 while i < len(sys.argv): if sys.argv[i] == '--username': username = sys.argv[i+1] i += 1 elif sys.argv[i] == '--password': password = sys.argv[i+1] i += 1 elif sys.argv[i] == '--port': listen_port = int(sys.argv[i+1]) i += 1 elif sys.argv[i] == '--allow-v4': allow_v4 = True else: if sys.argv[i] != '--help': debug('unknown option "%s"' % sys.argv[i]) print('usage: socks.py [--username --password ] [--port ]') sys.exit(1) i += 1 debug('Listening on port %d...' % listen_port) server = MyTCPServer(('localhost', listen_port), SocksHandler) server.timeout = 190 while True: server.handle_request() libtorrent-rasterbar-1.1.13/test/swarm_suite.cpp000066400000000000000000000167771351156116000220120ustar00rootroot00000000000000/* Copyright (c) 2014, 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/session.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/time.hpp" #include "libtorrent/random.hpp" #include #include "test.hpp" #include "setup_transfer.hpp" #include "settings.hpp" #include "swarm_suite.hpp" void test_swarm(int flags) { using namespace libtorrent; namespace lt = libtorrent; fprintf(stderr, "\n\n ==== TEST SWARM === %s%s%s%s%s ===\n\n\n" , (flags & super_seeding) ? "super-seeding ": "" , (flags & strict_super_seeding) ? "strict-super-seeding ": "" , (flags & seed_mode) ? "seed-mode ": "" , (flags & time_critical) ? "time-critical ": "" , (flags & suggest) ? "suggest ": "" ); // in case the previous run was terminated error_code ec; remove_all("tmp1_swarm", ec); remove_all("tmp2_swarm", ec); remove_all("tmp3_swarm", ec); // these are declared before the session objects // so that they are destructed last. This enables // the sessions to destruct in parallel session_proxy p1; session_proxy p2; session_proxy p3; settings_pack pack = settings(); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_dht, false); pack.set_bool(settings_pack::allow_multiple_connections_per_ip, true); if (flags & strict_super_seeding) pack.set_bool(settings_pack::strict_super_seeding, true); if (flags & suggest) pack.set_int(settings_pack::suggest_mode, settings_pack::suggest_read_cache); // this is to avoid everything finish from a single peer // immediately. To make the swarm actually connect all // three peers before finishing. float rate_limit = 100000; int port = lt::random() % 100; char iface[50]; snprintf(iface, sizeof(iface), "0.0.0.0:480%02d", port); pack.set_int(settings_pack::upload_rate_limit, rate_limit); pack.set_str(settings_pack::listen_interfaces, iface); pack.set_int(settings_pack::max_retry_port_bind, 1000); pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_forced); pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_forced); lt::session ses1(pack); snprintf(iface, sizeof(iface), "0.0.0.0:490%02d", port); pack.set_str(settings_pack::listen_interfaces, iface); pack.set_int(settings_pack::download_rate_limit, rate_limit / 2); pack.set_int(settings_pack::upload_rate_limit, rate_limit); lt::session ses2(pack); snprintf(iface, sizeof(iface), "0.0.0.0:500%02d", port); pack.set_str(settings_pack::listen_interfaces, iface); lt::session ses3(pack); torrent_handle tor1; torrent_handle tor2; torrent_handle tor3; add_torrent_params p; p.flags &= ~add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; if (flags & seed_mode) p.flags |= add_torrent_params::flag_seed_mode; // test using piece sizes smaller than 16kB boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true , false, true, "_swarm", 8 * 1024, 0, flags & super_seeding, &p); if (flags & time_critical) { tor2.set_piece_deadline(2, 0); tor2.set_piece_deadline(5, 1000); tor2.set_piece_deadline(8, 2000); } float sum_dl_rate2 = 0.f; float sum_dl_rate3 = 0.f; int count_dl_rates2 = 0; int count_dl_rates3 = 0; for (int i = 0; i < 80; ++i) { print_alerts(ses1, "ses1"); print_alerts(ses2, "ses2"); print_alerts(ses3, "ses3"); torrent_status st1 = tor1.status(); torrent_status st2 = tor2.status(); torrent_status st3 = tor3.status(); if (flags & super_seeding) { TEST_CHECK(st1.is_seeding); TEST_CHECK(st1.super_seeding); } if (st2.progress < 1.f && st2.progress > 0.5f) { sum_dl_rate2 += st2.download_payload_rate; ++count_dl_rates2; } if (st3.progress < 1.f && st3.progress > 0.5f) { sum_dl_rate3 += st3.download_rate; ++count_dl_rates3; } print_ses_rate(i, &st1, &st2, &st3); if (st2.is_seeding && st3.is_seeding) break; test_sleep(1000); } TEST_CHECK(tor2.status().is_seeding); TEST_CHECK(tor3.status().is_seeding); float average2 = sum_dl_rate2 / float(count_dl_rates2); float average3 = sum_dl_rate3 / float(count_dl_rates3); std::cerr << average2 << std::endl; std::cerr << "average rate: " << (average2 / 1000.f) << "kB/s - " << (average3 / 1000.f) << "kB/s" << std::endl; if (tor2.status().is_seeding && tor3.status().is_seeding) std::cerr << "done\n"; // make sure the files are deleted ses1.remove_torrent(tor1, lt::session::delete_files); ses2.remove_torrent(tor2, lt::session::delete_files); ses3.remove_torrent(tor3, lt::session::delete_files); alert const* a = wait_for_alert(ses1, torrent_deleted_alert::alert_type, "swarm_suite"); TEST_CHECK(alert_cast(a) != 0); // there shouldn't be any alerts generated from now on // make sure that the timer in wait_for_alert() works // this should time out (ret == 0) and it should take // about 2 seconds time_point start = clock_type::now(); alert const* ret; while ((ret = ses1.wait_for_alert(seconds(2)))) { fprintf(stderr, "wait returned: %d ms\n" , int(total_milliseconds(clock_type::now() - start))); std::vector alerts; ses1.pop_alerts(&alerts); for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { fprintf(stderr, "%s\n", ret->message().c_str()); } start = clock_type::now(); } fprintf(stderr, "loop returned: %d ms\n" , int(total_milliseconds(clock_type::now() - start))); // this allows shutting down the sessions in parallel p1 = ses1.abort(); p2 = ses2.abort(); p3 = ses3.abort(); time_point end = clock_type::now(); fprintf(stderr, "time: %d ms\n", int(total_milliseconds(end - start))); TEST_CHECK(end - start < milliseconds(3000)); TEST_CHECK(end - start > milliseconds(1900)); TEST_CHECK(!exists("tmp1_swarm/temporary")); TEST_CHECK(!exists("tmp2_swarm/temporary")); TEST_CHECK(!exists("tmp3_swarm/temporary")); remove_all("tmp1_swarm", ec); remove_all("tmp2_swarm", ec); remove_all("tmp3_swarm", ec); } libtorrent-rasterbar-1.1.13/test/swarm_suite.hpp000066400000000000000000000032461351156116000220020ustar00rootroot00000000000000/* Copyright (c) 2014, 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 "test.hpp" enum test_flags_t { super_seeding = 1, strict_super_seeding = 2, seed_mode = 4, time_critical = 8, suggest = 16, }; void EXPORT test_swarm(int flags = 0); libtorrent-rasterbar-1.1.13/test/test.cpp000066400000000000000000000056441351156116000204160ustar00rootroot00000000000000/* Copyright (c) 2008-2015, 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 #include // for tmpfile() #include "test.hpp" unit_test_t _g_unit_tests[1024]; int _g_num_unit_tests = 0; int _g_test_failures = 0; // flushed at start of every unit int _g_test_idx = 0; static std::vector failure_strings; int test_counter() { return _g_test_idx; } void report_failure(char const* err, char const* file, int line) { char buf[500]; snprintf(buf, sizeof(buf), "\x1b[41m***** %s:%d \"%s\" *****\x1b[0m\n", file, line, err); fprintf(stderr, "\n%s\n", buf); failure_strings.push_back(buf); ++_g_test_failures; } int print_failures() { int longest_name = 0; for (int i = 0; i < _g_num_unit_tests; ++i) { int len = strlen(_g_unit_tests[i].name); if (len > longest_name) longest_name = len; } fprintf(stderr, "\n\n"); int total_num_failures = 0; for (int i = 0; i < _g_num_unit_tests; ++i) { if (_g_unit_tests[i].run == false) continue; if (_g_unit_tests[i].num_failures == 0) { fprintf(stderr, "\x1b[32m[%-*s] ***PASS***\n" , longest_name, _g_unit_tests[i].name); } else { total_num_failures += _g_unit_tests[i].num_failures; fprintf(stderr, "\x1b[31m[%-*s] %d FAILURES\n" , longest_name , _g_unit_tests[i].name , _g_unit_tests[i].num_failures); } } fprintf(stderr, "\x1b[0m"); if (total_num_failures > 0) fprintf(stderr, "\n\n\x1b[41m == %d TEST(S) FAILED ==\x1b[0m\n\n\n" , total_num_failures); return total_num_failures; } libtorrent-rasterbar-1.1.13/test/test.hpp000066400000000000000000000113751351156116000204210ustar00rootroot00000000000000/* Copyright (c) 2008, 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 TEST_HPP #define TEST_HPP #include "libtorrent/address.hpp" #include "libtorrent/socket.hpp" #include #include #include #include #include #include "libtorrent/config.hpp" // tests are expected to even test deprecated functionality. There is no point // in warning about deprecated use in any of the tests. #if defined __clang__ #pragma clang diagnostic ignored "-Wdeprecated" #pragma clang diagnostic ignored "-Wdeprecated-declarations" #elif defined __GNUC__ #pragma GCC diagnostic ignored "-Wdeprecated" #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #elif defined _MSC_VER #pragma warning(disable : 4996) #endif #if defined TORRENT_BUILDING_TEST_SHARED #define EXPORT BOOST_SYMBOL_EXPORT #elif defined TORRENT_LINK_TEST_SHARED #define EXPORT BOOST_SYMBOL_IMPORT #else #define EXPORT #endif void EXPORT report_failure(char const* err, char const* file, int line); int EXPORT print_failures(); int EXPORT test_counter(); typedef void (*unit_test_fun_t)(); struct unit_test_t { unit_test_fun_t fun; char const* name; int num_failures; bool run; FILE* output; }; extern unit_test_t EXPORT _g_unit_tests[1024]; extern int EXPORT _g_num_unit_tests; extern int EXPORT _g_test_failures; #define TORRENT_TEST(test_name) \ static void BOOST_PP_CAT(unit_test_, test_name)(); \ static struct BOOST_PP_CAT(register_class_, test_name) { \ BOOST_PP_CAT(register_class_, test_name) () { \ unit_test_t& t = _g_unit_tests[_g_num_unit_tests]; \ t.fun = &BOOST_PP_CAT(unit_test_, test_name); \ t.name = __FILE__ "." #test_name; \ t.num_failures = 0; \ t.run = false; \ t.output = NULL; \ _g_num_unit_tests++; \ } \ } BOOST_PP_CAT(_static_registrar_, test_name); \ static void BOOST_PP_CAT(unit_test_, test_name)() #define TEST_REPORT_AUX(x, line, file) \ report_failure(x, line, file) #ifdef BOOST_NO_EXCEPTIONS #define TEST_CHECK(x) \ if (!(x)) \ TEST_REPORT_AUX("TEST_CHECK failed: \"" #x "\"", __FILE__, __LINE__); #define TEST_EQUAL(x, y) \ if ((x) != (y)) { \ std::stringstream s__; \ s__ << "TEST_EQUAL_ERROR:\n" #x ": " << (x) << "\nexpected: " << (y); \ TEST_REPORT_AUX(s__.str().c_str(), __FILE__, __LINE__); \ } #else #define TEST_CHECK(x) \ try \ { \ if (!(x)) \ TEST_REPORT_AUX("TEST_CHECK failed: \"" #x "\"", __FILE__, __LINE__); \ } \ catch (std::exception& e) \ { \ TEST_ERROR("Exception thrown: " #x " :" + std::string(e.what())); \ } \ catch (...) \ { \ TEST_ERROR("Exception thrown: " #x); \ } #define TEST_EQUAL(x, y) \ try { \ if ((x) != (y)) { \ std::stringstream s__; \ s__ << "TEST_EQUAL_ERROR: " #x ": " << (x) << " expected: " << (y); \ TEST_REPORT_AUX(s__.str().c_str(), __FILE__, __LINE__); \ } \ } \ catch (std::exception& e) \ { \ TEST_ERROR("Exception thrown: " #x " :" + std::string(e.what())); \ } \ catch (...) \ { \ TEST_ERROR("Exception thrown: " #x); \ } #endif #define TEST_ERROR(x) \ TEST_REPORT_AUX((std::string("ERROR: \"") + (x) + "\"").c_str(), __FILE__, __LINE__) #define TEST_NOTHROW(x) \ try \ { \ x; \ } \ catch (...) \ { \ TEST_ERROR("Exception thrown: " #x); \ } #define TEST_THROW(x) \ try \ { \ x; \ TEST_ERROR("No exception thrown: " #x); \ } \ catch (...) {} #endif // TEST_HPP libtorrent-rasterbar-1.1.13/test/test_alert_manager.cpp000066400000000000000000000242341351156116000232730ustar00rootroot00000000000000/* Copyright (c) 2015, 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 "test.hpp" #include "libtorrent/alert_manager.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/thread.hpp" #include "setup_transfer.hpp" #include #include #include using namespace libtorrent; TORRENT_TEST(limit) { alert_manager mgr(500, 0xffffffff); TEST_EQUAL(mgr.alert_queue_size_limit(), 500); TEST_EQUAL(mgr.pending(), false); // try add 600 torrent_add_alert to make sure we honor the limit of 500 // alerts. for (int i = 0; i < 600; ++i) mgr.emplace_alert(torrent_handle(), i); TEST_EQUAL(mgr.pending(), true); std::vector alerts; int num_resume; mgr.get_all(alerts, num_resume); // even though we posted 600, the limit was 500 TEST_EQUAL(alerts.size(), 500); TEST_EQUAL(mgr.pending(), false); // now, try lowering the limit and do the same thing again mgr.set_alert_queue_size_limit(200); for (int i = 0; i < 600; ++i) mgr.emplace_alert(torrent_handle(), i); TEST_EQUAL(mgr.pending(), true); mgr.get_all(alerts, num_resume); // even though we posted 600, the limit was 200 TEST_EQUAL(alerts.size(), 200); } TORRENT_TEST(limit_int_max) { int const inf = std::numeric_limits::max(); alert_manager mgr(inf, 0xffffffff); TEST_EQUAL(mgr.alert_queue_size_limit(), inf); for (int i = 0; i < 600; ++i) mgr.emplace_alert(torrent_handle(), i); for (int i = 0; i < 600; ++i) mgr.emplace_alert(torrent_handle(), sha1_hash()); std::vector alerts; int num_resume; mgr.get_all(alerts, num_resume); TEST_EQUAL(alerts.size(), 1200); } TORRENT_TEST(priority_limit) { alert_manager mgr(100, 0xffffffff); TEST_EQUAL(mgr.alert_queue_size_limit(), 100); std::vector alerts; int num_resume = 0; // this should only add 100 because of the limit for (int i = 0; i < 200; ++i) mgr.emplace_alert(torrent_handle(), i); mgr.get_all(alerts, num_resume); TEST_EQUAL(alerts.size(), 100); // the limit is higher for priority alerts for (int i = 0; i < 300; ++i) mgr.emplace_alert(torrent_handle(), i, error_code()); mgr.get_all(alerts, num_resume); // even though we posted 500, the limit was 100 for half of them and // 100 + 200 for the other half, meaning we should have 300 alerts now TEST_EQUAL(alerts.size(), 300); } void test_dispatch_fun(int& cnt, std::auto_ptr const& a) { ++cnt; } TORRENT_TEST(dispatch_function) { #ifndef TORRENT_NO_DEPRECATE int cnt = 0; alert_manager mgr(100, 0xffffffff); TEST_EQUAL(mgr.alert_queue_size_limit(), 100); TEST_EQUAL(mgr.pending(), false); for (int i = 0; i < 20; ++i) mgr.emplace_alert(torrent_handle(), add_torrent_params(), error_code()); TEST_EQUAL(mgr.pending(), true); mgr.set_dispatch_function(boost::bind(&test_dispatch_fun, boost::ref(cnt), _1)); TEST_EQUAL(mgr.pending(), false); TEST_EQUAL(cnt, 20); for (int i = 0; i < 200; ++i) mgr.emplace_alert(torrent_handle(), add_torrent_params(), error_code()); TEST_EQUAL(mgr.pending(), false); TEST_EQUAL(cnt, 220); #endif } void test_notify_fun(int& cnt) { ++cnt; } TORRENT_TEST(notify_function) { int cnt = 0; alert_manager mgr(100, 0xffffffff); TEST_EQUAL(mgr.alert_queue_size_limit(), 100); TEST_EQUAL(mgr.pending(), false); for (int i = 0; i < 20; ++i) mgr.emplace_alert(torrent_handle(), add_torrent_params(), error_code()); TEST_EQUAL(mgr.pending(), true); // if there are queued alerts when we set the notify function, // that counts as an edge and it's called mgr.set_notify_function(boost::bind(&test_notify_fun, boost::ref(cnt))); TEST_EQUAL(mgr.pending(), true); TEST_EQUAL(cnt, 1); // subsequent posted alerts will not cause an edge (because there are // already alerts queued) for (int i = 0; i < 20; ++i) mgr.emplace_alert(torrent_handle(), add_torrent_params(), error_code()); TEST_EQUAL(mgr.pending(), true); TEST_EQUAL(cnt, 1); // however, if we pop all the alerts and post new ones, there will be // and edge triggering the notify call std::vector alerts; int num_resume; mgr.get_all(alerts, num_resume); TEST_EQUAL(mgr.pending(), false); for (int i = 0; i < 20; ++i) mgr.emplace_alert(torrent_handle(), add_torrent_params(), error_code()); TEST_EQUAL(mgr.pending(), true); TEST_EQUAL(cnt, 2); } #ifndef TORRENT_DISABLE_EXTENSIONS int plugin_alerts[3] = { 0, 0, 0 }; struct test_plugin : libtorrent::plugin { test_plugin(int index) : m_index(index) {} virtual void on_alert(alert const* a) { ++plugin_alerts[m_index]; } int m_index; }; #endif TORRENT_TEST(extensions) { #ifndef TORRENT_DISABLE_EXTENSIONS memset(plugin_alerts, 0, sizeof(plugin_alerts)); alert_manager mgr(100, 0xffffffff); mgr.add_extension(boost::make_shared(0)); mgr.add_extension(boost::make_shared(1)); mgr.add_extension(boost::make_shared(2)); for (int i = 0; i < 53; ++i) mgr.emplace_alert(torrent_handle(), add_torrent_params(), error_code()); TEST_EQUAL(plugin_alerts[0], 53); TEST_EQUAL(plugin_alerts[1], 53); TEST_EQUAL(plugin_alerts[2], 53); for (int i = 0; i < 17; ++i) mgr.emplace_alert(torrent_handle(), add_torrent_params(), error_code()); TEST_EQUAL(plugin_alerts[0], 70); TEST_EQUAL(plugin_alerts[1], 70); TEST_EQUAL(plugin_alerts[2], 70); #endif } void post_torrent_added(alert_manager* mgr) { test_sleep(10); mgr->emplace_alert(torrent_handle(), add_torrent_params(), error_code()); } TORRENT_TEST(wait_for_alert) { alert_manager mgr(100, 0xffffffff); time_point start = clock_type::now(); alert* a = mgr.wait_for_alert(seconds(1)); time_point end = clock_type::now(); TEST_EQUAL(a, static_cast(0)); fprintf(stderr, "delay: %d ms (expected 1 second)\n" , int(total_milliseconds(end - start))); TEST_CHECK(end - start > milliseconds(900)); TEST_CHECK(end - start < milliseconds(1100)); mgr.emplace_alert(torrent_handle(), add_torrent_params(), error_code()); start = clock_type::now(); a = mgr.wait_for_alert(seconds(1)); end = clock_type::now(); fprintf(stderr, "delay: %d ms\n", int(total_milliseconds(end - start))); TEST_CHECK(end - start < milliseconds(1)); TEST_CHECK(a->type() == add_torrent_alert::alert_type); std::vector alerts; int num_resume = 0; mgr.get_all(alerts, num_resume); start = clock_type::now(); libtorrent::thread posting_thread(boost::bind(&post_torrent_added, &mgr)); a = mgr.wait_for_alert(seconds(10)); end = clock_type::now(); fprintf(stderr, "delay: %d ms\n", int(total_milliseconds(end - start))); TEST_CHECK(end - start < milliseconds(500)); TEST_CHECK(a->type() == add_torrent_alert::alert_type); posting_thread.join(); } TORRENT_TEST(queued_resume) { alert_manager mgr(100, 0xffffffff); TEST_EQUAL(mgr.num_queued_resume(), 0); for (int i = 0; i < 17; ++i) mgr.emplace_alert(torrent_handle(), add_torrent_params(), error_code()); TEST_EQUAL(mgr.num_queued_resume(), 0); std::vector alerts; int num_resume = 0; mgr.get_all(alerts, num_resume); TEST_EQUAL(num_resume, 0); TEST_EQUAL(alerts.size(), 17); TEST_EQUAL(mgr.num_queued_resume(), 0); error_code ec(boost::system::errc::no_such_file_or_directory , generic_category()); for (int i = 0; i < 2; ++i) mgr.emplace_alert(torrent_handle(), ec); TEST_EQUAL(mgr.num_queued_resume(), 2); mgr.get_all(alerts, num_resume); TEST_EQUAL(num_resume, 2); TEST_EQUAL(alerts.size(), 2); TEST_EQUAL(mgr.num_queued_resume(), 0); } TORRENT_TEST(alert_mask) { alert_manager mgr(100, 0xffffffff); TEST_CHECK(mgr.should_post()); TEST_CHECK(mgr.should_post()); mgr.set_alert_mask(0); TEST_CHECK(!mgr.should_post()); TEST_CHECK(!mgr.should_post()); } #ifndef TORRENT_DISABLE_EXTENSIONS struct post_plugin : lt::plugin { post_plugin(alert_manager& m) : mgr(m), depth(0) {} void on_alert(alert const* a) { if (++depth > 10) return; mgr.emplace_alert(torrent_handle(), 0); } alert_manager& mgr; int depth; }; // make sure the alert manager supports alerts being posted while executing a // plugin handler TORRENT_TEST(recursive_alerts) { alert_manager mgr(100, 0xffffffff); boost::shared_ptr pl = boost::make_shared(boost::ref(mgr)); mgr.add_extension(pl); mgr.emplace_alert(torrent_handle(), 0); TEST_EQUAL(pl->depth, 11); } #endif // TORRENT_DISABLE_EXTENSIONS libtorrent-rasterbar-1.1.13/test/test_auto_unchoke.cpp000066400000000000000000000125331351156116000231550ustar00rootroot00000000000000/* Copyright (c) 2011, 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/session.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/thread.hpp" #include #include #include "test.hpp" #include "setup_transfer.hpp" #include "settings.hpp" void test_swarm() { using namespace libtorrent; namespace lt = libtorrent; // these are declared before the session objects // so that they are destructed last. This enables // the sessions to destruct in parallel session_proxy p1; session_proxy p2; session_proxy p3; // this is to avoid everything finish from a single peer // immediately. To make the swarm actually connect all // three peers before finishing. float rate_limit = 50000; settings_pack pack = settings(); // run the choker once per second, to make it more likely to actually trigger // during the test. pack.set_int(settings_pack::unchoke_interval, 1); pack.set_bool(settings_pack::allow_multiple_connections_per_ip, true); pack.set_int(settings_pack::choking_algorithm, settings_pack::rate_based_choker); pack.set_int(settings_pack::upload_rate_limit, rate_limit); pack.set_int(settings_pack::unchoke_slots_limit, 1); pack.set_int(settings_pack::max_retry_port_bind, 900); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48010"); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_dht, false); #ifndef TORRENT_NO_DEPRECATE pack.set_bool(settings_pack::rate_limit_utp, true); #endif pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_forced); pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_forced); lt::session ses1(pack); pack.set_int(settings_pack::upload_rate_limit, rate_limit / 10); pack.set_int(settings_pack::download_rate_limit, rate_limit / 5); pack.set_int(settings_pack::unchoke_slots_limit, 0); pack.set_int(settings_pack::choking_algorithm, settings_pack::fixed_slots_choker); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:49010"); lt::session ses2(pack); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:49010"); lt::session ses3(pack); torrent_handle tor1; torrent_handle tor2; torrent_handle tor3; boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true, false, true, "_unchoke"); std::map cnt = get_counters(ses1); fprintf(stderr, "allowed_upload_slots: %d\n", int(cnt["ses.num_unchoke_slots"])); TEST_EQUAL(cnt["ses.num_unchoke_slots"], 1); for (int i = 0; i < 200; ++i) { print_alerts(ses1, "ses1"); print_alerts(ses2, "ses2"); print_alerts(ses3, "ses3"); cnt = get_counters(ses1); fprintf(stderr, "allowed unchoked: %d\n", int(cnt["ses.num_unchoke_slots"])); if (cnt["ses.num_unchoke_slots"] >= 2) break; torrent_status st1 = tor1.status(); torrent_status st2 = tor2.status(); torrent_status st3 = tor3.status(); print_ses_rate(i / 10.f, &st1, &st2, &st3); test_sleep(100); } TEST_CHECK(cnt["ses.num_unchoke_slots"] >= 2); // make sure the files are deleted ses1.remove_torrent(tor1, lt::session::delete_files); ses2.remove_torrent(tor2, lt::session::delete_files); ses3.remove_torrent(tor3, lt::session::delete_files); // this allows shutting down the sessions in parallel p1 = ses1.abort(); p2 = ses2.abort(); p3 = ses3.abort(); } TORRENT_TEST(auto_unchoke) { using namespace libtorrent; // in case the previous run was terminated error_code ec; remove_all("./tmp1_unchoke", ec); remove_all("./tmp2_unchoke", ec); remove_all("./tmp3_unchoke", ec); test_swarm(); TEST_CHECK(!exists("./tmp1_unchoke/temporary")); TEST_CHECK(!exists("./tmp2_unchoke/temporary")); TEST_CHECK(!exists("./tmp3_unchoke/temporary")); remove_all("./tmp1_unchoke", ec); remove_all("./tmp2_unchoke", ec); remove_all("./tmp3_unchoke", ec); } libtorrent-rasterbar-1.1.13/test/test_bandwidth_limiter.cpp000066400000000000000000000331521351156116000241620ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "libtorrent/bandwidth_manager.hpp" #include "libtorrent/bandwidth_queue_entry.hpp" #include "libtorrent/bandwidth_limit.hpp" #include "libtorrent/bandwidth_socket.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/time.hpp" #include "libtorrent/aux_/session_settings.hpp" #include #include #include #include #include #include struct torrent; struct peer_connection; using namespace libtorrent; const float sample_time = 20.f; // seconds //#define VERBOSE_LOGGING bandwidth_channel global_bwc; struct peer_connection: bandwidth_socket, boost::enable_shared_from_this { peer_connection(bandwidth_manager& bwm , bandwidth_channel& torrent_bwc, int prio, bool ignore_limits, std::string name) : m_bwm(bwm) , m_torrent_bandwidth_channel(torrent_bwc) , m_priority(prio) , m_ignore_limits(ignore_limits) , m_name(name) , m_quota(0) {} bool is_disconnecting() const { return false; } bool ignore_bandwidth_limits() { return m_ignore_limits; } void assign_bandwidth(int channel, int amount); void throttle(int limit) { m_bandwidth_channel.throttle(limit); } void start(); bandwidth_manager& m_bwm; bandwidth_channel m_bandwidth_channel; bandwidth_channel& m_torrent_bandwidth_channel; int m_priority; bool m_ignore_limits; std::string m_name; boost::int64_t m_quota; }; void peer_connection::assign_bandwidth(int channel, int amount) { m_quota += amount; #ifdef VERBOSE_LOGGING std::cout << " [" << m_name << "] assign bandwidth, " << amount << std::endl; #endif TEST_CHECK(amount > 0); start(); } void peer_connection::start() { bandwidth_channel* channels[] = { &m_bandwidth_channel , &m_torrent_bandwidth_channel , &global_bwc }; m_bwm.request_bandwidth(shared_from_this(), 400000000, m_priority, channels, 3); } typedef std::vector > connections_t; void do_change_rate(bandwidth_channel& t1, bandwidth_channel& t2, int limit) { static int counter = 10; --counter; if (counter == 0) { t1.throttle(limit); t2.throttle(limit); return; } t1.throttle(limit + limit / 2 * ((counter & 1)?-1:1)); t2.throttle(limit + limit / 2 * ((counter & 1)?1:-1)); } void do_change_peer_rate(connections_t& v, int limit) { static int count = 10; --count; if (count == 0) { std::for_each(v.begin(), v.end() , boost::bind(&peer_connection::throttle, _1, limit)); return; } int c = count; for (connections_t::iterator i = v.begin(); i != v.end(); ++i, ++c) i->get()->throttle(limit + limit / 2 * ((c & 1)?-1:1)); } static void nop() {} void run_test(connections_t& v , bandwidth_manager& manager , boost::function f = &nop) { std::cout << "-------------" << std::endl; std::for_each(v.begin(), v.end() , boost::bind(&peer_connection::start, _1)); libtorrent::aux::session_settings s; int tick_interval = s.get_int(settings_pack::tick_interval); for (int i = 0; i < int(sample_time * 1000 / tick_interval); ++i) { manager.update_quotas(milliseconds(tick_interval)); if ((i % 15) == 0) f(); } } bool close_to(float val, float comp, float err) { return fabs(val - comp) <= err; } void spawn_connections(connections_t& v, bandwidth_manager& bwm , bandwidth_channel& bwc, int num, char const* prefix) { for (int i = 0; i < num; ++i) { char name[200]; snprintf(name, sizeof(name), "%s%d", prefix, i); v.push_back(boost::shared_ptr(new peer_connection(bwm, bwc, 200, false, name))); } } void test_equal_connections(int num, int limit) { std::cout << "\ntest equal connections " << num << " " << limit << std::endl; bandwidth_manager manager(0); global_bwc.throttle(limit); bandwidth_channel t1; connections_t v; spawn_connections(v, manager, t1, num, "p"); run_test(v, manager); float sum = 0.f; float err = (std::max)(limit / num * 0.3f, 1000.f); for (connections_t::iterator i = v.begin() , end(v.end()); i != end; ++i) { sum += (*i)->m_quota; std::cout << (*i)->m_quota / sample_time << " target: " << (limit / num) << " eps: " << err << std::endl; TEST_CHECK(close_to((*i)->m_quota / sample_time, limit / num, err)); } sum /= sample_time; std::cout << "sum: " << sum << " target: " << limit << std::endl; TEST_CHECK(sum > 0); TEST_CHECK(close_to(sum, limit, 50)); } void test_connections_variable_rate(int num, int limit, int torrent_limit) { std::cout << "\ntest connections variable rate" << num << " l: " << limit << " t: " << torrent_limit << std::endl; bandwidth_manager manager(0); global_bwc.throttle(0); bandwidth_channel t1; if (torrent_limit) t1.throttle(torrent_limit); connections_t v; spawn_connections(v, manager, t1, num, "p"); std::for_each(v.begin(), v.end() , boost::bind(&peer_connection::throttle, _1, limit)); run_test(v, manager, boost::bind(&do_change_peer_rate , boost::ref(v), limit)); if (torrent_limit > 0 && limit * num > torrent_limit) limit = torrent_limit / num; float sum = 0.f; float err = limit * 0.3f; for (connections_t::iterator i = v.begin() , end(v.end()); i != end; ++i) { sum += (*i)->m_quota; std::cout << (*i)->m_quota / sample_time << " target: " << limit << " eps: " << err << std::endl; TEST_CHECK(close_to((*i)->m_quota / sample_time, limit, err)); } sum /= sample_time; std::cout << "sum: " << sum << " target: " << (limit * num) << std::endl; TEST_CHECK(sum > 0); TEST_CHECK(close_to(sum, limit * num, limit * 0.3f * num)); } void test_single_peer(int limit, bool torrent_limit) { std::cout << "\ntest single peer " << limit << " " << torrent_limit << std::endl; bandwidth_manager manager(0); bandwidth_channel t1; global_bwc.throttle(0); if (torrent_limit) t1.throttle(limit); else global_bwc.throttle(limit); connections_t v; spawn_connections(v, manager, t1, 1, "p"); run_test(v, manager); float sum = 0.f; for (connections_t::iterator i = v.begin() , end(v.end()); i != end; ++i) { sum += (*i)->m_quota; } sum /= sample_time; std::cout << sum << " target: " << limit << std::endl; TEST_CHECK(sum > 0); TEST_CHECK(close_to(sum, limit, 1000)); } void test_torrents(int num, int limit1, int limit2, int global_limit) { std::cout << "\ntest equal torrents " << num << " l1: " << limit1 << " l2: " << limit2 << " g: " << global_limit << std::endl; bandwidth_manager manager(0); global_bwc.throttle(global_limit); bandwidth_channel t1; bandwidth_channel t2; t1.throttle(limit1); t2.throttle(limit2); connections_t v1; spawn_connections(v1, manager, t1, num, "t1p"); connections_t v2; spawn_connections(v2, manager, t2, num, "t2p"); connections_t v; std::copy(v1.begin(), v1.end(), std::back_inserter(v)); std::copy(v2.begin(), v2.end(), std::back_inserter(v)); run_test(v, manager); if (global_limit > 0 && global_limit < limit1 + limit2) { limit1 = (std::min)(limit1, global_limit / 2); limit2 = global_limit - limit1; } float sum = 0.f; for (connections_t::iterator i = v1.begin() , end(v1.end()); i != end; ++i) { sum += (*i)->m_quota; } sum /= sample_time; std::cout << sum << " target: " << limit1 << std::endl; TEST_CHECK(sum > 0); TEST_CHECK(close_to(sum, limit1, 1000)); sum = 0.f; for (connections_t::iterator i = v2.begin() , end(v2.end()); i != end; ++i) { sum += (*i)->m_quota; } sum /= sample_time; std::cout << sum << " target: " << limit2 << std::endl; TEST_CHECK(sum > 0); TEST_CHECK(close_to(sum, limit2, 1000)); } void test_torrents_variable_rate(int num, int limit, int global_limit) { std::cout << "\ntest torrents variable rate" << num << " l: " << limit << " g: " << global_limit << std::endl; bandwidth_manager manager(0); global_bwc.throttle(global_limit); bandwidth_channel t1; bandwidth_channel t2; t1.throttle(limit); t2.throttle(limit); connections_t v1; spawn_connections(v1, manager, t1, num, "t1p"); connections_t v2; spawn_connections(v2, manager, t2, num, "t2p"); connections_t v; std::copy(v1.begin(), v1.end(), std::back_inserter(v)); std::copy(v2.begin(), v2.end(), std::back_inserter(v)); run_test(v, manager, boost::bind(&do_change_rate, boost::ref(t1), boost::ref(t2), limit)); if (global_limit > 0 && global_limit < 2 * limit) limit = global_limit / 2; float sum = 0.f; for (connections_t::iterator i = v1.begin() , end(v1.end()); i != end; ++i) { sum += (*i)->m_quota; } sum /= sample_time; std::cout << sum << " target: " << limit << std::endl; TEST_CHECK(sum > 0); TEST_CHECK(close_to(sum, limit, 1000)); sum = 0.f; for (connections_t::iterator i = v2.begin() , end(v2.end()); i != end; ++i) { sum += (*i)->m_quota; } sum /= sample_time; std::cout << sum << " target: " << limit << std::endl; TEST_CHECK(sum > 0); TEST_CHECK(close_to(sum, limit, 1000)); } void test_peer_priority(int limit, bool torrent_limit) { std::cout << "\ntest peer priority " << limit << " " << torrent_limit << std::endl; bandwidth_manager manager(0); bandwidth_channel t1; global_bwc.throttle(0); if (torrent_limit) t1.throttle(limit); else global_bwc.throttle(limit); connections_t v1; spawn_connections(v1, manager, t1, 10, "p"); connections_t v; std::copy(v1.begin(), v1.end(), std::back_inserter(v)); boost::shared_ptr p( new peer_connection(manager, t1, 1, false, "no-priority")); v.push_back(p); run_test(v, manager); float sum = 0.f; for (connections_t::iterator i = v1.begin() , end(v1.end()); i != end; ++i) { sum += (*i)->m_quota; } sum /= sample_time; std::cout << sum << " target: " << limit << std::endl; TEST_CHECK(sum > 0); TEST_CHECK(close_to(sum, limit, 50)); std::cout << "non-prioritized rate: " << p->m_quota / sample_time << " target: " << (limit / 200 / 10) << std::endl; TEST_CHECK(close_to(p->m_quota / sample_time, limit / 200 / 10, 5)); } void test_no_starvation(int limit) { std::cout << "\ntest no starvation " << limit << std::endl; bandwidth_manager manager(0); bandwidth_channel t1; bandwidth_channel t2; global_bwc.throttle(limit); const int num_peers = 20; connections_t v1; spawn_connections(v1, manager, t1, num_peers, "p"); connections_t v; std::copy(v1.begin(), v1.end(), std::back_inserter(v)); boost::shared_ptr p( new peer_connection(manager, t2, 1, false, "no-priority")); v.push_back(p); run_test(v, manager); float sum = 0.f; for (connections_t::iterator i = v.begin() , end(v.end()); i != end; ++i) { sum += (*i)->m_quota; } sum /= sample_time; std::cout << sum << " target: " << limit << std::endl; TEST_CHECK(sum > 0); TEST_CHECK(close_to(sum, limit, 50)); std::cout << "non-prioritized rate: " << p->m_quota / sample_time << " target: " << (limit / 200 / num_peers) << std::endl; TEST_CHECK(close_to(p->m_quota / sample_time, limit / 200 / num_peers, 5)); } TORRENT_TEST(equal_connection) { test_equal_connections( 2, 20); test_equal_connections( 2, 2000); test_equal_connections( 2, 20000); test_equal_connections( 3, 20000); test_equal_connections( 5, 20000); test_equal_connections( 7, 20000); test_equal_connections(33, 60000); test_equal_connections(33, 500000); test_equal_connections( 1, 1000000); test_equal_connections( 1, 6000000); } TORRENT_TEST(conn_var_rate) { test_connections_variable_rate( 2, 20, 0); test_connections_variable_rate( 5, 20000, 0); test_connections_variable_rate( 3, 2000, 6000); test_connections_variable_rate( 5, 2000, 30000); test_connections_variable_rate(33, 500000, 0); } TORRENT_TEST(torrents) { test_torrents( 2, 400, 400, 0); test_torrents( 2, 100, 500, 0); test_torrents( 2, 3000, 3000, 6000); test_torrents( 1, 40000, 40000, 0); test_torrents(24, 50000, 50000, 0); test_torrents( 5, 6000, 6000, 3000); test_torrents( 5, 6000, 5000, 4000); test_torrents( 5, 20000, 20000, 30000); } TORRENT_TEST(torrent_var_rate) { test_torrents_variable_rate(5, 6000, 3000); test_torrents_variable_rate(5, 20000, 30000); } TORRENT_TEST(bandwidth_limiter) { test_single_peer(40000, true); test_single_peer(40000, false); } TORRENT_TEST(peer_priority) { test_peer_priority(40000, false); test_peer_priority(40000, true); } TORRENT_TEST(no_starvation) { test_no_starvation(40000); } libtorrent-rasterbar-1.1.13/test/test_bdecode.cpp000066400000000000000000001025461351156116000220620ustar00rootroot00000000000000/* Copyright (c) 2015, 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 "test.hpp" #include "libtorrent/bdecode.hpp" using namespace libtorrent; // test integer TORRENT_TEST(integer) { char b[] = "i12453e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); std::pair section = e.data_section(); TEST_CHECK(std::memcmp(b, section.first, section.second) == 0); TEST_CHECK(section.second == sizeof(b) - 1); TEST_CHECK(e.type() == bdecode_node::int_t); TEST_CHECK(e.int_value() == 12453); } // test string TORRENT_TEST(string) { char b[] = "26:abcdefghijklmnopqrstuvwxyz"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); std::pair section = e.data_section(); TEST_CHECK(std::memcmp(b, section.first, section.second) == 0); TEST_EQUAL(section.second, sizeof(b) - 1); TEST_EQUAL(e.type(), bdecode_node::string_t); TEST_EQUAL(e.string_value(), std::string("abcdefghijklmnopqrstuvwxyz")); TEST_EQUAL(e.string_length(), 26); } // test string-prefix TORRENT_TEST(string_prefix1) { // test edge-case of a string that's nearly too long std::string test; test.resize(1000000 + 8); memcpy(&test[0], "1000000:", 8); // test is a valid bencoded string, that's quite long bdecode_node e; error_code ec; int ret = bdecode(test.c_str(), test.c_str() + test.size(), e, ec); TEST_CHECK(ret == 0); printf("%d bytes string\n", e.string_length()); std::pair section = e.data_section(); TEST_CHECK(std::memcmp(test.c_str(), section.first, section.second) == 0); TEST_EQUAL(section.second, int(test.size())); TEST_EQUAL(e.type(), bdecode_node::string_t); TEST_EQUAL(e.string_length(), 1000000); TEST_EQUAL(e.string_ptr(), test.c_str() + 8); } // test list TORRENT_TEST(list) { char b[] = "li12453e3:aaae"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); std::pair section = e.data_section(); TEST_CHECK(std::memcmp(b, section.first, section.second) == 0); TEST_CHECK(section.second == sizeof(b) - 1); TEST_CHECK(e.type() == bdecode_node::list_t); TEST_CHECK(e.list_size() == 2); TEST_CHECK(e.list_at(0).type() == bdecode_node::int_t); TEST_CHECK(e.list_at(1).type() == bdecode_node::string_t); TEST_CHECK(e.list_at(0).int_value() == 12453); TEST_CHECK(e.list_at(1).string_value() == std::string("aaa")); TEST_CHECK(e.list_at(1).string_length() == 3); section = e.list_at(1).data_section(); TEST_CHECK(std::memcmp("3:aaa", section.first, section.second) == 0); TEST_CHECK(section.second == 5); } // test dict TORRENT_TEST(dict) { char b[] = "d1:ai12453e1:b3:aaa1:c3:bbb1:X10:0123456789e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); printf("%s\n", print_entry(e).c_str()); std::pair section = e.data_section(); TEST_CHECK(std::memcmp(b, section.first, section.second) == 0); TEST_CHECK(section.second == sizeof(b) - 1); TEST_CHECK(e.type() == bdecode_node::dict_t); TEST_CHECK(e.dict_size() == 4); TEST_CHECK(e.dict_find("a").type() == bdecode_node::int_t); TEST_CHECK(e.dict_find("a").int_value() == 12453); TEST_CHECK(e.dict_find("b").type() == bdecode_node::string_t); TEST_CHECK(e.dict_find("b").string_value() == std::string("aaa")); TEST_CHECK(e.dict_find("b").string_length() == 3); TEST_CHECK(e.dict_find("c").type() == bdecode_node::string_t); TEST_CHECK(e.dict_find("c").string_value() == std::string("bbb")); TEST_CHECK(e.dict_find("c").string_length() == 3); TEST_CHECK(e.dict_find_string_value("X") == "0123456789"); } // test dictionary with a key without a value TORRENT_TEST(dict_key_novalue) { char b[] = "d1:ai1e1:be"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 10); TEST_EQUAL(ec, error_code(bdecode_errors::expected_value)); printf("%s\n", print_entry(e).c_str()); } // test dictionary with a key that's not a string TORRENT_TEST(dict_nonstring_key) { char b[] = "di5e1:ae"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 1); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); printf("%s\n", print_entry(e).c_str()); } // dictionary key with \0 TORRENT_TEST(dict_null_key) { char b[] = "d3:a\0bi1ee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); TEST_CHECK(e.dict_size() == 1); bdecode_node d = e.dict_find(std::string("a\0b", 3)); TEST_EQUAL(d.type(), bdecode_node::int_t); TEST_EQUAL(d.int_value(), 1); } // premature e TORRENT_TEST(premature_e) { char b[] = "e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, -1); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } // test strings with negative length-prefix TORRENT_TEST(negative_length_prefix) { char b[] = "-10:foobar"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 0); TEST_EQUAL(ec, error_code(bdecode_errors::expected_value)); printf("%s\n", print_entry(e).c_str()); } // test strings with overflow length-prefix TORRENT_TEST(overflow_length_prefix) { char b[] = "18446744073709551615:foobar"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 19); TEST_EQUAL(ec, error_code(bdecode_errors::overflow)); printf("%s\n", print_entry(e).c_str()); } // test strings with almost overflow (more than 8 digits) TORRENT_TEST(close_overflow_length_prefix) { char b[] = "99999999:foobar"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 8); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } // test strings with overflow (more than 8 digits) TORRENT_TEST(overflow_length_prefix2) { char b[] = "199999999:foobar"; bdecode_node e; error_code ec; int pos; // pretend that we have a large buffer like that int ret = bdecode(b, b + 999999999, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 0); TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded)); printf("%s\n", print_entry(e).c_str()); } // test integer without any digits TORRENT_TEST(nodigit_int) { char b[] = "ie"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 1); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); printf("%s\n", print_entry(e).c_str()); } // test integer with just a minus TORRENT_TEST(minus_int) { char b[] = "i-e"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 2); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); printf("%s\n", print_entry(e).c_str()); } // test integer with a minus inserted in it TORRENT_TEST(interior_minus_int) { char b[] = "i35412-5633e"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 6); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); printf("%s\n", print_entry(e).c_str()); } // test integers that don't fit in 64 bits TORRENT_TEST(int_overflow) { char b[] = "i18446744073709551615e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); printf("%s\n", print_entry(e).c_str()); // the lazy aspect makes this overflow when asking for // the value. turning it to zero. TEST_EQUAL(e.int_value(), 0); } // test integers with more than 20 digits (overflow on parsing) TORRENT_TEST(int_overflow2) { char b[] = "i184467440737095516154e"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 22); TEST_EQUAL(ec, error_code(bdecode_errors::overflow)); printf("%s\n", print_entry(e).c_str()); } // test truncated negative integer TORRENT_TEST(int_truncated) { char b[] = "i-"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 2); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } // bdecode_error TORRENT_TEST(bdecode_error) { error_code ec(bdecode_errors::overflow); TEST_EQUAL(ec.message(), "integer overflow"); TEST_EQUAL(ec.category().name(), std::string("bdecode")); ec.assign(5434, get_bdecode_category()); TEST_EQUAL(ec.message(), "Unknown error"); } // test integers that just exactly fit in 64 bits TORRENT_TEST(64bit_int) { char b[] = "i9223372036854775807e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); TEST_CHECK(e.int_value() == 9223372036854775807LL); } // test integers that just exactly fit in 64 bits TORRENT_TEST(64bit_int_negative) { char b[] = "i-9223372036854775807e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); TEST_CHECK(e.int_value() == -9223372036854775807LL); } // test integers that have invalid digits TORRENT_TEST(int_invalid_digit) { char b[] = "i92337203t854775807e"; bdecode_node e; error_code ec; int pos = 0; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 9); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); printf("%s\n", print_entry(e).c_str()); } // test invalid encoding TORRENT_TEST(invalid_encoding) { unsigned char buf[] = { 0x64, 0x31, 0x3a, 0x61, 0x64, 0x32, 0x3a, 0x69 , 0x64, 0x32, 0x30, 0x3a, 0x2a, 0x21, 0x19, 0x89 , 0x9f, 0xcd, 0x5f, 0xc9, 0xbc, 0x80, 0xc1, 0x76 , 0xfe, 0xe0, 0xc6, 0x84, 0x2d, 0xf6, 0xfc, 0xb8 , 0x39, 0x3a, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x68 , 0x61, 0xae, 0x68, 0x32, 0x30, 0x3a, 0x14, 0x78 , 0xd5, 0xb0, 0xdc, 0xf6, 0x82, 0x42, 0x32, 0xa0 , 0xd6, 0x88, 0xeb, 0x48, 0x57, 0x01, 0x89, 0x40 , 0x4e, 0xbc, 0x65, 0x31, 0x3a, 0x71, 0x39, 0x3a , 0x67, 0x65, 0x74, 0x5f, 0x70, 0x65, 0x65, 0x72 , 0x78, 0xff, 0x3a, 0x74, 0x38, 0x3a, 0xaa, 0xd4 , 0xa1, 0x88, 0x7a, 0x8d, 0xc3, 0xd6, 0x31, 0x3a , 0x79, 0x31, 0xae, 0x71, 0x65, 0}; printf("%s\n", buf); bdecode_node e; error_code ec; int ret = bdecode((char*)buf, (char*)buf + sizeof(buf), e, ec); TEST_CHECK(ret == -1); } // test the depth limit TORRENT_TEST(depth_limit) { char b[2048]; for (int i = 0; i < 1024; ++i) b[i]= 'l'; for (int i = 1024; i < 2048; ++i) b[i]= 'e'; // 1024 levels nested lists bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b), e, ec, NULL, 100); TEST_CHECK(ret != 0); TEST_EQUAL(ec, error_code(bdecode_errors::depth_exceeded , get_bdecode_category())); } // test the item limit TORRENT_TEST(item_limit) { char b[10240]; b[0] = 'l'; int i = 1; for (i = 1; i < 10239; i += 2) memcpy(&b[i], "0:", 2); b[i] = 'e'; bdecode_node e; error_code ec; int ret = bdecode(b, b + i + 1, e, ec, NULL, 1000, 1000); TEST_CHECK(ret != 0); TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded , get_bdecode_category())); } // test unexpected EOF TORRENT_TEST(unepected_eof) { char b[] = "l2:.."; // expected terminating 'e' bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 5); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } // test unexpected EOF in string length TORRENT_TEST(unepected_eof2) { char b[] = "l2:..0"; // expected ':' delimiter instead of EOF bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 6); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } // test expected string TORRENT_TEST(expected_string) { char b[] = "di2ei0ee"; // expected string (dict keys must be strings) bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 1); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); printf("%s\n", print_entry(e).c_str()); } // test unexpected EOF while parsing dict key TORRENT_TEST(unexpected_eof_dict_key) { char b[] = "d1000:..e"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 5); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } // test unexpected EOF while parsing dict key TORRENT_TEST(unexpected_eof_dict_key2) { char b[] = "d1000:"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 5); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } // test expected string while parsing dict key TORRENT_TEST(expected_string_dict_key2) { char b[] = "df00:"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 1); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); printf("%s\n", print_entry(e).c_str()); } // test unexpected EOF while parsing int TORRENT_TEST(unexpected_eof_int) { char b[] = "i"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 1); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } // test unexpected EOF while parsing int TORRENT_TEST(unexpected_eof_int2) { char b[] = "i10"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 3); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } // test expected colon TORRENT_TEST(expected_colon_dict) { char b[] = "d1000"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 5); TEST_EQUAL(ec, error_code(bdecode_errors::expected_colon)); printf("%s\n", print_entry(e).c_str()); } // test empty string TORRENT_TEST(empty_string) { char b[] = ""; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_EQUAL(ret, -1); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } // test partial string TORRENT_TEST(partial_string) { char b[] = "100:.."; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 3); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } TORRENT_TEST(list_ints) { std::string buf; buf += "l"; for (int i = 0; i < 1000; ++i) { char tmp[20]; snprintf(tmp, sizeof(tmp), "i%de", i); buf += tmp; } buf += "e"; bdecode_node e; error_code ec; int ret = bdecode((char*)&buf[0], (char*)&buf[0] + buf.size(), e, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_size(), 1000); for (int i = 0; i < 1000; ++i) { TEST_EQUAL(e.list_int_value_at(i), i); } } TORRENT_TEST(dict_ints) { std::string buf; buf += "d"; for (int i = 0; i < 1000; ++i) { char tmp[30]; snprintf(tmp, sizeof(tmp), "4:%04di%de", i, i); buf += tmp; } buf += "e"; printf("%s\n", buf.c_str()); bdecode_node e; error_code ec; int ret = bdecode((char*)&buf[0], (char*)&buf[0] + buf.size(), e, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::dict_t); TEST_EQUAL(e.dict_size(), 1000); for (int i = 0; i < 1000; ++i) { char tmp[30]; snprintf(tmp, sizeof(tmp), "%04d", i); TEST_EQUAL(e.dict_find_int_value(tmp), i); } } // test dict_at TORRENT_TEST(dict_at) { char b[] = "d3:fooi1e3:bari2ee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::dict_t); TEST_EQUAL(e.dict_size(), 2); TEST_EQUAL(e.dict_at(0).first, "foo"); TEST_EQUAL(e.dict_at(0).second.type(), bdecode_node::int_t); TEST_EQUAL(e.dict_at(0).second.int_value(), 1); TEST_EQUAL(e.dict_at(1).first, "bar"); TEST_EQUAL(e.dict_at(1).second.type(), bdecode_node::int_t); TEST_EQUAL(e.dict_at(1).second.int_value(), 2); } // test string_ptr TORRENT_TEST(string_ptr) { char b[] = "l3:fooe"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_size(), 1); TEST_EQUAL(e.list_at(0).type(), bdecode_node::string_t); TEST_EQUAL(e.list_at(0).string_ptr(), b + 3); TEST_EQUAL(e.list_at(0).string_length(), 3); } // test exceeding buffer size limit TORRENT_TEST(exceed_buf_limit) { char b[] = "l3:fooe"; bdecode_node e; error_code ec; int ret = bdecode(b, b + 0x3fffffff, e, ec); TEST_EQUAL(ret, -1); TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded)); printf("%s\n", print_entry(e).c_str()); } // test parse_int TORRENT_TEST(parse_int) { char b[] = "1234567890e"; boost::int64_t val = 0; bdecode_errors::error_code_enum ec = bdecode_errors::no_error; char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec); TEST_EQUAL(ec, bdecode_errors::no_error); TEST_EQUAL(val, 1234567890); TEST_EQUAL(e, b + sizeof(b) - 2); } // test invalid digit TORRENT_TEST(invalid_digit) { char b[] = "0o"; boost::int64_t val = 0; bdecode_errors::error_code_enum ec; char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec); TEST_EQUAL(ec, bdecode_errors::expected_digit); TEST_EQUAL(e, b + 1); } // test parse_int overflow TORRENT_TEST(parse_int_overflow) { char b[] = "9223372036854775808:"; boost::int64_t val = 0; bdecode_errors::error_code_enum ec; char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec); TEST_EQUAL(ec, bdecode_errors::overflow); TEST_EQUAL(e, b + 18); } TORRENT_TEST(parse_length_overflow) { char const* b[] = { "d1:a1919191010:11111", "d2143289344:a4:aaaae", "d214328934114:a4:aaaae", "d9205357638345293824:a4:aaaae", "d1:a9205357638345293824:11111", }; for (int i = 0; i < int(sizeof(b)/sizeof(b[0])); ++i) { error_code ec; bdecode_node e; int ret = bdecode(b[i], b[i] + strlen(b[i]), e, ec); TEST_EQUAL(ret, -1); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); } } TORRENT_TEST(expected_colon_string) { char b[] = "928"; boost::int64_t val = 0; bdecode_errors::error_code_enum ec = bdecode_errors::no_error; char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec); TEST_EQUAL(ec, bdecode_errors::no_error); TEST_EQUAL(e, b + 3); } // test dict_find_* functions TORRENT_TEST(dict_find_funs) { // a: int // b: string // c: list // d: dict char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(e.type(), bdecode_node::dict_t); // dict_find_int* TEST_EQUAL(e.dict_find_int_value("a"), 1); TEST_EQUAL(e.dict_find_int("a").type(), bdecode_node::int_t); TEST_EQUAL(e.dict_find_int_value("b", -10), -10); TEST_EQUAL(e.dict_find_int_value("x", -10), -10); TEST_EQUAL(e.dict_find_int("b").type(), bdecode_node::none_t); TEST_EQUAL(e.dict_find_int("x").type(), bdecode_node::none_t); // dict_find_string* TEST_EQUAL(e.dict_find_string_value("b"), "foo"); TEST_EQUAL(e.dict_find_string("b").type(), bdecode_node::string_t); TEST_EQUAL(e.dict_find_string_value("c", "blah"), "blah"); TEST_EQUAL(e.dict_find_string_value("x", "blah"), "blah"); TEST_EQUAL(e.dict_find_string("c").type(), bdecode_node::none_t); TEST_EQUAL(e.dict_find_string("x").type(), bdecode_node::none_t); // dict_find_list TEST_CHECK(e.dict_find_list("c")); TEST_EQUAL(e.dict_find_list("c").list_size(), 2); TEST_EQUAL(e.dict_find_list("c").list_int_value_at(0), 1); TEST_EQUAL(e.dict_find_list("c").list_int_value_at(1), 2); TEST_CHECK(!e.dict_find_list("d")); // dict_find_dict TEST_CHECK(e.dict_find_dict("d")); TEST_EQUAL(e.dict_find_dict("d").dict_find_int_value("x"), 1); TEST_EQUAL(e.dict_find_dict("d").dict_find_int_value("y", -10), -10); TEST_CHECK(!e.dict_find_dict("c")); // variants taking std::string TEST_EQUAL(e.dict_find_dict(std::string("d")).dict_find_int_value("x"), 1); TEST_CHECK(!e.dict_find_dict(std::string("c"))); TEST_CHECK(!e.dict_find_dict(std::string("x"))); TEST_EQUAL(e.dict_size(), 4); TEST_EQUAL(e.dict_size(), 4); // dict_at TEST_EQUAL(e.dict_at(0).first, "a"); TEST_EQUAL(e.dict_at(0).second.int_value(), 1); TEST_EQUAL(e.dict_at(1).first, "b"); TEST_EQUAL(e.dict_at(1).second.string_value(), "foo"); TEST_EQUAL(e.dict_at(2).first, "c"); TEST_EQUAL(e.dict_at(2).second.type(), bdecode_node::list_t); TEST_EQUAL(e.dict_at(3).first, "d"); TEST_EQUAL(e.dict_at(3).second.type(), bdecode_node::dict_t); } // test list_*_at functions TORRENT_TEST(list_at_funs) { // int // string // list // dict char b[] = "li1e3:fooli1ei2eed1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_int_value_at(0), 1); // make sure default values work TEST_EQUAL(e.list_int_value_at(1, -10), -10); TEST_EQUAL(e.list_string_value_at(1), "foo"); // make sure default values work TEST_EQUAL(e.list_string_value_at(2, "blah"), "blah"); TEST_EQUAL(e.list_at(2).type(), bdecode_node::list_t); TEST_EQUAL(e.list_at(2).list_size(), 2); TEST_EQUAL(e.list_at(2).list_int_value_at(0), 1); TEST_EQUAL(e.list_at(2).list_int_value_at(1), 2); TEST_EQUAL(e.list_at(3).type(), bdecode_node::dict_t); TEST_EQUAL(e.list_at(3).dict_size(), 1); TEST_EQUAL(e.list_at(3).dict_find_int_value("x"), 1); TEST_EQUAL(e.list_at(3).dict_find_int_value("y", -10), -10); TEST_EQUAL(e.list_size(), 4); TEST_EQUAL(e.list_size(), 4); } // test list_at in reverse order TORRENT_TEST(list_at_reverse) { // int // string // list // dict char b[] = "li1e3:fooli1ei2eed1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_at(3).type(), bdecode_node::dict_t); TEST_EQUAL(e.list_at(2).type(), bdecode_node::list_t); TEST_EQUAL(e.list_string_value_at(1), "foo"); TEST_EQUAL(e.list_int_value_at(0), 1); TEST_EQUAL(e.list_size(), 4); TEST_EQUAL(e.list_size(), 4); } // test dict_find_* functions TORRENT_TEST(dict_find_funs2) { // a: int // b: string // c: list // d: dict char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(e.type(), bdecode_node::dict_t); // try finding the last item in a dict (to skip all the other ones) TEST_EQUAL(e.dict_find("d").type(), bdecode_node::dict_t); TEST_EQUAL(e.dict_find(std::string("d")).type(), bdecode_node::dict_t); } // print_entry TORRENT_TEST(print_entry) { char b[] = "li1e3:fooli1ei2eed1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "[ 1, 'foo', [ 1, 2 ], { 'x': 1 } ]"); } TORRENT_TEST(print_entry2) { char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': { 'x': 1 } }"); } // test swap() TORRENT_TEST(swap) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; char b2[] = "i1e"; bdecode_node e1; bdecode_node e2; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec); TEST_EQUAL(ret, 0); ret = bdecode(b2, b2 + sizeof(b2)-1, e2, ec); TEST_EQUAL(ret, 0); std::string str1 = print_entry(e1); std::string str2 = print_entry(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::int_t); printf("%s\n", print_entry(e1).c_str()); e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::int_t); TEST_EQUAL(e2.type(), bdecode_node::dict_t); TEST_EQUAL(print_entry(e1), str2); TEST_EQUAL(print_entry(e2), str1); printf("%s\n", print_entry(e1).c_str()); e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::int_t); TEST_EQUAL(print_entry(e1), str1); TEST_EQUAL(print_entry(e2), str2); printf("%s\n", print_entry(e1).c_str()); } // test swap() (one node is the root of the other node) TORRENT_TEST(swap_root) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e1; bdecode_node e2; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec); TEST_EQUAL(ret, 0); e2 = e1.dict_find("c").list_at(0); std::string str1 = print_entry(e1); std::string str2 = print_entry(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::int_t); printf("%s\n", print_entry(e1).c_str()); e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::int_t); TEST_EQUAL(e2.type(), bdecode_node::dict_t); TEST_EQUAL(print_entry(e1), str2); TEST_EQUAL(print_entry(e2), str1); printf("%s\n", print_entry(e1).c_str()); // swap back e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::int_t); TEST_EQUAL(print_entry(e1), str1); TEST_EQUAL(print_entry(e2), str2); printf("%s\n", print_entry(e1).c_str()); } // test swap() (neither is a root and they don't share a root) TORRENT_TEST(swap_disjoint) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; char b2[] = "li1e3:fooli1ei2eed1:xi1eee"; bdecode_node e1_root; bdecode_node e2_root; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1_root, ec); TEST_EQUAL(ret, 0); ret = bdecode(b2, b2 + sizeof(b2)-1, e2_root, ec); TEST_EQUAL(ret, 0); bdecode_node e1 = e1_root.dict_find("c").list_at(0); bdecode_node e2 = e2_root.list_at(1); std::string str1 = print_entry(e1); std::string str2 = print_entry(e2); TEST_EQUAL(e1.type(), bdecode_node::int_t); TEST_EQUAL(e2.type(), bdecode_node::string_t); e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::string_t); TEST_EQUAL(e2.type(), bdecode_node::int_t); TEST_EQUAL(print_entry(e1), str2); TEST_EQUAL(print_entry(e2), str1); // swap back e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::int_t); TEST_EQUAL(e2.type(), bdecode_node::string_t); TEST_EQUAL(print_entry(e1), str1); TEST_EQUAL(print_entry(e2), str2); } // test swap() (one is a root and they don't share a root) TORRENT_TEST(swap_root_disjoint) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; char b2[] = "li1e3:fooli1ei2eed1:xi1eee"; bdecode_node e1_root; bdecode_node e2; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1_root, ec); TEST_EQUAL(ret, 0); ret = bdecode(b2, b2 + sizeof(b2)-1, e2, ec); TEST_EQUAL(ret, 0); bdecode_node e1 = e1_root.dict_find("d"); std::string str1 = print_entry(e1); std::string str2 = print_entry(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::list_t); e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::list_t); TEST_EQUAL(e2.type(), bdecode_node::dict_t); TEST_EQUAL(print_entry(e1), str2); TEST_EQUAL(print_entry(e2), str1); // swap back e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::list_t); TEST_EQUAL(print_entry(e1), str1); TEST_EQUAL(print_entry(e2), str2); } // make sure it's safe to reuse bdecode_nodes after clear() is called TORRENT_TEST(clear) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; char b2[] = "li1ei2ee"; bdecode_node e; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e, ec); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::dict_t); TEST_EQUAL(e.dict_size(), 4); TEST_EQUAL(e.dict_at(1).first, "b"); ret = bdecode(b2, b2 + sizeof(b2)-1, e, ec); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_size(), 2); TEST_EQUAL(e.list_int_value_at(1), 2); } // assignment/copy of root nodes TORRENT_TEST(copy_root) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e1; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e1.type(), bdecode_node::dict_t); printf("%s\n", print_entry(e1).c_str()); bdecode_node e2(e1); bdecode_node e3; e3 = e1; e1.clear(); TEST_EQUAL(e2.type(), bdecode_node::dict_t); TEST_EQUAL(e2.dict_size(), 4); TEST_EQUAL(e2.dict_at(1).first, "b"); TEST_EQUAL(e3.type(), bdecode_node::dict_t); TEST_EQUAL(e3.dict_size(), 4); TEST_EQUAL(e3.dict_at(1).first, "b"); } // non-owning references TORRENT_TEST(non_owning_refs) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e1; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e1.type(), bdecode_node::dict_t); printf("%s\n", print_entry(e1).c_str()); bdecode_node e2 = e1.non_owning(); TEST_EQUAL(e2.type(), bdecode_node::dict_t); e1.clear(); // e2 is invalid now } // test that a partial parse can be still be printed up to the // point where it faild TORRENT_TEST(partial_parse) { char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1-eee"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 35); TEST_EQUAL(e.type(), bdecode_node::dict_t); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': { 'x': {} } }"); } TORRENT_TEST(partial_parse2) { char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:d-d1:xi1eee"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 29); TEST_EQUAL(e.type(), bdecode_node::dict_t); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': {} }"); } TORRENT_TEST(partial_parse3) { char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee-1:dd1:xi1eee"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 26); TEST_EQUAL(e.type(), bdecode_node::dict_t); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ] }"); } TORRENT_TEST(partial_parse4) { char b[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 22); TEST_EQUAL(e.type(), bdecode_node::dict_t); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1 ] }"); } TORRENT_TEST(partial_parse_string) { // it's important to not have a null terminator here // to allow address sanitizer to trigger in case the decoder reads past the // end char b[] = { '5', '5'}; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b), e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 2); } // test switch_underlying_buffer TORRENT_TEST(switch_buffer) { char b1[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee"; char b2[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b1, b1 + sizeof(b1)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 22); TEST_EQUAL(e.type(), bdecode_node::dict_t); std::string string1 = print_entry(e); printf("%s\n", string1.c_str()); e.switch_underlying_buffer(b2); std::string string2 = print_entry(e); printf("%s\n", string2.c_str()); TEST_EQUAL(string1, string2); } libtorrent-rasterbar-1.1.13/test/test_bencoding.cpp000066400000000000000000000452251351156116000224250ustar00rootroot00000000000000/* Copyright (c) 2008, 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/bencode.hpp" #include #include #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/lazy_entry.hpp" #endif #include "test.hpp" using namespace libtorrent; // test vectors from bittorrent protocol description // http://www.bittorrent.com/protocol.html std::string encode(entry const& e) { std::string ret; bencode(std::back_inserter(ret), e); return ret; } entry decode(std::string const& str) { return bdecode(str.begin(), str.end()); } TORRENT_TEST(strings) { entry e("spam"); TEST_CHECK(encode(e) == "4:spam"); TEST_CHECK(decode(encode(e)) == e); } TORRENT_TEST(integers) { entry e(3); TEST_CHECK(encode(e) == "i3e"); TEST_CHECK(decode(encode(e)) == e); } TORRENT_TEST(integers2) { entry e(-3); TEST_CHECK(encode(e) == "i-3e"); TEST_CHECK(decode(encode(e)) == e); } TORRENT_TEST(integers3) { entry e(int(0)); TEST_CHECK(encode(e) == "i0e"); TEST_CHECK(decode(encode(e)) == e); } TORRENT_TEST(lists) { entry::list_type l; l.push_back(entry("spam")); l.push_back(entry("eggs")); entry e(l); TEST_CHECK(encode(e) == "l4:spam4:eggse"); TEST_CHECK(decode(encode(e)) == e); } TORRENT_TEST(dictionaries) { entry e(entry::dictionary_t); e["spam"] = entry("eggs"); e["cow"] = entry("moo"); TEST_CHECK(encode(e) == "d3:cow3:moo4:spam4:eggse"); TEST_CHECK(decode(encode(e)) == e); } TORRENT_TEST(preformatted) { entry e(entry::preformatted_t); char const str[] = "foobar"; e.preformatted().assign(str, str + sizeof(str)-1); TEST_EQUAL(encode(e), "foobar"); } TORRENT_TEST(preformatted_node) { entry e(entry::dictionary_t); char const str[] = "foobar"; e["info"] = entry::preformatted_type(str, str + sizeof(str)-1); TEST_EQUAL(encode(e), "d4:infofoobare"); } TORRENT_TEST(undefined_node) { entry e(entry::undefined_t); TEST_EQUAL(encode(e), "0:"); } TORRENT_TEST(undefined_node2) { entry e(entry::dictionary_t); e["info"] = entry(entry::undefined_t); TEST_EQUAL(encode(e), "d4:info0:e"); } TORRENT_TEST(print_dict_single_line) { entry e; e["foo"] = "bar"; e["bar"] = "foo"; TEST_EQUAL(e.to_string(true), "{ 'bar': 'foo', 'foo': 'bar' }"); } TORRENT_TEST(print_dict) { entry e; e["foo"] = "bar"; e["bar"] = "foo"; TEST_EQUAL(e.to_string(), "{\n 'bar': 'foo',\n 'foo': 'bar' }"); } TORRENT_TEST(print_list_single_line) { entry e; e.list().push_back(entry("foo")); e.list().push_back(entry("bar")); TEST_EQUAL(e.to_string(true), "[ 'foo', 'bar' ]"); } TORRENT_TEST(print_list) { entry e; e.list().push_back(entry("foo")); e.list().push_back(entry("bar")); TEST_EQUAL(e.to_string(), "[\n 'foo',\n 'bar' ]"); } TORRENT_TEST(print_int_single_line) { entry e(1337); TEST_EQUAL(e.to_string(true), "1337"); } TORRENT_TEST(print_int) { entry e(1337); TEST_EQUAL(e.to_string(), "1337"); } TORRENT_TEST(print_string_single_line) { entry e("foobar"); TEST_EQUAL(e.to_string(true), "'foobar'"); } TORRENT_TEST(print_string) { entry e("foobar"); TEST_EQUAL(e.to_string(), "'foobar'"); } TORRENT_TEST(print_deep_dict_single_line) { entry e; e["strings"].list().push_back(entry("foo")); e["strings"].list().push_back(entry("bar")); e["ints"].list().push_back(entry(1)); e["ints"].list().push_back(entry(2)); e["ints"].list().push_back(entry(3)); e["a"] = "foobar"; TEST_EQUAL(e.to_string(true), "{ 'a': 'foobar', 'ints': [ 1, 2, 3 ], 'strings': [ 'foo', 'bar' ] }"); } TORRENT_TEST(print_deep_dict) { entry e; e["strings"].list().push_back(entry("foo")); e["strings"].list().push_back(entry("bar")); e["ints"].list().push_back(entry(1)); e["ints"].list().push_back(entry(2)); e["ints"].list().push_back(entry(3)); e["a"] = "foobar"; TEST_EQUAL(e.to_string(), "{\n 'a': 'foobar',\n 'ints': [\n 1,\n 2,\n 3 ],\n 'strings': [\n 'foo',\n 'bar' ] }"); } #ifndef TORRENT_NO_DEPRECATE TORRENT_TEST(lazy_entry) { { char b[] = "i12453e"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); std::pair section = e.data_section(); TEST_CHECK(std::memcmp(b, section.first, section.second) == 0); TEST_CHECK(section.second == sizeof(b) - 1); TEST_CHECK(e.type() == lazy_entry::int_t); TEST_CHECK(e.int_value() == 12453); } { char b[] = "26:abcdefghijklmnopqrstuvwxyz"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); std::pair section = e.data_section(); TEST_CHECK(std::memcmp(b, section.first, section.second) == 0); TEST_CHECK(section.second == sizeof(b) - 1); TEST_CHECK(e.type() == lazy_entry::string_t); TEST_CHECK(e.string_value() == std::string("abcdefghijklmnopqrstuvwxyz")); TEST_CHECK(e.string_length() == 26); } { char b[] = "li12453e3:aaae"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); std::pair section = e.data_section(); TEST_CHECK(std::memcmp(b, section.first, section.second) == 0); TEST_CHECK(section.second == sizeof(b) - 1); TEST_CHECK(e.type() == lazy_entry::list_t); TEST_CHECK(e.list_size() == 2); TEST_CHECK(e.list_at(0)->type() == lazy_entry::int_t); TEST_CHECK(e.list_at(1)->type() == lazy_entry::string_t); TEST_CHECK(e.list_at(0)->int_value() == 12453); TEST_CHECK(e.list_at(1)->string_value() == std::string("aaa")); TEST_CHECK(e.list_at(1)->string_length() == 3); section = e.list_at(1)->data_section(); TEST_CHECK(std::memcmp("3:aaa", section.first, section.second) == 0); TEST_CHECK(section.second == 5); } { char b[] = "d1:ai12453e1:b3:aaa1:c3:bbb1:X10:0123456789e"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); std::pair section = e.data_section(); TEST_CHECK(std::memcmp(b, section.first, section.second) == 0); TEST_CHECK(section.second == sizeof(b) - 1); TEST_CHECK(e.type() == lazy_entry::dict_t); TEST_CHECK(e.dict_size() == 4); TEST_CHECK(e.dict_find("a")->type() == lazy_entry::int_t); TEST_CHECK(e.dict_find("a")->int_value() == 12453); TEST_CHECK(e.dict_find("b")->type() == lazy_entry::string_t); TEST_CHECK(e.dict_find("b")->string_value() == std::string("aaa")); TEST_CHECK(e.dict_find("b")->string_length() == 3); TEST_CHECK(e.dict_find("c")->type() == lazy_entry::string_t); TEST_CHECK(e.dict_find("c")->string_value() == std::string("bbb")); TEST_CHECK(e.dict_find("c")->string_length() == 3); TEST_CHECK(e.dict_find_string_value("X") == "0123456789"); } // dictionary key with \0 { char b[] = "d3:a\0bi1ee"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); TEST_CHECK(e.dict_size() == 1); lazy_entry* d = e.dict_find(std::string("a\0b", 3)); TEST_CHECK(d); TEST_EQUAL(d->type(), lazy_entry::int_t); TEST_EQUAL(d->int_value(), 1); } // test strings with negative length-prefix { char b[] = "-10:foobar"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_CHECK(ec == error_code(bdecode_errors::expected_value , get_bdecode_category())); } // test strings with overflow length-prefix { char b[] = "18446744073709551615:foobar"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_CHECK(ec == error_code(bdecode_errors::overflow , get_bdecode_category())); } // test integers that don't fit in 64 bits { char b[] = "i18446744073709551615e"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); // the lazy aspect makes this overflow when asking for // the value. turning it to zero. TEST_CHECK(e.int_value() == 0); } // test integers that just exactly fit in 64 bits { char b[] = "i9223372036854775807e"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); TEST_CHECK(e.int_value() == 9223372036854775807LL); } // test integers that just exactly fit in 64 bits { char b[] = "i-9223372036854775807e"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); TEST_CHECK(ret == 0); printf("%s\n", print_entry(e).c_str()); TEST_CHECK(e.int_value() == -9223372036854775807LL); } // test invalid encoding { unsigned char buf[] = { 0x64, 0x31, 0x3a, 0x61, 0x64, 0x32, 0x3a, 0x69 , 0x64, 0x32, 0x30, 0x3a, 0x2a, 0x21, 0x19, 0x89 , 0x9f, 0xcd, 0x5f, 0xc9, 0xbc, 0x80, 0xc1, 0x76 , 0xfe, 0xe0, 0xc6, 0x84, 0x2d, 0xf6, 0xfc, 0xb8 , 0x39, 0x3a, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x68 , 0x61, 0xae, 0x68, 0x32, 0x30, 0x3a, 0x14, 0x78 , 0xd5, 0xb0, 0xdc, 0xf6, 0x82, 0x42, 0x32, 0xa0 , 0xd6, 0x88, 0xeb, 0x48, 0x57, 0x01, 0x89, 0x40 , 0x4e, 0xbc, 0x65, 0x31, 0x3a, 0x71, 0x39, 0x3a , 0x67, 0x65, 0x74, 0x5f, 0x70, 0x65, 0x65, 0x72 , 0x78, 0xff, 0x3a, 0x74, 0x38, 0x3a, 0xaa, 0xd4 , 0xa1, 0x88, 0x7a, 0x8d, 0xc3, 0xd6, 0x31, 0x3a , 0x79, 0x31, 0xae, 0x71, 0x65, 0}; printf("%s\n", buf); lazy_entry e; error_code ec; int ret = lazy_bdecode((char*)buf, (char*)buf + sizeof(buf), e, ec); TEST_CHECK(ret == -1); } // test the depth limit { char b[2048]; for (int i = 0; i < 1024; ++i) b[i]= 'l'; for (int i = 1024; i < 2048; ++i) b[i]= 'e'; // 1024 levels nested lists lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b), e, ec); TEST_CHECK(ret != 0); TEST_EQUAL(ec, error_code(bdecode_errors::depth_exceeded , get_bdecode_category())); } // test the item limit { char b[10240]; b[0] = 'l'; int i = 1; for (i = 1; i < 10239; i += 2) memcpy(&b[i], "0:", 2); b[i] = 'e'; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + i + 1, e, ec, NULL, 1000, 1000); TEST_CHECK(ret != 0); TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded , get_bdecode_category())); } // test unexpected EOF { char b[] = "l2:.."; // expected terminating 'e' lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof , get_bdecode_category())); } // test unexpected EOF (really expected terminator) { char b[] = "l2:..0"; // expected terminating 'e' instead of '0' lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof , get_bdecode_category())); } // test expected string { char b[] = "di2ei0ee"; // expected string (dict keys must be strings) lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit , get_bdecode_category())); } // test unexpected EOF while parsing dict key { char b[] = "d1000:..e"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof , get_bdecode_category())); } // test unexpected EOF while parsing dict key { char b[] = "d1000:"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof , get_bdecode_category())); } // test expected string while parsing dict key { char b[] = "df00:"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit , get_bdecode_category())); } // test unexpected EOF while parsing int { char b[] = "i"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof , get_bdecode_category())); } // test unexpected EOF while parsing int { char b[] = "i10"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof , get_bdecode_category())); } // test expected colon { char b[] = "d1000"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ec, error_code(bdecode_errors::expected_colon , get_bdecode_category())); } // test empty string { char b[] = ""; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_EQUAL(ret, -1); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof , get_bdecode_category())); printf("%s\n", print_entry(e).c_str()); } // test partial string { char b[] = "100:.."; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_CHECK(ret != 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof , get_bdecode_category())); } // test pascal string dict { char b[] = "d6:foobar6:barfooe"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_EQUAL(ret, 0); printf("%s\n", print_entry(e).c_str()); pascal_string ps = e.dict_find_pstr("foobar"); TEST_EQUAL(memcmp(ps.ptr, "barfoo", ps.len), 0); TEST_EQUAL(ps.len, 6); ps = e.dict_find_pstr("foobar2"); TEST_EQUAL(ps.ptr, static_cast(0)); TEST_EQUAL(ps.len, 0); } // test pascal string in list { char b[] = "l6:foobari4ee"; lazy_entry e; error_code ec; int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec, NULL); TEST_EQUAL(ret, 0); printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(e.list_size(), 2); pascal_string ps = e.list_pstr_at(0); TEST_EQUAL(memcmp(ps.ptr, "foobar", ps.len), 0); TEST_EQUAL(ps.len, 6); ps = e.list_pstr_at(1); TEST_EQUAL(ps.ptr, static_cast(0)); TEST_EQUAL(ps.len, 0); } { unsigned char buf[] = { 0x44, 0x91, 0x3a }; entry ent = bdecode(buf, buf + sizeof(buf)); TEST_CHECK(ent == entry()); } { std::string buf; buf += "l"; for (int i = 0; i < 1000; ++i) { char tmp[20]; snprintf(tmp, sizeof(tmp), "i%de", i); buf += tmp; } buf += "e"; lazy_entry e; error_code ec; int ret = lazy_bdecode((char*)&buf[0], (char*)&buf[0] + buf.size(), e, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), lazy_entry::list_t); TEST_EQUAL(e.list_size(), 1000); for (int i = 0; i < 1000; ++i) { TEST_EQUAL(e.list_int_value_at(i), i); } } { std::string buf; buf += "d"; for (int i = 0; i < 1000; ++i) { char tmp[30]; snprintf(tmp, sizeof(tmp), "4:%04di%de", i, i); buf += tmp; } buf += "e"; printf("%s\n", buf.c_str()); lazy_entry e; error_code ec; int ret = lazy_bdecode((char*)&buf[0], (char*)&buf[0] + buf.size(), e, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), lazy_entry::dict_t); TEST_EQUAL(e.dict_size(), 1000); for (int i = 0; i < 1000; ++i) { char tmp[30]; snprintf(tmp, sizeof(tmp), "%04d", i); TEST_EQUAL(e.dict_find_int_value(tmp), i); } } // test parse_int { char b[] = "1234567890e"; boost::int64_t val = 0; bdecode_errors::error_code_enum ec = bdecode_errors::no_error; char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec); TEST_CHECK(ec == bdecode_errors::no_error); TEST_EQUAL(val, 1234567890); TEST_EQUAL(e, b + sizeof(b) - 2); } // test invalid digit { char b[] = "0o"; boost::int64_t val = 0; bdecode_errors::error_code_enum ec; char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec); TEST_EQUAL(ec, bdecode_errors::expected_digit); TEST_EQUAL(e, b + 1); } { char b[] = "9223372036854775808:"; boost::int64_t val = 0; bdecode_errors::error_code_enum ec; char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec); TEST_CHECK(ec == bdecode_errors::overflow); TEST_EQUAL(e, b + 18); } { char b[] = "928"; boost::int64_t val = 0; bdecode_errors::error_code_enum ec = bdecode_errors::no_error; char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec); TEST_CHECK(ec == bdecode_errors::no_error); TEST_EQUAL(e, b + 3); } { char const* b[] = { "d1:a1919191010:11111", "d2143289344:a4:aaaae", "d214328934114:a4:aaaae", "d9205357638345293824:a4:aaaae", "d1:a9205357638345293824:11111", }; for (int i = 0; i < int(sizeof(b)/sizeof(b[0])); ++i) { lazy_entry e; error_code ec; int ret = lazy_bdecode(b[i], b[i] + strlen(b[i]), e, ec, NULL); TEST_EQUAL(ret, -1); TEST_CHECK(ec == error_code(bdecode_errors::unexpected_eof)); printf("%s\n", print_entry(e).c_str()); } } } #endif // TORRENT_NO_DEPRECATE libtorrent-rasterbar-1.1.13/test/test_bitfield.cpp000066400000000000000000000122521351156116000222510ustar00rootroot00000000000000/* Copyright (c) 2008-2013, 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 "test.hpp" #include "libtorrent/bitfield.hpp" #include using namespace libtorrent; void print_bitfield(bitfield const& b) { std::string out; for (int i = 0; i < b.size(); ++i) { out += b.get_bit(i) ? "1" : "0"; } printf("%s\n", out.c_str()); } void test_iterators(bitfield& test1) { test1.set_all(); int num = 0; printf("expecting %d ones\n", test1.size()); for (bitfield::const_iterator i = test1.begin(); i != test1.end(); ++i) { printf("%d", *i); TEST_EQUAL(*i, true); num += *i; } printf("\n"); TEST_EQUAL(num, test1.size()); TEST_EQUAL(num, test1.count()); } TORRENT_TEST(bitfield) { bitfield test1(10, false); TEST_EQUAL(test1.size(), 10); TEST_EQUAL(test1.empty(), false); TEST_EQUAL(test1.count(), 0); test1.set_bit(9); TEST_EQUAL(test1.count(), 1); test1.clear_bit(9); TEST_EQUAL(test1.count(), 0); test1.set_bit(2); TEST_EQUAL(test1.count(), 1); test1.set_bit(1); test1.set_bit(9); TEST_EQUAL(test1.count(), 3); TEST_CHECK(test1.all_set() == false); test1.clear_bit(2); TEST_EQUAL(test1.count(), 2); int distance = std::distance(test1.begin(), test1.end()); printf("distance: %d\n", distance); TEST_CHECK(distance == 10); print_bitfield(test1); test1.set_all(); TEST_EQUAL(test1.count(), 10); test1.clear_all(); TEST_EQUAL(test1.count(), 0); test1.resize(2); test1.set_bit(0); test1.resize(16, true); TEST_EQUAL(test1.count(), 15); test1.resize(20, true); TEST_EQUAL(test1.count(), 19); TEST_EQUAL(test1.get_bit(0), true); TEST_EQUAL(test1.get_bit(1), false); bitfield test2 = test1; print_bitfield(test2); TEST_EQUAL(test2.count(), 19); TEST_EQUAL(test2.get_bit(0), true); TEST_EQUAL(test2.get_bit(1), false); TEST_EQUAL(test2.get_bit(2), true); test1.set_bit(1); test1.resize(1); TEST_EQUAL(test1.count(), 1); test1.resize(100, true); TEST_CHECK(test1.all_set() == true); TEST_CHECK(test1.count() == 100); test1.resize(200, false); TEST_CHECK(test1.all_set() == false); TEST_CHECK(test1.count() == 100); test1.resize(50, false); TEST_CHECK(test1.all_set() == true); TEST_CHECK(test1.count() == 50); test1.resize(101, true); TEST_CHECK(test1.all_set() == true); TEST_CHECK(test1.count() == 101); boost::uint8_t b1[] = { 0x08, 0x10 }; test1.assign((char*)b1, 14); print_bitfield(test1); TEST_EQUAL(test1.count(), 2); TEST_EQUAL(test1.get_bit(3), false); TEST_EQUAL(test1.get_bit(4), true); TEST_EQUAL(test1.get_bit(5), false); TEST_EQUAL(test1.get_bit(10), false); TEST_EQUAL(test1.get_bit(11), true); TEST_EQUAL(test1.get_bit(12), false); test1 = bitfield(); TEST_EQUAL(test1.size(), 0); TEST_EQUAL(test1.empty(), true); TEST_EQUAL(bitfield().empty(), true); test1 = test2; TEST_EQUAL(test1.size(), 20); TEST_EQUAL(test1.count(), 19); TEST_EQUAL(test1.get_bit(0), true); TEST_EQUAL(test1.get_bit(1), false); TEST_EQUAL(test1.get_bit(2), true); boost::uint8_t b2[] = { 0x08, 0x10, 0xff, 0xff, 0xff, 0xff, 0xf, 0xc, 0x7f }; test1.assign((char*)b2, 72); print_bitfield(test1); TEST_EQUAL(test1.count(), 47); boost::uint8_t b3[] = { 0x08, 0x10, 0xff, 0xff, 0xff, 0xff, 0xf, 0xc }; test1.assign((char*)b3, 64); print_bitfield(test1); TEST_EQUAL(test1.count(), 40); for (int i = 0; i < 100; ++i) { test1.resize(i, false); test_iterators(test1); } // test alignment boost::uint32_t buffer[4]; char* b = (char*)buffer; for (int i = 0; i < 4; ++i) { b[i] = 0xc0; test1.assign(b + i, 2); print_bitfield(test1); TEST_EQUAL(test1.count(), 2); TEST_EQUAL(test1.all_set(), true); } for (int i = 0; i < 4; ++i) { memset(b + i, 0xff, 5); b[i + 5] = 0xc0; test1.assign(b + i, 32 + 8 + 2); print_bitfield(test1); TEST_EQUAL(test1.count(), 32 + 8 + 2); TEST_EQUAL(test1.all_set(), true); } } libtorrent-rasterbar-1.1.13/test/test_block_cache.cpp000066400000000000000000000343541351156116000227130ustar00rootroot00000000000000/* Copyright (c) 2012, 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 "test.hpp" #include "libtorrent/block_cache.hpp" #include "libtorrent/io_service.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/disk_io_thread.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/session.hpp" #include #include #include using namespace libtorrent; struct test_storage_impl : storage_interface { virtual void initialize(storage_error& ec) {} virtual int readv(file::iovec_t const* bufs, int num_bufs , int piece, int offset, int flags, storage_error& ec) { return bufs_size(bufs, num_bufs); } virtual int writev(file::iovec_t const* bufs, int num_bufs , int piece, int offset, int flags, storage_error& ec) { return bufs_size(bufs, num_bufs); } virtual bool has_any_file(storage_error& ec) { return false; } virtual void set_file_priority(std::vector& prio , storage_error& ec) {} virtual int move_storage(std::string const& save_path, int flags , storage_error& ec) { return 0; } virtual bool verify_resume_data(bdecode_node const& rd , std::vector const* links , storage_error& ec) { return true; } virtual void write_resume_data(entry& rd, storage_error& ec) const {} virtual void release_files(storage_error& ec) {} virtual void rename_file(int index, std::string const& new_filenamem , storage_error& ec) {} virtual void delete_files(int, storage_error& ec) {} virtual void finalize_file(int, storage_error&) {} }; static void nop() {} #if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS #define INITIALIZE_JOB(j) j.in_use = true; #else #define INITIALIZE_JOB(j) #endif #define TEST_SETUP \ io_service ios; \ block_cache bc(0x4000, ios, boost::bind(&nop)); \ aux::session_settings sett; \ file_storage fs; \ fs.add_file("a/test0", 0x4000); \ fs.add_file("a/test1", 0x4000); \ fs.add_file("a/test2", 0x4000); \ fs.add_file("a/test3", 0x4000); \ fs.add_file("a/test4", 0x4000); \ fs.add_file("a/test5", 0x4000); \ fs.add_file("a/test6", 0x4000); \ fs.add_file("a/test7", 0x4000); \ fs.set_piece_length(0x8000); \ fs.set_num_pieces(5); \ test_storage_impl* st = new test_storage_impl; \ boost::shared_ptr pm(boost::make_shared(st, boost::shared_ptr(new int), &fs)); \ error_code ec; \ bc.set_settings(sett, ec); \ st->m_settings = &sett; \ disk_io_job rj; \ disk_io_job wj; \ INITIALIZE_JOB(rj) \ INITIALIZE_JOB(wj) \ rj.storage = pm; \ wj.storage = pm; \ cached_piece_entry* pe = NULL; \ int ret = 0; \ file::iovec_t iov[1]; \ (void)iov[0]; \ (void)ret; \ (void)pe #define WRITE_BLOCK(p, b) \ wj.flags = disk_io_job::in_progress; \ wj.action = disk_io_job::write; \ wj.d.io.offset = b * 0x4000; \ wj.d.io.buffer_size = 0x4000; \ wj.piece = p; \ wj.buffer.disk_block = bc.allocate_buffer("write-test"); \ pe = bc.add_dirty_block(&wj) #define READ_BLOCK(p, b, r) \ rj.action = disk_io_job::read; \ rj.d.io.offset = b * 0x4000; \ rj.d.io.buffer_size = 0x4000; \ rj.piece = p; \ rj.storage = pm; \ rj.requester = (void*)r; \ rj.buffer.disk_block = 0; \ ret = bc.try_read(&rj) #define RETURN_BUFFER \ if (rj.d.io.ref.storage) bc.reclaim_block(rj.d.io.ref); \ else if (rj.buffer.disk_block) bc.free_buffer(rj.buffer.disk_block); \ rj.d.io.ref.storage = 0 #define FLUSH(flushing) \ for (int i = 0; i < int(sizeof(flushing)/sizeof(flushing[0])); ++i) \ { \ pe->blocks[flushing[i]].pending = true; \ bc.inc_block_refcount(pe, 0, block_cache::ref_flushing); \ } \ bc.blocks_flushed(pe, flushing, sizeof(flushing)/sizeof(flushing[0])) #define INSERT(p, b) \ wj.piece = p; \ wj.requester = (void*)1; \ pe = bc.allocate_piece(&wj, cached_piece_entry::read_lru1); \ ret = bc.allocate_iovec(iov, 1); \ TEST_EQUAL(ret, 0); \ bc.insert_blocks(pe, b, iov, 1, &wj) void test_write() { TEST_SETUP; // write block (0,0) WRITE_BLOCK(0, 0); counters c; bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 1); TEST_EQUAL(c[counters::read_cache_blocks], 0); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 0); TEST_EQUAL(c[counters::arc_mru_ghost_size], 0); TEST_EQUAL(c[counters::arc_mfu_size], 0); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 1); TEST_EQUAL(c[counters::arc_volatile_size], 0); // try to read it back READ_BLOCK(0, 0, 1); TEST_EQUAL(bc.pinned_blocks(), 1); bc.update_stats_counters(c); TEST_EQUAL(c[counters::pinned_blocks], 1); // it's supposed to be a cache hit TEST_CHECK(ret >= 0); // return the reference to the buffer we just read RETURN_BUFFER; TEST_EQUAL(bc.pinned_blocks(), 0); bc.update_stats_counters(c); TEST_EQUAL(c[counters::pinned_blocks], 0); // try to read block (1, 0) READ_BLOCK(1, 0, 1); // that's supposed to be a cache miss TEST_CHECK(ret < 0); TEST_EQUAL(bc.pinned_blocks(), 0); bc.update_stats_counters(c); TEST_EQUAL(c[counters::pinned_blocks], 0); // just in case it wasn't we're supposed to return the reference // to the buffer RETURN_BUFFER; tailqueue jobs; bc.clear(jobs); } void test_flush() { TEST_SETUP; // write block (0,0) WRITE_BLOCK(0, 0); // pretend to flush to disk int flushing[1] = {0}; FLUSH(flushing); tailqueue jobs; bc.clear(jobs); } void test_insert() { TEST_SETUP; INSERT(0, 0); counters c; bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 0); TEST_EQUAL(c[counters::read_cache_blocks], 1); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 1); TEST_EQUAL(c[counters::arc_mru_ghost_size], 0); TEST_EQUAL(c[counters::arc_mfu_size], 0); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 0); TEST_EQUAL(c[counters::arc_volatile_size], 0); tailqueue jobs; bc.clear(jobs); } void test_evict() { TEST_SETUP; INSERT(0, 0); counters c; bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 0); TEST_EQUAL(c[counters::read_cache_blocks], 1); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 1); TEST_EQUAL(c[counters::arc_mru_ghost_size], 0); TEST_EQUAL(c[counters::arc_mfu_size], 0); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 0); TEST_EQUAL(c[counters::arc_volatile_size], 0); tailqueue jobs; // this should make it not be evicted // just free the buffers ++pe->piece_refcount; bc.evict_piece(pe, jobs, block_cache::allow_ghost); bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 0); TEST_EQUAL(c[counters::read_cache_blocks], 0); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 1); TEST_EQUAL(c[counters::arc_mru_ghost_size], 0); TEST_EQUAL(c[counters::arc_mfu_size], 0); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 0); TEST_EQUAL(c[counters::arc_volatile_size], 0); --pe->piece_refcount; bc.evict_piece(pe, jobs, block_cache::allow_ghost); bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 0); TEST_EQUAL(c[counters::read_cache_blocks], 0); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 0); TEST_EQUAL(c[counters::arc_mru_ghost_size], 1); TEST_EQUAL(c[counters::arc_mfu_size], 0); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 0); TEST_EQUAL(c[counters::arc_volatile_size], 0); bc.clear(jobs); } // test to have two different requestors read a block and // make sure it moves into the MFU list void test_arc_promote() { TEST_SETUP; INSERT(0, 0); counters c; bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 0); TEST_EQUAL(c[counters::read_cache_blocks], 1); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 1); TEST_EQUAL(c[counters::arc_mru_ghost_size], 0); TEST_EQUAL(c[counters::arc_mfu_size], 0); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 0); TEST_EQUAL(c[counters::arc_volatile_size], 0); READ_BLOCK(0, 0, 1); TEST_EQUAL(bc.pinned_blocks(), 1); bc.update_stats_counters(c); TEST_EQUAL(c[counters::pinned_blocks], 1); // it's supposed to be a cache hit TEST_CHECK(ret >= 0); // return the reference to the buffer we just read RETURN_BUFFER; bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 0); TEST_EQUAL(c[counters::read_cache_blocks], 1); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 1); TEST_EQUAL(c[counters::arc_mru_ghost_size], 0); TEST_EQUAL(c[counters::arc_mfu_size], 0); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 0); TEST_EQUAL(c[counters::arc_volatile_size], 0); READ_BLOCK(0, 0, 2); TEST_EQUAL(bc.pinned_blocks(), 1); bc.update_stats_counters(c); TEST_EQUAL(c[counters::pinned_blocks], 1); // it's supposed to be a cache hit TEST_CHECK(ret >= 0); // return the reference to the buffer we just read RETURN_BUFFER; bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 0); TEST_EQUAL(c[counters::read_cache_blocks], 1); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 0); TEST_EQUAL(c[counters::arc_mru_ghost_size], 0); TEST_EQUAL(c[counters::arc_mfu_size], 1); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 0); TEST_EQUAL(c[counters::arc_volatile_size], 0); tailqueue jobs; bc.clear(jobs); } void test_arc_unghost() { TEST_SETUP; INSERT(0, 0); counters c; bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 0); TEST_EQUAL(c[counters::read_cache_blocks], 1); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 1); TEST_EQUAL(c[counters::arc_mru_ghost_size], 0); TEST_EQUAL(c[counters::arc_mfu_size], 0); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 0); TEST_EQUAL(c[counters::arc_volatile_size], 0); tailqueue jobs; bc.evict_piece(pe, jobs, block_cache::allow_ghost); bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 0); TEST_EQUAL(c[counters::read_cache_blocks], 0); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 0); TEST_EQUAL(c[counters::arc_mru_ghost_size], 1); TEST_EQUAL(c[counters::arc_mfu_size], 0); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 0); TEST_EQUAL(c[counters::arc_volatile_size], 0); // the block is now a ghost. If we cache-hit it, // it should be promoted back to the main list bc.cache_hit(pe, (void*)1, false); bc.update_stats_counters(c); TEST_EQUAL(c[counters::write_cache_blocks], 0); // we didn't actually read in any blocks, so the cache size // is still 0 TEST_EQUAL(c[counters::read_cache_blocks], 0); TEST_EQUAL(c[counters::pinned_blocks], 0); TEST_EQUAL(c[counters::arc_mru_size], 1); TEST_EQUAL(c[counters::arc_mru_ghost_size], 0); TEST_EQUAL(c[counters::arc_mfu_size], 0); TEST_EQUAL(c[counters::arc_mfu_ghost_size], 0); TEST_EQUAL(c[counters::arc_write_size], 0); TEST_EQUAL(c[counters::arc_volatile_size], 0); bc.clear(jobs); } void test_iovec() { TEST_SETUP; ret = bc.allocate_iovec(iov, 1); bc.free_iovec(iov, 1); } void test_unaligned_read() { TEST_SETUP; INSERT(0, 0); INSERT(0, 1); rj.action = disk_io_job::read; rj.d.io.offset = 0x2000; rj.d.io.buffer_size = 0x4000; rj.piece = 0; rj.storage = pm; rj.requester = (void*)1; rj.buffer.disk_block = 0; ret = bc.try_read(&rj); // unaligned reads copies the data into a new buffer // rather than TEST_EQUAL(bc.pinned_blocks(), 0); counters c; bc.update_stats_counters(c); TEST_EQUAL(c[counters::pinned_blocks], 0); // it's supposed to be a cache hit TEST_CHECK(ret >= 0); // return the reference to the buffer we just read RETURN_BUFFER; tailqueue jobs; bc.clear(jobs); } TORRENT_TEST(block_cache) { test_write(); test_flush(); test_insert(); test_evict(); test_arc_promote(); test_arc_unghost(); test_iovec(); test_unaligned_read(); // TODO: test try_evict_blocks // TODO: test evicting volatile pieces, to see them be removed // TODO: test evicting dirty pieces // TODO: test free_piece // TODO: test abort_dirty // TODO: test unaligned reads } TORRENT_TEST(delete_piece) { TEST_SETUP; TEST_CHECK(bc.num_pieces() == 0); INSERT(0, 0); TEST_CHECK(bc.num_pieces() == 1); rj.action = disk_io_job::read; rj.d.io.offset = 0x2000; rj.d.io.buffer_size = 0x4000; rj.piece = 0; rj.storage = pm; rj.requester = (void*)1; rj.buffer.disk_block = 0; ret = bc.try_read(&rj); cached_piece_entry* pe_ = bc.find_piece(pm.get(), 0); bc.mark_for_eviction(pe_, block_cache::disallow_ghost); TEST_CHECK(bc.num_pieces() == 0); } libtorrent-rasterbar-1.1.13/test/test_bloom_filter.cpp000066400000000000000000000075341351156116000231530ustar00rootroot00000000000000/* Copyright (c) 2015, 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 "test.hpp" #include "libtorrent/bloom_filter.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/sha1_hash.hpp" #include using namespace libtorrent; void test_set_and_get() { bloom_filter<32> filter; sha1_hash k1 = hasher("test1", 5).final(); sha1_hash k2 = hasher("test2", 5).final(); sha1_hash k3 = hasher("test3", 5).final(); sha1_hash k4 = hasher("test4", 5).final(); TEST_CHECK(!filter.find(k1)); TEST_CHECK(!filter.find(k2)); TEST_CHECK(!filter.find(k3)); TEST_CHECK(!filter.find(k4)); filter.set(k1); TEST_CHECK(filter.find(k1)); TEST_CHECK(!filter.find(k2)); TEST_CHECK(!filter.find(k3)); TEST_CHECK(!filter.find(k4)); filter.set(k4); TEST_CHECK(filter.find(k1)); TEST_CHECK(!filter.find(k2)); TEST_CHECK(!filter.find(k3)); TEST_CHECK(filter.find(k4)); } void test_set_bits() { boost::uint8_t bits[4] = {0x00, 0x00, 0x00, 0x00}; for (int i = 0; i < 4 * 8; ++i) { boost::uint8_t t[4] = { boost::uint8_t(i & 0xff), 0, boost::uint8_t(i & 0xff), 0 }; TEST_CHECK(!has_bits(t, bits, 6)); } for (int i = 0; i < 4 * 8; i += 2) { boost::uint8_t t[4] = { boost::uint8_t(i & 0xff), 0, boost::uint8_t(i & 0xff), 0 }; TEST_CHECK(!has_bits(t, bits, 4)); set_bits(t, bits, 4); TEST_CHECK(has_bits(t, bits, 4)); } boost::uint8_t compare[4] = { 0x55, 0x55, 0x55, 0x55}; TEST_EQUAL(memcmp(compare, bits, 4), 0); } void test_count_zeroes() { boost::uint8_t bits[4] = {0x00, 0xff, 0x55, 0xaa}; TEST_EQUAL(count_zero_bits(bits, 4), 16); boost::uint8_t t[4] = { 4, 0, 4, 0 }; set_bits(t, bits, 4); TEST_EQUAL(count_zero_bits(bits, 4), 15); boost::uint8_t compare[4] = { 0x10, 0xff, 0x55, 0xaa}; TEST_EQUAL(memcmp(compare, bits, 4), 0); } void test_to_from_string() { boost::uint8_t bits[4] = { 0x10, 0xff, 0x55, 0xaa}; bloom_filter<4> filter; filter.from_string(reinterpret_cast(bits)); std::string bits_out = filter.to_string(); TEST_EQUAL(memcmp(bits_out.c_str(), bits, 4), 0); sha1_hash k( "\x01\x00\x02\x00 "); TEST_CHECK(!filter.find(k)); filter.set(k); TEST_CHECK(filter.find(k)); boost::uint8_t compare[4] = { 0x16, 0xff, 0x55, 0xaa}; bits_out = filter.to_string(); TEST_EQUAL(memcmp(compare, bits_out.c_str(), 4), 0); } TORRENT_TEST(bloom_filter) { test_set_and_get(); test_set_bits(); test_count_zeroes(); test_to_from_string(); // TODO: test size() // TODO: test clear() } libtorrent-rasterbar-1.1.13/test/test_buffer.cpp000066400000000000000000000173301351156116000217420ustar00rootroot00000000000000/* 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. */ #include #include #include #include #include #include "libtorrent/buffer.hpp" #include "libtorrent/chained_buffer.hpp" #include "libtorrent/socket.hpp" #include "test.hpp" using namespace libtorrent; /* template T const& min_(T const& x, T const& y) { return x < y ? x : y; } void test_speed() { buffer b; char data[32]; srand(0); boost::timer t; int const iterations = 5000000; int const step = iterations / 20; for (int i = 0; i < iterations; ++i) { int x = rand(); if (i % step == 0) std::cerr << "."; std::size_t n = rand() % 32; n = 32; if (x % 2) { b.insert(data, data + n); } else { b.erase(min_(b.size(), n)); } } float t1 = t.elapsed(); std::cerr << "buffer elapsed: " << t.elapsed() << "\n"; std::vector v; srand(0); t.restart(); for (int i = 0; i < iterations; ++i) { int x = rand(); if (i % step == 0) std::cerr << "."; std::size_t n = rand() % 32; n = 32; if (x % 2) { v.insert(v.end(), data, data + n); } else { v.erase(v.begin(), v.begin() + min_(v.size(), n)); } } float t2 = t.elapsed(); std::cerr << "std::vector elapsed: " << t.elapsed() << "\n"; assert(t1 < t2); } */ // -- test buffer -- TORRENT_TEST(buffer) { char data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; buffer b; TEST_CHECK(b.size() == 0); TEST_CHECK(b.capacity() == 0); TEST_CHECK(b.empty()); b.resize(10); TEST_CHECK(b.size() == 10); TEST_CHECK(b.capacity() == 10); std::memcpy(b.begin(), data, 10); b.reserve(50); TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0); TEST_CHECK(b.capacity() == 50); b.erase(b.begin() + 6, b.end()); TEST_CHECK(std::memcmp(b.begin(), data, 6) == 0); TEST_CHECK(b.capacity() == 50); TEST_CHECK(b.size() == 6); b.insert(b.begin(), data + 5, data + 10); TEST_CHECK(b.capacity() == 50); TEST_CHECK(b.size() == 11); TEST_CHECK(std::memcmp(b.begin(), data + 5, 5) == 0); b.clear(); TEST_CHECK(b.size() == 0); TEST_CHECK(b.capacity() == 50); b.insert(b.end(), data, data + 10); TEST_CHECK(b.size() == 10); TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0); b.erase(b.begin(), b.end()); TEST_CHECK(b.capacity() == 50); TEST_CHECK(b.size() == 0); buffer().swap(b); TEST_CHECK(b.capacity() == 0); } // -- test chained buffer -- std::set buffer_list; void free_buffer(char* m, void* userdata, block_cache_reference ref) { TEST_CHECK(userdata == (void*)0x1337); std::set::iterator i = buffer_list.find(m); TEST_CHECK(i != buffer_list.end()); buffer_list.erase(i); std::free(m); } char* allocate_buffer(int size) { char* mem = (char*)std::malloc(size); buffer_list.insert(mem); return mem; } template int copy_buffers(T const& b, char* target) { int copied = 0; for (typename T::const_iterator i = b.begin() , end(b.end()); i != end; ++i) { memcpy(target, boost::asio::buffer_cast(*i), boost::asio::buffer_size(*i)); target += boost::asio::buffer_size(*i); copied += boost::asio::buffer_size(*i); } return copied; } bool compare_chained_buffer(chained_buffer& b, char const* mem, int size) { if (size == 0) return true; std::vector flat(size); std::vector const& iovec2 = b.build_iovec(size); int copied = copy_buffers(iovec2, &flat[0]); TEST_CHECK(copied == size); return std::memcmp(&flat[0], mem, size) == 0; } TORRENT_TEST(chained_buffer) { char data[] = "foobar"; { chained_buffer b; TEST_CHECK(b.empty()); TEST_EQUAL(b.capacity(), 0); TEST_EQUAL(b.size(), 0); TEST_EQUAL(b.space_in_last_buffer(), 0); TEST_CHECK(buffer_list.empty()); // there are no buffers, we should not be able to allocate // an appendix in an existing buffer TEST_EQUAL(b.allocate_appendix(1), 0); char* b1 = allocate_buffer(512); std::memcpy(b1, data, 6); b.append_buffer(b1, 512, 6, &free_buffer, (void*)0x1337); TEST_EQUAL(buffer_list.size(), 1); TEST_CHECK(b.capacity() == 512); TEST_CHECK(b.size() == 6); TEST_CHECK(!b.empty()); TEST_CHECK(b.space_in_last_buffer() == 512 - 6); b.pop_front(3); TEST_CHECK(b.capacity() == 512); TEST_CHECK(b.size() == 3); TEST_CHECK(!b.empty()); TEST_CHECK(b.space_in_last_buffer() == 512 - 6); bool ret = b.append(data, 6); TEST_CHECK(ret == true); TEST_CHECK(b.capacity() == 512); TEST_CHECK(b.size() == 9); TEST_CHECK(!b.empty()); TEST_CHECK(b.space_in_last_buffer() == 512 - 12); char data2[1024]; ret = b.append(data2, 1024); TEST_CHECK(ret == false); char* b2 = allocate_buffer(512); std::memcpy(b2, data, 6); b.append_buffer(b2, 512, 6, free_buffer, (void*)0x1337); TEST_CHECK(buffer_list.size() == 2); char* b3 = allocate_buffer(512); std::memcpy(b3, data, 6); b.append_buffer(b3, 512, 6, &free_buffer, (void*)0x1337); TEST_CHECK(buffer_list.size() == 3); TEST_CHECK(b.capacity() == 512 * 3); TEST_CHECK(b.size() == 21); TEST_CHECK(!b.empty()); TEST_CHECK(b.space_in_last_buffer() == 512 - 6); TEST_CHECK(compare_chained_buffer(b, "barfoobar", 9)); for (int i = 1; i < 21; ++i) TEST_CHECK(compare_chained_buffer(b, "barfoobarfoobarfoobar", i)); b.pop_front(5 + 6); TEST_CHECK(buffer_list.size() == 2); TEST_CHECK(b.capacity() == 512 * 2); TEST_CHECK(b.size() == 10); TEST_CHECK(!b.empty()); TEST_CHECK(b.space_in_last_buffer() == 512 - 6); char const* str = "obarfooba"; TEST_CHECK(compare_chained_buffer(b, str, 9)); for (int i = 0; i < 9; ++i) { b.pop_front(1); ++str; TEST_CHECK(compare_chained_buffer(b, str, 8 - i)); TEST_CHECK(b.size() == 9 - i); } char* b4 = allocate_buffer(20); std::memcpy(b4, data, 6); std::memcpy(b4 + 6, data, 6); b.append_buffer(b4, 20, 12, &free_buffer, (void*)0x1337); TEST_CHECK(b.space_in_last_buffer() == 8); ret = b.append(data, 6); TEST_CHECK(ret == true); TEST_CHECK(b.space_in_last_buffer() == 2); std::cout << b.space_in_last_buffer() << std::endl; ret = b.append(data, 2); TEST_CHECK(ret == true); TEST_CHECK(b.space_in_last_buffer() == 0); std::cout << b.space_in_last_buffer() << std::endl; char* b5 = allocate_buffer(20); std::memcpy(b4, data, 6); b.append_buffer(b5, 20, 6, &free_buffer, (void*)0x1337); b.pop_front(22); TEST_CHECK(b.size() == 5); } TEST_CHECK(buffer_list.empty()); } libtorrent-rasterbar-1.1.13/test/test_checking.cpp000066400000000000000000000244701351156116000222470ustar00rootroot00000000000000/* Copyright (c) 2013, 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 // for chmod #include "libtorrent/session.hpp" #include "test.hpp" #include "settings.hpp" #include "setup_transfer.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/torrent_status.hpp" namespace { const int file_sizes[] = { 0, 5, 16 - 5, 16000, 17, 10, 8000, 8000, 1,1,1,1,1,100,1,1,1,1,100,1,1,1,1,1,1 ,1,1,1,1,1,1,13,65000,34,75,2,30,400,500,23000,900,43000,400,4300,6, 4 }; const int num_files = sizeof(file_sizes) / sizeof(file_sizes[0]); bool is_checking(int const state) { return state == lt::torrent_status::checking_files #ifndef TORRENT_NO_DEPRECATE || state == lt::torrent_status::queued_for_checking #endif || state == lt::torrent_status::checking_resume_data; }; } enum { // make sure we don't accidentally require files to be writable just to // check their hashes read_only_files = 1, // make sure we detect corrupt files and mark the appropriate pieces // as not had corrupt_files = 2, incomplete_files = 4, // make the files not be there when starting up, move the files in place and // force-recheck. Make sure the stat cache is cleared and let us pick up the // new files force_recheck = 8, }; void test_checking(int flags) { using namespace libtorrent; namespace lt = libtorrent; fprintf(stdout, "\n==== TEST CHECKING %s%s%s%s=====\n\n" , (flags & read_only_files) ? "read-only-files ":"" , (flags & corrupt_files) ? "corrupt ":"" , (flags & incomplete_files) ? "incomplete ":"" , (flags & force_recheck) ? "force_recheck ":""); error_code ec; create_directory("test_torrent_dir", ec); if (ec) fprintf(stdout, "ERROR: creating directory test_torrent_dir: (%d) %s\n" , ec.value(), ec.message().c_str()); file_storage fs; std::srand(10); int piece_size = 0x4000; create_random_files("test_torrent_dir", file_sizes, num_files, &fs); lt::create_torrent t(fs, piece_size, 0x4000 , lt::create_torrent::optimize_alignment); // calculate the hash for all pieces set_piece_hashes(t, ".", ec); if (ec) fprintf(stdout, "ERROR: set_piece_hashes: (%d) %s\n" , ec.value(), ec.message().c_str()); std::vector buf; bencode(std::back_inserter(buf), t.generate()); boost::shared_ptr ti(new torrent_info(&buf[0], buf.size(), ec)); fprintf(stdout, "generated torrent: %s test_torrent_dir\n" , to_hex(ti->info_hash().to_string()).c_str()); // truncate every file in half if (flags & incomplete_files) { for (int i = 0; i < num_files; ++i) { char name[1024]; snprintf(name, sizeof(name), "test%d", i); char dirname[200]; snprintf(dirname, sizeof(dirname), "test_dir%d", i / 5); std::string path = combine_path("test_torrent_dir", dirname); path = combine_path(path, name); error_code ec; file f(path, file::read_write, ec); if (ec) fprintf(stdout, "ERROR: opening file \"%s\": (%d) %s\n" , path.c_str(), ec.value(), ec.message().c_str()); f.set_size(file_sizes[i] / 2, ec); if (ec) fprintf(stdout, "ERROR: truncating file \"%s\": (%d) %s\n" , path.c_str(), ec.value(), ec.message().c_str()); } } // overwrite the files with new random data if (flags & corrupt_files) { fprintf(stdout, "corrupt file test. overwriting files\n"); // increase the size of some files. When they're read only that forces // the checker to open them in write-mode to truncate them static const int file_sizes2[] = { 0, 5, 16 - 5, 16001, 30, 10, 8000, 8000, 1,1,1,1,1,100,1,1,1,1,100,1,1,1,1,1,1 ,1,1,1,1,1,1,13,65000,34,75,2,30,400,500,23000,900,43000,400,4300,6, 4}; create_random_files("test_torrent_dir", file_sizes2, num_files); } // make the files read only if (flags & read_only_files) { fprintf(stdout, "making files read-only\n"); for (int i = 0; i < num_files; ++i) { char name[1024]; snprintf(name, sizeof(name), "test%d", i); char dirname[200]; snprintf(dirname, sizeof(dirname), "test_dir%d", i / 5); std::string path = combine_path("test_torrent_dir", dirname); path = combine_path(path, name); fprintf(stdout, " %s\n", path.c_str()); #ifdef TORRENT_WINDOWS SetFileAttributesA(path.c_str(), FILE_ATTRIBUTE_READONLY); #else chmod(path.c_str(), S_IRUSR); #endif } } if (flags & force_recheck) { remove_all("test_torrent_dir_tmp", ec); if (ec) fprintf(stdout, "ERROR: removing \"test_torrent_dir_tmp\": (%d) %s\n" , ec.value(), ec.message().c_str()); rename("test_torrent_dir", "test_torrent_dir_tmp", ec); if (ec) fprintf(stdout, "ERROR: renaming dir \"test_torrent_dir\": (%d) %s\n" , ec.value(), ec.message().c_str()); } lt::session ses1(settings()); add_torrent_params p; p.save_path = "."; p.ti = ti; torrent_handle tor1 = ses1.add_torrent(p, ec); TEST_CHECK(!ec); if (flags & force_recheck) { // first make sure the session tries to check for the file and can't find // them libtorrent::alert const* a = wait_for_alert( ses1, torrent_checked_alert::alert_type, "checking"); TEST_CHECK(a); // now, move back the files and force-recheck. make sure we pick up the // files this time remove_all("test_torrent_dir", ec); if (ec) fprintf(stdout, "ERROR: removing \"test_torrent_dir\": (%d) %s\n" , ec.value(), ec.message().c_str()); rename("test_torrent_dir_tmp", "test_torrent_dir", ec); if (ec) fprintf(stdout, "ERROR: renaming dir \"test_torrent_dir_tmp\": (%d) %s\n" , ec.value(), ec.message().c_str()); tor1.force_recheck(); } torrent_status st; for (int i = 0; i < 20; ++i) { print_alerts(ses1, "ses1"); st = tor1.status(); printf("%d %f %s\n", st.state, st.progress_ppm / 10000.f, st.errc.message().c_str()); if (!is_checking(st.state) || st.errc) break; test_sleep(500); } if (flags & incomplete_files) { TEST_CHECK(!st.is_seeding); test_sleep(500); st = tor1.status(); TEST_CHECK(!st.is_seeding); } if (flags & corrupt_files) { TEST_CHECK(!st.is_seeding); TEST_CHECK(!st.errc); if (st.errc) fprintf(stdout, "error: %s\n", st.errc.message().c_str()); } if ((flags & (incomplete_files | corrupt_files)) == 0) { TEST_CHECK(st.is_seeding); if (st.errc) fprintf(stdout, "error: %s\n", st.errc.message().c_str()); } // make the files writable again if (flags & read_only_files) { for (int i = 0; i < num_files; ++i) { char name[1024]; snprintf(name, sizeof(name), "test%d", i); char dirname[200]; snprintf(dirname, sizeof(dirname), "test_dir%d", i / 5); std::string path = combine_path("test_torrent_dir", dirname); path = combine_path(path, name); #ifdef TORRENT_WINDOWS SetFileAttributesA(path.c_str(), FILE_ATTRIBUTE_NORMAL); #else chmod(path.c_str(), S_IRUSR | S_IWUSR); #endif } } remove_all("test_torrent_dir", ec); if (ec) fprintf(stdout, "ERROR: removing test_torrent_dir: (%d) %s\n" , ec.value(), ec.message().c_str()); } TORRENT_TEST(checking) { test_checking(0); } TORRENT_TEST(read_only_corrupt) { test_checking(read_only_files | corrupt_files); } TORRENT_TEST(read_only) { test_checking(read_only_files); } TORRENT_TEST(incomplete) { test_checking(incomplete_files); } TORRENT_TEST(corrupt) { test_checking(corrupt_files); } TORRENT_TEST(force_recheck) { test_checking(force_recheck); } TORRENT_TEST(discrete_checking) { using namespace lt; printf("\n==== TEST CHECKING discrete =====\n\n"); error_code ec; create_directory("test_torrent_dir", ec); if (ec) printf("ERROR: creating directory test_torrent_dir: (%d) %s\n", ec.value(), ec.message().c_str()); int const megabyte = 0x100000; int const piece_size = 2 * megabyte; int const file_sizes[] = { 9 * megabyte, 4 * megabyte }; int const num_files = sizeof(file_sizes) / sizeof(file_sizes[0]); file_storage fs; create_random_files("test_torrent_dir", file_sizes, num_files, &fs); lt::create_torrent t(fs, piece_size, 0, lt::create_torrent::optimize_alignment); set_piece_hashes(t, ".", ec); if (ec) printf("ERROR: set_piece_hashes: (%d) %s\n", ec.value(), ec.message().c_str()); std::vector buf; bencode(std::back_inserter(buf), t.generate()); boost::shared_ptr ti(new torrent_info(&buf[0], buf.size(), ec)); printf("generated torrent: %s test_torrent_dir\n", to_hex(ti->info_hash().to_string()).c_str()); TEST_EQUAL(ti->num_files(), 3); { session ses1(settings()); add_torrent_params p; p.file_priorities.resize(ti->num_files()); p.file_priorities[0] = 1; p.save_path = "."; p.ti = ti; torrent_handle tor1 = ses1.add_torrent(p, ec); // change the priority of a file while checking and make sure it doesn't interrupt the checking. std::vector prio(ti->num_files(), 0); prio[2] = 1; tor1.prioritize_files(prio); TEST_CHECK(wait_for_alert(ses1, torrent_checked_alert::alert_type, "torrent checked", 1, seconds(50))); TEST_CHECK(tor1.status(0).is_seeding); } remove_all("test_torrent_dir", ec); if (ec) fprintf(stdout, "ERROR: removing test_torrent_dir: (%d) %s\n", ec.value(), ec.message().c_str()); } libtorrent-rasterbar-1.1.13/test/test_crc32.cpp000066400000000000000000000044651351156116000214120ustar00rootroot00000000000000/* Copyright (c) 2014, 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/crc32c.hpp" #include "test.hpp" TORRENT_TEST(crc32) { using namespace libtorrent; boost::uint32_t out; boost::uint32_t in1 = 0x5aa5feef; out = crc32c_32(in1); TEST_EQUAL(out, htonl(0xd5b9e35e)); boost::uint64_t buf[4]; memcpy(buf, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 32); out = crc32c(buf, 4); TEST_EQUAL(out, htonl(0xaa36918a)); memcpy(buf, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 32); out = crc32c(buf, 4); TEST_EQUAL(out, htonl(0x43aba862)); memcpy(buf, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 32); out = crc32c(buf, 4); TEST_EQUAL(out, htonl(0x4e79dd46)); } libtorrent-rasterbar-1.1.13/test/test_create_torrent.cpp000066400000000000000000000070611351156116000235110ustar00rootroot00000000000000/* Copyright (c) 2016, 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 "test.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/announce_entry.hpp" #include "libtorrent/aux_/escape_string.hpp" // for convert_path_to_posix #include #include namespace lt = libtorrent; // make sure creating a torrent from an existing handle preserves the // info-dictionary verbatim, so as to not alter the info-hash TORRENT_TEST(create_verbatim_torrent) { char const test_torrent[] = "d4:infod4:name6:foobar6:lengthi12345e12:piece lengthi65536e6:pieces20:ababababababababababee"; lt::torrent_info info(test_torrent, sizeof(test_torrent) - 1); lt::create_torrent t(info, true); std::vector buffer; lt::bencode(std::back_inserter(buffer), t.generate()); // now, make sure the info dictionary was unchanged buffer.push_back('\0'); char const* dest_info = std::strstr(&buffer[0], "4:info"); TEST_CHECK(dest_info != NULL); // +1 and -2 here is to strip the outermost dictionary from the source // torrent, since create_torrent may have added items next to the info dict TEST_CHECK(memcmp(dest_info, test_torrent + 1, sizeof(test_torrent)-3) == 0); } TORRENT_TEST(create_torrent_round_trip) { char const test_torrent[] = "d8:announce26:udp://testurl.com/announce7:comment22:this is a test comment13:creation datei1337e4:infod6:lengthi12345e4:name6:foobar12:piece lengthi65536e6:pieces20:ababababababababababee"; lt::torrent_info info1(test_torrent, sizeof(test_torrent) - 1); TEST_EQUAL(info1.comment(), "this is a test comment"); TEST_EQUAL(info1.trackers().size(), 1); TEST_EQUAL(info1.trackers().front().url, "udp://testurl.com/announce"); lt::create_torrent t(info1, true); std::vector buffer; lt::bencode(std::back_inserter(buffer), t.generate()); lt::torrent_info info2(&buffer[0], buffer.size()); TEST_EQUAL(info2.comment(), "this is a test comment"); TEST_EQUAL(info2.trackers().size(), 1); TEST_EQUAL(info2.trackers().front().url, "udp://testurl.com/announce"); TEST_CHECK(info1.info_hash() == info2.info_hash()); } libtorrent-rasterbar-1.1.13/test/test_dht.cpp000066400000000000000000002406601351156116000212540ustar00rootroot00000000000000/* Copyright (c) 2008, 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 #include "libtorrent/config.hpp" #include "libtorrent/session.hpp" #include "libtorrent/kademlia/node.hpp" // for verify_message #include "libtorrent/bencode.hpp" #include "libtorrent/socket_io.hpp" // for hash_address #include "libtorrent/broadcast_socket.hpp" // for supports_ipv6 #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/random.hpp" #include "libtorrent/ed25519.hpp" #include "libtorrent/kademlia/node_id.hpp" #include "libtorrent/kademlia/routing_table.hpp" #include "libtorrent/kademlia/item.hpp" #include "libtorrent/kademlia/dht_observer.hpp" #include "libtorrent/ed25519.hpp" #include #include #include "test.hpp" #include "setup_transfer.hpp" #ifdef TORRENT_USE_VALGRIND #include #endif #if TORRENT_USE_IOSTREAM #include #endif using namespace libtorrent; using namespace libtorrent::dht; void nop() {} static sha1_hash to_hash(char const* s) { sha1_hash ret; from_hex(s, 40, (char*)&ret[0]); return ret; } void add_and_replace(libtorrent::dht::node_id& dst, libtorrent::dht::node_id const& add) { bool carry = false; for (int k = 19; k >= 0; --k) { int sum = dst[k] + add[k] + (carry?1:0); dst[k] = sum & 255; carry = sum > 255; } } void node_push_back(void* userdata, libtorrent::dht::node_entry const& n) { using namespace libtorrent::dht; std::vector* nv = (std::vector*)userdata; nv->push_back(n); } static void nop(void* userdata, libtorrent::dht::node_entry const& n) {} std::list > g_sent_packets; struct mock_socket : udp_socket_interface { bool has_quota() { return true; } bool send_packet(entry& msg, udp::endpoint const& ep, int flags) { // TODO: ideally the mock_socket would contain this queue of packets, to // make tests independent g_sent_packets.push_back(std::make_pair(ep, msg)); return true; } }; sha1_hash generate_next() { sha1_hash ret; for (int i = 0; i < 20; ++i) ret[i] = rand() & 0xff; return ret; } boost::array generate_key() { boost::array ret; for (int i = 0; i < 64; ++i) ret[i] = rand() & 0xff; return ret; } static const std::string no; std::list >::iterator find_packet(udp::endpoint ep) { return std::find_if(g_sent_packets.begin(), g_sent_packets.end() , boost::bind(&std::pair::first, _1) == ep); } void lazy_from_entry(entry const& e, bdecode_node& l) { error_code ec; static char inbuf[1500]; int len = bencode(inbuf, e); int ret = bdecode(inbuf, inbuf + len, l, ec); TEST_CHECK(ret == 0); } void write_peers(entry::dictionary_type& r, std::set const& peers) { entry::list_type& pe = r["values"].list(); for (std::set::const_iterator it = peers.begin() ; it != peers.end(); ++it) { std::string endpoint(18, '\0'); std::string::iterator out = endpoint.begin(); libtorrent::detail::write_endpoint(*it, out); endpoint.resize(out - endpoint.begin()); pe.push_back(entry(endpoint)); } } struct msg_args { msg_args& info_hash(char const* i) { if (i) a["info_hash"] = std::string(i, 20); return *this; } msg_args& name(char const* n) { if (n) a["n"] = n; return *this; } msg_args& token(std::string t) { a["token"] = t; return *this; } msg_args& port(int p) { a["port"] = p; return *this; } msg_args& target(char const* t) { if (t) a["target"] = std::string(t, 20); return *this; } msg_args& value(entry const& v) { a["v"] = v; return *this; } msg_args& scrape(bool s) { a["scrape"] = s ? 1 : 0; return *this; } msg_args& seed(bool s) { a["seed"] = s ? 1 : 0; return *this; } msg_args& key(std::string k) { a["k"] = k; return *this; } msg_args& sig(std::string s) { a["sig"] = s; return *this; } msg_args& seq(int s) { a["seq"] = s; return *this; } msg_args& cas(boost::int64_t c) { a["cas"] = c; return *this; } msg_args& nid(sha1_hash const& n) { a["id"] = n.to_string(); return *this; } msg_args& salt(char const* s) { if (s) a["salt"] = s; return *this; } msg_args& want(std::string w) { a["want"].list().push_back(w); return *this; } msg_args& nodes(nodes_t const& n) { if (!n.empty()) dht::write_nodes_entry(a, n); return *this; } msg_args& peers(std::set const& p) { if (!p.empty()) write_peers(a.dict(), p); return *this; } entry a; }; void send_dht_request(node& node, char const* msg, udp::endpoint const& ep , bdecode_node* reply, msg_args const& args = msg_args() , char const* t = "10", bool has_response = true) { // we're about to clear out the backing buffer // for this lazy_entry, so we better clear it now reply->clear(); entry e; e["q"] = msg; e["t"] = t; e["y"] = "q"; e["a"] = args.a; e["a"].dict().insert(std::make_pair("id", generate_next().to_string())); char msg_buf[1500]; int size = bencode(msg_buf, e); #if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM // this yields a lot of output. too much // std::cout << "sending: " << e << "\n"; #endif #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_MEM_IS_DEFINED(msg_buf, size); #endif bdecode_node decoded; error_code ec; bdecode(msg_buf, msg_buf + size, decoded, ec); if (ec) fprintf(stdout, "bdecode failed: %s\n", ec.message().c_str()); dht::msg m(decoded, ep); node.incoming(m); // If the request is supposed to get a response, by now the node should have // invoked the send function and put the response in g_sent_packets std::list >::iterator i = find_packet(ep); if (has_response) { if (i == g_sent_packets.end()) { TEST_ERROR("not response from DHT node"); return; } lazy_from_entry(i->second, *reply); g_sent_packets.erase(i); return; } // this request suppose won't be responsed. if (i != g_sent_packets.end()) { TEST_ERROR("shouldn't have response from DHT node"); return; } } void send_dht_response(node& node, bdecode_node const& request, udp::endpoint const& ep , msg_args const& args = msg_args()) { entry e; e["y"] = "r"; e["t"] = request.dict_find_string_value("t"); // e["ip"] = endpoint_to_bytes(ep); e["r"] = args.a; e["r"].dict().insert(std::make_pair("id", generate_next().to_string())); char msg_buf[1500]; int size = bencode(msg_buf, e); #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_MEM_IS_DEFINED(msg_buf, size); #endif bdecode_node decoded; error_code ec; bdecode(msg_buf, msg_buf + size, decoded, ec); if (ec) fprintf(stdout, "bdecode failed: %s\n", ec.message().c_str()); dht::msg m(decoded, ep); node.incoming(m); } struct announce_item { sha1_hash next; int num_peers; entry ent; sha1_hash target; void gen() { num_peers = (rand() % 5) + 2; ent["next"] = next.to_string(); ent["A"] = "a"; ent["B"] = "b"; ent["num_peers"] = num_peers; char buf[512]; char* ptr = buf; int len = bencode(ptr, ent); target = hasher(buf, len).final(); } }; void announce_immutable_items(node& node, udp::endpoint const* eps , announce_item const* items, int num_items) { std::string token; for (int i = 0; i < 1000; ++i) { for (int j = 0; j < num_items; ++j) { if ((i % items[j].num_peers) == 0) continue; bdecode_node response; send_dht_request(node, "get", eps[i], &response , msg_args().target((char const*)&items[j].target[0])); key_desc_t desc[] = { { "r", bdecode_node::dict_t, 0, key_desc_t::parse_children }, { "id", bdecode_node::string_t, 20, 0}, { "token", bdecode_node::string_t, 0, 0}, { "ip", bdecode_node::string_t, 0, key_desc_t::optional | key_desc_t::last_child}, { "y", bdecode_node::string_t, 1, 0}, }; bdecode_node parsed[5]; char error_string[200]; // fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); int ret = verify_message(response, desc, parsed, error_string , sizeof(error_string)); if (ret) { TEST_EQUAL(parsed[4].string_value(), "r"); token = parsed[2].string_value(); // fprintf(stdout, "got token: %s\n", token.c_str()); } else { fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); fprintf(stdout, " invalid get response: %s\n", error_string); TEST_ERROR(error_string); } if (parsed[3]) { address_v4::bytes_type b; memcpy(&b[0], parsed[3].string_ptr(), b.size()); address_v4 addr(b); TEST_EQUAL(addr, eps[i].address()); } send_dht_request(node, "put", eps[i], &response , msg_args() .token(token) .target((char const*)&items[j].target[0]) .value(items[j].ent)); key_desc_t desc2[] = { { "y", bdecode_node::string_t, 1, 0 } }; bdecode_node parsed2[1]; ret = verify_message(response, desc2, parsed2, error_string , sizeof(error_string)); if (ret) { if (parsed2[0].string_value() != "r") fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); TEST_EQUAL(parsed2[0].string_value(), "r"); } else { fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); fprintf(stdout, " invalid put response: %s\n", error_string); TEST_ERROR(error_string); } } } std::set items_num; for (int j = 0; j < num_items; ++j) { bdecode_node response; send_dht_request(node, "get", eps[j], &response , msg_args().target((char const*)&items[j].target[0])); key_desc_t desc[] = { { "r", bdecode_node::dict_t, 0, key_desc_t::parse_children }, { "v", bdecode_node::dict_t, 0, 0}, { "id", bdecode_node::string_t, 20, key_desc_t::last_child}, { "y", bdecode_node::string_t, 1, 0}, }; bdecode_node parsed[4]; char error_string[200]; int ret = verify_message(response, desc, parsed, error_string , sizeof(error_string)); if (ret) { items_num.insert(items_num.begin(), j); } } // TODO: check to make sure the "best" items are stored TEST_EQUAL(items_num.size(), 4); } int sum_distance_exp(int s, node_entry const& e, node_id const& ref) { return s + distance_exp(e.id, ref); } std::vector g_got_peers; void get_peers_cb(std::vector const& peers) { g_got_peers.insert(g_got_peers.end(), peers.begin(), peers.end()); } std::vector g_got_items; dht::item g_put_item; int g_put_count; void get_mutable_item_cb(dht::item const& i, bool a) { if (!a) return; if (!i.empty()) g_got_items.push_back(i); } void put_mutable_item_data_cb(dht::item& i) { if (!i.empty()) g_got_items.push_back(i); TEST_CHECK(!g_put_item.empty()); i = g_put_item; g_put_count++; } void put_mutable_item_cb(dht::item const&, int num, int expect) { TEST_EQUAL(num, expect); } void get_immutable_item_cb(dht::item const& i) { if (!i.empty()) g_got_items.push_back(i); } void put_immutable_item_cb(int num, int expect) { TEST_EQUAL(num, expect); } struct obs : dht::dht_observer { virtual void set_external_address(address const& addr , address const& source) TORRENT_OVERRIDE {} virtual address external_address() TORRENT_OVERRIDE { return address_v4::from_string("236.0.0.1"); } virtual void get_peers(sha1_hash const& ih) TORRENT_OVERRIDE {} virtual void outgoing_get_peers(sha1_hash const& target , sha1_hash const& sent_target, udp::endpoint const& ep) TORRENT_OVERRIDE {} virtual void announce(sha1_hash const& ih, address const& addr, int port) TORRENT_OVERRIDE {} #ifndef TORRENT_DISABLE_LOGGING virtual void log(dht_logger::module_t l, char const* fmt, ...) TORRENT_OVERRIDE { va_list v; va_start(v, fmt); char buf[1024]; vsnprintf(buf, sizeof(buf), fmt, v); va_end(v); m_log.push_back(buf); } virtual void log_packet(message_direction_t dir, char const* pkt, int len , udp::endpoint node) TORRENT_OVERRIDE {} #endif virtual bool on_dht_request(char const* query, int query_len , dht::msg const& request, entry& response) TORRENT_OVERRIDE { return false; } std::vector m_log; }; dht_settings test_settings() { dht_settings sett; sett.max_torrents = 4; sett.max_dht_items = 4; sett.enforce_node_id = false; return sett; } // TODO: test obfuscated_get_peers // TODO: 2 split this test up into smaller test cases TORRENT_TEST(dht) { dht_settings sett = test_settings(); mock_socket s; obs observer; counters cnt; dht::node node(&s, sett, node_id(0), &observer, cnt); // DHT should be running on port 48199 now bdecode_node response; char error_string[200]; bool ret; // ====== ping ====== udp::endpoint source(address::from_string("10.0.0.1"), 20); send_dht_request(node, "ping", source, &response); dht::key_desc_t pong_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"t", bdecode_node::string_t, 2, 0}, {"r", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"id", bdecode_node::string_t, 20, key_desc_t::last_child}, }; bdecode_node pong_keys[4]; fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); ret = dht::verify_message(response, pong_desc, pong_keys, error_string , sizeof(error_string)); TEST_CHECK(ret); if (ret) { TEST_CHECK(pong_keys[0].string_value() == "r"); TEST_CHECK(pong_keys[1].string_value() == "10"); } else { fprintf(stdout, " invalid ping response: %s\n", error_string); } // ====== invalid message ====== send_dht_request(node, "find_node", source, &response); dht::key_desc_t err_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"e", bdecode_node::list_t, 2, 0} }; bdecode_node err_keys[2]; fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); ret = dht::verify_message(response, err_desc, err_keys, error_string , sizeof(error_string)); TEST_CHECK(ret); if (ret) { TEST_CHECK(err_keys[0].string_value() == "e"); if (err_keys[1].list_at(0).type() == bdecode_node::int_t && err_keys[1].list_at(1).type() == bdecode_node::string_t) { TEST_CHECK(err_keys[1].list_at(1).string_value() == "missing 'target' key"); } else { TEST_ERROR("invalid error response"); } } else { fprintf(stdout, " invalid error response: %s\n", error_string); } // ====== get_peers ====== send_dht_request(node, "get_peers", source, &response , msg_args().info_hash("01010101010101010101")); dht::key_desc_t peer1_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"r", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"token", bdecode_node::string_t, 0, 0}, {"id", bdecode_node::string_t, 20, key_desc_t::last_child}, }; bdecode_node peer1_keys[4]; std::string token; fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); ret = dht::verify_message(response, peer1_desc, peer1_keys, error_string , sizeof(error_string)); TEST_CHECK(ret); if (ret) { TEST_CHECK(peer1_keys[0].string_value() == "r"); token = peer1_keys[2].string_value(); // fprintf(stdout, "got token: %s\n", token.c_str()); } else { fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); fprintf(stdout, " invalid get_peers response: %s\n", error_string); } // ====== announce ====== send_dht_request(node, "announce_peer", source, &response , msg_args() .info_hash("01010101010101010101") .name("test") .token(token) .port(8080)); dht::key_desc_t ann_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"r", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"id", bdecode_node::string_t, 20, key_desc_t::last_child}, }; bdecode_node ann_keys[3]; fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); ret = dht::verify_message(response, ann_desc, ann_keys, error_string , sizeof(error_string)); TEST_CHECK(ret); if (ret) { TEST_CHECK(ann_keys[0].string_value() == "r"); } else { fprintf(stdout, " invalid announce response: %s\n", error_string); } init_rand_address(); // announce from 100 random IPs and make sure scrape works // 50 downloaders and 50 seeds for (int i = 0; i < 100; ++i) { source = udp::endpoint(rand_v4(), 6000); send_dht_request(node, "get_peers", source, &response , msg_args().info_hash("01010101010101010101")); ret = dht::verify_message(response, peer1_desc, peer1_keys, error_string , sizeof(error_string)); if (ret) { TEST_CHECK(peer1_keys[0].string_value() == "r"); token = peer1_keys[2].string_value(); // fprintf(stdout, "got token: %s\n", token.c_str()); } else { fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); fprintf(stdout, " invalid get_peers response: %s\n", error_string); } response.clear(); send_dht_request(node, "announce_peer", source, &response , msg_args() .info_hash("01010101010101010101") .name("test") .token(token) .port(8080) .seed(i >= 50)); response.clear(); } // ====== get_peers ====== send_dht_request(node, "get_peers", source, &response , msg_args().info_hash("01010101010101010101").scrape(true)); dht::key_desc_t peer2_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"r", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"BFpe", bdecode_node::string_t, 256, 0}, {"BFsd", bdecode_node::string_t, 256, 0}, {"id", bdecode_node::string_t, 20, key_desc_t::last_child}, }; bdecode_node peer2_keys[5]; fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); ret = dht::verify_message(response, peer2_desc, peer2_keys, error_string , sizeof(error_string)); TEST_CHECK(ret); if (ret) { TEST_CHECK(peer2_keys[0].string_value() == "r"); TEST_EQUAL(peer2_keys[1].dict_find_string_value("n"), "test"); bloom_filter<256> downloaders; bloom_filter<256> seeds; downloaders.from_string(peer2_keys[2].string_ptr()); seeds.from_string(peer2_keys[3].string_ptr()); fprintf(stdout, "seeds: %f\n", seeds.size()); fprintf(stdout, "downloaders: %f\n", downloaders.size()); TEST_CHECK(fabs(seeds.size() - 50.f) <= 3.f); TEST_CHECK(fabs(downloaders.size() - 50.f) <= 3.f); } else { fprintf(stdout, " invalid get_peers response: %s\n", error_string); } // ====== test node ID testing ===== { node_id rnd = generate_secret_id(); TEST_CHECK(verify_secret_id(rnd)); rnd[19] ^= 0x55; TEST_CHECK(!verify_secret_id(rnd)); rnd = generate_random_id(); make_id_secret(rnd); TEST_CHECK(verify_secret_id(rnd)); } // ====== test node ID enforcement ====== // enable node_id enforcement sett.enforce_node_id = true; // this is one of the test vectors from: // http://libtorrent.org/dht_sec.html source = udp::endpoint(address::from_string("124.31.75.21"), 1); node_id nid = to_hash("5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee401"); // verify that we reject invalid node IDs // this is now an invalid node-id for 'source' nid[0] = 0x18; int nodes_num = node.size().get<0>(); send_dht_request(node, "find_node", source, &response , msg_args().target("0101010101010101010101010101010101010101").nid(nid)); ret = dht::verify_message(response, err_desc, err_keys, error_string , sizeof(error_string)); TEST_CHECK(ret); if (ret) { TEST_CHECK(err_keys[0].string_value() == "e"); if (err_keys[1].list_at(0).type() == bdecode_node::int_t && err_keys[1].list_at(1).type() == bdecode_node::string_t) { TEST_CHECK(err_keys[1].list_at(1).string_value() == "invalid node ID"); } else { fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); TEST_ERROR("invalid error response"); } } else { fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); fprintf(stdout, " invalid error response: %s\n", error_string); } // a node with invalid node-id shouldn't be added to routing table. TEST_EQUAL(node.size().get<0>(), nodes_num); // now the node-id is valid. nid[0] = 0x5f; send_dht_request(node, "find_node", source, &response , msg_args().target("0101010101010101010101010101010101010101").nid(nid)); dht::key_desc_t nodes_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"r", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"id", bdecode_node::string_t, 20, key_desc_t::last_child}, }; bdecode_node nodes_keys[3]; fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); ret = dht::verify_message(response, nodes_desc, nodes_keys, error_string , sizeof(error_string)); TEST_CHECK(ret); if (ret) { TEST_CHECK(nodes_keys[0].string_value() == "r"); } else { fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); fprintf(stdout, " invalid error response: %s\n", error_string); } // node with valid node-id should be added to routing table. TEST_EQUAL(node.size().get<0>(), nodes_num + 1); sett.enforce_node_id = false; // =========================== bloom_filter<256> test; for (int i = 0; i < 256; ++i) { char adr[50]; snprintf(adr, 50, "192.0.2.%d", i); address a = address::from_string(adr); sha1_hash iphash; hash_address(a, iphash); test.set(iphash); } if (supports_ipv6()) { for (int i = 0; i < 0x3E8; ++i) { char adr[50]; snprintf(adr, 50, "2001:db8::%x", i); address a = address::from_string(adr); sha1_hash iphash; hash_address(a, iphash); test.set(iphash); } } // these are test vectors from BEP 33 // http://www.bittorrent.org/beps/bep_0033.html fprintf(stdout, "test.size: %f\n", test.size()); fprintf(stdout, "%s\n", to_hex(test.to_string()).c_str()); if (supports_ipv6()) { TEST_CHECK(fabs(test.size() - 1224.93f) < 0.001); TEST_CHECK(to_hex(test.to_string()) == "f6c3f5eaa07ffd91bde89f777f26fb2bff37bdb8fb2bbaa2fd3ddde7bacfff75ee7ccbaefe5eedb1fbfaff67f6abff5e43ddbca3fd9b9ffdf4ffd3e9dff12d1bdf59db53dbe9fa5b7ff3b8fdfcde1afb8bedd7be2f3ee71ebbbfe93bcdeefe148246c2bc5dbff7e7efdcf24fd8dc7adffd8fffdfddfff7a4bbeedf5cb95ce81fc7fcff1ff4ffffdfe5f7fdcbb7fd79b3fa1fc77bfe07fff905b7b7ffc7fefeffe0b8370bb0cd3f5b7f2bd93feb4386cfdd6f7fd5bfaf2e9ebffffeecd67adbf7c67f17efd5d75eba6ffeba7fff47a91eb1bfbb53e8abfb5762abe8ff237279bfefbfeef5ffc5febfdfe5adffadfee1fb737ffffbfd9f6aeffeee76b6fd8f72ef"); } else { TEST_CHECK(fabs(test.size() - 257.854f) < 0.001); TEST_CHECK(to_hex(test.to_string()) == "24c0004020043000102012743e00480037110820422110008000c0e302854835a05401a4045021302a306c060001881002d8a0a3a8001901b40a800900310008d2108110c2496a0028700010d804188b01415200082004088026411104a804048002002000080680828c400080cc40020c042c0494447280928041402104080d4240040414a41f0205654800b0811830d2020042b002c5800004a71d0204804a0028120a004c10017801490b834004044106005421000c86900a0020500203510060144e900100924a1018141a028012913f0041802250042280481200002004430804210101c08111c10801001080002038008211004266848606b035001048"); } response.clear(); // ====== put ====== init_rand_address(); udp::endpoint eps[1000]; for (int i = 0; i < 1000; ++i) eps[i] = udp::endpoint(rand_v4(), (rand() % 16534) + 1); announce_item items[] = { { generate_next(), 1 }, { generate_next(), 2 }, { generate_next(), 3 }, { generate_next(), 4 }, { generate_next(), 5 }, { generate_next(), 6 }, { generate_next(), 7 }, { generate_next(), 8 } }; for (int i = 0; i < int(sizeof(items)/sizeof(items[0])); ++i) items[i].gen(); announce_immutable_items(node, eps, items, sizeof(items)/sizeof(items[0])); key_desc_t desc2[] = { { "y", bdecode_node::string_t, 1, 0 } }; bdecode_node desc2_keys[1]; key_desc_t desc_error[] = { { "e", bdecode_node::list_t, 2, 0 }, { "y", bdecode_node::string_t, 1, 0}, }; bdecode_node desc_error_keys[2]; // ==== get / put mutable items === std::pair itemv; std::pair empty_salt; empty_salt.first = NULL; empty_salt.second = 0; char signature[item_sig_len]; char buffer[1200]; int seq = 4; char private_key[item_sk_len]; char public_key[item_pk_len]; for (int with_salt = 0; with_salt < 2; ++with_salt) { seq = 4; fprintf(stdout, "\nTEST GET/PUT%s \ngenerating ed25519 keys\n\n" , with_salt ? " with-salt" : " no-salt"); unsigned char seed[32]; ed25519_create_seed(seed); ed25519_create_keypair((unsigned char*)public_key, (unsigned char*)private_key, seed); fprintf(stdout, "pub: %s priv: %s\n" , to_hex(std::string(public_key, item_pk_len)).c_str() , to_hex(std::string(private_key, item_sk_len)).c_str()); TEST_CHECK(ret); std::pair salt((char*)0, 0); if (with_salt) salt = std::pair("foobar", 6); hasher h(public_key, 32); if (with_salt) h.update(salt.first, salt.second); sha1_hash target_id = h.final(); fprintf(stdout, "target_id: %s\n" , to_hex(target_id.to_string()).c_str()); send_dht_request(node, "get", source, &response , msg_args().target((char*)&target_id[0])); key_desc_t desc[] = { { "r", bdecode_node::dict_t, 0, key_desc_t::parse_children }, { "id", bdecode_node::string_t, 20, 0}, { "token", bdecode_node::string_t, 0, 0}, { "ip", bdecode_node::string_t, 0, key_desc_t::optional | key_desc_t::last_child}, { "y", bdecode_node::string_t, 1, 0}, }; bdecode_node desc_keys[5]; ret = verify_message(response, desc, desc_keys, error_string , sizeof(error_string)); if (ret) { TEST_EQUAL(desc_keys[4].string_value(), "r"); token = desc_keys[2].string_value(); fprintf(stdout, "get response: %s\n" , print_entry(response).c_str()); fprintf(stdout, "got token: %s\n", to_hex(token).c_str()); } else { fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); fprintf(stdout, " invalid get response: %s\n%s\n" , error_string, print_entry(response).c_str()); TEST_ERROR(error_string); } itemv = std::pair(buffer, bencode(buffer, items[0].ent)); sign_mutable_item(itemv, salt, seq, public_key, private_key, signature); TEST_EQUAL(verify_mutable_item(itemv, salt, seq, public_key, signature), true); #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len); #endif send_dht_request(node, "put", source, &response , msg_args() .token(token) .value(items[0].ent) .key(std::string(public_key, item_pk_len)) .sig(std::string(signature, item_sig_len)) .seq(seq) .salt(salt.first)); ret = verify_message(response, desc2, desc2_keys, error_string , sizeof(error_string)); if (ret) { fprintf(stdout, "put response: %s\n" , print_entry(response).c_str()); TEST_EQUAL(desc2_keys[0].string_value(), "r"); } else { fprintf(stdout, " invalid put response: %s\n%s\n" , error_string, print_entry(response).c_str()); TEST_ERROR(error_string); } send_dht_request(node, "get", source, &response , msg_args().target((char*)&target_id[0])); fprintf(stdout, "target_id: %s\n" , to_hex(target_id.to_string()).c_str()); key_desc_t desc3[] = { { "r", bdecode_node::dict_t, 0, key_desc_t::parse_children }, { "id", bdecode_node::string_t, 20, 0}, { "v", bdecode_node::none_t, 0, 0}, { "seq", bdecode_node::int_t, 0, 0}, { "sig", bdecode_node::string_t, 0, 0}, { "ip", bdecode_node::string_t, 0, key_desc_t::optional | key_desc_t::last_child}, { "y", bdecode_node::string_t, 1, 0}, }; bdecode_node desc3_keys[7]; ret = verify_message(response, desc3, desc3_keys, error_string , sizeof(error_string)); if (ret == 0) { fprintf(stdout, "msg: %s\n", print_entry(response).c_str()); fprintf(stdout, " invalid get response: %s\n%s\n" , error_string, print_entry(response).c_str()); TEST_ERROR(error_string); } else { fprintf(stdout, "get response: %s\n" , print_entry(response).c_str()); char value[1020]; char* ptr = value; int value_len = bencode(ptr, items[0].ent); TEST_EQUAL(value_len, desc3_keys[2].data_section().second); TEST_CHECK(memcmp(desc3_keys[2].data_section().first, value, value_len) == 0); TEST_EQUAL(seq, desc3_keys[3].int_value()); } // also test that invalid signatures fail! itemv.second = bencode(buffer, items[0].ent); sign_mutable_item(itemv, salt, seq, public_key, private_key, signature); TEST_EQUAL(verify_mutable_item(itemv, salt, seq, public_key, signature), 1); #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len); #endif // break the signature signature[2] ^= 0xaa; fprintf(stdout, "PUT broken signature\n"); TEST_CHECK(verify_mutable_item(itemv, salt, seq, public_key, signature) != 1); send_dht_request(node, "put", source, &response , msg_args() .token(token) .value(items[0].ent) .key(std::string(public_key, item_pk_len)) .sig(std::string(signature, item_sig_len)) .seq(seq) .salt(salt.first)); ret = verify_message(response, desc_error, desc_error_keys, error_string , sizeof(error_string)); if (ret) { fprintf(stdout, "put response: %s\n", print_entry(response).c_str()); TEST_EQUAL(desc_error_keys[1].string_value(), "e"); // 206 is the code for invalid signature TEST_EQUAL(desc_error_keys[0].list_int_value_at(0), 206); } else { fprintf(stdout, " invalid put response: %s\n%s\n" , error_string, print_entry(response).c_str()); TEST_ERROR(error_string); } // === test conditional get === send_dht_request(node, "get", source, &response , msg_args().target((char*)&target_id[0]).seq(seq - 1)); { bdecode_node r = response.dict_find_dict("r"); TEST_CHECK(r.dict_find("v")); TEST_CHECK(r.dict_find("k")); TEST_CHECK(r.dict_find("sig")); } send_dht_request(node, "get", source, &response , msg_args().target((char*)&target_id[0]).seq(seq)); { bdecode_node r = response.dict_find_dict("r"); TEST_CHECK(!r.dict_find("v")); TEST_CHECK(!r.dict_find("k")); TEST_CHECK(!r.dict_find("sig")); } // === test CAS put === // this is the sequence number we expect to be there boost::uint64_t cas = seq; // increment sequence number ++seq; // put item 1 itemv.second = bencode(buffer, items[1].ent); sign_mutable_item(itemv, salt, seq, public_key, private_key, signature); TEST_EQUAL(verify_mutable_item(itemv, salt, seq, public_key, signature), 1); #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len); #endif TEST_CHECK(item_target_id(salt, public_key) == target_id); fprintf(stdout, "PUT CAS 1\n"); send_dht_request(node, "put", source, &response , msg_args() .token(token) .value(items[1].ent) .key(std::string(public_key, item_pk_len)) .sig(std::string(signature, item_sig_len)) .seq(seq) .cas(cas) .salt(salt.first)); ret = verify_message(response, desc2, desc2_keys, error_string , sizeof(error_string)); if (ret) { fprintf(stdout, "put response: %s\n" , print_entry(response).c_str()); TEST_EQUAL(desc2_keys[0].string_value(), "r"); } else { fprintf(stdout, " invalid put response: %s\n%s\n" , error_string, print_entry(response).c_str()); TEST_ERROR(error_string); } fprintf(stdout, "PUT CAS 2\n"); // put the same message again. This should fail because the // CAS hash is outdated, it's not the hash of the value that's // stored anymore send_dht_request(node, "put", source, &response , msg_args() .token(token) .value(items[1].ent) .key(std::string(public_key, item_pk_len)) .sig(std::string(signature, item_sig_len)) .seq(seq) .cas(cas) .salt(salt.first)); ret = verify_message(response, desc_error, desc_error_keys, error_string , sizeof(error_string)); if (ret) { fprintf(stdout, "put response: %s\n" , print_entry(response).c_str()); TEST_EQUAL(desc_error_keys[1].string_value(), "e"); // 301 is the error code for CAS hash mismatch TEST_EQUAL(desc_error_keys[0].list_int_value_at(0), 301); } else { fprintf(stdout, " invalid put response: %s\n%s\nExpected failure 301 (CAS hash mismatch)\n" , error_string, print_entry(response).c_str()); TEST_ERROR(error_string); } } // test node-id functions using namespace libtorrent::dht; TEST_EQUAL(generate_prefix_mask(0), to_hash("0000000000000000000000000000000000000000")); TEST_EQUAL(generate_prefix_mask(1), to_hash("8000000000000000000000000000000000000000")); TEST_EQUAL(generate_prefix_mask(2), to_hash("c000000000000000000000000000000000000000")); TEST_EQUAL(generate_prefix_mask(11), to_hash("ffe0000000000000000000000000000000000000")); TEST_EQUAL(generate_prefix_mask(17), to_hash("ffff800000000000000000000000000000000000")); TEST_EQUAL(generate_prefix_mask(160), to_hash("ffffffffffffffffffffffffffffffffffffffff")); // test kademlia functions // distance_exp TEST_EQUAL(distance_exp( to_hash("ffffffffffffffffffffffffffffffffffffffff"), to_hash("0000000000000000000000000000000000000000")) , 159); TEST_EQUAL(distance_exp( to_hash("ffffffffffffffffffffffffffffffffffffffff"), to_hash("7fffffffffffffffffffffffffffffffffffffff")) , 159); TEST_EQUAL(distance_exp( to_hash("ffffffffffffffffffffffffffffffffffffffff"), to_hash("ffffffffffffffffffffffffffffffffffffffff")) , 0); TEST_EQUAL(distance_exp( to_hash("ffffffffffffffffffffffffffffffffffffffff"), to_hash("fffffffffffffffffffffffffffffffffffffffe")) , 0); TEST_EQUAL(distance_exp( to_hash("8000000000000000000000000000000000000000"), to_hash("fffffffffffffffffffffffffffffffffffffffe")) , 158); TEST_EQUAL(distance_exp( to_hash("c000000000000000000000000000000000000000"), to_hash("fffffffffffffffffffffffffffffffffffffffe")) , 157); TEST_EQUAL(distance_exp( to_hash("e000000000000000000000000000000000000000"), to_hash("fffffffffffffffffffffffffffffffffffffffe")) , 156); TEST_EQUAL(distance_exp( to_hash("f000000000000000000000000000000000000000"), to_hash("fffffffffffffffffffffffffffffffffffffffe")) , 155); TEST_EQUAL(distance_exp( to_hash("f8f2340985723049587230495872304958703294"), to_hash("f743589043r890f023980f90e203980d090c3840")) , 155); TEST_EQUAL(distance_exp( to_hash("ffff740985723049587230495872304958703294"), to_hash("ffff889043r890f023980f90e203980d090c3840")) , 159 - 16); { // test kademlia routing table dht_settings s; s.extended_routing_table = false; // s.restrict_routing_ips = false; node_id id = to_hash("3123456789abcdef01232456789abcdef0123456"); const int bucket_size = 10; dht::routing_table table(id, bucket_size, s, &observer); std::vector nodes; TEST_EQUAL(table.size().get<0>(), 0); node_id tmp = id; node_id diff = to_hash("15764f7459456a9453f8719b09547c11d5f34061"); // test a node with the same IP:port changing ID add_and_replace(tmp, diff); table.node_seen(tmp, udp::endpoint(address::from_string("4.4.4.4"), 4), 10); table.find_node(id, nodes, 0, 10); TEST_EQUAL(table.bucket_size(0), 1); TEST_EQUAL(table.size().get<0>(), 1); TEST_EQUAL(nodes.size(), 1); if (!nodes.empty()) { TEST_EQUAL(nodes[0].id, tmp); TEST_EQUAL(nodes[0].addr(), address_v4::from_string("4.4.4.4")); TEST_EQUAL(nodes[0].port(), 4); TEST_EQUAL(nodes[0].timeout_count, 0); } // set timeout_count to 1 table.node_failed(tmp, udp::endpoint(address_v4::from_string("4.4.4.4"), 4)); nodes.clear(); table.for_each_node(node_push_back, nop, &nodes); TEST_EQUAL(nodes.size(), 1); if (!nodes.empty()) { TEST_EQUAL(nodes[0].id, tmp); TEST_EQUAL(nodes[0].addr(), address_v4::from_string("4.4.4.4")); TEST_EQUAL(nodes[0].port(), 4); TEST_EQUAL(nodes[0].timeout_count, 1); } // add the exact same node again, it should set the timeout_count to 0 table.node_seen(tmp, udp::endpoint(address::from_string("4.4.4.4"), 4), 10); nodes.clear(); table.for_each_node(node_push_back, nop, &nodes); TEST_EQUAL(nodes.size(), 1); if (!nodes.empty()) { TEST_EQUAL(nodes[0].id, tmp); TEST_EQUAL(nodes[0].addr(), address_v4::from_string("4.4.4.4")); TEST_EQUAL(nodes[0].port(), 4); TEST_EQUAL(nodes[0].timeout_count, 0); } // test adding the same IP:port again with a new node ID (should replace the old one) add_and_replace(tmp, diff); table.node_seen(tmp, udp::endpoint(address::from_string("4.4.4.4"), 4), 10); table.find_node(id, nodes, 0, 10); TEST_EQUAL(table.bucket_size(0), 1); TEST_EQUAL(nodes.size(), 1); if (!nodes.empty()) { TEST_EQUAL(nodes[0].id, tmp); TEST_EQUAL(nodes[0].addr(), address_v4::from_string("4.4.4.4")); TEST_EQUAL(nodes[0].port(), 4); } // test adding the same node ID again with a different IP (should be ignored) table.node_seen(tmp, udp::endpoint(address::from_string("4.4.4.4"), 5), 10); table.find_node(id, nodes, 0, 10); TEST_EQUAL(table.bucket_size(0), 1); if (!nodes.empty()) { TEST_EQUAL(nodes[0].id, tmp); TEST_EQUAL(nodes[0].addr(), address_v4::from_string("4.4.4.4")); TEST_EQUAL(nodes[0].port(), 4); } // test adding a node that ends up in the same bucket with an IP // very close to the current one (should be ignored) // if restrict_routing_ips == true table.node_seen(tmp, udp::endpoint(address::from_string("4.4.4.5"), 5), 10); table.find_node(id, nodes, 0, 10); TEST_EQUAL(table.bucket_size(0), 1); if (!nodes.empty()) { TEST_EQUAL(nodes[0].id, tmp); TEST_EQUAL(nodes[0].addr(), address_v4::from_string("4.4.4.4")); TEST_EQUAL(nodes[0].port(), 4); } s.restrict_routing_ips = false; init_rand_address(); add_and_replace(tmp, diff); table.node_seen(id, udp::endpoint(rand_v4(), rand()), 10); nodes.clear(); for (int i = 0; i < 7000; ++i) { table.node_seen(tmp, udp::endpoint(rand_v4(), rand()), 20 + (tmp[19] & 0xff)); add_and_replace(tmp, diff); } printf("active buckets: %d\n", table.num_active_buckets()); TEST_EQUAL(table.num_active_buckets(), 10); TEST_CHECK(table.size().get<0>() >= 10 * 10); //#error test num_global_nodes //#error test need_refresh #if defined TORRENT_DEBUG table.print_state(std::cout); #endif table.for_each_node(node_push_back, nop, &nodes); printf("nodes: %d\n", int(nodes.size())); std::vector temp; std::generate(tmp.begin(), tmp.end(), random_byte); table.find_node(tmp, temp, 0, nodes.size() * 2); printf("returned-all: %d\n", int(temp.size())); TEST_EQUAL(temp.size(), nodes.size()); // This makes sure enough of the nodes returned are actually // part of the closest nodes std::set duplicates; #ifdef TORRENT_USE_VALGRIND const int reps = 3; #else const int reps = 50; #endif for (int r = 0; r < reps; ++r) { std::generate(tmp.begin(), tmp.end(), random_byte); table.find_node(tmp, temp, 0, bucket_size * 2); printf("returned: %d\n", int(temp.size())); TEST_EQUAL(int(temp.size()), (std::min)(bucket_size * 2, int(nodes.size()))); std::sort(nodes.begin(), nodes.end(), boost::bind(&compare_ref , boost::bind(&node_entry::id, _1) , boost::bind(&node_entry::id, _2), tmp)); int expected = std::accumulate(nodes.begin(), nodes.begin() + (bucket_size * 2) , 0, boost::bind(&sum_distance_exp, _1, _2, tmp)); int sum_hits = std::accumulate(temp.begin(), temp.end() , 0, boost::bind(&sum_distance_exp, _1, _2, tmp)); TEST_EQUAL(bucket_size * 2, int(temp.size())); printf("expected: %d actual: %d\n", expected, sum_hits); TEST_EQUAL(expected, sum_hits); duplicates.clear(); // This makes sure enough of the nodes returned are actually // part of the closest nodes for (std::vector::iterator i = temp.begin() , end(temp.end()); i != end; ++i) { TEST_CHECK(duplicates.count(i->id) == 0); duplicates.insert(i->id); } } using namespace libtorrent::dht; char const* ips[] = { "124.31.75.21", "21.75.31.124", "65.23.51.170", "84.124.73.14", "43.213.53.83", }; int rs[] = { 1,86,22,65,90 }; boost::uint8_t prefixes[][3] = { { 0x5f, 0xbf, 0xbf }, { 0x5a, 0x3c, 0xe9 }, { 0xa5, 0xd4, 0x32 }, { 0x1b, 0x03, 0x21 }, { 0xe5, 0x6f, 0x6c } }; for (int i = 0; i < 5; ++i) { address a = address_v4::from_string(ips[i]); node_id id = generate_id_impl(a, rs[i]); TEST_CHECK(id[0] == prefixes[i][0]); TEST_CHECK(id[1] == prefixes[i][1]); TEST_CHECK((id[2] & 0xf8) == (prefixes[i][2] & 0xf8)); TEST_CHECK(id[19] == rs[i]); fprintf(stdout, "IP address: %s r: %d node ID: %s\n", ips[i] , rs[i], to_hex(id.to_string()).c_str()); } } // test traversal algorithms dht::key_desc_t find_node_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"t", bdecode_node::string_t, 2, 0}, {"q", bdecode_node::string_t, 9, 0}, {"a", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"id", bdecode_node::string_t, 20, 0}, {"target", bdecode_node::string_t, 20, key_desc_t::optional}, {"info_hash", bdecode_node::string_t, 20, key_desc_t::optional | key_desc_t::last_child}, }; bdecode_node find_node_keys[7]; dht::key_desc_t get_peers_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"t", bdecode_node::string_t, 2, 0}, {"q", bdecode_node::string_t, 9, 0}, {"a", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"id", bdecode_node::string_t, 20, 0}, {"info_hash", bdecode_node::string_t, 20, key_desc_t::last_child}, }; bdecode_node get_peers_keys[6]; dht::key_desc_t get_item_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"t", bdecode_node::string_t, 2, 0}, {"q", bdecode_node::string_t, 3, 0}, {"a", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"id", bdecode_node::string_t, 20, 0}, {"target", bdecode_node::string_t, 20, key_desc_t::last_child}, }; bdecode_node get_item_keys[6]; // bootstrap g_sent_packets.clear(); do { dht::node node(&s, sett, (node_id::min)(), &observer, cnt); udp::endpoint initial_node(address_v4::from_string("4.4.4.4"), 1234); std::vector nodesv; nodesv.push_back(initial_node); node.bootstrap(nodesv, boost::bind(&nop)); TEST_EQUAL(g_sent_packets.size(), 1); if (g_sent_packets.empty()) break; TEST_EQUAL(g_sent_packets.front().first, initial_node); lazy_from_entry(g_sent_packets.front().second, response); ret = verify_message(response, find_node_desc, find_node_keys, error_string , sizeof(error_string)); if (ret) { TEST_EQUAL(find_node_keys[0].string_value(), "q"); TEST_CHECK(find_node_keys[2].string_value() == "find_node" || find_node_keys[2].string_value() == "get_peers"); if (find_node_keys[0].string_value() != "q" || (find_node_keys[2].string_value() != "find_node" && find_node_keys[2].string_value() != "get_peers")) break; } else { fprintf(stdout, " invalid find_node request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); break; } udp::endpoint found_node(address_v4::from_string("5.5.5.5"), 2235); nodes_t nodes; nodes.push_back(found_node); g_sent_packets.clear(); send_dht_response(node, response, initial_node, msg_args().nodes(nodes)); TEST_EQUAL(g_sent_packets.size(), 1); if (g_sent_packets.empty()) break; TEST_EQUAL(g_sent_packets.front().first, found_node); lazy_from_entry(g_sent_packets.front().second, response); ret = verify_message(response, find_node_desc, find_node_keys, error_string , sizeof(error_string)); if (ret) { TEST_EQUAL(find_node_keys[0].string_value(), "q"); TEST_CHECK(find_node_keys[2].string_value() == "find_node" || find_node_keys[2].string_value() == "get_peers"); if (find_node_keys[0].string_value() != "q" || (find_node_keys[2].string_value() != "find_node" && find_node_keys[2].string_value() == "get_peers")) break; } else { fprintf(stdout, " invalid find_node request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); break; } g_sent_packets.clear(); send_dht_response(node, response, found_node); TEST_CHECK(g_sent_packets.empty()); TEST_EQUAL(node.num_global_nodes(), 3); } while (false); // get_peers g_sent_packets.clear(); do { dht::node_id target = to_hash("1234876923549721020394873245098347598635"); dht::node node(&s, sett, (node_id::min)(), &observer, cnt); udp::endpoint initial_node(address_v4::from_string("4.4.4.4"), 1234); node.m_table.add_node(initial_node); node.announce(target, 1234, false, get_peers_cb); TEST_EQUAL(g_sent_packets.size(), 1); if (g_sent_packets.empty()) break; TEST_EQUAL(g_sent_packets.front().first, initial_node); lazy_from_entry(g_sent_packets.front().second, response); ret = verify_message(response, get_peers_desc, get_peers_keys, error_string , sizeof(error_string)); if (ret) { TEST_EQUAL(get_peers_keys[0].string_value(), "q"); TEST_EQUAL(get_peers_keys[2].string_value(), "get_peers"); TEST_EQUAL(get_peers_keys[5].string_value(), target.to_string()); if (get_peers_keys[0].string_value() != "q" || get_peers_keys[2].string_value() != "get_peers") break; } else { fprintf(stdout, " invalid get_peers request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); break; } std::set peers[2]; peers[0].insert(tcp::endpoint(address_v4::from_string("4.1.1.1"), 4111)); peers[0].insert(tcp::endpoint(address_v4::from_string("4.1.1.2"), 4112)); peers[0].insert(tcp::endpoint(address_v4::from_string("4.1.1.3"), 4113)); udp::endpoint next_node(address_v4::from_string("5.5.5.5"), 2235); nodes_t nodes; nodes.push_back(next_node); g_sent_packets.clear(); send_dht_response(node, response, initial_node , msg_args().nodes(nodes).token("10").port(1234).peers(peers[0])); TEST_EQUAL(g_sent_packets.size(), 1); if (g_sent_packets.empty()) break; TEST_EQUAL(g_sent_packets.front().first, next_node); lazy_from_entry(g_sent_packets.front().second, response); ret = verify_message(response, get_peers_desc, get_peers_keys, error_string , sizeof(error_string)); if (ret) { TEST_EQUAL(get_peers_keys[0].string_value(), "q"); TEST_EQUAL(get_peers_keys[2].string_value(), "get_peers"); TEST_EQUAL(get_peers_keys[5].string_value(), target.to_string()); if (get_peers_keys[0].string_value() != "q" || get_peers_keys[2].string_value() != "get_peers") break; } else { fprintf(stdout, " invalid get_peers request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); break; } peers[1].insert(tcp::endpoint(address_v4::from_string("4.1.1.4"), 4114)); peers[1].insert(tcp::endpoint(address_v4::from_string("4.1.1.5"), 4115)); peers[1].insert(tcp::endpoint(address_v4::from_string("4.1.1.6"), 4116)); g_sent_packets.clear(); send_dht_response(node, response, next_node , msg_args().token("11").port(1234).peers(peers[1])); for (std::list >::iterator i = g_sent_packets.begin() , end(g_sent_packets.end()); i != end; ++i) { // fprintf(stdout, " %s:%d: %s\n", i->first.address().to_string(ec).c_str() // , i->first.port(), i->second.to_string().c_str()); TEST_EQUAL(i->second["q"].string(), "announce_peer"); } g_sent_packets.clear(); for (int i = 0; i < 2; ++i) { for (std::set::iterator peer = peers[i].begin(); peer != peers[i].end(); ++peer) { TEST_CHECK(std::find(g_got_peers.begin(), g_got_peers.end(), *peer) != g_got_peers.end()); } } g_got_peers.clear(); } while (false); // immutable get g_sent_packets.clear(); do { dht::node node(&s, sett, (node_id::min)(), &observer, cnt); udp::endpoint initial_node(address_v4::from_string("4.4.4.4"), 1234); node.m_table.add_node(initial_node); node.get_item(items[0].target, get_immutable_item_cb); TEST_EQUAL(g_sent_packets.size(), 1); if (g_sent_packets.empty()) break; TEST_EQUAL(g_sent_packets.front().first, initial_node); lazy_from_entry(g_sent_packets.front().second, response); ret = verify_message(response, get_item_desc, get_item_keys, error_string , sizeof(error_string)); if (ret) { TEST_EQUAL(get_item_keys[0].string_value(), "q"); TEST_EQUAL(get_item_keys[2].string_value(), "get"); TEST_EQUAL(get_item_keys[5].string_value(), items[0].target.to_string()); if (get_item_keys[0].string_value() != "q" || get_item_keys[2].string_value() != "get") break; } else { fprintf(stdout, " invalid get request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); break; } g_sent_packets.clear(); send_dht_response(node, response, initial_node , msg_args().token("10").port(1234).value(items[0].ent)); TEST_CHECK(g_sent_packets.empty()); TEST_EQUAL(g_got_items.size(), 1); if (g_got_items.empty()) break; TEST_EQUAL(g_got_items.front().value(), items[0].ent); g_got_items.clear(); } while (false); // mutable get g_sent_packets.clear(); do { dht::node node(&s, sett, (node_id::min)(), &observer, cnt); udp::endpoint initial_node(address_v4::from_string("4.4.4.4"), 1234); node.m_table.add_node(initial_node); node.get_item(public_key, std::string(), get_mutable_item_cb); TEST_EQUAL(g_sent_packets.size(), 1); if (g_sent_packets.empty()) break; TEST_EQUAL(g_sent_packets.front().first, initial_node); lazy_from_entry(g_sent_packets.front().second, response); ret = verify_message(response, get_item_desc, get_item_keys, error_string , sizeof(error_string)); if (ret) { TEST_EQUAL(get_item_keys[0].string_value(), "q"); TEST_EQUAL(get_item_keys[2].string_value(), "get"); if (get_item_keys[0].string_value() != "q" || get_item_keys[2].string_value() != "get") break; } else { fprintf(stdout, " invalid get request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); break; } g_sent_packets.clear(); itemv.second = bencode(buffer, items[0].ent); sign_mutable_item(itemv, empty_salt, seq, public_key, private_key, signature); send_dht_response(node, response, initial_node , msg_args() .token("10") .port(1234) .value(items[0].ent) .key(std::string(public_key, item_pk_len)) .sig(std::string(signature, item_sig_len)) .seq(seq)); TEST_CHECK(g_sent_packets.empty()); TEST_EQUAL(g_got_items.size(), 1); if (g_got_items.empty()) break; TEST_EQUAL(g_got_items.front().value(), items[0].ent); TEST_CHECK(memcmp(g_got_items.front().pk().data(), public_key, item_pk_len) == 0); TEST_CHECK(memcmp(g_got_items.front().sig().data(), signature, item_sig_len) == 0); TEST_EQUAL(int(g_got_items.front().seq()), seq); g_got_items.clear(); } while (false); dht::key_desc_t put_immutable_item_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"t", bdecode_node::string_t, 2, 0}, {"q", bdecode_node::string_t, 3, 0}, {"a", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"id", bdecode_node::string_t, 20, 0}, {"token", bdecode_node::string_t, 2, 0}, {"v", bdecode_node::none_t, 0, key_desc_t::last_child}, }; bdecode_node put_immutable_item_keys[7]; dht::key_desc_t put_mutable_item_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"t", bdecode_node::string_t, 2, 0}, {"q", bdecode_node::string_t, 3, 0}, {"a", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"id", bdecode_node::string_t, 20, 0}, {"cas", bdecode_node::string_t, 20, key_desc_t::optional}, {"k", bdecode_node::string_t, item_pk_len, 0}, {"seq", bdecode_node::int_t, 0, 0}, {"sig", bdecode_node::string_t, item_sig_len, 0}, {"token", bdecode_node::string_t, 2, 0}, {"v", bdecode_node::none_t, 0, key_desc_t::last_child}, }; bdecode_node put_mutable_item_keys[11]; // immutable put g_sent_packets.clear(); for (int loop = 0; loop < 9; loop++) { // set the branching factor to k to make this a little easier int old_branching = sett.search_branching; sett.search_branching = 8; dht::node node(&s, sett, (node_id::min)(), &observer, cnt); enum { num_test_nodes = 8 }; node_entry nodes[num_test_nodes] = { node_entry(items[0].target, udp::endpoint(address_v4::from_string("1.1.1.1"), 1231)) , node_entry(items[1].target, udp::endpoint(address_v4::from_string("2.2.2.2"), 1232)) , node_entry(items[2].target, udp::endpoint(address_v4::from_string("3.3.3.3"), 1233)) , node_entry(items[3].target, udp::endpoint(address_v4::from_string("4.4.4.4"), 1234)) , node_entry(items[4].target, udp::endpoint(address_v4::from_string("5.5.5.5"), 1235)) , node_entry(items[5].target, udp::endpoint(address_v4::from_string("6.6.6.6"), 1236)) , node_entry(items[6].target, udp::endpoint(address_v4::from_string("7.7.7.7"), 1237)) , node_entry(items[7].target, udp::endpoint(address_v4::from_string("8.8.8.8"), 1238)) }; for (int i = 0; i < num_test_nodes; ++i) node.m_table.add_node(nodes[i]); entry put_data; put_data = "Hello world"; std::string flat_data; bencode(std::back_inserter(flat_data), put_data); sha1_hash target = item_target_id( std::pair(flat_data.c_str(), flat_data.size())); node.put_item(target, put_data, boost::bind(&put_immutable_item_cb, _1, loop)); TEST_EQUAL(g_sent_packets.size(), 8); if (g_sent_packets.size() != 8) break; for (int i = 0; i < 8; ++i) { std::list >::iterator packet = find_packet(nodes[i].ep()); TEST_CHECK(packet != g_sent_packets.end()); if (packet == g_sent_packets.end()) continue; lazy_from_entry(packet->second, response); ret = verify_message(response, get_item_desc, get_item_keys, error_string , sizeof(error_string)); if (!ret) { fprintf(stdout, " invalid get request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); continue; } char t[10]; snprintf(t, sizeof(t), "%02d", i); msg_args args; args.token(t).port(1234).nid(nodes[i].id).nodes(nodes_t(1, nodes[i])); send_dht_response(node, response, nodes[i].ep(), args); g_sent_packets.erase(packet); } TEST_EQUAL(g_sent_packets.size(), 8); if (g_sent_packets.size() != 8) break; itemv.second = bencode(buffer, put_data); for (int i = 0; i < 8; ++i) { std::list >::iterator packet = find_packet(nodes[i].ep()); TEST_CHECK(packet != g_sent_packets.end()); if (packet == g_sent_packets.end()) continue; lazy_from_entry(packet->second, response); ret = verify_message(response, put_immutable_item_desc, put_immutable_item_keys , error_string, sizeof(error_string)); if (ret) { TEST_EQUAL(put_immutable_item_keys[0].string_value(), "q"); TEST_EQUAL(put_immutable_item_keys[2].string_value(), "put"); std::pairv = put_immutable_item_keys[6].data_section(); TEST_EQUAL(std::string(v.first, v.second), flat_data); char t[10]; snprintf(t, sizeof(t), "%02d", i); TEST_EQUAL(put_immutable_item_keys[5].string_value(), t); if (put_immutable_item_keys[0].string_value() != "q" || put_immutable_item_keys[2].string_value() != "put") continue; if (i < loop) send_dht_response(node, response, nodes[i].ep()); } else { fprintf(stdout, " invalid immutable put request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); continue; } } sett.search_branching = old_branching; g_sent_packets.clear(); g_put_item.clear(); g_put_count = 0; }; // mutable put g_sent_packets.clear(); for (int loop = 0; loop < 9; loop++) { // set the branching factor to k to make this a little easier int old_branching = sett.search_branching; sett.search_branching = 8; dht::node node(&s, sett, (node_id::min)(), &observer, cnt); enum { num_test_nodes = 8 }; node_entry nodes[num_test_nodes] = { node_entry(items[0].target, udp::endpoint(address_v4::from_string("1.1.1.1"), 1231)) , node_entry(items[1].target, udp::endpoint(address_v4::from_string("2.2.2.2"), 1232)) , node_entry(items[2].target, udp::endpoint(address_v4::from_string("3.3.3.3"), 1233)) , node_entry(items[3].target, udp::endpoint(address_v4::from_string("4.4.4.4"), 1234)) , node_entry(items[4].target, udp::endpoint(address_v4::from_string("5.5.5.5"), 1235)) , node_entry(items[5].target, udp::endpoint(address_v4::from_string("6.6.6.6"), 1236)) , node_entry(items[6].target, udp::endpoint(address_v4::from_string("7.7.7.7"), 1237)) , node_entry(items[7].target, udp::endpoint(address_v4::from_string("8.8.8.8"), 1238)) }; for (int i = 0; i < num_test_nodes; ++i) node.m_table.add_node(nodes[i]); g_put_item.assign(items[0].ent, empty_salt, seq, public_key, private_key); std::string sig(g_put_item.sig().data(), item_sig_len); node.put_item(public_key, std::string() , boost::bind(&put_mutable_item_cb, _1, _2, loop) , put_mutable_item_data_cb); TEST_EQUAL(g_sent_packets.size(), 8); if (g_sent_packets.size() != 8) break; for (int i = 0; i < 8; ++i) { std::list >::iterator packet = find_packet(nodes[i].ep()); TEST_CHECK(packet != g_sent_packets.end()); if (packet == g_sent_packets.end()) continue; lazy_from_entry(packet->second, response); ret = verify_message(response, get_item_desc, get_item_keys, error_string , sizeof(error_string)); if (!ret) { fprintf(stdout, " invalid get request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); continue; } char t[10]; snprintf(t, sizeof(t), "%02d", i); msg_args args; args.token(t).port(1234).nid(nodes[i].id).nodes(nodes_t(1, nodes[i])); send_dht_response(node, response, nodes[i].ep(), args); g_sent_packets.erase(packet); } TEST_EQUAL(g_sent_packets.size(), 8); if (g_sent_packets.size() != 8) break; itemv.second = bencode(buffer, items[0].ent); for (int i = 0; i < 8; ++i) { std::list >::iterator packet = find_packet(nodes[i].ep()); TEST_CHECK(packet != g_sent_packets.end()); if (packet == g_sent_packets.end()) continue; lazy_from_entry(packet->second, response); ret = verify_message(response, put_mutable_item_desc, put_mutable_item_keys , error_string, sizeof(error_string)); if (ret) { TEST_EQUAL(put_mutable_item_keys[0].string_value(), "q"); TEST_EQUAL(put_mutable_item_keys[2].string_value(), "put"); TEST_EQUAL(put_mutable_item_keys[6].string_value(), std::string(public_key, item_pk_len)); TEST_EQUAL(put_mutable_item_keys[7].int_value(), seq); TEST_EQUAL(put_mutable_item_keys[8].string_value(), sig); std::pair v = put_mutable_item_keys[10].data_section(); TEST_EQUAL(v.second, itemv.second); TEST_CHECK(memcmp(v.first, itemv.first, itemv.second) == 0); char t[10]; snprintf(t, sizeof(t), "%02d", i); TEST_EQUAL(put_mutable_item_keys[9].string_value(), t); if (put_mutable_item_keys[0].string_value() != "q" || put_mutable_item_keys[2].string_value() != "put") continue; if (i < loop) send_dht_response(node, response, nodes[i].ep()); } else { fprintf(stdout, " invalid put request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); continue; } } sett.search_branching = old_branching; g_sent_packets.clear(); g_put_item.clear(); g_put_count = 0; } // verify that done() is only invoked once // See PR 252 g_sent_packets.clear(); do { // set the branching factor to k to make this a little easier int old_branching = sett.search_branching; sett.search_branching = 8; dht::node node(&s, sett, (node_id::min)(), &observer, cnt); sha1_hash target = hasher(public_key, item_pk_len).final(); enum { num_test_nodes = 9 }; // we need K + 1 nodes to create the failing sequence node_entry nodes[num_test_nodes] = { node_entry(target, udp::endpoint(address_v4::from_string("1.1.1.1"), 1231)) , node_entry(target, udp::endpoint(address_v4::from_string("2.2.2.2"), 1232)) , node_entry(target, udp::endpoint(address_v4::from_string("3.3.3.3"), 1233)) , node_entry(target, udp::endpoint(address_v4::from_string("4.4.4.4"), 1234)) , node_entry(target, udp::endpoint(address_v4::from_string("5.5.5.5"), 1235)) , node_entry(target, udp::endpoint(address_v4::from_string("6.6.6.6"), 1236)) , node_entry(target, udp::endpoint(address_v4::from_string("7.7.7.7"), 1237)) , node_entry(target, udp::endpoint(address_v4::from_string("8.8.8.8"), 1238)) , node_entry(target, udp::endpoint(address_v4::from_string("9.9.9.9"), 1239)) }; // invert the ith most significant byte so that the test nodes are // progressivly closer to the target item for (int i = 0; i < num_test_nodes; ++i) nodes[i].id[i] = ~nodes[i].id[i]; // add the first k nodes to the subject's routing table for (int i = 0; i < 8; ++i) node.m_table.add_node(nodes[i]); // kick off a mutable put request g_put_item.assign(items[0].ent, empty_salt, seq, public_key, private_key); node.put_item(public_key, std::string() , boost::bind(&put_mutable_item_cb, _1, _2, 0) , put_mutable_item_data_cb); TEST_EQUAL(g_sent_packets.size(), 8); if (g_sent_packets.size() != 8) break; // first send responses for the k closest nodes for (int i = 1;; ++i) { // once the k closest nodes have responded, send the final response // from the farthest node, this shouldn't trigger a second call to // get_item_cb if (i == num_test_nodes) i = 0; std::list >::iterator packet = find_packet(nodes[i].ep()); TEST_CHECK(packet != g_sent_packets.end()); if (packet == g_sent_packets.end()) continue; lazy_from_entry(packet->second, response); ret = verify_message(response, get_item_desc, get_item_keys, error_string , sizeof(error_string)); if (!ret) { fprintf(stdout, " invalid get request: %s\n", print_entry(response).c_str()); TEST_ERROR(error_string); continue; } char t[10]; snprintf(t, sizeof(t), "%02d", i); msg_args args; args.token(t).port(1234).nid(nodes[i].id); // add the address of the closest node to the first response if (i == 1) args.nodes(nodes_t(1, nodes[8])); send_dht_response(node, response, nodes[i].ep(), args); g_sent_packets.erase(packet); // once we've sent the response from the farthest node, we're done if (i == 0) break; } TEST_EQUAL(g_put_count, 1); // k nodes should now have outstanding put requests TEST_EQUAL(g_sent_packets.size(), 8); g_sent_packets.clear(); g_put_item.clear(); g_put_count = 0; sett.search_branching = old_branching; } while (false); } void get_test_keypair(char* public_key, char* private_key) { from_hex("77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548", 64, public_key); from_hex("e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74d" "b7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d", 128, private_key); } TORRENT_TEST(signing_test1) { // test vector 1 // test content std::pair test_content("12:Hello World!", 15); // test salt std::pair test_salt("foobar", 6); char private_key[item_sk_len]; char public_key[item_pk_len]; get_test_keypair(public_key, private_key); std::pair empty_salt; char signature[item_sig_len]; sign_mutable_item(test_content, empty_salt, 1, public_key , private_key, signature); TEST_EQUAL(to_hex(std::string(signature, 64)) , "305ac8aeb6c9c151fa120f120ea2cfb923564e11552d06a5d856091e5e853cff" "1260d3f39e4999684aa92eb73ffd136e6f4f3ecbfda0ce53a1608ecd7ae21f01"); sha1_hash target_id = item_target_id(empty_salt, public_key); TEST_EQUAL(to_hex(target_id.to_string()), "4a533d47ec9c7d95b1ad75f576cffc641853b750"); } TORRENT_TEST(signing_test2) { char private_key[item_sk_len]; char public_key[item_pk_len]; get_test_keypair(public_key, private_key); // test content std::pair test_content("12:Hello World!", 15); char signature[item_sig_len]; // test salt std::pair test_salt("foobar", 6); // test vector 2 (the keypair is the same as test 1) sign_mutable_item(test_content, test_salt, 1, public_key , private_key, signature); TEST_EQUAL(to_hex(std::string(signature, 64)) , "6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17d" "df9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08"); sha1_hash target_id = item_target_id(test_salt, public_key); TEST_EQUAL(to_hex(target_id.to_string()), "411eba73b6f087ca51a3795d9c8c938d365e32c1"); } TORRENT_TEST(signing_test3) { // test vector 3 // test content std::pair test_content("12:Hello World!", 15); sha1_hash target_id = item_target_id(test_content); TEST_EQUAL(to_hex(target_id.to_string()), "e5f96f6f38320f0f33959cb4d3d656452117aadb"); } // TODO: 2 split this up into smaller test cases TORRENT_TEST(verify_message) { char error_string[200]; // test verify_message static const key_desc_t msg_desc[] = { {"A", bdecode_node::string_t, 4, 0}, {"B", bdecode_node::dict_t, 0, key_desc_t::optional | key_desc_t::parse_children}, {"B1", bdecode_node::string_t, 0, 0}, {"B2", bdecode_node::string_t, 0, key_desc_t::last_child}, {"C", bdecode_node::dict_t, 0, key_desc_t::optional | key_desc_t::parse_children}, {"C1", bdecode_node::string_t, 0, 0}, {"C2", bdecode_node::string_t, 0, key_desc_t::last_child}, }; bdecode_node msg_keys[7]; bdecode_node ent; error_code ec; char const test_msg[] = "d1:A4:test1:Bd2:B15:test22:B25:test3ee"; bdecode(test_msg, test_msg + sizeof(test_msg)-1, ent, ec); fprintf(stdout, "%s\n", print_entry(ent).c_str()); bool ret = verify_message(ent, msg_desc, msg_keys, error_string , sizeof(error_string)); TEST_CHECK(ret); TEST_CHECK(msg_keys[0]); if (msg_keys[0]) TEST_EQUAL(msg_keys[0].string_value(), "test"); TEST_CHECK(msg_keys[1]); TEST_CHECK(msg_keys[2]); if (msg_keys[2]) TEST_EQUAL(msg_keys[2].string_value(), "test2"); TEST_CHECK(msg_keys[3]); if (msg_keys[3]) TEST_EQUAL(msg_keys[3].string_value(), "test3"); TEST_CHECK(!msg_keys[4]); TEST_CHECK(!msg_keys[5]); TEST_CHECK(!msg_keys[6]); char const test_msg2[] = "d1:A4:test1:Cd2:C15:test22:C25:test3ee"; bdecode(test_msg2, test_msg2 + sizeof(test_msg2)-1, ent, ec); fprintf(stdout, "%s\n", print_entry(ent).c_str()); ret = verify_message(ent, msg_desc, msg_keys, error_string , sizeof(error_string)); TEST_CHECK(ret); TEST_CHECK(msg_keys[0]); if (msg_keys[0]) TEST_EQUAL(msg_keys[0].string_value(), "test"); TEST_CHECK(!msg_keys[1]); TEST_CHECK(!msg_keys[2]); TEST_CHECK(!msg_keys[3]); TEST_CHECK(msg_keys[4]); TEST_CHECK(msg_keys[5]); if (msg_keys[5]) TEST_EQUAL(msg_keys[5].string_value(), "test2"); TEST_CHECK(msg_keys[6]); if (msg_keys[6]) TEST_EQUAL(msg_keys[6].string_value(), "test3"); char const test_msg3[] = "d1:Cd2:C15:test22:C25:test3ee"; bdecode(test_msg3, test_msg3 + sizeof(test_msg3)-1, ent, ec); fprintf(stdout, "%s\n", print_entry(ent).c_str()); ret = verify_message(ent, msg_desc, msg_keys, error_string , sizeof(error_string)); TEST_CHECK(!ret); fprintf(stdout, "%s\n", error_string); TEST_EQUAL(error_string, std::string("missing 'A' key")); char const test_msg4[] = "d1:A6:foobare"; bdecode(test_msg4, test_msg4 + sizeof(test_msg4)-1, ent, ec); fprintf(stdout, "%s\n", print_entry(ent).c_str()); ret = verify_message(ent, msg_desc, msg_keys, error_string , sizeof(error_string)); TEST_CHECK(!ret); fprintf(stdout, "%s\n", error_string); TEST_EQUAL(error_string, std::string("invalid value for 'A'")); char const test_msg5[] = "d1:A4:test1:Cd2:C15:test2ee"; bdecode(test_msg5, test_msg5 + sizeof(test_msg5)-1, ent, ec); fprintf(stdout, "%s\n", print_entry(ent).c_str()); ret = verify_message(ent, msg_desc, msg_keys, error_string , sizeof(error_string)); TEST_CHECK(!ret); fprintf(stdout, "%s\n", error_string); TEST_EQUAL(error_string, std::string("missing 'C2' key")); // test empty strings [ { "":1 }, "" ] char const test_msg6[] = "ld0:i1ee0:e"; bdecode(test_msg6, test_msg6 + sizeof(test_msg6)-1, ent, ec); fprintf(stdout, "%s\n", print_entry(ent).c_str()); TEST_CHECK(ent.type() == bdecode_node::list_t); if (ent.type() == bdecode_node::list_t) { TEST_CHECK(ent.list_size() == 2); if (ent.list_size() == 2) { TEST_CHECK(ent.list_at(0).dict_find_int_value("") == 1); TEST_CHECK(ent.list_at(1).string_value() == ""); } } } TORRENT_TEST(routing_table_uniform) { // test routing table dht_settings sett = test_settings(); obs observer; sett.extended_routing_table = false; node_id id = to_hash("1234876923549721020394873245098347598635"); node_id diff = to_hash("15764f7459456a9453f8719b09547c11d5f34061"); routing_table tbl(id, 8, sett, &observer); // insert 256 nodes evenly distributed across the ID space. // we expect to fill the top 5 buckets for (int i = 255; i >= 0; --i) { // test a node with the same IP:port changing ID add_and_replace(id, diff); // in order to make this node-load a bit more realistic, start from // distant nodes and work our way in closer to the node id // the routing table will reject nodes that are too imbalanced (if all // nodes are very close to our ID and none are far away, it's // suspicious). id[0] ^= i; tbl.node_seen(id, rand_udp_ep(), 20 + (id[19] & 0xff)); // restore the first byte of the node ID id[0] ^= i; } printf("num_active_buckets: %d\n", tbl.num_active_buckets()); // number of nodes per tree level (when adding 256 evenly distributed // nodes): // 0: 128 // 1: 64 // 2: 32 // 3: 16 // 4: 8 // i.e. no more than 5 levels TEST_EQUAL(tbl.num_active_buckets(), 5); #if defined TORRENT_DEBUG tbl.print_state(std::cout); #endif } TORRENT_TEST(routing_table_balance) { dht_settings sett = test_settings(); obs observer; sett.extended_routing_table = false; node_id id = to_hash("1234876923549721020394873245098347598635"); routing_table tbl(id, 8, sett, &observer); // insert nodes in the routing table that will force it to split // and make sure we don't end up with a table completely out of balance for (int i = 0; i < 32; ++i) { id[4] = i; tbl.node_seen(id, rand_udp_ep(), 20 + (id[19] & 0xff)); } printf("num_active_buckets: %d\n", tbl.num_active_buckets()); TEST_EQUAL(tbl.num_active_buckets(), 2); #if defined TORRENT_DEBUG tbl.print_state(std::cout); #endif } TORRENT_TEST(routing_table_extended) { dht_settings sett = test_settings(); obs observer; sett.extended_routing_table = true; node_id id = to_hash("1234876923549721020394873245098347598635"); node_id diff = to_hash("15764f7459456a9453f8719b09547c11d5f34061"); // we can't add the nodes in straight 0,1,2,3 order. That way the routing // table would get unbalanced and intermediate nodes would be dropped std::vector node_id_prefix; node_id_prefix.reserve(256); for (int i = 0; i < 256; ++i) node_id_prefix.push_back(i); std::random_shuffle(node_id_prefix.begin(), node_id_prefix.end()); routing_table tbl(id, 8, sett, &observer); for (int i = 0; i < 256; ++i) { add_and_replace(id, diff); id[0] = node_id_prefix[i]; tbl.node_seen(id, rand_udp_ep(), 20 + (id[19] & 0xff)); } TEST_EQUAL(tbl.num_active_buckets(), 6); #if defined TORRENT_DEBUG tbl.print_state(std::cout); #endif } void inserter(std::set* nodes, node_entry const& ne) { nodes->insert(nodes->begin(), ne.id); } TORRENT_TEST(routing_table_set_id) { dht_settings sett = test_settings(); sett.enforce_node_id = false; sett.extended_routing_table = false; obs observer; node_id id = to_hash("0000000000000000000000000000000000000000"); // we can't add the nodes in straight 0,1,2,3 order. That way the routing // table would get unbalanced and intermediate nodes would be dropped std::vector node_id_prefix; node_id_prefix.reserve(256); for (int i = 0; i < 256; ++i) node_id_prefix.push_back(i); std::random_shuffle(node_id_prefix.begin(), node_id_prefix.end()); routing_table tbl(id, 8, sett, &observer); for (int i = 0; i < 256; ++i) { id[0] = node_id_prefix[i]; tbl.node_seen(id, rand_udp_ep(), 20 + (id[19] & 0xff)); } TEST_EQUAL(tbl.num_active_buckets(), 6); std::set original_nodes; tbl.for_each_node(boost::bind(&inserter, &original_nodes, _1)); #if defined TORRENT_DEBUG tbl.print_state(std::cout); #endif id = to_hash("ffffffffffffffffffffffffffffffffffffffff"); tbl.update_node_id(id); TEST_CHECK(tbl.num_active_buckets() <= 4); std::set remaining_nodes; tbl.for_each_node(boost::bind(&inserter, &remaining_nodes, _1)); std::set intersection; std::set_intersection(remaining_nodes.begin(), remaining_nodes.end() , original_nodes.begin(), original_nodes.end() , std::inserter(intersection, intersection.begin())); // all remaining nodes also exist in the original nodes TEST_EQUAL(intersection.size(), remaining_nodes.size()); #if defined TORRENT_DEBUG tbl.print_state(std::cout); #endif } TORRENT_TEST(read_only_node) { dht_settings sett = test_settings(); sett.read_only = true; mock_socket s; obs observer; counters cnt; dht::node node(&s, sett, node_id(0), &observer, cnt); udp::endpoint source(address::from_string("10.0.0.1"), 20); bdecode_node response; msg_args args; // for incoming requests, read_only node won't response. send_dht_request(node, "ping", source, &response, args, "10", false); TEST_EQUAL(response.type(), bdecode_node::none_t); args.target("01010101010101010101"); send_dht_request(node, "get", source, &response, args, "10", false); TEST_EQUAL(response.type(), bdecode_node::none_t); // also, the sender shouldn't be added to routing table. TEST_EQUAL(node.size().get<0>(), 0); // for outgoing requests, read_only node will add 'ro' key (value == 1) // in top-level of request. bdecode_node parsed[7]; char error_string[200]; udp::endpoint initial_node(address_v4::from_string("4.4.4.4"), 1234); node.m_table.add_node(initial_node); bdecode_node request; sha1_hash target = generate_next(); node.get_item(target, get_immutable_item_cb); TEST_EQUAL(g_sent_packets.size(), 1); TEST_EQUAL(g_sent_packets.front().first, initial_node); dht::key_desc_t get_item_desc[] = { {"y", bdecode_node::string_t, 1, 0}, {"t", bdecode_node::string_t, 2, 0}, {"q", bdecode_node::string_t, 3, 0}, {"ro", bdecode_node::int_t, 4, key_desc_t::optional}, {"a", bdecode_node::dict_t, 0, key_desc_t::parse_children}, {"id", bdecode_node::string_t, 20, 0}, {"target", bdecode_node::string_t, 20, key_desc_t::last_child}, }; lazy_from_entry(g_sent_packets.front().second, request); bool ret = verify_message(request, get_item_desc, parsed, error_string , sizeof(error_string)); TEST_CHECK(ret); TEST_EQUAL(parsed[3].int_value(), 1); // should have one node now, which is 4.4.4.4:1234 TEST_EQUAL(node.size().get<0>(), 1); // now, disable read_only, try again. g_sent_packets.clear(); sett.read_only = false; send_dht_request(node, "get", source, &response); // sender should be added to routing table, there are 2 nodes now. TEST_EQUAL(node.size().get<0>(), 2); g_sent_packets.clear(); target = generate_next(); node.get_item(target, get_immutable_item_cb); // since we have 2 nodes, we should have two packets. TEST_EQUAL(g_sent_packets.size(), 2); // both of them shouldn't have a 'ro' key. lazy_from_entry(g_sent_packets.front().second, request); ret = verify_message(request, get_item_desc, parsed, error_string , sizeof(error_string)); TEST_CHECK(ret); TEST_CHECK(!parsed[3]); lazy_from_entry(g_sent_packets.back().second, request); ret = verify_message(request, get_item_desc, parsed, error_string , sizeof(error_string)); TEST_CHECK(ret); TEST_CHECK(!parsed[3]); } #ifndef TORRENT_DISABLE_LOGGING // these tests rely on logging being enabled TORRENT_TEST(invalid_error_msg) { dht_settings sett = test_settings(); mock_socket s; obs observer; counters cnt; dht::node node(&s, sett, node_id(0), &observer, cnt); udp::endpoint source(address::from_string("10.0.0.1"), 20); entry e; e["y"] = "e"; e["e"].string() = "Malformed Error"; char msg_buf[1500]; int size = bencode(msg_buf, e); bdecode_node decoded; error_code ec; bdecode(msg_buf, msg_buf + size, decoded, ec); if (ec) fprintf(stdout, "bdecode failed: %s\n", ec.message().c_str()); dht::msg m(decoded, source); node.incoming(m); bool found = false; for (int i = 0; i < int(observer.m_log.size()); ++i) { if (observer.m_log[i].find("INCOMING ERROR") != std::string::npos && observer.m_log[i].find("(malformed)") != std::string::npos) found = true; printf("%s\n", observer.m_log[i].c_str()); } TEST_EQUAL(found, true); } TORRENT_TEST(rpc_invalid_error_msg) { dht_settings sett = test_settings(); mock_socket s; obs observer; counters cnt; dht::routing_table table(node_id(), 8, sett, &observer); dht::rpc_manager rpc(node_id(), sett, table, &s, &observer); dht::node node(&s, sett, node_id(0), &observer, cnt); udp::endpoint source(address::from_string("10.0.0.1"), 20); // we need this to create an entry for this transaction ID, otherwise the // incoming message will just be dropped entry req; req["y"] = "q"; req["q"] = "bogus_query"; req["t"] = "\0\0\0\0"; g_sent_packets.clear(); boost::intrusive_ptr algo(new dht::traversal_algorithm( node, node_id())); observer_ptr o(new (rpc.allocate_observer()) null_observer(algo, source, node_id())); #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS o->m_in_constructor = false; #endif rpc.invoke(req, source, o); // here's the incoming (malformed) error message entry err; err["y"] = "e"; err["e"].string() = "Malformed Error"; err["t"] = g_sent_packets.begin()->second["t"].string(); char msg_buf[1500]; int size = bencode(msg_buf, err); bdecode_node decoded; error_code ec; bdecode(msg_buf, msg_buf + size, decoded, ec); if (ec) fprintf(stdout, "bdecode failed: %s\n", ec.message().c_str()); dht::msg m(decoded, source); node_id nid; rpc.incoming(m, &nid); bool found = false; for (int i = 0; i < int(observer.m_log.size()); ++i) { if (observer.m_log[i].find("reply with") != std::string::npos && observer.m_log[i].find("(malformed)") != std::string::npos && observer.m_log[i].find("error") != std::string::npos) found = true; printf("%s\n", observer.m_log[i].c_str()); } TEST_EQUAL(found, true); } #endif // test bucket distribution TORRENT_TEST(node_id_bucket_distribution) { int nodes_per_bucket[160] = {0}; dht::node_id reference_id = generate_id(rand_v4()); int const num_samples = 100000; for (int i = 0; i < num_samples; ++i) { dht::node_id nid = generate_id(rand_v4()); int const bucket = 159 - distance_exp(reference_id, nid); ++nodes_per_bucket[bucket]; } for (int i = 0; i < 25; ++i) { printf("%3d ", nodes_per_bucket[i]); } printf("\n"); int expected = num_samples / 2; for (int i = 0; i < 25; ++i) { TEST_CHECK(std::abs(nodes_per_bucket[i] - expected) < num_samples / 20); expected /= 2; } } TORRENT_TEST(dht_verify_node_address) { obs observer; // initial setup taken from dht test above dht_settings s; s.extended_routing_table = false; node_id id = to_hash("3123456789abcdef01232456789abcdef0123456"); const int bucket_size = 10; dht::routing_table table(id, bucket_size, s, &observer); std::vector nodes; TEST_EQUAL(table.size().get<0>(), 0); node_id tmp = id; node_id diff = to_hash("15764f7459456a9453f8719b09547c11d5f34061"); add_and_replace(tmp, diff); table.node_seen(tmp, udp::endpoint(address::from_string("4.4.4.4"), 4), 10); table.find_node(id, nodes, 0, 10); TEST_EQUAL(table.size().get<0>(), 1); TEST_EQUAL(nodes.size(), 1); // incorrect data, wrong id table.node_seen(to_hash("0123456789abcdef01232456789abcdef0123456") , udp::endpoint(address::from_string("4.4.4.4"), 4), 10); table.find_node(id, nodes, 0, 10); TEST_EQUAL(table.size().get<0>(), 1); TEST_EQUAL(nodes.size(), 1); // incorrect data, wrong IP table.node_seen(tmp , udp::endpoint(address::from_string("4.4.4.6"), 4), 10); table.find_node(id, nodes, 0, 10); TEST_EQUAL(table.size().get<0>(), 1); TEST_EQUAL(nodes.size(), 1); } TORRENT_TEST(routing_table_for_each) { dht_settings sett = test_settings(); obs observer; sett.extended_routing_table = false; node_id id = to_hash("1234876923549721020394873245098347598635"); dht::routing_table tbl(id, 2, sett, &observer); for (int i = 0; i < 32; ++i) { id[4] = i; tbl.node_seen(id, rand_udp_ep(), 20 + (id[19] & 0xff)); } int nodes; int replacements; boost::tie(nodes, replacements, boost::tuples::ignore) = tbl.size(); std::printf("num_active_buckets: %d\n", tbl.num_active_buckets()); std::printf("live nodes: %d\n", nodes); std::printf("replacements: %d\n", replacements); TEST_EQUAL(tbl.num_active_buckets(), 2); TEST_EQUAL(nodes, 2); TEST_EQUAL(replacements, 2); #if defined TORRENT_DEBUG tbl.print_state(std::cout); #endif std::vector v; tbl.for_each_node(node_push_back, nop, &v); TEST_EQUAL(v.size(), 2); v.clear(); tbl.for_each_node(nop, node_push_back, &v); TEST_EQUAL(v.size(), 2); v.clear(); tbl.for_each_node(node_push_back, node_push_back, &v); TEST_EQUAL(v.size(), 4); } #endif libtorrent-rasterbar-1.1.13/test/test_dht_storage.cpp000066400000000000000000000263541351156116000230020ustar00rootroot00000000000000/* Copyright (c) 2015, Alden Torres 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 #include "libtorrent/config.hpp" #include "libtorrent/session.hpp" #include "libtorrent/kademlia/node.hpp" // for verify_message #include "libtorrent/bencode.hpp" #include "libtorrent/socket_io.hpp" // for hash_address #include "libtorrent/broadcast_socket.hpp" // for supports_ipv6 #include "libtorrent/performance_counters.hpp" // for counters #include "libtorrent/random.hpp" #include "libtorrent/ed25519.hpp" #include "libtorrent/kademlia/dht_storage.hpp" #include "libtorrent/kademlia/node_id.hpp" #include "libtorrent/kademlia/routing_table.hpp" #include "libtorrent/kademlia/item.hpp" #include "libtorrent/kademlia/dht_observer.hpp" #include "libtorrent/random.hpp" #include "libtorrent/ed25519.hpp" #include #include "test.hpp" #include "setup_transfer.hpp" using namespace libtorrent; using namespace libtorrent::dht; namespace lt = libtorrent; namespace { dht_settings test_settings() { dht_settings sett; sett.max_torrents = 2; sett.max_dht_items = 2; sett.item_lifetime = seconds(120 * 60).count(); return sett; } static sha1_hash to_hash(char const *s) { sha1_hash ret; from_hex(s, 40, (char *) &ret[0]); return ret; } bool g_storage_constructor_invoked = false; dht_storage_interface* dht_custom_storage_constructor(sha1_hash const& id , dht_settings const& settings) { g_storage_constructor_invoked = true; return dht_default_storage_constructor(id, settings); } } const sha1_hash n1 = to_hash("5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee401"); const sha1_hash n2 = to_hash("5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee402"); const sha1_hash n3 = to_hash("5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee403"); const sha1_hash n4 = to_hash("5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee404"); TORRENT_TEST(announce_peer) { dht_settings sett = test_settings(); boost::scoped_ptr s(dht_default_storage_constructor(node_id(0), sett)); TEST_CHECK(s.get() != NULL); entry peers; s->get_peers(n1, false, false, peers); TEST_CHECK(peers["n"].string().empty()) TEST_CHECK(peers["values"].list().empty()); tcp::endpoint p1 = ep("124.31.75.21", 1); tcp::endpoint p2 = ep("124.31.75.22", 1); tcp::endpoint p3 = ep("124.31.75.23", 1); tcp::endpoint p4 = ep("124.31.75.24", 1); s->announce_peer(n1, p1, "torrent_name", false); s->get_peers(n1, false, false, peers); TEST_EQUAL(peers["n"].string(), "torrent_name") TEST_EQUAL(peers["values"].list().size(), 1) s->announce_peer(n2, p2, "torrent_name1", false); s->announce_peer(n2, p3, "torrent_name1", false); s->announce_peer(n3, p4, "torrent_name2", false); bool r = s->get_peers(n1, false, false, peers); TEST_CHECK(!r); } TORRENT_TEST(put_immutable_item) { dht_settings sett = test_settings(); boost::scoped_ptr s(dht_default_storage_constructor(node_id(0), sett)); TEST_CHECK(s.get() != NULL); entry item; bool r = s->get_immutable_item(n4, item); TEST_CHECK(!r); s->put_immutable_item(n4, "123", 3, address::from_string("124.31.75.21")); r = s->get_immutable_item(n4, item); TEST_CHECK(r); s->put_immutable_item(n1, "123", 3, address::from_string("124.31.75.21")); s->put_immutable_item(n2, "123", 3, address::from_string("124.31.75.21")); s->put_immutable_item(n3, "123", 3, address::from_string("124.31.75.21")); r = s->get_immutable_item(n1, item); TEST_CHECK(!r); r = s->get_mutable_item(n4, 0, false, item); TEST_CHECK(!r); char public_key[item_pk_len]; char signature[item_sig_len]; s->put_mutable_item(n4, "123", 3, signature, 1, public_key, "salt", 4, address::from_string("124.31.75.21")); r = s->get_mutable_item(n4, 0, false, item); TEST_CHECK(r); } TORRENT_TEST(counters) { dht_settings sett = test_settings(); boost::scoped_ptr s(dht_default_storage_constructor(node_id(0), sett)); TEST_CHECK(s.get() != NULL); sha1_hash n1 = to_hash("5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee401"); sha1_hash n2 = to_hash("5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee402"); sha1_hash n3 = to_hash("5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee403"); sha1_hash n4 = to_hash("5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee404"); TEST_EQUAL(s->counters().peers, 0); TEST_EQUAL(s->counters().torrents, 0); tcp::endpoint p1 = ep("124.31.75.21", 1); tcp::endpoint p2 = ep("124.31.75.22", 1); tcp::endpoint p3 = ep("124.31.75.23", 1); tcp::endpoint p4 = ep("124.31.75.24", 1); s->announce_peer(n1, p1, "torrent_name", false); TEST_EQUAL(s->counters().peers, 1); TEST_EQUAL(s->counters().torrents, 1); s->announce_peer(n2, p2, "torrent_name1", false); s->announce_peer(n2, p3, "torrent_name1", false); s->announce_peer(n3, p4, "torrent_name2", false); TEST_EQUAL(s->counters().peers, 3); TEST_EQUAL(s->counters().torrents, 2); entry item; s->put_immutable_item(n4, "123", 3, address::from_string("124.31.75.21")); TEST_EQUAL(s->counters().immutable_data, 1); s->put_immutable_item(n1, "123", 3, address::from_string("124.31.75.21")); s->put_immutable_item(n2, "123", 3, address::from_string("124.31.75.21")); s->put_immutable_item(n3, "123", 3, address::from_string("124.31.75.21")); TEST_EQUAL(s->counters().immutable_data, 2); char public_key[item_pk_len]; char signature[item_sig_len]; s->put_mutable_item(n4, "123", 3, signature, 1, public_key, "salt", 4, address::from_string("124.31.75.21")); TEST_EQUAL(s->counters().mutable_data, 1); } TORRENT_TEST(set_custom) { g_storage_constructor_invoked = false; settings_pack p; p.set_bool(settings_pack::enable_dht, false); p.set_str(settings_pack::dht_bootstrap_nodes, ""); lt::session ses(p); TEST_EQUAL(g_storage_constructor_invoked, false); bool r = ses.is_dht_running(); TEST_CHECK(!r); ses.set_dht_storage(dht_custom_storage_constructor); p.set_bool(settings_pack::enable_dht, true); p.set_str(settings_pack::dht_bootstrap_nodes, ""); ses.apply_settings(p); // async with dispatch r = ses.is_dht_running(); TEST_CHECK(r); TEST_EQUAL(g_storage_constructor_invoked, true); } TORRENT_TEST(default_set_custom) { g_storage_constructor_invoked = false; settings_pack p; p.set_bool(settings_pack::enable_dht, true); p.set_str(settings_pack::dht_bootstrap_nodes, ""); lt::session ses(p); bool r = ses.is_dht_running(); TEST_CHECK(r); ses.set_dht_storage(dht_custom_storage_constructor); p.set_bool(settings_pack::enable_dht, false); ses.apply_settings(p); // async with dispatch r = ses.is_dht_running(); TEST_CHECK(!r); TEST_EQUAL(g_storage_constructor_invoked, false); ses.set_dht_storage(dht_custom_storage_constructor); p.set_bool(settings_pack::enable_dht, true); ses.apply_settings(p); // async with dispatch r = ses.is_dht_running(); TEST_CHECK(r); TEST_EQUAL(g_storage_constructor_invoked, true); } TORRENT_TEST(peer_limit) { dht_settings sett = test_settings(); sett.max_peers = 42; boost::scoped_ptr s(dht_default_storage_constructor(node_id(0), sett)); TEST_CHECK(s.get() != NULL); for (int i = 0; i < 200; ++i) { s->announce_peer(n1, tcp::endpoint(rand_v4(), lt::random()) , "torrent_name", false); dht_storage_counters cnt = s->counters(); TEST_CHECK(cnt.peers <= 42); } dht_storage_counters cnt = s->counters(); TEST_EQUAL(cnt.peers, 42); } TORRENT_TEST(torrent_limit) { dht_settings sett = test_settings(); sett.max_torrents = 42; boost::scoped_ptr s(dht_default_storage_constructor(node_id(0), sett)); TEST_CHECK(s.get() != NULL); for (int i = 0; i < 200; ++i) { s->announce_peer(rand_hash(), tcp::endpoint(rand_v4(), lt::random()) , "", false); dht_storage_counters cnt = s->counters(); TEST_CHECK(cnt.torrents <= 42); } dht_storage_counters cnt = s->counters(); TEST_EQUAL(cnt.torrents, 42); } TORRENT_TEST(immutable_item_limit) { dht_settings sett = test_settings(); sett.max_dht_items = 42; boost::scoped_ptr s(dht_default_storage_constructor(node_id(0), sett)); TEST_CHECK(s.get() != NULL); for (int i = 0; i < 200; ++i) { s->put_immutable_item(rand_hash(), "123", 3, rand_v4()); dht_storage_counters cnt = s->counters(); TEST_CHECK(cnt.immutable_data <= 42); } dht_storage_counters cnt = s->counters(); TEST_EQUAL(cnt.immutable_data, 42); } TORRENT_TEST(mutable_item_limit) { dht_settings sett = test_settings(); sett.max_dht_items = 42; boost::scoped_ptr s(dht_default_storage_constructor(node_id(0), sett)); TEST_CHECK(s.get() != NULL); char public_key[item_pk_len]; char signature[item_sig_len]; for (int i = 0; i < 200; ++i) { s->put_mutable_item(rand_hash(), "123", 3, signature, 1, public_key, "salt", 4, rand_v4()); dht_storage_counters cnt = s->counters(); TEST_CHECK(cnt.mutable_data <= 42); } dht_storage_counters cnt = s->counters(); TEST_EQUAL(cnt.mutable_data, 42); } TORRENT_TEST(get_peers_dist) { // test that get_peers returns reasonably disjoint sets of peers with each call // take two samples of 100 peers from 1000 and make sure there aren't too many // peers found in both lists dht_settings sett = test_settings(); sett.max_peers = 1000; sett.max_peers_reply = 100; boost::scoped_ptr s(dht_default_storage_constructor(node_id(0), sett)); address addr = rand_v4(); for (int i = 0; i < 1000; ++i) { s->announce_peer(n1, tcp::endpoint(addr, uint16_t(i)) , "torrent_name", false); } std::set peer_set; int duplicates = 0; for (int i = 0; i < 2; ++i) { entry peers; s->get_peers(n1, false, false, peers); TEST_EQUAL(peers["values"].list().size(), 100); entry::list_type const& peers_list = peers["values"].list(); for (entry::list_type::const_iterator p = peers_list.begin(); p != peers_list.end(); ++p) { std::string::const_iterator it = p->string().begin(); int port = detail::read_v4_endpoint(it).port(); if (!peer_set.insert(port).second) ++duplicates; } } std::printf("duplicate peers found: %d\n", duplicates); TEST_CHECK(duplicates < 20); } #endif libtorrent-rasterbar-1.1.13/test/test_direct_dht.cpp000066400000000000000000000106731351156116000226050ustar00rootroot00000000000000/* Copyright (c) 2015, Steven Siloti 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 "test.hpp" #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/config.hpp" #include "libtorrent/session.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/alert_types.hpp" using namespace libtorrent; namespace lt = libtorrent; namespace { struct test_plugin : plugin { virtual void register_dht_extensions(dht_extensions_t& ext) { ext.push_back(dht_extensions_t::value_type("test_good", &good_response)); } static bool good_response(udp::endpoint const& source , bdecode_node const& request, entry& response) { if (request.dict_find_string_value("q") == "test_good") { response["r"]["good"] = 1; return true; } return false; } }; dht_direct_response_alert* get_direct_response(lt::session& ses) { for (;;) { alert* a = ses.wait_for_alert(seconds(20)); // it shouldn't take more than 20 seconds to get a response // so fail the test and bail out if we don't get an alert in that time TEST_CHECK(a); if (!a) return NULL; std::vector alerts; ses.pop_alerts(&alerts); for (std::vector::iterator i = alerts.begin(); i != alerts.end(); ++i) { if ((*i)->type() == dht_direct_response_alert::alert_type) return static_cast(&**i); } } } } #endif // #ifndef TORRENT_DISABLE_EXTENSIONS TORRENT_TEST(direct_dht_request) { #ifndef TORRENT_DISABLE_EXTENSIONS settings_pack sp; sp.set_bool(settings_pack::enable_lsd, false); sp.set_bool(settings_pack::enable_natpmp, false); sp.set_bool(settings_pack::enable_upnp, false); sp.set_str(settings_pack::dht_bootstrap_nodes, ""); sp.set_int(settings_pack::max_retry_port_bind, 800); sp.set_str(settings_pack::listen_interfaces, "127.0.0.1:42434"); lt::session responder(sp, 0); sp.set_str(settings_pack::listen_interfaces, "127.0.0.1:45434"); lt::session requester(sp, 0); responder.add_extension(boost::static_pointer_cast(boost::make_shared())); // successful request entry r; r["q"] = "test_good"; requester.dht_direct_request(udp::endpoint(address::from_string("127.0.0.1"), responder.listen_port()) , r, (void*)12345); dht_direct_response_alert* ra = get_direct_response(requester); TEST_CHECK(ra); if (ra) { bdecode_node response = ra->response(); TEST_EQUAL(ra->addr.address(), address::from_string("127.0.0.1")); TEST_EQUAL(ra->addr.port(), responder.listen_port()); TEST_EQUAL(response.type(), bdecode_node::dict_t); TEST_EQUAL(response.dict_find_dict("r").dict_find_int_value("good"), 1); TEST_EQUAL(ra->userdata, (void*)12345); } // failed request requester.dht_direct_request(udp::endpoint(address::from_string("127.0.0.1"), 53545) , r, (void*)123456); ra = get_direct_response(requester); TEST_CHECK(ra); if (ra) { TEST_EQUAL(ra->addr.address(), address::from_string("127.0.0.1")); TEST_EQUAL(ra->addr.port(), 53545); TEST_EQUAL(ra->response().type(), bdecode_node::none_t); TEST_EQUAL(ra->userdata, (void*)123456); } #endif // #ifndef TORRENT_DISABLE_EXTENSIONS } libtorrent-rasterbar-1.1.13/test/test_dos_blocker.cpp000066400000000000000000000060321351156116000227540ustar00rootroot00000000000000/* Copyright (c) 2013, 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 "test.hpp" #include "setup_transfer.hpp" #include "libtorrent/address.hpp" #include "libtorrent/time.hpp" #include "libtorrent/kademlia/dos_blocker.hpp" #include "libtorrent/kademlia/dht_observer.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/socket_io.hpp" // for print_endpoint #include using namespace libtorrent; struct log_t : libtorrent::dht::dht_logger { #ifndef TORRENT_DISABLE_LOGGING virtual void log(dht_logger::module_t m, char const* fmt, ...) TORRENT_OVERRIDE TORRENT_FORMAT(3, 4) { va_list v; va_start(v, fmt); vfprintf(stderr, fmt, v); va_end(v); } virtual void log_packet(message_direction_t dir, char const* pkt, int len , udp::endpoint node) TORRENT_OVERRIDE { libtorrent::bdecode_node print; libtorrent::error_code ec; int ret = bdecode(pkt, pkt + len, print, ec, NULL, 100, 100); TEST_EQUAL(ret, 0); std::string msg = print_entry(print, true); printf("%s", msg.c_str()); char const* prefix[2] = { "<==", "==>"}; printf("%s [%s] %s", prefix[dir], print_endpoint(node).c_str() , msg.c_str()); } #endif }; TORRENT_TEST(dos_blocker) { #ifndef TORRENT_DISABLE_DHT using namespace libtorrent::dht; log_t l; dos_blocker b; address spammer = address_v4::from_string("10.10.10.10"); time_point now = clock_type::now(); for (int i = 0; i < 1000; ++i) { b.incoming(spammer, now, &l); now += milliseconds(1); TEST_EQUAL(b.incoming(rand_v4(), now, &l), true); now += milliseconds(1); } now += milliseconds(1); TEST_EQUAL(b.incoming(spammer, now, &l), false); #endif } libtorrent-rasterbar-1.1.13/test/test_ed25519.cpp000066400000000000000000000206141351156116000214660ustar00rootroot00000000000000/* Copyright (c) 2016, Alden Torres 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 "test.hpp" #ifndef TORRENT_DISABLE_DHT #include #include "libtorrent/ed25519.hpp" #include "libtorrent/hex.hpp" using namespace libtorrent; namespace { void test_vector(std::string seed, std::string pub, std::string sig_hex, std::string message) { typedef unsigned char uchar; uchar s[32]; uchar sk[64]; uchar pk[32]; uchar sig[64]; std::vector msg(int(message.size()) / 2); from_hex(seed.c_str(), seed.size(), reinterpret_cast(s)); ed25519_create_keypair(pk, sk, s); TEST_EQUAL(to_hex(std::string(reinterpret_cast(pk), sizeof(pk))), pub); from_hex(message.c_str(), message.size(), reinterpret_cast(&msg[0])); ed25519_sign(sig, &msg[0], msg.size(), pk, sk); TEST_EQUAL(to_hex(std::string(reinterpret_cast(sig), sizeof(sig))), sig_hex); bool r = ed25519_verify(sig, &msg[0], msg.size(), pk); TEST_CHECK(r); } } // https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=blob;f=tests/t-ed25519.inp;hb=HEAD TORRENT_TEST(ed25519_test_vec1) { // TST: 2 test_vector( "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb" , "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c" , "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da" "085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00" , "72" ); // TST: 3 test_vector( "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7" , "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025" , "6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac" "18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a" , "af82" ); // TST: 4 test_vector( "0d4a05b07352a5436e180356da0ae6efa0345ff7fb1572575772e8005ed978e9" , "e61a185bcef2613a6c7cb79763ce945d3b245d76114dd440bcf5f2dc1aa57057" , "d9868d52c2bebce5f3fa5a79891970f309cb6591e3e1702a70276fa97c24b3a8" "e58606c38c9758529da50ee31b8219cba45271c689afa60b0ea26c99db19b00c" , "cbc77b" ); // TST: 47 test_vector( "89f0d68299ba0a5a83f248ae0c169f8e3849a9b47bd4549884305c9912b46603" , "aba3e795aab2012acceadd7b3bd9daeeed6ff5258bdcd7c93699c2a3836e3832" , "2c691fa8d487ce20d5d2fa41559116e0bbf4397cf5240e152556183541d66cf7" "53582401a4388d390339dbef4d384743caa346f55f8daba68ba7b9131a8a6e0b" , "4f1846dd7ad50e545d4cfbffbb1dc2ff145dc123754d08af4e44ecc0bc8c9141" "1388bc7653e2d893d1eac2107d05" ); // TST: 48 test_vector( "0a3c1844e2db070fb24e3c95cb1cc6714ef84e2ccd2b9dd2f1460ebf7ecf13b1" , "72e409937e0610eb5c20b326dc6ea1bbbc0406701c5cd67d1fbde09192b07c01" , "87f7fdf46095201e877a588fe3e5aaf476bd63138d8a878b89d6ac60631b3458" "b9d41a3c61a588e1db8d29a5968981b018776c588780922f5aa732ba6379dd05" , "4c8274d0ed1f74e2c86c08d955bde55b2d54327e82062a1f71f70d536fdc8722" "cdead7d22aaead2bfaa1ad00b82957" ); // TST: 49 test_vector( "c8d7a8818b98dfdb20839c871cb5c48e9e9470ca3ad35ba2613a5d3199c8ab23" , "90d2efbba4d43e6b2b992ca16083dbcfa2b322383907b0ee75f3e95845d3c47f" , "fa2e994421aef1d5856674813d05cbd2cf84ef5eb424af6ecd0dc6fdbdc2fe60" "5fe985883312ecf34f59bfb2f1c9149e5b9cc9ecda05b2731130f3ed28ddae0b" , "783e33c3acbdbb36e819f544a7781d83fc283d3309f5d3d12c8dcd6b0b3d0e89" "e38cfd3b4d0885661ca547fb9764abff" ); // TST: 50 test_vector( "b482703612d0c586f76cfcb21cfd2103c957251504a8c0ac4c86c9c6f3e429ff" , "fd711dc7dd3b1dfb9df9704be3e6b26f587fe7dd7ba456a91ba43fe51aec09ad" , "58832bdeb26feafc31b46277cf3fb5d7a17dfb7ccd9b1f58ecbe6feb97966682" "8f239ba4d75219260ecac0acf40f0e5e2590f4caa16bbbcd8a155d347967a607" , "29d77acfd99c7a0070a88feb6247a2bce9984fe3e6fbf19d4045042a21ab26cb" "d771e184a9a75f316b648c6920db92b87b" ); // TST: 51 test_vector( "84e50dd9a0f197e3893c38dbd91fafc344c1776d3a400e2f0f0ee7aa829eb8a2" , "2c50f870ee48b36b0ac2f8a5f336fb090b113050dbcc25e078200a6e16153eea" , "69e6a4491a63837316e86a5f4ba7cd0d731ecc58f1d0a264c67c89befdd8d382" "9d8de13b33cc0bf513931715c7809657e2bfb960e5c764c971d733746093e500" , "f3992cde6493e671f1e129ddca8038b0abdb77bb9035f9f8be54bd5d68c1aeff" "724ff47d29344391dc536166b8671cbbf123" ); // TST: 52 test_vector( "b322d46577a2a991a4d1698287832a39c487ef776b4bff037a05c7f1812bdeec" , "eb2bcadfd3eec2986baff32b98e7c4dbf03ff95d8ad5ff9aa9506e5472ff845f" , "c7b55137317ca21e33489ff6a9bfab97c855dc6f85684a70a9125a261b56d5e6" "f149c5774d734f2d8debfc77b721896a8267c23768e9badb910eef83ec258802" , "19f1bf5dcf1750c611f1c4a2865200504d82298edd72671f62a7b1471ac3d4a3" "0f7de9e5da4108c52a4ce70a3e114a52a3b3c5" ); // TST: 53 test_vector( "960cab5034b9838d098d2dcbf4364bec16d388f6376d73a6273b70f82bbc98c0" , "5e3c19f2415acf729f829a4ebd5c40e1a6bc9fbca95703a9376087ed0937e51a" , "27d4c3a1811ef9d4360b3bdd133c2ccc30d02c2f248215776cb07ee4177f9b13" "fc42dd70a6c2fed8f225c7663c7f182e7ee8eccff20dc7b0e1d5834ec5b1ea01" , "f8b21962447b0a8f2e4279de411bea128e0be44b6915e6cda88341a68a0d8183" "57db938eac73e0af6d31206b3948f8c48a447308" ); // TST: 224 test_vector( "ae1d2c6b171be24c2e413d364dcda97fa476aaf9123d3366b0be03a142fe6e7d" , "d437f57542c681dd543487408ec7a44bd42a5fd545ce2f4c8297d67bb0b3aa7b" , "909008f3fcfff43988aee1314b15b1822caaa8dab120bd452af494e08335b44a" "94c313c4b145eadd5166eaac034e29b7e6ac7941d5961fc49d260e1c4820b00e" , "9e6c2fc76e30f17cd8b498845da44f22d55bec150c6130b411c6339d14b39969" "ab1033be687569a991a06f70b2a8a6931a777b0e4be6723cd75e5aa7532813ef" "50b3d37271640fa2fb287c0355257641ea935c851c0b6ac68be72c88dfc5856f" "b53543fb377b0dbf64808afcc4274aa456855ad28f61267a419bc72166b9ca73" "cd3bb79bf7dd259baa75911440974b68e8ba95a78cbbe1cb6ad807a33a1cce2f" "406ff7bcbd058b44a311b38ab4d4e61416c4a74d883d6a6a794abd9cf1c03902" "8bf1b20e3d4990aae86f32bf06cd8349a7a884cce0165e36a0640e987b9d51" ); // TST: 225 test_vector( "0265a7944baccfebf417b87ae1e6df2ff2a544ffb58225a08e092be03f026097" , "63d327615ea0139be0740b618aff1acfa818d4b0c2cfeaf0da93cdd5245fb5a9" , "b6c445b7eddca5935c61708d44ea5906bd19cc54224eae3c8e46ce99f5cbbd34" "1f26623938f5fe04070b1b02e71fbb7c78a90c0dda66cb143fab02e6a0bae306" , "874ed712a2c41c26a2d9527c55233fde0a4ffb86af8e8a1dd0a820502c5a2693" "2bf87ee0de72a8874ef2eebf83384d443f7a5f46a1233b4fb514a24699818248" "94f325bf86aa0fe1217153d40f3556c43a8ea9269444e149fb70e9415ae0766c" "565d93d1d6368f9a23a0ad76f9a09dbf79634aa97178677734d04ef1a5b3f87c" "e1ee9fc5a9ac4e7a72c9d7d31ec89e28a845d2e1103c15d6410ce3c723b0cc22" "09f698aa9fa288bbbecfd9e5f89cdcb09d3c215feb47a58b71ea70e2abead67f" "1b08ea6f561fb93ef05232eedabfc1c7702ab039bc465cf57e207f1093fc8208" ); } TORRENT_TEST(create_seed) { typedef unsigned char uchar; uchar s1[32]; ed25519_create_seed(s1); uchar s2[32]; ed25519_create_seed(s2); TEST_CHECK(memcpy(s1, s2, sizeof(s1)) != 0); // what are the odds int n1 = 0; int n2 = 0; for (int i = 0; i < 32; i++) { if (s1[i] != 0) n1++; if (s2[i] != 0) n2++; } TEST_CHECK(n1 > 0); TEST_CHECK(n2 > 0); } #else TORRENT_TEST(empty) { TEST_CHECK(true); } #endif // TORRENT_DISABLE_DHT libtorrent-rasterbar-1.1.13/test/test_enum_net.cpp000066400000000000000000000061161351156116000223030ustar00rootroot00000000000000/* Copyright (c) 2008-2015, 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 "test.hpp" #include "libtorrent/enum_net.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/address.hpp" #include "libtorrent/error_code.hpp" using namespace libtorrent; TORRENT_TEST(is_local) { error_code ec; TEST_CHECK(is_local(address::from_string("192.168.0.1", ec))); TEST_CHECK(!ec); TEST_CHECK(is_local(address::from_string("10.1.1.56", ec))); TEST_CHECK(!ec); TEST_CHECK(!is_local(address::from_string("14.14.251.63", ec))); TEST_CHECK(!ec); } TORRENT_TEST(is_loopback) { error_code ec; TEST_CHECK(is_loopback(address::from_string("127.0.0.1", ec))); TEST_CHECK(!ec); #if TORRENT_USE_IPV6 if (supports_ipv6()) { error_code ec; TEST_CHECK(is_loopback(address::from_string("::1", ec))); TEST_CHECK(!ec); } #endif } TORRENT_TEST(is_any) { TEST_CHECK(is_any(address_v4::any())); error_code ec; TEST_CHECK(!is_any(address::from_string("31.53.21.64", ec))); TEST_CHECK(!ec); #if TORRENT_USE_IPV6 if (supports_ipv6()) { TEST_CHECK(is_any(address_v6::any())); TEST_CHECK(!ec); } #endif } TORRENT_TEST(match_addr_mask) { error_code ec; TEST_CHECK(match_addr_mask( address::from_string("10.0.1.176", ec), address::from_string("10.0.1.176", ec), address::from_string("255.255.255.0", ec))); TEST_CHECK(!ec); TEST_CHECK(match_addr_mask( address::from_string("10.0.1.3", ec), address::from_string("10.0.3.3", ec), address::from_string("255.255.0.0", ec))); TEST_CHECK(!ec); TEST_CHECK(!match_addr_mask( address::from_string("10.0.1.3", ec), address::from_string("10.1.3.3", ec), address::from_string("255.255.0.0", ec))); TEST_CHECK(!ec); } libtorrent-rasterbar-1.1.13/test/test_fast_extension.cpp000066400000000000000000000621671351156116000235320ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "settings.hpp" #include "test_utils.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/io.hpp" #include "libtorrent/alloca.hpp" #include "libtorrent/time.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/bdecode.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" using namespace libtorrent; namespace lt = libtorrent; void log(char const* fmt, ...) { va_list v; va_start(v, fmt); char buf[1024]; vsnprintf(buf, sizeof(buf), fmt, v); va_end(v); fprintf(stderr, "\x1b[1m\x1b[36m%s: %s\x1b[0m\n" , time_now_string(), buf); } void print_session_log(lt::session& ses) { print_alerts(ses, "ses", true, true); } int read_message(tcp::socket& s, char* buffer, int max_size) { using namespace libtorrent::detail; error_code ec; boost::asio::read(s, boost::asio::buffer(buffer, 4) , boost::asio::transfer_all(), ec); if (ec) { TEST_ERROR(ec.message()); return -1; } char* ptr = buffer; int length = read_int32(ptr); if (length > max_size) { log("message size: %d", length); TEST_ERROR("message size exceeds max limt"); return -1; } boost::asio::read(s, boost::asio::buffer(buffer, length) , boost::asio::transfer_all(), ec); if (ec) { TEST_ERROR(ec.message()); return -1; } return length; } void print_message(char const* buffer, int len) { char const* message_name[] = {"choke", "unchoke", "interested", "not_interested" , "have", "bitfield", "request", "piece", "cancel", "dht_port", "", "", "" , "suggest_piece", "have_all", "have_none", "reject_request", "allowed_fast"}; char message[50]; char extra[300]; extra[0] = 0; if (len == 0) { strcpy(message, "keepalive"); } else { int msg = buffer[0]; if (msg >= 0 && msg < int(sizeof(message_name)/sizeof(message_name[0]))) strcpy(message, message_name[msg]); else if (msg == 20) snprintf(message, sizeof(message), "extension msg [%d]", buffer[1]); else snprintf(message, sizeof(message), "unknown[%d]", msg); if (msg == 0x6 && len == 13) { peer_request r; const char* ptr = buffer + 1; r.piece = detail::read_int32(ptr); r.start = detail::read_int32(ptr); r.length = detail::read_int32(ptr); snprintf(extra, sizeof(extra), "p: %d s: %d l: %d", r.piece, r.start, r.length); } else if (msg == 0x11 && len == 5) { const char* ptr = buffer + 1; int index = detail::read_int32(ptr); snprintf(extra, sizeof(extra), "p: %d", index); } else if (msg == 20 && len > 4 && buffer[1] == 0 ) { snprintf(extra, sizeof(extra), "%s" , bdecode(buffer + 2, buffer + len).to_string().c_str()); } } log("<== %s %s", message, extra); } void send_allow_fast(tcp::socket& s, int piece) { log("==> allow fast: %d", piece); using namespace libtorrent::detail; char msg[] = "\0\0\0\x05\x11\0\0\0\0"; char* ptr = msg + 5; write_int32(piece, ptr); error_code ec; boost::asio::write(s, boost::asio::buffer(msg, 9) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); } void send_suggest_piece(tcp::socket& s, int piece) { log("==> suggest piece: %d", piece); using namespace libtorrent::detail; char msg[] = "\0\0\0\x05\x0d\0\0\0\0"; char* ptr = msg + 5; write_int32(piece, ptr); error_code ec; boost::asio::write(s, boost::asio::buffer(msg, 9) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); } void send_keepalive(tcp::socket& s) { log("==> keepalive"); char msg[] = "\0\0\0\0"; error_code ec; boost::asio::write(s, boost::asio::buffer(msg, 4) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); } void send_unchoke(tcp::socket& s) { log("==> unchoke"); char msg[] = "\0\0\0\x01\x01"; error_code ec; boost::asio::write(s, boost::asio::buffer(msg, 5) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); } void send_have_all(tcp::socket& s) { log("==> have_all"); char msg[] = "\0\0\0\x01\x0e"; // have_all error_code ec; boost::asio::write(s, boost::asio::buffer(msg, 5) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); } void send_have_none(tcp::socket& s) { log("==> have_none"); char msg[] = "\0\0\0\x01\x0f"; // have_none error_code ec; boost::asio::write(s, boost::asio::buffer(msg, 5) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); } void send_bitfield(tcp::socket& s, char const* bits) { using namespace libtorrent::detail; int num_pieces = strlen(bits); int packet_size = (num_pieces+7)/8 + 5; char* msg = (char*)TORRENT_ALLOCA(char, packet_size); memset(msg, 0, packet_size); char* ptr = msg; write_int32(packet_size-4, ptr); write_int8(5, ptr); log("==> bitfield [%s]", bits); for (int i = 0; i < num_pieces; ++i) { ptr[i/8] |= (bits[i] == '1' ? 1 : 0) << i % 8; } error_code ec; boost::asio::write(s, boost::asio::buffer(msg, packet_size) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); } void do_handshake(tcp::socket& s, sha1_hash const& ih, char* buffer) { char handshake[] = "\x13" "BitTorrent protocol\0\0\0\0\0\x10\0\x04" " " // space for info-hash "aaaaaaaaaaaaaaaaaaaa"; // peer-id log("==> handshake"); error_code ec; std::memcpy(handshake + 28, ih.begin(), 20); boost::asio::write(s, boost::asio::buffer(handshake, sizeof(handshake) - 1) , boost::asio::transfer_all(), ec); if (ec) { TEST_ERROR(ec.message()); return; } // read handshake boost::asio::read(s, boost::asio::buffer(buffer, 68) , boost::asio::transfer_all(), ec); if (ec) { TEST_ERROR(ec.message()); return; } log("<== handshake"); TEST_CHECK(buffer[0] == 19); TEST_CHECK(std::memcmp(buffer + 1, "BitTorrent protocol", 19) == 0); char* extensions = buffer + 20; // check for fast extension support TEST_CHECK(extensions[7] & 0x4); // check for extension protocol support bool const lt_extension_protocol = (extensions[5] & 0x10) != 0; #ifndef TORRENT_DISABLE_EXTENSIONS TEST_CHECK(lt_extension_protocol == true); #else TEST_CHECK(lt_extension_protocol == false); #endif // check for DHT support bool const dht_support = (extensions[7] & 0x1) != 0; #ifndef TORRENT_DISABLE_DHT TEST_CHECK(dht_support == true); #else TEST_CHECK(dht_support == false); #endif TEST_CHECK(std::memcmp(buffer + 28, ih.begin(), 20) == 0); } void send_extension_handshake(tcp::socket& s, entry const& e) { std::vector buf; // reserve space for the message header // uint32: packet-length // uint8: 20 (extension message) // uint8: 0 (handshake) buf.resize(4 + 1 + 1); bencode(std::back_inserter(buf), e); using namespace libtorrent::detail; char* ptr = &buf[0]; write_uint32(buf.size() - 4, ptr); write_uint8(20, ptr); write_uint8(0, ptr); error_code ec; boost::asio::write(s, boost::asio::buffer(&buf[0], buf.size()) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); } void send_request(tcp::socket& s, peer_request req) { using namespace libtorrent::detail; log("==> request %d (%d,%d)", req.piece, req.start, req.length); char msg[] = "\0\0\0\x0d\x06 "; // have_none char* ptr = msg + 5; write_uint32(req.piece, ptr); write_uint32(req.start, ptr); write_uint32(req.length, ptr); error_code ec; boost::asio::write(s, boost::asio::buffer(msg, 17) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); } entry read_extension_handshake(tcp::socket& s, char* recv_buffer, int size) { for (;;) { int len = read_message(s, recv_buffer, size); if (len == -1) { TEST_ERROR("failed to read message"); return entry(); } print_message(recv_buffer, len); if (len < 4) continue; int msg = recv_buffer[0]; if (msg != 20) continue; int extmsg = recv_buffer[1]; if (extmsg != 0) continue; return bdecode(recv_buffer + 2, recv_buffer + len); } } void send_ut_metadata_msg(tcp::socket& s, int ut_metadata_msg, int type, int piece) { std::vector buf; // reserve space for the message header // uint32: packet-length // uint8: 20 (extension message) // uint8: (ut_metadata) buf.resize(4 + 1 + 1); entry e; e["msg_type"] = type; e["piece"] = piece; bencode(std::back_inserter(buf), e); using namespace libtorrent::detail; char* ptr = &buf[0]; write_uint32(buf.size() - 4, ptr); write_uint8(20, ptr); write_uint8(ut_metadata_msg, ptr); log("==> ut_metadata [ type: %d piece: %d ]", type, piece); error_code ec; boost::asio::write(s, boost::asio::buffer(&buf[0], buf.size()) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); } entry read_ut_metadata_msg(tcp::socket& s, char* recv_buffer, int size) { for (;;) { int len = read_message(s, recv_buffer, size); if (len == -1) { TEST_ERROR("failed to read message"); return entry(); } print_message(recv_buffer, len); if (len < 4) continue; int msg = recv_buffer[0]; if (msg != 20) continue; int extmsg = recv_buffer[1]; if (extmsg != 1) continue; return bdecode(recv_buffer + 2, recv_buffer + len); } } boost::shared_ptr setup_peer(tcp::socket& s, sha1_hash& ih , boost::shared_ptr& ses, bool incoming = true , int flags = 0, torrent_handle* th = NULL) { boost::shared_ptr t = ::create_torrent(); ih = t->info_hash(); settings_pack sett = settings(); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48900"); sett.set_bool(settings_pack::enable_upnp, false); sett.set_bool(settings_pack::enable_natpmp, false); sett.set_bool(settings_pack::enable_lsd, false); sett.set_bool(settings_pack::enable_dht, false); sett.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled); sett.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled); sett.set_bool(settings_pack::enable_outgoing_utp, false); sett.set_bool(settings_pack::enable_incoming_utp, false); #ifndef TORRENT_NO_DEPRECATE sett.set_bool(settings_pack::rate_limit_utp, true); #endif ses.reset(new lt::session(sett, lt::session::add_default_plugins)); error_code ec; add_torrent_params p; p.flags &= ~add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; p.flags |= flags; p.ti = t; p.save_path = "./tmp1_fast"; remove("./tmp1_fast/temporary", ec); if (ec) log("remove(): %s", ec.message().c_str()); ec.clear(); torrent_handle ret = ses->add_torrent(p, ec); if (th) *th = ret; // wait for the torrent to be ready wait_for_downloading(*ses, "ses"); if (incoming) { s.connect(tcp::endpoint(address::from_string("127.0.0.1", ec), ses->listen_port()), ec); if (ec) TEST_ERROR(ec.message()); } else { tcp::acceptor l(lt::get_io_service(s)); l.open(tcp::v4()); l.bind(tcp::endpoint(address_v4::from_string("127.0.0.1") , 3000 + rand() % 60000)); l.listen(); tcp::endpoint addr = l.local_endpoint(); ret.connect_peer(addr); print_session_log(*ses); l.accept(s); } print_session_log(*ses); return t; } // makes sure that pieces that are allowed and then // rejected aren't requested again TORRENT_TEST(reject_fast) { std::cerr << "\n === test reject ===\n" << std::endl; sha1_hash ih; boost::shared_ptr ses; io_service ios; tcp::socket s(ios); setup_peer(s, ih, ses); char recv_buffer[1000]; do_handshake(s, ih, recv_buffer); print_session_log(*ses); send_have_all(s); print_session_log(*ses); std::vector allowed_fast; allowed_fast.push_back(0); allowed_fast.push_back(1); allowed_fast.push_back(2); allowed_fast.push_back(3); std::for_each(allowed_fast.begin(), allowed_fast.end() , boost::bind(&send_allow_fast, boost::ref(s), _1)); print_session_log(*ses); while (!allowed_fast.empty()) { print_session_log(*ses); int len = read_message(s, recv_buffer, sizeof(recv_buffer)); if (len == -1) break; print_message(recv_buffer, len); int msg = recv_buffer[0]; if (msg != 0x6) continue; using namespace libtorrent::detail; char* ptr = recv_buffer + 1; int piece = read_int32(ptr); std::vector::iterator i = std::find(allowed_fast.begin() , allowed_fast.end(), piece); TEST_CHECK(i != allowed_fast.end()); if (i != allowed_fast.end()) allowed_fast.erase(i); // send reject request recv_buffer[0] = 0x10; error_code ec; log("==> reject"); boost::asio::write(s, boost::asio::buffer("\0\0\0\x0d", 4) , boost::asio::transfer_all(), ec); if (ec) { TEST_ERROR(ec.message()); break; } boost::asio::write(s, boost::asio::buffer(recv_buffer, 13) , boost::asio::transfer_all(), ec); if (ec) { TEST_ERROR(ec.message()); break; } } print_session_log(*ses); s.close(); test_sleep(500); print_session_log(*ses); } TORRENT_TEST(invalid_suggest) { std::cerr << "\n === test suggest ===\n" << std::endl; sha1_hash ih; boost::shared_ptr ses; io_service ios; tcp::socket s(ios); setup_peer(s, ih, ses); char recv_buffer[1000]; do_handshake(s, ih, recv_buffer); print_session_log(*ses); send_have_all(s); print_session_log(*ses); // this is an invalid suggest message. We would not expect to receive a // request for that piece index. send_suggest_piece(s, -234); send_unchoke(s); test_sleep(500); print_session_log(*ses); int len = read_message(s, recv_buffer, sizeof(recv_buffer)); int idx = -1; while (len > 0) { if (recv_buffer[0] == 6) { char* ptr = recv_buffer + 1; idx = detail::read_int32(ptr); break; } len = read_message(s, recv_buffer, sizeof(recv_buffer)); } TEST_CHECK(idx != -234); TEST_CHECK(idx != -1); s.close(); } TORRENT_TEST(reject_suggest) { std::cerr << "\n === test suggest ===\n" << std::endl; sha1_hash ih; boost::shared_ptr ses; io_service ios; tcp::socket s(ios); setup_peer(s, ih, ses); char recv_buffer[1000]; do_handshake(s, ih, recv_buffer); print_session_log(*ses); send_have_all(s); print_session_log(*ses); std::vector suggested; suggested.push_back(0); suggested.push_back(1); suggested.push_back(2); suggested.push_back(3); std::for_each(suggested.begin(), suggested.end() , boost::bind(&send_suggest_piece, boost::ref(s), _1)); print_session_log(*ses); send_unchoke(s); print_session_log(*ses); send_keepalive(s); print_session_log(*ses); int fail_counter = 100; while (!suggested.empty() && fail_counter > 0) { print_session_log(*ses); int len = read_message(s, recv_buffer, sizeof(recv_buffer)); if (len == -1) break; print_message(recv_buffer, len); int msg = recv_buffer[0]; fail_counter--; if (msg != 0x6) continue; using namespace libtorrent::detail; char* ptr = recv_buffer + 1; int const piece = read_int32(ptr); std::vector::iterator i = std::find(suggested.begin() , suggested.end(), piece); TEST_CHECK(i != suggested.end()); if (i != suggested.end()) suggested.erase(i); // send reject request recv_buffer[0] = 0x10; error_code ec; log("==> reject"); boost::asio::write(s, boost::asio::buffer("\0\0\0\x0d", 4) , boost::asio::transfer_all(), ec); if (ec) { TEST_ERROR(ec.message()); break; } boost::asio::write(s, boost::asio::buffer(recv_buffer, 13) , boost::asio::transfer_all(), ec); if (ec) { TEST_ERROR(ec.message()); break; } } print_session_log(*ses); TEST_CHECK(fail_counter > 0); s.close(); test_sleep(500); print_session_log(*ses); } TORRENT_TEST(suggest_order) { std::cerr << "\n === test suggest ===\n" << std::endl; sha1_hash ih; boost::shared_ptr ses; io_service ios; tcp::socket s(ios); setup_peer(s, ih, ses); char recv_buffer[1000]; do_handshake(s, ih, recv_buffer); print_session_log(*ses); send_have_all(s); print_session_log(*ses); std::vector suggested; suggested.push_back(0); suggested.push_back(1); suggested.push_back(2); suggested.push_back(3); std::for_each(suggested.begin(), suggested.end() , boost::bind(&send_suggest_piece, boost::ref(s), _1)); print_session_log(*ses); send_unchoke(s); print_session_log(*ses); int fail_counter = 100; while (!suggested.empty() && fail_counter > 0) { print_session_log(*ses); int len = read_message(s, recv_buffer, sizeof(recv_buffer)); if (len == -1) break; print_message(recv_buffer, len); int msg = recv_buffer[0]; fail_counter--; // we're just interested in requests if (msg != 0x6) continue; using namespace libtorrent::detail; char* ptr = recv_buffer + 1; int const piece = read_int32(ptr); // make sure we receive the requests inverse order of sending the suggest // messages. The last suggest should be the highest priority int const expected_piece = suggested.back(); suggested.pop_back(); TEST_EQUAL(piece, expected_piece); } print_session_log(*ses); TEST_CHECK(fail_counter > 0); s.close(); test_sleep(500); print_session_log(*ses); } TORRENT_TEST(multiple_bitfields) { std::cerr << "\n === test multiple bitfields ===\n" << std::endl; sha1_hash ih; boost::shared_ptr ses; io_service ios; tcp::socket s(ios); boost::shared_ptr ti = setup_peer(s, ih, ses); print_session_log(*ses); char recv_buffer[1000]; do_handshake(s, ih, recv_buffer); print_session_log(*ses); std::string bitfield; bitfield.resize(ti->num_pieces(), '0'); send_bitfield(s, bitfield.c_str()); print_session_log(*ses); bitfield[0] = '1'; send_bitfield(s, bitfield.c_str()); print_session_log(*ses); bitfield[1] = '1'; send_bitfield(s, bitfield.c_str()); print_session_log(*ses); bitfield[2] = '1'; send_bitfield(s, bitfield.c_str()); print_session_log(*ses); s.close(); test_sleep(500); print_session_log(*ses); } TORRENT_TEST(multiple_have_all) { std::cerr << "\n === test multiple have_all ===\n" << std::endl; sha1_hash ih; boost::shared_ptr ses; io_service ios; tcp::socket s(ios); boost::shared_ptr ti = setup_peer(s, ih, ses); char recv_buffer[1000]; do_handshake(s, ih, recv_buffer); print_session_log(*ses); send_have_all(s); print_session_log(*ses); send_have_all(s); print_session_log(*ses); send_have_none(s); print_session_log(*ses); send_have_all(s); print_session_log(*ses); s.close(); print_session_log(*ses); test_sleep(500); print_session_log(*ses); } #ifndef TORRENT_DISABLE_EXTENSIONS // makes sure that pieces that are lost are not requested TORRENT_TEST(dont_have) { using namespace libtorrent::detail; std::cerr << "\n === test dont_have ===\n" << std::endl; sha1_hash ih; torrent_handle th; boost::shared_ptr ses; io_service ios; tcp::socket s(ios); boost::shared_ptr ti = setup_peer(s, ih, ses, true, 0, &th); char recv_buffer[1000]; do_handshake(s, ih, recv_buffer); print_session_log(*ses); send_have_all(s); print_session_log(*ses); test_sleep(300); print_session_log(*ses); std::vector pi; th.get_peer_info(pi); TEST_EQUAL(pi.size(), 1); if (pi.size() != 1) return; // at this point, the peer should be considered a seed TEST_CHECK(pi[0].flags & peer_info::seed); int lt_dont_have = 0; error_code ec; while (lt_dont_have == 0) { print_session_log(*ses); int len = read_message(s, recv_buffer, sizeof(recv_buffer)); if (len == -1) break; print_message(recv_buffer, len); if (len == 0) continue; int msg = recv_buffer[0]; if (msg != 20) continue; int ext_msg = recv_buffer[1]; if (ext_msg != 0) continue; bdecode_node e; int pos = 0; int ret = bdecode(recv_buffer + 2, recv_buffer + len, e, ec, &pos); if (ret != 0) { log("failed to parse extension handshake: %s at pos %d" , ec.message().c_str(), pos); } TEST_EQUAL(ret, 0); log("extension handshake: %s", print_entry(e).c_str()); bdecode_node m = e.dict_find_dict("m"); TEST_CHECK(m); if (!m) return; bdecode_node dont_have = m.dict_find_int("lt_donthave"); TEST_CHECK(dont_have); if (!dont_have) return; lt_dont_have = dont_have.int_value(); } print_session_log(*ses); char* ptr = recv_buffer; write_uint32(6, ptr); write_uint8(20, ptr); write_uint8(lt_dont_have, ptr); write_uint32(3, ptr); boost::asio::write(s, boost::asio::buffer(recv_buffer, 10) , boost::asio::transfer_all(), ec); if (ec) TEST_ERROR(ec.message()); print_session_log(*ses); test_sleep(1000); print_session_log(*ses); th.get_peer_info(pi); TEST_EQUAL(pi.size(), 1); if (pi.size() != 1) return; TEST_EQUAL(pi[0].flags & peer_info::seed, 0); TEST_EQUAL(pi[0].pieces.count(), pi[0].pieces.size() - 1); TEST_EQUAL(pi[0].pieces[3], false); TEST_EQUAL(pi[0].pieces[2], true); TEST_EQUAL(pi[0].pieces[1], true); TEST_EQUAL(pi[0].pieces[0], true); print_session_log(*ses); } // TEST metadata extension messages and edge cases // this tests sending a request for a metadata piece that's too high. This is // pos TORRENT_TEST(invalid_metadata_request) { using namespace libtorrent::detail; std::cerr << "\n === test invalid metadata ===\n" << std::endl; sha1_hash ih; boost::shared_ptr ses; io_service ios; tcp::socket s(ios); boost::shared_ptr ti = setup_peer(s, ih, ses); char recv_buffer[1000]; do_handshake(s, ih, recv_buffer); print_session_log(*ses); send_have_all(s); print_session_log(*ses); entry extensions; extensions["m"]["ut_metadata"] = 1; send_extension_handshake(s, extensions); extensions = read_extension_handshake(s, recv_buffer, sizeof(recv_buffer)); int ut_metadata = extensions["m"]["ut_metadata"].integer(); log("ut_metadata: %d", ut_metadata); // 0 = request // 1 = piece // 2 = dont-have // first send an invalid request send_ut_metadata_msg(s, ut_metadata, 0, 1); // then send a valid one. If we get a response to the second one, // we assume we were not disconnected because of the invalid one send_ut_metadata_msg(s, ut_metadata, 0, 0); entry ut_metadata_msg = read_ut_metadata_msg(s, recv_buffer , sizeof(recv_buffer)); // the first response should be "dont-have" TEST_EQUAL(ut_metadata_msg["msg_type"].integer(), 2); TEST_EQUAL(ut_metadata_msg["piece"].integer(), 1); ut_metadata_msg = read_ut_metadata_msg(s, recv_buffer , sizeof(recv_buffer)); // the second response should be the payload TEST_EQUAL(ut_metadata_msg["msg_type"].integer(), 1); TEST_EQUAL(ut_metadata_msg["piece"].integer(), 0); print_session_log(*ses); } TORRENT_TEST(invalid_request) { std::cerr << "\n === test request ===\n" << std::endl; sha1_hash ih; boost::shared_ptr ses; io_service ios; tcp::socket s(ios); setup_peer(s, ih, ses); char recv_buffer[1000]; do_handshake(s, ih, recv_buffer); print_session_log(*ses); send_have_none(s); peer_request req; req.piece = 124134235; req.start = 0; req.length = 0x4000; send_request(s, req); } void have_all_test(bool const incoming) { sha1_hash ih; boost::shared_ptr ses; io_service ios; tcp::socket s(ios); setup_peer(s, ih, ses, incoming, add_torrent_params::flag_seed_mode); char recv_buffer[1000]; do_handshake(s, ih, recv_buffer); print_session_log(*ses); // expect to receive a have-all (not a bitfield) // since we advertised support for FAST extensions for (;;) { int const len = read_message(s, recv_buffer, sizeof(recv_buffer)); if (len == -1) { TEST_ERROR("failed to receive have-all despite advertising support for FAST"); break; } print_message(recv_buffer, len); int const msg = recv_buffer[0]; if (msg == 0xe) // have-all { // success! break; } if (msg == 5) // bitfield { TEST_ERROR("received bitfield from seed despite advertising support for FAST"); break; } } } TORRENT_TEST(outgoing_have_all) { std::cerr << "\n === test outgoing have-all ===\n" << std::endl; have_all_test(true); } TORRENT_TEST(incoming_have_all) { std::cerr << "\n === test outgoing have-all ===\n" << std::endl; have_all_test(false); } #endif // TORRENT_DISABLE_EXTENSIONS // TODO: test sending invalid requests (out of bound piece index, offsets and // sizes) libtorrent-rasterbar-1.1.13/test/test_fence.cpp000066400000000000000000000131701351156116000215470ustar00rootroot00000000000000#include "libtorrent/storage.hpp" #include "libtorrent/disk_io_job.hpp" #include "test.hpp" #include using namespace libtorrent; TORRENT_TEST(empty_fence) { libtorrent::disk_job_fence fence; counters cnt; disk_io_job test_job[10]; // issue 5 jobs. None of them should be blocked by a fence int ret = 0; // add a fence job ret = fence.raise_fence(&test_job[5], &test_job[6], cnt); // since we don't have any outstanding jobs // we need to post this job TEST_CHECK(ret == disk_job_fence::fence_post_fence); ret = fence.is_blocked(&test_job[7]); TEST_CHECK(ret == true); ret = fence.is_blocked(&test_job[8]); TEST_CHECK(ret == true); tailqueue jobs; // complete the fence job fence.job_complete(&test_job[5], jobs); // now it's fine to post the blocked jobs TEST_CHECK(jobs.size() == 2); TEST_CHECK(jobs.first() == &test_job[7]); // the disk_io_fence has an assert in its destructor // to make sure all outstanding jobs are completed, so we must // complete them before we're done fence.job_complete(&test_job[7], jobs); fence.job_complete(&test_job[8], jobs); } TORRENT_TEST(job_fence) { counters cnt; libtorrent::disk_job_fence fence; disk_io_job test_job[10]; // issue 5 jobs. None of them should be blocked by a fence bool ret = false; TEST_CHECK(fence.num_outstanding_jobs() == 0); ret = fence.is_blocked(&test_job[0]); TEST_CHECK(ret == false); TEST_CHECK(fence.num_outstanding_jobs() == 1); ret = fence.is_blocked(&test_job[1]); TEST_CHECK(ret == false); ret = fence.is_blocked(&test_job[2]); TEST_CHECK(ret == false); ret = fence.is_blocked(&test_job[3]); TEST_CHECK(ret == false); ret = fence.is_blocked(&test_job[4]); TEST_CHECK(ret == false); TEST_CHECK(fence.num_outstanding_jobs() == 5); TEST_CHECK(fence.num_blocked() == 0); // add a fence job ret = fence.raise_fence(&test_job[5], &test_job[6], cnt); // since we have outstanding jobs, no need // to post anything TEST_CHECK(ret == disk_job_fence::fence_post_flush); ret = fence.is_blocked(&test_job[7]); TEST_CHECK(ret == true); ret = fence.is_blocked(&test_job[8]); TEST_CHECK(ret == true); tailqueue jobs; fence.job_complete(&test_job[3], jobs); TEST_CHECK(jobs.size() == 0); fence.job_complete(&test_job[2], jobs); TEST_CHECK(jobs.size() == 0); fence.job_complete(&test_job[4], jobs); TEST_CHECK(jobs.size() == 0); fence.job_complete(&test_job[1], jobs); TEST_CHECK(jobs.size() == 0); fence.job_complete(&test_job[0], jobs); TEST_EQUAL(jobs.size(), 0); // the flush job completes fence.job_complete(&test_job[6], jobs); // this was the last job. Now we should be // able to run the fence job TEST_EQUAL(jobs.size(), 1); TEST_CHECK(jobs.first() == &test_job[5]); jobs.pop_front(); // complete the fence job fence.job_complete(&test_job[5], jobs); // now it's fine to post the blocked jobs TEST_EQUAL(jobs.size(), 2); TEST_CHECK(jobs.first() == &test_job[7]); // the disk_io_fence has an assert in its destructor // to make sure all outstanding jobs are completed, so we must // complete them before we're done fence.job_complete(&test_job[7], jobs); fence.job_complete(&test_job[8], jobs); } TORRENT_TEST(double_fence) { counters cnt; libtorrent::disk_job_fence fence; disk_io_job test_job[10]; // issue 5 jobs. None of them should be blocked by a fence int ret = 0; TEST_CHECK(fence.num_outstanding_jobs() == 0); ret = fence.is_blocked(&test_job[0]); TEST_CHECK(ret == false); TEST_CHECK(fence.num_outstanding_jobs() == 1); ret = fence.is_blocked(&test_job[1]); TEST_CHECK(ret == false); ret = fence.is_blocked(&test_job[2]); TEST_CHECK(ret == false); ret = fence.is_blocked(&test_job[3]); TEST_CHECK(ret == false); ret = fence.is_blocked(&test_job[4]); TEST_CHECK(ret == false); TEST_CHECK(fence.num_outstanding_jobs() == 5); TEST_CHECK(fence.num_blocked() == 0); // add two fence jobs ret = fence.raise_fence(&test_job[5], &test_job[6], cnt); // since we have outstanding jobs, no need // to post anything TEST_CHECK(ret == disk_job_fence::fence_post_flush); ret = fence.raise_fence(&test_job[7], &test_job[8], cnt); // since we have outstanding jobs, no need // to post anything TEST_CHECK(ret == disk_job_fence::fence_post_none); fprintf(stderr, "ret: %d\n", ret); ret = fence.is_blocked(&test_job[9]); TEST_CHECK(ret == true); tailqueue jobs; fence.job_complete(&test_job[3], jobs); TEST_CHECK(jobs.size() == 0); fence.job_complete(&test_job[2], jobs); TEST_CHECK(jobs.size() == 0); fence.job_complete(&test_job[4], jobs); TEST_CHECK(jobs.size() == 0); fence.job_complete(&test_job[1], jobs); TEST_CHECK(jobs.size() == 0); fence.job_complete(&test_job[0], jobs); TEST_CHECK(jobs.size() == 0); fence.job_complete(&test_job[6], jobs); // this was the last job. Now we should be // able to run the fence job TEST_CHECK(jobs.size() == 1); TEST_CHECK(jobs.first() == &test_job[5]); jobs.pop_front(); // complete the fence job fence.job_complete(&test_job[5], jobs); // now it's fine to run the next fence job // first we get the flush job TEST_CHECK(jobs.size() == 1); TEST_CHECK(jobs.first() == &test_job[8]); jobs.pop_front(); fence.job_complete(&test_job[8], jobs); // then the fence itself TEST_CHECK(jobs.size() == 1); TEST_CHECK(jobs.first() == &test_job[7]); jobs.pop_front(); fence.job_complete(&test_job[7], jobs); // and now we can run the remaining blocked job TEST_CHECK(jobs.size() == 1); TEST_CHECK(jobs.first() == &test_job[9]); // the disk_io_fence has an assert in its destructor // to make sure all outstanding jobs are completed, so we must // complete them before we're done fence.job_complete(&test_job[9], jobs); } libtorrent-rasterbar-1.1.13/test/test_file.cpp000066400000000000000000000334421351156116000214120ustar00rootroot00000000000000/* Copyright (c) 2012, 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/file.hpp" #include "test.hpp" #include "setup_transfer.hpp" // for test_sleep #include // for strcmp #include #include using namespace libtorrent; int touch_file(std::string const& filename, int size) { using namespace libtorrent; std::vector v; v.resize(size); for (int i = 0; i < size; ++i) v[i] = i & 255; file f; error_code ec; if (!f.open(filename, file::write_only, ec)) return -1; if (ec) return -1; file::iovec_t b = {&v[0], v.size()}; boost::int64_t written = f.writev(0, &b, 1, ec); if (written != int(v.size())) return -3; if (ec) return -3; return 0; } TORRENT_TEST(create_directory) { error_code ec; create_directory("__foobar__", ec); TEST_CHECK(!ec); file_status st; stat_file("__foobar__", &st, ec); TEST_CHECK(!ec); TEST_CHECK(st.mode & file_status::directory); remove("__foobar__", ec); TEST_CHECK(!ec); } TORRENT_TEST(file_status) { error_code ec; // test that the modification timestamps touch_file("__test_timestamp__", 10); file_status st1; stat_file("__test_timestamp__", &st1, ec); TEST_CHECK(!ec); // sleep for 3 seconds and then make sure the difference in timestamp is // between 2-4 seconds after touching it again test_sleep(3000); touch_file("__test_timestamp__", 10); file_status st2; stat_file("__test_timestamp__", &st2, ec); TEST_CHECK(!ec); int diff = int(st2.mtime - st1.mtime); fprintf(stdout, "timestamp difference: %d seconds. expected approx. 3 seconds\n" , diff); TEST_CHECK(diff >= 2 && diff <= 4); } TORRENT_TEST(directory) { error_code ec; create_directory("file_test_dir", ec); if (ec) fprintf(stdout, "create_directory: %s\n", ec.message().c_str()); TEST_CHECK(!ec); std::string cwd = current_working_directory(); touch_file(combine_path("file_test_dir", "abc"), 10); touch_file(combine_path("file_test_dir", "def"), 100); touch_file(combine_path("file_test_dir", "ghi"), 1000); std::set files; for (directory i("file_test_dir", ec); !i.done(); i.next(ec)) { std::string f = i.file(); TEST_CHECK(files.count(f) == 0); files.insert(f); fprintf(stdout, " %s\n", f.c_str()); } TEST_CHECK(files.count("abc") == 1); TEST_CHECK(files.count("def") == 1); TEST_CHECK(files.count("ghi") == 1); TEST_CHECK(files.count("..") == 1); TEST_CHECK(files.count(".") == 1); files.clear(); recursive_copy("file_test_dir", "file_test_dir2", ec); for (directory i("file_test_dir2", ec); !i.done(); i.next(ec)) { std::string f = i.file(); TEST_CHECK(files.count(f) == 0); files.insert(f); fprintf(stdout, " %s\n", f.c_str()); } remove_all("file_test_dir", ec); if (ec) fprintf(stdout, "remove_all: %s\n", ec.message().c_str()); remove_all("file_test_dir2", ec); if (ec) fprintf(stdout, "remove_all: %s\n", ec.message().c_str()); } // test path functions TORRENT_TEST(paths) { TEST_EQUAL(combine_path("test1/", "test2"), "test1/test2"); TEST_EQUAL(combine_path("test1", "."), "test1"); TEST_EQUAL(combine_path(".", "test1"), "test1"); #ifdef TORRENT_WINDOWS TEST_EQUAL(combine_path("test1\\", "test2"), "test1\\test2"); TEST_EQUAL(combine_path("test1", "test2"), "test1\\test2"); #else TEST_EQUAL(combine_path("test1", "test2"), "test1/test2"); #endif #if TORRENT_USE_UNC_PATHS TEST_EQUAL(canonicalize_path("c:\\a\\..\\b"), "c:\\b"); TEST_EQUAL(canonicalize_path("a\\..\\b"), "b"); TEST_EQUAL(canonicalize_path("a\\..\\.\\b"), "b"); TEST_EQUAL(canonicalize_path("\\.\\a"), "\\a"); TEST_EQUAL(canonicalize_path("\\\\bla\\.\\a"), "\\\\bla\\a"); TEST_EQUAL(canonicalize_path("c:\\bla\\a"), "c:\\bla\\a"); #endif TEST_EQUAL(extension("blah"), ""); TEST_EQUAL(extension("blah.exe"), ".exe"); TEST_EQUAL(extension("blah.foo.bar"), ".bar"); TEST_EQUAL(extension("blah.foo."), "."); TEST_EQUAL(extension("blah.foo/bar"), ""); TEST_EQUAL(remove_extension("blah"), "blah"); TEST_EQUAL(remove_extension("blah.exe"), "blah"); TEST_EQUAL(remove_extension("blah.foo.bar"), "blah.foo"); TEST_EQUAL(remove_extension("blah.foo."), "blah.foo"); TEST_EQUAL(filename("blah"), "blah"); TEST_EQUAL(filename("/blah/foo/bar"), "bar"); TEST_EQUAL(filename("/blah/foo/bar/"), "bar"); TEST_EQUAL(filename("blah/"), "blah"); #ifdef TORRENT_WINDOWS TEST_EQUAL(is_root_path("c:\\blah"), false); TEST_EQUAL(is_root_path("c:\\"), true); TEST_EQUAL(is_root_path("\\\\"), true); TEST_EQUAL(is_root_path("\\\\foobar"), true); TEST_EQUAL(is_root_path("\\\\foobar\\"), true); TEST_EQUAL(is_root_path("\\\\foobar/"), true); TEST_EQUAL(is_root_path("\\\\foo/bar"), false); TEST_EQUAL(is_root_path("\\\\foo\\bar\\"), false); #else TEST_EQUAL(is_root_path("/blah"), false); TEST_EQUAL(is_root_path("/"), true); #endif #ifdef TORRENT_WINDOWS TEST_CHECK(compare_path("c:\\blah\\", "c:\\blah")); TEST_CHECK(compare_path("c:\\blah", "c:\\blah")); TEST_CHECK(compare_path("c:\\blah/", "c:\\blah")); TEST_CHECK(compare_path("c:\\blah", "c:\\blah\\")); TEST_CHECK(compare_path("c:\\blah", "c:\\blah")); TEST_CHECK(compare_path("c:\\blah", "c:\\blah/")); TEST_CHECK(!compare_path("c:\\bla", "c:\\blah/")); TEST_CHECK(!compare_path("c:\\bla", "c:\\blah")); TEST_CHECK(!compare_path("c:\\blah", "c:\\bla")); TEST_CHECK(!compare_path("c:\\blah\\sdf", "c:\\blah")); #else TEST_CHECK(compare_path("/blah", "/blah")); TEST_CHECK(compare_path("/blah/", "/blah")); TEST_CHECK(compare_path("/blah", "/blah")); TEST_CHECK(compare_path("/blah", "/blah/")); TEST_CHECK(!compare_path("/bla", "/blah/")); TEST_CHECK(!compare_path("/bla", "/blah")); TEST_CHECK(!compare_path("/blah", "/bla")); TEST_CHECK(!compare_path("/blah/sdf", "/blah")); #endif // if has_parent_path() returns false // parent_path() should return the empty string TEST_EQUAL(parent_path("blah"), ""); TEST_EQUAL(has_parent_path("blah"), false); TEST_EQUAL(parent_path("/blah/foo/bar"), "/blah/foo/"); TEST_EQUAL(has_parent_path("/blah/foo/bar"), true); TEST_EQUAL(parent_path("/blah/foo/bar/"), "/blah/foo/"); TEST_EQUAL(has_parent_path("/blah/foo/bar/"), true); TEST_EQUAL(parent_path("/a"), "/"); TEST_EQUAL(has_parent_path("/a"), true); TEST_EQUAL(parent_path("/"), ""); TEST_EQUAL(has_parent_path("/"), false); TEST_EQUAL(parent_path(""), ""); TEST_EQUAL(has_parent_path(""), false); #ifdef TORRENT_WINDOWS TEST_EQUAL(parent_path("\\\\"), ""); TEST_EQUAL(has_parent_path("\\\\"), false); TEST_EQUAL(parent_path("c:\\"), ""); TEST_EQUAL(has_parent_path("c:\\"), false); TEST_EQUAL(parent_path("c:\\a"), "c:\\"); TEST_EQUAL(has_parent_path("c:\\a"), true); TEST_EQUAL(has_parent_path("\\\\a"), false); TEST_EQUAL(has_parent_path("\\\\foobar/"), false); TEST_EQUAL(has_parent_path("\\\\foobar\\"), false); TEST_EQUAL(has_parent_path("\\\\foo/bar\\"), true); #endif #ifdef TORRENT_WINDOWS TEST_EQUAL(is_complete("c:\\"), true); TEST_EQUAL(is_complete("c:\\foo\\bar"), true); TEST_EQUAL(is_complete("\\\\foo\\bar"), true); TEST_EQUAL(is_complete("foo/bar"), false); TEST_EQUAL(is_complete("\\\\"), true); #else TEST_EQUAL(is_complete("/foo/bar"), true); TEST_EQUAL(is_complete("foo/bar"), false); TEST_EQUAL(is_complete("/"), true); TEST_EQUAL(is_complete(""), false); #endif TEST_EQUAL(complete("."), current_working_directory()); } // test split_string TORRENT_TEST(split_string) { char const* tags[10]; char tags_str[] = " this is\ta test\t string\x01to be split and it cannot " "extend over the limit of elements \t"; int ret = split_string(tags, 10, tags_str); TEST_CHECK(ret == 10); TEST_CHECK(strcmp(tags[0], "this") == 0); TEST_CHECK(strcmp(tags[1], "is") == 0); TEST_CHECK(strcmp(tags[2], "a") == 0); TEST_CHECK(strcmp(tags[3], "test") == 0); TEST_CHECK(strcmp(tags[4], "string") == 0); TEST_CHECK(strcmp(tags[5], "to") == 0); TEST_CHECK(strcmp(tags[6], "be") == 0); TEST_CHECK(strcmp(tags[7], "split") == 0); TEST_CHECK(strcmp(tags[8], "and") == 0); TEST_CHECK(strcmp(tags[9], "it") == 0); // replace_extension std::string test = "foo.bar"; replace_extension(test, "txt"); TEST_EQUAL(test, "foo.txt"); test = "_"; replace_extension(test, "txt"); TEST_EQUAL(test, "_.txt"); test = "1.2.3/_"; replace_extension(test, "txt"); TEST_EQUAL(test, "1.2.3/_.txt"); } // file class TORRENT_TEST(file) { error_code ec; file f; TEST_CHECK(f.open("test_file", file::read_write, ec)); if (ec) fprintf(stdout, "open failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_EQUAL(ec, error_code()); if (ec) fprintf(stdout, "%s\n", ec.message().c_str()); file::iovec_t b = {(void*)"test", 4}; TEST_EQUAL(f.writev(0, &b, 1, ec), 4); if (ec) fprintf(stdout, "writev failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_CHECK(!ec); char test_buf[5] = {0}; b.iov_base = test_buf; b.iov_len = 4; TEST_EQUAL(f.readv(0, &b, 1, ec), 4); if (ec) fprintf(stdout, "readv failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_EQUAL(ec, error_code()); TEST_CHECK(strcmp(test_buf, "test") == 0); f.close(); TEST_CHECK(f.open("test_file", file::read_only, ec)); if (ec) fprintf(stdout, "open failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_EQUAL(ec, error_code()); file::iovec_t two_buffers[2]; std::memset(test_buf, 0, sizeof(test_buf)); char test_buf2[5] = {0}; two_buffers[0].iov_base = test_buf; two_buffers[0].iov_len = 4; two_buffers[1].iov_base = test_buf2; two_buffers[1].iov_len = 4; TEST_EQUAL(f.readv(0, two_buffers, 2, ec), 4); if (ec) fprintf(stdout, "readv failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_EQUAL(ec, error_code()); TEST_CHECK(strcmp(test_buf, "test") == 0); f.close(); } TORRENT_TEST(hard_link) { // try to create a hard link to see what happens // first create a regular file to then add another link to. // create a file, write some stuff to it, create a hard link to that file, // read that file and assert we get the same stuff we wrote to the first file error_code ec; file f; TEST_CHECK(f.open("original_file", file::read_write, ec)); if (ec) fprintf(stdout, "open failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_EQUAL(ec, error_code()); file::iovec_t b = {(void*)"abcdefghijklmnopqrstuvwxyz", 26}; TEST_EQUAL(f.writev(0, &b, 1, ec), 26); if (ec) fprintf(stdout, "writev failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_EQUAL(ec, error_code()); f.close(); hard_link("original_file", "second_link", ec); if (ec) fprintf(stdout, "hard_link failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_EQUAL(ec, error_code()); TEST_CHECK(f.open("second_link", file::read_write, ec)); if (ec) fprintf(stdout, "open failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_EQUAL(ec, error_code()); char test_buf[27] = {0}; b.iov_base = test_buf; b.iov_len = 27; TEST_EQUAL(f.readv(0, &b, 1, ec), 26); if (ec) fprintf(stdout, "readv failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_EQUAL(ec, error_code()); TEST_CHECK(strcmp(test_buf, "abcdefghijklmnopqrstuvwxyz") == 0); f.close(); remove("original_file", ec); if (ec) fprintf(stdout, "remove failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); remove("second_link", ec); if (ec) fprintf(stdout, "remove failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); } TORRENT_TEST(coalesce_buffer) { error_code ec; file f; TEST_CHECK(f.open("test_file", file::read_write, ec)); if (ec) fprintf(stdout, "open failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_EQUAL(ec, error_code()); if (ec) fprintf(stdout, "%s\n", ec.message().c_str()); file::iovec_t b[2] = {{(void*)"test", 4}, {(void*)"foobar", 6}}; TEST_EQUAL(f.writev(0, b, 2, ec, file::coalesce_buffers), 4 + 6); if (ec) fprintf(stdout, "writev failed: [%s] %s\n", ec.category().name(), ec.message().c_str()); TEST_CHECK(!ec); char test_buf1[5] = {0}; char test_buf2[7] = {0}; b[0].iov_base = test_buf1; b[0].iov_len = 4; b[1].iov_base = test_buf2; b[1].iov_len = 6; TEST_EQUAL(f.readv(0, b, 2, ec), 4 + 6); if (ec) { fprintf(stdout, "readv failed: [%s] %s\n" , ec.category().name(), ec.message().c_str()); } TEST_EQUAL(ec, error_code()); TEST_CHECK(strcmp(test_buf1, "test") == 0); TEST_CHECK(strcmp(test_buf2, "foobar") == 0); f.close(); } #if 0 && TORRENT_USE_UNC_PATHS TORRENT_TEST(unc_paths) { std::string const reserved_name = "con"; error_code ec; { file f; TEST_CHECK(f.open(reserved_name, file::read_write, ec) && !ec); } remove(reserved_name, ec); TEST_CHECK(!ec); } #endif libtorrent-rasterbar-1.1.13/test/test_file_progress.cpp000066400000000000000000000064701351156116000233370ustar00rootroot00000000000000/* Copyright (c) 2015, 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 "test_utils.hpp" #include "libtorrent/aux_/file_progress.hpp" #include "libtorrent/file_storage.hpp" #include "libtorrent/piece_picker.hpp" using namespace libtorrent; TORRENT_TEST(init) { // test the init function to make sure it assigns // the correct number of bytes across the files const int piece_size = 256; file_storage fs; fs.add_file("torrent/1", 0); fs.add_file("torrent/2", 10); fs.add_file("torrent/3", 20); fs.add_file("torrent/4", 30); fs.add_file("torrent/5", 40); fs.add_file("torrent/6", 100000); fs.add_file("torrent/7", 30); fs.set_piece_length(piece_size); fs.set_num_pieces((fs.total_size() + piece_size - 1) / piece_size); for (int idx = 0; idx < fs.num_pieces(); ++idx) { piece_picker picker; picker.init(4, fs.total_size() % 4, fs.num_pieces()); picker.we_have(idx); aux::file_progress fp; fp.init(picker, fs); std::vector vec; fp.export_progress(vec); boost::uint64_t sum = 0; for (int i = 0; i < int(vec.size()); ++i) sum += vec[i]; TEST_EQUAL(int(sum), fs.piece_size(idx)); } } TORRENT_TEST(init2) { // test the init function to make sure it assigns // the correct number of bytes across the files const int piece_size = 256; file_storage fs; fs.add_file("torrent/1", 100000); fs.add_file("torrent/2", 10); fs.set_piece_length(piece_size); fs.set_num_pieces((fs.total_size() + piece_size - 1) / piece_size); for (int idx = 0; idx < fs.num_pieces(); ++idx) { piece_picker picker; picker.init(4, fs.total_size() % 4, fs.num_pieces()); picker.we_have(idx); std::vector vec; aux::file_progress fp; fp.init(picker, fs); fp.export_progress(vec); boost::uint64_t sum = 0; for (int i = 0; i < int(vec.size()); ++i) sum += vec[i]; TEST_EQUAL(int(sum), fs.piece_size(idx)); } } // TODO: test the update function too libtorrent-rasterbar-1.1.13/test/test_file_storage.cpp000066400000000000000000000237531351156116000231420ustar00rootroot00000000000000/* Copyright (c) 2013, 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 "test.hpp" #include "setup_transfer.hpp" #include "libtorrent/file_storage.hpp" #include "libtorrent/file.hpp" using namespace libtorrent; void setup_test_storage(file_storage& st) { st.add_file(combine_path("test", "a"), 10000); st.add_file(combine_path("test", "b"), 20000); st.add_file(combine_path("test", combine_path("c", "a")), 30000); st.add_file(combine_path("test", combine_path("c", "b")), 40000); st.set_piece_length(0x4000); st.set_num_pieces((st.total_size() + st.piece_length() - 1) / 0x4000); TEST_EQUAL(st.file_name(0), "a"); TEST_EQUAL(st.file_name(1), "b"); TEST_EQUAL(st.file_name(2), "a"); TEST_EQUAL(st.file_name(3), "b"); TEST_EQUAL(st.name(), "test"); TEST_EQUAL(st.file_path(0), combine_path("test", "a")); TEST_EQUAL(st.file_path(1), combine_path("test", "b")); TEST_EQUAL(st.file_path(2), combine_path("test", combine_path("c", "a"))); TEST_EQUAL(st.file_path(3), combine_path("test", combine_path("c", "b"))); TEST_EQUAL(st.file_size(0), 10000); TEST_EQUAL(st.file_size(1), 20000); TEST_EQUAL(st.file_size(2), 30000); TEST_EQUAL(st.file_size(3), 40000); TEST_EQUAL(st.file_offset(0), 0); TEST_EQUAL(st.file_offset(1), 10000); TEST_EQUAL(st.file_offset(2), 30000); TEST_EQUAL(st.file_offset(3), 60000); TEST_EQUAL(st.total_size(), 100000); TEST_EQUAL(st.piece_length(), 0x4000); printf("%d\n", st.num_pieces()); TEST_EQUAL(st.num_pieces(), (100000 + 0x3fff) / 0x4000); } TORRENT_TEST(coalesce_path) { file_storage st; st.add_file(combine_path("test", "a"), 10000); TEST_EQUAL(st.paths().size(), 1); TEST_EQUAL(st.paths()[0], ""); st.add_file(combine_path("test", "b"), 20000); TEST_EQUAL(st.paths().size(), 1); TEST_EQUAL(st.paths()[0], ""); st.add_file(combine_path("test", combine_path("c", "a")), 30000); TEST_EQUAL(st.paths().size(), 2); TEST_EQUAL(st.paths()[0], ""); TEST_EQUAL(st.paths()[1], "c"); // make sure that two files with the same path shares the path entry st.add_file(combine_path("test", combine_path("c", "b")), 40000); TEST_EQUAL(st.paths().size(), 2); TEST_EQUAL(st.paths()[0], ""); TEST_EQUAL(st.paths()[1], "c"); // cause pad files to be created, to make sure the pad files also share the // same path entries st.optimize(0, 1024, true); TEST_EQUAL(st.paths().size(), 3); TEST_EQUAL(st.paths()[0], ""); TEST_EQUAL(st.paths()[1], "c"); TEST_EQUAL(st.paths()[2], ".pad"); } TORRENT_TEST(rename_file) { // test rename_file file_storage st; setup_test_storage(st); st.rename_file(0, combine_path("test", combine_path("c", "d"))); TEST_EQUAL(st.file_path(0, "."), combine_path(".", combine_path("test" , combine_path("c", "d")))); TEST_EQUAL(st.file_path(0, ""), combine_path("test" , combine_path("c", "d"))); // files with absolute paths should ignore the save_path argument // passed in to file_path() #ifdef TORRENT_WINDOWS st.rename_file(0, "c:\\tmp\\a"); TEST_EQUAL(st.file_path(0, "."), "c:\\tmp\\a"); #else st.rename_file(0, "/tmp/a"); TEST_EQUAL(st.file_path(0, "."), "/tmp/a"); #endif st.rename_file(0, combine_path("test__", "a")); TEST_EQUAL(st.file_path(0, "."), combine_path(".", combine_path("test__" , "a"))); } TORRENT_TEST(set_name) { // test set_name. Make sure the name of the torrent is not encoded // in the paths of each individual file. When changing the name of the // torrent, the path of the files should change too file_storage st; setup_test_storage(st); st.set_name("test_2"); TEST_EQUAL(st.file_path(0, "."), combine_path(".", combine_path("test_2" , "a"))); } TORRENT_TEST(rename_file2) { // test rename_file file_storage st; st.add_file("a", 10000); TEST_EQUAL(st.file_path(0, ""), "a"); st.rename_file(0, combine_path("test", combine_path("c", "d"))); TEST_EQUAL(st.file_path(0, "."), combine_path(".", combine_path("test", combine_path("c", "d")))); TEST_EQUAL(st.file_path(0, ""), combine_path("test", combine_path("c", "d"))); #ifdef TORRENT_WINDOWS st.rename_file(0, "c:\\tmp\\a"); TEST_EQUAL(st.file_path(0, "."), "c:\\tmp\\a"); TEST_EQUAL(st.file_path(0, "c:\\test-1\\test2"), "c:\\tmp\\a"); #else st.rename_file(0, "/tmp/a"); TEST_EQUAL(st.file_path(0, "."), "/tmp/a"); TEST_EQUAL(st.file_path(0, "/usr/local/temp"), "/tmp/a"); #endif st.rename_file(0, combine_path("tmp", "a")); TEST_EQUAL(st.file_path(0, "."), combine_path("tmp", "a")); } TORRENT_TEST(pointer_offset) { // test applying pointer offset file_storage st; char const filename[] = "test1fooba"; st.add_file_borrow(filename, 5, combine_path("test-torrent-1", "test1") , 10); // test filename_ptr and filename_len TEST_EQUAL(st.file_name_ptr(0), filename); TEST_EQUAL(st.file_name_len(0), 5); TEST_EQUAL(st.file_path(0, ""), combine_path("test-torrent-1", "test1")); TEST_EQUAL(st.file_path(0, "tmp"), combine_path("tmp" , combine_path("test-torrent-1", "test1"))); // apply a pointer offset of 5 bytes. The name of the file should // change to "fooba". st.apply_pointer_offset(5); TEST_EQUAL(st.file_path(0, ""), combine_path("test-torrent-1", "fooba")); TEST_EQUAL(st.file_path(0, "tmp"), combine_path("tmp" , combine_path("test-torrent-1", "fooba"))); // test filename_ptr and filename_len TEST_EQUAL(st.file_name_ptr(0), filename + 5); TEST_EQUAL(st.file_name_len(0), 5); } TORRENT_TEST(map_file) { // test map_file file_storage fs; fs.set_piece_length(512); fs.add_file(combine_path("temp_storage", "test1.tmp"), 17); fs.add_file(combine_path("temp_storage", "test2.tmp"), 612); fs.add_file(combine_path("temp_storage", "test3.tmp"), 0); fs.add_file(combine_path("temp_storage", "test4.tmp"), 0); fs.add_file(combine_path("temp_storage", "test5.tmp"), 3253); // size: 3882 fs.add_file(combine_path("temp_storage", "test6.tmp"), 841); // size: 4723 peer_request rq = fs.map_file(0, 0, 10); TEST_EQUAL(rq.piece, 0); TEST_EQUAL(rq.start, 0); TEST_EQUAL(rq.length, 10); rq = fs.map_file(5, 0, 10); TEST_EQUAL(rq.piece, 7); TEST_EQUAL(rq.start, 298); TEST_EQUAL(rq.length, 10); rq = fs.map_file(5, 0, 1000); TEST_EQUAL(rq.piece, 7); TEST_EQUAL(rq.start, 298); TEST_EQUAL(rq.length, 841); } TORRENT_TEST(file_path_hash) { // test file_path_hash and path_hash. Make sure we can detect a path // whose name collides with file_storage fs; fs.set_piece_length(512); fs.add_file(combine_path("temp_storage", "Foo"), 17); fs.add_file(combine_path("temp_storage", "foo"), 612); fprintf(stderr, "path: %s\n", fs.file_path(0).c_str()); fprintf(stderr, "file: %s\n", fs.file_path(1).c_str()); boost::uint32_t file_hash0 = fs.file_path_hash(0, "a"); boost::uint32_t file_hash1 = fs.file_path_hash(1, "a"); TEST_EQUAL(file_hash0, file_hash1); } // make sure we pad the end of the torrent when tail_padding is specified TORRENT_TEST(optimize_tail_padding) { file_storage fs; fs.set_piece_length(512); fs.add_file(combine_path("s", "1"), 700); fs.optimize(512, 512, true); // since the size of file 3 is a multiple of the alignment (512), it should // be prioritized, to minimize the amount of padding. // after that, we want to pick the largest file (2), and since file 1 is // smaller than the pad-file limit (512) we won't pad it. Since tail_padding // is false, we won't pad the tail of the torrent either TEST_EQUAL(fs.num_files(), 2); TEST_EQUAL(fs.file_size(0), 700); TEST_EQUAL(fs.file_name(0), "1"); TEST_EQUAL(fs.pad_file_at(0), false); TEST_EQUAL(fs.file_size(1), 1024 - 700); TEST_EQUAL(fs.pad_file_at(1), true); } // make sure we fill in padding with small files TORRENT_TEST(optimize_pad_fillers) { file_storage fs; fs.set_piece_length(512); fs.add_file(combine_path("s", "1"), 1); fs.add_file(combine_path("s", "2"), 1000); fs.add_file(combine_path("s", "3"), 1001); fs.optimize(512, 512, false); // first we pick the largest file, then we need to add padding, since file 1 // is smaller than the pad file limit, it won't be aligned anyway, so we // place that as part of the padding TEST_EQUAL(fs.num_files(), 4); TEST_EQUAL(fs.file_size(0), 1001); TEST_EQUAL(fs.file_name(0), "3"); TEST_EQUAL(fs.pad_file_at(0), false); TEST_EQUAL(fs.file_size(1), 1); TEST_EQUAL(fs.file_name(1), "1"); TEST_EQUAL(fs.pad_file_at(1), false); TEST_EQUAL(fs.file_size(2), 1024 - (1001 + 1)); TEST_EQUAL(fs.pad_file_at(2), true); TEST_EQUAL(fs.file_size(3), 1000); TEST_EQUAL(fs.file_name(3), "2"); TEST_EQUAL(fs.pad_file_at(3), false); } // TODO: add more optimize() tests // TODO: test map_block // TODO: test piece_size(int piece) // TODO: test file_index_at_offset // TODO: test file attributes // TODO: test symlinks // TODO: test pad_files // TODO: test reorder_file (make sure internal_file_entry::swap() is used) libtorrent-rasterbar-1.1.13/test/test_gzip.cpp000066400000000000000000000051651351156116000214450ustar00rootroot00000000000000/* Copyright (c) 2014, 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/file.hpp" #include "test.hpp" #include "libtorrent/gzip.hpp" #include "setup_transfer.hpp" // for load_file #include "libtorrent/file.hpp" // for combine_path using namespace libtorrent; TORRENT_TEST(zeroes) { std::vector zipped; error_code ec; load_file(combine_path("..", "zeroes.gz"), zipped, ec, 1000000); if (ec) fprintf(stderr, "failed to open file: (%d) %s\n", ec.value() , ec.message().c_str()); TEST_CHECK(!ec); std::vector inflated; inflate_gzip(&zipped[0], zipped.size(), inflated, 1000000, ec); if (ec) { fprintf(stderr, "failed to unzip: %s\n", ec.message().c_str()); } TEST_CHECK(!ec); TEST_CHECK(inflated.size() > 0); for (int i = 0; i < int(inflated.size()); ++i) TEST_EQUAL(inflated[i], 0); } TORRENT_TEST(corrupt) { std::vector zipped; error_code ec; load_file(combine_path("..", "corrupt.gz"), zipped, ec, 1000000); if (ec) fprintf(stderr, "failed to open file: (%d) %s\n", ec.value() , ec.message().c_str()); TEST_CHECK(!ec); std::vector inflated; inflate_gzip(&zipped[0], zipped.size(), inflated, 1000000, ec); // we expect this to fail TEST_CHECK(ec); } libtorrent-rasterbar-1.1.13/test/test_hasher.cpp000066400000000000000000000046751351156116000217530ustar00rootroot00000000000000/* Copyright (c) 2008, 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/hasher.hpp" #include "libtorrent/hex.hpp" // from_hex #include "test.hpp" using namespace libtorrent; // test vectors from RFC 3174 // http://www.faqs.org/rfcs/rfc3174.html char const* test_array[4] = { "abc", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "a", "0123456701234567012345670123456701234567012345670123456701234567" }; long int repeat_count[4] = { 1, 1, 1000000, 10 }; char const* result_array[4] = { "A9993E364706816ABA3E25717850C26C9CD0D89D", "84983E441C3BD26EBAAE4AA1F95129E5E54670F1", "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F", "DEA356A2CDDD90C7A7ECEDC5EBB563934F460452" }; TORRENT_TEST(hasher) { using namespace libtorrent; for (int test = 0; test < 4; ++test) { hasher h; for (int i = 0; i < repeat_count[test]; ++i) h.update(test_array[test], std::strlen(test_array[test])); sha1_hash result; from_hex(result_array[test], 40, (char*)&result[0]); TEST_CHECK(result == h.final()); } } libtorrent-rasterbar-1.1.13/test/test_heterogeneous_queue.cpp000066400000000000000000000147101351156116000245500ustar00rootroot00000000000000/* Copyright (c) 2015, 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 "test.hpp" #include "libtorrent/heterogeneous_queue.hpp" struct A { int a; explicit A(int a_) : a(a_) {} virtual int type() = 0; virtual ~A() {} }; struct B : A { int b; explicit B(int a_, int b_) : A(a_), b(b_) {} virtual int type() { return 1; } }; struct C : A { char c[100]; explicit C(int a_, int c_) : A(a_) { memset(c, c_, sizeof(c)); } virtual int type() { return 2; } }; struct D { static int instances; D() { ++instances; } D(D const& d) { ++instances; } ~D() { --instances; } }; struct E { E(char const* msg) : string_member(msg) {} std::string string_member; }; int D::instances = 0; struct F { F(int f_) : self(this) , f(f_) , constructed(true) , destructed(false) , gutted(false) {} F(F const& f_) : self(this), f(f_.f) , constructed(f_.constructed) , destructed(f_.destructed) , gutted(false) { TEST_EQUAL(f_.constructed, true); TEST_EQUAL(f_.destructed, false); TEST_EQUAL(f_.gutted, false); } #if __cplusplus >= 201103L F(F&& f_) : self(this) , f(f_.f) , constructed(f_.constructed) , destructed(f_.destructed) , gutted(f_.gutted) { TEST_EQUAL(f_.constructed, true); TEST_EQUAL(f_.destructed, false); TEST_EQUAL(f_.gutted, false); f_.gutted = true; } #endif ~F() { TEST_EQUAL(constructed, true); TEST_EQUAL(destructed, false); TEST_EQUAL(self, this); destructed = true; constructed = false; } void check_invariant() { TEST_EQUAL(constructed, true); TEST_EQUAL(destructed, false); TEST_EQUAL(gutted, false); TEST_EQUAL(self, this); } F* self; int f; bool constructed; bool destructed; bool gutted; private: // non-copyable F& operator=(F const& f); }; // test push_back of heterogeneous types // and retrieval of their pointers TORRENT_TEST(push_back) { using namespace libtorrent; heterogeneous_queue q; q.push_back(B(0, 1)); TEST_EQUAL(q.size(), 1); q.push_back(B(2, 3)); TEST_EQUAL(q.size(), 2); q.push_back(B(4, 5)); TEST_EQUAL(q.size(), 3); q.push_back(C(6, 7)); TEST_EQUAL(q.size(), 4); q.push_back(C(8, 9)); TEST_EQUAL(q.size(), 5); q.push_back(C(10, 11)); TEST_EQUAL(q.size(), 6); std::vector ptrs; q.get_pointers(ptrs); TEST_EQUAL(int(ptrs.size()), q.size()); TEST_EQUAL(ptrs[0]->type(), 1); TEST_EQUAL(ptrs[1]->type(), 1); TEST_EQUAL(ptrs[2]->type(), 1); TEST_EQUAL(ptrs[3]->type(), 2); TEST_EQUAL(ptrs[4]->type(), 2); TEST_EQUAL(ptrs[5]->type(), 2); TEST_EQUAL(static_cast(ptrs[0])->a, 0); TEST_EQUAL(static_cast(ptrs[0])->b, 1); TEST_EQUAL(static_cast(ptrs[1])->a, 2); TEST_EQUAL(static_cast(ptrs[1])->b, 3); TEST_EQUAL(static_cast(ptrs[2])->a, 4); TEST_EQUAL(static_cast(ptrs[2])->b, 5); TEST_EQUAL(static_cast(ptrs[3])->a, 6); TEST_EQUAL(static_cast(ptrs[3])->c[0], 7); TEST_EQUAL(static_cast(ptrs[4])->a, 8); TEST_EQUAL(static_cast(ptrs[4])->c[0], 9); TEST_EQUAL(static_cast(ptrs[5])->a, 10); TEST_EQUAL(static_cast(ptrs[5])->c[0], 11); } // test swap TORRENT_TEST(swap) { using namespace libtorrent; heterogeneous_queue q1; heterogeneous_queue q2; q1.push_back(B(0, 1)); q1.push_back(B(2, 3)); q1.push_back(B(4, 5)); TEST_EQUAL(q1.size(), 3); q2.push_back(C(6, 7)); q2.push_back(C(8, 9)); TEST_EQUAL(q2.size(), 2); std::vector ptrs; q1.get_pointers(ptrs); TEST_EQUAL(int(ptrs.size()), q1.size()); TEST_EQUAL(ptrs[0]->type(), 1); TEST_EQUAL(ptrs[1]->type(), 1); TEST_EQUAL(ptrs[2]->type(), 1); q2.get_pointers(ptrs); TEST_EQUAL(int(ptrs.size()), q2.size()); TEST_EQUAL(ptrs[0]->type(), 2); TEST_EQUAL(ptrs[1]->type(), 2); q1.swap(q2); q1.get_pointers(ptrs); TEST_EQUAL(q1.size(), 2); TEST_EQUAL(int(ptrs.size()), q1.size()); TEST_EQUAL(ptrs[0]->type(), 2); TEST_EQUAL(ptrs[1]->type(), 2); q2.get_pointers(ptrs); TEST_EQUAL(q2.size(), 3); TEST_EQUAL(int(ptrs.size()), q2.size()); TEST_EQUAL(ptrs[0]->type(), 1); TEST_EQUAL(ptrs[1]->type(), 1); TEST_EQUAL(ptrs[2]->type(), 1); } // test destruction TORRENT_TEST(destruction) { using namespace libtorrent; heterogeneous_queue q; TEST_EQUAL(D::instances, 0); q.push_back(D()); TEST_EQUAL(D::instances, 1); q.push_back(D()); TEST_EQUAL(D::instances, 2); q.push_back(D()); TEST_EQUAL(D::instances, 3); q.push_back(D()); TEST_EQUAL(D::instances, 4); q.clear(); TEST_EQUAL(D::instances, 0); } // test copy/move TORRENT_TEST(copy_move) { using namespace libtorrent; heterogeneous_queue q; // make sure the queue has to grow at some point, to exercise its // copy/move of elements for (int i = 0; i < 1000; ++i) q.push_back(F(i)); std::vector ptrs; q.get_pointers(ptrs); TEST_EQUAL(int(ptrs.size()), 1000); for (int i = 0; i < int(ptrs.size()); ++i) { ptrs[i]->check_invariant(); TEST_EQUAL(ptrs[i]->f, i); } // destroy all objects, asserting that their invariant still holds q.clear(); } TORRENT_TEST(nontrivial) { using namespace libtorrent; heterogeneous_queue q; for (int i = 0; i < 10000; ++i) { q.push_back(E("testing to allocate non-trivial objects")); } } libtorrent-rasterbar-1.1.13/test/test_http_connection.cpp000066400000000000000000000173361351156116000236750ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "test_utils.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/socket_io.hpp" // print_endpoint #include "libtorrent/http_connection.hpp" #include "libtorrent/resolver.hpp" #include #include #include using namespace libtorrent; io_service ios; resolver res(ios); int connect_handler_called = 0; int handler_called = 0; int data_size = 0; int http_status = 0; error_code g_error_code; char data_buffer[4000]; void print_http_header(http_parser const& p) { std::cout << time_now_string() << " < " << p.status_code() << " " << p.message() << std::endl; for (std::multimap::const_iterator i = p.headers().begin(), end(p.headers().end()); i != end; ++i) { std::cout << time_now_string() << " < " << i->first << ": " << i->second << std::endl; } } void http_connect_handler(http_connection& c) { ++connect_handler_called; TEST_CHECK(c.socket().is_open()); error_code ec; std::cout << time_now_string() << " connected to: " << print_endpoint(c.socket().remote_endpoint(ec)) << std::endl; // this is not necessarily true when using a proxy and proxying hostnames // TEST_CHECK(c.socket().remote_endpoint(ec).address() == address::from_string("127.0.0.1", ec)); } void http_handler(error_code const& ec, http_parser const& parser , char const* data, int size, http_connection& c) { ++handler_called; data_size = size; g_error_code = ec; TORRENT_ASSERT(size == 0 || parser.finished()); if (parser.header_finished()) { http_status = parser.status_code(); if (http_status == 200) { TEST_CHECK(memcmp(data, data_buffer, size) == 0); } } print_http_header(parser); } void reset_globals() { connect_handler_called = 0; handler_called = 0; data_size = 0; http_status = 0; g_error_code = error_code(); } void run_test(std::string const& url, int size, int status, int connected , boost::optional ec, aux::proxy_settings const& ps , std::string const& auth = std::string()) { reset_globals(); std::cout << " ===== TESTING: " << url << " =====" << std::endl; std::cout << time_now_string() << " expecting: size: " << size << " status: " << status << " connected: " << connected << " error: " << (ec?ec->message():"no error") << std::endl; boost::shared_ptr h(new http_connection(ios , res, &::http_handler, true, 1024*1024, &::http_connect_handler)); h->get(url, seconds(5), 0, &ps, 5, "test/user-agent", boost::none, 0, auth); ios.reset(); error_code e; ios.run(e); if (e) std::cout << time_now_string() << " run failed: " << e.message() << std::endl; std::cout << time_now_string() << " connect_handler_called: " << connect_handler_called << std::endl; std::cout << time_now_string() << " handler_called: " << handler_called << std::endl; std::cout << time_now_string() << " status: " << http_status << std::endl; std::cout << time_now_string() << " size: " << data_size << std::endl; std::cout << time_now_string() << " expected-size: " << size << std::endl; std::cout << time_now_string() << " error_code: " << g_error_code.message() << std::endl; TEST_CHECK(connect_handler_called == connected); TEST_CHECK(handler_called == 1); TEST_CHECK(data_size == size || size == -1); TEST_CHECK(!ec || g_error_code == *ec); TEST_CHECK(http_status == status || status == -1); } void write_test_file() { std::srand(std::time(0)); std::generate(data_buffer, data_buffer + sizeof(data_buffer), &std::rand); error_code ec; file test_file("test_file", file::write_only, ec); TEST_CHECK(!ec); if (ec) fprintf(stdout, "file error: %s\n", ec.message().c_str()); file::iovec_t b = { data_buffer, 3216}; test_file.writev(0, &b, 1, ec); TEST_CHECK(!ec); if (ec) fprintf(stdout, "file error: %s\n", ec.message().c_str()); test_file.close(); } enum suite_flags_t { flag_chunked_encoding = 1, flag_keepalive = 2 }; void run_suite(std::string const& protocol , settings_pack::proxy_type_t proxy_type , int flags = flag_keepalive) { write_test_file(); // starting the web server will also generate test_file.gz (from test_file) // so it has to happen after we write test_file int port = start_web_server(protocol == "https" , flags & flag_chunked_encoding , flags & flag_keepalive); aux::proxy_settings ps; ps.hostname = "127.0.0.1"; ps.username = "testuser"; ps.password = "testpass"; ps.type = proxy_type; if (ps.type != settings_pack::none) ps.port = start_proxy(ps.type); typedef boost::optional err; char url[256]; snprintf(url, sizeof(url), "%s://127.0.0.1:%d/", protocol.c_str(), port); std::string url_base(url); run_test(url_base + "relative/redirect", 3216, 200, 2, error_code(), ps); run_test(url_base + "redirect", 3216, 200, 2, error_code(), ps); // the actual error code for an abort caused by too many // redirects is a bit unpredictable. under SSL for instance, // it will be an ungraceful shutdown run_test(url_base + "infinite_redirect", 0, 301, 6, err(), ps); run_test(url_base + "test_file", 3216, 200, 1, error_code(), ps); run_test(url_base + "test_file.gz", 3216, 200, 1, error_code(), ps); run_test(url_base + "non-existing-file", -1, 404, 1, err(), ps); run_test(url_base + "password_protected", 3216, 200, 1, error_code(), ps , "testuser:testpass"); // try a very long path std::string path; for (int i = 0; i < 6000; ++i) { path += static_cast(i % 26) + 'a'; } run_test(url_base + path, 0, 404, 1, err(), ps); // only run the tests to handle NX_DOMAIN if we have a proper internet // connection that doesn't inject false DNS responses (like Comcast does) hostent* h = gethostbyname("non-existent-domain.se"); printf("gethostbyname(\"non-existent-domain.se\") = %p. h_errno = %d\n", h, h_errno); if (h == 0 && h_errno == HOST_NOT_FOUND) { run_test(protocol + "://non-existent-domain.se/non-existing-file", -1, -1, 0, err(), ps); } if (ps.type != settings_pack::none) stop_proxy(ps.port); stop_web_server(); } #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(no_proxy_ssl) { run_suite("https", settings_pack::none); } TORRENT_TEST(http_ssl) { run_suite("https", settings_pack::http); } TORRENT_TEST(http_pw_ssl) { run_suite("https", settings_pack::http_pw); } #endif // USE_OPENSSL TORRENT_TEST(no_keepalive) { run_suite("http", settings_pack::none, 0); } libtorrent-rasterbar-1.1.13/test/test_http_parser.cpp000066400000000000000000000447671351156116000230420ustar00rootroot00000000000000/* Copyright (c) 2012, 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 "test.hpp" #include "libtorrent/http_parser.hpp" #include "libtorrent/parse_url.hpp" #include #include using namespace libtorrent; using boost::tuple; using boost::make_tuple; using boost::tie; tuple feed_bytes(http_parser& parser, char const* str) { tuple ret(0, 0, false); tuple prev(0, 0, false); for (int chunks = 1; chunks < 70; ++chunks) { ret = make_tuple(0, 0, false); parser.reset(); buffer::const_interval recv_buf(str, str); for (; *str;) { int chunk_size = (std::min)(chunks, int(strlen(recv_buf.end))); if (chunk_size == 0) break; recv_buf.end += chunk_size; int payload, protocol; bool error = false; tie(payload, protocol) = parser.incoming(recv_buf, error); ret.get<0>() += payload; ret.get<1>() += protocol; ret.get<2>() |= error; // std::cerr << payload << ", " << protocol << ", " << chunk_size << std::endl; TORRENT_ASSERT(payload + protocol == chunk_size || ret.get<2>()); } TEST_CHECK(prev == make_tuple(0, 0, false) || ret == prev || ret.get<2>()); if (!ret.get<2>()) { TEST_EQUAL(ret.get<0>() + ret.get<1>(), int(strlen(str))); } prev = ret; } return ret; } TORRENT_TEST(http_parser) { // HTTP request parser http_parser parser; boost::tuple received; received = feed_bytes(parser , "HTTP/1.1 200 OK\r\n" "Content-Length: 4\r\n" "Content-Type: text/plain\r\n" "\r\n" "test"); TEST_CHECK(received == make_tuple(4, 64, false)); TEST_CHECK(parser.finished()); TEST_CHECK(std::equal(parser.get_body().begin, parser.get_body().end, "test")); TEST_CHECK(parser.header("content-type") == "text/plain"); TEST_CHECK(atoi(parser.header("content-length").c_str()) == 4); parser.reset(); TEST_CHECK(!parser.finished()); char const* upnp_response = "HTTP/1.1 200 OK\r\n" "ST:upnp:rootdevice\r\n" "USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice\r\n" "Location: http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc\r\n" "Server: Custom/1.0 UPnP/1.0 Proc/Ver\r\n" "EXT:\r\n" "Cache-Control:max-age=180\r\n" "DATE: Fri, 02 Jan 1970 08:10:38 GMT\r\n\r\n"; received = feed_bytes(parser, upnp_response); TEST_CHECK(received == make_tuple(0, int(strlen(upnp_response)), false)); TEST_CHECK(parser.get_body().left() == 0); TEST_CHECK(parser.header("st") == "upnp:rootdevice"); TEST_CHECK(parser.header("location") == "http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc"); TEST_CHECK(parser.header("ext") == ""); TEST_CHECK(parser.header("date") == "Fri, 02 Jan 1970 08:10:38 GMT"); TEST_EQUAL(parser.connection_close(), false); // test connection close parser.reset(); TEST_CHECK(!parser.finished()); char const* http1_response = "HTTP/1.0 200 OK\r\n" "Cache-Control: max-age=180\r\n" "DATE: Fri, 02 Jan 1970 08:10:38 GMT\r\n\r\n"; received = feed_bytes(parser, http1_response); TEST_CHECK(received == make_tuple(0, int(strlen(http1_response)), false)); TEST_CHECK(parser.get_body().left() == 0); TEST_CHECK(parser.header("date") == "Fri, 02 Jan 1970 08:10:38 GMT"); TEST_EQUAL(parser.connection_close(), true); parser.reset(); TEST_CHECK(!parser.finished()); char const* close_response = "HTTP/1.1 200 OK\r\n" "Connection: close\r\n" "DATE: Fri, 02 Jan 1970 08:10:38 GMT\r\n\r\n"; received = feed_bytes(parser, close_response); TEST_CHECK(received == make_tuple(0, int(strlen(close_response)), false)); TEST_CHECK(parser.get_body().left() == 0); TEST_CHECK(parser.header("date") == "Fri, 02 Jan 1970 08:10:38 GMT"); TEST_EQUAL(parser.connection_close(), true); parser.reset(); TEST_CHECK(!parser.finished()); char const* keep_alive_response = "HTTP/1.1 200 OK\r\n" "Connection: keep-alive\r\n" "DATE: Fri, 02 Jan 1970 08:10:38 GMT\r\n\r\n"; received = feed_bytes(parser, keep_alive_response); TEST_CHECK(received == make_tuple(0, int(strlen(keep_alive_response)), false)); TEST_CHECK(parser.get_body().left() == 0); TEST_CHECK(parser.header("date") == "Fri, 02 Jan 1970 08:10:38 GMT"); TEST_EQUAL(parser.connection_close(), false); parser.reset(); TEST_CHECK(!parser.finished()); char const* upnp_notify = "NOTIFY * HTTP/1.1\r\n" "Host:239.255.255.250:1900\r\n" "NT:urn:schemas-upnp-org:device:MediaServer:1\r\n" "NTS:ssdp:alive\r\n" "Location:http://10.0.1.15:2353/upnphost/udhisapi.dll?content=uuid:c17f2c31-d19b-4912-af94-651945c8a84e\r\n" "USN:uuid:c17f0c32-d1db-4be8-ae94-25f94583026e::urn:schemas-upnp-org:device:MediaServer:1\r\n" "Cache-Control:max-age=900\r\n" "Server:Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0\r\n"; received = feed_bytes(parser, upnp_notify); TEST_CHECK(received == make_tuple(0, int(strlen(upnp_notify)), false)); TEST_CHECK(parser.method() == "notify"); TEST_CHECK(parser.path() == "*"); parser.reset(); TEST_CHECK(!parser.finished()); char const* bt_lsd = "BT-SEARCH * HTTP/1.1\r\n" "Host: 239.192.152.143:6771\r\n" "Port: 6881\r\n" "Infohash: 12345678901234567890\r\n" "\r\n"; received = feed_bytes(parser, bt_lsd); TEST_CHECK(received == make_tuple(0, int(strlen(bt_lsd)), false)); TEST_CHECK(parser.method() == "bt-search"); TEST_CHECK(parser.path() == "*"); TEST_CHECK(atoi(parser.header("port").c_str()) == 6881); TEST_CHECK(parser.header("infohash") == "12345678901234567890"); TEST_CHECK(parser.finished()); parser.reset(); TEST_CHECK(!parser.finished()); // test chunked encoding char const* chunked_test = "HTTP/1.1 200 OK\r\n" "Content-Length: 20\r\n" "Content-Type: text/plain\r\n" "Transfer-Encoding: chunked\r\n" "\r\n" "4\r\n" "test\r\n" "10\r\n" "0123456789abcdef\r\n" "0\r\n" "Test-header: foobar\r\n" "\r\n"; received = feed_bytes(parser, chunked_test); printf("payload: %d protocol: %d\n", received.get<0>(), received.get<1>()); TEST_CHECK(received == make_tuple(20, int(strlen(chunked_test)) - 20, false)); TEST_CHECK(parser.finished()); TEST_CHECK(std::equal(parser.get_body().begin, parser.get_body().end , "4\r\ntest\r\n10\r\n0123456789abcdef")); TEST_CHECK(parser.header("test-header") == "foobar"); TEST_CHECK(parser.header("content-type") == "text/plain"); TEST_CHECK(atoi(parser.header("content-length").c_str()) == 20); TEST_CHECK(parser.chunked_encoding()); typedef std::pair chunk_range; std::vector cmp; cmp.push_back(chunk_range(96, 100)); cmp.push_back(chunk_range(106, 122)); TEST_CHECK(cmp == parser.chunks()); // make sure we support trackers with incorrect line endings char const* tracker_response = "HTTP/1.1 200 OK\n" "content-length: 5\n" "content-type: test/plain\n" "\n" "\ntest"; received = feed_bytes(parser, tracker_response); TEST_CHECK(received == make_tuple(5, int(strlen(tracker_response) - 5), false)); TEST_CHECK(parser.get_body().left() == 5); parser.reset(); // make sure we support content-range responses // and that we're case insensitive char const* web_seed_response = "HTTP/1.1 206 OK\n" "contEnt-rAngE: bYTes 0-4\n" "conTent-TyPe: test/plain\n" "\n" "\ntest"; received = feed_bytes(parser, web_seed_response); TEST_CHECK(received == make_tuple(5, int(strlen(web_seed_response) - 5), false)); TEST_CHECK(parser.content_range() == (std::pair(0, 4))); TEST_CHECK(parser.content_length() == 5); parser.reset(); // test invalid content range char const* invalid_range_response = "HTTP/1.1 206 OK\n" "content-range: bytes 4-0\n" "content-type: test/plain\n" "\n" "\ntest"; received = feed_bytes(parser, invalid_range_response); TEST_CHECK(received.get<2>() == true); parser.reset(); // test invalid status line char const* invalid_status_response = "HTTP/1.1 206\n" "content-range: bytes 4-0\n" "content-type: test/plain\n" "\n" "\ntest"; received = feed_bytes(parser, invalid_status_response); TEST_CHECK(received.get<2>() == true); parser.reset(); // test invalid status line 2 char const* invalid_status_response2 = "HTTP/1.1\n" "content-range: bytes 4-0\n" "content-type: test/plain\n" "\n" "\ntest"; received = feed_bytes(parser, invalid_status_response2); TEST_CHECK(received.get<2>() == true); parser.reset(); // make sure we support content-range responses // and that we're case insensitive char const* one_hundred_response = "HTTP/1.1 100 Continue\n" "\r\n" "HTTP/1.1 200 OK\n" "Content-Length: 4\r\n" "Content-Type: test/plain\r\n" "\r\n" "test"; received = feed_bytes(parser, one_hundred_response); TEST_CHECK(received == make_tuple(4, int(strlen(one_hundred_response) - 4), false)); TEST_EQUAL(parser.content_length(), 4); { // test chunked encoding parser char const chunk_header1[] = "f;this is a comment\r\n"; boost::int64_t chunk_size; int header_size; bool ret = parser.parse_chunk_header(buffer::const_interval(chunk_header1, chunk_header1 + 10) , &chunk_size, &header_size); TEST_EQUAL(ret, false); ret = parser.parse_chunk_header(buffer::const_interval(chunk_header1, chunk_header1 + sizeof(chunk_header1)) , &chunk_size, &header_size); TEST_EQUAL(ret, true); TEST_EQUAL(chunk_size, 15); TEST_EQUAL(header_size, sizeof(chunk_header1) - 1); char const chunk_header2[] = "0;this is a comment\r\n" "test1: foo\r\n" "test2: bar\r\n" "\r\n"; ret = parser.parse_chunk_header(buffer::const_interval(chunk_header2, chunk_header2 + sizeof(chunk_header2)) , &chunk_size, &header_size); TEST_EQUAL(ret, true); TEST_EQUAL(chunk_size, 0); TEST_EQUAL(header_size, sizeof(chunk_header2) - 1); TEST_EQUAL(parser.headers().find("test1")->second, "foo"); TEST_EQUAL(parser.headers().find("test2")->second, "bar"); } // test url parsing error_code ec; TEST_CHECK(parse_url_components("http://foo:bar@host.com:80/path/to/file", ec) == make_tuple("http", "foo:bar", "host.com", 80, "/path/to/file")); TEST_CHECK(parse_url_components("http://host.com/path/to/file", ec) == make_tuple("http", "", "host.com", -1, "/path/to/file")); TEST_CHECK(parse_url_components("ftp://host.com:21/path/to/file", ec) == make_tuple("ftp", "", "host.com", 21, "/path/to/file")); TEST_CHECK(parse_url_components("http://host.com/path?foo:bar@foo:", ec) == make_tuple("http", "", "host.com", -1, "/path?foo:bar@foo:")); TEST_CHECK(parse_url_components("http://192.168.0.1/path/to/file", ec) == make_tuple("http", "", "192.168.0.1", -1, "/path/to/file")); TEST_CHECK(parse_url_components("http://[2001:ff00::1]:42/path/to/file", ec) == make_tuple("http", "", "2001:ff00::1", 42, "/path/to/file")); // leading spaces are supposed to be stripped TEST_CHECK(parse_url_components(" \thttp://[2001:ff00::1]:42/path/to/file", ec) == make_tuple("http", "", "2001:ff00::1", 42, "/path/to/file")); parse_url_components("http://[2001:ff00::1:42/path/to/file", ec); TEST_CHECK(ec == error_code(errors::expected_close_bracket_in_address)); parse_url_components("http:/", ec); TEST_CHECK(ec == error_code(errors::unsupported_url_protocol)); ec.clear(); parse_url_components("http:", ec); TEST_CHECK(ec == error_code(errors::unsupported_url_protocol)); ec.clear(); // test resolve_redirect_location TEST_EQUAL(resolve_redirect_location("http://example.com/a/b", "a") , "http://example.com/a/a"); TEST_EQUAL(resolve_redirect_location("http://example.com/a/b", "c/d/e/") , "http://example.com/a/c/d/e/"); TEST_EQUAL(resolve_redirect_location("http://example.com/a/b", "../a") , "http://example.com/a/../a"); TEST_EQUAL(resolve_redirect_location("http://example.com/a/b", "/c") , "http://example.com/c"); TEST_EQUAL(resolve_redirect_location("http://example.com/a/b", "http://test.com/d") , "http://test.com/d"); TEST_EQUAL(resolve_redirect_location("my-custom-scheme://example.com/a/b", "http://test.com/d") , "http://test.com/d"); TEST_EQUAL(resolve_redirect_location("http://example.com", "/d") , "http://example.com/d"); TEST_EQUAL(resolve_redirect_location("http://example.com", "d") , "http://example.com/d"); TEST_EQUAL(resolve_redirect_location("my-custom-scheme://example.com/a/b", "/d") , "my-custom-scheme://example.com/d"); TEST_EQUAL(resolve_redirect_location("my-custom-scheme://example.com/a/b", "c/d") , "my-custom-scheme://example.com/a/c/d"); // if the referrer is invalid, just respond the verbatim location TEST_EQUAL(resolve_redirect_location("example.com/a/b", "/c/d") , "/c/d"); // is_ok_status TEST_EQUAL(is_ok_status(200), true); TEST_EQUAL(is_ok_status(206), true); TEST_EQUAL(is_ok_status(299), false); TEST_EQUAL(is_ok_status(300), true); TEST_EQUAL(is_ok_status(399), true); TEST_EQUAL(is_ok_status(400), false); TEST_EQUAL(is_ok_status(299), false); // is_redirect TEST_EQUAL(is_redirect(299), false); TEST_EQUAL(is_redirect(100), false); TEST_EQUAL(is_redirect(300), true); TEST_EQUAL(is_redirect(399), true); TEST_EQUAL(is_redirect(400), false); } TORRENT_TEST(chunked_encoding) { char const* chunked_input = "HTTP/1.1 200 OK\r\n" "Transfer-Encoding: chunked\r\n" "Content-Type: text/plain\r\n" "\r\n" "4\r\ntest\r\n4\r\n1234\r\n10\r\n0123456789abcdef\r\n" "0\r\n\r\n"; http_parser parser; boost::tuple const received = feed_bytes(parser, chunked_input); TEST_EQUAL(strlen(chunked_input), 24 + 94) TEST_CHECK(received == make_tuple(24, 94, false)); TEST_CHECK(parser.finished()); char mutable_buffer[100]; memcpy(mutable_buffer, parser.get_body().begin, parser.get_body().left()); int len = parser.collapse_chunk_headers(mutable_buffer, parser.get_body().left()); TEST_CHECK(std::equal(mutable_buffer, mutable_buffer + len, "test12340123456789abcdef")); } TORRENT_TEST(invalid_content_length) { char const* chunked_input = "HTTP/1.1 200 OK\r\n" "Transfer-Encoding: chunked\r\n" "Content-Length: -45345\r\n" "\r\n"; http_parser parser; boost::tuple const received = feed_bytes(parser, chunked_input); TEST_CHECK(boost::get<2>(received) == true); } TORRENT_TEST(invalid_chunked) { char const* chunked_input = "HTTP/1.1 200 OK\r\n" "Transfer-Encoding: chunked\r\n" "\r\n" "-53465234545\r\n" "foobar"; http_parser parser; boost::tuple const received = feed_bytes(parser, chunked_input); TEST_CHECK(boost::get<2>(received) == true); } TORRENT_TEST(invalid_content_range_start) { char const* chunked_input = "HTTP/1.1 206 OK\n" "Content-Range: bYTes -3-4\n" "\n"; http_parser parser; boost::tuple const received = feed_bytes(parser, chunked_input); TEST_CHECK(boost::get<2>(received) == true); } TORRENT_TEST(invalid_content_range_end) { char const* chunked_input = "HTTP/1.1 206 OK\n" "Content-Range: bYTes 3--434\n" "\n"; http_parser parser; boost::tuple const received = feed_bytes(parser, chunked_input); TEST_CHECK(boost::get<2>(received) == true); } TORRENT_TEST(overflow_content_length) { char const* chunked_input = "HTTP/1.1 200 OK\r\n" "Content-Length: 9999999999999999999999999999\r\n" "\r\n"; http_parser parser; boost::tuple const received = feed_bytes(parser, chunked_input); TEST_CHECK(boost::get<2>(received) == true); } TORRENT_TEST(overflow_content_range_end) { char const* chunked_input = "HTTP/1.1 206 OK\n" "Content-Range: bytes 0-999999999999999999999999\n" "\n"; http_parser parser; boost::tuple const received = feed_bytes(parser, chunked_input); TEST_CHECK(boost::get<2>(received) == true); } TORRENT_TEST(overflow_content_range_begin) { char const* chunked_input = "HTTP/1.1 206 OK\n" "Content-Range: bytes 999999999999999999999999-0\n" "\n"; http_parser parser; boost::tuple const received = feed_bytes(parser, chunked_input); TEST_CHECK(boost::get<2>(received) == true); } TORRENT_TEST(invalid_chunk_afl) { boost::uint8_t const invalid_chunked_input[] = { 0x48, 0x6f, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, // HoTP/1.1 200 OK 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d, // Cont-Length: 20 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x2d, 0x4c, 0x65, // Contente: tn 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x32, 0x30, // Transfer-Encoding: chunked 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // 0x74, 0x65, 0x3a, 0x20, 0x74, 0x6e, 0x0d, 0x0a, // 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, // 0x2d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, // -89abc9abcdef 0x67, 0x3a, 0x20, 0x63, 0x68, 0x75, 0x6e, 0x6b, // ďż˝ 0x65, 0x64, 0x0d, 0x0a, 0x0d, 0x0d, 0x0a, 0x0d, // T����������def 0x0a, 0x0a, 0x2d, 0x38, 0x39, 0x61, 0x62, 0x63, // ďż˝ 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x0d, // T�����������est-headyr: foobar 0x0a, 0xd6, 0x0d, 0x0a, 0x54, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0x64, 0x65, 0x66, 0x0d, 0x0a, 0xd6, 0x0d, 0x0a, 0x54, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0x65, 0x73, 0x74, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x79, 0x72, 0x3a, 0x20, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x0d, 0x0a, 0x0d, 0x0a, 0x00 }; http_parser parser; boost::tuple const received = feed_bytes(parser, reinterpret_cast(invalid_chunked_input)); TEST_CHECK(boost::get<2>(received) == false); } libtorrent-rasterbar-1.1.13/test/test_identify_client.cpp000066400000000000000000000043141351156116000236400ustar00rootroot00000000000000/* Copyright (c) 2015, 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 "test.hpp" #include "libtorrent/identify_client.hpp" using namespace libtorrent; TORRENT_TEST(identify_client) { #ifndef TORRENT_NO_DEPRECATE TEST_EQUAL(identify_client(peer_id("-AZ123B-............")), "Azureus 1.2.3.11"); TEST_EQUAL(identify_client(peer_id("-AZ1230-............")), "Azureus 1.2.3"); TEST_EQUAL(identify_client(peer_id("S123--..............")), "Shadow 1.2.3"); TEST_EQUAL(identify_client(peer_id("S\x1\x2\x3....\0...........")), "Shadow 1.2.3"); TEST_EQUAL(identify_client(peer_id("M1-2-3--............")), "Mainline 1.2.3"); TEST_EQUAL(identify_client(peer_id("\0\0\0\0\0\0\0\0\0\0\0\0........")), "Generic"); TEST_EQUAL(identify_client(peer_id("-xx1230-............")), "xx 1.2.3"); #endif } libtorrent-rasterbar-1.1.13/test/test_ip_filter.cpp000066400000000000000000000205531351156116000224470ustar00rootroot00000000000000/* Copyright (c) 2008, 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 #include "test.hpp" #include "settings.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/session.hpp" /* Currently this test only tests that the filter can handle IPv4 addresses. Maybe it should be extended to IPv6 as well, but the actual code is just a template, so it is probably pretty safe to assume that as long as it works for IPv4 it also works for IPv6. */ using namespace libtorrent; template bool compare(ip_range const& lhs , ip_range const& rhs) { return lhs.first == rhs.first && lhs.last == rhs.last && lhs.flags == rhs.flags; } #define IP(x) address::from_string(x, ec) #define IP4(x) address_v4::from_string(x, ec) #define IP6(x) address_v6::from_string(x, ec) template void test_rules_invariant(std::vector > const& r, ip_filter const& f) { typedef typename std::vector >::const_iterator iterator; TEST_CHECK(!r.empty()); if (r.empty()) return; error_code ec; if (sizeof(r.front().first) == sizeof(address_v4)) { TEST_CHECK(r.front().first == IP("0.0.0.0")); TEST_CHECK(r.back().last == IP("255.255.255.255")); } else { TEST_CHECK(r.front().first == IP("::0")); TEST_CHECK(r.back().last == IP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")); } for (iterator i(r.begin()), j(boost::next(r.begin())) , end(r.end()); j != end; ++j, ++i) { TEST_EQUAL(f.access(i->last), int(i->flags)); TEST_EQUAL(f.access(j->first), int(j->flags)); TEST_CHECK(detail::plus_one(i->last.to_bytes()) == j->first.to_bytes()); } } TORRENT_TEST(session_get_ip_filter) { using namespace libtorrent; session ses(settings()); ip_filter const& ipf = ses.get_ip_filter(); #if TORRENT_USE_IPV6 TEST_EQUAL(boost::get<0>(ipf.export_filter()).size(), 1); #else TEST_EQUAL(ipf.export_filter().size(), 1); #endif } TORRENT_TEST(ip_filter) { using namespace libtorrent; std::vector > range; error_code ec; // **** test joining of ranges at the end **** ip_range expected1[] = { {IP4("0.0.0.0"), IP4("0.255.255.255"), 0} , {IP4("1.0.0.0"), IP4("3.0.0.0"), ip_filter::blocked} , {IP4("3.0.0.1"), IP4("255.255.255.255"), 0} }; { ip_filter f; f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked); f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked); #if TORRENT_USE_IPV6 range = boost::get<0>(f.export_filter()); #else range = f.export_filter(); #endif test_rules_invariant(range, f); TEST_CHECK(range.size() == 3); TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare)); } // **** test joining of ranges at the start **** { ip_filter f; f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked); f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked); #if TORRENT_USE_IPV6 range = boost::get<0>(f.export_filter()); #else range = f.export_filter(); #endif test_rules_invariant(range, f); TEST_CHECK(range.size() == 3); TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare)); } // **** test joining of overlapping ranges at the start **** { ip_filter f; f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked); f.add_rule(IP("1.0.0.0"), IP("2.4.0.0"), ip_filter::blocked); #if TORRENT_USE_IPV6 range = boost::get<0>(f.export_filter()); #else range = f.export_filter(); #endif test_rules_invariant(range, f); TEST_CHECK(range.size() == 3); TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare)); } // **** test joining of overlapping ranges at the end **** { ip_filter f; f.add_rule(IP("1.0.0.0"), IP("2.4.0.0"), ip_filter::blocked); f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked); #if TORRENT_USE_IPV6 range = boost::get<0>(f.export_filter()); #else range = f.export_filter(); #endif test_rules_invariant(range, f); TEST_CHECK(range.size() == 3); TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare)); } // **** test joining of multiple overlapping ranges 1 **** { ip_filter f; f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked); f.add_rule(IP("3.0.0.0"), IP("4.0.0.0"), ip_filter::blocked); f.add_rule(IP("5.0.0.0"), IP("6.0.0.0"), ip_filter::blocked); f.add_rule(IP("7.0.0.0"), IP("8.0.0.0"), ip_filter::blocked); f.add_rule(IP("1.0.1.0"), IP("9.0.0.0"), ip_filter::blocked); #if TORRENT_USE_IPV6 range = boost::get<0>(f.export_filter()); #else range = f.export_filter(); #endif test_rules_invariant(range, f); TEST_CHECK(range.size() == 3); ip_range expected[] = { {IP4("0.0.0.0"), IP4("0.255.255.255"), 0} , {IP4("1.0.0.0"), IP4("9.0.0.0"), ip_filter::blocked} , {IP4("9.0.0.1"), IP4("255.255.255.255"), 0} }; TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare)); } // **** test joining of multiple overlapping ranges 2 **** { ip_filter f; f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked); f.add_rule(IP("3.0.0.0"), IP("4.0.0.0"), ip_filter::blocked); f.add_rule(IP("5.0.0.0"), IP("6.0.0.0"), ip_filter::blocked); f.add_rule(IP("7.0.0.0"), IP("8.0.0.0"), ip_filter::blocked); f.add_rule(IP("0.0.1.0"), IP("7.0.4.0"), ip_filter::blocked); #if TORRENT_USE_IPV6 range = boost::get<0>(f.export_filter()); #else range = f.export_filter(); #endif test_rules_invariant(range, f); TEST_CHECK(range.size() == 3); ip_range expected[] = { {IP4("0.0.0.0"), IP4("0.0.0.255"), 0} , {IP4("0.0.1.0"), IP4("8.0.0.0"), ip_filter::blocked} , {IP4("8.0.0.1"), IP4("255.255.255.255"), 0} }; TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare)); } // **** test IPv6 **** #if TORRENT_USE_IPV6 ip_range expected2[] = { {IP6("::0"), IP6("0:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), 0} , {IP6("1::"), IP6("3::"), ip_filter::blocked} , {IP6("3::1"), IP6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), 0} }; { ip_filter f; f.add_rule(IP("2::1"), IP("3::"), ip_filter::blocked); f.add_rule(IP("1::"), IP("2::"), ip_filter::blocked); std::vector > range; range = boost::get<1>(f.export_filter()); test_rules_invariant(range, f); TEST_EQUAL(range.size(), 3); TEST_CHECK(std::equal(range.begin(), range.end(), expected2, &compare)); } #endif port_filter pf; // default contructed port filter should allow any port TEST_CHECK(pf.access(0) == 0); TEST_CHECK(pf.access(65535) == 0); TEST_CHECK(pf.access(6881) == 0); // block port 100 - 300 pf.add_rule(100, 300, port_filter::blocked); TEST_CHECK(pf.access(0) == 0); TEST_CHECK(pf.access(99) == 0); TEST_CHECK(pf.access(100) == port_filter::blocked); TEST_CHECK(pf.access(150) == port_filter::blocked); TEST_CHECK(pf.access(300) == port_filter::blocked); TEST_CHECK(pf.access(301) == 0); TEST_CHECK(pf.access(6881) == 0); TEST_CHECK(pf.access(65535) == 0); } libtorrent-rasterbar-1.1.13/test/test_ip_voter.cpp000066400000000000000000000145641351156116000223260ustar00rootroot00000000000000/* Copyright (c) 2015, 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 "test.hpp" #include "libtorrent/ip_voter.hpp" #include "libtorrent/address.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/random.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/aux_/session_interface.hpp" #include "libtorrent/broadcast_socket.hpp" // for supports_ipv6() #include "setup_transfer.hpp" // for rand_v4 using namespace libtorrent; bool cast_vote(ip_voter& ipv, address ext_ip, address voter) { bool new_ip = ipv.cast_vote(ext_ip, 1, voter); fprintf(stdout, "%15s -> %-15s\n" , print_address(voter).c_str() , print_address(ext_ip).c_str()); if (new_ip) { fprintf(stdout, " \x1b[1mnew external IP: %s\x1b[0m\n" , print_address(ipv.external_address()).c_str()); } return new_ip; } // test the case where every time we get a new IP. Make sure // we don't flap TORRENT_TEST(test_random) { init_rand_address(); ip_voter ipv; address_v4 addr1(address_v4::from_string("51.41.61.132")); bool new_ip = cast_vote(ipv, addr1, rand_v4()); TEST_CHECK(new_ip); TEST_CHECK(ipv.external_address() == addr1); for (int i = 0; i < 1000; ++i) { new_ip = cast_vote(ipv, rand_v4(), rand_v4()); TEST_CHECK(!new_ip); } TEST_CHECK(ipv.external_address() == addr1); } TORRENT_TEST(two_ips) { init_rand_address(); ip_voter ipv; address_v4 addr1(address_v4::from_string("51.1.1.1")); address_v4 addr2(address_v4::from_string("53.3.3.3")); // addr1 is the first address we see, which is the one we pick. Even though // we'll have as many votes for addr2, we shouldn't flap, since addr2 never // gets an overwhelming majority. bool new_ip = cast_vote(ipv, addr1, rand_v4()); TEST_CHECK(new_ip); for (int i = 0; i < 1000; ++i) { new_ip = cast_vote(ipv, addr2, rand_v4()); TEST_CHECK(!new_ip); new_ip = cast_vote(ipv, rand_v4(), rand_v4()); TEST_CHECK(!new_ip); new_ip = cast_vote(ipv, addr1, rand_v4()); TEST_CHECK(!new_ip); TEST_CHECK(ipv.external_address() == addr1); } } TORRENT_TEST(one_ip) { init_rand_address(); ip_voter ipv; address_v4 start_addr(address_v4::from_string("93.12.63.174")); address_v4 addr1(address_v4::from_string("51.1.1.1")); address_v4 addr2(address_v4::from_string("53.3.3.3")); bool new_ip = cast_vote(ipv, start_addr, rand_v4()); TEST_CHECK(new_ip); TEST_CHECK(ipv.external_address() != addr1); TEST_CHECK(ipv.external_address() == start_addr); for (int i = 0; i < 30; ++i) { new_ip = cast_vote(ipv, addr2, rand_v4()); if (new_ip) break; new_ip = cast_vote(ipv, rand_v4(), rand_v4()); if (new_ip) break; new_ip = cast_vote(ipv, addr1, rand_v4()); if (new_ip) break; new_ip = cast_vote(ipv, addr1, rand_v4()); if (new_ip) break; } TEST_CHECK(ipv.external_address() == addr1); for (int i = 0; i < 500; ++i) { new_ip = cast_vote(ipv, addr2, rand_v4()); TEST_CHECK(!new_ip); new_ip = cast_vote(ipv, rand_v4(), rand_v4()); TEST_CHECK(!new_ip); new_ip = cast_vote(ipv, addr1, rand_v4()); TEST_CHECK(!new_ip); new_ip = cast_vote(ipv, addr1, rand_v4()); TEST_CHECK(!new_ip); } TEST_CHECK(ipv.external_address() == addr1); } TORRENT_TEST(externa_ip_1) { init_rand_address(); // test external ip voting external_ip ipv1; // test a single malicious node // adds 50 legitimate responses from different peers // and 50 malicious responses from the same peer error_code ec; address real_external = address_v4::from_string("5.5.5.5", ec); TEST_CHECK(!ec); address malicious = address_v4::from_string("4.4.4.4", ec); TEST_CHECK(!ec); for (int i = 0; i < 50; ++i) { ipv1.cast_vote(real_external, aux::session_interface::source_dht, rand_v4()); ipv1.cast_vote(rand_v4(), aux::session_interface::source_dht, malicious); } TEST_CHECK(ipv1.external_address(rand_v4()) == real_external); } TORRENT_TEST(externa_ip_2) { init_rand_address(); external_ip ipv2; // test a single malicious node // adds 50 legitimate responses from different peers // and 50 consistent malicious responses from the same peer error_code ec; address malicious = address_v4::from_string("4.4.4.4", ec); TEST_CHECK(!ec); address real_external1 = address_v4::from_string("5.5.5.5", ec); TEST_CHECK(!ec); address real_external2; #if TORRENT_USE_IPV6 if (supports_ipv6()) { real_external2 = address_v6::from_string("2f80::", ec); TEST_CHECK(!ec); } #endif malicious = address_v4::from_string("4.4.4.4", ec); TEST_CHECK(!ec); address malicious_external = address_v4::from_string("3.3.3.3", ec); TEST_CHECK(!ec); for (int i = 0; i < 50; ++i) { ipv2.cast_vote(real_external1, aux::session_interface::source_dht, rand_v4()); #if TORRENT_USE_IPV6 if (supports_ipv6()) ipv2.cast_vote(real_external2, aux::session_interface::source_dht, rand_v6()); #endif ipv2.cast_vote(malicious_external, aux::session_interface::source_dht, malicious); } TEST_CHECK(ipv2.external_address(rand_v4()) == real_external1); #if TORRENT_USE_IPV6 if (supports_ipv6()) TEST_CHECK(ipv2.external_address(rand_v6()) == real_external2); #endif } libtorrent-rasterbar-1.1.13/test/test_linked_list.cpp000066400000000000000000000100731351156116000227670ustar00rootroot00000000000000/* Copyright (c) 2015, 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 "test.hpp" #include "libtorrent/linked_list.hpp" using namespace libtorrent; struct test_node : list_node { test_node(int v) : val(v) {} int val; }; void compare(linked_list const& list, int* array, int size) { TEST_EQUAL(list.size(), size); int idx = 0; for (test_node const* i = list.front(); i != NULL; i = i->next, ++idx) { TEST_EQUAL(i->val, array[idx]); } } TORRENT_TEST(push_back) { test_node n0(0); test_node n1(1); linked_list list; list.push_back(&n0); list.push_back(&n1); int expected[] = { 0, 1 }; compare(list, expected, 2); } TORRENT_TEST(push_front) { test_node n0(0); test_node n1(1); linked_list list; list.push_back(&n1); list.push_front(&n0); int expected[] = { 0, 1 }; compare(list, expected, 2); } TORRENT_TEST(erase_begin) { test_node n0(0); test_node n1(1); test_node n2(2); linked_list list; list.push_back(&n0); list.push_back(&n1); list.push_back(&n2); list.erase(&n0); int expected[] = { 1, 2 }; compare(list, expected, 2); } TORRENT_TEST(erase_end) { test_node n0(0); test_node n1(1); test_node n2(2); linked_list list; list.push_back(&n0); list.push_back(&n1); list.push_back(&n2); list.erase(&n2); int expected[] = { 0, 1 }; compare(list, expected, 2); } TORRENT_TEST(erase_middle) { test_node n0(0); test_node n1(1); test_node n2(2); linked_list list; list.push_back(&n0); list.push_back(&n1); list.push_back(&n2); list.erase(&n1); int expected[] = { 0, 2 }; compare(list, expected, 2); } TORRENT_TEST(erase_last) { test_node n0(0); linked_list list; list.push_back(&n0); list.erase(&n0); int expected[] = { -1 }; compare(list, expected, 0); TEST_CHECK(list.empty()); } TORRENT_TEST(iterate_forward) { test_node n0(0); test_node n1(1); test_node n2(2); linked_list list; list.push_back(&n0); list.push_back(&n1); list.push_back(&n2); list_iterator it = list.iterate(); TEST_EQUAL(it.get(), &n0); it.next(); TEST_EQUAL(it.get(), &n1); it.next(); TEST_EQUAL(it.get(), &n2); it.next(); TEST_EQUAL(it.get(), static_cast(NULL)); } TORRENT_TEST(iterate_backward) { test_node n0(0); test_node n1(1); test_node n2(2); linked_list list; list.push_back(&n0); list.push_back(&n1); list.push_back(&n2); list_iterator it = list.iterate(); it.next(); it.next(); TEST_EQUAL(it.get(), &n2); it.prev(); TEST_EQUAL(it.get(), &n1); it.prev(); TEST_EQUAL(it.get(), &n0); it.prev(); TEST_EQUAL(it.get(), static_cast(NULL)); } libtorrent-rasterbar-1.1.13/test/test_lsd.cpp000066400000000000000000000070711351156116000212540ustar00rootroot00000000000000/* Copyright (c) 2008, 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/session.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/torrent_status.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/thread.hpp" #include #include "test.hpp" #include "setup_transfer.hpp" #include void test_lsd() { using namespace libtorrent; namespace lt = libtorrent; // these are declared before the session objects // so that they are destructed last. This enables // the sessions to destruct in parallel session_proxy p1; session_proxy p2; settings_pack pack; pack.set_bool(settings_pack::allow_multiple_connections_per_ip, true); pack.set_bool(settings_pack::enable_dht, false); pack.set_bool(settings_pack::enable_lsd, true); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48100"); #ifndef TORRENT_NO_DEPRECATE pack.set_bool(settings_pack::rate_limit_utp, true); #endif lt::session ses1(pack); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:49100"); lt::session ses2(pack); torrent_handle tor1; torrent_handle tor2; using boost::tuples::ignore; boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0, true, false, false, "_lsd" , 16 * 1024, 0, false, 0, false); for (int i = 0; i < 30; ++i) { print_alerts(ses1, "ses1", true); print_alerts(ses2, "ses2", true); torrent_status st1 = tor1.status(); torrent_status st2 = tor2.status(); print_ses_rate(i, &st1, &st2); if (st2.is_seeding /*&& st3.is_seeding*/) break; test_sleep(1000); } TEST_CHECK(tor2.status().is_seeding); if (tor2.status().is_seeding) std::cerr << "done\n"; // this allows shutting down the sessions in parallel p1 = ses1.abort(); p2 = ses2.abort(); } TORRENT_TEST(lsd) { using namespace libtorrent; // in case the previous run was terminated error_code ec; remove_all("./tmp1_lsd", ec); remove_all("./tmp2_lsd", ec); remove_all("./tmp3_lsd", ec); test_lsd(); remove_all("./tmp1_lsd", ec); remove_all("./tmp2_lsd", ec); remove_all("./tmp3_lsd", ec); } libtorrent-rasterbar-1.1.13/test/test_magnet.cpp000066400000000000000000000335371351156116000217530ustar00rootroot00000000000000/* Copyright (c) 2012, 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 "test.hpp" #include "setup_transfer.hpp" #include "libtorrent/magnet_uri.hpp" #include "libtorrent/session.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent_info.hpp" // for announce_entry #include "libtorrent/announce_entry.hpp" #include "settings.hpp" using namespace libtorrent; namespace lt = libtorrent; void test_remove_url(std::string url) { lt::session s(settings()); add_torrent_params p; p.flags &= ~add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; p.url = url; p.save_path = "."; torrent_handle h = s.add_torrent(p); std::vector handles = s.get_torrents(); TEST_EQUAL(handles.size(), 1); TEST_NOTHROW(s.remove_torrent(h)); handles = s.get_torrents(); TEST_EQUAL(handles.size(), 0); } TORRENT_TEST(remove_url) { test_remove_url("magnet:?xt=urn:btih:0123456789abcdef0123456789abcdef01234567"); } TORRENT_TEST(remove_url2) { test_remove_url("http://non-existent.com/test.torrent"); } TORRENT_TEST(magnet) { session_proxy p1; session_proxy p2; // test session state load/restore settings_pack pack = settings(); pack.set_str(settings_pack::user_agent, "test"); pack.set_int(settings_pack::tracker_receive_timeout, 1234); pack.set_int(settings_pack::file_pool_size, 543); pack.set_int(settings_pack::urlseed_wait_retry, 74); pack.set_int(settings_pack::initial_picker_threshold, 351); pack.set_bool(settings_pack::upnp_ignore_nonrouters, true); pack.set_bool(settings_pack::coalesce_writes, true); pack.set_bool(settings_pack::close_redundant_connections, false); pack.set_int(settings_pack::auto_scrape_interval, 235); pack.set_int(settings_pack::auto_scrape_min_interval, 62); boost::scoped_ptr s(new lt::session(pack)); TEST_EQUAL(pack.get_str(settings_pack::user_agent), "test"); TEST_EQUAL(pack.get_int(settings_pack::tracker_receive_timeout), 1234); #ifndef TORRENT_DISABLE_DHT dht_settings dhts; dhts.max_peers_reply = 70; s->set_dht_settings(dhts); #endif /* #ifndef TORRENT_DISABLE_DHT dht_settings dht_sett; s->set_dht_settings(dht_sett); #endif */ entry session_state; s->save_state(session_state); // test magnet link parsing add_torrent_params p; p.flags &= ~add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; p.save_path = "."; error_code ec; p.url = "magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" "&tr=http://1" "&tr=http://2" "&tr=http://3" "&dn=foo" "&dht=127.0.0.1:43"; torrent_handle t = s->add_torrent(p, ec); TEST_CHECK(!ec); if (ec) fprintf(stderr, "%s\n", ec.message().c_str()); std::vector trackers = t.trackers(); TEST_EQUAL(trackers.size(), 3); std::set trackers_set; for (std::vector::iterator i = trackers.begin() , end(trackers.end()); i != end; ++i) trackers_set.insert(i->url); TEST_CHECK(trackers_set.count("http://1") == 1); TEST_CHECK(trackers_set.count("http://2") == 1); TEST_CHECK(trackers_set.count("http://3") == 1); p.url = "magnet:" "?tr=http://1" "&tr=http://2" "&dn=foo" "&dht=127.0.0.1:43" "&xt=urn:btih:c352cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"; torrent_handle t2 = s->add_torrent(p, ec); TEST_CHECK(!ec); if (ec) fprintf(stderr, "%s\n", ec.message().c_str()); trackers = t2.trackers(); TEST_EQUAL(trackers.size(), 2); TEST_EQUAL(trackers[0].tier, 0); TEST_EQUAL(trackers[1].tier, 1); p.url = "magnet:" "?tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80" "&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80" "&tr=udp%3A%2F%2Ftracker.ccc.de%3A80" "&xt=urn:btih:a38d02c287893842a32825aa866e00828a318f07" "&dn=Ubuntu+11.04+%28Final%29"; torrent_handle t3 = s->add_torrent(p, ec); TEST_CHECK(!ec); if (ec) fprintf(stderr, "%s\n", ec.message().c_str()); trackers = t3.trackers(); TEST_EQUAL(trackers.size(), 3); if (trackers.size() > 0) { TEST_EQUAL(trackers[0].url, "udp://tracker.openbittorrent.com:80"); TEST_EQUAL(trackers[0].tier, 0); fprintf(stderr, "1: %s\n", trackers[0].url.c_str()); } if (trackers.size() > 1) { TEST_EQUAL(trackers[1].url, "udp://tracker.publicbt.com:80"); TEST_EQUAL(trackers[1].tier, 1); fprintf(stderr, "2: %s\n", trackers[1].url.c_str()); } if (trackers.size() > 2) { TEST_EQUAL(trackers[2].url, "udp://tracker.ccc.de:80"); TEST_EQUAL(trackers[2].tier, 2); fprintf(stderr, "3: %s\n", trackers[2].url.c_str()); } TEST_EQUAL(to_hex(t.info_hash().to_string()), "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"); p1 = s->abort(); s.reset(new lt::session(settings())); std::vector buf; bencode(std::back_inserter(buf), session_state); bdecode_node session_state2; int ret = bdecode(&buf[0], &buf[0] + buf.size(), session_state2, ec); TEST_CHECK(ret == 0); fprintf(stderr, "session_state\n%s\n", print_entry(session_state2).c_str()); // make sure settings that haven't been changed from their defaults are not saved TEST_CHECK(session_state2.dict_find("settings") .dict_find("optimistic_disk_retry") == 0); s->load_state(session_state2); #define CMP_SET(x) fprintf(stderr, #x ": %d %d\n"\ , s->get_settings().get_int(settings_pack:: x)\ , pack.get_int(settings_pack:: x)); \ TEST_EQUAL(s->get_settings().get_int(settings_pack:: x), pack.get_int(settings_pack:: x)) CMP_SET(tracker_receive_timeout); CMP_SET(file_pool_size); CMP_SET(urlseed_wait_retry); CMP_SET(initial_picker_threshold); CMP_SET(auto_scrape_interval); CMP_SET(auto_scrape_min_interval); p2 = s->abort(); } TORRENT_TEST(parse_missing_hash) { // parse_magnet_uri error_code ec; add_torrent_params p; parse_magnet_uri("magnet:?dn=foo&dht=127.0.0.1:43", p, ec); TEST_EQUAL(ec, error_code(errors::missing_info_hash_in_uri)); ec.clear(); } TORRENT_TEST(parse_base32_hash) { // parse_magnet_uri error_code ec; add_torrent_params p; parse_magnet_uri("magnet:?xt=urn:btih:MFRGCYTBMJQWEYLCMFRGCYTBMJQWEYLC", p, ec); TEST_CHECK(!ec); TEST_EQUAL(p.info_hash, sha1_hash("abababababababababab")); ec.clear(); } TORRENT_TEST(parse_web_seeds) { // parse_magnet_uri error_code ec; add_torrent_params p; parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd&ws=http://foo.com/bar&ws=http://bar.com/foo", p, ec); TEST_CHECK(!ec); TEST_EQUAL(p.url_seeds.size(), 2); TEST_EQUAL(p.url_seeds[0], "http://foo.com/bar"); TEST_EQUAL(p.url_seeds[1], "http://bar.com/foo"); ec.clear(); } TORRENT_TEST(parse_missing_hash2) { error_code ec; add_torrent_params p; parse_magnet_uri("magnet:?xt=blah&dn=foo&dht=127.0.0.1:43", p, ec); TEST_EQUAL(ec, error_code(errors::missing_info_hash_in_uri)); ec.clear(); } TORRENT_TEST(parse_short_hash) { error_code ec; add_torrent_params p; parse_magnet_uri("magnet:?xt=urn:btih:abababab", p, ec); TEST_EQUAL(ec, error_code(errors::invalid_info_hash)); ec.clear(); } TORRENT_TEST(parse_long_hash) { error_code ec; add_torrent_params p; parse_magnet_uri("magnet:?xt=urn:btih:ababababababababababab", p, ec); TEST_EQUAL(ec, error_code(errors::invalid_info_hash)); ec.clear(); } TORRENT_TEST(parse_space_hash) { error_code ec; add_torrent_params p; parse_magnet_uri("magnet:?xt=urn:btih: abababababababababab", p, ec); TEST_EQUAL(ec, error_code(errors::invalid_info_hash)); ec.clear(); } TORRENT_TEST(parse_peer) { std::vector peers; parse_magnet_uri_peers("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd&dn=foo&x.pe=127.0.0.1:43&x.pe=&x.pe=:100&x.pe=[::1]:45", peers); #if TORRENT_USE_IPV6 TEST_EQUAL(peers.size(), 2); TEST_EQUAL(peers[0], ep("127.0.0.1", 43)); TEST_EQUAL(peers[1], ep("::1", 45)); #else TEST_EQUAL(peers.size(), 1); TEST_EQUAL(peers[0], ep("127.0.0.1", 43)); #endif } #ifndef TORRENT_DISABLE_DHT TORRENT_TEST(parse_dht_node) { error_code ec; add_torrent_params p; parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd&dn=foo&dht=127.0.0.1:43", p, ec); TEST_CHECK(!ec); if (ec) fprintf(stderr, "%s\n", ec.message().c_str()); ec.clear(); TEST_EQUAL(p.dht_nodes.size(), 1); TEST_EQUAL(p.dht_nodes[0].first, "127.0.0.1"); TEST_EQUAL(p.dht_nodes[0].second, 43); } #endif TORRENT_TEST(make_magnet_uri) { // make_magnet_uri entry info; info["pieces"] = "aaaaaaaaaaaaaaaaaaaa"; info["name"] = "slightly shorter name, it's kind of sad that people started the trend of incorrectly encoding the regular name field and then adding another one with correct encoding"; info["name.utf-8"] = "this is a long ass name in order to try to make make_magnet_uri overflow and hopefully crash. Although, by the time you read this that particular bug should have been fixed"; info["piece length"] = 16 * 1024; info["length"] = 3245; entry torrent; torrent["info"] = info; entry::list_type& al1 = torrent["announce-list"].list(); al1.push_back(entry::list_type()); entry::list_type& al = al1.back().list(); al.push_back(entry("http://bigtorrent.org:2710/announce")); al.push_back(entry("http://bt.careland.com.cn:6969/announce")); al.push_back(entry("http://bt.e-burg.org:2710/announce")); al.push_back(entry("http://bttrack.9you.com/announce")); al.push_back(entry("http://coppersurfer.tk:6969/announce")); al.push_back(entry("http://erdgeist.org/arts/software/opentracker/announce")); al.push_back(entry("http://exodus.desync.com/announce")); al.push_back(entry("http://fr33dom.h33t.com:3310/announce")); al.push_back(entry("http://genesis.1337x.org:1337/announce")); al.push_back(entry("http://inferno.demonoid.me:3390/announce")); al.push_back(entry("http://inferno.demonoid.ph:3390/announce")); al.push_back(entry("http://ipv6.tracker.harry.lu/announce")); al.push_back(entry("http://lnxroot.com:6969/announce")); al.push_back(entry("http://nemesis.1337x.org/announce")); al.push_back(entry("http://puto.me:6969/announce")); al.push_back(entry("http://sline.net:2710/announce")); al.push_back(entry("http://tracker.beeimg.com:6969/announce")); al.push_back(entry("http://tracker.ccc.de/announce")); al.push_back(entry("http://tracker.coppersurfer.tk/announce")); al.push_back(entry("http://tracker.coppersurfer.tk:6969/announce")); al.push_back(entry("http://tracker.cpleft.com:2710/announce")); al.push_back(entry("http://tracker.istole.it/announce")); al.push_back(entry("http://tracker.kamyu.net/announce")); al.push_back(entry("http://tracker.novalayer.org:6969/announce")); al.push_back(entry("http://tracker.torrent.to:2710/announce")); al.push_back(entry("http://tracker.torrentbay.to:6969/announce")); al.push_back(entry("udp://tracker.openbittorrent.com:80")); al.push_back(entry("udp://tracker.publicbt.com:80")); std::vector buf; bencode(std::back_inserter(buf), torrent); buf.push_back('\0'); printf("%s\n", &buf[0]); error_code ec; torrent_info ti(&buf[0], buf.size(), ec); TEST_EQUAL(al.size(), ti.trackers().size()); std::string magnet = make_magnet_uri(ti); printf("%s len: %d\n", magnet.c_str(), int(magnet.size())); } TORRENT_TEST(make_magnet_uri2) { // make_magnet_uri entry info; info["pieces"] = "aaaaaaaaaaaaaaaaaaaa"; info["name"] = "test"; info["name.utf-8"] = "test"; info["piece length"] = 16 * 1024; info["length"] = 3245; entry torrent; torrent["info"] = info; torrent["url-list"] = "http://foo.com/bar"; std::vector buf; bencode(std::back_inserter(buf), torrent); buf.push_back('\0'); printf("%s\n", &buf[0]); error_code ec; torrent_info ti(&buf[0], buf.size(), ec); std::string magnet = make_magnet_uri(ti); printf("%s len: %d\n", magnet.c_str(), int(magnet.size())); TEST_CHECK(magnet.find("&ws=http%3a%2f%2ffoo.com%2fbar") != std::string::npos); } TORRENT_TEST(trailing_whitespace) { session ses(settings()); add_torrent_params p; p.save_path = "."; p.url = "magnet:?xt=urn:btih:abaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"; // invalid hash TEST_THROW(ses.add_torrent(p)); p.url = "magnet:?xt=urn:btih:abaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; // now it's valid, because there's no trailing whitespace torrent_handle h = ses.add_torrent(p); TEST_CHECK(h.is_valid()); } TORRENT_TEST(invalid_tracker_escaping) { add_torrent_params p; p.save_path = "."; error_code ec; parse_magnet_uri("magnet:?tr=udp%3A%2F%2Ftracker.openjnt.com%\xf7" "A80&tr=udp%3A%2F%2Ftracker.pub.ciltbcom%3A80&tr=udp%3A%2F%2Ftracker.ccc.de%3A80&xt=urn:btih:a38d02c287893842a39737aa866e00828aA80&xt=urn:buntu+11.04+%28Final%29" , p, ec); TEST_CHECK(ec); } TORRENT_TEST(invalid_web_seed_escaping) { add_torrent_params p; p.save_path = "."; error_code ec; parse_magnet_uri("magnet:?ws=udp%3A%2F%2Ftracker.openjnt.com%\xf7" "A80", p, ec); TEST_CHECK(ec); } libtorrent-rasterbar-1.1.13/test/test_merkle.cpp000066400000000000000000000074311351156116000217510ustar00rootroot00000000000000/* Copyright (c) 2012, 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 "test.hpp" #include "libtorrent/aux_/merkle.hpp" TORRENT_TEST(merkle) { using namespace libtorrent; // test merkle_*() functions // this is the structure: // 0 // 1 2 // 3 4 5 6 // 7 8 9 10 11 12 13 14 // num_leafs = 8 TEST_EQUAL(merkle_num_leafs(1), 1); TEST_EQUAL(merkle_num_leafs(2), 2); TEST_EQUAL(merkle_num_leafs(3), 4); TEST_EQUAL(merkle_num_leafs(4), 4); TEST_EQUAL(merkle_num_leafs(5), 8); TEST_EQUAL(merkle_num_leafs(6), 8); TEST_EQUAL(merkle_num_leafs(7), 8); TEST_EQUAL(merkle_num_leafs(8), 8); TEST_EQUAL(merkle_num_leafs(9), 16); TEST_EQUAL(merkle_num_leafs(10), 16); TEST_EQUAL(merkle_num_leafs(11), 16); TEST_EQUAL(merkle_num_leafs(12), 16); TEST_EQUAL(merkle_num_leafs(13), 16); TEST_EQUAL(merkle_num_leafs(14), 16); TEST_EQUAL(merkle_num_leafs(15), 16); TEST_EQUAL(merkle_num_leafs(16), 16); TEST_EQUAL(merkle_num_leafs(17), 32); TEST_EQUAL(merkle_num_leafs(18), 32); // parents TEST_EQUAL(merkle_get_parent(1), 0); TEST_EQUAL(merkle_get_parent(2), 0); TEST_EQUAL(merkle_get_parent(3), 1); TEST_EQUAL(merkle_get_parent(4), 1); TEST_EQUAL(merkle_get_parent(5), 2); TEST_EQUAL(merkle_get_parent(6), 2); TEST_EQUAL(merkle_get_parent(7), 3); TEST_EQUAL(merkle_get_parent(8), 3); TEST_EQUAL(merkle_get_parent(9), 4); TEST_EQUAL(merkle_get_parent(10), 4); TEST_EQUAL(merkle_get_parent(11), 5); TEST_EQUAL(merkle_get_parent(12), 5); TEST_EQUAL(merkle_get_parent(13), 6); TEST_EQUAL(merkle_get_parent(14), 6); // siblings TEST_EQUAL(merkle_get_sibling(1), 2); TEST_EQUAL(merkle_get_sibling(2), 1); TEST_EQUAL(merkle_get_sibling(3), 4); TEST_EQUAL(merkle_get_sibling(4), 3); TEST_EQUAL(merkle_get_sibling(5), 6); TEST_EQUAL(merkle_get_sibling(6), 5); TEST_EQUAL(merkle_get_sibling(7), 8); TEST_EQUAL(merkle_get_sibling(8), 7); TEST_EQUAL(merkle_get_sibling(9), 10); TEST_EQUAL(merkle_get_sibling(10), 9); TEST_EQUAL(merkle_get_sibling(11), 12); TEST_EQUAL(merkle_get_sibling(12), 11); TEST_EQUAL(merkle_get_sibling(13), 14); TEST_EQUAL(merkle_get_sibling(14), 13); // total number of nodes given the number of leafs TEST_EQUAL(merkle_num_nodes(1), 1); TEST_EQUAL(merkle_num_nodes(2), 3); TEST_EQUAL(merkle_num_nodes(4), 7); TEST_EQUAL(merkle_num_nodes(8), 15); TEST_EQUAL(merkle_num_nodes(16), 31); } libtorrent-rasterbar-1.1.13/test/test_packet_buffer.cpp000066400000000000000000000106311351156116000232660ustar00rootroot00000000000000/* Copyright (c) 2012, 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 "test.hpp" #include "libtorrent/packet_buffer.hpp" using libtorrent::packet_buffer; // test packet_buffer TORRENT_TEST(insert) { packet_buffer pb; int a123 = 123; int a125 = 125; int a500 = 500; int a501 = 501; TEST_EQUAL(pb.capacity(), 0); TEST_EQUAL(pb.size(), 0); TEST_EQUAL(pb.span(), 0); pb.insert(123, &a123); TEST_EQUAL(pb.at(123 + 16), 0); TEST_CHECK(pb.at(123) == &a123); TEST_CHECK(pb.capacity() > 0); TEST_EQUAL(pb.size(), 1); TEST_EQUAL(pb.span(), 1); TEST_EQUAL(pb.cursor(), 123); pb.insert(125, &a125); TEST_CHECK(pb.at(125) == &a125); TEST_EQUAL(pb.size(), 2); TEST_EQUAL(pb.span(), 3); TEST_EQUAL(pb.cursor(), 123); pb.insert(500, &a500); TEST_EQUAL(pb.size(), 3); TEST_EQUAL(pb.span(), 501 - 123); TEST_EQUAL(pb.capacity(), 512); pb.insert(500, &a501); TEST_EQUAL(pb.size(), 3); pb.insert(500, &a500); TEST_EQUAL(pb.size(), 3); TEST_CHECK(pb.remove(123) == &a123); TEST_EQUAL(pb.size(), 2); TEST_EQUAL(pb.span(), 501 - 125); TEST_EQUAL(pb.cursor(), 125); TEST_CHECK(pb.remove(125) == &a125); TEST_EQUAL(pb.size(), 1); TEST_EQUAL(pb.span(), 1); TEST_EQUAL(pb.cursor(), 500); TEST_CHECK(pb.remove(500) == &a500); TEST_EQUAL(pb.size(), 0); TEST_EQUAL(pb.span(), 0); for (int i = 0; i < 0xff; ++i) { int index = (i + 0xfff0) & 0xffff; pb.insert(index, reinterpret_cast(index + 1)); fprintf(stdout, "insert: %u (mask: %x)\n", index, int(pb.capacity() - 1)); TEST_EQUAL(pb.capacity(), 512); if (i >= 14) { index = (index - 14) & 0xffff; fprintf(stdout, "remove: %u\n", index); TEST_CHECK(pb.remove(index) == reinterpret_cast(index + 1)); TEST_EQUAL(pb.size(), 14); } } } TORRENT_TEST(wrap) { // test wrapping the indices packet_buffer pb; TEST_EQUAL(pb.size(), 0); pb.insert(0xfffe, (void*)1); TEST_CHECK(pb.at(0xfffe) == (void*)1); pb.insert(2, (void*)2); TEST_CHECK(pb.at(2) == (void*)2); pb.remove(0xfffe); TEST_CHECK(pb.at(0xfffe) == (void*)0); TEST_CHECK(pb.at(2) == (void*)2); } TORRENT_TEST(wrap2) { // test wrapping the indices packet_buffer pb; TEST_EQUAL(pb.size(), 0); pb.insert(0xfff3, (void*)1); TEST_CHECK(pb.at(0xfff3) == (void*)1); int new_index = (0xfff3 + pb.capacity()) & 0xffff; pb.insert(new_index, (void*)2); TEST_CHECK(pb.at(new_index) == (void*)2); void* old = pb.remove(0xfff3); TEST_CHECK(old == (void*)1); TEST_CHECK(pb.at(0xfff3) == (void*)0); TEST_CHECK(pb.at(new_index) == (void*)2); } TORRENT_TEST(reverse_wrap) { // test wrapping the indices backwards packet_buffer pb; TEST_EQUAL(pb.size(), 0); pb.insert(0xfff3, (void*)1); TEST_CHECK(pb.at(0xfff3) == (void*)1); int new_index = (0xfff3 + pb.capacity()) & 0xffff; pb.insert(new_index, (void*)2); TEST_CHECK(pb.at(new_index) == (void*)2); void* old = pb.remove(0xfff3); TEST_CHECK(old == (void*)1); TEST_CHECK(pb.at(0xfff3) == (void*)0); TEST_CHECK(pb.at(new_index) == (void*)2); pb.insert(0xffff, (void*)0xffff); } libtorrent-rasterbar-1.1.13/test/test_part_file.cpp000066400000000000000000000124221351156116000224330ustar00rootroot00000000000000/* Copyright (c) 2012, 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 #include "test.hpp" #include "libtorrent/part_file.hpp" #include "libtorrent/file.hpp" #include "libtorrent/error_code.hpp" using namespace libtorrent; TORRENT_TEST(part_file) { error_code ec; std::string cwd = complete("."); remove_all(combine_path(cwd, "partfile_test_dir"), ec); if (ec) fprintf(stderr, "remove_all: %s\n", ec.message().c_str()); remove_all(combine_path(cwd, "partfile_test_dir2"), ec); if (ec) fprintf(stderr, "remove_all: %s\n", ec.message().c_str()); int piece_size = 16 * 0x4000; char buf[1024]; { create_directory(combine_path(cwd, "partfile_test_dir"), ec); if (ec) fprintf(stderr, "create_directory: %s\n", ec.message().c_str()); create_directory(combine_path(cwd, "partfile_test_dir2"), ec); if (ec) fprintf(stderr, "create_directory: %s\n", ec.message().c_str()); part_file pf(combine_path(cwd, "partfile_test_dir"), "partfile.parts", 100, piece_size); pf.flush_metadata(ec); if (ec) fprintf(stderr, "flush_metadata: %s\n", ec.message().c_str()); // since we don't have anything in the part file, it will have // not have been created yet TEST_CHECK(!exists(combine_path(combine_path(cwd, "partfile_test_dir"), "partfile.parts"))); // write something to the metadata file for (int i = 0; i < 1024; ++i) buf[i] = i; file::iovec_t v = {&buf, 1024}; pf.writev(&v, 1, 10, 0, ec); if (ec) fprintf(stderr, "part_file::writev: %s\n", ec.message().c_str()); pf.flush_metadata(ec); if (ec) fprintf(stderr, "flush_metadata: %s\n", ec.message().c_str()); // now wwe should have created the partfile TEST_CHECK(exists(combine_path(combine_path(cwd, "partfile_test_dir"), "partfile.parts"))); pf.move_partfile(combine_path(cwd, "partfile_test_dir2"), ec); TEST_CHECK(!ec); if (ec) fprintf(stderr, "move_partfile: %s\n", ec.message().c_str()); TEST_CHECK(!exists(combine_path(combine_path(cwd, "partfile_test_dir"), "partfile.parts"))); TEST_CHECK(exists(combine_path(combine_path(cwd, "partfile_test_dir2"), "partfile.parts"))); memset(buf, 0, sizeof(buf)); pf.readv(&v, 1, 10, 0, ec); if (ec) fprintf(stderr, "part_file::readv: %s\n", ec.message().c_str()); for (int i = 0; i < 1024; ++i) TEST_CHECK(buf[i] == char(i)); } { // load the part file back in part_file pf(combine_path(cwd, "partfile_test_dir2"), "partfile.parts", 100, piece_size); memset(buf, 0, sizeof(buf)); file::iovec_t v = {&buf, 1024}; pf.readv(&v, 1, 10, 0, ec); if (ec) fprintf(stderr, "part_file::readv: %s\n", ec.message().c_str()); for (int i = 0; i < 1024; ++i) TEST_CHECK(buf[i] == char(i)); // test exporting the piece to a file std::string output_filename = combine_path(combine_path(cwd, "partfile_test_dir") , "part_file_test_export"); file output(output_filename, file::read_write, ec); if (ec) fprintf(stderr, "export open file: %s\n", ec.message().c_str()); pf.export_file(output, 10 * piece_size, 1024, ec); if (ec) fprintf(stderr, "export_file: %s\n", ec.message().c_str()); pf.free_piece(10); pf.flush_metadata(ec); if (ec) fprintf(stderr, "flush_metadata: %s\n", ec.message().c_str()); // we just removed the last piece. The partfile does not // contain anything anymore, it should have deleted itself TEST_CHECK(!exists(combine_path(combine_path(cwd, "partfile_test_dir2"), "partfile.parts"), ec)); TEST_CHECK(!ec); if (ec) fprintf(stderr, "exists: %s\n", ec.message().c_str()); output.close(); // verify that the exported file is what we expect it to be output.open(output_filename, file::read_only, ec); if (ec) fprintf(stderr, "exported file open: %s\n", ec.message().c_str()); memset(buf, 0, sizeof(buf)); output.readv(0, &v, 1, ec); if (ec) fprintf(stderr, "exported file read: %s\n", ec.message().c_str()); for (int i = 0; i < 1024; ++i) TEST_CHECK(buf[i] == char(i)); } } libtorrent-rasterbar-1.1.13/test/test_pe_crypto.cpp000066400000000000000000000130171351156116000224730ustar00rootroot00000000000000/* Copyright (c) 2007, Un Shyam 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 #include #include "libtorrent/hasher.hpp" #include "libtorrent/pe_crypto.hpp" #include "libtorrent/session.hpp" #include "libtorrent/random.hpp" #include "setup_transfer.hpp" #include "test.hpp" extern "C" { #include "libtorrent/tommath.h" } #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) void test_enc_handler(libtorrent::crypto_plugin* a, libtorrent::crypto_plugin* b) { #ifdef TORRENT_USE_VALGRIND const int repcount = 10; #else const int repcount = 128; #endif for (int rep = 0; rep < repcount; ++rep) { int buf_len = rand() % (512 * 1024); char* buf = new char[buf_len]; char* cmp_buf = new char[buf_len]; std::generate(buf, buf + buf_len, &std::rand); std::memcpy(cmp_buf, buf, buf_len); using namespace boost::asio; std::vector iovec; iovec.push_back(mutable_buffer(buf, buf_len)); a->encrypt(iovec); TEST_CHECK(!std::equal(buf, buf + buf_len, cmp_buf)); TEST_CHECK(iovec.empty()); int consume = 0; int produce = buf_len; int packet_size = 0; iovec.push_back(mutable_buffer(buf, buf_len)); b->decrypt(iovec, consume, produce, packet_size); TEST_CHECK(std::equal(buf, buf + buf_len, cmp_buf)); TEST_CHECK(iovec.empty()); TEST_EQUAL(consume, 0); TEST_EQUAL(produce, buf_len); TEST_EQUAL(packet_size, 0); iovec.push_back(mutable_buffer(buf, buf_len)); b->encrypt(iovec); TEST_CHECK(!std::equal(buf, buf + buf_len, cmp_buf)); TEST_CHECK(iovec.empty()); consume = 0; produce = buf_len; packet_size = 0; iovec.push_back(mutable_buffer(buf, buf_len)); a->decrypt(iovec, consume, produce, packet_size); TEST_CHECK(std::equal(buf, buf + buf_len, cmp_buf)); TEST_CHECK(iovec.empty()); TEST_EQUAL(consume, 0); TEST_EQUAL(produce, buf_len); TEST_EQUAL(packet_size, 0); delete[] buf; delete[] cmp_buf; } } void print_key(char const* key) { for (int i = 0;i < 96; ++i) { printf("%02x ", unsigned(key[i])); } printf("\n"); } TORRENT_TEST(diffie_hellman) { using namespace libtorrent; #ifdef TORRENT_USE_VALGRIND const int repcount = 10; #else const int repcount = 128; #endif for (int rep = 0; rep < repcount; ++rep) { dh_key_exchange DH1, DH2; DH1.compute_secret(DH2.get_local_key()); DH2.compute_secret(DH1.get_local_key()); TEST_CHECK(std::equal(DH1.get_secret(), DH1.get_secret() + 96, DH2.get_secret())); if (!std::equal(DH1.get_secret(), DH1.get_secret() + 96, DH2.get_secret())) { printf("DH1 local: "); print_key(DH1.get_local_key()); printf("DH2 local: "); print_key(DH2.get_local_key()); printf("DH1 shared_secret: "); print_key(DH1.get_secret()); printf("DH2 shared_secret: "); print_key(DH2.get_secret()); } } } TORRENT_TEST(rc4) { using namespace libtorrent; sha1_hash test1_key = hasher("test1_key",8).final(); sha1_hash test2_key = hasher("test2_key",8).final(); fprintf(stderr, "testing RC4 handler\n"); rc4_handler rc41; rc41.set_incoming_key(&test2_key[0], 20); rc41.set_outgoing_key(&test1_key[0], 20); rc4_handler rc42; rc42.set_incoming_key(&test1_key[0], 20); rc42.set_outgoing_key(&test2_key[0], 20); test_enc_handler(&rc41, &rc42); } TORRENT_TEST(tommath) { const unsigned char key[96] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63 }; mp_int bigint; mp_init(&bigint); TEST_CHECK(mp_read_unsigned_bin(&bigint, key, sizeof(key)) == 0); TEST_EQUAL(mp_unsigned_bin_size(&bigint), sizeof(key)); mp_clear(&bigint); } #else TORRENT_TEST(disabled) { fprintf(stderr, "PE test not run because it's disabled\n"); } #endif libtorrent-rasterbar-1.1.13/test/test_peer_classes.cpp000066400000000000000000000117201351156116000231360ustar00rootroot00000000000000/* Copyright (c) 2012, 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 "test.hpp" #include "libtorrent/peer_class.hpp" #include "libtorrent/peer_class_set.hpp" #include "libtorrent/peer_class_type_filter.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/session.hpp" using namespace libtorrent; std::string class_name(peer_class_t id, peer_class_pool const& p) { peer_class const* c = p.at(id); TEST_CHECK(c != NULL); if (c == NULL) return ""; peer_class_info i; c->get_info(&i); return i.label; } TORRENT_TEST(peer_class) { peer_class_pool pool; peer_class_t id1 = pool.new_peer_class("test1"); peer_class_t id2 = pool.new_peer_class("test2"); // make sure there's no leak for (int i = 0; i < 1000; ++i) { peer_class_t tmp = pool.new_peer_class("temp"); pool.decref(tmp); } peer_class_t id3 = pool.new_peer_class("test3"); TEST_CHECK(id3 == id2 + 1); // make sure refcounting works TEST_EQUAL(class_name(id3, pool), "test3"); pool.incref(id3); TEST_EQUAL(class_name(id3, pool), "test3"); pool.decref(id3); TEST_EQUAL(class_name(id3, pool), "test3"); pool.decref(id3); // it should have been deleted now TEST_CHECK(pool.at(id3) == NULL); // test setting and retrieving upload and download rates pool.at(id2)->set_upload_limit(1000); pool.at(id2)->set_download_limit(2000); peer_class_info i; pool.at(id2)->get_info(&i); TEST_EQUAL(i.upload_limit, 1000); TEST_EQUAL(i.download_limit, 2000); // test peer_class_type_filter peer_class_type_filter filter; for (int i = 0; i < 5; ++i) { TEST_CHECK(filter.apply((libtorrent::peer_class_type_filter::socket_type_t)i , 0xffffffff) == 0xffffffff); } filter.disallow((libtorrent::peer_class_type_filter::socket_type_t)0, 0); TEST_CHECK(filter.apply((libtorrent::peer_class_type_filter::socket_type_t)0 , 0xffffffff) == 0xfffffffe); TEST_CHECK(filter.apply((libtorrent::peer_class_type_filter::socket_type_t)1 , 0xffffffff) == 0xffffffff); filter.allow((libtorrent::peer_class_type_filter::socket_type_t)0, 0); TEST_CHECK(filter.apply((libtorrent::peer_class_type_filter::socket_type_t)0 , 0xffffffff) == 0xffffffff); TEST_CHECK(filter.apply((libtorrent::peer_class_type_filter::socket_type_t)0, 0) == 0); filter.add((libtorrent::peer_class_type_filter::socket_type_t)0, 0); TEST_CHECK(filter.apply((libtorrent::peer_class_type_filter::socket_type_t)0, 0) == 1); filter.remove((libtorrent::peer_class_type_filter::socket_type_t)0, 0); TEST_CHECK(filter.apply((libtorrent::peer_class_type_filter::socket_type_t)0, 0) == 0); pool.decref(id2); pool.decref(id1); TEST_CHECK(pool.at(id2) == NULL); TEST_CHECK(pool.at(id1) == NULL); } TORRENT_TEST(session_peer_class_filter) { using namespace libtorrent; session ses; peer_class_t my_class = ses.create_peer_class("200.1.x.x IP range"); ip_filter f; f.add_rule(address_v4::from_string("200.1.1.0") , address_v4::from_string("200.1.255.255") , 1 << my_class); ses.set_peer_class_filter(f); #if TORRENT_USE_IPV6 TEST_CHECK(boost::get<0>(ses.get_peer_class_filter().export_filter()) == boost::get<0>(f.export_filter())); #else TEST_CHECK(ses.get_peer_class_filter().export_filter() == f.export_filter()); #endif } TORRENT_TEST(session_peer_class_type_filter) { using namespace libtorrent; session ses; peer_class_t my_class = ses.create_peer_class("all utp sockets"); peer_class_type_filter f; f.add(peer_class_type_filter::utp_socket, my_class); f.disallow(peer_class_type_filter::utp_socket, session::global_peer_class_id); ses.set_peer_class_type_filter(f); TEST_CHECK(ses.get_peer_class_type_filter() == f); } libtorrent-rasterbar-1.1.13/test/test_peer_list.cpp000066400000000000000000000720571351156116000224660ustar00rootroot00000000000000/* Copyright (c) 2013, 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/peer_list.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/torrent_peer_allocator.hpp" #include "libtorrent/peer_connection_interface.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/ip_voter.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/random.hpp" #include "libtorrent/socket_io.hpp" #include "test.hpp" #include "setup_transfer.hpp" #include #include using namespace libtorrent; struct mock_torrent; struct mock_peer_connection : peer_connection_interface , boost::enable_shared_from_this { mock_peer_connection(mock_torrent* tor, bool out, tcp::endpoint const& remote) : m_choked(false) , m_outgoing(out) , m_tp(NULL) , m_remote(remote) , m_local(ep("127.0.0.1", 8080)) , m_disconnect_called(false) , m_torrent(*tor) { for (int i = 0; i < 20; ++i) m_id[i] = rand(); } virtual ~mock_peer_connection() {} #if !defined TORRENT_DISABLE_LOGGING virtual void peer_log(peer_log_alert::direction_t dir, char const* event , char const* fmt, ...) const { va_list v; va_start(v, fmt); vprintf(fmt, v); va_end(v); } #endif bool was_disconnected() const { return m_disconnect_called; } void set_local_ep(tcp::endpoint const& ep) { m_local = ep; } libtorrent::stat m_stat; bool m_choked; bool m_outgoing; torrent_peer* m_tp; tcp::endpoint m_remote; tcp::endpoint m_local; peer_id m_id; bool m_disconnect_called; mock_torrent& m_torrent; virtual void get_peer_info(peer_info& p) const {} virtual tcp::endpoint const& remote() const { return m_remote; } virtual tcp::endpoint local_endpoint() const { return m_local; } virtual void disconnect(error_code const& ec , operation_t op, int error = 0); virtual peer_id const& pid() const { return m_id; } virtual void set_holepunch_mode() {} virtual torrent_peer* peer_info_struct() const { return m_tp; } virtual void set_peer_info(torrent_peer* pi) { m_tp = pi; } virtual bool is_outgoing() const { return m_outgoing; } virtual void add_stat(boost::int64_t downloaded, boost::int64_t uploaded) { m_stat.add_stat(downloaded, uploaded); } virtual bool fast_reconnect() const { return true; } virtual bool is_choked() const { return m_choked; } virtual bool failed() const { return false; } virtual libtorrent::stat const& statistics() const { return m_stat; } }; struct mock_torrent { mock_torrent(torrent_state* st) : m_p(NULL), m_state(st) {} virtual ~mock_torrent() {} bool connect_to_peer(torrent_peer* peerinfo, bool ignore_limit = false) { TORRENT_ASSERT(peerinfo->connection == NULL); if (peerinfo->connection) return false; boost::shared_ptr c = boost::make_shared(this, true, peerinfo->ip()); c->set_peer_info(peerinfo); m_connections.push_back(c); m_p->set_connection(peerinfo, c.get()); return true; } #ifndef TORRENT_DISABLE_LOGGING void debug_log(const char* fmt, ...) const { va_list v; va_start(v, fmt); vprintf(fmt, v); va_end(v); } #endif peer_list* m_p; torrent_state* m_state; std::vector > m_connections; }; void mock_peer_connection::disconnect(error_code const& ec , operation_t op, int error) { m_torrent.m_p->connection_closed(*this, 0, m_torrent.m_state); std::vector >::iterator i = std::find(m_torrent.m_connections.begin(), m_torrent.m_connections.end() , shared_from_this()); if (i != m_torrent.m_connections.end()) m_torrent.m_connections.erase(i); m_tp = 0; m_disconnect_called = true; } bool has_peer(peer_list const& p, tcp::endpoint const& ep) { std::pair its = p.find_peers(ep.address()); return its.first != its.second; } torrent_state init_state(external_ip& ext_ip) { torrent_state st; st.is_finished = false; st.is_paused = false; st.max_peerlist_size = 1000; st.allow_multiple_connections_per_ip = false; st.ip = &ext_ip; st.port = 9999; return st; } torrent_peer* add_peer(peer_list& p, torrent_state& st, tcp::endpoint const& ep) { int cc = p.num_connect_candidates(); torrent_peer* peer = p.add_peer(ep, 0, 0, &st); if (peer) { TEST_EQUAL(p.num_connect_candidates(), cc + 1); TEST_EQUAL(peer->port, ep.port()); } st.erased.clear(); return peer; } void connect_peer(peer_list& p, mock_torrent& t, torrent_state& st) { torrent_peer* tp = p.connect_one_peer(0, &st); TEST_CHECK(tp); if (!tp) return; t.connect_to_peer(tp); st.erased.clear(); TEST_CHECK(tp->connection); } static torrent_peer_allocator allocator; static external_ip ext_ip; // test multiple peers with the same IP // when disallowing it TORRENT_TEST(multiple_ips_disallowed) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); peer_list p(allocator); t.m_p = &p; TEST_EQUAL(p.num_connect_candidates(), 0); torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), 0, 0, &st); TEST_EQUAL(p.num_peers(), 1); TEST_EQUAL(p.num_connect_candidates(), 1); st.erased.clear(); torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), 0, 0, &st); TEST_EQUAL(p.num_peers(), 1); TEST_EQUAL(peer1, peer2); TEST_EQUAL(p.num_connect_candidates(), 1); st.erased.clear(); } // test multiple peers with the same IP // when allowing it TORRENT_TEST(multiple_ips_allowed) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = true; peer_list p(allocator); t.m_p = &p; torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), 0, 0, &st); TEST_EQUAL(p.num_connect_candidates(), 1); TEST_EQUAL(p.num_peers(), 1); st.erased.clear(); torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), 0, 0, &st); TEST_EQUAL(p.num_peers(), 2); TEST_CHECK(peer1 != peer2); TEST_EQUAL(p.num_connect_candidates(), 2); st.erased.clear(); } // test adding two peers with the same IP, but different ports, to // make sure they can be connected at the same time // with allow_multiple_connections_per_ip enabled TORRENT_TEST(multiple_ips_allowed2) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = true; peer_list p(allocator); t.m_p = &p; torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), 0, 0, &st); TEST_EQUAL(p.num_connect_candidates(), 1); st.erased.clear(); TEST_EQUAL(p.num_peers(), 1); torrent_peer* tp = p.connect_one_peer(0, &st); TEST_CHECK(tp); t.connect_to_peer(tp); st.erased.clear(); // we only have one peer, we can't // connect another one tp = p.connect_one_peer(0, &st); TEST_CHECK(tp == NULL); st.erased.clear(); torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), 0, 0, &st); TEST_EQUAL(p.num_peers(), 2); TEST_CHECK(peer1 != peer2); TEST_EQUAL(p.num_connect_candidates(), 1); st.erased.clear(); tp = p.connect_one_peer(0, &st); TEST_CHECK(tp); t.connect_to_peer(tp); TEST_EQUAL(p.num_connect_candidates(), 0); st.erased.clear(); } // test adding two peers with the same IP, but different ports, to // make sure they can not be connected at the same time // with allow_multiple_connections_per_ip disabled TORRENT_TEST(multiple_ips_disallowed2) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), 0, 0, &st); TEST_EQUAL(p.num_connect_candidates(), 1); TEST_EQUAL(peer1->port, 3000); st.erased.clear(); TEST_EQUAL(p.num_peers(), 1); torrent_peer* tp = p.connect_one_peer(0, &st); TEST_CHECK(tp); t.connect_to_peer(tp); st.erased.clear(); // we only have one peer, we can't // connect another one tp = p.connect_one_peer(0, &st); TEST_CHECK(tp == NULL); st.erased.clear(); torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), 0, 0, &st); TEST_EQUAL(p.num_peers(), 1); TEST_EQUAL(peer2->port, 9020); TEST_CHECK(peer1 == peer2); TEST_EQUAL(p.num_connect_candidates(), 0); st.erased.clear(); } // test incoming connection // and update_peer_port TORRENT_TEST(update_peer_port) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; TEST_EQUAL(p.num_connect_candidates(), 0); boost::shared_ptr c = boost::make_shared(&t, true, ep("10.0.0.1", 8080)); p.new_connection(*c, 0, &st); TEST_EQUAL(p.num_connect_candidates(), 0); TEST_EQUAL(p.num_peers(), 1); st.erased.clear(); p.update_peer_port(4000, c->peer_info_struct(), peer_info::incoming, &st); TEST_EQUAL(p.num_connect_candidates(), 0); TEST_EQUAL(p.num_peers(), 1); TEST_EQUAL(c->peer_info_struct()->port, 4000); st.erased.clear(); } // test incoming connection // and update_peer_port, causing collission TORRENT_TEST(update_peer_port_collide) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = true; peer_list p(allocator); t.m_p = &p; torrent_peer* peer2 = p.add_peer(ep("10.0.0.1", 4000), 0, 0, &st); TEST_CHECK(peer2); TEST_EQUAL(p.num_connect_candidates(), 1); boost::shared_ptr c = boost::make_shared(&t, true, ep("10.0.0.1", 8080)); p.new_connection(*c, 0, &st); TEST_EQUAL(p.num_connect_candidates(), 1); // at this point we have two peers, because we think they have different // ports TEST_EQUAL(p.num_peers(), 2); st.erased.clear(); // this peer will end up having the same port as the existing peer in the list p.update_peer_port(4000, c->peer_info_struct(), peer_info::incoming, &st); TEST_EQUAL(p.num_connect_candidates(), 0); // the expected behavior is to replace that one TEST_EQUAL(p.num_peers(), 1); TEST_EQUAL(c->peer_info_struct()->port, 4000); st.erased.clear(); } // test ip filter TORRENT_TEST(ip_filter) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; // add peer 1 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.2", 3000)); torrent_peer* peer2 = add_peer(p, st, ep("11.0.0.2", 9020)); TEST_CHECK(peer1 != peer2); connect_peer(p, t, st); connect_peer(p, t, st); boost::shared_ptr con1 = static_cast(peer1->connection)->shared_from_this(); TEST_EQUAL(con1->was_disconnected(), false); boost::shared_ptr con2 = static_cast(peer2->connection)->shared_from_this(); TEST_EQUAL(con2->was_disconnected(), false); // now, filter one of the IPs and make sure the peer is removed ip_filter filter; filter.add_rule(address_v4::from_string("11.0.0.0"), address_v4::from_string("255.255.255.255"), 1); std::vector
banned; p.apply_ip_filter(filter, &st, banned); // we just erased a peer, because it was filtered by the ip filter TEST_EQUAL(st.erased.size(), 1); TEST_EQUAL(p.num_connect_candidates(), 0); TEST_EQUAL(p.num_peers(), 1); TEST_EQUAL(banned.size(), 1); TEST_EQUAL(banned[0], address_v4::from_string("11.0.0.2")); TEST_EQUAL(con2->was_disconnected(), true); TEST_EQUAL(con1->was_disconnected(), false); } // test port filter TORRENT_TEST(port_filter) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; // add peer 1 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.2", 3000)); torrent_peer* peer2 = add_peer(p, st, ep("11.0.0.2", 9020)); TEST_CHECK(peer1 != peer2); connect_peer(p, t, st); connect_peer(p, t, st); boost::shared_ptr con1 = static_cast(peer1->connection)->shared_from_this(); TEST_EQUAL(con1->was_disconnected(), false); boost::shared_ptr con2 = static_cast(peer2->connection)->shared_from_this(); TEST_EQUAL(con2->was_disconnected(), false); // now, filter one of the IPs and make sure the peer is removed port_filter filter; filter.add_rule(9000, 10000, 1); std::vector
banned; p.apply_port_filter(filter, &st, banned); // we just erased a peer, because it was filtered by the ip filter TEST_EQUAL(st.erased.size(), 1); TEST_EQUAL(p.num_connect_candidates(), 0); TEST_EQUAL(p.num_peers(), 1); TEST_EQUAL(banned.size(), 1); TEST_EQUAL(banned[0], address_v4::from_string("11.0.0.2")); TEST_EQUAL(con2->was_disconnected(), true); TEST_EQUAL(con1->was_disconnected(), false); } // test banning peers TORRENT_TEST(ban_peers) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.1", 4000)); TEST_EQUAL(p.num_connect_candidates(), 1); boost::shared_ptr c = boost::make_shared(&t, true, ep("10.0.0.1", 8080)); p.new_connection(*c, 0, &st); TEST_EQUAL(p.num_connect_candidates(), 0); TEST_EQUAL(p.num_peers(), 1); st.erased.clear(); // now, ban the peer bool ok = p.ban_peer(c->peer_info_struct()); TEST_EQUAL(ok, true); TEST_EQUAL(peer1->banned, true); // we still have it in the list TEST_EQUAL(p.num_peers(), 1); // it's just not a connect candidate, nor allowed to receive incoming connections TEST_EQUAL(p.num_connect_candidates(), 0); p.connection_closed(*c, 0, &st); TEST_EQUAL(p.num_peers(), 1); TEST_EQUAL(p.num_connect_candidates(), 0); st.erased.clear(); c = boost::make_shared(&t, true, ep("10.0.0.1", 8080)); ok = p.new_connection(*c, 0, &st); // since it's banned, we should not allow this incoming connection TEST_EQUAL(ok, false); TEST_EQUAL(p.num_connect_candidates(), 0); st.erased.clear(); } // test erase_peers when we fill up the peer list TORRENT_TEST(erase_peers) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.max_peerlist_size = 100; st.allow_multiple_connections_per_ip = true; peer_list p(allocator); t.m_p = &p; for (int i = 0; i < 100; ++i) { TEST_EQUAL(st.erased.size(), 0); tcp::endpoint ep = rand_tcp_ep(); torrent_peer* peer = add_peer(p, st, ep); TEST_CHECK(peer); if (peer == NULL || st.erased.size() > 0) { fprintf(stderr, "unexpected rejection of peer: %s | %d in list. " "added peer %p, erased %d peers\n" , print_endpoint(ep).c_str(), p.num_peers(), static_cast(peer) , int(st.erased.size())); } } TEST_EQUAL(p.num_peers(), 100); // trigger the eviction of one peer torrent_peer* peer = p.add_peer(rand_tcp_ep(), 0, 0, &st); // we either removed an existing peer, or rejected this one // either is valid behavior when the list is full TEST_CHECK(st.erased.size() == 1 || peer == NULL); } // test set_ip_filter TORRENT_TEST(set_ip_filter) { torrent_state st = init_state(ext_ip); std::vector
banned; mock_torrent t(&st); peer_list p(allocator); t.m_p = &p; for (int i = 0; i < 100; ++i) { p.add_peer(tcp::endpoint( address_v4((10 << 24) + ((i + 10) << 16)), 353), 0, 0, &st); TEST_EQUAL(st.erased.size(), 0); st.erased.clear(); } TEST_EQUAL(p.num_peers(), 100); TEST_EQUAL(p.num_connect_candidates(), 100); // trigger the removal of one peer ip_filter filter; filter.add_rule(address_v4::from_string("10.13.0.0") , address_v4::from_string("10.13.255.255"), ip_filter::blocked); p.apply_ip_filter(filter, &st, banned); TEST_EQUAL(st.erased.size(), 1); TEST_EQUAL(st.erased[0]->address(), address_v4::from_string("10.13.0.0")); TEST_EQUAL(p.num_peers(), 99); TEST_EQUAL(p.num_connect_candidates(), 99); } // test set_port_filter TORRENT_TEST(set_port_filter) { torrent_state st = init_state(ext_ip); std::vector
banned; mock_torrent t(&st); peer_list p(allocator); t.m_p = &p; for (int i = 0; i < 100; ++i) { p.add_peer(tcp::endpoint( address_v4((10 << 24) + ((i + 10) << 16)), i + 10), 0, 0, &st); TEST_EQUAL(st.erased.size(), 0); st.erased.clear(); } TEST_EQUAL(p.num_peers(), 100); TEST_EQUAL(p.num_connect_candidates(), 100); // trigger the removal of one peer port_filter filter; filter.add_rule(13, 13, port_filter::blocked); p.apply_port_filter(filter, &st, banned); TEST_EQUAL(st.erased.size(), 1); TEST_EQUAL(st.erased[0]->address(), address_v4::from_string("10.13.0.0")); TEST_EQUAL(st.erased[0]->port, 13); TEST_EQUAL(p.num_peers(), 99); TEST_EQUAL(p.num_connect_candidates(), 99); } // test set_max_failcount TORRENT_TEST(set_max_failcount) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); peer_list p(allocator); t.m_p = &p; for (int i = 0; i < 100; ++i) { torrent_peer* peer = p.add_peer(tcp::endpoint( address_v4((10 << 24) + ((i + 10) << 16)), i + 10), 0, 0, &st); TEST_EQUAL(st.erased.size(), 0); st.erased.clear(); // every other peer has a failcount of 1 if (i % 2) p.inc_failcount(peer); } TEST_EQUAL(p.num_peers(), 100); TEST_EQUAL(p.num_connect_candidates(), 100); // set the max failcount to 1 and observe how half the peers no longer // are connect candidates st.max_failcount = 1; p.set_max_failcount(&st); TEST_EQUAL(p.num_connect_candidates(), 50); TEST_EQUAL(p.num_peers(), 100); } // test set_seed TORRENT_TEST(set_seed) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); peer_list p(allocator); t.m_p = &p; for (int i = 0; i < 100; ++i) { torrent_peer* peer = p.add_peer(tcp::endpoint( address_v4((10 << 24) + ((i + 10) << 16)), i + 10), 0, 0, &st); TEST_EQUAL(st.erased.size(), 0); st.erased.clear(); // make every other peer a seed if (i % 2) p.set_seed(peer, true); } TEST_EQUAL(p.num_peers(), 100); TEST_EQUAL(p.num_connect_candidates(), 100); // now, the torrent completes and we're no longer interested in // connecting to seeds. Make sure half the peers are no longer // considered connect candidates st.is_finished = true; // this will make the peer_list recalculate the connect candidates std::vector peers; p.connect_one_peer(1, &st); TEST_EQUAL(p.num_connect_candidates(), 50); TEST_EQUAL(p.num_peers(), 100); } // test has_peer TORRENT_TEST(has_peer) { torrent_state st = init_state(ext_ip); std::vector
banned; mock_torrent t(&st); peer_list p(allocator); t.m_p = &p; torrent_peer* peer1 = add_peer(p, st, ep("10.10.0.1", 10)); torrent_peer* peer2 = add_peer(p, st, ep("10.10.0.2", 11)); TEST_EQUAL(p.num_peers(), 2); TEST_EQUAL(p.num_connect_candidates(), 2); TEST_EQUAL(p.has_peer(peer1), true); TEST_EQUAL(p.has_peer(peer2), true); ip_filter filter; filter.add_rule(address_v4::from_string("10.10.0.1") , address_v4::from_string("10.10.0.1"), ip_filter::blocked); p.apply_ip_filter(filter, &st, banned); TEST_EQUAL(st.erased.size(), 1); st.erased.clear(); TEST_EQUAL(p.num_peers(), 1); TEST_EQUAL(p.num_connect_candidates(), 1); TEST_EQUAL(p.has_peer(peer1), false); TEST_EQUAL(p.has_peer(peer2), true); } // test connect_candidates torrent_finish TORRENT_TEST(connect_candidates_finish) { torrent_state st = init_state(ext_ip); std::vector
banned; mock_torrent t(&st); peer_list p(allocator); t.m_p = &p; torrent_peer* peer1 = add_peer(p, st, ep("10.10.0.1", 10)); TEST_CHECK(peer1); p.set_seed(peer1, true); torrent_peer* peer2 = add_peer(p, st, ep("10.10.0.2", 11)); TEST_CHECK(peer2); p.set_seed(peer2, true); torrent_peer* peer3 = add_peer(p, st, ep("10.10.0.3", 11)); TEST_CHECK(peer3); p.set_seed(peer3, true); torrent_peer* peer4 = add_peer(p, st, ep("10.10.0.4", 11)); TEST_CHECK(peer4); torrent_peer* peer5 = add_peer(p, st, ep("10.10.0.5", 11)); TEST_CHECK(peer5); TEST_EQUAL(p.num_peers(), 5); TEST_EQUAL(p.num_connect_candidates(), 5); st.is_finished = true; // we're finished downloading now, only the non-seeds are // connect candidates // connect to one of them connect_peer(p, t, st); TEST_EQUAL(p.num_peers(), 5); // and there should be one left TEST_EQUAL(p.num_connect_candidates(), 1); } // test self-connection TORRENT_TEST(self_connection) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; // add and connect peer torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 3000)); connect_peer(p, t, st); boost::shared_ptr con_out = static_cast(peer->connection)->shared_from_this(); con_out->set_local_ep(ep("10.0.0.2", 8080)); boost::shared_ptr con_in = boost::make_shared(&t, false, ep("10.0.0.2", 8080)); con_in->set_local_ep(ep("10.0.0.2", 3000)); p.new_connection(*con_in, 0, &st); // from the peer_list's point of view, this looks like we made one // outgoing connection and received an incoming one. Since they share // the exact same endpoints (IP ports) but just swapped source and // destination, the peer list is supposed to figure out that we connected // to ourself and disconnect it TEST_EQUAL(con_out->was_disconnected(), true); TEST_EQUAL(con_in->was_disconnected(), true); } // test double connection (both incoming) TORRENT_TEST(double_connection) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; // we are 10.0.0.1 and the other peer is 10.0.0.2 // first incoming connection boost::shared_ptr con1 = boost::make_shared(&t, false, ep("10.0.0.2", 7528)); con1->set_local_ep(ep("10.0.0.1", 8080)); p.new_connection(*con1, 0, &st); // and the incoming connection boost::shared_ptr con2 = boost::make_shared(&t, false, ep("10.0.0.2", 3561)); con2->set_local_ep(ep("10.0.0.1", 8080)); p.new_connection(*con2, 0, &st); // the second incoming connection should be closed TEST_EQUAL(con1->was_disconnected(), false); TEST_EQUAL(con2->was_disconnected(), true); } // test double connection (we loose) TORRENT_TEST(double_connection_loose) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; // we are 10.0.0.1 and the other peer is 10.0.0.2 // our outgoing connection torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 3000)); connect_peer(p, t, st); boost::shared_ptr con_out = static_cast(peer->connection)->shared_from_this(); con_out->set_local_ep(ep("10.0.0.1", 3163)); // and the incoming connection boost::shared_ptr con_in = boost::make_shared(&t, false, ep("10.0.0.2", 3561)); con_in->set_local_ep(ep("10.0.0.1", 8080)); p.new_connection(*con_in, 0, &st); // the rules are documented in peer_list.cpp TEST_EQUAL(con_out->was_disconnected(), true); TEST_EQUAL(con_in->was_disconnected(), false); } // test double connection with identical ports (random) TORRENT_TEST(double_connection_random) { int in = 0; int out = 0; for (int i = 0; i < 30; ++i) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; // we are 10.0.0.1 and the other peer is 10.0.0.2 // our outgoing connection torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 3000)); connect_peer(p, t, st); boost::shared_ptr con_out = static_cast(peer->connection)->shared_from_this(); con_out->set_local_ep(ep("10.0.0.1", 3000)); // and the incoming connection boost::shared_ptr con_in = boost::make_shared(&t, false, ep("10.0.0.2", 3000)); con_in->set_local_ep(ep("10.0.0.1", 3000)); p.new_connection(*con_in, 0, &st); // the rules are documented in peer_list.cpp out += con_out->was_disconnected(); in += con_in->was_disconnected(); } // we should have gone different ways randomly TEST_CHECK(out > 0); TEST_CHECK(in > 0); } // test double connection (we win) TORRENT_TEST(double_connection_win) { torrent_state st = init_state(ext_ip); mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; // we are 10.0.0.1 and the other peer is 10.0.0.2 // our outgoing connection torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 8080)); connect_peer(p, t, st); boost::shared_ptr con_out = static_cast(peer->connection)->shared_from_this(); con_out->set_local_ep(ep("10.0.0.1", 3163)); //and the incoming connection boost::shared_ptr con_in = boost::make_shared(&t, false, ep("10.0.0.2", 3561)); con_in->set_local_ep(ep("10.0.0.1", 3000)); p.new_connection(*con_in, 0, &st); // the rules are documented in peer_list.cpp TEST_EQUAL(con_out->was_disconnected(), false); TEST_EQUAL(con_in->was_disconnected(), true); } // test incoming connection when we are at the list size limit TORRENT_TEST(incoming_size_limit) { torrent_state st = init_state(ext_ip); st.max_peerlist_size = 5; mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.1", 8080)); TEST_CHECK(peer1); TEST_EQUAL(p.num_peers(), 1); torrent_peer* peer2 = add_peer(p, st, ep("10.0.0.2", 8080)); TEST_CHECK(peer2); TEST_EQUAL(p.num_peers(), 2); torrent_peer* peer3 = add_peer(p, st, ep("10.0.0.3", 8080)); TEST_CHECK(peer3); TEST_EQUAL(p.num_peers(), 3); torrent_peer* peer4 = add_peer(p, st, ep("10.0.0.4", 8080)); TEST_CHECK(peer4); TEST_EQUAL(p.num_peers(), 4); torrent_peer* peer5 = add_peer(p, st, ep("10.0.0.5", 8080)); TEST_CHECK(peer5); TEST_EQUAL(p.num_peers(), 5); boost::shared_ptr con_in = boost::make_shared(&t, false, ep("10.0.1.2", 3561)); con_in->set_local_ep(ep("10.0.2.1", 3000)); // since we're already at 5 peers in the peer list, this call should // erase one of the existing ones. p.new_connection(*con_in, 0, &st); TEST_EQUAL(con_in->was_disconnected(), false); TEST_EQUAL(p.num_peers(), 5); // one of the previous ones should have been removed TEST_EQUAL(has_peer(p, ep("10.0.0.1", 8080)) + has_peer(p, ep("10.0.0.2", 8080)) + has_peer(p, ep("10.0.0.3", 8080)) + has_peer(p, ep("10.0.0.4", 8080)) + has_peer(p, ep("10.0.0.5", 8080)) , 4); } // test new peer when we are at the list size limit TORRENT_TEST(new_peer_size_limit) { torrent_state st = init_state(ext_ip); st.max_peerlist_size = 5; mock_torrent t(&st); st.allow_multiple_connections_per_ip = false; peer_list p(allocator); t.m_p = &p; torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.1", 8080)); TEST_CHECK(peer1); TEST_EQUAL(p.num_peers(), 1); torrent_peer* peer2 = add_peer(p, st, ep("10.0.0.2", 8080)); TEST_CHECK(peer2); TEST_EQUAL(p.num_peers(), 2); torrent_peer* peer3 = add_peer(p, st, ep("10.0.0.3", 8080)); TEST_CHECK(peer3); TEST_EQUAL(p.num_peers(), 3); torrent_peer* peer4 = add_peer(p, st, ep("10.0.0.4", 8080)); TEST_CHECK(peer4); TEST_EQUAL(p.num_peers(), 4); torrent_peer* peer5 = add_peer(p, st, ep("10.0.0.5", 8080)); TEST_CHECK(peer5); TEST_EQUAL(p.num_peers(), 5); torrent_peer* peer6 = p.add_peer(ep("10.0.0.6", 8080), 0, 0, &st); TEST_CHECK(peer6 == NULL); TEST_EQUAL(p.num_peers(), 5); // one of the connection should have been removed TEST_EQUAL(has_peer(p, ep("10.0.0.1", 8080)) + has_peer(p, ep("10.0.0.2", 8080)) + has_peer(p, ep("10.0.0.3", 8080)) + has_peer(p, ep("10.0.0.4", 8080)) + has_peer(p, ep("10.0.0.5", 8080)) + has_peer(p, ep("10.0.0.6", 8080)) , 5); } // TODO: test erasing peers // TODO: test update_peer_port with allow_multiple_connections_per_ip and without // TODO: test add i2p peers // TODO: test allow_i2p_mixed // TODO: test insert_peer failing with all error conditions // TODO: test IPv6 // TODO: test connect_to_peer() failing // TODO: test connection_closed // TODO: connect candidates recalculation when incrementing failcount libtorrent-rasterbar-1.1.13/test/test_peer_priority.cpp000066400000000000000000000102371351156116000233640ustar00rootroot00000000000000/* Copyright (c) 2012, 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/peer_list.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/broadcast_socket.hpp" // for supports_ipv6() #include #include "test.hpp" using namespace libtorrent; boost::uint32_t hash_buffer(char const* buf, int len) { boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; crc.process_block(buf, buf + len); return crc.checksum(); } TORRENT_TEST(peer_priority) { // when the IP is the same, we hash the ports, sorted boost::uint32_t p = peer_priority( tcp::endpoint(address::from_string("230.12.123.3"), 0x4d2) , tcp::endpoint(address::from_string("230.12.123.3"), 0x12c)); TEST_EQUAL(p, hash_buffer("\x01\x2c\x04\xd2", 4)); // when we're in the same /24, we just hash the IPs p = peer_priority( tcp::endpoint(address::from_string("230.12.123.1"), 0x4d2) , tcp::endpoint(address::from_string("230.12.123.3"), 0x12c)); TEST_EQUAL(p, hash_buffer("\xe6\x0c\x7b\x01\xe6\x0c\x7b\x03", 8)); // when we're in the same /16, we just hash the IPs masked by // 0xffffff55 p = peer_priority( tcp::endpoint(address::from_string("230.12.23.1"), 0x4d2) , tcp::endpoint(address::from_string("230.12.123.3"), 0x12c)); TEST_EQUAL(p, hash_buffer("\xe6\x0c\x17\x01\xe6\x0c\x7b\x01", 8)); // when we're in different /16, we just hash the IPs masked by // 0xffff5555 p = peer_priority( tcp::endpoint(address::from_string("230.120.23.1"), 0x4d2) , tcp::endpoint(address::from_string("230.12.123.3"), 0x12c)); TEST_EQUAL(p, hash_buffer("\xe6\x0c\x51\x01\xe6\x78\x15\x01", 8)); // test vectors from BEP 40 TEST_EQUAL(peer_priority( tcp::endpoint(address::from_string("123.213.32.10"), 0) , tcp::endpoint(address::from_string("98.76.54.32"), 0)) , 0xec2d7224); TEST_EQUAL(peer_priority( tcp::endpoint(address::from_string("123.213.32.10"), 0) , tcp::endpoint(address::from_string("123.213.32.234"), 0)) , 0x99568189); if (supports_ipv6()) { // IPv6 has a twice as wide mask, and we only care about the top 64 bits // when the IPs are the same, just hash the ports p = peer_priority( tcp::endpoint(address::from_string("ffff:ffff:ffff:ffff::1"), 0x4d2) , tcp::endpoint(address::from_string("ffff:ffff:ffff:ffff::1"), 0x12c)); TEST_EQUAL(p, hash_buffer("\x01\x2c\x04\xd2", 4)); // these IPs don't belong to the same /32, so apply the full mask // 0xffffffff55555555 p = peer_priority( tcp::endpoint(address::from_string("ffff:ffff:ffff:ffff::1"), 0x4d2) , tcp::endpoint(address::from_string("ffff:0fff:ffff:ffff::1"), 0x12c)); TEST_EQUAL(p, hash_buffer( "\xff\xff\x0f\xff\x55\x55\x55\x55\x00\x00\x00\x00\x00\x00\x00\x01" "\xff\xff\xff\xff\x55\x55\x55\x55\x00\x00\x00\x00\x00\x00\x00\x01", 32)); } } libtorrent-rasterbar-1.1.13/test/test_pex.cpp000066400000000000000000000123671351156116000212720ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #ifndef TORRENT_DISABLE_EXTENSIONS #include "libtorrent/session.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/extensions/ut_pex.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/torrent_status.hpp" #include #include "setup_transfer.hpp" #include "settings.hpp" #include void test_pex() { using namespace libtorrent; namespace lt = libtorrent; // these are declared before the session objects // so that they are destructed last. This enables // the sessions to destruct in parallel session_proxy p1; session_proxy p2; session_proxy p3; // this is to avoid everything finish from a single peer // immediately. To make the swarm actually connect all // three peers before finishing. settings_pack pack = settings(); pack.set_int(settings_pack::download_rate_limit, 2000); pack.set_int(settings_pack::upload_rate_limit, 2000); pack.set_int(settings_pack::max_retry_port_bind, 800); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48200"); pack.set_bool(settings_pack::enable_dht, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_natpmp, false); #ifndef TORRENT_NO_DEPRECATE pack.set_bool(settings_pack::rate_limit_utp, true); #endif pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_forced); pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_forced); lt::session ses1(pack); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:49200"); lt::session ses3(pack); // make the peer connecting the two worthless to transfer // data, to force peer 3 to connect directly to peer 1 through pex pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:50200"); lt::session ses2(pack); ses1.add_extension(create_ut_pex_plugin); ses2.add_extension(create_ut_pex_plugin); torrent_handle tor1; torrent_handle tor2; torrent_handle tor3; boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true, false, false, "_pex"); ses2.apply_settings(pack); test_sleep(100); // in this test, ses1 is a seed, ses2 is connected to ses1 and ses3. // the expected behavior is that ses2 will introduce ses1 and ses3 to each other error_code ec; tor2.connect_peer(tcp::endpoint(address::from_string("127.0.0.1", ec), ses1.listen_port())); tor2.connect_peer(tcp::endpoint(address::from_string("127.0.0.1", ec), ses3.listen_port())); torrent_status st1; torrent_status st2; torrent_status st3; for (int i = 0; i < 610; ++i) { print_alerts(ses1, "ses1"); print_alerts(ses2, "ses2"); print_alerts(ses3, "ses3"); st1 = tor1.status(); st2 = tor2.status(); st3 = tor3.status(); print_ses_rate(i / 10.f, &st1, &st2, &st3); // this is the success condition if (st1.num_peers == 2 && st2.num_peers == 2 && st3.num_peers == 2) break; // this suggests that we failed. If session 3 completes without // actually connecting to session 1, everything was transferred // through session 2 if (st3.state == torrent_status::seeding) break; test_sleep(100); } TEST_CHECK(st1.num_peers == 2 && st2.num_peers == 2 && st3.num_peers == 2) if (!tor2.status().is_seeding && tor3.status().is_seeding) std::cerr << "done\n"; // this allows shutting down the sessions in parallel p1 = ses1.abort(); p2 = ses2.abort(); p3 = ses3.abort(); } #endif // TORRENT_DISABLE_EXTENSIONS TORRENT_TEST(pex) { #ifndef TORRENT_DISABLE_EXTENSIONS using namespace libtorrent; // in case the previous run was terminated error_code ec; remove_all("tmp1_pex", ec); remove_all("tmp2_pex", ec); remove_all("tmp3_pex", ec); test_pex(); remove_all("tmp1_pex", ec); remove_all("tmp2_pex", ec); remove_all("tmp3_pex", ec); #endif // TORRENT_DISABLE_EXTENSIONS } libtorrent-rasterbar-1.1.13/test/test_piece_picker.cpp000066400000000000000000002021451351156116000231130ustar00rootroot00000000000000/* Copyright (c) 2008, 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/piece_picker.hpp" #include "libtorrent/torrent_peer.hpp" #include "libtorrent/bitfield.hpp" #include "libtorrent/performance_counters.hpp" #include "libtorrent/random.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "test.hpp" using namespace libtorrent; const int blocks_per_piece = 4; bitfield string2vec(char const* have_str) { const int num_pieces = strlen(have_str); bitfield have(num_pieces, false); for (int i = 0; i < num_pieces; ++i) if (have_str[i] != ' ') have.set_bit(i); return have; } ipv4_peer* tmp_peer = 0; tcp::endpoint endp; ipv4_peer tmp0(endp, false, 0); ipv4_peer tmp1(endp, false, 0); ipv4_peer tmp2(endp, false, 0); ipv4_peer tmp3(endp, false, 0); ipv4_peer tmp4(endp, false, 0); ipv4_peer tmp5(endp, false, 0); ipv4_peer tmp6(endp, false, 0); ipv4_peer tmp7(endp, false, 0); ipv4_peer tmp8(endp, false, 0); ipv4_peer tmp9(endp, false, 0); ipv4_peer peer_struct(endp, true, 0); const std::vector empty_vector; // availability is a string where each character is the // availability of that piece, '1', '2' etc. // have_str is a string where each character represents a // piece, ' ' means we don't have the piece and any other // character means we have it boost::shared_ptr setup_picker( char const* availability , char const* have_str , char const* priority , char const* partial) { const int num_pieces = strlen(availability); TORRENT_ASSERT(int(strlen(have_str)) == num_pieces); boost::shared_ptr p(new piece_picker); p->init(blocks_per_piece, blocks_per_piece, num_pieces); for (int i = 0; i < num_pieces; ++i) { const int avail = availability[i] - '0'; assert(avail >= 0); static const torrent_peer* peers[10] = { &tmp0, &tmp1, &tmp2 , &tmp3, &tmp4, &tmp5, &tmp6, &tmp7, &tmp8, &tmp9 }; TORRENT_ASSERT(avail < 10); for (int j = 0; j < avail; ++j) p->inc_refcount(i, peers[j]); } bitfield have = string2vec(have_str); for (int i = 0; i < num_pieces; ++i) { if (partial[i] == 0) break; if (partial[i] == ' ') continue; int blocks = 0; if (partial[i] >= '0' && partial[i] <= '9') blocks = partial[i] - '0'; else blocks = partial[i] - 'a' + 10; int counter = 0; for (int j = 0; j < 4; ++j) { TEST_CHECK(!p->is_finished(piece_block(i, j))); if ((blocks & (1 << j)) == 0) continue; ++counter; bool ret = p->mark_as_downloading(piece_block(i, j), tmp_peer); TEST_CHECK(ret == true); TEST_CHECK(p->is_requested(piece_block(i, j)) == bool(blocks & (1 << j))); p->mark_as_writing(piece_block(i, j), tmp_peer); TEST_CHECK(!p->is_finished(piece_block(i, j))); // trying to mark a block as requested after it has been completed // should fail (return false) ret = p->mark_as_downloading(piece_block(i, j), tmp_peer); TEST_CHECK(ret == false); p->mark_as_finished(piece_block(i, j), tmp_peer); TEST_CHECK(p->is_downloaded(piece_block(i, j)) == bool(blocks & (1 << j))); TEST_CHECK(p->is_finished(piece_block(i, j)) == bool(blocks & (1 << j))); } piece_picker::downloading_piece st; p->piece_info(i, st); TEST_EQUAL(int(st.writing), 0); TEST_EQUAL(int(st.requested), 0); TEST_EQUAL(int(st.index), i); TEST_EQUAL(st.finished, counter); TEST_EQUAL(st.finished + st.requested + st.writing, counter); TEST_CHECK(p->is_piece_finished(i) == (counter == 4)); } for (int i = 0; i < num_pieces; ++i) { if (priority[i] == 0) break; const int prio = priority[i] - '0'; assert(prio >= 0); p->set_piece_priority(i, prio); TEST_CHECK(p->piece_priority(i) == prio); } for (int i = 0; i < num_pieces; ++i) { if (!have[i]) continue; p->we_have(i); for (int j = 0; j < blocks_per_piece; ++j) TEST_CHECK(p->is_finished(piece_block(i, j))); } std::vector availability_vec; p->get_availability(availability_vec); for (int i = 0; i < num_pieces; ++i) { const int avail = availability[i] - '0'; assert(avail >= 0); TEST_CHECK(avail == availability_vec[i]); } #if TORRENT_USE_INVARIANT_CHECKS p->check_invariant(); #endif return p; } bool verify_pick(boost::shared_ptr p , std::vector const& picked, bool allow_multi_blocks = false) { #if TORRENT_USE_INVARIANT_CHECKS p->check_invariant(); #endif if (!allow_multi_blocks) { for (std::vector::const_iterator i = picked.begin() , end(picked.end()); i != end; ++i) { if (p->num_peers(*i) > 0) return false; } } // make sure there are no duplicated std::set blocks; std::copy(picked.begin(), picked.end() , std::insert_iterator >(blocks, blocks.end())); std::cout << " verify: " << picked.size() << " " << blocks.size() << std::endl; return picked.size() == blocks.size(); } void print_availability(boost::shared_ptr const& p) { std::vector avail; p->get_availability(avail); printf("[ "); for (std::vector::iterator i = avail.begin() , end(avail.end()); i != end; ++i) { printf("%d ", *i); } printf("]\n"); } bool verify_availability(boost::shared_ptr const& p, char const* a) { std::vector avail; p->get_availability(avail); for (std::vector::iterator i = avail.begin() , end(avail.end()); i != end; ++i, ++a) { if (*a - '0' != *i) return false; } return true; } void print_pick(std::vector const& picked) { for (int i = 0; i < int(picked.size()); ++i) { std::cout << "(" << picked[i].piece_index << ", " << picked[i].block_index << ") "; } std::cout << std::endl; } void print_title(char const* name) { std::cout << "==== " << name << " ====\n"; } std::vector pick_pieces(boost::shared_ptr const& p , char const* availability , int num_blocks , int prefer_contiguous_blocks , torrent_peer* peer_struct , int options = piece_picker::rarest_first , std::vector const& suggested_pieces = empty_vector) { std::vector picked; counters pc; p->pick_pieces(string2vec(availability), picked , num_blocks, prefer_contiguous_blocks, peer_struct , options, suggested_pieces, 20, pc); print_pick(picked); TEST_CHECK(verify_pick(p, picked)); return picked; } int test_pick(boost::shared_ptr const& p , int options = piece_picker::rarest_first) { const std::vector empty_vector; std::vector picked = pick_pieces(p, "*******", 1, 0, 0 , options, empty_vector); if (picked.empty()) return -1; return picked[0].piece_index; } // TODO: 2 split this up into smaller tests (where we print_title) TORRENT_TEST(piece_picker) { tcp::endpoint endp; piece_picker::downloading_piece st; #if TORRENT_USE_ASSERTS tmp0.in_use = true; tmp1.in_use = true; tmp2.in_use = true; tmp3.in_use = true; tmp4.in_use = true; tmp5.in_use = true; tmp6.in_use = true; tmp7.in_use = true; tmp8.in_use = true; tmp9.in_use = true; peer_struct.in_use = true; #endif tmp_peer = &tmp1; std::vector picked; boost::shared_ptr p; const int options = piece_picker::rarest_first; std::pair dc; counters pc; print_title("test piece_block"); TEST_CHECK(piece_block(0, 0) != piece_block(0, 1)); TEST_CHECK(piece_block(0, 0) != piece_block(1, 0)); TEST_CHECK(!(piece_block(0, 0) != piece_block(0, 0))); TEST_CHECK(!(piece_block(0, 0) == piece_block(0, 1))); TEST_CHECK(!(piece_block(0, 0) == piece_block(1, 0))); TEST_CHECK(piece_block(0, 0) == piece_block(0, 0)); TEST_CHECK(!(piece_block(0, 1) < piece_block(0, 0))); TEST_CHECK(!(piece_block(1, 0) < piece_block(0, 0))); TEST_CHECK(piece_block(0, 0) < piece_block(0, 1)); TEST_CHECK(piece_block(0, 0) < piece_block(1, 0)); TEST_CHECK(!(piece_block(0, 0) < piece_block(0, 0))); TEST_CHECK(!(piece_block(1, 0) < piece_block(1, 0))); TEST_CHECK(!(piece_block(0, 1) < piece_block(0, 1))); // ======================================================== // test abort_download print_title("test abort_download"); p = setup_picker("1111111", " ", "7110000", ""); picked = pick_pieces(p, "*******", blocks_per_piece, 0, tmp_peer , options, empty_vector); TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); p->abort_download(piece_block(0,0), tmp_peer); picked = pick_pieces(p, "*******", blocks_per_piece, 0, tmp_peer , options, empty_vector); TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); p->mark_as_downloading(piece_block(0,0), &tmp1); picked = pick_pieces(p, "*******", blocks_per_piece, 0, tmp_peer , options, empty_vector); TEST_CHECK(p->is_requested(piece_block(0, 0)) == true); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) == picked.end()); p->abort_download(piece_block(0,0), tmp_peer); picked = pick_pieces(p, "*******", blocks_per_piece, 0, tmp_peer , options, empty_vector); TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); p->mark_as_downloading(piece_block(0,0), &tmp1); p->mark_as_downloading(piece_block(0,1), &tmp1); p->abort_download(piece_block(0,0), tmp_peer); picked = pick_pieces(p, "*******", blocks_per_piece, 0, tmp_peer , options, empty_vector); TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); p->mark_as_downloading(piece_block(0,0), &tmp1); p->mark_as_writing(piece_block(0,0), &tmp1); p->write_failed(piece_block(0,0)); picked = pick_pieces(p, "*******", blocks_per_piece, 0, tmp_peer , options, empty_vector); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(1,0)) != picked.end() || std::find(picked.begin(), picked.end(), piece_block(2,0)) != picked.end()); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) == picked.end()); p->restore_piece(0); picked = pick_pieces(p, "*******", blocks_per_piece, 0, tmp_peer , options, empty_vector); TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); p->mark_as_downloading(piece_block(0,0), &tmp1); p->mark_as_writing(piece_block(0,0), &tmp1); p->mark_as_finished(piece_block(0,0), &tmp1); p->abort_download(piece_block(0,0), tmp_peer); picked = pick_pieces(p, "*******", blocks_per_piece, 0, tmp_peer , options, empty_vector); TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) == picked.end()); // ======================================================== print_title("test abort_download"); p = setup_picker("1111111", " ", "7110000", ""); p->mark_as_downloading(piece_block(0,0), &tmp1); p->mark_as_finished(piece_block(0,1), 0); p->piece_info(0, st); TEST_EQUAL(st.requested, 1); TEST_EQUAL(st.finished, 1); p->abort_download(piece_block(0,0), tmp_peer); p->piece_info(0, st); TEST_EQUAL(st.requested, 0); TEST_EQUAL(st.finished, 1); picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0 , options, empty_vector); TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); // ======================================================== print_title("test get_downloaders"); p = setup_picker("1111111", " ", "7110000", ""); p->mark_as_downloading(piece_block(0, 2), &tmp1); p->mark_as_writing(piece_block(0, 2), &tmp1); p->abort_download(piece_block(0, 2), &tmp1); p->mark_as_downloading(piece_block(0, 2), &tmp2); p->mark_as_writing(piece_block(0, 2), &tmp2); std::vector d; p->get_downloaders(d, 0); TEST_EQUAL(d.size(), 4); TEST_CHECK(d[0] == NULL); TEST_CHECK(d[1] == NULL); TEST_CHECK(d[2] == &tmp2); TEST_CHECK(d[3] == NULL); p->mark_as_downloading(piece_block(0, 3), &tmp1); p->abort_download(piece_block(0, 3), &tmp1); p->mark_as_downloading(piece_block(0, 3), &tmp2); p->mark_as_writing(piece_block(0, 3), &tmp2); p->get_downloaders(d, 0); TEST_EQUAL(d.size(), 4); TEST_CHECK(d[0] == NULL); TEST_CHECK(d[1] == NULL); TEST_CHECK(d[2] == &tmp2); TEST_CHECK(d[3] == &tmp2); // if we ask for downloaders for a piece that's not // curently being downloaded, we get zeroes back p->get_downloaders(d, 1); TEST_EQUAL(d.size(), 4); TEST_CHECK(d[0] == NULL); TEST_CHECK(d[1] == NULL); TEST_CHECK(d[2] == NULL); TEST_CHECK(d[3] == NULL); // ======================================================== p = setup_picker("2222", " ", "", ""); for (int i = 0; i < 4; ++i) for (int k = 0; k < blocks_per_piece; ++k) p->mark_as_downloading(piece_block(i, k), &tmp1); p->mark_as_downloading(piece_block(0, 0), &tmp2); fprintf(stdout, "num_peers: %d\n", p->num_peers(piece_block(0, 0))); TEST_EQUAL(p->num_peers(piece_block(0, 0)), 2); p->abort_download(piece_block(0, 0), &tmp1); fprintf(stdout, "num_peers: %d\n", p->num_peers(piece_block(0, 0))); TEST_EQUAL(p->num_peers(piece_block(0, 0)), 1); // ======================================================== // make sure the block that is picked is from piece 1, since it // it is the piece with the lowest availability print_title("test pick lowest availability"); p = setup_picker("2223333", "* * * ", "", ""); TEST_CHECK(test_pick(p) == 1); // ======================================================== // make sure pieces with equal priority and availability // are picked at random print_title("test random pick at same priority"); std::map random_prio_pieces; for (int i = 0; i < 100; ++i) { p = setup_picker("1111112", " ", "", ""); ++random_prio_pieces[test_pick(p)]; } TEST_CHECK(random_prio_pieces.size() == 6); for (std::map::iterator i = random_prio_pieces.begin() , end(random_prio_pieces.end()); i != end; ++i) std::cout << i->first << ": " << i->second << " "; std::cout << std::endl; // ======================================================== // make sure the block that is picked is from piece 5, since it // has the highest priority among the available pieces print_title("test pick highest priority"); p = setup_picker("1111111", " ", "1111121", ""); TEST_CHECK(test_pick(p) == 5); p = setup_picker("1111111", " ", "1171121", ""); TEST_CHECK(test_pick(p) == 2); p = setup_picker("1111111", " ", "1131521", ""); TEST_CHECK(test_pick(p) == 4); // ======================================================== print_title("test reverse rarest first"); p = setup_picker("4179253", " ", "", ""); picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, &peer_struct , piece_picker::rarest_first | piece_picker::reverse, empty_vector); int expected_common_pieces[] = {3, 2, 5, 0, 6, 4, 1}; for (int i = 0; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(expected_common_pieces[i / blocks_per_piece], i % blocks_per_piece)); // piece 3 should NOT be prioritized since it's a partial, and not // reversed. Reversed partials are considered reversed p = setup_picker("1122111", " ", "3333333", " 1 "); TEST_CHECK(test_pick(p, piece_picker::rarest_first | piece_picker::reverse) == 2); // ======================================================== // make sure the 4 blocks are picked from the same piece if // whole pieces are preferred. Priority and availability is more // important. Piece 1 has the lowest availability even though // it is not a whole piece print_title("test pick whole pieces"); p = setup_picker("2212222", " ", "1111111", "1023460"); picked = pick_pieces(p, "****** ", 1, blocks_per_piece , &peer_struct, options, empty_vector); TEST_EQUAL(int(picked.size()), 3); for (int i = 0; i < blocks_per_piece && i < int(picked.size()); ++i) TEST_EQUAL(picked[i].piece_index, 2); p = setup_picker("1111111", " ", "1111111", ""); picked = pick_pieces(p, "****** ", 1, blocks_per_piece , &peer_struct, options, empty_vector); TEST_EQUAL(int(picked.size()), blocks_per_piece); for (int i = 0; i < blocks_per_piece && i < int(picked.size()); ++i) TEST_EQUAL(picked[i].block_index, i); p = setup_picker("2221222", " ", "", ""); picked = pick_pieces(p, "*******", 1, 7 * blocks_per_piece , &peer_struct, options, empty_vector); TEST_EQUAL(int(picked.size()), 7 * blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(i / blocks_per_piece, i % blocks_per_piece)); // ======================================================== // test the distributed copies function. It should include ourself // in the availability. i.e. piece 0 has availability 2. // there are 2 pieces with availability 2 and 5 with availability 3 print_title("test distributed copies"); p = setup_picker("1233333", "* ", "", ""); dc = p->distributed_copies(); TEST_CHECK(dc == std::make_pair(2, 5000 / 7)); // ======================================================== // make sure filtered pieces are ignored print_title("test filtered pieces"); p = setup_picker("1111111", " ", "0010000", ""); TEST_CHECK(test_pick(p, piece_picker::rarest_first) == 2); TEST_CHECK(test_pick(p, piece_picker::rarest_first | piece_picker::reverse) == 2); TEST_CHECK(test_pick(p, piece_picker::sequential) == 2); TEST_CHECK(test_pick(p, piece_picker::sequential | piece_picker::reverse) == 2); // ======================================================== // make sure we_dont_have works print_title("test we_dont_have"); p = setup_picker("1111111", "*******", "0100000", ""); TEST_CHECK(p->have_piece(1)); TEST_CHECK(p->have_piece(2)); p->we_dont_have(1); p->we_dont_have(2); TEST_CHECK(!p->have_piece(1)); TEST_CHECK(!p->have_piece(2)); picked = pick_pieces(p, "*** ** ", 1, 0, 0, options, empty_vector); TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(picked.front().piece_index == 1); // ======================================================== // make sure we can split m_seed when removing a refcount print_title("test dec_refcount split seed"); p = setup_picker("0000000", " ", "0000000", ""); p->inc_refcount_all(0); std::vector avail; p->get_availability(avail); TEST_EQUAL(avail.size(), 7); TEST_CHECK(avail[0] != 0); TEST_CHECK(avail[1] != 0); TEST_CHECK(avail[2] != 0); TEST_CHECK(avail[3] != 0); TEST_CHECK(avail[4] != 0); p->dec_refcount(3, 0); p->get_availability(avail); TEST_EQUAL(avail.size(), 7); TEST_CHECK(avail[0] != 0); TEST_CHECK(avail[1] != 0); TEST_CHECK(avail[2] != 0); TEST_CHECK(avail[3] == 0); TEST_CHECK(avail[4] != 0); // ======================================================== // make sure init preserves priorities print_title("test init"); p = setup_picker("1111111", " ", "1111111", ""); TEST_CHECK(p->num_filtered() == 0); TEST_CHECK(p->num_have_filtered() == 0); TEST_CHECK(p->num_have() == 0); p->set_piece_priority(0, 0); TEST_CHECK(p->num_filtered() == 1); TEST_CHECK(p->num_have_filtered() == 0); TEST_CHECK(p->num_have() == 0); p->we_have(0); TEST_CHECK(p->num_filtered() == 0); TEST_CHECK(p->num_have_filtered() == 1); TEST_CHECK(p->num_have() == 1); p->init(blocks_per_piece, blocks_per_piece, blocks_per_piece * 7); TEST_CHECK(p->piece_priority(0) == 0); TEST_CHECK(p->num_filtered() == 1); TEST_CHECK(p->num_have_filtered() == 0); TEST_CHECK(p->num_have() == 0); // ======================================================== // make sure requested blocks aren't picked print_title("test don't pick requested blocks"); p = setup_picker("1111111", " ", "", ""); picked = pick_pieces(p, "*******", 1, 0, 0, options, empty_vector); TEST_CHECK(int(picked.size()) > 0); piece_block first = picked.front(); p->mark_as_downloading(picked.front(), &peer_struct); TEST_CHECK(p->num_peers(picked.front()) == 1); picked = pick_pieces(p, "*******", 1, 0, 0, options, empty_vector); TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(picked.front() != first); // ======================================================== // make sure downloading pieces have higher priority print_title("test downloading piece priority"); p = setup_picker("1111111", " ", "", ""); picked = pick_pieces(p, "*******", 1, 0, 0, options, empty_vector); TEST_CHECK(int(picked.size()) > 0); first = picked.front(); p->mark_as_downloading(picked.front(), &peer_struct); TEST_CHECK(p->num_peers(picked.front()) == 1); picked = pick_pieces(p, "*******", 1, 0, 0, options, empty_vector); TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(picked.front() != first); TEST_CHECK(picked.front().piece_index == first.piece_index); // ======================================================== // when we're prioritizing partial pieces, make sure to first pick the // rarest of them. The blocks in this test are: // 0: [ ] avail: 1 // 1: [x ] avail: 1 // 2: [xx ] avail: 1 // 3: [xxx ] avail: 2 // 4: [ ] avail: 1 // 5: [ ] avail: 1 // 6: [xxxx] avail: 1 // piece 6 does not have any blocks left to pick, even though piece 3 only // has a single block left before it completes, it is less rare than piece // 2. Piece 2 is the best pick in this case. print_title("test partial piece order (rarest first)"); p = setup_picker("1112111", " ", "", "013700f"); picked = pick_pieces(p, "*******", 1, 0, 0 , options | piece_picker::prioritize_partials, empty_vector); TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(picked.front() == piece_block(2, 2) || picked.front() == piece_block(2, 3)); // as a tie breaker, make sure downloading pieces closer to completion have // higher priority. piece 3 is only 1 block from being completed, and should // be picked print_title("test partial piece order (most complete)"); p = setup_picker("1111111", " ", "", "013700f"); picked = pick_pieces(p, "*******", 1, 0, 0 , options | piece_picker::prioritize_partials, empty_vector); TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(picked.front() == piece_block(3, 3)); // if we don't use rarest first when we prioritize partials, but instead use // sequential order, make sure we pick the right one print_title("test partial piece order (sequential)"); p = setup_picker("1111111", " ", "", "013700f"); picked = pick_pieces(p, "*******", 1, 0, 0 , piece_picker::sequential | piece_picker::prioritize_partials, empty_vector); TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(picked.front() == piece_block(1, 1) || picked.front() == piece_block(1, 2) || picked.front() == piece_block(1, 3)); // ======================================================== // make sure the random piece picker can still pick partial pieces print_title("test random picking (downloading piece)"); p = setup_picker("1111111", " ", "", "013700f"); picked = pick_pieces(p, " *** *", 1, 0, 0 , 0, empty_vector); TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(picked.front() == piece_block(1, 1) || picked.front() == piece_block(2, 2) || picked.front() == piece_block(3, 3)); // make sure the random piece picker can still pick partial pieces // even when prefer_contiguous_blocks is set print_title("test random picking (downloading piece, prefer contiguous)"); p = setup_picker("1111111", " ", "", "013700f"); picked = pick_pieces(p, " *** *", 1, 4, 0 , 0, empty_vector); TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(picked.front() == piece_block(1, 1) || picked.front() == piece_block(2, 2) || picked.front() == piece_block(3, 3)); // ======================================================== // test sequential download print_title("test sequential download"); p = setup_picker("7654321", " ", "", ""); picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0 , piece_picker::sequential, empty_vector); TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(i / blocks_per_piece, i % blocks_per_piece)); // ======================================================== // test reverse sequential download print_title("test reverse sequential download"); p = setup_picker("7654321", " ", "", ""); picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0 , piece_picker::sequential | piece_picker::reverse, empty_vector); TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(6 - (i / blocks_per_piece), i % blocks_per_piece)); // ======================================================== // test priority sequential download print_title("test priority sequential download"); p = setup_picker("7654321", " ", "1117071", ""); picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0 , piece_picker::sequential, empty_vector); // the piece with priority 0 was not picked, everything else should // be picked TEST_EQUAL(int(picked.size()), 6 * blocks_per_piece); // the first two pieces picked should be 3 and 5 since those have priority 7 for (int i = 0; i < 2 * blocks_per_piece; ++i) TEST_CHECK(picked[i].piece_index == 3 || picked[i].piece_index == 5); int expected[] = {-1, -1, 0, 1, 2, 6}; for (int i = 2 * blocks_per_piece; i < int(picked.size()); ++i) TEST_EQUAL(picked[i].piece_index, expected[i / blocks_per_piece]); // ======================================================== // sweep up, we_have() print_title("test cursors. sweep up, we_have"); p = setup_picker("7654321", " ", "", ""); for (int i = 0; i < 7; ++i) { TEST_EQUAL(p->cursor(), i); TEST_EQUAL(p->reverse_cursor(), 7); p->we_have(i); } TEST_CHECK(p->is_finished()); TEST_CHECK(p->is_seeding()); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); // sweep up, set_piece_priority() print_title("test cursors. sweep up, set_piece_priority"); p = setup_picker("7654321", " ", "", ""); for (int i = 0; i < 7; ++i) { TEST_EQUAL(p->cursor(), i); TEST_EQUAL(p->reverse_cursor(), 7); p->set_piece_priority(i, 0); } TEST_CHECK(p->is_finished()); TEST_CHECK(!p->is_seeding()); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); // sweep down, we_have() print_title("test cursors. sweep down, we_have"); p = setup_picker("7654321", " ", "", ""); for (int i = 6; i >= 0; --i) { TEST_EQUAL(p->cursor(), 0); TEST_EQUAL(p->reverse_cursor(), i + 1); p->we_have(i); } TEST_CHECK(p->is_finished()); TEST_CHECK(p->is_seeding()); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); // sweep down, set_piece_priority() print_title("test cursors. sweep down, set_piece_priority"); p = setup_picker("7654321", " ", "", ""); for (int i = 6; i >= 0; --i) { TEST_EQUAL(p->cursor(), 0); TEST_EQUAL(p->reverse_cursor(), i + 1); p->set_piece_priority(i, 0); } TEST_CHECK(p->is_finished()); TEST_CHECK(!p->is_seeding()); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); // sweep in, set_piece_priority() print_title("test cursors. sweep in, set_piece_priority"); p = setup_picker("7654321", " ", "", ""); for (int left = 0, right = 6; left <= 3 && right >= 3; ++left, --right) { TEST_EQUAL(p->cursor(), left); TEST_EQUAL(p->reverse_cursor(), right + 1); p->set_piece_priority(left, 0); p->set_piece_priority(right, 0); } TEST_CHECK(p->is_finished()); TEST_CHECK(!p->is_seeding()); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); // sweep in, we_have() print_title("test cursors. sweep in, we_have"); p = setup_picker("7654321", " ", "", ""); for (int left = 0, right = 6; left <= 3 && right >= 3; ++left, --right) { TEST_EQUAL(p->cursor(), left); TEST_EQUAL(p->reverse_cursor(), right + 1); p->we_have(left); p->we_have(right); } TEST_CHECK(p->is_finished()); TEST_CHECK(p->is_seeding()); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); print_title("test cursors. sweep up, we_dont_have"); p = setup_picker("7654321", "*******", "", ""); TEST_CHECK(p->is_finished()); TEST_CHECK(p->is_seeding()); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); for (int i = 0; i < 7; ++i) { p->we_dont_have(i); TEST_EQUAL(p->cursor(), 0); TEST_EQUAL(p->reverse_cursor(), i + 1); } TEST_CHECK(!p->is_finished()); TEST_CHECK(!p->is_seeding()); TEST_EQUAL(p->cursor(), 0); TEST_EQUAL(p->reverse_cursor(), 7); print_title("test cursors. sweep down, we_dont_have"); p = setup_picker("7654321", "*******", "", ""); TEST_CHECK(p->is_finished()); TEST_CHECK(p->is_seeding()); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); for (int i = 6; i >= 0; --i) { p->we_dont_have(i); TEST_EQUAL(p->cursor(), i); TEST_EQUAL(p->reverse_cursor(), 7); } TEST_CHECK(!p->is_finished()); TEST_CHECK(!p->is_seeding()); TEST_EQUAL(p->cursor(), 0); TEST_EQUAL(p->reverse_cursor(), 7); print_title("test cursors. sweep out, we_dont_have"); p = setup_picker("7654321", "*******", "", ""); TEST_CHECK(p->is_finished()); TEST_CHECK(p->is_seeding()); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); for (int left = 3, right = 3; left >= 0 && right < 7; --left, ++right) { p->we_dont_have(left); p->we_dont_have(right); TEST_EQUAL(p->cursor(), left); TEST_EQUAL(p->reverse_cursor(), right + 1); } TEST_CHECK(!p->is_finished()); TEST_CHECK(!p->is_seeding()); TEST_EQUAL(p->cursor(), 0); TEST_EQUAL(p->reverse_cursor(), 7); // test cursors print_title("test cursors"); p = setup_picker("7654321", " ", "", ""); TEST_EQUAL(p->cursor(), 0); TEST_EQUAL(p->reverse_cursor(), 7); p->we_have(1); TEST_EQUAL(p->cursor(), 0); TEST_EQUAL(p->reverse_cursor(), 7); p->we_have(0); TEST_EQUAL(p->cursor(), 2); TEST_EQUAL(p->reverse_cursor(), 7); p->we_have(5); TEST_EQUAL(p->cursor(), 2); TEST_EQUAL(p->reverse_cursor(), 7); p->we_have(6); TEST_EQUAL(p->cursor(), 2); TEST_EQUAL(p->reverse_cursor(), 5); p->we_have(4); p->we_have(3); p->we_have(2); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); p = setup_picker("7654321", " ", "", ""); TEST_EQUAL(p->cursor() , 0); TEST_EQUAL(p->reverse_cursor(), 7); p->set_piece_priority(1, 0); TEST_EQUAL(p->cursor(), 0); TEST_EQUAL(p->reverse_cursor(), 7); p->set_piece_priority(0, 0); TEST_EQUAL(p->cursor(), 2); TEST_EQUAL(p->reverse_cursor(), 7); p->set_piece_priority(5, 0); TEST_EQUAL(p->cursor(), 2); TEST_EQUAL(p->reverse_cursor(), 7); p->set_piece_priority(6, 0); TEST_EQUAL(p->cursor(), 2); TEST_EQUAL(p->reverse_cursor(), 5); p->set_piece_priority(4, 0); p->set_piece_priority(3, 0); p->set_piece_priority(2, 0); TEST_EQUAL(p->cursor(), 7); TEST_EQUAL(p->reverse_cursor(), 0); p->set_piece_priority(3, 1); TEST_EQUAL(p->cursor(), 3); TEST_EQUAL(p->reverse_cursor(), 4); // ======================================================== // test piece priorities print_title("test piece priorities"); p = setup_picker("5555555", " ", "7654321", ""); TEST_CHECK(p->num_filtered() == 0); TEST_CHECK(p->num_have_filtered() == 0); p->set_piece_priority(0, 0); TEST_CHECK(p->num_filtered() == 1); TEST_CHECK(p->num_have_filtered() == 0); p->mark_as_finished(piece_block(0,0), 0); p->we_have(0); TEST_CHECK(p->num_filtered() == 0); TEST_CHECK(p->num_have_filtered() == 1); p->we_dont_have(0); p->set_piece_priority(0, 7); picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0 , options, empty_vector); TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(i / blocks_per_piece, i % blocks_per_piece)); // test changing priority on a piece we have p->we_have(0); p->set_piece_priority(0, 0); p->set_piece_priority(0, 1); p->set_piece_priority(0, 0); std::vector prios; p->piece_priorities(prios); TEST_CHECK(prios.size() == 7); int prio_comp[] = {0, 6, 5, 4, 3, 2, 1}; TEST_CHECK(std::equal(prios.begin(), prios.end(), prio_comp)); std::vector filter; p->filtered_pieces(filter); TEST_CHECK(prios.size() == 7); bool filter_comp[] = {true, false, false, false, false, false, false}; TEST_CHECK(std::equal(filter.begin(), filter.end(), filter_comp)); // ======================================================== // test restore_piece print_title("test restore piece"); p = setup_picker("1234567", " ", "", ""); p->mark_as_finished(piece_block(0,0), 0); p->mark_as_finished(piece_block(0,1), 0); p->mark_as_finished(piece_block(0,2), 0); p->mark_as_finished(piece_block(0,3), 0); picked = pick_pieces(p, "*******", 1, 0, 0, options, empty_vector); TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(picked.front().piece_index == 1); p->restore_piece(0); picked = pick_pieces(p, "*******", 1, 0, 0, options, empty_vector); TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(picked.front().piece_index == 0); p->mark_as_finished(piece_block(0,0), 0); p->mark_as_finished(piece_block(0,1), 0); p->mark_as_finished(piece_block(0,2), 0); p->mark_as_finished(piece_block(0,3), 0); p->set_piece_priority(0, 0); picked = pick_pieces(p, "*******", 1, 0, 0, options, empty_vector); TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(picked.front().piece_index == 1); p->restore_piece(0); picked = pick_pieces(p, "*******", 1, 0, 0, options, empty_vector); TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(picked.front().piece_index == 1); p->set_piece_priority(0, 7); picked = pick_pieces(p, "*******", 1, 0, 0, options, empty_vector); TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(picked.front().piece_index == 0); // ======================================================== // test random mode print_title("test random pick"); p = setup_picker("1234567", " ", "1111122", ""); std::set random_pieces; for (int i = 0; i < 100; ++i) random_pieces.insert(test_pick(p, 0)); TEST_CHECK(random_pieces.size() == 7); random_pieces.clear(); for (int i = 0; i < 7; ++i) { int piece = test_pick(p, 0); p->we_have(piece); random_pieces.insert(piece); } TEST_CHECK(random_pieces.size() == 7); // ======================================================== // make sure the piece picker will pick pieces that // are already requested from other peers if it has to print_title("test picking downloading blocks"); p = setup_picker("1111111", " ", "", ""); p->mark_as_downloading(piece_block(2,2), &tmp1); p->mark_as_downloading(piece_block(1,2), &tmp1); picked.clear(); p->pick_pieces(string2vec("*******"), picked, 7 * blocks_per_piece, 0, 0 , piece_picker::prioritize_partials, empty_vector, 20 , pc); TEST_CHECK(verify_pick(p, picked, true)); print_pick(picked); // don't pick both busy pieces, if there are already other blocks picked TEST_EQUAL(picked.size(), 7 * blocks_per_piece - 2); picked.clear(); p->pick_pieces(string2vec("*******"), picked, 7 * blocks_per_piece, 0, 0 , piece_picker::prioritize_partials | piece_picker::rarest_first, empty_vector, 20 , pc); TEST_CHECK(verify_pick(p, picked, true)); print_pick(picked); // don't pick both busy pieces, if there are already other blocks picked TEST_EQUAL(picked.size(), 7 * blocks_per_piece - 2); picked.clear(); p->pick_pieces(string2vec("*******"), picked, 7 * blocks_per_piece, 0, 0 , piece_picker::rarest_first, empty_vector, 20 , pc); TEST_CHECK(verify_pick(p, picked, true)); print_pick(picked); // don't pick both busy pieces, if there are already other blocks picked TEST_EQUAL(picked.size(), 7 * blocks_per_piece - 2); // make sure we still pick from a partial piece even when prefering whole pieces picked.clear(); p->pick_pieces(string2vec(" * "), picked, 1, blocks_per_piece, 0 , piece_picker::rarest_first | piece_picker::align_expanded_pieces, empty_vector, 20 , pc); TEST_CHECK(verify_pick(p, picked, true)); print_pick(picked); // always only pick one busy piece TEST_EQUAL(picked.size(), 1); TEST_CHECK(picked.size() >= 1 && picked[0].piece_index == 1); // don't pick locked pieces picked.clear(); p->lock_piece(1); p->pick_pieces(string2vec(" ** "), picked, 7, 0, 0 , piece_picker::rarest_first, empty_vector, 20 , pc); TEST_CHECK(verify_pick(p, picked, true)); print_pick(picked); // always only pick one busy piece TEST_EQUAL(picked.size(), 3); TEST_CHECK(picked.size() >= 1 && picked[0].piece_index == 2); p->restore_piece(1); p->mark_as_downloading(piece_block(2,0), &tmp1); p->mark_as_downloading(piece_block(2,1), &tmp1); p->mark_as_downloading(piece_block(2,3), &tmp1); p->mark_as_downloading(piece_block(1,0), &tmp1); p->mark_as_downloading(piece_block(1,1), &tmp1); p->mark_as_downloading(piece_block(1,2), &tmp1); p->mark_as_downloading(piece_block(1,3), &tmp1); picked.clear(); p->pick_pieces(string2vec(" ** "), picked, 2, 0, 0 , piece_picker::rarest_first, empty_vector, 20 , pc); TEST_CHECK(verify_pick(p, picked, true)); print_pick(picked); // always only pick one busy piece TEST_EQUAL(picked.size(), 1); picked.clear(); p->pick_pieces(string2vec(" ** "), picked, 2 * blocks_per_piece, 0, 0 , piece_picker::prioritize_partials, empty_vector, 0 , pc); TEST_CHECK(verify_pick(p, picked, true)); print_pick(picked); // always only pick one busy piece TEST_EQUAL(picked.size(), 1); picked.clear(); p->pick_pieces(string2vec(" ** "), picked, 2 * blocks_per_piece, 0, 0 , piece_picker::prioritize_partials, empty_vector, 20 , pc); TEST_CHECK(verify_pick(p, picked, true)); print_pick(picked); // always only pick one busy piece TEST_EQUAL(picked.size(), 1); // ======================================================== // test clear_peer print_title("test clear_peer"); p = setup_picker("1123333", " ", "", ""); p->mark_as_downloading(piece_block(0, 0), &tmp1); p->mark_as_downloading(piece_block(0, 1), &tmp2); p->mark_as_downloading(piece_block(0, 2), &tmp3); p->mark_as_downloading(piece_block(1, 1), &tmp1); p->mark_as_downloading(piece_block(2, 1), &tmp2); p->mark_as_downloading(piece_block(3, 1), &tmp3); std::vector dls; torrent_peer* expected_dls1[] = {&tmp1, &tmp2, &tmp3, 0}; torrent_peer* expected_dls2[] = {0, &tmp1, 0, 0}; torrent_peer* expected_dls3[] = {0, &tmp2, 0, 0}; torrent_peer* expected_dls4[] = {0, &tmp3, 0, 0}; torrent_peer* expected_dls5[] = {&tmp1, 0, &tmp3, 0}; p->get_downloaders(dls, 0); TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls1)); p->get_downloaders(dls, 1); TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls2)); p->get_downloaders(dls, 2); TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls3)); p->get_downloaders(dls, 3); TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls4)); p->clear_peer(&tmp2); p->get_downloaders(dls, 0); TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls5)); // ======================================================== // test have_all and have_none print_title("test have_all and have_none"); p = setup_picker("0123333", "* ", "", ""); dc = p->distributed_copies(); std::cout << "distributed copies: " << dc.first << "." << (dc.second / 1000.f) << std::endl; TEST_CHECK(dc == std::make_pair(1, 5000 / 7)); p->inc_refcount_all(&tmp8); dc = p->distributed_copies(); TEST_CHECK(dc == std::make_pair(2, 5000 / 7)); p->dec_refcount_all(&tmp8); dc = p->distributed_copies(); std::cout << "distributed copies: " << dc.first << "." << (dc.second / 1000.f) << std::endl; TEST_CHECK(dc == std::make_pair(1, 5000 / 7)); p->inc_refcount(0, &tmp0); p->dec_refcount_all(&tmp0); dc = p->distributed_copies(); std::cout << "distributed copies: " << dc.first << "." << (dc.second / 1000.f) << std::endl; TEST_CHECK(dc == std::make_pair(0, 6000 / 7)); TEST_CHECK(test_pick(p) == 2); // ======================================================== // test have_all and have_none print_title("test have_all and have_none with sequential download"); p = setup_picker("0123333", "* ", "", ""); dc = p->distributed_copies(); std::cout << "distributed copies: " << dc.first << "." << (dc.second / 1000.f) << std::endl; TEST_CHECK(dc == std::make_pair(1, 5000 / 7)); p->inc_refcount_all(&tmp8); dc = p->distributed_copies(); std::cout << "distributed copies: " << dc.first << "." << (dc.second / 1000.f) << std::endl; TEST_CHECK(dc == std::make_pair(2, 5000 / 7)); TEST_CHECK(test_pick(p) == 1); // ======================================================== // test inc_ref and dec_ref print_title("test inc_ref dec_ref"); p = setup_picker("1233333", " * ", "", ""); TEST_CHECK(test_pick(p) == 0); p->dec_refcount(0, &tmp0); TEST_CHECK(test_pick(p) == 1); p->dec_refcount(4, &tmp0); p->dec_refcount(4, &tmp1); TEST_CHECK(test_pick(p) == 4); // decrease refcount on something that's not in the piece list p->dec_refcount(5, &tmp0); p->inc_refcount(5, &tmp0); bitfield bits = string2vec("* "); TEST_EQUAL(bits.get_bit(0), true); TEST_EQUAL(bits.get_bit(1), false); TEST_EQUAL(bits.get_bit(2), false); TEST_EQUAL(bits.get_bit(3), false); TEST_EQUAL(bits.get_bit(4), false); TEST_EQUAL(bits.get_bit(5), false); TEST_EQUAL(bits.get_bit(6), false); p->inc_refcount(bits, &tmp0); bits = string2vec(" * "); TEST_EQUAL(bits.get_bit(0), false); TEST_EQUAL(bits.get_bit(1), false); TEST_EQUAL(bits.get_bit(2), false); TEST_EQUAL(bits.get_bit(3), false); TEST_EQUAL(bits.get_bit(4), true); TEST_EQUAL(bits.get_bit(5), false); TEST_EQUAL(bits.get_bit(6), false); p->dec_refcount(bits, &tmp2); TEST_EQUAL(test_pick(p), 0); // ======================================================== // test unverified_blocks, marking blocks and get_downloader print_title("test unverified blocks"); p = setup_picker("1111111", " ", "", "0300700"); TEST_CHECK(p->unverified_blocks() == 2 + 3); TEST_CHECK(p->get_downloader(piece_block(4, 0)) == tmp_peer); TEST_CHECK(p->get_downloader(piece_block(4, 1)) == tmp_peer); TEST_CHECK(p->get_downloader(piece_block(4, 2)) == tmp_peer); TEST_CHECK(p->get_downloader(piece_block(4, 3)) == 0); p->mark_as_downloading(piece_block(4, 3), &peer_struct); TEST_CHECK(p->get_downloader(piece_block(4, 3)) == &peer_struct); p->piece_info(4, st); TEST_CHECK(st.requested == 1); TEST_CHECK(st.writing == 0); TEST_CHECK(st.finished == 3); TEST_CHECK(p->unverified_blocks() == 2 + 3); p->mark_as_writing(piece_block(4, 3), &peer_struct); TEST_CHECK(p->get_downloader(piece_block(4, 3)) == &peer_struct); p->piece_info(4, st); TEST_CHECK(st.requested == 0); TEST_CHECK(st.writing == 1); TEST_CHECK(st.finished == 3); TEST_CHECK(p->unverified_blocks() == 2 + 3); p->mark_as_finished(piece_block(4, 3), &peer_struct); TEST_CHECK(p->get_downloader(piece_block(4, 3)) == &peer_struct); p->piece_info(4, st); TEST_CHECK(st.requested == 0); TEST_CHECK(st.writing == 0); TEST_CHECK(st.finished == 4); TEST_CHECK(p->unverified_blocks() == 2 + 4); p->we_have(4); p->piece_info(4, st); TEST_CHECK(st.requested == 0); TEST_CHECK(st.writing == 0); TEST_CHECK(st.finished == 4); TEST_CHECK(p->get_downloader(piece_block(4, 3)) == 0); TEST_CHECK(p->unverified_blocks() == 2); // ======================================================== // test prefer_contiguous_blocks print_title("test prefer contiguous blocks"); p = setup_picker("1111111", " ", "", ""); picked = pick_pieces(p, "*******", 1, 3 * blocks_per_piece , 0, options, empty_vector); TEST_CHECK(int(picked.size()) >= 3 * blocks_per_piece); piece_block b = picked.front(); for (int i = 1; i < int(picked.size()); ++i) { TEST_CHECK(picked[i].piece_index * blocks_per_piece + picked[i].block_index == b.piece_index * blocks_per_piece + b.block_index + 1); b = picked[i]; } picked = pick_pieces(p, "*******", 1, 3 * blocks_per_piece , 0, options, empty_vector); TEST_CHECK(int(picked.size()) >= 3 * blocks_per_piece); b = picked.front(); for (int i = 1; i < int(picked.size()); ++i) { TEST_CHECK(picked[i].piece_index * blocks_per_piece + picked[i].block_index == b.piece_index * blocks_per_piece + b.block_index + 1); b = picked[i]; } // make sure pieces that don't match the 'whole pieces' requirement // are picked if there's no other choice p = setup_picker("1111111", " ", "", ""); p->mark_as_downloading(piece_block(2,2), &tmp1); picked = pick_pieces(p, "*******", 7 * blocks_per_piece - 1, blocks_per_piece , 0, options, empty_vector); TEST_CHECK(picked.size() == 7 * blocks_per_piece - 1); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(2,2)) == picked.end()); // test aligned whole pieces print_title("test prefer aligned whole pieces"); p = setup_picker("2222221222222222", " ", "", ""); picked = pick_pieces(p, "****************", 1, 4 * blocks_per_piece, 0 , options | piece_picker::align_expanded_pieces, empty_vector); // the piece picker should pick piece 5, and then align it to even 4 pieces // i.e. it should have picked pieces: 4,5,6,7 print_pick(picked); TEST_EQUAL(picked.size() , 4 * blocks_per_piece); std::set picked_pieces; for (std::vector::iterator i = picked.begin() , end(picked.end()); i != end; ++i) picked_pieces.insert(picked_pieces.begin(), i->piece_index); TEST_CHECK(picked_pieces.size() == 4); int expected_pieces[] = {4,5,6,7}; TEST_CHECK(std::equal(picked_pieces.begin(), picked_pieces.end(), expected_pieces)) //#error test picking with partial pieces and other peers present so that both backup_pieces and backup_pieces2 are used // ======================================================== // test parole mode print_title("test parole mode"); p = setup_picker("3333133", " ", "", ""); p->mark_as_finished(piece_block(0, 0), 0); picked = pick_pieces(p, "*******", 1, blocks_per_piece, 0 , options | piece_picker::on_parole | piece_picker::prioritize_partials, empty_vector); TEST_EQUAL(int(picked.size()), blocks_per_piece - 1); for (int i = 1; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(0, i + 1)); // make sure that the partial piece is not picked by a // peer that is has not downloaded/requested the other blocks picked = pick_pieces(p, "*******", 1, blocks_per_piece , &peer_struct , options | piece_picker::on_parole | piece_picker::prioritize_partials, empty_vector); TEST_EQUAL(int(picked.size()), blocks_per_piece); for (int i = 1; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(4, i)); // ======================================================== // test suggested pieces print_title("test suggested pieces"); p = setup_picker("1111222233334444", " ", "", ""); int v[] = {1, 5}; const std::vector suggested_pieces(v, v + 2); picked = pick_pieces(p, "****************", 1, blocks_per_piece , 0, options, suggested_pieces); TEST_CHECK(int(picked.size()) >= blocks_per_piece); for (int i = 1; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(1, i)); p->set_piece_priority(0, 0); p->set_piece_priority(1, 0); p->set_piece_priority(2, 0); p->set_piece_priority(3, 0); picked = pick_pieces(p, "****************", 1, blocks_per_piece , 0, options, suggested_pieces); TEST_CHECK(int(picked.size()) >= blocks_per_piece); for (int i = 1; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(5, i)); p = setup_picker("1111222233334444", "**** ", "", ""); picked = pick_pieces(p, "****************", 1, blocks_per_piece , 0, options, suggested_pieces); TEST_CHECK(int(picked.size()) >= blocks_per_piece); for (int i = 1; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(5, i)); // ======================================================== // test bitfield optimization print_title("test bitfield optimization"); // we have less than half of the pieces p = setup_picker("2122222211221222", " ", "", ""); // make sure it's not dirty pick_pieces(p, "****************", 1, blocks_per_piece, 0); print_availability(p); p->dec_refcount(string2vec("** ** ** * "), &tmp0); print_availability(p); TEST_CHECK(verify_availability(p, "1022112200220222")); // make sure it's not dirty pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->inc_refcount(string2vec(" ** ** * * "), &tmp8); print_availability(p); TEST_CHECK(verify_availability(p, "1132123201220322")); // ======================================================== // test seed optimizaton print_title("test seed optimization"); p = setup_picker("0000000000000000", " ", "", ""); // make sure it's not dirty pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->inc_refcount_all(&tmp0); print_availability(p); TEST_CHECK(verify_availability(p, "1111111111111111")); pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->dec_refcount(string2vec(" **** ** "), &tmp0); print_availability(p); TEST_CHECK(verify_availability(p, "1100001100111111")); pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->inc_refcount(string2vec(" **** ** "), &tmp0); TEST_CHECK(verify_availability(p, "1111111111111111")); pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->dec_refcount_all(&tmp0); TEST_CHECK(verify_availability(p, "0000000000000000")); p->inc_refcount_all(&tmp1); print_availability(p); TEST_CHECK(verify_availability(p, "1111111111111111")); pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->dec_refcount(3, &tmp1); print_availability(p); TEST_CHECK(verify_availability(p, "1110111111111111")); p->inc_refcount(string2vec("****************"), &tmp2); print_availability(p); TEST_CHECK(verify_availability(p, "2221222222222222")); p->inc_refcount(string2vec("* * * * * * * * "), &tmp3); print_availability(p); TEST_CHECK(verify_availability(p, "3231323232323232")); p->dec_refcount(string2vec("****************"), &tmp2); print_availability(p); TEST_CHECK(verify_availability(p, "2120212121212121")); p->dec_refcount(string2vec("* * * * * * * * "), &tmp3); print_availability(p); TEST_CHECK(verify_availability(p, "1110111111111111")); // ======================================================== // test reversed peers print_title("test reversed peers"); p = setup_picker("3333333", " *****", "", ""); // a reversed peer picked a block from piece 0 // This should make the piece reversed p->mark_as_downloading(piece_block(0,0), &tmp1 , piece_picker::reverse); TEST_EQUAL(test_pick(p, piece_picker::rarest_first), 1); // make sure another reversed peer pick the same piece TEST_EQUAL(test_pick(p, piece_picker::rarest_first | piece_picker::reverse), 0); // ======================================================== // test reversed pieces upgrading to normal pieces print_title("test reversed piece upgrade"); p = setup_picker("3333333", " *****", "", ""); // make piece 0 partial and reversed p->mark_as_downloading(piece_block(0,1), &tmp1 , piece_picker::reverse); TEST_EQUAL(test_pick(p), 1); // now have a regular peer pick the reversed block. It should now // have turned into a regular one and be prioritized p->mark_as_downloading(piece_block(0,2), &tmp1); TEST_EQUAL(test_pick(p), 0); // ======================================================== // test pieces downgrading to reversed pieces print_title("test reversed piece downgrade"); // now make sure a piece can be demoted to reversed if there are no // other outstanding requests p = setup_picker("3333333", " ", "", ""); // make piece 0 partial and not reversed p->mark_as_finished(piece_block(0,1), &tmp1); // a reversed peer picked a block from piece 0 // This should make the piece reversed p->mark_as_downloading(piece_block(0,0), &tmp1 , piece_picker::reverse); TEST_EQUAL(test_pick(p, piece_picker::rarest_first | piece_picker::reverse), 0); // ======================================================== print_title("test piece_stats"); p = setup_picker("3456789", "* ", "", "0300000"); piece_picker::piece_stats_t stat = p->piece_stats(0); TEST_EQUAL(stat.peer_count, 3); TEST_EQUAL(stat.have, 1); TEST_EQUAL(stat.downloading, 0); stat = p->piece_stats(1); TEST_EQUAL(stat.peer_count, 4); TEST_EQUAL(stat.have, 0); TEST_EQUAL(stat.downloading, 1); // ======================================================== print_title("test piece passed"); p = setup_picker("1111111", "* ", "", "0300000"); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), false); TEST_EQUAL(p->num_passed(), 1); TEST_EQUAL(p->num_have(), 1); p->piece_passed(1); TEST_EQUAL(p->num_passed(), 2); TEST_EQUAL(p->num_have(), 1); p->we_have(1); TEST_EQUAL(p->num_have(), 2); p->mark_as_finished(piece_block(2,0), &tmp1); p->piece_passed(2); TEST_EQUAL(p->num_passed(), 3); // just because the hash check passed doesn't mean // we "have" the piece. We need to write it to disk first TEST_EQUAL(p->num_have(), 2); // piece 2 already passed the hash check, as soon as we've // written all the blocks to disk, we should have that piece too p->mark_as_finished(piece_block(2,1), &tmp1); p->mark_as_finished(piece_block(2,2), &tmp1); p->mark_as_finished(piece_block(2,3), &tmp1); TEST_EQUAL(p->num_have(), 3); TEST_EQUAL(p->have_piece(2), true); // ======================================================== print_title("test piece passed (causing we_have)"); p = setup_picker("1111111", "* ", "", "0700000"); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), false); TEST_EQUAL(p->num_passed(), 1); TEST_EQUAL(p->num_have(), 1); p->mark_as_finished(piece_block(1,3), &tmp1); TEST_EQUAL(p->num_passed(), 1); TEST_EQUAL(p->num_have(), 1); p->piece_passed(1); TEST_EQUAL(p->num_passed(), 2); TEST_EQUAL(p->num_have(), 2); // ======================================================== print_title("test break_one_seed"); p = setup_picker("0000000", "* ", "", "0700000"); p->inc_refcount_all(&tmp1); p->inc_refcount_all(&tmp2); p->inc_refcount_all(&tmp3); TEST_EQUAL(p->piece_stats(0).peer_count, 3); p->dec_refcount(0, &tmp1); TEST_EQUAL(p->piece_stats(0).peer_count, 2); TEST_EQUAL(p->piece_stats(1).peer_count, 3); TEST_EQUAL(p->piece_stats(2).peer_count, 3); TEST_EQUAL(p->piece_stats(3).peer_count, 3); // ======================================================== print_title("test we dont have"); p = setup_picker("1111111", "* * ", "1101111", ""); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), false); TEST_EQUAL(p->has_piece_passed(2), true); TEST_EQUAL(p->num_passed(), 2); TEST_EQUAL(p->num_have(), 2); TEST_EQUAL(p->num_have_filtered(), 1); TEST_EQUAL(p->num_filtered(), 0); p->we_dont_have(0); TEST_EQUAL(p->has_piece_passed(0), false); TEST_EQUAL(p->has_piece_passed(1), false); TEST_EQUAL(p->has_piece_passed(2), true); TEST_EQUAL(p->num_passed(), 1); TEST_EQUAL(p->num_have(), 1); TEST_EQUAL(p->num_have_filtered(), 1); p = setup_picker("1111111", "* * ", "1101111", ""); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), false); TEST_EQUAL(p->has_piece_passed(2), true); TEST_EQUAL(p->num_passed(), 2); TEST_EQUAL(p->num_have(), 2); TEST_EQUAL(p->num_have_filtered(), 1); TEST_EQUAL(p->num_filtered(), 0); p->we_dont_have(2); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), false); TEST_EQUAL(p->has_piece_passed(2), false); TEST_EQUAL(p->num_passed(), 1); TEST_EQUAL(p->num_have(), 1); TEST_EQUAL(p->num_have_filtered(), 0); // ======================================================== print_title("test we dont have (don't have but passed hash check)"); p = setup_picker("1111111", "* * ", "1101111", "0200000"); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), false); TEST_EQUAL(p->have_piece(0), true) TEST_EQUAL(p->have_piece(1), false) p->piece_passed(1); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), true); TEST_EQUAL(p->have_piece(1), false) p->we_dont_have(1); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), false); TEST_EQUAL(p->have_piece(1), false) // ======================================================== print_title("test write_failed"); p = setup_picker("1111111", "* * ", "1101111", "0200000"); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), false); TEST_EQUAL(p->have_piece(1), false); p->piece_passed(1); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), true); TEST_EQUAL(p->have_piece(1), false); p->mark_as_writing(piece_block(1, 0), &tmp1); p->write_failed(piece_block(1, 0)); TEST_EQUAL(p->has_piece_passed(0), true); TEST_EQUAL(p->has_piece_passed(1), false); TEST_EQUAL(p->have_piece(1), false); // make sure write_failed() and lock_piece() actually // locks the piece, and that it won't be picked. // also make sure restore_piece() unlocks it and makes // it available for picking again. picked = pick_pieces(p, " * ", 1, blocks_per_piece, 0); TEST_EQUAL(picked.size(), 0); p->restore_piece(1); picked = pick_pieces(p, " * ", 1, blocks_per_piece, 0); TEST_EQUAL(picked.size(), blocks_per_piece); // locking pieces only works on partial pieces p->mark_as_writing(piece_block(1, 0), &tmp1); p->lock_piece(1); picked = pick_pieces(p, " * ", 1, blocks_per_piece, 0); TEST_EQUAL(picked.size(), 0); // ======================================================== print_title("test write_failed (clear piece)"); p = setup_picker("1111111", "* * ", "1101111", ""); stat = p->piece_stats(1); TEST_EQUAL(stat.downloading, 0); p->mark_as_writing(piece_block(1, 0), &tmp1); stat = p->piece_stats(1); TEST_EQUAL(stat.downloading, 1); p->write_failed(piece_block(1, 0)); stat = p->piece_stats(1); TEST_EQUAL(stat.downloading, 0); // ======================================================== print_title("test mark_as_canceled"); p = setup_picker("1111111", "* * ", "1101111", ""); stat = p->piece_stats(1); TEST_EQUAL(stat.downloading, 0); p->mark_as_writing(piece_block(1, 0), &tmp1); stat = p->piece_stats(1); TEST_EQUAL(stat.downloading, 1); p->mark_as_canceled(piece_block(1, 0), &tmp1); stat = p->piece_stats(1); TEST_EQUAL(stat.downloading, 0); // ======================================================== print_title("test get_download_queue"); p = setup_picker("1111111", " ", "1101111", "0327000"); std::vector downloads = p->get_download_queue(); // the download queue should have piece 1, 2 and 3 in it TEST_EQUAL(downloads.size(), 3); TEST_CHECK(std::find_if(downloads.begin(), downloads.end() , boost::bind(&piece_picker::downloading_piece::index, _1) == boost::uint32_t(1)) != downloads.end()); TEST_CHECK(std::find_if(downloads.begin(), downloads.end() , boost::bind(&piece_picker::downloading_piece::index, _1) == boost::uint32_t(2)) != downloads.end()); TEST_CHECK(std::find_if(downloads.begin(), downloads.end() , boost::bind(&piece_picker::downloading_piece::index, _1) == boost::uint32_t(3)) != downloads.end()); // ======================================================== print_title("test get_download_queue_size"); p = setup_picker("1111111", " ", "1111111", "0327ff0"); TEST_EQUAL(p->get_download_queue_size(), 5); p->set_piece_priority(1, 0); int partial; int full; int finished; int zero_prio; p->get_download_queue_sizes(&partial, &full, &finished, &zero_prio); TEST_EQUAL(partial, 2); TEST_EQUAL(full, 0); TEST_EQUAL(finished, 2); TEST_EQUAL(zero_prio, 1); // ======================================================== print_title("test time_critical_mode"); p = setup_picker("1111111", " ", "1654741", "0352000"); // rarest-first picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, tmp_peer , piece_picker::rarest_first | piece_picker::time_critical_mode, empty_vector); TEST_EQUAL(picked.size(), blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_EQUAL(picked[0].piece_index, 4); // reverse rarest-first picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, tmp_peer , piece_picker::reverse | piece_picker::rarest_first | piece_picker::time_critical_mode, empty_vector); TEST_EQUAL(picked.size(), blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_EQUAL(picked[0].piece_index, 4); // sequential picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, tmp_peer , piece_picker::sequential | piece_picker::time_critical_mode, empty_vector); TEST_EQUAL(picked.size(), blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_EQUAL(picked[0].piece_index, 4); // reverse sequential picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, tmp_peer , piece_picker::reverse | piece_picker::sequential | piece_picker::time_critical_mode, empty_vector); TEST_EQUAL(picked.size(), blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_EQUAL(picked[0].piece_index, 4); // just critical picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, tmp_peer , piece_picker::time_critical_mode, empty_vector); TEST_EQUAL(picked.size(), blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_EQUAL(picked[0].piece_index, 4); // prioritize_partials picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, tmp_peer , piece_picker::prioritize_partials | piece_picker::time_critical_mode, empty_vector); TEST_EQUAL(picked.size(), blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_EQUAL(picked[0].piece_index, 4); // even when a non-critical piece is suggested should we ignore it picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, tmp_peer , piece_picker::rarest_first | piece_picker::time_critical_mode , suggested_pieces); TEST_EQUAL(picked.size(), blocks_per_piece); for (int i = 0; i < int(picked.size()); ++i) TEST_EQUAL(picked[0].piece_index, 4); { print_title("test mark_as_pad"); p = setup_picker("1111111", " ", "4444444", ""); piece_block const bl(2, 0); p->mark_as_pad(bl); bool ret = p->mark_as_downloading(piece_block(2, 1), NULL); TEST_EQUAL(ret, true); std::vector dl = p->get_download_queue(); TEST_EQUAL(dl.size(), 1); TEST_EQUAL(dl[0].finished, 1); TEST_EQUAL(dl[0].writing, 0); TEST_EQUAL(dl[0].requested, 1); TEST_EQUAL(dl[0].index, 2); piece_picker::block_info* blocks = p->blocks_for_piece(dl[0]); TEST_EQUAL(blocks[0].state, piece_picker::block_info::state_finished); TEST_EQUAL(blocks[1].state, piece_picker::block_info::state_requested); TEST_EQUAL(blocks[2].state, piece_picker::block_info::state_none); TEST_EQUAL(blocks[3].state, piece_picker::block_info::state_none); } { print_title("test mark_as_pad downloading"); p = setup_picker("1111111", " ", "4444444", ""); piece_block const bl(2, 0); p->mark_as_pad(bl); bool ret = p->mark_as_downloading(piece_block(2, 0), NULL); TEST_EQUAL(ret, false); std::vector dl = p->get_download_queue(); TEST_EQUAL(dl.size(), 1); TEST_EQUAL(dl[0].finished, 1); TEST_EQUAL(dl[0].writing, 0); TEST_EQUAL(dl[0].requested, 0); TEST_EQUAL(dl[0].index, 2); piece_picker::block_info* blocks = p->blocks_for_piece(dl[0]); TEST_EQUAL(blocks[0].state, piece_picker::block_info::state_finished); TEST_EQUAL(blocks[1].state, piece_picker::block_info::state_none); TEST_EQUAL(blocks[2].state, piece_picker::block_info::state_none); TEST_EQUAL(blocks[3].state, piece_picker::block_info::state_none); } { print_title("test mark_as_pad seeding"); p = setup_picker("1", " ", "4", ""); p->mark_as_pad(piece_block(0, 0)); p->mark_as_pad(piece_block(0, 1)); p->mark_as_pad(piece_block(0, 2)); TEST_CHECK(!p->is_seeding()); p->mark_as_finished(piece_block(0, 3), NULL); TEST_CHECK(!p->is_seeding()); p->piece_passed(0); TEST_CHECK(p->is_seeding()); } { print_title("test mark_as_pad whole pad piece, seeding"); p = setup_picker("11", " ", "44", ""); p->mark_as_pad(piece_block(0, 0)); p->mark_as_pad(piece_block(0, 1)); p->mark_as_pad(piece_block(0, 2)); p->mark_as_pad(piece_block(0, 3)); TEST_CHECK(!p->is_seeding()); p->mark_as_finished(piece_block(1, 0), NULL); p->mark_as_finished(piece_block(1, 1), NULL); p->mark_as_finished(piece_block(1, 2), NULL); p->mark_as_finished(piece_block(1, 3), NULL); TEST_CHECK(!p->is_seeding()); p->piece_passed(1); TEST_CHECK(p->is_seeding()); } } libtorrent-rasterbar-1.1.13/test/test_primitives.cpp000066400000000000000000000142131351156116000226610ustar00rootroot00000000000000/* Copyright (c) 2008-2012, 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/entry.hpp" #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/socket_io.hpp" // for print_endpoint #include "libtorrent/announce_entry.hpp" #include "libtorrent/fingerprint.hpp" #include "test.hpp" #include "setup_transfer.hpp" using namespace libtorrent; sha1_hash to_hash(char const* s) { sha1_hash ret; from_hex(s, 40, (char*)&ret[0]); return ret; } address_v4 v4(char const* str) { error_code ec; return address_v4::from_string(str, ec); } #if TORRENT_USE_IPV6 address_v6 v6(char const* str) { error_code ec; return address_v6::from_string(str, ec); } #endif TORRENT_TEST(primitives) { using namespace libtorrent; error_code ec; // make sure the retry interval keeps growing // on failing announces announce_entry ae("dummy"); int last = 0; aux::session_settings sett; sett.set_int(settings_pack::tracker_backoff, 250); for (int i = 0; i < 10; ++i) { ae.failed(sett, 5); int delay = ae.next_announce_in(); TEST_CHECK(delay > last); last = delay; fprintf(stdout, "%d, ", delay); } fprintf(stdout, "\n"); // test error codes TEST_CHECK(error_code(errors::http_error).message() == "HTTP error"); TEST_CHECK(error_code(errors::missing_file_sizes).message() == "missing or invalid 'file sizes' entry"); TEST_CHECK(error_code(errors::unsupported_protocol_version).message() == "unsupported protocol version"); TEST_CHECK(error_code(errors::no_i2p_router).message() == "no i2p router is set up"); TEST_CHECK(error_code(errors::http_parse_error).message() == "Invalid HTTP header"); TEST_CHECK(error_code(errors::error_code_max).message() == "Unknown error"); TEST_CHECK(error_code(errors::unauthorized, http_category()).message() == "401 Unauthorized"); TEST_CHECK(error_code(errors::service_unavailable, http_category()).message() == "503 Service Unavailable"); // test snprintf char msg[10]; snprintf(msg, sizeof(msg), "too %s format string", "long"); TEST_CHECK(strcmp(msg, "too long ") == 0); if (supports_ipv6()) { // make sure the assumption we use in policy's peer list hold std::multimap peers; std::multimap::iterator i; peers.insert(std::make_pair(address::from_string("::1", ec), 0)); peers.insert(std::make_pair(address::from_string("::2", ec), 3)); peers.insert(std::make_pair(address::from_string("::3", ec), 5)); i = peers.find(address::from_string("::2", ec)); TEST_CHECK(i != peers.end()); if (i != peers.end()) { TEST_CHECK(i->first == address::from_string("::2", ec)); TEST_CHECK(i->second == 3); } } // test network functions // CIDR distance test sha1_hash h1 = to_hash("0123456789abcdef01232456789abcdef0123456"); sha1_hash h2 = to_hash("0123456789abcdef01232456789abcdef0123456"); TEST_CHECK(common_bits(&h1[0], &h2[0], 20) == 160); h2 = to_hash("0120456789abcdef01232456789abcdef0123456"); TEST_CHECK(common_bits(&h1[0], &h2[0], 20) == 14); h2 = to_hash("012f456789abcdef01232456789abcdef0123456"); TEST_CHECK(common_bits(&h1[0], &h2[0], 20) == 12); h2 = to_hash("0123456789abcdef11232456789abcdef0123456"); TEST_CHECK(common_bits(&h1[0], &h2[0], 20) == 16 * 4 + 3); // test print_endpoint, parse_endpoint and print_address TEST_EQUAL(print_endpoint(ep("127.0.0.1", 23)), "127.0.0.1:23"); #if TORRENT_USE_IPV6 TEST_EQUAL(print_endpoint(ep("ff::1", 1214)), "[ff::1]:1214"); #endif ec.clear(); TEST_EQUAL(parse_endpoint("127.0.0.1:23", ec), ep("127.0.0.1", 23)); TEST_CHECK(!ec); ec.clear(); #if TORRENT_USE_IPV6 TEST_EQUAL(parse_endpoint(" \t[ff::1]:1214 \r", ec), ep("ff::1", 1214)); TEST_CHECK(!ec); #endif TEST_EQUAL(print_address(v4("241.124.23.5")), "241.124.23.5"); #if TORRENT_USE_IPV6 TEST_EQUAL(print_address(v6("2001:ff::1")), "2001:ff::1"); parse_endpoint("[ff::1]", ec); TEST_EQUAL(ec, error_code(errors::invalid_port)); #endif parse_endpoint("[ff::1:5", ec); TEST_EQUAL(ec, error_code(errors::expected_close_bracket_in_address)); // test address_to_bytes TEST_EQUAL(address_to_bytes(address_v4::from_string("10.11.12.13")), "\x0a\x0b\x0c\x0d"); TEST_EQUAL(address_to_bytes(address_v4::from_string("16.5.127.1")), "\x10\x05\x7f\x01"); // test endpoint_to_bytes TEST_EQUAL(endpoint_to_bytes(udp::endpoint(address_v4::from_string("10.11.12.13"), 8080)), "\x0a\x0b\x0c\x0d\x1f\x90"); TEST_EQUAL(endpoint_to_bytes(udp::endpoint(address_v4::from_string("16.5.127.1"), 12345)), "\x10\x05\x7f\x01\x30\x39"); // test gen_fingerprint TEST_EQUAL(generate_fingerprint("AB", 1, 2, 3, 4), "-AB1234-"); TEST_EQUAL(generate_fingerprint("AB", 1, 2), "-AB1200-"); TEST_EQUAL(generate_fingerprint("..", 1, 10), "-..1A00-"); TEST_EQUAL(generate_fingerprint("CZ", 1, 15), "-CZ1F00-"); TEST_EQUAL(generate_fingerprint("CZ", 1, 15, 16, 17), "-CZ1FGH-"); } libtorrent-rasterbar-1.1.13/test/test_priority.cpp000066400000000000000000000360501351156116000223520ustar00rootroot00000000000000/* Copyright (c) 2008-2013, 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/session.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/time.hpp" #include "libtorrent/file.hpp" #include "libtorrent/torrent_info.hpp" #include #include #include "test.hpp" #include "setup_transfer.hpp" #include "settings.hpp" #include #include using namespace libtorrent; namespace lt = libtorrent; using boost::tuples::ignore; int peer_disconnects = 0; bool on_alert(alert const* a) { if (alert_cast(a)) ++peer_disconnects; else if (alert_cast(a)) ++peer_disconnects; return false; } void cleanup() { error_code ec; remove_all("tmp1_priorities", ec); remove_all("tmp2_priorities", ec); remove_all("tmp1_priorities_moved", ec); remove_all("tmp2_priorities_moved", ec); } void test_transfer(settings_pack const& sett) { // this allows shutting down the sessions in parallel std::vector sp; cleanup(); settings_pack pack = sett; // we need a short reconnect time since we // finish the torrent and then restart it // immediately to complete the second half. // using a reconnect time > 0 will just add // to the time it will take to complete the test pack.set_int(settings_pack::min_reconnect_time, 0); pack.set_bool(settings_pack::enable_outgoing_utp, false); pack.set_bool(settings_pack::enable_incoming_utp, false); pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled); pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled); pack.set_bool(settings_pack::allow_multiple_connections_per_ip, false); pack.set_int(settings_pack::unchoke_slots_limit, 8); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_dht, false); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); #ifndef TORRENT_NO_DEPRECATE pack.set_bool(settings_pack::rate_limit_utp, true); #endif lt::session ses1(pack); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:49075"); lt::session ses2(pack); torrent_handle tor1; torrent_handle tor2; error_code ec; create_directory("tmp1_priority", ec); std::ofstream file("tmp1_priority/temporary"); boost::shared_ptr t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false); file.close(); wait_for_listen(ses1, "ses1"); wait_for_listen(ses2, "ses2"); peer_disconnects = 0; // test using piece sizes smaller than 16kB boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 , true, false, true, "_priority", 8 * 1024, &t, false, 0); int num_pieces = tor2.torrent_file()->num_pieces(); std::vector priorities(num_pieces, 1); // set half of the pieces to priority 0 std::fill(priorities.begin(), priorities.begin() + (num_pieces / 2), 0); tor2.prioritize_pieces(priorities); std::cerr << "setting priorities: "; std::copy(priorities.begin(), priorities.end(), std::ostream_iterator(std::cerr, ", ")); std::cerr << std::endl; for (int i = 0; i < 200; ++i) { print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); torrent_status st1 = tor1.status(); torrent_status st2 = tor2.status(); if (i % 10 == 0) { print_ses_rate(i / 10.f, &st1, &st2); } // st2 is finished when we have downloaded half of the pieces if (st2.is_finished) break; if (st2.state != torrent_status::downloading) { static char const* state_str[] = {"checking (q)", "checking", "dl metadata" , "downloading", "finished", "seeding", "allocating", "checking (r)"}; std::cerr << "st2 state: " << state_str[st2.state] << std::endl; } TEST_CHECK(st1.state == torrent_status::seeding || st1.state == torrent_status::checking_resume_data || st1.state == torrent_status::checking_files); TEST_CHECK(st2.state == torrent_status::downloading || st2.state == torrent_status::checking_resume_data); if (peer_disconnects >= 2) break; // if nothing is being transferred after 2 seconds, we're failing the test if (st1.upload_payload_rate == 0 && i > 20) break; test_sleep(100); } TEST_CHECK(!tor2.status().is_seeding); TEST_CHECK(tor2.status().is_finished); if (tor2.status().is_finished) std::cerr << "torrent is finished (50% complete)" << std::endl; else return; std::vector priorities2 = tor2.piece_priorities(); std::copy(priorities2.begin(), priorities2.end(), std::ostream_iterator(std::cerr, ", ")); std::cerr << std::endl; TEST_CHECK(std::equal(priorities.begin(), priorities.end(), priorities2.begin())); std::cerr << "force recheck" << std::endl; tor2.force_recheck(); priorities2 = tor2.piece_priorities(); std::copy(priorities2.begin(), priorities2.end(), std::ostream_iterator(std::cerr, ", ")); std::cerr << std::endl; TEST_CHECK(std::equal(priorities.begin(), priorities.end(), priorities2.begin())); peer_disconnects = 0; // when we're done checking, we're likely to be put in downloading state // for a split second before transitioning to finished. This loop waits // for the finished state torrent_status st2; for (int i = 0; i < 50; ++i) { print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); st2 = tor2.status(); if (i % 10 == 0) { std::cerr << int(st2.progress * 100) << "% " << std::endl; } if (st2.state == torrent_status::finished) break; test_sleep(100); } TEST_EQUAL(st2.state, torrent_status::finished); if (st2.state != torrent_status::finished) return; std::cerr << "recheck complete" << std::endl; priorities2 = tor2.piece_priorities(); std::copy(priorities2.begin(), priorities2.end(), std::ostream_iterator(std::cerr, ", ")); std::cerr << std::endl; TEST_CHECK(std::equal(priorities.begin(), priorities.end(), priorities2.begin())); tor2.pause(); wait_for_alert(ses2, torrent_paused_alert::alert_type, "ses2"); fprintf(stderr, "save resume data\n"); tor2.save_resume_data(); std::vector resume_data; time_point start = clock_type::now(); while (true) { ses2.wait_for_alert(seconds(10)); std::vector alerts; ses2.pop_alerts(&alerts); if (alerts.empty()) break; for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { alert* a = *i; std::cerr << "ses2: " << a->message() << std::endl; if (alert_cast(a)) { bencode(std::back_inserter(resume_data) , *alert_cast(a)->resume_data); fprintf(stderr, "saved resume data\n"); goto done; } else if (alert_cast(a)) { fprintf(stderr, "save resume failed\n"); goto done; } if (total_seconds(clock_type::now() - start) > 10) goto done; } } done: TEST_CHECK(resume_data.size()); if (resume_data.empty()) return; fprintf(stderr, "%s\n", &resume_data[0]); ses2.remove_torrent(tor2); std::cerr << "removed" << std::endl; test_sleep(100); std::cout << "re-adding" << std::endl; add_torrent_params p; p.flags &= ~add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; p.ti = t; p.save_path = "tmp2_priority"; p.resume_data = resume_data; tor2 = ses2.add_torrent(p, ec); tor2.prioritize_pieces(priorities); std::cout << "resetting priorities" << std::endl; tor2.resume(); // wait for torrent 2 to settle in back to finished state (it will // start as checking) torrent_status st1; for (int i = 0; i < 5; ++i) { print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); st1 = tor1.status(); st2 = tor2.status(); TEST_CHECK(st1.state == torrent_status::seeding); if (st2.is_finished) break; test_sleep(100); } // torrent 2 should not be seeding yet, it should // just be 50% finished TEST_CHECK(!st2.is_seeding); TEST_CHECK(st2.is_finished); std::fill(priorities.begin(), priorities.end(), 1); tor2.prioritize_pieces(priorities); std::cout << "setting priorities to 1" << std::endl; TEST_EQUAL(tor2.status().is_finished, false); std::copy(priorities.begin(), priorities.end(), std::ostream_iterator(std::cerr, ", ")); std::cerr << std::endl; // drain alerts print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); peer_disconnects = 0; // this loop makes sure ses2 reconnects to the peer now that it's // in download mode again. If this fails, the reconnect logic may // not work or be inefficient st1 = tor1.status(); st2 = tor2.status(); for (int i = 0; i < 130; ++i) { print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); st1 = tor1.status(); st2 = tor2.status(); if (i % 10 == 0) print_ses_rate(i / 10.f, &st1, &st2); if (st2.is_seeding) break; TEST_EQUAL(st1.state, torrent_status::seeding); TEST_EQUAL(st2.state, torrent_status::downloading); if (peer_disconnects >= 2) { fprintf(stderr, "too many disconnects (%d), exiting\n", peer_disconnects); break; } test_sleep(100); } st2 = tor2.status(); if (!st2.is_seeding) fprintf(stderr, "ses2 failed to reconnect to ses1!\n"); TEST_CHECK(st2.is_seeding); sp.push_back(ses1.abort()); sp.push_back(ses2.abort()); } TORRENT_TEST(priority) { using namespace libtorrent; settings_pack p = settings(); test_transfer(p); cleanup(); } // test to set piece and file priority on a torrent that doesn't have metadata // yet TORRENT_TEST(no_metadata_prioritize_files) { settings_pack pack = settings(); lt::session ses(pack); add_torrent_params addp; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; addp.info_hash = sha1_hash("abababababababababab"); addp.save_path = "."; torrent_handle h = ses.add_torrent(addp); std::vector prios(3); prios[0] = 0; h.prioritize_files(prios); // TODO 2: this should wait for an alert instead of just sleeping test_sleep(100); TEST_CHECK(h.file_priorities() == prios); prios[0] = 1; h.prioritize_files(prios); test_sleep(100); TEST_CHECK(h.file_priorities() == prios); ses.remove_torrent(h); } TORRENT_TEST(no_metadata_file_prio) { settings_pack pack = settings(); lt::session ses(pack); add_torrent_params addp; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; addp.info_hash = sha1_hash("abababababababababab"); addp.save_path = "."; torrent_handle h = ses.add_torrent(addp); h.file_priority(0, 0); // TODO 2: this should wait for an alert instead of just sleeping test_sleep(100); TEST_EQUAL(h.file_priority(0), 0); h.file_priority(0, 1); test_sleep(100); TEST_EQUAL(h.file_priority(0), 1); ses.remove_torrent(h); } TORRENT_TEST(no_metadata_piece_prio) { settings_pack pack = settings(); lt::session ses(pack); add_torrent_params addp; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; addp.info_hash = sha1_hash("abababababababababab"); addp.save_path = "."; torrent_handle h = ses.add_torrent(addp); // you can't set piece priorities before the metadata has been downloaded h.piece_priority(2, 0); TEST_EQUAL(h.piece_priority(2), 4); h.piece_priority(2, 1); TEST_EQUAL(h.piece_priority(2), 4); ses.remove_torrent(h); } TORRENT_TEST(export_file_while_seed) { settings_pack pack = settings(); lt::session ses(pack); error_code ec; create_directory("tmp2_priority", ec); std::ofstream file("tmp2_priority/temporary"); boost::shared_ptr t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false); file.close(); add_torrent_params addp; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; addp.save_path = "."; addp.ti = t; torrent_handle h = ses.add_torrent(addp); // write to the partfile h.file_priority(0, 0); std::vector piece(16 * 1024); for (int i = 0; i < int(piece.size()); ++i) piece[i] = (i % 26) + 'A'; for (int i = 0; i < t->num_pieces(); ++i) h.add_piece(i, &piece[0], piece.size()); TEST_CHECK(!exists("temporary")); for (int i = 0; i < 10; ++i) { if (h.status().is_seeding) break; test_sleep(100); } TEST_EQUAL(h.status().is_seeding, true); // this should cause the file to be exported h.file_priority(0, 1); for (int i = 0; i < 10; ++i) { if (h.file_priority(0) == 1) break; test_sleep(100); } TEST_CHECK(exists("temporary")); } TORRENT_TEST(test_piece_priority_after_resume) { int const new_prio = 1; std::vector fast_resume_buf; boost::shared_ptr ti = generate_torrent(); { int const prio = 6; add_torrent_params p; p.save_path = "."; p.ti = ti; p.file_priorities.resize(1, prio); lt::session ses(settings()); torrent_handle h = ses.add_torrent(p); TEST_EQUAL(h.piece_priority(0), prio); std::vector > piece_prios; piece_prios.push_back(std::make_pair(0, new_prio)); h.prioritize_pieces(piece_prios); TEST_EQUAL(h.piece_priority(0), new_prio); ses.pause(); h.save_resume_data(); alert const* a = wait_for_alert(ses, save_resume_data_alert::alert_type); save_resume_data_alert const* rd = alert_cast(a); bencode(std::back_inserter(fast_resume_buf), *(rd->resume_data)); } { add_torrent_params p; p.save_path = "."; p.ti = ti; p.resume_data = fast_resume_buf; lt::session ses(settings()); torrent_handle h = ses.add_torrent(p); TEST_EQUAL(h.piece_priority(0), new_prio); } } libtorrent-rasterbar-1.1.13/test/test_privacy.cpp000066400000000000000000000224141351156116000221450ustar00rootroot00000000000000/* Copyright (c) 2013, 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 "test.hpp" #include "setup_transfer.hpp" #include "dht_server.hpp" #include "peer_server.hpp" #include "udp_tracker.hpp" #include "test_utils.hpp" #include "settings.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/random.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/torrent_info.hpp" #include using namespace libtorrent; namespace lt = libtorrent; char const* proxy_name[] = { "none", "socks4", "socks5", "socks5_pw", "http", "http_pw", "i2p_proxy" }; std::vector rejected_trackers; bool alert_predicate(libtorrent::alert const* a) { anonymous_mode_alert const* am = alert_cast(a); if (am == NULL) return false; if (am->kind == anonymous_mode_alert::tracker_not_anonymous) rejected_trackers.push_back(am->str); return false; } enum flags_t { force_proxy_mode = 1, expect_http_connection = 2, expect_udp_connection = 4, expect_http_reject = 8, expect_udp_reject = 16, expect_dht_msg = 32, expect_peer_connection = 64, expect_possible_udp_connection = 128, expect_possible_dht_msg = 256, }; session_proxy test_proxy(settings_pack::proxy_type_t proxy_type, int flags) { #ifdef TORRENT_DISABLE_DHT // if DHT is disabled, we won't get any requests to it flags &= ~expect_dht_msg; #endif fprintf(stderr, "\n=== TEST == proxy: %s anonymous-mode: %s\n\n", proxy_name[proxy_type], (flags & force_proxy_mode) ? "yes" : "no"); int const http_port = start_web_server(); int const udp_port = start_udp_tracker(); int const dht_port = start_dht(); int const peer_port = start_peer(); int const prev_udp_announces = num_udp_announces(); settings_pack sett = settings(); sett.set_int(settings_pack::stop_tracker_timeout, 2); sett.set_int(settings_pack::tracker_completion_timeout, 2); sett.set_int(settings_pack::tracker_receive_timeout, 2); sett.set_bool(settings_pack::announce_to_all_trackers, true); sett.set_bool(settings_pack::announce_to_all_tiers, true); sett.set_bool(settings_pack::force_proxy, flags & force_proxy_mode); sett.set_bool(settings_pack::enable_upnp, false); sett.set_bool(settings_pack::enable_natpmp, false); sett.set_bool(settings_pack::enable_lsd, false); sett.set_bool(settings_pack::enable_dht, true); // since multiple sessions may exist simultaneously (because of the // pipelining of the tests) they actually need to use different ports static int listen_port = 10000 + libtorrent::random() % 50000; char iface[200]; snprintf(iface, sizeof(iface), "127.0.0.1:%d", listen_port); listen_port += (libtorrent::random() % 10) + 1; sett.set_str(settings_pack::listen_interfaces, iface); // if we don't do this, the peer connection test // will be delayed by several seconds, by first // trying uTP sett.set_bool(settings_pack::enable_outgoing_utp, false); // in non-anonymous mode we circumvent/ignore the proxy if it fails // wheras in anonymous mode, we just fail sett.set_str(settings_pack::proxy_hostname, "non-existing.com"); sett.set_int(settings_pack::proxy_type, proxy_type); sett.set_int(settings_pack::proxy_port, 4444); lt::session* s = new lt::session(sett); error_code ec; remove_all("tmp1_privacy", ec); create_directory("tmp1_privacy", ec); std::ofstream file(combine_path("tmp1_privacy", "temporary").c_str()); boost::shared_ptr t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false); file.close(); char http_tracker_url[200]; snprintf(http_tracker_url, sizeof(http_tracker_url), "http://127.0.0.1:%d/announce", http_port); t->add_tracker(http_tracker_url, 0); char udp_tracker_url[200]; snprintf(udp_tracker_url, sizeof(udp_tracker_url), "udp://127.0.0.1:%d/announce", udp_port); t->add_tracker(udp_tracker_url, 1); add_torrent_params addp; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; // we don't want to waste time checking the torrent, just go straight into // seeding it, announcing to trackers and connecting to peers addp.flags |= add_torrent_params::flag_seed_mode; addp.ti = t; addp.save_path = "tmp1_privacy"; addp.dht_nodes.push_back(std::pair("127.0.0.1", dht_port)); torrent_handle h = s->add_torrent(addp); printf("connect_peer: 127.0.0.1:%d\n", peer_port); h.connect_peer(tcp::endpoint(address_v4::from_string("127.0.0.1"), peer_port)); rejected_trackers.clear(); #ifdef TORRENT_USE_VALGRIND const int timeout = 100; #else const int timeout = 20; #endif for (int i = 0; i < timeout; ++i) { print_alerts(*s, "s", false, false, false, &alert_predicate); test_sleep(100); if (num_udp_announces() >= prev_udp_announces + 1 && num_peer_hits() > 0) break; } // we should have announced to the tracker by now if (flags & expect_possible_udp_connection) { // this flag is true if we may fail open, but also might not have had // enough time to fail yet TEST_CHECK(num_udp_announces() == prev_udp_announces || num_udp_announces() == prev_udp_announces + 1); } else { TEST_EQUAL(num_udp_announces(), prev_udp_announces + bool(flags & expect_udp_connection)); } if (flags & expect_possible_udp_connection) { // this flag is true if we may fail open, but also might not have had // enough time to fail yet TEST_CHECK(num_dht_hits() == 0 || num_dht_hits() == 1); } else { if (flags & expect_dht_msg) { TEST_CHECK(num_dht_hits() > 0); } else { TEST_EQUAL(num_dht_hits(), 0); } } if (flags & expect_peer_connection) { TEST_CHECK(num_peer_hits() > 0); } else { TEST_EQUAL(num_peer_hits(), 0); } if (flags & expect_udp_reject) TEST_CHECK(std::find(rejected_trackers.begin(), rejected_trackers.end(), udp_tracker_url) != rejected_trackers.end()); if (flags & expect_http_reject) TEST_CHECK(std::find(rejected_trackers.begin(), rejected_trackers.end(), http_tracker_url) != rejected_trackers.end()); fprintf(stderr, "%s: ~session\n", time_now_string()); session_proxy pr = s->abort(); delete s; stop_peer(); stop_dht(); stop_udp_tracker(); stop_web_server(); return pr; } // not using anonymous mode // UDP fails open if we can't connect to the proxy // or if the proxy doesn't support UDP TORRENT_TEST(no_proxy) { test_proxy(settings_pack::none, expect_udp_connection | expect_http_connection | expect_dht_msg | expect_peer_connection); } TORRENT_TEST(socks4) { test_proxy(settings_pack::socks4, expect_udp_connection | expect_dht_msg); } TORRENT_TEST(socks5) { test_proxy(settings_pack::socks5, expect_possible_udp_connection | expect_possible_dht_msg); } TORRENT_TEST(socks5_pw) { test_proxy(settings_pack::socks5_pw,expect_possible_udp_connection | expect_possible_dht_msg); } TORRENT_TEST(http) { test_proxy(settings_pack::http, expect_udp_connection | expect_dht_msg); } TORRENT_TEST(http_pt) { test_proxy(settings_pack::http_pw, expect_udp_connection | expect_dht_msg); } #if TORRENT_USE_I2P TORRENT_TEST(i2p) { test_proxy(settings_pack::i2p_proxy, expect_udp_connection | expect_dht_msg); } #endif // using anonymous mode // anonymous mode doesn't require a proxy when one isn't configured. It could be // used with a VPN for instance. This will all changed in 1.0, where anonymous // mode is separated from force_proxy TORRENT_TEST(anon_no_proxy) { test_proxy(settings_pack::none, force_proxy_mode | expect_peer_connection); } TORRENT_TEST(anon_socks4) { test_proxy(settings_pack::socks4, force_proxy_mode | expect_udp_reject); } TORRENT_TEST(anon_socks5) { test_proxy(settings_pack::socks5, force_proxy_mode); } TORRENT_TEST(anon_socks5_pw) { test_proxy(settings_pack::socks5_pw, force_proxy_mode); } TORRENT_TEST(anon_http) { test_proxy(settings_pack::http, force_proxy_mode | expect_udp_reject); } TORRENT_TEST(anon_http_pw) { test_proxy(settings_pack::http_pw, force_proxy_mode | expect_udp_reject); } #if TORRENT_USE_I2P TORRENT_TEST(anon_i2p) { test_proxy(settings_pack::i2p_proxy, force_proxy_mode); } #endif libtorrent-rasterbar-1.1.13/test/test_random.cpp000066400000000000000000000042651351156116000217540ustar00rootroot00000000000000/* Copyright (c) 2014, 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 "test.hpp" #include "setup_transfer.hpp" #include "libtorrent/random.hpp" using namespace libtorrent; TORRENT_TEST(random) { const int repetitions = 200000; for (int byte = 0; byte < 4; ++byte) { int buckets[256]; memset(buckets, 0, sizeof(buckets)); for (int i = 0; i < repetitions; ++i) { boost::uint32_t val = libtorrent::random(); val >>= byte * 8; ++buckets[val & 0xff]; } for (int i = 0; i < 256; ++i) { const int expected = repetitions / 256; // expect each bucket to be within 15% of the expected value fprintf(stderr, "%d: %f\n", i, float(buckets[i] - expected) * 100.f / expected); TEST_CHECK(abs(buckets[i] - expected) < expected / 6); } } } libtorrent-rasterbar-1.1.13/test/test_read_piece.cpp000066400000000000000000000112531351156116000225470ustar00rootroot00000000000000/* Copyright (c) 2013, 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/session.hpp" #include "test.hpp" #include "setup_transfer.hpp" #include "settings.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/torrent_info.hpp" enum flags_t { seed_mode = 1, time_critical = 2 }; void test_read_piece(int flags) { using namespace libtorrent; namespace lt = libtorrent; fprintf(stderr, "==== TEST READ PIECE =====\n"); // in case the previous run was terminated error_code ec; remove_all("tmp1_read_piece", ec); if (ec) fprintf(stderr, "ERROR: removing tmp1_read_piece: (%d) %s\n" , ec.value(), ec.message().c_str()); create_directory("tmp1_read_piece", ec); if (ec) fprintf(stderr, "ERROR: creating directory tmp1_read_piece: (%d) %s\n" , ec.value(), ec.message().c_str()); create_directory(combine_path("tmp1_read_piece", "test_torrent"), ec); if (ec) fprintf(stderr, "ERROR: creating directory test_torrent: (%d) %s\n" , ec.value(), ec.message().c_str()); file_storage fs; std::srand(10); int piece_size = 0x4000; static const int file_sizes[] = { 100000, 10000 }; create_random_files(combine_path("tmp1_read_piece", "test_torrent") , file_sizes, 2); add_files(fs, combine_path("tmp1_read_piece", "test_torrent")); libtorrent::create_torrent t(fs, piece_size, 0x4000); // calculate the hash for all pieces set_piece_hashes(t, "tmp1_read_piece", ec); if (ec) fprintf(stderr, "ERROR: set_piece_hashes: (%d) %s\n" , ec.value(), ec.message().c_str()); std::vector buf; bencode(std::back_inserter(buf), t.generate()); boost::shared_ptr ti(new torrent_info(&buf[0], buf.size(), ec)); fprintf(stderr, "generated torrent: %s tmp1_read_piece/test_torrent\n" , to_hex(ti->info_hash().to_string()).c_str()); settings_pack sett = settings(); sett.set_bool(settings_pack::enable_lsd, false); sett.set_bool(settings_pack::enable_natpmp, false); sett.set_bool(settings_pack::enable_upnp, false); sett.set_bool(settings_pack::enable_dht, false); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48000"); lt::session ses(sett); add_torrent_params p; p.flags &= ~add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; p.save_path = "tmp1_read_piece"; p.ti = ti; if (flags & seed_mode) p.flags |= add_torrent_params::flag_seed_mode; torrent_handle tor1 = ses.add_torrent(p, ec); TEST_CHECK(!ec); TEST_CHECK(tor1.is_valid()); alert const* a = wait_for_alert(ses, torrent_finished_alert::alert_type, "ses"); TEST_CHECK(a); TEST_CHECK(tor1.status().is_seeding); if (flags & time_critical) { tor1.set_piece_deadline(1, 0, torrent_handle::alert_when_available); } else { tor1.read_piece(1); } a = wait_for_alert(ses, read_piece_alert::alert_type, "ses"); TEST_CHECK(a); if (a) { read_piece_alert const* rp = alert_cast(a); TEST_CHECK(rp); if (rp) { TEST_EQUAL(rp->piece, 1); } } remove_all("tmp1_read_piece", ec); if (ec) fprintf(stderr, "ERROR: removing tmp1_read_piece: (%d) %s\n" , ec.value(), ec.message().c_str()); } TORRENT_TEST(read_piece) { test_read_piece(0); } TORRENT_TEST(seed_mode) { test_read_piece(seed_mode); } TORRENT_TEST(time_critical) { test_read_piece(time_critical); } libtorrent-rasterbar-1.1.13/test/test_receive_buffer.cpp000066400000000000000000000162711351156116000234470ustar00rootroot00000000000000/* Copyright (c) 2016, 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/config.hpp" #include "test.hpp" #include "libtorrent/receive_buffer.hpp" using namespace libtorrent; struct allocator : buffer_allocator_interface { void free_disk_buffer(char*) {} char* allocate_disk_buffer(char const*) { TORRENT_ASSERT(false); return NULL; } char* allocate_disk_buffer(bool& , boost::shared_ptr , char const*) { TORRENT_ASSERT(false); return NULL; } char* async_allocate_disk_buffer(char const* , boost::function const&) { TORRENT_ASSERT(false); return NULL; } void reclaim_block(block_cache_reference ref) {} }; TORRENT_TEST(recv_buffer_init) { allocator a; receive_buffer b(a); b.cut(0, 10); TEST_EQUAL(b.packet_size(), 10); TEST_EQUAL(b.packet_bytes_remaining(), 10); TEST_EQUAL(b.packet_finished(), false); TEST_EQUAL(b.pos(), 0); TEST_EQUAL(b.capacity(), 0); } TORRENT_TEST(recv_buffer_pos_at_end_false) { allocator a; receive_buffer b(a); b.cut(0, 1000); // allocate some space to receive into boost::array vec; int num_bufs = b.reserve(vec, 1000); // since we don't have a disk buffer, there should only be a single // range/buffer TEST_EQUAL(num_bufs, 1); b.received(1000); b.advance_pos(999); TEST_EQUAL(b.pos_at_end(), false); } TORRENT_TEST(recv_buffer_pos_at_end_true) { allocator a; receive_buffer b(a); b.cut(0, 1000); b.reserve(1000); boost::array vec; int num_bufs = b.reserve(vec, 1000); TEST_EQUAL(num_bufs, 1); b.received(1000); b.advance_pos(1000); TEST_EQUAL(b.pos_at_end(), true); } TORRENT_TEST(recv_buffer_packet_finished) { allocator a; receive_buffer b(a); // packet_size = 10 b.cut(0, 10); b.reserve(1000); boost::array vec; int num_bufs = b.reserve(vec, 1000); TEST_EQUAL(num_bufs, 1); b.received(1000); for (int i = 0; i < 10; ++i) { TEST_EQUAL(b.packet_finished(), false); b.advance_pos(1); } TEST_EQUAL(b.packet_finished(), true); } TORRENT_TEST(recv_buffer_disk_buffer) { char disk_buffer; // fake disk buffer pointer allocator a; receive_buffer b(a); b.reserve(1000); b.cut(0, 1000); // packet size = 1000 boost::array vec; b.assign_disk_buffer(&disk_buffer, 137); int num_bufs = b.reserve(vec, 1000); TEST_EQUAL(num_bufs, 2); // regular buffer disk buffer // -----------------====== // // |----------------------| 1000 // |-----| 137 // |----------------| 863 TEST_EQUAL(boost::asio::buffer_size(vec[0]), 863); TEST_EQUAL(boost::asio::buffer_size(vec[1]), 137); } #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) TORRENT_TEST(recv_buffer_mutable_buffers_regular_and_disk) { char disk_buffer; // fake disk buffer pointer allocator a; receive_buffer b(a); b.reserve(1100); b.cut(0, 100); // packet size = 100 b.received(1100); int packet_transferred = b.advance_pos(1100); // this is just the first packet TEST_EQUAL(packet_transferred, 100); // the next packet is 1000, and we're done with the first 100 bytes now b.cut(100, 1000); // packet size = 1000 // and it has a disk buffer b.assign_disk_buffer(&disk_buffer, 137); std::vector vec; packet_transferred = b.advance_pos(999); TEST_EQUAL(packet_transferred, 999); b.mutable_buffers(vec, 999); TEST_EQUAL(vec.size(), 2); // previous packet // | // v regular buffer disk buffer // - - - -----------------====== // ^ // | // m_recv_start // |----------------------| 1000 packet size // |-----| 137 disk buffer // |----------------| 863 regular buffer TEST_EQUAL(boost::asio::buffer_size(vec[0]), 863); TEST_EQUAL(boost::asio::buffer_size(vec[1]), 137 - 1); TEST_EQUAL(boost::asio::buffer_size(vec[0]) + boost::asio::buffer_size(vec[1]), 999); } TORRENT_TEST(recv_buffer_mutable_buffers_regular_only) { allocator a; receive_buffer b(a); b.reserve(1100); b.cut(0, 100); // packet size = 100 b.received(1100); int packet_transferred = b.advance_pos(1100); // this is just the first packet TEST_EQUAL(packet_transferred, 100); // the next packet is 1000, and we're done with the first 100 bytes now b.cut(100, 1000); // packet size = 1000 std::vector vec; packet_transferred = b.advance_pos(999); TEST_EQUAL(packet_transferred, 999); b.mutable_buffers(vec, 999); TEST_EQUAL(vec.size(), 1); // previous packet // | // v regular buffer // - - - ----------------------- // ^ // | // m_recv_start // |----------------------| 1000 packet size // |---------------------| 999 regular buffer TEST_EQUAL(boost::asio::buffer_size(vec[0]), 999); } TORRENT_TEST(recv_buffer_mutable_buffers_disk) { char disk_buffer; // fake disk buffer pointer allocator a; receive_buffer b(a); b.reserve(1100); b.cut(0, 100); // packet size = 100 b.received(1100); int packet_transferred = b.advance_pos(1100); // this is just the first packet TEST_EQUAL(packet_transferred, 100); // the next packet is 1000, and we're done with the first 100 bytes now b.cut(100, 1000); // packet size = 1000 // and it has a disk buffer b.assign_disk_buffer(&disk_buffer, 1000); std::vector vec; packet_transferred = b.advance_pos(999); TEST_EQUAL(packet_transferred, 999); b.mutable_buffers(vec, 999); TEST_EQUAL(vec.size(), 1); // previous packet // | // v disk buffer // - - - ======================= // ^ // | // m_recv_start // |----------------------| 1000 packet size // |----------------------| 999 disk buffer TEST_EQUAL(boost::asio::buffer_size(vec[0]), 999); TEST_EQUAL(boost::asio::buffer_cast(vec[0]), &disk_buffer); } #endif libtorrent-rasterbar-1.1.13/test/test_recheck.cpp000066400000000000000000000075511351156116000221010ustar00rootroot00000000000000/* Copyright (c) 2012, 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/session.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/time.hpp" #include "libtorrent/file.hpp" #include "libtorrent/error_code.hpp" #include #include #include "test.hpp" #include "setup_transfer.hpp" #include "settings.hpp" #include #include using namespace libtorrent; namespace lt = libtorrent; void wait_for_complete(lt::session& ses, torrent_handle h) { int last_progress = 0; clock_type::time_point last_change = clock_type::now(); for (int i = 0; i < 400; ++i) { print_alerts(ses, "ses1"); torrent_status st = h.status(); fprintf(stderr, "%f %%\n", st.progress_ppm / 10000.f); if (st.progress_ppm == 1000000) return; if (st.progress_ppm != last_progress) { last_progress = st.progress_ppm; last_change = clock_type::now(); } if (clock_type::now() - last_change > seconds(10)) break; test_sleep(500); } TEST_ERROR("torrent did not finish"); } TORRENT_TEST(recheck) { error_code ec; settings_pack sett = settings(); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48675"); sett.set_bool(settings_pack::enable_upnp, false); sett.set_bool(settings_pack::enable_natpmp, false); sett.set_bool(settings_pack::enable_lsd, false); sett.set_bool(settings_pack::enable_dht, false); lt::session ses1(sett); create_directory("tmp1_recheck", ec); if (ec) fprintf(stderr, "create_directory: %s\n", ec.message().c_str()); std::ofstream file("tmp1_recheck/temporary"); boost::shared_ptr t = ::create_torrent(&file, "temporary", 4 * 1024 * 1024 , 7, false); file.close(); add_torrent_params param; param.flags &= ~add_torrent_params::flag_paused; param.flags &= ~add_torrent_params::flag_auto_managed; param.ti = t; param.save_path = "tmp1_recheck"; param.flags |= add_torrent_params::flag_seed_mode; torrent_handle tor1 = ses1.add_torrent(param, ec); if (ec) fprintf(stderr, "add_torrent: %s\n", ec.message().c_str()); wait_for_listen(ses1, "ses1"); tor1.force_recheck(); torrent_status st1 = tor1.status(); TEST_CHECK(st1.progress_ppm <= 1000000); wait_for_complete(ses1, tor1); tor1.force_recheck(); st1 = tor1.status(); TEST_CHECK(st1.progress_ppm <= 1000000); wait_for_complete(ses1, tor1); } libtorrent-rasterbar-1.1.13/test/test_remap_files.cpp000066400000000000000000000357601351156116000227660ustar00rootroot00000000000000/* Copyright (c) 2014, 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/session.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/torrent_info.hpp" #include "setup_transfer.hpp" #include "settings.hpp" #include "test.hpp" #include #include using namespace libtorrent; namespace lt = libtorrent; using boost::tuples::ignore; namespace { template boost::shared_ptr clone_ptr(boost::shared_ptr const& ptr) { return boost::shared_ptr(new T(*ptr)); } int peer_disconnects = 0; bool on_alert(alert const* a) { if (alert_cast(a)) ++peer_disconnects; return false; } } // anonymous namespace void test_remap_files_gather(storage_mode_t storage_mode = storage_mode_sparse) { // in case the previous run was terminated error_code ec; session_proxy p1; session_proxy p2; settings_pack sett = settings(); sett.set_bool(settings_pack::enable_upnp, false); sett.set_bool(settings_pack::enable_natpmp, false); sett.set_bool(settings_pack::enable_lsd, false); sett.set_bool(settings_pack::enable_dht, false); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); lt::session ses1(sett); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:49075"); lt::session ses2(sett); torrent_handle tor1; torrent_handle tor2; create_directory("tmp1_remap", ec); create_directory(combine_path("tmp1_remap", "test_torrent_dir"), ec); if (ec) { fprintf(stderr, "error creating directory: %s\n" , ec.message().c_str()); TEST_CHECK(false); return; } static const int file_sizes[] = { 50, 16000-50, 16000, 1700, 100, 8000, 8000, 1,1,10,10,10,1000,10,10,10,10,1000,10,10,10,1,1,1 ,10,1000,1000,1000,10,1000,130,65000,340,750,20,300,400,5000,23000,900,43000,4000,43000,60, 40}; create_random_files(combine_path("tmp1_remap", "test_torrent_dir") , file_sizes, sizeof(file_sizes)/sizeof(file_sizes[0])); file_storage fs; // generate a torrent with pad files to make sure they // are not requested web seeds add_files(fs, combine_path("tmp1_remap", "test_torrent_dir")); libtorrent::create_torrent ct(fs, 0x8000, 0x4000); set_piece_hashes(ct, "tmp1_remap", ec); if (ec) { fprintf(stderr, "error creating hashes for test torrent: %s\n" , ec.message().c_str()); TEST_CHECK(false); return; } std::vector buf; bencode(std::back_inserter(buf), ct.generate()); boost::shared_ptr t(new torrent_info(&buf[0], buf.size(), ec)); boost::shared_ptr t2(new torrent_info(&buf[0], buf.size(), ec)); // remap the files to a single one file_storage st; st.add_file("single_file", t->total_size()); t2->remap_files(st); add_torrent_params params; params.storage_mode = storage_mode; params.flags &= ~add_torrent_params::flag_paused; params.flags &= ~add_torrent_params::flag_auto_managed; wait_for_listen(ses1, "ses1"); wait_for_listen(ses2, "ses2"); peer_disconnects = 0; // test using piece sizes smaller than 16kB boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 , true, false, true, "_remap", 8 * 1024, &t, false, ¶ms , true, false, &t2); fprintf(stderr, "\ntesting remap gather\n\n"); for (int i = 0; i < 50; ++i) { print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); torrent_status st1 = tor1.status(); torrent_status st2 = tor2.status(); if (i % 10 == 0) { print_ses_rate(i / 10.f, &st1, &st2); } if (st2.is_finished) break; if (st2.state != torrent_status::downloading) { static char const* state_str[] = {"checking (q)", "checking", "dl metadata" , "downloading", "finished", "seeding", "allocating", "checking (r)"}; std::cerr << "st2 state: " << state_str[st2.state] << std::endl; } TEST_CHECK(st1.state == torrent_status::seeding || st1.state == torrent_status::checking_resume_data || st1.state == torrent_status::checking_files); TEST_CHECK(st2.state == torrent_status::downloading || st2.state == torrent_status::checking_resume_data); if (peer_disconnects >= 2) break; test_sleep(100); } torrent_status st2 = tor2.status(); TEST_CHECK(st2.is_seeding); if (!st2.is_seeding) return; fprintf(stderr, "\ntesting force recheck\n\n"); // test force rechecking a seeding torrent with remapped files tor2.force_recheck(); for (int i = 0; i < 50; ++i) { print_alerts(ses2, "ses2", true, true, true, &on_alert); torrent_status st2 = tor2.status(); if (i % 10 == 0) { print_ses_rate(i / 10.f, NULL, &st2); } if (st2.state != torrent_status::checking_files) { static char const* state_str[] = {"checking (q)", "checking", "dl metadata" , "downloading", "finished", "seeding", "allocating", "checking (r)"}; std::cerr << "st2 state: " << state_str[st2.state] << std::endl; } if (st2.progress == 1.0) break; test_sleep(100); } st2 = tor2.status(); TEST_CHECK(st2.is_seeding); p1 = ses1.abort(); p2 = ses2.abort(); } void test_remap_files_scatter(storage_mode_t storage_mode = storage_mode_sparse) { int num_files = 10; // in case the previous run was terminated error_code ec; session_proxy p1; session_proxy p2; settings_pack sett = settings(); sett.set_bool(settings_pack::enable_upnp, false); sett.set_bool(settings_pack::enable_natpmp, false); sett.set_bool(settings_pack::enable_lsd, false); sett.set_bool(settings_pack::enable_dht, false); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); lt::session ses1(sett); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:49075"); lt::session ses2(sett); torrent_handle tor1; torrent_handle tor2; create_directory("tmp1_remap2", ec); std::ofstream file("tmp1_remap2/temporary"); boost::shared_ptr t = ::create_torrent(&file, "temporary", 32 * 1024, 7); file.close(); file_storage fs; for (int i = 0; i < num_files-1; ++i) { char name[100]; snprintf(name, sizeof(name), "multifile/file%d.txt", i); fs.add_file(name, t->total_size() / 10); } char name[100]; snprintf(name, sizeof(name), "multifile/file%d.txt", num_files); // the last file has to be a special case to make the size // add up exactly (in case the total size is not divisible by 10). fs.add_file(name, t->total_size() - fs.total_size()); boost::shared_ptr t2 = clone_ptr(t); t2->remap_files(fs); add_torrent_params params; params.storage_mode = storage_mode; params.flags &= ~add_torrent_params::flag_paused; params.flags &= ~add_torrent_params::flag_auto_managed; wait_for_listen(ses1, "ses1"); wait_for_listen(ses2, "ses2"); peer_disconnects = 0; // test using piece sizes smaller than 16kB boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 , true, false, true, "_remap2", 8 * 1024, &t, false, ¶ms , true, false, &t2); fprintf(stderr, "\ntesting remap scatter\n\n"); for (int i = 0; i < 50; ++i) { print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); torrent_status st1 = tor1.status(); torrent_status st2 = tor2.status(); if (i % 10 == 0) { print_ses_rate(i / 10.f, &st1, &st2); } if (st2.is_finished) break; if (st2.state != torrent_status::downloading) { static char const* state_str[] = {"checking (q)", "checking", "dl metadata" , "downloading", "finished", "seeding", "allocating", "checking (r)"}; std::cerr << "st2 state: " << state_str[st2.state] << std::endl; } TEST_CHECK(st1.state == torrent_status::seeding || st1.state == torrent_status::checking_resume_data || st1.state == torrent_status::checking_files); TEST_CHECK(st2.state == torrent_status::downloading || st2.state == torrent_status::checking_resume_data); if (peer_disconnects >= 2) break; test_sleep(100); } torrent_status st2 = tor2.status(); TEST_CHECK(st2.is_seeding); if (!st2.is_seeding) return; fprintf(stderr, "\ntesting force recheck\n\n"); // test force rechecking a seeding torrent with remapped files tor2.force_recheck(); for (int i = 0; i < 50; ++i) { print_alerts(ses2, "ses2", true, true, true, &on_alert); torrent_status st2 = tor2.status(); if (i % 10 == 0) { print_ses_rate(i / 10.f, NULL, &st2); } if (st2.state != torrent_status::checking_files) { static char const* state_str[] = {"checking (q)", "checking", "dl metadata" , "downloading", "finished", "seeding", "allocating", "checking (r)"}; std::cerr << "st2 state: " << state_str[st2.state] << std::endl; } if (st2.progress == 1.0) break; test_sleep(100); } st2 = tor2.status(); TEST_CHECK(st2.is_seeding); p1 = ses1.abort(); p2 = ses2.abort(); } void test_remap_files_prio(storage_mode_t storage_mode = storage_mode_sparse) { // in case the previous run was terminated error_code ec; session_proxy p1; session_proxy p2; settings_pack sett = settings(); sett.set_bool(settings_pack::enable_upnp, false); sett.set_bool(settings_pack::enable_natpmp, false); sett.set_bool(settings_pack::enable_lsd, false); sett.set_bool(settings_pack::enable_dht, false); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); lt::session ses1(sett); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:49075"); lt::session ses2(sett); torrent_handle tor1; torrent_handle tor2; create_directory("tmp1_remap3", ec); create_directory(combine_path("tmp1_remap3", "test_torrent_dir"), ec); // create a torrent with 2 files, remap them into 3 files and make sure // the file priorities don't break things static const int file_sizes[] = {100000, 100000}; const int num_files = sizeof(file_sizes)/sizeof(file_sizes[0]); create_random_files(combine_path("tmp1_remap3", "test_torrent_dir") , file_sizes, num_files); file_storage fs1; const int piece_size = 0x4000; add_files(fs1, combine_path("tmp1_remap3", "test_torrent_dir")); libtorrent::create_torrent ct(fs1, piece_size, 0x4000 , libtorrent::create_torrent::optimize_alignment); // calculate the hash for all pieces set_piece_hashes(ct, "tmp1_remap3", ec); if (ec) fprintf(stderr, "ERROR: set_piece_hashes: (%d) %s\n" , ec.value(), ec.message().c_str()); std::vector buf; bencode(std::back_inserter(buf), ct.generate()); boost::shared_ptr t(new torrent_info(&buf[0], buf.size(), ec)); int num_new_files = 3; file_storage fs; for (int i = 0; i < num_new_files-1; ++i) { char name[100]; snprintf(name, sizeof(name), "multifile/file%d.txt", i); fs.add_file(name, t->total_size() / 10); } char name[100]; snprintf(name, sizeof(name), "multifile/file%d.txt", num_new_files); // the last file has to be a special case to make the size // add up exactly (in case the total size is not divisible by 10). fs.add_file(name, t->total_size() - fs.total_size()); boost::shared_ptr t2 = clone_ptr(t); t2->remap_files(fs); add_torrent_params params; params.storage_mode = storage_mode; params.flags |= add_torrent_params::flag_paused; params.flags &= ~add_torrent_params::flag_auto_managed; wait_for_listen(ses1, "ses1"); wait_for_listen(ses2, "ses2"); peer_disconnects = 0; // test using piece sizes smaller than 16kB boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 , true, false, true, "_remap3", 8 * 1024, &t, false, ¶ms , true, false, &t2); std::vector file_prio(3, 1); file_prio[0] = 0; tor2.prioritize_files(file_prio); // torrent1 will attempt to connect to torrent2 // make sure torrent2 is up and running by then tor2.resume(); test_sleep(500); tor1.resume(); fprintf(stderr, "\ntesting remap scatter prio\n\n"); for (int i = 0; i < 50; ++i) { print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); torrent_status st1 = tor1.status(); torrent_status st2 = tor2.status(); if (i % 10 == 0) { std::cerr << "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s " << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " << "\033[0m" << int(st1.progress * 100) << "% " << st1.num_peers << ": " << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " << "\033[0m" << int(st2.progress * 100) << "% " << st2.num_peers << std::endl; } if (st2.is_finished) break; if (st2.state != torrent_status::downloading) { static char const* state_str[] = {"checking (q)", "checking", "dl metadata" , "downloading", "finished", "seeding", "allocating", "checking (r)"}; std::cerr << "st2 state: " << state_str[st2.state] << std::endl; } TEST_CHECK(st1.state == torrent_status::seeding); TEST_CHECK(st2.state == torrent_status::downloading || st2.state == torrent_status::checking_resume_data); if (peer_disconnects >= 2) break; test_sleep(100); } torrent_status st2 = tor2.status(); TEST_CHECK(st2.is_finished); p1 = ses1.abort(); p2 = ses2.abort(); } using namespace libtorrent; TORRENT_TEST(remap_files) { test_remap_files_gather(); error_code ec; remove_all("tmp1_remap", ec); remove_all("tmp2_remap", ec); remove_all("tmp1_remap2", ec); remove_all("tmp2_remap2", ec); } TORRENT_TEST(scatter) { test_remap_files_scatter(); error_code ec; remove_all("tmp1_remap", ec); remove_all("tmp2_remap", ec); remove_all("tmp1_remap2", ec); remove_all("tmp2_remap2", ec); remove_all("tmp1_remap3", ec); remove_all("tmp2_remap3", ec); } TORRENT_TEST(prio) { test_remap_files_prio(); error_code ec; remove_all("tmp1_remap", ec); remove_all("tmp2_remap", ec); remove_all("tmp1_remap2", ec); remove_all("tmp2_remap2", ec); remove_all("tmp1_remap3", ec); remove_all("tmp2_remap3", ec); } libtorrent-rasterbar-1.1.13/test/test_remove_torrent.cpp000066400000000000000000000131211351156116000235350ustar00rootroot00000000000000/* Copyright (c) 2017, 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/session.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/torrent_status.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/ip_filter.hpp" #include "test.hpp" #include "setup_transfer.hpp" #include "settings.hpp" #include #include #include using namespace libtorrent; namespace lt = libtorrent; using boost::tuples::ignore; enum test_case { complete_download, partial_download, mid_download }; void test_remove_torrent(int const remove_options , test_case const test = complete_download) { // this allows shutting down the sessions in parallel std::vector sp; settings_pack pack = settings(); // we do this to force pieces to be evicted into the ghost lists pack.set_int(settings_pack::cache_size, 10); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); lt::session ses1(pack); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:49075"); lt::session ses2(pack); torrent_handle tor1; torrent_handle tor2; int const num_pieces = (test == mid_download) ? 500 : 100; error_code ec; remove_all("tmp1_remove", ec); remove_all("tmp2_remove", ec); create_directory("tmp1_remove", ec); std::ofstream file("tmp1_remove/temporary"); boost::shared_ptr t = ::create_torrent(&file, "temporary" , 16 * 1024, num_pieces, false); file.close(); wait_for_listen(ses1, "ses1"); wait_for_listen(ses2, "ses2"); // test using piece sizes smaller than 16kB boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 , true, false, true, "_remove", 8 * 1024, &t, false, 0); if (test == partial_download) { std::vector priorities(num_pieces, 1); // set half of the pieces to priority 0 std::fill(priorities.begin(), priorities.begin() + (num_pieces / 2), 0); tor2.prioritize_pieces(priorities); } else if (test == mid_download) { tor1.set_upload_limit(static_cast(t->total_size())); tor2.set_download_limit(static_cast(t->total_size())); } torrent_status st1; torrent_status st2; for (int i = 0; i < 200; ++i) { print_alerts(ses1, "ses1", true, true, true); print_alerts(ses2, "ses2", true, true, true); st1 = tor1.status(); st2 = tor2.status(); if (test == mid_download && st2.num_pieces > num_pieces / 2) { TEST_CHECK(st2.is_finished == false); break; } if (st2.is_finished) break; TEST_CHECK(st1.state == torrent_status::seeding || st1.state == torrent_status::checking_resume_data || st1.state == torrent_status::checking_files); TEST_CHECK(st2.state == torrent_status::downloading || st2.state == torrent_status::checking_resume_data); // if nothing is being transferred after 3 seconds, we're failing the test if (st1.total_payload_upload == 0 && i > 30) { TEST_ERROR("no transfer"); return; } test_sleep(100); } TEST_CHECK(st1.num_pieces > 0); TEST_CHECK(st2.num_pieces > 0); ses2.remove_torrent(tor2, remove_options); ses1.remove_torrent(tor1, remove_options); std::cerr << "removed" << std::endl; for (int i = 0; tor2.is_valid() || tor1.is_valid(); ++i) { test_sleep(100); if (++i > 40) { std::cerr << "torrent handle(s) still valid: " << (tor1.is_valid() ? "tor1 " : "") << (tor2.is_valid() ? "tor2 " : "") << std::endl; TEST_ERROR("handle did not become invalid"); return; } } if (remove_options & session::delete_files) { TEST_CHECK(!exists("tmp1_remove/temporary")); TEST_CHECK(!exists("tmp2_remove/temporary")); } sp.push_back(ses1.abort()); sp.push_back(ses2.abort()); } TORRENT_TEST(remove_torrent) { test_remove_torrent(0); } TORRENT_TEST(remove_torrent_and_files) { test_remove_torrent(session::delete_files); } TORRENT_TEST(remove_torrent_partial) { test_remove_torrent(0, partial_download); } TORRENT_TEST(remove_torrent_and_files_partial) { test_remove_torrent(session::delete_files, partial_download); } TORRENT_TEST(remove_torrent_mid_download) { test_remove_torrent(0, mid_download); } TORRENT_TEST(remove_torrent_and_files_mid_download) { test_remove_torrent(session::delete_files, mid_download); } libtorrent-rasterbar-1.1.13/test/test_resolve_links.cpp000066400000000000000000000112361351156116000233470ustar00rootroot00000000000000/* Copyright (c) 2014, 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 "test.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/resolve_links.hpp" #include "libtorrent/file.hpp" // for combine_path #include #include using namespace libtorrent; struct test_torrent_t { char const* filename1; char const* filename2; int expected_matches; }; static test_torrent_t test_torrents[] = { // no match because shared file in test2 and test3 is not padded/aligned { "test2", "test1_pad_files", 0}, { "test3", "test1_pad_files", 0}, // in this case, test1 happens to have the shared file as the first one, // which makes it padded, however, the tail of it isn't padded, so it // still overlaps with the next file { "test1", "test1_pad_files", 0}, // test2 and test3 don't have the shared file aligned { "test2", "test1_pad_files", 0}, { "test3", "test1_pad_files", 0}, { "test2", "test1_single", 0}, // these are all padded. The first small file will accidentally also // match, even though it's not tail padded, the following file is identical { "test2_pad_files", "test1_pad_files", 2}, { "test3_pad_files", "test1_pad_files", 2}, { "test3_pad_files", "test2_pad_files", 2}, { "test1_pad_files", "test2_pad_files", 2}, { "test1_pad_files", "test3_pad_files", 2}, { "test2_pad_files", "test3_pad_files", 2}, // one might expect this to work, but since the tail of the single file // torrent is not padded, the last piece hash won't match { "test1_pad_files", "test1_single", 0}, // if it's padded on the other hand, it will work { "test1_pad_files", "test1_single_padded", 1}, // TODO: test files with different piece size (negative test) }; // TODO: it would be nice to test resolving of more than just 2 files as well. // like 3 single file torrents merged into one, resolving all 3 files. TORRENT_TEST(resolve_links) { #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS std::string path = combine_path(parent_path(current_working_directory()) , "mutable_test_torrents"); for (int i = 0; i < int(sizeof(test_torrents)/sizeof(test_torrents[0])); ++i) { test_torrent_t const& e = test_torrents[i]; std::string p = combine_path(path, e.filename1) + ".torrent"; fprintf(stdout, "loading %s\n", p.c_str()); boost::shared_ptr ti1 = boost::make_shared(p); p = combine_path(path, e.filename2) + ".torrent"; fprintf(stdout, "loading %s\n", p.c_str()); boost::shared_ptr ti2 = boost::make_shared(p); fprintf(stdout, "resolving\n"); resolve_links l(ti1); l.match(ti2, "."); std::vector const& links = l.get_links(); int num_matches = std::count_if(links.begin(), links.end() , boost::bind(&resolve_links::link_t::ti, _1)); // some debug output in case the test fails if (num_matches > e.expected_matches) { file_storage const& fs = ti1->files(); for (int i = 0; i < int(links.size()); ++i) { TORRENT_ASSERT(i < fs.num_files()); fprintf(stdout, "%s --> %s : %d\n", fs.file_name(i).c_str() , links[i].ti ? to_hex(links[i].ti->info_hash() .to_string()).c_str() : "", links[i].file_idx); } } TEST_EQUAL(num_matches, e.expected_matches); } #endif // TORRENT_DISABLE_MUTABLE_TORRENTS } libtorrent-rasterbar-1.1.13/test/test_resume.cpp000066400000000000000000000617341351156116000220000ustar00rootroot00000000000000/* Copyright (c) 2014, 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/session.hpp" #include "libtorrent/add_torrent_params.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/random.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/peer_info.hpp" #include #include "test.hpp" #include "setup_transfer.hpp" #include "settings.hpp" using namespace libtorrent; namespace lt = libtorrent; std::vector generate_resume_data(torrent_info* ti , char const* file_priorities = "") { entry rd; rd["file-format"] = "libtorrent resume file"; rd["file-version"] = 1; rd["info-hash"] = ti->info_hash().to_string(); rd["blocks per piece"] = (std::max)(1, ti->piece_length() / 0x4000); rd["pieces"] = std::string(ti->num_pieces(), '\x01'); rd["total_uploaded"] = 1337; rd["total_downloaded"] = 1338; rd["active_time"] = 1339; rd["seeding_time"] = 1340; rd["num_seeds"] = 1341; rd["num_downloaders"] = 1342; rd["upload_rate_limit"] = 1343; rd["download_rate_limit"] = 1344; rd["max_connections"] = 1345; rd["max_uploads"] = 1346; rd["seed_mode"] = 0; rd["super_seeding"] = 0; rd["added_time"] = 1347; rd["completed_time"] = 1348; rd["last_scrape"] = 1; rd["last_download"] = 2; rd["last_upload"] = 3; rd["finished_time"] = 1352; if (file_priorities && file_priorities[0]) { entry::list_type& file_prio = rd["file_priority"].list(); for (int i = 0; file_priorities[i]; ++i) file_prio.push_back(entry(file_priorities[i] - '0')); } rd["piece_priority"] = std::string(ti->num_pieces(), '\x01'); rd["auto_managed"] = 0; rd["sequential_download"] = 0; rd["paused"] = 0; entry::list_type& trackers = rd["trackers"].list(); trackers.push_back(entry(entry::list_t)); trackers.back().list().push_back(entry("http://resume_data_tracker.com/announce")); entry::list_type& url_list = rd["url-list"].list(); url_list.push_back(entry("http://resume_data_url_seed.com")); entry::list_type& httpseeds = rd["httpseeds"].list(); httpseeds.push_back(entry("http://resume_data_http_seed.com")); #ifdef TORRENT_WINDOWS rd["save_path"] = "c:\\resume_data save_path"; #else rd["save_path"] = "/resume_data save_path"; #endif std::vector ret; bencode(back_inserter(ret), rd); return ret; } torrent_handle test_resume_flags(lt::session& ses, int flags , char const* file_priorities = "1111", char const* resume_file_prio = "") { boost::shared_ptr ti = generate_torrent(); add_torrent_params p; p.ti = ti; p.flags = flags; #ifdef TORRENT_WINDOWS p.save_path = "c:\\add_torrent_params save_path"; #else p.save_path = "/add_torrent_params save_path"; #endif p.trackers.push_back("http://add_torrent_params_tracker.com/announce"); p.url_seeds.push_back("http://add_torrent_params_url_seed.com"); std::vector rd = generate_resume_data(ti.get(), resume_file_prio); p.resume_data.swap(rd); p.max_uploads = 1; p.max_connections = 2; p.upload_limit = 3; p.download_limit = 4; std::vector priorities_vector; for (int i = 0; file_priorities[i]; ++i) priorities_vector.push_back(file_priorities[i] - '0'); p.file_priorities = priorities_vector; torrent_handle h = ses.add_torrent(p); torrent_status s = h.status(); TEST_EQUAL(s.info_hash, ti->info_hash()); return h; } void default_tests(torrent_status const& s) { // allow some slack in the time stamps since they are reported as // relative times. If the computer is busy while running the unit test // or running under valgrind it may take several seconds int const now = duration_cast(clock_type::now().time_since_epoch()).count(); TEST_CHECK(s.last_scrape >= now - 1); TEST_CHECK(s.time_since_download >= now - 2); TEST_CHECK(s.time_since_upload >= now - 3); TEST_CHECK(s.active_time >= 1339); TEST_CHECK(s.last_scrape < now - 1 + 10); TEST_CHECK(s.time_since_download < now - 2 + 10); TEST_CHECK(s.time_since_upload < now - 3 + 10); TEST_CHECK(s.active_time < 1339 + 10); TEST_CHECK(s.finished_time >= 1352); TEST_CHECK(s.seeding_time >= 1340); TEST_CHECK(s.added_time >= 1347); TEST_CHECK(s.completed_time >= 1348); TEST_CHECK(s.finished_time < 1352 + 5); TEST_CHECK(s.seeding_time < 1340 + 5); TEST_CHECK(s.added_time < 1347 + 5); TEST_CHECK(s.completed_time < 1348 + 5); } void test_file_sizes(bool allocate) { error_code ec; remove_all("test_resume", ec); lt::settings_pack pack = settings(); // we're not testing the hash check, just accept the data we write pack.set_bool(settings_pack::disable_hash_checks, true); lt::session ses(pack); boost::shared_ptr ti = generate_torrent(); add_torrent_params p; p.ti = ti; p.save_path = "."; if (allocate) p.storage_mode = storage_mode_allocate; torrent_handle h = ses.add_torrent(p); wait_for_downloading(ses, "ses"); std::vector piece(ti->piece_length(), 0); h.add_piece(0, piece.data()); h.save_resume_data(); alert const* a = wait_for_alert(ses, save_resume_data_alert::alert_type); TEST_CHECK(a); save_resume_data_alert const* ra = alert_cast(a); TEST_CHECK(ra); if (ra) { fprintf(stderr, "%s\n", ra->resume_data->to_string().c_str()); bool const has_file_sizes = ra->resume_data->dict().count("file sizes") == 1; TEST_CHECK(has_file_sizes); if (!has_file_sizes) return; // { 'file sizes': [ [ size, timestamp], [...], ... ] } boost::int64_t const file_size = (*ra->resume_data)["file sizes"].list() .front().list().front().integer(); if (allocate) { TEST_EQUAL(file_size, ti->files().file_size(0)); } else { TEST_EQUAL(file_size, ti->piece_length()); } } } TORRENT_TEST(file_sizes_allocate) { test_file_sizes(true); } TORRENT_TEST(file_sizes) { test_file_sizes(false); } TORRENT_TEST(piece_priorities) { lt::session ses(settings()); boost::shared_ptr ti = generate_torrent(); add_torrent_params p; p.ti = ti; p.save_path = "."; torrent_handle h = ses.add_torrent(p); h.piece_priority(0, 0); h.piece_priority(ti->num_pieces()-1, 0); h.save_resume_data(); alert const* a = wait_for_alert(ses, save_resume_data_alert::alert_type); TEST_CHECK(a); save_resume_data_alert const* ra = alert_cast(a); TEST_CHECK(ra); if (ra) { fprintf(stderr, "%s\n", ra->resume_data->to_string().c_str()); entry::string_type prios = (*ra->resume_data)["piece_priority"].string(); TEST_EQUAL(int(prios.size()), ti->num_pieces()); TEST_EQUAL(prios[0], '\0'); TEST_EQUAL(prios[1], '\x04'); TEST_EQUAL(prios[ti->num_pieces()-1], '\0'); bencode(std::back_inserter(p.resume_data), *ra->resume_data); } ses.remove_torrent(h); // now, make sure the piece priorities are loaded correctly h = ses.add_torrent(p); TEST_EQUAL(h.piece_priority(0), 0); TEST_EQUAL(h.piece_priority(1), 4); TEST_EQUAL(h.piece_priority(ti->num_pieces()-1), 0); } // TODO: test what happens when loading a resume file with both piece priorities // and file priorities (file prio should take presedence) // TODO: make sure a resume file only ever contain file priorities OR piece // priorities. Never both. // TODO: generally save TORRENT_TEST(file_priorities_default) { lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses, 0, "", "").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 4); TEST_EQUAL(file_priorities[1], 4); TEST_EQUAL(file_priorities[2], 4); } // As long as the add_torrent_params priorities are empty, the file_priorities // from the resume data should take effect TORRENT_TEST(file_priorities_in_resume) { lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses, 0, "", "123").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 1); TEST_EQUAL(file_priorities[1], 2); TEST_EQUAL(file_priorities[2], 3); } // if both resume data and add_torrent_params has file_priorities, the // add_torrent_params one take precedence TORRENT_TEST(file_priorities_in_resume_and_params) { lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses, 0, "456", "123").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 4); TEST_EQUAL(file_priorities[1], 5); TEST_EQUAL(file_priorities[2], 6); } // if we set flag_override_resume_data, it should no affect file priorities TORRENT_TEST(file_priorities_override_resume) { lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses , add_torrent_params::flag_override_resume_data, "", "123").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 1); TEST_EQUAL(file_priorities[1], 2); TEST_EQUAL(file_priorities[2], 3); } TORRENT_TEST(file_priorities_resume_seed_mode) { // in share mode file priorities should always be 0 lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses, add_torrent_params::flag_share_mode, "", "123").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 0); TEST_EQUAL(file_priorities[1], 0); TEST_EQUAL(file_priorities[2], 0); } TORRENT_TEST(file_priorities_seed_mode) { // in share mode file priorities should always be 0 lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses, add_torrent_params::flag_share_mode, "123", "").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 0); TEST_EQUAL(file_priorities[1], 0); TEST_EQUAL(file_priorities[2], 0); } TORRENT_TEST(zero_file_prio) { lt::session ses(settings()); boost::shared_ptr ti = generate_torrent(); add_torrent_params p; p.ti = ti; p.save_path = "."; entry rd; rd["file-format"] = "libtorrent resume file"; rd["file-version"] = 1; rd["info-hash"] = ti->info_hash().to_string(); rd["blocks per piece"] = (std::max)(1, ti->piece_length() / 0x4000); // set file priorities to 0 rd["file_priority"] = entry::list_type(100, entry(0)); rd["pieces"] = std::string(ti->num_pieces(), '\x01'); bencode(back_inserter(p.resume_data), rd); torrent_handle h = ses.add_torrent(p); torrent_status s = h.status(); TEST_EQUAL(s.total_wanted, 0); } TORRENT_TEST(mixing_file_and_piece_prio) { lt::session ses(settings()); boost::shared_ptr ti = generate_torrent(); add_torrent_params p; p.ti = ti; p.save_path = "."; entry rd; rd["file-format"] = "libtorrent resume file"; rd["file-version"] = 1; rd["info-hash"] = ti->info_hash().to_string(); rd["blocks per piece"] = (std::max)(1, ti->piece_length() / 0x4000); // set file priorities to 0 rd["file_priority"] = entry::list_type(100, entry(0)); rd["pieces"] = std::string(ti->num_pieces(), '\x01'); // but set the piece priorities to 1. these take precedence rd["piece_priority"] = std::string(ti->num_pieces(), '\x01'); bencode(back_inserter(p.resume_data), rd); torrent_handle h = ses.add_torrent(p); torrent_status s = h.status(); TEST_EQUAL(s.total_wanted, ti->total_size()); } void test_seed_mode(bool file_prio, bool pieces_have, bool piece_prio , bool all_files_zero = false) { fprintf(stderr, "test_seed_mode file_prio: %d pieces_have: %d piece_prio: %d\n" , file_prio, pieces_have, piece_prio); lt::session ses(settings()); boost::shared_ptr ti = generate_torrent(); add_torrent_params p; p.ti = ti; p.save_path = "."; entry rd; rd["file-format"] = "libtorrent resume file"; rd["file-version"] = 1; rd["info-hash"] = ti->info_hash().to_string(); rd["blocks per piece"] = (std::max)(1, ti->piece_length() / 0x4000); if (file_prio) { // this should take it out of seed_mode entry::list_type& file_prio = rd["file_priority"].list(); file_prio.push_back(entry(0)); if (all_files_zero) { for (int i = 0; i < 100; ++i) { file_prio.push_back(entry(0)); } } } std::string pieces(ti->num_pieces(), '\x01'); if (pieces_have) { pieces[0] = '\0'; } rd["pieces"] = pieces; std::string pieces_prio(ti->num_pieces(), '\x01'); if (piece_prio) { pieces_prio[0] = '\0'; } rd["piece_priority"] = pieces_prio; rd["seed_mode"] = 1; bencode(back_inserter(p.resume_data), rd); torrent_handle h = ses.add_torrent(p); torrent_status s = h.status(); if (file_prio || piece_prio || pieces_have) { TEST_EQUAL(s.seed_mode, false); } else { TEST_EQUAL(s.seed_mode, true); } } TORRENT_TEST(seed_mode_file_prio) { test_seed_mode(true, false, false); } TORRENT_TEST(seed_mode_piece_prio) { test_seed_mode(false, true, false); } TORRENT_TEST(seed_mode_piece_have) { test_seed_mode(false, false, true); } TORRENT_TEST(seed_mode_preserve) { test_seed_mode(false, false, false); } TORRENT_TEST(seed_mode_load_peers) { lt::session ses(settings()); boost::shared_ptr ti = generate_torrent(); add_torrent_params p; p.ti = ti; p.save_path = "."; entry rd; rd["file-format"] = "libtorrent resume file"; rd["file-version"] = 1; rd["info-hash"] = ti->info_hash().to_string(); rd["blocks per piece"] = std::max(1, ti->piece_length() / 0x4000); rd["pieces"] = std::string(ti->num_pieces(), '\x01'); rd["piece_priority"] = std::string(ti->num_pieces(), '\x01'); rd["seed_mode"] = 1; rd["peers"] = "\x01\x02\x03\x04\x30\x39"; bencode(back_inserter(p.resume_data), rd); torrent_handle h = ses.add_torrent(p); wait_for_alert(ses, torrent_checked_alert::alert_type, "seed_mode_load_peers"); std::vector peers; h.get_full_peer_list(peers); TEST_EQUAL(peers.size(), 1); TEST_CHECK(peers[0].ip == tcp::endpoint(address::from_string("1.2.3.4"), 12345)); } TORRENT_TEST(resume_save_load) { lt::session ses(settings()); torrent_handle h = test_resume_flags(ses, 0, "123", ""); h.save_resume_data(); save_resume_data_alert const* a = alert_cast( wait_for_alert(ses, save_resume_data_alert::alert_type , "resume_save_load")); TEST_CHECK(a); if (a == NULL) return; TEST_CHECK(a->resume_data); entry& e = *a->resume_data.get(); entry::list_type& l = e["file_priority"].list(); entry::list_type::iterator i = l.begin(); TEST_EQUAL(l.size(), 3); TEST_EQUAL(*i++, 1); TEST_EQUAL(*i++, 2); TEST_EQUAL(*i++, 3); } TORRENT_TEST(resume_save_load_resume) { lt::session ses(settings()); torrent_handle h = test_resume_flags(ses, 0, "", "123"); h.save_resume_data(); save_resume_data_alert const* a = alert_cast( wait_for_alert(ses, save_resume_data_alert::alert_type , "resume_save_load")); TEST_CHECK(a); if (a == NULL) return; TEST_CHECK(a->resume_data); entry& e = *a->resume_data.get(); entry::list_type& l = e["file_priority"].list(); entry::list_type::iterator i = l.begin(); TEST_EQUAL(l.size(), 3); TEST_EQUAL(*i++, 1); TEST_EQUAL(*i++, 2); TEST_EQUAL(*i++, 3); } TORRENT_TEST(file_priorities_resume_override) { // make sure that an empty file_priorities vector in add_torrent_params won't // override the resume data file priorities, even when override resume data // flag is set. lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses, add_torrent_params::flag_override_resume_data, "", "123").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 1); TEST_EQUAL(file_priorities[1], 2); TEST_EQUAL(file_priorities[2], 3); } TORRENT_TEST(file_priorities_resume) { lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses, 0, "", "123").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 1); TEST_EQUAL(file_priorities[1], 2); TEST_EQUAL(file_priorities[2], 3); } TORRENT_TEST(file_priorities1) { lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses, 0, "010").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 0); TEST_EQUAL(file_priorities[1], 1); TEST_EQUAL(file_priorities[2], 0); //#error save resume data and assert the file priorities are preserved } TORRENT_TEST(file_priorities2) { lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses, 0, "123").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 1); TEST_EQUAL(file_priorities[1], 2); TEST_EQUAL(file_priorities[2], 3); } TORRENT_TEST(file_priorities3) { lt::session ses(settings()); std::vector file_priorities = test_resume_flags(ses, 0, "4321").file_priorities(); TEST_EQUAL(file_priorities.size(), 3); TEST_EQUAL(file_priorities[0], 4); TEST_EQUAL(file_priorities[1], 3); TEST_EQUAL(file_priorities[2], 2); } TORRENT_TEST(plain) { lt::session ses(settings()); torrent_status s = test_resume_flags(ses, 0).status(); default_tests(s); #ifdef TORRENT_WINDOWS TEST_EQUAL(s.save_path, "c:\\add_torrent_params save_path"); #else TEST_EQUAL(s.save_path, "/add_torrent_params save_path"); #endif TEST_EQUAL(s.sequential_download, false); TEST_EQUAL(s.paused, false); TEST_EQUAL(s.auto_managed, false); TEST_EQUAL(s.seed_mode, false); TEST_EQUAL(s.super_seeding, false); TEST_EQUAL(s.share_mode, false); TEST_EQUAL(s.upload_mode, false); TEST_EQUAL(s.ip_filter_applies, false); TEST_EQUAL(s.connections_limit, 1345); TEST_EQUAL(s.uploads_limit, 1346); } TORRENT_TEST(use_resume_save_path) { lt::session ses(settings()); torrent_status s = test_resume_flags(ses, add_torrent_params::flag_use_resume_save_path).status(); default_tests(s); #ifdef TORRENT_WINDOWS TEST_EQUAL(s.save_path, "c:\\resume_data save_path"); #else TEST_EQUAL(s.save_path, "/resume_data save_path"); #endif TEST_EQUAL(s.sequential_download, false); TEST_EQUAL(s.paused, false); TEST_EQUAL(s.auto_managed, false); TEST_EQUAL(s.seed_mode, false); TEST_EQUAL(s.super_seeding, false); TEST_EQUAL(s.share_mode, false); TEST_EQUAL(s.upload_mode, false); TEST_EQUAL(s.ip_filter_applies, false); TEST_EQUAL(s.connections_limit, 1345); TEST_EQUAL(s.uploads_limit, 1346); } TORRENT_TEST(override_resume_data) { lt::session ses(settings()); torrent_status s = test_resume_flags(ses , add_torrent_params::flag_override_resume_data | add_torrent_params::flag_paused).status(); default_tests(s); #ifdef TORRENT_WINDOWS TEST_EQUAL(s.save_path, "c:\\add_torrent_params save_path"); #else TEST_EQUAL(s.save_path, "/add_torrent_params save_path"); #endif TEST_EQUAL(s.sequential_download, false); TEST_EQUAL(s.paused, true); TEST_EQUAL(s.auto_managed, false); TEST_EQUAL(s.seed_mode, false); TEST_EQUAL(s.super_seeding, false); TEST_EQUAL(s.share_mode, false); TEST_EQUAL(s.upload_mode, false); TEST_EQUAL(s.ip_filter_applies, false); TEST_EQUAL(s.connections_limit, 2); TEST_EQUAL(s.uploads_limit, 1); } TORRENT_TEST(seed_mode) { lt::session ses(settings()); torrent_status s = test_resume_flags(ses, add_torrent_params::flag_override_resume_data | add_torrent_params::flag_seed_mode).status(); default_tests(s); #ifdef TORRENT_WINDOWS TEST_EQUAL(s.save_path, "c:\\add_torrent_params save_path"); #else TEST_EQUAL(s.save_path, "/add_torrent_params save_path"); #endif TEST_EQUAL(s.sequential_download, false); TEST_EQUAL(s.paused, false); TEST_EQUAL(s.auto_managed, false); TEST_EQUAL(s.seed_mode, true); TEST_EQUAL(s.super_seeding, false); TEST_EQUAL(s.share_mode, false); TEST_EQUAL(s.upload_mode, false); TEST_EQUAL(s.ip_filter_applies, false); TEST_EQUAL(s.connections_limit, 2); TEST_EQUAL(s.uploads_limit, 1); } TORRENT_TEST(upload_mode) { lt::session ses(settings()); torrent_status s = test_resume_flags(ses, add_torrent_params::flag_upload_mode).status(); default_tests(s); #ifdef TORRENT_WINDOWS TEST_EQUAL(s.save_path, "c:\\add_torrent_params save_path"); #else TEST_EQUAL(s.save_path, "/add_torrent_params save_path"); #endif TEST_EQUAL(s.sequential_download, false); TEST_EQUAL(s.paused, false); TEST_EQUAL(s.auto_managed, false); TEST_EQUAL(s.seed_mode, false); TEST_EQUAL(s.super_seeding, false); TEST_EQUAL(s.share_mode, false); TEST_EQUAL(s.upload_mode, true); TEST_EQUAL(s.ip_filter_applies, false); TEST_EQUAL(s.connections_limit, 1345); TEST_EQUAL(s.uploads_limit, 1346); } TORRENT_TEST(share_mode) { lt::session ses(settings()); torrent_status s = test_resume_flags(ses , add_torrent_params::flag_override_resume_data | add_torrent_params::flag_share_mode).status(); default_tests(s); #ifdef TORRENT_WINDOWS TEST_EQUAL(s.save_path, "c:\\add_torrent_params save_path"); #else TEST_EQUAL(s.save_path, "/add_torrent_params save_path"); #endif TEST_EQUAL(s.sequential_download, false); TEST_EQUAL(s.paused, false); TEST_EQUAL(s.auto_managed, false); TEST_EQUAL(s.seed_mode, false); TEST_EQUAL(s.super_seeding, false); TEST_EQUAL(s.share_mode, true); TEST_EQUAL(s.upload_mode, false); TEST_EQUAL(s.ip_filter_applies, false); TEST_EQUAL(s.connections_limit, 2); TEST_EQUAL(s.uploads_limit, 1); } TORRENT_TEST(auto_managed) { lt::session ses(settings()); // resume data overrides the auto-managed flag torrent_status s = test_resume_flags(ses, add_torrent_params::flag_auto_managed).status(); default_tests(s); #ifdef TORRENT_WINDOWS TEST_EQUAL(s.save_path, "c:\\add_torrent_params save_path"); #else TEST_EQUAL(s.save_path, "/add_torrent_params save_path"); #endif TEST_EQUAL(s.sequential_download, false); TEST_EQUAL(s.paused, false); TEST_EQUAL(s.auto_managed, false); TEST_EQUAL(s.seed_mode, false); TEST_EQUAL(s.super_seeding, false); TEST_EQUAL(s.share_mode, false); TEST_EQUAL(s.upload_mode, false); TEST_EQUAL(s.ip_filter_applies, false); TEST_EQUAL(s.connections_limit, 1345); TEST_EQUAL(s.uploads_limit, 1346); } TORRENT_TEST(paused) { lt::session ses(settings()); // resume data overrides the paused flag torrent_status s = test_resume_flags(ses, add_torrent_params::flag_paused).status(); default_tests(s); #ifdef TORRENT_WINDOWS TEST_EQUAL(s.save_path, "c:\\add_torrent_params save_path"); #else TEST_EQUAL(s.save_path, "/add_torrent_params save_path"); #endif TEST_EQUAL(s.sequential_download, false); TEST_EQUAL(s.paused, false); TEST_EQUAL(s.auto_managed, false); TEST_EQUAL(s.seed_mode, false); TEST_EQUAL(s.super_seeding, false); TEST_EQUAL(s.share_mode, false); TEST_EQUAL(s.upload_mode, false); TEST_EQUAL(s.ip_filter_applies, false); TEST_EQUAL(s.connections_limit, 1345); TEST_EQUAL(s.uploads_limit, 1346); // TODO: test all other resume flags here too. This would require returning // more than just the torrent_status from test_resume_flags. Also http seeds // and trackers for instance } TORRENT_TEST(url_seed_resume_data) { // merge url seeds with resume data fprintf(stderr, "flags: merge_resume_http_seeds\n"); lt::session ses(settings()); torrent_handle h = test_resume_flags(ses, add_torrent_params::flag_merge_resume_http_seeds); std::set us = h.url_seeds(); std::set ws = h.http_seeds(); TEST_EQUAL(us.size(), 3); TEST_EQUAL(std::count(us.begin(), us.end() , "http://add_torrent_params_url_seed.com"), 1); TEST_EQUAL(std::count(us.begin(), us.end() , "http://torrent_file_url_seed.com/"), 1); TEST_EQUAL(std::count(us.begin(), us.end() , "http://resume_data_url_seed.com/"), 1); TEST_EQUAL(ws.size(), 1); TEST_EQUAL(std::count(ws.begin(), ws.end() , "http://resume_data_http_seed.com"), 1); } TORRENT_TEST(resume_override_torrent) { // resume data overrides the .torrent_file fprintf(stderr, "flags: no merge_resume_http_seed\n"); lt::session ses(settings()); torrent_handle h = test_resume_flags(ses, add_torrent_params::flag_merge_resume_trackers); std::set us = h.url_seeds(); std::set ws = h.http_seeds(); TEST_EQUAL(ws.size(), 1); TEST_EQUAL(std::count(ws.begin(), ws.end() , "http://resume_data_http_seed.com"), 1); TEST_EQUAL(us.size(), 1); TEST_EQUAL(std::count(us.begin(), us.end() , "http://resume_data_url_seed.com/"), 1); } libtorrent-rasterbar-1.1.13/test/test_session.cpp000066400000000000000000000227401351156116000221550ustar00rootroot00000000000000/* Copyright (c) 2013, 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/session.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "test.hpp" #include "setup_transfer.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/session_stats.hpp" #include "libtorrent/performance_counters.hpp" #include "libtorrent/bdecode.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent_info.hpp" #include "settings.hpp" #include using namespace libtorrent; namespace lt = libtorrent; TORRENT_TEST(session) { settings_pack p = settings(); p.set_int(settings_pack::alert_mask, ~0); lt::session ses(p); settings_pack sett = settings(); sett.set_int(settings_pack::cache_size, 100); sett.set_int(settings_pack::max_queued_disk_bytes, 1000 * 16 * 1024); ses.apply_settings(sett); // verify that we get the appropriate performance warning because // we're allowing a larger queue than we have cache. alert const* a; for (;;) { a = wait_for_alert(ses, performance_alert::alert_type, "ses1"); if (a == NULL) break; TEST_EQUAL(a->type(), performance_alert::alert_type); if (alert_cast(a)->warning_code == performance_alert::too_high_disk_queue_limit) break; } TEST_CHECK(a); sett.set_int(settings_pack::unchoke_slots_limit, 0); ses.apply_settings(sett); TEST_CHECK(ses.get_settings().get_int(settings_pack::unchoke_slots_limit) == 0); sett.set_int(settings_pack::unchoke_slots_limit, -1); ses.apply_settings(sett); TEST_CHECK(ses.get_settings().get_int(settings_pack::unchoke_slots_limit) == -1); sett.set_int(settings_pack::unchoke_slots_limit, 8); ses.apply_settings(sett); TEST_CHECK(ses.get_settings().get_int(settings_pack::unchoke_slots_limit) == 8); // make sure the destructor waits properly // for the asynchronous call to set the alert // mask completes, before it goes on to destruct // the session object } TORRENT_TEST(load_empty_file) { settings_pack p = settings(); p.set_int(settings_pack::alert_mask, ~0); lt::session ses(p); add_torrent_params atp; error_code ignore_errors; atp.ti = boost::make_shared("", 0, boost::ref(ignore_errors)); atp.save_path = "."; error_code ec; torrent_handle h = ses.add_torrent(atp, ec); TEST_CHECK(!h.is_valid()); TEST_CHECK(ec == error_code(errors::no_metadata)) } TORRENT_TEST(session_stats) { std::vector stats = session_stats_metrics(); std::sort(stats.begin(), stats.end() , boost::bind(&stats_metric::value_index, _1) < boost::bind(&stats_metric::value_index, _2)); TEST_EQUAL(stats.size(), lt::counters::num_counters); // make sure every stat index is represented in the stats_metric vector for (int i = 0; i < int(stats.size()); ++i) { TEST_EQUAL(stats[i].value_index, i); } } TORRENT_TEST(paused_session) { lt::session s(settings()); s.pause(); lt::add_torrent_params ps; std::ofstream file("temporary"); ps.ti = ::create_torrent(&file, "temporary", 16 * 1024, 13, false); ps.flags = lt::add_torrent_params::flag_paused; ps.save_path = "."; torrent_handle h = s.add_torrent(ps); test_sleep(2000); h.resume(); test_sleep(1000); TEST_CHECK(!h.status().paused); } TORRENT_TEST(get_cache_info) { lt::session s(settings()); lt::cache_status ret; s.get_cache_info(&ret); TEST_CHECK(ret.pieces.empty()); #ifndef TORRENT_NO_DEPRECATE TEST_EQUAL(ret.blocks_written, 0); TEST_EQUAL(ret.writes, 0); TEST_EQUAL(ret.blocks_read, 0); TEST_EQUAL(ret.blocks_read_hit, 0); TEST_EQUAL(ret.reads, 0); TEST_EQUAL(ret.queued_bytes, 0); TEST_EQUAL(ret.cache_size, 0); TEST_EQUAL(ret.write_cache_size, 0); TEST_EQUAL(ret.read_cache_size, 0); TEST_EQUAL(ret.pinned_blocks, 0); TEST_EQUAL(ret.total_used_buffers, 0); TEST_EQUAL(ret.average_read_time, 0); TEST_EQUAL(ret.average_write_time, 0); TEST_EQUAL(ret.average_hash_time, 0); TEST_EQUAL(ret.average_job_time, 0); TEST_EQUAL(ret.cumulative_job_time, 0); TEST_EQUAL(ret.cumulative_read_time, 0); TEST_EQUAL(ret.cumulative_write_time, 0); TEST_EQUAL(ret.cumulative_hash_time, 0); TEST_EQUAL(ret.total_read_back, 0); TEST_EQUAL(ret.read_queue_size, 0); TEST_EQUAL(ret.blocked_jobs, 0); TEST_EQUAL(ret.queued_jobs, 0); TEST_EQUAL(ret.peak_queued, 0); TEST_EQUAL(ret.pending_jobs, 0); TEST_EQUAL(ret.num_jobs, 0); TEST_EQUAL(ret.num_read_jobs, 0); TEST_EQUAL(ret.num_write_jobs, 0); TEST_EQUAL(ret.arc_mru_size, 0); TEST_EQUAL(ret.arc_mru_ghost_size, 0); TEST_EQUAL(ret.arc_mfu_size, 0); TEST_EQUAL(ret.arc_mfu_ghost_size, 0); TEST_EQUAL(ret.arc_write_size, 0); TEST_EQUAL(ret.arc_volatile_size, 0); TEST_EQUAL(ret.num_writing_threads, 0); #endif } #if __cplusplus >= 201103L template void test_save_restore(Set setup, Save s, Default d, Load l) { entry st; { settings_pack p = settings(); setup(p); lt::session ses(p); s(ses, st); } { settings_pack p = settings(); d(p); lt::session ses(p); // the loading function takes a bdecode_node, so we have to transform the // entry printf("%s\n", st.to_string().c_str()); std::vector buf; bencode(std::back_inserter(buf), st); bdecode_node state; error_code ec; int ret = bdecode(buf.data(), buf.data() + buf.size() , state, ec, nullptr, 100, 1000); TEST_EQUAL(ret, 0); if (ec) { printf("bdecode: %s\n", ec.message().c_str()); printf("%s\n", std::string(buf.data(), buf.size()).c_str()); } TEST_CHECK(!ec); l(ses, state); } } TORRENT_TEST(save_restore_state) { test_save_restore( [](settings_pack& p) { // set the cache size p.set_int(settings_pack::cache_size, 1337); }, [](lt::session& ses, entry& st) { ses.save_state(st); }, [](settings_pack& p) { p.set_int(settings_pack::cache_size, 90); }, [](lt::session& ses, bdecode_node& st) { ses.load_state(st); // make sure we loaded the cache size correctly settings_pack sett = ses.get_settings(); TEST_EQUAL(sett.get_int(settings_pack::cache_size), 1337); }); } TORRENT_TEST(save_restore_state_save_filter) { test_save_restore( [](settings_pack& p) { // set the cache size p.set_int(settings_pack::cache_size, 1337); }, [](lt::session& ses, entry& st) { // save everything _but_ the settings ses.save_state(st, ~session::save_settings); }, [](settings_pack& p) { p.set_int(settings_pack::cache_size, 90); }, [](lt::session& ses, bdecode_node& st) { ses.load_state(st); // make sure whatever we loaded did not include the cache size settings_pack sett = ses.get_settings(); TEST_EQUAL(sett.get_int(settings_pack::cache_size), 90); }); } TORRENT_TEST(save_restore_state_load_filter) { test_save_restore( [](settings_pack& p) { // set the cache size p.set_int(settings_pack::cache_size, 1337); }, [](lt::session& ses, entry& st) { // save everything ses.save_state(st); }, [](settings_pack& p) { p.set_int(settings_pack::cache_size, 90); }, [](lt::session& ses, bdecode_node& st) { // load everything _but_ the settings ses.load_state(st, ~session::save_settings); settings_pack sett = ses.get_settings(); TEST_EQUAL(sett.get_int(settings_pack::cache_size), 90); }); } TORRENT_TEST(session_shutdown) { lt::settings_pack pack; lt::session ses(pack); } // make sure we don't restore peer_id from session state TORRENT_TEST(save_state_peer_id) { lt::settings_pack pack; pack.set_str(settings_pack::peer_fingerprint, "AAA"); lt::session ses(pack); TEST_EQUAL(ses.get_settings().get_str(settings_pack::peer_fingerprint), "AAA"); lt::entry st; ses.save_state(st); pack.set_str(settings_pack::peer_fingerprint, "foobar"); ses.apply_settings(pack); TEST_EQUAL(ses.get_settings().get_str(settings_pack::peer_fingerprint), "foobar"); std::vector buf; bencode(std::back_inserter(buf), st); bdecode_node state; error_code ec; int ret = bdecode(buf.data(), buf.data() + buf.size() , state, ec, nullptr, 100, 1000); TEST_EQUAL(ret, 0); ses.load_state(state); TEST_EQUAL(ses.get_settings().get_str(settings_pack::peer_fingerprint), "foobar"); } #endif libtorrent-rasterbar-1.1.13/test/test_settings_pack.cpp000066400000000000000000000161551351156116000233330ustar00rootroot00000000000000/* Copyright (c) 2012, 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 "test.hpp" #include "libtorrent/settings_pack.hpp" #include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" #include using namespace libtorrent; using namespace libtorrent::aux; TORRENT_TEST(default_settings) { aux::session_settings sett; entry e; save_settings_to_dict(sett, e.dict()); // all default values are supposed to be skipped // by save_settings TEST_EQUAL(e.dict().size(), 0); #if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM if (e.dict().size() > 0) std::cerr << e << std::endl; #endif } TORRENT_TEST(default_settings2) { aux::session_settings sett; settings_pack def = default_settings(); for (int i = 0; i < settings_pack::num_string_settings; ++i) { TEST_EQUAL(sett.get_str(settings_pack::string_type_base + i) , def.get_str(settings_pack::string_type_base + i)); } for (int i = 0; i < settings_pack::num_int_settings; ++i) { TEST_EQUAL(sett.get_int(settings_pack::int_type_base + i) , def.get_int(settings_pack::int_type_base + i)); } for (int i = 0; i < settings_pack::num_bool_settings; ++i) { TEST_EQUAL(sett.get_bool(settings_pack::bool_type_base + i) , def.get_bool(settings_pack::bool_type_base + i)); } } TORRENT_TEST(apply_pack) { aux::session_settings sett; settings_pack sp; sp.set_int(settings_pack::max_out_request_queue, 1337); TEST_CHECK(sett.get_int(settings_pack::max_out_request_queue) != 1337); apply_pack(&sp, sett); TEST_EQUAL(sett.get_int(settings_pack::max_out_request_queue), 1337); entry e; save_settings_to_dict(sett, e.dict()); TEST_EQUAL(e.dict().size(), 1); std::string out; bencode(std::back_inserter(out), e); TEST_EQUAL(out, "d21:max_out_request_queuei1337ee"); } TORRENT_TEST(sparse_pack) { settings_pack pack; TEST_EQUAL(pack.has_val(settings_pack::send_redundant_have), false); pack.set_bool(settings_pack::send_redundant_have, true); TEST_EQUAL(pack.has_val(settings_pack::send_redundant_have), true); TEST_EQUAL(pack.has_val(settings_pack::user_agent), false); TEST_EQUAL(pack.has_val(settings_pack::lazy_bitfields), false); TEST_EQUAL(pack.get_bool(settings_pack::send_redundant_have), true); } TORRENT_TEST(test_name) { #define TEST_NAME(n) \ TEST_EQUAL(setting_by_name(#n), settings_pack:: n) \ TEST_CHECK(strcmp(name_for_setting(settings_pack:: n), #n) == 0) TEST_NAME(contiguous_recv_buffer); TEST_NAME(choking_algorithm); TEST_NAME(seeding_piece_quota); #ifndef TORRENT_NO_DEPRECATE TEST_NAME(half_open_limit); TEST_NAME(mmap_cache); #endif TEST_NAME(peer_turnover_interval); } TORRENT_TEST(clear) { settings_pack pack; TEST_EQUAL(pack.has_val(settings_pack::send_redundant_have), false); pack.set_bool(settings_pack::send_redundant_have, true); TEST_EQUAL(pack.has_val(settings_pack::send_redundant_have), true); TEST_EQUAL(pack.has_val(settings_pack::user_agent), false); TEST_EQUAL(pack.has_val(settings_pack::lazy_bitfields), false); TEST_EQUAL(pack.get_bool(settings_pack::send_redundant_have), true); pack.clear(); TEST_EQUAL(pack.has_val(settings_pack::send_redundant_have), false); TEST_EQUAL(pack.has_val(settings_pack::user_agent), false); TEST_EQUAL(pack.has_val(settings_pack::lazy_bitfields), false); } TORRENT_TEST(clear_single_int) { settings_pack sp; sp.set_int(settings_pack::max_out_request_queue, 1337); TEST_EQUAL(sp.get_int(settings_pack::max_out_request_queue), 1337); sp.clear(settings_pack::max_out_request_queue); TEST_EQUAL(sp.get_int(settings_pack::max_out_request_queue), 0); } TORRENT_TEST(clear_single_bool) { settings_pack sp; sp.set_bool(settings_pack::send_redundant_have, true); TEST_EQUAL(sp.get_bool(settings_pack::send_redundant_have), true); sp.clear(settings_pack::send_redundant_have); TEST_EQUAL(sp.get_bool(settings_pack::send_redundant_have), false); } TORRENT_TEST(clear_single_string) { settings_pack sp; sp.set_str(settings_pack::user_agent, "foobar"); TEST_EQUAL(sp.get_str(settings_pack::user_agent), "foobar"); sp.clear(settings_pack::user_agent); TEST_EQUAL(sp.get_str(settings_pack::user_agent), std::string()); } TORRENT_TEST(duplicates) { settings_pack p; p.set_str(settings_pack::peer_fingerprint, "abc"); p.set_str(settings_pack::peer_fingerprint, "cde"); p.set_str(settings_pack::peer_fingerprint, "efg"); p.set_str(settings_pack::peer_fingerprint, "hij"); TEST_EQUAL(p.get_str(settings_pack::peer_fingerprint), "hij"); } TORRENT_TEST(settings_pack_abi) { // make sure enum values are preserved across libtorrent versions // for ABI compatibility // These values are only allowed to change across major versions TEST_EQUAL(settings_pack::string_type_base, 0x0000); TEST_EQUAL(settings_pack::int_type_base, 0x4000); TEST_EQUAL(settings_pack::bool_type_base, 0x8000); TEST_EQUAL(settings_pack::type_mask, 0xc000); // strings TEST_EQUAL(settings_pack::outgoing_interfaces, settings_pack::string_type_base + 4); TEST_EQUAL(settings_pack::dht_bootstrap_nodes, settings_pack::string_type_base + 11); // bool TEST_EQUAL(settings_pack::lazy_bitfields, settings_pack::bool_type_base + 3); TEST_EQUAL(settings_pack::use_read_cache, settings_pack::bool_type_base + 7); TEST_EQUAL(settings_pack::proxy_tracker_connections, settings_pack::bool_type_base + 68); // ints TEST_EQUAL(settings_pack::max_suggest_pieces, settings_pack::int_type_base + 66); TEST_EQUAL(settings_pack::connections_slack, settings_pack::int_type_base + 86); TEST_EQUAL(settings_pack::aio_threads, settings_pack::int_type_base + 104); TEST_EQUAL(settings_pack::max_http_recv_buffer_size, settings_pack::int_type_base + 115); TEST_EQUAL(settings_pack::web_seed_name_lookup_retry, settings_pack::int_type_base + 128); } // TODO: load_pack_from_dict libtorrent-rasterbar-1.1.13/test/test_sha1_hash.cpp000066400000000000000000000075421351156116000223340ustar00rootroot00000000000000/* Copyright (c) 2015, 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 "test.hpp" #include "libtorrent/sha1_hash.hpp" using namespace libtorrent; static sha1_hash to_hash(char const* s) { sha1_hash ret; from_hex(s, 40, (char*)&ret[0]); return ret; } TORRENT_TEST(sha1_hash) { sha1_hash h1(0); sha1_hash h2(0); TEST_CHECK(h1 == h2); TEST_CHECK(!(h1 != h2)); TEST_CHECK(!(h1 < h2)); TEST_CHECK(!(h1 < h2)); TEST_CHECK(h1.is_all_zeros()); h1 = to_hash("0123456789012345678901234567890123456789"); h2 = to_hash("0113456789012345678901234567890123456789"); TEST_CHECK(h2 < h1); TEST_CHECK(h2 == h2); TEST_CHECK(h1 == h1); h2.clear(); TEST_CHECK(h2.is_all_zeros()); h2 = to_hash("ffffffffff0000000000ffffffffff0000000000"); h1 = to_hash("fffff00000fffff00000fffff00000fffff00000"); h1 &= h2; TEST_CHECK(h1 == to_hash("fffff000000000000000fffff000000000000000")); h2 = to_hash("ffffffffff0000000000ffffffffff0000000000"); h1 = to_hash("fffff00000fffff00000fffff00000fffff00000"); h1 |= h2; TEST_CHECK(h1 == to_hash("fffffffffffffff00000fffffffffffffff00000")); h2 = to_hash("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); h1 ^= h2; TEST_CHECK(h1 == to_hash("f0f0f0f0f0f0f0ff0f0ff0f0f0f0f0f0f0ff0f0f")); TEST_CHECK(h1 != h2); h2 = sha1_hash(" "); TEST_CHECK(h2 == to_hash("2020202020202020202020202020202020202020")); h1 = to_hash("ffffffffff0000000000ffffffffff0000000000"); h1 <<= 12; TEST_CHECK(h1 == to_hash("fffffff0000000000ffffffffff0000000000000")); h1 >>= 12; TEST_CHECK(h1 == to_hash("000fffffff0000000000ffffffffff0000000000")); h1 = to_hash("7000000000000000000000000000000000000000"); h1 <<= 1; TEST_CHECK(h1 == to_hash("e000000000000000000000000000000000000000")); h1 = to_hash("0000000000000000000000000000000000000007"); h1 <<= 1; TEST_CHECK(h1 == to_hash("000000000000000000000000000000000000000e")); h1 = to_hash("0000000000000000000000000000000000000007"); h1 >>= 1; TEST_CHECK(h1 == to_hash("0000000000000000000000000000000000000003")); h1 = to_hash("7000000000000000000000000000000000000000"); h1 >>= 1; TEST_CHECK(h1 == to_hash("3800000000000000000000000000000000000000")); h1 = to_hash("7000000000000000000000000000000000000000"); h1 >>= 32; TEST_CHECK(h1 == to_hash("0000000070000000000000000000000000000000")); h1 >>= 33; TEST_CHECK(h1 == to_hash("0000000000000000380000000000000000000000")); h1 <<= 33; TEST_CHECK(h1 == to_hash("0000000070000000000000000000000000000000")); } libtorrent-rasterbar-1.1.13/test/test_sliding_average.cpp000066400000000000000000000067741351156116000236260ustar00rootroot00000000000000/* Copyright (c) 2014, 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 "test.hpp" #include "libtorrent/sliding_average.hpp" // normal distributed samples. mean=60 stddev=10 int samples[] = { 49, 51, 60, 46, 65, 53, 76, 59, 57, 54, 56, 51, 45, 80, 53, 62, 69, 67, 66, 56, 56, 61, 52, 61, 61, 62, 59, 53, 48, 68, 47, 47, 63, 51, 53, 54, 46, 65, 64, 64, 45, 68, 64, 66, 53, 42, 57, 58, 57, 47, 55, 59, 64, 61, 37, 67, 55, 52, 60, 60, 44, 57, 50, 77, 56, 54, 49, 68, 66, 64, 47, 60, 46, 47, 81, 74, 65, 62, 44, 75, 65, 43, 58, 59, 53, 67, 49, 51, 33, 47, 49, 50, 54, 48, 55, 80, 67, 51, 66, 52, 48, 57, 30, 51, 72, 65, 78, 56, 74, 68, 49, 66, 63, 57, 61, 62, 64, 62, 61, 52, 67, 64, 59, 61, 69, 60, 54, 69 }; using namespace libtorrent; // make sure we react quickly for the first few samples TORRENT_TEST(reaction_time) { sliding_average<10> avg; avg.add_sample(-10); avg.add_sample(10); TEST_EQUAL(avg.mean(), 0); } TORRENT_TEST(reaction_time2) { sliding_average<10> avg; avg.add_sample(10); avg.add_sample(20); TEST_EQUAL(avg.mean(), 15); } // make sure we converge TORRENT_TEST(converge) { sliding_average<10> avg; avg.add_sample(100); for (int i = 0; i < 20; ++i) avg.add_sample(10); TEST_CHECK(abs(avg.mean() - 10) <= 3); } TORRENT_TEST(converge2) { sliding_average<10> avg; avg.add_sample(-100); for (int i = 0; i < 20; ++i) avg.add_sample(-10); TEST_CHECK(abs(avg.mean() + 10) <= 3); } // test with a more realistic input TORRENT_TEST(random_converge) { sliding_average<10> avg; for (int i = 0; i < int(sizeof(samples)/sizeof(samples[0])); ++i) avg.add_sample(samples[i]); TEST_CHECK(abs(avg.mean() - 60) <= 3); } TORRENT_TEST(sliding_average) { sliding_average<4> avg; TEST_EQUAL(avg.mean(), 0); TEST_EQUAL(avg.avg_deviation(), 0); avg.add_sample(500); TEST_EQUAL(avg.mean(), 500); TEST_EQUAL(avg.avg_deviation(), 0); avg.add_sample(501); TEST_EQUAL(avg.avg_deviation(), 1); avg.add_sample(0); avg.add_sample(0); printf("avg: %d dev: %d\n", avg.mean(), avg.avg_deviation()); TEST_CHECK(abs(avg.mean() - 250) < 50); TEST_CHECK(abs(avg.avg_deviation() - 250) < 80); } libtorrent-rasterbar-1.1.13/test/test_socket_io.cpp000066400000000000000000000127251351156116000224530ustar00rootroot00000000000000/* Copyright (c) 2014, 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 "test.hpp" #include "setup_transfer.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/socket.hpp" #include using namespace libtorrent; using namespace libtorrent::detail; TORRENT_TEST(socket_io) { // test address_to_bytes TEST_EQUAL(address_to_bytes(address_v4::from_string("10.11.12.13")), "\x0a\x0b\x0c\x0d"); TEST_EQUAL(address_to_bytes(address_v4::from_string("16.5.127.1")), "\x10\x05\x7f\x01"); // test endpoint_to_bytes TEST_EQUAL(endpoint_to_bytes(udp::endpoint(address_v4::from_string("10.11.12.13"), 8080)), "\x0a\x0b\x0c\x0d\x1f\x90"); TEST_EQUAL(endpoint_to_bytes(udp::endpoint(address_v4::from_string("16.5.127.1"), 12345)), "\x10\x05\x7f\x01\x30\x39"); std::string buf; std::back_insert_iterator out1(buf); write_address(address_v4::from_string("16.5.128.1"), out1); TEST_EQUAL(buf, "\x10\x05\x80\x01"); std::string::iterator in = buf.begin(); address addr4 = read_v4_address(in); TEST_EQUAL(addr4, address_v4::from_string("16.5.128.1")); buf.clear(); std::back_insert_iterator out2(buf); write_endpoint(udp::endpoint(address_v4::from_string("16.5.128.1"), 1337), out2); TEST_EQUAL(buf, "\x10\x05\x80\x01\x05\x39"); in = buf.begin(); udp::endpoint ep4 = read_v4_endpoint(in); TEST_EQUAL(ep4, udp::endpoint(address_v4::from_string("16.5.128.1"), 1337)); #if TORRENT_USE_IPV6 buf.clear(); std::back_insert_iterator out3(buf); write_address(address_v6::from_string("1000::ffff"), out3); TEST_CHECK(std::equal(buf.begin(), buf.end(), "\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff")); in = buf.begin(); address addr6 = read_v6_address(in); TEST_EQUAL(addr6, address_v6::from_string("1000::ffff")); buf.clear(); std::back_insert_iterator out4(buf); write_endpoint(udp::endpoint(address_v6::from_string("1000::ffff"), 1337), out4); TEST_CHECK(std::equal(buf.begin(), buf.end(), "\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\x05\x39")); TEST_EQUAL(buf.size(), 18); in = buf.begin(); udp::endpoint ep6 = read_v6_endpoint(in); TEST_EQUAL(ep6, udp::endpoint(address_v6::from_string("1000::ffff"), 1337)); #endif char const eplist[] = "l6:\x10\x05\x80\x01\x05\x39" "18:\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\x05\x39" "e"; bdecode_node e; error_code ec; bdecode(eplist, eplist + sizeof(eplist)-1, e, ec); TEST_CHECK(!ec); std::vector list; read_endpoint_list(e, list); #if TORRENT_USE_IPV6 TEST_EQUAL(list.size(), 2); TEST_EQUAL(list[1], udp::endpoint(address_v6::from_string("1000::ffff"), 1337)); #else TEST_EQUAL(list.size(), 1); #endif TEST_EQUAL(list[0], udp::endpoint(address_v4::from_string("16.5.128.1"), 1337)); entry e2 = bdecode(eplist, eplist + sizeof(eplist)-1); list.clear(); read_endpoint_list(&e2, list); #if TORRENT_USE_IPV6 TEST_EQUAL(list.size(), 2); TEST_EQUAL(list[1], udp::endpoint(address_v6::from_string("1000::ffff"), 1337)); #else TEST_EQUAL(list.size(), 1); #endif TEST_EQUAL(list[0], udp::endpoint(address_v4::from_string("16.5.128.1"), 1337)); } TORRENT_TEST(parse_invalid_ipv4_endpoint) { error_code ec; tcp::endpoint endp; endp = parse_endpoint("127.0.0.1-4", ec); TEST_CHECK(ec); ec.clear(); endp = parse_endpoint("127.0.0.1", ec); TEST_CHECK(ec); ec.clear(); endp = parse_endpoint("127.0.0.1:", ec); TEST_CHECK(ec); ec.clear(); endp = parse_endpoint("127.0.0.1X", ec); TEST_CHECK(ec); ec.clear(); endp = parse_endpoint("127.0.0.1:4", ec); TEST_CHECK(!ec); TEST_EQUAL(endp, ep("127.0.0.1", 4)); ec.clear(); } #if TORRENT_USE_IPV6 TORRENT_TEST(parse_invalid_ipv6_endpoint) { error_code ec; tcp::endpoint endp; endp = parse_endpoint("[::1]-4", ec); TEST_CHECK(ec); ec.clear(); endp = parse_endpoint("[::1]", ec); TEST_CHECK(ec); ec.clear(); endp = parse_endpoint("[::1]:", ec); TEST_CHECK(ec); ec.clear(); endp = parse_endpoint("[::1]X", ec); TEST_CHECK(ec); ec.clear(); endp = parse_endpoint("[::1]:4", ec); TEST_CHECK(!ec); TEST_EQUAL(endp, ep("::1", 4)); ec.clear(); } #endif libtorrent-rasterbar-1.1.13/test/test_ssl.cpp000066400000000000000000000464571351156116000213060ustar00rootroot00000000000000/* Copyright (c) 2013, 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/session.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/file.hpp" #include "libtorrent/session_status.hpp" #include "libtorrent/torrent_info.hpp" #include "test.hpp" #include "test_utils.hpp" #include "setup_transfer.hpp" #include "settings.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include #include #include #ifdef TORRENT_USE_OPENSSL #include #include "libtorrent/aux_/disable_warnings_pop.hpp" using namespace libtorrent; using boost::tuples::ignore; struct test_config_t { char const* name; bool use_ssl_ports; bool seed_has_cert; bool downloader_has_cert; bool downloader_has_ssl_listen_port; bool expected_to_complete; int peer_errors; int ssl_disconnects; }; test_config_t const test_config[] = { // name sslport sd-cert dl-cert dl-port expect peer-error ssl-disconn {"nobody has a cert (connect to regular port)", false, false, false, true, false, 0, 1}, {"nobody has a cert (connect to ssl port)", true, false, false, true, false, 1, 1}, {"seed has a cert, but not downloader (connect to regular port)", false, true, false, true, false, 0, 1}, {"seed has a cert, but not downloader (connect to ssl port)", true, true, false, true, false, 1, 1}, {"downloader has a cert, but not seed (connect to regular port)", false, false, true, true, false, 0, 1}, {"downloader has a cert, but not seed (connect to ssl port)", true, false, true, true, false, 1, 1}, {"both downloader and seed has a cert (connect to regular port)", false, true, true, true, false, 0, 1}, {"both downloader and seed has a cert (connect to ssl port)", true, true, true, true, true, 0, 0}, // there is a disconnect (or failed connection attempt), that's not a peer // error though, so both counters stay 0 {"both downloader and seed has a cert (downloader has no SSL port)", true, true, true, false, false, 0, 0}, }; int peer_disconnects = 0; int peer_errors = 0; int ssl_peer_disconnects = 0; bool on_alert(alert const* a) { if (peer_disconnected_alert const* e = alert_cast(a)) { ++peer_disconnects; if (e->error.category() == boost::asio::error::get_ssl_category()) ++ssl_peer_disconnects; } if (peer_error_alert const* e = alert_cast(a)) { ++peer_disconnects; ++peer_errors; if (e->error.category() == boost::asio::error::get_ssl_category()) ++ssl_peer_disconnects; } return false; } void test_ssl(int const test_idx, bool const use_utp) { // these are declared before the session objects // so that they are destructed last. This enables // the sessions to destruct in parallel session_proxy p1; session_proxy p2; test_config_t const& test = test_config[test_idx]; fprintf(stderr, "\n%s TEST: %s Protocol: %s\n\n", time_now_string(), test.name, use_utp ? "uTP": "TCP"); // in case the previous run was terminated error_code ec; remove_all("tmp1_ssl", ec); remove_all("tmp2_ssl", ec); int ssl_port = 1024 + rand() % 50000; settings_pack sett = settings(); sett.set_int(settings_pack::max_retry_port_bind, 100); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); sett.set_bool(settings_pack::enable_incoming_utp, use_utp); sett.set_bool(settings_pack::enable_outgoing_utp, use_utp); sett.set_bool(settings_pack::enable_incoming_tcp, !use_utp); sett.set_bool(settings_pack::enable_outgoing_tcp, !use_utp); sett.set_bool(settings_pack::enable_dht, false); sett.set_bool(settings_pack::enable_lsd, false); sett.set_bool(settings_pack::enable_upnp, false); sett.set_bool(settings_pack::enable_natpmp, false); // if a peer fails once, don't try it again sett.set_int(settings_pack::max_failcount, 1); sett.set_int(settings_pack::ssl_listen, ssl_port); libtorrent::session ses1(sett, 0); if (test.downloader_has_ssl_listen_port) sett.set_int(settings_pack::ssl_listen, ssl_port + 20); else sett.set_int(settings_pack::ssl_listen, 0); libtorrent::session ses2(sett, 0); wait_for_listen(ses1, "ses1"); wait_for_listen(ses2, "ses2"); torrent_handle tor1; torrent_handle tor2; create_directory("tmp1_ssl", ec); std::ofstream file("tmp1_ssl/temporary"); boost::shared_ptr t = ::create_torrent(&file, "temporary" , 16 * 1024, 13, false, combine_path("..", combine_path("ssl", "root_ca_cert.pem"))); file.close(); add_torrent_params addp; addp.save_path = "tmp1_ssl"; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; peer_disconnects = 0; ssl_peer_disconnects = 0; peer_errors = 0; boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 , true, false, false, "_ssl", 16 * 1024, &t, false, &addp, true); if (test.seed_has_cert) { tor1.set_ssl_certificate( combine_path("..", combine_path("ssl", "peer_certificate.pem")) , combine_path("..", combine_path("ssl", "peer_private_key.pem")) , combine_path("..", combine_path("ssl", "dhparams.pem")) , "test"); } if (test.downloader_has_cert) { tor2.set_ssl_certificate( combine_path("..", combine_path("ssl", "peer_certificate.pem")) , combine_path("..", combine_path("ssl", "peer_private_key.pem")) , combine_path("..", combine_path("ssl", "dhparams.pem")) , "test"); } // make sure they've taken effect if (test.downloader_has_cert || test.seed_has_cert) { // this will cause a round-trip to the main thread, and make sure the // previous async. calls have completed ses1.listen_port(); ses2.listen_port(); } wait_for_alert(ses1, torrent_finished_alert::alert_type, "ses1"); wait_for_downloading(ses2, "ses2"); // connect the peers after setting the certificates int port = 0; if (test.use_ssl_ports) if (test.downloader_has_ssl_listen_port) port = ses2.ssl_listen_port(); else port = 13512; else port = ses2.listen_port(); fprintf(stderr, "\n\n%s: ses1: connecting peer port: %d\n\n\n" , time_now_string(), port); tor1.connect_peer(tcp::endpoint(address::from_string("127.0.0.1", ec) , port)); #ifdef TORRENT_USE_VALGRIND const int timeout = 100; #else const int timeout = 40; #endif for (int i = 0; i < timeout; ++i) { print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); torrent_status st1 = tor1.status(); torrent_status st2 = tor2.status(); if (i % 10 == 0) { std::cerr << time_now_string() << " " << "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s " << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " << "\033[0m" << int(st1.progress * 100) << "% " << st1.num_peers << ": " << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " << "\033[0m" << int(st2.progress * 100) << "% " << st2.num_peers << " cc: " << st2.connect_candidates << std::endl; } if (peer_disconnects >= 2) { fprintf(stderr, "too many disconnects (%d), breaking\n", peer_disconnects); break; } if (st2.is_finished) break; if (st2.state != torrent_status::downloading) { static char const* state_str[] = {"checking (q)", "checking", "dl metadata" , "downloading", "finished", "seeding", "allocating", "checking (r)"}; std::cerr << "st2 state: " << state_str[st2.state] << std::endl; } TEST_CHECK(st1.state == torrent_status::seeding || st1.state == torrent_status::checking_files); TEST_CHECK(st2.state == torrent_status::downloading || st2.state == torrent_status::checking_resume_data); test_sleep(100); } fprintf(stderr, "peer_errors: %d expected_errors: %d\n" , peer_errors, test.peer_errors); fprintf(stderr, "ssl_disconnects: %d expected: %d\n", ssl_peer_disconnects, test.ssl_disconnects); if (!use_utp) { TEST_EQUAL(ssl_peer_disconnects > 0, test.ssl_disconnects > 0); TEST_EQUAL(peer_errors > 0, test.peer_errors > 0); } fprintf(stderr, "%s: EXPECT: %s\n", time_now_string(), test.expected_to_complete ? "SUCCEESS" : "FAILURE"); fprintf(stderr, "%s: RESULT: %s\n", time_now_string(), tor2.status().is_seeding ? "SUCCEESS" : "FAILURE"); TEST_EQUAL(tor2.status().is_seeding, test.expected_to_complete); // this allows shutting down the sessions in parallel p1 = ses1.abort(); p2 = ses2.abort(); } std::string password_callback(int length, boost::asio::ssl::context::password_purpose p , std::string pw) { if (p != boost::asio::ssl::context::for_reading) return ""; return pw; } struct attack_t { // flags controlling the connection attempt boost::uint32_t flags; // whether or not we expect to be able to connect bool expect; }; enum attack_flags_t { valid_certificate = 1, invalid_certificate = 2, valid_sni_hash = 4, invalid_sni_hash = 8, valid_bittorrent_hash = 16, }; attack_t const attacks[] = { // positive test { valid_certificate | valid_sni_hash | valid_bittorrent_hash, true}, // SNI { valid_certificate | invalid_sni_hash | valid_bittorrent_hash, false}, { valid_certificate | valid_bittorrent_hash, false}, // certificate { valid_sni_hash | valid_bittorrent_hash, false}, { invalid_certificate | valid_sni_hash | valid_bittorrent_hash, false}, // bittorrent hash { valid_certificate | valid_sni_hash, false}, }; const int num_attacks = sizeof(attacks)/sizeof(attacks[0]); bool try_connect(libtorrent::session& ses1, int port , boost::shared_ptr const& t, boost::uint32_t flags) { using boost::asio::ssl::context; fprintf(stderr, "\nMALICIOUS PEER TEST: "); if (flags & invalid_certificate) fprintf(stderr, "invalid-certificate "); else if (flags & valid_certificate) fprintf(stderr, "valid-certificate "); else fprintf(stderr, "no-certificate "); if (flags & invalid_sni_hash) fprintf(stderr, "invalid-SNI-hash "); else if (flags & valid_sni_hash) fprintf(stderr, "valid-SNI-hash "); else fprintf(stderr, "no-SNI-hash "); if (flags & valid_bittorrent_hash) fprintf(stderr, "valid-bittorrent-hash "); else fprintf(stderr, "invalid-bittorrent-hash "); fprintf(stderr, " port: %d\n", port); error_code ec; boost::asio::io_service ios; // create the SSL context for this torrent. We need to // inject the root certificate, and no other, to // verify other peers against context ctx(context::sslv23); ctx.set_options(context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); // we're a malicious peer, we don't have any interest // in verifying peers ctx.set_verify_mode(context::verify_none, ec); if (ec) { fprintf(stderr, "Failed to set SSL verify mode: %s\n" , ec.message().c_str()); TEST_CHECK(!ec); return false; } std::string certificate = combine_path("..", combine_path("ssl", "peer_certificate.pem")); std::string private_key = combine_path("..", combine_path("ssl", "peer_private_key.pem")); std::string dh_params = combine_path("..", combine_path("ssl", "dhparams.pem")); if (flags & invalid_certificate) { certificate = combine_path("..", combine_path("ssl", "invalid_peer_certificate.pem")); private_key = combine_path("..", combine_path("ssl", "invalid_peer_private_key.pem")); } // TODO: test using a signed certificate with the wrong info-hash in DN if (flags & (valid_certificate | invalid_certificate)) { fprintf(stderr, "set_password_callback\n"); ctx.set_password_callback(boost::bind(&password_callback, _1, _2, "test"), ec); if (ec) { fprintf(stderr, "Failed to set certificate password callback: %s\n" , ec.message().c_str()); TEST_CHECK(!ec); return false; } fprintf(stderr, "use_certificate_file \"%s\"\n", certificate.c_str()); ctx.use_certificate_file(certificate, context::pem, ec); if (ec) { fprintf(stderr, "Failed to set certificate file: %s\n" , ec.message().c_str()); TEST_CHECK(!ec); return false; } fprintf(stderr, "use_private_key_file \"%s\"\n", private_key.c_str()); ctx.use_private_key_file(private_key, context::pem, ec); if (ec) { fprintf(stderr, "Failed to set private key: %s\n" , ec.message().c_str()); TEST_CHECK(!ec); return false; } fprintf(stderr, "use_tmp_dh_file \"%s\"\n", dh_params.c_str()); ctx.use_tmp_dh_file(dh_params, ec); if (ec) { fprintf(stderr, "Failed to set DH params: %s\n" , ec.message().c_str()); TEST_CHECK(!ec); return false; } } boost::asio::ssl::stream ssl_sock(ios, ctx); fprintf(stderr, "connecting 127.0.0.1:%d\n", port); ssl_sock.lowest_layer().connect(tcp::endpoint( address_v4::from_string("127.0.0.1"), port), ec); print_alerts(ses1, "ses1", true, true, true, &on_alert); if (ec) { fprintf(stderr, "Failed to connect: %s\n" , ec.message().c_str()); TEST_CHECK(!ec); return false; } if (flags & valid_sni_hash) { std::string name = to_hex(t->info_hash().to_string()); fprintf(stderr, "SNI: %s\n", name.c_str()); SSL_set_tlsext_host_name(ssl_sock.native_handle(), name.c_str()); } else if (flags & invalid_sni_hash) { char const hex_alphabet[] = "0123456789abcdef"; std::string name; name.reserve(40); for (int i = 0; i < 40; ++i) name += hex_alphabet[rand() % 16]; fprintf(stderr, "SNI: %s\n", name.c_str()); SSL_set_tlsext_host_name(ssl_sock.native_handle(), name.c_str()); } fprintf(stderr, "SSL handshake\n"); ssl_sock.handshake(boost::asio::ssl::stream_base::client, ec); print_alerts(ses1, "ses1", true, true, true, &on_alert); if (ec) { fprintf(stderr, "Failed SSL handshake: %s\n" , ec.message().c_str()); return false; } char handshake[] = "\x13" "BitTorrent protocol\0\0\0\0\0\0\0\x04" " " // space for info-hash "aaaaaaaaaaaaaaaaaaaa" // peer-id "\0\0\0\x01\x02"; // interested // fill in the info-hash if (flags & valid_bittorrent_hash) { std::memcpy(handshake + 28, &t->info_hash()[0], 20); } else { // TODO: also test using a hash that refers to a valid torrent // but that differs from the SNI hash std::generate(handshake + 28, handshake + 48, &rand); } // fill in the peer-id std::generate(handshake + 48, handshake + 68, &rand); fprintf(stderr, "bittorrent handshake\n"); boost::asio::write(ssl_sock, boost::asio::buffer(handshake, (sizeof(handshake) - 1)), ec); print_alerts(ses1, "ses1", true, true, true, &on_alert); if (ec) { fprintf(stderr, "failed to write bittorrent handshake: %s\n" , ec.message().c_str()); return false; } char buf[68]; fprintf(stderr, "read bittorrent handshake\n"); boost::asio::read(ssl_sock, boost::asio::buffer(buf, sizeof(buf)), ec); print_alerts(ses1, "ses1", true, true, true, &on_alert); if (ec) { fprintf(stderr, "failed to read bittorrent handshake: %s\n" , ec.message().c_str()); return false; } if (memcmp(buf, "\x13" "BitTorrent protocol", 20) != 0) { fprintf(stderr, "invalid bittorrent handshake\n"); return false; } if (memcmp(buf + 28, &t->info_hash()[0], 20) != 0) { fprintf(stderr, "invalid info-hash in bittorrent handshake\n"); return false; } fprintf(stderr, "successfully connected over SSL and shook hand over bittorrent\n"); return true; } void test_malicious_peer() { error_code ec; remove_all("tmp3_ssl", ec); // set up session int ssl_port = 1024 + rand() % 50000; settings_pack sett = settings(); sett.set_int(settings_pack::max_retry_port_bind, 100); sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); sett.set_int(settings_pack::ssl_listen, ssl_port); sett.set_bool(settings_pack::enable_dht, false); sett.set_bool(settings_pack::enable_lsd, false); sett.set_bool(settings_pack::enable_upnp, false); sett.set_bool(settings_pack::enable_natpmp, false); libtorrent::session ses1(sett, 0); wait_for_listen(ses1, "ses1"); // create torrent create_directory("tmp3_ssl", ec); std::ofstream file("tmp3_ssl/temporary"); boost::shared_ptr t = ::create_torrent(&file, "temporary" , 16 * 1024, 13, false, combine_path("..", combine_path("ssl", "root_ca_cert.pem"))); file.close(); TEST_CHECK(!t->ssl_cert().empty()); add_torrent_params addp; addp.save_path = "tmp3_ssl"; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; addp.ti = t; torrent_handle tor1 = ses1.add_torrent(addp, ec); tor1.set_ssl_certificate( combine_path("..", combine_path("ssl", "peer_certificate.pem")) , combine_path("..", combine_path("ssl", "peer_private_key.pem")) , combine_path("..", combine_path("ssl", "dhparams.pem")) , "test"); alert const* a = wait_for_alert(ses1 , torrent_finished_alert::alert_type, "ses1"); TEST_CHECK(a); if (a) { TEST_EQUAL(a->type(), torrent_finished_alert::alert_type); } for (int i = 0; i < num_attacks; ++i) { bool success = try_connect(ses1, ssl_port, t, attacks[i].flags); TEST_EQUAL(success, attacks[i].expect); } } #endif // TORRENT_USE_OPENSSL TORRENT_TEST(malicious_peer) { #ifdef TORRENT_USE_OPENSSL test_malicious_peer(); #endif } #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(utp_config0) { test_ssl(0, true); } TORRENT_TEST(utp_config1) { test_ssl(1, true); } TORRENT_TEST(utp_config2) { test_ssl(2, true); } TORRENT_TEST(utp_config3) { test_ssl(3, true); } TORRENT_TEST(utp_config4) { test_ssl(4, true); } TORRENT_TEST(utp_config5) { test_ssl(5, true); } TORRENT_TEST(utp_config6) { test_ssl(6, true); } TORRENT_TEST(utp_config7) { test_ssl(7, true); } TORRENT_TEST(utp_config8) { test_ssl(8, true); } TORRENT_TEST(tcp_config0) { test_ssl(0, false); } TORRENT_TEST(tcp_config1) { test_ssl(1, false); } TORRENT_TEST(tcp_config2) { test_ssl(2, false); } TORRENT_TEST(tcp_config3) { test_ssl(3, false); } TORRENT_TEST(tcp_config4) { test_ssl(4, false); } TORRENT_TEST(tcp_config5) { test_ssl(5, false); } TORRENT_TEST(tcp_config6) { test_ssl(6, false); } TORRENT_TEST(tcp_config7) { test_ssl(7, false); } TORRENT_TEST(tcp_config8) { test_ssl(8, false); } #endif libtorrent-rasterbar-1.1.13/test/test_stack_allocator.cpp000066400000000000000000000060771351156116000236440ustar00rootroot00000000000000/* Copyright (c) 2016, 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 "test.hpp" #include "libtorrent/stack_allocator.hpp" using libtorrent::aux::stack_allocator; TORRENT_TEST(copy_string) { stack_allocator a; int const idx1 = a.copy_string("testing"); // attempt to trigger a reallocation a.allocate(100000); int const idx2 = a.copy_string(std::string("foobar")); TEST_CHECK(strcmp(a.ptr(idx1), "testing") == 0); TEST_CHECK(strcmp(a.ptr(idx2), "foobar") == 0); } TORRENT_TEST(copy_buffer) { stack_allocator a; int const idx1 = a.copy_buffer("testing", 8); // attempt to trigger a reallocation a.allocate(100000); TEST_CHECK(strcmp(a.ptr(idx1), "testing") == 0); // attempt zero size allocation int const idx2 = a.copy_buffer("nothing", 0); TEST_CHECK(idx2 == -1); // attempt to get a pointer after zero allocation char* ptr = a.ptr(idx2); TEST_CHECK(ptr == NULL); } TORRENT_TEST(allocate) { stack_allocator a; int const idx1 = a.allocate(100); char* ptr = a.ptr(idx1); for (int i = 0; i < 100; ++i) ptr[i] = char(i % 256); // attempt to trigger a reallocation a.allocate(100000); ptr = a.ptr(idx1); for (int i = 0; i < 100; ++i) TEST_CHECK(ptr[i] == char(i % 256)); // attempt zero size allocation int const idx2 = a.allocate(0); TEST_CHECK(idx2 == -1); // attempt to get a pointer after zero allocation ptr = a.ptr(idx2); TEST_CHECK(ptr == NULL); } TORRENT_TEST(swap) { stack_allocator a1; stack_allocator a2; int const idx1 = a1.copy_string("testing"); int const idx2 = a2.copy_string("foobar"); a1.swap(a2); TEST_CHECK(strcmp(a1.ptr(idx2), "foobar") == 0); TEST_CHECK(strcmp(a2.ptr(idx1), "testing") == 0); } libtorrent-rasterbar-1.1.13/test/test_stat_cache.cpp000066400000000000000000000053421351156116000225670ustar00rootroot00000000000000/* Copyright (c) 2012, 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/stat_cache.hpp" #include "libtorrent/error_code.hpp" #include "test.hpp" using namespace libtorrent; TORRENT_TEST(stat_cache) { error_code ec; stat_cache sc; sc.init(10); for (int i = 0; i < 10; ++i) { TEST_CHECK(sc.get_filesize(i) == stat_cache::not_in_cache); TEST_CHECK(sc.get_filetime(i) == stat_cache::not_in_cache); } // out of bound accesses count as not-in-cache TEST_CHECK(sc.get_filesize(10) == stat_cache::not_in_cache); TEST_CHECK(sc.get_filesize(11) == stat_cache::not_in_cache); sc.set_error(3); TEST_CHECK(sc.get_filesize(3) == stat_cache::cache_error); sc.set_noexist(3); TEST_CHECK(sc.get_filesize(3) == stat_cache::no_exist); sc.set_cache(3, 101, 5555); TEST_CHECK(sc.get_filesize(3) == 101); TEST_CHECK(sc.get_filetime(3) == 5555); sc.set_error(11); TEST_CHECK(sc.get_filesize(10) == stat_cache::not_in_cache); TEST_CHECK(sc.get_filesize(11) == stat_cache::cache_error); sc.set_noexist(13); TEST_CHECK(sc.get_filesize(12) == stat_cache::not_in_cache); TEST_CHECK(sc.get_filesize(13) == stat_cache::no_exist); sc.set_cache(15, 1000, 3000); TEST_CHECK(sc.get_filesize(14) == stat_cache::not_in_cache); TEST_CHECK(sc.get_filesize(15) == 1000); TEST_CHECK(sc.get_filetime(15) == 3000); } libtorrent-rasterbar-1.1.13/test/test_storage.cpp000066400000000000000000001306351351156116000221410ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "test_utils.hpp" #include "settings.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/file_pool.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/session.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/torrent_info.hpp" #include #include #include #include #include using namespace libtorrent; namespace lt = libtorrent; const int piece_size = 16 * 1024 * 16; const int half = piece_size / 2; void signal_bool(bool* b, char const* string) { *b = true; std::cerr << time_now_string() << " " << string << std::endl; } void on_read_piece(int ret, disk_io_job const& j, char const* data, int size) { std::cerr << time_now_string() << " on_read_piece piece: " << j.piece << std::endl; TEST_EQUAL(ret, size); if (ret > 0) TEST_CHECK(std::equal(j.buffer.disk_block, j.buffer.disk_block + ret, data)); } void on_check_resume_data(disk_io_job const* j, bool* done) { std::cerr << time_now_string() << " on_check_resume_data ret: " << j->ret; switch (j->ret) { case piece_manager::no_error: std::cerr << time_now_string() << " success" << std::endl; break; case piece_manager::fatal_disk_error: std::cerr << time_now_string() << " disk error: " << j->error.ec.message() << " file: " << j->error.file << std::endl; break; case piece_manager::need_full_check: std::cerr << time_now_string() << " need full check" << std::endl; break; case piece_manager::disk_check_aborted: std::cerr << time_now_string() << " aborted" << std::endl; break; } std::cerr << std::endl; *done = true; } void print_error(char const* call, int ret, storage_error const& ec) { fprintf(stderr, "%s: %s() returned: %d error: \"%s\" in file: %d operation: %d\n" , time_now_string(), call, ret, ec.ec.message().c_str(), ec.file, ec.operation); } void run_until(io_service& ios, bool const& done) { while (!done) { ios.reset(); error_code ec; ios.run_one(ec); if (ec) { std::cerr << "run_one: " << ec.message().c_str() << std::endl; return; } std::cerr << time_now_string() << " done: " << done << std::endl; } } void nop() {} boost::shared_ptr setup_torrent_info(file_storage& fs , std::vector& buf) { fs.add_file(combine_path("temp_storage", "test1.tmp"), 8); fs.add_file(combine_path("temp_storage", combine_path("folder1", "test2.tmp")), 8); fs.add_file(combine_path("temp_storage", combine_path("folder2", "test3.tmp")), 0); fs.add_file(combine_path("temp_storage", combine_path("_folder3", "test4.tmp")), 0); fs.add_file(combine_path("temp_storage", combine_path("_folder3", combine_path("subfolder", "test5.tmp"))), 8); libtorrent::create_torrent t(fs, 4, -1, 0); char buf_[4] = {0, 0, 0, 0}; sha1_hash h = hasher(buf_, 4).final(); for (int i = 0; i < 6; ++i) t.set_hash(i, h); bencode(std::back_inserter(buf), t.generate()); error_code ec; boost::shared_ptr info(boost::make_shared(&buf[0] , buf.size(), boost::ref(ec), 0)); if (ec) { fprintf(stderr, "torrent_info constructor failed: %s\n" , ec.message().c_str()); } return info; } boost::shared_ptr setup_torrent(file_storage& fs , file_pool& fp , std::vector& buf , std::string const& test_path , aux::session_settings& set) { boost::shared_ptr info = setup_torrent_info(fs, buf); storage_params p; p.files = &fs; p.pool = &fp; p.path = test_path; p.mode = storage_mode_allocate; boost::shared_ptr s(new default_storage(p)); s->m_settings = &set; // allocate the files and create the directories storage_error se; s->initialize(se); if (se) { TEST_ERROR(se.ec.message().c_str()); fprintf(stderr, "default_storage::initialize %s: %d\n", se.ec.message().c_str(), int(se.file)); } return s; } typedef boost::shared_array buf_ptr; buf_ptr new_piece(int size) { buf_ptr ret(static_cast(malloc(size)), &free); std::generate(ret.get(), ret.get() + size, random_byte); return ret; } void run_storage_tests(boost::shared_ptr info , file_storage& fs , std::string const& test_path , libtorrent::storage_mode_t storage_mode , bool unbuffered) { TORRENT_ASSERT(fs.num_files() > 0); error_code ec; create_directory(combine_path(test_path, "temp_storage"), ec); if (ec) std::cerr << "create_directory '" << combine_path(test_path, "temp_storage") << "': " << ec.message() << std::endl; remove_all(combine_path(test_path, "temp_storage2"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage2") << "': " << ec.message() << std::endl; remove_all(combine_path(test_path, "part0"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "part0") << "': " << ec.message() << std::endl; int num_pieces = fs.num_pieces(); TEST_CHECK(info->num_pieces() == num_pieces); buf_ptr piece0 = new_piece(piece_size); buf_ptr piece1 = new_piece(piece_size); buf_ptr piece2 = new_piece(piece_size); aux::session_settings set; set.set_int(settings_pack::disk_io_write_mode , unbuffered ? settings_pack::disable_os_cache : settings_pack::enable_os_cache); set.set_int(settings_pack::disk_io_read_mode , unbuffered ? settings_pack::disable_os_cache : settings_pack::enable_os_cache); char* piece = static_cast(malloc(piece_size)); { // avoid having two storages use the same files file_pool fp; boost::asio::io_service ios; disk_buffer_pool dp(16 * 1024, ios, boost::bind(&nop)); storage_params p; p.path = test_path; p.files = &fs; p.pool = &fp; p.mode = storage_mode; boost::scoped_ptr s(new default_storage(p)); s->m_settings = &set; storage_error ec; s->initialize(ec); TEST_CHECK(!ec); if (ec) print_error("initialize", 0, ec); int ret = 0; // write piece 1 (in slot 0) file::iovec_t iov = { piece1.get(), half}; ret = s->writev(&iov, 1, 0, 0, 0, ec); if (ret != half) print_error("writev", ret, ec); iov.iov_base = piece1.get() + half; iov.iov_len = half; ret = s->writev(&iov, 1, 0, half, 0, ec); if (ret != half) print_error("writev", ret, ec); // test unaligned read (where the bytes are aligned) iov.iov_base = piece + 3; iov.iov_len = piece_size - 9; ret = s->readv(&iov, 1, 0, 3, 0, ec); if (ret != piece_size - 9) print_error("readv",ret, ec); TEST_CHECK(std::equal(piece+3, piece + piece_size-9, piece1.get()+3)); // test unaligned read (where the bytes are not aligned) iov.iov_base = piece; iov.iov_len = piece_size - 9; ret = s->readv(&iov, 1, 0, 3, 0, ec); TEST_CHECK(ret == piece_size - 9); if (ret != piece_size - 9) print_error("readv", ret, ec); TEST_CHECK(std::equal(piece, piece + piece_size-9, piece1.get()+3)); // verify piece 1 iov.iov_base = piece; iov.iov_len = piece_size; ret = s->readv(&iov, 1, 0, 0, 0, ec); TEST_CHECK(ret == piece_size); if (ret != piece_size) print_error("readv", ret, ec); TEST_CHECK(std::equal(piece, piece + piece_size, piece1.get())); // do the same with piece 0 and 2 (in slot 1 and 2) iov.iov_base = piece0.get(); iov.iov_len = piece_size; ret = s->writev(&iov, 1, 1, 0, 0, ec); if (ret != piece_size) print_error("writev", ret, ec); iov.iov_base = piece2.get(); iov.iov_len = piece_size; ret = s->writev(&iov, 1, 2, 0, 0, ec); if (ret != piece_size) print_error("writev", ret, ec); // verify piece 0 and 2 iov.iov_base = piece; iov.iov_len = piece_size; ret = s->readv(&iov, 1, 1, 0, 0, ec); if (ret != piece_size) print_error("readv", ret, ec); TEST_CHECK(std::equal(piece, piece + piece_size, piece0.get())); iov.iov_base = piece; iov.iov_len = piece_size; ret = s->readv(&iov, 1, 2, 0, 0, ec); if (ret != piece_size) print_error("readv", ret, ec); TEST_CHECK(std::equal(piece, piece + piece_size, piece2.get())); s->release_files(ec); } free(piece); } void test_remove(std::string const& test_path, bool unbuffered) { error_code ec; remove_all(combine_path(test_path, "temp_storage"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") << "': " << ec.message() << std::endl; TEST_CHECK(!exists(combine_path(test_path, "temp_storage"))); file_storage fs; std::vector buf; file_pool fp; io_service ios; disk_buffer_pool dp(16 * 1024, ios, boost::bind(&nop)); aux::session_settings set; set.set_int(settings_pack::disk_io_write_mode , unbuffered ? settings_pack::disable_os_cache : settings_pack::enable_os_cache); set.set_int(settings_pack::disk_io_read_mode , unbuffered ? settings_pack::disable_os_cache : settings_pack::enable_os_cache); boost::shared_ptr s = setup_torrent(fs, fp, buf, test_path, set); // directories are not created up-front, unless they contain // an empty file (all of which are created up-front, along with // all required directories) // files are created on first write TEST_CHECK(!exists(combine_path(test_path, combine_path("temp_storage" , combine_path("_folder3", combine_path("subfolder", "test5.tmp")))))); // this directory and file is created up-front because it's an empty file TEST_CHECK(exists(combine_path(test_path, combine_path("temp_storage" , combine_path("folder2", "test3.tmp"))))); // this isn't TEST_CHECK(!exists(combine_path(test_path, combine_path("temp_storage" , combine_path("folder1", "test2.tmp"))))); file::iovec_t b = {&buf[0], 4}; storage_error se; s->writev(&b, 1, 2, 0, 0, se); TEST_CHECK(exists(combine_path(test_path, combine_path("temp_storage" , combine_path("folder1", "test2.tmp"))))); TEST_CHECK(!exists(combine_path(test_path, combine_path("temp_storage" , combine_path("_folder3", combine_path("subfolder", "test5.tmp")))))); file_status st; stat_file(combine_path(test_path, combine_path("temp_storage" , combine_path("folder1", "test2.tmp"))), &st, ec); TEST_EQUAL(st.file_size, 8); s->writev(&b, 1, 4, 0, 0, se); TEST_CHECK(exists(combine_path(test_path, combine_path("temp_storage" , combine_path("_folder3", combine_path("subfolder", "test5.tmp")))))); stat_file(combine_path(test_path, combine_path("temp_storage" , combine_path("_folder3", "test5.tmp"))), &st, ec); TEST_EQUAL(st.file_size, 8); s->delete_files(session::delete_files, se); if (se) print_error("delete_files", 0, se.ec); if (se) { TEST_ERROR(se.ec.message().c_str()); fprintf(stderr, "default_storage::delete_files %s: %d\n", se.ec.message().c_str(), int(se.file)); } TEST_CHECK(!exists(combine_path(test_path, "temp_storage"))); } void test_rename(std::string const& test_path) { error_code ec; remove_all(combine_path(test_path, "temp_storage"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") << "': " << ec.message() << std::endl; TEST_CHECK(!exists(combine_path(test_path, "temp_storage"))); file_storage fs; std::vector buf; file_pool fp; io_service ios; disk_buffer_pool dp(16 * 1024, ios, boost::bind(&nop)); aux::session_settings set; boost::shared_ptr s = setup_torrent(fs, fp, buf, test_path , set); // directories are not created up-front, unless they contain // an empty file std::string first_file = fs.file_path(0); for (int i = 0; i < fs.num_files(); ++i) { TEST_CHECK(!exists(combine_path(test_path, combine_path("temp_storage" , fs.file_path(i))))); } storage_error se; s->rename_file(0, "new_filename", se); if (se.ec) { fprintf(stderr, "default_storage::rename_file failed: %s\n" , se.ec.message().c_str()); } TEST_CHECK(!se.ec); TEST_EQUAL(s->files().file_path(0), "new_filename"); } struct test_error_storage : default_storage { test_error_storage(storage_params const& params) : default_storage(params) {}; bool has_any_file(storage_error& ec) TORRENT_FINAL { ec.ec = make_error_code(boost::system::errc::permission_denied); ec.operation = storage_error::stat; return false; } }; void test_check_files(std::string const& test_path , libtorrent::storage_mode_t storage_mode , bool unbuffered) { boost::shared_ptr info; error_code ec; const int piece_size = 16 * 1024; remove_all(combine_path(test_path, "temp_storage"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") << "': " << ec.message() << std::endl; file_storage fs; fs.add_file("temp_storage/test1.tmp", piece_size); fs.add_file("temp_storage/test2.tmp", piece_size * 2); fs.add_file("temp_storage/test3.tmp", piece_size); buf_ptr piece0 = new_piece(piece_size); buf_ptr piece2 = new_piece(piece_size); libtorrent::create_torrent t(fs, piece_size, -1, 0); t.set_hash(0, hasher(piece0.get(), piece_size).final()); t.set_hash(1, sha1_hash(0)); t.set_hash(2, sha1_hash(0)); t.set_hash(3, hasher(piece2.get(), piece_size).final()); create_directory(combine_path(test_path, "temp_storage"), ec); if (ec) std::cerr << "create_directory: " << ec.message() << std::endl; std::ofstream f; f.open(combine_path(test_path, combine_path("temp_storage", "test1.tmp")).c_str() , std::ios::trunc | std::ios::binary); f.write(piece0.get(), piece_size); f.close(); f.open(combine_path(test_path, combine_path("temp_storage", "test3.tmp")).c_str() , std::ios::trunc | std::ios::binary); f.write(piece2.get(), piece_size); f.close(); std::vector buf; bencode(std::back_inserter(buf), t.generate()); info = boost::make_shared(&buf[0], buf.size(), boost::ref(ec), 0); file_pool fp; boost::asio::io_service ios; counters cnt; disk_io_thread io(ios, cnt, NULL); io.set_num_threads(1); std::vector prio(info->num_files(), 0); storage_params p; p.files = &fs; p.path = test_path; p.pool = &fp; p.mode = storage_mode; p.priorities = &prio; boost::shared_ptr dummy; boost::shared_ptr pm = boost::make_shared(new test_error_storage(p), dummy, &fs); bool done = false; bdecode_node frd; std::vector links; io.async_check_fastresume(pm.get(), &frd, links , boost::bind(&on_check_resume_data, _1, &done)); io.submit_jobs(); run_until(ios, done); for (int i = 0; i < info->num_pieces(); ++i) { done = false; io.async_hash(pm.get(), i, disk_io_job::sequential_access | disk_io_job::volatile_read , boost::bind(&on_check_resume_data, _1, &done), reinterpret_cast(1)); io.submit_jobs(); run_until(ios, done); } io.abort(true); } // TODO: 2 split this test up into smaller parts void run_test(bool unbuffered) { std::string test_path = current_working_directory(); std::cerr << "\n=== " << test_path << " ===\n" << std::endl; boost::shared_ptr info; buf_ptr piece0 = new_piece(piece_size); buf_ptr piece1 = new_piece(piece_size); buf_ptr piece2 = new_piece(piece_size); buf_ptr piece3 = new_piece(piece_size); { error_code ec; remove_all(combine_path(test_path, "temp_storage"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") << "': " << ec.message() << std::endl; file_storage fs; fs.add_file("temp_storage/test1.tmp", 17); fs.add_file("temp_storage/test2.tmp", 612); fs.add_file("temp_storage/test3.tmp", 0); fs.add_file("temp_storage/test4.tmp", 0); fs.add_file("temp_storage/test5.tmp", 3253); fs.add_file("temp_storage/test6.tmp", 841); const int last_file_size = 4 * piece_size - fs.total_size(); fs.add_file("temp_storage/test7.tmp", last_file_size); // File layout // +-+--+++-------+-------+----------------------------------------------------------------------------------------+ // |1| 2||| file5 | file6 | file7 | // +-+--+++-------+-------+----------------------------------------------------------------------------------------+ // | | | | | // | piece 0 | piece 1 | piece 2 | piece 3 | libtorrent::create_torrent t(fs, piece_size, -1, 0); TEST_CHECK(t.num_pieces() == 4); t.set_hash(0, hasher(piece0.get(), piece_size).final()); t.set_hash(1, hasher(piece1.get(), piece_size).final()); t.set_hash(2, hasher(piece2.get(), piece_size).final()); t.set_hash(3, hasher(piece3.get(), piece_size).final()); std::vector buf; bencode(std::back_inserter(buf), t.generate()); info = boost::make_shared(&buf[0], buf.size(), boost::ref(ec), 0); std::cerr << "=== test 1 === " << (unbuffered?"unbuffered":"buffered") << std::endl; // run_storage_tests writes piece 0, 1 and 2. not 3 run_storage_tests(info, fs, test_path, storage_mode_sparse, unbuffered); // make sure the files have the correct size std::string base = combine_path(test_path, "temp_storage"); fprintf(stderr, "base = \"%s\"\n", base.c_str()); TEST_EQUAL(file_size(combine_path(base, "test1.tmp")), 17); TEST_EQUAL(file_size(combine_path(base, "test2.tmp")), 612); // these files should have been allocated as 0 size TEST_CHECK(exists(combine_path(base, "test3.tmp"))); TEST_CHECK(exists(combine_path(base, "test4.tmp"))); TEST_CHECK(file_size(combine_path(base, "test3.tmp")) == 0); TEST_CHECK(file_size(combine_path(base, "test4.tmp")) == 0); TEST_EQUAL(file_size(combine_path(base, "test5.tmp")), 3253); TEST_EQUAL(file_size(combine_path(base, "test6.tmp")), 841); printf("file: %d expected: %d last_file_size: %d, piece_size: %d\n" , int(file_size(combine_path(base, "test7.tmp"))) , int(last_file_size - piece_size), last_file_size, piece_size); TEST_EQUAL(file_size(combine_path(base, "test7.tmp")), last_file_size - piece_size); remove_all(combine_path(test_path, "temp_storage"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") << "': " << ec.message() << std::endl; } // ============================================== { error_code ec; file_storage fs; fs.add_file(combine_path("temp_storage", "test1.tmp"), 3 * piece_size); libtorrent::create_torrent t(fs, piece_size, -1, 0); TEST_CHECK(fs.file_path(0) == combine_path("temp_storage", "test1.tmp")); t.set_hash(0, hasher(piece0.get(), piece_size).final()); t.set_hash(1, hasher(piece1.get(), piece_size).final()); t.set_hash(2, hasher(piece2.get(), piece_size).final()); std::vector buf; bencode(std::back_inserter(buf), t.generate()); info = boost::make_shared(&buf[0], buf.size(), boost::ref(ec), 0); std::cerr << "=== test 3 ===" << std::endl; run_storage_tests(info, fs, test_path, storage_mode_sparse, unbuffered); TEST_EQUAL(file_size(combine_path(test_path, combine_path("temp_storage", "test1.tmp"))), piece_size * 3); remove_all(combine_path(test_path, "temp_storage"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") << "': " << ec.message() << std::endl; // ============================================== std::cerr << "=== test 4 ===" << std::endl; run_storage_tests(info, fs, test_path, storage_mode_allocate, unbuffered); std::cerr << file_size(combine_path(test_path, combine_path("temp_storage", "test1.tmp"))) << std::endl; TEST_EQUAL(file_size(combine_path(test_path, combine_path("temp_storage", "test1.tmp"))), 3 * piece_size); remove_all(combine_path(test_path, "temp_storage"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") << "': " << ec.message() << std::endl; } // ============================================== std::cerr << "=== test 5 ===" << std::endl; test_remove(test_path, unbuffered); // ============================================== std::cerr << "=== test 6 ===" << std::endl; test_check_files(test_path, storage_mode_sparse, unbuffered); test_check_files(test_path, storage_mode_sparse, unbuffered); std::cerr << "=== test 7 ===" << std::endl; test_rename(test_path); } TORRENT_TEST(fastresume) { std::string test_path = current_working_directory(); error_code ec; std::cout << "\n\n=== test fastresume ===" << std::endl; remove_all(combine_path(test_path, "tmp1"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "tmp1") << "': " << ec.message() << std::endl; create_directory(combine_path(test_path, "tmp1"), ec); if (ec) std::cerr << "create_directory '" << combine_path(test_path, "tmp1") << "': " << ec.message() << std::endl; std::ofstream file(combine_path(test_path, "tmp1/temporary").c_str()); boost::shared_ptr t = ::create_torrent(&file); file.close(); TEST_CHECK(exists(combine_path(test_path, "tmp1/temporary"))); if (!exists(combine_path(test_path, "tmp1/temporary"))) return; entry resume; { settings_pack pack = settings(); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_dht, false); lt::session ses(pack); error_code ec; add_torrent_params p; p.ti = boost::make_shared(boost::cref(*t)); p.save_path = combine_path(test_path, "tmp1"); p.storage_mode = storage_mode_sparse; torrent_handle h = ses.add_torrent(p, ec); TEST_CHECK(exists(combine_path(p.save_path, "temporary"))); if (!exists(combine_path(p.save_path, "temporary"))) return; torrent_status s; for (int i = 0; i < 50; ++i) { print_alerts(ses, "ses"); s = h.status(); if (s.progress == 1.0f) { std::cout << "progress: 1.0f" << std::endl; break; } test_sleep(100); } // the whole point of the test is to have a resume // data which expects the file to exist in full. If // we failed to do that, we might as well abort TEST_EQUAL(s.progress, 1.0f); if (s.progress != 1.0f) return; h.save_resume_data(); alert const* ra = wait_for_alert(ses, save_resume_data_alert::alert_type); TEST_CHECK(ra); if (ra) resume = *alert_cast(ra)->resume_data; ses.remove_torrent(h, lt::session::delete_files); alert const* da = wait_for_alert(ses, torrent_deleted_alert::alert_type); TEST_CHECK(da); } TEST_CHECK(!exists(combine_path(test_path, combine_path("tmp1", "temporary")))); if (exists(combine_path(test_path, combine_path("tmp1", "temporary")))) return; std::cerr << resume.to_string() << "\n"; TEST_CHECK(resume.dict().find("file sizes") != resume.dict().end()); // make sure the fast resume check fails! since we removed the file { settings_pack pack = settings(); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_dht, false); lt::session ses(pack); add_torrent_params p; p.flags &= ~add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; p.ti = boost::make_shared(boost::cref(*t)); p.save_path = combine_path(test_path, "tmp1"); p.storage_mode = storage_mode_sparse; bencode(std::back_inserter(p.resume_data), resume); torrent_handle h = ses.add_torrent(p, ec); alert const* a = wait_for_alert(ses, fastresume_rejected_alert::alert_type , "ses"); // we expect the fast resume to be rejected because the files were removed TEST_CHECK(alert_cast(a) != 0); } remove_all(combine_path(test_path, "tmp1"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "tmp1") << "': " << ec.message() << std::endl; } bool got_file_rename_alert(alert const* a) { return alert_cast(a) || alert_cast(a); } TORRENT_TEST(rename_file) { std::vector buf; file_storage fs; boost::shared_ptr info = setup_torrent_info(fs, buf); settings_pack pack = settings(); pack.set_bool(settings_pack::disable_hash_checks, true); lt::session ses(pack); add_torrent_params p; p.ti = info; p.save_path = "."; error_code ec; torrent_handle h = ses.add_torrent(p, ec); // make it a seed std::vector tmp(info->piece_length()); for (int i = 0; i < info->num_pieces(); ++i) h.add_piece(i, &tmp[0]); // wait for the files to have been written alert const* pf = wait_for_alert(ses, piece_finished_alert::alert_type, "ses", info->num_pieces()); TEST_CHECK(pf); // now rename them. This is the test for (int i = 0; i < info->num_files(); ++i) { std::string name = fs.file_path(i); h.rename_file(i, "temp_storage__" + name.substr(12)); } // wait fir the files to have been renamed alert const* fra = wait_for_alert(ses, file_renamed_alert::alert_type, "ses", info->num_files()); TEST_CHECK(fra); TEST_CHECK(exists(info->name() + "__")); h.save_resume_data(); alert const* ra = wait_for_alert(ses, save_resume_data_alert::alert_type); TEST_CHECK(ra); if (!ra) return; entry resume = *alert_cast(ra)->resume_data; std::cerr << resume.to_string() << "\n"; entry::list_type files = resume.dict().find("mapped_files")->second.list(); for (entry::list_type::iterator i = files.begin(); i != files.end(); ++i) { TEST_EQUAL(i->string().substr(0, 14), "temp_storage__"); } } TORRENT_TEST(rename_file_fastresume) { std::string test_path = current_working_directory(); error_code ec; std::cout << "\n\n=== test rename file in fastresume ===" << std::endl; remove_all(combine_path(test_path, "tmp2"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "tmp2") << "': " << ec.message() << std::endl; create_directory(combine_path(test_path, "tmp2"), ec); if (ec) std::cerr << "create_directory: " << ec.message() << std::endl; std::ofstream file(combine_path(test_path, "tmp2/temporary").c_str()); boost::shared_ptr t = ::create_torrent(&file); file.close(); TEST_CHECK(exists(combine_path(test_path, "tmp2/temporary"))); entry resume; { settings_pack pack = settings(); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_dht, false); lt::session ses(pack); add_torrent_params p; p.ti = boost::make_shared(boost::cref(*t)); p.save_path = combine_path(test_path, "tmp2"); p.storage_mode = storage_mode_sparse; torrent_handle h = ses.add_torrent(p, ec); h.rename_file(0, "testing_renamed_files"); std::cout << "renaming file" << std::endl; bool renamed = false; for (int i = 0; i < 10; ++i) { if (print_alerts(ses, "ses", true, true, true, &got_file_rename_alert)) renamed = true; torrent_status s = h.status(); if (s.state == torrent_status::seeding && renamed) break; test_sleep(100); } std::cout << "stop loop" << std::endl; torrent_status s = h.status(); TEST_CHECK(s.state == torrent_status::seeding); h.save_resume_data(); alert const* ra = wait_for_alert(ses, save_resume_data_alert::alert_type); TEST_CHECK(ra); if (ra) resume = *alert_cast(ra)->resume_data; ses.remove_torrent(h); } TEST_CHECK(!exists(combine_path(test_path, "tmp2/temporary"))); TEST_CHECK(exists(combine_path(test_path, "tmp2/testing_renamed_files"))); TEST_CHECK(resume.dict().find("mapped_files") != resume.dict().end()); TEST_CHECK(resume.dict().find("file sizes") != resume.dict().end()); std::cerr << resume.to_string() << "\n"; // make sure the fast resume check succeeds, even though we renamed the file { settings_pack pack = settings(); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_dht, false); lt::session ses(pack); add_torrent_params p; p.ti = boost::make_shared(boost::cref(*t)); p.save_path = combine_path(test_path, "tmp2"); p.storage_mode = storage_mode_sparse; bencode(std::back_inserter(p.resume_data), resume); torrent_handle h = ses.add_torrent(p, ec); torrent_status stat; for (int i = 0; i < 50; ++i) { stat = h.status(); print_alerts(ses, "ses"); if (stat.state == torrent_status::seeding) break; test_sleep(100); } TEST_CHECK(stat.state == torrent_status::seeding); h.save_resume_data(); alert const* ra = wait_for_alert(ses, save_resume_data_alert::alert_type); TEST_CHECK(ra); if (ra) resume = *alert_cast(ra)->resume_data; ses.remove_torrent(h); } TEST_CHECK(resume.dict().find("mapped_files") != resume.dict().end()); std::cerr << resume.to_string() << "\n"; remove_all(combine_path(test_path, "tmp2"), ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) std::cerr << "remove_all '" << combine_path(test_path, "tmp2") << "': " << ec.message() << std::endl; } void alloc_iov(file::iovec_t* iov, int num_bufs) { for (int i = 0; i < num_bufs; ++i) { iov[i].iov_base = malloc(num_bufs * (i + 1)); iov[i].iov_len = num_bufs * (i + 1); } } void fill_pattern(file::iovec_t* iov, int num_bufs) { int counter = 0; for (int i = 0; i < num_bufs; ++i) { unsigned char* buf = (unsigned char*)iov[i].iov_base; for (int k = 0; k < int(iov[i].iov_len); ++k) { buf[k] = counter & 0xff; ++counter; } } } bool check_pattern(std::vector const& buf, int counter) { unsigned char* p = (unsigned char*)&buf[0]; for (int k = 0; k < int(buf.size()); ++k) { if (p[k] != (counter & 0xff)) return false; ++counter; } return true; } void fill_pattern2(file::iovec_t* iov, int num_bufs) { for (int i = 0; i < num_bufs; ++i) { unsigned char* buf = (unsigned char*)iov[i].iov_base; memset(buf, 0xfe, int(iov[i].iov_len)); } } void free_iov(file::iovec_t* iov, int num_bufs) { for (int i = 0; i < num_bufs; ++i) { free(iov[i].iov_base); iov[i].iov_len = 0; iov[i].iov_base = NULL; } } TORRENT_TEST(iovec_copy_bufs) { file::iovec_t iov1[10]; file::iovec_t iov2[10]; alloc_iov(iov1, 10); fill_pattern(iov1, 10); TEST_CHECK(bufs_size(iov1, 10) >= 106); // copy exactly 106 bytes from iov1 to iov2 int num_bufs = copy_bufs(iov1, 106, iov2); // verify that the first 100 bytes is pattern 1 // and that the remaining bytes are pattern 2 int counter = 0; for (int i = 0; i < num_bufs; ++i) { unsigned char* buf = (unsigned char*)iov2[i].iov_base; for (int k = 0; k < int(iov2[i].iov_len); ++k) { TEST_EQUAL(int(buf[k]), (counter & 0xff)); ++counter; } } TEST_EQUAL(counter, 106); free_iov(iov1, 10); } TORRENT_TEST(iovec_clear_bufs) { file::iovec_t iov[10]; alloc_iov(iov, 10); fill_pattern(iov, 10); clear_bufs(iov, 10); for (int i = 0; i < 10; ++i) { unsigned char* buf = (unsigned char*)iov[i].iov_base; for (int k = 0; k < int(iov[i].iov_len); ++k) { TEST_EQUAL(int(buf[k]), 0); } } free_iov(iov, 10); } TORRENT_TEST(iovec_bufs_size) { file::iovec_t iov[10]; for (int i = 1; i < 10; ++i) { alloc_iov(iov, i); int expected_size = 0; for (int k = 0; k < i; ++k) expected_size += i * (k + 1); TEST_EQUAL(bufs_size(iov, i), expected_size); free_iov(iov, i); } } TORRENT_TEST(iovec_advance_bufs) { file::iovec_t iov1[10]; file::iovec_t iov2[10]; alloc_iov(iov1, 10); fill_pattern(iov1, 10); memcpy(iov2, iov1, sizeof(iov1)); file::iovec_t* iov = iov2; file::iovec_t* end = iov2 + 10; // advance iov 13 bytes. Make sure what's left fits pattern 1 shifted // 13 bytes advance_bufs(iov, 13); // make sure what's in int counter = 13; for (int i = 0; i < end - iov; ++i) { unsigned char* buf = (unsigned char*)iov[i].iov_base; for (int k = 0; k < int(iov[i].iov_len); ++k) { TEST_EQUAL(int(buf[k]), (counter & 0xff)); ++counter; } } free_iov(iov1, 10); } TORRENT_TEST(unbuffered) { run_test(true); } TORRENT_TEST(buffered) { run_test(false); } file_storage make_fs() { file_storage fs; fs.add_file(combine_path("readwritev", "1"), 3); fs.add_file(combine_path("readwritev", "2"), 9); fs.add_file(combine_path("readwritev", "3"), 81); fs.add_file(combine_path("readwritev", "4"), 6561); fs.set_piece_length(0x1000); fs.set_num_pieces((fs.total_size() + 0xfff) / 0x1000); return fs; } struct test_fileop : fileop { test_fileop(int stripe_size) : m_stripe_size(stripe_size) {} int file_op(int file_index, boost::int64_t file_offset, int size , file::iovec_t const* bufs, storage_error& ec) { if (file_index >= int(m_file_data.size())) { m_file_data.resize(file_index + 1); } const int write_size = (std::min)(m_stripe_size, size); std::vector& file = m_file_data[file_index]; if (file_offset + write_size > int(file.size())) { file.resize(file_offset + write_size); } int left = write_size; while (left > 0) { const int copy_size = (std::min)(left, int(bufs->iov_len)); memcpy(&file[file_offset], bufs->iov_base, copy_size); ++bufs; file_offset += copy_size; left -= copy_size; } return write_size; } int m_stripe_size; std::vector > m_file_data; }; struct test_read_fileop : fileop { // EOF after size bytes read test_read_fileop(int size) : m_size(size), m_counter(0) {} int file_op(int file_index, boost::int64_t file_offset, int size , file::iovec_t const* bufs, storage_error& ec) { size = (std::min)(m_size, size); const int read = size; while (size > 0) { unsigned char* p = (unsigned char*)bufs->iov_base; const int len = (std::min)(int(bufs->iov_len), size); for (int i = 0; i < len; ++i) { p[i] = m_counter & 0xff; ++m_counter; } size -= len; m_size -= len; ++bufs; } return read; } int m_size; int m_counter; }; struct test_error_fileop : fileop { // EOF after size bytes read test_error_fileop(int error_file) : m_error_file(error_file) {} int file_op(int file_index, boost::int64_t file_offset, int size , file::iovec_t const* bufs, storage_error& ec) { if (m_error_file == file_index) { ec.file = file_index; ec.ec.assign(boost::system::errc::permission_denied , boost::system::generic_category()); ec.operation = storage_error::read; return -1; } return size; } int m_error_file; }; int count_bufs(file::iovec_t const* bufs, int bytes) { int size = 0; int count = 1; if (bytes == 0) return 0; for (file::iovec_t const* i = bufs;; ++i, ++count) { size += i->iov_len; if (size >= bytes) return count; } } TORRENT_TEST(readwritev_stripe_1) { const int num_bufs = 30; file::iovec_t iov[num_bufs]; alloc_iov(iov, num_bufs); fill_pattern(iov, num_bufs); file_storage fs = make_fs(); test_fileop fop(1); storage_error ec; TEST_CHECK(bufs_size(iov, num_bufs) >= fs.total_size()); file::iovec_t iov2[num_bufs]; copy_bufs(iov, fs.total_size(), iov2); int num_bufs2 = count_bufs(iov2, fs.total_size()); TEST_CHECK(num_bufs2 <= num_bufs); int ret = readwritev(fs, iov2, 0, 0, num_bufs2, fop, ec); TEST_EQUAL(ret, fs.total_size()); TEST_EQUAL(fop.m_file_data.size(), 4); TEST_EQUAL(fop.m_file_data[0].size(), 3); TEST_EQUAL(fop.m_file_data[1].size(), 9); TEST_EQUAL(fop.m_file_data[2].size(), 81); TEST_EQUAL(fop.m_file_data[3].size(), 6561); TEST_CHECK(check_pattern(fop.m_file_data[0], 0)); TEST_CHECK(check_pattern(fop.m_file_data[1], 3)); TEST_CHECK(check_pattern(fop.m_file_data[2], 3 + 9)); TEST_CHECK(check_pattern(fop.m_file_data[3], 3 + 9 + 81)); free_iov(iov, num_bufs); } TORRENT_TEST(readwritev_single_buffer) { file_storage fs = make_fs(); test_fileop fop(10000000); storage_error ec; std::vector buf(fs.total_size()); file::iovec_t iov = { &buf[0], buf.size() }; fill_pattern(&iov, 1); int ret = readwritev(fs, &iov, 0, 0, 1, fop, ec); TEST_EQUAL(ret, fs.total_size()); TEST_EQUAL(fop.m_file_data.size(), 4); TEST_EQUAL(fop.m_file_data[0].size(), 3); TEST_EQUAL(fop.m_file_data[1].size(), 9); TEST_EQUAL(fop.m_file_data[2].size(), 81); TEST_EQUAL(fop.m_file_data[3].size(), 6561); TEST_CHECK(check_pattern(fop.m_file_data[0], 0)); TEST_CHECK(check_pattern(fop.m_file_data[1], 3)); TEST_CHECK(check_pattern(fop.m_file_data[2], 3 + 9)); TEST_CHECK(check_pattern(fop.m_file_data[3], 3 + 9 + 81)); } TORRENT_TEST(readwritev_read) { file_storage fs = make_fs(); test_read_fileop fop(10000000); storage_error ec; std::vector buf(fs.total_size()); file::iovec_t iov = { &buf[0], buf.size() }; // read everything int ret = readwritev(fs, &iov, 0, 0, 1, fop, ec); TEST_EQUAL(ret, fs.total_size()); TEST_CHECK(check_pattern(buf, 0)); } TORRENT_TEST(readwritev_read_short) { file_storage fs = make_fs(); test_read_fileop fop(100); storage_error ec; std::vector buf(fs.total_size()); file::iovec_t iov = { &buf[0] , static_cast(fs.total_size()) }; // read everything int ret = readwritev(fs, &iov, 0, 0, 1, fop, ec); TEST_EQUAL(ec.file, 3); TEST_EQUAL(ret, 100); buf.resize(100); TEST_CHECK(check_pattern(buf, 0)); } TORRENT_TEST(readwritev_error) { file_storage fs = make_fs(); test_error_fileop fop(2); storage_error ec; std::vector buf(fs.total_size()); file::iovec_t iov = { &buf[0] , static_cast(fs.total_size()) }; // read everything int ret = readwritev(fs, &iov, 0, 0, 1, fop, ec); TEST_EQUAL(ret, -1); TEST_EQUAL(ec.file, 2); TEST_EQUAL(ec.operation, storage_error::read); TEST_EQUAL(ec.ec, boost::system::errc::permission_denied); printf("error: %s\n", ec.ec.message().c_str()); } TORRENT_TEST(readwritev_zero_size_files) { file_storage fs; fs.add_file(combine_path("readwritev", "1"), 3); fs.add_file(combine_path("readwritev", "2"), 0); fs.add_file(combine_path("readwritev", "3"), 81); fs.add_file(combine_path("readwritev", "4"), 0); fs.add_file(combine_path("readwritev", "5"), 6561); fs.set_piece_length(0x1000); fs.set_num_pieces((fs.total_size() + 0xfff) / 0x1000); test_read_fileop fop(10000000); storage_error ec; std::vector buf(fs.total_size()); file::iovec_t iov = { &buf[0] , static_cast(fs.total_size()) }; // read everything int ret = readwritev(fs, &iov, 0, 0, 1, fop, ec); TEST_EQUAL(ret, fs.total_size()); TEST_CHECK(check_pattern(buf, 0)); } void delete_dirs(std::string path) { error_code ec; remove_all(path, ec); if (ec && ec != boost::system::errc::no_such_file_or_directory) { fprintf(stderr, "remove_all \"%s\": %s\n" , path.c_str(), ec.message().c_str()); } TEST_CHECK(!exists(path)); } TORRENT_TEST(move_storage_to_self) { // call move_storage with the path to the exising storage. should be a no-op std::string const save_path = current_working_directory(); std::string const test_path = combine_path(save_path, "temp_storage"); delete_dirs(test_path); aux::session_settings set; file_storage fs; std::vector buf; file_pool fp; io_service ios; disk_buffer_pool dp(16 * 1024, ios, boost::bind(&nop)); boost::shared_ptr s = setup_torrent(fs, fp, buf, save_path, set); file::iovec_t const b = {&buf[0], 4}; storage_error se; s->writev(&b, 1, 2, 0, 0, se); TEST_CHECK(exists(combine_path(test_path, combine_path("folder2", "test3.tmp")))); TEST_CHECK(exists(combine_path(test_path, combine_path("_folder3", "test4.tmp")))); s->move_storage(save_path, 0, se); TEST_EQUAL(se.ec, boost::system::errc::success); TEST_CHECK(exists(test_path)); TEST_CHECK(exists(combine_path(test_path, combine_path("folder2", "test3.tmp")))); TEST_CHECK(exists(combine_path(test_path, combine_path("_folder3", "test4.tmp")))); } TORRENT_TEST(move_storage_into_self) { std::string const save_path = current_working_directory(); delete_dirs(combine_path(save_path, "temp_storage")); aux::session_settings set; file_storage fs; std::vector buf; file_pool fp; io_service ios; disk_buffer_pool dp(16 * 1024, ios, boost::bind(&nop)); boost::shared_ptr s = setup_torrent(fs, fp, buf, save_path, set); file::iovec_t const b = {&buf[0], 4}; storage_error se; s->writev(&b, 1, 2, 0, 0, se); std::string const test_path = combine_path(save_path, combine_path("temp_storage", "folder1")); s->move_storage(test_path, 0, se); TEST_EQUAL(se.ec, boost::system::errc::success); TEST_CHECK(exists(combine_path(test_path, combine_path("temp_storage" , combine_path("folder1", "test2.tmp"))))); // these directories and files are created up-front because they are empty files TEST_CHECK(exists(combine_path(test_path, combine_path("temp_storage" , combine_path("folder2", "test3.tmp"))))); TEST_CHECK(exists(combine_path(test_path, combine_path("temp_storage" , combine_path("_folder3", "test4.tmp"))))); } TORRENT_TEST(dont_move_intermingled_files) { std::string const save_path = combine_path(current_working_directory(), "save_path_1"); delete_dirs(combine_path(save_path, "temp_storage")); std::string test_path = combine_path(current_working_directory(), "save_path_2"); delete_dirs(combine_path(test_path, "temp_storage")); aux::session_settings set; file_storage fs; std::vector buf; file_pool fp; io_service ios; disk_buffer_pool dp(16 * 1024, ios, boost::bind(&nop)); boost::shared_ptr s = setup_torrent(fs, fp, buf, save_path, set); file::iovec_t b = {&buf[0], 4}; storage_error se; s->writev(&b, 1, 2, 0, 0, se); error_code ec; create_directory(combine_path(save_path, combine_path("temp_storage" , combine_path("_folder3", "alien_folder1"))), ec); TEST_EQUAL(ec, boost::system::errc::success); file f; f.open(combine_path(save_path, combine_path("temp_storage", "alien1.tmp")) , file::write_only, ec); f.close(); TEST_EQUAL(ec, boost::system::errc::success); f.open(combine_path(save_path, combine_path("temp_storage" , combine_path("folder1", "alien2.tmp"))), file::write_only, ec); f.close(); TEST_EQUAL(ec, boost::system::errc::success); s->move_storage(test_path, 0, se); TEST_EQUAL(se.ec, boost::system::errc::success); // torrent files moved to new place TEST_CHECK(exists(combine_path(test_path, combine_path("temp_storage" , combine_path("folder1", "test2.tmp"))))); // these directories and files are created up-front because they are empty files TEST_CHECK(exists(combine_path(test_path, combine_path("temp_storage" , combine_path("folder2", "test3.tmp"))))); TEST_CHECK(exists(combine_path(test_path, combine_path("temp_storage" , combine_path("_folder3", "test4.tmp"))))); // intermingled files and directories are still in old place TEST_CHECK(exists(combine_path(save_path, combine_path("temp_storage" , "alien1.tmp")))); TEST_CHECK(!exists(combine_path(test_path, combine_path("temp_storage" , "alien1.tmp")))); TEST_CHECK(exists(combine_path(save_path, combine_path("temp_storage" , combine_path("folder1", "alien2.tmp"))))); TEST_CHECK(!exists(combine_path(test_path, combine_path("temp_storage" , combine_path("folder1", "alien2.tmp"))))); TEST_CHECK(exists(combine_path(save_path, combine_path("temp_storage" , combine_path("_folder3", "alien_folder1"))))); TEST_CHECK(!exists(combine_path(test_path, combine_path("temp_storage" , combine_path("_folder3", "alien_folder1"))))); } libtorrent-rasterbar-1.1.13/test/test_string.cpp000066400000000000000000000317141351156116000220010ustar00rootroot00000000000000/* Copyright (c) 2012, 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 "test.hpp" #include "libtorrent/aux_/escape_string.hpp" #include "libtorrent/hex.hpp" #include "libtorrent/string_util.hpp" #include #include // for strcmp using namespace libtorrent; TORRENT_TEST(string) { // test maybe_url_encode TEST_EQUAL(maybe_url_encode("http://test:test@abc.com/abc<>abc"), "http://test:test@abc.com/abc%3c%3eabc"); TEST_EQUAL(maybe_url_encode("http://abc.com/foo bar"), "http://abc.com/foo%20bar"); TEST_EQUAL(maybe_url_encode("http://abc.com:80/foo bar"), "http://abc.com:80/foo%20bar"); TEST_EQUAL(maybe_url_encode("http://abc.com:8080/foo bar"), "http://abc.com:8080/foo%20bar"); TEST_EQUAL(maybe_url_encode("abc"), "abc"); TEST_EQUAL(maybe_url_encode("http://abc.com/abc"), "http://abc.com/abc"); // test to/from hex conversion char const* str = "0123456789012345678901234567890123456789"; char bin[20]; TEST_CHECK(from_hex(str, 40, bin)); char hex[41]; to_hex(bin, 20, hex); TEST_CHECK(strcmp(hex, str) == 0); TEST_CHECK(to_hex("\x55\x73") == "5573"); TEST_CHECK(to_hex("\xaB\xd0") == "abd0"); // test is_space TEST_CHECK(!is_space('C')); TEST_CHECK(!is_space('\b')); TEST_CHECK(!is_space('8')); TEST_CHECK(!is_space('=')); TEST_CHECK(is_space(' ')); TEST_CHECK(is_space('\t')); TEST_CHECK(is_space('\n')); TEST_CHECK(is_space('\r')); // test to_lower TEST_CHECK(to_lower('C') == 'c'); TEST_CHECK(to_lower('c') == 'c'); TEST_CHECK(to_lower('-') == '-'); TEST_CHECK(to_lower('&') == '&'); // test string_equal_no_case TEST_CHECK(string_equal_no_case("foobar", "FoobAR")); TEST_CHECK(string_equal_no_case("foobar", "foobar")); TEST_CHECK(!string_equal_no_case("foobar", "foobar ")); TEST_CHECK(!string_equal_no_case("foobar", "F00")); // test string_begins_no_case TEST_CHECK(string_begins_no_case("foobar", "FoobAR --")); TEST_CHECK(!string_begins_no_case("foobar", "F00")); // test itoa TEST_CHECK(to_string(345).elems == std::string("345")); TEST_CHECK(to_string(-345).elems == std::string("-345")); TEST_CHECK(to_string(0).elems == std::string("0")); TEST_CHECK(to_string(1000000000).elems == std::string("1000000000")); // base64 test vectors from http://www.faqs.org/rfcs/rfc4648.html TEST_CHECK(base64encode("") == ""); TEST_CHECK(base64encode("f") == "Zg=="); TEST_CHECK(base64encode("fo") == "Zm8="); TEST_CHECK(base64encode("foo") == "Zm9v"); TEST_CHECK(base64encode("foob") == "Zm9vYg=="); TEST_CHECK(base64encode("fooba") == "Zm9vYmE="); TEST_CHECK(base64encode("foobar") == "Zm9vYmFy"); // base32 test vectors from http://www.faqs.org/rfcs/rfc4648.html TEST_CHECK(base32encode("") == ""); TEST_CHECK(base32encode("f") == "MY======"); TEST_CHECK(base32encode("fo") == "MZXQ===="); TEST_CHECK(base32encode("foo") == "MZXW6==="); TEST_CHECK(base32encode("foob") == "MZXW6YQ="); TEST_CHECK(base32encode("fooba") == "MZXW6YTB"); TEST_CHECK(base32encode("foobar") == "MZXW6YTBOI======"); // base32 for i2p TEST_CHECK(base32encode("fo", string::no_padding) == "MZXQ"); TEST_CHECK(base32encode("foob", string::i2p) == "mzxw6yq"); TEST_CHECK(base32encode("foobar", string::lowercase) == "mzxw6ytboi======"); TEST_CHECK(base32decode("") == ""); TEST_CHECK(base32decode("MY======") == "f"); TEST_CHECK(base32decode("MZXQ====") == "fo"); TEST_CHECK(base32decode("MZXW6===") == "foo"); TEST_CHECK(base32decode("MZXW6YQ=") == "foob"); TEST_CHECK(base32decode("MZXW6YTB") == "fooba"); TEST_CHECK(base32decode("MZXW6YTBOI======") == "foobar"); TEST_CHECK(base32decode("MY") == "f"); TEST_CHECK(base32decode("MZXW6YQ") == "foob"); TEST_CHECK(base32decode("MZXW6YTBOI") == "foobar"); TEST_CHECK(base32decode("mZXw6yTBO1======") == "foobar"); // make sure invalid encoding returns the empty string TEST_CHECK(base32decode("mZXw6yTBO1{#&*()=") == ""); std::string test; for (int i = 0; i < 255; ++i) test += char(i); TEST_CHECK(base32decode(base32encode(test)) == test); // escape_string char const* test_string = "!@#$%^&*()-_=+/,. %?"; TEST_EQUAL(escape_string(test_string, strlen(test_string)) , "!%40%23%24%25%5e%26*()-_%3d%2b%2f%2c.%20%25%3f"); // escape_path TEST_EQUAL(escape_path(test_string, strlen(test_string)) , "!%40%23%24%25%5e%26*()-_%3d%2b/%2c.%20%25%3f"); error_code ec; TEST_CHECK(unescape_string(escape_path(test_string, strlen(test_string)), ec) == test_string); TEST_CHECK(!ec); if (ec) fprintf(stderr, "%s\n", ec.message().c_str()); // need_encoding char const* test_string2 = "!@$&()-_/,.%?"; TEST_CHECK(need_encoding(test_string, strlen(test_string)) == true); TEST_CHECK(need_encoding(test_string2, strlen(test_string2)) == false); TEST_CHECK(need_encoding("\n", 1) == true); // maybe_url_encode TEST_EQUAL(maybe_url_encode("http://bla.com/\n"), "http://bla.com/%0a"); TEST_EQUAL(maybe_url_encode("http://bla.com/foo%20bar"), "http://bla.com/foo%20bar"); TEST_EQUAL(maybe_url_encode("http://bla.com/foo%20bar?k=v&k2=v2"), "http://bla.com/foo%20bar?k=v&k2=v2"); TEST_EQUAL(maybe_url_encode("?&"), "?&"); // unescape_string TEST_CHECK(unescape_string(escape_string(test_string, strlen(test_string)), ec) == test_string); std::cerr << unescape_string(escape_string(test_string, strlen(test_string)), ec) << std::endl; // prematurely terminated string unescape_string("%", ec); TEST_CHECK(ec == error_code(errors::invalid_escaped_string)); unescape_string("%0", ec); TEST_CHECK(ec == error_code(errors::invalid_escaped_string)); // invalid hex character unescape_string("%GE", ec); TEST_CHECK(ec == error_code(errors::invalid_escaped_string)); unescape_string("%eg", ec); TEST_CHECK(ec == error_code(errors::invalid_escaped_string)); ec.clear(); TEST_CHECK(unescape_string("123+abc", ec) == "123 abc"); // read_until char const* test_string1 = "abcdesdf sdgf"; char const* tmp1 = test_string1; TEST_CHECK(read_until(tmp1, 'd', test_string1 + strlen(test_string1)) == "abc"); tmp1 = test_string1; TEST_CHECK(read_until(tmp1, '[', test_string1 + strlen(test_string1)) == "abcdesdf sdgf"); char hex_chars[] = "0123456789abcdefABCDEF"; for (int i = 1; i < 255; ++i) { bool hex = strchr(hex_chars, i) != NULL; char c = i; TEST_EQUAL(detail::is_hex(&c, 1), hex); } TEST_EQUAL(detail::hex_to_int('0'), 0); TEST_EQUAL(detail::hex_to_int('7'), 7); TEST_EQUAL(detail::hex_to_int('a'), 10); TEST_EQUAL(detail::hex_to_int('f'), 15); TEST_EQUAL(detail::hex_to_int('b'), 11); TEST_EQUAL(detail::hex_to_int('t'), -1); TEST_EQUAL(detail::hex_to_int('g'), -1); std::string path = "a\\b\\c"; convert_path_to_posix(path); TEST_EQUAL(path, "a/b/c"); // url_has_argument TEST_CHECK(url_has_argument("http://127.0.0.1/test", "test") == ""); TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24", "bar") == ""); TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24", "foo") == "24"); TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23", "foo") == "24"); TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23", "bar") == "23"); TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "bar") == "23"); TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "a") == "e"); TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "b") == ""); // resolve_file_url #ifdef TORRENT_WINDOWS std::string p = "c:/blah/foo/bar\\"; convert_path_to_windows(p); TEST_EQUAL(p, "c:\\blah\\foo\\bar\\"); TEST_EQUAL(resolve_file_url("file:///c:/blah/foo/bar"), "c:\\blah\\foo\\bar"); TEST_EQUAL(resolve_file_url("file:///c:/b%3fah/foo/bar"), "c:\\b?ah\\foo\\bar"); TEST_EQUAL(resolve_file_url("file://\\c:\\b%3fah\\foo\\bar"), "c:\\b?ah\\foo\\bar"); #else TEST_EQUAL(resolve_file_url("file:///c/blah/foo/bar"), "/c/blah/foo/bar"); TEST_EQUAL(resolve_file_url("file:///c/b%3fah/foo/bar"), "/c/b?ah/foo/bar"); #endif std::vector list; parse_comma_separated_string(" a,b, c, d ,e \t,foobar\n\r,[::1]", list); TEST_EQUAL(list.size(), 7); TEST_EQUAL(list[0], "a"); TEST_EQUAL(list[1], "b"); TEST_EQUAL(list[2], "c"); TEST_EQUAL(list[3], "d"); TEST_EQUAL(list[4], "e"); TEST_EQUAL(list[5], "foobar"); TEST_EQUAL(list[6], "[::1]"); std::vector > list2; parse_comma_separated_string_port(" a:4,b:35, c : 1000, d: 351 ,e \t:42,foobar:1337\n\r,[2001::1]:6881", list2); TEST_EQUAL(list2.size(), 7); TEST_EQUAL(list2[0].first, "a"); TEST_EQUAL(list2[1].first, "b"); TEST_EQUAL(list2[2].first, "c"); TEST_EQUAL(list2[3].first, "d"); TEST_EQUAL(list2[4].first, "e"); TEST_EQUAL(list2[5].first, "foobar"); TEST_EQUAL(list2[6].first, "2001::1"); TEST_EQUAL(list2[0].second, 4); TEST_EQUAL(list2[1].second, 35); TEST_EQUAL(list2[2].second, 1000); TEST_EQUAL(list2[3].second, 351); TEST_EQUAL(list2[4].second, 42); TEST_EQUAL(list2[5].second, 1337); TEST_EQUAL(list2[6].second, 6881); // test string_tokenize char test_tokenize[] = "a b c \"foo bar\" d\ne f"; char* next = test_tokenize; char* ptr = string_tokenize(next, ' ', &next); TEST_EQUAL(ptr, std::string("a")); ptr = string_tokenize(next, ' ', &next); TEST_EQUAL(ptr, std::string("b")); ptr = string_tokenize(next, ' ', &next); TEST_EQUAL(ptr, std::string("c")); ptr = string_tokenize(next, ' ', &next); TEST_EQUAL(ptr, std::string("\"foo bar\"")); ptr = string_tokenize(next, ' ', &next); TEST_EQUAL(ptr, std::string("d\ne")); ptr = string_tokenize(next, ' ', &next); TEST_EQUAL(ptr, std::string("f")); ptr = string_tokenize(next, ' ', &next); TEST_EQUAL(ptr, static_cast(0)); TEST_EQUAL(std::string("foobar"), convert_from_native(convert_to_native("foobar"))); TEST_EQUAL(std::string("foobar") , convert_from_native(convert_to_native("foo")) + convert_from_native(convert_to_native("bar"))); TEST_EQUAL(convert_to_native("foobar") , convert_to_native("foo") + convert_to_native("bar")); } TORRENT_TEST(string_hash_no_case) { string_hash_no_case hsh; // make sure different strings yield different hashes TEST_CHECK(hsh("ab") != hsh("ba")); // make sure case is ignored TEST_EQUAL(hsh("Ab"), hsh("ab")); TEST_EQUAL(hsh("Ab"), hsh("aB")); // make sure zeroes in strings are supported TEST_CHECK(hsh(std::string("\0a", 2)) != hsh(std::string("\0b", 2))); TEST_EQUAL(hsh(std::string("\0a", 2)), hsh(std::string("\0a", 2))); } TORRENT_TEST(string_eq_no_case) { string_eq_no_case cmp; TEST_CHECK(cmp("ab", "ba") == false); TEST_CHECK(cmp("", "")); TEST_CHECK(cmp("abc", "abc")); // make sure different lengths are correctly treated as different TEST_CHECK(cmp("abc", "ab") == false); // make sure case is ignored TEST_CHECK(cmp("Ab", "ab")); TEST_CHECK(cmp("Ab", "aB")); // make sure zeros are supported TEST_CHECK(cmp(std::string("\0a", 2), std::string("\0b", 2)) == false); TEST_CHECK(cmp(std::string("\0a", 2), std::string("\0a", 2))); } TORRENT_TEST(string_less_no_case) { string_less_no_case cmp; // ab < ba TEST_CHECK(cmp("ab", "ba")); TEST_CHECK(cmp("ba", "ab") == false); TEST_CHECK(cmp("", "") == false); TEST_CHECK(cmp("", "a")); TEST_CHECK(cmp("abc", "abc") == false); // shorter strings come before longer ones TEST_CHECK(cmp("abc", "ab") == false); TEST_CHECK(cmp("ab", "abc")); // make sure case is ignored TEST_CHECK(cmp("Ab", "ba")); TEST_CHECK(cmp("Ba", "ab") == false); TEST_CHECK(cmp("", "") == false); TEST_CHECK(cmp("", "a")); TEST_CHECK(cmp("abc", "Abc") == false); // shorter strings come before longer ones TEST_CHECK(cmp("Abc", "ab") == false); TEST_CHECK(cmp("Ab", "abc")); // make sure zeros are supported TEST_CHECK(cmp(std::string("\0a", 2), std::string("\0b", 2))); TEST_CHECK(cmp(std::string("\0a", 2), std::string("\0a", 2)) == false); } libtorrent-rasterbar-1.1.13/test/test_tailqueue.cpp000066400000000000000000000072331351156116000224700ustar00rootroot00000000000000/* Copyright (c) 2012, 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 "test.hpp" #include "libtorrent/tailqueue.hpp" using namespace libtorrent; struct test_node : tailqueue_node { test_node(char n) : name(n) {} char name; }; void check_chain(tailqueue& chain, char const* expected) { tailqueue_iterator i = chain.iterate(); while (i.get()) { TEST_EQUAL(((test_node*)i.get())->name, *expected); i.next(); ++expected; } TEST_EQUAL(expected[0], 0); } void free_chain(tailqueue& q) { test_node* chain = (test_node*)q.get_all(); while(chain) { test_node* del = (test_node*)chain; chain = (test_node*)chain->next; delete del; } } void build_chain(tailqueue& q, char const* str) { free_chain(q); char const* expected = str; while(*str) { q.push_back(new test_node(*str)); ++str; } check_chain(q, expected); } TORRENT_TEST(tailqueue) { tailqueue t1; tailqueue t2; // test prepend build_chain(t1, "abcdef"); build_chain(t2, "12345"); t1.prepend(t2); check_chain(t1, "12345abcdef"); check_chain(t2, ""); // test append build_chain(t1, "abcdef"); build_chain(t2, "12345"); t1.append(t2); check_chain(t1, "abcdef12345"); check_chain(t2, ""); // test swap build_chain(t1, "abcdef"); build_chain(t2, "12345"); t1.swap(t2); check_chain(t1, "12345"); check_chain(t2, "abcdef"); // test pop_front build_chain(t1, "abcdef"); delete t1.pop_front(); check_chain(t1, "bcdef"); // test push_back build_chain(t1, "abcdef"); t1.push_back(new test_node('1')); check_chain(t1, "abcdef1"); // test push_front build_chain(t1, "abcdef"); t1.push_front(new test_node('1')); check_chain(t1, "1abcdef"); // test size build_chain(t1, "abcdef"); TEST_EQUAL(t1.size(), 6); // test empty free_chain(t1); TEST_EQUAL(t1.empty(), true); build_chain(t1, "abcdef"); TEST_EQUAL(t1.empty(), false); // test get_all build_chain(t1, "abcdef"); test_node* n = (test_node*)t1.get_all(); TEST_EQUAL(t1.empty(), true); TEST_EQUAL(t1.size(), 0); char const* expected = "abcdef"; while (n) { test_node* del = n; TEST_EQUAL(n->name, *expected); n = (test_node*)n->next; ++expected; delete del; } free_chain(t1); free_chain(t2); } libtorrent-rasterbar-1.1.13/test/test_threads.cpp000066400000000000000000000072151351156116000221240ustar00rootroot00000000000000/* Copyright (c) 2010, 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/aux_/disable_warnings_push.hpp" #include #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/thread.hpp" #include "test.hpp" #include "setup_transfer.hpp" // for test_sleep using namespace libtorrent; void fun(condition_variable* s, libtorrent::mutex* m, int* waiting, int i) { fprintf(stdout, "thread %d waiting\n", i); libtorrent::mutex::scoped_lock l(*m); *waiting += 1; s->wait(l); fprintf(stdout, "thread %d done\n", i); } void increment(condition_variable* s, libtorrent::mutex* m, int* waiting, boost::atomic* c) { libtorrent::mutex::scoped_lock l(*m); *waiting += 1; s->wait(l); l.unlock(); for (int i = 0; i < 1000000; ++i) ++*c; } void decrement(condition_variable* s, libtorrent::mutex* m, int* waiting, boost::atomic* c) { libtorrent::mutex::scoped_lock l(*m); *waiting += 1; s->wait(l); l.unlock(); for (int i = 0; i < 1000000; ++i) --*c; } TORRENT_TEST(threads) { condition_variable cond; libtorrent::mutex m; std::list threads; int waiting = 0; for (int i = 0; i < 20; ++i) { threads.push_back(new libtorrent::thread(boost::bind(&fun, &cond, &m, &waiting, i))); } // make sure all threads are waiting on the condition_variable libtorrent::mutex::scoped_lock l(m); while (waiting < 20) { l.unlock(); test_sleep(10); l.lock(); } cond.notify_all(); l.unlock(); for (std::list::iterator i = threads.begin(); i != threads.end(); ++i) { (*i)->join(); delete *i; } threads.clear(); waiting = 0; boost::atomic c(0); for (int i = 0; i < 3; ++i) { threads.push_back(new libtorrent::thread(boost::bind(&increment, &cond, &m, &waiting, &c))); threads.push_back(new libtorrent::thread(boost::bind(&decrement, &cond, &m, &waiting, &c))); } // make sure all threads are waiting on the condition_variable l.lock(); while (waiting < 6) { l.unlock(); test_sleep(10); l.lock(); } cond.notify_all(); l.unlock(); for (std::list::iterator i = threads.begin(); i != threads.end(); ++i) { (*i)->join(); delete *i; } TEST_CHECK(c == 0); } libtorrent-rasterbar-1.1.13/test/test_time.cpp000066400000000000000000000062551351156116000214330ustar00rootroot00000000000000/* Copyright (c) 2013, 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 "test.hpp" #include "setup_transfer.hpp" // for test_sleep #include "libtorrent/time.hpp" #include "libtorrent/thread.hpp" #include using namespace libtorrent; void check_timer_loop(mutex& m, time_point& last, condition_variable& cv) { mutex::scoped_lock l(m); cv.wait(l); l.unlock(); for (int i = 0; i < 10000; ++i) { mutex::scoped_lock l(m); time_point now = clock_type::now(); TEST_CHECK(now >= last); last = now; } } TORRENT_TEST(time) { // make sure the time classes have correct semantics TEST_EQUAL(total_milliseconds(milliseconds(100)), 100); TEST_EQUAL(total_milliseconds(milliseconds(1)), 1); TEST_EQUAL(total_milliseconds(seconds(1)), 1000); TEST_EQUAL(total_seconds(minutes(1)), 60); TEST_EQUAL(total_seconds(hours(1)), 3600); // make sure it doesn't wrap at 32 bit arithmetic TEST_EQUAL(total_seconds(seconds(281474976)), 281474976); TEST_EQUAL(total_milliseconds(milliseconds(281474976)), 281474976); // make sure the timer is monotonic time_point now = clock_type::now(); time_point last = now; for (int i = 0; i < 1000; ++i) { now = clock_type::now(); TEST_CHECK(now >= last); last = now; } mutex m; condition_variable cv; libtorrent::thread t1(boost::bind(&check_timer_loop, boost::ref(m), boost::ref(last), boost::ref(cv))); libtorrent::thread t2(boost::bind(&check_timer_loop, boost::ref(m), boost::ref(last), boost::ref(cv))); libtorrent::thread t3(boost::bind(&check_timer_loop, boost::ref(m), boost::ref(last), boost::ref(cv))); libtorrent::thread t4(boost::bind(&check_timer_loop, boost::ref(m), boost::ref(last), boost::ref(cv))); test_sleep(100); cv.notify_all(); t1.join(); t2.join(); t3.join(); t4.join(); } libtorrent-rasterbar-1.1.13/test/test_time_critical.cpp000066400000000000000000000031501351156116000232740ustar00rootroot00000000000000/* Copyright (c) 2014, 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 "swarm_suite.hpp" TORRENT_TEST(time_crititcal) { // with time critical pieces test_swarm(time_critical); } libtorrent-rasterbar-1.1.13/test/test_timestamp_history.cpp000066400000000000000000000043771351156116000242640ustar00rootroot00000000000000/* Copyright (c) 2008-2015, 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 "test.hpp" #include "libtorrent/timestamp_history.hpp" TORRENT_TEST(timestamp_history) { using namespace libtorrent; timestamp_history h; TEST_EQUAL(h.add_sample(0x32, false), 0); TEST_EQUAL(h.base(), 0x32); TEST_EQUAL(h.add_sample(0x33, false), 0x1); TEST_EQUAL(h.base(), 0x32); TEST_EQUAL(h.add_sample(0x3433, false), 0x3401); TEST_EQUAL(h.base(), 0x32); TEST_EQUAL(h.add_sample(0x30, false), 0); TEST_EQUAL(h.base(), 0x30); // test that wrapping of the timestamp is properly handled h.add_sample(0xfffffff3, false); TEST_EQUAL(h.base(), 0xfffffff3); // TODO: test the case where we have > 120 samples (and have the base delay actually be updated) // TODO: test the case where a sample is lower than the history entry but not lower than the base } libtorrent-rasterbar-1.1.13/test/test_torrent.cpp000066400000000000000000000401051351156116000221620ustar00rootroot00000000000000/* Copyright (c) 2008, 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/session.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/time.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/magnet_uri.hpp" #include "settings.hpp" #include #include #include #include "test.hpp" #include "setup_transfer.hpp" using namespace libtorrent; namespace lt = libtorrent; namespace { bool wait_priority(torrent_handle const& h, std::vector const& prio) { for (int i = 0; i < 10; ++i) { if (h.file_priorities() == prio) { return true; } #ifdef NDEBUG test_sleep(100); #else test_sleep(300); #endif } return h.file_priorities() == prio; } bool prioritize_files(torrent_handle const& h, std::vector const& prio) { h.prioritize_files(prio); return wait_priority(h, prio); } void test_running_torrent(boost::shared_ptr info, boost::int64_t file_size) { settings_pack pack = settings(); pack.set_int(settings_pack::alert_mask, alert::piece_progress_notification | alert::storage_notification); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130"); pack.set_int(settings_pack::max_retry_port_bind, 10); lt::session ses(pack); std::vector zeroes; zeroes.resize(1000, 0); add_torrent_params p; p.flags &= ~add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; p.ti = info; p.save_path = "."; // make sure we correctly handle the case where we pass in // more values than there are files p.file_priorities = zeroes; error_code ec; torrent_handle h = ses.add_torrent(p, ec); if (ec) { fprintf(stdout, "add_torrent: %s\n", ec.message().c_str()); return; } std::vector ones(info->num_files(), 1); TEST_CHECK(prioritize_files(h, ones)) // test_sleep(500); torrent_status st = h.status(); TEST_EQUAL(st.total_wanted, file_size); // we want the single file TEST_EQUAL(st.total_wanted_done, 0); std::vector prio(info->num_files(), 1); prio[0] = 0; TEST_CHECK(prioritize_files(h, prio)) st = h.status(); TEST_EQUAL(st.total_wanted, 0); // we don't want anything TEST_EQUAL(st.total_wanted_done, 0); TEST_EQUAL(int(h.file_priorities().size()), info->num_files()); if (info->num_files() > 1) { prio[1] = 0; TEST_CHECK(prioritize_files(h, prio)) st = h.status(); TEST_EQUAL(st.total_wanted, file_size); TEST_EQUAL(st.total_wanted_done, 0); } if (info->num_pieces() > 0) { h.piece_priority(0, 1); st = h.status(); TEST_CHECK(st.pieces.size() > 0 && st.pieces[0] == false); std::vector piece(info->piece_length()); for (int i = 0; i < int(piece.size()); ++i) piece[i] = (i % 26) + 'A'; h.add_piece(0, &piece[0], torrent_handle::overwrite_existing); // wait until the piece is done writing and hashing wait_for_alert(ses, piece_finished_alert::alert_type, "piece_finished_alert"); st = h.status(); TEST_CHECK(st.pieces.size() > 0); std::cout << "reading piece 0" << std::endl; h.read_piece(0); alert const* a = wait_for_alert(ses, read_piece_alert::alert_type, "read_piece"); TEST_CHECK(a); read_piece_alert const* rpa = alert_cast(a); TEST_CHECK(rpa); if (rpa) { std::cout << "SUCCEEDED!" << std::endl; TEST_CHECK(memcmp(&piece[0], rpa->buffer.get(), info->piece_size(0)) == 0); TEST_CHECK(rpa->size == info->piece_size(0)); TEST_CHECK(rpa->piece == 0); TEST_CHECK(hasher(&piece[0], piece.size()).final() == info->hash_for_piece(0)); } } TEST_CHECK(h.file_priorities() == prio); } } // namespace TORRENT_TEST(long_names) { entry info; info["pieces"] = "aaaaaaaaaaaaaaaaaaaa"; info["name"] = "slightly shorter name, it's kind of sad that people started the trend of incorrectly encoding the regular name field and then adding another one with correct encoding"; info["name.utf-8"] = "this is a long ass name in order to try to make make_magnet_uri overflow and hopefully crash. Although, by the time you read this that particular bug should have been fixed"; info["piece length"] = 16 * 1024; info["length"] = 3245; entry torrent; torrent["info"] = info; std::vector buf; bencode(std::back_inserter(buf), torrent); error_code ec; boost::shared_ptr ti(boost::make_shared(&buf[0], buf.size(), boost::ref(ec))); TEST_CHECK(!ec); } TORRENT_TEST(total_wanted) { file_storage fs; fs.add_file("test_torrent_dir4/tmp1", 1024); fs.add_file("test_torrent_dir4/tmp2", 1024); fs.add_file("test_torrent_dir4/tmp3", 1024); fs.add_file("test_torrent_dir4/tmp4", 1024); libtorrent::create_torrent t(fs, 1024); std::vector tmp; bencode(std::back_inserter(tmp), t.generate()); error_code ec; boost::shared_ptr info(boost::make_shared( &tmp[0], tmp.size(), boost::ref(ec))); settings_pack pack = settings(); pack.set_int(settings_pack::alert_mask, alert::storage_notification); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130"); pack.set_int(settings_pack::max_retry_port_bind, 10); lt::session ses(pack); add_torrent_params p; p.ti = info; p.save_path = "."; // we just want 1 out of 4 files, 1024 out of 4096 bytes p.file_priorities.resize(4, 0); p.file_priorities[1] = 1; p.ti = info; torrent_handle h = ses.add_torrent(p); torrent_status st = h.status(); TEST_EQUAL(st.total_wanted, 1024); TEST_EQUAL(st.total_wanted_done, 0); // make sure that selecting and unseleting a file quickly still end up with // the last set priority h.file_priority(1, 4); h.file_priority(1, 0); TEST_EQUAL(h.status(0).total_wanted, 0); TEST_CHECK(wait_priority(h, std::vector(fs.num_files()))); TEST_EQUAL(h.status(0).total_wanted, 0); } TORRENT_TEST(added_peers) { file_storage fs; fs.add_file("test_torrent_dir4/tmp1", 1024); libtorrent::create_torrent t(fs, 1024); std::vector tmp; bencode(std::back_inserter(tmp), t.generate()); error_code ec; boost::shared_ptr info(boost::make_shared( &tmp[0], tmp.size(), boost::ref(ec))); settings_pack pack = settings(); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130"); pack.set_int(settings_pack::max_retry_port_bind, 10); lt::session ses(pack); add_torrent_params p; p.ti = info; p.save_path = "."; p.url = "?x.pe=127.0.0.1:48081&x.pe=127.0.0.2:48082"; torrent_handle h = ses.add_torrent(p); std::vector v; h.get_full_peer_list(v); TEST_EQUAL(v.size(), 2); } TORRENT_TEST(torrent) { /* { remove("test_torrent_dir2/tmp1"); remove("test_torrent_dir2/tmp2"); remove("test_torrent_dir2/tmp3"); file_storage fs; boost::int64_t file_size = 256 * 1024; fs.add_file("test_torrent_dir2/tmp1", file_size); fs.add_file("test_torrent_dir2/tmp2", file_size); fs.add_file("test_torrent_dir2/tmp3", file_size); libtorrent::create_torrent t(fs, 128 * 1024); t.add_tracker("http://non-existing.com/announce"); std::vector piece(128 * 1024); for (int i = 0; i < int(piece.size()); ++i) piece[i] = (i % 26) + 'A'; // calculate the hash for all pieces sha1_hash ph = hasher(&piece[0], piece.size()).final(); int num = t.num_pieces(); TEST_CHECK(t.num_pieces() > 0); for (int i = 0; i < num; ++i) t.set_hash(i, ph); std::vector tmp; std::back_insert_iterator > out(tmp); bencode(out, t.generate()); error_code ec; boost::shared_ptr info(boost::make_shared(&tmp[0], tmp.size(), boost::ref(ec), 0)); TEST_CHECK(info->num_pieces() > 0); test_running_torrent(info, file_size); } */ { file_storage fs; fs.add_file("test_torrent_dir2/tmp1", 1024); libtorrent::create_torrent t(fs, 1024, 6); std::vector piece(1024); for (int i = 0; i < int(piece.size()); ++i) piece[i] = (i % 26) + 'A'; // calculate the hash for all pieces sha1_hash const ph = hasher(&piece[0], piece.size()).final(); int const num = t.num_pieces(); TEST_CHECK(num > 0); for (int i = 0; i < num; ++i) t.set_hash(i, ph); std::vector tmp; std::back_insert_iterator > out(tmp); bencode(out, t.generate()); error_code ec; boost::shared_ptr info(boost::make_shared(&tmp[0], tmp.size(), boost::ref(ec), 0)); test_running_torrent(info, 1024); } } #ifndef TORRENT_DISABLE_EXTENSIONS struct test_plugin : libtorrent::torrent_plugin {}; struct plugin_creator { plugin_creator(int& c) : m_called(c) {} boost::shared_ptr operator()(torrent_handle const&, void*) { ++m_called; return boost::make_shared(); } int& m_called; }; TORRENT_TEST(duplicate_is_not_error) { file_storage fs; fs.add_file("test_torrent_dir2/tmp1", 1024); libtorrent::create_torrent t(fs, 128 * 1024, 6); std::vector piece(128 * 1024); for (int i = 0; i < int(piece.size()); ++i) piece[i] = (i % 26) + 'A'; // calculate the hash for all pieces sha1_hash ph = hasher(&piece[0], piece.size()).final(); int num = t.num_pieces(); TEST_CHECK(t.num_pieces() > 0); for (int i = 0; i < num; ++i) t.set_hash(i, ph); std::vector tmp; std::back_insert_iterator > out(tmp); bencode(out, t.generate()); error_code ec; int called = 0; plugin_creator creator(called); add_torrent_params p; p.ti = boost::make_shared(&tmp[0], tmp.size(), boost::ref(ec), 0); p.flags &= ~add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; p.flags &= ~add_torrent_params::flag_duplicate_is_error; p.save_path = "."; p.extensions.push_back(creator); lt::session ses(settings()); ses.async_add_torrent(p); ses.async_add_torrent(p); wait_for_downloading(ses, "ses"); // we should only have added the plugin once TEST_EQUAL(called, 1); } #endif TORRENT_TEST(torrent_total_size_zero) { file_storage fs; error_code ec; fs.add_file("test_torrent_dir2/tmp1", 0); TEST_CHECK(fs.num_files() == 1); TEST_CHECK(fs.total_size() == 0); ec.clear(); libtorrent::create_torrent t1(fs); set_piece_hashes(t1, ".", ec); TEST_CHECK(ec); fs.add_file("test_torrent_dir2/tmp2", 0); TEST_CHECK(fs.num_files() == 2); TEST_CHECK(fs.total_size() == 0); ec.clear(); libtorrent::create_torrent t2(fs); set_piece_hashes(t2, ".", ec); TEST_CHECK(ec); } void test_queue(add_torrent_params p) { lt::settings_pack pack = settings(); // we're not testing the hash check, just accept the data we write pack.set_bool(settings_pack::disable_hash_checks, true); lt::session ses(pack); std::vector torrents; for(int i = 0; i < 6; i++) { file_storage fs; std::stringstream file_path; file_path << "test_torrent_dir4/queue" << i; fs.add_file(file_path.str(), 1024); libtorrent::create_torrent t(fs, 128 * 1024, 6); std::vector buf; bencode(std::back_inserter(buf), t.generate()); boost::shared_ptr ti = boost::make_shared(&buf[0], buf.size()); p.ti = ti; p.save_path = "."; torrents.push_back(ses.add_torrent(p)); } print_alerts(ses, "ses"); std::vector pieces = torrents[5].piece_priorities(); std::vector > piece_prios; for (int i = 0; i < int(pieces.size()); ++i) { piece_prios.push_back(std::make_pair(i,0)); } torrents[5].prioritize_pieces(piece_prios); torrent_handle finished = torrents[5]; wait_for_alert(ses, torrent_finished_alert::alert_type, "ses"); // add_torrent should be ordered TEST_EQUAL(finished.queue_position(), -1); TEST_EQUAL(torrents[0].queue_position(), 0); TEST_EQUAL(torrents[1].queue_position(), 1); TEST_EQUAL(torrents[2].queue_position(), 2); TEST_EQUAL(torrents[3].queue_position(), 3); TEST_EQUAL(torrents[4].queue_position(), 4); // test top and bottom torrents[2].queue_position_top(); torrents[1].queue_position_bottom(); TEST_EQUAL(finished.queue_position(), -1); TEST_EQUAL(torrents[2].queue_position(), 0); TEST_EQUAL(torrents[0].queue_position(), 1); TEST_EQUAL(torrents[3].queue_position(), 2); TEST_EQUAL(torrents[4].queue_position(), 3); TEST_EQUAL(torrents[1].queue_position(), 4); // test set pos torrents[0].queue_position_set(0); torrents[1].queue_position_set(1); // torrent 2 should be get moved down by 0 and 1 to pos 2 TEST_EQUAL(finished.queue_position(), -1); TEST_EQUAL(torrents[0].queue_position(), 0); TEST_EQUAL(torrents[1].queue_position(), 1); TEST_EQUAL(torrents[2].queue_position(), 2); TEST_EQUAL(torrents[3].queue_position(), 3); TEST_EQUAL(torrents[4].queue_position(), 4); //test strange up and down commands torrents[0].queue_position_up(); torrents[4].queue_position_down(); TEST_EQUAL(finished.queue_position(), -1); TEST_EQUAL(torrents[0].queue_position(), 0); TEST_EQUAL(torrents[1].queue_position(), 1); TEST_EQUAL(torrents[2].queue_position(), 2); TEST_EQUAL(torrents[3].queue_position(), 3); TEST_EQUAL(torrents[4].queue_position(), 4); torrents[1].queue_position_up(); torrents[3].queue_position_down(); finished.queue_position_up(); TEST_EQUAL(finished.queue_position(), -1); TEST_EQUAL(torrents[1].queue_position(), 0); TEST_EQUAL(torrents[0].queue_position(), 1); TEST_EQUAL(torrents[2].queue_position(), 2); TEST_EQUAL(torrents[4].queue_position(), 3); TEST_EQUAL(torrents[3].queue_position(), 4); torrents[1].queue_position_down(); torrents[3].queue_position_up(); finished.queue_position_down(); TEST_EQUAL(finished.queue_position(), -1); TEST_EQUAL(torrents[0].queue_position(), 0); TEST_EQUAL(torrents[1].queue_position(), 1); TEST_EQUAL(torrents[2].queue_position(), 2); TEST_EQUAL(torrents[3].queue_position(), 3); TEST_EQUAL(torrents[4].queue_position(), 4); // test set pos on not existing pos torrents[3].queue_position_set(10); finished.queue_position_set(10); TEST_EQUAL(finished.queue_position(), -1); TEST_EQUAL(torrents[0].queue_position(), 0); TEST_EQUAL(torrents[1].queue_position(), 1); TEST_EQUAL(torrents[2].queue_position(), 2); TEST_EQUAL(torrents[4].queue_position(), 3); TEST_EQUAL(torrents[3].queue_position(), 4); } TORRENT_TEST(queue) { test_queue(add_torrent_params()); } TORRENT_TEST(queue_paused) { add_torrent_params p; p.flags |= add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; test_queue(p); } TORRENT_TEST(test_move_storage_no_metadata) { lt::session ses(settings()); add_torrent_params p; p.save_path = "save_path"; error_code ec; parse_magnet_uri("magnet?xt=urn:btih:abababababababababababababababababababab", p, ec); torrent_handle h = ses.add_torrent(p); TEST_EQUAL(h.status().save_path, complete("save_path")); h.move_storage("save_path_1"); TEST_EQUAL(h.status().save_path, complete("save_path_1")); } libtorrent-rasterbar-1.1.13/test/test_torrent_info.cpp000066400000000000000000000737361351156116000232150ustar00rootroot00000000000000/* Copyright (c) 2012, 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 "test.hpp" #include "libtorrent/file_storage.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/announce_entry.hpp" #include "libtorrent/aux_/escape_string.hpp" // for convert_path_to_posix #include "libtorrent/aux_/disable_warnings_push.hpp" #include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" #if TORRENT_USE_IOSTREAM #include #endif using namespace libtorrent; #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS TORRENT_TEST(mutable_torrents) { file_storage fs; fs.add_file("test/temporary.txt", 0x4000); libtorrent::create_torrent t(fs, 0x4000); // calculate the hash for all pieces int num = t.num_pieces(); sha1_hash ph; for (int i = 0; i < num; ++i) t.set_hash(i, ph); t.add_collection("collection1"); t.add_collection("collection2"); t.add_similar_torrent(sha1_hash("abababababababababab")); t.add_similar_torrent(sha1_hash("babababababababababa")); std::vector tmp; std::back_insert_iterator > out(tmp); entry tor = t.generate(); bencode(out, tor); torrent_info ti(&tmp[0], tmp.size()); std::vector similar; similar.push_back(sha1_hash("abababababababababab")); similar.push_back(sha1_hash("babababababababababa")); std::vector collections; collections.push_back("collection1"); collections.push_back("collection2"); TEST_CHECK(similar == ti.similar_torrents()); TEST_CHECK(collections == ti.collections()); } #endif struct test_torrent_t { char const* file; }; using namespace libtorrent; static test_torrent_t test_torrents[] = { { "base.torrent" }, { "empty_path.torrent" }, { "parent_path.torrent" }, { "hidden_parent_path.torrent" }, { "single_multi_file.torrent" }, { "slash_path.torrent" }, { "slash_path2.torrent" }, { "slash_path3.torrent" }, { "backslash_path.torrent" }, { "url_list.torrent" }, { "url_list2.torrent" }, { "url_list3.torrent" }, { "httpseed.torrent" }, { "empty_httpseed.torrent" }, { "long_name.torrent" }, { "whitespace_url.torrent" }, { "duplicate_files.torrent" }, { "pad_file.torrent" }, { "creation_date.torrent" }, { "no_creation_date.torrent" }, { "url_seed.torrent" }, { "url_seed_multi.torrent" }, { "url_seed_multi_space.torrent" }, { "url_seed_multi_space_nolist.torrent" }, { "root_hash.torrent" }, { "empty_path_multi.torrent" }, { "duplicate_web_seeds.torrent" }, { "invalid_name2.torrent" }, { "invalid_name3.torrent" }, { "symlink1.torrent" }, { "unordered.torrent" }, { "symlink_zero_size.torrent" }, { "pad_file_no_path.torrent" }, { "invalid_filename.torrent" }, { "invalid_filename2.torrent" }, }; struct test_failing_torrent_t { char const* file; error_code error; // the expected error }; test_failing_torrent_t test_error_torrents[] = { { "missing_piece_len.torrent", errors::torrent_missing_piece_length }, { "invalid_piece_len.torrent", errors::torrent_missing_piece_length }, { "negative_piece_len.torrent", errors::torrent_missing_piece_length }, { "no_name.torrent", errors::torrent_missing_name }, { "invalid_name.torrent", errors::torrent_missing_name }, { "invalid_info.torrent", errors::torrent_missing_info }, { "string.torrent", errors::torrent_is_no_dict }, { "negative_size.torrent", errors::torrent_invalid_length }, { "negative_file_size.torrent", errors::torrent_invalid_length }, { "invalid_path_list.torrent", errors::torrent_invalid_name}, { "missing_path_list.torrent", errors::torrent_missing_name }, { "invalid_pieces.torrent", errors::torrent_missing_pieces }, { "unaligned_pieces.torrent", errors::torrent_invalid_hashes }, { "invalid_root_hash.torrent", errors::torrent_invalid_hashes }, { "invalid_root_hash2.torrent", errors::torrent_missing_pieces }, { "invalid_merkle.torrent", errors::no_files_in_torrent}, { "invalid_file_size.torrent", errors::torrent_invalid_length }, { "invalid_symlink.torrent", errors::torrent_invalid_name }, }; // TODO: test remap_files // TODO: merkle torrents. specifically torrent_info::add_merkle_nodes and torrent with "root hash" // TODO: torrent with 'p' (padfile) attribute // TODO: torrent with 'h' (hidden) attribute // TODO: torrent with 'x' (executable) attribute // TODO: torrent with 'l' (symlink) attribute // TODO: creating a merkle torrent (torrent_info::build_merkle_list) // TODO: torrent with multiple trackers in multiple tiers, making sure we shuffle them (how do you test shuffling?, load it multiple times and make sure it's in different order at least once) // TODO: torrents with a zero-length name // TODO: torrents with a merkle tree and add_merkle_nodes // TODO: torrent with a non-dictionary info-section // TODO: torrents with DHT nodes // TODO: torrent with url-list as a single string // TODO: torrent with http seed as a single string // TODO: torrent with a comment // TODO: torrent with an SSL cert // TODO: torrent with attributes (executable and hidden) // TODO: torrent_info::add_tracker // TODO: torrent_info::unload // TODO: torrent_info constructor that takes an invalid bencoded buffer // TODO: verify_encoding with a string that triggers character replacement TORRENT_TEST(add_url_seed) { torrent_info ti(sha1_hash(" ")); TEST_EQUAL(ti.web_seeds().size(), 0); ti.add_url_seed("http://test.com"); TEST_EQUAL(ti.web_seeds().size(), 1); web_seed_entry we = ti.web_seeds()[0]; TEST_EQUAL(we.type, web_seed_entry::url_seed); TEST_EQUAL(we.url, "http://test.com"); } TORRENT_TEST(add_http_seed) { torrent_info ti(sha1_hash(" ")); TEST_EQUAL(ti.web_seeds().size(), 0); ti.add_http_seed("http://test.com"); TEST_EQUAL(ti.web_seeds().size(), 1); web_seed_entry we = ti.web_seeds()[0]; TEST_EQUAL(we.type, web_seed_entry::http_seed); TEST_EQUAL(we.url, "http://test.com"); } TORRENT_TEST(set_web_seeds) { torrent_info ti(sha1_hash(" ")); TEST_EQUAL(ti.web_seeds().size(), 0); std::vector seeds; web_seed_entry e1("http://test1.com", web_seed_entry::url_seed); seeds.push_back(e1); web_seed_entry e2("http://test2com", web_seed_entry::http_seed); seeds.push_back(e2); ti.set_web_seeds(seeds); TEST_EQUAL(ti.web_seeds().size(), 2); TEST_CHECK(ti.web_seeds() == seeds); } #ifdef TORRENT_WINDOWS #define SEPARATOR "\\" #else #define SEPARATOR "/" #endif TORRENT_TEST(sanitize_long_path) { // test sanitize_append_path_element std::string path; sanitize_append_path_element(path, "abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_" "abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_" "abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_", 250); sanitize_append_path_element(path, "abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_" "abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_" "abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcde.test", 250); TEST_EQUAL(path, "abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_" "abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_" "abcdefghi_abcdefghi_abcdefghi_abcdefghi_" SEPARATOR "abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_" "abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_" "abcdefghi_abcdefghi_abcdefghi_abcdefghi_.test"); } TORRENT_TEST(sanitize_path_trailing_dots) { std::string path; sanitize_append_path_element(path, "a", 1); sanitize_append_path_element(path, "abc...", 6); sanitize_append_path_element(path, "c", 1); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "a" SEPARATOR "abc" SEPARATOR "c"); #else TEST_EQUAL(path, "a" SEPARATOR "abc..." SEPARATOR "c"); #endif path.clear(); sanitize_append_path_element(path, "abc...", 6); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "abc"); #else TEST_EQUAL(path, "abc..."); #endif path.clear(); sanitize_append_path_element(path, "abc.", 4); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "abc"); #else TEST_EQUAL(path, "abc."); #endif path.clear(); sanitize_append_path_element(path, "a. . .", 6); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "a"); #else TEST_EQUAL(path, "a. . ."); #endif } TORRENT_TEST(sanitize_path_trailing_spaces) { std::string path; sanitize_append_path_element(path, "a", 1); sanitize_append_path_element(path, "abc ", 6); sanitize_append_path_element(path, "c", 1); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "a" SEPARATOR "abc" SEPARATOR "c"); #else TEST_EQUAL(path, "a" SEPARATOR "abc " SEPARATOR "c"); #endif path.clear(); sanitize_append_path_element(path, "abc ", 6); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "abc"); #else TEST_EQUAL(path, "abc "); #endif path.clear(); sanitize_append_path_element(path, "abc ", 4); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "abc"); #else TEST_EQUAL(path, "abc "); #endif } TORRENT_TEST(sanitize_path) { std::string path; sanitize_append_path_element(path, "\0\0\xed\0\x80", 5); TEST_EQUAL(path, "_"); path.clear(); sanitize_append_path_element(path, "/a/", 3); sanitize_append_path_element(path, "b", 1); sanitize_append_path_element(path, "c", 1); TEST_EQUAL(path, "a" SEPARATOR "b" SEPARATOR "c"); path.clear(); sanitize_append_path_element(path, "a...b", 5); TEST_EQUAL(path, "a...b"); path.clear(); sanitize_append_path_element(path, "a", 1); sanitize_append_path_element(path, "..", 2); sanitize_append_path_element(path, "c", 1); TEST_EQUAL(path, "a" SEPARATOR "c"); path.clear(); sanitize_append_path_element(path, "a", 1); sanitize_append_path_element(path, "..", 2); TEST_EQUAL(path, "a"); path.clear(); sanitize_append_path_element(path, "/..", 3); sanitize_append_path_element(path, ".", 1); sanitize_append_path_element(path, "c", 1); TEST_EQUAL(path, "c"); path.clear(); sanitize_append_path_element(path, "dev:", 4); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "dev_"); #else TEST_EQUAL(path, "dev:"); #endif path.clear(); sanitize_append_path_element(path, "c:", 2); sanitize_append_path_element(path, "b", 1); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "c_" SEPARATOR "b"); #else TEST_EQUAL(path, "c:" SEPARATOR "b"); #endif path.clear(); sanitize_append_path_element(path, "c:", 2); sanitize_append_path_element(path, ".", 1); sanitize_append_path_element(path, "c", 1); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "c_" SEPARATOR "c"); #else TEST_EQUAL(path, "c:" SEPARATOR "c"); #endif path.clear(); sanitize_append_path_element(path, "\\c", 2); sanitize_append_path_element(path, ".", 1); sanitize_append_path_element(path, "c", 1); TEST_EQUAL(path, "c" SEPARATOR "c"); path.clear(); sanitize_append_path_element(path, "\b", 1); TEST_EQUAL(path, "_"); path.clear(); sanitize_append_path_element(path, "\b", 1); sanitize_append_path_element(path, "filename", 8); TEST_EQUAL(path, "_" SEPARATOR "filename"); path.clear(); sanitize_append_path_element(path, "filename", 8); sanitize_append_path_element(path, "\b", 1); TEST_EQUAL(path, "filename" SEPARATOR "_"); path.clear(); sanitize_append_path_element(path, "abc", 3); sanitize_append_path_element(path, "", 0); TEST_EQUAL(path, "abc" SEPARATOR "_"); path.clear(); sanitize_append_path_element(path, "abc", 3); sanitize_append_path_element(path, " ", 3); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "abc"); #else TEST_EQUAL(path, "abc" SEPARATOR " "); #endif path.clear(); sanitize_append_path_element(path, "", 0); sanitize_append_path_element(path, "abc", 3); TEST_EQUAL(path, "_" SEPARATOR "abc"); path.clear(); sanitize_append_path_element(path, "\b?filename=4", 12); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "__filename=4"); #else TEST_EQUAL(path, "_?filename=4"); #endif path.clear(); sanitize_append_path_element(path, "filename=4", 10); TEST_EQUAL(path, "filename=4"); // valid 2-byte sequence path.clear(); sanitize_append_path_element(path, "filename\xc2\xa1", 10); TEST_EQUAL(path, "filename\xc2\xa1"); // truncated 2-byte sequence path.clear(); sanitize_append_path_element(path, "filename\xc2", 9); TEST_EQUAL(path, "filename_"); // valid 3-byte sequence path.clear(); sanitize_append_path_element(path, "filename\xe2\x9f\xb9", 11); TEST_EQUAL(path, "filename\xe2\x9f\xb9"); // truncated 3-byte sequence path.clear(); sanitize_append_path_element(path, "filename\xe2\x9f", 10); TEST_EQUAL(path, "filename_"); // truncated 3-byte sequence path.clear(); sanitize_append_path_element(path, "filename\xe2", 9); TEST_EQUAL(path, "filename_"); // valid 4-byte sequence path.clear(); sanitize_append_path_element(path, "filename\xf0\x9f\x92\x88", 12); TEST_EQUAL(path, "filename\xf0\x9f\x92\x88"); // truncated 4-byte sequence path.clear(); sanitize_append_path_element(path, "filename\xf0\x9f\x92", 11); TEST_EQUAL(path, "filename_"); // 5-byte utf-8 sequence (not allowed) path.clear(); sanitize_append_path_element(path, "filename\xf8\x9f\x9f\x9f\x9f" "foobar", 19); TEST_EQUAL(path, "filename_foobar"); // redundant (overlong) 2-byte sequence // ascii code 0x2e encoded with a leading 0 path.clear(); sanitize_append_path_element(path, "filename\xc0\xae", 10); TEST_EQUAL(path, "filename_"); // redundant (overlong) 3-byte sequence // ascii code 0x2e encoded with two leading 0s path.clear(); sanitize_append_path_element(path, "filename\xe0\x80\xae", 11); TEST_EQUAL(path, "filename_"); // redundant (overlong) 4-byte sequence // ascii code 0x2e encoded with three leading 0s path.clear(); sanitize_append_path_element(path, "filename\xf0\x80\x80\xae", 12); TEST_EQUAL(path, "filename_"); // a filename where every character is filtered is not replaced by an understcore path.clear(); sanitize_append_path_element(path, "//\\", 3); TEST_EQUAL(path, ""); // make sure suspicious unicode characters are filtered out path.clear(); // that's utf-8 for U+200e LEFT-TO-RIGHT MARK sanitize_append_path_element(path, "foo\xe2\x80\x8e" "bar", 9); TEST_EQUAL(path, "foobar"); // make sure suspicious unicode characters are filtered out path.clear(); // that's utf-8 for U+202b RIGHT-TO-LEFT EMBEDDING sanitize_append_path_element(path, "foo\xe2\x80\xab" "bar", 9); TEST_EQUAL(path, "foobar"); } TORRENT_TEST(sanitize_path_zeroes) { std::string path; sanitize_append_path_element(path, "\0foo", 4); TEST_EQUAL(path, "foo"); path.clear(); sanitize_append_path_element(path, "\0\0\0\0", 4); TEST_EQUAL(path, ""); } TORRENT_TEST(sanitize_path_colon) { std::string path; sanitize_append_path_element(path, "foo:bar", 7); #ifdef TORRENT_WINDOWS TEST_EQUAL(path, "foo_bar"); #else TEST_EQUAL(path, "foo:bar"); #endif } TORRENT_TEST(verify_encoding) { // verify_encoding std::string test = "\b?filename=4"; TEST_CHECK(verify_encoding(test)); TEST_CHECK(test == "\b?filename=4"); test = "filename=4"; TEST_CHECK(verify_encoding(test)); TEST_CHECK(test == "filename=4"); // valid 2-byte sequence test = "filename\xc2\xa1"; TEST_CHECK(verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename\xc2\xa1"); // truncated 2-byte sequence test = "filename\xc2"; TEST_CHECK(!verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename_"); // valid 3-byte sequence test = "filename\xe2\x9f\xb9"; TEST_CHECK(verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename\xe2\x9f\xb9"); // truncated 3-byte sequence test = "filename\xe2\x9f"; TEST_CHECK(!verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename_"); // truncated 3-byte sequence test = "filename\xe2"; TEST_CHECK(!verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename_"); // valid 4-byte sequence test = "filename\xf0\x9f\x92\x88"; TEST_CHECK(verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename\xf0\x9f\x92\x88"); // truncated 4-byte sequence test = "filename\xf0\x9f\x92"; TEST_CHECK(!verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename_"); // 5-byte utf-8 sequence (not allowed) test = "filename\xf8\x9f\x9f\x9f\x9f""foobar"; TEST_CHECK(!verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename_____foobar"); // redundant (overlong) 2-byte sequence // ascii code 0x2e encoded with a leading 0 test = "filename\xc0\xae"; TEST_CHECK(!verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename__"); // redundant (overlong) 3-byte sequence // ascii code 0x2e encoded with two leading 0s test = "filename\xe0\x80\xae"; TEST_CHECK(!verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename___"); // redundant (overlong) 4-byte sequence // ascii code 0x2e encoded with three leading 0s test = "filename\xf0\x80\x80\xae"; TEST_CHECK(!verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename____"); // missing byte header test = "filename\xed\0\x80"; TEST_CHECK(!verify_encoding(test)); fprintf(stdout, "%s\n", test.c_str()); TEST_CHECK(test == "filename_"); } TORRENT_TEST(parse_torrents) { error_code ec; // test torrent parsing entry info; info["pieces"] = "aaaaaaaaaaaaaaaaaaaa"; info["name.utf-8"] = "test1"; info["name"] = "test__"; info["piece length"] = 16 * 1024; info["length"] = 3245; entry torrent; torrent["info"] = info; std::vector buf; bencode(std::back_inserter(buf), torrent); torrent_info ti(&buf[0], buf.size(), ec); std::cerr << ti.name() << std::endl; TEST_CHECK(ti.name() == "test1"); #ifdef TORRENT_WINDOWS info["name.utf-8"] = "c:/test1/test2/test3"; #else info["name.utf-8"] = "/test1/test2/test3"; #endif torrent["info"] = info; buf.clear(); bencode(std::back_inserter(buf), torrent); torrent_info ti2(&buf[0], buf.size(), ec); std::cerr << ti2.name() << std::endl; #ifdef TORRENT_WINDOWS TEST_EQUAL(ti2.name(), "c_test1test2test3"); #else TEST_EQUAL(ti2.name(), "test1test2test3"); #endif info["name.utf-8"] = "test2/../test3/.././../../test4"; torrent["info"] = info; buf.clear(); bencode(std::back_inserter(buf), torrent); torrent_info ti3(&buf[0], buf.size(), ec); std::cerr << ti3.name() << std::endl; TEST_EQUAL(ti3.name(), "test2..test3.......test4"); std::string root_dir = parent_path(current_working_directory()); for (int i = 0; i < int(sizeof(test_torrents)/sizeof(test_torrents[0])); ++i) { fprintf(stdout, "loading %s\n", test_torrents[i].file); std::string filename = combine_path(combine_path(root_dir, "test_torrents") , test_torrents[i].file); boost::shared_ptr ti(new torrent_info(filename, ec)); TEST_CHECK(!ec); if (ec) fprintf(stdout, " loading(\"%s\") -> failed %s\n", filename.c_str() , ec.message().c_str()); if (std::string(test_torrents[i].file) == "whitespace_url.torrent") { // make sure we trimmed the url TEST_CHECK(ti->trackers().size() > 0); if (ti->trackers().size() > 0) TEST_CHECK(ti->trackers()[0].url == "udp://test.com/announce"); } else if (std::string(test_torrents[i].file) == "duplicate_files.torrent") { // make sure we disambiguated the files TEST_EQUAL(ti->num_files(), 2); TEST_CHECK(ti->files().file_path(0) == combine_path(combine_path("temp", "foo"), "bar.txt")); TEST_CHECK(ti->files().file_path(1) == combine_path(combine_path("temp", "foo"), "bar.1.txt")); } else if (std::string(test_torrents[i].file) == "pad_file.torrent") { TEST_EQUAL(ti->num_files(), 2); TEST_EQUAL(ti->files().file_flags(0) & file_storage::flag_pad_file, false); TEST_EQUAL(ti->files().file_flags(1) & file_storage::flag_pad_file, true); } else if (std::string(test_torrents[i].file) == "creation_date.torrent") { TEST_EQUAL(*ti->creation_date(), 1234567); } else if (std::string(test_torrents[i].file) == "duplicate_web_seeds.torrent") { TEST_EQUAL(ti->web_seeds().size(), 3); } else if (std::string(test_torrents[i].file) == "no_creation_date.torrent") { TEST_CHECK(!ti->creation_date()); } else if (std::string(test_torrents[i].file) == "url_seed.torrent") { TEST_EQUAL(ti->web_seeds().size(), 1); TEST_EQUAL(ti->web_seeds()[0].url, "http://test.com/file"); #ifndef TORRENT_NO_DEPRECATE TEST_EQUAL(ti->http_seeds().size(), 0); TEST_EQUAL(ti->url_seeds().size(), 1); TEST_EQUAL(ti->url_seeds()[0], "http://test.com/file"); #endif } else if (std::string(test_torrents[i].file) == "url_seed_multi.torrent") { TEST_EQUAL(ti->web_seeds().size(), 1); TEST_EQUAL(ti->web_seeds()[0].url, "http://test.com/file/"); #ifndef TORRENT_NO_DEPRECATE TEST_EQUAL(ti->http_seeds().size(), 0); TEST_EQUAL(ti->url_seeds().size(), 1); TEST_EQUAL(ti->url_seeds()[0], "http://test.com/file/"); #endif } else if (std::string(test_torrents[i].file) == "url_seed_multi_space.torrent" || std::string(test_torrents[i].file) == "url_seed_multi_space_nolist.torrent") { TEST_EQUAL(ti->web_seeds().size(), 1); TEST_EQUAL(ti->web_seeds()[0].url, "http://test.com/test%20file/foo%20bar/"); #ifndef TORRENT_NO_DEPRECATE TEST_EQUAL(ti->http_seeds().size(), 0); TEST_EQUAL(ti->url_seeds().size(), 1); TEST_EQUAL(ti->url_seeds()[0], "http://test.com/test%20file/foo%20bar/"); #endif } else if (std::string(test_torrents[i].file) == "invalid_name2.torrent") { // if, after all invalid characters are removed from the name, it ends up // being empty, it's set to the info-hash. Some torrents also have an empty name // in which case it's also set to the info-hash TEST_EQUAL(ti->name(), "b61560c2918f463768cd122b6d2fdd47b77bdb35"); } else if (std::string(test_torrents[i].file) == "invalid_name3.torrent") { // windows does not allow trailing spaces in filenames #ifdef TORRENT_WINDOWS TEST_EQUAL(ti->name(), "foobar"); #else TEST_EQUAL(ti->name(), "foobar "); #endif } else if (std::string(test_torrents[i].file) == "slash_path.torrent") { TEST_EQUAL(ti->num_files(), 1); TEST_EQUAL(ti->files().file_path(0), "temp" SEPARATOR "bar"); } else if (std::string(test_torrents[i].file) == "slash_path2.torrent") { TEST_EQUAL(ti->num_files(), 1); TEST_EQUAL(ti->files().file_path(0), "temp" SEPARATOR "abc....def" SEPARATOR "bar"); } else if (std::string(test_torrents[i].file) == "slash_path3.torrent") { TEST_EQUAL(ti->num_files(), 1); TEST_EQUAL(ti->files().file_path(0), "temp....abc"); } else if (std::string(test_torrents[i].file) == "symlink_zero_size.torrent") { TEST_EQUAL(ti->num_files(), 2); TEST_EQUAL(ti->files().symlink(1), combine_path("foo", "bar")); } else if (std::string(test_torrents[i].file) == "pad_file_no_path.torrent") { TEST_EQUAL(ti->num_files(), 2); TEST_EQUAL(ti->files().file_path(1), combine_path(".pad", "0")); } else if (std::string(test_torrents[i].file) == "invalid_filename.torrent") { TEST_EQUAL(ti->num_files(), 2); } else if (std::string(test_torrents[i].file) == "invalid_filename2.torrent") { TEST_EQUAL(ti->num_files(), 3); } file_storage const& fs = ti->files(); for (int i = 0; i < fs.num_files(); ++i) { int first = ti->map_file(i, 0, 0).piece; int last = ti->map_file(i, (std::max)(fs.file_size(i)-1, boost::int64_t(0)), 0).piece; int flags = fs.file_flags(i); fprintf(stdout, " %11" PRId64 " %c%c%c%c [ %4d, %4d ] %7u %s %s %s%s\n" , fs.file_size(i) , (flags & file_storage::flag_pad_file)?'p':'-' , (flags & file_storage::flag_executable)?'x':'-' , (flags & file_storage::flag_hidden)?'h':'-' , (flags & file_storage::flag_symlink)?'l':'-' , first, last , boost::uint32_t(fs.mtime(i)) , fs.hash(i) != sha1_hash(0) ? to_hex(fs.hash(i).to_string()).c_str() : "" , fs.file_path(i).c_str() , flags & file_storage::flag_symlink ? "-> ": "" , flags & file_storage::flag_symlink ? fs.symlink(i).c_str() : ""); } // test swap #if !defined TORRENT_NO_DEPRECATE && TORRENT_USE_IOSTREAM std::stringstream str1; ti->print(str1); torrent_info temp("temp", ec); temp.swap(*ti); std::stringstream str2; temp.print(str2); TEST_EQUAL(str1.str(), str2.str()); #endif } for (int i = 0; i < int(sizeof(test_error_torrents)/sizeof(test_error_torrents[0])); ++i) { error_code ec; fprintf(stdout, "loading %s\n", test_error_torrents[i].file); boost::shared_ptr ti(new torrent_info(combine_path( combine_path(root_dir, "test_torrents"), test_error_torrents[i].file), ec)); fprintf(stdout, "E: \"%s\"\nexpected: \"%s\"\n", ec.message().c_str() , test_error_torrents[i].error.message().c_str()); TEST_CHECK(ec.message() == test_error_torrents[i].error.message()); TEST_EQUAL(ti->is_valid(), false); } } void test_resolve_duplicates(int test_case) { file_storage fs; switch (test_case) { case 0: fs.add_file("test/temporary.txt", 0x4000); fs.add_file("test/Temporary.txt", 0x4000); fs.add_file("test/TeMPorArY.txT", 0x4000); fs.add_file("test/test/TEMPORARY.TXT", 0x4000); break; case 1: fs.add_file("test/b.exe", 0x4000); fs.add_file("test/B.ExE", 0x4000); fs.add_file("test/B.exe", 0x4000); fs.add_file("test/filler", 0x4000); break; case 2: fs.add_file("test/A/tmp", 0x4000); fs.add_file("test/a", 0x4000); fs.add_file("test/A", 0x4000); fs.add_file("test/filler", 0x4000); break; case 3: fs.add_file("test/long/path/name/that/collides", 0x4000); fs.add_file("test/long/path", 0x4000); fs.add_file("test/filler-1", 0x4000); fs.add_file("test/filler-2", 0x4000); break; } libtorrent::create_torrent t(fs, 0x4000); // calculate the hash for all pieces int num = t.num_pieces(); sha1_hash ph; for (int i = 0; i < num; ++i) t.set_hash(i, ph); std::vector tmp; std::back_insert_iterator > out(tmp); entry tor = t.generate(); bencode(out, tor); torrent_info ti(&tmp[0], tmp.size()); char const* filenames[4][4] = { { // case 0 "test/temporary.txt", "test/Temporary.1.txt", // duplicate of temporary.txt "test/TeMPorArY.2.txT", // duplicate of temporary.txt // a file with the same name in a seprate directory is fine "test/test/TEMPORARY.TXT", }, { // case 1 "test/b.exe", "test/B.1.ExE", // duplicate of b.exe "test/B.2.exe", // duplicate of b.exe "test/filler", }, { // case 2 "test/A/tmp", "test/a.1", // a file may not have the same name as a directory "test/A.2", // duplicate of directory a "test/filler", }, { // case 3 // a subset of this path collides with the next filename "test/long/path/name/that/collides", // so this file needs to be renamed, to not collide with the path name "test/long/path.1", "test/filler-1", "test/filler-2", } }; for (int i = 0; i < ti.num_files(); ++i) { std::string p = ti.files().file_path(i); convert_path_to_posix(p); fprintf(stdout, "%s == %s\n", p.c_str(), filenames[test_case][i]); TEST_EQUAL(p, filenames[test_case][i]); } } TORRENT_TEST(resolve_duplicates) { for (int i = 0; i < 4; ++i) test_resolve_duplicates(i); } TORRENT_TEST(empty_file) { error_code ec; boost::shared_ptr ti = boost::make_shared("", 0, boost::ref(ec)); TEST_CHECK(ec); } TORRENT_TEST(empty_file2) { try { boost::shared_ptr ti = boost::make_shared("", 0); TEST_ERROR("expected exception thrown"); } catch (libtorrent_exception& e) { printf("Expected error: %s\n", e.error().message().c_str()); } } TORRENT_TEST(copy) { using namespace libtorrent; boost::shared_ptr a(boost::make_shared( combine_path(parent_path(current_working_directory()) , combine_path("test_torrents", "sample.torrent")))); char const* expected_files[] = { "sample/text_file2.txt", "sample/.____padding_file/0", "sample/text_file.txt", }; sha1_hash file_hashes[] = { sha1_hash(0), sha1_hash(0), sha1_hash("abababababababababab") }; for (int i = 0; i < a->num_files(); ++i) { std::string p = a->files().file_path(i); convert_path_to_posix(p); TEST_EQUAL(p, expected_files[i]); fprintf(stdout, "%s\n", p.c_str()); TEST_EQUAL(a->files().hash(i), file_hashes[i]); } // copy the torrent_info object boost::shared_ptr b(boost::make_shared(*a)); // clear out the buffer for a, just to make sure b doesn't have any // references into it by mistake int s = a->metadata_size(); memset(a->metadata().get(), 0, s); a.reset(); TEST_EQUAL(b->num_files(), 3); for (int i = 0; i < b->num_files(); ++i) { std::string p = b->files().file_path(i); convert_path_to_posix(p); TEST_EQUAL(p, expected_files[i]); fprintf(stdout, "%s\n", p.c_str()); TEST_EQUAL(b->files().hash(i), file_hashes[i]); } } libtorrent-rasterbar-1.1.13/test/test_torrents/000077500000000000000000000000001351156116000216415ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/test/test_torrents/backslash_path.torrent000066400000000000000000000002551351156116000262310ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl1:\1:\3:bareee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/base.torrent000066400000000000000000000002161351156116000241710ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡eelibtorrent-rasterbar-1.1.13/test/test_torrents/creation_date.torrent000066400000000000000000000003221351156116000260560ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1234567e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece lengthi16384e6:pieces20:01234567890123456789ee libtorrent-rasterbar-1.1.13/test/test_torrents/duplicate_files.torrent000066400000000000000000000003251351156116000264140ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:bar.txteee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/duplicate_web_seeds.torrent000066400000000000000000000006021351156116000272500ustar00rootroot00000000000000d8:announce23:udp://test.com/announce10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡e8:url-listl21:http://foobar.com/foo21:http://foobar.com/foo21:http://foobar.com/foo24:http://foobar.com/foobar21:http://foobar.com/foo21:http://foobar.com/foo21:http://foobar.com/foo21:http://foobar.com/baree libtorrent-rasterbar-1.1.13/test/test_torrents/empty_httpseed.torrent000066400000000000000000000002541351156116000263170ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e9:httpseedsl0:e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡e8:url-listl0:ee libtorrent-rasterbar-1.1.13/test/test_torrents/empty_path.torrent000066400000000000000000000002461351156116000254340ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl0:0:eee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/empty_path_multi.torrent000066400000000000000000000003271351156116000266460ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl5:abcdeeed6:lengthi3e4:pathl0:eed6:lengthi5e4:pathl0:eee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/hidden_parent_path.torrent000066400000000000000000000002721351156116000271010ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl16:foo/../../../bar3:bareee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/httpseed.torrent000066400000000000000000000002771351156116000251060ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e9:httpseedsl18:http://foobar.com/e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡e8:url-listl0:ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_file_size.torrent000066400000000000000000000003301351156116000267330ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld4:pathl3:foo7:bar.txte6:lengthli45eeed4:pathl3:foo7:var.txte6:lengthi24124eee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_filename.torrent000066400000000000000000000004001351156116000265400ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1419490173e4:infod5:filesld6:lengthi51200e4:pathl1:feed6:lengthi14336e4:pathl1:.eee4:name5:\est212:piece lengthi16384e6:pieces80:01234567890123456789012345678901234567890123456789012345678901234567890123456789ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_filename2.torrent000066400000000000000000000004361351156116000266330ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1419490173e4:infod5:filesld6:lengthi51200e4:pathl1:feed6:lengthi14335e4:pathl1:.1:.eed6:lengthi1e4:pathl1:/1:.eee4:name5:\est212:piece lengthi16384e6:pieces80:01234567890123456789012345678901234567890123456789012345678901234567890123456789ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_info.torrent000066400000000000000000000001061351156116000257160ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:info5:filese libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_merkle.torrent000066400000000000000000000001551351156116000262460ustar00rootroot00000000000000d10:cion datei15992e4:infod6:lengthi000e4:name4:temp12:piece lengthi12e9:root hash20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_name.torrent000066400000000000000000000003251351156116000257060ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:namei1348e12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_name2.torrent000066400000000000000000000003231351156116000257660ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name2:..12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_name3.torrent000066400000000000000000000003301351156116000257650ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name7:foobar 12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_path_list.torrent000066400000000000000000000003331351156116000267540ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathli1242e3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_piece_len.torrent000066400000000000000000000003251351156116000267110ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece length5:163846:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_pieces.torrent000066400000000000000000000003031351156116000262320ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece lengthi16384e6:piecesi-23eee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_root_hash.torrent000066400000000000000000000002211351156116000267470ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e9:root hash19:žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_root_hash2.torrent000066400000000000000000000002011351156116000270270ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e9:root hashi1343eee libtorrent-rasterbar-1.1.13/test/test_torrents/invalid_symlink.torrent000066400000000000000000000003641351156116000264570ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi0e4:pathl1:a1:b3:bareed4:attr1:l6:lengthi425e4:pathl1:a1:b3:foo12:symlink pathl3:foo3:bari4eeeee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/long_name.torrent000066400000000000000000000006711351156116000252230ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name300:abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0.2345678912:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/missing_path_list.torrent000066400000000000000000000002771351156116000270060ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425eed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/missing_piece_len.torrent000066400000000000000000000002771351156116000267420ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/negative_file_size.torrent000066400000000000000000000003271351156116000271150ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld4:pathl3:foo7:bar.txte6:lengthi-45eed4:pathl3:foo7:var.txte6:lengthi24124eee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/negative_piece_len.torrent000066400000000000000000000003261351156116000270660ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece lengthi-16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/negative_size.torrent000066400000000000000000000002201351156116000261060ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi-425e4:name4:temp12:piece lengthi16384e6:pieces20:cdcdcdcdcdcdcdcdcdcdee libtorrent-rasterbar-1.1.13/test/test_torrents/no_creation_date.torrent000066400000000000000000000002711351156116000265550ustar00rootroot00000000000000d10:created by10:libtorrent4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece lengthi16384e6:pieces20:01234567890123456789ee libtorrent-rasterbar-1.1.13/test/test_torrents/no_name.torrent000066400000000000000000000003111351156116000246670ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/pad_file.torrent000066400000000000000000000003341351156116000250230ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld4:pathl3:foo7:bar.txte6:lengthi45eed4:pathl18:_____padding_file_e6:lengthi2124eee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/pad_file_no_path.torrent000066400000000000000000000003101351156116000265250ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld4:pathl3:foo7:bar.txte6:lengthi45eed4:attr1:p6:lengthi2124eee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/parent_path.torrent000066400000000000000000000002571351156116000255710ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl2:..2:..3:bareee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/root_hash.torrent000066400000000000000000000002221351156116000252420ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e9:root hash20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/sample.torrent000066400000000000000000000007711351156116000245460ustar00rootroot00000000000000d8:announce41:udp://tracker.opentracker.com:80/announce13:announce-listll41:udp://tracker.opentracker.com:80/announceel32:tracker.publicbt.com:80/announceee7:comment14:sample comment10:created by10:libtorrent13:creation datei1418787579e4:infod5:filesld6:lengthi25e4:pathl14:text_file2.txteed4:attr1:p6:lengthi16359e4:pathl17:.____padding_file1:0eed6:lengthi20e4:pathl13:text_file.txte4:sha120:ababababababababababee4:name6:sample12:piece lengthi16384e6:pieces40:ŠńJAQ.ĄMCŕ9ˇOĺkB»{[ź9áč\äGďîţn É‚ĺee libtorrent-rasterbar-1.1.13/test/test_torrents/single_multi_file.torrent000066400000000000000000000002541351156116000267530ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo3:bareee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/slash_path.torrent000066400000000000000000000002551351156116000254100ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl1:/1:/3:bareee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/slash_path2.torrent000066400000000000000000000002721351156116000254710ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl13:abc/../../def1:/3:bareee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/slash_path3.torrent000066400000000000000000000002341351156116000254700ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi12432e4:name14:temp/../../abc12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/string.torrent000066400000000000000000000000161351156116000245630ustar00rootroot0000000000000010:libtorrent libtorrent-rasterbar-1.1.13/test/test_torrents/symlink1.torrent000066400000000000000000000003631351156116000250310ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl1:a1:b3:bareed4:attr1:l6:lengthi425e4:pathl1:a1:b3:fooe12:symlink pathl3:foo3:bareee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/symlink_zero_size.torrent000066400000000000000000000003461351156116000270420ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl1:a1:b3:bareed4:attr1:l4:pathl1:a1:b3:fooe12:symlink pathl3:foo3:bareee4:name4:temp12:piece lengthi16384e6:pieces20:aaaaaaaaaaaaaaaaaaaaee libtorrent-rasterbar-1.1.13/test/test_torrents/unaligned_pieces.torrent000066400000000000000000000003311351156116000265530ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece lengthi16384e6:pieces24:012345678901234567890123ee libtorrent-rasterbar-1.1.13/test/test_torrents/unordered.torrent000066400000000000000000000002171351156116000252470ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod4:name4:temp6:lengthi425e12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_torrents/url_list.torrent000066400000000000000000000002351351156116000251150ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡e8:url-listl0:ee libtorrent-rasterbar-1.1.13/test/test_torrents/url_list2.torrent000066400000000000000000000002371351156116000252010ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡e8:url-listll0:eee libtorrent-rasterbar-1.1.13/test/test_torrents/url_list3.torrent000066400000000000000000000002401351156116000251740ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡e8:url-listli-20eee libtorrent-rasterbar-1.1.13/test/test_torrents/url_seed.torrent000066400000000000000000000002621351156116000250620ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡e8:url-listl20:http://test.com/fileee libtorrent-rasterbar-1.1.13/test/test_torrents/url_seed_multi.torrent000066400000000000000000000003701351156116000262740ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡e8:url-listl20:http://test.com/fileee libtorrent-rasterbar-1.1.13/test/test_torrents/url_seed_multi_space.torrent000066400000000000000000000004051351156116000274460ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡e8:url-listl33:http://test.com/test file/foo baree libtorrent-rasterbar-1.1.13/test/test_torrents/url_seed_multi_space_nolist.torrent000066400000000000000000000004051351156116000310360ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1359599503e4:infod5:filesld6:lengthi425e4:pathl3:foo7:bar.txteed6:lengthi425e4:pathl3:foo7:var.txteee4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡e8:url-listl33:http://test.com/test file/foo baree libtorrent-rasterbar-1.1.13/test/test_torrents/whitespace_url.torrent000066400000000000000000000002651351156116000263010ustar00rootroot00000000000000d8:announce25: udp://test.com/announce10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi16384e6:pieces20:‚žĽŚ&ľÇJW›}ÜA4u,·Ľ‘‡ee libtorrent-rasterbar-1.1.13/test/test_tracker.cpp000066400000000000000000000545651351156116000221370ustar00rootroot00000000000000/* Copyright (c) 2010, 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 "test.hpp" #include "setup_transfer.hpp" #include "udp_tracker.hpp" #include "settings.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/peer_info.hpp" // for peer_list_entry #include "libtorrent/broadcast_socket.hpp" // for supports_ipv6 #include "libtorrent/alert_types.hpp" #include "libtorrent/session.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/tracker_manager.hpp" #include "libtorrent/http_tracker_connection.hpp" // for parse_tracker_response #include "libtorrent/torrent_info.hpp" #include "libtorrent/announce_entry.hpp" #include #include using namespace libtorrent; namespace lt = libtorrent; // TODO: test scrape requests // TODO: test parse peers6 // TODO: test parse tracker-id // TODO: test parse failure-reason // TODO: test all failure paths, including // invalid bencoding // not a dictionary // no files entry in scrape response // no info-hash entry in scrape response // malformed peers in peer list of dictionaries // uneven number of bytes in peers and peers6 string responses TORRENT_TEST(parse_hostname_peers) { char const response[] = "d5:peersld7:peer id20:aaaaaaaaaaaaaaaaaaaa2:ip13:test_hostname4:porti1000eed7:peer id20:bbbbabaababababababa2:ip12:another_host4:porti1001eeee"; error_code ec; tracker_response resp = parse_tracker_response(response, sizeof(response) - 1 , ec, false, sha1_hash()); TEST_EQUAL(ec, error_code()); TEST_EQUAL(resp.peers.size(), 2); if (resp.peers.size() == 2) { peer_entry const& e0 = resp.peers[0]; peer_entry const& e1 = resp.peers[1]; TEST_EQUAL(e0.hostname, "test_hostname"); TEST_EQUAL(e0.port, 1000); TEST_EQUAL(e0.pid, peer_id("aaaaaaaaaaaaaaaaaaaa")); TEST_EQUAL(e1.hostname, "another_host"); TEST_EQUAL(e1.port, 1001); TEST_EQUAL(e1.pid, peer_id("bbbbabaababababababa")); } } TORRENT_TEST(parse_peers4) { char const response[] = "d5:peers12:\x01\x02\x03\x04\x30\x10" "\x09\x08\x07\x06\x20\x10" "e"; error_code ec; tracker_response resp = parse_tracker_response(response, sizeof(response) - 1 , ec, false, sha1_hash()); TEST_EQUAL(ec, error_code()); TEST_EQUAL(resp.peers4.size(), 2); if (resp.peers.size() == 2) { ipv4_peer_entry const& e0 = resp.peers4[0]; ipv4_peer_entry const& e1 = resp.peers4[1]; TEST_CHECK(e0.ip == address_v4::from_string("1.2.3.4").to_bytes()); TEST_EQUAL(e0.port, 0x3010); TEST_CHECK(e1.ip == address_v4::from_string("9.8.7.6").to_bytes()); TEST_EQUAL(e1.port, 0x2010); } } TORRENT_TEST(parse_i2p_peers) { // d8:completei8e10:incompletei4e8:intervali3600e5:peers352: ... boost::uint8_t const response[] = { 0x64, 0x38, 0x3a, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x69, 0x38, 0x65, 0x31, 0x30, 0x3a, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x69, 0x34, 0x65, 0x38, 0x3a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x69, 0x33, 0x36, 0x30, 0x30, 0x65, 0x35, 0x3a, 0x70, 0x65, 0x65, 0x72, 0x73, 0x33, 0x35, 0x32, 0x3a, 0xb1, 0x84, 0xe0, 0x96, 0x1f, 0xdb, 0xf2, 0xc9, 0xb0, 0x53, 0x9a, 0x31, 0xa5, 0x35, 0xcd, 0xe8, 0x59, 0xa0, 0x7c, 0xcd, 0xf2, 0x7c, 0x81, 0x81, 0x02, 0x11, 0x7b, 0xb4, 0x2a, 0xd1, 0x20, 0x87, 0xd6, 0x1b, 0x06, 0x4c, 0xbb, 0x4c, 0x4e, 0x30, 0xf9, 0xa3, 0x5d, 0x58, 0xa0, 0xa5, 0x10, 0x48, 0xfa, 0x9b, 0x3b, 0x10, 0x86, 0x43, 0x5c, 0x2e, 0xa2, 0xa6, 0x22, 0x31, 0xd0, 0x63, 0x6a, 0xfb, 0x4f, 0x25, 0x5b, 0xe2, 0x29, 0xbc, 0xcc, 0xa0, 0x1a, 0x0a, 0x30, 0x45, 0x32, 0xa1, 0xc8, 0x49, 0xf7, 0x9e, 0x03, 0xfd, 0x34, 0x80, 0x9a, 0x5b, 0xe9, 0x78, 0x04, 0x48, 0x4e, 0xbd, 0xc0, 0x5c, 0xdd, 0x4f, 0xf8, 0xbd, 0xc8, 0x4c, 0x4b, 0xcc, 0xf6, 0x25, 0x1b, 0xb3, 0x4d, 0xc0, 0x91, 0xb1, 0x4b, 0xb6, 0xbd, 0x95, 0xb7, 0x8e, 0x88, 0x79, 0xa8, 0xaa, 0x83, 0xa5, 0x7e, 0xec, 0x17, 0x60, 0x8d, 0x1d, 0xe2, 0xbe, 0x16, 0x35, 0x83, 0x25, 0xee, 0xe4, 0xd5, 0xbe, 0x54, 0x7b, 0xc8, 0x00, 0xdc, 0x5d, 0x56, 0xc7, 0x29, 0xd2, 0x1e, 0x6d, 0x7a, 0xfb, 0xfc, 0xef, 0x36, 0x05, 0x8a, 0xd0, 0xa7, 0x05, 0x4c, 0x11, 0xd5, 0x50, 0xe6, 0x2d, 0x7b, 0xe0, 0x7d, 0x84, 0xda, 0x47, 0x48, 0x9d, 0xf9, 0x77, 0xa2, 0xc7, 0x78, 0x90, 0xa4, 0xb5, 0x05, 0xf4, 0x95, 0xea, 0x36, 0x7b, 0x92, 0x8c, 0x5b, 0xf7, 0x8b, 0x18, 0x94, 0x2c, 0x2f, 0x88, 0xcf, 0xf8, 0xec, 0x5c, 0x52, 0xa8, 0x98, 0x8f, 0xd1, 0xd3, 0xf0, 0xd8, 0x63, 0x19, 0x73, 0x33, 0xd7, 0xeb, 0x1f, 0x87, 0x1c, 0x9f, 0x5b, 0xce, 0xe4, 0xd0, 0x15, 0x4e, 0x38, 0xb7, 0xe3, 0xbd, 0x93, 0x64, 0xe2, 0x15, 0x3d, 0xfc, 0x56, 0x4f, 0xd4, 0x19, 0x62, 0xe0, 0xb7, 0x59, 0x24, 0xff, 0x7f, 0x32, 0xdf, 0x56, 0xa5, 0x62, 0x42, 0x87, 0xa3, 0x04, 0xec, 0x09, 0x0a, 0x5b, 0x90, 0x48, 0x57, 0xc3, 0x32, 0x5f, 0x87, 0xeb, 0xfb, 0x08, 0x69, 0x6f, 0xa9, 0x46, 0x46, 0xa9, 0x54, 0x67, 0xec, 0x7b, 0x15, 0xc9, 0x68, 0x6b, 0x01, 0xb8, 0x10, 0x59, 0x53, 0x9c, 0xe6, 0x1b, 0x2e, 0x70, 0x72, 0x6e, 0x82, 0x7b, 0x03, 0xbc, 0xf2, 0x26, 0x9b, 0xb3, 0x91, 0xaa, 0xf1, 0xba, 0x62, 0x12, 0xbb, 0x74, 0x4b, 0x70, 0x44, 0x74, 0x19, 0xb2, 0xa1, 0x68, 0xd2, 0x30, 0xd6, 0xa5, 0x1b, 0xd9, 0xea, 0x4d, 0xdb, 0x81, 0x8e, 0x66, 0xbf, 0x4d, 0x6c, 0x32, 0x66, 0xc2, 0x8a, 0x22, 0x6b, 0x47, 0xc1, 0xd1, 0x52, 0x61, 0x66, 0xa0, 0x75, 0xab, 0x65 }; error_code ec; tracker_response resp = parse_tracker_response( reinterpret_cast(response), sizeof(response) , ec, tracker_request::i2p, sha1_hash()); TEST_EQUAL(ec, error_code()); TEST_EQUAL(resp.peers.size(), 11); if (resp.peers.size() == 11) { TEST_EQUAL(resp.peers[0].hostname, "wgcobfq73pzmtmcttiy2knon5bm2a7gn6j6idaiccf53ikwrecdq.b32.i2p"); TEST_EQUAL(resp.peers[10].hostname, "ufunemgwuun5t2sn3oay4zv7jvwdezwcrirgwr6b2fjgczvaowvq.b32.i2p"); } } TORRENT_TEST(parse_interval) { char const response[] = "d8:intervali1042e12:min intervali10e5:peers0:e"; error_code ec; tracker_response resp = parse_tracker_response(response, sizeof(response) - 1 , ec, false, sha1_hash()); TEST_EQUAL(ec, error_code()); TEST_EQUAL(resp.peers.size(), 0); TEST_EQUAL(resp.peers4.size(), 0); TEST_EQUAL(resp.interval, 1042); TEST_EQUAL(resp.min_interval, 10); } TORRENT_TEST(parse_warning) { char const response[] = "d5:peers0:15:warning message12:test messagee"; error_code ec; tracker_response resp = parse_tracker_response(response, sizeof(response) - 1 , ec, false, sha1_hash()); TEST_EQUAL(ec, error_code()); TEST_EQUAL(resp.peers.size(), 0); TEST_EQUAL(resp.warning_message, "test message"); } TORRENT_TEST(parse_failure_reason) { char const response[] = "d5:peers0:14:failure reason12:test messagee"; error_code ec; tracker_response resp = parse_tracker_response(response, sizeof(response) - 1 , ec, false, sha1_hash()); TEST_EQUAL(ec, errors::tracker_failure); TEST_EQUAL(resp.peers.size(), 0); TEST_EQUAL(resp.failure_reason, "test message"); } TORRENT_TEST(parse_scrape_response) { char const response[] = "d5:filesd20:aaaaaaaaaaaaaaaaaaaad8:completei1e10:incompletei2e10:downloadedi3e11:downloadersi6eeee"; error_code ec; tracker_response resp = parse_tracker_response(response, sizeof(response) - 1 , ec, true, sha1_hash("aaaaaaaaaaaaaaaaaaaa")); TEST_EQUAL(ec, error_code()); TEST_EQUAL(resp.complete, 1); TEST_EQUAL(resp.incomplete, 2); TEST_EQUAL(resp.downloaded, 3); TEST_EQUAL(resp.downloaders, 6); } TORRENT_TEST(parse_scrape_response_with_zero) { char const response[] = "d5:filesd20:aaa\0aaaaaaaaaaaaaaaad8:completei4e10:incompletei5e10:downloadedi6eeee"; error_code ec; tracker_response resp = parse_tracker_response(response, sizeof(response) - 1 , ec, true, sha1_hash("aaa\0aaaaaaaaaaaaaaaa")); TEST_EQUAL(ec, error_code()); TEST_EQUAL(resp.complete, 4); TEST_EQUAL(resp.incomplete, 5); TEST_EQUAL(resp.downloaded, 6); TEST_EQUAL(resp.downloaders, -1); } TORRENT_TEST(parse_external_ip) { char const response[] = "d5:peers0:11:external ip4:\x01\x02\x03\x04" "e"; error_code ec; tracker_response resp = parse_tracker_response(response, sizeof(response) - 1 , ec, false, sha1_hash()); TEST_EQUAL(ec, error_code()); TEST_EQUAL(resp.peers.size(), 0); TEST_EQUAL(resp.external_ip, address_v4::from_string("1.2.3.4")); } #if TORRENT_USE_IPV6 TORRENT_TEST(parse_external_ip6) { char const response[] = "d5:peers0:11:external ip16:\xf1\x02\x03\x04\0\0\0\0\0\0\0\0\0\0\xff\xff" "e"; error_code ec; tracker_response resp = parse_tracker_response(response, sizeof(response) - 1 , ec, false, sha1_hash()); TEST_EQUAL(ec, error_code()); TEST_EQUAL(resp.peers.size(), 0); TEST_EQUAL(resp.external_ip, address_v6::from_string("f102:0304::ffff")); } #endif peer_entry extract_peer(char const* peer_field, error_code expected_ec, bool expected_ret) { error_code ec; peer_entry result; bdecode_node n; bdecode(peer_field, peer_field + strlen(peer_field) , n, ec, NULL, 1000, 1000); TEST_CHECK(!ec); bool ret = extract_peer_info(n, result, ec); TEST_EQUAL(expected_ret, ret); TEST_EQUAL(expected_ec, ec); return result; } TORRENT_TEST(extract_peer) { peer_entry result = extract_peer("d7:peer id20:abababababababababab2:ip4:abcd4:porti1337ee" , error_code(), true); TEST_EQUAL(result.hostname, "abcd"); TEST_EQUAL(result.pid, peer_id("abababababababababab")); TEST_EQUAL(result.port, 1337); } TORRENT_TEST(extract_peer_hostname) { peer_entry result = extract_peer("d2:ip11:example.com4:porti1ee" , error_code(), true); TEST_EQUAL(result.hostname, "example.com"); TEST_EQUAL(result.pid, (peer_id::min)()); TEST_EQUAL(result.port, 1); } TORRENT_TEST(extract_peer_not_a_dictionary) { // not a dictionary peer_entry result = extract_peer("2:ip11:example.com" , errors::invalid_peer_dict, false); } TORRENT_TEST(extract_peer_missing_ip) { // missing IP peer_entry result = extract_peer("d7:peer id20:abababababababababab4:porti1337ee" , errors::invalid_tracker_response, false); } TORRENT_TEST(extract_peer_missing_port) { // missing port peer_entry result = extract_peer("d7:peer id20:abababababababababab2:ip4:abcde" , errors::invalid_tracker_response, false); } bool connect_alert(libtorrent::alert const* a, tcp::endpoint& ep) { if (peer_connect_alert const* pc = alert_cast(a)) { ep = pc->ip; return true; } return false; } void test_udp_tracker(std::string const& iface, address tracker, tcp::endpoint const& expected_peer) { int const udp_port = start_udp_tracker(tracker); int prev_udp_announces = num_udp_announces(); settings_pack pack = settings(); pack.set_bool(settings_pack::announce_to_all_trackers, true); pack.set_bool(settings_pack::announce_to_all_tiers, true); pack.set_str(settings_pack::listen_interfaces, iface + ":48875"); boost::scoped_ptr s(new lt::session(pack)); error_code ec; remove_all("tmp1_tracker", ec); create_directory("tmp1_tracker", ec); std::ofstream file(combine_path("tmp1_tracker", "temporary").c_str()); boost::shared_ptr t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false); file.close(); char tracker_url[200]; snprintf(tracker_url, sizeof(tracker_url), "udp://%s:%d/announce", iface.c_str(), udp_port); t->add_tracker(tracker_url, 0); add_torrent_params addp; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; addp.flags |= add_torrent_params::flag_seed_mode; addp.ti = t; addp.save_path = "tmp1_tracker"; torrent_handle h = s->add_torrent(addp); tcp::endpoint peer_ep; for (int i = 0; i < 50; ++i) { print_alerts(*s, "s", false, false, false, boost::bind(&connect_alert, _1, boost::ref(peer_ep))); if (num_udp_announces() == prev_udp_announces + 1) break; test_sleep(100); } // we should have announced to the tracker by now TEST_EQUAL(num_udp_announces(), prev_udp_announces + 1); // if we remove the torrent before it has received the response from the // tracker, it won't announce again to stop. So, wait a bit before removing. test_sleep(1000); s->remove_torrent(h); for (int i = 0; i < 50; ++i) { print_alerts(*s, "s", true, true, false, boost::bind(&connect_alert, _1, boost::ref(peer_ep))); if (num_udp_announces() == prev_udp_announces + 2) break; test_sleep(100); } TEST_CHECK(peer_ep == expected_peer); fprintf(stderr, "destructing session\n"); s.reset(); fprintf(stderr, "done\n"); // we should have announced the stopped event now TEST_EQUAL(num_udp_announces(), prev_udp_announces + 2); } TORRENT_TEST(udp_tracker_v4) { test_udp_tracker("127.0.0.1", address_v4::any(), ep("1.3.3.7", 1337)); } #if TORRENT_USE_IPV6 TORRENT_TEST(udp_tracker_v6) { if (supports_ipv6()) { test_udp_tracker("[::1]", address_v6::any(), ep("::1.3.3.7", 1337)); } } #endif TORRENT_TEST(http_peers) { int http_port = start_web_server(); settings_pack pack = settings(); pack.set_bool(settings_pack::announce_to_all_trackers, true); pack.set_bool(settings_pack::announce_to_all_tiers, false); pack.set_int(settings_pack::tracker_completion_timeout, 2); pack.set_int(settings_pack::tracker_receive_timeout, 1); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:39775"); boost::scoped_ptr s(new lt::session(pack)); error_code ec; remove_all("tmp2_tracker", ec); create_directory("tmp2_tracker", ec); std::ofstream file(combine_path("tmp2_tracker", "temporary").c_str()); boost::shared_ptr t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false); file.close(); char tracker_url[200]; // and this should not be announced to (since the one before it succeeded) snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce" , http_port); t->add_tracker(tracker_url, 0); add_torrent_params addp; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; addp.flags |= add_torrent_params::flag_seed_mode; addp.ti = t; addp.save_path = "tmp2_tracker"; torrent_handle h = s->add_torrent(addp); libtorrent::torrent_status status = h.status(); TEST_CHECK(status.current_tracker.empty()); // wait to hit the tracker wait_for_alert(*s, tracker_reply_alert::alert_type, "s"); status = h.status(); TEST_CHECK(!status.current_tracker.empty()); TEST_CHECK(status.current_tracker == tracker_url); // we expect to have certain peers in our peer list now // these peers are hard coded in web_server.py std::vector peers; h.get_full_peer_list(peers); std::set expected_peers; expected_peers.insert(tcp::endpoint(address_v4::from_string("65.65.65.65"), 16962)); expected_peers.insert(tcp::endpoint(address_v4::from_string("67.67.67.67"), 17476)); #if TORRENT_USE_IPV6 expected_peers.insert(tcp::endpoint(address_v6::from_string("4545:4545:4545:4545:4545:4545:4545:4545"), 17990)); #endif TEST_EQUAL(peers.size(), expected_peers.size()); for (std::vector::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { TEST_EQUAL(expected_peers.count(i->ip), 1); } fprintf(stderr, "destructing session\n"); s.reset(); fprintf(stderr, "done\n"); fprintf(stderr, "stop_web_server\n"); stop_web_server(); fprintf(stderr, "done\n"); } TORRENT_TEST(current_tracker) { // use a invalid tracker port int http_port = 39527; settings_pack pack = settings(); pack.set_bool(settings_pack::announce_to_all_trackers, true); pack.set_bool(settings_pack::announce_to_all_tiers, false); pack.set_int(settings_pack::tracker_completion_timeout, 2); pack.set_int(settings_pack::tracker_receive_timeout, 1); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:39775"); boost::scoped_ptr s(new lt::session(pack)); error_code ec; remove_all("tmp3_tracker", ec); create_directory("tmp3_tracker", ec); std::ofstream file(combine_path("tmp3_tracker", "temporary").c_str()); boost::shared_ptr t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false); file.close(); char tracker_url[200]; snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce" , http_port); t->add_tracker(tracker_url, 0); add_torrent_params addp; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; addp.flags |= add_torrent_params::flag_seed_mode; addp.ti = t; addp.save_path = "tmp3_tracker"; torrent_handle h = s->add_torrent(addp); libtorrent::torrent_status status = h.status(); TEST_CHECK(status.current_tracker.empty()); // wait to hit the tracker announce wait_for_alert(*s, tracker_announce_alert::alert_type, "s"); status = h.status(); TEST_CHECK(status.current_tracker.empty()); // wait to hit the tracker error wait_for_alert(*s, tracker_error_alert::alert_type, "s"); status = h.status(); TEST_CHECK(status.current_tracker.empty()); fprintf(stderr, "destructing session\n"); s.reset(); fprintf(stderr, "done\n"); } void test_proxy(bool proxy_trackers) { int http_port = start_web_server(); settings_pack pack = settings(); pack.set_bool(settings_pack::announce_to_all_trackers, true); pack.set_bool(settings_pack::announce_to_all_tiers, false); pack.set_int(settings_pack::tracker_completion_timeout, 2); pack.set_int(settings_pack::tracker_receive_timeout, 1); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:39775"); pack.set_bool(settings_pack::force_proxy, true); pack.set_str(settings_pack::proxy_hostname, "non-existing.com"); pack.set_int(settings_pack::proxy_type, settings_pack::socks5); pack.set_int(settings_pack::proxy_port, 4444); pack.set_bool(settings_pack::proxy_tracker_connections, proxy_trackers); boost::scoped_ptr s(new lt::session(pack)); error_code ec; remove_all("tmp2_tracker", ec); create_directory("tmp2_tracker", ec); std::ofstream file(combine_path("tmp2_tracker", "temporary").c_str()); boost::shared_ptr t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false); file.close(); char tracker_url[200]; // and this should not be announced to (since the one before it succeeded) snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce" , http_port); t->add_tracker(tracker_url, 0); add_torrent_params addp; addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; addp.flags |= add_torrent_params::flag_seed_mode; addp.ti = t; addp.save_path = "tmp2_tracker"; torrent_handle h = s->add_torrent(addp); // wait to hit the tracker const alert* a = wait_for_alert(*s, tracker_reply_alert::alert_type, "s"); if (proxy_trackers) { TEST_CHECK(a == NULL); } else { TEST_CHECK(a != NULL); } fprintf(stderr, "destructing session\n"); s.reset(); fprintf(stderr, "done\n"); fprintf(stderr, "stop_web_server\n"); stop_web_server(); fprintf(stderr, "done\n"); } TORRENT_TEST(tracker_proxy) { fprintf(stderr, "\n\nnot proxying tracker connections (expect to reach the tracker)\n\n"); test_proxy(false); fprintf(stderr, "\n\nproxying tracker connections through non-existent proxy (do not expect to reach the tracker)\n\n"); test_proxy(true); } #ifndef TORRENT_DISABLE_LOGGING int count_stopped_events(session& ses, int expected = 0) { int count = 0; int num = 70; // this number is adjusted per version, an estimate time_point const end_time = clock_type::now() + seconds(15); while (true) { time_point const now = clock_type::now(); if (now > end_time) return count; ses.wait_for_alert(end_time - now); std::vector alerts; ses.pop_alerts(&alerts); for (int i = 0; i < int(alerts.size()); ++i) { alert* a = alerts[i]; std::printf("%d: [%s] %s\n", num, a->what(), a->message().c_str()); if (a->type() == log_alert::alert_type) { std::string const msg = a->message(); if (msg.find("&event=stopped") != std::string::npos) { count++; --expected; } } num--; } if (num <= 0 && expected <= 0) return count; } return count; }; void test_stop_tracker_timeout(int const timeout) { // trick the min interval so that the stopped anounce is permitted immediately // after the initial announce int port = start_web_server(false, false, true); settings_pack p = settings(); p.set_bool(settings_pack::announce_to_all_trackers, true); p.set_bool(settings_pack::announce_to_all_tiers, true); p.set_str(settings_pack::listen_interfaces, "0.0.0.0:6881"); p.set_int(settings_pack::stop_tracker_timeout, timeout); lt::session s(p); error_code ec; remove_all("tmp4_tracker", ec); create_directory("tmp4_tracker", ec); std::ofstream file(combine_path("tmp4_tracker", "temporary").c_str()); boost::shared_ptr t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false); file.close(); add_torrent_params tp; tp.flags &= ~add_torrent_params::flag_paused; tp.flags &= ~add_torrent_params::flag_auto_managed; tp.flags |= add_torrent_params::flag_seed_mode; tp.ti = t; tp.save_path = "tmp4_tracker"; torrent_handle h = s.add_torrent(tp); char tracker_url[200]; snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", port); announce_entry ae(tracker_url); h.add_tracker(ae); // make sure it announced a event=started properly wait_for_alert(s, tracker_reply_alert::alert_type, "s"); s.remove_torrent(h); int const count = count_stopped_events(s, 1); TEST_EQUAL(count, 1); } TORRENT_TEST(stop_tracker_timeout) { std::printf("\n\nexpect to get ONE request with &event=stopped\n\n"); test_stop_tracker_timeout(1); } #endif libtorrent-rasterbar-1.1.13/test/test_transfer.cpp000066400000000000000000000330001351156116000223050ustar00rootroot00000000000000/* Copyright (c) 2008, 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/session.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/time.hpp" #include "libtorrent/file.hpp" #include "libtorrent/torrent_info.hpp" #include "test.hpp" #include "setup_transfer.hpp" #include "settings.hpp" #include "test_utils.hpp" #include #include #include #include using namespace libtorrent; namespace lt = libtorrent; using boost::tuples::ignore; int peer_disconnects = 0; bool on_alert(alert const* a) { if (alert_cast(a)) ++peer_disconnects; else if (alert_cast(a)) ++peer_disconnects; return false; } // simulate a full disk struct test_storage : default_storage { test_storage(storage_params const& params) : default_storage(params) , m_written(0) , m_limit(16 * 1024 * 2) {} virtual void set_file_priority(std::vector& p , storage_error& ec) TORRENT_OVERRIDE {} void set_limit(int lim) { mutex::scoped_lock l(m_mutex); m_limit = lim; } int writev( file::iovec_t const* bufs , int num_bufs , int piece_index , int offset , int flags , storage_error& se) TORRENT_OVERRIDE { mutex::scoped_lock l(m_mutex); if (m_written >= m_limit) { std::cerr << "storage written: " << m_written << " limit: " << m_limit << std::endl; error_code ec; ec = error_code(boost::system::errc::no_space_on_device, generic_category()); se.ec = ec; return 0; } for (int i = 0; i < num_bufs; ++i) m_written += bufs[i].iov_len; l.unlock(); return default_storage::writev(bufs, num_bufs, piece_index, offset, flags, se); } virtual ~test_storage() {} int m_written; int m_limit; mutex m_mutex; }; storage_interface* test_storage_constructor(storage_params const& params) { return new test_storage(params); } void test_transfer(int proxy_type, settings_pack const& sett , bool test_disk_full = false , storage_mode_t storage_mode = storage_mode_sparse) { char const* test_name[] = {"no", "SOCKS4", "SOCKS5", "SOCKS5 password", "HTTP", "HTTP password"}; fprintf(stderr, "\n\n ==== TESTING %s proxy ==== disk-full: %s\n\n\n" , test_name[proxy_type], test_disk_full ? "true": "false"); // in case the previous run was terminated error_code ec; remove_all("tmp1_transfer", ec); remove_all("tmp2_transfer", ec); remove_all("tmp1_transfer_moved", ec); remove_all("tmp2_transfer_moved", ec); // these are declared before the session objects // so that they are destructed last. This enables // the sessions to destruct in parallel session_proxy p1; session_proxy p2; settings_pack pack = settings(); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_dht, false); #ifndef TORRENT_NO_DEPRECATE pack.set_bool(settings_pack::rate_limit_utp, true); #endif lt::session ses1(pack); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:49075"); lt::session ses2(pack); int proxy_port = 0; if (proxy_type) { proxy_port = start_proxy(proxy_type); settings_pack pack; pack.set_str(settings_pack::proxy_username, "testuser"); pack.set_str(settings_pack::proxy_password, "testpass"); pack.set_int(settings_pack::proxy_type, proxy_type); pack.set_int(settings_pack::proxy_port, proxy_port); pack.set_bool(settings_pack::force_proxy, true); // test resetting the proxy in quick succession. // specifically the udp_socket connecting to a new // socks5 proxy while having one connection attempt // in progress. pack.set_str(settings_pack::proxy_hostname, "5.6.7.8"); ses1.apply_settings(pack); pack.set_str(settings_pack::proxy_hostname, "127.0.0.1"); ses1.apply_settings(pack); } pack = sett; // we need a short reconnect time since we // finish the torrent and then restart it // immediately to complete the second half. // using a reconnect time > 0 will just add // to the time it will take to complete the test pack.set_int(settings_pack::min_reconnect_time, 0); pack.set_int(settings_pack::stop_tracker_timeout, 1); pack.set_bool(settings_pack::announce_to_all_trackers, true); pack.set_bool(settings_pack::announce_to_all_tiers, true); // make sure we announce to both http and udp trackers pack.set_bool(settings_pack::prefer_udp_trackers, false); pack.set_bool(settings_pack::enable_outgoing_utp, false); pack.set_bool(settings_pack::enable_incoming_utp, false); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_dht, false); pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled); pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled); pack.set_bool(settings_pack::allow_multiple_connections_per_ip, false); // TODO: these settings_pack tests belong in their own test pack.set_int(settings_pack::unchoke_slots_limit, 0); ses1.apply_settings(pack); TEST_CHECK(ses1.get_settings().get_int(settings_pack::unchoke_slots_limit) == 0); pack.set_int(settings_pack::unchoke_slots_limit, -1); ses1.apply_settings(pack); TEST_CHECK(ses1.get_settings().get_int(settings_pack::unchoke_slots_limit) == -1); pack.set_int(settings_pack::unchoke_slots_limit, 8); ses1.apply_settings(pack); TEST_CHECK(ses1.get_settings().get_int(settings_pack::unchoke_slots_limit) == 8); ses2.apply_settings(pack); torrent_handle tor1; torrent_handle tor2; create_directory("tmp1_transfer", ec); std::ofstream file("tmp1_transfer/temporary"); boost::shared_ptr t = ::create_torrent(&file, "temporary", 32 * 1024, 13, false); file.close(); TEST_CHECK(exists(combine_path("tmp1_transfer", "temporary"))); add_torrent_params addp(&test_storage_constructor); addp.flags &= ~add_torrent_params::flag_paused; addp.flags &= ~add_torrent_params::flag_auto_managed; add_torrent_params params; params.storage_mode = storage_mode; params.flags &= ~add_torrent_params::flag_paused; params.flags &= ~add_torrent_params::flag_auto_managed; wait_for_listen(ses1, "ses1"); wait_for_listen(ses2, "ses2"); peer_disconnects = 0; // test using piece sizes smaller than 16kB boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 , true, false, true, "_transfer", 8 * 1024, &t, false, test_disk_full?&addp:¶ms); int num_pieces = tor2.torrent_file()->num_pieces(); std::vector priorities(num_pieces, 1); // also test to move the storage of the downloader and the uploader // to make sure it can handle switching paths bool test_move_storage = false; int upload_mode_timer = 0; wait_for_downloading(ses2, "ses2"); for (int i = 0; i < 200; ++i) { torrent_status st1 = tor1.status(); torrent_status st2 = tor2.status(); print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); if (i % 10 == 0) { print_ses_rate(i / 10.f, &st1, &st2); } if (!test_move_storage && st2.progress > 0.25f) { test_move_storage = true; tor1.move_storage("tmp1_transfer_moved"); tor2.move_storage("tmp2_transfer_moved"); std::cerr << "moving storage" << std::endl; } // wait 10 loops before we restart the torrent. This lets // us catch all events that failed (and would put the torrent // back into upload mode) before we restart it. // TODO: factor out the disk-full test into its own unit test if (test_disk_full && st2.upload_mode && ++upload_mode_timer > 10) { test_disk_full = false; ((test_storage*)tor2.get_storage_impl())->set_limit(16 * 1024 * 1024); // if we reset the upload mode too soon, there may be more disk // jobs failing right after, putting us back in upload mode. So, // give the disk some time to fail all disk jobs before resetting // upload mode to false test_sleep(500); // then we need to drain the alert queue, so the peer_disconnects // counter doesn't get incremented by old alerts print_alerts(ses1, "ses1", true, true, true, &on_alert); print_alerts(ses2, "ses2", true, true, true, &on_alert); lt::error_code err = tor2.status().errc; fprintf(stderr, "error: \"%s\"\n", err.message().c_str()); TEST_CHECK(!err); tor2.set_upload_mode(false); // at this point we probably disconnected the seed // so we need to reconnect as well fprintf(stderr, "%s: reconnecting peer\n", time_now_string()); error_code ec; tor2.connect_peer(tcp::endpoint(address::from_string("127.0.0.1", ec) , ses1.listen_port())); TEST_CHECK(tor2.status().is_finished == false); fprintf(stderr, "disconnects: %d\n", peer_disconnects); TEST_CHECK(peer_disconnects >= 2); fprintf(stderr, "%s: discovered disk full mode. Raise limit and disable upload-mode\n", time_now_string()); peer_disconnects = 0; continue; } if (!test_disk_full && st2.is_seeding) break; TEST_CHECK(st1.state == torrent_status::seeding || st1.state == torrent_status::checking_files); TEST_CHECK(st2.state == torrent_status::downloading || st2.state == torrent_status::checking_resume_data || (test_disk_full && st2.errc)); if (!test_disk_full && peer_disconnects >= 2) break; // if nothing is being transferred after 2 seconds, we're failing the test // if (!test_disk_full && st1.upload_payload_rate == 0 && i > 20) break; test_sleep(100); } TEST_CHECK(tor2.status().is_seeding); // this allows shutting down the sessions in parallel p1 = ses1.abort(); p2 = ses2.abort(); if (proxy_type) stop_proxy(proxy_port); } void cleanup() { error_code ec; remove_all("tmp1_transfer", ec); remove_all("tmp2_transfer", ec); remove_all("tmp1_transfer_moved", ec); remove_all("tmp2_transfer_moved", ec); } TORRENT_TEST(no_contiguous_buffers) { using namespace libtorrent; // test no contiguous_recv_buffers settings_pack p; p.set_bool(settings_pack::contiguous_recv_buffer, false); test_transfer(0, p); cleanup(); } // test with all kinds of proxies TORRENT_TEST(socks5_pw) { using namespace libtorrent; test_transfer(settings_pack::socks5_pw, settings_pack()); cleanup(); } TORRENT_TEST(http) { using namespace libtorrent; test_transfer(settings_pack::http, settings_pack()); cleanup(); } TORRENT_TEST(http_pw) { using namespace libtorrent; test_transfer(settings_pack::http_pw, settings_pack()); cleanup(); } /* TORRENT_TEST(i2p) { using namespace libtorrent; test_transfer(settings_pack::i2p_proxy, settings_pack()); cleanup(); } // this test is too flaky. Move it to a sim TORRENT_TEST(disk_full) { using namespace libtorrent; // test with a (simulated) full disk test_transfer(0, settings_pack(), true); cleanup(); } */ TORRENT_TEST(allow_fast) { using namespace libtorrent; // test allowed fast settings_pack p; p.set_int(settings_pack::allowed_fast_set_size, 2000); test_transfer(0, p, false); cleanup(); } TORRENT_TEST(coalesce_reads) { using namespace libtorrent; // test allowed fast settings_pack p; p.set_int(settings_pack::read_cache_line_size, 16); p.set_bool(settings_pack::coalesce_reads, true); test_transfer(0, p, false); cleanup(); } TORRENT_TEST(coalesce_writes) { using namespace libtorrent; // test allowed fast settings_pack p; p.set_bool(settings_pack::coalesce_writes, true); test_transfer(0, p, false); cleanup(); } TORRENT_TEST(no_coalesce_reads) { using namespace libtorrent; // test allowed fast settings_pack p; p.set_int(settings_pack::read_cache_line_size, 16); p.set_bool(settings_pack::coalesce_reads, false); test_transfer(0, p, false); cleanup(); } TORRENT_TEST(no_coalesce_writes) { using namespace libtorrent; // test allowed fast settings_pack p; p.set_bool(settings_pack::coalesce_writes, false); test_transfer(0, p, false); cleanup(); } TORRENT_TEST(allocate) { using namespace libtorrent; // test storage_mode_allocate fprintf(stderr, "full allocation mode\n"); test_transfer(0, settings_pack(), false, storage_mode_allocate); cleanup(); } libtorrent-rasterbar-1.1.13/test/test_upnp.cpp000066400000000000000000000171111351156116000214500ustar00rootroot00000000000000/* Copyright (c) 2008, 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/upnp.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/socket_io.hpp" // print_endpoint #include "libtorrent/http_parser.hpp" #include "test.hpp" #include "setup_transfer.hpp" #include #include #include #include #include using namespace libtorrent; broadcast_socket* sock = 0; int g_port = 0; char const* soap_add_response[] = { "" "" "", "" "" ""}; char const* soap_delete_response[] = { "" "" "", "" "" ""}; void incoming_msearch(udp::endpoint const& from, char* buffer , int size) { http_parser p; bool error = false; p.incoming(buffer::const_interval(buffer, buffer + size), error); if (error || !p.header_finished()) { std::cerr << "*** malformed HTTP from " << print_endpoint(from) << std::endl; return; } if (p.method() != "m-search") return; std::cerr << "< incoming m-search from " << from << std::endl; char msg[] = "HTTP/1.1 200 OK\r\n" "ST:upnp:rootdevice\r\n" "USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice\r\n" "Location: http://127.0.0.1:%d/upnp.xml\r\n" "Server: Custom/1.0 UPnP/1.0 Proc/Ver\r\n" "EXT:\r\n" "Cache-Control:max-age=180\r\n" "DATE: Fri, 02 Jan 1970 08:10:38 GMT\r\n\r\n"; TORRENT_ASSERT(g_port != 0); char buf[sizeof(msg) + 30]; int len = snprintf(buf, sizeof(buf), msg, g_port); error_code ec; sock->send(buf, len, ec); if (ec) std::cerr << "*** error sending " << ec.message() << std::endl; } void log_callback(char const* err) { std::cerr << "UPnP: " << err << std::endl; //TODO: store the log and verify that some key messages are there } struct callback_info { int mapping; int port; error_code ec; bool operator==(callback_info const& e) { return mapping == e.mapping && port == e.port && !ec == !e.ec; } }; std::list callbacks; void callback(int mapping, address const& ip, int port, int protocol, error_code const& err) { callback_info info = {mapping, port, err}; callbacks.push_back(info); std::cerr << "mapping: " << mapping << ", port: " << port << ", IP: " << ip << ", proto: " << protocol << ", error: \"" << err.message() << "\"\n"; } void run_upnp_test(char const* root_filename, char const* router_model, char const* control_name, int igd_version) { libtorrent::io_service ios; g_port = start_web_server(); std::vector buf; error_code ec; load_file(root_filename, buf, ec); buf.push_back(0); FILE* xml_file = fopen("upnp.xml", "w+"); if (xml_file == NULL) { fprintf(stderr, "failed to open file 'upnp.xml': %s\n", strerror(errno)); TEST_CHECK(false); return; } fprintf(xml_file, &buf[0], g_port); fclose(xml_file); std::ofstream xml(control_name, std::ios::trunc); xml.write(soap_add_response[igd_version-1], sizeof(soap_add_response[igd_version-1])-1); xml.close(); sock = new broadcast_socket(udp::endpoint(address_v4::from_string("239.255.255.250") , 1900)); sock->open(&incoming_msearch, ios, ec); std::string user_agent = "test agent"; boost::shared_ptr upnp_handler = boost::make_shared(boost::ref(ios) , address_v4::from_string("127.0.0.1") , user_agent, &callback, &log_callback, false); upnp_handler->start(); upnp_handler->discover_device(); for (int i = 0; i < 20; ++i) { ios.reset(); ios.poll(ec); if (ec) { fprintf(stderr, "io_service::run(): %s\n", ec.message().c_str()); ec.clear(); break; } if (!upnp_handler->router_model().empty()) break; test_sleep(100); } std::cerr << "router: " << upnp_handler->router_model() << std::endl; TEST_CHECK(!upnp_handler->router_model().empty()); int mapping1 = upnp_handler->add_mapping(upnp::tcp, 500, ep("127.0.0.1", 500)); int mapping2 = upnp_handler->add_mapping(upnp::udp, 501, ep("127.0.0.1", 501)); for (int i = 0; i < 40; ++i) { ios.reset(); ios.poll(ec); if (ec) { fprintf(stderr, "io_service::run(): %s\n", ec.message().c_str()); ec.clear(); break; } if (callbacks.size() >= 2) break; test_sleep(100); } callback_info expected1 = {mapping1, 500, error_code()}; callback_info expected2 = {mapping2, 501, error_code()}; TEST_EQUAL(std::count(callbacks.begin(), callbacks.end(), expected1), 1); TEST_EQUAL(std::count(callbacks.begin(), callbacks.end(), expected2), 1); xml.open(control_name, std::ios::trunc); xml.write(soap_delete_response[igd_version-1], sizeof(soap_delete_response[igd_version-1])-1); xml.close(); upnp_handler->close(); sock->close(); for (int i = 0; i < 40; ++i) { ios.reset(); ios.poll(ec); if (ec) { fprintf(stderr, "io_service::run(): %s\n", ec.message().c_str()); ec.clear(); break; } if (callbacks.size() >= 4) break; test_sleep(100); } // there should have been two DeleteMapping calls TEST_EQUAL(callbacks.size(), 4); stop_web_server(); callbacks.clear(); delete sock; } TORRENT_TEST(upnp) { run_upnp_test(combine_path("..", "root1.xml").c_str(), "Xtreme N GIGABIT Router", "wipconn", 1); run_upnp_test(combine_path("..", "root2.xml").c_str(), "D-Link Router", "WANIPConnection", 1); run_upnp_test(combine_path("..", "root3.xml").c_str(), "D-Link Router", "WANIPConnection_2", 2); } libtorrent-rasterbar-1.1.13/test/test_url_seed.cpp000066400000000000000000000041271351156116000222730ustar00rootroot00000000000000/* Copyright (c) 2008-2014, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" using namespace libtorrent; const int proxy = libtorrent::settings_pack::none; #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(url_seed_ssl_keepalive) { run_http_suite(proxy, "https", 1, 0, 0, 1); } TORRENT_TEST(url_seed_ssl) { run_http_suite(proxy, "https", 1, 0, 0, 0); } #endif TORRENT_TEST(url_seed_keepalive) { run_http_suite(proxy, "http", 1, 0, 0, 1); } TORRENT_TEST(url_seed) { run_http_suite(proxy, "http", 1, 0, 0, 0); } TORRENT_TEST(url_seed_keepalive_rename) { run_http_suite(proxy, "http", 1, 0, 0, 1, 1); } libtorrent-rasterbar-1.1.13/test/test_utf8.cpp000066400000000000000000000215021351156116000213530ustar00rootroot00000000000000/* Copyright (c) 2014, 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 "test.hpp" #include "libtorrent/utf8.hpp" #include "libtorrent/ConvertUTF.h" #include "setup_transfer.hpp" // for load_file #include "libtorrent/file.hpp" // for combine_path #include using namespace libtorrent; void verify_transforms(char const* utf8_source, int utf8_source_len = -1) { if (utf8_source_len == -1) utf8_source_len = strlen(utf8_source); // utf8 -> utf16 -> utf32 -> utf8 { std::vector utf16(utf8_source_len); UTF8 const* in8 = (UTF8 const*)utf8_source; UTF16* out16 = &utf16[0]; ConversionResult ret = ConvertUTF8toUTF16(&in8, in8 + utf8_source_len , &out16, out16 + utf16.size(), strictConversion); TEST_EQUAL(ret, conversionOK); if (ret != conversionOK && utf8_source_len < 10) { for (char const* i = utf8_source; *i != 0; ++i) fprintf(stderr, "%x ", UTF8(*i)); } std::vector utf32(utf8_source_len); UTF16 const* in16 = &utf16[0]; UTF32* out32 = &utf32[0]; ret = ConvertUTF16toUTF32(&in16, out16 , &out32, out32 + utf32.size(), strictConversion); TEST_EQUAL(ret, conversionOK); if (ret != conversionOK && utf8_source_len < 10) { for (char const* i = utf8_source; *i != 0; ++i) fprintf(stderr, "%x ", UTF8(*i)); } std::vector utf8(utf8_source_len); UTF32 const* in32 = &utf32[0]; UTF8* out8 = &utf8[0]; ret = ConvertUTF32toUTF8(&in32, out32 , &out8, out8 + utf8.size(), strictConversion); TEST_EQUAL(ret, conversionOK); if (ret != conversionOK && utf8_source_len < 10) { for (char const* i = utf8_source; *i != 0; ++i) fprintf(stderr, "%x ", UTF8(*i)); } TEST_EQUAL(out8 - &utf8[0], utf8_source_len); TEST_CHECK(std::equal(&utf8[0], out8, (UTF8 const*)utf8_source)); } // utf8 -> utf32 -> utf16 -> utf8 { std::vector utf32(utf8_source_len); UTF8 const* in8 = (UTF8 const*)utf8_source; UTF32* out32 = &utf32[0]; ConversionResult ret = ConvertUTF8toUTF32(&in8, in8 + utf8_source_len , &out32, out32 + utf32.size(), strictConversion); TEST_EQUAL(ret, conversionOK); if (ret != conversionOK && utf8_source_len < 10) { for (char const* i = utf8_source; *i != 0; ++i) fprintf(stderr, "%x ", UTF8(*i)); } std::vector utf16(utf8_source_len); UTF32 const* in32 = &utf32[0]; UTF16* out16 = &utf16[0]; ret = ConvertUTF32toUTF16(&in32, out32 , &out16, out16 + utf16.size(), strictConversion); TEST_EQUAL(ret, conversionOK); if (ret != conversionOK && utf8_source_len < 10) { for (char const* i = utf8_source; *i != 0; ++i) fprintf(stderr, "%x ", UTF8(*i)); } std::vector utf8(utf8_source_len); UTF16 const* in16 = &utf16[0]; UTF8* out8 = &utf8[0]; ret = ConvertUTF16toUTF8(&in16, out16 , &out8, out8 + utf8.size(), strictConversion); TEST_EQUAL(ret, conversionOK); if (ret != conversionOK && utf8_source_len < 10) { for (char const* i = utf8_source; *i != 0; ++i) fprintf(stderr, "%x ", UTF8(*i)); } TEST_EQUAL(out8 - &utf8[0], utf8_source_len); TEST_CHECK(std::equal(&utf8[0], out8, (UTF8 const*)utf8_source)); } } void expect_error(char const* utf8, ConversionResult expect) { UTF8 const* in8 = (UTF8 const*)utf8; std::vector utf32(strlen(utf8)); UTF32* out32 = &utf32[0]; ConversionResult ret = ConvertUTF8toUTF32(&in8, in8 + strlen(utf8) , &out32, out32 + utf32.size(), strictConversion); TEST_EQUAL(ret, expect); if (ret != expect) { fprintf(stderr, "%d expected %d\n", ret, expect); for (char const* i = utf8; *i != 0; ++i) fprintf(stderr, "%x ", UTF8(*i)); } in8 = (UTF8 const*)utf8; std::vector utf16(strlen(utf8)); UTF16* out16 = &utf16[0]; ret = ConvertUTF8toUTF16(&in8, in8 + strlen(utf8) , &out16, out16 + utf16.size(), strictConversion); TEST_EQUAL(ret, expect); if (ret != expect) { fprintf(stderr, "%d expected %d\n", ret, expect); for (char const* i = utf8; *i != 0; ++i) fprintf(stderr, "%x ", UTF8(*i)); } } TORRENT_TEST(utf8) { std::vector utf8_source; error_code ec; load_file(combine_path("..", "utf8_test.txt"), utf8_source, ec, 1000000); if (ec) fprintf(stderr, "failed to open file: (%d) %s\n", ec.value() , ec.message().c_str()); TEST_CHECK(!ec); // test lower level conversions verify_transforms(&utf8_source[0], utf8_source.size()); verify_transforms("\xc3\xb0"); verify_transforms("\xed\x9f\xbf"); verify_transforms("\xee\x80\x80"); verify_transforms("\xef\xbf\xbd"); verify_transforms("\xf4\x8f\xbf\xbf"); verify_transforms("\xf0\x91\x80\x80\x30"); // Unexpected continuation bytes expect_error("\x80", sourceIllegal); expect_error("\xbf", sourceIllegal); // Impossible bytes // The following two bytes cannot appear in a correct UTF-8 string expect_error("\xff", sourceExhausted); expect_error("\xfe", sourceExhausted); expect_error("\xff\xff\xfe\xfe", sourceExhausted); // Examples of an overlong ASCII character expect_error("\xc0\xaf", sourceIllegal); expect_error("\xe0\x80\xaf", sourceIllegal); expect_error("\xf0\x80\x80\xaf", sourceIllegal); expect_error("\xf8\x80\x80\x80\xaf ", sourceIllegal); expect_error("\xfc\x80\x80\x80\x80\xaf", sourceIllegal); // Maximum overlong sequences expect_error("\xc1\xbf", sourceIllegal); expect_error("\xe0\x9f\xbf", sourceIllegal); expect_error("\xf0\x8f\xbf\xbf", sourceIllegal); expect_error("\xf8\x87\xbf\xbf\xbf", sourceIllegal); expect_error("\xfc\x83\xbf\xbf\xbf\xbf", sourceIllegal); // Overlong representation of the NUL character expect_error("\xc0\x80", sourceIllegal); expect_error("\xe0\x80\x80", sourceIllegal); expect_error("\xf0\x80\x80\x80", sourceIllegal); expect_error("\xf8\x80\x80\x80\x80", sourceIllegal); expect_error("\xfc\x80\x80\x80\x80\x80", sourceIllegal); // Single UTF-16 surrogates expect_error("\xed\xa0\x80", sourceIllegal); expect_error("\xed\xad\xbf", sourceIllegal); expect_error("\xed\xae\x80", sourceIllegal); expect_error("\xed\xaf\xbf", sourceIllegal); expect_error("\xed\xb0\x80", sourceIllegal); expect_error("\xed\xbe\x80", sourceIllegal); expect_error("\xed\xbf\xbf", sourceIllegal); // Paired UTF-16 surrogates expect_error("\xed\xa0\x80\xed\xb0\x80", sourceIllegal); expect_error("\xed\xa0\x80\xed\xbf\xbf", sourceIllegal); expect_error("\xed\xad\xbf\xed\xb0\x80", sourceIllegal); expect_error("\xed\xad\xbf\xed\xbf\xbf", sourceIllegal); expect_error("\xed\xae\x80\xed\xb0\x80", sourceIllegal); expect_error("\xed\xae\x80\xed\xbf\xbf", sourceIllegal); expect_error("\xed\xaf\xbf\xed\xb0\x80", sourceIllegal); expect_error("\xed\xaf\xbf\xed\xbf\xbf", sourceIllegal); // test higher level conversions std::string utf8; std::copy(utf8_source.begin(), utf8_source.end(), std::back_inserter(utf8)); std::wstring wide; utf8_conv_result_t ret = utf8_wchar(utf8, wide); TEST_EQUAL(ret, conversion_ok); std::string identity; ret = wchar_utf8(wide, identity); TEST_EQUAL(ret, conversion_ok); TEST_EQUAL(utf8, identity); } TORRENT_TEST(invalid_encoding) { // thest invalid utf8 encodings. just treat it as "Latin-1" boost::uint8_t const test_string[] = { 0xd2, 0xe5, 0xf0, 0xea, 0xf1, 0x20, 0xe8, 0x20, 0xca, 0xe0, 0xe9, 0xea, 0xee, 0xf1, 0x2e, 0x32, 0x30, 0x31, 0x34, 0x2e, 0x42, 0x44, 0x52, 0x69, 0x70, 0x2e, 0x31, 0x30, 0x38, 0x30, 0x70, 0x2e, 0x6d, 0x6b, 0x76, 0x00 }; std::wstring wide; utf8_wchar((const char*)test_string, wide); std::wstring cmp_wide; std::copy(test_string, test_string + sizeof(test_string) - 1, std::back_inserter(cmp_wide)); TEST_CHECK(wide == cmp_wide); } libtorrent-rasterbar-1.1.13/test/test_utils.cpp000066400000000000000000000037311351156116000216310ustar00rootroot00000000000000/* Copyright (c) 2015, 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 "test_utils.hpp" #include "libtorrent/time.hpp" namespace libtorrent { char const* time_now_string() { static const time_point start = clock_type::now(); static char ret[200]; int t = total_milliseconds(clock_type::now() - start); int h = t / 1000 / 60 / 60; t -= h * 60 * 60 * 1000; int m = t / 1000 / 60; t -= m * 60 * 1000; int s = t / 1000; t -= s * 1000; int ms = t; snprintf(ret, sizeof(ret), "%02d:%02d:%02d.%03d", h, m, s, ms); return ret; } } libtorrent-rasterbar-1.1.13/test/test_utils.hpp000066400000000000000000000031741351156116000216370ustar00rootroot00000000000000/* Copyright (c) 2015, 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 TEST_UTILS_HPP #define TEST_UTILS_HPP #include "test.hpp" namespace libtorrent { EXPORT char const* time_now_string(); } #endif libtorrent-rasterbar-1.1.13/test/test_utp.cpp000066400000000000000000000112741351156116000213020ustar00rootroot00000000000000/* Copyright (c) 2008, 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/session.hpp" #include "libtorrent/session_settings.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/alert_types.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/thread.hpp" #include "libtorrent/time.hpp" #include "libtorrent/file.hpp" #include #include #include "test.hpp" #include "setup_transfer.hpp" #include "settings.hpp" #include #include using namespace libtorrent; namespace lt = libtorrent; using boost::tuples::ignore; void test_transfer() { // in case the previous run was terminated error_code ec; remove_all("./tmp1_utp", ec); remove_all("./tmp2_utp", ec); // these are declared before the session objects // so that they are destructed last. This enables // the sessions to destruct in parallel session_proxy p1; session_proxy p2; settings_pack pack = settings(); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_dht, false); pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled); pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled); pack.set_bool(settings_pack::enable_outgoing_tcp, false); pack.set_bool(settings_pack::enable_incoming_tcp, false); pack.set_bool(settings_pack::announce_to_all_trackers, true); pack.set_bool(settings_pack::announce_to_all_tiers, true); pack.set_bool(settings_pack::prefer_udp_trackers, false); pack.set_int(settings_pack::min_reconnect_time, 1); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48885"); lt::session ses1(pack); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:49885"); lt::session ses2(pack); torrent_handle tor1; torrent_handle tor2; create_directory("./tmp1_utp", ec); std::ofstream file("./tmp1_utp/temporary"); boost::shared_ptr t = ::create_torrent(&file, "temporary", 128 * 1024, 6, false); file.close(); // for performance testing add_torrent_params atp; atp.flags &= ~add_torrent_params::flag_paused; atp.flags &= ~add_torrent_params::flag_auto_managed; // atp.storage = &disabled_storage_constructor; // test using piece sizes smaller than 16kB boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 , true, false, true, "_utp", 0, &t, false, &atp); #ifdef TORRENT_USE_VALGRIND const int timeout = 16; #else const int timeout = 8; #endif for (int i = 0; i < timeout; ++i) { print_alerts(ses1, "ses1", true, true, true); print_alerts(ses2, "ses2", true, true, true); test_sleep(500); torrent_status st1 = tor1.status(); torrent_status st2 = tor2.status(); print_ses_rate(i / 2.f, &st1, &st2); if (st2.is_finished) break; TEST_CHECK(st1.state == torrent_status::seeding || st1.state == torrent_status::checking_files); TEST_CHECK(st2.state == torrent_status::downloading); } TEST_CHECK(tor1.status().is_finished); TEST_CHECK(tor2.status().is_finished); // this allows shutting down the sessions in parallel p1 = ses1.abort(); p2 = ses2.abort(); } TORRENT_TEST(utp) { using namespace libtorrent; test_transfer(); error_code ec; remove_all("./tmp1_utp", ec); remove_all("./tmp2_utp", ec); } libtorrent-rasterbar-1.1.13/test/test_web_seed.cpp000066400000000000000000000035031351156116000222430ustar00rootroot00000000000000/* Copyright (c) 2008-2014, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" using namespace libtorrent; const int proxy = libtorrent::settings_pack::none; #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(web_seed_ssl) { run_http_suite(proxy, "https", false); } #endif TORRENT_TEST(web_seed) { run_http_suite(proxy, "http", false); } libtorrent-rasterbar-1.1.13/test/test_web_seed_ban.cpp000066400000000000000000000037231351156116000230670ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" using namespace libtorrent; const int proxy = libtorrent::settings_pack::none; #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(http_seed_ssl) { run_http_suite(proxy, "https", 0, 0, 1); } TORRENT_TEST(url_seed_ssl) { run_http_suite(proxy, "https", 1, 0, 1); } #endif TORRENT_TEST(http_seed) { run_http_suite(proxy, "http", 0, 0, 1); } TORRENT_TEST(url_seed) { run_http_suite(proxy, "http", 1, 0, 1); } libtorrent-rasterbar-1.1.13/test/test_web_seed_chunked.cpp000066400000000000000000000037211351156116000237460ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" using namespace libtorrent; const int proxy = libtorrent::settings_pack::none; #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(web_seed_ssl) { run_http_suite(proxy, "https", 0, 1, 0); } TORRENT_TEST(url_seed_ssl) { run_http_suite(proxy, "https", 1, 1, 0); } #endif TORRENT_TEST(web_seed) { run_http_suite(proxy, "http", 0, 1, 0); } TORRENT_TEST(url_seed) { run_http_suite(proxy, "http", 1, 1, 0); } libtorrent-rasterbar-1.1.13/test/test_web_seed_http.cpp000066400000000000000000000037251351156116000233100ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" using namespace libtorrent; const int proxy = libtorrent::settings_pack::http; TORRENT_TEST(web_seed_http) { run_http_suite(proxy, "http", false); } TORRENT_TEST(url_seed_http) { run_http_suite(proxy, "http", true); } #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(web_seed_https) { run_http_suite(proxy, "https", false); } TORRENT_TEST(url_seed_https) { run_http_suite(proxy, "https", true); } #endif libtorrent-rasterbar-1.1.13/test/test_web_seed_http_pw.cpp000066400000000000000000000037521351156116000240160ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" using namespace libtorrent; const int proxy = libtorrent::settings_pack::http_pw; TORRENT_TEST(web_seed_http_pw) { run_http_suite(proxy, "http", false); } TORRENT_TEST(url_seed_http_pw) { run_http_suite(proxy, "http", true); } #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(web_seed_http_pw_ssl) { run_http_suite(proxy, "https", false); } TORRENT_TEST(url_seed_http_pw_ssl) { run_http_suite(proxy, "https", true); } #endif libtorrent-rasterbar-1.1.13/test/test_web_seed_redirect.cpp000066400000000000000000000063661351156116000241360ustar00rootroot00000000000000/* Copyright (c) 2008-2014, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" #include "settings.hpp" #include "libtorrent/create_torrent.hpp" #include "libtorrent/torrent_info.hpp" using namespace libtorrent; TORRENT_TEST(web_seed_redirect) { using namespace libtorrent; error_code ec; file_storage fs; int piece_size = 0x4000; char random_data[16000]; std::generate(random_data, random_data + sizeof(random_data), random_byte); file f("test_file", file::write_only, ec); if (ec) { fprintf(stderr, "failed to create file \"test_file\": (%d) %s\n" , ec.value(), ec.message().c_str()); TEST_ERROR("failed to create file"); return; } file::iovec_t b = { random_data, size_t(16000)}; f.writev(0, &b, 1, ec); fs.add_file("test_file", 16000); int port = start_web_server(); // generate a torrent with pad files to make sure they // are not requested web seeds libtorrent::create_torrent t(fs, piece_size, 0x4000); char tmp[512]; snprintf(tmp, sizeof(tmp), "http://127.0.0.1:%d/redirect", port); t.add_url_seed(tmp); // calculate the hash for all pieces set_piece_hashes(t, ".", ec); if (ec) { fprintf(stderr, "error creating hashes for test torrent: %s\n" , ec.message().c_str()); TEST_ERROR("failed to create hashes"); return; } std::vector buf; bencode(std::back_inserter(buf), t.generate()); boost::shared_ptr torrent_file(new torrent_info(&buf[0] , buf.size(), ec)); { settings_pack p = settings(); p.set_int(settings_pack::max_queued_disk_bytes, 256 * 1024); libtorrent::session ses(p); // disable keep-alive because otherwise the test will choke on seeing // the disconnect (from the redirect) test_transfer(ses, torrent_file, 0, 0, "http", true, false, false, false); } stop_web_server(); } libtorrent-rasterbar-1.1.13/test/test_web_seed_socks4.cpp000066400000000000000000000036751351156116000235430ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" using namespace libtorrent; const int proxy = libtorrent::settings_pack::socks4; #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(http_seed_ssl) { run_http_suite(proxy, "https", 0); } TORRENT_TEST(url_seed_ssl) { run_http_suite(proxy, "https", 1); } #endif TORRENT_TEST(http_seed) { run_http_suite(proxy, "http", 0); } TORRENT_TEST(url_seed) { run_http_suite(proxy, "http", 1); } libtorrent-rasterbar-1.1.13/test/test_web_seed_socks5.cpp000066400000000000000000000036751351156116000235440ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" using namespace libtorrent; const int proxy = libtorrent::settings_pack::socks5; #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(http_seed_ssl) { run_http_suite(proxy, "https", 0); } TORRENT_TEST(url_seed_ssl) { run_http_suite(proxy, "https", 1); } #endif TORRENT_TEST(http_seed) { run_http_suite(proxy, "http", 0); } TORRENT_TEST(url_seed) { run_http_suite(proxy, "http", 1); } libtorrent-rasterbar-1.1.13/test/test_web_seed_socks5_no_peers.cpp000066400000000000000000000036461351156116000254340ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" using namespace libtorrent; const int proxy = libtorrent::settings_pack::socks5; TORRENT_TEST(web_seed_socks5_no_peers_ssl) { #ifdef TORRENT_USE_OPENSSL run_http_suite(proxy, "https", false, false, false, false, false, false); #endif } TORRENT_TEST(web_seed_socks5_no_peers) { run_http_suite(proxy, "http", false, false, false, false, false, false); } libtorrent-rasterbar-1.1.13/test/test_web_seed_socks5_pw.cpp000066400000000000000000000037001351156116000242370ustar00rootroot00000000000000/* Copyright (c) 2008, 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 "test.hpp" #include "setup_transfer.hpp" #include "web_seed_suite.hpp" using namespace libtorrent; const int proxy = libtorrent::settings_pack::socks5_pw; #ifdef TORRENT_USE_OPENSSL TORRENT_TEST(http_seed_ssl) { run_http_suite(proxy, "https", 0); } TORRENT_TEST(url_seed_ssl) { run_http_suite(proxy, "https", 1); } #endif TORRENT_TEST(http_seed) { run_http_suite(proxy, "http", 0); } TORRENT_TEST(url_seed) { run_http_suite(proxy, "http", 1); } libtorrent-rasterbar-1.1.13/test/test_xml.cpp000066400000000000000000000333351351156116000212740ustar00rootroot00000000000000/* Copyright (c) 2012, 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/xml_parse.hpp" #include "libtorrent/upnp.hpp" #include "test.hpp" #include #include char upnp_xml[] = "" "" "1" "0" "" "http://192.168.0.1:5678" "" "" "urn:schemas-upnp-org:device:InternetGatewayDevice:1" "" "http://192.168.0.1:80" "D-Link Router" "D-Link" "http://www.dlink.com" "Internet Access Router" "D-Link Router" "uuid:upnp-InternetGatewayDevice-1_0-12345678900001" "123456789001" "" "" "urn:schemas-upnp-org:service:Layer3Forwarding:1" "urn:upnp-org:serviceId:L3Forwarding1" "/Layer3Forwarding" "/Layer3Forwarding" "/Layer3Forwarding.xml" "" "" "" "" "urn:schemas-upnp-org:device:WANDevice:1" "WANDevice" "D-Link" "http://www.dlink.com" "Internet Access Router" "D-Link Router" "1" "http://support.dlink.com" "12345678900001" "uuid:upnp-WANDevice-1_0-12345678900001" "123456789001" "" "" "" "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" "" "urn:upnp-org:serviceId:WANCommonInterfaceConfig" "/WANCommonInterfaceConfig" "/WANCommonInterfaceConfig" "/WANCommonInterfaceConfig.xml" "" "" "" "" "urn:schemas-upnp-org:device:WANConnectionDevice:1" "WAN Connection Device" "D-Link" "http://www.dlink.com" "Internet Access Router" "D-Link Router" "1" "http://support.dlink.com" "12345678900001" "uuid:upnp-WANConnectionDevice-1_0-12345678900001" "123456789001" "" "" "urn:schemas-upnp-org:service:WANIPConnection:1" "urn:upnp-org:serviceId:WANIPConnection" "/WANIPConnection" "/WANIPConnection" "/WANIPConnection.xml" "" "" "" "" "" "" "" ""; char upnp_xml2[] = "" "" "1" "0" "" "http://192.168.1.1:49152" "" "" "urn:schemas-upnp-org:device:InternetGatewayDevice:1" "" "LINKSYS WAG200G Gateway" "LINKSYS" "http://www.linksys.com" "LINKSYS WAG200G Gateway" "Wireless-G ADSL Home Gateway" "WAG200G" "http://www.linksys.com" "123456789" "uuid:8d401597-1dd2-11b2-a7d4-001ee5947cac" "WAG200G" "" "" "urn:schemas-upnp-org:service:Layer3Forwarding:1" "urn:upnp-org:serviceId:L3Forwarding1" "/upnp/control/L3Forwarding1" "/upnp/event/L3Forwarding1" "/l3frwd.xml" "" "" "" "" "urn:schemas-upnp-org:device:WANDevice:1" "WANDevice" "LINKSYS" "http://www.linksys.com/" "Residential Gateway" "Internet Connection Sharing" "1" "http://www.linksys.com/" "0000001" "uuid:8d401596-1dd2-11b2-a7d4-001ee5947cac" "WAG200G" "" "" "" "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" "" "urn:upnp-org:serviceId:WANCommonIFC1" "/upnp/control/WANCommonIFC1" "/upnp/event/WANCommonIFC1" "/cmnicfg.xml" "" "" "" "" "urn:schemas-upnp-org:device:WANConnectionDevice:1" "WANConnectionDevice" "LINKSYS" "http://www.linksys.com/" "Residential Gateway" "Internet Connection Sharing" "1" "http://www.linksys.com/" "0000001" "uuid:8d401597-1dd2-11b2-a7d3-001ee5947cac" "WAG200G" "" "" "" "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1" "" "urn:upnp-org:serviceId:WANEthLinkC1" "/upnp/control/WANEthLinkC1" "/upnp/event/WANEthLinkC1" "/wanelcfg.xml" "" "" "urn:schemas-upnp-org:service:WANPPPConnection:1" "urn:upnp-org:serviceId:WANPPPConn1" "/upnp/control/WANPPPConn1" "/upnp/event/WANPPPConn1" "/pppcfg.xml" "" "" "" "" "" "" "urn:schemas-upnp-org:device:LANDevice:1" "LANDevice" "LINKSYS" "http://www.linksys.com/" "Residential Gateway" "Residential Gateway" "1" "http://www.linksys.com/" "0000001" "uuid:8d401596-1dd2-11b2-a7d3-001ee5947cac" "WAG200G" "" "" "" "urn:schemas-upnp-org:service:LANHostConfigManagement:1" "" "urn:upnp-org:serviceId:LANHostCfg1" "/upnp/control/LANHostCfg1" "/upnp/event/LANHostCfg1" "/lanhostc.xml" "" "" "" "" "http://192.168.1.1/index.htm" "" ""; char upnp_xml3[] = "" "" "" "s:Client" "UPnPError" "" "" "402" "Invalid Args" "" "" "" "" ""; char upnp_xml4[] = "" "" "" "" "123.10.20.30" "" "" ""; using namespace libtorrent; void parser_callback(std::string& out, int token, char const* s, int len , char const* val, int val_len) { switch (token) { case xml_start_tag: out += "B"; break; case xml_end_tag: out += "F"; break; case xml_empty_tag: out += "E"; break; case xml_declaration_tag: out += "D"; break; case xml_comment: out += "C"; break; case xml_string: out += "S"; break; case xml_attribute: out += "A"; break; case xml_parse_error: out += "P"; break; case xml_tag_content: out += "T"; break; default: TEST_CHECK(false); } out.append(s, len); if (token == xml_attribute) { TEST_CHECK(val != NULL); out += "V"; out.append(val, val_len); } else { TEST_CHECK(val == NULL); } } void test_parse(char const* in, char const* expected) { std::string out; xml_parse(in, in + strlen(in), boost::bind(&parser_callback , boost::ref(out), _1, _2, _3, _4, _5)); fprintf(stdout, "in: %s\n out: %s\nexpected: %s\n" , in, out.c_str(), expected); TEST_EQUAL(out, expected); } TORRENT_TEST(upnp_parser1) { parse_state xml_s; xml_parse(upnp_xml, upnp_xml + sizeof(upnp_xml) , boost::bind(&find_control_url, _1, _2, _3, boost::ref(xml_s))); std::cout << "namespace " << xml_s.service_type << std::endl; std::cout << "url_base: " << xml_s.url_base << std::endl; std::cout << "control_url: " << xml_s.control_url << std::endl; std::cout << "model: " << xml_s.model << std::endl; TEST_EQUAL(xml_s.url_base, "http://192.168.0.1:5678"); TEST_EQUAL(xml_s.control_url, "/WANIPConnection"); TEST_EQUAL(xml_s.model, "D-Link Router"); } TORRENT_TEST(upnp_parser2) { parse_state xml_s; xml_parse(upnp_xml2, upnp_xml2 + sizeof(upnp_xml2) , boost::bind(&find_control_url, _1, _2, _3, boost::ref(xml_s))); std::cout << "namespace " << xml_s.service_type << std::endl; std::cout << "url_base: " << xml_s.url_base << std::endl; std::cout << "control_url: " << xml_s.control_url << std::endl; std::cout << "model: " << xml_s.model << std::endl; TEST_EQUAL(xml_s.url_base, "http://192.168.1.1:49152"); TEST_EQUAL(xml_s.control_url, "/upnp/control/WANPPPConn1"); TEST_EQUAL(xml_s.model, "Wireless-G ADSL Home Gateway"); } TORRENT_TEST(upnp_parser3) { error_code_parse_state xml_s; xml_parse(upnp_xml3, upnp_xml3 + sizeof(upnp_xml3) , boost::bind(&find_error_code, _1, _2, _3, boost::ref(xml_s))); std::cout << "error_code " << xml_s.error_code << std::endl; TEST_EQUAL(xml_s.error_code, 402); } TORRENT_TEST(upnp_parser4) { ip_address_parse_state xml_s; xml_parse(upnp_xml4, upnp_xml4 + sizeof(upnp_xml4) , boost::bind(&find_ip_address, _1, _2, _3, boost::ref(xml_s))); std::cout << "error_code " << xml_s.error_code << std::endl; std::cout << "ip_address " << xml_s.ip_address << std::endl; TEST_EQUAL(xml_s.error_code, -1); TEST_EQUAL(xml_s.ip_address, "123.10.20.30"); } TORRENT_TEST(tags) { test_parse("foobar", "BaSfooEbSbarFa"); } TORRENT_TEST(xml_tag_comment) { test_parse("" , "DxmlAversionV1.0EcAxV1AyV3BdAfooVbarFdAbooVfooCcomment"); } TORRENT_TEST(empty_tag) { test_parse("", "Efoo"); } TORRENT_TEST(empty_tag_whitespace) { test_parse("", "Efoo"); } TORRENT_TEST(xml_tag_no_attribute) { test_parse("", "Dxml"); } TORRENT_TEST(xml_tag_no_attribute_whitespace) { test_parse("", "Dxml"); } TORRENT_TEST(attribute_missing_qoute) { test_parse("foo #include #include #include "setup_transfer.hpp" #include using namespace libtorrent; namespace lt = libtorrent; namespace { int peer_disconnects = 0; bool on_alert(alert const* a) { if (alert_cast(a)) ++peer_disconnects; else if (alert_cast(a)) ++peer_disconnects; return false; } static char const* proxy_name[] = {"", "_socks4", "_socks5", "_socks5_pw", "_http", "_http_pw", "_i2p"}; } // anonymous namespace // proxy: 0=none, 1=socks4, 2=socks5, 3=socks5_pw 4=http 5=http_pw void test_transfer(lt::session& ses, boost::shared_ptr torrent_file , int proxy, int port, char const* protocol, bool url_seed , bool chunked_encoding, bool test_ban, bool keepalive, bool proxy_peers) { using namespace libtorrent; TORRENT_ASSERT(torrent_file->web_seeds().size() > 0); std::string save_path = "tmp2_web_seed"; save_path += proxy_name[proxy]; error_code ec; remove_all(save_path, ec); static char const* test_name[] = {"no", "SOCKS4", "SOCKS5", "SOCKS5 password", "HTTP", "HTTP password"}; fprintf(stdout, "\n\n ==== TESTING === proxy: %s ==== protocol: %s " "==== seed: %s === transfer-encoding: %s === corruption: %s " "==== keepalive: %s\n\n\n" , test_name[proxy], protocol, url_seed ? "URL seed" : "HTTP seed" , chunked_encoding ? "chunked": "none", test_ban ? "yes" : "no" , keepalive ? "yes" : "no"); int proxy_port = 0; if (proxy) { proxy_port = start_proxy(proxy); if (proxy_port < 0) { fprintf(stderr, "failed to start proxy"); return; } settings_pack pack; pack.set_str(settings_pack::proxy_hostname, "127.0.0.1"); pack.set_str(settings_pack::proxy_username, "testuser"); pack.set_str(settings_pack::proxy_password, "testpass"); pack.set_int(settings_pack::proxy_type, (settings_pack::proxy_type_t)proxy); pack.set_int(settings_pack::proxy_port, proxy_port); pack.set_bool(settings_pack::proxy_peer_connections, proxy_peers); ses.apply_settings(pack); } else { settings_pack pack; pack.set_str(settings_pack::proxy_hostname, ""); pack.set_str(settings_pack::proxy_username, ""); pack.set_str(settings_pack::proxy_password, ""); pack.set_int(settings_pack::proxy_type, settings_pack::none); pack.set_int(settings_pack::proxy_port, 0); pack.set_bool(settings_pack::proxy_peer_connections, proxy_peers); ses.apply_settings(pack); } add_torrent_params p; p.flags &= ~add_torrent_params::flag_paused; p.flags &= ~add_torrent_params::flag_auto_managed; // the reason to set sequential download is to make sure that the order in // which files are requested from the web server is consistent. Any specific // scenario that needs testing should be an explicit test case p.flags |= add_torrent_params::flag_sequential_download; p.ti = torrent_file; p.save_path = save_path; torrent_handle th = ses.add_torrent(p, ec); printf("adding torrent, save_path = \"%s\" cwd = \"%s\" torrent = \"%s\"\n" , save_path.c_str(), current_working_directory().c_str() , torrent_file->name().c_str()); std::vector empty; th.replace_trackers(empty); const boost::int64_t total_size = torrent_file->total_size(); file_storage const& fs = torrent_file->files(); int pad_file_size = 0; for (int i = 0; i < fs.num_files(); ++i) { if (fs.file_flags(i) & file_storage::flag_pad_file) pad_file_size += fs.file_size(i); } peer_disconnects = 0; std::map cnt = get_counters(ses); for (int i = 0; i < 40; ++i) { torrent_status s = th.status(); cnt = get_counters(ses); print_ses_rate(i / 10.f, &s, NULL); print_alerts(ses, " >> ses", test_ban, false, false, &on_alert); if (test_ban && th.url_seeds().empty() && th.http_seeds().empty()) { fprintf(stdout, "testing ban: URL seed removed\n"); // when we don't have any web seeds left, we know we successfully banned it break; } if (s.is_seeding) { fprintf(stdout, "SEEDING\n"); fprintf(stdout, "session.payload: %d session.redundant: %d\n" , int(cnt["net.recv_payload_bytes"]), int(cnt["net.recv_redundant_bytes"])); fprintf(stdout, "torrent.payload: %d torrent.redundant: %d\n" , int(s.total_payload_download), int(s.total_redundant_bytes)); TEST_EQUAL(s.total_payload_download - s.total_redundant_bytes, total_size - pad_file_size); break; } // if the web seed connection is disconnected, we're going to fail // the test. make sure to do so quickly if (!test_ban && keepalive && peer_disconnects >= 1) break; test_sleep(100); } cnt = get_counters(ses); if (test_ban) { // for test_ban tests, make sure we removed // the url seed (i.e. banned it) // torrents that don't have very many pieces will not ban the web seeds, // since they won't have an opportunity to accrue enough negative points if (torrent_file->files().num_pieces() > 3) { TEST_CHECK(th.url_seeds().empty()); TEST_CHECK(th.http_seeds().empty()); } } else { // if the web seed senr corrupt data and we banned it, we probably didn't // end up using all the cache anyway torrent_status st = th.status(); TEST_EQUAL(st.is_seeding, true); if (st.is_seeding) { boost::int64_t const total_blocks = (torrent_file->total_size() + 0x3fff) / 0x4000; // we need to sleep here a bit to let the session sync with the torrent stats // commented out because it takes such a long time for (int i = 0; i < 50; ++i) { cnt = get_counters(ses); if (std::abs(int(cnt["disk.read_cache_blocks"] - total_blocks)) <= 2 && std::abs(int(cnt["disk.disk_blocks_in_use"] - total_blocks)) <= 2) break; fprintf(stdout, "cache_size: %d/%d\n", int(cnt["disk.read_cache_blocks"]) , int(cnt["disk.disk_blocks_in_use"])); test_sleep(100); } TEST_CHECK(std::abs(int(cnt["disk.disk_blocks_in_use"] - total_blocks)) <= 2); } } std::cerr << "total_size: " << total_size << " read cache size: " << cnt["disk.disk_blocks_in_use"] << " total used buffer: " << cnt["disk.disk_blocks_in_use"] << " session total download: " << cnt["net.recv_payload_bytes"] << " torrent total download: " << th.status().total_payload_download << " redundant: " << th.status().total_redundant_bytes << std::endl; // if test_ban is true, we're not supposed to have completed the download // otherwise, we are supposed to have TEST_CHECK(th.status().is_seeding == !test_ban); if (proxy) stop_proxy(proxy_port); th.flush_cache(); // synchronize to make sure the files have been created on disk wait_for_alert(ses, cache_flushed_alert::alert_type, "ses"); print_alerts(ses, " >> ses", true, true, false, &on_alert, true); if (!test_ban) { file_storage const& fs = torrent_file->files(); for (int i = 0; i < fs.num_files(); ++i) { bool const expect = !fs.pad_file_at(i); std::string file_path = combine_path(save_path, fs.file_path(i)); fprintf(stdout, "checking file: %s\n", file_path.c_str()); TEST_EQUAL(exists(file_path), expect); } } ses.remove_torrent(th); } // proxy: 0=none, 1=socks4, 2=socks5, 3=socks5_pw 4=http 5=http_pw // protocol: "http" or "https" // test_url_seed determines whether to use url-seed or http-seed int EXPORT run_http_suite(int proxy, char const* protocol, bool test_url_seed , bool chunked_encoding, bool test_ban, bool keepalive, bool test_rename , bool proxy_peers) { using namespace libtorrent; std::string save_path = "web_seed"; save_path += proxy_name[proxy]; error_code ec; int const port = start_web_server(strcmp(protocol, "https") == 0, chunked_encoding, keepalive); std::vector test_cases; if (test_url_seed) { char url[512]; snprintf(url, sizeof(url), ("%s://127.0.0.1:%d/" + save_path).c_str(), protocol, port); fprintf(stdout, "testing: %s\n", url); create_directories(combine_path(save_path, "torrent_dir"), ec); torrent_args args; // test case 1 test_cases.push_back(torrent_args().file("0").file("5,padfile").file("11") .file("16000").file("368,padfile") .file("16384,padfile").file("16384,padfile").file("17").file("10") .file("8000").file("8000").file("1").file("1").file("1").file("1") .file("1").file("100").file("0").file("1").file("1").file("1") .file("100").file("1").file("1").file("1").file("1").file("1,padfile") .file("1,padfile").file("1,padfile").file("1").file("0").file("0") .file("0").file("1").file("13").file("65000").file("34").file("75") .file("2").file("30").file("400").file("500").file("23000") .file("900").file("43000").file("400").file("4300").file("6") .file("4,padfile") .name("torrent_dir") .url_seed(url)); // test case 2 (the end of the torrent are padfiles) test_cases.push_back(torrent_args() .file("0,padfile") .file("11") .file("5") .file("16000") .file("368,padfile") .file("16384,padfile") .name("torrent_dir") .url_seed(url)); // test case 3 (misaligned) test_cases.push_back(torrent_args() .file("16383") .file("11") .file("5") .file("16000") .name("torrent_dir") .url_seed(url)); // test case 4 (a full piece padfile) test_cases.push_back(torrent_args() .file("32768,padfile") .file("16000") .file("11") .file("5") .name("torrent_dir") .url_seed(url)); // test case 5 (properly aligned padfile) test_cases.push_back(torrent_args() .file("32760") .file("8,padfile") .file("32760") .file("8") .file("32700") .file("68,padfile") .file("32000") .name("torrent_dir") .url_seed(url)); snprintf(url, sizeof(url), ("%s://127.0.0.1:%d/" + save_path + "/test-single-file").c_str(), protocol, port); // test case 6 (single file torrent) test_cases.push_back(torrent_args() .file("199092,name=test-single-file") .name("torrent_dir") .url_seed(url)); } else { char url[512]; snprintf(url, sizeof(url), "%s://127.0.0.1:%d/%s/seed", protocol, port, save_path.c_str()); fprintf(stdout, "testing: %s\n", url); // there's really just one test case for http seeds test_cases.push_back(torrent_args().file("589824,name=seed") .http_seed(url)); } for (int a = 0; a < int(test_cases.size()); ++a) { fprintf(stdout, "\n\n ==== test case %d ====\n\n\n", a); boost::shared_ptr torrent_file = make_test_torrent(test_cases[a]); // if test_ban is true, we create the files with alternate content (that // doesn't match the hashes in the .torrent file) generate_files(*torrent_file, save_path, test_ban); if (ec) { fprintf(stderr, "error creating hashes for test torrent: %s\n" , ec.message().c_str()); TEST_CHECK(false); return 0; } { settings_pack pack = settings(); pack.set_int(settings_pack::max_queued_disk_bytes, 256 * 1024); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:51000"); pack.set_int(settings_pack::max_retry_port_bind, 1000); pack.set_bool(settings_pack::enable_lsd, false); pack.set_bool(settings_pack::enable_natpmp, false); pack.set_bool(settings_pack::enable_upnp, false); pack.set_bool(settings_pack::enable_dht, false); libtorrent::session ses(pack, 0); test_transfer(ses, torrent_file, proxy, port, protocol, test_url_seed , chunked_encoding, test_ban, keepalive, proxy_peers); if (test_url_seed && test_rename) { torrent_file->rename_file(0, combine_path(save_path, combine_path("torrent_dir", "renamed_test1"))); test_transfer(ses, torrent_file, 0, port, protocol, test_url_seed , chunked_encoding, test_ban, keepalive, proxy_peers); } } } stop_web_server(); return 0; } libtorrent-rasterbar-1.1.13/test/web_seed_suite.hpp000066400000000000000000000040161351156116000224220ustar00rootroot00000000000000/* Copyright (c) 2008-2013, 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 "test.hpp" int EXPORT run_http_suite(int proxy, char const* protocol , bool test_url_seed, bool chunked_encoding = false, bool test_ban = false , bool keepalive = true, bool test_rename = false, bool proxy_peers = true); void EXPORT test_transfer(libtorrent::session& ses , boost::shared_ptr torrent_file , int proxy = 0, int port = 0, char const* protocol = "http" , bool url_seed = true, bool chunked_encoding = false , bool test_ban = false, bool keepalive = true, bool proxy_peers = true); libtorrent-rasterbar-1.1.13/test/web_server.py000077500000000000000000000121421351156116000214420ustar00rootroot00000000000000#!/usr/bin/env python import BaseHTTPServer import SimpleHTTPServer import sys import os import ssl import gzip import base64 chunked_encoding = False keepalive = True try: fin = open('test_file', 'rb') f = gzip.open('test_file.gz', 'wb') f.writelines(fin) f.close() fin.close() except: pass class http_server_with_timeout(BaseHTTPServer.HTTPServer): allow_reuse_address = True timeout = 190 def handle_timeout(self): raise Exception('timeout') class http_handler(SimpleHTTPServer.SimpleHTTPRequestHandler): def do_GET(s): print 'INCOMING-REQUEST: ', s.requestline print s.headers global chunked_encoding global keepalive # if the request contains the hostname and port. strip it if s.path.startswith('http://') or s.path.startswith('https://'): s.path = s.path[8:] s.path = s.path[s.path.find('/'):] file_path = os.path.normpath(s.path) print file_path print s.path if s.path == '/password_protected': passed = False if 'Authorization' in s.headers: auth = s.headers['Authorization'] passed = auth == 'Basic %s' % base64.b64encode('testuser:testpass') if not passed: s.send_response(401) s.send_header("Connection", "close") s.end_headers() return s.path = '/test_file' file_path = os.path.normpath('/test_file') if s.path == '/redirect': s.send_response(301) s.send_header("Location", "/test_file") s.send_header("Connection", "close") s.end_headers() elif s.path == '/infinite_redirect': s.send_response(301) s.send_header("Location", "/infinite_redirect") s.send_header("Connection", "close") s.end_headers() elif s.path == '/relative/redirect': s.send_response(301) s.send_header("Location", "../test_file") s.send_header("Connection", "close") s.end_headers() elif s.path.startswith('/announce'): s.send_response(200) response = 'd8:intervali1800e8:completei1e10:incompletei1e' + \ '5:peers12:AAAABBCCCCDD' + \ '6:peers618:EEEEEEEEEEEEEEEEFF' + \ 'e' s.send_header("Content-Length", "%d" % len(response)) s.send_header("Connection", "close") s.end_headers() s.wfile.write(response) elif os.path.split(s.path)[1].startswith('seed?'): query = s.path[6:] args_raw = query.split('&') args = {} for a in args_raw: kvp = a.split('=') args[kvp[0]] = kvp[1] piece = int(args['piece']) ranges = args['ranges'].split('-') filename = '' try: filename = os.path.normpath(s.path[1:s.path.find('seed?') + 4]) print 'filename = %s' % filename f = open(filename, 'rb') f.seek(piece * 32 * 1024 + int(ranges[0])) data = f.read(int(ranges[1]) - int(ranges[0]) + 1) f.close() s.send_response(200) print 'sending %d bytes' % len(data) s.send_header("Content-Length", "%d" % len(data)) s.end_headers() s.wfile.write(data); except Exception, e: print 'FILE ERROR: ', filename, e s.send_response(404) s.send_header("Content-Length", "0") s.end_headers() else: filename = '' try: filename = os.path.normpath(file_path[1:]) # serve file by invoking default handler f = open(filename, 'rb') size = int(os.stat(filename).st_size) start_range = 0 end_range = size if 'Range' in s.headers: s.send_response(206) st, e = s.headers['range'][6:].split('-', 1) sl = len(st) el = len(e) if sl > 0: start_range = int(st) if el > 0: end_range = int(e) + 1 elif el > 0: ei = int(e) if ei < size: start_range = size - ei s.send_header('Content-Range', 'bytes ' + str(start_range) \ + '-' + str(end_range - 1) + '/' + str(size)) else: s.send_response(200) s.send_header('Accept-Ranges', 'bytes') if chunked_encoding: s.send_header('Transfer-Encoding', 'chunked') s.send_header('Content-Length', end_range - start_range) if filename.endswith('.gz'): s.send_header('Content-Encoding', 'gzip') if not keepalive: s.send_header("Connection", "close") try: s.request.shutdown(); except Exception, e: print 'Failed to shutdown read-channel of socket: ', e s.end_headers() f.seek(start_range) length = end_range - start_range while length > 0: to_send = min(length, 0x900) if chunked_encoding: s.wfile.write('%x\r\n' % to_send) data = f.read(to_send) print 'read %d bytes' % to_send s.wfile.write(data) if chunked_encoding: s.wfile.write('\r\n') length -= to_send print 'sent %d bytes (%d bytes left)' % (len(data), length) if chunked_encoding: s.wfile.write('0\r\n\r\n') except Exception, e: print 'FILE ERROR: ', filename, e s.send_response(404) s.send_header("Content-Length", "0") s.end_headers() if __name__ == '__main__': port = int(sys.argv[1]) chunked_encoding = sys.argv[2] != '0' use_ssl = sys.argv[3] != '0' keepalive = sys.argv[4] != '0' http_handler.protocol_version = 'HTTP/1.1' httpd = http_server_with_timeout(('127.0.0.1', port), http_handler) if use_ssl: httpd.socket = ssl.wrap_socket(httpd.socket, certfile='../ssl/server.pem', server_side=True) while True: httpd.handle_request() libtorrent-rasterbar-1.1.13/test/zeroes.gz000066400000000000000000000010321351156116000205670ustar00rootroot00000000000000‹ŁPcSzeroesíÁ1 őOm  ľ§ÉTĐlibtorrent-rasterbar-1.1.13/tools/000077500000000000000000000000001351156116000171035ustar00rootroot00000000000000libtorrent-rasterbar-1.1.13/tools/Jamfile000066400000000000000000000012151351156116000203740ustar00rootroot00000000000000import modules ; BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; use-project /torrent : .. ; if $(BOOST_ROOT) { use-project /boost : $(BOOST_ROOT) ; } rule link_libtorrent ( properties * ) { local result ; if shared in $(properties) { result += /torrent//torrent/shared/shared ; } else { result += /torrent//torrent/static/static ; } return $(result) ; } project tools : requirements multi @link_libtorrent : default-build static ; exe parse_access_log : parse_access_log.cpp ; exe dht : dht_put.cpp : ../ed25519/src ; libtorrent-rasterbar-1.1.13/tools/Makefile.am000066400000000000000000000012771351156116000211460ustar00rootroot00000000000000if ENABLE_EXAMPLES bin_PROGRAMS = $(tool_programs) endif EXTRA_PROGRAMS = $(tool_programs) EXTRA_DIST = Jamfile \ parse_bandwidth_log.py \ parse_buffer_log.py \ parse_dht_log.py \ parse_dht_rtt.py \ parse_dht_stats.py \ parse_disk_buffer_log.py\ parse_disk_log.py \ parse_memory_log.py \ parse_peer_log.py \ parse_sample.py \ parse_session_stats.py \ parse_utp_log.py LDADD = $(top_builddir)/src/libtorrent-rasterbar.la AM_CPPFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include @DEBUGFLAGS@ AM_LDFLAGS = @BOOST_SYSTEM_LIB@ #AM_LDFLAGS = $(LDFLAGS) @BOOST_SYSTEM_LIB@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ #AM_LDFLAGS = @OPENSSL_LDFLAGS@ libtorrent-rasterbar-1.1.13/tools/Makefile.in000066400000000000000000000404521351156116000211550ustar00rootroot00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 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@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd 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@ target_triplet = @target@ bin_PROGRAMS = EXTRA_PROGRAMS = subdir = tools ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ $(top_srcdir)/m4/ax_boost_chrono.m4 \ $(top_srcdir)/m4/ax_boost_python.m4 \ $(top_srcdir)/m4/ax_boost_random.m4 \ $(top_srcdir)/m4/ax_boost_system.m4 \ $(top_srcdir)/m4/ax_check_openssl.m4 \ $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BOOST_CHRONO_LIB = @BOOST_CHRONO_LIB@ BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ BOOST_LDFLAGS = @BOOST_LDFLAGS@ BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ BOOST_RANDOM_LIB = @BOOST_RANDOM_LIB@ BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUGFLAGS = @DEBUGFLAGS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONV_LIBS = @ICONV_LIBS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ PYTHON_CXXFLAGS = @PYTHON_CXXFLAGS@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = Jamfile \ parse_bandwidth_log.py \ parse_buffer_log.py \ parse_dht_log.py \ parse_dht_rtt.py \ parse_dht_stats.py \ parse_disk_buffer_log.py\ parse_disk_log.py \ parse_memory_log.py \ parse_peer_log.py \ parse_sample.py \ parse_session_stats.py \ parse_utp_log.py LDADD = $(top_builddir)/src/libtorrent-rasterbar.la AM_CPPFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include @DEBUGFLAGS@ AM_LDFLAGS = @BOOST_SYSTEM_LIB@ all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tools/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign tools/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: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$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 $(PROGRAMS) installdirs: for dir in "$(DESTDIR)$(bindir)"; 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: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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-binPROGRAMS clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-binPROGRAMS \ clean-generic clean-libtool cscopelist-am ctags-am distclean \ distclean-generic distclean-libtool distdir dvi dvi-am html \ html-am info info-am install install-am install-binPROGRAMS \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-binPROGRAMS .PRECIOUS: Makefile #AM_LDFLAGS = $(LDFLAGS) @BOOST_SYSTEM_LIB@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ #AM_LDFLAGS = @OPENSSL_LDFLAGS@ # 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: libtorrent-rasterbar-1.1.13/tools/parse_bandwidth_log.py000077500000000000000000000013521351156116000234600ustar00rootroot00000000000000#!/usr/bin/env python import os, sys, time keys = [['upload rate', 'x1y1', 6], ['history entries', 'x1y2', 10], ['queue', 'x1y2', 4]] out = open('bandwidth.gnuplot', 'wb') print >>out, "set term png size 1200,700" print >>out, 'set output "bandwidth_manager.png"' print >>out, 'set xrange [0:*]' print >>out, 'set xlabel "time (ms)"' print >>out, 'set ylabel "Rate (B/s)"' print >>out, 'set ytics 10000' print >>out, 'set y2label "number"' print >>out, 'set y2range [0:*]' #print >>out, "set style data lines" print >>out, "set key box" print >>out, 'plot', for k, a, c in keys: print >>out, ' "%s" using 1:%d title "%s" axes %s with steps,' % (sys.argv[1], c, k, a), print >>out, 'x=0' out.close() os.system('gnuplot bandwidth.gnuplot'); libtorrent-rasterbar-1.1.13/tools/parse_buffer_log.py000077500000000000000000000056651351156116000230000ustar00rootroot00000000000000#!/usr/bin/env python # Copyright Arvid Norberg 2008. 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) import os, sys, time lines = open(sys.argv[1], 'rb').readlines() #keys = ['send_buffer_utilization'] keys = ['send_buffer_size', 'used_send_buffer', 'protocol_buffer'] #keys = ['send_buffer_alloc', 'send_buffer', 'allocate_buffer_alloc', 'allocate_buffer', 'protocol_buffer'] #keys = ['send_buffer_alloc', 'send_buffer', 'allocate_buffer_alloc', 'allocate_buffer', 'protocol_buffer', 'append_send_buffer'] average = ['send_buffer_utilization', 'send_buffer_size', 'used_send_buffer'] average_interval = 120000 render = 'lines' time_limit = -1 if len(sys.argv) > 2: time_limit = long(sys.argv[2]) # logfile format: # # example: # 16434 allocate_buffer: 17 for k in keys: last_sample = 0 average_accumulator = 0 average_samples = 0 peak = 0 out = open(k + '.dat', 'wb') eval_average = False if k in average: eval_average = True peak_out = open(k + '_peak.dat', 'wb') for l in lines: l = l.split(' ') if len(l) != 3: print l continue try: if l[1] == k + ':': if time_limit != -1 and long(l[0]) > time_limit: break time = l[0] value = l[2] if eval_average: while long(time) > last_sample + average_interval: last_sample = last_sample + average_interval if average_samples < 1: average_samples = 1 print >>out, '%d %f' % (last_sample, average_accumulator / average_samples) print >>peak_out, '%d %f' % (last_sample, peak) average_accumulator = 0 average_samples = 0 peak = 0 average_accumulator = average_accumulator + float(value) average_samples = average_samples + 1 if float(value) > peak: peak = float(value) else: print >>out, time + ' ' + value, except: print l out.close() peak_out.close() out = open('send_buffer.gnuplot', 'wb') print >>out, "set term png size 1200,700" print >>out, 'set output "send_buffer.png"' print >>out, 'set xrange [0:*]' print >>out, 'set xlabel "time (ms)"' print >>out, 'set ylabel "bytes (B)"' print >>out, "set style data lines" print >>out, "set key box" print >>out, 'plot', for k in keys: if k in average: print >>out, ' "%s.dat" using 1:2 title "%s %d seconds average" with %s,' % (k, k, average_interval / 1000., render), print >>out, ' "%s_peak.dat" using 1:2 title "%s %d seconds peak" with %s,' % (k, k, average_interval / 1000., render), else: print >>out, ' "%s.dat" using 1:2 title "%s" with %s,' % (k, k, render), print >>out, 'x=0' out.close() os.system('gnuplot send_buffer.gnuplot') libtorrent-rasterbar-1.1.13/tools/parse_dht_log.py000077500000000000000000000220461351156116000222760ustar00rootroot00000000000000#!/usr/bin/env python import sys import os import time import calendar import pprint pp = pprint.PrettyPrinter(indent=4) up_time_quanta = 500 f = open(sys.argv[1]) announce_histogram = {} #TODO: make this histogram into a CDF node_uptime_histogram = {} counter = 0; # maps search_id to a list of events. Each event is a dict containing: # t: timestamp # d: distance (from target) # o: outstanding searches # e: event (NEW, COMPLETED, ADD, INVOKE, TIMEOUT) # i: node-id # a: IP address and port # s: source node-id (only for ADD events) outstanding_searches = {} # list of completed searches searches = [] def convert_timestamp(t): parts = t.split('.') hms = parts[0].split(':') return (int(hms[0]) * 3600 + int(hms[1]) * 60 + int(hms[2])) * 1000 + int(parts[1]) last_incoming = '' our_node_id = '' unique_ips = set() client_version_histogram = {} client_histogram = {} for line in f: counter += 1 # if counter % 1000 == 0: # print '\r%d' % counter, try: l = line.split(' ') if 'starting DHT tracker with node id:' in line: our_node_id = l[l.index('id:') + 1].strip() try: if len(l) > 4 and l[2] == '<==' and l[1] == '[dht_tracker]': ip = l[3].split(':')[0] if ip not in unique_ips: unique_ips.add(ip) json_blob = line.split(l[3])[1] version = json_blob.split("'v': '")[1].split("'")[0] if len(version) == 4: v = '%s-%d' % (version[0:2], (ord(version[2]) << 8) + ord(version[3])) elif len(version) == 8: v = '%c%c-%d' % (chr(int(version[0:2], 16)), chr(int(version[2:4], 16)), int(version[4:8], 16)) else: v = 'unknown' if not v in client_version_histogram: client_version_histogram[v] = 1 else: client_version_histogram[v] += 1 if not v[0:2] in client_histogram: client_histogram[v[0:2]] = 1 else: client_histogram[v[0:2]] += 1 except: pass if 'announce-distance:' in line: idx = l.index('announce-distance:') d = int(l[idx+1].strip()) if not d in announce_histogram: announce_histogram[d] = 0 announce_histogram[d] += 1 if 'NODE FAILED' in line: idx = l.index('fails:') if int(l[idx+1].strip()) != 1: continue; idx = l.index('up-time:') d = int(l[idx+1].strip()) # quantize d = d - (d % up_time_quanta) if not d in node_uptime_histogram: node_uptime_histogram[d] = 0 node_uptime_histogram[d] += 1 search_id = l[2] ts = l[0] event = l[3] if event == 'RESPONSE': outstanding = int(l[l.index('invoke-count:')+1]) nid = l[l.index('id:')+1] addr = l[l.index('addr:')+1] last_response = addr outstanding_searches[search_id].append({ 't': ts, 'd': distance, 'o': outstanding + 1, 'a':addr, 'e': event,'i':nid, 's':source}) elif event == 'NEW': nid = l[l.index('target:')+1] outstanding_searches[search_id] = [{ 't': ts, 'd': 0, 'o': 0, \ 'e': event, 'abstime': ts, 'i': nid}] last_response = '' elif event == 'INVOKE' or event == 'ADD' or event == '1ST_TIMEOUT' or \ event == 'TIMEOUT' or event == 'PEERS': if not search_id in outstanding_searches: print 'orphaned event: %s' % line else: outstanding = int(l[l.index('invoke-count:')+1]) distance = int(l[l.index('distance:')+1]) nid = l[l.index('id:')+1] addr = l[l.index('addr:')+1] source = '' if event == 'ADD': if last_response == '': continue source = last_response outstanding_searches[search_id].append({ 't': ts, 'd': distance, 'o': outstanding + 1, 'a':addr, 'e': event,'i':nid, 's':source}) elif event == 'ABORTED': outstanding_searches[search_id].append({ 't': ts, 'e': event}) elif event == 'COMPLETED': distance = int(l[l.index('distance:')+1]) lookup_type = l[l.index('type:')+1].strip() outstanding_searches[search_id].append({ 't': ts, 'd': distance, 'o': 0, 'e': event,'i':''}) outstanding_searches[search_id][0]['type'] = lookup_type s = outstanding_searches[search_id] try: start_time = convert_timestamp(s[0]['t']) for i in range(len(s)): s[i]['t'] = convert_timestamp(s[i]['t']) - start_time except: pass searches.append(s) del outstanding_searches[search_id] except Exception, e: print e print line.split(' ') lookup_times_min = [] lookup_times_max = [] # these are the timestamps for lookups crossing distance # to target boundaries lookup_distance = [] for i in range(0, 15): lookup_distance.append([]) for s in searches: for i in s: if not 'last_dist' in i: i['last_dist'] = -1 cur_dist = 160 - i['d'] last_dist = i['last_dist'] if cur_dist > last_dist: for j in range(last_dist + 1, cur_dist + 1): if j >= len(lookup_distance): break lookup_distance[j].append(i['t']) i['last_dist'] = cur_dist if i['e'] != 'PEERS': continue lookup_times_min.append(i['t']) break for i in reversed(s): if i['e'] != 'PEERS': continue lookup_times_max.append(i['t']) break lookup_times_min.sort() lookup_times_max.sort() out = open('dht_lookup_times_cdf.txt', 'w+') counter = 0 for i in range(len(lookup_times_min)): counter += 1 print >>out, '%d\t%d\t%f' % (lookup_times_min[i], lookup_times_max[i], counter / float(len(lookup_times_min))) out.close() for i in lookup_distance: i.sort() dist = 0 for i in lookup_distance: out = open('dht_lookup_distance_%d.txt' % dist, 'w+') dist += 1 counter = 0 for j in i: counter += 1 print >>out, '%d\t%f' % (j, counter / float(len(i))) out.close() out = open('dht_lookups.txt', 'w+') for s in searches: for i in s: if i['e'] == 'INVOKE': print >>out, ' ->', i['t'], 160 - i['d'], i['i'], i['a'] elif i['e'] == '1ST_TIMEOUT': print >>out, ' x ', i['t'], 160 - i['d'], i['i'], i['a'] elif i['e'] == 'TIMEOUT': print >>out, ' X ', i['t'], 160 - i['d'], i['i'], i['a'] elif i['e'] == 'ADD': print >>out, ' + ', i['t'], 160 - i['d'], i['i'], i['a'], i['s'] elif i['e'] == 'RESPONSE': print >>out, ' <-', i['t'], 160 - i['d'], i['i'], i['a'] elif i['e'] == 'PEERS': print >>out, ' <-', i['t'], 160 - i['d'], i['i'], i['a'] elif i['e'] == 'ABORTED': print >>out, 'abort' elif i['e'] == 'COMPLETED': print >>out, '***', i['t'], 160 - i['d'], '\n' elif i['e'] == 'NEW': print >>out, '===', i['abstime'], i['type'], '===' print >>out, '<> ', 0, our_node_id, i['i'] out.close() out = open('dht_announce_distribution.dat', 'w+') print 'announce distribution items: %d' % len(announce_histogram) for k,v in announce_histogram.items(): print >>out, '%d %d' % (k, v) print '%d %d' % (k, v) out.close() out = open('dht_node_uptime_cdf.txt', 'w+') s = 0 total_uptime_nodes = 0 for k,v in node_uptime_histogram.items(): total_uptime_nodes += v for k,v in sorted(node_uptime_histogram.items()): s += v print >>out, '%f %f' % (k / float(60), s / float(total_uptime_nodes)) print '%f %f' % (k / float(60), s / float(total_uptime_nodes)) out.close() print 'clients by version' client_version_histogram = sorted(client_version_histogram.items(), key=lambda x: x[1], reverse=True) pp.pprint(client_version_histogram) print 'clients' client_histogram = sorted(client_histogram.items(), key=lambda x: x[1], reverse=True) pp.pprint(client_histogram) out = open('dht.gnuplot', 'w+') out.write(''' set term png size 1200,700 small set output "dht_lookup_times_cdf.png" set title "portion of lookups that have received at least one data response" set ylabel "portion of lookups" set xlabel "time from start of lookup (ms)" set grid plot "dht_lookup_times_cdf.txt" using 1:3 with lines title "time to first result", \ "dht_lookup_times_cdf.txt" using 2:3 with lines title "time to last result" set terminal postscript set output "dht_lookup_times_cdf.ps" replot set term png size 1200,700 small set xtics 100 set xrange [0:2000] set output "dht_min_lookup_times_cdf.png" plot "dht_lookup_times_cdf.txt" using 1:3 with lines title "time to first result" set terminal postscript set output "dht_min_lookup_times_cdf.ps" replot set term png size 1200,700 small set output "dht_node_uptime_cdf.png" set xrange [0:*] set title "node up time" set ylabel "portion of nodes being offline" set xlabel "time from first seeing the node (minutes)" set xtics 10 unset grid plot "dht_node_uptime_cdf.txt" using 1:2 title "nodes" with lines set term png size 1200,700 small set output "dht_announce_distribution.png" set xrange [0:30] set xtics 1 set title "bucket # announces are made against relative to target node-id" set ylabel "# of announces" set boxwidth 1 set xlabel "bit prefix of nodes in announces" set style fill solid border -1 pattern 2 plot "dht_announce_distribution.dat" using 1:2 title "announces" with boxes set terminal postscript set output "dht_announce_distribution.ps" replot set term png size 1200,700 small set output "dht_lookup_distance_cdf.png" set title "portion of lookups that have reached a certain distance in their lookups" set ylabel "portion of lookups" set xlabel "time from start of lookup (ms)" set xrange [0:2000] set xtics 100 set grid plot ''') dist = 0 for i in lookup_distance: if dist > 0: out.write(', ') out.write('"dht_lookup_distance_%d.txt" using 1:2 title "%d" with lines' % (dist, dist)) dist += 1 out.close() os.system('gnuplot dht.gnuplot'); libtorrent-rasterbar-1.1.13/tools/parse_dht_rtt.py000077500000000000000000000021301351156116000223160ustar00rootroot00000000000000#!/usr/bin/env python import sys import os quantize = 100 max_rtt = 5000 f = open(sys.argv[1]) distribution = {} num_messages = 0 for i in range(0, max_rtt, quantize): distribution[i] = 0 for line in f: time = int(line.split('\t')[1]) if (time < 0 or time > max_rtt - quantize): continue num_messages += 1 time /= quantize time *= quantize distribution[time] += 1 f = open('round_trip_distribution.log', 'w+') for k, v in distribution.items(): print >>f, '%f %d' % ((k + (quantize / 2)) / 1000.0, v) f.close(); f = open('round_trip_distribution.gnuplot', 'w+') f.write(''' set term png size 1200,700 set title "Message round trip times" set terminal postscript set ylabel "# of requests" set xlabel "Round trip time (seconds)" set xrange [0:*] set grid set style fill solid border -1 pattern 2 set output "round_trip_distribution.ps" set boxwidth %f plot "round_trip_distribution.log" using 1:2 title "requests" with boxes set terminal png small set output "round_trip_distribution.png" replot ''' % (float(quantize) / 1000.0)) f.close() os.system('gnuplot round_trip_distribution.gnuplot'); libtorrent-rasterbar-1.1.13/tools/parse_dht_stats.py000077500000000000000000000030751351156116000226540ustar00rootroot00000000000000#!/usr/bin/env python import sys import os gnuplot_scripts = [] def gen_stats_gnuplot(name, y, lines): global gnuplot_scripts stat = open(sys.argv[1]) line = stat.readline() while not 'minute:' in line: line = stat.readline() names = line.strip().split(':') counter = 1 for i in names: print '%d: %s' % (counter, i) counter += 1 out = open('%s.gnuplot' % name, 'w+') out.write(''' set term png size 1200,700 small set output "%s.png" set title "%s" set ylabel "%s" set xlabel "time (minutes)" plot ''' % (name, name.strip('_'), y)) first = True for i in lines: if not first: out.write(', \\\n') first = False out.write('"%s" using 1:%d title "%s" with lines' % (sys.argv[1], names.index(i)+1, i)) out.write('\n') out.write('''set terminal postscript set output "%s.ps" replot ''' % (name)) out.close() gnuplot_scripts += [name] gen_stats_gnuplot('dht_routing_table_size', 'nodes', ['active nodes','passive nodes', 'confirmed nodes']) gen_stats_gnuplot('dht_tracker_table_size', '', ['num torrents', 'num peers']) gen_stats_gnuplot('dht_announces', 'messages per minute', ['announces per min', 'failed announces per min']) gen_stats_gnuplot('dht_clients', 'messages per minute', ['total msgs per min', 'az msgs per min', 'ut msgs per min', 'lt msgs per min', 'mp msgs per min', 'gr msgs per min']) gen_stats_gnuplot('dht_rate', 'bytes per second', ['bytes in per sec', 'bytes out per sec']) gen_stats_gnuplot('dht_errors', 'messages per minute', ['error replies sent', 'error queries recvd']) for i in gnuplot_scripts: os.system('gnuplot %s.gnuplot' % i); libtorrent-rasterbar-1.1.13/tools/parse_disk_buffer_log.py000077500000000000000000000037331351156116000240040ustar00rootroot00000000000000#!/usr/bin/env python import os, sys, time lines = open(sys.argv[1], 'rb').readlines() # logfile format: # : # example: # 16434 read cache: 17 key_order = ['receive buffer', 'send buffer', 'released send buffer', 'posted send buffer', 'received send buffer', 'dispatched send buffer', 'queued send buffer', 'write cache', 'read cache', 'hash temp'] colors = ['30f030', '001070', '101080', '2040a0', '4070d0', '80a0f0', 'f03030', '80f080', 'f08080', '4040ff'] keys = [] fields = {} maximum = {} out = open('disk_buffer_log.dat', 'w+') field_sum = {} field_num_samples = {} field_timestamp = {} for c in key_order: keys.append(c) fields[c] = 0 maximum[c] = 0 field_sum[c] = 0 field_num_samples[c] = 0 field_timestamp[c] = 0 last_t = 0 for l in lines: try: t = int(l[0:l.find(' ')]) c = l[l.find(' ')+1:l.find(':')] n = int(l[l.find(':')+1:-1]) except: print l continue if last_t != t: print >>out, '%d\t' % last_t, for i in keys: print >>out, '%d\t' % maximum[i], print >>out, '\n', if not c in keys: continue field_sum[c] += fields[c] * float(t - field_timestamp[c]) field_timestamp[c] = t fields[c] = n if n > maximum[c]: maximum[c] = n if last_t != t: last_t = t maximum = fields for i in keys: print '%s: avg: %f' % (i, field_sum[i] / last_t) print out.close() out = open('disk_buffer.gnuplot', 'wb') print >>out, "set term png size 1200,700" print >>out, 'set output "disk_buffer.png"' print >>out, 'set xrange [0:*]' print >>out, 'set xlabel "time (ms)"' print >>out, 'set ylabel "buffers"' print >>out, "set style data lines" print >>out, "set key box" print >>out, 'plot', count = 1 + len(keys) keys.reverse() comma = '' for k in keys: expr = "$%d" % count for i in xrange(2, count): expr += "+$%d" % i count -= 1 print >>out, ' %s"disk_buffer_log.dat" using 1:(%s) title "%s" with filledcurves x1 lt rgb "#%s"' % (comma, expr, k, colors[count-1]), comma = ',' out.close() os.system('gnuplot disk_buffer.gnuplot') libtorrent-rasterbar-1.1.13/tools/parse_disk_log.py000077500000000000000000000065011351156116000224470ustar00rootroot00000000000000#!/usr/bin/env python # Copyright Arvid Norberg 2008. 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) import os, sys, time lines = open(sys.argv[1], 'rb').readlines() if len(sys.argv) < 2: print "usage: parse_disk_log.py logfile [seconds]" sys.exit(1) keys = ['write', 'read', 'read-cache-hit', 'hash', 'move', 'release', 'idle', \ 'delete', 'check_fastresume', 'check_files', 'clear-cache', \ 'abort_thread', 'abort_torrent', 'save_resume_data', 'rename_file', \ 'flushing', 'update_settings', 'finalize_file', 'sorting_job', \ 'check_cache_hit'] throughput_keys = ['write', 'read', 'read-cache-hit'] # logfile format: # # example: # 34523 idle # 34722 write if len(sys.argv) > 2: quantization = long(sys.argv[2]) * 1000000 else: quantization = 1000000 out = open('disk_io.dat', 'wb') out2 = open('disk_throughput.dat', 'wb') state = 'idle' time = -1 start_time = -1 i = 0 state_timer = {} throughput = {} for k in keys: state_timer[k] = 0 for k in throughput_keys: throughput[k] = 0 for l in lines: l = l.strip().split() if len(l) < 2: print l continue # try: new_time = long(l[0]) if time == -1: time = new_time i = new_time start_time = new_time while new_time > i + quantization: i += quantization state_timer[state] += i - time time = i for k in keys: print >>out, (state_timer[k] / float(quantization) * 100.), print >>out print >>out2, time - start_time, for k in throughput_keys: print >>out2, throughput[k] * 1000 / float(quantization), print '-- %s %d' % (k, throughput[k]) print >>out2 for k in keys: state_timer[k] = 0 for k in throughput_keys: throughput[k] = 0 state_timer[state] += new_time - time time = new_time state = l[1] if state in throughput_keys: throughput[state] += long(l[2]) # except: # print l i += quantization state_timer[state] += i - time time = i for k in keys: print >>out, (state_timer[k] / float(quantization) * 100.), print >>out print >>out2, time - start_time, for k in throughput_keys: print >>out2, throughput[k] * 1000 / float(quantization), print '-- %s %d' % (k, throughput[k]) print >>out2 for k in keys: state_timer[k] = 0 for k in throughput_keys: throughput[k] = 0 out.close() out2.close() out = open('disk_io.gnuplot', 'wb') print >>out, "set term png size 1200,700" print >>out, 'set output "disk_throughput.png"' print >>out, 'set title "disk throughput per %f second(s)"' % (quantization / float(1000000)) print >>out, 'set ylabel "throughput (kB/s)"' print >>out, 'plot', i = 0 for k in throughput_keys: print >>out, ' "disk_throughput.dat" using 1:%d title "%s" with lines,' % (i + 2, throughput_keys[i]), i = i + 1 print >>out, 'x=0' print >>out, 'set output "disk_io.png"' print >>out, 'set ylabel "utilization (%)"' print >>out, 'set xrange [0:*]' print >>out, 'set title "disk io utilization per %f second(s)"' % (quantization / float(1000000)) print >>out, "set key box" print >>out, "set style data histogram" print >>out, "set style histogram rowstacked" print >>out, "set style fill solid" print >>out, 'plot', i = 0 for k in keys: if k != 'idle': print >>out, ' "disk_io.dat" using %d title "%s",' % (i + 1, keys[i]), i = i + 1 print >>out, 'x=0' out.close() os.system('gnuplot disk_io.gnuplot'); libtorrent-rasterbar-1.1.13/tools/parse_memory_log.py000077500000000000000000000066421351156116000230330ustar00rootroot00000000000000#!/usr/bin/env python import os, sys, time # usage: memory.log memory_index.log lines = open(sys.argv[1], 'rb').readlines() index = open(sys.argv[2], 'rb').readlines() # logfile format: # #
# example: # #12 38 A 0xd902a0 16 16 0 16 allocation_points_to_print = 30 def print_allocation_point(ap): print 'space_time: %d kBms' % (ap['spacetime'] / 1024) print 'allocations: %d' % ap['allocations'] print 'peak: %d kB' % (ap['peak'] / 1024) print 'stack: ' counter = 0 for e in ap['stack']: print '#%d %s' % (counter, e) counter += 1 allocation_points = [] for l in index: l = l.split('#') l.pop(0) ap = { 'allocations': 0, 'peak': 0, 'spacetime': 0, 'allocation_point': len(allocation_points), 'stack': l} allocation_points.append(ap); for l in lines: l = l.lstrip('#').rstrip('\n').split(' ') if len(l) != 8: print l continue try: ap = int(l[0]) allocation_points[ap]['allocations'] += 1 allocation_points[ap]['peak'] = int(l[7]) allocation_points[ap]['spacetime'] = int(l[6]) except Exception, e: print type(e), e, l print '=== space time ===' hot_ap = [] allocation_points.sort(key = lambda x:x['spacetime'], reverse=True); counter = 0 for ap in allocation_points[0:allocation_points_to_print]: print '== %d ==' % counter counter += 1 print_allocation_point(ap) hot_ap.append(ap['allocation_point']); print '=== allocations ===' allocation_points.sort(key = lambda x:x['allocations'], reverse=True); for ap in allocation_points[0:allocation_points_to_print]: print_allocation_point(ap) print '=== peak ===' allocation_points.sort(key = lambda x:x['peak'], reverse=True); for ap in allocation_points[0:allocation_points_to_print]: print_allocation_point(ap) # generate graph lines = open(sys.argv[1], 'rb').readlines() out = open('memory.dat', 'wb') cur_line = [0] * allocation_points_to_print prev_line = [0] * allocation_points_to_print last_time = 0 for l in lines: l = l.lstrip('#').rstrip('\n').split(' ') if len(l) != 8: print l continue try: time = int(l[1]) if time != last_time: print >>out, last_time, '\t', for i in range(allocation_points_to_print): if cur_line[i] == -1: print >>out, prev_line[i], '\t', else: print >>out, cur_line[i], '\t', prev_line[i] = cur_line[i] print >>out cur_line = [-1] * allocation_points_to_print last_time = time size = int(l[5]) ap = int(l[0]) if ap in hot_ap: index = hot_ap.index(ap) cur_line[index] = max(cur_line[index], size) except Exception, e: print type(e), e, l out.close() out = open('memory.gnuplot', 'wb') print >>out, "set term png size 1200,700" print >>out, 'set output "memory.png"' print >>out, 'set xrange [0:*]' print >>out, 'set xlabel "time (ms)"' print >>out, 'set ylabel "bytes (B)"' print >>out, "set style data lines" print >>out, "set key box" print >>out, 'plot', for k in range(allocation_points_to_print): print >>out, ' "memory.dat" using 1:(', for i in range(k, allocation_points_to_print): if i == k: print >>out, '$%d' % (i + 2), else: print >>out, '+$%d' % (i + 2), print >>out, ') title "%d" with filledcurves x1, \\' % k print >>out, 'x=0' out.close() os.system('gnuplot memory.gnuplot'); libtorrent-rasterbar-1.1.13/tools/parse_peer_log.py000077500000000000000000000032051351156116000224460ustar00rootroot00000000000000#!/usr/bin/env python import glob import os import sys # usage: parse_peer_log log_files = [] for p in glob.iglob(os.path.join(sys.argv[1], '*.log')): name = os.path.split(p)[1] if name == 'main_session.log': continue print name f = open(p, 'r') out_file = p + '.dat' log_files.append(out_file) out = open(out_file, 'w+') uploaded_blocks = 0; downloaded_blocks = 0; for l in f: t = l.split(': ')[0].split('.')[0] log_line = False if ' ==> PIECE' in l: uploaded_blocks+= 1 log_line = True if ' <== PIECE' in l: downloaded_blocks+= 1 log_line = True if log_line: print >>out, '%s\t%d\t%d' % (t, uploaded_blocks, downloaded_blocks) out.close() f.close() out = open('peers.gnuplot', 'wb') print >>out, "set term png size 1200,700" print >>out, 'set xrange [0:*]' print >>out, 'set xlabel "time"' print >>out, 'set ylabel "blocks"' print >>out, 'set key box' print >>out, 'set xdata time' print >>out, 'set timefmt "%H:%M:%S"' print >>out, 'set title "uploaded blocks"' print >>out, 'set output "peers_upload.png"' print >>out, 'plot', first = True for n in log_files: if not first: print >>out, ',', first = False print >>out, ' "%s" using 1:2 title "%s" with steps' % (n, os.path.split(n)[1].split('.log')[0]), print >>out, '' print >>out, 'set title "downloaded blocks"' print >>out, 'set output "peers_download.png"' print >>out, 'plot', first = True for n in log_files: if not first: print >>out, ',', first = False print >>out, ' "%s" using 1:3 title "%s" with steps' % (n, os.path.split(n)[1].split('.log')[0]), print >>out, '' out.close() os.system('gnuplot peers.gnuplot'); libtorrent-rasterbar-1.1.13/tools/parse_sample.py000077500000000000000000000067061351156116000221440ustar00rootroot00000000000000#!/usr/bin/env python import sys # to use this script, first run 'sample' to sample your libtorrent based process # the output can then be passed to this script to auto-fold call stacks at # relevant depths and to filter out low sample counts f = open(sys.argv[1]) def parse_line(l): indentation = 0 while indentation < len(l) and l[indentation] == ' ': indentation += 1 if indentation == 0: return (0, 0, '') l = l.strip().split(' ') samples = int(l[0]) fun = ' '.join(l[1:]) return (indentation, samples, fun) fold = -1 try: sample_limit = int(sys.argv[2]) except: sample_limit = 5 fun_samples = {} for l in f: if 'Sort by top of stack' in l: break indentation, samples, fun = parse_line(l) if samples < sample_limit: continue if fold != -1 and indentation > fold: continue fold = -1 if '__gnu_cxx::__normal_iterator<' in fun: fold = indentation - 1 continue if 'boost::_bi::bind_t' in fun: continue if 'boost::_bi::list' in fun: continue if 'boost::_mfi::mf' in fun: continue if 'boost::_bi::storage' in fun: continue # should only add leaves if fun in fun_samples: fun_samples[fun] += samples else: fun_samples[fun] = samples output = '%s%-4d %s' % (' ' * (indentation/2), samples, fun) if len(output) > 200: output = output[0:200] print output if 'invariant_checker_impl' in fun: fold = indentation if 'free_multiple_buffers' in fun: fold = indentation if 'libtorrent::condition::wait' in fun: fold = indentation if 'allocate_buffer' in fun: fold = indentation if '::find_POD' in fun: fold = indentation if 'SHA1_Update' in fun: fold = indentation if 'boost::detail::function::basic_vtable' in fun: fold = indentation if 'operator new' in fun: fold = indentation if 'malloc' == fun: fold = indentation if 'free' == fun: fold = indentation if 'std::_Rb_tree' in fun: fold = indentation if 'pthread_cond_wait' in fun: fold = indentation if 'mp_exptmod' == fun: fold = indentation if '::check_invariant()' in fun: fold = indentation if 'libtorrent::condition::wait' in fun: fold = indentation if '_sigtramp' in fun: fold = indentation if 'time_now_hires' in fun: fold = indentation if 'libtorrent::sleep' in fun: fold = indentation if 'puts' == fun: fold = indentation if 'boost::asio::basic_stream_socket' in fun: fold = indentation if 'recvmsg' == fun: fold = indentation if 'sendmsg' == fun: fold = indentation if 'semaphore_signal_trap' == fun: fold = indentation if 'boost::detail::atomic_count::operator' in fun: fold = indentation if 'pthread_mutex_lock' == fun: fold = indentation if 'pthread_mutex_unlock' == fun: fold = indentation if '>::~vector()' == fun: fold = indentation if 'szone_free_definite_size' == fun: fold = indentation if 'snprintf' == fun: fold = indentation if 'usleep' == fun: fold = indentation if 'pthread_mutex_lock' == fun: fold = indentation if 'pthread_mutex_unlock' == fun: fold = indentation if 'std::string::append' in fun: fold = indentation if 'getipnodebyname' == fun: fold = indentation if '__gnu_debug::_Safe_iterator/dev/null' % script) if ret != 0 and ret != 256: print 'system: %d\n' % ret raise Exception("abort") sys.stdout.write('.') sys.stdout.flush() def to_title(key): return key.replace('_', ' ').replace('.', ' - ') def gen_report(name, unit, lines, short_unit, generation, log_file, options): filename = os.path.join(output_dir, '%s_%04d.png' % (name, generation)) thumb = os.path.join(output_dir, '%s_%04d_thumb.png' % (name, generation)) # don't re-render a graph unless the logfile has changed try: dst1 = os.stat(filename) dst2 = os.stat(thumb) src = os.stat(log_file) if dst1.st_mtime > src.st_mtime and dst2.st_mtime > src.st_mtime: sys.stdout.write('.') return None except: pass script = os.path.join(output_dir, '%s_%04d.gnuplot' % (name, generation)) out = open(script, 'wb') print >>out, "set term png size 1200,700" print >>out, 'set output "%s"' % filename if not 'allow-negative' in options: print >>out, 'set yrange [0:*]' print >>out, "set tics nomirror" print >>out, "set key box" print >>out, "set key left top" colors = graph_colors if options['type'] == line_graph: colors = line_colors try: if options['colors'] == 'gradient16': colors = gradient16_colors elif options['colors'] == 'gradient6': colors = gradient6_colors if options['colors'] == 'gradient18': colors = gradient18_colors except: pass if options['type'] == histogram: binwidth = options['binwidth'] numbins = int(options['numbins']) print >>out, 'binwidth=%f' % binwidth print >>out, 'set boxwidth binwidth' print >>out, 'bin(x,width)=width*floor(x/width) + binwidth/2' print >>out, 'set xrange [0:%f]' % (binwidth * numbins) print >>out, 'set xlabel "%s"' % unit print >>out, 'set ylabel "number"' k = lines[0] try: column = keys.index(k) + 2 except: print '"%s" not found' % k return print >>out, 'plot "%s" using (bin($%d,binwidth)):(1.0) smooth freq with boxes' % (log_file, column) print >>out, '' print >>out, '' print >>out, '' elif options['type'] == stacked: print >>out, 'set xrange [0:*]' print >>out, 'set ylabel "%s"' % unit print >>out, 'set xlabel "time (s)"' print >>out, 'set format y "%%.1s%%c%s";' % short_unit print >>out, 'set style fill solid 1.0 noborder' print >>out, 'plot', column = 2 first = True graph = '' plot_expression = '' color = 0 for k in lines: try: column = keys.index(k) + 2 except: print '"%s" not found' % k continue; if not first: plot_expression = ', ' + plot_expression graph += '+' axis = 'x1y1' graph += '$%d' % column plot_expression = ' "%s" using 1:(%s) title "%s" axes %s with filledcurves x1 lc rgb "%s"' % (log_file, graph, to_title(k), axis, colors[color % len(colors)]) + plot_expression first = False color += 1 print >>out, plot_expression elif options['type'] == diff: print >>out, 'set xrange [0:*]' print >>out, 'set ylabel "%s"' % unit print >>out, 'set xlabel "time (s)"' print >>out, 'set format y "%%.1s%%c%s";' % short_unit column = 2 first = True graph = '' title = '' for k in lines: try: column = keys.index(k) + 2 except: print '"%s" not found' % k continue; if not first: graph += '-' title += ' - ' graph += '$%d' % column title += to_title(k) first = False print >>out, 'plot "%s" using 1:(%s) title "%s" with step' % (log_file, graph, title) else: print >>out, 'set xrange [0:*]' print >>out, 'set ylabel "%s"' % unit print >>out, 'set xlabel "time (s)"' print >>out, 'set format y "%%.1s%%c%s";' % short_unit print >>out, 'plot', column = 2 first = True color = 0 for k in lines: try: column = keys.index(k) + 2 except: print '"%s" not found' % k continue; if not first: print >>out, ', ', axis = 'x1y1' print >>out, ' "%s" using 1:%d title "%s" axes %s with steps lc rgb "%s"' % (log_file, column, to_title(k), axis, colors[color % len(colors)]), first = False color += 1 print >>out, '' print >>out, 'set term png size 150,100' print >>out, 'set output "%s"' % thumb print >>out, 'set key off' print >>out, 'unset tics' print >>out, 'set format x ""' print >>out, 'set format y ""' print >>out, 'set xlabel ""' print >>out, 'set ylabel ""' print >>out, 'set y2label ""' print >>out, 'set rmargin 0' print >>out, 'set lmargin 0' print >>out, 'set tmargin 0' print >>out, 'set bmargin 0' print >>out, "replot" out.close() return script def gen_html(reports, generations): file = open(os.path.join(output_dir, 'index.html'), 'w+') css = '''img { margin: 0} #head { display: block } #graphs { white-space:nowrap; } h1 { line-height: 1; display: inline } h2 { line-height: 1; display: inline; font-size: 1em; font-weight: normal};''' print >>file, '' % css for i in reports: print >>file, '